@nkzw/codiff-core 1.14.0 → 1.15.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/app/components/ReviewCodeView.d.ts +3 -1
- package/dist/app/hooks/code-editing-store.d.ts +68 -0
- package/dist/app/hooks/useCodeEditing.d.ts +34 -0
- package/dist/config/types.d.ts +2 -0
- package/dist/lib/code-view-options.d.ts +1 -2
- package/dist/lib/narrative-walkthrough-diff.d.ts +5 -1
- package/dist/lib/narrative-walkthrough-diff.mjs +4 -2
- package/dist/lib/narrative-walkthrough.d.ts +1 -1
- package/dist/react.mjs +813 -187
- package/dist/style.css +106 -27
- package/dist/types.d.ts +13 -0
- package/package.json +3 -3
package/dist/react.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { filterPatchToHunkIds, getSectionWalkthroughHunks, isGeneratedWalkthroughFile, isSyntheticWalkthroughHunk } from "./lib/narrative-walkthrough-diff.mjs";
|
|
1
|
+
import { filterPatchToHunkIds, getHunkOrdinal, getSectionWalkthroughHunks, isGeneratedWalkthroughFile, isSyntheticWalkthroughHunk } from "./lib/narrative-walkthrough-diff.mjs";
|
|
2
2
|
import { Slot } from "@radix-ui/react-slot";
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
|
-
import { Fragment, Suspense, forwardRef, lazy, useCallback, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useOptimistic, useRef, useState, useTransition } from "react";
|
|
4
|
+
import { Fragment, Suspense, forwardRef, lazy, useCallback, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useOptimistic, useRef, useState, useSyncExternalStore, useTransition } from "react";
|
|
5
5
|
import { createPortal, useFormStatus } from "react-dom";
|
|
6
6
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { MarkdownEditor } from "@nkzw/mdx-editor";
|
|
@@ -14,7 +14,7 @@ import { ChatCircleIcon } from "@phosphor-icons/react/ChatCircle";
|
|
|
14
14
|
import { PathIcon } from "@phosphor-icons/react/Path";
|
|
15
15
|
import { TreeStructureIcon } from "@phosphor-icons/react/TreeStructure";
|
|
16
16
|
import { FileTree, useFileTree } from "@pierre/trees/react";
|
|
17
|
-
import { hydratePartialDiff, parseDiffFromFile, parsePatchFiles, registerCustomTheme, setCustomExtension } from "@pierre/diffs";
|
|
17
|
+
import { cloneFileDiffMetadata, getFiletypeFromFileName, getHighlighterOptions, hydratePartialDiff, parseDiffFromFile, parsePatchFiles, preloadHighlighter, registerCustomTheme, setCustomExtension } from "@pierre/diffs";
|
|
18
18
|
import useRelativeTime from "@nkzw/use-relative-time";
|
|
19
19
|
import { CaretDownIcon } from "@phosphor-icons/react/CaretDown";
|
|
20
20
|
import "@phosphor-icons/react/CaretUp";
|
|
@@ -26,8 +26,9 @@ import { WarningOctagonIcon } from "@phosphor-icons/react/WarningOctagon";
|
|
|
26
26
|
import { XIcon } from "@phosphor-icons/react/X";
|
|
27
27
|
import { ColumnsIcon } from "@phosphor-icons/react/Columns";
|
|
28
28
|
import { ImageBrokenIcon } from "@phosphor-icons/react/ImageBroken";
|
|
29
|
+
import { PencilSimpleIcon } from "@phosphor-icons/react/PencilSimple";
|
|
29
30
|
import { SquareSplitVerticalIcon } from "@phosphor-icons/react/SquareSplitVertical";
|
|
30
|
-
import { CodeView, WorkerPoolContextProvider } from "@pierre/diffs/react";
|
|
31
|
+
import { CodeView, EditProvider, WorkerPoolContextProvider } from "@pierre/diffs/react";
|
|
31
32
|
import { CrosshairSimpleIcon } from "@phosphor-icons/react/CrosshairSimple";
|
|
32
33
|
import { ProhibitIcon } from "@phosphor-icons/react/Prohibit";
|
|
33
34
|
import { PersistentMarkdownEditor } from "@nkzw/mdx-editor/persistence";
|
|
@@ -3574,14 +3575,6 @@ registerCustomTheme("Licht", async () => licht_default);
|
|
|
3574
3575
|
registerCustomTheme("Dunkel", async () => dunkel_default);
|
|
3575
3576
|
setCustomExtension("cts", "typescript");
|
|
3576
3577
|
setCustomExtension("mts", "typescript");
|
|
3577
|
-
const statusLabel = {
|
|
3578
|
-
added: "Added",
|
|
3579
|
-
conflicted: "Conflicted",
|
|
3580
|
-
deleted: "Deleted",
|
|
3581
|
-
modified: "Modified",
|
|
3582
|
-
renamed: "Renamed",
|
|
3583
|
-
untracked: "Untracked"
|
|
3584
|
-
};
|
|
3585
3578
|
const sectionLabel = {
|
|
3586
3579
|
commit: "Commit",
|
|
3587
3580
|
"pull-request": "PR",
|
|
@@ -3601,7 +3594,7 @@ const workerHighlighterOptions = {
|
|
|
3601
3594
|
light: "Licht"
|
|
3602
3595
|
},
|
|
3603
3596
|
tokenizeMaxLineLength: 2e4,
|
|
3604
|
-
useTokenTransformer:
|
|
3597
|
+
useTokenTransformer: true
|
|
3605
3598
|
};
|
|
3606
3599
|
const codeViewUnsafeCSS = `
|
|
3607
3600
|
:host {
|
|
@@ -3934,7 +3927,7 @@ const getItemVersion = (value) => {
|
|
|
3934
3927
|
//#region lib/keyboard.ts
|
|
3935
3928
|
const isNativeInputTarget = (target) => {
|
|
3936
3929
|
const candidate = target;
|
|
3937
|
-
return candidate?.closest?.("input, select, textarea") != null || candidate?.isContentEditable === true;
|
|
3930
|
+
return candidate?.closest?.("input, select, textarea, [contenteditable=\"true\"]") != null || candidate?.isContentEditable === true || candidate?.shadowRoot?.activeElement != null && isNativeInputTarget(candidate.shadowRoot.activeElement);
|
|
3938
3931
|
};
|
|
3939
3932
|
const isMacPlatform = (platform = navigator.platform) => platform.toLowerCase().includes("mac");
|
|
3940
3933
|
const isPrimaryModifier = (event, platform = navigator.platform) => isMacPlatform(platform) ? event.metaKey && !event.ctrlKey : event.ctrlKey && !event.metaKey;
|
|
@@ -4052,6 +4045,477 @@ const getSourceKey = (source) => source.type === "commit" ? `commit:${source.ref
|
|
|
4052
4045
|
const getShortRef = (ref) => ref.slice(0, 7);
|
|
4053
4046
|
const getSourceLabel = (source) => source.type === "commit" ? getShortRef(source.ref) : source.type === "branch" || source.type === "branch-diff" ? `Branch vs ${source.ref}` : source.type === "branch-working-tree" ? `Local + branch vs ${source.ref}` : source.type === "range" ? rangeLabel(source) : source.type === "pull-request" ? source.number ? `${source.provider === "gitlab" ? "MR" : "PR"} #${source.number}` : source.provider === "gitlab" ? "Merge request" : "Pull request" : "Uncommitted";
|
|
4054
4047
|
//#endregion
|
|
4048
|
+
//#region config/defaults.ts
|
|
4049
|
+
const defaultConfigTemplate = {
|
|
4050
|
+
settings: {
|
|
4051
|
+
"agentBackend": "codex",
|
|
4052
|
+
"checkForUpdates": true,
|
|
4053
|
+
"claudeModel": "claude-sonnet-4-6",
|
|
4054
|
+
"codeFontFamily": "",
|
|
4055
|
+
"codeFontSize": 13,
|
|
4056
|
+
"copyCommentsOnClose": false,
|
|
4057
|
+
"diffStyle": "split",
|
|
4058
|
+
"editorCommand": "",
|
|
4059
|
+
"lastRepositoryPath": "",
|
|
4060
|
+
"openAIModel": "gpt-5.6-terra",
|
|
4061
|
+
"opencodeModel": "opencode-default",
|
|
4062
|
+
"piModel": "pi-default",
|
|
4063
|
+
"reviewCommentsPrefix": "# Address these Review Comments",
|
|
4064
|
+
"sidebarPosition": "left",
|
|
4065
|
+
"showOutdated": false,
|
|
4066
|
+
"showWhitespace": false,
|
|
4067
|
+
"theme": "system",
|
|
4068
|
+
"walkthroughPrompt": "",
|
|
4069
|
+
"wordWrap": false
|
|
4070
|
+
},
|
|
4071
|
+
keymap: {
|
|
4072
|
+
"askAgent": "Mod+Alt+Enter",
|
|
4073
|
+
"closeSearch": "Escape",
|
|
4074
|
+
"commandBar": "Mod+k",
|
|
4075
|
+
"diffSearch": "Mod+f",
|
|
4076
|
+
"discardComment": "Escape",
|
|
4077
|
+
"doneEditing": "Mod+s",
|
|
4078
|
+
"editFile": "Mod+e",
|
|
4079
|
+
"fileFilter": "Mod+p",
|
|
4080
|
+
"nextHunk": ["Ctrl+ArrowDown", "j"],
|
|
4081
|
+
"nextSearchMatch": "Enter",
|
|
4082
|
+
"openFile": "Mod+Shift+o",
|
|
4083
|
+
"prevHunk": ["Ctrl+ArrowUp", "k"],
|
|
4084
|
+
"prevSearchMatch": "Shift+Enter",
|
|
4085
|
+
"shortcutsHelp": "Shift+?",
|
|
4086
|
+
"submitComment": "Mod+Enter",
|
|
4087
|
+
"toggleSidebar": "Mod+Shift+b",
|
|
4088
|
+
"toggleWordWrap": "Alt+z"
|
|
4089
|
+
}
|
|
4090
|
+
};
|
|
4091
|
+
const createDefaultConfig = () => ({
|
|
4092
|
+
keymap: { ...defaultConfigTemplate.keymap },
|
|
4093
|
+
settings: { ...defaultConfigTemplate.settings }
|
|
4094
|
+
});
|
|
4095
|
+
const defaultKeymap = Object.freeze({ ...defaultConfigTemplate.keymap });
|
|
4096
|
+
Object.freeze({ ...defaultConfigTemplate.settings });
|
|
4097
|
+
const createFileEditingStore = (onChange) => {
|
|
4098
|
+
const sessionRef = { current: null };
|
|
4099
|
+
const draftRef = { current: "" };
|
|
4100
|
+
let timer;
|
|
4101
|
+
let pendingWrite = null;
|
|
4102
|
+
const clearTimer = () => {
|
|
4103
|
+
clearTimeout(timer);
|
|
4104
|
+
timer = void 0;
|
|
4105
|
+
};
|
|
4106
|
+
const setSession = (session) => {
|
|
4107
|
+
sessionRef.current = session ? {
|
|
4108
|
+
...session,
|
|
4109
|
+
dirty: draftRef.current !== session.document.content,
|
|
4110
|
+
hasChanges: session.finishing === "revert" ? session.hasChanges : draftRef.current !== session.originalContent
|
|
4111
|
+
} : null;
|
|
4112
|
+
if (!session) clearTimer();
|
|
4113
|
+
onChange();
|
|
4114
|
+
};
|
|
4115
|
+
const scheduleSave = () => {
|
|
4116
|
+
clearTimer();
|
|
4117
|
+
timer = setTimeout(() => void flush(), 300);
|
|
4118
|
+
};
|
|
4119
|
+
const flush = () => {
|
|
4120
|
+
clearTimer();
|
|
4121
|
+
if (pendingWrite) return pendingWrite.then((saved) => saved ? flush() : false);
|
|
4122
|
+
const current = sessionRef.current;
|
|
4123
|
+
if (!current || draftRef.current === current.document.content) return Promise.resolve(true);
|
|
4124
|
+
const content = draftRef.current;
|
|
4125
|
+
setSession({
|
|
4126
|
+
...current,
|
|
4127
|
+
saveError: void 0,
|
|
4128
|
+
saving: true
|
|
4129
|
+
});
|
|
4130
|
+
pendingWrite = Promise.resolve().then(() => window.codiff.saveRepositoryFile({
|
|
4131
|
+
...current.document,
|
|
4132
|
+
baseVersion: current.document.version,
|
|
4133
|
+
content
|
|
4134
|
+
})).then((result) => {
|
|
4135
|
+
if (result.status === "conflict") throw new Error("The file changed on disk. Your edits are kept; copy them before refreshing.");
|
|
4136
|
+
const latest = sessionRef.current;
|
|
4137
|
+
if (latest) setSession({
|
|
4138
|
+
...latest,
|
|
4139
|
+
document: result.document,
|
|
4140
|
+
saving: false
|
|
4141
|
+
});
|
|
4142
|
+
return true;
|
|
4143
|
+
}).catch((error) => {
|
|
4144
|
+
const latest = sessionRef.current;
|
|
4145
|
+
if (latest) setSession({
|
|
4146
|
+
...latest,
|
|
4147
|
+
saveError: error instanceof Error ? error.message : String(error),
|
|
4148
|
+
saving: false
|
|
4149
|
+
});
|
|
4150
|
+
return false;
|
|
4151
|
+
}).finally(() => {
|
|
4152
|
+
pendingWrite = null;
|
|
4153
|
+
const latest = sessionRef.current;
|
|
4154
|
+
if (latest && !latest.finishing && !latest.saveError && draftRef.current !== latest.document.content) scheduleSave();
|
|
4155
|
+
});
|
|
4156
|
+
return pendingWrite;
|
|
4157
|
+
};
|
|
4158
|
+
const finish = async (revert, beforeClose) => {
|
|
4159
|
+
const current = sessionRef.current;
|
|
4160
|
+
if (!current || current.finishing) return false;
|
|
4161
|
+
clearTimer();
|
|
4162
|
+
setSession({
|
|
4163
|
+
...current,
|
|
4164
|
+
finishing: revert ? "revert" : "done"
|
|
4165
|
+
});
|
|
4166
|
+
let previousDraft;
|
|
4167
|
+
try {
|
|
4168
|
+
if (pendingWrite && !await pendingWrite) return false;
|
|
4169
|
+
if (revert) {
|
|
4170
|
+
previousDraft = draftRef.current;
|
|
4171
|
+
draftRef.current = current.originalContent;
|
|
4172
|
+
}
|
|
4173
|
+
do {
|
|
4174
|
+
do
|
|
4175
|
+
if (!await flush()) {
|
|
4176
|
+
if (previousDraft !== void 0) draftRef.current = previousDraft;
|
|
4177
|
+
return false;
|
|
4178
|
+
}
|
|
4179
|
+
while (!revert && draftRef.current !== sessionRef.current?.document.content);
|
|
4180
|
+
await beforeClose?.();
|
|
4181
|
+
} while (!revert && draftRef.current !== sessionRef.current?.document.content);
|
|
4182
|
+
setSession(null);
|
|
4183
|
+
return true;
|
|
4184
|
+
} catch (error) {
|
|
4185
|
+
if (previousDraft !== void 0) draftRef.current = previousDraft;
|
|
4186
|
+
throw error;
|
|
4187
|
+
} finally {
|
|
4188
|
+
const latest = sessionRef.current;
|
|
4189
|
+
if (latest) setSession({
|
|
4190
|
+
...latest,
|
|
4191
|
+
finishing: void 0
|
|
4192
|
+
});
|
|
4193
|
+
}
|
|
4194
|
+
};
|
|
4195
|
+
return {
|
|
4196
|
+
draftRef,
|
|
4197
|
+
finish,
|
|
4198
|
+
flush,
|
|
4199
|
+
getSnapshot: () => sessionRef.current,
|
|
4200
|
+
setDraft: (content) => {
|
|
4201
|
+
const current = sessionRef.current;
|
|
4202
|
+
if (!current || current.finishing === "revert" || content === draftRef.current) return;
|
|
4203
|
+
const wasDirty = draftRef.current !== current.document.content;
|
|
4204
|
+
draftRef.current = content;
|
|
4205
|
+
const isDirty = content !== current.document.content;
|
|
4206
|
+
if (isDirty && !current.finishing && !current.saveError) scheduleSave();
|
|
4207
|
+
else clearTimer();
|
|
4208
|
+
if (wasDirty !== isDirty || current.hasChanges !== (content !== current.originalContent)) setSession({ ...current });
|
|
4209
|
+
},
|
|
4210
|
+
setSession
|
|
4211
|
+
};
|
|
4212
|
+
};
|
|
4213
|
+
const getCodeEditKey = (sourceKey, itemId) => JSON.stringify([sourceKey, itemId]);
|
|
4214
|
+
const createCodeEditingStore = () => {
|
|
4215
|
+
const entries = /* @__PURE__ */ new Map();
|
|
4216
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
4217
|
+
let snapshot = /* @__PURE__ */ new Map();
|
|
4218
|
+
const hasFile = (path) => [...snapshot.values()].some((session) => session.document.path === path);
|
|
4219
|
+
return {
|
|
4220
|
+
clear: () => {
|
|
4221
|
+
for (const entry of entries.values()) entry.setSession(null);
|
|
4222
|
+
},
|
|
4223
|
+
flush: (sourceKey) => Promise.all([...entries.values()].filter((entry) => entry.getSnapshot()?.sourceKey === sourceKey).map((entry) => entry.flush())),
|
|
4224
|
+
get: (sourceKey, itemId) => entries.get(getCodeEditKey(sourceKey, itemId)),
|
|
4225
|
+
getForItem: (item) => item.type === "diff" ? [...entries.values()].find((entry) => {
|
|
4226
|
+
const session = entry.getSnapshot();
|
|
4227
|
+
return session?.item.id === item.id && session.item.fileDiff === item.fileDiff;
|
|
4228
|
+
}) : void 0,
|
|
4229
|
+
getSnapshot: () => snapshot,
|
|
4230
|
+
hasFile,
|
|
4231
|
+
hasUnsavedEdits: () => [...entries.values()].some((entry) => {
|
|
4232
|
+
const session = entry.getSnapshot();
|
|
4233
|
+
return session && (session.saving || entry.draftRef.current !== session.document.content);
|
|
4234
|
+
}),
|
|
4235
|
+
start: (session) => {
|
|
4236
|
+
if (hasFile(session.document.path)) return null;
|
|
4237
|
+
const key = getCodeEditKey(session.sourceKey, session.item.id);
|
|
4238
|
+
const entry = createFileEditingStore(() => {
|
|
4239
|
+
const next = new Map(snapshot);
|
|
4240
|
+
const current = entry.getSnapshot();
|
|
4241
|
+
if (current) next.set(key, current);
|
|
4242
|
+
else {
|
|
4243
|
+
next.delete(key);
|
|
4244
|
+
entries.delete(key);
|
|
4245
|
+
}
|
|
4246
|
+
snapshot = next;
|
|
4247
|
+
for (const listener of listeners) listener();
|
|
4248
|
+
});
|
|
4249
|
+
entries.set(key, entry);
|
|
4250
|
+
entry.draftRef.current = session.document.content;
|
|
4251
|
+
entry.setSession(session);
|
|
4252
|
+
return entry;
|
|
4253
|
+
},
|
|
4254
|
+
subscribe: (listener) => {
|
|
4255
|
+
listeners.add(listener);
|
|
4256
|
+
return () => {
|
|
4257
|
+
listeners.delete(listener);
|
|
4258
|
+
};
|
|
4259
|
+
}
|
|
4260
|
+
};
|
|
4261
|
+
};
|
|
4262
|
+
//#endregion
|
|
4263
|
+
//#region app/hooks/useCodeEditing.ts
|
|
4264
|
+
let editorModule = null;
|
|
4265
|
+
let editorModulePromise = null;
|
|
4266
|
+
const loadEditor = () => editorModulePromise ??= import("@pierre/diffs/edit").then((module) => {
|
|
4267
|
+
editorModule = module;
|
|
4268
|
+
return module;
|
|
4269
|
+
}).catch((error) => {
|
|
4270
|
+
editorModulePromise = null;
|
|
4271
|
+
throw error;
|
|
4272
|
+
});
|
|
4273
|
+
const prepareEditor = (fileDiff) => {
|
|
4274
|
+
const lang = fileDiff.lang ?? getFiletypeFromFileName(fileDiff.name);
|
|
4275
|
+
return Promise.all([loadEditor(), preloadHighlighter(getHighlighterOptions(lang, workerHighlighterOptions))]);
|
|
4276
|
+
};
|
|
4277
|
+
const createCodeEditor = (type, options, key) => {
|
|
4278
|
+
if (!editorModule) throw new Error("The code editor has not loaded.");
|
|
4279
|
+
return new editorModule.Editor(type, options, key);
|
|
4280
|
+
};
|
|
4281
|
+
function useUnsavedCodeEditsGuard(store) {
|
|
4282
|
+
useEffect(() => {
|
|
4283
|
+
const beforeUnload = (event) => {
|
|
4284
|
+
if (store.hasUnsavedEdits()) {
|
|
4285
|
+
event.preventDefault();
|
|
4286
|
+
event.returnValue = "";
|
|
4287
|
+
}
|
|
4288
|
+
};
|
|
4289
|
+
window.addEventListener("beforeunload", beforeUnload);
|
|
4290
|
+
return () => window.removeEventListener("beforeunload", beforeUnload);
|
|
4291
|
+
}, [store]);
|
|
4292
|
+
}
|
|
4293
|
+
const errorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
4294
|
+
const getFirstVisibleChangedLine = (root, viewport) => {
|
|
4295
|
+
const viewportRect = viewport.getBoundingClientRect();
|
|
4296
|
+
const header = root.querySelector("[data-diffs-header][data-sticky]");
|
|
4297
|
+
const top = Math.max(viewportRect.top, header?.getBoundingClientRect().bottom ?? viewportRect.top) + 11;
|
|
4298
|
+
for (const row of root.querySelectorAll("[data-content] > [data-line-type=\"change-addition\"]")) {
|
|
4299
|
+
const rect = row.getBoundingClientRect();
|
|
4300
|
+
if (rect.height > 0 && rect.top >= top && rect.top < viewportRect.bottom) {
|
|
4301
|
+
const line = Number(row.dataset.line);
|
|
4302
|
+
if (Number.isInteger(line) && line > 0) return line;
|
|
4303
|
+
}
|
|
4304
|
+
}
|
|
4305
|
+
};
|
|
4306
|
+
function useCodeEditing({ codeViewRef, items, keymap = defaultKeymap, onLoadSectionContents, onRefresh, selectedItemId, sourceKey, store: sharedStore }) {
|
|
4307
|
+
const [localStore] = useState(createCodeEditingStore);
|
|
4308
|
+
const store = sharedStore ?? localStore;
|
|
4309
|
+
const sessions = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
4310
|
+
useUnsavedCodeEditsGuard(store);
|
|
4311
|
+
const busyRef = useRef(/* @__PURE__ */ new Set());
|
|
4312
|
+
const [busyItemKeys, setBusyItemKeys] = useState(() => /* @__PURE__ */ new Set());
|
|
4313
|
+
const [errors, setErrors] = useState(() => /* @__PURE__ */ new Map());
|
|
4314
|
+
const setBusy = useCallback((key, busy) => {
|
|
4315
|
+
if (busy) busyRef.current.add(key);
|
|
4316
|
+
else busyRef.current.delete(key);
|
|
4317
|
+
setBusyItemKeys(new Set(busyRef.current));
|
|
4318
|
+
}, []);
|
|
4319
|
+
const setError = useCallback((key, message) => {
|
|
4320
|
+
setErrors((current) => {
|
|
4321
|
+
const next = new Map(current);
|
|
4322
|
+
if (message) next.set(key, message);
|
|
4323
|
+
else next.delete(key);
|
|
4324
|
+
return next;
|
|
4325
|
+
});
|
|
4326
|
+
}, []);
|
|
4327
|
+
const currentSessions = useMemo(() => new Map([...sessions.values()].filter((session) => session.sourceKey === sourceKey).map((session) => [session.item.id, session])), [sessions, sourceKey]);
|
|
4328
|
+
const editorOptions = useMemo(() => {
|
|
4329
|
+
const focusedEditors = /* @__PURE__ */ new WeakSet();
|
|
4330
|
+
return { onAttach: (editor, fileInstance) => {
|
|
4331
|
+
if (!focusedEditors.has(editor)) {
|
|
4332
|
+
focusedEditors.add(editor);
|
|
4333
|
+
const viewer = codeViewRef.current?.getInstance();
|
|
4334
|
+
const root = viewer?.getRenderedItems().find(({ instance }) => instance === fileInstance)?.element.shadowRoot;
|
|
4335
|
+
const viewport = viewer?.getContainerElement();
|
|
4336
|
+
editor.focus({
|
|
4337
|
+
character: 0,
|
|
4338
|
+
lineNumber: (root && viewport ? getFirstVisibleChangedLine(root, viewport) : void 0) ?? "first-visible",
|
|
4339
|
+
offset: 11,
|
|
4340
|
+
preventScroll: true
|
|
4341
|
+
});
|
|
4342
|
+
const position = editor.getViewState().selections?.[0]?.start;
|
|
4343
|
+
const indentation = (position && editor.getEditState()?.document.getLineText(position.line))?.match(/^\s*/)?.[0].length ?? 0;
|
|
4344
|
+
if (position && indentation > 0) editor.focus({
|
|
4345
|
+
character: indentation,
|
|
4346
|
+
lineNumber: position.line + 1,
|
|
4347
|
+
preventScroll: true
|
|
4348
|
+
});
|
|
4349
|
+
}
|
|
4350
|
+
} };
|
|
4351
|
+
}, [codeViewRef]);
|
|
4352
|
+
const loadEditContents = useCallback((item, metadata) => {
|
|
4353
|
+
if (!item.fileDiff.isPartial) return Promise.resolve({
|
|
4354
|
+
newFile: {
|
|
4355
|
+
contents: item.fileDiff.additionLines.join(""),
|
|
4356
|
+
name: item.fileDiff.name
|
|
4357
|
+
},
|
|
4358
|
+
oldFile: null
|
|
4359
|
+
});
|
|
4360
|
+
return loadSectionContents(metadata.file, metadata.section, onLoadSectionContents);
|
|
4361
|
+
}, [onLoadSectionContents]);
|
|
4362
|
+
const prepareEdit = useCallback(async (item, metadata) => {
|
|
4363
|
+
if (item.type === "diff" && onLoadSectionContents) {
|
|
4364
|
+
const [, loaded] = await Promise.all([prepareEditor(item.fileDiff), loadEditContents(item, metadata)]);
|
|
4365
|
+
const rendered = codeViewRef.current?.getInstance()?.getRenderedItems().find(({ id }) => id === item.id);
|
|
4366
|
+
if (rendered?.type === "diff") {
|
|
4367
|
+
const fileDiff = item.fileDiff.isPartial ? hydratePartialDiff("clone", item.fileDiff, loaded) : item.fileDiff;
|
|
4368
|
+
await rendered.instance.primeHighlightCache(fileDiff);
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4371
|
+
}, [
|
|
4372
|
+
codeViewRef,
|
|
4373
|
+
loadEditContents,
|
|
4374
|
+
onLoadSectionContents
|
|
4375
|
+
]);
|
|
4376
|
+
const startEdit = useCallback(async (item, metadata) => {
|
|
4377
|
+
const itemKey = getCodeEditKey(sourceKey, item.id);
|
|
4378
|
+
if (item.type !== "diff" || !onLoadSectionContents || store.hasFile(metadata.file.path) || busyRef.current.has(itemKey)) return;
|
|
4379
|
+
setBusy(itemKey, true);
|
|
4380
|
+
setError(itemKey);
|
|
4381
|
+
try {
|
|
4382
|
+
const [, loaded, document] = await Promise.all([
|
|
4383
|
+
prepareEditor(item.fileDiff),
|
|
4384
|
+
loadEditContents(item, metadata),
|
|
4385
|
+
window.codiff.getRepositoryFile(metadata.file.path)
|
|
4386
|
+
]);
|
|
4387
|
+
if (!loaded.newFile || loaded.newFile.contents !== document.content) throw new Error("The file changed on disk. Refresh the diff before editing.");
|
|
4388
|
+
const key = crypto.randomUUID();
|
|
4389
|
+
const fileDiff = item.fileDiff.isPartial ? hydratePartialDiff("clone", item.fileDiff, loaded) : cloneFileDiffMetadata(item.fileDiff);
|
|
4390
|
+
fileDiff.cacheKey ??= `code-edit:${key}`;
|
|
4391
|
+
const rendered = codeViewRef.current?.getInstance()?.getRenderedItems().find(({ id }) => id === item.id);
|
|
4392
|
+
if (rendered?.type === "diff") await rendered.instance.primeHighlightCache(fileDiff);
|
|
4393
|
+
const nextSession = {
|
|
4394
|
+
dirty: false,
|
|
4395
|
+
document,
|
|
4396
|
+
hasChanges: false,
|
|
4397
|
+
item: {
|
|
4398
|
+
...item,
|
|
4399
|
+
collapsed: false,
|
|
4400
|
+
edit: true,
|
|
4401
|
+
fileDiff,
|
|
4402
|
+
version: getItemVersion(`code-edit:${key}`)
|
|
4403
|
+
},
|
|
4404
|
+
metadata,
|
|
4405
|
+
originalContent: document.content,
|
|
4406
|
+
sourceKey
|
|
4407
|
+
};
|
|
4408
|
+
if (store.start(nextSession)) codeViewRef.current?.clearSelectedLines();
|
|
4409
|
+
} catch (error) {
|
|
4410
|
+
setError(itemKey, errorMessage(error));
|
|
4411
|
+
} finally {
|
|
4412
|
+
setBusy(itemKey, false);
|
|
4413
|
+
}
|
|
4414
|
+
}, [
|
|
4415
|
+
codeViewRef,
|
|
4416
|
+
loadEditContents,
|
|
4417
|
+
onLoadSectionContents,
|
|
4418
|
+
setBusy,
|
|
4419
|
+
setError,
|
|
4420
|
+
sourceKey,
|
|
4421
|
+
store
|
|
4422
|
+
]);
|
|
4423
|
+
const finishEdit = useCallback(async (itemId, revert) => {
|
|
4424
|
+
const itemKey = getCodeEditKey(sourceKey, itemId);
|
|
4425
|
+
const entry = store.get(sourceKey, itemId);
|
|
4426
|
+
const current = entry?.getSnapshot();
|
|
4427
|
+
if (!entry || !current || busyRef.current.has(itemKey)) return;
|
|
4428
|
+
setBusy(itemKey, true);
|
|
4429
|
+
setError(itemKey);
|
|
4430
|
+
const needsRefresh = current.hasChanges || current.document.content !== current.originalContent || current.saving;
|
|
4431
|
+
try {
|
|
4432
|
+
await entry.finish(revert, async () => {
|
|
4433
|
+
const latest = entry.getSnapshot();
|
|
4434
|
+
if ((needsRefresh || latest?.hasChanges) && onRefresh && !await onRefresh(current.metadata.file, current.metadata.section)) throw new Error("File saved. Refresh the review to see the updated diff.");
|
|
4435
|
+
});
|
|
4436
|
+
} catch (error) {
|
|
4437
|
+
setError(itemKey, errorMessage(error));
|
|
4438
|
+
} finally {
|
|
4439
|
+
setBusy(itemKey, false);
|
|
4440
|
+
}
|
|
4441
|
+
}, [
|
|
4442
|
+
onRefresh,
|
|
4443
|
+
setBusy,
|
|
4444
|
+
setError,
|
|
4445
|
+
sourceKey,
|
|
4446
|
+
store
|
|
4447
|
+
]);
|
|
4448
|
+
const doneEdit = useCallback((itemId) => finishEdit(itemId, false), [finishEdit]);
|
|
4449
|
+
const revertEdit = useCallback((itemId) => finishEdit(itemId, true), [finishEdit]);
|
|
4450
|
+
const onItemEditChange = useCallback((event, item) => {
|
|
4451
|
+
store.getForItem(item)?.setDraft(event.file.contents);
|
|
4452
|
+
}, [store]);
|
|
4453
|
+
const onItemEditComplete = useCallback((_event, item, nextItem) => {
|
|
4454
|
+
const entry = store.getForItem(item);
|
|
4455
|
+
const current = entry?.getSnapshot();
|
|
4456
|
+
if (!entry || !current || nextItem.type !== "diff") return "reject";
|
|
4457
|
+
nextItem.fileDiff.cacheKey = `code-edit:${crypto.randomUUID()}`;
|
|
4458
|
+
entry.setSession({
|
|
4459
|
+
...current,
|
|
4460
|
+
item: {
|
|
4461
|
+
...nextItem,
|
|
4462
|
+
edit: true
|
|
4463
|
+
}
|
|
4464
|
+
});
|
|
4465
|
+
return "accept";
|
|
4466
|
+
}, [store]);
|
|
4467
|
+
const editedItems = useMemo(() => {
|
|
4468
|
+
if (currentSessions.size === 0) return items;
|
|
4469
|
+
const found = /* @__PURE__ */ new Set();
|
|
4470
|
+
const next = items.map((item) => {
|
|
4471
|
+
const session = currentSessions.get(item.id);
|
|
4472
|
+
if (!session) return item;
|
|
4473
|
+
found.add(item.id);
|
|
4474
|
+
return {
|
|
4475
|
+
...session.item,
|
|
4476
|
+
collapsed: item.collapsed
|
|
4477
|
+
};
|
|
4478
|
+
});
|
|
4479
|
+
for (const session of currentSessions.values()) if (!found.has(session.item.id)) next.push(session.item);
|
|
4480
|
+
return next;
|
|
4481
|
+
}, [currentSessions, items]);
|
|
4482
|
+
useEffect(() => {
|
|
4483
|
+
if (currentSessions.size === 0) return;
|
|
4484
|
+
const keyDown = (event) => {
|
|
4485
|
+
if (event.defaultPrevented || event.repeat || event.isComposing || !matchesShortcut(event, keymap, "doneEditing")) return;
|
|
4486
|
+
const path = event.composedPath();
|
|
4487
|
+
if (isNativeInputTarget(event.target) && !path.some((node) => node.matches?.("[data-content][contenteditable=\"true\"]"))) return;
|
|
4488
|
+
const itemId = codeViewRef.current?.getInstance()?.getRenderedItems().find(({ element, id }) => currentSessions.has(id) && path.includes(element))?.id ?? selectedItemId ?? (currentSessions.size === 1 ? currentSessions.keys().next().value : void 0);
|
|
4489
|
+
if (itemId == null || !currentSessions.has(itemId)) return;
|
|
4490
|
+
event.preventDefault();
|
|
4491
|
+
event.stopPropagation();
|
|
4492
|
+
doneEdit(itemId);
|
|
4493
|
+
};
|
|
4494
|
+
window.addEventListener("keydown", keyDown, true);
|
|
4495
|
+
return () => window.removeEventListener("keydown", keyDown, true);
|
|
4496
|
+
}, [
|
|
4497
|
+
codeViewRef,
|
|
4498
|
+
currentSessions,
|
|
4499
|
+
doneEdit,
|
|
4500
|
+
keymap,
|
|
4501
|
+
selectedItemId
|
|
4502
|
+
]);
|
|
4503
|
+
return {
|
|
4504
|
+
canStartEdit: (itemId, path) => !busyItemKeys.has(getCodeEditKey(sourceKey, itemId)) && ![...sessions.values()].some((session) => session.document.path === path),
|
|
4505
|
+
doneEdit,
|
|
4506
|
+
editorOptions,
|
|
4507
|
+
getError: (itemId) => currentSessions.get(itemId)?.saveError ?? errors.get(getCodeEditKey(sourceKey, itemId)),
|
|
4508
|
+
isBusy: (itemId) => busyItemKeys.has(getCodeEditKey(sourceKey, itemId)) || currentSessions.get(itemId)?.finishing != null,
|
|
4509
|
+
items: editedItems,
|
|
4510
|
+
onItemEditChange,
|
|
4511
|
+
onItemEditComplete,
|
|
4512
|
+
prepareEdit,
|
|
4513
|
+
revertEdit,
|
|
4514
|
+
sessions: currentSessions,
|
|
4515
|
+
startEdit
|
|
4516
|
+
};
|
|
4517
|
+
}
|
|
4518
|
+
//#endregion
|
|
4055
4519
|
//#region app/hooks/useCodeViewAnnotations.ts
|
|
4056
4520
|
function useCodeViewAnnotations() {
|
|
4057
4521
|
const [getAnnotations] = useState(() => {
|
|
@@ -4484,14 +4948,17 @@ const resolveWalkthroughHunkFile = (hunk, files) => {
|
|
|
4484
4948
|
};
|
|
4485
4949
|
};
|
|
4486
4950
|
const walkthroughHunkRunKey = (item, hunks) => `${item.id}:${hunks.map((hunk) => hunk.id).join(",")}`;
|
|
4487
|
-
/** Resolve
|
|
4951
|
+
/** Resolve authored hunk order, coalescing only forward runs within each file section. */
|
|
4488
4952
|
const resolveWalkthroughHunkRuns = (item, files) => {
|
|
4489
4953
|
const runs = [];
|
|
4490
4954
|
for (const hunk of item.hunks) {
|
|
4491
4955
|
const resolved = resolveWalkthroughHunkFile(hunk, files);
|
|
4492
4956
|
if (!resolved) continue;
|
|
4493
4957
|
const previous = runs.at(-1);
|
|
4494
|
-
|
|
4958
|
+
const previousHunk = previous?.hunks.at(-1);
|
|
4959
|
+
const previousOrdinal = previousHunk ? getHunkOrdinal(resolved.section.id, previousHunk.id) : null;
|
|
4960
|
+
const ordinal = getHunkOrdinal(resolved.section.id, hunk.id);
|
|
4961
|
+
if (previous && previous.resolved.file.path === resolved.file.path && previous.resolved.section.id === resolved.section.id && previousOrdinal != null && ordinal != null && previousOrdinal < ordinal) {
|
|
4495
4962
|
const hunks = [...previous.hunks, hunk];
|
|
4496
4963
|
runs[runs.length - 1] = {
|
|
4497
4964
|
...previous,
|
|
@@ -5095,124 +5562,165 @@ function CopyFilePathButton({ path }) {
|
|
|
5095
5562
|
})
|
|
5096
5563
|
});
|
|
5097
5564
|
}
|
|
5098
|
-
function
|
|
5565
|
+
function CodeEditButton({ disabled, isBusy, item, meta, onEdit, onPrepare }) {
|
|
5566
|
+
useEffect(() => {
|
|
5567
|
+
onPrepare(item, meta).catch(() => {});
|
|
5568
|
+
}, [
|
|
5569
|
+
item,
|
|
5570
|
+
meta,
|
|
5571
|
+
onPrepare
|
|
5572
|
+
]);
|
|
5573
|
+
return /* @__PURE__ */ jsxs(Button, {
|
|
5574
|
+
"aria-label": `Edit ${meta.file.path}`,
|
|
5575
|
+
disabled: disabled || isBusy,
|
|
5576
|
+
onClick: onEdit,
|
|
5577
|
+
title: "Edit working-tree file",
|
|
5578
|
+
type: "button",
|
|
5579
|
+
children: [/* @__PURE__ */ jsx(PencilSimpleIcon, {
|
|
5580
|
+
"aria-hidden": true,
|
|
5581
|
+
size: 14,
|
|
5582
|
+
weight: "bold"
|
|
5583
|
+
}), "Edit"]
|
|
5584
|
+
});
|
|
5585
|
+
}
|
|
5586
|
+
function CodeViewHeader({ allowViewedToggle, canCreateFileComment, editActions, isEditing = false, isSectionLoading, meta, onCreateFileComment, onLoadSection, onOpenFile, onToggleCollapsed, onToggleMarkdownPreview, onToggleViewed, readOnly }) {
|
|
5099
5587
|
const { canRenderMarkdown, file, isCollapsed, isMarkdownPreview, isSelected, isViewed, lineCount, reviewIdentity, section, sectionCount, walkthroughNote } = meta;
|
|
5100
5588
|
const canOpenFile = file.status !== "deleted";
|
|
5101
5589
|
const canLoadSection = shouldLoadDiffSectionContents(section);
|
|
5590
|
+
const directoryEnd = file.path.lastIndexOf("/") + 1;
|
|
5102
5591
|
return /* @__PURE__ */ jsxs("div", {
|
|
5103
5592
|
className: `codiff-file-header${walkthroughNote ? " with-note" : ""}${isCollapsed ? " collapsed" : ""}${isSelected ? " selected" : ""}${isViewed ? " viewed" : ""}`,
|
|
5104
|
-
children: [
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5593
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
5594
|
+
"aria-expanded": !isCollapsed,
|
|
5595
|
+
"aria-label": isCollapsed ? "Expand file" : "Collapse file",
|
|
5596
|
+
className: "codiff-header-toggle",
|
|
5597
|
+
onClick: () => onToggleCollapsed(file, isCollapsed, reviewIdentity.key),
|
|
5598
|
+
onKeyDown: (event) => {
|
|
5599
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
5600
|
+
event.preventDefault();
|
|
5601
|
+
onToggleCollapsed(file, isCollapsed, reviewIdentity.key);
|
|
5602
|
+
}
|
|
5603
|
+
},
|
|
5604
|
+
role: "button",
|
|
5605
|
+
tabIndex: 0,
|
|
5606
|
+
title: isCollapsed ? "Expand" : "Collapse",
|
|
5607
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
5608
|
+
className: "codiff-chevron-box",
|
|
5609
|
+
children: /* @__PURE__ */ jsx(CaretDownIcon, {
|
|
5610
|
+
"aria-hidden": true,
|
|
5611
|
+
className: isCollapsed ? "codiff-chevron collapsed" : "codiff-chevron",
|
|
5612
|
+
size: 16,
|
|
5613
|
+
weight: "bold"
|
|
5614
|
+
})
|
|
5615
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
5616
|
+
className: "codiff-file-heading",
|
|
5119
5617
|
children: [
|
|
5120
|
-
/* @__PURE__ */ jsx("span", {
|
|
5121
|
-
className: "codiff-chevron-box",
|
|
5122
|
-
children: /* @__PURE__ */ jsx(CaretDownIcon, {
|
|
5123
|
-
"aria-hidden": true,
|
|
5124
|
-
className: isCollapsed ? "codiff-chevron collapsed" : "codiff-chevron",
|
|
5125
|
-
size: 16,
|
|
5126
|
-
weight: "bold"
|
|
5127
|
-
})
|
|
5128
|
-
}),
|
|
5129
5618
|
/* @__PURE__ */ jsxs("span", {
|
|
5130
|
-
className: "codiff-file-
|
|
5619
|
+
className: "codiff-file-path-row",
|
|
5131
5620
|
children: [
|
|
5132
5621
|
/* @__PURE__ */ jsxs("span", {
|
|
5133
|
-
className: "codiff-file-path
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5622
|
+
className: "codiff-file-path",
|
|
5623
|
+
title: file.path,
|
|
5624
|
+
children: [directoryEnd > 0 ? /* @__PURE__ */ jsx("span", {
|
|
5625
|
+
className: "codiff-file-directory",
|
|
5626
|
+
children: file.path.slice(0, directoryEnd)
|
|
5627
|
+
}) : null, /* @__PURE__ */ jsx("span", {
|
|
5628
|
+
className: "codiff-file-name",
|
|
5629
|
+
children: file.path.slice(directoryEnd)
|
|
5630
|
+
})]
|
|
5138
5631
|
}),
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5632
|
+
/* @__PURE__ */ jsx(CopyFilePathButton, { path: file.path }),
|
|
5633
|
+
!readOnly && onOpenFile ? /* @__PURE__ */ jsx("button", {
|
|
5634
|
+
"aria-label": `Open ${file.path} in editor`,
|
|
5635
|
+
className: "codiff-open-file-button",
|
|
5636
|
+
disabled: !canOpenFile,
|
|
5637
|
+
onClick: (event) => {
|
|
5638
|
+
event.stopPropagation();
|
|
5639
|
+
onOpenFile(file);
|
|
5640
|
+
},
|
|
5641
|
+
onKeyDown: (event) => {
|
|
5642
|
+
if (event.key === "Enter" || event.key === " ") event.stopPropagation();
|
|
5643
|
+
},
|
|
5644
|
+
title: canOpenFile ? "Open file in editor" : "Deleted files cannot be opened",
|
|
5645
|
+
type: "button",
|
|
5646
|
+
children: /* @__PURE__ */ jsx(ArrowSquareOutIcon, {
|
|
5647
|
+
"aria-hidden": true,
|
|
5648
|
+
size: 16,
|
|
5649
|
+
weight: "bold"
|
|
5650
|
+
})
|
|
5146
5651
|
}) : null
|
|
5147
5652
|
]
|
|
5148
5653
|
}),
|
|
5149
|
-
|
|
5150
|
-
className:
|
|
5151
|
-
children:
|
|
5654
|
+
file.oldPath ? /* @__PURE__ */ jsx("span", {
|
|
5655
|
+
className: "codiff-file-old-path",
|
|
5656
|
+
children: file.oldPath
|
|
5152
5657
|
}) : null,
|
|
5153
|
-
|
|
5154
|
-
className: "codiff-
|
|
5155
|
-
|
|
5156
|
-
children: "Generated"
|
|
5658
|
+
walkthroughNote ? /* @__PURE__ */ jsx("span", {
|
|
5659
|
+
className: "codiff-file-note",
|
|
5660
|
+
children: walkthroughNote.reason
|
|
5157
5661
|
}) : null
|
|
5158
5662
|
]
|
|
5159
|
-
})
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
"
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
type: "button",
|
|
5183
|
-
children: isMarkdownPreview ? "View as Diff" : "View as Markdown"
|
|
5184
|
-
}) : null,
|
|
5185
|
-
canLoadSection && !readOnly ? /* @__PURE__ */ jsx("button", {
|
|
5186
|
-
className: "codiff-load-button",
|
|
5187
|
-
disabled: isSectionLoading,
|
|
5188
|
-
onClick: () => onLoadSection(file, section),
|
|
5189
|
-
title: isSectionLoading ? "Loading file contents" : "Load file contents",
|
|
5190
|
-
type: "button",
|
|
5191
|
-
children: isSectionLoading ? "Loading..." : "Load"
|
|
5192
|
-
}) : null,
|
|
5193
|
-
!readOnly && onOpenFile ? /* @__PURE__ */ jsx(Button, {
|
|
5194
|
-
disabled: !canOpenFile,
|
|
5195
|
-
onClick: () => onOpenFile(file),
|
|
5196
|
-
title: canOpenFile ? "Open file in editor" : "Deleted files cannot be opened",
|
|
5197
|
-
type: "button",
|
|
5198
|
-
children: "Open"
|
|
5199
|
-
}) : null,
|
|
5200
|
-
!readOnly || allowViewedToggle ? /* @__PURE__ */ jsxs("button", {
|
|
5201
|
-
"aria-pressed": isViewed,
|
|
5202
|
-
className: `codiff-viewed-button${isViewed ? " active" : ""}`,
|
|
5203
|
-
onClick: () => onToggleViewed(file, isViewed, reviewIdentity),
|
|
5204
|
-
type: "button",
|
|
5205
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
5206
|
-
"aria-hidden": true,
|
|
5207
|
-
className: "codiff-viewed-checkbox",
|
|
5208
|
-
children: isViewed ? /* @__PURE__ */ jsx(CheckIcon, {
|
|
5209
|
-
className: "codiff-viewed-check",
|
|
5210
|
-
size: 10,
|
|
5663
|
+
})]
|
|
5664
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
5665
|
+
className: "codiff-file-details",
|
|
5666
|
+
children: [
|
|
5667
|
+
/* @__PURE__ */ jsx(DiffLineCountBadge, { lineCount }),
|
|
5668
|
+
sectionCount > 1 ? /* @__PURE__ */ jsx("span", {
|
|
5669
|
+
className: `codiff-section-badge ${section.kind}`,
|
|
5670
|
+
children: sectionLabel[section.kind]
|
|
5671
|
+
}) : null,
|
|
5672
|
+
isGeneratedWalkthroughFile(file) ? /* @__PURE__ */ jsx("span", {
|
|
5673
|
+
className: "codiff-generated-badge",
|
|
5674
|
+
title: "Generated file",
|
|
5675
|
+
children: "Generated"
|
|
5676
|
+
}) : null,
|
|
5677
|
+
canCreateFileComment ? /* @__PURE__ */ jsxs(Button, {
|
|
5678
|
+
className: "codiff-file-comment-button",
|
|
5679
|
+
onClick: onCreateFileComment,
|
|
5680
|
+
title: "Comment on file",
|
|
5681
|
+
type: "button",
|
|
5682
|
+
children: [/* @__PURE__ */ jsx(ChatCircleIcon, {
|
|
5683
|
+
"aria-hidden": true,
|
|
5684
|
+
className: "codiff-file-comment-icon",
|
|
5685
|
+
size: 14,
|
|
5211
5686
|
weight: "bold"
|
|
5212
|
-
})
|
|
5213
|
-
}),
|
|
5214
|
-
|
|
5215
|
-
|
|
5687
|
+
}), "Comment"]
|
|
5688
|
+
}) : null,
|
|
5689
|
+
canRenderMarkdown ? /* @__PURE__ */ jsx(Button, {
|
|
5690
|
+
"aria-pressed": isMarkdownPreview,
|
|
5691
|
+
className: `codiff-markdown-button${isMarkdownPreview ? " active" : ""}`,
|
|
5692
|
+
disabled: isEditing,
|
|
5693
|
+
onClick: () => onToggleMarkdownPreview(file, section),
|
|
5694
|
+
title: isMarkdownPreview ? "View as Diff" : "View as Markdown",
|
|
5695
|
+
type: "button",
|
|
5696
|
+
children: isMarkdownPreview ? "View as Diff" : "View as Markdown"
|
|
5697
|
+
}) : null,
|
|
5698
|
+
editActions,
|
|
5699
|
+
canLoadSection && !readOnly ? /* @__PURE__ */ jsx("button", {
|
|
5700
|
+
className: "codiff-load-button",
|
|
5701
|
+
disabled: isSectionLoading,
|
|
5702
|
+
onClick: () => onLoadSection(file, section),
|
|
5703
|
+
title: isSectionLoading ? "Loading file contents" : "Load file contents",
|
|
5704
|
+
type: "button",
|
|
5705
|
+
children: isSectionLoading ? "Loading..." : "Load"
|
|
5706
|
+
}) : null,
|
|
5707
|
+
!readOnly || allowViewedToggle ? /* @__PURE__ */ jsxs("button", {
|
|
5708
|
+
"aria-pressed": isViewed,
|
|
5709
|
+
className: `codiff-viewed-button${isViewed ? " active" : ""}`,
|
|
5710
|
+
onClick: () => onToggleViewed(file, isViewed, reviewIdentity),
|
|
5711
|
+
type: "button",
|
|
5712
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
5713
|
+
"aria-hidden": true,
|
|
5714
|
+
className: "codiff-viewed-checkbox",
|
|
5715
|
+
children: isViewed ? /* @__PURE__ */ jsx(CheckIcon, {
|
|
5716
|
+
className: "codiff-viewed-check",
|
|
5717
|
+
size: 10,
|
|
5718
|
+
weight: "bold"
|
|
5719
|
+
}) : null
|
|
5720
|
+
}), "Viewed"]
|
|
5721
|
+
}) : null
|
|
5722
|
+
]
|
|
5723
|
+
})]
|
|
5216
5724
|
});
|
|
5217
5725
|
}
|
|
5218
5726
|
const getReviewAuthorDisplayName = (author) => author.name || author.login || "Git user";
|
|
@@ -6701,8 +7209,27 @@ const getHunkSelectionRange = (hunk) => {
|
|
|
6701
7209
|
} : null;
|
|
6702
7210
|
};
|
|
6703
7211
|
const isSameSelection = (a, b) => a.start === b.start && a.end === b.end && a.side === b.side;
|
|
6704
|
-
function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedToggle = false, blocks, bottomInset = codeViewLayout.paddingBottom, codeQualityFindings = [], collapsed, comments, commitMetadata, diffLineHeight = 20, diffStyle, disableWorkerPool = false, expandedReviewKeys = emptyExpandedReviewKeys, files, focusCommentId, focusCommentRequest, forceExpandedPaths, gitIdentity, hunkNavigation, initialMarkdownPreviewSectionIds = emptyMarkdownPreviewSectionIds, isReadOnly = false, itemVersionByKey, keymap, loadingSectionIds, onActiveBlockChange, onAskCodex, onCommentDraftChange, onCreateComment, onDeleteComment, onFindDefinitions, onLoadImageContent, onLoadSection, onLoadSectionContents, onOpenDefinition, 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 }) {
|
|
7212
|
+
function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedToggle = false, blocks, bottomInset = codeViewLayout.paddingBottom, codeEditingStore, codeQualityFindings = [], collapsed, comments, commitMetadata, diffLineHeight = 20, diffStyle, disableWorkerPool = false, expandedReviewKeys = emptyExpandedReviewKeys, files, focusCommentId, focusCommentRequest, forceExpandedPaths, gitIdentity, hunkNavigation, initialMarkdownPreviewSectionIds = emptyMarkdownPreviewSectionIds, isReadOnly = false, itemVersionByKey, keymap, loadingSectionIds, onActiveBlockChange, onAskCodex, onCommentDraftChange, onCreateComment, onDeleteComment, onFindDefinitions, onLoadImageContent, onLoadSection, onLoadSectionContents, onOpenDefinition, 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 }) {
|
|
6705
7213
|
const codeViewRef = useRef(null);
|
|
7214
|
+
const [codeViewElement, setCodeViewElement] = useState(null);
|
|
7215
|
+
const [compactHeaders, setCompactHeaders] = useState(false);
|
|
7216
|
+
useLayoutEffect(() => {
|
|
7217
|
+
if (!codeViewElement) return;
|
|
7218
|
+
const measure = () => {
|
|
7219
|
+
const width = codeViewElement.getBoundingClientRect().width;
|
|
7220
|
+
if (width > 0) setCompactHeaders(width <= 760);
|
|
7221
|
+
};
|
|
7222
|
+
measure();
|
|
7223
|
+
const observer = new ResizeObserver(measure);
|
|
7224
|
+
observer.observe(codeViewElement);
|
|
7225
|
+
return () => observer.disconnect();
|
|
7226
|
+
}, [codeViewElement]);
|
|
7227
|
+
const headerHeight = compactHeaders ? 90 : codeViewItemMetrics.diffHeaderHeight;
|
|
7228
|
+
const itemMetrics = useMemo(() => ({
|
|
7229
|
+
...codeViewItemMetrics,
|
|
7230
|
+
diffHeaderHeight: headerHeight
|
|
7231
|
+
}), [headerHeight]);
|
|
7232
|
+
const codeViewStyle = { "--codiff-file-header-height": `${headerHeight}px` };
|
|
6706
7233
|
const getPlaceholderFile = useCodeViewPlaceholderFile();
|
|
6707
7234
|
const getAnnotations = useCodeViewAnnotations();
|
|
6708
7235
|
const [markdownPreviewHeights] = useState(() => /* @__PURE__ */ new Map());
|
|
@@ -7013,6 +7540,91 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7013
7540
|
shouldShowCommitMessage,
|
|
7014
7541
|
sourceDescriptionItemId
|
|
7015
7542
|
]);
|
|
7543
|
+
const highlightedCodeItems = codeViewItems.filter((item) => itemMetadata.get(item.id)?.isSelected);
|
|
7544
|
+
const selectedCodeItemId = highlightedCodeItems.find((item) => item.id === selectedLines?.id && itemMetadata.get(item.id)?.section.kind === "unstaged")?.id ?? highlightedCodeItems.find((item) => itemMetadata.get(item.id)?.section.kind === "unstaged")?.id ?? highlightedCodeItems[0]?.id;
|
|
7545
|
+
const codeEditing = useCodeEditing({
|
|
7546
|
+
codeViewRef,
|
|
7547
|
+
items: codeViewItems,
|
|
7548
|
+
keymap,
|
|
7549
|
+
onLoadSectionContents,
|
|
7550
|
+
onRefresh: onRefreshMarkdown,
|
|
7551
|
+
selectedItemId: selectedCodeItemId,
|
|
7552
|
+
sourceKey,
|
|
7553
|
+
store: codeEditingStore
|
|
7554
|
+
});
|
|
7555
|
+
const canEditCodeItem = useCallback((item, meta) => meta != null && item.type === "diff" && !isReadOnly && !meta.section.binary && (meta.section.loadState == null || meta.section.loadState === "ready" || meta.section.loadState === "deferred") && !meta.isMarkdownPreview && meta.section.kind === "unstaged" && isEditableWorkingTreeSection(source.type, meta.file, meta.section) && onLoadSectionContents != null && window.codiff?.getRepositoryFile != null, [
|
|
7556
|
+
isReadOnly,
|
|
7557
|
+
onLoadSectionContents,
|
|
7558
|
+
source.type
|
|
7559
|
+
]);
|
|
7560
|
+
const startCodeEdit = useCallback((item, meta) => {
|
|
7561
|
+
if (meta.isCollapsed) onToggleCollapsed(meta.file, true, meta.reviewIdentity.key);
|
|
7562
|
+
codeEditing.startEdit(item, meta);
|
|
7563
|
+
}, [codeEditing, onToggleCollapsed]);
|
|
7564
|
+
const editAfterExpandRef = useRef(null);
|
|
7565
|
+
useEffect(() => {
|
|
7566
|
+
const pending = editAfterExpandRef.current;
|
|
7567
|
+
if (!pending) return;
|
|
7568
|
+
if (pending.sourceKey !== sourceKey || !highlightedCodeItems.some((item) => itemMetadata.get(item.id)?.file.path === pending.path)) {
|
|
7569
|
+
editAfterExpandRef.current = null;
|
|
7570
|
+
return;
|
|
7571
|
+
}
|
|
7572
|
+
const item = highlightedCodeItems.find((candidate) => {
|
|
7573
|
+
const meta = itemMetadata.get(candidate.id);
|
|
7574
|
+
return meta?.file.path === pending.path && canEditCodeItem(candidate, meta);
|
|
7575
|
+
});
|
|
7576
|
+
const meta = item && itemMetadata.get(item.id);
|
|
7577
|
+
if (item && meta) {
|
|
7578
|
+
editAfterExpandRef.current = null;
|
|
7579
|
+
startCodeEdit(item, meta);
|
|
7580
|
+
}
|
|
7581
|
+
}, [
|
|
7582
|
+
canEditCodeItem,
|
|
7583
|
+
highlightedCodeItems,
|
|
7584
|
+
itemMetadata,
|
|
7585
|
+
sourceKey,
|
|
7586
|
+
startCodeEdit
|
|
7587
|
+
]);
|
|
7588
|
+
useEffect(() => {
|
|
7589
|
+
const handleKeyDown = (event) => {
|
|
7590
|
+
if (event.defaultPrevented || event.repeat || event.isComposing || isNativeInputTarget(event.target) || !matchesShortcut(event, keymap, "editFile")) return;
|
|
7591
|
+
const item = codeViewItems.find((candidate) => candidate.id === selectedCodeItemId);
|
|
7592
|
+
const meta = item && itemMetadata.get(item.id);
|
|
7593
|
+
if (!item || !meta || !codeEditing.canStartEdit(item.id, meta.file.path)) return;
|
|
7594
|
+
if (!canEditCodeItem(item, meta)) {
|
|
7595
|
+
if (!meta.isCollapsed || !getVisibleDiffSections(meta.file, showWhitespace).some(({ fileDiff, section }) => canEditCodeItem({
|
|
7596
|
+
fileDiff,
|
|
7597
|
+
id: item.id,
|
|
7598
|
+
type: "diff"
|
|
7599
|
+
}, {
|
|
7600
|
+
...meta,
|
|
7601
|
+
section
|
|
7602
|
+
}))) return;
|
|
7603
|
+
event.preventDefault();
|
|
7604
|
+
editAfterExpandRef.current = {
|
|
7605
|
+
path: meta.file.path,
|
|
7606
|
+
sourceKey
|
|
7607
|
+
};
|
|
7608
|
+
onToggleCollapsed(meta.file, true, meta.reviewIdentity.key);
|
|
7609
|
+
return;
|
|
7610
|
+
}
|
|
7611
|
+
event.preventDefault();
|
|
7612
|
+
startCodeEdit(item, meta);
|
|
7613
|
+
};
|
|
7614
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
7615
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
7616
|
+
}, [
|
|
7617
|
+
canEditCodeItem,
|
|
7618
|
+
codeEditing,
|
|
7619
|
+
codeViewItems,
|
|
7620
|
+
itemMetadata,
|
|
7621
|
+
keymap,
|
|
7622
|
+
onToggleCollapsed,
|
|
7623
|
+
selectedCodeItemId,
|
|
7624
|
+
showWhitespace,
|
|
7625
|
+
sourceKey,
|
|
7626
|
+
startCodeEdit
|
|
7627
|
+
]);
|
|
7016
7628
|
const clearCommentLineHighlight = useCallback(() => {
|
|
7017
7629
|
codeViewRef.current?.clearSelectedLines();
|
|
7018
7630
|
navigatedSelectionRef.current = null;
|
|
@@ -7195,7 +7807,7 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7195
7807
|
expandUnchanged: false,
|
|
7196
7808
|
expansionLineCount: 100,
|
|
7197
7809
|
hunkSeparators: "line-info-basic",
|
|
7198
|
-
itemMetrics
|
|
7810
|
+
itemMetrics,
|
|
7199
7811
|
layout: {
|
|
7200
7812
|
...codeViewLayout,
|
|
7201
7813
|
paddingBottom: bottomInset
|
|
@@ -7203,11 +7815,12 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7203
7815
|
lineHoverHighlight: "both",
|
|
7204
7816
|
loadDiffFiles,
|
|
7205
7817
|
onGutterUtilityClick: (range, context) => {
|
|
7206
|
-
if (isReadOnly) return;
|
|
7818
|
+
if (isReadOnly || context.item.edit) return;
|
|
7207
7819
|
ignoreNextLineSelectionEndRef.current = context.item.type === "diff";
|
|
7208
7820
|
createCommentForRange(range, context);
|
|
7209
7821
|
},
|
|
7210
7822
|
onLineClick: (line, context) => {
|
|
7823
|
+
if (context.item.edit) return;
|
|
7211
7824
|
const lineElement = "lineElement" in line ? line.lineElement : null;
|
|
7212
7825
|
if (isPrimaryModifier(line.event) && onFindDefinitions) {
|
|
7213
7826
|
line.event.preventDefault();
|
|
@@ -7272,7 +7885,7 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7272
7885
|
});
|
|
7273
7886
|
},
|
|
7274
7887
|
onLineSelectionEnd: (range, context) => {
|
|
7275
|
-
if (isReadOnly) return;
|
|
7888
|
+
if (isReadOnly || context.item.edit) return;
|
|
7276
7889
|
if (ignoreNextLineSelectionEndRef.current) {
|
|
7277
7890
|
ignoreNextLineSelectionEndRef.current = false;
|
|
7278
7891
|
return;
|
|
@@ -7287,10 +7900,10 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7287
7900
|
node.classList.toggle("codiff-selected-item", metadata?.isSelected === true || selectedHeaderItemIds.has(context.item.id));
|
|
7288
7901
|
node.classList.toggle("codiff-markdown-preview-item", metadata?.isMarkdownPreview === true);
|
|
7289
7902
|
node.classList.toggle("codiff-image-preview-item", context.item.type === "file" && Boolean(metadata && canRenderImagePreview(metadata.file.path, metadata.section)));
|
|
7290
|
-
node.classList.toggle("codiff-loadable-summary-item", metadata != null && shouldLoadDiffSectionContents(metadata.section));
|
|
7903
|
+
node.classList.toggle("codiff-loadable-summary-item", !context.item.edit && metadata != null && shouldLoadDiffSectionContents(metadata.section));
|
|
7291
7904
|
node.classList.toggle("codiff-loading-summary-item", Boolean(metadata && loadingSectionIds.has(metadata.section.id)));
|
|
7292
7905
|
if (definitionModifierActiveRef.current) window.requestAnimationFrame(() => {
|
|
7293
|
-
if (definitionModifierActiveRef.current && node.isConnected) applyIdentifierNavigationState([{ element: node }], true);
|
|
7906
|
+
if (definitionModifierActiveRef.current && node.isConnected && !codeViewRef.current?.getItem(context.item.id)?.edit) applyIdentifierNavigationState([{ element: node }], true);
|
|
7294
7907
|
});
|
|
7295
7908
|
},
|
|
7296
7909
|
overflow: wordWrap ? "wrap" : "scroll",
|
|
@@ -7301,7 +7914,8 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7301
7914
|
},
|
|
7302
7915
|
themeType: theme,
|
|
7303
7916
|
tokenizeMaxLength: 1e5,
|
|
7304
|
-
unsafeCSS: codeViewUnsafeCSS
|
|
7917
|
+
unsafeCSS: codeViewUnsafeCSS,
|
|
7918
|
+
useTokenTransformer: workerHighlighterOptions.useTokenTransformer
|
|
7305
7919
|
}), [
|
|
7306
7920
|
bottomInset,
|
|
7307
7921
|
cancelPendingEmptyCommentDeletes,
|
|
@@ -7309,6 +7923,7 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7309
7923
|
diffStyle,
|
|
7310
7924
|
isReadOnly,
|
|
7311
7925
|
itemMetadata,
|
|
7926
|
+
itemMetrics,
|
|
7312
7927
|
loadDiffFiles,
|
|
7313
7928
|
loadingSectionIds,
|
|
7314
7929
|
onCreateComment,
|
|
@@ -7478,7 +8093,6 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7478
8093
|
const viewer = handle?.getInstance();
|
|
7479
8094
|
if (!handle || !viewer) return;
|
|
7480
8095
|
handledHunkNavRef.current = hunkNavigation.request;
|
|
7481
|
-
const headerHeight = codeViewItemMetrics.diffHeaderHeight;
|
|
7482
8096
|
const anchors = [];
|
|
7483
8097
|
const seen = /* @__PURE__ */ new Set();
|
|
7484
8098
|
const lineScrollTarget = (id, lineNumber, side) => ({
|
|
@@ -7597,6 +8211,7 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7597
8211
|
clearCommentLineHighlight,
|
|
7598
8212
|
diffLineHeight,
|
|
7599
8213
|
diffStyle,
|
|
8214
|
+
headerHeight,
|
|
7600
8215
|
hunkNavigation,
|
|
7601
8216
|
itemMetadata,
|
|
7602
8217
|
items
|
|
@@ -7625,7 +8240,7 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7625
8240
|
if (highlightFrameRef.current != null) window.cancelAnimationFrame(highlightFrameRef.current);
|
|
7626
8241
|
highlightFrameRef.current = window.requestAnimationFrame(() => {
|
|
7627
8242
|
highlightFrameRef.current = null;
|
|
7628
|
-
applySearchHighlights(viewer.getRenderedItems(), searchQuery, resolvedActiveSearchMatch);
|
|
8243
|
+
applySearchHighlights(viewer.getRenderedItems().filter(({ item }) => !item.edit), searchQuery, resolvedActiveSearchMatch);
|
|
7629
8244
|
});
|
|
7630
8245
|
}, [resolvedActiveSearchMatch, searchQuery]);
|
|
7631
8246
|
const updateRenderedIdentifierNavigation = useCallback((active, viewer) => {
|
|
@@ -7636,12 +8251,12 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7636
8251
|
definitionHighlightFrameRef.current = null;
|
|
7637
8252
|
}
|
|
7638
8253
|
if (!active) {
|
|
7639
|
-
applyIdentifierNavigationState(nextViewer.getRenderedItems(), false);
|
|
8254
|
+
applyIdentifierNavigationState(nextViewer.getRenderedItems().filter(({ item }) => !item.edit), false);
|
|
7640
8255
|
return;
|
|
7641
8256
|
}
|
|
7642
8257
|
definitionHighlightFrameRef.current = window.requestAnimationFrame(() => {
|
|
7643
8258
|
definitionHighlightFrameRef.current = null;
|
|
7644
|
-
applyIdentifierNavigationState(nextViewer.getRenderedItems(), true);
|
|
8259
|
+
applyIdentifierNavigationState(nextViewer.getRenderedItems().filter(({ item }) => !item.edit), true);
|
|
7645
8260
|
});
|
|
7646
8261
|
}, []);
|
|
7647
8262
|
const scheduleStickyHeaderStateUpdate = useCallback((viewer) => {
|
|
@@ -7783,10 +8398,47 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7783
8398
|
toggleSourceDescriptionCollapsed
|
|
7784
8399
|
]);
|
|
7785
8400
|
const renderCustomHeader = useCallback((item) => {
|
|
7786
|
-
const
|
|
7787
|
-
|
|
8401
|
+
const editSession = codeEditing.sessions.get(item.id);
|
|
8402
|
+
const isEditing = editSession != null;
|
|
8403
|
+
const meta = editSession?.metadata ?? itemMetadata.get(item.id);
|
|
8404
|
+
const isBusy = codeEditing.isBusy(item.id);
|
|
8405
|
+
const isAutosaving = editSession?.saving && !isBusy;
|
|
8406
|
+
const canEdit = canEditCodeItem(item, meta);
|
|
8407
|
+
const editError = codeEditing.getError(item.id);
|
|
8408
|
+
return meta ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(CodeViewHeader, {
|
|
7788
8409
|
allowViewedToggle,
|
|
7789
8410
|
canCreateFileComment: canCreateFileComments,
|
|
8411
|
+
editActions: isEditing ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Button, {
|
|
8412
|
+
"aria-label": `${editSession?.hasChanges ? "Revert edits to" : "Cancel editing"} ${meta.file.path}`,
|
|
8413
|
+
className: "codiff-edit-revert-button",
|
|
8414
|
+
"data-autosaving": isAutosaving ? "" : void 0,
|
|
8415
|
+
disabled: isBusy || editSession.saving,
|
|
8416
|
+
onClick: () => void codeEditing.revertEdit(item.id),
|
|
8417
|
+
title: editSession?.hasChanges ? "Restore the file to when editing started" : "Cancel editing",
|
|
8418
|
+
type: "button",
|
|
8419
|
+
variant: editSession?.hasChanges ? "destructive" : "default",
|
|
8420
|
+
children: editSession?.hasChanges ? "Revert" : "Cancel"
|
|
8421
|
+
}), /* @__PURE__ */ jsxs(Button, {
|
|
8422
|
+
"aria-label": `Done editing ${meta.file.path}`,
|
|
8423
|
+
"data-autosaving": isAutosaving ? "" : void 0,
|
|
8424
|
+
disabled: isBusy || editSession.saving,
|
|
8425
|
+
onClick: () => void codeEditing.doneEdit(item.id),
|
|
8426
|
+
title: "Finish editing; changes are saved automatically",
|
|
8427
|
+
type: "button",
|
|
8428
|
+
children: [/* @__PURE__ */ jsx(CheckIcon, {
|
|
8429
|
+
"aria-hidden": true,
|
|
8430
|
+
size: 14,
|
|
8431
|
+
weight: "bold"
|
|
8432
|
+
}), "Done"]
|
|
8433
|
+
})] }) : canEdit ? /* @__PURE__ */ jsx(CodeEditButton, {
|
|
8434
|
+
disabled: !codeEditing.canStartEdit(item.id, meta.file.path),
|
|
8435
|
+
isBusy,
|
|
8436
|
+
item,
|
|
8437
|
+
meta,
|
|
8438
|
+
onEdit: () => startCodeEdit(item, meta),
|
|
8439
|
+
onPrepare: codeEditing.prepareEdit
|
|
8440
|
+
}) : null,
|
|
8441
|
+
isEditing,
|
|
7790
8442
|
isSectionLoading: loadingSectionIds.has(meta.section.id),
|
|
7791
8443
|
meta,
|
|
7792
8444
|
onCreateFileComment: () => createFileComment(meta, item.id),
|
|
@@ -7796,11 +8448,17 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7796
8448
|
onToggleMarkdownPreview: toggleMarkdownPreview,
|
|
7797
8449
|
onToggleViewed,
|
|
7798
8450
|
readOnly: isReadOnly
|
|
7799
|
-
})
|
|
8451
|
+
}), editError ? /* @__PURE__ */ jsx("div", {
|
|
8452
|
+
className: "codiff-code-edit-error",
|
|
8453
|
+
role: "alert",
|
|
8454
|
+
children: editError
|
|
8455
|
+
}) : null] }) : null;
|
|
7800
8456
|
}, [
|
|
7801
8457
|
allowViewedToggle,
|
|
7802
8458
|
canCreateFileComments,
|
|
8459
|
+
canEditCodeItem,
|
|
7803
8460
|
createFileComment,
|
|
8461
|
+
codeEditing,
|
|
7804
8462
|
itemMetadata,
|
|
7805
8463
|
isReadOnly,
|
|
7806
8464
|
loadingSectionIds,
|
|
@@ -7808,6 +8466,7 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7808
8466
|
onOpenFile,
|
|
7809
8467
|
onToggleCollapsed,
|
|
7810
8468
|
onToggleViewed,
|
|
8469
|
+
startCodeEdit,
|
|
7811
8470
|
toggleMarkdownPreview
|
|
7812
8471
|
]);
|
|
7813
8472
|
const renderAnnotation = useCallback((annotation, item) => {
|
|
@@ -7909,10 +8568,14 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7909
8568
|
scheduleStickyHeaderStateUpdate,
|
|
7910
8569
|
updateRenderedIdentifierNavigation
|
|
7911
8570
|
]);
|
|
7912
|
-
|
|
7913
|
-
className:
|
|
8571
|
+
const codeView = /* @__PURE__ */ jsx(CodeView, {
|
|
8572
|
+
className: `code-view${compactHeaders ? " compact-file-headers" : ""}`,
|
|
8573
|
+
containerRef: setCodeViewElement,
|
|
7914
8574
|
disableWorkerPool,
|
|
7915
|
-
|
|
8575
|
+
editorOptions: codeEditing.editorOptions,
|
|
8576
|
+
items: codeEditing.items,
|
|
8577
|
+
onItemEditChange: codeEditing.onItemEditChange,
|
|
8578
|
+
onItemEditComplete: codeEditing.onItemEditComplete,
|
|
7916
8579
|
onScroll: handleScroll,
|
|
7917
8580
|
onSelectedLinesChange: setCodeViewSelectedLines,
|
|
7918
8581
|
options: codeViewOptions,
|
|
@@ -7920,14 +8583,20 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7920
8583
|
renderAnnotation,
|
|
7921
8584
|
renderCodeViewHeader: sourceDescriptionItemId ? renderCodeViewHeader : void 0,
|
|
7922
8585
|
renderCustomHeader,
|
|
7923
|
-
selectedLines: isReadOnly ? null : selectedLines
|
|
7924
|
-
|
|
8586
|
+
selectedLines: isReadOnly ? null : selectedLines,
|
|
8587
|
+
style: codeViewStyle
|
|
8588
|
+
});
|
|
8589
|
+
const renderedCodeView = disableWorkerPool ? codeView : /* @__PURE__ */ jsx(WorkerPoolContextProvider, {
|
|
7925
8590
|
highlighterOptions: workerHighlighterOptions,
|
|
7926
8591
|
poolOptions: workerPoolOptions,
|
|
7927
8592
|
children: /* @__PURE__ */ jsx(CodeView, {
|
|
7928
|
-
className:
|
|
8593
|
+
className: `code-view${compactHeaders ? " compact-file-headers" : ""}`,
|
|
8594
|
+
containerRef: setCodeViewElement,
|
|
7929
8595
|
disableWorkerPool: false,
|
|
7930
|
-
|
|
8596
|
+
editorOptions: codeEditing.editorOptions,
|
|
8597
|
+
items: codeEditing.items,
|
|
8598
|
+
onItemEditChange: codeEditing.onItemEditChange,
|
|
8599
|
+
onItemEditComplete: codeEditing.onItemEditComplete,
|
|
7931
8600
|
onScroll: handleScroll,
|
|
7932
8601
|
onSelectedLinesChange: setCodeViewSelectedLines,
|
|
7933
8602
|
options: codeViewOptions,
|
|
@@ -7935,8 +8604,13 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
|
|
|
7935
8604
|
renderAnnotation,
|
|
7936
8605
|
renderCodeViewHeader: sourceDescriptionItemId ? renderCodeViewHeader : void 0,
|
|
7937
8606
|
renderCustomHeader,
|
|
7938
|
-
selectedLines: isReadOnly ? null : selectedLines
|
|
8607
|
+
selectedLines: isReadOnly ? null : selectedLines,
|
|
8608
|
+
style: codeViewStyle
|
|
7939
8609
|
})
|
|
8610
|
+
});
|
|
8611
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(EditProvider, {
|
|
8612
|
+
createEditor: createCodeEditor,
|
|
8613
|
+
children: renderedCodeView
|
|
7940
8614
|
}), definitionLookup?.sourceKey === sourceKey && onOpenDefinition ? /* @__PURE__ */ jsx(DefinitionPopover, {
|
|
7941
8615
|
anchor: definitionLookup.anchor,
|
|
7942
8616
|
getDestination: (candidate) => getDefinitionDiffTarget(candidate) ? "diff" : candidate.canOpenInEditor ? "editor" : "unavailable",
|
|
@@ -9653,54 +10327,6 @@ function useReviewFileState({ initialSelectedPath = null, onViewedChange } = {})
|
|
|
9653
10327
|
};
|
|
9654
10328
|
}
|
|
9655
10329
|
//#endregion
|
|
9656
|
-
//#region config/defaults.ts
|
|
9657
|
-
const defaultConfigTemplate = {
|
|
9658
|
-
settings: {
|
|
9659
|
-
"agentBackend": "codex",
|
|
9660
|
-
"checkForUpdates": true,
|
|
9661
|
-
"claudeModel": "claude-sonnet-4-6",
|
|
9662
|
-
"codeFontFamily": "",
|
|
9663
|
-
"codeFontSize": 13,
|
|
9664
|
-
"copyCommentsOnClose": false,
|
|
9665
|
-
"diffStyle": "split",
|
|
9666
|
-
"editorCommand": "",
|
|
9667
|
-
"lastRepositoryPath": "",
|
|
9668
|
-
"openAIModel": "gpt-5.6-terra",
|
|
9669
|
-
"opencodeModel": "opencode-default",
|
|
9670
|
-
"piModel": "pi-default",
|
|
9671
|
-
"reviewCommentsPrefix": "# Address these Review Comments",
|
|
9672
|
-
"sidebarPosition": "left",
|
|
9673
|
-
"showOutdated": false,
|
|
9674
|
-
"showWhitespace": false,
|
|
9675
|
-
"theme": "system",
|
|
9676
|
-
"walkthroughPrompt": "",
|
|
9677
|
-
"wordWrap": false
|
|
9678
|
-
},
|
|
9679
|
-
keymap: {
|
|
9680
|
-
"askAgent": "Mod+Alt+Enter",
|
|
9681
|
-
"closeSearch": "Escape",
|
|
9682
|
-
"commandBar": "Mod+k",
|
|
9683
|
-
"diffSearch": "Mod+f",
|
|
9684
|
-
"discardComment": "Escape",
|
|
9685
|
-
"fileFilter": "Mod+p",
|
|
9686
|
-
"nextHunk": ["Ctrl+ArrowDown", "j"],
|
|
9687
|
-
"nextSearchMatch": "Enter",
|
|
9688
|
-
"openFile": "Mod+Shift+o",
|
|
9689
|
-
"prevHunk": ["Ctrl+ArrowUp", "k"],
|
|
9690
|
-
"prevSearchMatch": "Shift+Enter",
|
|
9691
|
-
"shortcutsHelp": "Shift+?",
|
|
9692
|
-
"submitComment": "Mod+Enter",
|
|
9693
|
-
"toggleSidebar": "Mod+Shift+b",
|
|
9694
|
-
"toggleWordWrap": "Alt+z"
|
|
9695
|
-
}
|
|
9696
|
-
};
|
|
9697
|
-
const createDefaultConfig = () => ({
|
|
9698
|
-
keymap: { ...defaultConfigTemplate.keymap },
|
|
9699
|
-
settings: { ...defaultConfigTemplate.settings }
|
|
9700
|
-
});
|
|
9701
|
-
Object.freeze({ ...defaultConfigTemplate.keymap });
|
|
9702
|
-
Object.freeze({ ...defaultConfigTemplate.settings });
|
|
9703
|
-
//#endregion
|
|
9704
10330
|
//#region lib/app-constants.ts
|
|
9705
10331
|
const AGENT_LABELS = {
|
|
9706
10332
|
claude: "Claude Code",
|