@stigmer/react 3.1.2 → 3.1.4
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/composer/SessionComposer.d.ts +37 -5
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +13 -5
- package/composer/SessionComposer.js.map +1 -1
- package/execution/ArtifactApplyButton.d.ts +25 -0
- package/execution/ArtifactApplyButton.d.ts.map +1 -0
- package/execution/ArtifactApplyButton.js +21 -0
- package/execution/ArtifactApplyButton.js.map +1 -0
- package/execution/ArtifactContentBody.d.ts +36 -0
- package/execution/ArtifactContentBody.d.ts.map +1 -0
- package/execution/ArtifactContentBody.js +62 -0
- package/execution/ArtifactContentBody.js.map +1 -0
- package/execution/ArtifactDocument.d.ts +48 -0
- package/execution/ArtifactDocument.d.ts.map +1 -0
- package/execution/ArtifactDocument.js +89 -0
- package/execution/ArtifactDocument.js.map +1 -0
- package/execution/ArtifactPreviewModal.d.ts +9 -9
- package/execution/ArtifactPreviewModal.d.ts.map +1 -1
- package/execution/ArtifactPreviewModal.js +18 -145
- package/execution/ArtifactPreviewModal.js.map +1 -1
- package/execution/ArtifactRow.d.ts +49 -0
- package/execution/ArtifactRow.d.ts.map +1 -0
- package/execution/ArtifactRow.js +56 -0
- package/execution/ArtifactRow.js.map +1 -0
- package/execution/ArtifactsWidget.d.ts +6 -9
- package/execution/ArtifactsWidget.d.ts.map +1 -1
- package/execution/ArtifactsWidget.js +15 -19
- package/execution/ArtifactsWidget.js.map +1 -1
- package/execution/FileChangeProgressBar.d.ts +47 -0
- package/execution/FileChangeProgressBar.d.ts.map +1 -0
- package/execution/FileChangeProgressBar.js +49 -0
- package/execution/FileChangeProgressBar.js.map +1 -0
- package/execution/FileChangesView.d.ts.map +1 -1
- package/execution/FileChangesView.js +19 -4
- package/execution/FileChangesView.js.map +1 -1
- package/execution/FileReviewAtoms.d.ts +27 -0
- package/execution/FileReviewAtoms.d.ts.map +1 -0
- package/execution/FileReviewAtoms.js +64 -0
- package/execution/FileReviewAtoms.js.map +1 -0
- package/execution/FileReviewCard.d.ts +2 -2
- package/execution/FileReviewCard.d.ts.map +1 -1
- package/execution/FileReviewCard.js +32 -62
- package/execution/FileReviewCard.js.map +1 -1
- package/execution/MessageEntry.d.ts +0 -9
- package/execution/MessageEntry.d.ts.map +1 -1
- package/execution/MessageEntry.js +1 -11
- package/execution/MessageEntry.js.map +1 -1
- package/execution/MessageThread.d.ts +3 -4
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +25 -11
- package/execution/MessageThread.js.map +1 -1
- package/execution/PlanArtifactCard.d.ts +16 -10
- package/execution/PlanArtifactCard.d.ts.map +1 -1
- package/execution/PlanArtifactCard.js +31 -7
- package/execution/PlanArtifactCard.js.map +1 -1
- package/execution/PlanCompletionCard.d.ts +4 -4
- package/execution/PlanCompletionCard.d.ts.map +1 -1
- package/execution/PlanCompletionCard.js +4 -4
- package/execution/PlanCompletionCard.js.map +1 -1
- package/execution/PlanStreamingCard.d.ts +7 -2
- package/execution/PlanStreamingCard.d.ts.map +1 -1
- package/execution/PlanStreamingCard.js +11 -6
- package/execution/PlanStreamingCard.js.map +1 -1
- package/execution/index.d.ts +12 -2
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +6 -1
- package/execution/index.js.map +1 -1
- package/execution/use-build-from-plan-hotkey.d.ts +1 -1
- package/execution/use-build-from-plan-hotkey.js +1 -1
- package/execution/useArtifactContent.d.ts +6 -1
- package/execution/useArtifactContent.d.ts.map +1 -1
- package/execution/useArtifactContent.js +7 -2
- package/execution/useArtifactContent.js.map +1 -1
- package/execution/useArtifactCopy.d.ts +51 -0
- package/execution/useArtifactCopy.d.ts.map +1 -0
- package/execution/useArtifactCopy.js +75 -0
- package/execution/useArtifactCopy.js.map +1 -0
- package/execution/useArtifactDownload.d.ts.map +1 -1
- package/execution/useArtifactDownload.js +17 -5
- package/execution/useArtifactDownload.js.map +1 -1
- package/execution/useArtifactInspection.d.ts +85 -0
- package/execution/useArtifactInspection.d.ts.map +1 -0
- package/execution/useArtifactInspection.js +143 -0
- package/execution/useArtifactInspection.js.map +1 -0
- package/execution/useCreateAgentExecution.d.ts +16 -3
- package/execution/useCreateAgentExecution.d.ts.map +1 -1
- package/execution/useCreateAgentExecution.js +1 -0
- package/execution/useCreateAgentExecution.js.map +1 -1
- package/index.d.ts +4 -4
- package/index.d.ts.map +1 -1
- package/index.js +3 -3
- package/index.js.map +1 -1
- package/internal/MermaidDiagram.d.ts +47 -0
- package/internal/MermaidDiagram.d.ts.map +1 -0
- package/internal/MermaidDiagram.js +118 -0
- package/internal/MermaidDiagram.js.map +1 -0
- package/internal/file-tree/FileTreeNode.d.ts.map +1 -1
- package/internal/file-tree/FileTreeNode.js +2 -2
- package/internal/file-tree/FileTreeNode.js.map +1 -1
- package/internal/markdown-components.d.ts.map +1 -1
- package/internal/markdown-components.js +34 -0
- package/internal/markdown-components.js.map +1 -1
- package/internal/mermaid-loader.d.ts +26 -0
- package/internal/mermaid-loader.d.ts.map +1 -0
- package/internal/mermaid-loader.js +34 -0
- package/internal/mermaid-loader.js.map +1 -0
- package/internal/mermaid-theme-css.d.ts +41 -0
- package/internal/mermaid-theme-css.d.ts.map +1 -0
- package/internal/mermaid-theme-css.js +179 -0
- package/internal/mermaid-theme-css.js.map +1 -0
- package/internal/slug.d.ts +9 -5
- package/internal/slug.d.ts.map +1 -1
- package/internal/slug.js +10 -6
- package/internal/slug.js.map +1 -1
- package/library/detect-plan-artifact.d.ts +27 -9
- package/library/detect-plan-artifact.d.ts.map +1 -1
- package/library/detect-plan-artifact.js +26 -6
- package/library/detect-plan-artifact.js.map +1 -1
- package/library/index.d.ts +1 -1
- package/library/index.d.ts.map +1 -1
- package/library/index.js +1 -1
- package/library/index.js.map +1 -1
- package/package.json +5 -4
- package/session/PlanEditor.js +1 -1
- package/session/PlanEditor.js.map +1 -1
- package/session/SessionViewer.d.ts.map +1 -1
- package/session/SessionViewer.js +122 -30
- package/session/SessionViewer.js.map +1 -1
- package/session/artifact-document.d.ts +13 -0
- package/session/artifact-document.d.ts.map +1 -0
- package/session/artifact-document.js +22 -0
- package/session/artifact-document.js.map +1 -0
- package/session/facets/ArtifactsTab.d.ts +30 -4
- package/session/facets/ArtifactsTab.d.ts.map +1 -1
- package/session/facets/ArtifactsTab.js +32 -10
- package/session/facets/ArtifactsTab.js.map +1 -1
- package/session/index.d.ts +2 -1
- package/session/index.d.ts.map +1 -1
- package/session/index.js +4 -1
- package/session/index.js.map +1 -1
- package/session/plan-document.d.ts +14 -2
- package/session/plan-document.d.ts.map +1 -1
- package/session/plan-document.js +14 -3
- package/session/plan-document.js.map +1 -1
- package/session/usePlanDraft.d.ts +1 -1
- package/session/useSessionArtifacts.d.ts +17 -0
- package/session/useSessionArtifacts.d.ts.map +1 -1
- package/session/useSessionArtifacts.js +16 -5
- package/session/useSessionArtifacts.js.map +1 -1
- package/session/useSessionConversation.d.ts +26 -3
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +51 -6
- package/session/useSessionConversation.js.map +1 -1
- package/session/useSessionPageFlow.d.ts.map +1 -1
- package/session/useSessionPageFlow.js +1 -0
- package/session/useSessionPageFlow.js.map +1 -1
- package/session/useSessionPanel.d.ts +20 -0
- package/session/useSessionPanel.d.ts.map +1 -1
- package/session/useSessionPanel.js +13 -0
- package/session/useSessionPanel.js.map +1 -1
- package/session/useSessionRailViews.d.ts +13 -1
- package/session/useSessionRailViews.d.ts.map +1 -1
- package/session/useSessionRailViews.js +5 -3
- package/session/useSessionRailViews.js.map +1 -1
- package/src/composer/SessionComposer.tsx +76 -7
- package/src/composer/__tests__/SessionComposer-editing.test.tsx +128 -0
- package/src/execution/ArtifactApplyButton.tsx +79 -0
- package/src/execution/ArtifactContentBody.tsx +255 -0
- package/src/execution/ArtifactDocument.tsx +344 -0
- package/src/execution/ArtifactPreviewModal.tsx +49 -415
- package/src/execution/ArtifactRow.tsx +165 -0
- package/src/execution/ArtifactsWidget.tsx +24 -33
- package/src/execution/FileChangeProgressBar.tsx +121 -0
- package/src/execution/FileChangesView.tsx +18 -4
- package/src/execution/FileReviewAtoms.tsx +89 -0
- package/src/execution/FileReviewCard.tsx +57 -90
- package/src/execution/MessageEntry.tsx +0 -62
- package/src/execution/MessageThread.tsx +29 -15
- package/src/execution/PlanArtifactCard.tsx +130 -52
- package/src/execution/PlanCompletionCard.tsx +5 -5
- package/src/execution/PlanStreamingCard.tsx +15 -11
- package/src/execution/__tests__/ArtifactContentBody.test.tsx +131 -0
- package/src/execution/__tests__/ArtifactDocument.test.tsx +100 -0
- package/src/execution/__tests__/ArtifactPreviewModal.test.tsx +6 -6
- package/src/execution/__tests__/ArtifactRow.test.tsx +139 -0
- package/src/execution/__tests__/ArtifactsWidget.test.tsx +78 -0
- package/src/execution/__tests__/FileChangeProgressBar.test.tsx +125 -0
- package/src/execution/__tests__/FileReviewCard.test.tsx +66 -7
- package/src/execution/__tests__/MessageThread.test.tsx +1 -1
- package/src/execution/__tests__/PlanArtifactCard.test.tsx +52 -18
- package/src/execution/__tests__/PlanCompletionCard.test.tsx +2 -2
- package/src/execution/__tests__/PlanStreamingCard.test.tsx +11 -4
- package/src/execution/__tests__/buildThreadItems-plan-completion.test.ts +104 -8
- package/src/execution/__tests__/message-entry.test.tsx +56 -39
- package/src/execution/__tests__/useArtifactCopy.test.tsx +90 -0
- package/src/execution/__tests__/useArtifactDownload.test.tsx +2 -0
- package/src/execution/__tests__/useArtifactInspection.test.tsx +151 -0
- package/src/execution/__tests__/useCreateAgentExecution.test.tsx +35 -0
- package/src/execution/index.ts +20 -2
- package/src/execution/use-build-from-plan-hotkey.ts +1 -1
- package/src/execution/useArtifactContent.ts +7 -0
- package/src/execution/useArtifactCopy.ts +109 -0
- package/src/execution/useArtifactDownload.ts +17 -5
- package/src/execution/useArtifactInspection.ts +252 -0
- package/src/execution/useCreateAgentExecution.ts +17 -3
- package/src/index.ts +17 -2
- package/src/internal/MermaidDiagram.tsx +164 -0
- package/src/internal/__tests__/a11y/mermaid-theme.a11y.test.tsx +122 -0
- package/src/internal/__tests__/markdown-components.test.tsx +93 -1
- package/src/internal/__tests__/mermaid-diagram.test.tsx +282 -0
- package/src/internal/file-tree/FileTreeNode.tsx +13 -2
- package/src/internal/markdown-components.tsx +36 -1
- package/src/internal/mermaid-loader.ts +44 -0
- package/src/internal/mermaid-theme-css.ts +178 -0
- package/src/internal/slug.ts +10 -6
- package/src/library/__tests__/detect-plan-artifact.test.ts +21 -0
- package/src/library/detect-plan-artifact.ts +32 -10
- package/src/library/index.ts +2 -0
- package/src/session/PlanEditor.tsx +1 -1
- package/src/session/SessionViewer.tsx +227 -51
- package/src/session/__tests__/PlanEditor.test.tsx +4 -4
- package/src/session/__tests__/PlanStreamingDocument.test.tsx +1 -1
- package/src/session/__tests__/SessionViewer-artifactTabs.test.tsx +210 -0
- package/src/session/__tests__/SessionViewer-editResubmit.test.tsx +233 -0
- package/src/session/__tests__/SessionViewer-fileReviewDock.test.tsx +57 -0
- package/src/session/__tests__/useSessionArtifacts.test.ts +57 -0
- package/src/session/__tests__/useSessionConversation.test.tsx +216 -1
- package/src/session/__tests__/useSessionPanel.test.ts +70 -1
- package/src/session/artifact-document.ts +23 -0
- package/src/session/facets/ArtifactsTab.tsx +66 -18
- package/src/session/facets/__tests__/ArtifactsTab.test.tsx +111 -47
- package/src/session/index.ts +5 -1
- package/src/session/plan-document.ts +14 -3
- package/src/session/usePlanDraft.ts +1 -1
- package/src/session/useSessionArtifacts.ts +16 -5
- package/src/session/useSessionConversation.ts +80 -10
- package/src/session/useSessionPageFlow.ts +1 -0
- package/src/session/useSessionPanel.ts +41 -0
- package/src/session/useSessionRailViews.tsx +21 -1
- package/src/workspace/EditorTabs.tsx +38 -9
- package/src/workspace/ExplorerTree.tsx +10 -4
- package/src/workspace/FileViewer.tsx +27 -6
- package/src/workspace/WorkspaceContentSearch.tsx +44 -20
- package/src/workspace/WorkspaceEditor.tsx +19 -16
- package/src/workspace/WorkspaceFileSearch.tsx +51 -28
- package/src/workspace/WorkspaceSurface.tsx +106 -48
- package/src/workspace/__tests__/EditorTabs.test.tsx +4 -2
- package/src/workspace/__tests__/ExplorerTree.test.tsx +52 -0
- package/src/workspace/__tests__/FileViewer.test.tsx +18 -0
- package/src/workspace/__tests__/WorkspaceContentSearch.test.tsx +38 -5
- package/src/workspace/__tests__/WorkspaceEditor-initialPanel.test.tsx +19 -0
- package/src/workspace/__tests__/WorkspaceFileSearch.test.tsx +27 -6
- package/src/workspace/__tests__/WorkspaceSurface.test.tsx +70 -0
- package/src/workspace/__tests__/a11y/harness.tsx +140 -0
- package/src/workspace/__tests__/a11y/mock-capabilities.ts +138 -0
- package/src/workspace/__tests__/a11y/workspace-editor.a11y.test.tsx +65 -0
- package/src/workspace/__tests__/a11y/workspace-explorer-search.a11y.test.tsx +112 -0
- package/src/workspace/__tests__/a11y/workspace-viewer-tabs.a11y.test.tsx +69 -0
- package/styles.css +1 -1
- package/workspace/EditorTabs.d.ts +21 -3
- package/workspace/EditorTabs.d.ts.map +1 -1
- package/workspace/EditorTabs.js +18 -6
- package/workspace/EditorTabs.js.map +1 -1
- package/workspace/ExplorerTree.js +8 -4
- package/workspace/ExplorerTree.js.map +1 -1
- package/workspace/FileViewer.d.ts.map +1 -1
- package/workspace/FileViewer.js +19 -8
- package/workspace/FileViewer.js.map +1 -1
- package/workspace/WorkspaceContentSearch.d.ts.map +1 -1
- package/workspace/WorkspaceContentSearch.js +23 -2
- package/workspace/WorkspaceContentSearch.js.map +1 -1
- package/workspace/WorkspaceEditor.d.ts.map +1 -1
- package/workspace/WorkspaceEditor.js +8 -10
- package/workspace/WorkspaceEditor.js.map +1 -1
- package/workspace/WorkspaceFileSearch.d.ts.map +1 -1
- package/workspace/WorkspaceFileSearch.js +23 -3
- package/workspace/WorkspaceFileSearch.js.map +1 -1
- package/workspace/WorkspaceSurface.d.ts.map +1 -1
- package/workspace/WorkspaceSurface.js +54 -18
- package/workspace/WorkspaceSurface.js.map +1 -1
- package/execution/ArtifactCard.d.ts +0 -81
- package/execution/ArtifactCard.d.ts.map +0 -1
- package/execution/ArtifactCard.js +0 -130
- package/execution/ArtifactCard.js.map +0 -1
- package/src/execution/ArtifactCard.tsx +0 -320
|
@@ -16,10 +16,11 @@ import {
|
|
|
16
16
|
import { toDisplayFileChange } from "@stigmer/sdk";
|
|
17
17
|
import { cn } from "@stigmer/theme";
|
|
18
18
|
import { FileChangeDiff } from "./FileChangesView.js";
|
|
19
|
+
import { FileKindBadge, FileLineStats } from "./FileReviewAtoms.js";
|
|
20
|
+
import { FilePathLink } from "./FilePathLink.js";
|
|
19
21
|
import { DiffSummary } from "../version-history/DiffSummary.js";
|
|
20
22
|
import { DecisionButton } from "../internal/DecisionButton.js";
|
|
21
23
|
import { InCardDecisionError } from "../internal/InCardDecisionError.js";
|
|
22
|
-
import { splitDisplayPath } from "./file-path-resolver.js";
|
|
23
24
|
import { fileDecisionKey, type FileDecisionOptions } from "./useFileReview.js";
|
|
24
25
|
import {
|
|
25
26
|
changeSetReviewability,
|
|
@@ -62,8 +63,8 @@ export interface FileReviewCardProps {
|
|
|
62
63
|
*
|
|
63
64
|
* `MessageThread` passes `false`: the transcript's stamped edit rows already
|
|
64
65
|
* show every diff in place, so the expanded body collapses to a compact file
|
|
65
|
-
* list (kind letter + path + per-file controls) and the card
|
|
66
|
-
* the decision surface. Decision semantics — digests, scopes,
|
|
66
|
+
* list (kind letter + linked path + `+N −M` + per-file controls) and the card
|
|
67
|
+
* stays purely the decision surface. Decision semantics — digests, scopes,
|
|
67
68
|
* acknowledgments — are identical in both modes; this is presentation only
|
|
68
69
|
* (opt-in with a backward-compatible default, DD-011).
|
|
69
70
|
*/
|
|
@@ -194,11 +195,18 @@ export const FileReviewCard = memo(function FileReviewCard({
|
|
|
194
195
|
|
|
195
196
|
// The bar starts expanded exactly when the collapsed bar cannot honestly carry
|
|
196
197
|
// the decision: an incomplete set (binary acknowledgment / blocked files need
|
|
197
|
-
// their per-file context)
|
|
198
|
-
//
|
|
199
|
-
//
|
|
198
|
+
// their per-file context), a review already in progress, or a multi-file set
|
|
199
|
+
// in compact-list mode — its honest decision surface includes the per-file
|
|
200
|
+
// Keep/Discard controls, which must be visible without hunting behind the
|
|
201
|
+
// expander (the docked strip bounds the height, so expanding is cheap there).
|
|
202
|
+
// Diff-rich mode (`showDiffs`) deliberately stays collapsed for a complete
|
|
203
|
+
// multi-file set: its expanded body is every full diff with no height cap
|
|
204
|
+
// (the workflow file-review list), where "Review" is the act of opening it.
|
|
205
|
+
// A settled record always starts collapsed — the transcript's stamped rows
|
|
206
|
+
// already show what changed, and the expander reveals detail on demand.
|
|
200
207
|
const [expanded, setExpanded] = useState(
|
|
201
|
-
() =>
|
|
208
|
+
() =>
|
|
209
|
+
interactive && (incomplete || decidedCount > 0 || (total > 1 && !showDiffs)),
|
|
202
210
|
);
|
|
203
211
|
const bodyId = useId();
|
|
204
212
|
|
|
@@ -251,12 +259,18 @@ export const FileReviewCard = memo(function FileReviewCard({
|
|
|
251
259
|
[onSubmit],
|
|
252
260
|
);
|
|
253
261
|
|
|
262
|
+
// The set's aggregate `+N −M`, summed from the capture-time per-file counts
|
|
263
|
+
// (linesAdded/linesRemoved are stamped by the runner with the same diff
|
|
264
|
+
// algorithm the renderers use — see CapturedFileChange). A file with no
|
|
265
|
+
// counts (binary, secret-withheld, or a record predating the fields)
|
|
266
|
+
// contributes zero; when NO file has counts the aggregate is hidden rather
|
|
267
|
+
// than shown as a dishonest "+0 −0".
|
|
254
268
|
const totals = useMemo(() => {
|
|
255
269
|
let additions = 0;
|
|
256
270
|
let deletions = 0;
|
|
257
271
|
for (const c of changes) {
|
|
258
|
-
|
|
259
|
-
|
|
272
|
+
additions += c.linesAdded;
|
|
273
|
+
deletions += c.linesRemoved;
|
|
260
274
|
}
|
|
261
275
|
return { additions, deletions };
|
|
262
276
|
}, [changes]);
|
|
@@ -311,8 +325,16 @@ export const FileReviewCard = memo(function FileReviewCard({
|
|
|
311
325
|
<span className="shrink-0 font-medium text-foreground">
|
|
312
326
|
{interactive ? "Review file changes" : "File changes"}
|
|
313
327
|
</span>
|
|
314
|
-
|
|
315
|
-
|
|
328
|
+
{/* The flexible middle: the summary (truncating) plus the set's
|
|
329
|
+
aggregate +N −M, visible even while collapsed so the bar carries
|
|
330
|
+
the magnitude of what is being decided, not just the file count.
|
|
331
|
+
Hidden when no file has counts (FileLineStats). */}
|
|
332
|
+
<span className="flex min-w-0 flex-1 items-center gap-2">
|
|
333
|
+
<span className="min-w-0 truncate text-muted-foreground">{summary}</span>
|
|
334
|
+
<FileLineStats
|
|
335
|
+
linesAdded={totals.additions}
|
|
336
|
+
linesRemoved={totals.deletions}
|
|
337
|
+
/>
|
|
316
338
|
</span>
|
|
317
339
|
|
|
318
340
|
{interactive && showBulkControls && (
|
|
@@ -390,9 +412,11 @@ export const FileReviewCard = memo(function FileReviewCard({
|
|
|
390
412
|
id={bodyId}
|
|
391
413
|
className="space-y-2 border-t border-border-muted px-3 py-2.5"
|
|
392
414
|
>
|
|
393
|
-
{/* The list body carries no summary header: the
|
|
394
|
-
file count
|
|
395
|
-
|
|
415
|
+
{/* The list body carries no summary header: the bar already shows
|
|
416
|
+
the file count and the aggregate +N −M (per-file counts are
|
|
417
|
+
capture-time facts on the wire — linesAdded/linesRemoved — so no
|
|
418
|
+
body fetch is ever needed to decorate the list). Diff mode keeps
|
|
419
|
+
its fuller DiffSummary line above the rendered diffs. */}
|
|
396
420
|
{showDiffs && (
|
|
397
421
|
<DiffSummary
|
|
398
422
|
fileCount={total}
|
|
@@ -640,15 +664,17 @@ function CapturedChangeDiff({ change }: { change: CapturedFileChange }) {
|
|
|
640
664
|
// ---------------------------------------------------------------------------
|
|
641
665
|
|
|
642
666
|
/**
|
|
643
|
-
* One file of the change set as a compact
|
|
644
|
-
*
|
|
667
|
+
* One file of the change set as a compact list line: kind letter, linked path,
|
|
668
|
+
* `+N −M` stats, provenance badge, and an optional trailing slot (the verdict
|
|
645
669
|
* control while reviewing; the committed verdict once settled).
|
|
646
670
|
*
|
|
647
|
-
* The path
|
|
648
|
-
*
|
|
649
|
-
*
|
|
650
|
-
*
|
|
651
|
-
*
|
|
671
|
+
* The path renders through `FilePathLink` — the platform-wide path idiom — so
|
|
672
|
+
* a reviewer can jump from the list to the file itself (the session viewer
|
|
673
|
+
* routes the click into the panel's diff-first viewer; GitHub/copy is the
|
|
674
|
+
* fallback) without leaving the decision surface. Deciding and navigating
|
|
675
|
+
* coexist: the link stops click propagation, so opening a file never fights
|
|
676
|
+
* the row's Keep/Discard controls. A rename shows its source path so the move
|
|
677
|
+
* reads in one line.
|
|
652
678
|
*/
|
|
653
679
|
function FileListRow({
|
|
654
680
|
change,
|
|
@@ -658,7 +684,6 @@ function FileListRow({
|
|
|
658
684
|
readonly trailing?: React.ReactNode;
|
|
659
685
|
}) {
|
|
660
686
|
const path = change.pathAfter || change.pathBefore;
|
|
661
|
-
const { dir, base } = splitDisplayPath(path);
|
|
662
687
|
const renamedFrom =
|
|
663
688
|
change.kind === FileChangeKind.RENAME && change.pathBefore !== change.pathAfter
|
|
664
689
|
? change.pathBefore
|
|
@@ -669,80 +694,22 @@ function FileListRow({
|
|
|
669
694
|
data-cursor-target="file-review-list-row"
|
|
670
695
|
>
|
|
671
696
|
<FileKindBadge kind={change.kind} />
|
|
672
|
-
{
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
)}
|
|
683
|
-
{dir && (
|
|
684
|
-
<span className="min-w-0 truncate text-muted-foreground-faint">{dir}</span>
|
|
685
|
-
)}
|
|
686
|
-
<span className="shrink-0">{base}</span>
|
|
687
|
-
</span>
|
|
697
|
+
{renamedFrom && (
|
|
698
|
+
<span className="min-w-0 truncate font-mono text-xs text-muted-foreground-faint">
|
|
699
|
+
{renamedFrom} →
|
|
700
|
+
</span>
|
|
701
|
+
)}
|
|
702
|
+
<FilePathLink path={path} dirDisplay="dim" className="min-w-0 flex-1 text-xs" />
|
|
703
|
+
<FileLineStats
|
|
704
|
+
linesAdded={change.linesAdded}
|
|
705
|
+
linesRemoved={change.linesRemoved}
|
|
706
|
+
/>
|
|
688
707
|
<CaptureBadge change={change} />
|
|
689
708
|
{trailing}
|
|
690
709
|
</div>
|
|
691
710
|
);
|
|
692
711
|
}
|
|
693
712
|
|
|
694
|
-
/** The kind letter, tone, and a11y name for each wire {@link FileChangeKind}. */
|
|
695
|
-
const KIND_BADGE: Partial<
|
|
696
|
-
Record<FileChangeKind, { letter: string; colorClass: string; label: string }>
|
|
697
|
-
> = {
|
|
698
|
-
[FileChangeKind.ADD]: {
|
|
699
|
-
letter: "A",
|
|
700
|
-
colorClass: "text-diff-added-fg",
|
|
701
|
-
label: "added",
|
|
702
|
-
},
|
|
703
|
-
[FileChangeKind.MODIFY]: {
|
|
704
|
-
letter: "M",
|
|
705
|
-
colorClass: "text-diff-hunk-header-fg",
|
|
706
|
-
label: "modified",
|
|
707
|
-
},
|
|
708
|
-
[FileChangeKind.DELETE]: {
|
|
709
|
-
letter: "D",
|
|
710
|
-
colorClass: "text-diff-removed-fg",
|
|
711
|
-
label: "deleted",
|
|
712
|
-
},
|
|
713
|
-
[FileChangeKind.RENAME]: {
|
|
714
|
-
letter: "R",
|
|
715
|
-
colorClass: "text-diff-hunk-header-fg",
|
|
716
|
-
label: "renamed",
|
|
717
|
-
},
|
|
718
|
-
// A binary change is a modification whose diff is not text; the letter stays
|
|
719
|
-
// M and the binary-ness is told by the row's "Keep anyway" + reason note.
|
|
720
|
-
[FileChangeKind.BINARY_CHANGE]: {
|
|
721
|
-
letter: "M",
|
|
722
|
-
colorClass: "text-diff-hunk-header-fg",
|
|
723
|
-
label: "modified (binary)",
|
|
724
|
-
},
|
|
725
|
-
};
|
|
726
|
-
|
|
727
|
-
/**
|
|
728
|
-
* The single-letter change-kind marker — the same M/A/D visual vocabulary as
|
|
729
|
-
* the version-history `DiffFileList`, extended with R for captured renames.
|
|
730
|
-
* The letter itself is the non-color channel; the full word is exposed to
|
|
731
|
-
* assistive tech. An UNSPECIFIED kind renders nothing rather than guessing.
|
|
732
|
-
*/
|
|
733
|
-
function FileKindBadge({ kind }: { kind: FileChangeKind }) {
|
|
734
|
-
const badge = KIND_BADGE[kind];
|
|
735
|
-
if (!badge) return null;
|
|
736
|
-
return (
|
|
737
|
-
<span
|
|
738
|
-
className={cn("shrink-0 font-mono text-[10px] font-bold", badge.colorClass)}
|
|
739
|
-
aria-label={badge.label}
|
|
740
|
-
>
|
|
741
|
-
{badge.letter}
|
|
742
|
-
</span>
|
|
743
|
-
);
|
|
744
|
-
}
|
|
745
|
-
|
|
746
713
|
/**
|
|
747
714
|
* A change's committed verdict as a static badge: APPROVE reads "Kept" (the
|
|
748
715
|
* bytes were reconciled into the workspace), REJECT reads "Discarded" (reverted
|
|
@@ -48,15 +48,6 @@ export interface MessageEntryProps {
|
|
|
48
48
|
* non-human messages.
|
|
49
49
|
*/
|
|
50
50
|
readonly interactionMode?: InteractionMode;
|
|
51
|
-
/**
|
|
52
|
-
* Renders a `MESSAGE_HUMAN` entry as a compact "Build from plan" chip
|
|
53
|
-
* instead of a prompt bubble. Stamped by the thread builder from
|
|
54
|
-
* `execution_config.build_from_plan`: the turn's message is a short label
|
|
55
|
-
* (the implement instruction is runner-injected), so a full-width bubble
|
|
56
|
-
* would dignify machine plumbing as user prose. Ignored for non-human
|
|
57
|
-
* messages.
|
|
58
|
-
*/
|
|
59
|
-
readonly isBuildFromPlan?: boolean;
|
|
60
51
|
}
|
|
61
52
|
|
|
62
53
|
/**
|
|
@@ -89,7 +80,6 @@ export const MessageEntry = memo(function MessageEntry({
|
|
|
89
80
|
onEdit,
|
|
90
81
|
isPlanDocument,
|
|
91
82
|
interactionMode,
|
|
92
|
-
isBuildFromPlan,
|
|
93
83
|
}: MessageEntryProps) {
|
|
94
84
|
useRenderTracer("MessageEntry", {
|
|
95
85
|
messageType: message.type,
|
|
@@ -99,11 +89,6 @@ export const MessageEntry = memo(function MessageEntry({
|
|
|
99
89
|
|
|
100
90
|
switch (message.type) {
|
|
101
91
|
case MessageType.MESSAGE_HUMAN:
|
|
102
|
-
if (isBuildFromPlan) {
|
|
103
|
-
return (
|
|
104
|
-
<BuildFromPlanChip content={message.content} className={className} />
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
92
|
return (
|
|
108
93
|
<HumanMessage
|
|
109
94
|
content={message.content}
|
|
@@ -196,53 +181,6 @@ function HumanMessage({
|
|
|
196
181
|
* label is not user prose to rephrase; refining the plan happens in the plan
|
|
197
182
|
* document, and the build is re-triggered from its card.
|
|
198
183
|
*/
|
|
199
|
-
function BuildFromPlanChip({
|
|
200
|
-
content,
|
|
201
|
-
className,
|
|
202
|
-
}: {
|
|
203
|
-
content: string;
|
|
204
|
-
className?: string;
|
|
205
|
-
}) {
|
|
206
|
-
return (
|
|
207
|
-
<div
|
|
208
|
-
role="article"
|
|
209
|
-
aria-label="Build from plan"
|
|
210
|
-
className={cn("flex justify-end", className)}
|
|
211
|
-
>
|
|
212
|
-
<span
|
|
213
|
-
className={cn(
|
|
214
|
-
"inline-flex items-center gap-1.5 rounded-full border border-border",
|
|
215
|
-
"bg-muted-subtle px-3 py-1.5 text-xs font-medium text-foreground",
|
|
216
|
-
)}
|
|
217
|
-
>
|
|
218
|
-
<BuildFromPlanIcon />
|
|
219
|
-
{content}
|
|
220
|
-
</span>
|
|
221
|
-
</div>
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/** The plan-card glyph (document lines + implement arrow) at chip scale. */
|
|
226
|
-
function BuildFromPlanIcon() {
|
|
227
|
-
return (
|
|
228
|
-
<svg
|
|
229
|
-
width="13"
|
|
230
|
-
height="13"
|
|
231
|
-
viewBox="0 0 16 16"
|
|
232
|
-
fill="none"
|
|
233
|
-
stroke="currentColor"
|
|
234
|
-
strokeWidth="1.5"
|
|
235
|
-
strokeLinecap="round"
|
|
236
|
-
strokeLinejoin="round"
|
|
237
|
-
className="shrink-0 text-muted-foreground"
|
|
238
|
-
aria-hidden="true"
|
|
239
|
-
>
|
|
240
|
-
<path d="M3 4h10M3 8h7M3 12h8" />
|
|
241
|
-
<path d="M12.5 10.5l1.5 1.5-1.5 1.5" />
|
|
242
|
-
</svg>
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
184
|
function EditIcon() {
|
|
247
185
|
return (
|
|
248
186
|
<svg
|
|
@@ -289,7 +289,7 @@ export function threadContentColumnClass(
|
|
|
289
289
|
* part of the public API.
|
|
290
290
|
*/
|
|
291
291
|
export type ThreadItem =
|
|
292
|
-
| { readonly kind: "message"; readonly message: AgentMessage; readonly key: string; readonly isPending?: boolean; readonly isFailed?: boolean; readonly isEditable?: boolean; readonly isPlanDocument?: boolean; readonly interactionMode?: InteractionMode
|
|
292
|
+
| { readonly kind: "message"; readonly message: AgentMessage; readonly key: string; readonly isPending?: boolean; readonly isFailed?: boolean; readonly isEditable?: boolean; readonly isPlanDocument?: boolean; readonly interactionMode?: InteractionMode }
|
|
293
293
|
| { readonly kind: "tool-group"; readonly toolCalls: readonly ToolCall[]; readonly subAgentExecutions: readonly SubAgentExecution[]; readonly key: string }
|
|
294
294
|
| { readonly kind: "sub-agent"; readonly subAgentExecution: SubAgentExecution; readonly key: string }
|
|
295
295
|
| { readonly kind: "phase-badge"; readonly phase: ExecutionPhase; readonly key: string }
|
|
@@ -317,9 +317,9 @@ export type ThreadItem =
|
|
|
317
317
|
readonly planTitle?: string;
|
|
318
318
|
/**
|
|
319
319
|
* True for the most recent completed Plan execution in the thread — the
|
|
320
|
-
* only plan whose card carries the primary "Build
|
|
321
|
-
*
|
|
322
|
-
*
|
|
320
|
+
* only plan whose card carries the primary "Build" action. Superseded
|
|
321
|
+
* plans keep their review actions (open/download) but never a build
|
|
322
|
+
* CTA, so a stale plan can't be implemented by accident.
|
|
323
323
|
*/
|
|
324
324
|
readonly isLatestPlan: boolean;
|
|
325
325
|
}
|
|
@@ -537,9 +537,9 @@ export function buildThreadItems(
|
|
|
537
537
|
? allExecutions.length - 1
|
|
538
538
|
: -1;
|
|
539
539
|
|
|
540
|
-
// The most recent completed Plan execution owns the primary "Build
|
|
541
|
-
//
|
|
542
|
-
//
|
|
540
|
+
// The most recent completed Plan execution owns the primary "Build" action;
|
|
541
|
+
// every earlier plan renders as a review-only record. Resolved up front so
|
|
542
|
+
// each segment can stamp its own card with the right authority.
|
|
543
543
|
let latestPlanExecutionId: string | null = null;
|
|
544
544
|
for (let i = allExecutions.length - 1; i >= 0; i--) {
|
|
545
545
|
if (isCompletedPlanExecution(allExecutions[i])) {
|
|
@@ -648,7 +648,13 @@ export function buildThreadItems(
|
|
|
648
648
|
}
|
|
649
649
|
|
|
650
650
|
const specMessage = exec.spec?.message;
|
|
651
|
-
|
|
651
|
+
// A Build-from-plan turn synthesizes NO prompt item: its message is a
|
|
652
|
+
// machine-written label ("Build from plan"), not user prose — the real
|
|
653
|
+
// instruction is runner-injected from the same flag. The plan card
|
|
654
|
+
// directly above the turn is the visible cause; rendering the label as a
|
|
655
|
+
// user bubble would attribute words to the user they never typed.
|
|
656
|
+
const isBuildTurn = exec.spec?.executionConfig?.buildFromPlan === true;
|
|
657
|
+
if (specMessage && specMessage !== "execute" && !isBuildTurn) {
|
|
652
658
|
const syntheticHumanMsg = create(AgentMessageSchema);
|
|
653
659
|
syntheticHumanMsg.type = MessageType.MESSAGE_HUMAN;
|
|
654
660
|
syntheticHumanMsg.content = specMessage;
|
|
@@ -671,10 +677,6 @@ export function buildThreadItems(
|
|
|
671
677
|
// The turn's mode marks the prompt bubble (a "Plan" pill on Plan
|
|
672
678
|
// turns) so the transcript reads unambiguously after mode switches.
|
|
673
679
|
interactionMode: exec.spec?.executionConfig?.interactionMode,
|
|
674
|
-
// A Build-from-plan turn's prompt renders as a compact chip, not the
|
|
675
|
-
// message text — the text is a short label; the real instruction is
|
|
676
|
-
// runner-injected from the same flag.
|
|
677
|
-
isBuildFromPlan: exec.spec?.executionConfig?.buildFromPlan || undefined,
|
|
678
680
|
});
|
|
679
681
|
}
|
|
680
682
|
|
|
@@ -896,11 +898,19 @@ export function buildThreadItems(
|
|
|
896
898
|
const reason = lastExec?.status?.error;
|
|
897
899
|
if (reason) {
|
|
898
900
|
const specMessage = lastExec?.spec?.message;
|
|
901
|
+
// A failed build turn offers no inline Retry: resending its label as an
|
|
902
|
+
// ordinary message would drop the buildFromPlan flag (no runner
|
|
903
|
+
// directive, no plan attachment). The plan card above the error is the
|
|
904
|
+
// fully-wired retry — its Build button re-runs the whole pipeline.
|
|
905
|
+
const isBuildTurn =
|
|
906
|
+
lastExec?.spec?.executionConfig?.buildFromPlan === true;
|
|
899
907
|
items.push({
|
|
900
908
|
kind: "execution-error",
|
|
901
909
|
error: reason,
|
|
902
910
|
retryMessage:
|
|
903
|
-
specMessage && specMessage !== "execute"
|
|
911
|
+
specMessage && specMessage !== "execute" && !isBuildTurn
|
|
912
|
+
? specMessage
|
|
913
|
+
: undefined,
|
|
904
914
|
key: `execution-error-${lastExec?.metadata?.id ?? lastPhase}`,
|
|
905
915
|
});
|
|
906
916
|
}
|
|
@@ -936,8 +946,13 @@ export function buildThreadItems(
|
|
|
936
946
|
// not thread items at all: they live in the composer-docked FileReviewDock.
|
|
937
947
|
|
|
938
948
|
if (pendingUserMessage) {
|
|
949
|
+
// A FAILED send never created an execution, so an existing execution with
|
|
950
|
+
// identical text (e.g. two build attempts of the same plan — both
|
|
951
|
+
// "Build from plan") must never swallow the failed bubble. Suppression
|
|
952
|
+
// only applies to the succeeded-send window where the stream has
|
|
953
|
+
// delivered the same turn but the pending state hasn't cleared yet.
|
|
939
954
|
const alreadySynthesized =
|
|
940
|
-
lastExec?.spec?.message === pendingUserMessage;
|
|
955
|
+
!pendingMessageFailed && lastExec?.spec?.message === pendingUserMessage;
|
|
941
956
|
if (!alreadySynthesized) {
|
|
942
957
|
const syntheticPending = create(AgentMessageSchema);
|
|
943
958
|
syntheticPending.type = MessageType.MESSAGE_HUMAN;
|
|
@@ -1333,7 +1348,6 @@ export function ThreadItemRenderer({
|
|
|
1333
1348
|
className={item.isPending ? "opacity-70" : undefined}
|
|
1334
1349
|
isPlanDocument={item.isPlanDocument}
|
|
1335
1350
|
interactionMode={item.interactionMode}
|
|
1336
|
-
isBuildFromPlan={item.isBuildFromPlan}
|
|
1337
1351
|
onEdit={
|
|
1338
1352
|
item.isEditable && onEditMessage
|
|
1339
1353
|
? () => onEditMessage(item.message.content)
|