@agimon-ai/po-git 0.0.0-alpha.33 → 0.0.0-alpha.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/native.mjs +490 -76
- package/dist/extensions/native.mjs.map +1 -1
- package/package.json +9 -8
|
@@ -1,12 +1,399 @@
|
|
|
1
1
|
import { createApiClient, defineActivityGroup, defineChannelFile, defineFill, defineGlobalStore, defineNativePlugin, defineSessionStore, defineWebLifecycle, nativeApiTransport, sessionApiAddress } from "@agimon-ai/po-core/native";
|
|
2
|
-
import { AlertIcon, Badge, BranchIcon, Button, ChevronRightIcon, Dot, FileIcon, Input, MessageIcon, Sheet, SheetBody, SheetContent, SheetHeader, SheetTitle, Spinner, SyntaxLine, Textarea, fontOf } from "@agimon-ai/po-native-components";
|
|
3
|
-
import { createElement, useCallback, useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
4
2
|
import { View } from "react-native-unistyles/components/native/View";
|
|
5
3
|
import { Text } from "react-native-unistyles/components/native/Text";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import { AlertIcon, Badge, BranchIcon, Button, ChevronRightIcon, Dot, FileIcon, Input, Label, ListRow, MessageIcon, SegmentedControl, Sheet, SheetBody, SheetContent, SheetHeader, SheetTitle, Spinner, SyntaxLine, Textarea, fontOf } from "@agimon-ai/po-native-components";
|
|
5
|
+
import { createElement, memo, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
8
6
|
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
|
9
7
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { ScrollView } from "react-native-unistyles/components/native/ScrollView";
|
|
9
|
+
import { Pressable } from "react-native-unistyles/components/native/Pressable";
|
|
10
|
+
const api = createApiClient({
|
|
11
|
+
/** Workspace: the new-session dialog's branch list. */
|
|
12
|
+
branches: {
|
|
13
|
+
method: "GET",
|
|
14
|
+
path: "/branches",
|
|
15
|
+
response: void 0
|
|
16
|
+
},
|
|
17
|
+
/** Workspace: a top-level worktree session on an existing or new branch. */
|
|
18
|
+
createSession: {
|
|
19
|
+
method: "POST",
|
|
20
|
+
path: "/sessions",
|
|
21
|
+
response: void 0
|
|
22
|
+
},
|
|
23
|
+
/** Workspace: that workspace's saved remote auth, never the token itself. */
|
|
24
|
+
auth: {
|
|
25
|
+
method: "GET",
|
|
26
|
+
path: "/auth",
|
|
27
|
+
query: ["repositoryId"],
|
|
28
|
+
response: void 0
|
|
29
|
+
},
|
|
30
|
+
/** Workspace: replaces that workspace's remote auth. Passkey step-up gated for remote devices. */
|
|
31
|
+
saveAuth: {
|
|
32
|
+
method: "PUT",
|
|
33
|
+
path: "/auth",
|
|
34
|
+
query: ["repositoryId"],
|
|
35
|
+
response: void 0
|
|
36
|
+
},
|
|
37
|
+
/** Session: the files this session's checkout changed against its base. */
|
|
38
|
+
review: {
|
|
39
|
+
method: "GET",
|
|
40
|
+
path: "/review",
|
|
41
|
+
response: void 0
|
|
42
|
+
},
|
|
43
|
+
/** Session: one changed file's hunks. */
|
|
44
|
+
reviewFile: {
|
|
45
|
+
method: "GET",
|
|
46
|
+
path: "/review/file",
|
|
47
|
+
query: ["path"],
|
|
48
|
+
response: void 0
|
|
49
|
+
}
|
|
50
|
+
}, {
|
|
51
|
+
scopes: [
|
|
52
|
+
"global",
|
|
53
|
+
"workspace",
|
|
54
|
+
"session"
|
|
55
|
+
],
|
|
56
|
+
basePath: "git",
|
|
57
|
+
transport: nativeApiTransport,
|
|
58
|
+
sessionAddress: sessionApiAddress
|
|
59
|
+
});
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/extensions/workspaces/(frontend)/_lib/newSessionForm.ts
|
|
62
|
+
const MAX_LISTED = 200;
|
|
63
|
+
/** Local branches first, then remote-only ones, filtered by a case-insensitive substring. */
|
|
64
|
+
function branchOptions(branches, query) {
|
|
65
|
+
const needle = query.trim().toLowerCase();
|
|
66
|
+
const matches = (text) => needle === "" || text.toLowerCase().includes(needle);
|
|
67
|
+
const local = branches.local.filter((branch) => matches(branch.name)).map((branch) => ({
|
|
68
|
+
key: `local:${branch.name}`,
|
|
69
|
+
name: branch.name,
|
|
70
|
+
current: branch.name === branches.current,
|
|
71
|
+
...branch.checkedOutAt === void 0 ? {} : { busyAt: branch.checkedOutAt }
|
|
72
|
+
}));
|
|
73
|
+
const remote = branches.remote.filter((branch) => matches(`${branch.remote}/${branch.name}`)).map((branch) => ({
|
|
74
|
+
key: `remote:${branch.remote}/${branch.name}`,
|
|
75
|
+
name: branch.name,
|
|
76
|
+
remote: branch.remote,
|
|
77
|
+
current: false
|
|
78
|
+
}));
|
|
79
|
+
return [...local, ...remote].slice(0, MAX_LISTED);
|
|
80
|
+
}
|
|
81
|
+
/** Why the form cannot be submitted yet, or undefined when it can. */
|
|
82
|
+
function formProblem(state) {
|
|
83
|
+
if (state.mode === "existing") {
|
|
84
|
+
if (state.selected === void 0) return "Pick a branch.";
|
|
85
|
+
if (state.selected.busyAt !== void 0) return `That branch is checked out at ${state.selected.busyAt}.`;
|
|
86
|
+
}
|
|
87
|
+
if (state.mode === "new" && state.newBranch.trim() === "") return "Name the new branch.";
|
|
88
|
+
}
|
|
89
|
+
/** The worktree create request for the form, or undefined in plain mode. */
|
|
90
|
+
function createRequest(state) {
|
|
91
|
+
const name = state.name.trim();
|
|
92
|
+
const named = name === "" ? {} : { name };
|
|
93
|
+
if (state.mode === "existing" && state.selected !== void 0) return {
|
|
94
|
+
mode: "existing-branch",
|
|
95
|
+
branch: state.selected.name,
|
|
96
|
+
...state.selected.remote === void 0 ? {} : { remote: state.selected.remote },
|
|
97
|
+
...named
|
|
98
|
+
};
|
|
99
|
+
if (state.mode === "new") {
|
|
100
|
+
const baseRef = state.baseRef.trim();
|
|
101
|
+
return {
|
|
102
|
+
mode: "new-branch",
|
|
103
|
+
branch: state.newBranch.trim(),
|
|
104
|
+
...baseRef === "" ? {} : { baseRef },
|
|
105
|
+
...named
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/extensions/workspaces/(frontend)/fill/_components/GitNewSessionSheet.native.tsx
|
|
111
|
+
const MODES = [
|
|
112
|
+
{
|
|
113
|
+
value: "existing",
|
|
114
|
+
label: "Existing"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
value: "new",
|
|
118
|
+
label: "New branch"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
value: "plain",
|
|
122
|
+
label: "No branch"
|
|
123
|
+
}
|
|
124
|
+
];
|
|
125
|
+
/** The native twin of GitNewSessionDialogView: the same form state and checks, in sheet parts. */
|
|
126
|
+
function GitNewSessionSheetView({ workspaceRoot, branches, loadError = "", initialForm, busy = false, error = "", onCreate }) {
|
|
127
|
+
const [form, setForm] = useState({
|
|
128
|
+
mode: "existing",
|
|
129
|
+
newBranch: "",
|
|
130
|
+
baseRef: "",
|
|
131
|
+
name: "",
|
|
132
|
+
...initialForm
|
|
133
|
+
});
|
|
134
|
+
const [query, setQuery] = useState("");
|
|
135
|
+
const ready = branches?.repository === true && loadError === "";
|
|
136
|
+
const loading = branches === null && loadError === "";
|
|
137
|
+
const mode = ready || loading ? form.mode : "plain";
|
|
138
|
+
const state = {
|
|
139
|
+
...form,
|
|
140
|
+
mode,
|
|
141
|
+
baseRef: form.baseRef || branches?.defaultBase || ""
|
|
142
|
+
};
|
|
143
|
+
const problem = formProblem(state);
|
|
144
|
+
const update = (patch) => setForm((current) => ({
|
|
145
|
+
...current,
|
|
146
|
+
...patch
|
|
147
|
+
}));
|
|
148
|
+
const options = branches === null ? [] : branchOptions(branches, query);
|
|
149
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(SheetHeader, { children: /* @__PURE__ */ jsx(SheetTitle, { children: "New session" }) }), /* @__PURE__ */ jsxs(SheetBody, {
|
|
150
|
+
style: styles$3.body,
|
|
151
|
+
children: [
|
|
152
|
+
/* @__PURE__ */ jsx(Text, {
|
|
153
|
+
style: styles$3.root,
|
|
154
|
+
numberOfLines: 1,
|
|
155
|
+
ellipsizeMode: "middle",
|
|
156
|
+
children: workspaceRoot
|
|
157
|
+
}),
|
|
158
|
+
!ready ? /* @__PURE__ */ jsx(Text, {
|
|
159
|
+
testID: "git-new-session-plain-note",
|
|
160
|
+
style: styles$3.dim,
|
|
161
|
+
children: loading ? "Reading branches..." : loadError || "This workspace is not a git checkout. Start a plain session in this folder."
|
|
162
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(SegmentedControl, {
|
|
163
|
+
accessibilityLabel: "Session kind",
|
|
164
|
+
options: MODES,
|
|
165
|
+
value: mode,
|
|
166
|
+
disabled: busy,
|
|
167
|
+
onValueChange: (value) => update({ mode: value })
|
|
168
|
+
}), mode === "existing" ? /* @__PURE__ */ jsxs(View, {
|
|
169
|
+
style: styles$3.field,
|
|
170
|
+
children: [
|
|
171
|
+
/* @__PURE__ */ jsx(Label, { children: "Branch" }),
|
|
172
|
+
/* @__PURE__ */ jsx(Input, {
|
|
173
|
+
testID: "git-new-session-branch-search",
|
|
174
|
+
accessibilityLabel: "Find a branch",
|
|
175
|
+
placeholder: "Find a branch",
|
|
176
|
+
autoCapitalize: "none",
|
|
177
|
+
autoCorrect: false,
|
|
178
|
+
value: query,
|
|
179
|
+
onChangeText: setQuery,
|
|
180
|
+
disabled: busy
|
|
181
|
+
}),
|
|
182
|
+
/* @__PURE__ */ jsxs(View, {
|
|
183
|
+
style: styles$3.list,
|
|
184
|
+
accessibilityLabel: "Branches",
|
|
185
|
+
children: [options.length === 0 ? /* @__PURE__ */ jsx(Text, {
|
|
186
|
+
style: [styles$3.dim, styles$3.pad],
|
|
187
|
+
children: "No matching branches"
|
|
188
|
+
}) : null, options.map((branch, index) => /* @__PURE__ */ jsxs(View, { children: [branch.remote !== void 0 && (index === 0 || options[index - 1]?.remote === void 0) ? /* @__PURE__ */ jsx(Text, {
|
|
189
|
+
style: [
|
|
190
|
+
styles$3.dim,
|
|
191
|
+
styles$3.pad,
|
|
192
|
+
styles$3.divider
|
|
193
|
+
],
|
|
194
|
+
children: "Remote branches"
|
|
195
|
+
}) : null, /* @__PURE__ */ jsx(ListRow, {
|
|
196
|
+
title: /* @__PURE__ */ jsx(Text, {
|
|
197
|
+
style: styles$3.mono,
|
|
198
|
+
children: branch.remote === void 0 ? branch.name : `${branch.remote}/${branch.name}`
|
|
199
|
+
}),
|
|
200
|
+
description: branch.busyAt !== void 0 ? `Checked out in ${branch.busyAt}` : branch.current ? "Current" : void 0,
|
|
201
|
+
selected: form.selected?.key === branch.key,
|
|
202
|
+
disabled: branch.busyAt !== void 0 || busy,
|
|
203
|
+
disclosure: false,
|
|
204
|
+
onPress: () => update({ selected: branch })
|
|
205
|
+
})] }, branch.key))]
|
|
206
|
+
})
|
|
207
|
+
]
|
|
208
|
+
}) : mode === "new" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(View, {
|
|
209
|
+
style: styles$3.field,
|
|
210
|
+
children: [/* @__PURE__ */ jsx(Label, { children: "Branch name" }), /* @__PURE__ */ jsx(Input, {
|
|
211
|
+
testID: "git-new-session-new-branch",
|
|
212
|
+
accessibilityLabel: "Branch name",
|
|
213
|
+
placeholder: "feature/my-change",
|
|
214
|
+
autoCapitalize: "none",
|
|
215
|
+
autoCorrect: false,
|
|
216
|
+
style: styles$3.mono,
|
|
217
|
+
value: form.newBranch,
|
|
218
|
+
onChangeText: (newBranch) => update({ newBranch }),
|
|
219
|
+
disabled: busy
|
|
220
|
+
})]
|
|
221
|
+
}), /* @__PURE__ */ jsxs(View, {
|
|
222
|
+
style: styles$3.field,
|
|
223
|
+
children: [/* @__PURE__ */ jsx(Label, { children: "From" }), /* @__PURE__ */ jsx(Input, {
|
|
224
|
+
testID: "git-new-session-base",
|
|
225
|
+
accessibilityLabel: "From",
|
|
226
|
+
placeholder: branches?.defaultBase ?? "Current branch",
|
|
227
|
+
autoCapitalize: "none",
|
|
228
|
+
autoCorrect: false,
|
|
229
|
+
style: styles$3.mono,
|
|
230
|
+
value: form.baseRef,
|
|
231
|
+
onChangeText: (baseRef) => update({ baseRef }),
|
|
232
|
+
disabled: busy
|
|
233
|
+
})]
|
|
234
|
+
})] }) : /* @__PURE__ */ jsx(Text, {
|
|
235
|
+
style: styles$3.dim,
|
|
236
|
+
children: "Start in this workspace folder, without creating a worktree."
|
|
237
|
+
})] }),
|
|
238
|
+
/* @__PURE__ */ jsxs(View, {
|
|
239
|
+
style: styles$3.field,
|
|
240
|
+
children: [/* @__PURE__ */ jsx(Label, { children: "Name" }), /* @__PURE__ */ jsx(Input, {
|
|
241
|
+
testID: "new-session-name",
|
|
242
|
+
accessibilityLabel: "Session name",
|
|
243
|
+
placeholder: mode === "plain" ? "Optional" : "Optional, defaults to the branch",
|
|
244
|
+
value: form.name,
|
|
245
|
+
onChangeText: (name) => update({ name }),
|
|
246
|
+
disabled: busy
|
|
247
|
+
})]
|
|
248
|
+
}),
|
|
249
|
+
mode !== "plain" ? /* @__PURE__ */ jsx(Text, {
|
|
250
|
+
style: styles$3.dim,
|
|
251
|
+
children: "Runs in its own worktree, so this checkout stays as it is."
|
|
252
|
+
}) : null,
|
|
253
|
+
error ? /* @__PURE__ */ jsx(Text, {
|
|
254
|
+
testID: "new-session-error",
|
|
255
|
+
style: styles$3.error,
|
|
256
|
+
accessibilityRole: "alert",
|
|
257
|
+
children: error
|
|
258
|
+
}) : null,
|
|
259
|
+
/* @__PURE__ */ jsx(Button, {
|
|
260
|
+
testID: "new-session-create",
|
|
261
|
+
variant: "primary",
|
|
262
|
+
size: "lg",
|
|
263
|
+
loading: busy,
|
|
264
|
+
loadingLabel: mode === "plain" ? "Creating session" : "Creating worktree",
|
|
265
|
+
disabled: loading || problem !== void 0,
|
|
266
|
+
accessibilityHint: problem,
|
|
267
|
+
onPress: () => onCreate(state),
|
|
268
|
+
children: "Create session"
|
|
269
|
+
})
|
|
270
|
+
]
|
|
271
|
+
})] });
|
|
272
|
+
}
|
|
273
|
+
function errorOf(body, status) {
|
|
274
|
+
return typeof body === "object" && body !== null && typeof body.error === "string" ? body.error : `The cockpit answered ${String(status)}.`;
|
|
275
|
+
}
|
|
276
|
+
/** The native new-session sheet when git is installed; mirrors GitNewSessionDialog's calls. */
|
|
277
|
+
function GitNewSessionSheet({ newSession }) {
|
|
278
|
+
const [branches, setBranches] = useState(null);
|
|
279
|
+
const [loadError, setLoadError] = useState("");
|
|
280
|
+
const [error, setError] = useState("");
|
|
281
|
+
const [busy, setBusy] = useState(false);
|
|
282
|
+
const workspaceId = newSession?.workspaceId;
|
|
283
|
+
const request = newSession?.requestWithStepUp;
|
|
284
|
+
useEffect(() => {
|
|
285
|
+
if (workspaceId === void 0 || request === void 0) return;
|
|
286
|
+
let stale = false;
|
|
287
|
+
request(api.workspace(workspaceId).branches.url()).then(async (response) => {
|
|
288
|
+
const body = await response.json().catch(() => void 0);
|
|
289
|
+
if (stale) return;
|
|
290
|
+
if (!response.ok) setLoadError(errorOf(body, response.status));
|
|
291
|
+
else setBranches(body);
|
|
292
|
+
}).catch(() => {
|
|
293
|
+
if (!stale) setLoadError("The branches could not be loaded.");
|
|
294
|
+
});
|
|
295
|
+
return () => {
|
|
296
|
+
stale = true;
|
|
297
|
+
};
|
|
298
|
+
}, [workspaceId, request]);
|
|
299
|
+
if (newSession === void 0) return null;
|
|
300
|
+
const submit = async (state) => {
|
|
301
|
+
if (busy || formProblem(state) !== void 0) return;
|
|
302
|
+
setBusy(true);
|
|
303
|
+
setError("");
|
|
304
|
+
const body = createRequest(state);
|
|
305
|
+
let failure;
|
|
306
|
+
try {
|
|
307
|
+
if (body === void 0) failure = await newSession.createPlain(state.name);
|
|
308
|
+
else {
|
|
309
|
+
const response = await newSession.requestWithStepUp(api.workspace(newSession.workspaceId).createSession.url(), {
|
|
310
|
+
method: "POST",
|
|
311
|
+
headers: { "Content-Type": "application/json" },
|
|
312
|
+
body: JSON.stringify(body)
|
|
313
|
+
});
|
|
314
|
+
const answer = await response.json().catch(() => void 0);
|
|
315
|
+
failure = response.ok ? await newSession.openCreated(answer.sessionId) : errorOf(answer, response.status);
|
|
316
|
+
}
|
|
317
|
+
} catch {
|
|
318
|
+
failure = "The cockpit is unreachable.";
|
|
319
|
+
}
|
|
320
|
+
if (failure === void 0) return;
|
|
321
|
+
setError(failure);
|
|
322
|
+
setBusy(false);
|
|
323
|
+
};
|
|
324
|
+
return /* @__PURE__ */ jsx(GitNewSessionSheetView, {
|
|
325
|
+
workspaceRoot: newSession.workspaceRoot,
|
|
326
|
+
branches,
|
|
327
|
+
loadError,
|
|
328
|
+
busy,
|
|
329
|
+
error,
|
|
330
|
+
onCreate: (state) => void submit(state)
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
const styles$3 = StyleSheet.create((theme) => {
|
|
334
|
+
const sans = fontOf(theme.fontFamily.sans);
|
|
335
|
+
const mono = fontOf(theme.fontFamily.mono);
|
|
336
|
+
return {
|
|
337
|
+
body: {
|
|
338
|
+
gap: theme.spacing(4),
|
|
339
|
+
uni__dependencies: [0]
|
|
340
|
+
},
|
|
341
|
+
root: {
|
|
342
|
+
fontFamily: mono,
|
|
343
|
+
fontSize: theme.fontSize.sm,
|
|
344
|
+
lineHeight: theme.lineHeight.sm,
|
|
345
|
+
color: theme.colors.dim,
|
|
346
|
+
textAlign: "center",
|
|
347
|
+
uni__dependencies: [0]
|
|
348
|
+
},
|
|
349
|
+
field: {
|
|
350
|
+
gap: theme.spacing(1.5),
|
|
351
|
+
uni__dependencies: [0]
|
|
352
|
+
},
|
|
353
|
+
dim: {
|
|
354
|
+
fontFamily: sans,
|
|
355
|
+
fontSize: theme.fontSize.sm,
|
|
356
|
+
lineHeight: theme.lineHeightRelaxed.sm,
|
|
357
|
+
color: theme.colors.dim,
|
|
358
|
+
uni__dependencies: [0]
|
|
359
|
+
},
|
|
360
|
+
error: {
|
|
361
|
+
fontFamily: sans,
|
|
362
|
+
fontSize: theme.fontSize.sm,
|
|
363
|
+
lineHeight: theme.lineHeightRelaxed.sm,
|
|
364
|
+
color: theme.colors.red,
|
|
365
|
+
uni__dependencies: [0]
|
|
366
|
+
},
|
|
367
|
+
mono: {
|
|
368
|
+
fontFamily: mono,
|
|
369
|
+
fontSize: theme.fontSize.base,
|
|
370
|
+
lineHeight: theme.lineHeight.base,
|
|
371
|
+
color: theme.colors.hi,
|
|
372
|
+
uni__dependencies: [0]
|
|
373
|
+
},
|
|
374
|
+
list: {
|
|
375
|
+
borderRadius: theme.borderRadius.lg,
|
|
376
|
+
borderWidth: 1,
|
|
377
|
+
borderColor: theme.colors["border-soft"],
|
|
378
|
+
overflow: "hidden",
|
|
379
|
+
uni__dependencies: [0]
|
|
380
|
+
},
|
|
381
|
+
pad: {
|
|
382
|
+
paddingHorizontal: theme.spacing(4),
|
|
383
|
+
paddingVertical: theme.spacing(2),
|
|
384
|
+
uni__dependencies: [0]
|
|
385
|
+
},
|
|
386
|
+
divider: {
|
|
387
|
+
borderTopWidth: StyleSheet.hairlineWidth,
|
|
388
|
+
borderTopColor: theme.colors["border-soft"],
|
|
389
|
+
uni__dependencies: [0]
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
});
|
|
393
|
+
//#endregion
|
|
394
|
+
//#region src/extensions/workspaces/(frontend)/fill/GitNewSession.new-session.native.tsx
|
|
395
|
+
var GitNewSession_new_session_native_default = defineFill({ component: GitNewSessionSheet });
|
|
396
|
+
//#endregion
|
|
10
397
|
//#region src/types/webWorktrees.ts
|
|
11
398
|
/**
|
|
12
399
|
* The cockpit's view of this session's worktrees.
|
|
@@ -858,56 +1245,6 @@ var git_changes_native_default = defineChannelFile(gitChangesChannel);
|
|
|
858
1245
|
//#endregion
|
|
859
1246
|
//#region src/extensions/workspaces/sessions/(frontend)/channel/git-worktrees.native.ts
|
|
860
1247
|
var git_worktrees_native_default = defineChannelFile(worktreesChannel);
|
|
861
|
-
const api = createApiClient({
|
|
862
|
-
/** Workspace: the new-session dialog's branch list. */
|
|
863
|
-
branches: {
|
|
864
|
-
method: "GET",
|
|
865
|
-
path: "/branches",
|
|
866
|
-
response: void 0
|
|
867
|
-
},
|
|
868
|
-
/** Workspace: a top-level worktree session on an existing or new branch. */
|
|
869
|
-
createSession: {
|
|
870
|
-
method: "POST",
|
|
871
|
-
path: "/sessions",
|
|
872
|
-
response: void 0
|
|
873
|
-
},
|
|
874
|
-
/** Workspace: that workspace's saved remote auth, never the token itself. */
|
|
875
|
-
auth: {
|
|
876
|
-
method: "GET",
|
|
877
|
-
path: "/auth",
|
|
878
|
-
query: ["repositoryId"],
|
|
879
|
-
response: void 0
|
|
880
|
-
},
|
|
881
|
-
/** Workspace: replaces that workspace's remote auth. Passkey step-up gated for remote devices. */
|
|
882
|
-
saveAuth: {
|
|
883
|
-
method: "PUT",
|
|
884
|
-
path: "/auth",
|
|
885
|
-
query: ["repositoryId"],
|
|
886
|
-
response: void 0
|
|
887
|
-
},
|
|
888
|
-
/** Session: the files this session's checkout changed against its base. */
|
|
889
|
-
review: {
|
|
890
|
-
method: "GET",
|
|
891
|
-
path: "/review",
|
|
892
|
-
response: void 0
|
|
893
|
-
},
|
|
894
|
-
/** Session: one changed file's hunks. */
|
|
895
|
-
reviewFile: {
|
|
896
|
-
method: "GET",
|
|
897
|
-
path: "/review/file",
|
|
898
|
-
query: ["path"],
|
|
899
|
-
response: void 0
|
|
900
|
-
}
|
|
901
|
-
}, {
|
|
902
|
-
scopes: [
|
|
903
|
-
"global",
|
|
904
|
-
"workspace",
|
|
905
|
-
"session"
|
|
906
|
-
],
|
|
907
|
-
basePath: "git",
|
|
908
|
-
transport: nativeApiTransport,
|
|
909
|
-
sessionAddress: sessionApiAddress
|
|
910
|
-
});
|
|
911
1248
|
//#endregion
|
|
912
1249
|
//#region src/extensions/workspaces/sessions/(frontend)/_lib/gitReviewApi.native.ts
|
|
913
1250
|
/**
|
|
@@ -1318,10 +1655,21 @@ function FileHeader({ entry, index, total, onPrevious, onNext, onChanges }) {
|
|
|
1318
1655
|
]
|
|
1319
1656
|
});
|
|
1320
1657
|
}
|
|
1321
|
-
function DiffRows({ rows, comments = [], selection, onLine, showInlineComments = true }) {
|
|
1658
|
+
const DiffRows = memo(function DiffRows({ rows, comments = [], selection, onLine, showInlineComments = true, gutterDigits }) {
|
|
1659
|
+
const anchoredComments = useMemo(() => {
|
|
1660
|
+
const index = /* @__PURE__ */ new Map();
|
|
1661
|
+
for (const comment of comments) {
|
|
1662
|
+
const key = `${comment.side}:${comment.endLine ?? comment.startLine}`;
|
|
1663
|
+
const entries = index.get(key);
|
|
1664
|
+
if (entries) entries.push(comment);
|
|
1665
|
+
else index.set(key, [comment]);
|
|
1666
|
+
}
|
|
1667
|
+
return index;
|
|
1668
|
+
}, [comments]);
|
|
1669
|
+
const digits = gutterDigits ?? rows.reduce((max, row) => Math.max(max, String(row.line).length), 1);
|
|
1322
1670
|
return /* @__PURE__ */ jsx(View, { children: rows.map((row, i) => {
|
|
1323
1671
|
const side = row.marker === "-" ? "old" : "new";
|
|
1324
|
-
const anchored =
|
|
1672
|
+
const anchored = anchoredComments.get(`${side}:${row.line}`) ?? [];
|
|
1325
1673
|
return /* @__PURE__ */ jsxs(View, { children: [/* @__PURE__ */ jsxs(Pressable, {
|
|
1326
1674
|
accessibilityRole: "button",
|
|
1327
1675
|
accessibilityLabel: `Select ${side} line ${row.line}`,
|
|
@@ -1334,8 +1682,9 @@ function DiffRows({ rows, comments = [], selection, onLine, showInlineComments =
|
|
|
1334
1682
|
],
|
|
1335
1683
|
children: [
|
|
1336
1684
|
/* @__PURE__ */ jsx(Text, {
|
|
1685
|
+
numberOfLines: 1,
|
|
1337
1686
|
style: [styles$1.mono, styles$1.gutter],
|
|
1338
|
-
children: row.line
|
|
1687
|
+
children: String(row.line).padStart(digits, "\xA0")
|
|
1339
1688
|
}),
|
|
1340
1689
|
/* @__PURE__ */ jsx(Text, {
|
|
1341
1690
|
style: [styles$1.dim, styles$1.mono],
|
|
@@ -1361,7 +1710,7 @@ function DiffRows({ rows, comments = [], selection, onLine, showInlineComments =
|
|
|
1361
1710
|
})]
|
|
1362
1711
|
}, comment.id))] }, i);
|
|
1363
1712
|
}) });
|
|
1364
|
-
}
|
|
1713
|
+
});
|
|
1365
1714
|
function lineLabel(comment) {
|
|
1366
1715
|
return comment.startLine === void 0 ? "File comment" : comment.endLine === comment.startLine || comment.endLine === void 0 ? `Line ${comment.startLine}` : `Lines ${comment.startLine} to ${comment.endLine}`;
|
|
1367
1716
|
}
|
|
@@ -1637,10 +1986,20 @@ const styles$1 = StyleSheet.create((theme) => ({
|
|
|
1637
1986
|
},
|
|
1638
1987
|
gutter: {
|
|
1639
1988
|
color: theme.colors.dim,
|
|
1640
|
-
width:
|
|
1989
|
+
width: "auto",
|
|
1990
|
+
minWidth: 24,
|
|
1991
|
+
flexShrink: 0,
|
|
1641
1992
|
textAlign: "right",
|
|
1642
1993
|
uni__dependencies: [0]
|
|
1643
1994
|
},
|
|
1995
|
+
disclosure: {
|
|
1996
|
+
padding: theme.spacing(3),
|
|
1997
|
+
gap: theme.spacing(2),
|
|
1998
|
+
borderTopWidth: 1,
|
|
1999
|
+
borderColor: theme.colors.border,
|
|
2000
|
+
backgroundColor: theme.colors.bg,
|
|
2001
|
+
uni__dependencies: [0]
|
|
2002
|
+
},
|
|
1644
2003
|
code: {
|
|
1645
2004
|
flex: 1,
|
|
1646
2005
|
minWidth: 0,
|
|
@@ -1676,8 +2035,25 @@ const styles$1 = StyleSheet.create((theme) => ({
|
|
|
1676
2035
|
},
|
|
1677
2036
|
remove: { alignSelf: "flex-end" }
|
|
1678
2037
|
}));
|
|
2038
|
+
const DIFF_ROW_STEP = 200;
|
|
1679
2039
|
function GitReviewView(props) {
|
|
1680
2040
|
const { screen, activity, summary, file, path, comments } = props;
|
|
2041
|
+
const rows = useMemo(() => file?.hunks.flatMap((hunk) => hunk.rows) ?? [], [file?.hunks]);
|
|
2042
|
+
const gutterDigits = useMemo(() => rows.reduce((max, row) => Math.max(max, String(row.line).length), 1), [rows]);
|
|
2043
|
+
const fileComments = useMemo(() => comments.filter((comment) => comment.path === path), [comments, path]);
|
|
2044
|
+
const initialLimit = props.initialVisibleRows ?? DIFF_ROW_STEP;
|
|
2045
|
+
const [disclosure, setDisclosure] = useState({
|
|
2046
|
+
rows,
|
|
2047
|
+
path,
|
|
2048
|
+
limit: initialLimit
|
|
2049
|
+
});
|
|
2050
|
+
const current = disclosure.rows === rows && disclosure.path === path ? disclosure : {
|
|
2051
|
+
rows,
|
|
2052
|
+
path,
|
|
2053
|
+
limit: initialLimit
|
|
2054
|
+
};
|
|
2055
|
+
if (current !== disclosure) setDisclosure(current);
|
|
2056
|
+
const visibleRows = useMemo(() => rows.slice(0, current.limit), [rows, current.limit]);
|
|
1681
2057
|
const index = summary?.files.findIndex((entry) => entry.path === path) ?? -1;
|
|
1682
2058
|
const entry = summary?.files[index];
|
|
1683
2059
|
return /* @__PURE__ */ jsxs(View, {
|
|
@@ -1789,13 +2165,14 @@ function GitReviewView(props) {
|
|
|
1789
2165
|
style: styles$1.section,
|
|
1790
2166
|
children: file.binary ? "Binary file" : "Diff too large to display"
|
|
1791
2167
|
})
|
|
1792
|
-
}) :
|
|
1793
|
-
rows:
|
|
1794
|
-
comments:
|
|
2168
|
+
}) : /* @__PURE__ */ jsx(DiffRows, {
|
|
2169
|
+
rows: visibleRows,
|
|
2170
|
+
comments: fileComments,
|
|
1795
2171
|
selection: props.selection,
|
|
1796
|
-
onLine: props.onLine
|
|
1797
|
-
|
|
1798
|
-
|
|
2172
|
+
onLine: props.onLine,
|
|
2173
|
+
gutterDigits
|
|
2174
|
+
}),
|
|
2175
|
+
fileComments.filter((comment) => comment.startLine === void 0).map((comment) => /* @__PURE__ */ jsxs(View, {
|
|
1799
2176
|
style: styles$1.inlineComment,
|
|
1800
2177
|
children: [/* @__PURE__ */ jsx(Text, {
|
|
1801
2178
|
style: styles$1.blue,
|
|
@@ -1819,7 +2196,7 @@ function GitReviewView(props) {
|
|
|
1819
2196
|
style: [styles$1.red, styles$1.content],
|
|
1820
2197
|
children: props.error
|
|
1821
2198
|
}) : null]
|
|
1822
|
-
}),
|
|
2199
|
+
}, `${screen}:${path ?? ""}`),
|
|
1823
2200
|
/* @__PURE__ */ jsx(Footer, {
|
|
1824
2201
|
count: comments.length,
|
|
1825
2202
|
confirmation: screen === "changes" && activity.changes?.rebase !== void 0,
|
|
@@ -1827,7 +2204,35 @@ function GitReviewView(props) {
|
|
|
1827
2204
|
onReview: () => props.onScreen("review"),
|
|
1828
2205
|
onSend: props.onSend,
|
|
1829
2206
|
onDiscard: props.onDiscard
|
|
1830
|
-
})
|
|
2207
|
+
}),
|
|
2208
|
+
screen === "file" && file && !file.binary && !file.tooLarge ? /* @__PURE__ */ jsxs(View, {
|
|
2209
|
+
style: styles$1.disclosure,
|
|
2210
|
+
children: [/* @__PURE__ */ jsxs(Text, {
|
|
2211
|
+
style: styles$1.dim,
|
|
2212
|
+
accessibilityLiveRegion: "polite",
|
|
2213
|
+
children: [
|
|
2214
|
+
Math.min(current.limit, rows.length),
|
|
2215
|
+
" of ",
|
|
2216
|
+
rows.length,
|
|
2217
|
+
" diff rows shown",
|
|
2218
|
+
props.selection ? ` · ${props.selection.side} line ${props.selection.startLine} selected` : ""
|
|
2219
|
+
]
|
|
2220
|
+
}), current.limit < rows.length ? /* @__PURE__ */ jsxs(Button, {
|
|
2221
|
+
variant: "outline",
|
|
2222
|
+
onPress: () => setDisclosure({
|
|
2223
|
+
...current,
|
|
2224
|
+
limit: Math.min(rows.length, current.limit + DIFF_ROW_STEP)
|
|
2225
|
+
}),
|
|
2226
|
+
children: [
|
|
2227
|
+
"Show ",
|
|
2228
|
+
Math.min(DIFF_ROW_STEP, rows.length - current.limit),
|
|
2229
|
+
" more rows"
|
|
2230
|
+
]
|
|
2231
|
+
}) : /* @__PURE__ */ jsx(Text, {
|
|
2232
|
+
style: styles$1.dim,
|
|
2233
|
+
children: "All diff rows shown"
|
|
2234
|
+
})]
|
|
2235
|
+
}) : null
|
|
1831
2236
|
]
|
|
1832
2237
|
});
|
|
1833
2238
|
}
|
|
@@ -1866,7 +2271,6 @@ function GitReviewPanelSession({ sessionId, sendSessionFrame }) {
|
|
|
1866
2271
|
const [path, setPath] = useState();
|
|
1867
2272
|
const [selection, setSelection] = useState();
|
|
1868
2273
|
const [draft, setDraft] = useState();
|
|
1869
|
-
const [comment, setComment] = useState("");
|
|
1870
2274
|
const request = useRef(void 0);
|
|
1871
2275
|
const fingerprint = JSON.stringify(activity.changes);
|
|
1872
2276
|
useEffect(() => {
|
|
@@ -1976,20 +2380,16 @@ function GitReviewPanelSession({ sessionId, sendSessionFrame }) {
|
|
|
1976
2380
|
if (!open) {
|
|
1977
2381
|
setDraft(void 0);
|
|
1978
2382
|
setSelection(void 0);
|
|
1979
|
-
setComment("");
|
|
1980
2383
|
}
|
|
1981
2384
|
},
|
|
1982
|
-
children: /* @__PURE__ */ jsx(SheetContent, { children: /* @__PURE__ */ jsx(SheetBody, { children: /* @__PURE__ */ jsx(
|
|
2385
|
+
children: /* @__PURE__ */ jsx(SheetContent, { children: /* @__PURE__ */ jsx(SheetBody, { children: /* @__PURE__ */ jsx(CommentEditor, {
|
|
1983
2386
|
range: draft?.range,
|
|
1984
2387
|
snippet: draft?.snippet ?? "",
|
|
1985
|
-
value: comment,
|
|
1986
|
-
onChange: setComment,
|
|
1987
2388
|
onCancel: () => {
|
|
1988
2389
|
setDraft(void 0);
|
|
1989
2390
|
setSelection(void 0);
|
|
1990
|
-
setComment("");
|
|
1991
2391
|
},
|
|
1992
|
-
onAdd: () => {
|
|
2392
|
+
onAdd: (comment) => {
|
|
1993
2393
|
if (!draft || !comment.trim()) return;
|
|
1994
2394
|
addReviewComment(sessionId, {
|
|
1995
2395
|
path: draft.path,
|
|
@@ -2000,9 +2400,8 @@ function GitReviewPanelSession({ sessionId, sendSessionFrame }) {
|
|
|
2000
2400
|
});
|
|
2001
2401
|
setDraft(void 0);
|
|
2002
2402
|
setSelection(void 0);
|
|
2003
|
-
setComment("");
|
|
2004
2403
|
}
|
|
2005
|
-
}) }) })
|
|
2404
|
+
}, draft ? "open" : "closed") }) })
|
|
2006
2405
|
}),
|
|
2007
2406
|
/* @__PURE__ */ jsx(Sheet, {
|
|
2008
2407
|
open: confirm !== void 0,
|
|
@@ -2034,6 +2433,15 @@ function GitReviewPanelSession({ sessionId, sendSessionFrame }) {
|
|
|
2034
2433
|
})
|
|
2035
2434
|
] });
|
|
2036
2435
|
}
|
|
2436
|
+
function CommentEditor({ onAdd, ...props }) {
|
|
2437
|
+
const [body, setBody] = useState("");
|
|
2438
|
+
return /* @__PURE__ */ jsx(CommentSheet, {
|
|
2439
|
+
...props,
|
|
2440
|
+
value: body,
|
|
2441
|
+
onChange: setBody,
|
|
2442
|
+
onAdd: () => onAdd(body)
|
|
2443
|
+
});
|
|
2444
|
+
}
|
|
2037
2445
|
//#endregion
|
|
2038
2446
|
//#region src/extensions/workspaces/sessions/(frontend)/fill/_components/WorktreesActivitySection.native.tsx
|
|
2039
2447
|
function GitChangesRow({ state = "changes", branch = "Detached HEAD", added = 0, removed = 0, onReview }) {
|
|
@@ -2241,6 +2649,12 @@ const via = (identity, value) => value === void 0 ? void 0 : {
|
|
|
2241
2649
|
};
|
|
2242
2650
|
const nativePlugin = defineNativePlugin({
|
|
2243
2651
|
id: "git",
|
|
2652
|
+
workspace: { get fills() {
|
|
2653
|
+
return [via({
|
|
2654
|
+
slot: "new-session",
|
|
2655
|
+
id: "git-new-session"
|
|
2656
|
+
}, GitNewSession_new_session_native_default)];
|
|
2657
|
+
} },
|
|
2244
2658
|
session: {
|
|
2245
2659
|
get activityGroups() {
|
|
2246
2660
|
return [via({ name: "git" }, git_native_default)];
|