@orion-agents/orion-code 0.3.18 → 0.3.19
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 +55 -3
- package/README.md +7 -6
- package/README.zh-CN.md +5 -4
- package/dist/runtime/release-receipts.d.ts +9 -0
- package/dist/runtime/release-receipts.js +17 -1
- package/dist/web/git-file-source.js +1 -1
- package/dist/web/git-history-service.js +19 -13
- package/dist/web/git-read-model-service.d.ts +24 -0
- package/dist/web/git-read-model-service.js +72 -2
- package/dist/web/protocol.d.ts +3 -3
- package/dist/web/server.js +10 -1
- package/dist/web-client/assets/{FilesPanel-BARQCmne.js → FilesPanel-DGWgLv6R.js} +1 -1
- package/dist/web-client/assets/GitPanel-DIuJNXoB.js +4 -0
- package/dist/web-client/assets/{ReviewPanel-YhEGlp-G.js → ReviewPanel-YWZXJdm5.js} +1 -1
- package/dist/web-client/assets/{TerminalPanel-DnWh3Vp2.js → TerminalPanel-DPSMepio.js} +1 -1
- package/dist/web-client/assets/{index-Drja0_BT.js → index-DYeu_rs7.js} +2 -2
- package/dist/web-client/assets/{useAutoNotice-DfQi4G1X.js → useAutoNotice-h7c0Iz1E.js} +1 -1
- package/dist/web-client/index.html +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/web-client/assets/GitPanel-DjB9eSiJ.js +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -22,11 +22,63 @@ which is **not** a pass.
|
|
|
22
22
|
|
|
23
23
|
## [Unreleased]
|
|
24
24
|
|
|
25
|
+
## [0.3.19] — 2026-09-19
|
|
26
|
+
|
|
27
|
+
> **Status: published.** Released as `@orion-agents/orion-code@0.3.19` from
|
|
28
|
+
> `refs/tags/v0.3.19`, and merged to `main` as `d17d211` (#259). Closes the five scenarios
|
|
29
|
+
> the v0.3.17 status table left as "feature present, assertion missing"
|
|
30
|
+
> (`docs/plan/evidence/v0.3.17-git/g317-status.md`), and fixes the three defects those
|
|
31
|
+
> assertions found.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- **The responsive tiers were inert.** `GitPanel` attached its `ResizeObserver` from an
|
|
36
|
+
effect with an empty dependency list. The panel's first render is the loading placeholder,
|
|
37
|
+
which does not contain the measured node, so the effect read a null ref, returned, and never
|
|
38
|
+
ran again: the width stayed `0`, `data-width` stayed `wide` on a 480px panel, and the narrow
|
|
39
|
+
affordances never activated on any load whose status was not already cached. The observer is
|
|
40
|
+
now attached with a callback ref, so it follows the node in and out of the tree.
|
|
41
|
+
- **A discarded diff read left the pane blank with a file still selected.** Only a newer
|
|
42
|
+
*selection* supersedes a read now. The document states the revision it was rendered against,
|
|
43
|
+
so accepting it and letting `stale` tell the truth is both safer and correct — dropping it
|
|
44
|
+
because the 2s visible poll had bumped the revision abandoned the read with nothing to
|
|
45
|
+
re-issue it.
|
|
46
|
+
- **Returning from the narrow detail pane threw away the reading position.** The back control
|
|
47
|
+
cleared `selectedFileId` to show the list, which also dropped the anchors and the cached
|
|
48
|
+
document. The pane choice is now its own state.
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- **A bare repository is no longer reported as "not a repository".** `rev-parse
|
|
53
|
+
--show-toplevel` fails for a bare repository too, and that single failure was read as "no
|
|
54
|
+
repository here". Status now carries `repositoryKind: 'worktree' | 'bare' | 'absent'` and
|
|
55
|
+
`hasWorktree`; every worktree-backed surface gates on `hasWorktree`, which is the only flag
|
|
56
|
+
that cannot be wrong. A bare repository's branch is reported instead of hidden, its change
|
|
57
|
+
lists stay empty, and the history limit names the real reason.
|
|
58
|
+
- **G317 assertion backlog:** an external write no longer moves the reader (the refresh is
|
|
59
|
+
detected and the document is marked stale instead of silently kept); `在 Files 打开` is
|
|
60
|
+
asserted end to end; the theme is measured rather than assumed (system preference, the
|
|
61
|
+
explicit choice through Settings, reduced motion and forced colours); the narrow tier's back
|
|
62
|
+
affordance and overflow are asserted; and repeated open/close plus a hundred consecutive
|
|
63
|
+
diffs are asserted bounded, with S5/S6 samples recorded by
|
|
64
|
+
`scripts/perf/v0319-git-s6-samples.mjs`.
|
|
65
|
+
|
|
66
|
+
### Known limits
|
|
67
|
+
|
|
68
|
+
- The `compact` and `wide` panel tiers are pinned by `resolveGitPanelLayout` unit tests, not in
|
|
69
|
+
a browser: neither seeding `workPanel.widthPx` nor dragging the splitter reproduced a tier
|
|
70
|
+
change in the E2E harness. `docs/plan/evidence/v0.3.17-git/s6-responsive-keyboard.md` records
|
|
71
|
+
the measurement.
|
|
72
|
+
- The drawer entry point below 1180px is not asserted; those widths assert overflow only.
|
|
73
|
+
|
|
25
74
|
## [0.3.18]
|
|
26
75
|
|
|
27
|
-
> **Status:
|
|
28
|
-
>
|
|
29
|
-
>
|
|
76
|
+
> **Status: npm-published, not tagged or merged.** The exact contents below are
|
|
77
|
+
> published to the registry as 0.3.18, but `refs/tags/v0.3.18` does not exist and
|
|
78
|
+
> `main` does not carry this work yet, so the release is not complete.
|
|
79
|
+
> Closes the assertion and evidence backlog of the Git right-hand work panel
|
|
80
|
+
> shipped in 0.3.17, and fixes the CRLF part-staging defect found right after
|
|
81
|
+
> that release.
|
|
30
82
|
|
|
31
83
|
### Fixed
|
|
32
84
|
|
package/README.md
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Local-first, goal-driven coding agent for the terminal and browser.
|
|
4
4
|
|
|
5
|
-
> v0.3.
|
|
6
|
-
>
|
|
7
|
-
>
|
|
8
|
-
>
|
|
9
|
-
>
|
|
5
|
+
> v0.3.19 — closes the v0.3.17 Git work panel's assertion backlog
|
|
6
|
+
> (`docs/plan/evidence/v0.3.17-git/g317-status.md`) and fixes what those
|
|
7
|
+
> assertions found: responsive tiers that never activated, a diff read discarded
|
|
8
|
+
> on a background refresh that left the pane blank, a narrow back control that
|
|
9
|
+
> dropped the reading position, and a bare repository reported as "not a
|
|
10
|
+
> repository". Published to npm as 0.3.19 and tagged `v0.3.19`.
|
|
10
11
|
|
|
11
12
|
[中文说明](README.zh-CN.md) ·
|
|
12
13
|
[v0.3.7 plan](docs/plan/v0.3.7-left-rail-improvement-plan.md) ·
|
|
@@ -171,7 +172,7 @@ current development environments. Node 20 is upstream EOL and is no longer a v0.
|
|
|
171
172
|
After the immutable `0.3.5` npm receipt exists:
|
|
172
173
|
|
|
173
174
|
```bash
|
|
174
|
-
npm install -g @orion-agents/orion-code@0.3.
|
|
175
|
+
npm install -g @orion-agents/orion-code@0.3.19
|
|
175
176
|
orion --version
|
|
176
177
|
orion doctor
|
|
177
178
|
```
|
package/README.zh-CN.md
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
面向终端与浏览器、本地优先的目标驱动 Coding Agent。
|
|
4
4
|
|
|
5
|
-
> v0.3.
|
|
6
|
-
>
|
|
7
|
-
>
|
|
5
|
+
> v0.3.19:补齐 v0.3.17 Git 工作栏的断言欠账
|
|
6
|
+
> (`docs/plan/evidence/v0.3.17-git/g317-status.md`),并修掉这些断言找出来的问题——
|
|
7
|
+
> 响应式三档从未生效、被后台刷新丢弃的 Diff 读取导致内容区空白、窄屏返回丢失阅读位置、
|
|
8
|
+
> 裸仓库被当成「不是仓库」。已发布 npm 0.3.19 并打 tag `v0.3.19`。
|
|
8
9
|
|
|
9
10
|
[English](README.md) ·
|
|
10
11
|
[v0.3.7 方案](docs/plan/v0.3.7-left-rail-improvement-plan.md) ·
|
|
@@ -101,7 +102,7 @@ Current。Node 20 已结束上游维护,不再属于 v0.3 Runtime 合同。
|
|
|
101
102
|
当 npm 已存在不可变的 `0.3.4` 发布凭据后:
|
|
102
103
|
|
|
103
104
|
```bash
|
|
104
|
-
npm install -g @orion-agents/orion-code@0.3.
|
|
105
|
+
npm install -g @orion-agents/orion-code@0.3.19
|
|
105
106
|
orion --version
|
|
106
107
|
orion doctor
|
|
107
108
|
```
|
|
@@ -21,6 +21,15 @@ export declare const WEB_E2E_WEB38_SCENARIOS_V1: readonly ["WEB38-P0-01", "WEB38
|
|
|
21
21
|
* actually verify; the per-stage unit suites are registered separately.
|
|
22
22
|
*/
|
|
23
23
|
export declare const WEB_E2E_WEB317_SCENARIOS_V1: readonly string[];
|
|
24
|
+
/**
|
|
25
|
+
* v0.3.19 — closing the v0.3.17 G317 assertion backlog.
|
|
26
|
+
*
|
|
27
|
+
* Each id is one of the five scenarios that were left as "feature present, assertion
|
|
28
|
+
* missing": an external (agent) write must not move the reader, `在 Files 打开` must
|
|
29
|
+
* actually reach the Files panel, the theme and narrow tiers must be verified rather than
|
|
30
|
+
* assumed, and repeated open/close plus a hundred diffs must stay bounded.
|
|
31
|
+
*/
|
|
32
|
+
export declare const WEB_E2E_WEB34_SCENARIOS_V1: readonly string[];
|
|
24
33
|
export declare const WEB_E2E_FULL_SCENARIOS_V1: readonly string[];
|
|
25
34
|
export declare const WEB_E2E_SETTINGS_SCENARIOS_V1: readonly string[];
|
|
26
35
|
export declare const WEB_E2E_WEB31_CRITICAL_SCENARIOS_V1: readonly ["WEB31-P0-01", "WEB31-P0-02", "WEB31-P0-08", "WEB31-P0-09", "WEB31-P0-10", "WEB31-P0-12"];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WEB_E2E_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_WEB33_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_WEB32_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_WEB31_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_SETTINGS_SCENARIOS_V1 = exports.WEB_E2E_FULL_SCENARIOS_V1 = exports.WEB_E2E_WEB317_SCENARIOS_V1 = exports.WEB_E2E_WEB38_SCENARIOS_V1 = exports.WEB_E2E_WEB33_SCENARIOS_V1 = exports.WEB_E2E_WEB33_SESSION_SCENARIOS_V1 = exports.WEB_E2E_WEB33_THEME_SCENARIOS_V1 = exports.WEB_E2E_WEB32_SCENARIOS_V1 = exports.WEB_E2E_WEB31_SCENARIOS_V1 = exports.WEB_E2E_LEGACY_SCENARIOS_V1 = exports.SUPPORTED_RELEASE_VERSION_LINE_V1 = exports.SUPPORTED_RELEASE_NODE_FLOORS_V1 = exports.SUPPORTED_RELEASE_NODE_MAJORS_V1 = exports.RELEASE_RECEIPT_VERSION_V1 = void 0;
|
|
3
|
+
exports.WEB_E2E_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_WEB33_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_WEB32_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_WEB31_CRITICAL_SCENARIOS_V1 = exports.WEB_E2E_SETTINGS_SCENARIOS_V1 = exports.WEB_E2E_FULL_SCENARIOS_V1 = exports.WEB_E2E_WEB34_SCENARIOS_V1 = exports.WEB_E2E_WEB317_SCENARIOS_V1 = exports.WEB_E2E_WEB38_SCENARIOS_V1 = exports.WEB_E2E_WEB33_SCENARIOS_V1 = exports.WEB_E2E_WEB33_SESSION_SCENARIOS_V1 = exports.WEB_E2E_WEB33_THEME_SCENARIOS_V1 = exports.WEB_E2E_WEB32_SCENARIOS_V1 = exports.WEB_E2E_WEB31_SCENARIOS_V1 = exports.WEB_E2E_LEGACY_SCENARIOS_V1 = exports.SUPPORTED_RELEASE_VERSION_LINE_V1 = exports.SUPPORTED_RELEASE_NODE_FLOORS_V1 = exports.SUPPORTED_RELEASE_NODE_MAJORS_V1 = exports.RELEASE_RECEIPT_VERSION_V1 = void 0;
|
|
4
4
|
exports.createTarballArtifactReceiptV1 = createTarballArtifactReceiptV1;
|
|
5
5
|
exports.verifyTarballArtifactReceiptV1 = verifyTarballArtifactReceiptV1;
|
|
6
6
|
exports.createRuntimeMatrixReceiptV1 = createRuntimeMatrixReceiptV1;
|
|
@@ -125,6 +125,21 @@ exports.WEB_E2E_WEB317_SCENARIOS_V1 = Object.freeze([
|
|
|
125
125
|
'WEB39-P0-04',
|
|
126
126
|
'WEB39-P0-05',
|
|
127
127
|
]);
|
|
128
|
+
/**
|
|
129
|
+
* v0.3.19 — closing the v0.3.17 G317 assertion backlog.
|
|
130
|
+
*
|
|
131
|
+
* Each id is one of the five scenarios that were left as "feature present, assertion
|
|
132
|
+
* missing": an external (agent) write must not move the reader, `在 Files 打开` must
|
|
133
|
+
* actually reach the Files panel, the theme and narrow tiers must be verified rather than
|
|
134
|
+
* assumed, and repeated open/close plus a hundred diffs must stay bounded.
|
|
135
|
+
*/
|
|
136
|
+
exports.WEB_E2E_WEB34_SCENARIOS_V1 = Object.freeze([
|
|
137
|
+
'WEB34-P0-01',
|
|
138
|
+
'WEB34-P0-02',
|
|
139
|
+
'WEB34-P0-03',
|
|
140
|
+
'WEB34-P0-04',
|
|
141
|
+
'WEB34-P0-05',
|
|
142
|
+
]);
|
|
128
143
|
exports.WEB_E2E_FULL_SCENARIOS_V1 = Object.freeze([
|
|
129
144
|
...exports.WEB_E2E_LEGACY_SCENARIOS_V1,
|
|
130
145
|
...exports.WEB_E2E_WEB31_SCENARIOS_V1,
|
|
@@ -132,6 +147,7 @@ exports.WEB_E2E_FULL_SCENARIOS_V1 = Object.freeze([
|
|
|
132
147
|
...exports.WEB_E2E_WEB33_SCENARIOS_V1,
|
|
133
148
|
...exports.WEB_E2E_WEB38_SCENARIOS_V1,
|
|
134
149
|
...exports.WEB_E2E_WEB317_SCENARIOS_V1,
|
|
150
|
+
...exports.WEB_E2E_WEB34_SCENARIOS_V1,
|
|
135
151
|
]);
|
|
136
152
|
exports.WEB_E2E_SETTINGS_SCENARIOS_V1 = Object.freeze(exports.WEB_E2E_FULL_SCENARIOS_V1.filter(id => id.startsWith('SET-P0-')));
|
|
137
153
|
exports.WEB_E2E_WEB31_CRITICAL_SCENARIOS_V1 = Object.freeze([
|
|
@@ -16,6 +16,6 @@ exports.GIT_FILE_SOURCES_V1 = Object.freeze([
|
|
|
16
16
|
'commit',
|
|
17
17
|
]);
|
|
18
18
|
function isGitFileSourceV1(value) {
|
|
19
|
-
return
|
|
19
|
+
return typeof value === 'string' && exports.GIT_FILE_SOURCES_V1.includes(value);
|
|
20
20
|
}
|
|
21
21
|
//# sourceMappingURL=git-file-source.js.map
|
|
@@ -191,9 +191,7 @@ function createGitHistoryReader(options) {
|
|
|
191
191
|
const chosen = parents[Math.min(parentIndex, parents.length - 1)];
|
|
192
192
|
return {
|
|
193
193
|
baseOid: chosen,
|
|
194
|
-
baseLabel: parents.length > 1
|
|
195
|
-
? `父提交 ${parentIndex + 1}/${parents.length}`
|
|
196
|
-
: '父提交',
|
|
194
|
+
baseLabel: parents.length > 1 ? `父提交 ${parentIndex + 1}/${parents.length}` : '父提交',
|
|
197
195
|
};
|
|
198
196
|
};
|
|
199
197
|
return {
|
|
@@ -276,7 +274,12 @@ function createGitHistoryReader(options) {
|
|
|
276
274
|
// The body is a separate call: a commit message may contain any byte, including the
|
|
277
275
|
// separators the list format relies on.
|
|
278
276
|
const message = await runGit(['log', '-1', '--format=%B', summary.id]);
|
|
279
|
-
const committerRaw = (await runGit([
|
|
277
|
+
const committerRaw = (await runGit([
|
|
278
|
+
'log',
|
|
279
|
+
'-1',
|
|
280
|
+
`--format=%cn${FIELD_SEPARATOR}%ce${FIELD_SEPARATOR}%cI`,
|
|
281
|
+
summary.id,
|
|
282
|
+
])).trim();
|
|
280
283
|
const [committerName, authorEmail, committedAt] = committerRaw.split(FIELD_SEPARATOR);
|
|
281
284
|
const base = await resolveBase(summary.id, parentIndex);
|
|
282
285
|
return Object.freeze({
|
|
@@ -475,17 +478,16 @@ function createGitHistoryReader(options) {
|
|
|
475
478
|
.toString()
|
|
476
479
|
.trim();
|
|
477
480
|
if (!tracked) {
|
|
478
|
-
return Object.freeze({
|
|
481
|
+
return Object.freeze({
|
|
482
|
+
path,
|
|
483
|
+
rev: rev ?? null,
|
|
484
|
+
lines: Object.freeze([]),
|
|
485
|
+
truncated: false,
|
|
486
|
+
});
|
|
479
487
|
}
|
|
480
488
|
// `--porcelain` is machine-readable and stable; the date stays ISO so the client never
|
|
481
489
|
// re-implements time. Uncommitted lines show as boundary/not-committed metadata.
|
|
482
|
-
const raw = await runGit([
|
|
483
|
-
'blame',
|
|
484
|
-
'--porcelain',
|
|
485
|
-
...(target ? [target] : []),
|
|
486
|
-
'--',
|
|
487
|
-
path,
|
|
488
|
-
]);
|
|
490
|
+
const raw = await runGit(['blame', '--porcelain', ...(target ? [target] : []), '--', path]);
|
|
489
491
|
const commits = new Map();
|
|
490
492
|
const lines = [];
|
|
491
493
|
let currentOid = null;
|
|
@@ -541,7 +543,11 @@ function createGitHistoryReader(options) {
|
|
|
541
543
|
else if (key === 'boundary')
|
|
542
544
|
boundary = true;
|
|
543
545
|
if (pendingAuthor && pendingTime && pendingSummary && !commits.has(currentOid)) {
|
|
544
|
-
commits.set(currentOid, Object.freeze({
|
|
546
|
+
commits.set(currentOid, Object.freeze({
|
|
547
|
+
author: pendingAuthor,
|
|
548
|
+
authoredAt: pendingTime,
|
|
549
|
+
summary: pendingSummary,
|
|
550
|
+
}));
|
|
545
551
|
}
|
|
546
552
|
}
|
|
547
553
|
return Object.freeze({
|
|
@@ -28,8 +28,32 @@ export interface WebGitStatusCountsV1 {
|
|
|
28
28
|
readonly untracked: number;
|
|
29
29
|
readonly loaded: number;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* v0.3.19 (G317-22) — what this workspace actually is, stated instead of inferred.
|
|
33
|
+
*
|
|
34
|
+
* `git rev-parse --show-toplevel` fails for a bare repository, and that used to be reported
|
|
35
|
+
* as "not a repository at all". Those are two different facts and the reader is owed the
|
|
36
|
+
* right one:
|
|
37
|
+
*
|
|
38
|
+
* - `worktree` — an ordinary repository with a working tree; the panel reads changes.
|
|
39
|
+
* - `bare` — a repository with **no** working tree. It is a repository, its history
|
|
40
|
+
* exists, but this panel has no changes and no diff to read, and it must
|
|
41
|
+
* never invent any.
|
|
42
|
+
* - `absent` — no repository here at all.
|
|
43
|
+
*
|
|
44
|
+
* `isRepository` stays the coarse question ("is this a repository?"). Anything that needs
|
|
45
|
+
* a working tree must ask `hasWorktree`.
|
|
46
|
+
*/
|
|
47
|
+
export type GitRepositoryKindV1 = 'worktree' | 'bare' | 'absent';
|
|
31
48
|
export interface WebGitStatusV1 {
|
|
32
49
|
readonly isRepository: boolean;
|
|
50
|
+
/** v0.3.19 (G317-22) — the precise answer to "what is this workspace". */
|
|
51
|
+
readonly repositoryKind: GitRepositoryKindV1;
|
|
52
|
+
/**
|
|
53
|
+
* v0.3.19 (G317-22) — true only for `repositoryKind: 'worktree'`. Every worktree-backed
|
|
54
|
+
* surface (change lists, diffs, staging, commit) is meaningless when this is false.
|
|
55
|
+
*/
|
|
56
|
+
readonly hasWorktree: boolean;
|
|
33
57
|
readonly repositoryRevision: string;
|
|
34
58
|
readonly rootLabel?: string;
|
|
35
59
|
readonly branch: string | null;
|
|
@@ -84,10 +84,15 @@ class GitReadModelServiceV1 {
|
|
|
84
84
|
const nextOffset = offset + page.length;
|
|
85
85
|
return Object.freeze({
|
|
86
86
|
isRepository: snapshot.isRepository,
|
|
87
|
+
repositoryKind: snapshot.repositoryKind,
|
|
88
|
+
hasWorktree: snapshot.repositoryKind === 'worktree',
|
|
87
89
|
repositoryRevision: snapshot.revision,
|
|
88
90
|
...(snapshot.root ? { rootLabel: (0, path_1.basename)(snapshot.root) } : {}),
|
|
89
91
|
branch: snapshot.branch,
|
|
90
|
-
|
|
92
|
+
// v0.3.19 (G317-22) — only a worktree-backed checkout can be "detached". A bare
|
|
93
|
+
// repository has no branch either, and reporting that as a detached HEAD invented a
|
|
94
|
+
// state the repository is not in.
|
|
95
|
+
detached: snapshot.repositoryKind === 'worktree' && !snapshot.branch,
|
|
91
96
|
head: snapshot.head ? snapshot.head.slice(0, 12) : null,
|
|
92
97
|
upstream: snapshot.upstream,
|
|
93
98
|
ahead: snapshot.ahead,
|
|
@@ -454,6 +459,7 @@ class GitReadModelServiceV1 {
|
|
|
454
459
|
}
|
|
455
460
|
async capture() {
|
|
456
461
|
let root;
|
|
462
|
+
let repositoryKind = 'absent';
|
|
457
463
|
try {
|
|
458
464
|
root =
|
|
459
465
|
this.repositoryRoot ??
|
|
@@ -461,11 +467,53 @@ class GitReadModelServiceV1 {
|
|
|
461
467
|
if (!isWithinRoot(root, this.workspace))
|
|
462
468
|
throw new Error('repository root escaped workspace');
|
|
463
469
|
this.repositoryRoot = root;
|
|
470
|
+
repositoryKind = 'worktree';
|
|
464
471
|
}
|
|
465
472
|
catch {
|
|
473
|
+
// v0.3.19 (G317-22) — `--show-toplevel` fails for a bare repository too, so the failure
|
|
474
|
+
// alone cannot say which case this is. Ask directly. Conflating "no repository" with
|
|
475
|
+
// "a repository that has no working tree" told the reader the wrong thing about a
|
|
476
|
+
// directory that is in fact a valid repository.
|
|
477
|
+
const bare = (await this.tryGit(['rev-parse', '--is-bare-repository'], this.workspace))
|
|
478
|
+
?.trim()
|
|
479
|
+
.toLowerCase();
|
|
480
|
+
repositoryKind = bare === 'true' ? 'bare' : 'absent';
|
|
481
|
+
}
|
|
482
|
+
if (repositoryKind === 'bare') {
|
|
483
|
+
// A bare repository has no index and no working tree, so there is nothing to report as
|
|
484
|
+
// changed — but its HEAD is real and worth stating. `clean` stays true in `status()`
|
|
485
|
+
// because zero files differ from an index that does not exist; `hasWorktree` is what
|
|
486
|
+
// tells the reader not to trust that as "you have nothing to commit".
|
|
487
|
+
const [branchResult, headResult] = await Promise.all([
|
|
488
|
+
this.tryGit(['symbolic-ref', '--quiet', '--short', 'HEAD'], this.workspace),
|
|
489
|
+
this.tryGit(['rev-parse', 'HEAD'], this.workspace),
|
|
490
|
+
]);
|
|
491
|
+
const branch = branchResult?.trim() || null;
|
|
492
|
+
const head = headResult?.trim() || null;
|
|
493
|
+
const revision = (0, crypto_1.createHash)('sha256')
|
|
494
|
+
.update(JSON.stringify({ bare: this.workspace, branch, head }))
|
|
495
|
+
.digest('hex');
|
|
496
|
+
return Object.freeze({
|
|
497
|
+
isRepository: true,
|
|
498
|
+
repositoryKind: 'bare',
|
|
499
|
+
branch,
|
|
500
|
+
head,
|
|
501
|
+
upstream: null,
|
|
502
|
+
ahead: 0,
|
|
503
|
+
behind: 0,
|
|
504
|
+
records: Object.freeze([]),
|
|
505
|
+
rawStatus: '',
|
|
506
|
+
stagedRecords: 0,
|
|
507
|
+
conflictedRecords: 0,
|
|
508
|
+
indexDigest: (0, crypto_1.createHash)('sha256').update('').digest('hex'),
|
|
509
|
+
revision,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
if (repositoryKind === 'absent') {
|
|
466
513
|
const revision = (0, crypto_1.createHash)('sha256').update(`not-git:${this.workspace}`).digest('hex');
|
|
467
514
|
return Object.freeze({
|
|
468
515
|
isRepository: false,
|
|
516
|
+
repositoryKind: 'absent',
|
|
469
517
|
branch: null,
|
|
470
518
|
head: null,
|
|
471
519
|
upstream: null,
|
|
@@ -479,6 +527,11 @@ class GitReadModelServiceV1 {
|
|
|
479
527
|
revision,
|
|
480
528
|
});
|
|
481
529
|
}
|
|
530
|
+
if (!root) {
|
|
531
|
+
// Only the `worktree` branch assigns a root and both other kinds returned above, so this
|
|
532
|
+
// is unreachable today — it exists so the invariant is enforced, not merely inferred.
|
|
533
|
+
throw new Error('Resolved a non-worktree repository without a root.');
|
|
534
|
+
}
|
|
482
535
|
const [rawStatus, branchResult, headResult, upstreamResult, indexListing] = await Promise.all([
|
|
483
536
|
this.runGit(['status', '--porcelain=v1', '-z', '--untracked-files=all'], root),
|
|
484
537
|
this.tryGit(['symbolic-ref', '--quiet', '--short', 'HEAD'], root),
|
|
@@ -532,6 +585,7 @@ class GitReadModelServiceV1 {
|
|
|
532
585
|
.digest('hex');
|
|
533
586
|
return Object.freeze({
|
|
534
587
|
isRepository: true,
|
|
588
|
+
repositoryKind: 'worktree',
|
|
535
589
|
root,
|
|
536
590
|
branch,
|
|
537
591
|
head,
|
|
@@ -941,6 +995,13 @@ class GitReadModelServiceV1 {
|
|
|
941
995
|
}
|
|
942
996
|
async requireRepositoryRoot() {
|
|
943
997
|
const snapshot = await this.capture();
|
|
998
|
+
if (snapshot.repositoryKind === 'bare') {
|
|
999
|
+
// v0.3.19 (G317-22) — a bare repository *is* a repository, so "unavailable" was the
|
|
1000
|
+
// wrong word for it. What it lacks is a working tree, and this panel is worktree-based:
|
|
1001
|
+
// the limit is stated rather than worked around, and reading from a different working
|
|
1002
|
+
// tree would widen access in a way the workspace boundary does not allow.
|
|
1003
|
+
throw new Error('This workspace is a bare repository. The Git panel reads a working tree, so it has no changes or history to show here.');
|
|
1004
|
+
}
|
|
944
1005
|
if (!snapshot.isRepository || !snapshot.root) {
|
|
945
1006
|
throw new Error('Git repository is unavailable.');
|
|
946
1007
|
}
|
|
@@ -1196,7 +1257,16 @@ function diffCommands(path, source, ignoreWhitespace = false, wordDiff = false)
|
|
|
1196
1257
|
return Object.freeze([
|
|
1197
1258
|
{
|
|
1198
1259
|
title: 'Staged',
|
|
1199
|
-
args: [
|
|
1260
|
+
args: [
|
|
1261
|
+
'diff',
|
|
1262
|
+
'--cached',
|
|
1263
|
+
...modes,
|
|
1264
|
+
'--no-ext-diff',
|
|
1265
|
+
'--no-textconv',
|
|
1266
|
+
'--unified=3',
|
|
1267
|
+
'--',
|
|
1268
|
+
path,
|
|
1269
|
+
],
|
|
1200
1270
|
},
|
|
1201
1271
|
]);
|
|
1202
1272
|
}
|
package/dist/web/protocol.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { ToolDetailPage, ToolDetailSummary } from '../runtime/tool-detail-r
|
|
|
8
8
|
import type { SessionMeta } from '../services/session-storage';
|
|
9
9
|
import type { ToolConfirmationPolicy } from '../services/global-config';
|
|
10
10
|
import type { WebFileContentPageV1, WebFileNodeV1, WebFileTreePageV1 } from './file-read-service';
|
|
11
|
-
import type { GitCommitIdentityV1, GitCommitOutcomeV1, GitCommitPreviewV1, GitIndexEntryV1, GitMutationResultV1, GitWorktreeSourceV1, WebGitCommitV1, WebGitDiffPageV1, WebGitFileSourceV1, WebGitFileV1, WebGitLogPageV1, WebGitStatusCountsV1, WebGitStatusV1 } from './git-read-model-service';
|
|
11
|
+
import type { GitCommitIdentityV1, GitCommitOutcomeV1, GitCommitPreviewV1, GitIndexEntryV1, GitMutationResultV1, GitRepositoryKindV1, GitWorktreeSourceV1, WebGitCommitV1, WebGitDiffPageV1, WebGitFileSourceV1, WebGitFileV1, WebGitLogPageV1, WebGitStatusCountsV1, WebGitStatusV1 } from './git-read-model-service';
|
|
12
12
|
import type { GitDiffCapabilitiesV2, GitDiffDocumentV2, GitDiffDocumentKindV2, GitDiffLineKindV2, GitDiffLineV2, GitHunkV2 } from './git-diff-document';
|
|
13
13
|
import type { WebReviewSnapshotV1 } from './review-service';
|
|
14
14
|
import type { WebSessionRuntimeSummaryV1 } from './session-runtime-registry';
|
|
@@ -423,9 +423,9 @@ export type WebWorkspaceInvalidationReasonV1 = 'context-change' | 'filesystem-ch
|
|
|
423
423
|
export type WebToolDetailSummaryV1 = ToolDetailSummary;
|
|
424
424
|
export type WebToolDetailPageV1 = ToolDetailPage;
|
|
425
425
|
export type { GitBlameLineV1, GitGraphPageV1, GitGraphRowV1, GitBlameResultV1, GitRefKindV1, GitRefV1, GitRefsV1, GitCommitSummaryV1, GitCommitDetailV1, GitCommitFileV1, GitCommitFilesV1, GitHistoryQueryV1, GitHistoryPageV1, GitFileHistoryPageV1, } from './git-history-service';
|
|
426
|
-
export type { GitCompareFileV1, GitCompareModeV1, GitCompareResultV1
|
|
426
|
+
export type { GitCompareFileV1, GitCompareModeV1, GitCompareResultV1 } from './git-compare-service';
|
|
427
427
|
export type { GitBlobResultV1, GitConflictShapeV1, GitConflictVersionsV1, GitFileVersionV1, GitSubmoduleEntryV1, GitVersionLabelV1, } from './git-version-service';
|
|
428
|
-
export type { WebFileContentPageV1, WebFileNodeV1, WebFileTreePageV1, GitCommitIdentityV1, GitCommitOutcomeV1, GitCommitPreviewV1, GitIndexEntryV1, GitMutationResultV1, GitWorktreeSourceV1, GitDiffCapabilitiesV2, GitDiffDocumentV2, GitDiffDocumentKindV2, GitDiffLineKindV2, GitDiffLineV2, GitHunkV2, WebGitCommitV1, WebGitDiffPageV1, WebGitFileSourceV1, WebGitFileV1, WebGitLogPageV1, WebGitStatusCountsV1, WebGitStatusV1, WebReviewSnapshotV1, WebTerminalCreateResultV1, WebTerminalExitV1, WebTerminalGapV1, WebTerminalMetadataV1, WebTerminalOutputFrameV1, WebTerminalStateV1, };
|
|
428
|
+
export type { WebFileContentPageV1, WebFileNodeV1, WebFileTreePageV1, GitCommitIdentityV1, GitCommitOutcomeV1, GitCommitPreviewV1, GitIndexEntryV1, GitMutationResultV1, GitWorktreeSourceV1, GitDiffCapabilitiesV2, GitDiffDocumentV2, GitDiffDocumentKindV2, GitDiffLineKindV2, GitDiffLineV2, GitHunkV2, GitRepositoryKindV1, WebGitCommitV1, WebGitDiffPageV1, WebGitFileSourceV1, WebGitFileV1, WebGitLogPageV1, WebGitStatusCountsV1, WebGitStatusV1, WebReviewSnapshotV1, WebTerminalCreateResultV1, WebTerminalExitV1, WebTerminalGapV1, WebTerminalMetadataV1, WebTerminalOutputFrameV1, WebTerminalStateV1, };
|
|
429
429
|
export interface WebBootstrapV1 {
|
|
430
430
|
readonly apiVersion: 1;
|
|
431
431
|
readonly productVersion: string;
|
package/dist/web/server.js
CHANGED
|
@@ -607,7 +607,16 @@ async function handleRequest(context) {
|
|
|
607
607
|
if (method === 'GET' && path === '/git/history') {
|
|
608
608
|
const contextGuard = requireContextGuardQuery(url);
|
|
609
609
|
sendJson(response, 200, await context.workbench.gitHistory(contextGuard, {
|
|
610
|
-
...optionalQueryParams(url, [
|
|
610
|
+
...optionalQueryParams(url, [
|
|
611
|
+
'message',
|
|
612
|
+
'author',
|
|
613
|
+
'sha',
|
|
614
|
+
'path',
|
|
615
|
+
'since',
|
|
616
|
+
'until',
|
|
617
|
+
'rev',
|
|
618
|
+
'cursor',
|
|
619
|
+
]),
|
|
611
620
|
pageSize: boundedInteger(url.searchParams.get('pageSize'), 30, 1, 100),
|
|
612
621
|
}));
|
|
613
622
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as a,j as e,I as D,s as
|
|
1
|
+
import{r as a,j as e,I as D,s as be,C as ye,W as ge}from"./index-DYeu_rs7.js";import{u as Ce,R as Ne}from"./useAutoNotice-h7c0Iz1E.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};
|