editor-ts 0.0.1 → 0.0.12
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 +197 -318
- package/index.ts +70 -0
- package/package.json +36 -11
- package/src/core/ComponentManager.ts +697 -6
- package/src/core/ComponentPalette.ts +109 -0
- package/src/core/CustomComponentRegistry.ts +74 -0
- package/src/core/KeyboardShortcuts.ts +220 -0
- package/src/core/LayerManager.ts +378 -0
- package/src/core/Page.ts +24 -5
- package/src/core/StorageManager.ts +447 -0
- package/src/core/StyleManager.ts +38 -2
- package/src/core/VersionControl.ts +189 -0
- package/src/core/aiChat.ts +427 -0
- package/src/core/iframeCanvas.ts +672 -0
- package/src/core/init.ts +3081 -248
- package/src/server/bun_server.ts +155 -0
- package/src/server/cf_worker.ts +225 -0
- package/src/server/schema.ts +21 -0
- package/src/server/sync.ts +195 -0
- package/src/types/sqlocal.d.ts +6 -0
- package/src/types.ts +591 -18
- package/src/utils/toolbar.ts +15 -1
package/README.md
CHANGED
|
@@ -1,401 +1,280 @@
|
|
|
1
|
-
|
|
2
1
|
# EditorTs
|
|
3
2
|
|
|
4
|
-
DO NOT USE THIS IN PRODUCTION YET
|
|
5
|
-
|
|
6
|
-
A powerful TypeScript library for editing HTML content while maintaining its structure in a JSON representation. EditorTs provides a simple `init()` function to create a fully-functional WYSIWYG editor with minimal configuration.
|
|
7
|
-
|
|
8
|
-
## Features
|
|
3
|
+
DO NOT USE THIS IN PRODUCTION YET. Early development.
|
|
9
4
|
|
|
10
|
-
|
|
11
|
-
- **WYSIWYG Editing**: Click-to-edit interface with context toolbars
|
|
12
|
-
- **Runtime Configuration**: Toolbars configured in JavaScript, not stored in JSON
|
|
13
|
-
- **Iframe Sandboxing**: Template runs in isolated iframe for proper CSS/JS scoping
|
|
14
|
-
- **Event System**: Hook into component selection, editing, deletion, etc.
|
|
15
|
-
- **Type-Safe**: Full TypeScript support
|
|
16
|
-
- **Clean JSON**: Only stores component metadata and styles, no behavioral config
|
|
5
|
+
EditorTs is a TypeScript library for editing HTML content while keeping the source of truth in clean, portable JSON (components/styles/assets). The editor runtime (toolbars, permissions, UI layout, event handlers) stays in JavaScript.
|
|
17
6
|
|
|
18
|
-
##
|
|
7
|
+
## Quickstart (run the demo)
|
|
19
8
|
|
|
20
9
|
```bash
|
|
21
10
|
bun install
|
|
22
11
|
bun run dev
|
|
23
12
|
```
|
|
24
13
|
|
|
25
|
-
Open http://localhost:5021
|
|
14
|
+
Open `http://localhost:5021`.
|
|
15
|
+
|
|
16
|
+
The demo UI is `index.html` and is wired by `examples/quickstart.ts`.
|
|
17
|
+
|
|
18
|
+
## Core concepts
|
|
19
|
+
|
|
20
|
+
### Data vs runtime config
|
|
21
|
+
|
|
22
|
+
- JSON (“data”): components, styles/CSS, assets.
|
|
23
|
+
- JS (“runtime”): toolbars, UI wiring, event handlers, editor behaviors.
|
|
24
|
+
|
|
25
|
+
This separation is intentional: the same JSON can be used in different apps with different editor experiences.
|
|
26
|
+
|
|
27
|
+
### Components-first rendering
|
|
28
|
+
|
|
29
|
+
When both are present:
|
|
30
|
+
- `components` are the source of truth.
|
|
31
|
+
- `Page.getHTML()` renders from components.
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
When only HTML is present:
|
|
34
|
+
- HTML can be converted to components when DOM is available.
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
### Minimal init
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { init, type PageData } from 'editorts'
|
|
32
42
|
|
|
33
|
-
// Initialize editor with one function call
|
|
34
43
|
const editor = init({
|
|
35
|
-
|
|
36
|
-
data: pageData,
|
|
37
|
-
|
|
38
|
-
// Configure toolbars (runtime only, not saved to JSON)
|
|
39
|
-
toolbars: {
|
|
40
|
-
byId: {
|
|
41
|
-
'header': {
|
|
42
|
-
enabled: true,
|
|
43
|
-
actions: [
|
|
44
|
-
{ id: 'edit', label: 'Edit', icon: '✏️', enabled: true },
|
|
45
|
-
{ id: 'duplicate', label: 'Duplicate', icon: '📋', enabled: true }
|
|
46
|
-
]
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
byType: {
|
|
50
|
-
'custom-code': {
|
|
51
|
-
actions: [
|
|
52
|
-
{ id: 'editJS', label: 'Edit Code', icon: '📜', enabled: true }
|
|
53
|
-
]
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
|
|
58
|
-
// Optional callbacks
|
|
59
|
-
onComponentSelect: (component) => {
|
|
60
|
-
console.log('Selected:', component.attributes?.id);
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// Access the Page API
|
|
65
|
-
editor.page.components.findById('header');
|
|
66
|
-
editor.page.styles.updateStyle('#header', { 'color': 'red' });
|
|
67
|
-
|
|
68
|
-
// Add event listeners
|
|
69
|
-
editor.on('componentEdit', (component) => {
|
|
70
|
-
console.log('Editing:', component);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Save the page (clean JSON, no toolbar configs)
|
|
74
|
-
const json = editor.save();
|
|
44
|
+
iframeId: 'preview-iframe',
|
|
45
|
+
data: pageData satisfies PageData,
|
|
46
|
+
})
|
|
75
47
|
```
|
|
76
48
|
|
|
77
|
-
|
|
49
|
+
### Storage adapters
|
|
78
50
|
|
|
79
|
-
|
|
51
|
+
Local storage is the default, but we recommend SQLocal for persistent, browser-native SQLite storage. SQLocal requires cross-origin isolation headers, so the easiest way is to use the Vite examples in `examples/localsql` or `examples/solid`.
|
|
80
52
|
|
|
81
|
-
|
|
53
|
+
```ts
|
|
54
|
+
import { init } from 'editorts'
|
|
55
|
+
import { SQLocal } from 'sqlocal'
|
|
82
56
|
|
|
83
|
-
|
|
84
|
-
- Component metadata (type, attributes, tagName)
|
|
85
|
-
- Styling (CSS, inline styles)
|
|
86
|
-
- Content (text, HTML, assets)
|
|
87
|
-
- Component structure
|
|
57
|
+
const sqlocalClient = new SQLocal('editorts.sqlite')
|
|
88
58
|
|
|
89
|
-
|
|
90
|
-
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
59
|
+
const editor = init({
|
|
60
|
+
iframeId: 'preview-iframe',
|
|
61
|
+
data: pageData,
|
|
62
|
+
storage: {
|
|
63
|
+
type: 'sqlocal',
|
|
64
|
+
client: sqlocalClient,
|
|
65
|
+
// databaseName: 'editorts.sqlite', // when not passing client
|
|
66
|
+
},
|
|
67
|
+
})
|
|
68
|
+
```
|
|
95
69
|
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
// JSON stays clean
|
|
99
|
-
const page = new Page(jsonData);
|
|
70
|
+
Run the SQLocal demos (Vite):
|
|
100
71
|
|
|
101
|
-
|
|
102
|
-
|
|
72
|
+
```bash
|
|
73
|
+
cd examples/localsql
|
|
74
|
+
bun install
|
|
75
|
+
bun run dev
|
|
76
|
+
```
|
|
103
77
|
|
|
104
|
-
|
|
105
|
-
|
|
78
|
+
```bash
|
|
79
|
+
cd examples/solid
|
|
80
|
+
bun install
|
|
81
|
+
bun run dev
|
|
106
82
|
```
|
|
107
83
|
|
|
108
|
-
|
|
84
|
+
Open `http://localhost:5173`.
|
|
85
|
+
|
|
86
|
+
### Toolbars (runtime only)
|
|
109
87
|
|
|
110
|
-
|
|
88
|
+
```ts
|
|
89
|
+
import { init } from 'editorts'
|
|
111
90
|
|
|
112
|
-
```typescript
|
|
113
91
|
const editor = init({
|
|
114
|
-
|
|
92
|
+
iframeId: 'preview-iframe',
|
|
115
93
|
data: pageData,
|
|
116
94
|
toolbars: {
|
|
117
|
-
// By component ID
|
|
118
95
|
byId: {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
'box': { actions: [...] }
|
|
127
|
-
},
|
|
128
|
-
|
|
129
|
-
// By tag name
|
|
130
|
-
byTag: {
|
|
131
|
-
'div': { actions: [...] }
|
|
96
|
+
header: {
|
|
97
|
+
enabled: true,
|
|
98
|
+
actions: [
|
|
99
|
+
{ id: 'edit', label: 'Edit', icon: '✏️', enabled: true },
|
|
100
|
+
{ id: 'duplicate', label: 'Duplicate', icon: '📋', enabled: true },
|
|
101
|
+
],
|
|
102
|
+
},
|
|
132
103
|
},
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
default: { actions: [...] }
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// Or configure after init
|
|
140
|
-
editor.page.toolbars.configureById('sidebar', {
|
|
141
|
-
enabled: true,
|
|
142
|
-
actions: [
|
|
143
|
-
{ id: 'edit', label: 'Edit', icon: '✏️', enabled: true },
|
|
144
|
-
{ id: 'delete', label: 'Delete', icon: '🗑️', enabled: false, danger: true }
|
|
145
|
-
]
|
|
146
|
-
});
|
|
104
|
+
},
|
|
105
|
+
})
|
|
147
106
|
```
|
|
148
107
|
|
|
149
|
-
###
|
|
150
|
-
|
|
151
|
-
- **Edit (✏️)** - Edit component properties
|
|
152
|
-
- **Edit JS (📜)** - Edit component JavaScript with Monaco editor
|
|
153
|
-
- **Duplicate (📋)** - Create a copy of the component
|
|
154
|
-
- **Delete (🗑️)** - Remove component from page
|
|
108
|
+
### UI containers (you own the layout)
|
|
155
109
|
|
|
156
|
-
|
|
110
|
+
EditorTs does not create your sidebar/tabs/layout. You provide containers and `init()` wires them.
|
|
157
111
|
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
**Returns:** EditorTsEditor instance
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
interface EditorTsEditor {
|
|
184
|
-
page: Page; // Full Page instance
|
|
185
|
-
on(event, callback): void; // Add event listener
|
|
186
|
-
off(event, callback): void; // Remove event listener
|
|
187
|
-
refresh(): void; // Refresh preview
|
|
188
|
-
save(): string; // Export JSON
|
|
189
|
-
destroy(): void; // Clean up
|
|
190
|
-
elements: { // Access DOM elements
|
|
191
|
-
container: HTMLElement;
|
|
192
|
-
sidebar?: HTMLElement;
|
|
193
|
-
canvas: HTMLElement;
|
|
194
|
-
iframe: HTMLIFrameElement;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
112
|
+
```ts
|
|
113
|
+
const editor = init({
|
|
114
|
+
iframeId: 'preview-iframe',
|
|
115
|
+
data: pageData,
|
|
116
|
+
ui: {
|
|
117
|
+
stats: { containerId: 'stats-container' },
|
|
118
|
+
layers: { containerId: 'layers-container' },
|
|
119
|
+
selectedInfo: { containerId: 'selected-info' },
|
|
120
|
+
viewTabs: {
|
|
121
|
+
editorButtonId: 'tab-editor',
|
|
122
|
+
codeButtonId: 'tab-code',
|
|
123
|
+
defaultView: 'editor',
|
|
124
|
+
},
|
|
125
|
+
editors: {
|
|
126
|
+
js: { containerId: 'js-editor-container' },
|
|
127
|
+
css: { containerId: 'css-editor-container' },
|
|
128
|
+
json: { containerId: 'json-editor-container' },
|
|
129
|
+
jsx: { containerId: 'jsx-editor-container' },
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
})
|
|
197
133
|
```
|
|
198
134
|
|
|
199
|
-
|
|
200
|
-
- `componentSelect` - When component is clicked
|
|
201
|
-
- `componentEdit` - When edit action is triggered
|
|
202
|
-
- `componentDuplicate` - When component is duplicated
|
|
203
|
-
- `componentDelete` - When component is deleted
|
|
204
|
-
- `componentEditJS` - When Edit JS is triggered
|
|
135
|
+
## Built-in code editors
|
|
205
136
|
|
|
206
|
-
|
|
137
|
+
EditorTs can render editors into your containers:
|
|
207
138
|
|
|
208
|
-
|
|
139
|
+
- Default: `textarea` (zero deps)
|
|
140
|
+
- Optional: `modern-monaco` (syntax highlighting)
|
|
209
141
|
|
|
210
|
-
```
|
|
211
|
-
|
|
142
|
+
```ts
|
|
143
|
+
const editor = init({
|
|
144
|
+
iframeId: 'preview-iframe',
|
|
145
|
+
data: pageData,
|
|
146
|
+
codeEditor: { provider: 'modern-monaco' },
|
|
147
|
+
})
|
|
148
|
+
```
|
|
212
149
|
|
|
213
|
-
|
|
150
|
+
Notes:
|
|
151
|
+
- `modern-monaco` is an optional peer dependency.
|
|
152
|
+
- `typescript` is an optional peer dependency (used for TSX/JSX parsing).
|
|
214
153
|
|
|
215
|
-
|
|
216
|
-
page.getTitle();
|
|
217
|
-
page.setTitle(title);
|
|
218
|
-
page.getHTML();
|
|
219
|
-
page.getCSS();
|
|
220
|
-
page.toJSON();
|
|
221
|
-
page.clone();
|
|
154
|
+
## Component conversions
|
|
222
155
|
|
|
223
|
-
|
|
224
|
-
page.components.findById(id);
|
|
225
|
-
page.components.updateComponent(id, updates);
|
|
226
|
-
page.styles.updateStyle(selector, properties);
|
|
227
|
-
page.assets.getImages();
|
|
228
|
-
page.toolbars.configureById(id, config);
|
|
229
|
-
```
|
|
156
|
+
### Components → HTML
|
|
230
157
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
```typescript
|
|
234
|
-
page.components.find(query);
|
|
235
|
-
page.components.findById(id);
|
|
236
|
-
page.components.findByType(type);
|
|
237
|
-
page.components.addComponent(component);
|
|
238
|
-
page.components.removeComponent(id);
|
|
239
|
-
page.components.updateComponent(id, updates);
|
|
240
|
-
page.components.getAll();
|
|
241
|
-
page.components.count();
|
|
158
|
+
```ts
|
|
159
|
+
const html = editor.page.components.toHTML()
|
|
242
160
|
```
|
|
243
161
|
|
|
244
|
-
###
|
|
162
|
+
### HTML → Components
|
|
245
163
|
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
page.
|
|
249
|
-
page.styles.addStyle(style);
|
|
250
|
-
page.styles.updateStyle(selector, properties);
|
|
251
|
-
page.styles.removeBySelector(selector);
|
|
252
|
-
page.styles.compileToCSS();
|
|
253
|
-
page.styles.getAll();
|
|
164
|
+
```ts
|
|
165
|
+
// Requires DOM (browser). Server-side: inject an adapter or it will warn and no-op.
|
|
166
|
+
editor.page.components.setFromHTML('<body><div id="root">Hello</div></body>')
|
|
254
167
|
```
|
|
255
168
|
|
|
256
|
-
###
|
|
169
|
+
### Components → JSX/TSX
|
|
257
170
|
|
|
258
|
-
```
|
|
259
|
-
page.
|
|
260
|
-
page.assets.getImages();
|
|
261
|
-
page.assets.addAsset(asset);
|
|
262
|
-
page.assets.removeAsset(src);
|
|
263
|
-
page.assets.updateAsset(src, updates);
|
|
264
|
-
page.assets.getAll();
|
|
171
|
+
```ts
|
|
172
|
+
const jsxSource = editor.page.components.toJSX({ pretty: true })
|
|
265
173
|
```
|
|
266
174
|
|
|
267
|
-
|
|
175
|
+
`toJSX()` outputs React-style function components named from `attributes.id` when possible.
|
|
268
176
|
|
|
269
|
-
|
|
270
|
-
page.toolbars.configureById(id, config);
|
|
271
|
-
page.toolbars.configureByType(type, config);
|
|
272
|
-
page.toolbars.configureByTag(tagName, config);
|
|
273
|
-
page.toolbars.configureCustom(matcher, config);
|
|
274
|
-
page.toolbars.getToolbarForComponent(component);
|
|
275
|
-
page.toolbars.setGlobalDefault(config);
|
|
276
|
-
```
|
|
177
|
+
### JSX/TSX → Components
|
|
277
178
|
|
|
278
|
-
|
|
179
|
+
```ts
|
|
180
|
+
// Uses optional peer dependency `typescript`.
|
|
181
|
+
await editor.page.components.setFromJSX(`
|
|
182
|
+
export function Header() {
|
|
183
|
+
return <div id="header">Hello</div>
|
|
184
|
+
}
|
|
185
|
+
`)
|
|
186
|
+
```
|
|
279
187
|
|
|
280
|
-
|
|
188
|
+
## Server sync (Bun + Cloudflare)
|
|
281
189
|
|
|
282
|
-
|
|
283
|
-
import { init } from 'editorts';
|
|
190
|
+
EditorTs ships lightweight websocket utilities for server-side sync.
|
|
284
191
|
|
|
285
|
-
|
|
286
|
-
containerId: 'root',
|
|
287
|
-
data: pageData
|
|
288
|
-
});
|
|
192
|
+
### Bun server
|
|
289
193
|
|
|
290
|
-
|
|
291
|
-
|
|
194
|
+
```ts
|
|
195
|
+
import { createBunSyncServer, createSyncMessage } from 'editorts'
|
|
292
196
|
|
|
293
|
-
|
|
197
|
+
const server = createBunSyncServer({
|
|
198
|
+
port: 8787,
|
|
199
|
+
onSync: async (message) => {
|
|
200
|
+
console.log('received', message.payload)
|
|
201
|
+
},
|
|
202
|
+
})
|
|
294
203
|
|
|
295
|
-
|
|
296
|
-
const
|
|
297
|
-
containerId: 'root',
|
|
298
|
-
data: pageData,
|
|
299
|
-
toolbars: {
|
|
300
|
-
byType: {
|
|
301
|
-
'custom-code': {
|
|
302
|
-
actions: [
|
|
303
|
-
{ id: 'editJS', label: 'Edit Code', icon: '📜', enabled: true }
|
|
304
|
-
]
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
});
|
|
204
|
+
// elsewhere, send a message
|
|
205
|
+
const payload = createSyncMessage(pageData)
|
|
309
206
|
```
|
|
310
207
|
|
|
311
|
-
###
|
|
208
|
+
### Cloudflare worker
|
|
312
209
|
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// Add more listeners after init
|
|
322
|
-
editor.on('componentEdit', (component) => {
|
|
323
|
-
// Custom edit logic
|
|
324
|
-
});
|
|
210
|
+
```ts
|
|
211
|
+
import { createCfSyncWorker } from 'editorts'
|
|
212
|
+
|
|
213
|
+
export default createCfSyncWorker({
|
|
214
|
+
onSync: async (message) => {
|
|
215
|
+
console.log('received', message.payload)
|
|
216
|
+
},
|
|
217
|
+
})
|
|
325
218
|
```
|
|
326
219
|
|
|
327
|
-
###
|
|
220
|
+
### Message helpers
|
|
328
221
|
|
|
329
|
-
```
|
|
330
|
-
import {
|
|
222
|
+
```ts
|
|
223
|
+
import { createSyncMessage, parseSyncEnvelope } from 'editorts'
|
|
331
224
|
|
|
332
|
-
const
|
|
225
|
+
const message = createSyncMessage(pageData)
|
|
226
|
+
const parsed = parseSyncEnvelope(JSON.stringify(message))
|
|
227
|
+
```
|
|
333
228
|
|
|
334
|
-
|
|
335
|
-
const header = page.components.findById('header');
|
|
336
|
-
const boxes = page.components.findByType('box');
|
|
229
|
+
## AI provider (OpenCode)
|
|
337
230
|
|
|
338
|
-
|
|
339
|
-
page.styles.updateStyle('#header', {
|
|
340
|
-
'background-color': '#333',
|
|
341
|
-
'padding': '2rem'
|
|
342
|
-
});
|
|
231
|
+
Optional integration via `@opencode-ai/sdk`.
|
|
343
232
|
|
|
344
|
-
|
|
345
|
-
|
|
233
|
+
```ts
|
|
234
|
+
import { createOpencodeClient } from '@opencode-ai/sdk'
|
|
346
235
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
236
|
+
const editor = init({
|
|
237
|
+
iframeId: 'preview-iframe',
|
|
238
|
+
data: pageData,
|
|
239
|
+
aiProvider: {
|
|
240
|
+
provider: 'opencode',
|
|
241
|
+
mode: 'client',
|
|
242
|
+
baseUrl: 'http://localhost:4096',
|
|
350
243
|
|
|
351
|
-
|
|
244
|
+
// Optional: pass your own client
|
|
245
|
+
client: createOpencodeClient({ baseUrl: 'http://localhost:4096' }),
|
|
246
|
+
},
|
|
247
|
+
})
|
|
352
248
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
├── src/
|
|
356
|
-
│ ├── core/
|
|
357
|
-
│ │ ├── init.ts # Editor initialization
|
|
358
|
-
│ │ ├── Page.ts # Main Page class
|
|
359
|
-
│ │ ├── ComponentManager.ts # Component operations
|
|
360
|
-
│ │ ├── StyleManager.ts # Style operations
|
|
361
|
-
│ │ ├── AssetManager.ts # Asset operations
|
|
362
|
-
│ │ └── ToolbarManager.ts # Runtime toolbar config
|
|
363
|
-
│ ├── styles/
|
|
364
|
-
│ │ └── branding.css # Editor branding
|
|
365
|
-
│ ├── utils/
|
|
366
|
-
│ │ ├── helpers.ts # Utility functions
|
|
367
|
-
│ │ └── toolbar.ts # Toolbar utilities
|
|
368
|
-
│ └── types.ts # Type definitions
|
|
369
|
-
├── examples/
|
|
370
|
-
│ ├── quickstart.ts # Simple editor example
|
|
371
|
-
│ ├── basic-usage.ts # Library API examples
|
|
372
|
-
│ └── toolbar-example.ts # Toolbar configuration examples
|
|
373
|
-
├── samples/
|
|
374
|
-
│ └── page_template.json # Sample page data
|
|
375
|
-
├── index.html # Editor HTML template
|
|
376
|
-
├── local-server.ts # Simple Bun dev server
|
|
377
|
-
├── index.ts # Main library exports
|
|
378
|
-
└── AGENTS.md # Architecture principles
|
|
249
|
+
// Later
|
|
250
|
+
const client = await editor.ai?.getClient()
|
|
379
251
|
```
|
|
380
252
|
|
|
381
|
-
##
|
|
253
|
+
## Events
|
|
382
254
|
|
|
383
|
-
|
|
384
|
-
# Run the editor
|
|
385
|
-
bun run dev
|
|
255
|
+
The editor emits typed events:
|
|
386
256
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
257
|
+
- `componentSelect`
|
|
258
|
+
- `componentEdit`, `componentEditJS`
|
|
259
|
+
- `componentDuplicate`, `componentDelete`
|
|
260
|
+
- `componentReorder`
|
|
261
|
+
- `pageEditCSS`, `pageEditJSON`
|
|
262
|
+
- `pageSaved`, `pageLoaded`
|
|
390
263
|
|
|
391
|
-
|
|
392
|
-
bun build index.ts --outdir=dist
|
|
393
|
-
```
|
|
264
|
+
See `src/types.ts` for the full event map.
|
|
394
265
|
|
|
395
|
-
##
|
|
266
|
+
## Development
|
|
396
267
|
|
|
397
|
-
|
|
268
|
+
```bash
|
|
269
|
+
bun run build
|
|
270
|
+
bun run test
|
|
271
|
+
```
|
|
398
272
|
|
|
399
|
-
##
|
|
273
|
+
## Project map
|
|
400
274
|
|
|
401
|
-
|
|
275
|
+
- Core entry: `src/core/init.ts`
|
|
276
|
+
- Page model: `src/core/Page.ts`
|
|
277
|
+
- Data managers: `src/core/ComponentManager.ts`, `src/core/StyleManager.ts`, `src/core/AssetManager.ts`
|
|
278
|
+
- Storage: `src/core/StorageManager.ts`
|
|
279
|
+
- Demo: `index.html` + `examples/quickstart.ts`
|
|
280
|
+
- Architecture + workflow: `AGENTS.md`
|