266 lines
16 KiB
HTML
266 lines
16 KiB
HTML
<style>
|
||
#lime-workstation-study {
|
||
--ls-base: #1c2022; --ls-panel: #22282a; --ls-raised: #2b3435;
|
||
--ls-fg: #d3d9d2; --ls-muted: #9ca9a4; --ls-line: #4c5957;
|
||
--ls-accent: #bfd2ab; --ls-secondary: #a6b8cd; --ls-string: #c9b99b;
|
||
--ls-chrome-font: "Dina", "Lucida Console", ui-monospace, monospace;
|
||
color-scheme: dark; color: var(--ls-fg); background: var(--ls-base);
|
||
font: 13px/1.6 ui-monospace, "DejaVu Sans Mono", monospace;
|
||
width: 100%; box-sizing: border-box;
|
||
}
|
||
#lime-workstation-study *, #lime-workstation-study *::before { box-sizing: border-box; }
|
||
#lime-workstation-study button, #lime-workstation-study input { font: inherit; }
|
||
#lime-workstation-study button { color: inherit; cursor: pointer; }
|
||
#lime-workstation-study button:focus-visible, #lime-workstation-study input:focus-visible {
|
||
outline: 2px solid var(--ls-accent); outline-offset: 2px;
|
||
}
|
||
#lime-workstation-study .ls-bar {
|
||
display: flex; align-items: stretch; flex-wrap: wrap; min-height: 36px;
|
||
border-bottom: 1px solid var(--ls-line); font-family: var(--ls-chrome-font);
|
||
}
|
||
#lime-workstation-study .ls-bar button { border: 0; background: transparent; padding: 7px 12px; }
|
||
#lime-workstation-study .ls-bar button:hover, #lime-workstation-study .ls-bar button[aria-expanded="true"] {
|
||
background: var(--ls-raised); color: var(--ls-accent);
|
||
}
|
||
#lime-workstation-study .ls-brand { color: var(--ls-accent); font-weight: 700; }
|
||
#lime-workstation-study .ls-divider { align-self: center; color: var(--ls-line); }
|
||
#lime-workstation-study .ls-layout { padding: 7px 12px; }
|
||
#lime-workstation-study .ls-bar-title {
|
||
flex: 1 1 150px; padding: 7px 14px; background: var(--ls-panel);
|
||
border-inline: 1px solid var(--ls-line); overflow-wrap: anywhere;
|
||
}
|
||
#lime-workstation-study .ls-time { padding: 7px 14px; color: var(--ls-muted); }
|
||
#lime-workstation-study .ls-main { position: relative; }
|
||
#lime-workstation-study .ls-desk {
|
||
display: grid; grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
|
||
gap: 8px; padding: 12px; min-height: 480px;
|
||
}
|
||
#lime-workstation-study .ls-window {
|
||
min-width: 0; display: flex; flex-direction: column;
|
||
background: var(--ls-base); border: 1px solid var(--ls-line);
|
||
}
|
||
#lime-workstation-study .ls-editor { border-top: 2px solid var(--ls-accent); }
|
||
#lime-workstation-study .ls-side { display: grid; grid-template-rows: 0.9fr 1.1fr; gap: 8px; min-width: 0; }
|
||
#lime-workstation-study .ls-content { padding: 16px 18px; flex: 1; overflow-wrap: anywhere; }
|
||
#lime-workstation-study .ls-file-context { margin-bottom: 24px; color: var(--ls-muted); }
|
||
#lime-workstation-study .ls-code { white-space: pre-wrap; margin: 0; font: inherit; overflow-wrap: anywhere; }
|
||
#lime-workstation-study .ls-keyword { color: var(--ls-accent); }
|
||
#lime-workstation-study .ls-name { color: var(--ls-secondary); }
|
||
#lime-workstation-study .ls-string { color: var(--ls-string); }
|
||
#lime-workstation-study .ls-comment, #lime-workstation-study .ls-muted { color: var(--ls-muted); }
|
||
#lime-workstation-study .ls-selection { background: var(--ls-raised); }
|
||
#lime-workstation-study .ls-cursor {
|
||
display: inline-block; height: 1.15em; width: 0.58em; vertical-align: -0.15em;
|
||
border: 1px solid var(--ls-accent);
|
||
}
|
||
#lime-workstation-study .ls-modeline {
|
||
display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 12px;
|
||
padding: 4px 10px; background: var(--ls-panel); color: var(--ls-muted);
|
||
border-top: 1px solid var(--ls-line); font-family: var(--ls-chrome-font); font-size: 12px;
|
||
}
|
||
#lime-workstation-study .ls-editor .ls-modeline { color: var(--ls-fg); }
|
||
#lime-workstation-study .ls-repl-line { margin: 0 0 14px; }
|
||
#lime-workstation-study .ls-object {
|
||
background: none; border: 0; border-bottom: 1px dotted var(--ls-accent);
|
||
padding: 0; color: var(--ls-accent); overflow-wrap: anywhere; text-align: left;
|
||
}
|
||
#lime-workstation-study .ls-object-detail {
|
||
margin: 10px 0; border-left: 2px solid var(--ls-line); padding-left: 12px;
|
||
color: var(--ls-muted); white-space: pre-wrap;
|
||
}
|
||
#lime-workstation-study .ls-shell { background: var(--ls-terminal-bg, var(--ls-base)); color: var(--ls-terminal-fg, var(--ls-fg)); }
|
||
#lime-workstation-study .ls-shell .ls-prompt { color: var(--ls-terminal-prompt, var(--ls-accent)); }
|
||
#lime-workstation-study .ls-shell .ls-comment { color: var(--ls-terminal-muted, var(--ls-muted)); }
|
||
#lime-workstation-study .ls-menu {
|
||
position: absolute; top: 8px; left: 12px; right: 12px; z-index: 3;
|
||
border: 1px solid var(--ls-accent); background: var(--ls-panel);
|
||
box-shadow: 0 12px 24px #00000050;
|
||
}
|
||
#lime-workstation-study [hidden] { display: none !important; }
|
||
#lime-workstation-study .ls-menu-header {
|
||
display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
|
||
padding: 8px 12px; background: var(--ls-raised); border-bottom: 1px solid var(--ls-line);
|
||
}
|
||
#lime-workstation-study .ls-menu-path { color: var(--ls-accent); flex: 1; }
|
||
#lime-workstation-study .ls-menu-header button { padding: 2px 8px; background: transparent; border: 1px solid var(--ls-line); }
|
||
#lime-workstation-study .ls-command-list { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); padding: 10px; gap: 6px; }
|
||
#lime-workstation-study .ls-command {
|
||
display: flex; align-items: center; gap: 10px; text-align: left;
|
||
padding: 10px; border: 1px solid transparent; background: transparent; overflow-wrap: anywhere;
|
||
}
|
||
#lime-workstation-study .ls-command:hover { border-color: var(--ls-line); background: var(--ls-raised); }
|
||
#lime-workstation-study .ls-command kbd { color: var(--ls-accent); min-width: 22px; font: inherit; }
|
||
#lime-workstation-study .ls-command .ls-branch { color: var(--ls-muted); margin-left: auto; }
|
||
#lime-workstation-study .ls-menu-search { width: calc(100% - 24px); margin: 12px 12px 0; padding: 8px; border: 1px solid var(--ls-line); background: var(--ls-base); color: var(--ls-fg); }
|
||
#lime-workstation-study .ls-footer { padding: 4px 14px 9px; color: var(--ls-muted); font-size: 12px; }
|
||
#lime-workstation-study .ls-menu-status { margin: 0; padding: 6px 14px; border-top: 1px solid var(--ls-line); color: var(--ls-muted); font-size: 12px; }
|
||
@media (max-width: 680px) {
|
||
#lime-workstation-study .ls-desk { grid-template-columns: minmax(0,1fr); }
|
||
#lime-workstation-study .ls-bar-title { flex-basis: 65%; }
|
||
#lime-workstation-study .ls-content { padding: 14px; }
|
||
#lime-workstation-study .ls-command-list { grid-template-columns: repeat(2, minmax(0,1fr)); }
|
||
#lime-workstation-study .ls-file-context { margin-bottom: 14px; }
|
||
}
|
||
@media (pointer: coarse) {
|
||
#lime-workstation-study button { min-height: 44px; }
|
||
#lime-workstation-study input { min-height: 44px; font-size: 16px; }
|
||
}
|
||
</style>
|
||
|
||
<div id="lime-workstation-study" aria-label="Lime alternate-present workstation design study">
|
||
<header class="ls-bar" aria-label="Lime head bar">
|
||
<button class="ls-brand" type="button" data-menu="root" aria-expanded="false" aria-controls="ls-command-menu">Lime</button>
|
||
<span class="ls-divider" aria-hidden="true">|</span>
|
||
<button type="button" data-menu="views" aria-label="Choose a view">WM project:<span data-view>Code</span>*</button>
|
||
<span class="ls-divider" aria-hidden="true">|</span>
|
||
<span class="ls-layout" aria-label="Master stack layout">[]=</span>
|
||
<span class="ls-bar-title">layout.lisp — Emacs</span>
|
||
<span class="ls-time">15:42</span>
|
||
</header>
|
||
<main class="ls-main">
|
||
<div class="ls-desk">
|
||
<section class="ls-window ls-editor" aria-label="Emacs editor with matching theme">
|
||
<div class="ls-content">
|
||
<div class="ls-file-context">lime / src / layout.lisp</div>
|
||
<pre class="ls-code"><span class="ls-comment">;;; Arrangements are values.</span>
|
||
<span class="ls-comment">;;; The activity remains yours.</span>
|
||
|
||
(<span class="ls-keyword">defun</span> <span class="ls-name">remember-arrangement</span> (world head name)
|
||
(<span class="ls-keyword">let</span> ((draft (current-presentation world head)))
|
||
(<span class="ls-selection">save-view world draft name</span>)))
|
||
|
||
(<span class="ls-keyword">defun</span> <span class="ls-name">return-to-code</span> (world head)
|
||
(<span class="ls-keyword">let</span> ((view (find-view world <span class="ls-string">"wm-project/code"</span>)))
|
||
(activate-view-strictly world head view)))
|
||
|
||
<span class="ls-comment">;; Keep the work; change the perspective.</span>
|
||
<span class="ls-cursor" aria-label="Editor cursor"></span></pre>
|
||
</div>
|
||
<footer class="ls-modeline"><span>** layout.lisp</span><span>Lisp · 8:5</span></footer>
|
||
</section>
|
||
<div class="ls-side">
|
||
<section class="ls-window ls-shell" aria-label="Terminal">
|
||
<div class="ls-content">
|
||
<pre class="ls-code"><span class="ls-prompt">~/code/lime ›</span> git status --short
|
||
M src/layout.lisp
|
||
M test/model.lisp
|
||
|
||
<span class="ls-comment">master · 2 modified files</span>
|
||
|
||
<span class="ls-prompt">~/code/lime ›</span> <span class="ls-cursor" aria-label="Terminal cursor"></span></pre>
|
||
</div>
|
||
<footer class="ls-modeline"><span>st · ~/code/lime</span><span>shell</span></footer>
|
||
</section>
|
||
<section class="ls-window" aria-label="SLY listener concept">
|
||
<div class="ls-content">
|
||
<p class="ls-repl-line"><span class="ls-keyword">LIME.CORE></span> (current-presentation world head)</p>
|
||
<button type="button" class="ls-object" aria-expanded="false" aria-controls="ls-inspected-object">#<PRESENTATION WM project:Code*></button>
|
||
<div id="ls-inspected-object" class="ls-object-detail" hidden>activity WM project
|
||
source-view Code
|
||
dirty-p T
|
||
clients (editor terminal browser)</div>
|
||
<p class="ls-repl-line ls-comment" style="margin-top:18px">; 248 checks passed.</p>
|
||
<div><span class="ls-keyword">LIME.CORE></span> <span class="ls-cursor" aria-label="Listener cursor"></span></div>
|
||
</div>
|
||
<footer class="ls-modeline"><span>SLY · listener</span><span>SBCL · local</span></footer>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
<section id="ls-command-menu" class="ls-menu" aria-label="Command menu concept" hidden>
|
||
<header class="ls-menu-header">
|
||
<span class="ls-menu-path">Lime</span>
|
||
<button type="button" data-back hidden>Back</button>
|
||
<button type="button" data-close>Esc · Close</button>
|
||
</header>
|
||
<input class="ls-menu-search" type="search" aria-label="Search commands" placeholder="Search commands…" hidden>
|
||
<div class="ls-command-list"></div>
|
||
<p class="ls-menu-status" aria-live="polite">Choose a branch.</p>
|
||
</section>
|
||
</main>
|
||
<footer class="ls-footer">Concept study · Slate / celadon · <span data-footer-hint>Lime opens commands; the listener value is inspectable.</span></footer>
|
||
</div>
|
||
<script>
|
||
(() => {
|
||
const root = document.getElementById('lime-workstation-study');
|
||
const state = { palette: 'celadon', matchedTerminal: true, type: 'bitmap' };
|
||
const palettes = {
|
||
celadon: { label: 'Slate / celadon', base: '#1c2022', panel: '#22282a', raised: '#2b3435', fg: '#d3d9d2', muted: '#9ca9a4', line: '#4c5957', accent: '#bfd2ab', secondary: '#a6b8cd', string: '#c9b99b' },
|
||
violet: { label: 'Graphite / violet', base: '#1d1d22', panel: '#25252c', raised: '#30303a', fg: '#d6d5df', muted: '#a4a1b3', line: '#555360', accent: '#bcb0d5', secondary: '#9ebdc1', string: '#c6bba4' },
|
||
ice: { label: 'Ink / ice', base: '#192127', panel: '#222d34', raised: '#2c3c46', fg: '#d1dce0', muted: '#9cafb9', line: '#49616e', accent: '#a8cddd', secondary: '#b5c9b5', string: '#ccbd9e' }
|
||
};
|
||
function renderDesign() {
|
||
const p = palettes[state.palette];
|
||
for (const [key, value] of Object.entries(p)) if (key !== 'label') root.style.setProperty('--ls-' + key, value);
|
||
root.style.setProperty('--ls-chrome-font', state.type === 'bitmap' ? '"Dina", "Lucida Console", ui-monospace, monospace' : 'ui-monospace, "DejaVu Sans Mono", monospace');
|
||
root.querySelector('.ls-footer').firstChild.textContent = 'Concept study · ' + p.label + ' · ';
|
||
const terminal = root.querySelector('.ls-shell');
|
||
for (const [key, value] of Object.entries({ bg: '#060a0c', fg: '#aaca66', prompt: '#e7c77c', muted: '#78a7bd' })) {
|
||
if (state.matchedTerminal) terminal.style.removeProperty('--ls-terminal-' + key);
|
||
else terminal.style.setProperty('--ls-terminal-' + key, value);
|
||
}
|
||
}
|
||
const menu = root.querySelector('.ls-menu');
|
||
const list = root.querySelector('.ls-command-list');
|
||
const search = root.querySelector('.ls-menu-search');
|
||
const back = root.querySelector('[data-back]');
|
||
const status = root.querySelector('.ls-menu-status');
|
||
const branches = {
|
||
root: [['a', 'Activities', 'activities'], ['v', 'Views', 'views'], ['w', 'Windows', 'windows'], ['l', 'Layout', 'layout'], ['u', 'User', 'user'], ['Space', 'Search commands', 'search']],
|
||
activities: [['s', 'Switch activity'], ['n', 'New activity']],
|
||
views: [['c', 'Code', null, 'Code'], ['d', 'Debug', null, 'Debug'], ['o', 'Docs', null, 'Docs'], ['s', 'Save checkpoint']],
|
||
windows: [['h', 'Hide window'], ['i', 'Show hidden window'], ['f', 'Float / tile'], ['c', 'Close window']],
|
||
layout: [['t', 'Master / stack'], ['m', 'Monocle'], ['f', 'Floating'], ['a', 'Manual']],
|
||
user: [['r', 'Reload configuration'], ['t', 'Open terminal']]
|
||
};
|
||
let branch = 'root';
|
||
function showBranch(name) {
|
||
branch = name;
|
||
menu.hidden = false;
|
||
root.querySelector('.ls-brand').setAttribute('aria-expanded', 'true');
|
||
root.querySelector('.ls-menu-path').textContent = name === 'root' ? 'Lime' : 'Lime › ' + name[0].toUpperCase() + name.slice(1);
|
||
back.hidden = name === 'root';
|
||
search.hidden = name !== 'search';
|
||
status.textContent = name === 'root' ? 'Choose a branch.' : 'Concept interactions only; your running session is unchanged.';
|
||
renderCommands();
|
||
if (name === 'search') search.focus();
|
||
}
|
||
function renderCommands() {
|
||
list.replaceChildren();
|
||
const options = branch === 'search' ? Object.entries(branches).filter(([key]) => key !== 'root').flatMap(([, items]) => items).filter(item => item[1].toLowerCase().includes(search.value.toLowerCase())) : branches[branch];
|
||
for (const item of options) {
|
||
const button = document.createElement('button');
|
||
button.type = 'button'; button.className = 'ls-command';
|
||
const key = document.createElement('kbd'); key.textContent = item[0];
|
||
const name = document.createElement('span'); name.textContent = item[1];
|
||
button.append(key, name);
|
||
if (item[2]) { const marker = document.createElement('span'); marker.className = 'ls-branch'; marker.textContent = '+'; button.append(marker); }
|
||
button.addEventListener('click', () => {
|
||
if (item[2]) showBranch(item[2]);
|
||
else if (item[3]) { root.querySelector('[data-view]').textContent = item[3]; status.textContent = item[3] + ' selected in this study.'; }
|
||
else status.textContent = item[1] + ' — command location preview only.';
|
||
});
|
||
list.append(button);
|
||
}
|
||
}
|
||
function closeMenu() { menu.hidden = true; root.querySelector('.ls-brand').setAttribute('aria-expanded', 'false'); root.querySelector('.ls-brand').focus(); }
|
||
root.querySelectorAll('[data-menu]').forEach(button => button.addEventListener('click', () => {
|
||
if (!menu.hidden && branch === button.dataset.menu) closeMenu(); else showBranch(button.dataset.menu);
|
||
}));
|
||
root.querySelector('[data-close]').addEventListener('click', closeMenu);
|
||
back.addEventListener('click', () => showBranch('root'));
|
||
search.addEventListener('input', renderCommands);
|
||
root.addEventListener('keydown', event => { if (event.key === 'Escape' && !menu.hidden) { event.preventDefault(); closeMenu(); } });
|
||
root.querySelector('.ls-object').addEventListener('click', event => {
|
||
const detail = root.querySelector('#ls-inspected-object'); detail.hidden = !detail.hidden;
|
||
event.currentTarget.setAttribute('aria-expanded', String(!detail.hidden));
|
||
});
|
||
renderDesign();
|
||
if (globalThis.Tweak) {
|
||
const tweak = new Tweak({ container: root, onChange: renderDesign });
|
||
tweak.addSelect(state, 'palette', { label: 'Color direction', options: [{ label: 'Slate / celadon', value: 'celadon' }, { label: 'Graphite / violet', value: 'violet' }, { label: 'Ink / ice', value: 'ice' }] });
|
||
tweak.addToggle(state, 'matchedTerminal', { label: 'Terminal follows the environment' });
|
||
tweak.addSelect(state, 'type', { label: 'Chrome typography', options: [{ label: 'Dina when locally available', value: 'bitmap' }, { label: 'System monospace', value: 'mono' }] });
|
||
}
|
||
})();
|
||
</script>
|