@robota-sdk/agent-ui-web 0.0.0-stage → 3.0.0-beta.101
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/CHANGELOG.md +557 -0
- package/LICENSE +661 -0
- package/README.md +19 -2
- package/dist/browser/index.d.ts +736 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +8525 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/node/index.cjs +8575 -0
- package/dist/node/index.d.cts +736 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.ts +736 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +8525 -0
- package/dist/node/index.js.map +1 -0
- package/docs/README.md +62 -0
- package/docs/SPEC.md +130 -0
- package/package.json +99 -3
- package/src/app-view/bridge.ts +217 -0
- package/src/app-view/call-tool-result.ts +58 -0
- package/src/app-view/host-context.ts +37 -0
- package/src/app-view/types.ts +12 -0
- package/src/client/runtime-host.ts +84 -0
- package/src/client/ws-session-client.ts +131 -0
- package/src/clipboard.ts +81 -0
- package/src/components/AgentActivityPanel.tsx +371 -0
- package/src/components/AgentSwitcherSheet.tsx +93 -0
- package/src/components/AppView.tsx +147 -0
- package/src/components/Brand.tsx +33 -0
- package/src/components/Composer.tsx +685 -0
- package/src/components/ConversationView.tsx +789 -0
- package/src/components/Dialog.tsx +230 -0
- package/src/components/DiffLines.tsx +81 -0
- package/src/components/ExecutionDetailSheet.tsx +142 -0
- package/src/components/HelpSheet.tsx +104 -0
- package/src/components/PermissionPrompt.tsx +762 -0
- package/src/components/PersonalUsageDashboard.tsx +39 -0
- package/src/components/PersonalUsageDashboardDetails.tsx +368 -0
- package/src/components/PersonalUsageDashboardSections.tsx +250 -0
- package/src/components/PopupMenu.tsx +166 -0
- package/src/components/ProjectPanel.tsx +268 -0
- package/src/components/SessionMonitor.tsx +180 -0
- package/src/components/SessionSidebar.tsx +429 -0
- package/src/components/SessionSurface.tsx +482 -0
- package/src/components/SessionSurfaceChrome.tsx +319 -0
- package/src/components/SettingsFields.tsx +159 -0
- package/src/components/SettingsGeneralSection.tsx +49 -0
- package/src/components/SettingsMcpSection.tsx +157 -0
- package/src/components/SettingsPermissionsSection.tsx +137 -0
- package/src/components/SettingsPluginsSection.tsx +165 -0
- package/src/components/SettingsProvidersSection.tsx +211 -0
- package/src/components/SettingsScreen.tsx +340 -0
- package/src/components/StatusControls.tsx +294 -0
- package/src/components/composer-attachments.ts +246 -0
- package/src/components/personal-usage-dashboard-types.ts +26 -0
- package/src/driver-labels.ts +88 -0
- package/src/execution-entry-status.ts +55 -0
- package/src/goal-view.ts +19 -0
- package/src/hooks/command-menu.ts +65 -0
- package/src/hooks/excluded-commands.ts +52 -0
- package/src/hooks/prompt-state.ts +83 -0
- package/src/hooks/server-message-handling.ts +95 -0
- package/src/hooks/session-client-types.ts +344 -0
- package/src/hooks/ui-intent-state.ts +74 -0
- package/src/hooks/use-agent-switcher-state.ts +125 -0
- package/src/hooks/use-app-view-requests.ts +178 -0
- package/src/hooks/use-execution-detail.ts +118 -0
- package/src/hooks/use-media-query.ts +27 -0
- package/src/hooks/use-model-list.ts +45 -0
- package/src/hooks/use-personal-usage.ts +138 -0
- package/src/hooks/use-project-panel.ts +117 -0
- package/src/hooks/use-schedules-state.ts +92 -0
- package/src/hooks/use-session-directory.ts +202 -0
- package/src/hooks/use-settings-state.ts +115 -0
- package/src/hooks/useSessionClient.ts +1051 -0
- package/src/index.ts +72 -0
- package/src/product-identity.tsx +26 -0
- package/src/schedule-view.ts +92 -0
- package/src/styles/surface.css +231 -0
- package/src/styles/surface.css.d.mts +1 -0
- package/src/styles/theme.css +38 -0
- package/src/styles/theme.css.d.mts +1 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// @robota-sdk/agent-ui-web — the GUI presentation layer for a agent session.
|
|
2
|
+
// React components + a wire-protocol session reducer + a desktop shell, rendered over the transport-neutral
|
|
3
|
+
// TServerMessage stream. The GUI analog of agent-ui-terminal; consumed by apps/agent-app (desktop) and the
|
|
4
|
+
// browser surface (agent-transport-webrtc-web).
|
|
5
|
+
|
|
6
|
+
// ── Session reducer (transport-neutral) ─────────────────────
|
|
7
|
+
export { useSessionClient, useWsSession } from './hooks/useSessionClient.js';
|
|
8
|
+
export type {
|
|
9
|
+
IConversationMessage,
|
|
10
|
+
ICommandOutputEntry,
|
|
11
|
+
IToolGroupEntry,
|
|
12
|
+
TConversationEntry,
|
|
13
|
+
IActiveTool,
|
|
14
|
+
ISessionNotice,
|
|
15
|
+
IWsSessionState,
|
|
16
|
+
ISessionClientHandle,
|
|
17
|
+
TMakeSessionClient,
|
|
18
|
+
} from './hooks/useSessionClient.js';
|
|
19
|
+
export type {
|
|
20
|
+
TAppToolArguments,
|
|
21
|
+
TAppToolOutcome,
|
|
22
|
+
TAppView,
|
|
23
|
+
TSessionListing,
|
|
24
|
+
TSessionsError,
|
|
25
|
+
} from './hooks/session-client-types.js';
|
|
26
|
+
// Issue #3280 §5: a host that can restart the runtime calls this before reloading the page, so the
|
|
27
|
+
// first session listing after reconnecting can switch back to the session the person was in.
|
|
28
|
+
export { rememberSessionForRestore } from './hooks/use-session-directory.js';
|
|
29
|
+
|
|
30
|
+
// ── WS session client (loopback / localhost) ────────────────
|
|
31
|
+
export { createWsSessionClient } from './client/ws-session-client.js';
|
|
32
|
+
export type { IWsSessionClientCallbacks, TConnectionStatus } from './client/ws-session-client.js';
|
|
33
|
+
export { resolveClientRuntimeHost } from './client/runtime-host.js';
|
|
34
|
+
export type {
|
|
35
|
+
IClientRuntimeBridge,
|
|
36
|
+
IClientRuntimeEnvironment,
|
|
37
|
+
IClientRuntimeHost,
|
|
38
|
+
IClientTrustQuestion,
|
|
39
|
+
TClientRuntimeState,
|
|
40
|
+
TClientTrustChoice,
|
|
41
|
+
} from './client/runtime-host.js';
|
|
42
|
+
|
|
43
|
+
// ── Prompt (permission/ask) state ───────────────────────────
|
|
44
|
+
export { applyPromptEvent, permissionResponse, askResponse } from './hooks/prompt-state.js';
|
|
45
|
+
export type { TPendingPrompt } from './hooks/prompt-state.js';
|
|
46
|
+
|
|
47
|
+
// ── UI-intent (command screen-request) state — CMD-004 Stage D ──
|
|
48
|
+
export { describeUiIntentForGui, guiScreenForUiIntent } from './hooks/ui-intent-state.js';
|
|
49
|
+
|
|
50
|
+
// ── Presentation components ─────────────────────────────────
|
|
51
|
+
export { ConversationView } from './components/ConversationView.js';
|
|
52
|
+
export { AgentActivityPanel } from './components/AgentActivityPanel.js';
|
|
53
|
+
export { AgentSwitcherSheet } from './components/AgentSwitcherSheet.js';
|
|
54
|
+
export { PermissionPrompt } from './components/PermissionPrompt.js';
|
|
55
|
+
export { PersonalUsageDashboard } from './components/PersonalUsageDashboard.js';
|
|
56
|
+
export type { TPersonalUsageDashboardState } from './components/personal-usage-dashboard-types.js';
|
|
57
|
+
export { SessionSurface, CenteredChrome } from './components/SessionSurface.js';
|
|
58
|
+
export type { IComposerHandle, IPickedFile } from './components/Composer.js';
|
|
59
|
+
export { SessionSidebar } from './components/SessionSidebar.js';
|
|
60
|
+
export { SessionMonitor } from './components/SessionMonitor.js';
|
|
61
|
+
// #3282 §4a: the shared modal-shell primitive (also consumed by #3282 §2's mode-change confirmation
|
|
62
|
+
// and #3289 §1's session-delete confirmation) and the Settings screen it backs.
|
|
63
|
+
export { Dialog, ConfirmDialog } from './components/Dialog.js';
|
|
64
|
+
export type { IDialogProps, IConfirmDialogProps } from './components/Dialog.js';
|
|
65
|
+
export { SettingsScreen } from './components/SettingsScreen.js';
|
|
66
|
+
// #3282 §4e: the Help sheet `/help` opens instead of the terminal-style text list.
|
|
67
|
+
export { HelpSheet } from './components/HelpSheet.js';
|
|
68
|
+
// Pieces of a surface's chrome, not roots: render them inside a `agent-ui` scope.
|
|
69
|
+
export { ProductMark, ProductWordmark } from './components/Brand.js';
|
|
70
|
+
|
|
71
|
+
export { ProductIdentityProvider, useProductIdentity } from './product-identity.js';
|
|
72
|
+
export type { IWebProductIdentity } from './product-identity.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createContext, useContext, type ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
/** Only the public identity needed by a browser surface; hosts resolve its values. */
|
|
4
|
+
export interface IWebProductIdentity {
|
|
5
|
+
readonly identity: { readonly displayName: string; readonly cliName: string };
|
|
6
|
+
readonly storage: { readonly browserNamespace: string };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const ProductIdentityContext = createContext<IWebProductIdentity | null>(null);
|
|
10
|
+
|
|
11
|
+
export function ProductIdentityProvider({ value, children }: {
|
|
12
|
+
readonly value: IWebProductIdentity;
|
|
13
|
+
readonly children?: ReactNode;
|
|
14
|
+
}): React.ReactElement {
|
|
15
|
+
if (!value.identity.displayName || !value.identity.cliName || !value.storage.browserNamespace) {
|
|
16
|
+
throw new Error('A public product identity is required');
|
|
17
|
+
}
|
|
18
|
+
// Changing product replaces the subtree so drafts, in-flight requests and session refs cannot cross it.
|
|
19
|
+
return <ProductIdentityContext.Provider key={value.storage.browserNamespace} value={value}>{children}</ProductIdentityContext.Provider>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function useProductIdentity(): IWebProductIdentity {
|
|
23
|
+
const identity = useContext(ProductIdentityContext);
|
|
24
|
+
if (identity === null) throw new Error('ProductIdentityProvider is required');
|
|
25
|
+
return identity;
|
|
26
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #3282 §4 part b-3 — plain-words presentation for the Agents panel's "Scheduled" group. Never an
|
|
3
|
+
* ISO string or a cron expression: a concrete `nextFireAt` is shown in local time as "Today 9:00" /
|
|
4
|
+
* "Tomorrow 9:00" / a weekday / a date; a handful of common cron shapes are phrased ("Every weekday
|
|
5
|
+
* at 9:00"); anything else falls back to "Repeats".
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
IBackgroundTaskState,
|
|
10
|
+
TBackgroundTaskStatus,
|
|
11
|
+
} from '@robota-sdk/agent-interface-execution';
|
|
12
|
+
|
|
13
|
+
type TScheduleTask = IBackgroundTaskState<'scheduled'>;
|
|
14
|
+
|
|
15
|
+
const WEEKDAY_NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
16
|
+
|
|
17
|
+
function formatClock(date: Date): string {
|
|
18
|
+
return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isSameLocalDay(a: Date, b: Date): boolean {
|
|
22
|
+
return (
|
|
23
|
+
a.getFullYear() === b.getFullYear() &&
|
|
24
|
+
a.getMonth() === b.getMonth() &&
|
|
25
|
+
a.getDate() === b.getDate()
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** A concrete fire time, in plain local words — never the raw ISO string. */
|
|
30
|
+
export function formatPlainLocalTime(iso: string, now: Date = new Date()): string {
|
|
31
|
+
const when = new Date(iso);
|
|
32
|
+
if (Number.isNaN(when.getTime())) return 'Unknown';
|
|
33
|
+
const clock = formatClock(when);
|
|
34
|
+
if (isSameLocalDay(when, now)) return `Today ${clock}`;
|
|
35
|
+
const tomorrow = new Date(now);
|
|
36
|
+
tomorrow.setDate(now.getDate() + 1);
|
|
37
|
+
if (isSameLocalDay(when, tomorrow)) return `Tomorrow ${clock}`;
|
|
38
|
+
const sixDaysOut = new Date(now);
|
|
39
|
+
sixDaysOut.setDate(now.getDate() + 6);
|
|
40
|
+
if (when.getTime() > now.getTime() && when.getTime() <= sixDaysOut.getTime()) {
|
|
41
|
+
return `${WEEKDAY_NAMES[when.getDay()]} ${clock}`;
|
|
42
|
+
}
|
|
43
|
+
const date = when.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
|
44
|
+
return `${date} ${clock}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** A handful of common 5-field cron shapes phrased plainly; anything else is `undefined`. */
|
|
48
|
+
function describeCronPlainly(cronExpression: string): string | undefined {
|
|
49
|
+
const fields = cronExpression.trim().split(/\s+/);
|
|
50
|
+
if (fields.length !== 5) return undefined;
|
|
51
|
+
const [minute, hour, dayOfMonth, month, dayOfWeek] = fields;
|
|
52
|
+
if (dayOfMonth !== '*' || month !== '*') return undefined;
|
|
53
|
+
if (!/^\d{1,2}$/.test(minute!) || !/^\d{1,2}$/.test(hour!)) return undefined;
|
|
54
|
+
const clock = `${hour!.padStart(2, '0')}:${minute!.padStart(2, '0')}`;
|
|
55
|
+
if (dayOfWeek === '*') return `Every day at ${clock}`;
|
|
56
|
+
if (dayOfWeek === '1-5') return `Every weekday at ${clock}`;
|
|
57
|
+
if (dayOfWeek === '0,6' || dayOfWeek === '6,0') return `Every weekend at ${clock}`;
|
|
58
|
+
if (/^\d$/.test(dayOfWeek!)) {
|
|
59
|
+
const name = WEEKDAY_NAMES[Number(dayOfWeek)];
|
|
60
|
+
return name ? `Every ${name} at ${clock}` : undefined;
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** "When it runs next" — the Scheduled row's own line, in plain local time (never raw wire data). */
|
|
66
|
+
export function describeScheduleTiming(task: TScheduleTask, now: Date = new Date()): string {
|
|
67
|
+
if (task.nextFireAt) return formatPlainLocalTime(task.nextFireAt, now);
|
|
68
|
+
const cronExpression = task.schedule?.cronExpression;
|
|
69
|
+
if (!cronExpression) return '—';
|
|
70
|
+
return describeCronPlainly(cronExpression) ?? 'Repeats';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** What a schedule runs — its one-line instruction, never the raw label/id. */
|
|
74
|
+
export function describeScheduleInstruction(task: TScheduleTask): string {
|
|
75
|
+
return task.schedule?.agentInstruction ?? task.label;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const STATUS_LABELS: Partial<Record<TBackgroundTaskStatus, string>> = {
|
|
79
|
+
sleeping: 'Active',
|
|
80
|
+
paused: 'Paused',
|
|
81
|
+
running: 'Running',
|
|
82
|
+
queued: 'Queued',
|
|
83
|
+
cancelled: 'Deleted',
|
|
84
|
+
failed: 'Failed',
|
|
85
|
+
completed: 'Done',
|
|
86
|
+
waiting_permission: 'Needs you',
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/** The schedule's status in plain words. */
|
|
90
|
+
export function describeScheduleStatus(task: TScheduleTask): string {
|
|
91
|
+
return STATUS_LABELS[task.status] ?? task.status;
|
|
92
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The GUI surface's design (agent-ui-web), scoped to `.agent-ui`: a calm desktop-app surface where the
|
|
3
|
+
* conversation is the product. Readable sizes, one humanist family (Pretendard, bundled — a page's CSP may
|
|
4
|
+
* load fonts only from itself), monospace kept for code and paths, and a neutral palette with one accent.
|
|
5
|
+
*
|
|
6
|
+
* Every root component of the surface carries `agent-ui`, so its tokens apply inside it and nowhere else:
|
|
7
|
+
* a host with tokens of its own under the same names keeps them. The host owns the Tailwind entry
|
|
8
|
+
* (`@import 'tailwindcss'` + `@source '…/agent-ui-web/src'`) and `@import`s this file there, so Tailwind
|
|
9
|
+
* maps the tokens below onto utilities. A page that is the surface imports `theme.css` instead.
|
|
10
|
+
*/
|
|
11
|
+
@import 'pretendard/dist/web/variable/pretendardvariable-dynamic-subset.css';
|
|
12
|
+
|
|
13
|
+
/* Map the design tokens (CSS vars below) onto Tailwind color/font utilities (bg-card, text-primary, …). */
|
|
14
|
+
@theme inline {
|
|
15
|
+
--color-background: var(--background);
|
|
16
|
+
--color-foreground: var(--foreground);
|
|
17
|
+
--color-sidebar: var(--sidebar);
|
|
18
|
+
--color-card: var(--card);
|
|
19
|
+
--color-card-foreground: var(--card-foreground);
|
|
20
|
+
--color-raised: var(--raised);
|
|
21
|
+
--color-hover: var(--hover);
|
|
22
|
+
--color-popover: var(--popover);
|
|
23
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
24
|
+
--color-primary: var(--primary);
|
|
25
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
26
|
+
--color-secondary: var(--secondary);
|
|
27
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
28
|
+
--color-muted: var(--muted);
|
|
29
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
30
|
+
--color-subtle: var(--subtle);
|
|
31
|
+
--color-accent: var(--accent);
|
|
32
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
33
|
+
--color-destructive: var(--destructive);
|
|
34
|
+
--color-warning: var(--warning);
|
|
35
|
+
--color-success: var(--success);
|
|
36
|
+
--color-border: var(--border);
|
|
37
|
+
--color-input: var(--input);
|
|
38
|
+
--color-ring: var(--ring);
|
|
39
|
+
--font-mono: var(--font-mono);
|
|
40
|
+
--font-sans: var(--font-sans);
|
|
41
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
42
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
43
|
+
--radius-lg: var(--radius);
|
|
44
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/*
|
|
48
|
+
* Paper (light) and graphite (dark), one token each: `light-dark()` picks by `color-scheme`, which follows
|
|
49
|
+
* the system unless the host pins `data-theme` on the scope or above it. Text colours keep WCAG AA (4.5:1)
|
|
50
|
+
* on every surface they sit on.
|
|
51
|
+
*/
|
|
52
|
+
.agent-ui {
|
|
53
|
+
color-scheme: light dark;
|
|
54
|
+
--radius: 0.75rem;
|
|
55
|
+
|
|
56
|
+
--background: light-dark(#ffffff, #1a1a1c); /* conversation canvas */
|
|
57
|
+
--foreground: light-dark(#1c1c1e, #ededee);
|
|
58
|
+
--sidebar: light-dark(#f7f7f5, #141416); /* the quieter frame around it */
|
|
59
|
+
--card: light-dark(
|
|
60
|
+
#f4f4f2,
|
|
61
|
+
#242427
|
|
62
|
+
); /* cards, menus, the composer — one tonal step, no outline */
|
|
63
|
+
--card-foreground: light-dark(#1c1c1e, #ededee);
|
|
64
|
+
--raised: light-dark(#ececea, #2c2c30); /* user bubble, selected row, pressed chips */
|
|
65
|
+
--hover: light-dark(#e8e8e5, #303035);
|
|
66
|
+
--popover: light-dark(#ffffff, #232326);
|
|
67
|
+
--popover-foreground: light-dark(#1c1c1e, #ededee);
|
|
68
|
+
--primary: light-dark(#1c1c1e, #f2f2f3); /* the send button and primary actions: ink on paper */
|
|
69
|
+
--primary-foreground: light-dark(#ffffff, #151517);
|
|
70
|
+
--secondary: light-dark(#f1f1ef, #29292d);
|
|
71
|
+
--secondary-foreground: light-dark(#1c1c1e, #ededee);
|
|
72
|
+
--muted: light-dark(#f4f4f2, #232326);
|
|
73
|
+
--muted-foreground: light-dark(#55555c, #a8a8af); /* secondary text */
|
|
74
|
+
--subtle: light-dark(#68686f, #8e8e97); /* tertiary text: still >= 4.5:1 on every surface */
|
|
75
|
+
--accent: light-dark(#0a7a55, #5fd4a8); /* agent's mark, live state — sparingly */
|
|
76
|
+
--accent-foreground: light-dark(#ffffff, #0b1a14);
|
|
77
|
+
--destructive: light-dark(#c42f40, #f2727f);
|
|
78
|
+
--warning: light-dark(#8f5a0b, #f0b35b);
|
|
79
|
+
--success: light-dark(#0a7a55, #5fd4a8);
|
|
80
|
+
--border: light-dark(rgb(0 0 0 / 0.08), rgb(255 255 255 / 0.08));
|
|
81
|
+
--input: light-dark(rgb(0 0 0 / 0.12), rgb(255 255 255 / 0.1));
|
|
82
|
+
--ring: light-dark(rgb(10 122 85 / 0.4), rgb(95 212 168 / 0.45));
|
|
83
|
+
|
|
84
|
+
--font-sans:
|
|
85
|
+
'Pretendard Variable', Pretendard, -apple-system, BlinkMacSystemFont, 'Apple SD Gothic Neo',
|
|
86
|
+
'Segoe UI', 'Noto Sans KR', sans-serif;
|
|
87
|
+
--font-mono:
|
|
88
|
+
'SF Mono', ui-monospace, 'Cascadia Code', 'JetBrains Mono', Menlo, Consolas, monospace;
|
|
89
|
+
}
|
|
90
|
+
.agent-ui[data-theme='light'],
|
|
91
|
+
[data-theme='light'] .agent-ui {
|
|
92
|
+
color-scheme: light;
|
|
93
|
+
}
|
|
94
|
+
.agent-ui[data-theme='dark'],
|
|
95
|
+
[data-theme='dark'] .agent-ui {
|
|
96
|
+
color-scheme: dark;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@layer base {
|
|
100
|
+
/*
|
|
101
|
+
* The surface's type, set where it renders and never on the root element: on a page that is only
|
|
102
|
+
* the surface (`<html class="agent-ui">`) it goes on the body, so `1rem` stays the browser's and the
|
|
103
|
+
* rem-based spacing keeps its size. In the base layer, so a utility class on a root still wins.
|
|
104
|
+
*/
|
|
105
|
+
.agent-ui:not(:root),
|
|
106
|
+
:root.agent-ui > body {
|
|
107
|
+
color: var(--foreground);
|
|
108
|
+
font-family: var(--font-sans);
|
|
109
|
+
font-size: 15px;
|
|
110
|
+
line-height: 1.6;
|
|
111
|
+
letter-spacing: -0.005em;
|
|
112
|
+
font-feature-settings:
|
|
113
|
+
'ss06' 1,
|
|
114
|
+
'tnum' 0;
|
|
115
|
+
-webkit-font-smoothing: antialiased;
|
|
116
|
+
-moz-osx-font-smoothing: grayscale;
|
|
117
|
+
text-rendering: optimizeLegibility;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.agent-ui,
|
|
121
|
+
.agent-ui *,
|
|
122
|
+
.agent-ui ::before,
|
|
123
|
+
.agent-ui ::after {
|
|
124
|
+
border-color: var(--border);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.agent-ui ::selection {
|
|
128
|
+
background: color-mix(in oklab, var(--accent) 32%, transparent);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.agent-ui ::placeholder {
|
|
132
|
+
color: var(--subtle);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.agent-ui button {
|
|
136
|
+
cursor: pointer;
|
|
137
|
+
}
|
|
138
|
+
.agent-ui button:disabled {
|
|
139
|
+
cursor: default;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.agent-ui :focus-visible {
|
|
143
|
+
outline: 2px solid var(--ring);
|
|
144
|
+
outline-offset: 2px;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* The live dot: a soft halo, not a glow. */
|
|
149
|
+
.status-glow {
|
|
150
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 22%, transparent);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* Entrances. */
|
|
154
|
+
@keyframes gui-rise {
|
|
155
|
+
from {
|
|
156
|
+
opacity: 0;
|
|
157
|
+
transform: translateY(6px);
|
|
158
|
+
}
|
|
159
|
+
to {
|
|
160
|
+
opacity: 1;
|
|
161
|
+
transform: none;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
.gui-rise {
|
|
165
|
+
animation: gui-rise 0.36s cubic-bezier(0.2, 0.7, 0.2, 1) both;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* Work in progress reads as a sweep of light across the label. */
|
|
169
|
+
@keyframes gui-shimmer {
|
|
170
|
+
from {
|
|
171
|
+
background-position: 150% 0;
|
|
172
|
+
}
|
|
173
|
+
to {
|
|
174
|
+
background-position: -50% 0;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
.gui-shimmer {
|
|
178
|
+
background-image: linear-gradient(
|
|
179
|
+
100deg,
|
|
180
|
+
var(--muted-foreground) 30%,
|
|
181
|
+
var(--foreground) 50%,
|
|
182
|
+
var(--muted-foreground) 70%
|
|
183
|
+
);
|
|
184
|
+
background-size: 250% 100%;
|
|
185
|
+
-webkit-background-clip: text;
|
|
186
|
+
background-clip: text;
|
|
187
|
+
color: transparent;
|
|
188
|
+
animation: gui-shimmer 2.2s linear infinite;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@media (prefers-reduced-motion: reduce) {
|
|
192
|
+
.gui-rise,
|
|
193
|
+
.gui-shimmer,
|
|
194
|
+
.agent-ui .animate-pulse,
|
|
195
|
+
.agent-ui .animate-spin,
|
|
196
|
+
.agent-ui .animate-ping {
|
|
197
|
+
animation: none;
|
|
198
|
+
}
|
|
199
|
+
.gui-shimmer {
|
|
200
|
+
color: var(--muted-foreground);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* Agent prose: comfortable measure and rhythm for long answers. In the components layer, so the
|
|
205
|
+
utilities on its children (a heading's larger top margin) still win. */
|
|
206
|
+
@layer components {
|
|
207
|
+
.gui-prose {
|
|
208
|
+
font-size: 15px;
|
|
209
|
+
line-height: 1.7;
|
|
210
|
+
color: var(--foreground);
|
|
211
|
+
overflow-wrap: anywhere;
|
|
212
|
+
}
|
|
213
|
+
.gui-prose > * + * {
|
|
214
|
+
margin-top: 0.75em;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/*
|
|
219
|
+
* A code block's own horizontal scrollbar (#3289 §2) stays visible rather than only-on-hover like the
|
|
220
|
+
* rest of the page (`theme.css`'s quiet overlay scrollbar) — a long unwrapped line needs a standing
|
|
221
|
+
* signal that there is more of it, not one that only appears once the person already found the edge.
|
|
222
|
+
* `.gui-code-scroll::-webkit-scrollbar-thumb` outranks that page-wide bare `::-webkit-scrollbar-thumb`
|
|
223
|
+
* rule on specificity alone, so it wins regardless of which stylesheet loads first.
|
|
224
|
+
*/
|
|
225
|
+
.gui-code-scroll {
|
|
226
|
+
scrollbar-width: thin;
|
|
227
|
+
scrollbar-color: color-mix(in oklab, var(--foreground) 28%, transparent) transparent;
|
|
228
|
+
}
|
|
229
|
+
.gui-code-scroll::-webkit-scrollbar-thumb {
|
|
230
|
+
background-color: color-mix(in oklab, var(--foreground) 28%, transparent);
|
|
231
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The GUI as a whole page (the GUI web app, and the desktop app and `agent --serve --open` that load its
|
|
3
|
+
* build): the surface's design from `surface.css`, plus what only a page that is the surface owns — its
|
|
4
|
+
* height, its background and its scrollbars. The page puts `agent-ui` on its `<html>`, so the tokens
|
|
5
|
+
* reach the body too.
|
|
6
|
+
*/
|
|
7
|
+
@import './surface.css';
|
|
8
|
+
|
|
9
|
+
@layer base {
|
|
10
|
+
html,
|
|
11
|
+
body,
|
|
12
|
+
#root {
|
|
13
|
+
height: 100%;
|
|
14
|
+
margin: 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
body {
|
|
18
|
+
background: var(--background);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Quiet overlay scrollbars. */
|
|
23
|
+
::-webkit-scrollbar {
|
|
24
|
+
width: 10px;
|
|
25
|
+
height: 10px;
|
|
26
|
+
}
|
|
27
|
+
::-webkit-scrollbar-track {
|
|
28
|
+
background: transparent;
|
|
29
|
+
}
|
|
30
|
+
::-webkit-scrollbar-thumb {
|
|
31
|
+
background: transparent;
|
|
32
|
+
border-radius: 999px;
|
|
33
|
+
border: 3px solid transparent;
|
|
34
|
+
background-clip: padding-box;
|
|
35
|
+
}
|
|
36
|
+
:hover::-webkit-scrollbar-thumb {
|
|
37
|
+
background-color: color-mix(in oklab, var(--foreground) 16%, transparent);
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|