anentrypoint-design 0.0.62 → 0.0.65
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anentrypoint-design",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.65",
|
|
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",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"./desktop/icons.js": "./src/desktop/icons.js",
|
|
24
24
|
"./desktop/shell.js": "./src/desktop/shell.js",
|
|
25
25
|
"./desktop/freddie-dashboard.js": "./src/desktop/freddie-dashboard.js",
|
|
26
|
+
"./desktop/freddie-dashboard.css": "./src/desktop/freddie-dashboard.css",
|
|
26
27
|
"./page-html": {
|
|
27
28
|
"import": "./src/page-html.js",
|
|
28
29
|
"default": "./src/page-html.js"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* freddie-dashboard.css — minimal supplemental rule.
|
|
2
|
+
The dashboard now renders through bible components (AppShell/Panel/Kpi/
|
|
3
|
+
Table/Receipt/Hero/Row/EmptyState/Chip) that ship their own classes via
|
|
4
|
+
247420.css. The only extra: a styled <pre> for raw JSON dumps. */
|
|
5
|
+
.app-fd .fd-pre {
|
|
6
|
+
font-family: var(--ff-mono, JetBrains Mono, monospace);
|
|
7
|
+
font-size: 12px;
|
|
8
|
+
white-space: pre-wrap;
|
|
9
|
+
word-break: break-all;
|
|
10
|
+
margin: 0;
|
|
11
|
+
max-height: 320px;
|
|
12
|
+
overflow: auto;
|
|
13
|
+
background: var(--panel-2, #DDD3BC);
|
|
14
|
+
padding: 8px 10px;
|
|
15
|
+
border-radius: var(--r-1, 6px);
|
|
16
|
+
}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import * as webjsx from '../../vendor/webjsx/index.js';
|
|
2
|
+
import * as components from '../components.js';
|
|
3
|
+
|
|
4
|
+
const h = webjsx.createElement;
|
|
5
|
+
const {
|
|
6
|
+
AppShell, Topbar, Side, Crumb, Status, Brand, Glyph,
|
|
7
|
+
Panel, Row, RowLink, Hero, Receipt, Kpi, Table, Section,
|
|
8
|
+
EmptyState, Chip,
|
|
9
|
+
} = components;
|
|
10
|
+
|
|
1
11
|
const ROUTES = [
|
|
2
12
|
{ path: 'projects', label: 'projects', glyph: '◆' },
|
|
3
13
|
{ path: 'home', label: 'home', glyph: '⌂' },
|
|
@@ -16,130 +26,40 @@ const ROUTES = [
|
|
|
16
26
|
{ path: 'gateway', label: 'gateway', glyph: '⇌' },
|
|
17
27
|
];
|
|
18
28
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
else if (k === 'text') e.textContent = attrs.text;
|
|
26
|
-
else e.setAttribute(k, attrs[k]);
|
|
27
|
-
}
|
|
28
|
-
return e;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function ensureStyles() {
|
|
32
|
-
if (document.getElementById('freddie-dashboard-style')) return;
|
|
33
|
-
const s = document.createElement('style');
|
|
34
|
-
s.id = 'freddie-dashboard-style';
|
|
35
|
-
s.textContent = `
|
|
36
|
-
.fdash { display: flex; height: 100%; font-family: var(--ff-ui, Nunito, sans-serif); color: var(--ink, #1F1B16); }
|
|
37
|
-
.fdash-side { flex: 0 0 180px; background: var(--panel-1, #ECE6D5); border-right: 1px solid var(--panel-2, #DDD3BC); padding: 8px 0; overflow-y: auto; }
|
|
38
|
-
.fdash-nav { display: flex; flex-direction: column; gap: 2px; }
|
|
39
|
-
.fdash-nav button { text-align: left; padding: 6px 12px; background: transparent; color: inherit; border: 0; cursor: pointer; font-family: inherit; font-size: 12px; border-left: 4px solid transparent; }
|
|
40
|
-
.fdash-nav button:hover { background: var(--panel-hover, rgba(0,0,0,0.04)); }
|
|
41
|
-
.fdash-nav button.active { background: var(--panel-select, #C8E4CA); border-left-color: var(--panel-accent, #3F8A4A); font-weight: 600; }
|
|
42
|
-
.fdash-nav .glyph { display: inline-block; width: 16px; opacity: 0.7; font-family: var(--ff-mono, JetBrains Mono, monospace); }
|
|
43
|
-
.fdash-main { flex: 1; padding: 12px 16px; overflow: auto; min-width: 0; }
|
|
44
|
-
.fdash-h { font-size: 14px; font-weight: 700; margin: 0 0 8px; }
|
|
45
|
-
.fdash-kpi { display: flex; gap: 12px; margin-bottom: 12px; flex-wrap: wrap; }
|
|
46
|
-
.fdash-kpi .k { background: var(--panel-2, #DDD3BC); padding: 6px 12px; border-radius: var(--r-1, 6px); min-width: 80px; }
|
|
47
|
-
.fdash-kpi .k .v { font-size: 18px; font-weight: 700; font-family: var(--ff-mono, JetBrains Mono, monospace); }
|
|
48
|
-
.fdash-kpi .k .l { font-size: 10px; opacity: 0.7; text-transform: uppercase; }
|
|
49
|
-
.fdash-panel { background: var(--panel-1, #ECE6D5); border-radius: var(--r-1, 6px); padding: 8px 12px; margin-bottom: 8px; }
|
|
50
|
-
.fdash-panel h3 { font-size: 12px; font-weight: 700; margin: 0 0 6px; opacity: 0.8; text-transform: uppercase; }
|
|
51
|
-
.fdash-panel pre { font-family: var(--ff-mono, JetBrains Mono, monospace); font-size: 11px; white-space: pre-wrap; word-break: break-all; margin: 0; max-height: 280px; overflow: auto; }
|
|
52
|
-
.fdash-panel table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
53
|
-
.fdash-panel th, .fdash-panel td { padding: 4px 8px; text-align: left; border-bottom: 1px solid var(--panel-2, #DDD3BC); }
|
|
54
|
-
.fdash-panel th { font-weight: 600; opacity: 0.7; font-size: 10px; text-transform: uppercase; }
|
|
55
|
-
.fdash-row { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: 12px; }
|
|
56
|
-
.fdash-row .code { font-family: var(--ff-mono, JetBrains Mono, monospace); opacity: 0.6; }
|
|
57
|
-
.fdash-row .meta { margin-left: auto; opacity: 0.5; font-size: 11px; }
|
|
58
|
-
.fdash-form { display: flex; gap: 4px; flex-wrap: wrap; align-items: center; margin-bottom: 8px; }
|
|
59
|
-
.fdash-form input, .fdash-form textarea, .fdash-form select { font: inherit; padding: 4px 8px; border: 1px solid var(--panel-2, #DDD3BC); border-radius: var(--r-1, 6px); background: var(--panel-0, #F5F0E4); color: inherit; }
|
|
60
|
-
.fdash-form button { padding: 4px 12px; background: var(--panel-accent, #3F8A4A); color: #fff; border: 0; border-radius: var(--r-1, 6px); cursor: pointer; }
|
|
61
|
-
.fdash-form button.danger { background: #c44; }
|
|
62
|
-
.fdash-chip { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 11px; margin: 2px; }
|
|
63
|
-
.fdash-chip.ok { background: var(--panel-select, #C8E4CA); }
|
|
64
|
-
.fdash-chip.miss { background: var(--panel-2, #DDD3BC); opacity: 0.6; }
|
|
65
|
-
.fdash-empty { padding: 20px; text-align: center; opacity: 0.5; font-size: 12px; }
|
|
66
|
-
@media (max-width: 600px) { .fdash-side { flex: 0 0 56px; } .fdash-nav button .label { display: none; } }
|
|
67
|
-
`;
|
|
68
|
-
document.head.appendChild(s);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function kpi(items) {
|
|
72
|
-
const c = el('div', 'fdash-kpi');
|
|
73
|
-
for (const [v, l] of items) {
|
|
74
|
-
const k = el('div', 'k');
|
|
75
|
-
k.appendChild(el('div', 'v', { text: String(v) }));
|
|
76
|
-
k.appendChild(el('div', 'l', { text: String(l) }));
|
|
77
|
-
c.appendChild(k);
|
|
78
|
-
}
|
|
79
|
-
return c;
|
|
80
|
-
}
|
|
29
|
+
const OS_ROUTE_DEFS = [
|
|
30
|
+
{ path: 'os-instances', label: 'instances', glyph: '◫' },
|
|
31
|
+
{ path: 'os-windows', label: 'windows', glyph: '▭' },
|
|
32
|
+
{ path: 'os-x', label: 'x-server', glyph: '✕' },
|
|
33
|
+
{ path: 'os-fs', label: 'fs', glyph: '📁' },
|
|
34
|
+
];
|
|
81
35
|
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
const h = el('h3'); h.textContent = title + (count != null ? ' · ' + count : '');
|
|
85
|
-
p.appendChild(h);
|
|
86
|
-
if (body instanceof Node) p.appendChild(body);
|
|
87
|
-
else if (Array.isArray(body)) for (const n of body) if (n) p.appendChild(n);
|
|
88
|
-
else if (typeof body === 'string') { const pre = el('pre'); pre.textContent = body; p.appendChild(pre); }
|
|
89
|
-
return p;
|
|
36
|
+
function pre(obj) {
|
|
37
|
+
return h('pre', { class: 'fd-pre' }, typeof obj === 'string' ? obj : JSON.stringify(obj, null, 2));
|
|
90
38
|
}
|
|
91
39
|
|
|
92
|
-
function
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
40
|
+
function form(opts) {
|
|
41
|
+
const { fields = [], submit = 'submit', onSubmit } = opts;
|
|
42
|
+
return h('form', { class: 'row-form', onsubmit: (ev) => { ev.preventDefault(); onSubmit && onSubmit(ev); } },
|
|
43
|
+
...fields.map(f => f.kind === 'textarea'
|
|
44
|
+
? h('textarea', { name: f.name, placeholder: f.placeholder || '', rows: f.rows || 4 })
|
|
45
|
+
: h('input', { name: f.name, type: f.type || 'text', placeholder: f.placeholder || '', value: f.value || '', required: f.required ? 'true' : null })),
|
|
46
|
+
h('button', { type: 'submit', class: 'btn-primary' }, submit));
|
|
99
47
|
}
|
|
100
48
|
|
|
101
|
-
function
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
thead.appendChild(trh); t.appendChild(thead);
|
|
106
|
-
const tb = el('tbody');
|
|
107
|
-
for (const r of rows) {
|
|
108
|
-
const tr = el('tr');
|
|
109
|
-
for (const c of r) tr.appendChild(el('td', null, { text: String(c) }));
|
|
110
|
-
tb.appendChild(tr);
|
|
111
|
-
}
|
|
112
|
-
t.appendChild(tb);
|
|
113
|
-
return t;
|
|
114
|
-
}
|
|
49
|
+
export function createFreddieDashboard({ instance, bootHost, osSurfaces }) {
|
|
50
|
+
const root = document.createElement('div');
|
|
51
|
+
root.className = 'app-fd ds-247420';
|
|
52
|
+
root.style.cssText = 'height:100%;overflow:hidden;display:flex;flex-direction:column;';
|
|
115
53
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const nav = el('div', 'fdash-nav');
|
|
123
|
-
const main = el('div', 'fdash-main');
|
|
124
|
-
side.appendChild(nav);
|
|
125
|
-
root.appendChild(side); root.appendChild(main);
|
|
126
|
-
|
|
127
|
-
let active = 'home';
|
|
54
|
+
const state = {
|
|
55
|
+
active: 'home',
|
|
56
|
+
ts: new Date().toLocaleTimeString(),
|
|
57
|
+
body: null,
|
|
58
|
+
error: null,
|
|
59
|
+
};
|
|
128
60
|
let host = instance.host || null;
|
|
129
61
|
|
|
130
|
-
|
|
131
|
-
active = p;
|
|
132
|
-
for (const b of nav.querySelectorAll('button')) b.classList.toggle('active', b.dataset.path === p);
|
|
133
|
-
render();
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
for (const r of ROUTES) {
|
|
137
|
-
const b = el('button', null, { 'data-path': r.path, on: { click: () => setActive(r.path) } });
|
|
138
|
-
b.appendChild(el('span', 'glyph', { text: r.glyph }));
|
|
139
|
-
b.appendChild(document.createTextNode(' '));
|
|
140
|
-
b.appendChild(el('span', 'label', { text: r.label }));
|
|
141
|
-
nav.appendChild(b);
|
|
142
|
-
}
|
|
62
|
+
const allRoutes = osSurfaces ? [...ROUTES, ...OS_ROUTE_DEFS] : ROUTES;
|
|
143
63
|
|
|
144
64
|
async function ensureHost() {
|
|
145
65
|
if (host) return host;
|
|
@@ -148,247 +68,362 @@ export function createFreddieDashboard({ instance, bootHost }) {
|
|
|
148
68
|
return host;
|
|
149
69
|
}
|
|
150
70
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
71
|
+
function setActive(p) {
|
|
72
|
+
state.active = p;
|
|
73
|
+
rerender();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function buildSide() {
|
|
77
|
+
const sections = [{
|
|
78
|
+
group: 'FREDDIE',
|
|
79
|
+
items: ROUTES.map(r => ({
|
|
80
|
+
glyph: r.glyph, label: r.label, href: '#fd-' + r.path,
|
|
81
|
+
active: state.active === r.path,
|
|
82
|
+
onClick: (ev) => { ev.preventDefault(); setActive(r.path); },
|
|
83
|
+
})),
|
|
84
|
+
}];
|
|
85
|
+
if (osSurfaces) sections.push({
|
|
86
|
+
group: 'OS',
|
|
87
|
+
items: OS_ROUTE_DEFS.map(r => ({
|
|
88
|
+
glyph: r.glyph, label: r.label, href: '#fd-' + r.path,
|
|
89
|
+
active: state.active === r.path,
|
|
90
|
+
onClick: (ev) => { ev.preventDefault(); setActive(r.path); },
|
|
91
|
+
})),
|
|
92
|
+
});
|
|
93
|
+
return Side({ sections });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function view() {
|
|
97
|
+
const route = allRoutes.find(r => r.path === state.active) || ROUTES[1];
|
|
98
|
+
return AppShell({
|
|
99
|
+
topbar: Topbar({ brand: 'freddie', leaf: 'dashboard', items: [], active: '' }),
|
|
100
|
+
crumb: Crumb({ trail: ['freddie', instance.id], leaf: route.path, right: state.error ? Chip({ tone: 'miss', children: 'error' }) : Chip({ tone: 'ok', children: 'live' }) }),
|
|
101
|
+
side: buildSide(),
|
|
102
|
+
main: state.body || EmptyState({ text: 'loading…', glyph: '◌' }),
|
|
103
|
+
status: Status({ left: ['ds-247420 · webjsx · ' + allRoutes.length + ' routes', 'instance=' + instance.id], right: [state.ts] }),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function rerender() {
|
|
108
|
+
webjsx.applyDiff(root, view());
|
|
109
|
+
loadActive();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function loadActive() {
|
|
156
113
|
try {
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
|
|
114
|
+
const h0 = await ensureHost();
|
|
115
|
+
const page = PAGES[state.active] || PAGES.home;
|
|
116
|
+
state.body = await page(h0, instance);
|
|
117
|
+
state.error = null;
|
|
160
118
|
} catch (e) {
|
|
161
|
-
|
|
119
|
+
state.error = String(e && e.stack || e);
|
|
120
|
+
state.body = Panel({ title: 'error', children: pre(state.error) });
|
|
162
121
|
}
|
|
122
|
+
state.ts = new Date().toLocaleTimeString();
|
|
123
|
+
webjsx.applyDiff(root, view());
|
|
163
124
|
}
|
|
164
125
|
|
|
165
126
|
const PAGES = {
|
|
166
|
-
async projects(
|
|
167
|
-
const list =
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
form.appendChild(el('button', null, { type: 'submit', text: 'add' }));
|
|
177
|
-
const rows = list.map(p => {
|
|
178
|
-
const r = el('div', 'fdash-row');
|
|
179
|
-
r.appendChild(el('span', 'code', { text: p.name === active?.name ? '●' : '○' }));
|
|
180
|
-
r.appendChild(el('span', null, { text: p.name + (p.name === active?.name ? ' (active)' : '') }));
|
|
181
|
-
r.appendChild(el('span', 'meta', { text: p.path }));
|
|
182
|
-
if (p.name !== 'default') {
|
|
183
|
-
const del = el('button', null, { type: 'button', text: 'remove', on: { click: () => { try { h.pi.projects.remove(p.name); render(); } catch (e) { alert(e.message); } } } });
|
|
184
|
-
r.appendChild(del);
|
|
185
|
-
}
|
|
186
|
-
if (p.name !== active?.name) {
|
|
187
|
-
const sw = el('button', null, { type: 'button', text: 'switch', on: { click: () => { h.pi.projects.setActive(p.name); render(); } } });
|
|
188
|
-
r.appendChild(sw);
|
|
189
|
-
}
|
|
190
|
-
return r;
|
|
191
|
-
});
|
|
127
|
+
async projects(h0) {
|
|
128
|
+
const list = h0.pi.projects.list();
|
|
129
|
+
const activeProj = (typeof h0.pi.projects.active === 'function') ? h0.pi.projects.active() : null;
|
|
130
|
+
const rows = list.map(p => Row({
|
|
131
|
+
key: p.name,
|
|
132
|
+
code: p.name === activeProj?.name ? '●' : '○',
|
|
133
|
+
title: p.name + (p.name === activeProj?.name ? ' (active)' : ''),
|
|
134
|
+
meta: p.path,
|
|
135
|
+
onClick: () => { if (p.name !== activeProj?.name) try { h0.pi.projects.setActive(p.name); rerender(); } catch (e) { alert(e.message); } },
|
|
136
|
+
}));
|
|
192
137
|
return [
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
138
|
+
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' }),
|
|
139
|
+
Kpi({ items: [[list.length, 'projects'], [activeProj?.name || '—', 'active'], [activeProj?.path?.length > 30 ? '…' + activeProj.path.slice(-28) : (activeProj?.path || '—'), 'path']] }),
|
|
140
|
+
Panel({ title: 'add a project', children: form({
|
|
141
|
+
fields: [{ name: 'name', placeholder: 'project name', required: true }, { name: 'path', placeholder: '/abs/path' }],
|
|
142
|
+
submit: 'add',
|
|
143
|
+
onSubmit: (ev) => { try { h0.pi.projects.create({ name: ev.target.elements.name.value, path: ev.target.elements.path.value }); rerender(); } catch (e) { alert(e.message); } },
|
|
144
|
+
}) }),
|
|
145
|
+
Panel({ title: 'all projects', count: list.length, children: rows.length ? rows : EmptyState({ text: 'no projects', glyph: '◆' }) }),
|
|
146
|
+
Panel({ title: 'how encapsulation works', children: Receipt({ rows: [
|
|
147
|
+
['sessions db', '<project>/sessions.db'],
|
|
148
|
+
['config', '<project>/config.json'],
|
|
149
|
+
['skills', '<project>/skills/'],
|
|
150
|
+
['plugins', '<project>/plugins/'],
|
|
151
|
+
['cron', '<project>/cron.db'],
|
|
152
|
+
['batches', '<project>/batches/'],
|
|
153
|
+
['logs', '<project>/logs/'],
|
|
154
|
+
['auth', '<project>/auth.json'],
|
|
155
|
+
] }) }),
|
|
196
156
|
];
|
|
197
157
|
},
|
|
198
|
-
async home(
|
|
199
|
-
const sessions = await
|
|
200
|
-
const tools =
|
|
201
|
-
const skills =
|
|
202
|
-
const health =
|
|
158
|
+
async home(h0) {
|
|
159
|
+
const sessions = await h0.pi.sessions.list();
|
|
160
|
+
const tools = h0.pi.tools.size;
|
|
161
|
+
const skills = h0.pi.skills.size;
|
|
162
|
+
const health = (typeof h0.pi.health === 'function') ? h0.pi.health() : { ok: true };
|
|
203
163
|
return [
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
['
|
|
208
|
-
['list
|
|
209
|
-
['
|
|
210
|
-
|
|
211
|
-
|
|
164
|
+
Hero({ title: 'freddie', body: 'open js agent harness — pi-mono · xstate · floosie · anentrypoint-design.', accent: h0.version || 'web' }),
|
|
165
|
+
Kpi({ items: [[sessions.length, 'sessions'], [tools, 'tools'], [skills, 'skills']] }),
|
|
166
|
+
Panel({ title: 'quick start', children: Receipt({ rows: [
|
|
167
|
+
['open chat', "click 'chat' in sidebar"],
|
|
168
|
+
['list tools', '/tools in chat → tools tab'],
|
|
169
|
+
['list skills', '/skills → skills tab'],
|
|
170
|
+
['set api key', 'keys tab → click chip'],
|
|
171
|
+
['add cron', 'cron tab → form'],
|
|
172
|
+
] }) }),
|
|
173
|
+
Panel({ title: 'host', children: Receipt({ rows: Object.entries(health).map(([k, v]) => [k, String(v)]) }) }),
|
|
212
174
|
];
|
|
213
175
|
},
|
|
214
|
-
async chat(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
176
|
+
async chat(h0) {
|
|
177
|
+
try { if (typeof window !== 'undefined' && window.__debug?.shell?.openApp) window.__debug.shell.openApp('chat'); } catch {}
|
|
178
|
+
return [
|
|
179
|
+
Panel({ title: 'chat', children: EmptyState({ text: 'opening chat window — chat lives in its own thebird app.', glyph: '⌨' }) }),
|
|
180
|
+
Panel({ title: 'cli surface', count: h0.pi.cli.size,
|
|
181
|
+
children: Table({ headers: ['command', 'description'], rows: [...h0.pi.cli.values()].map(c => [c.name, c.description || '']) }) }),
|
|
182
|
+
];
|
|
220
183
|
},
|
|
221
|
-
async sessions(
|
|
222
|
-
const list = await
|
|
184
|
+
async sessions(h0) {
|
|
185
|
+
const list = await h0.pi.sessions.list();
|
|
223
186
|
return [
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
?
|
|
227
|
-
:
|
|
187
|
+
Kpi({ items: [[list.length, 'sessions']] }),
|
|
188
|
+
Panel({ title: 'recent sessions', count: list.length, children: list.length === 0
|
|
189
|
+
? EmptyState({ text: 'no sessions yet — start a chat', glyph: '✉' })
|
|
190
|
+
: Table({ headers: ['id', 'title', 'platform', 'model', 'turns'],
|
|
191
|
+
rows: list.map(s => [(s.id || '').slice(0, 8), s.title || '—', s.platform || '—', s.model || '—', s.turn_count || 0]) }) }),
|
|
228
192
|
];
|
|
229
193
|
},
|
|
230
|
-
async agents(
|
|
231
|
-
const a = await
|
|
194
|
+
async agents(h0) {
|
|
195
|
+
const a = (typeof h0.pi.agents === 'function') ? await h0.pi.agents() : { count: 0, turns: 0, active: null };
|
|
232
196
|
return [
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
['total turns', String(a.turns)],
|
|
197
|
+
Kpi({ items: [[a.count || 0, 'active'], [a.turns || 0, 'turns']] }),
|
|
198
|
+
Panel({ title: 'agent overview', children: Receipt({ rows: [
|
|
199
|
+
['total turns', String(a.turns || 0)],
|
|
236
200
|
['active session', a.active || '(none)'],
|
|
237
201
|
['last activity', a.last_activity ? new Date(a.last_activity).toLocaleString() : '—'],
|
|
238
|
-
])),
|
|
202
|
+
] }) }),
|
|
239
203
|
];
|
|
240
204
|
},
|
|
241
|
-
async analytics(
|
|
242
|
-
const list = await
|
|
243
|
-
const tools = [...
|
|
205
|
+
async analytics(h0) {
|
|
206
|
+
const list = await h0.pi.sessions.list();
|
|
207
|
+
const tools = [...h0.pi.tools.values()];
|
|
244
208
|
const byPlatform = list.reduce((a, s) => { const k = s.platform || '?'; a[k] = (a[k] || 0) + 1; return a; }, {});
|
|
245
209
|
const byModel = list.reduce((a, s) => { const k = s.model || '?'; a[k] = (a[k] || 0) + 1; return a; }, {});
|
|
210
|
+
const byToolset = tools.reduce((a, t) => { (a[t.toolset || 'core'] = a[t.toolset || 'core'] || []).push(t.name); return a; }, {});
|
|
246
211
|
return [
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
212
|
+
Kpi({ items: [[list.length, 'sessions'], [tools.length, 'tools']] }),
|
|
213
|
+
Panel({ title: 'sessions by platform', children: Object.keys(byPlatform).length === 0
|
|
214
|
+
? EmptyState({ text: 'no data', glyph: '◉' })
|
|
215
|
+
: Table({ headers: ['platform', 'count'], rows: Object.entries(byPlatform).sort((a, b) => b[1] - a[1]) }) }),
|
|
216
|
+
Panel({ title: 'sessions by model', children: Object.keys(byModel).length === 0
|
|
217
|
+
? EmptyState({ text: 'no data', glyph: '◎' })
|
|
218
|
+
: Table({ headers: ['model', 'count'], rows: Object.entries(byModel).sort((a, b) => b[1] - a[1]) }) }),
|
|
219
|
+
Panel({ title: 'tool distribution', children: Table({ headers: ['toolset', 'count', 'tools'],
|
|
220
|
+
rows: Object.entries(byToolset).map(([k, v]) => [k, v.length, v.slice(0, 4).join(', ') + (v.length > 4 ? '…' : '')]) }) }),
|
|
251
221
|
];
|
|
252
222
|
},
|
|
253
|
-
async models(
|
|
254
|
-
const cfg =
|
|
223
|
+
async models(h0) {
|
|
224
|
+
const cfg = (typeof h0.pi.config?.load === 'function') ? await h0.pi.config.load() : {};
|
|
255
225
|
const agent = cfg.agent || {};
|
|
256
|
-
const form = el('form', 'fdash-form', { on: { submit: (ev) => {
|
|
257
|
-
ev.preventDefault();
|
|
258
|
-
h.pi.config.saveValue('agent.provider', ev.target.elements.provider.value);
|
|
259
|
-
h.pi.config.saveValue('agent.model', ev.target.elements.model.value);
|
|
260
|
-
render();
|
|
261
|
-
} } });
|
|
262
|
-
form.appendChild(el('input', null, { name: 'provider', placeholder: 'provider', value: agent.provider || '' }));
|
|
263
|
-
form.appendChild(el('input', null, { name: 'model', placeholder: 'model id', value: agent.model || '' }));
|
|
264
|
-
form.appendChild(el('button', null, { type: 'submit', text: 'update' }));
|
|
265
226
|
return [
|
|
266
|
-
|
|
267
|
-
|
|
227
|
+
Kpi({ items: [[agent.provider || '—', 'provider'], [agent.model || '—', 'model']] }),
|
|
228
|
+
Panel({ title: 'active model', children: Receipt({ rows: [
|
|
268
229
|
['provider', agent.provider || '(unset)'],
|
|
269
230
|
['model', agent.model || '(unset)'],
|
|
270
231
|
['max_iterations', String(agent.max_iterations || '—')],
|
|
271
|
-
|
|
272
|
-
|
|
232
|
+
['max_tokens', String(agent.max_tokens || '—')],
|
|
233
|
+
['temperature', String(agent.temperature ?? '—')],
|
|
234
|
+
] }) }),
|
|
235
|
+
Panel({ title: 'change model', children: form({
|
|
236
|
+
fields: [{ name: 'provider', placeholder: 'provider', value: agent.provider || '' }, { name: 'model', placeholder: 'model id', value: agent.model || '' }],
|
|
237
|
+
submit: 'update',
|
|
238
|
+
onSubmit: async (ev) => {
|
|
239
|
+
await h0.pi.config.saveValue('agent.provider', ev.target.elements.provider.value);
|
|
240
|
+
await h0.pi.config.saveValue('agent.model', ev.target.elements.model.value);
|
|
241
|
+
rerender();
|
|
242
|
+
},
|
|
243
|
+
}) }),
|
|
273
244
|
];
|
|
274
245
|
},
|
|
275
|
-
async logs(
|
|
276
|
-
const dbg =
|
|
277
|
-
return [
|
|
278
|
-
panel('host debug snapshot', pre(dbg)),
|
|
279
|
-
];
|
|
246
|
+
async logs(h0) {
|
|
247
|
+
const dbg = (typeof h0.pi.debug === 'function') ? h0.pi.debug() : { note: 'no debug surface' };
|
|
248
|
+
return [Panel({ title: 'host debug snapshot', children: pre(dbg) })];
|
|
280
249
|
},
|
|
281
|
-
async cron(
|
|
282
|
-
const list = await
|
|
283
|
-
const form = el('form', 'fdash-form', { on: { submit: async (ev) => {
|
|
284
|
-
ev.preventDefault();
|
|
285
|
-
try { await h.pi.cron.create({ cron: ev.target.elements.cron.value, prompt: ev.target.elements.prompt.value }); render(); }
|
|
286
|
-
catch (e) { alert(e.message); }
|
|
287
|
-
} } });
|
|
288
|
-
form.appendChild(el('input', null, { name: 'cron', placeholder: '* * * * *', required: 'true' }));
|
|
289
|
-
form.appendChild(el('input', null, { name: 'prompt', placeholder: 'prompt', required: 'true' }));
|
|
290
|
-
form.appendChild(el('button', null, { type: 'submit', text: 'create' }));
|
|
291
|
-
const tbl = list.length === 0
|
|
292
|
-
? el('div', 'fdash-empty', { text: 'no cron jobs' })
|
|
293
|
-
: table(['id', 'cron', 'prompt', 'enabled'], list.map(j => [j.id, j.cron, (j.prompt || '').slice(0, 40), j.enabled ? 'yes' : 'no']));
|
|
250
|
+
async cron(h0) {
|
|
251
|
+
const list = await h0.pi.cron.list();
|
|
294
252
|
return [
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
253
|
+
Kpi({ items: [[list.length, 'cron jobs']] }),
|
|
254
|
+
Panel({ title: 'add job', children: form({
|
|
255
|
+
fields: [{ name: 'cron', placeholder: '* * * * *', required: true }, { name: 'prompt', placeholder: 'prompt', required: true }],
|
|
256
|
+
submit: 'create',
|
|
257
|
+
onSubmit: async (ev) => { try { await h0.pi.cron.create({ cron: ev.target.elements.cron.value, prompt: ev.target.elements.prompt.value }); rerender(); } catch (e) { alert(e.message); } },
|
|
258
|
+
}) }),
|
|
259
|
+
Panel({ title: 'scheduled jobs', count: list.length, children: list.length === 0
|
|
260
|
+
? EmptyState({ text: 'no cron jobs — add one above', glyph: '◷' })
|
|
261
|
+
: Table({ headers: ['id', 'cron', 'prompt', 'enabled'],
|
|
262
|
+
rows: list.map(j => [j.id, j.cron, (j.prompt || '').slice(0, 40), j.enabled ? 'yes' : 'no']) }) }),
|
|
298
263
|
];
|
|
299
264
|
},
|
|
300
|
-
async skills(
|
|
301
|
-
const list = [...
|
|
265
|
+
async skills(h0) {
|
|
266
|
+
const list = [...h0.pi.skills.values()];
|
|
302
267
|
const byCat = list.reduce((a, s) => { (a[s.category || 'other'] = a[s.category || 'other'] || []).push(s); return a; }, {});
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
268
|
+
return [
|
|
269
|
+
Kpi({ items: [[list.length, 'skills'], [Object.keys(byCat).length, 'categories']] }),
|
|
270
|
+
...Object.entries(byCat).map(([cat, ss]) => Panel({ title: cat, count: ss.length,
|
|
271
|
+
children: ss.length === 0 ? EmptyState({ text: 'none', glyph: '◈' })
|
|
272
|
+
: Table({ headers: ['name', 'description'], rows: ss.map(s => [s.shortName || s.name, (s.description || '').slice(0, 100)]) }) })),
|
|
273
|
+
];
|
|
308
274
|
},
|
|
309
|
-
async config(
|
|
310
|
-
const cfg =
|
|
311
|
-
const profiles =
|
|
312
|
-
const commands =
|
|
313
|
-
const form = el('form', 'fdash-form', { on: { submit: (ev) => {
|
|
314
|
-
ev.preventDefault();
|
|
315
|
-
let v = ev.target.elements.value.value;
|
|
316
|
-
try { v = JSON.parse(v); } catch {}
|
|
317
|
-
h.pi.config.saveValue(ev.target.elements.key.value, v);
|
|
318
|
-
render();
|
|
319
|
-
} } });
|
|
320
|
-
form.appendChild(el('input', null, { name: 'key', placeholder: 'dotted.key', required: 'true' }));
|
|
321
|
-
form.appendChild(el('input', null, { name: 'value', placeholder: 'value (json or string)', required: 'true' }));
|
|
322
|
-
form.appendChild(el('button', null, { type: 'submit', text: 'save' }));
|
|
275
|
+
async config(h0) {
|
|
276
|
+
const cfg = (typeof h0.pi.config?.load === 'function') ? await h0.pi.config.load() : {};
|
|
277
|
+
const profiles = (typeof h0.pi.profiles?.list === 'function') ? h0.pi.profiles.list() : [];
|
|
278
|
+
const commands = (typeof h0.pi.commands?.list === 'function') ? h0.pi.commands.list() : [];
|
|
323
279
|
return [
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
280
|
+
Kpi({ items: [[profiles.length, 'profiles'], [commands.length, 'commands'], [cfg._config_version || 0, 'config version']] }),
|
|
281
|
+
Panel({ title: 'set config value', children: form({
|
|
282
|
+
fields: [{ name: 'key', placeholder: 'dotted.key (e.g. agent.model)', required: true }, { name: 'value', placeholder: 'value (json or string)', required: true }],
|
|
283
|
+
submit: 'save',
|
|
284
|
+
onSubmit: async (ev) => {
|
|
285
|
+
let v = ev.target.elements.value.value;
|
|
286
|
+
try { v = JSON.parse(v); } catch {}
|
|
287
|
+
await h0.pi.config.saveValue(ev.target.elements.key.value, v);
|
|
288
|
+
rerender();
|
|
289
|
+
},
|
|
290
|
+
}) }),
|
|
291
|
+
Panel({ title: 'commands', count: commands.length,
|
|
292
|
+
children: Table({ headers: ['name', 'category', 'description'], rows: commands.map(c => [c.name, c.category || '', c.description || '']) }) }),
|
|
293
|
+
Panel({ title: 'active config', children: pre(cfg) }),
|
|
328
294
|
];
|
|
329
295
|
},
|
|
330
|
-
async env(
|
|
331
|
-
const list =
|
|
296
|
+
async env(h0) {
|
|
297
|
+
const list = (typeof h0.pi.env?.list === 'function') ? h0.pi.env.list() : [];
|
|
332
298
|
const setCount = list.filter(k => k.set).length;
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
299
|
+
const chipNodes = list.map(k => h(
|
|
300
|
+
'span',
|
|
301
|
+
{
|
|
302
|
+
key: k.key,
|
|
303
|
+
onclick: () => {
|
|
304
|
+
const v = prompt('set ' + k.key + ' (empty to unset):');
|
|
305
|
+
if (v == null) return;
|
|
306
|
+
if (typeof h0.pi.env.set === 'function') { h0.pi.env.set(k.key, v); rerender(); }
|
|
307
|
+
},
|
|
308
|
+
style: 'cursor:pointer',
|
|
309
|
+
},
|
|
310
|
+
Chip({ tone: k.set ? 'ok' : 'miss', children: k.key + (k.set ? ' ✓' : ' ·') })
|
|
311
|
+
));
|
|
312
|
+
return [
|
|
313
|
+
Kpi({ items: [[setCount, 'set'], [list.length - setCount, 'missing'], [list.length, 'total known']] }),
|
|
314
|
+
Panel({
|
|
315
|
+
title: 'environment variables',
|
|
316
|
+
right: h('span', {}, Chip({ tone: 'ok', children: setCount + ' set' }), ' ', Chip({ tone: 'miss', children: (list.length - setCount) + ' missing' })),
|
|
317
|
+
children: h('div', { style: 'padding:8px 4px;display:flex;flex-wrap:wrap;gap:6px' }, ...chipNodes),
|
|
318
|
+
}),
|
|
319
|
+
];
|
|
320
|
+
},
|
|
321
|
+
async tools(h0) {
|
|
322
|
+
const list = [...h0.pi.tools.values()];
|
|
323
|
+
const byToolset = list.reduce((a, t) => { (a[t.toolset || 'core'] = a[t.toolset || 'core'] || []).push(t); return a; }, {});
|
|
324
|
+
return [
|
|
325
|
+
Kpi({ items: [[list.length, 'tools'], [Object.keys(byToolset).length, 'toolsets']] }),
|
|
326
|
+
...Object.entries(byToolset).map(([ts, items]) => Panel({ title: 'toolset · ' + ts, count: items.length,
|
|
327
|
+
children: items.map(t => Row({ key: t.name, code: '⚒', title: t.name, sub: (t.description || (t.schema && t.schema.description) || '').slice(0, 80) })) })),
|
|
328
|
+
];
|
|
329
|
+
},
|
|
330
|
+
async batch(h0) {
|
|
331
|
+
const out = h('div', { id: 'fd-batch-out' });
|
|
332
|
+
return [
|
|
333
|
+
Section({ title: '// batch runner', children: [
|
|
334
|
+
Panel({ title: 'run prompts', children: form({
|
|
335
|
+
fields: [{ name: 'prompts', kind: 'textarea', placeholder: 'one prompt per line' }, { name: 'concurrency', type: 'number', value: '4' }],
|
|
336
|
+
submit: 'run',
|
|
337
|
+
onSubmit: async (ev) => {
|
|
338
|
+
const prompts = ev.target.elements.prompts.value.split('\n').map(s => s.trim()).filter(Boolean);
|
|
339
|
+
if (!prompts.length) return;
|
|
340
|
+
const node = root.querySelector('#fd-batch-out');
|
|
341
|
+
if (node) node.textContent = 'running…';
|
|
342
|
+
try {
|
|
343
|
+
const r = await h0.pi.batch.run({ prompts, concurrency: Number(ev.target.elements.concurrency.value) || 4 });
|
|
344
|
+
if (node) { node.innerHTML = ''; node.appendChild(document.createTextNode(JSON.stringify(r, null, 2))); }
|
|
345
|
+
} catch (e) { if (node) node.textContent = 'error: ' + (e.message || e); }
|
|
346
|
+
},
|
|
347
|
+
}) }),
|
|
348
|
+
Panel({ title: 'results', children: out }),
|
|
349
|
+
Panel({ title: 'cli usage', children: Receipt({ rows: [
|
|
350
|
+
['run batch file', 'freddie batch prompts.txt'],
|
|
351
|
+
['set concurrency', 'freddie batch prompts.txt --concurrency 8'],
|
|
352
|
+
['jsonl output', 'freddie batch prompts.txt > out.jsonl'],
|
|
353
|
+
] }) }),
|
|
354
|
+
] }),
|
|
355
|
+
];
|
|
356
|
+
},
|
|
357
|
+
async gateway(h0) {
|
|
358
|
+
const platforms = (typeof h0.pi.gateway?.platforms === 'function') ? h0.pi.gateway.platforms() : [];
|
|
359
|
+
const active = platforms.filter(p => p.enabled);
|
|
360
|
+
return [
|
|
361
|
+
Kpi({ items: [[platforms.length, 'platforms'], [active.length, 'active']] }),
|
|
362
|
+
Panel({ title: 'platforms', count: platforms.length,
|
|
363
|
+
right: active.length > 0 ? Chip({ tone: 'ok', children: active.length + ' active' }) : Chip({ tone: 'miss', children: 'none active' }),
|
|
364
|
+
children: platforms.length === 0 ? EmptyState({ text: 'no platforms registered', glyph: '⇌' })
|
|
365
|
+
: platforms.map(p => Row({ key: p.name, code: p.enabled ? '●' : '○', title: p.name, sub: p.note || '', meta: p.enabled ? 'enabled' : '' })) }),
|
|
366
|
+
Panel({ title: 'start gateway', children: Receipt({ rows: [
|
|
367
|
+
['webhook + api_server', 'freddie gateway --port 3000'],
|
|
368
|
+
['specific platform', 'TELEGRAM_BOT_TOKEN=… freddie gateway'],
|
|
369
|
+
['all platforms', 'set env vars per platform, then freddie gateway'],
|
|
370
|
+
] }) }),
|
|
371
|
+
];
|
|
372
|
+
},
|
|
373
|
+
async ['os-instances']() {
|
|
374
|
+
const list = (osSurfaces && osSurfaces.instances && osSurfaces.instances()) || [];
|
|
375
|
+
const activeId = osSurfaces && osSurfaces.activeInstanceId && osSurfaces.activeInstanceId();
|
|
344
376
|
return [
|
|
345
|
-
|
|
346
|
-
|
|
377
|
+
Kpi({ items: [[list.length, 'instances'], [activeId || '—', 'active']] }),
|
|
378
|
+
Panel({ title: 'instances', count: list.length, children: list.length === 0
|
|
379
|
+
? EmptyState({ text: 'no instances', glyph: '◫' })
|
|
380
|
+
: Table({ headers: ['id', 'active', 'shells', 'windows'],
|
|
381
|
+
rows: list.map(i => [i.id, i.id === activeId ? '●' : '', String((i.shells || []).length), String((i.windows || []).length)]) }) }),
|
|
347
382
|
];
|
|
348
383
|
},
|
|
349
|
-
async
|
|
350
|
-
const
|
|
384
|
+
async ['os-windows']() {
|
|
385
|
+
const wins = (osSurfaces && osSurfaces.wm && osSurfaces.wm.list && osSurfaces.wm.list()) || [];
|
|
386
|
+
const focused = osSurfaces && osSurfaces.wm && osSurfaces.wm.focused;
|
|
351
387
|
return [
|
|
352
|
-
|
|
353
|
-
|
|
388
|
+
Kpi({ items: [[wins.length, 'windows'], [focused ? (focused.id || focused.title || '?') : '—', 'focused']] }),
|
|
389
|
+
Panel({ title: 'windows', count: wins.length, children: wins.length === 0
|
|
390
|
+
? EmptyState({ text: 'no windows open', glyph: '▭' })
|
|
391
|
+
: Table({ headers: ['id', 'title', 'min', 'max', 'pos'],
|
|
392
|
+
rows: wins.map(w => [w.id || '?', w.title || '', w.min ? '●' : '', w.max ? '●' : '',
|
|
393
|
+
(w.el ? `${w.el.offsetLeft},${w.el.offsetTop} ${w.el.offsetWidth}×${w.el.offsetHeight}` : '')]) }) }),
|
|
354
394
|
];
|
|
355
395
|
},
|
|
356
|
-
async
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
ev.preventDefault();
|
|
360
|
-
const prompts = ev.target.elements.prompts.value.split('\n').map(s => s.trim()).filter(Boolean);
|
|
361
|
-
if (!prompts.length) return;
|
|
362
|
-
out.textContent = 'running…';
|
|
363
|
-
try { const r = await h.pi.batch.run({ prompts, concurrency: Number(ev.target.elements.conc.value) || 4 });
|
|
364
|
-
out.innerHTML = ''; out.appendChild(pre(r));
|
|
365
|
-
} catch (e) { out.textContent = 'error: ' + (e.message || e); }
|
|
366
|
-
} } });
|
|
367
|
-
const ta = el('textarea', null, { name: 'prompts', rows: '5', placeholder: 'one prompt per line' });
|
|
368
|
-
form.appendChild(ta);
|
|
369
|
-
form.appendChild(el('input', null, { name: 'conc', type: 'number', value: '4' }));
|
|
370
|
-
form.appendChild(el('button', null, { type: 'submit', text: 'run' }));
|
|
396
|
+
async ['os-x']() {
|
|
397
|
+
const x = osSurfaces && osSurfaces.xServer && osSurfaces.xServer();
|
|
398
|
+
if (!x) return [Panel({ title: 'x-server', children: EmptyState({ text: 'x-server not running in this instance', glyph: '✕' }) })];
|
|
371
399
|
return [
|
|
372
|
-
|
|
373
|
-
|
|
400
|
+
Kpi({ items: [[x.windows, 'windows'], [x.pixmaps, 'pixmaps'], [x.gcs, 'gcs'], [x.atoms, 'atoms'], [x.cursors, 'cursors']] }),
|
|
401
|
+
Panel({ title: 'display', children: pre(x) }),
|
|
374
402
|
];
|
|
375
403
|
},
|
|
376
|
-
async
|
|
377
|
-
const
|
|
404
|
+
async ['os-fs']() {
|
|
405
|
+
const list = await instance.fs.list('/');
|
|
378
406
|
return [
|
|
379
|
-
|
|
380
|
-
|
|
407
|
+
Kpi({ items: [[list.length, 'paths'], [instance.id, 'instance']] }),
|
|
408
|
+
Panel({ title: 'paths', count: list.length, children: list.length === 0
|
|
409
|
+
? EmptyState({ text: 'empty fs', glyph: '📁' })
|
|
410
|
+
: pre(list.join('\n')) }),
|
|
381
411
|
];
|
|
382
412
|
},
|
|
383
413
|
};
|
|
384
414
|
|
|
385
|
-
|
|
415
|
+
rerender();
|
|
386
416
|
|
|
387
417
|
if (typeof window !== 'undefined') {
|
|
388
418
|
window.__debug = window.__debug || {};
|
|
389
419
|
window.__debug.instances = window.__debug.instances || {};
|
|
390
420
|
window.__debug.instances[instance.id] = window.__debug.instances[instance.id] || {};
|
|
391
|
-
window.__debug.instances[instance.id].dashboard = {
|
|
421
|
+
window.__debug.instances[instance.id].dashboard = {
|
|
422
|
+
root,
|
|
423
|
+
routes: allRoutes.map(r => r.path),
|
|
424
|
+
setActive,
|
|
425
|
+
get active() { return state.active; },
|
|
426
|
+
};
|
|
392
427
|
}
|
|
393
428
|
|
|
394
429
|
return { node: root, dispose() {} };
|