diff --git a/web/app.js b/web/app.js
index 40ed2b0..79b8032 100644
--- a/web/app.js
+++ b/web/app.js
@@ -28,6 +28,10 @@
activeMonth: null,
intent: 'grab',
flashId: null,
+ peekMode: 'preview',
+ runChecked: new Set(),
+ fillValues: {},
+ fillActive: 0,
};
const $ = (sel) => document.querySelector(sel);
@@ -536,109 +540,348 @@
`;
}
+ function fmtDateLong(dateStr) {
+ const d = new Date(dateStr);
+ const months = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
+ return `${months[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()} · ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
+ }
+
function renderDetailPane() {
const pane = $('#detail-pane');
const e = state.entities[state.selectedIndex];
if (!e) {
- pane.innerHTML = '
-
- ${descHtml}
- ${titleHtml}
-
${escHtml(e.body)}
- ${tags ? `
${tags}
` : ''}
- ${cardContent}
-
${actions}
+ return `
`;
}
- function renderCardContent(e) {
- if (!e.card_data) return '';
- let data;
- try { data = JSON.parse(e.card_data); } catch { return ''; }
+ function renderCardPeek(e) {
+ const glyph = GLYPHS[e.card_type] || '◆';
+ const gc = GLYPH_CLASSES[e.card_type] || 'glyph-snippet';
+ const affs = detectAffordances(e);
+ const data = e.card_data ? (() => { try { return JSON.parse(e.card_data); } catch { return {}; } })() : {};
+ const tags = (e.tags || []).map(t => `
#${t} `).join('');
+ const affHtml = affs.map(a => `
${AFF_LABELS[a]} `).join('');
+ const hasSteps = data.steps && data.steps.length;
+ const hasDecision = data.chose != null;
+ const hasFill = /\$\{[^}]+\}/.test(e.body || '');
+ const hasLink = !!data.url;
- switch (e.card_type) {
- case 'template':
- if (!data.slots || !data.slots.length) return '';
- return `
`;
+ let sections = '';
- case 'checklist':
- if (!data.steps || !data.steps.length) return '';
- return `
- ${data.steps.map((s, i) => `
-
-
- ${escHtml(s.text)}
-
- `).join('')}
-
`;
-
- case 'decision':
- return `
-
chose
${escHtml(data.chose || '—')}
-
why
${escHtml(data.why || '—')}
- ${data.rejected && data.rejected.length ? `
rejected
${data.rejected.map(escHtml).join(', ') || '—'}
` : ''}
-
`;
-
- case 'link':
- if (data.url && isSafeUrl(data.url)) {
- return `
- open link
-
`;
- }
- return '';
-
- default:
- return '';
+ if (hasDecision) {
+ const rejected = (data.rejected || []).map(r => `
${escHtml(r)} `).join('');
+ sections += `
+
decision${data.status || 'decided'}
+
+
${escHtml(data.chose)}
+
why ${escHtml(data.why || '')}
+ ${rejected ? `
` : ''}
+
+
`;
}
+
+ if (hasLink && !hasDecision) {
+ sections += `
`;
+ }
+
+ if (hasSteps) {
+ const steps = data.steps.map((s, i) => `
○ ${escHtml(s.text || s)}
`).join('');
+ sections += `
+
steps · ${data.steps.length}▶ run
+
+
`;
+ }
+
+ if (!hasDecision && e.body) {
+ const lang = data.lang || '';
+ sections += `
+
content${lang ? `${lang} ` : ''}${hasFill ? `⤓ fill ` : ''}
+
+
`;
+ }
+
+ let actions = `
copy ⏎ `;
+ if (hasFill) actions += `
fill f `;
+ if (hasSteps) actions += `
run r `;
+ actions += `
edit e `;
+ actions += `
${e.pinned ? 'unpin' : 'pin'} p `;
+ actions += `
delete `;
+
+ return `
`;
+ }
+
+ function renderRunMode(e) {
+ const data = e.card_data ? (() => { try { return JSON.parse(e.card_data); } catch { return {}; } })() : {};
+ if (!data.steps) return renderCardPeek(e);
+ const total = data.steps.length;
+ const checked = state.runChecked || new Set();
+ const done = checked.size;
+ const pct = total > 0 ? Math.round(done / total * 100) : 0;
+
+ const steps = data.steps.map((s, i) => {
+ const isDone = checked.has(i);
+ const text = s.text || s;
+ return `
+ ${isDone ? '●' : '○'}
+ ${escHtml(text)}
+
`;
+ }).join('');
+
+ return `
`;
+ }
+
+ function renderFillMode(e) {
+ const slots = [];
+ const re = /\$\{([^}]+)\}/g;
+ let m;
+ const seen = new Set();
+ while ((m = re.exec(e.body || '')) !== null) {
+ const name = m[1].trim();
+ if (!seen.has(name)) { seen.add(name); slots.push(name); }
+ }
+ if (!slots.length) return renderCardPeek(e);
+ const fill = state.fillValues || {};
+ const active = state.fillActive || 0;
+
+ let content = escHtml(e.body);
+ for (const name of slots) {
+ const val = fill[name] || '';
+ const idx = slots.indexOf(name);
+ const cls = idx === active ? 'fill-slot active' : (val ? 'fill-slot filled' : 'fill-slot');
+ const width = Math.max(name.length, val.length, 4) * 8 + 16;
+ content = content.replace(`\${${name}}`, `
`);
+ }
+
+ const allFilled = slots.every(s => fill[s]);
+
+ return `
`;
+ }
+
+ function renderEditMode(e) {
+ return `
`;
+ }
+
+ function bindPeekEvents(e) {
+ const pane = $('#detail-pane');
+ if (!e) return;
+
+ if (state.peekMode === 'run') {
+ pane.querySelectorAll('.peek-run-step').forEach(el => {
+ el.addEventListener('click', () => {
+ const idx = parseInt(el.dataset.step);
+ if (!state.runChecked) state.runChecked = new Set();
+ if (state.runChecked.has(idx)) state.runChecked.delete(idx);
+ else state.runChecked.add(idx);
+ renderDetailPane();
+ });
+ });
+ }
+
+ if (state.peekMode === 'fill') {
+ pane.querySelectorAll('.fill-slot input').forEach(input => {
+ input.addEventListener('input', () => {
+ if (!state.fillValues) state.fillValues = {};
+ state.fillValues[input.dataset.slot] = input.value;
+ });
+ input.addEventListener('focus', () => {
+ state.fillActive = parseInt(input.dataset.idx);
+ });
+ input.addEventListener('keydown', (ev) => {
+ if (ev.key === 'Tab') {
+ ev.preventDefault();
+ const slots = pane.querySelectorAll('.fill-slot input');
+ const cur = parseInt(input.dataset.idx);
+ const next = ev.shiftKey ? Math.max(0, cur - 1) : Math.min(slots.length - 1, cur + 1);
+ state.fillActive = next;
+ renderDetailPane();
+ setTimeout(() => {
+ const el = pane.querySelector(`.fill-slot input[data-idx="${next}"]`);
+ if (el) el.focus();
+ }, 0);
+ } else if (ev.key === 'Enter' && !ev.shiftKey) {
+ ev.preventDefault();
+ nibApp.completeFill();
+ } else if (ev.key === 'Escape') {
+ ev.preventDefault();
+ nibApp.exitMode();
+ }
+ });
+ });
+ setTimeout(() => {
+ const el = pane.querySelector(`.fill-slot input[data-idx="${state.fillActive || 0}"]`);
+ if (el) el.focus();
+ }, 0);
+ }
+
+ if (state.peekMode === 'edit') {
+ const bodyTa = pane.querySelector('#edit-body');
+ if (bodyTa) {
+ bodyTa.addEventListener('keydown', (ev) => {
+ if (ev.key === 'Enter' && (ev.metaKey || ev.ctrlKey)) { ev.preventDefault(); nibApp.saveEdit(e.id); }
+ if (ev.key === 'Escape') { ev.preventDefault(); nibApp.exitMode(); }
+ });
+ }
+ }
+
+ // Double-click to edit (stream peek)
+ const titleEl = pane.querySelector('.peek-title[data-id]');
+ if (titleEl) titleEl.addEventListener('dblclick', () => startEditField('title'));
+ const bodyEl = pane.querySelector('.peek-body[data-id]');
+ if (bodyEl) bodyEl.addEventListener('dblclick', startEditBody);
}
// ========== Inline edit ==========
@@ -713,6 +956,10 @@
function selectEntity(idx) {
state.selectedIndex = idx;
+ state.peekMode = 'preview';
+ state.runChecked = new Set();
+ state.fillValues = {};
+ state.fillActive = 0;
renderEntityList();
renderDetailPane();
}
@@ -932,6 +1179,67 @@
await loadEntities();
selectEntity(state.entities.findIndex(x => x.id === id));
},
+
+ enterMode(mode) {
+ state.peekMode = mode;
+ if (mode === 'run') state.runChecked = new Set();
+ if (mode === 'fill') { state.fillValues = {}; state.fillActive = 0; }
+ renderDetailPane();
+ },
+
+ exitMode() {
+ state.peekMode = 'preview';
+ renderDetailPane();
+ },
+
+ resetRun() {
+ state.runChecked = new Set();
+ renderDetailPane();
+ },
+
+ async completeFill() {
+ const e = state.entities[state.selectedIndex];
+ if (!e) return;
+ let resolved = e.body || '';
+ const fill = state.fillValues || {};
+ for (const [name, val] of Object.entries(fill)) {
+ resolved = resolved.replace(new RegExp('\\$\\{' + name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\}', 'g'), val);
+ }
+ try {
+ await navigator.clipboard.writeText(resolved);
+ await api.useEntity(e.id);
+ state.peekMode = 'preview';
+ await loadEntities();
+ showToast('copied resolved');
+ } catch (err) {
+ console.error('clipboard:', err);
+ }
+ },
+
+ async saveEdit(id) {
+ const title = ($('#edit-title') || {}).value || null;
+ const desc = ($('#edit-desc') || {}).value || null;
+ const body = ($('#edit-body') || {}).value || '';
+ const tagsStr = ($('#edit-tags') || {}).value || '';
+ const tags = tagsStr.split(/\s+/).filter(Boolean);
+ await api.updateEntity(id, { body, title, description: desc, tags });
+ state.peekMode = 'preview';
+ await loadEntities();
+ await loadTags();
+ const idx = state.entities.findIndex(x => x.id === id);
+ if (idx >= 0) selectEntity(idx);
+ showToast('saved');
+ },
+
+ async togglePin(id) {
+ const e = state.entities.find(x => x.id === id);
+ if (!e) return;
+ await api.updateEntity(id, { pinned: !e.pinned });
+ await loadEntities();
+ const idx = state.entities.findIndex(x => x.id === id);
+ if (idx >= 0) { state.selectedIndex = idx; renderEntityList(); renderDetailPane(); }
+ showToast(e.pinned ? 'unpinned' : 'pinned');
+ },
};
// ========== Promote modal ==========
@@ -977,6 +1285,13 @@
return;
}
+ if (state.peekMode !== 'preview' && ev.key === 'Escape') {
+ nibApp.exitMode();
+ return;
+ }
+
+ const sel = state.entities[state.selectedIndex];
+
switch (ev.key) {
case 'j':
ev.preventDefault();
@@ -992,36 +1307,42 @@
ev.preventDefault();
$('#capture-input').focus();
break;
- case 'p': {
- const e = state.entities[state.selectedIndex];
- if (e && !e.card_type) nibApp.showPromote(e.id);
+ case 'p':
+ if (sel && sel.card_type && state.view === 'cards') {
+ nibApp.togglePin(sel.id);
+ } else if (sel && !sel.card_type) {
+ nibApp.showPromote(sel.id);
+ }
break;
- }
- case 'Enter': {
- const e = state.entities[state.selectedIndex];
- if (e) nibApp.copyEntity(e.id);
+ case 'Enter':
+ if (sel) nibApp.copyEntity(sel.id);
+ break;
+ case 'r':
+ if (sel && sel.card_type && state.view === 'cards') nibApp.enterMode('run');
+ break;
+ case 'f':
+ if (sel && sel.card_type && state.view === 'cards') nibApp.enterMode('fill');
+ break;
+ case 'e':
+ if (sel && sel.card_type && state.view === 'cards') {
+ nibApp.enterMode('edit');
+ } else {
+ startEditBody();
+ }
break;
- }
case 'd': {
const now = Date.now();
if (now - lastDTime < 400) {
- const e = state.entities[state.selectedIndex];
- if (e) nibApp.deleteEntity(e.id);
+ if (sel) nibApp.deleteEntity(sel.id);
lastDTime = 0;
} else {
lastDTime = now;
}
break;
}
- case 'e': {
- startEditBody();
+ case 'a':
+ if (sel && !sel.card_type) nibApp.showAbsorb(sel.id);
break;
- }
- case 'a': {
- const e = state.entities[state.selectedIndex];
- if (e && !e.card_type) nibApp.showAbsorb(e.id);
- break;
- }
case '1': switchView('stream'); break;
case '2': switchView('cards'); break;
}
diff --git a/web/style.css b/web/style.css
index 9f37905..21a35be 100644
--- a/web/style.css
+++ b/web/style.css
@@ -660,7 +660,7 @@ main {
letter-spacing: .04em;
}
-/* ── DETAIL PANE ────────────────────────────────────── */
+/* ── PEEK PANE ──────────────────────────────────────── */
#detail-pane {
background: var(--surf);
border-left: 1px solid var(--border);
@@ -669,10 +669,11 @@ main {
overflow: hidden;
}
-.detail-scroll {
+.peek-scroll {
flex: 1;
overflow-y: auto;
- padding: 20px;
+ display: flex;
+ flex-direction: column;
}
.detail-empty {
@@ -683,49 +684,283 @@ main {
font-family: var(--mono);
}
-.detail-header {
+/* peek idle */
+.peek-idle {
+ padding: 18px;
display: flex;
- align-items: center;
- gap: 8px;
- margin-bottom: 16px;
+ flex-direction: column;
+ gap: 14px;
+ flex: 1;
+ overflow-y: auto;
}
-.detail-glyph { font-size: 16px; }
-
-.detail-id {
+.peek-idle-eyebrow {
font-family: var(--mono);
- font-size: 10px;
- color: var(--dim);
+ font-size: 9px;
+ text-transform: uppercase;
+ letter-spacing: .16em;
+ color: var(--accent);
+ margin-bottom: 6px;
}
-.detail-desc {
- font-family: var(--sans);
- font-size: 11px;
- color: var(--muted);
- margin-bottom: 4px;
- cursor: text;
- padding: 2px 6px;
- margin-left: -6px;
- border-radius: var(--r2);
- transition: background var(--t-fast);
-}
-
-.detail-desc:hover { background: var(--raised); }
-
-.detail-title {
+.peek-idle-title {
font-family: var(--sans);
font-size: 15px;
font-weight: 600;
- margin-bottom: 12px;
+ color: var(--text);
+ margin-bottom: 4px;
+}
+
+.peek-idle-sub {
+ font-family: var(--sans);
+ font-size: 12px;
+ color: var(--muted);
+ line-height: 1.55;
+}
+
+.peek-shortcuts { display: flex; flex-direction: column; gap: 10px; }
+.peek-sc-sec { margin-bottom: 2px; }
+.peek-sc-lbl { font-family: var(--mono); font-size: 9px; text-transform: uppercase; letter-spacing: .14em; color: var(--dim); margin-bottom: 5px; }
+.peek-sc-row { display: flex; align-items: center; gap: 5px; padding: 2px 0; font-family: var(--mono); font-size: 11px; color: var(--muted); }
+.peek-sc-row span { color: var(--dim); margin-left: 2px; }
+.peek-sc-code { font-family: var(--mono); font-size: 10px; color: var(--accent); background: var(--bg); border: 1px solid var(--border); border-radius: var(--r2); padding: 4px 8px; margin-bottom: 5px; }
+.peek-sc-hint { font-family: var(--mono); font-size: 9px; color: var(--dim); padding-bottom: 3px; }
+kbd { background: var(--raised); border: 1px solid var(--border); border-radius: 2px; padding: 1px 4px; font-size: 9px; font-family: var(--mono); color: var(--muted); display: inline-block; line-height: 1.4; }
+
+/* peek eyebrow */
+.peek-brow {
+ padding: 14px 20px 0;
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ flex-shrink: 0;
+ font-family: var(--mono);
+ font-size: 9px;
+ letter-spacing: .12em;
+ text-transform: uppercase;
+ color: var(--dim);
+}
+
+.peek-brow-g { font-size: 13px; margin-right: 1px; flex-shrink: 0; }
+.peek-brow-kind { color: var(--muted); }
+.peek-brow-sep { color: var(--dim); opacity: .4; }
+.peek-brow-id { color: var(--dim); }
+.peek-brow-ts { margin-left: auto; color: var(--dim); letter-spacing: 0; text-transform: none; white-space: nowrap; }
+
+/* peek title / desc / body */
+.peek-title {
+ padding: 9px 20px 4px;
+ font-family: var(--sans);
+ font-size: 15px;
+ font-weight: 600;
+ color: var(--text);
+ line-height: 1.3;
+ flex-shrink: 0;
+}
+
+.peek-desc {
+ padding: 0 20px 10px;
+ font-family: var(--sans);
+ font-size: 12px;
+ color: var(--muted);
+ line-height: 1.55;
+ flex-shrink: 0;
+}
+
+.peek-body {
+ padding: 10px 20px 14px;
+ font-family: var(--mono);
+ font-size: 13px;
+ line-height: 1.72;
+ color: var(--text);
+ white-space: pre-wrap;
+ word-break: break-word;
+ flex-shrink: 0;
cursor: text;
- padding: 2px 6px;
- margin-left: -6px;
border-radius: var(--r2);
transition: background var(--t-fast);
}
-.detail-title:hover { background: var(--raised); }
+.peek-body:hover { background: var(--raised); }
+.peek-meta {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 5px;
+ padding: 0 20px 12px;
+ flex-shrink: 0;
+}
+
+.peek-pin { color: var(--accent); font-size: 11px; }
+
+/* peek sections */
+.peek-sec { border-top: 1px solid var(--soft); flex-shrink: 0; }
+.peek-sec-lbl {
+ padding: 8px 20px 5px;
+ font-family: var(--mono);
+ font-size: 9px;
+ text-transform: uppercase;
+ letter-spacing: .16em;
+ color: var(--dim);
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.peek-sec-lang { color: var(--accent); letter-spacing: 0; text-transform: none; font-size: 9px; }
+.peek-sec-status {
+ color: var(--ok);
+ letter-spacing: 0;
+ text-transform: none;
+ font-size: 9px;
+ border: 1px solid rgba(122,171,114,.4);
+ background: rgba(122,171,114,.06);
+ padding: 0 6px;
+ border-radius: var(--r1);
+}
+
+.peek-sec-run {
+ margin-left: auto;
+ font-family: var(--mono);
+ font-size: 9px;
+ color: var(--ok);
+ border: 1px solid rgba(122,171,114,.4);
+ padding: 1px 8px;
+ border-radius: var(--r1);
+ transition: background var(--t-fast);
+}
+
+.peek-sec-run:hover { background: rgba(122,171,114,.1); }
+
+.peek-sec-inner { padding: 0 20px 14px; }
+.tag-pills { display: flex; flex-wrap: wrap; gap: 5px; }
+
+/* peek context */
+.peek-ctx { display: flex; flex-direction: column; gap: 5px; font-family: var(--mono); font-size: 11px; color: var(--muted); }
+.peek-ctx-lbl { font-size: 9px; text-transform: uppercase; letter-spacing: .1em; color: var(--dim); margin-right: 5px; }
+.peek-ctx-promoted { color: var(--ok); }
+
+/* peek card container */
+.peek-card {
+ margin: 12px;
+ border: 1px solid var(--border);
+ border-radius: var(--r3);
+ overflow: hidden;
+ flex-shrink: 0;
+}
+
+.peek-card-head {
+ background: var(--bg);
+ border-bottom: 1px solid var(--soft);
+ padding-bottom: 0;
+}
+
+.peek-card .peek-sec { border-top-color: var(--border); }
+.peek-card .peek-sec-inner { padding: 0 16px 14px; }
+.peek-card .peek-sec-lbl { padding: 8px 16px 5px; }
+
+/* peek code block */
+.peek-code {
+ background: var(--bg);
+ border: 1px solid var(--border);
+ border-radius: var(--r2);
+ padding: 10px 12px;
+ overflow-x: auto;
+}
+
+.peek-code pre {
+ font-family: var(--mono);
+ font-size: 11px;
+ line-height: 1.65;
+ color: var(--text);
+ white-space: pre-wrap;
+ word-break: break-word;
+}
+
+/* peek steps */
+.peek-steps { display: flex; flex-direction: column; gap: 3px; }
+.peek-step { display: flex; align-items: flex-start; gap: 8px; padding: 3px 0; font-family: var(--mono); font-size: 11px; line-height: 1.45; }
+.peek-step-mark { flex-shrink: 0; margin-top: 1px; }
+.peek-step-text { color: var(--text); }
+
+/* peek decision */
+.peek-decision { padding: 0; }
+.peek-dec-choice { font-family: var(--sans); font-size: 15px; font-weight: 600; color: var(--text); margin-bottom: 6px; }
+.peek-dec-choice::before { content: '▸ '; color: var(--accent); }
+.peek-dec-why { font-family: var(--sans); font-size: 12px; color: var(--muted); line-height: 1.55; margin-bottom: 8px; }
+.peek-dec-key { color: var(--dim); font-size: 9px; text-transform: uppercase; letter-spacing: .1em; font-family: var(--mono); margin-right: 5px; }
+.peek-dec-rejected { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
+.peek-dec-rej { font-family: var(--mono); font-size: 10px; color: var(--muted); border: 1px solid var(--border); padding: 1px 6px; border-radius: var(--r1); text-decoration: line-through; opacity: .6; }
+
+/* peek link */
+.peek-link-url {
+ display: flex;
+ align-items: flex-start;
+ gap: 6px;
+ font-family: var(--mono);
+ font-size: 11px;
+ color: var(--event);
+ border: 1px solid var(--soft);
+ padding: 8px 10px;
+ border-radius: var(--r2);
+ background: var(--bg);
+ word-break: break-all;
+ line-height: 1.5;
+}
+
+/* peek actions */
+.peek-acts {
+ display: flex;
+ gap: 5px;
+ flex-wrap: wrap;
+ padding: 12px 20px 18px;
+ border-top: 1px solid var(--soft);
+ flex-shrink: 0;
+}
+
+/* peek mode pills */
+.peek-run-pill { font-family: var(--mono); font-size: 9px; color: var(--ok); border: 1px solid rgba(122,171,114,.4); background: rgba(122,171,114,.06); padding: 1px 7px; border-radius: var(--r1); }
+.peek-fill-pill { font-family: var(--mono); font-size: 9px; color: var(--lineage); border: 1px solid rgba(152,120,188,.4); background: rgba(152,120,188,.06); padding: 1px 7px; border-radius: var(--r1); }
+.peek-edit-pill { font-family: var(--mono); font-size: 9px; color: var(--todo); border: 1px solid rgba(212,168,75,.4); background: rgba(212,168,75,.06); padding: 1px 7px; border-radius: var(--r1); }
+
+/* run mode */
+.peek-run-prog-wrap { display: flex; align-items: center; gap: 10px; padding: 0 20px 14px; flex-shrink: 0; }
+.peek-run-prog-track { flex: 1; height: 3px; background: var(--border); border-radius: 2px; overflow: hidden; }
+.peek-run-prog { height: 100%; background: var(--ok); border-radius: 2px; transition: width var(--t-base); }
+.peek-run-pct { font-family: var(--mono); font-size: 10px; color: var(--ok); min-width: 28px; }
+.peek-run-steps { flex-shrink: 0; }
+.peek-run-step { display: flex; align-items: flex-start; gap: 10px; padding: 7px 20px; cursor: pointer; border-left: 2px solid transparent; transition: background var(--t-fast), border-left-color var(--t-fast); }
+.peek-run-step:hover { background: var(--raised); }
+.peek-run-step.done .peek-run-text { text-decoration: line-through; color: var(--dim); }
+.peek-run-mark { font-family: var(--mono); font-size: 12px; flex-shrink: 0; margin-top: 1px; }
+.peek-run-text { font-family: var(--sans); font-size: 12px; line-height: 1.5; color: var(--text); }
+
+/* fill mode */
+.peek-fill-canvas { padding: 14px 20px; flex-shrink: 0; }
+.peek-fill-canvas code { font-family: var(--mono); font-size: 12px; line-height: 2; color: var(--text); white-space: pre-wrap; word-break: break-word; }
+
+.fill-slot { display: inline-block; border-bottom: 1.5px solid var(--lineage); }
+.fill-slot.active { border-color: var(--accent); border-bottom-width: 2px; }
+.fill-slot.filled { border-color: var(--ok); }
+.fill-slot input { background: transparent; border: none; outline: none; color: var(--lineage); font-family: var(--mono); font-size: 12px; padding: 0 2px; min-width: 30px; line-height: 2; }
+.fill-slot.active input { color: var(--text); }
+.fill-slot.filled input { color: var(--ok); }
+
+/* edit mode */
+.peek-edit-fields { padding: 12px 20px; display: flex; flex-direction: column; gap: 12px; flex-shrink: 0; }
+.peek-edit-field { display: flex; flex-direction: column; gap: 4px; }
+.peek-edit-lbl { font-family: var(--mono); font-size: 9px; text-transform: uppercase; letter-spacing: .14em; color: var(--dim); }
+.peek-edit-in { background: var(--bg); border: 1px solid var(--border); border-radius: var(--r2); padding: 6px 9px; font-family: var(--mono); font-size: 12px; color: var(--text); outline: none; transition: border-color var(--t-fast); }
+.peek-edit-in:focus { border-color: var(--accent); }
+.peek-edit-ta { background: var(--bg); border: 1px solid var(--border); border-radius: var(--r2); padding: 6px 9px; font-family: var(--mono); font-size: 12px; color: var(--text); outline: none; resize: vertical; min-height: 100px; line-height: 1.55; transition: border-color var(--t-fast); }
+.peek-edit-ta:focus { border-color: var(--accent); }
+
+/* hints bar */
+.peek-hints { display: flex; gap: 12px; padding: 10px 20px; font-family: var(--mono); font-size: 9px; color: var(--dim); border-top: 1px solid var(--soft); flex-shrink: 0; }
+.peek-hints span { display: flex; align-items: center; gap: 3px; }
+
+/* legacy detail support (inline edit) */
.detail-field-edit {
display: block;
width: 100%;
@@ -740,22 +975,6 @@ main {
outline: none;
}
-.detail-body {
- font-family: var(--mono);
- font-size: 13px;
- line-height: 1.72;
- margin-bottom: 16px;
- white-space: pre-wrap;
- word-break: break-word;
- cursor: text;
- border-radius: var(--r2);
- padding: 4px 6px;
- margin-left: -6px;
- transition: background var(--t-fast);
-}
-
-.detail-body:hover { background: var(--raised); }
-
.detail-body-edit {
display: block;
width: 100%;
@@ -777,9 +996,8 @@ main {
.detail-tags {
display: flex;
- gap: 6px;
+ gap: 5px;
flex-wrap: wrap;
- margin-bottom: 16px;
}
.detail-tag {
@@ -792,14 +1010,6 @@ main {
border-radius: var(--r1);
}
-.detail-actions {
- display: flex;
- gap: 5px;
- flex-wrap: wrap;
- border-top: 1px solid var(--soft);
- padding-top: 12px;
-}
-
.action-btn {
font-family: var(--sans);
font-size: 11px;
@@ -816,8 +1026,10 @@ main {
.action-btn:hover { color: var(--accent); border-color: var(--accent); }
.action-btn.primary { color: var(--accent); border-color: var(--accent); background: var(--a-bg); }
+.action-btn.dim { opacity: .45; }
.action-btn.danger { color: var(--danger); border-color: rgba(184,88,88,.4); }
.action-btn.danger:hover { border-color: var(--danger); }
+.action-btn kbd { font-size: 9px; background: rgba(0,0,0,.2); border: 1px solid rgba(0,0,0,.3); border-radius: 2px; padding: 0 3px; opacity: .65; }
/* ── TEMPLATE SLOTS ─────────────────────────────────── */
.slot-form { margin: 16px 0; }