@opengeni/react 0.12.0 → 0.15.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/README.md +20 -14
- package/dist/chunk-TOJR776I.js +2280 -0
- package/dist/chunk-TOJR776I.js.map +1 -0
- package/dist/index.d.ts +1040 -363
- package/dist/index.js +10938 -6281
- package/dist/index.js.map +1 -1
- package/dist/{machines-BeZ3bD3t.d.ts → machines-BpdwuQcD.d.ts} +134 -14
- package/dist/machines.d.ts +1 -1
- package/dist/machines.js +23 -1
- package/package.json +20 -12
- package/src/approvals.ts +16 -4
- package/src/client.ts +41 -5
- package/src/commands/index.ts +1 -7
- package/src/commands/registry.ts +46 -12
- package/src/components/chat-composer.tsx +417 -69
- package/src/components/code-editor.tsx +28 -36
- package/src/components/command-palette.tsx +26 -5
- package/src/components/desktop-viewer.tsx +29 -16
- package/src/components/diff-view.tsx +27 -189
- package/src/components/enrollment-consent.tsx +28 -10
- package/src/components/enrollment-device-flow.tsx +8 -5
- package/src/components/file-browser.tsx +190 -56
- package/src/components/fleet-tile.tsx +13 -4
- package/src/components/machine-card.tsx +93 -15
- package/src/components/machine-dock-bar.tsx +20 -8
- package/src/components/machine-health-pill.tsx +68 -0
- package/src/components/machine-metrics.tsx +28 -19
- package/src/components/machine-status-pill.tsx +26 -5
- package/src/components/machines/health.ts +146 -0
- package/src/components/machines/machine-detail.tsx +220 -0
- package/src/components/machines/metric-history-chart.tsx +298 -0
- package/src/components/machines/metric-sparkline.tsx +76 -0
- package/src/components/machines/series.ts +113 -0
- package/src/components/machines-dashboard.tsx +21 -3
- package/src/components/markdown.tsx +39 -9
- package/src/components/message-timeline.tsx +633 -109
- package/src/components/pierre-diff.tsx +83 -15
- package/src/components/pierre-file.tsx +10 -9
- package/src/components/queue-surface.tsx +578 -0
- package/src/components/sandbox-files.tsx +193 -225
- package/src/components/sandbox-terminal.tsx +256 -88
- package/src/components/sandbox-workspace.tsx +957 -0
- package/src/components/session-status.tsx +22 -2
- package/src/components/workbench-changes.tsx +585 -0
- package/src/components/workspace-dock.tsx +213 -59
- package/src/hooks/internal.ts +5 -2
- package/src/hooks/use-available-models.ts +7 -2
- package/src/hooks/use-billing-usage.ts +4 -1
- package/src/hooks/use-codex-accounts.ts +74 -44
- package/src/hooks/use-composer.ts +414 -63
- package/src/hooks/use-environments.ts +92 -47
- package/src/hooks/use-file-attachments.ts +101 -55
- package/src/hooks/use-goal.ts +52 -16
- package/src/hooks/use-machine-chip.ts +134 -0
- package/src/hooks/use-machines.ts +34 -16
- package/src/hooks/use-packs.ts +24 -19
- package/src/hooks/use-relay-frame-stream.ts +5 -2
- package/src/hooks/use-rigs.ts +335 -0
- package/src/hooks/use-sandbox-files.ts +213 -39
- package/src/hooks/use-sandbox-git.ts +188 -11
- package/src/hooks/use-sandbox-terminal.ts +18 -14
- package/src/hooks/use-scheduled-tasks.ts +10 -2
- package/src/hooks/use-session-capabilities.ts +77 -20
- package/src/hooks/use-session-control.ts +50 -23
- package/src/hooks/use-session-events.ts +50 -21
- package/src/hooks/use-session-lineage.ts +128 -0
- package/src/hooks/use-session.ts +53 -30
- package/src/hooks/use-slash-commands.ts +6 -4
- package/src/hooks/use-turn-queue.ts +202 -147
- package/src/hooks/use-windowed-sections.ts +204 -0
- package/src/hooks/use-workspace-capture.ts +233 -0
- package/src/hooks/use-workspace-edit.ts +231 -0
- package/src/hooks/use-workspace-sessions.ts +48 -5
- package/src/hooks/use-workspaces.ts +18 -15
- package/src/index.ts +133 -23
- package/src/lib/format.ts +3 -3
- package/src/lib/xterm-renderer.ts +65 -0
- package/src/lib/xterm-theme.ts +224 -18
- package/src/machines.ts +29 -3
- package/src/provider.tsx +195 -6
- package/src/timeline/activity-rail.tsx +213 -54
- package/src/timeline/disclosure-context.tsx +12 -2
- package/src/timeline/index.ts +21 -2
- package/src/timeline/parsers.ts +87 -16
- package/src/timeline/projection.ts +412 -87
- package/src/timeline/shared.tsx +111 -24
- package/src/timeline/tool-diff.tsx +24 -20
- package/src/timeline/tool-renderers.tsx +98 -21
- package/src/timeline/turn-summary.tsx +76 -23
- package/src/timeline/types.ts +96 -12
- package/styles/index.css +22 -0
- package/styles/tokens.css +2 -2
- package/dist/chunk-5TIXRCSI.js +0 -1291
- package/dist/chunk-5TIXRCSI.js.map +0 -1
package/src/index.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// oxlint-disable-next-line typescript/triple-slash-reference -- package consumers must load the ambient type for this optional untyped peer without emitting a runtime import.
|
|
2
|
+
/// <reference path="./types/external.d.ts" />
|
|
3
|
+
|
|
1
4
|
// @opengeni/react — hooks + styled components on @opengeni/sdk.
|
|
2
5
|
//
|
|
3
6
|
// Import the styles once in your Tailwind entry CSS:
|
|
@@ -12,31 +15,74 @@ export type { ClientOverride, OpenGeniContextValue, OpenGeniProviderProps } from
|
|
|
12
15
|
export { useSession, isTitleEvent } from "./hooks/use-session";
|
|
13
16
|
export type { UseSessionOptions, UseSessionResult } from "./hooks/use-session";
|
|
14
17
|
export { useSessionEvents } from "./hooks/use-session-events";
|
|
15
|
-
export type {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
export type {
|
|
19
|
+
SessionEventsConnectionState,
|
|
20
|
+
UseSessionEventsOptions,
|
|
21
|
+
UseSessionEventsResult,
|
|
22
|
+
} from "./hooks/use-session-events";
|
|
20
23
|
export {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
useComposer,
|
|
25
|
+
composeSendInput,
|
|
26
|
+
shouldSteerOnKey,
|
|
27
|
+
shouldSubmitOnKey,
|
|
28
|
+
FILE_ONLY_MESSAGE_TEXT,
|
|
29
|
+
} from "./hooks/use-composer";
|
|
30
|
+
export type { ComposerSendExtras, ComposerState, UseComposerOptions } from "./hooks/use-composer";
|
|
31
|
+
export { useFileAttachments } from "./hooks/use-file-attachments";
|
|
32
|
+
export type {
|
|
33
|
+
FileAttachment,
|
|
34
|
+
UseFileAttachmentsOptions,
|
|
35
|
+
UseFileAttachmentsResult,
|
|
36
|
+
} from "./hooks/use-file-attachments";
|
|
37
|
+
export { useTurnQueue, isTurnQueueEvent } from "./hooks/use-turn-queue";
|
|
38
|
+
export type {
|
|
39
|
+
QueueMutationKind,
|
|
40
|
+
UseTurnQueueOptions,
|
|
41
|
+
UseTurnQueueResult,
|
|
28
42
|
} from "./hooks/use-turn-queue";
|
|
29
|
-
export
|
|
43
|
+
export { QueueSurface } from "./components/queue-surface";
|
|
44
|
+
export type { QueueSurfaceProps } from "./components/queue-surface";
|
|
45
|
+
export { OPEN_WORKSTREAM_CONTROL_EVENT } from "./components/chat-composer";
|
|
30
46
|
export { useGoal, isGoalEvent } from "./hooks/use-goal";
|
|
31
47
|
export type { UseGoalOptions, UseGoalResult } from "./hooks/use-goal";
|
|
32
48
|
export { useSessionControl } from "./hooks/use-session-control";
|
|
33
|
-
export type {
|
|
49
|
+
export type {
|
|
50
|
+
UseSessionControlOptions,
|
|
51
|
+
UseSessionControlResult,
|
|
52
|
+
} from "./hooks/use-session-control";
|
|
34
53
|
export { useScheduledTasks } from "./hooks/use-scheduled-tasks";
|
|
35
|
-
export type {
|
|
54
|
+
export type {
|
|
55
|
+
UseScheduledTasksOptions,
|
|
56
|
+
UseScheduledTasksResult,
|
|
57
|
+
} from "./hooks/use-scheduled-tasks";
|
|
36
58
|
export { useWorkspaceSessions } from "./hooks/use-workspace-sessions";
|
|
37
|
-
export type {
|
|
38
|
-
|
|
39
|
-
|
|
59
|
+
export type {
|
|
60
|
+
UseWorkspaceSessionsOptions,
|
|
61
|
+
UseWorkspaceSessionsResult,
|
|
62
|
+
} from "./hooks/use-workspace-sessions";
|
|
63
|
+
export { useSessionLineage, isLineageRefreshEvent } from "./hooks/use-session-lineage";
|
|
64
|
+
export type {
|
|
65
|
+
UseSessionLineageOptions,
|
|
66
|
+
UseSessionLineageResult,
|
|
67
|
+
} from "./hooks/use-session-lineage";
|
|
68
|
+
export { useVariableSets, useEnvironments } from "./hooks/use-environments";
|
|
69
|
+
export type {
|
|
70
|
+
UseVariableSetsOptions,
|
|
71
|
+
UseVariableSetsResult,
|
|
72
|
+
UseEnvironmentsOptions,
|
|
73
|
+
UseEnvironmentsResult,
|
|
74
|
+
} from "./hooks/use-environments";
|
|
75
|
+
export { useRigs, useRig, useRigVersions, useRigChanges } from "./hooks/use-rigs";
|
|
76
|
+
export type {
|
|
77
|
+
UseRigsOptions,
|
|
78
|
+
UseRigsResult,
|
|
79
|
+
UseRigOptions,
|
|
80
|
+
UseRigResult,
|
|
81
|
+
UseRigVersionsOptions,
|
|
82
|
+
UseRigVersionsResult,
|
|
83
|
+
UseRigChangesOptions,
|
|
84
|
+
UseRigChangesResult,
|
|
85
|
+
} from "./hooks/use-rigs";
|
|
40
86
|
export { usePacks } from "./hooks/use-packs";
|
|
41
87
|
export type { UsePacksOptions, UsePacksResult } from "./hooks/use-packs";
|
|
42
88
|
export { useWorkspaces } from "./hooks/use-workspaces";
|
|
@@ -44,7 +90,10 @@ export type { UseWorkspacesOptions, UseWorkspacesResult } from "./hooks/use-work
|
|
|
44
90
|
export { useBillingUsage } from "./hooks/use-billing-usage";
|
|
45
91
|
export type { UseBillingUsageOptions, UseBillingUsageResult } from "./hooks/use-billing-usage";
|
|
46
92
|
export { useAvailableModels } from "./hooks/use-available-models";
|
|
47
|
-
export type {
|
|
93
|
+
export type {
|
|
94
|
+
UseAvailableModelsOptions,
|
|
95
|
+
UseAvailableModelsResult,
|
|
96
|
+
} from "./hooks/use-available-models";
|
|
48
97
|
|
|
49
98
|
// Sandbox surfacing (Phase 5): capability negotiation + terminal/files/diff/desktop
|
|
50
99
|
export { useSessionCapabilities } from "./hooks/use-session-capabilities";
|
|
@@ -84,6 +133,27 @@ export type {
|
|
|
84
133
|
export { useSandboxGit } from "./hooks/use-sandbox-git";
|
|
85
134
|
export type { UseSandboxGitOptions, UseSandboxGitResult } from "./hooks/use-sandbox-git";
|
|
86
135
|
|
|
136
|
+
// Workbench v2 turn-end capture (the cold-paint data layer)
|
|
137
|
+
export { useWorkspaceCapture } from "./hooks/use-workspace-capture";
|
|
138
|
+
export type {
|
|
139
|
+
UseWorkspaceCaptureOptions,
|
|
140
|
+
UseWorkspaceCaptureResult,
|
|
141
|
+
} from "./hooks/use-workspace-capture";
|
|
142
|
+
export { useWorkspaceEdit } from "./hooks/use-workspace-edit";
|
|
143
|
+
export type {
|
|
144
|
+
UseWorkspaceEditOptions,
|
|
145
|
+
UseWorkspaceEditResult,
|
|
146
|
+
WorkspaceEditConflict,
|
|
147
|
+
WorkspaceEditState,
|
|
148
|
+
} from "./hooks/use-workspace-edit";
|
|
149
|
+
export { deriveMachineChip, formatAsOf, useMachineChip } from "./hooks/use-machine-chip";
|
|
150
|
+
export type {
|
|
151
|
+
DeriveMachineChipInput,
|
|
152
|
+
MachineChip,
|
|
153
|
+
MachineChipState,
|
|
154
|
+
UseMachineChipOptions,
|
|
155
|
+
} from "./hooks/use-machine-chip";
|
|
156
|
+
|
|
87
157
|
// Pending-approvals projection
|
|
88
158
|
export { approvalsFromRequiresAction, projectPendingApprovals } from "./approvals";
|
|
89
159
|
export type { PendingApproval } from "./approvals";
|
|
@@ -100,6 +170,7 @@ export {
|
|
|
100
170
|
export type {
|
|
101
171
|
ActivityItem,
|
|
102
172
|
AgentMessageItem,
|
|
173
|
+
AuthNeededItem,
|
|
103
174
|
GoalItem,
|
|
104
175
|
NoticeItem,
|
|
105
176
|
ReasoningItem,
|
|
@@ -114,7 +185,12 @@ export type {
|
|
|
114
185
|
} from "./timeline";
|
|
115
186
|
|
|
116
187
|
// Tool-renderer registry + the per-tool renderers (the timeline's extension API)
|
|
117
|
-
export {
|
|
188
|
+
export {
|
|
189
|
+
createDefaultToolRegistry,
|
|
190
|
+
createToolRegistry,
|
|
191
|
+
defaultToolRegistry,
|
|
192
|
+
rawTypeOf,
|
|
193
|
+
} from "./timeline";
|
|
118
194
|
export type {
|
|
119
195
|
CreateToolRegistryOptions,
|
|
120
196
|
ToolRegistry,
|
|
@@ -206,7 +282,11 @@ export type { MessageTimelineProps } from "./components/message-timeline";
|
|
|
206
282
|
export { Markdown } from "./components/markdown";
|
|
207
283
|
export type { MarkdownProps } from "./components/markdown";
|
|
208
284
|
export { SessionStatus, StatusDot, SESSION_STATUS_META } from "./components/session-status";
|
|
209
|
-
export type {
|
|
285
|
+
export type {
|
|
286
|
+
SessionStatusProps,
|
|
287
|
+
StatusDotProps,
|
|
288
|
+
SessionStatusMeta,
|
|
289
|
+
} from "./components/session-status";
|
|
210
290
|
export { FleetTile, sessionDisplayTitle } from "./components/fleet-tile";
|
|
211
291
|
export type { FleetTileProps } from "./components/fleet-tile";
|
|
212
292
|
|
|
@@ -230,12 +310,33 @@ export type { DesktopViewerProps } from "./components/desktop-viewer";
|
|
|
230
310
|
export { WorkspaceDock } from "./components/workspace-dock";
|
|
231
311
|
export type { WorkspaceDockProps, WorkspaceTab } from "./components/workspace-dock";
|
|
232
312
|
|
|
313
|
+
// The embeddable dock "brain" (Workbench v2, M4): the whole session workspace as
|
|
314
|
+
// one component + the tab-building hook + the pre-paint default-tab helper.
|
|
315
|
+
export {
|
|
316
|
+
SandboxWorkspace,
|
|
317
|
+
useSandboxWorkspaceTabs,
|
|
318
|
+
initialWorkspaceTab,
|
|
319
|
+
WORKBENCH_TAB_CHANGES,
|
|
320
|
+
WORKBENCH_TAB_FILES,
|
|
321
|
+
} from "./components/sandbox-workspace";
|
|
322
|
+
export type {
|
|
323
|
+
SandboxWorkspaceProps,
|
|
324
|
+
UseSandboxWorkspaceTabsOptions,
|
|
325
|
+
UseSandboxWorkspaceTabsResult,
|
|
326
|
+
WorkspaceMachine,
|
|
327
|
+
WorkspaceNotification,
|
|
328
|
+
} from "./components/sandbox-workspace";
|
|
329
|
+
|
|
233
330
|
// Connected-machine UI moved to the "@opengeni/react/machines" subpath; re-exported
|
|
234
331
|
// here for back-compat (#144).
|
|
235
332
|
export * from "./machines";
|
|
236
333
|
// Multi-account Codex (P1): accounts list + active-switch hook.
|
|
237
334
|
export { useCodexAccounts, isCodexAccountEvent } from "./hooks/use-codex-accounts";
|
|
238
|
-
export type {
|
|
335
|
+
export type {
|
|
336
|
+
CodexAccountsClientLike,
|
|
337
|
+
UseCodexAccountsOptions,
|
|
338
|
+
UseCodexAccountsResult,
|
|
339
|
+
} from "./hooks/use-codex-accounts";
|
|
239
340
|
|
|
240
341
|
// Sandbox helpers
|
|
241
342
|
export { gitFileDiffToPatch } from "./lib/git-patch";
|
|
@@ -243,4 +344,13 @@ export { xtermThemeFromTokens } from "./lib/xterm-theme";
|
|
|
243
344
|
|
|
244
345
|
// Utilities
|
|
245
346
|
export { cn } from "./lib/cn";
|
|
246
|
-
export {
|
|
347
|
+
export {
|
|
348
|
+
CREDIT_EXHAUSTION_MESSAGE,
|
|
349
|
+
formatBytes,
|
|
350
|
+
formatRelativeTime,
|
|
351
|
+
humanizeFailureReason,
|
|
352
|
+
isCreditExhaustion,
|
|
353
|
+
stringifyPayload,
|
|
354
|
+
truncate,
|
|
355
|
+
tryParseJson,
|
|
356
|
+
} from "./lib/format";
|
package/src/lib/format.ts
CHANGED
|
@@ -140,13 +140,13 @@ export function humanizeFailureReason(reason: string | null): string | null {
|
|
|
140
140
|
normalized.includes("invalid api key") ||
|
|
141
141
|
normalized.includes("invalid_api_key") ||
|
|
142
142
|
normalized.includes("platform.openai.com/account/api-keys") ||
|
|
143
|
-
(normalized.includes("401") &&
|
|
143
|
+
(normalized.includes("401") &&
|
|
144
|
+
(normalized.includes("api key") || normalized.includes("unauthorized")));
|
|
144
145
|
if (authFailure) {
|
|
145
146
|
return "The model provider rejected this deployment's engine credentials. Sending messages won't help until the deployment's engine configuration is fixed.";
|
|
146
147
|
}
|
|
147
148
|
const quotaFailure =
|
|
148
|
-
normalized.includes("insufficient_quota") ||
|
|
149
|
-
normalized.includes("exceeded your current quota");
|
|
149
|
+
normalized.includes("insufficient_quota") || normalized.includes("exceeded your current quota");
|
|
150
150
|
if (quotaFailure) {
|
|
151
151
|
return "The model provider refused the request: this deployment's provider quota is exhausted.";
|
|
152
152
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/* ----------------------------------------------------------------------------
|
|
2
|
+
xterm renderer selection ladder: WebGL → 2D canvas → DOM.
|
|
3
|
+
|
|
4
|
+
WebGL is the fast path (GPU-composited cells — the whole point of the burst
|
|
5
|
+
responsiveness win). But a WebGL context can fail to init (headless / no GPU /
|
|
6
|
+
blocklisted driver) or be LOST at runtime (GPU reset, tab backgrounded). On
|
|
7
|
+
either, we dispose the addon and step down one tier so the terminal keeps
|
|
8
|
+
painting via the 2D canvas addon, and failing that xterm's built-in DOM
|
|
9
|
+
renderer (no addon).
|
|
10
|
+
|
|
11
|
+
`attachRenderer` is pure over its `loaders` (each closes over the xterm
|
|
12
|
+
instance and constructs+loads its addon, throwing on failure), so the ladder
|
|
13
|
+
+ context-loss downgrade unit-test without a real WebGL context.
|
|
14
|
+
-------------------------------------------------------------------------- */
|
|
15
|
+
|
|
16
|
+
export type RendererTier = "webgl" | "canvas" | "dom";
|
|
17
|
+
|
|
18
|
+
export type RendererAddon = { dispose: () => void };
|
|
19
|
+
|
|
20
|
+
/** A loader constructs + `loadAddon`s its renderer, wiring `onLoss` to the
|
|
21
|
+
* addon's context-loss event, and throws if the renderer can't initialize. */
|
|
22
|
+
export type RendererLoaders = {
|
|
23
|
+
webgl?: ((onLoss: () => void) => Promise<RendererAddon>) | undefined;
|
|
24
|
+
canvas?: (() => Promise<RendererAddon>) | undefined;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const ORDER: RendererTier[] = ["webgl", "canvas", "dom"];
|
|
28
|
+
|
|
29
|
+
/** The next lower renderer tier, or null at the bottom (DOM). Pure. */
|
|
30
|
+
export function nextRendererTier(tier: RendererTier): RendererTier | null {
|
|
31
|
+
const i = ORDER.indexOf(tier);
|
|
32
|
+
return i >= 0 && i < ORDER.length - 1 ? ORDER[i + 1]! : null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Attach the highest renderer tier at or below `prefer` that initializes,
|
|
37
|
+
* reporting the settled tier via `onTier`. On a WebGL/canvas context loss the
|
|
38
|
+
* loader's `onLoss` re-enters at the next tier down, so `onTier` is called again
|
|
39
|
+
* with the downgraded tier. Returns the tier chosen on this pass.
|
|
40
|
+
*/
|
|
41
|
+
export async function attachRenderer(
|
|
42
|
+
prefer: RendererTier,
|
|
43
|
+
loaders: RendererLoaders,
|
|
44
|
+
onTier: (tier: RendererTier) => void,
|
|
45
|
+
): Promise<RendererTier> {
|
|
46
|
+
const start = Math.max(0, ORDER.indexOf(prefer));
|
|
47
|
+
for (let i = start; i < ORDER.length; i++) {
|
|
48
|
+
const tier = ORDER[i]!;
|
|
49
|
+
if (tier === "dom") break;
|
|
50
|
+
const loader = tier === "webgl" ? loaders.webgl : loaders.canvas;
|
|
51
|
+
if (!loader) continue;
|
|
52
|
+
try {
|
|
53
|
+
const downgrade = nextRendererTier(tier) ?? "dom";
|
|
54
|
+
await loader(() => {
|
|
55
|
+
void attachRenderer(downgrade, loaders, onTier);
|
|
56
|
+
});
|
|
57
|
+
onTier(tier);
|
|
58
|
+
return tier;
|
|
59
|
+
} catch {
|
|
60
|
+
// Initialization failed — fall through to the next tier down.
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
onTier("dom");
|
|
64
|
+
return "dom";
|
|
65
|
+
}
|
package/src/lib/xterm-theme.ts
CHANGED
|
@@ -1,31 +1,237 @@
|
|
|
1
1
|
import type { XtermTheme } from "../components/sandbox-terminal";
|
|
2
2
|
|
|
3
|
+
/* ----------------------------------------------------------------------------
|
|
4
|
+
xterm theming + font resolution from the `--og-*` token system.
|
|
5
|
+
|
|
6
|
+
xterm paints glyphs into a canvas (WebGL / 2D) and builds its font strings by
|
|
7
|
+
hand, so it can consume NEITHER CSS custom properties (`var(--og-font-mono)`
|
|
8
|
+
never resolves in a canvas font string) NOR a partial palette (an unset ANSI
|
|
9
|
+
slot falls back to xterm's stock VGA color). This module resolves both to
|
|
10
|
+
concrete values: a full 16-color ANSI ITheme designed against the og palette
|
|
11
|
+
for BOTH themes, and a concrete monospace family + px size.
|
|
12
|
+
|
|
13
|
+
The color-building + font-resolution cores are pure over a token `read`
|
|
14
|
+
function so they unit-test without a DOM; thin `*FromTokens` / `resolve*`
|
|
15
|
+
wrappers do the `getComputedStyle` read on the client.
|
|
16
|
+
-------------------------------------------------------------------------- */
|
|
17
|
+
|
|
18
|
+
export type ThemeMode = "dark" | "light";
|
|
19
|
+
|
|
20
|
+
/** Reads a resolved token value (already `getComputedStyle`-flattened). */
|
|
21
|
+
export type TokenReader = (name: string) => string | undefined;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The 16 ANSI colors + selection/cursor treatment, designed against the og
|
|
25
|
+
* OKLCH palette. Hues are borrowed from the og status ramp so terminal color
|
|
26
|
+
* reads as part of the same system: red≈status-failed(22), green≈status-idle
|
|
27
|
+
* (155), yellow≈status-running(80), blue≈accent(255), magenta≈status-waiting
|
|
28
|
+
* (305), cyan≈200. Dark uses lifted lightness on the dark slate ground; light
|
|
29
|
+
* uses deeper, more saturated inks on the near-white ground. Bright variants
|
|
30
|
+
* add lightness + a touch of chroma. `black`/`white` are neutral slate ramp
|
|
31
|
+
* stops (never pure #000/#fff — that clashes with the calm neutral ground).
|
|
32
|
+
*/
|
|
33
|
+
export type AnsiPalette = {
|
|
34
|
+
black: string;
|
|
35
|
+
red: string;
|
|
36
|
+
green: string;
|
|
37
|
+
yellow: string;
|
|
38
|
+
blue: string;
|
|
39
|
+
magenta: string;
|
|
40
|
+
cyan: string;
|
|
41
|
+
white: string;
|
|
42
|
+
brightBlack: string;
|
|
43
|
+
brightRed: string;
|
|
44
|
+
brightGreen: string;
|
|
45
|
+
brightYellow: string;
|
|
46
|
+
brightBlue: string;
|
|
47
|
+
brightMagenta: string;
|
|
48
|
+
brightCyan: string;
|
|
49
|
+
brightWhite: string;
|
|
50
|
+
/** Selection wash + its foreground; sits over the terminal ground. */
|
|
51
|
+
selectionBackground: string;
|
|
52
|
+
selectionForeground: string;
|
|
53
|
+
selectionInactiveBackground: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const DARK_ANSI: AnsiPalette = {
|
|
57
|
+
black: "oklch(0.3 0.014 260)",
|
|
58
|
+
red: "oklch(0.68 0.17 22)",
|
|
59
|
+
green: "oklch(0.75 0.13 155)",
|
|
60
|
+
yellow: "oklch(0.8 0.11 80)",
|
|
61
|
+
blue: "oklch(0.7 0.14 255)",
|
|
62
|
+
magenta: "oklch(0.72 0.13 305)",
|
|
63
|
+
cyan: "oklch(0.75 0.1 210)",
|
|
64
|
+
white: "oklch(0.8 0.01 260)",
|
|
65
|
+
brightBlack: "oklch(0.45 0.014 260)",
|
|
66
|
+
brightRed: "oklch(0.76 0.17 22)",
|
|
67
|
+
brightGreen: "oklch(0.82 0.14 155)",
|
|
68
|
+
brightYellow: "oklch(0.86 0.12 85)",
|
|
69
|
+
brightBlue: "oklch(0.78 0.13 255)",
|
|
70
|
+
brightMagenta: "oklch(0.8 0.13 305)",
|
|
71
|
+
brightCyan: "oklch(0.83 0.1 200)",
|
|
72
|
+
brightWhite: "oklch(0.97 0.005 260)",
|
|
73
|
+
selectionBackground: "oklch(0.72 0.15 255 / 0.34)",
|
|
74
|
+
selectionForeground: "oklch(0.985 0.005 260)",
|
|
75
|
+
selectionInactiveBackground: "oklch(0.72 0.02 260 / 0.22)",
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const LIGHT_ANSI: AnsiPalette = {
|
|
79
|
+
black: "oklch(0.35 0.015 260)",
|
|
80
|
+
red: "oklch(0.52 0.19 22)",
|
|
81
|
+
green: "oklch(0.5 0.13 155)",
|
|
82
|
+
yellow: "oklch(0.56 0.13 80)",
|
|
83
|
+
blue: "oklch(0.52 0.18 255)",
|
|
84
|
+
magenta: "oklch(0.52 0.16 305)",
|
|
85
|
+
cyan: "oklch(0.52 0.1 210)",
|
|
86
|
+
white: "oklch(0.7 0.01 260)",
|
|
87
|
+
brightBlack: "oklch(0.5 0.014 260)",
|
|
88
|
+
brightRed: "oklch(0.55 0.2 22)",
|
|
89
|
+
brightGreen: "oklch(0.54 0.14 155)",
|
|
90
|
+
brightYellow: "oklch(0.6 0.14 80)",
|
|
91
|
+
brightBlue: "oklch(0.48 0.19 255)",
|
|
92
|
+
brightMagenta: "oklch(0.54 0.17 305)",
|
|
93
|
+
brightCyan: "oklch(0.54 0.11 210)",
|
|
94
|
+
brightWhite: "oklch(0.3 0.015 260)",
|
|
95
|
+
selectionBackground: "oklch(0.55 0.18 255 / 0.24)",
|
|
96
|
+
selectionForeground: "oklch(0.21 0.015 260)",
|
|
97
|
+
selectionInactiveBackground: "oklch(0.6 0.02 260 / 0.16)",
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/** The designed ANSI-16 palette for a theme mode. Pure. */
|
|
101
|
+
export function ogAnsiPalette(mode: ThemeMode): AnsiPalette {
|
|
102
|
+
return mode === "light" ? LIGHT_ANSI : DARK_ANSI;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Build a COMPLETE xterm ITheme from token values. Background/foreground/cursor
|
|
107
|
+
* come from the live `--og-*` tokens (so an embedder's rebrand flows through);
|
|
108
|
+
* the ANSI-16 + selection come from the designed palette for `mode`. Every ANSI
|
|
109
|
+
* slot is always set — xterm never falls back to its stock VGA colors. Pure.
|
|
110
|
+
*/
|
|
111
|
+
export function buildXtermTheme(read: TokenReader, mode: ThemeMode): XtermTheme {
|
|
112
|
+
const ansi = ogAnsiPalette(mode);
|
|
113
|
+
const bgFallback = mode === "light" ? "oklch(0.978 0.003 260)" : "oklch(0.155 0.012 260)";
|
|
114
|
+
const fgFallback = mode === "light" ? "oklch(0.21 0.015 260)" : "oklch(0.955 0.005 260)";
|
|
115
|
+
const bg = read("--og-color-bg") ?? bgFallback;
|
|
116
|
+
const fg = read("--og-color-fg") ?? fgFallback;
|
|
117
|
+
const accent = read("--og-color-accent") ?? ansi.brightBlue;
|
|
118
|
+
return {
|
|
119
|
+
// Match the dock surface the terminal paints on (`--og-color-bg`).
|
|
120
|
+
background: bg,
|
|
121
|
+
foreground: fg,
|
|
122
|
+
cursor: accent,
|
|
123
|
+
cursorAccent: bg,
|
|
124
|
+
selectionBackground: ansi.selectionBackground,
|
|
125
|
+
selectionForeground: ansi.selectionForeground,
|
|
126
|
+
selectionInactiveBackground: ansi.selectionInactiveBackground,
|
|
127
|
+
black: ansi.black,
|
|
128
|
+
red: ansi.red,
|
|
129
|
+
green: ansi.green,
|
|
130
|
+
yellow: ansi.yellow,
|
|
131
|
+
blue: ansi.blue,
|
|
132
|
+
magenta: ansi.magenta,
|
|
133
|
+
cyan: ansi.cyan,
|
|
134
|
+
white: ansi.white,
|
|
135
|
+
brightBlack: ansi.brightBlack,
|
|
136
|
+
brightRed: ansi.brightRed,
|
|
137
|
+
brightGreen: ansi.brightGreen,
|
|
138
|
+
brightYellow: ansi.brightYellow,
|
|
139
|
+
brightBlue: ansi.brightBlue,
|
|
140
|
+
brightMagenta: ansi.brightMagenta,
|
|
141
|
+
brightCyan: ansi.brightCyan,
|
|
142
|
+
brightWhite: ansi.brightWhite,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Which og theme is active for `el`, matching the `use-theme-type` convention
|
|
147
|
+
* (nearest `data-og-theme` / `.og-light` ancestor; else the computed
|
|
148
|
+
* `color-scheme`; else dark, the first-class default). */
|
|
149
|
+
function detectMode(el: Element, style: CSSStyleDeclaration): ThemeMode {
|
|
150
|
+
const closest = el.closest?.bind(el);
|
|
151
|
+
if (closest) {
|
|
152
|
+
if (closest('[data-og-theme="light"]') || closest(".og-light")) return "light";
|
|
153
|
+
const explicit = closest("[data-og-theme]");
|
|
154
|
+
if (explicit) return explicit.getAttribute("data-og-theme") === "light" ? "light" : "dark";
|
|
155
|
+
}
|
|
156
|
+
const scheme = (
|
|
157
|
+
style.getPropertyValue("color-scheme").trim() ||
|
|
158
|
+
style.colorScheme ||
|
|
159
|
+
""
|
|
160
|
+
).toLowerCase();
|
|
161
|
+
if (scheme.includes("light") && !scheme.includes("dark")) return "light";
|
|
162
|
+
return "dark";
|
|
163
|
+
}
|
|
164
|
+
|
|
3
165
|
/**
|
|
4
|
-
* Derive
|
|
5
|
-
* Reads the COMPUTED values so xterm
|
|
6
|
-
*
|
|
7
|
-
*
|
|
166
|
+
* Derive a COMPLETE xterm `ITheme` from the live `--og-*` token system on the
|
|
167
|
+
* DOM. Reads the COMPUTED values so xterm gets concrete colors, detects the
|
|
168
|
+
* active theme mode, and fills every ANSI slot. Call on mount + re-derive on a
|
|
169
|
+
* `data-og-theme` flip. SSR-safe: returns undefined off the DOM.
|
|
8
170
|
*
|
|
9
|
-
*
|
|
171
|
+
* `root` should be an element INSIDE the themed subtree (e.g. the terminal
|
|
172
|
+
* container) so nested `[data-og-theme]` themes resolve correctly; when omitted
|
|
173
|
+
* it falls back to the nearest `[data-og-theme]` element, then documentElement.
|
|
10
174
|
*/
|
|
11
175
|
export function xtermThemeFromTokens(root?: HTMLElement | null): XtermTheme | undefined {
|
|
12
176
|
if (typeof window === "undefined" || typeof getComputedStyle === "undefined") return undefined;
|
|
13
|
-
const el =
|
|
177
|
+
const el =
|
|
178
|
+
root ??
|
|
179
|
+
(typeof document !== "undefined"
|
|
180
|
+
? ((document.querySelector("[data-og-theme]") as HTMLElement | null) ??
|
|
181
|
+
document.documentElement)
|
|
182
|
+
: null);
|
|
183
|
+
if (!el) return undefined;
|
|
14
184
|
const style = getComputedStyle(el);
|
|
15
|
-
const read = (name
|
|
185
|
+
const read: TokenReader = (name) => {
|
|
16
186
|
const value = style.getPropertyValue(name).trim();
|
|
17
187
|
return value || undefined;
|
|
18
188
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
189
|
+
return buildXtermTheme(read, detectMode(el, style));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ── Font resolution ──────────────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
export type ResolvedTerminalFont = { fontFamily: string; fontSize: number; lineHeight: number };
|
|
195
|
+
|
|
196
|
+
/** Concrete fallback if the token is unset or (defensively) still a `var()`. */
|
|
197
|
+
const FALLBACK_MONO = "ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace";
|
|
198
|
+
|
|
199
|
+
export type FontOverrides = { fontFamily?: string | undefined; fontSize?: number | undefined };
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Resolve xterm's font from token values. The critical bug this fixes:
|
|
203
|
+
* `fontFamily: "var(--og-font-mono)"` is measured by xterm via a canvas font
|
|
204
|
+
* string where `var()` does NOT resolve, so xterm silently mis-measures every
|
|
205
|
+
* glyph. Here the family is a CONCRETE stack; a `var(` in the input is treated
|
|
206
|
+
* as unresolved and replaced with the fallback. Pure over a token `read`.
|
|
207
|
+
*/
|
|
208
|
+
export function resolveTerminalFontFromReader(
|
|
209
|
+
read: TokenReader,
|
|
210
|
+
overrides?: FontOverrides,
|
|
211
|
+
): ResolvedTerminalFont {
|
|
212
|
+
const rawFamily = overrides?.fontFamily ?? read("--og-font-mono");
|
|
213
|
+
const fontFamily =
|
|
214
|
+
rawFamily && rawFamily.trim() && !rawFamily.includes("var(") ? rawFamily.trim() : FALLBACK_MONO;
|
|
215
|
+
|
|
216
|
+
let fontSize = overrides?.fontSize;
|
|
217
|
+
if (fontSize == null) {
|
|
218
|
+
const raw = read("--og-code-font-size");
|
|
219
|
+
const parsed = raw ? Number.parseFloat(raw) : Number.NaN;
|
|
220
|
+
fontSize = Number.isFinite(parsed) && parsed > 0 ? parsed : 13;
|
|
28
221
|
}
|
|
29
|
-
|
|
30
|
-
return
|
|
222
|
+
|
|
223
|
+
return { fontFamily, fontSize, lineHeight: 1.3 };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** DOM wrapper: resolve the terminal font from an element's computed tokens. */
|
|
227
|
+
export function resolveTerminalFont(
|
|
228
|
+
el: HTMLElement,
|
|
229
|
+
overrides?: FontOverrides,
|
|
230
|
+
): ResolvedTerminalFont {
|
|
231
|
+
const style = getComputedStyle(el);
|
|
232
|
+
const read: TokenReader = (name) => {
|
|
233
|
+
const value = style.getPropertyValue(name).trim();
|
|
234
|
+
return value || undefined;
|
|
235
|
+
};
|
|
236
|
+
return resolveTerminalFontFromReader(read, overrides);
|
|
31
237
|
}
|
package/src/machines.ts
CHANGED
|
@@ -34,12 +34,34 @@ export { MachineMetrics } from "./components/machine-metrics";
|
|
|
34
34
|
export type { MachineMetricsProps } from "./components/machine-metrics";
|
|
35
35
|
export { MachineCard } from "./components/machine-card";
|
|
36
36
|
export type { MachineCardProps } from "./components/machine-card";
|
|
37
|
+
export { MachineHealthPill } from "./components/machine-health-pill";
|
|
38
|
+
export type { MachineHealthPillProps } from "./components/machine-health-pill";
|
|
39
|
+
// Telemetry: fused health signal + history charts + the per-machine detail view.
|
|
40
|
+
export { deriveHealth, HEALTH_TOKEN, healthPulses } from "./components/machines/health";
|
|
41
|
+
export type { HealthLevel, HealthVerdict } from "./components/machines/health";
|
|
42
|
+
export { MetricSparkline } from "./components/machines/metric-sparkline";
|
|
43
|
+
export type { MetricSparklineProps } from "./components/machines/metric-sparkline";
|
|
44
|
+
export { MetricHistoryChart } from "./components/machines/metric-history-chart";
|
|
45
|
+
export type {
|
|
46
|
+
MetricHistoryChartProps,
|
|
47
|
+
SeriesPoint,
|
|
48
|
+
} from "./components/machines/metric-history-chart";
|
|
49
|
+
export { MachineDetail } from "./components/machines/machine-detail";
|
|
50
|
+
export type { MachineDetailProps } from "./components/machines/machine-detail";
|
|
51
|
+
export { METRICS, METRIC_WINDOWS, WINDOW_LABEL, pointsFor } from "./components/machines/series";
|
|
52
|
+
export type { MetricDef, MetricKey, MetricWindow } from "./components/machines/series";
|
|
37
53
|
export { MachinesDashboard } from "./components/machines-dashboard";
|
|
38
54
|
export type { MachinesDashboardProps } from "./components/machines-dashboard";
|
|
39
55
|
export { MachineDockBar, SharedMachineDisclosure } from "./components/machine-dock-bar";
|
|
40
|
-
export type {
|
|
56
|
+
export type {
|
|
57
|
+
MachineDockBarProps,
|
|
58
|
+
SharedMachineDisclosureProps,
|
|
59
|
+
} from "./components/machine-dock-bar";
|
|
41
60
|
export { EnrollmentDeviceFlow } from "./components/enrollment-device-flow";
|
|
42
|
-
export type {
|
|
61
|
+
export type {
|
|
62
|
+
DeviceFlowPhase,
|
|
63
|
+
EnrollmentDeviceFlowProps,
|
|
64
|
+
} from "./components/enrollment-device-flow";
|
|
43
65
|
export { EnrollmentConsent } from "./components/enrollment-consent";
|
|
44
66
|
export type {
|
|
45
67
|
EnrollmentConsentMachine,
|
|
@@ -47,4 +69,8 @@ export type {
|
|
|
47
69
|
EnrollmentConsentProps,
|
|
48
70
|
} from "./components/enrollment-consent";
|
|
49
71
|
export { useMachines } from "./hooks/use-machines";
|
|
50
|
-
export type {
|
|
72
|
+
export type {
|
|
73
|
+
MachinesClientLike,
|
|
74
|
+
UseMachinesOptions,
|
|
75
|
+
UseMachinesResult,
|
|
76
|
+
} from "./hooks/use-machines";
|