/* Life OS command line: only the listed application commands are executable. */ (() => { const t = (key, values) => LifeI18n.t(`terminal.${key}`, values); const actions = () => window.LifeOSActions; const startedAt = Date.now(); const localDate = offset => { const value = new Date(); value.setDate(value.getDate() + offset); return `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, '0')}-${String(value.getDate()).padStart(2, '0')}`; }; const date = value => value === 'today' ? localDate(0) : value === 'tomorrow' ? localDate(1) : value || ''; const validDay = value => { const parsed = new Date(`${value}T12:00:00`); return /^\d{4}-\d{2}-\d{2}$/.test(value) && !Number.isNaN(parsed.getTime()) && `${parsed.getFullYear()}-${String(parsed.getMonth() + 1).padStart(2, '0')}-${String(parsed.getDate()).padStart(2, '0')}` === value; }; const words = line => { const result = []; let current = '', quote = '', escaped = false, started = false; for (const char of line) { if (escaped) { current += char; escaped = false; started = true; } else if (char === '\\' && quote) escaped = true; else if (quote) { if (char === quote) quote = ''; else current += char; } else if (char === '"' || char === "'") { quote = char; started = true; } else if (/\s/.test(char)) { if (started) { result.push(current); current = ''; started = false; } } else { current += char; started = true; } } if (quote || escaped) throw new Error('unclosedQuote'); if (started) result.push(current); return result; }; const options = (input, allowed) => { const flags = {}, values = []; for (let index = 0; index < input.length; index++) { const value = input[index]; if (value.startsWith('--')) { const key = value.slice(2); if (!allowed.includes(key)) throw new Error('invalidOption'); if (key === 'pin') { flags.pin = true; continue; } if (input[index + 1] === undefined || input[index + 1].startsWith('--')) throw new Error('invalidOption'); if (key === 'tag') (flags.tag ||= []).push(input[++index]); else flags[key] = input[++index]; } else values.push(value); } return { flags, values }; }; const resolve = (items, value) => { if (!value) return ''; if (/^[1-9]\d*$/.test(value) && items[Number(value) - 1]) return items[Number(value) - 1].id; const matches = items.filter(item => item.id === value || item.title.toLocaleLowerCase() === value.toLocaleLowerCase()); if (!matches.length) throw new Error('notFound'); if (matches.length > 1) throw new Error('ambiguous'); return matches[0].id; }; const board = () => actions().board(); const requireTitle = value => { if (!value?.trim()) throw new Error('usage'); return value.trim(); }; const help = () => ['help', 'memory add / memory add "…" — ' + t('guidedHelp'), 'memory add "…" [--date YYYY-MM-DD] [--tag …] [--energy 1-5] [--weather cloudy] [--place …] [--task …] [--text …]', 'memory list [--tag …] [--query …] / memory delete [#|date|title]', 'goal add ["…"] / goal list / goal delete [#|title]', 'goal add "…" --due YYYY-MM-DD [--description …] [--pin]', 'goal done <#> / goal reopen <#>', 'task add ["…"] / task list / task delete [#|title]', 'task add "…" --due tomorrow [--goal …] [--priority high|normal|low]', 'task done <#> / task reopen <#>', 'date / time / datetime / uptime / version', 'status / apps / history / echo "…"', 'open / clear / console'].join('\n'); const wizardFields = ['title', 'date', 'tags', 'energy', 'mood', 'location', 'taskId']; let wizard = null; let flow = null; const itemsFor = kind => kind === 'memory' ? mems() : board()[kind === 'goal' ? 'goals' : 'tasks']; const itemLabel = (kind, item) => kind === 'memory' ? `${item.date} ${item.title || item.text?.slice(0, 40) || '—'}` : item.title; const findItem = (kind, value) => { const items = itemsFor(kind); if (/^[1-9]\d*$/.test(value) && items[Number(value) - 1]) return items[Number(value) - 1]; const matches = items.filter(item => item.id === value || (kind === 'memory' && item.date === value) || item.title?.toLocaleLowerCase() === value.toLocaleLowerCase()); if (!matches.length) throw new Error('notFound'); if (matches.length > 1) throw new Error('ambiguous'); return matches[0]; }; const flowFields = { goal: ['title','description','targetDate','showOnDesktop'], task: ['title','dueDate','goalId','priority'] }; const flowPrompt = () => { if (flow.operation === 'delete' && !flow.target) return { message: `${t('flow.pick', { kind: t(flow.kind) })}\n${itemsFor(flow.kind).map((item,index) => `${index + 1}. ${itemLabel(flow.kind,item)}`).slice(0,30).join('\n')}\n${t('wizard.controls')}` }; if (flow.operation === 'delete') return { message: t('flow.confirmDelete', { kind: t(flow.kind), title: itemLabel(flow.kind,flow.target) }) }; const field = flowFields[flow.kind][flow.step]; if (!field) return { message: t('flow.confirmCreate', { kind: t(flow.kind), title: flow.draft.title }) }; const items = field === 'goalId' ? board().goals.map((goal,index) => `${index + 1}. ${goal.title}`).slice(0,20).join('\n') || '—' : ''; const value = field === 'goalId' ? board().goals.find(goal => goal.id === flow.draft.goalId)?.title : flow.draft[field]; return { message: `${t(`flow.${field}`, { value: value || '—', items })}\n${t('wizard.controls')}` }; }; const startFlow = (kind, operation, value = '') => { if (operation === 'delete') { if (!itemsFor(kind).length) return { message: t('empty') }; flow = { kind, operation, target: value ? findItem(kind,value) : null }; return flowPrompt(); } flow = { kind, operation, step: value ? 1 : 0, draft: kind === 'goal' ? { title: value, description: '', targetDate: '', showOnDesktop: false } : { title: value, dueDate: '', goalId: '', priority: 'normal' } }; return flowPrompt(); }; const answerFlow = input => { const value = input.trim(), current = flow; if (value === 'cancel') { flow = null; return { message: t('flow.cancelled'), cancel: true }; } if (value === 'back') { if (current.operation === 'delete') current.target = null; else current.step = Math.max(0,current.step - 1); return flowPrompt(); } if (current.operation === 'delete') { if (!current.target) { if (!value) throw new Error('usage'); current.target = findItem(current.kind,value); return flowPrompt(); } if (!['yes','y'].includes(value.toLowerCase())) { if (!['no','n'].includes(value.toLowerCase())) throw new Error('confirmAnswer'); flow = null; return { message: t('flow.cancelled'), cancel: true }; } const item = itemsFor(current.kind).find(record => record.id === current.target.id); if (!item) { flow = null; throw new Error('notFound'); } const label = itemLabel(current.kind,item); actions()[`delete${current.kind[0].toUpperCase()}${current.kind.slice(1)}`](item.id); flow = null; return { message: t('flow.deleted', { kind: t(current.kind), title: label }) }; } const field = flowFields[current.kind][current.step], draft = current.draft; if (!field) { if (!['yes','y'].includes(value.toLowerCase())) { if (!['no','n'].includes(value.toLowerCase())) throw new Error('confirmAnswer'); flow = null; return { message: t('flow.cancelled'), cancel: true }; } const item = current.kind === 'goal' ? actions().createGoal(draft) : actions().createTask(draft); flow = null; return { message: t(current.kind === 'goal' ? 'goalSaved' : 'taskSaved', { title: item.title }) }; } if (field === 'title') { if (!value && !draft.title || value.length > (current.kind === 'goal' ? 100 : 140)) throw new Error('invalidTitle'); draft.title = value || draft.title; } if (field === 'description') { if (value.length > 240) throw new Error('invalidValue'); if (value) draft.description = value === '-' ? '' : value; } if (field === 'targetDate' || field === 'dueDate') { const selected = value === '-' ? '' : date(value) || draft[field]; if (selected && !validDay(selected)) throw new Error('invalidDate'); draft[field] = selected; } if (field === 'showOnDesktop') { if (value && !['yes','y','no','n'].includes(value.toLowerCase())) throw new Error('invalidValue'); const enabled = value ? ['yes','y'].includes(value.toLowerCase()) : draft.showOnDesktop; if (enabled && !draft.targetDate) throw new Error('pinNeedsDate'); draft.showOnDesktop = enabled; } if (field === 'goalId' && value) draft.goalId = value === '-' ? '' : resolve(board().goals,value); if (field === 'priority') { if (value && !['high','normal','low'].includes(value.toLowerCase())) throw new Error('invalidPriority'); draft.priority = value ? value.toLowerCase() : draft.priority; } current.step++; return flowPrompt(); }; const wizardPrompt = () => { if (wizard.step >= wizardFields.length) return { compose: { ...wizard.draft } }; const field = wizardFields[wizard.step], tasks = field === 'taskId' ? board().tasks.map((item, index) => `${index + 1}. ${item.title}`).slice(0, 20).join('\n') || '—' : ''; const current = field === 'taskId' ? board().tasks.find(item => item.id === wizard.draft.taskId)?.title : field === 'tags' ? wizard.draft.tags.join(', ') : wizard.draft[field]; return { message: `${t(`wizard.${field}`, { tasks, value: current || '—' })}\n${t('wizard.controls')}` }; }; const nextStep = () => { while (wizard.step < wizardFields.length && wizard.seeded.has(wizardFields[wizard.step])) wizard.step++; return wizardPrompt(); }; const backStep = () => { if (!wizard) return { message: t('noWizard') }; wizard.step = Math.min(wizard.step, wizardFields.length); wizard.step = Math.max(0, wizard.step - 1); return wizardPrompt(); }; const answer = input => { const value = input.trim(); if (value === 'cancel') { wizard = null; return { message: t('cancelled'), cancel: true }; } if (value === 'back') return backStep(); const field = wizardFields[wizard.step], draft = wizard.draft; if (field === 'title') { if ((!value && !draft.title) || value.length > 120) throw new Error('invalidTitle'); draft.title = value || draft.title; } if (field === 'date') { const selected = date(value) || draft.date || localDate(0); if (!validDay(selected)) throw new Error('invalidDate'); if (mems().some(item => item.date === selected)) throw new Error('memoryExists'); draft.date = selected; } if (field === 'tags' && value) draft.tags = value === '-' ? [] : [...new Set(value.split(/[,,]/).map(tag => tag.trim()).filter(Boolean))].slice(0, 12); if (field === 'energy') { if (value && !/^[1-5]$/.test(value)) throw new Error('invalidValue'); draft.energy = value ? Number(value) : draft.energy; } if (field === 'mood') { if (value && !['clear','light','cloudy','rain','storm'].includes(value.toLowerCase())) throw new Error('invalidValue'); draft.mood = value ? value.toLowerCase() : draft.mood; } if (field === 'location') { if (value.length > 100) throw new Error('invalidValue'); if (value) draft.location = value === '-' ? '' : value; } if (field === 'taskId' && value) draft.taskId = value === '-' ? '' : resolve(board().tasks, value); wizard.step++; return nextStep(); }; const run = line => { if (flow) return answerFlow(line); if (wizard) return answer(line); const [kind, verb, ...rest] = words(line.trim()); if (!kind) return {}; if (kind === 'help') return { message: help() }; if (kind === 'clear') return { clear: true }; if (kind === 'date' || kind === 'time' || kind === 'datetime') { if (verb) throw new Error('usage'); const options = kind === 'date' ? { dateStyle: 'full' } : kind === 'time' ? { timeStyle: 'medium' } : { dateStyle: 'full', timeStyle: 'medium' }; return { message: new Intl.DateTimeFormat(lang() === 'zh' ? 'zh-CN' : 'en-US', options).format(new Date()) }; } if (kind === 'uptime') { if (verb) throw new Error('usage'); const seconds = Math.floor((Date.now() - startedAt) / 1000); return { message: t('uptime', { minutes: Math.floor(seconds / 60), seconds: seconds % 60 }) }; } if (kind === 'version') { if (verb) throw new Error('usage'); return { message: `Life OS v${window.LifeOSVersion || '1.15.0'}` }; } if (kind === 'status') { if (verb) throw new Error('usage'); const data = board(); return { message: t('status', { memories: mems().length, goals: data.goals.length, tasks: data.tasks.length }) }; } if (kind === 'apps') { if (verb) throw new Error('usage'); return { message: Object.keys(APPS).map(id => `${id} — ${LifeI18n.t(`app.${id}`)}`).join('\n') }; } if (kind === 'history') { if (verb) throw new Error('usage'); return { message: history.slice(-20).map((entry,index) => `${index + 1}. ${entry}`).join('\n') || t('empty') }; } if (kind === 'echo') return { message: words(line.trim()).slice(1).join(' ') }; if (kind === 'console') return { message: t('consoleHelp') }; if (kind === 'open') { if (!Object.hasOwn(APPS, verb)) throw new Error('unknownApp'); openApp(verb); return { message: t('opened', { app: LifeI18n.t(`app.${verb}`) }) }; } if (kind === 'memory' && verb === 'add') { const { flags, values } = options(rest, ['date','tag','energy','weather','place','task','text']), title = values.join(' ').trim(); const draft = { title, date: date(flags.date) || localDate(0), tags: flags.tag || [], energy: flags.energy === undefined ? 3 : Number(flags.energy), mood: flags.weather || 'cloudy', location: flags.place || '', taskId: resolve(board().tasks, flags.task), text: flags.text || '' }; if (!validDay(draft.date)) throw new Error('invalidDate'); if (mems().some(item => item.date === draft.date) && (flags.date || flags.text !== undefined)) throw new Error('memoryExists'); if (!Number.isInteger(draft.energy) || draft.energy < 1 || draft.energy > 5 || !['clear','light','cloudy','rain','storm'].includes(draft.mood)) throw new Error('invalidValue'); if (flags.text === undefined) { wizard = { draft, step: 0, seeded: new Set([...(title ? ['title'] : []), ...Object.keys(flags).map(key => ({ tag: 'tags', weather: 'mood', place: 'location', task: 'taskId' }[key] || key))]) }; return nextStep(); } requireTitle(title); const memory = actions().createMemory(draft); return { message: t('memorySaved', { title: memory.title, date: memory.date }) }; } if (kind === 'memory' && verb === 'list') { const { flags, values } = options(rest, ['tag','query']); if (values.length) throw new Error('usage'); const found = mems().filter(item => (!flags.tag?.length || flags.tag.every(tag => (item.tags || []).includes(tag))) && (!flags.query || [item.title, item.text, item.location].join(' ').toLocaleLowerCase().includes(flags.query.toLocaleLowerCase()))); return { message: found.length ? found.slice(0, 30).map(item => `${item.date} ${item.title || item.text?.slice(0, 50) || '—'}`).join('\n') : t('empty') }; } if (kind === 'memory' && verb === 'delete') return startFlow('memory','delete',rest.join(' ')); if (kind === 'goal' && verb === 'add') { if (!rest.some(part => part.startsWith('--'))) return startFlow('goal','add',rest.join(' ')); const { flags, values } = options(rest, ['due','description','pin']); if (flags.pin && !flags.due) throw new Error('pinNeedsDate'); const goal = actions().createGoal({ title: requireTitle(values.join(' ')), targetDate: date(flags.due), description: flags.description || '', showOnDesktop: Boolean(flags.pin) }); return { message: t('goalSaved', { title: goal.title }) }; } if (kind === 'goal' && verb === 'list') { const goals = board().goals; return { message: goals.length ? goals.map((goal, index) => `${index + 1}. ${goal.done ? '✓' : '○'} ${goal.title}${goal.targetDate ? ` ${goal.targetDate}` : ''}`).join('\n') : t('empty') }; } if (kind === 'goal' && verb === 'delete') return startFlow('goal','delete',rest.join(' ')); if (kind === 'goal' && ['done','reopen'].includes(verb)) { if (!rest.length) throw new Error('usage'); const goal = actions().setGoalDone(findItem('goal',rest.join(' ')).id,verb === 'done'); return { message: t(verb === 'done' ? 'goalCompleted' : 'goalReopened', { title: goal.title }) }; } if (kind === 'task' && verb === 'add') { if (!rest.some(part => part.startsWith('--'))) return startFlow('task','add',rest.join(' ')); const { flags, values } = options(rest, ['due','goal','priority']); const task = actions().createTask({ title: requireTitle(values.join(' ')), dueDate: date(flags.due), goalId: resolve(board().goals, flags.goal), priority: flags.priority || 'normal' }); return { message: t('taskSaved', { title: task.title }) }; } if (kind === 'task' && verb === 'list') { const tasks = board().tasks; return { message: tasks.length ? tasks.map((task, index) => `${index + 1}. ${task.done ? '✓' : '○'} ${task.title}${task.dueDate ? ` ${task.dueDate}` : ''}`).join('\n') : t('empty') }; } if (kind === 'task' && verb === 'delete') return startFlow('task','delete',rest.join(' ')); if (kind === 'task' && ['done', 'reopen'].includes(verb)) { const index = Number(rest[0]); if (!Number.isInteger(index) || index < 1 || rest.length !== 1 || !board().tasks[index - 1]) throw new Error('notFound'); const task = actions().setTaskDone(board().tasks[index - 1].id, verb === 'done'); return { message: t(verb === 'done' ? 'taskCompleted' : 'taskReopened', { title: task.title }) }; } throw new Error('unknownCommand'); }; const finishWizard = () => { wizard = null; }; const cancelSession = () => { wizard = null; flow = null; }; const active = () => Boolean(wizard || flow); window.LifeOSTerminalEngine = { run, words, backStep, finishWizard, cancelSession, active }; const lines = [], history = []; let welcomed = false; let historyIndex = 0, compose = null, noteDraft = ''; const addLine = (value, kind = 'result') => { lines.push({ value, kind }); if (lines.length > 150) lines.shift(); }; const output = node => { const screen = node.querySelector('.terminal-output'); if (!screen) return; screen.replaceChildren(); for (const line of lines) { const pre = document.createElement('pre'); pre.className = `terminal-line ${line.kind}`; pre.textContent = line.value; screen.append(pre); } screen.scrollTop = screen.scrollHeight; }; const composer = (node, active) => { node.querySelector('.terminal-compose').hidden = !active; node.querySelector('.terminal-form').hidden = active; if (active) { node.querySelector('.terminal-note').value = noteDraft; node.querySelector('.terminal-note').focus(); } else node.querySelector('.terminal-input').focus(); }; window.terminalApp = node => { if (node.querySelector('.terminal-shell') && node.dataset.terminalLocale === lang()) { output(node); return; } if (!welcomed) { lines.unshift({ value: t('welcome', { version: window.LifeOSVersion || '1.15.0' }), kind: 'system' }); welcomed = true; } node.dataset.terminalLocale = lang(); node.innerHTML = `
Life OS / ${t('title')}${t('hint')}
`; output(node); composer(node, Boolean(compose)); node.querySelector('.terminal-form').addEventListener('submit', event => { event.preventDefault(); const input = node.querySelector('.terminal-input'), line = input.value.trim(); if (!line && !active()) return; if (line) history.push(line); historyIndex = history.length; input.value = ''; addLine(`lifeos > ${line || '↵'}`, 'command'); try { const result = run(line); if (result.clear) lines.length = 0; if (result.message) addLine(result.message); if (result.compose) { compose = result.compose; addLine(t('notePrompt')); composer(node, true); } if (result.cancel) { compose = null; noteDraft = ''; } } catch (error) { addLine(t(error.message in LifeI18n.dictionaries[lang() === 'zh' ? 'zh' : 'en'].terminal ? error.message : 'unknownCommand'), 'error'); } output(node); }); node.querySelector('.terminal-input').addEventListener('keydown', event => { const input = event.currentTarget; if (event.key === 'Escape' && active()) { event.preventDefault(); event.stopPropagation(); addLine(t('flow.cancelled')); cancelSession(); output(node); return; } if (event.key === 'ArrowUp' || event.key === 'ArrowDown') { event.preventDefault(); historyIndex = Math.max(0, Math.min(history.length, historyIndex + (event.key === 'ArrowUp' ? -1 : 1))); input.value = history[historyIndex] || ''; } if (event.key === 'Tab' && !active()) { const roots = ['help','memory add ','memory list','memory delete','goal add ','goal list','goal delete','task add ','task list','task delete','task done ','task reopen ','open ','clear']; const matches = roots.filter(item => item.startsWith(input.value)); if (matches.length === 1) { event.preventDefault(); input.value = matches[0]; } } }); node.querySelector('.terminal-shortcuts').addEventListener('click', event => { const command = event.target.closest('[data-command]')?.dataset.command; if (command) { const input = node.querySelector('.terminal-input'); input.value = command; input.focus(); } }); const saveNote = () => { try { const memory = actions().createMemory({ ...compose, text: node.querySelector('.terminal-note').value }); addLine(t('memorySaved', { title: memory.title, date: memory.date })); compose = null; noteDraft = ''; finishWizard(); composer(node, false); } catch (error) { addLine(t(error.message in LifeI18n.dictionaries[lang() === 'zh' ? 'zh' : 'en'].terminal ? error.message : 'unknownCommand'), 'error'); } output(node); }; const cancelNote = () => { compose = null; noteDraft = ''; finishWizard(); addLine(t('cancelled')); composer(node, false); output(node); }; node.querySelector('.terminal-compose').addEventListener('click', event => { const action = event.target.closest('[data-note]')?.dataset.note; if (action === 'save') saveNote(); if (action === 'cancel') cancelNote(); if (action === 'back') { compose = null; const result = backStep(); addLine(result.message); composer(node, false); output(node); } }); node.querySelector('.terminal-note').addEventListener('input', event => { noteDraft = event.currentTarget.value; }); node.querySelector('.terminal-note').addEventListener('keydown', event => { if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) { event.preventDefault(); saveNote(); } if (event.key === 'Escape') { event.stopPropagation(); cancelNote(); } }); }; const consoleOutput = result => { if (result.clear) lines.length = 0; if (result.message) addLine(result.message); if (result.compose) { compose = result.compose; addLine(t('notePrompt')); } if (result.cancel) { compose = null; noteDraft = ''; } const node = document.querySelector('#content-terminal'); if (node?.querySelector('.terminal-shell')) { output(node); if (result.compose || result.cancel) composer(node, Boolean(compose)); } const message = result.compose ? t('consoleNotePrompt') : result.message || (result.clear ? t('cleared') : ''); if (message) console.info('[Life OS]', message); return message; }; window.LifeOS = Object.freeze({ run(line) { const input = String(line || '').trim(); if (compose) { console.info('[Life OS]', t('consoleNotePrompt')); return t('consoleNotePrompt'); } if (!input && !active()) return t('usage'); try { if (input) history.push(input); addLine(`devtools > ${input || '↵'}`, 'command'); return consoleOutput(run(input)); } catch (error) { const key = Object.hasOwn(LifeI18n.dictionaries[lang() === 'zh' ? 'zh' : 'en'].terminal,error.message) ? error.message : 'unknownCommand'; return consoleOutput({ message: t(key) }); } }, help() { return this.run('help'); }, note(text) { if (!compose) { console.warn('[Life OS]', t('noNote')); return t('noNote'); } try { const memory = actions().createMemory({ ...compose, text: String(text || '') }); compose = null; noteDraft = ''; finishWizard(); const node = document.querySelector('#content-terminal'); if (node?.querySelector('.terminal-shell')) composer(node, false); return consoleOutput({ message: t('memorySaved', { title: memory.title, date: memory.date }) }); } catch (error) { const key = Object.hasOwn(LifeI18n.dictionaries[lang() === 'zh' ? 'zh' : 'en'].terminal,error.message) ? error.message : 'unknownCommand'; return consoleOutput({ message: t(key) }); } }, cancel() { cancelSession(); compose = null; noteDraft = ''; return consoleOutput({ message: t('flow.cancelled'), cancel: true }); } }); console.info('[Life OS]', t('consoleReady')); const style = document.createElement('style'); style.textContent = `#content-terminal{padding:0;overflow:hidden}.terminal-shell{height:100%;display:flex;flex-direction:column;background:#111a16;color:#d4efd2;font:13px/1.55 'DM Mono',ui-monospace,monospace}.terminal-header{display:flex;justify-content:space-between;gap:10px;padding:12px 15px;border-bottom:1px solid #375040;color:#a8ed9c}.terminal-header span{color:#94a899;font-size:11px}.terminal-output{flex:1;min-height:0;overflow:auto;padding:15px;white-space:pre-wrap;overflow-wrap:anywhere}.terminal-line{font:inherit;margin:0 0 10px;white-space:pre-wrap}.terminal-line.command,.terminal-line.system{color:#a8ed9c}.terminal-line.error{color:#ff9e99}.terminal-shortcuts{display:flex;gap:6px;overflow:auto;padding:7px 12px;border-top:1px solid #375040}.terminal-shortcuts button,.terminal-compose button,.terminal-form button{border:1px solid #456047;background:#233329;color:#d4efd2;border-radius:7px;padding:5px 9px;white-space:nowrap}.terminal-form{display:flex;align-items:center;gap:8px;border-top:1px solid #375040;padding:10px 14px}.terminal-form label{color:#a8ed9c;white-space:nowrap}.terminal-input{flex:1;min-width:0;background:transparent;color:#f0fff0;border:0;outline:none;font:inherit}.terminal-compose{border-top:1px solid #375040;padding:12px;display:grid;gap:8px}.terminal-compose[hidden],.terminal-form[hidden]{display:none}.terminal-note{width:100%;resize:vertical;background:#1c2a21;color:#f0fff0;border:1px solid #456047;border-radius:7px;padding:10px;font:inherit}.terminal-compose>div{display:flex;gap:8px}@media(max-width:650px){.terminal-header{display:block}.terminal-header span{display:block}.terminal-shell{font-size:12px}}`; document.head.append(style); })();