@sethumadhavan004/ink-editor 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/dist/index.js ADDED
@@ -0,0 +1,2670 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ InkEditor: () => InkEditor,
34
+ MINIMAL_DEFAULTS: () => MINIMAL_DEFAULTS,
35
+ PARCHMENT_DEFAULTS: () => PARCHMENT_DEFAULTS
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/InkEditor.tsx
40
+ var import_react5 = require("@tiptap/react");
41
+ var import_starter_kit = __toESM(require("@tiptap/starter-kit"));
42
+ var import_extension_text_align = __toESM(require("@tiptap/extension-text-align"));
43
+ var import_extension_underline = __toESM(require("@tiptap/extension-underline"));
44
+ var import_react6 = require("react");
45
+
46
+ // node_modules/@tiptap/core/dist/index.js
47
+ var import_state = require("@tiptap/pm/state");
48
+ var import_view = require("@tiptap/pm/view");
49
+ var import_keymap = require("@tiptap/pm/keymap");
50
+ var import_model = require("@tiptap/pm/model");
51
+ var import_transform = require("@tiptap/pm/transform");
52
+ var import_commands = require("@tiptap/pm/commands");
53
+ var import_schema_list = require("@tiptap/pm/schema-list");
54
+ function createChainableState(config) {
55
+ const { state, transaction } = config;
56
+ let { selection } = transaction;
57
+ let { doc } = transaction;
58
+ let { storedMarks } = transaction;
59
+ return {
60
+ ...state,
61
+ apply: state.apply.bind(state),
62
+ applyTransaction: state.applyTransaction.bind(state),
63
+ plugins: state.plugins,
64
+ schema: state.schema,
65
+ reconfigure: state.reconfigure.bind(state),
66
+ toJSON: state.toJSON.bind(state),
67
+ get storedMarks() {
68
+ return storedMarks;
69
+ },
70
+ get selection() {
71
+ return selection;
72
+ },
73
+ get doc() {
74
+ return doc;
75
+ },
76
+ get tr() {
77
+ selection = transaction.selection;
78
+ doc = transaction.doc;
79
+ storedMarks = transaction.storedMarks;
80
+ return transaction;
81
+ }
82
+ };
83
+ }
84
+ var CommandManager = class {
85
+ constructor(props) {
86
+ this.editor = props.editor;
87
+ this.rawCommands = this.editor.extensionManager.commands;
88
+ this.customState = props.state;
89
+ }
90
+ get hasCustomState() {
91
+ return !!this.customState;
92
+ }
93
+ get state() {
94
+ return this.customState || this.editor.state;
95
+ }
96
+ get commands() {
97
+ const { rawCommands, editor, state } = this;
98
+ const { view } = editor;
99
+ const { tr } = state;
100
+ const props = this.buildProps(tr);
101
+ return Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
102
+ const method = (...args) => {
103
+ const callback = command2(...args)(props);
104
+ if (!tr.getMeta("preventDispatch") && !this.hasCustomState) {
105
+ view.dispatch(tr);
106
+ }
107
+ return callback;
108
+ };
109
+ return [name, method];
110
+ }));
111
+ }
112
+ get chain() {
113
+ return () => this.createChain();
114
+ }
115
+ get can() {
116
+ return () => this.createCan();
117
+ }
118
+ createChain(startTr, shouldDispatch = true) {
119
+ const { rawCommands, editor, state } = this;
120
+ const { view } = editor;
121
+ const callbacks = [];
122
+ const hasStartTransaction = !!startTr;
123
+ const tr = startTr || state.tr;
124
+ const run = () => {
125
+ if (!hasStartTransaction && shouldDispatch && !tr.getMeta("preventDispatch") && !this.hasCustomState) {
126
+ view.dispatch(tr);
127
+ }
128
+ return callbacks.every((callback) => callback === true);
129
+ };
130
+ const chain = {
131
+ ...Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
132
+ const chainedCommand = (...args) => {
133
+ const props = this.buildProps(tr, shouldDispatch);
134
+ const callback = command2(...args)(props);
135
+ callbacks.push(callback);
136
+ return chain;
137
+ };
138
+ return [name, chainedCommand];
139
+ })),
140
+ run
141
+ };
142
+ return chain;
143
+ }
144
+ createCan(startTr) {
145
+ const { rawCommands, state } = this;
146
+ const dispatch = false;
147
+ const tr = startTr || state.tr;
148
+ const props = this.buildProps(tr, dispatch);
149
+ const formattedCommands = Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
150
+ return [name, (...args) => command2(...args)({ ...props, dispatch: void 0 })];
151
+ }));
152
+ return {
153
+ ...formattedCommands,
154
+ chain: () => this.createChain(tr, dispatch)
155
+ };
156
+ }
157
+ buildProps(tr, shouldDispatch = true) {
158
+ const { rawCommands, editor, state } = this;
159
+ const { view } = editor;
160
+ const props = {
161
+ tr,
162
+ editor,
163
+ view,
164
+ state: createChainableState({
165
+ state,
166
+ transaction: tr
167
+ }),
168
+ dispatch: shouldDispatch ? () => void 0 : void 0,
169
+ chain: () => this.createChain(tr, shouldDispatch),
170
+ can: () => this.createCan(tr),
171
+ get commands() {
172
+ return Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
173
+ return [name, (...args) => command2(...args)(props)];
174
+ }));
175
+ }
176
+ };
177
+ return props;
178
+ }
179
+ };
180
+ function getExtensionField(extension, field, context) {
181
+ if (extension.config[field] === void 0 && extension.parent) {
182
+ return getExtensionField(extension.parent, field, context);
183
+ }
184
+ if (typeof extension.config[field] === "function") {
185
+ const value = extension.config[field].bind({
186
+ ...context,
187
+ parent: extension.parent ? getExtensionField(extension.parent, field, context) : null
188
+ });
189
+ return value;
190
+ }
191
+ return extension.config[field];
192
+ }
193
+ function splitExtensions(extensions) {
194
+ const baseExtensions = extensions.filter((extension) => extension.type === "extension");
195
+ const nodeExtensions = extensions.filter((extension) => extension.type === "node");
196
+ const markExtensions = extensions.filter((extension) => extension.type === "mark");
197
+ return {
198
+ baseExtensions,
199
+ nodeExtensions,
200
+ markExtensions
201
+ };
202
+ }
203
+ function getNodeType(nameOrType, schema) {
204
+ if (typeof nameOrType === "string") {
205
+ if (!schema.nodes[nameOrType]) {
206
+ throw Error(`There is no node type named '${nameOrType}'. Maybe you forgot to add the extension?`);
207
+ }
208
+ return schema.nodes[nameOrType];
209
+ }
210
+ return nameOrType;
211
+ }
212
+ function isFunction(value) {
213
+ return typeof value === "function";
214
+ }
215
+ function callOrReturn(value, context = void 0, ...props) {
216
+ if (isFunction(value)) {
217
+ if (context) {
218
+ return value.bind(context)(...props);
219
+ }
220
+ return value(...props);
221
+ }
222
+ return value;
223
+ }
224
+ function isRegExp(value) {
225
+ return Object.prototype.toString.call(value) === "[object RegExp]";
226
+ }
227
+ function getType(value) {
228
+ return Object.prototype.toString.call(value).slice(8, -1);
229
+ }
230
+ function isPlainObject(value) {
231
+ if (getType(value) !== "Object") {
232
+ return false;
233
+ }
234
+ return value.constructor === Object && Object.getPrototypeOf(value) === Object.prototype;
235
+ }
236
+ function mergeDeep(target, source) {
237
+ const output = { ...target };
238
+ if (isPlainObject(target) && isPlainObject(source)) {
239
+ Object.keys(source).forEach((key) => {
240
+ if (isPlainObject(source[key]) && isPlainObject(target[key])) {
241
+ output[key] = mergeDeep(target[key], source[key]);
242
+ } else {
243
+ output[key] = source[key];
244
+ }
245
+ });
246
+ }
247
+ return output;
248
+ }
249
+ var Extension = class _Extension {
250
+ constructor(config = {}) {
251
+ this.type = "extension";
252
+ this.name = "extension";
253
+ this.parent = null;
254
+ this.child = null;
255
+ this.config = {
256
+ name: this.name,
257
+ defaultOptions: {}
258
+ };
259
+ this.config = {
260
+ ...this.config,
261
+ ...config
262
+ };
263
+ this.name = this.config.name;
264
+ if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
265
+ console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
266
+ }
267
+ this.options = this.config.defaultOptions;
268
+ if (this.config.addOptions) {
269
+ this.options = callOrReturn(getExtensionField(this, "addOptions", {
270
+ name: this.name
271
+ }));
272
+ }
273
+ this.storage = callOrReturn(getExtensionField(this, "addStorage", {
274
+ name: this.name,
275
+ options: this.options
276
+ })) || {};
277
+ }
278
+ static create(config = {}) {
279
+ return new _Extension(config);
280
+ }
281
+ configure(options = {}) {
282
+ const extension = this.extend({
283
+ ...this.config,
284
+ addOptions: () => {
285
+ return mergeDeep(this.options, options);
286
+ }
287
+ });
288
+ extension.name = this.name;
289
+ extension.parent = this.parent;
290
+ return extension;
291
+ }
292
+ extend(extendedConfig = {}) {
293
+ const extension = new _Extension({ ...this.config, ...extendedConfig });
294
+ extension.parent = this;
295
+ this.child = extension;
296
+ extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name;
297
+ if (extendedConfig.defaultOptions && Object.keys(extendedConfig.defaultOptions).length > 0) {
298
+ console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
299
+ }
300
+ extension.options = callOrReturn(getExtensionField(extension, "addOptions", {
301
+ name: extension.name
302
+ }));
303
+ extension.storage = callOrReturn(getExtensionField(extension, "addStorage", {
304
+ name: extension.name,
305
+ options: extension.options
306
+ }));
307
+ return extension;
308
+ }
309
+ };
310
+ function getTextBetween(startNode, range, options) {
311
+ const { from, to } = range;
312
+ const { blockSeparator = "\n\n", textSerializers = {} } = options || {};
313
+ let text = "";
314
+ startNode.nodesBetween(from, to, (node, pos, parent, index) => {
315
+ var _a;
316
+ if (node.isBlock && pos > from) {
317
+ text += blockSeparator;
318
+ }
319
+ const textSerializer = textSerializers === null || textSerializers === void 0 ? void 0 : textSerializers[node.type.name];
320
+ if (textSerializer) {
321
+ if (parent) {
322
+ text += textSerializer({
323
+ node,
324
+ pos,
325
+ parent,
326
+ index,
327
+ range
328
+ });
329
+ }
330
+ return false;
331
+ }
332
+ if (node.isText) {
333
+ text += (_a = node === null || node === void 0 ? void 0 : node.text) === null || _a === void 0 ? void 0 : _a.slice(Math.max(from, pos) - pos, to - pos);
334
+ }
335
+ });
336
+ return text;
337
+ }
338
+ function getTextSerializersFromSchema(schema) {
339
+ return Object.fromEntries(Object.entries(schema.nodes).filter(([, node]) => node.spec.toText).map(([name, node]) => [name, node.spec.toText]));
340
+ }
341
+ var ClipboardTextSerializer = Extension.create({
342
+ name: "clipboardTextSerializer",
343
+ addOptions() {
344
+ return {
345
+ blockSeparator: void 0
346
+ };
347
+ },
348
+ addProseMirrorPlugins() {
349
+ return [
350
+ new import_state.Plugin({
351
+ key: new import_state.PluginKey("clipboardTextSerializer"),
352
+ props: {
353
+ clipboardTextSerializer: () => {
354
+ const { editor } = this;
355
+ const { state, schema } = editor;
356
+ const { doc, selection } = state;
357
+ const { ranges } = selection;
358
+ const from = Math.min(...ranges.map((range2) => range2.$from.pos));
359
+ const to = Math.max(...ranges.map((range2) => range2.$to.pos));
360
+ const textSerializers = getTextSerializersFromSchema(schema);
361
+ const range = { from, to };
362
+ return getTextBetween(doc, range, {
363
+ ...this.options.blockSeparator !== void 0 ? { blockSeparator: this.options.blockSeparator } : {},
364
+ textSerializers
365
+ });
366
+ }
367
+ }
368
+ })
369
+ ];
370
+ }
371
+ });
372
+ var blur = () => ({ editor, view }) => {
373
+ requestAnimationFrame(() => {
374
+ var _a;
375
+ if (!editor.isDestroyed) {
376
+ view.dom.blur();
377
+ (_a = window === null || window === void 0 ? void 0 : window.getSelection()) === null || _a === void 0 ? void 0 : _a.removeAllRanges();
378
+ }
379
+ });
380
+ return true;
381
+ };
382
+ var clearContent = (emitUpdate = false) => ({ commands: commands2 }) => {
383
+ return commands2.setContent("", emitUpdate);
384
+ };
385
+ var clearNodes = () => ({ state, tr, dispatch }) => {
386
+ const { selection } = tr;
387
+ const { ranges } = selection;
388
+ if (!dispatch) {
389
+ return true;
390
+ }
391
+ ranges.forEach(({ $from, $to }) => {
392
+ state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
393
+ if (node.type.isText) {
394
+ return;
395
+ }
396
+ const { doc, mapping } = tr;
397
+ const $mappedFrom = doc.resolve(mapping.map(pos));
398
+ const $mappedTo = doc.resolve(mapping.map(pos + node.nodeSize));
399
+ const nodeRange = $mappedFrom.blockRange($mappedTo);
400
+ if (!nodeRange) {
401
+ return;
402
+ }
403
+ const targetLiftDepth = (0, import_transform.liftTarget)(nodeRange);
404
+ if (node.type.isTextblock) {
405
+ const { defaultType } = $mappedFrom.parent.contentMatchAt($mappedFrom.index());
406
+ tr.setNodeMarkup(nodeRange.start, defaultType);
407
+ }
408
+ if (targetLiftDepth || targetLiftDepth === 0) {
409
+ tr.lift(nodeRange, targetLiftDepth);
410
+ }
411
+ });
412
+ });
413
+ return true;
414
+ };
415
+ var command = (fn) => (props) => {
416
+ return fn(props);
417
+ };
418
+ var createParagraphNear = () => ({ state, dispatch }) => {
419
+ return (0, import_commands.createParagraphNear)(state, dispatch);
420
+ };
421
+ var cut = (originRange, targetPos) => ({ editor, tr }) => {
422
+ const { state } = editor;
423
+ const contentSlice = state.doc.slice(originRange.from, originRange.to);
424
+ tr.deleteRange(originRange.from, originRange.to);
425
+ const newPos = tr.mapping.map(targetPos);
426
+ tr.insert(newPos, contentSlice.content);
427
+ tr.setSelection(new import_state.TextSelection(tr.doc.resolve(Math.max(newPos - 1, 0))));
428
+ return true;
429
+ };
430
+ var deleteCurrentNode = () => ({ tr, dispatch }) => {
431
+ const { selection } = tr;
432
+ const currentNode = selection.$anchor.node();
433
+ if (currentNode.content.size > 0) {
434
+ return false;
435
+ }
436
+ const $pos = tr.selection.$anchor;
437
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
438
+ const node = $pos.node(depth);
439
+ if (node.type === currentNode.type) {
440
+ if (dispatch) {
441
+ const from = $pos.before(depth);
442
+ const to = $pos.after(depth);
443
+ tr.delete(from, to).scrollIntoView();
444
+ }
445
+ return true;
446
+ }
447
+ }
448
+ return false;
449
+ };
450
+ var deleteNode = (typeOrName) => ({ tr, state, dispatch }) => {
451
+ const type = getNodeType(typeOrName, state.schema);
452
+ const $pos = tr.selection.$anchor;
453
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
454
+ const node = $pos.node(depth);
455
+ if (node.type === type) {
456
+ if (dispatch) {
457
+ const from = $pos.before(depth);
458
+ const to = $pos.after(depth);
459
+ tr.delete(from, to).scrollIntoView();
460
+ }
461
+ return true;
462
+ }
463
+ }
464
+ return false;
465
+ };
466
+ var deleteRange = (range) => ({ tr, dispatch }) => {
467
+ const { from, to } = range;
468
+ if (dispatch) {
469
+ tr.delete(from, to);
470
+ }
471
+ return true;
472
+ };
473
+ var deleteSelection = () => ({ state, dispatch }) => {
474
+ return (0, import_commands.deleteSelection)(state, dispatch);
475
+ };
476
+ var enter = () => ({ commands: commands2 }) => {
477
+ return commands2.keyboardShortcut("Enter");
478
+ };
479
+ var exitCode = () => ({ state, dispatch }) => {
480
+ return (0, import_commands.exitCode)(state, dispatch);
481
+ };
482
+ function objectIncludes(object1, object2, options = { strict: true }) {
483
+ const keys = Object.keys(object2);
484
+ if (!keys.length) {
485
+ return true;
486
+ }
487
+ return keys.every((key) => {
488
+ if (options.strict) {
489
+ return object2[key] === object1[key];
490
+ }
491
+ if (isRegExp(object2[key])) {
492
+ return object2[key].test(object1[key]);
493
+ }
494
+ return object2[key] === object1[key];
495
+ });
496
+ }
497
+ function findMarkInSet(marks, type, attributes = {}) {
498
+ return marks.find((item) => {
499
+ return item.type === type && objectIncludes(
500
+ // Only check equality for the attributes that are provided
501
+ Object.fromEntries(Object.keys(attributes).map((k) => [k, item.attrs[k]])),
502
+ attributes
503
+ );
504
+ });
505
+ }
506
+ function isMarkInSet(marks, type, attributes = {}) {
507
+ return !!findMarkInSet(marks, type, attributes);
508
+ }
509
+ function getMarkRange($pos, type, attributes) {
510
+ var _a;
511
+ if (!$pos || !type) {
512
+ return;
513
+ }
514
+ let start = $pos.parent.childAfter($pos.parentOffset);
515
+ if (!start.node || !start.node.marks.some((mark2) => mark2.type === type)) {
516
+ start = $pos.parent.childBefore($pos.parentOffset);
517
+ }
518
+ if (!start.node || !start.node.marks.some((mark2) => mark2.type === type)) {
519
+ return;
520
+ }
521
+ attributes = attributes || ((_a = start.node.marks[0]) === null || _a === void 0 ? void 0 : _a.attrs);
522
+ const mark = findMarkInSet([...start.node.marks], type, attributes);
523
+ if (!mark) {
524
+ return;
525
+ }
526
+ let startIndex = start.index;
527
+ let startPos = $pos.start() + start.offset;
528
+ let endIndex = startIndex + 1;
529
+ let endPos = startPos + start.node.nodeSize;
530
+ while (startIndex > 0 && isMarkInSet([...$pos.parent.child(startIndex - 1).marks], type, attributes)) {
531
+ startIndex -= 1;
532
+ startPos -= $pos.parent.child(startIndex).nodeSize;
533
+ }
534
+ while (endIndex < $pos.parent.childCount && isMarkInSet([...$pos.parent.child(endIndex).marks], type, attributes)) {
535
+ endPos += $pos.parent.child(endIndex).nodeSize;
536
+ endIndex += 1;
537
+ }
538
+ return {
539
+ from: startPos,
540
+ to: endPos
541
+ };
542
+ }
543
+ function getMarkType(nameOrType, schema) {
544
+ if (typeof nameOrType === "string") {
545
+ if (!schema.marks[nameOrType]) {
546
+ throw Error(`There is no mark type named '${nameOrType}'. Maybe you forgot to add the extension?`);
547
+ }
548
+ return schema.marks[nameOrType];
549
+ }
550
+ return nameOrType;
551
+ }
552
+ var extendMarkRange = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
553
+ const type = getMarkType(typeOrName, state.schema);
554
+ const { doc, selection } = tr;
555
+ const { $from, from, to } = selection;
556
+ if (dispatch) {
557
+ const range = getMarkRange($from, type, attributes);
558
+ if (range && range.from <= from && range.to >= to) {
559
+ const newSelection = import_state.TextSelection.create(doc, range.from, range.to);
560
+ tr.setSelection(newSelection);
561
+ }
562
+ }
563
+ return true;
564
+ };
565
+ var first = (commands2) => (props) => {
566
+ const items = typeof commands2 === "function" ? commands2(props) : commands2;
567
+ for (let i = 0; i < items.length; i += 1) {
568
+ if (items[i](props)) {
569
+ return true;
570
+ }
571
+ }
572
+ return false;
573
+ };
574
+ function isTextSelection(value) {
575
+ return value instanceof import_state.TextSelection;
576
+ }
577
+ function minMax(value = 0, min = 0, max = 0) {
578
+ return Math.min(Math.max(value, min), max);
579
+ }
580
+ function resolveFocusPosition(doc, position = null) {
581
+ if (!position) {
582
+ return null;
583
+ }
584
+ const selectionAtStart = import_state.Selection.atStart(doc);
585
+ const selectionAtEnd = import_state.Selection.atEnd(doc);
586
+ if (position === "start" || position === true) {
587
+ return selectionAtStart;
588
+ }
589
+ if (position === "end") {
590
+ return selectionAtEnd;
591
+ }
592
+ const minPos = selectionAtStart.from;
593
+ const maxPos = selectionAtEnd.to;
594
+ if (position === "all") {
595
+ return import_state.TextSelection.create(doc, minMax(0, minPos, maxPos), minMax(doc.content.size, minPos, maxPos));
596
+ }
597
+ return import_state.TextSelection.create(doc, minMax(position, minPos, maxPos), minMax(position, minPos, maxPos));
598
+ }
599
+ function isAndroid() {
600
+ return navigator.platform === "Android" || /android/i.test(navigator.userAgent);
601
+ }
602
+ function isiOS() {
603
+ return [
604
+ "iPad Simulator",
605
+ "iPhone Simulator",
606
+ "iPod Simulator",
607
+ "iPad",
608
+ "iPhone",
609
+ "iPod"
610
+ ].includes(navigator.platform) || navigator.userAgent.includes("Mac") && "ontouchend" in document;
611
+ }
612
+ function isSafari() {
613
+ return typeof navigator !== "undefined" ? /^((?!chrome|android).)*safari/i.test(navigator.userAgent) : false;
614
+ }
615
+ var focus = (position = null, options = {}) => ({ editor, view, tr, dispatch }) => {
616
+ options = {
617
+ scrollIntoView: true,
618
+ ...options
619
+ };
620
+ const delayedFocus = () => {
621
+ if (isiOS() || isAndroid()) {
622
+ view.dom.focus();
623
+ }
624
+ requestAnimationFrame(() => {
625
+ if (!editor.isDestroyed) {
626
+ view.focus();
627
+ if (isSafari() && !isiOS() && !isAndroid()) {
628
+ view.dom.focus({ preventScroll: true });
629
+ }
630
+ }
631
+ });
632
+ };
633
+ if (view.hasFocus() && position === null || position === false) {
634
+ return true;
635
+ }
636
+ if (dispatch && position === null && !isTextSelection(editor.state.selection)) {
637
+ delayedFocus();
638
+ return true;
639
+ }
640
+ const selection = resolveFocusPosition(tr.doc, position) || editor.state.selection;
641
+ const isSameSelection = editor.state.selection.eq(selection);
642
+ if (dispatch) {
643
+ if (!isSameSelection) {
644
+ tr.setSelection(selection);
645
+ }
646
+ if (isSameSelection && tr.storedMarks) {
647
+ tr.setStoredMarks(tr.storedMarks);
648
+ }
649
+ delayedFocus();
650
+ }
651
+ return true;
652
+ };
653
+ var forEach = (items, fn) => (props) => {
654
+ return items.every((item, index) => fn(item, { ...props, index }));
655
+ };
656
+ var insertContent = (value, options) => ({ tr, commands: commands2 }) => {
657
+ return commands2.insertContentAt({ from: tr.selection.from, to: tr.selection.to }, value, options);
658
+ };
659
+ var removeWhitespaces = (node) => {
660
+ const children = node.childNodes;
661
+ for (let i = children.length - 1; i >= 0; i -= 1) {
662
+ const child = children[i];
663
+ if (child.nodeType === 3 && child.nodeValue && /^(\n\s\s|\n)$/.test(child.nodeValue)) {
664
+ node.removeChild(child);
665
+ } else if (child.nodeType === 1) {
666
+ removeWhitespaces(child);
667
+ }
668
+ }
669
+ return node;
670
+ };
671
+ function elementFromString(value) {
672
+ const wrappedValue = `<body>${value}</body>`;
673
+ const html = new window.DOMParser().parseFromString(wrappedValue, "text/html").body;
674
+ return removeWhitespaces(html);
675
+ }
676
+ function createNodeFromContent(content, schema, options) {
677
+ if (content instanceof import_model.Node || content instanceof import_model.Fragment) {
678
+ return content;
679
+ }
680
+ options = {
681
+ slice: true,
682
+ parseOptions: {},
683
+ ...options
684
+ };
685
+ const isJSONContent = typeof content === "object" && content !== null;
686
+ const isTextContent = typeof content === "string";
687
+ if (isJSONContent) {
688
+ try {
689
+ const isArrayContent = Array.isArray(content) && content.length > 0;
690
+ if (isArrayContent) {
691
+ return import_model.Fragment.fromArray(content.map((item) => schema.nodeFromJSON(item)));
692
+ }
693
+ const node = schema.nodeFromJSON(content);
694
+ if (options.errorOnInvalidContent) {
695
+ node.check();
696
+ }
697
+ return node;
698
+ } catch (error) {
699
+ if (options.errorOnInvalidContent) {
700
+ throw new Error("[tiptap error]: Invalid JSON content", { cause: error });
701
+ }
702
+ console.warn("[tiptap warn]: Invalid content.", "Passed value:", content, "Error:", error);
703
+ return createNodeFromContent("", schema, options);
704
+ }
705
+ }
706
+ if (isTextContent) {
707
+ if (options.errorOnInvalidContent) {
708
+ let hasInvalidContent = false;
709
+ let invalidContent = "";
710
+ const contentCheckSchema = new import_model.Schema({
711
+ topNode: schema.spec.topNode,
712
+ marks: schema.spec.marks,
713
+ // Prosemirror's schemas are executed such that: the last to execute, matches last
714
+ // This means that we can add a catch-all node at the end of the schema to catch any content that we don't know how to handle
715
+ nodes: schema.spec.nodes.append({
716
+ __tiptap__private__unknown__catch__all__node: {
717
+ content: "inline*",
718
+ group: "block",
719
+ parseDOM: [
720
+ {
721
+ tag: "*",
722
+ getAttrs: (e) => {
723
+ hasInvalidContent = true;
724
+ invalidContent = typeof e === "string" ? e : e.outerHTML;
725
+ return null;
726
+ }
727
+ }
728
+ ]
729
+ }
730
+ })
731
+ });
732
+ if (options.slice) {
733
+ import_model.DOMParser.fromSchema(contentCheckSchema).parseSlice(elementFromString(content), options.parseOptions);
734
+ } else {
735
+ import_model.DOMParser.fromSchema(contentCheckSchema).parse(elementFromString(content), options.parseOptions);
736
+ }
737
+ if (options.errorOnInvalidContent && hasInvalidContent) {
738
+ throw new Error("[tiptap error]: Invalid HTML content", { cause: new Error(`Invalid element found: ${invalidContent}`) });
739
+ }
740
+ }
741
+ const parser = import_model.DOMParser.fromSchema(schema);
742
+ if (options.slice) {
743
+ return parser.parseSlice(elementFromString(content), options.parseOptions).content;
744
+ }
745
+ return parser.parse(elementFromString(content), options.parseOptions);
746
+ }
747
+ return createNodeFromContent("", schema, options);
748
+ }
749
+ function selectionToInsertionEnd(tr, startLen, bias) {
750
+ const last = tr.steps.length - 1;
751
+ if (last < startLen) {
752
+ return;
753
+ }
754
+ const step = tr.steps[last];
755
+ if (!(step instanceof import_transform.ReplaceStep || step instanceof import_transform.ReplaceAroundStep)) {
756
+ return;
757
+ }
758
+ const map = tr.mapping.maps[last];
759
+ let end = 0;
760
+ map.forEach((_from, _to, _newFrom, newTo) => {
761
+ if (end === 0) {
762
+ end = newTo;
763
+ }
764
+ });
765
+ tr.setSelection(import_state.Selection.near(tr.doc.resolve(end), bias));
766
+ }
767
+ var isFragment = (nodeOrFragment) => {
768
+ return !("type" in nodeOrFragment);
769
+ };
770
+ var insertContentAt = (position, value, options) => ({ tr, dispatch, editor }) => {
771
+ var _a;
772
+ if (dispatch) {
773
+ options = {
774
+ parseOptions: editor.options.parseOptions,
775
+ updateSelection: true,
776
+ applyInputRules: false,
777
+ applyPasteRules: false,
778
+ ...options
779
+ };
780
+ let content;
781
+ const emitContentError = (error) => {
782
+ editor.emit("contentError", {
783
+ editor,
784
+ error,
785
+ disableCollaboration: () => {
786
+ if (editor.storage.collaboration) {
787
+ editor.storage.collaboration.isDisabled = true;
788
+ }
789
+ }
790
+ });
791
+ };
792
+ const parseOptions = {
793
+ preserveWhitespace: "full",
794
+ ...options.parseOptions
795
+ };
796
+ if (!options.errorOnInvalidContent && !editor.options.enableContentCheck && editor.options.emitContentError) {
797
+ try {
798
+ createNodeFromContent(value, editor.schema, {
799
+ parseOptions,
800
+ errorOnInvalidContent: true
801
+ });
802
+ } catch (e) {
803
+ emitContentError(e);
804
+ }
805
+ }
806
+ try {
807
+ content = createNodeFromContent(value, editor.schema, {
808
+ parseOptions,
809
+ errorOnInvalidContent: (_a = options.errorOnInvalidContent) !== null && _a !== void 0 ? _a : editor.options.enableContentCheck
810
+ });
811
+ } catch (e) {
812
+ emitContentError(e);
813
+ return false;
814
+ }
815
+ let { from, to } = typeof position === "number" ? { from: position, to: position } : { from: position.from, to: position.to };
816
+ let isOnlyTextContent = true;
817
+ let isOnlyBlockContent = true;
818
+ const nodes = isFragment(content) ? content : [content];
819
+ nodes.forEach((node) => {
820
+ node.check();
821
+ isOnlyTextContent = isOnlyTextContent ? node.isText && node.marks.length === 0 : false;
822
+ isOnlyBlockContent = isOnlyBlockContent ? node.isBlock : false;
823
+ });
824
+ if (from === to && isOnlyBlockContent) {
825
+ const { parent } = tr.doc.resolve(from);
826
+ const isEmptyTextBlock = parent.isTextblock && !parent.type.spec.code && !parent.childCount;
827
+ if (isEmptyTextBlock) {
828
+ from -= 1;
829
+ to += 1;
830
+ }
831
+ }
832
+ let newContent;
833
+ if (isOnlyTextContent) {
834
+ if (Array.isArray(value)) {
835
+ newContent = value.map((v) => v.text || "").join("");
836
+ } else if (value instanceof import_model.Fragment) {
837
+ let text = "";
838
+ value.forEach((node) => {
839
+ if (node.text) {
840
+ text += node.text;
841
+ }
842
+ });
843
+ newContent = text;
844
+ } else if (typeof value === "object" && !!value && !!value.text) {
845
+ newContent = value.text;
846
+ } else {
847
+ newContent = value;
848
+ }
849
+ tr.insertText(newContent, from, to);
850
+ } else {
851
+ newContent = content;
852
+ tr.replaceWith(from, to, newContent);
853
+ }
854
+ if (options.updateSelection) {
855
+ selectionToInsertionEnd(tr, tr.steps.length - 1, -1);
856
+ }
857
+ if (options.applyInputRules) {
858
+ tr.setMeta("applyInputRules", { from, text: newContent });
859
+ }
860
+ if (options.applyPasteRules) {
861
+ tr.setMeta("applyPasteRules", { from, text: newContent });
862
+ }
863
+ }
864
+ return true;
865
+ };
866
+ var joinUp = () => ({ state, dispatch }) => {
867
+ return (0, import_commands.joinUp)(state, dispatch);
868
+ };
869
+ var joinDown = () => ({ state, dispatch }) => {
870
+ return (0, import_commands.joinDown)(state, dispatch);
871
+ };
872
+ var joinBackward = () => ({ state, dispatch }) => {
873
+ return (0, import_commands.joinBackward)(state, dispatch);
874
+ };
875
+ var joinForward = () => ({ state, dispatch }) => {
876
+ return (0, import_commands.joinForward)(state, dispatch);
877
+ };
878
+ var joinItemBackward = () => ({ state, dispatch, tr }) => {
879
+ try {
880
+ const point = (0, import_transform.joinPoint)(state.doc, state.selection.$from.pos, -1);
881
+ if (point === null || point === void 0) {
882
+ return false;
883
+ }
884
+ tr.join(point, 2);
885
+ if (dispatch) {
886
+ dispatch(tr);
887
+ }
888
+ return true;
889
+ } catch {
890
+ return false;
891
+ }
892
+ };
893
+ var joinItemForward = () => ({ state, dispatch, tr }) => {
894
+ try {
895
+ const point = (0, import_transform.joinPoint)(state.doc, state.selection.$from.pos, 1);
896
+ if (point === null || point === void 0) {
897
+ return false;
898
+ }
899
+ tr.join(point, 2);
900
+ if (dispatch) {
901
+ dispatch(tr);
902
+ }
903
+ return true;
904
+ } catch {
905
+ return false;
906
+ }
907
+ };
908
+ var joinTextblockBackward = () => ({ state, dispatch }) => {
909
+ return (0, import_commands.joinTextblockBackward)(state, dispatch);
910
+ };
911
+ var joinTextblockForward = () => ({ state, dispatch }) => {
912
+ return (0, import_commands.joinTextblockForward)(state, dispatch);
913
+ };
914
+ function isMacOS() {
915
+ return typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
916
+ }
917
+ function normalizeKeyName(name) {
918
+ const parts = name.split(/-(?!$)/);
919
+ let result = parts[parts.length - 1];
920
+ if (result === "Space") {
921
+ result = " ";
922
+ }
923
+ let alt;
924
+ let ctrl;
925
+ let shift;
926
+ let meta;
927
+ for (let i = 0; i < parts.length - 1; i += 1) {
928
+ const mod = parts[i];
929
+ if (/^(cmd|meta|m)$/i.test(mod)) {
930
+ meta = true;
931
+ } else if (/^a(lt)?$/i.test(mod)) {
932
+ alt = true;
933
+ } else if (/^(c|ctrl|control)$/i.test(mod)) {
934
+ ctrl = true;
935
+ } else if (/^s(hift)?$/i.test(mod)) {
936
+ shift = true;
937
+ } else if (/^mod$/i.test(mod)) {
938
+ if (isiOS() || isMacOS()) {
939
+ meta = true;
940
+ } else {
941
+ ctrl = true;
942
+ }
943
+ } else {
944
+ throw new Error(`Unrecognized modifier name: ${mod}`);
945
+ }
946
+ }
947
+ if (alt) {
948
+ result = `Alt-${result}`;
949
+ }
950
+ if (ctrl) {
951
+ result = `Ctrl-${result}`;
952
+ }
953
+ if (meta) {
954
+ result = `Meta-${result}`;
955
+ }
956
+ if (shift) {
957
+ result = `Shift-${result}`;
958
+ }
959
+ return result;
960
+ }
961
+ var keyboardShortcut = (name) => ({ editor, view, tr, dispatch }) => {
962
+ const keys = normalizeKeyName(name).split(/-(?!$)/);
963
+ const key = keys.find((item) => !["Alt", "Ctrl", "Meta", "Shift"].includes(item));
964
+ const event = new KeyboardEvent("keydown", {
965
+ key: key === "Space" ? " " : key,
966
+ altKey: keys.includes("Alt"),
967
+ ctrlKey: keys.includes("Ctrl"),
968
+ metaKey: keys.includes("Meta"),
969
+ shiftKey: keys.includes("Shift"),
970
+ bubbles: true,
971
+ cancelable: true
972
+ });
973
+ const capturedTransaction = editor.captureTransaction(() => {
974
+ view.someProp("handleKeyDown", (f) => f(view, event));
975
+ });
976
+ capturedTransaction === null || capturedTransaction === void 0 ? void 0 : capturedTransaction.steps.forEach((step) => {
977
+ const newStep = step.map(tr.mapping);
978
+ if (newStep && dispatch) {
979
+ tr.maybeStep(newStep);
980
+ }
981
+ });
982
+ return true;
983
+ };
984
+ function isNodeActive(state, typeOrName, attributes = {}) {
985
+ const { from, to, empty } = state.selection;
986
+ const type = typeOrName ? getNodeType(typeOrName, state.schema) : null;
987
+ const nodeRanges = [];
988
+ state.doc.nodesBetween(from, to, (node, pos) => {
989
+ if (node.isText) {
990
+ return;
991
+ }
992
+ const relativeFrom = Math.max(from, pos);
993
+ const relativeTo = Math.min(to, pos + node.nodeSize);
994
+ nodeRanges.push({
995
+ node,
996
+ from: relativeFrom,
997
+ to: relativeTo
998
+ });
999
+ });
1000
+ const selectionRange = to - from;
1001
+ const matchedNodeRanges = nodeRanges.filter((nodeRange) => {
1002
+ if (!type) {
1003
+ return true;
1004
+ }
1005
+ return type.name === nodeRange.node.type.name;
1006
+ }).filter((nodeRange) => objectIncludes(nodeRange.node.attrs, attributes, { strict: false }));
1007
+ if (empty) {
1008
+ return !!matchedNodeRanges.length;
1009
+ }
1010
+ const range = matchedNodeRanges.reduce((sum, nodeRange) => sum + nodeRange.to - nodeRange.from, 0);
1011
+ return range >= selectionRange;
1012
+ }
1013
+ var lift = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
1014
+ const type = getNodeType(typeOrName, state.schema);
1015
+ const isActive = isNodeActive(state, type, attributes);
1016
+ if (!isActive) {
1017
+ return false;
1018
+ }
1019
+ return (0, import_commands.lift)(state, dispatch);
1020
+ };
1021
+ var liftEmptyBlock = () => ({ state, dispatch }) => {
1022
+ return (0, import_commands.liftEmptyBlock)(state, dispatch);
1023
+ };
1024
+ var liftListItem = (typeOrName) => ({ state, dispatch }) => {
1025
+ const type = getNodeType(typeOrName, state.schema);
1026
+ return (0, import_schema_list.liftListItem)(type)(state, dispatch);
1027
+ };
1028
+ var newlineInCode = () => ({ state, dispatch }) => {
1029
+ return (0, import_commands.newlineInCode)(state, dispatch);
1030
+ };
1031
+ function getSchemaTypeNameByName(name, schema) {
1032
+ if (schema.nodes[name]) {
1033
+ return "node";
1034
+ }
1035
+ if (schema.marks[name]) {
1036
+ return "mark";
1037
+ }
1038
+ return null;
1039
+ }
1040
+ function deleteProps(obj, propOrProps) {
1041
+ const props = typeof propOrProps === "string" ? [propOrProps] : propOrProps;
1042
+ return Object.keys(obj).reduce((newObj, prop) => {
1043
+ if (!props.includes(prop)) {
1044
+ newObj[prop] = obj[prop];
1045
+ }
1046
+ return newObj;
1047
+ }, {});
1048
+ }
1049
+ var resetAttributes = (typeOrName, attributes) => ({ tr, state, dispatch }) => {
1050
+ let nodeType = null;
1051
+ let markType = null;
1052
+ const schemaType = getSchemaTypeNameByName(typeof typeOrName === "string" ? typeOrName : typeOrName.name, state.schema);
1053
+ if (!schemaType) {
1054
+ return false;
1055
+ }
1056
+ if (schemaType === "node") {
1057
+ nodeType = getNodeType(typeOrName, state.schema);
1058
+ }
1059
+ if (schemaType === "mark") {
1060
+ markType = getMarkType(typeOrName, state.schema);
1061
+ }
1062
+ if (dispatch) {
1063
+ tr.selection.ranges.forEach((range) => {
1064
+ state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
1065
+ if (nodeType && nodeType === node.type) {
1066
+ tr.setNodeMarkup(pos, void 0, deleteProps(node.attrs, attributes));
1067
+ }
1068
+ if (markType && node.marks.length) {
1069
+ node.marks.forEach((mark) => {
1070
+ if (markType === mark.type) {
1071
+ tr.addMark(pos, pos + node.nodeSize, markType.create(deleteProps(mark.attrs, attributes)));
1072
+ }
1073
+ });
1074
+ }
1075
+ });
1076
+ });
1077
+ }
1078
+ return true;
1079
+ };
1080
+ var scrollIntoView = () => ({ tr, dispatch }) => {
1081
+ if (dispatch) {
1082
+ tr.scrollIntoView();
1083
+ }
1084
+ return true;
1085
+ };
1086
+ var selectAll = () => ({ tr, dispatch }) => {
1087
+ if (dispatch) {
1088
+ const selection = new import_state.AllSelection(tr.doc);
1089
+ tr.setSelection(selection);
1090
+ }
1091
+ return true;
1092
+ };
1093
+ var selectNodeBackward = () => ({ state, dispatch }) => {
1094
+ return (0, import_commands.selectNodeBackward)(state, dispatch);
1095
+ };
1096
+ var selectNodeForward = () => ({ state, dispatch }) => {
1097
+ return (0, import_commands.selectNodeForward)(state, dispatch);
1098
+ };
1099
+ var selectParentNode = () => ({ state, dispatch }) => {
1100
+ return (0, import_commands.selectParentNode)(state, dispatch);
1101
+ };
1102
+ var selectTextblockEnd = () => ({ state, dispatch }) => {
1103
+ return (0, import_commands.selectTextblockEnd)(state, dispatch);
1104
+ };
1105
+ var selectTextblockStart = () => ({ state, dispatch }) => {
1106
+ return (0, import_commands.selectTextblockStart)(state, dispatch);
1107
+ };
1108
+ function createDocument(content, schema, parseOptions = {}, options = {}) {
1109
+ return createNodeFromContent(content, schema, {
1110
+ slice: false,
1111
+ parseOptions,
1112
+ errorOnInvalidContent: options.errorOnInvalidContent
1113
+ });
1114
+ }
1115
+ var setContent = (content, emitUpdate = false, parseOptions = {}, options = {}) => ({ editor, tr, dispatch, commands: commands2 }) => {
1116
+ var _a, _b;
1117
+ const { doc } = tr;
1118
+ if (parseOptions.preserveWhitespace !== "full") {
1119
+ const document2 = createDocument(content, editor.schema, parseOptions, {
1120
+ errorOnInvalidContent: (_a = options.errorOnInvalidContent) !== null && _a !== void 0 ? _a : editor.options.enableContentCheck
1121
+ });
1122
+ if (dispatch) {
1123
+ tr.replaceWith(0, doc.content.size, document2).setMeta("preventUpdate", !emitUpdate);
1124
+ }
1125
+ return true;
1126
+ }
1127
+ if (dispatch) {
1128
+ tr.setMeta("preventUpdate", !emitUpdate);
1129
+ }
1130
+ return commands2.insertContentAt({ from: 0, to: doc.content.size }, content, {
1131
+ parseOptions,
1132
+ errorOnInvalidContent: (_b = options.errorOnInvalidContent) !== null && _b !== void 0 ? _b : editor.options.enableContentCheck
1133
+ });
1134
+ };
1135
+ function getMarkAttributes(state, typeOrName) {
1136
+ const type = getMarkType(typeOrName, state.schema);
1137
+ const { from, to, empty } = state.selection;
1138
+ const marks = [];
1139
+ if (empty) {
1140
+ if (state.storedMarks) {
1141
+ marks.push(...state.storedMarks);
1142
+ }
1143
+ marks.push(...state.selection.$head.marks());
1144
+ } else {
1145
+ state.doc.nodesBetween(from, to, (node) => {
1146
+ marks.push(...node.marks);
1147
+ });
1148
+ }
1149
+ const mark = marks.find((markItem) => markItem.type.name === type.name);
1150
+ if (!mark) {
1151
+ return {};
1152
+ }
1153
+ return { ...mark.attrs };
1154
+ }
1155
+ function defaultBlockAt(match) {
1156
+ for (let i = 0; i < match.edgeCount; i += 1) {
1157
+ const { type } = match.edge(i);
1158
+ if (type.isTextblock && !type.hasRequiredAttrs()) {
1159
+ return type;
1160
+ }
1161
+ }
1162
+ return null;
1163
+ }
1164
+ function findParentNodeClosestToPos($pos, predicate) {
1165
+ for (let i = $pos.depth; i > 0; i -= 1) {
1166
+ const node = $pos.node(i);
1167
+ if (predicate(node)) {
1168
+ return {
1169
+ pos: i > 0 ? $pos.before(i) : 0,
1170
+ start: $pos.start(i),
1171
+ depth: i,
1172
+ node
1173
+ };
1174
+ }
1175
+ }
1176
+ }
1177
+ function findParentNode(predicate) {
1178
+ return (selection) => findParentNodeClosestToPos(selection.$from, predicate);
1179
+ }
1180
+ function getSplittedAttributes(extensionAttributes, typeName, attributes) {
1181
+ return Object.fromEntries(Object.entries(attributes).filter(([name]) => {
1182
+ const extensionAttribute = extensionAttributes.find((item) => {
1183
+ return item.type === typeName && item.name === name;
1184
+ });
1185
+ if (!extensionAttribute) {
1186
+ return false;
1187
+ }
1188
+ return extensionAttribute.attribute.keepOnSplit;
1189
+ }));
1190
+ }
1191
+ function isMarkActive(state, typeOrName, attributes = {}) {
1192
+ const { empty, ranges } = state.selection;
1193
+ const type = typeOrName ? getMarkType(typeOrName, state.schema) : null;
1194
+ if (empty) {
1195
+ return !!(state.storedMarks || state.selection.$from.marks()).filter((mark) => {
1196
+ if (!type) {
1197
+ return true;
1198
+ }
1199
+ return type.name === mark.type.name;
1200
+ }).find((mark) => objectIncludes(mark.attrs, attributes, { strict: false }));
1201
+ }
1202
+ let selectionRange = 0;
1203
+ const markRanges = [];
1204
+ ranges.forEach(({ $from, $to }) => {
1205
+ const from = $from.pos;
1206
+ const to = $to.pos;
1207
+ state.doc.nodesBetween(from, to, (node, pos) => {
1208
+ if (!node.isText && !node.marks.length) {
1209
+ return;
1210
+ }
1211
+ const relativeFrom = Math.max(from, pos);
1212
+ const relativeTo = Math.min(to, pos + node.nodeSize);
1213
+ const range2 = relativeTo - relativeFrom;
1214
+ selectionRange += range2;
1215
+ markRanges.push(...node.marks.map((mark) => ({
1216
+ mark,
1217
+ from: relativeFrom,
1218
+ to: relativeTo
1219
+ })));
1220
+ });
1221
+ });
1222
+ if (selectionRange === 0) {
1223
+ return false;
1224
+ }
1225
+ const matchedRange = markRanges.filter((markRange) => {
1226
+ if (!type) {
1227
+ return true;
1228
+ }
1229
+ return type.name === markRange.mark.type.name;
1230
+ }).filter((markRange) => objectIncludes(markRange.mark.attrs, attributes, { strict: false })).reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
1231
+ const excludedRange = markRanges.filter((markRange) => {
1232
+ if (!type) {
1233
+ return true;
1234
+ }
1235
+ return markRange.mark.type !== type && markRange.mark.type.excludes(type);
1236
+ }).reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
1237
+ const range = matchedRange > 0 ? matchedRange + excludedRange : matchedRange;
1238
+ return range >= selectionRange;
1239
+ }
1240
+ function isList(name, extensions) {
1241
+ const { nodeExtensions } = splitExtensions(extensions);
1242
+ const extension = nodeExtensions.find((item) => item.name === name);
1243
+ if (!extension) {
1244
+ return false;
1245
+ }
1246
+ const context = {
1247
+ name: extension.name,
1248
+ options: extension.options,
1249
+ storage: extension.storage
1250
+ };
1251
+ const group = callOrReturn(getExtensionField(extension, "group", context));
1252
+ if (typeof group !== "string") {
1253
+ return false;
1254
+ }
1255
+ return group.split(" ").includes("list");
1256
+ }
1257
+ function isNodeEmpty(node, { checkChildren = true, ignoreWhitespace = false } = {}) {
1258
+ var _a;
1259
+ if (ignoreWhitespace) {
1260
+ if (node.type.name === "hardBreak") {
1261
+ return true;
1262
+ }
1263
+ if (node.isText) {
1264
+ return /^\s*$/m.test((_a = node.text) !== null && _a !== void 0 ? _a : "");
1265
+ }
1266
+ }
1267
+ if (node.isText) {
1268
+ return !node.text;
1269
+ }
1270
+ if (node.isAtom || node.isLeaf) {
1271
+ return false;
1272
+ }
1273
+ if (node.content.childCount === 0) {
1274
+ return true;
1275
+ }
1276
+ if (checkChildren) {
1277
+ let isContentEmpty = true;
1278
+ node.content.forEach((childNode) => {
1279
+ if (isContentEmpty === false) {
1280
+ return;
1281
+ }
1282
+ if (!isNodeEmpty(childNode, { ignoreWhitespace, checkChildren })) {
1283
+ isContentEmpty = false;
1284
+ }
1285
+ });
1286
+ return isContentEmpty;
1287
+ }
1288
+ return false;
1289
+ }
1290
+ function canSetMark(state, tr, newMarkType) {
1291
+ var _a;
1292
+ const { selection } = tr;
1293
+ let cursor = null;
1294
+ if (isTextSelection(selection)) {
1295
+ cursor = selection.$cursor;
1296
+ }
1297
+ if (cursor) {
1298
+ const currentMarks = (_a = state.storedMarks) !== null && _a !== void 0 ? _a : cursor.marks();
1299
+ return !!newMarkType.isInSet(currentMarks) || !currentMarks.some((mark) => mark.type.excludes(newMarkType));
1300
+ }
1301
+ const { ranges } = selection;
1302
+ return ranges.some(({ $from, $to }) => {
1303
+ let someNodeSupportsMark = $from.depth === 0 ? state.doc.inlineContent && state.doc.type.allowsMarkType(newMarkType) : false;
1304
+ state.doc.nodesBetween($from.pos, $to.pos, (node, _pos, parent) => {
1305
+ if (someNodeSupportsMark) {
1306
+ return false;
1307
+ }
1308
+ if (node.isInline) {
1309
+ const parentAllowsMarkType = !parent || parent.type.allowsMarkType(newMarkType);
1310
+ const currentMarksAllowMarkType = !!newMarkType.isInSet(node.marks) || !node.marks.some((otherMark) => otherMark.type.excludes(newMarkType));
1311
+ someNodeSupportsMark = parentAllowsMarkType && currentMarksAllowMarkType;
1312
+ }
1313
+ return !someNodeSupportsMark;
1314
+ });
1315
+ return someNodeSupportsMark;
1316
+ });
1317
+ }
1318
+ var setMark = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
1319
+ const { selection } = tr;
1320
+ const { empty, ranges } = selection;
1321
+ const type = getMarkType(typeOrName, state.schema);
1322
+ if (dispatch) {
1323
+ if (empty) {
1324
+ const oldAttributes = getMarkAttributes(state, type);
1325
+ tr.addStoredMark(type.create({
1326
+ ...oldAttributes,
1327
+ ...attributes
1328
+ }));
1329
+ } else {
1330
+ ranges.forEach((range) => {
1331
+ const from = range.$from.pos;
1332
+ const to = range.$to.pos;
1333
+ state.doc.nodesBetween(from, to, (node, pos) => {
1334
+ const trimmedFrom = Math.max(pos, from);
1335
+ const trimmedTo = Math.min(pos + node.nodeSize, to);
1336
+ const someHasMark = node.marks.find((mark) => mark.type === type);
1337
+ if (someHasMark) {
1338
+ node.marks.forEach((mark) => {
1339
+ if (type === mark.type) {
1340
+ tr.addMark(trimmedFrom, trimmedTo, type.create({
1341
+ ...mark.attrs,
1342
+ ...attributes
1343
+ }));
1344
+ }
1345
+ });
1346
+ } else {
1347
+ tr.addMark(trimmedFrom, trimmedTo, type.create(attributes));
1348
+ }
1349
+ });
1350
+ });
1351
+ }
1352
+ }
1353
+ return canSetMark(state, tr, type);
1354
+ };
1355
+ var setMeta = (key, value) => ({ tr }) => {
1356
+ tr.setMeta(key, value);
1357
+ return true;
1358
+ };
1359
+ var setNode = (typeOrName, attributes = {}) => ({ state, dispatch, chain }) => {
1360
+ const type = getNodeType(typeOrName, state.schema);
1361
+ let attributesToCopy;
1362
+ if (state.selection.$anchor.sameParent(state.selection.$head)) {
1363
+ attributesToCopy = state.selection.$anchor.parent.attrs;
1364
+ }
1365
+ if (!type.isTextblock) {
1366
+ console.warn('[tiptap warn]: Currently "setNode()" only supports text block nodes.');
1367
+ return false;
1368
+ }
1369
+ return chain().command(({ commands: commands2 }) => {
1370
+ const canSetBlock = (0, import_commands.setBlockType)(type, { ...attributesToCopy, ...attributes })(state);
1371
+ if (canSetBlock) {
1372
+ return true;
1373
+ }
1374
+ return commands2.clearNodes();
1375
+ }).command(({ state: updatedState }) => {
1376
+ return (0, import_commands.setBlockType)(type, { ...attributesToCopy, ...attributes })(updatedState, dispatch);
1377
+ }).run();
1378
+ };
1379
+ var setNodeSelection = (position) => ({ tr, dispatch }) => {
1380
+ if (dispatch) {
1381
+ const { doc } = tr;
1382
+ const from = minMax(position, 0, doc.content.size);
1383
+ const selection = import_state.NodeSelection.create(doc, from);
1384
+ tr.setSelection(selection);
1385
+ }
1386
+ return true;
1387
+ };
1388
+ var setTextSelection = (position) => ({ tr, dispatch }) => {
1389
+ if (dispatch) {
1390
+ const { doc } = tr;
1391
+ const { from, to } = typeof position === "number" ? { from: position, to: position } : position;
1392
+ const minPos = import_state.TextSelection.atStart(doc).from;
1393
+ const maxPos = import_state.TextSelection.atEnd(doc).to;
1394
+ const resolvedFrom = minMax(from, minPos, maxPos);
1395
+ const resolvedEnd = minMax(to, minPos, maxPos);
1396
+ const selection = import_state.TextSelection.create(doc, resolvedFrom, resolvedEnd);
1397
+ tr.setSelection(selection);
1398
+ }
1399
+ return true;
1400
+ };
1401
+ var sinkListItem = (typeOrName) => ({ state, dispatch }) => {
1402
+ const type = getNodeType(typeOrName, state.schema);
1403
+ return (0, import_schema_list.sinkListItem)(type)(state, dispatch);
1404
+ };
1405
+ function ensureMarks(state, splittableMarks) {
1406
+ const marks = state.storedMarks || state.selection.$to.parentOffset && state.selection.$from.marks();
1407
+ if (marks) {
1408
+ const filteredMarks = marks.filter((mark) => splittableMarks === null || splittableMarks === void 0 ? void 0 : splittableMarks.includes(mark.type.name));
1409
+ state.tr.ensureMarks(filteredMarks);
1410
+ }
1411
+ }
1412
+ var splitBlock = ({ keepMarks = true } = {}) => ({ tr, state, dispatch, editor }) => {
1413
+ const { selection, doc } = tr;
1414
+ const { $from, $to } = selection;
1415
+ const extensionAttributes = editor.extensionManager.attributes;
1416
+ const newAttributes = getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs);
1417
+ if (selection instanceof import_state.NodeSelection && selection.node.isBlock) {
1418
+ if (!$from.parentOffset || !(0, import_transform.canSplit)(doc, $from.pos)) {
1419
+ return false;
1420
+ }
1421
+ if (dispatch) {
1422
+ if (keepMarks) {
1423
+ ensureMarks(state, editor.extensionManager.splittableMarks);
1424
+ }
1425
+ tr.split($from.pos).scrollIntoView();
1426
+ }
1427
+ return true;
1428
+ }
1429
+ if (!$from.parent.isBlock) {
1430
+ return false;
1431
+ }
1432
+ const atEnd = $to.parentOffset === $to.parent.content.size;
1433
+ const deflt = $from.depth === 0 ? void 0 : defaultBlockAt($from.node(-1).contentMatchAt($from.indexAfter(-1)));
1434
+ let types = atEnd && deflt ? [
1435
+ {
1436
+ type: deflt,
1437
+ attrs: newAttributes
1438
+ }
1439
+ ] : void 0;
1440
+ let can = (0, import_transform.canSplit)(tr.doc, tr.mapping.map($from.pos), 1, types);
1441
+ if (!types && !can && (0, import_transform.canSplit)(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : void 0)) {
1442
+ can = true;
1443
+ types = deflt ? [
1444
+ {
1445
+ type: deflt,
1446
+ attrs: newAttributes
1447
+ }
1448
+ ] : void 0;
1449
+ }
1450
+ if (dispatch) {
1451
+ if (can) {
1452
+ if (selection instanceof import_state.TextSelection) {
1453
+ tr.deleteSelection();
1454
+ }
1455
+ tr.split(tr.mapping.map($from.pos), 1, types);
1456
+ if (deflt && !atEnd && !$from.parentOffset && $from.parent.type !== deflt) {
1457
+ const first2 = tr.mapping.map($from.before());
1458
+ const $first = tr.doc.resolve(first2);
1459
+ if ($from.node(-1).canReplaceWith($first.index(), $first.index() + 1, deflt)) {
1460
+ tr.setNodeMarkup(tr.mapping.map($from.before()), deflt);
1461
+ }
1462
+ }
1463
+ }
1464
+ if (keepMarks) {
1465
+ ensureMarks(state, editor.extensionManager.splittableMarks);
1466
+ }
1467
+ tr.scrollIntoView();
1468
+ }
1469
+ return can;
1470
+ };
1471
+ var splitListItem = (typeOrName, overrideAttrs = {}) => ({ tr, state, dispatch, editor }) => {
1472
+ var _a;
1473
+ const type = getNodeType(typeOrName, state.schema);
1474
+ const { $from, $to } = state.selection;
1475
+ const node = state.selection.node;
1476
+ if (node && node.isBlock || $from.depth < 2 || !$from.sameParent($to)) {
1477
+ return false;
1478
+ }
1479
+ const grandParent = $from.node(-1);
1480
+ if (grandParent.type !== type) {
1481
+ return false;
1482
+ }
1483
+ const extensionAttributes = editor.extensionManager.attributes;
1484
+ if ($from.parent.content.size === 0 && $from.node(-1).childCount === $from.indexAfter(-1)) {
1485
+ if ($from.depth === 2 || $from.node(-3).type !== type || $from.index(-2) !== $from.node(-2).childCount - 1) {
1486
+ return false;
1487
+ }
1488
+ if (dispatch) {
1489
+ let wrap = import_model.Fragment.empty;
1490
+ const depthBefore = $from.index(-1) ? 1 : $from.index(-2) ? 2 : 3;
1491
+ for (let d = $from.depth - depthBefore; d >= $from.depth - 3; d -= 1) {
1492
+ wrap = import_model.Fragment.from($from.node(d).copy(wrap));
1493
+ }
1494
+ const depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount ? 1 : $from.indexAfter(-2) < $from.node(-3).childCount ? 2 : 3;
1495
+ const newNextTypeAttributes2 = {
1496
+ ...getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs),
1497
+ ...overrideAttrs
1498
+ };
1499
+ const nextType2 = ((_a = type.contentMatch.defaultType) === null || _a === void 0 ? void 0 : _a.createAndFill(newNextTypeAttributes2)) || void 0;
1500
+ wrap = wrap.append(import_model.Fragment.from(type.createAndFill(null, nextType2) || void 0));
1501
+ const start = $from.before($from.depth - (depthBefore - 1));
1502
+ tr.replace(start, $from.after(-depthAfter), new import_model.Slice(wrap, 4 - depthBefore, 0));
1503
+ let sel = -1;
1504
+ tr.doc.nodesBetween(start, tr.doc.content.size, (n, pos) => {
1505
+ if (sel > -1) {
1506
+ return false;
1507
+ }
1508
+ if (n.isTextblock && n.content.size === 0) {
1509
+ sel = pos + 1;
1510
+ }
1511
+ });
1512
+ if (sel > -1) {
1513
+ tr.setSelection(import_state.TextSelection.near(tr.doc.resolve(sel)));
1514
+ }
1515
+ tr.scrollIntoView();
1516
+ }
1517
+ return true;
1518
+ }
1519
+ const nextType = $to.pos === $from.end() ? grandParent.contentMatchAt(0).defaultType : null;
1520
+ const newTypeAttributes = {
1521
+ ...getSplittedAttributes(extensionAttributes, grandParent.type.name, grandParent.attrs),
1522
+ ...overrideAttrs
1523
+ };
1524
+ const newNextTypeAttributes = {
1525
+ ...getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs),
1526
+ ...overrideAttrs
1527
+ };
1528
+ tr.delete($from.pos, $to.pos);
1529
+ const types = nextType ? [
1530
+ { type, attrs: newTypeAttributes },
1531
+ { type: nextType, attrs: newNextTypeAttributes }
1532
+ ] : [{ type, attrs: newTypeAttributes }];
1533
+ if (!(0, import_transform.canSplit)(tr.doc, $from.pos, 2)) {
1534
+ return false;
1535
+ }
1536
+ if (dispatch) {
1537
+ const { selection, storedMarks } = state;
1538
+ const { splittableMarks } = editor.extensionManager;
1539
+ const marks = storedMarks || selection.$to.parentOffset && selection.$from.marks();
1540
+ tr.split($from.pos, 2, types).scrollIntoView();
1541
+ if (!marks || !dispatch) {
1542
+ return true;
1543
+ }
1544
+ const filteredMarks = marks.filter((mark) => splittableMarks.includes(mark.type.name));
1545
+ tr.ensureMarks(filteredMarks);
1546
+ }
1547
+ return true;
1548
+ };
1549
+ var joinListBackwards = (tr, listType) => {
1550
+ const list = findParentNode((node) => node.type === listType)(tr.selection);
1551
+ if (!list) {
1552
+ return true;
1553
+ }
1554
+ const before = tr.doc.resolve(Math.max(0, list.pos - 1)).before(list.depth);
1555
+ if (before === void 0) {
1556
+ return true;
1557
+ }
1558
+ const nodeBefore = tr.doc.nodeAt(before);
1559
+ const canJoinBackwards = list.node.type === (nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.type) && (0, import_transform.canJoin)(tr.doc, list.pos);
1560
+ if (!canJoinBackwards) {
1561
+ return true;
1562
+ }
1563
+ tr.join(list.pos);
1564
+ return true;
1565
+ };
1566
+ var joinListForwards = (tr, listType) => {
1567
+ const list = findParentNode((node) => node.type === listType)(tr.selection);
1568
+ if (!list) {
1569
+ return true;
1570
+ }
1571
+ const after = tr.doc.resolve(list.start).after(list.depth);
1572
+ if (after === void 0) {
1573
+ return true;
1574
+ }
1575
+ const nodeAfter = tr.doc.nodeAt(after);
1576
+ const canJoinForwards = list.node.type === (nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.type) && (0, import_transform.canJoin)(tr.doc, after);
1577
+ if (!canJoinForwards) {
1578
+ return true;
1579
+ }
1580
+ tr.join(after);
1581
+ return true;
1582
+ };
1583
+ var toggleList = (listTypeOrName, itemTypeOrName, keepMarks, attributes = {}) => ({ editor, tr, state, dispatch, chain, commands: commands2, can }) => {
1584
+ const { extensions, splittableMarks } = editor.extensionManager;
1585
+ const listType = getNodeType(listTypeOrName, state.schema);
1586
+ const itemType = getNodeType(itemTypeOrName, state.schema);
1587
+ const { selection, storedMarks } = state;
1588
+ const { $from, $to } = selection;
1589
+ const range = $from.blockRange($to);
1590
+ const marks = storedMarks || selection.$to.parentOffset && selection.$from.marks();
1591
+ if (!range) {
1592
+ return false;
1593
+ }
1594
+ const parentList = findParentNode((node) => isList(node.type.name, extensions))(selection);
1595
+ if (range.depth >= 1 && parentList && range.depth - parentList.depth <= 1) {
1596
+ if (parentList.node.type === listType) {
1597
+ return commands2.liftListItem(itemType);
1598
+ }
1599
+ if (isList(parentList.node.type.name, extensions) && listType.validContent(parentList.node.content) && dispatch) {
1600
+ return chain().command(() => {
1601
+ tr.setNodeMarkup(parentList.pos, listType);
1602
+ return true;
1603
+ }).command(() => joinListBackwards(tr, listType)).command(() => joinListForwards(tr, listType)).run();
1604
+ }
1605
+ }
1606
+ if (!keepMarks || !marks || !dispatch) {
1607
+ return chain().command(() => {
1608
+ const canWrapInList = can().wrapInList(listType, attributes);
1609
+ if (canWrapInList) {
1610
+ return true;
1611
+ }
1612
+ return commands2.clearNodes();
1613
+ }).wrapInList(listType, attributes).command(() => joinListBackwards(tr, listType)).command(() => joinListForwards(tr, listType)).run();
1614
+ }
1615
+ return chain().command(() => {
1616
+ const canWrapInList = can().wrapInList(listType, attributes);
1617
+ const filteredMarks = marks.filter((mark) => splittableMarks.includes(mark.type.name));
1618
+ tr.ensureMarks(filteredMarks);
1619
+ if (canWrapInList) {
1620
+ return true;
1621
+ }
1622
+ return commands2.clearNodes();
1623
+ }).wrapInList(listType, attributes).command(() => joinListBackwards(tr, listType)).command(() => joinListForwards(tr, listType)).run();
1624
+ };
1625
+ var toggleMark = (typeOrName, attributes = {}, options = {}) => ({ state, commands: commands2 }) => {
1626
+ const { extendEmptyMarkRange = false } = options;
1627
+ const type = getMarkType(typeOrName, state.schema);
1628
+ const isActive = isMarkActive(state, type, attributes);
1629
+ if (isActive) {
1630
+ return commands2.unsetMark(type, { extendEmptyMarkRange });
1631
+ }
1632
+ return commands2.setMark(type, attributes);
1633
+ };
1634
+ var toggleNode = (typeOrName, toggleTypeOrName, attributes = {}) => ({ state, commands: commands2 }) => {
1635
+ const type = getNodeType(typeOrName, state.schema);
1636
+ const toggleType = getNodeType(toggleTypeOrName, state.schema);
1637
+ const isActive = isNodeActive(state, type, attributes);
1638
+ let attributesToCopy;
1639
+ if (state.selection.$anchor.sameParent(state.selection.$head)) {
1640
+ attributesToCopy = state.selection.$anchor.parent.attrs;
1641
+ }
1642
+ if (isActive) {
1643
+ return commands2.setNode(toggleType, attributesToCopy);
1644
+ }
1645
+ return commands2.setNode(type, { ...attributesToCopy, ...attributes });
1646
+ };
1647
+ var toggleWrap = (typeOrName, attributes = {}) => ({ state, commands: commands2 }) => {
1648
+ const type = getNodeType(typeOrName, state.schema);
1649
+ const isActive = isNodeActive(state, type, attributes);
1650
+ if (isActive) {
1651
+ return commands2.lift(type);
1652
+ }
1653
+ return commands2.wrapIn(type, attributes);
1654
+ };
1655
+ var undoInputRule = () => ({ state, dispatch }) => {
1656
+ const plugins = state.plugins;
1657
+ for (let i = 0; i < plugins.length; i += 1) {
1658
+ const plugin = plugins[i];
1659
+ let undoable;
1660
+ if (plugin.spec.isInputRules && (undoable = plugin.getState(state))) {
1661
+ if (dispatch) {
1662
+ const tr = state.tr;
1663
+ const toUndo = undoable.transform;
1664
+ for (let j = toUndo.steps.length - 1; j >= 0; j -= 1) {
1665
+ tr.step(toUndo.steps[j].invert(toUndo.docs[j]));
1666
+ }
1667
+ if (undoable.text) {
1668
+ const marks = tr.doc.resolve(undoable.from).marks();
1669
+ tr.replaceWith(undoable.from, undoable.to, state.schema.text(undoable.text, marks));
1670
+ } else {
1671
+ tr.delete(undoable.from, undoable.to);
1672
+ }
1673
+ }
1674
+ return true;
1675
+ }
1676
+ }
1677
+ return false;
1678
+ };
1679
+ var unsetAllMarks = () => ({ tr, dispatch }) => {
1680
+ const { selection } = tr;
1681
+ const { empty, ranges } = selection;
1682
+ if (empty) {
1683
+ return true;
1684
+ }
1685
+ if (dispatch) {
1686
+ ranges.forEach((range) => {
1687
+ tr.removeMark(range.$from.pos, range.$to.pos);
1688
+ });
1689
+ }
1690
+ return true;
1691
+ };
1692
+ var unsetMark = (typeOrName, options = {}) => ({ tr, state, dispatch }) => {
1693
+ var _a;
1694
+ const { extendEmptyMarkRange = false } = options;
1695
+ const { selection } = tr;
1696
+ const type = getMarkType(typeOrName, state.schema);
1697
+ const { $from, empty, ranges } = selection;
1698
+ if (!dispatch) {
1699
+ return true;
1700
+ }
1701
+ if (empty && extendEmptyMarkRange) {
1702
+ let { from, to } = selection;
1703
+ const attrs = (_a = $from.marks().find((mark) => mark.type === type)) === null || _a === void 0 ? void 0 : _a.attrs;
1704
+ const range = getMarkRange($from, type, attrs);
1705
+ if (range) {
1706
+ from = range.from;
1707
+ to = range.to;
1708
+ }
1709
+ tr.removeMark(from, to, type);
1710
+ } else {
1711
+ ranges.forEach((range) => {
1712
+ tr.removeMark(range.$from.pos, range.$to.pos, type);
1713
+ });
1714
+ }
1715
+ tr.removeStoredMark(type);
1716
+ return true;
1717
+ };
1718
+ var updateAttributes = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
1719
+ let nodeType = null;
1720
+ let markType = null;
1721
+ const schemaType = getSchemaTypeNameByName(typeof typeOrName === "string" ? typeOrName : typeOrName.name, state.schema);
1722
+ if (!schemaType) {
1723
+ return false;
1724
+ }
1725
+ if (schemaType === "node") {
1726
+ nodeType = getNodeType(typeOrName, state.schema);
1727
+ }
1728
+ if (schemaType === "mark") {
1729
+ markType = getMarkType(typeOrName, state.schema);
1730
+ }
1731
+ if (dispatch) {
1732
+ tr.selection.ranges.forEach((range) => {
1733
+ const from = range.$from.pos;
1734
+ const to = range.$to.pos;
1735
+ let lastPos;
1736
+ let lastNode;
1737
+ let trimmedFrom;
1738
+ let trimmedTo;
1739
+ if (tr.selection.empty) {
1740
+ state.doc.nodesBetween(from, to, (node, pos) => {
1741
+ if (nodeType && nodeType === node.type) {
1742
+ trimmedFrom = Math.max(pos, from);
1743
+ trimmedTo = Math.min(pos + node.nodeSize, to);
1744
+ lastPos = pos;
1745
+ lastNode = node;
1746
+ }
1747
+ });
1748
+ } else {
1749
+ state.doc.nodesBetween(from, to, (node, pos) => {
1750
+ if (pos < from && nodeType && nodeType === node.type) {
1751
+ trimmedFrom = Math.max(pos, from);
1752
+ trimmedTo = Math.min(pos + node.nodeSize, to);
1753
+ lastPos = pos;
1754
+ lastNode = node;
1755
+ }
1756
+ if (pos >= from && pos <= to) {
1757
+ if (nodeType && nodeType === node.type) {
1758
+ tr.setNodeMarkup(pos, void 0, {
1759
+ ...node.attrs,
1760
+ ...attributes
1761
+ });
1762
+ }
1763
+ if (markType && node.marks.length) {
1764
+ node.marks.forEach((mark) => {
1765
+ if (markType === mark.type) {
1766
+ const trimmedFrom2 = Math.max(pos, from);
1767
+ const trimmedTo2 = Math.min(pos + node.nodeSize, to);
1768
+ tr.addMark(trimmedFrom2, trimmedTo2, markType.create({
1769
+ ...mark.attrs,
1770
+ ...attributes
1771
+ }));
1772
+ }
1773
+ });
1774
+ }
1775
+ }
1776
+ });
1777
+ }
1778
+ if (lastNode) {
1779
+ if (lastPos !== void 0) {
1780
+ tr.setNodeMarkup(lastPos, void 0, {
1781
+ ...lastNode.attrs,
1782
+ ...attributes
1783
+ });
1784
+ }
1785
+ if (markType && lastNode.marks.length) {
1786
+ lastNode.marks.forEach((mark) => {
1787
+ if (markType === mark.type) {
1788
+ tr.addMark(trimmedFrom, trimmedTo, markType.create({
1789
+ ...mark.attrs,
1790
+ ...attributes
1791
+ }));
1792
+ }
1793
+ });
1794
+ }
1795
+ }
1796
+ });
1797
+ }
1798
+ return true;
1799
+ };
1800
+ var wrapIn = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
1801
+ const type = getNodeType(typeOrName, state.schema);
1802
+ return (0, import_commands.wrapIn)(type, attributes)(state, dispatch);
1803
+ };
1804
+ var wrapInList = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
1805
+ const type = getNodeType(typeOrName, state.schema);
1806
+ return (0, import_schema_list.wrapInList)(type, attributes)(state, dispatch);
1807
+ };
1808
+ var commands = /* @__PURE__ */ Object.freeze({
1809
+ __proto__: null,
1810
+ blur,
1811
+ clearContent,
1812
+ clearNodes,
1813
+ command,
1814
+ createParagraphNear,
1815
+ cut,
1816
+ deleteCurrentNode,
1817
+ deleteNode,
1818
+ deleteRange,
1819
+ deleteSelection,
1820
+ enter,
1821
+ exitCode,
1822
+ extendMarkRange,
1823
+ first,
1824
+ focus,
1825
+ forEach,
1826
+ insertContent,
1827
+ insertContentAt,
1828
+ joinBackward,
1829
+ joinDown,
1830
+ joinForward,
1831
+ joinItemBackward,
1832
+ joinItemForward,
1833
+ joinTextblockBackward,
1834
+ joinTextblockForward,
1835
+ joinUp,
1836
+ keyboardShortcut,
1837
+ lift,
1838
+ liftEmptyBlock,
1839
+ liftListItem,
1840
+ newlineInCode,
1841
+ resetAttributes,
1842
+ scrollIntoView,
1843
+ selectAll,
1844
+ selectNodeBackward,
1845
+ selectNodeForward,
1846
+ selectParentNode,
1847
+ selectTextblockEnd,
1848
+ selectTextblockStart,
1849
+ setContent,
1850
+ setMark,
1851
+ setMeta,
1852
+ setNode,
1853
+ setNodeSelection,
1854
+ setTextSelection,
1855
+ sinkListItem,
1856
+ splitBlock,
1857
+ splitListItem,
1858
+ toggleList,
1859
+ toggleMark,
1860
+ toggleNode,
1861
+ toggleWrap,
1862
+ undoInputRule,
1863
+ unsetAllMarks,
1864
+ unsetMark,
1865
+ updateAttributes,
1866
+ wrapIn,
1867
+ wrapInList
1868
+ });
1869
+ var Commands = Extension.create({
1870
+ name: "commands",
1871
+ addCommands() {
1872
+ return {
1873
+ ...commands
1874
+ };
1875
+ }
1876
+ });
1877
+ var Drop = Extension.create({
1878
+ name: "drop",
1879
+ addProseMirrorPlugins() {
1880
+ return [
1881
+ new import_state.Plugin({
1882
+ key: new import_state.PluginKey("tiptapDrop"),
1883
+ props: {
1884
+ handleDrop: (_, e, slice, moved) => {
1885
+ this.editor.emit("drop", {
1886
+ editor: this.editor,
1887
+ event: e,
1888
+ slice,
1889
+ moved
1890
+ });
1891
+ }
1892
+ }
1893
+ })
1894
+ ];
1895
+ }
1896
+ });
1897
+ var Editable = Extension.create({
1898
+ name: "editable",
1899
+ addProseMirrorPlugins() {
1900
+ return [
1901
+ new import_state.Plugin({
1902
+ key: new import_state.PluginKey("editable"),
1903
+ props: {
1904
+ editable: () => this.editor.options.editable
1905
+ }
1906
+ })
1907
+ ];
1908
+ }
1909
+ });
1910
+ var focusEventsPluginKey = new import_state.PluginKey("focusEvents");
1911
+ var FocusEvents = Extension.create({
1912
+ name: "focusEvents",
1913
+ addProseMirrorPlugins() {
1914
+ const { editor } = this;
1915
+ return [
1916
+ new import_state.Plugin({
1917
+ key: focusEventsPluginKey,
1918
+ props: {
1919
+ handleDOMEvents: {
1920
+ focus: (view, event) => {
1921
+ editor.isFocused = true;
1922
+ const transaction = editor.state.tr.setMeta("focus", { event }).setMeta("addToHistory", false);
1923
+ view.dispatch(transaction);
1924
+ return false;
1925
+ },
1926
+ blur: (view, event) => {
1927
+ editor.isFocused = false;
1928
+ const transaction = editor.state.tr.setMeta("blur", { event }).setMeta("addToHistory", false);
1929
+ view.dispatch(transaction);
1930
+ return false;
1931
+ }
1932
+ }
1933
+ }
1934
+ })
1935
+ ];
1936
+ }
1937
+ });
1938
+ var Keymap = Extension.create({
1939
+ name: "keymap",
1940
+ addKeyboardShortcuts() {
1941
+ const handleBackspace = () => this.editor.commands.first(({ commands: commands2 }) => [
1942
+ () => commands2.undoInputRule(),
1943
+ // maybe convert first text block node to default node
1944
+ () => commands2.command(({ tr }) => {
1945
+ const { selection, doc } = tr;
1946
+ const { empty, $anchor } = selection;
1947
+ const { pos, parent } = $anchor;
1948
+ const $parentPos = $anchor.parent.isTextblock && pos > 0 ? tr.doc.resolve(pos - 1) : $anchor;
1949
+ const parentIsIsolating = $parentPos.parent.type.spec.isolating;
1950
+ const parentPos = $anchor.pos - $anchor.parentOffset;
1951
+ const isAtStart = parentIsIsolating && $parentPos.parent.childCount === 1 ? parentPos === $anchor.pos : import_state.Selection.atStart(doc).from === pos;
1952
+ if (!empty || !parent.type.isTextblock || parent.textContent.length || !isAtStart || isAtStart && $anchor.parent.type.name === "paragraph") {
1953
+ return false;
1954
+ }
1955
+ return commands2.clearNodes();
1956
+ }),
1957
+ () => commands2.deleteSelection(),
1958
+ () => commands2.joinBackward(),
1959
+ () => commands2.selectNodeBackward()
1960
+ ]);
1961
+ const handleDelete = () => this.editor.commands.first(({ commands: commands2 }) => [
1962
+ () => commands2.deleteSelection(),
1963
+ () => commands2.deleteCurrentNode(),
1964
+ () => commands2.joinForward(),
1965
+ () => commands2.selectNodeForward()
1966
+ ]);
1967
+ const handleEnter = () => this.editor.commands.first(({ commands: commands2 }) => [
1968
+ () => commands2.newlineInCode(),
1969
+ () => commands2.createParagraphNear(),
1970
+ () => commands2.liftEmptyBlock(),
1971
+ () => commands2.splitBlock()
1972
+ ]);
1973
+ const baseKeymap = {
1974
+ Enter: handleEnter,
1975
+ "Mod-Enter": () => this.editor.commands.exitCode(),
1976
+ Backspace: handleBackspace,
1977
+ "Mod-Backspace": handleBackspace,
1978
+ "Shift-Backspace": handleBackspace,
1979
+ Delete: handleDelete,
1980
+ "Mod-Delete": handleDelete,
1981
+ "Mod-a": () => this.editor.commands.selectAll()
1982
+ };
1983
+ const pcKeymap = {
1984
+ ...baseKeymap
1985
+ };
1986
+ const macKeymap = {
1987
+ ...baseKeymap,
1988
+ "Ctrl-h": handleBackspace,
1989
+ "Alt-Backspace": handleBackspace,
1990
+ "Ctrl-d": handleDelete,
1991
+ "Ctrl-Alt-Backspace": handleDelete,
1992
+ "Alt-Delete": handleDelete,
1993
+ "Alt-d": handleDelete,
1994
+ "Ctrl-a": () => this.editor.commands.selectTextblockStart(),
1995
+ "Ctrl-e": () => this.editor.commands.selectTextblockEnd()
1996
+ };
1997
+ if (isiOS() || isMacOS()) {
1998
+ return macKeymap;
1999
+ }
2000
+ return pcKeymap;
2001
+ },
2002
+ addProseMirrorPlugins() {
2003
+ return [
2004
+ // With this plugin we check if the whole document was selected and deleted.
2005
+ // In this case we will additionally call `clearNodes()` to convert e.g. a heading
2006
+ // to a paragraph if necessary.
2007
+ // This is an alternative to ProseMirror's `AllSelection`, which doesn’t work well
2008
+ // with many other commands.
2009
+ new import_state.Plugin({
2010
+ key: new import_state.PluginKey("clearDocument"),
2011
+ appendTransaction: (transactions, oldState, newState) => {
2012
+ if (transactions.some((tr2) => tr2.getMeta("composition"))) {
2013
+ return;
2014
+ }
2015
+ const docChanges = transactions.some((transaction) => transaction.docChanged) && !oldState.doc.eq(newState.doc);
2016
+ const ignoreTr = transactions.some((transaction) => transaction.getMeta("preventClearDocument"));
2017
+ if (!docChanges || ignoreTr) {
2018
+ return;
2019
+ }
2020
+ const { empty, from, to } = oldState.selection;
2021
+ const allFrom = import_state.Selection.atStart(oldState.doc).from;
2022
+ const allEnd = import_state.Selection.atEnd(oldState.doc).to;
2023
+ const allWasSelected = from === allFrom && to === allEnd;
2024
+ if (empty || !allWasSelected) {
2025
+ return;
2026
+ }
2027
+ const isEmpty = isNodeEmpty(newState.doc);
2028
+ if (!isEmpty) {
2029
+ return;
2030
+ }
2031
+ const tr = newState.tr;
2032
+ const state = createChainableState({
2033
+ state: newState,
2034
+ transaction: tr
2035
+ });
2036
+ const { commands: commands2 } = new CommandManager({
2037
+ editor: this.editor,
2038
+ state
2039
+ });
2040
+ commands2.clearNodes();
2041
+ if (!tr.steps.length) {
2042
+ return;
2043
+ }
2044
+ return tr;
2045
+ }
2046
+ })
2047
+ ];
2048
+ }
2049
+ });
2050
+ var Paste = Extension.create({
2051
+ name: "paste",
2052
+ addProseMirrorPlugins() {
2053
+ return [
2054
+ new import_state.Plugin({
2055
+ key: new import_state.PluginKey("tiptapPaste"),
2056
+ props: {
2057
+ handlePaste: (_view, e, slice) => {
2058
+ this.editor.emit("paste", {
2059
+ editor: this.editor,
2060
+ event: e,
2061
+ slice
2062
+ });
2063
+ }
2064
+ }
2065
+ })
2066
+ ];
2067
+ }
2068
+ });
2069
+ var Tabindex = Extension.create({
2070
+ name: "tabindex",
2071
+ addProseMirrorPlugins() {
2072
+ return [
2073
+ new import_state.Plugin({
2074
+ key: new import_state.PluginKey("tabindex"),
2075
+ props: {
2076
+ attributes: () => this.editor.isEditable ? { tabindex: "0" } : {}
2077
+ }
2078
+ })
2079
+ ];
2080
+ }
2081
+ });
2082
+
2083
+ // src/extensions/PageLayout.ts
2084
+ var import_state2 = require("@tiptap/pm/state");
2085
+ var import_view2 = require("@tiptap/pm/view");
2086
+
2087
+ // src/types.ts
2088
+ var PAGE_DIMENSIONS = {
2089
+ A4: {
2090
+ widthMm: 210,
2091
+ heightMm: 297,
2092
+ paddingTopMm: 22,
2093
+ paddingBottomMm: 22,
2094
+ paddingLeftMm: 28,
2095
+ paddingRightMm: 28
2096
+ },
2097
+ Letter: {
2098
+ widthMm: 216,
2099
+ heightMm: 279,
2100
+ paddingTopMm: 22,
2101
+ paddingBottomMm: 22,
2102
+ paddingLeftMm: 28,
2103
+ paddingRightMm: 28
2104
+ }
2105
+ };
2106
+ var PX_PER_MM = 3.7795;
2107
+ function getBodyHeightPx(size) {
2108
+ const d = PAGE_DIMENSIONS[size];
2109
+ return Math.round((d.heightMm - d.paddingTopMm - d.paddingBottomMm) * PX_PER_MM);
2110
+ }
2111
+ function getBodyWidthPx(size) {
2112
+ const d = PAGE_DIMENSIONS[size];
2113
+ return Math.round((d.widthMm - d.paddingLeftMm - d.paddingRightMm) * PX_PER_MM);
2114
+ }
2115
+ function getPageWidthPx(size) {
2116
+ const d = PAGE_DIMENSIONS[size];
2117
+ return Math.round(d.widthMm * PX_PER_MM);
2118
+ }
2119
+ var PARCHMENT_DEFAULTS = {
2120
+ canvasBg: "#e0ddd4",
2121
+ paperColor: "#f5f4ed",
2122
+ textColor: "#141413",
2123
+ lineColor: "#c8c4b8",
2124
+ accentColor: "#1b365d"
2125
+ };
2126
+ var MINIMAL_DEFAULTS = {
2127
+ canvasBg: "#e8e8e8",
2128
+ paperColor: "#ffffff",
2129
+ textColor: "#111111",
2130
+ lineColor: "#d0d0d0",
2131
+ accentColor: "#2563eb"
2132
+ };
2133
+
2134
+ // src/extensions/PageLayout.ts
2135
+ var META_KEY = "pageBreakDecos";
2136
+ var pluginKey = new import_state2.PluginKey("pageBreak");
2137
+ function makeGapWidget(gapHeightPx, pageIndex) {
2138
+ const el = document.createElement("div");
2139
+ el.className = "ink-page-gap";
2140
+ el.setAttribute("data-page-gap", String(pageIndex));
2141
+ el.style.height = `${gapHeightPx}px`;
2142
+ el.contentEditable = "false";
2143
+ return el;
2144
+ }
2145
+ function computeDecorations(doc, view, bodyHeightPx, gapHeightPx) {
2146
+ const decorations = [];
2147
+ let pageIndex = 1;
2148
+ let baseY = null;
2149
+ doc.descendants((node, pos) => {
2150
+ if (!node.isBlock) return;
2151
+ const insidePos = pos + 1;
2152
+ if (insidePos >= doc.content.size) return;
2153
+ let top;
2154
+ let bottom;
2155
+ try {
2156
+ top = view.coordsAtPos(insidePos).top;
2157
+ const endPos = pos + node.nodeSize - 1;
2158
+ bottom = endPos > insidePos ? view.coordsAtPos(endPos).bottom : top + 28;
2159
+ } catch {
2160
+ return;
2161
+ }
2162
+ if (baseY === null) baseY = top;
2163
+ const threshold = pageIndex * bodyHeightPx + (pageIndex - 1) * gapHeightPx;
2164
+ const relTop = top - baseY;
2165
+ const relBottom = bottom - baseY;
2166
+ if (relTop >= threshold || relBottom > threshold && relTop < threshold) {
2167
+ const idx = pageIndex;
2168
+ decorations.push(
2169
+ import_view2.Decoration.widget(pos, () => makeGapWidget(gapHeightPx, idx), {
2170
+ side: -1,
2171
+ key: `page-gap-${idx}`
2172
+ })
2173
+ );
2174
+ pageIndex++;
2175
+ }
2176
+ });
2177
+ return import_view2.DecorationSet.create(doc, decorations);
2178
+ }
2179
+ function measurePageDimensions(view, pageSize) {
2180
+ const card = view.dom.closest(".ink-page-card");
2181
+ if (!card) return null;
2182
+ const style = window.getComputedStyle(card);
2183
+ const paddingTop = parseFloat(style.paddingTop);
2184
+ const paddingBottom = parseFloat(style.paddingBottom);
2185
+ const gapHeightPx = Math.round(paddingTop + paddingBottom);
2186
+ const dims = PAGE_DIMENSIONS[pageSize];
2187
+ const bodyMm = dims.heightMm - dims.paddingTopMm - dims.paddingBottomMm;
2188
+ const gapMm = dims.paddingTopMm + dims.paddingBottomMm;
2189
+ const pmPaddingTop = parseFloat(window.getComputedStyle(view.dom).paddingTop) || 28;
2190
+ const rawWidgetHeight = gapHeightPx - pmPaddingTop;
2191
+ const widgetHeightPx = Math.round(rawWidgetHeight / 28) * 28;
2192
+ const rawBodyHeight = Math.round(bodyMm / gapMm * gapHeightPx);
2193
+ const bodyHeightPx = Math.floor(rawBodyHeight / 28) * 28;
2194
+ return { bodyHeightPx, gapHeightPx: widgetHeightPx };
2195
+ }
2196
+ function buildPageBreakPlugin(pageSize) {
2197
+ let rafId = null;
2198
+ function scheduleUpdate(view) {
2199
+ if (rafId !== null) cancelAnimationFrame(rafId);
2200
+ rafId = requestAnimationFrame(() => {
2201
+ rafId = null;
2202
+ if (view.isDestroyed) return;
2203
+ const dims = measurePageDimensions(view, pageSize);
2204
+ if (!dims) return;
2205
+ const { bodyHeightPx, gapHeightPx } = dims;
2206
+ const decos = computeDecorations(view.state.doc, view, bodyHeightPx, gapHeightPx);
2207
+ const tr = view.state.tr.setMeta(META_KEY, decos);
2208
+ tr.setMeta("addToHistory", false);
2209
+ view.dispatch(tr);
2210
+ });
2211
+ }
2212
+ return new import_state2.Plugin({
2213
+ key: pluginKey,
2214
+ state: {
2215
+ init() {
2216
+ return import_view2.DecorationSet.empty;
2217
+ },
2218
+ apply(tr, old) {
2219
+ const meta = tr.getMeta(META_KEY);
2220
+ if (meta) return meta;
2221
+ if (tr.docChanged) return old.map(tr.mapping, tr.doc);
2222
+ return old;
2223
+ }
2224
+ },
2225
+ props: {
2226
+ decorations(state) {
2227
+ return pluginKey.getState(state) ?? import_view2.DecorationSet.empty;
2228
+ }
2229
+ },
2230
+ view(editorView) {
2231
+ scheduleUpdate(editorView);
2232
+ return {
2233
+ update(view, prevState) {
2234
+ if (!view.state.doc.eq(prevState.doc)) {
2235
+ scheduleUpdate(view);
2236
+ }
2237
+ },
2238
+ destroy() {
2239
+ if (rafId !== null) cancelAnimationFrame(rafId);
2240
+ }
2241
+ };
2242
+ }
2243
+ });
2244
+ }
2245
+ var PageLayout = Extension.create({
2246
+ name: "pageLayout",
2247
+ addOptions() {
2248
+ return { pageSize: "A4" };
2249
+ },
2250
+ addProseMirrorPlugins() {
2251
+ return [buildPageBreakPlugin(this.options.pageSize)];
2252
+ }
2253
+ });
2254
+
2255
+ // src/extensions/TabIndent.ts
2256
+ var TabIndent = Extension.create({
2257
+ name: "tabIndent",
2258
+ addKeyboardShortcuts() {
2259
+ return {
2260
+ Tab: () => this.editor.commands.insertContent(" ")
2261
+ };
2262
+ }
2263
+ });
2264
+
2265
+ // src/components/PagedEditorContent.tsx
2266
+ var import_react4 = require("@tiptap/react");
2267
+
2268
+ // src/components/FloatingToolbar.tsx
2269
+ var import_react3 = require("react");
2270
+ var import_lucide_react3 = require("lucide-react");
2271
+
2272
+ // src/components/FontPicker.tsx
2273
+ var import_react = require("react");
2274
+ var import_lucide_react = require("lucide-react");
2275
+ var import_jsx_runtime = require("react/jsx-runtime");
2276
+ var GOOGLE_FONTS_URL = "https://fonts.googleapis.com/css2?family=Dancing+Script&family=Inter&family=Roboto+Slab&family=JetBrains+Mono&display=swap";
2277
+ var FONTS = {
2278
+ cursive: { label: "Cursive", family: "'Dancing Script', cursive" },
2279
+ serif: { label: "Serif", family: "Georgia, serif" },
2280
+ sans: { label: "Sans", family: "'Inter', sans-serif" },
2281
+ slab: { label: "Slab", family: "'Roboto Slab', serif" },
2282
+ mono: { label: "Mono", family: "'JetBrains Mono', monospace" }
2283
+ };
2284
+ function FontPicker({ font, onChange, open, onToggle, onClose }) {
2285
+ const ref = (0, import_react.useRef)(null);
2286
+ (0, import_react.useEffect)(() => {
2287
+ if (!open) return;
2288
+ const handler = (e) => {
2289
+ if (!ref.current?.contains(e.target)) onClose();
2290
+ };
2291
+ document.addEventListener("mousedown", handler);
2292
+ return () => document.removeEventListener("mousedown", handler);
2293
+ }, [open, onClose]);
2294
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ink-popover-anchor", ref, children: [
2295
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2296
+ "button",
2297
+ {
2298
+ type: "button",
2299
+ className: `ink-toolbar-btn${open ? " ink-toolbar-btn--active" : ""}`,
2300
+ onClick: onToggle,
2301
+ "aria-label": "Font",
2302
+ title: "Font",
2303
+ style: { width: "auto", padding: "0 8px", gap: 4, fontSize: 13 },
2304
+ children: [
2305
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Type, { size: 14 }),
2306
+ FONTS[font].label
2307
+ ]
2308
+ }
2309
+ ),
2310
+ open && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ink-popover ink-popover--right ink-font-picker", role: "menu", children: Object.keys(FONTS).map((key) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2311
+ "button",
2312
+ {
2313
+ type: "button",
2314
+ role: "menuitem",
2315
+ className: `ink-font-option${font === key ? " ink-font-option--active" : ""}`,
2316
+ style: { fontFamily: FONTS[key].family },
2317
+ onClick: () => {
2318
+ onChange(key);
2319
+ onClose();
2320
+ },
2321
+ children: FONTS[key].label
2322
+ },
2323
+ key
2324
+ )) })
2325
+ ] });
2326
+ }
2327
+
2328
+ // src/components/ColorPanel.tsx
2329
+ var import_react2 = require("react");
2330
+ var import_lucide_react2 = require("lucide-react");
2331
+ var import_jsx_runtime2 = require("react/jsx-runtime");
2332
+ var SWATCHES = {
2333
+ canvasBg: ["#e0ddd4", "#d6e4f0", "#e8f5e9", "#f3e5f5", "#fff8e1", "#2c2c2c"],
2334
+ paperColor: ["#f5f4ed", "#ffffff", "#fffde7", "#fce4ec", "#e8eaf6", "#1a1a1a"],
2335
+ textColor: ["#141413", "#111111", "#1a237e", "#4a148c", "#1b5e20", "#e0e0e0"],
2336
+ lineColor: ["#c8c4b8", "#d0d0d0", "#b0bec5", "#ce93d8", "#a5d6a7", "#555555"],
2337
+ accentColor: ["#1b365d", "#2563eb", "#00796b", "#7b1fa2", "#e65100", "#f48fb1"]
2338
+ };
2339
+ var COLOR_LABELS = {
2340
+ canvasBg: "Canvas",
2341
+ paperColor: "Paper",
2342
+ textColor: "Text",
2343
+ lineColor: "Lines",
2344
+ accentColor: "Accent"
2345
+ };
2346
+ var COLOR_KEYS = Object.keys(SWATCHES);
2347
+ function ColorPanel({ colors, onChange, open, onToggle, onClose }) {
2348
+ const ref = (0, import_react2.useRef)(null);
2349
+ (0, import_react2.useEffect)(() => {
2350
+ if (!open) return;
2351
+ const handler = (e) => {
2352
+ if (!ref.current?.contains(e.target)) onClose();
2353
+ };
2354
+ document.addEventListener("mousedown", handler);
2355
+ return () => document.removeEventListener("mousedown", handler);
2356
+ }, [open, onClose]);
2357
+ function set(key, value) {
2358
+ onChange({ ...colors, [key]: value });
2359
+ }
2360
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ink-popover-anchor", ref, children: [
2361
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2362
+ "button",
2363
+ {
2364
+ type: "button",
2365
+ className: `ink-toolbar-btn${open ? " ink-toolbar-btn--active" : ""}`,
2366
+ onClick: onToggle,
2367
+ "aria-label": "Colors",
2368
+ title: "Colors",
2369
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Palette, { size: 16 })
2370
+ }
2371
+ ),
2372
+ open && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ink-popover ink-popover--right ink-color-panel", role: "dialog", "aria-label": "Color customization", children: COLOR_KEYS.map((key) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ink-color-row", children: [
2373
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "ink-color-label", children: COLOR_LABELS[key] }),
2374
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ink-swatches", children: [
2375
+ SWATCHES[key].map((swatch) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2376
+ "button",
2377
+ {
2378
+ type: "button",
2379
+ className: `ink-swatch${colors[key] === swatch ? " ink-swatch--active" : ""}`,
2380
+ style: { background: swatch },
2381
+ "aria-label": swatch,
2382
+ title: swatch,
2383
+ onClick: () => set(key, swatch)
2384
+ },
2385
+ swatch
2386
+ )),
2387
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "ink-color-custom", title: "Custom color", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2388
+ "input",
2389
+ {
2390
+ type: "color",
2391
+ value: colors[key],
2392
+ onChange: (e) => set(key, e.target.value),
2393
+ "aria-label": `Custom ${COLOR_LABELS[key]} color`
2394
+ }
2395
+ ) })
2396
+ ] })
2397
+ ] }, key)) })
2398
+ ] });
2399
+ }
2400
+
2401
+ // src/components/FloatingToolbar.tsx
2402
+ var import_jsx_runtime3 = require("react/jsx-runtime");
2403
+ function Sep() {
2404
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ink-toolbar-sep", "aria-hidden": "true" });
2405
+ }
2406
+ function FloatingToolbar({
2407
+ editor,
2408
+ buttons,
2409
+ ruled,
2410
+ onToggleRuled,
2411
+ font,
2412
+ onFontChange,
2413
+ colors,
2414
+ onColorsChange
2415
+ }) {
2416
+ const [openPanel, setOpenPanel] = (0, import_react3.useState)(null);
2417
+ (0, import_react3.useEffect)(() => {
2418
+ if (document.querySelector(`link[href="${GOOGLE_FONTS_URL}"]`)) return;
2419
+ const link = document.createElement("link");
2420
+ link.rel = "stylesheet";
2421
+ link.href = GOOGLE_FONTS_URL;
2422
+ document.head.appendChild(link);
2423
+ }, []);
2424
+ function togglePanel(panel) {
2425
+ setOpenPanel((prev) => prev === panel ? null : panel);
2426
+ }
2427
+ const iconSize = 16;
2428
+ const allGroups = [
2429
+ {
2430
+ key: "bold",
2431
+ configs: [{
2432
+ label: "Bold",
2433
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Bold, { size: iconSize }),
2434
+ isActive: () => editor.isActive("bold"),
2435
+ action: () => editor.chain().focus().toggleBold().run()
2436
+ }]
2437
+ },
2438
+ {
2439
+ key: "italic",
2440
+ configs: [{
2441
+ label: "Italic",
2442
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Italic, { size: iconSize }),
2443
+ isActive: () => editor.isActive("italic"),
2444
+ action: () => editor.chain().focus().toggleItalic().run()
2445
+ }]
2446
+ },
2447
+ {
2448
+ key: "underline",
2449
+ configs: [{
2450
+ label: "Underline",
2451
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Underline, { size: iconSize }),
2452
+ isActive: () => editor.isActive("underline"),
2453
+ action: () => editor.chain().focus().toggleUnderline().run()
2454
+ }]
2455
+ },
2456
+ {
2457
+ key: "h1",
2458
+ configs: [{
2459
+ label: "Heading 1",
2460
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Heading1, { size: iconSize }),
2461
+ isActive: () => editor.isActive("heading", { level: 1 }),
2462
+ action: () => editor.chain().focus().toggleHeading({ level: 1 }).run()
2463
+ }]
2464
+ },
2465
+ {
2466
+ key: "h2",
2467
+ configs: [{
2468
+ label: "Heading 2",
2469
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Heading2, { size: iconSize }),
2470
+ isActive: () => editor.isActive("heading", { level: 2 }),
2471
+ action: () => editor.chain().focus().toggleHeading({ level: 2 }).run()
2472
+ }]
2473
+ },
2474
+ {
2475
+ key: "align",
2476
+ configs: [
2477
+ { label: "Align left", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.AlignLeft, { size: iconSize }), isActive: () => editor.isActive({ textAlign: "left" }), action: () => editor.chain().focus().setTextAlign("left").run() },
2478
+ { label: "Align center", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.AlignCenter, { size: iconSize }), isActive: () => editor.isActive({ textAlign: "center" }), action: () => editor.chain().focus().setTextAlign("center").run() },
2479
+ { label: "Align right", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.AlignRight, { size: iconSize }), isActive: () => editor.isActive({ textAlign: "right" }), action: () => editor.chain().focus().setTextAlign("right").run() },
2480
+ { label: "Justify", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.AlignJustify, { size: iconSize }), isActive: () => editor.isActive({ textAlign: "justify" }), action: () => editor.chain().focus().setTextAlign("justify").run() }
2481
+ ]
2482
+ },
2483
+ {
2484
+ key: "list",
2485
+ configs: [
2486
+ { label: "Bullet list", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.List, { size: iconSize }), isActive: () => editor.isActive("bulletList"), action: () => editor.chain().focus().toggleBulletList().run() },
2487
+ { label: "Ordered list", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.ListOrdered, { size: iconSize }), isActive: () => editor.isActive("orderedList"), action: () => editor.chain().focus().toggleOrderedList().run() }
2488
+ ]
2489
+ },
2490
+ {
2491
+ key: "indent",
2492
+ configs: [
2493
+ { label: "Indent", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Indent, { size: iconSize }), isActive: () => false, action: () => editor.chain().focus().sinkListItem("listItem").run() },
2494
+ { label: "Outdent", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Outdent, { size: iconSize }), isActive: () => false, action: () => editor.chain().focus().liftListItem("listItem").run() }
2495
+ ]
2496
+ },
2497
+ {
2498
+ key: "lines",
2499
+ configs: [{
2500
+ label: "Toggle ruled lines",
2501
+ icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Rows, { size: iconSize }),
2502
+ isActive: () => ruled,
2503
+ action: onToggleRuled
2504
+ }]
2505
+ }
2506
+ ];
2507
+ const activeGroups = allGroups.filter((g) => buttons.includes(g.key));
2508
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ink-floating-toolbar-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ink-floating-toolbar", role: "toolbar", "aria-label": "Text formatting", children: [
2509
+ activeGroups.map((group, gi) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "ink-toolbar-group", style: { display: "contents" }, children: [
2510
+ group.configs.map((cfg) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2511
+ "button",
2512
+ {
2513
+ type: "button",
2514
+ className: `ink-toolbar-btn${cfg.isActive() ? " ink-toolbar-btn--active" : ""}`,
2515
+ onClick: cfg.action,
2516
+ "aria-label": cfg.label,
2517
+ title: cfg.label,
2518
+ children: cfg.icon
2519
+ },
2520
+ cfg.label
2521
+ )),
2522
+ gi < activeGroups.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Sep, {})
2523
+ ] }, group.key)),
2524
+ activeGroups.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Sep, {}),
2525
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2526
+ FontPicker,
2527
+ {
2528
+ font,
2529
+ onChange: onFontChange,
2530
+ open: openPanel === "font",
2531
+ onToggle: () => togglePanel("font"),
2532
+ onClose: () => setOpenPanel(null)
2533
+ }
2534
+ ),
2535
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2536
+ ColorPanel,
2537
+ {
2538
+ colors,
2539
+ onChange: onColorsChange,
2540
+ open: openPanel === "color",
2541
+ onToggle: () => togglePanel("color"),
2542
+ onClose: () => setOpenPanel(null)
2543
+ }
2544
+ )
2545
+ ] }) });
2546
+ }
2547
+
2548
+ // src/components/PagedEditorContent.tsx
2549
+ var import_jsx_runtime4 = require("react/jsx-runtime");
2550
+ function PagedEditorContent({
2551
+ editor,
2552
+ pageSize,
2553
+ theme,
2554
+ toolbar,
2555
+ ruled,
2556
+ onToggleRuled,
2557
+ font,
2558
+ onFontChange,
2559
+ colors,
2560
+ onColorsChange
2561
+ }) {
2562
+ const widthPx = getPageWidthPx(pageSize);
2563
+ const bodyWidthPx = getBodyWidthPx(pageSize);
2564
+ const dims = PAGE_DIMENSIONS[pageSize];
2565
+ const pageHeightPx = Math.round(dims.heightMm * 3.7795);
2566
+ const hasToolbar = toolbar.length > 0;
2567
+ const bodyHeightPx = getBodyHeightPx(pageSize);
2568
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2569
+ "div",
2570
+ {
2571
+ className: "ink-page-wrap",
2572
+ "data-theme": theme,
2573
+ style: {
2574
+ "--ink-bg": colors.canvasBg,
2575
+ "--ink-page": colors.paperColor,
2576
+ "--ink-text": colors.textColor,
2577
+ "--ink-border-line": colors.lineColor,
2578
+ "--ink-accent": colors.accentColor,
2579
+ "--ink-font-body": FONTS[font].family
2580
+ },
2581
+ children: [
2582
+ editor && hasToolbar && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2583
+ FloatingToolbar,
2584
+ {
2585
+ editor,
2586
+ buttons: toolbar,
2587
+ ruled,
2588
+ onToggleRuled,
2589
+ font,
2590
+ onFontChange,
2591
+ colors,
2592
+ onColorsChange
2593
+ }
2594
+ ),
2595
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2596
+ "div",
2597
+ {
2598
+ className: `ink-page-card${ruled ? " ink-ruled" : ""}`,
2599
+ style: {
2600
+ width: widthPx,
2601
+ minHeight: pageHeightPx,
2602
+ padding: `${dims.paddingTopMm}mm ${dims.paddingRightMm}mm ${dims.paddingBottomMm}mm ${dims.paddingLeftMm}mm`,
2603
+ ["--ink-padding-top"]: `${dims.paddingTopMm}mm`,
2604
+ ["--ink-padding-right"]: `${dims.paddingRightMm}mm`,
2605
+ ["--ink-padding-left"]: `${dims.paddingLeftMm}mm`,
2606
+ ["--ink-body-width"]: `${bodyWidthPx}px`,
2607
+ ["--ink-body-height"]: `${bodyHeightPx}px`
2608
+ },
2609
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react4.EditorContent, { editor })
2610
+ }
2611
+ )
2612
+ ]
2613
+ }
2614
+ );
2615
+ }
2616
+
2617
+ // src/InkEditor.tsx
2618
+ var import_jsx_runtime5 = require("react/jsx-runtime");
2619
+ var DEFAULT_TOOLBAR = ["bold", "italic", "underline", "h1", "h2", "align", "list", "indent", "lines"];
2620
+ function InkEditor({
2621
+ pageSize = "A4",
2622
+ onChange,
2623
+ theme = "parchment",
2624
+ toolbar = DEFAULT_TOOLBAR
2625
+ }) {
2626
+ const [ruled, setRuled] = (0, import_react6.useState)(false);
2627
+ const [font, setFont] = (0, import_react6.useState)("cursive");
2628
+ const [colors, setColors] = (0, import_react6.useState)(
2629
+ theme === "minimal" ? MINIMAL_DEFAULTS : PARCHMENT_DEFAULTS
2630
+ );
2631
+ const editor = (0, import_react5.useEditor)({
2632
+ extensions: [
2633
+ import_starter_kit.default,
2634
+ PageLayout.configure({ pageSize }),
2635
+ import_extension_text_align.default.configure({ types: ["heading", "paragraph"] }),
2636
+ import_extension_underline.default,
2637
+ TabIndent
2638
+ ],
2639
+ onUpdate({ editor: editor2 }) {
2640
+ onChange?.(editor2.getJSON());
2641
+ }
2642
+ });
2643
+ (0, import_react6.useEffect)(() => {
2644
+ return () => {
2645
+ editor?.destroy();
2646
+ };
2647
+ }, [editor]);
2648
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2649
+ PagedEditorContent,
2650
+ {
2651
+ editor,
2652
+ pageSize,
2653
+ theme,
2654
+ toolbar,
2655
+ ruled,
2656
+ onToggleRuled: () => setRuled((r) => !r),
2657
+ font,
2658
+ onFontChange: setFont,
2659
+ colors,
2660
+ onColorsChange: setColors
2661
+ }
2662
+ );
2663
+ }
2664
+ // Annotate the CommonJS export names for ESM import in node:
2665
+ 0 && (module.exports = {
2666
+ InkEditor,
2667
+ MINIMAL_DEFAULTS,
2668
+ PARCHMENT_DEFAULTS
2669
+ });
2670
+ //# sourceMappingURL=index.js.map