@opengeni/react 0.12.0 → 0.13.0

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.
Files changed (85) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-5TIXRCSI.js → chunk-NFYVQWIB.js} +150 -64
  3. package/dist/chunk-NFYVQWIB.js.map +1 -0
  4. package/dist/index.d.ts +760 -142
  5. package/dist/index.js +5345 -2146
  6. package/dist/index.js.map +1 -1
  7. package/dist/{machines-BeZ3bD3t.d.ts → machines-CnlMb7E-.d.ts} +5 -5
  8. package/dist/machines.d.ts +1 -1
  9. package/dist/machines.js +1 -1
  10. package/package.json +17 -12
  11. package/src/approvals.ts +16 -4
  12. package/src/client.ts +32 -5
  13. package/src/commands/index.ts +1 -7
  14. package/src/commands/registry.ts +46 -12
  15. package/src/components/chat-composer.tsx +135 -39
  16. package/src/components/code-editor.tsx +28 -36
  17. package/src/components/command-palette.tsx +26 -5
  18. package/src/components/desktop-viewer.tsx +29 -16
  19. package/src/components/diff-view.tsx +27 -189
  20. package/src/components/enrollment-consent.tsx +28 -10
  21. package/src/components/enrollment-device-flow.tsx +8 -5
  22. package/src/components/file-browser.tsx +190 -56
  23. package/src/components/fleet-tile.tsx +13 -4
  24. package/src/components/machine-card.tsx +16 -4
  25. package/src/components/machine-dock-bar.tsx +20 -8
  26. package/src/components/machine-metrics.tsx +31 -8
  27. package/src/components/machine-status-pill.tsx +26 -5
  28. package/src/components/machines-dashboard.tsx +8 -2
  29. package/src/components/markdown.tsx +39 -9
  30. package/src/components/message-timeline.tsx +633 -109
  31. package/src/components/pierre-diff.tsx +83 -15
  32. package/src/components/pierre-file.tsx +10 -9
  33. package/src/components/sandbox-files.tsx +110 -227
  34. package/src/components/sandbox-terminal.tsx +256 -88
  35. package/src/components/sandbox-workspace.tsx +823 -0
  36. package/src/components/session-status.tsx +28 -2
  37. package/src/components/workbench-changes.tsx +541 -0
  38. package/src/components/workspace-dock.tsx +85 -22
  39. package/src/hooks/internal.ts +5 -2
  40. package/src/hooks/use-available-models.ts +7 -2
  41. package/src/hooks/use-billing-usage.ts +4 -1
  42. package/src/hooks/use-codex-accounts.ts +74 -44
  43. package/src/hooks/use-composer.ts +57 -36
  44. package/src/hooks/use-environments.ts +92 -47
  45. package/src/hooks/use-file-attachments.ts +101 -55
  46. package/src/hooks/use-goal.ts +52 -16
  47. package/src/hooks/use-machine-chip.ts +134 -0
  48. package/src/hooks/use-machines.ts +34 -16
  49. package/src/hooks/use-packs.ts +24 -19
  50. package/src/hooks/use-relay-frame-stream.ts +5 -2
  51. package/src/hooks/use-rigs.ts +335 -0
  52. package/src/hooks/use-sandbox-files.ts +213 -39
  53. package/src/hooks/use-sandbox-git.ts +188 -11
  54. package/src/hooks/use-sandbox-terminal.ts +18 -14
  55. package/src/hooks/use-scheduled-tasks.ts +10 -2
  56. package/src/hooks/use-session-capabilities.ts +77 -20
  57. package/src/hooks/use-session-control.ts +49 -21
  58. package/src/hooks/use-session-events.ts +48 -20
  59. package/src/hooks/use-session-lineage.ts +119 -0
  60. package/src/hooks/use-session.ts +43 -28
  61. package/src/hooks/use-slash-commands.ts +6 -4
  62. package/src/hooks/use-turn-queue.ts +74 -147
  63. package/src/hooks/use-windowed-sections.ts +204 -0
  64. package/src/hooks/use-workspace-capture.ts +233 -0
  65. package/src/hooks/use-workspace-edit.ts +231 -0
  66. package/src/hooks/use-workspace-sessions.ts +48 -5
  67. package/src/hooks/use-workspaces.ts +18 -15
  68. package/src/index.ts +127 -23
  69. package/src/lib/format.ts +3 -3
  70. package/src/lib/xterm-renderer.ts +65 -0
  71. package/src/lib/xterm-theme.ts +224 -18
  72. package/src/machines.ts +13 -3
  73. package/src/provider.tsx +3 -1
  74. package/src/timeline/activity-rail.tsx +213 -54
  75. package/src/timeline/disclosure-context.tsx +12 -2
  76. package/src/timeline/index.ts +21 -2
  77. package/src/timeline/parsers.ts +44 -10
  78. package/src/timeline/projection.ts +388 -85
  79. package/src/timeline/shared.tsx +111 -24
  80. package/src/timeline/tool-diff.tsx +24 -20
  81. package/src/timeline/tool-renderers.tsx +98 -21
  82. package/src/timeline/turn-summary.tsx +76 -23
  83. package/src/timeline/types.ts +96 -12
  84. package/styles/tokens.css +2 -2
  85. package/dist/chunk-5TIXRCSI.js.map +0 -1
@@ -1,8 +1,14 @@
1
- import type { GitDiffLine, GitFileDiff } from "@opengeni/sdk";
1
+ import type { GitFileDiff } from "@opengeni/sdk";
2
2
  import { type ReactNode } from "react";
3
3
  import { cn } from "../lib/cn";
4
+ import { PierreDiff } from "./pierre-diff";
4
5
 
5
- /** Theme tokens for the diff line backgrounds. */
6
+ /**
7
+ * @deprecated Use {@link PierreDiff} directly. Retained only as a thin,
8
+ * back-compat alias that renders the Pierre (Shiki) renderer with a plain-text
9
+ * degrade. The old hand-rolled hunk renderer was removed in Workbench v2 — the
10
+ * workbench keeps exactly ONE diff renderer. This alias is removed next major.
11
+ */
6
12
  export type DiffTheme = {
7
13
  addBackground?: string;
8
14
  delBackground?: string;
@@ -10,54 +16,39 @@ export type DiffTheme = {
10
16
  metaForeground?: string;
11
17
  };
12
18
 
19
+ /**
20
+ * @deprecated Use {@link PierreDiff}. `theme`/`onSelectFile`/`emptyState` from the
21
+ * removed hand-rolled renderer are accepted for source compatibility and ignored;
22
+ * `fallback` still overrides the plain-text degrade.
23
+ */
13
24
  export type DiffViewProps = {
14
- /** From `useSandboxGit().diff` — the structured per-file hunks. */
15
25
  diff: GitFileDiff[];
16
- /** Rendered instead of the built-in diff (the Pierre-swap escape hatch). */
17
26
  fallback?: ReactNode | undefined;
18
- /** unified | split. Default "unified". */
19
27
  layout?: "unified" | "split" | undefined;
28
+ themeType?: "dark" | "light" | undefined;
29
+ /** @deprecated line-background theming was part of the removed renderer. */
20
30
  theme?: DiffTheme | undefined;
31
+ /** @deprecated the alias no longer renders a clickable file header. */
21
32
  onSelectFile?: ((path: string) => void) | undefined;
22
- /** Distinguishes "no changes" from "no repository mounted". */
23
33
  isRepo?: boolean | undefined;
24
34
  emptyState?: ReactNode | undefined;
25
35
  className?: string | undefined;
26
36
  };
27
37
 
28
- const STATUS_LABEL: Record<GitFileDiff["status"], string> = {
29
- added: "added",
30
- modified: "modified",
31
- deleted: "deleted",
32
- renamed: "renamed",
33
- copied: "copied",
34
- untracked: "untracked",
35
- ignored: "ignored",
36
- conflicted: "conflicted",
37
- typechange: "typechange",
38
- };
39
-
40
38
  /**
41
- * The diff view, fed by the Git service via `useSandboxGit().diff`. This is our
42
- * `PierreDiff` boundary: the built-in renderer delivers the same UX (per-file
43
- * hunks, unified or side-by-side, old/new line numbers, add/del counts), and a
44
- * consumer with Pierre's `@pierre/diffs` installed can swap it via `fallback`
45
- * while keeping the same `GitFileDiff[]` data contract from the hook.
39
+ * @deprecated Use {@link PierreDiff} directly. Thin alias: renders `PierreDiff`
40
+ * (Shiki-highlighted, unified/split) with a plain-text fallback for a host
41
+ * without `@pierre/diffs`. Removed next major.
46
42
  */
47
43
  export function DiffView({
48
44
  diff,
49
45
  fallback,
50
46
  layout = "unified",
51
- theme,
52
- onSelectFile,
47
+ themeType,
53
48
  isRepo = true,
54
49
  emptyState,
55
50
  className,
56
51
  }: DiffViewProps) {
57
- if (fallback !== undefined) {
58
- return <div className={className}>{fallback}</div>;
59
- }
60
-
61
52
  if (diff.length === 0) {
62
53
  return (
63
54
  <div className={cn("p-3 text-og-sm text-og-fg-subtle", className)}>
@@ -65,166 +56,13 @@ export function DiffView({
65
56
  </div>
66
57
  );
67
58
  }
68
-
69
- return (
70
- <div className={cn("min-w-0 overflow-auto", className)} data-opengeni-diff>
71
- {diff.map((file) => (
72
- <FileDiffBlock
73
- key={file.path}
74
- file={file}
75
- layout={layout}
76
- theme={theme}
77
- {...(onSelectFile ? { onSelect: () => onSelectFile(file.path) } : {})}
78
- />
79
- ))}
80
- </div>
81
- );
82
- }
83
-
84
- function FileDiffBlock({
85
- file,
86
- layout,
87
- theme,
88
- onSelect,
89
- }: {
90
- file: GitFileDiff;
91
- layout: "unified" | "split";
92
- theme: DiffTheme | undefined;
93
- onSelect?: (() => void) | undefined;
94
- }) {
95
- return (
96
- <section className="mb-2 overflow-hidden rounded-og-sm border border-og-border">
97
- <header
98
- className={cn(
99
- "flex items-center justify-between gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1 text-og-sm",
100
- onSelect && "cursor-pointer",
101
- )}
102
- onClick={onSelect}
103
- >
104
- <span className="truncate font-og-mono">
105
- {file.oldPath && file.oldPath !== file.path ? `${file.oldPath} → ${file.path}` : file.path}
106
- </span>
107
- <span className="flex shrink-0 items-center gap-2">
108
- <span className="rounded-og-xs bg-og-bg px-1 py-0.5 text-og-xs text-og-fg-subtle">
109
- {STATUS_LABEL[file.status]}
110
- </span>
111
- <span className="text-og-status-idle">+{file.additions}</span>
112
- <span className="text-og-status-failed">−{file.deletions}</span>
113
- </span>
114
- </header>
115
- {file.isBinary ? (
116
- <div className="px-2 py-3 text-og-sm text-og-fg-subtle">Binary file not shown</div>
117
- ) : file.truncated ? (
118
- <div className="px-2 py-3 text-og-sm text-og-fg-subtle">Diff too large — truncated</div>
119
- ) : layout === "split" ? (
120
- <SplitHunks file={file} theme={theme} />
121
- ) : (
122
- <UnifiedHunks file={file} theme={theme} />
123
- )}
124
- </section>
125
- );
126
- }
127
-
128
- function lineBg(type: GitDiffLine["type"], theme: DiffTheme | undefined): string | undefined {
129
- if (type === "add") return theme?.addBackground ?? "var(--og-color-diff-add-bg)";
130
- if (type === "del") return theme?.delBackground ?? "var(--og-color-diff-del-bg)";
131
- if (type === "meta") return undefined;
132
- return theme?.contextBackground;
133
- }
134
-
135
- function marker(type: GitDiffLine["type"]): string {
136
- if (type === "add") return "+";
137
- if (type === "del") return "−";
138
- return " ";
139
- }
140
-
141
- function UnifiedHunks({ file, theme }: { file: GitFileDiff; theme: DiffTheme | undefined }) {
142
- return (
143
- <div className="font-og-mono text-og-xs">
144
- {file.hunks.map((hunk, hi) => (
145
- <div key={`${file.path}-h${hi}`}>
146
- <div
147
- className="px-2 py-0.5 text-og-accent"
148
- style={{ color: theme?.metaForeground }}
149
- >
150
- {hunk.header}
151
- </div>
152
- {hunk.lines.map((line, li) => (
153
- <div
154
- key={`${file.path}-h${hi}-l${li}`}
155
- className="flex"
156
- style={{ backgroundColor: lineBg(line.type, theme) }}
157
- >
158
- <span className="w-10 shrink-0 select-none px-1 text-right text-og-fg-subtle">
159
- {line.oldNo ?? ""}
160
- </span>
161
- <span className="w-10 shrink-0 select-none px-1 text-right text-og-fg-subtle">
162
- {line.newNo ?? ""}
163
- </span>
164
- <span className="w-4 shrink-0 select-none text-center text-og-fg-subtle">
165
- {marker(line.type)}
166
- </span>
167
- <span className="whitespace-pre-wrap break-all px-1">{line.text}</span>
168
- </div>
169
- ))}
170
- </div>
171
- ))}
172
- </div>
173
- );
174
- }
175
-
176
- function SplitHunks({ file, theme }: { file: GitFileDiff; theme: DiffTheme | undefined }) {
177
59
  return (
178
- <div className="font-og-mono text-og-xs">
179
- {file.hunks.map((hunk, hi) => {
180
- // Pair del/add lines side-by-side; context spans both columns.
181
- const left: (GitDiffLine | null)[] = [];
182
- const right: (GitDiffLine | null)[] = [];
183
- for (const line of hunk.lines) {
184
- if (line.type === "context" || line.type === "meta") {
185
- left.push(line);
186
- right.push(line);
187
- } else if (line.type === "del") {
188
- left.push(line);
189
- } else {
190
- right.push(line);
191
- }
192
- }
193
- const rows = Math.max(left.length, right.length);
194
- return (
195
- <div key={`${file.path}-h${hi}`}>
196
- <div className="px-2 py-0.5 text-og-accent" style={{ color: theme?.metaForeground }}>
197
- {hunk.header}
198
- </div>
199
- {Array.from({ length: rows }).map((_, ri) => {
200
- const l = left[ri] ?? null;
201
- const r = right[ri] ?? null;
202
- return (
203
- <div key={`${file.path}-h${hi}-r${ri}`} className="flex">
204
- <span
205
- className="flex w-1/2 min-w-0"
206
- style={{ backgroundColor: l ? lineBg(l.type, theme) : undefined }}
207
- >
208
- <span className="w-10 shrink-0 select-none px-1 text-right text-og-fg-subtle">
209
- {l?.oldNo ?? ""}
210
- </span>
211
- <span className="whitespace-pre-wrap break-all px-1">{l?.text ?? ""}</span>
212
- </span>
213
- <span
214
- className="flex w-1/2 min-w-0 border-l border-og-border"
215
- style={{ backgroundColor: r ? lineBg(r.type, theme) : undefined }}
216
- >
217
- <span className="w-10 shrink-0 select-none px-1 text-right text-og-fg-subtle">
218
- {r?.newNo ?? ""}
219
- </span>
220
- <span className="whitespace-pre-wrap break-all px-1">{r?.text ?? ""}</span>
221
- </span>
222
- </div>
223
- );
224
- })}
225
- </div>
226
- );
227
- })}
228
- </div>
60
+ <PierreDiff
61
+ diff={diff}
62
+ layout={layout}
63
+ themeType={themeType}
64
+ {...(fallback !== undefined ? { fallback } : {})}
65
+ className={className}
66
+ />
229
67
  );
230
68
  }
@@ -95,7 +95,10 @@ export function EnrollmentConsent({
95
95
  className={className}
96
96
  tone="danger"
97
97
  title="Could not complete enrollment"
98
- body={errorMessage ?? "The code may have expired. Re-run the install one-liner on the machine for a fresh code."}
98
+ body={
99
+ errorMessage ??
100
+ "The code may have expired. Re-run the install one-liner on the machine for a fresh code."
101
+ }
99
102
  />
100
103
  );
101
104
  }
@@ -113,10 +116,13 @@ export function EnrollmentConsent({
113
116
  <ShieldAlertIcon className="size-5" aria-hidden />
114
117
  </span>
115
118
  <div className="min-w-0">
116
- <h1 className="text-og-md font-semibold text-og-fg">Give the agent your whole machine?</h1>
119
+ <h1 className="text-og-md font-semibold text-og-fg">
120
+ Give the agent your whole machine?
121
+ </h1>
117
122
  <p className="mt-1 text-og-base text-og-fg-muted">
118
- Approving lets the OpenGeni agent run on <span className="font-medium text-og-fg">{machine.machineName}</span> with
119
- full access. This is your real computer — not a sandbox.
123
+ Approving lets the OpenGeni agent run on{" "}
124
+ <span className="font-medium text-og-fg">{machine.machineName}</span> with full access.
125
+ This is your real computer — not a sandbox.
120
126
  </p>
121
127
  </div>
122
128
  </div>
@@ -127,7 +133,9 @@ export function EnrollmentConsent({
127
133
  <span className="font-og-mono text-og-fg-subtle">
128
134
  {machine.os}/{machine.arch}
129
135
  </span>
130
- <span aria-hidden className="text-og-fg-subtle">·</span>
136
+ <span aria-hidden className="text-og-fg-subtle">
137
+ ·
138
+ </span>
131
139
  <span className="text-og-fg-subtle">
132
140
  code <span className="font-og-mono font-medium text-og-fg">{userCode}</span>
133
141
  </span>
@@ -171,15 +179,16 @@ export function EnrollmentConsent({
171
179
  Also let the agent control my mouse & keyboard
172
180
  </span>
173
181
  <span className="mt-0.5 block text-og-sm text-og-fg-muted">
174
- Optional. Enables computer-use — the agent can move the pointer, type, and click on this machine's desktop. Leave
175
- off to let it only watch.
182
+ Optional. Enables computer-use — the agent can move the pointer, type, and click on
183
+ this machine's desktop. Leave off to let it only watch.
176
184
  </span>
177
185
  </span>
178
186
  </label>
179
187
  ) : (
180
188
  <p className="flex items-start gap-2 rounded-og-md border border-og-border bg-og-bg px-3 py-2 text-og-sm text-og-fg-muted">
181
189
  <CircleAlertIcon className="mt-px size-3.5 shrink-0 text-og-fg-subtle" aria-hidden />
182
- This machine has no display, so screen control isn't available — files, terminal, and git only.
190
+ This machine has no display, so screen control isn't available — files, terminal, and git
191
+ only.
183
192
  </p>
184
193
  )}
185
194
 
@@ -225,7 +234,11 @@ function ConsentResult({
225
234
  ? "border-og-status-failed/30"
226
235
  : "border-og-border";
227
236
  const iconClass =
228
- tone === "ok" ? "text-og-status-running" : tone === "danger" ? "text-og-status-failed" : "text-og-fg-subtle";
237
+ tone === "ok"
238
+ ? "text-og-status-running"
239
+ : tone === "danger"
240
+ ? "text-og-status-failed"
241
+ : "text-og-fg-subtle";
229
242
  return (
230
243
  <div
231
244
  data-enrollment-result={tone}
@@ -235,7 +248,12 @@ function ConsentResult({
235
248
  className,
236
249
  )}
237
250
  >
238
- <span className={cn("flex size-10 items-center justify-center rounded-full bg-og-surface-2", iconClass)}>
251
+ <span
252
+ className={cn(
253
+ "flex size-10 items-center justify-center rounded-full bg-og-surface-2",
254
+ iconClass,
255
+ )}
256
+ >
239
257
  <LaptopIcon className="size-5" aria-hidden />
240
258
  </span>
241
259
  <h1 className="text-og-md font-semibold text-og-fg">{title}</h1>
@@ -99,7 +99,9 @@ export function EnrollmentDeviceFlow({
99
99
 
100
100
  {installCommand ? (
101
101
  <div className="flex flex-col gap-1.5">
102
- <span className="text-[11px] font-medium uppercase tracking-wide text-og-fg-subtle">1 · Run on the machine</span>
102
+ <span className="text-[11px] font-medium uppercase tracking-wide text-og-fg-subtle">
103
+ 1 · Run on the machine
104
+ </span>
103
105
  <div className="flex items-center justify-between gap-2 overflow-hidden rounded-og-md border border-og-border bg-og-bg px-2.5 py-1.5">
104
106
  <code className="flex min-w-0 items-center gap-2 overflow-x-auto font-og-mono text-[12px] text-og-fg">
105
107
  <TerminalIcon className="size-3.5 shrink-0 text-og-fg-subtle" aria-hidden />
@@ -164,13 +166,14 @@ export function EnrollmentDeviceFlow({
164
166
  {awaitingCode ? (
165
167
  <>
166
168
  The one-liner prints a short code. Open{" "}
167
- <span className="font-og-mono text-og-fg-muted">{verificationUri}</span> and confirm the code shown there
168
- matches.
169
+ <span className="font-og-mono text-og-fg-muted">{verificationUri}</span> and confirm the
170
+ code shown there matches.
169
171
  </>
170
172
  ) : (
171
173
  <>
172
- Confirm code <span className="font-og-mono text-og-fg-muted">{userCode}</span> — the one printed by the
173
- one-liner — at <span className="font-og-mono text-og-fg-muted">{verificationUri}</span>.
174
+ Confirm code <span className="font-og-mono text-og-fg-muted">{userCode}</span> — the one
175
+ printed by the one-liner — at{" "}
176
+ <span className="font-og-mono text-og-fg-muted">{verificationUri}</span>.
174
177
  {typeof expiresInSeconds === "number" ? (
175
178
  <> Expires in {Math.max(0, Math.round(expiresInSeconds / 60))} min.</>
176
179
  ) : null}