anentrypoint-design 0.0.186 → 0.0.190
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/chat.css +62 -0
- package/dist/247420.css +62 -0
- package/dist/247420.js +11 -11
- package/package.json +1 -1
- package/src/components/agent-chat.js +45 -2
- package/src/components/content.js +2 -1
- package/src/components/shell.js +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anentrypoint-design",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.190",
|
|
4
4
|
"description": "247420 design system SDK — webjsx + modified ripple-ui, single-file ESM bundle for reproducible use of the AnEntrypoint design.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/247420.js",
|
|
@@ -92,10 +92,17 @@ export function AgentChat(props = {}) {
|
|
|
92
92
|
onSelectAgent, onSelectModel, onSend, onStop, onNewChat, onInput,
|
|
93
93
|
onCwdEdit, onCwdSave, onCwdCancel, onCwdClear, onCwdDraft,
|
|
94
94
|
canSend = true,
|
|
95
|
+
suggestions = [], onSuggestionClick,
|
|
95
96
|
} = props;
|
|
96
97
|
|
|
97
98
|
const name = agentName || (agents.find((a) => a.id === selectedAgent)?.name) || selectedAgent || 'agent';
|
|
98
99
|
const lastIdx = messages.length - 1;
|
|
100
|
+
const lastMsg = messages[lastIdx];
|
|
101
|
+
// True when streaming but the live assistant turn already shows content/parts,
|
|
102
|
+
// so its inline typing dots have stopped — a long silent tool call would
|
|
103
|
+
// otherwise read as frozen. We append a standalone "working" indicator below.
|
|
104
|
+
const showWorkingTail = busy && lastMsg && lastMsg.role === 'assistant'
|
|
105
|
+
&& (lastMsg.content || (Array.isArray(lastMsg.parts) && lastMsg.parts.length));
|
|
99
106
|
const rows = messages.map((m, i) => {
|
|
100
107
|
const isAssistant = m.role === 'assistant';
|
|
101
108
|
const isStreaming = busy && i === lastIdx && isAssistant;
|
|
@@ -106,7 +113,15 @@ export function AgentChat(props = {}) {
|
|
|
106
113
|
if (!isStreaming && isAssistant && !m.content && !hasParts) return null;
|
|
107
114
|
const parts = [];
|
|
108
115
|
if (m.content) parts.push({ kind: isAssistant ? 'md' : 'text', text: m.content });
|
|
109
|
-
|
|
116
|
+
// A message's parts may be bare strings (legacy text lines) OR structured
|
|
117
|
+
// part objects ({ kind:'tool'|'tool_result'|'code'|..., ... }). Pass the
|
|
118
|
+
// structured objects straight through to ChatMessage's renderPart so the
|
|
119
|
+
// host can surface real tool cards / code blocks; wrap only bare strings as
|
|
120
|
+
// text. This is what lets an orchestration host (agentgui) render the kit's
|
|
121
|
+
// collapsible ToolCallNode instead of flattening tools into plain text.
|
|
122
|
+
if (hasParts) for (const p of m.parts) {
|
|
123
|
+
parts.push((p && typeof p === 'object' && p.kind) ? p : { kind: 'text', text: String(p) });
|
|
124
|
+
}
|
|
110
125
|
return ChatMessage({
|
|
111
126
|
key: m.id || String(i),
|
|
112
127
|
who: isAssistant ? 'them' : 'you',
|
|
@@ -118,14 +133,36 @@ export function AgentChat(props = {}) {
|
|
|
118
133
|
});
|
|
119
134
|
});
|
|
120
135
|
|
|
136
|
+
// While streaming, the composer's send button becomes an inline stop button
|
|
137
|
+
// (busy + onCancel) so the user can halt the turn from where their hands
|
|
138
|
+
// already are, not only from the controls cluster up top.
|
|
121
139
|
const composer = ChatComposer({
|
|
122
140
|
value: draft,
|
|
123
141
|
disabled: !canSend,
|
|
142
|
+
busy,
|
|
124
143
|
placeholder: placeholder || (selectedAgent ? 'message…' : 'choose an agent first'),
|
|
125
144
|
onInput: (v) => onInput && onInput(v),
|
|
126
145
|
onSend: (v) => onSend && onSend(v),
|
|
146
|
+
onCancel: busy && onStop ? () => onStop() : undefined,
|
|
127
147
|
});
|
|
128
148
|
|
|
149
|
+
// Empty state: a fresh thread is a void without this. Mirrors the kit's Chat
|
|
150
|
+
// empty surface (title, sub, optional starter prompts) so AgentChat opens to
|
|
151
|
+
// an invitation, not a blank panel.
|
|
152
|
+
const emptyState = (messages.length === 0)
|
|
153
|
+
? h('div', { class: 'agentchat-empty', role: 'status' },
|
|
154
|
+
h('p', { class: 'agentchat-empty-title' }, selectedAgent ? 'Start a conversation with ' + name : 'Choose an agent to begin'),
|
|
155
|
+
h('p', { class: 'agentchat-empty-sub' },
|
|
156
|
+
selectedAgent ? 'Type a message below. The agent can read files, run tools, and search.' : 'Pick an agent from the selector above, then send a message.'),
|
|
157
|
+
(suggestions && suggestions.length)
|
|
158
|
+
? h('div', { class: 'agentchat-empty-suggestions' },
|
|
159
|
+
...suggestions.map((s, i) => h('button', {
|
|
160
|
+
key: 'sug' + i, type: 'button', class: 'agentchat-empty-suggestion',
|
|
161
|
+
onclick: () => { const t = typeof s === 'string' ? s : (s.prompt || s.text || ''); if (onSuggestionClick) onSuggestionClick(t); },
|
|
162
|
+
}, typeof s === 'string' ? s : (s.label || s.text || s.prompt))))
|
|
163
|
+
: null)
|
|
164
|
+
: null;
|
|
165
|
+
|
|
129
166
|
return h('div', { class: 'agentchat' },
|
|
130
167
|
AgentControls({ agents, selectedAgent, models, selectedModel, busy, status, modelsLoading,
|
|
131
168
|
onSelectAgent, onSelectModel, onNewChat, onStop }),
|
|
@@ -137,7 +174,13 @@ export function AgentChat(props = {}) {
|
|
|
137
174
|
h('span', { class: 'agentchat-sub', 'aria-live': 'polite' },
|
|
138
175
|
busy ? 'streaming…' : (messages.length ? messages.length + (messages.length === 1 ? ' message' : ' messages') : ''))),
|
|
139
176
|
h('div', { class: 'agentchat-thread', ref: threadRef(messages.length), role: 'log', 'aria-label': 'conversation' },
|
|
140
|
-
|
|
177
|
+
emptyState,
|
|
178
|
+
...rows.filter(Boolean),
|
|
179
|
+
showWorkingTail
|
|
180
|
+
? h('div', { key: '_working', class: 'agentchat-working', role: 'status', 'aria-live': 'polite' },
|
|
181
|
+
h('span', { class: 'chat-thinking-dots', 'aria-hidden': 'true' }, h('span'), h('span'), h('span')),
|
|
182
|
+
h('span', { class: 'agentchat-working-text' }, 'working…'))
|
|
183
|
+
: null),
|
|
141
184
|
composer,
|
|
142
185
|
);
|
|
143
186
|
}
|
package/src/components/shell.js
CHANGED
|
@@ -249,7 +249,9 @@ export function AppShell({ topbar, crumb, side, main, status, narrow } = {}) {
|
|
|
249
249
|
h('div', { class: 'app-body' + (hasSide ? '' : ' no-side') },
|
|
250
250
|
h('div', { class: 'app-side-scrim', 'aria-hidden': 'true', onclick: () => toggleSide(false) }),
|
|
251
251
|
h('div', { class: 'app-side-shell', onclick: (e) => { if (e.target.closest('a')) toggleSide(false); } }, sideNode),
|
|
252
|
-
|
|
252
|
+
// tabindex=-1 so the skip-link (href="#app-main") actually moves
|
|
253
|
+
// keyboard focus into the main region, not just scroll to it.
|
|
254
|
+
h('main', { class: 'app-main' + (narrow ? ' narrow' : ''), id: 'app-main', tabindex: '-1' }, ...(Array.isArray(main) ? main : [main]))
|
|
253
255
|
),
|
|
254
256
|
status || null
|
|
255
257
|
);
|