@zuilib/text-editor 0.3.1 → 0.5.0

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
@@ -13,11 +13,14 @@ document outline, and collapsible sections.
13
13
  blockquotes, links, fenced code (with syntax highlighting), tables
14
14
  - **Tables**: GFM pipe tables, edited in place (tab between cells, ranges,
15
15
  inline formatting), styled like Claude artifacts
16
- - **Block width**: tables and diagrams switch between full width and
17
- content width, Slab-style
18
- - **Diagrams**: a drawing canvas embedded in the document cards with
19
- attached text slots, bound arrows that follow their cards, elbow and
20
- multi-waypoint routing, labels, color palettes, dark mode
16
+ - **Table settings**: full/content width and row density per table;
17
+ diagrams switch between full and content width too — Slab-style
18
+ - **Diagrams**: a drawing canvas embedded in the document. Eight box
19
+ shapes (rectangle, ellipse, diamond, note, database, cloud, queue, actor)
20
+ with attached text slots, bound arrows that follow their boxes and attach
21
+ to a chosen side, auto-routed elbows that avoid other boxes, multi-select,
22
+ color palettes, dark mode, Mermaid export. LLMs author diagrams as a
23
+ coordinate-free ` ```diagram ` skeleton that the editor lays out
21
24
  - **Outline**: optional table-of-contents sidebar with click-to-scroll and
22
25
  current-section highlight
23
26
  - **Section folding**: collapse everything under a heading, view-layer only
@@ -98,26 +101,33 @@ Editing is in place: Tab/arrows between cells, cell range selection, inline
98
101
  formatting inside cells. Cell content is single-line in markdown; newlines
99
102
  are escaped as `\n`.
100
103
 
101
- ### Full width vs content width
104
+ ### Table settings: width and density
102
105
 
103
- Tables span the editor by default. Place the caret in a table and a small
104
- toggle floats at its top-right corner: **full width** (columns share the
105
- editor width) or **content width** (the table shrinks to fit its columns,
106
- left-aligned with the text, never wider than the editor). The setting is
107
- persisted as an HTML comment on the line directly above the table — other
108
- markdown renderers hide it:
106
+ Place the caret in a table and a small toolbar floats at its top-right
107
+ corner:
108
+
109
+ - **Width** *full* (default: columns share the editor width) or
110
+ *content* (the table shrinks to fit its columns, left-aligned with the
111
+ text, never wider than the editor).
112
+ - **Density** — *compact*, *comfortable* (default), or *spacious* cell
113
+ padding and font size.
114
+
115
+ Non-default settings are persisted as one HTML comment on the line directly
116
+ above the table — other markdown renderers hide it:
109
117
 
110
118
  ```md
111
- <!-- width: content -->
119
+ <!-- width: content; density: compact -->
112
120
  | Key | Value |
113
121
  | --- | --- |
114
122
  | Region | eu-west-1 |
115
123
  ```
116
124
 
117
- Programmatic access: `useMarkdownEditor().tableWidth` / `setTableWidth`
118
- (for custom toolbars), or `$getTableWidth(node)` / `$setTableWidth(node,
119
- width)` inside `editor.update`. The marker string is exported as
120
- `TABLE_WIDTH_MARKER`.
125
+ Programmatic access: `useMarkdownEditor().tableWidth` / `setTableWidth` and
126
+ `tableDensity` / `setTableDensity` (for custom toolbars), or
127
+ `$getTableSettings(node)` / `$setTableSettings(node, { width, density })`
128
+ inside `editor.update`. `parseTableSettingsMarker` /
129
+ `formatTableSettingsMarker` convert between the comment line and a
130
+ `TableSettings` object.
121
131
 
122
132
  ## Toolbar & custom toolbars
123
133
 
@@ -203,35 +213,86 @@ function BoldButton() {
203
213
 
204
214
  It returns `editor` (the Lexical instance), `activeFormats`,
205
215
  `toggleFormat`, `insertTable`, `insertDrawing`, `tableWidth` /
206
- `setTableWidth` (width of the table containing the selection, `null`
207
- outside tables), `canUndo`, `canRedo`, `undo`, `redo`.
216
+ `setTableWidth` and `tableDensity` / `setTableDensity` (settings of the
217
+ table containing the selection, `null` outside tables), `canUndo`,
218
+ `canRedo`, `undo`, `redo`.
208
219
 
209
220
  ## Diagrams (drawing canvas)
210
221
 
211
222
  The toolbar's insert-drawing button embeds a canvas; the drawing persists
212
- in the markdown as a ` ```drawing ` fenced JSON block, fully specified in
213
- **[DRAWING_FORMAT.md](./DRAWING_FORMAT.md)**.
223
+ in the markdown as a ` ```drawing ` fenced JSON block (format version 2),
224
+ fully specified in **[DRAWING_FORMAT.md](./DRAWING_FORMAT.md)**.
214
225
 
215
- - **Shapes**: rectangle, ellipse, triangle, pentagon, arrow, line, text —
216
- with stroke/fill palettes. Draw by picking a tool and dragging.
226
+ - **Shapes**: rectangle, ellipse, diamond, note (sticky note), database
227
+ (cylinder), cloud, queue, actor (stick figure), arrow, line, text. The
228
+ toolbar shows the common tools and a "more shapes" popover for the rest.
229
+ Draw by picking a tool and dragging.
217
230
  - **Cards**: every box carries three text slots that move, resize, and wrap
218
- with it a bold **label** on top, **content** in the center, a dim
219
- **footer** at the bottom. Click a selected box (or double-click its
220
- top/middle/bottom strip, or press Enter) to edit a slot.
231
+ with it: a bold **label** on top, **content** in the center, a dim
232
+ **footer** at the bottom (actor has only the name under the figure).
233
+ Click a selected box (or double-click its top/middle/bottom strip, or
234
+ press Enter) to edit a slot.
221
235
  - **Bound connectors**: an arrow drawn from one card to another attaches to
222
- both moving a card moves its arrows, endpoints anchored to the border.
223
- Drag an endpoint off/onto a card to detach/re-attach.
224
- - **Routing**: straight (diagonal) by default; toggle **elbow** for right
225
- angles, or drag the dashed "+" handles on a selected connector to add any
226
- number of **waypoints** (they snap to neighbors' axes for clean 90°
227
- bends). One-way / two-way arrowhead toggle; midpoint **labels**.
236
+ both; moving a card moves its arrows, endpoints anchored to the outline.
237
+ An endpoint either auto-aims at the other end or sticks to a fixed point
238
+ on the box (a side, or a ratio inside the box). Drag an endpoint off/onto
239
+ a card to detach/re-attach.
240
+ - **Routing**: straight (diagonal) by default; toggle **elbow** for an
241
+ auto-routed right-angled path that leaves the box perpendicular to its
242
+ attach side and avoids the other boxes, or drag the dashed "+" handles on
243
+ a selected connector to add any number of **waypoints** (they snap to
244
+ neighbors' axes for clean 90° bends). One-way / two-way arrowhead toggle;
245
+ midpoint **labels**.
246
+ - **Multi-select**: shift-click, or drag a marquee on empty canvas, then
247
+ move, delete, or recolor the selection together. A contextual property
248
+ bar shows the options for whatever is selected.
249
+ - **Copy as Mermaid**: a canvas button copies the drawing as a Mermaid
250
+ `flowchart` (also available as `drawingToMermaid(data)`).
228
251
  - **Canvas**: resizable height, dot grid, white surface that inverts
229
- Excalidraw-style in dark mode (`.dark` ancestor class).
252
+ Excalidraw-style in dark mode (`.dark` ancestor class). An optional
253
+ `canvasWidth` scales the drawing to fit narrower layouts.
230
254
  - **Width**: the toggle at the right end of the canvas toolbar switches
231
255
  between **full width** (spans the editor) and **content width** (the
232
256
  canvas fits the rightmost shape and grows as shapes move). Stored as
233
257
  `"width":"content"` in the payload; omitted when full.
234
258
 
259
+ A stored drawing looks like this:
260
+
261
+ ````md
262
+ ```drawing
263
+ {"version":2,"canvasHeight":260,"shapes":[
264
+ {"id":"web","type":"rect","x":40,"y":70,"w":170,"h":100,"stroke":"#1971c2","fill":"#a5d8ff","strokeWidth":2,"label":"CLIENT","text":"Web App"},
265
+ {"id":"api","type":"rect","x":330,"y":70,"w":170,"h":100,"stroke":"#2f9e44","fill":"#b2f2bb","strokeWidth":2,"label":"SERVICE","text":"API"},
266
+ {"id":"e1","type":"arrow","x":216,"y":120,"w":108,"h":0,"stroke":"#1e1e1e","fill":"transparent","strokeWidth":2,"startBinding":{"id":"web"},"endBinding":{"id":"api"},"text":"REST"}
267
+ ]}
268
+ ```
269
+ ````
270
+
271
+ ### The ` ```diagram ` skeleton
272
+
273
+ Generators (LLMs, scripts) do not need coordinates. A ` ```diagram ` block
274
+ holds a skeleton: boxes, connectors, colors. On import the editor lays it
275
+ out along `direction` (`right` by default, or `down`), sizes boxes to their
276
+ text, and stores the result as a normal ` ```drawing ` block (one-way
277
+ expansion).
278
+
279
+ ````md
280
+ ```diagram
281
+ {"boxes":[
282
+ {"id":"web","label":"CLIENT","text":"Web App","color":"blue"},
283
+ {"id":"api","label":"SERVICE","text":"API","color":"green"},
284
+ {"id":"db","type":"cylinder","text":"Postgres"}
285
+ ],"connectors":[
286
+ {"from":"web","to":"api","text":"REST"},
287
+ {"from":"api","to":"db","routing":"elbow"}
288
+ ]}
289
+ ```
290
+ ````
291
+
292
+ The full skeleton spec (attach sides, explicit positions, free texts) is
293
+ in [DRAWING_FORMAT.md](./DRAWING_FORMAT.md). Programmatic access:
294
+ `parseDrawingSkeleton`, `expandSkeleton`, `DRAWING_SKELETON_JSON_SCHEMA`.
295
+
235
296
  ## Outline & section folding
236
297
 
237
298
  `outline` docks a collapsible table-of-contents sidebar: live heading list
@@ -248,14 +309,19 @@ remount; a folded section auto-expands if the cursor enters it.
248
309
  Documents are plain markdown, so LLMs can generate them — including
249
310
  diagrams:
250
311
 
251
- - **[DRAWING_FORMAT.md](./DRAWING_FORMAT.md)** is the authoritative
252
- ` ```drawing ` payload spec, written to be pasted into a prompt (ships in
253
- the npm package next to this README).
254
- - `DRAWING_DATA_JSON_SCHEMA` (exported) is the same contract as JSON Schema
255
- use it to validate generated payloads or as a structured-output/tool
256
- schema.
257
- - `parseDrawingData(json)` is the editor's own lenient parser (invalid
258
- shapes drop out; never throws); `serializeDrawingData` is its inverse.
312
+ - Emit ` ```diagram ` skeleton blocks: no coordinates, the editor lays them
313
+ out. Use ` ```drawing ` (format v2) only to edit an existing concrete
314
+ payload.
315
+ - **[DRAWING_FORMAT.md](./DRAWING_FORMAT.md)** is the authoritative spec of
316
+ both payloads, written to be pasted into a prompt (ships in the npm
317
+ package next to this README).
318
+ - `DRAWING_SKELETON_JSON_SCHEMA` and `DRAWING_DATA_JSON_SCHEMA` (exported)
319
+ are the same contracts as JSON Schema: use them to validate generated
320
+ payloads or as structured-output/tool schemas.
321
+ - `parseDrawingSkeleton(json)` / `expandSkeleton(skeleton)` turn a skeleton
322
+ into `DrawingData`. `parseDrawingData(json)` is the editor's own lenient
323
+ parser (invalid shapes drop out; never throws); `serializeDrawingData` is
324
+ its inverse. `drawingToMermaid(data)` exports a Mermaid `flowchart`.
259
325
  - A ready-made Claude Code skill lives in the monorepo at
260
326
  `.claude/skills/text-editor-documents/` — copy it into consuming repos so
261
327
  agents there know the dialect.
@@ -269,12 +335,17 @@ diagrams:
269
335
  | `Toolbar`, `ToolbarButton`, `ToolbarDivider`, `FormatButtons`, `InsertButtons`, `HistoryButtons` | Toolbar primitives |
270
336
  | `@zuilib/text-editor/styles.css` | Editor chrome + theme styles (required) |
271
337
  | `TABLE` | GFM table markdown transformer |
272
- | `BlockWidth`, `$getTableWidth`, `$setTableWidth`, `$getSelectedTable`, `TABLE_WIDTH_MARKER` | Table full/content width helpers |
273
- | `DRAWING`, `DrawingNode`, `$createDrawingNode`, `$isDrawingNode` | Drawing node + markdown transformer |
338
+ | `BlockWidth`, `TableDensity`, `TableSettings`, `DEFAULT_TABLE_SETTINGS` | Table setting types |
339
+ | `$getTableSettings`, `$setTableSettings`, `$getTableWidth`, `$setTableWidth`, `$getTableDensity`, `$setTableDensity`, `$getSelectedTable` | Table setting helpers (inside `editor.update`/`read`) |
340
+ | `parseTableSettingsMarker`, `formatTableSettingsMarker` | Marker comment ⇄ `TableSettings` |
341
+ | `DRAWING`, `DIAGRAM`, `DrawingNode`, `$createDrawingNode`, `$isDrawingNode` | Drawing node + markdown transformers (` ```drawing ` and ` ```diagram `) |
274
342
  | `FRONTMATTER`, `FrontmatterNode`, `$createFrontmatterNode`, `$isFrontmatterNode` | Frontmatter node + transformer |
275
343
  | `DrawingData`, `DrawingShape`, `DrawingShapeType` | Drawing payload types |
276
344
  | `parseDrawingData`, `serializeDrawingData` | Drawing payload (de)serialization |
277
345
  | `DRAWING_DATA_JSON_SCHEMA` | JSON Schema of the drawing payload |
346
+ | `DrawingSkeleton`, `SkeletonBox`, `SkeletonConnector` | Skeleton types |
347
+ | `parseDrawingSkeleton`, `expandSkeleton`, `DRAWING_SKELETON_JSON_SCHEMA` | Skeleton parsing, expansion to `DrawingData`, JSON Schema |
348
+ | `drawingToMermaid` | Mermaid `flowchart` export |
278
349
 
279
350
  ## Form integration
280
351
 
@@ -295,13 +366,15 @@ ZUI convention: a `dark` class on `<html>`.
295
366
  - Source: `src/EditorRoot.tsx` (composer + plugins), `src/EditorContent.tsx`,
296
367
  `src/MarkdownEditor.tsx` (default composition), `src/useMarkdownEditor.ts`,
297
368
  `src/components/Toolbar.tsx`; plugins in `src/plugins/`; drawing
298
- canvas in `src/components/`; custom nodes in `src/nodes/`;
369
+ canvas in `src/drawing/`; custom nodes in `src/nodes/`;
299
370
  transformers in `src/transformers/`
300
371
  - Markdown import/export via `@lexical/markdown` transformers; order
301
- matters: `FRONTMATTER` and `DRAWING` claim their blocks before `CODE`,
302
- `CHECK_LIST` before `UNORDERED_LIST`
303
- - Drawing geometry (bindings resolver, elbow/waypoint paths, wrapping) is
304
- pure and lives in `src/components/drawingGeometry.ts`
372
+ matters: `FRONTMATTER`, `DRAWING` and `DIAGRAM` claim their blocks before
373
+ `CODE`, `CHECK_LIST` before `UNORDERED_LIST`
374
+ - `src/drawing/` keeps the format (`types.ts`, `schema.ts`), shape
375
+ definitions (`shapes/`), pure geometry (`geometry.ts`), the Mermaid
376
+ exporter (`mermaid.ts`) and the React canvas (`canvas/`) apart, so the
377
+ router, bindings and skeleton expansion run without a DOM
305
378
  - Tests: `pnpm test` runs a headless-Lexical markdown round-trip suite
306
379
  (`tests/roundtrip.mjs`)
307
380