MV3 extension that captures the rendered active tab (via the shared snapshot-extractor contract) and imports it into OpenPencil. Logic lives in the new op-chrome-extension-core crate (wasm): endpoint rules, chunked-transfer integrity, /mcp envelope + reply classification, download-name sanitisation, SSO/account session parsing, and hub snapshot-inbox delivery. JS is glue only (chrome.* APIs, fetch, popup DOM, injected page functions). Capture: full page + element pick. Delivery: local ingress (POST /api/import/web-snapshot) with /mcp fallback, JSON download, and — when signed in to OP Hub — the account snapshot inbox. Flat popup UI, 15-locale strings with an in-popup language switcher, store packaging. Why a crate: keeps the security-sensitive logic in tested Rust rather than glue JS, and the SW/popup split keeps dynamic import() out of the service-worker graph (guarded by check-sw-imports).
5.6 KiB
Packages
The web SDK packages that remain after the TypeScript retirement. This directory is the npm/Bun workspace root (packages/package.json); the repository root is a pure Cargo workspace with no package.json.
The
pen-*packages (pen-types, pen-core, pen-engine, pen-renderer, pen-figma, pen-mcp, pen-ai-skills, pen-sdk, pen-react, pen-acp) and pen-codegen were retired along withapps/*. Their functionality now lives in the Rustcrates/(seecrates/CLAUDE.md). Nothing here depends on them. Theagent-nativeZig runtime was also removed — the built-in agent runtime is now the Rustagentcrate (vendor/agent, shared with Zode).
Workspace tooling
Run these from packages/:
- Lint / format the SDK:
bun run lint(oxlint, plus the extension's extractor drift check) /bun run format(oxfmt). - Iconify catalog (Rust assets):
bun run generate-iconify-catalog—scripts/generate-iconify-catalog.mjsreads@iconify-json/*and writescrates/op-editor-ui/assets/iconify-catalog-{core,brands}.json(the icon catalog embedded in / served by the Rust web target). - Sync SDK versions:
bun run sync-versionreads the canonical version from rootCargo.tomland updates all SDK consumers; verify withbun run sync-version:check.
op-web-sdk (op-web-sdk/)
Read-only OpenPencil .op viewer SDK for the web, wasm-backed. Wraps the op-host-web CanvasKit wasm bundle behind a small JS/TS embedding API (mount / load .op / viewport control / zoom-to-fit). Replaces the public role of the retired pen-react (viewing only — editing is not a goal of the public SDK).
- Zero runtime dependencies; ships its own wasm under
wasm/. - Build:
tsup(bun run buildinside the package). Tests:vitest.
op-web-sdk-react (op-web-sdk-react/)
React 19 adapter for op-web-sdk (component + hooks wrapper). Depends only on @zseven-w/op-web-sdk (+ peer react / react-dom).
op-web-sdk-vue (op-web-sdk-vue/)
Vue 3 adapter for op-web-sdk. Depends only on @zseven-w/op-web-sdk (+ peer vue).
op-chrome-extension (op-chrome-extension/)
Manifest V3 Chrome extension that captures the rendered active tab and
imports it into a running OpenPencil. No dependencies, not a Bun workspace
member — load it unpacked from chrome://extensions.
- The logic is Rust.
crates/op-chrome-extension-core(endpoint rules, chunked-transfer integrity,/mcpenvelope + reply classification, download-name sanitisation) compiles to wasm;packages/op-chrome-extension/scripts/build-wasm.shrunscargo build --target wasm32-unknown-unknown+wasm-bindgen --target weband installs the shim + module intoop-chrome-extension/wasm/, which is gitignored like every other wasm-bindgen output in the repo. Build it before "Load unpacked" — the popup reports an actionable error otherwise. Unit-test the logic natively withcargo test -p op-chrome-extension-core. - The JS that remains is glue:
chrome.*calls,fetch, popup DOM, and the functions injected into the captured tab (which must be JS — they run in the page's process). The manifest declares'wasm-unsafe-eval'incontent_security_policy.extension_pages, which is what MV3 requires to instantiate a bundled.wasm; no code is fetched from outside the package. - The capture engine is
vendor/snapshot-extractor.js, a byte-identical copy ofcrates/op-html/assets/snapshot-extractor.js(the contractop_html::import_snapshotparses).bun run lintrunsop-chrome-extension/scripts/check-extractor-sync.sh, which fails on drift;--fixre-copies the canonical asset. Never edit the copy. - Ingress:
POST /api/import/web-snapshoton the desktop app's live MCP endpoint (insert-only, tokenless, the one route there that accepts achrome-extension://origin — seecrates/op-host-services/src/mcp_live/snapshot_ingest.rs), falling back to a plain/mcptools/call import_web_snapshotfor the unmanaged--serve-webdaemon.Download JSON+op import:snapshotis the offline path. - Account (optional). The popup header can sign in to OP Hub through the
hub's own BFF: a tab on
GET /api/v1/auth/login?return_to=/account, thenGET /api/v1/sessionwithcredentials: 'include'. The extension is a public client — no SSO secret, no token, nocookiespermission; the hub'sHttpOnlysession cookie stays in the browser's jar. Regionscn/globalmap tohttps://op.zseven.cn/https://op.zseven.tech, both inhost_permissions, andaccount.rsasserts the manifest agrees. - Account delivery (optional, signed in).
POST <hub>/api/v1/snapshotswithcredentials: 'include'+X-CSRF-Token, i.e. op-hub's per-user snapshot inbox. The envelope, the page-title-derived name, the 32 MiB ceiling and the reply classification are Rust (hub.rs/hub_reply.rs);delivery.rsremains the single rule that decides local vs account, and an expired session collapses to local. The service worker's element-pick flow runs its own session probe (account.jsis in the worker's static graph and stays dynamic-import-free). Contract history:op-chrome-extension/docs/hub-inbox-api-proposal.md. - Store packaging.
bun run package-extension(orop-chrome-extension/scripts/package-extension.sh) builds the wasm, runs the tests and all four guards, stages only the runtime files, copiespttopt_BR/pt_PTfor Chrome's manifest-locale lookup, and writesop-chrome-extension/dist/op-chrome-extension-<version>.zip(gitignored). The listing's privacy policy isop-chrome-extension/docs/privacy-policy.md.