Commit graph

2,973 commits

Author SHA1 Message Date
Admin
fbfec26ad7 wm: the Android super-app compiles tiles on the phone against the host engine
The APK carries rustc, the checkout and a packed target tree as LZ4
frames. First compile streams them out of the asset manager into files/
and shows progress on the desk; later launches skip unpack when the
stamp matches. App crates keep a desktop Cargo.toml — dynamic-module is
empty — and rustc `--extern force:` binds makepad_wm_engine already in
the process so widgets stay the host dylib.

libs/lz4 grows a streaming frame codec and a makepad-lz4 CLI; libs/tar
unpacks those frames without buffering the archive. Android Vulkan
records two in-flight repaints instead of waiting every pass, and the
capture Y-flip applies only on the OpenGL fallback.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 17:50:20 +02:00
Admin
6f1e446495 builder: Makepad WM and Makepad Apps are direct launches; Scope comes to the front with --focus
The root menu names the two entries Makepad WM and Makepad Apps, in that
order, below Compile and run Scope. Every entry in Makepad Apps now does
what the WM entry already did: set up any missing compiler, download the
sources, compile and open the app, then return to the list with the
selection kept. The nested per-app checklist and its Back entries are
gone, so the menu has one shape. The Builder's runbook follows.

The platform reads --focus since this morning, not MAKEPAD_FOCUS; the
macOS launch of Scope passes the argument.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 14:41:09 +02:00
Admin
384d0e031c tools: the Builder replaces makepad_loader, the web server moves to makepad/webserver, fleet scripts, docs and the workspace members
tools/makepad_builder replaces tools/makepad_loader: one build target
shared across app builds, workspace package selection, checkout
progress on the public Git API, detached built apps with a completion
state, waits for Windows security scans, manual retry after compiler
locks, dedicated-folder installer checks, catalog and runtime fixes.
tools/web_server and its scripts leave for github.com/makepad/webserver.
Arch USB clone/restore scripts, the qwen38 box scripts and the G-belt
serial test join tools/. docs/agents records the agent workflow and the
remote-control handoff protocol; AGENTS.md forbids vendored sources and
bulk imports. Cargo.toml lists apps/wm-dyn, libs/code_language,
libs/search, libs/tar, libs/loader_bundle and tools/makepad_builder,
and drops the two removed crates.

Squashed from work:
- Share Builder target across Makepad app builds
- Fix Builder workspace package selection
- Align Builder checkout progress with public Git API
- Detach built apps and show completion state
- Wait for Windows security scans
- Offer manual retry after Windows compiler locks
- docs: the agent workflow of record and the remote-control handoff protocol
- builder: dedicated-folder installer checks, catalog and runtime fixes; Windows job objects hold c_void handles
- tools: Arch USB clone/restore scripts, the qwen38 box scripts, and the G-belt serial test
- tools: the web server moves to makepad/webserver
- AGENTS.md: no vendored sources or bulk imports in the tree

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 12:17:59 +02:00
Admin
25a08dccbe apps: Apple Mail with Gmail labels, attachments and reimport; the Android super-app and its module apps; terminal exit polling; TextFlow word selection
apps/mail indexes local Apple Mail through the Envelope Index: Gmail
Inbox, Sent, Starred and Important are label rows, attachments open
from the message view, and a reimport button wipes the cache. apps/wm
hosts apps as on-demand dylibs on Android (apps/wm-dyn, with the module
apps' manifests and module.rs following). apps/terminal polls the child
with MpTerm::process_exited and resolves widget fonts through
makepad_widgets. Widgets: double-click selects a word in TextFlow,
links keep their hand cursor, and three stale tests follow the tree's
root rule and the mobile font policy.

Squashed from work (the apps and widgets parts of each):
- Index local Apple Mail with Gmail labels, attachments and reimport
- terminal: MpTerm::process_exited polls the child, and widget fonts resolve through makepad_widgets
- widgets: double-click selects a word in TextFlow; links keep their hand cursor
- wm: the Android super-app hosts apps as on-demand dylibs (apps/wm-dyn)
- widgets: three stale tests follow the tree's root rule and the mobile font policy

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 12:17:03 +02:00
Admin
2522a19e4f libs: sqlite WITHOUT ROWID reads, an in-repo tar reader, packed git imports, a Haskell lexer, and the AI crate warning cleanup
libs/sqlite_query reads WITHOUT ROWID tables through the index cursor,
refuses to write them, and the pager releases its process write slot on
drop. libs/tar is an in-repo tar reader with gzip through fast_inflate.
libs/git imports packed objects with bounded checkout writers and its
HTTP file responses carry a trailer. libs/code_language gains a Haskell lexer with literate (Bird) dialect
detection. libs/loader_bundle and libs/search are new;
libs/app_module carries the super-app module surface; libs/workspace
adds Settings.infinite_zoom and RendererChoice::gpu_env_value; libs/ai
builds without warnings across the hub, llm, metal and model crates;
windows-rs job object handles are c_void.

Squashed from work (the libs parts of each):
- Restore retained renderer support for Scope (libs/search)
- Share Builder target across Makepad app builds (libs/loader_bundle)
- Index local Apple Mail with Gmail labels, attachments and reimport (libs/sqlite_query)
- libs/ai: warning cleanup across the hub, llm, metal and model crates
- code_language: a Haskell lexer with literate (Bird) dialect detection
- git: packed imports and bounded checkout writers; HTTP file responses carry a trailer
- builder: dedicated-folder installer checks, catalog and runtime fixes; Windows job objects hold c_void handles (libs/windows)
- workspace: Settings.infinite_zoom, the experimental prepared map inside the glyph
- wm: the Android super-app hosts apps as on-demand dylibs (libs/app_module)
- libs/tar: an in-repo tar reader; the super-app unpacks its archives with it
- workspace: RendererChoice::gpu_env_value follows the platform's runtime GPU choice

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 12:15:55 +02:00
Admin
9b5575a2db platform: render-loop fixes from review, the remote gate, --focus, and owned state instead of globals
Review of the retained renderer on top of the runtime GPU choice: a
refused GL retained upload skips the draw and dyn-uniform writes are
bounds checked; Vulkan draws retained publications whose CPU-side data
is empty and records a fresh retained transfer command buffer after
each submission; Metal stamps consumption for every encoded retained
item, empty ranges included; a refused WebGL retained upload is final
for that content.
TaskPool::new_with_priority sets the heavy workers' thread priority.
Remote control: keys no longer hold the gate, if_user_seq is optional,
and status waits through a stall; `--focus` brings the app to the front
as its macOS window opens. The package map, the remote activity ledger
and the GPU choice are owned Cx state, not globals.

Squashed from work:
- platform: a refused GL retained upload skips the draw; dyn-uniform writes are bounds checked
- platform: TaskPool::new_with_priority sets the heavy workers' thread priority
- platform: Vulkan draws retained publications whose CPU-side data is empty
- platform: MAKEPAD_FOCUS activates the app when its window opens on macOS
- platform: `--focus` brings the app to the front as its macOS window opens
- platform: Vulkan records a fresh retained transfer command buffer after each submission
- platform: Metal stamps consumption for every encoded retained item, empty ranges included
- remote: keys no longer hold the gate, if_user_seq is optional, status waits through a stall
- platform: a refused WebGL retained upload is final for that content
- platform: the package map, the remote activity ledger and the GPU choice are owned state, not globals

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 12:15:08 +02:00
Kevin Boos
bc8c37357a Choose the Linux GPU backend at runtime, and make Vulkan the default (#1237)
* Choose the Linux GPU backend at runtime and pace Wayland frames adaptively

A Vulkan-capable desktop Linux build (the `vulkan` feature, or
`MAKEPAD=vulkan`) now carries OpenGL ES as well and picks between them
when its event loop starts: Vulkan on Wayland when a hardware device
answers, OpenGL ES when none does (no driver, only a software rasterizer,
or an X11 session). `MAKEPAD_GPU=auto|gl|vulkan` overrides the choice, and
`MAKEPAD=gl` still produces an OpenGL-only binary. The feature stays
opt-in: the hosted (`--stdin-loop`) and direct renderers of such a build
are Vulkan-only, and Vulkan has no video texture import yet.

Wayland frame pacing
- Pace presents by what the backend and the session can actually do,
  rather than by a fixed number (new `wayland/frame_pacer.rs`). Vulkan
  runs two presents in flight only when the compositor offers `fifo-v1`
  and the driver uses it; otherwise a second present would block inside
  `vkQueuePresentKHR` on a callback an occluded window never receives.
  OpenGL starts the next frame early only when its measured cost says the
  swap would land after the outstanding callback is due, so cheap frames
  are not committed twice per refresh. One present in flight, which is
  what this did before, left a heavy scene at half the display rate.
- Bound the pacing gate at 250 ms so an occluded window cannot freeze the
  app's clocks, and let pending screenshot requests through it.
- Treat WouldBlock on the display flush as transient.

Vulkan
- Bound the frame fence wait and the swapchain acquire on Linux instead of
  waiting forever.
- Keep the per-frame packet arena mapped, recycle completed frame
  resources on the window path, and ask for one more swapchain image on
  Linux, where the pacing can keep two presents queued.
- Skip CPU devices unless `MAKEPAD_GPU=vulkan` asks for Vulkan explicitly.

OpenGL
- Stop repainting forever at rest: poll the texture lifetime fence once
  per frame, and check for time-driven shaders only after the
  zero-instance skip, as Vulkan does. The explicit
  `Cx::frame_completion_serial` poll still always arms a fence.
- Upload draw-call uniforms only when they changed; they were uploaded
  twice per draw call per frame. A zbias shift now marks them dirty, so a
  call skipped that frame still uploads when it next draws.
- Compute the retained-instance upload plan once per buffer per frame; it
  was computed three times.
- Target remote screenshot requests at the presenting window. Every
  `--remote` grab timed out on OpenGL before this.
- Emit the `gpu.present` trace with render and swap timings.

Retained instances
- `upload_plan` settles segments that kept their slot and offset by `Arc`
  identity, scans for the first few that moved, and only then builds a
  pointer-keyed map. On a large map this took a plan from 0.3-1.5 ms to
  about 0.07 ms. Results are identical to the previous planner.
- Add `collect_backlog` so a renderer can drain retirements once a frame.

Runtime backend consistency
- `CxOs::vulkan_active()` replaces the compile-time branches that decided
  between the two renderers, so a build that fell back to OpenGL releases
  its uniform buffers, shares host swapchains and retires textures the way
  an OpenGL build does.

Wayland teardown
- Drop windows before the `Connection`, and destroy a window's EGL surface
  and `wl_egl_window` before its `wl_surface`. Every OpenGL exit on
  Wayland segfaulted inside NVIDIA's egl-wayland.

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

* Make Vulkan the default on desktop Linux, with mipmaps and hosted fallback

Every desktop Linux binary now carries both renderers and picks at startup,
instead of only the apps that asked for Vulkan by name. Three things had to
be true first.

Gate the feature where the fallback exists. build.rs derived `use_vulkan`
from `target_os == "linux"` alone, which also matches OpenHarmony and every
other Linux triple, none of which carry `naga`, and it ignored
`MAKEPAD=linux_direct`, whose DRM/KMS renderer has no OpenGL fallback of its
own. The feature now only reaches x86_64/aarch64 gnu windowed builds;
`MAKEPAD=linux_direct+vulkan` remains the way to ask for direct Vulkan.

Give Vulkan a mip chain. `image_cache_use_mipmaps` was off for Vulkan
because the uploader only ever filled level 0, so every minified image
aliased. Images now allocate their full chain and fill levels below the
first with `vkCmdBlitImage`, the way `glGenerateMipmap` does, skipping
formats the device cannot linearly blit. On Robrix's sign-in icons this
takes Vulkan from 2153 pixels differing from the OpenGL render by more than
8, to 400.

Choose the hosted renderer at runtime too. `--stdin-loop` mode was
Vulkan-only in a Vulkan-capable build and panicked when no device answered,
while its host, on an X11 session, had already fallen back to OpenGL: with
the feature on by default that combination would have killed every child the
wm launches. The hosted path now selects the way the windowed one does, its
import follows the renderer the process actually started, and a hosted child
rejects software devices for the same reason a window does.

Video and `Texture::read_back` are still OpenGL-only; the video error now
names `MAKEPAD_GPU=gl`, and the feature comment says so.

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

* workspace: drop the renderer-routing argument and its build-time wording

One desktop Linux binary now carries both GPU backends and picks at startup,
so an app honouring a saved renderer choice passes it to the platform as
MAKEPAD_GPU and restarts itself. Nothing produces `--renderer-routed` any
more; an argument this parser does not know was already ignored, so dropping
its arm changes nothing for anyone still passing it.

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

* Keep the shared Android and direct-display paths as they were

An audit of what this branch reaches on platforms that share these files
found five places where it changed behaviour it was never meant to touch.
All of them come from code this branch made runtime-selected or relaxed.

Mipmaps are desktop Linux only, matching `image_cache_use_mipmaps`, which
is what asks for the format. An Android or Quest Vulkan build shared the
new chain code and would have allocated levels and recorded blits that
nothing there requests and nothing measured.

The mip chain also needs more of the format than it asked for. It checked
only that the format samples linearly, while `record_mip_chain` blits
between levels, so it now requires BLIT_SRC and BLIT_DST too and keeps a
single level otherwise.

Shader compilation stays a compile-time answer off desktop Linux. Whether
a draw shader is compiled to SPIR-V became a runtime `vulkan_active()`
test, which on Quest would follow an Android Vulkan init failure instead
of the build. Only desktop Linux has that fallback.

The hosted loop compiles GLSL only when OpenGL is the renderer. Losing its
cfg left it calling `gl()` in a Vulkan hosted child, which has no EGL
context, so it panicked. Its Wayland sibling already guards this way.

The direct display build keeps its software-buffer upload. `texture_for_draw`
gained a `not(linux_direct)` that was never needed: `MAKEPAD=linux_direct`
without Vulkan has its own `upload_presentable_image_software_buffer` in
os/linux/presentable.rs, and the outer gate already excludes the direct
Vulkan build.

Also: `gpu_preference` is now gated exactly where its caller is compiled,
since `vulkan_linux.rs` builds for every `target_os = "linux"` under
`use_vulkan`, and the hosted loop's imports follow the block that uses
them, which the direct Vulkan build does not compile.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 12:14:30 +02:00
Admin
a67096d20b platform: the retained renderer for Scope, the vulkan cargo feature and Cx::gpu_backend()
Retained renderer support is back for Scope on Metal, Vulkan, OpenGL,
WebGL and the simulated GPU. A `vulkan` cargo feature picks Vulkan on
desktop Linux, Cx::gpu_backend() names the compiled GPU API, and the
direct WM builds again. Settings.renderer in libs/workspace keeps the
saved GPU API choice (Vulkan | OpenGL) behind the --renderer-routed
argument. The Android build keeps the texture alloc types imported for
OES adoption, and that import stays off the web build. The simulated
GPU builds on Linux again.

Squashed from work, without the cargo vendor snapshot the retained
renderer commit carried there:
- platform: a `vulkan` cargo feature picks Vulkan on desktop Linux; Cx::gpu_backend() names the compiled GPU API; the direct WM builds again
- workspace: Settings.renderer — the saved GPU API choice (Vulkan | OpenGL) and the --renderer-routed argument
- Restore retained renderer support for Scope
- platform: Android builds again — the texture alloc types stay imported for OES adoption
- platform: the Android texture-adoption import stays off the web build
- platform: the simulated GPU builds on Linux again

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 12:14:21 +02:00
Admin
b5a545bfe2 Share language tokenizers with the code editor on work 2026-09-18 12:12:36 +02:00
Kevin Boos
b5bd9bc6b9
Ten fixes from moving an app onto current dev: macOS drawable/resize, the UI signal broadcast, internal drag on Linux, and log noise (#1239)
* Button: add `label_align` to center a wrapped label

`Button::draw_walk` passed `Align::default()` to `draw_text.draw_walk`, so
a label that wrapped onto more rows left-aligned them under each other even
when the button itself centered its content, and no script property could
reach that argument.

* New `#[live] label_align: Align`, mirroring the one `TextInput` already has.
* Defaults to left, so every existing button draws exactly as before.
* Pair it with a `Fill`-width `label_walk` to give the rows room to move.

* macOS: don't paint into a drawable the layer has since outgrown

The prefetching `DrawableWorker` hands back a drawable that `nextDrawable`
acquired on the previous beat. A frame that straddles a resize therefore
attaches a texture of the old size to a viewport derived from the new one.

* moving a window between displays of different DPI drew the whole UI at
  the old scale, and it stuck: a dpi change dirties the pass exactly once
* dragging a border fast left the strip the old texture didn't cover
  unpainted, which macOS shows as magenta

Check the invariant where the drawable is consumed: a texture whose size
isn't the layer's `cal_size` is dropped and one is acquired on this beat,
the way the pre-worker path did every beat. The pool was just rebuilt for
the new size, so that acquire doesn't block. Traced on the `present` topic.

* Window: don't put an app icon in the caption bar by default

`AppIcon` falls back to a generic placeholder tile for any app id makepad
ships no artwork for, so every third-party app got a meaningless icon next
to its title, and an off-centre title with it.

* `AppIcon` gains `visible`, which it had no way to express before
* the caption icon defaults to hidden; apps opt in with
  `caption_icon +: {visible: true}`

* Quiet the startup and per-decode log spam

A plain run printed ~1500 lines before anything happened.

* `zune-qoi` was the only zune crate with `log` in its default features,
  and cargo unifies that onto `zune-core`, so every zune decoder logged
  per image. zune-core's macros became real logging in this tree, so what
  used to be inert now floods the log
* memory budget, task-pool priority/summary and the Metal retained-upload
  budgets move onto the `MAKEPAD_TRACE` topics this tree added
  (`memory`, `pool`, `gpu.upload`)
* drop the studio-websocket line, which only says a disabled thing is off

* Make the UI-hang sampler opt-in

It started with every `Cx`, so a shipped app carried a thread waking ~16
times a second forever, and any stall over 250ms got the UI thread
suspended once per sample while its stack was walked.

`MAKEPAD_UI_HANG_MS` is now the switch as well as the threshold. Unset, no
thread starts and the phase guards see a null registration, which is a TLS
read and a null check.

* Wake the event loop from render workers without raising the UI signal

`Event::Signal` means "a worker has something for you" and is dispatched to
the whole widget tree. The submitter, the instance allocator and the
drawable worker raised it after every commit, so an app painting at 120fps
walked its tree 120 extra times a second. Measured in robrix: 119 signals
against 111 repaints, down to ~0.3 per frame.

They only ever wanted the loop awake, so give them `wake_ui_loop()`, which
is what `set_ui_signal` already called underneath. A dirty pass is what
keeps the paint clock armed, so nothing depends on the flag to get painted.

* Compare the resident instance bytes instead of hashing them

`immediate_payload_hash` FNV'd every byte of every dirty draw call to skip
the upload when nothing changed. That suits a few big payloads, not a 2D
frame: robrix scrolls ~1090 draw calls of ~110 bytes, and the hash cost
2.3ms a frame in a debug build to skip ~12% of 110KB of uploads.

Instance buffers are StorageModeShared, so the resident copy can just be
compared. `memcmp` stays fast in an unoptimized build, and an exact
comparison can't collide into a stale frame the way a hash can.

* Install a platform stylesheet only when an app asks for one

`current()` picked "ios"/"android" straight off `OsType`, so any app built
for a phone was silently restyled: ~270 theme tokens including the fonts,
over whatever the app had already set.

Worse, it only half-landed. `apply_theme` runs from `widgets_mod` but
`apply_widgets` runs from `script_mod`, so an app that calls the
`theme_mod` + `widgets_mod` pair got the mobile palette with desktop
metrics. Every in-tree user already calls `install` or sets
`MAKEPAD_WIDGET_STYLE`, so that variable is now the only implicit route.

* Let an internal drag deliver its pointer events on Linux and wasm

Moving internal drag handling into shared code changed it in two ways that
the macOS and old X11 paths never had, and a dock tab shows both.

The pointer event was replaced by the drag event rather than followed by
it, so no widget saw the `MouseUp`. `Tab` sets `is_dragging` on FingerMove
and clears it only on FingerUp, so a tab could be reordered once and then
never dragged again. Dispatch the pointer event first and append the drag
one, with a flag so that dispatch doesn't produce the drag event again.

A release that never moved after `start_dragging` also produced no Drop and
no DragEnd at all, so `Dock` never cleared `dragging_tab` and kept painting
the ghost. `Tab::min_drag_dist` has no default, so a press, one motion and a
release reaches it. Every other backend ends the drag unconditionally.

* Split the UI signal so makepad's own queues don't broadcast Event::Signal

`set_ui_signal` was the one wake for everything, and every platform loop
answered it by running makepad's handlers AND broadcasting `Event::Signal`
to the whole tree. So termination, the network runtime, live reload and
every pool completion (label shaping, per frame) woke every widget.

* `set_internal_signal`: the loops run their handlers and don't broadcast
* `TaskPool::submit_internal`: a job whose result makepad polls at draw
* `Event::Signal` is documented, and the loops treat the app flag as a
  superset of the internal one, so nothing left on it can regress

The scheduler keeps the app signal on purpose: `service_scheduler` re-arms
the platform timer from `call_event_handler`, which only the app half runs.
Media device changes still go through `SignalToUI::set`, whose instance API
is app-facing; they are hotplug-rare, so splitting that is left alone.

* Harden the drawable re-acquire, the internal drag and the opt-in sampler

Follow-ups from reviewing the five commits above.

* the resize re-acquire only runs while the drawable pool has a free slot.
  Exhausted, `nextDrawable` blocks the UI thread on the compositor, which is
  what the worker exists to avoid; skip the beat and stay dirty instead
* the byte compare no longer skips an item the GPU has evicted, which would
  leave it invisible in a pass that then repaints forever
* the internal drag suspends its items across the pointer dispatch instead
  of holding a flag. An unwound dispatch now ends the drag rather than
  wedging it for the life of the process, and a widget that starts a new
  drag from that dispatch keeps it instead of tripping "start drag twice"
* `tests/ui_hang.rs` opts the sampler in, since it is the thing under test
2026-09-18 09:06:31 +02:00
Kevin Boos
d771eda6fb
Button: add label_align to center a wrapped label (#1238)
`Button::draw_walk` passed `Align::default()` to `draw_text.draw_walk`, so
a label that wrapped onto more rows left-aligned them under each other even
when the button itself centered its content, and no script property could
reach that argument.

* New `#[live] label_align: Align`, mirroring the one `TextInput` already has.
* Defaults to left, so every existing button draws exactly as before.
* Pair it with a `Fill`-width `label_walk` to give the rows room to move.
2026-09-18 09:03:53 +02:00
Admin
a4ea2536a4 platform: topic hunks the per-commit assembly could not place, the Sep 2–15 dev PRs (#1208–#1236) as adapted to this tree, and the zero-warning chore
code_editor/Cargo.toml                          |     1 -
 code_editor/src/lib.rs                          |     3 -
 draw/src/cx_2d.rs                               |    83 +
 draw/src/cx_draw.rs                             |    43 +
 draw/src/draw_list_2d.rs                        |    22 +
 draw/src/geometry/geometry_gen.rs               |     7 +-
 draw/src/image_cache.rs                         |    51 +-
 draw/src/lib.rs                                 |     1 -
 draw/src/shader/draw_text.rs                    |    40 +-
 draw/src/shader/mod.rs                          |     1 -
 draw/src/text/fonts.rs                          |     2 +-
 draw/src/text/mod.rs                            |     1 -
 draw/src/text/slug_atlas.rs                     |     2 +-
 draw/src/turtle.rs                              |  3469 ++++-
 draw/src/vector/triangulate.rs                  |   378 +-
 libs/error_log/src/lib.rs                       |    17 +-
 platform/network/src/backend.rs                 |     6 +
 platform/network/src/backend/apple/http.rs      |     2 +-
 platform/network/src/http_server.rs             |    15 +-
 platform/script/derive/src/derive_scriptable.rs |    19 +-
 platform/script/src/lib.rs                      |     4 +
 platform/script/src/shader_calls.rs             |    92 +-
 platform/script/src/shader_hlsl.rs              |     9 +-
 platform/script/src/shader_metal.rs             |    18 +-
 platform/script/src/shader_wgsl.rs              |    12 +-
 platform/script/tests/hook_scope.rs             |     8 +-
 platform/src/action.rs                          |    12 +-
 platform/src/app_main.rs                        |   110 +-
 platform/src/cx.rs                              |   466 +-
 platform/src/cx_api.rs                          |   368 +-
 platform/src/draw_list.rs                       |  2598 +++-
 platform/src/draw_pass.rs                       |    35 +-
 platform/src/draw_vars.rs                       |   103 +-
 platform/src/geometry.rs                        |   588 +-
 platform/src/gpu_texture.rs                     |     2 +-
 platform/src/id_pool.rs                         |    71 +-
 platform/src/lib.rs                             |    26 +-
 platform/src/log.rs                             |   111 +-
 platform/src/os/apple/apple_game_input.rs       |     6 +
 platform/src/os/apple/apple_sys.rs              |     1 +
 platform/src/os/apple/macos/macos_app.rs        |   384 +-
 platform/src/os/apple/metal.rs                  |  3347 +++-
 platform/src/os/cx_native.rs                    |     5 +-
 platform/src/os/cx_shared.rs                    |   202 +-
 platform/src/os/gpusim/event_loop.rs            |    24 +-
 platform/src/os/gpusim/mod.rs                   |     8 +
 platform/src/os/gpusim/raster.rs                |    11 +
 platform/src/os/linux/opengl.rs                 |   856 +-
 platform/src/os/linux/vulkan.rs                 |   752 +-
 platform/src/os/linux/vulkan_linux.rs           |     4 -
 platform/src/os/linux/wayland/linux_wayland.rs  |    15 +-
 platform/src/os/linux/wayland/opengl_wayland.rs |    67 +-
 platform/src/os/linux/wayland/wayland_state.rs  |    37 +-
 platform/src/os/web/web.js                      |  1409 +-
 platform/src/os/web/web.rs                      |   212 +-
 platform/src/os/web/web_gl.rs                   |   284 +-
 platform/src/os/web/web_worker.js               |    76 +-
 platform/src/os/windows/d3d11.rs                |  1323 +-
 platform/src/remote.rs                          |  1297 +-
 platform/src/retained_instances.rs              |     3 -
 platform/src/script/res.rs                      |   245 +-
 platform/src/sploded.rs                         |    59 +-
 platform/src/texture.rs                         |   416 +-
 platform/src/thread.rs                          |  2816 +++-
 platform/src/thread/ui_hang/linux.rs            |     2 +-
 platform/src/thread/ui_hang/macos.rs            |    22 +-
 platform/src/thread/ui_hang/native.rs           |     3 +
 platform/src/thread/ui_hang/windows.rs          |     2 +-
 platform/src/web_socket.rs                      |    45 +-
 platform/src/window.rs                          |   129 +-
 platform/tests/texture_lifetime.rs              |   143 +-
 platform/video/src/stream_debug.rs              |     2 +-
 platform/video/tests/stream_codec.rs            |    85 +-
 widgets/derive_widget/src/derive_widget.rs      |     4 +-
 widgets/src/animator.rs                         |   138 +-
 widgets/src/combo_box.rs                        |    10 +
 widgets/src/desktop_style.rs                    |     4 +-
 widgets/src/drop_down.rs                        |    96 +
 widgets/src/font_policy.rs                      |    25 +
 widgets/src/grid.rs                             |     1 +
 widgets/src/image.rs                            |     3 +
 widgets/src/lib.rs                              |    18 +-
 widgets/src/map/archive.rs                      |   544 +-
 widgets/src/map/geometry.rs                     |    34 +-
 widgets/src/map/label.rs                        |    48 +-
 widgets/src/map/tile.rs                         |  6890 ++++++---
 widgets/src/map/tile_draw.rs                    |     2 -
 widgets/src/map/view.rs                         | 17896 ++++++++++++++--------
 widgets/src/menu_bar.rs                         |    18 +-
 widgets/src/screen_cap.rs                       |   513 +-
 widgets/src/slider.rs                           |     6 +
 widgets/src/splash.rs                           |   280 +-
 widgets/src/tweaker.rs                          |   221 +-
 widgets/src/view.rs                             |   105 +-
 widgets/src/widget.rs                           |     5 +-
 widgets/src/widget_async.rs                     |   269 +-
 widgets/src/window.rs                           |   601 +-
 widgets/src/window_voice_input.rs               |    17 +-
 98 files changed, 38159 insertions(+), 12677 deletions(-)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:33 +02:00
Admin
12d223737a tools, arch, docs, workspace, examples, sqlite, wasm_bridge
Squash of 55 work commits (Sep 1–12):
  0fd356d  windows: the vendored bindings are generated from a checked-in filter
  79882b5  fabric: a photo or a live camera to a fitted sewing pattern
  1e93309  AGENTS.md: designs stay local; no OS screenshots; focus and hidden-window laws
  aa96dbd  cargo-makepad wasm: package the bin target's wasm and create dirs before minifying
  14d0723  cargo-makepad wasm: production packaging — strip, small profile LTO, optional binaryen, size report
  79e7526  sqlite: a page-store seam — the file backend as before, an in-memory backend, and open_memory / open_with
  60ee978  cargo-makepad: package artifacts carry a content hash so a re-upload is a new URL
  611c9eb  cargo-makepad: production packaging stays off fat LTO; script VM under LTO investigated
  ab8febc  cargo-makepad: fonts packaged from the app's font manifest
  f6bbee9  wasm bridge: shared memory asks for the 4 GiB wasm32 ceiling and steps down where the engine refuses
  fb1416d  cargo-makepad: the threaded wasm module is linked with the 4 GiB wasm32 memory ceiling
  fd5d70c  cargo-makepad: the app's own resources are packaged under its bin name, which is how self:// resolves
  f51ca07  workspace: the wasm interpreter's tests build at opt-level 1 — its own profile setting is ignored inside a workspace, and opt-level 0 overflowed the script eval stack
  942ff86  sqlite: the browser store has one owner — its locks never wait on a clock
  82d0cfa  web path: the trace helper keeps its doc, the journal nonce steps a counter where there is no clock or pid
  6f8c08f  web-server: POST /api/crash stores crash reports in a rotating log on both servers
  106b38c  wasm bridge: the imported memory honours the module's declared limits
  94b8726  dj-pack: tracks in, stems through the hub, a site store snapshot out
  d9f04fc  dj-pack: pack reads caches, never creates them; dry-run writes nothing
  e6a305c  ai-hub + dj-pack: a whole track fits a stems job; long tracks split into spans
  d1910b8  web-server: a store snapshot's extensionless routes are served with the types the exporter recorded
  c1f7b53  asset-client + dj-pack: a long description never rejects a snapshot; the packer writes one bounded line
  453197d  dj-pack: analyse produces the beat grid, overview and loop-splat caches the demo cache ships
  9b4a3ca  network: every completion raises the UI signal
  fad1c49  web server: audio and text files are served, and models/ is immutable like maps/
  569b4a7  dj-pack: every CC BY and CC BY-SA version and the public domain mark are redistributable licences
  dc9cce6  workspace: no std clock on the web in any crate the web apps link — the last start-up worker death is gone
  c7639f0  clippy: timed std waits (sleep, recv_timeout, wait_timeout, park_timeout) are disallowed — they read the std clock and panic on wasm workers
  d748753  wasm bridge: the page environment carries js_worker_wait so the module links — the pool landing added the import for workers only
  ec40fdb  vj + widgets: double-click a knob or fader to reset it to its default — the Slider handles tap_count 2 and emits its normal Slide action; the deck controls carry their neutral defaults (pitch 0, gain/EQ/stems 1, filter centre, crossfader centre)
  fe23e06  AGENTS.md: the execution policy — zero locking on the UI thread as one mechanism for native and wasm, no temporary threads (the pool), the standard operating flow (Codex codes, Fable designs and reviews, Grok tests), and the tweaker on Shift+F10
  e689aea  web_server + geodata + route: live radar, wind and weather on makepad.nl/api — one bounded poller per feed, hourly, disk-backed cache served from an Arc snapshot (a restart never re-polls early), 503 warming until the first result, health reports ok/warming/unavailable with timestamps; KNMI key from --knmi-key-file, the documented anonymous open-data key otherwise; libs/geodata fetches through the platform HTTP client instead of shelling out to curl; the client retries 503 after 30 s and disables a layer only on 404
  cd33943  web_server: radar and weather run on KNMI's documented anonymous key when no --knmi-key-file is given; without --live-cache the pollers keep an in-memory cache and say so once
  2f3e393  web_server: a directory path without its trailing slash (/score) redirects to /score/ instead of 404, query preserved
  9a31d21  flow-ui + widgets: a chosen model shows no node list, and a closed ComboBox shows the start of a long label
  22b2c78  docs: streamline agent runbook and extract reference guides
  6058284  terminal: add hostable session multiplexing via tools/screen
  8a9345b  tools: migrate Cargo.toml lookups to segment-path keys
  8749b91  counter: keep app state across Splash reloads
  3ffd485  tools: add agent launcher and AIHub node update and smoke scripts
  c33a9d3  screen: add bypass and resume menu options
  c40d234  AGENTS.md: current delegation hierarchy (Grok mechanical, Codex hard, Fable manages)
  4184455  Workspace: scope lives at apps/scope (clone of makepad/scope)
  27844c9  makepad arch usb builder
  dd967eb  Workspace: drop nine members that are not in the repository
  1961768  AGENTS.md: hierarchy 2026-09-11 — Fable builds, Codex reviews, Grok proves
  9cd9f94  AGENTS.md: rendering is verified on the real GPU backend, headless is for logic tests only
  5f53254  AGENTS.md: GPU proofs may run hidden; only the headless CPU backend is out
  e950b08  docs: app-remote — hidden GPU runs vs the simulated-GPU backend, measured grab cadence, remote hazards
  9c94a77  arch: the platform plan names the simulated-GPU backend gpusim
  3009257  micro_serde: serde_json-style JsonValue accessors, pretty printer, depth limit and strict parse
  134cd76  gitignore: alternate target directories, root scratch dirs and stray logs are never source
  60ba86e  arch: the render node refs name platform/src/os/gpusim/mod.rs
  1dc571a  tools/arch_usb: Wi-Fi, Intel GPU firmware, the AI hub service and game-hardware udev rules on the Arch image; the WM session script picks the saved compositor GPU
  cc3b05a  tools/arch_usb: a polkit rule lets the arch account start, stop and restart the WM service

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:33 +02:00
Admin
ef94cb59d6 libs: piano_model, drumkit_phys, show_control, score_view, diffusion
Squash of 5 work commits (Sep 2–9):
  066c782  piano model: the lower register is hammered, not plucked
  f2b0253  drumkit: a sample-based kit for the score preview; the physical model parked as drumkit_phys
  3dfe5aa  show-control: restore sixteen DMX scenes with local save overlays
  b49e2e5  piano-model: calibrate partial gains and decay against recordings
  d73cd40  piano_model: add an interactive piano-physics explainer page

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:32 +02:00
Admin
b4f4eb1abd libs: geodata, map_build, mbtile_reader, map_nav
Squash of 7 work commits (Sep 1–12):
  f7093bf  map_nav: the search db's positioned read builds on Windows
  3c03397  geodata: a data library keeps its own clock — the GUI platform is not a dependency
  64d2d3a  map-build: an unfinished bake resumes or restarts itself, and the maps root does not depend on the cwd
  cb572ae  map-tiles: makepad-map-repack rewrites an archive to what the renderer reads
  30fc13b  map-tiles: repack runs on all cores, resumes per shard, reports --status
  2db9d48  mkmap: a root record may decode to 512 MiB — the densest world shards list over five million tiles
  e8be900  deps: drop osmpbf and serde_json from the root workspace

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:32 +02:00
Admin
f08ef0a5e3 libs: git
Squash of 1 work commits (Sep 9–9):
  842ea96  git: topological log, worktree lifecycle and bounded line diff in libs/git

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:31 +02:00
Admin
d2130e6550 libs: model, mesh_edit, csg, scene, sim, render, xr
Squash of 25 work commits (Sep 1–12):
  237da90  render: the sprite lane hands the screen draw back the way it found it
  895b9a7  particles: an emitter can ride a body's own frame
  914471f  ai-hub: a feed session whose last socket left ends on its idle timeout; skin: parent, skinned centroid and a nodes-only rig for retargets
  3fcccf3  sim: the whole world is implicitly editable, and one seam says where the ground is
  5692fab  sim: the landform world proves itself — walker through the tunnel included
  f4af6df  sim: terrain knows who changed it — a plan layer over player history
  adbb078  render: a water volume can be physics without a picture
  c17480f  render: a non-rigid body may carry its own orientation
  acad401  sim: an agent with no route holds and retries instead of walking into the wall
  22b2bf9  sim + chat: the composed world surface takes a map floor; the chat gets plan tools
  faf8112  web path: the tessellator's lap timer, the trace span and the fusion cycle timer have no clock on the web
  d3472eb  tsdf: the clock-taking XR helpers are native-only — the browser has no depth camera and no Instant
  4946c9e  sim + render: a repaint is not a world edit — colour and glow restyles never rebake the lightmap
  4317f58  sim + render + chat: walk decks as a surface, the filmed body is no obstruction, the brief never asks
  9791279  render: support rigged models and custom materials across viewers
  3ce2792  sim: use deck geometry for collision and sensing
  c8b79ff  Add portable PBR, rig and soft-body authoring support
  108d423  Add transactional polygon modeling and editable asset documents
  38f4d3b  Refine editable modeling and firm yarn character behavior
  08a2637  sim: add entity-owned lights and vehicle headlights
  00d96a7  render: add clustered lights, local shadows and incremental GI
  cf916af  render: import glTF asset extensions and wire clustered GI
  c6d2cca  model: cut transaction memory and raise capacity limits
  41af47a  raytrace: add a CPU probe-ray BVH budget example
  c963d0a  libs: the game sim splits into makepad-scene and makepad-soft-body; render, model, fab and the asset importer retarget

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:31 +02:00
Admin
81e2c16c30 flow, flowgraph, flow-server
Squash of 55 work commits (Sep 3–9):
  2d4037d  flow: every asset-creator generation kind as a prelude prototype, and every pipeline as a flow template — inventory and gaps alongside
  90dfcb6  flow: the language — splash prelude for nodes, evaluate a flow file into a graph with typed ports and located errors, tool schema from Input/Output, canonical writer
  2a902c6  flow: the recipe prelude loads behind the core one and every pipeline template evaluates, round-trips and is canonical — a test per template
  13f9359  flow: the server host (state thread, two planes over bounded_http, definition routes, events long-poll, lock/listen/token files, watcher) and the client (fail-closed connect, session, subscriber, embed policy); apps/flow-server bin and the flow-ui shell that hosts the server in-process
  2b98a1f  flow: the run engine — instances with an inputs table, one VM per run with live Fn closures, a dataflow scheduler, five executors (chat over the hub session, gen over the fleet, fn, http under an egress policy, ask that parks a run), content-addressed values with a RAM budget and spill
  b3da0c5  flow: ports declare their types — ports: {in: {name: @type}, out: {…}} — so a second image port (mask, last_frame, reference) is as typed as the first; Inpaint, edit references, Music.lyrics, Paint.reference_image, Control.control restored; DREAM closes its loop again; the node catalog walks the prelude
  098ba77  flow: three hostile test batteries — the server routes over real sockets, the language, the engine — with the findings pinned as ignored tests naming the bug
  428d23e  flow: instances, runs and values on the wire — instance CRUD and inputs (answers a parked Ask), runs with cancel, values on the data plane with ETag/Range and PUT upload, run events fanned into the ring by topic, autostart, janitor, bounded shutdown; real seams by default with with_seams for tests
  58f0700  flow: media inputs route by (domain, port) — inpaint takes image and mask as named inputs, control's image is the primary — and every gen node's declared port types are authoritative, including derived Image and Upscale; ocr template typed; empty legacy port arrays warn
  c36f1e7  flow-ui: the AI service bridge — a flows service (authoring + what is running) and one service per definition on the aichat bus, per-turn context, tools answered off worker threads
  a2e0c43  flow: the old pipelines ship as templates — GET /v1/templates lists them with label, brief, inputs and outputs; POST /v1/flows/{name} {template} creates a definition from one
  09e4bf1  flow: what the batteries pinned — 3xx refused, a node object where a port reference is required is an error, undeclared Fn ports refuse edges, parameter errors point at the field, the omitted node is named, allowed HTTP methods listed, a 192 KiB source cap, an unterminated Flow{ is a parse error
  263d6f0  flow-ui: the canvas — node frames with faces evaluated in an isolate per instance, typed port dots, bezier wires, drag-move and drag-connect, palette, inspector, source pane, App view, Running list — integrated with the chat bridge and wired to the run routes
  7b10d61  flow: the hub's models on the wire — GET /v1/models snapshots the discovered fleet (health + model list per node, refreshed on demand, fleet hints for tests) and /v1/nodes carries the model ids per domain
  14b194b  flow: the contact-sheet acceptance and an instance/run/value battery over real sockets — Http read, Fn, two images in parallel, an Ask that parks and resumes the run, a third image, an Http POST of the result
  a40ad74  flow-ui: the empty-state icon is the node's output type — a picture card waits with the picture icon, not the text glyph
  662a121  flow: the gen executor renews the hub lease every two seconds and says bye on shutdown, so fleet boxes stop reaping our jobs; flow-ui's bridge publishes run and instance events as messages on the bus, long runs return early with a subscription, and the AI gets templates, models and create tools
  64f6d41  flow: pin the exact chat hand-off the Llm node makes (system, user turn, model) as a regression test
  2dbd1b7  flow + hub: a flow's LLM turn asks the node not to think and sends no token cap for zero, so a fleet Qwen node answers with a visible paragraph; the empty-state placeholder draws one icon
  df9f449  flow: the style picker lives on the node whose input it sets — the add_style Fn, not the image
  e2a37c9  flow-ui: a format picker with common image sizes and a swap beside width and height, the picture clipped inside the card under the selection outline, a face-declared control mounts as a labelled row on the node it sets with a hint when it binds elsewhere, the Fn face gets a strip for them
  8b05496  flow + flow-ui: width and height snap to the type's step (16 for images, the doc is the authority) in the fields, the inspector, the presets and the evaluator with a warning, so a fleet backend never sees an illegal size; text in cards and the inspector scrolls inside a bounded area that follows streaming until you scroll up
  4aec005  flow-ui: wires route around cards with fillets and a pulse that travels the cable when a value lands; Clear drops an instance's generated state and keeps its inputs, as a route, a toolbar button, a menu entry and a shortcut
  f95e34b  flow-ui: the inspector has one purpose — the selected node's header and note, the settings its card does not show, its connections as chips, its result, the raw face source folded under Advanced; a full-window image viewer with cursor-anchored zoom, pan, fit and 1:1, arrows through the instance's pictures, save and copy digest
  8ff1d1c  flow-ui: wires never pass behind a card — endpoint cards are obstacles except at their own port stub, targets below or below-left route through a clear corridor; the selection outline is a stroke on the card's own rounded body under the port discs; a press on a picture box drags the card and a still click opens the viewer
  b7d95d8  flow-ui: the image viewer opens fitted and centred — the picture syncs after the stage has drawn instead of reading its cleared area as empty; a sampling-mode field replaces the abuse of the image shader's rotation; pan clamps at the picture's edge, 1:1 means one image pixel per device pixel, the checker stays fixed to the screen
  6139a73  flow + flow-ui: a card can face right-to-left — flip: true in the file mirrors its ports; the router is directional; a card auto-flips when that cuts its cable length below 80 percent, a hand flip pins it; the flip animates
  b41fab4  flow-ui: a wire takes the simplest shape the geometry allows — the clear S-curve wins over any corridor route, tangents scale with the hop, level ports get a straight line, and no route has more bends than an obstacle forces
  4781378  flow-ui: the image viewer is modal for input — wheel, pointer and keys stop at it, and the canvas ignores a wheel under any open overlay
  2ed495f  flow-ui: the flip decision scores length, crossings, bends and loops instead of length alone, so a crossed pair unflips; ports and wire midpoints carry direction chevrons that hide below half zoom
  8c40d28  flow-ui: a close button at the top-left of the image viewer, fixed to the screen; the bar and the button fade with the viewer
  4c37048  flow-ui: wires are stable and selectable — a route depends only on its own obstacles and keeps its kind unless a new one is clearly better, with a fixed tie-break; a click within six pixels selects a cable, the inspector shows the connection, Delete removes it
  c90df1f  flow + hub: a node advertises only the models it can admit, the flow picks admitted nodes for the named model, retries on refusal, and names every node's reason when none can take it
  8f23e3b  flow-ui: the port chevron sits between the disc and its label, never inside the disc
  f5ec84e  flow-ui: the port disc's shape carries the flow direction — outputs end in a point, inputs have the matching dimple
  cf3b648  flow-ui: oval port discs with a small input notch, the icon visually centred, and wires that start at the output's tip
  c6890fd  flowgraph: the flow canvas and wire router become libs/flowgraph, a widgets-only crate any UI can host; flow-ui is its first consumer
  27f02f2  flowgraph: the canvas's style table resolves its registered type, so node and port icons draw again
  2a51724  flowgraph + flow-ui: two wire modes never mixed — bezier or routed — and routed wires use narrow gaps
  5863c43  flow-ui: an input card is one text area filling the card, with no name beside it
  30ef925  flow-ui: the image generator card shows only its settings; the picture lives on the Output card it feeds
  d20a208  flowgraph: a routed wire turns right after its port and hugs its own card
  7e397cf  flow + flow-ui: Publish end node writes to the asset library; Assets tab in the Inspector; prompt-to-library template
  c71d639  flow + flow-ui + flowgraph: a stale keep-alive is replayed on a fresh socket; a palette drop rewrites the file text so the new card mounts; the progress bar is centred in the header and inset past the corners
  a777488  flow + flow-ui + flowgraph: the loaded flow is the editable design (Input.value lives in the file); Play clones it into a numbered locked run instance with a Design button back; inspector text rows follow the graph
  767e260  flow + flow-ui: parallel runs — random seeds with a die, a fleet parallelism estimate (generation nodes bound it; the chat box never caps the image fleet), batch routes, the Run dropdown (1 / max · N), Ctrl+Enter queues a run, and the Running panel becomes a Queue of every run with per-row and per-batch cancel and Clear all
  acd2328  flow + flow-ui: a cancelled run clears its cards and bars at once and its live nodes read cancelled; layout edits (move, size, flip) work while a run is shown; the queue row is one centre line — name, strip, state · time, x
  8f51704  flow-ui: a Templates menu on the bar lists every template the server serves; choosing one creates and opens a flow from it
  778dbb8  flow: accept bodyless 204 responses
  b0b0e41  flow-ui: select runs from the whole queue row
  2b4bff9  widgets + flow-ui: menu entries fire again — the bar's Closed action no longer hides the Selected one behind it, and an open menu takes pointer and key input before the panels beneath its popup
  6f2fdfa  flowgraph: a wire between nearly level ports takes the short run instead of a trip around the card (the fillet rule rejected any interior segment under two radii); fewer bends win only among routes of comparable length; six screenshot fixtures and the routing review
  39a1b74  flow-ui: a locked run's pictures and clips still take clicks — the viewer opens from the Output card again; only the inputs stay inert
  83a00d2  flow: archive generated assets and add live source editing
  47a043e  flow: add leased, reroutable fleet job admission for gen nodes

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:31 +02:00
Admin
60615db4ed libs: ai hub/models/cuda, speech, chat_ui
Squash of 54 work commits (Sep 1–12):
  6251f7c  ai-hub: body domain — live pose packets ride the realtime session
  ea50c77  chat_ui: the feed's session gets its profile brief back
  f51b5f3  ai-body: the crate for the native SAM 3D Body port, with its weights reader
  8211ae6  ai-body: the MHR rig and the pose head's parameter decoding, oracle-exact
  9e343a8  ai-body: the DINOv3 ViT-H+/16 backbone, crop and ray conditioning; Metal gains rope-half and affine layer norm
  69d842c  ai-body: the promptable pose decoder and its refinement loop, oracle-matched on Metal
  66e5e2f  ai-hub: SAM 3D Body runs natively — `sam3dbody` on the body domain, oracle-matched end to end
  a634198  ai-hub: the body-native commit carried a peer's in-flight hub hunks; put them back where they were
  9ff44e8  ai-hub: the body-native wiring, this time only the lane's hunks
  6a1c16b  ai-body: third-party notices — what the port is implemented after, and what it is not
  d78411a  ai-body: the per-step work moves to the GPU
  b22259b  ai-body: the context stays on the GPU; only the pose token leaves the loop
  346f31f  ai-body: flash attention for the head-dim-64 blocks
  45b5b98  ai-body: the crop size is a runtime knob, and the loop reports where its time goes
  4be6d19  ai-body: the test modules import the grid constants they still use
  7598346  ai-body: tensor-core GEMMs for the backbone, and the rig's correctives only where they count
  a9ce596  ai-body: the crop warp runs across cores
  8964ba6  ai-body: an FP8 backbone mode, off by default, measured against the oracle
  a2aaa8f  ai-body: the FP8 bias rides a column-broadcast add on the device
  d53c77d  metal: a device-resident ViT stack, and the body backbone rides it
  d006d0a  metal: resident f32 linears keep their weight on the device
  525ba1c  metal: a device-resident two-way decoder layer, and the body decoder rides it
  c9e6d88  ai-body: the hands pass — hand crops, the hand decoder, the hand-mode rig and the wrist fusion
  62dff26  ai-body: the mask prompt — a person's segmentation mask conditions the body pass
  a648cf8  ai-hub: body session options — hands, detect, persons=N
  8c568df  ai-hub: drop the SAM 3D Body reference worker backend
  7ff875a  ai-hub: keep a peer's in-flight beats/notes/local work out of the body commits
  31e5faa  ai-hub: local model runner, licence acknowledgements, a shared install panel; Beat This!, Basic Pitch and the Salamander drum-kit entries
  b94bc58  ai-services: the wire, the app port and the panel state — one conversation, many apps
  2acb798  ai-services: wire v2 — endpoints, receiver-side caps, result disposition
  8ae0ffb  ai-services: the engine core — registry, router and conversation, tested against a scripted model
  2308736  ai-services: the real models behind the engine feature — local through the hub, Claude, and none
  c3f631d  livepipe: one reusable pipe from a camera to a fleet node and back
  ff62db3  ai libs: the runtime env-var cleanup — precision is a per-caller policy, not an environment side channel
  04a94ef  realtime: one service-log line when a live session opens and one when it closes
  0ecb81c  ai models: the model-crates env-var cleanup — 172 research knobs gone, the unset default is the code
  4ca36c1  ai hub + services: the assistant's model comes from wherever it is resident — the fleet chat box, with tools, then the local weights
  432121e  aichat engine + wm: launch, then use — the assistant continues in the same turn once the app it started is on the bus
  7a5bf69  ai-hub registry: the Salamander drumkit samples come from the makepad.nl mirror — the GitHub repo only carries the .sfz files
  102ffc5  ai-services: messages on the bus — a manifest declares topics, the engine subscribes on a tool's behalf or by ToolResult.subscribe, a service publishes Message frames, an idle conversation wakes on a message as an event turn under rate laws; the WM bus forwards the new frames; every app that matches the wire gets its arm
  a837792  hub + flow: a whitespace-only chat completion is retried once and then fails instead of passing as an answer; a flow's model is a fleet model id unless it names a weight file on disk; chat models show under the text domain in /v1/models
  bc6c620  hub + flow: what the chat review found — the in-process route retries an empty completion too, a node says whether its prefill opened thinking so a brief-mode answer is never discarded, a preferred model falls back to normal election when no node has it, discovery keeps looking for the preferred model until patience runs out
  75c3441  hub: the PRO 6000 serves image as well as chat and text
  ad5e98b  hub registry: flux2-dev's VRAM estimate is its measured peak, 30 GB
  c7241e0  hub: a node that evicted every resident releases its cached allocator pool before refusing a load or publishing usable VRAM
  30575f0  flow: route generation by request workload
  1be1e21  ai-hub: gate downloads by disk capacity and recover fleet admission
  df6b394  filesystem_watcher, bounded_http, ai services: live and tool prerequisites
  79ebdb9  ai-hub: add a native Pixal3D image-to-3D backend
  0ba0d74  ai-hub: propagate typed refusals under reject queue policy
  cc6c872  Speed up H3 conditioning and video decoding
  e512059  Fix Qwen vision residency and generated material colors
  2864f68  ai-hub http client: bound every plain TCP connect to 3 s per address
  3d93229  ai: CUDA is a Linux/Windows-only dependency; the hub library defaults to llm + stt

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:31 +02:00
Admin
c4ceb84fd3 asset-ui, asset-server, libs/asset
Squash of 39 work commits (Sep 1–9):
  0d7185e  asset chat: catalog SQL answers in-process, and the game brief stops teaching the y mistake
  c18b7f4  game brief: railways are one call too
  b7fe32c  game brief: a destructible world, path-edited railways, and models that load now
  0e94bba  game brief: rivers, highways, junctions and gates are one call
  c189b03  game brief: agents follow the rules by themselves — you never script a stop
  c86a750  chat: a game session reads its doctrine first and is not the generation assistant
  1ef6f59  game brief: on a diet and world.plan-first
  e18cc69  chat: a tool call the model wrapped inside its own think block still executes
  d7f9813  game brief: to change a map, edit the plan you were given; vegetation is a layer
  876e3c4  game brief: buildings have insides — ask for a program at the door
  47ac158  asset-client: portable primitives — completion transport, cache store, static-site config
  9ae91ac  asset-store: static export contract — versioned static index and a sanitized deterministic export-static tool
  f233756  game brief: races, rings, mocap and biomes are one call each
  942476f  vj: the whole app type-checks for wasm32 — portable hub discovery and typed-unavailable client facades
  2e48c5b  asset-client: the static-store read mode and polled runtime
  e2a262b  asset-store: the portable core — embedded feature, page-store and CAS seams, a synchronous in-process API
  9942e26  asset-store: the embedded module lives in its directory now (the browser-durability landing's deletion)
  670cc76  asset-store: the platform storage API is a hard dependency on the web, where the embedded store is the only store
  6b77318  chat: the in-game builder never asks before building
  9630337  asset-store: export-static selects what the namespace holds and says why when it does not
  0684b41  asset-store: export-static reads every content schema the server serves
  5fdd5d5  vj: on the web the explorer lists the site store's music and a deck loads it with its stems
  40e483f  vj: the web music browse actually fires — proven on the live path
  44b3a7f  dj-pack + vj: every track carries its artist, licence and source, and the DJ UI shows them
  cb75d36  asset client + store: no std clock on the web — the catalog worker no longer dies at start-up
  78a1681  bounded_http: the store's HTTP/1.1 server parser is its own crate — the store keeps only its JSON response shim; flow-server is the second consumer
  285bc98  asset-ui: workers from the runtime pool, the video player owns its frames and audio state
  90c4f59  asset-store: bump the static-export golden snapshot for the new rights fields
  b37b116  media_view: the video, audio, mesh and splat viewers leave asset-ui for one crate; flow-ui shows every media type on the Output card and in the modal viewer
  88048aa  assets: extend authoring pipelines and typed asset search
  0ac0892  Expose bounded modeling tools and reference-image continuations
  36121c3  Keep modeling conversations running until completion or manual stop
  2cea218  asset: centralize library and store path resolution
  72dbc5a  asset-creator: run pipelines through Flow graphs
  0047e77  asset-store: add guarded, alias-scoped atomic publish
  e85e36a  asset-store: refresh model-preview sessions with a lease
  933d885  asset-chat: compact repeated image diagnostics; require 45-degree views
  626e797  asset-importer: gate pack import to desktop Unix; add missing newest field
  f5e136d  asset-importer: shade world previews with vertex colours

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:30 +02:00
Admin
bb5022775e apps: terminal
Squash of 2 work commits (Sep 2–2):
  7f1fefd  terminal + browser: the assistant can read the screen, run a command, read and steer the tabs
  a772ec8  terminal + browser: a warm-pool standby joins the AI bus only once adopted

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:30 +02:00
Admin
0a12d4c976 apps: browser + CEF
Squash of 3 work commits (Sep 2–6):
  5c40761  cef: the build script downloads its CEF distribution itself, on every host, with no shell
  eec516e  cef: a Windows backend — the browser's page renders on Windows
  3dce90e  Propagate browser appearance changes to Chromium page media

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:29 +02:00
Admin
22aa634642 apps: vj
Squash of 34 work commits (Sep 2–9):
  cb97fc5  vj: the loop splat — a song sliced into per-stem, beat-quantized loops on the APC40 grid
  5b309b3  vj: 32-bit usize constants no longer overflow on wasm32
  441d604  vj: dropped or picked audio publishes into the connected store
  6be1118  vj: the whole app on the web — site store for content, embedded store for local imports, native-only seams Unavailable
  aa7345d  vj: the whole app on the web — site store for content, embedded store for local imports, native-only seams Unavailable
  511048c  vj: no filesystem paths on the web startup path — the store is the disk there
  78bfd5c  vj: the web explorer shows the store's tracks — the listing reaches the rows
  cc8ee5c  vj: the web music browse fires when the store is ready, and startup never stalls the UI thread
  53f0e96  vj: the DJ/VJ app exposes itself to the desktop assistant
  ffd62da  dj-pack + vj: the demo cache carries every per-track analysis — slices, beat grid, overviews — and the web app reads instead of computes
  f35dd1d  vj: effect thumbnails actually render and persist on the web
  6fddf9e  vj: loading a track onto a deck fetches and decodes its audio on the web
  5c78ac0  vj: web effect thumbnails show the effect, not a black frame
  21fce80  vj: stale-recipe thumbnails are swept from storage on startup
  afdffd7  vj: clicking an effect tile puts it in a channel on the web vj: clicking an effect tile puts it in a channel on the web
  cc1f256  vj: cached thumbnails appear within a second vj: cached thumbnails appear within a second and the pipeline never hangs
  4f98c73  vj: a deck shows what it is loading — fetch, decode, stems — with real progress vj: a deck shows what it is loading — fetch, decode, stems — with real progress
  66617cd  vj: stem separation is a setting — hub by default, local only on purpose, never auto-started on the operator's machine
  645de42  vj: a deck plays and draws while it decodes, and the stems swap in sample-aligned — one streaming path for native and web
  c3d9314  vj: each deck's transport sits on two larger rows under its mixer — every button visible on both decks
  b0ec2a4  vj: the audio engine owns the mix state — the UI sends commands over a wait-free ring and reads snapshots, no lock on either thread
  acb8689  vj: the loop grid is built and refined on the web too — one pool + channel path for both targets
  707d702  vj: every worker thread comes from the platform spawner — the loop ½ button no longer panics the web app
  71c0e1e  vj: a paused deck is never moved by sync or the loop grid, a loop cell plays exactly its bars with the view held, and the stems banner clears
  35ac364  vj: the catalog runtime pump no longer logs every poll
  5a9fa62  vj: no timed std waits on wasm workers — blocking channels, one tagged deck+background inbox for the stems/lyrics workers, a lost-wakeup fix in the thumb queue, native-only gates on the video decode loops; the DJ web app stops losing a worker at start-up
  32bea89  vj: a playing slice keeps the big waveform of the whole sample — the active slot is an overlay on it (accent band, bracket edges, wrapped playhead, slot number) while the lane's viewport stays fixed; the grid cells are selectors only, their mini waveforms are gone
  dd51a02  vj: a go-to-start transport button per deck — seeks to 0:00 through the normal seek path, keeps the playing state, leaves the loop alone; skip_start.svg icon; decks test
  bf9418e  vj: ironfish / synth rack, program mix, and a clean release check
  28016ca  vj: keep generated clips at the head of the grid
  f45793a  vj: batch live video publishes into grid rows
  75b295c  vj: synchronize decks and queue remote stem processing
  ba662d2  Add responsive DJ and VJ modes with system and black-orange appearances
  a18b2ee  vj: route DREAM runs through the shared Flow engine

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:29 +02:00
Admin
3811545d48 apps: director, studio, scope, aichat
Squash of 8 work commits (Sep 2–10):
  7b9ed2c  aichat: the assistant as an app — the panel owns the engine, the bus client, settings with the local-only lock
  e0c6e74  aichat: the progress bar and system lines use the theme's highlight colour
  8b46ce0  aichat: the composer's hint is a dark grey Ask AI, not the typed colour
  b99a631  toml_parser, rust_tokenizer: rewrite both for the code analyser
  3bbcea2  aichat: add Studio evaluation-feedback widget
  b61845f  studio: Architecture view, the third workspace mode
  d7a76cf  studio: add bounded code context and source APIs
  524142a  Split Studio into makepad director (public) and makepad scope (private)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:29 +02:00
Admin
3b8cd8a69a apps: WM, wm-all, phone apps, files, route, sheets, photos, image-tiles; the mp prefix rename
Squash of 59 work commits (Sep 1–12):
  b0380ba  image-tiles: the picture-wall engine as a library — tape atlases, a baker CLI, and the TileGrid widget
  83d8eac  mpsheets: demo feature and the SheetDocs seam
  daff390  finance: demo feature — generated ledger, SQLite target-gated, import hidden
  13b4c48  mpfiles: demo build — procedural fake filesystem, still-image thumbnails, chat feature
  58e1f72  mpsheets: review fixes for the demo seam
  3203e32  finance: review fixes — id remapping on persist, structural determinism tests, one cfg seam
  0537687  mpfiles: distinct repo-owned pictures for the demo thumbnail pool
  fe920c8  files: review fixes — demo scan exclusions, Zipf sizes, trash root guarded, depth bound, tests
  4144dc4  files: the spy test filesystem implements the clock
  1940f3d  fonts: leave apps/wm untouched — its font-set declaration waits for the aichat lane to land
  b0e34c2  wm: the AI pane and its bus — the aichat child seated in the slot, F10, the os service with the typed open
  de937ac  route: the web build is a one-to-one recompile — native UI, service seams behind it
  2f5a984  files: the file browser on the AI bus — four read-only tools through a correlated, cancellable runner
  2b035ff  route: the navigation session keeps time on the platform clock — the std clock traps on wasm
  1b70899  files: the space view is where a tab starts
  412b3c9  files: the space view rescans through the virtual filesystem — the web has no other disk
  0105fa5  files: the space view opens in 2.5D
  5d007e0  files: an unset projection preference means 2.5D
  6857d86  files: the projected treemap clips boxes at the near plane and keeps the camera above them
  d3cd233  wm: one desk for every target — the host seam, the assistant seated in-process, the web profile, and the assistant up at boot
  6d47c3f  wm: the omarchy themes moved to omacom/omarchy — the importer follows
  82def6b  wm: every app under the desk — the chat keeps the keyboard through an automatic refocus, a pool that gives up, polite closes for browser, sheets and aichat
  6105b61  sheets + files: the assistant reads and writes cells, makes folders, renames and trashes
  82ac768  photos: the picture wall as an app — the SMBC archive on the tile engine, a module from its first line; fabric in the launcher
  190062e  route: the maps app exposes its tool table to the desktop assistant
  3768653  image_tiles: the wall is box-packed — justified rows keep every picture's own shape and fill the width
  3691875  wm: launcher icons — a photo for Photos, a shirt for Fabric, a globe for Route, a play badge for Video, a pulse for the task manager
  8625076  aichat + photos + image_tiles: make me a picture and it lands on the wall; the wall filters as you type, tiles flying to their places
  1dcca41  wm: a theme with no wallpapers fetches them on its own
  ff6cfec  wm: minimise, maximise and close in the bar's top right on Windows and Linux
  62f38a5  route: --hour=N pins the theme hour for harness grabs
  362ac59  wm: the checkout is found from the working directory too, and a sibling binary is an .exe on Windows
  0fdfba7  files: the web's makepad home is a fixed virtual path — std's temp_dir panics there
  daf88e1  route demo: the tiles come from the repacked world archive at its own never-cached path
  15c3b11  ai-services: what the pubsub review found — modules get a subscription seam and a publish sink through both WM paths, lease end flushes unsubscribes to hosted services, the subscription cap counts closing rows, an endpoint's queue is dropped after Unregister, the prompt drops a subscription on final
  d852699  route: no tile-source dropdown — makepad.nl is the tile source through the local range cache; a world.mkmap in the saved maps folder is the only override; the stale preference file is removed on start
  aa271c3  map + route + geodata: the Terrain layer through the archive plane — TerrainSource (hosted .mkmap elevation shards fetched by range through the shared reader, a local MBTiles only as a dev override), the hillshade rendered on the pool's heavy lane with reused TerrainScratch buffers (web capped at 2048×1536 at DPR 1, native DPR-aware), one request in flight; the demo checkbox now renders terrain like native
  dbc7838  route: the maps-folder field and its save button are gone from the settings panel; the maps root is automatic
  a53d9b6  route: no clock-driven night theme — the theme is the user's toggle, remembered in cx.storage, default day (the hour rule fired on the web for the first time once wasm had a clock and darkened the map after 19:00)
  ca92ff9  route: the open sea on the web — the ocean-low/ocean-high overlays are an always-on group in the shared hosted overlay table, read through the archive plane (makepad.nl, cached); native keeps the local ocean files only as a dev override; the native-only OCEAN_MBTILES path is gone
  4cd24c4  route demo: the first location fix flies the map to the user at zoom 14, as native does; Amsterdam stays the default until a fix arrives
  2e869f8  route: the ocean-high overlay comes from the re-sharded archive (58 shards of at most 16 MB, one small leaf directory each) instead of two shards whose directories decoded to 407 MiB
  8021bb0  Add Clock and Weather with shared mobile tile views and persistent alarms
  77b8309  Make application layouts and custom widgets follow desktop and mobile themes
  b357164  Add desktop and mobile compositor shells with OS switching and dock transitions
  11dec2e  Keep hosted app output readers off the shared task pool
  c4002c5  Round complete desktop surfaces and add Windows snap layouts
  a5f8dd8  Frame home photo tiles and clip the picture wall below app controls
  678721d  Give mobile app libraries real search focus and hosted keyboard input
  4794cdf  Keep window title fills opaque at the application surface join
  2cc584e  Match Windows 2000 and NeXTSTEP window chrome to original screenshots
  4a876a8  Give Files navigation and storage tools a clearer hierarchy
  7821c90  Keep prewarmed browsers in the active desktop appearance
  5bfe694  Compact the Files toolbar and repair navigation and selection actions
  44fef36  Preserve Photos subjects and zoom across host view and aspect changes
  ae20efc  Draw larger macOS traffic lights with centered hover symbols
  2bc4d89  wm: the WM is a library plus a desktop binary, Linux controls, hosted tick pacing
  d643eb4  apps: the in-process app wave — mail, notes, calendar, reminders, calculator; clock, weather, finance, photos and route as modules; civil time, read-only sqlite, Linux CEF
  56c1dee  wm: a timer beat missed while the child renders is serviced on its acknowledgement

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:28 +02:00
Admin
4945f1873b platform: rename the CPU simulated-GPU backend from headless to gpusim
Squash of 1 work commits (Sep 12–12):
  e74b919  platform: the CPU simulated-GPU backend is `gpusim` — the word "headless" now means only window-less

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:28 +02:00
Admin
b53ea0b30a platform: macOS display-link, App Nap, waker, IME-adjacent event loop
Squash of 2 work commits (Sep 9–12):
  8d851ef  platform: per-draw alpha blend, macOS waker, texture lifetime, file drop
  cc2fa8f  platform: the Metal display link is gone; the hang line prints its tick deficit; hidden instances refuse App Nap

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:24 +02:00
Admin
2e27ce2ff1 platform: retained draw lists, shared publications and GPU residency (DL-0..DL-5)
Squash of 27 work commits (Sep 3–12):
  aa907ca  platform: a frame trace on both desktop backends, and a window that cannot present stops ticking at 600 Hz
  e3abf4d  map: the hosted-tile cache keeps the same ~2x-visible margin as the local one — a budget pinned at the visible set evicted the trailing edge of every pan on the next frame and refetched it a moment later
  8d542fe  platform: a child pass orphaned by its attaching draw list is no longer painted — the window's gauss_scene pass stayed a live_with_parent child after the map went flat and re-encoded a frozen 400-item list every pan frame with stale geometry ids (new tile meshes × old instance counts, tens of millions of triangles into a texture nobody read); make_child_pass records the recording list + redraw id, stale passes are skipped, a cached View re-attaches on a cache hit; tests for the orphan gate and the pool generation contract
  bb49fe6  map + platform: retained per-tile draw lists — each resident tile owns one DrawList2d per carto pass (fill, casing, stroke, icon, icon-high, shadow) and the label glyph batches are retained the same way, recorded when the bake, LOD ring, fringe/icon gates, flat/tilted or clip change and re-attached otherwise; a pan/zoom/tilt frame pushes this frame's uniforms onto the retained calls (DrawVars::update_uniforms_on_area, resolved slot table) and uploads zero instance bytes; the tilted per-pass depth is a pass_depth uniform; a held list's zbias resolves at entry (zbias_hold in every backend); the shimmer heartbeat patches shiny_time in place without a redraw; a freed/reused sub-list id is skipped by every draw-tree walker and the mask list re-records empty on the flat transition (contract test). Web pan tail 1,098 → 20 MiB/s, flat pan 2.4 → 0.43 MiB/frame; Metal grabs within the run-to-run noise floor
  313265d  Studio code atlas: geometry code views, live filter, 3D size lens, lanes as terminals
  9c4e0cc  Studio code atlas: performance round — retained uploads, worker labels, exact search, no forks
  7235d7e  Studio code atlas: stall fix, GPU working set, lens hard switch, filter masks, parallel index
  56a6da5  platform: per-pass GPU counter timing on Metal; retained publications replace in place
  72e2443  platform: present-path trace (1 Hz cause histograms), bounded drawable wait and retirement on macOS
  1c5d583  platform: bounded retained maintenance on empty paint beats; republish actual backend debt
  edfed73  platform: retained residency high/low water and hysteresis; no distance eviction without pressure
  acab6a0  platform: critical upload class serves present-blocking items first; identical immediate re-records upload nothing
  906c774  platform: uniform_range on DrawVars and patch_retained_uniforms on retained draw lists
  c29031c  platform/draw/widgets: heap-keyed script resources and RecordingBuffer draw items — the files today's commits depend on
  cd0964f  platform headless: homogeneous near-plane clipping before the perspective divide
  61d424d  platform: release retained bindings of released textures so pool evictions complete
  a00287a  platform: texture-tile cache support — per-item instance ranges, painted pass receipts, display-dpi pass uniform, retained render targets, present gate on the drawable pool
  74b63be  platform: retained upload floor reverted, unconfirmed presents counted
  c03fcc5  platform: tile-cache round 3 support — O(1) demand on re-recorded lists, evictions counter, allocated_size, lost-button release, Vec2d::round
  e515d75  platform: shared instance publications — the DL-0/DL-1 contract, additive beside the retained path
  142a337  platform: shared instance publications — close the seven review items (DL-1b)
  6811a19  platform: Debug for SharedInstances, WeakSharedInstances and PublishReceipt
  c061e47  platform: Metal draws are resident by construction — the hole path and its gates are gone (DL-2)
  0bdbb29  platform: drop the unreachable InstancesNotResident present cause
  de3c868  platform: `drawlist` trace names the holder of a stale draw-list id; the per-frame upload line moves to `gpu.upload`
  721c3d8  platform: publication backends — Metal per-publication backings, lease-keyed uniform ring, receipts on every backend, Vulkan draws attached items (DL-3)
  393de54  platform: integrated deletion — the draw-list system is generic again (DL-5)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:23 +02:00
Admin
9580527fdd platform: Linux hosted GPU, Vulkan, OpenGL, Wayland/X11/direct
Squash of 7 work commits (Sep 2–12):
  95fd7d6  platform: a hosted window with a dpi override lays out in its own points and gets the host's pointer remapped
  c2c7f51  platform: a hosted window on Windows draws again — the depth buffer matches the shared target's allocation
  3b1a8c2  platform: an in-app drag works without an OS drag session (web, Linux) — effect tiles drop into the channels on the web platform: an in-app drag works without an OS drag session, so effect tiles drop into the channels on the web
  5ff7397  platform: typed geometry uploads on OpenGL; the typed gate names only the backend that still lacks it
  80bf3d3  platform: Linux hosted GPU transport and routing, hosted tick pacing, WGSL packed vertex members, Vulkan typed geometry
  e2d8a0a  platform: the Linux GL and gpusim cfgs build warning-free again
  c8c757a  vulkan: honor tile draw inputs and retire submitted frames correctly

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:39:56 +02:00
Admin
bb5de43918 script: Splash VM host contract and script_mod apply
Squash of 2 work commits (Sep 2–2):
  a6d0338  widgets: popup menu items run in the popup owner's script VM; app_main! releases its borrow after a trap
  232909d  script: the VM reaches std and its slot through one host — no aliased references

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:39:47 +02:00
Admin
61b0e5e791 platform: network crate, web-server HTTP, native body cap
Squash of 10 work commits (Sep 2–12):
  9633ded  web server: site static serving plus the first nav backends
  ba1010d  asset-client: review fixes — additive web feature, shared transport contract, guarded cache and base URL
  81ef71c  network: native backends honour the response body cap
  4d75e65  web server: hardening after security review — worker-only parsing, FD-relative static opens, strict framing, bounded work, panic recovery, Cloudflare-aware limits
  dfe2087  web-server: O(1) report admission, one connection deadline, shared route sampler, static fallbacks, cache policy
  9c9c72b  web-server: bodies land before workers, client keys normalized, verbs fail closed
  6c32c88  web-server: ETags from size and mtime, deadlines from size, Allow per resource
  7613f3e  web-server: one deadline per response from its size, 404 before 405 for unknown API paths, one Earth radius
  7deb052  web-server: ETags from size and mtime, never from content; upgrades only where a socket route exists
  f407342  libs, network: in-tree shims replace crates.io libc/log in the Wayland and zune crates; deterministic math; the websocket flushes control messages at once

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:39:44 +02:00
Admin
906f94a949 platform: thread pool, clocks, storage, trace, game input
Squash of 10 work commits (Sep 2–12):
  3389475  trace: one switch — MAKEPAD_TRACE=<topics>, trace!(topic, …), and /trace on the bridge
  c55a315  platform: monotonic clock beside the wall clock, trap-safe dispatch, studio worker only with a studio
  89fe453  platform: wheels and flight sticks are game inputs, with an output-report handle
  e5d37e0  platform: a web file picker and file drop that hand apps bytes
  19cf377  platform: the thread runtime contract — spawner, tasks, pools, scheduler, UI waker
  cf2b8ca  platform: no std::time on web — clippy guard and the platform clock everywhere a web build runs
  c527cd8  vj: the web thumbnail pipeline never blocks the main thread
  2a064d3  workspace: add loader, haptics, voice, and runtime fixes
  541c886  platform: name heavy pool jobs over 250 ms; headless Startup sent once per Cx
  d476e52  Fix Linux worker sizing and screen recording defaults

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:39:43 +02:00
Admin
0410ce4ef9 platform: cfg/warning/docs chores inside the render/UI core
Squash of 3 work commits (Sep 2–12):
  debd8c1  rename: the mp prefix goes — apps/wm, files, terminal, browser, task, sheets, image, video, pdf; libs/wm_api and wm_theme
  6dcca00  workspace: no timed std waits on web-reachable paths — the clippy gate covers every web demo's dependency set
  9d8e314  platform: drop two in-band coordination notes that were committed with the tree

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:39:38 +02:00
Admin
573164e40c widgets/map: retained map renderer, typed streams, gestures
Squash of 39 work commits (Sep 2–3):
  ad24534  map: persistent asynchronous tile archive over a completion-based byte source
  5d28ed5  map: tile archive review fixes — legacy MBTiles path restored, decode off the UI thread, retry, cancellation, blob dedup, validation
  a7eae82  map: the tile build keeps time with the platform clock
  d931f9d  map: tile archive third pass — per-pass pruning, validated watcher metadata, shared blob bytes, timer watchdog, atomic cancellation
  4b529a1  map: tile ranges fetch once, centre-out, and never time out while queued
  04a36de  map-build: the bake produces only what the renderer reads; a bad tile is skipped and logged
  383b83e  map: the Amsterdam bake report — the real archive through the real bake path, bytes per stream and milliseconds per tile
  2d4ff16  map: POI symbols and building walls are instances — one shared mesh per symbol slot, one record per footprint edge, extruded and placed in the vertex shader
  72b60b6  map: the memory diet folded in — CPU staging freed after upload, budgets from one platform number, bakes capped by bytes in flight, per-zoom bake profiles, a memory report; instance records count as retained CPU
  a6f495d  map: street trees are instances of one template per tile; the CPU staging of every uploaded stream is dropped; the memory diet keeps its platform half only
  9047600  map: the bake report can dump the raw and decoded tiles it measured
  57995b6  map: uploaded staging is freed on a pool worker — a large free on the UI thread of the threaded web build contends the allocator lock, and a contended lock there is an Atomics.wait the main thread may not make
  02b58fc  map: ground fills on a 20-byte vertex — position, colour, variant + AA coverage, depth ticks; DrawMapFill is the fill path of the map shader
  ef25495  map: building shadows derived at draw time in a screen-space mask pass
  9d664ca  map: roads on a 32-byte vertex
  5b49fee  platform, map: the UI thread never futex-waits on wasm
  99d2180  map: roofs on a 20-byte vertex, contact shadows as instances
  5a5eda2  map: road, fill and roof streams on the typed vertex formats, u16 indices
  f987651  map: the analytic road fringe is baked only when the view is flat
  7e66991  map: finished bakes reach the screen on the next frame
  d8539d8  map: the detail parse and merge stop allocating
  7a2c09a  map: road-union faces on a 16-byte vertex
  a2cbe38  map, platform: the tile budgets follow the platform's one memory number
  fef8058  map: marker stalks and stoplights are instances
  662d141  map: building wall instances on a 20-byte record
  d6d3241  map: round road caps are a fragment SDF, dead cap rows gone
  8ae7c3c  map: every typed stream index stays u16 — streams chunk under 65,536 vertices
  4e61e44  route + map: the desktop app streams the makepad.nl archive through a persistent on-disk range cache, chosen in settings
  a8b0382  map: the water/foliage shimmer read draw_pass.time, which flags every DrawMap* shader as animated and repaints the whole map at display rate forever — a Rust-stamped shiny_time uniform instead; the 20 Hz heartbeat drives it
  0d3a9b8  map: nothing animates at rest — the water/foliage shimmer heartbeat runs only during interaction or a camera animation, plus a 1 s settle tail, then freezes at its last phase; flat and tilted views alike
  1f24428  map bake: a face-band triangle is a face only when all three records pack (a mixed triangle stays on the road path); a panicking pool job no longer takes the worker down — caught, reported once, the tile fails; the bake panic that blacked the native map and killed the web workers
  199be9e  map + route: the six overlays (EV chargers, transit, nature, districts, building age, population) read through the same archive plane as the base tiles — OverlaySource {name, TileSourceConfig}, hosted .mkmap archives on makepad.nl fetched by range through the shared archive reader and disk cache, a local .mbtiles only as a dev override; one layer table (apps/route/src/overlays.rs) for the native and demo builds, the demo checkboxes now set overlays like native
  01e77d9  map: the shimmer clock is one pass-level uniform (draw_pass.shiny_time, a map-owned slot, never draw_pass.time) written once per heartbeat tick instead of patched into every retained draw call's block — the settle tail stops re-uploading 600 uniform blocks per tick
  d641cbf  map: labels no longer vanish — the gesture label budget is charged from the placement loop, not from candidate collection (4-7 ms on the web at z15-16, up to 6000 candidates in space warp), so a place can no longer commit an empty cache; a truncated pass is never a strict cache hit, arms its own settle wake, and the at-rest follow-up chain is capped at 4
  635c3a3  map: ready tiles are inserted at most two per frame (byte budget kept), queued visible ring first then margin ring, the stale tile drawn until its replacement lands — a restyle burst of 4-9 refined tiles no longer stalls a frame for 70-120 ms
  ddc4709  map: touch gestures — one-finger pan and double-tap zoom, two-finger pinch zoom around the midpoint, rotate with a 5° dead zone, and a parallel vertical slide for tilt, one state machine native and web; the web page keeps browser pinch-zoom out (non-passive touch listeners, touch-action none, maximum-scale 1)
  8eaa6ec  map: two-finger tilt follows the phone convention — fingers up tilt into 3D, down flattens
  018ce73  map: labels hold still through a gesture and never pop — the settled placement rides the camera delta while anything moves (pan now part of the motion signature; re-place only beyond the pan/zoom law), every draw uses the rect-centre fold pivot so CPU placement and the GPU warp agree (a fresh place drew about the screen corner: the giant space-warp labels), and a re-place cross-fades: survivors keep their birth, newcomers fade in, dropped labels retire from their own camera over 250 ms
  c79e84e  map: a tilt is two fingers moving together up or down — same vertical direction for both, the pair's stroke within a fifth of vertical; spread and angle no longer matter, so real fingers trigger it

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:39:37 +02:00
Admin
e32b74b988 platform: Web/wasm runtime, WebGL, web audio, browser storage
Squash of 38 work commits (Sep 2–6):
  ed5b2fa  web: wasm32 portability in libs and web startup geometry deferral
  0ccd384  platform web: focus dispatch, window-zero geometry and generation-correct ids after the startup deferral
  a7af3ea  platform + mpfiles: platform clock instead of std::time on the web, unwind-safe event dispatch
  52251b7  platform: a namespaced async key/value storage API on Cx — files natively, IndexedDB on the web
  84b46c1  mbtile reader: file-backed readers are native-only so the map stack builds for wasm
  6b661bf  web: crashes report themselves — panic text, breadcrumbs, memory, workers; a dead instance stops pumping
  372bfd7  asset-store: browser durability — generation extents over cx.storage, chunked CAS, quota and GC
  0f967ce  web path: the stream trace and the sqlite pager never wait on a clock the browser does not have
  4e58ab9  vj: the output window exists only once opened — never on the web
  93232a2  platform: two pool workers on wasm until the allocator is per-thread
  c7fe851  vj: effect thumbnails render, encode and persist in browser storage on the web
  5b33781  platform: thread-caching allocator for the threaded wasm build
  463de64  web: shaders compile per draw list, link in parallel
  e7be0e3  vj + platform: the browser UI thread never waits on a lock, and the web hot paths log only errors and summaries
  7b33f7b  vj + platform: a loaded deck actually plays on the web vj + platform: a loaded deck actually plays on the web
  dc85eb0  web + vj: render-to-texture passes keep their 3D camera on WebGL, thumbnails wait for shader compile, bundled tiles re-ask — effect thumbnails match native
  b39d301  web audio: the worklet links the whole module — every platform import the audio thread does not serve is stubbed, clocks and the UI wake are real
  1e2dcd0  web: a pass without a draw list is skipped instead of taking the app down (F12 layers overlay)
  0ef0311  web audio: a throw inside the worklet's process() is reported with its real text instead of a bare ErrorEvent
  97e0701  vj + platform: the web audio thread never waits on a lock — a loaded deck plays
  58d3fd5  web audio: the output is created inside the first gesture, and a stalled worklet module load is retried on a fresh context
  410acd0  web + vj + route: the console carries failures and one-line summaries, nothing per request, per tile or per hiccup
  a3248d1  web audio: the worklet gets the audio access pointer as its context — the thread-stack call gained a request id and the audio start was still passing the pointer in its place
  9e2d65b  pdf + photos + task + wm + video + automate + widgets: no per-job threads — pool jobs and start-up workers
  639887d  webgl: texture passes get their depth target — the tilted map (and every 3D scene drawn into a texture) was draw-order only: hollow buildings, no roofs, landmarks buried
  2aa0780  webgl: BGRA uploads become RGBA at upload and sample_as_bgra is a plain sample on the web, as on every native backend — the tilt-shift's sharp band showed red/blue-swapped water
  de9aba3  vj + web: the Layer button works on the web — a second Window is a queried capability (OsType::is_single_window; the web creates none and reports it once), so the output becomes an in-page full-canvas layer with browser fullscreen (Esc, the browser's own fullscreen exit, or a double-click leave it); a pass without a draw list settles its dirty flag instead of erroring every frame; exitFullscreen fixed; fullscreenchange feeds the window geometry
  f615054  tweaker + webgl: click-to-climb continues only from the widget the climb started on, so a press on a sibling picks that sibling (the empty draw_bg was a bare View being pinned); the Shader tab says when a layer has no live draw call; the WebGL paint walk no longer resets every draw list's view_transform — the magnified material well drew at the window origin on the web
  0d7ddee  webgl: a uniform block is uploaded only when its generation moved — per draw call (uniforms_gen), per draw list (uniforms_gen, recording_gen), per pass (pass_uniforms_gen) and per shader scope block; the JS side caches (ptr, len, gen) per uniform buffer and re-records/recompiles reset it; direct camera writers (vj effects, render scene, the web flipped copy) bump the pass generation
  e48b056  Revert "webgl: a uniform block is uploaded only when its generation moved — per draw call (uniforms_gen), per draw list (uniforms_gen, recording_gen), per pass (pass_uniforms_gen) and per shader scope block; the JS side caches (ptr, len, gen) per uniform buffer and re-records/recompiles reset it; direct camera writers (vj effects, render scene, the web flipped copy) bump the pass generation"
  b043332  webgl: uniform blocks upload only when their generation moved — one global monotonic counter on Cx hands out every generation (draw call create/dirty/zbias, draw list allocate/transform/re-record, pass allocate/time/dpi/ortho/camera and the web flipped copy, shader scope writes), so a reused pool slot can never match a cached generation; clear_draw_items, pool reuse and VAO recreation reset the caches; the JS caches key on the generation alone. Proven on a local build: the pan screenshot keeps every tile, the settle tail drops 20.5 → 12.8 MiB/s
  3c2530d  web memory diet: the 805 MiB at load was the ocean-high archive's 13 M-entry leaf directory decoded to 407 MiB per lookup round and evicted at once — leaves now parse streaming into a window around the waiting tile ids (LeafParseLimits); a phone policy on the web (deviceMemory, UA, touch + short side) caps wasm at 512 MiB with a 320 MiB budget; archive leaf/range caches, reads and bakes in flight follow the budget; packed tile bytes stay packed until the bake decodes them; terrain scratch sized to the viewport and dropped with the layer; Cx::memory_report by owner. Phone viewport 1334 → 308 MiB after a minute of pans, desktop 1208 → 588
  303458d  webgl: a 2D texture pass builds its camera through set_ortho_matrix like every other backend, so the exploded z-layer view's camera reaches the GPU — the hand-built ortho branch uploaded an identity view and clipped every exploded draw, leaving the tweaker's layers view a bare window on the web; the Y flip for render-to-texture is one helper shared with the keep-camera branch
  1801e38  Harden web renderer and make Route location opt-in
  09fa1f0  Restore WebGL text with complete fallback samplers
  9e61553  Keep Route 3D buildings under bounded web memory pressure
  b005c6e  Preserve complete Route geometry within measured web memory budgets
  0832b35  platform: support float GI targets and retained mesh snapshots

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:39:18 +02:00
Admin
48e7a01ec9 draw: geometry, shaders, WGSL, fonts, turtle/layout
Squash of 10 work commits (Sep 2–11):
  120b7e2  score view: the engraver as a shared library, with drum and pitched score builders
  09b41a8  fonts: FontSet and FontPolicy — one application choice, selected-only loading, a font-assets manifest
  6981e90  fonts: manifest generated from the chains, app-level font assets, a symbol fallback, deprecated i18n aliases
  77d9138  wm: the module contract and the first in-process app — sheets in a tile, in an isolate of its own
  55a3810  platform: typed compact vertex formats and u16 indices
  83a8d4f  fonts: the web demos start with the Latin set — CJK and emoji faces load on the first glyph that needs them
  1980c24  platform: a draw call whose geometry id went stale is skipped, not drawn with whatever mesh now sits in the reused slot — the runaway triangle count that took a web map pan to 1 fps; reported with a power-of-ten backoff, never per frame
  a75fe91  layout: extend turtle sizing and add Grid
  4429551  draw, widgets: text clips by the list clip; GaussChain; map colour roles
  6e02468  draw: restore Cx2d::set_current_pass_dpi_factor (raster density) beside the display-dpi setter

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:39:02 +02:00
Admin
0f410eeb6a platform: video encode/decode, H.264, camera, Apple/Windows media
Squash of 9 work commits (Sep 1–5):
  929f822  video: the single-frame mp4 encode exists on every platform
  294cb9e  video: the single-frame mp4 is written on every platform, not stubbed
  730b5b8  video: the Windows H.264 stream decoder pulls output before it knows the format
  c1febc7  windows h264 stream decoder: low-latency mode, output type before first ProcessOutput, real MF_E codes, trace file
  d6cc49a  windows mft: PROVIDES_SAMPLES is bit 0x100, not bit 0
  dcf7d21  windows h264 decoder: ICodecAPI low-latency, per-pump trace, access-unit dumps, and the stream tests run on Windows
  d1e7a6f  windows h264 decoder: AVLowLatencyMode is a VT_UI4; the round-trip test tolerates the MF encoder's access unit delimiters
  aa816ed  windows h264 decoder: pictures come out one access unit later — rewrite the SPS level so the DPB is one picture deep
  2f44d20  apple: avoid blocking video clocks and release native players once

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:38:49 +02:00
Admin
874374635f widgets: glass, dock, tweaker, themes, code_editor, phone shell
Squash of 36 work commits (Sep 1–12):
  176433a  tweaker: click-again climbs the pick — the container under the children is reachable
  97e9572  speech: one STT/TTS API on every platform, through the ai-hub
  8ea3684  widgets: EventOrder reachable from the DSL; DataGrid hit-tests where it was drawn
  0fd1ea2  route: demo profile — native/demo features, side-panel and provisioning seams, hosted tiles, HTTP nav client
  cf4c84c  keys: F10 is the assistant — the exploded-view debugger moves to Shift+F10, the screen recorder to Ctrl+F10
  126d8c9  route: the demo profile runs in the browser — merged panel draw, platform clock, unavailable backends tolerated
  e21db96  route: demo review fixes — route origin, rain lifecycle, hosted route validation, request context, provisioning
  c24c206  aichat: the Window overlay — F10 in every standalone app, the in-process port, the /ai bridge routes, sheets as the pilot
  dbe43bd  wm + widgets: the AI panel on the left, pushing the body in
  5184340  platform + vj + map + files + widgets + image_tiles: the runtime owns one warm two-lane task pool — jobs never spawn threads
  697215e  route + converse + example-map: every worker comes from the runtime pool or a start-up worker
  862f3bd  asset widgets + chat ui + render: fan-outs and jobs on the runtime pool, the transcript read without a lock on draw
  976ea0e  tweaker: Shift+F10 toggles it on every platform (KeyEvent::is_tweaker_toggle, one call site; the web page swallows exactly Shift+F10 so the browser never sees it); the exploded z-layer view has no keyboard shortcut any more — it is a button in the tweaker
  4e8e4cd  flow-ui: the design pass — menu bar and toolbar with the total run bar, continuous zoom through the draw-list view transform with pointer remapping, dark checker canvas with grid steps, shadowed cards with icon labels and port icons, glowing wires, per-node progress bars, full-bleed image cards, palette cards you drag out, the fab edit controls in the inspector, a template picker behind New, model pickers from the hub; MenuBar widget in the shared crate
  075e1a7  flow-ui: pickers filled from the hub for image and text nodes, popups anchored through the canvas transform, labelled face controls, add_style explains itself, cards resize from a grip with size: vec2 kept in the file, full-bleed pictures, a click anywhere on a card selects it and still reaches the face, no remount on layout-only edits, panels float over the canvas
  a50750f  flow-ui: Flows, Running and Palette as their own rounded panels with splitters, the inspector and source pane likewise, columns resizable; gaussian frame shadows; keys and IME reach the focused face field through the canvas transform
  43302a6  flow-ui: every card owns a draw list and draws in z order, selection brings it to the front; and the design review's findings — every event kind remapped through the camera, run events keyed by run id, no remount mid-run, an input journal that survives a failed PUT, terminal states reconciled, the total bar from the planned node set, isolate ownership on instance change, popups retired before an isolate is freed, the Ask face answers on a button, the menu bar navigates by keyboard, no per-frame allocation in the canvas draw
  4ee4f4c  flow-ui: the resize path sets walks and fits through typed setters, never a script apply from the main VM on an isolate's widget — a failed apply had left a freed script object behind and wedged every frame; a resized card fills its picture box, clips its face and lets the last flexible element take the height
  cff15ac  widgets: a fab number field drops a label that cannot fit instead of crushing it to a dot
  24c927a  flow-ui + widgets: every dropdown is the searchable ComboBox
  a6c5f15  widgets: FabValueInput honours visible, so the seed picker's random mode hides the number field
  1181a3b  flow + flow-ui + widgets: every creator pipeline is a template — 55 templates in six groups (Image, Video, Audio, 3D, Vision & text, Utilities), all evaluated and engine-exercised in tests; the New picker, the flows.templates tool and the palette group the same way; the Templates menu shows them under group headings, and a menu taller than the window scrolls
  f8b9a67  widgets: preserve numeric edit completion and menu focus
  ed5f2e2  Add hotloadable OS themes and preserve widget state across style changes
  f1d3b39  Center resized app recordings on a fixed black canvas
  915fcae  Remove icon rim highlights and align compact home tile contents
  bfa7805  Keep terminal palettes theme-aware and resize above mobile keyboards
  7535ce8  Fix workspace build regressions (#1220)
  2233cbc  Replace legacy Studio with docked and canvas agent workspace
  708aa9f  code_editor: range views, anchors, prepared documents, read-only, tab stops
  0eae276  widgets: capture Studio evaluation feedback and recordings
  487c602  widgets: let Studio pump dock bodies across presentations
  c5adb93  Studio code atlas: settle-line diagnostics, index progress, chrome fades, exact search budget
  ee9ab46  widgets: every screen capture lands in the repo's local/screencap, named by app
  dcc9673  draw, widgets: the phone shell's glass, hosted-view and overlay support, app icons for the new apps
  2fbc679  wm: preserve app caption controls and add Scope to the launcher

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:38:48 +02:00
Kevin Boos
2be77caa39
Fix Escape and Back ownership across widget lifecycles (#1236)
* Event: trace cancel scopes, and gate StackNavigationView's Back on ownership

A scope held by a widget that has stopped being the active thing wedges
Escape and the back gesture for everything behind it, and the only symptom
is that the gesture silently stops working -- which is indistinguishable
from there being nothing to cancel. MAKEPAD_CANCEL_TRACE=1 now logs every
scope begun and ended, and which one each press was stamped to, each named
by the call site that began it.

That location comes from #[track_caller] on both Cx::begin_cancel_scope and
CxCancelScopes::begin: the attribute propagates through the chain, so
Location::caller() names the widget rather than either of makepad's own
frames. No signature changes, no caller passes anything new, and the
existing tests needed no edits. Releases are logged from Drop rather than
end(), so giving a scope up by dropping it -- including a widget being torn
down, the case most likely to leak -- is reported exactly once on either
route.

StackNavigationView called the consuming back_pressed() whenever it was
Active, with no ownership check. A modal or pane opened over a pushed stack
view owns that press, but the view could consume it first and pop: the
wrong thing acts and the owner is starved, on one gesture. It worked only
because children are dispatched before the closure request, which is
precedence by traversal order -- the thing cancel scopes exist to replace.

A pushed view genuinely is what Back should pop when nothing is in front,
so it now holds a scope while Active and acts only when it owns the press.
Its five state writes route through a single set_nav_state that moves the
state and the scope together, acquired at the transition because ownership
is stamped before dispatch. The left_button and mouse-back-button paths
stay ungated: those are unambiguous clicks on this view, matching Modal,
which gates only back_pressed().

* Fix Escape and Back ownership across widget lifecycles

Allow gesture-specific scopes, preserve held Escape ownership across Back and focus changes, and suppress repeated Android Back dispatch without invoking Activity fallback first.

Release popup, modal, drag, and navigation scopes on every exit; support suspended navigation, isolate Pop actions, and finalize wide-window hide animations. Add focused ownership and lifecycle regressions.

Validated with 14 platform cancellation tests, 14 widget cancellation tests, Android Rust and Java checks, and a release modal Escape smoke test.

* Resolve cancel ownership from the active widget hierarchy

Bind widget scopes to their owners and resolve visibility and descendant priority only when Escape or Back begins. Retained inactive pages, collapsed controls, and unfocused windows no longer require application activation callbacks.

Preserve press ownership through repeats and release, suppress scoped or repeated TextInput Escape actions, and remove the StackNavigation cancellation activation API. Cover hierarchy, container, wrapper, focus, and gesture ownership regressions.

* Simplify cancel traversal and remove unsafe root lookup

* Reuse validated widget paths for repeated activity queries

* Remove PR-added cancellation tests and tracing

* Arbitrate the mouse back button with cancel scopes

The mouse's back button is the same navigation gesture as Android Back, but it
never received a cancel owner: handle_event clears press_owner for every event
and only restores it for Escape and BackPressed. owns_cancel was therefore false
for every scope while a MouseUp was delivered, so a widget could not gate that
button on ownership at all. The ones that tried had to fall back on ad-hoc
conditions -- "is my tab the visible one" -- which cannot express the thing that
actually decides it, namely that something else is in front.

Stamp a Back press for Event::MouseUp with the back button: in
resolve_widget_owner so widget-bound scopes are resolved against the hierarchy,
and in handle_event so ownership is settled before dispatch, exactly as for the
gesture itself.

StackNavigationView's mouse-back path is gated on that ownership to match its
back_pressed(). A pane or modal opened over a pushed view now takes the first
click and the view stays put; the second pops it. The left_button path stays
ungated, being an explicit click on the view's own header rather than a gesture
something in front of it could have a better claim to.

* Close a Modal on the mouse's back button

The back button is the desktop equivalent of the back gesture, and is arbitrated
by the same cancel scope, but Modal acted only on Escape, BackPressed, and a
click on its backdrop. A back-click inside the content did nothing at all, and
one outside it closed the modal only incidentally, as a background click.

Gated on ownership like the other two, so a modal opened over another one keeps
its place, and left inside can_dismiss so a non-dismissible modal still ignores
it. This is what lets a full-screen modal's content -- an image viewer, say --
respond to the back button without handling the gesture itself.

* Fold Modal's Escape and mouse-back checks under one ownership test

Same behaviour with one ownership test instead of two, matching how the other
cancel-gesture handlers read. Back consumption stays outside can_dismiss, so a
non-dismissible modal still blocks back-navigation for the widgets behind it.
2026-09-15 06:55:18 +02:00
Kevin Boos
d3dde28f66
Event: let the foreground widget own a cancel gesture (#1232)
* Event: let the foreground widget own a cancel gesture

Several widgets act on Escape, and today more than one can act on a single
press: a modal closes and background dictation stops; a popup closes and the
microphone keeps recording. Dispatch order cannot arbitrate this. Siblings are
handled in reverse declaration order, a parent runs before its children, and
declaration order doubles as the z-order knob, so dispatch order is not
foreground order and cannot be made into it.

Add a stack of cancel scopes on Cx. A widget begins a scope when it becomes the
active thing -- a modal opens, a drag starts, a dictation session begins -- and
ends it when it stops being; the most recently begun live scope is in front. On
a fresh Escape key-down or a back gesture, call_event_handler records which
scope is in front, and that scope owns the whole press, its repeats and its
release included. A widget asks owns_cancel() and acts only if the press is its
own, so exclusivity needs no consumption primitive: only one scope is in front.

A scope that ends part-way through a press does not hand the rest of it to
whatever was behind, so an Escape that stops dictation cannot also close the
modal it was running in front of. Dropping a scope gives it up, so a widget torn
down without a tidy close cannot wedge the key for everything behind it.

Nothing changes for a widget that never begins a scope, so adoption is
incremental.

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

* Widgets: adopt cancel scopes for Escape and the back gesture

Every widget that treats Escape or the back gesture as "cancel" now holds
a CancelScope while it is active, and acts on a press only when it owns
it. Foreground order decides who cancels, not dispatch order: a modal
opened in front of another modal takes the press, and nothing behind it
acts on the same one.

Widgets whose active state can end by several routes reconcile their
scope from that state on each event rather than trusting a single close
path. That also fixes the tweaker holding its drag state open after the
panel is toggled off with F12.

* Fix cancel-scope timing and Back gesture ownership

Acquire drag and color-popup scopes at activation and release them on each exit path, before the next cancel event chooses its owner. Gate Back consumption on scope ownership across modals and their popup/drag controls; non-dismissible foreground modals consume Back without closing.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Admin <info@makepad.nl>
2026-09-12 20:54:19 +02:00
Kevin Boos
a2fdeb325d
Turtle: compute max_height from the height axis, not the width (#1231)
max_height() forwards its walk to next_walk_width(), so a height is
resolved against the horizontal axis: for Size::Fill it takes the space
the width flow rules pick, then subtracts the walk's left and right
margins instead of its top and bottom.

Only Size::Fill diverges, which is why this has gone unseen. Both
routines return v.max(0.0) for Size::Fixed, and Size::Fit never reaches
the call thanks to the early return. DrawText::draw_walk_laidout is
where it would surface: it builds its box from max_width() and
max_height() and passes both to walk_turtle() as Size::Fixed, so text
drawn with a Fill height would take its height from the available width.
2026-09-12 08:38:55 +02:00
Kevin Boos
fc7fa10b31
TextInput: run the scroll bar down the input's edge, not over its text (#1230)
draw_scroll_bar passes the inner rect to ScrollBar, which places the bar
at `view_rect.size.x - bar_size` relative to the turtle's outer origin.
The bar therefore lands a right padding short of the edge, covering the
last few pixels of every wrapped line, and moves further into the text
the wider that padding is.

Draw it along the input's right edge instead, inside the padding. A text
input's visible area is not the area its bar runs along, so ScrollBar
gains draw_scroll_bar_along, which takes the track separately from the
visible size; draw_scroll_bar now delegates to it and every existing
caller keeps its geometry. TextInput also gains scroll_bar_inset, so an
app can keep the bar clear of anything it overlays on the input.

Sync the view total into the bar before the scroll position too:
clamping the position against the previous content height left the
handle a frame behind whenever the text grew and scrolled.
2026-09-12 08:38:43 +02:00
Kevin Boos
2235534781
apple: quieten the log lines a normal run prints (#1228)
Makepad's log! is its own printer rather than `tracing`, so an app cannot
filter it: anything logged this way is printed unconditionally, and an app
developer reading their own output has to scroll past it.

Six calls on always-taken success paths are commented out rather than
deleted, so they are one uncomment away for anyone debugging that area:

  macos_window.rs  titlebar container swapped, once per window
  macos_app.rs     display link pinned, paint pacing, once per window
  macos_app.rs     PIN stats, on every pointer-lock release
  audio_unit.rs    voice input native format and ducking level, on every
                   microphone open, which a dictating app does repeatedly

None of them report a problem or anything the app developer can act on;
they describe internal decisions in makepad's own vocabulary. Error and
warning paths beside them are untouched, as are logs already gated behind
an env var, a feature, or a once-per-process flag.
2026-09-12 08:37:22 +02:00
Kevin Boos
4fc39c49c3
macOS: preserve IME commands and consume composition key releases (#1227)
* macOS: don't deliver IME-consumed keys as KeyDown

`process_ns_event` hands each NSEvent to AppKit via `sendEvent:` before
emitting Makepad's own KeyDown. When an IME has marked (composition)
text, that dispatch lets the IME consume the key: Return/Space commit
the candidate, digits pick one, arrows navigate, Escape discards,
Backspace edits the preedit. A committing key clears the marked text
during dispatch, so the old post-dispatch `hasMarkedText` check (which
only covered Backspace) could not see it, and the Return that merely
committed a pinyin candidate was also delivered as KeyDown(ReturnKey).
TextInput then treated it as a submit.

Snapshot `hasMarkedText` before `sendEvent:` and skip the KeyDown
callback when the IME was composing. This subsumes the Backspace check
and also fixes the case where Backspace deleted the last preedit
character and then fell through to delete committed text.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017HLgZDz8vuuqqMya1nCWKf

* macOS: preserve IME commands and pair consumed key releases

---------

Co-authored-by: ymote <151983+ymote@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-12 08:37:11 +02:00
Kevin Boos
a843d772b5
Html: fix collapsed details with void tags corrupting table layout (#1222)
* Html: keep collapsed details with void tags from swallowing table closures

* Html: harden the parser, the walker and the widget against malformed input

Follows the void-tag fix in the `<details>` skip loop by auditing the rest of
the HTML code for the same class of defect. Robrix renders `formatted_body`
straight from Matrix events, so every parser crash here is reachable from a
message any stranger can send.

Crashes, all reachable from a chat message:

- Numeric character references were parsed as `i64` and cast to `u32`, then
  handed to `char::from_u32(..).unwrap()`. `&#xD800;`, `&#x110000;`, `&#-1;`
  and `&#99999999999999;` aborted the process. They are validated now, and a
  reference that names no scalar value stays literal text.
- An unterminated `&` stayed pending across a tag boundary or a closing
  attribute quote, so a later `;` could fire `decoded.truncate()` and
  retroactively invalidate byte ranges of nodes already emitted —
  `<p>&am<b>p;</b></p>` produced out-of-bounds and mid-character ranges.
  The pending entity is dropped at each of those boundaries.
- An unquoted attribute value beginning with a multi-byte character recorded
  `decoded.len() - 1` as its start, splitting the character.
- `</summary>` with no `<summary>` popped an empty tracker stack, and stray
  `</td>`, `</tr>`, `</li>` and friends reached `cx.end_turtle()` with nothing
  to end. The widget now tracks what it opened and ignores unmatched closes.
- `('A' as u8 + count as u8 - 1)` overflowed on an attacker-controlled `start`
  or `value`; alphabetic list markers now number a..z, aa, ab, ...

Content silently lost or mangled:

- `jump_to_close` counted every open tag toward depth, but a void element
  written without a slash emits no close tag, so it overshot and swallowed the
  rest of the document. `<a href=u>x<br>y</a>` hid everything after the link.
  Only tags with the same id affect depth now, and an element with no close tag
  leaves the walker where it is. `mod_html::find_close_tag` had it too.
- A `<` that cannot start a tag is literal text, the way a browser reads it.
  `5<10 and 6<12` used to parse `<10` as an element and drop the rest.
- `?` mid-tag-name and `<!-->` / `<!--->` ran to end of input.
- `/` in an unquoted value ended it, truncating `href=http://host/path` at the
  first slash; only a slash immediately before `>` closes the tag now.
- `<a href=>text</a>` took `>` as the value's first character, so the tag never
  closed and its content leaked out as text.
- Unquoted values never decoded entities at all, unlike quoted ones.
- `<pre>`/`<code>` whitespace preservation was a single flag that any nested
  tag cancelled, so a syntax-highlighted code block lost its indentation. It is
  a depth counter now.
- HTML's whitespace set is five ASCII characters, not Unicode's;
  `char::is_whitespace` collapsed `&nbsp;` runs and ate the full-width spaces
  in CJK text.
- `find_text` returned the zero-length node the parser emits before every tag,
  so `<a href=x><b>label</b></a>` rendered an empty link. `find_tag_text`
  matched the case-sensitive id and missed any tag carrying an attribute.
- Duplicate `id` attributes bound two elements to one cached sub-widget, so a
  second link could render its own text over the first link's href.
- `<li>a<li>b` and `<td>a<td>b` now implicitly close the previous item, and
  anything a document leaves open is unwound before `TextFlow::end`.

Entity table, which had been generated by folding names case-insensitively:

- 146 names took their case-twin's code point. `&eacute;` rendered `É`,
  `&alpha;` rendered `Α`, `&rarr;` rendered `⇒`, `&copf;` rendered `ℂ`.
- `Igrave`/`Icirc`/`Iuml` had been transcribed as `Lgrave`/`Lcirc`/`Luml`, and
  `Iacute` was missing outright; the invented l-spellings are removed.
- `permil` mapped to the Windows-1252 byte 0x89 rather than U+2030, and an
  empty-string key sat where it belonged, so `&;` decoded to `‰`.
- `tilde`, `lang` and `rang` were wrong.
The ALL-CAPS aliases the table also carries are left as they were.

Also: dropped the `unwrap` in `ElementSelfClose`, memoised table column counts
(quadratic in the number of `<table>` tags), and replaced the backward node
scan on every tag close with the depth counter.

Adds 18 tests covering each of the above. Verified by exhaustive enumeration of
all 12.2M inputs up to length 6 over a markup-heavy alphabet, and 6M randomized
structured cases, both checking that no input panics and that every node's byte
range is ordered, in bounds, on a character boundary, non-overlapping, and
agrees with its `all_ws` flag.

* Html: recover from malformed tags without leaking them into the text

A second pass over the same code, after the first round of fixes changed what
the edge cases look like.

- `</` followed by something that cannot name an element is literal text, the
  rule `<` already follows. `i </3 u` used to emit a close tag named `3` and
  drop the rest of the line.
- Junk inside a tag is discarded up to its `>` rather than resuming text in the
  middle of it, which leaked the tag's own `>` into the output: `a</p x>b` and
  `a<br/x>b` rendered `>b`.
- A custom widget with no close tag of its own is void, so it has no text.
  Reading ahead picked up the *following* sibling's text, and now that
  `jump_to_close` correctly stays put, the main loop drew that text a second
  time: `<img src=x>caption` showed `caption` twice.
- `table_columns_cache` is keyed by node index, so it has to be cleared per
  draw or a recycled widget lays a table out with a previous document's column
  count.
- `<ol start="2147483647">` overflowed the item counter.

* Html: bound jump_to_close's scan and cut the measured hot spots

Benchmarked against the branch point (best-of-7, black_box'd, release).

- `jump_to_close` stops at the first close tag belonging to an enclosing
  element instead of reading to the end of the node vector. It tracks the
  elements opened inside this one so a descendant's close tag is still
  matched correctly, and allocates nothing for the common case of an element
  whose content is plain text.
- Numeric character references were compared against all ~1500 named-entity
  arms before reaching the catch-all. Dispatching on the leading `#` first
  makes them 2.9x faster (991us -> 342us for 3000 references).
- `process_entity` is `#[inline]`; it is called once per character.
- `decoded` is reserved up front, worth ~4% on text-heavy input. `nodes`
  deliberately is not: its length tracks tag count rather than byte count, and
  sizing it from `body.len()` cost a tag-sparse document a large pointless
  allocation — that made the numeric-entity case 3x *slower* before it was
  measured and removed.
- The widget rejects an unmatched close tag from a tally instead of scanning
  the whole open-element stack, which was quadratic on a message combining
  deep nesting with stray close tags.
- `align_keyword_to_x` compares in place rather than lowercasing into a fresh
  String for every aligned cell on every draw.

Tag-heavy parsing is ~2-3% slower than the branch point, which is the standing
cost of the `<pre>` depth tracking, the literal-`<` guard and the entity state
carried across characters. Plain text is ~4% faster.

* Html: follow the tokenizer's recovery rules and resolve element ends at parse time

The parser's states now mirror the WHATWG tokenizer's, so malformed input
produces the tokens a browser would build from it rather than a guess:

- `</` followed by anything but a letter opens a bogus comment that runs to
  the next `>`, `</>` is dropped, and `<?...>` is a bogus comment too. `<`
  or `</` at the very end of input is text.
- `<a/b>` reads as `<a b>`: the slash was not a self-closing marker, so no
  close tag is synthesized. `<x/>` still emits one — the SVG parser is built
  on this walker and XML needs it — which is the one deliberate departure.
- In an unquoted attribute value a `/` is just another character, so
  `href=http://host/path` keeps its path and `<img src=x/>` is `src="x/"`.
- `<!--x--!>` closes a comment, `<!-x>` is a bogus comment, and a tag cut
  off by the end of input is dropped whole.
- Numeric character references follow the tokenizer's end state: zero, a
  surrogate, or anything past U+10FFFF becomes U+FFFD, and the C1 range is
  read as Windows-1252, so `&#151;` is an em dash as legacy content intends.
  Digits are accumulated with saturation so a forty-digit reference lands on
  U+FFFD rather than an error. A decoded space collapses like a literal one.
- `<pre>`/`<code>` are tracked as a stack: a stray `</code>` cannot cancel an
  enclosing `<pre>`, and `</pre>` closes a `<code>` left open inside it.

Every element's end is now resolved once at parse time (`HtmlDoc::closes`),
with the recovery a browser applies: a close tag ends the innermost open
element of its name and everything still open inside it, and a close tag
that matches nothing is ignored. `jump_to_close` and the new
`HtmlWalker::close_index` are lookups, which removes the last quadratic
case — a paragraph of thousands of `<img>` tags cost 3.4ms a frame — and a
stray `</span>` no longer stops a link's `</a>` from being found. The tally
that rejects stray close tags hashes `LiveId` through an identity hasher,
since it is already a 64-bit hash; with SipHash the pass cost 20%.

Widget:
- A `<summary>` left open is closed by `</details>` or the end of the
  document, so its bold run and glyph tracker no longer leak into everything
  drawn after it.
- Implicit closes follow the tree builder's scope rules — `<li>` closes an
  open item up to its list, a cell up to its row, a row with its cells, a
  heading directly following a heading, and any block element an open `<p>`
  — rather than only the innermost element.
- A custom widget's label is all the text inside it, so
  `<a href=x><b>Click</b> me</a>` reads "Click me", and a void one has none.
- Sub-widgets are keyed only by node index. Keying by the `id` attribute let
  a document choose cache keys, and a repeated id bound two links to one
  widget.
- `TrimWhitespaceInText`, `combine_spaces` and `ignore_newlines` are gone:
  all three were written at every site and read at none.
- List markers are borrowed rather than allocated per item per draw, table
  cell alignment compares in place, and link hit-testing no longer clones
  its area list on every event.

Script module: `.html` printed raw hex for every tag and attribute name,
because the document was parsed without interning; it is interned now and
text and attribute values are escaped on the way out, so the output parses
back to the same document. `find_elements` counted every open tag toward
depth, the void-element bug again; it steps by resolved close index.

23 parser tests, exhaustive enumeration of all inputs up to length 6 over a
markup-heavy alphabet, and 6M randomized structured cases, checking that no
input panics and that every node range and close index is consistent.

* Html: resolve every element's end in the tokenizer, and close the review's findings

An adversarial review of the previous commit against the WHATWG tokenizer,
the branch point and a reference parser found the gaps below. All fixed.

The parser now keeps the open-element stack as tags stream past, so each
element's end is resolved in the same pass that tokenizes it — the recovery a
browser's tree builder applies: a close tag ends the innermost open element
of its name and everything still open inside it; a close tag that matches
nothing is ignored; the spec's void elements are whole at their open tag;
what is still open at end of input ends there. `HtmlDoc` records both the
element's own close tag (`close_index`) and where it ends (`end_index`).
That distinction was missing: an element ended by an ancestor looked the
same as a void one, so the script module gave `<li>a<li>b` items empty
ranges — no `.text`, no `.html`, children promoted to siblings — and the
widget dropped the label of a link ended by `</td>`. Both read correctly now.
Because the whitespace-preserving stack is the same stack, a `<pre>` ended
by an enclosing element's close tag stops preserving at that tag, which it
did not before.

Tokenizer fixes, each per the spec's state machine:
- `<!>` and `<!->` are complete bogus comments; they used to swallow text up
  to the next `>`.
- A numeric character reference ends at the first non-digit whether or not
  `;` follows (`&#38 b` reads `& b`), and has no length limit: forty digits
  saturate to U+FFFD as the previous commit claimed but did not do.
- An end tag followed by junk and then end of input is dropped like any
  other tag cut off there; it used to emit its close tag anyway.
- `\r\n` and lone `\r` become `\n`, as the input stream preprocessing says.
- A repeated attribute name on one tag is dropped, so a consumer iterating
  attributes sees the first `data-mx-color` rather than the last.
- A comment is not content, so `a <!-- c --> b` collapses to one space.
- `find_tag_text` answers for the first matching element and does not fall
  through to a later one.

The maps that reject stray close tags and duplicate attributes are keyed
with a per-parse random seed and a multiply-fold hash: the previous identity
hasher let crafted tag names collide and made the pass quadratic, and the
standard SipHash cost a quarter of the parse time.

Widget:
- A `<summary>` is tied to the `<details>` that owns it. A `<details>` opened
  inside a summary was taken for the owner, and `</details>` then popped an
  empty tracker stack — a panic reachable from a chat message.
- `</summary>` and `</details>` end whatever was opened inside them, so an
  `<li>` or a table cell opened in a summary no longer swallows the content
  that follows.
- A collapsed body is skipped to the element's resolved end, so a
  `<details>` ended by an ancestor no longer hides everything after it.
- `count_table_columns` ends the first row at the next `<tr>` as well as
  `</tr>`; a table written without `</tr>` had every column halved.
- The `<p>` rule runs before the heading rule, as the tree builder orders
  them, so `<h1><p>a<h2>` no longer nests the second heading in the first.

Script module: ranges are `(open, end)` with an exclusive end; `parse_query`
no longer panics on `a]b[`.

30 parser tests, exhaustive enumeration of all inputs up to length 6 over a
markup-heavy alphabet, and 6M randomized structured cases, checking every
node range, every `close_index`/`end_index`, nesting consistency, and
determinism.

* Html: build the tree builder's implicit closes into the parser, and end every element where it says

Two verification rounds against the previous commit — a spec-conformance
review, a stack-based reference for element ends, a simulation of the widget's
draw loop over exhaustive and random tag soups, and a round-trip check of the
script module — found the gaps below. All fixed.

The parser now applies the tree builder's implicit closes as it builds the
element stack: a block start tag closes an open `<p>`; a heading closes a
heading that is the current node; `<li>` closes an open item up to its list,
`<dd>`/`<dt>` likewise; a cell closes an open cell up to its row; `<tr>` closes
a row and its cells; a table section closes section, row and cells; a second
`<a>` closes the first. Every consumer therefore sees the tree a browser
builds: `<li>a<li>b` is two items, `<a href=1>x<a href=2>y</a>` two links,
and `<li><a href=u>one<li>two` gives the first link the label "one" rather
than "onetwo". The widget's own copy of these rules is gone; it closes each
element at the index the parser resolved, before that node is handled, and
`<details>`/`<summary>` without a close tag of their own are ended the same
way. Two bugs that fell out of them being special:
- a `<details>` ended by an enclosing close tag stayed on the stack, a later
  `<summary>` bound to it, and the collapse-skip resumed *behind* the walker.
  One stale level drew the text twice; N of them re-walked the document 2^N
  times — a 380-byte message hung the UI. A resume is now never behind the
  walker, and no level is left behind to be claimed.
- a `<summary>` ended by an enclosing close tag never popped its bold run and
  glyph tracker, which leaked into everything drawn after it.

Per-name depth stacks replace the per-name counts, so finding the innermost
open element of a name, or the outermost one above a scope boundary, is a
lookup; scanning the stack made a document of nested `<div>`s quadratic.
A tag with thousands of attributes no longer makes every later tag pay to
clear the attribute-name set. Every nesting shape measured is linear.

Tokenizer and tree builder, per the spec: `</br>` is read as `<br>`, so
`x</br>y` breaks the line; the newline immediately after `<pre>` is not
content; NUL is dropped from text and replaced in attribute values.

Widget: a table whose first row is empty is sized by the first row that has
cells rather than falling back to 100px columns.

Script module: `.html` always writes `=""` and doubles a newline that starts
a `<pre>`, so its output parses back to the same document; `.text` is the
decoded text verbatim, no longer inventing a space inside a word split by a
comment or an inline tag; a query on a selection searches inside it, as
`querySelectorAll` does; descendant steps skip ranges already scanned, which
made `b b` on deeply nested `<b>` quadratic; `parse_query`'s grammar is
documented as implemented.

Deliberately unchanged: the entity table's omissions (`&euro;`, ...), named
references without `;`, and an unquoted attribute value ending in `/` before
`>` (per the tokenizer the slash is part of the value; XML requires quotes).

33 parser tests; exhaustive enumeration of all inputs up to length 6 over a
markup-heavy alphabet and 6M randomized structured cases, checking every node
range, every `close_index`/`end_index`, nesting consistency, attribute
dedupe and determinism.
2026-09-11 10:54:28 +02:00
Kevin Boos
1793f724da
button: honor grab_key_focus on press (#1226)
* cargo-makepad: declare native speech recognition metadata

* cargo-makepad: make Apple plist customization opt-in

* cargo-makepad: use macOS tools for plist overlays

* TextInput: add replace_range for edits that aren't typing

Anything that writes into a text field without being the keyboard —
dictation, autocomplete, a paste button — had only set_text and
restore_state to work with. Both clear the undo history, neither emits
Changed, and both end an IME composition that the platform keyboard still
thinks is in progress, so the next commit from the keyboard lands on text
the widget no longer agrees about.

replace_range(range, text, UndoGroup) goes through the same edit path as
typing: the input filter applies (text it rejects outright is refused
rather than deleting the range), the edit lands in the undo history,
Changed is emitted, and the selection is carried across it (anything that
was inside the range ends up after the replacement). UndoGroup::Extend
joins consecutive external edits into one undo step, so every revision of
a dictated phrase undoes together, while any typing in between splits
them. The IME composition belongs to the keyboard: an edit beside it moves
it and pushes the new text to the platform, and an edit overlapping it is
refused with ReplaceRangeError::Composing. is_composing() lets callers
wait for it to clear, and force_new_edit_group is now reachable from the
ref as well.

Undo and redo now end a composition, since the text the IME was composing
is gone with the rewind; the widget used to keep pointing at it, which
also stalled its IME syncing until focus was lost.

* ios: report the keyboard's marked text as the IME composition

The UITextView bridge forwarded marked text (kana awaiting conversion and
the like) to the widget as plain text with no composition range, so
TextInput never knew the keyboard was mid-composition on iOS:
is_composing() stayed false, replace_range's Composing refusal never
engaged, and the widget's next push did a whole-text setText: that
detached the keyboard's composition from the buffer.

forward_state_to_makepad now reads markedTextRange and carries it through
full_state_sync, so the widget records it exactly as it does for Android.
When the widget pushes text while it still holds a composition (an edit
beside it through replace_range), set_ime_text writes the text around the
composition and marks the composed part again with setMarkedText: instead
of committing it.

* macos: complete the handshake with a development launcher

`cargo run` starts a bare executable, which macOS gives no bundle identity.
Microphone, speech and location prompts are then attributed to the terminal
or editor that spawned it, and denied outright when that process has no
matching usage description, so a permission-using app cannot be developed
with plain `cargo run` at all. The way around it is a cargo runner that
launches a real .app through LaunchServices.

Three things are then lost, because LaunchServices forks the process itself
and starts it in `/`: the runner never learns the app's pid, so it has
nothing to forward a Ctrl-C to; it cannot pass on the terminal's working
directory; and it never sees the app's exit code, so `cargo run` always
reports success. All three are only knowable in-process.

The macOS event loop now reports them through the directory named by
MAKEPAD_DEV_LAUNCH_DIR, adopting MAKEPAD_DEV_WORKING_DIR before any
resource is loaded. Apps launched any other way see neither variable and
do nothing, so this replaces the same handshake being hand-written in every
app's main() that wants to develop against a permission-gated API.

* button: honour grab_key_focus on press

Button's FingerDown handling took keyboard focus twice: once guarded by
grab_key_focus, and again unconditionally at the end of the arm. That
second call is `self.set_key_focus(cx)`, which resolves through
WidgetNode to `cx.set_key_focus(self.area())` — and the derived `area()`
comes from the `#[redraw] draw_bg` field, so it is exactly the call the
guard above it wraps.

The upshot was that `grab_key_focus: false` did nothing on press, and a
button that deliberately opts out of focus still pulled it away from
whatever held it. Robrix's dictation hit this: tapping the microphone took
focus off the composer, hiding the caret the transcript is inserted at and
dismissing the soft keyboard on mobile, so it had to hand focus back by
hand afterwards.

Dropping the unconditional call leaves behaviour identical wherever the
flag is true, which is its default and every use in this repository.
2026-09-11 10:54:15 +02:00
Kevin Boos
4afa30f052
macos: complete the handshake with a development launcher (#1225)
* cargo-makepad: declare native speech recognition metadata

* cargo-makepad: make Apple plist customization opt-in

* cargo-makepad: use macOS tools for plist overlays

* TextInput: add replace_range for edits that aren't typing

Anything that writes into a text field without being the keyboard —
dictation, autocomplete, a paste button — had only set_text and
restore_state to work with. Both clear the undo history, neither emits
Changed, and both end an IME composition that the platform keyboard still
thinks is in progress, so the next commit from the keyboard lands on text
the widget no longer agrees about.

replace_range(range, text, UndoGroup) goes through the same edit path as
typing: the input filter applies (text it rejects outright is refused
rather than deleting the range), the edit lands in the undo history,
Changed is emitted, and the selection is carried across it (anything that
was inside the range ends up after the replacement). UndoGroup::Extend
joins consecutive external edits into one undo step, so every revision of
a dictated phrase undoes together, while any typing in between splits
them. The IME composition belongs to the keyboard: an edit beside it moves
it and pushes the new text to the platform, and an edit overlapping it is
refused with ReplaceRangeError::Composing. is_composing() lets callers
wait for it to clear, and force_new_edit_group is now reachable from the
ref as well.

Undo and redo now end a composition, since the text the IME was composing
is gone with the rewind; the widget used to keep pointing at it, which
also stalled its IME syncing until focus was lost.

* ios: report the keyboard's marked text as the IME composition

The UITextView bridge forwarded marked text (kana awaiting conversion and
the like) to the widget as plain text with no composition range, so
TextInput never knew the keyboard was mid-composition on iOS:
is_composing() stayed false, replace_range's Composing refusal never
engaged, and the widget's next push did a whole-text setText: that
detached the keyboard's composition from the buffer.

forward_state_to_makepad now reads markedTextRange and carries it through
full_state_sync, so the widget records it exactly as it does for Android.
When the widget pushes text while it still holds a composition (an edit
beside it through replace_range), set_ime_text writes the text around the
composition and marks the composed part again with setMarkedText: instead
of committing it.

* macos: complete the handshake with a development launcher

`cargo run` starts a bare executable, which macOS gives no bundle identity.
Microphone, speech and location prompts are then attributed to the terminal
or editor that spawned it, and denied outright when that process has no
matching usage description, so a permission-using app cannot be developed
with plain `cargo run` at all. The way around it is a cargo runner that
launches a real .app through LaunchServices.

Three things are then lost, because LaunchServices forks the process itself
and starts it in `/`: the runner never learns the app's pid, so it has
nothing to forward a Ctrl-C to; it cannot pass on the terminal's working
directory; and it never sees the app's exit code, so `cargo run` always
reports success. All three are only knowable in-process.

The macOS event loop now reports them through the directory named by
MAKEPAD_DEV_LAUNCH_DIR, adopting MAKEPAD_DEV_WORKING_DIR before any
resource is loaded. Apps launched any other way see neither variable and
do nothing, so this replaces the same handshake being hand-written in every
app's main() that wants to develop against a permission-gated API.
2026-09-11 10:54:04 +02:00
Kevin Boos
a80e26bba5
TextInput: add replace_range for edits that aren't typing (#1224)
* cargo-makepad: declare native speech recognition metadata

* cargo-makepad: make Apple plist customization opt-in

* cargo-makepad: use macOS tools for plist overlays

* TextInput: add replace_range for edits that aren't typing

Anything that writes into a text field without being the keyboard —
dictation, autocomplete, a paste button — had only set_text and
restore_state to work with. Both clear the undo history, neither emits
Changed, and both end an IME composition that the platform keyboard still
thinks is in progress, so the next commit from the keyboard lands on text
the widget no longer agrees about.

replace_range(range, text, UndoGroup) goes through the same edit path as
typing: the input filter applies (text it rejects outright is refused
rather than deleting the range), the edit lands in the undo history,
Changed is emitted, and the selection is carried across it (anything that
was inside the range ends up after the replacement). UndoGroup::Extend
joins consecutive external edits into one undo step, so every revision of
a dictated phrase undoes together, while any typing in between splits
them. The IME composition belongs to the keyboard: an edit beside it moves
it and pushes the new text to the platform, and an edit overlapping it is
refused with ReplaceRangeError::Composing. is_composing() lets callers
wait for it to clear, and force_new_edit_group is now reachable from the
ref as well.

Undo and redo now end a composition, since the text the IME was composing
is gone with the rewind; the widget used to keep pointing at it, which
also stalled its IME syncing until focus was lost.

* ios: report the keyboard's marked text as the IME composition

The UITextView bridge forwarded marked text (kana awaiting conversion and
the like) to the widget as plain text with no composition range, so
TextInput never knew the keyboard was mid-composition on iOS:
is_composing() stayed false, replace_range's Composing refusal never
engaged, and the widget's next push did a whole-text setText: that
detached the keyboard's composition from the buffer.

forward_state_to_makepad now reads markedTextRange and carries it through
full_state_sync, so the widget records it exactly as it does for Android.
When the widget pushes text while it still holds a composition (an edit
beside it through replace_range), set_ime_text writes the text around the
composition and marks the composed part again with setMarkedText: instead
of committing it.
2026-09-11 10:53:53 +02:00
ymote
5a86431bdb
macOS: don't deliver IME-consumed keys as KeyDown (#1223)
`process_ns_event` hands each NSEvent to AppKit via `sendEvent:` before
emitting Makepad's own KeyDown. When an IME has marked (composition)
text, that dispatch lets the IME consume the key: Return/Space commit
the candidate, digits pick one, arrows navigate, Escape discards,
Backspace edits the preedit. A committing key clears the marked text
during dispatch, so the old post-dispatch `hasMarkedText` check (which
only covered Backspace) could not see it, and the Return that merely
committed a pinyin candidate was also delivered as KeyDown(ReturnKey).
TextInput then treated it as a submit.

Snapshot `hasMarkedText` before `sendEvent:` and skip the KeyDown
callback when the IME was composing. This subsumes the Backspace check
and also fixes the case where Backspace deleted the last preedit
character and then fell through to delete committed text.


Claude-Session: https://claude.ai/code/session_017HLgZDz8vuuqqMya1nCWKf

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 08:19:40 +02:00
66cc4f15f5 fix(widgets): restore enter_isolate/leave_isolate dropped by upstream merge
The fe5b75d92 merge took upstream's widget_async.rs and dropped the
wm isolate-entry API (IsolateEntry, enter_isolate, leave_isolate)
while lib.rs and apps/flow-ui + apps/wm still use it, breaking
makepad-widgets. Restored verbatim from 77d91385f; structs unchanged
so the code applies as-is. makepad-widgets, makepad-app-flow-ui and
makepad-wm all check clean.
2026-09-10 19:01:52 +03:00