@dev.fast/whiteboard 0.0.0-stage → 0.2.1-preview.20261005.98
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/LICENSE +21 -0
- package/README.md +146 -2
- package/THIRD_PARTY_NOTICES.md +93 -0
- package/dist/account-alias-DOHN1RSH.js +973 -0
- package/dist/agent-cli-ChOq0ZuO.js +117 -0
- package/dist/agent-cli-DzWfZr6G.js +2 -0
- package/dist/agent-client-CPQo7iTI.js +305 -0
- package/dist/authoring-tools-Cr6Kpjsx.js +234 -0
- package/dist/build-info.json +1 -0
- package/dist/cli-hVgHcdsv.js +47 -0
- package/dist/cli-runner-D8y2_luU.js +5095 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +128 -0
- package/dist/client-DFoh50W3.js +950 -0
- package/dist/desktop-discovery-DBO-PM2V.js +164 -0
- package/dist/error-message-OtiDonty.js +6 -0
- package/dist/fs-utils-BMPLt0cr.js +22 -0
- package/dist/fuzzy-match-BoAmcyak.js +30 -0
- package/dist/headless-host-BJxf4dUU.js +94 -0
- package/dist/input-error-OLgB_h31.js +11 -0
- package/dist/local-data-CxLk75rx.js +4491 -0
- package/dist/mcp-QeF8vkdf.js +140 -0
- package/dist/package-paths-B6-zxvIO.js +47 -0
- package/dist/process-error-telemetry-v9e5h6D7.js +3952 -0
- package/dist/profile-4ry2f3AM.js +76 -0
- package/dist/profile-Bh5FIrmf.js +2 -0
- package/dist/request-origin-D_4QIdoN.js +811 -0
- package/dist/review-agent-traces-DNJFYzc8.js +3866 -0
- package/dist/review-home-paths-6zZH-1c9.js +2560 -0
- package/dist/review-telemetry-DEFnQsQ2.js +1195 -0
- package/dist/runtime-CKiWhGrf.js +52 -0
- package/dist/runtime.d.ts +9 -0
- package/dist/runtime.js +2 -0
- package/dist/s3-SzLpeCG6.js +345 -0
- package/dist/s3-config-BgxkSoOy.js +2 -0
- package/dist/s3-config-CAxhO9u_.js +676 -0
- package/dist/server/desktop-host.d.ts +4 -0
- package/dist/server/desktop-host.js +1702 -0
- package/dist/server-discovery-Dbzn3w6Z.js +66 -0
- package/dist/sharing/index.d.ts +1977 -0
- package/dist/sharing/index.js +2 -0
- package/dist/src-CGP5ytbV.js +283 -0
- package/dist/src-CmdiBL20.js +3676 -0
- package/dist/src-DnwdaQ2r.js +865 -0
- package/dist/src-X9phtB2j.js +68 -0
- package/dist/stored-document-migration-DHeMpLMb.js +2671 -0
- package/dist/tool-failure-C8zB73HV.js +98 -0
- package/dist/tutorial-trace-DrBvkaeL.js +43 -0
- package/instructions/authoring.md +36 -0
- package/instructions/file-lenses.md +13 -0
- package/instructions/scratchpad.md +23 -0
- package/instructions/trace-archaeology.md +113 -0
- package/onboarding.md +8 -0
- package/package.json +106 -3
- package/src/agent-selection.ts +100 -0
- package/src/agent-session-ref.ts +80 -0
- package/src/ask/agents.ts +351 -0
- package/src/ask/checkout-files.ts +47 -0
- package/src/ask/file-refs.ts +111 -0
- package/src/ask/pi-mcp.ts +154 -0
- package/src/ask/protocol.ts +244 -0
- package/src/ask/thread-state.ts +350 -0
- package/src/ask/thread.ts +1400 -0
- package/src/ask/threads.ts +219 -0
- package/src/ask/watch.ts +79 -0
- package/src/cli-install.ts +1041 -0
- package/src/cli-runner.ts +1461 -0
- package/src/cli-runtime-info.ts +35 -0
- package/src/cli.ts +222 -0
- package/src/connect-prompts.ts +248 -0
- package/src/cursor-deeplink.ts +11 -0
- package/src/desktop-discovery.ts +355 -0
- package/src/diff-selection-migration.ts +63 -0
- package/src/embedded-posthog-key.ts +6 -0
- package/src/error-telemetry.ts +247 -0
- package/src/evidence.ts +14 -0
- package/src/exception-telemetry.ts +126 -0
- package/src/fixtures/blocks/call_stack_diff.json +27 -0
- package/src/fixtures/blocks/callout.json +11 -0
- package/src/fixtures/blocks/code.json +9 -0
- package/src/fixtures/blocks/code_peek.json +8 -0
- package/src/fixtures/blocks/database_lens.json +51 -0
- package/src/fixtures/blocks/divider.json +1 -0
- package/src/fixtures/blocks/fixtures.ts +33 -0
- package/src/fixtures/blocks/flow_diagram.json +36 -0
- package/src/fixtures/blocks/ids.ts +8 -0
- package/src/fixtures/blocks/image.json +9 -0
- package/src/fixtures/blocks/markdown.json +7 -0
- package/src/fixtures/blocks/section.json +11 -0
- package/src/fixtures/blocks/sequence.json +31 -0
- package/src/fixtures/blocks/software_map.json +7 -0
- package/src/fixtures/blocks/trace_quote.json +9 -0
- package/src/fixtures/blocks/tutorial.json +51 -0
- package/src/fs-utils.ts +32 -0
- package/src/fuzzy-match.ts +94 -0
- package/src/install.ts +29 -0
- package/src/legacy-skills.ts +133 -0
- package/src/lens-selection.ts +230 -0
- package/src/markdown-latex-math.ts +230 -0
- package/src/markdown.ts +81 -0
- package/src/package-paths.ts +53 -0
- package/src/posthog-capture-client.ts +610 -0
- package/src/review-api/README.md +295 -0
- package/src/review-api/activity.ts +340 -0
- package/src/review-api/agent-cli.ts +219 -0
- package/src/review-api/agent-client.ts +188 -0
- package/src/review-api/anchor-quotes.ts +88 -0
- package/src/review-api/ask-history.ts +242 -0
- package/src/review-api/authoring-tools.ts +211 -0
- package/src/review-api/blocks/call_stack_diff.ts +75 -0
- package/src/review-api/blocks/callout.ts +24 -0
- package/src/review-api/blocks/code.ts +9 -0
- package/src/review-api/blocks/code_peek.ts +13 -0
- package/src/review-api/blocks/database_lens.ts +150 -0
- package/src/review-api/blocks/definition.ts +40 -0
- package/src/review-api/blocks/divider.ts +6 -0
- package/src/review-api/blocks/flow_diagram.ts +111 -0
- package/src/review-api/blocks/image.ts +10 -0
- package/src/review-api/blocks/index.ts +90 -0
- package/src/review-api/blocks/markdown.ts +17 -0
- package/src/review-api/blocks/section.ts +24 -0
- package/src/review-api/blocks/sequence.ts +56 -0
- package/src/review-api/blocks/software_map.ts +9 -0
- package/src/review-api/blocks/trace_quote.ts +10 -0
- package/src/review-api/blocks/tutorial.ts +39 -0
- package/src/review-api/checkout-fs.ts +14 -0
- package/src/review-api/client.ts +1 -0
- package/src/review-api/comparison-coverage.ts +304 -0
- package/src/review-api/component-reference.ts +30 -0
- package/src/review-api/diff-lenses.ts +175 -0
- package/src/review-api/document-headings.ts +51 -0
- package/src/review-api/document-text.ts +200 -0
- package/src/review-api/document.ts +906 -0
- package/src/review-api/file-lenses.ts +100 -0
- package/src/review-api/http.ts +1877 -0
- package/src/review-api/image-decode.ts +29 -0
- package/src/review-api/input-error.ts +9 -0
- package/src/review-api/instructions.ts +89 -0
- package/src/review-api/lens-alignment.ts +52 -0
- package/src/review-api/local-data.ts +1837 -0
- package/src/review-api/map-input.ts +154 -0
- package/src/review-api/mcp-client-agent.ts +32 -0
- package/src/review-api/mcp.ts +240 -0
- package/src/review-api/origin.ts +43 -0
- package/src/review-api/profile.ts +146 -0
- package/src/review-api/public-tools.ts +102 -0
- package/src/review-api/pull-request.ts +389 -0
- package/src/review-api/read-schemas.ts +85 -0
- package/src/review-api/recovery.ts +2 -0
- package/src/review-api/request-origin.ts +33 -0
- package/src/review-api/review-progress.ts +382 -0
- package/src/review-api/status-tool.ts +10 -0
- package/src/review-api/store-schema.ts +22 -0
- package/src/review-api/store.ts +1647 -0
- package/src/review-api/tool-failure.ts +64 -0
- package/src/review-api/trace-schema.ts +13 -0
- package/src/review-api/traces.ts +119 -0
- package/src/review-api/unsupported-files.integration.ts +91 -0
- package/src/review-api/workspaces.ts +692 -0
- package/src/review-api/worktree-source.ts +170 -0
- package/src/review-api/worktree-structural.integration.ts +320 -0
- package/src/review-app-launcher.ts +432 -0
- package/src/review-app-picker.ts +168 -0
- package/src/review-app.ts +134 -0
- package/src/review-bundled-tools.ts +249 -0
- package/src/review-checkout-paths.ts +37 -0
- package/src/review-diff-files.ts +89 -0
- package/src/review-head-checkout.ts +300 -0
- package/src/review-home-paths.ts +73 -0
- package/src/review-info.ts +59 -0
- package/src/review-instances.ts +113 -0
- package/src/review-logger.ts +183 -0
- package/src/review-preferences.ts +88 -0
- package/src/review-prepare.ts +291 -0
- package/src/review-stack.ts +112 -0
- package/src/review-telemetry.ts +1066 -0
- package/src/runtime.ts +74 -0
- package/src/server/account-alias.ts +30 -0
- package/src/server/bounded-stream.ts +34 -0
- package/src/server/bug-report.ts +301 -0
- package/src/server/client-error-budget.ts +45 -0
- package/src/server/crash-report.ts +273 -0
- package/src/server/desktop-host-shutdown.ts +64 -0
- package/src/server/desktop-host.ts +190 -0
- package/src/server/desktop-server.ts +703 -0
- package/src/server/diffr-config.ts +509 -0
- package/src/server/diffr-languages.ts +97 -0
- package/src/server/global-verb-relay.ts +190 -0
- package/src/server/headless-host.ts +149 -0
- package/src/server/hono-http.ts +162 -0
- package/src/server/http-json.ts +24 -0
- package/src/server/json-review-reporting.ts +174 -0
- package/src/server/process-error-telemetry.ts +174 -0
- package/src/server/review-api-parsers.ts +90 -0
- package/src/server/review-lifecycle-telemetry.ts +73 -0
- package/src/server/review-open-watchdog.ts +46 -0
- package/src/server/review-server-core.ts +290 -0
- package/src/server/structural-comparisons.ts +153 -0
- package/src/server/structural-diff.ts +186 -0
- package/src/server/tutorial-service.ts +241 -0
- package/src/server/ui-telemetry.ts +198 -0
- package/src/server-discovery.ts +95 -0
- package/src/session-markers.ts +132 -0
- package/src/sharing/auth.ts +41 -0
- package/src/sharing/cli.ts +83 -0
- package/src/sharing/client.ts +334 -0
- package/src/sharing/export.ts +195 -0
- package/src/sharing/host.ts +316 -0
- package/src/sharing/import.ts +787 -0
- package/src/sharing/index.ts +16 -0
- package/src/sharing/repository.ts +145 -0
- package/src/sharing/routes.ts +34 -0
- package/src/slug.ts +23 -0
- package/src/software-map-diff-counts.ts +517 -0
- package/src/software-map-model.ts +1147 -0
- package/src/software-map-topology-diff.ts +260 -0
- package/src/source.ts +92 -0
- package/src/startup-trace.ts +232 -0
- package/src/stored-document-migration.ts +151 -0
- package/src/telemetry-clean-text.ts +257 -0
- package/src/telemetry-config.ts +298 -0
- package/src/telemetry-debug-sink.ts +38 -0
- package/src/telemetry.ts +13 -0
- package/src/trace-cli.ts +156 -0
- package/src/trace-storage-cli.ts +509 -0
- package/src/tutorial-conversation.ts +18 -0
- package/src/ui-telemetry-events.ts +765 -0
- package/src/unified-diff.ts +71 -0
- package/src/viewed-coverage.ts +259 -0
- package/src/windows-cli.ts +125 -0
- package/tutorial/document.json +275 -0
- package/tutorial/runtime-manifest.json +12 -0
- package/tutorial/sample-service/package.json +9 -0
- package/tutorial/sample-service/src/api/checkout-api.ts +17 -0
- package/tutorial/sample-service/src/app.ts +26 -0
- package/tutorial/sample-service/src/database/schema.ts +10 -0
- package/tutorial/sample-service/src/fulfillment/fulfillment-queue.ts +15 -0
- package/tutorial/sample-service/src/fulfillment/fulfillment-worker.ts +24 -0
- package/tutorial/sample-service/src/inventory/inventory-service.ts +11 -0
- package/tutorial/sample-service/src/orders/order-service.ts +34 -0
- package/tutorial/sample-service/src/orders/order.ts +21 -0
- package/tutorial/sample-service/src/orders/orders-repository.ts +24 -0
- package/tutorial/sample-service/src/payments/payment-gateway.ts +12 -0
- package/tutorial/sample-service/src/shipping/shipping-gateway.ts +15 -0
- package/tutorial/sample-service/tsconfig.json +12 -0
- package/tutorial/software-map.json +139 -0
- package/tutorial/trace.json +20 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type LensSource,
|
|
3
|
+
anchorSchema,
|
|
4
|
+
anchorSelection,
|
|
5
|
+
} from "@review/lens-selection.js";
|
|
6
|
+
import type { FileLineRange } from "@review/source.js";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
|
|
9
|
+
import { label } from "./blocks/definition.js";
|
|
10
|
+
import { ReviewInputError } from "./input-error.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Lenses partition a review's change for the Diff view. They live on the
|
|
14
|
+
* snapshot beside the document, are versioned with it, and are written one
|
|
15
|
+
* at a time with the lens command; the document never holds them.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const patternsSchema = z.array(label).min(1).max(1000);
|
|
19
|
+
|
|
20
|
+
export const lensTargetSchema = z.discriminatedUnion("kind", [
|
|
21
|
+
z.strictObject({ kind: z.literal("files"), patterns: patternsSchema }),
|
|
22
|
+
z.strictObject({
|
|
23
|
+
kind: z.literal("ranges"),
|
|
24
|
+
sources: z.array(anchorSchema).min(1).max(10000),
|
|
25
|
+
}),
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
export type LensTarget = z.infer<typeof lensTargetSchema>;
|
|
29
|
+
|
|
30
|
+
export const lensTargetsSchema = z
|
|
31
|
+
.array(lensTargetSchema)
|
|
32
|
+
.min(1)
|
|
33
|
+
.max(1000)
|
|
34
|
+
.describe(
|
|
35
|
+
"Select changed lines using repository-relative paths/globs or source ranges. Counts and viewed actions apply only to those lines.",
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
export const lensSchema = z.strictObject({
|
|
39
|
+
id: label,
|
|
40
|
+
title: label,
|
|
41
|
+
targets: lensTargetsSchema,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export type Lens = z.infer<typeof lensSchema>;
|
|
45
|
+
|
|
46
|
+
export const LENS_LIMIT = 200;
|
|
47
|
+
|
|
48
|
+
/** One lens edit. Ids are host-assigned (`lens-N`); an update patches only
|
|
49
|
+
* the fields it names. */
|
|
50
|
+
export const lensEditSchema = z.discriminatedUnion("type", [
|
|
51
|
+
z.strictObject({
|
|
52
|
+
type: z.literal("insert"),
|
|
53
|
+
title: label,
|
|
54
|
+
targets: lensTargetsSchema,
|
|
55
|
+
afterId: label
|
|
56
|
+
.optional()
|
|
57
|
+
.describe("The lens this one follows. Omitted appends."),
|
|
58
|
+
}),
|
|
59
|
+
z.strictObject({
|
|
60
|
+
type: z.literal("update"),
|
|
61
|
+
targetId: label,
|
|
62
|
+
title: label.optional(),
|
|
63
|
+
targets: lensTargetsSchema.optional(),
|
|
64
|
+
}),
|
|
65
|
+
z.strictObject({ type: z.literal("remove"), targetId: label }),
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
export type LensEdit = z.infer<typeof lensEditSchema>;
|
|
69
|
+
|
|
70
|
+
/** Patterns are evaluated against the changed-file list, never the
|
|
71
|
+
* filesystem, but must still read as repository-relative globs. */
|
|
72
|
+
export function checkLensTargets(targets: readonly LensTarget[]): void {
|
|
73
|
+
for (const pattern of targets.flatMap((target) =>
|
|
74
|
+
target.kind === "files" ? target.patterns : [],
|
|
75
|
+
))
|
|
76
|
+
if (
|
|
77
|
+
pattern.startsWith("/") ||
|
|
78
|
+
pattern.split("/").includes("..") ||
|
|
79
|
+
pattern.includes("\\") ||
|
|
80
|
+
pattern.includes("\0")
|
|
81
|
+
)
|
|
82
|
+
throw new ReviewInputError(
|
|
83
|
+
"File lens patterns must be repository-relative paths or globs using forward slashes.",
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Apply one edit in place and return the lens it landed on. */
|
|
88
|
+
export function applyLensEdit(
|
|
89
|
+
lenses: Lens[],
|
|
90
|
+
edit: LensEdit,
|
|
91
|
+
allocate: () => string,
|
|
92
|
+
): Lens {
|
|
93
|
+
const index = (id: string) => {
|
|
94
|
+
const at = lenses.findIndex((lens) => lens.id === id);
|
|
95
|
+
|
|
96
|
+
if (at < 0) throw new ReviewInputError(`Lens ${id} does not exist.`);
|
|
97
|
+
|
|
98
|
+
return at;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
if (edit.type === "insert") {
|
|
102
|
+
if (lenses.length >= LENS_LIMIT)
|
|
103
|
+
throw new ReviewInputError(
|
|
104
|
+
`A review holds at most ${LENS_LIMIT} lenses.`,
|
|
105
|
+
);
|
|
106
|
+
checkLensTargets(edit.targets);
|
|
107
|
+
|
|
108
|
+
const lens: Lens = {
|
|
109
|
+
id: allocate(),
|
|
110
|
+
title: edit.title,
|
|
111
|
+
targets: edit.targets,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
lenses.splice(
|
|
115
|
+
edit.afterId === undefined ? lenses.length : index(edit.afterId) + 1,
|
|
116
|
+
0,
|
|
117
|
+
lens,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return lens;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const at = index(edit.targetId);
|
|
124
|
+
const lens = lenses[at]!;
|
|
125
|
+
|
|
126
|
+
if (edit.type === "remove") {
|
|
127
|
+
lenses.splice(at, 1);
|
|
128
|
+
|
|
129
|
+
return lens;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (edit.title === undefined && edit.targets === undefined)
|
|
133
|
+
throw new ReviewInputError("A lens update names a title or targets.");
|
|
134
|
+
|
|
135
|
+
if (edit.targets) checkLensTargets(edit.targets);
|
|
136
|
+
|
|
137
|
+
const next: Lens = {
|
|
138
|
+
...lens,
|
|
139
|
+
...(edit.title !== undefined && { title: edit.title }),
|
|
140
|
+
...(edit.targets !== undefined && { targets: edit.targets }),
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
lenses[at] = next;
|
|
144
|
+
|
|
145
|
+
return next;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Every range a lens selects, with an id stable for its position. Lens
|
|
149
|
+
* ranges read at the review's pins. */
|
|
150
|
+
export function lensSelections(
|
|
151
|
+
lenses: readonly Lens[],
|
|
152
|
+
): { id: string; source: LensSource }[] {
|
|
153
|
+
return lenses.flatMap((lens) =>
|
|
154
|
+
lens.targets.flatMap((target, index) =>
|
|
155
|
+
target.kind === "ranges"
|
|
156
|
+
? target.sources.map((source, range) => ({
|
|
157
|
+
id: `${lens.id}:target:${index}:${range}`,
|
|
158
|
+
source: anchorSelection(source),
|
|
159
|
+
}))
|
|
160
|
+
: [],
|
|
161
|
+
),
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** A lens the Diff view lists: a named set of changed lines. */
|
|
166
|
+
export interface DiffLens {
|
|
167
|
+
id: string;
|
|
168
|
+
title: string;
|
|
169
|
+
sources: FileLineRange[];
|
|
170
|
+
fileCount?: number;
|
|
171
|
+
wholeFiles?: boolean;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** The lens id the Diff view gives changed lines no lens selects. */
|
|
175
|
+
export const UNCATEGORIZED_LENS_ID = "automatic-uncategorized";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { markdownText, parseMarkdown } from "@review/markdown.js";
|
|
2
|
+
import { slugify, uniqueId } from "@review/slug.js";
|
|
3
|
+
|
|
4
|
+
import { type Block, elements } from "./document.js";
|
|
5
|
+
|
|
6
|
+
export type ReviewHeadingLevel = "h2" | "h3";
|
|
7
|
+
|
|
8
|
+
/** A section title or a root-level h2/h3 of a markdown block. `block` rather
|
|
9
|
+
* than its id: import needs slugs before the store has assigned ids. */
|
|
10
|
+
export interface DocumentHeading {
|
|
11
|
+
id: string;
|
|
12
|
+
text: string;
|
|
13
|
+
level: ReviewHeadingLevel;
|
|
14
|
+
block: Block;
|
|
15
|
+
/** Position among the markdown block's own headings; absent for a section. */
|
|
16
|
+
index?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Heading ids by the rule the MDX renderer published, `slugify(text)` made
|
|
20
|
+
* unique in document order, so imported `#fragment` links still land. */
|
|
21
|
+
export function documentHeadings(blocks: Block[]): DocumentHeading[] {
|
|
22
|
+
const used = new Set<string>();
|
|
23
|
+
|
|
24
|
+
const assign = (heading: Omit<DocumentHeading, "id">): DocumentHeading => {
|
|
25
|
+
const id = uniqueId(slugify(heading.text) || "section", used);
|
|
26
|
+
used.add(id);
|
|
27
|
+
|
|
28
|
+
return { id, ...heading };
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return elements(blocks).flatMap((block): DocumentHeading[] => {
|
|
32
|
+
if (block.type === "section")
|
|
33
|
+
return [assign({ text: block.title, level: "h2", block })];
|
|
34
|
+
|
|
35
|
+
if (block.type !== "markdown") return [];
|
|
36
|
+
let index = 0;
|
|
37
|
+
|
|
38
|
+
return (parseMarkdown(block.markdown).children ?? []).flatMap((node) =>
|
|
39
|
+
node.type === "heading" && (node.depth === 2 || node.depth === 3)
|
|
40
|
+
? [
|
|
41
|
+
assign({
|
|
42
|
+
text: markdownText(node),
|
|
43
|
+
level: node.depth === 2 ? "h2" : "h3",
|
|
44
|
+
block,
|
|
45
|
+
index: index++,
|
|
46
|
+
}),
|
|
47
|
+
]
|
|
48
|
+
: [],
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { type Element, ReviewInputError, elements } from "./document.js";
|
|
2
|
+
import type { Snapshot } from "./store.js";
|
|
3
|
+
|
|
4
|
+
/** A reading view of saved content, not another document format to maintain. */
|
|
5
|
+
export function documentText(
|
|
6
|
+
snapshot: Snapshot,
|
|
7
|
+
targetId?: string,
|
|
8
|
+
full = false,
|
|
9
|
+
): string {
|
|
10
|
+
const target =
|
|
11
|
+
targetId === undefined
|
|
12
|
+
? undefined
|
|
13
|
+
: elements(snapshot.document).find((item) => item.id === targetId);
|
|
14
|
+
|
|
15
|
+
if (targetId !== undefined && !target)
|
|
16
|
+
throw new ReviewInputError("Target not found in this version.", 404);
|
|
17
|
+
const detailed = full || target !== undefined;
|
|
18
|
+
|
|
19
|
+
const lines = [
|
|
20
|
+
`# ${snapshot.title}`,
|
|
21
|
+
`Session ${snapshot.reviewId} · version ${snapshot.version}`,
|
|
22
|
+
"",
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const write = (depth: number, text: string) => {
|
|
26
|
+
for (const line of text.split("\n")) lines.push(" ".repeat(depth) + line);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const code = (depth: number, value: { language: string; text: string }) => {
|
|
30
|
+
write(depth, `Code (${value.language}):`);
|
|
31
|
+
write(depth + 1, value.text);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const render = (element: Element, depth: number) => {
|
|
35
|
+
const title = "title" in element ? element.title : undefined;
|
|
36
|
+
write(depth, `[${element.id}] ${element.type}${title ? `: ${title}` : ""}`);
|
|
37
|
+
const detail = (text: string) => write(depth + 1, text);
|
|
38
|
+
|
|
39
|
+
switch (element.type) {
|
|
40
|
+
case "section":
|
|
41
|
+
case "callout":
|
|
42
|
+
if (element.type === "callout") detail(`Tone: ${element.tone}`);
|
|
43
|
+
|
|
44
|
+
if (element.type === "section" && element.defaultCollapsed)
|
|
45
|
+
detail("Initially collapsed");
|
|
46
|
+
element.children.forEach((child) => render(child, depth + 1));
|
|
47
|
+
break;
|
|
48
|
+
case "markdown":
|
|
49
|
+
detail(
|
|
50
|
+
detailed
|
|
51
|
+
? element.markdown
|
|
52
|
+
: element.markdown.split("\n")[0] +
|
|
53
|
+
(element.markdown.includes("\n") ? " …" : ""),
|
|
54
|
+
);
|
|
55
|
+
break;
|
|
56
|
+
case "code":
|
|
57
|
+
if (element.caption) detail(element.caption);
|
|
58
|
+
|
|
59
|
+
if (detailed) code(depth + 1, element);
|
|
60
|
+
else
|
|
61
|
+
detail(
|
|
62
|
+
`${element.language}, ${element.text.split("\n").length} lines`,
|
|
63
|
+
);
|
|
64
|
+
break;
|
|
65
|
+
case "divider":
|
|
66
|
+
break;
|
|
67
|
+
case "code_peek":
|
|
68
|
+
detail(element.source);
|
|
69
|
+
|
|
70
|
+
if (element.caption) detail(element.caption);
|
|
71
|
+
break;
|
|
72
|
+
case "sequence":
|
|
73
|
+
for (const [key, label] of Object.entries(element.actors))
|
|
74
|
+
detail(`Actor ${key}: ${label}`);
|
|
75
|
+
element.steps.forEach((step) => render(step, depth + 1));
|
|
76
|
+
break;
|
|
77
|
+
case "step":
|
|
78
|
+
detail(
|
|
79
|
+
`${element.from} → ${element.to}: ${element.label} (${element.style})`,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
if (element.source) detail(element.source);
|
|
83
|
+
|
|
84
|
+
if (detailed && element.explanation) detail(element.explanation);
|
|
85
|
+
|
|
86
|
+
if (detailed && element.code) code(depth + 1, element.code);
|
|
87
|
+
break;
|
|
88
|
+
case "call_stack_diff":
|
|
89
|
+
for (const side of ["base", "head"] as const) {
|
|
90
|
+
detail(`${side}:`);
|
|
91
|
+
element[side].forEach((frame) => {
|
|
92
|
+
write(
|
|
93
|
+
depth + 2,
|
|
94
|
+
`${frame.label ?? frame.key ?? "Frame"}: ${frame.source}`,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
if (detailed && frame.via)
|
|
98
|
+
write(depth + 3, `${frame.via.kind}: ${frame.via.reason}`);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
break;
|
|
103
|
+
case "database_lens":
|
|
104
|
+
for (const [key, label] of Object.entries(element.actors))
|
|
105
|
+
detail(`Actor ${key}: ${label}`);
|
|
106
|
+
|
|
107
|
+
for (const [key, store] of Object.entries(element.stores)) {
|
|
108
|
+
detail(`Store ${key}: ${store.label} (${store.storage})`);
|
|
109
|
+
|
|
110
|
+
for (const [key, collection] of Object.entries(store.collections)) {
|
|
111
|
+
write(depth + 2, `Collection ${key}: ${collection.label}`);
|
|
112
|
+
|
|
113
|
+
if (detailed)
|
|
114
|
+
for (const [key, field] of Object.entries(collection.fields)) {
|
|
115
|
+
write(
|
|
116
|
+
depth + 3,
|
|
117
|
+
`${key}: ${field.label}, ${field.dataType}${field.primaryKey ? ", primary key" : ""}${field.nullable ? ", nullable" : ""}${field.references ? ` → ${field.references.store}.${field.references.collection}.${field.references.field}` : ""}`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (const useCase of element.useCases) {
|
|
124
|
+
detail(`Use case: ${useCase.label}`);
|
|
125
|
+
|
|
126
|
+
if (detailed && useCase.summary) write(depth + 2, useCase.summary);
|
|
127
|
+
|
|
128
|
+
for (const operation of useCase.operations)
|
|
129
|
+
write(
|
|
130
|
+
depth + 2,
|
|
131
|
+
`${operation.actor}: ${operation.kind} ${operation.store}.${operation.collection}${operation.field ? `.${operation.field}` : ""} — ${operation.label} (${operation.source})`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
break;
|
|
136
|
+
case "image":
|
|
137
|
+
detail(`${element.alt} (asset: ${element.assetId})`);
|
|
138
|
+
|
|
139
|
+
if (element.caption) detail(element.caption);
|
|
140
|
+
break;
|
|
141
|
+
case "trace_quote":
|
|
142
|
+
detail(`${element.traceId}, event ${element.eventId}`);
|
|
143
|
+
detail(element.text);
|
|
144
|
+
break;
|
|
145
|
+
case "flow_diagram":
|
|
146
|
+
if (element.description) detail(element.description);
|
|
147
|
+
|
|
148
|
+
for (const node of element.nodes) {
|
|
149
|
+
detail(`[${node.id}] ${node.key}: ${node.label}`);
|
|
150
|
+
|
|
151
|
+
if (detailed && node.description) write(depth + 2, node.description);
|
|
152
|
+
|
|
153
|
+
for (const attachment of node.attachments)
|
|
154
|
+
write(
|
|
155
|
+
depth + 2,
|
|
156
|
+
`${attachment.label}: ${attachment.sources.join(", ")}`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
for (const edge of element.edges)
|
|
161
|
+
detail(
|
|
162
|
+
`[${edge.id}] ${edge.from} → ${edge.to}${edge.label ? `: ${edge.label}` : ""}`,
|
|
163
|
+
);
|
|
164
|
+
break;
|
|
165
|
+
case "software_map":
|
|
166
|
+
detail(
|
|
167
|
+
`Map: ${element.mapVersionId}${element.focusElementId ? `, focus: ${element.focusElementId}` : ""}`,
|
|
168
|
+
);
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
(target ? [target] : snapshot.document).forEach((element) =>
|
|
174
|
+
render(element, 0),
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (!snapshot.document.length) lines.push("(Empty review)");
|
|
178
|
+
|
|
179
|
+
// Lenses sit beside the document; list them so their ids are at hand.
|
|
180
|
+
if (!target && snapshot.lenses?.length) {
|
|
181
|
+
lines.push("", "Lenses (edit with session_lens_edit):");
|
|
182
|
+
|
|
183
|
+
for (const lens of snapshot.lenses) {
|
|
184
|
+
write(0, `[${lens.id}] ${lens.title}`);
|
|
185
|
+
|
|
186
|
+
for (const item of lens.targets)
|
|
187
|
+
if (item.kind === "files")
|
|
188
|
+
write(1, `Files: ${item.patterns.join(", ")}`);
|
|
189
|
+
else for (const source of item.sources) write(1, `Range: ${source}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!detailed && snapshot.document.length)
|
|
194
|
+
lines.push(
|
|
195
|
+
"",
|
|
196
|
+
"Use targetId for one component's complete content, or full:true for the whole review.",
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
return lines.join("\n");
|
|
200
|
+
}
|