canopycms 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/CHANGELOG.md +188 -47
  2. package/dist/api/branch-review.d.ts +11 -1
  3. package/dist/api/branch-review.js +96 -18
  4. package/dist/api/branch-status.d.ts +26 -2
  5. package/dist/api/branch-status.js +38 -2
  6. package/dist/api/branch-withdraw.js +4 -7
  7. package/dist/api/branch.d.ts +2 -0
  8. package/dist/api/client.d.ts +3 -3
  9. package/dist/api/client.js +4 -4
  10. package/dist/api/comments.d.ts +2 -2
  11. package/dist/api/comments.js +4 -0
  12. package/dist/api/content.d.ts +2 -2
  13. package/dist/api/github-sync.d.ts +4 -0
  14. package/dist/api/github-sync.js +38 -6
  15. package/dist/api/pr-comment-sync.d.ts +18 -0
  16. package/dist/api/pr-comment-sync.js +77 -0
  17. package/dist/api/request-deadline.d.ts +15 -7
  18. package/dist/api/request-deadline.js +15 -7
  19. package/dist/api/schema.d.ts +16 -16
  20. package/dist/authorization/review-role.d.ts +33 -0
  21. package/dist/authorization/review-role.js +17 -0
  22. package/dist/branch-metadata-file.d.ts +3 -0
  23. package/dist/branch-metadata-file.js +1 -0
  24. package/dist/cli/cli.js +699 -130
  25. package/dist/cli/generate-ai-content.js +524 -59
  26. package/dist/cli/github-app-output.d.ts +34 -0
  27. package/dist/cli/github-app-output.js +85 -0
  28. package/dist/cli/init-github-app.d.ts +18 -0
  29. package/dist/cli/init-github-app.js +66 -58
  30. package/dist/cli/init.js +10 -1
  31. package/dist/comment-store.d.ts +5 -0
  32. package/dist/comment-store.js +24 -0
  33. package/dist/config/helpers.js +1 -3
  34. package/dist/config/schemas/collection.d.ts +2 -2
  35. package/dist/config/schemas/config.d.ts +3 -0
  36. package/dist/config/schemas/config.js +2 -1
  37. package/dist/config/schemas/url.d.ts +6 -0
  38. package/dist/config/schemas/url.js +16 -0
  39. package/dist/config/types.d.ts +12 -1
  40. package/dist/content-id-index.d.ts +9 -0
  41. package/dist/content-id-index.js +31 -0
  42. package/dist/editor/BranchManager.d.ts +3 -0
  43. package/dist/editor/BranchManager.js +75 -44
  44. package/dist/editor/CanopyEditor.js +2 -2
  45. package/dist/editor/CanopyEditorPage.js +3 -4
  46. package/dist/editor/Editor.js +47 -8
  47. package/dist/editor/branch-lock.d.ts +7 -0
  48. package/dist/editor/branch-lock.js +9 -2
  49. package/dist/editor/client-reference-resolver.d.ts +5 -3
  50. package/dist/editor/client-reference-resolver.js +10 -5
  51. package/dist/editor/components/BranchesLoadFailedNotice.d.ts +13 -0
  52. package/dist/editor/components/BranchesLoadFailedNotice.js +12 -0
  53. package/dist/editor/components/EditorHeader.d.ts +10 -4
  54. package/dist/editor/components/EditorHeader.js +108 -28
  55. package/dist/editor/components/EntryHeader.d.ts +5 -1
  56. package/dist/editor/components/EntryHeader.js +3 -1
  57. package/dist/editor/components/ReasonedMenuItem.d.ts +8 -0
  58. package/dist/editor/components/ReasonedMenuItem.js +2 -2
  59. package/dist/editor/components/ReviewDialogs.d.ts +19 -0
  60. package/dist/editor/components/ReviewDialogs.js +41 -0
  61. package/dist/editor/copy.d.ts +4 -0
  62. package/dist/editor/copy.js +4 -0
  63. package/dist/editor/fields/MarkdownField.js +1 -1
  64. package/dist/editor/hooks/create-branch-request.d.ts +6 -2
  65. package/dist/editor/hooks/create-branch-request.js +7 -2
  66. package/dist/editor/hooks/useBranchManager.d.ts +7 -2
  67. package/dist/editor/hooks/useBranchManager.js +252 -37
  68. package/dist/editor-url.d.ts +20 -0
  69. package/dist/editor-url.js +71 -0
  70. package/dist/git-manager.js +10 -6
  71. package/dist/github-service.d.ts +2 -2
  72. package/dist/github-service.js +4 -2
  73. package/dist/pr-comments.d.ts +33 -0
  74. package/dist/pr-comments.js +41 -0
  75. package/dist/schema/schema-store.d.ts +8 -8
  76. package/dist/services.d.ts +2 -17
  77. package/dist/services.js +38 -1
  78. package/dist/settings-workspace.d.ts +21 -7
  79. package/dist/settings-workspace.js +91 -16
  80. package/dist/submission-attribution.d.ts +38 -1
  81. package/dist/submission-attribution.js +197 -18
  82. package/dist/task-queue/cms-task-queue.d.ts +1 -1
  83. package/dist/types.d.ts +6 -0
  84. package/dist/utils/content-serialize.js +7 -6
  85. package/dist/utils/markdown-body-splice.d.ts +15 -10
  86. package/dist/utils/markdown-body-splice.js +71 -28
  87. package/dist/utils/markdown-export-options.d.ts +13 -0
  88. package/dist/utils/markdown-export-options.js +13 -0
  89. package/dist/utils/markdown-restyle.d.ts +58 -0
  90. package/dist/utils/markdown-restyle.js +482 -0
  91. package/dist/version.d.ts +1 -1
  92. package/dist/version.js +1 -1
  93. package/dist/worker/bundle-header.d.ts +13 -9
  94. package/dist/worker/bundle-header.js +17 -11
  95. package/dist/worker/git-process.d.ts +2 -2
  96. package/dist/worker/git-sync.js +5 -0
  97. package/dist/worker/github-gateway-http.js +3 -2
  98. package/dist/worker/github-gateway-wire.d.ts +3 -0
  99. package/dist/worker/github-gateway-wire.js +1 -0
  100. package/dist/worker/github-gateway.d.ts +1 -1
  101. package/dist/worker/github-transfer.js +72 -2
  102. package/dist/worker/task-runner.js +53 -3
  103. package/package.json +2 -2
  104. package/dist/editor/fields/markdown-export-options.d.ts +0 -2
  105. package/dist/editor/fields/markdown-export-options.js +0 -8
@@ -780,7 +780,7 @@ var init_collection = __esm({
780
780
 
781
781
  // dist/config/schemas/url.js
782
782
  import { z as z4 } from "zod";
783
- var uploadTargetUrlSchema, previewPrefixSchema;
783
+ var uploadTargetUrlSchema, previewPrefixSchema, editorUrlSchema;
784
784
  var init_url = __esm({
785
785
  "dist/config/schemas/url.js"() {
786
786
  "use strict";
@@ -791,6 +791,14 @@ var init_url = __esm({
791
791
  previewPrefixSchema = z4.string().trim().refine((value) => isHttpUrlOrSameOriginPath(value), {
792
792
  message: 'editor.previewPrefix must be an absolute http(s) URL or a site-relative path beginning with a single "/", with no query or fragment'
793
793
  });
794
+ editorUrlSchema = z4.string().trim().refine((value) => {
795
+ if (!isHttpUrlOrSameOriginPath(value) || !/^https?:\/\//i.test(value))
796
+ return false;
797
+ const url = new URL(value);
798
+ return !url.username && !url.password;
799
+ }, {
800
+ message: "editorUrl must be an absolute http(s) URL with no credentials, query or fragment"
801
+ });
794
802
  }
795
803
  });
796
804
 
@@ -941,6 +949,7 @@ var init_config = __esm({
941
949
  contentRoot: contentRootSchema.default("content"),
942
950
  sourceRoot: sourceRootSchema.optional(),
943
951
  basePath: basePathSchema,
952
+ editorUrl: editorUrlSchema.optional(),
944
953
  unauthenticatedStatus: z6.union([z6.literal(401), z6.literal(419)]).optional(),
945
954
  danglingReferences: z6.enum(["error", "warn"]).optional(),
946
955
  editor: editorConfigSchema.optional(),
@@ -1533,6 +1542,428 @@ import { gfm } from "micromark-extension-gfm";
1533
1542
  import { mdxjs } from "micromark-extension-mdxjs";
1534
1543
  init_error();
1535
1544
 
1545
+ // dist/utils/markdown-export-options.js
1546
+ var MARKDOWN_EXPORT_OPTIONS = {
1547
+ listItemIndent: "one",
1548
+ bullet: "-",
1549
+ emphasis: "_",
1550
+ strong: "*",
1551
+ rule: "-"
1552
+ };
1553
+
1554
+ // dist/utils/markdown-restyle.js
1555
+ var BULLETS = [MARKDOWN_EXPORT_OPTIONS.bullet, "*"];
1556
+ var DELIMITERS = [".", ")"];
1557
+ var EMPHASIS = MARKDOWN_EXPORT_OPTIONS.emphasis;
1558
+ var STRONG = MARKDOWN_EXPORT_OPTIONS.strong.repeat(2);
1559
+ var RULE = MARKDOWN_EXPORT_OPTIONS.rule.repeat(3);
1560
+ var WORD_CHAR = /[^\s\p{P}\p{S}]/u;
1561
+ var GAP = /^[ \t>]*(?:\r?\n[ \t>]*)+$/;
1562
+ function startOf(node) {
1563
+ return node?.position?.start.offset;
1564
+ }
1565
+ function endOf(node) {
1566
+ return node?.position?.end.offset;
1567
+ }
1568
+ function apply(source, edits, from = 0, to = source.length) {
1569
+ let out = "";
1570
+ let at = from;
1571
+ for (const edit of [...edits].sort((a, b) => a.start - b.start)) {
1572
+ if (edit.start < at || edit.end > to)
1573
+ continue;
1574
+ out += source.slice(at, edit.start) + edit.text;
1575
+ at = edit.end;
1576
+ }
1577
+ return out + source.slice(at, to);
1578
+ }
1579
+ function underscoreStarts(node) {
1580
+ const bare = startOf(node) === startOf(node.children?.[0]);
1581
+ if (node.type === "link" && !bare || node.type === "linkReference")
1582
+ return [];
1583
+ if (node.type === "text" || node.type === "mdxTextExpression") {
1584
+ const start = startOf(node);
1585
+ return start !== void 0 && (node.value ?? "").includes("_") ? [start] : [];
1586
+ }
1587
+ return (node.children ?? []).flatMap(underscoreStarts);
1588
+ }
1589
+ function orderedNumber(numbers, index) {
1590
+ const [first = 1, second, third] = numbers;
1591
+ if (index === 0)
1592
+ return first;
1593
+ const ones = numbers.length > 1 && second === 1 && (first !== 0 || numbers.length < 3 || third === 1);
1594
+ return ones ? 1 : first + index;
1595
+ }
1596
+ var Restyler = class {
1597
+ constructor(source) {
1598
+ this.source = source;
1599
+ this.listMarker = /* @__PURE__ */ new Map();
1600
+ this.underscores = /* @__PURE__ */ new Map();
1601
+ }
1602
+ /** Edits for `node` and everything under it, given its ancestors (nearest last). */
1603
+ collect(node, ancestors, out) {
1604
+ const inside = [...ancestors, node];
1605
+ switch (node.type) {
1606
+ case "emphasis":
1607
+ this.emphasis(node, ancestors, out);
1608
+ break;
1609
+ case "strong":
1610
+ this.strong(node, out);
1611
+ break;
1612
+ case "text":
1613
+ this.text(node, ancestors, out);
1614
+ return;
1615
+ case "thematicBreak":
1616
+ this.replaceWhole(node, this.ruleIn(ancestors), out);
1617
+ return;
1618
+ case "code":
1619
+ this.fence(node, out);
1620
+ return;
1621
+ case "heading":
1622
+ this.heading(node, inside, out);
1623
+ return;
1624
+ case "table":
1625
+ this.table(node, inside, out);
1626
+ return;
1627
+ default:
1628
+ break;
1629
+ }
1630
+ this.children(node, inside, out);
1631
+ }
1632
+ /**
1633
+ * Edits for `parent`'s children and the gaps between them. `fixed` names children whose text
1634
+ * must stay as it is (untouched top-level blocks); their lists still count for alternation.
1635
+ */
1636
+ children(parent, inside, out, fixed = () => false, outFor = () => out) {
1637
+ const children = parent.children ?? [];
1638
+ const markers = this.listMarkers(children, fixed);
1639
+ children.forEach((child, index) => {
1640
+ if (fixed(index))
1641
+ return;
1642
+ const edits = outFor(index);
1643
+ const marker = markers.get(child);
1644
+ if (marker !== void 0)
1645
+ this.list(child, marker, edits);
1646
+ this.collect(child, inside, edits);
1647
+ });
1648
+ const always = parent.type === "root" || parent.type === "blockquote";
1649
+ const spaced = always || parent.type === "list" || parent.type === "listItem";
1650
+ if (!spaced)
1651
+ return;
1652
+ for (let index = 1; index < children.length; index++) {
1653
+ if (fixed(index) && fixed(index - 1))
1654
+ continue;
1655
+ this.gap(children[index - 1], children[index], always, outFor(fixed(index) ? index - 1 : index));
1656
+ }
1657
+ }
1658
+ /** The marker each list among `children` takes: its own, then alternating with the one before. */
1659
+ listMarkers(children, fixed) {
1660
+ const markers = /* @__PURE__ */ new Map();
1661
+ let previous;
1662
+ children.forEach((child, index) => {
1663
+ if (child.type !== "list") {
1664
+ previous = void 0;
1665
+ return;
1666
+ }
1667
+ const ordered = Boolean(child.ordered);
1668
+ const current = this.markerOf(child);
1669
+ if (current === void 0) {
1670
+ previous = void 0;
1671
+ return;
1672
+ }
1673
+ let marker = current;
1674
+ if (!fixed(index)) {
1675
+ const choices = ordered ? DELIMITERS : BULLETS;
1676
+ const next = children[index + 1];
1677
+ const nextFixed = next?.type === "list" && Boolean(next.ordered) === ordered && fixed(index + 1) ? this.markerOf(next) : void 0;
1678
+ const before = previous?.ordered === ordered ? previous.marker : void 0;
1679
+ marker = choices.find((choice) => choice !== before && choice !== nextFixed) ?? current;
1680
+ markers.set(child, marker);
1681
+ }
1682
+ this.listMarker.set(child, marker);
1683
+ previous = { ordered, marker };
1684
+ });
1685
+ return markers;
1686
+ }
1687
+ /**
1688
+ * A rule's text: `---`, but inside a list Prettier writes `***`, or `---` when the list takes
1689
+ * the alternate marker.
1690
+ */
1691
+ ruleIn(ancestors) {
1692
+ const list = [...ancestors].reverse().find((a) => a.type === "list");
1693
+ if (list === void 0)
1694
+ return RULE;
1695
+ const marker = this.listMarker.get(list);
1696
+ return marker === BULLETS[0] || marker === DELIMITERS[0] ? "***" : RULE;
1697
+ }
1698
+ /** A list's bullet, or its ordered delimiter, read from its first item. */
1699
+ markerOf(list) {
1700
+ const at = startOf(list.children?.[0]);
1701
+ if (at === void 0)
1702
+ return void 0;
1703
+ if (!list.ordered)
1704
+ return this.source[at];
1705
+ return /^\d{1,9}([.)])/.exec(this.source.slice(at, at + 10))?.[1];
1706
+ }
1707
+ /** Item markers: the list's bullet or delimiter, and Prettier's numbering where widths allow. */
1708
+ list(list, marker, out) {
1709
+ const items = list.children ?? [];
1710
+ const starts = items.map(startOf);
1711
+ if (!list.ordered) {
1712
+ for (const at of starts) {
1713
+ if (at !== void 0 && this.source[at] !== marker) {
1714
+ out.push({ start: at, end: at + 1, text: marker });
1715
+ }
1716
+ }
1717
+ return;
1718
+ }
1719
+ const numbers = starts.map((at) => at === void 0 ? null : /^(\d{1,9})[.)]/.exec(this.source.slice(at, at + 11)));
1720
+ const typed = numbers.map((match) => Number(match?.[1]));
1721
+ const renumber = numbers.every((match, i) => match !== null && String(orderedNumber(typed, i)).length === match[1]?.length);
1722
+ numbers.forEach((match, i) => {
1723
+ const at = starts[i];
1724
+ if (match === null || at === void 0)
1725
+ return;
1726
+ const digits = match[1] ?? "";
1727
+ const wanted = renumber ? String(orderedNumber(typed, i)) : digits;
1728
+ if (wanted !== digits || match[0].slice(-1) !== marker) {
1729
+ out.push({ start: at, end: at + match[0].length, text: wanted + marker });
1730
+ }
1731
+ });
1732
+ }
1733
+ /**
1734
+ * The gap between two siblings with exactly one blank line, kept in the container's own
1735
+ * prefix and line ending; where `always` is false, only a gap that had a blank line.
1736
+ */
1737
+ gap(a, b, always, out) {
1738
+ const end = this.contentEnd(a);
1739
+ const start = startOf(b);
1740
+ if (end === void 0 || start === void 0)
1741
+ return;
1742
+ const gap = this.source.slice(end, start);
1743
+ if (!GAP.test(gap))
1744
+ return;
1745
+ const lines = gap.split(/\r?\n/);
1746
+ if (!always && lines.length < 3)
1747
+ return;
1748
+ const eol = gap.includes("\r\n") ? "\r\n" : "\n";
1749
+ const prefix = lines[lines.length - 1] ?? "";
1750
+ const together = a?.type === "definition" && b?.type === "definition";
1751
+ const text = together ? eol + prefix : eol + prefix.trimEnd() + eol + prefix;
1752
+ if (text !== gap)
1753
+ out.push({ start: end, end: start, text });
1754
+ }
1755
+ /**
1756
+ * Where `node`'s last line of content ends: a list's position runs on over the blank lines
1757
+ * after it, which in a block quote are lines of `>`.
1758
+ */
1759
+ contentEnd(node) {
1760
+ const start = startOf(node);
1761
+ let end = endOf(node);
1762
+ if (start === void 0 || end === void 0)
1763
+ return end;
1764
+ for (; ; ) {
1765
+ const lineStart = this.source.lastIndexOf("\n", end - 1);
1766
+ if (lineStart <= start || !/^[ \t>]*$/.test(this.source.slice(lineStart + 1, end)))
1767
+ break;
1768
+ end = this.source[lineStart - 1] === "\r" ? lineStart - 1 : lineStart;
1769
+ }
1770
+ return end;
1771
+ }
1772
+ emphasis(node, ancestors, out) {
1773
+ const start = startOf(node);
1774
+ const end = endOf(node);
1775
+ if (start === void 0 || end === void 0)
1776
+ return;
1777
+ const open = this.source[start];
1778
+ if (open !== this.source[end - 1] || open !== "*" && open !== "_")
1779
+ return;
1780
+ const marker = ancestors.some((a) => a.type === "emphasis") || this.besideWord(start, end) || this.afterUnderscore(start, ancestors) ? "*" : EMPHASIS;
1781
+ if (open === marker)
1782
+ return;
1783
+ out.push({ start, end: start + 1, text: marker }, { start: end - 1, end, text: marker });
1784
+ }
1785
+ strong(node, out) {
1786
+ const start = startOf(node);
1787
+ const end = endOf(node);
1788
+ if (start === void 0 || end === void 0)
1789
+ return;
1790
+ if (this.source.slice(start, start + 2) !== "__" || this.source.slice(end - 2, end) !== "__") {
1791
+ return;
1792
+ }
1793
+ out.push({ start, end: start + 2, text: STRONG }, { start: end - 2, end, text: STRONG });
1794
+ }
1795
+ /** Whether the characters past the markers around `[start, end)` include a word character. */
1796
+ besideWord(start, end) {
1797
+ const markers = "*_";
1798
+ let before = start - 1;
1799
+ while (before >= 0 && markers.includes(this.source[before] ?? ""))
1800
+ before--;
1801
+ let after = end;
1802
+ while (after < this.source.length && markers.includes(this.source[after] ?? ""))
1803
+ after++;
1804
+ return WORD_CHAR.test(this.source[before] ?? " ") || WORD_CHAR.test(this.source[after] ?? " ");
1805
+ }
1806
+ afterUnderscore(start, ancestors) {
1807
+ const block = [...ancestors].reverse().find((a) => a.type === "paragraph" || a.type === "heading" || a.type === "tableCell");
1808
+ if (block === void 0)
1809
+ return false;
1810
+ let starts = this.underscores.get(block);
1811
+ if (starts === void 0) {
1812
+ starts = underscoreStarts(block);
1813
+ this.underscores.set(block, starts);
1814
+ }
1815
+ const link = [...ancestors].reverse().find((a) => a.type === "link" || a.type === "linkReference");
1816
+ const inLink = (link?.children ?? []).flatMap(underscoreStarts);
1817
+ return [...starts, ...inLink].some((at) => at < start);
1818
+ }
1819
+ text(node, ancestors, out) {
1820
+ const start = startOf(node);
1821
+ const end = endOf(node);
1822
+ if (start === void 0 || end === void 0)
1823
+ return;
1824
+ const raw = this.source.slice(start, end);
1825
+ for (const match of raw.matchAll(/[ \t]+(?=\r?\n)/g)) {
1826
+ out.push({ start: start + match.index, end: start + match.index + match[0].length, text: "" });
1827
+ }
1828
+ if (ancestors.some((a) => a.type === "link" || a.type === "linkReference"))
1829
+ return;
1830
+ const block = [...ancestors].reverse().find((a) => a.type === "paragraph" || a.type === "heading" || a.type === "tableCell");
1831
+ const blockStart = startOf(block);
1832
+ const blockEnd = endOf(block);
1833
+ if (blockStart === void 0 || blockEnd === void 0)
1834
+ return;
1835
+ if (/\$|\[\[/.test(this.source.slice(blockStart, blockEnd)))
1836
+ return;
1837
+ for (const match of raw.matchAll(/(?<=[\p{L}\p{N}])\\_(?=[\p{L}\p{N}])/gu)) {
1838
+ out.push({ start: start + match.index, end: start + match.index + 1, text: "" });
1839
+ }
1840
+ }
1841
+ replaceWhole(node, text, out) {
1842
+ const start = startOf(node);
1843
+ const end = endOf(node);
1844
+ if (start === void 0 || end === void 0)
1845
+ return;
1846
+ if (this.source.slice(start, end) !== text)
1847
+ out.push({ start, end, text });
1848
+ }
1849
+ /** A `~~~` fence as backticks, when nothing inside it would end a backtick fence. */
1850
+ fence(node, out) {
1851
+ const start = startOf(node);
1852
+ const end = endOf(node);
1853
+ if (start === void 0 || end === void 0)
1854
+ return;
1855
+ const raw = this.source.slice(start, end);
1856
+ const close = /(?:\r?\n)[ \t>]*(~{3,})[ \t]*$/.exec(raw);
1857
+ if (!/^~~~(?!~)[^`\n]*\n/.test(raw) || close?.[1] === void 0 || raw.includes("`"))
1858
+ return;
1859
+ const closeAt = start + close.index + close[0].indexOf("~");
1860
+ out.push({ start, end: start + 3, text: "```" }, { start: closeAt, end: closeAt + close[1].length, text: "```" });
1861
+ }
1862
+ /** A one-line heading as `# x`, its content with its own edits. */
1863
+ heading(node, inside, out) {
1864
+ const inner = [];
1865
+ this.children(node, inside, inner);
1866
+ const children = node.children ?? [];
1867
+ const start = startOf(node);
1868
+ const end = endOf(node);
1869
+ const contentStart = startOf(children[0]);
1870
+ const contentEnd = endOf(children[children.length - 1]);
1871
+ if (start === void 0 || end === void 0 || contentStart === void 0 || contentEnd === void 0 || /[\r\n]/.test(this.source.slice(contentStart, contentEnd))) {
1872
+ out.push(...inner);
1873
+ return;
1874
+ }
1875
+ const content = apply(this.source, inner, contentStart, contentEnd);
1876
+ const text = `${"#".repeat(node.depth ?? 1)} ${content}`;
1877
+ if (text !== this.source.slice(start, end))
1878
+ out.push({ start, end, text });
1879
+ else
1880
+ out.push(...inner);
1881
+ }
1882
+ /** A table padded to its columns, as Prettier pads it; its cells keep their own text. */
1883
+ table(node, inside, out) {
1884
+ const start = startOf(node);
1885
+ const end = endOf(node);
1886
+ if (start === void 0 || end === void 0)
1887
+ return;
1888
+ const prefix = this.source.slice(this.source.lastIndexOf("\n", start - 1) + 1, start);
1889
+ const inner = [];
1890
+ const rows = (node.children ?? []).map((row) => (row.children ?? []).map((cell) => {
1891
+ const edits = [];
1892
+ this.children(cell, [...inside, row, cell], edits);
1893
+ inner.push(...edits);
1894
+ const content = cell.children ?? [];
1895
+ const from = startOf(content[0]);
1896
+ const to = endOf(content[content.length - 1]);
1897
+ return from === void 0 || to === void 0 ? "" : apply(this.source, edits, from, to);
1898
+ }));
1899
+ const plain = rows.every((row) => row.every((cell) => /^[\x20-\x7e]*$/.test(cell)));
1900
+ if (!plain || !/^[ \t>]*$/.test(prefix)) {
1901
+ out.push(...inner);
1902
+ return;
1903
+ }
1904
+ const align = node.align ?? [];
1905
+ const columns = Math.max(...rows.map((row) => row.length), align.length);
1906
+ const widths = Array.from({ length: columns }, (_, c) => Math.max(3, ...rows.map((row) => row[c]?.length ?? 0)));
1907
+ const pad = (text2, c) => {
1908
+ const room = (widths[c] ?? 3) - text2.length;
1909
+ if (align[c] === "right")
1910
+ return " ".repeat(room) + text2;
1911
+ if (align[c] === "center") {
1912
+ const left = Math.floor(room / 2);
1913
+ return " ".repeat(left) + text2 + " ".repeat(room - left);
1914
+ }
1915
+ return text2 + " ".repeat(room);
1916
+ };
1917
+ const delimiter = widths.map((width, c) => {
1918
+ const a = align[c];
1919
+ if (a === "left")
1920
+ return `:${"-".repeat(width - 1)}`;
1921
+ if (a === "right")
1922
+ return `${"-".repeat(width - 1)}:`;
1923
+ if (a === "center")
1924
+ return `:${"-".repeat(width - 2)}:`;
1925
+ return "-".repeat(width);
1926
+ });
1927
+ const line = (cells) => `| ${cells.join(" | ")} |`;
1928
+ const lines = rows.map((row) => line(row.map(pad)));
1929
+ lines.splice(1, 0, line(delimiter));
1930
+ const eol = this.source.slice(start, end).includes("\r\n") ? "\r\n" : "\n";
1931
+ const text = lines.join(eol + prefix);
1932
+ if (text !== this.source.slice(start, end))
1933
+ out.push({ start, end, text });
1934
+ }
1935
+ };
1936
+ function restyleMarkdown(source, tree, fixed, readsBack) {
1937
+ if (source.startsWith("\uFEFF"))
1938
+ return void 0;
1939
+ const blocks = tree.children ?? [];
1940
+ const groups = blocks.map(() => []);
1941
+ new Restyler(source).children(tree, [], [], fixed, (index) => groups[index] ?? []);
1942
+ const changed = groups.filter((group) => group.length > 0);
1943
+ if (changed.length === 0)
1944
+ return void 0;
1945
+ let kept = [];
1946
+ let best;
1947
+ let budget = 8 * Math.ceil(Math.log2(changed.length + 1)) + 8;
1948
+ const attempt = (tried) => {
1949
+ if (budget-- <= 0)
1950
+ return;
1951
+ const trial = [...kept, ...tried.flat()];
1952
+ const text = apply(source, trial);
1953
+ const parsed = readsBack(text);
1954
+ if (parsed !== void 0) {
1955
+ kept = trial;
1956
+ best = { text, tree: parsed };
1957
+ } else if (tried.length > 1) {
1958
+ const half = Math.ceil(tried.length / 2);
1959
+ attempt(tried.slice(0, half));
1960
+ attempt(tried.slice(half));
1961
+ }
1962
+ };
1963
+ attempt(changed);
1964
+ return best;
1965
+ }
1966
+
1536
1967
  // dist/utils/yaml-source-splice.js
1537
1968
  import { isDeepStrictEqual } from "node:util";
1538
1969
  import { Document, parseDocument, isAlias, isCollection, isMap, isNode, isPair, isScalar, isSeq, YAMLMap, visit, YAMLSeq } from "yaml";
@@ -1971,15 +2402,15 @@ function canonical(node) {
1971
2402
  function kindOf(node) {
1972
2403
  return `${node.type}:${String(node.ordered ?? "")}:${String(node.checked ?? "")}`;
1973
2404
  }
1974
- function startOf(node) {
2405
+ function startOf2(node) {
1975
2406
  return node?.position?.start.offset;
1976
2407
  }
1977
- function endOf(node) {
2408
+ function endOf2(node) {
1978
2409
  return node?.position?.end.offset;
1979
2410
  }
1980
2411
  function sliceOf(text, node) {
1981
- const start = startOf(node);
1982
- const end = endOf(node);
2412
+ const start = startOf2(node);
2413
+ const end = endOf2(node);
1983
2414
  return start === void 0 || end === void 0 ? void 0 : text.slice(start, end);
1984
2415
  }
1985
2416
  function reindent(text, from, to) {
@@ -2053,8 +2484,8 @@ var Splicer = class {
2053
2484
  body(before, after) {
2054
2485
  const oNodes = before.children ?? [];
2055
2486
  const nNodes = after.children ?? [];
2056
- const first = startOf(oNodes[0]);
2057
- const last = endOf(oNodes[oNodes.length - 1]);
2487
+ const first = startOf2(oNodes[0]);
2488
+ const last = endOf2(oNodes[oNodes.length - 1]);
2058
2489
  if (first === void 0 || last === void 0 || nNodes.length === 0)
2059
2490
  return void 0;
2060
2491
  const o = { text: this.original, indent: "" };
@@ -2162,9 +2593,9 @@ var Splicer = class {
2162
2593
  item(o, oItem, n, nItem) {
2163
2594
  const oChildren = oItem.children ?? [];
2164
2595
  const nChildren = nItem.children ?? [];
2165
- const oStart = startOf(oItem);
2166
- const oContent = startOf(oChildren[0]);
2167
- const nContent = startOf(nChildren[0]);
2596
+ const oStart = startOf2(oItem);
2597
+ const oContent = startOf2(oChildren[0]);
2598
+ const nContent = startOf2(nChildren[0]);
2168
2599
  if (oStart === void 0 || oContent === void 0 || nContent === void 0)
2169
2600
  return void 0;
2170
2601
  const prefix = o.text.slice(oStart, oContent);
@@ -2202,12 +2633,12 @@ var Splicer = class {
2202
2633
  }
2203
2634
  };
2204
2635
  function gapBetween(text, a, b) {
2205
- const end = endOf(a);
2206
- const start = startOf(b);
2636
+ const end = endOf2(a);
2637
+ const start = startOf2(b);
2207
2638
  return end === void 0 || start === void 0 ? void 0 : text.slice(end, start);
2208
2639
  }
2209
2640
  function listMarkers(text, items, ordered) {
2210
- const firstStart = startOf(items[0]);
2641
+ const firstStart = startOf2(items[0]);
2211
2642
  if (firstStart === void 0)
2212
2643
  return void 0;
2213
2644
  if (!ordered) {
@@ -2220,16 +2651,14 @@ function listMarkers(text, items, ordered) {
2220
2651
  const first = numberAt(firstStart);
2221
2652
  if (first === null)
2222
2653
  return void 0;
2223
- const start = Number(first[1]);
2224
2654
  const delimiter = first[2] ?? ".";
2225
2655
  const padTo = first[1]?.startsWith("0") ? first[1].length : 0;
2226
- const second = items.length > 1 ? numberAt(startOf(items[1])) : null;
2227
- const step = second !== null && Number(second[1]) === start ? 0 : 1;
2656
+ const typed = items.map((item) => Number(numberAt(startOf2(item))?.[1]));
2228
2657
  return (itemText, index) => {
2229
2658
  const current = /^\d{1,9}[.)]/.exec(itemText);
2230
2659
  if (current === null)
2231
2660
  return void 0;
2232
- const marker = `${String(start + step * index).padStart(padTo, "0")}${delimiter}`;
2661
+ const marker = `${String(orderedNumber(typed, index)).padStart(padTo, "0")}${delimiter}`;
2233
2662
  if (marker.length !== current[0].length && itemText.includes("\n"))
2234
2663
  return void 0;
2235
2664
  return marker + itemText.slice(current[0].length);
@@ -2245,22 +2674,56 @@ function preserveMarkdownSource(original, updated, format) {
2245
2674
  return updated;
2246
2675
  }
2247
2676
  }
2677
+ function diskBlocks(oNodes, nNodes) {
2678
+ const oKeys = oNodes.map(canonical);
2679
+ const nKeys = nNodes.map(canonical);
2680
+ const pairs = matchSequences(oKeys, nKeys);
2681
+ const fromDisk = new Set(pairs.map(([, ni]) => ni));
2682
+ const matched = new Set(pairs.map(([oi]) => oi));
2683
+ const unmatched = /* @__PURE__ */ new Map();
2684
+ oKeys.forEach((key, index) => {
2685
+ if (!matched.has(index))
2686
+ unmatched.set(key, (unmatched.get(key) ?? 0) + 1);
2687
+ });
2688
+ nKeys.forEach((key, index) => {
2689
+ const left = unmatched.get(key) ?? 0;
2690
+ if (fromDisk.has(index) || left === 0)
2691
+ return;
2692
+ unmatched.set(key, left - 1);
2693
+ fromDisk.add(index);
2694
+ });
2695
+ return fromDisk;
2696
+ }
2248
2697
  function splice(original, updated, format) {
2249
- const before = parse(original, format);
2698
+ const before = original === void 0 ? void 0 : parse(original, format);
2250
2699
  const after = parse(updated, format);
2251
- if (before === void 0 || after === void 0)
2700
+ if (after === void 0)
2252
2701
  return void 0;
2253
2702
  const target = canonical(after);
2254
- for (const deep of [true, false]) {
2255
- const candidate = new Splicer(original, updated, deep).body(before, after);
2256
- if (candidate === void 0)
2257
- continue;
2258
- const reparsed = parse(candidate, format);
2259
- if (reparsed !== void 0 && canonical(reparsed) === target)
2260
- return candidate;
2261
- log.debug("markdown-body-splice", "splice does not read back as the edit", { deep });
2703
+ const fromDisk = diskBlocks(before?.children ?? [], after.children);
2704
+ const restyled = restyleMarkdown(updated, after, (index) => fromDisk.has(index), (text) => {
2705
+ const tree = parse(text, format);
2706
+ return tree !== void 0 && canonical(tree) === target ? tree : void 0;
2707
+ });
2708
+ if (original === void 0 || before === void 0)
2709
+ return restyled?.text;
2710
+ const asSent = { text: updated, tree: after };
2711
+ const edits = restyled === void 0 || restyled.text === updated ? [asSent] : [restyled, asSent];
2712
+ for (const edit of edits) {
2713
+ for (const deep of [true, false]) {
2714
+ const candidate = new Splicer(original, edit.text, deep).body(before, edit.tree);
2715
+ if (candidate === void 0)
2716
+ continue;
2717
+ const reparsed = parse(candidate, format);
2718
+ if (reparsed !== void 0 && canonical(reparsed) === target)
2719
+ return candidate;
2720
+ log.debug("markdown-body-splice", "splice does not read back as the edit", {
2721
+ deep,
2722
+ restyled: edit !== asSent
2723
+ });
2724
+ }
2262
2725
  }
2263
- return void 0;
2726
+ return restyled?.text;
2264
2727
  }
2265
2728
 
2266
2729
  // dist/utils/content-serialize.js
@@ -2549,7 +3012,7 @@ function writesFrontmatter(data) {
2549
3012
  }
2550
3013
  function bodyToWrite(body, data, format, existingRaw) {
2551
3014
  const priorBody = existingRaw === void 0 ? void 0 : extractBody(existingRaw);
2552
- const spliced = priorBody === void 0 ? body : preserveMarkdownSource(priorBody, body, format);
3015
+ const spliced = preserveMarkdownSource(priorBody, body, format);
2553
3016
  if (!writesFrontmatter(data)) {
2554
3017
  const flush = spliced.replace(/^(?:\r?\n)+/, "");
2555
3018
  return /^\uFEFF?---/.test(flush) ? `
@@ -3255,12 +3718,41 @@ var isValidId = isValidContentId;
3255
3718
  // dist/content-id-index.js
3256
3719
  init_error();
3257
3720
  init_logger();
3721
+
3722
+ // dist/utils/typed-filename.js
3723
+ var parseTypedFilename = (filename, entryTypes) => {
3724
+ if (filename.startsWith("."))
3725
+ return null;
3726
+ const lastDot = filename.lastIndexOf(".");
3727
+ if (lastDot === -1)
3728
+ return null;
3729
+ const nameWithoutExt = filename.slice(0, lastDot);
3730
+ const parts = nameWithoutExt.split(".");
3731
+ if (parts.length < 3)
3732
+ return null;
3733
+ const potentialType = parts[0];
3734
+ if (entryTypes && !entryTypes.some((e) => e.name === potentialType)) {
3735
+ return null;
3736
+ }
3737
+ const id = parts[parts.length - 1];
3738
+ if (!isValidId(id))
3739
+ return null;
3740
+ const slug = parts.slice(1, -1).join(".").toLowerCase();
3741
+ return {
3742
+ type: potentialType,
3743
+ slug,
3744
+ id
3745
+ };
3746
+ };
3747
+
3748
+ // dist/content-id-index.js
3258
3749
  var EMPTY_LOGICAL_PATH = "";
3259
3750
  function toLogicalCollectionPath(physicalPath) {
3260
3751
  if (physicalPath === ".")
3261
3752
  return EMPTY_LOGICAL_PATH;
3262
3753
  return physicalPath.split("/").map((seg) => extractSlugFromFilename(seg)).join("/");
3263
3754
  }
3755
+ var ENTRY_EXTENSIONS = new Set(Object.keys({ md: true, mdx: true, json: true, yaml: true }).map((format) => getFormatExtension(format)));
3264
3756
  var ContentIdIndex = class {
3265
3757
  constructor(root) {
3266
3758
  this.idToLocation = /* @__PURE__ */ new Map();
@@ -3631,34 +4123,6 @@ function extractSlugFromFilename(filename, entryTypeName) {
3631
4123
 
3632
4124
  // dist/url-collision.js
3633
4125
  init_error();
3634
-
3635
- // dist/utils/typed-filename.js
3636
- var parseTypedFilename = (filename, entryTypes) => {
3637
- if (filename.startsWith("."))
3638
- return null;
3639
- const lastDot = filename.lastIndexOf(".");
3640
- if (lastDot === -1)
3641
- return null;
3642
- const nameWithoutExt = filename.slice(0, lastDot);
3643
- const parts = nameWithoutExt.split(".");
3644
- if (parts.length < 3)
3645
- return null;
3646
- const potentialType = parts[0];
3647
- if (entryTypes && !entryTypes.some((e) => e.name === potentialType)) {
3648
- return null;
3649
- }
3650
- const id = parts[parts.length - 1];
3651
- if (!isValidId(id))
3652
- return null;
3653
- const slug = parts.slice(1, -1).join(".").toLowerCase();
3654
- return {
3655
- type: potentialType,
3656
- slug,
3657
- id
3658
- };
3659
- };
3660
-
3661
- // dist/url-collision.js
3662
4126
  var CONTENT_EXTENSIONS = [".md", ".mdx", ".json", ".yaml"];
3663
4127
  async function readDirSafe(dir) {
3664
4128
  try {
@@ -7155,6 +7619,7 @@ var branchMetadataShape = {
7155
7619
  pullRequestNumber: z7.number().int().optional(),
7156
7620
  submittedAt: z7.string().optional(),
7157
7621
  pushedToGitHubAt: z7.string().optional(),
7622
+ editorUrl: z7.string().optional(),
7158
7623
  syncStatus: enumOf({
7159
7624
  synced: "synced",
7160
7625
  "pending-sync": "pending-sync",
@@ -8547,7 +9012,7 @@ var GitManager = class _GitManager {
8547
9012
  });
8548
9013
  await step("clone", () => _GitManager.cloneWorkspace(remoteUrl, options.workspacePath, baseBranch, {
8549
9014
  remoteName,
8550
- noCheckout: options.branchType === "content",
9015
+ noCheckout: true,
8551
9016
  config: identity
8552
9017
  }));
8553
9018
  justCloned = true;
@@ -9015,7 +9480,7 @@ var GitManager = class _GitManager {
9015
9480
  log9.debug("git", "Checked out the remote settings branch", { branchName });
9016
9481
  return;
9017
9482
  }
9018
- await this.git.raw(["checkout", "--orphan", branchName]);
9483
+ await this.git.raw(["switch", "--orphan", branchName]);
9019
9484
  await this.git.raw(["rm", "-r", "-f", "-q", "--sparse", "--ignore-unmatch", "--", "."]);
9020
9485
  const leftover = (await this.git.raw(["ls-files"])).trim();
9021
9486
  if (leftover) {