openpencil/crates/op-host-desktop/Cargo.toml
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

199 lines
9.6 KiB
TOML

[package]
name = "op-host-desktop"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
description = "OpenPencil desktop host — winit + skia-safe binary that drives the Rust editor on macOS / Linux / Windows"
# The shipped executable keeps the stable `openpencil-desktop` name so
# release artifacts + external CLI integrations are unaffected by the
# Phase 7.3 crate rename.
[[bin]]
name = "openpencil-desktop"
path = "src/main.rs"
# File-association metadata for `cargo-bundle` (`cargo bundle`). This
# declares OpenPencil as the OS-level handler for `.op` / `.pen`
# documents: macOS writes `CFBundleDocumentTypes` into the `.app`
# Info.plist, Windows registers the extensions, Linux emits a
# `.desktop` MimeType entry. Once the association is registered the
# OS launches this binary with the document path in argv, which
# `main.rs` opens at startup (see `src/main.rs` argv handling).
[package.metadata.bundle]
name = "OpenPencil"
identifier = "com.zseven-w.openpencil"
[[package.metadata.bundle.file_associations]]
ext = "op"
name = "OpenPencil Document"
role = "Editor"
[[package.metadata.bundle.file_associations]]
ext = "pen"
name = "OpenPencil Document"
role = "Editor"
# Desktop-only binary. The op-host-native lib is multi-platform (mobile
# stubs land in Step 1f) but the runner here pulls winit + glutin via
# op-host-native's desktop dep set, so the whole crate is gated to
# macos / linux / windows.
[target.'cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))'.dependencies]
# Phase 7.3 reorg: openpencil-shell-core dissolved — widget facade /
# render-backend / layout scene paths resolve through op-editor-ui.
op-editor-ui = { path = "../op-editor-ui" }
op-host-native = { path = "../op-host-native", version = "0.1.0" }
# Phase 5 strangler reorg: the stdio MCP server (`mcp_serve.rs`) drives an
# `op_editor_core::EditorState` (canonical `.op` document) instead of the
# old shell-core `Document`.
op-editor-core = { path = "../op-editor-core" }
# Locale tables — git dialog strings go through `op_i18n::translate`
# so they are localized like the rest of the chrome.
op-i18n = { path = "../op-i18n" }
# Phase 7 strangler reorg: the MCP tool registry + JSON-RPC stdio layer
# (`mcp.rs` + `mcp/*`) was extracted out of the dissolved shell-core into the
# op-mcp crate; `mcp_serve.rs` registers tools through `op_mcp::*`.
op-mcp = { path = "../op-mcp" }
# Phase 7 strangler reorg: the transport-free AI chat data shapes
# (`ChatProvider` trait + model catalog + agent-settings state) were
# extracted into op-ai; the `src/chat_*.rs` real transports + the
# model-discovery path import them through `op_ai::*`.
op-ai = { path = "../op-ai" }
# AI skill engine — the BuiltIn agent provider resolves
# generation-phase prompt skills through it (`chat_runtime.rs`).
op-ai-skills = { path = "../op-ai-skills" }
# ACP client — the `AcpProvider` chat backend (`chat_acp.rs`) drives
# third-party Agent Client Protocol agents through it.
op-acp = { path = "../op-acp" }
# Canonical `.op` (PenDocument) → shell `Document` loader / adapter.
# Extracted out of this binary into a shared library crate so library
# crates (op-host-native) can reuse the conversion;
# `persistence.rs` keeps only the desktop-only `rfd` Save/Open + error
# dialogs.
op-pen-loader = { path = "../op-pen-loader" }
# In-app Git — `git_session.rs` binds an `op_git::GitRepo` to the
# currently-open document so the Git panel can show branch / status
# / history and drive commits.
op-git = { path = "../op-git" }
# Structured node-level 3-way merge for `.op` documents — the in-app
# Git merge orchestrator feeds it the conflicting file's three index
# stages so a merge conflict resolves per PenNode, not as raw text.
op-opmerge = { path = "../op-opmerge" }
# Canonical `.op` / `.pen` schema — all persistence + interop goes
# through `PenDocument` rather than the desktop's private DocPayload.
# Falling back to the schema's `load_str` lets us open files saved by
# the TS editor, Jian apps, or any tool that emits the canonical form.
jian-ops-schema = { path = "../../vendor/jian/crates/jian-ops-schema" }
# jian-core ships the taffy-backed `LayoutEngine` that is the
# canonical flex solver for `.op` files. We reuse it (instead of
# rolling our own) so layout stays bit-identical with what the TS
# editor + future jian native runtime produce.
jian-core = { path = "../../vendor/jian/crates/jian-core" }
# jian-skia ships `SkiaMeasure` — a `MeasureBackend` impl that
# defers to skia's paragraph shaper. Wiring it into the layout
# engine replaces the ~10% character-count heuristic with real
# glyph advances so text frames sized to `fit_content` agree
# with what the canvas painter draws (TS parity for line-wrap +
# fixed-width text growth).
jian-skia = { path = "../../vendor/jian/crates/jian-skia", features = ["textlayout"] }
skia-safe = { version = "0.97.0", default-features = false, features = [
"binary-cache",
"textlayout",
"gl",
"pdf",
] }
# `casement` — ZSeven-W's winit fork (vendored as a submodule under
# `vendor/casement`). It adds `winit::platform::macos::drain_opened_file_urls`,
# the macOS open-documents Apple-event hook upstream winit lacks. The
# `package` key keeps the `winit` import name, so all `use winit::…`
# stays unchanged.
winit = { package = "casement", path = "../../vendor/casement", default-features = false, features = [
"x11",
"wayland",
"wayland-csd-adwaita",
"rwh_06",
] }
# Document Save / Open (.pen / .op). Serialization lives in
# `src/persistence.rs` as a hand-rolled DTO so shell-core stays
# serde-free (Color / Rect / Point2D come from glam + jian-core
# which would otherwise need wrapper types). rfd pops the native
# Save/Open file dialogs so the user picks the path.
serde = { workspace = true }
serde_json = { workspace = true }
rfd = "0.14"
# Base64-encodes chat image attachments for providers that take
# inline image content blocks (Claude SDK) or a base64 body field
# (HTTP-server transport). Same pure-Rust crate op-figma uses.
base64 = "0.22"
# 2D affine transforms used by PNG export's bounds walker so nested
# rotated containers don't clip their children. Pinned to the same
# version shell-core uses for `Point2D = glam::Vec2`.
glam = { version = "0.29", default-features = false, features = ["std"] }
# Resolves the platform-specific user-config dir for the auto-saved
# settings file (TS parity with `agent-settings-store` localStorage):
# macOS → ~/Library/Application Support/openpencil/settings.json
# Linux → ~/.config/openpencil/settings.json
# Windows → %APPDATA%/openpencil/settings.json
dirs = "5"
# Cross-product Rust agent runtime — provides `QueryEngine` + the
# generic `Provider` trait + streaming `Event`s. Vendored as the
# `vendor/agent` git submodule (the agent-rs repo) and referenced by
# relative path so the BuiltIn ChatProvider in `src/chat_runtime.rs`
# can drive a real LLM turn. The submodule keeps CI self-contained.
#
# Compiled with NO default features today — the `anthropic` feature
# would drag in reqwest's TLS stack (rustls / icu / idna) which needs
# rustc ≥ 1.86 while this workspace pins 1.85. The BuiltIn bridge ships
# the trait + the engine wiring + `from_provider`; concrete
# Provider impls (Anthropic / OpenAI-compat / Ollama) flip on once the
# workspace rust-toolchain bumps.
agent = { path = "../../vendor/agent/crates/agent", default-features = false }
# Forked from bartolli/anthropic-agent-sdk — lives under
# `vendor/anthropic-agent-sdk/` so OP owns the source and can diverge
# from upstream. Provides the Claude Code CLI subprocess transport +
# stream-json message parser that `src/chat_claude.rs` wraps as a
# `ChatProvider`. Default features (no rmcp) keep the dep set tight.
anthropic-agent-sdk = { path = "../../vendor/anthropic-agent-sdk" }
# Official GitHub Copilot CLI SDK (github/copilot-sdk). Replaces
# the former in-workspace fork. Drives the Copilot chat bridge in
# `src/chat_copilot.rs` over the CLI's `--server --stdio` JSON-RPC
# transport. Requires Rust 1.94 / edition 2024 (the workspace
# toolchain was bumped for it).
github-copilot-sdk = "0.1"
async-trait = "0.1"
# HTTP client for the Codex / OpenCode bridges. They run as local
# HTTP servers (spawned via `<bin> serve`) and we POST JSON requests
# to them — `reqwest` is already in the dep graph via
# anthropic-agent-sdk so adding it here costs no new deps.
# Default-features-off + rustls-tls keeps the TLS choice consistent
# with the SDK's. We still talk to 127.0.0.1, but rustls is a
# small price for not having two parallel TLS stacks.
reqwest = { version = "0.12", default-features = false, features = [
"rustls-tls",
"json",
"stream",
] }
# Async runtime — agent-rs is async; the BuiltIn ChatProvider impl
# bridges its `EventStream` into the sync `Iterator<ChatDelta>`
# shape via a tokio task + std::sync::mpsc channel.
tokio = { version = "1", default-features = false, features = [
"rt-multi-thread",
"macros",
"sync",
"io-util",
"process",
"net",
"time",
] }
futures = { version = "0.3", default-features = false, features = ["std"] }
# Native menu bar (`src/menu.rs`). winit owns the window but has no
# menu primitive, so `muda` builds the NSMenu / Win32 menu and routes
# selections through its global event channel. Gated to macOS /
# Windows: `muda`'s Linux backend needs a GTK window, but this
# binary's winit is built for the x11 / wayland backends (no GTK), so
# Linux falls back to the in-canvas File menu. `default-features =
# false` drops the Linux-only `gtk` / `libxdo` feature deps.
[target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies]
muda = { version = "0.17", default-features = false }