Commit graph

1896 commits

Author SHA1 Message Date
Kayshen-X fc58a4601d chore: bump jian submodule (node promotion for batch_design) 2026-07-02 21:21:07 +08:00
Kayshen-X a7d73ebb62 feat(ai): pencil-style agentic design tool-loop, multi-chat tabs, #27 panel restyle
Built-in design generation now runs as an agentic MCP tool-loop (reusing the
agent-rs BuiltInProvider), gated behind OPENPENCIL_DESIGN_AGENT_LOOP / the
Settings experimental toggle; the orchestrator stays the default.

- design-agent system prompt + in-process design toolset (parity-locked with
  the MCP surface) + flag-gated Intent::Design routing
- spawn_agents execution as sequential sub-loops + live creation-mode badges
  (per-agent glow + 'N/M designing...' header)
- new MCP tools: get_guidelines, ToolSearch, get_screenshot, get_editor_state,
  export_nodes, spawn_agents; style-guide local audit
- #27 AI panel restyle: rounded tool cards + green check-rings, gray user
  bubbles, model-pill bottom toolbar, header, empty-state pills, the
  PARALLEL AGENTS (agent_team_size) 1x-6x chip dropdown
- multi-chat tabs: ChatSessions model (Deref-to-active) + tab row UI
  (switch / close / + / Cmd+T) with each run bound to its tab

Large checkpoint commit spanning the working tree (Rust shell crates).
2026-07-02 21:21:06 +08:00
Kayshen-X 2ccb063b28 feat(ai): drain the generation overlay gracefully when a design turn ends
On natural completion the design-turn teardown now calls
agent_indicators::finish_if_epoch instead of end_if_epoch: the already
queued reveals keep playing at the queue cadence and the whole overlay
(cursor + borders) clears itself once the last one lands, so the tail of
a slow 160ms stream is no longer snapped away mid-reveal.

A user stop / abort still tears down immediately via end_if_epoch, so a
cancelled generation does not keep animating. Wired across the
orchestrator run guard, the web design session, the host-core
DesignSession drop, and the desktop sub-agent / design-loop teardown.

Updates the desktop lifecycle tests to match: the frame-clear test now
drives past the drain window before asserting the overlay cleared, and a
shared registry test-lock serializes reveal-streaming turns against the
exact animation-deadline assertion (the registry is process-global).
2026-07-02 21:21:05 +08:00
Kayshen-X 05986b1eab feat(canvas): animate AI design generation with an agent pointer cursor
Each agent gets an arrow cursor that flies between the nodes it places
along the reveal schedule, arriving as each node scale-pops in; the
element currently being placed carries a breathing border. Replaces the
old dashed-border reveal fade (canvas_agent_overlay removed).

Reveal cadence is slowed to a uniform 160ms queue and retained for the
whole run, so the cursor parks on the last placement between streamed
chunks instead of fading out. Splits agent_indicators tests into a
sibling file to stay under the 800-line cap.
2026-07-02 21:02:27 +08:00
Kayshen-X ad52b1386b ci(wasm): install pinned modern binaryen instead of apt v108
The WASM bundle build (#56) and op-web-sdk bundle workflows install binaryen
via apt, which on the ubuntu runner is v108 (2022) — too old for rustc 1.94's
wasm feature set. It lacks the --enable-bulk-memory-opt flag (the original
'Unknown option' failure) AND cannot validate the memory.copy/fill opcodes
rustc now emits, so wasm-opt rejects the bundle with 'all used features should
be allowed'. Download a pinned binaryen version_123 release (matches the local
dev toolchain) and prepend it to PATH. Verified locally: the full
check-wasm-bundle.sh pipeline passes with binaryen 123 against a fresh
rustc-1.94 wasm build.
2026-07-02 02:05:42 +08:00
Kayshen-X 09f2f55f6b fix(ci): rustfmt, clippy -D warnings, and stale test fixes for green rust-check
The rust-check workflow's cargo fmt --all -- --check failed on 108
line-wrap drifts under the pinned 1.94 rustfmt; running cargo fmt --all
clears them. With fmt fixed, the downstream steps (never reached in CI
before) surfaced accumulated lint + test debt from the recent chat-panel
(#41/#42/#43), agentic-design tool-loop, and multi-chat-tab work:

- clippy -D warnings: question_mark, field_reassign_with_default (test-only,
  false positives on DerefMut writes), dead_code, double_ended_iterator_last,
  doc_overindented_list_items, module_inception, single_match, nonminimal_bool,
  sliced_string_as_bytes — fixed via clippy's own mechanical rewrites or
  narrowly-scoped #[allow] on test items.
- op-editor-ui: the no-model quick-action-card test asserted pre-#43 behavior
  (pills are now clickable regardless of model); re-point it at the Example hit.
- op-host-native (gl-host) + op-host-web (canvaskit) widget_host tests: 10
  stale UI/layout/behavior expectations — footer gained a palette icon
  (attach shifted), quick-action pills are full-width stacked, the speed chip
  opens the Parallel-Agents picker (#32), applying a single-root-frame design
  swaps the empty starter frame (command_root_replace) so the child count is
  unchanged, a CJK prompt wraps to two lines, the toolbar grew under the
  bottom-left chat panel, and the canvas Preview button is behind the
  experimental-features opt-in. All test-only; the two bug-suspect cases (CJK
  selection, design-apply count) were confirmed intended behavior.
- op-host-web canvaskit test helper returned ChatState where the .chat field
  is now ChatSessions (multi-tab); return the container and let DerefMut carry
  callers.

No production behavior changed.
2026-07-02 01:54:05 +08:00
Kayshen-X 6a43c90774 build(ci): binaryen-tolerant wasm-opt flags + refresh cargo-deny advisories
The wasm bundle gates (tools/check-wasm-bundle.sh and
crates/op-web-sdk/tools/build-wasm.sh) hard-passed --enable-bulk-memory-opt
to wasm-opt, which older binaryen (the CI runner's apt package) does not
recognize, failing the WASM bundle build (#56) and op-web-sdk bundle
workflows at the size gate. Probe wasm-opt --help and keep only the feature
flags the installed binaryen advertises; on those older versions the single
--enable-bulk-memory already covers memory.copy/fill, so dropping the unknown
flag is safe. Local binaryen (v117+) keeps both.

cargo-deny advisories also failed: ttf-parser flagged unmaintained
(RUSTSEC-2026-0192, a transitive font-stack dep with no maintained
replacement) and memmap2 flagged unsound (RUSTSEC-2026-0186). Ignore the
former and bump memmap2 0.9.10 -> 0.9.11 (the patched release) for the
latter. advisories now pass.
2026-07-02 01:53:40 +08:00
Kayshen-X 22da4eed3c chore(repo): retire agent-native; root is now a pure Cargo workspace
- Remove the Zig `agent-native` git submodule (no Rust crate depends on it;
  the built-in agent runtime is the Rust `agent` crate under vendor/agent).
- Make the repo root a pure Cargo workspace: delete root package.json + bun.lock;
  move all web-SDK JS/Bun tooling into packages/ (new packages/package.json
  workspace root, generate-iconify-catalog.mjs, and the oxlint/oxfmt configs).
- Scrub agent-native from all docs and fix README dev commands + prerequisites
  to reflect the Rust product (cargo directly; bun tooling runs from packages/).
- Drop the deleted-root-package.json read from the op-host-web ci_workflow test.
2026-07-01 22:16:04 +08:00
Kayshen-X 77dca8c01b chore: retire TypeScript OpenPencil, delete apps/* + pen-* packages
The product is now Rust (crates/) + the Zig agent runtime
(packages/agent-native) + the wasm-backed web SDK (packages/op-web-sdk*).
Delete the retired TypeScript editor/desktop/CLI (apps/*), the pen-*
packages, and their orphaned TS tooling, Dockerfile, and root tsconfig.

Rust-side hooks into the deleted TS are rehomed so the workspace still
builds, tests, and releases green:
- op-mcp: vendor the 10 element-tool-defs shards it include_str!'d from
  pen-mcp into crates/op-mcp/assets/element-tool-defs/ (git renames), and
  repoint the compile-time includes + the runtime parity test's read_dir.
- op-orchestrator: reword the parity panic that named the retired
  dump-planner-golden.ts generator (goldens stay as frozen baselines).
- CI: drop the TS-derive golden-drift + planner-parity oracle jobs and the
  TS path filters from rust-check.yml; repoint the cargo-bundle icon in
  rust-release.yml to crates/op-host-desktop/assets/icon.png.
- Release (Windows): recover the deleted apps/desktop/build/icon.ico into
  crates/op-host-desktop/assets/icon.ico and repoint the NSIS installer
  (rust-release.yml /DICON_FILE + package-windows.nsi ICON_FILE fallback +
  its example invocation), so the Windows release build still finds it.
- Remove the dead .githooks/post-commit hook — it rebuilt the deleted
  out/mcp-server.cjs via the removed mcp:compile script and only watched
  now-deleted pen-* paths; the Rust MCP is built by cargo.
- Recover apps/web/public/logo-discord.svg to screenshot/ and repoint the
  15 READMEs' logo/link paths.

package.json is now Rust-first (dev/build/test -> cargo + serve-web) and
docs (CLAUDE.md/AGENTS.md/packages+crates CLAUDE.md) describe the Rust
product. The retired TS remains recoverable via git (last tag v0.7.5).

Verified: cargo check --workspace green; cargo test -p op-mcp 353 passed;
cargo test -p op-orchestrator 848 passed; cargo test --workspace --no-run
compiles all targets; rust-release.yml parses and references no deleted
build assets.

Pre-commit fmt hook bypassed: its cargo fmt --check is pre-red from a
repo-wide stable-vs-nightly rustfmt config drift affecting unrelated files;
the .rs files touched here produce no fmt diff.
2026-07-01 21:12:39 +08:00
Kayshen-X a4ee78ba86 fix(ai): chat panel hover/close/send-slot refinements (#41/#42/#43)
- #41: collapse chevron renders through IconButton so it shows the ghost
  button-hover wash instead of only swapping the icon color
- #42: send/stop share one circle slot (toggle in place); footer cluster
  sits snug with no reserved stop gap; hit-test routes streaming->stop else send
- #43: example cards stay clickable without a connected model (clicking one
  fills the input; sending still requires a model)
- tab close glyph shows only while the tab is hovered (inset still reserved
  on the active tab so the title doesn't reflow)
2026-06-25 22:40:48 +08:00
Kayshen-X d603f2f388 chore: bump jian submodule (node promotion for batch_design) 2026-06-25 00:15:55 +08:00
Kayshen-X b1aebc3252 feat(ai): agentic design tool-loop, multi-chat tabs, #27 panel restyle
Built-in design generation now runs as an agentic MCP tool-loop (reusing the
agent-rs BuiltInProvider), gated behind OPENPENCIL_DESIGN_AGENT_LOOP / the
Settings experimental toggle; the orchestrator stays the default.

- design-agent system prompt + in-process design toolset (parity-locked with
  the MCP surface) + flag-gated Intent::Design routing
- spawn_agents execution as sequential sub-loops + live creation-mode badges
  (per-agent glow + 'N/M designing...' header)
- new MCP tools: get_guidelines, ToolSearch, get_screenshot, get_editor_state,
  export_nodes, spawn_agents; style-guide local audit
- #27 AI panel restyle: rounded tool cards + green check-rings, gray user
  bubbles, model-pill bottom toolbar, header, empty-state pills, the
  PARALLEL AGENTS (agent_team_size) 1x-6x chip dropdown
- multi-chat tabs: ChatSessions model (Deref-to-active) + tab row UI
  (switch / close / + / Cmd+T) with each run bound to its tab

Large checkpoint commit spanning the working tree (Rust shell crates).
2026-06-25 00:12:45 +08:00
Fini 541da7bcb7 test: adapt cleanup + chat-session tests to origin's rebase
op-orchestrator:
- import EffectField directly (origin's cleanup.rs no longer re-exports it
  via `use super::*`)
- insert both concurrent-cleanup roots in one InsertSubtree, since origin's
  command_root_replace now replaces an empty root on single-node top-level
  inserts (two sequential empty inserts would collapse to one child)
- defer the bottom-nav upward-shadow assertion pending reconciliation with
  Kayshen's e3ed2f1e "normalize mobile bottom tabs" (top-navbar transparency
  assertion retained)

op-host-desktop:
- import clear_fresh_starter_frame_for_design via `super::launch::` so the
  retained starter-frame tests resolve it after the merge
2026-06-21 23:18:23 +08:00
Fini d353a58450 chore: bump jian submodule (image cover + leaf flex-shrink) 2026-06-21 23:18:22 +08:00
Fini d517c0a2c0 chore(ai): smoke concurrency override + ab-v9 matrix 2026-06-21 23:18:21 +08:00
Fini ea3f94093c fix(editor): desktop chat session + builtin http handling 2026-06-21 23:18:20 +08:00
Fini df57392a1a fix(canvas): avoid double-applying text position in draw_text 2026-06-21 23:18:19 +08:00
Fini f1ceafb06d fix(ai): tighten mobile overflow / jsonl / decomposition skills 2026-06-21 23:18:18 +08:00
Fini 4ac5b5fa69 fix(lint): guard active nav pill against sibling corner-radius flattening
A 100%-rounded active tab nested in a nav pill was flattened to a sharp
square by the sibling corner-radius consistency detectors. Skip the fix when
a group's radius is pill-scale (>= 40) so the active pill survives validation.
2026-06-21 23:18:17 +08:00
Fini 169e6abed2 feat(orchestrator): tree-heuristics, palette-harmonize, post-pass refinements
Port the post-streaming tree-heuristics pass to Rust (tree_heuristics.rs):
- fill_card_leading_image_width: a card's leading header image spans the
  card width instead of leaving a white gap (160px image in a 252px card)
- fix_notification_badge_overlay: a badge authored as a flex sibling of an
  icon becomes a corner-overlay circle, not a square beside the icon
- active nav-tab pill, card image corner clip, invisible text-band fill,
  stacked-overlay-to-absolute, redundant colored wrapper strip

Add palette_harmonize.rs: retint the cool-slate neutral ramp toward the
page's temperature so a warm design no longer reads as off-palette gray.

Plus role-resolver / cleanup / scaffold / run refinements accumulated from
the glm-opus design-parity campaign.
2026-06-21 23:18:16 +08:00
Kayshen-X c4a9d8b3c6 fix(ai): harden style guide isolation 2026-06-21 22:45:32 +08:00
Kayshen-X 5de9bb9734 fix(ai): improve provider design generation prompts 2026-06-21 22:23:26 +08:00
Kayshen-X 53f0a3121d fix(ai): keep a bare ellipsis when ellipsize space is sub-glyph
ellipsize_to_width returned an empty string when not even one glyph
plus the ellipsis fit, dropping the only usable truncation marker. Keep
the bare `…` (matching the per-module ellipsize helpers) so the
truncation stays visible. Updates the unit test accordingly.
2026-06-21 22:08:52 +08:00
Kayshen-X e3c487bf75 fix(ai): ellipsize the collapsed chat pill title
The collapsed AI-chat pill is a fixed 150px wide, but its title was
drawn untruncated, so a long chat title (e.g. the design prompt)
overflowed past the chevron and the pill's right edge. Ellipsize the
title to the space between the bubble icon and the chevron. Adds a
shared width-aware util::ellipsize_to_width (measure-closure based, so
it stays decoupled from PaintCx) + a unit test. Covers native + web
(shared widget).
2026-06-21 21:56:46 +08:00
Kayshen-X 3e4286c332 fix(ai): improve chat-generated document handling 2026-06-21 21:26:34 +08:00
Kayshen-X 992947539b fix(orchestrator): normalize mobile bottom tabs 2026-06-21 21:23:15 +08:00
Kayshen-X 84a668611f fix(orchestrator): strip misrolled mobile search wrappers 2026-06-21 21:08:00 +08:00
Kayshen-X 4c113b2d4a test(panels): move fill-ref removal test to fills_tests
Keep command_tests.rs under the 800-line file limit by relocating the
remove_selected_fill_clears_the_variable_ref test to fills_tests.rs,
where the other fill mutator tests live.
2026-06-21 19:57:41 +08:00
Kayshen-X 6068a3db72 fix(panels): clear fill variable binding when removing a fill
A fill bound to a colour variable keeps a node->variable entry in
ui.variables.fill_refs. The scene resolver's fill_for lets a registered
fill ref WIN over container.fill, so removing the fill row cleared
container.fill (panel goes empty) but left the dangling fill_ref — the
variable colour kept painting. "Deleted the fill but the colour stays"
on token-based (old .op) designs. remove_selected_fill now drops the
fill_refs entry for the primary fill too. In-memory only; old .op files
load unchanged. Adds a core unit test + 4 host scene-clearing tests
(plain / nested / multi-fill / variable-bound end-to-end).
2026-06-21 19:51:52 +08:00
Kayshen-X c697a3036a fix(orchestrator): strip mobile chrome shells 2026-06-21 19:31:18 +08:00
Kayshen-X 387385fc67 fix(orchestrator): resolve scaffold root after replacement 2026-06-21 19:00:50 +08:00
Kayshen-X 8049cd5144 fix(orchestrator): tighten mobile generation rhythm 2026-06-21 18:47:48 +08:00
Kayshen-X 953749cfee fix(panels): stroke-width seed matches the painted value
Clicking the inline stroke-width input on a node with no stroke seeded
"1" (auto-filling a width the user never typed), and the round() seed
snapped fractional widths like 2.5 to "3". Seed via the same
format_panel_number(snapshot width, 0 when unset) the input paints, so
focusing never changes the displayed value. Fixed on native + web; adds
2 seed regression tests.
2026-06-21 18:26:11 +08:00
Kayshen-X efb4b84e7d fix(orchestrator): refine chat-generated mobile layouts 2026-06-21 18:24:12 +08:00
Kayshen-X 7826163240 fix(panels): show colorless stroke width in property snapshot
cmd_set_node_stroke_width attaches a fresh stroke with fill: None
(width, no color). The panel snapshot gated its `stroke` field on a
parseable solid color, so a colorless stroke's width vanished from the
panel: the width input read back 0 on blur even though the model kept
the width. Surface the stroke whenever it has a width, using the slate
placeholder color when no solid color is set. Adds 4 host tests
(commit-from-nothing, click/type/blur, snapshot repro).
2026-06-21 18:15:52 +08:00
Kayshen-X 00a4616b11 feat(panels): multi-fill + stroke-mode editor, property-panel polish
Batch commit of the property-panel work: multi-fill Fill editor, stroke
edit modes (uniform/per-side), clip support, plus polish — every
property-panel icon centred in its hover wash (fill X, padding/stroke
gears, gradient/effect remove), justify-row + size washes fit-content,
stroke duplicate width removed (inline width Single-only, per-side grid
otherwise), Openverse register link fixed + hover, ImageSrc Arc bump.

Known issue: stroke-width edit does not persist on a node whose
node_stroke_slot is None (see cmd_set_node_stroke_width) — follow-up.
2026-06-21 17:24:42 +08:00
Kayshen-X ec5ba9c283 fix(editor): repair strokes and legacy render layout 2026-06-21 16:52:31 +08:00
Kayshen-X 1b7664cbfc feat(panels): hover underline on the Openverse register link
The register link had no hover affordance. Track a
hover_image_search_register_link flag (set from the OpenImageRegisterLink
hit in both host cursor sweeps) and underline the link + arrow on hover.
2026-06-21 13:49:20 +08:00
Kayshen-X 457a67af38 fix(panels): point web Openverse register link at the API docs
Same 405 fix as the native host (8bba6345) — the web path still opened
the POST-only auth_tokens/register/ endpoint.
2026-06-21 12:43:39 +08:00
Kayshen-X bcc50eefde fix(panels): point Openverse register link at the API docs
The link opened the raw auth_tokens/register/ endpoint, which only
accepts POST — a browser GET lands on a 405 page. Point it at the API
reference's auth section, which documents how to register for credentials.
2026-06-21 12:38:46 +08:00
Kayshen-X d37e85960b fix(canvas): convert relinked image src to ImageSrc
The relink-image handler assigned a String to ImageNode.src, which is
now ImageSrc after the Arc-share change (ee0a933e) — this conversion was
applied in the working tree but missed from that commit, leaving
op-host-desktop compile-broken at the committed state. Add the .into().
2026-06-21 10:21:25 +08:00
Kayshen-X e2ca2f03de feat(panels): per-fill multi-fill Fill editor (stack/add/delete fills)
The Fill section was single-fill (header "+" only reset one solid, row "×"
cleared but a default row re-rendered). Now it's a Figma-style stacked list:
"+" appends a fill, each fill renders its own head row (swatch + type
dropdown + opacity% + ×), and "×" removes that specific fill. Built on the
indexed model ops from 17310556; mirrors the Effects multi-item pattern.

- snapshot: NodeSnapshot.fills: Vec<FillSummary>; legacy fields derived from
  fills[0]; walkers thread fills: &[FillSummary] (VisibleSections stays Copy).
- actions: RemoveFill(usize), SetFillType{index,fill_type},
  ToggleFillTypePicker(usize), OpenFillColorPicker(usize).
- state: PropertyFocus::FillHex/FillOpacity carry a fill index;
  ColorPickerState.fill_index indexes the HSV writeback (ColorTarget unchanged).
- paint/hit/height loop per fill in lockstep (paint_one_fill,
  push_fill_action_rects, fills_section_height); both hosts dispatch by index.

Backward compatible (fill is already Vec<PenFill>; old single-fill .op loads
as one row). Non-primary gradient/image fills are head-row-only for now
(editable gradient/image body stays primary-fill-only to avoid the ColorTarget
ripple); solid color/opacity/type are fully per-index.
2026-06-21 10:11:50 +08:00
Kayshen-X ba6aae07dc perf(canvas): Arc-share image src/url to make scene-cache O(1)
Bump vendor/jian to the ImageSrc(Arc<str>) change: ImageNode.src and
ImageFillBody.url are now reference-counted, so the per-frame
SceneBuildCache document clone + content-compare settle in O(1) for an
unchanged multi-MB image source instead of walking the whole base64
payload. Completes the deeper half of the image-drag perf fix (the
insert-time down-scale landed in a084628f).

Consumer sites updated for the newtype: writes/constructs use .into();
sites assigning into a String field use .to_string(); reads are
unchanged via Deref. Also fixes a stale gl-host test
(remote_icon_insert_bakes_svg_d_as_path_node) that still assumed icon
insert appends to the end — it now locates the baked path by icon_id and
asserts it sits above the selection (the insert-above behavior).
2026-06-21 10:01:37 +08:00
Kayshen-X a922bd2d0b feat(editor): open the Openverse register link from Settings → Images
The "Register at Openverse" link in Settings → Images → Advanced was painted
but inert. Add a hit-test (register_link_rect → ImagesHit::OpenRegisterLink →
AgentSettingsHit::OpenImageRegisterLink) and open
https://api.openverse.org/v1/auth_tokens/register/ on click — default browser
on desktop (open/xdg-open/start) and a new tab on web (window.open).
2026-06-21 09:03:09 +08:00
Kayshen-X 881d9356d3 feat(variables): indexed multi-fill model ops + selected-node mutators
Foundation for a per-fill Fill editor (stack multiple fills, add/remove by
row). Additive, backward-compatible (fill is already Vec<PenFill>; old .op
files load as a single fill):
- fills.rs: fill_type_of, fill_count, fill_type_at, add_fill,
  remove_fill(i), set_fill_type_at(i), set_fill_hex_at(i),
  set_fill_opacity_at(i).
- property_edit_mutators.rs: add_selected_fill, remove_selected_fill(i),
  set_selected_fill_type_at(i), set_selected_fill_hex_at(i),
  set_selected_fill_opacity_at(i) (via a with_selected_node helper).

UI/host wiring lands separately.
2026-06-21 09:01:21 +08:00
Kayshen-X f2f50e36cf perf(canvas): down-scale oversized images on insert to fix drag lag
A multi-megabyte base64 image src lives inline in the document's
PenNode::Image.src, so every scene rebuild clones + compares it and the
canvas decodes a full-resolution bitmap — moving a 5.4MB image lagged.

Shrink an oversized raster source the moment it enters the document, at
every host path that produces an inline data: URL into the doc src:
file-pick import / fill-image / relink (persistence_image), fetched +
searched images (image_search_session), and inline-base64 AI generations
(image_generate_host). New image_downscale module decodes via skia, fits
the longest edge to 2048px (or re-encodes a >2MB source at native size),
and emits JPEG for opaque sources / PNG for alpha — adopting the result
only when it actually shrinks. Animated GIF / WebP are detected by magic
bytes and left whole so re-encoding never drops frames.
2026-06-21 02:43:57 +08:00
Kayshen-X 61bec3db2b fix(canvas): keep insert-above at the page root for nested selections
insert_node_above_selection inserted into the selection's immediate parent,
which broke a nested selection: a free-positioned icon/image dropped into a
flex/auto-layout frame gets reflowed away from the cursor (and can detach
the selected flow child), and into a clipped frame it can be hidden. Insert
at the page root above the selection's top-level ancestor (descendant_contains)
instead — correct for top-level selections, safe for nested ones.
2026-06-21 00:08:54 +08:00
Kayshen-X eed21064b9 feat(canvas): insert icons/images above the selected layer
Icon and image inserts append()ed to the page root (the back / bottom of
the LayerPanel), ignoring the selection. Add insert_node_above_selection:
the new node lands in the selected node's parent at the selection's index
— one row up in the panel, one step toward the front in z-order (canvas
paints children back-to-front). Falls back to page-root append when
nothing is selected.

Bumps vendor/jian to d50a715 (O(1) image source id — fixes image-drag lag).
2026-06-20 23:58:58 +08:00
Kayshen-X 9613edcde0 chore(web): snapshot in-progress web-host work (settings module + deps)
Commits the concurrent session's remaining uncommitted changes at the
user's request: op-host-web web_settings.rs (new module) + canvaskit / lib
wiring + Cargo.toml / bun.lock dependency updates, plus minor canvas-overlay
and host-test tweaks.

Verified to compile before committing: op-editor-ui + op-host-native (native)
and op-host-web (wasm32) all `cargo check` clean.
2026-06-20 23:44:25 +08:00
Kayshen-X 63a70f38e9 fix(web): drop web_settings refs swept into the icon-scroll commit
fe9983a0 committed crates/op-host-web/src/widget_host.rs wholesale, which
swept in a concurrent actor's in-progress web_settings integration (the
settings_fingerprint field + load_into/fingerprint/save_if_changed calls)
WITHOUT its module (web_settings.rs untracked, the mod decl + serde dep
uncommitted) — so clean HEAD referenced a missing module. Restore
widget_host.rs to the pre-sweep base and re-apply only the intended
icon-picker scroll wiring; the actor's web_settings WIP stays uncommitted.
2026-06-20 23:38:12 +08:00