@chronista-club/creoui-editor-host 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +381 -0
- package/dist/auto-discover.d.ts +47 -0
- package/dist/auto-discover.d.ts.map +1 -0
- package/dist/binder.d.ts +28 -0
- package/dist/binder.d.ts.map +1 -0
- package/dist/console.d.ts +115 -0
- package/dist/console.d.ts.map +1 -0
- package/dist/control.d.ts +54 -0
- package/dist/control.d.ts.map +1 -0
- package/dist/cross-tab.d.ts +16 -0
- package/dist/cross-tab.d.ts.map +1 -0
- package/dist/export-bar.d.ts +13 -0
- package/dist/export-bar.d.ts.map +1 -0
- package/dist/export.d.ts +29 -0
- package/dist/export.d.ts.map +1 -0
- package/dist/fields.d.ts +9 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/helpers/concentric.d.ts +40 -0
- package/dist/helpers/concentric.d.ts.map +1 -0
- package/dist/hooks.d.ts +14 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/host.d.ts +3 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/i18n/index.d.ts +8 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/locale.d.ts +26 -0
- package/dist/i18n/locale.d.ts.map +1 -0
- package/dist/i18n/messages.d.ts +109 -0
- package/dist/i18n/messages.d.ts.map +1 -0
- package/dist/i18n/types.d.ts +40 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/useT.d.ts +11 -0
- package/dist/i18n/useT.d.ts.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1844 -0
- package/dist/index.js.map +1 -0
- package/dist/layer.d.ts +3 -0
- package/dist/layer.d.ts.map +1 -0
- package/dist/provider.d.ts +16 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/selection.d.ts +10 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/shortcut.d.ts +7 -0
- package/dist/shortcut.d.ts.map +1 -0
- package/dist/target.d.ts +26 -0
- package/dist/target.d.ts.map +1 -0
- package/dist/theme-editor.d.ts +3 -0
- package/dist/theme-editor.d.ts.map +1 -0
- package/dist/theme-info.d.ts +26 -0
- package/dist/theme-info.d.ts.map +1 -0
- package/dist/types.d.ts +158 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/url-sync.d.ts +35 -0
- package/dist/url-sync.d.ts.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
# @chronista-club/creoui-editor-host
|
|
2
|
+
|
|
3
|
+
creoui — Editor Mode reference runtime for SolidJS。**Live design surface** として、デザイナ / エンジニア / AI agent が同じ app を rebuild なしで mutate できる paradigm を提供。
|
|
4
|
+
|
|
5
|
+
- **Target × Control 2 軸直交設計** + `bind()` 1 本で field を宣言
|
|
6
|
+
- **DevTools Console REPL** (`window.creoEditor.slider(...)` で即 slider 追加)
|
|
7
|
+
- **DOM auto-discover** (既知 CSS 変数を自動 bind)
|
|
8
|
+
- **URL shareable state** (`#creo=...` で URL 1 本で共有)
|
|
9
|
+
- **Cross-tab sync** (同 origin の複数 tab で values 追従)
|
|
10
|
+
- **Export to patch** (JSON / YAML / CSS / CSS-patch の serializer)
|
|
11
|
+
- **AI pair design** (claude-in-chrome MCP + `window.creoEditor` で Claude が直接 field 操作)
|
|
12
|
+
|
|
13
|
+
[docs/design/editor-mode.md](https://github.com/chronista-club/creoui/blob/main/docs/design/editor-mode.md) (D-1〜D-12) の protocol を実装。
|
|
14
|
+
|
|
15
|
+
## v0.5.0 changes (2026-05-06)
|
|
16
|
+
|
|
17
|
+
### Public type re-exports — consumer が host / config を annotate 可能に
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import type {
|
|
21
|
+
EditorHostConfig, // <EditorHostProvider config={...}> の型
|
|
22
|
+
EditorShortcut, // config.shortcut の型 ({ ctrl?, shift?, alt?, meta?, key })
|
|
23
|
+
EditorHost, // useEditorHost() 戻り値
|
|
24
|
+
EditorHostMcpApi, // host.mcp の AI agent 向け subset
|
|
25
|
+
EditorField, // field 宣言用
|
|
26
|
+
EditorFieldType,
|
|
27
|
+
EditorFieldConstraints,
|
|
28
|
+
} from '@chronista-club/creoui-editor-host'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### `exposeConsole` の default が DEV-gated に変更 (consumer-actionable)
|
|
32
|
+
|
|
33
|
+
> **Behavior change**: production build で `window.creoEditor` を **expose しない** が default に。 dev (Vite) では従来通り expose。
|
|
34
|
+
|
|
35
|
+
```diff
|
|
36
|
+
# v0.4.x — production でも expose されていた
|
|
37
|
+
- <EditorHostProvider />
|
|
38
|
+
+ # v0.5.0 — default は import.meta.env.DEV に追従
|
|
39
|
+
+ <EditorHostProvider /> // dev only
|
|
40
|
+
+ <EditorHostProvider config={{ exposeConsole: true }} /> // production でも明示 expose
|
|
41
|
+
+ <EditorHostProvider config={{ exposeConsole: false }} /> // dev でも expose しない (CI 等)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
CLAUDE.md EH-6 規定 (`Console REPL を production で無条件に expose しない`) への準拠です。 Vite consumer は `vite/client` を tsconfig に含めると `import.meta.env.DEV` の completion が効きます。
|
|
45
|
+
|
|
46
|
+
## インストール
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
bun add @chronista-club/creoui-editor-host creoui solid-js
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Peer: `solid-js ^1.9.0`。`creoui/tokens.css` を app で import しておくと、Editor Layer が `--editor-mode-*` / `--color-*` token を consume する。
|
|
53
|
+
|
|
54
|
+
## Quick start
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { createSignal } from 'solid-js'
|
|
58
|
+
import 'creoui/tokens.css'
|
|
59
|
+
import {
|
|
60
|
+
bind,
|
|
61
|
+
cssVarNumberTarget,
|
|
62
|
+
signalTarget,
|
|
63
|
+
editorHostTarget,
|
|
64
|
+
number,
|
|
65
|
+
color,
|
|
66
|
+
select,
|
|
67
|
+
EditorHostProvider,
|
|
68
|
+
EditorLayer,
|
|
69
|
+
useEditorHost,
|
|
70
|
+
useEditorSelectable,
|
|
71
|
+
} from '@chronista-club/creoui-editor-host'
|
|
72
|
+
|
|
73
|
+
export default function App() {
|
|
74
|
+
return (
|
|
75
|
+
<EditorHostProvider>
|
|
76
|
+
<Main />
|
|
77
|
+
<EditorLayer />
|
|
78
|
+
</EditorHostProvider>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function Main() {
|
|
83
|
+
// (1) CSS 変数を slider で編集 (Target: cssVarNumber × Control: number slider)
|
|
84
|
+
const spacing = bind({
|
|
85
|
+
target: cssVarNumberTarget('tokens.spacing.m', '--spacing-m', 16, 'px'),
|
|
86
|
+
control: number({ min: 0, max: 48, step: 1, unit: 'px', variant: 'slider' }),
|
|
87
|
+
placement: { label: 'spacing.md', semantic: 'tool', order: 0 },
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// (2) 自前 signal を color picker で編集
|
|
91
|
+
const [bg, setBg] = createSignal('#73e7aa')
|
|
92
|
+
const cardBg = bind({
|
|
93
|
+
target: signalTarget('card.bg', bg, setBg),
|
|
94
|
+
control: color({ variant: 'picker' }),
|
|
95
|
+
placement: { label: 'BG', semantic: 'tool', order: 1 },
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
// (3) theme mode を segmented control で切替
|
|
99
|
+
const host = useEditorHost()
|
|
100
|
+
const theme = bind({
|
|
101
|
+
target: editorHostTarget('theme.mode', host, 'mint-dark'),
|
|
102
|
+
control: select(
|
|
103
|
+
['mint-dark', 'mint-light', 'sora-dark', 'sora-light',
|
|
104
|
+
'contrast-dark', 'contrast-light', 'oldschool-dark', 'oldschool-light'],
|
|
105
|
+
'dropdown',
|
|
106
|
+
),
|
|
107
|
+
placement: { label: 'テーマ', semantic: 'global', order: -10 },
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<article
|
|
112
|
+
ref={useEditorSelectable({ binders: [spacing, cardBg] })}
|
|
113
|
+
style={{ padding: `${spacing()}px`, background: cardBg() }}
|
|
114
|
+
>
|
|
115
|
+
<p>spacing = {spacing()}px, bg = {cardBg()}, theme = {theme()}</p>
|
|
116
|
+
</article>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`Ctrl+Shift+E` で Editor Mode ON、TOP に `theme` select、LEFT に ThemeEditor (active theme の swatch)、RIGHT に `spacing` slider と `bg` color picker が自動で現れる。
|
|
122
|
+
|
|
123
|
+
## Live design surface (F1-F5)
|
|
124
|
+
|
|
125
|
+
### F1. Console REPL — 開発中に console から slider を生やす
|
|
126
|
+
|
|
127
|
+
`<EditorHostProvider>` が mount されると、DevTools Console で `window.creoEditor` 経由で API が即使える。**rebuild なしで field を追加できる**ので、デザイナとのペアワークで効く。
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
// DevTools Console で:
|
|
131
|
+
creoEditor.slider('--spacing-m', 16, { min: 0, max: 48, unit: 'px' })
|
|
132
|
+
creoEditor.picker('--color-brand-primary', '#73e7aa')
|
|
133
|
+
creoEditor.flip('app.show-footer', true)
|
|
134
|
+
creoEditor.chooser('theme.mode', 'mint-dark', ['mint-dark', 'sora-dark', ...])
|
|
135
|
+
|
|
136
|
+
creoEditor.fields() // 現 field list
|
|
137
|
+
creoEditor.describe('foo.bar') // 特定 field の meta + current value
|
|
138
|
+
|
|
139
|
+
// Safe experiment:
|
|
140
|
+
const snap = creoEditor.snapshot()
|
|
141
|
+
creoEditor.setValue('tokens.spacing.m', 48) // 試す
|
|
142
|
+
creoEditor.restore(snap) // 戻す
|
|
143
|
+
|
|
144
|
+
creoEditor.mode.enable() / disable() / toggle() / is()
|
|
145
|
+
creoEditor.help() // 使い方 print
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Full form も使える:
|
|
149
|
+
```js
|
|
150
|
+
creoEditor.bind({
|
|
151
|
+
target: creoEditor.t.cssVarNumber('x', '--my-gap', 16, 'px'),
|
|
152
|
+
control: creoEditor.c.number({ min: 0, max: 48, variant: 'slider' }),
|
|
153
|
+
placement: { label: 'Gap', semantic: 'tool' },
|
|
154
|
+
})
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Opt-out**: `<EditorHostProvider config={{ exposeConsole: false }}>` / **name 変更**: `config: { consoleName: 'myEditor' }`。
|
|
158
|
+
|
|
159
|
+
### F2. DOM auto-discover — 既知 CSS 変数を自動 bind
|
|
160
|
+
|
|
161
|
+
`:root` の computed style から `--color-*` / `--spacing-*` / `--radius-*` / `--typography-size-*` を自動検出、型を infer して slider / picker を一括生成:
|
|
162
|
+
|
|
163
|
+
```js
|
|
164
|
+
creoEditor.autoDiscover() // default prefixes
|
|
165
|
+
creoEditor.autoDiscover({ prefixes: ['--color-'] }) // 色だけ
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
明示 `bind()` なしで既存 token が全部触れるようになる。
|
|
169
|
+
|
|
170
|
+
### F3. Export — 現 state を patch として書き出し
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
creoEditor.export({ format: 'json' }) // 全 field の JSON
|
|
174
|
+
creoEditor.export({ format: 'css-patch' }) // 変更分のみ CSS
|
|
175
|
+
creoEditor.export({ format: 'yaml' }) // flat YAML
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
返り値は string のみ (clipboard 操作なし、consumer 側で `copy(out)` 等)。
|
|
179
|
+
|
|
180
|
+
### F4. URL shareable state
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
<EditorHostProvider config={{
|
|
184
|
+
urlSync: { autoSync: true, autoApply: true, key: 'creo' },
|
|
185
|
+
}}>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Editor で値を動かすたび `#creo=<base64>` が URL に付く。別 tab / 別 session で URL を開くと editor state が即時復元される。
|
|
189
|
+
|
|
190
|
+
```js
|
|
191
|
+
creoEditor.share() // 現 URL (更新済) を return
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### F5. Cross-tab sync (BroadcastChannel)
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
<EditorHostProvider config={{ crossTab: true }}>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
同 origin の複数 tab で editor values を双方向 sync。デザイナが mobile と desktop の 2 tab で同時確認、slider 操作が両方に反映。
|
|
201
|
+
|
|
202
|
+
## AI pair design with Claude (claude-in-chrome MCP)
|
|
203
|
+
|
|
204
|
+
`window.creoEditor` が expose されると、**専用 MCP server なしで** Claude が editor を操作できる:
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
// Claude が claude-in-chrome MCP の javascript_tool 経由で:
|
|
208
|
+
creoEditor.fields() // 現 editor state を inspect
|
|
209
|
+
creoEditor.slider('--spacing-m', 12, {...}) // 提案値で slider 追加
|
|
210
|
+
creoEditor.setValue('tokens.spacing.m', 20) // 直接 値変更
|
|
211
|
+
creoEditor.snapshot() // 実験前に保存
|
|
212
|
+
creoEditor.restore(snap) // 気に入らなければ戻す
|
|
213
|
+
creoEditor.export({ format: 'css-patch' }) // 最終 diff を取得
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Claude が screenshot で結果確認 → setValue で微調整 → export で commit 候補を生成、の **rebuild なしデザイン iteration loop**。
|
|
217
|
+
|
|
218
|
+
## コンセプト: Target × Control の 2 軸
|
|
219
|
+
|
|
220
|
+
| 軸 | 責務 | factory 例 |
|
|
221
|
+
|----|------|-----------|
|
|
222
|
+
| **Target** (データ源) | "何をどこから read/write" | `cssVarTarget` / `cssVarNumberTarget` / `signalTarget` / `localStorageTarget` / `ephemeralTarget` / `editorHostTarget` |
|
|
223
|
+
| **Control** (UI 操作体系) | "どう編集するか (widget)" | `number` / `color` / `boolean` / `select` / `string` / `readonlyText` |
|
|
224
|
+
|
|
225
|
+
直交なので、7 × 6 = **42 種の編集体験** を factory 組み合わせだけで作れる。新 Target source (GraphQL / SurrealDB / WebSocket) や新 Control widget (oklch-sliders / gradient stops / bezier) は既存を touch せず追加可能。
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
bind({ target: T, control: C, placement: P })
|
|
229
|
+
└───data───┘└───UI───┘ └──配置──┘
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## 4 方向 semantic layout (D-2 / D-3)
|
|
233
|
+
|
|
234
|
+
| Region | Semantic | 用途 |
|
|
235
|
+
|--------|----------|------|
|
|
236
|
+
| **TOP** | `global` | 全体設定 / 視線の起点 (theme select / shortcut hint) |
|
|
237
|
+
| **LEFT** | `source` | 時系列過去 / 参照 (ThemeEditor) |
|
|
238
|
+
| **RIGHT** | `tool` | 時系列未来 / ツール (field slider / picker panel) |
|
|
239
|
+
| **BOTTOM** | `utility` | ローカル utility (batch / AI chat — Phase 2 で充実) |
|
|
240
|
+
|
|
241
|
+
Placement `semantic` で field がどの region に出るか決まる。
|
|
242
|
+
|
|
243
|
+
## Content 非侵襲 (D-6)
|
|
244
|
+
|
|
245
|
+
Editor Layer は Content layer の座標・可視性を奪わない:
|
|
246
|
+
|
|
247
|
+
- `pointer-events: none` baseline、4 region のみ `auto`
|
|
248
|
+
- Mode OFF で `visibility: hidden` (Content は 100% 通常動作)
|
|
249
|
+
- 4 region は `color-mix(in oklch, ...)` で半透明 + `backdrop-filter: blur(...)`
|
|
250
|
+
- Mode ON/OFF で Content の DOM / layout / scroll 位置は不変
|
|
251
|
+
|
|
252
|
+
## Manual toggle のみ (D-7)
|
|
253
|
+
|
|
254
|
+
- `Ctrl+Shift+E` — Mode 切替 (configurable via `config.shortcut`)
|
|
255
|
+
- `Escape` — selection 解除 → Mode OFF の 2 段階退出
|
|
256
|
+
- `host.toggle()` / `host.mcp.enable()` — programmatic
|
|
257
|
+
|
|
258
|
+
自動 ON (hover 等) は存在しない。
|
|
259
|
+
|
|
260
|
+
## AI agent ready (D-10)
|
|
261
|
+
|
|
262
|
+
`useEditorHost().mcp` に `listFields` / `getValue` / `setValue` / `mode` / `enable` / `disable` を expose。Phase 2b の `@chronista-club/creoui-editor-host-mcp` (予定) がこれを stdio MCP server として公開、Claude 等から直接 field を操作できる。
|
|
263
|
+
|
|
264
|
+
```tsx
|
|
265
|
+
const host = useEditorHost()
|
|
266
|
+
host.mcp.setValue('tokens.spacing.m', 24) // 外部から書換、chain が走る
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## API Reference
|
|
270
|
+
|
|
271
|
+
### `<EditorHostProvider config?>`
|
|
272
|
+
|
|
273
|
+
```tsx
|
|
274
|
+
<EditorHostProvider config={{
|
|
275
|
+
shortcut: { ctrl: true, shift: true, key: 'e' },
|
|
276
|
+
localStorageNamespace: 'my-app',
|
|
277
|
+
initialMode: 'off',
|
|
278
|
+
}}>
|
|
279
|
+
{children}
|
|
280
|
+
</EditorHostProvider>
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Target factories
|
|
284
|
+
|
|
285
|
+
| Factory | 説明 |
|
|
286
|
+
|---------|------|
|
|
287
|
+
| `cssVarTarget(id, cssVar, initial)` | CSS custom property を string で read/write |
|
|
288
|
+
| `cssVarNumberTarget(id, cssVar, initial, unit='px')` | CSS 変数を number + unit で書く (slider と相性◎) |
|
|
289
|
+
| `signalTarget(id, accessor, setter)` | SolidJS signal に直結 |
|
|
290
|
+
| `localStorageTarget(id, initial, namespace?)` | localStorage に JSON 永続化 |
|
|
291
|
+
| `ephemeralTarget(id, initial)` | in-memory + subscribe 対応 |
|
|
292
|
+
| `editorHostTarget(id, host, initial)` | host.values に直結 (mcp / subscribe 全部委譲) |
|
|
293
|
+
|
|
294
|
+
Custom Target は `Target<T>` を満たせば OK:
|
|
295
|
+
```ts
|
|
296
|
+
interface Target<T> {
|
|
297
|
+
id: string; initial: T
|
|
298
|
+
get(): T; set(value: T): void
|
|
299
|
+
subscribe?(listener: (value: T) => void): () => void
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Control factories
|
|
304
|
+
|
|
305
|
+
| Factory | Variant | Kind |
|
|
306
|
+
|---------|---------|------|
|
|
307
|
+
| `number(opts?)` | `'slider' \| 'input' \| 'stepper'` | number |
|
|
308
|
+
| `color(opts?)` | `'picker' \| 'oklch-sliders' \| 'palette'` | color |
|
|
309
|
+
| `boolean(opts?)` | `'switch' \| 'checkbox'` | boolean |
|
|
310
|
+
| `select(options, variant?)` | `'dropdown' \| 'segmented'` | select |
|
|
311
|
+
| `string(variant?)` | `'input' \| 'textarea'` | string |
|
|
312
|
+
| `readonlyText()` | — | readonly-text |
|
|
313
|
+
|
|
314
|
+
### `bind({ target, control, placement })` → `Binder<T>`
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
interface Binder<T> extends Accessor<T> {
|
|
318
|
+
set(value: T): void // host 経由の 2-way sync で書き込み
|
|
319
|
+
readonly id: string
|
|
320
|
+
readonly target: Target<T>
|
|
321
|
+
readonly control: Control<T>
|
|
322
|
+
readonly placement: Placement
|
|
323
|
+
selectable(): (el: HTMLElement) => void // ref callback
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Accessor として読み、`.set()` で書き、`.selectable()` で selection ref を取る。
|
|
328
|
+
|
|
329
|
+
### Hooks
|
|
330
|
+
|
|
331
|
+
| Hook | 用途 |
|
|
332
|
+
|------|------|
|
|
333
|
+
| `useEditorHost()` | host 直接取得 |
|
|
334
|
+
| `useEditorMode()` | `Accessor<'on' \| 'off'>` |
|
|
335
|
+
| `useEditorSelection()` | `Accessor<SelectionInfo \| null>` |
|
|
336
|
+
| `useEditorHover()` | `Accessor<SelectionInfo \| null>` |
|
|
337
|
+
| `useEditorSelectable({ binders, id? })` | `ref={...}` 用 callback。binder 配列から `data-editor-fields` を set |
|
|
338
|
+
|
|
339
|
+
### Theme meta
|
|
340
|
+
|
|
341
|
+
8 theme (4 family × light/dark) の公式 meta は `creoui@0.1.0` に同梱。consumer は `THEME_INFO[themeId]` で取得。
|
|
342
|
+
|
|
343
|
+
```tsx
|
|
344
|
+
import { THEME_INFO, THEME_IDS, DEFAULT_THEME_ID, SWATCH_ROWS } from '@chronista-club/creoui-editor-host'
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## ファイル構成
|
|
348
|
+
|
|
349
|
+
```
|
|
350
|
+
src/
|
|
351
|
+
├── index.ts public API
|
|
352
|
+
├── types.ts internal: EditorField / EditorHost / SelectionInfo
|
|
353
|
+
├── host.ts internal: createEditorHost() core state
|
|
354
|
+
├── host.test.ts 19 cases (core)
|
|
355
|
+
├── selection.ts internal: DOM hover/click + ResizeObserver
|
|
356
|
+
├── shortcut.ts internal: Ctrl+Shift+E handler
|
|
357
|
+
├── provider.tsx <EditorHostProvider> + useEditorHost()
|
|
358
|
+
├── hooks.ts public hooks (useEditorMode / Selectable 等)
|
|
359
|
+
├── target.ts ⭐ Target interface + 6 factory
|
|
360
|
+
├── target.test.ts 8 cases
|
|
361
|
+
├── control.ts ⭐ Control types + 6 factory
|
|
362
|
+
├── control.test.ts 5 cases
|
|
363
|
+
├── binder.ts ⭐ bind() conductor
|
|
364
|
+
├── fields.tsx internal: FieldEditor (Control kind で分岐)
|
|
365
|
+
├── theme-editor.tsx ThemeEditor (LEFT region 実装)
|
|
366
|
+
├── theme-info.ts 8 theme meta + SWATCH_ROWS
|
|
367
|
+
└── layer.tsx <EditorLayer> 4 region overlay root
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Roadmap
|
|
371
|
+
|
|
372
|
+
- ✅ Step 1-4: core state / handlers / UI / README
|
|
373
|
+
- ✅ Step 5: Target × Control 分離 + `bind()`
|
|
374
|
+
- ⏳ Step 6: `editor-host-v0.1.0` publish + examples/web-demo を consumer に rewrite
|
|
375
|
+
- 📋 M4: MCP server (`editor_mode_*` stdio tool)
|
|
376
|
+
- 📋 M5: Swift runtime (SwiftUI + NSAppearance)
|
|
377
|
+
- 📋 M6: Custom Control plugin (oklch-sliders / gradient / spline)
|
|
378
|
+
|
|
379
|
+
## License
|
|
380
|
+
|
|
381
|
+
Apache-2.0 — [LICENSE](https://github.com/chronista-club/creoui/blob/main/LICENSE)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Owner } from 'solid-js';
|
|
2
|
+
import { Binder } from './binder';
|
|
3
|
+
import { EditorHost, EditorSemantic } from './types';
|
|
4
|
+
export interface AutoDiscoverOptions {
|
|
5
|
+
/** どの CSS var prefix を拾うか (default: 4 種) */
|
|
6
|
+
prefixes?: readonly string[];
|
|
7
|
+
/** 配置 (default: 'tool') */
|
|
8
|
+
semantic?: EditorSemantic;
|
|
9
|
+
/** 既に register 済みの id は skip (default: true) */
|
|
10
|
+
skipExisting?: boolean;
|
|
11
|
+
/** field.order の start (default: 1000、既存 0-99 と衝突しない) */
|
|
12
|
+
placementOrderStart?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface DiscoveredVar {
|
|
15
|
+
cssVar: string;
|
|
16
|
+
id: string;
|
|
17
|
+
value: string;
|
|
18
|
+
kind: 'color' | 'number' | 'unknown';
|
|
19
|
+
numericValue?: number;
|
|
20
|
+
unit?: string;
|
|
21
|
+
}
|
|
22
|
+
/** :root の computed style から prefix match の CSS var を列挙 */
|
|
23
|
+
export declare function scanCssVars(prefixes?: readonly string[]): DiscoveredVar[];
|
|
24
|
+
declare function inferType(cssVar: string, raw: string): DiscoveredVar;
|
|
25
|
+
/** '--color-brand-primary' → 'color.brand.primary' */
|
|
26
|
+
declare function cssVarToId(cssVar: string): string;
|
|
27
|
+
declare function heuristicRange(value: number): {
|
|
28
|
+
min: number;
|
|
29
|
+
max: number;
|
|
30
|
+
step: number;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* 現 DOM の CSS 変数を scan、適切な Target + Control で bind する。
|
|
34
|
+
* 成功した binder の配列を返す。
|
|
35
|
+
*
|
|
36
|
+
* owner: provider の getOwner() を渡すと console / effect 経由で呼ばれる時の
|
|
37
|
+
* SolidJS context が維持される (重要: 呼び出し元の runWithOwner でも同等)。
|
|
38
|
+
*/
|
|
39
|
+
export declare function autoDiscover(host: EditorHost, owner: Owner | null, opts?: AutoDiscoverOptions): Binder[];
|
|
40
|
+
export declare const __test__: {
|
|
41
|
+
scanCssVars: typeof scanCssVars;
|
|
42
|
+
inferType: typeof inferType;
|
|
43
|
+
cssVarToId: typeof cssVarToId;
|
|
44
|
+
heuristicRange: typeof heuristicRange;
|
|
45
|
+
};
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=auto-discover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-discover.d.ts","sourceRoot":"","sources":["../src/auto-discover.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,KAAK,KAAK,EAAgB,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,UAAU,CAAA;AAG5C,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAczD,MAAM,WAAW,mBAAmB;IAClC,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC5B,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,gDAAgD;IAChD,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,yDAAyD;IACzD,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAA;IACpC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,2DAA2D;AAC3D,wBAAgB,WAAW,CAAC,QAAQ,GAAE,SAAS,MAAM,EAAqB,GAAG,aAAa,EAAE,CAuB3F;AAED,iBAAS,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CAiB7D;AAED,sDAAsD;AACtD,iBAAS,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1C;AAED,iBAAS,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAOjF;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,IAAI,GAAE,mBAAwB,GAC7B,MAAM,EAAE,CA+CV;AAED,eAAO,MAAM,QAAQ;;;;;CAAyD,CAAA"}
|
package/dist/binder.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Accessor } from 'solid-js';
|
|
2
|
+
import { Control } from './control';
|
|
3
|
+
import { Target } from './target';
|
|
4
|
+
import { EditorPersistence, EditorRole, EditorSemantic } from './types';
|
|
5
|
+
export interface Placement {
|
|
6
|
+
label: string;
|
|
7
|
+
semantic: EditorSemantic;
|
|
8
|
+
group?: string;
|
|
9
|
+
order?: number;
|
|
10
|
+
role?: EditorRole;
|
|
11
|
+
persistence?: EditorPersistence;
|
|
12
|
+
}
|
|
13
|
+
export interface BindOptions<T> {
|
|
14
|
+
target: Target<T>;
|
|
15
|
+
control: Control<T>;
|
|
16
|
+
placement: Placement;
|
|
17
|
+
}
|
|
18
|
+
export interface Binder<T = any> extends Accessor<T> {
|
|
19
|
+
set(value: T): void;
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly target: Target<T>;
|
|
22
|
+
readonly control: Control<T>;
|
|
23
|
+
readonly placement: Placement;
|
|
24
|
+
/** ref callback: el.dataset.editorFields = this.id */
|
|
25
|
+
selectable(): (el: HTMLElement) => void;
|
|
26
|
+
}
|
|
27
|
+
export declare function bind<T>(opts: BindOptions<T>): Binder<T>;
|
|
28
|
+
//# sourceMappingURL=binder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binder.d.ts","sourceRoot":"","sources":["../src/binder.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACtC,OAAO,KAAK,EAIV,iBAAiB,EACjB,UAAU,EACV,cAAc,EACf,MAAM,SAAS,CAAA;AAEhB,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,cAAc,CAAA;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,WAAW,CAAC,EAAE,iBAAiB,CAAA;CAChC;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;IACjB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IACnB,SAAS,EAAE,SAAS,CAAA;CACrB;AAGD,MAAM,WAAW,MAAM,CAAC,CAAC,GAAG,GAAG,CAAE,SAAQ,QAAQ,CAAC,CAAC,CAAC;IAClD,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAA;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,sDAAsD;IACtD,UAAU,IAAI,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,CAAA;CACxC;AAsBD,wBAAgB,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAgEvD"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Owner } from 'solid-js';
|
|
2
|
+
import { BindOptions, Binder } from './binder';
|
|
3
|
+
import { BooleanControl, ColorControl, NumberControl, SelectControl, StringControl, boolean as booleanControl, color, number, readonlyText, select, string as stringControl } from './control';
|
|
4
|
+
import { cssVarNumberTarget, cssVarTarget, editorHostTarget, ephemeralTarget, localStorageTarget, signalTarget } from './target';
|
|
5
|
+
import { EditorField, EditorHost, EditorMode, EditorRole, EditorSemantic } from './types';
|
|
6
|
+
export interface ConsoleApi {
|
|
7
|
+
readonly host: EditorHost;
|
|
8
|
+
readonly version: string;
|
|
9
|
+
bind<T>(opts: BindOptions<T>): Binder<T>;
|
|
10
|
+
readonly t: {
|
|
11
|
+
cssVar: typeof cssVarTarget;
|
|
12
|
+
cssVarNumber: typeof cssVarNumberTarget;
|
|
13
|
+
signal: typeof signalTarget;
|
|
14
|
+
localStorage: typeof localStorageTarget;
|
|
15
|
+
ephemeral: typeof ephemeralTarget;
|
|
16
|
+
host: typeof editorHostTarget;
|
|
17
|
+
};
|
|
18
|
+
readonly c: {
|
|
19
|
+
number: typeof number;
|
|
20
|
+
color: typeof color;
|
|
21
|
+
boolean: typeof booleanControl;
|
|
22
|
+
select: typeof select;
|
|
23
|
+
string: typeof stringControl;
|
|
24
|
+
readonlyText: typeof readonlyText;
|
|
25
|
+
};
|
|
26
|
+
slider(cssVar: string, initial: number, opts?: Omit<NumberControl, 'kind'> & {
|
|
27
|
+
label?: string;
|
|
28
|
+
semantic?: EditorSemantic;
|
|
29
|
+
}): Binder<number>;
|
|
30
|
+
picker(cssVar: string, initial: string, opts?: Omit<ColorControl, 'kind'> & {
|
|
31
|
+
label?: string;
|
|
32
|
+
semantic?: EditorSemantic;
|
|
33
|
+
}): Binder<string>;
|
|
34
|
+
flip(id: string, initial: boolean, opts?: Omit<BooleanControl, 'kind'> & {
|
|
35
|
+
label?: string;
|
|
36
|
+
semantic?: EditorSemantic;
|
|
37
|
+
}): Binder<boolean>;
|
|
38
|
+
chooser(id: string, initial: string, options: readonly string[], opts?: Omit<SelectControl, 'kind' | 'options'> & {
|
|
39
|
+
label?: string;
|
|
40
|
+
semantic?: EditorSemantic;
|
|
41
|
+
}): Binder<string>;
|
|
42
|
+
text(id: string, initial: string, opts?: Omit<StringControl, 'kind'> & {
|
|
43
|
+
label?: string;
|
|
44
|
+
semantic?: EditorSemantic;
|
|
45
|
+
}): Binder<string>;
|
|
46
|
+
getValue<T>(id: string): T | undefined;
|
|
47
|
+
setValue<T>(id: string, value: T): void;
|
|
48
|
+
fields(filter?: {
|
|
49
|
+
semantic?: EditorSemantic;
|
|
50
|
+
role?: EditorRole;
|
|
51
|
+
}): EditorField[];
|
|
52
|
+
values(): Record<string, unknown>;
|
|
53
|
+
describe(id: string): void;
|
|
54
|
+
snapshot(): Record<string, unknown>;
|
|
55
|
+
restore(snapshot: Record<string, unknown>): void;
|
|
56
|
+
readonly mode: {
|
|
57
|
+
enable(): void;
|
|
58
|
+
disable(): void;
|
|
59
|
+
toggle(): void;
|
|
60
|
+
is(): EditorMode;
|
|
61
|
+
};
|
|
62
|
+
share(): string;
|
|
63
|
+
export(opts?: {
|
|
64
|
+
format?: 'json' | 'yaml' | 'css' | 'css-patch';
|
|
65
|
+
onlyChanged?: boolean;
|
|
66
|
+
indent?: number;
|
|
67
|
+
}): string;
|
|
68
|
+
autoDiscover(opts?: {
|
|
69
|
+
prefixes?: readonly string[];
|
|
70
|
+
semantic?: EditorSemantic;
|
|
71
|
+
skipExisting?: boolean;
|
|
72
|
+
}): Binder[];
|
|
73
|
+
/**
|
|
74
|
+
* 現 editor state を dev server の `/_creo/tokens/commit` に POST し、
|
|
75
|
+
* tokens/*.json の `$value` を書き換える。Vite plugin `creoTokensPlugin` が
|
|
76
|
+
* 同 endpoint を listen している前提。production では fetch 失敗 or 404 が返る。
|
|
77
|
+
*/
|
|
78
|
+
commitToTokens(opts?: {
|
|
79
|
+
endpoint?: string;
|
|
80
|
+
onlyChanged?: boolean;
|
|
81
|
+
}): Promise<{
|
|
82
|
+
applied: {
|
|
83
|
+
id: string;
|
|
84
|
+
value: unknown;
|
|
85
|
+
file: string;
|
|
86
|
+
}[];
|
|
87
|
+
skipped: string[];
|
|
88
|
+
}>;
|
|
89
|
+
help(): void;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Provider から注入される sibling feature 群。循環依存を避けるため interface で
|
|
93
|
+
* 受ける (実装は export.ts / url-sync.ts / auto-discover.ts が提供)。
|
|
94
|
+
*/
|
|
95
|
+
export interface ConsoleApiDeps {
|
|
96
|
+
host: EditorHost;
|
|
97
|
+
/** provider の onMount で保持した getOwner() の結果 */
|
|
98
|
+
owner: Owner | null;
|
|
99
|
+
exportSnapshot: (host: EditorHost, opts?: {
|
|
100
|
+
format?: 'json' | 'yaml' | 'css' | 'css-patch';
|
|
101
|
+
onlyChanged?: boolean;
|
|
102
|
+
indent?: number;
|
|
103
|
+
}) => string;
|
|
104
|
+
shareUrl: (host: EditorHost) => string;
|
|
105
|
+
autoDiscover: (host: EditorHost, owner: Owner | null, opts?: {
|
|
106
|
+
prefixes?: readonly string[];
|
|
107
|
+
semantic?: EditorSemantic;
|
|
108
|
+
skipExisting?: boolean;
|
|
109
|
+
}) => Binder[];
|
|
110
|
+
}
|
|
111
|
+
/** provider 側で deps を wire して console API object を構築する */
|
|
112
|
+
export declare function buildConsoleApi(deps: ConsoleApiDeps): ConsoleApi;
|
|
113
|
+
/** window[name] に api を expose。戻り値で uninstall (delete) */
|
|
114
|
+
export declare function installConsoleApi(api: ConsoleApi, name?: string): () => void;
|
|
115
|
+
//# sourceMappingURL=console.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../src/console.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @chronista-club/creoui-editor-host — Control (UI 操作体系)
|
|
3
|
+
*
|
|
4
|
+
* "どう編集するか" の抽象。Target (データ源) と直交。kind で型を、variant で
|
|
5
|
+
* UI widget の好みを表す。
|
|
6
|
+
*
|
|
7
|
+
* Built-in kinds: number / color / boolean / select / string / readonly-text
|
|
8
|
+
*
|
|
9
|
+
* consumer が独自 Control kind を追加したい場合は `Control<T>` に `'custom'`
|
|
10
|
+
* 系の extension を作り、FieldEditor 側で `registerControlRenderer` で表示
|
|
11
|
+
* (Phase 2 で拡張予定)。
|
|
12
|
+
*/
|
|
13
|
+
export type NumberVariant = 'slider' | 'input' | 'stepper';
|
|
14
|
+
export type ColorVariant = 'picker' | 'oklch-sliders' | 'palette';
|
|
15
|
+
export type BooleanVariant = 'switch' | 'checkbox';
|
|
16
|
+
export type SelectVariant = 'dropdown' | 'segmented';
|
|
17
|
+
export type StringVariant = 'input' | 'textarea';
|
|
18
|
+
export interface NumberControl {
|
|
19
|
+
kind: 'number';
|
|
20
|
+
min?: number;
|
|
21
|
+
max?: number;
|
|
22
|
+
step?: number;
|
|
23
|
+
unit?: string;
|
|
24
|
+
variant?: NumberVariant;
|
|
25
|
+
}
|
|
26
|
+
export interface ColorControl {
|
|
27
|
+
kind: 'color';
|
|
28
|
+
variant?: ColorVariant;
|
|
29
|
+
palette?: readonly string[];
|
|
30
|
+
}
|
|
31
|
+
export interface BooleanControl {
|
|
32
|
+
kind: 'boolean';
|
|
33
|
+
variant?: BooleanVariant;
|
|
34
|
+
}
|
|
35
|
+
export interface SelectControl {
|
|
36
|
+
kind: 'select';
|
|
37
|
+
options: readonly string[];
|
|
38
|
+
variant?: SelectVariant;
|
|
39
|
+
}
|
|
40
|
+
export interface StringControl {
|
|
41
|
+
kind: 'string';
|
|
42
|
+
variant?: StringVariant;
|
|
43
|
+
}
|
|
44
|
+
export interface ReadonlyTextControl {
|
|
45
|
+
kind: 'readonly-text';
|
|
46
|
+
}
|
|
47
|
+
export type Control<_T = any> = NumberControl | ColorControl | BooleanControl | SelectControl | StringControl | ReadonlyTextControl;
|
|
48
|
+
export declare function number(opts?: Omit<NumberControl, 'kind'>): NumberControl;
|
|
49
|
+
export declare function color(opts?: Omit<ColorControl, 'kind'>): ColorControl;
|
|
50
|
+
export declare function boolean(opts?: Omit<BooleanControl, 'kind'>): BooleanControl;
|
|
51
|
+
export declare function select(options: readonly string[], variant?: SelectVariant): SelectControl;
|
|
52
|
+
export declare function string(variant?: StringVariant): StringControl;
|
|
53
|
+
export declare function readonlyText(): ReadonlyTextControl;
|
|
54
|
+
//# sourceMappingURL=control.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control.d.ts","sourceRoot":"","sources":["../src/control.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAA;AAC1D,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,eAAe,GAAG,SAAS,CAAA;AACjE,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,UAAU,CAAA;AAClD,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,WAAW,CAAA;AACpD,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,UAAU,CAAA;AAEhD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,QAAQ,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,aAAa,CAAA;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAA;IACf,OAAO,CAAC,EAAE,cAAc,CAAA;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,QAAQ,CAAA;IACd,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1B,OAAO,CAAC,EAAE,aAAa,CAAA;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,QAAQ,CAAA;IACd,OAAO,CAAC,EAAE,aAAa,CAAA;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,eAAe,CAAA;CACtB;AAGD,MAAM,MAAM,OAAO,CAAC,EAAE,GAAG,GAAG,IACxB,aAAa,GACb,YAAY,GACZ,cAAc,GACd,aAAa,GACb,aAAa,GACb,mBAAmB,CAAA;AAIvB,wBAAgB,MAAM,CAAC,IAAI,GAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAM,GAAG,aAAa,CAE5E;AAED,wBAAgB,KAAK,CAAC,IAAI,GAAE,IAAI,CAAC,YAAY,EAAE,MAAM,CAAM,GAAG,YAAY,CAEzE;AAED,wBAAgB,OAAO,CAAC,IAAI,GAAE,IAAI,CAAC,cAAc,EAAE,MAAM,CAAM,GAAG,cAAc,CAE/E;AAED,wBAAgB,MAAM,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,aAAa,CAEzF;AAED,wBAAgB,MAAM,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,aAAa,CAE7D;AAED,wBAAgB,YAAY,IAAI,mBAAmB,CAElD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { EditorHost } from './types';
|
|
2
|
+
export interface CrossTabOptions {
|
|
3
|
+
/** BroadcastChannel 名 (default: '@chronista-club/creoui-editor-host:{namespace}') */
|
|
4
|
+
channel?: string;
|
|
5
|
+
/** namespace (localStorageNamespace と同値を期待、default '@chronista-club/creoui-editor-host') */
|
|
6
|
+
namespace?: string;
|
|
7
|
+
}
|
|
8
|
+
declare function defaultChannelName(namespace: string): string;
|
|
9
|
+
declare function generateTabId(): string;
|
|
10
|
+
export declare function installCrossTabSync(host: EditorHost, opts?: CrossTabOptions): () => void;
|
|
11
|
+
export declare const __test__: {
|
|
12
|
+
defaultChannelName: typeof defaultChannelName;
|
|
13
|
+
generateTabId: typeof generateTabId;
|
|
14
|
+
};
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=cross-tab.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-tab.d.ts","sourceRoot":"","sources":["../src/cross-tab.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC,MAAM,WAAW,eAAe;IAC9B,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAOD,iBAAS,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,iBAAS,aAAa,IAAI,MAAM,CAK/B;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,GAAE,eAAoB,GAAG,MAAM,IAAI,CAgE5F;AAED,eAAO,MAAM,QAAQ;;;CAAwC,CAAA"}
|