@aicayzer/inkkit 0.3.2 → 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.
@@ -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"
@@ -52026,6 +52028,9 @@ var ImageView = class {
52026
52028
  /** The card or picture, which a standalone image's block row holds. */
52027
52029
  element;
52028
52030
  image;
52031
+ handle;
52032
+ destroyed = false;
52033
+ restoreResizeFocus = false;
52029
52034
  media;
52030
52035
  pdf;
52031
52036
  width = null;
@@ -52143,6 +52148,7 @@ var ImageView = class {
52143
52148
  this.pdf = void 0;
52144
52149
  }
52145
52150
  destroy() {
52151
+ this.destroyed = true;
52146
52152
  this.view.dom.removeEventListener("inkkit-cancel-resize", this.cancelResize);
52147
52153
  this.view.dom.removeEventListener(mediaPolicyEvent, this.mediaPolicy);
52148
52154
  this.stopResize?.();
@@ -52205,6 +52211,7 @@ var ImageView = class {
52205
52211
  if (!state.selection.eq(selection)) this.view.dispatch(state.tr.setSelection(selection));
52206
52212
  }
52207
52213
  card(label, status, retry = false) {
52214
+ if (this.resolution?.state.status !== "loading" && !this.handle) this.restoreResizeFocus = false;
52208
52215
  const footer = document.createElement("span");
52209
52216
  footer.className = "inkkit-file-footer";
52210
52217
  const name = document.createElement("span");
@@ -52239,6 +52246,7 @@ var ImageView = class {
52239
52246
  this.render();
52240
52247
  }
52241
52248
  render() {
52249
+ if (this.handle && document.activeElement === this.handle) this.restoreResizeFocus = true;
52242
52250
  this.stopResize?.();
52243
52251
  this.stopResize = void 0;
52244
52252
  this.releaseMedia();
@@ -52250,6 +52258,7 @@ var ImageView = class {
52250
52258
  this.width = width;
52251
52259
  this.element.textContent = "";
52252
52260
  this.image = void 0;
52261
+ this.handle = void 0;
52253
52262
  if (this.runtime) {
52254
52263
  this.element.classList.add("inkkit-file");
52255
52264
  const state = this.resolution.state;
@@ -52301,6 +52310,7 @@ var ImageView = class {
52301
52310
  const pdf = document.createElement("iframe");
52302
52311
  pdf.title = label;
52303
52312
  pdf.setAttribute("sandbox", "");
52313
+ pdf.tabIndex = -1;
52304
52314
  pdf.referrerPolicy = "no-referrer";
52305
52315
  if (width != null) pdf.style.width = `${width}px`;
52306
52316
  pdf.addEventListener("error", () => {
@@ -52311,6 +52321,7 @@ var ImageView = class {
52311
52321
  this.element.append(pdf);
52312
52322
  this.card(label, this.runtime.labels.filePDFPreview);
52313
52323
  } else this.card(label);
52324
+ this.restoreHandleFocus();
52314
52325
  return;
52315
52326
  }
52316
52327
  const presentation = this.adapter?.presentation(String(this.node.attrs.src ?? ""));
@@ -52321,6 +52332,12 @@ var ImageView = class {
52321
52332
  }
52322
52333
  this.element.classList.remove("inkkit-image-absent");
52323
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 });
52324
52341
  }
52325
52342
  renderImage(url, alt, width) {
52326
52343
  const image = document.createElement("img");
@@ -52337,9 +52354,57 @@ var ImageView = class {
52337
52354
  preview.append(image);
52338
52355
  const handle = document.createElement("span");
52339
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);
52340
52363
  handle.addEventListener("pointerdown", this.startResize);
52364
+ this.handle = handle;
52341
52365
  this.element.append(preview, handle);
52342
- }
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
+ };
52343
52408
  startResize = (event) => {
52344
52409
  event.preventDefault();
52345
52410
  const image = this.image;
@@ -52394,7 +52459,7 @@ var ImageView = class {
52394
52459
  this.view.dispatch(closeHistory(this.view.state.tr));
52395
52460
  }
52396
52461
  canResize() {
52397
- 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();
52398
52463
  }
52399
52464
  };
52400
52465
  var svg$1 = "http://www.w3.org/2000/svg";
@@ -56483,28 +56548,141 @@ function onTaskMarker(event) {
56483
56548
  const top = rect.top + parseFloat(marker.top);
56484
56549
  return event.clientX >= left && event.clientX <= left + parseFloat(marker.width) && event.clientY >= top && event.clientY <= top + parseFloat(marker.height);
56485
56550
  }
56486
- var taskListPlugin = $prose(() => new Plugin({
56487
- key: new PluginKey("taskToggle"),
56488
- props: { handleDOMEvents: {
56489
- mousedown(view, event) {
56490
- if (!view.editable || event.button !== 0 || !onTaskMarker(event)) return false;
56491
- event.preventDefault();
56492
- return true;
56493
- },
56494
- click(view, event) {
56495
- if (!view.editable || event.button !== 0 || !onTaskMarker(event)) return false;
56496
- const nodePos = view.posAtDOM(event.target, 0) - 1;
56497
- const node = view.state.doc.nodeAt(nodePos);
56498
- if (node?.type.name !== "list_item" || node.attrs.checked == null) return false;
56499
- event.preventDefault();
56500
- view.dispatch(view.state.tr.setNodeMarkup(nodePos, void 0, {
56501
- ...node.attrs,
56502
- checked: !node.attrs.checked
56503
- }));
56504
- 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;
56505
56628
  }
56506
- } }
56507
- }));
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
+ }
56508
56686
  function listItemsInSelection(view) {
56509
56687
  const { from, to } = view.state.selection;
56510
56688
  const items = [];
@@ -56892,6 +57070,7 @@ var formatCommands = [
56892
57070
  "bulletList",
56893
57071
  "orderedList",
56894
57072
  "taskList",
57073
+ "toggleTask",
56895
57074
  "link"
56896
57075
  ];
56897
57076
  /** Checks a format call's options against its command. */
@@ -56962,6 +57141,7 @@ var shortcutNames = [
56962
57141
  "moveBlockDown",
56963
57142
  "indent",
56964
57143
  "outdent",
57144
+ "openLink",
56965
57145
  ...tableCommands.map(tableShortcut)
56966
57146
  ];
56967
57147
  /** Checks a keymap and drops omitted entries, so a `null` binding keeps its default. */
@@ -57547,6 +57727,7 @@ var InkKitEditor = class InkKitEditor {
57547
57727
  } })
57548
57728
  }));
57549
57729
  keys = () => false;
57730
+ readingKeys = () => false;
57550
57731
  events;
57551
57732
  /** The host's extra link schemes, in lower case. */
57552
57733
  linkSchemes;
@@ -57816,6 +57997,7 @@ var InkKitEditor = class InkKitEditor {
57816
57997
  attributes: {
57817
57998
  ...inputAttributes,
57818
57999
  role: "textbox",
58000
+ tabindex: "0",
57819
58001
  "aria-multiline": "true",
57820
58002
  "aria-label": this.options.labels?.formattedEditor ?? defaultLabels.formattedEditor
57821
58003
  }
@@ -57865,6 +58047,7 @@ var InkKitEditor = class InkKitEditor {
57865
58047
  bulletList: formatted && (innermostListDepth(textBounds(state).$from) != null || check(wrapInBulletListCommand.key)),
57866
58048
  orderedList: formatted && (innermostListDepth(textBounds(state).$from) != null || check(wrapInOrderedListCommand.key)),
57867
58049
  taskList: formatted && (innermostListDepth(textBounds(state).$from) != null || check(wrapInBulletListCommand.key)),
58050
+ toggleTask: formatted && taskAtCaret(state) != null,
57868
58051
  link: check(toggleLinkCommand.key, {})
57869
58052
  };
57870
58053
  const tables = formatted ? tableAvailability(this.editor.ctx) : Object.fromEntries(tableCommands.map((name) => [name, false]));
@@ -58489,10 +58672,19 @@ var InkKitEditor = class InkKitEditor {
58489
58672
  }
58490
58673
  keymapPlugin = $prose(() => new Plugin({
58491
58674
  key: new PluginKey("appKeymap"),
58492
- props: { handleKeyDown: (view, event) => {
58493
- if (event.altKey && event.key === "Enter") return this.forPerson(() => this.navigateFootnote(event.shiftKey ? "reference" : "definition"), true);
58494
- return this.keys(view, event);
58495
- } }
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
+ }
58496
58688
  }));
58497
58689
  indentPlugin = new Plugin({
58498
58690
  key: new PluginKey("listIndent"),
@@ -58677,7 +58869,10 @@ var InkKitEditor = class InkKitEditor {
58677
58869
  return true;
58678
58870
  }
58679
58871
  }));
58680
- }).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-LB7Mkd5W.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, {
58681
58876
  capture: () => instance.adapterCapture(),
58682
58877
  active: () => instance.ready && !instance.destroyed && !instance.literalSurface,
58683
58878
  labels: {
@@ -59439,13 +59634,14 @@ var InkKitEditor = class InkKitEditor {
59439
59634
  return goToNextCell(shiftKey ? -1 : 1)(view.state, view.dispatch);
59440
59635
  }
59441
59636
  if (!this.writable) {
59637
+ if (!this.literalSurface && this.readOnlyKeys(this.editor.ctx.get(editorViewCtx), event)) return true;
59442
59638
  if ([
59443
59639
  "Enter",
59444
59640
  "Tab",
59445
59641
  "Backspace",
59446
59642
  "Delete"
59447
59643
  ].includes(key)) this.assertMutation(generation);
59448
- this.readOnlyKeys(this.editor.ctx.get(editorViewCtx), event);
59644
+ if (this.literalSurface) this.readOnlyKeys(this.editor.ctx.get(editorViewCtx), event);
59449
59645
  }
59450
59646
  if (this.literalSurface) {
59451
59647
  if (this.literalKeys(this.editor.ctx.get(editorViewCtx), event)) return true;
@@ -59472,7 +59668,11 @@ var InkKitEditor = class InkKitEditor {
59472
59668
  focus(expectedGeneration) {
59473
59669
  this.assertReady(expectedGeneration);
59474
59670
  if (this.literalSurface) this.plain.focus();
59475
- 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
+ }
59476
59676
  }
59477
59677
  /** Moves focus off InkKit's text surfaces. Focus anywhere else, such as in a host control or a
59478
59678
  * control InkKit draws, is left alone. */
@@ -59493,7 +59693,7 @@ var InkKitEditor = class InkKitEditor {
59493
59693
  requestRefresh(this.editor.ctx.get(editorViewCtx).dom, { ...names && { targets: names } });
59494
59694
  }
59495
59695
  /** Replaces every binding. Keys are in ProseMirror's notation. A key given to two shortcuts goes
59496
- * 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
59497
59697
  * way every time. A table binding passes a key it shares with a block move to the move outside
59498
59698
  * a table. List indentation keys apply after all of these, and after the table keys the editor
59499
59699
  * keeps for itself. */
@@ -59521,6 +59721,24 @@ var InkKitEditor = class InkKitEditor {
59521
59721
  return this.forPerson(() => this[name](), true);
59522
59722
  };
59523
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;
59524
59742
  for (const name of formatShortcuts) for (const key of keymap[name] ?? []) bindings[key] = () => {
59525
59743
  if (!this.ready || !this.commandState().commands.format[name]) return true;
59526
59744
  this.forPerson(() => this.format(name), false);
@@ -59541,10 +59759,11 @@ var InkKitEditor = class InkKitEditor {
59541
59759
  if (!this.ready || !this.commandState().commands.table[command]) return moves[key]?.(state, dispatch, view) ?? false;
59542
59760
  return this.forPerson(() => this.table(command), true);
59543
59761
  };
59544
- 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 : () => {
59545
59763
  throw new InkKitError("read-only", "The editor is read-only");
59546
59764
  }])));
59547
59765
  this.keys = keydownHandler(bindings);
59766
+ this.readingKeys = keydownHandler(Object.fromEntries(Object.entries(bindings).filter(([, handler]) => handler === openLink)));
59548
59767
  this.indentKeys = keydownHandler(indents);
59549
59768
  this.literalKeys = keydownHandler(historyBindings);
59550
59769
  }
@@ -59894,6 +60113,9 @@ var InkKitEditor = class InkKitEditor {
59894
60113
  case "taskList":
59895
60114
  toggleTaskList(this.editor.ctx);
59896
60115
  break;
60116
+ case "toggleTask":
60117
+ toggleTask(this.editor.ctx);
60118
+ break;
59897
60119
  case "link": this.link(checked?.destination);
59898
60120
  }
59899
60121
  this.focus();
@@ -1,4 +1,4 @@
1
- import { a as Decoration, c as PluginKey, d as __toESM, i as $prose, o as DecorationSet, s as Plugin, u as __commonJSMin } from "./core-DTew7k7c.js";
1
+ import { a as Decoration, c as PluginKey, d as __toESM, i as $prose, o as DecorationSet, s as Plugin, u as __commonJSMin } from "./core-BsI6Awlu.js";
2
2
  //#region node_modules/.pnpm/highlight.js@11.11.2/node_modules/highlight.js/es/languages/arduino.js
3
3
  /** @type LanguageFn */
4
4
  function cPlusPlus(hljs) {
package/dist/core.js CHANGED
@@ -1,2 +1,2 @@
1
- import { r as InkKitError, t as InkKitEditor } from "./chunks/core-DTew7k7c.js";
1
+ import { r as InkKitError, t as InkKitEditor } from "./chunks/core-BsI6Awlu.js";
2
2
  export { InkKitEditor, InkKitError };
package/dist/editor.d.ts CHANGED
@@ -27,7 +27,7 @@ export interface CaretState {
27
27
  /** One quote holds the whole selection; the block is what sits inside it. */
28
28
  quoted: boolean;
29
29
  }
30
- declare const formatCommands: readonly ['heading1', 'heading2', 'heading3', 'heading4', 'heading5', 'heading6', 'paragraph', 'bold', 'italic', 'strikethrough', 'highlight', 'code', 'codeBlock', 'quote', 'bulletList', 'orderedList', 'taskList', 'link'];
30
+ declare const formatCommands: readonly ['heading1', 'heading2', 'heading3', 'heading4', 'heading5', 'heading6', 'paragraph', 'bold', 'italic', 'strikethrough', 'highlight', 'code', 'codeBlock', 'quote', 'bulletList', 'orderedList', 'taskList', 'toggleTask', 'link'];
31
31
  /** A command for `format`. */
32
32
  export type FormatCommand = (typeof formatCommands)[number];
33
33
  /** `destination` goes only with `link`, and `language` only with `codeBlock`. */
@@ -50,9 +50,9 @@ export interface ImageInsertion {
50
50
  alt: string;
51
51
  title?: string;
52
52
  }
53
- /** A name in `Keymap`: every format command except `link`, history, block moves, list indentation,
53
+ /** A name in `Keymap`: every format command except `link`, history, link opening, block moves, list indentation,
54
54
  * and every table command. */
55
- export type ShortcutName = Exclude<FormatCommand, 'link'> | 'undo' | 'redo' | 'moveBlockUp' | 'moveBlockDown' | 'indent' | 'outdent' | `table${Capitalize<TableCommand>}`;
55
+ export type ShortcutName = Exclude<FormatCommand, 'link'> | 'undo' | 'redo' | 'moveBlockUp' | 'moveBlockDown' | 'indent' | 'outdent' | 'openLink' | `table${Capitalize<TableCommand>}`;
56
56
  /** Host bindings in ProseMirror key notation, by shortcut name. */
57
57
  export type Keymap = Partial<Record<ShortcutName, readonly string[]>>;
58
58
  /** Every `ShortcutName`. */
@@ -206,6 +206,7 @@ export declare class InkKitEditor {
206
206
  private suggestionBase?;
207
207
  private changePlugin;
208
208
  private keys;
209
+ private readingKeys;
209
210
  private readonly events;
210
211
  /** The host's extra link schemes, in lower case. */
211
212
  private readonly linkSchemes;
@@ -433,7 +434,7 @@ export declare class InkKitEditor {
433
434
  */
434
435
  refreshReferences(targets?: readonly string[], expectedGeneration?: number): void;
435
436
  /** Replaces every binding. Keys are in ProseMirror's notation. A key given to two shortcuts goes
436
- * to the later one, in the order history, formatting, block moves, tables, so it lands the same
437
+ * to the later one, in the order history, link opening, formatting, block moves, tables, so it lands the same
437
438
  * way every time. A table binding passes a key it shares with a block move to the move outside
438
439
  * a table. List indentation keys apply after all of these, and after the table keys the editor
439
440
  * keeps for itself. */
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as registerMermaid, r as InkKitError, t as InkKitEditor } from "./chunks/core-DTew7k7c.js";
1
+ import { n as registerMermaid, r as InkKitError, t as InkKitEditor } from "./chunks/core-BsI6Awlu.js";
2
2
  //#region src/index.ts
3
3
  registerMermaid(async () => (await import("mermaid")).default);
4
4
  //#endregion
package/dist/style.css CHANGED
@@ -14,7 +14,7 @@
14
14
  line-height: var(--inkkit-line-height, normal);
15
15
  -webkit-font-smoothing: antialiased;
16
16
  -webkit-text-size-adjust: none;
17
- --_inkkit-accent: var(--inkkit-accent-color, #0a84ff);
17
+ --_inkkit-accent: var(--inkkit-accent-color, light-dark(#0069d1, #409cff));
18
18
  --_inkkit-text: var(--inkkit-text-color, CanvasText);
19
19
  --_inkkit-muted: var(
20
20
  --inkkit-muted-color,
@@ -347,6 +347,7 @@
347
347
  opacity: 0;
348
348
  }
349
349
  .inkkit-root .ProseMirror .inkkit-image:hover .inkkit-image-handle,
350
+ .inkkit-root .ProseMirror .inkkit-image:focus-within .inkkit-image-handle,
350
351
  .inkkit-root .ProseMirror .inkkit-image-handle:active {
351
352
  opacity: 1;
352
353
  }
@@ -730,7 +731,7 @@
730
731
  border: 1.5px solid
731
732
  var(
732
733
  --inkkit-checkbox-border-color,
733
- color-mix(in srgb, CanvasText 35%, transparent)
734
+ color-mix(in srgb, CanvasText 55%, transparent)
734
735
  );
735
736
  border-radius: 4px;
736
737
  cursor: pointer;
@@ -762,6 +763,33 @@
762
763
  color: var(--_inkkit-muted);
763
764
  }
764
765
 
766
+ /* The semantic control overlays the existing marker without entering its text. */
767
+ .inkkit-root .inkkit-task-checkbox {
768
+ position: absolute;
769
+ left: calc(var(--_inkkit-font-size) * -1.667);
770
+ top: var(--_inkkit-task-top);
771
+ width: var(--_inkkit-task-size);
772
+ height: var(--_inkkit-task-size);
773
+ cursor: pointer;
774
+ border-radius: 4px;
775
+ }
776
+
777
+ .inkkit-root .inkkit-task-checkbox:focus-visible {
778
+ outline: 2px solid var(--inkkit-focus-color, var(--_inkkit-accent));
779
+ outline-offset: 2px;
780
+ }
781
+
782
+ @media (forced-colors: active) {
783
+ .inkkit-root .ProseMirror li[data-item-type='task']::before {
784
+ border-color: CanvasText;
785
+ }
786
+ .inkkit-root
787
+ .ProseMirror
788
+ li[data-item-type='task'][data-checked='true']::after {
789
+ border-color: CanvasText;
790
+ }
791
+ }
792
+
765
793
  .inkkit-root .ProseMirror del {
766
794
  color: var(--_inkkit-muted);
767
795
  }
@@ -986,6 +1014,7 @@
986
1014
 
987
1015
  /* One ring for every control InkKit draws; the editing surfaces can be set apart. */
988
1016
  .inkkit-root button:focus-visible,
1017
+ .inkkit-root .inkkit-image-handle:focus-visible,
989
1018
  .inkkit-root .inkkit-file audio:focus-visible,
990
1019
  .inkkit-root .inkkit-file video:focus-visible {
991
1020
  outline: var(--inkkit-focus-width, 2px) solid
package/dist/types.d.ts CHANGED
@@ -249,6 +249,7 @@ export interface EditorLabels {
249
249
  slashBulletList: string;
250
250
  slashOrderedList: string;
251
251
  slashTaskList: string;
252
+ taskCheckbox: string;
252
253
  slashQuote: string;
253
254
  slashCodeBlock: string;
254
255
  slashTable: string;
@@ -292,6 +293,7 @@ export interface EditorLabels {
292
293
  fileOpen: string;
293
294
  filePreviewUnavailable: string;
294
295
  filePDFPreview: string;
296
+ imageResize: string;
295
297
  wikiMissing: string;
296
298
  /** Name of an author comment, which stays out of the readable text. */
297
299
  comment: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aicayzer/inkkit",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "A preserving Markdown editor for offline web views and the web",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -41,7 +41,7 @@
41
41
  "node": ">=26"
42
42
  },
43
43
  "scripts": {
44
- "build": "node scripts/clean-build.mjs && tsc -p tsconfig.build.json && vite build --config vite.package.config.ts && node scripts/finish-build.mjs",
44
+ "build": "node scripts/clean-build.mjs && tsc -p tsconfig.build.json && vite build --config vite.package.config.ts && node scripts/build-host-notices.ts && node scripts/finish-build.mjs",
45
45
  "typecheck": "tsc --noEmit",
46
46
  "test": "vitest run",
47
47
  "format": "prettier --write .",