Spaces:
Running
Running
Commit History
-
Rebuild map bundle for Desert biome + golden sand; carry desert assets
- Rebuild web/mapSandbox.js from auto-battler: Desolate Desert page (golden-sand patches, grouped Biome nav) + tilesheet click-to-identify (pulsing outline + used-cell tint), with scoped worldmap.css. - Carry the curated DesolateDesert assets (tiles/props/prop-shadows/mockup) into web/assets so the Space can render the biome. - Sync web/engine.js + web/comboBattler.js to auto-battler dev (spawnActor/field). - web/tiny.js: World Map tab opens the finite Game World page. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>977a4c1
-
Game: hero creation + selection flow; pin the Space
Shared hero creator (heroCreator.js) used by the Personas page and a new Game 'Create a Hero'/'Hero Details' modal: two-state layout (recruit ⇄ portrait panel), streamed details, generate portrait + quote + voice, barracks, fixed-size modal (no resize across states/streaming), and portrait repaint disabled unless the appearance changed. Hero picker: cards show the saved portrait (or an animated class idle); picking opens a hero detail page (portrait/about/quote + Select) instead of spawning immediately; on Select the camera flies down to the spawn point and the hero drops in. Picker auto-refreshes on roster changes. - web/comboBattler.js, web/mapSandbox.js: rebuilt bundles (flyTo + getSpawnWorld). - README: pinned: true. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>3ef6bd6
-
Game: diagonal 2x map + half-size heroes; hidden overlay sidebar; persona-select state
- web/*: rebuilt comboBattler bundle; mirror shell/sidebar.js (starts hidden). - app.py: #battle-stage spans the full width so the sidebar overlays it as a drawer instead of pushing/resizing the canvas — fixes the blank map strip on toggle. - web/tiny.js: bottom persona picker — mount the map with no hero, selectHero on pick, re-show the picker when there's no live hero (initial + on death). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>0abf9a2 -
Game page: directly-controlled Forgotten Plains battle; rename to "Game"
Replace the Battle tab's combat window with the new single-sim map game (web/comboBattler.js, bundled from auto-battler): control the persona with WASD/tap, attack + cast skills (Space / 1–3 / on-screen ⚔ buttons), enemies aggro on proximity, camera drag/scroll/pinch like the World Map sandbox. - tiny.js: mount mountComboBattler on #battle-stage (persona → engine profession + class skills; enemy band with aggroRadius); drop the old hardcoded 4v4 loop. - build.sh: esbuild web/comboBattler.js; sed the sidebar label "Combo-Battler" → "Game" (Space-only; auto-battler's own nav is unchanged). - personaPanel.js: export CLASS_SLUG (single-source the class→slug map). - Rebuilt bundles pick up the additive teamBattle terrain/aggro hooks. - app.py + requirements.txt: load a local .env for `python app.py` (python-dotenv; no-op on the Space where real secrets are used). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>5bdce49
-
Style the skill-detail card on classes/enemies to match auto-battler
The selected-skill detail card reuses CBGame's .cbgame-skill-* component (built by classes/enemiesSandbox.js), but that rule family — the dark card background, Fraunces name, tone-coloured stat tags, trigger pills and --fx effect rows — lives in auto-battler's styles.css, which the Space never shipped (only classes.css). build.sh now folds the .cbgame-skill-* rules from styles.css into the scoped classes.css (brace-balanced, by selector prefix), inside the same #classes-stage,#enemies-stage wrap. Skill + condition icons were already curated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>ca975ec -
Add World Map sandbox tab; fix ported-component styling on the Space
Wire the shared Map playground (web/mapSandbox.js, synced from auto-battler) into a World Map tab: pill switcher + all six sub-pages. build.sh bundles it, copies worldmap.css, and curates exactly the ~105 map tilesets via the MAP_ASSET_URLS manifest (no whole-pack copy). curate_assets.py is now manifest-driven and also walks effects.json (its 204 effect/status icons were missing). Host fixes so ported component CSS matches auto-battler instead of losing to Gradio's own styles: - extract :root design tokens -> web/shell/tokens.css (linked in HEAD) - strip Gradio's .prose/.gradio-style wrappers off sandbox stages (web/tiny.js) - scope component CSS under its stage id (build.sh) so it outranks Gradio's .gradio-container-<ver> button/* base resets - reroot /assets -> /sprites (incl. *.json) in the URL shim - gate upgrade-insecure-requests (CSP meta + middleware) to the HTTPS Space so plain-http LAN testing no longer hits ERR_SSL_PROTOCOL_ERROR Co-Authored-By: Claude Opus 4.8 (1M cont…c7dba29 -
Restore Settings + Personas sidebar items (synced from auto-battler nav.json)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>07a1161 -
Add Classes + Enemies sandbox tabs (synced from auto-battler)
Wire the shared Classes/Enemies playgrounds (web/classesSandbox.js, web/enemiesSandbox.js, bundled by build.sh from auto-battler) into the Space: gr.Tab + #classes-stage / #enemies-stage full-bleed CSS, link classes.css, and mount them from web/tiny.js (remapping /assets/ → /sprites/). The engine bundle (web/engine.js) and nav.json refresh too. build.sh: esbuild the two sandbox modules, copy classes.css + classes.json + enemies.json + effects.json, and curate the ~44 CB skill icons + condition pips into web/gw (served via a new /gw mount). The customize panel is read-only on the Space (no /api). Future combat changes cascade with one build.sh run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>38a1e17
-
Subtitle: small minds → small models
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>b408d69 -
Add Coding Model setting + Skill Forge sandbox
New "Coding Model" settings section (below Persona Prompt) selecting between two backends: • Nemotron 3 Nano 30B-A3B via NVIDIA NIM (integrate.api.nvidia.com, reasoning off) — too large to self-host on ZeroGPU. • Mellum2 12B-A2.5B via a ZeroGPU sidecar (polats/tiny-army-mellum-zerogpu), same /generate(+_stream) contract as the Aya/MiniCPM5 sidecars. New "Skill Forge" tab (Sandbox group, after Sprite Animations): pick a recruited hero, describe a skill, and the chosen coding model authors a tailored skill definition. Reuses personaStore (roster) + streamCoding. Backend: app.py routes mellum2-zerogpu and nemotron-3-nano-30b-nim model ids in /text/generate/stream; _nim_text_stream() streams NIM's OpenAI-compatible SSE. Frontend: codingModel.js store (separate from the persona/diary model), codingModelBar.js picker, shared sseText.js SSE reader (extracted from engineServer.js). Needs Space config: TINY_MELLUM_SPACE + NVIDIA_NIM_API_KEY. Co-Authored-By: Claude Opus 4.8 (1M c…1f1908e -
Add Tiny Aya text generation option
f9dd2fe
-
Portraits step 3: persona panel Portrait section + per-hero caching
- New "Portrait" section in the hero panel: an editable appearance prompt (auto- built from name/class/about) and a 🎨 paint button that generates a portrait via the active image provider (local Z-Image or cloud FLUX), shown beneath it. - Reuses the voice spinner/badge UX (now generalized to .persona-ico): the button badges when there's no portrait or the appearance changed, spins while painting. - Per-hero PNG cached in IndexedDB (new 'portraits' store, DB v2) and the appearance prompt persisted, so portraits survive a refresh and live in the barracks. Verified locally: section renders with auto-appearance; 🎨 paints a 1024² portrait (~15 s on the 3090) that renders + persists across reload; badge clears when current. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>6a861b9
-
Settings: hide the app URL printed in Gradio's Settings header
Gradio's Settings header rendered "Settings <app-url>" (tinyarmy.noods.cc in prod). Hide that .url div so the header just reads "Settings". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>476605d -
Settings: "Use recommended for this device" button (GPU/device-tier heuristic)
Inside the "Your device" panel, a button picks a preset from the hardware: - Mobile → Low, but Medium if a Qualcomm Adreno 700-series (or higher) is found. - Desktop → Medium, but High if an NVIDIA RTX 3090 (or higher) is found (newer generation, or 30-gen ≥90; so 3090/3090 Ti and all 40xx/50xx qualify). GPU is read from the WebGL ANGLE renderer string + WebGPU adapter info. Applies and persists the chosen preset, and shows what it picked and why. Heuristic unit-tested across 13 GPU strings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>7b49a92 -
Settings presets: real padding (beat Gradio reset) + readable active hover
Gradio's button reset was zeroing the preset buttons' padding, so the text sat against the edges — restore it with !important (14px 16px), like the other persona buttons. Also: a selected button turns light on hover, so force its text to ink (dark) on hover instead of staying light-on-light. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>dbd7553 -
Settings: recommended quality presets + device readout; persist all choices
New "Recommended settings" section at the top of Settings (above Local AI Model): - High / Medium / Low presets that set the LLM model AND voice provider together, like a game's graphics presets: High = Qwen3 0.6B + Kokoro 82M Medium = Qwen2.5 0.5B + Kitten TTS Low = Qwen2.5 0.5B + Web Speech Presets resolve the model within the active engine's catalog (family fallback). Changing the model or voice by hand drops to Custom. - Expandable "Your device" readout (CPU threads, RAM, WebGPU/GPU, storage, screen, UA) for debugging and benchmarking. Persistence: the LLM engine + per-engine model (runtime.js), the voice provider (tts.js), and the preset choice (qualityPreset.js) are all saved to localStorage, so settings survive a refresh. runtime.js now validates a stored model against the active engine and emits onModelChange; the model/voice bars re-sync when a preset changes them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>750ca83 -
Voice: provider-driven per-hero voices; settings is provider-only
Settings → Voice is now just a provider picker (no global voice dropdown, no "narrate war diaries" checkbox) — the voice belongs to the hero, not the app. Copy updated to explain the model. Persona panel now respects the chosen provider: - Qwen3-TTS (design): voice-design text stays editable; designs/clones as before. - Kokoro/Kitten/Web Speech (fixed-voice): the design text goes read-only and a per-hero "Voice" dropdown of the provider's named voices appears. Play synthesizes the quote in that voice (cached as a WAV for Kokoro/Kitten via a new PCM→WAV encoder; spoken live for Web Speech). The pick is saved per hero (voiceId) and the cache tracks voiceIdUsed for staleness, mirroring Qwen3. The provider is set on another tab, so tts.js now emits onTtsEngineChange and the panel re-renders its voice controls live (no reliance on tab visibility). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>e648dca -
Persona: simplify prompt to name/about/quote/voice; editable in Settings
- Drop the specialty/personality/vibe tags from the generation prompt and the hero panel — only name, about, quote and voice design remain. - New Settings → "Persona Prompt" section: view and edit the system prompt that writes each hero. Saved locally (tinyarmy.personaPrompt) and used on the next "Recruit hero"; Reset restores the built-in default. - personaPrompts.js exposes PERSONA_SYSTEM_DEFAULT + get/set/reset/isCustom; personaPanel.js calls getPersonaSystem() so an override wins. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>575fb61 -
Persona voice: re-DESIGN when the voice description changes (not clone)
Editing the voice design now regenerates a fresh timbre instead of cloning the old voice. play() cloned whenever a cached voice existed, so a changed design just re-spoke the old voice. Now it clones only when the timbre should be kept (voice unchanged, new words) and designs otherwise (new/edited description). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>c531198 -
Persona voice: play↔stop toggle, loading spinner, auto-stop on nav/new pick
- ▶ becomes ⏹ while a voice is sounding; clicking again stops it. - While a voice is being generated the button shows a spinning loading ring (CSS .busy::before) — the spinner clears the instant the WAV is ready, then the button flips to ⏹ for playback. - Voice auto-stops when: navigating away from the Personas tab (Intersection Observer on the hidden stage), picking another hero from the barracks, and starting a new generation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>ba238a8 -
Persona voice: cloud voice-clone, varied voice designs, animated class picker
- app.py: DashScope cloud voice-clone for prod — two-call enroll (qwen-voice-enrollment) → synthesize (qwen3-tts-vc-2026-01-22), then fetch the returned OSS audio URL. Wired into /qwen-tts when ref_audio is present (mirrors the local design→clone flow). Verified flow returns 200 + audio when the account's paid tier is enabled. - personaPrompts.js: voice field now asks for a DISTINCT, class-fit voice with wide variety between heroes (was producing near-identical gruff voices). - personaPanel.js/persona.css: class picker is now a custom dropdown showing each class's looping idle sprite (front-right row, Web Animations API), wired to characters.json; mirrors a native <select> (.value, 'change'). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>26cb5c0 -
Personas → heroes: shield icon, hero terminology, empty state, active highlight
- Nav icon for Personas: 🪖 (contemporary helmet) → 🛡 (fantasy shield). - "Recruit a soldier" → "Recruit hero"; all "soldier" copy → "hero" ("No heroes saved yet", prompts, store defaults, diary). - Persona page hides the hero fields until a hero is generated or picked (empty state "Recruit a hero, or pick one from the barracks"). - The selected hero is highlighted in the barracks roster (ink fill + red accent bar). Verified: icon, empty/shown toggle, active highlight, terminology. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>29474fc -
Fix: red headings, voice-edit badge, and voices persisting across refresh
- Persist voiceQuote + voiceDesignUsed in the saved persona record. Without them, a reloaded persona always read as "dirty" so ▶ re-synthesized instead of replaying the cached file — that's why "voices weren't saved on refresh." Now a saved voice reloads as current and replays from IndexedDB; only real quote/voice edits mark it stale. - Section headings: color !important so Gradio's text var can't blacken them (now red). Verified red = rgb(216,39,26). - Confirmed: editing the voice design (not just the quote) badges ▶; audio + voice-state survive a page reload. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>9f5e779 -
Persona voice: one pulsing play button, sidebar-style headings, live badge
- One ▶ play button (create button removed). Always shown on the Quote heading; it designs the voice when there's none, clones the last voice when the quote/voice was edited (same timbre), or replays the cached file when current. - Pulsing red badge on ▶ whenever there's no voice yet OR the quote/voice changed — updates LIVE as you type (input listener), not just on blur. - Section headings restyled like the sidebar: a short ink line before a small red uppercase heading; the action button is anchored right after the heading (not at the page edge). Verified: badge pulses on a no-voice persona; headings + button placement. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>ea860c1 -
Persona: red section headings, icon buttons, edit-aware voice + clone-on-play
- Result laid out in sections (line + red heading): About, Quote, Voice design. - ▶ play icon sits on the Quote heading; 🎙 create/recreate sits on the Voice design heading; both simplified to icons (play is neutral, not red). - Editing the quote or voice marks the cached audio stale → a red badge on ▶. - Tapping ▶ when badged does NOT redesign the voice: it CLONES the last voice file (Qwen3-TTS Base model, keeping the exact timbre) for the new line, plays it, saves over the old file, and clears the badge. Otherwise ▶ replays the cached file. 🎙 is the explicit "new voice from the description" path. - Backend: TINY_TTS_MODE=local loads the Base model lazily and clones via generate_voice_clone((numpy,sr) ref). Prod (DashScope) gets the voice description so it re-designs gracefully (no clone model). Verified clone end-to-end on the GPU (5s). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>72160ec -
Persona: editable fields (auto-save), quote after about, Create/Replay voice
- Reorder result: name → tags → about → quote → voice design. - All fields (name/about/quote/voice) are click-to-edit (contentEditable); every edit auto-saves to the roster — no Save button. Generated personas auto-save immediately. - "Hear voice" → "🎙 Create voice": synthesizes the QUOTE in the designed voice, caches the WAV in IndexedDB (personaStore put/getAudio), and plays it. Once created, a ▶ replay button beside the quote replays the exact cached file; button becomes "Recreate voice". - tts.js: createVoiceWav() returns the raw WAV (caller caches/plays); qwen3.synthWav() added. Verified end-to-end on the local GPU: synth → cache → retrieve. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>308478f -
Personas-as-agents: spoken quote + save to a local-first barracks roster
Each persona now also gets a `quote` (a short spoken line). "Hear voice" designs the voice and speaks the QUOTE (not the bio). New personaStore.js — a local-first roster (localStorage JSON blob + CRUD + change listeners + a pluggable sync hook), modeled on woid's Shelter store — so saved soldiers persist for returning visitors. Persona panel gains "💾 Save to barracks" + a roster list (view / remove); MAX_TOKENS 160→200 for the extra fields. Verified: save → reload → persists. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>0ecdf98 -
Move Voice settings into the Settings page; add a sidebar ⚙ Settings button
- settingsPanel.js injects two sections into Gradio's settings page now: "Local AI Model" + "Voice" (the TTS engine/voice picker, moved off the diary page). Both ride the shared singletons. Auto-narrate is now global facade state (getAutoNarrate / setAutoNarrate) so the diary reads it without the bar being mounted. - diaryPanel: drop the voice bar; keep the Read-aloud button + narration; read getAutoNarrate() for "narrate as it writes". - Sidebar gains a "⚙ Settings" item (nav.json) that opens the SAME Gradio settings page as the footer link (tiny.js wraps tacNavigate to click the footer button). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>dffe06d -
Add Qwen3-TTS Voice Design (DashScope) + persona voice descriptions
Reuse the oikomi Space's one trick — proxy Alibaba DashScope's qwen-voice-design API — but with our own DASHSCOPE_API_KEY (Space secret), server-side so the key never hits the browser. No GPU/ZeroGPU needed. - app.py: POST /qwen-tts {text, instruct} → DashScope qwen-voice-design → 24kHz WAV. - web/ttsQwen3.js: new TTS engine (mode pcm) calling /qwen-tts; the "voice" is a free-form description (preset, or the persona's). Registered in the TTS facade with setVoiceDescription() + previewVoice(); labeled cloud (not local-first). - Persona generation now emits a `voice` field (one-line how-they-sound description); cap raised 100→160. Parser keeps it; panel shows it + a 🔊 Hear voice button that designs the voice and reads the about aloud. - Diary: Qwen3 "persona voice" is designed from the unit's traits. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>717332c -
Settings: model section to top; default WebLLM + Qwen3 0.6B; copyable debug
- Inject the "Local AI Model" section at the TOP of Gradio's settings page. - Default engine = WebLLM (best on mobile WebGPU; falls back to wllama with no WebGPU) and default model = Qwen3 0.6B. - Persona + diary panels gain a "📋 Copy debug" button that copies a paste-ready report (engine · backend · model · input · outcome · raw output · stripped text, plus exception/stack), so failures like the empty-JSON persona can be sent back verbatim. Clipboard-blocked contexts fall back to selecting the text. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>898540a -
Put the model picker in Gradio's OWN settings page (not a custom tab)
Per the clarification: inject the engine/model picker into Gradio's native settings page (footer "Settings" → ?view=settings) instead of a custom sidebar tab. Gradio's settings page isn't an official extension point (footer_links only toggles which of api/gradio/settings show), so settingsPanel.js anchors on its "Display Theme" section, clones the section + heading classes, and appends a native-looking "Local AI Model" section driving the shared runtime.js singleton. Reverts the custom Settings tab + nav item + #settings-stage. Graceful no-op if Gradio's DOM changes (app still runs on the default model). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>cd43499 -
Move engine/model settings to a Settings page; share one model across both pages
The model picker was duplicated on Personas and War Diaries. Move the engine + model selector and cache controls into a new Settings tab (settingsPanel.js); both pages now read the same selection from the runtime.js singleton, so there's one place to choose the model and it's guaranteed identical on both. The diary page keeps its Voice (TTS) controls. nav.json gains an "App › Settings" item (Space-only). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>627d835 -
Mobile: collapse model/voice control bars behind tap-to-expand
On phones the diary had two full control bars (model + voice) stacking ~830px of controls above the story. Wrap each bar in a <details> that's open on desktop (summary hidden) and collapsed on mobile (tap "⚙ Model" / "🔊 Voice" to expand), so the war story / persona sits near the top on a phone. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>0218dca -
Mobile: give persona/diary content real space on phones
On phones the controls panel and result were two scroll panes splitting a fixed- height box, squeezing the story/persona to a sliver, and the floating ☰ button overlapped the top of the content. Now (≤768px): the whole .persona-view scrolls as one column, the result gets min-height 62vh, controls clear the hamburger (padding-top), inputs use 16px (no iOS focus-zoom) with bigger tap targets, and tapping Generate/Write scrolls the result into view. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>db5fc22 -
Add in-browser TTS: read war diaries aloud (Kokoro / Kitten / Web Speech)
The diary panel can now narrate entries on the user's device — fully local-first, no audio touches the Space. A switchable TTS facade (tts.js) mirrors the LLM facade: - ttsKokoro.js — Kokoro-82M via kokoro-js (Transformers.js/ONNX, WebGPU/WASM), best quality, Apache-2.0, ~92MB q8 / 326MB fp32; weights from HF. - ttsKitten.js — KittenTTS Nano 15M (~24MB) via onnxruntime-web + phonemizer, WASM for robustness; tokenizer/voices vendored under web/kitten/. - ttsWebSpeech.js— Web Speech API, zero download, OS voices (instant fallback). - ttsAudio.js — one shared AudioContext, sequential sentence playback. - tts.js — facade + makeNarrator(): streams sentence-by-sentence so the diary narrates itself while the LLM is still writing. - ttsBar.js — engine + voice picker + "narrate as it writes" toggle. diaryPanel.js gains a 🔊 Read aloud / ⏹ Stop button and live auto-narration. Reuses storage.js persist() so the voice m…9c371b5 -
Multi-engine benchmark: wllama vs Transformers.js vs WebLLM (engine selector)
Add a runtime facade (runtime.js) over three in-browser LLM engines so the same model can be A/B'd and tok/s compared: - engineWllama.js — llama.cpp WASM + WebGPU (GGUF from HF; 🦙 + local-first) - engineTransformers.js — HF Transformers.js (ONNX, WebGPU/WASM) - engineWebllm.js — MLC WebLLM (WebGPU only; greyed out without GPU) - genStats.js — shared tok/s tracker (decode rate, TTFT) for fair benchmarking modelBar.js gains an Engine selector (unavailable engines disabled); persona + diary panels now import from runtime.js. Removes the old wllamaLlm.js (folded into engineWllama.js). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>f8d0843 -
In-browser models: catalog + picker + cache delete, live tok/s stats, raw 'thinking' stream
- modelCatalog.js: 8 verified ungated GGUFs ≤~2GB (SmolLM2 360M/1.7B, Qwen2.5 0.5B/1.5B/3B, Qwen3 1.7B, Llama 3.2 1B/3B) with real sizes; bigger (Gemma 4 E2B 3GB, Qwen3-4B, MiniMax) excluded — exceed the browser 2GB single-file limit. - wllamaLlm.js: setModel/ensureModel per-model, cache list/delete via wllama ModelManager (exit() + remove()), tok/s + first-token stats during streaming. - modelBar.js: model dropdown (label · size · ✓downloaded) + 🗑 delete-from-cache. - persona/diary panels: model picker, live tok/s, and a raw 'thinking' stream view so you watch tokens arrive. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>8eac3eb -
Personas + war-diary via llama.cpp (reusing woid's persona SSE protocol)
- llm.py: configurable runtime behind one stream_chat() — in-Space llama-cpp-python (local GGUF or HF pull) by default, OR an external OpenAI-compatible llama.cpp endpoint (TINY_LLM_BASE_URL, woid's switch). Lock-serialized; stub fallback. - prompts.py: Tiny-Army persona + war-diary system prompts. - persona_parse.py: woid's parse.js ported to Python (defensive JSON extraction). - app.py: Barracks diary() now streams from the model (Gradio generator); new POST /persona/generate/stream emitting woid-compatible SSE; Personas tab. - web/personaStream.js: vendored from woid (now takes an optional path param, upstream-safe); web/personaPanel.js: vanilla panel reusing it verbatim; web/shell/persona.css. nav.json gains a Personas item. - Dockerfile/requirements: llama-cpp-python prebuilt CPU wheel + huggingface_hub. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>67f4321
-
Space: brand title in Fraunces w/ red dot + 'small minds, big dreams' subtitle; CHARACTERS title small+red; stage fixed-fills full height like main sidebar
0d345ef -
Chrome: left-align picker rows (Gradio was centering them)
48bdfa8 -
Space: fix Carnival assets (URL-decode), red section headers w/ line, square selections, kill Gradio list bullets/margins, stage fills page
8f28cd7 -
Shell/chrome: force label spans to inherit link colour (Gradio --body-text-color hid active-item + compass text on dark fills)
0feb400 -
Space: mirror auto-battler styling — parchment theme, fonts, paper sidebar
- Re-theme the shell sidebar (sidebar.css) from a bespoke dark palette to the auto-battler paper theme (paper-2 cream like the stage, ink text, 2px ink borders, Space Grotesk / JetBrains Mono) so the gray/white-text drift is gone. - Load Space Grotesk / JetBrains Mono / Fraunces on the Space + a parchment body theme so the whole UI mirrors the app instead of Gradio's Montserrat-on-grey. - Defend the extras buttons' font/size/padding with !important so Gradio's button theme stops enlarging them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>d6b7853 -
Shell: defend movement chrome link/key/extra colors with !important vs Gradio .prose
d633da3 -
Space: render the FULL Sprite Animations chrome via shared playground
Replace the dropdown+buttons with the same spritePlayground.js + spriteScene.css the React app uses: team-grouped character picker, on-canvas instructions, aimed-attack compass, and the extras list with keypress hints — all from one source. app.py just hosts the container; the playground builds everything. build.sh now bundles playground.js and copies the chrome CSS too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>0c573fe -
Space: close drift — curate full asset set, unified nav IR, sync via build.sh
- Assets: curate_assets.py copies every manifest-referenced sheet (177 files: shadows, extras, companions, diagonals) from the full auto-battler set, so the Space renders shadows/effects like React instead of 404-degrading. - Nav IR: app.py build_sidebar now consumes the SAME src/shell/nav.json the React app renders (items with a `space` tab label); no more two hardcoded sidebars. - build.sh now also copies web/shell/* and runs curate_assets.py, so shell + assets can't silently drift from auto-battler. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>23ab228 -
Space: fix mixed-content fonts (upgrade-insecure-requests + proxy_headers) and self-style collapse/reopen buttons
Custom domain served Gradio's theme.css over http:// (proxy didn't signal HTTPS), blocked as mixed content -> theme font fell back to Arial. Add a CSP upgrade-insecure-requests meta + uvicorn proxy_headers so assets load over https. The collapse/reopen <button>s leaned on Gradio's leaked button styling for their background; with theme.css blocked they rendered bare. Give them a self-contained icon-button look in the shared sidebar.css, defended with !important against the host button reset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>9b8bd1e -
Shell: !important link-reset on nav items so host link themes can't leak in
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>2f7b5a7 -
Shell: scope nav-item link styles under .tac-sidebar (beats host .prose a)
Gradio's .prose a re-coloured/underlined the nav links; bumping specificity with the .tac-sidebar ancestor (framework-neutral) keeps the sidebar's own styling. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>48ba265 -
Space: shared app-shell sidebar (nav.json IR + sidebar.css/js) wired into Gradio
The same nav.json + sidebar.css + sidebar.js the React app uses (auto-battler src/shell/*) now drive a slide-in sidebar on the Gradio Space: Python templates the IR into markup, css_paths injects the shared CSS, head injects the shared behaviour JS. Proves UI chrome is shareable across React and Gradio from one source (Pixi excluded — it's canvas-only and already shared). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>f29c07a