@inkandswitch/patchwork-pkg-base 0.0.127 → 0.0.129

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/module-settings-url +1 -1
  2. package/package.json +1 -1
  3. package/packages/account/dist/{AccountPicker-TCOUU4YK.js → AccountPicker-SBCGXHRS.js} +13 -44
  4. package/packages/account/dist/AccountPicker-SBCGXHRS.js.map +7 -0
  5. package/packages/account/dist/{ContactAvatar-MKDWQC6G.js → ContactAvatar-IVYTJ7M4.js} +5 -2
  6. package/packages/account/dist/ContactAvatar-IVYTJ7M4.js.map +7 -0
  7. package/packages/account/dist/{ContactCursor-4FYCATG5.js → ContactCursor-2FVKF5QA.js} +5 -2
  8. package/packages/account/dist/ContactCursor-2FVKF5QA.js.map +7 -0
  9. package/packages/account/dist/chunk-L6JWPAD3.js +52 -0
  10. package/packages/account/dist/chunk-L6JWPAD3.js.map +7 -0
  11. package/packages/account/dist/index.js +3 -3
  12. package/packages/codemirror-base/dist/{chunk-A2WBDY3J.js → chunk-REXVGJ53.js} +27 -2
  13. package/packages/codemirror-base/dist/{chunk-A2WBDY3J.js.map → chunk-REXVGJ53.js.map} +2 -2
  14. package/packages/codemirror-base/dist/lib/codemirror.js +1 -1
  15. package/packages/codemirror-base/dist/tool.js +1 -1
  16. package/packages/codemirror-markdown/dist/extensions/links.js +16 -11
  17. package/packages/codemirror-markdown/dist/extensions/links.js.map +2 -2
  18. package/packages/codemirror-markdown/dist/extensions/markdown.js +404 -63
  19. package/packages/codemirror-markdown/dist/extensions/markdown.js.map +3 -3
  20. package/packages/theming/dist/{active-theme-6H5UANUR.js → active-theme-WA6XMDTP.js} +2 -2
  21. package/packages/theming/dist/{chunk-HHCCYVOT.js → chunk-4GOF67HA.js} +2 -2
  22. package/packages/theming/dist/{chunk-VJZ4NPK4.js → chunk-T7VWPJQG.js} +26 -1
  23. package/packages/theming/dist/chunk-T7VWPJQG.js.map +7 -0
  24. package/packages/theming/dist/main.js +4 -4
  25. package/packages/theming/dist/{theme-HE7ECLIW.css → theme-HV4IDVYM.css} +44 -0
  26. package/packages/theming/dist/{theme-editor-datatype-TJ6LYT2U.js → theme-editor-datatype-DZFQK7HV.js} +2 -2
  27. package/packages/theming/dist/theme-editor.js +1 -1
  28. package/packages/theming/dist/{theme-titlebar-UX4BND5Z.js → theme-titlebar-LG6PFBKR.js} +2 -2
  29. package/packages/theming/dist/{theme-tray-B477G2WR.js → theme-tray-R3PJDYCL.js} +2 -2
  30. package/packages/theming/dist/tool.js +1 -1
  31. package/packages/threepane/dist/IsolatedDocumentArea-JJMLEL4O.js +2 -0
  32. package/packages/threepane/dist/{PatchworkFrame-22ABHJVG.js → PatchworkFrame-HMA5KZLY.js} +2 -2
  33. package/packages/threepane/dist/chunk-UOTRB26X.js +846 -0
  34. package/packages/threepane/dist/chunk-UOTRB26X.js.map +7 -0
  35. package/packages/threepane/dist/index.js +1 -1
  36. package/plugin-registry-url +1 -1
  37. package/packages/account/dist/AccountPicker-TCOUU4YK.js.map +0 -7
  38. package/packages/account/dist/ContactAvatar-MKDWQC6G.js.map +0 -7
  39. package/packages/account/dist/ContactCursor-4FYCATG5.js.map +0 -7
  40. package/packages/theming/dist/chunk-VJZ4NPK4.js.map +0 -7
  41. package/packages/threepane/dist/IsolatedDocumentArea-3HGASUGM.js +0 -2
  42. package/packages/threepane/dist/chunk-34WWZW2V.js +0 -1215
  43. package/packages/threepane/dist/chunk-34WWZW2V.js.map +0 -7
  44. /package/packages/theming/dist/{active-theme-6H5UANUR.js.map → active-theme-WA6XMDTP.js.map} +0 -0
  45. /package/packages/theming/dist/{chunk-HHCCYVOT.js.map → chunk-4GOF67HA.js.map} +0 -0
  46. /package/packages/theming/dist/{theme-editor-datatype-TJ6LYT2U.js.map → theme-editor-datatype-DZFQK7HV.js.map} +0 -0
  47. /package/packages/theming/dist/{theme-titlebar-UX4BND5Z.js.map → theme-titlebar-LG6PFBKR.js.map} +0 -0
  48. /package/packages/theming/dist/{theme-tray-B477G2WR.js.map → theme-tray-R3PJDYCL.js.map} +0 -0
  49. /package/packages/threepane/dist/{IsolatedDocumentArea-3HGASUGM.js.map → IsolatedDocumentArea-JJMLEL4O.js.map} +0 -0
  50. /package/packages/threepane/dist/{PatchworkFrame-22ABHJVG.js.map → PatchworkFrame-HMA5KZLY.js.map} +0 -0
@@ -2066,7 +2066,7 @@ var searchExtensions = [
2066
2066
  ];
2067
2067
 
2068
2068
  // src/extensions/markdown.ts
2069
- import { EditorView as EditorView9, keymap as keymap5 } from "@codemirror/view";
2069
+ import { EditorView as EditorView9, keymap as keymap6 } from "@codemirror/view";
2070
2070
 
2071
2071
  // src/extensions/formatting.ts
2072
2072
  import { EditorSelection as EditorSelection2 } from "@codemirror/state";
@@ -2167,6 +2167,7 @@ var TaskWidget = class extends WidgetType {
2167
2167
  box.disabled = !view.state.facet(EditorView2.editable);
2168
2168
  box.setAttribute("aria-label", this.checked ? "Done" : "To do");
2169
2169
  box.addEventListener("mousedown", (event) => {
2170
+ if (event.button !== 0) return;
2170
2171
  event.preventDefault();
2171
2172
  if (box.disabled) return;
2172
2173
  toggle(view, view.posAtDOM(box));
@@ -2235,18 +2236,23 @@ import {
2235
2236
  WidgetType as WidgetType2
2236
2237
  } from "@codemirror/view";
2237
2238
  var hidden = Decoration3.replace({});
2238
- var taskMark = Decoration3.mark({ class: "cm-md-hang", inclusive: true });
2239
+ var taskMark = Decoration3.mark({
2240
+ class: "cm-md-hang cm-md-list-marker",
2241
+ inclusive: true
2242
+ });
2239
2243
  var HeadingLabel = class extends WidgetType2 {
2240
- constructor(level) {
2244
+ /** `shown` only on the caret's line; elsewhere it keeps its room, unseen. */
2245
+ constructor(level, shown) {
2241
2246
  super();
2242
2247
  this.level = level;
2248
+ this.shown = shown;
2243
2249
  }
2244
2250
  eq(other) {
2245
- return other.level === this.level;
2251
+ return other.level === this.level && other.shown === this.shown;
2246
2252
  }
2247
2253
  toDOM() {
2248
2254
  const span = document.createElement("span");
2249
- span.className = "cm-md-hang cm-md-heading-label";
2255
+ span.className = `cm-md-hang cm-md-heading-label${this.shown ? "" : " cm-md-heading-label-idle"}`;
2250
2256
  span.setAttribute("aria-hidden", "true");
2251
2257
  const n = document.createElement("span");
2252
2258
  n.className = "cm-md-heading-level";
@@ -2261,7 +2267,7 @@ var Bullet = class extends WidgetType2 {
2261
2267
  }
2262
2268
  toDOM() {
2263
2269
  const span = document.createElement("span");
2264
- span.className = "cm-md-hang";
2270
+ span.className = "cm-md-hang cm-md-list-marker";
2265
2271
  const dot = document.createElement("span");
2266
2272
  dot.className = "cm-bullet";
2267
2273
  span.append(dot);
@@ -2278,7 +2284,7 @@ var NumberLabel = class extends WidgetType2 {
2278
2284
  }
2279
2285
  toDOM() {
2280
2286
  const span = document.createElement("span");
2281
- span.className = "cm-md-hang cm-md-hang-number";
2287
+ span.className = "cm-md-hang cm-md-list-marker cm-md-hang-number";
2282
2288
  span.textContent = this.text;
2283
2289
  return span;
2284
2290
  }
@@ -2297,7 +2303,129 @@ function renderedNumber(state, mark) {
2297
2303
  }
2298
2304
  const firstMark = first.getChild("ListMark");
2299
2305
  const start = firstMark ? parseInt(state.doc.sliceString(firstMark.from, firstMark.to), 10) : 1;
2300
- return `${(Number.isNaN(start) ? 1 : start) + index}${delimiter}`;
2306
+ const n = (Number.isNaN(start) ? 1 : start) + index;
2307
+ let depth = 0;
2308
+ for (let p = item.parent?.parent; p; p = p.parent)
2309
+ if (p.name === "OrderedList") depth++;
2310
+ return `${counter(n, depth)}${delimiter}`;
2311
+ }
2312
+ function counter(n, depth) {
2313
+ const style = depth % 3;
2314
+ if (style === 1 && n > 0 && n < 4e3) return roman(n);
2315
+ if (style === 2 && n > 0) return alpha(n);
2316
+ return String(n);
2317
+ }
2318
+ function roman(n) {
2319
+ const numerals = [
2320
+ [1e3, "m"],
2321
+ [900, "cm"],
2322
+ [500, "d"],
2323
+ [400, "cd"],
2324
+ [100, "c"],
2325
+ [90, "xc"],
2326
+ [50, "l"],
2327
+ [40, "xl"],
2328
+ [10, "x"],
2329
+ [9, "ix"],
2330
+ [5, "v"],
2331
+ [4, "iv"],
2332
+ [1, "i"]
2333
+ ];
2334
+ let out = "";
2335
+ for (const [value, numeral] of numerals)
2336
+ while (n >= value) {
2337
+ out += numeral;
2338
+ n -= value;
2339
+ }
2340
+ return out;
2341
+ }
2342
+ function alpha(n) {
2343
+ let out = "";
2344
+ while (n > 0) {
2345
+ n--;
2346
+ out = String.fromCharCode(97 + n % 26) + out;
2347
+ n = Math.floor(n / 26);
2348
+ }
2349
+ return out;
2350
+ }
2351
+ var codeLine = Decoration3.line({ class: "cm-md-code" });
2352
+ var calloutMarker = Decoration3.mark({ class: "cm-md-callout-marker" });
2353
+ function caretLines(state) {
2354
+ const lines = /* @__PURE__ */ new Set();
2355
+ for (const r of state.selection.ranges) {
2356
+ const last = state.doc.lineAt(r.to).number;
2357
+ for (let n = state.doc.lineAt(r.from).number; n <= last; n++) lines.add(n);
2358
+ }
2359
+ return lines;
2360
+ }
2361
+ var CALLOUTS = {
2362
+ note: "Note",
2363
+ tip: "Tip",
2364
+ important: "Important",
2365
+ warning: "Warning",
2366
+ caution: "Caution"
2367
+ };
2368
+ function calloutAt(line) {
2369
+ const m = /^((?:\s*>\s?)+)\[!(\w+)\][ \t]*(.*)$/.exec(line.text);
2370
+ if (!m) return null;
2371
+ const type = m[2].toLowerCase();
2372
+ if (!(type in CALLOUTS)) return null;
2373
+ const from = line.from + m[1].length;
2374
+ return {
2375
+ type,
2376
+ from,
2377
+ to: line.from + line.text.length - m[3].length,
2378
+ titled: m[3].trim() !== ""
2379
+ };
2380
+ }
2381
+ var ICONS = {
2382
+ note: '<circle cx="8" cy="8" r="6.25"/><path d="M8 7.25v4M8 4.75v.01"/>',
2383
+ tip: '<path d="M5.75 11.5h4.5M6.5 14h3M8 1.75a4.25 4.25 0 0 0-2.5 7.7c.5.4.75.9.75 1.55V11.5h3.5V11c0-.65.25-1.15.75-1.55A4.25 4.25 0 0 0 8 1.75z"/>',
2384
+ important: '<path d="M2.25 3.25a1.5 1.5 0 0 1 1.5-1.5h8.5a1.5 1.5 0 0 1 1.5 1.5v6.5a1.5 1.5 0 0 1-1.5 1.5H7l-3 2.5v-2.5h-.25a1.5 1.5 0 0 1-1.5-1.5z"/><path d="M8 4.5v3M8 9.5v.01"/>',
2385
+ warning: '<path d="M7.13 2.25a1 1 0 0 1 1.74 0l5.6 9.75a1 1 0 0 1-.87 1.5H2.4a1 1 0 0 1-.87-1.5z"/><path d="M8 6v3M8 11v.01"/>',
2386
+ caution: '<path d="M5.5 1.75h5l3.75 3.75v5l-3.75 3.75h-5L1.75 10.5v-5z"/><path d="M8 5v3.5M8 10.75v.01"/>'
2387
+ };
2388
+ var CalloutTitle = class extends WidgetType2 {
2389
+ constructor(type, label) {
2390
+ super();
2391
+ this.type = type;
2392
+ this.label = label;
2393
+ }
2394
+ eq(other) {
2395
+ return other.type === this.type && other.label === this.label;
2396
+ }
2397
+ toDOM() {
2398
+ const span = document.createElement("span");
2399
+ span.className = "cm-md-callout-title";
2400
+ span.innerHTML = `<svg viewBox="0 0 16 16" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">${ICONS[this.type]}</svg>`;
2401
+ if (this.label) span.append(CALLOUTS[this.type]);
2402
+ else span.setAttribute("aria-label", CALLOUTS[this.type]);
2403
+ return span;
2404
+ }
2405
+ };
2406
+ function listLines(state, from, to, decos) {
2407
+ const { doc: doc2 } = state;
2408
+ const tree = syntaxTree2(state);
2409
+ const last = doc2.lineAt(to).number;
2410
+ for (let n = doc2.lineAt(from).number; n <= last; n++) {
2411
+ const line = doc2.line(n);
2412
+ const indent = /^[ \t]*/.exec(line.text)[0].length;
2413
+ if (indent === line.length) continue;
2414
+ let depth = 0;
2415
+ let code = false;
2416
+ for (let node = tree.resolveInner(line.from + indent, 1); node; node = node.parent) {
2417
+ if (node.name === "ListItem") depth++;
2418
+ if (node.name === "FencedCode" || node.name === "CodeBlock") code = true;
2419
+ }
2420
+ if (!depth) continue;
2421
+ decos.push(
2422
+ Decoration3.line({
2423
+ class: "cm-md-list-line",
2424
+ attributes: { style: `--md-list-depth: ${depth}` }
2425
+ }).range(line.from)
2426
+ );
2427
+ if (indent && !code) decos.push(hidden.range(line.from, line.from + indent));
2428
+ }
2301
2429
  }
2302
2430
  function skipSpaces(state, pos) {
2303
2431
  const line = state.doc.lineAt(pos);
@@ -2307,6 +2435,7 @@ function skipSpaces(state, pos) {
2307
2435
  function build2(view) {
2308
2436
  const { state } = view;
2309
2437
  const { doc: doc2 } = state;
2438
+ const editing = caretLines(state);
2310
2439
  const decos = [];
2311
2440
  for (const { from, to } of view.visibleRanges) {
2312
2441
  syntaxTree2(state).iterate({
@@ -2318,10 +2447,14 @@ function build2(view) {
2318
2447
  if (heading) {
2319
2448
  const mark = node.node.firstChild;
2320
2449
  if (!mark || mark.name !== "HeaderMark") return;
2450
+ const line = doc2.lineAt(node.from);
2321
2451
  decos.push(
2322
2452
  Decoration3.replace({
2323
- widget: new HeadingLabel(Number(heading[1]))
2324
- }).range(doc2.lineAt(node.from).from, skipSpaces(state, mark.to))
2453
+ widget: new HeadingLabel(Number(heading[1]), editing.has(line.number))
2454
+ }).range(line.from, skipSpaces(state, mark.to)),
2455
+ // the heading's size on the whole line, for what the highlighter
2456
+ // doesn't wrap (a rendered link): see headingLines in the theme.
2457
+ Decoration3.line({ class: `cm-md-h${heading[1]}` }).range(line.from)
2325
2458
  );
2326
2459
  return;
2327
2460
  }
@@ -2333,7 +2466,9 @@ function build2(view) {
2333
2466
  const isTask = bullet && task?.name === "TaskMarker" && doc2.sliceString(end, task.from).trim() === "";
2334
2467
  if (isTask && task) {
2335
2468
  if (end > node.from) decos.push(hidden.range(node.from, end));
2336
- decos.push(taskMark.range(task.from, skipSpaces(state, task.to)));
2469
+ decos.push(taskMark.range(task.from, task.to));
2470
+ const after = skipSpaces(state, task.to);
2471
+ if (after > task.to) decos.push(hidden.range(task.to, after));
2337
2472
  return;
2338
2473
  }
2339
2474
  if (bullet) {
@@ -2349,20 +2484,43 @@ function build2(view) {
2349
2484
  }
2350
2485
  return;
2351
2486
  }
2487
+ if ((name === "BulletList" || name === "OrderedList") && node.node.parent?.name !== "ListItem") {
2488
+ listLines(state, node.from, node.to, decos);
2489
+ return;
2490
+ }
2491
+ if (name === "FencedCode" || name === "CodeBlock") {
2492
+ const first = doc2.lineAt(node.from).number;
2493
+ const last = doc2.lineAt(node.to).number;
2494
+ for (let n = first; n <= last; n++)
2495
+ decos.push(codeLine.range(doc2.line(n).from));
2496
+ return false;
2497
+ }
2352
2498
  if (name === "Blockquote") {
2353
2499
  for (let p = node.node.parent; p; p = p.parent)
2354
2500
  if (p.name === "Blockquote") return;
2355
2501
  const first = doc2.lineAt(node.from).number;
2356
2502
  const last = doc2.lineAt(node.to).number;
2503
+ const callout = calloutAt(doc2.line(first));
2357
2504
  for (let n = first; n <= last; n++) {
2358
2505
  const line = doc2.line(n);
2359
2506
  const classes = ["cm-md-quote"];
2360
2507
  if (n === first) classes.push("cm-md-quote-first");
2361
2508
  if (n === last) classes.push("cm-md-quote-last");
2509
+ if (callout) {
2510
+ classes.push("cm-md-callout", `cm-md-callout-${callout.type}`);
2511
+ if (n === first) classes.push("cm-md-callout-title-line");
2512
+ }
2362
2513
  decos.push(
2363
2514
  Decoration3.line({ class: classes.join(" ") }).range(line.from)
2364
2515
  );
2365
2516
  }
2517
+ if (callout) {
2518
+ decos.push(
2519
+ editing.has(first) ? calloutMarker.range(callout.from, callout.to) : Decoration3.replace({
2520
+ widget: new CalloutTitle(callout.type, !callout.titled)
2521
+ }).range(callout.from, callout.to)
2522
+ );
2523
+ }
2366
2524
  return;
2367
2525
  }
2368
2526
  if (name === "QuoteMark") {
@@ -2390,7 +2548,7 @@ var marginPlugin = ViewPlugin3.fromClass(
2390
2548
  this.decorations = build2(view);
2391
2549
  }
2392
2550
  update(update) {
2393
- if (update.docChanged || update.viewportChanged || syntaxTree2(update.startState) !== syntaxTree2(update.state)) {
2551
+ if (update.docChanged || update.selectionSet || update.viewportChanged || syntaxTree2(update.startState) !== syntaxTree2(update.state)) {
2394
2552
  this.decorations = build2(update.view);
2395
2553
  }
2396
2554
  }
@@ -2400,7 +2558,9 @@ var marginPlugin = ViewPlugin3.fromClass(
2400
2558
  var zoomed = (length) => `calc(${length} * var(--markdown-zoom, 1))`;
2401
2559
  var marginTheme = EditorView3.baseTheme({
2402
2560
  ".cm-content": {
2403
- "--markdown-hang": zoomed("2.5rem")
2561
+ "--markdown-hang": zoomed("2.5rem"),
2562
+ "--markdown-list-indent": zoomed("1.6rem"),
2563
+ "--markdown-quote-indent": zoomed("1.1rem")
2404
2564
  },
2405
2565
  ".cm-md-hang": {
2406
2566
  display: "inline-flex",
@@ -2433,30 +2593,137 @@ var marginTheme = EditorView3.baseTheme({
2433
2593
  fontFamily: "var(--markdown-body-family, inherit)",
2434
2594
  letterSpacing: "0.02em"
2435
2595
  },
2596
+ ".cm-md-heading-label-idle": {
2597
+ visibility: "hidden"
2598
+ },
2599
+ // a list line: padded one indent per level (CodeMirror's own 6px first)
2600
+ ".cm-md-list-line": {
2601
+ paddingLeft: "calc(6px + var(--md-list-depth, 1) * var(--markdown-list-indent))"
2602
+ },
2603
+ // its marker, in that padding: from the start of the indent, not hung right
2604
+ ".cm-md-list-marker": {
2605
+ width: "var(--markdown-list-indent)",
2606
+ marginLeft: "calc(-1 * var(--markdown-list-indent))",
2607
+ justifyContent: "flex-start",
2608
+ paddingRight: "0"
2609
+ },
2436
2610
  ".cm-md-heading-level": {
2437
2611
  fontSize: "0.75em",
2438
2612
  verticalAlign: "-0.15em"
2439
2613
  },
2440
2614
  ".cm-md-hang .cm-bullet": {
2441
2615
  display: "inline-block",
2442
- width: "0.4em",
2443
- height: "0.4em",
2616
+ width: "0.5em",
2617
+ height: "0.5em",
2618
+ // in the box's left, centred under where a number's first digit sits
2619
+ marginLeft: "0.15em",
2444
2620
  borderRadius: "50%",
2445
- background: "var(--syntax-list-marker, currentColor)",
2446
- color: "var(--text-editor-line)",
2621
+ // theming's list marker colour (the primary accent, as ink)
2622
+ background: "var(--syntax-list-marker, var(--text-editor-primary-text, currentColor))",
2447
2623
  alignSelf: "center"
2448
2624
  },
2449
2625
  ".cm-md-hang .cm-task": {
2450
2626
  margin: "0"
2451
2627
  },
2628
+ // a quote: its bar on the body's column, its text one quote indent in
2452
2629
  ".cm-md-quote": {
2453
- position: "relative"
2630
+ position: "relative",
2631
+ paddingLeft: "calc(6px + var(--markdown-quote-indent))"
2632
+ },
2633
+ ".cm-md-code": {
2634
+ fontFamily: "var(--text-editor-family-code)"
2635
+ },
2636
+ // GitHub-style callouts. Colours from theming's --studio-callout-* (the
2637
+ // accent: bar) and --text-editor-callout-*-text/-fill (title ink, box
2638
+ // tint), with fallbacks for running without the theme.
2639
+ ...Object.fromEntries(
2640
+ Object.keys(CALLOUTS).map((type) => {
2641
+ const fallback = { note: "#36e", tip: "#1a7f37", important: "#8250df", warning: "#d4a72c", caution: "#d1242f" }[type];
2642
+ return [
2643
+ `.cm-md-callout-${type}`,
2644
+ {
2645
+ "--md-callout": `var(--studio-callout-${type}, ${fallback})`,
2646
+ "--md-callout-text": `var(--text-editor-callout-${type}-text, color-mix(in oklch, var(--md-callout), currentColor 35%))`,
2647
+ "--md-callout-fill": `var(--text-editor-callout-${type}-fill, color-mix(in oklch, var(--md-callout) 9%, transparent))`
2648
+ }
2649
+ ];
2650
+ })
2651
+ ),
2652
+ ".cm-md-callout": {
2653
+ // the body reads as body text, not as a quote: the theme's quote styling
2654
+ // (italic, the punctuation grey) comes through these variables.
2655
+ "--syntax-quote": "var(--text-editor-line)",
2656
+ "--syntax-style-quote": "normal",
2657
+ "--syntax-weight-quote": "normal",
2658
+ "--syntax-fill-quote": "transparent",
2659
+ // a stacking context, so the tint drawn behind the text (z-index -1)
2660
+ // stays above the editor's own background.
2661
+ isolation: "isolate",
2662
+ paddingRight: zoomed("0.75rem")
2663
+ },
2664
+ // (doubled up to outrank the plain quote bar's rule further down)
2665
+ ".cm-md-quote.cm-md-callout::before": {
2666
+ background: "var(--md-callout)",
2667
+ opacity: "1"
2668
+ },
2669
+ ".cm-md-callout::after": {
2670
+ content: '""',
2671
+ position: "absolute",
2672
+ zIndex: "-1",
2673
+ left: "6px",
2674
+ right: "0",
2675
+ top: "0",
2676
+ bottom: "0",
2677
+ background: "var(--md-callout-fill)"
2678
+ },
2679
+ ".cm-md-callout.cm-md-quote-first": {
2680
+ paddingTop: zoomed("0.5rem")
2681
+ },
2682
+ ".cm-md-callout.cm-md-quote-last": {
2683
+ paddingBottom: zoomed("0.5rem")
2684
+ },
2685
+ ".cm-md-callout.cm-md-quote-first::before, .cm-md-callout.cm-md-quote-first::after": {
2686
+ top: "0",
2687
+ borderTopRightRadius: "6px"
2688
+ },
2689
+ ".cm-md-callout.cm-md-quote-last::before, .cm-md-callout.cm-md-quote-last::after": {
2690
+ bottom: "0",
2691
+ borderBottomRightRadius: "6px"
2692
+ },
2693
+ ".cm-md-callout.cm-md-quote-first::before": { borderTopRightRadius: "0" },
2694
+ ".cm-md-callout.cm-md-quote-last::before": { borderBottomRightRadius: "0" },
2695
+ ".cm-md-callout-title-line": {
2696
+ color: "var(--md-callout-text)",
2697
+ fontWeight: "600"
2698
+ },
2699
+ ".cm-md-callout-title": {
2700
+ display: "inline-flex",
2701
+ alignItems: "center",
2702
+ gap: "0.4em",
2703
+ color: "var(--md-callout-text)",
2704
+ fontWeight: "600",
2705
+ marginRight: "0.4em"
2706
+ },
2707
+ ".cm-md-callout-title svg": {
2708
+ width: "1em",
2709
+ height: "1em",
2710
+ flex: "none"
2711
+ },
2712
+ // the raw `[!NOTE]`, on the caret's line. it parses as a reference link,
2713
+ // so the highlighter's link colour and underline are undone inside it.
2714
+ ".cm-md-callout-marker, .cm-md-callout-marker *": {
2715
+ color: "var(--md-callout-text)",
2716
+ textDecoration: "none"
2717
+ },
2718
+ ".cm-md-callout-marker": {
2719
+ fontFamily: "var(--text-editor-family-code)",
2720
+ fontSize: "0.9em"
2454
2721
  },
2455
2722
  ".cm-md-quote::before": {
2456
2723
  content: '""',
2457
2724
  position: "absolute",
2458
- // centred in the left half of the margin, clear of a hung `>`.
2459
- left: "calc(-0.5 * var(--markdown-hang))",
2725
+ // on the body's column (past CodeMirror's own 6px line padding)
2726
+ left: "6px",
2460
2727
  top: "0",
2461
2728
  bottom: "0",
2462
2729
  width: "3px",
@@ -2566,8 +2833,10 @@ var headings = [
2566
2833
  ];
2567
2834
 
2568
2835
  // src/extensions/lists.ts
2569
- import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2836
+ import { ensureSyntaxTree, syntaxTree as syntaxTree4 } from "@codemirror/language";
2837
+ import { indentMore } from "@codemirror/commands";
2570
2838
  import {
2839
+ Prec as Prec3,
2571
2840
  StateEffect as StateEffect2,
2572
2841
  StateField as StateField2
2573
2842
  } from "@codemirror/state";
@@ -2575,7 +2844,8 @@ import {
2575
2844
  Decoration as Decoration4,
2576
2845
  EditorView as EditorView4,
2577
2846
  ViewPlugin as ViewPlugin4,
2578
- WidgetType as WidgetType3
2847
+ WidgetType as WidgetType3,
2848
+ keymap as keymap2
2579
2849
  } from "@codemirror/view";
2580
2850
  function listAt(state, pos) {
2581
2851
  for (let n = syntaxTree4(state).resolveInner(pos, 1); n; n = n.parent) {
@@ -2616,7 +2886,8 @@ function renumber(state, list) {
2616
2886
  const all = items(list);
2617
2887
  const firstMark = all[0]?.getChild("ListMark");
2618
2888
  if (!firstMark) return changes;
2619
- const start = parseInt(state.doc.sliceString(firstMark.from, firstMark.to), 10) || 0;
2889
+ const nested = list.parent?.name === "ListItem";
2890
+ const start = nested ? 1 : parseInt(state.doc.sliceString(firstMark.from, firstMark.to), 10) || 0;
2620
2891
  all.forEach((item, i) => {
2621
2892
  const mark = item.getChild("ListMark");
2622
2893
  if (!mark) return;
@@ -2640,9 +2911,6 @@ function renumber(state, list) {
2640
2911
  });
2641
2912
  return changes;
2642
2913
  }
2643
- function needsRenumber(state, list) {
2644
- return list.name === "OrderedList" && renumber(state, list).length > 0;
2645
- }
2646
2914
  function clearDone(state, list) {
2647
2915
  const all = items(list);
2648
2916
  const gone = new Set(all.flatMap((it, i) => done(state, it) ? [i] : []));
@@ -2664,6 +2932,31 @@ function moveDoneDown(state, list) {
2664
2932
  ];
2665
2933
  }
2666
2934
  var countDone = (state, list) => items(list).filter((it) => done(state, it)).length;
2935
+ var textStart = (line) => line.from + /^\s*/.exec(line.text)[0].length;
2936
+ var tabInOrderedList = (view) => {
2937
+ const before = view.state;
2938
+ const line = before.doc.lineAt(before.selection.main.head);
2939
+ const was = listAt(before, textStart(line));
2940
+ if (!was || was.name !== "OrderedList" || before.selection.ranges.length !== 1)
2941
+ return false;
2942
+ if (!indentMore(view)) return false;
2943
+ const { state } = view;
2944
+ const now = state.doc.lineAt(state.selection.main.head);
2945
+ ensureSyntaxTree(state, now.to, 100);
2946
+ const list = listAt(state, textStart(now));
2947
+ const first = list && items(list)[0];
2948
+ const mark = first?.getChild("ListMark");
2949
+ if (!list || !mark || list.name !== "OrderedList" || list.parent?.name !== "ListItem" || state.doc.lineAt(first.from).number !== now.number)
2950
+ return true;
2951
+ const text = state.doc.sliceString(mark.from, mark.to);
2952
+ const want = `1${text.slice(-1)}`;
2953
+ if (text !== want)
2954
+ view.dispatch({
2955
+ changes: { from: mark.from, to: mark.to, insert: want },
2956
+ userEvent: "input.renumber"
2957
+ });
2958
+ return true;
2959
+ };
2667
2960
  var setHideDone = StateEffect2.define();
2668
2961
  var HiddenDone = class extends WidgetType3 {
2669
2962
  constructor(count) {
@@ -2750,16 +3043,28 @@ function menuItems(view, pos) {
2750
3043
  if (!list) return [];
2751
3044
  const apply = (changes, userEvent) => () => view.dispatch({ changes, userEvent });
2752
3045
  const out = [];
2753
- if (needsRenumber(state, list)) {
2754
- out.push({ label: "Fix numbering", run: apply(renumber(state, list), "input.renumber") });
3046
+ if (list.name === "OrderedList") {
3047
+ const changes = renumber(state, list);
3048
+ out.push({
3049
+ label: "Fix numbering",
3050
+ run: apply(changes, "input.renumber"),
3051
+ disabled: !changes.length
3052
+ });
2755
3053
  }
2756
3054
  const hasTasks = items(list).some((it) => done(state, it) !== null);
2757
3055
  if (hasTasks) {
2758
3056
  const n = countDone(state, list);
2759
- if (moveDoneDown(state, list).length)
2760
- out.push({ label: "Move done to bottom", run: apply(moveDoneDown(state, list), "input.move") });
2761
- if (n)
2762
- out.push({ label: `Clear done (${n})`, run: apply(clearDone(state, list), "delete.clear-done") });
3057
+ const moves = moveDoneDown(state, list);
3058
+ out.push({
3059
+ label: "Move done to bottom",
3060
+ run: apply(moves, "input.move"),
3061
+ disabled: !moves.length
3062
+ });
3063
+ out.push({
3064
+ label: n ? `Clear done (${n})` : "Clear done",
3065
+ run: apply(clearDone(state, list), "delete.clear-done"),
3066
+ disabled: !n
3067
+ });
2763
3068
  const hiding = isHidingDone(state);
2764
3069
  out.push({
2765
3070
  label: hiding ? "Show done" : "Hide done",
@@ -2774,12 +3079,13 @@ function openMenu(view, x, y, entries) {
2774
3079
  menu.setAttribute("role", "menu");
2775
3080
  menu.style.left = `${x}px`;
2776
3081
  menu.style.top = `${y}px`;
2777
- const buttons = entries.map(({ label, run }) => {
3082
+ const buttons = entries.map(({ label, run, disabled }) => {
2778
3083
  const b = document.createElement("button");
2779
3084
  b.type = "button";
2780
3085
  b.className = "cm-md-menu-item";
2781
3086
  b.setAttribute("role", "menuitem");
2782
3087
  b.textContent = label;
3088
+ b.disabled = !!disabled;
2783
3089
  b.addEventListener("click", () => {
2784
3090
  close();
2785
3091
  run();
@@ -2800,10 +3106,13 @@ function openMenu(view, x, y, entries) {
2800
3106
  } else if (e.key === "ArrowDown" || e.key === "ArrowUp") {
2801
3107
  e.preventDefault();
2802
3108
  const step2 = e.key === "ArrowDown" ? 1 : -1;
2803
- buttons[(i + step2 + buttons.length) % buttons.length]?.focus();
3109
+ const live = buttons.filter((b) => !b.disabled);
3110
+ const at = live.indexOf(buttons[i]);
3111
+ live[(at + step2 + live.length) % live.length]?.focus();
2804
3112
  }
2805
3113
  };
2806
3114
  function close() {
3115
+ if (menu.matches?.(":popover-open")) menu.hidePopover();
2807
3116
  menu.remove();
2808
3117
  window.removeEventListener("pointerdown", outside, true);
2809
3118
  window.removeEventListener("scroll", close, true);
@@ -2815,10 +3124,20 @@ function openMenu(view, x, y, entries) {
2815
3124
  window.addEventListener("blur", close);
2816
3125
  menu.addEventListener("keydown", keys);
2817
3126
  view.dom.appendChild(menu);
2818
- const r = menu.getBoundingClientRect();
2819
- if (r.right > window.innerWidth) menu.style.left = `${Math.max(0, x - r.width)}px`;
2820
- if (r.bottom > window.innerHeight) menu.style.top = `${Math.max(0, y - r.height)}px`;
2821
- buttons[0]?.focus();
3127
+ if (typeof menu.showPopover === "function") {
3128
+ menu.popover = "manual";
3129
+ menu.showPopover();
3130
+ }
3131
+ let r = menu.getBoundingClientRect();
3132
+ if (r.left !== x || r.top !== y) {
3133
+ menu.style.left = `${x + (x - r.left)}px`;
3134
+ menu.style.top = `${y + (y - r.top)}px`;
3135
+ r = menu.getBoundingClientRect();
3136
+ }
3137
+ const shift = (prop, by) => menu.style[prop] = `${parseFloat(menu.style[prop]) - by}px`;
3138
+ if (r.right > window.innerWidth) shift("left", r.width);
3139
+ if (r.bottom > window.innerHeight) shift("top", r.height);
3140
+ buttons.find((b) => !b.disabled)?.focus();
2822
3141
  return close;
2823
3142
  }
2824
3143
  var menuPlugin = ViewPlugin4.fromClass(
@@ -2848,6 +3167,9 @@ var menuPlugin = ViewPlugin4.fromClass(
2848
3167
  var listsTheme = EditorView4.baseTheme({
2849
3168
  ".cm-md-menu": {
2850
3169
  position: "fixed",
3170
+ // undo the popover defaults (centred, inset 0)
3171
+ inset: "auto",
3172
+ margin: "0",
2851
3173
  zIndex: "var(--studio-layer-popover, 4000)",
2852
3174
  display: "flex",
2853
3175
  flexDirection: "column",
@@ -2867,7 +3189,10 @@ var listsTheme = EditorView4.baseTheme({
2867
3189
  borderRadius: "4px",
2868
3190
  cursor: "var(--studio-cursor-default, default)"
2869
3191
  },
2870
- ".cm-md-menu-item:hover, .cm-md-menu-item:focus-visible": {
3192
+ ".cm-md-menu-item:disabled": {
3193
+ opacity: "0.45"
3194
+ },
3195
+ ".cm-md-menu-item:not(:disabled):hover, .cm-md-menu-item:focus-visible": {
2871
3196
  background: "var(--studio-fill-offset-20, #eee)"
2872
3197
  },
2873
3198
  ".cm-md-hidden-done": {
@@ -2882,16 +3207,22 @@ var listsTheme = EditorView4.baseTheme({
2882
3207
  textDecoration: "underline"
2883
3208
  }
2884
3209
  });
2885
- var lists = [hideDoneField, menuPlugin, listsTheme];
3210
+ var lists = [
3211
+ hideDoneField,
3212
+ menuPlugin,
3213
+ listsTheme,
3214
+ // ahead of indentWithTab in the main keymap
3215
+ Prec3.high(keymap2.of([{ key: "Tab", run: tabInOrderedList }]))
3216
+ ];
2886
3217
 
2887
3218
  // src/extensions/prefix.ts
2888
3219
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
2889
3220
  import {
2890
3221
  EditorSelection as EditorSelection4,
2891
3222
  EditorState as EditorState2,
2892
- Prec as Prec3
3223
+ Prec as Prec4
2893
3224
  } from "@codemirror/state";
2894
- import { EditorView as EditorView5, keymap as keymap2 } from "@codemirror/view";
3225
+ import { EditorView as EditorView5, keymap as keymap3 } from "@codemirror/view";
2895
3226
  var MARKS2 = /* @__PURE__ */ new Set(["QuoteMark", "ListMark", "HeaderMark", "TaskMarker"]);
2896
3227
  function skipSpaces2(state, pos, end) {
2897
3228
  while (pos < end && /[ \t]/.test(state.doc.sliceString(pos, pos + 1))) pos++;
@@ -3008,8 +3339,8 @@ var prefixes = [
3008
3339
  keepOut,
3009
3340
  replaceMarker,
3010
3341
  // above lang-markdown's own (Prec.high) Backspace.
3011
- Prec3.highest(
3012
- keymap2.of([
3342
+ Prec4.highest(
3343
+ keymap3.of([
3013
3344
  { key: "Backspace", run: backspace },
3014
3345
  { key: "Delete", run: deleteForward }
3015
3346
  ])
@@ -3017,9 +3348,9 @@ var prefixes = [
3017
3348
  ];
3018
3349
 
3019
3350
  // src/extensions/vertical.ts
3020
- import { EditorSelection as EditorSelection5, Prec as Prec4 } from "@codemirror/state";
3351
+ import { EditorSelection as EditorSelection5, Prec as Prec5 } from "@codemirror/state";
3021
3352
  import { cursorLineDown, cursorLineUp } from "@codemirror/commands";
3022
- import { keymap as keymap3 } from "@codemirror/view";
3353
+ import { keymap as keymap4 } from "@codemirror/view";
3023
3354
  var streaks = /* @__PURE__ */ new WeakMap();
3024
3355
  var vertical = (move) => (view) => {
3025
3356
  const { state } = view;
@@ -3034,7 +3365,8 @@ var vertical = (move) => (view) => {
3034
3365
  if (!move(view)) return false;
3035
3366
  const now = view.state.selection.main;
3036
3367
  const to = view.state.doc.lineAt(now.head).number;
3037
- const back = streak.visited.get(to);
3368
+ const back = to !== from ? streak.visited.get(to) : void 0;
3369
+ if (to === from) streak.visited.set(to, now.head);
3038
3370
  if (back !== void 0 && back !== now.head && view.state.doc === streak.doc) {
3039
3371
  view.dispatch({
3040
3372
  selection: EditorSelection5.cursor(back),
@@ -3047,8 +3379,8 @@ var vertical = (move) => (view) => {
3047
3379
  streaks.set(view, streak);
3048
3380
  return true;
3049
3381
  };
3050
- var verticalMemory = Prec4.high(
3051
- keymap3.of([
3382
+ var verticalMemory = Prec5.high(
3383
+ keymap4.of([
3052
3384
  { key: "ArrowUp", run: vertical(cursorLineUp) },
3053
3385
  { key: "ArrowDown", run: vertical(cursorLineDown) },
3054
3386
  { mac: "Ctrl-p", run: vertical(cursorLineUp) },
@@ -3057,8 +3389,8 @@ var verticalMemory = Prec4.high(
3057
3389
  );
3058
3390
 
3059
3391
  // src/extensions/zoom.ts
3060
- import { Prec as Prec5 } from "@codemirror/state";
3061
- import { keymap as keymap4 } from "@codemirror/view";
3392
+ import { Prec as Prec6 } from "@codemirror/state";
3393
+ import { keymap as keymap5 } from "@codemirror/view";
3062
3394
  var MIN_FONT_SIZE = 6;
3063
3395
  var FALLBACK_FONT_SIZE = 16;
3064
3396
  var px = (value) => parseFloat(value) || FALLBACK_FONT_SIZE;
@@ -3087,8 +3419,8 @@ function step(view, by) {
3087
3419
  var zoomIn = (view) => step(view, 1);
3088
3420
  var zoomOut = (view) => step(view, -1);
3089
3421
  var zoomReset = (view) => step(view, 0);
3090
- var markdownZoom = Prec5.high(
3091
- keymap4.of([
3422
+ var markdownZoom = Prec6.high(
3423
+ keymap5.of([
3092
3424
  // `+` and `_` are the shifted keys, for keyboards that need Shift.
3093
3425
  { key: "Mod-=", run: zoomIn, preventDefault: true },
3094
3426
  { key: "Mod-+", run: zoomIn, preventDefault: true },
@@ -3198,6 +3530,18 @@ var baseHeadingStyles = {
3198
3530
  fontWeight: "var(--studio-heading-weight, 400)",
3199
3531
  textDecoration: "none"
3200
3532
  };
3533
+ var headingSizes = {
3534
+ 1: { fontSize: `var(--studio-heading-size-1, ${zoomed2("1.5rem")})`, lineHeight: zoomed2("2rem") },
3535
+ 2: { fontSize: `var(--studio-heading-size-2, ${zoomed2("1.5rem")})`, lineHeight: zoomed2("2rem") },
3536
+ 3: { fontSize: `var(--studio-heading-size-3, ${zoomed2("1.25rem")})`, lineHeight: zoomed2("1.75rem") },
3537
+ 4: { fontSize: `var(--studio-heading-size-4, ${zoomed2("1.1rem")})` }
3538
+ };
3539
+ var headingLines = Object.fromEntries(
3540
+ Object.entries(headingSizes).map(([level, size]) => [
3541
+ `.cm-md-h${level}`,
3542
+ { fontFamily: baseHeadingStyles.fontFamily, fontWeight: baseHeadingStyles.fontWeight, ...size }
3543
+ ])
3544
+ );
3201
3545
  var baseCodeStyles = {
3202
3546
  fontFamily: "var(--text-editor-family-code)",
3203
3547
  fontSize: "1em"
@@ -3216,31 +3560,28 @@ var markdownSyntaxHighlighting = (style) => HighlightStyle.define([
3216
3560
  {
3217
3561
  tag: tags.heading1,
3218
3562
  ...baseHeadingStyles,
3219
- fontSize: `var(--studio-heading-size-1, ${zoomed2("1.5rem")})`,
3220
- lineHeight: zoomed2("2rem"),
3563
+ ...headingSizes[1],
3221
3564
  marginBottom: zoomed2("1rem"),
3222
3565
  marginTop: zoomed2("2rem")
3223
3566
  },
3224
3567
  {
3225
3568
  tag: tags.heading2,
3226
3569
  ...baseHeadingStyles,
3227
- fontSize: `var(--studio-heading-size-2, ${zoomed2("1.5rem")})`,
3228
- lineHeight: zoomed2("2rem"),
3570
+ ...headingSizes[2],
3229
3571
  marginBottom: zoomed2("1rem"),
3230
3572
  marginTop: zoomed2("2rem")
3231
3573
  },
3232
3574
  {
3233
3575
  tag: tags.heading3,
3234
3576
  ...baseHeadingStyles,
3235
- fontSize: `var(--studio-heading-size-3, ${zoomed2("1.25rem")})`,
3236
- lineHeight: zoomed2("1.75rem"),
3577
+ ...headingSizes[3],
3237
3578
  marginBottom: zoomed2("1rem"),
3238
3579
  marginTop: zoomed2("2rem")
3239
3580
  },
3240
3581
  {
3241
3582
  tag: tags.heading4,
3242
3583
  ...baseHeadingStyles,
3243
- fontSize: `var(--studio-heading-size-4, ${zoomed2("1.1rem")})`,
3584
+ ...headingSizes[4],
3244
3585
  marginBottom: zoomed2("1rem"),
3245
3586
  marginTop: zoomed2("2rem")
3246
3587
  },
@@ -3253,7 +3594,7 @@ var markdownSyntaxHighlighting = (style) => HighlightStyle.define([
3253
3594
  color: "var(--syntax-comment, #555)",
3254
3595
  fontFamily: "var(--text-editor-family-code)"
3255
3596
  },
3256
- { tag: tags.quote, fontStyle: "var(--syntax-style-quote, italic)" },
3597
+ // quotes are set off by their bar (margin.ts), not by italics.
3257
3598
  {
3258
3599
  tag: tags.strong,
3259
3600
  fontFamily: bodyFont(style),
@@ -3315,7 +3656,7 @@ var markdownSyntaxHighlighting = (style) => HighlightStyle.define([
3315
3656
  { tag: tags.monospace, ...baseCodeStyles }
3316
3657
  ]);
3317
3658
  var theme = (style) => [
3318
- EditorView8.theme(markdownStyles(style)),
3659
+ EditorView8.theme({ ...markdownStyles(style), ...headingLines }),
3319
3660
  syntaxHighlighting(markdownSyntaxHighlighting(style))
3320
3661
  ];
3321
3662
 
@@ -3325,7 +3666,7 @@ function markdownExtensions() {
3325
3666
  ...theme("sans"),
3326
3667
  indentOnInput(),
3327
3668
  markdownZoom,
3328
- keymap5.of([
3669
+ keymap6.of([
3329
3670
  ...markdownFormattingKeymap,
3330
3671
  ...defaultKeymap,
3331
3672
  ...searchKeymap,