Commit graph

144 commits

Author SHA1 Message Date
Kayshen-X 70e202d802 feat(editor): improve document workflows and rendering 2026-07-23 21:15:04 +08:00
Kayshen-X 01820bf5ea fix(editor): improve import and generation reliability
Expand HTML/CSS import fidelity, preserve layered fills and accurate fonts across HTML, Figma, and OP files, and make missing-font resolution selectable and case-insensitive. Harden code generation recovery and preview rendering, make model selection immediate, and keep image decode/compositing consistent across native and web hosts. Add mobile dependency and widget-boundary guards so all supported targets retain the intended feature surface.
2026-07-21 06:34:53 +08:00
Kayshen-X 4bda4cb0e9 feat(renderer): even-odd fill, per-corner radii, background blur end-to-end
Vector plan Tasks 3-5: winding rule imports onto PathNode.fillRule and
reaches skia/CanvasKit fill types through new *_with_fill_rule painter
entry points; per-corner radii flow payload->scene->RRect on both
backends; BACKGROUND_BLUR renders via backdrop save layers (native
SaveLayerRec backdrop, CanvasKit saveLayer backdrop). Includes jian
pointer bump (6bed2be). no-verify: repo fmt gate trips on unrelated
in-progress op-html sources.
2026-07-19 20:45:02 +08:00
Fini 0431313804 fix(renderer): pull jian's null-family font fallback fix
Bumps the vendored pin to merge/responsive-m1a-into-main @ fd17e40, which
names a default fallback family so Skia's font manager never builds an
SkString from a null family pointer.
2026-07-19 19:30:26 +08:00
Fini 4162006fab feat(renderer): adopt responsive-capable jian and adapt hosts
Merge Kayshen's feat/responsive-m1a (schema 1.2 + responsive opt-in,
breakpoint ranges, variant tables, transactional variant swap) plus
main's fill-rule/corner-radii/background-blur into the vendored pin
(merge/responsive-m1a-into-main @ 42772b6). Responsive stays double-
gated opt-in, so every existing document renders unchanged.

Adaptation is mostly new optional fields defaulted to None/false. The
one real behavior interaction: jian's node_rect now bakes a root's
authored origin into its returned rect (root_origins, added for the
responsive multi-root canvas), which doubled offsets at the three
places that added the origin themselves — the loader's layout harvest,
App Mode tap solving, and the preview's scene-to-runtime-space mapping
(the last now subtracts the root origin back out, gated on jian's own
is_origin_normalized so responsive roots stay untouched).
2026-07-19 02:27:09 +08:00
Kayshen-X 23467901d4 chore(deps): bump jian for image table dedup and Arc scene payloads
jian bfd6ca5 (deduplicated .op image table + shared-Arc load) and
633b9a0 (SceneNode image payloads as Arc<str>). no-verify: the repo
fmt gate currently trips on unrelated in-progress op-html sources.
2026-07-18 13:23:38 +08:00
Kayshen-X 6721a02837 chore: update Jian submodule 2026-07-15 21:17:21 +08:00
Fini 44a4fe4e24 feat(editor): align canvas layout and sizing behavior 2026-07-14 00:29:58 +08:00
Fini c5feaeec01 fix(desktop): open a multi-page file where its design actually is
A 16-page document whose first page was a blank cover opened onto empty
canvas ("content bbox None") and read as a load failure - the design was
on page 2. With no editor metadata to restore, the file now lands on the
first page that has content; a file that is empty everywhere still opens
on page 1.
2026-07-14 00:29:51 +08:00
Kayshen-X a2c26855df chore(agent): update vendor agent submodule 2026-07-12 14:39:30 +08:00
Fini b61a859396 chore(vendor): bump agent runtime submodule 2026-07-12 02:56:43 +08:00
Kayshen-X d866690cc4 chore(agent): bump vendor/agent to 2ab5e97
Picks up the multilingual product-overview README (docs-only); pointer
target is on the agent repo's origin/main.
2026-07-11 09:46:28 +08:00
Kayshen-X da7fa17a78 chore(agent): bump vendor/agent to 5c0f950 (match-centered grep cap)
Advance past c8fb619b (head/tail grep cap that could hide a match in a
long line) to origin/main, which centers the per-line cap on the match
plus repairs feature gates/advisories.
2026-07-07 21:44:12 +08:00
Kayshen-X 8e9f1c5f1e chore(agent): bump vendor/agent to c8fb619b (rebased onto main + grep cap)
Advance the submodule past the pre-rebase 876c890 (no longer on any
remote branch) to origin/main, which carries the ripgrep/compression
series rebased onto main plus the grep per-line output cap.
2026-07-07 21:33:18 +08:00
Kayshen-X 08b5be80b4 chore(agent): bump vendor/agent to 876c890
Advance the vendored agent runtime submodule to origin/main
(876c890 'feat(shell): apply output compression before the Bash cap').
2026-07-06 22:13:02 +08:00
Kayshen-X 41a023ca9a fix(renderer): serialize native skia font access via jian font lock
Windows CI segfaulted (STATUS_ACCESS_VIOLATION 0xc0000005) because the
design worker thread runs SkiaMeasure layout concurrently with the UI
thread paint/measure, and skia Windows FontMgr (DirectWrite) is not
thread-safe. Wrap NativeBackend::{new,draw_text,enumerate_system_font_families}
in jian_skia::with_font_lock and bump vendor/jian to the commit adding
that global reentrant lock.

Also un-gate chat_intent_host_tests on Windows (the lock makes its
intra-test worker/UI concurrency safe) and harden the flaky
explicit_family_typeface_lookup_is_cached test with a
retry-until-clean-window against concurrent generation bumps.
2026-07-06 21:07:49 +08:00
Kayshen-X 0611cc9714 chore(vendor): bump jian for the empty-source FontMgr fix (Windows CI segfault) 2026-07-05 22:24:31 +08:00
Kayshen-X 54b626759e chore(vendor): bump agent submodule to ea27441 (origin/main)
Update the vendor/agent pointer from a6f1897 to the current agent-rs
origin/main tip (ea27441). Verified: cargo check --workspace builds clean
against it; ea27441 is on origin so submodule checkout resolves in CI.
2026-07-05 19:41:04 +08:00
Kayshen-X d48ef56624 chore(vendor): bump jian for the widgets MSRV clippy fix 2026-07-05 19:26:11 +08:00
Kayshen-X 53da54ab8b fix(desktop): persist imported font before mutating the live registry
FontStore::import registered the font in the process-global registry
(bumping the generation) BEFORE writing it to disk. A failed
create_dir_all/write/save_index then left the font live + rendered this
session but unpersisted — while the caller reported the import as failed
and popped an error dialog. Reorder to parse (no registry mutation via
the new jian_skia::parse_imported_font_meta) -> persist to disk ->
register last, so the live registry is only mutated once persistence is
durable. New test failed_persist_does_not_leak_into_the_live_registry
(file-rooted store forces a disk failure) pins it.

Bumps vendor/jian to the parse_imported_font_meta commit.
2026-07-05 14:21:49 +08:00
Kayshen-X 2b3c883b71 feat(renderer): font-generation invalidation for imported fonts (phase 1)
Thread the jian_skia font-registry generation through the native render
+ measure caches so a runtime font import reflows an already-open
document instead of keeping stale fallback-font layout.

- op-pen-loader: CachingMeasureBackend drops its memo map, and
  SceneBuildCache folds font_generation into its rebuild-decision
  inputs, whenever the generation advances (current_font_generation is
  cfg-gated on skia-measure; const 0 for the estimate build).
- op-host-native: NativeBackend + the export EXPORT_BACKEND inherit the
  refresh for free through their shared FontResolver. widget_host's
  refresh_layout_scene now also rebuilds when the generation changed
  (tracked in layout_scene_font_generation) since a font import does not
  dirty editor_state; the generation is read before the initial scene is
  built to avoid a constructor race.
- Tests: end-to-end measure-changes-after-register (native resolver) and
  a host-level regression test for the scene-rebuild gate.

Bumps vendor/jian to the mutable imported-font registry commit.
2026-07-05 14:21:46 +08:00
Kayshen-X db20f1890c feat(editor): render imported Gaussian layer blur
Figma "Layer blur" effects were imported into the canonical document
but dropped before rendering (the scene carried only drop shadows), so
blurred background shapes rendered sharp. Carry the layer-blur radius
through the adapter (NodePayload.layer_blur) into an Effect::Blur, and
wrap the node's paint in a Skia blur layer (save_layer + blur image
filter, sigma = radius/2 × zoom) at every paint return path. Bumps the
vendored jian for the scene Effect::Blur variant + painter blur hook.
2026-07-04 20:05:42 +08:00
Kayshen-X 04e3cbcc69 chore(jian): update submodule to rebased main 2026-07-04 13:31:12 +08:00
Kayshen-X 686536a15e feat(openpencil): add code-to-design conversion flow 2026-07-04 13:23:28 +08:00
Fini acfcb9f864 feat(renderer): resolve measure and paint text through one font chain
Bump jian for the shared font resolver, path/line flex sizing, and
input-chrome measurement; route the native backend's draw_text and
measure_text_weighted through the resolver so fit_content boxes match
what actually paints (synthetic bold and multi-line heights included).
2026-07-04 02:15:52 +08:00
Kayshen-X ccc0297261 chore(agent): bump vendored agent runtime to upstream main 2026-07-03 22:18:08 +08:00
Kayshen-X ae11d8894a feat(ai): teach and promote radio-group, number-input and progress roles
Bumps vendor/jian to feat/promote-widen (single-source ROLE_TABLE,
9 promotable kinds, promotable_roles() export). The jian-components
generation skill teaches the five new role strings, and the loader's
role-vocabulary test now iterates promotable_roles() directly — the
skill can never silently drift from the promote table again
(jian-ops-schema joins op-ai-skills as a dev-dependency for that
test). Tabs is documented as deliberately not promotable.
2026-07-03 22:15:35 +08:00
Fini f04c724c09 merge: sync mesh/shader rendering, web live-sync and platform fixes from remote
Conflicts were the two mesh/shader implementations meeting: kept the
remote's newer complete version (typed shader uniforms, shader color
uniform binding, mesh vertex editing defaults, status-bar shell stroke
handling); deduped two identically-replayed RenderBackend methods.
2026-07-03 01:25:08 +08:00
Kayshen-X 47ff290cf6 fix(desktop): windows console, spawn, and url-opening hygiene
Four Windows runtime defects from the platform audit:

- The binary stayed in the console subsystem, parking a console window
  behind the GUI when launched from Explorer. Release builds now set
  windows_subsystem = "windows"; debug keeps stderr tracing visible.
- Background CLI probes (model discovery, provider version checks) and
  the vendored Claude SDK's per-turn spawns lacked CREATE_NO_WINDOW,
  flashing console windows once the GUI detaches from the console.
- MCP stdio servers naming .cmd/.bat shims (npx and most npm-installed
  servers) could not spawn: CreateProcess cannot execute shims and Rust
  1.77+ refuses them as program names. vendor/agent now resolves the
  command PATHEXT-style against the PATH the server will actually see
  (per-server env override wins) and routes only genuine shims through
  cmd /c — real executables keep direct spawn semantics.
- cmd /C start truncated URLs at `&` (every OAuth authorize URL). The
  URL now travels double-quoted via raw_arg so cmd keeps it literal.
2026-07-03 00:08:21 +08:00
Kayshen-X 061296c779 feat(render): handle mesh-gradient + shader fills across editor consumers
vendor/jian ff39a41 added PenFill::MeshGradient / PenFill::Shader plus
SceneGradient::Mesh and SceneNode.shader; every exhaustive match over
those types stopped compiling. Cover them with faithful conservative
semantics: opacity getters/setters treat the new bodies like their
gradient siblings, variable binding walks mesh vertex stops (shader
uniforms are untyped — left alone), canvas paint degrades mesh to its
first vertex colour until the RenderBackend grows a mesh method, the
property panel reports the neutral Solid type (no lossy conversion
offered), and lint contrast checks skip both like image fills.
2026-07-03 00:08:19 +08:00
Kayshen-X 0ba60361d4 fix(canvas): don't arm an erase frame when a design run finishes empty
finish_if_epoch previously routed the empty-queue case through
drain_finished_run, which set the process-global needs_final_frame flag
even though a run that never queued a reveal never put a cursor on
screen. That stray flag made next_reveal_deadline_ms return a redraw
deadline out of an idle registry, and — because the registry is shared
across the whole test binary — perturbed an unrelated exact
animation-deadline assertion whenever a design-session test dropped an
empty session in parallel.

Clear an empty finish inline without arming the flag; the paint-path
drain still arms it after real reveals prune, where a cursor genuinely
was on screen. Adds a regression test.

Verification note: the op-editor-core suite could not be run for this
commit because the shared workspace is transiently non-compiling under a
concurrent mesh-gradient/SkSL-shader jian bump (new PenFill variants not
yet handled in fills.rs — unrelated files). Change is trace-verified and
touches only agent_indicators; re-run pending the tree compiling again.
2026-07-03 00:08:18 +08:00
Fini 1e3fdc91e1 chore(deps): bump jian for main-axis fill_container grow
Points the jian submodule at the layout fix that grows a main-axis
fill_container child (a sidebar footer spacer) into the column remainder.
2026-07-02 21:21:52 +08:00
Fini 0730e1ccce feat(host): headless render-parity tooling — bundled fonts, margin env, layout dump
Bundle 11 OFL design-font TTFs and register them with jian-skia at startup so
--render-shots reproduces Pencil's glyph metrics without system fonts.
OPENPENCIL_RENDER_MARGIN tightens the export crop to match Pencil export_nodes
(no frame), and OPENPENCIL_DUMP_LAYOUT prints every node's computed rect as JSONL
to diff our layout against Pencil snapshot_layout element-for-element. Bumps
vendor/jian to the bundled-font + Auto=no-wrap render-parity commits.
2026-07-02 21:21:30 +08:00
Fini bf4a741d8e feat(loader): mesh/shader fills + .pen→.op legacy normalize hardening + pen2op bin + design-library merge
Bumps jian submodule for mesh-gradient/shader PenFill variants. normalize_legacy_doc
gains 8 .pen-compat repairs (stroke-fill string, gradient fill, icon/prompt nodes,
$ref cornerRadius, single-object fill). New pen2op bin converts Pencil .pen→.op.
library.rs merges a .lib.op's reusable masters+variables into a working doc.
2026-07-02 21:21:09 +08:00
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 d603f2f388 chore: bump jian submodule (node promotion for batch_design) 2026-06-25 00:15:55 +08:00
Fini d353a58450 chore: bump jian submodule (image cover + leaf flex-shrink) 2026-06-21 23:18:22 +08:00
Kayshen-X ec5ba9c283 fix(editor): repair strokes and legacy render layout 2026-06-21 16:52:31 +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 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 9b77f170c8 refactor(git): render masked HTTPS credential via unified TextInputView
Route the Remotes-section HTTPS credential field through the unified
TextInputView with mask: Some(&mask_credential) (new
paint_text_input_view_masked helper), deleting the ~90-line hand-rolled
masked renderer + masked_byte_for_source_byte caret remap in
git_panel_remotes. The token still shows as user:•••• with a correct,
family-aware caret. Other TextInputView call sites pass mask: None.

Bumps vendor/jian to c451e16 (TextInputView::mask).
2026-06-20 22:26:28 +08:00
Kayshen-X 11431e1a18 refactor(store): reuse jian_core::color, drop op-editor-core color_convert
The colour maths now lives once in jian_core::color; op-editor-core
re-exports it through color_picker (so crate::color_picker::parse_hex_rgb
and op_editor_core::* paths are unchanged) and deletes its duplicate
color_convert module. Bumps vendor/jian to 3404ad8.
2026-06-20 21:41:17 +08:00
Kayshen-X d706222f4e feat(panels): editable R/G/B fields in the colour picker
Click an R/G/B box to edit that channel as a 0..255 value through the
unified TextInputView; the host routes keystrokes via color_picker_rgb_*
(now_ms-threaded, digit-gated, 3-char cap) and live-applies by splicing
the channel into the current RGB and committing through set_hsv.
ColorPickerState gains rgb_focus + rgb_input; active_text_input + the
text-input gate resolve it; press dispatch focuses/blurs hex vs rgb.

Extracts the hex+rgb edit state machines into color_picker_edit.rs to
keep color_picker.rs under the 800-line cap. Bumps vendor/jian to d11d54e.
2026-06-20 21:27:08 +08:00
Kayshen-X da6e7cd228 refactor(panels): drive colour-picker hex bar through unified TextInputState
The picker hex field now stores a jian TextInputState (hex_input) seeded
on focus and edited via the same caret-aware insert/backspace gating as
the property-panel hex input, instead of a hand-rolled String draft +
manual caret. ColorPickerState carries hex_input; the host threads now_ms
for the caret blink and routes keystrokes through color_picker_hex_char/
backspace(now_ms); active_text_input() resolves the picker hex so its
blink drives the redraw. Fixes the caret drift and removes the picker's
last hand-rolled input.

Bumps vendor/jian to cce4523.
2026-06-20 20:57:10 +08:00
Kayshen-X 0c2af63b4e fix(canvas): resolve named draw family in caret measurement
Implement Painter::measure_text_family on the native skia backend
(NativeBackend::measure_text_family_styled segments per-codepoint
against the real family, exactly like draw_text) and forward it through
NativeFrameBackend + BaselineAdjustingBackend so the family-aware
measure reaches skia through the wrappers. Fixes the ~6px caret drift
in every TextInputView/TextArea chrome input (property pills, chat, git,
settings) — measure now uses the same typeface paint does.

Bumps vendor/jian to 19fb00b (Painter::measure_text_family).
2026-06-20 20:29:41 +08:00
Kayshen-X 1744b2aa8a feat(panels): editable hex input in the colour picker
Click the picker's hex bar to focus it, then type a #RRGGBB value
(or paste one char at a time): the bar shows the live draft + caret,
Enter/Escape/outside-click commit-and-blur, and a valid hex re-seeds
HSV so the SV box + hue handle follow. Keystrokes route through the
text-input gate so they don't leak into chat/shortcuts.

Bumps vendor/jian to ce19bed (ColorPicker::HexInput hit + draft render).
2026-06-20 19:29:47 +08:00
Kayshen-X 934aef334d fix(editor): borderless top-bar icon dropdowns + git commit-input baseline
The TopBar globe + file-menu SelectTriggers now pass bordered:false (no
Outline box, just the icon+chevron with a hover wash); the form-field
dropdowns keep the border. The git commit-message TextArea (ready +
status panels) now paints through BaselineAdjustingBackend so its text
sits on the baseline instead of clipping at the top. Bumps vendor/jian.
2026-06-20 18:05:12 +08:00
Kayshen-X 18fd0000dd fix(editor): bump vendor/jian for consistent baseline contract
jian a53a61c: ColorPicker's box-centered labels now route through
centered_text_baseline_y like every other component; non-box-centered
placements (RGB captions, Dialog title) and the host-baselined input
glyph layers are documented exceptions.
2026-06-20 17:01:02 +08:00
Kayshen-X 3f7ed3dc7e fix(editor): bump vendor/jian for text-baseline fix
jian dc15ea7: component labels (buttons, tabs, toggles, badges, menus,
dropdowns, tooltips, accordions) now position text by the BASELINE
(center + font_size*0.35) instead of the (height - font_size)/2 top-left
form, so they no longer render ~font_size too high in the editor chrome.
Input glyph layers stay top-relative (the host's BaselineAdjustingBackend
handles their baseline).
2026-06-20 16:49:34 +08:00