@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.
- package/dist/assets/CanvasRenderer-DdAQFilo.js +1 -0
- package/dist/assets/WebGLRenderer-D0xPb_sN.js +1 -0
- package/dist/assets/WebGPURenderer-DOHdaBx2.js +1 -0
- package/dist/assets/browserAll-r2hAe-_H.js +1 -0
- package/dist/assets/{dist-4Q-3H32Y.js → dist-BCNuV7xG.js} +1 -1
- package/dist/assets/dist-BIdWjo7l.js +1 -0
- package/dist/assets/{dist-CV0FPhgu.js → dist-BVK0Rssu.js} +1 -1
- package/dist/assets/{dist-Bw7c0_jc.js → dist-BozUQC43.js} +1 -1
- package/dist/assets/{dist-C2CZRWaX.js → dist-CDVL3YEX.js} +1 -1
- package/dist/assets/{dist-C87gRqf_.js → dist-CLV0HJPY.js} +1 -1
- package/dist/assets/{dist-CFABLsEW.js → dist-CTzhdbTq.js} +1 -1
- package/dist/assets/dist-CaLzADt5.js +1 -0
- package/dist/assets/{dist-LUCFMJ0H.js → dist-CiOmMqf9.js} +1 -1
- package/dist/assets/{dist-DDkSOHP1.js → dist-CnERwHuC.js} +1 -1
- package/dist/assets/dist-DOlIviuv.js +1 -0
- package/dist/assets/{dist-DAT_ScNa.js → dist-YiF0YY_U.js} +1 -1
- package/dist/assets/{ghostty-web-CCC3Geb4.js → ghostty-web-CZWq-tLQ.js} +1 -1
- package/dist/assets/{index-DQNMhAvr.js → index-4c2vKipy.js} +194 -189
- package/dist/assets/index-BWGHsSSL.css +1 -0
- package/dist/assets/{init-YHF-o1HV.js → init-DIWiFCrW.js} +1 -1
- package/dist/assets/trpc-sHsseX53.js +1 -0
- package/dist/assets/webworkerAll-C2ADhLOV.js +1 -0
- package/dist/index.html +2 -2
- package/dist-ssg/client/.vite/ssr-manifest.json +33 -15
- package/dist-ssg/client/assets/CanvasRenderer-BnHuf5TM.js +1 -0
- package/dist-ssg/client/assets/WebGLRenderer-QYh372d6.js +1 -0
- package/dist-ssg/client/assets/WebGPURenderer-By0AaAH2.js +1 -0
- package/dist-ssg/client/assets/browserAll-DPwpu3rh.js +1 -0
- package/dist-ssg/client/assets/dist-BEN6F4c9.js +1 -0
- package/dist-ssg/client/assets/{dist-IUx13mN_.js → dist-B_W8Dqsb.js} +1 -1
- package/dist-ssg/client/assets/{dist-605X54ro.js → dist-BcdNCEyE.js} +1 -1
- package/dist-ssg/client/assets/{dist-Cq0edT--.js → dist-Be-EJt6Y.js} +1 -1
- package/dist-ssg/client/assets/{dist-BzIh3dSQ.js → dist-ByMJ_rO0.js} +1 -1
- package/dist-ssg/client/assets/{dist-BqMeFwto.js → dist-CMm3Nmq-.js} +1 -1
- package/dist-ssg/client/assets/{dist-Df67DVWO.js → dist-CcH1wf1k.js} +1 -1
- package/dist-ssg/client/assets/dist-D1b5s3ht.js +1 -0
- package/dist-ssg/client/assets/dist-DPvPfazk.js +1 -0
- package/dist-ssg/client/assets/{dist-D2UdxWFE.js → dist-DVuQHEv_.js} +1 -1
- package/dist-ssg/client/assets/{dist-CF5IRlgL.js → dist-fM5ujDIS.js} +1 -1
- package/dist-ssg/client/assets/{dist-10DRfH0N.js → dist-oT5Lx9wx.js} +1 -1
- package/dist-ssg/client/assets/{ghostty-web-DzvBtNVX.js → ghostty-web-DSO4nFSe.js} +1 -1
- package/dist-ssg/client/assets/index-CGJJczvl.css +1 -0
- package/dist-ssg/client/assets/{index.ssg-BEtxTyFQ.js → index.ssg-ph-ofjeJ.js} +167 -162
- package/dist-ssg/client/assets/{init-BJbVbp-i.js → init-DOhQsgqw.js} +1 -1
- package/dist-ssg/client/assets/trpc-DvRhEguu.js +1 -0
- package/dist-ssg/client/assets/webworkerAll-J8DER0Bd.js +1 -0
- package/dist-ssg/client/index.ssg.html +2 -2
- package/dist-ssg/server/entry-server.js +1528 -147
- package/package.json +1 -1
- package/dist/assets/CanvasRenderer-D7DGGDQU.js +0 -1
- package/dist/assets/WebGLRenderer-CjDeBZzX.js +0 -1
- package/dist/assets/WebGPURenderer-BEzco9Ka.js +0 -1
- package/dist/assets/browserAll-DkGrhjFB.js +0 -1
- package/dist/assets/dist-C1SIv52I.js +0 -1
- package/dist/assets/dist-CrPybnRq.js +0 -1
- package/dist/assets/dist-PzWhymM8.js +0 -1
- package/dist/assets/index-DHDRgs_i.css +0 -1
- package/dist/assets/trpc-q0DFddsK.js +0 -1
- package/dist/assets/webworkerAll-CKlnpVkj.js +0 -1
- package/dist-ssg/client/assets/CanvasRenderer-ByUXbKXl.js +0 -1
- package/dist-ssg/client/assets/WebGLRenderer-ChYLzDCd.js +0 -1
- package/dist-ssg/client/assets/WebGPURenderer-BobKBO0x.js +0 -1
- package/dist-ssg/client/assets/browserAll-UN3GPOPc.js +0 -1
- package/dist-ssg/client/assets/dist-BuDT5pXI.js +0 -1
- package/dist-ssg/client/assets/dist-BxaDOJmU.js +0 -1
- package/dist-ssg/client/assets/dist-ScSFKyoF.js +0 -1
- package/dist-ssg/client/assets/index-14LuyayW.css +0 -1
- package/dist-ssg/client/assets/trpc-bHbo3cKe.js +0 -1
- 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)(
|
|
94431
|
-
|
|
94432
|
-
"
|
|
94433
|
-
|
|
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
|
|
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)(
|
|
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
|
|
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", {
|
|
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:
|
|
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:
|
|
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)(
|
|
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
|
-
|
|
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
|
|
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
|
-
}, [
|
|
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(
|
|
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.
|
|
127061
|
+
case 1: return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h1", {
|
|
126800
127062
|
id,
|
|
126801
127063
|
className,
|
|
126802
127064
|
style,
|
|
126803
|
-
|
|
127065
|
+
...dataAttributes,
|
|
127066
|
+
children: [
|
|
127067
|
+
children,
|
|
127068
|
+
suffix ? " " : null,
|
|
127069
|
+
suffix
|
|
127070
|
+
]
|
|
126804
127071
|
});
|
|
126805
|
-
case 2: return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
127072
|
+
case 2: return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h2", {
|
|
126806
127073
|
id,
|
|
126807
127074
|
className,
|
|
126808
127075
|
style,
|
|
126809
|
-
|
|
127076
|
+
...dataAttributes,
|
|
127077
|
+
children: [
|
|
127078
|
+
children,
|
|
127079
|
+
suffix ? " " : null,
|
|
127080
|
+
suffix
|
|
127081
|
+
]
|
|
126810
127082
|
});
|
|
126811
|
-
case 3: return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
127083
|
+
case 3: return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", {
|
|
126812
127084
|
id,
|
|
126813
127085
|
className,
|
|
126814
127086
|
style,
|
|
126815
|
-
|
|
127087
|
+
...dataAttributes,
|
|
127088
|
+
children: [
|
|
127089
|
+
children,
|
|
127090
|
+
suffix ? " " : null,
|
|
127091
|
+
suffix
|
|
127092
|
+
]
|
|
126816
127093
|
});
|
|
126817
|
-
case 4: return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
127094
|
+
case 4: return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h4", {
|
|
126818
127095
|
id,
|
|
126819
127096
|
className,
|
|
126820
127097
|
style,
|
|
126821
|
-
|
|
127098
|
+
...dataAttributes,
|
|
127099
|
+
children: [
|
|
127100
|
+
children,
|
|
127101
|
+
suffix ? " " : null,
|
|
127102
|
+
suffix
|
|
127103
|
+
]
|
|
126822
127104
|
});
|
|
126823
|
-
case 5: return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
127105
|
+
case 5: return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h5", {
|
|
126824
127106
|
id,
|
|
126825
127107
|
className,
|
|
126826
127108
|
style,
|
|
126827
|
-
|
|
127109
|
+
...dataAttributes,
|
|
127110
|
+
children: [
|
|
127111
|
+
children,
|
|
127112
|
+
suffix ? " " : null,
|
|
127113
|
+
suffix
|
|
127114
|
+
]
|
|
126828
127115
|
});
|
|
126829
|
-
case 6: return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
127116
|
+
case 6: return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h6", {
|
|
126830
127117
|
id,
|
|
126831
127118
|
className,
|
|
126832
127119
|
style,
|
|
126833
|
-
|
|
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
|
-
/*
|
|
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)(
|
|
146267
|
-
|
|
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
|
|
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)(
|
|
152676
|
-
|
|
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)(
|
|
153165
|
-
|
|
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)(
|
|
153462
|
-
|
|
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)(
|
|
154006
|
-
|
|
154007
|
-
|
|
154008
|
-
|
|
154009
|
-
|
|
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
|
});
|