@nkzw/codiff-core 1.8.1 → 1.9.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/react.mjs CHANGED
@@ -7,7 +7,7 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
7
  import { MarkdownEditor } from "@nkzw/mdx-editor";
8
8
  import { frontmatterPlugin, imagePlugin } from "@nkzw/mdx-editor/core";
9
9
  import { DownloadSimpleIcon } from "@phosphor-icons/react/DownloadSimple";
10
- import { Copy, ExternalLink, X } from "lucide-react";
10
+ import { Copy, ExternalLink, Trash2, X } from "lucide-react";
11
11
  import { FileTree, useFileTree } from "@pierre/trees/react";
12
12
  import { hydratePartialDiff, parseDiffFromFile, parsePatchFiles, registerCustomTheme, setCustomExtension } from "@pierre/diffs";
13
13
  import useRelativeTime from "@nkzw/use-relative-time";
@@ -25,7 +25,6 @@ import { ImageBrokenIcon } from "@phosphor-icons/react/ImageBroken";
25
25
  import { SquareSplitVerticalIcon } from "@phosphor-icons/react/SquareSplitVertical";
26
26
  import { CodeView, WorkerPoolContextProvider } from "@pierre/diffs/react";
27
27
  import { PersistentMarkdownEditor } from "@nkzw/mdx-editor/persistence";
28
- import { ArrowsClockwiseIcon } from "@phosphor-icons/react/ArrowsClockwise";
29
28
  import { GitBranchIcon } from "@phosphor-icons/react/GitBranch";
30
29
  import { PathIcon } from "@phosphor-icons/react/Path";
31
30
  import { ShareNetworkIcon } from "@phosphor-icons/react/ShareNetwork";
@@ -38,6 +37,7 @@ import { ArrowLeftIcon } from "@phosphor-icons/react/ArrowLeft";
38
37
  import { ArrowRightIcon } from "@phosphor-icons/react/ArrowRight";
39
38
  import { CaretLeftIcon } from "@phosphor-icons/react/CaretLeft";
40
39
  import { CaretRightIcon } from "@phosphor-icons/react/CaretRight";
40
+ import { ArrowsClockwiseIcon } from "@phosphor-icons/react/ArrowsClockwise";
41
41
  import { Terminal, init } from "ghostty-web";
42
42
  //#region app/components/Button.tsx
43
43
  const buttonVariants = cva("codiff-button", {
@@ -92,6 +92,15 @@ function Button({ action, asChild = false, children, className, disabled, onClic
92
92
  });
93
93
  }
94
94
  //#endregion
95
+ //#region app/components/Thinking.tsx
96
+ function Thinking() {
97
+ return /* @__PURE__ */ jsx("div", {
98
+ className: "review-source-loading loading pulse italic",
99
+ role: "status",
100
+ children: "Thinking…"
101
+ });
102
+ }
103
+ //#endregion
95
104
  //#region app/components/Avatar.tsx
96
105
  const graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
97
106
  const getNameParts = (name) => {
@@ -951,7 +960,7 @@ function SharedPlanCommentRail({ activeThreadId, commenting, layoutPass, layouts
951
960
  })
952
961
  });
953
962
  }
954
- function PlanReviewSurface({ commenting, signInLabel = "Sign in to comment", snapshot }) {
963
+ function PlanReviewSurface({ commenting, onDeleteShare, signInLabel = "Sign in to comment", snapshot }) {
955
964
  const editorRef = useRef(null);
956
965
  const [activeThreadId, setActiveThreadId] = useState(null);
957
966
  const [commentTarget, setCommentTarget] = useState(null);
@@ -1046,6 +1055,14 @@ function PlanReviewSurface({ commenting, signInLabel = "Sign in to comment", sna
1046
1055
  link.remove();
1047
1056
  URL.revokeObjectURL(url);
1048
1057
  }, [snapshot]);
1058
+ const deleteShare = useCallback(async () => {
1059
+ if (!onDeleteShare || !window.confirm("Delete this shared plan? This cannot be undone.")) return;
1060
+ try {
1061
+ await onDeleteShare();
1062
+ } catch (error) {
1063
+ window.alert(error instanceof Error ? error.message : String(error));
1064
+ }
1065
+ }, [onDeleteShare]);
1049
1066
  return /* @__PURE__ */ jsxs("main", {
1050
1067
  className: "plan-shell shared-plan-shell",
1051
1068
  "data-theme": snapshot.preferences.theme === "system" ? void 0 : snapshot.preferences.theme,
@@ -1085,18 +1102,33 @@ function PlanReviewSurface({ commenting, signInLabel = "Sign in to comment", sna
1085
1102
  })
1086
1103
  })
1087
1104
  })
1088
- }), /* @__PURE__ */ jsx(Button, {
1089
- "aria-label": "Download plan",
1090
- className: "plan-download-button",
1091
- onClick: downloadPlan,
1092
- size: "icon",
1093
- title: "Download plan",
1094
- type: "button",
1095
- children: /* @__PURE__ */ jsx(DownloadSimpleIcon, {
1096
- "aria-hidden": true,
1097
- size: 16,
1098
- weight: "bold"
1099
- })
1105
+ }), /* @__PURE__ */ jsxs("div", {
1106
+ className: "plan-file-actions",
1107
+ children: [onDeleteShare ? /* @__PURE__ */ jsx(Button, {
1108
+ action: deleteShare,
1109
+ "aria-label": "Delete shared plan",
1110
+ pendingPlaceholder: "…",
1111
+ size: "icon",
1112
+ title: "Delete shared plan",
1113
+ type: "button",
1114
+ variant: "destructive",
1115
+ children: /* @__PURE__ */ jsx(Trash2, {
1116
+ "aria-hidden": true,
1117
+ size: 16
1118
+ })
1119
+ }) : null, /* @__PURE__ */ jsx(Button, {
1120
+ "aria-label": "Download plan",
1121
+ className: "plan-download-button",
1122
+ onClick: downloadPlan,
1123
+ size: "icon",
1124
+ title: "Download plan",
1125
+ type: "button",
1126
+ children: /* @__PURE__ */ jsx(DownloadSimpleIcon, {
1127
+ "aria-hidden": true,
1128
+ size: 16,
1129
+ weight: "bold"
1130
+ })
1131
+ })]
1100
1132
  })]
1101
1133
  }), /* @__PURE__ */ jsx(MarkdownEditor, {
1102
1134
  activeAnnotationId: pendingTarget ? pendingPlanCommentId : activeThreadId,
@@ -2098,14 +2130,41 @@ const getReviewCommentRangeProps = (comment) => {
2098
2130
  ...startSide !== comment.side ? { startSide } : {}
2099
2131
  } : {};
2100
2132
  };
2101
- const toPullRequestReviewComment = (comment) => ({
2133
+ const toPullRequestReviewComment = (comment, { includeSectionId = false } = {}) => ({
2102
2134
  body: comment.body,
2103
2135
  filePath: comment.filePath,
2104
2136
  ...comment.lineNumber != null ? { lineNumber: comment.lineNumber } : {},
2137
+ ...includeSectionId ? { sectionId: comment.sectionId } : {},
2105
2138
  ...comment.side ? { side: comment.side } : {},
2106
2139
  ...getReviewCommentRangeProps(comment),
2107
2140
  ...comment.threadId ? { threadId: comment.threadId } : {}
2108
2141
  });
2142
+ const toSubmittedReviewComment = (comment, draft) => ({
2143
+ ...comment.anchor ? { anchor: comment.anchor } : {},
2144
+ author: comment.author,
2145
+ body: comment.body,
2146
+ ...comment.canDelete ? { canDelete: true } : {},
2147
+ ...comment.canEdit ? { canEdit: true } : {},
2148
+ ...comment.canReplyThread === false ? { canReplyThread: false } : {},
2149
+ ...comment.canResolveThread ? { canResolveThread: true } : {},
2150
+ filePath: comment.filePath,
2151
+ id: comment.id,
2152
+ ...comment.isOutdated ? { isOutdated: true } : {},
2153
+ isReadOnly: true,
2154
+ ...comment.isThreadResolved ? { isThreadResolved: true } : {},
2155
+ ...comment.lineNumber != null ? { lineNumber: comment.lineNumber } : {},
2156
+ sectionId: comment.sectionId ?? draft.sectionId,
2157
+ ...comment.side ? { side: comment.side } : {},
2158
+ ...comment.startLineNumber != null ? { startLineNumber: comment.startLineNumber } : {},
2159
+ ...comment.startSide ? { startSide: comment.startSide } : {},
2160
+ ...comment.submittedAt ? { submittedAt: comment.submittedAt } : {},
2161
+ ...comment.threadId ? { threadId: comment.threadId } : {},
2162
+ ...comment.url ? { url: comment.url } : {}
2163
+ });
2164
+ const mergeReviewComments = (snapshotComments, localComments) => {
2165
+ const snapshotIds = new Set(snapshotComments.map((comment) => comment.id));
2166
+ return [...snapshotComments, ...localComments.filter((comment) => !snapshotIds.has(comment.id))];
2167
+ };
2109
2168
  const isPendingPullRequestReviewComment = (comment) => !comment.isReadOnly && !comment.threadId && comment.remoteSubmit?.status !== "submitting" && comment.body.trim().length > 0;
2110
2169
  const getPendingPullRequestReviewComments = (comments, activeDraft = null) => {
2111
2170
  return comments.flatMap((comment) => {
@@ -2120,8 +2179,9 @@ const findReusableReviewCommentDraft = (comments, activeDraft = null) => comment
2120
2179
  const getCommentKey = (comment) => isFileReviewComment(comment) ? `${comment.sectionId}:file` : `${comment.sectionId}:${comment.side}:${comment.lineNumber}:${comment.startLineNumber ?? comment.lineNumber}:${comment.startSide ?? comment.side}`;
2121
2180
  const getCommentTextDigest = (value) => value ? `${value.length},${value.split("\n").length}` : "0,0";
2122
2181
  const getReviewCommentsDigest = (comments) => comments.map((comment) => `${comment.id}:${comment.sectionId}:${comment.side}:${comment.lineNumber}:${comment.startLineNumber ?? ""}:${comment.startSide ?? ""}:${comment.anchor ?? ""}:${getCommentTextDigest(comment.body)}:${comment.codexReply?.status ?? ""}:${getCommentTextDigest(comment.codexReply?.body)}:${getCommentTextDigest(comment.codexReply?.error)}:${comment.remoteSubmit?.status ?? ""}:${comment.remoteSubmit?.error ?? ""}:${comment.threadId ?? ""}:${comment.canResolveThread === true ? "1" : "0"}:${comment.isThreadResolved === true ? "1" : "0"}`).join("\0");
2123
- const getReviewCommentsFromState = (state) => state.source.type === "pull-request" ? (state.reviewComments ?? []).flatMap((comment) => {
2124
- const section = state.files.find((candidate) => candidate.path === comment.filePath)?.sections[0];
2182
+ const getReviewCommentsFromState = (state) => (state.reviewComments ?? []).flatMap((comment) => {
2183
+ const file = state.files.find((candidate) => candidate.path === comment.filePath);
2184
+ const section = file?.sections.find((candidate) => candidate.id === comment.sectionId) ?? file?.sections[0];
2125
2185
  return section ? [{
2126
2186
  author: comment.author,
2127
2187
  body: comment.body,
@@ -2143,7 +2203,7 @@ const getReviewCommentsFromState = (state) => state.source.type === "pull-reques
2143
2203
  ...comment.threadId ? { threadId: comment.threadId } : {},
2144
2204
  url: comment.url
2145
2205
  }] : [];
2146
- }) : [];
2206
+ });
2147
2207
  const shouldDiscardReviewCommentOnEscape = (body, confirmDiscard = window.confirm) => body.trim().length === 0 || confirmDiscard("Discard this review comment?");
2148
2208
  //#endregion
2149
2209
  //#region app/components/useCopiedState.ts
@@ -4097,8 +4157,8 @@ function TocStop({ current, onSelect, stop, visited }) {
4097
4157
  })]
4098
4158
  });
4099
4159
  }
4100
- function SupportingFilesStop({ changedPaths, files, navigation, showWhitespace, walkthroughView }) {
4101
- const uncoveredFiles = getUncoveredWalkthroughFileLineItems(files, walkthroughView, showWhitespace).filter((file) => !changedPaths?.has(file.path));
4160
+ function SupportingFilesStop({ files, navigation, showWhitespace, walkthroughView }) {
4161
+ const uncoveredFiles = getUncoveredWalkthroughFileLineItems(files, walkthroughView, showWhitespace);
4102
4162
  if (walkthroughView.support.length === 0 && uncoveredFiles.length === 0) return null;
4103
4163
  const current = navigation.mode === "support";
4104
4164
  const isDone = navigation.supportVisited && !current;
@@ -4141,40 +4201,7 @@ function SupportingFilesStop({ changedPaths, files, navigation, showWhitespace,
4141
4201
  })]
4142
4202
  });
4143
4203
  }
4144
- function ChangedFilesStop({ changedPaths, files, navigation, showWhitespace, walkthroughView }) {
4145
- const changedFiles = getUncoveredWalkthroughFileLineItems(files, walkthroughView, showWhitespace).filter((file) => changedPaths?.has(file.path));
4146
- if (changedFiles.length === 0) return null;
4147
- const fileRows = formatWalkthroughFileLineRows(changedFiles);
4148
- return /* @__PURE__ */ jsxs("div", {
4149
- className: "wt-toc-chapter",
4150
- children: [/* @__PURE__ */ jsxs("div", {
4151
- className: "wt-toc-chapter-head",
4152
- children: [/* @__PURE__ */ jsx("span", {
4153
- className: "wt-toc-chapter-icon",
4154
- children: /* @__PURE__ */ jsx(ArrowsClockwiseIcon, { size: 15 })
4155
- }), /* @__PURE__ */ jsx("span", {
4156
- className: "wt-toc-chapter-title",
4157
- children: "Changed"
4158
- })]
4159
- }), /* @__PURE__ */ jsx("div", {
4160
- className: "wt-toc-stops",
4161
- children: /* @__PURE__ */ jsxs("button", {
4162
- className: "wt-toc-stop",
4163
- onClick: navigation.openSupport,
4164
- title: "Changed after the walkthrough was generated",
4165
- type: "button",
4166
- children: [/* @__PURE__ */ jsx("span", {
4167
- className: "wt-toc-rail",
4168
- children: /* @__PURE__ */ jsx("span", { className: "wt-toc-node" })
4169
- }), /* @__PURE__ */ jsx("span", {
4170
- className: "wt-toc-main",
4171
- children: /* @__PURE__ */ jsx(TocFileRows, { files: fileRows })
4172
- })]
4173
- })
4174
- })]
4175
- });
4176
- }
4177
- function NarrativeSidebar({ allowCommit = true, changedPaths, files, navigation, onShareWalkthrough, shareWalkthroughDisabled = false, showWhitespace, walkthrough }) {
4204
+ function NarrativeSidebar({ allowCommit = true, files, navigation, onShareWalkthrough, shareWalkthroughDisabled = false, showWhitespace, walkthrough }) {
4178
4205
  const { walkthroughView } = navigation;
4179
4206
  if (!walkthroughView) return /* @__PURE__ */ jsx("div", {
4180
4207
  className: "wt-empty",
@@ -4220,14 +4247,6 @@ function NarrativeSidebar({ allowCommit = true, changedPaths, files, navigation,
4220
4247
  })]
4221
4248
  }, chapter.id)),
4222
4249
  /* @__PURE__ */ jsx(SupportingFilesStop, {
4223
- changedPaths,
4224
- files,
4225
- navigation,
4226
- showWhitespace,
4227
- walkthroughView
4228
- }),
4229
- /* @__PURE__ */ jsx(ChangedFilesStop, {
4230
- changedPaths,
4231
4250
  files,
4232
4251
  navigation,
4233
4252
  showWhitespace,
@@ -5171,7 +5190,7 @@ function ImageDiffPreview({ file, loadImageContent, onLayoutReady, section, sour
5171
5190
  })]
5172
5191
  });
5173
5192
  }
5174
- function ReviewCommentEditor({ agentId, agentLabel, comment, displayName, focusCommentId, focusEditorRef, identity, isPullRequest, keymap, onAskCodex, onCommentBlur, onCommentDraftChange, onCommentFocus, onDeleteComment, onSaveCommentEdit, onSubmitComment, onUpdateComment }) {
5193
+ function ReviewCommentEditor({ agentId, agentLabel, comment, displayName, focusCommentId, focusEditorRef, identity, keymap, onAskCodex, onCommentBlur, onCommentDraftChange, onCommentFocus, onDeleteComment, onSaveCommentEdit, onSubmitComment, onUpdateComment, supportsReviewCommentActions }) {
5175
5194
  const [editState, setEditState] = useState(() => ({
5176
5195
  commentId: comment.id,
5177
5196
  draft: comment.body,
@@ -5213,7 +5232,7 @@ function ReviewCommentEditor({ agentId, agentLabel, comment, displayName, focusC
5213
5232
  const draftComment = withCommentBody(comment, draft);
5214
5233
  const canAskCodex = onAskCodex != null && canAskCodexForComment(draftComment);
5215
5234
  const commentCanSubmit = canSubmitComment(draftComment);
5216
- const canEditExistingComment = isPullRequest && comment.isReadOnly && comment.canEdit === true;
5235
+ const canEditExistingComment = supportsReviewCommentActions && comment.isReadOnly && comment.canEdit === true;
5217
5236
  const canSaveEdit = canEditExistingComment && !editSubmitting && Boolean(editDraft.trim()) && editDraft.trim() !== displayedBody.trim();
5218
5237
  const editEditorRef = useRef(null);
5219
5238
  const setEditEditorRef = useCallback((editor) => {
@@ -5395,13 +5414,13 @@ function ReviewCommentEditor({ agentId, agentLabel, comment, displayName, focusC
5395
5414
  ]);
5396
5415
  const handleKeyDown = useCallback((event) => {
5397
5416
  if (matchesShortcut(event, keymap, "submitComment")) {
5398
- if (isPullRequest && commentCanSubmit) {
5417
+ if (supportsReviewCommentActions && commentCanSubmit) {
5399
5418
  event.preventDefault();
5400
5419
  event.stopPropagation();
5401
5420
  handleSubmitComment();
5402
5421
  return;
5403
5422
  }
5404
- if (!isPullRequest && canAskCodex) {
5423
+ if (!supportsReviewCommentActions && canAskCodex) {
5405
5424
  event.preventDefault();
5406
5425
  event.stopPropagation();
5407
5426
  handleAskCodex();
@@ -5421,9 +5440,9 @@ function ReviewCommentEditor({ agentId, agentLabel, comment, displayName, focusC
5421
5440
  draft,
5422
5441
  handleAskCodex,
5423
5442
  handleSubmitComment,
5424
- isPullRequest,
5425
5443
  keymap,
5426
- onDeleteComment
5444
+ onDeleteComment,
5445
+ supportsReviewCommentActions
5427
5446
  ]);
5428
5447
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
5429
5448
  className: "review-comment",
@@ -5431,7 +5450,7 @@ function ReviewCommentEditor({ agentId, agentLabel, comment, displayName, focusC
5431
5450
  className: "review-comment-body",
5432
5451
  children: [
5433
5452
  /* @__PURE__ */ jsxs("div", {
5434
- className: `review-comment-header${isPullRequest && !comment.isReadOnly || canEditExistingComment || comment.canDelete || editingExistingComment ? " with-comment-action" : ""}${comment.isReadOnly ? " read-only" : ""}`,
5453
+ className: `review-comment-header${supportsReviewCommentActions && !comment.isReadOnly || canEditExistingComment || comment.canDelete || editingExistingComment ? " with-comment-action" : ""}${comment.isReadOnly ? " read-only" : ""}`,
5435
5454
  children: [
5436
5455
  /* @__PURE__ */ jsx("strong", { children: displayName }),
5437
5456
  editingExistingComment ? /* @__PURE__ */ jsxs("span", {
@@ -5484,7 +5503,7 @@ function ReviewCommentEditor({ agentId, agentLabel, comment, displayName, focusC
5484
5503
  src: agentIconUrl(agentId)
5485
5504
  }), "Ask"]
5486
5505
  }) : null,
5487
- isPullRequest && !comment.isReadOnly ? /* @__PURE__ */ jsxs("button", {
5506
+ supportsReviewCommentActions && !comment.isReadOnly ? /* @__PURE__ */ jsxs("button", {
5488
5507
  className: "review-comment-action",
5489
5508
  disabled: !commentCanSubmit,
5490
5509
  onClick: handleSubmitComment,
@@ -5636,7 +5655,7 @@ const groupReviewCommentsByThread = (comments) => {
5636
5655
  };
5637
5656
  const noopResolveThread = () => {};
5638
5657
  const noopLayoutReady = () => {};
5639
- function ReviewCommentThreadGroup({ agentId, agentLabel, comments, focusCommentId, focusEditorRef, identity, isPullRequest, keymap, onAskCodex, onCommentBlur, onCommentDraftChange, onCommentFocus, onDeleteComment, onReplyToThread, onResolveThread = noopResolveThread, onSaveCommentEdit, onSubmitComment, onUpdateComment }) {
5658
+ function ReviewCommentThreadGroup({ agentId, agentLabel, comments, focusCommentId, focusEditorRef, identity, keymap, onAskCodex, onCommentBlur, onCommentDraftChange, onCommentFocus, onDeleteComment, onReplyToThread, onResolveThread = noopResolveThread, onSaveCommentEdit, onSubmitComment, onUpdateComment, supportsReviewCommentActions }) {
5640
5659
  const [resolveState, setResolveState] = useState({
5641
5660
  error: null,
5642
5661
  submitting: false,
@@ -5645,8 +5664,8 @@ function ReviewCommentThreadGroup({ agentId, agentLabel, comments, focusCommentI
5645
5664
  const lastComment = comments.at(-1);
5646
5665
  const threadId = lastComment?.threadId;
5647
5666
  const threadResolved = comments.some((comment) => comment.isThreadResolved === true);
5648
- const canResolveThread = isPullRequest && threadId != null && comments.some((comment) => comment.canResolveThread === true);
5649
- const canReplyToThread = isPullRequest && threadId != null && comments.some((comment) => comment.isReadOnly) && !comments.some((comment) => !comment.isReadOnly) && !comments.some((comment) => comment.canReplyThread === false) && !threadResolved;
5667
+ const canResolveThread = supportsReviewCommentActions && threadId != null && comments.some((comment) => comment.canResolveThread === true);
5668
+ const canReplyToThread = supportsReviewCommentActions && threadId != null && comments.some((comment) => comment.isReadOnly) && !comments.some((comment) => !comment.isReadOnly) && !comments.some((comment) => comment.canReplyThread === false) && !threadResolved;
5650
5669
  const hasThreadActions = canReplyToThread || canResolveThread;
5651
5670
  const resolving = resolveState.threadId === threadId && resolveState.submitting;
5652
5671
  const resolveError = resolveState.threadId === threadId ? resolveState.error : null;
@@ -5693,7 +5712,6 @@ function ReviewCommentThreadGroup({ agentId, agentLabel, comments, focusCommentI
5693
5712
  focusCommentId,
5694
5713
  focusEditorRef,
5695
5714
  identity,
5696
- isPullRequest,
5697
5715
  keymap,
5698
5716
  onAskCodex,
5699
5717
  onCommentBlur,
@@ -5702,7 +5720,8 @@ function ReviewCommentThreadGroup({ agentId, agentLabel, comments, focusCommentI
5702
5720
  onDeleteComment,
5703
5721
  onSaveCommentEdit,
5704
5722
  onSubmitComment,
5705
- onUpdateComment
5723
+ onUpdateComment,
5724
+ supportsReviewCommentActions
5706
5725
  }, comment.id);
5707
5726
  }), hasThreadActions ? /* @__PURE__ */ jsxs("div", {
5708
5727
  className: "review-comment-thread-footer",
@@ -5727,7 +5746,7 @@ function ReviewCommentThreadGroup({ agentId, agentLabel, comments, focusCommentI
5727
5746
  }) : null]
5728
5747
  });
5729
5748
  }
5730
- function ReviewAnnotation({ agentId, agentLabel, annotation, comments, focusCommentId, focusCommentRequest, identity, isPullRequest, keymap, onAskCodex, onCommentBlur, onCommentDraftChange, onCommentFocus, onDeleteComment, onHeightChange, onReplyToThread, onResolveThread = noopResolveThread, onSaveCommentEdit, onSubmitComment, onUpdateComment }) {
5749
+ function ReviewAnnotation({ agentId, agentLabel, annotation, comments, focusCommentId, focusCommentRequest, identity, keymap, onAskCodex, onCommentBlur, onCommentDraftChange, onCommentFocus, onDeleteComment, onHeightChange, onReplyToThread, onResolveThread = noopResolveThread, onSaveCommentEdit, onSubmitComment, onUpdateComment, supportsReviewCommentActions }) {
5731
5750
  const focusEditorRef = useRef(null);
5732
5751
  const threadRef = useRef(null);
5733
5752
  const setFocusEditorRef = useCallback((node) => {
@@ -5767,7 +5786,6 @@ function ReviewAnnotation({ agentId, agentLabel, annotation, comments, focusComm
5767
5786
  focusCommentId,
5768
5787
  focusEditorRef: setFocusEditorRef,
5769
5788
  identity,
5770
- isPullRequest,
5771
5789
  keymap,
5772
5790
  onAskCodex,
5773
5791
  onCommentBlur,
@@ -5778,7 +5796,8 @@ function ReviewAnnotation({ agentId, agentLabel, annotation, comments, focusComm
5778
5796
  onResolveThread,
5779
5797
  onSaveCommentEdit,
5780
5798
  onSubmitComment,
5781
- onUpdateComment
5799
+ onUpdateComment,
5800
+ supportsReviewCommentActions
5782
5801
  }, group.key))
5783
5802
  });
5784
5803
  }
@@ -5966,7 +5985,7 @@ const getHunkSelectionRange = (hunk) => {
5966
5985
  } : null;
5967
5986
  };
5968
5987
  const isSameSelection = (a, b) => a.start === b.start && a.end === b.end && a.side === b.side;
5969
- function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedToggle = false, blocks, bottomInset = codeViewLayout.paddingBottom, codeQualityFindings = [], collapsed, comments, commitMetadata, diffLineHeight = 20, diffStyle, disableWorkerPool = false, expandedGenerated = emptyExpandedGenerated, files, focusCommentId, focusCommentRequest, forceExpandedPaths, gitIdentity, hunkNavigation, initialMarkdownPreviewSectionIds = emptyMarkdownPreviewSectionIds, isPullRequest, isReadOnly = false, itemVersionByKey, keymap, loadingSectionIds, onActiveBlockChange, onAskCodex, onCommentDraftChange, onCreateComment, onDeleteComment, onLoadImageContent, onLoadSection, onLoadSectionContents, onOpenFile, onRefreshMarkdown, onResolveThread = noopResolveThread, onSaveCommentEdit, onSelectPathFromScroll, onSubmitComment, onToggleCollapsed, onToggleViewed, onUpdateComment, onUpdateSourceDescription, onUpdateSourceTitle, onUploadSourceDescriptionAsset, reviewIdentityByPath, scrollTarget, searchQuery, selectedPath, showSourceDescription = true, showWhitespace, source, sourceDescriptionActions, sourceDescriptionFooter, theme = "system", viewed, walkthroughNotes, wordWrap }) {
5988
+ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedToggle = false, blocks, bottomInset = codeViewLayout.paddingBottom, codeQualityFindings = [], collapsed, comments, commitMetadata, diffLineHeight = 20, diffStyle, disableWorkerPool = false, expandedGenerated = emptyExpandedGenerated, files, focusCommentId, focusCommentRequest, forceExpandedPaths, gitIdentity, hunkNavigation, initialMarkdownPreviewSectionIds = emptyMarkdownPreviewSectionIds, isReadOnly = false, itemVersionByKey, keymap, loadingSectionIds, onActiveBlockChange, onAskCodex, onCommentDraftChange, onCreateComment, onDeleteComment, onLoadImageContent, onLoadSection, onLoadSectionContents, onOpenFile, onRefreshMarkdown, onResolveThread = noopResolveThread, onSaveCommentEdit, onSelectPathFromScroll, onSubmitComment, onToggleCollapsed, onToggleViewed, onUpdateComment, onUpdateSourceDescription, onUpdateSourceTitle, onUploadSourceDescriptionAsset, reviewIdentityByPath, scrollTarget, searchQuery, selectedPath, showSourceDescription = true, showWhitespace, source, sourceDescriptionActions, sourceDescriptionFooter, supportsReviewCommentActions, theme = "system", viewed, walkthroughNotes, wordWrap }) {
5970
5989
  const codeViewRef = useRef(null);
5971
5990
  const markdownEditorRefs = useRef(/* @__PURE__ */ new Map());
5972
5991
  const refreshingMarkdownSectionsRef = useRef(/* @__PURE__ */ new Set());
@@ -6885,7 +6904,6 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
6885
6904
  focusCommentId,
6886
6905
  focusCommentRequest,
6887
6906
  identity: gitIdentity,
6888
- isPullRequest,
6889
6907
  keymap,
6890
6908
  onAskCodex,
6891
6909
  onCommentBlur: blurComment,
@@ -6897,7 +6915,8 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
6897
6915
  onResolveThread,
6898
6916
  onSaveCommentEdit,
6899
6917
  onSubmitComment,
6900
- onUpdateComment
6918
+ onUpdateComment,
6919
+ supportsReviewCommentActions
6901
6920
  });
6902
6921
  }, [
6903
6922
  agentId,
@@ -6908,7 +6927,6 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
6908
6927
  focusCommentRequest,
6909
6928
  focusComment,
6910
6929
  gitIdentity,
6911
- isPullRequest,
6912
6930
  itemMetadata,
6913
6931
  keymap,
6914
6932
  markMarkdownPreviewLayoutReady,
@@ -6924,7 +6942,8 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
6924
6942
  renderComments,
6925
6943
  replyToThread,
6926
6944
  setMarkdownEditorRef,
6927
- source
6945
+ source,
6946
+ supportsReviewCommentActions
6928
6947
  ]);
6929
6948
  const handleScroll = useCallback((_scrollTop, viewer) => {
6930
6949
  onSelectPathFromScroll(viewer);
@@ -7528,27 +7547,6 @@ function SupportHeader({ current }) {
7528
7547
  }), /* @__PURE__ */ jsx(Narration, { prose: "Supporting changes grouped outside the main sequence." })]
7529
7548
  });
7530
7549
  }
7531
- function ChangedHeader({ current, onRegenerate, regenerateDisabled = false }) {
7532
- return /* @__PURE__ */ jsxs("div", {
7533
- className: `wt-stop-block wt-stop-block-header${current ? " current" : ""}`,
7534
- children: [/* @__PURE__ */ jsxs("div", {
7535
- className: "wt-stage-title-row",
7536
- children: [/* @__PURE__ */ jsx("h2", {
7537
- className: "wt-stage-title",
7538
- children: "Changed"
7539
- }), onRegenerate ? /* @__PURE__ */ jsxs("button", {
7540
- className: "wt-regenerate",
7541
- disabled: regenerateDisabled,
7542
- onClick: onRegenerate,
7543
- type: "button",
7544
- children: [/* @__PURE__ */ jsx(ArrowsClockwiseIcon, {
7545
- size: 13,
7546
- weight: "bold"
7547
- }), regenerateDisabled ? "Regenerating…" : "Regenerate walkthrough"]
7548
- }) : null]
7549
- }), /* @__PURE__ */ jsx(Narration, { prose: "These changes arrived after the walkthrough was generated and are not part of its narrative yet." })]
7550
- });
7551
- }
7552
7550
  const createWalkthroughBlocks = (files, walkthroughView, currentIndex) => {
7553
7551
  const blocks = [];
7554
7552
  const firstBlockIdByStop = [];
@@ -7600,7 +7598,7 @@ const getBlockReviewTarget = (blocks, blockId) => {
7600
7598
  reviewIdentity: block.reviewIdentity
7601
7599
  } : null;
7602
7600
  };
7603
- const createSupportBlocks = (files, selected, walkthroughView, showWhitespace, changedPaths, onRegenerateWalkthrough, regenerateDisabled) => {
7601
+ const createSupportBlocks = (files, selected, walkthroughView, showWhitespace) => {
7604
7602
  const blocks = [];
7605
7603
  for (const group of walkthroughView.supportByReason) for (const item of group.files) getFocusedRunDiffs(item, files).forEach(({ file, note, reviewIdentity }, runIndex) => {
7606
7604
  const blockId = `walkthrough:support:${item.id}:${runIndex}`;
@@ -7616,9 +7614,7 @@ const createSupportBlocks = (files, selected, walkthroughView, showWhitespace, c
7616
7614
  });
7617
7615
  });
7618
7616
  const uncoveredFiles = getUncoveredWalkthroughFiles(files, walkthroughView, showWhitespace);
7619
- const changedFiles = uncoveredFiles.filter((file) => changedPaths?.has(file.path));
7620
- const uncoveredSupportFiles = uncoveredFiles.filter((file) => !changedPaths?.has(file.path));
7621
- for (const file of uncoveredSupportFiles) {
7617
+ for (const file of uncoveredFiles) {
7622
7618
  const blockId = `walkthrough:uncovered:${file.path}`;
7623
7619
  const isFirstBlock = blocks.length === 0;
7624
7620
  blocks.push({
@@ -7634,25 +7630,6 @@ const createSupportBlocks = (files, selected, walkthroughView, showWhitespace, c
7634
7630
  }
7635
7631
  });
7636
7632
  }
7637
- changedFiles.forEach((file, fileIndex) => {
7638
- const blockId = `walkthrough:changed:${file.path}`;
7639
- blocks.push({
7640
- file,
7641
- header: fileIndex === 0 ? /* @__PURE__ */ jsx(ChangedHeader, {
7642
- current: selected,
7643
- onRegenerate: onRegenerateWalkthrough,
7644
- regenerateDisabled
7645
- }) : null,
7646
- headerSelected: selected,
7647
- id: blockId,
7648
- itemIdPrefix: blockId,
7649
- note: "Changed after the walkthrough was generated.",
7650
- reviewIdentity: {
7651
- fingerprint: file.fingerprint,
7652
- key: blockId
7653
- }
7654
- });
7655
- });
7656
7633
  return blocks;
7657
7634
  };
7658
7635
  function Arc({ committable, navigation, onShareWalkthrough, shareWalkthroughDisabled = false, supportAvailable, walkthroughView }) {
@@ -7815,7 +7792,7 @@ function Arc({ committable, navigation, onShareWalkthrough, shareWalkthroughDisa
7815
7792
  ]
7816
7793
  });
7817
7794
  }
7818
- function NarrativeWalkthroughView({ allowCommit = true, changedPaths, files, navigation, onActiveReviewTargetChange, onCommit, onCommitOutput, onRegenerateWalkthrough, onShareWalkthrough, onUpdateCommitMessage, regenerateDisabled, renderDiffBlocks, shareWalkthroughDisabled, showWhitespace, walkthrough }) {
7795
+ function NarrativeWalkthroughView({ allowCommit = true, files, navigation, onActiveReviewTargetChange, onCommit, onCommitOutput, onShareWalkthrough, onUpdateCommitMessage, renderDiffBlocks, shareWalkthroughDisabled, showWhitespace, walkthrough }) {
7819
7796
  const { walkthroughView } = navigation;
7820
7797
  const committable = allowCommit && isWalkthroughCommittable(walkthrough);
7821
7798
  const walkthroughBlocks = useMemo(() => walkthroughView ? createWalkthroughBlocks(files, walkthroughView, navigation.index) : emptyWalkthroughBlockSet, [
@@ -7823,12 +7800,9 @@ function NarrativeWalkthroughView({ allowCommit = true, changedPaths, files, nav
7823
7800
  navigation.index,
7824
7801
  walkthroughView
7825
7802
  ]);
7826
- const supportBlocks = useMemo(() => walkthroughView ? createSupportBlocks(files, navigation.mode === "support", walkthroughView, showWhitespace, changedPaths, onRegenerateWalkthrough, regenerateDisabled) : [], [
7827
- changedPaths,
7803
+ const supportBlocks = useMemo(() => walkthroughView ? createSupportBlocks(files, navigation.mode === "support", walkthroughView, showWhitespace) : [], [
7828
7804
  files,
7829
7805
  navigation.mode,
7830
- onRegenerateWalkthrough,
7831
- regenerateDisabled,
7832
7806
  showWhitespace,
7833
7807
  walkthroughView
7834
7808
  ]);
@@ -8712,8 +8686,16 @@ const disabledCommitMessage = async () => ({
8712
8686
  reason: "Shared walkthroughs are read-only.",
8713
8687
  status: "unavailable"
8714
8688
  });
8715
- function ReviewSurface({ commenting, externalUrl, gitIdentity = null, initialMode, interactive, onModeChange, providerLabel = "provider", settingsBar, signInLabel = "Sign in to comment", snapshot, sourceDescriptionFooterAside, title }) {
8689
+ function ReviewSurface({ commenting, externalUrl, gitIdentity = null, initialMode, interactive, onDeleteShare, onModeChange, providerLabel = "provider", settingsBar, signInLabel = "Sign in to comment", snapshot, sourceDescriptionFooterAside, title }) {
8716
8690
  const canComment = commenting?.canComment ?? Boolean(interactive);
8691
+ const deleteShare = useCallback(async () => {
8692
+ if (!onDeleteShare || !window.confirm("Delete this shared walkthrough? This cannot be undone.")) return;
8693
+ try {
8694
+ await onDeleteShare();
8695
+ } catch (error) {
8696
+ window.alert(error instanceof Error ? error.message : String(error));
8697
+ }
8698
+ }, [onDeleteShare]);
8717
8699
  const submitReviewComment = commenting?.onSubmitComment ?? interactive?.onSubmitComment;
8718
8700
  const submitGeneralDiscussion = commenting?.onSubmitGeneralComment ?? interactive?.onSubmitGeneralComment;
8719
8701
  const updateReviewComment = commenting?.onUpdateComment ?? interactive?.onUpdateComment;
@@ -8741,7 +8723,7 @@ function ReviewSurface({ commenting, externalUrl, gitIdentity = null, initialMod
8741
8723
  body: editedReviewCommentBodies[comment.id]
8742
8724
  } : comment), [editedReviewCommentBodies, snapshotReviewComments]);
8743
8725
  const [localReviewComments, setLocalReviewComments] = useState(emptyReviewComments);
8744
- const reviewComments = useMemo(() => [...visibleSnapshotReviewComments, ...localReviewComments], [localReviewComments, visibleSnapshotReviewComments]);
8726
+ const reviewComments = useMemo(() => mergeReviewComments(visibleSnapshotReviewComments, localReviewComments), [localReviewComments, visibleSnapshotReviewComments]);
8745
8727
  const { activeReviewCommentDraftRef, activeReviewCommentDraftState, clearCommentFocus, createComment, deleteComment: deleteLocalComment, focusCommentId, focusCommentRequest, reviewCommentsRef, updateActiveReviewCommentDraft, updateComment } = useReviewCommentDrafts({
8746
8728
  canCreateComment: canComment,
8747
8729
  comments: reviewComments,
@@ -8751,6 +8733,7 @@ function ReviewSurface({ commenting, externalUrl, gitIdentity = null, initialMod
8751
8733
  const generalCommentThreads = snapshot.repository.generalComments ?? emptyGeneralCommentThreads;
8752
8734
  const generalComments = useMemo(() => (snapshot.repository.generalComments ?? emptyGeneralCommentThreads).flatMap((thread) => thread.comments), [snapshot.repository.generalComments]);
8753
8735
  const generalCommentCount = generalComments.length;
8736
+ const showCommentsTab = Boolean(commenting || interactive || generalCommentCount > 0);
8754
8737
  const [generalCommentDraft, setGeneralCommentDraft] = useState("");
8755
8738
  const [generalCommentEditDraft, setGeneralCommentEditDraft] = useState("");
8756
8739
  const [editingGeneralCommentId, setEditingGeneralCommentId] = useState(null);
@@ -8823,6 +8806,7 @@ function ReviewSurface({ commenting, externalUrl, gitIdentity = null, initialMod
8823
8806
  const comment = reviewCommentsRef.current.find((candidate) => candidate.id === commentId);
8824
8807
  if (comment?.isReadOnly && comment.canDelete && commenting?.onDeleteComment) {
8825
8808
  updateActiveReviewCommentDraft(null);
8809
+ setLocalReviewComments((current) => current.filter((candidate) => candidate.id !== commentId));
8826
8810
  commenting.onDeleteComment(commentId).catch((error) => {
8827
8811
  window.alert(error instanceof Error ? error.message : String(error));
8828
8812
  });
@@ -8843,9 +8827,12 @@ function ReviewSurface({ commenting, externalUrl, gitIdentity = null, initialMod
8843
8827
  ...candidate,
8844
8828
  remoteSubmit: { status: "submitting" }
8845
8829
  } : candidate));
8846
- submitReviewComment(toPullRequestReviewComment(comment)).then(() => {
8830
+ submitReviewComment(toPullRequestReviewComment(comment, { includeSectionId: commenting != null })).then((submittedComment) => {
8847
8831
  clearCommentFocus(commentId);
8848
- setLocalReviewComments((current) => current.filter((candidate) => candidate.id !== commentId));
8832
+ setLocalReviewComments((current) => current.flatMap((candidate) => {
8833
+ if (candidate.id !== commentId) return [candidate];
8834
+ return [toSubmittedReviewComment(submittedComment, candidate)];
8835
+ }));
8849
8836
  bumpItemVersion(comment.filePath);
8850
8837
  }).catch((error) => {
8851
8838
  setLocalReviewComments((current) => current.map((candidate) => candidate.id === commentId ? {
@@ -8860,6 +8847,7 @@ function ReviewSurface({ commenting, externalUrl, gitIdentity = null, initialMod
8860
8847
  }, [
8861
8848
  bumpItemVersion,
8862
8849
  clearCommentFocus,
8850
+ commenting,
8863
8851
  reviewCommentsRef,
8864
8852
  submitReviewComment,
8865
8853
  updateActiveReviewCommentDraft
@@ -8871,7 +8859,7 @@ function ReviewSurface({ commenting, externalUrl, gitIdentity = null, initialMod
8871
8859
  if (event === "COMMENT" && pendingComments.length === 0 && !body?.trim()) return;
8872
8860
  const pendingIds = new Set(pendingComments.map((comment) => comment.id));
8873
8861
  setPullRequestReviewSubmitting(event);
8874
- const formattedComments = pendingComments.map(toPullRequestReviewComment);
8862
+ const formattedComments = pendingComments.map((comment) => toPullRequestReviewComment(comment));
8875
8863
  return (body ? interactive.onSubmitReview(event, formattedComments, body) : interactive.onSubmitReview(event, formattedComments)).then(() => {
8876
8864
  updateActiveReviewCommentDraft(null);
8877
8865
  setLocalReviewComments((current) => current.filter((comment) => !pendingIds.has(comment.id)));
@@ -9034,7 +9022,6 @@ function ReviewSurface({ commenting, externalUrl, gitIdentity = null, initialMod
9034
9022
  gitIdentity,
9035
9023
  hunkNavigation: null,
9036
9024
  initialMarkdownPreviewSectionIds,
9037
- isPullRequest: snapshot.repository.source.type === "pull-request",
9038
9025
  isReadOnly: !canComment,
9039
9026
  itemVersionByKey,
9040
9027
  keymap,
@@ -9056,6 +9043,7 @@ function ReviewSurface({ commenting, externalUrl, gitIdentity = null, initialMod
9056
9043
  searchQuery: "",
9057
9044
  showWhitespace: snapshot.preferences.showWhitespace,
9058
9045
  source: snapshot.repository.source,
9046
+ supportsReviewCommentActions: submitReviewComment != null,
9059
9047
  theme: snapshot.preferences.theme,
9060
9048
  viewed,
9061
9049
  wordWrap: snapshot.preferences.wordWrap
@@ -9196,7 +9184,7 @@ function ReviewSurface({ commenting, externalUrl, gitIdentity = null, initialMod
9196
9184
  }),
9197
9185
  /* @__PURE__ */ jsxs("div", {
9198
9186
  "aria-label": "Review order",
9199
- className: "sidebar-mode-toggle",
9187
+ className: `sidebar-mode-toggle${showCommentsTab ? " sidebar-mode-toggle-with-comments" : ""}`,
9200
9188
  role: "tablist",
9201
9189
  children: [
9202
9190
  /* @__PURE__ */ jsx("button", {
@@ -9213,7 +9201,7 @@ function ReviewSurface({ commenting, externalUrl, gitIdentity = null, initialMod
9213
9201
  type: "button",
9214
9202
  children: "Walkthrough"
9215
9203
  }),
9216
- commenting || interactive || generalCommentCount > 0 ? /* @__PURE__ */ jsxs("button", {
9204
+ showCommentsTab ? /* @__PURE__ */ jsxs("button", {
9217
9205
  "aria-label": generalCommentCount > 0 ? `Comments (${generalCommentCount})` : "Comments",
9218
9206
  "aria-selected": sidebarMode === "comments",
9219
9207
  onClick: () => changeSidebarMode("comments"),
@@ -9255,13 +9243,29 @@ function ReviewSurface({ commenting, externalUrl, gitIdentity = null, initialMod
9255
9243
  }), walkthroughStatusDescription ? /* @__PURE__ */ jsx("span", { children: walkthroughStatusDescription }) : null]
9256
9244
  })
9257
9245
  }),
9258
- settingsBar || showTotalLineCount ? /* @__PURE__ */ jsxs("div", {
9246
+ onDeleteShare || settingsBar || showTotalLineCount ? /* @__PURE__ */ jsxs("div", {
9259
9247
  className: "sidebar-settings-bar",
9260
- children: [settingsBar, showTotalLineCount ? /* @__PURE__ */ jsx(DiffLineCountBadge, {
9261
- ariaLabelPrefix: "Total change",
9262
- className: "sidebar-total-line-count sidebar-settings-line-count",
9263
- lineCount: totalLineCount
9264
- }) : null]
9248
+ children: [
9249
+ onDeleteShare ? /* @__PURE__ */ jsx(Button, {
9250
+ action: deleteShare,
9251
+ "aria-label": "Delete shared walkthrough",
9252
+ pendingPlaceholder: "…",
9253
+ size: "icon",
9254
+ title: "Delete shared walkthrough",
9255
+ type: "button",
9256
+ variant: "destructive",
9257
+ children: /* @__PURE__ */ jsx(Trash2, {
9258
+ "aria-hidden": true,
9259
+ size: 16
9260
+ })
9261
+ }) : null,
9262
+ settingsBar,
9263
+ showTotalLineCount ? /* @__PURE__ */ jsx(DiffLineCountBadge, {
9264
+ ariaLabelPrefix: "Total change",
9265
+ className: "sidebar-total-line-count sidebar-settings-line-count",
9266
+ lineCount: totalLineCount
9267
+ }) : null
9268
+ ]
9265
9269
  }) : null
9266
9270
  ]
9267
9271
  }),
@@ -9340,7 +9344,7 @@ function ReviewSurface({ commenting, externalUrl, gitIdentity = null, initialMod
9340
9344
  ]
9341
9345
  })
9342
9346
  }) : /* @__PURE__ */ jsx("div", {
9343
- className: "loading codex italic",
9347
+ className: "loading codex",
9344
9348
  children: /* @__PURE__ */ jsx(WalkthroughProgress, {
9345
9349
  phase: null,
9346
9350
  responseLabelIndex: 0,
@@ -9352,4 +9356,4 @@ function ReviewSurface({ commenting, externalUrl, gitIdentity = null, initialMod
9352
9356
  });
9353
9357
  }
9354
9358
  //#endregion
9355
- export { Button, PlanReviewSurface, ReadOnlyGeneralCommentCard, ReviewSurface, buttonVariants, getPlanCommentAffordancePosition, getPlanCommentColorIndex, getPlanCommentIdentityColorIndex, getSharedPlanDownloadContent };
9359
+ export { Button, PlanReviewSurface, ReadOnlyGeneralCommentCard, ReviewSurface, Thinking, buttonVariants, getPlanCommentAffordancePosition, getPlanCommentColorIndex, getPlanCommentIdentityColorIndex, getSharedPlanDownloadContent };