diff --git a/AGENTS.md b/AGENTS.md index 72b76bbac..13e4edba6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -214,6 +214,7 @@ Keep responsibilities distinct: engine tests cover state contracts, Playwright b - Colocate `ComponentName.stories.ts` with `ComponentName.vue`. Multipart composition stories may use a descriptive family name. Preserve explicit Storybook titles and exported story names during file moves; keep default playgrounds static and give interaction flows named stories. Use deterministic fixtures and colocated Vue demos for substantial markup. - `src/components/ui/**` is store-free app design-system code; feature controls stay in their domain. - SDK property primitives remain controlled/editor-agnostic. Compose property rows from `PanelGrid`, `PanelFieldGroup`, `PanelItemRow`, and `PropertyItemRow`; use `BindableValue`, `FillRoot`, and `FillSwatch` rather than rebuilding binding/picker infrastructure. +- Do not add automated tests or snapshot baselines for simple CSS-only UI changes, including spacing, sizing, colors, and responsive breakpoints. Verify these visually instead. Keep automated coverage focused on behavior and contracts; the separate canvas-renderer visual coverage requirement still applies. - Prefer accessible role/name, label, then text in tests. Use scoped `data-slot` anatomy or semantic attributes (`data-property`, `data-command`, `data-node-id`) when needed; reserve `data-test-id` for integration boundaries and never add test-hook props. - Use Reka UI primitives and typed Tailwind Variants themes under `src/theme/**`; merge per-instance `ui` slot overrides, expose `class` for single-root components, and do not add one-off class props. Use `UI` casing in type names. - Bind visual state through semantic `data-*` attributes; Steiger rejects template-time `use*UI()`, visual-state utility branches, and raw SVG app icons. diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a3be4b2a..a2fba417a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ - Add local AI usage and technical diagnostics, including token telemetry, provider/model summaries, recent failures, configurable retention, export, and clear controls. (#588) - Import, render, edit, resize, select, and export Figma text-on-path layers while preserving their curved glyph layout. - Show temporary Figma-style distance measurements between selected and Option/Alt-hovered layers. (#491) -- Edit Design JSX and HTML/CSS previews in CodeMirror, with Tailwind viewing, completion, diagnostics, bounded execution, and session-level undo. (#130) +- Edit Design JSX and HTML/CSS previews in CodeMirror, with theme-aware highlighting, Tailwind viewing, completion, diagnostics, bounded execution, and session-level undo. (#130) - Set provider-specific reasoning effort on supported AI model profiles. (#454) - Show unavailable or substituted document fonts with affected-layer selection and retry actions, and expose font fidelity through the Figma API and MCP tooling. (#503) - Add reusable remote MCP connections for ACP agents, with Streamable HTTP endpoints and credential-backed bearer tokens. @@ -36,6 +36,7 @@ ### Changed +- Use compact desktop Home search actions with consistent responsive layout and control sizing. - Keep applied and available Effect styles concise, and collapse equal independent corner fields when all four use the same variable. - Keep pixel-grid rounding invisible while showing alignment guides only for real geometry, objects, and canvas/layout guides. - Copy selections with embedded images into Figma while preserving typed geometry, text sizing, images, components, variables, modes, and shared styles for lossless in-app paste. diff --git a/bun.lock b/bun.lock index 40aac3cb2..a8b0b17f5 100644 --- a/bun.lock +++ b/bun.lock @@ -23,6 +23,7 @@ "@codemirror/search": "^6.7.1", "@codemirror/state": "^6.7.1", "@codemirror/view": "^6.43.9", + "@lezer/highlight": "^1.2.3", "@nanostores/i18n": "^1.3.3", "@nanostores/vue": "^1.1.0", "@open-pencil/cli": "workspace:*", diff --git a/package.json b/package.json index 4665b1a52..6c6940bc2 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "@codemirror/search": "^6.7.1", "@codemirror/state": "^6.7.1", "@codemirror/view": "^6.43.9", + "@lezer/highlight": "^1.2.3", "@nanostores/i18n": "^1.3.3", "@nanostores/vue": "^1.1.0", "@open-pencil/cli": "workspace:*", diff --git a/src/components/code-editor/CodeEditor.vue b/src/components/code-editor/CodeEditor.vue index 255587611..7a7ef4298 100644 --- a/src/components/code-editor/CodeEditor.vue +++ b/src/components/code-editor/CodeEditor.vue @@ -3,14 +3,7 @@ import { closeBrackets, closeBracketsKeymap, completionKeymap } from '@codemirro import { defaultKeymap, history, historyKeymap, redo, undo } from '@codemirror/commands' import { html } from '@codemirror/lang-html' import { javascript } from '@codemirror/lang-javascript' -import { - bracketMatching, - defaultHighlightStyle, - foldGutter, - foldKeymap, - indentOnInput, - syntaxHighlighting -} from '@codemirror/language' +import { bracketMatching, foldGutter, foldKeymap, indentOnInput } from '@codemirror/language' import { lintKeymap } from '@codemirror/lint' import { searchKeymap } from '@codemirror/search' import { Compartment, EditorState, Transaction, type Extension } from '@codemirror/state' @@ -25,8 +18,10 @@ import { } from '@codemirror/view' import { onBeforeUnmount, onMounted, useTemplateRef, watch } from 'vue' +import { resolvedAppTheme } from '@/app/shell/theme' import { designJSXExtensions } from '@/components/code-editor/extensions' import type { CodeEditorLanguage } from '@/components/code-editor/types' +import { codeEditorTheme } from '@/theme/code/editor' const { modelValue, @@ -48,6 +43,7 @@ const host = useTemplateRef('host') const languageCompartment = new Compartment() const editableCompartment = new Compartment() const labelCompartment = new Compartment() +const themeCompartment = new Compartment() let editor: EditorView | undefined let externalUpdate = false @@ -78,7 +74,7 @@ onMounted(() => { drawSelection(), EditorState.allowMultipleSelections.of(true), indentOnInput(), - syntaxHighlighting(defaultHighlightStyle, { fallback: true }), + themeCompartment.of(codeEditorTheme(resolvedAppTheme.value === 'dark')), bracketMatching(), closeBrackets(), highlightActiveLine(), @@ -97,21 +93,7 @@ onMounted(() => { editableCompartment.of(editableExtensions(readOnly)), labelCompartment.of(EditorView.contentAttributes.of({ 'aria-label': label })), EditorView.lineWrapping, - EditorView.theme({ - '&': { height: '100%', backgroundColor: 'transparent', color: 'var(--color-surface)' }, - '.cm-scroller': { overflow: 'auto', fontFamily: 'var(--font-mono)' }, - '.cm-content': { padding: '12px 0', caretColor: 'var(--color-accent)' }, - '.cm-line': { padding: '0 12px' }, - '.cm-gutters': { - backgroundColor: 'transparent', - color: 'color-mix(in srgb, var(--color-muted) 45%, transparent)', - border: 'none' - }, - '&.cm-focused': { outline: 'none' }, - '.cm-selectionBackground, &.cm-focused .cm-selectionBackground': { - backgroundColor: 'color-mix(in srgb, var(--color-accent) 22%, transparent)' - } - }), + EditorView.updateListener.of((update) => { if (!update.docChanged || externalUpdate) return emit('update:modelValue', update.state.doc.toString()) @@ -155,6 +137,10 @@ watch( }) ) +watch(resolvedAppTheme, (theme) => + editor?.dispatch({ effects: themeCompartment.reconfigure(codeEditorTheme(theme === 'dark')) }) +) + onBeforeUnmount(() => editor?.destroy()) diff --git a/src/components/home/search/HomeSearchActions.vue b/src/components/home/search/HomeSearchActions.vue index e78d3d9b8..a9ac4e919 100644 --- a/src/components/home/search/HomeSearchActions.vue +++ b/src/components/home/search/HomeSearchActions.vue @@ -39,27 +39,32 @@ watch(