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.
Files changed (202) hide show
  1. package/README.md +253 -253
  2. package/app-shell.css +931 -594
  3. package/colors_and_type.css +226 -226
  4. package/community.css +817 -1222
  5. package/dist/247420.css +2202 -2084
  6. package/dist/247420.js +13 -13
  7. package/package.json +80 -80
  8. package/src/bootstrap.js +25 -25
  9. package/src/components/chat.js +199 -199
  10. package/src/components/community.js +190 -208
  11. package/src/components/content.js +269 -269
  12. package/src/components/editor-primitives.js +100 -0
  13. package/src/components/files-modals.js +107 -107
  14. package/src/components/files.js +118 -118
  15. package/src/components/freddie/helpers.js +50 -50
  16. package/src/components/freddie.js +33 -33
  17. package/src/components/shell.js +117 -117
  18. package/src/components/theme-toggle.js +70 -70
  19. package/src/components.js +59 -57
  20. package/src/debug.js +30 -30
  21. package/src/deck-stage.js +21 -21
  22. package/src/highlight.js +65 -32
  23. package/src/index.js +86 -86
  24. package/src/kits/os/about-app.js +52 -52
  25. package/src/kits/os/app-panes.css +152 -152
  26. package/src/kits/os/browser-app.js +58 -58
  27. package/src/kits/os/files-app.js +44 -44
  28. package/src/kits/os/freddie/helpers.js +59 -59
  29. package/src/kits/os/freddie/pages-chat.js +143 -143
  30. package/src/kits/os/freddie/pages-core.js +101 -101
  31. package/src/kits/os/freddie/pages-os.js +51 -51
  32. package/src/kits/os/freddie/pages-tools.js +183 -183
  33. package/src/kits/os/freddie/routes.js +24 -24
  34. package/src/kits/os/freddie-dashboard.css +51 -51
  35. package/src/kits/os/freddie-dashboard.js +101 -101
  36. package/src/kits/os/icons.js +17 -17
  37. package/src/kits/os/index.js +17 -17
  38. package/src/kits/os/launcher.css +61 -61
  39. package/src/kits/os/launcher.js +79 -79
  40. package/src/kits/os/monitor-app.js +34 -34
  41. package/src/kits/os/shell.js +214 -214
  42. package/src/kits/os/terminal-app.js +45 -45
  43. package/src/kits/os/theme.css +450 -450
  44. package/src/kits/os/validate.css +19 -19
  45. package/src/kits/os/validator-app.js +55 -55
  46. package/src/kits/os/wm.css +115 -115
  47. package/src/kits/os/wm.js +111 -111
  48. package/src/markdown.js +39 -39
  49. package/src/motion.js +35 -35
  50. package/src/page-html.js +196 -196
  51. package/src/styles.js +25 -25
  52. package/src/theme.js +99 -99
  53. package/src/web-components/ds-chat.js +116 -116
  54. package/dist/.nojekyll +0 -0
  55. package/dist/app-shell.css +0 -594
  56. package/dist/colors_and_type.css +0 -197
  57. package/dist/favicon.svg +0 -1
  58. package/dist/index.html +0 -308
  59. package/dist/preview/buttons.html +0 -28
  60. package/dist/preview/colors-core.html +0 -45
  61. package/dist/preview/colors-lore.html +0 -28
  62. package/dist/preview/colors-semantic.html +0 -34
  63. package/dist/preview/dateline.html +0 -19
  64. package/dist/preview/dropzone.html +0 -30
  65. package/dist/preview/file-grid.html +0 -19
  66. package/dist/preview/file-row.html +0 -20
  67. package/dist/preview/file-toolbar.html +0 -40
  68. package/dist/preview/file-viewer.html +0 -31
  69. package/dist/preview/header.html +0 -24
  70. package/dist/preview/icons-unicode.html +0 -26
  71. package/dist/preview/index-row.html +0 -25
  72. package/dist/preview/inputs.html +0 -22
  73. package/dist/preview/manifesto.html +0 -52
  74. package/dist/preview/motion-default.js +0 -106
  75. package/dist/preview/rules.html +0 -16
  76. package/dist/preview/spacing.html +0 -18
  77. package/dist/preview/stamps-lore.html +0 -20
  78. package/dist/preview/stamps.html +0 -14
  79. package/dist/preview/theme-ink.html +0 -15
  80. package/dist/preview/type-display.html +0 -16
  81. package/dist/preview/type-mono.html +0 -15
  82. package/dist/preview/type-prose.html +0 -11
  83. package/dist/preview/type-scale.html +0 -20
  84. package/dist/preview/wordmarks.html +0 -28
  85. package/dist/robots.txt +0 -8
  86. package/dist/site/content/globals/navigation.yaml +0 -5
  87. package/dist/site/content/globals/site.yaml +0 -16
  88. package/dist/site/content/pages/freddie.yaml +0 -88
  89. package/dist/site/content/pages/home.yaml +0 -190
  90. package/dist/site/theme.mjs +0 -368
  91. package/dist/sitemap.xml +0 -31
  92. package/dist/slides/deck-stage-overlay.js +0 -63
  93. package/dist/slides/deck-stage-state.js +0 -81
  94. package/dist/slides/deck-stage-style.js +0 -117
  95. package/dist/slides/deck-stage.js +0 -159
  96. package/dist/slides/index.html +0 -276
  97. package/dist/src/bootstrap.js +0 -25
  98. package/dist/src/components/chat.js +0 -199
  99. package/dist/src/components/community.js +0 -167
  100. package/dist/src/components/content.js +0 -213
  101. package/dist/src/components/files-modals.js +0 -107
  102. package/dist/src/components/files.js +0 -118
  103. package/dist/src/components/freddie/helpers.js +0 -50
  104. package/dist/src/components/freddie.js +0 -33
  105. package/dist/src/components/shell.js +0 -117
  106. package/dist/src/components/theme-toggle.js +0 -70
  107. package/dist/src/components.js +0 -52
  108. package/dist/src/debug.js +0 -30
  109. package/dist/src/deck-stage.js +0 -21
  110. package/dist/src/highlight.js +0 -32
  111. package/dist/src/index.js +0 -86
  112. package/dist/src/kits/os/about-app.js +0 -52
  113. package/dist/src/kits/os/app-panes.css +0 -152
  114. package/dist/src/kits/os/browser-app.js +0 -58
  115. package/dist/src/kits/os/files-app.js +0 -44
  116. package/dist/src/kits/os/freddie/helpers.js +0 -59
  117. package/dist/src/kits/os/freddie/pages-chat.js +0 -143
  118. package/dist/src/kits/os/freddie/pages-core.js +0 -101
  119. package/dist/src/kits/os/freddie/pages-os.js +0 -51
  120. package/dist/src/kits/os/freddie/pages-tools.js +0 -183
  121. package/dist/src/kits/os/freddie/routes.js +0 -24
  122. package/dist/src/kits/os/freddie-dashboard.css +0 -51
  123. package/dist/src/kits/os/freddie-dashboard.js +0 -101
  124. package/dist/src/kits/os/icons.js +0 -17
  125. package/dist/src/kits/os/index.js +0 -5
  126. package/dist/src/kits/os/launcher.css +0 -61
  127. package/dist/src/kits/os/launcher.js +0 -79
  128. package/dist/src/kits/os/monitor-app.js +0 -34
  129. package/dist/src/kits/os/shell.js +0 -214
  130. package/dist/src/kits/os/terminal-app.js +0 -45
  131. package/dist/src/kits/os/theme.css +0 -412
  132. package/dist/src/kits/os/validate.css +0 -19
  133. package/dist/src/kits/os/validator-app.js +0 -55
  134. package/dist/src/kits/os/wm.css +0 -115
  135. package/dist/src/kits/os/wm.js +0 -111
  136. package/dist/src/markdown.js +0 -39
  137. package/dist/src/motion.js +0 -35
  138. package/dist/src/page-html.js +0 -196
  139. package/dist/src/styles.js +0 -25
  140. package/dist/src/theme.js +0 -99
  141. package/dist/src/web-components/ds-chat.js +0 -45
  142. package/dist/ui_kits/aicat/README.md +0 -7
  143. package/dist/ui_kits/aicat/app.js +0 -156
  144. package/dist/ui_kits/aicat/index.html +0 -26
  145. package/dist/ui_kits/aicat/sample-square.png +0 -0
  146. package/dist/ui_kits/aicat/sample-svg.svg +0 -1
  147. package/dist/ui_kits/aicat/sample.pdf +0 -32
  148. package/dist/ui_kits/blog/README.md +0 -3
  149. package/dist/ui_kits/blog/index.html +0 -90
  150. package/dist/ui_kits/chat/README.md +0 -5
  151. package/dist/ui_kits/chat/app.js +0 -110
  152. package/dist/ui_kits/chat/index.html +0 -26
  153. package/dist/ui_kits/chat/sample-square.png +0 -0
  154. package/dist/ui_kits/chat/sample-svg.svg +0 -1
  155. package/dist/ui_kits/chat/sample.pdf +0 -32
  156. package/dist/ui_kits/community/app.js +0 -134
  157. package/dist/ui_kits/community/index.html +0 -24
  158. package/dist/ui_kits/dashboard/app.js +0 -92
  159. package/dist/ui_kits/dashboard/index.html +0 -26
  160. package/dist/ui_kits/docs/README.md +0 -3
  161. package/dist/ui_kits/docs/index.html +0 -123
  162. package/dist/ui_kits/error_404/app.js +0 -56
  163. package/dist/ui_kits/error_404/index.html +0 -26
  164. package/dist/ui_kits/file_browser/README.md +0 -48
  165. package/dist/ui_kits/file_browser/app.js +0 -231
  166. package/dist/ui_kits/file_browser/index.html +0 -33
  167. package/dist/ui_kits/gallery/app.js +0 -121
  168. package/dist/ui_kits/gallery/index.html +0 -26
  169. package/dist/ui_kits/homepage/README.md +0 -7
  170. package/dist/ui_kits/homepage/app.js +0 -167
  171. package/dist/ui_kits/homepage/index.html +0 -46
  172. package/dist/ui_kits/project_page/README.md +0 -3
  173. package/dist/ui_kits/project_page/app.js +0 -154
  174. package/dist/ui_kits/project_page/index.html +0 -45
  175. package/dist/ui_kits/search/app.js +0 -107
  176. package/dist/ui_kits/search/index.html +0 -26
  177. package/dist/ui_kits/settings/app.js +0 -133
  178. package/dist/ui_kits/settings/index.html +0 -26
  179. package/dist/ui_kits/signin/app.js +0 -115
  180. package/dist/ui_kits/signin/index.html +0 -26
  181. package/dist/ui_kits/slide_deck/app.js +0 -174
  182. package/dist/ui_kits/slide_deck/index.html +0 -26
  183. package/dist/ui_kits/system_primer/app.js +0 -152
  184. package/dist/ui_kits/system_primer/index.html +0 -26
  185. package/dist/ui_kits/terminal/app.js +0 -150
  186. package/dist/ui_kits/terminal/index.html +0 -26
  187. package/dist/vendor/webjsx/applyDiff.js +0 -182
  188. package/dist/vendor/webjsx/attributes.js +0 -154
  189. package/dist/vendor/webjsx/constants.js +0 -4
  190. package/dist/vendor/webjsx/createDOMElement.js +0 -52
  191. package/dist/vendor/webjsx/createElement.js +0 -75
  192. package/dist/vendor/webjsx/elementTags.js +0 -115
  193. package/dist/vendor/webjsx/factory.js +0 -6
  194. package/dist/vendor/webjsx/index.js +0 -6
  195. package/dist/vendor/webjsx/jsx-dev-runtime.js +0 -2
  196. package/dist/vendor/webjsx/jsx-runtime.js +0 -30
  197. package/dist/vendor/webjsx/jsx.js +0 -2
  198. package/dist/vendor/webjsx/package.json +0 -39
  199. package/dist/vendor/webjsx/renderSuspension.js +0 -25
  200. package/dist/vendor/webjsx/types.js +0 -5
  201. package/dist/vendor/webjsx/utils.js +0 -84
  202. 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
+ }