@nkzw/codiff-core 1.14.0 → 1.16.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
@@ -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: false
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,479 @@ 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
+ "expandUnchanged": false,
4060
+ "lastRepositoryPath": "",
4061
+ "openAIModel": "gpt-5.6-terra",
4062
+ "openAIReasoningEffort": "",
4063
+ "opencodeModel": "opencode-default",
4064
+ "piModel": "pi-default",
4065
+ "reviewCommentsPrefix": "# Address these Review Comments",
4066
+ "sidebarPosition": "left",
4067
+ "showOutdated": false,
4068
+ "showWhitespace": false,
4069
+ "theme": "system",
4070
+ "walkthroughPrompt": "",
4071
+ "wordWrap": false
4072
+ },
4073
+ keymap: {
4074
+ "askAgent": "Mod+Alt+Enter",
4075
+ "closeSearch": "Escape",
4076
+ "commandBar": "Mod+k",
4077
+ "diffSearch": "Mod+f",
4078
+ "discardComment": "Escape",
4079
+ "doneEditing": "Mod+s",
4080
+ "editFile": "Mod+e",
4081
+ "fileFilter": "Mod+p",
4082
+ "nextHunk": ["Ctrl+ArrowDown", "j"],
4083
+ "nextSearchMatch": "Enter",
4084
+ "openFile": "Mod+Shift+o",
4085
+ "prevHunk": ["Ctrl+ArrowUp", "k"],
4086
+ "prevSearchMatch": "Shift+Enter",
4087
+ "shortcutsHelp": "Shift+?",
4088
+ "submitComment": "Mod+Enter",
4089
+ "toggleSidebar": "Mod+Shift+b",
4090
+ "toggleWordWrap": "Alt+z"
4091
+ }
4092
+ };
4093
+ const createDefaultConfig = () => ({
4094
+ keymap: { ...defaultConfigTemplate.keymap },
4095
+ settings: { ...defaultConfigTemplate.settings }
4096
+ });
4097
+ const defaultKeymap = Object.freeze({ ...defaultConfigTemplate.keymap });
4098
+ Object.freeze({ ...defaultConfigTemplate.settings });
4099
+ const createFileEditingStore = (onChange) => {
4100
+ const sessionRef = { current: null };
4101
+ const draftRef = { current: "" };
4102
+ let timer;
4103
+ let pendingWrite = null;
4104
+ const clearTimer = () => {
4105
+ clearTimeout(timer);
4106
+ timer = void 0;
4107
+ };
4108
+ const setSession = (session) => {
4109
+ sessionRef.current = session ? {
4110
+ ...session,
4111
+ dirty: draftRef.current !== session.document.content,
4112
+ hasChanges: session.finishing === "revert" ? session.hasChanges : draftRef.current !== session.originalContent
4113
+ } : null;
4114
+ if (!session) clearTimer();
4115
+ onChange();
4116
+ };
4117
+ const scheduleSave = () => {
4118
+ clearTimer();
4119
+ timer = setTimeout(() => void flush(), 300);
4120
+ };
4121
+ const flush = () => {
4122
+ clearTimer();
4123
+ if (pendingWrite) return pendingWrite.then((saved) => saved ? flush() : false);
4124
+ const current = sessionRef.current;
4125
+ if (!current || draftRef.current === current.document.content) return Promise.resolve(true);
4126
+ const content = draftRef.current;
4127
+ setSession({
4128
+ ...current,
4129
+ saveError: void 0,
4130
+ saving: true
4131
+ });
4132
+ pendingWrite = Promise.resolve().then(() => window.codiff.saveRepositoryFile({
4133
+ ...current.document,
4134
+ baseVersion: current.document.version,
4135
+ content
4136
+ })).then((result) => {
4137
+ if (result.status === "conflict") throw new Error("The file changed on disk. Your edits are kept; copy them before refreshing.");
4138
+ const latest = sessionRef.current;
4139
+ if (latest) setSession({
4140
+ ...latest,
4141
+ document: result.document,
4142
+ saving: false
4143
+ });
4144
+ return true;
4145
+ }).catch((error) => {
4146
+ const latest = sessionRef.current;
4147
+ if (latest) setSession({
4148
+ ...latest,
4149
+ saveError: error instanceof Error ? error.message : String(error),
4150
+ saving: false
4151
+ });
4152
+ return false;
4153
+ }).finally(() => {
4154
+ pendingWrite = null;
4155
+ const latest = sessionRef.current;
4156
+ if (latest && !latest.finishing && !latest.saveError && draftRef.current !== latest.document.content) scheduleSave();
4157
+ });
4158
+ return pendingWrite;
4159
+ };
4160
+ const finish = async (revert, beforeClose) => {
4161
+ const current = sessionRef.current;
4162
+ if (!current || current.finishing) return false;
4163
+ clearTimer();
4164
+ setSession({
4165
+ ...current,
4166
+ finishing: revert ? "revert" : "done"
4167
+ });
4168
+ let previousDraft;
4169
+ try {
4170
+ if (pendingWrite && !await pendingWrite) return false;
4171
+ if (revert) {
4172
+ previousDraft = draftRef.current;
4173
+ draftRef.current = current.originalContent;
4174
+ }
4175
+ do {
4176
+ do
4177
+ if (!await flush()) {
4178
+ if (previousDraft !== void 0) draftRef.current = previousDraft;
4179
+ return false;
4180
+ }
4181
+ while (!revert && draftRef.current !== sessionRef.current?.document.content);
4182
+ await beforeClose?.();
4183
+ } while (!revert && draftRef.current !== sessionRef.current?.document.content);
4184
+ setSession(null);
4185
+ return true;
4186
+ } catch (error) {
4187
+ if (previousDraft !== void 0) draftRef.current = previousDraft;
4188
+ throw error;
4189
+ } finally {
4190
+ const latest = sessionRef.current;
4191
+ if (latest) setSession({
4192
+ ...latest,
4193
+ finishing: void 0
4194
+ });
4195
+ }
4196
+ };
4197
+ return {
4198
+ draftRef,
4199
+ finish,
4200
+ flush,
4201
+ getSnapshot: () => sessionRef.current,
4202
+ setDraft: (content) => {
4203
+ const current = sessionRef.current;
4204
+ if (!current || current.finishing === "revert" || content === draftRef.current) return;
4205
+ const wasDirty = draftRef.current !== current.document.content;
4206
+ draftRef.current = content;
4207
+ const isDirty = content !== current.document.content;
4208
+ if (isDirty && !current.finishing && !current.saveError) scheduleSave();
4209
+ else clearTimer();
4210
+ if (wasDirty !== isDirty || current.hasChanges !== (content !== current.originalContent)) setSession({ ...current });
4211
+ },
4212
+ setSession
4213
+ };
4214
+ };
4215
+ const getCodeEditKey = (sourceKey, itemId) => JSON.stringify([sourceKey, itemId]);
4216
+ const createCodeEditingStore = () => {
4217
+ const entries = /* @__PURE__ */ new Map();
4218
+ const listeners = /* @__PURE__ */ new Set();
4219
+ let snapshot = /* @__PURE__ */ new Map();
4220
+ const hasFile = (path) => [...snapshot.values()].some((session) => session.document.path === path);
4221
+ return {
4222
+ clear: () => {
4223
+ for (const entry of entries.values()) entry.setSession(null);
4224
+ },
4225
+ flush: (sourceKey) => Promise.all([...entries.values()].filter((entry) => entry.getSnapshot()?.sourceKey === sourceKey).map((entry) => entry.flush())),
4226
+ get: (sourceKey, itemId) => entries.get(getCodeEditKey(sourceKey, itemId)),
4227
+ getForItem: (item) => item.type === "diff" ? [...entries.values()].find((entry) => {
4228
+ const session = entry.getSnapshot();
4229
+ return session?.item.id === item.id && session.item.fileDiff === item.fileDiff;
4230
+ }) : void 0,
4231
+ getSnapshot: () => snapshot,
4232
+ hasFile,
4233
+ hasUnsavedEdits: () => [...entries.values()].some((entry) => {
4234
+ const session = entry.getSnapshot();
4235
+ return session && (session.saving || entry.draftRef.current !== session.document.content);
4236
+ }),
4237
+ start: (session) => {
4238
+ if (hasFile(session.document.path)) return null;
4239
+ const key = getCodeEditKey(session.sourceKey, session.item.id);
4240
+ const entry = createFileEditingStore(() => {
4241
+ const next = new Map(snapshot);
4242
+ const current = entry.getSnapshot();
4243
+ if (current) next.set(key, current);
4244
+ else {
4245
+ next.delete(key);
4246
+ entries.delete(key);
4247
+ }
4248
+ snapshot = next;
4249
+ for (const listener of listeners) listener();
4250
+ });
4251
+ entries.set(key, entry);
4252
+ entry.draftRef.current = session.document.content;
4253
+ entry.setSession(session);
4254
+ return entry;
4255
+ },
4256
+ subscribe: (listener) => {
4257
+ listeners.add(listener);
4258
+ return () => {
4259
+ listeners.delete(listener);
4260
+ };
4261
+ }
4262
+ };
4263
+ };
4264
+ //#endregion
4265
+ //#region app/hooks/useCodeEditing.ts
4266
+ let editorModule = null;
4267
+ let editorModulePromise = null;
4268
+ const loadEditor = () => editorModulePromise ??= import("@pierre/diffs/edit").then((module) => {
4269
+ editorModule = module;
4270
+ return module;
4271
+ }).catch((error) => {
4272
+ editorModulePromise = null;
4273
+ throw error;
4274
+ });
4275
+ const prepareEditor = (fileDiff) => {
4276
+ const lang = fileDiff.lang ?? getFiletypeFromFileName(fileDiff.name);
4277
+ return Promise.all([loadEditor(), preloadHighlighter(getHighlighterOptions(lang, workerHighlighterOptions))]);
4278
+ };
4279
+ const createCodeEditor = (type, options, key) => {
4280
+ if (!editorModule) throw new Error("The code editor has not loaded.");
4281
+ return new editorModule.Editor(type, options, key);
4282
+ };
4283
+ function useUnsavedCodeEditsGuard(store) {
4284
+ useEffect(() => {
4285
+ const beforeUnload = (event) => {
4286
+ if (store.hasUnsavedEdits()) {
4287
+ event.preventDefault();
4288
+ event.returnValue = "";
4289
+ }
4290
+ };
4291
+ window.addEventListener("beforeunload", beforeUnload);
4292
+ return () => window.removeEventListener("beforeunload", beforeUnload);
4293
+ }, [store]);
4294
+ }
4295
+ const errorMessage = (error) => error instanceof Error ? error.message : String(error);
4296
+ const getFirstVisibleChangedLine = (root, viewport) => {
4297
+ const viewportRect = viewport.getBoundingClientRect();
4298
+ const header = root.querySelector("[data-diffs-header][data-sticky]");
4299
+ const top = Math.max(viewportRect.top, header?.getBoundingClientRect().bottom ?? viewportRect.top) + 11;
4300
+ for (const row of root.querySelectorAll("[data-content] > [data-line-type=\"change-addition\"]")) {
4301
+ const rect = row.getBoundingClientRect();
4302
+ if (rect.height > 0 && rect.top >= top && rect.top < viewportRect.bottom) {
4303
+ const line = Number(row.dataset.line);
4304
+ if (Number.isInteger(line) && line > 0) return line;
4305
+ }
4306
+ }
4307
+ };
4308
+ function useCodeEditing({ codeViewRef, items, keymap = defaultKeymap, onLoadSectionContents, onRefresh, selectedItemId, sourceKey, store: sharedStore }) {
4309
+ const [localStore] = useState(createCodeEditingStore);
4310
+ const store = sharedStore ?? localStore;
4311
+ const sessions = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
4312
+ useUnsavedCodeEditsGuard(store);
4313
+ const busyRef = useRef(/* @__PURE__ */ new Set());
4314
+ const [busyItemKeys, setBusyItemKeys] = useState(() => /* @__PURE__ */ new Set());
4315
+ const [errors, setErrors] = useState(() => /* @__PURE__ */ new Map());
4316
+ const setBusy = useCallback((key, busy) => {
4317
+ if (busy) busyRef.current.add(key);
4318
+ else busyRef.current.delete(key);
4319
+ setBusyItemKeys(new Set(busyRef.current));
4320
+ }, []);
4321
+ const setError = useCallback((key, message) => {
4322
+ setErrors((current) => {
4323
+ const next = new Map(current);
4324
+ if (message) next.set(key, message);
4325
+ else next.delete(key);
4326
+ return next;
4327
+ });
4328
+ }, []);
4329
+ const currentSessions = useMemo(() => new Map([...sessions.values()].filter((session) => session.sourceKey === sourceKey).map((session) => [session.item.id, session])), [sessions, sourceKey]);
4330
+ const editorOptions = useMemo(() => {
4331
+ const focusedEditors = /* @__PURE__ */ new WeakSet();
4332
+ return { onAttach: (editor, fileInstance) => {
4333
+ if (!focusedEditors.has(editor)) {
4334
+ focusedEditors.add(editor);
4335
+ const viewer = codeViewRef.current?.getInstance();
4336
+ const root = viewer?.getRenderedItems().find(({ instance }) => instance === fileInstance)?.element.shadowRoot;
4337
+ const viewport = viewer?.getContainerElement();
4338
+ editor.focus({
4339
+ character: 0,
4340
+ lineNumber: (root && viewport ? getFirstVisibleChangedLine(root, viewport) : void 0) ?? "first-visible",
4341
+ offset: 11,
4342
+ preventScroll: true
4343
+ });
4344
+ const position = editor.getViewState().selections?.[0]?.start;
4345
+ const indentation = (position && editor.getEditState()?.document.getLineText(position.line))?.match(/^\s*/)?.[0].length ?? 0;
4346
+ if (position && indentation > 0) editor.focus({
4347
+ character: indentation,
4348
+ lineNumber: position.line + 1,
4349
+ preventScroll: true
4350
+ });
4351
+ }
4352
+ } };
4353
+ }, [codeViewRef]);
4354
+ const loadEditContents = useCallback((item, metadata) => {
4355
+ if (!item.fileDiff.isPartial) return Promise.resolve({
4356
+ newFile: {
4357
+ contents: item.fileDiff.additionLines.join(""),
4358
+ name: item.fileDiff.name
4359
+ },
4360
+ oldFile: null
4361
+ });
4362
+ return loadSectionContents(metadata.file, metadata.section, onLoadSectionContents);
4363
+ }, [onLoadSectionContents]);
4364
+ const prepareEdit = useCallback(async (item, metadata) => {
4365
+ if (item.type === "diff" && onLoadSectionContents) {
4366
+ const [, loaded] = await Promise.all([prepareEditor(item.fileDiff), loadEditContents(item, metadata)]);
4367
+ const rendered = codeViewRef.current?.getInstance()?.getRenderedItems().find(({ id }) => id === item.id);
4368
+ if (rendered?.type === "diff") {
4369
+ const fileDiff = item.fileDiff.isPartial ? hydratePartialDiff("clone", item.fileDiff, loaded) : item.fileDiff;
4370
+ await rendered.instance.primeHighlightCache(fileDiff);
4371
+ }
4372
+ }
4373
+ }, [
4374
+ codeViewRef,
4375
+ loadEditContents,
4376
+ onLoadSectionContents
4377
+ ]);
4378
+ const startEdit = useCallback(async (item, metadata) => {
4379
+ const itemKey = getCodeEditKey(sourceKey, item.id);
4380
+ if (item.type !== "diff" || !onLoadSectionContents || store.hasFile(metadata.file.path) || busyRef.current.has(itemKey)) return;
4381
+ setBusy(itemKey, true);
4382
+ setError(itemKey);
4383
+ try {
4384
+ const [, loaded, document] = await Promise.all([
4385
+ prepareEditor(item.fileDiff),
4386
+ loadEditContents(item, metadata),
4387
+ window.codiff.getRepositoryFile(metadata.file.path)
4388
+ ]);
4389
+ if (!loaded.newFile || loaded.newFile.contents !== document.content) throw new Error("The file changed on disk. Refresh the diff before editing.");
4390
+ const key = crypto.randomUUID();
4391
+ const fileDiff = item.fileDiff.isPartial ? hydratePartialDiff("clone", item.fileDiff, loaded) : cloneFileDiffMetadata(item.fileDiff);
4392
+ fileDiff.cacheKey ??= `code-edit:${key}`;
4393
+ const rendered = codeViewRef.current?.getInstance()?.getRenderedItems().find(({ id }) => id === item.id);
4394
+ if (rendered?.type === "diff") await rendered.instance.primeHighlightCache(fileDiff);
4395
+ const nextSession = {
4396
+ dirty: false,
4397
+ document,
4398
+ hasChanges: false,
4399
+ item: {
4400
+ ...item,
4401
+ collapsed: false,
4402
+ edit: true,
4403
+ fileDiff,
4404
+ version: getItemVersion(`code-edit:${key}`)
4405
+ },
4406
+ metadata,
4407
+ originalContent: document.content,
4408
+ sourceKey
4409
+ };
4410
+ if (store.start(nextSession)) codeViewRef.current?.clearSelectedLines();
4411
+ } catch (error) {
4412
+ setError(itemKey, errorMessage(error));
4413
+ } finally {
4414
+ setBusy(itemKey, false);
4415
+ }
4416
+ }, [
4417
+ codeViewRef,
4418
+ loadEditContents,
4419
+ onLoadSectionContents,
4420
+ setBusy,
4421
+ setError,
4422
+ sourceKey,
4423
+ store
4424
+ ]);
4425
+ const finishEdit = useCallback(async (itemId, revert) => {
4426
+ const itemKey = getCodeEditKey(sourceKey, itemId);
4427
+ const entry = store.get(sourceKey, itemId);
4428
+ const current = entry?.getSnapshot();
4429
+ if (!entry || !current || busyRef.current.has(itemKey)) return;
4430
+ setBusy(itemKey, true);
4431
+ setError(itemKey);
4432
+ const needsRefresh = current.hasChanges || current.document.content !== current.originalContent || current.saving;
4433
+ try {
4434
+ await entry.finish(revert, async () => {
4435
+ const latest = entry.getSnapshot();
4436
+ 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.");
4437
+ });
4438
+ } catch (error) {
4439
+ setError(itemKey, errorMessage(error));
4440
+ } finally {
4441
+ setBusy(itemKey, false);
4442
+ }
4443
+ }, [
4444
+ onRefresh,
4445
+ setBusy,
4446
+ setError,
4447
+ sourceKey,
4448
+ store
4449
+ ]);
4450
+ const doneEdit = useCallback((itemId) => finishEdit(itemId, false), [finishEdit]);
4451
+ const revertEdit = useCallback((itemId) => finishEdit(itemId, true), [finishEdit]);
4452
+ const onItemEditChange = useCallback((event, item) => {
4453
+ store.getForItem(item)?.setDraft(event.file.contents);
4454
+ }, [store]);
4455
+ const onItemEditComplete = useCallback((_event, item, nextItem) => {
4456
+ const entry = store.getForItem(item);
4457
+ const current = entry?.getSnapshot();
4458
+ if (!entry || !current || nextItem.type !== "diff") return "reject";
4459
+ nextItem.fileDiff.cacheKey = `code-edit:${crypto.randomUUID()}`;
4460
+ entry.setSession({
4461
+ ...current,
4462
+ item: {
4463
+ ...nextItem,
4464
+ edit: true
4465
+ }
4466
+ });
4467
+ return "accept";
4468
+ }, [store]);
4469
+ const editedItems = useMemo(() => {
4470
+ if (currentSessions.size === 0) return items;
4471
+ const found = /* @__PURE__ */ new Set();
4472
+ const next = items.map((item) => {
4473
+ const session = currentSessions.get(item.id);
4474
+ if (!session) return item;
4475
+ found.add(item.id);
4476
+ return {
4477
+ ...session.item,
4478
+ collapsed: item.collapsed
4479
+ };
4480
+ });
4481
+ for (const session of currentSessions.values()) if (!found.has(session.item.id)) next.push(session.item);
4482
+ return next;
4483
+ }, [currentSessions, items]);
4484
+ useEffect(() => {
4485
+ if (currentSessions.size === 0) return;
4486
+ const keyDown = (event) => {
4487
+ if (event.defaultPrevented || event.repeat || event.isComposing || !matchesShortcut(event, keymap, "doneEditing")) return;
4488
+ const path = event.composedPath();
4489
+ if (isNativeInputTarget(event.target) && !path.some((node) => node.matches?.("[data-content][contenteditable=\"true\"]"))) return;
4490
+ 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);
4491
+ if (itemId == null || !currentSessions.has(itemId)) return;
4492
+ event.preventDefault();
4493
+ event.stopPropagation();
4494
+ doneEdit(itemId);
4495
+ };
4496
+ window.addEventListener("keydown", keyDown, true);
4497
+ return () => window.removeEventListener("keydown", keyDown, true);
4498
+ }, [
4499
+ codeViewRef,
4500
+ currentSessions,
4501
+ doneEdit,
4502
+ keymap,
4503
+ selectedItemId
4504
+ ]);
4505
+ return {
4506
+ canStartEdit: (itemId, path) => !busyItemKeys.has(getCodeEditKey(sourceKey, itemId)) && ![...sessions.values()].some((session) => session.document.path === path),
4507
+ doneEdit,
4508
+ editorOptions,
4509
+ getError: (itemId) => currentSessions.get(itemId)?.saveError ?? errors.get(getCodeEditKey(sourceKey, itemId)),
4510
+ isBusy: (itemId) => busyItemKeys.has(getCodeEditKey(sourceKey, itemId)) || currentSessions.get(itemId)?.finishing != null,
4511
+ items: editedItems,
4512
+ onItemEditChange,
4513
+ onItemEditComplete,
4514
+ prepareEdit,
4515
+ revertEdit,
4516
+ sessions: currentSessions,
4517
+ startEdit
4518
+ };
4519
+ }
4520
+ //#endregion
4055
4521
  //#region app/hooks/useCodeViewAnnotations.ts
4056
4522
  function useCodeViewAnnotations() {
4057
4523
  const [getAnnotations] = useState(() => {
@@ -4484,14 +4950,17 @@ const resolveWalkthroughHunkFile = (hunk, files) => {
4484
4950
  };
4485
4951
  };
4486
4952
  const walkthroughHunkRunKey = (item, hunks) => `${item.id}:${hunks.map((hunk) => hunk.id).join(",")}`;
4487
- /** Resolve item hunks in authored order, coalescing adjacent hunks from the same file section. */
4953
+ /** Resolve authored hunk order, coalescing only forward runs within each file section. */
4488
4954
  const resolveWalkthroughHunkRuns = (item, files) => {
4489
4955
  const runs = [];
4490
4956
  for (const hunk of item.hunks) {
4491
4957
  const resolved = resolveWalkthroughHunkFile(hunk, files);
4492
4958
  if (!resolved) continue;
4493
4959
  const previous = runs.at(-1);
4494
- if (previous && previous.resolved.file.path === resolved.file.path && previous.resolved.section.id === resolved.section.id) {
4960
+ const previousHunk = previous?.hunks.at(-1);
4961
+ const previousOrdinal = previousHunk ? getHunkOrdinal(resolved.section.id, previousHunk.id) : null;
4962
+ const ordinal = getHunkOrdinal(resolved.section.id, hunk.id);
4963
+ if (previous && previous.resolved.file.path === resolved.file.path && previous.resolved.section.id === resolved.section.id && previousOrdinal != null && ordinal != null && previousOrdinal < ordinal) {
4495
4964
  const hunks = [...previous.hunks, hunk];
4496
4965
  runs[runs.length - 1] = {
4497
4966
  ...previous,
@@ -5095,124 +5564,165 @@ function CopyFilePathButton({ path }) {
5095
5564
  })
5096
5565
  });
5097
5566
  }
5098
- function CodeViewHeader({ allowViewedToggle, canCreateFileComment, isSectionLoading, meta, onCreateFileComment, onLoadSection, onOpenFile, onToggleCollapsed, onToggleMarkdownPreview, onToggleViewed, readOnly }) {
5567
+ function CodeEditButton({ disabled, isBusy, item, meta, onEdit, onPrepare }) {
5568
+ useEffect(() => {
5569
+ onPrepare(item, meta).catch(() => {});
5570
+ }, [
5571
+ item,
5572
+ meta,
5573
+ onPrepare
5574
+ ]);
5575
+ return /* @__PURE__ */ jsxs(Button, {
5576
+ "aria-label": `Edit ${meta.file.path}`,
5577
+ disabled: disabled || isBusy,
5578
+ onClick: onEdit,
5579
+ title: "Edit working-tree file",
5580
+ type: "button",
5581
+ children: [/* @__PURE__ */ jsx(PencilSimpleIcon, {
5582
+ "aria-hidden": true,
5583
+ size: 14,
5584
+ weight: "bold"
5585
+ }), "Edit"]
5586
+ });
5587
+ }
5588
+ function CodeViewHeader({ allowViewedToggle, canCreateFileComment, editActions, isEditing = false, isSectionLoading, meta, onCreateFileComment, onLoadSection, onOpenFile, onToggleCollapsed, onToggleMarkdownPreview, onToggleViewed, readOnly }) {
5099
5589
  const { canRenderMarkdown, file, isCollapsed, isMarkdownPreview, isSelected, isViewed, lineCount, reviewIdentity, section, sectionCount, walkthroughNote } = meta;
5100
5590
  const canOpenFile = file.status !== "deleted";
5101
5591
  const canLoadSection = shouldLoadDiffSectionContents(section);
5592
+ const directoryEnd = file.path.lastIndexOf("/") + 1;
5102
5593
  return /* @__PURE__ */ jsxs("div", {
5103
5594
  className: `codiff-file-header${walkthroughNote ? " with-note" : ""}${isCollapsed ? " collapsed" : ""}${isSelected ? " selected" : ""}${isViewed ? " viewed" : ""}`,
5104
- children: [
5105
- /* @__PURE__ */ jsxs("div", {
5106
- "aria-expanded": !isCollapsed,
5107
- "aria-label": isCollapsed ? "Expand file" : "Collapse file",
5108
- className: "codiff-header-toggle",
5109
- onClick: () => onToggleCollapsed(file, isCollapsed, reviewIdentity.key),
5110
- onKeyDown: (event) => {
5111
- if (event.key === "Enter" || event.key === " ") {
5112
- event.preventDefault();
5113
- onToggleCollapsed(file, isCollapsed, reviewIdentity.key);
5114
- }
5115
- },
5116
- role: "button",
5117
- tabIndex: 0,
5118
- title: isCollapsed ? "Expand" : "Collapse",
5595
+ children: [/* @__PURE__ */ jsxs("div", {
5596
+ "aria-expanded": !isCollapsed,
5597
+ "aria-label": isCollapsed ? "Expand file" : "Collapse file",
5598
+ className: "codiff-header-toggle",
5599
+ onClick: () => onToggleCollapsed(file, isCollapsed, reviewIdentity.key),
5600
+ onKeyDown: (event) => {
5601
+ if (event.key === "Enter" || event.key === " ") {
5602
+ event.preventDefault();
5603
+ onToggleCollapsed(file, isCollapsed, reviewIdentity.key);
5604
+ }
5605
+ },
5606
+ role: "button",
5607
+ tabIndex: 0,
5608
+ title: isCollapsed ? "Expand" : "Collapse",
5609
+ children: [/* @__PURE__ */ jsx("span", {
5610
+ className: "codiff-chevron-box",
5611
+ children: /* @__PURE__ */ jsx(CaretDownIcon, {
5612
+ "aria-hidden": true,
5613
+ className: isCollapsed ? "codiff-chevron collapsed" : "codiff-chevron",
5614
+ size: 16,
5615
+ weight: "bold"
5616
+ })
5617
+ }), /* @__PURE__ */ jsxs("span", {
5618
+ className: "codiff-file-heading",
5119
5619
  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
5620
  /* @__PURE__ */ jsxs("span", {
5130
- className: "codiff-file-heading",
5621
+ className: "codiff-file-path-row",
5131
5622
  children: [
5132
5623
  /* @__PURE__ */ jsxs("span", {
5133
- className: "codiff-file-path-row",
5134
- children: [/* @__PURE__ */ jsx("span", {
5135
- className: "codiff-file-path",
5136
- children: file.path
5137
- }), /* @__PURE__ */ jsx(CopyFilePathButton, { path: file.path })]
5624
+ className: "codiff-file-path",
5625
+ title: file.path,
5626
+ children: [directoryEnd > 0 ? /* @__PURE__ */ jsx("span", {
5627
+ className: "codiff-file-directory",
5628
+ children: file.path.slice(0, directoryEnd)
5629
+ }) : null, /* @__PURE__ */ jsx("span", {
5630
+ className: "codiff-file-name",
5631
+ children: file.path.slice(directoryEnd)
5632
+ })]
5138
5633
  }),
5139
- file.oldPath ? /* @__PURE__ */ jsx("span", {
5140
- className: "codiff-file-old-path",
5141
- children: file.oldPath
5142
- }) : null,
5143
- walkthroughNote ? /* @__PURE__ */ jsx("span", {
5144
- className: "codiff-file-note",
5145
- children: walkthroughNote.reason
5634
+ /* @__PURE__ */ jsx(CopyFilePathButton, { path: file.path }),
5635
+ !readOnly && onOpenFile ? /* @__PURE__ */ jsx("button", {
5636
+ "aria-label": `Open ${file.path} in editor`,
5637
+ className: "codiff-open-file-button",
5638
+ disabled: !canOpenFile,
5639
+ onClick: (event) => {
5640
+ event.stopPropagation();
5641
+ onOpenFile(file);
5642
+ },
5643
+ onKeyDown: (event) => {
5644
+ if (event.key === "Enter" || event.key === " ") event.stopPropagation();
5645
+ },
5646
+ title: canOpenFile ? "Open file in editor" : "Deleted files cannot be opened",
5647
+ type: "button",
5648
+ children: /* @__PURE__ */ jsx(ArrowSquareOutIcon, {
5649
+ "aria-hidden": true,
5650
+ size: 16,
5651
+ weight: "bold"
5652
+ })
5146
5653
  }) : null
5147
5654
  ]
5148
5655
  }),
5149
- sectionCount > 1 ? /* @__PURE__ */ jsx("span", {
5150
- className: `codiff-section-badge ${section.kind}`,
5151
- children: sectionLabel[section.kind]
5656
+ file.oldPath ? /* @__PURE__ */ jsx("span", {
5657
+ className: "codiff-file-old-path",
5658
+ children: file.oldPath
5152
5659
  }) : null,
5153
- isGeneratedWalkthroughFile(file) ? /* @__PURE__ */ jsx("span", {
5154
- className: "codiff-generated-badge",
5155
- title: "Generated file",
5156
- children: "Generated"
5660
+ walkthroughNote ? /* @__PURE__ */ jsx("span", {
5661
+ className: "codiff-file-note",
5662
+ children: walkthroughNote.reason
5157
5663
  }) : null
5158
5664
  ]
5159
- }),
5160
- /* @__PURE__ */ jsx(DiffLineCountBadge, { lineCount }),
5161
- /* @__PURE__ */ jsx("div", {
5162
- className: `codiff-status-badge ${file.status}`,
5163
- children: statusLabel[file.status]
5164
- }),
5165
- canCreateFileComment ? /* @__PURE__ */ jsxs(Button, {
5166
- className: "codiff-file-comment-button",
5167
- onClick: onCreateFileComment,
5168
- title: "Comment on file",
5169
- type: "button",
5170
- children: [/* @__PURE__ */ jsx(ChatCircleIcon, {
5171
- "aria-hidden": true,
5172
- className: "codiff-file-comment-icon",
5173
- size: 14,
5174
- weight: "bold"
5175
- }), "Comment"]
5176
- }) : null,
5177
- canRenderMarkdown ? /* @__PURE__ */ jsx(Button, {
5178
- "aria-pressed": isMarkdownPreview,
5179
- className: `codiff-markdown-button${isMarkdownPreview ? " active" : ""}`,
5180
- onClick: () => onToggleMarkdownPreview(file, section),
5181
- title: isMarkdownPreview ? "View as Diff" : "View as Markdown",
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,
5665
+ })]
5666
+ }), /* @__PURE__ */ jsxs("div", {
5667
+ className: "codiff-file-details",
5668
+ children: [
5669
+ /* @__PURE__ */ jsx(DiffLineCountBadge, { lineCount }),
5670
+ sectionCount > 1 ? /* @__PURE__ */ jsx("span", {
5671
+ className: `codiff-section-badge ${section.kind}`,
5672
+ children: sectionLabel[section.kind]
5673
+ }) : null,
5674
+ isGeneratedWalkthroughFile(file) ? /* @__PURE__ */ jsx("span", {
5675
+ className: "codiff-generated-badge",
5676
+ title: "Generated file",
5677
+ children: "Generated"
5678
+ }) : null,
5679
+ canCreateFileComment ? /* @__PURE__ */ jsxs(Button, {
5680
+ className: "codiff-file-comment-button",
5681
+ onClick: onCreateFileComment,
5682
+ title: "Comment on file",
5683
+ type: "button",
5684
+ children: [/* @__PURE__ */ jsx(ChatCircleIcon, {
5685
+ "aria-hidden": true,
5686
+ className: "codiff-file-comment-icon",
5687
+ size: 14,
5211
5688
  weight: "bold"
5212
- }) : null
5213
- }), "Viewed"]
5214
- }) : null
5215
- ]
5689
+ }), "Comment"]
5690
+ }) : null,
5691
+ canRenderMarkdown ? /* @__PURE__ */ jsx(Button, {
5692
+ "aria-pressed": isMarkdownPreview,
5693
+ className: `codiff-markdown-button${isMarkdownPreview ? " active" : ""}`,
5694
+ disabled: isEditing,
5695
+ onClick: () => onToggleMarkdownPreview(file, section),
5696
+ title: isMarkdownPreview ? "View as Diff" : "View as Markdown",
5697
+ type: "button",
5698
+ children: isMarkdownPreview ? "View as Diff" : "View as Markdown"
5699
+ }) : null,
5700
+ editActions,
5701
+ canLoadSection && !readOnly ? /* @__PURE__ */ jsx("button", {
5702
+ className: "codiff-load-button",
5703
+ disabled: isSectionLoading,
5704
+ onClick: () => onLoadSection(file, section),
5705
+ title: isSectionLoading ? "Loading file contents" : "Load file contents",
5706
+ type: "button",
5707
+ children: isSectionLoading ? "Loading..." : "Load"
5708
+ }) : null,
5709
+ !readOnly || allowViewedToggle ? /* @__PURE__ */ jsxs("button", {
5710
+ "aria-pressed": isViewed,
5711
+ className: `codiff-viewed-button${isViewed ? " active" : ""}`,
5712
+ onClick: () => onToggleViewed(file, isViewed, reviewIdentity),
5713
+ type: "button",
5714
+ children: [/* @__PURE__ */ jsx("span", {
5715
+ "aria-hidden": true,
5716
+ className: "codiff-viewed-checkbox",
5717
+ children: isViewed ? /* @__PURE__ */ jsx(CheckIcon, {
5718
+ className: "codiff-viewed-check",
5719
+ size: 10,
5720
+ weight: "bold"
5721
+ }) : null
5722
+ }), "Viewed"]
5723
+ }) : null
5724
+ ]
5725
+ })]
5216
5726
  });
5217
5727
  }
5218
5728
  const getReviewAuthorDisplayName = (author) => author.name || author.login || "Git user";
@@ -6701,8 +7211,27 @@ const getHunkSelectionRange = (hunk) => {
6701
7211
  } : null;
6702
7212
  };
6703
7213
  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 }) {
7214
+ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedToggle = false, blocks, bottomInset = codeViewLayout.paddingBottom, codeEditingStore, codeQualityFindings = [], collapsed, comments, commitMetadata, diffLineHeight = 20, diffStyle, disableWorkerPool = false, expandedReviewKeys = emptyExpandedReviewKeys, expandUnchanged = false, 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
7215
  const codeViewRef = useRef(null);
7216
+ const [codeViewElement, setCodeViewElement] = useState(null);
7217
+ const [compactHeaders, setCompactHeaders] = useState(false);
7218
+ useLayoutEffect(() => {
7219
+ if (!codeViewElement) return;
7220
+ const measure = () => {
7221
+ const width = codeViewElement.getBoundingClientRect().width;
7222
+ if (width > 0) setCompactHeaders(width <= 760);
7223
+ };
7224
+ measure();
7225
+ const observer = new ResizeObserver(measure);
7226
+ observer.observe(codeViewElement);
7227
+ return () => observer.disconnect();
7228
+ }, [codeViewElement]);
7229
+ const headerHeight = compactHeaders ? 90 : codeViewItemMetrics.diffHeaderHeight;
7230
+ const itemMetrics = useMemo(() => ({
7231
+ ...codeViewItemMetrics,
7232
+ diffHeaderHeight: headerHeight
7233
+ }), [headerHeight]);
7234
+ const codeViewStyle = { "--codiff-file-header-height": `${headerHeight}px` };
6706
7235
  const getPlaceholderFile = useCodeViewPlaceholderFile();
6707
7236
  const getAnnotations = useCodeViewAnnotations();
6708
7237
  const [markdownPreviewHeights] = useState(() => /* @__PURE__ */ new Map());
@@ -6718,6 +7247,7 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
6718
7247
  const focusedCommentEditorIdRef = useRef(null);
6719
7248
  const definitionHighlightFrameRef = useRef(null);
6720
7249
  const highlightFrameRef = useRef(null);
7250
+ const lastScrolledSearchMatchRef = useRef(null);
6721
7251
  const ignoreNextLineSelectionEndRef = useRef(false);
6722
7252
  const navigatedSelectionRef = useRef(null);
6723
7253
  const initialMarkdownFiles = files.length > 0 ? files : blocks?.map((block) => block.file).filter((file) => file != null) ?? [];
@@ -7013,6 +7543,91 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7013
7543
  shouldShowCommitMessage,
7014
7544
  sourceDescriptionItemId
7015
7545
  ]);
7546
+ const highlightedCodeItems = codeViewItems.filter((item) => itemMetadata.get(item.id)?.isSelected);
7547
+ 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;
7548
+ const codeEditing = useCodeEditing({
7549
+ codeViewRef,
7550
+ items: codeViewItems,
7551
+ keymap,
7552
+ onLoadSectionContents,
7553
+ onRefresh: onRefreshMarkdown,
7554
+ selectedItemId: selectedCodeItemId,
7555
+ sourceKey,
7556
+ store: codeEditingStore
7557
+ });
7558
+ 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, [
7559
+ isReadOnly,
7560
+ onLoadSectionContents,
7561
+ source.type
7562
+ ]);
7563
+ const startCodeEdit = useCallback((item, meta) => {
7564
+ if (meta.isCollapsed) onToggleCollapsed(meta.file, true, meta.reviewIdentity.key);
7565
+ codeEditing.startEdit(item, meta);
7566
+ }, [codeEditing, onToggleCollapsed]);
7567
+ const editAfterExpandRef = useRef(null);
7568
+ useEffect(() => {
7569
+ const pending = editAfterExpandRef.current;
7570
+ if (!pending) return;
7571
+ if (pending.sourceKey !== sourceKey || !highlightedCodeItems.some((item) => itemMetadata.get(item.id)?.file.path === pending.path)) {
7572
+ editAfterExpandRef.current = null;
7573
+ return;
7574
+ }
7575
+ const item = highlightedCodeItems.find((candidate) => {
7576
+ const meta = itemMetadata.get(candidate.id);
7577
+ return meta?.file.path === pending.path && canEditCodeItem(candidate, meta);
7578
+ });
7579
+ const meta = item && itemMetadata.get(item.id);
7580
+ if (item && meta) {
7581
+ editAfterExpandRef.current = null;
7582
+ startCodeEdit(item, meta);
7583
+ }
7584
+ }, [
7585
+ canEditCodeItem,
7586
+ highlightedCodeItems,
7587
+ itemMetadata,
7588
+ sourceKey,
7589
+ startCodeEdit
7590
+ ]);
7591
+ useEffect(() => {
7592
+ const handleKeyDown = (event) => {
7593
+ if (event.defaultPrevented || event.repeat || event.isComposing || isNativeInputTarget(event.target) || !matchesShortcut(event, keymap, "editFile")) return;
7594
+ const item = codeViewItems.find((candidate) => candidate.id === selectedCodeItemId);
7595
+ const meta = item && itemMetadata.get(item.id);
7596
+ if (!item || !meta || !codeEditing.canStartEdit(item.id, meta.file.path)) return;
7597
+ if (!canEditCodeItem(item, meta)) {
7598
+ if (!meta.isCollapsed || !getVisibleDiffSections(meta.file, showWhitespace).some(({ fileDiff, section }) => canEditCodeItem({
7599
+ fileDiff,
7600
+ id: item.id,
7601
+ type: "diff"
7602
+ }, {
7603
+ ...meta,
7604
+ section
7605
+ }))) return;
7606
+ event.preventDefault();
7607
+ editAfterExpandRef.current = {
7608
+ path: meta.file.path,
7609
+ sourceKey
7610
+ };
7611
+ onToggleCollapsed(meta.file, true, meta.reviewIdentity.key);
7612
+ return;
7613
+ }
7614
+ event.preventDefault();
7615
+ startCodeEdit(item, meta);
7616
+ };
7617
+ window.addEventListener("keydown", handleKeyDown);
7618
+ return () => window.removeEventListener("keydown", handleKeyDown);
7619
+ }, [
7620
+ canEditCodeItem,
7621
+ codeEditing,
7622
+ codeViewItems,
7623
+ itemMetadata,
7624
+ keymap,
7625
+ onToggleCollapsed,
7626
+ selectedCodeItemId,
7627
+ showWhitespace,
7628
+ sourceKey,
7629
+ startCodeEdit
7630
+ ]);
7016
7631
  const clearCommentLineHighlight = useCallback(() => {
7017
7632
  codeViewRef.current?.clearSelectedLines();
7018
7633
  navigatedSelectionRef.current = null;
@@ -7192,10 +7807,10 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7192
7807
  diffStyle,
7193
7808
  enableGutterUtility: !isReadOnly,
7194
7809
  enableLineSelection: !isReadOnly,
7195
- expandUnchanged: false,
7810
+ expandUnchanged: expandUnchanged && loadDiffFiles != null,
7196
7811
  expansionLineCount: 100,
7197
7812
  hunkSeparators: "line-info-basic",
7198
- itemMetrics: codeViewItemMetrics,
7813
+ itemMetrics,
7199
7814
  layout: {
7200
7815
  ...codeViewLayout,
7201
7816
  paddingBottom: bottomInset
@@ -7203,11 +7818,12 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7203
7818
  lineHoverHighlight: "both",
7204
7819
  loadDiffFiles,
7205
7820
  onGutterUtilityClick: (range, context) => {
7206
- if (isReadOnly) return;
7821
+ if (isReadOnly || context.item.edit) return;
7207
7822
  ignoreNextLineSelectionEndRef.current = context.item.type === "diff";
7208
7823
  createCommentForRange(range, context);
7209
7824
  },
7210
7825
  onLineClick: (line, context) => {
7826
+ if (context.item.edit) return;
7211
7827
  const lineElement = "lineElement" in line ? line.lineElement : null;
7212
7828
  if (isPrimaryModifier(line.event) && onFindDefinitions) {
7213
7829
  line.event.preventDefault();
@@ -7272,7 +7888,7 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7272
7888
  });
7273
7889
  },
7274
7890
  onLineSelectionEnd: (range, context) => {
7275
- if (isReadOnly) return;
7891
+ if (isReadOnly || context.item.edit) return;
7276
7892
  if (ignoreNextLineSelectionEndRef.current) {
7277
7893
  ignoreNextLineSelectionEndRef.current = false;
7278
7894
  return;
@@ -7287,10 +7903,10 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7287
7903
  node.classList.toggle("codiff-selected-item", metadata?.isSelected === true || selectedHeaderItemIds.has(context.item.id));
7288
7904
  node.classList.toggle("codiff-markdown-preview-item", metadata?.isMarkdownPreview === true);
7289
7905
  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));
7906
+ node.classList.toggle("codiff-loadable-summary-item", !context.item.edit && metadata != null && shouldLoadDiffSectionContents(metadata.section));
7291
7907
  node.classList.toggle("codiff-loading-summary-item", Boolean(metadata && loadingSectionIds.has(metadata.section.id)));
7292
7908
  if (definitionModifierActiveRef.current) window.requestAnimationFrame(() => {
7293
- if (definitionModifierActiveRef.current && node.isConnected) applyIdentifierNavigationState([{ element: node }], true);
7909
+ if (definitionModifierActiveRef.current && node.isConnected && !codeViewRef.current?.getItem(context.item.id)?.edit) applyIdentifierNavigationState([{ element: node }], true);
7294
7910
  });
7295
7911
  },
7296
7912
  overflow: wordWrap ? "wrap" : "scroll",
@@ -7301,14 +7917,17 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7301
7917
  },
7302
7918
  themeType: theme,
7303
7919
  tokenizeMaxLength: 1e5,
7304
- unsafeCSS: codeViewUnsafeCSS
7920
+ unsafeCSS: codeViewUnsafeCSS,
7921
+ useTokenTransformer: workerHighlighterOptions.useTokenTransformer
7305
7922
  }), [
7306
7923
  bottomInset,
7307
7924
  cancelPendingEmptyCommentDeletes,
7308
7925
  createCommentForRange,
7309
7926
  diffStyle,
7927
+ expandUnchanged,
7310
7928
  isReadOnly,
7311
7929
  itemMetadata,
7930
+ itemMetrics,
7312
7931
  loadDiffFiles,
7313
7932
  loadingSectionIds,
7314
7933
  onCreateComment,
@@ -7478,7 +8097,6 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7478
8097
  const viewer = handle?.getInstance();
7479
8098
  if (!handle || !viewer) return;
7480
8099
  handledHunkNavRef.current = hunkNavigation.request;
7481
- const headerHeight = codeViewItemMetrics.diffHeaderHeight;
7482
8100
  const anchors = [];
7483
8101
  const seen = /* @__PURE__ */ new Set();
7484
8102
  const lineScrollTarget = (id, lineNumber, side) => ({
@@ -7597,6 +8215,7 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7597
8215
  clearCommentLineHighlight,
7598
8216
  diffLineHeight,
7599
8217
  diffStyle,
8218
+ headerHeight,
7600
8219
  hunkNavigation,
7601
8220
  itemMetadata,
7602
8221
  items
@@ -7625,7 +8244,7 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7625
8244
  if (highlightFrameRef.current != null) window.cancelAnimationFrame(highlightFrameRef.current);
7626
8245
  highlightFrameRef.current = window.requestAnimationFrame(() => {
7627
8246
  highlightFrameRef.current = null;
7628
- applySearchHighlights(viewer.getRenderedItems(), searchQuery, resolvedActiveSearchMatch);
8247
+ applySearchHighlights(viewer.getRenderedItems().filter(({ item }) => !item.edit), searchQuery, resolvedActiveSearchMatch);
7629
8248
  });
7630
8249
  }, [resolvedActiveSearchMatch, searchQuery]);
7631
8250
  const updateRenderedIdentifierNavigation = useCallback((active, viewer) => {
@@ -7636,12 +8255,12 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7636
8255
  definitionHighlightFrameRef.current = null;
7637
8256
  }
7638
8257
  if (!active) {
7639
- applyIdentifierNavigationState(nextViewer.getRenderedItems(), false);
8258
+ applyIdentifierNavigationState(nextViewer.getRenderedItems().filter(({ item }) => !item.edit), false);
7640
8259
  return;
7641
8260
  }
7642
8261
  definitionHighlightFrameRef.current = window.requestAnimationFrame(() => {
7643
8262
  definitionHighlightFrameRef.current = null;
7644
- applyIdentifierNavigationState(nextViewer.getRenderedItems(), true);
8263
+ applyIdentifierNavigationState(nextViewer.getRenderedItems().filter(({ item }) => !item.edit), true);
7645
8264
  });
7646
8265
  }, []);
7647
8266
  const scheduleStickyHeaderStateUpdate = useCallback((viewer) => {
@@ -7716,9 +8335,22 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7716
8335
  }, []);
7717
8336
  useEffect(() => invalidateDefinitionLookup, [invalidateDefinitionLookup]);
7718
8337
  useEffect(() => {
8338
+ if (!activeSearchMatch) {
8339
+ lastScrolledSearchMatchRef.current = null;
8340
+ return;
8341
+ }
7719
8342
  const handle = codeViewRef.current;
7720
8343
  const viewer = handle?.getInstance();
7721
8344
  if (!handle || !viewer || !resolvedActiveSearchMatch) return;
8345
+ const lastScrolledSearchMatch = lastScrolledSearchMatchRef.current;
8346
+ if (lastScrolledSearchMatch?.match === activeSearchMatch && lastScrolledSearchMatch.itemId === resolvedActiveSearchMatch.itemId) {
8347
+ scheduleSearchHighlights();
8348
+ return;
8349
+ }
8350
+ lastScrolledSearchMatchRef.current = {
8351
+ itemId: resolvedActiveSearchMatch.itemId,
8352
+ match: activeSearchMatch
8353
+ };
7722
8354
  if (resolvedActiveSearchMatch.lineNumber == null) handle.scrollTo({
7723
8355
  align: "center",
7724
8356
  behavior: "smooth-auto",
@@ -7735,7 +8367,11 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7735
8367
  type: "line"
7736
8368
  });
7737
8369
  scheduleSearchHighlights();
7738
- }, [resolvedActiveSearchMatch, scheduleSearchHighlights]);
8370
+ }, [
8371
+ activeSearchMatch,
8372
+ resolvedActiveSearchMatch,
8373
+ scheduleSearchHighlights
8374
+ ]);
7739
8375
  const renderCodeViewHeader = useCallback(() => /* @__PURE__ */ jsxs("div", {
7740
8376
  className: "codiff-source-description-panel codiff-code-view-source-description",
7741
8377
  children: [/* @__PURE__ */ jsx(SourceDescriptionHeader, {
@@ -7783,10 +8419,47 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7783
8419
  toggleSourceDescriptionCollapsed
7784
8420
  ]);
7785
8421
  const renderCustomHeader = useCallback((item) => {
7786
- const meta = itemMetadata.get(item.id);
7787
- return meta ? /* @__PURE__ */ jsx(CodeViewHeader, {
8422
+ const editSession = codeEditing.sessions.get(item.id);
8423
+ const isEditing = editSession != null;
8424
+ const meta = editSession?.metadata ?? itemMetadata.get(item.id);
8425
+ const isBusy = codeEditing.isBusy(item.id);
8426
+ const isAutosaving = editSession?.saving && !isBusy;
8427
+ const canEdit = canEditCodeItem(item, meta);
8428
+ const editError = codeEditing.getError(item.id);
8429
+ return meta ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(CodeViewHeader, {
7788
8430
  allowViewedToggle,
7789
8431
  canCreateFileComment: canCreateFileComments,
8432
+ editActions: isEditing ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Button, {
8433
+ "aria-label": `${editSession?.hasChanges ? "Revert edits to" : "Cancel editing"} ${meta.file.path}`,
8434
+ className: "codiff-edit-revert-button",
8435
+ "data-autosaving": isAutosaving ? "" : void 0,
8436
+ disabled: isBusy || editSession.saving,
8437
+ onClick: () => void codeEditing.revertEdit(item.id),
8438
+ title: editSession?.hasChanges ? "Restore the file to when editing started" : "Cancel editing",
8439
+ type: "button",
8440
+ variant: editSession?.hasChanges ? "destructive" : "default",
8441
+ children: editSession?.hasChanges ? "Revert" : "Cancel"
8442
+ }), /* @__PURE__ */ jsxs(Button, {
8443
+ "aria-label": `Done editing ${meta.file.path}`,
8444
+ "data-autosaving": isAutosaving ? "" : void 0,
8445
+ disabled: isBusy || editSession.saving,
8446
+ onClick: () => void codeEditing.doneEdit(item.id),
8447
+ title: "Finish editing; changes are saved automatically",
8448
+ type: "button",
8449
+ children: [/* @__PURE__ */ jsx(CheckIcon, {
8450
+ "aria-hidden": true,
8451
+ size: 14,
8452
+ weight: "bold"
8453
+ }), "Done"]
8454
+ })] }) : canEdit ? /* @__PURE__ */ jsx(CodeEditButton, {
8455
+ disabled: !codeEditing.canStartEdit(item.id, meta.file.path),
8456
+ isBusy,
8457
+ item,
8458
+ meta,
8459
+ onEdit: () => startCodeEdit(item, meta),
8460
+ onPrepare: codeEditing.prepareEdit
8461
+ }) : null,
8462
+ isEditing,
7790
8463
  isSectionLoading: loadingSectionIds.has(meta.section.id),
7791
8464
  meta,
7792
8465
  onCreateFileComment: () => createFileComment(meta, item.id),
@@ -7796,11 +8469,17 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7796
8469
  onToggleMarkdownPreview: toggleMarkdownPreview,
7797
8470
  onToggleViewed,
7798
8471
  readOnly: isReadOnly
7799
- }) : null;
8472
+ }), editError ? /* @__PURE__ */ jsx("div", {
8473
+ className: "codiff-code-edit-error",
8474
+ role: "alert",
8475
+ children: editError
8476
+ }) : null] }) : null;
7800
8477
  }, [
7801
8478
  allowViewedToggle,
7802
8479
  canCreateFileComments,
8480
+ canEditCodeItem,
7803
8481
  createFileComment,
8482
+ codeEditing,
7804
8483
  itemMetadata,
7805
8484
  isReadOnly,
7806
8485
  loadingSectionIds,
@@ -7808,6 +8487,7 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7808
8487
  onOpenFile,
7809
8488
  onToggleCollapsed,
7810
8489
  onToggleViewed,
8490
+ startCodeEdit,
7811
8491
  toggleMarkdownPreview
7812
8492
  ]);
7813
8493
  const renderAnnotation = useCallback((annotation, item) => {
@@ -7909,10 +8589,14 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7909
8589
  scheduleStickyHeaderStateUpdate,
7910
8590
  updateRenderedIdentifierNavigation
7911
8591
  ]);
7912
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [disableWorkerPool ? /* @__PURE__ */ jsx(CodeView, {
7913
- className: "code-view",
8592
+ const codeView = /* @__PURE__ */ jsx(CodeView, {
8593
+ className: `code-view${compactHeaders ? " compact-file-headers" : ""}`,
8594
+ containerRef: setCodeViewElement,
7914
8595
  disableWorkerPool,
7915
- items: codeViewItems,
8596
+ editorOptions: codeEditing.editorOptions,
8597
+ items: codeEditing.items,
8598
+ onItemEditChange: codeEditing.onItemEditChange,
8599
+ onItemEditComplete: codeEditing.onItemEditComplete,
7916
8600
  onScroll: handleScroll,
7917
8601
  onSelectedLinesChange: setCodeViewSelectedLines,
7918
8602
  options: codeViewOptions,
@@ -7920,14 +8604,20 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7920
8604
  renderAnnotation,
7921
8605
  renderCodeViewHeader: sourceDescriptionItemId ? renderCodeViewHeader : void 0,
7922
8606
  renderCustomHeader,
7923
- selectedLines: isReadOnly ? null : selectedLines
7924
- }) : /* @__PURE__ */ jsx(WorkerPoolContextProvider, {
8607
+ selectedLines: isReadOnly ? null : selectedLines,
8608
+ style: codeViewStyle
8609
+ });
8610
+ const renderedCodeView = disableWorkerPool ? codeView : /* @__PURE__ */ jsx(WorkerPoolContextProvider, {
7925
8611
  highlighterOptions: workerHighlighterOptions,
7926
8612
  poolOptions: workerPoolOptions,
7927
8613
  children: /* @__PURE__ */ jsx(CodeView, {
7928
- className: "code-view",
8614
+ className: `code-view${compactHeaders ? " compact-file-headers" : ""}`,
8615
+ containerRef: setCodeViewElement,
7929
8616
  disableWorkerPool: false,
7930
- items: codeViewItems,
8617
+ editorOptions: codeEditing.editorOptions,
8618
+ items: codeEditing.items,
8619
+ onItemEditChange: codeEditing.onItemEditChange,
8620
+ onItemEditComplete: codeEditing.onItemEditComplete,
7931
8621
  onScroll: handleScroll,
7932
8622
  onSelectedLinesChange: setCodeViewSelectedLines,
7933
8623
  options: codeViewOptions,
@@ -7935,8 +8625,13 @@ function ReviewCodeView({ activeSearchMatch, agentId, agentLabel, allowViewedTog
7935
8625
  renderAnnotation,
7936
8626
  renderCodeViewHeader: sourceDescriptionItemId ? renderCodeViewHeader : void 0,
7937
8627
  renderCustomHeader,
7938
- selectedLines: isReadOnly ? null : selectedLines
8628
+ selectedLines: isReadOnly ? null : selectedLines,
8629
+ style: codeViewStyle
7939
8630
  })
8631
+ });
8632
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(EditProvider, {
8633
+ createEditor: createCodeEditor,
8634
+ children: renderedCodeView
7940
8635
  }), definitionLookup?.sourceKey === sourceKey && onOpenDefinition ? /* @__PURE__ */ jsx(DefinitionPopover, {
7941
8636
  anchor: definitionLookup.anchor,
7942
8637
  getDestination: (candidate) => getDefinitionDiffTarget(candidate) ? "diff" : candidate.canOpenInEditor ? "editor" : "unavailable",
@@ -9653,54 +10348,6 @@ function useReviewFileState({ initialSelectedPath = null, onViewedChange } = {})
9653
10348
  };
9654
10349
  }
9655
10350
  //#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
10351
  //#region lib/app-constants.ts
9705
10352
  const AGENT_LABELS = {
9706
10353
  claude: "Claude Code",