editor-ts 0.0.10 → 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 CHANGED
@@ -1,401 +1,280 @@
1
-
2
1
  # EditorTs
3
2
 
4
- DO NOT USE THIS IN PRODUCTION YET IT IS A WORK IN PROGRESS - EARLY DEVELOPMENT
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
- - **One-Line Setup**: Initialize a complete editor with `init()`
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
- ## Quick Start
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 to see the editor!
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
- ## Simple Usage with `init()`
33
+ When only HTML is present:
34
+ - HTML can be converted to components when DOM is available.
28
35
 
29
- ```typescript
30
- import { init } from 'editorts';
31
- import pageData from './page.json';
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
- containerId: 'root',
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
- ## Architecture: Runtime vs. Stored Data
49
+ ### Storage adapters
78
50
 
79
- EditorTs follows a clean separation between **data** (stored in JSON) and **behavior** (configured in JavaScript).
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
- **See [AGENTS.md](./AGENTS.md) for full architecture principles.**
53
+ ```ts
54
+ import { init } from 'editorts'
55
+ import { SQLocal } from 'sqlocal'
82
56
 
83
- ### What's Stored in JSON (Database)
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
- ### What's Configured in JavaScript (Runtime)
90
- - Toolbar configurations
91
- - Editor behaviors
92
- - UI interactions
93
- - Event handlers
94
- - Permissions
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
- **Example:**
97
- ```typescript
98
- // JSON stays clean
99
- const page = new Page(jsonData);
70
+ Run the SQLocal demos (Vite):
100
71
 
101
- // Configure at runtime
102
- page.toolbars.configureById('header', { ... });
72
+ ```bash
73
+ cd examples/localsql
74
+ bun install
75
+ bun run dev
76
+ ```
103
77
 
104
- // Export is still clean
105
- page.toJSON(); // No toolbar property in output
78
+ ```bash
79
+ cd examples/solid
80
+ bun install
81
+ bun run dev
106
82
  ```
107
83
 
108
- ## Toolbar System
84
+ Open `http://localhost:5173`.
85
+
86
+ ### Toolbars (runtime only)
109
87
 
110
- ### Configure Toolbars at Runtime
88
+ ```ts
89
+ import { init } from 'editorts'
111
90
 
112
- ```typescript
113
91
  const editor = init({
114
- containerId: 'root',
92
+ iframeId: 'preview-iframe',
115
93
  data: pageData,
116
94
  toolbars: {
117
- // By component ID
118
95
  byId: {
119
- 'header': { enabled: true, actions: [...] },
120
- 'footer': { enabled: false, actions: [] }
121
- },
122
-
123
- // By component type
124
- byType: {
125
- 'custom-code': { actions: [...] },
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
- // Global default
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
- ### Toolbar Actions
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
- ### Toolbar Presets
110
+ EditorTs does not create your sidebar/tabs/layout. You provide containers and `init()` wires them.
157
111
 
158
- ```typescript
159
- import { toolbarPresets } from 'editorts';
160
-
161
- // Use presets
162
- page.toolbars.configureById('header', toolbarPresets.full);
163
- page.toolbars.configureById('footer', toolbarPresets.editOnly);
164
- page.toolbars.configureById('banner', toolbarPresets.minimal);
165
- ```
166
-
167
- Available presets:
168
- - `full` - All actions enabled
169
- - `editOnly` - Edit, Edit JS, Duplicate (no delete)
170
- - `minimal` - Edit and Delete only
171
- - `readOnly` - View only
172
- - `disabled` - No toolbar
173
-
174
- ## API Reference
175
-
176
- ### `init(config)` → EditorTsEditor
177
-
178
- Initialize the editor with configuration.
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
- **Events:**
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
- ### Page Class
137
+ EditorTs can render editors into your containers:
207
138
 
208
- Direct API for programmatic manipulation:
139
+ - Default: `textarea` (zero deps)
140
+ - Optional: `modern-monaco` (syntax highlighting)
209
141
 
210
- ```typescript
211
- import { Page } from 'editorts';
142
+ ```ts
143
+ const editor = init({
144
+ iframeId: 'preview-iframe',
145
+ data: pageData,
146
+ codeEditor: { provider: 'modern-monaco' },
147
+ })
148
+ ```
212
149
 
213
- const page = new Page(jsonData);
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
- // Page methods
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
- // Managers
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
- ### ComponentManager
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
- ### StyleManager
162
+ ### HTML → Components
245
163
 
246
- ```typescript
247
- page.styles.findBySelector(selector);
248
- page.styles.findByMedia(mediaText);
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
- ### AssetManager
169
+ ### Components → JSX/TSX
257
170
 
258
- ```typescript
259
- page.assets.findByType(type);
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
- ### ToolbarManager
175
+ `toJSX()` outputs React-style function components named from `attributes.id` when possible.
268
176
 
269
- ```typescript
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
- ## Examples
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
- ### Basic Editor Setup
188
+ ## Server sync (Bun + Cloudflare)
281
189
 
282
- ```typescript
283
- import { init } from 'editorts';
190
+ EditorTs ships lightweight websocket utilities for server-side sync.
284
191
 
285
- const editor = init({
286
- containerId: 'root',
287
- data: pageData
288
- });
192
+ ### Bun server
289
193
 
290
- console.log('Components:', editor.page.components.count());
291
- ```
194
+ ```ts
195
+ import { createBunSyncServer, createSyncMessage } from 'editorts'
292
196
 
293
- ### With Toolbar Configuration
197
+ const server = createBunSyncServer({
198
+ port: 8787,
199
+ onSync: async (message) => {
200
+ console.log('received', message.payload)
201
+ },
202
+ })
294
203
 
295
- ```typescript
296
- const editor = init({
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
- ### With Event Handlers
208
+ ### Cloudflare worker
312
209
 
313
- ```typescript
314
- const editor = init({
315
- containerId: 'root',
316
- data: pageData,
317
- onComponentSelect: (comp) => console.log('Selected:', comp),
318
- onComponentDelete: (comp) => console.log('Deleted:', comp)
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
- ### Programmatic Page Manipulation
220
+ ### Message helpers
328
221
 
329
- ```typescript
330
- import { Page } from 'editorts';
222
+ ```ts
223
+ import { createSyncMessage, parseSyncEnvelope } from 'editorts'
331
224
 
332
- const page = new Page(jsonData);
225
+ const message = createSyncMessage(pageData)
226
+ const parsed = parseSyncEnvelope(JSON.stringify(message))
227
+ ```
333
228
 
334
- // Find components
335
- const header = page.components.findById('header');
336
- const boxes = page.components.findByType('box');
229
+ ## AI provider (OpenCode)
337
230
 
338
- // Update styles
339
- page.styles.updateStyle('#header', {
340
- 'background-color': '#333',
341
- 'padding': '2rem'
342
- });
231
+ Optional integration via `@opencode-ai/sdk`.
343
232
 
344
- // Manage assets
345
- const images = page.assets.getImages();
233
+ ```ts
234
+ import { createOpencodeClient } from '@opencode-ai/sdk'
346
235
 
347
- // Export clean JSON
348
- const json = page.toJSON();
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
- ## Project Structure
244
+ // Optional: pass your own client
245
+ client: createOpencodeClient({ baseUrl: 'http://localhost:4096' }),
246
+ },
247
+ })
352
248
 
353
- ```
354
- editorts/
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
- ## Development
253
+ ## Events
382
254
 
383
- ```bash
384
- # Run the editor
385
- bun run dev
255
+ The editor emits typed events:
386
256
 
387
- # Run examples
388
- bun run examples/basic-usage.ts
389
- bun run examples/toolbar-example.ts
257
+ - `componentSelect`
258
+ - `componentEdit`, `componentEditJS`
259
+ - `componentDuplicate`, `componentDelete`
260
+ - `componentReorder`
261
+ - `pageEditCSS`, `pageEditJSON`
262
+ - `pageSaved`, `pageLoaded`
390
263
 
391
- # Build
392
- bun build index.ts --outdir=dist
393
- ```
264
+ See `src/types.ts` for the full event map.
394
265
 
395
- ## License
266
+ ## Development
396
267
 
397
- MIT
268
+ ```bash
269
+ bun run build
270
+ bun run test
271
+ ```
398
272
 
399
- ## Contributing
273
+ ## Project map
400
274
 
401
- Contributions are welcome! Please read [AGENTS.md](./AGENTS.md) for architecture guidelines.
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`