From 1bd474f1caf2bdf578e02bfa72d5565d38fc78df Mon Sep 17 00:00:00 2001 From: Ivan Bondarenko Date: Fri, 10 Jul 2026 18:26:33 +0300 Subject: [PATCH 01/10] feat(outpost): unified Tasks surface, rail 7 to 6 --- src/ui/static/app.js | 6 +- src/ui/static/css/views.css | 388 +++++++++++-------------- src/ui/static/index.html | 246 +++------------- src/ui/static/modules/config.js | 12 - src/ui/static/modules/tabs.js | 40 +-- src/ui/static/modules/tasks-surface.js | 188 ++++++++++++ src/ui/static/modules/tasks.js | 6 + src/ui/static/modules/ui-updates.js | 220 +------------- tests/e2e/specs/lists.spec.ts | 22 +- tests/e2e/specs/polling.spec.ts | 1 - tests/e2e/specs/tabs.spec.ts | 3 +- 11 files changed, 428 insertions(+), 704 deletions(-) create mode 100644 src/ui/static/modules/tasks-surface.js diff --git a/src/ui/static/app.js b/src/ui/static/app.js index 7c5c94d6..b0fdce49 100644 --- a/src/ui/static/app.js +++ b/src/ui/static/app.js @@ -43,12 +43,12 @@ document.addEventListener('DOMContentLoaded', async () => { initSidebarItemClicks(); await initProductNav(); initModalClose(); - initPipelineInfiniteScroll(); + initTasksSurface(); - // Pipeline workflow filter + // Tasks workflow filter document.getElementById('pipeline-workflow-filter')?.addEventListener('change', (e) => { pipelineWorkflowFilter = e.target.value || null; - if (lastState?.tasks) updatePipelineView(lastState.tasks); + if (lastState?.tasks) updateTasksSurface(lastState.tasks); }); initActions(); initNotifications(); diff --git a/src/ui/static/css/views.css b/src/ui/static/css/views.css index 32b65b06..d3e868c0 100644 --- a/src/ui/static/css/views.css +++ b/src/ui/static/css/views.css @@ -2,289 +2,256 @@ * Tab content views: pipeline, workflow, product, settings */ -/* ========== PIPELINE VIEW ========== */ -.pipeline-wrapper { +/* ========== TASKS SURFACE (#606) ========== */ +.tasks-surface { display: flex; flex-direction: column; - gap: 16px; + gap: 12px; height: 100%; } -.pipeline-stage { - flex: 1; - display: flex; - flex-direction: column; - min-height: 0; -} - -.pipeline-stage-header { +.tasks-toolbar { display: flex; align-items: center; + justify-content: space-between; gap: 12px; - padding: 8px 12px; - margin-bottom: 8px; + flex-wrap: wrap; } -.pipeline-stage-title { - font-size: 10px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.1em; - color: var(--color-primary-dim); +.filter-chips { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; } -.pipeline-workflow-filter { - padding: 4px 8px; - background: var(--bg-screen); - border: 1px solid var(--primary-15); +.filter-chip { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 3px 12px; + background: transparent; + border: 1px solid var(--bezel-edge); border-radius: 3px; - color: var(--color-primary-dim); - font-family: var(--font-ui); - font-size: 9px; - text-transform: uppercase; - letter-spacing: 0.05em; + font-family: var(--font-mono); + font-size: 11px; + color: var(--color-muted); cursor: pointer; transition: all 0.15s ease; } -.pipeline-workflow-filter:hover { - border-color: var(--primary-30); +.filter-chip:hover { + color: var(--color-primary); + background: var(--primary-05); } -.pipeline-workflow-filter:focus { - outline: none; - border-color: var(--color-secondary); +.filter-chip.active { + color: var(--color-primary); + border-color: var(--color-primary); + background: var(--primary-10); } -.pipeline-stage-title.analysis { - color: var(--color-secondary); +.chip-count { + font-size: 10px; + color: var(--color-primary-dim); } -.pipeline-stage-title.execution { - color: var(--color-secondary); +.chips-extra { + margin-left: 8px; + font-size: 10px; + color: var(--color-muted); } -.pipeline-container { +.tasks-toolbar-actions { display: flex; + align-items: center; gap: 8px; - flex: 1; - min-height: 0; - align-items: stretch; } -.pipeline-column { - flex: 1; - background: var(--bg-screen); - border: 1px solid var(--primary-12); - border-radius: 4px; +.tasks-progress-strip { display: flex; - flex-direction: column; - overflow: hidden; - background-image: - linear-gradient(var(--grid-color) 1px, transparent 1px), - linear-gradient(90deg, var(--grid-color) 1px, transparent 1px), - linear-gradient(var(--grid-color-strong) 1px, transparent 1px), - linear-gradient(90deg, var(--grid-color-strong) 1px, transparent 1px); - background-size: 10px 10px, 10px 10px, 50px 50px, 50px 50px; + align-items: center; + gap: 12px; } -.pipeline-column.highlight { - border-color: var(--secondary-30); +.tasks-progress-track { + flex: 1; + height: 6px; + background: var(--bg-screen); + border: 1px solid var(--bezel-edge); + border-radius: 3px; + overflow: hidden; } -.column-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 12px 14px; - background: var(--primary-05); - border-bottom: 1px solid var(--primary-10); - position: relative; +.tasks-progress-bar { + height: 100%; + width: 0; + background: var(--color-primary); + box-shadow: 0 0 6px var(--primary-glow); + transition: width 0.3s ease; } -.column-resize-handle { - position: absolute; - right: -4px; - top: 0; - bottom: 0; - width: 8px; - cursor: col-resize; - z-index: 5; +.tasks-progress-label { + font-size: 10px; + color: var(--color-muted); + text-transform: uppercase; + letter-spacing: 0.05em; + white-space: nowrap; } -.column-resize-handle::after { - content: ''; - position: absolute; - left: 3px; - top: 20%; - bottom: 20%; - width: 2px; - background: transparent; - transition: background 0.15s ease; - border-radius: 1px; +.tasks-main { + display: grid; + grid-template-columns: 1fr minmax(280px, 340px); + gap: 12px; + min-height: 200px; } -.column-resize-handle:hover::after, -.column-resize-handle.dragging::after { - background: var(--color-primary-dim); +.tasks-list .task-list-item.tasks-row { + display: flex; + align-items: center; + gap: 10px; } -.column-label { - font-size: 10px; +.task-status-word { + flex: 0 0 auto; + min-width: 88px; + font-size: 9px; font-weight: 600; - letter-spacing: 0.08em; text-transform: uppercase; - color: var(--color-primary-dim); + letter-spacing: 0.05em; + color: var(--type-color, var(--color-muted)); } -.column-count { - font-size: 18px; - font-weight: 700; - color: var(--color-primary); - text-shadow: 0 0 10px var(--primary-glow); -} +.task-status-word.status-todo { color: var(--color-primary); } +.task-status-word.status-analysing, +.task-status-word.status-in-progress { color: var(--color-secondary); } +.task-status-word.status-needs-input, +.task-status-word.status-needs-review { color: var(--color-warning); } +.task-status-word.status-analysed { color: var(--color-info); } +.task-status-word.status-done { color: var(--color-success); } +.task-status-word.status-skipped { color: var(--color-muted); } -.column-items { +.tasks-row .task-list-item-name { flex: 1; - padding: 8px; - overflow-y: auto; - display: flex; - flex-direction: column; - gap: 6px; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } -/* Animated flow arrows */ -.flow-arrow { - display: flex; - align-items: center; - justify-content: center; - color: var(--color-primary); - font-size: 24px; - text-shadow: 0 0 15px var(--primary-glow); - animation: flowPulse 1.5s ease-in-out infinite; +.tasks-row .task-row-actions { + flex: 0 0 auto; } -@keyframes flowPulse { - 0%, 100% { opacity: 0.3; transform: translateX(0); } - 50% { opacity: 1; transform: translateX(3px); } +.tasks-row.ignored { + opacity: 0.45; } -/* Pipeline task cards (full detail) */ -.pipeline-task { - padding: 10px 12px; - background: var(--bg-black-overlay-heavy); - border: 1px solid var(--primary-15); - border-radius: 3px; - cursor: pointer; - transition: all 0.15s ease; - position: relative; - padding-left: 14px; +.tasks-row.selected { + background: var(--primary-08); + border-color: var(--primary-30); } -.pipeline-task::before { - content: ''; - position: absolute; - left: 0; - top: 0; - bottom: 0; - width: 4px; - background: var(--color-primary); - border-radius: 3px 0 0 3px; +.tasks-show-more { + display: block; + width: 100%; + margin-top: 8px; + text-align: center; } -.pipeline-task.priority-high::before { background: var(--color-error); } -.pipeline-task.priority-med::before { background: var(--color-primary); } -.pipeline-task.priority-low::before { background: var(--color-success); } - -.pipeline-task:hover { - background: var(--primary-08); - border-color: var(--primary-30); +.task-detail-panel { + background: var(--bg-module); + border: 1px solid var(--bezel-edge); + border-radius: 4px; + padding: 14px; + overflow-y: auto; + max-height: 60vh; } -.pipeline-task.active { - border-color: var(--color-secondary); - background: rgba(95, 179, 179, 0.1); +.task-detail-header { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 8px; + margin-bottom: 10px; } -.pipeline-task.active::before { - background: var(--color-secondary); +.task-detail-title { + font-size: 13px; + font-weight: 600; + color: var(--color-primary); } -.pipeline-load-more { - display: block; - width: 100%; - margin-top: 4px; - padding: 6px 12px; - background: var(--primary-05); - border: 1px dashed var(--primary-30); - border-radius: 3px; - color: var(--label-color); - font-family: inherit; - font-size: 11px; - letter-spacing: 0.04em; +.task-detail-full-link { + font-size: 10px; + color: var(--color-secondary); text-transform: uppercase; + letter-spacing: 0.05em; + white-space: nowrap; cursor: pointer; - transition: all 0.15s ease; } -.pipeline-load-more:hover { - background: var(--primary-08); - border-color: var(--primary-30); - color: var(--color-primary); +.task-detail-actions { + margin-bottom: 10px; } -.pipeline-task .task-id { - font-size: 8px; - color: var(--label-color); - margin-bottom: 4px; - font-family: var(--font-ui); - word-break: break-all; +.task-detail-body { + font-size: 12px; } -.pipeline-task .task-title { - font-size: 11px; - color: var(--color-primary); - line-height: 1.4; - margin-bottom: 6px; +.tasks-processes-section { + border-top: 1px solid var(--bezel-edge); + padding-top: 12px; } -.pipeline-task .task-tags { +.tasks-processes-header { display: flex; - gap: 4px; - flex-wrap: wrap; + align-items: center; + justify-content: space-between; + margin-bottom: 8px; } -.pipeline-task .task-tag { - padding: 2px 6px; - background: var(--primary-10); - border-radius: 2px; - font-size: 8px; +.tasks-processes-title { + font-size: 10px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.1em; color: var(--color-primary-dim); } -/* Review-waiting tasks share the "Needs Input" column but are flagged with a - warning-toned chip to distinguish them from input-waiting tasks (#500). */ -.pipeline-task .task-tag.tag-review { - color: var(--color-warning); - background: rgb(var(--color-warning-rgb) / 0.12); +.tasks-processes-counts { + font-size: 10px; + color: var(--color-muted); } -.pipeline-task:has(.completed-date) { - padding-bottom: 24px; +@media (max-width: 900px) { + .tasks-main { + grid-template-columns: 1fr; + } } -.pipeline-task > .completed-date { - position: absolute; - bottom: 6px; - right: 8px; - padding: 2px 8px; - background: var(--surface-tertiary); - color: var(--color-success); +.pipeline-workflow-filter { + padding: 4px 8px; + background: var(--bg-screen); + border: 1px solid var(--primary-15); + border-radius: 3px; + color: var(--color-primary-dim); font-family: var(--font-ui); - font-size: 8px; - border-radius: 10px; + font-size: 9px; + text-transform: uppercase; + letter-spacing: 0.05em; + cursor: pointer; + transition: all 0.15s ease; +} + +.pipeline-workflow-filter:hover { + border-color: var(--primary-30); } +.pipeline-workflow-filter:focus { + outline: none; + border-color: var(--color-secondary); +} /* ========== PRODUCT TAB ========== */ .product-viewer { display: flex; @@ -2756,35 +2723,6 @@ } /* ========== ROADMAP TASK ACTIONS ========== */ -.roadmap-column-header { - gap: 10px; -} - -.roadmap-column-actions { - display: flex; - align-items: center; - gap: 8px; - margin-left: auto; -} - -.pipeline-task.ignored { - background: rgba(222, 145, 74, 0.08); - border-color: rgba(222, 145, 74, 0.28); -} - -.pipeline-task.blocked { - border-style: dashed; - background: rgba(94, 120, 145, 0.08); -} - -.pipeline-task.manual-ignored::before { - background: var(--color-warning); -} - -.pipeline-task.blocked::before { - background: var(--color-secondary); -} - .roadmap-task-note { margin-top: 8px; font-size: 9px; diff --git a/src/ui/static/index.html b/src/ui/static/index.html index b624a2fd..cd4db6e7 100644 --- a/src/ui/static/index.html +++ b/src/ui/static/index.html @@ -75,9 +75,8 @@ - +
-
- - - - -
-
@@ -1086,8 +1071,6 @@
- - diff --git a/src/ui/static/modules/icons.js b/src/ui/static/modules/icons.js index 8858c542..73b80219 100644 --- a/src/ui/static/modules/icons.js +++ b/src/ui/static/modules/icons.js @@ -56,12 +56,6 @@ function replaceIconPlaceholders() { const stopBtn = document.querySelector('.ctrl-btn[data-action="stop"]'); if (stopBtn) stopBtn.innerHTML = getIcon('stop') + ' STOP'; - // Replace hamburger menu with menu icon - const hamburger = document.getElementById('hamburger-menu'); - if (hamburger) { - hamburger.innerHTML = getIcon('menu', 24); - } - // Replace sidebar toggles with expand icons document.querySelectorAll('.sidebar-toggle').forEach(toggle => { toggle.innerHTML = getIcon('expandMore', 16); diff --git a/src/ui/static/modules/layout.js b/src/ui/static/modules/layout.js index 876763d8..6dbc14d6 100644 --- a/src/ui/static/modules/layout.js +++ b/src/ui/static/modules/layout.js @@ -1,17 +1,10 @@ /** - * Layout persistence: draggable panel splitter + resizable pipeline columns. - * Storage keys: dotbot:layout:sidebarWidth, dotbot:layout:columnWidths + * Shared localStorage helpers (dotbot:* keys). + * The splitter/column-resize machinery that used to live here died with the + * contextual sidebar and the pipeline kanban (#606); these helpers remain + * because other modules persist state through them (e.g. shell.js rail pin). */ -const SIDEBAR_MIN = 160; -const SIDEBAR_MAX = 520; -const SIDEBAR_DEFAULT = 280; -const COLUMN_MIN = 120; -const STORAGE_SIDEBAR = 'dotbot:layout:sidebarWidth'; -const STORAGE_COLUMNS = 'dotbot:layout:columnWidths'; - -// ── Storage helpers ────────────────────────────────────────────────────────── - function layoutGet(key) { try { return window.localStorage.getItem(key); } catch (e) { return null; } } @@ -23,174 +16,3 @@ function layoutSet(key, value) { function layoutRemove(key) { try { window.localStorage.removeItem(key); } catch (e) { /* ignore */ } } - -// ── Sidebar width ──────────────────────────────────────────────────────────── - -function applySidebarWidth(px) { - document.documentElement.style.setProperty('--sidebar-width', `${px}px`); -} - -function restoreSidebarWidth() { - const stored = layoutGet(STORAGE_SIDEBAR); - if (stored) { - const px = parseInt(stored, 10); - if (px >= SIDEBAR_MIN && px <= SIDEBAR_MAX) applySidebarWidth(px); - } -} - -function initPanelSplitter() { - const splitter = document.getElementById('panel-splitter'); - const layout = document.getElementById('main-layout'); - if (!splitter || !layout) return; - - let dragging = false; - let startX = 0; - let startWidth = 0; - - splitter.addEventListener('mousedown', (e) => { - dragging = true; - startX = e.clientX; - startWidth = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--sidebar-width'), 10) || SIDEBAR_DEFAULT; - splitter.classList.add('dragging'); - document.body.style.cursor = 'col-resize'; - document.body.style.userSelect = 'none'; - e.preventDefault(); - }); - - document.addEventListener('mousemove', (e) => { - if (!dragging) return; - const delta = e.clientX - startX; - const newWidth = Math.max(SIDEBAR_MIN, Math.min(SIDEBAR_MAX, startWidth + delta)); - applySidebarWidth(newWidth); - }); - - document.addEventListener('mouseup', () => { - if (!dragging) return; - dragging = false; - splitter.classList.remove('dragging'); - document.body.style.cursor = ''; - document.body.style.userSelect = ''; - const current = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--sidebar-width'), 10); - if (current !== SIDEBAR_DEFAULT) { - layoutSet(STORAGE_SIDEBAR, current); - } else { - layoutRemove(STORAGE_SIDEBAR); - } - }); - - splitter.addEventListener('dblclick', () => { - applySidebarWidth(SIDEBAR_DEFAULT); - layoutRemove(STORAGE_SIDEBAR); - }); -} - -// ── Pipeline column resize ─────────────────────────────────────────────────── - -function loadColumnWidths() { - try { - const raw = layoutGet(STORAGE_COLUMNS); - return raw ? JSON.parse(raw) : {}; - } catch (e) { return {}; } -} - -function saveColumnWidths(map) { - try { layoutSet(STORAGE_COLUMNS, JSON.stringify(map)); } catch (e) { /* ignore */ } -} - -function getColumnKey(col) { - const label = col.querySelector('.column-label'); - return label ? label.textContent.trim().replace(/\s+/g, '_') : null; -} - -function applyColumnWidth(col, px) { - col.style.flex = 'none'; - col.style.width = `${px}px`; -} - -function resetColumnWidth(col) { - col.style.flex = ''; - col.style.width = ''; -} - -function initColumnResizeHandles() { - const container = document.querySelector('.pipeline-container'); - if (!container) return; - - const columns = Array.from(container.querySelectorAll('.pipeline-column')); - const widths = loadColumnWidths(); - - columns.forEach((col) => { - const key = getColumnKey(col); - if (key && widths[key]) { - applyColumnWidth(col, widths[key]); - } - - const header = col.querySelector('.column-header'); - if (!header) return; - - const handle = document.createElement('div'); - handle.className = 'column-resize-handle'; - handle.title = 'Drag to resize · Double-click to reset'; - header.appendChild(handle); - - let dragging = false; - let startX = 0; - let startWidth = 0; - - handle.addEventListener('mousedown', (e) => { - dragging = true; - startX = e.clientX; - startWidth = col.getBoundingClientRect().width; - handle.classList.add('dragging'); - document.body.style.cursor = 'col-resize'; - document.body.style.userSelect = 'none'; - e.preventDefault(); - e.stopPropagation(); - }); - - document.addEventListener('mousemove', (e) => { - if (!dragging) return; - const delta = e.clientX - startX; - const newWidth = Math.max(COLUMN_MIN, startWidth + delta); - applyColumnWidth(col, newWidth); - }); - - document.addEventListener('mouseup', () => { - if (!dragging) return; - dragging = false; - handle.classList.remove('dragging'); - document.body.style.cursor = ''; - document.body.style.userSelect = ''; - - if (key) { - const map = loadColumnWidths(); - map[key] = Math.round(col.getBoundingClientRect().width); - saveColumnWidths(map); - } - }); - - handle.addEventListener('dblclick', (e) => { - e.stopPropagation(); - resetColumnWidth(col); - if (key) { - const map = loadColumnWidths(); - delete map[key]; - if (Object.keys(map).length) { - saveColumnWidths(map); - } else { - layoutRemove(STORAGE_COLUMNS); - } - } - }); - }); -} - -// ── Init ───────────────────────────────────────────────────────────────────── - -// Restore sidebar width before first paint to avoid flash. -restoreSidebarWidth(); - -document.addEventListener('DOMContentLoaded', () => { - initPanelSplitter(); - initColumnResizeHandles(); -}); diff --git a/src/ui/static/modules/sidebar.js b/src/ui/static/modules/sidebar.js index ba57acf7..c99a1357 100644 --- a/src/ui/static/modules/sidebar.js +++ b/src/ui/static/modules/sidebar.js @@ -20,24 +20,7 @@ function initSidebarCollapse() { }); } -/** - * Initialize collapse for dynamically created sections - * @param {HTMLElement} container - Container element - */ -function initSidebarCollapseForContainer(container) { - container.querySelectorAll('.sidebar-header').forEach(header => { - header.addEventListener('click', () => { - const section = header.closest('.sidebar-section'); - const content = section.querySelector('.sidebar-content'); - const isCollapsed = section.classList.toggle('collapsed'); - content.style.display = isCollapsed ? 'none' : 'block'; - const toggle = header.querySelector('.sidebar-toggle'); - if (toggle) { - toggle.innerHTML = getIcon(isCollapsed ? 'chevronRight' : 'expandMore', 16); - } - }); - }); -} + /** * Initialize the sidebar with dynamic content @@ -199,18 +182,4 @@ function renderFlatItems(container, items, type, shortType) { }); } -/** - * Initialize sidebar item click handlers - */ -function initSidebarItemClicks() { - document.querySelectorAll('.sidebar-item').forEach(item => { - item.addEventListener('click', (e) => { - e.stopPropagation(); - const type = item.dataset.type; - const file = item.dataset.file; - if (type && file) { - showWorkflowItem(type, file); - } - }); - }); -} + diff --git a/src/ui/static/modules/tabs.js b/src/ui/static/modules/tabs.js index 3cd9810b..3c46176a 100644 --- a/src/ui/static/modules/tabs.js +++ b/src/ui/static/modules/tabs.js @@ -37,26 +37,16 @@ function switchToTab(targetId) { // Keep the shell rail's active marker in sync (modules/shell.js) syncRailActive(targetId); - // Switch context panel in left sidebar + // Run per-tab side effects switchContextPanel(targetId); } /** - * Switch context panel based on current tab + * Per-tab side effects (the contextual sidebar this used to drive was + * removed in #606 — the name survives for its many call sites). * @param {string} tabId - Tab ID */ function switchContextPanel(tabId) { - // Hide all context panels - document.querySelectorAll('.context-panel').forEach(panel => { - panel.classList.add('hidden'); - }); - - // Show the context panel matching the tab - const targetPanel = document.querySelector(`.context-panel[data-context="${tabId}"]`); - if (targetPanel) { - targetPanel.classList.remove('hidden'); - } - // Tasks surface hosts the process list — poll while it's visible if (tabId === 'tasks') { startProcessPolling(); @@ -102,22 +92,3 @@ function initLogoClick() { } } -/** - * Initialize hamburger menu for mobile - */ -function initHamburgerMenu() { - const hamburger = document.getElementById('hamburger-menu'); - const sidebar = document.querySelector('.sidebar-left'); - const overlay = document.getElementById('mobile-overlay'); - - if (!hamburger || !sidebar || !overlay) return; - - const toggleMenu = () => { - hamburger.classList.toggle('active'); - sidebar.classList.toggle('mobile-open'); - overlay.classList.toggle('active'); - }; - - hamburger.addEventListener('click', toggleMenu); - overlay.addEventListener('click', toggleMenu); -} From 6fe2c17b0d38a96a0dc2995f06e96d74cea91402 Mon Sep 17 00:00:00 2001 From: Ivan Bondarenko Date: Mon, 13 Jul 2026 11:03:58 +0300 Subject: [PATCH 05/10] feat(outpost): drop installed-workflow pills from topbar --- src/shared/css/dotbot-shell.css | 6 ++--- src/shared/css/harness.html | 2 +- src/ui/static/css/layout.css | 38 +++-------------------------- src/ui/static/index.html | 1 - src/ui/static/modules/ui-updates.js | 27 +++----------------- 5 files changed, 10 insertions(+), 64 deletions(-) diff --git a/src/shared/css/dotbot-shell.css b/src/shared/css/dotbot-shell.css index 4d52399f..d7e498c6 100644 --- a/src/shared/css/dotbot-shell.css +++ b/src/shared/css/dotbot-shell.css @@ -1,6 +1,6 @@ /* DOTBOT Shared Navigation Shell * One shell, many rooms: the v4 navigation frame shared by Mothership and Outpost. - * Spec: docs/DOTBOT-v4-DESIGN-IDEAS.md §7 — 44px top bar · 56px icon rail (pinnable + * Spec: docs/DOTBOT-v4-DESIGN-IDEAS.md #7 — 44px top bar · 56px icon rail (pinnable * to 200px labelled mode) · 28px dispatch ticker · content capped at 1280px. * * Consumption order: dotbot-tokens.css → dotbot-crt.css → dotbot-shell.css @@ -25,7 +25,7 @@ --shell-rail-w-pinned: 200px; --shell-ticker-h: 28px; --shell-content-max: 1280px; - /* Scanline intensity: 6% on chrome, 0% on content (spec §11) */ + /* Scanline intensity: 6% on chrome, 0% on content (spec #11) */ --shell-scanline-alpha: 0.06; display: grid; @@ -46,7 +46,7 @@ } /* The shell owns its own chrome texture; suppress the global crt.css overlay - * so content panels stay at 0% (spec §11). No-op until .shell markup exists. */ + * so content panels stay at 0% (spec #11). No-op until .shell markup exists. */ body:has(.shell)::after { content: none; } diff --git a/src/shared/css/harness.html b/src/shared/css/harness.html index 90288f78..2d63416e 100644 --- a/src/shared/css/harness.html +++ b/src/shared/css/harness.html @@ -147,7 +147,7 @@

SHELL HARNESS