@orion-agents/orion-code 0.3.24 → 0.3.26
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/CHANGELOG.md +60 -4
- package/README.md +8 -5
- package/README.zh-CN.md +6 -4
- package/dist/runtime/legacy-thread-materializer.d.ts +13 -0
- package/dist/runtime/legacy-thread-materializer.js +72 -0
- package/dist/runtime/model-coordinator.d.ts +1 -0
- package/dist/runtime/model-coordinator.js +9 -2
- package/dist/runtime/session-composer-control.js +5 -1
- package/dist/services/session-storage.js +21 -0
- package/dist/services/workspace-registry.d.ts +9 -0
- package/dist/services/workspace-registry.js +13 -2
- package/dist/web/git-diff-document.d.ts +19 -0
- package/dist/web/git-diff-document.js +46 -2
- package/dist/web/git-history-service.js +3 -0
- package/dist/web/git-read-model-service.d.ts +9 -0
- package/dist/web/git-read-model-service.js +111 -5
- package/dist/web/git-version-service.d.ts +8 -0
- package/dist/web/git-version-service.js +50 -26
- package/dist/web/protocol.d.ts +5 -0
- package/dist/web/workbench-controller.js +1 -0
- package/dist/web-client/assets/{FilesPanel-BjEDjE7l.js → FilesPanel-CdoCa8u0.js} +1 -1
- package/dist/web-client/assets/{GitPanel-DvgAZDRA.js → GitPanel-CRr_iIDz.js} +1 -1
- package/dist/web-client/assets/{ReviewPanel-BMPaYKDY.js → ReviewPanel-C1Cu2IRV.js} +1 -1
- package/dist/web-client/assets/{TerminalPanel-Da1ZEhGF.js → TerminalPanel-BniCjatZ.js} +1 -1
- package/dist/web-client/assets/{index-DZOlUgt6.js → index-kQqA3li6.js} +11 -11
- package/dist/web-client/assets/{useAutoNotice-Ylj5bfSH.js → useAutoNotice-DPXFWIwK.js} +1 -1
- package/dist/web-client/index.html +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ exports.assertRelativeGitPath = assertRelativeGitPath;
|
|
|
6
6
|
const child_process_1 = require("child_process");
|
|
7
7
|
const crypto_1 = require("crypto");
|
|
8
8
|
const fs_1 = require("fs");
|
|
9
|
+
const promises_1 = require("fs/promises");
|
|
9
10
|
const path_1 = require("path");
|
|
10
11
|
const redaction_1 = require("../services/redaction");
|
|
11
12
|
const errors_1 = require("./errors");
|
|
@@ -235,7 +236,13 @@ class GitReadModelServiceV1 {
|
|
|
235
236
|
*/
|
|
236
237
|
async diffDocument(input) {
|
|
237
238
|
const page = await this.diff(input);
|
|
238
|
-
|
|
239
|
+
// v0.3.26 #275 — the incoming cursor may carry the hunk state this page
|
|
240
|
+
// continues; diff() already validated the cursor, so decoding it here only
|
|
241
|
+
// reads the resume state.
|
|
242
|
+
const resumeHunk = input.cursor
|
|
243
|
+
? this.decodeCursor(input.cursor, 'diff', page.repositoryRevision, page.fileId, diffScope(input.ignoreWhitespace === true, input.wordDiff === true)).resumeHunk
|
|
244
|
+
: undefined;
|
|
245
|
+
const document = (0, git_diff_document_1.parseGitDiffDocument)({
|
|
239
246
|
fileToken: page.fileId,
|
|
240
247
|
path: page.path,
|
|
241
248
|
source: page.source,
|
|
@@ -243,7 +250,18 @@ class GitReadModelServiceV1 {
|
|
|
243
250
|
lines: page.lines,
|
|
244
251
|
hasMore: page.nextCursor !== null,
|
|
245
252
|
nextCursor: page.nextCursor,
|
|
253
|
+
...(resumeHunk ? { resume: resumeHunk } : {}),
|
|
246
254
|
});
|
|
255
|
+
// A page that ends inside a hunk re-mints the cursor with the hunk state so
|
|
256
|
+
// the next page resumes it instead of demoting the rows to meta.
|
|
257
|
+
if (page.nextCursor && document.pendingHunk) {
|
|
258
|
+
const decoded = this.decodeCursor(page.nextCursor, 'diff', page.repositoryRevision, page.fileId, diffScope(input.ignoreWhitespace === true, input.wordDiff === true));
|
|
259
|
+
return Object.freeze({
|
|
260
|
+
...document,
|
|
261
|
+
nextCursor: this.encodeCursor({ ...decoded, resumeHunk: document.pendingHunk }),
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
return document;
|
|
247
265
|
}
|
|
248
266
|
historyReader() {
|
|
249
267
|
this.historyReaderInstance ?? (this.historyReaderInstance = (0, git_history_service_1.createGitHistoryReader)({
|
|
@@ -299,7 +317,8 @@ class GitReadModelServiceV1 {
|
|
|
299
317
|
.update(`compare-diff:${input.baseOid}:${input.headOid}:${path}:${snapshot.revision}`)
|
|
300
318
|
.digest('hex')
|
|
301
319
|
.slice(0, 24);
|
|
302
|
-
const
|
|
320
|
+
const cursorState = input.cursor ? decodeCommitDiffCursor(input.cursor, margin) : null;
|
|
321
|
+
const offset = cursorState?.offset ?? 0;
|
|
303
322
|
const page = await streamDiffPage({
|
|
304
323
|
cwd: root,
|
|
305
324
|
commands: Object.freeze([
|
|
@@ -338,9 +357,19 @@ class GitReadModelServiceV1 {
|
|
|
338
357
|
nextCursor: page.hasMore
|
|
339
358
|
? encodeCommitDiffCursor({ margin, offset: offset + page.returnedLines })
|
|
340
359
|
: null,
|
|
360
|
+
...(cursorState?.resumeHunk ? { resume: cursorState.resumeHunk } : {}),
|
|
341
361
|
});
|
|
362
|
+
// v0.3.26 #275 — re-mint the continuation cursor with the split hunk state.
|
|
363
|
+
const nextCursor = page.hasMore && document.pendingHunk
|
|
364
|
+
? encodeCommitDiffCursor({
|
|
365
|
+
margin,
|
|
366
|
+
offset: offset + page.returnedLines,
|
|
367
|
+
resumeHunk: document.pendingHunk,
|
|
368
|
+
})
|
|
369
|
+
: document.nextCursor;
|
|
342
370
|
return Object.freeze({
|
|
343
371
|
...document,
|
|
372
|
+
nextCursor,
|
|
344
373
|
baseOid: input.baseOid,
|
|
345
374
|
baseLabel: `比较 ${input.baseOid.slice(0, 10)} → ${input.headOid.slice(0, 10)}`,
|
|
346
375
|
});
|
|
@@ -349,9 +378,43 @@ class GitReadModelServiceV1 {
|
|
|
349
378
|
this.versionReaderInstance ?? (this.versionReaderInstance = (0, git_version_service_1.createGitVersionReader)({
|
|
350
379
|
runGit: async (args) => this.runGit(args, await this.requireRepositoryRoot()),
|
|
351
380
|
runGitBytes: async (args) => this.runGitBytes(args, await this.requireRepositoryRoot()),
|
|
381
|
+
readWorktreeFile: async (relativePath) => this.readContainedWorktreeFile(relativePath),
|
|
352
382
|
}));
|
|
353
383
|
return this.versionReaderInstance;
|
|
354
384
|
}
|
|
385
|
+
/**
|
|
386
|
+
* v0.3.25 #269 — reads one working-tree file for the conflict view, refusing
|
|
387
|
+
* to leave the repository: the requested relative path (already guarded as
|
|
388
|
+
* relative and non-sensitive by the version reader) is resolved against the
|
|
389
|
+
* repository root and both the file's real path and its parent's real path
|
|
390
|
+
* must stay inside the root's real path. Returns null instead of throwing so
|
|
391
|
+
* a missing or unreadable file reads as an absent worktree version.
|
|
392
|
+
*/
|
|
393
|
+
async readContainedWorktreeFile(relativePath) {
|
|
394
|
+
let root;
|
|
395
|
+
try {
|
|
396
|
+
root = (0, fs_1.realpathSync)(await this.requireRepositoryRoot());
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
const absolute = (0, path_1.resolve)(root, relativePath);
|
|
403
|
+
const fileReal = (0, fs_1.realpathSync)(absolute);
|
|
404
|
+
const parentReal = (0, fs_1.realpathSync)((0, path_1.resolve)(fileReal, '..'));
|
|
405
|
+
if (fileReal !== root && !fileReal.startsWith(root + path_1.sep))
|
|
406
|
+
return null;
|
|
407
|
+
if (parentReal !== root && !parentReal.startsWith(root + path_1.sep))
|
|
408
|
+
return null;
|
|
409
|
+
const stat = (0, fs_1.statSync)(fileReal);
|
|
410
|
+
if (!stat.isFile())
|
|
411
|
+
return null;
|
|
412
|
+
return await (0, promises_1.readFile)(fileReal);
|
|
413
|
+
}
|
|
414
|
+
catch {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
355
418
|
/**
|
|
356
419
|
* v0.3.17 S5 — base / ours / theirs / worktree for a conflicted file.
|
|
357
420
|
*
|
|
@@ -415,7 +478,8 @@ class GitReadModelServiceV1 {
|
|
|
415
478
|
.update(`commit-diff:${detail.id}:${detail.baseOid}:${parentIndex}:${snapshot.revision}`)
|
|
416
479
|
.digest('hex')
|
|
417
480
|
.slice(0, 24);
|
|
418
|
-
const
|
|
481
|
+
const cursorState = input.cursor ? decodeCommitDiffCursor(input.cursor, margin) : null;
|
|
482
|
+
const offset = cursorState?.offset ?? 0;
|
|
419
483
|
const page = await streamDiffPage({
|
|
420
484
|
cwd: root,
|
|
421
485
|
commands: Object.freeze([
|
|
@@ -454,8 +518,22 @@ class GitReadModelServiceV1 {
|
|
|
454
518
|
nextCursor: page.hasMore
|
|
455
519
|
? encodeCommitDiffCursor({ margin, offset: offset + page.returnedLines })
|
|
456
520
|
: null,
|
|
521
|
+
...(cursorState?.resumeHunk ? { resume: cursorState.resumeHunk } : {}),
|
|
522
|
+
});
|
|
523
|
+
// v0.3.26 #275 — re-mint the continuation cursor with the split hunk state.
|
|
524
|
+
const nextCursor = page.hasMore && document.pendingHunk
|
|
525
|
+
? encodeCommitDiffCursor({
|
|
526
|
+
margin,
|
|
527
|
+
offset: offset + page.returnedLines,
|
|
528
|
+
resumeHunk: document.pendingHunk,
|
|
529
|
+
})
|
|
530
|
+
: document.nextCursor;
|
|
531
|
+
return Object.freeze({
|
|
532
|
+
...document,
|
|
533
|
+
nextCursor,
|
|
534
|
+
baseLabel: detail.baseLabel,
|
|
535
|
+
baseOid: detail.baseOid,
|
|
457
536
|
});
|
|
458
|
-
return Object.freeze({ ...document, baseLabel: detail.baseLabel, baseOid: detail.baseOid });
|
|
459
537
|
}
|
|
460
538
|
async capture() {
|
|
461
539
|
let root;
|
|
@@ -1176,6 +1254,9 @@ class GitReadModelServiceV1 {
|
|
|
1176
1254
|
parsed.offset < 0) {
|
|
1177
1255
|
return invalidCursor();
|
|
1178
1256
|
}
|
|
1257
|
+
if (parsed.resumeHunk !== undefined && !isDiffHunkResume(parsed.resumeHunk)) {
|
|
1258
|
+
return invalidCursor();
|
|
1259
|
+
}
|
|
1179
1260
|
if (parsed.revision !== revision) {
|
|
1180
1261
|
throw new errors_1.WebWorkbenchError(409, 'Git cursor revision is stale.', 'git_revision_conflict');
|
|
1181
1262
|
}
|
|
@@ -1189,6 +1270,22 @@ class GitReadModelServiceV1 {
|
|
|
1189
1270
|
}
|
|
1190
1271
|
}
|
|
1191
1272
|
exports.GitReadModelServiceV1 = GitReadModelServiceV1;
|
|
1273
|
+
/** v0.3.26 #275 — the hunk header shape, mirrored for cursor validation. */
|
|
1274
|
+
const HUNK_HEADER_PATTERN = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)$/u;
|
|
1275
|
+
/** v0.3.26 #275 — validates a cursor-carried hunk resume state. */
|
|
1276
|
+
function isDiffHunkResume(value) {
|
|
1277
|
+
if (!value || typeof value !== 'object')
|
|
1278
|
+
return false;
|
|
1279
|
+
const candidate = value;
|
|
1280
|
+
return (typeof candidate.header === 'string' &&
|
|
1281
|
+
HUNK_HEADER_PATTERN.test(candidate.header) &&
|
|
1282
|
+
Number.isSafeInteger(candidate.index) &&
|
|
1283
|
+
candidate.index >= 0 &&
|
|
1284
|
+
Number.isSafeInteger(candidate.oldCursor) &&
|
|
1285
|
+
candidate.oldCursor >= 1 &&
|
|
1286
|
+
Number.isSafeInteger(candidate.newCursor) &&
|
|
1287
|
+
candidate.newCursor >= 1);
|
|
1288
|
+
}
|
|
1192
1289
|
function parsePorcelainV1(output) {
|
|
1193
1290
|
const fields = output.split('\0');
|
|
1194
1291
|
const records = [];
|
|
@@ -1657,7 +1754,16 @@ function decodeCommitDiffCursor(cursor, margin) {
|
|
|
1657
1754
|
candidate.offset < 0) {
|
|
1658
1755
|
throw new errors_1.WebWorkbenchError(409, 'Diff changed; reload before paginating.', 'git_cursor_invalid');
|
|
1659
1756
|
}
|
|
1660
|
-
|
|
1757
|
+
if (candidate.resumeHunk !== undefined && !isDiffHunkResume(candidate.resumeHunk)) {
|
|
1758
|
+
throw new errors_1.WebWorkbenchError(400, 'Diff cursor is not valid.', 'git_cursor_invalid');
|
|
1759
|
+
}
|
|
1760
|
+
return {
|
|
1761
|
+
margin,
|
|
1762
|
+
offset: candidate.offset,
|
|
1763
|
+
...(candidate.resumeHunk !== undefined
|
|
1764
|
+
? { resumeHunk: candidate.resumeHunk }
|
|
1765
|
+
: {}),
|
|
1766
|
+
};
|
|
1661
1767
|
}
|
|
1662
1768
|
/**
|
|
1663
1769
|
* Kills a detached child and everything it spawned.
|
|
@@ -66,6 +66,14 @@ export interface GitVersionReaderOptions {
|
|
|
66
66
|
readonly runGit: (args: readonly string[]) => Promise<string>;
|
|
67
67
|
/** Runs git and returns raw bytes, for content that may not be valid UTF-8. */
|
|
68
68
|
readonly runGitBytes: (args: readonly string[]) => Promise<Buffer>;
|
|
69
|
+
/**
|
|
70
|
+
* v0.3.25 #269 — reads one working-tree file relative to the repository root,
|
|
71
|
+
* returning null when it does not exist or cannot be read safely. The
|
|
72
|
+
* `worktree` conflict version must come from the filesystem: `hash-object`
|
|
73
|
+
* without `-w` computes an OID that is not in the object database, so any
|
|
74
|
+
* edited working file made `cat-file` fail the whole conflict response.
|
|
75
|
+
*/
|
|
76
|
+
readonly readWorktreeFile?: (relativePath: string) => Promise<Buffer | null>;
|
|
69
77
|
}
|
|
70
78
|
export declare function createGitVersionReader(options: GitVersionReaderOptions): GitVersionReader;
|
|
71
79
|
//# sourceMappingURL=git-version-service.d.ts.map
|
|
@@ -85,28 +85,25 @@ function parseLfsPointer(head) {
|
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
function createGitVersionReader(options) {
|
|
88
|
-
const { runGit, runGitBytes } = options;
|
|
88
|
+
const { runGit, runGitBytes, readWorktreeFile } = options;
|
|
89
|
+
/** Absent version — the label legitimately has no content for this shape. */
|
|
90
|
+
const absentVersion = (label) => Object.freeze({
|
|
91
|
+
label,
|
|
92
|
+
oid: null,
|
|
93
|
+
exists: false,
|
|
94
|
+
binary: false,
|
|
95
|
+
byteSize: 0,
|
|
96
|
+
truncated: false,
|
|
97
|
+
content: null,
|
|
98
|
+
lfs: null,
|
|
99
|
+
});
|
|
89
100
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* being pulled into memory just to be truncated.
|
|
101
|
+
* v0.3.25 #269 — decodes already-fetched bytes into a version, shared by the
|
|
102
|
+
* object-database reader and the working-tree reader so both apply the same
|
|
103
|
+
* size bound, binary probe and LFS-pointer detection.
|
|
94
104
|
*/
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
return Object.freeze({
|
|
98
|
-
label,
|
|
99
|
-
oid: null,
|
|
100
|
-
exists: false,
|
|
101
|
-
binary: false,
|
|
102
|
-
byteSize: 0,
|
|
103
|
-
truncated: false,
|
|
104
|
-
content: null,
|
|
105
|
-
lfs: null,
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
const sizeRaw = (await runGit(['cat-file', '-s', oid])).trim();
|
|
109
|
-
const byteSize = Number(sizeRaw) || 0;
|
|
105
|
+
const versionFromBytes = (label, oid, bytes) => {
|
|
106
|
+
const byteSize = bytes.length;
|
|
110
107
|
if (byteSize > MAX_VERSION_BYTES) {
|
|
111
108
|
return Object.freeze({
|
|
112
109
|
label,
|
|
@@ -119,7 +116,6 @@ function createGitVersionReader(options) {
|
|
|
119
116
|
lfs: null,
|
|
120
117
|
});
|
|
121
118
|
}
|
|
122
|
-
const bytes = await runGitBytes(['cat-file', 'blob', oid]);
|
|
123
119
|
const probe = bytes.subarray(0, LFS_PROBE_BYTES).toString('utf8');
|
|
124
120
|
const lfs = parseLfsPointer(probe);
|
|
125
121
|
if (lfs) {
|
|
@@ -149,6 +145,31 @@ function createGitVersionReader(options) {
|
|
|
149
145
|
dataUrl,
|
|
150
146
|
});
|
|
151
147
|
};
|
|
148
|
+
/**
|
|
149
|
+
* Reads one blob by OID, bounded, with binary and LFS detection.
|
|
150
|
+
*
|
|
151
|
+
* The size is read first so an enormous historical file is reported by its size instead of
|
|
152
|
+
* being pulled into memory just to be truncated.
|
|
153
|
+
*/
|
|
154
|
+
const readVersion = async (label, oid) => {
|
|
155
|
+
if (oid === null)
|
|
156
|
+
return absentVersion(label);
|
|
157
|
+
const sizeRaw = (await runGit(['cat-file', '-s', oid])).trim();
|
|
158
|
+
const byteSize = Number(sizeRaw) || 0;
|
|
159
|
+
if (byteSize > MAX_VERSION_BYTES) {
|
|
160
|
+
return Object.freeze({
|
|
161
|
+
label,
|
|
162
|
+
oid,
|
|
163
|
+
exists: true,
|
|
164
|
+
binary: false,
|
|
165
|
+
byteSize,
|
|
166
|
+
truncated: true,
|
|
167
|
+
content: null,
|
|
168
|
+
lfs: null,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return versionFromBytes(label, oid, await runGitBytes(['cat-file', 'blob', oid]));
|
|
172
|
+
};
|
|
152
173
|
return {
|
|
153
174
|
async conflict(pathInput) {
|
|
154
175
|
const path = assertRelativePath(pathInput);
|
|
@@ -168,15 +189,18 @@ function createGitVersionReader(options) {
|
|
|
168
189
|
continue;
|
|
169
190
|
stageOids.set(Number(stage), oid);
|
|
170
191
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
192
|
+
// v0.3.25 #269 — the working file is read from the filesystem. The previous
|
|
193
|
+
// `hash-object` (without -w) → `cat-file` chain only worked while the edited
|
|
194
|
+
// content happened to match an object git already stored; any real edit made
|
|
195
|
+
// `cat-file` fail the entire conflict response.
|
|
196
|
+
const worktreeBytes = await readWorktreeFile?.(path);
|
|
175
197
|
const [base, ours, theirs, worktree] = await Promise.all([
|
|
176
198
|
readVersion('base', stageOids.get(1) ?? null),
|
|
177
199
|
readVersion('ours', stageOids.get(2) ?? null),
|
|
178
200
|
readVersion('theirs', stageOids.get(3) ?? null),
|
|
179
|
-
|
|
201
|
+
worktreeBytes
|
|
202
|
+
? Promise.resolve(versionFromBytes('worktree', null, worktreeBytes))
|
|
203
|
+
: Promise.resolve(absentVersion('worktree')),
|
|
180
204
|
]);
|
|
181
205
|
const hasBase = stageOids.has(1);
|
|
182
206
|
const hasOurs = stageOids.has(2);
|
package/dist/web/protocol.d.ts
CHANGED
|
@@ -361,6 +361,11 @@ export interface WebWorkspaceSummaryV1 {
|
|
|
361
361
|
readonly available: boolean;
|
|
362
362
|
readonly sessionCount: number;
|
|
363
363
|
readonly lastActivatedAt: string;
|
|
364
|
+
/**
|
|
365
|
+
* v0.3.25 #272 — when the workspace was first registered. The navigation's
|
|
366
|
+
* stable default order is derived from this; optional for older Hosts.
|
|
367
|
+
*/
|
|
368
|
+
readonly registeredAt?: string;
|
|
364
369
|
readonly pinnedOrder?: number;
|
|
365
370
|
}
|
|
366
371
|
export interface WebWorkspaceProjectSummaryV1 {
|
|
@@ -238,6 +238,7 @@ class WebWorkbenchController {
|
|
|
238
238
|
available,
|
|
239
239
|
sessionCount: counts.get(entry.canonicalPath) ?? 0,
|
|
240
240
|
lastActivatedAt: entry.lastActivatedAt,
|
|
241
|
+
registeredAt: entry.registeredAt,
|
|
241
242
|
...(entry.pinnedOrder !== undefined ? { pinnedOrder: entry.pinnedOrder } : {}),
|
|
242
243
|
});
|
|
243
244
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as a,j as e,I as D,s as be,C as ye,W as ge}from"./index-
|
|
1
|
+
import{r as a,j as e,I as D,s as be,C as ye,W as ge}from"./index-kQqA3li6.js";import{u as Ce,R as Ne}from"./useAutoNotice-DPXFWIwK.js";function Le({workspaceId:s,refreshEpoch:o,actions:l,navigatorWidthPx:r,onNavigatorWidthCommit:d,revealRequest:c}){const[f,m]=a.useState({}),[x,j]=a.useState(new Set(["workspace-root"])),[i,N]=a.useState(null),[F,B]=a.useState(""),[M,G]=a.useState(null),[Y,P]=a.useState(!1),[H,K]=a.useState(""),{notice:J,showNotice:$,clearNotice:V}=Ce(),[T,ue]=a.useState(""),[Z,_]=a.useState({}),[I,A]=a.useState(""),[O,R]=a.useState("view"),[L,w]=a.useState(""),[k,q]=a.useState(!1),[de,S]=a.useState(!1),[fe,me]=a.useState(""),[he,W]=a.useState(!1),h=a.useRef(0),C=a.useRef(0),ee=a.useRef(null),E=async(t,n=!1,y=h.current)=>{if(y!==h.current)return;const p=f[t],g=n?p?.nextCursor:void 0;if(!(n&&!g)){m(u=>({...u,[t]:{items:p?.items??[],nextCursor:p?.nextCursor??null,revision:p?.revision??"",loading:!0}}));try{const u=await l.listFiles(t,g??void 0);if(y!==h.current)return;m(v=>({...v,[t]:{items:n?ke(v[t]?.items??[],u.items):u.items,nextCursor:u.nextCursor,revision:u.revision,loading:!1}}))}catch(u){if(y!==h.current)return;if(n&&U(u)){$("目录已变化,已重新载入第一页。"),E(t,!1,y);return}m(v=>({...v,[t]:{items:v[t]?.items??[],nextCursor:v[t]?.nextCursor??null,revision:v[t]?.revision??"",loading:!1,error:z(u)}}))}}},Q=async(t=h.current)=>{try{const n=await l.gitStatus();if(t!==h.current)return;_(Se(n)),A(n.nextCursor?"Git 装饰仅显示首批 200 个变更;在 Git 面板继续分页查看。":"")}catch(n){if(t!==h.current)return;_({}),A(`Git 状态装饰不可用:${z(n)}`)}},te=a.useRef(s);a.useEffect(()=>{const t=h.current+1;h.current=t,C.current+=1;const n=te.current===s?ee.current:null;te.current=s,m({}),j(new Set(["workspace-root"])),N(n),B(""),G(null),P(!1),K(""),V(),_({}),A(""),s&&(E("workspace-root",!1,t),Q(t),n&&b(n,!1,!1,t))},[o,s]);const se=a.useRef(null),b=async(t,n=!1,y=!1,p=h.current,g=C.current+1)=>{if(p===h.current&&!(!t.readable||t.sensitive||X(t))){C.current=g,N(t),K(""),n||(B(""),G(null),P(!1),R("view"),w(""),S(!1)),y||V();try{const u=await l.readFileContent(t.id,n?M??void 0:void 0);if(p!==h.current||g!==C.current)return;P(u.binary),B(v=>n?`${v}${u.content??""}`:u.content??""),G(u.nextCursor),me(u.revision),y&&$("文件已变化,已从第一页重新载入。")}catch(u){if(p!==h.current||g!==C.current)return;if(n&&U(u)){await b(t,!1,!0,p,g);return}K(z(u))}}},re=De({selected:i,binary:Y,hasMorePages:M!==null}),xe=()=>{w(F),S(!1),R("edit")},ie=()=>{if(L!==F){W(!0);return}ne()},ne=()=>{R("view"),w(""),S(!1),W(!1)},je=async()=>{if(!(!i||k)){q(!0),S(!1);try{await l.writeFileContent(i.id,L,fe),R("view"),w(""),await b(i,!1,!0,h.current,C.current+1),$("已保存文件。","success"),Q()}catch(t){U(t)?S(!0):$(z(t),"error")}finally{q(!1)}}},ae=t=>{const n=new Set(x);n.has(t)?n.delete(t):(n.add(t),f[t]||E(t)),j(n)};a.useEffect(()=>{c&&se.current!==c.id&&(se.current=c.id,b({id:c.fileId,name:c.displayPath.split("/").pop()??c.displayPath,displayPath:c.displayPath,kind:"file",modifiedAt:new Date().toISOString(),sensitive:!1,readable:!0}))},[c?.id]),a.useEffect(()=>{ee.current=i},[i]);const pe=a.useMemo(()=>Object.values(f).flatMap(t=>t.items),[f]),oe=T.trim()?pe.filter(t=>t.name.toLocaleLowerCase().includes(T.toLocaleLowerCase())):null,ve=!!f["workspace-root"]?.loading;return e.jsxs("div",{className:"work-resource-panel files-panel",children:[e.jsxs("div",{className:"resource-toolbar",children:[e.jsxs("label",{className:"resource-search",children:[e.jsx("span",{className:"sr-only",children:"搜索已加载文件"}),e.jsx(D,{name:"search",size:16}),e.jsx("input",{type:"search",value:T,onChange:t=>ue(t.target.value),placeholder:"搜索已加载文件",title:"搜索仅覆盖已加载的目录和文件"})]}),e.jsx("button",{type:"button",className:"icon-button","aria-label":"刷新文件树","aria-busy":ve,onClick:()=>{E("workspace-root"),Q()},children:e.jsx(D,{name:"refresh",size:16})})]}),e.jsxs(Ne,{panelId:"files",navigatorWidthPx:r,onNavigatorWidthCommit:d,contentLabel:"文件预览",navigatorLabel:"工作区文件",handleLabel:"调整文件目录宽度",contentClassName:"file-preview",navigatorClassName:"file-tree",children:[e.jsxs(e.Fragment,{children:[I?e.jsx("p",{className:"resource-hint",role:"status",children:I}):null,oe?e.jsx(e.Fragment,{children:e.jsx("ul",{role:"list",className:"file-node-list search-results",children:oe.map(t=>e.jsx(ce,{node:t,depth:0,expanded:!1,selected:i?.id===t.id,gitLabels:Z[t.displayPath]??[],onToggle:ae,onSelect:n=>{b(n)}},t.id))})}):e.jsx(le,{parentId:"workspace-root",depth:0,directories:f,expanded:x,selectedId:i?.id,gitDecorations:Z,onToggle:ae,onSelect:t=>{b(t)},onLoadMore:t=>{E(t,!0)}})]}),e.jsxs(e.Fragment,{children:[J?e.jsx("p",{className:"resource-notice",role:"status",children:J.text}):null,i?e.jsxs(e.Fragment,{children:[e.jsxs("header",{children:[e.jsx("strong",{children:i.name}),e.jsx("span",{children:Fe(i.sizeBytes??0)})]}),H?e.jsx("p",{className:"resource-error",role:"alert",children:H}):Y?e.jsxs("div",{className:"resource-empty",children:[e.jsx(D,{name:"code",size:16}),e.jsx("strong",{title:"出于安全和性能考虑,只显示元数据。",children:"二进制文件"})]}):e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"file-preview-actions",children:O==="view"?e.jsx("button",{type:"button",className:"text-button",disabled:!re,title:re?void 0:"仅支持编辑 512 KB 内的 UTF-8 文本文件",onClick:xe,children:"编辑"}):e.jsxs(e.Fragment,{children:[e.jsx("button",{type:"button",className:"primary-button",disabled:k||L===F,"aria-busy":k,onClick:()=>{je()},children:k?"保存中…":"保存"}),e.jsx("button",{type:"button",className:"text-button",disabled:k,onClick:ie,children:"取消"})]})}),de?e.jsxs("p",{className:"resource-error",role:"alert",children:["文件已在别处变更,保存被拒绝。",e.jsx("button",{type:"button",className:"text-button",onClick:()=>{b(i,!1,!0)},children:"重新加载"})]}):null,O==="edit"?e.jsx("textarea",{className:"file-editor",value:L,spellCheck:!1,"aria-label":`编辑文件 ${i.name}`,onChange:t=>w(t.target.value),onKeyDown:t=>{t.key==="Escape"&&(t.preventDefault(),ie())}}):e.jsx("pre",{tabIndex:0,className:"file-code-view","aria-label":`文件内容 ${i.name}`,children:be(F)})]}),M&&O==="view"?e.jsx("button",{type:"button",className:"secondary-button",onClick:()=>{b(i,!0)},children:"加载更多内容"}):null]}):e.jsxs("div",{className:"resource-empty",children:[e.jsx(D,{name:"workspace",size:16}),e.jsx("strong",{children:"选择文件预览"}),e.jsx("p",{title:"敏感文件、工作区外链接和二进制正文不会返回浏览器。",children:"受限内容不会返回浏览器。"})]})]})]}),e.jsx(ye,{open:he,onClose:()=>W(!1),title:"放弃未保存的修改?",body:"当前草稿尚未保存,放弃后将回到文件的最新内容。",confirmLabel:"放弃修改",danger:!0,onConfirm:ne})]})}function le({parentId:s,depth:o,directories:l,expanded:r,selectedId:d,gitDecorations:c,onToggle:f,onSelect:m,onLoadMore:x}){const j=l[s];return j?j.error?e.jsx("p",{className:"resource-error",role:"alert",children:j.error}):e.jsxs("ul",{role:"list",className:"file-node-list",children:[j.items.map(i=>{const N=r.has(i.id);return e.jsxs("li",{children:[e.jsx(ce,{node:i,depth:o,expanded:N,selected:d===i.id,gitLabels:c[i.displayPath]??[],onToggle:f,onSelect:m}),X(i)&&N?e.jsx(le,{parentId:i.id,depth:o+1,directories:l,expanded:r,selectedId:d,gitDecorations:c,onToggle:f,onSelect:m,onLoadMore:x}):null]},i.id)}),j.loading?e.jsx("li",{className:"resource-loading",children:"正在读取…"}):null,j.nextCursor?e.jsx("li",{children:e.jsx("button",{type:"button",className:"text-button",onClick:()=>x(s),children:"加载更多"})}):null]}):e.jsx("p",{className:"resource-loading",children:"正在读取…"})}function ce({node:s,depth:o,expanded:l,selected:r,gitLabels:d,onToggle:c,onSelect:f}){const m=s.sensitive||!s.readable,x=X(s);return e.jsxs("button",{type:"button",className:`file-node ${r?"selected":""} ${m?"blocked":""}`,style:{paddingInlineStart:`${8+o*15}px`},"aria-expanded":x?l:void 0,"aria-label":`${we(s)} ${s.name}${m?",不可读取":""}${d.length?`,Git ${d.join("、")}`:""}`,onClick:()=>x?c(s.id):f(s),disabled:m,children:[e.jsx(D,{name:x?"workspace":"code",size:16}),e.jsx("span",{children:s.name}),s.kind==="symlink"?e.jsx("small",{children:"链接"}):null,s.sensitive?e.jsx("small",{children:"敏感"}):null,d.length?e.jsx("small",{className:"file-git-status",title:`Git ${d.join("、")}`,children:d.join(" · ")}):null]})}function we(s){return s.kind==="directory"?"目录":s.kind==="symlink"?s.targetKind==="directory"?"符号链接目录":"符号链接":"文件"}function X(s){return s.kind==="directory"||s.kind==="symlink"&&s.targetKind==="directory"}function ke(s,o){const l=new Map(s.map(r=>[r.id,r]));for(const r of o)l.set(r.id,r);return[...l.values()]}function Se(s){if(!s.hasWorktree)return{};const o=new Map,l=(r,d)=>{const c=r.replace(/\\/gu,"/").replace(/^\.\//u,"");if(!c)return;const f=o.get(c)??new Set;f.add(d),o.set(c,f);const m=c.split("/");for(let x=1;x<m.length;x+=1){const j=m.slice(0,x).join("/"),i=o.get(j)??new Set;i.add("含变更"),o.set(j,i)}};return s.conflicted.forEach(r=>l(r.path,"冲突")),s.staged.forEach(r=>l(r.path,"已暂存")),s.unstaged.forEach(r=>l(r.path,"未暂存")),s.untracked.forEach(r=>l(r.path,"未跟踪")),Object.fromEntries([...o].map(([r,d])=>[r,Object.freeze([...d])]))}const Ee=512*1024;function De(s){const{selected:o,binary:l,hasMorePages:r}=s;return!o||l||o.sensitive||!o.readable||r?!1:(o.sizeBytes??0)<=Ee}function Fe(s){return s<1024?`${s} B`:s<1048576?`${(s/1024).toFixed(1)} KB`:`${(s/1048576).toFixed(1)} MB`}function U(s){return s instanceof ge&&s.code==="file_revision_conflict"}function z(s){return s instanceof Error?s.message:"文件请求失败。"}export{Ee as FILE_EDIT_MAX_BYTES,Le as FilesPanel,De as canEditFileContent};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var $e=Object.defineProperty;var Pe=(t,s,n)=>s in t?$e(t,s,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[s]=n;var ie=(t,s,n)=>Pe(t,typeof s!="symbol"?s+"":s,n);import{r as l,a as He,j as e,I as T}from"./index-DZOlUgt6.js";import{u as Oe,R as Me}from"./useAutoNotice-Ylj5bfSH.js";const Ne="orion.web.git-display.v1",_e=Object.freeze(["changes","history","compare"]),me=Object.freeze({message:"",author:"",path:"",sha:"",since:"",until:""}),xe=Object.freeze({view:"changes",group:null,query:"",selectedFileId:null,selectedSource:null,selectedFileIds:Object.freeze([]),selectedLineIds:Object.freeze([]),collapsedHunks:Object.freeze([]),anchorLineId:null,navigatorScrollTop:0,commitDraft:Object.freeze({summary:"",body:""}),historyQuery:me,selectedCommitId:null,selectedCommitParentIndex:0,selectedCommitPath:null,fileHistoryPath:null,notice:""}),pe=Object.freeze({mode:"unified",wrap:!1,showWhitespace:!1,ignoreWhitespace:!1,wordDiff:!1});function Ae(t){return t||"__no-workspace__"}function ze(){const t=new Map;return{read(s){return t.get(s)??xe},update(s,n){const i=Object.freeze({...t.get(s)??xe,...n});return t.set(s,i),i},clear(s){t.delete(s)}}}const Be=ze();function Ge(){try{const t=globalThis.localStorage?.getItem(Ne);if(!t)return pe;const s=JSON.parse(t);return Te(s)}catch{return pe}}function We(t){try{globalThis.localStorage?.setItem(Ne,JSON.stringify(t))}catch{}}function Te(t){if(typeof t!="object"||t===null)return pe;const s=t;return Object.freeze({mode:s.mode==="side-by-side"?"side-by-side":"unified",wrap:s.wrap===!0,showWhitespace:s.showWhitespace===!0,ignoreWhitespace:s.ignoreWhitespace===!0,wordDiff:s.wordDiff===!0})}function qe(t){if(t.entries.length===0)return{fileId:null,path:null,notice:""};const s=t.entries.find(o=>o.fileId===t.previousFileId);if(s)return{fileId:s.fileId,path:s.path,notice:""};const i=t.entries.find(o=>o.path===t.previousPath)??t.entries[0];return{fileId:i.fileId,path:i.path,notice:t.previousFileId===null?"":`原选中的改动已不在此列表,已跳到 ${i.path}。`}}const Qe=959,Ve=620,Ke=640;function Ue(t,s){if(!Number.isFinite(t)||t<=0)return{tier:"wide",narrow:!1,effectiveMode:s,dataWidth:"wide"};const n=t<=Ve?"narrow":t<=Qe?"compact":"wide";return{tier:n,narrow:n==="narrow",effectiveMode:t<=Ke?"unified":s,dataWidth:n}}const Ye=350,je=30;function Xe(t){const{enabled:s,session:n,setSession:i,actions:o,refreshEpoch:h}=t,g=n.historyQuery??me,[x,a]=l.useState(null),[p,j]=l.useState(null),[b,d]=l.useState(null),[S,u]=l.useState(null),[_,L]=l.useState(null),[v,N]=l.useState(null),[Q,P]=l.useState(!1),[B,E]=l.useState(!1),[q,H]=l.useState(!1),[k,R]=l.useState(""),$=l.useRef(0),r=l.useRef(0),I=l.useMemo(()=>({...g.message.trim()?{message:g.message.trim()}:{},...g.author.trim()?{author:g.author.trim()}:{},...g.path.trim()?{path:g.path.trim()}:{},...g.sha.trim()?{sha:g.sha.trim()}:{},...g.since.trim()?{since:g.since.trim()}:{},...g.until.trim()?{until:g.until.trim()}:{}}),[g]);l.useEffect(()=>{if(!s)return;const w=$.current+1;$.current=w,P(!0),R("");const A=setTimeout(()=>{(async()=>{try{const[z,W]=await Promise.all([o.gitRefs(),o.gitHistory({...I,pageSize:je})]);if(w!==$.current)return;a(z),j(W)}catch(z){if(w!==$.current)return;R(le(z))}finally{w===$.current&&P(!1)}})()},I.message||I.author||I.path||I.sha?Ye:0);return()=>clearTimeout(A)},[o,s,h,I]),l.useEffect(()=>{const w=n.selectedCommitId;if(!s||!w){d(null),u(null),L(null);return}const A=r.current+1;r.current=A;const z=n.selectedCommitParentIndex;H(!0),R(""),(async()=>{try{const[W,X]=await Promise.all([o.gitCommitDetail(w,z),o.gitCommitFiles(w,z)]);if(A!==r.current)return;d(W),u(X);const J=X.files.find(U=>!U.sensitive&&!U.binary),ee=n.selectedCommitPath&&X.files.some(U=>U.path===n.selectedCommitPath)?n.selectedCommitPath:J?.path??null;ee!==n.selectedCommitPath&&i({selectedCommitPath:ee}),ee||L(null)}catch(W){if(A!==r.current)return;R(le(W))}finally{A===r.current&&H(!1)}})()},[o,s,n.selectedCommitId,n.selectedCommitParentIndex]),l.useEffect(()=>{const w=n.selectedCommitId,A=n.selectedCommitPath;if(!s||!w||!A){L(null);return}const z=r.current;(async()=>{try{const W=await o.gitCommitDiff({oid:w,path:A,parentIndex:n.selectedCommitParentIndex});if(z!==r.current)return;L(W)}catch(W){if(z!==r.current)return;R(le(W)),L(null)}})()},[o,s,n.selectedCommitId,n.selectedCommitPath,n.selectedCommitParentIndex]),l.useEffect(()=>{const w=n.fileHistoryPath;if(!s||!w){N(null);return}const A=r.current;(async()=>{try{const z=await o.gitFileHistory({path:w});if(A!==r.current)return;N(z)}catch(z){if(A!==r.current)return;R(le(z))}})()},[o,s,n.fileHistoryPath]);const f=l.useCallback(w=>{i({historyQuery:{...g,...w}})},[g,i]),V=l.useCallback(()=>{i({historyQuery:me})},[i]),c=l.useCallback(w=>{i({selectedCommitId:w.id,selectedCommitParentIndex:0,selectedCommitPath:null})},[i]),M=l.useCallback(()=>{i({selectedCommitId:null,selectedCommitPath:null,selectedCommitParentIndex:0})},[i]),G=l.useCallback(w=>{i({selectedCommitParentIndex:w,selectedCommitPath:null})},[i]),K=l.useCallback(w=>i({selectedCommitPath:w}),[i]),Y=l.useCallback(()=>{const w=p?.nextCursor;if(!w||B)return;const A=$.current;E(!0),(async()=>{try{const z=await o.gitHistory({...I,cursor:w,pageSize:je});if(A!==$.current)return;j(W=>W?{...z,items:[...W.items,...z.items]}:z)}catch(z){if(A!==$.current)return;R(le(z))}finally{A===$.current&&E(!1)}})()},[o,B,p,I]),se=l.useCallback(w=>i({fileHistoryPath:w}),[i]),D=l.useCallback(()=>i({fileHistoryPath:null}),[i]);return{query:g,refs:x,page:p,detail:b,files:S,diff:_,fileHistory:v,loading:Q,loadingMore:B,loadingDetail:q,error:k,isEmptyRepository:p!==null&&p.tipOid===null,setQuery:f,resetQuery:V,selectCommit:c,closeDetail:M,setParentIndex:G,selectFile:K,loadMore:Y,openFileHistory:se,closeFileHistory:D}}function le(t){return t?.code==="git_cursor_invalid"?"历史已变化,已重新载入列表。":t instanceof Error?t.message:"Git 历史读取失败。"}function Je(t){if(!(t instanceof HTMLElement))return!1;const s=t.tagName.toLowerCase();return s==="input"||s==="textarea"||s==="select"||t.isContentEditable?!0:t.closest('[role="dialog"]')!==null}function Ze(t){const{enabled:s,session:n,setSession:i,fileIds:o,anchorLineIds:h,onOpenSelection:g,onEscapeFallback:x}=t;l.useEffect(()=>{if(!s)return;const a=p=>{if(Je(p.target))return;if(p.key==="F7"&&h.length>0){p.preventDefault();const b=n.anchorLineId?h.indexOf(n.anchorLineId):-1,d=p.shiftKey?-1:1,S=b<0?d>0?0:h.length-1:Math.min(h.length-1,Math.max(0,b+d));i({anchorLineId:h[S]});return}if(p.key==="Escape"){if(n.query){p.preventDefault(),i({query:"",selectedFileId:null});return}if(n.selectedLineIds.length>0){p.preventDefault(),i({selectedLineIds:[]});return}x();return}if(o.length===0)return;const j=n.selectedFileId?o.indexOf(n.selectedFileId):-1;if(p.key==="ArrowDown"||p.key==="ArrowUp"){p.preventDefault();const b=p.key==="ArrowDown"?1:-1,d=j<0?b>0?0:o.length-1:Math.min(o.length-1,Math.max(0,j+b));i({selectedFileId:o[d],selectedSource:null,anchorLineId:null});return}if(p.key==="Enter"&&j>=0){p.preventDefault(),g();return}if(p.key===" "){if(j<0)return;p.preventDefault();const b=o[j];i({selectedFileIds:n.selectedFileIds.includes(b)?n.selectedFileIds.filter(d=>d!==b):[...n.selectedFileIds,b]})}};return window.addEventListener("keydown",a),()=>window.removeEventListener("keydown",a)},[s,n.query,n.selectedFileId,n.selectedLineIds,n.anchorLineId,n.selectedFileIds,i,o,h,g,x])}class et{constructor(s){ie(this,"fetcher");ie(this,"freshnessMs");ie(this,"slots",new Map);this.fetcher=s.fetcher,this.freshnessMs=s.freshnessMs??3e4}getSnapshot(s){const n=this.slotFor(s);n.invalidated&&(n.inFlight=null,n.invalidated=!1);const i=n.snapshot;if(i&&Date.now()-i.fetchedAt<=this.freshnessMs)return Promise.resolve(i);if(n.inFlight)return n.inFlight;n.generation+=1;const o=n.generation,h=this.fetcher(s).then(g=>{const x=this.slots.get(s);if(!x||x.generation!==o||x.invalidated)throw new tt(s);return x.snapshot=Object.freeze({...g,fetchedAt:Date.now()}),x.inFlight=null,x.snapshot}).catch(g=>{const x=this.slots.get(s);throw x&&x.inFlight===h&&(x.inFlight=null),g});return n.inFlight=h,h}invalidate(s){const n=this.slots.get(s);if(!n)return!1;const i=!!(n.snapshot||n.inFlight);return n.invalidated=!0,n.snapshot=null,i}isStale(s,n){const i=this.slots.get(s)?.snapshot;return i?n==null?!1:i.repositoryRevision!==n:!0}cachedSnapshot(s){const n=this.slots.get(s);return!n||n.invalidated||!n.snapshot?null:n.snapshot}clear(){this.slots.clear()}slotFor(s){const n=this.slots.get(s);if(n)return n;const i={workspaceId:s,snapshot:null,inFlight:null,generation:0,invalidated:!1};return this.slots.set(s,i),i}}class tt extends Error{constructor(n){super(`Repository snapshot for ${n} is stale; refetching.`);ie(this,"code","workspace_snapshot_stale")}}const st=20*1024*1024,nt=5*1024*1024;function it(t){let s=0;for(const n of t.hunks){s+=n.header.length;for(const i of n.lines)s+=i.raw.length+48}return s+t.path.length+256}function lt(t){const s=st,n=nt,i=new Map;let o=0,h=0,g=0;const x=(a,p)=>`${a}\0${p}`;return{read(a,p){const j=x(a,p),b=i.get(j);return b?(i.delete(j),i.set(j,b),h+=1,b.document):(g+=1,null)},write(a,p,j){const b=it(j);if(b>n)return;const d=x(a,p),S=i.get(d);for(S&&(i.delete(d),o-=S.bytes),i.set(d,{document:j,bytes:b}),o+=b;o>s&&i.size>0;){const u=i.keys().next();if(u.done)break;const _=i.get(u.value);i.delete(u.value),o-=_?.bytes??0}},clear(){i.clear(),o=0},stats(){return{entries:i.size,bytes:o,hits:h,misses:g}}}}const at=lt(),rt=2e3;function ct(t){const{workspaceId:s,refreshEpoch:n,actions:i}=t,o=t.store??Be,h=t.cache??at,g=Ae(s),[x,a]=l.useState(()=>o.read(g)),[p,j]=l.useState(()=>Ge()),[b,d]=l.useState(null),[S,u]=l.useState(null),[_,L]=l.useState(null),[v,N]=l.useState(!1),[Q,P]=l.useState(!1),[B,E]=l.useState(""),[q,H]=l.useState(!1),k=l.useRef(0),R=l.useRef(0),$=l.useRef(x);$.current=x;const r=l.useRef(b),I=l.useRef(p);I.current=p,r.current=b;const f=l.useCallback(m=>{a(o.update(g,m))},[g,o]),V=l.useCallback(m=>{j(y=>{const F=Object.freeze({...y,...m});return We(F),F})},[]),c=l.useCallback(async(m,y)=>{if(m===k.current){y.quiet?P(!0):N(!0),E("");try{const F=$.current,[C,O]=await Promise.all([i.gitStatus(void 0,{...F.group?{group:F.group}:{},...F.query?{query:F.query}:{}}),i.gitLog()]);if(m!==k.current)return;if(d(C),u(O),!C.hasWorktree){L(null),H(!1);return}const ne=ue(C);if(!y.keepSelection){L(null),f({selectedFileId:null,selectedSource:null,anchorLineId:null}),H(!1);return}const ae=F.selectedFileId,Re=ne.find(de=>de.fileId===ae)?.path??(_?_.path:null),oe=qe({entries:ne,previousFileId:ae,previousPath:Re});if(oe.fileId&&oe.fileId!==ae){f({selectedFileId:null,selectedSource:null,notice:oe.notice}),L(null),H(!1);return}if(ae){const de=_!==null&&C.repositoryRevision===_.repositoryRevision;H(!de)}else H(!1)}catch(F){if(m!==k.current)return;E(Z(F))}finally{m===k.current&&(N(!1),P(!1))}}},[i,_,f]),M=l.useCallback(()=>{const m=k.current+1;k.current=m,R.current+=1,c(m,{keepSelection:!0,quiet:b!==null})},[c,b]);l.useEffect(()=>{const m=k.current+1;k.current=m,R.current+=1,d(null),u(null),L(null),E(""),H(!1),N(!!s),a(o.read(g)),s?c(m,{keepSelection:!1}):N(!1)},[g,s,o]),l.useEffect(()=>{if(!s||n===0)return;const m=k.current+1;k.current=m,R.current+=1,c(m,{keepSelection:!0,quiet:!0})},[n]),l.useEffect(()=>{if(!s||typeof document>"u")return;let m;const y=()=>{m!==void 0&&clearInterval(m),m=void 0},F=()=>{y(),m=setInterval(()=>{const O=k.current+1;k.current=O,c(O,{keepSelection:!0,quiet:!0})},rt)},C=()=>{if(document.visibilityState==="visible"){const O=k.current+1;k.current=O,c(O,{keepSelection:!0,quiet:!0}),F()}else y()};return document.visibilityState==="visible"&&F(),document.addEventListener("visibilitychange",C),()=>{y(),document.removeEventListener("visibilitychange",C)}},[s,x.group,x.query]);const G=l.useCallback(m=>{const y=R.current+1;R.current=y,f({selectedLineIds:[],selectedFileId:m.fileId,selectedSource:m.source,anchorLineId:null,notice:""}),E("");const F=r.current?.repositoryRevision,C=F?h.read(F,m.fileId):null;if(C){L(C),H(!1),N(!1);return}L(null),N(!0),(async()=>{try{const O=await i.gitDiffDocument(m.fileId,void 0,I.current.ignoreWhitespace,I.current.wordDiff);if(y!==R.current)return;h.write(O.repositoryRevision,m.fileId,O),L(O),H(O.repositoryRevision!==r.current?.repositoryRevision)}catch(O){if(y!==R.current)return;if(ut(O)){E("仓库已变化,已重新载入 Git 状态。"),f({selectedFileId:null,selectedSource:null});return}E(Z(O))}finally{y===R.current&&N(!1)}})()},[i,h,f]),K=l.useCallback(()=>{const m=b?.nextCursor;if(!m)return;const y=k.current,F=b?.repositoryRevision;(async()=>{try{const C=await i.gitStatus(m,{...x.group?{group:x.group}:{},...x.query?{query:x.query}:{}});if(y!==k.current)return;if(F!==void 0&&C.repositoryRevision!==F){E("仓库已变化,已重新载入 Git 状态。");return}d(O=>O&&{...C,conflicted:re(O.conflicted,C.conflicted),staged:re(O.staged,C.staged),unstaged:re(O.unstaged,C.unstaged),untracked:re(O.untracked,C.untracked),counts:{...C.counts,loaded:O.counts.loaded+C.counts.loaded}})}catch(C){if(y!==k.current)return;E(Z(C))}})()},[i,x.group,x.query,b]),Y=l.useCallback(()=>{const m=S?.nextCursor;if(!m)return;const y=k.current;(async()=>{try{const F=await i.gitLog(m);if(y!==k.current)return;u(C=>C&&{...F,items:dt(C.items,F.items)})}catch(F){if(y!==k.current)return;E(Z(F))}})()},[i,S]),se=l.useCallback(()=>{const m=_?.nextCursor,y=x.selectedFileId;if(!m||!y)return;const F=k.current,C=R.current;(async()=>{try{const O=await i.gitDiffDocument(y,m,I.current.ignoreWhitespace,I.current.wordDiff);if(F!==k.current||C!==R.current)return;L(ne=>ne?{...O,hunks:[...ne.hunks,...O.hunks]}:O)}catch(O){if(F!==k.current||C!==R.current)return;E(Z(O))}})()},[i,_,x.selectedFileId]);l.useEffect(()=>{!b||!x.selectedFileId||ue(b).some(y=>y.fileId===x.selectedFileId)||(L(null),f({selectedFileId:null,selectedSource:null,anchorLineId:null}))},[b,x.selectedFileId,f]);const D=l.useMemo(()=>!b||!x.selectedFileId?null:ue(b).find(m=>m.fileId===x.selectedFileId)??null,[b,x.selectedFileId]),[w,A]=l.useState(!1),[z,W]=l.useState(null),X=l.useRef(null);X.current===null&&(X.current=new et({fetcher:async m=>{const y=await i.gitStatus(void 0,{});return Object.freeze({repositoryRevision:y.repositoryRevision,branch:y.branch,counts:Object.freeze({totalChangedFiles:y.counts.total,staged:y.counts.staged,unstaged:y.counts.unstaged,untracked:y.counts.untracked,conflicted:y.counts.conflicted}),fetchedAt:Date.now()})}}));const J=X.current,ee=l.useCallback(async()=>{const m=r.current;if(m?.hasWorktree)try{if((await J.getSnapshot(s)).repositoryRevision===m.repositoryRevision)return;J.invalidate(s),await c(k.current,{keepSelection:!0,quiet:!0})}catch{}},[c,J,s]);l.useEffect(()=>{if(!s)return;let m=!1;const y=()=>{m||document.hidden||(m=!0,ee().finally(()=>{m=!1}))},F=()=>{document.hidden||y()};return window.addEventListener("focus",y),document.addEventListener("visibilitychange",F),()=>{window.removeEventListener("focus",y),document.removeEventListener("visibilitychange",F)}},[ee,s]);const U=l.useCallback(async()=>{h.clear(),J.invalidate(s);const m=k.current+1;k.current=m,R.current+=1,await c(m,{keepSelection:!0,quiet:!0})},[h,c,J,s]),te=l.useCallback(async(m,y)=>{if(!w){A(!0),E("");try{await m(),await U(),f({notice:y})}catch(F){E(Z(F))}finally{A(!1)}}},[w,U,f]),Ce=l.useCallback(async m=>{const y=r.current?.repositoryRevision;y&&(await te(()=>i.gitStage(m,y),m.length>1?`已暂存 ${m.length} 个文件。`:"已暂存。"),f({selectedFileIds:[]}))},[i,te,f]),we=l.useCallback(async m=>{const y=r.current?.repositoryRevision;y&&(await te(()=>i.gitUnstage(m,y),m.length>1?`已取消暂存 ${m.length} 个文件。`:"已取消暂存。"),f({selectedFileIds:[]}))},[i,te,f]),Fe=l.useCallback(async m=>{const y=r.current?.repositoryRevision;y&&await te(()=>i.gitApplyPatch({...m,expectedRepositoryRevision:y}),"已应用所选改动到索引。")},[i,te]),Le=l.useCallback(async m=>{const y=r.current?.repositoryRevision;if(!y)throw new Error("Git repository is unavailable.");A(!0),E("");const F=He();try{const C=await i.gitCommit({summary:m.summary,...m.body?{body:m.body}:{},expectedRepositoryRevision:y,requestId:F});return await U(),f({commitDraft:{summary:"",body:""},notice:C.warning?`已提交 ${C.commitSha.slice(0,8)},但:${C.warning}`:`已提交 ${C.commitSha.slice(0,8)}。`}),C.commitSha}catch(C){throw E(Z(C)),C}finally{A(!1)}},[i,U,f]),De=l.useCallback(m=>{const y=$.current.selectedFileIds;f({selectedFileIds:y.includes(m)?y.filter(F=>F!==m):[...y,m]})},[f]),Ee=l.useCallback(()=>f({selectedFileIds:[]}),[f]);return l.useEffect(()=>{if(!b?.hasWorktree){W(null);return}let m=!1;return(async()=>{try{const y=await i.gitCommitPreview();m||W(y)}catch{m||W(null)}})(),()=>{m=!0}},[i,b?.hasWorktree,b?.repositoryRevision]),{session:x,display:p,status:b,log:S,diff:_,loading:v,refreshing:Q,error:B,stale:q,setSession:f,setDisplay:V,openFile:G,reload:M,loadMoreStatus:K,loadMoreLog:Y,loadMoreDiff:se,selectedEntry:D,busy:w,commitPreview:z,toggleSelection:De,clearSelection:Ee,stagePaths:Ce,unstagePaths:we,applyPatch:Fe,commit:Le}}function ue(t){return[...t.conflicted,...t.unstaged,...t.staged,...t.untracked]}function ot(t,s){return s==="conflict"?t.conflicted:s==="unstaged"?t.unstaged:s==="staged"?t.staged:t.untracked}function re(t,s){const n=new Map(t.map(i=>[i.fileId,i]));for(const i of s)n.set(i.fileId,i);return[...n.values()]}function dt(t,s){const n=new Map(t.map(i=>[i.id,i]));for(const i of s)n.set(i.id,i);return[...n.values()]}function Z(t){return t instanceof Error?t.message:"Git 请求失败。"}function ut(t){return typeof t=="object"&&t!==null&&t.code==="git_revision_conflict"}function ht(t){const s=[];let n=0;const i=t.lines;for(;n<i.length;){const o=i[n];if(o.kind==="deletion"){const h=[];for(;n<i.length&&i[n].kind==="deletion";)h.push(i[n]),n+=1;const g=[];for(;n<i.length&&i[n].kind==="addition";)g.push(i[n]),n+=1;const x=Math.max(h.length,g.length);for(let a=0;a<x;a+=1){const p=h[a]??null,j=g[a]??null;s.push(Object.freeze({key:`${t.hunkId}:${p?.lineId??`-${a}`}:${j?.lineId??`-${a}`}`,left:p,right:j,changed:!0}))}continue}if(o.kind==="addition"){s.push(Object.freeze({key:`${t.hunkId}:${o.lineId}`,left:null,right:o,changed:!0})),n+=1;continue}s.push(Object.freeze({key:`${t.hunkId}:${o.lineId}`,left:o,right:o,changed:!1})),n+=1}return s}function ft(t,s,n){const i=new Set(s),o=[];for(const h of t){if(o.push(Object.freeze({kind:"hunk-header",hunk:h})),i.has(h.hunkId)){o.push(Object.freeze({kind:"folded",hunk:h}));continue}if(n==="side-by-side"){for(const g of ht(h))o.push(Object.freeze({kind:"side-by-side",hunk:h,row:g}));continue}for(const g of h.lines)o.push(Object.freeze({kind:"unified-line",hunk:h,line:g}))}return o}function ve(t){const s=[];for(const n of t){let i=!1;for(const o of n.lines){const h=o.kind==="addition"||o.kind==="deletion";h&&!i&&s.push(Object.freeze({lineId:o.lineId,hunkId:n.hunkId,oldLineNumber:o.oldLineNumber,newLineNumber:o.newLineNumber})),i=h}}return s}function mt(t,s,n=500){const i=s.trim().toLowerCase();if(!i)return[];const o=[];for(const h of t)for(const g of h.lines){if(o.length>=n)return o;g.text.toLowerCase().includes(i)&&o.push(Object.freeze({lineId:g.lineId,hunkId:h.hunkId,side:g.oldLineNumber!==null&&g.newLineNumber!==null?"both":g.oldLineNumber!==null?"old":"new"}))}return o}function pt(t){let s=0,n=0;for(const i of t)for(const o of i.lines)o.kind==="addition"?s+=1:o.kind==="deletion"&&(n+=1);return Object.freeze({additions:s,deletions:n,hunks:t.length})}function ce(t,s){return s?t.replace(/\t/gu,"→ ").replace(/ +$/gu,n=>"·".repeat(n.length)):t}function ge(t){return t==="addition"?"+":t==="deletion"?"-":t==="meta"?"\\":" "}function gt(t,s){const n={schemaVersion:2,type:"review_context",repositoryRevision:t.repositoryRevision,path:t.path,source:t.source,hunk:s.header,hunkId:s.hunkId,complete:s.complete},i=s.lines.map(o=>o.raw).join(`
|
|
1
|
+
var $e=Object.defineProperty;var Pe=(t,s,n)=>s in t?$e(t,s,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[s]=n;var ie=(t,s,n)=>Pe(t,typeof s!="symbol"?s+"":s,n);import{r as l,a as He,j as e,I as T}from"./index-kQqA3li6.js";import{u as Oe,R as Me}from"./useAutoNotice-DPXFWIwK.js";const Ne="orion.web.git-display.v1",_e=Object.freeze(["changes","history","compare"]),me=Object.freeze({message:"",author:"",path:"",sha:"",since:"",until:""}),xe=Object.freeze({view:"changes",group:null,query:"",selectedFileId:null,selectedSource:null,selectedFileIds:Object.freeze([]),selectedLineIds:Object.freeze([]),collapsedHunks:Object.freeze([]),anchorLineId:null,navigatorScrollTop:0,commitDraft:Object.freeze({summary:"",body:""}),historyQuery:me,selectedCommitId:null,selectedCommitParentIndex:0,selectedCommitPath:null,fileHistoryPath:null,notice:""}),pe=Object.freeze({mode:"unified",wrap:!1,showWhitespace:!1,ignoreWhitespace:!1,wordDiff:!1});function Ae(t){return t||"__no-workspace__"}function ze(){const t=new Map;return{read(s){return t.get(s)??xe},update(s,n){const i=Object.freeze({...t.get(s)??xe,...n});return t.set(s,i),i},clear(s){t.delete(s)}}}const Be=ze();function Ge(){try{const t=globalThis.localStorage?.getItem(Ne);if(!t)return pe;const s=JSON.parse(t);return Te(s)}catch{return pe}}function We(t){try{globalThis.localStorage?.setItem(Ne,JSON.stringify(t))}catch{}}function Te(t){if(typeof t!="object"||t===null)return pe;const s=t;return Object.freeze({mode:s.mode==="side-by-side"?"side-by-side":"unified",wrap:s.wrap===!0,showWhitespace:s.showWhitespace===!0,ignoreWhitespace:s.ignoreWhitespace===!0,wordDiff:s.wordDiff===!0})}function qe(t){if(t.entries.length===0)return{fileId:null,path:null,notice:""};const s=t.entries.find(o=>o.fileId===t.previousFileId);if(s)return{fileId:s.fileId,path:s.path,notice:""};const i=t.entries.find(o=>o.path===t.previousPath)??t.entries[0];return{fileId:i.fileId,path:i.path,notice:t.previousFileId===null?"":`原选中的改动已不在此列表,已跳到 ${i.path}。`}}const Qe=959,Ve=620,Ke=640;function Ue(t,s){if(!Number.isFinite(t)||t<=0)return{tier:"wide",narrow:!1,effectiveMode:s,dataWidth:"wide"};const n=t<=Ve?"narrow":t<=Qe?"compact":"wide";return{tier:n,narrow:n==="narrow",effectiveMode:t<=Ke?"unified":s,dataWidth:n}}const Ye=350,je=30;function Xe(t){const{enabled:s,session:n,setSession:i,actions:o,refreshEpoch:h}=t,g=n.historyQuery??me,[x,a]=l.useState(null),[p,j]=l.useState(null),[b,d]=l.useState(null),[S,u]=l.useState(null),[_,L]=l.useState(null),[v,N]=l.useState(null),[Q,P]=l.useState(!1),[B,E]=l.useState(!1),[q,H]=l.useState(!1),[k,R]=l.useState(""),$=l.useRef(0),r=l.useRef(0),I=l.useMemo(()=>({...g.message.trim()?{message:g.message.trim()}:{},...g.author.trim()?{author:g.author.trim()}:{},...g.path.trim()?{path:g.path.trim()}:{},...g.sha.trim()?{sha:g.sha.trim()}:{},...g.since.trim()?{since:g.since.trim()}:{},...g.until.trim()?{until:g.until.trim()}:{}}),[g]);l.useEffect(()=>{if(!s)return;const w=$.current+1;$.current=w,P(!0),R("");const A=setTimeout(()=>{(async()=>{try{const[z,W]=await Promise.all([o.gitRefs(),o.gitHistory({...I,pageSize:je})]);if(w!==$.current)return;a(z),j(W)}catch(z){if(w!==$.current)return;R(le(z))}finally{w===$.current&&P(!1)}})()},I.message||I.author||I.path||I.sha?Ye:0);return()=>clearTimeout(A)},[o,s,h,I]),l.useEffect(()=>{const w=n.selectedCommitId;if(!s||!w){d(null),u(null),L(null);return}const A=r.current+1;r.current=A;const z=n.selectedCommitParentIndex;H(!0),R(""),(async()=>{try{const[W,X]=await Promise.all([o.gitCommitDetail(w,z),o.gitCommitFiles(w,z)]);if(A!==r.current)return;d(W),u(X);const J=X.files.find(U=>!U.sensitive&&!U.binary),ee=n.selectedCommitPath&&X.files.some(U=>U.path===n.selectedCommitPath)?n.selectedCommitPath:J?.path??null;ee!==n.selectedCommitPath&&i({selectedCommitPath:ee}),ee||L(null)}catch(W){if(A!==r.current)return;R(le(W))}finally{A===r.current&&H(!1)}})()},[o,s,n.selectedCommitId,n.selectedCommitParentIndex]),l.useEffect(()=>{const w=n.selectedCommitId,A=n.selectedCommitPath;if(!s||!w||!A){L(null);return}const z=r.current;(async()=>{try{const W=await o.gitCommitDiff({oid:w,path:A,parentIndex:n.selectedCommitParentIndex});if(z!==r.current)return;L(W)}catch(W){if(z!==r.current)return;R(le(W)),L(null)}})()},[o,s,n.selectedCommitId,n.selectedCommitPath,n.selectedCommitParentIndex]),l.useEffect(()=>{const w=n.fileHistoryPath;if(!s||!w){N(null);return}const A=r.current;(async()=>{try{const z=await o.gitFileHistory({path:w});if(A!==r.current)return;N(z)}catch(z){if(A!==r.current)return;R(le(z))}})()},[o,s,n.fileHistoryPath]);const f=l.useCallback(w=>{i({historyQuery:{...g,...w}})},[g,i]),V=l.useCallback(()=>{i({historyQuery:me})},[i]),c=l.useCallback(w=>{i({selectedCommitId:w.id,selectedCommitParentIndex:0,selectedCommitPath:null})},[i]),M=l.useCallback(()=>{i({selectedCommitId:null,selectedCommitPath:null,selectedCommitParentIndex:0})},[i]),G=l.useCallback(w=>{i({selectedCommitParentIndex:w,selectedCommitPath:null})},[i]),K=l.useCallback(w=>i({selectedCommitPath:w}),[i]),Y=l.useCallback(()=>{const w=p?.nextCursor;if(!w||B)return;const A=$.current;E(!0),(async()=>{try{const z=await o.gitHistory({...I,cursor:w,pageSize:je});if(A!==$.current)return;j(W=>W?{...z,items:[...W.items,...z.items]}:z)}catch(z){if(A!==$.current)return;R(le(z))}finally{A===$.current&&E(!1)}})()},[o,B,p,I]),se=l.useCallback(w=>i({fileHistoryPath:w}),[i]),D=l.useCallback(()=>i({fileHistoryPath:null}),[i]);return{query:g,refs:x,page:p,detail:b,files:S,diff:_,fileHistory:v,loading:Q,loadingMore:B,loadingDetail:q,error:k,isEmptyRepository:p!==null&&p.tipOid===null,setQuery:f,resetQuery:V,selectCommit:c,closeDetail:M,setParentIndex:G,selectFile:K,loadMore:Y,openFileHistory:se,closeFileHistory:D}}function le(t){return t?.code==="git_cursor_invalid"?"历史已变化,已重新载入列表。":t instanceof Error?t.message:"Git 历史读取失败。"}function Je(t){if(!(t instanceof HTMLElement))return!1;const s=t.tagName.toLowerCase();return s==="input"||s==="textarea"||s==="select"||t.isContentEditable?!0:t.closest('[role="dialog"]')!==null}function Ze(t){const{enabled:s,session:n,setSession:i,fileIds:o,anchorLineIds:h,onOpenSelection:g,onEscapeFallback:x}=t;l.useEffect(()=>{if(!s)return;const a=p=>{if(Je(p.target))return;if(p.key==="F7"&&h.length>0){p.preventDefault();const b=n.anchorLineId?h.indexOf(n.anchorLineId):-1,d=p.shiftKey?-1:1,S=b<0?d>0?0:h.length-1:Math.min(h.length-1,Math.max(0,b+d));i({anchorLineId:h[S]});return}if(p.key==="Escape"){if(n.query){p.preventDefault(),i({query:"",selectedFileId:null});return}if(n.selectedLineIds.length>0){p.preventDefault(),i({selectedLineIds:[]});return}x();return}if(o.length===0)return;const j=n.selectedFileId?o.indexOf(n.selectedFileId):-1;if(p.key==="ArrowDown"||p.key==="ArrowUp"){p.preventDefault();const b=p.key==="ArrowDown"?1:-1,d=j<0?b>0?0:o.length-1:Math.min(o.length-1,Math.max(0,j+b));i({selectedFileId:o[d],selectedSource:null,anchorLineId:null});return}if(p.key==="Enter"&&j>=0){p.preventDefault(),g();return}if(p.key===" "){if(j<0)return;p.preventDefault();const b=o[j];i({selectedFileIds:n.selectedFileIds.includes(b)?n.selectedFileIds.filter(d=>d!==b):[...n.selectedFileIds,b]})}};return window.addEventListener("keydown",a),()=>window.removeEventListener("keydown",a)},[s,n.query,n.selectedFileId,n.selectedLineIds,n.anchorLineId,n.selectedFileIds,i,o,h,g,x])}class et{constructor(s){ie(this,"fetcher");ie(this,"freshnessMs");ie(this,"slots",new Map);this.fetcher=s.fetcher,this.freshnessMs=s.freshnessMs??3e4}getSnapshot(s){const n=this.slotFor(s);n.invalidated&&(n.inFlight=null,n.invalidated=!1);const i=n.snapshot;if(i&&Date.now()-i.fetchedAt<=this.freshnessMs)return Promise.resolve(i);if(n.inFlight)return n.inFlight;n.generation+=1;const o=n.generation,h=this.fetcher(s).then(g=>{const x=this.slots.get(s);if(!x||x.generation!==o||x.invalidated)throw new tt(s);return x.snapshot=Object.freeze({...g,fetchedAt:Date.now()}),x.inFlight=null,x.snapshot}).catch(g=>{const x=this.slots.get(s);throw x&&x.inFlight===h&&(x.inFlight=null),g});return n.inFlight=h,h}invalidate(s){const n=this.slots.get(s);if(!n)return!1;const i=!!(n.snapshot||n.inFlight);return n.invalidated=!0,n.snapshot=null,i}isStale(s,n){const i=this.slots.get(s)?.snapshot;return i?n==null?!1:i.repositoryRevision!==n:!0}cachedSnapshot(s){const n=this.slots.get(s);return!n||n.invalidated||!n.snapshot?null:n.snapshot}clear(){this.slots.clear()}slotFor(s){const n=this.slots.get(s);if(n)return n;const i={workspaceId:s,snapshot:null,inFlight:null,generation:0,invalidated:!1};return this.slots.set(s,i),i}}class tt extends Error{constructor(n){super(`Repository snapshot for ${n} is stale; refetching.`);ie(this,"code","workspace_snapshot_stale")}}const st=20*1024*1024,nt=5*1024*1024;function it(t){let s=0;for(const n of t.hunks){s+=n.header.length;for(const i of n.lines)s+=i.raw.length+48}return s+t.path.length+256}function lt(t){const s=st,n=nt,i=new Map;let o=0,h=0,g=0;const x=(a,p)=>`${a}\0${p}`;return{read(a,p){const j=x(a,p),b=i.get(j);return b?(i.delete(j),i.set(j,b),h+=1,b.document):(g+=1,null)},write(a,p,j){const b=it(j);if(b>n)return;const d=x(a,p),S=i.get(d);for(S&&(i.delete(d),o-=S.bytes),i.set(d,{document:j,bytes:b}),o+=b;o>s&&i.size>0;){const u=i.keys().next();if(u.done)break;const _=i.get(u.value);i.delete(u.value),o-=_?.bytes??0}},clear(){i.clear(),o=0},stats(){return{entries:i.size,bytes:o,hits:h,misses:g}}}}const at=lt(),rt=2e3;function ct(t){const{workspaceId:s,refreshEpoch:n,actions:i}=t,o=t.store??Be,h=t.cache??at,g=Ae(s),[x,a]=l.useState(()=>o.read(g)),[p,j]=l.useState(()=>Ge()),[b,d]=l.useState(null),[S,u]=l.useState(null),[_,L]=l.useState(null),[v,N]=l.useState(!1),[Q,P]=l.useState(!1),[B,E]=l.useState(""),[q,H]=l.useState(!1),k=l.useRef(0),R=l.useRef(0),$=l.useRef(x);$.current=x;const r=l.useRef(b),I=l.useRef(p);I.current=p,r.current=b;const f=l.useCallback(m=>{a(o.update(g,m))},[g,o]),V=l.useCallback(m=>{j(y=>{const F=Object.freeze({...y,...m});return We(F),F})},[]),c=l.useCallback(async(m,y)=>{if(m===k.current){y.quiet?P(!0):N(!0),E("");try{const F=$.current,[C,O]=await Promise.all([i.gitStatus(void 0,{...F.group?{group:F.group}:{},...F.query?{query:F.query}:{}}),i.gitLog()]);if(m!==k.current)return;if(d(C),u(O),!C.hasWorktree){L(null),H(!1);return}const ne=ue(C);if(!y.keepSelection){L(null),f({selectedFileId:null,selectedSource:null,anchorLineId:null}),H(!1);return}const ae=F.selectedFileId,Re=ne.find(de=>de.fileId===ae)?.path??(_?_.path:null),oe=qe({entries:ne,previousFileId:ae,previousPath:Re});if(oe.fileId&&oe.fileId!==ae){f({selectedFileId:null,selectedSource:null,notice:oe.notice}),L(null),H(!1);return}if(ae){const de=_!==null&&C.repositoryRevision===_.repositoryRevision;H(!de)}else H(!1)}catch(F){if(m!==k.current)return;E(Z(F))}finally{m===k.current&&(N(!1),P(!1))}}},[i,_,f]),M=l.useCallback(()=>{const m=k.current+1;k.current=m,R.current+=1,c(m,{keepSelection:!0,quiet:b!==null})},[c,b]);l.useEffect(()=>{const m=k.current+1;k.current=m,R.current+=1,d(null),u(null),L(null),E(""),H(!1),N(!!s),a(o.read(g)),s?c(m,{keepSelection:!1}):N(!1)},[g,s,o]),l.useEffect(()=>{if(!s||n===0)return;const m=k.current+1;k.current=m,R.current+=1,c(m,{keepSelection:!0,quiet:!0})},[n]),l.useEffect(()=>{if(!s||typeof document>"u")return;let m;const y=()=>{m!==void 0&&clearInterval(m),m=void 0},F=()=>{y(),m=setInterval(()=>{const O=k.current+1;k.current=O,c(O,{keepSelection:!0,quiet:!0})},rt)},C=()=>{if(document.visibilityState==="visible"){const O=k.current+1;k.current=O,c(O,{keepSelection:!0,quiet:!0}),F()}else y()};return document.visibilityState==="visible"&&F(),document.addEventListener("visibilitychange",C),()=>{y(),document.removeEventListener("visibilitychange",C)}},[s,x.group,x.query]);const G=l.useCallback(m=>{const y=R.current+1;R.current=y,f({selectedLineIds:[],selectedFileId:m.fileId,selectedSource:m.source,anchorLineId:null,notice:""}),E("");const F=r.current?.repositoryRevision,C=F?h.read(F,m.fileId):null;if(C){L(C),H(!1),N(!1);return}L(null),N(!0),(async()=>{try{const O=await i.gitDiffDocument(m.fileId,void 0,I.current.ignoreWhitespace,I.current.wordDiff);if(y!==R.current)return;h.write(O.repositoryRevision,m.fileId,O),L(O),H(O.repositoryRevision!==r.current?.repositoryRevision)}catch(O){if(y!==R.current)return;if(ut(O)){E("仓库已变化,已重新载入 Git 状态。"),f({selectedFileId:null,selectedSource:null});return}E(Z(O))}finally{y===R.current&&N(!1)}})()},[i,h,f]),K=l.useCallback(()=>{const m=b?.nextCursor;if(!m)return;const y=k.current,F=b?.repositoryRevision;(async()=>{try{const C=await i.gitStatus(m,{...x.group?{group:x.group}:{},...x.query?{query:x.query}:{}});if(y!==k.current)return;if(F!==void 0&&C.repositoryRevision!==F){E("仓库已变化,已重新载入 Git 状态。");return}d(O=>O&&{...C,conflicted:re(O.conflicted,C.conflicted),staged:re(O.staged,C.staged),unstaged:re(O.unstaged,C.unstaged),untracked:re(O.untracked,C.untracked),counts:{...C.counts,loaded:O.counts.loaded+C.counts.loaded}})}catch(C){if(y!==k.current)return;E(Z(C))}})()},[i,x.group,x.query,b]),Y=l.useCallback(()=>{const m=S?.nextCursor;if(!m)return;const y=k.current;(async()=>{try{const F=await i.gitLog(m);if(y!==k.current)return;u(C=>C&&{...F,items:dt(C.items,F.items)})}catch(F){if(y!==k.current)return;E(Z(F))}})()},[i,S]),se=l.useCallback(()=>{const m=_?.nextCursor,y=x.selectedFileId;if(!m||!y)return;const F=k.current,C=R.current;(async()=>{try{const O=await i.gitDiffDocument(y,m,I.current.ignoreWhitespace,I.current.wordDiff);if(F!==k.current||C!==R.current)return;L(ne=>ne?{...O,hunks:[...ne.hunks,...O.hunks]}:O)}catch(O){if(F!==k.current||C!==R.current)return;E(Z(O))}})()},[i,_,x.selectedFileId]);l.useEffect(()=>{!b||!x.selectedFileId||ue(b).some(y=>y.fileId===x.selectedFileId)||(L(null),f({selectedFileId:null,selectedSource:null,anchorLineId:null}))},[b,x.selectedFileId,f]);const D=l.useMemo(()=>!b||!x.selectedFileId?null:ue(b).find(m=>m.fileId===x.selectedFileId)??null,[b,x.selectedFileId]),[w,A]=l.useState(!1),[z,W]=l.useState(null),X=l.useRef(null);X.current===null&&(X.current=new et({fetcher:async m=>{const y=await i.gitStatus(void 0,{});return Object.freeze({repositoryRevision:y.repositoryRevision,branch:y.branch,counts:Object.freeze({totalChangedFiles:y.counts.total,staged:y.counts.staged,unstaged:y.counts.unstaged,untracked:y.counts.untracked,conflicted:y.counts.conflicted}),fetchedAt:Date.now()})}}));const J=X.current,ee=l.useCallback(async()=>{const m=r.current;if(m?.hasWorktree)try{if((await J.getSnapshot(s)).repositoryRevision===m.repositoryRevision)return;J.invalidate(s),await c(k.current,{keepSelection:!0,quiet:!0})}catch{}},[c,J,s]);l.useEffect(()=>{if(!s)return;let m=!1;const y=()=>{m||document.hidden||(m=!0,ee().finally(()=>{m=!1}))},F=()=>{document.hidden||y()};return window.addEventListener("focus",y),document.addEventListener("visibilitychange",F),()=>{window.removeEventListener("focus",y),document.removeEventListener("visibilitychange",F)}},[ee,s]);const U=l.useCallback(async()=>{h.clear(),J.invalidate(s);const m=k.current+1;k.current=m,R.current+=1,await c(m,{keepSelection:!0,quiet:!0})},[h,c,J,s]),te=l.useCallback(async(m,y)=>{if(!w){A(!0),E("");try{await m(),await U(),f({notice:y})}catch(F){E(Z(F))}finally{A(!1)}}},[w,U,f]),Ce=l.useCallback(async m=>{const y=r.current?.repositoryRevision;y&&(await te(()=>i.gitStage(m,y),m.length>1?`已暂存 ${m.length} 个文件。`:"已暂存。"),f({selectedFileIds:[]}))},[i,te,f]),we=l.useCallback(async m=>{const y=r.current?.repositoryRevision;y&&(await te(()=>i.gitUnstage(m,y),m.length>1?`已取消暂存 ${m.length} 个文件。`:"已取消暂存。"),f({selectedFileIds:[]}))},[i,te,f]),Fe=l.useCallback(async m=>{const y=r.current?.repositoryRevision;y&&await te(()=>i.gitApplyPatch({...m,expectedRepositoryRevision:y}),"已应用所选改动到索引。")},[i,te]),Le=l.useCallback(async m=>{const y=r.current?.repositoryRevision;if(!y)throw new Error("Git repository is unavailable.");A(!0),E("");const F=He();try{const C=await i.gitCommit({summary:m.summary,...m.body?{body:m.body}:{},expectedRepositoryRevision:y,requestId:F});return await U(),f({commitDraft:{summary:"",body:""},notice:C.warning?`已提交 ${C.commitSha.slice(0,8)},但:${C.warning}`:`已提交 ${C.commitSha.slice(0,8)}。`}),C.commitSha}catch(C){throw E(Z(C)),C}finally{A(!1)}},[i,U,f]),De=l.useCallback(m=>{const y=$.current.selectedFileIds;f({selectedFileIds:y.includes(m)?y.filter(F=>F!==m):[...y,m]})},[f]),Ee=l.useCallback(()=>f({selectedFileIds:[]}),[f]);return l.useEffect(()=>{if(!b?.hasWorktree){W(null);return}let m=!1;return(async()=>{try{const y=await i.gitCommitPreview();m||W(y)}catch{m||W(null)}})(),()=>{m=!0}},[i,b?.hasWorktree,b?.repositoryRevision]),{session:x,display:p,status:b,log:S,diff:_,loading:v,refreshing:Q,error:B,stale:q,setSession:f,setDisplay:V,openFile:G,reload:M,loadMoreStatus:K,loadMoreLog:Y,loadMoreDiff:se,selectedEntry:D,busy:w,commitPreview:z,toggleSelection:De,clearSelection:Ee,stagePaths:Ce,unstagePaths:we,applyPatch:Fe,commit:Le}}function ue(t){return[...t.conflicted,...t.unstaged,...t.staged,...t.untracked]}function ot(t,s){return s==="conflict"?t.conflicted:s==="unstaged"?t.unstaged:s==="staged"?t.staged:t.untracked}function re(t,s){const n=new Map(t.map(i=>[i.fileId,i]));for(const i of s)n.set(i.fileId,i);return[...n.values()]}function dt(t,s){const n=new Map(t.map(i=>[i.id,i]));for(const i of s)n.set(i.id,i);return[...n.values()]}function Z(t){return t instanceof Error?t.message:"Git 请求失败。"}function ut(t){return typeof t=="object"&&t!==null&&t.code==="git_revision_conflict"}function ht(t){const s=[];let n=0;const i=t.lines;for(;n<i.length;){const o=i[n];if(o.kind==="deletion"){const h=[];for(;n<i.length&&i[n].kind==="deletion";)h.push(i[n]),n+=1;const g=[];for(;n<i.length&&i[n].kind==="addition";)g.push(i[n]),n+=1;const x=Math.max(h.length,g.length);for(let a=0;a<x;a+=1){const p=h[a]??null,j=g[a]??null;s.push(Object.freeze({key:`${t.hunkId}:${p?.lineId??`-${a}`}:${j?.lineId??`-${a}`}`,left:p,right:j,changed:!0}))}continue}if(o.kind==="addition"){s.push(Object.freeze({key:`${t.hunkId}:${o.lineId}`,left:null,right:o,changed:!0})),n+=1;continue}s.push(Object.freeze({key:`${t.hunkId}:${o.lineId}`,left:o,right:o,changed:!1})),n+=1}return s}function ft(t,s,n){const i=new Set(s),o=[];for(const h of t){if(o.push(Object.freeze({kind:"hunk-header",hunk:h})),i.has(h.hunkId)){o.push(Object.freeze({kind:"folded",hunk:h}));continue}if(n==="side-by-side"){for(const g of ht(h))o.push(Object.freeze({kind:"side-by-side",hunk:h,row:g}));continue}for(const g of h.lines)o.push(Object.freeze({kind:"unified-line",hunk:h,line:g}))}return o}function ve(t){const s=[];for(const n of t){let i=!1;for(const o of n.lines){const h=o.kind==="addition"||o.kind==="deletion";h&&!i&&s.push(Object.freeze({lineId:o.lineId,hunkId:n.hunkId,oldLineNumber:o.oldLineNumber,newLineNumber:o.newLineNumber})),i=h}}return s}function mt(t,s,n=500){const i=s.trim().toLowerCase();if(!i)return[];const o=[];for(const h of t)for(const g of h.lines){if(o.length>=n)return o;g.text.toLowerCase().includes(i)&&o.push(Object.freeze({lineId:g.lineId,hunkId:h.hunkId,side:g.oldLineNumber!==null&&g.newLineNumber!==null?"both":g.oldLineNumber!==null?"old":"new"}))}return o}function pt(t){let s=0,n=0;for(const i of t)for(const o of i.lines)o.kind==="addition"?s+=1:o.kind==="deletion"&&(n+=1);return Object.freeze({additions:s,deletions:n,hunks:t.length})}function ce(t,s){return s?t.replace(/\t/gu,"→ ").replace(/ +$/gu,n=>"·".repeat(n.length)):t}function ge(t){return t==="addition"?"+":t==="deletion"?"-":t==="meta"?"\\":" "}function gt(t,s){const n={schemaVersion:2,type:"review_context",repositoryRevision:t.repositoryRevision,path:t.path,source:t.source,hunk:s.header,hunkId:s.hunkId,complete:s.complete},i=s.lines.map(o=>o.raw).join(`
|
|
2
2
|
`).slice(0,12e3);return[`请审阅 ${t.path} 的这个 diff hunk,并指出正确性、安全性和测试风险:`,"","```review_context",JSON.stringify(n,null,2),"```","","```diff",i,"```"].join(`
|
|
3
3
|
`)}function be({document:t,mode:s,wrap:n,showWhitespace:i,collapsedHunks:o,anchorLineId:h,loading:g=!1,stale:x=!1,onChangeMode:a,onToggleWrap:p,onToggleWhitespace:j,onToggleHunk:b,hunkAction:d=null,selectedLineIds:S=[],onToggleLine:u,ignoreWhitespace:_=!1,onToggleIgnoreWhitespace:L,wordDiff:v=!1,onToggleWordDiff:N,onSetAllFolded:Q,onAnchorChange:P,onNotice:B,onLoadMore:E,onSendToComposer:q}){const H=l.useRef(null),[k,R]=l.useState(!1),[$,r]=l.useState(""),[I,f]=l.useState(0),V=l.useMemo(()=>ft(t.hunks,o,s),[t.hunks,o,s]),c=l.useMemo(()=>ve(t.hunks),[t.hunks]),M=l.useMemo(()=>pt(t.hunks),[t.hunks]),G=l.useMemo(()=>k?mt(t.hunks,$):[],[t.hunks,k,$]);l.useEffect(()=>{!h||!H.current||H.current.querySelector(`[data-line-id="${CSS.escape(h)}"]`)?.scrollIntoView({block:"center"})},[h]),l.useEffect(()=>{f(0)},[$]);const K=l.useMemo(()=>h?c.findIndex(D=>D.lineId===h):-1,[c,h]),Y=D=>{if(c.length===0){B("这个文件没有改动。");return}const w=K<0?D>0?-1:0:K,A=Math.min(c.length-1,Math.max(0,w+D));P(c[A].lineId)},se=D=>{if(D.key==="F7"){D.preventDefault(),Y(D.shiftKey?-1:1);return}if(D.key==="Escape"){k&&(D.stopPropagation(),R(!1),r(""));return}if(D.key==="Enter"&&k&&G.length>0){D.preventDefault();const w=D.shiftKey?(I-1+G.length)%G.length:(I+1)%G.length;f(w),P(G[w].lineId)}};return t.kind==="binary"||t.kind==="symlink"||t.kind==="submodule"?e.jsxs("div",{className:"diff-viewer",children:[e.jsx(he,{doc:t,stats:M,stale:x}),e.jsxs("div",{className:"resource-empty compact",children:[e.jsx(T,{name:"code"}),e.jsx("strong",{children:Nt(t.kind)}),e.jsx("p",{children:"只展示变更事实,不返回可预览内容。"}),e.jsx("ul",{className:"diff-meta-list",children:t.meta.slice(0,8).map(D=>e.jsx("li",{children:e.jsx("code",{children:D})},D))})]})]}):t.kind==="metadata"?e.jsxs("div",{className:"diff-viewer",children:[e.jsx(he,{doc:t,stats:M,stale:x}),e.jsxs("div",{className:"resource-empty compact",children:[e.jsx(T,{name:"code"}),e.jsx("strong",{children:"仅有元数据变化"}),e.jsx("p",{children:"没有文本 Hunk,但文件确实变了。"}),e.jsx("ul",{className:"diff-meta-list",children:t.meta.slice(0,8).map(D=>e.jsx("li",{children:e.jsx("code",{children:D})},D))})]})]}):e.jsxs("div",{className:"diff-viewer",children:[e.jsx(he,{doc:t,stats:M,stale:x}),e.jsxs("div",{className:"diff-toolbar",role:"toolbar","aria-label":"Diff 显示选项",children:[e.jsxs("div",{className:"diff-mode-group",role:"group","aria-label":"Diff 布局",children:[e.jsx("button",{type:"button",className:"text-button","aria-pressed":s==="unified",onClick:()=>a("unified"),children:"统一"}),e.jsx("button",{type:"button",className:"text-button","aria-pressed":s==="side-by-side",onClick:()=>a("side-by-side"),children:"并排"})]}),e.jsx("button",{type:"button",className:"text-button","aria-pressed":n,onClick:p,children:"换行"}),e.jsx("button",{type:"button",className:"text-button","aria-pressed":i,onClick:j,children:"空白字符"}),N?e.jsx("button",{type:"button",className:`text-button ${v?"diff-toggle-on":""}`,"aria-pressed":v,title:"词级差异(重新读取 Diff)",onClick:N,children:"词级"}):null,L?e.jsx("button",{type:"button",className:`text-button ${_?"diff-toggle-on":""}`,"aria-pressed":_,title:"忽略空白差异(重新读取 Diff)",onClick:L,children:"忽略空白"}):null,e.jsx("button",{type:"button",className:"text-button",onClick:()=>Q(o.length===0),children:o.length===0?"折叠全部":"展开全部"}),e.jsx("button",{type:"button",className:"text-button",onClick:()=>Y(-1),disabled:c.length===0,"aria-label":"上一个改动",children:"↑改动"}),e.jsx("button",{type:"button",className:"text-button",onClick:()=>Y(1),disabled:c.length===0,"aria-label":"下一个改动",children:"↓改动"}),e.jsx("span",{className:"diff-anchor-count","aria-live":"polite",children:c.length===0?"无改动":`改动 ${Math.max(1,K+1)}/${c.length}`}),e.jsx("button",{type:"button",className:"text-button","aria-pressed":k,onClick:()=>R(D=>!D),children:"查找"})]}),k?e.jsxs("div",{className:"diff-find",children:[e.jsx("input",{type:"search",value:$,autoFocus:!0,"aria-label":"在已加载的 Diff 中查找",placeholder:"在已加载内容中查找",onChange:D=>r(D.target.value)}),e.jsxs("span",{"aria-live":"polite",children:[$.trim()?G.length===0?"无命中":`${I+1}/${G.length}`:"输入以查找",t.completeness!=="complete"?" · 仅已加载内容":""]})]}):null,t.completeness==="paged"?e.jsx("p",{className:"resource-notice",role:"status",children:"当前为分页预览,未加载的 Hunk 不能执行任何写入。"}):null,e.jsx("div",{className:`diff-body diff-body-${s} ${n?"diff-wrap":""}`,ref:H,tabIndex:0,role:"region","aria-label":`Diff ${t.path}`,onKeyDown:se,children:V.length===0?e.jsxs("div",{className:"resource-empty compact",children:[e.jsx(T,{name:"check"}),e.jsx("strong",{children:"没有文本改动"})]}):V.map(D=>e.jsx(bt,{row:D,mode:s,showWhitespace:i,folded:o.includes(kt(D).hunkId),activeAnchor:h,onToggleHunk:b,hunkAction:d,selectedLineIds:S,onToggleLine:u,wordDiff:v},yt(D)))}),e.jsxs("footer",{className:"diff-footer",children:[q?e.jsx("button",{type:"button",className:"secondary-button",disabled:t.hunks.length===0,onClick:()=>q(gt({path:t.path,source:t.source,repositoryRevision:t.repositoryRevision},t.hunks[Math.max(0,c.findIndex(D=>D.lineId===h))]??t.hunks[0])),children:"发送当前 Hunk 到对话"}):null,t.nextCursor&&E?e.jsx("button",{type:"button",className:"text-button",onClick:E,disabled:g,children:g?"加载中…":"加载更多 Diff"}):null,e.jsxs("span",{className:"diff-footer-hint",children:[t.capabilities.stageFile?"可暂存此文件":t.capabilities.unstageFile?"可取消暂存此文件":"只读",t.capabilities.reason?` · ${t.capabilities.reason}`:""]})]})]})}function he({doc:t,stats:s,stale:n}){return e.jsxs("header",{children:[e.jsx("strong",{title:t.path,children:t.path}),e.jsx("span",{className:"diff-source",children:vt(t.source)}),e.jsxs("span",{className:"diff-stat-add",children:["+",s.additions]}),e.jsxs("span",{className:"diff-stat-del",children:["-",s.deletions]}),e.jsxs("span",{children:[s.hunks," 个 Hunk"]}),n?e.jsx("span",{className:"diff-stale",role:"status",children:"已变化"}):null]})}function bt({row:t,mode:s,showWhitespace:n,folded:i,activeAnchor:o,onToggleHunk:h,hunkAction:g=null,selectedLineIds:x=[],onToggleLine:a,wordDiff:p=!1}){if(t.kind==="hunk-header")return e.jsxs("div",{className:"diff-hunk-header",children:[e.jsx("button",{type:"button",className:"diff-fold","aria-expanded":!i,"aria-label":i?"展开 Hunk":"折叠 Hunk",onClick:()=>h(t.hunk.hunkId),children:i?"▸":"▾"}),e.jsx("code",{children:t.hunk.header}),t.hunk.complete?null:e.jsx("em",{className:"diff-incomplete",children:"未完整加载"}),g&&t.hunk.complete?e.jsx("button",{type:"button",className:"text-button diff-hunk-action","aria-label":g.label,onClick:()=>g.run(t.hunk.hunkId),children:g.label}):null]});if(t.kind==="folded")return e.jsxs("button",{type:"button",className:"diff-folded-row",onClick:()=>h(t.hunk.hunkId),children:["已折叠 ",t.hunk.lines.length," 行 · 点击展开"]});if(t.kind==="side-by-side")return e.jsx(jt,{row:t.row,showWhitespace:n});const j=t.line;return e.jsxs("div",{className:`diff-line diff-line-${j.kind} ${o===j.lineId?"diff-line-anchor":""}`,"data-line-id":j.lineId,"data-side":j.oldLineNumber!==null&&j.newLineNumber!==null?"both":j.oldLineNumber!==null?"old":"new",children:[e.jsx("span",{className:"diff-gutter",children:j.oldLineNumber??""}),e.jsx("span",{className:"diff-gutter",children:j.newLineNumber??""}),a&&(j.kind==="addition"||j.kind==="deletion")?e.jsx("input",{type:"checkbox",className:"diff-line-select","aria-label":`选择第 ${j.newLineNumber??j.oldLineNumber??""} 行`,checked:x.includes(j.lineId),onChange:()=>a(j.lineId)}):null,e.jsx("span",{className:"diff-marker","aria-hidden":"true",children:ge(j.kind)}),e.jsx("span",{className:"diff-code",children:p?xt(ce(j.text,n)):ce(j.text,n)||" "}),null]})}function xt(t){if(!t)return" ";const s=[],n=/\[-([\s\S]*?)-\]|\{\+([\s\S]*?)\+\}/gu;let i=0,o,h=0;for(;(o=n.exec(t))!==null;)o.index>i&&s.push(t.slice(i,o.index)),o[1]!==void 0?s.push(e.jsx("em",{className:"diff-word-del",children:o[1]},h++)):s.push(e.jsx("em",{className:"diff-word-add",children:o[2]},h++)),i=o.index+o[0].length;return i<t.length&&s.push(t.slice(i)),s.length>0?s:" "}function jt({row:t,showWhitespace:s}){return e.jsxs("div",{className:`diff-sbs-row ${t.changed?"diff-sbs-changed":""}`,children:[e.jsxs("div",{className:`diff-sbs-side ${t.left?.kind==="deletion"?"diff-line-deletion":""}`,"data-line-id":t.left?.lineId??void 0,children:[e.jsx("span",{className:"diff-gutter",children:t.left?.oldLineNumber??""}),e.jsx("span",{className:"diff-marker","aria-hidden":"true",children:t.left?ge(t.left.kind):" "}),e.jsx("span",{className:"diff-code",children:t.left?ce(t.left.text,s)||" ":""})]}),e.jsxs("div",{className:`diff-sbs-side ${t.right?.kind==="addition"?"diff-line-addition":""}`,"data-line-id":t.right?.lineId??void 0,children:[e.jsx("span",{className:"diff-gutter",children:t.right?.newLineNumber??""}),e.jsx("span",{className:"diff-marker","aria-hidden":"true",children:t.right?ge(t.right.kind):" "}),e.jsx("span",{className:"diff-code",children:t.right?ce(t.right.text,s)||" ":""})]})]})}function yt(t){return t.kind==="hunk-header"?`h:${t.hunk.hunkId}`:t.kind==="folded"?`f:${t.hunk.hunkId}`:t.kind==="side-by-side"?t.row.key:t.line.lineId}function kt(t){return t.hunk}function Nt(t){return t==="binary"?"二进制差异":t==="symlink"?"符号链接变化":t==="submodule"?"Submodule 指针变化":"元数据变化"}function vt(t){return t==="staged"?"HEAD → 索引":t==="unstaged"?"索引 → 工作区":t==="untracked"?"空 → 未跟踪文件":"冲突版本"}const ye=Object.freeze({snapshot:"两端快照 A → B","merge-base":"共同祖先 → B"});function It({refs:t,actions:s,display:n,collapsedHunks:i,anchorLineId:o,onDisplayChange:h,onToggleHunk:g,onSetAllFolded:x,onAnchorChange:a,onNotice:p,onSendToComposer:j}){const b=l.useMemo(()=>St(t),[t]),d=b.find(f=>f.isBranch)?.value??b[0]?.value??"",S=t?.refs.find(f=>f.isHead)?.shortName??d,[u,_]=l.useState(d),[L,v]=l.useState(S),[N,Q]=l.useState("snapshot"),[P,B]=l.useState(null),[E,q]=l.useState(null),[H,k]=l.useState(!1),[R,$]=l.useState(""),r=()=>{!u||!L||H||(k(!0),$(""),(async()=>{try{const f=await s.gitCompare({baseRef:u,headRef:L,mode:N});B(f),q(null)}catch(f){$(ke(f))}finally{k(!1)}})())},I=f=>{!P||f.sensitive||H||(k(!0),$(""),(async()=>{try{const V=await s.gitCompareFileDiff({baseOid:P.baseOid,headOid:P.headOid,path:f.path});q(V)}catch(V){$(ke(V))}finally{k(!1)}})())};return b.length===0?e.jsxs("div",{className:"resource-empty",children:[e.jsx(T,{name:"branch",size:16}),e.jsx("strong",{children:"没有可比较的 ref"}),e.jsx("p",{children:"这个仓库还没有分支或标签。"})]}):e.jsxs("div",{className:"git-compare","aria-label":"分支与版本比较",children:[e.jsxs("div",{className:"git-compare-form",children:[e.jsxs("label",{children:[e.jsx("span",{children:"基准 A"}),e.jsx("select",{value:u,"aria-label":"选择基准 A",onChange:f=>_(f.target.value),children:b.map(f=>e.jsx("option",{value:f.value,children:f.label},f.value))})]}),e.jsxs("label",{children:[e.jsx("span",{children:"目标 B"}),e.jsx("select",{value:L,"aria-label":"选择目标 B",onChange:f=>v(f.target.value),children:b.map(f=>e.jsx("option",{value:f.value,children:f.label},f.value))})]}),e.jsxs("label",{children:[e.jsx("span",{children:"模式"}),e.jsx("select",{value:N,"aria-label":"选择比较模式",onChange:f=>Q(f.target.value),children:Object.keys(ye).map(f=>e.jsx("option",{value:f,children:ye[f]},f))})]}),e.jsx("button",{type:"button",className:"primary-button",disabled:H||u===L,"aria-busy":H,onClick:r,children:H?"比较中…":"比较"})]}),e.jsx("p",{className:"git-compare-hint",children:"修改选择不会立即发起查询;点「比较」或按 Enter 才执行。交换 A/B 后会取消旧的请求。"}),R?e.jsx("p",{className:"resource-error",role:"alert",children:R}):null,P?e.jsxs(e.Fragment,{children:[e.jsxs("p",{className:"git-compare-range",children:[P.baseLabel," ",e.jsx("code",{children:P.baseOid.slice(0,10)})," → ",P.headLabel," ",e.jsx("code",{children:P.headOid.slice(0,10)}),P.truncated?" · 文件列表已截断":""]}),e.jsxs("p",{className:"git-compare-range",children:[P.files.length," 个文件 ·",e.jsxs("em",{className:"diff-stat-add",children:[" +",P.additions]}),e.jsxs("em",{className:"diff-stat-del",children:[" -",P.deletions]}),e.jsx("button",{type:"button",className:"text-button",onClick:r,disabled:H,children:"重新比较"})]}),e.jsx("ul",{className:"git-compare-files",children:P.files.map(f=>e.jsx("li",{children:e.jsxs("button",{type:"button",className:E?.path===f.path?"selected":"","aria-current":E?.path===f.path?"true":void 0,disabled:f.sensitive,onClick:()=>I(f),children:[e.jsx("span",{className:"git-commit-file-stats",children:f.binary?"bin":`+${f.additions} -${f.deletions}`}),e.jsx("span",{title:f.path,children:f.renamedFrom?`${f.renamedFrom} → ${f.path}`:f.path}),f.sensitive?e.jsx("em",{className:"git-sensitive-badge",children:"受保护"}):null]})},`${f.renamedFrom??""}:${f.path}`))}),E?e.jsx(be,{document:E,mode:n.mode,wrap:n.wrap,showWhitespace:n.showWhitespace,collapsedHunks:i,anchorLineId:o,loading:H,onChangeMode:f=>h({mode:f}),onToggleWrap:()=>h({wrap:!n.wrap}),onToggleWhitespace:()=>h({showWhitespace:!n.showWhitespace}),onToggleHunk:g,onSetAllFolded:x,onAnchorChange:a,onNotice:p,onSendToComposer:j}):e.jsx("p",{className:"git-compare-hint",children:"点一个文件查看该比较下的 Diff。"})]}):null]})}function St(t){return t?t.refs.map(s=>Object.freeze({value:s.shortName,label:s.kind==="branch"?s.shortName:`${s.shortName}(${s.kind==="tag"?"tag":s.kind==="remote"?"远程":"其他"})`,isBranch:s.kind==="branch"})):[]}function ke(t){const s=t?.code;return s==="git_merge_base_multiple"?"这两个 ref 有多个共同祖先,没有唯一分叉点;请改用「两端快照」模式。":s==="git_merge_base_missing"?"两个 ref 没有共同祖先,无法用「共同祖先」模式;请改用「两端快照」模式。":s==="git_shallow_history"?"仓库缺少完成该比较所需的对象(浅克隆?)。":s==="git_ref_not_found"?"所选 ref 无法解析为提交。":t instanceof Error?t.message:"比较失败。"}const Ct=Object.freeze({"both-modified":"双方都修改","both-added":"双方都新增","added-by-us":"我们新增 · 对方删除","added-by-them":"对方新增 · 我们删除","deleted-by-us":"我们删除 · 对方修改","deleted-by-them":"对方删除 · 我们修改","both-deleted":"双方都删除",other:"其他冲突形态"}),wt=Object.freeze({base:"base(共同祖先)",ours:"ours(当前分支)",theirs:"theirs(合入分支)",worktree:"工作区文件",revision:"该版本"});function Ft({path:t,actions:s}){const[n,i]=l.useState(null),[o,h]=l.useState(""),[g,x]=l.useState(!0);if(l.useEffect(()=>{let p=!1;return x(!0),h(""),(async()=>{try{const j=await s.gitConflictVersions(t);p||i(j)}catch(j){p||(i(null),h(j instanceof Error?j.message:"冲突详情读取失败。"))}finally{p||x(!1)}})(),()=>{p=!0}},[s,t]),g)return e.jsx("p",{className:"resource-loading",children:"正在读取冲突版本…"});if(o)return e.jsx("p",{className:"resource-error",role:"alert",children:o});if(!n)return null;const a=[n.base,n.ours,n.theirs,n.worktree];return e.jsxs("section",{className:"git-conflict","aria-label":"冲突版本",children:[e.jsxs("header",{className:"git-conflict-header",children:[e.jsx(T,{name:"warning",size:14}),e.jsx("strong",{children:n.path}),e.jsx("em",{className:"git-merge-badge",children:Ct[n.shape]})]}),e.jsx("p",{className:"git-compare-hint",children:"直接读 index 的 stage 1/2/3,不用普通暂存 Diff 代替完整冲突视图。缺失的版本是这种冲突形态的正常结果。"}),e.jsx("div",{className:"git-conflict-grid",children:a.map(p=>e.jsxs("article",{className:"git-conflict-version",children:[e.jsxs("header",{children:[e.jsx("span",{children:wt[p.label]}),p.exists?e.jsx("code",{children:p.oid?.slice(0,10)}):e.jsx("em",{className:"git-conflict-absent",children:"此形态下不存在"})]}),p.exists?p.lfs?e.jsxs("p",{className:"git-conflict-note",children:["LFS 指针 · oid ",p.lfs.oid.slice(0,16),"…",p.lfs.size!==null?` · ${p.lfs.size} 字节(不下载对象)`:""]}):p.binary?e.jsxs("p",{className:"git-conflict-note",children:["二进制内容(",p.byteSize," 字节),不在此处渲染。"]}):p.truncated?e.jsxs("p",{className:"git-conflict-note",children:["超过上限(",p.byteSize," 字节),未读取内容。"]}):e.jsx("pre",{className:"git-conflict-content",children:p.content??""}):e.jsx("p",{className:"git-conflict-note",children:"没有这个版本。"})]},p.label))})]})}function Lt({history:t,actions:s,display:n,collapsedHunks:i,anchorLineId:o,onDisplayChange:h,onToggleHunk:g,onSetAllFolded:x,onAnchorChange:a,onNotice:p,onSendToComposer:j}){const{page:b,detail:d,files:S,diff:u,fileHistory:_,error:L}=t,[v,N]=l.useState(null),[Q,P]=l.useState(!1),[B,E]=l.useState(null),[q,H]=l.useState(!1),[k,R]=l.useState(null),$=l.useMemo(()=>(b?.items??[]).filter(r=>r.isMerge).length,[b]);return t.isEmptyRepository?e.jsxs("div",{className:"resource-empty",children:[e.jsx(T,{name:"branch",size:16}),e.jsx("strong",{children:"尚无提交"}),e.jsx("p",{children:"这个仓库还没有提交。新文件仍可暂存并完成首次提交。"})]}):e.jsxs("div",{className:"git-history","aria-label":"Git 历史",children:[e.jsxs("div",{className:"git-history-search",children:[e.jsx("input",{type:"search","aria-label":"搜索提交消息",placeholder:"搜索提交消息…",value:t.query.message,onChange:r=>t.setQuery({message:r.target.value})}),e.jsx("input",{type:"search","aria-label":"按作者筛选",placeholder:"作者",value:t.query.author,onChange:r=>t.setQuery({author:r.target.value})}),e.jsx("input",{type:"search","aria-label":"按文件路径筛选",placeholder:"文件路径",value:t.query.path,onChange:r=>t.setQuery({path:r.target.value})}),e.jsx("input",{type:"search","aria-label":"按提交哈希查找",placeholder:"提交哈希",value:t.query.sha,onChange:r=>t.setQuery({sha:r.target.value})}),e.jsxs("div",{className:"git-history-dates",children:[e.jsx("input",{type:"search","aria-label":"起始日期",placeholder:"自 (2026-01-01)",value:t.query.since,onChange:r=>t.setQuery({since:r.target.value})}),e.jsx("input",{type:"search","aria-label":"结束日期",placeholder:"至",value:t.query.until,onChange:r=>t.setQuery({until:r.target.value})})]}),e.jsxs("div",{className:"git-history-search-actions",children:[e.jsxs("span",{role:"status",children:[b?`已载 ${b.items.length}${b.truncated?"+":""} 个提交${$>0?` · 含 ${$} 个合并`:""}`:"读取中…",t.refs?.detached?" · detached HEAD":""]}),e.jsx("button",{type:"button",className:`text-button ${B?"diff-toggle-on":""}`,"aria-pressed":B!==null,onClick:()=>{if(B){E(null);return}H(!0),s.gitGraph().then(E).finally(()=>H(!1))},children:"拓扑"}),e.jsx("button",{type:"button",className:"text-button",onClick:t.resetQuery,children:"清除筛选"})]})]}),L?e.jsx("p",{className:"resource-error",role:"alert",children:L}):null,_?e.jsxs("section",{className:"git-file-history","aria-label":"文件历史",children:[e.jsxs("header",{children:[e.jsxs("strong",{children:["文件历史 · ",_.path]}),e.jsx("button",{type:"button",className:"text-button",onClick:t.closeFileHistory,children:"返回提交列表"})]}),e.jsx("p",{className:"git-file-history-note",children:"跟随重命名;复制与复杂合并场景的追踪可能不完整。"}),e.jsx("ul",{children:_.items.map(r=>e.jsx("li",{children:e.jsxs("button",{type:"button",onClick:()=>{t.closeFileHistory(),t.selectCommit(r)},children:[e.jsx("code",{children:r.shortId}),e.jsx("span",{children:r.subject}),e.jsx("time",{dateTime:r.authoredAt,children:fe(r.authoredAt)})]})},r.id))}),_.truncated?e.jsx("p",{className:"git-file-history-note",children:"结果已截断,仅显示最近的提交。"}):null]}):null,d?e.jsxs("section",{className:"git-commit-detail","aria-label":"提交详情",children:[e.jsxs("header",{children:[e.jsxs("div",{children:[e.jsx("code",{children:d.shortId}),d.isMerge?e.jsxs("span",{className:"git-merge-badge",children:["合并提交 · ",d.parents.length," 个父提交"]}):d.parents.length===0?e.jsx("span",{className:"git-root-badge",children:"根提交"}):null]}),e.jsx("button",{type:"button",className:"text-button",onClick:t.closeDetail,children:"关闭详情"})]}),e.jsx("p",{className:"git-commit-subject",children:d.subject}),d.message.trim()!==d.subject.trim()?e.jsx("pre",{className:"git-commit-message",children:d.message.trim()}):null,e.jsxs("p",{className:"git-commit-meta",children:[d.authorName," <",d.authorEmail,"> · ",fe(d.authoredAt),d.committerName&&d.committerName!==d.authorName?` · 提交者 ${d.committerName}`:""]}),e.jsxs("p",{className:"git-commit-base",children:["比较基准:",d.baseLabel," ",e.jsx("code",{children:d.baseOid.slice(0,10)})]}),d.isMerge?e.jsx("div",{className:"git-parent-picker",role:"group","aria-label":"选择父提交",children:d.parents.map((r,I)=>e.jsxs("button",{type:"button","aria-pressed":d.parentIndex===I,className:d.parentIndex===I?"selected":"",onClick:()=>t.setParentIndex(I),children:["父 ",I+1," ",e.jsx("code",{children:r.slice(0,8)})]},r))}):null,S?e.jsxs(e.Fragment,{children:[e.jsxs("p",{className:"git-commit-stats-line",children:[S.files.length," 个文件 ·",e.jsxs("em",{className:"diff-stat-add",children:[" +",S.additions]}),e.jsxs("em",{className:"diff-stat-del",children:[" -",S.deletions]}),S.truncated?" · 已截断":""]}),e.jsx("ul",{className:"git-commit-file-list",children:S.files.map(r=>e.jsxs("li",{children:[e.jsxs("button",{type:"button",className:d&&t.diff?.path===r.path?"selected":"","aria-current":t.diff?.path===r.path?"true":void 0,onClick:()=>t.selectFile(r.path),children:[e.jsx("span",{className:"git-commit-file-stats",children:r.binary?"bin":`+${r.additions} -${r.deletions}`}),e.jsx("span",{title:r.path,children:r.renamedFrom?`${r.renamedFrom} → ${r.path}`:r.path}),r.sensitive?e.jsx("em",{className:"git-sensitive-badge",children:"受保护"}):null]}),e.jsx("button",{type:"button",className:"text-button","aria-label":`查看 ${r.path} 的文件历史`,onClick:()=>t.openFileHistory(r.path),children:"历史"}),e.jsx("button",{type:"button",className:"text-button","aria-label":`查看 ${r.path} 在该提交的版本`,onClick:()=>{d&&s.gitBlob({path:r.path,rev:d.id}).then(I=>R({path:I.path,rev:I.rev,lines:I.content,binary:I.binary,truncated:I.truncated,byteSize:I.byteSize,dataUrl:I.dataUrl})).catch(()=>R(null))},children:"该版本"}),e.jsx("button",{type:"button",className:"text-button","aria-label":`查看 ${r.path} 的 Blame`,onClick:()=>{d&&(P(!0),s.gitBlame({path:r.path,rev:d.id}).then(I=>N({...I,rev:d.id})).catch(()=>N(null)).finally(()=>P(!1)))},children:"Blame"})]},r.path))})]}):e.jsx("p",{className:"resource-loading",children:"正在读取该提交的改动…"})]}):null,u?e.jsx(be,{document:u,mode:n.mode,wrap:n.wrap,showWhitespace:n.showWhitespace,collapsedHunks:i,anchorLineId:o,loading:t.loadingDetail,onChangeMode:r=>h({mode:r}),onToggleWrap:()=>h({wrap:!n.wrap}),onToggleWhitespace:()=>h({showWhitespace:!n.showWhitespace}),onToggleHunk:g,onSetAllFolded:x,onAnchorChange:a,onNotice:p,onSendToComposer:j}):null,q?e.jsx("p",{className:"git-file-history-note",children:"正在读取拓扑…"}):null,B?e.jsxs("section",{className:"git-graph","aria-label":"提交拓扑",children:[e.jsxs("header",{children:[e.jsxs("strong",{children:["拓扑(",B.rows.length,B.truncated?"+":""," 行)"]}),e.jsx("button",{type:"button",className:"text-button",onClick:()=>E(null),children:"关闭"})]}),e.jsx("p",{className:"git-file-history-note",children:"图形列由 git 直接绘制,不由前端重算。"}),e.jsx("div",{className:"git-graph-body",children:B.rows.map((r,I)=>e.jsxs("button",{type:"button",className:r.id&&d?.id===r.id?"selected":"",disabled:!r.id,onClick:()=>{r.id&&(E(null),t.selectCommit({id:r.id}))},children:[e.jsx("code",{children:r.graph}),e.jsx("span",{children:r.id?r.subject:""})]},`${r.id??"graph"}:${I}`))})]}):null,k?e.jsxs("section",{className:"git-blob","aria-label":"该版本内容",children:[e.jsxs("header",{children:[e.jsxs("strong",{children:[k.path," @ ",k.rev.slice(0,10)]}),e.jsx("button",{type:"button",className:"text-button",onClick:()=>R(null),children:"关闭"})]}),k.truncated?e.jsxs("p",{className:"git-file-history-note",children:["文件超过上限(",k.byteSize," 字节),未读取内容。"]}):k.dataUrl?e.jsx("img",{src:k.dataUrl,alt:`${k.path} 在该提交的图像版本`,className:"git-blob-image"}):k.binary?e.jsxs("p",{className:"git-file-history-note",children:["二进制内容(",k.byteSize," 字节),不在此处渲染。"]}):e.jsx("pre",{className:"git-blob-content",children:k.lines.join(`
|
|
4
4
|
`)})]}):null,v?e.jsxs("section",{className:"git-blame","aria-label":"Blame",children:[e.jsxs("header",{children:[e.jsxs("strong",{children:["Blame · ",v.path]}),e.jsx("button",{type:"button",className:"text-button",onClick:()=>N(null),children:"关闭"})]}),Q?e.jsx("p",{className:"git-file-history-note",children:"读取中…"}):null,e.jsx("div",{className:"git-blame-table",children:v.lines.map(r=>e.jsxs("div",{className:"git-blame-row",children:[e.jsx("code",{children:r.commitShort}),e.jsx("span",{className:"git-blame-author",title:r.summary,children:r.author}),e.jsx("time",{children:r.authoredAt.slice(0,10)}),e.jsx("span",{className:"git-blame-summary",title:r.summary,children:r.summary})]},r.lineNumber))}),v.truncated?e.jsx("p",{className:"git-file-history-note",children:"结果已截断。"}):null]}):null,e.jsx("ul",{className:"git-history-list",children:(b?.items??[]).map(r=>e.jsx("li",{children:e.jsxs("button",{type:"button",className:d?.id===r.id?"selected":"","aria-current":d?.id===r.id?"true":void 0,onClick:()=>t.selectCommit(r),children:[e.jsx("code",{children:r.shortId}),e.jsx("span",{title:r.subject,children:r.subject}),r.isMerge?e.jsx("em",{className:"git-merge-badge-inline",children:"合并"}):null,r.decoration.length>0?e.jsx("em",{className:"git-ref-badge",title:r.decoration.join(", "),children:r.decoration[0]}):null,e.jsx("time",{dateTime:r.authoredAt,children:fe(r.authoredAt)})]})},r.id))}),b&&b.items.length===0&&!t.loading?e.jsxs("div",{className:"resource-empty compact",children:[e.jsx(T,{name:"search",size:16}),e.jsx("strong",{children:"没有匹配的提交"}),e.jsx("p",{children:"筛选在 Host 上对整个可达历史执行,不只是当前页。"})]}):null,b?.nextCursor?e.jsx("button",{type:"button",className:"text-button resource-load-more",onClick:t.loadMore,disabled:t.loadingMore,children:t.loadingMore?"加载中…":"加载更多提交"}):null]})}function fe(t){const s=new Date(t);return Number.isNaN(s.getTime())?t:s.toLocaleDateString("zh-CN",{year:"numeric",month:"2-digit",day:"2-digit"})}const Dt={changes:"变更",history:"历史",compare:"比较"},Ie={conflict:"冲突",unstaged:"未暂存",staged:"已暂存",untracked:"未跟踪"},Se=["conflict","unstaged","staged","untracked"];function Et(t,s){return s==="conflict"?t.conflicted:t[s]}function _t({workspaceId:t,refreshEpoch:s,actions:n,onSendToComposer:i,onRevealInFiles:o,navigatorWidthPx:h,onNavigatorWidthCommit:g,store:x}){const a=ct({workspaceId:t,refreshEpoch:s,actions:n,store:x}),{notice:p,showNotice:j,clearNotice:b}=Oe(),{status:d,diff:S,session:u}=a,_=()=>{if(!u.selectedFileId||u.selectedLineIds.length===0)return;const c=u.selectedFileId,M=u.selectedLineIds;a.applyPatch({fileId:c,lineIds:M}),a.setSession({selectedLineIds:[]})},L=l.useRef(null),v=l.useCallback(c=>{if(L.current?.disconnect(),L.current=null,!c)return;const M=new ResizeObserver(G=>{for(const K of G)Q(K.contentRect.width)});M.observe(c),L.current=M,Q(c.getBoundingClientRect().width)},[]),[N,Q]=l.useState(0);l.useEffect(()=>()=>L.current?.disconnect(),[]);const P=Ue(N,a.display.mode),B=P.narrow,E=P.effectiveMode,[q,H]=l.useState(!1),k=l.useMemo(()=>{const c=a.selectedEntry;if(!o||!c||u.view!=="changes")return null;const M=c.fileId;return()=>{n.gitFilesTarget(M).then(G=>o({path:G.path,filesToken:G.filesToken})).catch(()=>j("该文件无法在 Files 中打开。"))}},[n,a.selectedEntry,o,u.view,j]),R=l.useMemo(()=>{if(u.view!=="changes"||!u.selectedFileId||!S)return null;const c=u.selectedFileId;return S.source!=="staged"&&S.source!=="unstaged"&&S.source!=="untracked"?null:{label:S.source==="staged"?"取消暂存此 Hunk":"暂存此 Hunk",run:G=>{a.applyPatch({fileId:c,hunkIds:[G]})}}},[S,a,u.selectedFileId,u.view]),$=Xe({enabled:(u.view==="history"||u.view==="compare")&&!!t,session:u,setSession:a.setSession,actions:n,refreshEpoch:s}),r=l.useMemo(()=>d?Se.map(c=>({source:c,label:Ie[c],count:Et(d.counts,c),items:ot(d,c)})):[],[d]),I=l.useMemo(()=>u.group?r.filter(c=>c.source===u.group):r,[r,u.group]),f=l.useMemo(()=>I.flatMap(c=>c.items.map(M=>M.fileId)),[I]),V=l.useMemo(()=>S?ve(S.hunks).map(c=>c.lineId):[],[S]);return Ze({enabled:u.view==="changes",session:u,setSession:a.setSession,fileIds:f,anchorLineIds:V,onOpenSelection:()=>{const c=a.selectedEntry;c&&a.openFile(c)},onEscapeFallback:()=>{}}),a.error&&!d?e.jsxs("div",{className:"resource-empty",role:"alert",children:[e.jsx(T,{name:"warning"}),e.jsx("strong",{children:"Git 状态读取失败"}),e.jsx("p",{children:a.error}),e.jsx("button",{type:"button",className:"secondary-button",onClick:a.reload,children:"重试"})]}):a.loading&&!d?e.jsx("p",{className:"resource-loading",children:"正在读取 Git 状态…"}):d&&d.repositoryKind==="bare"?e.jsxs("div",{className:"resource-empty",children:[e.jsx(T,{name:"branch",size:16}),e.jsx("strong",{children:"当前项目是裸仓库"}),e.jsx("p",{children:"裸仓库没有工作区,工作栏没有可显示的变更或 Diff。文件和 Agent 不受影响。"})]}):d&&!d.isRepository?e.jsxs("div",{className:"resource-empty",children:[e.jsx(T,{name:"branch",size:16}),e.jsx("strong",{children:"当前项目不是 Git 仓库"}),e.jsx("p",{children:"文件和 Agent 不受影响。"})]}):e.jsxs("div",{ref:v,"data-width":P.dataWidth,"data-narrow-view":B&&q&&u.selectedFileId?"detail":"list",className:"work-resource-panel git-panel",children:[e.jsxs("div",{className:"git-summary",children:[e.jsxs("div",{children:[e.jsx(T,{name:"branch",size:16}),e.jsx("strong",{children:d?.detached?`detached ${d.head??""}`:d?.branch||"HEAD"})]}),e.jsxs("span",{children:[d?.upstream||"无 upstream",d&&(d.ahead||d.behind)?` · ↑${d.ahead} ↓${d.behind}`:""]}),e.jsx("button",{type:"button",className:"icon-button","aria-label":"刷新 Git 状态","aria-busy":a.refreshing,onClick:()=>{b(),a.reload()},disabled:a.refreshing,children:e.jsx(T,{name:"refresh",size:16})})]}),e.jsxs("div",{className:"git-views",role:"tablist","aria-label":"Git 视图",children:[_e.map(c=>e.jsxs("button",{type:"button",role:"tab","aria-selected":u.view===c,className:u.view===c?"selected":"",onClick:()=>a.setSession({view:c}),children:[Dt[c],c==="changes"&&d&&d.counts.total>0?e.jsx("span",{className:"git-view-count",children:d.counts.total}):null]},c)),a.refreshing?e.jsx("span",{className:"git-refreshing",children:"更新中…"}):null]}),p?e.jsx("p",{className:"resource-notice",role:"status",children:p.text}):null,u.notice?e.jsx("p",{className:"resource-notice",role:"status",children:u.notice}):null,a.error?e.jsx("p",{className:"resource-error",role:"alert",children:a.error}):null,e.jsxs(Me,{panelId:"git",navigatorWidthPx:h,onNavigatorWidthCommit:g,contentLabel:"Git 内容",navigatorLabel:"Git 导航",handleLabel:"调整 Git 导航宽度",contentClassName:"git-content",navigatorClassName:"git-changes",children:[e.jsxs(e.Fragment,{children:[B&&u.selectedFileId?e.jsx("button",{type:"button",className:"git-back-to-list",onClick:()=>H(!1),children:"← 返回文件列表"}):null,u.view==="changes"?e.jsx($t,{groups:I,activeGroup:u.group,query:u.query,total:d?.counts.total??0,clean:d?.clean??!1,busy:a.busy,selectedFileIds:u.selectedFileIds,onQueryChange:c=>a.setSession({query:c,selectedFileId:null}),onGroupChange:c=>a.setSession({group:c,selectedFileId:null}),selectedFileId:u.selectedFileId,onSelect:c=>{a.setSession({selectedLineIds:[]}),H(!0),a.openFile(c)},onToggleSelection:a.toggleSelection,onClearSelection:a.clearSelection,onStage:c=>{a.stagePaths(c)},onUnstage:c=>{a.unstagePaths(c)}}):null,u.view==="history"?e.jsx("div",{className:"git-history-nav",children:e.jsx("p",{className:"git-history-nav-note",children:"提交列表与详情显示在右侧。"})}):null,u.view==="compare"?e.jsx(It,{refs:$.refs,actions:n,display:a.display,collapsedHunks:u.collapsedHunks,anchorLineId:u.anchorLineId,onDisplayChange:c=>a.setDisplay(c),onToggleHunk:c=>a.setSession({collapsedHunks:u.collapsedHunks.includes(c)?u.collapsedHunks.filter(M=>M!==c):[...u.collapsedHunks,c]}),onSetAllFolded:c=>a.setSession({collapsedHunks:c?u.collapsedHunks:[]}),onAnchorChange:c=>a.setSession({anchorLineId:c}),onNotice:j,onSendToComposer:i}):null]}),e.jsxs(e.Fragment,{children:[u.view==="changes"?a.selectedEntry?.source==="conflict"?e.jsx(Ft,{path:a.selectedEntry.path,actions:n}):S?e.jsxs("div",{children:[k?e.jsx("div",{className:"git-reveal-row",children:e.jsx("button",{type:"button",className:"text-button",onClick:k,children:"在 Files 打开"})}):null,u.selectedLineIds.length>0?e.jsxs("div",{className:"git-line-actions",role:"group","aria-label":"选中的行",children:[e.jsxs("span",{children:["已选 ",u.selectedLineIds.length," 行"]}),e.jsx("button",{type:"button",className:"secondary-button",disabled:a.busy,onClick:_,children:"暂存选中行"}),e.jsx("button",{type:"button",className:"text-button",onClick:()=>a.setSession({selectedLineIds:[]}),children:"清除"})]}):null,e.jsx(be,{hunkAction:R,ignoreWhitespace:a.display.ignoreWhitespace,onToggleIgnoreWhitespace:()=>a.setDisplay({ignoreWhitespace:!a.display.ignoreWhitespace}),wordDiff:a.display.wordDiff,onToggleWordDiff:()=>a.setDisplay({wordDiff:!a.display.wordDiff}),selectedLineIds:u.selectedLineIds,onToggleLine:c=>a.setSession({selectedLineIds:u.selectedLineIds.includes(c)?u.selectedLineIds.filter(M=>M!==c):[...u.selectedLineIds,c]}),document:S,mode:E,wrap:a.display.wrap,showWhitespace:a.display.showWhitespace,collapsedHunks:u.collapsedHunks,anchorLineId:u.anchorLineId,loading:a.loading,stale:a.stale,onChangeMode:c=>a.setDisplay({mode:c}),onToggleWrap:()=>a.setDisplay({wrap:!a.display.wrap}),onToggleWhitespace:()=>a.setDisplay({showWhitespace:!a.display.showWhitespace}),onToggleHunk:c=>a.setSession({collapsedHunks:u.collapsedHunks.includes(c)?u.collapsedHunks.filter(M=>M!==c):[...u.collapsedHunks,c]}),onSetAllFolded:c=>a.setSession({collapsedHunks:c?S.hunks.map(M=>M.hunkId):[]}),onAnchorChange:c=>a.setSession({anchorLineId:c}),onNotice:j,onLoadMore:S.nextCursor?a.loadMoreDiff:void 0,onSendToComposer:i})]}):e.jsxs("div",{className:"resource-empty",children:[e.jsx(T,{name:"code",size:16}),e.jsx("strong",{children:"选择变更查看 Diff"}),e.jsx("p",{children:"同一文件的「未暂存」和「已暂存」是两个不同的比较目标。"})]}):null,u.view==="changes"?e.jsx(Rt,{preview:a.commitPreview,draft:u.commitDraft,busy:a.busy,onDraftChange:c=>a.setSession({commitDraft:c}),onCommit:c=>a.commit(c)}):null,u.view==="history"?e.jsx(Lt,{history:$,actions:n,display:a.display,collapsedHunks:u.collapsedHunks,anchorLineId:u.anchorLineId,onDisplayChange:c=>a.setDisplay(c),onToggleHunk:c=>a.setSession({collapsedHunks:u.collapsedHunks.includes(c)?u.collapsedHunks.filter(M=>M!==c):[...u.collapsedHunks,c]}),onSetAllFolded:c=>a.setSession({collapsedHunks:c?u.collapsedHunks:[]}),onAnchorChange:c=>a.setSession({anchorLineId:c}),onNotice:j,onSendToComposer:i}):null,u.view==="compare"?e.jsx(Pt,{title:"比较结果",stage:"S5"}):null]})]})]})}function Rt({preview:t,draft:s,busy:n,onDraftChange:i,onCommit:o}){const[h,g]=l.useState(!1),[x,a]=l.useState(""),p=t?.blockedReason??"正在读取暂存区…",j=!!t?.canCommit&&s.summary.trim().length>0&&!n,b=()=>{a(""),o({summary:s.summary,...s.body.trim()?{body:s.body}:{}}).catch(d=>a(d instanceof Error?d.message:"提交失败。"))};return e.jsxs("section",{className:"git-commit","aria-label":"提交",children:[e.jsxs("button",{type:"button",className:"git-commit-toggle","aria-expanded":h,onClick:()=>g(d=>!d),children:[h?"▾":"▸"," 提交 ",t?`${t.filesChanged} 个文件`:"",t&&(t.additions>0||t.deletions>0)?e.jsxs("span",{className:"git-commit-stats",children:[e.jsxs("em",{className:"diff-stat-add",children:["+",t.additions]})," ",e.jsxs("em",{className:"diff-stat-del",children:["-",t.deletions]})]}):null]}),h?e.jsxs("div",{className:"git-commit-body",children:[t?e.jsxs(e.Fragment,{children:[e.jsxs("p",{className:"git-commit-meta",children:[t.detached?"detached HEAD":t.branch||"HEAD"," ·"," ",t.identity.name??"未配置 user.name"," <",t.identity.email??"未配置 user.email",">",t.identity.signingEnabled?" · 已启用签名":" · 未启用签名",` · hooks ${t.identity.hooksPresent?"存在":"不存在"}`]}),e.jsxs("ul",{className:"git-commit-files",children:[t.entries.slice(0,40).map(d=>e.jsxs("li",{children:[e.jsx("code",{children:d.status}),e.jsx("span",{children:d.path})]},`${d.source}:${d.path}`)),t.entries.length>40?e.jsxs("li",{className:"git-commit-more",children:["…还有 ",t.entries.length-40," 个文件"]}):null]})]}):null,e.jsxs("label",{children:[e.jsx("span",{children:"摘要"}),e.jsx("input",{type:"text",value:s.summary,maxLength:2e3,disabled:n,placeholder:"本次提交做了什么",onChange:d=>i({...s,summary:d.target.value})})]}),e.jsxs("label",{children:[e.jsx("span",{children:"正文(可选)"}),e.jsx("textarea",{value:s.body,rows:3,disabled:n,placeholder:"为什么这样做",onChange:d=>i({...s,body:d.target.value})})]}),x?e.jsx("p",{className:"resource-error",role:"alert",children:x}):null,e.jsxs("div",{className:"git-commit-actions",children:[e.jsx("button",{type:"button",className:"primary-button",disabled:!j,"aria-busy":n,onClick:b,children:n?"提交中…":"提交到本地索引"}),t?.canCommit?null:e.jsx("span",{className:"git-commit-blocked",children:p})]}),e.jsx("p",{className:"git-commit-note",children:"只提交本地 index:不自动暂存、不 amend、不 push。"})]}):null]})}function $t({groups:t,activeGroup:s,query:n,total:i,clean:o,busy:h,selectedFileIds:g,onQueryChange:x,onGroupChange:a,selectedFileId:p,onSelect:j,onToggleSelection:b,onClearSelection:d,onStage:S,onUnstage:u}){const _=new Set(g),L=g.length>0;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"git-filter",children:[e.jsx("input",{type:"search",value:n,"aria-label":"搜索文件路径",placeholder:"搜索文件…",onChange:v=>x(v.target.value)}),e.jsxs("div",{className:"git-filter-groups",role:"group","aria-label":"按分组筛选",children:[e.jsx("button",{type:"button","aria-pressed":s===null,onClick:()=>a(null),children:"全部"}),Se.map(v=>e.jsx("button",{type:"button","aria-pressed":s===v,onClick:()=>a(s===v?null:v),children:Ie[v]},v))]}),e.jsx("p",{className:"git-filter-summary",role:"status",children:i===0?n?"没有匹配的改动":"没有改动":`匹配 ${i} 个改动`}),L?e.jsxs("div",{className:"git-bulk",role:"group","aria-label":"对选中文件操作",children:[e.jsxs("span",{children:["已选 ",g.length," 个文件"]}),e.jsx("button",{type:"button",className:"secondary-button",disabled:h,onClick:()=>S(g),children:"暂存选中"}),e.jsx("button",{type:"button",className:"secondary-button",disabled:h,onClick:()=>u(g),children:"取消暂存选中"}),e.jsx("button",{type:"button",className:"text-button",onClick:d,children:"清除"})]}):null]}),o?e.jsxs("div",{className:"resource-empty compact",children:[e.jsx(T,{name:"check"}),e.jsx("strong",{children:"工作区干净"})]}):t.map(v=>v.count?e.jsxs("div",{className:"git-group",children:[e.jsxs("h3",{children:[v.label," ",e.jsx("span",{children:v.count}),v.items.length<v.count?e.jsxs("small",{className:"git-group-loaded",children:["已载 ",v.items.length]}):null,v.source==="unstaged"||v.source==="untracked"?e.jsx("button",{type:"button",className:"text-button git-group-action",disabled:h,onClick:()=>S(v.items.map(N=>N.fileId)),children:"暂存该组已载"}):null,v.source==="staged"?e.jsx("button",{type:"button",className:"text-button git-group-action",disabled:h,onClick:()=>u(v.items.map(N=>N.fileId)),children:"取消暂存该组已载"}):null]}),v.items.map(N=>e.jsxs("div",{className:`git-row ${p===N.fileId?"selected":""}`,children:[e.jsx("input",{type:"checkbox","aria-label":`选择 ${N.path}`,checked:_.has(N.fileId),onChange:()=>b(N.fileId)}),e.jsxs("button",{type:"button",className:p===N.fileId?"selected":"","aria-current":p===N.fileId?"true":void 0,onClick:()=>j(N),children:[e.jsx("span",{className:`git-status-code ${v.source}`,children:N.indexStatus.trim()||N.worktreeStatus.trim()||"?"}),e.jsx("span",{title:N.path,children:N.renamedFrom?`${N.renamedFrom} → ${N.path}`:N.path})]}),v.source==="unstaged"||v.source==="untracked"?e.jsx("button",{type:"button",className:"text-button",disabled:h,"aria-label":`暂存 ${N.path}`,onClick:()=>S([N.fileId]),children:"暂存"}):null,v.source==="staged"?e.jsx("button",{type:"button",className:"text-button",disabled:h,"aria-label":`取消暂存 ${N.path}`,onClick:()=>u([N.fileId]),children:"取消"}):null,v.source==="conflict"?e.jsx("span",{className:"git-conflict-hint",children:"需在 Files 处理"}):null]},N.fileId))]},v.source):null)]})}function Pt({title:t,stage:s}){return e.jsxs("div",{className:"resource-empty",children:[e.jsx(T,{name:"info",size:16}),e.jsx("strong",{children:t}),e.jsxs("p",{children:["计划阶段 ",s," 交付,当前版本尚未实现。"]})]})}export{_t as GitPanel};
|