@agimon-ai/po-git 0.0.0-alpha.3 → 0.0.0-alpha.31

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.
@@ -1,10 +1,11 @@
1
1
  import { createApiClient, defineActivityGroup, defineChannelFile, defineFill, defineGlobalStore, defineNativePlugin, defineSessionStore, defineWebLifecycle, nativeApiTransport, sessionApiAddress } from "@agimon-ai/po-core/native";
2
- import { BranchIcon, Button, Dot, Input, ListRow, Sheet, SheetBody, SheetContent, SheetHeader, SheetTitle, Textarea, fontOf } from "@agimon-ai/po-native-components";
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
3
  import { createElement, useCallback, useEffect, useRef, useState, useSyncExternalStore } from "react";
4
4
  import { View } from "react-native-unistyles/components/native/View";
5
5
  import { Text } from "react-native-unistyles/components/native/Text";
6
6
  import { ScrollView } from "react-native-unistyles/components/native/ScrollView";
7
- import { StyleSheet } from "react-native-unistyles";
7
+ import { Pressable } from "react-native-unistyles/components/native/Pressable";
8
+ import { StyleSheet, useUnistyles } from "react-native-unistyles";
8
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
10
  //#region src/types/webWorktrees.ts
10
11
  /**
@@ -43,6 +44,15 @@ const GIT_WORKTREES_TYPE = "git_worktrees";
43
44
  * - `sendSessionFrame` for a command. It envelopes the frame as a session
44
45
  * command bound for the agent, which never reaches a channel's `receive`.
45
46
  */
47
+ /** Presentation state follows hub operation labels, including session startup progress. */
48
+ function worktreesPresentation(state) {
49
+ return {
50
+ working: state.worktrees.filter((item) => !item.orphaned && !item.unowned),
51
+ attention: state.worktrees.filter((item) => item.orphaned || item.unowned),
52
+ starting: state.requestedBranch !== void 0 ? !state.error && !state.worktrees.some((item) => item.branch === state.requestedBranch) : !!state.pending && /^(creating|starting session)/i.test(state.pending),
53
+ closeErrorId: state.errorTarget?.action === "close" ? state.errorTarget.id : void 0
54
+ };
55
+ }
46
56
  const worktreeActivity = defineSessionStore({
47
57
  worktrees: [],
48
58
  pending: void 0,
@@ -117,8 +127,12 @@ const worktreesChannel = worktreeActivity.channel({
117
127
  errorTarget: optionalErrorTarget$1(input.errorTarget)
118
128
  };
119
129
  },
120
- reduce(_current, next) {
121
- return next;
130
+ reduce(current, next) {
131
+ const requestedBranch = current.requestedBranch;
132
+ return {
133
+ ...next,
134
+ ...requestedBranch && next.pending && !next.error ? { requestedBranch } : {}
135
+ };
122
136
  }
123
137
  });
124
138
  /** Sends one command to the session's worktrees channel. */
@@ -132,6 +146,13 @@ function command(sessionId, payload) {
132
146
  /** Asks the hub for a worktree. The hub answers on the same channel. */
133
147
  function requestWorktreeCreate(sessionId, branch, baseRef, reservationId) {
134
148
  const trimmedBase = baseRef.trim();
149
+ if (worktreeSender.store.state) worktreeActivity.update(sessionId, (current) => ({
150
+ ...current,
151
+ requestedBranch: branch.trim(),
152
+ pending: "creating",
153
+ error: void 0,
154
+ errorTarget: void 0
155
+ }));
135
156
  command(sessionId, {
136
157
  action: "create",
137
158
  branch: branch.trim(),
@@ -162,137 +183,507 @@ function worktreesTab() {
162
183
  panel: WorktreesPanel
163
184
  };
164
185
  }
165
- function WorktreesPanel({ sessionId, sessionReservationId, openSession }) {
166
- const state = useWorktrees(sessionId);
167
- const [creating, setCreating] = useState(false);
168
- const [branch, setBranch] = useState("");
169
- const [baseRef, setBaseRef] = useState("");
170
- const [closing, setClosing] = useState();
171
- const busy = state.pending !== void 0;
172
- return /* @__PURE__ */ jsxs(ScrollView, {
173
- contentContainerStyle: styles$1.body,
186
+ function WorktreesHeader({ activity = false, onNew, disabled = false }) {
187
+ return /* @__PURE__ */ jsxs(View, {
188
+ style: styles$2.header,
189
+ children: [/* @__PURE__ */ jsx(Text, {
190
+ style: styles$2.title,
191
+ children: activity ? "Activity" : "Worktrees"
192
+ }), !activity ? /* @__PURE__ */ jsx(Button, {
193
+ testID: "git-worktree-create",
194
+ variant: "ghost",
195
+ style: styles$2.newPill,
196
+ disabled,
197
+ onPress: onNew,
198
+ children: /* @__PURE__ */ jsx(Text, {
199
+ style: styles$2.jade,
200
+ children: "+ New"
201
+ })
202
+ }) : null]
203
+ });
204
+ }
205
+ function WorktreeRow({ item, error, compact = false, onClose, onOpen, disabled = false }) {
206
+ return /* @__PURE__ */ jsxs(View, {
207
+ style: styles$2.worktree,
174
208
  children: [
175
- /* @__PURE__ */ jsx(Button, {
176
- testID: "git-worktree-create",
177
- disabled: sessionId === null || busy,
178
- onPress: () => setCreating(true),
179
- children: "Create a worktree"
180
- }),
181
- state.pending ? /* @__PURE__ */ jsx(Text, {
182
- style: styles$1.meta,
183
- children: state.pending
184
- }) : null,
185
- state.error ? /* @__PURE__ */ jsx(Text, {
186
- style: styles$1.error,
187
- children: state.error
188
- }) : null,
189
- state.worktrees.map((worktree) => /* @__PURE__ */ jsxs(View, {
190
- style: styles$1.card,
209
+ /* @__PURE__ */ jsxs(View, {
210
+ style: styles$2.line,
191
211
  children: [
192
- /* @__PURE__ */ jsx(ListRow, {
193
- title: worktree.branch,
194
- description: `${worktree.path}${worktree.orphaned ? "\nOrphaned" : ""}${worktree.unowned ? "\nUnowned" : ""}`
212
+ /* @__PURE__ */ jsx(Dot, {
213
+ tone: item.orphaned ? "yellow" : item.unowned ? "blue" : "green",
214
+ size: "md"
195
215
  }),
196
- worktree.sessionId && openSession ? /* @__PURE__ */ jsx(Button, {
197
- variant: "outline",
198
- onPress: () => openSession(worktree.sessionId),
199
- children: "Open session"
216
+ /* @__PURE__ */ jsxs(Pressable, {
217
+ style: styles$2.grow,
218
+ accessibilityRole: onOpen ? "button" : void 0,
219
+ accessibilityLabel: onOpen ? `Open session for ${item.branch}` : void 0,
220
+ onPress: onOpen,
221
+ disabled: !onOpen,
222
+ children: [/* @__PURE__ */ jsx(Text, {
223
+ numberOfLines: 1,
224
+ ellipsizeMode: "middle",
225
+ style: styles$2.branch,
226
+ children: compact && item.branch.length > 36 ? `${item.branch.slice(0, 19)}…${item.branch.slice(-14)}` : item.branch
227
+ }), !compact ? /* @__PURE__ */ jsx(Text, {
228
+ style: styles$2.dim,
229
+ children: item.orphaned ? "Its folder is missing" : item.unowned ? "Its session has ended" : "Session running"
230
+ }) : null]
231
+ }),
232
+ item.orphaned ? /* @__PURE__ */ jsx(Badge, {
233
+ tone: "yellow",
234
+ style: styles$2.caution,
235
+ children: "Orphaned"
236
+ }) : null,
237
+ item.unowned ? /* @__PURE__ */ jsx(Badge, {
238
+ tone: "blue",
239
+ style: styles$2.info,
240
+ children: "Unowned"
200
241
  }) : null,
201
- /* @__PURE__ */ jsx(Button, {
202
- variant: "danger-outline",
203
- disabled: sessionId === null || busy,
204
- testID: `git-worktree-close-${worktree.id}`,
205
- onPress: () => setClosing(worktree.id),
206
- children: "Close worktree"
242
+ !compact ? /* @__PURE__ */ jsx(Button, {
243
+ variant: "ghost",
244
+ style: styles$2.more,
245
+ accessibilityLabel: `Close ${item.branch}`,
246
+ testID: `git-worktree-close-${item.id}`,
247
+ onPress: onClose,
248
+ disabled,
249
+ children: "⋮"
250
+ }) : /* @__PURE__ */ jsx(ChevronRightIcon, {
251
+ size: 16,
252
+ color: "dim"
207
253
  })
208
254
  ]
209
- }, worktree.id)),
210
- state.worktrees.length === 0 ? /* @__PURE__ */ jsx(Text, {
211
- style: styles$1.meta,
212
- children: "No worktrees yet. A worktree is its own checkout, branch and session."
255
+ }),
256
+ !compact ? /* @__PURE__ */ jsx(Text, {
257
+ numberOfLines: 1,
258
+ ellipsizeMode: "middle",
259
+ style: styles$2.path,
260
+ children: item.path
213
261
  }) : null,
214
- /* @__PURE__ */ jsx(Sheet, {
215
- open: creating,
216
- onOpenChange: setCreating,
217
- children: /* @__PURE__ */ jsxs(SheetContent, { children: [/* @__PURE__ */ jsx(SheetHeader, { children: /* @__PURE__ */ jsx(SheetTitle, { children: "Create a worktree" }) }), /* @__PURE__ */ jsx(SheetBody, { children: /* @__PURE__ */ jsxs(View, {
218
- style: styles$1.body,
219
- children: [
220
- /* @__PURE__ */ jsx(Input, {
221
- accessibilityLabel: "Branch",
222
- placeholder: "wt/fix-auth",
223
- value: branch,
224
- onChangeText: setBranch
225
- }),
226
- /* @__PURE__ */ jsx(Input, {
227
- accessibilityLabel: "Base ref",
228
- placeholder: "Base (current)",
229
- value: baseRef,
230
- onChangeText: setBaseRef
262
+ error ? /* @__PURE__ */ jsx(Text, {
263
+ accessibilityRole: "alert",
264
+ style: styles$2.error,
265
+ children: error
266
+ }) : null
267
+ ]
268
+ });
269
+ }
270
+ function WorktreeStarting({ branch }) {
271
+ return /* @__PURE__ */ jsxs(View, {
272
+ style: styles$2.starting,
273
+ children: [/* @__PURE__ */ jsx(Spinner, { label: "Starting session..." }), /* @__PURE__ */ jsxs(View, {
274
+ style: styles$2.grow,
275
+ children: [branch ? /* @__PURE__ */ jsx(Text, {
276
+ numberOfLines: 1,
277
+ ellipsizeMode: "middle",
278
+ style: styles$2.branch,
279
+ children: branch
280
+ }) : null, /* @__PURE__ */ jsx(Text, {
281
+ style: styles$2.dim,
282
+ children: "Starting session..."
283
+ })]
284
+ })]
285
+ });
286
+ }
287
+ function WorktreeGroup({ title, count }) {
288
+ return /* @__PURE__ */ jsxs(View, {
289
+ style: styles$2.group,
290
+ children: [/* @__PURE__ */ jsx(Text, {
291
+ style: styles$2.heading,
292
+ children: title
293
+ }), /* @__PURE__ */ jsx(Text, {
294
+ style: styles$2.dim,
295
+ children: count
296
+ })]
297
+ });
298
+ }
299
+ function WorktreeForm({ close = false, branch = "", baseRef = "", onBranch, onBaseRef, onConfirm, onCancel, disabled = false, framed = true }) {
300
+ return /* @__PURE__ */ jsxs(View, {
301
+ style: [styles$2.sheet, !framed && styles$2.unframed],
302
+ children: [
303
+ framed ? /* @__PURE__ */ jsx(View, { style: styles$2.handle }) : null,
304
+ /* @__PURE__ */ jsx(Text, {
305
+ style: styles$2.sheetTitle,
306
+ children: close ? "Close this worktree?" : "New worktree"
307
+ }),
308
+ close ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Text, {
309
+ style: styles$2.branch,
310
+ children: branch
311
+ }), /* @__PURE__ */ jsx(Text, {
312
+ style: styles$2.body,
313
+ children: "Removes the checkout and stops its session."
314
+ })] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
315
+ /* @__PURE__ */ jsx(Text, {
316
+ style: styles$2.body,
317
+ children: "A separate checkout on its own branch, with its own session. This session stays where it is."
318
+ }),
319
+ /* @__PURE__ */ jsx(Text, {
320
+ style: styles$2.label,
321
+ children: "Branch"
322
+ }),
323
+ /* @__PURE__ */ jsx(Input, {
324
+ accessibilityLabel: "Branch",
325
+ value: branch,
326
+ onChangeText: onBranch,
327
+ style: styles$2.monoInput
328
+ }),
329
+ /* @__PURE__ */ jsx(Text, {
330
+ style: styles$2.label,
331
+ children: "Start from"
332
+ }),
333
+ /* @__PURE__ */ jsx(Input, {
334
+ accessibilityLabel: "Start from",
335
+ value: baseRef,
336
+ onChangeText: onBaseRef,
337
+ placeholder: "Current branch"
338
+ })
339
+ ] }),
340
+ /* @__PURE__ */ jsx(Button, {
341
+ onPress: onConfirm,
342
+ disabled: disabled || !close && branch.trim() === "",
343
+ variant: close ? "danger" : "primary",
344
+ style: [styles$2.fullButton, !close && styles$2.createButton],
345
+ children: close ? "Close worktree" : /* @__PURE__ */ jsx(Text, {
346
+ style: styles$2.createLabel,
347
+ children: "Create worktree"
348
+ })
349
+ }),
350
+ /* @__PURE__ */ jsx(Button, {
351
+ variant: "outline",
352
+ style: styles$2.fullButton,
353
+ onPress: onCancel,
354
+ children: "Cancel"
355
+ })
356
+ ]
357
+ });
358
+ }
359
+ function WorktreesView({ state, disabled = false, onNew, onClose, onOpen }) {
360
+ const { working, attention, starting, closeErrorId } = worktreesPresentation(state);
361
+ const row = (item) => /* @__PURE__ */ jsx(WorktreeRow, {
362
+ item,
363
+ disabled: disabled || !!state.pending,
364
+ onClose: () => onClose?.(item),
365
+ onOpen: item.sessionId && onOpen ? () => onOpen(item.sessionId) : void 0,
366
+ error: state.errorTarget?.action === "close" && state.errorTarget.id === item.id ? state.error : void 0
367
+ }, item.id);
368
+ return /* @__PURE__ */ jsxs(View, {
369
+ style: styles$2.screen,
370
+ children: [
371
+ /* @__PURE__ */ jsx(WorktreesHeader, {
372
+ disabled: disabled || !!state.pending,
373
+ onNew
374
+ }),
375
+ state.worktrees.length || starting ? /* @__PURE__ */ jsxs(View, {
376
+ style: styles$2.content,
377
+ children: [
378
+ /* @__PURE__ */ jsx(WorktreeGroup, {
379
+ title: "Working",
380
+ count: `${working.length} open${starting ? " · 1 starting" : ""}`
381
+ }),
382
+ working.map(row),
383
+ starting ? /* @__PURE__ */ jsx(WorktreeStarting, { branch: state.requestedBranch }) : null,
384
+ attention.length ? /* @__PURE__ */ jsxs(Fragment, { children: [
385
+ /* @__PURE__ */ jsx(WorktreeGroup, {
386
+ title: "Needs a look",
387
+ count: `${attention.length} open`
231
388
  }),
232
- /* @__PURE__ */ jsx(Text, {
233
- style: styles$1.meta,
234
- children: "Creates the worktree outside the repository and starts a session nested under this one."
389
+ /* @__PURE__ */ jsxs(Text, {
390
+ style: styles$2.dim,
391
+ children: [
392
+ attention.length === 2 ? "Two worktrees" : attention.length === 1 ? "One worktree" : `${attention.length} worktrees`,
393
+ " ",
394
+ "would like your eye, whenever you are ready."
395
+ ]
235
396
  }),
236
- /* @__PURE__ */ jsx(Button, {
237
- disabled: sessionId === null || busy || branch.trim() === "",
238
- onPress: () => {
239
- if (sessionId === null) return;
240
- requestWorktreeCreate(sessionId, branch, baseRef, sessionReservationId);
241
- setCreating(false);
242
- if (sessionReservationId === void 0) {
243
- setBranch("");
244
- setBaseRef("");
245
- }
246
- },
247
- children: "Create"
248
- })
249
- ]
250
- }) })] })
397
+ attention.map(row)
398
+ ] }) : null
399
+ ]
400
+ }) : /* @__PURE__ */ jsxs(View, {
401
+ style: styles$2.empty,
402
+ children: [/* @__PURE__ */ jsx(Text, {
403
+ style: styles$2.heading,
404
+ children: "No worktrees yet"
405
+ }), /* @__PURE__ */ jsx(Text, {
406
+ style: styles$2.emptyCopy,
407
+ children: "Create a worktree to run a separate session on its own branch."
408
+ })]
251
409
  }),
252
- /* @__PURE__ */ jsx(Sheet, {
253
- open: closing !== void 0,
254
- onOpenChange: (open) => {
255
- if (!open) setClosing(void 0);
256
- },
257
- children: /* @__PURE__ */ jsxs(SheetContent, { children: [/* @__PURE__ */ jsx(SheetHeader, { children: /* @__PURE__ */ jsx(SheetTitle, { children: "Close this worktree?" }) }), /* @__PURE__ */ jsxs(SheetBody, { children: [/* @__PURE__ */ jsx(Text, {
258
- style: styles$1.meta,
259
- children: "The checkout is removed. Git refuses a dirty worktree."
260
- }), /* @__PURE__ */ jsx(Button, {
261
- variant: "danger-outline",
262
- disabled: busy,
263
- onPress: () => {
264
- if (sessionId !== null && closing !== void 0) requestWorktreeClose(sessionId, closing);
265
- setClosing(void 0);
266
- },
267
- children: "Close worktree"
268
- })] })] })
269
- })
410
+ state.pending && !starting ? /* @__PURE__ */ jsx(Text, {
411
+ style: styles$2.dim,
412
+ children: state.pending
413
+ }) : null,
414
+ state.error && (!closeErrorId || !state.worktrees.some((item) => item.id === closeErrorId)) ? /* @__PURE__ */ jsx(Text, {
415
+ accessibilityRole: "alert",
416
+ style: styles$2.error,
417
+ children: state.error
418
+ }) : null
270
419
  ]
271
420
  });
272
421
  }
273
- const styles$1 = StyleSheet.create((theme) => ({
274
- body: {
275
- padding: theme.spacing(3),
422
+ function WorktreesPanel({ sessionId, sessionReservationId, openSession }) {
423
+ const state = useWorktrees(sessionId);
424
+ const [creating, setCreating] = useState(false);
425
+ const [branch, setBranch] = useState("");
426
+ const [baseRef, setBaseRef] = useState("");
427
+ const [closing, setClosing] = useState();
428
+ const busy = sessionId === null || state.pending !== void 0;
429
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
430
+ /* @__PURE__ */ jsx(ScrollView, {
431
+ contentContainerStyle: styles$2.scroll,
432
+ children: /* @__PURE__ */ jsx(WorktreesView, {
433
+ state,
434
+ disabled: sessionId === null,
435
+ onNew: () => setCreating(true),
436
+ onClose: setClosing,
437
+ onOpen: openSession
438
+ })
439
+ }),
440
+ /* @__PURE__ */ jsx(Sheet, {
441
+ open: creating,
442
+ onOpenChange: setCreating,
443
+ children: /* @__PURE__ */ jsx(SheetContent, { children: /* @__PURE__ */ jsx(WorktreeForm, {
444
+ framed: false,
445
+ branch,
446
+ baseRef,
447
+ onBranch: setBranch,
448
+ onBaseRef: setBaseRef,
449
+ disabled: busy,
450
+ onCancel: () => setCreating(false),
451
+ onConfirm: () => {
452
+ if (sessionId === null) return;
453
+ requestWorktreeCreate(sessionId, branch, baseRef, sessionReservationId);
454
+ setCreating(false);
455
+ if (sessionReservationId === void 0) {
456
+ setBranch("");
457
+ setBaseRef("");
458
+ }
459
+ }
460
+ }) })
461
+ }),
462
+ /* @__PURE__ */ jsx(Sheet, {
463
+ open: !!closing,
464
+ onOpenChange: (open) => {
465
+ if (!open) setClosing(void 0);
466
+ },
467
+ children: /* @__PURE__ */ jsx(SheetContent, { children: /* @__PURE__ */ jsx(WorktreeForm, {
468
+ framed: false,
469
+ close: true,
470
+ branch: closing?.branch,
471
+ disabled: busy,
472
+ onCancel: () => setClosing(void 0),
473
+ onConfirm: () => {
474
+ if (sessionId !== null && closing) requestWorktreeClose(sessionId, closing.id);
475
+ setClosing(void 0);
476
+ }
477
+ }) })
478
+ })
479
+ ] });
480
+ }
481
+ const styles$2 = StyleSheet.create((theme) => ({
482
+ screen: {
483
+ flex: 1,
484
+ backgroundColor: theme.colors.rail,
485
+ uni__dependencies: [0]
486
+ },
487
+ scroll: { flexGrow: 1 },
488
+ header: {
489
+ flexDirection: "row",
490
+ alignItems: "center",
491
+ justifyContent: "space-between",
492
+ padding: theme.spacing(5),
493
+ minHeight: 88,
494
+ uni__dependencies: [0]
495
+ },
496
+ title: {
497
+ fontFamily: fontOf(theme.fontFamily.sans),
498
+ fontSize: theme.fontSize["2xl"],
499
+ fontWeight: "600",
500
+ color: theme.colors.hi,
501
+ uni__dependencies: [0]
502
+ },
503
+ newPill: {
504
+ backgroundColor: theme.colors["tint-green"],
505
+ borderRadius: theme.borderRadius.full,
506
+ minHeight: 44,
507
+ paddingHorizontal: theme.spacing(4),
508
+ uni__dependencies: [0]
509
+ },
510
+ content: {
511
+ paddingHorizontal: theme.spacing(5),
276
512
  gap: theme.spacing(2),
277
513
  uni__dependencies: [0]
278
514
  },
279
- card: {
280
- borderWidth: 1,
515
+ group: {
516
+ flexDirection: "row",
517
+ alignItems: "center",
518
+ gap: theme.spacing(3),
519
+ paddingTop: theme.spacing(4),
520
+ paddingBottom: theme.spacing(2),
521
+ borderBottomWidth: StyleSheet.hairlineWidth,
281
522
  borderColor: theme.colors["border-soft"],
282
- borderRadius: theme.borderRadius.md,
283
- backgroundColor: theme.colors.panel,
284
- padding: theme.spacing(2),
285
- gap: theme.spacing(1),
286
523
  uni__dependencies: [0]
287
524
  },
288
- meta: {
525
+ heading: {
526
+ fontFamily: fontOf(theme.fontFamily.sans),
527
+ fontSize: theme.fontSize.lg,
528
+ lineHeight: theme.lineHeight.lg,
529
+ fontWeight: "600",
530
+ color: theme.colors.hi,
531
+ uni__dependencies: [0]
532
+ },
533
+ dim: {
534
+ fontFamily: fontOf(theme.fontFamily.sans),
535
+ fontSize: theme.fontSize.sm,
536
+ lineHeight: theme.lineHeight.sm,
537
+ color: theme.colors.dim,
538
+ uni__dependencies: [0]
539
+ },
540
+ body: {
541
+ fontFamily: fontOf(theme.fontFamily.sans),
542
+ fontSize: theme.fontSize.base,
543
+ lineHeight: theme.lineHeight.lg,
289
544
  color: theme.colors.dim,
545
+ uni__dependencies: [0]
546
+ },
547
+ label: {
548
+ fontFamily: fontOf(theme.fontFamily.sans),
549
+ fontSize: theme.fontSize.base,
550
+ fontWeight: "500",
551
+ color: theme.colors.text,
552
+ marginTop: theme.spacing(2),
553
+ uni__dependencies: [0]
554
+ },
555
+ branch: {
556
+ fontFamily: fontOf(theme.fontFamily.mono),
557
+ fontSize: theme.fontSize.base,
558
+ lineHeight: theme.lineHeight.lg,
559
+ color: theme.colors.hi,
560
+ uni__dependencies: [0]
561
+ },
562
+ path: {
563
+ fontFamily: fontOf(theme.fontFamily.mono),
290
564
  fontSize: theme.fontSize.sm,
565
+ lineHeight: theme.lineHeight.sm,
566
+ color: theme.colors.dim,
567
+ marginLeft: theme.spacing(4.5),
568
+ uni__dependencies: [0]
569
+ },
570
+ worktree: {
571
+ gap: theme.spacing(1),
572
+ paddingVertical: theme.spacing(2),
573
+ borderBottomWidth: StyleSheet.hairlineWidth,
574
+ borderColor: theme.colors["border-soft"],
575
+ uni__dependencies: [0]
576
+ },
577
+ line: {
578
+ flexDirection: "row",
579
+ alignItems: "center",
580
+ gap: theme.spacing(2),
581
+ minHeight: 44,
582
+ uni__dependencies: [0]
583
+ },
584
+ grow: {
585
+ flex: 1,
586
+ minWidth: 0,
587
+ minHeight: 44,
588
+ justifyContent: "center",
589
+ gap: theme.spacing(1),
590
+ uni__dependencies: [0]
591
+ },
592
+ more: {
593
+ width: 44,
594
+ height: 44,
595
+ padding: 0
596
+ },
597
+ caution: {
598
+ backgroundColor: theme.colors["tint-yellow"],
599
+ uni__dependencies: [0]
600
+ },
601
+ info: {
602
+ backgroundColor: theme.colors["tint-blue"],
603
+ uni__dependencies: [0]
604
+ },
605
+ starting: {
606
+ flexDirection: "row",
607
+ alignItems: "center",
608
+ gap: theme.spacing(2),
609
+ minHeight: 52,
610
+ uni__dependencies: [0]
611
+ },
612
+ jade: {
613
+ fontFamily: fontOf(theme.fontFamily.sans),
614
+ fontSize: theme.fontSize.base,
615
+ lineHeight: theme.lineHeight.base,
616
+ color: theme.colors.green,
617
+ fontWeight: "500",
291
618
  uni__dependencies: [0]
292
619
  },
293
620
  error: {
294
- color: theme.colors.red,
621
+ fontFamily: fontOf(theme.fontFamily.sans),
295
622
  fontSize: theme.fontSize.sm,
623
+ lineHeight: theme.lineHeight.sm,
624
+ color: theme.colors.red,
625
+ uni__dependencies: [0]
626
+ },
627
+ unframed: { borderTopWidth: 0 },
628
+ sheet: {
629
+ backgroundColor: theme.colors.bg,
630
+ borderTopWidth: 1,
631
+ borderColor: theme.colors.border,
632
+ borderTopLeftRadius: theme.borderRadius.lg,
633
+ borderTopRightRadius: theme.borderRadius.lg,
634
+ padding: theme.spacing(5),
635
+ gap: theme.spacing(3),
636
+ uni__dependencies: [0]
637
+ },
638
+ handle: {
639
+ alignSelf: "center",
640
+ width: 40,
641
+ height: 4,
642
+ borderRadius: theme.borderRadius.full,
643
+ backgroundColor: theme.colors.border,
644
+ marginBottom: theme.spacing(2),
645
+ uni__dependencies: [0]
646
+ },
647
+ sheetTitle: {
648
+ fontFamily: fontOf(theme.fontFamily.sans),
649
+ fontSize: theme.fontSize.xl,
650
+ fontWeight: "600",
651
+ color: theme.colors.hi,
652
+ uni__dependencies: [0]
653
+ },
654
+ monoInput: {
655
+ fontFamily: fontOf(theme.fontFamily.mono),
656
+ uni__dependencies: [0]
657
+ },
658
+ fullButton: {
659
+ height: 44,
660
+ alignSelf: "stretch"
661
+ },
662
+ createButton: {
663
+ backgroundColor: theme.colors.green,
664
+ uni__dependencies: [0]
665
+ },
666
+ createLabel: {
667
+ fontFamily: fontOf(theme.fontFamily.sans),
668
+ fontSize: theme.fontSize.base,
669
+ fontWeight: "600",
670
+ color: theme.colors.bg,
671
+ uni__dependencies: [0]
672
+ },
673
+ empty: {
674
+ flex: 1,
675
+ alignItems: "center",
676
+ justifyContent: "center",
677
+ padding: theme.spacing(8),
678
+ gap: theme.spacing(3),
679
+ uni__dependencies: [0]
680
+ },
681
+ emptyCopy: {
682
+ fontFamily: fontOf(theme.fontFamily.sans),
683
+ fontSize: theme.fontSize.base,
684
+ lineHeight: theme.lineHeight.lg,
685
+ color: theme.colors.dim,
686
+ textAlign: "center",
296
687
  uni__dependencies: [0]
297
688
  }
298
689
  }));
@@ -570,6 +961,31 @@ async function fetchReviewFile(sessionId, path, signal) {
570
961
  };
571
962
  }
572
963
  //#endregion
964
+ //#region src/extensions/workspaces/sessions/(frontend)/_lib/gitReviewSelection.ts
965
+ /** A different side starts a fresh selection. The second same-side tap completes it. */
966
+ function selectReviewLine(anchor, row) {
967
+ const side = row.marker === "-" ? "old" : "new";
968
+ if (!anchor || anchor.side !== side) return {
969
+ range: {
970
+ side,
971
+ startLine: row.line,
972
+ endLine: row.line
973
+ },
974
+ complete: false
975
+ };
976
+ return {
977
+ range: {
978
+ side,
979
+ startLine: Math.min(anchor.startLine, row.line),
980
+ endLine: Math.max(anchor.startLine, row.line)
981
+ },
982
+ complete: true
983
+ };
984
+ }
985
+ function reviewRangeRows(rows, range) {
986
+ return rows.filter((row) => (row.marker === "-" ? "old" : "new") === range.side && row.line >= range.startLine && row.line <= range.endLine);
987
+ }
988
+ //#endregion
573
989
  //#region src/extensions/workspaces/sessions/(frontend)/_lib/gitReviewStore.native.ts
574
990
  const gitReview = defineSessionStore({ comments: [] });
575
991
  function addReviewComment(sessionId, comment) {
@@ -615,6 +1031,23 @@ function conflictPrompt(branch, conflicts) {
615
1031
  ].join("\n");
616
1032
  }
617
1033
  /**
1034
+ * The one sync the bar fills in: pull while the branch is behind (a push would
1035
+ * be rejected), push while it is only ahead or has no upstream yet. Nothing
1036
+ * while a rebase is paused, detached, or already level.
1037
+ */
1038
+ function suggestedSyncAction(changes) {
1039
+ if (changes?.branch === void 0 || changes.rebase !== void 0) return void 0;
1040
+ if (changes.upstream === void 0) return "push";
1041
+ if (changes.upstream.behind > 0) return "pull";
1042
+ return changes.upstream.ahead > 0 ? "push" : void 0;
1043
+ }
1044
+ /** "2 to push, 1 new on origin/feat/git", or "Up to date with origin/feat/git". */
1045
+ function upstreamSummary(upstream) {
1046
+ if (upstream === void 0) return "No upstream yet";
1047
+ const parts = [...upstream.ahead > 0 ? [`${String(upstream.ahead)} to push`] : [], ...upstream.behind > 0 ? [`${String(upstream.behind)} new on ${upstream.ref}`] : []];
1048
+ return parts.length === 0 ? `Up to date with ${upstream.ref}` : parts.join(", ");
1049
+ }
1050
+ /**
618
1051
  * Hands one deferred follow-up to the session. Returns the reason it failed,
619
1052
  * or undefined once sent. The sender throws when the session is disconnected.
620
1053
  */
@@ -630,411 +1063,1164 @@ function deliverToAgent(send, sessionId, message) {
630
1063
  }
631
1064
  }
632
1065
  //#endregion
633
- //#region src/extensions/workspaces/sessions/(frontend)/_components/GitReviewPanel.native.tsx
634
- function useGitChanges(sessionId) {
635
- const subscribe = useCallback((listener) => {
636
- const sub = gitChanges.store.subscribe(listener);
637
- return () => sub.unsubscribe();
638
- }, []);
639
- return useSyncExternalStore(subscribe, () => gitChanges.select(gitChanges.store.state, sessionId));
1066
+ //#region src/extensions/workspaces/sessions/(frontend)/_components/GitReviewView.native.tsx
1067
+ function Action({ children, variant = "outline", disabled = false, onPress }) {
1068
+ const { theme } = useUnistyles();
1069
+ return /* @__PURE__ */ jsx(Button, {
1070
+ onPress,
1071
+ variant,
1072
+ disabled,
1073
+ style: [styles$1.action, variant === "primary" && styles$1.primary],
1074
+ children: variant === "primary" ? /* @__PURE__ */ jsx(Text, {
1075
+ style: [styles$1.primaryLabel, { color: theme.scheme === "dark" ? theme.colors.rail : theme.colors["on-selected"] }],
1076
+ children
1077
+ }) : children
1078
+ });
640
1079
  }
641
- function reviewTab() {
642
- return {
643
- id: "git-review",
644
- label: "review",
645
- panel: GitReviewPanel
646
- };
1080
+ function Footer({ count, discard = false, confirmation = false, onReview, onSend, onDiscard }) {
1081
+ return /* @__PURE__ */ jsxs(View, {
1082
+ style: styles$1.footer,
1083
+ children: [discard ? /* @__PURE__ */ jsx(Action, {
1084
+ variant: "ghost",
1085
+ onPress: onDiscard,
1086
+ children: "Discard"
1087
+ }) : /* @__PURE__ */ jsxs(Pressable, {
1088
+ accessibilityRole: "button",
1089
+ accessibilityLabel: "Review queued comments",
1090
+ onPress: onReview,
1091
+ style: [styles$1.inline, styles$1.action],
1092
+ children: [/* @__PURE__ */ jsx(MessageIcon, {
1093
+ size: 16,
1094
+ color: "blue"
1095
+ }), /* @__PURE__ */ jsxs(Text, {
1096
+ style: styles$1.dim,
1097
+ children: [
1098
+ count,
1099
+ " ",
1100
+ count === 1 ? "comment" : "comments",
1101
+ " queued"
1102
+ ]
1103
+ })]
1104
+ }), /* @__PURE__ */ jsx(Action, {
1105
+ variant: confirmation ? "outline" : "primary",
1106
+ disabled: count === 0,
1107
+ onPress: onSend,
1108
+ children: "Send review"
1109
+ })]
1110
+ });
647
1111
  }
648
- function GitReviewPanel({ sessionId, sendSessionFrame }) {
649
- const activity = useGitChanges(sessionId);
650
- const subscribe = useCallback((listener) => {
651
- const sub = gitReview.store.subscribe(listener);
652
- return () => sub.unsubscribe();
653
- }, []);
654
- const comments = useSyncExternalStore(subscribe, () => gitReview.select(gitReview.store.state, sessionId).comments);
655
- const [summary, setSummary] = useState();
656
- const [file, setFile] = useState();
657
- const [error, setError] = useState();
658
- const [dismissedError, setDismissedError] = useState();
659
- const [confirm, setConfirm] = useState();
660
- const [draft, setDraft] = useState();
661
- const [comment, setComment] = useState("");
662
- const [start, setStart] = useState("");
663
- const [end, setEnd] = useState("");
664
- const request = useRef(void 0);
665
- const fingerprint = JSON.stringify(activity.changes);
666
- useEffect(() => {
667
- if (sessionId === null) return void 0;
668
- const controller = new AbortController();
669
- fetchReviewSummary(sessionId, controller.signal).then((result) => {
670
- if (!controller.signal.aborted) {
671
- if (result.ok) setSummary(result.summary);
672
- else setError(result.error);
673
- }
674
- }).catch((cause) => {
675
- if (!controller.signal.aborted) setError(cause instanceof Error ? cause.message : String(cause));
676
- });
677
- return () => {
678
- controller.abort();
679
- request.current?.abort();
680
- };
681
- }, [sessionId, fingerprint]);
682
- const loadFile = async (path) => {
683
- if (sessionId === null) return;
684
- request.current?.abort();
685
- const controller = new AbortController();
686
- request.current = controller;
687
- setFile(void 0);
688
- setError(void 0);
689
- try {
690
- const result = await fetchReviewFile(sessionId, path, controller.signal);
691
- if (!controller.signal.aborted) {
692
- if (result.ok) setFile(result.diff);
693
- else setError(result.error);
694
- }
695
- } catch (cause) {
696
- if (!controller.signal.aborted) setError(cause instanceof Error ? cause.message : String(cause));
697
- }
698
- };
699
- if (sessionId === null) return null;
700
- const changes = activity.changes;
701
- const busy = activity.pending !== void 0;
702
- const paused = changes?.rebase !== void 0;
703
- const send = (message, onSent) => {
704
- const failure = deliverToAgent(sendSessionFrame, sessionId, message);
705
- setError(failure);
706
- if (failure === void 0) onSent?.();
707
- };
708
- const sync = (action) => requestGitSync(sessionId, action);
709
- const forceRequired = activity.error !== dismissedError && activity.errorTarget?.action === "push" && activity.errorTarget.forceRequired === true;
710
- return /* @__PURE__ */ jsxs(ScrollView, {
711
- contentContainerStyle: styles.body,
1112
+ function SyncCard({ changes, busy = false, onSync }) {
1113
+ const suggested = suggestedSyncAction(changes);
1114
+ return /* @__PURE__ */ jsxs(View, {
1115
+ style: styles$1.card,
712
1116
  children: [
713
- /* @__PURE__ */ jsx(Text, {
714
- style: styles.title,
715
- children: changes?.branch ?? "Detached HEAD"
716
- }),
717
- /* @__PURE__ */ jsxs(Text, {
718
- style: styles.meta,
719
- children: [
720
- changes?.base ? `vs ${changes.base}` : "Uncommitted changes",
721
- " · +",
722
- changes?.added ?? 0,
723
- " -",
724
- changes?.removed ?? 0,
725
- "\\n",
726
- changes?.upstream ? `↑${changes.upstream.ahead} ↓${changes.upstream.behind} ${changes.upstream.ref}` : "No upstream yet"
727
- ]
728
- }),
729
1117
  /* @__PURE__ */ jsxs(View, {
730
- style: styles.actions,
1118
+ style: styles$1.inline,
731
1119
  children: [
732
- /* @__PURE__ */ jsx(Button, {
733
- variant: "outline",
734
- disabled: busy || paused || changes?.upstream === void 0,
735
- onPress: () => sync("pull"),
736
- children: "Pull"
1120
+ /* @__PURE__ */ jsx(BranchIcon, {
1121
+ size: 18,
1122
+ color: "dim"
1123
+ }),
1124
+ /* @__PURE__ */ jsx(Text, {
1125
+ style: styles$1.branch,
1126
+ children: changes?.branch ?? "Detached HEAD"
737
1127
  }),
738
- /* @__PURE__ */ jsx(Button, {
739
- variant: "outline",
740
- disabled: busy || paused || changes?.branch === void 0,
741
- onPress: () => sync("push"),
742
- children: "Push"
1128
+ /* @__PURE__ */ jsx(View, { style: styles$1.grow }),
1129
+ /* @__PURE__ */ jsxs(Text, {
1130
+ style: styles$1.green,
1131
+ children: ["+", changes?.added ?? 0]
743
1132
  }),
744
- /* @__PURE__ */ jsx(Button, {
745
- variant: "outline",
746
- disabled: busy || paused || changes?.base === void 0,
747
- onPress: () => sync("rebase"),
748
- children: "Rebase"
1133
+ /* @__PURE__ */ jsxs(Text, {
1134
+ style: styles$1.red,
1135
+ children: ["-", changes?.removed ?? 0]
749
1136
  })
750
1137
  ]
751
1138
  }),
752
- activity.pending ? /* @__PURE__ */ jsx(Text, {
753
- style: styles.meta,
754
- children: activity.pending
755
- }) : null,
756
- activity.error && activity.error !== dismissedError ? /* @__PURE__ */ jsxs(View, { children: [/* @__PURE__ */ jsx(Text, {
757
- style: styles.error,
758
- children: activity.error
759
- }), /* @__PURE__ */ jsx(Button, {
760
- variant: "ghost",
761
- onPress: () => setDismissedError(activity.error),
762
- children: "Dismiss"
763
- })] }) : null,
764
- forceRequired ? /* @__PURE__ */ jsx(Button, {
765
- variant: "danger-outline",
1139
+ /* @__PURE__ */ jsx(Text, {
1140
+ style: [styles$1.dim, styles$1.mono],
1141
+ children: changes?.base ? `vs ${changes.base}` : "Uncommitted changes"
1142
+ }),
1143
+ /* @__PURE__ */ jsx(Text, {
1144
+ style: styles$1.dim,
1145
+ children: upstreamSummary(changes?.upstream)
1146
+ }),
1147
+ /* @__PURE__ */ jsx(View, {
1148
+ style: styles$1.inline,
1149
+ children: [
1150
+ "pull",
1151
+ "push",
1152
+ "rebase"
1153
+ ].map((action) => /* @__PURE__ */ jsx(Button, {
1154
+ variant: suggested === action ? "outline" : "ghost",
1155
+ style: suggested === action ? [styles$1.action, styles$1.suggested] : styles$1.action,
1156
+ disabled: busy || changes?.rebase !== void 0 || (action === "pull" ? changes?.upstream === void 0 : action === "push" ? changes?.branch === void 0 : changes?.base === void 0),
1157
+ onPress: () => onSync?.(action),
1158
+ children: action === "pull" ? "Pull" : action === "push" ? "Push" : "Rebase"
1159
+ }, action))
1160
+ })
1161
+ ]
1162
+ });
1163
+ }
1164
+ function FileList({ files, comments = [], onFile }) {
1165
+ return /* @__PURE__ */ jsxs(View, {
1166
+ style: styles$1.list,
1167
+ children: [/* @__PURE__ */ jsxs(Text, {
1168
+ style: styles$1.section,
1169
+ children: [files.length, " files"]
1170
+ }), files.map((file) => /* @__PURE__ */ jsxs(Pressable, {
1171
+ accessibilityRole: "button",
1172
+ testID: `git-review-file-${file.path}`,
1173
+ onPress: () => onFile?.(file.path),
1174
+ style: styles$1.fileRow,
1175
+ children: [
1176
+ /* @__PURE__ */ jsx(Text, {
1177
+ style: file.status === "added" ? styles$1.green : file.status === "untracked" ? styles$1.blue : styles$1.yellow,
1178
+ children: file.status[0]?.toUpperCase()
1179
+ }),
1180
+ /* @__PURE__ */ jsxs(View, {
1181
+ style: styles$1.grow,
1182
+ children: [/* @__PURE__ */ jsx(Text, {
1183
+ style: styles$1.body,
1184
+ children: file.path.split("/").at(-1)
1185
+ }), /* @__PURE__ */ jsx(Text, {
1186
+ numberOfLines: 1,
1187
+ style: [styles$1.dim, styles$1.mono],
1188
+ children: file.path.split("/").slice(0, -1).join("/") || "."
1189
+ })]
1190
+ }),
1191
+ /* @__PURE__ */ jsxs(Text, { children: [
1192
+ /* @__PURE__ */ jsxs(Text, {
1193
+ style: styles$1.green,
1194
+ children: ["+", file.added]
1195
+ }),
1196
+ " ",
1197
+ /* @__PURE__ */ jsxs(Text, {
1198
+ style: styles$1.red,
1199
+ children: ["-", file.removed]
1200
+ })
1201
+ ] }),
1202
+ comments.some((comment) => comment.path === file.path) ? /* @__PURE__ */ jsxs(View, {
1203
+ style: styles$1.inline,
1204
+ children: [/* @__PURE__ */ jsx(MessageIcon, {
1205
+ size: 12,
1206
+ color: "blue"
1207
+ }), /* @__PURE__ */ jsx(Text, {
1208
+ style: styles$1.blue,
1209
+ children: comments.filter((comment) => comment.path === file.path).length
1210
+ })]
1211
+ }) : null,
1212
+ /* @__PURE__ */ jsx(ChevronRightIcon, {
1213
+ size: 16,
1214
+ color: "dim"
1215
+ })
1216
+ ]
1217
+ }, file.path))]
1218
+ });
1219
+ }
1220
+ function RebaseCard({ confirmation = false, conflicts, busy = false, onResolve, onAbort }) {
1221
+ return /* @__PURE__ */ jsxs(View, {
1222
+ style: [styles$1.card, styles$1.caution],
1223
+ children: [
1224
+ /* @__PURE__ */ jsxs(View, {
1225
+ style: styles$1.inline,
1226
+ children: [/* @__PURE__ */ jsx(AlertIcon, {
1227
+ size: 20,
1228
+ color: "yellow"
1229
+ }), /* @__PURE__ */ jsx(Text, {
1230
+ style: styles$1.section,
1231
+ children: "Rebase paused"
1232
+ })]
1233
+ }),
1234
+ /* @__PURE__ */ jsx(Text, {
1235
+ style: styles$1.body,
1236
+ children: "Resolve conflicts or ask Po to do it for you."
1237
+ }),
1238
+ /* @__PURE__ */ jsx(Text, {
1239
+ style: [styles$1.dim, styles$1.mono],
1240
+ children: conflicts.join("\n")
1241
+ }),
1242
+ /* @__PURE__ */ jsx(Action, {
766
1243
  disabled: busy,
767
- onPress: () => setConfirm("force"),
768
- children: "Force push with lease…"
769
- }) : null,
770
- paused ? /* @__PURE__ */ jsxs(View, {
771
- style: styles.body,
1244
+ onPress: onResolve,
1245
+ variant: confirmation ? "outline" : "primary",
1246
+ children: "Ask Po to resolve"
1247
+ }),
1248
+ /* @__PURE__ */ jsx(Action, {
1249
+ disabled: busy,
1250
+ onPress: onAbort,
1251
+ variant: "danger-outline",
1252
+ children: "Abort rebase"
1253
+ })
1254
+ ]
1255
+ });
1256
+ }
1257
+ function FileHeader({ entry, index, total, onPrevious, onNext, onChanges }) {
1258
+ return /* @__PURE__ */ jsxs(View, {
1259
+ style: styles$1.content,
1260
+ children: [
1261
+ /* @__PURE__ */ jsx(Text, {
1262
+ style: styles$1.heading,
1263
+ children: entry.path.split("/").at(-1)
1264
+ }),
1265
+ /* @__PURE__ */ jsx(Text, {
1266
+ style: [styles$1.dim, styles$1.mono],
1267
+ children: entry.path.split("/").slice(0, -1).join("/") || "."
1268
+ }),
1269
+ /* @__PURE__ */ jsxs(View, {
1270
+ style: styles$1.inline,
772
1271
  children: [
773
1272
  /* @__PURE__ */ jsxs(Text, {
774
- style: styles.error,
775
- children: ["Rebase paused\\n", changes.rebase?.conflicts.join("\n")]
1273
+ style: styles$1.dim,
1274
+ children: [entry.status[0]?.toUpperCase(), entry.status.slice(1)]
776
1275
  }),
777
- /* @__PURE__ */ jsx(Button, {
778
- disabled: busy,
779
- onPress: () => send(conflictPrompt(changes.branch, changes.rebase?.conflicts ?? [])),
780
- children: "Ask agent to resolve"
1276
+ /* @__PURE__ */ jsxs(Text, {
1277
+ style: styles$1.green,
1278
+ children: ["+", entry.added]
781
1279
  }),
782
- /* @__PURE__ */ jsx(Button, {
783
- variant: "danger-outline",
784
- disabled: busy,
785
- onPress: () => setConfirm("abort"),
786
- children: "Abort rebase…"
1280
+ /* @__PURE__ */ jsxs(Text, {
1281
+ style: styles$1.red,
1282
+ children: ["-", entry.removed]
787
1283
  })
788
1284
  ]
789
- }) : null,
790
- error ? /* @__PURE__ */ jsx(Text, {
791
- style: styles.error,
792
- children: error
793
- }) : null,
794
- summary ? summary.files.map((entry) => /* @__PURE__ */ jsxs(Button, {
795
- variant: "ghost",
796
- testID: `git-review-file-${entry.path}`,
797
- onPress: () => void loadFile(entry.path),
798
- children: [
799
- entry.path,
800
- " · ",
801
- entry.status,
802
- " · +",
803
- entry.added,
804
- " -",
805
- entry.removed
806
- ]
807
- }, entry.path)) : /* @__PURE__ */ jsx(Text, {
808
- style: styles.meta,
809
- children: "Loading changes…"
810
1285
  }),
811
- summary?.files.length === 0 ? /* @__PURE__ */ jsx(Text, {
812
- style: styles.meta,
813
- children: summary.repository ? "No changed files" : "Not a Git checkout"
814
- }) : null,
815
- summary?.truncated ? /* @__PURE__ */ jsx(Text, {
816
- style: styles.meta,
817
- children: "The file list was truncated."
818
- }) : null,
819
- file ? /* @__PURE__ */ jsxs(View, {
820
- style: styles.body,
1286
+ /* @__PURE__ */ jsxs(View, {
1287
+ style: styles$1.navigation,
821
1288
  children: [
822
- /* @__PURE__ */ jsx(Text, {
823
- style: styles.title,
824
- children: file.path
1289
+ /* @__PURE__ */ jsx(Action, {
1290
+ variant: "ghost",
1291
+ disabled: index === 0,
1292
+ onPress: onPrevious,
1293
+ children: "Previous"
825
1294
  }),
826
- file.binary || file.tooLarge ? /* @__PURE__ */ jsx(Text, {
827
- style: styles.meta,
828
- children: file.binary ? "Binary file" : "Diff too large to display"
829
- }) : file.hunks.map((hunk, index) => /* @__PURE__ */ jsx(View, { children: hunk.rows.map((row, rowIndex) => /* @__PURE__ */ jsxs(Text, {
830
- selectable: true,
831
- style: styles.diff(row.marker),
832
- onPress: () => {
833
- setDraft({
834
- path: file.path,
835
- rows: hunk.rows
836
- });
837
- setStart(String(row.line));
838
- setEnd(String(row.line));
839
- },
840
- children: [
841
- row.marker,
842
- " ",
843
- row.line,
844
- " ",
845
- row.content
846
- ]
847
- }, rowIndex)) }, index)),
848
- /* @__PURE__ */ jsx(Button, {
849
- variant: "outline",
850
- onPress: () => {
851
- setDraft({
852
- path: file.path,
853
- rows: []
854
- });
855
- setStart("");
856
- setEnd("");
857
- },
858
- children: "Comment on file"
1295
+ /* @__PURE__ */ jsx(Pressable, {
1296
+ accessibilityRole: "button",
1297
+ accessibilityLabel: "Return to changes",
1298
+ onPress: onChanges,
1299
+ style: [styles$1.inline, styles$1.action],
1300
+ children: /* @__PURE__ */ jsxs(Text, {
1301
+ style: styles$1.dim,
1302
+ children: [
1303
+ onChanges ? "Changes · " : "",
1304
+ index + 1,
1305
+ " of ",
1306
+ total
1307
+ ]
1308
+ })
1309
+ }),
1310
+ /* @__PURE__ */ jsx(Action, {
1311
+ variant: "ghost",
1312
+ disabled: index === total - 1,
1313
+ onPress: onNext,
1314
+ children: "Next"
859
1315
  })
860
1316
  ]
1317
+ })
1318
+ ]
1319
+ });
1320
+ }
1321
+ function DiffRows({ rows, comments = [], selection, onLine, showInlineComments = true }) {
1322
+ return /* @__PURE__ */ jsx(View, { children: rows.map((row, i) => {
1323
+ const side = row.marker === "-" ? "old" : "new";
1324
+ const anchored = comments.filter((comment) => comment.side === side && (comment.endLine ?? comment.startLine) === row.line);
1325
+ return /* @__PURE__ */ jsxs(View, { children: [/* @__PURE__ */ jsxs(Pressable, {
1326
+ accessibilityRole: "button",
1327
+ accessibilityLabel: `Select ${side} line ${row.line}`,
1328
+ onPress: () => onLine?.(row),
1329
+ style: [
1330
+ styles$1.diffRow,
1331
+ row.marker === "+" && styles$1.added,
1332
+ row.marker === "-" && styles$1.removed,
1333
+ selection?.side === side && row.line >= selection.startLine && row.line <= selection.endLine && styles$1.selection
1334
+ ],
1335
+ children: [
1336
+ /* @__PURE__ */ jsx(Text, {
1337
+ style: [styles$1.mono, styles$1.gutter],
1338
+ children: row.line
1339
+ }),
1340
+ /* @__PURE__ */ jsx(Text, {
1341
+ style: [styles$1.dim, styles$1.mono],
1342
+ children: row.marker
1343
+ }),
1344
+ /* @__PURE__ */ jsx(SyntaxLine, {
1345
+ text: row.content || " ",
1346
+ style: [styles$1.mono, styles$1.code]
1347
+ }),
1348
+ anchored.length ? /* @__PURE__ */ jsx(MessageIcon, {
1349
+ size: 12,
1350
+ color: "blue"
1351
+ }) : null
1352
+ ]
1353
+ }), showInlineComments && anchored.map((comment) => /* @__PURE__ */ jsxs(View, {
1354
+ style: styles$1.inlineComment,
1355
+ children: [/* @__PURE__ */ jsxs(Text, {
1356
+ style: styles$1.blue,
1357
+ children: ["Queued comment · ", lineLabel(comment)]
1358
+ }), /* @__PURE__ */ jsx(Text, {
1359
+ style: styles$1.body,
1360
+ children: comment.body
1361
+ })]
1362
+ }, comment.id))] }, i);
1363
+ }) });
1364
+ }
1365
+ function lineLabel(comment) {
1366
+ 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
+ }
1368
+ function CommentSheet({ range, snippet, value, onChange, onAdd, onCancel }) {
1369
+ return /* @__PURE__ */ jsxs(View, {
1370
+ style: styles$1.sheet,
1371
+ children: [
1372
+ /* @__PURE__ */ jsx(View, { style: styles$1.handle }),
1373
+ /* @__PURE__ */ jsx(Text, {
1374
+ style: styles$1.sheetTitle,
1375
+ children: range ? lineLabel(range) : "Comment on file"
1376
+ }),
1377
+ snippet ? /* @__PURE__ */ jsx(View, {
1378
+ style: styles$1.quote,
1379
+ children: /* @__PURE__ */ jsx(SyntaxLine, {
1380
+ text: snippet,
1381
+ style: [styles$1.body, styles$1.mono]
1382
+ })
861
1383
  }) : null,
862
- comments.map((item) => /* @__PURE__ */ jsxs(View, { children: [/* @__PURE__ */ jsxs(Text, {
863
- style: styles.meta,
864
- children: [
865
- item.relPath,
866
- item.startLine === void 0 ? "" : `:${item.startLine}-${item.endLine ?? item.startLine}`,
867
- "\\n",
868
- item.body
869
- ]
870
- }), /* @__PURE__ */ jsx(Button, {
1384
+ /* @__PURE__ */ jsx(Textarea, {
1385
+ accessibilityLabel: "What should change here?",
1386
+ placeholder: "What should change here?",
1387
+ rows: 4,
1388
+ value,
1389
+ onChangeText: onChange
1390
+ }),
1391
+ /* @__PURE__ */ jsx(Action, {
1392
+ variant: "primary",
1393
+ disabled: value !== void 0 && value.trim() === "",
1394
+ onPress: onAdd,
1395
+ children: "Add comment"
1396
+ }),
1397
+ /* @__PURE__ */ jsx(Action, {
871
1398
  variant: "ghost",
872
- onPress: () => removeReviewComment(sessionId, item.id),
873
- children: "Remove comment"
874
- })] }, item.id)),
875
- comments.length > 0 ? /* @__PURE__ */ jsxs(View, {
876
- style: styles.actions,
877
- children: [/* @__PURE__ */ jsx(Button, {
878
- onPress: () => {
879
- const body = comments.map((item) => {
880
- return `### ${item.startLine === void 0 ? item.relPath : item.side === "old" ? `${item.relPath} (removed lines ${item.startLine}-${item.endLine ?? item.startLine})` : `${item.relPath}:${item.startLine}-${item.endLine ?? item.startLine}`}\n\n${item.snippet.split("\n").slice(0, 20).join("\n").slice(0, 2e3).split("\n").map((line) => `> ${line}`).join("\n")}\n\n${item.body.trim()}`;
881
- }).join("\n\n");
882
- send(`${reviewHeading(comments.length, summary?.branch, summary?.base)}\n\n${body}`, () => clearReviewComments(sessionId));
883
- },
884
- children: "Send review"
885
- }), /* @__PURE__ */ jsx(Button, {
886
- variant: "outline",
887
- onPress: () => setConfirm("discard"),
888
- children: "Discard review…"
889
- })]
1399
+ onPress: onCancel,
1400
+ children: "Cancel"
1401
+ })
1402
+ ]
1403
+ });
1404
+ }
1405
+ function CommentCards({ comments, onRemove }) {
1406
+ return /* @__PURE__ */ jsxs(View, {
1407
+ style: styles$1.content,
1408
+ children: [/* @__PURE__ */ jsxs(Text, {
1409
+ style: styles$1.body,
1410
+ children: [
1411
+ comments.length === 2 ? "Two" : comments.length,
1412
+ " ",
1413
+ comments.length === 1 ? "comment" : "comments",
1414
+ " queued for Po, whenever you are ready."
1415
+ ]
1416
+ }), comments.map((comment) => /* @__PURE__ */ jsxs(View, {
1417
+ style: styles$1.card,
1418
+ children: [
1419
+ /* @__PURE__ */ jsx(Text, {
1420
+ style: [styles$1.body, styles$1.mono],
1421
+ children: comment.relPath
1422
+ }),
1423
+ /* @__PURE__ */ jsx(Text, {
1424
+ style: styles$1.dim,
1425
+ children: lineLabel(comment)
1426
+ }),
1427
+ comment.snippet ? /* @__PURE__ */ jsx(View, {
1428
+ style: styles$1.quote,
1429
+ children: /* @__PURE__ */ jsx(SyntaxLine, {
1430
+ text: comment.snippet,
1431
+ style: [styles$1.body, styles$1.mono]
1432
+ })
1433
+ }) : null,
1434
+ /* @__PURE__ */ jsx(Text, {
1435
+ style: styles$1.body,
1436
+ children: comment.body
1437
+ }),
1438
+ /* @__PURE__ */ jsx(View, {
1439
+ style: styles$1.remove,
1440
+ children: /* @__PURE__ */ jsx(Action, {
1441
+ variant: "ghost",
1442
+ onPress: () => onRemove?.(comment.id),
1443
+ children: "Remove"
1444
+ })
1445
+ })
1446
+ ]
1447
+ }, comment.id))]
1448
+ });
1449
+ }
1450
+ const styles$1 = StyleSheet.create((theme) => ({
1451
+ heading: {
1452
+ fontFamily: fontOf(theme.fontFamily.sans),
1453
+ fontSize: theme.fontSize["2xl"],
1454
+ fontWeight: "600",
1455
+ color: theme.colors.hi,
1456
+ uni__dependencies: [0]
1457
+ },
1458
+ sheetTitle: {
1459
+ fontFamily: fontOf(theme.fontFamily.sans),
1460
+ fontSize: theme.fontSize.xl,
1461
+ fontWeight: "600",
1462
+ color: theme.colors.hi,
1463
+ uni__dependencies: [0]
1464
+ },
1465
+ section: {
1466
+ fontFamily: fontOf(theme.fontFamily.sans),
1467
+ fontSize: theme.fontSize.lg,
1468
+ fontWeight: "600",
1469
+ color: theme.colors.hi,
1470
+ uni__dependencies: [0]
1471
+ },
1472
+ body: {
1473
+ fontFamily: fontOf(theme.fontFamily.sans),
1474
+ fontSize: theme.fontSize.base,
1475
+ lineHeight: theme.lineHeightRelaxed.base,
1476
+ color: theme.colors.text,
1477
+ uni__dependencies: [0]
1478
+ },
1479
+ dim: {
1480
+ fontFamily: fontOf(theme.fontFamily.sans),
1481
+ fontSize: theme.fontSize.sm,
1482
+ lineHeight: theme.lineHeightRelaxed.sm,
1483
+ color: theme.colors.dim,
1484
+ uni__dependencies: [0]
1485
+ },
1486
+ mono: {
1487
+ fontFamily: fontOf(theme.fontFamily.mono),
1488
+ fontSize: theme.fontSize.sm,
1489
+ lineHeight: theme.lineHeightRelaxed.sm,
1490
+ uni__dependencies: [0]
1491
+ },
1492
+ branch: {
1493
+ fontFamily: fontOf(theme.fontFamily.mono),
1494
+ fontSize: theme.fontSize.lg,
1495
+ fontWeight: "600",
1496
+ color: theme.colors.hi,
1497
+ uni__dependencies: [0]
1498
+ },
1499
+ green: {
1500
+ fontSize: theme.fontSize.sm,
1501
+ color: theme.colors.green,
1502
+ uni__dependencies: [0]
1503
+ },
1504
+ red: {
1505
+ fontSize: theme.fontSize.sm,
1506
+ color: theme.colors.red,
1507
+ uni__dependencies: [0]
1508
+ },
1509
+ blue: {
1510
+ fontFamily: fontOf(theme.fontFamily.sans),
1511
+ fontSize: theme.fontSize.sm,
1512
+ color: theme.colors.blue,
1513
+ uni__dependencies: [0]
1514
+ },
1515
+ yellow: {
1516
+ fontSize: theme.fontSize.sm,
1517
+ color: theme.colors.yellow,
1518
+ uni__dependencies: [0]
1519
+ },
1520
+ content: {
1521
+ padding: theme.spacing(4),
1522
+ gap: theme.spacing(3),
1523
+ uni__dependencies: [0]
1524
+ },
1525
+ card: {
1526
+ borderWidth: 1,
1527
+ borderColor: theme.colors.border,
1528
+ borderRadius: theme.borderRadius.lg,
1529
+ padding: theme.spacing(3),
1530
+ gap: theme.spacing(2),
1531
+ uni__dependencies: [0]
1532
+ },
1533
+ inline: {
1534
+ flexDirection: "row",
1535
+ alignItems: "center",
1536
+ gap: theme.spacing(2),
1537
+ uni__dependencies: [0]
1538
+ },
1539
+ grow: {
1540
+ flex: 1,
1541
+ minWidth: 0
1542
+ },
1543
+ action: { minHeight: 44 },
1544
+ primary: {
1545
+ backgroundColor: theme.colors.green,
1546
+ uni__dependencies: [0]
1547
+ },
1548
+ primaryLabel: {
1549
+ fontFamily: fontOf(theme.fontFamily.sans),
1550
+ fontSize: theme.fontSize.base,
1551
+ fontWeight: "600",
1552
+ color: theme.scheme === "dark" ? theme.colors.rail : theme.colors["on-selected"],
1553
+ uni__dependencies: [0]
1554
+ },
1555
+ suggested: {
1556
+ backgroundColor: theme.colors["tint-green"],
1557
+ borderColor: theme.colors["edge-green"],
1558
+ flex: 1,
1559
+ uni__dependencies: [0]
1560
+ },
1561
+ list: {
1562
+ gap: theme.spacing(2),
1563
+ uni__dependencies: [0]
1564
+ },
1565
+ fileRow: {
1566
+ minHeight: 44,
1567
+ paddingVertical: theme.spacing(2),
1568
+ flexDirection: "row",
1569
+ alignItems: "center",
1570
+ gap: theme.spacing(2),
1571
+ borderBottomWidth: StyleSheet.hairlineWidth,
1572
+ borderColor: theme.colors.border,
1573
+ uni__dependencies: [0]
1574
+ },
1575
+ footer: {
1576
+ minHeight: 76,
1577
+ padding: theme.spacing(4),
1578
+ flexDirection: "row",
1579
+ alignItems: "center",
1580
+ justifyContent: "space-between",
1581
+ gap: theme.spacing(2),
1582
+ borderTopWidth: 1,
1583
+ borderColor: theme.colors.border,
1584
+ backgroundColor: theme.colors.rail,
1585
+ uni__dependencies: [0]
1586
+ },
1587
+ caution: {
1588
+ backgroundColor: theme.colors["tint-yellow"],
1589
+ borderColor: theme.colors["edge-yellow"],
1590
+ uni__dependencies: [0]
1591
+ },
1592
+ failure: {
1593
+ backgroundColor: theme.colors["tint-red"],
1594
+ borderColor: theme.colors["edge-red"],
1595
+ uni__dependencies: [0]
1596
+ },
1597
+ sheet: {
1598
+ padding: theme.spacing(4),
1599
+ gap: theme.spacing(3),
1600
+ borderTopWidth: 1,
1601
+ borderColor: theme.colors.border,
1602
+ borderTopLeftRadius: theme.borderRadius.lg,
1603
+ borderTopRightRadius: theme.borderRadius.lg,
1604
+ backgroundColor: theme.colors.rail,
1605
+ uni__dependencies: [0]
1606
+ },
1607
+ handle: {
1608
+ height: 4,
1609
+ width: 40,
1610
+ borderRadius: theme.borderRadius.md,
1611
+ backgroundColor: theme.colors.border,
1612
+ alignSelf: "center",
1613
+ uni__dependencies: [0]
1614
+ },
1615
+ empty: {
1616
+ flex: 1,
1617
+ alignItems: "center",
1618
+ justifyContent: "center",
1619
+ gap: theme.spacing(3),
1620
+ padding: theme.spacing(6),
1621
+ uni__dependencies: [0]
1622
+ },
1623
+ navigation: {
1624
+ flexDirection: "row",
1625
+ justifyContent: "space-between",
1626
+ alignItems: "center"
1627
+ },
1628
+ diffRow: {
1629
+ flexDirection: "row",
1630
+ alignItems: "flex-start",
1631
+ gap: theme.spacing(2),
1632
+ paddingHorizontal: theme.spacing(2),
1633
+ paddingVertical: theme.spacing(1),
1634
+ borderLeftWidth: 3,
1635
+ borderLeftColor: theme.colors.bg,
1636
+ uni__dependencies: [0]
1637
+ },
1638
+ gutter: {
1639
+ color: theme.colors.dim,
1640
+ width: 24,
1641
+ textAlign: "right",
1642
+ uni__dependencies: [0]
1643
+ },
1644
+ code: {
1645
+ flex: 1,
1646
+ minWidth: 0,
1647
+ color: theme.colors.text,
1648
+ uni__dependencies: [0]
1649
+ },
1650
+ added: {
1651
+ backgroundColor: theme.colors["tint-green"],
1652
+ uni__dependencies: [0]
1653
+ },
1654
+ removed: {
1655
+ backgroundColor: theme.colors["tint-red"],
1656
+ uni__dependencies: [0]
1657
+ },
1658
+ selection: {
1659
+ borderLeftColor: theme.colors.green,
1660
+ uni__dependencies: [0]
1661
+ },
1662
+ inlineComment: {
1663
+ margin: theme.spacing(2),
1664
+ padding: theme.spacing(3),
1665
+ borderWidth: 1,
1666
+ borderColor: theme.colors["edge-blue"],
1667
+ borderRadius: theme.borderRadius.md,
1668
+ gap: theme.spacing(1),
1669
+ uni__dependencies: [0]
1670
+ },
1671
+ quote: {
1672
+ padding: theme.spacing(2),
1673
+ backgroundColor: theme.colors.panel,
1674
+ borderRadius: theme.borderRadius.md,
1675
+ uni__dependencies: [0]
1676
+ },
1677
+ remove: { alignSelf: "flex-end" }
1678
+ }));
1679
+ function GitReviewView(props) {
1680
+ const { screen, activity, summary, file, path, comments } = props;
1681
+ const index = summary?.files.findIndex((entry) => entry.path === path) ?? -1;
1682
+ const entry = summary?.files[index];
1683
+ return /* @__PURE__ */ jsxs(View, {
1684
+ style: styles$1.grow,
1685
+ children: [
1686
+ screen === "review" ? /* @__PURE__ */ jsxs(View, {
1687
+ style: styles$1.inline,
1688
+ children: [/* @__PURE__ */ jsx(Action, {
1689
+ variant: "ghost",
1690
+ onPress: () => props.onScreen("changes"),
1691
+ children: "Changes"
1692
+ }), screen === "review" && path ? /* @__PURE__ */ jsx(Action, {
1693
+ variant: "ghost",
1694
+ onPress: () => props.onScreen("file"),
1695
+ children: "File diff"
1696
+ }) : null]
890
1697
  }) : null,
891
- /* @__PURE__ */ jsx(Sheet, {
892
- open: draft !== void 0,
893
- onOpenChange: (open) => {
894
- if (!open) setDraft(void 0);
895
- },
896
- children: /* @__PURE__ */ jsxs(SheetContent, { children: [/* @__PURE__ */ jsx(SheetHeader, { children: /* @__PURE__ */ jsx(SheetTitle, { children: "Review comment" }) }), /* @__PURE__ */ jsx(SheetBody, { children: /* @__PURE__ */ jsxs(View, {
897
- style: styles.body,
1698
+ /* @__PURE__ */ jsxs(ScrollView, {
1699
+ style: styles$1.grow,
1700
+ children: [screen === "changes" ? /* @__PURE__ */ jsxs(View, {
1701
+ style: styles$1.content,
898
1702
  children: [
899
- draft?.rows.length ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Input, {
900
- accessibilityLabel: "Start line",
901
- value: start,
902
- onChangeText: setStart,
903
- keyboardType: "number-pad"
904
- }), /* @__PURE__ */ jsx(Input, {
905
- accessibilityLabel: "End line",
906
- value: end,
907
- onChangeText: setEnd,
908
- keyboardType: "number-pad"
909
- })] }) : null,
910
- /* @__PURE__ */ jsx(Textarea, {
911
- accessibilityLabel: "Comment",
912
- value: comment,
913
- onChangeText: setComment
1703
+ summary?.repository !== false ? /* @__PURE__ */ jsx(SyncCard, {
1704
+ changes: activity.changes,
1705
+ busy: activity.pending !== void 0,
1706
+ onSync: props.onSync
1707
+ }) : null,
1708
+ activity.pending ? /* @__PURE__ */ jsx(Text, {
1709
+ style: styles$1.dim,
1710
+ children: activity.pending
1711
+ }) : null,
1712
+ activity.error ? /* @__PURE__ */ jsxs(View, {
1713
+ style: [styles$1.card, props.forceRequired ? styles$1.caution : styles$1.failure],
1714
+ children: [
1715
+ /* @__PURE__ */ jsx(Text, {
1716
+ style: styles$1.body,
1717
+ children: activity.error
1718
+ }),
1719
+ props.forceRequired ? /* @__PURE__ */ jsx(Action, {
1720
+ variant: "danger-outline",
1721
+ onPress: props.onForce,
1722
+ children: "Force push with lease"
1723
+ }) : null,
1724
+ /* @__PURE__ */ jsx(Action, {
1725
+ variant: "ghost",
1726
+ onPress: props.onDismiss,
1727
+ children: "Dismiss"
1728
+ })
1729
+ ]
1730
+ }) : null,
1731
+ activity.changes?.rebase ? /* @__PURE__ */ jsx(RebaseCard, {
1732
+ conflicts: activity.changes.rebase.conflicts,
1733
+ busy: activity.pending !== void 0,
1734
+ onResolve: props.onResolve,
1735
+ onAbort: props.onAbort
1736
+ }) : null,
1737
+ !summary ? /* @__PURE__ */ jsx(Text, {
1738
+ style: styles$1.dim,
1739
+ children: "Loading changes…"
1740
+ }) : summary.files.length ? /* @__PURE__ */ jsx(FileList, {
1741
+ files: summary.files,
1742
+ comments,
1743
+ onFile: props.onFile
1744
+ }) : /* @__PURE__ */ jsxs(View, {
1745
+ style: styles$1.empty,
1746
+ children: [/* @__PURE__ */ jsx(Text, {
1747
+ style: styles$1.sheetTitle,
1748
+ children: summary.repository ? "No changes yet" : "Not a Git checkout"
1749
+ }), /* @__PURE__ */ jsx(Text, {
1750
+ style: styles$1.dim,
1751
+ children: summary.repository ? `Nothing to review on ${summary.branch ?? "this checkout"}.` : "This folder is not a Git repository, so there is nothing to sync."
1752
+ })]
914
1753
  }),
915
- /* @__PURE__ */ jsx(Button, {
916
- disabled: comment.trim() === "" || Boolean(draft?.rows.length) && (!Number.isInteger(Number(start)) || Number(start) < 1 || !Number.isInteger(Number(end)) || Number(end) < Number(start)),
917
- onPress: () => {
918
- if (!draft) return;
919
- const rows = draft.rows.filter((row) => row.line >= Number(start) && row.line <= Number(end));
920
- const kept = rows.filter((row) => row.marker !== "-");
921
- const numbered = kept.length > 0 ? kept : rows;
922
- addReviewComment(sessionId, {
923
- path: draft.path,
924
- relPath: draft.path,
925
- body: comment.trim(),
926
- snippet: numbered.map((row) => row.content).join("\n"),
927
- ...numbered.length === 0 ? {} : {
928
- side: kept.length > 0 ? "new" : "old",
929
- startLine: Math.min(...numbered.map((row) => row.line)),
930
- endLine: Math.max(...numbered.map((row) => row.line))
931
- }
932
- });
933
- setDraft(void 0);
934
- setComment("");
935
- },
936
- children: "Add comment"
937
- })
1754
+ summary?.truncated ? /* @__PURE__ */ jsx(Text, {
1755
+ style: styles$1.dim,
1756
+ children: "The file list was truncated."
1757
+ }) : null
938
1758
  ]
939
- }) })] })
1759
+ }) : screen === "file" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1760
+ entry ? /* @__PURE__ */ jsx(FileHeader, {
1761
+ entry,
1762
+ index,
1763
+ total: summary?.files.length ?? 0,
1764
+ onChanges: () => props.onScreen("changes"),
1765
+ onPrevious: () => {
1766
+ const previous = summary?.files[index - 1];
1767
+ if (previous) props.onFile(previous.path);
1768
+ },
1769
+ onNext: () => {
1770
+ const next = summary?.files[index + 1];
1771
+ if (next) props.onFile(next.path);
1772
+ }
1773
+ }) : /* @__PURE__ */ jsx(Text, {
1774
+ style: styles$1.heading,
1775
+ children: path
1776
+ }),
1777
+ !file ? /* @__PURE__ */ jsxs(View, {
1778
+ style: styles$1.content,
1779
+ children: [/* @__PURE__ */ jsx(Text, {
1780
+ style: styles$1.dim,
1781
+ children: props.error ? "The diff could not be loaded. Please try again." : "Loading diff…"
1782
+ }), props.error && path ? /* @__PURE__ */ jsx(Action, {
1783
+ onPress: () => props.onFile(path),
1784
+ children: "Retry"
1785
+ }) : null]
1786
+ }) : file.binary || file.tooLarge ? /* @__PURE__ */ jsx(View, {
1787
+ style: styles$1.content,
1788
+ children: /* @__PURE__ */ jsx(Text, {
1789
+ style: styles$1.section,
1790
+ children: file.binary ? "Binary file" : "Diff too large to display"
1791
+ })
1792
+ }) : file.hunks.map((hunk, i) => /* @__PURE__ */ jsx(DiffRows, {
1793
+ rows: hunk.rows,
1794
+ comments: comments.filter((comment) => comment.path === file.path),
1795
+ selection: props.selection,
1796
+ onLine: props.onLine
1797
+ }, i)),
1798
+ comments.filter((comment) => comment.path === path && comment.startLine === void 0).map((comment) => /* @__PURE__ */ jsxs(View, {
1799
+ style: styles$1.inlineComment,
1800
+ children: [/* @__PURE__ */ jsx(Text, {
1801
+ style: styles$1.blue,
1802
+ children: "Queued file comment"
1803
+ }), /* @__PURE__ */ jsx(Text, {
1804
+ style: styles$1.body,
1805
+ children: comment.body
1806
+ })]
1807
+ }, comment.id)),
1808
+ /* @__PURE__ */ jsx(View, {
1809
+ style: styles$1.content,
1810
+ children: /* @__PURE__ */ jsx(Action, {
1811
+ onPress: props.onComment,
1812
+ children: "Comment on file"
1813
+ })
1814
+ })
1815
+ ] }) : /* @__PURE__ */ jsx(CommentCards, {
1816
+ comments,
1817
+ onRemove: props.onRemove
1818
+ }), props.error ? /* @__PURE__ */ jsx(Text, {
1819
+ style: [styles$1.red, styles$1.content],
1820
+ children: props.error
1821
+ }) : null]
940
1822
  }),
941
- /* @__PURE__ */ jsx(Sheet, {
942
- open: confirm !== void 0,
943
- onOpenChange: (open) => {
944
- if (!open) setConfirm(void 0);
1823
+ /* @__PURE__ */ jsx(Footer, {
1824
+ count: comments.length,
1825
+ confirmation: screen === "changes" && activity.changes?.rebase !== void 0,
1826
+ discard: screen === "review",
1827
+ onReview: () => props.onScreen("review"),
1828
+ onSend: props.onSend,
1829
+ onDiscard: props.onDiscard
1830
+ })
1831
+ ]
1832
+ });
1833
+ }
1834
+ //#endregion
1835
+ //#region src/extensions/workspaces/sessions/(frontend)/_components/GitReviewPanel.native.tsx
1836
+ function useGitChanges(sessionId) {
1837
+ const subscribe = useCallback((listener) => {
1838
+ const sub = gitChanges.store.subscribe(listener);
1839
+ return () => sub.unsubscribe();
1840
+ }, []);
1841
+ return useSyncExternalStore(subscribe, () => gitChanges.select(gitChanges.store.state, sessionId));
1842
+ }
1843
+ function reviewTab() {
1844
+ return {
1845
+ id: "git-review",
1846
+ label: "review",
1847
+ panel: GitReviewPanel
1848
+ };
1849
+ }
1850
+ function GitReviewPanel(props) {
1851
+ return /* @__PURE__ */ jsx(GitReviewPanelSession, { ...props }, props.sessionId);
1852
+ }
1853
+ function GitReviewPanelSession({ sessionId, sendSessionFrame }) {
1854
+ const activity = useGitChanges(sessionId);
1855
+ const subscribe = useCallback((listener) => {
1856
+ const sub = gitReview.store.subscribe(listener);
1857
+ return () => sub.unsubscribe();
1858
+ }, []);
1859
+ const comments = useSyncExternalStore(subscribe, () => gitReview.select(gitReview.store.state, sessionId).comments);
1860
+ const [summary, setSummary] = useState();
1861
+ const [file, setFile] = useState();
1862
+ const [error, setError] = useState();
1863
+ const [dismissedError, setDismissedError] = useState();
1864
+ const [confirm, setConfirm] = useState();
1865
+ const [screen, setScreen] = useState("changes");
1866
+ const [path, setPath] = useState();
1867
+ const [selection, setSelection] = useState();
1868
+ const [draft, setDraft] = useState();
1869
+ const [comment, setComment] = useState("");
1870
+ const request = useRef(void 0);
1871
+ const fingerprint = JSON.stringify(activity.changes);
1872
+ useEffect(() => {
1873
+ if (sessionId === null) return void 0;
1874
+ const controller = new AbortController();
1875
+ fetchReviewSummary(sessionId, controller.signal).then((result) => {
1876
+ if (!controller.signal.aborted) {
1877
+ if (result.ok) setSummary(result.summary);
1878
+ else setError(result.error);
1879
+ }
1880
+ }).catch((cause) => {
1881
+ if (!controller.signal.aborted) setError(cause instanceof Error ? cause.message : String(cause));
1882
+ });
1883
+ return () => {
1884
+ controller.abort();
1885
+ request.current?.abort();
1886
+ };
1887
+ }, [sessionId, fingerprint]);
1888
+ const loadFile = async (path) => {
1889
+ if (sessionId === null) return;
1890
+ setScreen("file");
1891
+ setPath(path);
1892
+ setSelection(void 0);
1893
+ setDraft(void 0);
1894
+ request.current?.abort();
1895
+ const controller = new AbortController();
1896
+ request.current = controller;
1897
+ setFile(void 0);
1898
+ setError(void 0);
1899
+ try {
1900
+ const result = await fetchReviewFile(sessionId, path, controller.signal);
1901
+ if (!controller.signal.aborted) {
1902
+ if (result.ok) setFile(result.diff);
1903
+ else setError(result.error);
1904
+ }
1905
+ } catch (cause) {
1906
+ if (!controller.signal.aborted) setError(cause instanceof Error ? cause.message : String(cause));
1907
+ }
1908
+ };
1909
+ if (sessionId === null) return null;
1910
+ const changes = activity.changes;
1911
+ const send = (message, onSent) => {
1912
+ const failure = deliverToAgent(sendSessionFrame, sessionId, message);
1913
+ setError(failure);
1914
+ if (failure === void 0) onSent?.();
1915
+ };
1916
+ const sync = (action) => requestGitSync(sessionId, action);
1917
+ const forceRequired = activity.error !== dismissedError && activity.errorTarget?.action === "push" && activity.errorTarget.forceRequired === true;
1918
+ const sendReview = () => {
1919
+ if (comments.length === 0) return;
1920
+ const body = comments.map((item) => {
1921
+ return `### ${item.startLine === void 0 ? item.relPath : item.side === "old" ? `${item.relPath} (removed lines ${item.startLine}-${item.endLine ?? item.startLine})` : `${item.relPath}:${item.startLine}-${item.endLine ?? item.startLine}`}\n\n${item.snippet.split("\n").slice(0, 20).join("\n").slice(0, 2e3).split("\n").map((line) => `> ${line}`).join("\n")}\n\n${item.body.trim()}`;
1922
+ }).join("\n\n");
1923
+ send(`${reviewHeading(comments.length, summary?.branch, summary?.base)}\n\n${body}`, () => clearReviewComments(sessionId));
1924
+ };
1925
+ const selectLine = (row) => {
1926
+ if (!file) return;
1927
+ const next = selectReviewLine(selection, row);
1928
+ setSelection(next.range);
1929
+ if (next.complete) {
1930
+ const rows = reviewRangeRows(file.hunks.flatMap((hunk) => hunk.rows), next.range);
1931
+ setDraft({
1932
+ path: file.path,
1933
+ range: next.range,
1934
+ snippet: rows.map((item) => item.content).join("\n")
1935
+ });
1936
+ }
1937
+ };
1938
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1939
+ /* @__PURE__ */ jsx(GitReviewView, {
1940
+ screen,
1941
+ activity: {
1942
+ ...activity,
1943
+ error: activity.error === dismissedError ? void 0 : activity.error
1944
+ },
1945
+ summary,
1946
+ file,
1947
+ path,
1948
+ comments,
1949
+ error,
1950
+ forceRequired,
1951
+ selection,
1952
+ onScreen: setScreen,
1953
+ onFile: (path) => void loadFile(path),
1954
+ onLine: selectLine,
1955
+ onSync: sync,
1956
+ onForce: () => setConfirm("force"),
1957
+ onAbort: () => setConfirm("abort"),
1958
+ onResolve: () => send(conflictPrompt(changes?.branch, changes?.rebase?.conflicts ?? [])),
1959
+ onDismiss: () => setDismissedError(activity.error),
1960
+ onComment: () => {
1961
+ if (path) {
1962
+ setSelection(void 0);
1963
+ setDraft({
1964
+ path,
1965
+ snippet: ""
1966
+ });
1967
+ }
1968
+ },
1969
+ onRemove: (id) => removeReviewComment(sessionId, id),
1970
+ onSend: sendReview,
1971
+ onDiscard: () => setConfirm("discard")
1972
+ }),
1973
+ /* @__PURE__ */ jsx(Sheet, {
1974
+ open: draft !== void 0,
1975
+ onOpenChange: (open) => {
1976
+ if (!open) {
1977
+ setDraft(void 0);
1978
+ setSelection(void 0);
1979
+ setComment("");
1980
+ }
1981
+ },
1982
+ children: /* @__PURE__ */ jsx(SheetContent, { children: /* @__PURE__ */ jsx(SheetBody, { children: /* @__PURE__ */ jsx(CommentSheet, {
1983
+ range: draft?.range,
1984
+ snippet: draft?.snippet ?? "",
1985
+ value: comment,
1986
+ onChange: setComment,
1987
+ onCancel: () => {
1988
+ setDraft(void 0);
1989
+ setSelection(void 0);
1990
+ setComment("");
945
1991
  },
946
- children: /* @__PURE__ */ jsxs(SheetContent, { children: [/* @__PURE__ */ jsx(SheetHeader, { children: /* @__PURE__ */ jsx(SheetTitle, { children: confirm === "force" ? "Force push with lease?" : confirm === "abort" ? "Abort rebase?" : "Discard review?" }) }), /* @__PURE__ */ jsxs(SheetBody, { children: [/* @__PURE__ */ jsx(Text, {
947
- style: styles.meta,
1992
+ onAdd: () => {
1993
+ if (!draft || !comment.trim()) return;
1994
+ addReviewComment(sessionId, {
1995
+ path: draft.path,
1996
+ relPath: draft.path,
1997
+ body: comment.trim(),
1998
+ snippet: draft.snippet,
1999
+ ...draft.range
2000
+ });
2001
+ setDraft(void 0);
2002
+ setSelection(void 0);
2003
+ setComment("");
2004
+ }
2005
+ }) }) })
2006
+ }),
2007
+ /* @__PURE__ */ jsx(Sheet, {
2008
+ open: confirm !== void 0,
2009
+ onOpenChange: (open) => {
2010
+ if (!open) setConfirm(void 0);
2011
+ },
2012
+ children: /* @__PURE__ */ jsxs(SheetContent, { children: [/* @__PURE__ */ jsx(SheetHeader, { children: /* @__PURE__ */ jsx(SheetTitle, { children: confirm === "force" ? "Force push with lease?" : confirm === "abort" ? "Abort rebase?" : "Discard review?" }) }), /* @__PURE__ */ jsxs(SheetBody, { children: [
2013
+ /* @__PURE__ */ jsx(Text, {
2014
+ style: styles$1.dim,
948
2015
  children: confirm === "force" ? "Git refuses this if the remote holds commits you never fetched and integrated." : confirm === "abort" ? "Abort discards any conflict resolution in progress." : "Your queued comments will be removed."
949
- }), /* @__PURE__ */ jsx(Button, {
950
- variant: "danger-outline",
2016
+ }),
2017
+ /* @__PURE__ */ jsx(Button, {
2018
+ variant: "danger",
2019
+ disabled: confirm !== "discard" && activity.pending !== void 0,
951
2020
  onPress: () => {
952
2021
  if (confirm === "force") requestGitSync(sessionId, "push", true);
953
2022
  else if (confirm === "abort") sync("abort-rebase");
954
2023
  else clearReviewComments(sessionId);
955
2024
  setConfirm(void 0);
956
2025
  },
957
- children: "Confirm"
958
- })] })] })
959
- })
2026
+ children: confirm === "force" ? "Force push with lease" : confirm === "abort" ? "Abort rebase" : "Discard"
2027
+ }),
2028
+ /* @__PURE__ */ jsx(Button, {
2029
+ variant: "ghost",
2030
+ onPress: () => setConfirm(void 0),
2031
+ children: "Cancel"
2032
+ })
2033
+ ] })] })
2034
+ })
2035
+ ] });
2036
+ }
2037
+ //#endregion
2038
+ //#region src/extensions/workspaces/sessions/(frontend)/fill/_components/WorktreesActivitySection.native.tsx
2039
+ function GitChangesRow({ state = "changes", branch = "Detached HEAD", added = 0, removed = 0, onReview }) {
2040
+ return /* @__PURE__ */ jsxs(Pressable, {
2041
+ testID: "activity-diff-changes",
2042
+ accessibilityRole: "button",
2043
+ accessibilityLabel: "Open changes review",
2044
+ onPress: onReview,
2045
+ style: styles.changeRow,
2046
+ children: [/* @__PURE__ */ jsxs(View, {
2047
+ style: styles.line,
2048
+ children: [
2049
+ /* @__PURE__ */ jsx(FileIcon, {
2050
+ size: 18,
2051
+ color: "dim"
2052
+ }),
2053
+ /* @__PURE__ */ jsx(Text, {
2054
+ style: [styles.heading, styles.grow],
2055
+ children: "Changes"
2056
+ }),
2057
+ /* @__PURE__ */ jsx(ChevronRightIcon, {
2058
+ size: 16,
2059
+ color: "dim"
2060
+ })
2061
+ ]
2062
+ }), /* @__PURE__ */ jsxs(View, {
2063
+ style: styles.changeDetail,
2064
+ children: [/* @__PURE__ */ jsx(Text, {
2065
+ style: [styles.branch, styles.grow],
2066
+ children: branch
2067
+ }), state === "changes" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Text, {
2068
+ style: styles.jade,
2069
+ children: ["+", added]
2070
+ }), /* @__PURE__ */ jsxs(Text, {
2071
+ style: styles.error,
2072
+ children: ["-", removed]
2073
+ })] }) : state === "pushing" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Spinner, { label: "Pushing..." }), /* @__PURE__ */ jsx(Text, {
2074
+ style: styles.dim,
2075
+ children: "Pushing..."
2076
+ })] }) : /* @__PURE__ */ jsx(Text, {
2077
+ style: styles.dim,
2078
+ children: "No changes yet"
2079
+ })]
2080
+ })]
2081
+ });
2082
+ }
2083
+ function WorktreesActivityView({ git, state, disabled = false, onReview, onWorktrees }) {
2084
+ const { starting } = worktreesPresentation(state);
2085
+ return /* @__PURE__ */ jsxs(View, {
2086
+ style: styles.section,
2087
+ children: [
2088
+ git.changes ? /* @__PURE__ */ jsx(GitChangesRow, {
2089
+ branch: git.changes.branch ?? "Detached HEAD",
2090
+ added: git.changes.added,
2091
+ removed: git.changes.removed,
2092
+ state: git.pending?.startsWith("pushing") ? "pushing" : git.changes.added === 0 && git.changes.removed === 0 && git.changes.files === 0 ? "clean" : "changes",
2093
+ onReview
2094
+ }) : null,
2095
+ /* @__PURE__ */ jsx(WorktreeGroup, {
2096
+ title: "Worktrees",
2097
+ count: `${state.worktrees.length} open${starting ? " · 1 starting" : ""}`
2098
+ }),
2099
+ state.worktrees.map((item) => /* @__PURE__ */ jsx(WorktreeRow, {
2100
+ item,
2101
+ compact: true,
2102
+ onOpen: onWorktrees
2103
+ }, item.id)),
2104
+ starting ? /* @__PURE__ */ jsx(WorktreeStarting, { branch: state.requestedBranch }) : state.pending ? /* @__PURE__ */ jsx(Text, {
2105
+ style: styles.dim,
2106
+ children: state.pending
2107
+ }) : null,
2108
+ state.worktrees.length === 0 && !state.pending ? /* @__PURE__ */ jsxs(View, {
2109
+ style: styles.idle,
2110
+ children: [/* @__PURE__ */ jsx(Text, {
2111
+ style: styles.dim,
2112
+ children: "Idle"
2113
+ }), /* @__PURE__ */ jsx(Button, {
2114
+ testID: "activity-git-create",
2115
+ variant: "ghost",
2116
+ style: styles.link,
2117
+ disabled,
2118
+ onPress: onWorktrees,
2119
+ children: /* @__PURE__ */ jsx(Text, {
2120
+ style: styles.jade,
2121
+ children: "Create a worktree →"
2122
+ })
2123
+ })]
2124
+ }) : null
960
2125
  ]
961
2126
  });
962
2127
  }
2128
+ function WorktreesActivitySection({ sessionId, openTransientTab }) {
2129
+ const git = useGitChanges(sessionId);
2130
+ const state = useWorktrees(sessionId);
2131
+ return /* @__PURE__ */ jsx(WorktreesActivityView, {
2132
+ git,
2133
+ state: {
2134
+ ...state,
2135
+ worktrees: state.worktrees.filter((item) => item.sessionId !== sessionId)
2136
+ },
2137
+ disabled: sessionId === null,
2138
+ onReview: () => openTransientTab(reviewTab()),
2139
+ onWorktrees: () => openTransientTab(worktreesTab())
2140
+ });
2141
+ }
963
2142
  const styles = StyleSheet.create((theme) => ({
964
- body: {
965
- padding: theme.spacing(3),
2143
+ section: {
966
2144
  gap: theme.spacing(2),
967
2145
  uni__dependencies: [0]
968
2146
  },
969
- actions: {
2147
+ heading: {
2148
+ fontFamily: fontOf(theme.fontFamily.sans),
2149
+ fontSize: theme.fontSize.lg,
2150
+ lineHeight: theme.lineHeight.lg,
2151
+ fontWeight: "600",
2152
+ color: theme.colors.hi,
2153
+ uni__dependencies: [0]
2154
+ },
2155
+ dim: {
2156
+ fontFamily: fontOf(theme.fontFamily.sans),
2157
+ fontSize: theme.fontSize.sm,
2158
+ lineHeight: theme.lineHeight.sm,
2159
+ color: theme.colors.dim,
2160
+ uni__dependencies: [0]
2161
+ },
2162
+ branch: {
2163
+ fontFamily: fontOf(theme.fontFamily.mono),
2164
+ fontSize: theme.fontSize.base,
2165
+ lineHeight: theme.lineHeight.lg,
2166
+ color: theme.colors.hi,
2167
+ uni__dependencies: [0]
2168
+ },
2169
+ line: {
970
2170
  flexDirection: "row",
971
- flexWrap: "wrap",
2171
+ alignItems: "center",
2172
+ gap: theme.spacing(2),
2173
+ minHeight: 44,
2174
+ uni__dependencies: [0]
2175
+ },
2176
+ grow: {
2177
+ flex: 1,
2178
+ minWidth: 0,
972
2179
  gap: theme.spacing(1),
973
2180
  uni__dependencies: [0]
974
2181
  },
975
- title: {
976
- color: theme.colors.hi,
2182
+ jade: {
2183
+ fontFamily: fontOf(theme.fontFamily.sans),
977
2184
  fontSize: theme.fontSize.base,
978
- fontWeight: "600",
2185
+ lineHeight: theme.lineHeight.base,
2186
+ color: theme.colors.green,
2187
+ fontWeight: "500",
979
2188
  uni__dependencies: [0]
980
2189
  },
981
- meta: {
982
- color: theme.colors.dim,
2190
+ error: {
2191
+ fontFamily: fontOf(theme.fontFamily.sans),
983
2192
  fontSize: theme.fontSize.sm,
2193
+ lineHeight: theme.lineHeight.sm,
2194
+ color: theme.colors.red,
984
2195
  uni__dependencies: [0]
985
2196
  },
986
- error: {
987
- color: theme.colors.red,
988
- fontSize: theme.fontSize.sm,
2197
+ changeRow: {
2198
+ gap: theme.spacing(1),
2199
+ paddingVertical: theme.spacing(2),
2200
+ borderBottomWidth: StyleSheet.hairlineWidth,
2201
+ borderColor: theme.colors["border-soft"],
989
2202
  uni__dependencies: [0]
990
2203
  },
991
- diff: (marker) => ({
992
- fontFamily: fontOf(theme.fontFamily.mono),
993
- fontSize: theme.fontSize.sm,
994
- color: marker === "+" ? theme.colors.green : marker === "-" ? theme.colors.red : theme.colors.text,
995
- paddingVertical: theme.spacing(1),
2204
+ changeDetail: {
2205
+ flexDirection: "row",
2206
+ alignItems: "center",
2207
+ gap: theme.spacing(3),
2208
+ paddingLeft: theme.spacing(6),
2209
+ minHeight: 28,
2210
+ uni__dependencies: [0]
2211
+ },
2212
+ idle: {
2213
+ flexDirection: "row",
2214
+ alignItems: "center",
2215
+ gap: theme.spacing(4),
996
2216
  uni__dependencies: [0]
997
- })
2217
+ },
2218
+ link: {
2219
+ minHeight: 44,
2220
+ paddingHorizontal: 0
2221
+ }
998
2222
  }));
999
2223
  //#endregion
1000
- //#region src/extensions/workspaces/sessions/(frontend)/fill/_components/WorktreesActivitySection.native.tsx
1001
- function WorktreesActivitySection({ sessionId, openTransientTab }) {
1002
- const git = useGitChanges(sessionId);
1003
- const state = useWorktrees(sessionId);
1004
- const worktrees = state.worktrees.filter((item) => item.sessionId !== sessionId);
1005
- return /* @__PURE__ */ jsxs(View, { children: [
1006
- git.changes ? /* @__PURE__ */ jsxs(Button, {
1007
- variant: "ghost",
1008
- testID: "activity-diff-changes",
1009
- onPress: () => openTransientTab(reviewTab()),
1010
- children: [
1011
- git.changes.branch ?? "Detached HEAD",
1012
- " · +",
1013
- git.changes.added,
1014
- " -",
1015
- git.changes.removed
1016
- ]
1017
- }) : null,
1018
- state.pending ? /* @__PURE__ */ jsx(ListRow, { title: state.pending }) : null,
1019
- worktrees.map((item) => /* @__PURE__ */ jsx(ListRow, {
1020
- leading: /* @__PURE__ */ jsx(Dot, { tone: item.orphaned ? "red" : item.sessionId === null ? "muted" : "blue" }),
1021
- title: /* @__PURE__ */ jsx(Button, {
1022
- variant: "ghost",
1023
- onPress: () => openTransientTab(worktreesTab()),
1024
- children: item.branch
1025
- }),
1026
- description: [item.orphaned ? "Orphaned" : "", item.unowned ? "Unowned" : ""].filter(Boolean).join(" · ")
1027
- }, item.id)),
1028
- worktrees.length === 0 && state.pending === void 0 ? /* @__PURE__ */ jsx(Button, {
1029
- variant: "ghost",
1030
- testID: "activity-git-create",
1031
- disabled: sessionId === null,
1032
- onPress: () => openTransientTab(worktreesTab()),
1033
- children: "Create a worktree"
1034
- }) : null
1035
- ] });
1036
- }
1037
- //#endregion
1038
2224
  //#region src/extensions/workspaces/sessions/(frontend)/fill/Worktrees.activity.git.native.tsx
1039
2225
  var Worktrees_activity_git_native_default = defineFill({ component: WorktreesActivitySection });
1040
2226
  //#endregion