@opengeni/react 0.3.1 → 0.5.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.
- package/dist/index.d.ts +1055 -27
- package/dist/index.js +6993 -1954
- package/dist/index.js.map +1 -1
- package/package.json +65 -2
- package/src/client.ts +22 -0
- package/src/components/code-editor.tsx +398 -0
- package/src/components/desktop-viewer.tsx +647 -0
- package/src/components/diff-view.tsx +230 -0
- package/src/components/file-browser.tsx +838 -0
- package/src/components/fleet-tile.tsx +5 -0
- package/src/components/message-timeline.tsx +70 -196
- package/src/components/pierre-diff.tsx +140 -0
- package/src/components/pierre-file.tsx +142 -0
- package/src/components/sandbox-files.tsx +509 -0
- package/src/components/sandbox-terminal.tsx +425 -0
- package/src/components/workspace-dock.tsx +247 -0
- package/src/hooks/use-desktop-stream.ts +214 -0
- package/src/hooks/use-sandbox-files.ts +670 -0
- package/src/hooks/use-sandbox-git.ts +105 -0
- package/src/hooks/use-sandbox-terminal.ts +226 -0
- package/src/hooks/use-session-capabilities.ts +415 -0
- package/src/hooks/use-session.ts +80 -12
- package/src/hooks/use-terminal-stream.ts +207 -0
- package/src/index.ts +112 -3
- package/src/lib/cn.ts +20 -1
- package/src/lib/git-patch.ts +43 -0
- package/src/lib/use-theme-type.ts +40 -0
- package/src/lib/xterm-theme.ts +34 -0
- package/src/timeline/activity-rail.tsx +207 -0
- package/src/timeline/disclosure-context.tsx +34 -0
- package/src/timeline/index.ts +85 -0
- package/src/timeline/parsers.ts +253 -0
- package/src/{timeline.ts → timeline/projection.ts} +59 -134
- package/src/timeline/registry.ts +96 -0
- package/src/timeline/screenshot-lightbox.tsx +152 -0
- package/src/timeline/shared.tsx +481 -0
- package/src/timeline/tool-diff.tsx +91 -0
- package/src/timeline/tool-renderers.tsx +882 -0
- package/src/timeline/turn-summary.tsx +125 -0
- package/src/timeline/types.ts +131 -0
- package/src/types/external.d.ts +7 -0
- 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
|
+
}
|