irongit

ui: trim terminal commands, open every link in a new tab

Keep /clear, /email, /copy, /linkedin, /github, /places, /mode, /lock,
/exit. Email (terminal and page) and links in answers open in a new tab.
huncholanehuncholaneauthored
parent 78baf3dcommit afdc6cefafc5a3c64bcde75a687f16185f06a2d8Browse files

3 files changed, +11 -52

+6-51src/lib/gcs/console.ts
@@ -1,7 +1,7 @@
11 // Docked terminal, bottom right, styled after the Claude CLI. Plain text is a
22 // question about Lane: it streams from DeepSeek through the worker
3-// (/__ai/chat), which grounds the model in /llms.txt. Slash commands navigate
4-// the page, open contact links, and drive the camera feed. "/" or "`" opens
3+// (/__ai/chat), which grounds the model in /llms.txt. Slash commands open
4+// contact links (always in a new tab), list places, and drive the camera feed. "/" or "`" opens
55 // it; Esc closes the slash menu, then interrupts an answer, then closes.
66 // The conversation survives reloads for the tab session (sessionStorage).
77 import { track } from '../track';
@@ -41,10 +41,8 @@ function linkify(el: HTMLElement, text: string) {
4141 a.href = isMail ? `mailto:${m[0]}` : m[0];
4242 a.textContent = m[0];
4343 a.className = 'term-link';
44- if (!isMail) {
45- a.target = '_blank';
46- a.rel = 'noopener';
47- }
44+ a.target = '_blank';
45+ a.rel = 'noopener';
4846 a.addEventListener('click', () => track('console_link', { kind: isMail ? 'email' : 'url' }));
4947 parts.push(a);
5048 last = m.index! + m[0].length;
@@ -150,30 +148,8 @@ export function initConsole() {
150148 };
151149
152150 const isOpen = () => !panel.hidden;
153- const narrow = () => window.matchMedia('(max-width: 767px)').matches;
154-
155- // Scroll to a section. Skills falls back to Work where the Skills panel is
156- // replaced by the right-hand column. Small screens close the terminal so the
157- // section is visible.
158- const goTo = (id: string) => {
159- let el = document.getElementById(id);
160- if (el && getComputedStyle(el).display === 'none') el = document.getElementById('work');
161- if (narrow()) close();
162- el?.scrollIntoView({ behavior: reduce ? 'auto' : 'smooth' });
163- print(`Scrolled to ${id}.`, 'muted');
164- };
165151
166152 const commands: Record<string, Command> = {
167- help: {
168- help: 'list commands and shortcuts',
169- run: () => {
170- print('Type a question to ask about Lane, or a command:', 'muted');
171- for (const [name, c] of Object.entries(commands)) {
172- if (c.help) print(` /${(c.usage ?? name).padEnd(20)} ${c.help}`, 'muted');
173- }
174- print(' ↑ ↓ history tab completes esc interrupts or closes', 'muted');
175- },
176- },
177153 clear: {
178154 help: 'start a new conversation',
179155 run: () => {
@@ -187,7 +163,7 @@ export function initConsole() {
187163 run: () => {
188164 print(`Opening mail to ${EMAIL}`, 'vol');
189165 track('contact_click', { channel: 'email', location: 'console' });
190- window.location.href = `mailto:${EMAIL}`;
166+ window.open(`mailto:${EMAIL}`, '_blank', 'noopener');
191167 },
192168 },
193169 copy: {
@@ -219,16 +195,6 @@ export function initConsole() {
219195 window.open(GITHUB, '_blank', 'noopener');
220196 },
221197 },
222- work: { help: 'go to work and experience', run: () => goTo('work') },
223- education: { help: 'go to education', run: () => goTo('education') },
224- skills: { help: 'go to skills', run: () => goTo('skills') },
225- contact: { help: 'go to contact', run: () => goTo('contact') },
226- tracks: {
227- help: 'list every project',
228- run: () => {
229- for (const r of document.querySelectorAll<HTMLElement>('[data-trk]')) print(` ${r.dataset.trk} ${r.dataset.trkName}`);
230- },
231- },
232198 places: {
233199 help: 'places Lane has lived and worked',
234200 run: () => {
@@ -255,25 +221,14 @@ export function initConsole() {
255221 print('DEV 01 reacquired', 'vol');
256222 },
257223 },
258- status: {
259- help: 'system status',
260- run: () => {
261- const feed = document.querySelector<HTMLElement>('.feed');
262- print(`LINK OK UTC ${new Date().toISOString().slice(11, 19)}Z`);
263- print(`SENSOR 1 ${(feed?.dataset.mode ?? 'eo').toUpperCase()} ${document.querySelector('[data-tgt]')?.textContent ?? 'TRK 01'}`);
264- print(`CONTEXT ${convo.length} messages`);
265- },
266- },
267224 exit: { help: 'close the terminal', run: () => close() },
268- // Not listed in help.
269- giga: { run: () => (window.location.href = '/giga') },
270225 };
271226
272227 function runCommand(raw: string) {
273228 const [name, ...args] = raw.slice(1).toLowerCase().split(/\s+/);
274229 const cmd = commands[name];
275230 track('console_command', { cmd: cmd ? name : 'unknown' });
276- if (!cmd) return void print(`Unknown command /${name}. Type /help for the list.`, 'err');
231+ if (!cmd) return void print(`Unknown command /${name}. Type / to see the commands.`, 'err');
277232 try {
278233 cmd.run(args);
279234 } catch (err) {
+4-0src/pages/index.astro
@@ -349,6 +349,8 @@ const pad = (n: number) => String(n).padStart(2, '0');
349349 <div class="mt-9 flex flex-wrap items-center gap-3">
350350 <a
351351 href={`mailto:${email}`}
352+ target="_blank"
353+ rel="noopener"
352354 class={`inline-flex items-center rounded-[2px] bg-vol px-5 py-3 ${mono} font-bold text-black transition-colors hover:bg-vol-deep`}
353355 data-track="contact_click"
354356 data-track-channel="email"
@@ -524,6 +526,8 @@ const pad = (n: number) => String(n).padStart(2, '0');
524526 <a
525527 class={`text-[clamp(1.4rem,6vw,2.25rem)] font-semibold font-stretch-[112%] text-vol ${textLink}`}
526528 href={`mailto:${email}`}
529+ target="_blank"
530+ rel="noopener"
527531 data-track="contact_click"
528532 data-track-channel="email"
529533 data-track-location="contact"
+1-1worker/index.ts
@@ -39,7 +39,7 @@ Rules:
3939 - Never share a phone number or home address.
4040 - For hiring, contracting, or contact questions, point to lane@henslee.me.
4141 - Stay on topic: Lane, Lane's work and skills, and how to reach Lane. Politely decline unrelated tasks.
42-- The terminal also has slash commands (/help lists them, /email opens mail, /copy copies the address). Mention them only when useful.
42+- The terminal also has slash commands (typing / lists them, /email opens mail, /copy copies the address). Mention them only when useful.
4343
4444 PROFILE
4545 `;