@seed-ship/mcp-ui-solid 4.0.5 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/AgentCard.cjs +122 -0
- package/dist/components/AgentCard.cjs.map +1 -0
- package/dist/components/AgentCard.d.ts +20 -0
- package/dist/components/AgentCard.d.ts.map +1 -0
- package/dist/components/AgentCard.js +122 -0
- package/dist/components/AgentCard.js.map +1 -0
- package/dist/components/AgentHandoff.cjs +49 -0
- package/dist/components/AgentHandoff.cjs.map +1 -0
- package/dist/components/AgentHandoff.d.ts +12 -0
- package/dist/components/AgentHandoff.d.ts.map +1 -0
- package/dist/components/AgentHandoff.js +49 -0
- package/dist/components/AgentHandoff.js.map +1 -0
- package/dist/components/BriefingDiff.cjs +165 -0
- package/dist/components/BriefingDiff.cjs.map +1 -0
- package/dist/components/BriefingDiff.d.ts +12 -0
- package/dist/components/BriefingDiff.d.ts.map +1 -0
- package/dist/components/BriefingDiff.js +165 -0
- package/dist/components/BriefingDiff.js.map +1 -0
- package/dist/components/ScratchpadPanel.cjs +618 -411
- package/dist/components/ScratchpadPanel.cjs.map +1 -1
- package/dist/components/ScratchpadPanel.d.ts.map +1 -1
- package/dist/components/ScratchpadPanel.js +619 -412
- package/dist/components/ScratchpadPanel.js.map +1 -1
- package/dist/components/SplitStepper.cjs +121 -0
- package/dist/components/SplitStepper.cjs.map +1 -0
- package/dist/components/SplitStepper.d.ts +12 -0
- package/dist/components/SplitStepper.d.ts.map +1 -0
- package/dist/components/SplitStepper.js +121 -0
- package/dist/components/SplitStepper.js.map +1 -0
- package/dist/components/UIResourceRenderer.cjs +1 -2
- package/dist/components/UIResourceRenderer.cjs.map +1 -1
- package/dist/components/UIResourceRenderer.js +2 -3
- package/dist/components/UIResourceRenderer.js.map +1 -1
- package/dist/components/index.d.ts +8 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components.cjs +9 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +8 -0
- package/dist/components.d.ts +8 -0
- package/dist/components.js +9 -0
- package/dist/components.js.map +1 -1
- package/dist/index.cjs +9 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/types/chat-bus.d.ts +81 -1
- package/dist/types/chat-bus.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/AgentCard.tsx +109 -0
- package/src/components/AgentHandoff.tsx +64 -0
- package/src/components/BriefingDiff.tsx +93 -0
- package/src/components/ScratchpadPanel.tsx +131 -49
- package/src/components/SplitStepper.tsx +111 -0
- package/src/components/UIResourceRenderer.tsx +1 -1
- package/src/components/index.ts +13 -0
- package/src/index.ts +15 -0
- package/src/types/chat-bus.ts +70 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BriefingDiff — highlight changes between two briefings
|
|
3
|
+
* v4.1.0: AITL sprint — added/removed/changed entries with color coding
|
|
4
|
+
*
|
|
5
|
+
* @experimental
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { For, Show } from 'solid-js'
|
|
9
|
+
import type { BriefingDiffContent } from '../types/chat-bus'
|
|
10
|
+
|
|
11
|
+
export interface BriefingDiffProps {
|
|
12
|
+
content: BriefingDiffContent
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const CHANGE_CONFIG: Record<string, { icon: string; color: string; bg: string }> = {
|
|
16
|
+
added: { icon: '+', color: 'text-green-700 dark:text-green-300', bg: 'bg-green-50 dark:bg-green-900/10' },
|
|
17
|
+
removed: { icon: '\u2212', color: 'text-red-700 dark:text-red-300', bg: 'bg-red-50 dark:bg-red-900/10' },
|
|
18
|
+
changed: { icon: '\u2194', color: 'text-amber-700 dark:text-amber-300', bg: 'bg-amber-50 dark:bg-amber-900/10' },
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function BriefingDiff(props: BriefingDiffProps) {
|
|
22
|
+
const c = () => props.content
|
|
23
|
+
|
|
24
|
+
if (typeof console !== 'undefined') {
|
|
25
|
+
console.info('[MCP-UI:BriefingDiff] mounted', {
|
|
26
|
+
changes: c().changes.length,
|
|
27
|
+
stats: c().stats,
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div class="briefing-diff space-y-2">
|
|
33
|
+
{/* Header */}
|
|
34
|
+
<Show when={c().title || c().previousDate || c().currentDate}>
|
|
35
|
+
<div class="flex items-center justify-between text-xs text-gray-500 dark:text-gray-400">
|
|
36
|
+
<Show when={c().title}><span class="font-medium">{c().title}</span></Show>
|
|
37
|
+
<Show when={c().previousDate && c().currentDate}>
|
|
38
|
+
<span>{c().previousDate} → {c().currentDate}</span>
|
|
39
|
+
</Show>
|
|
40
|
+
</div>
|
|
41
|
+
</Show>
|
|
42
|
+
|
|
43
|
+
{/* Stats summary */}
|
|
44
|
+
<Show when={c().stats}>
|
|
45
|
+
{(stats) => (
|
|
46
|
+
<div class="flex items-center gap-3 text-xs">
|
|
47
|
+
<Show when={stats().added > 0}>
|
|
48
|
+
<span class="text-green-600 dark:text-green-400">+{stats().added} added</span>
|
|
49
|
+
</Show>
|
|
50
|
+
<Show when={stats().removed > 0}>
|
|
51
|
+
<span class="text-red-600 dark:text-red-400">{stats().removed} removed</span>
|
|
52
|
+
</Show>
|
|
53
|
+
<Show when={stats().changed > 0}>
|
|
54
|
+
<span class="text-amber-600 dark:text-amber-400">{stats().changed} changed</span>
|
|
55
|
+
</Show>
|
|
56
|
+
</div>
|
|
57
|
+
)}
|
|
58
|
+
</Show>
|
|
59
|
+
|
|
60
|
+
{/* Change list */}
|
|
61
|
+
<div class="space-y-1">
|
|
62
|
+
<For each={c().changes}>
|
|
63
|
+
{(change) => {
|
|
64
|
+
const cfg = CHANGE_CONFIG[change.type] || CHANGE_CONFIG.changed
|
|
65
|
+
return (
|
|
66
|
+
<div class={`flex items-start gap-2 px-2 py-1.5 rounded ${cfg.bg}`}>
|
|
67
|
+
<span class={`flex-shrink-0 w-5 h-5 rounded-full flex items-center justify-center text-xs font-bold ${cfg.color}`}>
|
|
68
|
+
{cfg.icon}
|
|
69
|
+
</span>
|
|
70
|
+
<div class="flex-1 min-w-0">
|
|
71
|
+
<span class={`text-sm font-medium ${cfg.color}`}>{change.label}</span>
|
|
72
|
+
<Show when={change.type === 'changed' && change.previous && change.current}>
|
|
73
|
+
<div class="mt-0.5 text-xs">
|
|
74
|
+
<span class="text-red-500 line-through">{change.previous}</span>
|
|
75
|
+
<span class="mx-1 text-gray-400">→</span>
|
|
76
|
+
<span class="text-green-600 dark:text-green-400">{change.current}</span>
|
|
77
|
+
</div>
|
|
78
|
+
</Show>
|
|
79
|
+
<Show when={change.type === 'added' && change.current}>
|
|
80
|
+
<div class="mt-0.5 text-xs text-gray-600 dark:text-gray-400">{change.current}</div>
|
|
81
|
+
</Show>
|
|
82
|
+
<Show when={change.type === 'removed' && change.previous}>
|
|
83
|
+
<div class="mt-0.5 text-xs text-gray-500 line-through">{change.previous}</div>
|
|
84
|
+
</Show>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
)
|
|
88
|
+
}}
|
|
89
|
+
</For>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
@@ -6,13 +6,17 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { Component, Show, For, Switch, Match, createSignal, createEffect, onCleanup } from 'solid-js'
|
|
9
|
-
import type { ScratchpadState, ScratchpadSection, VerifiedTextContent, DataPreviewContent, MapSectionContent } from '../types/chat-bus'
|
|
9
|
+
import type { ScratchpadState, ScratchpadSection, VerifiedTextContent, DataPreviewContent, MapSectionContent, AgentCardContent, SplitStepperContent, AgentHandoffContent, BriefingDiffContent } from '../types/chat-bus'
|
|
10
10
|
import type { FormFieldParams, ChartComponentParams } from '../types'
|
|
11
11
|
import { FormFieldRenderer } from './FormFieldRenderer'
|
|
12
12
|
import { VerifiedText } from './VerifiedText'
|
|
13
13
|
import { DataPreviewSection } from './DataPreviewSection'
|
|
14
14
|
import { MapRenderer } from './MapRenderer'
|
|
15
15
|
import { ChartJSRenderer } from './ChartJSRenderer'
|
|
16
|
+
import { AgentCard, AgentStatusBadge } from './AgentCard'
|
|
17
|
+
import { SplitStepper } from './SplitStepper'
|
|
18
|
+
import { AgentHandoff } from './AgentHandoff'
|
|
19
|
+
import { BriefingDiff } from './BriefingDiff'
|
|
16
20
|
|
|
17
21
|
export interface ScratchpadPanelProps {
|
|
18
22
|
state: ScratchpadState
|
|
@@ -177,6 +181,14 @@ export const ScratchpadPanel: Component<ScratchpadPanelProps> = (props) => {
|
|
|
177
181
|
<div class="flex items-center gap-2">
|
|
178
182
|
<span class="text-base">📝</span>
|
|
179
183
|
<h3 class="text-sm font-semibold text-gray-900 dark:text-white">{props.state.title}</h3>
|
|
184
|
+
{/* AgentStatusBadge — auto-detected from agent_card sections (v4.1.0) */}
|
|
185
|
+
{(() => {
|
|
186
|
+
const agentSection = props.state.sections.find(s => s.type === 'agent_card')
|
|
187
|
+
if (!agentSection) return null
|
|
188
|
+
const ac = parseContent(agentSection.content) as AgentCardContent | null
|
|
189
|
+
if (!ac?.name) return null
|
|
190
|
+
return <AgentStatusBadge agentName={ac.name} status={ac.status || 'idle'} />
|
|
191
|
+
})()}
|
|
180
192
|
<Show when={isCollapsible()}>
|
|
181
193
|
<svg class={`w-3.5 h-3.5 text-gray-400 transition-transform ${collapsed() ? '-rotate-90' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
182
194
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
|
@@ -363,7 +375,7 @@ const SectionRenderer: Component<{
|
|
|
363
375
|
onSubmit?: (sectionId: string, values: Record<string, unknown>) => void
|
|
364
376
|
}> = (props) => {
|
|
365
377
|
return (
|
|
366
|
-
<div class="px-4 py-3">
|
|
378
|
+
<div class="px-4 py-3 animate-[slideDown_0.2s_ease-out]">
|
|
367
379
|
<h4 class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-2">{props.section.title}</h4>
|
|
368
380
|
<Switch>
|
|
369
381
|
<Match when={props.section.type === 'data'}><DataSection content={parseContent(props.section.content)} /></Match>
|
|
@@ -383,6 +395,10 @@ const SectionRenderer: Component<{
|
|
|
383
395
|
<Match when={props.section.type === 'data_preview'}><DataPreviewSection content={parseContent(props.section.content) as DataPreviewContent} /></Match>
|
|
384
396
|
<Match when={props.section.type === 'map'}>{(() => { const c = parseContent(props.section.content) as MapSectionContent; return <MapRenderer params={{ geojson: c.geojson, center: c.center, zoom: c.zoom, geojsonStyle: c.style, popup: c.popup, layers: c.layers, height: c.height || '300px', fitBounds: true }} /> })()}</Match>
|
|
385
397
|
<Match when={props.section.type === 'chart'}>{(() => { const c = parseContent(props.section.content) as ChartComponentParams; return <ChartJSRenderer component={{ id: props.section.id, type: 'chart', position: { colStart: 1, colSpan: 12 }, params: { ...c, renderer: 'native', height: (c as any)?.height || '250px' } }} /> })()}</Match>
|
|
398
|
+
<Match when={props.section.type === 'agent_card'}><AgentCard content={parseContent(props.section.content) as AgentCardContent} /></Match>
|
|
399
|
+
<Match when={props.section.type === 'split_stepper'}><SplitStepper content={parseContent(props.section.content) as SplitStepperContent} /></Match>
|
|
400
|
+
<Match when={props.section.type === 'agent_handoff'}><AgentHandoff content={parseContent(props.section.content) as AgentHandoffContent} /></Match>
|
|
401
|
+
<Match when={props.section.type === 'briefing_diff'}><BriefingDiff content={parseContent(props.section.content) as BriefingDiffContent} /></Match>
|
|
386
402
|
<Match when={true}><pre class="text-xs text-gray-500 overflow-auto">{JSON.stringify(props.section.content, null, 2)}</pre></Match>
|
|
387
403
|
</Switch>
|
|
388
404
|
</div>
|
|
@@ -654,30 +670,63 @@ const ActionSection: Component<{
|
|
|
654
670
|
content: unknown
|
|
655
671
|
onAction?: (action: string, data?: unknown) => void
|
|
656
672
|
}> = (props) => {
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
if (
|
|
661
|
-
|
|
662
|
-
return obj.actions as Array<{ label: string; value?: string; action?: string; variant?: string; icon?: string }>
|
|
663
|
-
}
|
|
664
|
-
return []
|
|
673
|
+
const data = () => {
|
|
674
|
+
const c = props.content as any
|
|
675
|
+
if (Array.isArray(c)) return { actions: c, title: undefined, preview: undefined, validation: undefined }
|
|
676
|
+
if (c && Array.isArray(c.actions)) return { actions: c.actions, title: c.title, preview: c.preview, validation: c.validation }
|
|
677
|
+
return { actions: [], title: undefined, preview: undefined, validation: undefined }
|
|
665
678
|
}
|
|
679
|
+
|
|
666
680
|
return (
|
|
667
|
-
<div
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
681
|
+
<div>
|
|
682
|
+
{/* Confirm checkpoint: title + preview (v4.1.0) */}
|
|
683
|
+
<Show when={data().title}>
|
|
684
|
+
<p class="text-sm font-medium text-gray-800 dark:text-gray-200 mb-2">{data().title}</p>
|
|
685
|
+
</Show>
|
|
686
|
+
<Show when={data().preview}>
|
|
687
|
+
{(preview) => (
|
|
688
|
+
<div class="mb-2 p-2 rounded bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 text-xs text-gray-600 dark:text-gray-400">
|
|
689
|
+
<Show when={preview().count != null}>
|
|
690
|
+
<span class="font-medium text-gray-800 dark:text-gray-200">{preview().count}</span> items
|
|
691
|
+
</Show>
|
|
692
|
+
<Show when={preview().summary}>
|
|
693
|
+
<span class="ml-1">— {preview().summary}</span>
|
|
694
|
+
</Show>
|
|
695
|
+
</div>
|
|
679
696
|
)}
|
|
680
|
-
</
|
|
697
|
+
</Show>
|
|
698
|
+
<Show when={data().validation && data().validation.confidence != null}>
|
|
699
|
+
<div class="mb-2 flex items-center gap-2 text-xs">
|
|
700
|
+
<span classList={{
|
|
701
|
+
'text-green-600 dark:text-green-400': data().validation.confidence >= 0.8,
|
|
702
|
+
'text-amber-600 dark:text-amber-400': data().validation.confidence >= 0.5 && data().validation.confidence < 0.8,
|
|
703
|
+
'text-red-600 dark:text-red-400': data().validation.confidence < 0.5,
|
|
704
|
+
}}>
|
|
705
|
+
{Math.round(data().validation.confidence * 100)}% verified
|
|
706
|
+
</span>
|
|
707
|
+
<Show when={data().validation.hallucinated?.length > 0}>
|
|
708
|
+
<span class="text-amber-600">({data().validation.hallucinated.length} unverified)</span>
|
|
709
|
+
</Show>
|
|
710
|
+
</div>
|
|
711
|
+
</Show>
|
|
712
|
+
|
|
713
|
+
{/* Action buttons */}
|
|
714
|
+
<div class="flex flex-wrap gap-2">
|
|
715
|
+
<For each={data().actions as Array<{ label: string; value?: string; action?: string; variant?: string; icon?: string }>}>
|
|
716
|
+
{(item) => (
|
|
717
|
+
<button type="button" on:click={() => props.onAction?.(item.value || item.action || item.label, item)}
|
|
718
|
+
class={`px-3 py-1.5 text-sm font-medium rounded-lg transition-colors ${
|
|
719
|
+
item.variant === 'primary' ? 'bg-blue-600 text-white hover:bg-blue-700'
|
|
720
|
+
: item.variant === 'danger' ? 'bg-red-600 text-white hover:bg-red-700'
|
|
721
|
+
: item.variant === 'secondary' ? 'border border-blue-300 dark:border-blue-600 text-blue-700 dark:text-blue-300 hover:bg-blue-50 dark:hover:bg-blue-900/20'
|
|
722
|
+
: 'border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'
|
|
723
|
+
}`}>
|
|
724
|
+
<Show when={item.icon}><span class="mr-1">{item.icon}</span></Show>
|
|
725
|
+
{item.label}
|
|
726
|
+
</button>
|
|
727
|
+
)}
|
|
728
|
+
</For>
|
|
729
|
+
</div>
|
|
681
730
|
</div>
|
|
682
731
|
)
|
|
683
732
|
}
|
|
@@ -737,18 +786,21 @@ const FeedbackSection: Component<{
|
|
|
737
786
|
}> = (props) => {
|
|
738
787
|
const [comment, setComment] = createSignal('')
|
|
739
788
|
const [showComment, setShowComment] = createSignal(false)
|
|
789
|
+
const [submitted, setSubmitted] = createSignal<string | null>(null)
|
|
740
790
|
const data = () => {
|
|
741
791
|
const c = props.content as any
|
|
742
|
-
// Support both formats: options array (universal) and approve/reject (simple)
|
|
743
792
|
const options = c?.options || [
|
|
744
|
-
{ value: c?.approve?.value || 'approve', label: c?.approve?.label || 'Yes', icon: '
|
|
745
|
-
{ value: c?.reject?.value || 'reject', label: c?.reject?.label || 'No', icon: '
|
|
793
|
+
{ value: c?.approve?.value || 'approve', label: c?.approve?.label || 'Yes', icon: '\uD83D\uDC4D', variant: 'primary' },
|
|
794
|
+
{ value: c?.reject?.value || 'reject', label: c?.reject?.label || 'No', icon: '\uD83D\uDC4E' },
|
|
746
795
|
]
|
|
747
796
|
return {
|
|
748
797
|
question: c?.question || '',
|
|
749
798
|
options: options as Array<{ value: string; label: string; icon?: string; variant?: string; needsComment?: boolean }>,
|
|
750
799
|
allowFreeText: c?.allowFreeText ?? c?.allowComment ?? false,
|
|
751
800
|
placeholder: c?.placeholder || c?.commentPlaceholder || 'Add a comment...',
|
|
801
|
+
// v4.1.0: per-step feedback
|
|
802
|
+
agentId: c?.agentId as string | undefined,
|
|
803
|
+
stepId: c?.stepId as string | undefined,
|
|
752
804
|
}
|
|
753
805
|
}
|
|
754
806
|
|
|
@@ -757,35 +809,65 @@ const FeedbackSection: Component<{
|
|
|
757
809
|
setShowComment(true)
|
|
758
810
|
return
|
|
759
811
|
}
|
|
760
|
-
|
|
812
|
+
setSubmitted(option.value)
|
|
813
|
+
const payload = {
|
|
814
|
+
option: option.value,
|
|
815
|
+
comment: comment(),
|
|
816
|
+
...(data().agentId ? { agentId: data().agentId } : {}),
|
|
817
|
+
...(data().stepId ? { stepId: data().stepId } : {}),
|
|
818
|
+
}
|
|
819
|
+
console.info('[MCP-UI:HITL] user responded', {
|
|
820
|
+
agentId: data().agentId, stepId: data().stepId, action: option.value,
|
|
821
|
+
})
|
|
822
|
+
props.onAction?.('feedback', payload)
|
|
761
823
|
}
|
|
762
824
|
|
|
763
825
|
return (
|
|
764
826
|
<div class="space-y-3">
|
|
765
827
|
<p class="text-sm text-gray-700 dark:text-gray-300">{data().question}</p>
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
<
|
|
787
|
-
|
|
828
|
+
|
|
829
|
+
{/* Already submitted — show micro-badge */}
|
|
830
|
+
<Show when={submitted()}>
|
|
831
|
+
<div class="flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400">
|
|
832
|
+
<span classList={{
|
|
833
|
+
'text-green-600': submitted() === 'approve',
|
|
834
|
+
'text-red-600': submitted() === 'reject',
|
|
835
|
+
'text-blue-600': submitted() !== 'approve' && submitted() !== 'reject',
|
|
836
|
+
}}>
|
|
837
|
+
{submitted() === 'approve' ? '\u2705' : submitted() === 'reject' ? '\u274C' : '\uD83D\uDCAC'} {submitted()}
|
|
838
|
+
</span>
|
|
839
|
+
<Show when={comment()}>
|
|
840
|
+
<span class="italic">— {comment()}</span>
|
|
841
|
+
</Show>
|
|
842
|
+
</div>
|
|
843
|
+
</Show>
|
|
844
|
+
|
|
845
|
+
{/* Buttons — hidden after submit */}
|
|
846
|
+
<Show when={!submitted()}>
|
|
847
|
+
<div class="flex flex-wrap gap-2">
|
|
848
|
+
<For each={data().options}>
|
|
849
|
+
{(option) => (
|
|
850
|
+
<button type="button" on:click={() => handleOption(option)}
|
|
851
|
+
class={`px-3 py-1.5 text-sm font-medium rounded-lg transition-colors flex items-center gap-1 ${
|
|
852
|
+
option.variant === 'primary' ? 'bg-blue-600 text-white hover:bg-blue-700'
|
|
853
|
+
: option.variant === 'danger' ? 'bg-red-600 text-white hover:bg-red-700'
|
|
854
|
+
: 'border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'
|
|
855
|
+
}`}>
|
|
856
|
+
<Show when={option.icon}><span>{option.icon}</span></Show>
|
|
857
|
+
{option.label}
|
|
858
|
+
</button>
|
|
859
|
+
)}
|
|
860
|
+
</For>
|
|
788
861
|
</div>
|
|
862
|
+
<Show when={data().allowFreeText || showComment()}>
|
|
863
|
+
<div class="flex gap-1">
|
|
864
|
+
<input type="text" value={comment()} onInput={(e) => setComment(e.currentTarget.value)}
|
|
865
|
+
placeholder={data().placeholder} autofocus={showComment()}
|
|
866
|
+
class="flex-1 px-3 py-2 text-sm border border-gray-200 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:border-blue-400 outline-none" />
|
|
867
|
+
<button type="button" on:click={() => { setSubmitted('comment'); props.onAction?.('feedback', { option: 'comment', comment: comment(), agentId: data().agentId, stepId: data().stepId }) }}
|
|
868
|
+
class="px-3 py-2 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700">Send</button>
|
|
869
|
+
</div>
|
|
870
|
+
</Show>
|
|
789
871
|
</Show>
|
|
790
872
|
</div>
|
|
791
873
|
)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SplitStepper — parallel agent steppers side by side
|
|
3
|
+
* v4.1.0: AITL sprint — 2-3 columns, synthesis row at bottom
|
|
4
|
+
*
|
|
5
|
+
* @experimental
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { For, Show } from 'solid-js'
|
|
9
|
+
import type { SplitStepperContent } from '../types/chat-bus'
|
|
10
|
+
|
|
11
|
+
export interface SplitStepperProps {
|
|
12
|
+
content: SplitStepperContent
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const STEP_ICONS: Record<string, string> = {
|
|
16
|
+
done: '\u2705',
|
|
17
|
+
active: '\uD83D\uDD04',
|
|
18
|
+
pending: '\u23F3',
|
|
19
|
+
skipped: '\u23ED\uFE0F',
|
|
20
|
+
error: '\u274C',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const AGENT_STATUS_COLORS: Record<string, string> = {
|
|
24
|
+
done: 'border-green-400 dark:border-green-600',
|
|
25
|
+
active: 'border-blue-400 dark:border-blue-500',
|
|
26
|
+
pending: 'border-gray-300 dark:border-gray-600',
|
|
27
|
+
error: 'border-red-400 dark:border-red-600',
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function SplitStepper(props: SplitStepperProps) {
|
|
31
|
+
const c = () => props.content
|
|
32
|
+
|
|
33
|
+
if (typeof console !== 'undefined') {
|
|
34
|
+
console.info('[MCP-UI:SplitStepper] mounted', {
|
|
35
|
+
agents: c().agents.map(a => `${a.id}:${a.status}`),
|
|
36
|
+
synthesis: c().synthesis?.status,
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div class="split-stepper">
|
|
42
|
+
{/* Agent columns */}
|
|
43
|
+
<div class="grid gap-3" style={{ "grid-template-columns": `repeat(${Math.min(c().agents.length, 3)}, 1fr)` }}>
|
|
44
|
+
<For each={c().agents}>
|
|
45
|
+
{(agent) => (
|
|
46
|
+
<div class={`rounded-lg border-2 ${AGENT_STATUS_COLORS[agent.status] || AGENT_STATUS_COLORS.pending} p-3`}>
|
|
47
|
+
{/* Agent header */}
|
|
48
|
+
<div class="flex items-center gap-2 mb-2">
|
|
49
|
+
<span class="font-medium text-sm text-gray-900 dark:text-white truncate">{agent.name}</span>
|
|
50
|
+
<span class={`w-2 h-2 rounded-full flex-shrink-0 ${
|
|
51
|
+
agent.status === 'done' ? 'bg-green-500' :
|
|
52
|
+
agent.status === 'active' ? 'bg-blue-500 animate-pulse' :
|
|
53
|
+
agent.status === 'error' ? 'bg-red-500' :
|
|
54
|
+
'bg-gray-400'
|
|
55
|
+
}`} />
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
{/* Steps */}
|
|
59
|
+
<div class="space-y-1">
|
|
60
|
+
<For each={agent.steps}>
|
|
61
|
+
{(step) => (
|
|
62
|
+
<div class="flex items-center gap-2 text-xs">
|
|
63
|
+
<span class="flex-shrink-0 w-4 text-center">{STEP_ICONS[step.status] || STEP_ICONS.pending}</span>
|
|
64
|
+
<span classList={{
|
|
65
|
+
'text-gray-900 dark:text-white font-medium': step.status === 'active',
|
|
66
|
+
'text-gray-500 dark:text-gray-400': step.status !== 'active',
|
|
67
|
+
'line-through opacity-50': step.status === 'skipped',
|
|
68
|
+
}}>
|
|
69
|
+
{step.label}
|
|
70
|
+
</span>
|
|
71
|
+
</div>
|
|
72
|
+
)}
|
|
73
|
+
</For>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
)}
|
|
77
|
+
</For>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
{/* Synthesis row */}
|
|
81
|
+
<Show when={c().synthesis}>
|
|
82
|
+
{(syn) => (
|
|
83
|
+
<div class={`mt-3 p-3 rounded-lg border-2 text-center ${
|
|
84
|
+
syn().status === 'done' ? 'border-green-400 dark:border-green-600 bg-green-50 dark:bg-green-900/10' :
|
|
85
|
+
syn().status === 'active' ? 'border-blue-400 dark:border-blue-500 bg-blue-50 dark:bg-blue-900/10' :
|
|
86
|
+
'border-dashed border-gray-300 dark:border-gray-600'
|
|
87
|
+
}`}>
|
|
88
|
+
<div class="flex items-center justify-center gap-2 text-sm">
|
|
89
|
+
<Show when={syn().status === 'active'}>
|
|
90
|
+
<div class="w-3 h-3 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
|
|
91
|
+
</Show>
|
|
92
|
+
<Show when={syn().status === 'done'}>
|
|
93
|
+
<span>{STEP_ICONS.done}</span>
|
|
94
|
+
</Show>
|
|
95
|
+
<Show when={syn().status === 'pending'}>
|
|
96
|
+
<span class="text-gray-400">{STEP_ICONS.pending}</span>
|
|
97
|
+
</Show>
|
|
98
|
+
<span classList={{
|
|
99
|
+
'font-medium text-blue-700 dark:text-blue-300': syn().status === 'active',
|
|
100
|
+
'font-medium text-green-700 dark:text-green-300': syn().status === 'done',
|
|
101
|
+
'text-gray-500 dark:text-gray-400': syn().status === 'pending',
|
|
102
|
+
}}>
|
|
103
|
+
{syn().label}
|
|
104
|
+
</span>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
)}
|
|
108
|
+
</Show>
|
|
109
|
+
</div>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
@@ -645,7 +645,7 @@ function TableRenderer(props: {
|
|
|
645
645
|
scope="col"
|
|
646
646
|
class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider border-b border-gray-200 dark:border-gray-700 first:pl-6 last:pr-6 bg-gray-50 dark:bg-gray-900/50 cursor-pointer select-none hover:bg-gray-100 dark:hover:bg-gray-800/50 transition-colors"
|
|
647
647
|
style={column.width ? { width: column.width } : {}}
|
|
648
|
-
|
|
648
|
+
on:click={() => handleSort(column.key)}
|
|
649
649
|
title={`Sort by ${column.label}`}
|
|
650
650
|
>
|
|
651
651
|
<span class="inline-flex items-center gap-1">
|
package/src/components/index.ts
CHANGED
|
@@ -64,6 +64,19 @@ export type { CodeBlockRendererProps } from './CodeBlockRenderer'
|
|
|
64
64
|
export { MapRenderer } from './MapRenderer'
|
|
65
65
|
export type { MapRendererProps } from './MapRenderer'
|
|
66
66
|
|
|
67
|
+
// Agent AITL (v4.1.0)
|
|
68
|
+
export { AgentCard, AgentStatusBadge } from './AgentCard'
|
|
69
|
+
export type { AgentCardProps, AgentStatusBadgeProps } from './AgentCard'
|
|
70
|
+
|
|
71
|
+
export { SplitStepper } from './SplitStepper'
|
|
72
|
+
export type { SplitStepperProps } from './SplitStepper'
|
|
73
|
+
|
|
74
|
+
export { AgentHandoff } from './AgentHandoff'
|
|
75
|
+
export type { AgentHandoffProps } from './AgentHandoff'
|
|
76
|
+
|
|
77
|
+
export { BriefingDiff } from './BriefingDiff'
|
|
78
|
+
export type { BriefingDiffProps } from './BriefingDiff'
|
|
79
|
+
|
|
67
80
|
// Data Verification (v3.1.0 — anti-hallucination)
|
|
68
81
|
export { VerifiedText } from './VerifiedText'
|
|
69
82
|
export type { VerifiedTextProps } from './VerifiedText'
|
package/src/index.ts
CHANGED
|
@@ -48,6 +48,12 @@ export { dispatchScratchpad, useScratchpadState } from './stores/scratchpad-stor
|
|
|
48
48
|
export { VerifiedText } from './components/VerifiedText'
|
|
49
49
|
export { DataPreviewSection } from './components/DataPreviewSection'
|
|
50
50
|
|
|
51
|
+
// Agent AITL Components (v4.1.0)
|
|
52
|
+
export { AgentCard, AgentStatusBadge } from './components/AgentCard'
|
|
53
|
+
export { SplitStepper } from './components/SplitStepper'
|
|
54
|
+
export { AgentHandoff } from './components/AgentHandoff'
|
|
55
|
+
export { BriefingDiff } from './components/BriefingDiff'
|
|
56
|
+
|
|
51
57
|
// Autocomplete Components
|
|
52
58
|
export { GhostText, GhostTextInput } from './components/GhostText'
|
|
53
59
|
export { AutocompleteDropdown } from './components/AutocompleteDropdown'
|
|
@@ -68,6 +74,10 @@ export type { ChatPromptProps } from './components/ChatPrompt'
|
|
|
68
74
|
export type { ScratchpadPanelProps } from './components/ScratchpadPanel'
|
|
69
75
|
export type { VerifiedTextProps } from './components/VerifiedText'
|
|
70
76
|
export type { DataPreviewSectionProps } from './components/DataPreviewSection'
|
|
77
|
+
export type { AgentCardProps, AgentStatusBadgeProps } from './components/AgentCard'
|
|
78
|
+
export type { SplitStepperProps } from './components/SplitStepper'
|
|
79
|
+
export type { AgentHandoffProps } from './components/AgentHandoff'
|
|
80
|
+
export type { BriefingDiffProps } from './components/BriefingDiff'
|
|
71
81
|
export type { GhostTextProps, GhostTextInputProps } from './components/GhostText'
|
|
72
82
|
export type { AutocompleteDropdownProps } from './components/AutocompleteDropdown'
|
|
73
83
|
export type { AutocompleteFormFieldProps, AutocompleteFormFieldParams } from './components/AutocompleteFormField'
|
|
@@ -271,4 +281,9 @@ export type {
|
|
|
271
281
|
DataPreviewColumn,
|
|
272
282
|
DataPreviewContent,
|
|
273
283
|
MapSectionContent,
|
|
284
|
+
// Agent AITL types (v4.1.0)
|
|
285
|
+
AgentCardContent,
|
|
286
|
+
SplitStepperContent,
|
|
287
|
+
AgentHandoffContent,
|
|
288
|
+
BriefingDiffContent,
|
|
274
289
|
} from './types/chat-bus'
|
package/src/types/chat-bus.ts
CHANGED
|
@@ -111,6 +111,10 @@ export interface ChatCommands {
|
|
|
111
111
|
/** Change the chat mode */
|
|
112
112
|
setMode: (mode: string) => void
|
|
113
113
|
|
|
114
|
+
// --- Agents (v4.1.0) ---
|
|
115
|
+
/** Trigger an agent from the chat (replaces /macro command) */
|
|
116
|
+
triggerAgent: (agentId: string, params?: Record<string, unknown>) => void
|
|
117
|
+
|
|
114
118
|
// --- UI ---
|
|
115
119
|
/** Scroll to a specific message */
|
|
116
120
|
scrollToMessage: (messageId: string) => void
|
|
@@ -366,7 +370,7 @@ export interface ScratchpadState {
|
|
|
366
370
|
export interface ScratchpadSection {
|
|
367
371
|
id: string
|
|
368
372
|
title: string
|
|
369
|
-
type: 'data' | 'filter' | 'preview' | 'message' | 'action' | 'steps' | 'form' | 'understanding' | 'feedback' | 'prompt' | 'stepper' | 'error' | 'source_card' | 'diff' | 'verified_text' | 'data_preview' | 'map' | 'chart'
|
|
373
|
+
type: 'data' | 'filter' | 'preview' | 'message' | 'action' | 'steps' | 'form' | 'understanding' | 'feedback' | 'prompt' | 'stepper' | 'error' | 'source_card' | 'diff' | 'verified_text' | 'data_preview' | 'map' | 'chart' | 'agent_card' | 'split_stepper' | 'agent_handoff' | 'briefing_diff'
|
|
370
374
|
content: unknown
|
|
371
375
|
/** Can the human edit this section? */
|
|
372
376
|
editable: boolean
|
|
@@ -541,3 +545,68 @@ export interface MapSectionContent {
|
|
|
541
545
|
/** Map height (CSS, default: '300px') */
|
|
542
546
|
height?: string
|
|
543
547
|
}
|
|
548
|
+
|
|
549
|
+
// ─── Agent section types (v4.1.0 — AITL sprint) ────────────
|
|
550
|
+
|
|
551
|
+
/** Content for agent_card scratchpad section */
|
|
552
|
+
export interface AgentCardContent {
|
|
553
|
+
agentId: string
|
|
554
|
+
name: string
|
|
555
|
+
/** Avatar icon key (e.g. 'scales', 'chart', 'search') or emoji */
|
|
556
|
+
avatar?: string
|
|
557
|
+
status: 'idle' | 'running' | 'waiting' | 'done' | 'error'
|
|
558
|
+
/** Agent capabilities as string badges */
|
|
559
|
+
capabilities?: string[]
|
|
560
|
+
/** LLM model used */
|
|
561
|
+
model?: string
|
|
562
|
+
/** Current step info (shown when running) */
|
|
563
|
+
currentStep?: { id: string; label: string }
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/** Content for split_stepper scratchpad section (parallel agents) */
|
|
567
|
+
export interface SplitStepperContent {
|
|
568
|
+
agents: Array<{
|
|
569
|
+
id: string
|
|
570
|
+
name: string
|
|
571
|
+
steps: Array<{ id: string; label: string; status: 'done' | 'active' | 'pending' | 'skipped' | 'error' }>
|
|
572
|
+
status: 'done' | 'active' | 'pending' | 'error'
|
|
573
|
+
}>
|
|
574
|
+
/** Final synthesis step (activates when all agents are done) */
|
|
575
|
+
synthesis?: {
|
|
576
|
+
status: 'done' | 'active' | 'pending'
|
|
577
|
+
label: string
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/** Content for agent_handoff scratchpad section */
|
|
582
|
+
export interface AgentHandoffContent {
|
|
583
|
+
from: { id: string; name: string; avatar?: string }
|
|
584
|
+
to: { id: string; name: string; avatar?: string }
|
|
585
|
+
/** Data keys transferred */
|
|
586
|
+
dataKeys?: string[]
|
|
587
|
+
/** Summary of what was transferred */
|
|
588
|
+
summary?: string
|
|
589
|
+
/** Count of items transferred */
|
|
590
|
+
itemCount?: number
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/** Content for briefing_diff scratchpad section */
|
|
594
|
+
export interface BriefingDiffContent {
|
|
595
|
+
/** Title of the comparison */
|
|
596
|
+
title?: string
|
|
597
|
+
/** When was the previous version */
|
|
598
|
+
previousDate?: string
|
|
599
|
+
/** When is the current version */
|
|
600
|
+
currentDate?: string
|
|
601
|
+
/** List of changes */
|
|
602
|
+
changes: Array<{
|
|
603
|
+
type: 'added' | 'removed' | 'changed'
|
|
604
|
+
label: string
|
|
605
|
+
/** Previous value (for 'changed' and 'removed') */
|
|
606
|
+
previous?: string
|
|
607
|
+
/** Current value (for 'changed' and 'added') */
|
|
608
|
+
current?: string
|
|
609
|
+
}>
|
|
610
|
+
/** Summary stats */
|
|
611
|
+
stats?: { added: number; removed: number; changed: number }
|
|
612
|
+
}
|