* Fix rendering, gradient, sampling, etc issues on older GPUs
* Fix bug in `box_y` sdf function, which caused gradients to be split
into two bands incorrectly. Mostly a problem on lower-res screens.
* Use per-texture filtering instead of GL sampler objects on Linux,
especially for Mesa drivers that ignore min filter samplers.
This should help prevent blocky/pixellated things like emoji/avatars
* Improve rendering sharpness on low-DPI screens (icons, emoji, images)
On 1.0-DPI screens, emoji/SVG-icons/avatars were minified without
adequate sampling and SVG AA was sub-pixel, producing blocky/aliased
output. This reworks each path and adds optional full-window SSAA.
SVG icons (device-aware AA + round caps):
- draw_svg.rs/draw_vector.rs/render.rs: size the fill & stroke AA
fringe and the curve-flatten tolerance in DEVICE pixels (≈constant
regardless of icon size), so edges resolve via the analytic
d/fwidth coverage and curves stay smooth at any scale; re-tessellate
on scale change.
- triangulate.rs: thread the flatten tolerance through path fill/stroke.
- tessellate.rs: emit round caps as a solid disc (u=0.5) instead of a
radial fade that collapsed to a square at small sizes.
- widgets/icon.rs: don't clip the Icon to its Fit bounds, so round
caps that extend past the box render fully instead of being sheared.
Emoji:
- glyph_raster_image.rs/rasterizer.rs: rasterize color emoji near the
on-screen size with an alpha-weighted box downscale (geometric-mean
scale factor) instead of the font's native PNG strike.
Images / avatars (mipmaps):
- image_cache.rs/texture.rs/draw_list.rs/lib.rs: optionally emit a CPU
mipmap chain (VecMipBGRAu8_32) for non-animated images so minified
avatars/thumbnails sample cleanly. Env-gated MAKEPAD_IMAGE_MIPMAPS;
default on for GL on Linux.
- metal.rs: real per-level mip upload. d3d11.rs/vulkan.rs/web_gl.rs:
safe single-level fallback (no crash; real mips TODO).
Full-window supersampling (optional):
- window.rs: render the whole UI into an offscreen target at
MAKEPAD_SUPERSAMPLE× device resolution and downscale-resolve into the
window. Default 2×, env-tunable, 1× disables. Modeled on the existing
GaussStack render-to-texture path.
* don't unconditionally enable supersampling SSAA of 2x by default
it's too expensive and too slow for most older devices
* cleanup, reduce comment verbosity
* improve SVG anti aliasing
* Windows: fix laggy/juddery scroll performance
- Pace the render loop to the display refresh using a DXGI frame-latency
waitable object and present with vsync, replacing the free-spinning,
uncapped Poll loop that caused uneven scroll cadence.
- Coalesce consecutive WM_MOUSEMOVE messages and paint once per loop pass
to stop the judder when moving the mouse during fling deceleration.
- Cache get_dpi_factor() and the WM_NCHITTEST WindowDragQuery result to
avoid per-mouse-move GetDeviceCaps syscalls and widget-tree hit-tests.
- Throttle XInput/DirectInput polling of empty/disconnected controller
slots, which was stalling the UI thread.
- Rework the momentum fling to a native exponential model with a
frame-interval EMA, and stop the tail auto-scroll from fighting an
active fling/drag.
- D3D11: update the glyph atlas and image textures in place via
UpdateSubresource instead of recreating them on every change, and
spread D3D11 shader-object creation across frames.
- Slug atlas: only force a full re-layout on a width change; append rows
on height growth.
* Windows: correctness fixes from review (off the scroll hot path)
- d3d11: close the DXGI frame-latency waitable HANDLE in Drop (it was
leaked once per main-window lifecycle and the field comment was wrong);
keep popup swap chains at frame-latency 1; track the waitable-swapchain
flag for ResizeBuffers instead of inferring it from the handle; present
without the vsync interval during a live resize.
- windows.rs: poll game input on the idle signal tick so a gamepad button
can be serviced while the app is otherwise idle.
- win32_window / window: invalidate the WM_NCHITTEST / WindowDragQuery
caches on window move and on a caption relayout, with a generation
counter guarding against a reentrant invalidation being clobbered.
- windows_game_input: detect a controller already plugged in at launch via
a one-shot full scan on the first poll, probe slot 0 (Player 1) first,
and offset the DirectInput enumeration so it never stacks with the
XInput probe.
- comment/doc corrections.
* Image cache: accept any Arc<D: AsRef<[u8]> + ?Sized> for async image data
The load_image_from_data_async family required Arc<Vec<u8>>, forcing callers
that already hold the bytes as Arc<[u8]> (e.g. a content-addressed media cache)
to copy the whole buffer via .to_vec() just to satisfy the type. Generalize the
data parameter to Arc<D> where D: AsRef<[u8]> + ?Sized, so those callers can pass
their existing Arc by refcount-clone with no byte copy. The decode path only ever
borrowed the bytes (&[u8]), so this is purely a signature relaxation; existing
Arc<Vec<u8>> callers are unaffected (D = Vec<u8>).
* Linux: GL glyph-atlas in-place texture update + X11/Wayland mouse-move coalescing
* Scroll: unified fling model + native trackpad momentum deceleration
Share one kinetic-scroll model between PortalList and ScrollBar (and thus
ScrollXView/ScrollYView/ScrollXYView) via a new widgets/src/scroll_motion.rs:
- Touch-drag flicks use an iOS-style exponential self-decay, frame-rate
independent via a per-frame integrator with dt smoothing.
- Trackpad scrolling applies the OS momentum directly while fast (responsive,
full native speed), then hands off to a gentler self-decaying tail once it
slows past a threshold, so the deceleration is longer and smoother than the
OS's short, choppy tail. Handoff is seeded at the current speed for a
continuous transition; the seed is clamped against degenerate event timing.
- Add ScrollPhase to scroll events, mapped from NSEventPhase/momentumPhase on
macOS and wl_pointer AxisStop on Wayland; None elsewhere (wheels/X11/Windows
behave as before). MAKEPAD_RAW_TRACKPAD_MOMENTUM=1 bypasses the smoothed tail.
- A press catches an in-progress fling (stops the scroll, consumes the press so
it doesn't also activate a child), matching iOS/Android/macOS.
* Shader codegen: prefix Metal/WGSL locals to avoid reserved-word collisions
The Metal/WGSL backends emitted user-declared shader locals verbatim, so a
local named after a reserved type keyword (e.g. `half`) produced invalid
shader source and failed to compile at runtime. Prefix them with `l_` like the
HLSL/GLSL backends already do.
* TextFlow: don't panic on unbalanced HTML close tags
end_code/end_quote unwrapped the area stack, so a stray `</pre>` or
`</blockquote>` in untrusted content (e.g. a chat message) panicked. Return
early instead, and drop a vestigial per-list-item area-stack push that leaked
an entry and could hand a stray close tag the wrong block's area.
* Scroll: expose fling decel, handoff threshold, & tail-decel as `#[live]` fields
This allows app devs to override the scroll feel per-widget in the DSL,
or globally by overriding the base widget's defaults — verified that a DSL
override takes effect.
* Windows: fix frame pacing, paste crash, and wheel input backlog
- Wait on the frame-latency waitable right before each window's vsync
present instead of on every Paint, so input no longer stalls behind
waits that have no matching present. Drain leftover credits after a
live resize.
- Pasting when the clipboard has no text no longer panics.
- The poll loop now handles up to 32 messages (2 ms) per frame and
merges consecutive mouse-wheel messages, so fast wheels can't build a
backlog that keeps scrolling after the gesture ends. Sleep 1 ms when a
frame presents nothing so animation polling doesn't spin a core.
* Image: don't build unused mip chains; fix stale images in recycled widgets
- Only build the CPU mip chain when a backend actually uploads it
(Metal, behind its env var), and build it on the decode thread instead
of the UI thread. Linux GL still gets its mipmaps via glGenerateMipmap
and now retains less CPU memory per image.
- Recycled Image widgets no longer show the previous item's image or
apply an old decode result. Placeholder textures set via set_texture
(like blurhashes) stay visible while the real image decodes, and a
failed load clears the widget instead of leaving old content up.
* Widgets: avoid needless caption redraws; cheaper PortalList height tracking
- Label::set_text does nothing when the text hasn't changed, and the
window caption title is only synced when it actually changes, so mouse
moves and animation ticks no longer redraw the whole window every
event. The caption centering padding requests its own redraw now.
- PortalList records item heights only when new or changed, and only
re-applies the default height after it drifts by half a pixel, so big
lists don't walk every unmeasured item on every scroll frame.
* Text: cache layouts of long texts; stop cloning glyph outlines every frame
- The layout cache now accepts texts of any length (long messages and
code blocks used to re-layout on every scroll frame). It is a real LRU
with a byte budget on top of the entry cap, and texts drawn in the
current frame are never evicted, so one heavy frame can't thrash the
cache into a permanent miss cycle. The shaper cache is LRU now too.
- Glyph outlines are shared via Rc, so drawing a cached glyph no longer
copies its command list, and outline complexity is computed once when
the outline is built instead of every frame.
* Html: fix stale links/spans in recycled widgets and <details> renumbering
- set_text only rebuilds when the content actually changed, so a
recycled link can't open the previous message's URL, and re-setting
identical content keeps the user's <details> open/closed state.
- Custom widgets and <details> are keyed by their node index instead of
a visit-order counter, so toggling a collapsed <details> can't
renumber the widgets after it and rebind them to the wrong nodes.
item_with_scope also recreates its widget when the template changes.
* Linux: fixed-distance wheel scrolling; Wayland frame-callback pacing
- Wheel scrolling moves a fixed 60 px per detent on X11 and Wayland
instead of a timing-based guess that flipped between 12 px and 240 px
depending on how events batched. Wayland reads real detent counts via
AxisValue120 (wl_seat v9, with AxisDiscrete as the older fallback) and
maps keymaps MAP_PRIVATE as v7+ requires. Touchpads are unchanged.
- Wayland frames are paced with wl_surface frame callbacks and swap
interval 0, so redrawing a hidden or minimized window can no longer
hang the whole app inside eglSwapBuffers (compositors withhold frame
callbacks for hidden windows). Windows with a callback in flight skip
presenting and stay dirty; X11 keeps vsync exactly as before.
* Text: bigger layout cache budget, reclaimed at the end of each frame
A maximal ~60 KB message lays out to roughly 4 MB of glyphs, so the 4 MB
budget couldn't hold even one alongside a normal screen. Raise it to
16 MB, and run eviction at the end of every frame so memory over the
budget is freed one frame after its content leaves the screen, instead
of lingering until some later layout happens to insert a new entry.
* Fix oversized uniform slices: the array lengths were in bytes, not f32 elements
* Wayland: flush buffered mouse motion before scroll events, and drop motion for closed windows
* GL: fall back to non-mipmapped filtering when glGenerateMipmap fails on strict GLES3 drivers
* Text: bucket emoji raster scales so zooming reuses atlas slots instead of re-decoding every step
* Image: add has_content() and record texture provenance on cache-hit loads too
* Scroll: native trackpad momentum, Chrome-model bounce; presses catch motion, never click children
* Dock: redraw the newly selected tab immediately when the active tab is closed
* Html: standard link colors with pressed precedence; skip re-parsing unchanged text; color setters
* Image: don't redraw on cache-hit loads of the already-bound image (per-draw reloaders looped forever)
* Scroll: log macOS momentum-end phase bits to check Cancelled (touch-cut) vs Ended (natural fade)
* Scroll: momentum state machine; flicks survive pagination; edge sentinels & once-per-frame actions
* Scroll: remove the MAKEPAD_SCROLL_DEBUG diagnostics
* Scroll: time-based fling velocity window; pointer fan-out guard; parked flings survive pagination
* PortalList: optional reached-start/end margins (Some(0) default); repositioning re-announces the edges
* Linux/Wayland: honor UI zoom across window resizes; scale caption bar with zoom
Keep the wayland-side window geom in native units so the zoomed dpi isn't
read back as "native" on the next Configure — UI zoom no longer resets or
flickers on maximize/tile. Also let the caption bar height scale with the
zoom (pin to native only on macOS, where the buttons are OS traffic lights).
* Windows: D3D11 fixes for drawlist mgmt
trying to help with the `new_batch` bugs
* draw_list.rs — `set_zbias` now returns whether it changed
* d3d11.rs — uploads draw_call_uniforms on the given condition:
`uniforms_dirty || zbias_changed || buffer.is_none()`
and the zbias advance is hoisted above the early-continues
* Scroll: hard flicks carry farther and boost on re-flick; iOS-style touch rubber band with per-edge bounce gating; Android fling spline behind a flag
* Splitter: redraw both pane subtrees on drag; cached views keep fresh fixed sizes in the dirty check
* iOS: re-deliver window-geometry changes dropped by re-entrant UIKit callbacks; Init only ever from the first draw
* final cleanup fixes. Ensure all examples, experiments, `studio` all work
* Image: skip re-parsing an SVG that is already shown, keyed on the caller's shared bytes
* CachedView: fix upside-down offscreen texture on GL/GL-ES
GL/GL-ES store offscreen FBOs bottom-up, unlike Metal/D3D/WGSL.
The DSL->script-shader migration switched the CachedView composite to plain
.sample() (non-flipping sample2d on GL), so cached views rendered
vertically mirrored on GL/GL-ES; Metal was fine on macOS.
Add a `sample_rt` script sampler (emits the V-flipping sample2d_rt on
GLSL, plain no-flip sample elsewhere) and use it in the CachedView and
CachedRoundedView composites.
* Fix `AdaptiveView::redraw()` to actually do something
* Simplify tooltip logic, fix positioning to respect safe inset areas
And other misc positioning/formatting fixes, like wrapping
after a hard line break, as well as ensuring that the callout
arrow thing itself is centered (to the fullest extent possible)
w.r.t. the rest of the tooltip body.
Basically, now it looks good again.
* Fix CheckBox/Toggle `set_active` to animate like others
All other widgets allow you to pass an `animate` arg when setting
them as active, except CheckBox (and by proxy, its wrapper Toggle).
This is necessary for proper non-animated thigns like restoring the
state of a toggle from persistent storage, or other similar examples
where you don't really want the animation to occur (because that'll
look like the user did it accidentally or some kind of phantom movement).
* Introduce UI constants and tweak studio layout
Consolidate and adjust studio UI sizing, spacing and styling across desktop widgets.
Key changes:
- studio/desktop/src/app_ui.rs: Add STUDIO_HEADER_HEIGHT and StudioDock; use constant for various header/caption heights; reorganize PaneToolbar into grouped Views, adjust spacing/margins, refine caption label styling, and apply custom draw_text/draw_bg for tabs.
- studio/desktop/src/desktop_file_tree.rs: Add STUDIO_FILE_TREE_ROW_HEIGHT and STUDIO_FILE_TREE_NODE_HEIGHT; switch hardcoded row/node heights to constants, adjust padding and pass node_height to FileTree; update DesktopFileTree::ROW_HEIGHT.
- studio/desktop/src/desktop_log_view.rs: Increase LogEmptyItem height, adjust padding/alignment and label text style; update DesktopLogView::EMPTY_ROW_HEIGHT.
Why: unify header/row sizing via constants, improve spacing and visual consistency, and centralize small styling tweaks for easier future adjustments.
* Add TerminalCloseableTab and TerminalAddTab
Define two new tab templates in app_ui.rs: TerminalCloseableTab (closeable terminal tab style with custom close button sizing and colors) and TerminalAddTab (compact "+" add-tab style with centered text and custom background/hover colors). Wire them into the StudioDock templates and switch the terminal_add DockTab to use TerminalAddTab. Update app_backend.rs to insert terminal tabs using TerminalCloseableTab instead of the generic CloseableTab.
* Standardize UI border colors and add draw_bg style
Replace explicit border color values with theme.color_u_hidden for border_color and border_color_2, and set active borders to a subtle bg tint (theme.color_bg_app * 0.92) to reduce visual prominence. Add a new draw_bg style block (with colors and the same hidden/active border settings) and apply the same border adjustments to related UI elements to unify border appearance across the app UI.
* Refactor and restyle app UI components
Add new reusable styles and refine layout/spacing in app_ui.rs. Introduces LogToolbarToggle and SidebarFilterInput styles and applies them to the log toolbar and file tree filter respectively, tightening visual consistency. Adds StudioTerminalView with padding and uses it in TerminalPane. Adjusts several sizes and spacings (log filter width 232 -> 216, clear button 24 -> 20, spacing 6 -> 4, adds 10px spacer View), and updates paddings for buttons. Refactors caption toggles into a shared CaptionChromeToggle (size/icon_walk/border_radius/colors tweaked) and defines CaptionSidebarToggle/CaptionPanelToggle as specializations that set their SVG icons. These changes are primarily visual/layout polish and consolidation of repeated style definitions for maintainability.
* Adjust close button size, margin and colors
Refine the close button styling: add a close_button block and standardize its size to 11x11, update margins (left: 1.0, right: 7.0) and unify draw_button colors/hover/active states to updated hex values. These tweaks align spacing and visual states across tab elements for a cleaner, more consistent UI.
* Adjust app background and empty text colors
Tweak background state multipliers for theme.color_bg_app to slightly brighter values (color: 0.82, hover: 0.88, focus: 0.92, down: 0.85, empty: 0.82 vs previous 0.78/0.86/0.9/0.82/0.78). Also switch empty text colors from theme.color_label_outer_off to theme.color_label_inner_inactive (hover matches), leaving color_empty_focus as theme.color_label_outer. These changes improve contrast and visual consistency for empty and interactive app UI states.
* 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.