Save GIF / Share image / Voxel data are client-built <button>s, so Gradio's
theme paints them with --button-secondary-background-fill (white); our
non-!important background lost to it, leaving the white .l label invisible on a
white pill. Mark the pill backgrounds !important (base + .save/.native, and add
an explicit .voxel) so they beat the theme and the labels read again.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(lab): stop the canvas grid overflowing the right edge on wide screens
.buddy-canvas is 98vw but its Gradio parent column is max-width-capped and
centered on wide screens, so margin:auto let the 98vw box overflow the column
to the right (visible in the HF iframe; hidden locally only because a narrow
window doesn't hit the cap). Center it on the viewport with the full-bleed
margin trick (margin:2px calc(50% - 49vw)) so the parent width cancels out and
it never spills past the right edge.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Landing now has two entries again: "Quick Start →" (restored Start → Lab with
seeded parents) and "✏️ Build from scratch" (re-added). Both route through the
Lab before any splice can happen, which mounts the lazily-mounted screen_lab and
fixes the blank-screen bug on the reveal's "← Back" (next_btn → screen_lab: an
unmounted Lab, half-mounted by do_fuse's slots_view/tree_view writes, rendered
blank).
Splice Bench input box is now fully model-free in both runtimes: LlamaCpp
generate_creature (pick_exemplar only) and edit_creature (keyword → assemble_part,
dropped the _freeform_edit_creature model fallback) match MockRuntime. The only
model call left in the app is fuse_creatures (Splice).
Reveal Share/Back are hidden over the fuse loading state via CSS
(.screen:has(.buddy-reveal-row) .buddy-reveal-actions) instead of gr.update(
visible=), which races on the freshly-revealed lazy column.
Docs/wiki: README hook + "How it works" + How-to-play and wiki system-overview /
index / log / grad…
feat(landing): Start opens the from-scratch bench, drop the second button
The landing "✏️ Build from scratch" button is removed; Start → now runs the
FreeLand chain (freeland_open → freeland_fill), opening the Splice Bench with
both slots empty instead of the Lab pick screen. Dead #buddy-freeland-btn CSS
dropped; the in-Lab Build-from-scratch button is unchanged.
Docs follow the code: rewrite README "How to play" and correct the wiki
(system-overview, index) to drop the long-removed "🪄 Freeform Lab" screen
(do_freeform / free_enter_lab no longer exist) — drawing-from-text now happens
in the bench's per-side box.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(lab): resize tree GL buffer on height drift, not just width
The scissor renderer only called setSize when the canvas WIDTH changed.
.buddy-canvas height is min(calc(100vh-142px),1040px), and inside the
auto-resizing HF iframe 100vh keeps shifting after init (the documented
height runaway). When height changed but width didn't, the GL drawing
buffer stayed stale while the scissor/viewport math used the live
clientHeight, so every tree card below the first painted shifted up and
stretched. Reproduced only through the Space wrapper (100vh is stable
locally). Guard now resizes when either dimension drifts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chore(rename): point Space URL/remote at build-small-hackathon/hatchimera
The Space was renamed build-buddy -> hatchimera. Update SPACE_URL (the
share/OG target for X, Facebook, LinkedIn, native share), the wiki Deployment
note, and the CLAUDE.md verification wrapper URL.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(share): year-prefixed download names + Voxel data right of Save GIF
shareStamp now emits YYYYMMDD-HHMMSS so saved files (Save GIF / Voxel data)
are fully dated and sort chronologically: e.g. 20260616-031529_Hatchimera_Share.gif.
Move the Voxel data button to sit immediately right of Save GIF by prepending it
(it builds before the GIF resolves, so Save GIF / native share prepend ahead of
it) — row now reads: Share image | Save GIF | Voxel data | X | fb | in.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
refactor(runtime): single Gemma 4 12B model + Hatchimera rename
Drop the dual Qwen2.5-3B / SmolLM2-1.7B recipe-model setup. Every AI moment —
freeform generation and hatch fusion — now runs through a single model,
Gemma 4 12B, the only family that draws hard voxel forms in the model-selection
spike. LlamaRuntimeConfig defaults and env overrides collapse onto the freeform
model; the now-dead fallback-model fields and Qwen/SmolLM references are removed.
Retitle the project to "Hatchimera — Voxel Pet Fusion" across the README and
wiki, and align the wiki (deployment, hackathon, overview, local-dev, voice)
with the single-model story.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Re-bake all landing-backdrop buddy PNGs from the current Compendium box
templates, grafting 0-2 random parts (wings, horns, hats, tails, feet, etc.)
onto each so the drifting grid reads as varied chimeras rather than bare
templates. 52 transparent 512x512 RGBA thumbnails (was 50), baked headless via
the proven voxel bake harness (bounds-driven hero camera, ACES 1.4, alpha).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(share): download voxel data + dated, readable download filenames
- New "Voxel data" button on the share screen exports the focus buddy as JSON
(format/version, name, palette, the freeform box layout when freeform, and the
resolved unit voxel cells [{x,y,z,color}] — portable into other voxel software).
Offered independently of the GIF (works even if the GIF render fails).
- Download filenames are now MMDD-HHMMSS_Hatchimera_<label>.<ext> (local time) via
shareStamp/shareFilename, so the GIF saves as e.g. 0616-023920_Hatchimera_Share.gif
instead of a blob UUID, and the voxel export as ..._Hatchimera_VoxelData.json.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(bench): show Splice only when both slots filled + a periodic wobble nudge
The Splice button is hidden until BOTH bench slots hold a creature, and once shown
it gives a brief wobble every ~4.6s (after a 1.6s beat) as a 'you can splice now'
reminder — paused on hover/focus, off under prefers-reduced-motion.
Visibility is driven CLIENT-SIDE (buddyToggleSplice in the voxel poll counts filled
vs from-scratch bench cells and toggles .buddy-can-splice) rather than Gradio
`visible`: a never-mounted visible=False button's first reveal in a multi-output
response silently dropped (the same mount race as the bench/FAB), so the button
flickered between not-showing and showing depending on which slot filled last. The
DOM is the source of truth, so it's robust across re-renders.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(canvas): restore the dot-grid under the scissor 3D layer via multiply
The single scissor WebGL canvas (.buddy-canvas first child) composites opaque-
white over the CSS dot-grid behind it on real Chrome, leaving a plain white
canvas. Putting the dots on the GL canvas's own background fails identically
(the element composites white though the buffer is [0,0,0,0]). mix-blend-mode:
multiply turns the transparent-white gaps into a no-op so the .buddy-canvas dots
show through, while cards stay vivid (near-white backdrop). Verified locally.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(bench): random parts on 🎲, well-supported hints, Lab scratch entry
- 🎲 Random now grafts 1-2 catalog parts from distinct decorative categories
(props excluded) onto the picked Compendium template, so re-rolling the same
template still looks different (_graft_random_parts).
- Suggestion chips: every SUGGEST_POOL entry now resolves to a real catalog part
via edit_parser.match_part. Dropped the two body-shape phrasings that silently
mis-matched ("make it short and round" -> round ears, "make it tall and skinny"
-> a tophat), since the edit path can only ADD parts, not reshape the body.
- Bench input placeholder is randomized per open (GEN_EXAMPLE_POOL) and every
example maps to one of pick_exemplar's 6 body shapes via a recognized keyword
(no silent quadruped fallback), 2 per shape.
- New Lab top-right "✏️ Build from scratch" (scratch_open/scratch_fill): opens the
bench with both slots empty but KEEPS the current family (a splice adds to the
tree); distinct from the landing FreeLand which starts a fresh family.
- From-scratch panel cop…
merge: bring main's part-assembly-edit feature into the FreeLand branch
Merges main (the parallel parts-catalog + keyword/parametric assembler edit
path, single-context scissor tree rendering, runtime selector rework) into the
FreeLand-from-scratch / Random / draft-creature / canvas-and-nav-polish branch.
fusion_ui.py and voxel_embed.py auto-merged cleanly (the two efforts edited
disjoint regions); wiki/log.md was the only content conflict (append-only on
both sides, kept both). Two semantic artifacts the line-merge could not catch,
both fixed and verified locally:
- Canvas pan dead-zone: this branch's installCanvasPan suppressed panning when a
drag began on a node (it assumed per-node OrbitControls to rotate). main's
scissor renderer draws the tree through ONE pointer-events:none context with
auto-rotate, so node-drag rotated nothing AND no longer panned. Now a drag
anywhere pans (matches main's installTreeInteractions intent); the __buddyPanned
threshold still keeps a clean tap selecting.
- Splice Bench FAB resurrected on reveal -> Lab Back: …
feat(edit): parts fuse and redistribute instead of stacking
Adding the same part again now accumulates its instance count (tracked via _part/
_pc tags on the placed boxes, which survive the freeform_genome -> genome dict
round-trip) and RE-distributes the whole set evenly across the anchor face, instead
of piling new copies on the previous ones. So 'add wing' then 'add wing' gives four
wings (two per side, fanned front-to-back), not two stacked pairs.
Also fixes symmetric parts drifting far off to one side: they now straddle the body
(left half hugged to the left face, mirrored across the BODY center to the right)
instead of mirroring within the part and face-aligning the whole pair to one side.
Distribution spans the body's own extent so many instances hug the body.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
perf(tree): render the family tree from one WebGL context via scissor
The pedigree tree booted one WebGL context per node card (pooled by cullCanvasNodes
to on-screen nodes). A tree with many nodes visible at once could still exceed the
browser's ~16-context cap and blank/flicker. Replace it with installTreeScissor: one
renderer per .buddy-canvas (sitting above its dot-grid, below the panning card layer)
draws every node into its on-screen rect via the scissor test, clipped to the canvas
viewport — so the tree uses exactly 1 context regardless of size.
Node cards become transparent windows onto the shared canvas: the A/B badge moves
into the footer and selection shows as a card ring (both outside the thumb) so the
behind-content canvas never occludes them. The renderer keys on its real canvas
child (not a JS-prop lock) so it re-attaches after Gradio swaps the tree HTML, and
the loop self-disposes when its canvas is stripped. cullCanvasNodes is now a no-op.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
UX follow-ups on the FreeLand/bench flow:
- Selection no longer re-renders the canvas. on_select stops returning tree_view;
the A/B badge is applied client-side (buddySelect reads the DOM badges as the
source of truth and re-applies the same toggle logic). Picking a creature after
panning no longer resets the pan to center or reboots the live-3D nodes — the
"jump back to blank center then show selection" flash is gone. The server still
returns sel_a/sel_b + data-ready + the FAB so the bench and auto-advance stay
in sync.
- Canvas enlarged toward full-bleed: width 98vw (max 1860px), height
min(100vh - 142px, 1040px); collapsed the hidden slots carrier and tightened the
Lab column gap to reclaim the strip above the canvas.
- Reveal screen: fixed the Share button position (the Next button's margin:auto
was absorbing the flex row and shoving Share to the edge); relabeled
"Next -> back to the family" to "<- Back" with the shared white-pill back style;
share screen "<-…
feat(edit): replace small-model selector with keyword + parametric assembler
Edit path is now pure Python: an English keyword parser maps the instruction to
a catalog part plus count/scale/anchor/rotation/color, and the assembler snaps it
on deterministically (symmetric pairs first, overflow to rear; float scale rounded
to the grid; exact 90deg integer-grid rotation). Instant, no GPU. Gemma 12B stays
only for full generation, hatch fusion, and the freeform-edit safety valve.
Removes select_part, the MiniCPM selector constants/preflight/warmup bench, and
SELECT_SCHEMA/build_select_messages.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(parts): author ~100 categorized voxel parts across 10 categories
Build a buddy from scratch and splice it, reworking the Lab/bench flow.
- FreeLand: landing "Build from scratch" button opens the bench with both
slots empty; each empty slot generates a freeform creature from a typed
description (runtime.generate_creature, exemplar fallback). The bench is now
the single A/B workspace: a slot is either a tree-picked creature or one
built from scratch.
- Advance relaxed from two picks to >=1: the unselected side becomes a
from-scratch panel; splice is gated until both slots are filled.
- 🎲 Random per slot: fills with a random Compendium template, kept distinct
from the other slot (no duplicate sides).
- Draft creatures: bench candidates (Random / from-scratch) are drafts hidden
from the family tree until a splice commits their two parents, so re-rolling
no longer litters the tree with orphan "ancestors" (Creature.draft, serialized).
- Bench Back returns to its origin (landing for FreeLand, Lab otherwise) via a
bench_origin State, ins…
feat(runtime): log selector picks; fake edit keyword-matches the catalog
Print [selector] picked part=.. anchor=.. scale=.. on each catalog hit so the
Space log shows what the small model chose. FakeBuddyRuntime.edit_creature now
keyword-matches the instruction to a catalog part (not always horns) so local UI
testing exercises the whole part set.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(runtime): default selector to MiniCPM5-1B-GGUF with auto-fallback to Qwen
Wire the selector default to openbmb/MiniCPM5-1B-GGUF / MiniCPM5-1B-Q4_K_M.gguf
(the base openbmb/MiniCPM5-1B has no GGUF). If it fails to load on the Space's
llama-cpp-python 0.3.19 (e.g. chat-template unknown tag), select_part logs it,
switches the container to Qwen2.5-3B, and retries immediately so the edit still
lands fast — no repeated attempts on the broken model. Override via
BUDDY_SELECTOR_MODEL_REPO/_FILE.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(runtime): selector->assembler edit path with freeform fallback
Route edit_creature through a small grammar-enforced selector (select_part) that
maps free text to {part, anchor, scale, color} from the catalog; assemble_part
snaps it on (~2-3s). Off-catalog / transform / selector miss falls through to the
freeform Gemma edit (renamed _freeform_edit_creature); model failure is logged,
not swallowed. Selector model is env-configurable (BUDDY_SELECTOR_MODEL_REPO/_FILE),
default Qwen2.5-3B; env-gated _selector_preflight() prints load ok/FAILED so a
0.3.19 chat-template failure (e.g. when trying MiniCPM5-1B) is visible.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(parts/assembler): catalog parts + deterministic anchor placement
parts.py: hand-authored additive part catalog (horns/wings/tail/ears + props
house/hat/crown) in local coords with anchor/scale/symmetry metadata.
assembler.py: pure-function assemble_part — face-aligns a (scaled, mirrored)
part to the creature's bounding box at the chosen anchor, clamps via repair_boxes,
appends. Unknown part / empty boxes -> unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On-Space bench: Gemma-4-12B freeform decodes ~17-22 tok/s WITH the GBNF grammar
vs 35.9 tok/s without (same 400-token prompt) — grammar costs ~2x, and it is
intrinsic per-token CPU sampling overhead, not schema bloat (BOX_SCHEMA is
already lean). Two levers:
- A: BUDDY_FREEFORM_NO_GRAMMAR=1 drops the grammar on the freeform box path only
(heavy=True calls: generate/fuse/edit), leaning on parse_json_object +
repair_boxes for the unconstrained output. Recipe GENOME_* calls always keep
the grammar (fragile without it). Env-gated so it can be A/B'd live and
reverted instantly.
- B: lower the default BUDDY_FREEFORM_MAX_TOKENS 1500 -> 1000. Trims the typical
wait and closes the residual crash edge (1500 tok / ~20 tok/s ~= 75s overran
the duration=60 budget; 1000 -> ~50s fits).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(runtime): grammar bench measures OFF only to avoid ZeroGPU abort
Running grammar-on + grammar-off back-to-back inside the single demo.load
warmup handler stacked a 3rd GPU acquisition after the warmup gen, which
ZeroGPU aborted ("GPU task aborted"). grammar-on throughput is already the
normal [zerogpu-bench] number every real freeform call prints (~17-22 tok/s),
so measure only the unconstrained grammar-off run here — keeping the handler
to 2 GPU acquisitions (warmup + bench), the range the log shows succeeds.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the static pedigree tree + detail modal with a fixed-px pannable
canvas (dot-grid background, left-drag pan) holding live 3D nodes that
boot/teardown as they pan in/out of view, plus a floating top-left toolbar.
Recolor the bench hint bubbles to opaque white + navy text for legibility,
and unpack the new 4-tuple edit_creature return at the bench caller.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
perf(runtime): give freeform 12B calls their own GPU budget + grammar bench
Freeform Gemma-4-12B calls generate 300-600 tokens at ~20 tok/s and hit
25-31s total, overrunning the shared ZeroGPU duration=30 budget and crashing
mid-decode (ggml_cuda_mul_mat_q CUDA abort -> the call dies and the user gets
the fallback after a full wait). Route generate/fuse/edit through a separate
duration=60 wrapper (BUDDY_FREEFORM_GPU_DURATION); recipe calls keep 30.
Add an env-gated grammar A/B bench (BUDDY_GRAMMAR_BENCH=1) that runs the same
freeform prompt with and without the GBNF grammar to isolate whether
grammar-constrained sampling is the ~20 tok/s throttle vs raw 12B decode.
edit_creature now returns the edited geometry AND the next 4 tweak hints in
ONE completion (EDIT_SCHEMA), so a bench tweak is a single ZeroGPU call
instead of edit + a separate suggest_tweaks call.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chore: make tracked content English-only (keep CJK command-match table)
Translate cosmetic Chinese (comments, report title, spec mockup) to English.
Keep the load-bearing CJK synonym tables in fallback.py that let the offline
interpreter recognise Chinese command input.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(ui): unify live render with share GIF, recolor text, simplify Lab
Render (live setupVoxel now matches buildBloodlineGif):
- bright hemisphere/key/fill rig + exposure 1.4; head crown no longer dark
- solid=true build mesh so cube-by-cube assembly is solid, not a hollow shell
- real PCFSoft shadow map + ShadowMaterial catcher (thumbnails keep the blob)
- near-frontal "hero" initial camera (0.46/0.40/1.16) instead of 3/4 isometric
Fixes:
- inspect modal passes boxes through, so freeform creatures render their real
geometry instead of falling back to the archetype chick ("purple chicken")
- suppress Gradio's stuck orange .generating border (kill the pulse keyframe
animation, not just border-color)
Lab/UI:
- remove "Draw a new buddy" and the whole Freeform Lab screen (+ handlers/CSS)
- remove the visible A/B slot cards; keep a hidden data-ready carrier for
auto-advance; selection shown by the tree A/B badges
- add "Open the Splice Bench" button for manual (re-)entry after Back
- center the Compendium action; restyle the inspect magnifier button
- …
merge: share lineage GIF + voxel build animation into main
Add a full-screen share view after a buddy hatches: a looping GIF that
dollies along the direct bloodline (A × B → hatched descendant), each
creature assembling cube-by-cube bottom-up, with a near-frontal hero
camera (azimuth ~22°, elevation ~18°), shaped ShadowMaterial projection,
and bright studio lighting.
- fusion.py: Lineage.bloodline(focus_id) returns the bounded direct
vertical lineage (ancestors breadth-first + representative descendant
chain), oldest-first by (generation, creation order).
- voxel_embed.py: progressive drawRange "build" animation on every live
rebuild (solid + non-solid modes); client-side GIF pipeline via gifenc
(jsdelivr ESM) with frame coalescing; shadow-mapped form-based shadow.
- fusion_ui.py: screen_share state, icon-only X/FB/LinkedIn buttons,
Save GIF + native share; homepage share extended to the same platforms
with corrected Facebook/X/LinkedIn URLs.
- tests: Lineage.bloodline coverage + share_text formatter test.
Co-Authored-By: Claude …
fix(bench): raise the model so hint bubbles overlap its top slightly
Lower frame_lift 0.16 -> 0.06 on the bench so the creature sits higher and its
top edge reaches into the hint-bubble band (bubbles now cover a little of the
upper area), per request. Shadow stays at the base.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Enlarge the 3D scene (460x444) to use the space above the Splice button.
- Add per-render camera options to voxel_html (frame_pad / frame_lift -> cfg
framePad/frameLift in setupVoxel): the bench pulls the camera back (1.22) and
raises the aim (0.16) so the creature reads smaller and sits lower, leaving
clear headroom for the top hint bubbles without covering the model or its
contact shadow. Defaults keep tree/reveal/modal framing unchanged.
- Nudge the × token 12px left + 6px up so it lands on the true midpoint between
the two scenes (the bare flex slot sat ~12px right). Verified xHoriz/xVert
offset 0.
Verified on the fake runtime; 76 tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(bench): fill the 3D scene, center the ×, one-line input with send arrow
- The voxel root section wasn't stretched to the scene, so its stage collapsed
to auto height and setupVoxel fell back to a 300px canvas — leaving an empty
blue band below the model. Stretch .buddy-voxel-root (and force the canvas to
fill) so the 3D fills the full 380px scene (verified canvas 380 == scene 380,
gap 0).
- Re-center the × token on the now-full scene (xVsScene ~0).
- Tweak controls on ONE line: widen the row to the scene width + nowrap so the
input and send button no longer stack. Replace the 'Apply' button with a
compact ChatGPT-style accent square showing an up-arrow (↑).
Verified on the fake runtime; 76 tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(bench): big 3D scenes + Gemma-suggested hint bubbles in a halo arc
Splice Bench redesign from user feedback:
- Title centered to the VIEWPORT (Back pulled out of flow), not the row.
- Each side becomes a large 3D scene (440x380); the creature fills it and the
name is merged into the A/B header. The colour-swatch card is removed.
- The 4 quick-tweak chips become glassy hint bubbles floating in a halo arc
across the top of the scene (outer-low, inner-high; right side anchored with
right: so fixed-width pills never clip). Gentle float animation
(reduced-motion-aware). They keep the buddy-chip class, so clicks still route
through the existing buddyChip -> on_chip bridge (no JS change).
- Hints are full imperative sentences ("Add two curved horns"), shown verbatim
and sent as the tweak prompt.
- Hints are now AI-driven: first render samples a static pool; after EVERY tweak
on a side, Gemma regenerates that side's 4 hints for the new form
(runtime.suggest_tweaks + build_freeform_suggest_messages + TWEAK_SCHEMA;
fake runtime / model failure…
The bench's controls drifted off-centre because each side mixed separate-width
components (260px stage, 540px tweak row, 260px chips), so nothing shared a
centre line, and the × used a brittle padding hack.
Redesign, each side is one centred stack:
- Quick-tweak chips become game-style ability buttons that FLANK the model (two
per side), moved into the bench HTML; short labels, full instruction in
data-prompt. Widen the bench columns so both chip columns stay beside the model.
- × is a soft circular game token, measured dead-centre on the model (xVsStage 0).
- Comfortable gap between the model and its name plate (was cramped).
- Custom tweak input + Apply unified to the pod width and centred.
Verified on the fake runtime: chips flank on one line, splice still reveals the
chimera. 73 tests pass (test_bench_ui updated for chips-in-bench-side).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(compendium): in-app Compendium screen + seed lineage from it
- New Compendium screen: scrollable grid of all 52 built-in creatures rendered
as static thumbnails (thumb=True -> one shared offscreen renderer, flat WebGL
context count), client-side theme filter chips, tap-to-add a creature into the
family tree (buddyDexAdd -> dex bridge -> on_dex_add).
- Lab gains a '📖 Compendium' entry beside 'Draw a new buddy'.
- _new_session() now seeds two ancestors from compendium.random_sample(2)
instead of the EXEMPLARS stopgap (drops the unused random/EXEMPLARS imports).
- Verified end-to-end on the fake runtime: Start -> Lab (2 seeded) -> Compendium
-> tap-to-add -> tree grows to 3 -> select two -> Splice -> chimera reveal.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(compendium): expand to 52 broad creatures + shared-renderer report
- Remove IP-adjacent content; 52 original creatures across mammal/mythical/
aquatic/plant/bird/alien/bug/reptile/mascot/robot/amphibian themes via
parametric builders (_quad/_biped/_bird/_blob/_bug/_fish/_serpent) + 16
hand-authored specials.
- report.html: one shared offscreen WebGLRenderer snapshots each creature to an
<img> instead of 52 live contexts (the ~16 WebGL-context cap blanked most
cards). Static 0.7 rad yaw.
- Re-sync report_data.js (52 creatures).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(compendium): data module + report tool + first 12 creatures
Claude-authored freeform creatures across themes (alien/mythical/plant/aquatic/
mascot/bug). compendium.py exposes all_entries/get/random_sample/as_genome;
compendium_data.py holds the growing roster (toward ~50). tools/compendium/
report.html (+ sync_report_data.py) renders the whole roster with the app's
AO voxelizer so the set can be browsed offline anytime.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(freeform): route the whole main flow through the freeform path
Phase 1 of all-freeform: ancestors seed as freeform (exemplars, stopgap for the
compendium), AI Tweak edits box geometry via a new edit_creature path instead of
the recipe interpret_genome_command (which stripped boxes), splice already routes
to fuse_creatures for box parents, Draw-from-prompt moved into the Lab, warmup
fires the Gemma freeform model. Qwen recipe code kept dormant.
New: prompts.build_freeform_edit_messages, fallback.edit_boxes_fallback
(deterministic box editor), runtime.edit_creature (Protocol + Fake + LlamaCpp).
Verified end-to-end on the fake runtime (seed -> tweak -> splice -> reveal).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Compact bench genome card (name+swatches+parts only, fixed min-height) so the
A/B columns and their Tweak/Apply rows align instead of drifting by text length.
- Bigger bench voxel stage (220x180 -> 260x230); recentre the × divider.
- Apply buttons get a readable soft-clay secondary style (was dark/dim default).
Reveal + detail-modal cards keep the full card via the new compact flag.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(voxel): DoubleSide material so creatures aren't see-through at some angles
Per-face quad winding wasn't uniformly CCW, so FrontSide culling dropped the
-X/+Y/-Z faces and the creature went hollow at angles exposing them. Hidden
faces are already culled geometrically (single shell) and normals are explicit,
so DoubleSide is correct and cheap.
Also: make the landing Freeform Lab button a readable white secondary pill
(it was falling back to Gradio's dark default with unreadable dim text).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(freeform): steer one dominant body colour + accents in prompts
Add an opt-in freeform path where the model draws raw box-layout geometry
({name, boxes:[{x,y,z,w,h,d,color}]}) instead of the recipe, so creatures can
take cross-form shapes (five arms, a house on the head) the closed recipe
vocabulary cannot express. Gemma 4 12B (the model-selection spike winner),
grammar-enforced, seeded with a hand-authored reference exemplar.
Freeform creatures are first-class: they enter the family tree and breed. A
splice routes to a Gemma "merge two creatures" call when either parent carries
boxes (freeform x freeform and freeform x recipe both yield a box child);
otherwise the recipe fuse runs. Fallbacks (fake runtime / model failure) use a
reference exemplar or a deterministic box merge, so the app never renders empty.
- genome: optional Genome.boxes + freeform_genome(); round-trips, recipe
creatures stay box-free
- prompts: BOX_SCHEMA, build_freeform_messages, build_freeform_fuse_messages
- runtime: generate_creature / fuse_creatures on Gemma via per-cal…
Gradio lazily mounts visible=False columns, and a never-yet-mounted column's
visible=True is silently dropped when the same response also writes the Pick
screen's HTML children or is reached via a chained .then. Drive the bench reveal
from a dedicated #buddy-adv-submit bridge with a clean chain (screens, then bench
content); the client clicks it once both slots are staged (data-ready). Verified
end-to-end via Playwright.