anentrypoint-design 0.0.383 → 0.0.385
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/community.css +21 -18
- package/dist/247420.css +117 -49
- package/dist/247420.js +22 -22
- package/package.json +1 -1
- package/scripts/lint-css.mjs +24 -10
- package/src/components/chat/message.js +130 -0
- package/src/components/chat/stats.js +36 -0
- package/src/components/chat/thread-scroll.js +42 -0
- package/src/components/freddie/pages-chat.js +149 -0
- package/src/components/freddie/pages-config.js +203 -0
- package/src/components/freddie/pages-infra.js +120 -0
- package/src/components/freddie/pages-overview.js +107 -0
- package/src/components/freddie/pages-runners.js +102 -0
- package/src/components/freddie/pages-telemetry.js +126 -0
- package/src/components/freddie/pages-workspace.js +176 -0
- package/src/components/freddie/shared.js +32 -0
- package/src/components/freddie/sse.js +85 -0
- package/src/components/freddie.js +21 -1064
- package/src/components/shell/app-shell.js +195 -0
- package/src/components/shell/atoms.js +165 -0
- package/src/components/shell/icons.js +123 -0
- package/src/components/shell/workspace-columns.js +179 -0
- package/src/components/shell/workspace-shell.js +181 -0
- package/src/components/shell.js +15 -805
- package/src/css/app-shell/chat-polish.css +18 -6
- package/src/css/app-shell/files.css +15 -4
- package/src/css/app-shell/hero-content.css +26 -1
- package/src/css/app-shell/kits-appended.css +26 -15
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Shared render helpers used by every freddie page module: the `section`
|
|
2
|
+
// Panel wrapper, the alert/refresh/live-region chrome, and the truncation
|
|
3
|
+
// vocabulary (named widths + the span/JSON renderers that carry the full
|
|
4
|
+
// text in a title tooltip).
|
|
5
|
+
|
|
6
|
+
import * as webjsx from '../../../vendor/webjsx/index.js';
|
|
7
|
+
import { Panel } from '../content.js';
|
|
8
|
+
import { Btn, Icon } from '../shell.js';
|
|
9
|
+
|
|
10
|
+
const h = webjsx.createElement;
|
|
11
|
+
|
|
12
|
+
export const section = (title, ...children) => Panel({ title, children: children.flat().filter(Boolean) });
|
|
13
|
+
export const noteAlert = (note) => note ? h('div', { class: 'ds-alert ds-alert-' + note.kind, role: 'alert' },
|
|
14
|
+
h('span', { class: 'ds-alert-icon' }, '!'),
|
|
15
|
+
h('div', { class: 'ds-alert-content' }, note.msg)) : null;
|
|
16
|
+
// Manual refresh button for non-polling pages — parity with auto-refreshing ones.
|
|
17
|
+
export const refreshBtn = (onClick, busy) => Btn({ children: busy ? 'refreshing…' : [Icon('refresh'), ' refresh'], disabled: !!busy, onClick, 'aria-label': 'refresh' });
|
|
18
|
+
// Polite live region announcing async busy/done state to screen readers.
|
|
19
|
+
export const liveRegion = (msg) => h('div', { class: 'fd-sr-live', role: 'status', 'aria-live': 'polite' }, msg || '');
|
|
20
|
+
// Truncate with a title tooltip carrying the full text.
|
|
21
|
+
export const trunc = (s, n = 90) => { const str = String(s || ''); return str.length > n ? { text: str.slice(0, n) + '…', title: str } : { text: str, title: null }; };
|
|
22
|
+
// Named truncation widths so list pages cap display consistently (and any
|
|
23
|
+
// raw .slice(0,N) on user text routes through trunc() for ellipsis + tooltip).
|
|
24
|
+
export const TRUNC_TITLE = 60; // session/skill/tool titles
|
|
25
|
+
export const TRUNC_SUB = 80; // row sub-text (prompts, descriptions)
|
|
26
|
+
export const TRUNC_OUTPUT = 70; // batch output cells
|
|
27
|
+
export const TRUNC_DESC = 90; // long descriptions
|
|
28
|
+
export const TRUNC_PROMPT = 50; // batch prompt cells
|
|
29
|
+
// Render trunc() result as a span carrying the full text in its title tooltip.
|
|
30
|
+
export const truncSpan = (s, n) => { const t = trunc(s, n); return h('span', { title: t.title }, t.text); };
|
|
31
|
+
// Cap a raw JSON dump for an inline table cell without losing the data via tooltip.
|
|
32
|
+
export const truncJson = (v, n = TRUNC_TITLE) => truncSpan(JSON.stringify(v), n);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// SSE transport for the freddie chat page: a generic POST-friendly
|
|
2
|
+
// Server-Sent-Events frame decoder plus the two payload->AgentChat-part
|
|
3
|
+
// mappers (streaming tool_progress, and the authoritative post-`done`
|
|
4
|
+
// rebuild from freddie's persisted message list).
|
|
5
|
+
|
|
6
|
+
// Parse a fetch Response body as a Server-Sent-Events frame stream. There is
|
|
7
|
+
// no EventSource-over-POST in browsers (EventSource only does GET, no custom
|
|
8
|
+
// headers/body), so a POST-based SSE consumer has to manually decode the
|
|
9
|
+
// ReadableStream and split on blank-line-terminated `event: X\ndata: Y\n\n`
|
|
10
|
+
// frames. No existing SSE-parsing utility exists elsewhere in this SDK
|
|
11
|
+
// (checked idb-outbox.js and grepped src/ for `text/event-stream`) -- this is
|
|
12
|
+
// the first, generic enough (event name + JSON.parse'd data) to reuse for any
|
|
13
|
+
// future SSE endpoint, not freddie-chat-specific in shape.
|
|
14
|
+
export async function* parseSseStream(response) {
|
|
15
|
+
const reader = response.body.getReader();
|
|
16
|
+
const decoder = new TextDecoder();
|
|
17
|
+
let buf = '';
|
|
18
|
+
try {
|
|
19
|
+
for (;;) {
|
|
20
|
+
const { done, value } = await reader.read();
|
|
21
|
+
if (done) break;
|
|
22
|
+
buf += decoder.decode(value, { stream: true });
|
|
23
|
+
// Frames are separated by a blank line; a frame may itself contain
|
|
24
|
+
// multiple `field: value` lines (event/data/id/retry) but this
|
|
25
|
+
// server only ever emits one `event:` + one `data:` line per frame.
|
|
26
|
+
let sep;
|
|
27
|
+
while ((sep = buf.indexOf('\n\n')) !== -1) {
|
|
28
|
+
const frame = buf.slice(0, sep);
|
|
29
|
+
buf = buf.slice(sep + 2);
|
|
30
|
+
let event = 'message', dataLines = [];
|
|
31
|
+
for (const line of frame.split('\n')) {
|
|
32
|
+
if (line.startsWith('event:')) event = line.slice(6).trim();
|
|
33
|
+
else if (line.startsWith('data:')) dataLines.push(line.slice(5).trim());
|
|
34
|
+
}
|
|
35
|
+
if (!dataLines.length) continue;
|
|
36
|
+
let data;
|
|
37
|
+
try { data = JSON.parse(dataLines.join('\n')); } catch { data = dataLines.join('\n'); }
|
|
38
|
+
yield { event, data };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
} finally {
|
|
42
|
+
try { reader.releaseLock(); } catch { /* swallow: stream already closed/errored */ }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Map a freddie tool_progress SSE payload ({name, args, partial}) to an
|
|
47
|
+
// AgentChat tool part. There is no matching id to correlate a later
|
|
48
|
+
// "done" state (freddie's stream has no discrete tool-start/tool-end pair --
|
|
49
|
+
// tool_progress fires zero-or-more times per call while it runs, and the
|
|
50
|
+
// authoritative role:'tool' result only arrives batched in the final
|
|
51
|
+
// `message`/`done` events) so every progress part renders as 'running'; the
|
|
52
|
+
// turn-settle pass below promotes matching parts to 'done'/'error' once the
|
|
53
|
+
// real tool_call_id/content pairs are known.
|
|
54
|
+
export function toolProgressPart(payload) {
|
|
55
|
+
return { kind: 'tool', name: payload.name || 'tool', args: payload.args || {}, status: 'running' };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// After `done`, freddie's persisted message list is the source of truth:
|
|
59
|
+
// walk it and rebuild the assistant turn's parts as interleaved
|
|
60
|
+
// text/tool/tool_result, replacing the provisional tool_progress-only parts
|
|
61
|
+
// accumulated during streaming. assistant messages with tool_calls become
|
|
62
|
+
// running tool parts (by call id); role:'tool' messages settle the matching
|
|
63
|
+
// part to done/error by tool_call_id.
|
|
64
|
+
export function partsFromMessages(assistantAndToolMessages) {
|
|
65
|
+
const parts = [];
|
|
66
|
+
const byId = new Map();
|
|
67
|
+
for (const m of assistantAndToolMessages) {
|
|
68
|
+
if (m.role === 'assistant') {
|
|
69
|
+
if (m.content) parts.push({ kind: 'md', text: m.content });
|
|
70
|
+
for (const tc of (m.tool_calls || [])) {
|
|
71
|
+
const part = { kind: 'tool', _id: tc.id, name: tc.name || tc.function?.name || 'tool', args: tc.arguments || tc.function?.arguments || {}, status: 'running' };
|
|
72
|
+
parts.push(part);
|
|
73
|
+
if (tc.id) byId.set(tc.id, part);
|
|
74
|
+
}
|
|
75
|
+
} else if (m.role === 'tool') {
|
|
76
|
+
const target = m.tool_call_id ? byId.get(m.tool_call_id) : null;
|
|
77
|
+
let content = m.content;
|
|
78
|
+
let isError = false;
|
|
79
|
+
try { const parsed = JSON.parse(content); if (parsed && parsed.error) { isError = true; } } catch { /* swallow: not JSON, leave as-is */ }
|
|
80
|
+
if (target) { target.result = content; target.status = isError ? 'error' : 'done'; target.error = isError || undefined; }
|
|
81
|
+
else parts.push({ kind: 'tool_result', name: 'result', result: content, error: isError || undefined, status: isError ? 'error' : 'done' });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return parts;
|
|
85
|
+
}
|