editor-ts 0.0.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 ADDED
@@ -0,0 +1,401 @@
1
+
2
+ # EditorTs
3
+
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
9
+
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
17
+
18
+ ## Quick Start
19
+
20
+ ```bash
21
+ bun install
22
+ bun run dev
23
+ ```
24
+
25
+ Open http://localhost:5021 to see the editor!
26
+
27
+ ## Simple Usage with `init()`
28
+
29
+ ```typescript
30
+ import { init } from 'editorts';
31
+ import pageData from './page.json';
32
+
33
+ // Initialize editor with one function call
34
+ 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();
75
+ ```
76
+
77
+ ## Architecture: Runtime vs. Stored Data
78
+
79
+ EditorTs follows a clean separation between **data** (stored in JSON) and **behavior** (configured in JavaScript).
80
+
81
+ **See [AGENTS.md](./AGENTS.md) for full architecture principles.**
82
+
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
88
+
89
+ ### What's Configured in JavaScript (Runtime)
90
+ - Toolbar configurations
91
+ - Editor behaviors
92
+ - UI interactions
93
+ - Event handlers
94
+ - Permissions
95
+
96
+ **Example:**
97
+ ```typescript
98
+ // JSON stays clean
99
+ const page = new Page(jsonData);
100
+
101
+ // Configure at runtime
102
+ page.toolbars.configureById('header', { ... });
103
+
104
+ // Export is still clean
105
+ page.toJSON(); // No toolbar property in output
106
+ ```
107
+
108
+ ## Toolbar System
109
+
110
+ ### Configure Toolbars at Runtime
111
+
112
+ ```typescript
113
+ const editor = init({
114
+ containerId: 'root',
115
+ data: pageData,
116
+ toolbars: {
117
+ // By component ID
118
+ 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: [...] }
132
+ },
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
+ });
147
+ ```
148
+
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
155
+
156
+ ### Toolbar Presets
157
+
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
+ }
197
+ ```
198
+
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
205
+
206
+ ### Page Class
207
+
208
+ Direct API for programmatic manipulation:
209
+
210
+ ```typescript
211
+ import { Page } from 'editorts';
212
+
213
+ const page = new Page(jsonData);
214
+
215
+ // Page methods
216
+ page.getTitle();
217
+ page.setTitle(title);
218
+ page.getHTML();
219
+ page.getCSS();
220
+ page.toJSON();
221
+ page.clone();
222
+
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
+ ```
230
+
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();
242
+ ```
243
+
244
+ ### StyleManager
245
+
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();
254
+ ```
255
+
256
+ ### AssetManager
257
+
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();
265
+ ```
266
+
267
+ ### ToolbarManager
268
+
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
+ ```
277
+
278
+ ## Examples
279
+
280
+ ### Basic Editor Setup
281
+
282
+ ```typescript
283
+ import { init } from 'editorts';
284
+
285
+ const editor = init({
286
+ containerId: 'root',
287
+ data: pageData
288
+ });
289
+
290
+ console.log('Components:', editor.page.components.count());
291
+ ```
292
+
293
+ ### With Toolbar Configuration
294
+
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
+ });
309
+ ```
310
+
311
+ ### With Event Handlers
312
+
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
+ });
325
+ ```
326
+
327
+ ### Programmatic Page Manipulation
328
+
329
+ ```typescript
330
+ import { Page } from 'editorts';
331
+
332
+ const page = new Page(jsonData);
333
+
334
+ // Find components
335
+ const header = page.components.findById('header');
336
+ const boxes = page.components.findByType('box');
337
+
338
+ // Update styles
339
+ page.styles.updateStyle('#header', {
340
+ 'background-color': '#333',
341
+ 'padding': '2rem'
342
+ });
343
+
344
+ // Manage assets
345
+ const images = page.assets.getImages();
346
+
347
+ // Export clean JSON
348
+ const json = page.toJSON();
349
+ ```
350
+
351
+ ## Project Structure
352
+
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
379
+ ```
380
+
381
+ ## Development
382
+
383
+ ```bash
384
+ # Run the editor
385
+ bun run dev
386
+
387
+ # Run examples
388
+ bun run examples/basic-usage.ts
389
+ bun run examples/toolbar-example.ts
390
+
391
+ # Build
392
+ bun build index.ts --outdir=dist
393
+ ```
394
+
395
+ ## License
396
+
397
+ MIT
398
+
399
+ ## Contributing
400
+
401
+ Contributions are welcome! Please read [AGENTS.md](./AGENTS.md) for architecture guidelines.
package/index.ts ADDED
@@ -0,0 +1,81 @@
1
+ /**
2
+ * EditorTs - TypeScript library for HTML content editing with JSON representation
3
+ *
4
+ * @example
5
+ * ```typescript
6
+ * import { Page } from 'editorts';
7
+ *
8
+ * // Load from JSON
9
+ * const page = new Page(jsonData);
10
+ *
11
+ * // Find and update components
12
+ * const header = page.components.findById('header');
13
+ * page.components.updateComponent('header', { style: 'color: red;' });
14
+ *
15
+ * // Manage styles
16
+ * page.styles.updateStyle('#header', { 'font-size': '2rem' });
17
+ *
18
+ * // Export back to JSON
19
+ * const json = page.toJSON();
20
+ * ```
21
+ */
22
+
23
+ // Core exports
24
+ export { Page } from './src/core/Page';
25
+ export { ComponentManager } from './src/core/ComponentManager';
26
+ export { StyleManager } from './src/core/StyleManager';
27
+ export { AssetManager } from './src/core/AssetManager';
28
+ export { ToolbarManager } from './src/core/ToolbarManager';
29
+ export { init } from './src/core/init';
30
+
31
+ // Type exports
32
+ export type {
33
+ PageData,
34
+ PageBody,
35
+ Component,
36
+ Asset,
37
+ Style,
38
+ CSSProperties,
39
+ ComponentQuery,
40
+ StyleQuery,
41
+ UpdateOptions,
42
+ SelectorObject,
43
+ ParsedComponents,
44
+ ToolbarConfig,
45
+ ToolbarAction,
46
+ ToolbarRule,
47
+ ComponentSelector,
48
+ InitConfig,
49
+ ToolbarInitConfig,
50
+ EditorTsEditor,
51
+ } from './src/types';
52
+
53
+ // Utility exports
54
+ export {
55
+ deepClone,
56
+ generateId,
57
+ cssObjectToString,
58
+ cssStringToObject,
59
+ flattenComponents,
60
+ getComponentByPath,
61
+ sanitizeHTML,
62
+ extractComponentIds,
63
+ mergeCSSProperties,
64
+ isIdSelector,
65
+ isClassSelector,
66
+ parseSelector,
67
+ formatFileSize,
68
+ isValidURL,
69
+ extractDomain,
70
+ } from './src/utils/helpers';
71
+
72
+ // Toolbar exports
73
+ export {
74
+ defaultToolbarActions,
75
+ defaultToolbarConfig,
76
+ createToolbarConfig,
77
+ mergeToolbarConfigs,
78
+ getEnabledActions,
79
+ findToolbarAction,
80
+ toolbarPresets,
81
+ } from './src/utils/toolbar';
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "editor-ts",
3
+ "version": "0.0.1",
4
+ "description": "TypeScript library for editing HTML content with JSON representation",
5
+ "module": "index.ts",
6
+ "main": "index.ts",
7
+ "type": "module",
8
+ "keywords": [
9
+ "html",
10
+ "json",
11
+ "editor",
12
+ "page-builder",
13
+ "cms",
14
+ "typescript",
15
+ "web"
16
+ ],
17
+ "author": "",
18
+ "license": "MIT",
19
+ "scripts": {
20
+ "dev": "bun run local-server.ts",
21
+ "server": "bun run server.ts",
22
+ "build": "bun build index.ts --outdir=dist",
23
+ "example": "bun run examples/basic-usage.ts",
24
+ "test": "bun run examples/basic-usage.ts",
25
+ "test:api": "bun run examples/test-api.ts"
26
+ },
27
+ "devDependencies": {
28
+ "@types/bun": "latest",
29
+ "playwright": "^1.57.0"
30
+ },
31
+ "peerDependencies": {
32
+ "typescript": "^5"
33
+ },
34
+ "files": [
35
+ "src",
36
+ "index.ts",
37
+ "README.md"
38
+ ],
39
+ "dependencies": {
40
+ "modern-monaco": "^0.3.6"
41
+ }
42
+ }
@@ -0,0 +1,127 @@
1
+ import type { PageBody, Asset } from '../types';
2
+
3
+ /**
4
+ * Manager for handling assets
5
+ */
6
+ export class AssetManager {
7
+ private body: PageBody;
8
+ private assets: Asset[];
9
+
10
+ constructor(body: PageBody) {
11
+ this.body = body;
12
+ this.assets = body.assets || [];
13
+ }
14
+
15
+ /**
16
+ * Find assets by type
17
+ */
18
+ findByType(type: Asset['type']): Asset[] {
19
+ return this.assets.filter((asset) => asset.type === type);
20
+ }
21
+
22
+ /**
23
+ * Find assets by source URL
24
+ */
25
+ findBySource(src: string): Asset[] {
26
+ return this.assets.filter((asset) => asset.src === src || asset.src.includes(src));
27
+ }
28
+
29
+ /**
30
+ * Find asset by exact source URL
31
+ */
32
+ findByExactSource(src: string): Asset | null {
33
+ const asset = this.assets.find((asset) => asset.src === src);
34
+ return asset || null;
35
+ }
36
+
37
+ /**
38
+ * Add a new asset
39
+ */
40
+ addAsset(asset: Asset): void {
41
+ this.assets.push(asset);
42
+ }
43
+
44
+ /**
45
+ * Remove asset by source URL
46
+ */
47
+ removeAsset(src: string): boolean {
48
+ const initialLength = this.assets.length;
49
+ this.assets = this.assets.filter((asset) => asset.src !== src);
50
+ return this.assets.length < initialLength;
51
+ }
52
+
53
+ /**
54
+ * Update an asset
55
+ */
56
+ updateAsset(src: string, updates: Partial<Asset>): boolean {
57
+ const asset = this.assets.find((a) => a.src === src);
58
+ if (asset) {
59
+ Object.assign(asset, updates);
60
+ return true;
61
+ }
62
+ return false;
63
+ }
64
+
65
+ /**
66
+ * Get all assets
67
+ */
68
+ getAll(): Asset[] {
69
+ return this.assets;
70
+ }
71
+
72
+ /**
73
+ * Get all image assets
74
+ */
75
+ getImages(): Asset[] {
76
+ return this.findByType('image');
77
+ }
78
+
79
+ /**
80
+ * Get all video assets
81
+ */
82
+ getVideos(): Asset[] {
83
+ return this.findByType('video');
84
+ }
85
+
86
+ /**
87
+ * Get all audio assets
88
+ */
89
+ getAudio(): Asset[] {
90
+ return this.findByType('audio');
91
+ }
92
+
93
+ /**
94
+ * Get all document assets
95
+ */
96
+ getDocuments(): Asset[] {
97
+ return this.findByType('document');
98
+ }
99
+
100
+ /**
101
+ * Get asset count
102
+ */
103
+ count(): number {
104
+ return this.assets.length;
105
+ }
106
+
107
+ /**
108
+ * Get assets from CDN
109
+ */
110
+ getCDNAssets(): Asset[] {
111
+ return this.assets.filter((asset) => asset.blinkCDN === true);
112
+ }
113
+
114
+ /**
115
+ * Sync changes back to page body
116
+ */
117
+ sync(): void {
118
+ this.body.assets = this.assets;
119
+ }
120
+
121
+ /**
122
+ * Replace all assets
123
+ */
124
+ replaceAll(assets: Asset[]): void {
125
+ this.assets = assets;
126
+ }
127
+ }