@zuilib/text-editor 0.10.0 → 0.11.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/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog — @zuilib/text-editor
2
2
 
3
+ ## 0.11.0
4
+
5
+ - Depends on **`@zuilib/tokens`** (optional peer) instead of `@zuilib/core`:
6
+ the editor only ever consumed the design tokens, never a component.
7
+ Load `@zuilib/tokens/styles.css` (or `tokens.css` without Tailwind) where
8
+ you previously loaded `@zuilib/core/styles.css`; without it the editor
9
+ falls back to its built-in defaults
10
+ - Documented that a Tailwind host must `@source` this package for the
11
+ document-content classes (headings, lists, code) to be generated
12
+
13
+ ## 0.10.1
14
+
15
+ - **Table columns** get the same rail as rows: a strip along the table's
16
+ top edge with a bar on the caret's column and a `+` handle after the
17
+ last column that snaps to column boundaries while hovered
18
+ - **Deleting moves onto the rails.** The handle follows the pointer: near a
19
+ boundary it is a `+` (insert; hover previews the line), over the middle
20
+ of a row / column it is a `−` that removes it (hover tints what goes).
21
+ The three row buttons added to the floating toolbar in 0.10.0 are gone
22
+ - The layout toolbar now floats **above the table**, stacked over the
23
+ column rail at the top-right corner
24
+ - `useMarkdownEditor()`: `tableRow` is now `tableCell` (`{ row, column,
25
+ rows, columns, hasHeader }`); adds `insertTableColumn(position)` and
26
+ `deleteTableColumn()`. Node helpers: `$getTableCellPosition` (was
27
+ `$getTableRowPosition`), `$insertTableColumnAt`, `$deleteTableColumnAt`,
28
+ `$insertTableColumnNear`, `$deleteSelectedTableColumn`,
29
+ `insertableColumnIndices`, `canDeleteColumn`
30
+
3
31
  ## 0.10.0
4
32
 
5
33
  - **Table rows.** Put the caret in a table and a **row rail** appears along
package/README.md CHANGED
@@ -12,7 +12,7 @@ document outline, and collapsible sections.
12
12
  - **Markdown shortcuts** while typing: headings, lists, checklists,
13
13
  blockquotes, links, fenced code (with syntax highlighting), tables
14
14
  - **Tables**: GFM pipe tables, edited in place (tab between cells, ranges,
15
- add / remove rows from a hover rail, toolbar or keyboard,
15
+ add / remove rows and columns from hover rails or the keyboard,
16
16
  inline formatting), styled like Claude artifacts
17
17
  - **Text measure and block width**: an opt-in readable text column
18
18
  (`measure="48rem"`); each table and diagram picks *full* / *text* /
@@ -36,7 +36,7 @@ frontmatter as `---` blocks. Feature history: [CHANGELOG](./CHANGELOG.md).
36
36
  ## Installation
37
37
 
38
38
  ```bash
39
- pnpm add @zuilib/text-editor @zuilib/core \
39
+ pnpm add @zuilib/text-editor @zuilib/tokens \
40
40
  lexical @lexical/react @lexical/markdown @lexical/rich-text \
41
41
  @lexical/code @lexical/list @lexical/link @lexical/table @lexical/utils
42
42
  ```
@@ -48,7 +48,7 @@ All `lexical`/`@lexical/*` packages are peer dependencies at `^0.35.0`;
48
48
 
49
49
  ```tsx
50
50
  import { useState } from 'react'
51
- import '@zuilib/core/styles.css'
51
+ import '@zuilib/tokens/styles.css'
52
52
  import '@zuilib/text-editor/styles.css'
53
53
  import { MarkdownEditor } from '@zuilib/text-editor'
54
54
 
@@ -105,15 +105,16 @@ Editing is in place: Tab/arrows between cells, cell range selection, inline
105
105
  formatting inside cells. Cell content is single-line in markdown; newlines
106
106
  are escaped as `\n`.
107
107
 
108
- ### Rows
108
+ ### Rows and columns
109
109
 
110
- Click into a table and a slim **row rail** appears along its left edge. A
111
- bar marks the row the caret is in, and a `+` handle rests under the last row:
112
- click it to append a row. Move the pointer over the rail and the handle snaps
113
- to the nearest row boundary hovering it previews the insertion as a line
114
- across the table, clicking inserts there and moves the caret into the new
115
- row. The floating toolbar at the table's top-right corner has **insert row
116
- above**, **insert row below** and **delete row** for the caret's row.
110
+ Click into a table and two slim **rails** appear: one along its left edge
111
+ for rows, one along its top edge for columns. A bar on each marks the row
112
+ and column the caret is in, and a `+` handle rests after the last row /
113
+ column: click it to append one. Move the pointer over a rail and the handle
114
+ follows it near a boundary it is a `+` that inserts there (hovering it
115
+ previews the insertion as a line across the table), over the middle of a
116
+ row or column it becomes a `−` that removes it (hovering tints what will
117
+ go). The caret moves into whatever was inserted.
117
118
 
118
119
  Keyboard, inside a table:
119
120
 
@@ -125,19 +126,21 @@ Keyboard, inside a table:
125
126
  | `Tab` in the last cell | Append a row |
126
127
 
127
128
  GFM tables have exactly one header row, the first, so nothing is inserted
128
- above it (`above` there inserts right below the header) and deleting the
129
- header row makes the next row the header. The last remaining row can't be
130
- deleted.
131
-
132
- Programmatic access: `useMarkdownEditor().tableRow` (`{ index, column,
133
- count, hasHeader }` or `null`), `insertTableRow('above' | 'below')` and
134
- `deleteTableRow()`; or, inside `editor.update`, `$insertTableRowAt(table,
135
- index)`, `$deleteTableRowAt(table, index)`, `$insertTableRowNear(table,
136
- position)` and `$deleteSelectedTableRow(table)`.
129
+ above it (`above` there inserts right below the header), deleting the
130
+ header row makes the next row the header, and a new column gets a header
131
+ cell. The last remaining row or column can't be deleted.
132
+
133
+ Programmatic access: `useMarkdownEditor().tableCell` (`{ row, column,
134
+ rows, columns, hasHeader }` or `null`), `insertTableRow('above' | 'below')`,
135
+ `deleteTableRow()`, `insertTableColumn('before' | 'after')` and
136
+ `deleteTableColumn()`; or, inside `editor.update`, `$insertTableRowAt(table,
137
+ index)`, `$deleteTableRowAt(table, index)`, `$insertTableColumnAt(table,
138
+ index)`, `$deleteTableColumnAt(table, index)` and their `*Near` /
139
+ `$deleteSelected*` selection-relative forms.
137
140
 
138
141
  ### Table density
139
142
 
140
- Place the caret in a table and a small toolbar floats at its top-right
143
+ Place the caret in a table and a small toolbar floats above its top-right
141
144
  corner with the table's width (see [Block width and text
142
145
  measure](#block-width-and-text-measure)) and its **density** — *compact*,
143
146
  *comfortable* (default), or *spacious* cell padding and font size.
@@ -303,8 +306,8 @@ It returns `editor` (the Lexical instance), `activeFormats`,
303
306
  `toggleFormat`, `insertTable`, `insertDrawing`, `blockWidth` /
304
307
  `setBlockWidth` (width of the table or drawing containing the selection,
305
308
  `null` outside both), `tableDensity` / `setTableDensity` (`null` outside
306
- tables), `tableRow` / `insertTableRow` / `deleteTableRow` (see
307
- [Rows](#rows)), `canUndo`, `canRedo`, `undo`, `redo`. `tableWidth` /
309
+ tables), `tableCell` / `insertTableRow` / `deleteTableRow` / `insertTableColumn` /
310
+ `deleteTableColumn` (see [Rows and columns](#rows-and-columns)), `canUndo`, `canRedo`, `undo`, `redo`. `tableWidth` /
308
311
  `setTableWidth` remain as deprecated aliases limited to tables.
309
312
 
310
313
  ## Diagrams (drawing canvas)
@@ -485,10 +488,22 @@ diagrams:
485
488
  </FormField>
486
489
  ```
487
490
 
488
- Load both CSS entry points (`@zuilib/core/styles.css`,
491
+ Load both CSS entry points (`@zuilib/tokens/styles.css`,
489
492
  `@zuilib/text-editor/styles.css`) in the app layout. Dark mode follows the
490
493
  ZUI convention: a `dark` class on `<html>`.
491
494
 
495
+ `@zuilib/tokens` is an optional peer: the editor's chrome reads the ZUI
496
+ custom properties (`--primary`, `--popover`, `--border`, …) and falls back
497
+ to its own defaults when they are unset. Document content (headings,
498
+ lists, code blocks) is styled with Tailwind utility classes, so a Tailwind
499
+ v4 host must scan this package for them to be generated:
500
+
501
+ ```css
502
+ @import "@zuilib/tokens/styles.css";
503
+ @source "../node_modules/@zuilib/text-editor/dist";
504
+ @import "@zuilib/text-editor/styles.css";
505
+ ```
506
+
492
507
  ## Architecture notes (for extenders)
493
508
 
494
509
  - Source: `src/EditorRoot.tsx` (composer + plugins), `src/EditorContent.tsx`,
@@ -508,7 +523,8 @@ ZUI convention: a `dark` class on `<html>`.
508
523
 
509
524
  ## Related packages
510
525
 
511
- - `@zuilib/core` — design tokens and base styles
526
+ - `@zuilib/tokens` — design tokens and base styles
527
+ - `@zuilib/components` — the UI primitives (Button, Input, …)
512
528
  - `@zuilib/form` — react-hook-form wiring
513
529
 
514
530
  ## Build & release (maintainers)
package/dist/index.d.ts CHANGED
@@ -266,55 +266,75 @@ declare function parseTableSettingsMarker(text: string): Partial<TableSettings>
266
266
  declare function formatTableSettingsMarker(settings: TableSettings, options?: TableSettingsOptions): string | null;
267
267
 
268
268
  /**
269
- * Row operations for GFM tables. Markdown tables have no merged cells and
270
- * exactly one header row (the first), so these helpers work on plain row
271
- * indices and keep that invariant: a row can't be inserted above the
272
- * header, and deleting the header promotes the row below it.
269
+ * Row and column operations for GFM tables. Markdown tables have no merged
270
+ * cells, every row has the same number of cells and there is exactly one
271
+ * header row (the first), so these helpers work on plain indices and keep
272
+ * that shape: a row can't be inserted above the header, deleting the
273
+ * header promotes the row below it, and new cells in the header row are
274
+ * header cells.
273
275
  */
274
- /** Position of the selection inside its table */
275
- type TableRowPosition = Readonly<{
276
- /** Row index of the caret (or the anchor cell of a cell range) */
277
- index: number;
276
+ /** Where the selection sits in its table */
277
+ type TableCellPosition = Readonly<{
278
+ /** Row index of the caret (or of the anchor cell of a cell range) */
279
+ row: number;
278
280
  /** Column index of that cell */
279
281
  column: number;
280
282
  /** Number of rows in the table */
281
- count: number;
283
+ rows: number;
284
+ /** Number of columns in the table */
285
+ columns: number;
282
286
  /** Whether the first row is a header row */
283
287
  hasHeader: boolean;
284
288
  }>;
285
289
  /** The cell containing the selection (caret or the anchor of a cell range) */
286
290
  declare function $getSelectedTableCell(): TableCellNode | null;
287
291
  /** Where the selection sits in its table, or null outside tables */
288
- declare function $getTableRowPosition(): TableRowPosition | null;
292
+ declare function $getTableCellPosition(): TableCellPosition | null;
289
293
  /**
290
- * The row boundaries a new row may be inserted at: `0..count`, minus `0`
294
+ * The row boundaries a new row may be inserted at: `0..rows`, minus `0`
291
295
  * when the first row is a header (a table's header is always its first row).
292
296
  */
293
- declare function insertableRowIndices(position: Pick<TableRowPosition, 'count' | 'hasHeader'>): number[];
297
+ declare function insertableRowIndices(position: Pick<TableCellPosition, 'rows' | 'hasHeader'>): number[];
294
298
  /** A row can be removed as long as one row stays */
295
- declare function canDeleteRow(position: Pick<TableRowPosition, 'count'>): boolean;
299
+ declare function canDeleteRow(position: Pick<TableCellPosition, 'rows'>): boolean;
296
300
  /**
297
- * Insert an empty row so that it becomes row `index` (`0` = first,
298
- * `count` = last). Column header state is copied from the neighbouring
299
- * row; the header row itself is never displaced, so `index` is clamped to
300
- * `1` when the table has a header. Returns the new row, selected at its
301
- * `column`-th cell (default: the first).
301
+ * Insert an empty row so that it becomes row `index` (`0` = first, `rows`
302
+ * = last). Column header state is copied from the row above; the header
303
+ * row itself is never displaced, so `index` is clamped to `1` when the
304
+ * table has a header. The caret moves into the new row, at `column`.
302
305
  */
303
306
  declare function $insertTableRowAt(table: TableNode, index: number, column?: number): TableRowNode;
304
307
  /**
305
308
  * Remove row `index`. No-op on a single-row table (see `canDeleteRow`).
306
309
  * Removing the header row makes the next row the header so the markdown
307
- * keeps its divider. Selection moves to the same column of the row that
308
- * takes the removed row's place (or the last row).
310
+ * keeps its divider. The caret moves to the row that takes the removed
311
+ * row's place (or the last row), at `column`.
309
312
  */
310
313
  declare function $deleteTableRowAt(table: TableNode, index: number, column?: number): boolean;
311
- /**
312
- * Insert a row next to the selection's row. Returns the new row, or null
313
- * outside tables.
314
- */
314
+ /** Insert a row next to the selection's row. Returns the new row, or null outside tables. */
315
315
  declare function $insertTableRowNear(table: TableNode, position: 'above' | 'below'): TableRowNode | null;
316
316
  /** Remove the selection's row. Returns false when nothing was removed. */
317
317
  declare function $deleteSelectedTableRow(table: TableNode): boolean;
318
+ /** The column boundaries a new column may be inserted at: `0..columns` */
319
+ declare function insertableColumnIndices(position: Pick<TableCellPosition, 'columns'>): number[];
320
+ /** A column can be removed as long as one column stays */
321
+ declare function canDeleteColumn(position: Pick<TableCellPosition, 'columns'>): boolean;
322
+ /**
323
+ * Insert an empty column so that it becomes column `index` (`0` = first,
324
+ * `columns` = last). Cells in the header row are header cells. The caret
325
+ * moves into the new column, at `row`.
326
+ */
327
+ declare function $insertTableColumnAt(table: TableNode, index: number, row?: number): void;
328
+ /**
329
+ * Remove column `index`. No-op on a single-column table (see
330
+ * `canDeleteColumn`). The caret moves to the column that takes the removed
331
+ * column's place (or the last column), at `row`.
332
+ */
333
+ declare function $deleteTableColumnAt(table: TableNode, index: number, row?: number): boolean;
334
+ /** Insert a column next to the selection's column; no-op outside tables. */
335
+ declare function $insertTableColumnNear(table: TableNode, position: 'before' | 'after'): boolean;
336
+ /** Remove the selection's column. Returns false when nothing was removed. */
337
+ declare function $deleteSelectedTableColumn(table: TableNode): boolean;
318
338
 
319
339
  type MarkdownEditorApi = Readonly<{
320
340
  /** The underlying Lexical editor, for dispatching your own commands */
@@ -361,8 +381,8 @@ type MarkdownEditorApi = Readonly<{
361
381
  tableDensity: TableDensity | null;
362
382
  /** Change the density of the table containing the selection; no-op outside tables */
363
383
  setTableDensity: (density: TableDensity) => void;
364
- /** The selection's row in its table (index, column, row count); `null` outside tables */
365
- tableRow: TableRowPosition | null;
384
+ /** The selection's cell in its table (row, column, counts, header); `null` outside tables */
385
+ tableCell: TableCellPosition | null;
366
386
  /**
367
387
  * Insert an empty row next to the selection's row (default `below`) and
368
388
  * move the caret into it; no-op outside tables. A row is never inserted
@@ -374,6 +394,13 @@ type MarkdownEditorApi = Readonly<{
374
394
  * remaining row. Deleting the header row makes the next row the header.
375
395
  */
376
396
  deleteTableRow: () => void;
397
+ /**
398
+ * Insert an empty column next to the selection's column (default
399
+ * `after`) and move the caret into it; no-op outside tables.
400
+ */
401
+ insertTableColumn: (position?: 'before' | 'after') => void;
402
+ /** Remove the selection's column; no-op outside tables and on the last remaining column. */
403
+ deleteTableColumn: () => void;
377
404
  canUndo: boolean;
378
405
  canRedo: boolean;
379
406
  undo: () => void;
@@ -1366,4 +1393,4 @@ declare const codeTokenizer: LexicalTokenizer;
1366
1393
  */
1367
1394
  declare function registerCodeBlockHighlighting(editor: LexicalEditor): () => void;
1368
1395
 
1369
- export { $createDrawingNode, $createFrontmatterNode, $deleteSelectedTableRow, $deleteTableRowAt, $getSelectedTable, $getSelectedTableCell, $getTableDensity, $getTableRowPosition, $getTableSettings, $getTableWidth, $insertTableRowAt, $insertTableRowNear, $isDrawingNode, $isFrontmatterNode, $isTableWidthExplicit, $setTableDensity, $setTableSettings, $setTableWidth, BLOCK_WIDTHS, BOX_DEFINITIONS, BOX_TYPES, BUILTIN_CODE_LANGUAGES, type Binding, type BindingSide, type BlockWidth, type BlockWidthDefaults, type BoxDefinition, type BoxType, CODE_BLOCK, CODE_TOKEN_TYPES, COLOR_PRESETS, CONNECTOR_TYPES, type CodeGrammar, type CodeRule, type CodeToken, type CodeTokenType, type ColorName, type ConnectorType, DEFAULT_TABLE_SETTINGS, DIAGRAM, DRAWING, DRAWING_DATA_JSON_SCHEMA, DRAWING_SKELETON_JSON_SCHEMA, type DrawingData, DrawingNode, type DrawingShape, type DrawingShapeType, type DrawingSkeleton, type DrawingStyle, EMPTY_DRAWING, type EditorContentProps, type EditorMode, type EditorRootProps, FILL_COLORS, FRONTMATTER, FormatButtons, FrontmatterNode, HistoryButtons, type InkOptions, type InkStroke, InsertButtons, MarkdownEditor, type MarkdownEditorApi, type Props as MarkdownEditorProps, type MermaidDirection, type MermaidOptions, PLAIN_LANGUAGE, type Point, SHAPE_TYPES, SIDE_FIXED_POINTS, STROKE_COLORS, type SerializedDrawingNode, type SerializedFrontmatterNode, type SkeletonBox, type SkeletonConnector, type SkeletonEnd, type SkeletonText, TABLE, TABLE_WIDTH_MARKER, type TableDensity, type TableRowPosition, type TableSettings, type TableSettingsOptions, type TextField, Toolbar, ToolbarButton, ToolbarDivider, type ToolbarItems, anchorPoint, bindEndpoints, boxOutline, canDeleteRow, codeTokenizer, connectorPoints, drawingToMermaid, expandSkeleton, findBoxAt, fixedPointFor, formatTableSettingsMarker, getCodeLanguages, hasCodeLanguage, inkAmplitude, inkFillOffset, inkStroke, insertableRowIndices, isBlockWidth, isDrawingSkeleton, makeBinding, normalizeDrawingData, parseDrawingData, parseDrawingSkeleton, parseTableSettingsMarker, registerCodeBlockHighlighting, registerCodeLanguage, resolveBindings, resolveCodeLanguage, roundedPolyline, roundedRectPolygon, routeElbow, seedFrom, serializeDrawingData, tokenizeCode, useMarkdownEditor };
1396
+ export { $createDrawingNode, $createFrontmatterNode, $deleteSelectedTableColumn, $deleteSelectedTableRow, $deleteTableColumnAt, $deleteTableRowAt, $getSelectedTable, $getSelectedTableCell, $getTableCellPosition, $getTableDensity, $getTableSettings, $getTableWidth, $insertTableColumnAt, $insertTableColumnNear, $insertTableRowAt, $insertTableRowNear, $isDrawingNode, $isFrontmatterNode, $isTableWidthExplicit, $setTableDensity, $setTableSettings, $setTableWidth, BLOCK_WIDTHS, BOX_DEFINITIONS, BOX_TYPES, BUILTIN_CODE_LANGUAGES, type Binding, type BindingSide, type BlockWidth, type BlockWidthDefaults, type BoxDefinition, type BoxType, CODE_BLOCK, CODE_TOKEN_TYPES, COLOR_PRESETS, CONNECTOR_TYPES, type CodeGrammar, type CodeRule, type CodeToken, type CodeTokenType, type ColorName, type ConnectorType, DEFAULT_TABLE_SETTINGS, DIAGRAM, DRAWING, DRAWING_DATA_JSON_SCHEMA, DRAWING_SKELETON_JSON_SCHEMA, type DrawingData, DrawingNode, type DrawingShape, type DrawingShapeType, type DrawingSkeleton, type DrawingStyle, EMPTY_DRAWING, type EditorContentProps, type EditorMode, type EditorRootProps, FILL_COLORS, FRONTMATTER, FormatButtons, FrontmatterNode, HistoryButtons, type InkOptions, type InkStroke, InsertButtons, MarkdownEditor, type MarkdownEditorApi, type Props as MarkdownEditorProps, type MermaidDirection, type MermaidOptions, PLAIN_LANGUAGE, type Point, SHAPE_TYPES, SIDE_FIXED_POINTS, STROKE_COLORS, type SerializedDrawingNode, type SerializedFrontmatterNode, type SkeletonBox, type SkeletonConnector, type SkeletonEnd, type SkeletonText, TABLE, TABLE_WIDTH_MARKER, type TableCellPosition, type TableDensity, type TableSettings, type TableSettingsOptions, type TextField, Toolbar, ToolbarButton, ToolbarDivider, type ToolbarItems, anchorPoint, bindEndpoints, boxOutline, canDeleteColumn, canDeleteRow, codeTokenizer, connectorPoints, drawingToMermaid, expandSkeleton, findBoxAt, fixedPointFor, formatTableSettingsMarker, getCodeLanguages, hasCodeLanguage, inkAmplitude, inkFillOffset, inkStroke, insertableColumnIndices, insertableRowIndices, isBlockWidth, isDrawingSkeleton, makeBinding, normalizeDrawingData, parseDrawingData, parseDrawingSkeleton, parseTableSettingsMarker, registerCodeBlockHighlighting, registerCodeLanguage, resolveBindings, resolveCodeLanguage, roundedPolyline, roundedRectPolygon, routeElbow, seedFrom, serializeDrawingData, tokenizeCode, useMarkdownEditor };