feat: point-and-change — hold Option to see (and change) which layer owns any pixel - #631
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…olver W1 of exploration 0399. Hold Option: the element under the pointer is outlined and labelled with the layer that owns it and one sentence saying what a change there would move. - packages/devkit/src/blast-radius.ts — resolveLane() routes a pointed element to the lowest lane that can satisfy a change (token/slot/plugin/ source), keeping LANE (mechanism) separate from SCOPE (how much moves). Kernel packages are refused as returned data, not a throw. - apps/web/src/dev/source-stamp.ts + jsx-dev-runtime-stamp.ts — the stamp itself. A Babel visitor cannot work here: esbuild lowers JSX before plugin-react's Babel pass runs, so JSXOpeningElement never fires. Instead the dev JSX runtime is shimmed and esbuild's own source argument is folded into props, which also survives React 19 dropping _debugSource. - scripts/guard-no-source-stamp.mjs — three tiers: the config must gate on command === 'serve', nothing may import the shim by name, and no built asset may carry the attribute. Wired into CI. - W1 ships with no edit affordance by design; a test asserts it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
W2 of exploration 0399. Option-click an element and the panel leads with the blast-radius sentence, then offers the smallest control that can make the change: the token's value, or the slot's own registered commands. - packages/ui/src/theme/tokens.ts holds the token-override contract as plain functions, keyed by custom-property name (never by element, which would be a second source of truth). ThemeProvider delegates to it, so there is one implementation with two entry points. - That second entry point is load-bearing: the overlay is mounted at the app root to cover onboarding and loading screens, which puts it outside App's ThemeProvider. Calling useTheme() there threw and took the whole app down. A test now renders the panel with no provider to lock that in. - inspect/lane1.ts runs the ALREADY REGISTERED slot commands, the same ones the palette and drag handles use. Every applier returns its own inverse, and the inverse reports whether it ran rather than clearing the Undo button and implying success. - Fixes two bugs found while testing: a bare function passed to setUndo was treated by React as an updater and invoked the reversal immediately; and token attribution compared HSL component triples against computed rgb() strings, which can never match and silently disabled Lane 1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…ew, draft PR W3 of exploration 0399, node side. Everything that must be true or shown before an agent edits xNet's own source from a gesture. - probeDevEnvironment() follows 0393's ladder: checkout, pnpm, gh. gh is probed but excluded from 'ready' — a user can run tasks and keep local checkpoints without ever opening a PR. - assertEditable() re-derives the lane and the kernel rule from the source ref rather than trusting the incoming verdict. A check that only runs on the side that can be bypassed is not a check; a test forges an allowed kernel resolution and it is still refused. - previewWorktree() refuses to bind the editing session's port and resolves only on a confirmed readiness line for the port it asked for. Sharing the port is how a broken edit takes down the surface you are editing from. - reviewWorktree() returns the diff, the file list and the gate result, and is prReady only when the gate passed AND something changed — a green gate over an empty diff is a task that did nothing. - openPullRequest() now opens a draft by default and never merges. Also fixes a false positive found by live verification: Tailwind's preflight sets border-color on every element, so attributing it made nearly the whole tree claim to be a Lane 1 token change and hid Lanes 2 and 3. Paint attribution now requires a drawn border and, for text colour, text the element owns. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Contributor
🖼️ UI changes in this PRNo visual differences detected in the changed UI. |
Contributor
|
Preview removed for PR #631. |
…ency Adding @xnetjs/devkit to apps/web meant a non-frozen pnpm install, which churned the lockfile far beyond the new entry — it flipped jiti@1.21.7 to 2.6.1 across most resolutions and dropped 131 lines. That re-resolution broke better-sqlite3's native build in CI and shifted a generated dts chunk hash in packages/data, failing all three test shards and typecheck on a change that touches neither. The leaf is browser-safe and needs no package boundary: alias it to source in vite.config.ts and mirror the mapping in tsconfig paths, the same shape @xnetjs/react already uses. Lockfile and apps/web/package.json return to main's state exactly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements W1–W3 of exploration 0399. Marked
[-]— 25/30 items; the four deferred ones are listed in the doc with reasons.What you can do now
Hold ⌥ and hover: the element under the pointer is outlined and labelled with the layer that owns it and one sentence on what changing it would move. ⌥-click opens a panel that leads with that sentence, then offers the smallest control that can make the change.
packages/*/apps/*sourcepackages/{sync,crypto,identity,data}Verified live in the running app, not just in tests: all five verdicts, plus apply-and-undo writing and clearing a real token.
Three decisions worth reviewing
The stamp is a JSX runtime shim, not a Babel plugin. A
JSXOpeningElementvisitor never fires here —@vitejs/plugin-reactdoesn't transform JSX under the automatic runtime; esbuild does, earlier. By the time Babel runs, every<div>is already ajsxDEV(...)call. SojsxImportSourcepoints at a shim that reads thesourceargument esbuild already passes. Side benefit: it doesn't depend on React storing it, so it survives React 19 dropping_debugSource.Lane ≠ blast radius. A Lane 1 token edit is the cheapest mechanism available and repaints the whole app; a Lane 3 edit drags a PR through CI and may move one button.
Resolutioncarries both, and the UI shows the scope sentence — otherwise "make this blue" quietly restyles everything.No per-element override store.
packages/ui/src/theme/tokens.tsis the token contract as plain functions;ThemeProviderdelegates to it. Keyed by custom-property name, never by element — a per-element map would be a second source of truth and permanently fork the app's appearance from its stylesheet (the documented Plasmic failure mode).Safety
scripts/guard-no-source-stamp.mjs: the config must gate oncommand === 'serve', no source file may import the shim by name, and no built asset may contain the attribute. Verified against a real production build (0 hits) and verified to go red when deliberately broken. Wired into CI.assertEditable(), which re-derives from the source ref. A test forges anallowed: truekernel resolution; it is still refused.previewWorktree()refuses the session's own port. Sharing it is how a broken edit takes down the surface you're editing from.lane3Prompt()takes aResolution(source location) and the user's instruction. The pointed element's rendered text — workspace content, possibly authored by someone else — has no field to travel through.Four bugs found while building, all now regression-tested
setUndo— React treats it as an updater and called it, un-applying the change immediately.0 0% 98%) against computedrgb(...); they can never match, so Lane 1 silently did nothing.border-coloron every element, so an unguarded border check made nearly the whole tree claim Lane 1 and hid Lanes 2 and 3. Attribution now requires a drawn border, and text colour requires text the element owns.App'sThemeProvider—useTheme()threw and took the whole app down. That's the self-bricking hazard in miniature; a test now renders the panel with no provider.Checks
turbo run typecheck97/97 ·vitest run11,267 passed / 3 skipped · lint clean · guard green · changeset for@xnetjs/devkit+@xnetjs/ui(both minor) · changelog fragment added.Also points 0190 at this doc — its dev-loop body was built and had no front end.
🤖 Generated with Claude Code