Commit graph

4 commits

Author SHA1 Message Date
Danila Poyarkov 6a3960a5d4
feat(code): link the Code tab to canvas layers and sync both ways (#805)
* feat(code): link code to canvas layers and underline design issues

Code in the Code tab and layers on the canvas were unrelated: finding the
element behind a layer, or the layer behind an element, meant reading names.

Generated Design JSX and Tailwind JSX report the layer behind each element
in the order elements open, and edited Design JSX keeps the source line of
every element through the sandbox and renderer, so hovering an element
highlights its layer, Cmd/Ctrl-click brings it into view without changing
the selection the code shows, and errors and warnings from the design check
are underlined on the property that causes them.

* feat(code): explain the Code tab when nothing is selected

With no selection the editor showed a starter frame that read like a real
layer. The tab now says it shows the selected layers' code and offers Write
JSX, which opens the editor focused on the starter template.

* refactor(code): group code-to-layer linking into its own domain

Layer link types, issue mapping, and the hover and reveal behavior move
from the Code panel and a component file into src/app/code/layers, with
useCodeLayers as the panel's entry point, so app code no longer imports
types from components.

* fix(code): underline off-scale gaps after the spacing rule renamed its property

* feat(code): mark the layer of the element around the cursor

Hover highlighting and ⌘-click reveal replaced by one model: the element
around the cursor marks its opening and closing tag names and outlines its
layer on the canvas while the editor has focus. ⌘-click also collided
with CodeMirror's add-a-cursor gesture. Read-only Tailwind JSX now takes a
cursor so it links the same way.

Leaving the editor now ends a live Design JSX edit as one undo step.
Before, canvas edits made after typing never reached the code until the
tab was reopened, and their undo entries landed before the edit's.

* feat(code): sync the Code tab and the canvas both ways by patching

Canvas edits now patch the Design JSX a person wrote instead of waiting
for them to leave the editor: each linked element remembers the layer as
Design JSX last wrote it, and a canvas change rewrites only the attributes,
text and child elements that differ from that base, as CodeMirror changes
that keep the cursor, comments, formatting and history. Attributes written
as expressions are never overwritten; the code marks them when the canvas
now differs. Untouched code is regenerated with a minimal text change.

Code edits update layers in place: the new render is reconciled into the
existing layers (reconcileRenderedLayers), which keep their ids, so links,
selection and canvas edits survive typing. Each edit is one coalesced undo
step, replacing the restore-and-rerender preview and the commit on blur.

* feat(code): patch reordered layers and aliased properties in edited code

Reordering layers on the canvas now moves their elements in code a person
wrote: each child element and the blank lines and comments above it form a
block kept as written, and the children are written again in the new
order, staying linked. Children that cannot move safely, such as a loop
between them, keep their order and are marked.

Properties accepted under several names now come from one alias table in
the Design JSX schema, which the renderer resolves through and the patcher
and issue underlines use, so a canvas change to `w` patches `width` where
the person wrote that, instead of adding a second attribute.

* feat(code): keep the cursor in moved code and patch values written in style

A reorder rewrites the children span in one change, which collapsed a
cursor or out-of-sync marker inside a moved element to the span's edge.
The patch now carries where each block moved and places selections and
markers inside it at their new position.

Properties the renderer also reads from style={{ … }} come from a table in
the Design JSX schema instead of a hand-written list, keeping the rule
that an attribute under any of its names wins. The patcher uses it to
update a value written in style where it is, as a number or a px string
as written; values the renderer cannot read, such as '50%', are marked.

The layer patcher is split by concern: syntax helpers, attribute and
style patches, child patches, out-of-sync state and transaction assembly.

* feat(code): show the code's layer on the canvas as a tinted box

The layer of the element around the cursor used the canvas hover slot, so
moving the pointer over the canvas replaced it and the two read the same.
It now has its own shared editor state, codeFocusNodeId, drawn as the hover
outline over a light tint in every pane: hover stays an outline and the
selection keeps its handles, without borrowing the dashed outlines that
already mean component sets, drag parents and ghosts.

* fix(code): write added and removed layers when a reorder cannot move the code

When children could not be moved, such as two written on one line, the
patch marked the order and returned before adding or removing elements,
so a layer created in the same change never reached the code. It now
marks the order and still writes additions and removals.

* refactor(design-jsx): format the rebased layer description and stroke aliases

* feat(code): mount the layer-linked code editor through useCodeMirror

Master moved the code editor onto the shared useCodeMirror composable.
Its layer links, issue underlines, canvas patches, minimal text updates,
autofocus and read-only cursor now sit on that composable instead of a
hand-mounted view.
2026-10-04 10:08:40 +00:00
Danila Poyarkov f6848434ec
feat: check designs live with a Lint panel, canvas markers, and fixes (#804)
* feat: check designs live with a Check panel and canvas issue markers

Design lint only ran from the CLI and AI tools, and its rules were too noisy
to show continuously: on a real imported page 786 of 888 layers had a
warning. The rules now report where a finding is actionable (a hardcoded
color only when a variable matches it, nesting only where the limit is
crossed, instance sublayers through their main component) and carry
structured data, and Recommended keeps warnings for likely problems.

The app checks the current page after edits settle. The Check tab groups
issues by rule with hover highlighting, reveal on click, and one-step
variable binding. Errors and warnings are marked on the canvas with
clustered markers that roll up to visible ancestors when zoomed out; markers
explain themselves on hover, open Check on click, and toggle with
View > Design issues.

* fix: keep the right panel and markers stable

The Check tab made the right-panel tab row overflow at common window widths,
so focusing the zoom menu scrolled the row and shifted the panel. Code and
AI tabs now drop their labels to screen readers when the row is narrow.

Touch target names are matched as whole words: "Rectangle" contained "cta"
and marked every rectangle. Markers also stay drawn during interactive edits
instead of blinking while a value is scrubbed.

* fix(ui): show right panel tab labels whenever they fit

* fix(ui): name the design check tab Lint and keep panel tabs consistent

The tab was an unlabelled icon between labelled Code and AI tabs. It is now
Lint, with the same icon and label anatomy as its neighbours, and its icon
takes the severity color instead of a count badge. All labelled tabs show
their labels when the row fits and drop them together when it does not.

* refactor(ui): build the Lint panel from shared components

Issue groups use AppCollapsible, actions use AppButton, and the severity
filters are a Reka toggle group with keyboard navigation. Issue rows no
longer nest a button inside a button. Panel state, visibility and the
focused-issue scroll live in useDesignCheckPanel, the rules menu is its own
component, and rule preferences change through preference actions.
Severity ordering reuses Core's ranking, detail numbers follow the app
language, and the check debounce uses useTimeoutFn.

* fix(lint): check the WCAG AA touch target size in the Recommended preset

Recommended flagged a 394 × 39 input because it required the 44 × 44 AAA size. It now checks the 24 × 24 AA minimum through a minSize option; Strict and Accessibility keep 44 × 44.

* feat(lint): fix design issues from rules, the Lint panel, the CLI, and agents

Rules attach fixes as data: a safe fix keeps the design as it looks (bind a
color to the variable it matches, round subpixel geometry that layout does
not own), a suggestion changes values (snap radius and spacing to the
scale, raise small text to the minimum). One Core applier re-validates
each fix against the current graph and merges changes per layer.

The Lint panel offers a fix per row and Fix all for safe fixes as one undo
step; openpencil lint --fix writes the fixed document; the lint and
lint_fix tools expose the same to MCP and AI chat.

The design-check spec's Close button is now 24 x 20: at 24 x 24 it passes
the WCAG AA touch target size that Recommended checks.

* feat(lint): pin issues outside the view to the canvas edge

Errors and warnings on layers outside the viewport had no marker, so a
check could report issues nobody could see. They are now pinned to the
canvas edge where a ray from the viewport center toward them leaves it,
with a chevron pointing their way; pins in one direction merge like
markers. Hovering lists them under the direction they lie in, and
clicking reveals and opens the most severe, nearest one.

Pins keep clear of UI floating over the canvas: the toolbar marks itself
with data-canvas-obstacle, and canvases report such rectangles to the
renderer through getOverlayObstacles each frame.

* feat(lint): mark layers with design issues in the Layers panel

Like an IDE marks files with problems and the folders holding them, a
layer with errors or warnings shows the most severe as an icon, and a
collapsed layer with issues inside it shows a dot in that color.
Suggestions stay in the Lint panel, as on the canvas, and the marks
follow the View → Design issues toggle.

* feat(lint): show issues per page and across the document

Loaded pages beyond the current one are now checked in the background,
one page at a time while the editor is idle, and checked again only when
an edit touches them; pages a large .fig file has not loaded are left
alone until opened rather than forced in. The page list shows each page's
errors and warnings like an IDE's problem count, and the Lint panel gains
a Document scope that lists every page's issues, tags the ones on other
pages, and switches to a row's page when it is opened.

* test(lint): use the core-tests alias and no comma operator in lint tests

Master now rejects ../../ imports and the comma operator in tests.

* refactor(app): create the Lint session with the editor store modules

The composition root passed its line budget once master added recent
pages; the Lint session belongs with the other per-editor services that
the modules factory creates and disposes.

* docs(changelog): keep master's latest Unreleased entries
2026-10-04 10:08:39 +00:00
Danila Poyarkov 904f3ca390
fix: use official Homebrew cask installation guidance (#712)
* fix: use official Homebrew cask installation guidance

* docs: guide localized pages through the old Homebrew tap

The translated getting-started pages documented the official cask but not
the migration from the archived tap, so readers of those pages had no
uninstall step for the old formula.
2026-09-17 13:36:17 +03:00
Danila Poyarkov 3ef554ab2d docs: reorganize documentation and improve integrity
- Replace the ambiguous Guide section with explicit overview, reference, and development routes while preserving legacy URLs with redirects
- Route missing localized content to maintained canonical pages and emit SEO alternates only for real translations
- Add parser-backed documentation integrity checks and make the optimized local build the default while retaining a complete production build
2026-08-13 14:19:12 +03:00