Skip to content

Commit 8458c79

Browse files
committed
workers
1 parent 519edb5 commit 8458c79

File tree

3 files changed

+85
-25
lines changed

3 files changed

+85
-25
lines changed

packages/ai/src/InlineWorkerCard.tsx

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,15 @@ import {
1010
} from '@phosphor-icons/react';
1111
import { AnimatePresence, motion } from 'framer-motion';
1212
import { ToolCall } from './ToolCall';
13+
import { Markdown } from './Markdown';
1314
import type { TranscriptStep } from './types';
1415
import { pairTranscriptSteps } from './types';
1516

17+
/** Collapse runs of 3+ spaces to 2, preventing Markdown 4-space code blocks. */
18+
function stripExcessWhitespace(text: string): string {
19+
return text.replace(/ {3,}/g, ' ');
20+
}
21+
1622
interface InlineWorkerCardProps {
1723
title: string;
1824
status: string;
@@ -40,17 +46,12 @@ function InlineWorkerCard({
4046

4147
const items = useMemo(() => pairTranscriptSteps(transcript), [transcript]);
4248

43-
const toolItems = useMemo(
44-
() => items.filter((item) => item.kind === 'tool'),
45-
[items]
46-
);
47-
4849
const isRunning = status === 'running';
4950
const isDone = status === 'completed';
5051

5152
return (
52-
<div className={clsx('group flex flex-col items-start', className)}>
53-
<div className="overflow-hidden rounded-2xl border border-app-line/50 bg-app-box/30 backdrop-blur-sm">
53+
<div className={clsx('group flex min-w-0 flex-col items-start', className)}>
54+
<div className="min-w-0 max-w-full overflow-hidden rounded-2xl border border-app-line/50 bg-app-box/30 backdrop-blur-sm">
5455
<button
5556
onClick={() => setExpanded((v) => !v)}
5657
className="flex w-full items-start gap-3 px-4 py-3 text-left transition-colors hover:bg-app-box/30"
@@ -123,11 +124,17 @@ function InlineWorkerCard({
123124
<div className="text-xs text-ink-faint">
124125
Loading worker transcript...
125126
</div>
126-
) : toolItems.length > 0 ? (
127-
toolItems.map((item) =>
127+
) : items.length > 0 ? (
128+
items.map((item, index) =>
128129
item.kind === 'tool' ? (
129130
<ToolCall key={item.pair.id} pair={item.pair} />
130-
) : null
131+
) : (
132+
<Markdown
133+
key={`text-${index}`}
134+
content={stripExcessWhitespace(item.text)}
135+
className="text-xs text-ink-dull"
136+
/>
137+
)
131138
)
132139
) : (
133140
<div className="text-xs text-ink-faint">

packages/ai/src/ToolCall.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -801,8 +801,8 @@ const STATUS_ICONS: Record<ToolCallStatus, string> = {
801801

802802
const STATUS_COLORS: Record<ToolCallStatus, string> = {
803803
running: "text-accent",
804-
completed: "text-emerald-500",
805-
error: "text-red-400",
804+
completed: "text-status-success",
805+
error: "text-status-error",
806806
};
807807

808808
function formatToolName(name: string): string {
@@ -847,7 +847,7 @@ export function ToolCall({pair}: {pair: ToolCallPair}) {
847847
<div
848848
className={clsx(
849849
"rounded-md border bg-app-dark-box/30",
850-
pair.status === "error" ? "border-red-500/30" : "border-app-line/50",
850+
pair.status === "error" ? "border-status-error/30" : "border-app-line/50",
851851
)}
852852
>
853853
<button

packages/primitives/src/NumberStepper.tsx

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {clsx} from "clsx";
2-
import {forwardRef} from "react";
2+
import {forwardRef, useState, useCallback, useEffect, useRef} from "react";
33
import {Minus, Plus} from "@phosphor-icons/react";
44

55
export interface NumberStepperProps {
@@ -23,7 +23,7 @@ const NumberStepper = forwardRef<HTMLDivElement, NumberStepperProps>(
2323
value,
2424
onChange,
2525
min = 0,
26-
max = 100,
26+
max,
2727
step = 1,
2828
allowFloat = false,
2929
disabled = false,
@@ -35,21 +35,50 @@ const NumberStepper = forwardRef<HTMLDivElement, NumberStepperProps>(
3535
},
3636
ref,
3737
) => {
38+
const [editing, setEditing] = useState(false);
39+
const [inputValue, setInputValue] = useState("");
40+
const inputRef = useRef<HTMLInputElement>(null);
41+
42+
useEffect(() => {
43+
if (editing && inputRef.current) {
44+
inputRef.current.focus();
45+
inputRef.current.select();
46+
}
47+
}, [editing]);
48+
49+
const clampValue = useCallback(
50+
(v: number) => {
51+
let clamped = Math.max(min, v);
52+
if (max !== undefined) clamped = Math.min(max, clamped);
53+
return clamped;
54+
},
55+
[min, max],
56+
);
57+
3858
const handleDecrement = () => {
3959
const newValue = allowFloat
40-
? Math.max(min, value - step)
41-
: Math.max(min, Math.floor(value - step));
60+
? clampValue(value - step)
61+
: clampValue(Math.floor(value - step));
4262
onChange(newValue);
4363
};
4464

4565
const handleIncrement = () => {
4666
const newValue = allowFloat
47-
? Math.min(max, value + step)
48-
: Math.min(max, Math.ceil(value + step));
67+
? clampValue(value + step)
68+
: clampValue(Math.ceil(value + step));
4969
onChange(newValue);
5070
};
5171

52-
const progress = ((value - min) / (max - min)) * 100;
72+
const commitInput = () => {
73+
const parsed = allowFloat ? parseFloat(inputValue) : parseInt(inputValue, 10);
74+
if (!isNaN(parsed)) {
75+
onChange(clampValue(parsed));
76+
}
77+
setEditing(false);
78+
};
79+
80+
const progress =
81+
max !== undefined ? ((value - min) / (max - min)) * 100 : undefined;
5382

5483
return (
5584
<div ref={ref} className={clsx("flex flex-col gap-1", className)}>
@@ -72,13 +101,37 @@ const NumberStepper = forwardRef<HTMLDivElement, NumberStepperProps>(
72101
>
73102
<Minus className="size-4 text-ink" />
74103
</button>
75-
<span className="min-w-[3rem] text-center text-sm font-medium text-ink">
76-
{allowFloat ? value.toFixed(1) : value}{suffix}
77-
</span>
104+
{editing ? (
105+
<input
106+
ref={inputRef}
107+
type="text"
108+
inputMode="decimal"
109+
value={inputValue}
110+
onChange={(e) => setInputValue(e.target.value)}
111+
onBlur={commitInput}
112+
onKeyDown={(e) => {
113+
if (e.key === "Enter") commitInput();
114+
if (e.key === "Escape") setEditing(false);
115+
}}
116+
className="min-w-[3rem] w-[4rem] bg-transparent text-center text-sm font-medium text-ink outline-none"
117+
/>
118+
) : (
119+
<span
120+
className="min-w-[3rem] text-center text-sm font-medium text-ink cursor-text select-none"
121+
onDoubleClick={() => {
122+
if (!disabled) {
123+
setInputValue(String(allowFloat ? value.toFixed(1) : value));
124+
setEditing(true);
125+
}
126+
}}
127+
>
128+
{allowFloat ? value.toFixed(1) : value}{suffix}
129+
</span>
130+
)}
78131
<button
79132
type="button"
80133
onClick={handleIncrement}
81-
disabled={disabled || value >= max}
134+
disabled={disabled || (max !== undefined && value >= max)}
82135
className={clsx(
83136
"flex h-8 w-8 items-center justify-center rounded-md border border-app-line bg-app-box",
84137
"hover:bg-app-hover disabled:opacity-50 disabled:cursor-not-allowed",
@@ -88,7 +141,7 @@ const NumberStepper = forwardRef<HTMLDivElement, NumberStepperProps>(
88141
<Plus className="size-4 text-ink" />
89142
</button>
90143
</div>
91-
{showProgress && (
144+
{showProgress && progress !== undefined && (
92145
<div className="h-1 w-full overflow-hidden rounded-full bg-app-line">
93146
<div
94147
className="h-full bg-accent transition-all duration-200"

0 commit comments

Comments
 (0)