@dev.fast/whiteboard 0.4.0 → 0.4.1-preview.20261026.138
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/{account-alias-2yTAxoBx.js → account-alias-BPlPnzQM.js} +7 -7
- package/dist/{agent-cli-CHNDd4Ga.js → agent-cli-BbCf9uid.js} +11 -11
- package/dist/agent-cli-ipN_JmzG.js +2 -0
- package/dist/{agent-client-Dc0vHFJt.js → agent-client-DfpM8F5N.js} +3 -3
- package/dist/{authoring-tools-D8EkIDjm.js → authoring-tools-bm39DRoV.js} +23 -10
- package/dist/{background-server-DyWnPJyZ.js → background-server--0EeCCeV.js} +1 -1
- package/dist/{background-server-DHWHzpo5.js → background-server-B--5p72o.js} +6 -6
- package/dist/build-info.json +1 -1
- package/dist/{cli-qZomwJmK.js → cli-DBTQIroM.js} +2 -2
- package/dist/{cli-runner-DFBaVGij.js → cli-runner-CRFCdb7G.js} +25 -25
- package/dist/cli.js +3 -3
- package/dist/{fuzzy-match-CfSxqzBF.js → fuzzy-match-l3mJZ1Ge.js} +5 -5
- package/dist/{headless-host-dwlqhqMY.js → headless-host-5ei3nHS8.js} +1 -1
- package/dist/{headless-host-DJhcab6r.js → headless-host-BkJ9OUQY.js} +131 -299
- package/dist/{client-p9WB8Sj0.js → import-Bwvf9Wos.js} +505 -390
- package/dist/{lens-selection-9byzEgvU.js → lens-selection-EuZdag4Q.js} +1 -1
- package/dist/{local-data-DHedfhxw.js → local-data-BCSeTyic.js} +98 -154
- package/dist/{mcp-Br-r_wwR.js → mcp-_Xg1gQuA.js} +17 -8
- package/dist/{package-paths-BeYHYjeR.js → package-paths-Bg731FHS.js} +2 -2
- package/dist/profile-C8zL4u3L.js +2 -0
- package/dist/{profile-D9wdlPgw.js → profile-xzrUFgbT.js} +4 -4
- package/dist/{remote-attach-DSGpTOJ4.js → remote-attach-DF25Epoa.js} +4 -4
- package/dist/{remote-extensions-WtJO7pkb.js → remote-extensions-BJamgsCe.js} +1 -1
- package/dist/{remote-extensions-CsI_BJ_f.js → remote-extensions-CeywZL-Z.js} +4 -4
- package/dist/{remote-language-server-Dx13Mg2m.js → remote-language-server-CH8sG_CU.js} +6 -6
- package/dist/{remote-uninstall-Dp2o3qz0.js → remote-uninstall-Cq-lkBY7.js} +7 -7
- package/dist/{request-origin-CNSLVn7x.js → request-origin-CargJFvH.js} +14 -3
- package/dist/{review-app-launcher-BZAnR8Vp.js → review-app-launcher-D8ApNDUV.js} +3 -3
- package/dist/{review-home-paths-CG-sb9Cw.js → review-home-paths-DcB4xLD-.js} +2 -2
- package/dist/{review-telemetry-BM-_KdLi.js → review-telemetry-BW7gJrSo.js} +7 -7
- package/dist/{runtime-C6_1K-3Z.js → runtime-DY6Ctyog.js} +1 -1
- package/dist/runtime.js +1 -1
- package/dist/{s3-CsG2EWED.js → s3-BIWv4h1J.js} +2 -2
- package/dist/{s3-config-zz4GWh2T.js → s3-config-Du3RBIvv.js} +1 -1
- package/dist/server/desktop-host.js +51 -17
- package/dist/{server-discovery-DKLIOgg3.js → server-discovery-aHK_1r3U.js} +5 -5
- package/dist/sharing/index.d.ts +209 -98
- package/dist/sharing/index.js +1 -1
- package/dist/{src-DYHVzX3b.js → src-DEOc8bvC.js} +63 -2
- package/dist/{src-Z1x4-Qmb.js → src-DjRaCctk.js} +6 -6
- package/dist/{stored-document-migration-DEt8iwem.js → stored-document-migration-CwmTwTb8.js} +139 -13
- package/dist/{thread-state-CO97FUqK.js → thread-state-CbLB0jUA.js} +1 -1
- package/dist/{tool-failure-C1L1vJyz.js → tool-failure-DZaQzi4f.js} +2 -2
- package/instructions/animation.md +345 -0
- package/instructions/authoring.md +2 -2
- package/package.json +6 -6
- package/src/fixtures/blocks/animation.json +19 -0
- package/src/review-api/authoring-tools.ts +17 -6
- package/src/review-api/blocks/animation.ts +98 -0
- package/src/review-api/blocks/index.ts +3 -0
- package/src/review-api/blocks/sequence.ts +3 -1
- package/src/review-api/comparison-coverage.ts +120 -89
- package/src/review-api/component-reference.ts +16 -0
- package/src/review-api/document-text.ts +11 -0
- package/src/review-api/document.ts +26 -5
- package/src/review-api/http.ts +31 -2
- package/src/review-api/instructions.ts +11 -2
- package/src/review-api/local-data.ts +21 -4
- package/src/review-api/mcp.ts +22 -14
- package/src/review-api/onboarding.ts +100 -26
- package/src/review-preferences.ts +43 -0
- package/src/server/desktop-server.ts +24 -0
- package/src/server/json-review-reporting.ts +1 -1
- package/src/server/review-server-core.ts +2 -0
- package/src/server/structural-comparisons.ts +5 -11
- package/src/server/structural-diff.ts +8 -2
- package/src/sharing/cli.ts +1 -1
- package/src/sharing/client.ts +54 -222
- package/src/sharing/export.ts +172 -114
- package/src/sharing/host.ts +32 -36
- package/src/sharing/import.ts +93 -147
- package/src/sharing/index.ts +1 -6
- package/src/sharing/routes.ts +18 -17
- package/src/ui-telemetry-events.ts +11 -0
- package/dist/agent-cli-Cn4-16XA.js +0 -2
- package/dist/profile-BopGMZUh.js +0 -2
- package/dist/{s3-config-CjcLyFV5.js → s3-config-D7Yt0VhS.js} +1 -1
- package/dist/{src-X9ZebeOD.js → src-DXnn7RrI.js} +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
import crypto from "node:crypto";
|
|
3
|
-
import { stat } from "node:fs/promises";
|
|
3
|
+
import { readFile, stat } from "node:fs/promises";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
|
|
@@ -82,25 +82,54 @@ async function modified(file: string): Promise<number> {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/** The folder holding a bare clone's `.git` file, as in `project/.bare`;
|
|
86
|
+
* else the clone itself. */
|
|
87
|
+
async function bareRoot(bare: string) {
|
|
88
|
+
const parent = path.dirname(bare);
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const gitdir = /^gitdir:\s*(.+)$/m.exec(
|
|
92
|
+
await readFile(path.join(parent, ".git"), "utf8"),
|
|
93
|
+
)?.[1];
|
|
94
|
+
|
|
95
|
+
if (gitdir && path.resolve(parent, gitdir.trim()) === bare) return parent;
|
|
96
|
+
} catch {
|
|
97
|
+
// No `.git` file beside it.
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return bare;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** A folder's project: its main checkout, its bare clone, or itself. */
|
|
104
|
+
async function projectOf(folder: string, run: OnboardingRun) {
|
|
105
|
+
const git = (args: string[], cwd = folder) =>
|
|
106
|
+
run("git", args, cwd).then((output) => output.trim());
|
|
107
|
+
|
|
108
|
+
const common = path.resolve(
|
|
109
|
+
folder,
|
|
110
|
+
await git(["rev-parse", "--path-format=absolute", "--git-common-dir"]),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (path.basename(common) === ".git")
|
|
114
|
+
return { root: path.dirname(common), common, bare: false };
|
|
115
|
+
|
|
116
|
+
if ((await git(["rev-parse", "--is-bare-repository"], common)) === "true")
|
|
117
|
+
return { root: await bareRoot(common), common, bare: true };
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
root: await git(["rev-parse", "--show-toplevel"]),
|
|
121
|
+
common,
|
|
122
|
+
bare: false,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
85
126
|
/** The repository a folder belongs to, and when it last saw Git activity.
|
|
86
127
|
* Worktrees resolve to their main checkout, which lists them as targets. */
|
|
87
128
|
async function repositoryOf(folder: string, run: OnboardingRun) {
|
|
88
129
|
try {
|
|
89
130
|
if (!(await stat(folder)).isDirectory()) return undefined;
|
|
90
131
|
|
|
91
|
-
const common =
|
|
92
|
-
folder,
|
|
93
|
-
(
|
|
94
|
-
await run(
|
|
95
|
-
"git",
|
|
96
|
-
["rev-parse", "--path-format=absolute", "--git-common-dir"],
|
|
97
|
-
folder,
|
|
98
|
-
)
|
|
99
|
-
).trim(),
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
// A bare repository has no checkout to draw from.
|
|
103
|
-
if (path.basename(common) !== ".git") return undefined;
|
|
132
|
+
const { root, common } = await projectOf(folder, run);
|
|
104
133
|
|
|
105
134
|
const activity = await Promise.all(
|
|
106
135
|
["index", "FETCH_HEAD", path.join("logs", "HEAD")].map((file) =>
|
|
@@ -108,7 +137,7 @@ async function repositoryOf(folder: string, run: OnboardingRun) {
|
|
|
108
137
|
),
|
|
109
138
|
);
|
|
110
139
|
|
|
111
|
-
return { root
|
|
140
|
+
return { root, active: Math.max(...activity) };
|
|
112
141
|
} catch {
|
|
113
142
|
return undefined;
|
|
114
143
|
}
|
|
@@ -151,6 +180,8 @@ export async function onboardingProjects(
|
|
|
151
180
|
interface Worktree {
|
|
152
181
|
path: string;
|
|
153
182
|
branch?: string;
|
|
183
|
+
bare?: true;
|
|
184
|
+
prunable?: true;
|
|
154
185
|
}
|
|
155
186
|
|
|
156
187
|
/** `git worktree list --porcelain`: blocks of `worktree`, `HEAD`, `branch`. */
|
|
@@ -162,15 +193,40 @@ export function parseWorktrees(output: string): Worktree[] {
|
|
|
162
193
|
if (line.startsWith("worktree ")) {
|
|
163
194
|
current = { path: line.slice("worktree ".length) };
|
|
164
195
|
worktrees.push(current);
|
|
165
|
-
} else if (current
|
|
196
|
+
} else if (!current) continue;
|
|
197
|
+
else if (line.startsWith("branch "))
|
|
166
198
|
current.branch = line
|
|
167
199
|
.slice("branch ".length)
|
|
168
200
|
.replace(/^refs\/heads\//, "");
|
|
201
|
+
else if (line === "bare") current.bare = true;
|
|
202
|
+
else if (line.startsWith("prunable")) current.prunable = true;
|
|
169
203
|
}
|
|
170
204
|
|
|
171
205
|
return worktrees;
|
|
172
206
|
}
|
|
173
207
|
|
|
208
|
+
const drawable = (worktree: Worktree) =>
|
|
209
|
+
!worktree.bare &&
|
|
210
|
+
!worktree.prunable &&
|
|
211
|
+
// Whiteboard's own checkouts live under the git dir.
|
|
212
|
+
!worktree.path.split(/[\\/]/).includes("dev-fast") &&
|
|
213
|
+
// Scratch checkouts, such as an agent's throwaway worktree.
|
|
214
|
+
!isInside(TEMPORARY_ROOTS, worktree.path);
|
|
215
|
+
|
|
216
|
+
/** In a bare clone, a pull request is drawn from the worktree on its branch. */
|
|
217
|
+
async function checkoutOf(project: string, branch: string, run: OnboardingRun) {
|
|
218
|
+
const worktrees = parseWorktrees(
|
|
219
|
+
await run("git", ["worktree", "list", "--porcelain"], project).catch(
|
|
220
|
+
() => "",
|
|
221
|
+
),
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
if (!worktrees[0]?.bare) return project;
|
|
225
|
+
const usable = worktrees.filter(drawable);
|
|
226
|
+
|
|
227
|
+
return (usable.find((tree) => tree.branch === branch) ?? usable[0])?.path;
|
|
228
|
+
}
|
|
229
|
+
|
|
174
230
|
const pullRequestsSchema = z.array(
|
|
175
231
|
z.object({
|
|
176
232
|
number: z.number().int().positive(),
|
|
@@ -281,20 +337,31 @@ export async function onboardingTargets(
|
|
|
281
337
|
run: OnboardingRun = runCommand,
|
|
282
338
|
): Promise<ReviewOnboardingTargetsResponse> {
|
|
283
339
|
let root: string;
|
|
340
|
+
let bare = false;
|
|
284
341
|
|
|
285
342
|
try {
|
|
286
343
|
root = (
|
|
287
344
|
await run("git", ["rev-parse", "--show-toplevel"], directory)
|
|
288
345
|
).trim();
|
|
289
346
|
} catch {
|
|
290
|
-
|
|
347
|
+
const project = await projectOf(directory, run).catch(() => undefined);
|
|
348
|
+
|
|
349
|
+
if (!project?.bare)
|
|
350
|
+
throw new ReviewInputError(
|
|
351
|
+
"Choose a folder inside a Git repository.",
|
|
352
|
+
400,
|
|
353
|
+
);
|
|
354
|
+
root = project.root;
|
|
355
|
+
bare = true;
|
|
291
356
|
}
|
|
292
357
|
|
|
293
358
|
const [branch, worktreeList, base, prs, times] = await Promise.all([
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
(
|
|
297
|
-
|
|
359
|
+
bare
|
|
360
|
+
? ""
|
|
361
|
+
: run("git", ["branch", "--show-current"], root).then(
|
|
362
|
+
(output) => output.trim(),
|
|
363
|
+
() => "",
|
|
364
|
+
),
|
|
298
365
|
run("git", ["worktree", "list", "--porcelain"], root).catch(() => ""),
|
|
299
366
|
defaultBase(root, run),
|
|
300
367
|
pullRequests(root, run),
|
|
@@ -311,10 +378,7 @@ export async function onboardingTargets(
|
|
|
311
378
|
(worktree) =>
|
|
312
379
|
worktree.branch &&
|
|
313
380
|
path.resolve(worktree.path) !== path.resolve(root) &&
|
|
314
|
-
|
|
315
|
-
!worktree.path.split(/[\\/]/).includes("dev-fast") &&
|
|
316
|
-
// Scratch checkouts, such as an agent's throwaway worktree.
|
|
317
|
-
!isInside(TEMPORARY_ROOTS, worktree.path),
|
|
381
|
+
drawable(worktree),
|
|
318
382
|
)
|
|
319
383
|
.map((worktree) =>
|
|
320
384
|
local({
|
|
@@ -417,7 +481,17 @@ export function registerOnboardingRoutes(
|
|
|
417
481
|
const { target } = input;
|
|
418
482
|
|
|
419
483
|
// A pull request is drawn from the project; a branch from its checkout.
|
|
420
|
-
const cwd =
|
|
484
|
+
const cwd =
|
|
485
|
+
target.kind === "pr"
|
|
486
|
+
? await checkoutOf(input.projectPath, target.branch, run)
|
|
487
|
+
: target.path;
|
|
488
|
+
|
|
489
|
+
if (!cwd)
|
|
490
|
+
throw new ReviewInputError(
|
|
491
|
+
"This bare clone has no worktrees. Add one with git worktree add.",
|
|
492
|
+
400,
|
|
493
|
+
);
|
|
494
|
+
|
|
421
495
|
let head: string;
|
|
422
496
|
|
|
423
497
|
try {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { isJsonObject, parseJsonText } from "@dev.fast/review-protocol";
|
|
5
|
+
import { writePrivateJsonAtomic } from "@dev.fast/trace-core";
|
|
6
|
+
|
|
7
|
+
import { devReviewHome } from "./review-home-paths";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Machine-wide Review preferences the server itself needs. Workbench settings
|
|
11
|
+
* do not work here: the review server never reads the workbench
|
|
12
|
+
* configuration.
|
|
13
|
+
*/
|
|
14
|
+
function reviewPreferencesPath(devHome = devReviewHome()): string {
|
|
15
|
+
return path.join(devHome, "preferences.json");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Whether agents are offered animation authoring; existing animations
|
|
19
|
+
* always render. A missing or unreadable file means off; it never throws. */
|
|
20
|
+
export async function readAnimationsEnabled(
|
|
21
|
+
devHome?: string,
|
|
22
|
+
): Promise<boolean> {
|
|
23
|
+
try {
|
|
24
|
+
const raw = parseJsonText(
|
|
25
|
+
await readFile(reviewPreferencesPath(devHome), "utf8"),
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
return isJsonObject(raw) && raw.animationsEnabled === true;
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function writeAnimationsEnabled(
|
|
35
|
+
enabled: boolean,
|
|
36
|
+
devHome?: string,
|
|
37
|
+
): Promise<boolean> {
|
|
38
|
+
await writePrivateJsonAtomic(reviewPreferencesPath(devHome), {
|
|
39
|
+
animationsEnabled: enabled,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return enabled;
|
|
43
|
+
}
|
|
@@ -46,6 +46,10 @@ import {
|
|
|
46
46
|
reviewInstanceDiscoveryPath,
|
|
47
47
|
reviewLegacyDiscoveryPath,
|
|
48
48
|
} from "@review/review-home-paths";
|
|
49
|
+
import {
|
|
50
|
+
readAnimationsEnabled,
|
|
51
|
+
writeAnimationsEnabled,
|
|
52
|
+
} from "@review/review-preferences";
|
|
49
53
|
import {
|
|
50
54
|
ReviewTelemetry,
|
|
51
55
|
type ReviewTelemetryContext,
|
|
@@ -173,6 +177,10 @@ export function createGlobalReviewServer(
|
|
|
173
177
|
let closing = false;
|
|
174
178
|
const cliPath = path.join(input.packageRoot, "dist", "cli.js");
|
|
175
179
|
|
|
180
|
+
// The animations preference, read once at listen and kept current by the
|
|
181
|
+
// settings endpoint below: this server is the only writer while it runs.
|
|
182
|
+
let animationsEnabled = false;
|
|
183
|
+
|
|
176
184
|
const discovery: ReviewDesktopDiscovery = {
|
|
177
185
|
version: REVIEW_DESKTOP_DISCOVERY_VERSION,
|
|
178
186
|
instanceId,
|
|
@@ -205,6 +213,7 @@ export function createGlobalReviewServer(
|
|
|
205
213
|
relay,
|
|
206
214
|
token,
|
|
207
215
|
instanceId,
|
|
216
|
+
animations: () => animationsEnabled,
|
|
208
217
|
status: () => {
|
|
209
218
|
const { key, channel, checkout, appVersion, cliVersion, instanceId } =
|
|
210
219
|
discovery;
|
|
@@ -257,6 +266,20 @@ export function createGlobalReviewServer(
|
|
|
257
266
|
app.all("/reviews-api", (context) => gateway.fetch(context.req.raw));
|
|
258
267
|
app.all("/reviews-api/*", (context) => gateway.fetch(context.req.raw));
|
|
259
268
|
app.get("/remote-hosts", () => serverJson(200, gateway.hosts()));
|
|
269
|
+
app.get("/preferences/animations", () =>
|
|
270
|
+
serverJson(200, { enabled: animationsEnabled }),
|
|
271
|
+
);
|
|
272
|
+
app.put("/preferences/animations", async (context) => {
|
|
273
|
+
const request = z
|
|
274
|
+
.object({ enabled: z.boolean() })
|
|
275
|
+
.safeParse(await readBoundedRequestJson(context.req.raw));
|
|
276
|
+
|
|
277
|
+
if (!request.success)
|
|
278
|
+
throw new ReviewServerError("enabled must be a boolean.", 400);
|
|
279
|
+
animationsEnabled = await writeAnimationsEnabled(request.data.enabled);
|
|
280
|
+
|
|
281
|
+
return serverJson(200, { enabled: animationsEnabled });
|
|
282
|
+
});
|
|
260
283
|
app.post("/app/focus", async () => {
|
|
261
284
|
const result = await relay.dispatch({
|
|
262
285
|
name: "focusWindow",
|
|
@@ -499,6 +522,7 @@ export function createGlobalReviewServer(
|
|
|
499
522
|
},
|
|
500
523
|
listen: async () => {
|
|
501
524
|
await migrateDiffrConfig();
|
|
525
|
+
animationsEnabled = await readAnimationsEnabled();
|
|
502
526
|
boundPort = await listen(loopback.server, input.port);
|
|
503
527
|
discovery.url = urlForBoundPort();
|
|
504
528
|
|
|
@@ -168,7 +168,7 @@ export function createJsonReviewReporting(
|
|
|
168
168
|
files: { "review.json": JSON.stringify(snapshot) },
|
|
169
169
|
omitted: [],
|
|
170
170
|
}),
|
|
171
|
-
map: async () => JSON.stringify(imported.
|
|
171
|
+
map: async () => JSON.stringify(imported.maps),
|
|
172
172
|
diff: () => jsonReviewBugReportSource(store, snapshot).diff(),
|
|
173
173
|
}
|
|
174
174
|
: jsonReviewBugReportSource(store, snapshot),
|
|
@@ -132,6 +132,7 @@ export interface WhiteboardCoreInput {
|
|
|
132
132
|
token: string;
|
|
133
133
|
instanceId: string;
|
|
134
134
|
softwareMapEnabled?: boolean;
|
|
135
|
+
animations?: () => boolean;
|
|
135
136
|
status: () => JsonObject;
|
|
136
137
|
hooks?: ReviewApiHooks;
|
|
137
138
|
ask?: {
|
|
@@ -198,6 +199,7 @@ export function createWhiteboardCore(input: WhiteboardCoreInput) {
|
|
|
198
199
|
threads: askThreads,
|
|
199
200
|
agents: async () => detectAskAgents(await ask?.env),
|
|
200
201
|
},
|
|
202
|
+
input.animations,
|
|
201
203
|
);
|
|
202
204
|
|
|
203
205
|
// A shared store mounts the publisher with the rest of sharing.
|
|
@@ -42,19 +42,16 @@ export class StructuralComparisons {
|
|
|
42
42
|
} finally {
|
|
43
43
|
entry.readers--;
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
entry.failed
|
|
49
|
-
) {
|
|
45
|
+
// Summaries arrive after the files, so a run nobody reads stops. Full
|
|
46
|
+
// comparisons keep coverage as a reader until diffr completes.
|
|
47
|
+
if ((!entry.done && !entry.readers) || entry.error || entry.failed) {
|
|
50
48
|
entry.abort.abort();
|
|
51
49
|
this.entries.delete(key);
|
|
52
50
|
}
|
|
53
|
-
// Keep the most recently read idle comparisons, including background enrichment.
|
|
54
|
-
// Coverage can finish before an editor subscribes; it must not cancel summaries.
|
|
55
51
|
|
|
52
|
+
// Keep the most recently read finished comparisons.
|
|
56
53
|
const idle = [...this.entries].filter(
|
|
57
|
-
([, value]) =>
|
|
54
|
+
([, value]) => value.done && !value.readers,
|
|
58
55
|
);
|
|
59
56
|
|
|
60
57
|
for (const [oldKey, old] of idle.slice(0, -IDLE_COMPARISONS)) {
|
|
@@ -124,7 +121,6 @@ class Comparison {
|
|
|
124
121
|
done = false;
|
|
125
122
|
error: unknown;
|
|
126
123
|
failed = false;
|
|
127
|
-
initialReady = false;
|
|
128
124
|
private remaining: Set<string> | undefined;
|
|
129
125
|
private readonly listeners = new Set<() => void>();
|
|
130
126
|
|
|
@@ -144,13 +140,11 @@ class Comparison {
|
|
|
144
140
|
(entry) => (entry.file.rhs ?? entry.file.lhs)!.path,
|
|
145
141
|
),
|
|
146
142
|
);
|
|
147
|
-
this.initialReady = this.remaining.size === 0;
|
|
148
143
|
} else if (event.type === "file") {
|
|
149
144
|
const path = (event.file.rhs ?? event.file.lhs)!.path;
|
|
150
145
|
|
|
151
146
|
if (!this.remaining?.delete(path))
|
|
152
147
|
throw new Error(`Unexpected structural result: ${path}`);
|
|
153
|
-
this.initialReady = this.remaining.size === 0;
|
|
154
148
|
}
|
|
155
149
|
|
|
156
150
|
if (event.type === "complete")
|
|
@@ -61,7 +61,7 @@ export async function* structuralDiff(
|
|
|
61
61
|
args.push(`${comparison.base}...${comparison.head}`);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
args.push("--", ...(input.paths ?? []));
|
|
64
|
+
args.push("--defer", "--", ...(input.paths ?? []));
|
|
65
65
|
|
|
66
66
|
// Plugins can exceed two minutes between records. Apply this timeout only
|
|
67
67
|
// until the manifest arrives; plugins enforce their own request timeouts.
|
|
@@ -101,6 +101,7 @@ export async function* structuralDiff(
|
|
|
101
101
|
let completed = false;
|
|
102
102
|
let aborted: StructuralProblem | undefined;
|
|
103
103
|
let failed = 0;
|
|
104
|
+
const sent = new Set<string>();
|
|
104
105
|
const lines = createInterface({ input: child.stdout, crlfDelay: Infinity });
|
|
105
106
|
|
|
106
107
|
try {
|
|
@@ -131,7 +132,12 @@ export async function* structuralDiff(
|
|
|
131
132
|
|
|
132
133
|
failed = event.failed;
|
|
133
134
|
aborted = event.aborted;
|
|
134
|
-
} else if (event.type
|
|
135
|
+
} else if (event.type === "file") {
|
|
136
|
+
sent.add((event.file.rhs ?? event.file.lhs)!.path);
|
|
137
|
+
} else if (
|
|
138
|
+
event.type !== "update" ||
|
|
139
|
+
!sent.has((event.file.rhs ?? event.file.lhs)!.path)
|
|
140
|
+
) {
|
|
135
141
|
throw new Error(`Unexpected diffr event: ${event.type}`);
|
|
136
142
|
}
|
|
137
143
|
|
package/src/sharing/cli.ts
CHANGED
|
@@ -43,7 +43,7 @@ export async function runShareCli(input: {
|
|
|
43
43
|
: z.number().int().nonnegative().parse(Number(input.version));
|
|
44
44
|
|
|
45
45
|
input.stderr.write(
|
|
46
|
-
"Sharing
|
|
46
|
+
"Sharing uploads the whiteboard with its maps, full trace conversations, and the code lines it references. Anyone with the link can read them; no GitHub access is needed. Opening it in Whiteboard Desktop also fetches the pinned commits from GitHub.\n",
|
|
47
47
|
);
|
|
48
48
|
|
|
49
49
|
const result = resultSchema.parse(
|