Skip to content

feat: agents builder template, AI kit workspace primitives and a shared live preview - #43

Merged
yyyyaaa merged 6 commits into
mainfrom
phat/agents-builder-template
Sep 24, 2026
Merged

yyyyaaa merged 6 commits into
mainfrom
phat/agents-builder-template

Conversation

@yyyyaaa

@yyyyaaa yyyyaaa commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • AI kit (@constructive/ai): new agent workspace primitives — ToolTrace, ThinkingStatus, AskCard, ConnectPrompt, AgentDraftCard, UsageNotice, PromptInputTray, PromptInputAttachment(s), MarkTile, and a ModelSelector with reasoning levels, optional pricing per 1M tokens, cost tiers and recommendations. Kbd now derives its fill from the surrounding text colour, so keys read on any surface.
  • Agents Builder template (@constructive/agents-builder): an analytics agent workspace composed from the AI kit.
    • Chat with scripted, branching replies (connect a source, answer questions, approve an agent draft).
    • Inbox: agent-triaged SMS, email and Slack threads with drafted replies, tool activity and approvals.
    • Schedules: a today timeline plus cards or a table, with pause switches, run history and a live "Run now".
    • Sources, skills, and an agent page whose run shows a plan, reasoning, tool calls, a SQL query with results, sub-agents, findings with sources, and an approval step, next to a pannable canvas.
    • Controlled by data and callbacks; responsive through container queries; renders on the server without browser globals.
  • Docs site: Agents Builder page and full-screen preview, new AI kit pages, expanded Prompt Input compositions, and one shared LivePreview shell (viewport switch, custom width, reload, open in new tab, full screen, loading state) replacing the per-showcase shells.
  • Deps: React 19.3 and motion 13.4 (dev dependencies; AnimateView is used only for in-block show/hide swaps and reorders, never for route transitions).

Test plan

  • UI typecheck
  • Agents Builder behaviour tests (views, inbox reply and approval, schedule toggle, chat connect flow, server rendering)
  • AI kit tests (ask card, model selector)
  • Registry build and generated demo source check
  • Browser QA in Storybook and the docs site: light and dark, desktop and phone widths
  • pnpm check, pnpm build:pages, pnpm build:storybook in CI
  • Clean registry install of ai and agents-builder

Generated with Devin

… kit

Tool trace, thinking status, ask card, connect prompt, agent draft card, usage notice, prompt tray and attachments, and a model selector with reasoning levels, pricing and recommendations. Kbd now derives its fill from the surrounding text colour so it reads on any surface.
An analytics agent workspace: chat with scripted replies, an agentic inbox, schedules as cards or a table, sources, skills, and an agent page with a live run and canvas. Responsive through container queries and safe for server rendering.
…shell

Adds the Agents Builder page and preview route, AI kit docs and prompt input compositions, and replaces the per-showcase preview shells with a single LivePreview.
@tenki-reviewer

tenki-reviewer Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Review complete. 🟡 4 medium

💬 Inline comments (4)

🧹 Nitpicks (2) — 🟢 2 low
  • 🟢 Documented view union omits inbox and schedules (application-blocks.ts:457) — The new agents-builder API reference documents view / defaultView / onViewChange as "chat" | "integrations" | "skills" | "agent" (apps/blocks/src/lib/application-blocks.ts:457), but the actual AgentsBuilderView type is "chat" | "inbox" | "schedules" | "integrations" | "skills" | "agent" and AgentsBuilder renders InboxView and SchedulesView for those values.
  • 🟢 ToolTraceDemo needs an extra click to restart (ai-component-preview.tsx:705) — The status mapping gives every step done once done === TRACE_STEPS.length and no step running (since index === done never matches), yet the button still reads "Advance" because the label check uses done > TRACE_STEPS.length (apps/blocks/src/components/ai-showcase/ai-component-preview.tsx:710).

This PR introduces the agents-builder component tree in packages/ui, a suite of AI primitives (PromptInputTray, ModelSelector, ToolTrace, ThinkingStatus, UsageNotice, ConnectPrompt, AgentDraftCard), and a redesigned Kbd, plus docs-app showcase pages, registry metadata, smoke-install coverage, tests, and workspace-wide React 19.3/motion 13 bumps.

Files Change
packages/ui/src/components/agents-builder/* New scripted demo Agents Builder block: canvas, chat, inbox, schedules, integrations, skills views with playback hooks and view-transition guards
packages/ui/src/components/ai/* New AI kit primitives (prompt-input tray/attachment, model selector, tool trace, status/usage/ask/draft cards)
packages/ui/registry.json, scripts/build-registry.ts, apps/registry/scripts/smoke-install.ts Registry entries and smoke-install coverage for the new roots
apps/blocks/src/** Docs showcase pages, live-preview shell extraction, navigation and generated demo-source updates
packages/*/package.json React 19.3 / motion 13 dependency bumps with widened peer ranges
packages/ui/test/* Tests for agents-builder (incl. SSR), ask-card, and model-selector; CSS.escape polyfill per repo guidance

Reviewed commit: 4de8dcc

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updated@​types/​react@​19.2.17 ⏵ 19.3.0100 +110079 +193100
Updatedreact@​19.2.7 ⏵ 19.3.0100 +110084 +197100
Updatedreact-dom@​19.2.7 ⏵ 19.3.010010092 +198100
Updatedmotion@​12.42.2 ⏵ 13.4.0100 +1100100 +197100

View full report

@yyyyaaa
yyyyaaa merged commit 8017f35 into main Sep 24, 2026
6 checks passed

@tenki-reviewer tenki-reviewer Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adds the Agents Builder template block with new AI kit primitives, registry and docs wiring, and React 19.3/motion 13 dependency bumps.

Key findings

Comment on lines +441 to +447
<ResizeHandle
max={Math.max(MIN_CUSTOM_WIDTH, Math.floor(available))}
onReset={() => onCustomWidth(null)}
onResize={onCustomWidth}
onResizingChange={setResizing}
width={Math.round(width * scale)}
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 bug · medium

Resize handle mixes displayed and logical widths

The resize handle receives the frame's displayed width, Math.round(width * scale), but the values it emits are stored as the logical customWidth (apps/blocks/src/components/docs/live-preview.tsx:446). When the stage is narrower than the frame (scale < 1), dragging computes the next size from the scaled starting width and stores it unscaled, so a rightward drag shrinks the frame (e.g. at scale 0.8 a displayed 312 stores 332, which renders as 266).

The keyboard bounds are also in the wrong space: Home/MIN_CUSTOM_WIDTH = 320 is a logical minimum compared against a displayed value, and aria-valuemax/aria-valuenow mix the two units, so reported values are wrong whenever scale ≠ 1.

📋 Prompt for AI Agents

In apps/blocks/src/components/docs/live-preview.tsx around lines 441-447, <ResizeHandle> is given width={Math.round(width * scale)} and max={Math.floor(available)}, but the values it emits via onResize are stored as the logical customWidth. Fix the unit mismatch: pass the logical width (and a logical max of roughly available / scale) into ResizeHandle, keep the MIN_CUSTOM_WIDTH clamp in logical units, and only convert to display space (multiply by scale) for aria-valuenow and aria-valuetext. This prevents rightward drags from shrinking the frame and wrong aria values when the preview is scaled down (scale < 1).

return (
<th
scope="col"
aria-sort={active ? sort?.direction : undefined}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 bug · medium

Updated column inverts aria-sort semantics

The skills table sets aria-sort={active ? sort?.direction : undefined} (packages/ui/src/components/agents-builder/skills-view.tsx:74), but the updated comparator is newest-first (packages/ui/src/components/agents-builder/skills-view.tsx:202) while name/type/author are oldest-modelled ascending. Clicking the Updated column starts at direction ascending yet renders newest-first, so assistive tech is told the column is sorted ascending when the visible order is descending by date. Screen-reader users get the wrong model of the table ordering.

📋 Prompt for AI Agents

In packages/ui/src/components/agents-builder/skills-view.tsx, the 'updated' case in compareSkills (line 202) sorts newest-first while the other columns sort ascending-first, so for the Updated column the direction state (and the aria-sort emitted at line 74) is inverted relative to the actual row order. Change the 'updated' comparator to Date.parse(left.updatedAt) - Date.parse(right.updatedAt) so all columns share the same ascending-first convention, keeping the existing reverse-on-descending logic at line 231.

Comment on lines +155 to +168
export function useAgentRun(script: AgentRunScript, { autoplay = true }: { autoplay?: boolean } = {}) {
const timeline = React.useMemo(() => buildRunTimeline(script), [script]);
const [run, setRun] = React.useState(() =>
autoplay ? { state: INITIAL, next: 0 } : { state: settledState(script), next: timeline.length },
);

usePlaybackClock(timeline[run.next], false, () =>
setRun((current) => {
const frame = timeline[current.next];
return frame ? { state: frame.apply(current.state), next: current.next + 1 } : current;
}),
);

const stop = React.useCallback(() => setRun({ state: settledState(script), next: timeline.length }), [script, timeline.length]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 bug · medium

useAgentRun never resets when script changes

useAgentRun rebuilds the timeline whenever the script identity changes (packages/ui/src/components/agents-builder/use-agent-run.ts:156) but never resets run.next or the run state, which are seeded only once at mount (packages/ui/src/components/agents-builder/use-agent-run.ts:157-159). If a host passes a new script (e.g. after a data update), playback resumes at the old index into the new timeline: frames can be skipped, misapplied, or — when the new timeline is shorter — next lands past the end and the run freezes on stale state. A host-driven data change mid-run leaves the run panel showing a broken, half-played state.

📋 Prompt for AI Agents

In packages/ui/src/components/agents-builder/use-agent-run.ts, useAgentRun (lines 155-171) seeds run only in the useState initializer, so a change to the script prop rebuilds timeline (line 156) while run.next keeps its old value, causing skipped or frozen playback. Add a React.useEffect(() => setRun(autoplay ? { state: INITIAL, next: 0 } : { state: settledState(script), next: timeline.length }), [script]) that resets the run whenever script identity changes.

useEffect(() => {
setLoaded(false);
const frame = innerRef.current;
if (frame?.contentDocument?.readyState === 'complete' && frame.contentDocument.URL !== 'about:blank') setLoaded(true);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 bug · medium

Stale readyState check defeats the loading veil on reload

In PreviewFrame, the effect keyed on src calls setLoaded(false) and then immediately reads frame.contentDocument.readyState (apps/blocks/src/components/docs/live-preview.tsx:245); on a src change the iframe still holds the previous, complete document, so setLoaded(true) overrides the reset in the same batch. The loading veil therefore never renders for the Reload button or any src change — users see stale frame content with no loading indication until the new document's load event fires. Only the first mount behaves correctly, because the initial document is about:blank.

📋 Prompt for AI Agents

In apps/blocks/src/components/docs/live-preview.tsx, the effect in PreviewFrame (lines 242-248) resets loaded on src change but then synchronously reads the still-old document via frame.contentDocument?.readyState === 'complete', immediately setting loaded back to true so the loading veil never shows on reload. Keep the synchronous pre-check only for the first mount (e.g. compare against a ref of the previously loaded src) so src changes rely solely on the load event and the LOAD_FALLBACK_MS timer.

@yyyyaaa
yyyyaaa deleted the phat/agents-builder-template branch September 25, 2026 02:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant