This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
A CoffeeScript GUI framework — a "web operating system" (windows, desktop, drag-and-drop, live in-system editing tools) rendered on a single HTML5 <canvas>. It descends from Morphic.js. ~470 .coffee files live in src/.
Prerequisites are installed globally, not via npm: coffee (npm i -g coffeescript), terser, python3, and fswatch (watch script only). npm test is a do-nothing stub — see Testing.
- Build:
./build_it_please.sh→ output goes to../Fizzygum-builds/latest.--keepTestsDirectoryAsIs— skip recopying tests; much faster, use while iterating.--homepage— production build: strips tests + experimental code, ships a pre-compiled image.--notests— drop tests, keep experimental code.--includeVideoPlayer --includeVideos— bundle the video player + assets.--noSyntaxCheck— skip the build-time CoffeeScript syntax gate (see Testing).
- ✅ Test a change — PREFERRED / DEFAULT:
./build_and_test.sh— full build + the whole SystemTest suite run headless, in parallel shards, atspeed=fastest, dpr 1 (~1 min on a many-core box). This is the standard way to verify a behaviour change; don't watch the suite in a browser (~15 min) or run tests one-by-one unless debugging a single test. One-time setup:cd ../Fizzygum-tests && npm i(Puppeteer). - Build + boot-smoke gate:
./build_and_smoke.shbuilds (incl. the syntax gate) then headless-boots the world (native + SWCanvas), failing on any console error — a lighter boot-only check (use when a full suite run is overkill). - Watch + rebuild on save:
./build_as_soon_as_anything_changes.sh(fswatchoversrc/; the syntax gate runs on every save). - Run: open
../Fizzygum-builds/latest/index.htmlin a browser. It loads overfile://; no dev server needed.
The build requires this sibling layout and aborts without it:
Fizzygum-all/
Fizzygum/ ← this repo (source only)
Fizzygum-builds/ ← build output (generated; never hand-edit)
Fizzygum-tests/ ← SystemTests + Automator source (separate repo)
Fizzygum-website/
- Source-as-text + in-browser compilation.
buildSystem/build.pydoes not compilesrc/classes. It wraps each class's CoffeeScript source into an escaped JS string (window.Foo_coffeSource = "…"), grouped into ~12sources_batch_*.jsfiles; the browser compiles them at boot with the bundled CoffeeScript compiler.--homepageinstead ships apre-compiled.jsimage (generated by loading the page with?generatePreCompiled) for instant startup. Onlysrc/boot/*is compiled to JS at build time. - No imports / no module system. Zero
require/import; every class is a global. Load order is auto-discovered bysrc/boot/dependencies-finding.coffee, which regex-scans source text forextends X,@augmentWith X, andnew X. To reference another class just name it — but use those literal forms so the dependency finder sees the edge. - Class hierarchy:
TreeNode→Widget→PanelWdgt→ … →WorldWdgt, the global singletonwindow.world. Widgets form a tree, painted recursively; a Widget with no owner is never drawn. - Meta classes.
src/meta/Class.coffeeandMixin.coffeeparse each source at runtime, which is what enables live inspection/editing of any class from inside the running world. - Rendering is a broken-rectangles (dirty-region) repaint loop on the canvas. Call
changed()to invalidate just this widget,fullChanged()for it + its subtree. Pluggable*Appearanceobjects do the drawing; widgets opting intoBackBufferMixincache themselves to an offscreen canvas. - Mixins are applied with
@augmentWith SomeMixin(seesrc/mixins/). The whole widget tree can be serialized/deserialized viaDeepCopierMixin— that is how SystemTests snapshot state. Mixins are being phased out in favour of plain-OO delegation; the first example isMacroToolkit(world.macroToolkit, insrc/macros/), the macro-test helper toolkit split out ofWorldWdgt.
Two fast automated checks complement the (manual/browser) SystemTests:
- Build-time syntax gate (
buildSystem/check-coffee-syntax.js, run automatically bybuild_it_please.sh): since the ~470 class/mixin sources ship as text and compile in-browser, this is what catches CoffeeScript parse errors at build time. It LOADS the realsrc/meta/Class.coffee/Mixin.coffeeand compiles each source the same fragmented way the browser does — a whole-fileCoffeeScript.compile(src,{bare:true})is NOT faithful (it false-fails on most files). Skip with--noSyntaxCheck. - Headless boot smoke (
../Fizzygum-tests/scripts/smoke-boot-headless.js, via./build_and_smoke.shorcd ../Fizzygum-tests && npm run smoke): boots the built world headless (native + SWCanvas) and fails on any console/page error — catching the load-order/runtime faults the syntax gate cannot.
Behavioural tests are SystemTests: each one drives the running world from a high-level macro (a generator that asks the live world where things are and synthesises real input events), then compares canvas screenshots pixel-by-pixel against reference images. The Automator source and 160 tests live in the sibling Fizzygum-tests repo (not here); any non---homepage build copies them in. (Macros are the only authoring path — the old input recorder has been removed.)
- Author: write the test directly as a macro — the framework-side helper toolkit lives in
src/macros/(engineMacro.coffee+MacroToolkit.coffee, reached asworld.macroToolkit) and is documented insrc/macros/AGENTS.md; see also the/author-macro-testskill inFizzygum-tests. - ✅ Run the whole suite — PREFERRED / DEFAULT:
./build_and_test.sh(build + suite), or against an existing buildcd ../Fizzygum-tests && npm test(=scripts/run-all-headless.js). Runs all 160 tests headless, in parallel shards, atspeed=fastest, dpr 1 — ~1 min on a many-core box (vs ~15 min watching a browser).npm run test:allis the single-process variant. This is the default way to verify a change. The suite can also run headless under Safari/WebKit (Chrome stays default) as a cross-engine determinism check —--browser=webkit/npm run test:webkit, reusing the same references; see../Fizzygum-tests/AGENTS.md. - Run one (debugging a single test): open the built
worldWithSystemTestHarness.html, thenworld.automator.loader.loadAndRunSingleTestFromName('SystemTest_name'); or headlessnode scripts/run-macro-test-headless.js SystemTest_<name>. - Determinism (the suite asserts byte-exact pixels — keep it deterministic): render/layout/input code must be a pure function of the event stream and final geometry, never of wall-clock timers (
setTimeout/Date.now), frame/cycle counts, or intermediate layout passes — those diverge at dpr 2 under parallel load (heavy cycles starve timers and drain multiple events per frame). The contract, the recurring bug-class with worked case law, and the diagnosis playbook (deterministic heavy-cycle reproduction, pixel-delta forensics, disable-the-mechanism proof) are in../Fizzygum-tests/DETERMINISM.md— read it before touching the rendering loop,doLayout, orActivePointerWdgtinput recognition.
- One class per file; filename must equal the class name (
Widget.coffee↔class Widget).build.pykeys off this. Wdgtis the modern name for the legacyMorph(both extendWidget). The intended end state is all-Wdgt—Morphis legacy. New widgets are*Wdgtfrom the start, and existing*Morphclasses ARE meant to migrate to*Wdgtover time. Do it incrementally, not in one sweep: rename a coherent group (a class + its family) when you're already touching it, with a reason, because (a) a rename here is not always pixel-free — menu/hierarchy labels stripWdgt, so renaming a class whose colloquial name is drawn (e.g. menu items) shifts that label and forces SystemTest screenshot recapture; verify per class — and (b) each rename is a whole-tree identifier+file+serialization sweep, best done one verifiable batch at a time.nilmeansundefined(defined insrc/boot/globalFunctions.coffee); the codebase uses it instead ofnull/undefined.- Edit only
src/**/*.coffee. Never edit../Fizzygum-builds/**— it is regenerated every build. # … excluded from the fizzygum homepage buildcomments andif Automator?guards mark test/experimental code that--homepagestrips as dead code — keep them intact.- For subsystem-specific depth, check for a local
AGENTS.mdin that subdirectory (e.g.src/boot/,src/meta/,src/patch-programming/) before assuming this file is complete.