anentrypoint-design 0.0.87 → 0.0.89
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/app-shell.css +23 -5
- package/dist/247420.app.js +4 -4
- package/dist/247420.css +23 -5
- package/dist/247420.js +32 -32
- package/package.json +1 -1
- package/src/components/freddie/pages-config.js +34 -58
- package/src/components/freddie/pages-core.js +77 -25
- package/src/components/freddie/pages-runtime.js +69 -0
- package/src/components/freddie/pages-voice.js +5 -4
- package/src/components/freddie.js +4 -2
- package/src/desktop/launcher.css +17 -0
- package/src/desktop/launcher.js +0 -8
- package/src/styles.js +33 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anentrypoint-design",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.89",
|
|
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",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as webjsx from '../../../vendor/webjsx/index.js';
|
|
2
|
-
import { Panel,
|
|
2
|
+
import { Panel, Hero, Receipt, Kpi, Table, Form } from '../content.js';
|
|
3
3
|
import { Chip } from '../shell.js';
|
|
4
4
|
import { EmptyState } from '../files.js';
|
|
5
5
|
import { skillLabel } from './helpers.js';
|
|
@@ -24,7 +24,10 @@ export async function models(h0) {
|
|
|
24
24
|
const ms = Array.isArray(probeState[p.name]) ? probeState[p.name] : p.models;
|
|
25
25
|
const loading = probeState[p.name] === 'loading';
|
|
26
26
|
if (loading) probedPanels.push(Panel({ title: p.name + ' ⏳', children: h('span', { class: 'fd-muted' }, 'probing…') }));
|
|
27
|
-
else if (ms && ms.length > 0) probedPanels.push(Panel({ title: p.name + (p.available ? ' ●' : ' ○'), count: ms.length, children:
|
|
27
|
+
else if (ms && ms.length > 0) probedPanels.push(Panel({ title: p.name + (p.available ? ' ●' : ' ○'), count: ms.length, children: h('div', { class: 'fd-list fd-list-compact' }, ...ms.map((m, i) => h('div', { key: m, class: 'fd-list-row', 'data-cat': 'preview' },
|
|
28
|
+
h('span', { class: 'fd-list-code' }, String(i+1).padStart(2,'0')),
|
|
29
|
+
h('div', { class: 'fd-list-main' }, h('div', { class: 'fd-list-title fd-mono' }, m))
|
|
30
|
+
))) }));
|
|
28
31
|
else unprobedRows.push([p.name, p.available ? 'available' : 'unavailable', p.modelsError ? 'error: '+p.modelsError : '—']);
|
|
29
32
|
}
|
|
30
33
|
const modelPanels = [
|
|
@@ -64,7 +67,16 @@ export async function cron(h0) {
|
|
|
64
67
|
['weekdays 18:00', '0 18 * * 1-5'],
|
|
65
68
|
['every 15 min', '*/15 * * * *']
|
|
66
69
|
] }) }),
|
|
67
|
-
Panel({ title: 'jobs', count: list.length, children: list.length === 0
|
|
70
|
+
Panel({ title: 'jobs', count: list.length, children: list.length === 0
|
|
71
|
+
? EmptyState({ text: 'no cron jobs — add one with the form above', glyph: '◷' })
|
|
72
|
+
: h('div', { class: 'fd-list' }, ...list.map(j => h('div', { key: j.id, class: 'fd-list-row', 'data-cat': j.enabled ? 'kit' : 'external' },
|
|
73
|
+
h('span', { class: 'fd-list-code' }, j.enabled ? '●' : '○'),
|
|
74
|
+
h('div', { class: 'fd-list-main' },
|
|
75
|
+
h('div', { class: 'fd-list-title' }, (j.prompt||'(no prompt)').slice(0,80)),
|
|
76
|
+
h('div', { class: 'fd-list-sub' }, j.cron + ' · ' + j.id)
|
|
77
|
+
),
|
|
78
|
+
h('div', { class: 'fd-list-meta' }, h('span', { class: 'fd-list-meta-mono' }, j.enabled ? 'enabled' : 'disabled'))
|
|
79
|
+
))) })
|
|
68
80
|
];
|
|
69
81
|
}
|
|
70
82
|
|
|
@@ -75,7 +87,13 @@ export async function skills(h0) {
|
|
|
75
87
|
Hero({ title: 'skills', body: 'SKILL.md bundles loaded from ~/.freddie/skills + plugins.', accent: list.length+' loaded' }),
|
|
76
88
|
Kpi({ items: [[list.length,'skills'],[Object.keys(byCat).length,'categories']] }),
|
|
77
89
|
list.length === 0 ? EmptyState({ text: 'no skills — add SKILL.md files to ~/.freddie/skills/', glyph: '◈' }) : null,
|
|
78
|
-
...Object.entries(byCat).map(([cat, ss]) => Panel({ title: cat, count: ss.length, children:
|
|
90
|
+
...Object.entries(byCat).map(([cat, ss]) => Panel({ title: cat, count: ss.length, children: h('div', { class: 'fd-list' }, ...ss.map((s, i) => h('div', { key: s.name, class: 'fd-list-row', 'data-cat': cat === 'creative' ? 'preview' : cat === 'software-development' ? 'kit' : cat === 'planning' ? 'doc' : cat === 'ops' ? 'external' : 'doc' },
|
|
91
|
+
h('span', { class: 'fd-list-code' }, String(i+1).padStart(2,'0')),
|
|
92
|
+
h('div', { class: 'fd-list-main' },
|
|
93
|
+
h('div', { class: 'fd-list-title' }, skillLabel(s)),
|
|
94
|
+
s.description ? h('div', { class: 'fd-list-sub' }, (s.description||'').slice(0,140)) : null
|
|
95
|
+
)
|
|
96
|
+
))) }))
|
|
79
97
|
].filter(Boolean);
|
|
80
98
|
}
|
|
81
99
|
|
|
@@ -114,62 +132,20 @@ export async function tools(h0) {
|
|
|
114
132
|
Hero({ title: 'tools', body: 'every tool the agent can call. param count + required env per row.', accent: list.length+' tools' }),
|
|
115
133
|
Kpi({ items: [[list.length,'tools'],[filtered.length,'shown'],[Object.keys(bySet).length,'toolsets']] }),
|
|
116
134
|
Panel({ title: 'filter', right: search, children: filtered.length === 0 ? EmptyState({ text: 'no matches for "'+f.q+'"', glyph: '⌕' }) : h('span', { class: 'fd-muted' }, filtered.length+' / '+list.length+' tools shown') }),
|
|
117
|
-
...Object.entries(bySet).map(([ts, items]) => Panel({ title: 'toolset · '+ts, count: items.length, children: items.map(t => {
|
|
135
|
+
...Object.entries(bySet).map(([ts, items]) => Panel({ title: 'toolset · '+ts, count: items.length, children: h('div', { class: 'fd-list' }, ...items.map(t => {
|
|
118
136
|
const params = t.schema?.parameters?.properties ? Object.keys(t.schema.parameters.properties).length : 0;
|
|
119
137
|
const reqEnv = Array.isArray(t.requiresEnv) ? t.requiresEnv : [];
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
138
|
+
return h('div', { key: t.name, class: 'fd-list-row', 'data-cat': 'kit' },
|
|
139
|
+
h('span', { class: 'fd-list-code' }, '⚒'),
|
|
140
|
+
h('div', { class: 'fd-list-main' },
|
|
141
|
+
h('div', { class: 'fd-list-title fd-mono' }, t.name),
|
|
142
|
+
h('div', { class: 'fd-list-sub' }, (t.description || (t.schema && t.schema.description) || '').slice(0,120))
|
|
143
|
+
),
|
|
144
|
+
h('div', { class: 'fd-list-meta' },
|
|
145
|
+
params > 0 ? Chip({ tone: 'neutral', children: params+' params' }) : null,
|
|
146
|
+
...reqEnv.map(k => Chip({ tone: envIsSet(k) ? 'ok' : 'miss', children: k }))
|
|
147
|
+
));
|
|
148
|
+
})) }))
|
|
126
149
|
];
|
|
127
150
|
}
|
|
128
151
|
|
|
129
|
-
export async function batch(h0) {
|
|
130
|
-
const results = window.__fd_batchResults = window.__fd_batchResults || [];
|
|
131
|
-
const status = window.__fd_batchStatus = window.__fd_batchStatus || { running: false, error: null };
|
|
132
|
-
const onSubmit = async ev => {
|
|
133
|
-
const prompts = ev.target.elements.prompts.value.split('\n').map(s => s.trim()).filter(Boolean);
|
|
134
|
-
if (!prompts.length) return;
|
|
135
|
-
status.running = true; status.error = null; window.__fd_batchResults = [];
|
|
136
|
-
if (typeof window.__fd_nav === 'function') window.__fd_nav('batch');
|
|
137
|
-
try {
|
|
138
|
-
const r = await h0.pi.batch.run(prompts, Number(ev.target.elements.concurrency.value) || 4);
|
|
139
|
-
const arr = Array.isArray(r) ? r : (r && typeof r === 'object' ? Object.entries(r).map(([k, v]) => ({ prompt: k, result: v })) : []);
|
|
140
|
-
window.__fd_batchResults = arr;
|
|
141
|
-
} catch (e) { status.error = e.message || String(e); }
|
|
142
|
-
status.running = false;
|
|
143
|
-
if (typeof window.__fd_nav === 'function') window.__fd_nav('batch');
|
|
144
|
-
};
|
|
145
|
-
const resPanel = status.running ? Panel({ title: 'results', children: h('span', {}, 'running…') })
|
|
146
|
-
: status.error ? Panel({ title: 'results', children: h('span', { class: 'fd-muted' }, 'error: '+status.error) })
|
|
147
|
-
: results.length === 0 ? Panel({ title: 'results', children: EmptyState({ text: 'no results yet', glyph: '⊞' }) })
|
|
148
|
-
: Panel({ title: 'results', count: results.length, children: Table({ headers: ['#','prompt','result','status'], rows: results.map((it, i) => [String(i+1), (it.prompt||'').slice(0,60), (it.result||it.error||'').slice(0,120), it.error ? 'error' : 'ok']) }) });
|
|
149
|
-
return [
|
|
150
|
-
Hero({ title: 'batch', body: 'run many prompts in parallel. one per line.', accent: results.length ? results.length+' results' : 'idle' }),
|
|
151
|
-
Panel({ title: 'run batch', children: Form({ fields: [
|
|
152
|
-
{ name: 'prompts', kind: 'textarea', placeholder: 'one prompt per line', rows: 6 },
|
|
153
|
-
{ name: 'concurrency', type: 'number', value: '4' }
|
|
154
|
-
], submit: 'run', onSubmit }) }),
|
|
155
|
-
resPanel
|
|
156
|
-
];
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export async function gateway(h0) {
|
|
160
|
-
const platforms = typeof h0.pi.gateway?.platforms === 'function' ? h0.pi.gateway.platforms() : [];
|
|
161
|
-
const active = platforms.filter(p => p.enabled);
|
|
162
|
-
const envIsSet = k => typeof h0.pi.env?.isSet === 'function' ? h0.pi.env.isSet(k) : false;
|
|
163
|
-
return [
|
|
164
|
-
Hero({ title: 'gateway', body: 'webhook + bot platforms. each adapter declares required env keys.', accent: active.length+' / '+platforms.length+' active' }),
|
|
165
|
-
Kpi({ items: [[platforms.length,'platforms'],[active.length,'active']] }),
|
|
166
|
-
Panel({ title: 'platforms', count: platforms.length, right: active.length > 0 ? Chip({ tone: 'ok', children: active.length+' active' }) : Chip({ tone: 'miss', children: 'none active' }),
|
|
167
|
-
children: platforms.length === 0 ? EmptyState({ text: 'no platforms registered', glyph: '⇌' }) : platforms.map(p => {
|
|
168
|
-
const reqEnv = Array.isArray(p.requiresEnv) ? p.requiresEnv : [];
|
|
169
|
-
const setN = reqEnv.filter(envIsSet).length;
|
|
170
|
-
const envSummary = reqEnv.length === 0 ? '' : setN === reqEnv.length ? '✓ env ready' : 'missing '+(reqEnv.length-setN)+' / '+reqEnv.length;
|
|
171
|
-
return Row({ key: p.name, code: p.enabled ? '●' : '○', title: p.name, sub: p.note || '', meta: [p.enabled ? 'enabled' : '', envSummary].filter(Boolean).join(' · ') });
|
|
172
|
-
})
|
|
173
|
-
})
|
|
174
|
-
];
|
|
175
|
-
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as webjsx from '../../../vendor/webjsx/index.js';
|
|
2
|
-
import { Panel,
|
|
2
|
+
import { Panel, Hero, Receipt, Kpi } from '../content.js';
|
|
3
|
+
import { Chip } from '../shell.js';
|
|
3
4
|
import { EmptyState } from '../files.js';
|
|
4
5
|
import { skillLabel } from './helpers.js';
|
|
5
6
|
const h = webjsx.createElement;
|
|
@@ -25,40 +26,77 @@ export async function home(h0) {
|
|
|
25
26
|
];
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
function relTime(ts) {
|
|
30
|
+
if (!ts) return '';
|
|
31
|
+
const t = typeof ts === 'string' ? Date.parse(ts) : Number(ts);
|
|
32
|
+
if (!Number.isFinite(t)) return '';
|
|
33
|
+
const diff = (Date.now() - t) / 1000;
|
|
34
|
+
if (diff < 60) return Math.floor(diff)+'s ago';
|
|
35
|
+
if (diff < 3600) return Math.floor(diff/60)+'m ago';
|
|
36
|
+
if (diff < 86400) return Math.floor(diff/3600)+'h ago';
|
|
37
|
+
if (diff < 86400*30) return Math.floor(diff/86400)+'d ago';
|
|
38
|
+
return new Date(t).toISOString().slice(0,10);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const PLATFORM_CAT = { web: 'kit', cli: 'doc', telegram: 'preview', slack: 'preview', discord: 'preview', api_server: 'external', webhook: 'external' };
|
|
42
|
+
|
|
28
43
|
export async function sessions(h0) {
|
|
29
44
|
const list = await h0.pi.sessions.list();
|
|
30
45
|
const f = window.__fd_sessFilter = window.__fd_sessFilter || { q: '' };
|
|
31
46
|
const q = (f.q || '').toLowerCase();
|
|
32
47
|
const filtered = q ? list.filter(s => (s.title||'').toLowerCase().includes(q) || (s.id||'').includes(q) || (s.platform||'').toLowerCase().includes(q) || (s.cwd||'').toLowerCase().includes(q)) : list;
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
const openSession = async s => {
|
|
49
|
+
const msgs = await h0.pi.sessions.getMessages(s.id);
|
|
50
|
+
const cs = window.__fd_chatState = window.__fd_chatState || { messages: [], busy: false, sessionId: null, cwd: '', skill: '', provider: '', model: '' };
|
|
51
|
+
cs.sessionId = s.id;
|
|
52
|
+
cs.messages = msgs.map(m => ({ role: m.role, content: String(m.content || '') }));
|
|
53
|
+
if (s.cwd) cs.cwd = s.cwd;
|
|
54
|
+
if (s.skill) cs.skill = s.skill;
|
|
55
|
+
if (typeof window.__fd_nav === 'function') window.__fd_nav('chat');
|
|
56
|
+
};
|
|
57
|
+
const items = filtered.map(s => {
|
|
58
|
+
const cat = PLATFORM_CAT[s.platform] || 'doc';
|
|
59
|
+
const subParts = [s.platform || '—', s.model || '—', s.skill ? skillLabel({name:s.skill}) : null].filter(Boolean);
|
|
60
|
+
const cwdMeta = s.cwd ? '…'+s.cwd.slice(-28) : '';
|
|
61
|
+
const tsMeta = relTime(s.updated_at || s.created_at || s.ts);
|
|
62
|
+
return h('div', { key: s.id, class: 'fd-list-row', 'data-cat': cat, role: 'button', tabindex: '0', onclick: () => openSession(s), onkeydown: ev => { if (ev.key === 'Enter' || ev.key === ' ') { ev.preventDefault(); openSession(s); } } },
|
|
63
|
+
h('span', { class: 'fd-list-code' }, (s.id||'').slice(0,8)),
|
|
64
|
+
h('div', { class: 'fd-list-main' },
|
|
65
|
+
h('div', { class: 'fd-list-title' }, s.title || '(untitled)'),
|
|
66
|
+
h('div', { class: 'fd-list-sub' }, subParts.join(' · '))
|
|
67
|
+
),
|
|
68
|
+
h('div', { class: 'fd-list-meta' },
|
|
69
|
+
cwdMeta ? h('span', { class: 'fd-list-meta-mono' }, cwdMeta) : null,
|
|
70
|
+
tsMeta ? h('span', { class: 'fd-list-meta-rel' }, tsMeta) : null
|
|
71
|
+
)
|
|
72
|
+
);
|
|
44
73
|
});
|
|
45
|
-
const search = h('input', { type: 'search', placeholder: 'filter by title / id / platform / cwd…', value: f.q, oninput: ev => { f.q = ev.target.value; if (typeof window.__fd_nav === 'function') window.__fd_nav('sessions'); }, class: 'fd-search' });
|
|
74
|
+
const search = h('input', { type: 'search', 'aria-label': 'filter sessions', placeholder: 'filter by title / id / platform / cwd…', value: f.q, oninput: ev => { f.q = ev.target.value; if (typeof window.__fd_nav === 'function') window.__fd_nav('sessions'); }, class: 'fd-search' });
|
|
75
|
+
const body = list.length === 0
|
|
76
|
+
? EmptyState({ text: 'no sessions yet — start one in /chat', glyph: '✉' })
|
|
77
|
+
: filtered.length === 0
|
|
78
|
+
? EmptyState({ text: 'no matches for "'+f.q+'"', glyph: '⌕' })
|
|
79
|
+
: h('div', { class: 'fd-list' }, ...items);
|
|
46
80
|
return [
|
|
47
|
-
Hero({ title: 'sessions', body: 'every chat turn lives here.', accent: list.length+' total' }),
|
|
81
|
+
Hero({ title: 'sessions', body: 'every chat turn lives here. click a row to continue.', accent: list.length+' total' }),
|
|
48
82
|
Kpi({ items: [[list.length,'sessions'],[filtered.length,'shown']] }),
|
|
49
|
-
Panel({ title: 'sessions', count: filtered.length, right: search, children:
|
|
83
|
+
Panel({ title: 'sessions', count: filtered.length, right: search, children: body })
|
|
50
84
|
];
|
|
51
85
|
}
|
|
52
86
|
|
|
53
87
|
export async function projects(h0) {
|
|
54
88
|
const list = h0.pi.projects.list();
|
|
55
89
|
const active = h0.pi.projects.active();
|
|
56
|
-
const rows = list.map(p =>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
90
|
+
const rows = h('div', { class: 'fd-list' }, ...list.map(p => {
|
|
91
|
+
const isActive = p.name === active?.name;
|
|
92
|
+
return h('div', { key: p.name, class: 'fd-list-row', 'data-cat': isActive ? 'kit' : 'doc', role: 'button', tabindex: '0',
|
|
93
|
+
onclick: () => { if (!isActive) h0.pi.projects.setActive(p.name); },
|
|
94
|
+
onkeydown: ev => { if ((ev.key === 'Enter' || ev.key === ' ') && !isActive) { ev.preventDefault(); h0.pi.projects.setActive(p.name); } } },
|
|
95
|
+
h('span', { class: 'fd-list-code' }, isActive ? '●' : '○'),
|
|
96
|
+
h('div', { class: 'fd-list-main' },
|
|
97
|
+
h('div', { class: 'fd-list-title' }, p.name + (isActive ? ' (active)' : '')),
|
|
98
|
+
h('div', { class: 'fd-list-sub' }, p.path)
|
|
99
|
+
));
|
|
62
100
|
}));
|
|
63
101
|
return [
|
|
64
102
|
Hero({ title: 'projects', body: 'each project is its own ~/.freddie home.', accent: active ? 'active · '+active.name : 'no active project' }),
|
|
@@ -68,7 +106,7 @@ export async function projects(h0) {
|
|
|
68
106
|
h('input', { name: 'path', placeholder: '/abs/path' }),
|
|
69
107
|
h('button', { type: 'submit', class: 'btn-primary' }, 'add')
|
|
70
108
|
) }),
|
|
71
|
-
Panel({ title: 'all projects', count: list.length, children:
|
|
109
|
+
Panel({ title: 'all projects', count: list.length, children: list.length ? rows : EmptyState({ text: 'no projects', glyph: '◆' }) })
|
|
72
110
|
];
|
|
73
111
|
}
|
|
74
112
|
|
|
@@ -89,7 +127,16 @@ export async function agents(h0) {
|
|
|
89
127
|
] }) }),
|
|
90
128
|
Panel({ title: 'recent sessions', count: recent.length, children: recent.length === 0
|
|
91
129
|
? EmptyState({ text: 'no recent sessions', glyph: '✉' })
|
|
92
|
-
:
|
|
130
|
+
: h('div', { class: 'fd-list' }, ...recent.map(s => h('div', { key: s.id, class: 'fd-list-row', 'data-cat': PLATFORM_CAT[s.platform] || 'doc' },
|
|
131
|
+
h('span', { class: 'fd-list-code' }, (s.id||'').slice(0,8)),
|
|
132
|
+
h('div', { class: 'fd-list-main' },
|
|
133
|
+
h('div', { class: 'fd-list-title' }, s.title || '(untitled)'),
|
|
134
|
+
h('div', { class: 'fd-list-sub' }, [s.platform||'—', s.model||'—'].join(' · '))
|
|
135
|
+
),
|
|
136
|
+
h('div', { class: 'fd-list-meta' },
|
|
137
|
+
h('span', { class: 'fd-list-meta-mono' }, String(s.turns ?? s.message_count ?? 0)+' turns')
|
|
138
|
+
)
|
|
139
|
+
)))
|
|
93
140
|
})
|
|
94
141
|
];
|
|
95
142
|
}
|
|
@@ -102,10 +149,15 @@ export async function analytics(h0) {
|
|
|
102
149
|
const byModel = list.reduce((a,s) => { const k = s.model||'(unset)'; a[k] = (a[k]||0)+1; return a; }, {});
|
|
103
150
|
const byToolset = tools.reduce((a,t) => { const k = t.toolset||'core'; a[k] = (a[k]||0)+1; return a; }, {});
|
|
104
151
|
const bySkillCat = skills.reduce((a,s) => { const k = s.category||'other'; a[k] = (a[k]||0)+1; return a; }, {});
|
|
105
|
-
const sortDesc = obj => Object.entries(obj).sort((a,b) => b[1]-a[1])
|
|
152
|
+
const sortDesc = obj => Object.entries(obj).sort((a,b) => b[1]-a[1]);
|
|
153
|
+
const CAT_BY_TITLE = { 'by platform': 'kit', 'by model': 'preview', 'by toolset': 'doc', 'by skill category': 'external' };
|
|
106
154
|
const mkPanel = (title, obj, glyph) => Panel({ title, count: Object.keys(obj).length, children: Object.keys(obj).length === 0
|
|
107
155
|
? EmptyState({ text: 'no data', glyph })
|
|
108
|
-
:
|
|
156
|
+
: h('div', { class: 'fd-list fd-list-compact' }, ...sortDesc(obj).map(([k, n], i) => h('div', { key: k, class: 'fd-list-row', 'data-cat': CAT_BY_TITLE[title] || 'doc' },
|
|
157
|
+
h('span', { class: 'fd-list-code' }, String(i+1).padStart(2,'0')),
|
|
158
|
+
h('div', { class: 'fd-list-main' }, h('div', { class: 'fd-list-title' }, k)),
|
|
159
|
+
h('div', { class: 'fd-list-meta' }, h('span', { class: 'fd-list-meta-mono' }, String(n)))
|
|
160
|
+
)))
|
|
109
161
|
});
|
|
110
162
|
return [
|
|
111
163
|
Hero({ title: 'analytics', body: 'shape of work — what platforms, what models, what tools.' }),
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as webjsx from '../../../vendor/webjsx/index.js';
|
|
2
|
+
import { Panel, Hero, Kpi, Form } from '../content.js';
|
|
3
|
+
import { Chip } from '../shell.js';
|
|
4
|
+
import { EmptyState } from '../files.js';
|
|
5
|
+
const h = webjsx.createElement;
|
|
6
|
+
|
|
7
|
+
export async function batch(h0) {
|
|
8
|
+
const results = window.__fd_batchResults = window.__fd_batchResults || [];
|
|
9
|
+
const status = window.__fd_batchStatus = window.__fd_batchStatus || { running: false, error: null };
|
|
10
|
+
const onSubmit = async ev => {
|
|
11
|
+
const prompts = ev.target.elements.prompts.value.split('\n').map(s => s.trim()).filter(Boolean);
|
|
12
|
+
if (!prompts.length) return;
|
|
13
|
+
status.running = true; status.error = null; window.__fd_batchResults = [];
|
|
14
|
+
if (typeof window.__fd_nav === 'function') window.__fd_nav('batch');
|
|
15
|
+
try {
|
|
16
|
+
const r = await h0.pi.batch.run(prompts, Number(ev.target.elements.concurrency.value) || 4);
|
|
17
|
+
const arr = Array.isArray(r) ? r : (r && typeof r === 'object' ? Object.entries(r).map(([k, v]) => ({ prompt: k, result: v })) : []);
|
|
18
|
+
window.__fd_batchResults = arr;
|
|
19
|
+
} catch (e) { status.error = e.message || String(e); }
|
|
20
|
+
status.running = false;
|
|
21
|
+
if (typeof window.__fd_nav === 'function') window.__fd_nav('batch');
|
|
22
|
+
};
|
|
23
|
+
const resPanel = status.running ? Panel({ title: 'results', children: h('span', {}, 'running…') })
|
|
24
|
+
: status.error ? Panel({ title: 'results', children: h('span', { class: 'fd-muted' }, 'error: '+status.error) })
|
|
25
|
+
: results.length === 0 ? Panel({ title: 'results', children: EmptyState({ text: 'no results yet', glyph: '⊞' }) })
|
|
26
|
+
: Panel({ title: 'results', count: results.length, children: h('div', { class: 'fd-list' }, ...results.map((it, i) => h('div', { key: i, class: 'fd-list-row', 'data-cat': it.error ? 'external' : 'kit' },
|
|
27
|
+
h('span', { class: 'fd-list-code' }, String(i+1).padStart(2,'0')),
|
|
28
|
+
h('div', { class: 'fd-list-main' },
|
|
29
|
+
h('div', { class: 'fd-list-title' }, (it.prompt||'').slice(0,80)),
|
|
30
|
+
h('div', { class: 'fd-list-sub' }, (it.result||it.error||'').slice(0,160))
|
|
31
|
+
),
|
|
32
|
+
h('div', { class: 'fd-list-meta' }, h('span', { class: 'fd-list-meta-mono' }, it.error ? 'error' : 'ok'))
|
|
33
|
+
))) });
|
|
34
|
+
return [
|
|
35
|
+
Hero({ title: 'batch', body: 'run many prompts in parallel. one per line.', accent: results.length ? results.length+' results' : 'idle' }),
|
|
36
|
+
Panel({ title: 'run batch', children: Form({ fields: [
|
|
37
|
+
{ name: 'prompts', kind: 'textarea', placeholder: 'one prompt per line', rows: 6 },
|
|
38
|
+
{ name: 'concurrency', type: 'number', value: '4' }
|
|
39
|
+
], submit: 'run', onSubmit }) }),
|
|
40
|
+
resPanel
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function gateway(h0) {
|
|
45
|
+
const platforms = typeof h0.pi.gateway?.platforms === 'function' ? h0.pi.gateway.platforms() : [];
|
|
46
|
+
const active = platforms.filter(p => p.enabled);
|
|
47
|
+
const envIsSet = k => typeof h0.pi.env?.isSet === 'function' ? h0.pi.env.isSet(k) : false;
|
|
48
|
+
return [
|
|
49
|
+
Hero({ title: 'gateway', body: 'webhook + bot platforms. each adapter declares required env keys.', accent: active.length+' / '+platforms.length+' active' }),
|
|
50
|
+
Kpi({ items: [[platforms.length,'platforms'],[active.length,'active']] }),
|
|
51
|
+
Panel({ title: 'platforms', count: platforms.length, right: active.length > 0 ? Chip({ tone: 'ok', children: active.length+' active' }) : Chip({ tone: 'miss', children: 'none active' }),
|
|
52
|
+
children: platforms.length === 0 ? EmptyState({ text: 'no platforms registered', glyph: '⇌' }) : h('div', { class: 'fd-list' }, ...platforms.map(p => {
|
|
53
|
+
const reqEnv = Array.isArray(p.requiresEnv) ? p.requiresEnv : [];
|
|
54
|
+
const setN = reqEnv.filter(envIsSet).length;
|
|
55
|
+
const envSummary = reqEnv.length === 0 ? '' : setN === reqEnv.length ? '✓ env ready' : 'missing '+(reqEnv.length-setN)+' / '+reqEnv.length;
|
|
56
|
+
return h('div', { key: p.name, class: 'fd-list-row', 'data-cat': p.enabled ? 'kit' : 'external' },
|
|
57
|
+
h('span', { class: 'fd-list-code' }, p.enabled ? '●' : '○'),
|
|
58
|
+
h('div', { class: 'fd-list-main' },
|
|
59
|
+
h('div', { class: 'fd-list-title' }, p.name),
|
|
60
|
+
h('div', { class: 'fd-list-sub' }, p.note || '—')
|
|
61
|
+
),
|
|
62
|
+
h('div', { class: 'fd-list-meta' },
|
|
63
|
+
h('span', { class: 'fd-list-meta-mono' }, p.enabled ? 'enabled' : 'disabled'),
|
|
64
|
+
envSummary ? h('span', { class: 'fd-list-meta-rel' }, envSummary) : null
|
|
65
|
+
));
|
|
66
|
+
}))
|
|
67
|
+
})
|
|
68
|
+
];
|
|
69
|
+
}
|
|
@@ -76,10 +76,11 @@ export async function voice(h0) {
|
|
|
76
76
|
),
|
|
77
77
|
lines.length === 0 && !s.partial
|
|
78
78
|
? EmptyState({ text: s.listening ? 'listening — speak into your mic' : 'press start to capture speech', glyph: '◌' })
|
|
79
|
-
: h('div', { class: 'fd-
|
|
80
|
-
...lines.map((it, i) => h('div', { key: i, class: 'fd-
|
|
81
|
-
h('span', { class: 'fd-
|
|
82
|
-
h('
|
|
79
|
+
: h('div', { class: 'fd-list', role: 'log', 'aria-live': 'polite' },
|
|
80
|
+
...lines.map((it, i) => h('div', { key: i, class: 'fd-list-row', 'data-cat': 'doc' },
|
|
81
|
+
h('span', { class: 'fd-list-code' }, new Date(it.ts).toLocaleTimeString().slice(0,8)),
|
|
82
|
+
h('div', { class: 'fd-list-main' }, h('div', { class: 'fd-list-title' }, it.text))
|
|
83
|
+
)))
|
|
83
84
|
] });
|
|
84
85
|
|
|
85
86
|
const ttsPanel = !ttsSupported
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
export { skillLabel, getRecentPaths, saveRecentPath, renderChatMessages } from './freddie/helpers.js';
|
|
2
2
|
export { home, sessions, projects, agents, analytics } from './freddie/pages-core.js';
|
|
3
3
|
export { chat } from './freddie/pages-chat.js';
|
|
4
|
-
export { models, cron, skills, env, tools
|
|
4
|
+
export { models, cron, skills, env, tools } from './freddie/pages-config.js';
|
|
5
|
+
export { batch, gateway } from './freddie/pages-runtime.js';
|
|
5
6
|
export { config } from './freddie/pages-config-edit.js';
|
|
6
7
|
export { voice } from './freddie/pages-voice.js';
|
|
7
8
|
|
|
8
9
|
import { home, sessions, projects, agents, analytics } from './freddie/pages-core.js';
|
|
9
10
|
import { chat } from './freddie/pages-chat.js';
|
|
10
|
-
import { models, cron, skills, env, tools
|
|
11
|
+
import { models, cron, skills, env, tools } from './freddie/pages-config.js';
|
|
12
|
+
import { batch, gateway } from './freddie/pages-runtime.js';
|
|
11
13
|
import { config } from './freddie/pages-config-edit.js';
|
|
12
14
|
import { voice } from './freddie/pages-voice.js';
|
|
13
15
|
export const FREDDIE_PAGES = { home, chat, voice, sessions, projects, agents, analytics, models, cron, skills, config, env, tools, batch, gateway };
|
package/src/desktop/launcher.css
CHANGED
|
@@ -41,4 +41,21 @@
|
|
|
41
41
|
}
|
|
42
42
|
.launcher-add { font-size: 20px; }
|
|
43
43
|
|
|
44
|
+
.launcher-instances {
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-direction: column;
|
|
47
|
+
align-items: center;
|
|
48
|
+
gap: 8px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.launcher-row {
|
|
52
|
+
display: flex;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
align-items: center;
|
|
55
|
+
gap: 2px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.launcher-close { font-size: 11px; opacity: 0.7; width: 20px; height: 20px; }
|
|
59
|
+
.launcher-close:hover { opacity: 1; }
|
|
60
|
+
|
|
44
61
|
.launcher-dock + .wm-root { left: 56px !important; }
|
package/src/desktop/launcher.js
CHANGED
|
@@ -19,10 +19,6 @@ export function renderDock(opts = {}) {
|
|
|
19
19
|
|
|
20
20
|
const instancesHost = document.createElement('div');
|
|
21
21
|
instancesHost.className = 'launcher-instances';
|
|
22
|
-
instancesHost.style.display = 'flex';
|
|
23
|
-
instancesHost.style.flexDirection = 'column';
|
|
24
|
-
instancesHost.style.gap = '8px';
|
|
25
|
-
instancesHost.style.alignItems = 'center';
|
|
26
22
|
el.appendChild(instancesHost);
|
|
27
23
|
|
|
28
24
|
root.appendChild(el);
|
|
@@ -41,10 +37,6 @@ export function renderDock(opts = {}) {
|
|
|
41
37
|
const row = document.createElement('div');
|
|
42
38
|
row.className = 'launcher-row';
|
|
43
39
|
row.dataset.instanceId = inst.id;
|
|
44
|
-
row.style.display = 'flex';
|
|
45
|
-
row.style.flexDirection = 'column';
|
|
46
|
-
row.style.gap = '2px';
|
|
47
|
-
row.style.alignItems = 'center';
|
|
48
40
|
|
|
49
41
|
const selBtn = document.createElement('button');
|
|
50
42
|
selBtn.className = 'launcher-btn';
|