* Introduce knowledge of device screen bounds/cutous/"safe inset areas"
Tested working on iOS, implemented for Android but not yet tested.
The approach may need to be improved, because it currently restricts
the whole app window to being fully within the safe areas.
We may not necessarily want that, or if we do, then we probably also
need to support setting the base color of the reserved system areas
(beneath the app bounds and above in the notification bar area).
* Use metal scissor rect to prevent SVGs/icons from being mis-drawn in safe areas
This prevents anything from being accidentally drawn in the safe
inset areas when the pass clear_color is transparent. Of course,
we can still draw the pass clear_color in those areas.
* Workaround: apply a scissor rect within safe inset area
Only apply it to clip any DrawSvg/DrawVector-specific draw calls
within the safe inset area.
This is unfortunately still just a hacky solution, because if we
actually do want to draw svg/vectors within that safe inset area,
then we won't be able to.
* Properly fix gpu artifacts when rendering SVGs
The `DrawSvg` vertex shader had a GPU fringe expansion pass designed for `fill_gpu()` mode, where fringe vertices encode per-vertex normals in the `v` and `stroke_dist` fields. SVG rendering used `fill_gpu()`, which produces **coincident-vertex fringe triangles** (body and outer fringe at the same CPU position, expanded on the GPU). These zero-area triangles caused **Metal GPU rasterization artifacts** — stray fragments appearing at unexpected screen positions.
**`draw/src/svg/render.rs`** — Switch SVG fill from `fill_gpu()` to `fill()`. Pre-computed fringe produces vertices at physically different positions (no coincident vertices).
**`draw/src/shader/draw_svg.rs`** — Remove the GPU fringe expansion code from the vertex shader. With pre-computed fringe, the `v` and `stroke_dist` fields are constants (`1.0` and `0.0`), not per-vertex normals. The expansion code was misinterpreting `v=1.0` as a horizontal normal, corrupting vertex positions.
**`libs/apple_sys/src/lib.rs`** — Added `MTLScissorRect` struct (unused now but available for future use).
**`src/home/rooms_sidebar.rs`** — Changed shadow offset from `vec2(1.0, 0.0)` to `vec2(0.0, 10.0)` so the `RoundedShadowView` shadow only draws below the header, eliminating the gray line at the top of the screen (issue 1).
* Expose safe area inset padding to app, don't forcibly apply it to root window
* Fixed safe area insets padding, with support for rotation
We now make these values available to the app dev (see below)
instead of forcibly inserting them as padding on all root windows.
This will allow each app to choose how and when they want to apply said pad values
(or if they want to at all) in an easy way, both at the Splash level
or more dynamically/programmatically at the Rust level.
Required quite a few changes to how things work in the iOS platform plumbing,
also described below in the generated summary:
On iOS and Android, Makepad apps render content behind device cutouts (Dynamic Island, camera notch), home indicators, and rounded screen corners because the framework has no awareness of safe area insets.
Added platform-level safe area inset querying on iOS and Android, exposed the values through both the Splash DSL (`mod.widgets.SAFE_INSET_PAD_*`) and Rust (`cx.display_context.safe_area_insets`), and ensured they update correctly on device rotation.
**New types:**
- `UIEdgeInsets` struct in `libs/apple_sys` for Objective-C interop
- `SafeAreaInsets` struct in `platform/src/event/window.rs` (top/right/bottom/left in logical points)
- Added `safe_area_insets` field to `WindowGeom` and `DisplayContext`
**iOS (`platform/src/os/apple/ios/`):**
- Query `[UIView safeAreaInsets]` from the MTKView in `check_window_geom()`
- Added `viewSafeAreaInsetsDidChange` callback on `MakepadViewController` to detect inset changes on rotation
- Populate `display_context` before `Event::Startup` so values are available during app script initialization
- Fixed MTKView setup: removed redundant `addSubview:` (conflicted with `setRootViewController:`) and added autoresizing mask — both required for safe area propagation on rotation
**Android (`platform/src/os/linux/android/`):**
- Added `SafeAreaInsets` variant to `FromJavaMessage` and corresponding JNI function
- Java side (`ResizingLayout.onApplyWindowInsets`): queries `WindowInsets.Type.systemBars() | displayCutout()` and sends insets to Rust (converted from px to dp)
- Added `safe_area_insets` field to `CxOs`, populated on `SafeAreaInsets` message and included in `WindowGeom` construction
- Added `surfaceOnSafeAreaInsets` native method to `MakepadNative.java`
**Splash DSL variables (`widgets/src/lib.rs`):**
- `mod.widgets.SAFE_INSET_PAD_TOP`
- `mod.widgets.SAFE_INSET_PAD_BOTTOM`
- `mod.widgets.SAFE_INSET_PAD_LEFT`
- `mod.widgets.SAFE_INSET_PAD_RIGHT`
- Values read from `display_context` at widget module initialization (during `Event::Startup`)
- Updated on the script heap via `Cx::update_safe_inset_script_values()` on `WindowGeomChange`
**Rotation support:**
- Added `pending_script_reapply` flag on `Cx` — set when safe area insets change, checked at the end of the platform event loop iteration
- Fires a deferred `LiveEdit` event to re-evaluate and re-apply all Splash widget definitions with updated inset values
- Implemented in both iOS and Android event loops
**StackNavigationView fix (`widgets/src/stack_navigation.rs`):**
- Full-screen stack views now position at `max(safe_area_insets.top, parent_rect.pos.y)` instead of hardcoded `y: 0`, respecting both mobile safe areas and desktop title bars
**All other platforms:**
- Added `..Default::default()` to all `WindowGeom` constructors (macOS, Windows, Linux X11/Wayland/Direct, web, tvOS, OpenHarmony) so the new `safe_area_insets` field defaults to zeros
* Additional opptimizations for windows shader compilation
`hlsl_compile_shaders` was called unconditionally after every draw event, even on
frames where no new shaders needed compilation. Added an early return:
```rust
if self.draw_shaders.compile_set.is_empty() {
return;
}
```
The loop previously collected `compile_set` into a temporary `Vec` before
iterating, in order to release the borrow on `compile_set` so the loop body
could mutate other `draw_shaders` fields. This caused a heap allocation and a
full copy of all indices on every compilation batch.
`std::mem::take` atomically replaces `compile_set` with an empty `BTreeSet` and
returns ownership of the original — no intermediate allocation, no copy, and no
separate `.clear()` needed at the end:
```rust
let compile_set = std::mem::take(&mut self.draw_shaders.compile_set);
for draw_shader_id in compile_set { ... }
// no .clear() needed
```
Previously `shader_cache_dir()` was an inner function called inside
`CxOsDrawShader::new`, meaning it ran once **per shader** on every compilation.
Each call performs two syscalls: `env::var("LOCALAPPDATA")` and
`fs::create_dir_all`. With N shaders compiling on first launch, this was 2N
unnecessary syscalls.
`shader_cache_dir()` is now a module-level function called **once** before the
loop in `hlsl_compile_shaders`, and the resulting `Option<&Path>` is passed into
`new` as a parameter.
The HLSL source already lives in `cx_shader.mapping.code`. The previous code
cloned it into an owned `String` before passing it to `new`, even though all
downstream uses (hashing, `D3DCompile`, cache I/O, error printing) only need a
`&str`. Changed the parameter type to `&str` and restructured the loop body into
a block scope so the immutable borrow on `cx_shader` ends before the mutable
reborrow — eliminating the clone entirely.
`CxOsDrawShader::new` already took `&UniformBufferBindings` by reference. The
clone existed only because the immutable borrow on `cx_shader` had to be released
before the mutable reborrow. The same block-scope restructuring from fix#4
resolves this: `&cx_shader.mapping.uniform_buffer_bindings` is now passed
directly.
This field was written once on construction and **never read** — the only reader
was the O(n) deduplication scan removed in the previous round of fixes. It held
a full copy of each shader's HLSL source for the entire lifetime of the
application. At tens of KB per shader and dozens of shaders, this was megabytes
of permanently retained dead storage. The field is gone.
* More shader optimizations on windows
Properly get the Local AppData directory instead of using the
env var %LOCALAPPDATA, which may not always be there.
Now we do it with `SHGetKnownFolderPath(FOLDERID_LocalAppData)`,
which is canonically correct.
We also cache the directory path itself.
* ft makepad_test
* Improve run handling, manifest parsing, and stdout newline
Replace dynamic free-port lookup with an ephemeral localhost SocketAddr in test runtime and remove the unused find_free_listen_address helper. Ensure headless stdout messages end with a newline. Simplify send_to_app error handling and add a test that queued bootstrap messages are delivered once an app socket connects. Substantially enhance process_manager: unify cargo flag parsing, parse Cargo.toml to determine package/bin targets, resolve the correct binary name for direct stdio runs, and build the cargo/build+exec script from the resolved args. Add unit tests for manifest parsing and script generation and adjust related call sites.
* test harness
* Preserve test attrs; return Vec for gateway binds
In the test macro (libs/makepad_test/macros/src/lib.rs) preserve wrapper-only attributes (ignore and should_panic) on the generated wrapper test while removing them from the inner function. Added Attribute import, is_wrapper_only_test_attr helper, adjusted attribute filtering and emission, and added unit tests to verify attribute placement and expansion.
In the hub (studio/hub/src/hub.rs) change gateway_bind_candidates to return a Vec<SocketAddr> instead of an iterator and special-case ephemeral port 0 to preserve ephemeral binding; otherwise collect the range of candidate ports into a Vec. Added tests to validate candidate behavior. Also minor formatting/whitespace tweaks and a small IPv6 formatting adjustment.
* Add visible Studio mode and remote client
Enable running UI tests visibly through a running Makepad Studio. Adds a new makepad-network dependency and studio_remote client (libs/makepad_test/src/studio_remote.rs) and integrates it into the runtime via a TestConnection enum. Introduces visible-mode tooling: env vars (MAKEPAD_TEST_VISIBLE, MAKEPAD_TEST_STUDIO, MAKEPAD_TEST_STUDIO_MOUNT, MAKEPAD_TEST_STARTUP_DELAY_MS, MAKEPAD_TEST_ACTION_DELAY_MS, MAKEPAD_TEST_KEEP_OPEN_MS), pacing/delays after actions, and pause-before-shutdown. Splits startup into start_headless_app/start_visible_app, clears existing visible builds before launching, and updates tests, docs (GUIDE.md, README.md), and selector/runtime minor cleanups/formatting.
* Add animated sidebar toggle and splitter APIs
Add an animated, persistent sidebar toggle and supporting splitter APIs/UI.
- New icon resource: studio/desktop/resources/icons/icon_sidebar_toggle.svg
- UI: replace hidden caption bar with a visible caption that includes a sidebar toggle button (CaptionSidebarToggle), layout adjustments and related controls.
- App behavior: add SidebarAnimation struct and App fields to track animation state and next-frame. Handle button actions, next-frame stepping, and window drag queries to avoid initiating window drag over the toggle.
- Add App methods to query/set the workspace root splitter position, start/step sidebar animations, toggle sidebar (remember/restore width), and sync tab-bar visibility for mounts.
- Persist mount sidebar restore width by adding sidebar_restore_width to MountState and initializing default.
- Make save_state pub(super) so App can save when animation finishes.
- Widgets: expose splitter position and set_splitter_align on Dock and Splitter to allow programmatic width changes and redrawing.
These changes improve UX by providing a smooth animated sidebar hide/show, remembering user width per mount, and ensuring the dock UI updates correctly during mount/tab changes.
* Persist sidebar_restore_width in mount state
Add an Option<f64> sidebar_restore_width to PersistedMountStateRon and wire it through loading and collection so the sidebar width is saved and restored. Include tests to verify a round-trip serialization/deserialization preserves the value and that missing legacy data defaults to None for backward compatibility.
* Sync run preview splitter state
Hide and restore the Run preview column based on whether run tabs exist. Added run_panel_split_restore to AppData to remember the last editor_split ratio per mount, plus a helper (run_preview_splitter_is_collapsed) and a new method sync_run_preview_splitter that collapses the preview when there are no runs and restores the previous ratio when runs reappear (defaults to Weighted(0.62)). Called sync_run_preview_splitter from relevant places: after creating/ensuring run tabs, when clearing build tabs, when closing run tabs, and at startup to initialize each mount. Also added /.cocoindex_code/ to .gitignore.
* Add bottom panel toggle with animation
Introduce a bottom panel toggle UI and animation support. Adds a new icon resource and CaptionPanelToggle button in the caption bar, moves/adjusts caption layout, and wires the button to toggle the bottom panel. Adds a TerminalShellPane, bottom_terminal_tab, and integrates bottom_panel_tabs into the workspace layout. Implements BottomPanelAnimation, animation helpers (panel_animation_progress, start/step logic), workspace splitter height getters/setters, and toggle/select helpers (toggle_bottom_panel, select_bottom_terminal_panel). Persists mount bottom_panel_restore_height in MountState and persisted state with tests updated. Also updates drag/tab behavior to account for the new bottom terminal tab and routes animation next-frame events.
* Refactor reflow_resize and add wrap test
Rewrite reflow_resize to simplify reflow logic and improve handling of growing/shrinking rows and scrollback. The change always captures the old grid state, builds logical (re-wrapped) scrollback lines, and then branches on whether the new height is greater, less, or equal to the old height. On growth it may pull rows from the logical scrollback when the terminal was full, preserve content when a custom scroll region is present, and correctly update cursor, high-water, saved cursor and bottom_trimmed_rows. On shrink it chooses how many bottom rows to push into scrollback based on cursor position, trimmed rows, and content below the cursor, and trims scrollback to max_scrollback. Also adjust cursor bounds and pending_wrap handling. Add a test (visible_wrapped_prompt_rows_do_not_reflow_on_width_growth) to ensure visible wrapped prompt rows do not get reflowed when the width increases.
* Fix windows build: add missing constants in vendored windows-rs bindings
* fix more build errors and warnings in windows-rs vendored copy
* Fix SVG parsing and vector drawing
* Fix erroneous unzip glob pattern that doesn't work on normal linux
* try another approach: unzip everything, cp needed dirs
Mat4f::mul(a, b) was computing b*a due to transposed summation
indices in the multiplication loop. All call sites (glTF TRS
composition, view-projection, MVP chains, scene hierarchy) are
written expecting standard a*b order, and transform_vec4 uses
standard column-major M*v convention.
Fix: swap the operand bindings so the existing index pattern
produces the correct a*b result.
Add regression test mat4_mul_order that verifies:
- Scale(2)*Translate(5,7) yields tx=10 (scaled translation)
- transform_vec4 on result*(1,1,0,1) yields (12,16)
Co-authored-by: ant <ant@offline.click>
* regenerate windows-rs to export missing functions
* fix compilation errors on windows
* import WS_EX_TOOLWINDOW from windows-rs instead of customizing the constant
---------
Co-authored-by: jasonqiu <jasonqiuchen@outlook.com>
* Add wasm server_manager and ownership guard
Introduce a new wasm server_manager module that implements WasmServerOwnershipGuard to manage per-workspace lock files, PID/port ownership, and safe replacement of stale or live servers. Integrate the guard into compile::run by preparing and activating the guard before starting the HTTP server; start_wasm_server now accepts the guard, returns a Result, checks bind success, activates the guard, and propagates thread join errors. The server manager includes platform-specific PID handling, port-probing, lock read/write/remove helpers, and unit tests exercising startup scenarios and lock lifecycle. Also add mod declaration and apply minor formatting/refactor cleanups across compile.rs (error formatting, whitespace, and logging improvements).
* Add startup mutex and port occupant diagnostics
Rename server_manager into top-level module and add startup mutex handling and improved diagnostics. Introduces atomic lock writes (write_file_atomically), a StartupMutexGuard with configurable timeouts/polling, and logic to detect/recover stale startup locks to prevent concurrent wasm run startups. Extends ServerManagerProbes with describe_port_occupant and implements platform-specific detection (lsof on Unix, netstat/tasklist on Windows) to provide clearer errors when ports are occupied. Updates imports (main.rs, compile.rs, mod.rs) and adds unit tests covering startup lock behavior and unknown-occupant error text.
* Improve hot-reload logging and delivery
Add clearer logging and error handling for wasm hot-reload events. Introduces hot_reload_display_name to extract a user-friendly file name, logs when a hotreload is detected, and checks tx.send result to avoid panics when the watcher channel is closed. broadcast_hot_reload_event now logs whether events were skipped (no /$watch clients) or sent and includes the delivered client count with proper pluralization.
* Check lock PID owns port and add startup timestamp
Require that a lock's PID both be alive and actually own the server port to be considered a live lock (classify_lock_state now takes listen_addr). Add parsing of a started_at timestamp in startup locks and use now_unix_millis to age out stale startup locks even if the PID is alive. Extend ServerManagerProbes with now_unix_millis and pid_owns_port, implement port_occupant_info and PortOccupant to centralize occupant detection/refinement for unix and windows, and refactor describe_port_occupant to use it. Update tests and MockProbes to exercise PID ownership checks and startup-lock aging; add tests for PID reuse (treated as stale if it doesn't own the port) and for startup lock aging.
* Hot reload: dedupe sites, add logging
Avoid duplicate ScriptMod sites during hot reload and improve diagnostics. collect_compiled_sites_for_file now tracks seen ScriptModKey values to prevent pushing duplicates. handle_cx_live_edit counts processed files and logs how many overrides were applied and from how many changed files. forward_hot_reload_fs_event logs each detected file and reports if the watcher channel is closed. Minor import cleanup and a helper hot_reload_display_name were added for nicer log output.
* shared core for reload
* Warn and fallback to unminified JS on write error
When writing the minified JS file fails, log a warning and fall back to copying the original JS file instead of returning an error. This avoids failing the build on IO/write errors (e.g. permissions or disk issues) while preserving the previous behavior of using the unminified copy when reading fails. No change to successful minification path.
* wip
* Handle wasm data segments and manage brotli artifacts
Add full support for parsing, encoding and rewriting Wasm data segments: introduce WasmDataSegmentKind (Active/Passive), helpers to encode varints and const i32 exprs, and functions to rewrite the data section or replace a section payload. Update wasm_split_data_segments to preserve passive segments and data.count (section 12), only extract active segments into a separate split blob, and adjust segment counting and tests accordingly.
Bump brotli dependency to 8.0 in tools, and add remove_brotli_artifact to remove leftover .br files when brotli compression is disabled. Call this cleanup in cargo_makepad build/copy paths and when removing split data, and add .bin MIME mapping and print mapping for .bin in the server output. Tests updated to cover passive segments and data_count preservation.
* Support split data v2 and wasm rebuild
Add support for a new split data format (version 2) and the ability to rebuild a WASM module with its data section. wasm_bridge.js: parse v1/v2 split blobs, return {version, segments}, add varint encode/decode helpers, encode split-data section payloads, implement rebuild_split_wasm, fetch-and-instantiate logic to fetch both wasm and split blobs and handle v1 preloaded splits or rebuild for v2. wasm_strip.rs: bump split data version to 2, include segment kind and memory_index in encoded split data, preserve passive segments, update encoding/decoding and tests, and return the updated segment count. tools/cargo_makepad/src/wasm/compile.rs: separate target spec directories for threaded vs single builds (threads/single).
* basic splitting
* Instantiate secondary Wasm module in threads
Store and expose a compiled secondary WebAssembly module from the primary module, and ensure worker contexts (Web Worker and AudioWorklet) instantiate that secondary module before running thread entrypoints. Changes: save _secondary_module on primary_wasm in wasm_bridge, include secondary_module in WasmWebBrowser info, and add async instantiate_secondary logic + awaits in audio_worklet and web_worker so the secondary module is instantiated with {env, primary: primary_wasm.exports} prior to initializing stack/TLS or starting execution. This ensures the secondary module can import primary exports in threaded contexts and prevents race conditions by waiting for instantiation to complete.
* more cleanups
* more cleanup
* wip
* remove double wasm pump
* cleanup
* Cold-first auto wasm split with fallback
Add a cold-only function-splitting mode and automatic fallback to preserve startup-safe behavior. Introduces wasm_split_functions_cold() and a split_auto flag to run a cold-first pass that moves defer-safe cold functions to a secondary wasm; if no useful cold candidates are found the build falls back to the normal startup-path function split so the app still gets a secondary payload.
Changes include: update to CLI help text, new split_auto handling in WasmConfig, AutoSplitOutcome variants, compile logic to prefer cold-only splits then fall back to the regular split, adjusted logging for automatic mode, and README wording clarifications. Files touched: README.md, libs/wasm_strip/src/wasm_strip.rs, tools/cargo_makepad/src/main.rs, tools/cargo_makepad/src/wasm/compile.rs, tools/cargo_makepad/src/wasm/mod.rs.
* Tune brotli compression parameters
Adjust brotli settings in tools/cargo_makepad/src/wasm/compile.rs: increase buffer size from 4KB to 64KB, lower quality from 12 to 11, and raise window size from 22 to 24. This balances throughput and compression ratio for larger wasm artifacts—bigger buffer and window can improve compression effectiveness while slightly reducing CPU cost by lowering quality.
* Add optional wasm-opt optimization flag
Introduce an optional --wasm-opt option that runs Binaryen's wasm-opt -Os on built wasm when available. Adds a wasm_opt flag to WasmConfig (default false), parses the CLI option, and integrates a try_wasm_opt helper that writes a temp wasm, invokes wasm-opt, reads back the optimized output and prints size/reporting while gracefully falling back on errors. The wasm-opt step runs before the existing split/strip pipeline. Also updates CLI help text, minor Cargo.toml formatting changes, and removes a vendor UPSTREAM.md reference.
* update readme
* Shorten split exports to $s/$p and use base62
Rename split-related exports/imports to shorter identifiers and compress numeric indices using base62. Changes: export/table slot prefix changed from "__mp_split_table"/"__mp_split_slot_*" to "$s" and the primary import namespace from "primary" to "$p" across wasm_bridge, audio_worklet, and web_worker. Introduces encode_base62 in wasm_strip to emit $f/$t/$m/$g names with base62-encoded indices, and updates primary/secondary module generation and tests accordingly. Also includes minor JS formatting/whitespace cleanups and small safety/compatibility tweaks during WebAssembly instantiation.
* Preload WASM modules and set cache headers
Inject modulepreload links into generated HTML (conditionally including wasm_bridge and bindgen when bindgen is enabled, otherwise preloading web_gl only) to improve module loading. Also change served asset Cache-Control from max-age=0 to max-age=86400 (1 day) for both brotli-compressed and uncompressed responses to enable client caching and reduce repeated fetches.
* Minify JS when copying before brotli
Add a lightweight JS minifier and apply it in cp_brotli for .js files. Introduces minify_js which strips line/block comments, collapses unnecessary whitespace, preserves strings/escaped characters and simple regex literals (heuristic), and removes empty lines. cp_brotli now attempts to read and minify .js input and write the minified output to the destination (falling back to cp on read error), then continues to optionally brotli-compress the result. This reduces payload size prior to compression with a small, simple minification step.
* Format web.js and disable XR capability checks
Apply consistent JS formatting (spacing, brace placement, object literal spacing, and minor whitespace cleanups) across platform/src/os/web/web.js. Replace the previous XR capability detection with a no-op (query_xr_capabilities now returns Promise.all([])) and remove the await call in load_deps so XR checks are not performed during startup. Also includes minor non-functional tweaks (timers, audio worklet messaging formatting, fetch call spacing, and various input/keyboard handler cleanups). Overall changes are primarily stylistic with the notable behavior change of disabling XR capability probing.
* Add WASM no-cache headers; remove web video arms
Set Cache-Control to "no-store, must-revalidate" (plus Pragma/Expires) for .wasm responses while keeping max-age=86400 for other assets; wire these into the HTTP response headers. Also remove the explicit VideoSource::InMemory and VideoSource::Filesystem match arms from the web platform code (they previously logged errors and emitted VideoDecodingError events).