anentrypoint-design 0.0.121 → 0.0.124
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/README.md +253 -253
- package/app-shell.css +931 -594
- package/colors_and_type.css +226 -226
- package/community.css +817 -1222
- package/dist/247420.css +2202 -2084
- package/dist/247420.js +13 -13
- package/package.json +80 -80
- package/src/bootstrap.js +25 -25
- package/src/components/chat.js +199 -199
- package/src/components/community.js +190 -208
- package/src/components/content.js +269 -269
- package/src/components/editor-primitives.js +100 -0
- package/src/components/files-modals.js +107 -107
- package/src/components/files.js +118 -118
- package/src/components/freddie/helpers.js +50 -50
- package/src/components/freddie.js +33 -33
- package/src/components/shell.js +117 -117
- package/src/components/theme-toggle.js +70 -70
- package/src/components.js +59 -57
- package/src/debug.js +30 -30
- package/src/deck-stage.js +21 -21
- package/src/highlight.js +65 -32
- package/src/index.js +86 -86
- package/src/kits/os/about-app.js +52 -52
- package/src/kits/os/app-panes.css +152 -152
- package/src/kits/os/browser-app.js +58 -58
- package/src/kits/os/files-app.js +44 -44
- package/src/kits/os/freddie/helpers.js +59 -59
- package/src/kits/os/freddie/pages-chat.js +143 -143
- package/src/kits/os/freddie/pages-core.js +101 -101
- package/src/kits/os/freddie/pages-os.js +51 -51
- package/src/kits/os/freddie/pages-tools.js +183 -183
- package/src/kits/os/freddie/routes.js +24 -24
- package/src/kits/os/freddie-dashboard.css +51 -51
- package/src/kits/os/freddie-dashboard.js +101 -101
- package/src/kits/os/icons.js +17 -17
- package/src/kits/os/index.js +17 -17
- package/src/kits/os/launcher.css +61 -61
- package/src/kits/os/launcher.js +79 -79
- package/src/kits/os/monitor-app.js +34 -34
- package/src/kits/os/shell.js +214 -214
- package/src/kits/os/terminal-app.js +45 -45
- package/src/kits/os/theme.css +450 -450
- package/src/kits/os/validate.css +19 -19
- package/src/kits/os/validator-app.js +55 -55
- package/src/kits/os/wm.css +115 -115
- package/src/kits/os/wm.js +111 -111
- package/src/markdown.js +39 -39
- package/src/motion.js +35 -35
- package/src/page-html.js +196 -196
- package/src/styles.js +25 -25
- package/src/theme.js +99 -99
- package/src/web-components/ds-chat.js +116 -116
- package/dist/.nojekyll +0 -0
- package/dist/app-shell.css +0 -594
- package/dist/colors_and_type.css +0 -197
- package/dist/favicon.svg +0 -1
- package/dist/index.html +0 -308
- package/dist/preview/buttons.html +0 -28
- package/dist/preview/colors-core.html +0 -45
- package/dist/preview/colors-lore.html +0 -28
- package/dist/preview/colors-semantic.html +0 -34
- package/dist/preview/dateline.html +0 -19
- package/dist/preview/dropzone.html +0 -30
- package/dist/preview/file-grid.html +0 -19
- package/dist/preview/file-row.html +0 -20
- package/dist/preview/file-toolbar.html +0 -40
- package/dist/preview/file-viewer.html +0 -31
- package/dist/preview/header.html +0 -24
- package/dist/preview/icons-unicode.html +0 -26
- package/dist/preview/index-row.html +0 -25
- package/dist/preview/inputs.html +0 -22
- package/dist/preview/manifesto.html +0 -52
- package/dist/preview/motion-default.js +0 -106
- package/dist/preview/rules.html +0 -16
- package/dist/preview/spacing.html +0 -18
- package/dist/preview/stamps-lore.html +0 -20
- package/dist/preview/stamps.html +0 -14
- package/dist/preview/theme-ink.html +0 -15
- package/dist/preview/type-display.html +0 -16
- package/dist/preview/type-mono.html +0 -15
- package/dist/preview/type-prose.html +0 -11
- package/dist/preview/type-scale.html +0 -20
- package/dist/preview/wordmarks.html +0 -28
- package/dist/robots.txt +0 -8
- package/dist/site/content/globals/navigation.yaml +0 -5
- package/dist/site/content/globals/site.yaml +0 -16
- package/dist/site/content/pages/freddie.yaml +0 -88
- package/dist/site/content/pages/home.yaml +0 -190
- package/dist/site/theme.mjs +0 -368
- package/dist/sitemap.xml +0 -31
- package/dist/slides/deck-stage-overlay.js +0 -63
- package/dist/slides/deck-stage-state.js +0 -81
- package/dist/slides/deck-stage-style.js +0 -117
- package/dist/slides/deck-stage.js +0 -159
- package/dist/slides/index.html +0 -276
- package/dist/src/bootstrap.js +0 -25
- package/dist/src/components/chat.js +0 -199
- package/dist/src/components/community.js +0 -167
- package/dist/src/components/content.js +0 -213
- package/dist/src/components/files-modals.js +0 -107
- package/dist/src/components/files.js +0 -118
- package/dist/src/components/freddie/helpers.js +0 -50
- package/dist/src/components/freddie.js +0 -33
- package/dist/src/components/shell.js +0 -117
- package/dist/src/components/theme-toggle.js +0 -70
- package/dist/src/components.js +0 -52
- package/dist/src/debug.js +0 -30
- package/dist/src/deck-stage.js +0 -21
- package/dist/src/highlight.js +0 -32
- package/dist/src/index.js +0 -86
- package/dist/src/kits/os/about-app.js +0 -52
- package/dist/src/kits/os/app-panes.css +0 -152
- package/dist/src/kits/os/browser-app.js +0 -58
- package/dist/src/kits/os/files-app.js +0 -44
- package/dist/src/kits/os/freddie/helpers.js +0 -59
- package/dist/src/kits/os/freddie/pages-chat.js +0 -143
- package/dist/src/kits/os/freddie/pages-core.js +0 -101
- package/dist/src/kits/os/freddie/pages-os.js +0 -51
- package/dist/src/kits/os/freddie/pages-tools.js +0 -183
- package/dist/src/kits/os/freddie/routes.js +0 -24
- package/dist/src/kits/os/freddie-dashboard.css +0 -51
- package/dist/src/kits/os/freddie-dashboard.js +0 -101
- package/dist/src/kits/os/icons.js +0 -17
- package/dist/src/kits/os/index.js +0 -5
- package/dist/src/kits/os/launcher.css +0 -61
- package/dist/src/kits/os/launcher.js +0 -79
- package/dist/src/kits/os/monitor-app.js +0 -34
- package/dist/src/kits/os/shell.js +0 -214
- package/dist/src/kits/os/terminal-app.js +0 -45
- package/dist/src/kits/os/theme.css +0 -412
- package/dist/src/kits/os/validate.css +0 -19
- package/dist/src/kits/os/validator-app.js +0 -55
- package/dist/src/kits/os/wm.css +0 -115
- package/dist/src/kits/os/wm.js +0 -111
- package/dist/src/markdown.js +0 -39
- package/dist/src/motion.js +0 -35
- package/dist/src/page-html.js +0 -196
- package/dist/src/styles.js +0 -25
- package/dist/src/theme.js +0 -99
- package/dist/src/web-components/ds-chat.js +0 -45
- package/dist/ui_kits/aicat/README.md +0 -7
- package/dist/ui_kits/aicat/app.js +0 -156
- package/dist/ui_kits/aicat/index.html +0 -26
- package/dist/ui_kits/aicat/sample-square.png +0 -0
- package/dist/ui_kits/aicat/sample-svg.svg +0 -1
- package/dist/ui_kits/aicat/sample.pdf +0 -32
- package/dist/ui_kits/blog/README.md +0 -3
- package/dist/ui_kits/blog/index.html +0 -90
- package/dist/ui_kits/chat/README.md +0 -5
- package/dist/ui_kits/chat/app.js +0 -110
- package/dist/ui_kits/chat/index.html +0 -26
- package/dist/ui_kits/chat/sample-square.png +0 -0
- package/dist/ui_kits/chat/sample-svg.svg +0 -1
- package/dist/ui_kits/chat/sample.pdf +0 -32
- package/dist/ui_kits/community/app.js +0 -134
- package/dist/ui_kits/community/index.html +0 -24
- package/dist/ui_kits/dashboard/app.js +0 -92
- package/dist/ui_kits/dashboard/index.html +0 -26
- package/dist/ui_kits/docs/README.md +0 -3
- package/dist/ui_kits/docs/index.html +0 -123
- package/dist/ui_kits/error_404/app.js +0 -56
- package/dist/ui_kits/error_404/index.html +0 -26
- package/dist/ui_kits/file_browser/README.md +0 -48
- package/dist/ui_kits/file_browser/app.js +0 -231
- package/dist/ui_kits/file_browser/index.html +0 -33
- package/dist/ui_kits/gallery/app.js +0 -121
- package/dist/ui_kits/gallery/index.html +0 -26
- package/dist/ui_kits/homepage/README.md +0 -7
- package/dist/ui_kits/homepage/app.js +0 -167
- package/dist/ui_kits/homepage/index.html +0 -46
- package/dist/ui_kits/project_page/README.md +0 -3
- package/dist/ui_kits/project_page/app.js +0 -154
- package/dist/ui_kits/project_page/index.html +0 -45
- package/dist/ui_kits/search/app.js +0 -107
- package/dist/ui_kits/search/index.html +0 -26
- package/dist/ui_kits/settings/app.js +0 -133
- package/dist/ui_kits/settings/index.html +0 -26
- package/dist/ui_kits/signin/app.js +0 -115
- package/dist/ui_kits/signin/index.html +0 -26
- package/dist/ui_kits/slide_deck/app.js +0 -174
- package/dist/ui_kits/slide_deck/index.html +0 -26
- package/dist/ui_kits/system_primer/app.js +0 -152
- package/dist/ui_kits/system_primer/index.html +0 -26
- package/dist/ui_kits/terminal/app.js +0 -150
- package/dist/ui_kits/terminal/index.html +0 -26
- package/dist/vendor/webjsx/applyDiff.js +0 -182
- package/dist/vendor/webjsx/attributes.js +0 -154
- package/dist/vendor/webjsx/constants.js +0 -4
- package/dist/vendor/webjsx/createDOMElement.js +0 -52
- package/dist/vendor/webjsx/createElement.js +0 -75
- package/dist/vendor/webjsx/elementTags.js +0 -115
- package/dist/vendor/webjsx/factory.js +0 -6
- package/dist/vendor/webjsx/index.js +0 -6
- package/dist/vendor/webjsx/jsx-dev-runtime.js +0 -2
- package/dist/vendor/webjsx/jsx-runtime.js +0 -30
- package/dist/vendor/webjsx/jsx.js +0 -2
- package/dist/vendor/webjsx/package.json +0 -39
- package/dist/vendor/webjsx/renderSuspension.js +0 -25
- package/dist/vendor/webjsx/types.js +0 -5
- package/dist/vendor/webjsx/utils.js +0 -84
- package/src/components/overlays.js +0 -151
|
@@ -1,143 +1,143 @@
|
|
|
1
|
-
// Chat page — its own module because of SSE plumbing weight.
|
|
2
|
-
import * as webjsx from '../../../../vendor/webjsx/index.js';
|
|
3
|
-
import * as components from '../../../components.js';
|
|
4
|
-
import { getRecentPaths, saveRecentPath, skillLabel, renderChatMessages } from './helpers.js';
|
|
5
|
-
|
|
6
|
-
const h = webjsx.createElement;
|
|
7
|
-
const { Panel, Receipt, Chip } = components;
|
|
8
|
-
|
|
9
|
-
function parseSseEvents(text) {
|
|
10
|
-
const events = [];
|
|
11
|
-
let curEvent = null, curData = '';
|
|
12
|
-
for (const line of text.split('\n')) {
|
|
13
|
-
if (line.startsWith('event: ')) { curEvent = line.slice(7).trim(); }
|
|
14
|
-
else if (line.startsWith('data: ')) { curData = line.slice(6).trim(); }
|
|
15
|
-
else if (line === '' && curEvent) {
|
|
16
|
-
try { events.push({ event: curEvent, data: JSON.parse(curData) }); } catch {}
|
|
17
|
-
curEvent = null; curData = '';
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return events;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function makeChatPage(ctx) {
|
|
24
|
-
return async function chat(h0) {
|
|
25
|
-
const root = ctx.root;
|
|
26
|
-
const skills = [...h0.pi.skills.values()];
|
|
27
|
-
const providers = await fetch('/api/providers').then(r => r.json()).catch(() => []);
|
|
28
|
-
const configuredProviders = providers.filter(p => p.configured);
|
|
29
|
-
|
|
30
|
-
const chatState = window.__fd_chatState = window.__fd_chatState || {
|
|
31
|
-
cwd: '', skill: '', provider: '', model: '', messages: [], busy: false, sessionId: null,
|
|
32
|
-
};
|
|
33
|
-
if (!chatState.cwd) chatState.cwd = (getRecentPaths()[0] || '');
|
|
34
|
-
|
|
35
|
-
const getMsgsContainer = () => root.querySelector('#fd-chat-msgs');
|
|
36
|
-
|
|
37
|
-
const newSession = () => {
|
|
38
|
-
if (chatState.busy) return;
|
|
39
|
-
chatState.messages = [];
|
|
40
|
-
chatState.sessionId = null;
|
|
41
|
-
renderChatMessages(getMsgsContainer(), chatState.messages);
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const sendChat = async (ev) => {
|
|
45
|
-
ev.preventDefault();
|
|
46
|
-
if (chatState.busy) return;
|
|
47
|
-
const promptEl = ev.target.elements.prompt;
|
|
48
|
-
const prompt = promptEl.value.trim();
|
|
49
|
-
if (!prompt) return;
|
|
50
|
-
chatState.messages.push({ role: 'user', content: prompt });
|
|
51
|
-
promptEl.value = '';
|
|
52
|
-
promptEl.style.height = 'auto';
|
|
53
|
-
chatState.busy = true;
|
|
54
|
-
saveRecentPath(chatState.cwd);
|
|
55
|
-
renderChatMessages(getMsgsContainer(), chatState.messages);
|
|
56
|
-
try {
|
|
57
|
-
const body = { prompt, cwd: chatState.cwd || undefined, skill: chatState.skill || undefined, provider: chatState.provider || undefined, model: chatState.model || undefined, sessionId: chatState.sessionId || undefined };
|
|
58
|
-
const resp = await fetch('/api/chat', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) });
|
|
59
|
-
const text = await resp.text();
|
|
60
|
-
const events = parseSseEvents(text);
|
|
61
|
-
let assistantContent = '';
|
|
62
|
-
for (const { event, data } of events) {
|
|
63
|
-
if (event === 'start' && data.sessionId) chatState.sessionId = data.sessionId;
|
|
64
|
-
if (event === 'done' && data.sessionId) chatState.sessionId = data.sessionId;
|
|
65
|
-
if (event === 'message') {
|
|
66
|
-
const role = data.role;
|
|
67
|
-
if (role === 'assistant') {
|
|
68
|
-
const content = Array.isArray(data.content) ? data.content : [{ type: 'text', text: String(data.content || '') }];
|
|
69
|
-
for (const block of content) {
|
|
70
|
-
if (block.type === 'text') assistantContent += block.text;
|
|
71
|
-
if (block.type === 'tool_use') {
|
|
72
|
-
if (assistantContent) { chatState.messages.push({ role: 'assistant', content: assistantContent }); assistantContent = ''; }
|
|
73
|
-
const argsSummary = JSON.stringify(block.input || {}).slice(0, 60);
|
|
74
|
-
chatState.messages.push({ role: 'tool', name: block.name, argsSummary, content: JSON.stringify(block.input || {}, null, 2) });
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
} else if (role === 'tool') {
|
|
78
|
-
const tc = Array.isArray(data.content) ? data.content[0] : data;
|
|
79
|
-
chatState.messages.push({ role: 'tool', name: 'result', argsSummary: '', content: String(tc?.content || tc?.text || JSON.stringify(tc)) });
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
if (event === 'done' && data.result) { if (!assistantContent) assistantContent = data.result; }
|
|
83
|
-
if (event === 'error') assistantContent = 'error: ' + (data.error || 'unknown');
|
|
84
|
-
}
|
|
85
|
-
if (assistantContent) chatState.messages.push({ role: 'assistant', content: assistantContent });
|
|
86
|
-
if (!events.length) chatState.messages.push({ role: 'assistant', content: '(no response)' });
|
|
87
|
-
} catch (e) {
|
|
88
|
-
chatState.messages.push({ role: 'assistant', content: 'error: ' + e.message });
|
|
89
|
-
}
|
|
90
|
-
chatState.busy = false;
|
|
91
|
-
renderChatMessages(getMsgsContainer(), chatState.messages);
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const recentPaths = getRecentPaths();
|
|
95
|
-
const datalistId = 'fd-cwd-list';
|
|
96
|
-
const byCat = skills.reduce((a, s) => { const c = s.category || 'other'; (a[c] = a[c] || []).push(s); return a; }, {});
|
|
97
|
-
|
|
98
|
-
setTimeout(() => renderChatMessages(getMsgsContainer(), chatState.messages), 50);
|
|
99
|
-
|
|
100
|
-
const selSkill = h('select', { name: 'skill', onchange: (ev) => { chatState.skill = ev.target.value; } },
|
|
101
|
-
h('option', { value: '' }, '— no skill —'),
|
|
102
|
-
...Object.entries(byCat).map(([cat, ss]) =>
|
|
103
|
-
h('optgroup', { label: cat },
|
|
104
|
-
...ss.map(s => h('option', { value: s.name, selected: chatState.skill === s.name ? 'true' : null, title: s.description || s.name }, skillLabel(s)))
|
|
105
|
-
)));
|
|
106
|
-
|
|
107
|
-
const selProv = h('select', { name: 'provider', onchange: (ev) => { chatState.provider = ev.target.value; } },
|
|
108
|
-
h('option', { value: '' }, configuredProviders.length ? '— auto —' : '— no providers configured —'),
|
|
109
|
-
...configuredProviders.map(p => h('option', { value: p.name, selected: chatState.provider === p.name ? 'true' : null }, (p.available ? '● ' : '○ ') + p.name)));
|
|
110
|
-
|
|
111
|
-
return [
|
|
112
|
-
Panel({
|
|
113
|
-
title: 'chat',
|
|
114
|
-
right: h('button', { class: 'btn-primary fd-btn-mini', onclick: (ev) => { ev.preventDefault(); newSession(); }, disabled: chatState.busy ? 'true' : null }, '+ new session'),
|
|
115
|
-
children: [
|
|
116
|
-
h('datalist', { id: datalistId }, ...recentPaths.map(p => h('option', { value: p }))),
|
|
117
|
-
h('form', { class: 'row-form fd-chat-form', onsubmit: sendChat },
|
|
118
|
-
h('div', { class: 'fd-chat-field' },
|
|
119
|
-
h('label', {}, 'working directory'),
|
|
120
|
-
h('input', { name: 'cwd', type: 'text', placeholder: 'e.g. C:/dev/myproject or /home/user/project', value: chatState.cwd, list: datalistId, oninput: (ev) => { chatState.cwd = ev.target.value; } })),
|
|
121
|
-
h('div', { class: 'fd-chat-row' },
|
|
122
|
-
h('div', { class: 'fd-chat-field fd-chat-field-grow' }, h('label', {}, 'skill'), selSkill),
|
|
123
|
-
h('div', { class: 'fd-chat-field fd-chat-field-grow' }, h('label', {}, 'provider'), selProv),
|
|
124
|
-
h('div', { class: 'fd-chat-field fd-chat-field-grow' }, h('label', {}, 'model (optional)'),
|
|
125
|
-
h('input', { name: 'model', type: 'text', placeholder: configuredProviders.find(p => p.name === chatState.provider)?.defaultModel || 'default', value: chatState.model, oninput: (ev) => { chatState.model = ev.target.value; } }))),
|
|
126
|
-
h('div', { class: 'fd-chat-submit' },
|
|
127
|
-
h('textarea', { name: 'prompt', placeholder: 'describe what you want to do in the working directory…', rows: 4,
|
|
128
|
-
oninput: (ev) => { ev.target.style.height = 'auto'; ev.target.style.height = Math.min(ev.target.scrollHeight, 240) + 'px'; } }),
|
|
129
|
-
h('button', { type: 'submit', class: 'btn-primary', disabled: chatState.busy ? 'true' : null }, chatState.busy ? '…' : 'send'))),
|
|
130
|
-
h('div', { id: 'fd-chat-msgs', class: 'fd-chatlog' }),
|
|
131
|
-
],
|
|
132
|
-
}),
|
|
133
|
-
configuredProviders.length === 0
|
|
134
|
-
? Panel({ title: 'no providers configured', children: Receipt({ rows: [
|
|
135
|
-
['set API key', 'go to keys tab, click a provider chip to set its key'],
|
|
136
|
-
['then reload', 'refresh this page to see providers here'],
|
|
137
|
-
['or use acptoapi', 'run acptoapi server on localhost:4800 for local LLMs'],
|
|
138
|
-
] }) })
|
|
139
|
-
: Panel({ title: 'configured providers', children: h('div', { class: 'fd-chip-wrap' },
|
|
140
|
-
...providers.map(p => Chip({ tone: p.configured ? (p.available ? 'ok' : 'warn') : 'miss', children: p.name + (p.configured ? (p.available ? ' ●' : ' ○') : '') }))) }),
|
|
141
|
-
];
|
|
142
|
-
};
|
|
143
|
-
}
|
|
1
|
+
// Chat page — its own module because of SSE plumbing weight.
|
|
2
|
+
import * as webjsx from '../../../../vendor/webjsx/index.js';
|
|
3
|
+
import * as components from '../../../components.js';
|
|
4
|
+
import { getRecentPaths, saveRecentPath, skillLabel, renderChatMessages } from './helpers.js';
|
|
5
|
+
|
|
6
|
+
const h = webjsx.createElement;
|
|
7
|
+
const { Panel, Receipt, Chip } = components;
|
|
8
|
+
|
|
9
|
+
function parseSseEvents(text) {
|
|
10
|
+
const events = [];
|
|
11
|
+
let curEvent = null, curData = '';
|
|
12
|
+
for (const line of text.split('\n')) {
|
|
13
|
+
if (line.startsWith('event: ')) { curEvent = line.slice(7).trim(); }
|
|
14
|
+
else if (line.startsWith('data: ')) { curData = line.slice(6).trim(); }
|
|
15
|
+
else if (line === '' && curEvent) {
|
|
16
|
+
try { events.push({ event: curEvent, data: JSON.parse(curData) }); } catch {}
|
|
17
|
+
curEvent = null; curData = '';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return events;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function makeChatPage(ctx) {
|
|
24
|
+
return async function chat(h0) {
|
|
25
|
+
const root = ctx.root;
|
|
26
|
+
const skills = [...h0.pi.skills.values()];
|
|
27
|
+
const providers = await fetch('/api/providers').then(r => r.json()).catch(() => []);
|
|
28
|
+
const configuredProviders = providers.filter(p => p.configured);
|
|
29
|
+
|
|
30
|
+
const chatState = window.__fd_chatState = window.__fd_chatState || {
|
|
31
|
+
cwd: '', skill: '', provider: '', model: '', messages: [], busy: false, sessionId: null,
|
|
32
|
+
};
|
|
33
|
+
if (!chatState.cwd) chatState.cwd = (getRecentPaths()[0] || '');
|
|
34
|
+
|
|
35
|
+
const getMsgsContainer = () => root.querySelector('#fd-chat-msgs');
|
|
36
|
+
|
|
37
|
+
const newSession = () => {
|
|
38
|
+
if (chatState.busy) return;
|
|
39
|
+
chatState.messages = [];
|
|
40
|
+
chatState.sessionId = null;
|
|
41
|
+
renderChatMessages(getMsgsContainer(), chatState.messages);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const sendChat = async (ev) => {
|
|
45
|
+
ev.preventDefault();
|
|
46
|
+
if (chatState.busy) return;
|
|
47
|
+
const promptEl = ev.target.elements.prompt;
|
|
48
|
+
const prompt = promptEl.value.trim();
|
|
49
|
+
if (!prompt) return;
|
|
50
|
+
chatState.messages.push({ role: 'user', content: prompt });
|
|
51
|
+
promptEl.value = '';
|
|
52
|
+
promptEl.style.height = 'auto';
|
|
53
|
+
chatState.busy = true;
|
|
54
|
+
saveRecentPath(chatState.cwd);
|
|
55
|
+
renderChatMessages(getMsgsContainer(), chatState.messages);
|
|
56
|
+
try {
|
|
57
|
+
const body = { prompt, cwd: chatState.cwd || undefined, skill: chatState.skill || undefined, provider: chatState.provider || undefined, model: chatState.model || undefined, sessionId: chatState.sessionId || undefined };
|
|
58
|
+
const resp = await fetch('/api/chat', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) });
|
|
59
|
+
const text = await resp.text();
|
|
60
|
+
const events = parseSseEvents(text);
|
|
61
|
+
let assistantContent = '';
|
|
62
|
+
for (const { event, data } of events) {
|
|
63
|
+
if (event === 'start' && data.sessionId) chatState.sessionId = data.sessionId;
|
|
64
|
+
if (event === 'done' && data.sessionId) chatState.sessionId = data.sessionId;
|
|
65
|
+
if (event === 'message') {
|
|
66
|
+
const role = data.role;
|
|
67
|
+
if (role === 'assistant') {
|
|
68
|
+
const content = Array.isArray(data.content) ? data.content : [{ type: 'text', text: String(data.content || '') }];
|
|
69
|
+
for (const block of content) {
|
|
70
|
+
if (block.type === 'text') assistantContent += block.text;
|
|
71
|
+
if (block.type === 'tool_use') {
|
|
72
|
+
if (assistantContent) { chatState.messages.push({ role: 'assistant', content: assistantContent }); assistantContent = ''; }
|
|
73
|
+
const argsSummary = JSON.stringify(block.input || {}).slice(0, 60);
|
|
74
|
+
chatState.messages.push({ role: 'tool', name: block.name, argsSummary, content: JSON.stringify(block.input || {}, null, 2) });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
} else if (role === 'tool') {
|
|
78
|
+
const tc = Array.isArray(data.content) ? data.content[0] : data;
|
|
79
|
+
chatState.messages.push({ role: 'tool', name: 'result', argsSummary: '', content: String(tc?.content || tc?.text || JSON.stringify(tc)) });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (event === 'done' && data.result) { if (!assistantContent) assistantContent = data.result; }
|
|
83
|
+
if (event === 'error') assistantContent = 'error: ' + (data.error || 'unknown');
|
|
84
|
+
}
|
|
85
|
+
if (assistantContent) chatState.messages.push({ role: 'assistant', content: assistantContent });
|
|
86
|
+
if (!events.length) chatState.messages.push({ role: 'assistant', content: '(no response)' });
|
|
87
|
+
} catch (e) {
|
|
88
|
+
chatState.messages.push({ role: 'assistant', content: 'error: ' + e.message });
|
|
89
|
+
}
|
|
90
|
+
chatState.busy = false;
|
|
91
|
+
renderChatMessages(getMsgsContainer(), chatState.messages);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const recentPaths = getRecentPaths();
|
|
95
|
+
const datalistId = 'fd-cwd-list';
|
|
96
|
+
const byCat = skills.reduce((a, s) => { const c = s.category || 'other'; (a[c] = a[c] || []).push(s); return a; }, {});
|
|
97
|
+
|
|
98
|
+
setTimeout(() => renderChatMessages(getMsgsContainer(), chatState.messages), 50);
|
|
99
|
+
|
|
100
|
+
const selSkill = h('select', { name: 'skill', onchange: (ev) => { chatState.skill = ev.target.value; } },
|
|
101
|
+
h('option', { value: '' }, '— no skill —'),
|
|
102
|
+
...Object.entries(byCat).map(([cat, ss]) =>
|
|
103
|
+
h('optgroup', { label: cat },
|
|
104
|
+
...ss.map(s => h('option', { value: s.name, selected: chatState.skill === s.name ? 'true' : null, title: s.description || s.name }, skillLabel(s)))
|
|
105
|
+
)));
|
|
106
|
+
|
|
107
|
+
const selProv = h('select', { name: 'provider', onchange: (ev) => { chatState.provider = ev.target.value; } },
|
|
108
|
+
h('option', { value: '' }, configuredProviders.length ? '— auto —' : '— no providers configured —'),
|
|
109
|
+
...configuredProviders.map(p => h('option', { value: p.name, selected: chatState.provider === p.name ? 'true' : null }, (p.available ? '● ' : '○ ') + p.name)));
|
|
110
|
+
|
|
111
|
+
return [
|
|
112
|
+
Panel({
|
|
113
|
+
title: 'chat',
|
|
114
|
+
right: h('button', { class: 'btn-primary fd-btn-mini', onclick: (ev) => { ev.preventDefault(); newSession(); }, disabled: chatState.busy ? 'true' : null }, '+ new session'),
|
|
115
|
+
children: [
|
|
116
|
+
h('datalist', { id: datalistId }, ...recentPaths.map(p => h('option', { value: p }))),
|
|
117
|
+
h('form', { class: 'row-form fd-chat-form', onsubmit: sendChat },
|
|
118
|
+
h('div', { class: 'fd-chat-field' },
|
|
119
|
+
h('label', {}, 'working directory'),
|
|
120
|
+
h('input', { name: 'cwd', type: 'text', placeholder: 'e.g. C:/dev/myproject or /home/user/project', value: chatState.cwd, list: datalistId, oninput: (ev) => { chatState.cwd = ev.target.value; } })),
|
|
121
|
+
h('div', { class: 'fd-chat-row' },
|
|
122
|
+
h('div', { class: 'fd-chat-field fd-chat-field-grow' }, h('label', {}, 'skill'), selSkill),
|
|
123
|
+
h('div', { class: 'fd-chat-field fd-chat-field-grow' }, h('label', {}, 'provider'), selProv),
|
|
124
|
+
h('div', { class: 'fd-chat-field fd-chat-field-grow' }, h('label', {}, 'model (optional)'),
|
|
125
|
+
h('input', { name: 'model', type: 'text', placeholder: configuredProviders.find(p => p.name === chatState.provider)?.defaultModel || 'default', value: chatState.model, oninput: (ev) => { chatState.model = ev.target.value; } }))),
|
|
126
|
+
h('div', { class: 'fd-chat-submit' },
|
|
127
|
+
h('textarea', { name: 'prompt', placeholder: 'describe what you want to do in the working directory…', rows: 4,
|
|
128
|
+
oninput: (ev) => { ev.target.style.height = 'auto'; ev.target.style.height = Math.min(ev.target.scrollHeight, 240) + 'px'; } }),
|
|
129
|
+
h('button', { type: 'submit', class: 'btn-primary', disabled: chatState.busy ? 'true' : null }, chatState.busy ? '…' : 'send'))),
|
|
130
|
+
h('div', { id: 'fd-chat-msgs', class: 'fd-chatlog' }),
|
|
131
|
+
],
|
|
132
|
+
}),
|
|
133
|
+
configuredProviders.length === 0
|
|
134
|
+
? Panel({ title: 'no providers configured', children: Receipt({ rows: [
|
|
135
|
+
['set API key', 'go to keys tab, click a provider chip to set its key'],
|
|
136
|
+
['then reload', 'refresh this page to see providers here'],
|
|
137
|
+
['or use acptoapi', 'run acptoapi server on localhost:4800 for local LLMs'],
|
|
138
|
+
] }) })
|
|
139
|
+
: Panel({ title: 'configured providers', children: h('div', { class: 'fd-chip-wrap' },
|
|
140
|
+
...providers.map(p => Chip({ tone: p.configured ? (p.available ? 'ok' : 'warn') : 'miss', children: p.name + (p.configured ? (p.available ? ' ●' : ' ○') : '') }))) }),
|
|
141
|
+
];
|
|
142
|
+
};
|
|
143
|
+
}
|
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
// Core freddie pages: projects, home, sessions, agents, logs.
|
|
2
|
-
import * as webjsx from '../../../../vendor/webjsx/index.js';
|
|
3
|
-
import * as components from '../../../components.js';
|
|
4
|
-
import { pre, form, skillLabel } from './helpers.js';
|
|
5
|
-
|
|
6
|
-
const h = webjsx.createElement;
|
|
7
|
-
const { Panel, Row, Hero, Receipt, Kpi, Table, EmptyState } = components;
|
|
8
|
-
|
|
9
|
-
export function makeCorePages(ctx) {
|
|
10
|
-
return {
|
|
11
|
-
async projects(h0) {
|
|
12
|
-
const list = h0.pi.projects.list();
|
|
13
|
-
const activeProj = (typeof h0.pi.projects.active === 'function') ? h0.pi.projects.active() : null;
|
|
14
|
-
const rows = list.map(p => Row({
|
|
15
|
-
key: p.name,
|
|
16
|
-
code: p.name === activeProj?.name ? '●' : '○',
|
|
17
|
-
title: p.name + (p.name === activeProj?.name ? ' (active)' : ''),
|
|
18
|
-
meta: p.path,
|
|
19
|
-
onClick: () => { if (p.name !== activeProj?.name) try { h0.pi.projects.setActive(p.name); ctx.rerender(); } catch (e) { alert(e.message); } },
|
|
20
|
-
}));
|
|
21
|
-
return [
|
|
22
|
-
Hero({ title: 'projects', body: 'each project is its own ~/.freddie home: separate sessions, agents, skills, config, env, cron, batches.', accent: activeProj ? 'active · ' + activeProj.name : 'no active project' }),
|
|
23
|
-
Kpi({ items: [[list.length, 'projects'], [activeProj?.name || '—', 'active'], [activeProj?.path?.length > 30 ? '…' + activeProj.path.slice(-28) : (activeProj?.path || '—'), 'path']] }),
|
|
24
|
-
Panel({ title: 'add a project', children: form({
|
|
25
|
-
fields: [{ name: 'name', placeholder: 'project name', required: true }, { name: 'path', placeholder: '/abs/path' }],
|
|
26
|
-
submit: 'add',
|
|
27
|
-
onSubmit: (ev) => { try { h0.pi.projects.create({ name: ev.target.elements.name.value, path: ev.target.elements.path.value }); ctx.rerender(); } catch (e) { alert(e.message); } },
|
|
28
|
-
}) }),
|
|
29
|
-
Panel({ title: 'all projects', count: list.length, children: rows.length ? rows : EmptyState({ text: 'no projects', glyph: '◆' }) }),
|
|
30
|
-
Panel({ title: 'how encapsulation works', children: Receipt({ rows: [
|
|
31
|
-
['sessions db', '<project>/sessions.db'],
|
|
32
|
-
['config', '<project>/config.json'],
|
|
33
|
-
['skills', '<project>/skills/'],
|
|
34
|
-
['plugins', '<project>/plugins/'],
|
|
35
|
-
['cron', '<project>/cron.db'],
|
|
36
|
-
['batches', '<project>/batches/'],
|
|
37
|
-
['logs', '<project>/logs/'],
|
|
38
|
-
['auth', '<project>/auth.json'],
|
|
39
|
-
] }) }),
|
|
40
|
-
];
|
|
41
|
-
},
|
|
42
|
-
async home(h0) {
|
|
43
|
-
const sessions = await h0.pi.sessions.list();
|
|
44
|
-
const tools = h0.pi.tools.size;
|
|
45
|
-
const skills = h0.pi.skills.size;
|
|
46
|
-
const health = (typeof h0.pi.health === 'function') ? h0.pi.health() : { ok: true };
|
|
47
|
-
return [
|
|
48
|
-
Hero({ title: 'freddie', body: 'open js agent harness — pi-mono · xstate · floosie · anentrypoint-design.', accent: h0.version || 'web' }),
|
|
49
|
-
Kpi({ items: [[sessions.length, 'sessions'], [tools, 'tools'], [skills, 'skills']] }),
|
|
50
|
-
Panel({ title: 'quick start', children: Receipt({ rows: [
|
|
51
|
-
['open chat', "click 'chat' in sidebar — set a working directory and pick a skill"],
|
|
52
|
-
['pick skill', "software dev, research, planning — shown with descriptions"],
|
|
53
|
-
['pick model', "select a configured provider + model in the chat bar"],
|
|
54
|
-
['list tools', '/tools in chat → tools tab'],
|
|
55
|
-
['set api key', 'keys tab → click chip to set value'],
|
|
56
|
-
['add cron', 'cron tab → form'],
|
|
57
|
-
] }) }),
|
|
58
|
-
Panel({ title: 'host', children: Receipt({ rows: Object.entries(health).map(([k, v]) => [k, String(v)]) }) }),
|
|
59
|
-
];
|
|
60
|
-
},
|
|
61
|
-
async sessions(h0) {
|
|
62
|
-
const list = await h0.pi.sessions.list();
|
|
63
|
-
const rows = list.map(s => {
|
|
64
|
-
const cont = h('button', {
|
|
65
|
-
class: 'btn-primary fd-btn-mini',
|
|
66
|
-
onclick: async () => {
|
|
67
|
-
const msgs = await h0.pi.sessions.getMessages(s.id);
|
|
68
|
-
const cs = window.__fd_chatState = window.__fd_chatState || { messages: [], busy: false, sessionId: null, cwd: '', skill: '', provider: '', model: '' };
|
|
69
|
-
cs.sessionId = s.id;
|
|
70
|
-
cs.messages = msgs.map(m => ({ role: m.role, content: String(m.content || '') }));
|
|
71
|
-
if (s.cwd) cs.cwd = s.cwd;
|
|
72
|
-
if (s.skill) cs.skill = s.skill;
|
|
73
|
-
if (typeof window.__fd_nav === 'function') window.__fd_nav('chat');
|
|
74
|
-
},
|
|
75
|
-
}, 'continue');
|
|
76
|
-
return [(s.id || '').slice(0, 8), s.title || '—', s.platform || '—', s.model || '—', s.cwd ? s.cwd.slice(-30) : '—', s.skill ? skillLabel({ name: s.skill }) : '—', cont];
|
|
77
|
-
});
|
|
78
|
-
return [
|
|
79
|
-
Kpi({ items: [[list.length, 'sessions']] }),
|
|
80
|
-
Panel({ title: 'recent sessions', count: list.length, children: list.length === 0
|
|
81
|
-
? EmptyState({ text: 'no sessions yet — open chat and send a message', glyph: '✉' })
|
|
82
|
-
: Table({ headers: ['id', 'title', 'platform', 'model', 'cwd', 'skill', ''], rows }) }),
|
|
83
|
-
];
|
|
84
|
-
},
|
|
85
|
-
async agents(h0) {
|
|
86
|
-
const a = (typeof h0.pi.agents === 'function') ? await h0.pi.agents() : { count: 0, turns: 0, active: null };
|
|
87
|
-
return [
|
|
88
|
-
Kpi({ items: [[a.count || 0, 'active'], [a.turns || 0, 'turns']] }),
|
|
89
|
-
Panel({ title: 'agent overview', children: Receipt({ rows: [
|
|
90
|
-
['total turns', String(a.turns || 0)],
|
|
91
|
-
['active session', a.active || '(none)'],
|
|
92
|
-
['last activity', a.last_activity ? new Date(a.last_activity).toLocaleString() : '—'],
|
|
93
|
-
] }) }),
|
|
94
|
-
];
|
|
95
|
-
},
|
|
96
|
-
async logs(h0) {
|
|
97
|
-
const dbg = (typeof h0.pi.debug === 'function') ? h0.pi.debug() : { note: 'no debug surface' };
|
|
98
|
-
return [Panel({ title: 'host debug snapshot', children: pre(dbg) })];
|
|
99
|
-
},
|
|
100
|
-
};
|
|
101
|
-
}
|
|
1
|
+
// Core freddie pages: projects, home, sessions, agents, logs.
|
|
2
|
+
import * as webjsx from '../../../../vendor/webjsx/index.js';
|
|
3
|
+
import * as components from '../../../components.js';
|
|
4
|
+
import { pre, form, skillLabel } from './helpers.js';
|
|
5
|
+
|
|
6
|
+
const h = webjsx.createElement;
|
|
7
|
+
const { Panel, Row, Hero, Receipt, Kpi, Table, EmptyState } = components;
|
|
8
|
+
|
|
9
|
+
export function makeCorePages(ctx) {
|
|
10
|
+
return {
|
|
11
|
+
async projects(h0) {
|
|
12
|
+
const list = h0.pi.projects.list();
|
|
13
|
+
const activeProj = (typeof h0.pi.projects.active === 'function') ? h0.pi.projects.active() : null;
|
|
14
|
+
const rows = list.map(p => Row({
|
|
15
|
+
key: p.name,
|
|
16
|
+
code: p.name === activeProj?.name ? '●' : '○',
|
|
17
|
+
title: p.name + (p.name === activeProj?.name ? ' (active)' : ''),
|
|
18
|
+
meta: p.path,
|
|
19
|
+
onClick: () => { if (p.name !== activeProj?.name) try { h0.pi.projects.setActive(p.name); ctx.rerender(); } catch (e) { alert(e.message); } },
|
|
20
|
+
}));
|
|
21
|
+
return [
|
|
22
|
+
Hero({ title: 'projects', body: 'each project is its own ~/.freddie home: separate sessions, agents, skills, config, env, cron, batches.', accent: activeProj ? 'active · ' + activeProj.name : 'no active project' }),
|
|
23
|
+
Kpi({ items: [[list.length, 'projects'], [activeProj?.name || '—', 'active'], [activeProj?.path?.length > 30 ? '…' + activeProj.path.slice(-28) : (activeProj?.path || '—'), 'path']] }),
|
|
24
|
+
Panel({ title: 'add a project', children: form({
|
|
25
|
+
fields: [{ name: 'name', placeholder: 'project name', required: true }, { name: 'path', placeholder: '/abs/path' }],
|
|
26
|
+
submit: 'add',
|
|
27
|
+
onSubmit: (ev) => { try { h0.pi.projects.create({ name: ev.target.elements.name.value, path: ev.target.elements.path.value }); ctx.rerender(); } catch (e) { alert(e.message); } },
|
|
28
|
+
}) }),
|
|
29
|
+
Panel({ title: 'all projects', count: list.length, children: rows.length ? rows : EmptyState({ text: 'no projects', glyph: '◆' }) }),
|
|
30
|
+
Panel({ title: 'how encapsulation works', children: Receipt({ rows: [
|
|
31
|
+
['sessions db', '<project>/sessions.db'],
|
|
32
|
+
['config', '<project>/config.json'],
|
|
33
|
+
['skills', '<project>/skills/'],
|
|
34
|
+
['plugins', '<project>/plugins/'],
|
|
35
|
+
['cron', '<project>/cron.db'],
|
|
36
|
+
['batches', '<project>/batches/'],
|
|
37
|
+
['logs', '<project>/logs/'],
|
|
38
|
+
['auth', '<project>/auth.json'],
|
|
39
|
+
] }) }),
|
|
40
|
+
];
|
|
41
|
+
},
|
|
42
|
+
async home(h0) {
|
|
43
|
+
const sessions = await h0.pi.sessions.list();
|
|
44
|
+
const tools = h0.pi.tools.size;
|
|
45
|
+
const skills = h0.pi.skills.size;
|
|
46
|
+
const health = (typeof h0.pi.health === 'function') ? h0.pi.health() : { ok: true };
|
|
47
|
+
return [
|
|
48
|
+
Hero({ title: 'freddie', body: 'open js agent harness — pi-mono · xstate · floosie · anentrypoint-design.', accent: h0.version || 'web' }),
|
|
49
|
+
Kpi({ items: [[sessions.length, 'sessions'], [tools, 'tools'], [skills, 'skills']] }),
|
|
50
|
+
Panel({ title: 'quick start', children: Receipt({ rows: [
|
|
51
|
+
['open chat', "click 'chat' in sidebar — set a working directory and pick a skill"],
|
|
52
|
+
['pick skill', "software dev, research, planning — shown with descriptions"],
|
|
53
|
+
['pick model', "select a configured provider + model in the chat bar"],
|
|
54
|
+
['list tools', '/tools in chat → tools tab'],
|
|
55
|
+
['set api key', 'keys tab → click chip to set value'],
|
|
56
|
+
['add cron', 'cron tab → form'],
|
|
57
|
+
] }) }),
|
|
58
|
+
Panel({ title: 'host', children: Receipt({ rows: Object.entries(health).map(([k, v]) => [k, String(v)]) }) }),
|
|
59
|
+
];
|
|
60
|
+
},
|
|
61
|
+
async sessions(h0) {
|
|
62
|
+
const list = await h0.pi.sessions.list();
|
|
63
|
+
const rows = list.map(s => {
|
|
64
|
+
const cont = h('button', {
|
|
65
|
+
class: 'btn-primary fd-btn-mini',
|
|
66
|
+
onclick: async () => {
|
|
67
|
+
const msgs = await h0.pi.sessions.getMessages(s.id);
|
|
68
|
+
const cs = window.__fd_chatState = window.__fd_chatState || { messages: [], busy: false, sessionId: null, cwd: '', skill: '', provider: '', model: '' };
|
|
69
|
+
cs.sessionId = s.id;
|
|
70
|
+
cs.messages = msgs.map(m => ({ role: m.role, content: String(m.content || '') }));
|
|
71
|
+
if (s.cwd) cs.cwd = s.cwd;
|
|
72
|
+
if (s.skill) cs.skill = s.skill;
|
|
73
|
+
if (typeof window.__fd_nav === 'function') window.__fd_nav('chat');
|
|
74
|
+
},
|
|
75
|
+
}, 'continue');
|
|
76
|
+
return [(s.id || '').slice(0, 8), s.title || '—', s.platform || '—', s.model || '—', s.cwd ? s.cwd.slice(-30) : '—', s.skill ? skillLabel({ name: s.skill }) : '—', cont];
|
|
77
|
+
});
|
|
78
|
+
return [
|
|
79
|
+
Kpi({ items: [[list.length, 'sessions']] }),
|
|
80
|
+
Panel({ title: 'recent sessions', count: list.length, children: list.length === 0
|
|
81
|
+
? EmptyState({ text: 'no sessions yet — open chat and send a message', glyph: '✉' })
|
|
82
|
+
: Table({ headers: ['id', 'title', 'platform', 'model', 'cwd', 'skill', ''], rows }) }),
|
|
83
|
+
];
|
|
84
|
+
},
|
|
85
|
+
async agents(h0) {
|
|
86
|
+
const a = (typeof h0.pi.agents === 'function') ? await h0.pi.agents() : { count: 0, turns: 0, active: null };
|
|
87
|
+
return [
|
|
88
|
+
Kpi({ items: [[a.count || 0, 'active'], [a.turns || 0, 'turns']] }),
|
|
89
|
+
Panel({ title: 'agent overview', children: Receipt({ rows: [
|
|
90
|
+
['total turns', String(a.turns || 0)],
|
|
91
|
+
['active session', a.active || '(none)'],
|
|
92
|
+
['last activity', a.last_activity ? new Date(a.last_activity).toLocaleString() : '—'],
|
|
93
|
+
] }) }),
|
|
94
|
+
];
|
|
95
|
+
},
|
|
96
|
+
async logs(h0) {
|
|
97
|
+
const dbg = (typeof h0.pi.debug === 'function') ? h0.pi.debug() : { note: 'no debug surface' };
|
|
98
|
+
return [Panel({ title: 'host debug snapshot', children: pre(dbg) })];
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
// OS-overlay freddie pages, only mounted when osSurfaces is provided.
|
|
2
|
-
import * as components from '../../../components.js';
|
|
3
|
-
import { pre } from './helpers.js';
|
|
4
|
-
|
|
5
|
-
const { Panel, Kpi, Table, EmptyState } = components;
|
|
6
|
-
|
|
7
|
-
export function makeOsPages(ctx) {
|
|
8
|
-
const { osSurfaces, instance } = ctx;
|
|
9
|
-
return {
|
|
10
|
-
async ['os-instances']() {
|
|
11
|
-
const list = (osSurfaces && osSurfaces.instances && osSurfaces.instances()) || [];
|
|
12
|
-
const activeId = osSurfaces && osSurfaces.activeInstanceId && osSurfaces.activeInstanceId();
|
|
13
|
-
return [
|
|
14
|
-
Kpi({ items: [[list.length, 'instances'], [activeId || '—', 'active']] }),
|
|
15
|
-
Panel({ title: 'instances', count: list.length, children: list.length === 0
|
|
16
|
-
? EmptyState({ text: 'no instances', glyph: '◫' })
|
|
17
|
-
: Table({ headers: ['id', 'active', 'shells', 'windows'],
|
|
18
|
-
rows: list.map(i => [i.id, i.id === activeId ? '●' : '', String((i.shells || []).length), String((i.windows || []).length)]) }) }),
|
|
19
|
-
];
|
|
20
|
-
},
|
|
21
|
-
async ['os-windows']() {
|
|
22
|
-
const wins = (osSurfaces && osSurfaces.wm && osSurfaces.wm.list && osSurfaces.wm.list()) || [];
|
|
23
|
-
const focused = osSurfaces && osSurfaces.wm && osSurfaces.wm.focused;
|
|
24
|
-
return [
|
|
25
|
-
Kpi({ items: [[wins.length, 'windows'], [focused ? (focused.id || focused.title || '?') : '—', 'focused']] }),
|
|
26
|
-
Panel({ title: 'windows', count: wins.length, children: wins.length === 0
|
|
27
|
-
? EmptyState({ text: 'no windows open', glyph: '▭' })
|
|
28
|
-
: Table({ headers: ['id', 'title', 'min', 'max', 'pos'],
|
|
29
|
-
rows: wins.map(w => [w.id || '?', w.title || '', w.min ? '●' : '', w.max ? '●' : '',
|
|
30
|
-
(w.el ? `${w.el.offsetLeft},${w.el.offsetTop} ${w.el.offsetWidth}×${w.el.offsetHeight}` : '')]) }) }),
|
|
31
|
-
];
|
|
32
|
-
},
|
|
33
|
-
async ['os-x']() {
|
|
34
|
-
const x = osSurfaces && osSurfaces.xServer && osSurfaces.xServer();
|
|
35
|
-
if (!x) return [Panel({ title: 'x-server', children: EmptyState({ text: 'x-server not running in this instance', glyph: '✕' }) })];
|
|
36
|
-
return [
|
|
37
|
-
Kpi({ items: [[x.windows, 'windows'], [x.pixmaps, 'pixmaps'], [x.gcs, 'gcs'], [x.atoms, 'atoms'], [x.cursors, 'cursors']] }),
|
|
38
|
-
Panel({ title: 'display', children: pre(x) }),
|
|
39
|
-
];
|
|
40
|
-
},
|
|
41
|
-
async ['os-fs']() {
|
|
42
|
-
const list = await instance.fs.list('/');
|
|
43
|
-
return [
|
|
44
|
-
Kpi({ items: [[list.length, 'paths'], [instance.id, 'instance']] }),
|
|
45
|
-
Panel({ title: 'paths', count: list.length, children: list.length === 0
|
|
46
|
-
? EmptyState({ text: 'empty fs', glyph: '📁' })
|
|
47
|
-
: pre(list.join('\n')) }),
|
|
48
|
-
];
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
}
|
|
1
|
+
// OS-overlay freddie pages, only mounted when osSurfaces is provided.
|
|
2
|
+
import * as components from '../../../components.js';
|
|
3
|
+
import { pre } from './helpers.js';
|
|
4
|
+
|
|
5
|
+
const { Panel, Kpi, Table, EmptyState } = components;
|
|
6
|
+
|
|
7
|
+
export function makeOsPages(ctx) {
|
|
8
|
+
const { osSurfaces, instance } = ctx;
|
|
9
|
+
return {
|
|
10
|
+
async ['os-instances']() {
|
|
11
|
+
const list = (osSurfaces && osSurfaces.instances && osSurfaces.instances()) || [];
|
|
12
|
+
const activeId = osSurfaces && osSurfaces.activeInstanceId && osSurfaces.activeInstanceId();
|
|
13
|
+
return [
|
|
14
|
+
Kpi({ items: [[list.length, 'instances'], [activeId || '—', 'active']] }),
|
|
15
|
+
Panel({ title: 'instances', count: list.length, children: list.length === 0
|
|
16
|
+
? EmptyState({ text: 'no instances', glyph: '◫' })
|
|
17
|
+
: Table({ headers: ['id', 'active', 'shells', 'windows'],
|
|
18
|
+
rows: list.map(i => [i.id, i.id === activeId ? '●' : '', String((i.shells || []).length), String((i.windows || []).length)]) }) }),
|
|
19
|
+
];
|
|
20
|
+
},
|
|
21
|
+
async ['os-windows']() {
|
|
22
|
+
const wins = (osSurfaces && osSurfaces.wm && osSurfaces.wm.list && osSurfaces.wm.list()) || [];
|
|
23
|
+
const focused = osSurfaces && osSurfaces.wm && osSurfaces.wm.focused;
|
|
24
|
+
return [
|
|
25
|
+
Kpi({ items: [[wins.length, 'windows'], [focused ? (focused.id || focused.title || '?') : '—', 'focused']] }),
|
|
26
|
+
Panel({ title: 'windows', count: wins.length, children: wins.length === 0
|
|
27
|
+
? EmptyState({ text: 'no windows open', glyph: '▭' })
|
|
28
|
+
: Table({ headers: ['id', 'title', 'min', 'max', 'pos'],
|
|
29
|
+
rows: wins.map(w => [w.id || '?', w.title || '', w.min ? '●' : '', w.max ? '●' : '',
|
|
30
|
+
(w.el ? `${w.el.offsetLeft},${w.el.offsetTop} ${w.el.offsetWidth}×${w.el.offsetHeight}` : '')]) }) }),
|
|
31
|
+
];
|
|
32
|
+
},
|
|
33
|
+
async ['os-x']() {
|
|
34
|
+
const x = osSurfaces && osSurfaces.xServer && osSurfaces.xServer();
|
|
35
|
+
if (!x) return [Panel({ title: 'x-server', children: EmptyState({ text: 'x-server not running in this instance', glyph: '✕' }) })];
|
|
36
|
+
return [
|
|
37
|
+
Kpi({ items: [[x.windows, 'windows'], [x.pixmaps, 'pixmaps'], [x.gcs, 'gcs'], [x.atoms, 'atoms'], [x.cursors, 'cursors']] }),
|
|
38
|
+
Panel({ title: 'display', children: pre(x) }),
|
|
39
|
+
];
|
|
40
|
+
},
|
|
41
|
+
async ['os-fs']() {
|
|
42
|
+
const list = await instance.fs.list('/');
|
|
43
|
+
return [
|
|
44
|
+
Kpi({ items: [[list.length, 'paths'], [instance.id, 'instance']] }),
|
|
45
|
+
Panel({ title: 'paths', count: list.length, children: list.length === 0
|
|
46
|
+
? EmptyState({ text: 'empty fs', glyph: '📁' })
|
|
47
|
+
: pre(list.join('\n')) }),
|
|
48
|
+
];
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|