diff --git a/package-lock.json b/package-lock.json index aaa31e8..989099a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5344,11 +5344,11 @@ }, "packages/copilot-plugin": { "name": "@plan-review/copilot-plugin", - "version": "0.0.7" + "version": "0.0.8" }, "packages/core": { "name": "@plan-review/core", - "version": "0.0.7", + "version": "0.0.8", "dependencies": { "mdast-util-to-string": "^4.0.0", "mermaid": "^11.12.3", @@ -5369,7 +5369,7 @@ } }, "packages/markdown-review": { - "version": "0.0.7", + "version": "0.0.8", "license": "MIT", "bin": { "markdown-review": "bin/markdown-review.js" @@ -5384,7 +5384,7 @@ }, "packages/vscode": { "name": "markdown-review-vscode", - "version": "0.0.7", + "version": "0.0.8", "devDependencies": { "@types/vscode": "^1.85.0", "esbuild": "^0.25.0" @@ -5395,7 +5395,7 @@ }, "packages/web": { "name": "@plan-review/web", - "version": "0.0.7", + "version": "0.0.8", "dependencies": { "@plan-review/core": "*" }, diff --git a/packages/core/src/components/App.tsx b/packages/core/src/components/App.tsx index fbf4cba..4e6e37e 100644 --- a/packages/core/src/components/App.tsx +++ b/packages/core/src/components/App.tsx @@ -12,6 +12,7 @@ import { questionEls, answeredCount, markdownSource, + themeMode, } from '../state'; import { CSS } from '../styles'; import { MarkdownRenderer } from './MarkdownRenderer'; @@ -32,6 +33,15 @@ export function App() { const qEls = questionEls.value; const answered = answeredCount.value; + // Apply forced theme class on for CSS variable overrides + useEffect(() => { + const root = document.documentElement; + root.classList.remove('theme-light', 'theme-dark'); + const mode = themeMode.value; + if (mode === 'light') root.classList.add('theme-light'); + else if (mode === 'dark') root.classList.add('theme-dark'); + }, [themeMode.value]); + // Scroll-spy + progress bar useEffect(() => { let rafId: number | null = null; diff --git a/packages/core/src/highlighter.ts b/packages/core/src/highlighter.ts index 2054092..65980dd 100644 --- a/packages/core/src/highlighter.ts +++ b/packages/core/src/highlighter.ts @@ -1,6 +1,7 @@ import { createHighlighterCore, type HighlighterCore } from 'shiki/core'; import { createJavaScriptRegexEngine } from 'shiki/engine/javascript'; import themeDarker from 'shiki/themes/material-theme-darker.mjs'; +import themeLight from 'shiki/themes/github-light.mjs'; import langTypescript from 'shiki/langs/typescript.mjs'; import langPython from 'shiki/langs/python.mjs'; import langJavascript from 'shiki/langs/javascript.mjs'; @@ -11,11 +12,9 @@ import langCss from 'shiki/langs/css.mjs'; let highlighter: HighlighterCore | null = null; -const THEME = 'material-theme-darker'; - export async function initHighlighter(): Promise { highlighter = await createHighlighterCore({ - themes: [themeDarker], + themes: [themeDarker, themeLight], langs: [langTypescript, langPython, langJavascript, langJson, langBash, langHtml, langCss], engine: createJavaScriptRegexEngine(), }); @@ -31,6 +30,10 @@ export function highlightCode(code: string, lang: string): string | null { return highlighter.codeToHtml(code, { lang: effectiveLang, - theme: THEME, + themes: { + dark: 'material-theme-darker', + light: 'github-light', + }, + defaultColor: false, }); } diff --git a/packages/core/src/index.tsx b/packages/core/src/index.tsx index 3b701ca..67f1a47 100644 --- a/packages/core/src/index.tsx +++ b/packages/core/src/index.tsx @@ -13,13 +13,16 @@ import { fileName, markdownSource, resetState, + themeMode, } from './state.js'; import { App } from './components/App.js'; export type { Element, BaseElement, CodeElement, QuestionElement } from './types.js'; +export type { ThemeMode } from './state.js'; export { extractElements } from './extract-elements.js'; export { highlightCode } from './highlighter.js'; export { resetState } from './state.js'; +export { CSS_VARS } from './styles.js'; export interface PlanReviewOptions { container: HTMLElement; @@ -29,14 +32,29 @@ export interface PlanReviewOptions { postUrl?: string; onFeedback?: (feedback: unknown) => void; showUpload?: boolean; + /** 'system' = follow OS prefers-color-scheme; 'light'/'dark' = forced */ + themeMode?: 'system' | 'light' | 'dark'; +} + +/** Resolve effective light/dark given themeMode */ +function isLightTheme(mode: 'system' | 'light' | 'dark'): boolean { + if (mode === 'light') return true; + if (mode === 'dark') return false; + return typeof window !== 'undefined' + && window.matchMedia('(prefers-color-scheme: light)').matches; } export const PlanReview = { async init(options: PlanReviewOptions): Promise { resetState(); await initHighlighter(); - // Initialize bundled mermaid - mermaid.initialize({ startOnLoad: false, theme: 'dark' }); + + const mode = options.themeMode ?? 'system'; + themeMode.value = mode; + + // Initialize bundled mermaid with resolved theme + mermaid.initialize({ startOnLoad: false, theme: isLightTheme(mode) ? 'default' : 'dark' }); + const tree = unified().use(remarkParse).use(remarkGfm).parse(options.markdown); elements.value = extractElements(tree); markdownSource.value = options.markdown; diff --git a/packages/core/src/state.ts b/packages/core/src/state.ts index 3b34798..1f9e121 100644 --- a/packages/core/src/state.ts +++ b/packages/core/src/state.ts @@ -30,6 +30,10 @@ export const questionAnswers = signal>({}); export const dismissed = signal>({}); export const editing = signal>({}); +// Theme +export type ThemeMode = 'system' | 'light' | 'dark'; +export const themeMode = signal('system'); + // Config export const feedbackMode = signal<'download' | 'post' | 'vscode'>('download'); export const postUrl = signal(''); @@ -100,4 +104,5 @@ export function resetState() { panelOpen.value = false; outlineOpen.value = false; fileName.value = 'PLAN.md'; + themeMode.value = 'system'; } diff --git a/packages/core/src/styles.ts b/packages/core/src/styles.ts index 2c35fd6..e83d790 100644 --- a/packages/core/src/styles.ts +++ b/packages/core/src/styles.ts @@ -1,214 +1,274 @@ -export const CSS = ` +const LIGHT_VARS = ` + --bg-body:#f8f9fa;--bg-surface:#fff;--bg-elevated:#f0f0f0;--bg-code:#f6f8fa; + --bg-code-header:#eef1f5;--bg-hover:#e0e0e0;--bg-fab:#f0f0f0;--bg-fab-hover:#e0e0e0; + --bg-button:#e8e8e8;--bg-inverted:#111;--bg-inverted-hover:#333; + --bg-inline-code:rgba(0,0,0,.07);--bg-drag:#e8f0ff; + --text-heading:#111;--text-secondary:#333;--text-body:#555;--text-sub-heading:#444; + --text-muted:#666;--text-faint:#888;--text-dimmed:#999;--text-dimmed-hover:#555; + --text-ghost:#bbb;--text-input:#1a1a1a;--text-inline-code:#222;--text-inverted:#fff; + --text-code:#334;--text-code-meta:#6b8cb5;--text-code-meta-hover:#4a6d90; + --accent-purple:#8b5cf6;--accent-link:#0077cc;--accent-error:#dc3545;--accent-error-hover:#c82333; + --accent-blue:#0969da;--accent-blue-hover:#0550ae; + --border-subtle:rgba(0,0,0,.06);--border-light:rgba(0,0,0,.08); + --border-medium:rgba(0,0,0,.1);--border-strong:rgba(0,0,0,.12); + --border-accent:rgba(0,0,0,.15);--border-quote:#d0d0d0;--border-radio:#bbb;--border-button:#ccc; + --surface-hover:rgba(0,0,0,.03);--surface-subtle:rgba(0,0,0,.02); + --surface-selected:rgba(0,0,0,.05);--surface-handle:rgba(0,0,0,.1); + --surface-quote-border:rgba(0,0,0,.2); + --overlay-bg:rgba(0,0,0,.3);--overlay-outline:rgba(0,0,0,.2);--shadow-color:rgba(0,0,0,.1); + --flash-start:rgba(139,92,246,.12); + --copilot-dim-text:rgba(9,105,218,.6);--copilot-dim-border:rgba(9,105,218,.2); + --copilot-dim-bg:rgba(9,105,218,.06);--copilot-dim-text-hover:rgba(9,105,218,.75); + --copilot-dim-border-hover:rgba(9,105,218,.35); + --copilot-dl-bg:rgba(9,105,218,.1);--copilot-dl-bg-hover:rgba(9,105,218,.15)`; + +export const CSS_VARS = ` +:root { + --bg-body:#111;--bg-surface:#161616;--bg-elevated:#1a1a1a;--bg-code:#0d1117; + --bg-code-header:#0a0e16;--bg-hover:#222;--bg-fab:#1e1e1e;--bg-fab-hover:#262626; + --bg-button:#2a2a2a;--bg-inverted:#fff;--bg-inverted-hover:#e8e8e8; + --bg-inline-code:rgba(255,255,255,.1);--bg-drag:#1a1a2e; + --text-heading:#fff;--text-secondary:#ccc;--text-body:#999;--text-sub-heading:#aaa; + --text-muted:#777;--text-faint:#555;--text-dimmed:#444;--text-dimmed-hover:#888; + --text-ghost:#333;--text-input:#e0e0e0;--text-inline-code:#ddd;--text-inverted:#111; + --text-code:#bbc;--text-code-meta:#3d5070;--text-code-meta-hover:#5a7da0; + --accent-purple:#c792ea;--accent-link:#6cf;--accent-error:#f55;--accent-error-hover:#e55; + --accent-blue:#388bfd;--accent-blue-hover:#1f6feb; + --border-subtle:rgba(255,255,255,.06);--border-light:rgba(255,255,255,.07); + --border-medium:rgba(255,255,255,.1);--border-strong:rgba(255,255,255,.13); + --border-accent:rgba(255,255,255,.18);--border-quote:#333;--border-radio:#444;--border-button:#444; + --surface-hover:rgba(255,255,255,.04);--surface-subtle:rgba(255,255,255,.02); + --surface-selected:rgba(255,255,255,.07);--surface-handle:rgba(255,255,255,.1); + --surface-quote-border:rgba(255,255,255,.25); + --overlay-bg:rgba(0,0,0,.5);--overlay-outline:rgba(0,0,0,.4);--shadow-color:rgba(0,0,0,.6); + --flash-start:rgba(199,146,234,.15); + --copilot-dim-text:rgba(56,132,255,.55);--copilot-dim-border:rgba(56,132,255,.2); + --copilot-dim-bg:rgba(56,132,255,.06);--copilot-dim-text-hover:rgba(56,132,255,.75); + --copilot-dim-border-hover:rgba(56,132,255,.35); + --copilot-dl-bg:rgba(56,132,255,.15);--copilot-dl-bg-hover:rgba(56,132,255,.22) +} +/* System theme: light when OS says so, unless forced dark */ +@media(prefers-color-scheme:light){:root:not(.theme-dark){${LIGHT_VARS}}} +/* Forced light (VS Code light theme with autoDetect off) */ +:root.theme-light{${LIGHT_VARS}} +`; + +export const CSS = `${CSS_VARS} *,*::before,*::after{box-sizing:border-box;margin:0;padding:0} -html{-webkit-text-size-adjust:100%} -body{background:#111;color:#999;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif; +html{-webkit-text-size-adjust:100%;color-scheme:light dark} +body{background:var(--bg-body);color:var(--text-body);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif; -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;overflow-x:hidden; text-rendering:optimizeLegibility} .doc{max-width:580px;margin:0 auto;padding:60px 28px 160px} /* Block hover */ .block{cursor:pointer;border-radius:6px;padding:3px 6px;margin:2px -6px;position:relative;transition:background .15s} -.block:hover{background:rgba(255,255,255,.04)} +.block:hover{background:var(--surface-hover)} /* Typography */ -.el-h1{font-size:26px;font-weight:700;color:#fff;letter-spacing:-.4px;line-height:1.3;margin-top:28px} -.el-h2{font-size:12px;font-weight:600;color:#777;text-transform:uppercase;letter-spacing:1.3px;margin-top:36px} -.el-h3{font-size:14px;font-weight:600;color:#aaa;margin-top:18px} -.el-p{font-size:15px;color:#999;line-height:1.8;margin-top:8px;letter-spacing:.01em} -.el-li{font-size:15px;color:#ccc;line-height:1.8;padding-left:18px;position:relative;margin-top:5px} -.el-li::before{content:"●";position:absolute;left:0;color:#555;font-size:7px;top:7px} -.el-ol{font-size:15px;color:#ccc;line-height:1.8;padding-left:18px;position:relative;margin-top:5px} -.el-ol::before{content:"–";position:absolute;left:2px;color:#555;font-size:15px} -.el-quote{border-left:2px solid #333;padding-left:16px;color:#aaa;font-style:italic;font-size:15px;line-height:1.8;margin-top:8px} +.el-h1{font-size:26px;font-weight:700;color:var(--text-heading);letter-spacing:-.4px;line-height:1.3;margin-top:28px} +.el-h2{font-size:12px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:1.3px;margin-top:36px} +.el-h3{font-size:14px;font-weight:600;color:var(--text-sub-heading);margin-top:18px} +.el-p{font-size:15px;color:var(--text-body);line-height:1.8;margin-top:8px;letter-spacing:.01em} +.el-li{font-size:15px;color:var(--text-secondary);line-height:1.8;padding-left:18px;position:relative;margin-top:5px} +.el-li::before{content:"●";position:absolute;left:0;color:var(--text-faint);font-size:7px;top:7px} +.el-ol{font-size:15px;color:var(--text-secondary);line-height:1.8;padding-left:18px;position:relative;margin-top:5px} +.el-ol::before{content:"–";position:absolute;left:2px;color:var(--text-faint);font-size:15px} +.el-quote{border-left:2px solid var(--border-quote);padding-left:16px;color:var(--text-sub-heading);font-style:italic;font-size:15px;line-height:1.8;margin-top:8px} /* Images */ .doc img{max-width:100%;height:auto;border-radius:8px;margin-top:10px} /* Code blocks */ -.code-wrap{background:#0d1117;border:1px solid rgba(255,255,255,.06);border-radius:10px;overflow:hidden;margin-top:10px} -.code-header{background:#0a0e16;padding:7px 14px;border-bottom:1px solid rgba(255,255,255,.05);display:flex;justify-content:space-between;align-items:center} -.code-lang{color:#3d5070;font-family:'Fira Code','JetBrains Mono',monospace;text-transform:uppercase;font-size:11px} -.code-copy{background:none;border:none;color:#3d5070;font-size:11px;cursor:pointer;font-family:inherit} -.code-copy:hover{color:#5a7da0} +.code-wrap{background:var(--bg-code);border:1px solid var(--border-subtle);border-radius:10px;overflow:hidden;margin-top:10px} +.code-header{background:var(--bg-code-header);padding:7px 14px;border-bottom:1px solid var(--border-subtle);display:flex;justify-content:space-between;align-items:center} +.code-lang{color:var(--text-code-meta);font-family:'Fira Code','JetBrains Mono',monospace;text-transform:uppercase;font-size:11px} +.code-copy{background:none;border:none;color:var(--text-code-meta);font-size:11px;cursor:pointer;font-family:inherit} +.code-copy:hover{color:var(--text-code-meta-hover)} .code-body{padding:16px;font-size:13.5px;line-height:1.75;font-family:'Fira Code','JetBrains Mono',monospace; - color:#bbc;overflow-x:auto;white-space:pre} + color:var(--text-code);overflow-x:auto;white-space:pre} /* Shiki output styling */ .code-body pre{margin:0;padding:0;background:transparent!important} .code-body code{font-family:inherit;font-size:inherit;line-height:inherit;background:transparent} .code-body .shiki{background:transparent!important} +/* Shiki dual-theme: dark by default, light via media query or forced class */ +.shiki,.shiki span{color:var(--shiki-dark)!important;background-color:transparent!important} +@media(prefers-color-scheme:light){:root:not(.theme-dark) .shiki,:root:not(.theme-dark) .shiki span{color:var(--shiki-light)!important}} +.theme-light .shiki,.theme-light .shiki span{color:var(--shiki-light)!important} + /* Mermaid diagrams */ .mermaid-body{padding:16px;display:flex;justify-content:center;overflow-x:auto} .mermaid-body svg{max-width:100%;height:auto} /* Annotation dot */ -.ann-dot{position:absolute;left:-14px;top:50%;transform:translateY(-50%);width:7px;height:7px;border-radius:50%;background:#c792ea} +.ann-dot{position:absolute;left:-14px;top:50%;transform:translateY(-50%);width:7px;height:7px;border-radius:50%;background:var(--accent-purple)} .code-wrap .ann-dot{left:-14px;top:14px;transform:none} /* Inline markdown */ -.doc strong{color:#fff;font-weight:600} -.doc code.inline{font-family:monospace;font-size:.87em;background:rgba(255,255,255,.1);color:#ddd;padding:2px 6px;border-radius:3px} +.doc strong{color:var(--text-heading);font-weight:600} +.doc code.inline{font-family:monospace;font-size:.87em;background:var(--bg-inline-code);color:var(--text-inline-code);padding:2px 6px;border-radius:3px} /* ---- Question Blocks ---- */ .q-block{margin:16px -6px;border-radius:12px;overflow:hidden;transition:all .2s} -.q-block.unanswered{border:1px solid rgba(255,255,255,.13);background:rgba(255,255,255,.04)} -.q-block.answered{border:1px solid rgba(255,255,255,.08);background:rgba(255,255,255,.02);transition:background .15s} -.q-block.answered:hover{background:rgba(255,255,255,.05)} +.q-block.unanswered{border:1px solid var(--border-strong);background:var(--surface-hover)} +.q-block.answered{border:1px solid var(--border-light);background:var(--surface-subtle);transition:background .15s} +.q-block.answered:hover{background:var(--surface-hover)} .q-header{padding:14px 16px 10px;display:flex;gap:10px;align-items:flex-start} -.q-block.unanswered .q-header{border-bottom:1px solid rgba(255,255,255,.07)} -.q-icon{font-size:9px;color:#555;text-transform:uppercase;letter-spacing:1px;font-family:monospace;flex-shrink:0;margin-top:3px} +.q-block.unanswered .q-header{border-bottom:1px solid var(--border-light)} +.q-icon{font-size:9px;color:var(--text-faint);text-transform:uppercase;letter-spacing:1px;font-family:monospace;flex-shrink:0;margin-top:3px} .q-text{flex:1;font-size:15px;line-height:1.55} -.q-block.unanswered .q-text{color:#ddd;font-weight:500} -.q-block.answered .q-text{color:#666;font-weight:400} -.q-dismiss{font-size:18px;color:#444;cursor:pointer;background:none;border:none;line-height:1;padding:0 2px;flex-shrink:0} -.q-dismiss:hover{color:#888} +.q-block.unanswered .q-text{color:var(--text-inline-code);font-weight:500} +.q-block.answered .q-text{color:var(--text-muted);font-weight:400} +.q-dismiss{font-size:18px;color:var(--text-dimmed);cursor:pointer;background:none;border:none;line-height:1;padding:0 2px;flex-shrink:0} +.q-dismiss:hover{color:var(--text-dimmed-hover)} .q-body{padding:10px 16px 14px} /* Answer summary */ .q-answer-row{padding:10px 16px 14px;display:flex;align-items:center;justify-content:space-between} -.q-answer-text{font-size:13.5px;color:#aaa} -.q-edit-btn{font-size:11px;color:#444;background:none;border:none;cursor:pointer;font-family:inherit} -.q-edit-btn:hover{color:#888} +.q-answer-text{font-size:13.5px;color:var(--text-sub-heading)} +.q-edit-btn{font-size:11px;color:var(--text-dimmed);background:none;border:none;cursor:pointer;font-family:inherit} +.q-edit-btn:hover{color:var(--text-dimmed-hover)} /* Open textarea */ -.q-textarea{width:100%;background:transparent;border:none;border-bottom:1px solid rgba(255,255,255,.1); - color:#e0e0e0;font-size:14px;font-family:inherit;resize:none;outline:none;padding:4px 0;line-height:1.6} +.q-textarea{width:100%;background:transparent;border:none;border-bottom:1px solid var(--border-medium); + color:var(--text-input);font-size:14px;font-family:inherit;resize:none;outline:none;padding:4px 0;line-height:1.6} /* Choice / Checkbox options */ .q-option{display:flex;align-items:center;gap:10px;padding:9px 12px;border-radius:8px;cursor:pointer; - border:1px solid rgba(255,255,255,.07);background:transparent;color:#aaa;margin-bottom:6px;transition:all .15s;font-size:14.5px} -.q-option.selected{border-color:rgba(255,255,255,.18);background:rgba(255,255,255,.07);color:#fff} -.q-radio{width:16px;height:16px;border-radius:50%;border:1.5px solid #444;flex-shrink:0;display:flex;align-items:center;justify-content:center} -.q-option.selected .q-radio{border-color:#fff} -.q-radio-inner{width:7px;height:7px;border-radius:50%;background:#fff;display:none} + border:1px solid var(--border-light);background:transparent;color:var(--text-sub-heading);margin-bottom:6px;transition:all .15s;font-size:14.5px} +.q-option.selected{border-color:var(--border-accent);background:var(--surface-selected);color:var(--text-heading)} +.q-radio{width:16px;height:16px;border-radius:50%;border:1.5px solid var(--border-radio);flex-shrink:0;display:flex;align-items:center;justify-content:center} +.q-option.selected .q-radio{border-color:var(--text-heading)} +.q-radio-inner{width:7px;height:7px;border-radius:50%;background:var(--text-heading);display:none} .q-option.selected .q-radio-inner{display:block} -.q-check{width:16px;height:16px;border-radius:4px;border:1.5px solid #444;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:10px;color:#111} -.q-option.selected .q-check{background:#fff;border-color:#fff} -.q-other-input{width:100%;background:transparent;border:none;border-bottom:1px solid rgba(255,255,255,.1); - color:#e0e0e0;font-size:13px;outline:none;padding:6px 0;margin-top:4px;font-family:inherit} +.q-check{width:16px;height:16px;border-radius:4px;border:1.5px solid var(--border-radio);flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:10px;color:var(--text-inverted)} +.q-option.selected .q-check{background:var(--text-heading);border-color:var(--text-heading)} +.q-other-input{width:100%;background:transparent;border:none;border-bottom:1px solid var(--border-medium); + color:var(--text-input);font-size:13px;outline:none;padding:6px 0;margin-top:4px;font-family:inherit} /* Submit btn inside question */ .q-submit{margin-top:10px;padding:8px 18px;border-radius:8px;border:none;font-size:13px;font-family:inherit;cursor:pointer;float:right} -.q-submit.active{background:#fff;color:#111} -.q-submit.inactive{background:rgba(255,255,255,.06);color:#333;cursor:default} +.q-submit.active{background:var(--bg-inverted);color:var(--text-inverted)} +.q-submit.inactive{background:var(--border-subtle);color:var(--text-ghost);cursor:default} /* Nudge */ -.nudge{font-size:12.5px;color:#444;text-align:center;border:1px solid rgba(255,255,255,.06);border-radius:10px;padding:14px 16px;margin-top:32px} +.nudge{font-size:12.5px;color:var(--text-dimmed);text-align:center;border:1px solid var(--border-subtle);border-radius:10px;padding:14px 16px;margin-top:32px} /* ---- FAB ---- */ .fab-stack{position:fixed;bottom:36px;right:24px;z-index:30;display:flex;flex-direction:column;gap:12px;align-items:center} -.fab-notes{width:44px;height:44px;border-radius:50%;background:#1a1a1a;border:1px solid rgba(255,255,255,.1); - color:#555;font-size:16px;cursor:pointer;position:relative;display:flex;align-items:center;justify-content:center} -.fab-notes:hover{background:#222} -.fab-badge{position:absolute;top:-3px;right:-3px;width:16px;height:16px;border-radius:50%;background:#fff; - color:#111;font-size:9px;font-weight:700;display:flex;align-items:center;justify-content:center} -.fab-write{width:52px;height:52px;border-radius:50%;background:#1e1e1e;border:1px solid rgba(255,255,255,.12); - color:#777;font-size:22px;cursor:pointer;display:flex;align-items:center;justify-content:center} -.fab-write:hover{background:#262626} +.fab-notes{width:44px;height:44px;border-radius:50%;background:var(--bg-elevated);border:1px solid var(--border-medium); + color:var(--text-faint);font-size:16px;cursor:pointer;position:relative;display:flex;align-items:center;justify-content:center} +.fab-notes:hover{background:var(--bg-hover)} +.fab-badge{position:absolute;top:-3px;right:-3px;width:16px;height:16px;border-radius:50%;background:var(--bg-inverted); + color:var(--text-inverted);font-size:9px;font-weight:700;display:flex;align-items:center;justify-content:center} +.fab-write{width:52px;height:52px;border-radius:50%;background:var(--bg-fab);border:1px solid var(--border-medium); + color:var(--text-muted);font-size:22px;cursor:pointer;display:flex;align-items:center;justify-content:center} +.fab-write:hover{background:var(--bg-fab-hover)} /* ---- Overlay ---- */ .overlay{position:fixed;inset:0;z-index:40;opacity:0;pointer-events:none;transition:opacity .3s} .overlay.open{opacity:1;pointer-events:auto} /* ---- Sheet ---- */ -.sheet{position:fixed;bottom:0;left:0;right:0;z-index:50;background:#171717; - border-top:1px solid rgba(255,255,255,.07);border-radius:16px 16px 0 0; +.sheet{position:fixed;bottom:0;left:0;right:0;z-index:50;background:var(--bg-surface); + border-top:1px solid var(--border-light);border-radius:16px 16px 0 0; padding:20px 24px calc(env(safe-area-inset-bottom, 0px) + 28px);transform:translateY(110%);transition:transform .3s cubic-bezier(.32,.72,0,1); - box-shadow:0 -8px 40px rgba(0,0,0,.6)} + box-shadow:0 -8px 40px var(--shadow-color)} .sheet.open{transform:translateY(0)} -.sheet-handle{width:32px;height:3px;background:rgba(255,255,255,.1);border-radius:2px;margin:0 auto 16px} -.sheet-ctx{font-size:12px;color:#aaa;font-style:italic;border-left:2px solid rgba(255,255,255,.25); +.sheet-handle{width:32px;height:3px;background:var(--surface-handle);border-radius:2px;margin:0 auto 16px} +.sheet-ctx{font-size:12px;color:var(--text-sub-heading);font-style:italic;border-left:2px solid var(--surface-quote-border); padding-left:10px;margin-bottom:12px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden} -.sheet textarea{width:100%;background:transparent;border:none;color:#e0e0e0;font-size:16px;line-height:1.6; +.sheet textarea{width:100%;background:transparent;border:none;color:var(--text-input);font-size:16px;line-height:1.6; resize:none;outline:none;font-family:inherit} .sheet-send{margin-top:12px;margin-bottom:8px;float:right;width:40px;height:40px;border-radius:50%;border:none;cursor:pointer; display:flex;align-items:center;justify-content:center} -.sheet-send.active{background:#fff;color:#111} -.sheet-send.inactive{background:rgba(255,255,255,.06);color:#333;cursor:default} +.sheet-send.active{background:var(--bg-inverted);color:var(--text-inverted)} +.sheet-send.inactive{background:var(--border-subtle);color:var(--text-ghost);cursor:default} /* ---- Notes Panel ---- */ -.panel-overlay{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:60;opacity:0;pointer-events:none;transition:opacity .3s} +.panel-overlay{position:fixed;inset:0;background:var(--overlay-bg);z-index:60;opacity:0;pointer-events:none;transition:opacity .3s} .panel-overlay.open{opacity:1;pointer-events:auto} -.panel{position:fixed;top:0;right:0;bottom:0;width:min(340px,92vw);z-index:70;background:#161616; - border-left:1px solid rgba(255,255,255,.07);transform:translateX(100%);transition:transform .3s cubic-bezier(.32,.72,0,1); +.panel{position:fixed;top:0;right:0;bottom:0;width:min(340px,92vw);z-index:70;background:var(--bg-surface); + border-left:1px solid var(--border-light);transform:translateX(100%);transition:transform .3s cubic-bezier(.32,.72,0,1); overflow-y:auto;display:flex;flex-direction:column} .panel.open{transform:translateX(0)} .panel-head{padding:52px 20px 16px;display:flex;justify-content:space-between;align-items:center;flex-shrink:0} -.panel-title{font-size:13px;font-weight:600;color:#e0e0e0} +.panel-title{font-size:13px;font-weight:600;color:var(--text-input)} .panel-actions{display:flex;gap:12px;align-items:center} -.panel-export{font-size:11px;color:#555;background:none;border:1px solid rgba(255,255,255,.1);border-radius:6px; +.panel-export{font-size:11px;color:var(--text-faint);background:none;border:1px solid var(--border-medium);border-radius:6px; padding:4px 10px;cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;gap:5px;transition:all .2s} -.panel-export:hover{color:#aaa;border-color:rgba(255,255,255,.2)} -.panel-export.copilot-dim{color:rgba(56,132,255,.55);border-color:rgba(56,132,255,.2);background:rgba(56,132,255,.06)} -.panel-export.copilot-dim:hover{color:rgba(56,132,255,.75);border-color:rgba(56,132,255,.35)} -.panel-export.copilot-active{color:#fff;border-color:#388bfd;background:#388bfd} -.panel-export.copilot-active:hover{background:#1f6feb} -.panel-close{font-size:18px;color:#444;background:none;border:none;cursor:pointer} -.panel-close:hover{color:#888} +.panel-export:hover{color:var(--text-sub-heading);border-color:var(--border-accent)} +.panel-export.copilot-dim{color:var(--copilot-dim-text);border-color:var(--copilot-dim-border);background:var(--copilot-dim-bg)} +.panel-export.copilot-dim:hover{color:var(--copilot-dim-text-hover);border-color:var(--copilot-dim-border-hover)} +.panel-export.copilot-active{color:#fff;border-color:var(--accent-blue);background:var(--accent-blue)} +.panel-export.copilot-active:hover{background:var(--accent-blue-hover)} +.panel-close{font-size:18px;color:var(--text-dimmed);background:none;border:none;cursor:pointer} +.panel-close:hover{color:var(--text-dimmed-hover)} .panel-section{padding:0 20px 16px} -.panel-label{font-size:10px;color:#444;text-transform:uppercase;font-family:monospace;letter-spacing:1px;margin-bottom:10px;margin-top:16px} +.panel-label{font-size:10px;color:var(--text-dimmed);text-transform:uppercase;font-family:monospace;letter-spacing:1px;margin-bottom:10px;margin-top:16px} .panel-q-item{margin-bottom:14px;padding:6px 8px;border-radius:6px;transition:background .15s} -.panel-q-item:hover{background:rgba(255,255,255,.04)} -.panel-q-question{font-size:11px;color:#555;margin-bottom:3px} -.panel-q-answer{font-size:13.5px;color:#ccc} +.panel-q-item:hover{background:var(--surface-hover)} +.panel-q-question{font-size:11px;color:var(--text-faint)} +.panel-q-answer{font-size:13.5px;color:var(--text-secondary)} .panel-c-item{margin-bottom:14px;padding:6px 8px;border-radius:6px;transition:background .15s} -.panel-c-item:hover{background:rgba(255,255,255,.04)} -.panel-c-ctx{font-size:11px;color:#3a3a3a;font-style:italic;border-left:2px solid rgba(255,255,255,.08);padding-left:8px;margin-bottom:3px} -.panel-c-note{font-size:13px;color:#ccc} -.panel-c-label{font-size:10px;color:#444;font-family:monospace;margin-bottom:3px} +.panel-c-item:hover{background:var(--surface-hover)} +.panel-c-ctx{font-size:11px;color:var(--text-ghost);font-style:italic;border-left:2px solid var(--border-light);padding-left:8px;margin-bottom:3px} +.panel-c-note{font-size:13px;color:var(--text-secondary)} +.panel-c-label{font-size:10px;color:var(--text-dimmed);font-family:monospace;margin-bottom:3px} .panel-footer{padding:16px 20px 28px;flex-shrink:0;margin-top:auto} -.panel-dl{width:100%;padding:12px;border-radius:10px;background:#fff;color:#111;border:none; +.panel-dl{width:100%;padding:12px;border-radius:10px;background:var(--bg-inverted);color:var(--text-inverted);border:none; font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;justify-content:center;gap:6px;transition:all .2s} -.panel-dl:hover{background:#e8e8e8} -.panel-dl.copilot-dim{background:rgba(56,132,255,.15);color:rgba(56,132,255,.6);border:1px solid rgba(56,132,255,.15)} -.panel-dl.copilot-dim:hover{background:rgba(56,132,255,.22);color:rgba(56,132,255,.75)} -.panel-dl.copilot-active{background:#388bfd;color:#fff;border:none} -.panel-dl.copilot-active:hover{background:#1f6feb} -.panel-dl-cap{font-size:11px;color:#333;text-align:center;margin-top:6px} -.panel-empty{color:#333;font-size:13px;padding:40px 20px;text-align:center} +.panel-dl:hover{background:var(--bg-inverted-hover)} +.panel-dl.copilot-dim{background:var(--copilot-dl-bg);color:var(--copilot-dim-text);border:1px solid var(--copilot-dl-bg)} +.panel-dl.copilot-dim:hover{background:var(--copilot-dl-bg-hover);color:var(--copilot-dim-text-hover)} +.panel-dl.copilot-active{background:var(--accent-blue);color:#fff;border:none} +.panel-dl.copilot-active:hover{background:var(--accent-blue-hover)} +.panel-dl-cap{font-size:11px;color:var(--text-ghost);text-align:center;margin-top:6px} +.panel-empty{color:var(--text-ghost);font-size:13px;padding:40px 20px;text-align:center} /* Toast */ .toast{position:fixed;bottom:100px;left:50%;transform:translateX(-50%) translateY(20px); - background:#222;color:#ccc;font-size:13px;padding:8px 18px;border-radius:8px;opacity:0; - transition:all .3s ease;pointer-events:none;z-index:80;border:1px solid rgba(255,255,255,.08)} + background:var(--bg-hover);color:var(--text-secondary);font-size:13px;padding:8px 18px;border-radius:8px;opacity:0; + transition:all .3s ease;pointer-events:none;z-index:80;border:1px solid var(--border-light)} .toast.show{opacity:1;transform:translateX(-50%) translateY(0)} /* Block highlight flash */ -@keyframes flash{0%{background:rgba(199,146,234,.15)}100%{background:transparent}} +@keyframes flash{0%{background:var(--flash-start)}100%{background:transparent}} .block-flash{animation:flash .8s ease-out} /* Sheet hint */ -.sheet-hint{font-size:11px;color:#444;margin-top:6px;clear:both} +.sheet-hint{font-size:11px;color:var(--text-dimmed);margin-top:6px;clear:both} /* Progress bar */ -.nudge-bar{height:3px;border-radius:2px;background:rgba(255,255,255,.06);margin-top:10px;overflow:hidden} -.nudge-bar-fill{height:100%;background:#c792ea;border-radius:2px;transition:width .4s ease} +.nudge-bar{height:3px;border-radius:2px;background:var(--border-subtle);margin-top:10px;overflow:hidden} +.nudge-bar-fill{height:100%;background:var(--accent-purple);border-radius:2px;transition:width .4s ease} /* Delete button in panel */ -.panel-delete{font-size:11px;color:#444;background:none;border:none;cursor:pointer;float:right;margin-top:-2px} -.panel-delete:hover{color:#e55} +.panel-delete{font-size:11px;color:var(--text-dimmed);background:none;border:none;cursor:pointer;float:right;margin-top:-2px} +.panel-delete:hover{color:var(--accent-error-hover)} /* Scroll progress bar */ -.scroll-progress{position:fixed;top:0;left:0;width:0%;height:3px;background:#c792ea;z-index:90;transition:width .1s linear;border-radius:0 0 2px 0} +.scroll-progress{position:fixed;top:0;left:0;width:0%;height:3px;background:var(--accent-purple);z-index:90;transition:width .1s linear;border-radius:0 0 2px 0} .outline{position:fixed;top:0;left:0;bottom:0;width:200px;z-index:20;padding:60px 16px 40px; overflow-y:auto;display:none} -.outline-item{display:block;font-size:12px;color:#444;padding:4px 0;cursor:pointer; +.outline-item{display:block;font-size:12px;color:var(--text-dimmed);padding:4px 0;cursor:pointer; text-decoration:none;transition:color .15s;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} -.outline-item:hover{color:#aaa} -.outline-item.active{color:#c792ea} -.outline-item.level-h1{font-weight:600;color:#666;font-size:13px;margin-top:8px} +.outline-item:hover{color:var(--text-sub-heading)} +.outline-item.active{color:var(--accent-purple)} +.outline-item.level-h1{font-weight:600;color:var(--text-muted);font-size:13px;margin-top:8px} .outline-item.level-h2{padding-left:8px;margin-top:6px} .outline-item.level-h3{padding-left:16px} /* Mobile outline: overlay drawer */ .outline-toggle{position:fixed;top:16px;left:16px;z-index:25;width:36px;height:36px;border-radius:50%; - background:#1a1a1a;border:1px solid rgba(255,255,255,.1);color:#666;font-size:14px;cursor:pointer; + background:var(--bg-elevated);border:1px solid var(--border-medium);color:var(--text-muted);font-size:14px;cursor:pointer; display:flex;align-items:center;justify-content:center} -.outline-toggle:hover{background:#222;color:#aaa} +.outline-toggle:hover{background:var(--bg-hover);color:var(--text-sub-heading)} @media(max-width:1099px){ - .outline{background:#161616;border-right:1px solid rgba(255,255,255,.07);z-index:75; + .outline{background:var(--bg-surface);border-right:1px solid var(--border-light);z-index:75; transform:translateX(-100%);transition:transform .3s cubic-bezier(.32,.72,0,1);display:block} .outline.open{transform:translateX(0)} } @@ -218,7 +278,7 @@ body{background:#111;color:#999;font-family:-apple-system,BlinkMacSystemFont,'Se .doc{margin-left:200px} } -.outline-overlay{position:fixed;inset:0;z-index:74;background:rgba(0,0,0,.4);opacity:0;pointer-events:none;transition:opacity .3s} +.outline-overlay{position:fixed;inset:0;z-index:74;background:var(--overlay-outline);opacity:0;pointer-events:none;transition:opacity .3s} .outline-overlay.open{opacity:1;pointer-events:auto} /* ---- Desktop: panel always open ---- */ diff --git a/packages/vscode/images/hero-demo.gif b/packages/vscode/images/hero-demo.gif index 3460c10..88ee9e0 100644 Binary files a/packages/vscode/images/hero-demo.gif and b/packages/vscode/images/hero-demo.gif differ diff --git a/packages/vscode/images/hero-screenshot.png b/packages/vscode/images/hero-screenshot.png index 1abb428..80b28d5 100644 Binary files a/packages/vscode/images/hero-screenshot.png and b/packages/vscode/images/hero-screenshot.png differ diff --git a/packages/vscode/markdown-plan-review-feedback/SKILL.md b/packages/vscode/markdown-plan-review-feedback/SKILL.md index fc5fda9..f3fe50a 100644 --- a/packages/vscode/markdown-plan-review-feedback/SKILL.md +++ b/packages/vscode/markdown-plan-review-feedback/SKILL.md @@ -1,11 +1,13 @@ --- name: markdown-plan-review-feedback -description: Generate markdown (*.md) with inline questions for the user to answer. Use when designing plans, clarifying specs, or gathering structured feedback on proposals. +description: Generate markdown with inline questions for the user to answer. Use when designing plans, clarifying specs, or gathering structured feedback on proposals. --- # Plan Review Skill -Generate or edit a `*.md` file that the user will review in an interactive Plan Review UI. The UI renders your markdown and lets the user answer embedded questions, leave inline annotations on any block, and add general comments. +Generate or edit a review file that the user will review in an interactive Plan Review UI. The UI renders your markdown and lets the user answer embedded questions, leave inline annotations on any block, and add general comments. + +**File extension:** Check the VS Code setting `planReview.fileSuffix` for the configured extension (default: `.spec.md`). Always create files with this suffix (e.g. `plan.spec.md`, `auth-design.spec.md`). The review editor only activates for files matching this suffix. ## Asking Questions diff --git a/packages/vscode/package.json b/packages/vscode/package.json index 766a944..5e64d29 100644 --- a/packages/vscode/package.json +++ b/packages/vscode/package.json @@ -38,6 +38,16 @@ "README.md" ], "contributes": { + "configuration": { + "title": "Markdown Review", + "properties": { + "planReview.fileSuffix": { + "type": "string", + "default": ".spec.md", + "description": "File suffix that triggers the Plan Review editor (e.g. '.spec.md', '.review.md', or '.md' for all markdown files). The Copilot skill also uses this when creating review documents." + } + } + }, "chatSkills": [ { "path": "./markdown-plan-review-feedback/SKILL.md" @@ -58,7 +68,7 @@ "filenamePattern": "*.md" } ], - "priority": "default" + "priority": "option" } ], "menus": { diff --git a/packages/vscode/src/extension.ts b/packages/vscode/src/extension.ts index 3735690..1fb1cca 100644 --- a/packages/vscode/src/extension.ts +++ b/packages/vscode/src/extension.ts @@ -21,10 +21,22 @@ function loadCoreBundle(): string { return coreBundle; } +function resolveThemeMode(): 'system' | 'light' | 'dark' { + const autoDetect = vscode.workspace + .getConfiguration('window') + .get('autoDetectColorScheme', false); + if (autoDetect) return 'system'; + const kind = vscode.window.activeColorTheme.kind; + return (kind === vscode.ColorThemeKind.Light || kind === vscode.ColorThemeKind.HighContrastLight) + ? 'light' + : 'dark'; +} + function buildWebviewHtml(webview: vscode.Webview, markdown: string, fileName: string, documentUri?: vscode.Uri): string { const coreBundle = loadCoreBundle(); const nonce = getNonce(); const escapedMarkdown = JSON.stringify(markdown); + const themeModeValue = resolveThemeMode(); const cspSource = webview.cspSource; const baseTag = documentUri @@ -37,6 +49,7 @@ function buildWebviewHtml(webview: vscode.Webview, markdown: string, fileName: s + ${baseTag} Plan Review: ${escapeHtml(fileName)} @@ -50,6 +63,7 @@ function buildWebviewHtml(webview: vscode.Webview, markdown: string, fileName: s markdown: ${escapedMarkdown}, fileName: ${JSON.stringify(fileName)}, feedbackMode: 'vscode', + themeMode: ${JSON.stringify(themeModeValue)}, onFeedback: function(payload) { vscode.postMessage({ type: 'feedback', payload: payload }); }, @@ -102,8 +116,16 @@ class PlanReviewEditorProvider implements vscode.CustomTextEditorProvider { } }); + // Re-render when VS Code theme or autoDetectColorScheme changes + const themeSubscription = vscode.window.onDidChangeActiveColorTheme(() => updateWebview()); + const configSubscription = vscode.workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration('window.autoDetectColorScheme')) updateWebview(); + }); + webviewPanel.onDidDispose(() => { changeSubscription.dispose(); + themeSubscription.dispose(); + configSubscription.dispose(); if (cachedFeedback) { sendFeedbackToChat(cachedFeedback); cachedFeedback = null; @@ -112,7 +134,48 @@ class PlanReviewEditorProvider implements vscode.CustomTextEditorProvider { } } +const EDITOR_VIEW_TYPE = 'planReview.markdownEditor'; + +/** Keep workbench.editorAssociations in sync with planReview.fileSuffix */ +async function syncEditorAssociation() { + const suffix = vscode.workspace + .getConfiguration('planReview') + .get('fileSuffix', '.spec.md'); + const pattern = `*${suffix}`; + + const config = vscode.workspace.getConfiguration('workbench'); + const current = config.get>('editorAssociations', {}); + const updated = { ...current }; + + // Remove any existing association pointing to our editor + for (const key of Object.keys(updated)) { + if (updated[key] === EDITOR_VIEW_TYPE) { + delete updated[key]; + } + } + + // Add the new one + updated[pattern] = EDITOR_VIEW_TYPE; + + // Only write if something actually changed + if (JSON.stringify(current) !== JSON.stringify(updated)) { + await config.update('editorAssociations', updated, vscode.ConfigurationTarget.Global); + } +} + export function activate(context: vscode.ExtensionContext) { + // Sync editor association on startup + syncEditorAssociation(); + + // Re-sync when the setting changes + context.subscriptions.push( + vscode.workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration('planReview.fileSuffix')) { + syncEditorAssociation(); + } + }), + ); + // Register custom editor for .md files const provider = new PlanReviewEditorProvider(context); context.subscriptions.push( @@ -166,7 +229,17 @@ export function activate(context: vscode.ExtensionContext) { panel.webview.html = buildWebviewHtml(panel.webview, markdown, fileName, fileUri); setupMessageListener(panel.webview, context); + const updatePanel = () => { + panel.webview.html = buildWebviewHtml(panel.webview, markdown, fileName, fileUri); + }; + const themeListener = vscode.window.onDidChangeActiveColorTheme(() => updatePanel()); + const configListener = vscode.workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration('window.autoDetectColorScheme')) updatePanel(); + }); + panel.onDidDispose(() => { + themeListener.dispose(); + configListener.dispose(); if (cachedFeedback) { sendFeedbackToChat(cachedFeedback); cachedFeedback = null; diff --git a/packages/web/index.html b/packages/web/index.html index 26ccb73..0fc1d04 100644 --- a/packages/web/index.html +++ b/packages/web/index.html @@ -4,6 +4,7 @@ + Plan Review diff --git a/packages/web/src/Upload.tsx b/packages/web/src/Upload.tsx index 3045303..a8e75ef 100644 --- a/packages/web/src/Upload.tsx +++ b/packages/web/src/Upload.tsx @@ -69,8 +69,8 @@ export function Upload({ onFile }: UploadProps) { onDragLeave={onDragLeave} style={{ ...styles.dropZone, - borderColor: dragging ? '#6cf' : '#333', - background: dragging ? '#1a1a2e' : '#161616', + borderColor: dragging ? 'var(--accent-link)' : 'var(--border-quote)', + background: dragging ? 'var(--bg-drag)' : 'var(--bg-surface)', }} >

Drop your .md file here

@@ -98,10 +98,10 @@ const styles: Record = { alignItems: 'center', justifyContent: 'center', minHeight: '100vh', - background: '#111', + background: 'var(--bg-body)', }, dropZone: { - border: '2px dashed #333', + border: '2px dashed var(--border-quote)', borderRadius: '12px', padding: '48px 64px', textAlign: 'center', @@ -110,26 +110,26 @@ const styles: Record = { width: '100%', }, heading: { - color: '#999', + color: 'var(--text-body)', fontSize: '18px', margin: '0 0 8px', }, or: { - color: '#555', + color: 'var(--text-faint)', fontSize: '14px', margin: '0 0 16px', }, button: { - background: '#2a2a2a', - color: '#ccc', - border: '1px solid #444', + background: 'var(--bg-button)', + color: 'var(--text-secondary)', + border: '1px solid var(--border-button)', borderRadius: '6px', padding: '8px 20px', fontSize: '14px', cursor: 'pointer', }, error: { - color: '#f55', + color: 'var(--accent-error)', marginTop: '16px', fontSize: '14px', }, diff --git a/packages/web/src/main.tsx b/packages/web/src/main.tsx index ebd27c0..e5f9653 100644 --- a/packages/web/src/main.tsx +++ b/packages/web/src/main.tsx @@ -1,8 +1,13 @@ import { h, render } from 'preact'; import { useState, useRef, useEffect } from 'preact/hooks'; -import { PlanReview } from '@plan-review/core'; +import { PlanReview, CSS_VARS } from '@plan-review/core'; import { Upload, UploadResult } from './Upload'; +// Inject theme variables immediately so Upload screen can use them +const _style = document.createElement('style'); +_style.textContent = CSS_VARS; +document.head.appendChild(_style); + function App() { const [file, setFile] = useState(null); const reviewRef = useRef(null); @@ -43,7 +48,7 @@ function App() { const styles: Record = { root: { - background: '#111', + background: 'var(--bg-body)', minHeight: '100vh', }, header: { @@ -51,14 +56,14 @@ const styles: Record = { alignItems: 'center', gap: '12px', padding: '10px 20px', - borderBottom: '1px solid #222', + borderBottom: '1px solid var(--bg-hover)', }, fileName: { - color: '#666', + color: 'var(--text-muted)', fontSize: '13px', }, changeLink: { - color: '#6cf', + color: 'var(--accent-link)', fontSize: '13px', textDecoration: 'none', cursor: 'pointer', diff --git a/tests/__snapshots__/mermaid.spec.ts/mermaid-diagram.png b/tests/__snapshots__/mermaid.spec.ts/mermaid-diagram.png index 29cbff4..a34e279 100644 Binary files a/tests/__snapshots__/mermaid.spec.ts/mermaid-diagram.png and b/tests/__snapshots__/mermaid.spec.ts/mermaid-diagram.png differ diff --git a/tests/__snapshots__/ui-snapshots.spec.ts/annotation-dot-visible.png b/tests/__snapshots__/ui-snapshots.spec.ts/annotation-dot-visible.png index 379b1c5..7a1f784 100644 Binary files a/tests/__snapshots__/ui-snapshots.spec.ts/annotation-dot-visible.png and b/tests/__snapshots__/ui-snapshots.spec.ts/annotation-dot-visible.png differ diff --git a/tests/__snapshots__/ui-snapshots.spec.ts/basic-doc-desktop.png b/tests/__snapshots__/ui-snapshots.spec.ts/basic-doc-desktop.png index 40f14cf..79cd30c 100644 Binary files a/tests/__snapshots__/ui-snapshots.spec.ts/basic-doc-desktop.png and b/tests/__snapshots__/ui-snapshots.spec.ts/basic-doc-desktop.png differ diff --git a/tests/__snapshots__/ui-snapshots.spec.ts/basic-doc-mobile.png b/tests/__snapshots__/ui-snapshots.spec.ts/basic-doc-mobile.png index ef29851..b02696b 100644 Binary files a/tests/__snapshots__/ui-snapshots.spec.ts/basic-doc-mobile.png and b/tests/__snapshots__/ui-snapshots.spec.ts/basic-doc-mobile.png differ diff --git a/tests/__snapshots__/ui-snapshots.spec.ts/question-open-answered.png b/tests/__snapshots__/ui-snapshots.spec.ts/question-open-answered.png index 4174aa3..0bc63c6 100644 Binary files a/tests/__snapshots__/ui-snapshots.spec.ts/question-open-answered.png and b/tests/__snapshots__/ui-snapshots.spec.ts/question-open-answered.png differ diff --git a/tests/__snapshots__/ui-snapshots.spec.ts/questions-unanswered-desktop.png b/tests/__snapshots__/ui-snapshots.spec.ts/questions-unanswered-desktop.png index 8106ed8..94f5da1 100644 Binary files a/tests/__snapshots__/ui-snapshots.spec.ts/questions-unanswered-desktop.png and b/tests/__snapshots__/ui-snapshots.spec.ts/questions-unanswered-desktop.png differ diff --git a/tests/__snapshots__/ui-snapshots.spec.ts/questions-unanswered-mobile.png b/tests/__snapshots__/ui-snapshots.spec.ts/questions-unanswered-mobile.png index afac642..fbd42f2 100644 Binary files a/tests/__snapshots__/ui-snapshots.spec.ts/questions-unanswered-mobile.png and b/tests/__snapshots__/ui-snapshots.spec.ts/questions-unanswered-mobile.png differ diff --git a/tests/__snapshots__/ui-snapshots.spec.ts/questions-unanswered-tablet.png b/tests/__snapshots__/ui-snapshots.spec.ts/questions-unanswered-tablet.png index 1e9c8fc..9297b48 100644 Binary files a/tests/__snapshots__/ui-snapshots.spec.ts/questions-unanswered-tablet.png and b/tests/__snapshots__/ui-snapshots.spec.ts/questions-unanswered-tablet.png differ diff --git a/tests/__snapshots__/ui-snapshots.spec.ts/sheet-open.png b/tests/__snapshots__/ui-snapshots.spec.ts/sheet-open.png index 9ce1497..441e9ef 100644 Binary files a/tests/__snapshots__/ui-snapshots.spec.ts/sheet-open.png and b/tests/__snapshots__/ui-snapshots.spec.ts/sheet-open.png differ