Files
zooqueenandHanzo Dev f717dd89ad keys: the key a user pastes is sk-, not the retired hk-
28 apps told the user to paste an `hk-` key β€” input placeholders, settings
help text, READMEs, the error thrown when the field is empty β€” and five of
them validated that paste against /^hk-/ before sending it as a bearer.
Nothing mints that prefix any more: cloud accepts pk- and sk-, IAM mints
sk-. So the guard admitted a credential that authenticates nothing, and the
panel failed later at the gateway with nothing on screen to explain it.

The three leak detectors keep hk- and gain sk-. Salesforce's source scan
and the two Zendesk tests exist to catch a key committed to source or
echoed back through a request; retired keys are still in circulation and
leak exactly as badly, while a detector that only knew hk- would have gone
blind to every key minted from here on.

`sk-` used to be the example of the foreign key the guard screened out β€” an
OpenAI key. It cannot mean that now, and shape alone cannot tell one
vendor's sk- from another's, so those comments say what the check really
proves and point at the /v1/models call that proves the rest. The three
tests that pinned "sk- is rejected" now pin "hk- is rejected", which is the
invariant that survived.

Verified: 2029 tests pass across the 28 packages; typecheck clean on 25 of
them (hubspot, jupyter and teams fail identically at HEAD on JSX and ESM
type conflicts in files this does not touch); salesforce validate-source
green.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-13 18:07:16 -07:00

5.8 KiB

Hanzo AI for Sketch

@hanzo/sketch β€” a native Sketch plugin that puts Hanzo AI on your macOS design canvas. Critique a selection, rewrite / translate / content-fill text layers, and ask about your design, without leaving Sketch.

It is a thin adapter over the shared Hanzo design-AI action catalog β€” the same prompt builders, context assembly, and response parsing that back @hanzo/figma and @hanzo/canva, expressed once as the PURE design-core.ts. Model calls route through the api.hanzo.ai gateway via the published @hanzo/ai headless client. No /api/ prefix, ever β€” always /v1/....

Design actions

Command What it does Write-back
Summarize / critique selection UX/copy/hierarchy critique of the selection, leading with a one-line take. Inserts a note layer
Rewrite selected text Rewrites each text layer's copy (optionally per an instruction). layer.text = …
Translate text layers Translates each text layer into a target language. layer.text = …
Content fill Replaces each text layer with realistic, role-fitting placeholder copy. layer.text = …
Ask Hanzo… Free-form question about the selection. Inserts a note layer

rewrite, translate, and contentfill are per-layer: the model returns a JSON array positionally aligned to the selected layers, parsed by parseLayerEdits into { id, name, text } edits the panel previews before you Apply to layers. critique and ask return prose you can Add note to canvas as a new Text layer next to the selection.

Selection read β†’ write-back flow

Sketch selection
  β”‚  command.js β†’ sketch-bridge.readSelection()   (sketch/dom)
  β–Ό
Selection { layers: TextLayer[], totalCount }
  β”‚  seeded into the WebView (window.__hanzoSeed)
  β–Ό
design-core.runAction()  β†’  @hanzo/ai createAiClient().chat.completions.create()
  β”‚  (the model call runs in the WebView β€” it has fetch)
  β–Ό
raw text
  β”œβ”€ per-layer  β†’ parseLayerEdits(raw, layers) β†’ LayerEdit[]
  β”‚                window.postMessage('applyEdits') β†’ sketch-bridge.applyLayerEdits()
  β”‚                                                    layer.text = edit.text
  └─ prose      β†’ resultNote(id, raw) β†’ { heading, body }
                   window.postMessage('insertNote') β†’ sketch-bridge.insertNote()
                                                       new sketch.Text(...)

sketch-bridge.js is the ONLY file that touches sketch/dom; design-core.ts is pure and fully unit-tested.

Your Hanzo key

The plugin never bundles or commits a key. Open the panel, expand Hanzo key & model, paste an sk-… Hanzo API key (or an IAM access token from @hanzo/iam), and Save. It is stored per-plugin with sketch/settings. Optionally set a model (default zen5).

Build

pnpm install          # from the monorepo root
pnpm --filter @hanzo/sketch build      # β†’ dist/Hanzo AI.sketchplugin
pnpm --filter @hanzo/sketch watch      # rebuild on change
pnpm --filter @hanzo/sketch test       # vitest
pnpm --filter @hanzo/sketch typecheck  # tsc --noEmit

build.js assembles the .sketchplugin bundle in two esbuild passes: command.js β†’ Contents/Sketch/ as CommonJS for Sketch's CocoaScript runtime (sketch/* external), and the WebView app.ts (with @hanzo/ai bundled) β†’ Contents/Resources/app.js as a browser IIFE, alongside webview.html, the manifest.json, and the icon.

Install

  • Local: run the build, then double-click dist/Hanzo AI.sketchplugin. Sketch installs it into ~/Library/Application Support/com.bohemiancoding.sketch3/Plugins/.
  • The commands appear under Plugins β†’ Hanzo AI with the shortcuts declared in manifest.json (βŒƒβ‡§K critique, βŒƒβ‡§R rewrite, βŒƒβ‡§T translate, βŒƒβ‡§F content-fill, βŒƒβ‡§A ask).

Publish to the Sketch plugin directory

The build produces a standards-compliant .sketchplugin bundle with a manifest.json (identifier: ai.hanzo.sketch, appcast, bundleVersion). To publish to the Sketch plugin directory:

  1. Host the built .sketchplugin and a Sparkle appcast.xml (the appcast URL in the manifest) so Sketch can auto-update installs.
  2. Tag a release in the plugin's GitHub repo containing the built bundle.
  3. Submit the repo via the Sketch extensions submission form; Sketch reviews and lists it. Bump version/bundleVersion in manifest.json on each release β€” patch bumps only.

Layout

src/
  manifest.json       Sketch plugin manifest β€” 5 commands + menu
  command.cjs         plugin entry (CommonJS, CocoaScript runtime): opens the WebView, bridges messages
  sketch-bridge.cjs   sketch/dom read (readSelection) + write-back (applyLayerEdits, insertNote)
  design-core.ts      PURE: action catalog, context assembly/truncation, @hanzo/ai shaping, parsing
  webview/
    index.html        the assistant panel
    app.ts            panel logic over design-core + @hanzo/ai (built to Resources/app.js)
build.js              assembles the .sketchplugin bundle
tests/                vitest β€” the pure design-core + bridge helpers

command.cjs and sketch-bridge.cjs are CommonJS (.cjs) because they run on Sketch's JavaScriptCore/CocoaScript runtime, not Node ESM. build.js emits them as Contents/Sketch/command.js for the manifest to reference.