@opengeni/react 0.3.0 → 0.4.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 (40) hide show
  1. package/dist/index.d.ts +1035 -14
  2. package/dist/index.js +6867 -1884
  3. package/dist/index.js.map +1 -1
  4. package/package.json +65 -2
  5. package/src/client.ts +21 -0
  6. package/src/components/code-editor.tsx +398 -0
  7. package/src/components/desktop-viewer.tsx +647 -0
  8. package/src/components/diff-view.tsx +230 -0
  9. package/src/components/file-browser.tsx +838 -0
  10. package/src/components/message-timeline.tsx +70 -196
  11. package/src/components/pierre-diff.tsx +140 -0
  12. package/src/components/pierre-file.tsx +142 -0
  13. package/src/components/sandbox-files.tsx +509 -0
  14. package/src/components/sandbox-terminal.tsx +425 -0
  15. package/src/components/workspace-dock.tsx +247 -0
  16. package/src/hooks/use-desktop-stream.ts +214 -0
  17. package/src/hooks/use-sandbox-files.ts +670 -0
  18. package/src/hooks/use-sandbox-git.ts +105 -0
  19. package/src/hooks/use-sandbox-terminal.ts +226 -0
  20. package/src/hooks/use-session-capabilities.ts +415 -0
  21. package/src/hooks/use-terminal-stream.ts +207 -0
  22. package/src/index.ts +111 -2
  23. package/src/lib/cn.ts +20 -1
  24. package/src/lib/git-patch.ts +37 -0
  25. package/src/lib/use-theme-type.ts +40 -0
  26. package/src/lib/xterm-theme.ts +34 -0
  27. package/src/timeline/activity-rail.tsx +207 -0
  28. package/src/timeline/disclosure-context.tsx +34 -0
  29. package/src/timeline/index.ts +85 -0
  30. package/src/timeline/parsers.ts +248 -0
  31. package/src/{timeline.ts → timeline/projection.ts} +59 -134
  32. package/src/timeline/registry.ts +96 -0
  33. package/src/timeline/screenshot-lightbox.tsx +152 -0
  34. package/src/timeline/shared.tsx +481 -0
  35. package/src/timeline/tool-diff.tsx +91 -0
  36. package/src/timeline/tool-renderers.tsx +882 -0
  37. package/src/timeline/turn-summary.tsx +125 -0
  38. package/src/timeline/types.ts +131 -0
  39. package/src/types/external.d.ts +7 -0
  40. package/styles/index.css +72 -0
@@ -0,0 +1,125 @@
1
+ import { CheckIcon, ChevronRightIcon, CircleSlashIcon, TriangleAlertIcon } from "lucide-react";
2
+ import { useState } from "react";
3
+ import { Collapsible } from "radix-ui";
4
+ import { cn } from "../lib/cn";
5
+ import { useForcedDefaultOpen } from "./disclosure-context";
6
+ import { applyPatchOps, isApplyPatch } from "./parsers";
7
+ import { rawTypeOf } from "./registry";
8
+ import type { ActivityItem } from "./types";
9
+
10
+ /* ----------------------------------------------------------------------------
11
+ Turn summary
12
+
13
+ A completed (or failed/cancelled) turn's activity folds behind one quiet
14
+ summary chip: "N steps · M files · K commands · 1 screenshot". The chip is the
15
+ default surface; expanding it reveals the full activity rail (the caller's
16
+ rendered rows). A live turn never folds — render its rows directly.
17
+
18
+ This keeps the timeline calm: a finished turn is a single line until the
19
+ reader chooses to look inside it.
20
+ -------------------------------------------------------------------------- */
21
+
22
+ export type TurnOutcome = "complete" | "failed" | "cancelled";
23
+
24
+ export type TurnSummaryProps = {
25
+ /** The activity items in the turn (used only to compute the facet counts). */
26
+ items: ActivityItem[];
27
+ outcome: TurnOutcome;
28
+ /** A short failure reason shown inline on a failed chip (never hidden). */
29
+ failureText?: string | undefined;
30
+ /** Start expanded. */
31
+ defaultOpen?: boolean | undefined;
32
+ /** The rendered activity rail revealed on expand. */
33
+ children: React.ReactNode;
34
+ };
35
+
36
+ export function TurnSummary({ items, outcome, failureText, defaultOpen, children }: TurnSummaryProps) {
37
+ // An explicit `defaultOpen` always wins; otherwise an ancestor may seed it
38
+ // (screenshot instrumentation); otherwise the turn starts folded.
39
+ const forcedDefaultOpen = useForcedDefaultOpen();
40
+ const [open, setOpen] = useState(defaultOpen ?? forcedDefaultOpen ?? false);
41
+ const facets = summarizeTurn(items);
42
+
43
+ return (
44
+ <Collapsible.Root open={open} onOpenChange={setOpen} className="animate-og-enter">
45
+ <Collapsible.Trigger
46
+ className={cn(
47
+ "group flex w-full items-center gap-2.5 rounded-og-md border px-3 py-2 text-left text-og-base transition-colors",
48
+ // Only a failed turn earns the one filled/tinted card in the timeline;
49
+ // complete and cancelled stay flat and calm.
50
+ outcome === "failed"
51
+ ? "border-og-status-failed/30 bg-og-status-failed/[0.06] hover:border-og-status-failed/50"
52
+ : "border-og-border bg-og-surface-1/50 hover:border-og-border-strong",
53
+ )}
54
+ >
55
+ {/* Disclosure grammar matches the rows: chevron leads (far left), then the
56
+ outcome glyph, then the facets — one expand affordance side everywhere. */}
57
+ <ChevronRightIcon className="size-3.5 shrink-0 text-og-fg-subtle transition-transform duration-150 group-data-[state=open]:rotate-90" />
58
+ {/* Only the exceptional outcomes earn a filled tinted circle. A clean
59
+ (complete) run draws a bare muted check — zero colored fills, so the
60
+ eye is pulled only to a turn that needs attention. */}
61
+ <span
62
+ className={cn(
63
+ "inline-flex size-5 shrink-0 items-center justify-center rounded-full",
64
+ outcome === "failed"
65
+ ? "bg-og-status-failed/15 text-og-status-failed"
66
+ : outcome === "cancelled"
67
+ ? "bg-og-fg-subtle/15 text-og-fg-subtle"
68
+ : "text-og-fg-subtle",
69
+ )}
70
+ >
71
+ {outcome === "failed" ? (
72
+ <TriangleAlertIcon className="size-3" />
73
+ ) : outcome === "cancelled" ? (
74
+ <CircleSlashIcon className="size-3" />
75
+ ) : (
76
+ <CheckIcon className="size-3.5" />
77
+ )}
78
+ </span>
79
+ <span className="min-w-0 flex-1 truncate text-og-fg-muted">
80
+ {facets}
81
+ {outcome === "failed" && failureText ? (
82
+ <span className="text-og-status-failed"> · {failureText}</span>
83
+ ) : null}
84
+ {outcome === "cancelled" ? <span className="text-og-fg-subtle"> · interrupted</span> : null}
85
+ </span>
86
+ </Collapsible.Trigger>
87
+ <Collapsible.Content className="overflow-hidden data-[state=closed]:animate-og-collapse data-[state=open]:animate-og-expand">
88
+ <div className="pt-2">{children}</div>
89
+ </Collapsible.Content>
90
+ </Collapsible.Root>
91
+ );
92
+ }
93
+
94
+ /** Compose the facet summary line ("14 steps · 3 files · 2 commands · 1 screenshot · 4m"). */
95
+ function summarizeTurn(items: ActivityItem[]): string {
96
+ let files = 0;
97
+ let commands = 0;
98
+ let screenshots = 0;
99
+ for (const item of items) {
100
+ if (item.kind !== "tool-call") {
101
+ continue;
102
+ }
103
+ // `item` is narrowed to ToolCallItem by the guard above — no cast needed.
104
+ if (isApplyPatch(item)) {
105
+ files += applyPatchOps(item.raw).length;
106
+ } else if (item.name === "exec_command") {
107
+ commands += 1;
108
+ } else if (rawTypeOf(item) === "computer_call" || item.name === "computer_call") {
109
+ if (typeof item.output === "string" && item.output.startsWith("data:image")) {
110
+ screenshots += 1;
111
+ }
112
+ }
113
+ }
114
+ const parts = [`${items.length} ${items.length === 1 ? "step" : "steps"}`];
115
+ if (files) {
116
+ parts.push(`${files} ${files === 1 ? "file" : "files"} edited`);
117
+ }
118
+ if (commands) {
119
+ parts.push(`${commands} ${commands === 1 ? "command" : "commands"}`);
120
+ }
121
+ if (screenshots) {
122
+ parts.push(`${screenshots} ${screenshots === 1 ? "screenshot" : "screenshots"}`);
123
+ }
124
+ return parts.join(" · ");
125
+ }
@@ -0,0 +1,131 @@
1
+ import type { ResourceRef, SessionStatus, ToolRef } from "@opengeni/sdk";
2
+
3
+ /* ----------------------------------------------------------------------------
4
+ Timeline item types
5
+
6
+ The projected, renderable shapes that `buildTimeline` folds a session's raw
7
+ event log into. These are the data contract the renderer registry and every
8
+ row component consume — the SINGLE SOURCE OF TRUTH used by both the live app
9
+ and the component demo.
10
+ -------------------------------------------------------------------------- */
11
+
12
+ export type UserMessageItem = {
13
+ kind: "user-message";
14
+ id: string;
15
+ text: string;
16
+ /** Resources attached to this message (file uploads, repositories). */
17
+ resources: ResourceRef[];
18
+ /** Tools requested for the turn this message starts. */
19
+ tools: ToolRef[];
20
+ occurredAt: string;
21
+ };
22
+
23
+ export type AgentMessageItem = {
24
+ kind: "agent-message";
25
+ id: string;
26
+ turnId: string | null;
27
+ text: string;
28
+ /** Still receiving deltas (no completed/turn-end seen yet). */
29
+ streaming: boolean;
30
+ occurredAt: string;
31
+ };
32
+
33
+ export type ReasoningItem = {
34
+ kind: "reasoning";
35
+ id: string;
36
+ turnId: string | null;
37
+ text: string;
38
+ streaming: boolean;
39
+ occurredAt: string;
40
+ };
41
+
42
+ export type ToolCallItem = {
43
+ kind: "tool-call";
44
+ id: string;
45
+ turnId: string | null;
46
+ callId: string | null;
47
+ name: string;
48
+ arguments: unknown;
49
+ output: unknown;
50
+ /**
51
+ * The provider-native tool item (`agent.toolCall.created.payload.raw`). Carries
52
+ * `type` (e.g. `apply_patch_call`, `computer_call`, `hosted_tool_call`) and the
53
+ * tool-specific fields the per-tool renderers read (`operation`, `action`,
54
+ * `providerData`, …). `undefined` for first-party MCP tools, which carry their
55
+ * payload in `arguments`/`output` instead.
56
+ */
57
+ raw: unknown;
58
+ status: "running" | "complete" | "failed" | "cancelled";
59
+ occurredAt: string;
60
+ };
61
+
62
+ /**
63
+ * An orchestration call against another session — the manager spawning or
64
+ * messaging a worker. Rendered as a first-class "worker" row, not a generic
65
+ * tool call.
66
+ */
67
+ export type WorkerItem = {
68
+ kind: "worker";
69
+ id: string;
70
+ turnId: string | null;
71
+ callId: string | null;
72
+ action: "spawn" | "message";
73
+ /** The worker's initial message / the message sent to it, when parseable. */
74
+ prompt: string | null;
75
+ /** The target/spawned worker session id, when parseable from args/output. */
76
+ workerSessionId: string | null;
77
+ status: "running" | "complete" | "failed" | "cancelled";
78
+ occurredAt: string;
79
+ };
80
+
81
+ export type SandboxItem = {
82
+ kind: "sandbox";
83
+ id: string;
84
+ turnId: string | null;
85
+ name: string;
86
+ command: string | null;
87
+ output: string;
88
+ status: "running" | "complete" | "failed" | "cancelled";
89
+ occurredAt: string;
90
+ };
91
+
92
+ export type SessionStatusItem = {
93
+ kind: "session-status";
94
+ id: string;
95
+ status: SessionStatus;
96
+ occurredAt: string;
97
+ };
98
+
99
+ export type GoalItem = {
100
+ kind: "goal";
101
+ id: string;
102
+ action: "set" | "updated" | "completed" | "paused" | "resumed" | "continuation";
103
+ text: string | null;
104
+ occurredAt: string;
105
+ };
106
+
107
+ export type NoticeItem = {
108
+ kind: "notice";
109
+ id: string;
110
+ tone: "waiting" | "cancelled" | "failed";
111
+ text: string;
112
+ occurredAt: string;
113
+ };
114
+
115
+ export type TimelineItem =
116
+ | UserMessageItem
117
+ | AgentMessageItem
118
+ | ReasoningItem
119
+ | ToolCallItem
120
+ | WorkerItem
121
+ | SandboxItem
122
+ | SessionStatusItem
123
+ | GoalItem
124
+ | NoticeItem;
125
+
126
+ /** Activity items cluster between chat messages (reasoning, tools, workers, sandbox). */
127
+ export type ActivityItem = ReasoningItem | ToolCallItem | WorkerItem | SandboxItem;
128
+
129
+ export type TimelineGroup =
130
+ | { kind: "item"; item: TimelineItem }
131
+ | { kind: "activity"; id: string; items: ActivityItem[] };
@@ -0,0 +1,7 @@
1
+ // Ambient declarations for optional, untyped peer dependencies that are only
2
+ // ever reached via a lazy dynamic import inside a client-side effect. The
3
+ // concrete shape is narrowed at the call site with an explicit cast, so a bare
4
+ // module declaration is enough to keep `tsc` happy without pulling DOM-only
5
+ // libs into the type graph.
6
+
7
+ declare module "@novnc/novnc";
package/styles/index.css CHANGED
@@ -20,6 +20,18 @@
20
20
  --font-og-sans: var(--og-font-sans);
21
21
  --font-og-mono: var(--og-font-mono);
22
22
 
23
+ /* Type ramp — a real 4-step scale (no half-pixel hand-tuning). Every
24
+ timeline string maps to one of these via `text-og-{xs,sm,base,md}`;
25
+ mono reuses the same steps. */
26
+ --text-og-xs: 11px;
27
+ --text-og-xs--line-height: 1.45;
28
+ --text-og-sm: 12px;
29
+ --text-og-sm--line-height: 1.5;
30
+ --text-og-base: 13px;
31
+ --text-og-base--line-height: 1.55;
32
+ --text-og-md: 15px;
33
+ --text-og-md--line-height: 1.6;
34
+
23
35
  --color-og-bg: var(--og-color-bg);
24
36
  --color-og-surface-1: var(--og-color-surface-1);
25
37
  --color-og-surface-2: var(--og-color-surface-2);
@@ -63,6 +75,44 @@
63
75
  --animate-og-blink: og-blink 1s steps(2, start) infinite;
64
76
  --animate-og-shimmer: og-shimmer 2.2s linear infinite;
65
77
  --animate-og-spin: og-spin 0.9s linear infinite;
78
+ --animate-og-expand: og-expand var(--og-duration-fast) var(--og-ease-in-out);
79
+ --animate-og-collapse: og-collapse var(--og-duration-fast) var(--og-ease-in-out);
80
+ }
81
+
82
+ /* Radix Collapsible disclosure: height auto-animates via the content var. Used
83
+ by the timeline tool rows so an expand glides open instead of snapping.
84
+
85
+ Height carries the motion; opacity is a SUBTLE fade that resolves slightly
86
+ ahead of the height so the body never pops in at full size, but also never
87
+ flickers half-transparent mid-glide. The ease is a symmetric ease-in-out
88
+ (no overshoot), so open and close feel like the same short, calm motion with
89
+ no double-bounce. */
90
+ @keyframes og-expand {
91
+ from {
92
+ height: 0;
93
+ opacity: 0;
94
+ }
95
+ 60% {
96
+ opacity: 1;
97
+ }
98
+ to {
99
+ height: var(--radix-collapsible-content-height);
100
+ opacity: 1;
101
+ }
102
+ }
103
+
104
+ @keyframes og-collapse {
105
+ from {
106
+ height: var(--radix-collapsible-content-height);
107
+ opacity: 1;
108
+ }
109
+ 40% {
110
+ opacity: 0;
111
+ }
112
+ to {
113
+ height: 0;
114
+ opacity: 0;
115
+ }
66
116
  }
67
117
 
68
118
  /* Entry: messages and timeline rows slide in just enough to feel placed. */
@@ -155,3 +205,25 @@
155
205
  scroll-behavior: auto !important;
156
206
  }
157
207
  }
208
+
209
+ /* ----------------------------------------------------------------------------
210
+ xterm.js hardening (SandboxTerminal)
211
+
212
+ xterm 6's DOM renderer measures glyph widths by stamping a scratch element
213
+ (`.xterm-width-cache-measure-container`) full of repeated characters
214
+ (`]]]]bbbb yyyy…`). The shipped xterm.css hides the older
215
+ `.xterm-char-measure-element` but NOT this newer container, so on first paint
216
+ it leaks a row of garbage glyphs across the top of the terminal. Clip every
217
+ xterm measurement scratch element off-screen so the transcript is the only
218
+ thing visible.
219
+ -------------------------------------------------------------------------- */
220
+ .xterm .xterm-width-cache-measure-container,
221
+ .xterm-width-cache-measure-container,
222
+ .xterm .xterm-char-measure-element {
223
+ position: absolute !important;
224
+ top: 0;
225
+ left: 0;
226
+ visibility: hidden !important;
227
+ pointer-events: none !important;
228
+ z-index: -1 !important;
229
+ }