Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .fallowrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
}
],
"ignoreDependencies": [
"@fontsource-variable/inter",
"@ai-sdk/anthropic",
"@ai-sdk/google",
"@ai-sdk/openai",
Expand Down
21 changes: 19 additions & 2 deletions apps/agent-forge/e2e/docShots.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,31 @@ import { SEED_ERROR, SEED_NEWEST_FAILURE, seedObserve } from './fixtures/seedObs
* LOUSHO_DOC_SHOTS=1 npx playwright test -c apps/agent-forge/playwright.config.ts docShots
*
* writes `docs/images/studio-<screen>.png` at the repo root (1280x800, light).
* Two optional overrides, for before/after reviews of a restyle:
*
* LOUSHO_DOC_SHOTS_OUT=<dir> write the PNGs there instead (absolute,
* or relative to the process cwd)
* LOUSHO_DOC_SHOTS_SCHEME=light|dark the colour scheme to shoot (default light)
*
* The small fixtures below fill the screens the seed leaves thin: a fleet with
* mixed health, a day of chart buckets for it, and a few threads.
*/
test.skip(!process.env.LOUSHO_DOC_SHOTS, 'set LOUSHO_DOC_SHOTS=1 to regenerate the docs screenshots');
test.use({ viewport: { width: 1280, height: 800 }, colorScheme: 'light' });

/** `LOUSHO_DOC_SHOTS_SCHEME`, failing fast on anything but light or dark. */
function shotScheme(): 'light' | 'dark' {
const raw = process.env.LOUSHO_DOC_SHOTS_SCHEME ?? 'light';
if (raw === 'light' || raw === 'dark') return raw;
throw new Error(`LOUSHO_DOC_SHOTS_SCHEME must be "light" or "dark", got "${raw}"`);
}

// Validated only when the shots run, so a stray value never breaks collection of a normal e2e run.
test.use({ viewport: { width: 1280, height: 800 }, colorScheme: process.env.LOUSHO_DOC_SHOTS ? shotScheme() : 'light' });

const APP = '/?token=e2e-studio-token';
const OUT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../docs/images');
const OUT = process.env.LOUSHO_DOC_SHOTS_OUT
? path.resolve(process.cwd(), process.env.LOUSHO_DOC_SHOTS_OUT)
: path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../docs/images');

const NOW = Date.now();
const MINUTE = 60_000;
Expand Down
2 changes: 2 additions & 0 deletions apps/agent-forge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@ai-sdk/anthropic": "^0.0.42",
"@ai-sdk/openai": "^0.0.42",
"@axe-core/playwright": "4.13.0",
"@fontsource-variable/inter": "^5.3.0",
"@playwright/test": "^1.63.0",
"@types/dagre": "^0.7.52",
"@types/express": "^5.0.5",
Expand All @@ -45,6 +46,7 @@
"@types/ws": "^8.18.1",
"@vitejs/plugin-react": "^4.3.4",
"jsdom": "^25.0.1",
"lucide-react": "^1.63.0",
"ollama-ai-provider": "^1.2.0",
"supertest": "^7.3.0",
"tsup": "^8.0.0",
Expand Down
10 changes: 10 additions & 0 deletions apps/agent-forge/src/__tests__/contrast.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ const PAIRS: [text: string, background: string][] = [
// Prompt version links (runs, Playground) draw --accent on the version card's --surface. The diff's removed/added
// lines reuse danger/danger-soft and success/success-soft, and its line numbers text-faint/surface (all listed above).
['accent', 'surface'],
// Running status pills draw info on its own tint; violet (LLM/agent) pills likewise.
['info', 'info-soft'],
['violet', 'violet-soft'],
// Pressed / selected neutrals and code blocks sit on --surface-3.
['text-muted', 'surface-3'],
['text', 'surface-3'],
// A hovered or pressed primary button keeps white text on its deeper fill.
['on-accent', 'accent-solid-hover'],
// A switched-on chip (Requires approval) draws --accent on the accent tint.
['accent', 'accent-soft'],
];

describe('theme contrast', () => {
Expand Down
9 changes: 3 additions & 6 deletions apps/agent-forge/src/canvas/AgentNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import type { AgentGraphNode, AgentGraphNodeType } from '../graph/types';
import { StatusPill } from '../ui/StatusPill';

/**
* Custom ReactFlow node renderer for every `AgentGraphNodeType`, styled to
* match `.design-ref/agent-forge-mockup.html`'s `.node` chrome: a colored
* Custom ReactFlow node renderer for every `AgentGraphNodeType`: a colored
* type-swatch header with the node's name, a meta line (an LLM node's
* `provider/model` chip and tool count, and the node's run status pill), a
* body of key/value config rows read straight from the node's
Expand All @@ -26,8 +25,7 @@ const NODE_META: Record<AgentGraphNodeType, { color: string; icon: string; hasIn
approval: { color: 'var(--warning)', icon: '✓', hasIn: true, hasOut: true },
output: { color: 'var(--success)', icon: '→', hasIn: true, hasOut: false },
// Router shares tool's warning-orange swatch family (both are
// "control" nodes in the mockup's visual language, see
// .design-ref/agent-forge-mockup.html) but with its own icon so it reads
// "control" nodes) but with its own icon so it reads
// distinctly on the canvas; `hasOut: true` matters here more than
// anywhere else - it's the one node type actually expected to carry
// MULTIPLE outgoing edges (its branches), which ReactFlow already
Expand Down Expand Up @@ -106,8 +104,7 @@ function nodeClassName(selected: boolean | undefined, highlighted?: boolean, has
return `rf-node${selected ? ' selected' : ''}${highlighted ? ' highlighted' : ''}${hasBreakpoint ? ' has-breakpoint' : ''}`;
}

// Pre·N / post·N badges, matching the mockup's `.hook-badge`
// (search `.design-ref/agent-forge-mockup.html` for `hook-badge`) - only
// Pre·N / post·N badges (`.hook-badge`) - only
// ENABLED hooks count, since a disabled hook has no runtime effect (see
// graphToSpec.ts's serialization, which drops disabled hooks entirely).
function HookBadge({ phase, count }: { phase: 'pre' | 'post'; count: number }) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`AgentNode card > renders the LLM card markup 1`] = `"<div class="rf-node" data-node-type="llm" data-node-id="llm-0"><div data-handlepos="left" data-id="1-null-null-target" class="react-flow__handle react-flow__handle-left nodrag nopan rf-port rf-port-in target connectable connectablestart connectableend connectionindicator"></div><div data-handlepos="right" data-id="1-null-null-source" class="react-flow__handle react-flow__handle-right nodrag nopan rf-port rf-port-out source connectable connectablestart connectableend connectionindicator"></div><div class="rf-node-head"><span class="node-swatch" style="background: var(--accent); width: 16px; height: 16px; border-radius: 5px;">🧠</span><span class="rf-node-title" title="Double-click to rename">support-bot</span></div><div class="rf-node-meta"><code class="node-chip node-model" title="Provider/model">openai/gpt-4o-mini</code><span class="node-chip node-tools">1 tool</span><span class="rf-node-meta-spacer"></span><span class="status-pill pill-warning"><span class="dot" aria-hidden="true"></span>awaiting approval</span></div></div>"`;
exports[`AgentNode card > renders the LLM card markup 1`] = `"<div class="rf-node" data-node-type="llm" data-node-id="llm-0"><div data-handlepos="left" data-id="1-null-null-target" class="react-flow__handle react-flow__handle-left nodrag nopan rf-port rf-port-in target connectable connectablestart connectableend connectionindicator"></div><div data-handlepos="right" data-id="1-null-null-source" class="react-flow__handle react-flow__handle-right nodrag nopan rf-port rf-port-out source connectable connectablestart connectableend connectionindicator"></div><div class="rf-node-head"><span class="node-swatch" style="background: var(--accent); width: 16px; height: 16px; border-radius: 5px;">🧠</span><span class="rf-node-title" title="Double-click to rename">support-bot</span></div><div class="rf-node-meta"><code class="node-chip node-model" title="Provider/model">openai/gpt-4o-mini</code><span class="node-chip node-tools">1 tool</span><span class="rf-node-meta-spacer"></span><span class="status-pill pill-warning"><span class="dot" aria-hidden="true"></span><span class="status-pill-label">awaiting approval</span></span></div></div>"`;
5 changes: 2 additions & 3 deletions apps/agent-forge/src/components/ChatPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
* Chat with a running agent - the Chat tab's real content, replacing
* BottomDrawer's former placeholder. Renders `AppState.chat` (the server's
* real `Message[]` history, reconciled and streamed over the existing
* `WS /agents/:id/stream` channel - see runRegistry.ts/chatReconcile.ts),
* ported from `.design-ref/agent-forge-mockup.html`'s bubble/avatar/
* timestamp layout.
* `WS /agents/:id/stream` channel - see runRegistry.ts/chatReconcile.ts)
* as bubbles with avatars and timestamps.
*
* Streaming granularity: MESSAGE-level, not token-level. The
* server's `AgentExecutor.execute({ onAgentEvent })` streams each model
Expand Down
3 changes: 1 addition & 2 deletions apps/agent-forge/src/components/Inspector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ function useHookSelection(hooks: AgentNodeHookInstance[]) {
* `AgentNodeHookInstance`), lets you toggle each on/off, add one from a
* starter template (the redact-pii/rate-limit/audit-log/
* inject-context), and edit its code body in a real CodeMirror editor
* (replacing the mockup's static `.hook-code` preview - see
* `.design-ref/agent-forge-mockup.html`). Only rendered for `llm`/`tool`
* (not a static preview). Only rendered for `llm`/`tool`
* nodes, the two node types a `toolCall`/`generate` hook can meaningfully
* attach to.
*
Expand Down
3 changes: 1 addition & 2 deletions apps/agent-forge/src/components/SettingsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,7 @@ function DeploySection({ deployAdapter, onAdapterChange }: DeploySectionProps) {
* Real Settings tab content, replacing the static placeholder
* ("Provider keys and deploy target settings are wired up later.").
*
* Three sections, matching `.design-ref/agent-forge-mockup.html`'s
* `#drawerSettings` layout:
* Three sections:
* - Provider keys: add/replace/remove a stored key per provider, with
* the input always rendered blank (never pre-filled with a real key -
* the server only ever returns a masked status, see runtimeClient.ts's
Expand Down
3 changes: 1 addition & 2 deletions apps/agent-forge/src/components/drawer.css
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,7 @@
}

/* ---------- Chat ----------
Ported from .design-ref/agent-forge-mockup.html's `.chat-*` classes -
user/agent bubbles, avatars, timestamps, a typing indicator, and inline
The `.chat-*` classes - user/agent bubbles, avatars, timestamps, a typing indicator, and inline
tool-call/approval cards under an agent bubble. See ChatPanel.tsx. */
.chat-panel {
display: flex;
Expand Down
2 changes: 2 additions & 0 deletions apps/agent-forge/src/components/topbarNarrow.css
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
.topbar-narrow {
gap: 6px;
padding: 0 8px;
/* never widen the page: the crumbs ellipsize instead */
min-width: 0;
}
.topbar-narrow .crumbs {
flex: 1;
Expand Down
16 changes: 16 additions & 0 deletions apps/agent-forge/src/fonts.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Inter Variable, latin only. The package's own index.css / wght.css declare
* every subset (cyrillic, greek, vietnamese, latin-ext ...) and Vite would ship
* a woff2 for each, so this is the single latin wght rule copied from
* @fontsource-variable/inter/wght.css, pointing at the package's file (Vite
* resolves and fingerprints it). Imported once, from main.tsx.
*/
@font-face {
font-family: 'Inter Variable';
font-style: normal;
font-display: swap;
font-weight: 100 900;
src: url('@fontsource-variable/inter/files/inter-latin-wght-normal.woff2') format('woff2-variations');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
3 changes: 1 addition & 2 deletions apps/agent-forge/src/hooks/hookTemplates.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { AgentNodeHookPhase, AgentNodeHookPoint } from '../graph/types';

/**
* Starter hook templates, matching `.design-ref/agent-forge-mockup.html`'s
* Inspector "Hooks" section examples (`redact-pii`, `rate-limit`,
* Starter hook templates for the Inspector "Hooks" section (`redact-pii`, `rate-limit`,
* `inject-context`, `audit-log`). Each is an editable starting point - the
* Inspector's CodeMirror editor lets a user change `code` freely after
* dragging/adding one onto a node; `templateId` is kept on the resulting
Expand Down
1 change: 1 addition & 0 deletions apps/agent-forge/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './fonts.css';
import './theme.css';
import { App } from './App';
import { applyStoredTheme } from './shell/theme';
Expand Down
3 changes: 2 additions & 1 deletion apps/agent-forge/src/screens/playground/playground.css
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@
}
.pg-model {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
/* the provider select's longest option ("custom (provider/model)") needs the wider column */
grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
gap: 8px;
}

Expand Down
Loading
Loading