@agimon-ai/po-git 0.0.0-alpha.78 → 0.0.0-alpha.79

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,5 +1,5 @@
1
1
  import { createApiClient, defineActivityGroup, defineChannelFile, defineFill, defineGlobalStore, defineLeaderBinding, defineRepositorySettingsPanel, defineSessionStore, defineToolRenderer, defineWebLifecycle, defineWebPlugin, sessionApiAddress } from "@agimon-ai/po-core/web";
2
- import { ArrowRightIcon, BranchIcon, Button, CheckIcon, ChevronDownIcon, ChevronUpIcon, CodeBlock, Dialog, DialogContent, DialogTitle, DiffView, Dot, DropdownMenuItem, EmptyState, Input, MessageIcon, MessageItem, MessageItemBody, ReviewCommentDraft, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SettingsActions, SettingsCard, SettingsNote, SettingsRow, SettingsRowDescription, SettingsRowText, SettingsRowTitle, SettingsSection, SettingsSectionDescription, SettingsSectionHeader, SettingsSectionTitle, ShieldIcon, Spinner, StatusBadge, Tabs, TabsContent, TabsList, TabsTrigger, buildReviewPrompt } from "@agimon-ai/po-web-components";
2
+ import { ArrowRightIcon, BranchIcon, Button, CheckIcon, ChevronDownIcon, ChevronUpIcon, CodeBlock, Dialog, DialogContent, DialogTitle, DiffView, Dot, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptyState, FolderIcon, Input, KebabIcon, MessageIcon, MessageItem, MessageItemBody, ReviewCommentDraft, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SettingsActions, SettingsCard, SettingsNote, SettingsRow, SettingsRowDescription, SettingsRowText, SettingsRowTitle, SettingsSection, SettingsSectionDescription, SettingsSectionHeader, SettingsSectionTitle, ShieldIcon, Spinner, StatusBadge, Tabs, TabsContent, TabsList, TabsTrigger, TrashIcon, buildReviewPrompt } from "@agimon-ai/po-web-components";
3
3
  import { createElement, useCallback, useEffect, useEffectEvent, useId, useRef, useState } from "react";
4
4
  import { browserTransport } from "@agimon-ai/po-web-security/browser";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -11,6 +11,16 @@ const api = createApiClient({
11
11
  path: "/branches",
12
12
  response: void 0
13
13
  },
14
+ deleteBranch: {
15
+ method: "DELETE",
16
+ path: "/branches",
17
+ response: void 0
18
+ },
19
+ openBranchFolder: {
20
+ method: "POST",
21
+ path: "/branches/open",
22
+ response: void 0
23
+ },
14
24
  /** Workspace: a top-level worktree session on an existing or new branch. */
15
25
  createSession: {
16
26
  method: "POST",
@@ -124,7 +134,7 @@ function Field$1({ label, children }) {
124
134
  });
125
135
  }
126
136
  /** Inline presentation also lets story capture inspect content, rather than a portal-only root. */
127
- function GitNewSessionDialogView({ workspaceRoot, branches, loadError = "", initialForm, busy = false, error = "", riftMissing = false, onClose, onCreate }) {
137
+ function GitNewSessionDialogView({ workspaceRoot, branches, loadError = "", initialForm, busy = false, error = "", riftMissing = false, onClose, onCreate, onDeleteBranch, onOpenBranchFolder }) {
128
138
  const [form, setForm] = useState({
129
139
  mode: "existing",
130
140
  newBranch: "",
@@ -133,6 +143,7 @@ function GitNewSessionDialogView({ workspaceRoot, branches, loadError = "", init
133
143
  ...initialForm
134
144
  });
135
145
  const [query, setQuery] = useState("");
146
+ const [deleting, setDeleting] = useState();
136
147
  const ready = branches?.repository === true && loadError === "";
137
148
  const loading = branches === null && loadError === "";
138
149
  const mode = ready || loading ? form.mode : "plain";
@@ -149,7 +160,7 @@ function GitNewSessionDialogView({ workspaceRoot, branches, loadError = "", init
149
160
  const options = branches === null ? [] : branchOptions(branches, query);
150
161
  const bases = branches === null ? [] : baseOptions(branches);
151
162
  const submit = () => {
152
- if (!busy && !loading && problem === void 0) onCreate(state);
163
+ if (!busy && !loading && deleting === void 0 && problem === void 0) onCreate(state);
153
164
  };
154
165
  return /* @__PURE__ */ jsxs("section", {
155
166
  "data-testid": "new-session-dialog",
@@ -230,32 +241,94 @@ function GitNewSessionDialogView({ workspaceRoot, branches, loadError = "", init
230
241
  }) : null, options.map((branch, index) => /* @__PURE__ */ jsxs("div", { children: [branch.remote !== void 0 && (index === 0 || options[index - 1]?.remote === void 0) ? /* @__PURE__ */ jsx("p", {
231
242
  className: "mt-1 border-t border-po-border-soft px-3 pt-2 text-sm text-po-dim",
232
243
  children: "Remote branches"
233
- }) : null, /* @__PURE__ */ jsxs("button", {
234
- "data-testid": "git-new-session-branch-option",
235
- "data-branch": branch.name,
236
- "data-remote": branch.remote,
237
- "aria-pressed": form.selected?.key === branch.key,
238
- disabled: branch.busyAt !== void 0 || busy,
239
- onClick: () => update({ selected: branch }),
240
- className: `flex w-full flex-col gap-1 rounded px-3 py-2 text-left text-base ${form.selected?.key === branch.key ? "bg-po-tint-green text-po-green" : "text-po-text"}`,
241
- children: [/* @__PURE__ */ jsxs("span", {
242
- className: "flex items-center justify-between gap-2",
243
- children: [/* @__PURE__ */ jsx("span", {
244
- className: "font-mono",
245
- children: branch.remote === void 0 ? branch.name : `${branch.remote}/${branch.name}`
246
- }), form.selected?.key === branch.key ? /* @__PURE__ */ jsx(CheckIcon, { className: "size-4" }) : null]
247
- }), branch.busyAt !== void 0 ? /* @__PURE__ */ jsxs("span", {
248
- className: "text-sm text-po-dim",
249
- children: ["Checked out in ", /* @__PURE__ */ jsx("span", {
250
- className: "font-mono break-all",
251
- children: branch.busyAt
252
- })]
253
- }) : branch.current ? /* @__PURE__ */ jsx("span", {
254
- className: "text-sm text-po-dim",
255
- children: "Current"
256
- }) : null]
244
+ }) : null, /* @__PURE__ */ jsxs("div", {
245
+ className: "flex min-w-0 items-start gap-1",
246
+ children: [/* @__PURE__ */ jsxs("button", {
247
+ "data-testid": "git-new-session-branch-option",
248
+ "data-branch": branch.name,
249
+ "data-remote": branch.remote,
250
+ "aria-pressed": form.selected?.key === branch.key,
251
+ disabled: branch.busyAt !== void 0 || busy,
252
+ onClick: () => update({ selected: branch }),
253
+ className: `flex min-w-0 flex-1 flex-col gap-1 rounded px-3 py-2 text-left text-base ${form.selected?.key === branch.key ? "bg-po-tint-green text-po-green" : "text-po-text"}`,
254
+ children: [/* @__PURE__ */ jsxs("span", {
255
+ className: "flex items-center justify-between gap-2",
256
+ children: [/* @__PURE__ */ jsx("span", {
257
+ className: "font-mono break-all",
258
+ children: branch.remote === void 0 ? branch.name : `${branch.remote}/${branch.name}`
259
+ }), form.selected?.key === branch.key ? /* @__PURE__ */ jsx(CheckIcon, { className: "size-4 shrink-0" }) : null]
260
+ }), branch.busyAt !== void 0 ? /* @__PURE__ */ jsxs("span", {
261
+ className: "text-sm text-po-dim",
262
+ children: ["Checked out in ", /* @__PURE__ */ jsx("span", {
263
+ className: "font-mono break-all",
264
+ children: branch.busyAt
265
+ })]
266
+ }) : branch.current ? /* @__PURE__ */ jsx("span", {
267
+ className: "text-sm text-po-dim",
268
+ children: "Current"
269
+ }) : null]
270
+ }), /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
271
+ asChild: true,
272
+ children: /* @__PURE__ */ jsx(Button, {
273
+ variant: "ghost",
274
+ size: "icon-md",
275
+ className: "mt-1",
276
+ "aria-label": `Actions for ${branch.remote === void 0 ? branch.name : `${branch.remote}/${branch.name}`}`,
277
+ disabled: busy,
278
+ children: /* @__PURE__ */ jsx(KebabIcon, { className: "size-4" })
279
+ })
280
+ }), /* @__PURE__ */ jsxs(DropdownMenuContent, { children: [
281
+ /* @__PURE__ */ jsxs(DropdownMenuItem, {
282
+ disabled: branch.busyAt === void 0 || onOpenBranchFolder === void 0,
283
+ title: branch.busyAt === void 0 ? "This branch has no checked-out folder." : branch.busyAt,
284
+ onSelect: () => onOpenBranchFolder?.(branch.name),
285
+ children: [/* @__PURE__ */ jsx(FolderIcon, { className: "size-4" }), "Open in Finder"]
286
+ }),
287
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
288
+ /* @__PURE__ */ jsxs(DropdownMenuItem, {
289
+ variant: "destructive",
290
+ disabled: branch.remote !== void 0 || branch.busyAt !== void 0 || branch.current || onDeleteBranch === void 0,
291
+ title: branch.remote !== void 0 ? "Only local branches can be deleted here." : branch.busyAt !== void 0 || branch.current ? "Close this branch’s worktree before deleting it." : void 0,
292
+ onSelect: () => setDeleting(branch),
293
+ children: [/* @__PURE__ */ jsx(TrashIcon, { className: "size-4" }), "Delete branch"]
294
+ })
295
+ ] })] })]
257
296
  })] }, branch.key))]
258
- })
297
+ }),
298
+ deleting !== void 0 ? /* @__PURE__ */ jsxs("fieldset", {
299
+ "aria-label": "Confirm branch deletion",
300
+ className: "flex flex-col gap-3 rounded-md border border-po-edge-red bg-po-tint-red p-3",
301
+ children: [/* @__PURE__ */ jsxs("p", {
302
+ className: "text-sm text-po-text",
303
+ children: [
304
+ "Delete local branch ",
305
+ /* @__PURE__ */ jsx("span", {
306
+ className: "font-mono break-all",
307
+ children: deleting.name
308
+ }),
309
+ "? Git will refuse if it contains unmerged commits. No worktree or remote branch will be removed."
310
+ ]
311
+ }), /* @__PURE__ */ jsxs("div", {
312
+ className: "flex justify-end gap-2",
313
+ children: [/* @__PURE__ */ jsx(Button, {
314
+ variant: "ghost",
315
+ disabled: busy,
316
+ onClick: () => setDeleting(void 0),
317
+ children: "Cancel deletion"
318
+ }), /* @__PURE__ */ jsx(Button, {
319
+ variant: "danger-outline",
320
+ disabled: busy,
321
+ onClick: async () => {
322
+ if (busy || onDeleteBranch === void 0) return;
323
+ if (await onDeleteBranch(deleting.name)) {
324
+ if (form.selected?.key === deleting.key) update({ selected: void 0 });
325
+ setDeleting(void 0);
326
+ }
327
+ },
328
+ children: "Delete branch"
329
+ })]
330
+ })]
331
+ }) : null
259
332
  ]
260
333
  }),
261
334
  /* @__PURE__ */ jsxs(TabsContent, {
@@ -353,7 +426,7 @@ rift init`,
353
426
  "data-testid": "new-session-create",
354
427
  variant: "primary",
355
428
  title: problem,
356
- disabled: busy || loading || problem !== void 0,
429
+ disabled: busy || loading || deleting !== void 0 || problem !== void 0,
357
430
  onClick: submit,
358
431
  children: busy ? mode === "plain" ? "Creating session..." : "Creating worktree..." : riftMissing ? "Try again" : "Create session"
359
432
  })]
@@ -423,6 +496,35 @@ function GitNewSessionDialog({ newSession }) {
423
496
  setError(failure);
424
497
  setBusy(false);
425
498
  };
499
+ const branchAction = async (branch, action) => {
500
+ if (busy) return false;
501
+ setBusy(true);
502
+ setError("");
503
+ setRiftMissing(false);
504
+ try {
505
+ const workspace = api.workspace(newSession.workspaceId);
506
+ const response = await newSession.requestWithStepUp(action === "delete" ? workspace.deleteBranch.url() : workspace.openBranchFolder.url(), {
507
+ method: action === "delete" ? "DELETE" : "POST",
508
+ headers: { "Content-Type": "application/json" },
509
+ body: JSON.stringify({ branch })
510
+ });
511
+ const body = await response.json().catch(() => void 0);
512
+ if (!response.ok) {
513
+ setError(errorOf$1(body, response.status));
514
+ return false;
515
+ }
516
+ if (action === "delete") setBranches((current) => current === null ? null : {
517
+ ...current,
518
+ local: current.local.filter((item) => item.name !== branch)
519
+ });
520
+ return true;
521
+ } catch {
522
+ setError("The cockpit is unreachable.");
523
+ return false;
524
+ } finally {
525
+ setBusy(false);
526
+ }
527
+ };
426
528
  return /* @__PURE__ */ jsx(Dialog, {
427
529
  open: true,
428
530
  onOpenChange: (next) => {
@@ -443,7 +545,9 @@ function GitNewSessionDialog({ newSession }) {
443
545
  error,
444
546
  riftMissing,
445
547
  onClose: newSession.close,
446
- onCreate: (state) => void submit(state)
548
+ onCreate: (state) => void submit(state),
549
+ onDeleteBranch: (branch) => branchAction(branch, "delete"),
550
+ onOpenBranchFolder: (branch) => void branchAction(branch, "open")
447
551
  })]
448
552
  })
449
553
  });