Skip to content

refactor(dev-tweaks): rebuild the panel on a DialKit fork - #285

Open
aimeritething wants to merge 9 commits into
mainfrom
aimeritething/grill-dialkit-frame-mode
Open

refactor(dev-tweaks): rebuild the panel on a DialKit fork#285
aimeritething wants to merge 9 commits into
mainfrom
aimeritething/grill-dialkit-frame-mode

Conversation

@aimeritething

Copy link
Copy Markdown
Member

Rebuilds @workspace/dev-tweaks on a fork of DialKit v1.4.3 (MIT), then re-establishes the interaction model and product vocabulary the swap dropped. The old hand-rolled store, panel, and styles are gone; what replaces them is owned code held to this repo's toolchain.

What changed

The panel is a fork, not a vendor drop. src/panel is first-class source — no @ts-nocheck, no Biome or ESLint carve-outs. Strict-mode type errors (noUncheckedIndexedAccess, React 19 ref types) and every lint finding were fixed for real; only cognitive-complexity and the render-phase ref-mirror pattern are suppressed inline with reasons, keeping the ported logic structurally intact. ORIGIN.md records the fork point and the MIT LICENSE stays; there is no upstream re-sync procedure. DialRoot's enabled gate moved into a wrapper so hooks no longer sit behind an early return — that was a real rules-of-hooks hazard.

The package speaks only "dev tweaks." useDevTweaks, DevTweaksRoot, DevTweaksConfig; CSS, DOM classes, and sessionStorage keys are all dev-tweaks-*. The entry is thinned to the hook, the root, config types, and cssVarOverrides. productionEnabled is dropped — the app's compile-time gate already decides whether the root mounts.

Frame docking is back, as the default posture. The panel docks as a full-height right-edge strip while the page's <body> insets into a rounded, ringed card (contain: layout keeps portals inside it). The chrome rides the top layer via popover="manual" so the contained body can't trap it. ⌃⌥T toggles globally; posture and the bubble's dragged position persist to localStorage. Below 1024px float is forced, and reduced motion zeroes the transitions.

The launcher bubble is a dirty indicator. Hidden while closed and clean, visible once any value deviates from its default. Demo builds (NEXT_PUBLIC_DEV_TWEAKS=1) keep it pinned as the visible entry point — deliberately reinstating the pre-#277 split now that the launcher is a small bubble rather than the old text capsule.

Dev mocks are a first-class panel concept. A mock is a remote control for a mode whose truth lives outside the panel (a cookie the server also rewrites), not an override value the panel owns. MockStore keeps that boundary: changes write through the feature's source and re-load from it, external changes arrive via the source's watch with unchanged echoes deduped, and mock state never enters the panel's localStorage persistence. Mocks render as badged sections ahead of tweaks, and an enabled mock counts as dirty so the launcher warns that pages show fixture data.

Header actions and dropdown portalling. The title row hosts a unified button group — a new dark/light theme toggle (persisted, default dark), the posture toggle, and close — leaving only the timeline switch on the root toolbar. The version and shortcuts menus previously portalled to document.body with position: fixed, so frame posture clipped them away and the top-layer panel painted over them in popover mode; both now follow the select-control pattern, portalling into the dev-tweaks root.

Merge note

main's cost-center work taught the old store a driver with an external-change subscription so the billing mock cookie could drive a tweaks group. That store no longer exists, so the driver plumbing is dropped with it and billing-dev-mock.tsx becomes a DevTweaksMockSource over the same cookie — the cookie protocol is unchanged, set still drops the SWR cache, and watch keeps the cookieStore/focus/interval fallbacks. The server fixture dispatcher and every /billing surface merge in untouched.

Verification

bun typecheck and bun check pass. Verified in the running app: ⌃⌥T docks the page, the Dock/Float toggle switches posture, posture survives reloads, and the bubble appears only while a value deviates from its default.

CONTEXT.md gains the Dev Tweaks glossary (Panel Posture, Panel Mode, Launcher).

🤖 Generated with Claude Code

aimeritething and others added 9 commits August 20, 2026 10:55
Vendor DialKit v1.4.3 (github.com/joshpuckett/dialkit, MIT) into
packages/dev-tweaks/src/vendor/dialkit — core + React adapter only — and
make it the whole implementation and UI of @workspace/dev-tweaks. The
package entry re-exports the DialKit API (useDialKit, useDialKitController,
useDialTimeline, DialRoot, DialTimeline) plus a package-owned
cssVarOverrides bridge that keeps the old contract: CSS defaults stay the
source of truth, inline style carries only overridden values.

- Vendored files carry a leading @ts-nocheck and are excluded from Biome
  and ESLint (see VENDOR.md); re-syncs stay clean overwrites.
- apps/ui mounts <DialRoot position="bottom-left"> behind the existing
  dev/demo build gate; frame mode, the MOCK capsule, and persistence
  drivers are gone with the old implementation.
- Onboarding and horizon tweak defs move to DialKit config tuples with
  sessionStorage persistence and stable ids. Integer sliders get explicit
  step 1 — DialKit's inferred step can be coarser than a default (102 with
  step 5) and its sliders snap values onto the step grid.
- Deps: @base-ui/react out, motion in; sideEffects keeps theme.css.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drop the vendor concept: src/vendor/dialkit becomes src/dialkit, owned
and maintained here with no upstream re-sync (VENDOR.md's sync procedure
is replaced by ORIGIN.md recording the fork point; the MIT LICENSE stays).

Hold the code to the workspace toolchain:
- remove every @ts-nocheck header and fix the resulting strict-mode type
  errors (noUncheckedIndexedAccess guards, React 19 ref/useRef types)
- remove the biome.jsonc and eslint ignore carve-outs; fix all lint
  findings for real (button types, aria on decorative svgs and
  interactive divs, stable keys, for...of, hoisted regexes, unnested
  ternaries) — cognitive-complexity and the render-phase ref-mirror
  pattern are suppressed inline with reasons rather than refactored,
  keeping the ported logic structurally intact
- split DialRoot's enabled gate into a wrapper so hooks no longer sit
  behind an early return (fixes a real rules-of-hooks hazard)
- rename all files to kebab-case per the filenaming convention

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…memory

Bring back the interaction model the DialKit swap dropped:

- ⌃⌥T toggles the panel globally (the fork's only global hotkey); the
  root open state is lifted into DialRoot so the hotkey, the bubble, and
  the header all drive one switch, threaded to Folder/Panel as a
  controlled `open` prop.
- Frame posture returns as the default: the panel docks as a full-height
  right-edge strip while the page's own <body> insets into a rounded,
  ringed card (`contain: layout` keeps every portal inside it, <html>
  carries the scrim). The chrome rides the top layer via popover="manual"
  so the contained body can't trap it. Below 1024px float is forced;
  reduced motion zeroes the transitions.
- Posture and the bubble's dragged position persist to localStorage
  (dev-tweaks.ui.v2); a Dock/Float toggle sits in the panel toolbar.
- The launcher bubble becomes a dirty indicator behind the new
  `launcher="dirty"` DialRoot option: hidden while closed and clean,
  visible once any value deviates from its default. Local dev uses it;
  demo builds (NEXT_PUBLIC_DEV_TWEAKS=1) keep the bubble pinned as the
  visible entry point — deliberately reinstating the pre-#277 split now
  that the launcher is a small bubble instead of the old text capsule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DialRoot renders null until the first useDialKit call site registers, so
the showPopover() layout effect used to fire while the popover element
did not exist yet and never re-ran — leaving the whole chrome hidden by
the UA's [popover] display:none. Gate the effect on "has surfaces" so it
re-runs when the root actually mounts. Verified in the running app:
⌃⌥T docks the page (contain: layout keeps portals inside the card,
margin-right 32×2+280), the toolbar Dock/Float toggle switches posture,
posture persists across reloads, and the launcher bubble appears only
while a value deviates from its default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keep the fork as implementation (ORIGIN.md + MIT LICENSE) but make
@workspace/dev-tweaks speak only "dev tweaks": useDevTweaks,
DevTweaksRoot, DevTweaksConfig; move src/dialkit to src/panel; rename
CSS, DOM classes, and sessionStorage keys to dev-tweaks-*. Thin the
package entry to the hook, the root, config types, and cssVarOverrides.
Drop productionEnabled — the app's compile-time gate already decides
whether the root mounts.
…r root

Both menus portalled to document.body with position: fixed, so in frame
posture the contained body clipped them away entirely, and in popover
mode the top-layer panel painted over them regardless of z-index. Port
them to the select-control pattern (portal into the dev-tweaks root,
absolute positioning via getDropdownPosition), re-enable pointer-events
for dropdowns portalled directly into the popover root, and read
presets/activePresetId through useSyncExternalStore with a stable empty
snapshot so preset changes update reactively.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The title row now hosts a unified header-button group — a new dark/light
theme toggle (persisted in UI prefs, default now dark), the posture
toggle, and close — replacing the scattered toolbar buttons; the root
toolbar keeps only the timeline switch. Frame posture widens to 340px.
CONTEXT.md gains the Dev Tweaks glossary (Panel Posture, Panel Mode,
Launcher).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A mock is a remote control for a mode whose truth lives outside the panel
(a cookie the server also rewrites), not an override value the panel owns.
MockStore keeps that boundary: every change writes through the feature's
source and is re-loaded from it, external changes arrive via the source's
watch with unchanged echoes deduped, and mock state never enters the
panel's localStorage persistence. The panel renders registered mocks as
badged sections ahead of tweaks, and an enabled mock counts as dirty so
the launcher warns that pages show fixture data.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
main's cost-center work taught the old dev-tweaks store a driver with an
external-change subscription so the billing mock cookie could drive a
tweaks group. This branch had already replaced that store with the
DialKit-fork panel, which now models mocks first-class — so the driver
plumbing (store.ts, types.ts, registry.test.tsx) is dropped with the old
store, and billing-dev-mock.tsx becomes a DevTweaksMockSource over the
same cookie: load/set parse and write the unchanged cookie protocol, set
still drops the SWR cache, and watch keeps the cookieStore/focus/interval
fallbacks. The server fixture dispatcher and every /billing surface merge
in untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant