@openspecui/web 3.5.2 → 3.6.0

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 (69) hide show
  1. package/dist/assets/CanvasRenderer-DdAQFilo.js +1 -0
  2. package/dist/assets/WebGLRenderer-D0xPb_sN.js +1 -0
  3. package/dist/assets/WebGPURenderer-DOHdaBx2.js +1 -0
  4. package/dist/assets/browserAll-r2hAe-_H.js +1 -0
  5. package/dist/assets/{dist-4Q-3H32Y.js → dist-BCNuV7xG.js} +1 -1
  6. package/dist/assets/dist-BIdWjo7l.js +1 -0
  7. package/dist/assets/{dist-CV0FPhgu.js → dist-BVK0Rssu.js} +1 -1
  8. package/dist/assets/{dist-Bw7c0_jc.js → dist-BozUQC43.js} +1 -1
  9. package/dist/assets/{dist-C2CZRWaX.js → dist-CDVL3YEX.js} +1 -1
  10. package/dist/assets/{dist-C87gRqf_.js → dist-CLV0HJPY.js} +1 -1
  11. package/dist/assets/{dist-CFABLsEW.js → dist-CTzhdbTq.js} +1 -1
  12. package/dist/assets/dist-CaLzADt5.js +1 -0
  13. package/dist/assets/{dist-LUCFMJ0H.js → dist-CiOmMqf9.js} +1 -1
  14. package/dist/assets/{dist-DDkSOHP1.js → dist-CnERwHuC.js} +1 -1
  15. package/dist/assets/dist-DOlIviuv.js +1 -0
  16. package/dist/assets/{dist-DAT_ScNa.js → dist-YiF0YY_U.js} +1 -1
  17. package/dist/assets/{ghostty-web-CCC3Geb4.js → ghostty-web-CZWq-tLQ.js} +1 -1
  18. package/dist/assets/{index-DQNMhAvr.js → index-4c2vKipy.js} +194 -189
  19. package/dist/assets/index-BWGHsSSL.css +1 -0
  20. package/dist/assets/{init-YHF-o1HV.js → init-DIWiFCrW.js} +1 -1
  21. package/dist/assets/trpc-sHsseX53.js +1 -0
  22. package/dist/assets/webworkerAll-C2ADhLOV.js +1 -0
  23. package/dist/index.html +2 -2
  24. package/dist-ssg/client/.vite/ssr-manifest.json +33 -15
  25. package/dist-ssg/client/assets/CanvasRenderer-BnHuf5TM.js +1 -0
  26. package/dist-ssg/client/assets/WebGLRenderer-QYh372d6.js +1 -0
  27. package/dist-ssg/client/assets/WebGPURenderer-By0AaAH2.js +1 -0
  28. package/dist-ssg/client/assets/browserAll-DPwpu3rh.js +1 -0
  29. package/dist-ssg/client/assets/dist-BEN6F4c9.js +1 -0
  30. package/dist-ssg/client/assets/{dist-IUx13mN_.js → dist-B_W8Dqsb.js} +1 -1
  31. package/dist-ssg/client/assets/{dist-605X54ro.js → dist-BcdNCEyE.js} +1 -1
  32. package/dist-ssg/client/assets/{dist-Cq0edT--.js → dist-Be-EJt6Y.js} +1 -1
  33. package/dist-ssg/client/assets/{dist-BzIh3dSQ.js → dist-ByMJ_rO0.js} +1 -1
  34. package/dist-ssg/client/assets/{dist-BqMeFwto.js → dist-CMm3Nmq-.js} +1 -1
  35. package/dist-ssg/client/assets/{dist-Df67DVWO.js → dist-CcH1wf1k.js} +1 -1
  36. package/dist-ssg/client/assets/dist-D1b5s3ht.js +1 -0
  37. package/dist-ssg/client/assets/dist-DPvPfazk.js +1 -0
  38. package/dist-ssg/client/assets/{dist-D2UdxWFE.js → dist-DVuQHEv_.js} +1 -1
  39. package/dist-ssg/client/assets/{dist-CF5IRlgL.js → dist-fM5ujDIS.js} +1 -1
  40. package/dist-ssg/client/assets/{dist-10DRfH0N.js → dist-oT5Lx9wx.js} +1 -1
  41. package/dist-ssg/client/assets/{ghostty-web-DzvBtNVX.js → ghostty-web-DSO4nFSe.js} +1 -1
  42. package/dist-ssg/client/assets/index-CGJJczvl.css +1 -0
  43. package/dist-ssg/client/assets/{index.ssg-BEtxTyFQ.js → index.ssg-ph-ofjeJ.js} +167 -162
  44. package/dist-ssg/client/assets/{init-BJbVbp-i.js → init-DOhQsgqw.js} +1 -1
  45. package/dist-ssg/client/assets/trpc-DvRhEguu.js +1 -0
  46. package/dist-ssg/client/assets/webworkerAll-J8DER0Bd.js +1 -0
  47. package/dist-ssg/client/index.ssg.html +2 -2
  48. package/dist-ssg/server/entry-server.js +1528 -147
  49. package/package.json +1 -1
  50. package/dist/assets/CanvasRenderer-D7DGGDQU.js +0 -1
  51. package/dist/assets/WebGLRenderer-CjDeBZzX.js +0 -1
  52. package/dist/assets/WebGPURenderer-BEzco9Ka.js +0 -1
  53. package/dist/assets/browserAll-DkGrhjFB.js +0 -1
  54. package/dist/assets/dist-C1SIv52I.js +0 -1
  55. package/dist/assets/dist-CrPybnRq.js +0 -1
  56. package/dist/assets/dist-PzWhymM8.js +0 -1
  57. package/dist/assets/index-DHDRgs_i.css +0 -1
  58. package/dist/assets/trpc-q0DFddsK.js +0 -1
  59. package/dist/assets/webworkerAll-CKlnpVkj.js +0 -1
  60. package/dist-ssg/client/assets/CanvasRenderer-ByUXbKXl.js +0 -1
  61. package/dist-ssg/client/assets/WebGLRenderer-ChYLzDCd.js +0 -1
  62. package/dist-ssg/client/assets/WebGPURenderer-BobKBO0x.js +0 -1
  63. package/dist-ssg/client/assets/browserAll-UN3GPOPc.js +0 -1
  64. package/dist-ssg/client/assets/dist-BuDT5pXI.js +0 -1
  65. package/dist-ssg/client/assets/dist-BxaDOJmU.js +0 -1
  66. package/dist-ssg/client/assets/dist-ScSFKyoF.js +0 -1
  67. package/dist-ssg/client/assets/index-14LuyayW.css +0 -1
  68. package/dist-ssg/client/assets/trpc-bHbo3cKe.js +0 -1
  69. package/dist-ssg/client/assets/webworkerAll-BeYUBsxP.js +0 -1
@@ -38436,13 +38436,6 @@ function writeLocalStorage(layout, key = getLocalStorageKey()) {
38436
38436
  localStorage.setItem(key, JSON.stringify(layout));
38437
38437
  } catch {}
38438
38438
  }
38439
- function collapseProjectDetailLocation(location) {
38440
- const tabId = pathToTabId(location.pathname);
38441
- if (!tabId) return location;
38442
- if (location.pathname === tabId) return location;
38443
- if (tabId === "/changes" || tabId === "/specs" || tabId === "/archive") return parseHref(tabId, location.state);
38444
- return location;
38445
- }
38446
38439
  var carryActiveOnMovePlugin = ({ prevState, nextState, event }) => {
38447
38440
  if (event.type !== "MOVE_TAB") return nextState;
38448
38441
  const sourceArea = prevState.bottomTabs.includes(event.tabId) ? "bottom" : "main";
@@ -39078,8 +39071,6 @@ var NavController = class {
39078
39071
  }
39079
39072
  this.state = normalizeState({
39080
39073
  ...this.state,
39081
- mainLocation: collapseProjectDetailLocation(this.state.mainLocation),
39082
- bottomLocation: collapseProjectDetailLocation(this.state.bottomLocation),
39083
39074
  updatedAt: 0
39084
39075
  });
39085
39076
  this.normalizeUrl();
@@ -50003,6 +49994,15 @@ async function getSpec(id) {
50003
49994
  return snapshotSpecToSpec(snapSpec);
50004
49995
  }
50005
49996
  /**
49997
+ * Get raw spec content (markdown)
49998
+ */
49999
+ async function getSpecRaw(id) {
50000
+ const snapshot = await loadSnapshot();
50001
+ if (!snapshot) return null;
50002
+ const spec = snapshot.specs.find((s) => s.id === id);
50003
+ return spec?.content ?? spec?.sourceContent ?? null;
50004
+ }
50005
+ /**
50006
50006
  * Get all changes metadata
50007
50007
  */
50008
50008
  async function getChanges() {
@@ -52924,6 +52924,12 @@ function useSpecSubscription(id) {
52924
52924
  onError: callbacks.onError
52925
52925
  }), () => getSpec(id), [id], getSpecSubscriptionCacheKey(id));
52926
52926
  }
52927
+ function useSpecRawSubscription(id) {
52928
+ return useSubscription((callbacks) => trpcClient.spec.subscribeRaw.subscribe({ id }, {
52929
+ onData: callbacks.onData,
52930
+ onError: callbacks.onError
52931
+ }), () => getSpecRaw(id), [id], `spec.subscribeRaw:${id}`);
52932
+ }
52927
52933
  function useChangesSubscription() {
52928
52934
  return useSubscription((callbacks) => trpcClient.change.subscribe.subscribe(void 0, {
52929
52935
  onData: callbacks.onData,
@@ -55415,6 +55421,42 @@ function useManualReconnect() {
55415
55421
  }, []);
55416
55422
  }
55417
55423
  //#endregion
55424
+ //#region src/components/badge.tsx
55425
+ var toneClassNames = {
55426
+ primary: "bg-primary text-primary-foreground",
55427
+ subtle: "border border-primary/35 bg-primary/10 text-primary",
55428
+ muted: "border border-border bg-muted text-muted-foreground",
55429
+ custom: ""
55430
+ };
55431
+ var sizeClassNames$1 = {
55432
+ dot: "h-1.5 w-1.5 min-w-0 p-0",
55433
+ xs: "h-4 min-w-4 px-1 text-[10px] leading-none",
55434
+ sm: "h-5 min-w-5 px-1.5 text-[11px] leading-none"
55435
+ };
55436
+ var shapeClassNames = {
55437
+ pill: "rounded-full",
55438
+ box: "rounded"
55439
+ };
55440
+ function Badge({ tone = "primary", size = "xs", shape = "pill", className, ...props }) {
55441
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
55442
+ "data-ui-badge": "true",
55443
+ className: cn$1("inline-flex shrink-0 items-center justify-center gap-0.5 whitespace-nowrap font-semibold", toneClassNames[tone], sizeClassNames$1[size], shapeClassNames[shape], className),
55444
+ ...props
55445
+ });
55446
+ }
55447
+ function formatCountBadgeValue(count, max = 99) {
55448
+ return count > max ? `${max}+` : String(count);
55449
+ }
55450
+ function CountBadge({ count, max = 99, hideWhenZero = false, title, ...props }) {
55451
+ if (hideWhenZero && count <= 0) return null;
55452
+ const value = formatCountBadgeValue(count, max);
55453
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
55454
+ title,
55455
+ ...props,
55456
+ children: value
55457
+ });
55458
+ }
55459
+ //#endregion
55418
55460
  //#region ../core/src/pty-protocol.ts
55419
55461
  var PositiveInt = numberType().int().positive();
55420
55462
  var PtyPlatformSchema = enumType([
@@ -68448,11 +68490,11 @@ function assertPath$1(path2) {
68448
68490
  function removeUrlParams(url) {
68449
68491
  return url.split("?")[0].split("#")[0];
68450
68492
  }
68451
- function escapeRegExp(string) {
68493
+ function escapeRegExp$1(string) {
68452
68494
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
68453
68495
  }
68454
68496
  function replaceAll$1(str, find, replace) {
68455
- return str.replace(new RegExp(escapeRegExp(find), "g"), replace);
68497
+ return str.replace(new RegExp(escapeRegExp$1(find), "g"), replace);
68456
68498
  }
68457
68499
  function normalizeStringPosix(path2, allowAboveRoot) {
68458
68500
  let res = "";
@@ -94427,10 +94469,10 @@ function NotificationEntryButton({ className, badgeClassName, iconClassName }) {
94427
94469
  "aria-label": label,
94428
94470
  title: label,
94429
94471
  "data-notification-entry-button": "true",
94430
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Bell, { className: cn$1("h-4 w-4", iconClassName) }), unreadCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
94431
- className: cn$1("bg-primary ring-background text-primary-foreground absolute -right-1.5 -top-1.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] leading-4 ring-2", badgeClassName),
94432
- "aria-hidden": "true",
94433
- children: unreadCount > 99 ? "99+" : unreadCount
94472
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Bell, { className: cn$1("h-4 w-4", iconClassName) }), unreadCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CountBadge, {
94473
+ count: unreadCount,
94474
+ className: cn$1("ring-background absolute -right-1.5 -top-1.5 ring-2", badgeClassName),
94475
+ "aria-hidden": "true"
94434
94476
  })]
94435
94477
  })
94436
94478
  });
@@ -126159,12 +126201,15 @@ var { codeToHtml, codeToHast, codeToTokens, codeToTokensBase, codeToTokensWithTh
126159
126201
  * Simple markdown renderer with GFM support and shiki code highlighting.
126160
126202
  * For full markdown viewing with ToC, use MarkdownViewer instead.
126161
126203
  */
126162
- function MarkdownContent({ children, className = "", components }) {
126204
+ function MarkdownContent({ children, className = "", components, inlineTextAnnotations = [], blockAnnotations = [] }) {
126205
+ const blockAnnotationByOffset = (0, import_react.useMemo)(() => new Map(blockAnnotations.map((annotation) => [createBlockAnnotationKey$1(annotation), annotation])), [blockAnnotations]);
126206
+ const annotationComponents = (0, import_react.useMemo)(() => createAnnotationComponents(inlineTextAnnotations, blockAnnotationByOffset), [inlineTextAnnotations, blockAnnotationByOffset]);
126163
126207
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
126164
126208
  className: `markdown-content ${className}`,
126165
126209
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Markdown, {
126166
126210
  remarkPlugins: [remarkGfm],
126167
126211
  components: {
126212
+ ...annotationComponents,
126168
126213
  code: CodeBlock,
126169
126214
  pre: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children }),
126170
126215
  ...components
@@ -126173,6 +126218,156 @@ function MarkdownContent({ children, className = "", components }) {
126173
126218
  })
126174
126219
  });
126175
126220
  }
126221
+ function renderInlineAnnotatedChildren(children, annotations) {
126222
+ if (annotations.length === 0) return children;
126223
+ if (typeof children === "string" || typeof children === "number") return renderInlineAnnotatedText(String(children), annotations);
126224
+ if (Array.isArray(children)) return children.map((child, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Fragment, { children: renderInlineAnnotatedChildren(child, annotations) }, `inline-node-${index}`));
126225
+ return children;
126226
+ }
126227
+ function renderInlineAnnotatedText(text, annotations) {
126228
+ const pattern = createInlineAnnotationPattern(annotations);
126229
+ if (!pattern) return text;
126230
+ const annotationByText = new Map(annotations.map((annotation) => [annotation.text, annotation]));
126231
+ const nodes = [];
126232
+ let lastIndex = 0;
126233
+ for (const match of text.matchAll(pattern)) {
126234
+ const matchText = match[0];
126235
+ const start = match.index ?? 0;
126236
+ const annotation = annotationByText.get(matchText);
126237
+ if (!annotation) continue;
126238
+ if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
126239
+ nodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
126240
+ className: annotation.className,
126241
+ ...annotation.dataAttributes,
126242
+ children: matchText
126243
+ }, `inline-annotation-${start}-${matchText}`));
126244
+ lastIndex = start + matchText.length;
126245
+ }
126246
+ if (lastIndex === 0) return text;
126247
+ if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
126248
+ return nodes;
126249
+ }
126250
+ function createInlineAnnotationPattern(annotations) {
126251
+ const terms = Array.from(new Set(annotations.map((annotation) => annotation.text.trim()))).filter(Boolean).sort((left, right) => right.length - left.length);
126252
+ if (terms.length === 0) return void 0;
126253
+ return new RegExp(`\\b(${terms.map(escapeRegExp).join("|")})\\b`, "g");
126254
+ }
126255
+ function escapeRegExp(value) {
126256
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
126257
+ }
126258
+ function getMarkdownNodeSourceStartOffset(node) {
126259
+ if (!node || typeof node !== "object" || !("position" in node)) return void 0;
126260
+ const position = node.position;
126261
+ if (!position || typeof position !== "object" || !("start" in position)) return void 0;
126262
+ const start = position.start;
126263
+ if (!start || typeof start !== "object" || !("offset" in start)) return void 0;
126264
+ const offset = start.offset;
126265
+ return typeof offset === "number" ? offset : void 0;
126266
+ }
126267
+ function getBlockAnnotation(node, annotationByOffset, sourceKind) {
126268
+ const sourceStartOffset = getMarkdownNodeSourceStartOffset(node);
126269
+ if (sourceStartOffset === void 0) return void 0;
126270
+ return (sourceKind ? annotationByOffset.get(createBlockAnnotationKey$1({
126271
+ sourceStartOffset,
126272
+ sourceKind
126273
+ })) : void 0) ?? annotationByOffset.get(createBlockAnnotationKey$1({ sourceStartOffset }));
126274
+ }
126275
+ function createBlockAnnotationKey$1(annotation) {
126276
+ return `${annotation.sourceStartOffset}:${annotation.sourceKind ?? "*"}`;
126277
+ }
126278
+ function mergeClassName(...classNames) {
126279
+ return classNames.filter(Boolean).join(" ") || void 0;
126280
+ }
126281
+ function createAnnotationComponents(inlineAnnotations, blockAnnotationByOffset) {
126282
+ if (inlineAnnotations.length === 0 && blockAnnotationByOffset.size === 0) return {};
126283
+ const render = (children) => renderInlineAnnotatedChildren(children, inlineAnnotations);
126284
+ return {
126285
+ p: ({ children, node, ...props }) => {
126286
+ const annotation = getBlockAnnotation(node, blockAnnotationByOffset, "paragraph");
126287
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
126288
+ ...props,
126289
+ ...annotation?.dataAttributes,
126290
+ className: mergeClassName(props.className, annotation?.className),
126291
+ children: render(children)
126292
+ });
126293
+ },
126294
+ ul: ({ children, node, ...props }) => {
126295
+ const annotation = getBlockAnnotation(node, blockAnnotationByOffset, "list");
126296
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
126297
+ ...props,
126298
+ ...annotation?.dataAttributes,
126299
+ className: mergeClassName(props.className, annotation?.className),
126300
+ children
126301
+ });
126302
+ },
126303
+ ol: ({ children, node, ...props }) => {
126304
+ const annotation = getBlockAnnotation(node, blockAnnotationByOffset, "list");
126305
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", {
126306
+ ...props,
126307
+ ...annotation?.dataAttributes,
126308
+ className: mergeClassName(props.className, annotation?.className),
126309
+ children
126310
+ });
126311
+ },
126312
+ li: ({ children, node, className, ...props }) => {
126313
+ const annotation = getBlockAnnotation(node, blockAnnotationByOffset, "listItem");
126314
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", {
126315
+ ...props,
126316
+ ...annotation?.dataAttributes,
126317
+ className: mergeClassName(className, annotation?.className),
126318
+ children: render(children)
126319
+ });
126320
+ },
126321
+ strong: ({ children, node, ...props }) => {
126322
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", {
126323
+ ...props,
126324
+ children: render(children)
126325
+ });
126326
+ },
126327
+ em: ({ children, node, ...props }) => {
126328
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("em", {
126329
+ ...props,
126330
+ children: render(children)
126331
+ });
126332
+ },
126333
+ a: ({ children, node, ...props }) => {
126334
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
126335
+ ...props,
126336
+ children: render(children)
126337
+ });
126338
+ },
126339
+ blockquote: ({ children, node, ...props }) => {
126340
+ const annotation = getBlockAnnotation(node, blockAnnotationByOffset, "blockquote");
126341
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("blockquote", {
126342
+ ...props,
126343
+ ...annotation?.dataAttributes,
126344
+ className: mergeClassName(props.className, annotation?.className),
126345
+ children
126346
+ });
126347
+ },
126348
+ table: ({ children, node, ...props }) => {
126349
+ const annotation = getBlockAnnotation(node, blockAnnotationByOffset, "table");
126350
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("table", {
126351
+ ...props,
126352
+ ...annotation?.dataAttributes,
126353
+ className: mergeClassName(props.className, annotation?.className),
126354
+ children
126355
+ });
126356
+ },
126357
+ th: ({ children, node, ...props }) => {
126358
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
126359
+ ...props,
126360
+ children: render(children)
126361
+ });
126362
+ },
126363
+ td: ({ children, node, ...props }) => {
126364
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
126365
+ ...props,
126366
+ children: render(children)
126367
+ });
126368
+ }
126369
+ };
126370
+ }
126176
126371
  /** Shared code block component with shiki syntax highlighting */
126177
126372
  function CodeBlock({ children, className }) {
126178
126373
  const [html, setHtml] = (0, import_react.useState)(null);
@@ -126276,32 +126471,38 @@ function Toc({ items, defaultCollapsed = true, className = "" }) {
126276
126471
  const tree = (0, import_react.useMemo)(() => buildTocTree(items), [items]);
126277
126472
  if (items.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("aside", { className: `hidden ${className}` });
126278
126473
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("aside", {
126279
- className: `toc-root scrollbar-none sticky top-0 z-10 max-h-[calc(100cqh-3rem)] self-start overflow-y-auto ${className}`,
126474
+ className: `toc-root sticky top-0 z-10 h-10 w-full min-w-0 max-w-full self-start ${className}`,
126280
126475
  children: [
126281
126476
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: tocStyles }),
126282
126477
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
126283
- className: "toc-narrow border-border bg-background overflow-hidden rounded border",
126478
+ className: "toc-narrow border-border bg-background/60 overflow-hidden rounded border backdrop-blur-sm",
126284
126479
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
126285
126480
  onClick: () => setCollapsed(!collapsed),
126286
- className: `text-foreground flex w-full items-center gap-2 px-3 py-2 ${collapsed ? "" : "border-border border-b"}`,
126481
+ className: `text-foreground flex w-full min-w-0 items-center gap-2 px-3 py-2 ${collapsed ? "" : "border-border border-b"}`,
126287
126482
  "aria-label": collapsed ? "Show table of contents" : "Hide table of contents",
126288
126483
  children: [
126289
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(List, { className: "h-4 w-4" }),
126484
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(List, { className: "h-4 w-4 shrink-0" }),
126290
126485
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
126291
- className: "text-sm",
126486
+ className: "min-w-0 truncate text-sm",
126292
126487
  children: "Contents"
126293
126488
  }),
126294
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: `ml-auto h-4 w-4 transition-transform ${collapsed ? "" : "rotate-180"}` })
126489
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: `ml-auto h-4 w-4 shrink-0 transition-transform ${collapsed ? "" : "rotate-180"}` })
126295
126490
  ]
126296
- }), !collapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TocTree, { nodes: tree })]
126491
+ }), !collapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
126492
+ className: "toc-scroll toc-narrow-scroll scrollbar-thin scrollbar-track-transparent min-h-0 overflow-y-auto overscroll-contain p-2",
126493
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TocTree, { nodes: tree })
126494
+ })]
126297
126495
  }),
126298
126496
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("nav", {
126299
- className: "toc-wide flex flex-col",
126497
+ className: "toc-wide min-w-0 max-w-full flex-col overflow-hidden",
126300
126498
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
126301
- className: "text-muted-foreground flex items-center gap-2 px-3 py-2 text-xs font-medium uppercase tracking-wide",
126302
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(List, { className: "h-3.5 w-3.5" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "On this page" })]
126499
+ className: "text-muted-foreground flex min-w-0 items-center gap-2 px-3 py-2 text-xs font-medium uppercase tracking-wide",
126500
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(List, { className: "h-3.5 w-3.5 shrink-0" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
126501
+ className: "min-w-0 truncate",
126502
+ children: "On this page"
126503
+ })]
126303
126504
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
126304
- className: "scrollbar-thin scrollbar-track-transparent min-h-0 flex-1 overflow-y-auto p-2",
126505
+ className: "toc-scroll toc-wide-scroll scrollbar-thin scrollbar-track-transparent min-h-0 flex-1 overflow-y-auto overscroll-contain p-2",
126305
126506
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TocTree, { nodes: tree })
126306
126507
  })]
126307
126508
  })
@@ -126312,7 +126513,7 @@ function Toc({ items, defaultCollapsed = true, className = "" }) {
126312
126513
  * Find the nearest scrollable ancestor element.
126313
126514
  */
126314
126515
  function findScrollableParent(element) {
126315
- return element.closest(".toc-root");
126516
+ return element.closest(".toc-scroll") ?? element.closest(".toc-root");
126316
126517
  }
126317
126518
  /**
126318
126519
  * Scroll element into view within its scrollable container using scrollTo.
@@ -126374,10 +126575,18 @@ function TocTree({ nodes, depth = 0 }) {
126374
126575
  var tocStyles = String.raw`
126375
126576
  /* Default: narrow mode (collapsible) */
126376
126577
  .toc-narrow {
126377
- display: block;
126578
+ display: flex;
126579
+ flex-direction: column;
126580
+ max-height: min(20rem, calc(100cqh - 2rem), calc(100svh - 2rem));
126581
+ max-width: 100%;
126582
+ min-width: 0;
126583
+ }
126584
+ .toc-narrow-scroll {
126585
+ max-height: min(18rem, calc(100cqh - 5rem), calc(100svh - 5rem));
126378
126586
  }
126379
126587
  .toc-wide {
126380
126588
  display: none;
126589
+ max-height: min(calc(100cqh - 3rem), calc(100svh - 3rem));
126381
126590
  }
126382
126591
 
126383
126592
  /* Wide container: show sidebar mode */
@@ -126386,7 +126595,19 @@ var tocStyles = String.raw`
126386
126595
  display: none;
126387
126596
  }
126388
126597
  .toc-wide {
126389
- display: block;
126598
+ display: flex;
126599
+ }
126600
+ }
126601
+
126602
+ @supports not (height: 100cqh) {
126603
+ .toc-narrow {
126604
+ max-height: min(20rem, calc(100svh - 2rem));
126605
+ }
126606
+ .toc-narrow-scroll {
126607
+ max-height: min(18rem, calc(100svh - 5rem));
126608
+ }
126609
+ .toc-wide {
126610
+ max-height: calc(100svh - 3rem);
126390
126611
  }
126391
126612
  }
126392
126613
 
@@ -126582,26 +126803,41 @@ function useSectionTimeline() {
126582
126803
  *
126583
126804
  * 嵌套时自动检测父级 Context,只渲染内容不显示 ToC sidebar。
126584
126805
  */
126585
- function MarkdownViewer({ markdown, className = "", onReady, collectToc = true }) {
126806
+ function MarkdownViewer({ markdown, className = "", onReady, collectToc = true, headingTransform, inlineTextAnnotations, blockAnnotations }) {
126586
126807
  const isNested = !!useTocContext();
126587
126808
  if (!collectToc) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlainMarkdownViewer, {
126588
126809
  markdown,
126589
- className
126810
+ className,
126811
+ headingTransform,
126812
+ inlineTextAnnotations,
126813
+ blockAnnotations
126590
126814
  });
126591
126815
  if (isNested) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NestedMarkdownViewer, {
126592
126816
  markdown,
126593
- className
126817
+ className,
126818
+ headingTransform,
126819
+ inlineTextAnnotations,
126820
+ blockAnnotations
126594
126821
  });
126595
126822
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RootMarkdownViewer, {
126596
126823
  markdown,
126597
126824
  className,
126598
- onReady
126825
+ onReady,
126826
+ headingTransform,
126827
+ inlineTextAnnotations,
126828
+ blockAnnotations
126599
126829
  });
126600
126830
  }
126601
- function PlainMarkdownViewer({ markdown, className = "" }) {
126602
- if (typeof markdown === "string") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarkdownContent, {
126831
+ function PlainMarkdownViewer({ markdown, className = "", headingTransform, inlineTextAnnotations, blockAnnotations }) {
126832
+ if (typeof markdown === "string") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StringMarkdownContent, {
126833
+ markdown,
126603
126834
  className,
126604
- children: markdown
126835
+ collector: new TocCollector(),
126836
+ levelOffset: 0,
126837
+ headingTransform,
126838
+ inlineTextAnnotations,
126839
+ blockAnnotations,
126840
+ collectToc: false
126605
126841
  });
126606
126842
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlainBuilderMarkdownContent, {
126607
126843
  builder: markdown,
@@ -126644,7 +126880,7 @@ function PlainBuilderMarkdownContent({ builder, className }) {
126644
126880
  children: builder(components)
126645
126881
  });
126646
126882
  }
126647
- function RootMarkdownViewer({ markdown, className, onReady }) {
126883
+ function RootMarkdownViewer({ markdown, className, onReady, headingTransform, inlineTextAnnotations, blockAnnotations }) {
126648
126884
  const [tocItems, setTocItems] = (0, import_react.useState)([]);
126649
126885
  const collectorRef = (0, import_react.useRef)(null);
126650
126886
  const readyCalledRef = (0, import_react.useRef)(false);
@@ -126666,7 +126902,10 @@ function RootMarkdownViewer({ markdown, className, onReady }) {
126666
126902
  const content = typeof markdown === "string" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StringMarkdownContent, {
126667
126903
  markdown,
126668
126904
  collector,
126669
- levelOffset: 0
126905
+ levelOffset: 0,
126906
+ headingTransform,
126907
+ inlineTextAnnotations,
126908
+ blockAnnotations
126670
126909
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BuilderMarkdownContent, {
126671
126910
  builder: markdown,
126672
126911
  collector,
@@ -126679,27 +126918,30 @@ function RootMarkdownViewer({ markdown, className, onReady }) {
126679
126918
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
126680
126919
  className: `@container-[size] h-full ${className}`,
126681
126920
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: viewerStyles }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(MarkdownContainer, {
126682
- className: "viewer-scroll viewer-layout gap-6",
126921
+ className: "viewer-scroll toc-page-layout viewer-layout gap-6",
126683
126922
  timelineScope,
126684
126923
  enableHashNavigation: true,
126685
126924
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Toc, {
126686
126925
  items: tocItems,
126687
- className: "viewer-toc"
126926
+ className: "toc-page-sidebar viewer-toc"
126688
126927
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
126689
- className: "viewer-content min-w-0",
126928
+ className: "toc-page-content viewer-content min-w-0",
126690
126929
  children: content
126691
126930
  })]
126692
126931
  })]
126693
126932
  })
126694
126933
  });
126695
126934
  }
126696
- function NestedMarkdownViewer({ markdown, className }) {
126935
+ function NestedMarkdownViewer({ markdown, className, headingTransform, inlineTextAnnotations, blockAnnotations }) {
126697
126936
  const { collector, levelOffset } = useTocContext();
126698
126937
  return typeof markdown === "string" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StringMarkdownContent, {
126699
126938
  markdown,
126700
126939
  className,
126701
126940
  collector,
126702
- levelOffset
126941
+ levelOffset,
126942
+ headingTransform,
126943
+ inlineTextAnnotations,
126944
+ blockAnnotations
126703
126945
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BuilderMarkdownContent, {
126704
126946
  builder: markdown,
126705
126947
  className,
@@ -126707,21 +126949,33 @@ function NestedMarkdownViewer({ markdown, className }) {
126707
126949
  levelOffset
126708
126950
  });
126709
126951
  }
126710
- function StringMarkdownContent({ markdown, collector, levelOffset, className }) {
126952
+ function StringMarkdownContent({ markdown, collector, levelOffset, className, headingTransform, inlineTextAnnotations, blockAnnotations, collectToc = true }) {
126711
126953
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarkdownContent, {
126712
126954
  className,
126713
126955
  components: (0, import_react.useMemo)(() => {
126714
126956
  const createHeading = (level) => {
126715
- return function Heading({ children }) {
126957
+ return function Heading({ children, node }) {
126716
126958
  const text = extractTextFromChildren(children);
126717
126959
  const sectionTimelineIndex = useSectionTimeline();
126960
+ const sourceRange = getMarkdownNodeSourceRange(node);
126718
126961
  const adjustedLevel = Math.min(level + levelOffset, 6);
126719
- const registration = sectionTimelineIndex === null ? collector.add(text, adjustedLevel) : collector.bindSectionHeading(sectionTimelineIndex, text, adjustedLevel);
126962
+ const transform = headingTransform?.({
126963
+ sourceLevel: level,
126964
+ level: adjustedLevel,
126965
+ text,
126966
+ ...sourceRange.start !== void 0 ? { sourceStartOffset: sourceRange.start } : {},
126967
+ ...sourceRange.end !== void 0 ? { sourceEndOffset: sourceRange.end } : {}
126968
+ });
126969
+ const tocLabel = transform?.tocLabel ?? text;
126970
+ const registration = collectToc === false ? collector.add(tocLabel, adjustedLevel, transform?.id) : sectionTimelineIndex === null ? collector.add(tocLabel, adjustedLevel, transform?.id) : collector.bindSectionHeading(sectionTimelineIndex, tocLabel, adjustedLevel, transform?.id);
126720
126971
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HeadingElement, {
126721
126972
  level: adjustedLevel,
126722
126973
  id: registration.id,
126723
126974
  timelineIndex: registration.timelineIndex,
126724
- bindTimeline: registration.binding === "heading",
126975
+ bindTimeline: collectToc !== false && registration.binding === "heading",
126976
+ className: transform?.className,
126977
+ dataAttributes: transform?.dataAttributes,
126978
+ suffix: transform?.suffix,
126725
126979
  children
126726
126980
  });
126727
126981
  };
@@ -126734,19 +126988,27 @@ function StringMarkdownContent({ markdown, collector, levelOffset, className })
126734
126988
  h5: createHeading(5),
126735
126989
  h6: createHeading(6)
126736
126990
  };
126737
- }, [collector, levelOffset]),
126991
+ }, [
126992
+ collector,
126993
+ levelOffset,
126994
+ headingTransform,
126995
+ collectToc
126996
+ ]),
126997
+ inlineTextAnnotations,
126998
+ blockAnnotations,
126738
126999
  children: markdown
126739
127000
  });
126740
127001
  }
126741
127002
  function BuilderMarkdownContent({ builder, collector, levelOffset, className }) {
126742
127003
  const components = (0, import_react.useMemo)(() => {
126743
127004
  const createHeading = (level) => {
126744
- return function Heading({ id: fixedId, className, children }) {
127005
+ return function Heading({ id: fixedId, className, children, tocLabel }) {
126745
127006
  const currentLevelOffset = useTocContext()?.levelOffset ?? levelOffset;
126746
127007
  const sectionTimelineIndex = useSectionTimeline();
126747
127008
  const text = extractTextFromChildren(children);
127009
+ const label = tocLabel ?? text;
126748
127010
  const adjustedLevel = Math.min(level + currentLevelOffset, 6);
126749
- const registration = sectionTimelineIndex === null ? collector.add(text, adjustedLevel, fixedId) : collector.bindSectionHeading(sectionTimelineIndex, text, adjustedLevel, fixedId);
127011
+ const registration = sectionTimelineIndex === null ? collector.add(label, adjustedLevel, fixedId) : collector.bindSectionHeading(sectionTimelineIndex, label, adjustedLevel, fixedId);
126750
127012
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HeadingElement, {
126751
127013
  level: adjustedLevel,
126752
127014
  id: registration.id,
@@ -126793,44 +127055,74 @@ function SectionElement({ timelineIndex, children, className }) {
126793
127055
  children
126794
127056
  });
126795
127057
  }
126796
- function HeadingElement({ level, id, timelineIndex, bindTimeline = false, children, className }) {
127058
+ function HeadingElement({ level, id, timelineIndex, bindTimeline = false, children, suffix, className, dataAttributes }) {
126797
127059
  const style = bindTimeline && timelineIndex !== void 0 ? { viewTimelineName: `--toc-${timelineIndex}` } : void 0;
126798
127060
  switch (level) {
126799
- case 1: return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
127061
+ case 1: return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h1", {
126800
127062
  id,
126801
127063
  className,
126802
127064
  style,
126803
- children
127065
+ ...dataAttributes,
127066
+ children: [
127067
+ children,
127068
+ suffix ? " " : null,
127069
+ suffix
127070
+ ]
126804
127071
  });
126805
- case 2: return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
127072
+ case 2: return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h2", {
126806
127073
  id,
126807
127074
  className,
126808
127075
  style,
126809
- children
127076
+ ...dataAttributes,
127077
+ children: [
127078
+ children,
127079
+ suffix ? " " : null,
127080
+ suffix
127081
+ ]
126810
127082
  });
126811
- case 3: return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", {
127083
+ case 3: return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", {
126812
127084
  id,
126813
127085
  className,
126814
127086
  style,
126815
- children
127087
+ ...dataAttributes,
127088
+ children: [
127089
+ children,
127090
+ suffix ? " " : null,
127091
+ suffix
127092
+ ]
126816
127093
  });
126817
- case 4: return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", {
127094
+ case 4: return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h4", {
126818
127095
  id,
126819
127096
  className,
126820
127097
  style,
126821
- children
127098
+ ...dataAttributes,
127099
+ children: [
127100
+ children,
127101
+ suffix ? " " : null,
127102
+ suffix
127103
+ ]
126822
127104
  });
126823
- case 5: return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h5", {
127105
+ case 5: return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h5", {
126824
127106
  id,
126825
127107
  className,
126826
127108
  style,
126827
- children
127109
+ ...dataAttributes,
127110
+ children: [
127111
+ children,
127112
+ suffix ? " " : null,
127113
+ suffix
127114
+ ]
126828
127115
  });
126829
- case 6: return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h6", {
127116
+ case 6: return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h6", {
126830
127117
  id,
126831
127118
  className,
126832
127119
  style,
126833
- children
127120
+ ...dataAttributes,
127121
+ children: [
127122
+ children,
127123
+ suffix ? " " : null,
127124
+ suffix
127125
+ ]
126834
127126
  });
126835
127127
  }
126836
127128
  }
@@ -126866,6 +127158,24 @@ function extractTextFromChildren(children) {
126866
127158
  if (typeof children === "object" && "props" in children) return extractTextFromChildren(children.props?.children);
126867
127159
  return "";
126868
127160
  }
127161
+ function getMarkdownNodeSourceRange(node) {
127162
+ if (!node || typeof node !== "object" || !("position" in node)) return {};
127163
+ const position = node.position;
127164
+ if (!position || typeof position !== "object") return {};
127165
+ const start = readMarkdownNodeOffset(position, "start");
127166
+ const end = readMarkdownNodeOffset(position, "end");
127167
+ return {
127168
+ ...start !== void 0 ? { start } : {},
127169
+ ...end !== void 0 ? { end } : {}
127170
+ };
127171
+ }
127172
+ function readMarkdownNodeOffset(position, key) {
127173
+ if (!(key in position)) return void 0;
127174
+ const point = position[key];
127175
+ if (!point || typeof point !== "object" || !("offset" in point)) return void 0;
127176
+ const offset = point.offset;
127177
+ return typeof offset === "number" ? offset : void 0;
127178
+ }
126869
127179
  /** 比较两个 TocItem 数组是否相等 */
126870
127180
  function arraysEqual(a, b) {
126871
127181
  if (a.length !== b.length) return false;
@@ -126873,28 +127183,7 @@ function arraysEqual(a, b) {
126873
127183
  }
126874
127184
  /** CSS for container queries layout */
126875
127185
  var viewerStyles = String.raw`
126876
- /* Container query based layout */
126877
- .viewer-layout {
126878
- display: block;
126879
- }
126880
- .viewer-toc {
126881
- margin-bottom: 1rem;
126882
- }
126883
-
126884
- /* Wide container: grid layout with ToC on right */
126885
- @container (min-width: 768px) {
126886
- .viewer-layout {
126887
- display: grid;
126888
- grid-template-columns: minmax(0, 1fr) 180px;
126889
- }
126890
- .viewer-toc {
126891
- order: 2;
126892
- margin-bottom: 0;
126893
- }
126894
- .viewer-content {
126895
- order: 1;
126896
- }
126897
- }
127186
+ /* MarkdownViewer keeps layout hooks local; shared ToC geometry lives in index.css. */
126898
127187
  `;
126899
127188
  //#endregion
126900
127189
  //#region src/components/tabs.tsx
@@ -146263,8 +146552,11 @@ function ChangeList() {
146263
146552
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
146264
146553
  className: "flex flex-col items-end gap-1 text-right text-sm",
146265
146554
  children: [
146266
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
146267
- className: `rounded border px-1.5 py-0.5 text-[11px] font-medium ${phase.toneClass}`,
146555
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
146556
+ tone: "custom",
146557
+ size: "sm",
146558
+ shape: "box",
146559
+ className: `border ${phase.toneClass}`,
146268
146560
  children: phase.label
146269
146561
  }),
146270
146562
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -149673,7 +149965,7 @@ function Switch({ checked, onCheckedChange, ariaLabel, id, name, required, disab
149673
149965
  className: cn$1("inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border border-transparent p-0.5 outline-none transition-colors", "focus-visible:ring-primary focus-visible:ring-1", checked ? "border-primary bg-primary" : "bg-muted-foreground/30 hover:bg-muted-foreground/40", disabled && "cursor-not-allowed opacity-50", readOnly && !disabled && "cursor-default", className),
149674
149966
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
149675
149967
  "aria-hidden": "true",
149676
- className: cn$1("pointer-events-none block h-5 w-5 rounded-full bg-white shadow-sm transition-transform", checked ? "translate-x-5" : "translate-x-0", thumbClassName)
149968
+ className: cn$1("pointer-events-none block h-5 w-5 rounded-full bg-white transition-transform", checked ? "translate-x-5" : "translate-x-0", thumbClassName)
149677
149969
  })
149678
149970
  })] });
149679
149971
  }
@@ -152672,8 +152964,11 @@ function DiffStat({ diff, className = "" }) {
152672
152964
  }
152673
152965
  function GitFilesBadge({ files }) {
152674
152966
  if (files <= 0) return null;
152675
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
152676
- className: "inline-flex items-center rounded border border-zinc-500/35 bg-zinc-500/10 px-[0.15rem] py-0 font-mono text-[10px] text-zinc-700 dark:border-zinc-300/40 dark:bg-zinc-300/15 dark:text-zinc-100",
152967
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Badge, {
152968
+ tone: "custom",
152969
+ size: "xs",
152970
+ shape: "box",
152971
+ className: "h-auto min-w-0 border border-zinc-500/35 bg-zinc-500/10 px-[0.15rem] py-0 font-mono text-[10px] font-normal text-zinc-700 dark:border-zinc-300/40 dark:bg-zinc-300/15 dark:text-zinc-100",
152677
152972
  children: [files, "f"]
152678
152973
  });
152679
152974
  }
@@ -153161,8 +153456,11 @@ function Dashboard() {
153161
153456
  className: "text-xs font-semibold",
153162
153457
  children: schema.schemaName
153163
153458
  })
153164
- }), schema.readyToArchive > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
153165
- className: "text-muted-foreground rounded border px-1 py-0.5 text-[10px]",
153459
+ }), schema.readyToArchive > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Badge, {
153460
+ tone: "custom",
153461
+ size: "xs",
153462
+ shape: "box",
153463
+ className: "text-muted-foreground border",
153166
153464
  children: ["archive-ready ", schema.readyToArchive]
153167
153465
  }) : null]
153168
153466
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
@@ -153458,8 +153756,11 @@ function Dashboard() {
153458
153756
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
153459
153757
  className: "shrink-0 text-right text-sm",
153460
153758
  children: [
153461
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
153462
- className: `rounded border px-1.5 py-0.5 text-[11px] font-medium ${phase.toneClass}`,
153759
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
153760
+ tone: "custom",
153761
+ size: "sm",
153762
+ shape: "box",
153763
+ className: `border ${phase.toneClass}`,
153463
153764
  children: phase.label
153464
153765
  }),
153465
153766
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -153909,6 +154210,1129 @@ function SpecList() {
153909
154210
  });
153910
154211
  }
153911
154212
  //#endregion
154213
+ //#region ../core/src/markdown-facts.ts
154214
+ function toMarkdownFactKind(type) {
154215
+ switch (type) {
154216
+ case "root":
154217
+ case "heading":
154218
+ case "paragraph":
154219
+ case "list":
154220
+ case "listItem":
154221
+ case "blockquote":
154222
+ case "table":
154223
+ case "tableRow":
154224
+ case "tableCell":
154225
+ case "code":
154226
+ case "thematicBreak":
154227
+ case "html":
154228
+ case "definition":
154229
+ case "footnoteDefinition": return type;
154230
+ default: return "unknown";
154231
+ }
154232
+ }
154233
+ function parseMarkdownFacts(sourceMarkdown) {
154234
+ const root = fromMarkdown(sourceMarkdown, {
154235
+ extensions: [gfm()],
154236
+ mdastExtensions: [gfmFromMarkdown()]
154237
+ });
154238
+ const facts = [];
154239
+ const nodeIdByNode = /* @__PURE__ */ new Map();
154240
+ const visit = (node, parentId) => {
154241
+ const fact = createFact(node, sourceMarkdown, facts.length, parentId);
154242
+ facts.push(fact);
154243
+ nodeIdByNode.set(node, fact.id);
154244
+ if (isParentNode(node)) for (const child of node.children) {
154245
+ if (!isSupportedNode(child)) continue;
154246
+ visit(child, fact.id);
154247
+ const childId = nodeIdByNode.get(child);
154248
+ if (childId) fact.children.push(childId);
154249
+ }
154250
+ };
154251
+ visit(root);
154252
+ return {
154253
+ sourceMarkdown,
154254
+ rootId: "md-1",
154255
+ facts
154256
+ };
154257
+ }
154258
+ function createFact(node, sourceMarkdown, index, parentId) {
154259
+ const base = {
154260
+ id: `md-${index + 1}`,
154261
+ kind: toMarkdownFactKind(node.type),
154262
+ mdastType: node.type,
154263
+ text: getNodeText(node),
154264
+ children: [],
154265
+ range: toSourceRange(sourceMarkdown, node.position),
154266
+ ...parentId ? { parentId } : {}
154267
+ };
154268
+ switch (node.type) {
154269
+ case "heading": return {
154270
+ ...base,
154271
+ depth: node.depth
154272
+ };
154273
+ case "list": return {
154274
+ ...base,
154275
+ ordered: Boolean(node.ordered)
154276
+ };
154277
+ case "listItem": return {
154278
+ ...base,
154279
+ ...typeof node.checked === "boolean" ? { checked: node.checked } : {}
154280
+ };
154281
+ case "code": return {
154282
+ ...base,
154283
+ text: node.value,
154284
+ value: node.value,
154285
+ ...node.lang ? { language: node.lang } : {}
154286
+ };
154287
+ case "html": return {
154288
+ ...base,
154289
+ value: node.value
154290
+ };
154291
+ default: return base;
154292
+ }
154293
+ }
154294
+ function toSourceRange(sourceMarkdown, position) {
154295
+ if (!position) return void 0;
154296
+ const startOffset = position.start.offset;
154297
+ const endOffset = position.end.offset;
154298
+ const rawMarkdown = typeof startOffset === "number" && typeof endOffset === "number" ? sourceMarkdown.slice(startOffset, endOffset) : "";
154299
+ return {
154300
+ start: {
154301
+ line: position.start.line,
154302
+ column: position.start.column,
154303
+ ...typeof startOffset === "number" ? { offset: startOffset } : {}
154304
+ },
154305
+ end: {
154306
+ line: position.end.line,
154307
+ column: position.end.column,
154308
+ ...typeof endOffset === "number" ? { offset: endOffset } : {}
154309
+ },
154310
+ rawMarkdown
154311
+ };
154312
+ }
154313
+ function getNodeText(node) {
154314
+ if (node.type === "code" || node.type === "html") return node.value;
154315
+ return toString$1(node, {
154316
+ includeHtml: true,
154317
+ includeImageAlt: true
154318
+ }).trim();
154319
+ }
154320
+ function isParentNode(node) {
154321
+ return Array.isArray(node.children);
154322
+ }
154323
+ function isSupportedNode(node) {
154324
+ switch (node.type) {
154325
+ case "blockquote":
154326
+ case "code":
154327
+ case "definition":
154328
+ case "footnoteDefinition":
154329
+ case "heading":
154330
+ case "html":
154331
+ case "list":
154332
+ case "listItem":
154333
+ case "paragraph":
154334
+ case "table":
154335
+ case "tableRow":
154336
+ case "tableCell":
154337
+ case "thematicBreak": return true;
154338
+ default: return isParentNode(node);
154339
+ }
154340
+ }
154341
+ //#endregion
154342
+ //#region ../core/src/markdown-reading.ts
154343
+ function createMarkdownReadingDocument(sourceMarkdown, plugins = []) {
154344
+ return createMarkdownReadingDocumentFromFacts(parseMarkdownFacts(sourceMarkdown), plugins);
154345
+ }
154346
+ function createMarkdownReadingDocumentFromFacts(factsDocument, plugins = []) {
154347
+ const lookup = createLookup(factsDocument);
154348
+ const annotations = [];
154349
+ for (const rule of plugins.flatMap((plugin) => plugin.annotationRules ?? [])) {
154350
+ const context = createAnnotationContext(lookup, annotations);
154351
+ for (const input of rule.annotate(context)) annotations.push({
154352
+ id: `${rule.id}:${annotations.length + 1}`,
154353
+ ruleId: rule.id,
154354
+ kind: input.kind,
154355
+ targetFactId: input.targetFactId,
154356
+ ...input.sourceSpan ? { sourceSpan: input.sourceSpan } : {},
154357
+ ...input.textSpan ? { textSpan: input.textSpan } : {},
154358
+ confidence: input.confidence,
154359
+ ...input.metadata ? { metadata: input.metadata } : {}
154360
+ });
154361
+ }
154362
+ const projections = {};
154363
+ for (const rule of plugins.flatMap((plugin) => plugin.projectionRules ?? [])) {
154364
+ const context = createProjectionContext(lookup, annotations, projections);
154365
+ const output = rule.project(context);
154366
+ if (output !== void 0) projections[rule.id] = output;
154367
+ }
154368
+ return {
154369
+ ...factsDocument,
154370
+ annotations,
154371
+ projections
154372
+ };
154373
+ }
154374
+ function getMarkdownFactSpan(fact) {
154375
+ const start = fact.range?.start.offset;
154376
+ const end = fact.range?.end.offset;
154377
+ if (typeof start !== "number" || typeof end !== "number") return void 0;
154378
+ return {
154379
+ start,
154380
+ end
154381
+ };
154382
+ }
154383
+ function trimMarkdownSlice(sourceMarkdown, start, end) {
154384
+ return sourceMarkdown.slice(start, Math.max(start, end)).trim();
154385
+ }
154386
+ function getMarkdownHeadingFacts(document) {
154387
+ return document.facts.filter((fact) => fact.kind === "heading" && typeof fact.depth === "number");
154388
+ }
154389
+ function getMarkdownHeadingEnd(headings, index, sourceLength) {
154390
+ const heading = headings[index];
154391
+ if (!heading) return sourceLength;
154392
+ const headingDepth = heading.depth ?? 6;
154393
+ for (let i = index + 1; i < headings.length; i++) {
154394
+ const next = headings[i];
154395
+ if (next && (next.depth ?? 6) <= headingDepth) return getMarkdownFactSpan(next)?.start ?? sourceLength;
154396
+ }
154397
+ return sourceLength;
154398
+ }
154399
+ function getMarkdownAnnotationsForFact(annotations, factId, kind) {
154400
+ return annotations.filter((annotation) => annotation.targetFactId === factId && (!kind || annotation.kind === kind));
154401
+ }
154402
+ function getMarkdownAnnotation(annotations, factId, kind) {
154403
+ return annotations.find((annotation) => annotation.targetFactId === factId && annotation.kind === kind);
154404
+ }
154405
+ function buildMarkdownParentMap(facts) {
154406
+ const factById = new Map(facts.map((fact) => [fact.id, fact]));
154407
+ const parentById = /* @__PURE__ */ new Map();
154408
+ for (const fact of facts) {
154409
+ if (!fact.parentId) continue;
154410
+ const parent = factById.get(fact.parentId);
154411
+ if (parent) parentById.set(fact.id, parent);
154412
+ }
154413
+ return parentById;
154414
+ }
154415
+ function createLookup(document) {
154416
+ return {
154417
+ sourceMarkdown: document.sourceMarkdown,
154418
+ rootId: document.rootId,
154419
+ facts: document.facts,
154420
+ factById: new Map(document.facts.map((fact) => [fact.id, fact])),
154421
+ parentById: buildMarkdownParentMap(document.facts)
154422
+ };
154423
+ }
154424
+ function createAnnotationContext(lookup, previousAnnotations) {
154425
+ return {
154426
+ ...lookup,
154427
+ previousAnnotations,
154428
+ getAnnotationsForFact: (factId, kind) => getMarkdownAnnotationsForFact(previousAnnotations, factId, kind),
154429
+ getAnnotation: (factId, kind) => getMarkdownAnnotation(previousAnnotations, factId, kind)
154430
+ };
154431
+ }
154432
+ function createProjectionContext(lookup, annotations, projections) {
154433
+ return {
154434
+ ...lookup,
154435
+ annotations,
154436
+ projections,
154437
+ getAnnotationsForFact: (factId, kind) => getMarkdownAnnotationsForFact(annotations, factId, kind),
154438
+ getAnnotation: (factId, kind) => getMarkdownAnnotation(annotations, factId, kind)
154439
+ };
154440
+ }
154441
+ //#endregion
154442
+ //#region ../core/src/openspec-annotations.ts
154443
+ var OPEN_SPEC_ANNOTATION_RULES = {
154444
+ documentTitle: "openspec.heading.document-title.v2",
154445
+ purposeSection: "openspec.heading.purpose-section.v2",
154446
+ requirementsSection: "openspec.heading.requirements-section.v2",
154447
+ requirementPrefix: "openspec.heading.requirement-prefix.v2",
154448
+ requirementUnderSection: "openspec.heading.requirement-under-section.v2",
154449
+ requirementCapabilityPrefix: "openspec.heading.capability-prefix.v2",
154450
+ requirementCapabilityText: "openspec.heading.capability-text.v2",
154451
+ scenarioPrefix: "openspec.heading.scenario-prefix.v2",
154452
+ scenarioExamplePrefix: "openspec.heading.example-prefix.v2",
154453
+ scenarioStepHeading: "openspec.heading.step-backed-scenario.v2",
154454
+ scenarioStep: "openspec.list-item.scenario-step.v2",
154455
+ keyword: "openspec.inline.keyword.v2"
154456
+ };
154457
+ var REQUIREMENT_PREFIX_PATTERN = /^(?:Requirement|Capability):\s*/i;
154458
+ var SCENARIO_PREFIX_PATTERN = /^(?:Scenario|Example):\s*/i;
154459
+ var SCENARIO_STEP_KEYWORDS = [
154460
+ "GIVEN",
154461
+ "WHEN",
154462
+ "THEN",
154463
+ "AND",
154464
+ "BUT"
154465
+ ];
154466
+ var REQUIREMENT_KEYWORD_PATTERN = new RegExp(`\\b(${[
154467
+ "SHALL",
154468
+ "MUST",
154469
+ "SHOULD",
154470
+ "MAY"
154471
+ ].join("|")})\\b`, "g");
154472
+ var SCENARIO_STEP_PATTERN = new RegExp(`^\\s*(?:[-*+]\\s+)?(?:\\[[ xX]\\]\\s+)?(?:\\*\\*)?(${SCENARIO_STEP_KEYWORDS.join("|")})\\b(?:\\*\\*)?\\s*:?\\s*(.+?)\\s*$`, "i");
154473
+ var REQUIREMENT_SECTION_TERMS = [
154474
+ "requirement",
154475
+ "specification",
154476
+ "capability",
154477
+ "capabilities"
154478
+ ];
154479
+ var PURPOSE_SECTION_TERMS = [
154480
+ "purpose",
154481
+ "overview",
154482
+ "objective",
154483
+ "goal",
154484
+ "goals"
154485
+ ];
154486
+ var REQUIREMENT_BODY_SIGNAL_PATTERN = /\b(SHALL|MUST|SHOULD|MAY|CAN|WILL)\b/i;
154487
+ var NON_SCENARIO_HEADING_PATTERN = /^(notes?|details?|rationale|reason|migration|examples?|open questions?)$/i;
154488
+ var builtinOpenSpecReadingPlugin = {
154489
+ id: "openspec.builtin-reading.v2",
154490
+ annotationRules: [
154491
+ {
154492
+ id: OPEN_SPEC_ANNOTATION_RULES.documentTitle,
154493
+ annotate(context) {
154494
+ return context.facts.flatMap((fact) => {
154495
+ if (fact.kind !== "heading" || fact.depth !== 1) return [];
154496
+ return [{
154497
+ kind: "document-title",
154498
+ targetFactId: fact.id,
154499
+ confidence: "strong",
154500
+ metadata: { title: fact.text }
154501
+ }];
154502
+ });
154503
+ }
154504
+ },
154505
+ {
154506
+ id: OPEN_SPEC_ANNOTATION_RULES.purposeSection,
154507
+ annotate(context) {
154508
+ return context.facts.flatMap((fact) => {
154509
+ if (fact.kind !== "heading" || fact.depth !== 2) return [];
154510
+ if (!matchesAnyTerm(fact.text, PURPOSE_SECTION_TERMS)) return [];
154511
+ return [{
154512
+ kind: "purpose-section",
154513
+ targetFactId: fact.id,
154514
+ confidence: isExactTerm(fact.text, ["Purpose", "Overview"]) ? "strong" : "weak",
154515
+ metadata: { title: fact.text }
154516
+ }];
154517
+ });
154518
+ }
154519
+ },
154520
+ {
154521
+ id: OPEN_SPEC_ANNOTATION_RULES.requirementsSection,
154522
+ annotate(context) {
154523
+ return context.facts.flatMap((fact) => {
154524
+ if (fact.kind !== "heading" || fact.depth !== 2) return [];
154525
+ if (!matchesAnyTerm(fact.text, REQUIREMENT_SECTION_TERMS)) return [];
154526
+ return [{
154527
+ kind: "requirements-section",
154528
+ targetFactId: fact.id,
154529
+ confidence: fact.text.toLowerCase().includes("requirement") ? "strong" : "weak",
154530
+ metadata: { title: fact.text }
154531
+ }];
154532
+ });
154533
+ }
154534
+ },
154535
+ {
154536
+ id: OPEN_SPEC_ANNOTATION_RULES.requirementPrefix,
154537
+ annotate(context) {
154538
+ return context.facts.flatMap((fact) => {
154539
+ if (fact.kind !== "heading" || fact.depth !== 3) return [];
154540
+ if (!/^Requirement:\s*/i.test(fact.text)) return [];
154541
+ return [createRequirementAnnotation(fact, "strong")];
154542
+ });
154543
+ }
154544
+ },
154545
+ {
154546
+ id: OPEN_SPEC_ANNOTATION_RULES.requirementCapabilityPrefix,
154547
+ annotate(context) {
154548
+ return context.facts.flatMap((fact) => {
154549
+ if (fact.kind !== "heading" || fact.depth !== 3) return [];
154550
+ if (!/^Capability:\s*/i.test(fact.text)) return [];
154551
+ return [createRequirementAnnotation(fact, "weak")];
154552
+ });
154553
+ }
154554
+ },
154555
+ {
154556
+ id: OPEN_SPEC_ANNOTATION_RULES.requirementUnderSection,
154557
+ annotate(context) {
154558
+ return getRequirementSections(context).flatMap((section) => {
154559
+ return getChildHeadings(context, section, 3).flatMap((fact) => {
154560
+ if (context.getAnnotation(fact.id, "requirement")) return [];
154561
+ if (REQUIREMENT_PREFIX_PATTERN.test(fact.text)) return [];
154562
+ if (!hasRequirementBodySignals(context, fact, section.end)) return [];
154563
+ return [{
154564
+ kind: "requirement",
154565
+ targetFactId: fact.id,
154566
+ confidence: "weak",
154567
+ metadata: { title: fact.text }
154568
+ }];
154569
+ });
154570
+ });
154571
+ }
154572
+ },
154573
+ {
154574
+ id: OPEN_SPEC_ANNOTATION_RULES.requirementCapabilityText,
154575
+ annotate(context) {
154576
+ return getRequirementSections(context).flatMap((section) => {
154577
+ return getChildHeadings(context, section, 3).flatMap((fact) => {
154578
+ if (context.getAnnotation(fact.id, "requirement")) return [];
154579
+ if (!matchesAnyTerm(fact.text, [
154580
+ "capability",
154581
+ "feature",
154582
+ "behavior"
154583
+ ])) return [];
154584
+ return [{
154585
+ kind: "requirement",
154586
+ targetFactId: fact.id,
154587
+ confidence: "weak",
154588
+ metadata: { title: stripRequirementPrefix(fact.text) || fact.text }
154589
+ }];
154590
+ });
154591
+ });
154592
+ }
154593
+ },
154594
+ {
154595
+ id: OPEN_SPEC_ANNOTATION_RULES.scenarioPrefix,
154596
+ annotate(context) {
154597
+ return getRequirementHeadings(context).flatMap((requirement) => {
154598
+ return getNestedHeadings(context, requirement).flatMap((fact) => {
154599
+ if (fact.depth !== 4 || !/^Scenario:\s*/i.test(fact.text)) return [];
154600
+ return [createScenarioAnnotation(fact, "strong")];
154601
+ });
154602
+ });
154603
+ }
154604
+ },
154605
+ {
154606
+ id: OPEN_SPEC_ANNOTATION_RULES.scenarioExamplePrefix,
154607
+ annotate(context) {
154608
+ return getRequirementHeadings(context).flatMap((requirement) => {
154609
+ return getNestedHeadings(context, requirement).flatMap((fact) => {
154610
+ if (fact.depth !== 4 || !/^Example:\s*/i.test(fact.text)) return [];
154611
+ return [createScenarioAnnotation(fact, "weak")];
154612
+ });
154613
+ });
154614
+ }
154615
+ },
154616
+ {
154617
+ id: OPEN_SPEC_ANNOTATION_RULES.scenarioStepHeading,
154618
+ annotate(context) {
154619
+ return getRequirementHeadings(context).flatMap((requirement) => {
154620
+ return getNestedHeadings(context, requirement).flatMap((fact) => {
154621
+ if (fact.depth !== 4) return [];
154622
+ if (context.getAnnotation(fact.id, "scenario")) return [];
154623
+ if (NON_SCENARIO_HEADING_PATTERN.test(fact.text)) return [];
154624
+ if (!hasScenarioStepSignals(context, fact)) return [];
154625
+ return [{
154626
+ kind: "scenario",
154627
+ targetFactId: fact.id,
154628
+ confidence: "weak",
154629
+ metadata: { title: stripScenarioPrefix(fact.text) || fact.text }
154630
+ }];
154631
+ });
154632
+ });
154633
+ }
154634
+ },
154635
+ {
154636
+ id: OPEN_SPEC_ANNOTATION_RULES.scenarioStep,
154637
+ annotate(context) {
154638
+ const scenarioSections = getScenarioSections(context);
154639
+ return context.facts.flatMap((fact) => {
154640
+ if (fact.kind !== "listItem") return [];
154641
+ if (!isWithinAnySection(fact, scenarioSections)) return [];
154642
+ const metadata = parseScenarioStepFact(fact);
154643
+ if (!metadata) return [];
154644
+ return [{
154645
+ kind: "scenario-step",
154646
+ targetFactId: fact.id,
154647
+ confidence: "strong",
154648
+ metadata
154649
+ }];
154650
+ });
154651
+ }
154652
+ },
154653
+ {
154654
+ id: OPEN_SPEC_ANNOTATION_RULES.keyword,
154655
+ annotate(context) {
154656
+ const scenarioKeywordAnnotations = context.previousAnnotations.flatMap((annotation) => {
154657
+ if (annotation.kind !== "scenario-step") return [];
154658
+ const fact = context.factById.get(annotation.targetFactId);
154659
+ if (!fact) return [];
154660
+ return findScenarioStepKeyword(fact, readAnnotationKeyword(annotation.metadata));
154661
+ });
154662
+ const requirementKeywordAnnotations = context.facts.flatMap((fact) => {
154663
+ if (!canAnnotateInlineKeywords(fact)) return [];
154664
+ return findRequirementKeywords(fact);
154665
+ });
154666
+ return [...scenarioKeywordAnnotations, ...requirementKeywordAnnotations];
154667
+ }
154668
+ }
154669
+ ]
154670
+ };
154671
+ function createRequirementAnnotation(fact, confidence) {
154672
+ return {
154673
+ kind: "requirement",
154674
+ targetFactId: fact.id,
154675
+ confidence,
154676
+ metadata: { title: stripRequirementPrefix(fact.text) || fact.text }
154677
+ };
154678
+ }
154679
+ function createScenarioAnnotation(fact, confidence) {
154680
+ return {
154681
+ kind: "scenario",
154682
+ targetFactId: fact.id,
154683
+ confidence,
154684
+ metadata: { title: stripScenarioPrefix(fact.text) || "Scenario" }
154685
+ };
154686
+ }
154687
+ function matchesAnyTerm(text, terms) {
154688
+ const normalized = text.toLowerCase();
154689
+ return terms.some((term) => normalized.includes(term));
154690
+ }
154691
+ function isExactTerm(text, terms) {
154692
+ const normalized = text.trim().toLowerCase();
154693
+ return terms.some((term) => normalized === term.toLowerCase());
154694
+ }
154695
+ function stripRequirementPrefix(text) {
154696
+ return text.replace(REQUIREMENT_PREFIX_PATTERN, "").trim();
154697
+ }
154698
+ function stripScenarioPrefix(text) {
154699
+ return text.replace(SCENARIO_PREFIX_PATTERN, "").trim();
154700
+ }
154701
+ function getHeadingSections(context) {
154702
+ const headings = getMarkdownHeadingFacts(context);
154703
+ return headings.reduce((sections, fact, index) => {
154704
+ const span = getMarkdownFactSpan(fact);
154705
+ if (!span) return sections;
154706
+ sections.push({
154707
+ fact,
154708
+ start: span.start,
154709
+ end: getMarkdownHeadingEnd(headings, index, context.sourceMarkdown.length)
154710
+ });
154711
+ return sections;
154712
+ }, []);
154713
+ }
154714
+ function getRequirementSections(context) {
154715
+ return getHeadingSections(context).filter((section) => context.getAnnotation(section.fact.id, "requirements-section"));
154716
+ }
154717
+ function getChildHeadings(context, section, depth) {
154718
+ return getMarkdownHeadingFacts(context).filter((fact) => {
154719
+ if (fact.depth !== depth) return false;
154720
+ const span = getMarkdownFactSpan(fact);
154721
+ return !!span && span.start > section.start && span.start < section.end;
154722
+ });
154723
+ }
154724
+ function getRequirementHeadings(context) {
154725
+ return getMarkdownHeadingFacts(context).filter((fact) => context.getAnnotation(fact.id, "requirement"));
154726
+ }
154727
+ function getNestedHeadings(context, parentHeading) {
154728
+ const headings = getMarkdownHeadingFacts(context);
154729
+ const parentIndex = headings.findIndex((fact) => fact.id === parentHeading.id);
154730
+ const parentSpan = getMarkdownFactSpan(parentHeading);
154731
+ if (parentIndex < 0 || !parentSpan) return [];
154732
+ const end = getMarkdownHeadingEnd(headings, parentIndex, context.sourceMarkdown.length);
154733
+ return headings.filter((fact) => {
154734
+ const span = getMarkdownFactSpan(fact);
154735
+ return !!span && span.start > parentSpan.start && span.start < end;
154736
+ });
154737
+ }
154738
+ function getScenarioSections(context) {
154739
+ const headings = getMarkdownHeadingFacts(context);
154740
+ return getHeadingSections(context).filter((section) => {
154741
+ if (!context.getAnnotation(section.fact.id, "scenario")) return false;
154742
+ if (NON_SCENARIO_HEADING_PATTERN.test(section.fact.text)) return false;
154743
+ return headings.findIndex((fact) => fact.id === section.fact.id) >= 0;
154744
+ });
154745
+ }
154746
+ function hasRequirementBodySignals(context, requirementHeading, sectionEnd) {
154747
+ const span = getMarkdownFactSpan(requirementHeading);
154748
+ if (!span) return false;
154749
+ const headings = getMarkdownHeadingFacts(context);
154750
+ const index = headings.findIndex((fact) => fact.id === requirementHeading.id);
154751
+ const end = index >= 0 ? getMarkdownHeadingEnd(headings, index, sectionEnd) : sectionEnd;
154752
+ const body = context.sourceMarkdown.slice(span.end, Math.min(end, sectionEnd));
154753
+ return REQUIREMENT_BODY_SIGNAL_PATTERN.test(body);
154754
+ }
154755
+ function hasScenarioStepSignals(context, scenarioHeading) {
154756
+ const span = getMarkdownFactSpan(scenarioHeading);
154757
+ if (!span) return false;
154758
+ const headings = getMarkdownHeadingFacts(context);
154759
+ const index = headings.findIndex((fact) => fact.id === scenarioHeading.id);
154760
+ const end = index >= 0 ? getMarkdownHeadingEnd(headings, index, context.sourceMarkdown.length) : context.sourceMarkdown.length;
154761
+ return context.sourceMarkdown.slice(span.end, end).split("\n").some((line) => SCENARIO_STEP_PATTERN.test(line));
154762
+ }
154763
+ function isWithinAnySection(fact, sections) {
154764
+ const span = getMarkdownFactSpan(fact);
154765
+ if (!span) return false;
154766
+ return sections.some((section) => span.start > section.start && span.start < section.end);
154767
+ }
154768
+ function parseScenarioStepFact(fact) {
154769
+ const rawMarkdown = fact.range?.rawMarkdown.trim() || fact.text;
154770
+ const match = rawMarkdown.match(SCENARIO_STEP_PATTERN) ?? fact.text.match(SCENARIO_STEP_PATTERN);
154771
+ if (!match) return void 0;
154772
+ return {
154773
+ keyword: match[1].toUpperCase(),
154774
+ contentMarkdown: match[2].trim(),
154775
+ rawMarkdown
154776
+ };
154777
+ }
154778
+ function canAnnotateInlineKeywords(fact) {
154779
+ return fact.kind === "paragraph" || fact.kind === "listItem" || fact.kind === "heading" || fact.kind === "tableCell";
154780
+ }
154781
+ function findScenarioStepKeyword(fact, keyword) {
154782
+ if (!isScenarioStepKeywordValue(keyword)) return [];
154783
+ const match = fact.text.match(new RegExp(`^\\s*(${SCENARIO_STEP_KEYWORDS.join("|")})\\b`, "i"));
154784
+ if (!match) return [];
154785
+ const text = match[1];
154786
+ const textStart = match.index ?? 0;
154787
+ const sourceSpan = findSourceSpanForFactText(fact, text);
154788
+ return [{
154789
+ kind: "keyword",
154790
+ targetFactId: fact.id,
154791
+ ...sourceSpan ? { sourceSpan } : {},
154792
+ textSpan: {
154793
+ start: textStart,
154794
+ end: textStart + text.length
154795
+ },
154796
+ confidence: "strong",
154797
+ metadata: {
154798
+ keyword,
154799
+ keywordText: text,
154800
+ keywordRole: "scenario-step"
154801
+ }
154802
+ }];
154803
+ }
154804
+ function findRequirementKeywords(fact) {
154805
+ const span = getMarkdownFactSpan(fact);
154806
+ const text = fact.text;
154807
+ if (!span || !text) return [];
154808
+ return Array.from(text.matchAll(REQUIREMENT_KEYWORD_PATTERN), (match) => {
154809
+ const keyword = match[1].toUpperCase();
154810
+ const textStart = match.index;
154811
+ const textEnd = textStart + match[1].length;
154812
+ const sourceSpan = findSourceSpanForFactText(fact, match[1], textStart);
154813
+ return {
154814
+ kind: "keyword",
154815
+ targetFactId: fact.id,
154816
+ ...sourceSpan ? { sourceSpan } : {},
154817
+ textSpan: {
154818
+ start: textStart,
154819
+ end: textEnd
154820
+ },
154821
+ confidence: "strong",
154822
+ metadata: {
154823
+ keyword,
154824
+ keywordText: match[1],
154825
+ keywordRole: isScenarioStepKeyword$1(keyword) ? "scenario-step" : "requirement-modal"
154826
+ }
154827
+ };
154828
+ });
154829
+ }
154830
+ function isScenarioStepKeywordValue(value) {
154831
+ return typeof value === "string" && SCENARIO_STEP_KEYWORDS.some((stepKeyword) => stepKeyword === value);
154832
+ }
154833
+ function isScenarioStepKeyword$1(keyword) {
154834
+ return SCENARIO_STEP_KEYWORDS.some((stepKeyword) => stepKeyword === keyword);
154835
+ }
154836
+ function readAnnotationKeyword(metadata) {
154837
+ return metadata && "keyword" in metadata ? metadata.keyword : void 0;
154838
+ }
154839
+ function findSourceSpanForFactText(fact, text, textStart = 0) {
154840
+ const factSpan = getMarkdownFactSpan(fact);
154841
+ const rawMarkdown = fact.range?.rawMarkdown;
154842
+ if (!factSpan || !rawMarkdown) return void 0;
154843
+ const rawStart = rawMarkdown.indexOf(text, Math.min(textStart, rawMarkdown.length));
154844
+ const index = rawStart >= 0 ? rawStart : rawMarkdown.indexOf(text);
154845
+ if (index < 0) return void 0;
154846
+ const start = factSpan.start + index;
154847
+ return {
154848
+ start,
154849
+ end: start + text.length
154850
+ };
154851
+ }
154852
+ //#endregion
154853
+ //#region ../core/src/openspec-projection.ts
154854
+ var OPEN_SPEC_SPEC_PROJECTION_ID = "openspec.projection.spec.v2";
154855
+ var OPEN_SPEC_READING_SECTIONS_PROJECTION_ID = "openspec.projection.reading-sections.v2";
154856
+ function createOpenSpecReadingPlugin(options) {
154857
+ return {
154858
+ ...builtinOpenSpecReadingPlugin,
154859
+ id: "openspec.builtin-reading-with-projections.v2",
154860
+ projectionRules: [createOpenSpecReadingSectionsProjectionRule(), createOpenSpecSpecProjectionRule(options)]
154861
+ };
154862
+ }
154863
+ function projectOpenSpecMarkdown(sourceMarkdown, options, plugins = [createOpenSpecReadingPlugin(options)]) {
154864
+ return toProjectedOpenSpecDocument(createMarkdownReadingDocument(sourceMarkdown, plugins));
154865
+ }
154866
+ function getOpenSpecProjectionAnnotation(annotations, factId, kind) {
154867
+ return annotations.find((annotation) => annotation.targetFactId === factId && annotation.kind === kind);
154868
+ }
154869
+ function createOpenSpecReadingSectionsProjectionRule() {
154870
+ return {
154871
+ id: OPEN_SPEC_READING_SECTIONS_PROJECTION_ID,
154872
+ project(context) {
154873
+ return {
154874
+ sections: collectSpecSections(toOpenSpecProjectionContext(context)),
154875
+ requirements: collectRequirementBlocks(toOpenSpecProjectionContext(context))
154876
+ };
154877
+ }
154878
+ };
154879
+ }
154880
+ function createOpenSpecSpecProjectionRule(options) {
154881
+ return {
154882
+ id: OPEN_SPEC_SPEC_PROJECTION_ID,
154883
+ project(context) {
154884
+ return projectOpenSpecContextToSpec(toOpenSpecProjectionContext(context), options);
154885
+ }
154886
+ };
154887
+ }
154888
+ function projectOpenSpecContextToSpec(context, options) {
154889
+ const name = context.annotations.find((annotation) => annotation.kind === "document-title")?.metadata?.title || options.specId;
154890
+ const overviewSection = collectSpecSections(context).find((section) => section.kind === "overview");
154891
+ const overview = overviewSection ? trimMarkdownSlice(context.sourceMarkdown, getContentStartAfterHeading(context, overviewSection.factId, overviewSection.start), overviewSection.end) : "";
154892
+ const requirements = collectRequirementBlocks(context).map((requirement) => projectRequirement(context, requirement));
154893
+ return {
154894
+ id: options.specId,
154895
+ name: name || options.specId,
154896
+ overview: overview.trim(),
154897
+ requirements,
154898
+ metadata: {
154899
+ version: "1.0.0",
154900
+ format: "openspec"
154901
+ }
154902
+ };
154903
+ }
154904
+ function createRequirementText(title, bodyMarkdown, scenarioText) {
154905
+ return [
154906
+ title,
154907
+ bodyMarkdown,
154908
+ scenarioText
154909
+ ].filter((part) => part.trim()).join("\n\n");
154910
+ }
154911
+ function collectSpecSections(context) {
154912
+ const headings = getMarkdownHeadingFacts(context);
154913
+ return headings.reduce((sections, fact, index) => {
154914
+ if (fact.depth !== 2) return sections;
154915
+ const span = getFactSpan(fact);
154916
+ if (!span) return sections;
154917
+ sections.push({
154918
+ id: fact.id,
154919
+ title: fact.text,
154920
+ kind: getSectionKind(context.annotations, fact.id),
154921
+ factId: fact.id,
154922
+ start: span.start,
154923
+ end: getMarkdownHeadingEnd(headings, index, context.sourceMarkdown.length)
154924
+ });
154925
+ return sections;
154926
+ }, []);
154927
+ }
154928
+ function collectRequirementBlocks(context) {
154929
+ const headings = getMarkdownHeadingFacts(context);
154930
+ let reqIndex = 0;
154931
+ return headings.reduce((requirements, fact, index) => {
154932
+ const annotation = getOpenSpecProjectionAnnotation(context.annotations, fact.id, "requirement");
154933
+ if (!annotation) return requirements;
154934
+ const span = getFactSpan(fact);
154935
+ if (!span) return requirements;
154936
+ reqIndex++;
154937
+ const end = getMarkdownHeadingEnd(headings, index, context.sourceMarkdown.length);
154938
+ const title = annotation.metadata?.title?.trim() || fact.text;
154939
+ requirements.push({
154940
+ id: `req-${reqIndex}`,
154941
+ title,
154942
+ factId: fact.id,
154943
+ start: span.start,
154944
+ end,
154945
+ scenarios: collectScenarioBlocks(context, headings, fact, end)
154946
+ });
154947
+ return requirements;
154948
+ }, []);
154949
+ }
154950
+ function collectScenarioBlocks(context, headings, requirementFact, requirementEnd) {
154951
+ const requirementSpan = getFactSpan(requirementFact);
154952
+ if (!requirementSpan) return [];
154953
+ return headings.reduce((scenarios, fact, index) => {
154954
+ const annotation = getOpenSpecProjectionAnnotation(context.annotations, fact.id, "scenario");
154955
+ if (!annotation) return scenarios;
154956
+ const span = getFactSpan(fact);
154957
+ if (!span || span.start <= requirementSpan.start || span.start >= requirementEnd) return scenarios;
154958
+ scenarios.push({
154959
+ title: annotation.metadata?.title?.trim() || fact.text,
154960
+ factId: fact.id,
154961
+ start: span.start,
154962
+ end: Math.min(getScenarioEnd(context, headings, index, requirementEnd), requirementEnd)
154963
+ });
154964
+ return scenarios;
154965
+ }, []);
154966
+ }
154967
+ function getScenarioEnd(context, headings, scenarioIndex, requirementEnd) {
154968
+ for (let i = scenarioIndex + 1; i < headings.length; i++) {
154969
+ const next = headings[i];
154970
+ const nextSpan = getFactSpan(next);
154971
+ if (!nextSpan || nextSpan.start >= requirementEnd) return requirementEnd;
154972
+ if ((next.depth ?? 6) <= 3 || getOpenSpecProjectionAnnotation(context.annotations, next.id, "scenario")) return nextSpan.start;
154973
+ }
154974
+ return requirementEnd;
154975
+ }
154976
+ function getContentStartAfterHeading(context, factId, fallback) {
154977
+ const fact = context.factById.get(factId);
154978
+ return fact ? getFactSpan(fact)?.end ?? fallback : fallback;
154979
+ }
154980
+ function projectScenario(context, scenario) {
154981
+ const bodyMarkdown = trimMarkdownSlice(context.sourceMarkdown, getContentStartAfterHeading(context, scenario.factId, scenario.start), scenario.end);
154982
+ const rawText = [scenario.title, bodyMarkdown].filter((part) => part.trim()).join("\n");
154983
+ return {
154984
+ title: scenario.title,
154985
+ bodyMarkdown,
154986
+ rawText,
154987
+ steps: getScenarioStepsFromAnnotations(context, scenario)
154988
+ };
154989
+ }
154990
+ function getScenarioStepsFromAnnotations(context, scenario) {
154991
+ return context.annotations.reduce((steps, annotation) => {
154992
+ if (annotation.kind !== "scenario-step") return steps;
154993
+ const fact = context.factById.get(annotation.targetFactId);
154994
+ const span = fact ? getFactSpan(fact) : void 0;
154995
+ if (!span || span.start <= scenario.start || span.start >= scenario.end) return steps;
154996
+ const keyword = annotation.metadata?.keyword;
154997
+ const contentMarkdown = annotation.metadata?.contentMarkdown;
154998
+ const rawText = annotation.metadata?.rawMarkdown;
154999
+ if (!isScenarioStepKeyword(keyword) || !contentMarkdown || !rawText) return steps;
155000
+ steps.push({
155001
+ keyword,
155002
+ contentMarkdown,
155003
+ rawText
155004
+ });
155005
+ return steps;
155006
+ }, []);
155007
+ }
155008
+ function isScenarioStepKeyword(value) {
155009
+ return value === "GIVEN" || value === "WHEN" || value === "THEN" || value === "AND" || value === "BUT";
155010
+ }
155011
+ function projectRequirement(context, requirement) {
155012
+ const bodyEnd = requirement.scenarios.map((scenario) => scenario.start).sort((left, right) => left - right)[0] ?? requirement.end;
155013
+ const bodyMarkdown = trimMarkdownSlice(context.sourceMarkdown, getContentStartAfterHeading(context, requirement.factId, requirement.start), bodyEnd);
155014
+ const scenarios = requirement.scenarios.map((scenario) => projectScenario(context, scenario));
155015
+ const scenarioText = scenarios.map((scenario) => scenario.rawText).join("\n\n");
155016
+ return {
155017
+ id: requirement.id,
155018
+ title: requirement.title,
155019
+ bodyMarkdown,
155020
+ text: createRequirementText(requirement.title, bodyMarkdown, scenarioText),
155021
+ scenarios
155022
+ };
155023
+ }
155024
+ function getFactSpan(fact) {
155025
+ return getMarkdownFactSpan(fact);
155026
+ }
155027
+ function getSectionKind(annotations, factId) {
155028
+ if (getOpenSpecProjectionAnnotation(annotations, factId, "purpose-section")) return "overview";
155029
+ if (getOpenSpecProjectionAnnotation(annotations, factId, "requirements-section")) return "requirements";
155030
+ return "other";
155031
+ }
155032
+ function toOpenSpecProjectionContext(context) {
155033
+ return {
155034
+ sourceMarkdown: context.sourceMarkdown,
155035
+ facts: context.facts,
155036
+ factById: context.factById,
155037
+ annotations: context.annotations.filter(isOpenSpecAnnotation)
155038
+ };
155039
+ }
155040
+ function toProjectedOpenSpecDocument(document) {
155041
+ return {
155042
+ ...document,
155043
+ annotations: document.annotations.filter(isOpenSpecAnnotation)
155044
+ };
155045
+ }
155046
+ function isOpenSpecAnnotation(annotation) {
155047
+ return annotation.kind === "document-title" || annotation.kind === "purpose-section" || annotation.kind === "requirements-section" || annotation.kind === "requirement" || annotation.kind === "scenario" || annotation.kind === "scenario-step" || annotation.kind === "keyword";
155048
+ }
155049
+ //#endregion
155050
+ //#region src/components/spec-markdown-document.tsx
155051
+ var OPENSPEC_PREFIXES = {
155052
+ requirement: /^(?:Requirement|Capability):\s*/i,
155053
+ scenario: /^(?:Scenario|Example):\s*/i
155054
+ };
155055
+ var OPENSPEC_INLINE_KEYWORD_CLASS = "openspec-inline-keyword";
155056
+ var OPENSPEC_SCENARIO_STEP_CLASS = "spec-scenario-step";
155057
+ var OPENSPEC_BLOCK_FACT_KINDS = new Set([
155058
+ "paragraph",
155059
+ "list",
155060
+ "listItem",
155061
+ "blockquote",
155062
+ "table"
155063
+ ]);
155064
+ function stripPrefix(text, prefix) {
155065
+ return text.replace(prefix, "").trim();
155066
+ }
155067
+ function describeOpenSpecHeading(sourceLevel, text) {
155068
+ if (sourceLevel === 1) return {
155069
+ kind: "spec",
155070
+ id: slugify(text) || "spec",
155071
+ title: text,
155072
+ tocLabel: text
155073
+ };
155074
+ if (sourceLevel === 2) return {
155075
+ kind: "section",
155076
+ id: slugify(text) || "section",
155077
+ title: text,
155078
+ tocLabel: text
155079
+ };
155080
+ if (sourceLevel === 3 && OPENSPEC_PREFIXES.requirement.test(text)) {
155081
+ const title = stripPrefix(text, OPENSPEC_PREFIXES.requirement);
155082
+ return {
155083
+ kind: "requirement",
155084
+ id: `requirement-${slugify(title) || "item"}`,
155085
+ title,
155086
+ tocLabel: title
155087
+ };
155088
+ }
155089
+ if (sourceLevel === 4 && OPENSPEC_PREFIXES.scenario.test(text)) {
155090
+ const title = stripPrefix(text, OPENSPEC_PREFIXES.scenario);
155091
+ return {
155092
+ kind: "scenario",
155093
+ id: `scenario-${slugify(title) || "item"}`,
155094
+ title,
155095
+ tocLabel: title
155096
+ };
155097
+ }
155098
+ }
155099
+ function describeAnnotatedOpenSpecHeading(document, headingFact, sourceLevel, text) {
155100
+ if (!headingFact) return describeOpenSpecHeading(sourceLevel, text);
155101
+ const readingProjection = document.projections[OPEN_SPEC_READING_SECTIONS_PROJECTION_ID];
155102
+ if (sourceLevel === 2) {
155103
+ const section = readingProjection?.sections.find((section) => section.factId === headingFact.id);
155104
+ return {
155105
+ kind: "section",
155106
+ id: slugify(text) || "section",
155107
+ title: text,
155108
+ tocLabel: text,
155109
+ ...section ? { sectionKind: section.kind } : {}
155110
+ };
155111
+ }
155112
+ const requirement = getOpenSpecProjectionAnnotation(document.annotations, headingFact.id, "requirement");
155113
+ if (requirement) {
155114
+ const title = requirement.metadata?.title?.trim() || text;
155115
+ const requirementIndex = readingProjection?.requirements.findIndex((block) => block.factId === headingFact.id) ?? -1;
155116
+ return {
155117
+ kind: "requirement",
155118
+ id: `requirement-${slugify(title) || "item"}`,
155119
+ title,
155120
+ tocLabel: title,
155121
+ label: requirementIndex >= 0 ? formatRequirementLabel(requirementIndex + 1) : "Requirement"
155122
+ };
155123
+ }
155124
+ const scenario = getOpenSpecProjectionAnnotation(document.annotations, headingFact.id, "scenario");
155125
+ if (scenario) {
155126
+ const title = scenario.metadata?.title?.trim() || text;
155127
+ return {
155128
+ kind: "scenario",
155129
+ id: `scenario-${slugify(title) || "item"}`,
155130
+ title,
155131
+ tocLabel: title,
155132
+ label: "Scenario"
155133
+ };
155134
+ }
155135
+ return describeOpenSpecHeading(sourceLevel, text);
155136
+ }
155137
+ function createAnnotatedHeadingTransform(document, requirementCount) {
155138
+ const headingByStartOffset = /* @__PURE__ */ new Map();
155139
+ for (const fact of document.facts) {
155140
+ if (fact.kind !== "heading" || typeof fact.depth !== "number") continue;
155141
+ const span = getMarkdownFactSpan(fact);
155142
+ if (span) headingByStartOffset.set(span.start, fact);
155143
+ }
155144
+ return ({ sourceLevel, text, sourceStartOffset }) => {
155145
+ const heading = describeAnnotatedOpenSpecHeading(document, sourceStartOffset === void 0 ? void 0 : headingByStartOffset.get(sourceStartOffset), sourceLevel, text);
155146
+ if (!heading) return void 0;
155147
+ return {
155148
+ id: heading.id,
155149
+ tocLabel: heading.tocLabel,
155150
+ className: createHeadingClassName(heading, requirementCount),
155151
+ suffix: createHeadingSuffix(heading, requirementCount),
155152
+ dataAttributes: {
155153
+ "data-openspec-kind": heading.kind,
155154
+ "data-openspec-title": heading.title,
155155
+ ...heading.label ? { "data-openspec-label": heading.label } : {},
155156
+ ...heading.sectionKind ? { "data-openspec-section-kind": heading.sectionKind } : {}
155157
+ }
155158
+ };
155159
+ };
155160
+ }
155161
+ function createHeadingClassName(heading, requirementCount) {
155162
+ if (heading.kind !== "section" || heading.title !== "Requirements") return void 0;
155163
+ if (requirementCount === void 0) return void 0;
155164
+ return "openspec-heading-with-chip";
155165
+ }
155166
+ function createHeadingSuffix(heading, requirementCount) {
155167
+ if (heading.kind !== "section" || heading.title !== "Requirements") return void 0;
155168
+ if (requirementCount === void 0) return void 0;
155169
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CountBadge, {
155170
+ count: requirementCount,
155171
+ tone: "subtle",
155172
+ size: "sm",
155173
+ shape: "box",
155174
+ className: "openspec-heading-chip",
155175
+ "aria-label": String(requirementCount),
155176
+ title: `${requirementCount} requirements`
155177
+ });
155178
+ }
155179
+ /**
155180
+ * Renders the processed spec Markdown as the visual source while attaching
155181
+ * OpenSpec structure metadata for styling, anchors, and ToC alignment.
155182
+ */
155183
+ function SpecMarkdownDocument({ markdown, spec, requirementCount, className = "" }) {
155184
+ const resolvedRequirementCount = requirementCount ?? spec?.requirements.length;
155185
+ const document = (0, import_react.useMemo)(() => projectOpenSpecMarkdown(markdown, { specId: spec?.id ?? "inline" }), [markdown, spec?.id]);
155186
+ const headingTransform = (0, import_react.useMemo)(() => createAnnotatedHeadingTransform(document, resolvedRequirementCount), [document, resolvedRequirementCount]);
155187
+ const inlineTextAnnotations = (0, import_react.useMemo)(() => createOpenSpecInlineTextAnnotations(document), [document]);
155188
+ const blockAnnotations = (0, import_react.useMemo)(() => createOpenSpecBlockAnnotations(document), [document]);
155189
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarkdownViewer, {
155190
+ className: `spec-markdown-document spec-reading-document ${className}`,
155191
+ markdown,
155192
+ headingTransform,
155193
+ inlineTextAnnotations,
155194
+ blockAnnotations
155195
+ });
155196
+ }
155197
+ function createOpenSpecBlockAnnotations(document) {
155198
+ const factById = new Map(document.facts.map((fact) => [fact.id, fact]));
155199
+ const annotationByOffset = /* @__PURE__ */ new Map();
155200
+ const readingProjection = document.projections[OPEN_SPEC_READING_SECTIONS_PROJECTION_ID];
155201
+ if (readingProjection) {
155202
+ for (const section of readingProjection.sections) {
155203
+ if (section.kind !== "overview") continue;
155204
+ addRangeBlockAnnotations(document, annotationByOffset, {
155205
+ start: getFactEnd(factById, section.factId, section.start),
155206
+ end: section.end,
155207
+ dataAttributes: {
155208
+ "data-openspec-zone": "purpose",
155209
+ "data-openspec-section-kind": section.kind,
155210
+ "data-openspec-section-title": section.title
155211
+ }
155212
+ });
155213
+ }
155214
+ const requirementsSection = readingProjection.sections.find((section) => section.kind === "requirements");
155215
+ if (requirementsSection) {
155216
+ const firstRequirementStart = readingProjection.requirements.map((requirement) => requirement.start).sort((left, right) => left - right)[0];
155217
+ addRangeBlockAnnotations(document, annotationByOffset, {
155218
+ start: getFactEnd(factById, requirementsSection.factId, requirementsSection.start),
155219
+ end: firstRequirementStart ?? requirementsSection.end,
155220
+ dataAttributes: {
155221
+ "data-openspec-zone": "requirements-intro",
155222
+ "data-openspec-section-kind": requirementsSection.kind,
155223
+ "data-openspec-section-title": requirementsSection.title
155224
+ }
155225
+ });
155226
+ }
155227
+ for (const [index, requirement] of readingProjection.requirements.entries()) {
155228
+ const requirementLabel = formatRequirementLabel(index + 1);
155229
+ const firstScenarioStart = requirement.scenarios.map((scenario) => scenario.start).sort((left, right) => left - right)[0];
155230
+ const requirementData = {
155231
+ "data-openspec-requirement-id": requirement.id,
155232
+ "data-openspec-requirement-label": requirementLabel,
155233
+ "data-openspec-requirement-title": requirement.title
155234
+ };
155235
+ addRangeBlockAnnotations(document, annotationByOffset, {
155236
+ start: getFactEnd(factById, requirement.factId, requirement.start),
155237
+ end: firstScenarioStart ?? requirement.end,
155238
+ dataAttributes: {
155239
+ "data-openspec-zone": "requirement-body",
155240
+ ...requirementData
155241
+ }
155242
+ });
155243
+ for (const scenario of requirement.scenarios) addRangeBlockAnnotations(document, annotationByOffset, {
155244
+ start: getFactEnd(factById, scenario.factId, scenario.start),
155245
+ end: scenario.end,
155246
+ dataAttributes: {
155247
+ "data-openspec-zone": "scenario-body",
155248
+ ...requirementData,
155249
+ "data-openspec-scenario-title": scenario.title
155250
+ }
155251
+ });
155252
+ }
155253
+ }
155254
+ for (const annotation of document.annotations) {
155255
+ if (annotation.kind !== "scenario-step") continue;
155256
+ const fact = factById.get(annotation.targetFactId);
155257
+ const span = fact ? getMarkdownFactSpan(fact) : void 0;
155258
+ if (!fact || !span) continue;
155259
+ const keyword = annotation.metadata?.keyword;
155260
+ upsertBlockAnnotation(annotationByOffset, {
155261
+ sourceStartOffset: span.start,
155262
+ sourceKind: fact.mdastType,
155263
+ className: OPENSPEC_SCENARIO_STEP_CLASS,
155264
+ dataAttributes: {
155265
+ "data-openspec-kind": "scenario-step",
155266
+ ...keyword ? { "data-openspec-step-keyword": keyword } : {}
155267
+ }
155268
+ });
155269
+ }
155270
+ return Array.from(annotationByOffset.values());
155271
+ }
155272
+ function addRangeBlockAnnotations(document, annotationByOffset, range) {
155273
+ for (const fact of document.facts) {
155274
+ if (!OPENSPEC_BLOCK_FACT_KINDS.has(fact.kind)) continue;
155275
+ const span = getMarkdownFactSpan(fact);
155276
+ if (!span || span.start < range.start || span.start >= range.end) continue;
155277
+ upsertBlockAnnotation(annotationByOffset, {
155278
+ sourceStartOffset: span.start,
155279
+ sourceKind: fact.mdastType,
155280
+ dataAttributes: {
155281
+ "data-openspec-block-kind": fact.kind,
155282
+ ...range.dataAttributes
155283
+ }
155284
+ });
155285
+ }
155286
+ }
155287
+ function upsertBlockAnnotation(annotationByOffset, annotation) {
155288
+ const key = createBlockAnnotationKey(annotation);
155289
+ const previous = annotationByOffset.get(key);
155290
+ if (!previous) {
155291
+ annotationByOffset.set(key, annotation);
155292
+ return;
155293
+ }
155294
+ annotationByOffset.set(key, {
155295
+ sourceStartOffset: annotation.sourceStartOffset,
155296
+ sourceKind: annotation.sourceKind,
155297
+ className: [previous.className, annotation.className].filter(Boolean).join(" ") || void 0,
155298
+ dataAttributes: {
155299
+ ...previous.dataAttributes,
155300
+ ...annotation.dataAttributes
155301
+ }
155302
+ });
155303
+ }
155304
+ function createBlockAnnotationKey(annotation) {
155305
+ return `${annotation.sourceStartOffset}:${annotation.sourceKind ?? "*"}`;
155306
+ }
155307
+ function getFactEnd(factById, factId, fallback) {
155308
+ const fact = factById.get(factId);
155309
+ return fact ? getMarkdownFactSpan(fact)?.end ?? fallback : fallback;
155310
+ }
155311
+ function formatRequirementLabel(index) {
155312
+ return `REQ-${String(index).padStart(2, "0")}`;
155313
+ }
155314
+ function createOpenSpecInlineTextAnnotations(document) {
155315
+ const terms = /* @__PURE__ */ new Map();
155316
+ for (const annotation of document.annotations) {
155317
+ if (annotation.kind !== "keyword") continue;
155318
+ const { keyword, keywordText, keywordRole } = annotation.metadata ?? {};
155319
+ const text = keywordText ?? keyword;
155320
+ if (!keyword || !text) continue;
155321
+ terms.set(text, {
155322
+ text,
155323
+ className: OPENSPEC_INLINE_KEYWORD_CLASS,
155324
+ dataAttributes: createOpenSpecKeywordDataAttributes(keyword, keywordRole)
155325
+ });
155326
+ }
155327
+ return Array.from(terms.values());
155328
+ }
155329
+ function createOpenSpecKeywordDataAttributes(keyword, keywordRole) {
155330
+ return {
155331
+ "data-openspec-keyword": keyword,
155332
+ ...keywordRole ? { "data-openspec-keyword-role": keywordRole } : {}
155333
+ };
155334
+ }
155335
+ //#endregion
153912
155336
  //#region src/routes/spec-view.tsx
153913
155337
  function SpecView() {
153914
155338
  const { specId } = useParams({ from: "/specs/$specId" });
@@ -153918,8 +155342,9 @@ function SpecView() {
153918
155342
  entityId: specId
153919
155343
  }), [specId]);
153920
155344
  const { data: spec, isLoading } = useSpecSubscription(specId);
155345
+ const { data: rawMarkdown, isLoading: isRawLoading } = useSpecRawSubscription(specId);
153921
155346
  const validation = null;
153922
- if (isLoading && !spec) {
155347
+ if (isLoading && !spec || isRawLoading && !rawMarkdown) {
153923
155348
  if (handoff) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
153924
155349
  className: "flex min-h-0 flex-1 flex-col gap-6 p-4",
153925
155350
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -153961,10 +155386,11 @@ function SpecView() {
153961
155386
  });
153962
155387
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SpecContent, {
153963
155388
  spec,
155389
+ rawMarkdown: rawMarkdown ?? "",
153964
155390
  validation
153965
155391
  });
153966
155392
  }
153967
- function SpecContent({ spec, validation }) {
155393
+ function SpecContent({ spec, rawMarkdown, validation }) {
153968
155394
  const headerRef = (0, import_react.useRef)(null);
153969
155395
  const sharedDescriptor = (0, import_react.useMemo)(() => ({
153970
155396
  family: "specs",
@@ -154002,56 +155428,11 @@ function SpecContent({ spec, validation }) {
154002
155428
  })]
154003
155429
  }),
154004
155430
  validation && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ValidationStatus, { validation }),
154005
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarkdownViewer, {
154006
- className: "vt-detail-content min-h-0 flex-1",
154007
- markdown: ({ H1, H2, Section }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
154008
- className: "space-y-6",
154009
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Section, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(H1, {
154010
- id: "overview",
154011
- children: "Overview"
154012
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
154013
- className: "bg-muted/30 mt-2 rounded-lg p-4",
154014
- children: spec.overview ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarkdownViewer, { markdown: spec.overview }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
154015
- className: "text-muted-foreground",
154016
- children: "No overview"
154017
- })
154018
- })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Section, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(H1, {
154019
- id: "requirements",
154020
- children: [
154021
- "Requirements (",
154022
- spec.requirements.length,
154023
- ")"
154024
- ]
154025
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
154026
- className: "mt-3 space-y-4",
154027
- children: [spec.requirements.map((req) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Section, {
154028
- className: "border-border rounded-lg border p-4",
154029
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(H2, {
154030
- className: "text-base",
154031
- id: `req-${req.id}`,
154032
- children: req.text
154033
- }), req.scenarios.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
154034
- className: "mt-3",
154035
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
154036
- className: "text-muted-foreground mb-2 text-sm font-medium",
154037
- children: [
154038
- "Scenarios (",
154039
- req.scenarios.length,
154040
- ")"
154041
- ]
154042
- }), req.scenarios.map((scenario, i) => {
154043
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
154044
- className: "bg-muted/50 rounded-md p-3",
154045
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarkdownViewer, { markdown: scenario.rawText.replace(/^---\n?/, "").replace(/\n?---$/, "").trim() })
154046
- }, i);
154047
- })]
154048
- })]
154049
- }, req.id)), spec.requirements.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
154050
- className: "text-muted-foreground",
154051
- children: "No requirements defined"
154052
- })]
154053
- })] })]
154054
- })
155431
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SpecMarkdownDocument, {
155432
+ markdown: rawMarkdown,
155433
+ spec,
155434
+ requirementCount: spec.requirements.length,
155435
+ className: "vt-detail-content min-h-0 flex-1"
154055
155436
  })
154056
155437
  ]
154057
155438
  });