Commit graph

2,282 commits

Author SHA1 Message Date
Admin
6251f7c615 ai-hub: body domain — live pose packets ride the realtime session
New `body` backend (sam3dbody-ref): a persistent length-prefixed-PNG /
JSON-lines worker subprocess seam with ready handshake, per-frame timeout,
bounded restarts. LiveFrameOut grows aux_json — structured per-frame JSON
sent to the client before the frame — and output_encoding "none" makes a
session pose-only (refused with loop_mode feedback, also on control flips,
which upgraded apply_control to Result). Worker code+model stay
box-provisioned via MAKEPAD_SAM3DBODY_WORKER; the repo carries only the
MIT seam. Codex lane + Fable review (ready handshake, spawn timeout).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0165w1ZL1f1TruX5u2qC7mSX
2026-09-01 19:05:34 +02:00
Admin
176433a56e tweaker: click-again climbs the pick — the container under the children is reachable
The deepest-wins pick could never land on a container its children fully
cover (the chat panel, any rounded-bg pane). Now clicking inside the
pinned widget again walks the pin up one ancestor per click, skipping
design-transparent views, zero-rect wrappers and ancestors whose area
misses the click (a splitter's grab bar); at the window the climb wraps
back to the deepest pick. The window itself is never borrowed — it is
mid-dispatch when the handler runs (tree lookups decide before any
widget borrow).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 18:18:30 +02:00
Admin
915ce7c4e2 asset-server: the embed module — fully-local mode becomes shared
The VJ's attach-or-host logic (health probe, beacon listen, the main
store's server.lock as proof-of-life; loopback-only Host with the library
publisher, rooted in the main library when one exists) moves into
makepad-app-asset-server as embed::resolve, steered by <PREFIX>_ASSET_
EMBED/ROOT/PORT. vj/local_store.rs becomes the thin VJ-flavoured wrapper.
First second consumer: the sandbox.
2026-09-01 18:01:21 +02:00
Admin
0131ba49ed mpwm: --test-action launch-<app> — put one registered app on the desk, deterministically
the existing startup test hook learns the launcher's verb, which is how a
hidden instance hosts a real child for framebuffer verification.
2026-09-01 17:43:47 +02:00
Admin
df265f7a55 vj: a short console draws smaller — height joins the scale chain
console_scale defended the width and rearranged the very short, but
between the fold thresholds (~820 points) and the height the console
actually needs (~1000) lay a dead zone where the lower region simply
clipped — an mpwm tile at 844 points lost its rail from MAP down and half
a grid row. TARGET_HEIGHT_POINTS closes it: console_dpi now takes the
window's physical height and a short console shrinks exactly as a narrow
one does, to the same MIN_SCALE floor, before the folds take over —
measured in the shrunk points. A wide, short window still prefers the
lists-beside arrangement (judged at native scale, so the chain stays a
pure function of the physical window). The whole rule chain threads the
height; tests cover the tile shape that exposed this and the new ladder.
2026-09-01 17:43:47 +02:00
Admin
d5a6d64d1b vj: LIGHTS and ARCHIVE are rail chips under MAP — any content chip is the way back to the grid
The tiny icon page-tabs are gone. The special pages join the lane rail as
first-class chips: when one is up no content lane reads selected, and
clicking VIDEO (or any lane) flips the lower region back to the grid — the
persisted ARCHIVE page can no longer read as an empty library.
2026-09-01 17:21:35 +02:00
Admin
1d97d63e2f rustc 1.98 sweep: script module glob imports nothing uses, and strlen declared as libc has it
1.98's sharper macro-use tracking flags the live-id macro globs five script
modules kept without using; windows-strings' strlen extern takes *const
c_char with a cast at the two call sites instead of tripping
suspicious_runtime_symbol_definitions (PCSTR is transparent over *const u8,
so the ABI never changed).
2026-09-01 17:12:15 +02:00
Admin
e4a2e75d66 ai-hub: backend imports and helpers follow their feature gates
the vision and ocr backends' ContentBackend machinery lives behind the
llm feature; the imports and the lane-refill helpers now do too, so a
client crate compiling the hub with a leaner feature set (vj, chat-ui)
builds without warnings. pipeline_submit_collect stays feature-free for
its tests and says so.
2026-09-01 17:09:26 +02:00
Admin
89fe23fb68 vj: the self-hosted store is the full host — the library publisher rides along, loopback only, no beacon
Attach still wins whenever discovery finds a server (health probe, UDP
beacon, main-store lock). When nothing answers, the VJ now boots the
standalone asset-server's Host instead of a bare AssetServer: catalog +
CAS plus the ai-content library publisher over local/ai_content_library,
so a self-hosted VJ sees the same rows asset-ui publishes — not an empty
seed store. The fallback root is the user's main library when one holds a
catalog (server.lock arbitrates the race; losing it means attach). Both
planes bind 127.0.0.1 and no beacon runs: a private host is not a thing
the network can discover. HostConfig grows a blob_refs knob so the VJ
keeps its reference-import policy; the store decision is logged at boot.
2026-09-01 17:09:26 +02:00
Admin
e6c84d9b6a zune: the crate-root macro_use attrs go — rustc 1.98 phases the inner form out
zune-core's was load-bearing (alloc macros, no_std) and becomes the outer
form on its extern crate; bmp, jpeg and qoi never used it — deleted.
2026-09-01 17:08:31 +02:00
Admin
46cd1735d0 chore: workspace members and remaining sources
Squashed from work:
- frametween: the VJ's in-betweener becomes a library — the whole mode set, one definition
- rtsmap: one seeded generator for tiled strategy maps
- workspace: register the score crates, archive_org and mp4_index
- map: bake a runnable Amsterdam test map from inside the route app
- platform: file dialogs on Android and iOS, and unbreak the Android build
- ai-hub: makepad-asset-ai becomes makepad-ai-hub at libs/ai/hub, the chat pane becomes makepad-chat-ui, the service bin
- strict-json: the dependency-free JSON module gets its own crate; asset-client re-exports it so nothing downstream move
- asset-creator: the pipeline library is born — specs, the deps gate, and the derived-state law (aicore §9)
- makepad_ai is deleted — every backend is a hub pipe, the agent seam lives with its consumers (aicore §14, decided 2026
2026-09-01 16:46:35 +02:00
Admin
0257d6c6dc engine libs: render and sim for a strategy round, the per-unit decision hook, the mp4 sample index, stitch and xatlas quieted, example refresh
Squashed from work:
- docs: button shader annotations — widgets/button.rs complete + splash demo buttons
- tweaker: the Shader tab shows the pinned widget's ANIMATOR STATES as little posed swatches under the well — one per tr
- widgets: the glass stops reading draw_pass.time, so glass apps idle again
- render, sim, platform: what a strategy round needs underneath
- draw, platform: overlays now composite above content that uses draw_depth
- asset: mp4 sample index for range-streaming, chat tools, import profiles
- platform: native file and save dialogs, in-house on all three desktops
- sim: a per-unit decision hook — one commandable unit can think for itself, before the kit steers
- makepad_ai is deleted — every backend is a hub pipe, the agent seam lives with its consumers (aicore §14, decided 2026
- libs: the zero-warning sweep — stitch casts say what they mean, xatlas keeps upstream's surface quietly
- zero-warning sweep, round two — the first full-workspace pass
- zero-warning sweep, round three — the model lanes and the deep examples
- zero-warning sweep, round four — the last stragglers
2026-09-01 16:46:35 +02:00
Admin
87474bd68d finance: a personal ledger, budgets and reports on the DataGrid
Squashed from work:
- finance: a personal ledger, budgets and reports on the DataGrid
- zero-warning sweep, round two — the first full-workspace pass
2026-09-01 16:46:35 +02:00
Admin
79ccb10e81 mp*: the treemap camera era — canon-space packing, 2.5D and orbit views, streaming walks, the qwen sidepanel; mpwm linux desktop integration; pdf_parse recovery
Squashed from work:
- pdf_parse: indirect /Length, and recover the glyph names a symbol font uses
- mpwm: make the Linux desktop integration usable (#1204)
- mpfiles: the size map goes all the way down, and remembers what it found
- mpfiles: the treemap walk streams every directory at every depth — no more opaque growing blocks over deep heavy subtr
- mpfiles: local qwen chat sidepanel over the treemap selection
- mpfiles: a camera over the treemap — scroll zooms at the cursor with real re-layout detail, drag pans, double-click fi
- mpfiles: a 2.5D treemap — the map extrudes by nesting depth under a 3d toolbar mode
- mpfiles: a real orbit camera over the block view — left-drag orbits, right-drag pans, click stays a click; the project
- mpfiles: the wheel glides, the filter stops paying per frame and per keystroke — measured weights cached against the t
- mpfiles: the 3d view wears no name tags — labels only on hover, the tooltip carries the numbers
- mpfiles: a camera gesture never re-flows the map — mid-motion the picture rides one rigid remap of the last layout, an
- mpfiles: a small zoom keeps the map still — the layout is only remade when the camera truly leaves it (a 1.3x detail b
- mpfiles: a camera reveal is not an arrival — cells the cull swings into view appear instantly at full strength, cells
- mpfiles: the treemap packs in canon space — the arrangement is zoom-invariant by construction; zoom only refines the t
- mpfiles: the map stops breathing — insets are fractions of map space, names reserve no room and float on scrims with a
- mpfiles: a camera settle is calm — zoom detail materialises in place, no arrival fade; the ceremony stays with filter
- mpfiles: camera motion animates nothing but the camera — flat labels freeze per relayout and ride the remap; a calm se
- mpfiles: a fast zoom-out never outruns the map — cull escape relayouts the same frame, the glide's destination is laid
- mpfiles: wheel zoom in the raised modes anchors on the tile top under the cursor, not the ground hiding behind it
- mpfiles: first consumer of the ai-hub — the ask panel rides AiHub::start_local_chat, its private engine is gone (aicor
- ai-hub: chats run the machine election — route to a serving holder, wait on a loading one, claim and publish when open
- zero-warning sweep, round two — the first full-workspace pass
- zero-warning sweep, round three — the model lanes and the deep examples

Co-authored-by: Alex <blackanger.z@gmail.com>
2026-09-01 16:46:34 +02:00
Admin
517308e676 vj: deck sync as a held lock, archive.org as a content source, frametween becomes a library, runs execute in the app
Squashed from work:
- vj decks: sync is a held lock — pinned master, per-pump rate servo
- vj: the deck explorer opens on music
- frametween: the VJ's in-betweener becomes a library — the whole mode set, one definition
- frametween: the gate says which tier moved the picture, from pixels
- frametween: BGRA words go in as they are, and a host may own the clock
- vj: archive.org as a content source
- platform: native file and save dialogs, in-house on all three desktops
- vj: rounder slider caps, and the cap body reads as a blob
- ai-hub: makepad-asset-ai becomes makepad-ai-hub at libs/ai/hub, the chat pane becomes makepad-chat-ui, the service bin
- vj: DREAM runs execute in the app — pipelines.rs becomes the run it used to watch (aicore §9 / F1)
- vj: plain generations execute in the app too — the store's job queue loses its last vj client (aicore §9 / F2)
- asset-creator: the runner — generate one thing and put it in the catalog, one implementation for every surface (aicore
- importer + asset-server host: the coordination era ends (aicore P7)
- client + chat dispatcher: the dead wire comes out (aicore P7/P8)
- zero-warning sweep, round five — vj and chat-ui
- zero-warning sweep, round six — three cascades
- zero-warning sweep, round seven — the last two
2026-09-01 16:46:34 +02:00
Admin
c460905e2a maps + route: the self-baking Amsterdam test map, reversible mkmap weave, the rtsmap generator, road faces, guarded first-run
Squashed from work:
- rtsmap: one seeded generator for tiled strategy maps
- map: bake a runnable Amsterdam test map from inside the route app
- route: the first-run test map starts itself, and its buttons are guarded
- map: the test map bakes its road faces too, from the same shared pass
- map_tiles: mkmap-extract — the weave is reversible, world-cells reconstructible from world.mkmap
- route: the local dispatcher rides the hub — its copied engine is deleted, limits preserved (aicore P1)
- route + converse: off makepad_ai — the Agent seam moves to converse, route's cloud dispatcher rides the hub's Claude p
- ai-hub: chats run the machine election — route to a serving holder, wait on a loading one, claim and publish when open
- libs: the zero-warning sweep — stitch casts say what they mean, xatlas keeps upstream's surface quietly
2026-09-01 16:46:33 +02:00
Admin
145d0b1fe2 score: the notation suite — engraving, layout, playback, midi and musicxml import, the physical piano model, and the score app
Squashed from work:
- score: a headless music engraving, playback and notation engine
- score: the notation app — pianist mode, editing, playback
- piano_model: it was a plucked string by construction, and 20 voicings
- score: one document you can pan, zoom and navigate
- piano_model: the body tap was a click, and the objective was rewarding noise
- score: add the sound panel and library modules
- piano_model: a second engine, and the attack that finally sounded right
- score: two instruments, reverb and brightness — and the rest of the panel gone
- score model: a note remembers how it was struck, and the score remembers the pedal
- score import: keep the velocities and the pedal the file was carrying
- score playback: play the performance, not a flattened copy of it
- score ui: the music list moves to the sidebar, and the view stops fighting itself
- score: the application ships its font and eight performances
- piano_model: a limiter that rides the music, so the knee stops shaping chords
- piano_model: the forte bell was the treble's dynamic slope, and the bass was dying at its own prompt rate
- piano_model: the bridge decides each partial's decay, and a fixed multiplier cannot say that
- piano_model: each partial gets its own two coupled modes, from the eigen algebra
- piano_model: a median that fell between the peaks made every bass partial a drain
- score-ai: LocalBroker — the seam's in-process implementation over the session engine (aicore P8)
- client + chat dispatcher: the dead wire comes out (aicore P7/P8)
- score_pdf: the score model grew a pedal map — the pdf importer initialises it
- libs: the zero-warning sweep — stitch casts say what they mean, xatlas keeps upstream's surface quietly
- score app: the shipped-piece test speaks the PERFORMANCES table
- zero-warning sweep, round three — the model lanes and the deep examples
2026-09-01 16:46:32 +02:00
Admin
e37c263b9c ai backbone: the hub era — makepad_ai deleted, every backend is a hub pipe; machine residency elections, job leases, ETA placement; the store only stores; creator pipelines run in the app (aicore)
Squashed from work:
- asset-ai: FastH3 4-step fast video backend; clip keyframes on the wire
- asset-ui: loop video chains — text→image→video that ends where it began
- h3: safetensors -> pruned-Q4_K GGUF quantizer for the 24GB DiT tiers
- h3_quant_gguf verify: row-error gates calibrated to the measured Q4_K floor
- asset-ai realtime: the feedback loop — the source anchors, the drifted frame inits
- asset-ai realtime: a feedback loop survives a resize and travels by default
- asset-ai realtime: the feedback loop frees itself from the feed handshake and pauses for its listener
- asset-ai realtime: the outbound encode leaves the loop's critical path
- asset-ai ocr: the ocr domain — Chandra 2 at page resolution, and the tower goes planner-owned
- llm slots: a lane can hold an image span — embedding prefill and a rope cursor of its own
- vision tower on CUDA: the encode leg gets its two missing kernels
- llm/ocr: one M-RoPE grid encoder for both image paths, and a livelock made an error
- vision tower on CUDA: the f16 GEMM keeps the precision it was throwing away
- live: a feed that moves box takes its trip with it — one seed image
- vision tower on CUDA: the tiled attention becomes bit-exact, and tensor cores go
- llm prefill on CUDA: the MMA attention kernel gets the tile a 4-to-1 model needs
- asset-ai ocr: the CUDA encode lane joins the integration — vision-parity sits beside run's three arms, and the kernels
- Merge branch 'ocr-perf-integration' into work
- asset-ai: the live anchor can follow the trip, and text leaves the 5090
- asset-ai: the camera moves the world, and the world starts still
- asset-import: the EA strategy classics, in the one 2D contract
- rtsmap: one seeded generator for tiled strategy maps
- asset-ui: one card for the strategy classics, with a pack dropdown
- asset-ai: music3 reference-audio path, ocr/h3 backends, registry
- asset: mp4 sample index for range-streaming, chat tools, import profiles
- cnc: tiberium is twelve growth frames, not twelve empty variants
- platform: native file and save dialogs, in-house on all three desktops
- chat: the scan holds out for a lane home
- chat: a full home queues you — take the free lane
- chat: the preload has a percentage, and the boundless cap stops showing
- llm cuda: the 32x2 attention tile — even GQA ratios stay on MMA
- sa3 gets a bake path: the sfx model's tables precomputed by a diffusion-side bin
- sqlite_query: anti-join regression test
- td import: HARV's second frame block is its harvesting cycle, not a turret
- asset-ui: sprite enhancement runs on the 32B dev DiT — distillation, not the prompt, was the ceiling
- ai-hub: makepad-asset-ai becomes makepad-ai-hub at libs/ai/hub, the chat pane becomes makepad-chat-ui, the service bin
- asset-ui: test health fixtures grow the realtime field they were born without
- ai-hub: one home at ~/.makepad — weights/ run/ cache/ logs/, the service cache migrates from ai_content by a single re
- ai-hub: subprocess workers die with the node — process groups everywhere, PDEATHSIG on linux, one KILL_ON_JOB_CLOSE Jo
- ai-hub: the hub object — AiHub::in_process, pipes vocabulary, and the local LLM engine generalized out of mpfiles (aic
- strict-json: the dependency-free JSON module gets its own crate; asset-client re-exports it so nothing downstream move
- ai-hub: the machine layer — node entries, the 0600 machine token, and the residency election that IS the lock (aicore
- ai-hub: MPHUB1 — the fabric beacon only dedicated nodes can send (aicore §4)
- ai-hub: job leases — work lives only while it is renewed (aicore §8)
- asset-creator: the pipeline library is born — specs, the deps gate, and the derived-state law (aicore §9)
- ai-hub: RAM residency facts — the CPU-side twin of residency.rs (aicore §3)
- ai-hub: ETA placement primitives — relative GPU throughput, the four-term estimate, and an observable breakdown (aicor
- ai-hub: leases go live on the wire — origin fields on submit, /job/<id>/keepalive, /bye, and the reaper that cancels w
- ai-hub: the chat providers move in — fleet qwen, openai, grok, claude/codex/grok CLIs, the responses driver, and the w
- asset-creator: the engine — one pipeline run against the hub, deps-gated, spliced, cancellable, resumable-by-construct
- ai-hub: the machine node mode — --machine binds loopback, registers in ~/.makepad/run, and exits on its own once idle
- asset-creator: makepad-creator-run — the detached client for runs that must outlive a window (aicore §9)
- ai-hub: a native Claude Messages-API provider — API-key or Claude Code OAuth, bounded SSE streaming, injected tools (a
- route + converse: off makepad_ai — the Agent seam moves to converse, route's cloud dispatcher rides the hub's Claude p
- asset-creator: the preset tables move in — fifteen chain-policy constants shared by every creator app (aicore §9 / P6)
- makepad_ai is deleted — every backend is a hub pipe, the agent seam lives with its consumers (aicore §14, decided 2026
- ai-hub: loads hold the machine residency election — set_model_state claims on Loaded and publishes the service port (a
- ai-hub: chats run the machine election — route to a serving holder, wait on a loading one, claim and publish when open
- ai-hub: pick_for_domain_eta — ETA-ranked placement over the shared hard-filter core (aicore §6 / P4)
- asset-creator: the engine picks a provider per stage at dispatch time — a chain's later stages see fresh fleet state (
- ai-hub: the fabric secret gates the service HTTP surface — bearer on everything but /health and the ticketed peer path
- vj: DREAM runs execute in the app — pipelines.rs becomes the run it used to watch (aicore §9 / F1)
- asset-creator: the runner — generate one thing and put it in the catalog, one implementation for every surface (aicore
- chat-ui: the session runs in the app — no broker anywhere on the chat path (aicore P8 / F5)
- asset-store: assets.query is a first-class query endpoint — the bounded SQL surface outlives the broker (aicore P8 / F
- asset-creator: CreatorTools — the chat tool pack for a store that only stores (aicore §9 / P8)
- asset-store: the shrink — the store stores (aicore P7)
- importer + asset-server host: the coordination era ends (aicore P7)
- store config purge + asset-ui goes fleet-direct; the derive protocol gets its route proof (aicore P7)
- client + chat dispatcher: the dead wire comes out (aicore P7/P8)
- ai-hub: 0.3.0 — the health version says which era a node runs
- ai-hub: the default fleet is 'gen' — apps hear the LAN without env plumbing
- ai-hub: the preload note percents the prefill, not the job bar
- ai-hub: conversations keep their KV — the wire mirror, the lane identity, the in-turn dynamic context (aicore §7)
- ai-hub: an open-think model is thinking from its first token
- libs: the zero-warning sweep — stitch casts say what they mean, xatlas keeps upstream's surface quietly
- zero-warning sweep, round two — the first full-workspace pass
- zero-warning sweep, round three — the model lanes and the deep examples
- zero-warning sweep, round four — the last stragglers
- zero-warning sweep, round five — vj and chat-ui
- zero-warning sweep, round six — three cascades

Co-authored-by: Claude <info@makepad.nl>
2026-09-01 16:46:31 +02:00
Admin
dbb82b8b61 chore: the zero-warning sweeps — every target of every workspace crate compiles clean on macos, windows, linux, ios and android
Squashed from work:
- platform, draw, widgets: the cross-target zero-warning sweep
- zero-warning sweep, round two — the first full-workspace pass
2026-09-01 16:46:29 +02:00
Admin
bada23dda2 frame witnesses: presents, uploads, encode and gpu time, a pinned display link, SHIFT+F12 screen capture, MPINPUT latency
Squashed from work:
- every window can record itself: SHIFT+F12 writes picture and sound to local/screencap
- the frame has witnesses now: presents, uploads, encode time, gpu time, and a pinned display link
- the upload counter names its kind: instances and textures split, and any single item over half a megabyte logs itself
- MPINPUT: input-to-glass latency in the present pulse
2026-09-01 16:46:29 +02:00
Admin
7e6e87fa1a platform: native file and save dialogs, in-house on all three desktops plus Android and iOS
Squashed from work:
- platform: native file and save dialogs, in-house on all three desktops
- platform: file dialogs on Android and iOS, and unbreak the Android build
2026-09-01 16:46:28 +02:00
Admin
db3864b0bd map rendering: overlays and labels, per-view clip, the world-clamped centre, carto's road ladder, the @cam gate
Squashed from work:
- map: overlay and label rendering
- map: bake a runnable Amsterdam test map from inside the route app
- map: gate the @cam readout; a .mkmap archive is a directory
- map: the view's own draw list carries its clip
- the map's centre is clamped to the world, not wrapped past it
- a country is drawn in hairlines: carto's road ladder joins by zoom
2026-09-01 16:46:28 +02:00
Admin
3b0b16e8ee draw: overlays composite above draw_depth content, per-session DrawVector geometry, one uniform into a whole draw list
Squashed from work:
- draw: DrawVector reused one geometry slot for every session in a frame
- draw, platform: overlays now composite above content that uses draw_depth
- DrawVars::set_uniform_on_draw_list — one uniform into every retained call of a shader in a list, pass repainted
2026-09-01 16:46:28 +02:00
Admin
149d19fcbb platform: midi pitch-bend byte order, many http connects at once, what a strategy round needs underneath
Squashed from work:
- render, sim, platform: what a strategy round needs underneath
- platform: midi pitch bend was sent with its bytes the wrong way round
- http: the connect slot is a gate, not a turnstile — many connects at once
2026-09-01 16:46:27 +02:00
Admin
e0530b061b glass + widgets: realtime parent blur, glass button icons, idling without draw_pass.time, fold_header opens honestly
Squashed from work:
- glass: a glass button can carry an icon
- widgets: the fab palette cell's colour is a field, not an object
- widgets: the glass stops reading draw_pass.time, so glass apps idle again
- live-with-parent passes: the glass blurs the world in realtime again
- fold_header: visually open is open
- fold_header: the ease snaps to its ends
- fold_header: settled state is the truth, the pane edge is the law, the area is the fold
2026-09-01 16:46:27 +02:00
Admin
b408131172 video: hardware first-frame decode from RAM, one encoder-transform report, stills without an AVAssetWriter
Squashed from work:
- video: hardware first-frame decode straight from RAM — no temp files
- video: the encoder transform is reported once, not once per encoder
- video: a single still does not need a whole AVAssetWriter
2026-09-01 16:46:27 +02:00
Admin
b3dd79978b tweaker + shader const-table: the design-feedback campaign — annotated props, hot-patchable shader constants, animator-state wells, the theme tab, typed editors, undo through anonymous paths
Squashed from work:
- widget_tree: skip-search nodes bound path-cache invalidation
- tweaker: fold the shader source view; plain TextInput, not CodeView
- docs: button shader annotations — widgets/button.rs complete + splash demo buttons
- tweaker: the Shader tab shows the pinned widget's ANIMATOR STATES as little posed swatches under the well — one per tr
- tweaker: the animator-state swatches are full-size wells stacked vertically under the main material well (same width,
- tweaker: typed editors for structured values — a reflected Vec2/3/4 is fused x/y/z/w scrub fields (the whole vector re
- shader: const-table mode — /** name min..max step s */ float literals inside fn bodies compile to hot-patchable scope-
- tweaker: the Props tab opens with TWEAKABLES — every annotated value (a /** */ doc on the key) hot-first, each with it
- tweaker: the sidebar row templates are hoisted into shared let bindings (one source of truth for the Props list, the S
- docs: shader-code constants annotated in the six core widgets
- tweaker: SHADER CONSTANTS — the Props tab opens with the annotated literals inside the pinned widget's draw-layer fn b
- tweaker: undo/redo resolve the pinned widget when a history step's path runs through anonymous segments (a '-' or a li
- shader const table: whole-number literal operands lift too — the parser packs `x + 6.`, `y - 2. - b`, `w * 4.`, `n - 1
- tweaker: the Shader tab reads well stack → SHADER CONSTANTS for the mirrored layer → INPUTS (its uniforms/instances, a
- tweaker: the panel cleaned up to the user's list — the filter (with the exploded-view and note buttons) is the top row
- tweaker: three panel polish nits — a section whose rows are all secondary leads with its first three instead of an emp
- tweaker: the VJ session's fix batch — (1) mirrored material wells clip to their scroll viewport: the well is its own d
- tweaker: theme colours, chunk 1 — the panel reads the app's palette from mod.theme's cascade once per session (every c
- tweaker: the theme hover pulse — hovering a colour chip pulses that theme colour live across the whole app, and grabs
- tweaker: the theme palette strip in the colour popover — hover names and pulses a theme colour, a click binds the prop
- tweaker: the Theme tab — every theme colour and number in a fourth panel tab, colours edited live app-wide, all of it
- tweaker: F12 works without the bridge
- tweaker: the selection wears viewfinder corners, not a box
- tweaker: radius handles come to the hand, not the eye
2026-09-01 16:46:26 +02:00
Kevin Boos
0e19a65eac
x11/opengl/wayland/metal: implement what these backends silently skipped (#1201)
* windows: publish shader-cache entries atomically, and unwind a cancelled resize

Two ways a second instance, or an interrupted drag, leaves a window permanently
degraded. Both were found auditing the backend rather than reported, and both
are cheap.

The DXBC cache is a plain per-user directory that every makepad process on the
machine reads and writes, and entries were written straight to their final path.
`fs::write` truncates first, so a second instance starting at the same moment
could read the prefix of a shader the first was still writing -- the file exists
and the read succeeds, so nothing notices until `CreateVertexShader` is handed a
truncated blob. `shader_bytes_cached` even documented the read path as catching
this, which it did not. Entries are now written to a temporary file and renamed
into place, which is atomic, and a blob that is not a complete DXBC container
(the 32-byte header and its magic) is recompiled instead of used. The temporary
name carries the process id so two writers cannot collide on it either.

`is_in_resize` and the 8 ms resize timer are armed by WM_ENTERSIZEMOVE and were
disarmed only by WM_EXITSIZEMOVE. Win32 does not guarantee that pairing, and a
drag that ends without it leaves the timer forcing repaints forever and the
window presenting unpaced for the rest of its life -- it never returns to vsync.
WM_CANCELMODE now unwinds the loop, gated on having actually been in it, since
that message also arrives for menus and capture changes and unwinding a resize
that was not running would cost a needless ResizeBuffers each time.

Verified against a release build: two instances launched simultaneously on a
cold cache both come up correctly with no panics and no temporary files left
behind, and window resizing is unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* d3d11: validate a cached shader against the size its own header declares

Checking the magic and a 32-byte minimum accepts a blob that was truncated
after its header, which is exactly the shape a half-written cache entry takes.
The DXBC header carries the container's own total size at offset 24, so
comparing it to the length rejects those too.

Publishing entries atomically stops this backend from writing a partial one,
but the cache is a plain shared directory that older builds have already
written to, so the read path still has to be able to tell.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* windows: stop double-scaling a popup window's position

`CxOsOp::CreatePopupWindow` arrives with a position the caller has already put
in physical screen pixels -- it adds the parent window's physical origin to an
offset it scaled by the parent's per-monitor DPI -- and `new_popup` then
multiplied it by the DPI again. The popup landed at `dpi` times its intended
screen coordinates: exact at 100%, and progressively further away above it.

The size was scaled by the *system* DPI, which is the primary display's and
goes stale after a live scale change, so on a second display of a different
scale it was the wrong number twice over. It is now passed through unscaled,
because it is provisional either way: `init` runs `set_inner_size` immediately
afterwards, which scales by the window's own per-monitor DPI once the HWND
exists on its target display.

Note for reviewers: this is unreachable today. `WindowHandle::new_popup` is the
only producer of the op and nothing in the tree calls it, so there is no symptom
to reproduce -- which is also why the arithmetic was free to drift.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* x11: implement window resizing, and stop a protocol error killing the app

Two things the X11 backend did not do, both of which it silently pretended to.

`set_inner_size` and `set_outer_size` were empty function bodies, so
`CxOsOp::ResizeWindow` dispatched to nothing: an app calling `WindowHandle`'s
resize on X11 got no error, no log and no resize. They now call
`XResizeWindow`, clamped to the CARD16 range the protocol encodes an extent in,
since a zero is a BadValue. `set_outer_size` delegates rather than pretending:
the window manager owns the decoration frame, so a client can only ask for its
own extent.

Xlib's default error handler prints to stderr and calls `exit(1)`, and makepad
installed none, so a single rejected request killed the process outright -- a
bad geometry, a race against a window the WM has already destroyed, a missing
extension. Protocol errors are asynchronous and frequently not caused by the
code that happens to be running, so terminating is never the proportionate
response. A handler is now installed before the first request and logs the
error, request and minor codes plus the resource id.

`XResizeWindow` and `XSetErrorHandler` were not in the hand-written Xlib
bindings; both are added, along with the `XErrorHandler` callback type.

Untested: the author has no X11 machine. Both changes are small, local and fail
closed -- an unusable size is clamped rather than sent, and the handler only
turns an existing hard exit into a log line -- but neither has been run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* opengl/wayland/metal: name three failures the backends currently swallow

None of these is device-loss recovery -- that stays a D3D11-only feature. They
are the cases where a backend already fails and says nothing useful, so a bug
report arrives as "the window went black" with no cause attached.

EGL: `eglMakeCurrent` and `eglSwapBuffers` failures were logged unlatched on
paths that run every frame, so a persistent failure emitted thousands of lines a
second. Both are latched once per outage and cleared on the next success, and
`EGL_CONTEXT_LOST` (0x300E) is now named explicitly, since that is EGL saying
the GPU reset and every GL object is dead -- which this backend cannot yet
recover from, and should at least say so. `OpenglCx::make_current` returned `()`
while discarding the result of a call that can fail; it returns `bool` now, so a
caller can skip GL work that would otherwise run with no current context and be
silently dropped.

Wayland: `wp_viewport.set_destination` raises the `bad_value` protocol error --
which disconnects the client -- on a zero or negative extent, and a float-to-int
cast turns both a negative and a NaN into zero. `WaylandWindow::new` clamps the
size for the EGL call but stores it unclamped into the geometry, so an app
created at a degenerate size reached that request. The two destination writes
are floored the way the EGL extent already is.

Wayland `CxOsOp::ResizeWindow` was an empty arm. A client has no "set my size"
request, but window geometry defaults to whatever the surface commits, and the
paint path derives the EGL extent and viewport destination from
`window_geom.inner_size` -- so writing it is the whole operation. Refused for a
maximized or fullscreen toplevel, which must keep the configured geometry or the
compositor raises `invalid_surface_state`. `RepositionWindow` stays a no-op and
now says why: `move` is interactive and serial-gated, and `reposition` is an
xdg_popup request needing a protocol version this backend does not bind.

Metal: a command buffer that ends in `MTLCommandBufferStatusError` produced no
pixels, and nothing noticed -- the completion handler runs either way, so the
in-flight queue drains and the hang watchdog stays quiet over a stale window.
The status is now checked in the handler that already exists and the error named,
capped at 32 reports.

Untested: none of these three backends can be run here. All three are
type-checked for their targets. Deliberately NOT written: anything for Vulkan
(vulkan.rs is not compiled by any target available here, so it cannot even be
type-checked) or the web backend (no JS runtime available to syntax-check the
shim), and any Metal device-loss latch or recovery, because `MTLCreateSystemDefaultDevice`
returns the non-removable SoC GPU and loss in the D3D11 sense does not occur there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* x11/wayland: cap a window extent, and refuse a resize that says nothing

Running the branch on Linux turned up four things it and #1197 let through.

A saved size is floored but never capped, and Wayland is the one backend with
no displays to fit against, so `{"inner_size":[1e9,1e9]}` reached
`eglCreateWindowSurface` and tripped its `assert!`. The app then died before it
could rewrite the state file that was killing it, so every later launch died the
same way -- the exact failure #1197 exists to prevent, one line further down.
`sanitize_window_geom` now caps as well as floors, and both Wayland EGL surface
creations fall back to the default size rather than taking the process with them.

`CxOsOp::ResizeWindow` had the same hole and no cap at all on Wayland:
`resize(100000, 100000)` went straight into the window geometry, which left the
surface EGL_BAD_SURFACE and corrupted the size the toplevel restores to -- a
maximize/restore round trip came back 1259x1259, a fullscreen one 100000x100000.
X11 had the opposite problem: it clamped a zero or a negative up to the CARD16
floor and produced a one-pixel window, silently, where Wayland refused the same
request and said why. Both now go through one `sanitize_resize`, so they answer
a bad request identically.

Last, the new X11 error handler is an `extern "C"` frame and logging panics on a
closed stdout -- `app | head` is enough -- so a protocol error aborted the
process instead of the `exit(1)` the handler exists to prevent. Reporting is
wrapped in `catch_unwind`.

Verified on Ubuntu 25.10 / GNOME on both backends: the 1e9 file now starts and
repairs itself, the resize matrix caps at 16384 with no EGL error and restores
correctly, and the two backends log the same refusal. 76 unit tests.

Still open, deliberately not fixed here: a Wayland self-resize dispatches no
WindowGeomChange. handle_platform_ops holds the Cx borrow for its whole loop, so
sending one needs a deferred-event path that does not exist yet.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 21:50:37 +02:00
Kevin Boos
fd6f307a2b
windows: publish shader-cache entries atomically, and unwind a cancelled resize (#1200)
* windows: publish shader-cache entries atomically, and unwind a cancelled resize

Two ways a second instance, or an interrupted drag, leaves a window permanently
degraded. Both were found auditing the backend rather than reported, and both
are cheap.

The DXBC cache is a plain per-user directory that every makepad process on the
machine reads and writes, and entries were written straight to their final path.
`fs::write` truncates first, so a second instance starting at the same moment
could read the prefix of a shader the first was still writing -- the file exists
and the read succeeds, so nothing notices until `CreateVertexShader` is handed a
truncated blob. `shader_bytes_cached` even documented the read path as catching
this, which it did not. Entries are now written to a temporary file and renamed
into place, which is atomic, and a blob that is not a complete DXBC container
(the 32-byte header and its magic) is recompiled instead of used. The temporary
name carries the process id so two writers cannot collide on it either.

`is_in_resize` and the 8 ms resize timer are armed by WM_ENTERSIZEMOVE and were
disarmed only by WM_EXITSIZEMOVE. Win32 does not guarantee that pairing, and a
drag that ends without it leaves the timer forcing repaints forever and the
window presenting unpaced for the rest of its life -- it never returns to vsync.
WM_CANCELMODE now unwinds the loop, gated on having actually been in it, since
that message also arrives for menus and capture changes and unwinding a resize
that was not running would cost a needless ResizeBuffers each time.

Verified against a release build: two instances launched simultaneously on a
cold cache both come up correctly with no panics and no temporary files left
behind, and window resizing is unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* d3d11: validate a cached shader against the size its own header declares

Checking the magic and a 32-byte minimum accepts a blob that was truncated
after its header, which is exactly the shape a half-written cache entry takes.
The DXBC header carries the container's own total size at offset 24, so
comparing it to the length rejects those too.

Publishing entries atomically stops this backend from writing a partial one,
but the cache is a plain shared directory that older builds have already
written to, so the read path still has to be able to tell.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* windows: stop double-scaling a popup window's position

`CxOsOp::CreatePopupWindow` arrives with a position the caller has already put
in physical screen pixels -- it adds the parent window's physical origin to an
offset it scaled by the parent's per-monitor DPI -- and `new_popup` then
multiplied it by the DPI again. The popup landed at `dpi` times its intended
screen coordinates: exact at 100%, and progressively further away above it.

The size was scaled by the *system* DPI, which is the primary display's and
goes stale after a live scale change, so on a second display of a different
scale it was the wrong number twice over. It is now passed through unscaled,
because it is provisional either way: `init` runs `set_inner_size` immediately
afterwards, which scales by the window's own per-monitor DPI once the HWND
exists on its target display.

Note for reviewers: this is unreachable today. `WindowHandle::new_popup` is the
only producer of the op and nothing in the tree calls it, so there is no symptom
to reproduce -- which is also why the arithmetic was free to drift.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 21:50:26 +02:00
Admin
fd0ea7e264 chore: workspace members and remaining sources
Squashed from work; the fine-grained history is under tag archive/work-2026-08-29:
- mp* wave: mpwm window manager + the mp app family, WM API, theme bridge, PDF engine fix
- Modal claims no layout slot: the DJ page fills its window again
2026-08-29 09:26:27 +02:00
Admin
4ddba62d4b engine libs: csg document, stitch simd, sim providers, teamtalk LAN voice, gltf writer, render, route and maps, bench hygiene
Squashed from work; the fine-grained history is under tag archive/work-2026-08-29:
- mp* wave: mpwm window manager + the mp app family, WM API, theme bridge, PDF engine fix
- mpwm polish wave: terminal key focus, focus-history close order, pop-back-to-origin, occupied-workspace cycling, demo
- work: land the sources the last commits reference
- route: the assistant icon the committed UI references
- fast_inflate: the benches name their dev-deps
- gif/weezl: drop the vendored benches nobody can run
- weezl: the decode tests generate their own LZW fixture
2026-08-29 09:26:27 +02:00
Admin
d25cce9ae6 mp*: the window-manager app family — quick-look, warm-instance pool, pty teardown, theme bridge
Squashed from work; the fine-grained history is under tag archive/work-2026-08-29:
- mp* wave: mpwm window manager + the mp app family, WM API, theme bridge, PDF engine fix
- mpwm polish wave: terminal key focus, focus-history close order, pop-back-to-origin, occupied-workspace cycling, demo
- mpwm: ghost-launch/menu-flash root cause, scroll z-gate, browser binds, path-free status, viewers delisted, Fab opens
- mpwm: Hyprland-exact close animation over a frozen snapshot
- mpwm: warm-instance pool, flat-luminance opens, flicker-free CEF resize
- work: land the sources the last commits reference
- mpwm: quick-look gap fixes; image cache eviction on preview unload
- tweaker: material thumbnails + vibecode popup + ctrl-space notes, undo/redo over the edit ledger, capture-semantics pi
- tweaker: tabbed side panel (Props/Shader/Tree) - shader tab with checkerboard material well + prompt, complete widget-
- mpwm+mpterm: text quick-look — pty teardown deadlock fix, in-place retarget verified, debug probes stripped
2026-08-29 09:26:26 +02:00
Admin
19097335b6 vj: the responsive DJ console (PR #1199 by vjroger), the DREAM expand-flux-video pipeline as one store graph, 16:9 image particles, XF defaults
Squashed from work; the fine-grained history is under tag archive/work-2026-08-29:
- mp* wave: mpwm window manager + the mp app family, WM API, theme bridge, PDF engine fix
- mpwm polish wave: terminal key focus, focus-history close order, pop-back-to-origin, occupied-workspace cycling, demo
- work: land the sources the last commits reference
- vj: responsive DJ mixer + Windows drag-and-drop, cherry-picked from PR #1199 (vjroger)
- vj particles: image mode homes on a 16:9 picture plane
- vj: DREAM pipeline — expand -> flux1 still -> minimax i2v loop, with a canvas picker
- vj: the row shows the real prompt, a flux picker, XF default, and new clips stop vanishing
- a DREAM run is one declared graph the store runs without us
2026-08-29 09:26:26 +02:00
Admin
d24cbee60d asset platform: store pipelines with honest banded progress, vision annotation through the job queue, fleet scheduling (roles, spread, peer provisioning), the music contract, synonym search, the runs card
Squashed from work; the fine-grained history is under tag archive/work-2026-08-29:
- mp* wave: mpwm window manager + the mp app family, WM API, theme bridge, PDF engine fix
- work: land the sources the last commits reference
- kenney: catalogue all 50 free 3D kits; Modal dismissed() never fired
- asset store: central vision-annotation queue; Kenney donate prompt
- asset-ai: vision domain — image + prompt -> text on every fleet node
- annotation runs the fleet's vision services through the normal job queue
- vj: responsive DJ mixer + Windows drag-and-drop, cherry-picked from PR #1199 (vjroger)
- store search: WordNet synonym expansion, query-side; per-term seeks
- video scheduling: a cold model pin never downloads past a warm one, and a dedicated box only serves its role
- an expansion can never lose a run — and `expand: true` finally means something
- fleet panel: a slow box is not a missing box
- h3: first+last keyframe conditioning (the weights were always FL2VA)
- registry: the three H3 FL2VA tiers name their real conditioning
- fleet scheduling: spread before stacking, and a stuck job moves
- a download never steals a job from a box that has the weights
- the faster GPU takes the tie: 6000 > 5090 > 4090
- an evicted flux model gives the card its VRAM back
- a job now records what each of its stages was handed
- open a stage in RUNS and read what it sent
- every expanded song was an instrumental: the lyrics had nowhere to go
- the writer never overwrites words the person wrote
- store: a dependent job's body is spliced from its deps' results at claim
- a fleet box's job row says what that job was asked for
- the store runs a whole pipeline, and one record says how far it got
- a client can declare a whole run, watch it, and stop it
- 100% now means published, and the expander is a job you can queue
- one card says what a spawned task is doing, everywhere
- a run that ends says so, instead of being noticed later
- when every box holding the model is busy, buy another copy
2026-08-29 09:26:25 +02:00
Admin
3b4d6a4ff7 platform+widgets: sploded 3D inspect view, the tweaker design-feedback suite, script math-AOT and docs channel, pointer-pin and pixel-probe, modal layout, map warp
Squashed from work; the fine-grained history is under tag archive/work-2026-08-29:
- mp* wave: mpwm window manager + the mp app family, WM API, theme bridge, PDF engine fix
- mpwm polish wave: terminal key focus, focus-history close order, pop-back-to-origin, occupied-workspace cycling, demo
- mpwm: warm-instance pool, flat-luminance opens, flicker-free CEF resize
- work: land the sources the last commits reference
- kenney: catalogue all 50 free 3D kits; Modal dismissed() never fired
- platform: windows check green again — SetWindowTextW binding
- map: exact warp-aware inverse projections — pointer ops work folded
- mpwm: quick-look gap fixes; image cache eviction on preview unload
- tweaker: material thumbnails + vibecode popup + ctrl-space notes, undo/redo over the edit ledger, capture-semantics pi
- tweaker: vibe popup card chrome + dispatch order, ctrl-space notes verified, sploded design v2 chapter
- tweaker: tabbed side panel (Props/Shader/Tree) - shader tab with checkerboard material well + prompt, complete widget-
- sploded v2: nesting-depth z, hairline scope frames, body pass
- sploded: pin the depth convention with a test, kill the draw_depth residue
- sploded: real body-pass split (scene-pass capture, panel flat) + y-convention source of truth with anti-flip gate test
- sploded: hollow outlines, flat-band input, ray-pick unprojection
- tweaker: shader tab defaults to the selection's first draw layer, stale hint trimmed
- sploded: outlines become clipped, antialiased strips; tighter deck
- sploded: merge the lane's v2 (nesting-depth z, clipped AA strip outlines, flat-band input, unproject, SplodedStack bod
- sploded: the exploded view is a LIVE view — pointer events route through the inverse explode transform (ray -> plane -
- tweaker: tabs are real widgets (uid, tree node under the dock, own plane in 3D) and pickable; navigation-class clicks
- sploded: pinned/hover outlines render on the widget's own plane in 3D — per-widget nesting depth lives on the platform
- tweaker: the material well renders the pinned widget's actual shader — the swatch byte-copies the widget's live draw c
- tweaker: the Shader tab shows the shader as written — the layer's pixel/vertex fn source (nearest definition up the co
- sploded: I = true isometric preset (yaw 45°, pitch atan(1/√2))
- tweaker: eyedropper — the colour popover's pick button arms a pixel probe; the next press in the app samples that devi
- tweaker: the shader loop closes — /tweak/apply resolves the pinned widget by uid (anonymous path segments never round-
- vj: responsive DJ mixer + Windows drag-and-drop, cherry-picked from PR #1199 (vjroger)
- tweaker: the material well is a magnifier — the mirrored instance draws at the widget's native size in the well's own
- tweaker: per-layer material thumbnails — the Widget derive emits WidgetNode::layer_areas() (every #[live] Draw… field
- tweaker: the shader source view is the real CodeView (syntax highlighting, selection, editing) when the app registers
- tweaker: Ctrl+Enter sends on every platform (TextInput treated only Cmd as primary on macOS, so Ctrl+Enter inserted a
- Modal claims no layout slot: the DJ page fills its window again
- tweaker: every fn apply recompiles (eval_chunk ran every chunk under ONE synthetic callsite, so the script body — and
- tweaker: an apply whose draw shader fails to compile is rejected — the layer goes back (last live fns / the fn as writ
- tweaker: the Shader tab's source view owns its scrolling (the ScrollYView around the CodeView double-scrolled the care
- widgets: set_visible belongs to every widget, not just View (#1194)
- script: a dead heap's resource handles must not outlive it (#1195)
- Resources: search the executable's directory, not only the working directory (#1196)
- Windows: fit a restored window to the displays that are actually attached (#1197)
- d3d11: a failing GPU call reports the loss instead of killing the process (#1198)

Co-authored-by: Kevin Boos <1139460+kevinaboos@users.noreply.github.com>
2026-08-29 09:26:24 +02:00
Kevin Boos
7a342be4d4
d3d11: a failing GPU call reports the loss instead of killing the process (#1198)
* d3d11: a failing GPU call reports the loss instead of killing the process

The backend already notices a removed device when `Present` returns
DXGI_ERROR_DEVICE_REMOVED, but a device rarely dies at a moment as convenient
as a present. It dies between frames, and the next thing that touches it is a
resource creation or a buffer map -- of which this file had 76 unwrapped, plus
three `std::process::exit(1)`. So the usual outcome of a GPU driver reset,
a TDR or a hybrid-GPU transition across suspend/resume was a panic, and the
graceful path was unreachable.

Route the calls a dead device actually reaches through `D3d11Cx::note_error`,
which asks `GetDeviceRemovedReason` rather than pattern-matching the HRESULT the
failing call happened to return -- creation calls do not reliably return the two
DXGI device-lost codes, while the device itself always knows and keeps saying
so. That answer sets a process-wide `device_lost` latch and logs once.

The softened sites are the ones a dead device lands on first: draw-list and pass
uniform buffers, which upload every frame with no dirty gate; texture and render
target creation; and shader object creation, whose `CxOsDrawShader::new` already
returned `Option` with both callers handling `None`.

Three latent bugs fall out of auditing them, each of which loses content
permanently rather than noisily:

  - `update_vec_texture` consumed the dirty flag with `take_updated()` and then
    returned early when the pixel buffer was out on loan, so a texture that hit
    that window was never uploaded again. For the glyph atlas that means all
    text disappears for the life of the process. The Metal backend already
    guards this; D3D11 did not.
  - `hlsl_compile_shaders` drains `compile_set` destructively, so a shader whose
    object creation failed was dropped from the queue forever and everything
    drawn with it silently stopped rendering. Failed creations go back in the
    queue.
  - `render_view` unwrapped the geometry index buffer but passed the vertex
    buffer through as an `Option`, so a missing one bound null and drew nothing
    with no error anywhere. Both are now checked together, and a draw call with
    either missing is skipped under a `debug_assert!` that it only happens on a
    lost device.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit cc980805b5774253e592e183f577c5ba08ce85db)

* d3d11: rebuild the device and every GPU resource after a device loss

Detection landed already: present() sets a per-window device_lost and the paint
loop stops re-dirtying the pass. Nothing rebuilt anything, so the window stayed
frozen until the app was restarted.

Recovery runs at the top of win32_event_callback, the one place holding both
&mut D3d11Cx and &mut Vec<D3d11Window> while nothing is mid-render. It releases
each window's swap chain, back buffer, view and paint-beat registration (DXGI
allows one flip-model chain per HWND, so the dead one must be gone first),
recreates the device tier when the device really is gone, drops every GPU handle
the Cx holds, and rebuilds each swap chain against the same HWND.

Clearing handles is only half of a sweep. Geometry and instance uploads are
gated on dirty flags cleared unconditionally once the upload runs, and textures
on a dirty rect consumed by take_updated, so every gate is re-armed or the empty
slot is never refilled. The CPU-side sources all survive: texture pixels live in
TextureFormat::Vec*, geometry in CxGeometry, and shaders keep their compiled
DXBC plus the on-disk cache, so recovery recreates shader objects without
compiling any HLSL.

Retries are spaced 250ms to 4s and driven by the existing signal heartbeat
rather than a new timer, because the GPU can stay absent for a long time. While
lost, the loop is forced to Wait at both EventFlow decisions -- every condition
that would otherwise choose Poll is unsatisfiable when nothing can paint, so it
would spin for the whole outage -- and pending screenshot requests are failed,
both to release their requester and because a non-empty queue is itself one of
those conditions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit d603d3e7996cd71b5da6ed3372c9dc75eebb4a77)

* d3d11: bind the hot-path buffers by reference, not by clone

The device-loss bails introduced an AddRef/Release pair per uniform-buffer
upload and per draw call, on paths that run for every draw list, pass and
geometry every frame. Borrowing reads the same Option without touching the
refcount; only IASetVertexBuffers genuinely needs an owned Option, which is what
the code built before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit ecfab9a9355d50754a7117de8303d66f8919b2ee)

* d3d11: make the device-loss recovery actually work, and add a way to exercise it

Three defects, each of which stopped recovery dead, and none of which is visible
without running it. `MAKEPAD_D3D11_TEST_DEVICE_LOSS=<seconds>` forces a full
device recreation on a timer so the path can be exercised without a driver
reset; a real removal cannot be provoked from inside the process. It is a
stronger test than merely setting the latch, because the device really is
replaced, so any GPU object the sweep fails to rebuild still belongs to the old
device and cannot render against the new one.

  - The sweep called `set_updated` on every texture, which panics for anything
    that is not a `Vec*` format. The first render target it reached took the app
    down. Only vec textures carry a dirty rect; render targets, depth buffers
    and shared textures have no CPU-side contents and get their alloc record
    cleared instead.

  - Every rebuilt swap chain failed with `E_ACCESSDENIED`. DXGI allows one
    flip-model swap chain per HWND at a time and D3D11 destroys lazily, so the
    immediate context's own reference to the back-buffer view kept the old chain
    -- and its claim on the window -- alive after the application had dropped
    every handle it held. `ClearState` + `Flush` once, after all the windows have
    released and before any rebuild.

  - The post-recovery redraw marked every pass slot dirty, including ones
    nothing had drawn into, and `draw_pass_to_texture` unwraps
    `main_draw_list_id` immediately. Only passes that have one are marked.

Verified against a release build: six consecutive forced device recreations,
no panics, the UI rendering correctly after each (text included, so the glyph
atlas re-uploads from its retained pixels), `ResizeBuffers` working on a rebuilt
chain, handle count flat across recoveries, and the loop idle afterwards.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit bb7d1c019612a4fb2668640f47b8e16c3886e1cf)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 09:41:33 +02:00
Kevin Boos
e40a5318f7
Windows: fit a restored window to the displays that are actually attached (#1197)
An app that persists its window geometry has to restore it into a display
arrangement that may have changed completely since it was saved: the display
the window sat on can be gone, a docked laptop can be back on its built-in
panel, and the file can hold values no display ever had. Nothing validated any
of it -- `configure_window` stored the numbers and each backend passed them
straight to `CreateWindowExW` / `initWithContentRect:` / `XCreateWindow` -- so a
window could come back off-screen or too small to grab, with no way back except
deleting the state file.

Windows also manufactured those values. Win32 reports a minimized window at
`(-32000, -32000)` with a zero-sized client rect, `WM_SIZE` published that as
the window's authoritative geometry, and an app saving on shutdown wrote it
down. `WM_MOVE` meanwhile published nothing, so a window that was dragged but
not resized persisted its pre-drag position (X11's `ConfigureNotify` and macOS's
`windowDidMove:` both already published).

Add `platform/src/screen.rs`, holding the policy in one place:

  - `sanitize_window_geom` needs no display knowledge and every backend reaches
    it through `CxWindow::create_geom`. It is what protects the backends a fit
    cannot help: Wayland enumerates no displays for a client and hands the size
    to `wl_egl_window_create`, which rejects a non-positive one -- a persisted
    `0` or `NaN` panicked the app at startup -- and X11 encodes extents as
    unsigned 16-bit and answers a zero with a protocol error that, with no error
    handler installed, terminates the process.
  - `clamp_point_to_screens` pins the origin BEFORE the window is created. The
    fit alone is too late: `set_inner_size` runs in between and works relative
    to wherever the window landed.
  - `fit_window_rect_to_screens` corrects the finished rectangle. A window
    already wholly on the desktop is returned untouched, including one
    deliberately spanning two adjacent displays; anything else moves to the
    display it overlaps most, or nearest by centre, capped to that work area.

Displays come from `EnumDisplayMonitors` + `GetMonitorInfoW` on Windows (both
absent from the vendored bindings, so linked here), `NSScreen.screens` on macOS,
and the root geometry plus EWMH `_NET_WORKAREA` on X11. Wayland is unaffected by
the class of bug: a client there cannot know or choose where its windows go.

Report a minimized window from `GetWindowPlacement().rcNormalPosition`,
converted out of workspace coordinates, so what an app persists is the geometry
the window actually returns to; skip publishing on `SIZE_MINIMIZED`; publish on
`WM_MOVE`, deferred to `WM_EXITSIZEMOVE` during a user drag because the Cx
handler redraws on every geometry event.

Positions are now documented and implemented as physical screen pixels on
Windows and X11 (points on macOS) and are never DPI-scaled, matching
`get_position`, `create_position` and the platform calls. `set_position` alone
had been scaling its argument, so `set_position(get_position())` moved a window
to twice its coordinates on a 200% display.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 07:44:21 +02:00
Kevin Boos
dd4c8309d9
Resources: search the executable's directory, not only the working directory (#1196)
A packaged desktop build addresses its resources through a relative package root
-- `cargo packager` and `robius-packaging-commands` both use `.`, with the
resource trees sitting beside the executable -- and a relative `File::open`
resolves against the process working directory. Any launcher that sets no
working directory therefore starts the app somewhere unrelated and every font,
icon and image open fails: a URL-protocol handler (`HKCR\<scheme>\shell\open\
command` carries no working directory), a file association, a service, a
shortcut created without one.

The result is not a clean failure. The window comes up and lays out correctly,
shader-drawn shapes and buttons render, and network-loaded images appear, but
every glyph and every bundled icon is missing, because those are the parts that
need a file. It reads as a renderer bug rather than a missing directory.

macOS avoids this through `apple_bundle_load_dependencies`, and a Linux `deb`
package uses an absolute `/usr/lib/<name>`, so Windows is the only desktop
target whose resource lookup depends on where it was started from.

Retry a failed open against the directory holding the executable. This is
purely additive -- a path that resolves today resolves identically, and only an
open that would have failed reaches the fallback -- so a dev build's
workspace-relative dependency paths keep working unchanged.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 07:44:10 +02:00
Kevin Boos
5aad53afbc
script: a dead heap's resource handles must not outlive it (#1195)
* widgets: set_visible belongs to every widget, not just View

`ui.value_lg.set_visible(w >= 150)` logged "widget method set_visible not
found for uid WidgetUid(1236)" once per resize, and the widget simply never
reflowed. Label, Button and every other leaf refused a method that View
alone implemented — even though visibility is a Widget-trait property that
`#[visible]` derives for exactly those widgets.

it went unnoticed because the fixed-slot list pattern wraps its rows in
Views. what it breaks is the widget tiles, where a bare Label is toggled by
an on_widget_resize, and the failure reads as a layout that just doesn't
respond to its size.

handled once now, in WidgetRef::script_call, after the widget's own
script_call declines the method — so set_visible (and a visible() getter)
work on anything, and View's copy is gone. a bad argument still keeps the
current visibility and returns an error instead of guessing true.

* script: a dead heap's resource handles must not outlive it

the launcher died about one run in five, always inside the GC and never
anywhere near what caused it:

  gc.rs:300: index out of bounds: the len is 21 but the index is 22

only after an isolate had been torn down and another started — closing a
widget preview, or granting `network` (alloc-time, so the app's isolates
restart).

CxScriptResources caches (heap_key, abs_path) -> that heap's LOCAL handle,
and a heap_key is an allocation ADDRESS. the only cleanup was
CxScriptResourceGc, which runs when the owning heap's own GC sweeps that
handle — and a heap that is dropped wholesale, as a Splash isolate's is,
never sweeps anything. so the entries outlived the heap, and the next
isolate whose root_objects landed on that freed address asked for the same
font and was handed the dead heap's handle index. it stored it in its own
FontMember{res, asc, desc}, where 22 means nothing in a table of 21 —
and nothing noticed until that heap's next collection walked the font
object it had every right to walk.

gc_heaps() drops a dead heap's entries, and detaches handles no surviving
heap still maps to (handle values are heap-local, so two heaps' handles can
be equal). called from gc_dead_splash_isolates beside the storage and
bridge purges, which already runs before a new isolate can allocate.

anything keyed by heap_key needs to be in that function, for this reason.

the same hunt turned up a second crossing, fixed here too:
View::script_call(render) built its `me` object in whatever VM happened to
be calling, protoed off the SOURCE view's heap, and forwarded the caller's
args object into the target VM. render a view whose isolate has since been
torn down and that object stays behind in the CALLER's heap holding a dead
heap's index. it refuses now when the two heaps differ.
2026-08-26 10:43:58 +02:00
Kevin Boos
9c62043f9a
widgets: set_visible belongs to every widget, not just View (#1194)
`ui.value_lg.set_visible(w >= 150)` logged "widget method set_visible not
found for uid WidgetUid(1236)" once per resize, and the widget simply never
reflowed. Label, Button and every other leaf refused a method that View
alone implemented — even though visibility is a Widget-trait property that
`#[visible]` derives for exactly those widgets.

it went unnoticed because the fixed-slot list pattern wraps its rows in
Views. what it breaks is the widget tiles, where a bare Label is toggled by
an on_widget_resize, and the failure reads as a layout that just doesn't
respond to its size.

handled once now, in WidgetRef::script_call, after the widget's own
script_call declines the method — so set_visible (and a visible() getter)
work on anything, and View's copy is gone. a bad argument still keeps the
current visibility and returns an error instead of guessing true.
2026-08-26 10:43:42 +02:00
Admin
93f27f802e chore: workspace members for the new crates, makepad.splash, the naming and loader check scripts, and error_log
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- vj: the thumbnail pipeline becomes one honest machine, and effects go livecodable
- widgets: boxed labels center on their ink, not on the font's line box
- metal: a fresh texture forgets nothing it never had — reallocated vec textures upload whole
- fab: a 3D creation shell and the viewer built on it
- texcomp: the block codec and the container every texture will travel in
- mixer: a live LR-Mix surface for the XR18 — strips paired the way the desk is run, auto-connect, EQ that bends its own curve, and a sweep paced so the console drops nothing
2026-08-26 08:49:50 +02:00
Admin
6c8d301da9 asset-ui: the classic import surface follows what the importer now produces, and the chat tells the truth while it works
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- vj: the thumbnail pipeline becomes one honest machine, and effects go livecodable
- asset-ai: the chat tells the truth while it works
- audio: a FLAC decoder from the specification, beside the MP3 and Vorbis ones
- asset-ui: the classic import surface follows what the importer now produces
2026-08-26 08:49:50 +02:00
Admin
4c5228d9ca mixer: a live LR-Mix surface for the XR18
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- mixer: a live LR-Mix surface for the XR18 — strips paired the way the desk is run, auto-connect, EQ that bends its own curve, and a sweep paced so the console drops nothing
2026-08-26 08:49:49 +02:00
Admin
705d6c73ed vj: the console — real transports and reverse, the music explorer's IMPORT, local store, lyrics and model plumbing, DJ-tab scroll knobs, title-click reset, crossfade fix, stem headroom, and music/sfx browse models
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- vj: the music explorer gets IMPORT, and catalog controls fold away in local mode
- vjfx: three lanes land — engine hooks + hold stage, the videomesh engine, and the audio picture
- vj: the thumbnail pipeline becomes one honest machine, and effects go livecodable
- vj: thumbnails become mp4 — hardware-coded sheets at measured-4K cells, and the bake stops racing the GPU
- store: the ceremony dies — batch publish, one transaction, and the engine stops re-reading its own log
- store: the ceremony dies — batch publish, one transaction, and the engine stops re-reading its own log
- vj: the console grows real transports, and the deck stops lying about reverse
- vj: reverse earns a memory, and the effects stop aging
- vj: video goes NV12 end to end, and the GPU does the unpacking
- vj: windows hands the main thread one megabyte, and the script tree wants two
- vj: the GPU learns to see motion — realtime frame tweening on every deck
- vj: the tweener learns — RIFE runs on the Mac and feeds the warp
- vj: the classical tweener grows up, and every deck gets a tween chip
- models: an interrupted install can never load broken
- vj: the transport becomes a platter — velocity in, position out, one map
- vj: the tween presenter reads the platter — one clock per deck, cued once at the frame on screen
- vj: the presenter switch lands without its scaffolding
- vj: the next pair's fields are fetched ahead of the change under the capacity law — a pair change costs an ordinary beat; macos: the layer's own display link paces the frame when the system offers it, the old path stays as fallback
- vj: AI3 subdivides adaptively — one, three or seven neural frames per pair, chosen from measured synth time against the pair's own period, with classical flow between them and a 7-3-1-FL fallback; the deck shows the depth
- vj: local store, lyrics and model plumbing, and the frame-interpolator's device parity check
- vj: DJ-tab scroll knobs, title-click reset, crossfade fix, stem headroom — plus the pre-Ampere CUDA fix (#1193)
- vj: the deck explorer lists music-tagged audio, the sfx surface filters by tag not category, the track list fills the window, and the modal host has no layout footprint

Co-authored-by: Rogier de Leeuw <vjroger@gmail.com>
2026-08-26 08:49:49 +02:00
Admin
dc389d71a1 vj: the transport becomes a platter — one clock per deck, the producer contract, GPU frame tweening with RIFE, NV12 end to end, AI3 adaptive subdivision, and bit-identical decks
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- vj: thumbnails become mp4 — hardware-coded sheets at measured-4K cells, and the bake stops racing the GPU
- vj: the console grows real transports, and the deck stops lying about reverse
- vj: reverse earns a memory, and the effects stop aging
- vj: video goes NV12 end to end, and the GPU does the unpacking
- vj: the GPU learns to see motion — realtime frame tweening on every deck
- vj: the tweener learns — RIFE runs on the Mac and feeds the warp
- vj: the classical tweener grows up, and every deck gets a tween chip
- vj: the tween clock tells presented time, not producer time
- vj: the transport becomes a platter — velocity in, position out, one map
- vj: the tween presenter reads the platter — one clock per deck, cued once at the frame on screen
- vj: the OFF tier joins the platter — a resident clip's picture is cache[nearest(pos)]
- vj: the media thread loses its second clock — resident clips park the decoder
- vj: the producer gets a contract — keyed ladders, deadlines, and a capacity law
- vj: two decks, one law — identical inputs are bit-identical, and the warp agrees to the byte
- vj: the presenter switch lands without its scaffolding
- vj: the next pair's fields are fetched ahead of the change under the capacity law — a pair change costs an ordinary beat; macos: the layer's own display link paces the frame when the system offers it, the old path stays as fallback
- vj: AI3 subdivides adaptively — one, three or seven neural frames per pair, chosen from measured synth time against the pair's own period, with classical flow between them and a 7-3-1-FL fallback; the deck shows the depth
- video_flow: the flow debug bins, declared behind the convert feature so --no-default-features skips them instead of failing
2026-08-26 08:49:48 +02:00
Admin
cb49b032df vjfx: 104 presets, engine hooks + hold stage, the videomesh engine, the audio picture, livecodable effects, and mp4 thumbnail sheets
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- vjfx: 104 new presets — the transition lane fills out and the screen family goes wide
- vjfx: three lanes land — engine hooks + hold stage, the videomesh engine, and the audio picture
- vj: the thumbnail pipeline becomes one honest machine, and effects go livecodable
- vj: thumbnails become mp4 — hardware-coded sheets at measured-4K cells, and the bake stops racing the GPU
- store: the ceremony dies — batch publish, one transaction, and the engine stops re-reading its own log
- store: the ceremony dies — batch publish, one transaction, and the engine stops re-reading its own log
- vj: the console grows real transports, and the deck stops lying about reverse
- vj: reverse earns a memory, and the effects stop aging
- fab: a 3D creation shell and the viewer built on it
2026-08-26 08:49:48 +02:00
Admin
9821263b2c fab: a 3D creation shell and the viewer built on it — colour picker, material textures, dials that move the scene while they drag, the glTF loader, the tour, and the probes
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- fab: a 3D creation shell and the viewer built on it
- raytrace: the traced pane starts coarse and doubles to native, with the raster underneath
- fab: a colour picker, a material's textures, and dials that move the scene while they drag
- texcomp: the block codec and the container every texture will travel in
- fab: FAB_PROBE_MAT — per-material triangle counts, texture presence and uv spread in the roof probe
2026-08-26 08:49:47 +02:00
Admin
8f3c826265 csg: deterministic boolean bench + fuzz harnesses
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- csg: add deterministic boolean bench + fuzz harnesses (examples)
2026-08-26 08:49:47 +02:00
Admin
d72cffd0f6 raytrace: the traced pane starts coarse and doubles to native, with the raster underneath
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- fab: a 3D creation shell and the viewer built on it
- raytrace: the traced pane starts coarse and doubles to native, with the raster underneath
- texcomp: the block codec and the container every texture will travel in
2026-08-26 08:49:47 +02:00
Admin
87e0ce82e9 texcomp: the block codec and the container every texture will travel in
Squashed from work; the fine-grained history is under tag archive/work-2026-08-26:
- texcomp: the block codec and the container every texture will travel in
2026-08-26 08:49:47 +02:00