@zuilib/text-editor 0.9.0 → 0.10.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,26 @@
1
1
  # Changelog — @zuilib/text-editor
2
2
 
3
+ ## 0.10.0
4
+
5
+ - **Table rows.** Put the caret in a table and a **row rail** appears along
6
+ its left edge: a bar marks the caret's row, and a `+` handle rests under
7
+ the last row (click to append). Move over the rail and the handle snaps
8
+ to the nearest row boundary; hovering it draws the insertion line across
9
+ the table. The floating table toolbar gains **insert row above / below**
10
+ and **delete row**. Keyboard: `⌘↩` / `Ctrl+Enter` inserts below,
11
+ `⌘⇧↩` above, `⌘⇧⌫` deletes the row, and **Tab in the last cell appends
12
+ a row** instead of leaving the table
13
+ - Rows follow GFM's shape: nothing is inserted above the header row, and
14
+ deleting the header promotes the next row so the markdown keeps its
15
+ divider; the last remaining row can't be deleted
16
+ - `useMarkdownEditor()` adds `tableRow` (index, column, count, hasHeader),
17
+ `insertTableRow(position)` and `deleteTableRow()`. Node-level helpers
18
+ `$getTableRowPosition`, `$insertTableRowAt`, `$deleteTableRowAt`,
19
+ `$insertTableRowNear`, `$deleteSelectedTableRow`, `insertableRowIndices`
20
+ and `canDeleteRow` are exported for custom toolbars
21
+ - Rail and toolbar use the theme tokens (`--primary`, `--popover`,
22
+ `--border`, `--shadow-medium`); motion respects `prefers-reduced-motion`
23
+
3
24
  ## 0.9.0
4
25
 
5
26
  - **Ink drawing style**: `drawingStyle="ink"` on the editor renders shapes
package/README.md CHANGED
@@ -12,6 +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
16
  inline formatting), styled like Claude artifacts
16
17
  - **Text measure and block width**: an opt-in readable text column
17
18
  (`measure="48rem"`); each table and diagram picks *full* / *text* /
@@ -104,6 +105,36 @@ Editing is in place: Tab/arrows between cells, cell range selection, inline
104
105
  formatting inside cells. Cell content is single-line in markdown; newlines
105
106
  are escaped as `\n`.
106
107
 
108
+ ### Rows
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.
117
+
118
+ Keyboard, inside a table:
119
+
120
+ | Shortcut | Action |
121
+ | --- | --- |
122
+ | `⌘↩` / `Ctrl+Enter` | Insert row below |
123
+ | `⌘⇧↩` / `Ctrl+Shift+Enter` | Insert row above |
124
+ | `⌘⇧⌫` / `Ctrl+Shift+Backspace` | Delete row |
125
+ | `Tab` in the last cell | Append a row |
126
+
127
+ 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)`.
137
+
107
138
  ### Table density
108
139
 
109
140
  Place the caret in a table and a small toolbar floats at its top-right
@@ -272,7 +303,8 @@ It returns `editor` (the Lexical instance), `activeFormats`,
272
303
  `toggleFormat`, `insertTable`, `insertDrawing`, `blockWidth` /
273
304
  `setBlockWidth` (width of the table or drawing containing the selection,
274
305
  `null` outside both), `tableDensity` / `setTableDensity` (`null` outside
275
- tables), `canUndo`, `canRedo`, `undo`, `redo`. `tableWidth` /
306
+ tables), `tableRow` / `insertTableRow` / `deleteTableRow` (see
307
+ [Rows](#rows)), `canUndo`, `canRedo`, `undo`, `redo`. `tableWidth` /
276
308
  `setTableWidth` remain as deprecated aliases limited to tables.
277
309
 
278
310
  ## Diagrams (drawing canvas)
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, ReactElement } from 'react';
3
3
  import { LexicalEditor, TextFormatType, ElementNode, NodeKey, EditorConfig, SerializedElementNode, LexicalNode, DecoratorNode, Spread, SerializedLexicalNode, DOMExportOutput, DOMConversionMap } from 'lexical';
4
- import { TableNode } from '@lexical/table';
4
+ import { TableNode, TableCellNode, TableRowNode } from '@lexical/table';
5
5
  import { MultilineElementTransformer, ElementTransformer } from '@lexical/markdown';
6
6
  import { registerCodeHighlighting } from '@lexical/code';
7
7
 
@@ -265,6 +265,57 @@ declare function parseTableSettingsMarker(text: string): Partial<TableSettings>
265
265
  */
266
266
  declare function formatTableSettingsMarker(settings: TableSettings, options?: TableSettingsOptions): string | null;
267
267
 
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.
273
+ */
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;
278
+ /** Column index of that cell */
279
+ column: number;
280
+ /** Number of rows in the table */
281
+ count: number;
282
+ /** Whether the first row is a header row */
283
+ hasHeader: boolean;
284
+ }>;
285
+ /** The cell containing the selection (caret or the anchor of a cell range) */
286
+ declare function $getSelectedTableCell(): TableCellNode | null;
287
+ /** Where the selection sits in its table, or null outside tables */
288
+ declare function $getTableRowPosition(): TableRowPosition | null;
289
+ /**
290
+ * The row boundaries a new row may be inserted at: `0..count`, minus `0`
291
+ * when the first row is a header (a table's header is always its first row).
292
+ */
293
+ declare function insertableRowIndices(position: Pick<TableRowPosition, 'count' | 'hasHeader'>): number[];
294
+ /** A row can be removed as long as one row stays */
295
+ declare function canDeleteRow(position: Pick<TableRowPosition, 'count'>): boolean;
296
+ /**
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).
302
+ */
303
+ declare function $insertTableRowAt(table: TableNode, index: number, column?: number): TableRowNode;
304
+ /**
305
+ * Remove row `index`. No-op on a single-row table (see `canDeleteRow`).
306
+ * 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).
309
+ */
310
+ 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
+ */
315
+ declare function $insertTableRowNear(table: TableNode, position: 'above' | 'below'): TableRowNode | null;
316
+ /** Remove the selection's row. Returns false when nothing was removed. */
317
+ declare function $deleteSelectedTableRow(table: TableNode): boolean;
318
+
268
319
  type MarkdownEditorApi = Readonly<{
269
320
  /** The underlying Lexical editor, for dispatching your own commands */
270
321
  editor: LexicalEditor;
@@ -310,6 +361,19 @@ type MarkdownEditorApi = Readonly<{
310
361
  tableDensity: TableDensity | null;
311
362
  /** Change the density of the table containing the selection; no-op outside tables */
312
363
  setTableDensity: (density: TableDensity) => void;
364
+ /** The selection's row in its table (index, column, row count); `null` outside tables */
365
+ tableRow: TableRowPosition | null;
366
+ /**
367
+ * Insert an empty row next to the selection's row (default `below`) and
368
+ * move the caret into it; no-op outside tables. A row is never inserted
369
+ * above the header row: `above` there inserts right below it.
370
+ */
371
+ insertTableRow: (position?: 'above' | 'below') => void;
372
+ /**
373
+ * Remove the selection's row; no-op outside tables and on the last
374
+ * remaining row. Deleting the header row makes the next row the header.
375
+ */
376
+ deleteTableRow: () => void;
313
377
  canUndo: boolean;
314
378
  canRedo: boolean;
315
379
  undo: () => void;
@@ -1302,4 +1366,4 @@ declare const codeTokenizer: LexicalTokenizer;
1302
1366
  */
1303
1367
  declare function registerCodeBlockHighlighting(editor: LexicalEditor): () => void;
1304
1368
 
1305
- export { $createDrawingNode, $createFrontmatterNode, $getSelectedTable, $getTableDensity, $getTableSettings, $getTableWidth, $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 TableSettings, type TableSettingsOptions, type TextField, Toolbar, ToolbarButton, ToolbarDivider, type ToolbarItems, anchorPoint, bindEndpoints, boxOutline, codeTokenizer, connectorPoints, drawingToMermaid, expandSkeleton, findBoxAt, fixedPointFor, formatTableSettingsMarker, getCodeLanguages, hasCodeLanguage, inkAmplitude, inkFillOffset, inkStroke, isBlockWidth, isDrawingSkeleton, makeBinding, normalizeDrawingData, parseDrawingData, parseDrawingSkeleton, parseTableSettingsMarker, registerCodeBlockHighlighting, registerCodeLanguage, resolveBindings, resolveCodeLanguage, roundedPolyline, roundedRectPolygon, routeElbow, seedFrom, serializeDrawingData, tokenizeCode, useMarkdownEditor };
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 };