Skip to content

Commit 1545c27

Browse files
author
acore2026
committed
update
1 parent c156eb2 commit 1545c27

3 files changed

Lines changed: 322 additions & 76 deletions

File tree

src/App.tsx

Lines changed: 192 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ type PresentationMessage = {
9494
sections: PresentationMessageDetailSection[];
9595
};
9696
};
97+
type NarrativeStageSummary = {
98+
id: string;
99+
summaryTitle: string;
100+
status: 'pending' | 'active' | 'done';
101+
taskTitle: string;
102+
body?: string;
103+
items: Array<{ id: string; label: string; phase: 'pending' | 'processing' | 'done' }>;
104+
};
97105
type ScriptBubble = { node: string; text: string; icon?: BubbleIcon };
98106
type ScriptAction = {
99107
id: string;
@@ -1591,6 +1599,100 @@ function buildFinalDeliveryPresentation(messages: PresentationMessage[]) {
15911599
};
15921600
}
15931601

1602+
function getFixedStageSummaryTitle(stageIndex: number) {
1603+
switch (stageIndex) {
1604+
case 0:
1605+
return 'Onboard Family Domain';
1606+
case 1:
1607+
return 'Discover And Assign Courier';
1608+
case 2:
1609+
return 'Notify And Coordinate Delivery';
1610+
case 3:
1611+
return 'Verify And Close Handoff';
1612+
default:
1613+
return `Stage ${stageIndex + 1}`;
1614+
}
1615+
}
1616+
1617+
function getStageNarrativeSnapshot(script: DemoScript, stageIndex: number) {
1618+
const stage = script.stages[stageIndex];
1619+
if (!stage) {
1620+
return { title: 'Narrative unavailable', body: '' };
1621+
}
1622+
const firstAction = flattenStage(stage)[0];
1623+
if (stageIndex === 0) {
1624+
return {
1625+
title: 'Family domain created and ready',
1626+
body: '- **Family Domain** is now active.\n- Members: **UE Assistant** and **RobotDog**.\n- Domain ID: `4sg520s2.acn.domain.cmcc`.',
1627+
};
1628+
}
1629+
if (stageIndex === 1) {
1630+
return {
1631+
title: 'Courier discovered and pickup assigned',
1632+
body: '- **RobotArm** has been selected as the courier.\n- The pickup task has been delivered across both gateways.',
1633+
};
1634+
}
1635+
if (stageIndex === 2) {
1636+
return {
1637+
title: 'User notified and delivery is underway',
1638+
body: '- The phone has received the **RobotArm** identity.\n- The delivery workflow is now moving into live coordination.',
1639+
};
1640+
}
1641+
if (stageIndex === 3) {
1642+
return buildFinalDeliveryPresentation([]);
1643+
}
1644+
const scenarioIds = firstAction ? (ACTION_SCENARIO_MAP[firstAction.id] ?? STAGE_SCENARIO_MAP[stageIndex] ?? []) : (STAGE_SCENARIO_MAP[stageIndex] ?? []);
1645+
return deriveScenarioNarrative(
1646+
scenarioIds,
1647+
firstAction?.presentation?.title ?? stage.title,
1648+
firstAction?.presentation?.body ?? `- **Current step:** ${firstAction?.stepLabel ?? stage.title}`,
1649+
);
1650+
}
1651+
1652+
function deriveNarrativeStageSummaries(
1653+
script: DemoScript,
1654+
playback: PlaybackFrame,
1655+
presentationCard: { title: string; body: string; messages: PresentationMessage[] },
1656+
): NarrativeStageSummary[] {
1657+
return script.stages.map((stage, index) => {
1658+
const stageActions = flattenStage(stage);
1659+
let status: NarrativeStageSummary['status'] = 'pending';
1660+
if (playback.phase === 'complete') {
1661+
status = 'done';
1662+
} else if (playback.phase !== 'standby') {
1663+
if (index < playback.stageIndex) {
1664+
status = 'done';
1665+
} else if (index === playback.stageIndex) {
1666+
status = playback.phase === 'gate' ? 'done' : 'active';
1667+
}
1668+
}
1669+
1670+
const fallbackSnapshot = getStageNarrativeSnapshot(script, index);
1671+
const title = index === playback.stageIndex && playback.phase !== 'standby'
1672+
? presentationCard.title
1673+
: fallbackSnapshot.title;
1674+
const body = index === playback.stageIndex && playback.phase !== 'standby'
1675+
? presentationCard.body
1676+
: fallbackSnapshot.body;
1677+
const items = index === playback.stageIndex
1678+
? playback.checklistItems
1679+
: stageActions.map((action) => ({
1680+
id: action.id,
1681+
label: action.checklistTitle ? checklistDisplayId(action.id) : action.stepLabel,
1682+
phase: status === 'done' ? 'done' as const : 'pending' as const,
1683+
}));
1684+
1685+
return {
1686+
id: stage.id,
1687+
summaryTitle: getFixedStageSummaryTitle(index),
1688+
status,
1689+
taskTitle: title,
1690+
body,
1691+
items,
1692+
};
1693+
});
1694+
}
1695+
15941696
function derivePresentationCard(
15951697
script: DemoScript,
15961698
playback: PlaybackFrame,
@@ -1930,6 +2032,7 @@ function Dashboard() {
19302032
const [scriptPanelOpen, setScriptPanelOpen] = useState(false);
19312033
const [selectedMessage, setSelectedMessage] = useState<PresentationMessage | null>(null);
19322034
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
2035+
const [expandedNarrativeStageId, setExpandedNarrativeStageId] = useState<string | null>(null);
19332036
const scriptRef = useRef(scriptDoc);
19342037
const playbackRef = useRef(playback);
19352038
const pollInFlightRef = useRef(false);
@@ -2348,6 +2451,19 @@ function Dashboard() {
23482451
? 'Continue'
23492452
: 'Start';
23502453
const presentationCard = derivePresentationCard(scriptDoc, playback, activeAction);
2454+
const narrativeStages = deriveNarrativeStageSummaries(scriptDoc, playback, presentationCard);
2455+
const currentNarrativeTitle = presentationCard.title;
2456+
2457+
useEffect(() => {
2458+
const currentStageId = scriptDoc.stages[playback.stageIndex]?.id ?? null;
2459+
if (currentStageId) {
2460+
setExpandedNarrativeStageId(currentStageId);
2461+
return;
2462+
}
2463+
if (playback.phase === 'standby') {
2464+
setExpandedNarrativeStageId(null);
2465+
}
2466+
}, [playback.phase, playback.stageIndex, scriptDoc.stages]);
23512467

23522468
useEffect(() => {
23532469
const previousPhase = previousPlaybackPhaseRef.current;
@@ -2521,10 +2637,83 @@ function Dashboard() {
25212637
<aside className="sidebar">
25222638
<div className="presentation-panel">
25232639
<div className="presentation-panel-kicker">✨ AI Narrative</div>
2524-
<h2 className="presentation-panel-title">{presentationCard.title}</h2>
2525-
<div className="presentation-panel-body">{renderMessageBody(presentationCard.body)}</div>
2640+
<h2 className="presentation-current-title">{currentNarrativeTitle}</h2>
2641+
<div className="narrative-progress">
2642+
<div className="narrative-progress-list">
2643+
{narrativeStages.map((stage, index) => (
2644+
<section
2645+
key={stage.id}
2646+
className={cn(
2647+
'narrative-stage-card',
2648+
stage.status === 'active' && 'narrative-stage-card-active',
2649+
stage.status === 'done' && 'narrative-stage-card-done',
2650+
)}
2651+
>
2652+
<button
2653+
type="button"
2654+
className="narrative-stage-head"
2655+
onClick={() => setExpandedNarrativeStageId((current) => current === stage.id ? null : stage.id)}
2656+
>
2657+
<span className={cn(
2658+
'narrative-stage-dot',
2659+
stage.status === 'active' && 'narrative-stage-dot-active',
2660+
stage.status === 'done' && 'narrative-stage-dot-done',
2661+
)}>
2662+
{stage.status === 'done' ? '✓' : index + 1}
2663+
</span>
2664+
<div className="narrative-stage-copy">
2665+
<div className="narrative-stage-summary">{stage.summaryTitle}</div>
2666+
<div className="narrative-stage-task-title">{stage.taskTitle}</div>
2667+
</div>
2668+
</button>
2669+
{expandedNarrativeStageId === stage.id ? (
2670+
<div className="narrative-stage-expanded">
2671+
{stage.items.length ? (
2672+
<div className="narrative-stage-items">
2673+
{stage.items.map((item) => (
2674+
<div
2675+
key={item.id}
2676+
className={cn(
2677+
'step-queue-item',
2678+
item.phase === 'processing' && 'step-queue-item-active',
2679+
item.phase === 'done' && 'step-queue-item-done',
2680+
)}
2681+
>
2682+
<span className="step-queue-dot">
2683+
{item.phase === 'done' ? '✓' : item.phase === 'processing' ? '•' : ''}
2684+
</span>
2685+
<div className="step-queue-copy">
2686+
<span className="step-queue-label">
2687+
{item.phase === 'processing' ? 'In Progress' : item.phase === 'done' ? 'Done' : 'Pending'}
2688+
</span>
2689+
<span className="step-queue-value">{item.label}</span>
2690+
{stage.status === 'active' && item.phase === 'processing' && stage.body ? (
2691+
<div className="step-queue-description">{renderMessageBody(stage.body)}</div>
2692+
) : null}
2693+
</div>
2694+
</div>
2695+
))}
2696+
</div>
2697+
) : stage.status === 'active' && stage.body ? (
2698+
<div className="narrative-stage-items">
2699+
<div className="step-queue-item step-queue-item-active">
2700+
<span className="step-queue-dot"></span>
2701+
<div className="step-queue-copy">
2702+
<span className="step-queue-label">In Progress</span>
2703+
<span className="step-queue-value">{stage.taskTitle}</span>
2704+
<div className="step-queue-description">{renderMessageBody(stage.body)}</div>
2705+
</div>
2706+
</div>
2707+
</div>
2708+
) : null}
2709+
</div>
2710+
) : null}
2711+
</section>
2712+
))}
2713+
</div>
2714+
</div>
25262715
<div className="presentation-chat">
2527-
<div className="presentation-chat-kicker">Agent Chat & Messages</div>
2716+
<div className="presentation-chat-kicker">Agent Messages</div>
25282717
<div ref={presentationChatListRef} className="presentation-chat-list">
25292718
{presentationCard.messages.map((message, index) => (
25302719
<button
@@ -2549,13 +2738,6 @@ function Dashboard() {
25492738
<span className="presentation-chat-speaker">{message.title}</span>
25502739
{message.time ? <span className="presentation-chat-time">{message.time}</span> : null}
25512740
</div>
2552-
{message.chips?.length ? (
2553-
<div className="presentation-chat-chips">
2554-
{message.chips.map((chip) => (
2555-
<span key={chip} className="presentation-chat-chip">{chip}</span>
2556-
))}
2557-
</div>
2558-
) : null}
25592741
<div className="presentation-chat-body">{renderMessageBody(message.body)}</div>
25602742
</div>
25612743
</button>
@@ -2579,13 +2761,6 @@ function Dashboard() {
25792761
<h3 className="message-detail-title">{selectedMessage.details.title ?? selectedMessage.title}</h3>
25802762
{selectedMessage.time ? <span className="message-detail-time">{selectedMessage.time}</span> : null}
25812763
</div>
2582-
{selectedMessage.chips?.length ? (
2583-
<div className="message-detail-chips">
2584-
{selectedMessage.chips.map((chip) => (
2585-
<span key={chip} className="message-detail-chip">{chip}</span>
2586-
))}
2587-
</div>
2588-
) : null}
25892764
<div className="message-detail-summary">{renderMessageBody(selectedMessage.body)}</div>
25902765
<div className="message-detail-sections">
25912766
{selectedMessage.details.sections.map((section) => (

0 commit comments

Comments
 (0)