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
2 changes: 1 addition & 1 deletion motion-kit/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ const chatHooks = (html: string, spec: DemoSpec): string => {
.forEach((turn, index) => {
hook(turn, `activity-${index}`);
within(turn)
.filter((el) => el.name === 'li' && hasClass(el, activityStyles.tool!))
.filter((el) => el.name === 'li' && hasClass(el, activityStyles.step!))
.forEach((line, step) => hook(line, `activity-${index}-${step}`));
});

Expand Down
43 changes: 11 additions & 32 deletions src/components/organisms/Studio/hooks/useChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { useCallback, useEffect, useRef, useState } from 'react';

import {
endTurnActivity,
recordApproval,
recordStep,
restoreActivity,
type ActivityEntry,
type SavedActivityEntry,
Expand Down Expand Up @@ -557,38 +559,9 @@ export const useChat = (initial?: ChatSnapshot) => {

// One Activity line per step_id. A later frame for the
// same step closes or updates the line it opened.
patchActivity((entry) => {
const existing = entry.tools.find((tool) => tool.id === frame.step_id);
const patch = {
summary: frame.detail ?? undefined,
durationMs: frame.duration_ms ?? undefined,
widgetId: frame.produced?.component_id,
state: frame.status,
};
const arrived = Date.now();

if (!existing) {
return {
...entry,
tools: [
...entry.tools,
{
id: frame.step_id,
name: frame.label,
args: frame.tool ?? undefined,
startedAt: Date.now(),
...patch,
},
],
};
}

return {
...entry,
tools: entry.tools.map((tool) =>
tool.id === frame.step_id ? { ...tool, ...patch } : tool,
),
};
});
patchActivity((entry) => recordStep(entry, frame, arrived));

break;
}
Expand Down Expand Up @@ -686,12 +659,18 @@ export const useChat = (initial?: ChatSnapshot) => {
}
case 'plan.approved': {
// Comes first on the approve's stream, so the reply it
// writes into is the transcript's last message.
// writes into is the transcript's last message. The
// turn's Activity entry records the approval time,
// which places the plan among its steps.
const approved = approvedFromFrame(frame);
const approvedAt = Date.now();

setState((prev) => ({
...prev,
messages: withApprovedPlan(prev.messages, approved),
activity: prev.activity.map((entry) =>
entry.turn === turn ? recordApproval(entry, approvedAt) : entry,
),
}));
break;
}
Expand Down
Loading
Loading