Rewrite with React & Tailwind CSS - #1
Draft
romeoahmed wants to merge 129 commits into
Draft
Conversation
1. Fix teal color: #7dc4e4 → #8bd5ca (was duplicate of sapphire) 2. Split tsconfig into app (browser, no bun) + node (bun/config files); remove sourceMap:false and noUncheckedSideEffectImports 3. Add src/lib/throttle.ts leading+trailing throttle utility 4. Fix useSimulation: throttle slider param Comlink calls at 16ms to prevent unbounded Promise queue growth on slider drag (~60Hz) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New branch for complete React + WebGPU rewrite. Removes old PixiJS/SCSS stack. Includes all production and dev dependencies, strict TypeScript config split into app (browser) and node (bun) configs, Catppuccin Macchiato theme via @catppuccin/palette, IBM Plex font pair. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix TS error code reference in global.d.ts (TS2307, not TS2882) - Remove redundant build.sourcemap:false from vite.config.ts (Vite default) - Add comment to toCssVars() explaining its JS-context usage
Biome 2.4: organizeImports under assist.actions.source, files.includes with negations instead of files.ignore, noImportantStyles:off for accessibility !important in prefers-reduced-motion block. - css.parser.tailwindDirectives:true to allow @theme syntax - Folder excludes without trailing /** (Biome 2.2+ convention) - passWithNoTests:true so vitest exits 0 when no tests exist yet - Auto-fixed: vite.config.ts import order, globals.css font-feature-settings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Defines all shared interfaces (circuit graph, physics config, DI types), default physics constants, Zod schemas for voltage spec and circuit definition validation, and leading+trailing throttle for Comlink rate-limiting. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ypes Explicit interface annotation widened const literal types to number, making 'as const' inert. 'as const satisfies T' preserves narrow types (e.g. dashPattern as readonly [number, number]) while still enforcing the interface shape. Also adds color-sourcing comment.
ClockSource, SignalSource, DFlipFlop now accept ComponentDeps instead of separate config+rng args, enabling direct registration with ComponentRegistry.
CircuitGraph instantiates components/nets from definition, propagates voltages, topologically sorts combinational logic. SimulationEngine owns graph + WaveformBuffer, runs fixed-timestep accumulator at 10kHz.
Worker entry exposes loadCircuit, setParam, setSettings, start/stop via Comlink. Tick loop at ~120Hz with fixed-timestep accumulator. Sends frame data to Render Worker via MessageChannel at ~120Hz, voltage status to main thread at ~20Hz.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…unused interpolants Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
plugin-react@6 dropped Babel support, leaving the Lingui macro transform silently disabled. Both dev builds and tests relied on an accidental no-op. SWC transforms macros at build and test time, unblocking production builds and letting the macro mocks in src/test/setup.ts stay a belt-and-suspenders safety net.
Added shadcn token aliases to src/styles/globals.css in both dark and light theme blocks, mapping their CSS vars (--background, --foreground, --card, --primary, etc.) onto the project's existing Apple tokens. Also exposed these aliases to Tailwind via @theme inline with --color-* prefixes and radius utility variants. Fixed import order in src/lib/utils.ts to pass Biome linting. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…group, dialog, sheet) Patch slider.tsx for exactOptionalPropertyTypes compatibility (conditional spread of defaultValue/value) and add a biome-ignore for the inherent thumb-index key (thumbs have no stable id).
Replace `space-y` wrapper and individual `grid-cols-[1fr_auto]` declarations with a unified parent grid that defines column tracks once. Child rows use `col-span-2 grid grid-cols-subgrid` to inherit those exact tracks, ensuring label columns and value columns align across all rows regardless of content width. Updates: - ControlPanel: parent grid with grid-cols-[1fr_auto] gap-x-3 gap-y-2 - ParamSlider: subgrid row with explicit row-start-2 for full-width slider - ParamToggle: subgrid row with label + switch - ParamMomentary: subgrid row with label + momentary button - SettingsSheet: voltage input section uses same pattern Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add SheetDescription/DialogDescription to SettingsSheet, AboutSheet, and ShortcutsOverlay so Radix stops warning about missing description linkage. Wrap atom-driven store.set in act() inside the ProbeStateAnnouncer test so React stops warning about unwrapped updates.
…ng Lingui - CLAUDE.md: tech stack (SWC + shadcn + Lingui), atom inventory, ui/ directory, dual-theme + shadcn token-alias styling section, Lingui caveats (.mjs catalogs, SWC plugin, vitest macro shim), commands - README.md / README.zh-CN.md: Catppuccin → Apple dual theme; Radix → shadcn/ui (Radix + Tailwind); add subgrid + theme + locale toggle UX; fix locales path src/i18n/locales; add hooks list; add lingui scripts
…rrectness - Remove dead duplicate src/components/nav/SettingsSheet.tsx - Drop 5 redundant @radix-ui/react-* scoped packages (unified radix-ui umbrella already present) - engine.ts: discard negative dt to prevent accumulator underflow on timer regressions - clock-source.ts: fix single-step phase wrap to robust modulo formula - signal.ts: validate ringFreq/zeta are positive finite in applyConfig - i18n/index.ts: guard against stale locale race — drop late-resolving catalog write if a newer switch is in flight - ui-atoms.ts: persist shaderStyleAtom and localeAtom via atomWithStorage; use safe localStorage wrapper that gracefully no-ops in test environments with broken Storage stubs - ProbeStateAnnouncer: prune stale probe entries from messagesRef when active probes change - ParamMomentary: derive visual active state from atom (keyboard shortcuts now update the button); add onPointerCancel to release stuck hold state - Toolbar.tsx: fix ShaderStyle type guard — widen array type rather than casting the argument - CircuitSchematic.test: replace vague toBeTruthy() with not.toBeNull() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e boundaries - ui-atoms: replace null as unknown as Storage with a typed noopStorage object that satisfies StateStorage structurally (no double-cast needed) - useSimulation: remove dead Comlink.releaseProxy call on local proxy callback (Comlink.proxy() returns T & ProxyMarked, not Remote<T>, so [releaseProxy] is never present); simplify RenderAPI init cast via named RenderInitArgs alias - render.worker: replace 5 bare 'as' casts on MessageEvent.data fields with a typed FrameMessage interface + isFrameMessage() type guard - gpu-device: remove incorrect 'as GPUDevice | null' cast (requestDevice() returns GPUDevice per spec); keep defensive !device guard for safety Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…k proxies - Add `if (cancelled) return` after loadCircuit, registerStatusCallback, and render.init so a terminated bridge is never restarted - Clear bridgeInFlightRef on createWorkerBridge() rejection so the hook can recover instead of awaiting a rejected promise forever - Expose releaseStatusCallback() on the physics worker and call it from the main thread cleanup. Comlink.proxy() returns T & ProxyMarked (no [releaseProxy]), so only the worker's Remote<fn> can release the channel — route the call through a dedicated worker method. - Drop redundant releaseProxy() calls from worker-bridge.terminate() — the RELEASE message is lost when the worker is immediately terminated - Push persisted shaderStyle and voltageSpecs to the bridge right after init, so localStorage values actually take effect on first paint
releaseStatusCallback() sent a RELEASE message to the worker, but terminate() was called immediately after, killing the worker before it could process the message. Move the release into stop() which runs synchronously inside the worker; terminate() handles port cleanup for app teardown automatically.
The previous implementation scheduled activeProbeIdsAtom reset and clearCircuitAtoms(def) inside a setTimeout(0). React's effect-order guarantees mean the new effect's synchronous body always runs before that timeout fires and always clears it, so the reset never executed. Detect real circuit change by comparing def references across renders and perform the cleanup synchronously in the new effect body. StrictMode remount (same def object) correctly skips the clear and preserves probe selection; user-driven circuit switches get a fresh start.
paramAtomFamily initialized every atom to 0, ignoring the circuit's declared defaults. Slider UI displayed its fallback value while the atom held 0, and the physics worker ran on its internal default — three sources of truth diverged at first paint. After loadCircuit, walk circuitDef.controls and store.set each atom to ctrl.defaultValue (or 0). The already-registered store.sub subscribers forward the initial value to the physics worker, making UI, atom, and worker consistent from the first frame.
Replacing this.engine while the loop was running could fire a tick against the new engine in its just-constructed state, before the caller's start() restored deterministic timing. Halt the loop up front; the caller resumes with start() after loadCircuit returns.
…applyConfig The applyConfig runtime guard was bypassed for objects built via the constructor. Extract validation into a private static helper and call it from both entry points so every Signal instance has a physically meaningful second-order oscillator configuration.
…lity The @theme inline block never forwarded --accent or --accent-foreground into Tailwind tokens, and --color-accent was claimed by the project's Apple Blue. Shadcn components using bg-accent / text-accent-foreground therefore rendered Apple Blue or no colour at all on hover/active states. Rename the shadcn aliases to --shadcn-accent / --shadcn-accent-foreground, forward them as --color-shadcn-accent{,-foreground}, and switch button.tsx outline/ghost variants plus toggle.tsx data-[state=on] styling to the new utility names. Also fixes dialog.tsx close-button data-[state=open]:bg-accent. Project intents for Apple Blue via bg-accent remain intact.
settingsOpenAtom and aboutOpenAtom live in src/atoms/ui-atoms.ts; shortcutsOpenAtom was declared inside the ShortcutsOverlay component file and re-exported, which made App.tsx import atom state from a component module. Colocate all three overlay atoms to match CLAUDE.md's documented architecture and avoid the implicit circular-import risk. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
useKeyboardShortcuts listed the caller-supplied options object in its effect deps, but the caller passes a fresh literal every render. The effect was running on every AppInner render, removing and re-adding window keydown/keyup listeners — a keypress landing in the ~1ms gap would be silently dropped. Capture options.onOpenHelp in a ref that's synced by its own tiny effect, and drop options from the main effect's dep array. Listeners now attach once per mount.
…element The real <Plural> from @lingui/react/macro returns a ReactElement and accepts ReactNode for one/other. The test shim returned a raw string and typed the branches as string, so any test rendering JSX inside a Plural branch would fail silently and type-check incorrectly. Handle both string branches (with Lingui's # substitution) and ReactNode branches (pass-through) and always return an element.
…orrectly buildSchematicDescription returned a plain English string joined from literal fragments, meaning screen readers in non-English locales always heard English for the SVG description. Split the helper into a pure structural parts builder; assemble the final string inside the component using Lingui's t macro so locale switches translate naturally. The colocated describe.test.ts and CircuitSchematic.test.tsx are updated to assert against derived counts rather than hardcoded literals so future circuit changes don't silently break the tests.
… adapter
CLAUDE.md mandates English-only source text. The existing Chinese
describe/it strings and the 'as unknown as { requestDevice: ... }'
casts on mockAdapter were carried over from an earlier draft. Replace
them with English test names and a locally-typed MockAdapter whose
requestDevice is a properly-typed vi.fn — no double casts remain on
the mock-setup path.
CLAUDE.md already states "No ! non-null assertions in production code". The Biome rule was only a warning, so violations wouldn't fail CI. Flip it to error so the rule is mechanically enforced. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.
No description provided.