Commit graph

37 commits

Author SHA1 Message Date
Fini 0406908ba2 fix(submodule): pin vendor/casement to op-file-open head
CI checkout was failing on a ghost superproject pointer
c8df95eb82df3487e77e96c20e20864eb35e107a — that commit is not in the
casement remote and not in the local clone either. Local disk state is
5ad98f1c (== origin/op-file-open head), which IS pushed and accessible
to GitHub Actions' submodule fetch.

This unblocks the 'Rust Check' / 'Rust multi-platform build' / 'WASM
bundle check' workflows whose actions/checkout@v4 step uses
submodules: recursive. Cargo workspace still excludes vendor/casement
itself (op-host-native/op-host-desktop use it via path = ... winit
fork).
2026-05-23 18:53:21 +08:00
Kayshen-X 2efca5aa3b feat(topbar): sun/moon theme toggle icon + traffic-light reposition fixes
Theme-toggle button now paints a Sun glyph in dark mode (click → light) and a Moon glyph in light mode (click → dark); the Sun icon was hardcoded before. Adds Icon::Moon (lucide crescent) and threads theme_mode into TopBar.

Bumps the casement submodule with a fix for the native macOS traffic-light reposition: idempotent absolute placement against resize, baseline invalidation on fullscreen exit, and a poison guard so a transitional re-capture can't drop the lights below their default position.
2026-05-23 12:59:02 +08:00
Fini 1fa2d9415a Merge branch 's3a-orchestrator' into v0.8.0-new 2026-05-23 01:27:30 +08:00
Fini b1c3136deb feat(editor): EditorCommand::InsertSubtree — nested PenNode subtree insert
Existing EditorCommand variants are leaf-only (BatchInsertItem carries
only kind/name/x/y/w/h/fill_hex). The design orchestrator (S3a) must
apply rich nested designs — frames with children, layout, text — so
add InsertSubtree { nodes: Vec<PenNode>, parent_id }.

cmd_insert_subtree validates the parent is a container (or NONE = page
root), remaps every incoming node id to a fresh editor id via
remap_subtree_ids (so an externally-authored subtree can't collide
with live ids), and appends under the parent. The apply arm wraps it
in a history snapshot so the insert is one undo step.

NOT verified locally: op-editor-core does not currently build —
vendor/jian is pinned to unpushed commit 80121906 whose DesignMd*
types op-editor-core depends on are absent from every available jian.
The 8 InsertSubtree tests in command_subtree_tests.rs run once the
jian build is restored.

S3a Plan A.
2026-05-22 22:56:10 +08:00
Kayshen-X bb4a989709 feat(desktop): native window chrome + per-agent TopBar icons
Hide the native title bar and let the TopBar own the window
chrome — the Electron `titleBarStyle: 'hidden'` recipe:

- macOS keeps a real `NSWindow` (rounded corners, shadow,
  edge-resize, key-window responsiveness) with the title bar made
  transparent + emptied; the native traffic-light buttons stay,
  pushed down via casement's `with_traffic_light_inset` to centre
  in the 40 px TopBar. Windows / Linux drop decorations and the
  TopBar paints its own close / minimise / maximise dots.
- The TopBar reserves a left inset for the controls; it collapses
  in macOS fullscreen (native lights hide), tracked by a
  per-frame `window.fullscreen()` poll. `window_control_at`
  returns `None` on macOS so a fullscreen click on a left-edge
  app icon can't trigger a window control.
- A press on the TopBar's blank area drags the window.

TopBar chip also gains one brand icon per connected agent + an
`N agent[s] · M MCP` status (new `topbar.agentPlural` across all
15 locales).

Bumps the vendor/casement submodule to 5ad98f1c.
2026-05-22 15:48:21 +08:00
Kayshen-X 12747384c6 fix(agent): tolerate unknown Claude stream message types
Newer Claude Code CLIs emit stream message types the bundled SDK
was not compiled against — `rate_limit_event` being the one that
surfaced — and serde aborted the whole chat stream on the first
one ("unknown variant rate_limit_event").

The `Message` enum gains a `#[serde(other)] Unknown` catch-all so
any unmodelled `type` deserializes cleanly instead of failing the
parse; `hooks.rs` and `chat_claude.rs` match it as a silent no-op
(an unknown event carries no hook payload and no chat turn).
2026-05-22 11:39:57 +08:00
Kayshen-X 90fad0e17d fix(editor): clippy needless_range_loop in ai_chat_transcript
Use `messages.iter().enumerate().skip(start)` instead of the
explicit index loop `for i in start..messages.len()`; clippy's
`-D warnings` gate on Rust 1.94 trips on the former. Also bump
the casement submodule to da0bf09 so its .gitattributes forces
LF for source files (fixes Windows CI `cargo fmt --check` on
the vendored fork).
2026-05-21 22:33:10 +08:00
Kayshen-X 25e644dbc7 build(deps): vendor casement (winit fork) as a submodule
The casement crate was depended on through a sibling-repo path
(`../../../winit`) that only existed on the maintainer's machine,
so CI couldn't load the workspace manifest and every Rust Check
job died with "failed to read winit/Cargo.toml".

Vendoring it as a real submodule under `vendor/casement` (matching
the `vendor/jian` pattern, picked up by CI's `submodules:
recursive` checkout) closes that gap. The renamed GitHub repo
`ZSeven-W/casement` (was `ZSeven-W/winit`) tracks the `op-file-open`
branch — `feat(macos): drain_opened_file_urls` + the package rename
landed there as commit 5877fa83.

- `.gitmodules`: add vendor/casement.
- Root Cargo.toml: exclude vendor/casement from the workspace glob
  (it's its own workspace).
- op-host-native + op-host-desktop: path = "../../vendor/casement".
2026-05-21 22:23:34 +08:00
Kayshen-X 1902d57807 feat(editor): add UIKit component-browser panel
Closes the last TS-vs-Rust parity gap — the Rust shell lacked the
TS `component-browser-panel.tsx` (UIKit library browser +
click-to-instantiate).

- op-editor-core: `uikit.rs` with `UIKit` / `KitComponent` /
  `ComponentCategory` types + a built-in starter kit (6 components
  spanning button/input/card/nav/layout/feedback) as PenNode
  templates; `EditorState::instantiate_kit_component` deep-clones
  with fresh ids and translates the whole subtree to the drop point
  (children carry document-absolute coords).
- op-editor-ui: `component_browser_panel.rs` floating draggable
  panel — header (close), category pills (filtered to non-empty),
  3-col card grid with name + scaled preview rect; kit-id + search
  filters applied.
- op-host-native: paint at §11.5 (below the Design-MD panel),
  `dispatch_component_browser_press`, drag lifecycle, shared
  `over_topmost_panel` helper covers both top-most panels across
  wheel / pan / right-press / cursor_hint / layer-hover / 4
  overlay-hover blocks + align hover + stale-hover clear.
- op-host-desktop: View-menu toggle, `drain_component_browser_insert`
  places at the viewport centre.
- op-editor-core: `active_children`/`active_children_mut` /
  `ensure_pages` symmetric `pages: Some([])` fallback —
  inserts land in `doc.children` and survive a subsequent
  `add_page` (which migrates them into Page 1).
- op-i18n: new `componentBrowser.empty` × 15 locales.

Bumps vendor/jian for the rebased `DesignMdSpec` schema + the
`jian pack` designMd-filter for packaged apps.
2026-05-21 21:24:12 +08:00
Kayshen-X 6e012049e4 feat(editor): add Design-MD panel and complete i18n coverage
Design-MD panel — closes the last TS-vs-Rust parity gap:
- New floating op-editor-ui design_md_panel + design_md_markdown
  renderer: collapsible theme/colour/typography/component/layout/
  notes sections, colour swatches, inline markdown highlighting.
- op-editor-core: parse_design_md parser + panel UI state.
- Host: View-menu toggle, top-most paint + hit-test, drag,
  .md import/export; bumps the vendor/jian DesignMdSpec pointer.

i18n coverage:
- Git panel, merge/conflict/error dialogs and GitError messages
  routed through op-i18n; git locale tables split into
  <base>_git.rs under the 800-line cap.
- Closed 10 app-wide hardcoded-string gaps (AI chat, Figma import,
  file menu, layer context menu, property-panel fill, update /
  file-picker / load-error dialogs, accesskit labels).
2026-05-19 22:29:22 +08:00
Kayshen-X 7d1e54b6e8 fix: track skia-safe/src/docs/pdf_document.rs (was caught by a global docs/ ignore) 2026-05-17 01:36:11 +08:00
Kayshen-X 8f8b36b76f docs: fix stale i18n + renderer paths flagged by Codex review 2026-05-16 15:45:54 +08:00
Kayshen-X 65e2dd9ebb chore: bump vendor/jian to the clippy-clean commit 2026-05-16 14:17:31 +08:00
Kayshen-X 1ef513d5a6 chore: bump vendor/jian to the relocated-renderer commit 2026-05-16 13:57:08 +08:00
Kayshen-X 5b6cf39775 chore: vendor anthropic-agent-sdk out of crates/ 2026-05-16 13:14:20 +08:00
Kayshen-X f3f57081da chore(crates): move vendored SDKs into workspace as forkable crates
Per user direction "可以不放在 vendor 里面,我们移动到自己的工程,
后面就和他们分叉" — promote the two community SDKs from vendor/ to
crates/ so they become first-class OP workspace members we own and
evolve, instead of read-only vendored snapshots.

Moves:
  vendor/anthropic-agent-sdk/  →  crates/anthropic-agent-sdk/
  vendor/copilot-sdk-rust/     →  crates/copilot-sdk/

Workspace integration:
  - Root `Cargo.toml` exclude list drops both vendor entries; the
    existing `members = ["crates/*"]` glob auto-includes them.
  - `crates/copilot-sdk/Cargo.toml`: stripped all `[[example]]`
    blocks (22 of them) — the examples/ dir was already removed
    during the import, and leaving the entries broke
    `cargo test --workspace --no-run`.
  - `crates/anthropic-agent-sdk/Cargo.toml`: already had its
    `[[example]]` blocks pruned in the previous commit.

Lockfile pins (workspace `Cargo.lock`):
  Pulling reqwest 0.12.28 (via anthropic-agent-sdk) into the
  unified workspace dep graph re-resolved several `icu_*` crates to
  the 2.2 line, which requires rustc 1.86. OP's toolchain is 1.85
  (locked to stay compatible with the skia-safe-op fork). Pinned:
    icu_collections      2.2.0 → 2.1.1
    icu_locale_core      2.2.0 → 2.1.1
    icu_normalizer       2.2.0 → 2.1.1
    icu_normalizer_data  2.2.0 → 2.1.1
    icu_properties       2.2.0 → 2.1.2
    icu_properties_data  2.2.0 → 2.1.2
    icu_provider         2.2.0 → 2.1.1
    idna_adapter         1.2.2 → 1.2.1
  All eight pins are the latest versions on each crate's 2.1.x /
  1.2.x line that compile on rustc 1.85.

Verification:
  - `cargo check -p anthropic-agent-sdk` ✓
  - `cargo check -p copilot-sdk` ✓
  - `cargo test --workspace --no-run` ✓
  - `cargo test -p openpencil-shell-core --lib` → 250 pass
  - `cargo test -p openpencil-desktop chat_` → 16 pass

Next: replace the hand-rolled subprocess parser in chat_subprocess.rs
with thin per-CLI adapters that route Claude Code through
`anthropic_agent_sdk::SubprocessTransport` and Copilot through
`copilot_sdk::Client + Session`. Gemini stays on the generic stdin
bridge until an upstream Rust SDK exists. Codex + OpenCode get an
HttpServerProvider that spawns `<bin> serve` then connects via a
local HTTP client.
2026-05-14 16:49:17 +08:00
Kayshen-X cb5104af78 chore(vendor): add anthropic-agent-sdk + copilot-sdk-rust as IPC source-of-truth
User direction: instead of hand-rolling subprocess JSON bridges in
`chat_subprocess.rs`, pull the community SDKs into vendor/ + adapt
them. Both repos are MIT-licensed Rust SDKs purpose-built for their
respective CLIs and ship more capable wire-protocol parsers than the
generic line-based approach in this branch's HEAD.

`vendor/anthropic-agent-sdk/` (was bartolli/anthropic-agent-sdk @ main,
2026-05-14):
  - SubprocessTransport for `claude --print --verbose --output-format
    stream-json -- <prompt>`
  - Recognized message envelope (system / user / assistant / result
    shapes per Claude Code's documented headless protocol)
  - Binary-lookup fallback through ~/.npm-global/bin, /usr/local/bin,
    ~/.local/bin, ~/node_modules/.bin, ~/.yarn/bin (via `which` +
    manual probe)
  - Dangerous-env-var scrub (LD_PRELOAD / DYLD_INSERT_LIBRARIES /
    NODE_OPTIONS / ...) for spawn safety
  - CancellationToken-based abort wiring
  - Trimmed locally: removed examples/, demos/, tests/, docs/, .git/.
    Inner `[workspace]` block stripped so OP's root workspace owns the
    build. `typed-builder` pinned to `=0.21.0` because upstream's
    `0.23.2` uses stable `let`-chains (Rust 1.88+) and OP rust-toolchain
    is 1.85 to stay compatible with the skia-safe-op fork.

`vendor/copilot-sdk-rust/` (was copilot-community-sdk/copilot-sdk-rust
@ main, 2026-05-14):
  - LSP-style Content-Length-framed JSON-RPC over stdio for
    `gh copilot` (the new community CLI that succeeds the legacy
    `gh-copilot suggest` subcommand)
  - Client + Session abstraction with event subscription
    (`AssistantMessage` / `SessionIdle` / tool-use events)
  - Trimmed: examples/, tests/, .git/ removed. Cargo.toml unchanged
    (already 2021 edition + 1.85 rust-version + no problematic deps).

Workspace integration:
  - Both directories appear in OP root Cargo.toml's `exclude` list so
    `cargo build --workspace` doesn't try to compile them (each
    declares its own `edition` / `rust-version` distinct from OP).
  - openpencil-desktop will consume them via target-gated path deps
    in the next commit + replace the hand-rolled provider in
    `chat_subprocess.rs` with thin adapters that route per CliName:
      Claude Code → anthropic_agent_sdk::SubprocessTransport
      Copilot     → copilot_sdk::Client + Session
      Gemini      → keep the generic stdin/stdout bridge (no upstream
                    Rust SDK exists yet for the gemini CLI)
      Codex /
      OpenCode    → HttpServer bridge (separate, spawn `<bin> serve`
                    + connect to local 127.0.0.1:port)

Per the user clarification "opencode 和 codex 我们调用 http server, 通过
ipc 启动本地的 server 模式": Codex + OpenCode stay on the HttpServer
path even though they're also spawned subprocesses — the local
server is what we IPC with via HTTP, not their stdio.

Standalone build verified for both vendored crates: ✓ check passes
on rustc 1.85.1 (this host).
2026-05-14 16:36:27 +08:00
Kayshen-X 1a194efc3d feat(shell): canonical .op loader + jian-core layout + visual fidelity pass
Pivot the desktop's Open path to the canonical `jian-ops-schema`
parser and route layout through `jian-core::LayoutEngine` so files
saved by the TS editor, Jian apps, or any tool emitting the
canonical schema load through the shared parser + paragraph shaper.

Loader (pen_doc_adapter.rs + pen_doc_path_bounds.rs)

- All 12 PenNode variants → NodePayload, with each root's authored
  (base.x, base.y) added to harvested rects so multi-design files
  (e.g. pencil-demo.op's 14 mockups) spread across the canvas.
- Path anchors port `getPathBoundsFromAnchors` — endpoints + Bezier
  handles + cubic-derivative extrema — so curved paths scale into
  their (width, height) the way the canonical renderer paints.
- jian-skia's `SkiaMeasure` plugged in via
  `LayoutEngine::with_backend(...)`, replacing the ~10% character-
  count heuristic with real paragraph-shaper metrics. Wrap/layout
  now agree with paint instead of cascading 10% errors.
- Numeric-string fontWeight (`"700"`, `"normal"`, ...) parsed in
  both jian-core and the desktop adapter; expanded keyword table
  covers black/heavy/extralight/extrabold/demibold/hairline/etc.
- Version-tolerant `load_canonical` retries with `version` rewritten
  to `"1.0"` so legacy `version: "2.8"` files still load.

Text + icon rendering

- `Node.text_wrap` gated on `textGrowth: fixed-width` — single-line
  by default so font-fallback overshoot doesn't break lines the TS
  app shows on one line.
- CJK-aware `wrap_text` (canvas_viewport_overlay.rs) — per-char CJK
  breaks, word breaks for Latin, blank-line preservation, explicit
  `\n` splits. Takes a weight param.
- `RenderBackend::measure_text_weighted` added with NativeBackend +
  WebBackend overrides so wrap measurement matches weighted paint.
- icons.rs + new icons_data.rs sibling cover ~75 lucide variants
  for first-party `iconFontName` names from pen-core element-builders
  (trending-up/down, compass, refresh-cw, layout-dashboard, users,
  package, zap, sliders-horizontal, activity, loader, focus,
  chart-line, settings-2, arrow-right, check-circle, alert-triangle,
  alert-octagon, sticky-note, bar-chart-2, bold/italic/underline,
  shopping-cart/bag, send, message-circle, rocket, menu, credit-card,
  x-circle, mail, smartphone, chrome, apple, user, ...). Unknown
  names stroke a dot fallback (FALLBACK_ICON_D) instead of a block.
  All d-strings copied from lucide-react@0.545.0.
- `Icon::from_name(&str)` resolves kebab-case + common aliases.
- Synthetic bold via PaintStyle::StrokeAndFill for weights ≥600 on
  both native and web (single-weight bundles can't serve a real
  bold variant).

Chrome polish

- Hover state on file menu / locale picker / shape picker / layer
  panel rows / AgentSettings nav + provider cards. Host's
  apply_cursor_move updates each per its open state.
- File menu compacted (row 30, header 22, no `…` suffix on actions),
  recent file names truncate with a CJK-aware helper.
- `rfd::MessageDialog` on every failed Open / OpenRecent / Save /
  SaveAs / ExportImage with bilingual (EN/ZH) title + path + detail.
  OpenRecent failures prune the stale entry.
- `figma_import.rs` modal honest-stub (Coming soon copy, brand glyph),
  TopBar Folder+Chevron compound + Figma button.
- Settings sidebar nav + provider cards tinted on hover.
- Recent-files panel polished to single-line names with age column.

Tests

- pen_doc_adapter_tests.rs (sibling via #[path]) — 19 cases covering
  multi-root canvas offsets, shape size fallbacks, path anchor
  absolutize + Bezier extrema, fixed-width wrap, numeric-string
  weights, login.op + pencil-demo.op fixture loads.
- canvas_viewport_overlay.rs wrap_tests — 7 cases: ASCII / CJK /
  CJK+Latin / explicit-newline / blank-line / weighted advances.
- icons.rs first_party_icon_font_names_all_resolve guards 27+
  authored names against placeholder regressions.

File-cap discipline

- pen_doc_adapter.rs split into mod + path-bounds sibling + tests
  sibling.
- icons.rs split into mod + icons_data.rs sibling so the catalogue
  can grow without busting the cap.
- canvas_viewport_overlay.rs absorbs wrap_text + UniformBackend /
  WeightedBackend test stubs.

Sub-modules

- vendor/jian advanced for `resolve_weight` numeric-string parsing.
2026-05-14 09:26:08 +08:00
Kayshen-X 0c84202798 feat(shell): caret blink driven by jian-core::anim primitives
Sinks the blink phase logic into vendor/jian (jian-core::anim) so any
host can wire the same square-wave timing instead of reimplementing
per-product. Both OpenPencil chrome and Zode TUI consume the same
helpers.

- vendor/jian bumped to head with new `jian_core::anim` module
  (blink_visible / next_blink_flip_ms, 9 unit tests)
- ChatState: `caret_anchor_ms` resets on focus / keystroke /
  example fill so the caret reappears solid right after the user
  acts, not mid-fade
- AIChatPlaceholder.now_ms threaded from host; paint computes
  caret visibility = focused && jian_core::anim::blink_visible
- AIChatPlaceholder caret X uses RenderBackend::measure_text for
  pixel-accurate trailing edge (replaces the 7px / 13px guess
  per char that drifted on Roboto + Noto-CJK)
- WidgetHostNative.set_now_ms / chat_focused / next_animation_
  deadline_ms surface; runner refreshes from a single Instant
  anchor + sets ControlFlow::WaitUntil at the next blink flip
- inspector_window: new_events handles ResumeTimeReached → request
  redraw so winit actually wakes for the next frame
2026-05-10 18:19:46 +08:00
Kayshen-X d6e655277c chore(vendor): add skia-safe-op fork with wasm32-unknown-unknown platform
Vendors rust-skia 0.97.0 (from crates.io tarballs) into
vendor/skia-safe-op/{skia-bindings,skia-safe} and adds a single new
platform module — build_support/platform/wasm_unknown.rs — that
recompiles Skia C++ directly into the wasm32-unknown-unknown ABI:

  - reuses emsdk's libc / libcxx headers via -isystem
  - drops C++ exceptions + RTTI (-fno-exceptions -fno-rtti) so the
    resulting .o files do not import emscripten's exception runtime
  - forces clang's target via --target=wasm32-unknown-unknown
  - sets CC_/CXX_/AR_wasm32_unknown_unknown so the cc-crate FFI shim
    also picks up emsdk's bundled clang (host clang lacks wasm32)
  - on every gn_args invocation overwrites skia/bin/activate-emsdk
    with a no-op python stub so Skia's GN does not try to bootstrap
    a parallel emsdk install (we use emsdk's clang directly)

Why a fork: wasm-bindgen --target=web on wasm32-unknown-emscripten
emits emscripten library glue, not a browser-loadable ES module,
which is incompatible with our distribution model. Compiling Skia
to wasm32-unknown-unknown unblocks the browser ES module path; the
remaining libc/libcxx/libm gap is filled by crates/wasm-libc-shim
in a follow-up commit.

The Skia C++ source tree under skia-bindings/skia/ (~770 MB, 30k+
files) is gitignored — it is fetched at build time by
binary_cache::download. Only the Rust source from the upstream
0.97.0 crate tarballs and the new wasm_unknown platform module are
committed here.

Step 1b §3.2 P0.5B Run path, sub-phase C-hard.1.
2026-05-09 21:05:00 +08:00
Fini af9292d8f5 fix(ai): classify Type 0 components as non-mobile to skip phone chrome
Why: "Design a profile card" through MiniMax-M2.7 produced a 375×803 mobile
screen with auto-injected status bar, because the planner skill listed
"profiles" as a Type 2 single-task screen and the orchestrator's
isMobileScreen heuristic ran on width≤480 alone.

What: design-type.md + decomposition.md add Type 0 (single component:
card / badge / chip / modal) with width=400 height=0 1 subtask no chrome.
isMobileFullScreen helper extracted to orchestrator-plan-classify.ts and
required by both orchestrator.ts and orchestrator-sub-agent.ts so the
two paths can't drift on what "mobile" means (Codex review caught this
when only orchestrator.ts had the new check).

Verified with same MiniMax + same prompt: 400×320 component, 8 nodes,
firstChildRole=card, no status-bar.
2026-05-09 21:00:00 +08:00
Kayshen-X 340e8afb3e chore(vendor): add skia-safe-op fork with wasm32-unknown-unknown platform
Vendors rust-skia 0.97.0 (from crates.io tarballs) into
vendor/skia-safe-op/{skia-bindings,skia-safe} and adds a single new
platform module — build_support/platform/wasm_unknown.rs — that
recompiles Skia C++ directly into the wasm32-unknown-unknown ABI:

  - reuses emsdk's libc / libcxx headers via -isystem
  - drops C++ exceptions + RTTI (-fno-exceptions -fno-rtti) so the
    resulting .o files do not import emscripten's exception runtime
  - forces clang's target via --target=wasm32-unknown-unknown
  - sets CC_/CXX_/AR_wasm32_unknown_unknown so the cc-crate FFI shim
    also picks up emsdk's bundled clang (host clang lacks wasm32)
  - on every gn_args invocation overwrites skia/bin/activate-emsdk
    with a no-op python stub so Skia's GN does not try to bootstrap
    a parallel emsdk install (we use emsdk's clang directly)

Why a fork: wasm-bindgen --target=web on wasm32-unknown-emscripten
emits emscripten library glue, not a browser-loadable ES module,
which is incompatible with our distribution model. Compiling Skia
to wasm32-unknown-unknown unblocks the browser ES module path; the
remaining libc/libcxx/libm gap is filled by crates/wasm-libc-shim
in a follow-up commit.

The Skia C++ source tree under skia-bindings/skia/ (~770 MB, 30k+
files) is gitignored — it is fetched at build time by
binary_cache::download. Only the Rust source from the upstream
0.97.0 crate tarballs and the new wasm_unknown platform module are
committed here.

Step 1b §3.2 P0.5B Run path, sub-phase C-hard.1.
2026-05-09 20:59:19 +08:00
Kayshen-X 136274a3ec chore(vendor): bump jian submodule to d5d358e (Step 1b §3.2 P0.5A)
Picks up the keyboard/IME/focus event additions + W3C wheel deltaMode
landed in jian commit d5d358e. shell-core re-exports of the new types
land in the next commit; this commit only moves the pointer + Cargo.lock.

cargo test -p openpencil-shell-core --test gesture_re_export → 6/6 PASS
against the pinned submodule.
2026-05-08 22:03:08 +08:00
Kayshen-X 097ee7f4cb ci+vendor: pin Jian submodule to c4a794dc (main HEAD) + drop v0.8.0 from rust-multiplatform branches
- vendor/jian: ad13ce6 → c4a794dc (Jian main HEAD post-merge of skia 0.97 upgrade + Tab-tree FocusManager fixes)
- rust-multiplatform.yml: branches list 删 v0.8.0,仅留 [main](v0.8.0 是 transient version label,不该 hardcode CI)
2026-05-05 22:18:00 +08:00
Kayshen-X cf50616db1 style: apply formatter + bump vendor/agent submodule + ignore vendors in oxfmt
- .prettierignore: 加 vendor/agent + vendor/jian + target/(submodule 不在本仓 format 范围)
- vendor/agent: 62c4bad(cosmetic format-only delta in agent-rs)
- root + shell-native + shell-web Cargo.toml / deny.toml / README.md / wasm-bundle-check workflow: oxfmt auto-style
2026-05-05 22:12:00 +08:00
Kayshen-X c55807e432 ci: remove TS/Electron workflows (build-electron / ci / docker / publish-cli)
Rust-ification 阶段,CI 只保留 Rust 相关:
- rust-check.yml: cargo fmt + build + test (with STEP1A_REQUIRE_GPU=1 on Linux) + clippy + cargo-deny
- wasm-bundle-check.yml: wasm32 target check

删除:
- build-electron.yml: Electron desktop build (Rust 化后用 openpencil-shell-native)
- ci.yml: TS type-check + Vitest + web build (Rust 化后已废)
- docker.yml: TS Docker image (Rust 化后重做)
- publish-cli.yml: npm packages (Rust 化后改 cargo publish)
2026-05-05 22:09:00 +08:00
Kayshen-X c894514a91 ci+vendor: pin Jian submodule to c4a794dc (main HEAD) + drop v0.8.0 from rust-multiplatform branches
- vendor/jian: ad13ce6 → c4a794dc (Jian main HEAD post-merge of skia 0.97 upgrade + Tab-tree FocusManager fixes)
- rust-multiplatform.yml: branches list 删 v0.8.0,仅留 [main](v0.8.0 是 transient version label,不该 hardcode CI)
2026-05-05 21:30:00 +08:00
Kayshen-X 7fb674d928 style: apply formatter + bump vendor/agent submodule + ignore vendors in oxfmt
- .prettierignore: 加 vendor/agent + vendor/jian + target/(submodule 不在本仓 format 范围)
- vendor/agent: 62c4bad(cosmetic format-only delta in agent-rs)
- root + shell-native + shell-web Cargo.toml / deny.toml / README.md / wasm-bundle-check workflow: oxfmt auto-style
2026-05-05 21:24:00 +08:00
Kayshen-X 2f60bd49f8 feat(workspace): pin Jian submodule and shell wrapper deps (Step 1a Task 1)
Anchor v19 pivot at the workspace level: vendor Jian as a git submodule
pinned to fork commit ad13ce6 (P0.5 mini-gate GO; skia-safe 0.78 → 0.97 +
new pub draw_on_canvas adapter), wire jian-core / jian-skia / jian-host-desktop
as path deps with explicit version per spec §12.2, and re-export the
Jian render/geometry/scene types from shell-core so shell-native can
translate the OP RenderBackend facade into jian DrawOp commands.

shell-core stays wasm32-clean: only jian-core (already wasm32-validated
in P0.5) plus glam / bitflags / thiserror / tracing land here.
shell-native picks up the full P0-pinned GL stack (skia-safe 0.97.0,
glutin 0.32.3, glutin-winit 0.5.0, glow 0.17.0, winit 0.30.13,
raw-window-handle 0.6.2, scopeguard 1.2) plus jian-skia (textlayout)
and target-gated jian-host-desktop (default-features = false, no `run`
feature so we skip Jian's softbuffer raster present path — OP owns its
own GPU swap_buffers per spec §3.6).

Adds OP RenderBackend trait + Rect / Color (with RED/GREEN/BLUE/BLACK/
WHITE/TRANSPARENT named constants per spec §5.2) + TextLayout facade
that wraps jian_core::render::TextRun explicitly (TextRun has no Default
impl, fields enumerated to honour spec §5.2 round-2 CONCERN-1 fix).

Boundary checks all pass:
- wasm32 shell-web metadata: no jian-host-desktop / jian-skia
- aarch64-linux-android shell-native metadata: no jian-host-desktop
- shell-core src: no glutin / skia_safe / winit / glow imports

Tasks 2-4 (SharedSkiaContext + NativeBackend + ShellEvent mapping +
acceptance) follow per plan v7.
2026-05-05 21:00:00 +08:00
Kayshen-X 22003f9b0c chore(shell-native): add transient P0 probe gate (Step 1a)
Drives the three-OS CI matrix verification of the skia-safe + glutin +
glow + winit dep stack per Step 1a spec §7.

- examples/p0_probe.rs: stencil_visibility + readback chain runner (must
  own a real OS main thread because winit on macOS rejects
  EventLoop::new() from cargo test worker threads).
- tests/p0_probe.rs: subprocess-invoke wrapper, gated
  #[ignore = "P0_PROBE_GATE"] so default cargo test stays untouched.
- Cargo.toml: add transient [target.'cfg(not(target_arch = "wasm32"))'.
  dev-dependencies] block (skia-safe 0.97 + glutin 0.32.3 + glutin-winit
  0.5.0 + glow 0.17.0 + raw-window-handle 0.6.2 + scopeguard 1.2.0 +
  winit defaults). Pinned to versions resolved in /tmp/skia-glow-probe.
- .github/workflows/rust-check.yml: install Linux GL prereqs (xvfb,
  mesa, libxkbcommon, libwayland) and add a P0-probe-gate step running
  cargo test --ignored on each OS (Linux through xvfb-run; Windows
  early-returns per spec §8.2 WINDOWS_GPU_DEFERRED_NO_RUNNER).

All three artefacts are TRANSIENT — reverted in a follow-up cleanup
commit after CI is green and the loader-compat notes commit lands.
Task 1 owns the permanent integration.
2026-05-05 12:23:49 +08:00
Kayshen-X 2a56781cc6 ci+vendor: pin Jian submodule to c4a794dc (main HEAD) + drop v0.8.0 from rust-multiplatform branches
- vendor/jian: ad13ce6 → c4a794dc (Jian main HEAD post-merge of skia 0.97 upgrade + Tab-tree FocusManager fixes)
- rust-multiplatform.yml: branches list 删 v0.8.0,仅留 [main](v0.8.0 是 transient version label,不该 hardcode CI)
2026-05-05 12:23:36 +08:00
Kayshen-X db0b50f7b5 style: apply formatter + bump vendor/agent submodule + ignore vendors in oxfmt
- .prettierignore: 加 vendor/agent + vendor/jian + target/(submodule 不在本仓 format 范围)
- vendor/agent: 62c4bad(cosmetic format-only delta in agent-rs)
- root + shell-native + shell-web Cargo.toml / deny.toml / README.md / wasm-bundle-check workflow: oxfmt auto-style
2026-05-05 12:23:34 +08:00
Kayshen-X d5011547f1 ci: remove TS/Electron workflows (build-electron / ci / docker / publish-cli)
Rust-ification 阶段,CI 只保留 Rust 相关:
- rust-check.yml: cargo fmt + build + test (with STEP1A_REQUIRE_GPU=1 on Linux) + clippy + cargo-deny
- wasm-bundle-check.yml: wasm32 target check

删除:
- build-electron.yml: Electron desktop build (Rust 化后用 openpencil-shell-native)
- ci.yml: TS type-check + Vitest + web build (Rust 化后已废)
- docker.yml: TS Docker image (Rust 化后重做)
- publish-cli.yml: npm packages (Rust 化后改 cargo publish)
2026-05-05 12:23:33 +08:00
Kayshen-X 315eb28ff5 ci+vendor: pin Jian submodule to c4a794dc (main HEAD) + drop v0.8.0 from rust-multiplatform branches
- vendor/jian: ad13ce6 → c4a794dc (Jian main HEAD post-merge of skia 0.97 upgrade + Tab-tree FocusManager fixes)
- rust-multiplatform.yml: branches list 删 v0.8.0,仅留 [main](v0.8.0 是 transient version label,不该 hardcode CI)
2026-05-05 12:23:20 +08:00
Kayshen-X e6d6b1bd6f style: apply formatter + bump vendor/agent submodule + ignore vendors in oxfmt
- .prettierignore: 加 vendor/agent + vendor/jian + target/(submodule 不在本仓 format 范围)
- vendor/agent: 62c4bad(cosmetic format-only delta in agent-rs)
- root + shell-native + shell-web Cargo.toml / deny.toml / README.md / wasm-bundle-check workflow: oxfmt auto-style
2026-05-05 12:23:18 +08:00
Kayshen-X 2dcc8a96d3 feat(workspace): pin Jian submodule and shell wrapper deps (Step 1a Task 1)
Anchor v19 pivot at the workspace level: vendor Jian as a git submodule
pinned to fork commit ad13ce6 (P0.5 mini-gate GO; skia-safe 0.78 → 0.97 +
new pub draw_on_canvas adapter), wire jian-core / jian-skia / jian-host-desktop
as path deps with explicit version per spec §12.2, and re-export the
Jian render/geometry/scene types from shell-core so shell-native can
translate the OP RenderBackend facade into jian DrawOp commands.

shell-core stays wasm32-clean: only jian-core (already wasm32-validated
in P0.5) plus glam / bitflags / thiserror / tracing land here.
shell-native picks up the full P0-pinned GL stack (skia-safe 0.97.0,
glutin 0.32.3, glutin-winit 0.5.0, glow 0.17.0, winit 0.30.13,
raw-window-handle 0.6.2, scopeguard 1.2) plus jian-skia (textlayout)
and target-gated jian-host-desktop (default-features = false, no `run`
feature so we skip Jian's softbuffer raster present path — OP owns its
own GPU swap_buffers per spec §3.6).

Adds OP RenderBackend trait + Rect / Color (with RED/GREEN/BLUE/BLACK/
WHITE/TRANSPARENT named constants per spec §5.2) + TextLayout facade
that wraps jian_core::render::TextRun explicitly (TextRun has no Default
impl, fields enumerated to honour spec §5.2 round-2 CONCERN-1 fix).

Boundary checks all pass:
- wasm32 shell-web metadata: no jian-host-desktop / jian-skia
- aarch64-linux-android shell-native metadata: no jian-host-desktop
- shell-core src: no glutin / skia_safe / winit / glow imports

Tasks 2-4 (SharedSkiaContext + NativeBackend + ShellEvent mapping +
acceptance) follow per plan v7.
2026-05-05 12:23:10 +08:00
Kayshen-X cebe6614cc chore(vendor): add agent-rs submodule at vendor/agent 2026-05-03 23:15:00 +08:00