@aicayzer/inkkit 0.3.1 → 0.3.3

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.
@@ -7916,7 +7916,7 @@ function postprocess(events) {
7916
7916
  * @returns {Array<Chunk>}
7917
7917
  * Chunks.
7918
7918
  */
7919
- var search$1 = /[\0\t\n\r]/g;
7919
+ var search$2 = /[\0\t\n\r]/g;
7920
7920
  /**
7921
7921
  * @returns {Preprocessor}
7922
7922
  * Preprocess a value.
@@ -7941,8 +7941,8 @@ function preprocess() {
7941
7941
  start = void 0;
7942
7942
  }
7943
7943
  while (startPosition < value.length) {
7944
- search$1.lastIndex = startPosition;
7945
- const match = search$1.exec(value);
7944
+ search$2.lastIndex = startPosition;
7945
+ const match = search$2.exec(value);
7946
7946
  const endPosition = match && match.index !== void 0 ? match.index : value.length;
7947
7947
  const code = value.charCodeAt(endPosition);
7948
7948
  if (!match) {
@@ -22734,7 +22734,7 @@ var SerializerState = class extends Stack {
22734
22734
  #closeMark;
22735
22735
  };
22736
22736
  //#endregion
22737
- //#region node_modules/.pnpm/prosemirror-view@1.42.6_patch_hash=808819d35255b79fc131ebc4873ab2fbcd47cd8f5bc05cd11e8caab02ecee548/node_modules/prosemirror-view/dist/index.js
22737
+ //#region node_modules/.pnpm/prosemirror-view@1.42.6_patch_hash=f2853b430244e6a0ce28d0ee793854c199c3f40279a807cb6690af21fae2d25e/node_modules/prosemirror-view/dist/index.js
22738
22738
  var domIndex = function(node) {
22739
22739
  for (var index = 0;; index++) {
22740
22740
  node = node.previousSibling;
@@ -23907,7 +23907,7 @@ var NodeViewDesc = class NodeViewDesc extends ViewDesc {
23907
23907
  this.children = replaceNodes(this.children, pos, pos + text.length, view, desc);
23908
23908
  }
23909
23909
  update(node, outerDeco, innerDeco, view) {
23910
- if (this.dirty == NODE_DIRTY || !node.sameMarkup(this.node)) return false;
23910
+ if (this.dirty == NODE_DIRTY || (this.parent ? !node.sameMarkup(this.node) : node.type != this.node.type)) return false;
23911
23911
  this.updateInner(node, outerDeco, innerDeco, view);
23912
23912
  return true;
23913
23913
  }
@@ -28352,6 +28352,7 @@ var defaultLabels = {
28352
28352
  slashBulletList: "Bulleted List",
28353
28353
  slashOrderedList: "Numbered List",
28354
28354
  slashTaskList: "Checklist",
28355
+ taskCheckbox: "Task",
28355
28356
  slashQuote: "Quote",
28356
28357
  slashCodeBlock: "Code Block",
28357
28358
  slashTable: "Table",
@@ -28393,6 +28394,7 @@ var defaultLabels = {
28393
28394
  fileOpen: "Open",
28394
28395
  filePreviewUnavailable: "Preview unavailable",
28395
28396
  filePDFPreview: "Open to view this PDF",
28397
+ imageResize: "Resize image",
28396
28398
  wikiMissing: "Link unavailable",
28397
28399
  comment: "Author comment",
28398
28400
  preservedMarkdown: "Preserved Markdown"
@@ -44149,14 +44151,16 @@ function samePresentation(a, b) {
44149
44151
  var FileResolution = class {
44150
44152
  runtime;
44151
44153
  changed;
44154
+ cache;
44152
44155
  state = { status: "idle" };
44153
44156
  reference;
44154
44157
  key = "";
44155
44158
  request;
44156
44159
  dead = false;
44157
- constructor(runtime, changed) {
44160
+ constructor(runtime, changed, cache) {
44158
44161
  this.runtime = runtime;
44159
44162
  this.changed = changed;
44163
+ this.cache = cache;
44160
44164
  }
44161
44165
  update(reference) {
44162
44166
  const key = JSON.stringify([
@@ -44167,11 +44171,29 @@ var FileResolution = class {
44167
44171
  ]);
44168
44172
  this.reference = Object.freeze({ ...reference });
44169
44173
  if (this.key === key && this.request?.isCurrent()) return;
44174
+ if (this.key !== key) this.cache?.release(this, this.key);
44170
44175
  this.key = key;
44176
+ this.cache?.retain(this, key);
44177
+ const cached = this.cache?.get(key);
44178
+ if (cached) {
44179
+ if (this.request?.pending) this.request.controller.abort();
44180
+ this.request = {
44181
+ controller: cached.controller,
44182
+ pending: false,
44183
+ isCurrent: cached.isCurrent
44184
+ };
44185
+ this.state = {
44186
+ status: "resolved",
44187
+ presentation: cached.presentation
44188
+ };
44189
+ this.changed();
44190
+ return;
44191
+ }
44171
44192
  this.retry();
44172
44193
  }
44173
44194
  /** Resolves again from a blank loading card. */
44174
44195
  retry() {
44196
+ this.cache?.delete(this.key);
44175
44197
  this.cancel();
44176
44198
  this.start(true);
44177
44199
  }
@@ -44180,17 +44202,19 @@ var FileResolution = class {
44180
44202
  * the host's change doesn't affect neither blanks nor moves.
44181
44203
  */
44182
44204
  refresh() {
44205
+ this.cache?.delete(this.key);
44183
44206
  if (this.dead || !this.runtime.active()) return;
44184
44207
  if (this.state.status === "idle") this.retry();
44185
44208
  else this.start(false);
44186
44209
  }
44187
44210
  start(blank) {
44188
- this.request?.controller.abort();
44211
+ if (!this.cache || this.request?.pending) this.request?.controller.abort();
44189
44212
  this.request = void 0;
44190
44213
  if (this.dead || !this.reference || !this.runtime.active()) return;
44191
44214
  const captured = this.runtime.capture();
44192
44215
  const request = {
44193
44216
  controller: new AbortController(),
44217
+ pending: true,
44194
44218
  isCurrent: captured.isCurrent
44195
44219
  };
44196
44220
  this.request = request;
@@ -44204,10 +44228,16 @@ var FileResolution = class {
44204
44228
  }).then((presentation) => {
44205
44229
  if (!this.current(request)) return;
44206
44230
  const before = this.state;
44231
+ request.pending = false;
44207
44232
  this.state = {
44208
44233
  status: "resolved",
44209
44234
  presentation
44210
44235
  };
44236
+ this.cache?.set(this.key, {
44237
+ presentation,
44238
+ isCurrent: request.isCurrent,
44239
+ controller: request.controller
44240
+ });
44211
44241
  if (before.status !== "resolved" || !samePresentation(before.presentation, presentation)) this.changed();
44212
44242
  }, (failure) => {
44213
44243
  if (!this.current(request)) return;
@@ -44228,9 +44258,63 @@ var FileResolution = class {
44228
44258
  this.request = void 0;
44229
44259
  this.state = { status: "idle" };
44230
44260
  }
44261
+ /** Hidden views keep an answer for this document, but never accept a late answer. */
44262
+ suspend() {
44263
+ if (this.request?.pending || this.state.status === "idle") this.cancel();
44264
+ }
44265
+ invalidate() {
44266
+ this.cache?.delete(this.key);
44267
+ this.cancel();
44268
+ }
44231
44269
  destroy() {
44232
44270
  this.dead = true;
44233
- this.cancel();
44271
+ this.cache?.release(this, this.key);
44272
+ if (!this.cache || this.request?.pending || this.state.status !== "resolved") this.cancel();
44273
+ }
44274
+ };
44275
+ /** A source commit may replace a node view without changing its authored reference. */
44276
+ var FilePresentationCache = class {
44277
+ entries = /* @__PURE__ */ new Map();
44278
+ retain(owner, key) {
44279
+ let entry = this.entries.get(key);
44280
+ if (!entry) this.entries.set(key, entry = {
44281
+ owners: /* @__PURE__ */ new Set(),
44282
+ controllers: /* @__PURE__ */ new Set()
44283
+ });
44284
+ entry.owners.add(owner);
44285
+ }
44286
+ release(owner, key) {
44287
+ const entry = this.entries.get(key);
44288
+ if (!entry) return;
44289
+ entry.owners.delete(owner);
44290
+ queueMicrotask(() => {
44291
+ if (this.entries.get(key) === entry && !entry.owners.size) {
44292
+ for (const controller of entry.controllers) controller.abort();
44293
+ this.entries.delete(key);
44294
+ }
44295
+ });
44296
+ }
44297
+ get(key) {
44298
+ const value = this.entries.get(key)?.value;
44299
+ if (value && !value.isCurrent()) {
44300
+ this.delete(key);
44301
+ return;
44302
+ }
44303
+ return value;
44304
+ }
44305
+ set(key, value) {
44306
+ const entry = this.entries.get(key);
44307
+ if (!entry) return;
44308
+ entry.value = value;
44309
+ entry.controllers.add(value.controller);
44310
+ }
44311
+ delete(key) {
44312
+ const entry = this.entries.get(key);
44313
+ if (entry) {
44314
+ for (const controller of entry.controllers) controller.abort();
44315
+ entry.controllers.clear();
44316
+ entry.value = void 0;
44317
+ }
44234
44318
  }
44235
44319
  };
44236
44320
  //#endregion
@@ -45319,6 +45403,81 @@ function caretAfterInsertion(tr, view) {
45319
45403
  return tr.setSelection($end.parent.inlineContent ? TextSelection.create(tr.doc, end) : caretAfter(tr.doc, end, view && calloutFolds(view, tr)));
45320
45404
  }
45321
45405
  //#endregion
45406
+ //#region src/parser.ts
45407
+ /**
45408
+ * Milkdown's parser state, except that it throws where Milkdown would drop content. Milkdown
45409
+ * catches the failure to build a node (`createNodeInParserFail`), logs it and carries on without
45410
+ * the node and everything inside it, so the document it returns can silently lack part of its
45411
+ * source.
45412
+ */
45413
+ var CheckedParserState = class extends ParserState {
45414
+ constructor(schema) {
45415
+ super(schema);
45416
+ const closeNode = this.closeNode;
45417
+ const addNode = this.addNode;
45418
+ const unbuildable = (type, attrs, content) => {
45419
+ try {
45420
+ return !type.createAndFill(attrs, content);
45421
+ } catch {
45422
+ return true;
45423
+ }
45424
+ };
45425
+ this.closeNode = () => {
45426
+ const element = this.top();
45427
+ if (!element || this.size() < 2 || unbuildable(element.type, element.attrs, element.content)) throw new Error(`Cannot build ${element?.type.name ?? "a node"}`);
45428
+ return closeNode();
45429
+ };
45430
+ this.addNode = (type, attrs, content) => {
45431
+ if (!this.top() || unbuildable(type, attrs, content)) throw new Error(`Cannot build ${type.name}`);
45432
+ return addNode(type, attrs, content);
45433
+ };
45434
+ }
45435
+ };
45436
+ /** The document, or undefined when Milkdown cannot build all of it. */
45437
+ function build(schema, tree) {
45438
+ try {
45439
+ return new CheckedParserState(schema).next(tree).toDoc();
45440
+ } catch {
45441
+ return;
45442
+ }
45443
+ }
45444
+ /**
45445
+ * Parses Markdown as Milkdown does, but never returns a document that lacks part of its source.
45446
+ * A top-level block that cannot be built is kept as its literal source, as unsupported syntax is,
45447
+ * so the rest of the document stays editable; when that is not enough, it throws `preservation`.
45448
+ */
45449
+ function parseMarkdown(schema, remark, markdown, parsedTree) {
45450
+ const tree = parsedTree ?? remark.runSync(remark.parse(markdown), markdown);
45451
+ const doc = build(schema, tree);
45452
+ if (doc) return doc;
45453
+ const source = markdown.replace(/^\uFEFF/, "");
45454
+ tree.children = tree.children.map((node) => {
45455
+ if (build(schema, {
45456
+ type: "root",
45457
+ children: [node]
45458
+ })) return node;
45459
+ const start = node.position?.start.offset;
45460
+ const end = node.position?.end.offset;
45461
+ if (start == null || end == null) return node;
45462
+ const value = source.slice(start, end);
45463
+ return {
45464
+ type: "inkkitLiteral",
45465
+ value,
45466
+ comments: literalCommentSpans(value),
45467
+ position: node.position
45468
+ };
45469
+ });
45470
+ const kept = build(schema, tree);
45471
+ if (kept) return kept;
45472
+ throw new InkKitError("preservation", "The Markdown cannot be shown without losing part of it");
45473
+ }
45474
+ /** Replaces Milkdown's parser with `parseMarkdown`, for every caller of `parserCtx`. */
45475
+ var checkedParser = (ctx) => async () => {
45476
+ await ctx.wait(ParserReady);
45477
+ const schema = ctx.get(schemaCtx);
45478
+ ctx.set(parserCtx, (markdown) => parseMarkdown(schema, ctx.get(remarkCtx), markdown));
45479
+ };
45480
+ //#endregion
45322
45481
  //#region src/gutter.ts
45323
45482
  var listMarker = /(?:[-*+]|\d{1,9}[.)])(?=[ \t])/g;
45324
45483
  /**
@@ -46974,6 +47133,18 @@ var nothingCarried = {
46974
47133
  var Preservation = class Preservation {
46975
47134
  ctx;
46976
47135
  source;
47136
+ static recent = /* @__PURE__ */ new WeakMap();
47137
+ processor;
47138
+ rawTree;
47139
+ sourceTree;
47140
+ static read(ctx, source) {
47141
+ const cached = this.recent.get(ctx);
47142
+ const previous = cached?.processor === ctx.get(remarkCtx) ? cached : void 0;
47143
+ if (previous?.source === source) return previous;
47144
+ const next = new Preservation(ctx, source, previous);
47145
+ this.recent.set(ctx, next);
47146
+ return next;
47147
+ }
46977
47148
  /** The document as loaded from `source`. */
46978
47149
  baseline;
46979
47150
  blocks = [];
@@ -46991,18 +47162,22 @@ var Preservation = class Preservation {
46991
47162
  nestedLiterals = /* @__PURE__ */ new Set();
46992
47163
  literalsRead = /* @__PURE__ */ new WeakSet();
46993
47164
  definitionSources;
46994
- constructor(ctx, source) {
47165
+ constructor(ctx, source, previous) {
46995
47166
  this.ctx = ctx;
46996
47167
  this.source = source;
46997
47168
  try {
46998
47169
  const parse = (text) => parseDoc(ctx, text);
46999
- this.baseline = parse(source);
47000
47170
  this.lineEnding = !source.includes("\n") ? source.includes("\r") ? "\r" : "\n" : source.includes("\r\n") && !source.replaceAll("\r\n", "").includes("\n") ? "\r\n" : "\n";
47001
- const processor = ctx.get(remarkCtx);
47171
+ const processor = this.processor = ctx.get(remarkCtx);
47002
47172
  const offset = source.startsWith("") ? 1 : 0;
47003
47173
  const body = source.slice(offset);
47004
- const rawTree = parseTree(ctx, source);
47005
- const tree = processor.runSync(structuredClone(rawTree), { value: body });
47174
+ const incremental = previous?.readEditedSource(source);
47175
+ const rawTree = incremental?.raw ?? parseTree(ctx, source);
47176
+ const tree = incremental?.tree ?? processor.runSync(structuredClone(rawTree), { value: body });
47177
+ this.rawTree = rawTree;
47178
+ this.sourceTree = tree;
47179
+ this.baseline = incremental?.doc ?? parseMarkdown(ctx.get(schemaCtx), processor, source, structuredClone(tree));
47180
+ const positions = new Map(rawTree.children.map((node) => [`${node.position?.start.offset}:${node.position?.end.offset}`, node]));
47006
47181
  for (const [index, node] of tree.children.entries()) {
47007
47182
  let start = node.position?.start.offset;
47008
47183
  const end = node.position?.end.offset;
@@ -47014,9 +47189,9 @@ var Preservation = class Preservation {
47014
47189
  const text = source.slice(start, sourceEnd);
47015
47190
  const own = this.baseline.childCount === tree.children.length;
47016
47191
  const parsed = own ? this.baseline.type.create(null, [this.baseline.child(index)]) : parse(text);
47017
- const blockText = parsed.childCount === 1 ? blockForm(ctx, parsed.firstChild) : form(write(ctx, parsed));
47192
+ const blockText = previous?.forms.get(parsed.firstChild) ?? (parsed.childCount === 1 ? blockForm(ctx, parsed.firstChild) : form(write(ctx, parsed)));
47018
47193
  if (own) this.forms.set(this.baseline.child(index), blockText);
47019
- const ast = positionalNode(rawTree, node);
47194
+ const ast = positions.get(`${node.position?.start.offset}:${node.position?.end.offset}`) ?? node;
47020
47195
  if (parsed.childCount === 1 && parsed.firstChild.type.name === "literal_markdown") literalForms.set(ast, blockText);
47021
47196
  this.blocks.push({
47022
47197
  start,
@@ -47053,6 +47228,66 @@ var Preservation = class Preservation {
47053
47228
  throw new PreservationError("Cannot read Markdown for preservation", { cause });
47054
47229
  }
47055
47230
  }
47231
+ readEditedSource(source) {
47232
+ if (this.source.startsWith("") || source.startsWith("") || /[\[\]<>%]/.test(this.source) || /[\[\]<>%]/.test(source) || this.blocks.length !== this.baseline.childCount || this.blocks.some((_, index) => !["paragraph", "heading"].includes(this.baseline.child(index).type.name))) return;
47233
+ let prefix = 0, suffix = 0;
47234
+ while (prefix < source.length && prefix < this.source.length && source[prefix] === this.source[prefix]) prefix++;
47235
+ while (suffix < source.length - prefix && suffix < this.source.length - prefix && source[source.length - suffix - 1] === this.source[this.source.length - suffix - 1]) suffix++;
47236
+ const first = Math.max(0, this.blocks.findIndex((block) => block.end >= prefix) - 1);
47237
+ let last = this.blocks.findIndex((block) => block.start > this.source.length - suffix);
47238
+ if (last < 0) last = this.blocks.length - 1;
47239
+ else last = Math.min(this.blocks.length - 1, last + 1);
47240
+ if (!this.blocks[first] || !this.blocks[last]) return;
47241
+ const start = this.blocks[first].start;
47242
+ const end = this.blocks[last].end;
47243
+ const delta = source.length - this.source.length;
47244
+ const nextEnd = end + delta;
47245
+ if (nextEnd < start || prefix < start || this.source.length - suffix > end) return;
47246
+ const part = new Preservation(this.ctx, source.slice(start, nextEnd));
47247
+ if (part.blocks.length !== part.baseline.childCount || part.blocks.some((_, index) => !["paragraph", "heading"].includes(part.baseline.child(index).type.name))) return;
47248
+ const lines = [0];
47249
+ for (const match of source.matchAll(/\r\n|\r|\n/g)) lines.push(match.index + match[0].length);
47250
+ const point = (offset) => {
47251
+ let low = 0, high = lines.length;
47252
+ while (low + 1 < high) {
47253
+ const middle = low + high >> 1;
47254
+ if (lines[middle] <= offset) low = middle;
47255
+ else high = middle;
47256
+ }
47257
+ return {
47258
+ offset,
47259
+ line: low + 1,
47260
+ column: offset - lines[low] + 1
47261
+ };
47262
+ };
47263
+ const shifted = (node, amount) => {
47264
+ const visit = (value) => {
47265
+ if (Array.isArray(value)) return value.map(visit);
47266
+ if (!value || typeof value !== "object") return value;
47267
+ if ("offset" in value && typeof value.offset === "number") return point(value.offset + amount);
47268
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, visit(child)]));
47269
+ };
47270
+ return visit(node);
47271
+ };
47272
+ const splice = (old, fresh) => ({
47273
+ type: "root",
47274
+ children: [
47275
+ ...old.children.slice(0, first),
47276
+ ...fresh.children.map((node) => shifted(node, start)),
47277
+ ...old.children.slice(last + 1).map((node) => shifted(node, delta))
47278
+ ]
47279
+ });
47280
+ const children = [];
47281
+ this.baseline.forEach((node, _offset, index) => {
47282
+ if (index === first) part.baseline.forEach((child) => children.push(child));
47283
+ if (index < first || index > last) children.push(node);
47284
+ });
47285
+ return {
47286
+ raw: splice(this.rawTree, part.rawTree),
47287
+ tree: splice(this.sourceTree, part.sourceTree),
47288
+ doc: this.baseline.copy(Fragment.fromArray(children))
47289
+ };
47290
+ }
47056
47291
  retainBlock(block, after, canonical, joined = [], reorder) {
47057
47292
  const offset = this.source.startsWith("") ? 1 : 0;
47058
47293
  const start = block.ast.position?.start.offset;
@@ -47189,7 +47424,7 @@ var Preservation = class Preservation {
47189
47424
  static plainInline(node) {
47190
47425
  let plain = true;
47191
47426
  node.descendants((child) => {
47192
- if (child.isInline && !child.isText && child.type.name !== "hardbreak") plain = false;
47427
+ if (child.isInline && !child.isText && !["hardbreak", "footnote_reference"].includes(child.type.name)) plain = false;
47193
47428
  return plain;
47194
47429
  });
47195
47430
  return plain;
@@ -47473,6 +47708,12 @@ var Preservation = class Preservation {
47473
47708
  const block = position == null ? void 0 : this.blocks[position];
47474
47709
  if (index === 0) return position === 0 && block ? this.source.slice(0, block.start) : this.source.startsWith("") ? "" : "";
47475
47710
  const previous = positions[index - 1];
47711
+ if (previous === this.blocks.length - 1 && position == null) {
47712
+ const ending = this.source.slice(this.blocks[previous].end);
47713
+ const lines = ending.replace(/\r\n?/g, "\n");
47714
+ if (/\n[ \t]*\n/.test(lines)) return ending + (lines.endsWith("\n") ? "" : this.lineEnding);
47715
+ return ending + this.lineEnding.repeat(lines.endsWith("\n") ? 1 : 2);
47716
+ }
47476
47717
  return previous != null && position === previous + 1 && block ? this.source.slice(this.blocks[previous].end, block.start) : this.lineEnding.repeat(2);
47477
47718
  }
47478
47719
  build(doc, positions, current, carried, from, to, canonical, tree) {
@@ -48505,81 +48746,6 @@ function normalizeTableColumnAlignment(element) {
48505
48746
  return normalizeTableAlignment(header ?? element);
48506
48747
  }
48507
48748
  //#endregion
48508
- //#region src/parser.ts
48509
- /**
48510
- * Milkdown's parser state, except that it throws where Milkdown would drop content. Milkdown
48511
- * catches the failure to build a node (`createNodeInParserFail`), logs it and carries on without
48512
- * the node and everything inside it, so the document it returns can silently lack part of its
48513
- * source.
48514
- */
48515
- var CheckedParserState = class extends ParserState {
48516
- constructor(schema) {
48517
- super(schema);
48518
- const closeNode = this.closeNode;
48519
- const addNode = this.addNode;
48520
- const unbuildable = (type, attrs, content) => {
48521
- try {
48522
- return !type.createAndFill(attrs, content);
48523
- } catch {
48524
- return true;
48525
- }
48526
- };
48527
- this.closeNode = () => {
48528
- const element = this.top();
48529
- if (!element || this.size() < 2 || unbuildable(element.type, element.attrs, element.content)) throw new Error(`Cannot build ${element?.type.name ?? "a node"}`);
48530
- return closeNode();
48531
- };
48532
- this.addNode = (type, attrs, content) => {
48533
- if (!this.top() || unbuildable(type, attrs, content)) throw new Error(`Cannot build ${type.name}`);
48534
- return addNode(type, attrs, content);
48535
- };
48536
- }
48537
- };
48538
- /** The document, or undefined when Milkdown cannot build all of it. */
48539
- function build(schema, tree) {
48540
- try {
48541
- return new CheckedParserState(schema).next(tree).toDoc();
48542
- } catch {
48543
- return;
48544
- }
48545
- }
48546
- /**
48547
- * Parses Markdown as Milkdown does, but never returns a document that lacks part of its source.
48548
- * A top-level block that cannot be built is kept as its literal source, as unsupported syntax is,
48549
- * so the rest of the document stays editable; when that is not enough, it throws `preservation`.
48550
- */
48551
- function parseMarkdown(schema, remark, markdown) {
48552
- const tree = remark.runSync(remark.parse(markdown), markdown);
48553
- const doc = build(schema, tree);
48554
- if (doc) return doc;
48555
- const source = markdown.replace(/^\uFEFF/, "");
48556
- tree.children = tree.children.map((node) => {
48557
- if (build(schema, {
48558
- type: "root",
48559
- children: [node]
48560
- })) return node;
48561
- const start = node.position?.start.offset;
48562
- const end = node.position?.end.offset;
48563
- if (start == null || end == null) return node;
48564
- const value = source.slice(start, end);
48565
- return {
48566
- type: "inkkitLiteral",
48567
- value,
48568
- comments: literalCommentSpans(value),
48569
- position: node.position
48570
- };
48571
- });
48572
- const kept = build(schema, tree);
48573
- if (kept) return kept;
48574
- throw new InkKitError("preservation", "The Markdown cannot be shown without losing part of it");
48575
- }
48576
- /** Replaces Milkdown's parser with `parseMarkdown`, for every caller of `parserCtx`. */
48577
- var checkedParser = (ctx) => async () => {
48578
- await ctx.wait(ParserReady);
48579
- const schema = ctx.get(schemaCtx);
48580
- ctx.set(parserCtx, (markdown) => parseMarkdown(schema, ctx.get(remarkCtx), markdown));
48581
- };
48582
- //#endregion
48583
48749
  //#region src/callouts.ts
48584
48750
  var kinds = /* @__PURE__ */ new Set([
48585
48751
  "NOTE",
@@ -51648,8 +51814,7 @@ function modeSelectionMap(ctx, source, doc) {
51648
51814
  if (at >= 0) append(value, Array.from({ length: value.length + 1 }, (_, index) => start + bom + at + index));
51649
51815
  }
51650
51816
  };
51651
- const processor = ctx.get(remarkCtx);
51652
- processor.runSync(processor.parse(source), { value: source }).children.forEach((node) => visit(node));
51817
+ Preservation.read(ctx, source).sourceTree.children.forEach((node) => visit(node));
51653
51818
  let cursor = 0;
51654
51819
  doc.descendants((node, position) => {
51655
51820
  if ([
@@ -51863,15 +52028,19 @@ var ImageView = class {
51863
52028
  /** The card or picture, which a standalone image's block row holds. */
51864
52029
  element;
51865
52030
  image;
52031
+ handle;
52032
+ destroyed = false;
52033
+ restoreResizeFocus = false;
51866
52034
  media;
51867
52035
  pdf;
51868
52036
  width = null;
51869
52037
  stopResize;
51870
52038
  resolution;
51871
52039
  previewFailed = false;
52040
+ suspended = false;
51872
52041
  /** Present when the host sets `files`, which then shows every image. */
51873
52042
  runtime;
51874
- constructor(node, view, getPos, adapter, host, files, canMutate = () => true) {
52043
+ constructor(node, view, getPos, adapter, host, files, cache, canMutate = () => true) {
51875
52044
  this.node = node;
51876
52045
  this.view = view;
51877
52046
  this.getPos = getPos;
@@ -51900,7 +52069,7 @@ var ImageView = class {
51900
52069
  }, () => {
51901
52070
  this.previewFailed = false;
51902
52071
  this.render();
51903
- });
52072
+ }, cache);
51904
52073
  this.resolution.update(this.reference());
51905
52074
  }
51906
52075
  this.render();
@@ -51933,18 +52102,33 @@ var ImageView = class {
51933
52102
  if (!this.runtime) return;
51934
52103
  const refresh = refreshOf(event);
51935
52104
  if (refresh) {
51936
- if (!this.active() || !refreshes(refresh, this.reference().target)) return;
52105
+ if (!refreshes(refresh, this.reference().target)) return;
52106
+ if (!this.active()) {
52107
+ this.resolution?.invalidate();
52108
+ return;
52109
+ }
51937
52110
  if (this.previewFailed) this.resolution?.retry();
51938
52111
  else this.resolution?.refresh();
51939
52112
  return;
51940
52113
  }
51941
52114
  if (!this.active()) {
52115
+ if (this.suspended) return;
52116
+ this.suspended = true;
51942
52117
  this.stopResize?.();
51943
52118
  this.stopResize = void 0;
51944
- this.resolution?.cancel();
51945
- this.releaseMedia();
51946
- this.render();
51947
- } else this.resolution?.update(this.reference());
52119
+ this.resolution?.suspend();
52120
+ if (this.media || this.pdf) this.render();
52121
+ } else {
52122
+ const suspended = this.suspended;
52123
+ this.suspended = false;
52124
+ this.resolution?.update(this.reference());
52125
+ const state = this.resolution?.state;
52126
+ if (suspended && state?.status === "resolved" && [
52127
+ "audio",
52128
+ "video",
52129
+ "pdf"
52130
+ ].includes(state.presentation.kind)) this.render();
52131
+ }
51948
52132
  };
51949
52133
  cancelResize = () => {
51950
52134
  if (!this.stopResize) return;
@@ -51964,6 +52148,7 @@ var ImageView = class {
51964
52148
  this.pdf = void 0;
51965
52149
  }
51966
52150
  destroy() {
52151
+ this.destroyed = true;
51967
52152
  this.view.dom.removeEventListener("inkkit-cancel-resize", this.cancelResize);
51968
52153
  this.view.dom.removeEventListener(mediaPolicyEvent, this.mediaPolicy);
51969
52154
  this.stopResize?.();
@@ -52026,6 +52211,7 @@ var ImageView = class {
52026
52211
  if (!state.selection.eq(selection)) this.view.dispatch(state.tr.setSelection(selection));
52027
52212
  }
52028
52213
  card(label, status, retry = false) {
52214
+ if (this.resolution?.state.status !== "loading" && !this.handle) this.restoreResizeFocus = false;
52029
52215
  const footer = document.createElement("span");
52030
52216
  footer.className = "inkkit-file-footer";
52031
52217
  const name = document.createElement("span");
@@ -52060,6 +52246,7 @@ var ImageView = class {
52060
52246
  this.render();
52061
52247
  }
52062
52248
  render() {
52249
+ if (this.handle && document.activeElement === this.handle) this.restoreResizeFocus = true;
52063
52250
  this.stopResize?.();
52064
52251
  this.stopResize = void 0;
52065
52252
  this.releaseMedia();
@@ -52071,6 +52258,7 @@ var ImageView = class {
52071
52258
  this.width = width;
52072
52259
  this.element.textContent = "";
52073
52260
  this.image = void 0;
52261
+ this.handle = void 0;
52074
52262
  if (this.runtime) {
52075
52263
  this.element.classList.add("inkkit-file");
52076
52264
  const state = this.resolution.state;
@@ -52092,6 +52280,14 @@ var ImageView = class {
52092
52280
  return;
52093
52281
  }
52094
52282
  const presentation = state.presentation;
52283
+ if (!this.active() && [
52284
+ "audio",
52285
+ "video",
52286
+ "pdf"
52287
+ ].includes(presentation.kind)) {
52288
+ this.card(label);
52289
+ return;
52290
+ }
52095
52291
  if (presentation.kind === "missing") this.card(label, this.runtime.labels.fileMissing, true);
52096
52292
  else if (presentation.kind === "error") this.card(label, presentation.message || this.runtime.labels.fileError, true);
52097
52293
  else if (presentation.kind === "image") {
@@ -52114,6 +52310,7 @@ var ImageView = class {
52114
52310
  const pdf = document.createElement("iframe");
52115
52311
  pdf.title = label;
52116
52312
  pdf.setAttribute("sandbox", "");
52313
+ pdf.tabIndex = -1;
52117
52314
  pdf.referrerPolicy = "no-referrer";
52118
52315
  if (width != null) pdf.style.width = `${width}px`;
52119
52316
  pdf.addEventListener("error", () => {
@@ -52124,6 +52321,7 @@ var ImageView = class {
52124
52321
  this.element.append(pdf);
52125
52322
  this.card(label, this.runtime.labels.filePDFPreview);
52126
52323
  } else this.card(label);
52324
+ this.restoreHandleFocus();
52127
52325
  return;
52128
52326
  }
52129
52327
  const presentation = this.adapter?.presentation(String(this.node.attrs.src ?? ""));
@@ -52134,6 +52332,12 @@ var ImageView = class {
52134
52332
  }
52135
52333
  this.element.classList.remove("inkkit-image-absent");
52136
52334
  this.renderImage(presentation.url, alt, width);
52335
+ this.restoreHandleFocus();
52336
+ }
52337
+ restoreHandleFocus() {
52338
+ if (!this.restoreResizeFocus || !this.handle) return;
52339
+ this.restoreResizeFocus = false;
52340
+ if (this.canResize() && document.activeElement === document.body) this.handle.focus({ preventScroll: true });
52137
52341
  }
52138
52342
  renderImage(url, alt, width) {
52139
52343
  const image = document.createElement("img");
@@ -52150,9 +52354,57 @@ var ImageView = class {
52150
52354
  preview.append(image);
52151
52355
  const handle = document.createElement("span");
52152
52356
  handle.className = "inkkit-image-handle";
52357
+ handle.tabIndex = 0;
52358
+ handle.setAttribute("role", "slider");
52359
+ handle.setAttribute("aria-orientation", "horizontal");
52360
+ handle.setAttribute("aria-label", `${this.host?.labels.imageResize ?? defaultLabels.imageResize} ${alt}`.trim());
52361
+ handle.addEventListener("focus", this.updateHandle);
52362
+ handle.addEventListener("keydown", this.resizeKey);
52153
52363
  handle.addEventListener("pointerdown", this.startResize);
52364
+ this.handle = handle;
52154
52365
  this.element.append(preview, handle);
52155
- }
52366
+ this.updateHandle();
52367
+ image.addEventListener("load", this.updateHandle);
52368
+ }
52369
+ updateHandle = () => {
52370
+ if (!this.handle || !this.image) return;
52371
+ const width = Math.round(this.image.getBoundingClientRect().width || this.width || minimumWidth);
52372
+ this.handle.setAttribute("aria-valuemin", String(Math.min(minimumWidth, width)));
52373
+ this.handle.setAttribute("aria-valuemax", String(Math.max(minimumWidth, width, this.view.dom.clientWidth)));
52374
+ this.handle.setAttribute("aria-valuenow", String(width));
52375
+ this.handle.setAttribute("aria-valuetext", `${width}px`);
52376
+ };
52377
+ resizeKey = (event) => {
52378
+ if (event.altKey || event.ctrlKey || event.metaKey || !this.canResize()) return;
52379
+ const image = this.image;
52380
+ if (!image || event.currentTarget !== this.handle || !this.handle.isConnected) return;
52381
+ const limit = Math.max(minimumWidth, this.view.dom.clientWidth);
52382
+ const current = Math.round(image.getBoundingClientRect().width || this.width || minimumWidth);
52383
+ const step = event.shiftKey ? 1 : 10;
52384
+ let width;
52385
+ switch (event.key) {
52386
+ case "ArrowLeft":
52387
+ case "ArrowDown":
52388
+ width = current - step;
52389
+ break;
52390
+ case "ArrowRight":
52391
+ case "ArrowUp":
52392
+ width = current + step;
52393
+ break;
52394
+ case "Home":
52395
+ width = minimumWidth;
52396
+ break;
52397
+ case "End":
52398
+ width = limit;
52399
+ break;
52400
+ default: return;
52401
+ }
52402
+ event.preventDefault();
52403
+ this.width = Math.min(limit, Math.max(minimumWidth, width));
52404
+ this.commit();
52405
+ this.handle?.focus({ preventScroll: true });
52406
+ this.updateHandle();
52407
+ };
52156
52408
  startResize = (event) => {
52157
52409
  event.preventDefault();
52158
52410
  const image = this.image;
@@ -52207,7 +52459,7 @@ var ImageView = class {
52207
52459
  this.view.dispatch(closeHistory(this.view.state.tr));
52208
52460
  }
52209
52461
  canResize() {
52210
- return this.view.editable && !this.view.composing && this.canMutate();
52462
+ return !this.destroyed && this.view.editable && !this.view.composing && (!this.host || this.host.active()) && this.canMutate();
52211
52463
  }
52212
52464
  };
52213
52465
  var svg$1 = "http://www.w3.org/2000/svg";
@@ -52222,7 +52474,8 @@ function openArrow() {
52222
52474
  return icon;
52223
52475
  }
52224
52476
  function imageView(adapter, host, files, canMutate = () => true) {
52225
- const view = (schema) => $view(schema.node, () => (node, view, getPos) => new ImageView(node, view, getPos, adapter, host, files, canMutate));
52477
+ const cache = new FilePresentationCache();
52478
+ const view = (schema) => $view(schema.node, () => (node, view, getPos) => new ImageView(node, view, getPos, adapter, host, files, cache, canMutate));
52226
52479
  return [view(imageSchema), view(imageBlockSchema)];
52227
52480
  }
52228
52481
  //#endregion
@@ -55420,18 +55673,92 @@ function covered(state) {
55420
55673
  const { from, to, empty, visible } = state.selection;
55421
55674
  if (empty || !visible) return [];
55422
55675
  const text = [];
55423
- state.doc.nodesBetween(from, to, (node, pos) => {
55676
+ let index = 0;
55677
+ state.doc.descendants((node, pos) => {
55424
55678
  if (!node.isTextblock) return true;
55425
55679
  const start = Math.max(from, pos + 1);
55426
55680
  const end = Math.min(to, pos + node.nodeSize - 1);
55427
55681
  if (end > start) text.push({
55428
55682
  from: start,
55429
- to: end
55683
+ to: end,
55684
+ start: pos + 1,
55685
+ end: pos + node.nodeSize - 1,
55686
+ index
55430
55687
  });
55688
+ index++;
55431
55689
  return false;
55432
55690
  });
55433
55691
  return text;
55434
55692
  }
55693
+ function blockContents(view) {
55694
+ return Array.from(view.dom.querySelectorAll("p,h1,h2,h3,h4,h5,h6,pre,.inkkit-comment-block")).filter((element) => {
55695
+ const excluded = element.closest(".ProseMirror-widget,[contenteditable=\"false\"]");
55696
+ return !excluded || excluded === view.dom;
55697
+ }).map((element) => element.tagName === "PRE" && !element.classList.contains("inkkit-literal-markdown") ? element.querySelector(":scope > code") ?? element : element);
55698
+ }
55699
+ function matchesBlock(node, content) {
55700
+ if (!content) return false;
55701
+ switch (node.type.name) {
55702
+ case "paragraph": return content.tagName === "P";
55703
+ case "heading": return content.tagName === `H${node.attrs.level}`;
55704
+ case "code_block": return content.tagName === "CODE" && content.parentElement?.tagName === "PRE";
55705
+ case "literal_markdown": return content.classList.contains("inkkit-literal-markdown");
55706
+ case "comment_block": return content.classList.contains("inkkit-comment-block");
55707
+ default: return false;
55708
+ }
55709
+ }
55710
+ function blockRange(view, span, content) {
55711
+ const range = view.dom.ownerDocument.createRange();
55712
+ range.selectNodeContents(content);
55713
+ const children = Array.from(content.childNodes);
55714
+ const decoration = (node) => node instanceof Element && (node.classList.contains("ProseMirror-widget") || node.classList.contains("ProseMirror-trailingBreak"));
55715
+ const first = children.find((node) => !decoration(node));
55716
+ const last = [...children].reverse().find((node) => !decoration(node));
55717
+ if (first) range.setStartBefore(first);
55718
+ if (last) range.setEndAfter(last);
55719
+ if (span.from !== span.start) {
55720
+ const start = view.domAtPos(span.from);
55721
+ range.setStart(start.node, start.offset);
55722
+ }
55723
+ if (span.to !== span.end) {
55724
+ const end = view.domAtPos(span.to);
55725
+ range.setEnd(end.node, end.offset);
55726
+ }
55727
+ return range;
55728
+ }
55729
+ function addBlockRanges(highlight, view, spans, contents) {
55730
+ let first;
55731
+ let last;
55732
+ const flush = () => {
55733
+ if (!first || !last) return;
55734
+ const range = view.dom.ownerDocument.createRange();
55735
+ range.setStart(first.content.firstChild, first.span.from - first.span.start);
55736
+ range.setEnd(last.content.firstChild, last.span.to - last.span.start);
55737
+ highlight.add(range);
55738
+ first = last = void 0;
55739
+ };
55740
+ for (const span of spans) {
55741
+ const content = contents[span.index];
55742
+ if (!(/^(P|H[1-6])$/.test(content.tagName) && content.childNodes.length === 1 && content.firstChild?.nodeType === Node.TEXT_NODE)) {
55743
+ flush();
55744
+ highlight.add(blockRange(view, span, content));
55745
+ continue;
55746
+ }
55747
+ if (last && last.content.nextSibling !== content) flush();
55748
+ first ??= {
55749
+ span,
55750
+ content
55751
+ };
55752
+ last = {
55753
+ span,
55754
+ content
55755
+ };
55756
+ }
55757
+ flush();
55758
+ }
55759
+ function setSelectionHostActive(view, active) {
55760
+ Painter.of(view.dom.ownerDocument).hostActive(view, active);
55761
+ }
55435
55762
  function domRange(view, span) {
55436
55763
  const start = view.domAtPos(span.from);
55437
55764
  const end = view.domAtPos(span.to);
@@ -55473,6 +55800,13 @@ var Painter = class Painter {
55473
55800
  custom = paintable();
55474
55801
  owner = null;
55475
55802
  windowFocused;
55803
+ hostActivation = /* @__PURE__ */ new WeakMap();
55804
+ painted;
55805
+ hostActive(view, active) {
55806
+ if (active === null) this.hostActivation.delete(view);
55807
+ else this.hostActivation.set(view, active);
55808
+ this.sync();
55809
+ }
55476
55810
  static of(doc) {
55477
55811
  let painter = Painter.all.get(doc);
55478
55812
  if (!painter) Painter.all.set(doc, painter = new Painter(doc));
@@ -55534,17 +55868,34 @@ var Painter = class Painter {
55534
55868
  this.win[method]("blur", this.onWindow);
55535
55869
  }
55536
55870
  sync() {
55537
- for (const [view, root] of this.views) root.classList.toggle(activeClass, this.windowFocused && focused(view));
55871
+ for (const [view, root] of this.views) root.classList.toggle(activeClass, (this.hostActivation.get(view) ?? this.windowFocused) && focused(view));
55538
55872
  if (!this.custom) return;
55539
55873
  const { owner } = this;
55874
+ if (owner && this.painted?.owner === owner && this.painted.doc === owner.state.doc && this.painted.selection.eq(owner.state.selection)) return;
55540
55875
  const spans = owner ? covered(owner.state) : [];
55541
55876
  if (!owner || !spans.length) {
55542
55877
  CSS.highlights.delete(name);
55878
+ this.painted = void 0;
55543
55879
  return;
55544
55880
  }
55545
55881
  const highlight = new Highlight();
55546
- for (const span of spans) highlight.add(domRange(owner, span));
55882
+ const contents = blockContents(owner);
55883
+ let blocks = 0;
55884
+ let mapped = true;
55885
+ owner.state.doc.descendants((node) => {
55886
+ if (!node.isTextblock) return true;
55887
+ if (!matchesBlock(node, contents[blocks])) mapped = false;
55888
+ blocks++;
55889
+ return false;
55890
+ });
55891
+ if (mapped && contents.length === blocks) addBlockRanges(highlight, owner, spans, contents);
55892
+ else for (const span of spans) highlight.add(domRange(owner, span));
55547
55893
  CSS.highlights.set(name, highlight);
55894
+ this.painted = {
55895
+ owner,
55896
+ doc: owner.state.doc,
55897
+ selection: owner.state.selection
55898
+ };
55548
55899
  }
55549
55900
  };
55550
55901
  var selectionPlugin = $prose(() => {
@@ -55866,7 +56217,7 @@ var searchKey = new PluginKey("search");
55866
56217
  Returns a plugin that stores a current search query and searched
55867
56218
  range, and highlights matches of the query.
55868
56219
  */
55869
- function search(options = {}) {
56220
+ function search$1(options = {}) {
55870
56221
  return new Plugin({
55871
56222
  key: searchKey,
55872
56223
  state: {
@@ -55915,6 +56266,56 @@ function setSearchState(tr, query, range = null) {
55915
56266
  }
55916
56267
  //#endregion
55917
56268
  //#region src/search.ts
56269
+ function search() {
56270
+ const original = search$1();
56271
+ return new Plugin({
56272
+ ...original.spec,
56273
+ state: {
56274
+ init: original.spec.state.init,
56275
+ apply(tr, current, oldState, state) {
56276
+ if (tr.getMeta(original) || current.range) return original.spec.state.apply.call(this, tr, current, oldState, state);
56277
+ if (!current.query.valid || !tr.docChanged && !tr.selectionSet) return current;
56278
+ let deco = tr.docChanged ? current.deco.map(tr.mapping, tr.doc) : current.deco;
56279
+ let from = tr.doc.content.size, to = 0;
56280
+ if (tr.docChanged) {
56281
+ tr.mapping.maps.forEach((map, index) => map.forEach((_a, _b, start, end) => {
56282
+ const rest = tr.mapping.slice(index + 1);
56283
+ from = Math.min(from, rest.map(start, -1));
56284
+ to = Math.max(to, rest.map(end, 1));
56285
+ }));
56286
+ if (from <= to) {
56287
+ const left = tr.doc.resolve(Math.max(0, from - 1));
56288
+ const right = tr.doc.resolve(Math.min(tr.doc.content.size, to + 1));
56289
+ from = left.depth ? left.before(1) : left.pos;
56290
+ to = right.depth ? right.after(1) : right.pos;
56291
+ deco = deco.remove(deco.find(from, to));
56292
+ const added = [];
56293
+ for (let position = from; position < to;) {
56294
+ const match = current.query.findNext(state, position, to);
56295
+ if (!match) break;
56296
+ added.push(Decoration.inline(match.from, match.to, { class: "ProseMirror-search-match" }));
56297
+ position = Math.max(match.to, position + 1);
56298
+ }
56299
+ deco = deco.add(state.doc, added);
56300
+ }
56301
+ }
56302
+ const previous = {
56303
+ from: tr.mapping.map(oldState.selection.from),
56304
+ to: tr.mapping.map(oldState.selection.to)
56305
+ };
56306
+ for (const [selection, active] of [[previous, false], [state.selection, true]]) {
56307
+ const matches = deco.find(selection.from, selection.to).filter((item) => item.from === selection.from && item.to === selection.to);
56308
+ const replacements = matches.map((item) => Decoration.inline(item.from, item.to, { class: active ? "ProseMirror-active-search-match" : "ProseMirror-search-match" }));
56309
+ deco = deco.remove(matches).add(state.doc, replacements);
56310
+ }
56311
+ return {
56312
+ ...current,
56313
+ deco
56314
+ };
56315
+ }
56316
+ }
56317
+ });
56318
+ }
55918
56319
  function escapedPattern(text) {
55919
56320
  return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
55920
56321
  }
@@ -56076,8 +56477,10 @@ function formattedLocations(doc) {
56076
56477
  return headings;
56077
56478
  }
56078
56479
  function sourceLocations(ctx, source) {
56079
- const processor = ctx.get(remarkCtx);
56080
- const tree = processor.runSync(processor.parse(source), { value: source });
56480
+ const cached = sourceOutlines.get(ctx);
56481
+ if (cached?.source === source) return cached.headings;
56482
+ const preservation = Preservation.read(ctx, source);
56483
+ const tree = preservation.sourceTree;
56081
56484
  const positions = [];
56082
56485
  const bom = source.startsWith("") ? 1 : 0;
56083
56486
  const visit = (node) => {
@@ -56088,13 +56491,19 @@ function sourceLocations(ctx, source) {
56088
56491
  node.children?.forEach(visit);
56089
56492
  };
56090
56493
  visit(tree);
56091
- const parsed = formattedLocations(ctx.get(parserCtx)(source));
56494
+ const parsed = formattedLocations(preservation.baseline);
56092
56495
  if (positions.length !== parsed.length || positions.some((item, index) => item.level !== parsed[index].node.attrs.level)) throw new InkKitError("preservation", "The current Markdown headings cannot be located safely");
56093
- return parsed.map((heading, index) => ({
56496
+ const headings = parsed.map((heading, index) => ({
56094
56497
  ...heading,
56095
56498
  position: positions[index].position
56096
56499
  }));
56500
+ sourceOutlines.set(ctx, {
56501
+ source,
56502
+ headings
56503
+ });
56504
+ return headings;
56097
56505
  }
56506
+ var sourceOutlines = /* @__PURE__ */ new WeakMap();
56098
56507
  function entry(heading, context) {
56099
56508
  return Object.freeze({
56100
56509
  id: `heading:${context.key}:${heading.position}`,
@@ -56139,28 +56548,141 @@ function onTaskMarker(event) {
56139
56548
  const top = rect.top + parseFloat(marker.top);
56140
56549
  return event.clientX >= left && event.clientX <= left + parseFloat(marker.width) && event.clientY >= top && event.clientY <= top + parseFloat(marker.height);
56141
56550
  }
56142
- var taskListPlugin = $prose(() => new Plugin({
56143
- key: new PluginKey("taskToggle"),
56144
- props: { handleDOMEvents: {
56145
- mousedown(view, event) {
56146
- if (!view.editable || event.button !== 0 || !onTaskMarker(event)) return false;
56147
- event.preventDefault();
56148
- return true;
56149
- },
56150
- click(view, event) {
56151
- if (!view.editable || event.button !== 0 || !onTaskMarker(event)) return false;
56152
- const nodePos = view.posAtDOM(event.target, 0) - 1;
56153
- const node = view.state.doc.nodeAt(nodePos);
56154
- if (node?.type.name !== "list_item" || node.attrs.checked == null) return false;
56155
- event.preventDefault();
56156
- view.dispatch(view.state.tr.setNodeMarkup(nodePos, void 0, {
56157
- ...node.attrs,
56158
- checked: !node.attrs.checked
56159
- }));
56160
- return true;
56551
+ function taskListPlugin(run) {
56552
+ return $prose((ctx) => {
56553
+ const controls = /* @__PURE__ */ new Set();
56554
+ function decorations(doc, from = 0, to = doc.content.size, items = /* @__PURE__ */ new Set()) {
56555
+ const widgets = [];
56556
+ doc.nodesBetween(from, to, (node, pos) => {
56557
+ if (node.isTextblock) return false;
56558
+ if (node.type.name !== "list_item") return;
56559
+ items.add(pos + 1);
56560
+ if (node.attrs.checked == null) return;
56561
+ widgets.push(Decoration.widget(pos + 1, (view, getPos) => {
56562
+ const checkbox = document.createElement("span");
56563
+ checkbox.className = "inkkit-task-checkbox";
56564
+ checkbox.contentEditable = "false";
56565
+ checkbox.tabIndex = 0;
56566
+ checkbox.setAttribute("role", "checkbox");
56567
+ const current = () => {
56568
+ const at = getPos();
56569
+ return at == null ? void 0 : view.state.doc.nodeAt(at - 1);
56570
+ };
56571
+ const update = () => {
56572
+ const task = current();
56573
+ checkbox.setAttribute("aria-checked", String(!!task?.attrs.checked));
56574
+ checkbox.setAttribute("aria-readonly", String(!view.editable));
56575
+ checkbox.setAttribute("aria-label", task?.firstChild?.textContent || editorLabels(ctx).taskCheckbox);
56576
+ };
56577
+ controls.add(update);
56578
+ update();
56579
+ const toggle = () => {
56580
+ const at = getPos();
56581
+ const focused = document.activeElement === checkbox;
56582
+ run(() => at != null && toggleTaskAt(view, at - 1));
56583
+ if (focused && at != null) view.nodeDOM(at - 1)?.querySelector(":scope > .inkkit-task-checkbox")?.focus({ preventScroll: true });
56584
+ };
56585
+ checkbox.addEventListener("mousedown", (event) => event.preventDefault());
56586
+ checkbox.addEventListener("click", (event) => {
56587
+ event.preventDefault();
56588
+ toggle();
56589
+ });
56590
+ checkbox.addEventListener("keydown", (event) => {
56591
+ if (event.key !== " " && event.key !== "Enter") return;
56592
+ event.preventDefault();
56593
+ event.stopPropagation();
56594
+ toggle();
56595
+ });
56596
+ checkbox.cleanup = () => controls.delete(update);
56597
+ return checkbox;
56598
+ }, {
56599
+ side: -1,
56600
+ key: "task-checkbox",
56601
+ ignoreSelection: true,
56602
+ stopEvent: () => true,
56603
+ destroy: (node) => node.cleanup()
56604
+ }));
56605
+ });
56606
+ return widgets;
56607
+ }
56608
+ function update(previous, tr) {
56609
+ let mapped = previous.map(tr.mapping, tr.doc);
56610
+ const ranges = [];
56611
+ tr.mapping.maps.forEach((map, index) => {
56612
+ const rest = tr.mapping.slice(index + 1);
56613
+ map.forEach((_, __, from, to) => ranges.push([rest.map(from, -1), rest.map(to, 1)]));
56614
+ });
56615
+ if (ranges.length === 0) {
56616
+ const start = tr.before.content.findDiffStart(tr.doc.content);
56617
+ const end = tr.before.content.findDiffEnd(tr.doc.content);
56618
+ if (start != null && end) ranges.push([start, Math.max(start, end.b)]);
56619
+ }
56620
+ for (const [from, to] of ranges) {
56621
+ const items = /* @__PURE__ */ new Set();
56622
+ const widgets = decorations(tr.doc, from, to, items);
56623
+ const stale = new Set(mapped.find(from, to));
56624
+ for (const pos of items) for (const widget of mapped.find(pos, pos)) stale.add(widget);
56625
+ mapped = mapped.remove([...stale]).add(tr.doc, widgets);
56626
+ }
56627
+ return mapped;
56161
56628
  }
56162
- } }
56163
- }));
56629
+ return new Plugin({
56630
+ key: new PluginKey("taskToggle"),
56631
+ state: {
56632
+ init: (_, state) => DecorationSet.create(state.doc, decorations(state.doc)),
56633
+ apply: (tr, previous) => tr.docChanged ? update(previous, tr) : previous
56634
+ },
56635
+ props: {
56636
+ decorations(state) {
56637
+ return this.getState(state);
56638
+ },
56639
+ handleDOMEvents: {
56640
+ mousedown(view, event) {
56641
+ if (!view.editable || event.button !== 0 || !onTaskMarker(event)) return false;
56642
+ event.preventDefault();
56643
+ return true;
56644
+ },
56645
+ click(view, event) {
56646
+ if (!view.editable || event.button !== 0 || !onTaskMarker(event)) return false;
56647
+ const nodePos = view.posAtDOM(event.target, 0) - 1;
56648
+ event.preventDefault();
56649
+ run(() => toggleTaskAt(view, nodePos));
56650
+ return true;
56651
+ }
56652
+ }
56653
+ },
56654
+ view: (view) => {
56655
+ let editable = view.editable;
56656
+ return { update(view, previousState) {
56657
+ if (previousState.doc === view.state.doc && editable === view.editable) return;
56658
+ editable = view.editable;
56659
+ controls.forEach((update) => update());
56660
+ } };
56661
+ }
56662
+ });
56663
+ });
56664
+ }
56665
+ function toggleTaskAt(view, pos) {
56666
+ const node = view.state.doc.nodeAt(pos);
56667
+ if (node?.type.name !== "list_item" || node.attrs.checked == null) return false;
56668
+ view.dispatch(view.state.tr.setNodeMarkup(pos, void 0, {
56669
+ ...node.attrs,
56670
+ checked: !node.attrs.checked
56671
+ }));
56672
+ return true;
56673
+ }
56674
+ function taskAtCaret(state) {
56675
+ const { $from } = state.selection;
56676
+ for (let depth = $from.depth; depth > 0; depth--) {
56677
+ const node = $from.node(depth);
56678
+ if (node.type.name === "list_item") return node.attrs.checked == null ? void 0 : $from.before(depth);
56679
+ }
56680
+ }
56681
+ function toggleTask(ctx) {
56682
+ const view = ctx.get(editorViewCtx);
56683
+ const pos = taskAtCaret(view.state);
56684
+ return pos != null && toggleTaskAt(view, pos);
56685
+ }
56164
56686
  function listItemsInSelection(view) {
56165
56687
  const { from, to } = view.state.selection;
56166
56688
  const items = [];
@@ -56548,6 +57070,7 @@ var formatCommands = [
56548
57070
  "bulletList",
56549
57071
  "orderedList",
56550
57072
  "taskList",
57073
+ "toggleTask",
56551
57074
  "link"
56552
57075
  ];
56553
57076
  /** Checks a format call's options against its command. */
@@ -56618,6 +57141,7 @@ var shortcutNames = [
56618
57141
  "moveBlockDown",
56619
57142
  "indent",
56620
57143
  "outdent",
57144
+ "openLink",
56621
57145
  ...tableCommands.map(tableShortcut)
56622
57146
  ];
56623
57147
  /** Checks a keymap and drops omitted entries, so a `null` binding keeps its default. */
@@ -57191,7 +57715,7 @@ var InkKitEditor = class InkKitEditor {
57191
57715
  const anchor = view.state.doc.attrs[sourceAttribute];
57192
57716
  const from = anchor?.from ?? this.plain.selectionStart;
57193
57717
  const to = anchor?.to ?? this.plain.selectionEnd;
57194
- if (!this.plainComposing) this.plain.value = markdown;
57718
+ if (!this.plainComposing) this.setSourceValue(markdown);
57195
57719
  if (!this.plainComposing) {
57196
57720
  this.plain.setSelectionRange(Math.min(from, this.plain.value.length), Math.min(to, this.plain.value.length));
57197
57721
  this.revealLiteralSelection();
@@ -57203,6 +57727,7 @@ var InkKitEditor = class InkKitEditor {
57203
57727
  } })
57204
57728
  }));
57205
57729
  keys = () => false;
57730
+ readingKeys = () => false;
57206
57731
  events;
57207
57732
  /** The host's extra link schemes, in lower case. */
57208
57733
  linkSchemes;
@@ -57440,6 +57965,12 @@ var InkKitEditor = class InkKitEditor {
57440
57965
  }
57441
57966
  this.publishCommandState();
57442
57967
  }
57968
+ /** Supplies native host activation for selection presentation. Null restores browser detection. */
57969
+ setHostActive(active) {
57970
+ this.assertAlive();
57971
+ if (active !== null) expectBoolean(active, "active");
57972
+ setSelectionHostActive(this.editor.ctx.get(editorViewCtx), active);
57973
+ }
57443
57974
  /** Replaces the input preferences. A field left out takes its default. Throws `composition`
57444
57975
  * during text composition. */
57445
57976
  setTextInputPreferences(preferences) {
@@ -57458,12 +57989,15 @@ var InkKitEditor = class InkKitEditor {
57458
57989
  }
57459
57990
  applyInputPolicy() {
57460
57991
  const view = this.editor.ctx.get(editorViewCtx);
57461
- const attributes = typeof view.props.attributes === "function" ? view.props.attributes(view.state) : view.props.attributes;
57992
+ const inputAttributes = { ...typeof view.props.attributes === "function" ? view.props.attributes(view.state) : view.props.attributes };
57993
+ delete inputAttributes["aria-readonly"];
57994
+ if (!this.writable) inputAttributes["aria-readonly"] = "true";
57462
57995
  view.setProps({
57463
57996
  editable: () => this.writable && !this.literalSurface,
57464
57997
  attributes: {
57465
- ...attributes,
57998
+ ...inputAttributes,
57466
57999
  role: "textbox",
58000
+ tabindex: "0",
57467
58001
  "aria-multiline": "true",
57468
58002
  "aria-label": this.options.labels?.formattedEditor ?? defaultLabels.formattedEditor
57469
58003
  }
@@ -57513,6 +58047,7 @@ var InkKitEditor = class InkKitEditor {
57513
58047
  bulletList: formatted && (innermostListDepth(textBounds(state).$from) != null || check(wrapInBulletListCommand.key)),
57514
58048
  orderedList: formatted && (innermostListDepth(textBounds(state).$from) != null || check(wrapInOrderedListCommand.key)),
57515
58049
  taskList: formatted && (innermostListDepth(textBounds(state).$from) != null || check(wrapInBulletListCommand.key)),
58050
+ toggleTask: formatted && taskAtCaret(state) != null,
57516
58051
  link: check(toggleLinkCommand.key, {})
57517
58052
  };
57518
58053
  const tables = formatted ? tableAvailability(this.editor.ctx) : Object.fromEntries(tableCommands.map((name) => [name, false]));
@@ -57627,7 +58162,7 @@ var InkKitEditor = class InkKitEditor {
57627
58162
  if (provenance) {
57628
58163
  preservation = this.sourcePreservations.get(provenance);
57629
58164
  if (!preservation) {
57630
- preservation = new Preservation(this.editor.ctx, provenance.text);
58165
+ preservation = Preservation.read(this.editor.ctx, provenance.text);
57631
58166
  this.sourcePreservations.set(provenance, preservation);
57632
58167
  }
57633
58168
  }
@@ -57669,7 +58204,7 @@ var InkKitEditor = class InkKitEditor {
57669
58204
  this.plainSearch = "";
57670
58205
  this.literalMatch = void 0;
57671
58206
  if (mode === "source") {
57672
- this.plain.value = text;
58207
+ this.setSourceValue(text);
57673
58208
  this.plain.setSelectionRange(Math.min(anchor, head), Math.max(anchor, head), anchor > head ? "backward" : "forward");
57674
58209
  } else view.dispatch(view.state.tr.setSelection(TextSelection.between(view.state.doc.resolve(Math.min(anchor, view.state.doc.content.size)), view.state.doc.resolve(Math.min(head, view.state.doc.content.size)))).setMeta("addToHistory", false));
57675
58210
  const suppressed = this.reportingSuppressed;
@@ -57754,10 +58289,27 @@ var InkKitEditor = class InkKitEditor {
57754
58289
  if (this.typedSteps.length > sourceStepLimit) this.typedSteps.shift();
57755
58290
  }
57756
58291
  showTypedSource(text, from, to) {
57757
- this.plain.value = text;
58292
+ this.setSourceValue(text);
57758
58293
  this.plain.setSelectionRange(Math.min(from, this.plain.value.length), Math.min(to, this.plain.value.length));
57759
58294
  this.revealLiteralSelection();
57760
58295
  }
58296
+ setSourceValue(text) {
58297
+ const plain = this.plain;
58298
+ if (text.length < 65536 || !plain.isConnected || this.plainComposing) {
58299
+ plain.value = text;
58300
+ return;
58301
+ }
58302
+ const focused = plain.ownerDocument.activeElement === plain;
58303
+ const { selectionStart, selectionEnd, selectionDirection, scrollTop, scrollLeft } = plain;
58304
+ const marker = plain.ownerDocument.createComment("source");
58305
+ plain.replaceWith(marker);
58306
+ plain.value = text;
58307
+ marker.replaceWith(plain);
58308
+ plain.setSelectionRange(selectionStart, selectionEnd, selectionDirection);
58309
+ if (focused) plain.focus({ preventScroll: true });
58310
+ plain.scrollTop = scrollTop;
58311
+ plain.scrollLeft = scrollLeft;
58312
+ }
57761
58313
  /**
57762
58314
  * Undoes or redoes typed source that the formatted document has not taken
57763
58315
  * in, on the text alone. Returns whether there was a step to take.
@@ -57829,7 +58381,7 @@ var InkKitEditor = class InkKitEditor {
57829
58381
  try {
57830
58382
  if (this.formatType === "txt") parsed = view.state.schema.topNodeType.create(null, view.state.schema.nodes.paragraph.create(null, text ? view.state.schema.text(text) : void 0));
57831
58383
  else {
57832
- const preservation = new Preservation(this.editor.ctx, text);
58384
+ const preservation = Preservation.read(this.editor.ctx, text);
57833
58385
  this.sourcePreservations.set(provenance, preservation);
57834
58386
  parsed = preservation.baseline;
57835
58387
  }
@@ -57993,7 +58545,7 @@ var InkKitEditor = class InkKitEditor {
57993
58545
  const valid = content.firstChild?.isInline ? Fragment.from(schema.nodes.paragraph.create(null, content)) : content;
57994
58546
  let markdown;
57995
58547
  try {
57996
- markdown = selectedSource ? selectedSource.markdown : all ? snapshot.text : completeDiagrams.size ? new Preservation(this.editor.ctx, snapshot.text).serialize(doc.type.create(null, selectionContent(doc, valid))) : selectionMarkdown(this.editor.ctx, doc, valid);
58548
+ markdown = selectedSource ? selectedSource.markdown : all ? snapshot.text : completeDiagrams.size ? Preservation.read(this.editor.ctx, snapshot.text).serialize(doc.type.create(null, selectionContent(doc, valid))) : selectionMarkdown(this.editor.ctx, doc, valid);
57997
58549
  } catch (error) {
57998
58550
  throw coded(error, "preservation");
57999
58551
  }
@@ -58120,10 +58672,19 @@ var InkKitEditor = class InkKitEditor {
58120
58672
  }
58121
58673
  keymapPlugin = $prose(() => new Plugin({
58122
58674
  key: new PluginKey("appKeymap"),
58123
- props: { handleKeyDown: (view, event) => {
58124
- if (event.altKey && event.key === "Enter") return this.forPerson(() => this.navigateFootnote(event.shiftKey ? "reference" : "definition"), true);
58125
- return this.keys(view, event);
58126
- } }
58675
+ props: {
58676
+ handleDOMEvents: { keydown: (view, event) => {
58677
+ if (!this.writable && this.readingKeys(view, event)) {
58678
+ event.preventDefault();
58679
+ return true;
58680
+ }
58681
+ return false;
58682
+ } },
58683
+ handleKeyDown: (view, event) => {
58684
+ if (event.altKey && event.key === "Enter") return this.forPerson(() => this.navigateFootnote(event.shiftKey ? "reference" : "definition"), true);
58685
+ return this.keys(view, event);
58686
+ }
58687
+ }
58127
58688
  }));
58128
58689
  indentPlugin = new Plugin({
58129
58690
  key: new PluginKey("listIndent"),
@@ -58308,7 +58869,10 @@ var InkKitEditor = class InkKitEditor {
58308
58869
  return true;
58309
58870
  }
58310
58871
  }));
58311
- }).use(labelsCtx).use(linkSchemesCtx).use(instance.pasteController.plugin).use(visibleClipboard).use($prose(() => instance.suggestions.plugin())).use(instance.keymapPlugin).use(createDialect(Boolean(options.images || options.files), Boolean(options.wikiLinks), Boolean(options.files))).use(codeControlCtx).use(codeControlsPlugin(() => instance.viewport().rect)).use(tablePlugins).use(tableBlock).use(instance.changePlugin).use(instance.historyInputPlugin).use(history).use(clipboard).use(cursor).use(taskListPlugin).use(undoTypedRule).use(quoteBackspace).use(calloutEnter).use(literalEnter).use(literalReread).use(listItemBackspace).use(nestedItemEnter).use(rejoinLists).use(movedListItems).use(textInputPolicy).use(events.copy ? codeCopyPlugin((text) => events.copy?.(text)) : []).use(placeholderPlugin).use(options.rendering?.codeHighlighting === false ? [] : (await import("./highlight-GlZZ_7st.js")).highlightPlugin).use(options.rendering?.diagramPreview === false || !diagramsAvailable() ? [] : mermaidPreview((error) => instance.report(error))).use(selectionPlugin).use($prose(() => search())).use(instance.policyPlugin).use(options.wikiLinks ? wikiLinkView(options.wikiLinks, {
58872
+ }).use(labelsCtx).use(linkSchemesCtx).use(instance.pasteController.plugin).use(visibleClipboard).use($prose(() => instance.suggestions.plugin())).use(instance.keymapPlugin).use(createDialect(Boolean(options.images || options.files), Boolean(options.wikiLinks), Boolean(options.files))).use(codeControlCtx).use(codeControlsPlugin(() => instance.viewport().rect)).use(tablePlugins).use(tableBlock).use(instance.changePlugin).use(instance.historyInputPlugin).use(history).use(clipboard).use(cursor).use(taskListPlugin((operation) => instance.forPerson(() => instance.facadeCall(() => {
58873
+ instance.assertMutation();
58874
+ return !instance.literalSurface && operation();
58875
+ }), false))).use(undoTypedRule).use(quoteBackspace).use(calloutEnter).use(literalEnter).use(literalReread).use(listItemBackspace).use(nestedItemEnter).use(rejoinLists).use(movedListItems).use(textInputPolicy).use(events.copy ? codeCopyPlugin((text) => events.copy?.(text)) : []).use(placeholderPlugin).use(options.rendering?.codeHighlighting === false ? [] : (await import("./highlight-X_ZzV-a6.js")).highlightPlugin).use(options.rendering?.diagramPreview === false || !diagramsAvailable() ? [] : mermaidPreview((error) => instance.report(error))).use(selectionPlugin).use($prose(() => search())).use(instance.policyPlugin).use(options.wikiLinks ? wikiLinkView(options.wikiLinks, {
58312
58876
  capture: () => instance.adapterCapture(),
58313
58877
  active: () => instance.ready && !instance.destroyed && !instance.literalSurface,
58314
58878
  labels: {
@@ -58358,7 +58922,7 @@ var InkKitEditor = class InkKitEditor {
58358
58922
  instance.plain.addEventListener("input", () => {
58359
58923
  if (!instance.ready || !instance.literalSurface) return;
58360
58924
  if (!instance.writable) {
58361
- instance.plain.value = instance.currentText();
58925
+ instance.setSourceValue(instance.currentText());
58362
58926
  return;
58363
58927
  }
58364
58928
  try {
@@ -58509,7 +59073,7 @@ var InkKitEditor = class InkKitEditor {
58509
59073
  const markdown = input.format === "txt" ? "" : input.text;
58510
59074
  let preservation;
58511
59075
  try {
58512
- preservation = new Preservation(this.editor.ctx, markdown);
59076
+ preservation = Preservation.read(this.editor.ctx, markdown);
58513
59077
  } catch (error) {
58514
59078
  throw new InkKitError("preservation", "The document was not loaded because its Markdown could not be shown without losing part of it", { cause: error });
58515
59079
  }
@@ -58534,7 +59098,7 @@ var InkKitEditor = class InkKitEditor {
58534
59098
  this.plainSearch = "";
58535
59099
  this.literalMatch = void 0;
58536
59100
  this.formatType = input.format;
58537
- this.plain.value = input.text;
59101
+ this.setSourceValue(input.text);
58538
59102
  this.plain.setSelectionRange(this.plain.value.length, this.plain.value.length);
58539
59103
  this.load(markdown, preservation);
58540
59104
  this.originalSource = input.text;
@@ -59070,13 +59634,14 @@ var InkKitEditor = class InkKitEditor {
59070
59634
  return goToNextCell(shiftKey ? -1 : 1)(view.state, view.dispatch);
59071
59635
  }
59072
59636
  if (!this.writable) {
59637
+ if (!this.literalSurface && this.readOnlyKeys(this.editor.ctx.get(editorViewCtx), event)) return true;
59073
59638
  if ([
59074
59639
  "Enter",
59075
59640
  "Tab",
59076
59641
  "Backspace",
59077
59642
  "Delete"
59078
59643
  ].includes(key)) this.assertMutation(generation);
59079
- this.readOnlyKeys(this.editor.ctx.get(editorViewCtx), event);
59644
+ if (this.literalSurface) this.readOnlyKeys(this.editor.ctx.get(editorViewCtx), event);
59080
59645
  }
59081
59646
  if (this.literalSurface) {
59082
59647
  if (this.literalKeys(this.editor.ctx.get(editorViewCtx), event)) return true;
@@ -59103,7 +59668,11 @@ var InkKitEditor = class InkKitEditor {
59103
59668
  focus(expectedGeneration) {
59104
59669
  this.assertReady(expectedGeneration);
59105
59670
  if (this.literalSurface) this.plain.focus();
59106
- else this.editor.ctx.get(editorViewCtx).focus();
59671
+ else {
59672
+ const view = this.editor.ctx.get(editorViewCtx);
59673
+ if (!this.writable) view.dom.focus({ preventScroll: true });
59674
+ view.focus();
59675
+ }
59107
59676
  }
59108
59677
  /** Moves focus off InkKit's text surfaces. Focus anywhere else, such as in a host control or a
59109
59678
  * control InkKit draws, is left alone. */
@@ -59115,7 +59684,7 @@ var InkKitEditor = class InkKitEditor {
59115
59684
  * Resolves wiki links and file embeds again, all of them or those whose `target` is named, after
59116
59685
  * something they point to changed. A file keeps its card until the new answer differs. Changes no
59117
59686
  * document state, so it works in a read-only editor and during composition. Source and TXT modes
59118
- * draw no references, and they resolve afresh when formatted mode returns.
59687
+ * draw no references; named file answers are invalidated and resolved when formatted mode returns.
59119
59688
  */
59120
59689
  refreshReferences(targets, expectedGeneration) {
59121
59690
  this.assertAlive();
@@ -59124,7 +59693,7 @@ var InkKitEditor = class InkKitEditor {
59124
59693
  requestRefresh(this.editor.ctx.get(editorViewCtx).dom, { ...names && { targets: names } });
59125
59694
  }
59126
59695
  /** Replaces every binding. Keys are in ProseMirror's notation. A key given to two shortcuts goes
59127
- * to the later one, in the order history, formatting, block moves, tables, so it lands the same
59696
+ * to the later one, in the order history, link opening, formatting, block moves, tables, so it lands the same
59128
59697
  * way every time. A table binding passes a key it shares with a block move to the move outside
59129
59698
  * a table. List indentation keys apply after all of these, and after the table keys the editor
59130
59699
  * keeps for itself. */
@@ -59152,6 +59721,24 @@ var InkKitEditor = class InkKitEditor {
59152
59721
  return this.forPerson(() => this[name](), true);
59153
59722
  };
59154
59723
  Object.assign(bindings, historyBindings);
59724
+ const openLink = (state) => {
59725
+ if (!this.ready || this.composing || this.literalSurface) return false;
59726
+ const { selection } = state;
59727
+ const node = selection instanceof NodeSelection ? selection.node : selection.$from.nodeAfter;
59728
+ if (node?.type.name === "inkkit_wiki_link" && this.options.wikiLinks) {
59729
+ const captured = this.adapterCapture();
59730
+ try {
59731
+ if (captured.isCurrent()) this.options.wikiLinks.open(wikiReference(node), captured.context);
59732
+ } catch (error) {
59733
+ this.report(coded(error, "file-unavailable"));
59734
+ }
59735
+ } else {
59736
+ const href = [selection.$from.nodeAfter, selection.$from.nodeBefore].flatMap((node) => node?.marks ?? []).find((mark) => mark.type.name === "link")?.attrs.href;
59737
+ if (href && isSafeLinkTarget(href, this.linkSchemes)) this.events.openLink?.(href);
59738
+ }
59739
+ return true;
59740
+ };
59741
+ for (const key of keymap.openLink ?? []) bindings[key] = openLink;
59155
59742
  for (const name of formatShortcuts) for (const key of keymap[name] ?? []) bindings[key] = () => {
59156
59743
  if (!this.ready || !this.commandState().commands.format[name]) return true;
59157
59744
  this.forPerson(() => this.format(name), false);
@@ -59172,10 +59759,11 @@ var InkKitEditor = class InkKitEditor {
59172
59759
  if (!this.ready || !this.commandState().commands.table[command]) return moves[key]?.(state, dispatch, view) ?? false;
59173
59760
  return this.forPerson(() => this.table(command), true);
59174
59761
  };
59175
- this.readOnlyKeys = keydownHandler(Object.fromEntries(Object.keys(bindings).filter((key) => !moves[key]).map((key) => [key, () => {
59762
+ this.readOnlyKeys = keydownHandler(Object.fromEntries(Object.keys(bindings).filter((key) => !moves[key]).map((key) => [key, bindings[key] === openLink ? openLink : () => {
59176
59763
  throw new InkKitError("read-only", "The editor is read-only");
59177
59764
  }])));
59178
59765
  this.keys = keydownHandler(bindings);
59766
+ this.readingKeys = keydownHandler(Object.fromEntries(Object.entries(bindings).filter(([, handler]) => handler === openLink)));
59179
59767
  this.indentKeys = keydownHandler(indents);
59180
59768
  this.literalKeys = keydownHandler(historyBindings);
59181
59769
  }
@@ -59236,7 +59824,7 @@ var InkKitEditor = class InkKitEditor {
59236
59824
  const view = this.editor.ctx.get(editorViewCtx);
59237
59825
  const { doc, tr } = view.state;
59238
59826
  const blocks = this.imageBlocks(images);
59239
- if (doc.childCount === 1 && doc.firstChild.content.size === 0) tr.replaceWith(0, doc.content.size, blocks);
59827
+ if (doc.childCount === 1 && doc.firstChild.type.name === "paragraph" && doc.firstChild.content.size === 0 && this.currentText().trim() === "") tr.replaceWith(0, doc.content.size, blocks);
59240
59828
  else tr.insert(doc.content.size, blocks);
59241
59829
  view.dispatch(closeHistory(tr));
59242
59830
  view.dispatch(closeHistory(view.state.tr).setMeta("addToHistory", false));
@@ -59525,6 +60113,9 @@ var InkKitEditor = class InkKitEditor {
59525
60113
  case "taskList":
59526
60114
  toggleTaskList(this.editor.ctx);
59527
60115
  break;
60116
+ case "toggleTask":
60117
+ toggleTask(this.editor.ctx);
60118
+ break;
59528
60119
  case "link": this.link(checked?.destination);
59529
60120
  }
59530
60121
  this.focus();
@@ -59704,6 +60295,7 @@ var InkKitEditor = class InkKitEditor {
59704
60295
  "pasteAsPlainText",
59705
60296
  "commandState",
59706
60297
  "setEditable",
60298
+ "setHostActive",
59707
60299
  "setTextInputPreferences",
59708
60300
  "setEditingMode",
59709
60301
  "replaceSource",