@aicayzer/inkkit 0.0.17 → 0.1.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.
Files changed (81) hide show
  1. package/NOTICE +4 -2
  2. package/README.md +12 -6
  3. package/dist/THIRD-PARTY-NOTICES.txt +2742 -0
  4. package/dist/chunks/core-vtCr4MBS.js +56503 -0
  5. package/dist/chunks/highlight-Daj2fv_u.js +14122 -0
  6. package/dist/core.d.ts +4 -0
  7. package/dist/core.js +2 -0
  8. package/dist/editor.d.ts +261 -48
  9. package/dist/index.d.ts +1 -7
  10. package/dist/index.js +5 -2
  11. package/dist/style.css +242 -221
  12. package/dist/types.d.ts +143 -18
  13. package/package.json +26 -15
  14. package/dist/autolink.d.ts +0 -1
  15. package/dist/autolink.js +0 -18
  16. package/dist/callouts.d.ts +0 -26
  17. package/dist/callouts.js +0 -311
  18. package/dist/clipboard.d.ts +0 -11
  19. package/dist/clipboard.js +0 -261
  20. package/dist/code-fences.d.ts +0 -8
  21. package/dist/code-fences.js +0 -166
  22. package/dist/comments.d.ts +0 -55
  23. package/dist/comments.js +0 -494
  24. package/dist/decorations.d.ts +0 -2
  25. package/dist/decorations.js +0 -67
  26. package/dist/dialect.d.ts +0 -9
  27. package/dist/dialect.js +0 -358
  28. package/dist/editor.js +0 -2441
  29. package/dist/files.d.ts +0 -40
  30. package/dist/files.js +0 -133
  31. package/dist/highlight.d.ts +0 -1
  32. package/dist/highlight.js +0 -136
  33. package/dist/image-blocks.d.ts +0 -71
  34. package/dist/image-blocks.js +0 -286
  35. package/dist/images.d.ts +0 -51
  36. package/dist/images.js +0 -398
  37. package/dist/inline-highlight.d.ts +0 -6
  38. package/dist/inline-highlight.js +0 -112
  39. package/dist/labels.d.ts +0 -36
  40. package/dist/labels.js +0 -34
  41. package/dist/linked-syntax.d.ts +0 -48
  42. package/dist/linked-syntax.js +0 -320
  43. package/dist/literals.d.ts +0 -19
  44. package/dist/literals.js +0 -211
  45. package/dist/mermaid.d.ts +0 -8
  46. package/dist/mermaid.js +0 -321
  47. package/dist/outline.d.ts +0 -22
  48. package/dist/outline.js +0 -79
  49. package/dist/paste-html.d.ts +0 -23
  50. package/dist/paste-html.js +0 -441
  51. package/dist/paste.d.ts +0 -26
  52. package/dist/paste.js +0 -482
  53. package/dist/portable-files.d.ts +0 -10
  54. package/dist/portable-files.js +0 -36
  55. package/dist/preserve.d.ts +0 -21
  56. package/dist/preserve.js +0 -757
  57. package/dist/print.d.ts +0 -7
  58. package/dist/print.js +0 -409
  59. package/dist/reference-clipboard.d.ts +0 -8
  60. package/dist/reference-clipboard.js +0 -201
  61. package/dist/references.d.ts +0 -14
  62. package/dist/references.js +0 -368
  63. package/dist/search.d.ts +0 -15
  64. package/dist/search.js +0 -202
  65. package/dist/selection.d.ts +0 -1
  66. package/dist/selection.js +0 -59
  67. package/dist/source.d.ts +0 -16
  68. package/dist/source.js +0 -57
  69. package/dist/spreadsheet.d.ts +0 -3
  70. package/dist/spreadsheet.js +0 -275
  71. package/dist/table-alignment.d.ts +0 -2
  72. package/dist/table-alignment.js +0 -46
  73. package/dist/tables.d.ts +0 -27
  74. package/dist/tables.js +0 -322
  75. package/dist/tasks.d.ts +0 -3
  76. package/dist/tasks.js +0 -51
  77. package/dist/text-ranges.d.ts +0 -29
  78. package/dist/text-ranges.js +0 -256
  79. package/dist/types.js +0 -8
  80. package/dist/wiki-view.d.ts +0 -21
  81. package/dist/wiki-view.js +0 -78
package/dist/tables.js DELETED
@@ -1,322 +0,0 @@
1
- import { editorLabels } from './labels.js';
2
- import { commandsCtx, editorViewCtx } from '@milkdown/kit/core';
3
- import { tableBlock, tableBlockConfig, } from '@milkdown/kit/component/table-block';
4
- import { insertTableCommand, exitTable, moveRowCommand, moveColCommand, } from '@milkdown/kit/preset/gfm';
5
- import { addRowAfter, addRowBefore, addColumnAfter, addColumnBefore, deleteRow, deleteColumn, deleteTable, goToNextCell, selectedRect, TableMap, isInTable, CellSelection, } from '@milkdown/kit/prose/tables';
6
- import { Fragment } from '@milkdown/kit/prose/model';
7
- import { closeHistory } from '@milkdown/kit/prose/history';
8
- import { keymap } from '@milkdown/kit/prose/keymap';
9
- import { $prose } from '@milkdown/kit/utils';
10
- import { clipboardText } from './clipboard.js';
11
- export const tableCommands = [
12
- 'insert',
13
- 'addRowBefore',
14
- 'addRowAfter',
15
- 'addColumnBefore',
16
- 'addColumnAfter',
17
- 'deleteRow',
18
- 'deleteColumn',
19
- 'deleteTable',
20
- 'alignLeft',
21
- 'alignCenter',
22
- 'alignRight',
23
- 'moveRowUp',
24
- 'moveRowDown',
25
- 'moveColumnLeft',
26
- 'moveColumnRight',
27
- 'sortRows',
28
- 'exit',
29
- ];
30
- export function tableContext(state) {
31
- if (!isInTable(state))
32
- return undefined;
33
- const rect = selectedRect(state);
34
- return {
35
- row: rect.top,
36
- column: rect.left,
37
- rows: rect.map.height,
38
- columns: rect.map.width,
39
- };
40
- }
41
- /** Checks the same default operations without dispatching a transaction. */
42
- export function tableAvailability(ctx) {
43
- const view = ctx.get(editorViewCtx);
44
- const state = view.state;
45
- const commands = ctx.get(commandsCtx);
46
- const available = Object.fromEntries(tableCommands.map((name) => [name, false]));
47
- available.insert = commands.get(insertTableCommand.key)({ row: 3, col: 2 })(state);
48
- const actions = {
49
- addRowBefore,
50
- addRowAfter,
51
- addColumnBefore,
52
- addColumnAfter,
53
- deleteRow,
54
- deleteColumn,
55
- deleteTable,
56
- };
57
- for (const [name, action] of Object.entries(actions))
58
- available[name] = action(state);
59
- available.exit = commands.get(exitTable.key)()(state);
60
- if (!isInTable(state))
61
- return available;
62
- const rect = selectedRect(state);
63
- available.alignLeft = available.alignCenter = available.alignRight = true;
64
- if (!editableTable(rect.table))
65
- return available;
66
- available.moveRowUp = rect.top > 1;
67
- available.moveRowDown = rect.top > 0 && rect.bottom < rect.map.height;
68
- available.moveColumnLeft = rect.left > 0;
69
- available.moveColumnRight = rect.right < rect.map.width;
70
- const body = [];
71
- rect.table.forEach((row, _offset, index) => {
72
- if (index)
73
- body.push({ key: clipboardText(row.child(rect.left).content), index });
74
- });
75
- const sorted = [...body].sort((a, b) => compareCells(a.key, b.key, 'text', 'ascending') || a.index - b.index);
76
- available.sortRows = sorted.some((row, index) => row !== body[index]);
77
- return available;
78
- }
79
- const tableLabels = {
80
- add_row: 'tableAddRow',
81
- add_col: 'tableAddColumn',
82
- delete_row: 'tableDeleteRow',
83
- delete_col: 'tableDeleteColumn',
84
- align_col_left: 'tableAlignLeft',
85
- align_col_center: 'tableAlignCenter',
86
- align_col_right: 'tableAlignRight',
87
- col_drag_handle: 'tableMoveColumn',
88
- row_drag_handle: 'tableMoveRow',
89
- };
90
- function tableButton(label) {
91
- const span = document.createElement('span');
92
- span.setAttribute('role', 'img');
93
- span.setAttribute('aria-label', label);
94
- span.textContent = label;
95
- return span.outerHTML;
96
- }
97
- export const tablePlugins = [
98
- moveRowCommand,
99
- moveColCommand,
100
- tableBlockConfig,
101
- (ctx) => () => {
102
- ctx.update(tableBlockConfig.key, (config) => ({
103
- ...config,
104
- renderButton: (type) => tableButton(editorLabels(ctx)[tableLabels[type]]),
105
- }));
106
- },
107
- tableBlock,
108
- $prose(() => keymap({ Tab: goToNextCell(1), 'Shift-Tab': goToNextCell(-1) })),
109
- ].flat();
110
- export function tableCommand(ctx, command, options) {
111
- const view = ctx.get(editorViewCtx);
112
- const commands = ctx.get(commandsCtx);
113
- if (command === 'insert') {
114
- const row = options?.rows ?? 3, col = options?.columns ?? 2;
115
- if (!Number.isInteger(row) ||
116
- !Number.isInteger(col) ||
117
- row < 1 ||
118
- row > 100 ||
119
- col < 1 ||
120
- col > 100)
121
- throw new RangeError('Table dimensions must be integers from 1 to 100');
122
- return commands.call(insertTableCommand.key, { row, col });
123
- }
124
- if (command === 'exit')
125
- return commands.call(exitTable.key);
126
- if (command.startsWith('move') || command === 'sortRows')
127
- return refineTable(view, command, options);
128
- if (command === 'alignLeft' ||
129
- command === 'alignCenter' ||
130
- command === 'alignRight') {
131
- if (!isInTable(view.state))
132
- return false;
133
- const rect = selectedRect(view.state), map = TableMap.get(rect.table);
134
- const alignment = command === 'alignLeft'
135
- ? 'left'
136
- : command === 'alignRight'
137
- ? 'right'
138
- : 'center';
139
- const tr = view.state.tr;
140
- // GFM stores alignment on the header; apply it to the entire column so
141
- // editing a body cell does not disappear when Markdown is saved.
142
- const positions = new Set();
143
- for (let row = 0; row < map.height; row++)
144
- for (let col = rect.left; col < rect.right; col++)
145
- positions.add(map.map[row * map.width + col]);
146
- for (const offset of positions) {
147
- const pos = rect.tableStart + offset, cell = tr.doc.nodeAt(pos);
148
- tr.setNodeMarkup(pos, undefined, { ...cell.attrs, alignment });
149
- }
150
- view.dispatch(tr);
151
- return true;
152
- }
153
- const actions = {
154
- addRowBefore,
155
- addRowAfter,
156
- addColumnBefore,
157
- addColumnAfter,
158
- deleteRow,
159
- deleteColumn,
160
- deleteTable,
161
- };
162
- return actions[command](view.state, view.dispatch);
163
- }
164
- export function dispatchTableOperation(view, tr) {
165
- view.dispatch(closeHistory(tr).scrollIntoView());
166
- view.dispatch(closeHistory(view.state.tr).setMeta('addToHistory', false));
167
- }
168
- export function configureTableMovement(ctx, canEdit) {
169
- const commands = ctx.get(commandsCtx);
170
- for (const [key, rowMovement] of [
171
- [moveRowCommand.key, true],
172
- [moveColCommand.key, false],
173
- ]) {
174
- commands.remove(key);
175
- commands.create(key, (options = {}) => () => {
176
- const view = ctx.get(editorViewCtx);
177
- if (!canEdit() ||
178
- !view.editable ||
179
- view.composing ||
180
- !isInTable(view.state))
181
- return false;
182
- const rect = selectedRect(view.state);
183
- const from = options.from ?? 0, to = options.to ?? 0;
184
- const length = rowMovement ? rect.map.height : rect.map.width;
185
- if (!editableTable(rect.table) ||
186
- (options.pos != null && options.pos !== rect.tableStart) ||
187
- !Number.isInteger(from) ||
188
- !Number.isInteger(to) ||
189
- from < 0 ||
190
- to < 0 ||
191
- from >= length ||
192
- to >= length ||
193
- from === to ||
194
- (rowMovement && (from === 0 || to === 0)))
195
- return false;
196
- const rows = [];
197
- rect.table.forEach((row) => rows.push(row));
198
- if (rowMovement) {
199
- const moved = rows.splice(from, 1)[0];
200
- rows.splice(to, 0, moved);
201
- }
202
- else {
203
- for (let index = 0; index < rows.length; index++) {
204
- const row = rows[index], cells = [];
205
- row.forEach((cell) => cells.push(cell));
206
- cells.splice(to, 0, cells.splice(from, 1)[0]);
207
- rows[index] = row.copy(Fragment.fromArray(cells));
208
- }
209
- }
210
- const table = rect.table.copy(Fragment.fromArray(rows));
211
- const tr = view.state.tr.replaceWith(rect.tableStart - 1, rect.tableStart - 1 + rect.table.nodeSize, table);
212
- selectTableRectangle(tr, rect.tableStart, table, rowMovement ? to : 0, rowMovement ? 0 : to, rowMovement ? to + 1 : rect.map.height, rowMovement ? rect.map.width : to + 1);
213
- dispatchTableOperation(view, tr);
214
- return true;
215
- });
216
- }
217
- }
218
- export function editableTable(table) {
219
- const map = TableMap.get(table);
220
- if (map.problems?.length || map.width > 100 || map.height > 100)
221
- return false;
222
- let valid = table.firstChild?.type.name === 'table_header_row';
223
- table.forEach((row, _offset, index) => {
224
- valid &&= row.type.name === (index ? 'table_row' : 'table_header_row');
225
- valid &&= row.childCount === map.width;
226
- row.forEach((cell) => {
227
- valid &&= cell.attrs.colspan === 1 && cell.attrs.rowspan === 1;
228
- });
229
- });
230
- return Boolean(valid);
231
- }
232
- function refineTable(view, command, options) {
233
- if (!isInTable(view.state))
234
- return false;
235
- const rect = selectedRect(view.state);
236
- if (!editableTable(rect.table))
237
- return false;
238
- const rows = [];
239
- rect.table.forEach((row) => rows.push(row));
240
- let top = rect.top, bottom = rect.bottom, left = rect.left, right = rect.right;
241
- if (command === 'sortRows') {
242
- const column = options?.column ?? rect.left;
243
- if (!Number.isInteger(column) || column < 0 || column >= rect.map.width)
244
- throw new RangeError('Sort column must identify an existing table column');
245
- const order = options?.order ?? 'ascending';
246
- const comparison = options?.comparison ?? 'text';
247
- if (!['ascending', 'descending'].includes(order))
248
- throw new RangeError('Sort order must be ascending or descending');
249
- if (!['text', 'number'].includes(comparison))
250
- throw new RangeError('Sort comparison must be text or number');
251
- const body = rows.slice(1).map((row, index) => ({
252
- row,
253
- index,
254
- key: clipboardText(row.child(column).content),
255
- }));
256
- if (comparison === 'number' &&
257
- body.some(({ key }) => {
258
- const value = key.trim();
259
- return value !== '' && decimalNumber(value) === undefined;
260
- }))
261
- throw new RangeError('Numeric sorting requires finite decimal values or blank cells');
262
- body.sort((a, b) => compareCells(a.key, b.key, comparison, order) || a.index - b.index);
263
- if (body.every(({ row }, index) => row === rows[index + 1]))
264
- return false;
265
- const activeIndex = top - 1;
266
- rows.splice(1, rows.length - 1, ...body.map(({ row }) => row));
267
- top =
268
- top === 0 ? 0 : body.findIndex(({ index }) => index === activeIndex) + 1;
269
- bottom = top + 1;
270
- }
271
- else if (command === 'moveRowUp' || command === 'moveRowDown') {
272
- const up = command === 'moveRowUp';
273
- if (top === 0 || (up ? top === 1 : bottom === rows.length))
274
- return false;
275
- const moved = rows.splice(top, bottom - top);
276
- const destination = top + (up ? -1 : 1);
277
- rows.splice(destination, 0, ...moved);
278
- top = destination;
279
- bottom += up ? -1 : 1;
280
- }
281
- else {
282
- const backward = command === 'moveColumnLeft';
283
- if (backward ? left === 0 : right === rect.map.width)
284
- return false;
285
- for (let index = 0; index < rows.length; index++) {
286
- const row = rows[index];
287
- const cells = [];
288
- row.forEach((cell) => cells.push(cell));
289
- const moved = cells.splice(left, right - left);
290
- cells.splice(left + (backward ? -1 : 1), 0, ...moved);
291
- rows[index] = row.copy(Fragment.fromArray(cells));
292
- }
293
- left += backward ? -1 : 1;
294
- right += backward ? -1 : 1;
295
- }
296
- const table = rect.table.copy(Fragment.fromArray(rows));
297
- const tr = view.state.tr.replaceWith(rect.tableStart - 1, rect.tableStart - 1 + rect.table.nodeSize, table);
298
- selectTableRectangle(tr, rect.tableStart, table, top, left, bottom, right);
299
- dispatchTableOperation(view, tr);
300
- return true;
301
- }
302
- export function selectTableRectangle(tr, tableStart, table, top, left, bottom, right) {
303
- const map = TableMap.get(table);
304
- tr.setSelection(CellSelection.create(tr.doc, tableStart + map.map[top * map.width + left], tableStart + map.map[(bottom - 1) * map.width + right - 1]));
305
- }
306
- function compareCells(a, b, comparison, order) {
307
- const x = a.trim(), y = b.trim();
308
- if (!x || !y)
309
- return x === y ? 0 : x ? -1 : 1;
310
- const direction = order === 'ascending' ? 1 : -1;
311
- const text = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
312
- if (comparison === 'text')
313
- return direction * text(a, b);
314
- const nx = decimalNumber(x), ny = decimalNumber(y);
315
- return direction * (nx < ny ? -1 : nx > ny ? 1 : 0);
316
- }
317
- function decimalNumber(value) {
318
- return /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(value) &&
319
- Number.isFinite(Number(value))
320
- ? Number(value)
321
- : undefined;
322
- }
package/dist/tasks.d.ts DELETED
@@ -1,3 +0,0 @@
1
- import type { Ctx } from '@milkdown/kit/ctx';
2
- export declare const taskListPlugin: import("@milkdown/kit/utils").$Prose;
3
- export declare function toggleTaskList(ctx: Ctx): void;
package/dist/tasks.js DELETED
@@ -1,51 +0,0 @@
1
- import { commandsCtx, editorViewCtx } from '@milkdown/kit/core';
2
- import { wrapInBulletListCommand } from '@milkdown/kit/preset/commonmark';
3
- import { Plugin, PluginKey } from '@milkdown/kit/prose/state';
4
- import { $prose } from '@milkdown/kit/utils';
5
- // A click on the task marker lands on the <li> itself, since the marker is a
6
- // pseudo-element; clicks inside the item's text land on its children.
7
- export const taskListPlugin = $prose(() => new Plugin({
8
- key: new PluginKey('taskToggle'),
9
- props: {
10
- handleClickOn(view, _pos, node, nodePos, event) {
11
- if (!view.editable)
12
- return false;
13
- if (node.type.name !== 'list_item' || node.attrs.checked == null)
14
- return false;
15
- const target = event.target;
16
- if (!target.matches('li[data-item-type="task"]'))
17
- return false;
18
- view.dispatch(view.state.tr.setNodeMarkup(nodePos, undefined, {
19
- ...node.attrs,
20
- checked: !node.attrs.checked,
21
- }));
22
- return true;
23
- },
24
- },
25
- }));
26
- function listItemsInSelection(view) {
27
- const { from, to } = view.state.selection;
28
- const items = [];
29
- view.state.doc.nodesBetween(from, to, (node, pos) => {
30
- if (node.type.name === 'list_item')
31
- items.push({ pos, node });
32
- });
33
- return items;
34
- }
35
- export function toggleTaskList(ctx) {
36
- const view = ctx.get(editorViewCtx);
37
- let items = listItemsInSelection(view);
38
- if (items.length === 0) {
39
- ctx.get(commandsCtx).call(wrapInBulletListCommand.key);
40
- items = listItemsInSelection(view);
41
- }
42
- const allTasks = items.every(({ node }) => node.attrs.checked != null);
43
- let tr = view.state.tr;
44
- for (const { pos, node } of items) {
45
- tr = tr.setNodeMarkup(pos, undefined, {
46
- ...node.attrs,
47
- checked: allTasks ? null : (node.attrs.checked ?? false),
48
- });
49
- }
50
- view.dispatch(tr);
51
- }
@@ -1,29 +0,0 @@
1
- import type { Node as ProseNode } from '@milkdown/kit/prose/model';
2
- import type { EditorView } from '@milkdown/kit/prose/view';
3
- import { type TextRect } from './types.js';
4
- export interface TextSpan {
5
- from: number;
6
- to: number;
7
- start: number;
8
- end: number;
9
- kind: 'text' | 'break' | 'embed' | 'separator';
10
- }
11
- export interface TextProjection {
12
- text: string;
13
- spans: TextSpan[];
14
- }
15
- export declare function readableProjection(doc: ProseNode): TextProjection;
16
- export declare function projectionPosition(projection: TextProjection, offset: number, side: -1 | 1): number;
17
- export declare function projectionOffset(projection: TextProjection, position: number): number;
18
- export declare function validateOffsets(text: string, from: number, to: number): void;
19
- export declare function textRect(rect: Pick<TextRect, 'left' | 'top' | 'right' | 'bottom'>): TextRect;
20
- export declare function formattedRects(view: EditorView, projection: TextProjection, from: number, to: number): TextRect[];
21
- export declare function literalRangesRects(plain: HTMLTextAreaElement, ranges: readonly {
22
- from: number;
23
- to: number;
24
- }[]): TextRect[][];
25
- export declare function intersectsViewport(rect: Pick<TextRect, 'top' | 'right' | 'bottom' | 'left'>, viewport: TextRect): boolean;
26
- export declare function literalVisibleRanges(plain: HTMLTextAreaElement, viewport: TextRect): {
27
- from: number;
28
- to: number;
29
- }[];
@@ -1,256 +0,0 @@
1
- import { InkKitError } from './types.js';
2
- import { fileReference } from './linked-syntax.js';
3
- import { imageBlockName } from './image-blocks.js';
4
- export function readableProjection(doc) {
5
- const result = { text: '', spans: [] };
6
- let blocks = 0;
7
- const append = (text, start, end, kind) => {
8
- const from = result.text.length;
9
- result.text += text;
10
- result.spans.push({ from, to: result.text.length, start, end, kind });
11
- };
12
- const block = (start) => {
13
- if (blocks++)
14
- append('\n', result.spans.at(-1)?.end ?? start, start, 'separator');
15
- };
16
- doc.descendants((node, position) => {
17
- const name = node.type.name;
18
- if (name === 'comment_inline' ||
19
- name === 'comment_block' ||
20
- name === 'reference_definition')
21
- return false;
22
- if (name === 'inkkit_callout') {
23
- block(position);
24
- append(node.attrs.title ||
25
- String(node.attrs.kind).replace(/^./, (value) => value.toUpperCase()), position, position + 1, 'embed');
26
- return true;
27
- }
28
- if (name === imageBlockName) {
29
- block(position);
30
- append(fileReference(node).label || 'Image', position, position + node.nodeSize, 'embed');
31
- return false;
32
- }
33
- if (node.isTextblock) {
34
- block(position + 1);
35
- if (!node.content.size)
36
- append('', position + 1, position + 1, 'text');
37
- return true;
38
- }
39
- if (node.isText) {
40
- append(node.text ?? '', position, position + node.nodeSize, 'text');
41
- return false;
42
- }
43
- if (name === 'hardbreak' || name === 'hard_break') {
44
- append('\n', position, position + node.nodeSize, 'break');
45
- return false;
46
- }
47
- if (name === 'image' ||
48
- name === 'footnote_reference' ||
49
- name === 'inkkit_wiki_link') {
50
- append(name === 'image'
51
- ? fileReference(node).label || 'Image'
52
- : name === 'inkkit_wiki_link'
53
- ? String(node.attrs.label || node.attrs.target || 'Link')
54
- : `[${node.attrs.label}]`, position, position + node.nodeSize, 'embed');
55
- return false;
56
- }
57
- return true;
58
- });
59
- return result;
60
- }
61
- export function projectionPosition(projection, offset, side) {
62
- const spans = projection.spans;
63
- const span = spans.find((span) => (offset > span.from && offset < span.to) ||
64
- (side > 0 ? offset === span.from : offset === span.to)) ?? (side > 0 ? spans.at(-1) : spans[0]);
65
- if (!span)
66
- return 0;
67
- if (span.kind === 'text')
68
- return (span.start +
69
- Math.max(0, Math.min(offset - span.from, span.to - span.from)));
70
- return offset <= span.from ? span.start : span.end;
71
- }
72
- export function projectionOffset(projection, position) {
73
- for (const span of projection.spans) {
74
- if (position < span.start)
75
- return span.from;
76
- if (position <= span.end)
77
- return span.kind === 'text'
78
- ? span.from + Math.max(0, position - span.start)
79
- : position === span.start
80
- ? span.from
81
- : span.to;
82
- }
83
- return projection.text.length;
84
- }
85
- export function validateOffsets(text, from, to) {
86
- const split = (offset) => offset > 0 &&
87
- offset < text.length &&
88
- /[\uD800-\uDBFF]/.test(text[offset - 1]) &&
89
- /[\uDC00-\uDFFF]/.test(text[offset]);
90
- if (!Number.isInteger(from) ||
91
- !Number.isInteger(to) ||
92
- from < 0 ||
93
- to < from ||
94
- to > text.length ||
95
- split(from) ||
96
- split(to))
97
- throw new InkKitError('invalid-range', 'Range must use valid UTF-16 boundaries in the text snapshot');
98
- }
99
- export function textRect(rect) {
100
- return {
101
- left: rect.left,
102
- top: rect.top,
103
- right: rect.right,
104
- bottom: rect.bottom,
105
- width: Math.max(0, rect.right - rect.left),
106
- height: Math.max(0, rect.bottom - rect.top),
107
- };
108
- }
109
- export function formattedRects(view, projection, from, to) {
110
- if (from === to) {
111
- const position = projectionPosition(projection, from, 1);
112
- const dom = view.domAtPos(position);
113
- const element = dom.node.nodeType === 1 ? dom.node : dom.node.parentElement;
114
- if (!element?.getClientRects().length)
115
- return [];
116
- return [textRect(view.coordsAtPos(position))];
117
- }
118
- const rects = [];
119
- let low = 0;
120
- let high = projection.spans.length;
121
- while (low < high) {
122
- const middle = (low + high) >>> 1;
123
- if (projection.spans[middle].to <= from)
124
- low = middle + 1;
125
- else
126
- high = middle;
127
- }
128
- for (let index = low; index < projection.spans.length; index++) {
129
- const span = projection.spans[index];
130
- if (span.from >= to)
131
- break;
132
- if (span.kind === 'separator')
133
- continue;
134
- if (span.kind === 'embed' || span.kind === 'break') {
135
- const node = view.nodeDOM(span.start);
136
- if (node instanceof Element) {
137
- // A standalone image's node is its full-width row; the card is what shows.
138
- const label = node.matches('.inkkit-callout')
139
- ? (node.querySelector(':scope > .inkkit-callout-header > .inkkit-callout-title') ?? node)
140
- : node.matches('.inkkit-image-block')
141
- ? (node.firstElementChild ?? node)
142
- : node;
143
- rects.push(...Array.from(label.getClientRects(), textRect));
144
- }
145
- continue;
146
- }
147
- const start = view.domAtPos(span.start + Math.max(0, from - span.from), 1);
148
- const end = view.domAtPos(span.start + Math.min(span.to - span.from, to - span.from), -1);
149
- const range = view.dom.ownerDocument.createRange();
150
- range.setStart(start.node, start.offset);
151
- range.setEnd(end.node, end.offset);
152
- rects.push(...Array.from(range.getClientRects(), textRect));
153
- }
154
- return rects.filter((rect) => rect.height > 0 && rect.width >= 0);
155
- }
156
- function withLiteralLayout(plain, measure) {
157
- if (!plain.clientWidth || !plain.clientHeight)
158
- return undefined;
159
- const document = plain.ownerDocument;
160
- const style = document.defaultView.getComputedStyle(plain);
161
- const mirror = document.createElement('div');
162
- for (const property of [
163
- 'font',
164
- 'font-family',
165
- 'font-size',
166
- 'font-weight',
167
- 'font-style',
168
- 'font-stretch',
169
- 'font-variant',
170
- 'line-height',
171
- 'letter-spacing',
172
- 'word-spacing',
173
- 'tab-size',
174
- 'padding',
175
- 'text-indent',
176
- 'text-transform',
177
- 'direction',
178
- 'word-break',
179
- ])
180
- mirror.style.setProperty(property, style.getPropertyValue(property));
181
- const origin = plain.getBoundingClientRect();
182
- Object.assign(mirror.style, {
183
- position: 'fixed',
184
- visibility: 'hidden',
185
- pointerEvents: 'none',
186
- left: `${origin.left + plain.clientLeft - plain.scrollLeft}px`,
187
- top: `${origin.top + plain.clientTop - plain.scrollTop}px`,
188
- width: `${plain.clientWidth}px`,
189
- boxSizing: 'border-box',
190
- whiteSpace: plain.wrap === 'off' ? 'pre' : 'pre-wrap',
191
- overflowWrap: plain.wrap === 'off' ? 'normal' : 'break-word',
192
- });
193
- mirror.setAttribute('aria-hidden', 'true');
194
- const text = document.createTextNode(plain.value + '\u200b');
195
- mirror.append(text);
196
- document.body.append(mirror);
197
- try {
198
- return measure(text);
199
- }
200
- finally {
201
- mirror.remove();
202
- }
203
- }
204
- export function literalRangesRects(plain, ranges) {
205
- return (withLiteralLayout(plain, (text) => {
206
- const range = plain.ownerDocument.createRange();
207
- return ranges.map(({ from, to }) => {
208
- range.setStart(text, from);
209
- range.setEnd(text, from === to ? Math.min(to + 1, text.length) : to);
210
- return Array.from(range.getClientRects(), (rect) => textRect({
211
- left: rect.left,
212
- right: from === to ? rect.left : rect.right,
213
- top: rect.top,
214
- bottom: rect.bottom,
215
- }));
216
- });
217
- }) ?? ranges.map(() => []));
218
- }
219
- export function intersectsViewport(rect, viewport) {
220
- return (viewport.width > 0 &&
221
- viewport.height > 0 &&
222
- rect.bottom > viewport.top &&
223
- rect.top < viewport.bottom &&
224
- rect.right >= viewport.left &&
225
- rect.left < viewport.right);
226
- }
227
- export function literalVisibleRanges(plain, viewport) {
228
- return (withLiteralLayout(plain, (text) => {
229
- const range = plain.ownerDocument.createRange();
230
- const ranges = [];
231
- const visible = (from, to) => {
232
- range.setStart(text, from);
233
- range.setEnd(text, to);
234
- return Array.from(range.getClientRects()).some((rect) => intersectsViewport(rect, viewport));
235
- };
236
- for (let start = 0; start < plain.value.length;) {
237
- const newline = plain.value.indexOf('\n', start);
238
- const end = newline < 0 ? plain.value.length : newline + 1;
239
- if (visible(start, end)) {
240
- for (let from = start; from < end;) {
241
- const to = from + (plain.value.codePointAt(from) > 0xffff ? 2 : 1);
242
- if (visible(from, to)) {
243
- const previous = ranges.at(-1);
244
- if (previous?.to === from)
245
- previous.to = to;
246
- else
247
- ranges.push({ from, to });
248
- }
249
- from = to;
250
- }
251
- }
252
- start = end;
253
- }
254
- return ranges;
255
- }) ?? []);
256
- }
package/dist/types.js DELETED
@@ -1,8 +0,0 @@
1
- export class InkKitError extends Error {
2
- code;
3
- constructor(code, message) {
4
- super(message);
5
- this.code = code;
6
- this.name = 'InkKitError';
7
- }
8
- }