{"_id":"@chronista-club/creoui-editor-host","_rev":"2-bb1bd8cfa0114273c3fe04c051387753","name":"@chronista-club/creoui-editor-host","dist-tags":{"latest":"0.5.1"},"versions":{"0.5.1":{"name":"@chronista-club/creoui-editor-host","version":"0.5.1","keywords":["design-system","editor-mode","creoui","solid-js","design-tokens","live-editing","devtools","ai-pair-design"],"license":"Apache-2.0","_id":"@chronista-club/creoui-editor-host@0.5.1","maintainers":[{"name":"mako357","email":"mito@chronista.club"}],"homepage":"https://github.com/chronista-club/creoui#readme","bugs":{"url":"https://github.com/chronista-club/creoui/issues"},"dist":{"shasum":"368d4d79917d01432eee02bdfc0e53d6ae76f6ce","tarball":"https://registry.npmjs.org/@chronista-club/creoui-editor-host/-/creoui-editor-host-0.5.1.tgz","fileCount":56,"integrity":"sha512-+EfWxCqgJuUBMEDAUxRjQ1r1XSuM9bKAXGU/74nBYPkIYPt1TEZETINGTlXMUDoTO04GaVcwsFtfuRPmVdISOQ==","signatures":[{"sig":"MEYCIQC5MPO4SzEckm56SGPCMmkBOVU4H5B1tpJsRynkDfrJNAIhAJUCe3Ue4N7/MvFb36zGuR2S0FQPLt2AbFGx6Eifboxm","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":292650},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"db31be7627867fc7f3dd0dcc623e86e556c97284","scripts":{"build":"vite build","prepublishOnly":"bun run build","typecheck:build":"tsc -p tsconfig.build.json --noEmit"},"_npmUser":{"name":"mako357","email":"mito@chronista.club"},"repository":{"url":"git+https://github.com/chronista-club/creoui.git","type":"git","directory":"packages/editor-host"},"_npmVersion":"10.8.2","description":"creoui — Editor Mode reference runtime for SolidJS. Live design surface with console REPL, auto-discover, URL share, cross-tab sync, and export-to-patch.","directories":{},"_nodeVersion":"20.20.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^5.4.0","solid-js":"^1.9.0","typescript":"^5.5.0","vite-plugin-dts":"^4.2.0","vite-plugin-solid":"^2.10.0"},"peerDependencies":{"solid-js":"^1.9.0"},"_npmOperationalInternal":{"tmp":"tmp/creoui-editor-host_0.5.1_1779009021462_0.33841898490981714","host":"s3://npm-registry-packages-npm-production"},"deprecated":"renamed to '@chronista-club/creo-ui-editor-host' — https://github.com/chronista-club/creo-ui"}},"time":{"created":"2026-05-17T09:10:21.407Z","modified":"2026-07-11T00:02:42.072Z","0.5.1":"2026-05-17T09:10:21.623Z"},"bugs":{"url":"https://github.com/chronista-club/creoui/issues"},"license":"Apache-2.0","homepage":"https://github.com/chronista-club/creoui#readme","keywords":["design-system","editor-mode","creoui","solid-js","design-tokens","live-editing","devtools","ai-pair-design"],"repository":{"url":"git+https://github.com/chronista-club/creoui.git","type":"git","directory":"packages/editor-host"},"description":"creoui — Editor Mode reference runtime for SolidJS. Live design surface with console REPL, auto-discover, URL share, cross-tab sync, and export-to-patch.","maintainers":[{"name":"mako357","email":"mito@chronista.club"}],"readme":"# @chronista-club/creoui-editor-host\n\ncreoui — Editor Mode reference runtime for SolidJS。**Live design surface** として、デザイナ / エンジニア / AI agent が同じ app を rebuild なしで mutate できる paradigm を提供。\n\n- **Target × Control 2 軸直交設計** + `bind()` 1 本で field を宣言\n- **DevTools Console REPL** (`window.creoEditor.slider(...)` で即 slider 追加)\n- **DOM auto-discover** (既知 CSS 変数を自動 bind)\n- **URL shareable state** (`#creo=...` で URL 1 本で共有)\n- **Cross-tab sync** (同 origin の複数 tab で values 追従)\n- **Export to patch** (JSON / YAML / CSS / CSS-patch の serializer)\n- **AI pair design** (claude-in-chrome MCP + `window.creoEditor` で Claude が直接 field 操作)\n\n[docs/design/editor-mode.md](https://github.com/chronista-club/creoui/blob/main/docs/design/editor-mode.md) (D-1〜D-12) の protocol を実装。\n\n## v0.5.0 changes (2026-05-06)\n\n### Public type re-exports — consumer が host / config を annotate 可能に\n\n```ts\nimport type {\n  EditorHostConfig,    // <EditorHostProvider config={...}> の型\n  EditorShortcut,      // config.shortcut の型 ({ ctrl?, shift?, alt?, meta?, key })\n  EditorHost,          // useEditorHost() 戻り値\n  EditorHostMcpApi,    // host.mcp の AI agent 向け subset\n  EditorField,         // field 宣言用\n  EditorFieldType,\n  EditorFieldConstraints,\n} from '@chronista-club/creoui-editor-host'\n```\n\n### `exposeConsole` の default が DEV-gated に変更 (consumer-actionable)\n\n> **Behavior change**: production build で `window.creoEditor` を **expose しない** が default に。 dev (Vite) では従来通り expose。\n\n```diff\n# v0.4.x — production でも expose されていた\n- <EditorHostProvider />\n+ # v0.5.0 — default は import.meta.env.DEV に追従\n+ <EditorHostProvider />                                       // dev only\n+ <EditorHostProvider config={{ exposeConsole: true }} />      // production でも明示 expose\n+ <EditorHostProvider config={{ exposeConsole: false }} />     // dev でも expose しない (CI 等)\n```\n\nCLAUDE.md EH-6 規定 (`Console REPL を production で無条件に expose しない`) への準拠です。 Vite consumer は `vite/client` を tsconfig に含めると `import.meta.env.DEV` の completion が効きます。\n\n## インストール\n\n```bash\nbun add @chronista-club/creoui-editor-host creoui solid-js\n```\n\nPeer: `solid-js ^1.9.0`。`creoui/tokens.css` を app で import しておくと、Editor Layer が `--editor-mode-*` / `--color-*` token を consume する。\n\n## Quick start\n\n```tsx\nimport { createSignal } from 'solid-js'\nimport 'creoui/tokens.css'\nimport {\n  bind,\n  cssVarNumberTarget,\n  signalTarget,\n  editorHostTarget,\n  number,\n  color,\n  select,\n  EditorHostProvider,\n  EditorLayer,\n  useEditorHost,\n  useEditorSelectable,\n} from '@chronista-club/creoui-editor-host'\n\nexport default function App() {\n  return (\n    <EditorHostProvider>\n      <Main />\n      <EditorLayer />\n    </EditorHostProvider>\n  )\n}\n\nfunction Main() {\n  // (1) CSS 変数を slider で編集 (Target: cssVarNumber × Control: number slider)\n  const spacing = bind({\n    target: cssVarNumberTarget('tokens.spacing.m', '--spacing-m', 16, 'px'),\n    control: number({ min: 0, max: 48, step: 1, unit: 'px', variant: 'slider' }),\n    placement: { label: 'spacing.md', semantic: 'tool', order: 0 },\n  })\n\n  // (2) 自前 signal を color picker で編集\n  const [bg, setBg] = createSignal('#73e7aa')\n  const cardBg = bind({\n    target: signalTarget('card.bg', bg, setBg),\n    control: color({ variant: 'picker' }),\n    placement: { label: 'BG', semantic: 'tool', order: 1 },\n  })\n\n  // (3) theme mode を segmented control で切替\n  const host = useEditorHost()\n  const theme = bind({\n    target: editorHostTarget('theme.mode', host, 'mint-dark'),\n    control: select(\n      ['mint-dark', 'mint-light', 'sora-dark', 'sora-light',\n       'contrast-dark', 'contrast-light', 'oldschool-dark', 'oldschool-light'],\n      'dropdown',\n    ),\n    placement: { label: 'テーマ', semantic: 'global', order: -10 },\n  })\n\n  return (\n    <article\n      ref={useEditorSelectable({ binders: [spacing, cardBg] })}\n      style={{ padding: `${spacing()}px`, background: cardBg() }}\n    >\n      <p>spacing = {spacing()}px, bg = {cardBg()}, theme = {theme()}</p>\n    </article>\n  )\n}\n```\n\n`Ctrl+Shift+E` で Editor Mode ON、TOP に `theme` select、LEFT に ThemeEditor (active theme の swatch)、RIGHT に `spacing` slider と `bg` color picker が自動で現れる。\n\n## Live design surface (F1-F5)\n\n### F1. Console REPL — 開発中に console から slider を生やす\n\n`<EditorHostProvider>` が mount されると、DevTools Console で `window.creoEditor` 経由で API が即使える。**rebuild なしで field を追加できる**ので、デザイナとのペアワークで効く。\n\n```js\n// DevTools Console で:\ncreoEditor.slider('--spacing-m', 16, { min: 0, max: 48, unit: 'px' })\ncreoEditor.picker('--color-brand-primary', '#73e7aa')\ncreoEditor.flip('app.show-footer', true)\ncreoEditor.chooser('theme.mode', 'mint-dark', ['mint-dark', 'sora-dark', ...])\n\ncreoEditor.fields()            // 現 field list\ncreoEditor.describe('foo.bar') // 特定 field の meta + current value\n\n// Safe experiment:\nconst snap = creoEditor.snapshot()\ncreoEditor.setValue('tokens.spacing.m', 48)   // 試す\ncreoEditor.restore(snap)                        // 戻す\n\ncreoEditor.mode.enable() / disable() / toggle() / is()\ncreoEditor.help()              // 使い方 print\n```\n\nFull form も使える:\n```js\ncreoEditor.bind({\n  target: creoEditor.t.cssVarNumber('x', '--my-gap', 16, 'px'),\n  control: creoEditor.c.number({ min: 0, max: 48, variant: 'slider' }),\n  placement: { label: 'Gap', semantic: 'tool' },\n})\n```\n\n**Opt-out**: `<EditorHostProvider config={{ exposeConsole: false }}>` / **name 変更**: `config: { consoleName: 'myEditor' }`。\n\n### F2. DOM auto-discover — 既知 CSS 変数を自動 bind\n\n`:root` の computed style から `--color-*` / `--spacing-*` / `--radius-*` / `--typography-size-*` を自動検出、型を infer して slider / picker を一括生成:\n\n```js\ncreoEditor.autoDiscover()                          // default prefixes\ncreoEditor.autoDiscover({ prefixes: ['--color-'] }) // 色だけ\n```\n\n明示 `bind()` なしで既存 token が全部触れるようになる。\n\n### F3. Export — 現 state を patch として書き出し\n\n```js\ncreoEditor.export({ format: 'json' })       // 全 field の JSON\ncreoEditor.export({ format: 'css-patch' })  // 変更分のみ CSS\ncreoEditor.export({ format: 'yaml' })       // flat YAML\n```\n\n返り値は string のみ (clipboard 操作なし、consumer 側で `copy(out)` 等)。\n\n### F4. URL shareable state\n\n```tsx\n<EditorHostProvider config={{\n  urlSync: { autoSync: true, autoApply: true, key: 'creo' },\n}}>\n```\n\nEditor で値を動かすたび `#creo=<base64>` が URL に付く。別 tab / 別 session で URL を開くと editor state が即時復元される。\n\n```js\ncreoEditor.share()   // 現 URL (更新済) を return\n```\n\n### F5. Cross-tab sync (BroadcastChannel)\n\n```tsx\n<EditorHostProvider config={{ crossTab: true }}>\n```\n\n同 origin の複数 tab で editor values を双方向 sync。デザイナが mobile と desktop の 2 tab で同時確認、slider 操作が両方に反映。\n\n## AI pair design with Claude (claude-in-chrome MCP)\n\n`window.creoEditor` が expose されると、**専用 MCP server なしで** Claude が editor を操作できる:\n\n```js\n// Claude が claude-in-chrome MCP の javascript_tool 経由で:\ncreoEditor.fields()                          // 現 editor state を inspect\ncreoEditor.slider('--spacing-m', 12, {...}) // 提案値で slider 追加\ncreoEditor.setValue('tokens.spacing.m', 20) // 直接 値変更\ncreoEditor.snapshot()                         // 実験前に保存\ncreoEditor.restore(snap)                      // 気に入らなければ戻す\ncreoEditor.export({ format: 'css-patch' })   // 最終 diff を取得\n```\n\nClaude が screenshot で結果確認 → setValue で微調整 → export で commit 候補を生成、の **rebuild なしデザイン iteration loop**。\n\n## コンセプト: Target × Control の 2 軸\n\n| 軸 | 責務 | factory 例 |\n|----|------|-----------|\n| **Target** (データ源) | \"何をどこから read/write\" | `cssVarTarget` / `cssVarNumberTarget` / `signalTarget` / `localStorageTarget` / `ephemeralTarget` / `editorHostTarget` |\n| **Control** (UI 操作体系) | \"どう編集するか (widget)\" | `number` / `color` / `boolean` / `select` / `string` / `readonlyText` |\n\n直交なので、7 × 6 = **42 種の編集体験** を factory 組み合わせだけで作れる。新 Target source (GraphQL / SurrealDB / WebSocket) や新 Control widget (oklch-sliders / gradient stops / bezier) は既存を touch せず追加可能。\n\n```\nbind({ target: T, control: C, placement: P })\n       └───data───┘└───UI───┘ └──配置──┘\n```\n\n## 4 方向 semantic layout (D-2 / D-3)\n\n| Region | Semantic | 用途 |\n|--------|----------|------|\n| **TOP** | `global` | 全体設定 / 視線の起点 (theme select / shortcut hint) |\n| **LEFT** | `source` | 時系列過去 / 参照 (ThemeEditor) |\n| **RIGHT** | `tool` | 時系列未来 / ツール (field slider / picker panel) |\n| **BOTTOM** | `utility` | ローカル utility (batch / AI chat — Phase 2 で充実) |\n\nPlacement `semantic` で field がどの region に出るか決まる。\n\n## Content 非侵襲 (D-6)\n\nEditor Layer は Content layer の座標・可視性を奪わない:\n\n- `pointer-events: none` baseline、4 region のみ `auto`\n- Mode OFF で `visibility: hidden` (Content は 100% 通常動作)\n- 4 region は `color-mix(in oklch, ...)` で半透明 + `backdrop-filter: blur(...)`\n- Mode ON/OFF で Content の DOM / layout / scroll 位置は不変\n\n## Manual toggle のみ (D-7)\n\n- `Ctrl+Shift+E` — Mode 切替 (configurable via `config.shortcut`)\n- `Escape` — selection 解除 → Mode OFF の 2 段階退出\n- `host.toggle()` / `host.mcp.enable()` — programmatic\n\n自動 ON (hover 等) は存在しない。\n\n## AI agent ready (D-10)\n\n`useEditorHost().mcp` に `listFields` / `getValue` / `setValue` / `mode` / `enable` / `disable` を expose。Phase 2b の `@chronista-club/creoui-editor-host-mcp` (予定) がこれを stdio MCP server として公開、Claude 等から直接 field を操作できる。\n\n```tsx\nconst host = useEditorHost()\nhost.mcp.setValue('tokens.spacing.m', 24)  // 外部から書換、chain が走る\n```\n\n## API Reference\n\n### `<EditorHostProvider config?>`\n\n```tsx\n<EditorHostProvider config={{\n  shortcut: { ctrl: true, shift: true, key: 'e' },\n  localStorageNamespace: 'my-app',\n  initialMode: 'off',\n}}>\n  {children}\n</EditorHostProvider>\n```\n\n### Target factories\n\n| Factory | 説明 |\n|---------|------|\n| `cssVarTarget(id, cssVar, initial)` | CSS custom property を string で read/write |\n| `cssVarNumberTarget(id, cssVar, initial, unit='px')` | CSS 変数を number + unit で書く (slider と相性◎) |\n| `signalTarget(id, accessor, setter)` | SolidJS signal に直結 |\n| `localStorageTarget(id, initial, namespace?)` | localStorage に JSON 永続化 |\n| `ephemeralTarget(id, initial)` | in-memory + subscribe 対応 |\n| `editorHostTarget(id, host, initial)` | host.values に直結 (mcp / subscribe 全部委譲) |\n\nCustom Target は `Target<T>` を満たせば OK:\n```ts\ninterface Target<T> {\n  id: string; initial: T\n  get(): T; set(value: T): void\n  subscribe?(listener: (value: T) => void): () => void\n}\n```\n\n### Control factories\n\n| Factory | Variant | Kind |\n|---------|---------|------|\n| `number(opts?)` | `'slider' \\| 'input' \\| 'stepper'` | number |\n| `color(opts?)` | `'picker' \\| 'oklch-sliders' \\| 'palette'` | color |\n| `boolean(opts?)` | `'switch' \\| 'checkbox'` | boolean |\n| `select(options, variant?)` | `'dropdown' \\| 'segmented'` | select |\n| `string(variant?)` | `'input' \\| 'textarea'` | string |\n| `readonlyText()` | — | readonly-text |\n\n### `bind({ target, control, placement })` → `Binder<T>`\n\n```ts\ninterface Binder<T> extends Accessor<T> {\n  set(value: T): void              // host 経由の 2-way sync で書き込み\n  readonly id: string\n  readonly target: Target<T>\n  readonly control: Control<T>\n  readonly placement: Placement\n  selectable(): (el: HTMLElement) => void  // ref callback\n}\n```\n\nAccessor として読み、`.set()` で書き、`.selectable()` で selection ref を取る。\n\n### Hooks\n\n| Hook | 用途 |\n|------|------|\n| `useEditorHost()` | host 直接取得 |\n| `useEditorMode()` | `Accessor<'on' \\| 'off'>` |\n| `useEditorSelection()` | `Accessor<SelectionInfo \\| null>` |\n| `useEditorHover()` | `Accessor<SelectionInfo \\| null>` |\n| `useEditorSelectable({ binders, id? })` | `ref={...}` 用 callback。binder 配列から `data-editor-fields` を set |\n\n### Theme meta\n\n8 theme (4 family × light/dark) の公式 meta は `creoui@0.1.0` に同梱。consumer は `THEME_INFO[themeId]` で取得。\n\n```tsx\nimport { THEME_INFO, THEME_IDS, DEFAULT_THEME_ID, SWATCH_ROWS } from '@chronista-club/creoui-editor-host'\n```\n\n## ファイル構成\n\n```\nsrc/\n├── index.ts              public API\n├── types.ts              internal: EditorField / EditorHost / SelectionInfo\n├── host.ts               internal: createEditorHost() core state\n├── host.test.ts          19 cases (core)\n├── selection.ts          internal: DOM hover/click + ResizeObserver\n├── shortcut.ts           internal: Ctrl+Shift+E handler\n├── provider.tsx          <EditorHostProvider> + useEditorHost()\n├── hooks.ts              public hooks (useEditorMode / Selectable 等)\n├── target.ts             ⭐ Target interface + 6 factory\n├── target.test.ts        8 cases\n├── control.ts            ⭐ Control types + 6 factory\n├── control.test.ts       5 cases\n├── binder.ts             ⭐ bind() conductor\n├── fields.tsx            internal: FieldEditor (Control kind で分岐)\n├── theme-editor.tsx      ThemeEditor (LEFT region 実装)\n├── theme-info.ts         8 theme meta + SWATCH_ROWS\n└── layer.tsx             <EditorLayer> 4 region overlay root\n```\n\n## Roadmap\n\n- ✅ Step 1-4: core state / handlers / UI / README\n- ✅ Step 5: Target × Control 分離 + `bind()`\n- ⏳ Step 6: `editor-host-v0.1.0` publish + examples/web-demo を consumer に rewrite\n- 📋 M4: MCP server (`editor_mode_*` stdio tool)\n- 📋 M5: Swift runtime (SwiftUI + NSAppearance)\n- 📋 M6: Custom Control plugin (oklch-sliders / gradient / spline)\n\n## License\n\nApache-2.0 — [LICENSE](https://github.com/chronista-club/creoui/blob/main/LICENSE)\n","readmeFilename":"README.md"}