anentrypoint-design 0.0.461 → 0.0.463

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.461",
3
+ "version": "0.0.463",
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",
@@ -17,7 +17,7 @@
17
17
 
18
18
  import * as webjsx from '../../../vendor/webjsx/index.js';
19
19
  import { makePage, api, loadingState, emptyState } from './runtime.js';
20
- import { Table, PageHeader } from '../content.js';
20
+ import { Table, PageHeader, Select } from '../content.js';
21
21
  import { Chip } from '../shell.js';
22
22
  import { formatTime } from '../../locale.js';
23
23
  import { queueMessage, watchReconnect, isOnline } from '../../idb-outbox.js';
@@ -86,7 +86,37 @@ function applyEnvelope(msgs, env, sendApprove) {
86
86
  }
87
87
 
88
88
  export const chat = makePage((ctx) => {
89
- Object.assign(ctx.state, { loading: false, messages: [], draft: '', busy: false, error: null, sessionId: null, ws: null, conn: 'closed' });
89
+ Object.assign(ctx.state, { loading: false, messages: [], draft: '', busy: false, error: null, sessionId: null, ws: null, conn: 'closed', sessions: [], staged: [] });
90
+
91
+ // Session picker (kimi web's sessions sidebar, compact form): recent
92
+ // conversations from /api/sessions, needsInput badges included. Picking
93
+ // one reconnects the WS under that id and rebuilds from server replay.
94
+ api('/api/sessions').then(rows => { ctx.state.sessions = Array.isArray(rows) ? rows : []; ctx.rerender(); }).catch(() => { /* swallow: picker degrades to new-chat-only */ });
95
+
96
+ // File upload (kimi web parity): files are staged to disk via the gui-agent
97
+ // endpoint and ride the next prompt frame as path references — the agent
98
+ // reads them with its file tools, so no model-capability negotiation here.
99
+ async function attachFiles(fileList) {
100
+ const st = ctx.state;
101
+ if (!st.sessionId) st.sessionId = newSessionId();
102
+ for (const file of fileList || []) {
103
+ try {
104
+ const dataUrl = await new Promise((res, rej) => { const r = new FileReader(); r.onload = () => res(r.result); r.onerror = rej; r.readAsDataURL(file); });
105
+ const base64 = String(dataUrl).split(',')[1] || '';
106
+ const r = await api('/api/sessions/' + encodeURIComponent(st.sessionId) + '/files', { method: 'POST', body: { name: file.name, contentBase64: base64 } });
107
+ if (r && r.path) { st.staged = [...st.staged, { name: r.name || file.name, path: r.path }]; }
108
+ } catch (e) { ctx.set({ error: 'upload failed: ' + (e && e.message || e) }); }
109
+ }
110
+ ctx.rerender();
111
+ }
112
+
113
+ function switchSession(id) {
114
+ const st = ctx.state;
115
+ if (!id || id === st.sessionId) return;
116
+ try { st.ws && st.ws.close(); } catch { /* already closed */ }
117
+ ctx.set({ sessionId: id, messages: [], ws: null, conn: 'closed', busy: false, error: null });
118
+ ensureWs();
119
+ }
90
120
 
91
121
  // Offline outbox: a prompt sent while genuinely offline queues to
92
122
  // IndexedDB and auto-flushes on the real 'online' event via the legacy
@@ -144,6 +174,8 @@ export const chat = makePage((ctx) => {
144
174
  if (f.error && !c.error) c.error = f.error;
145
175
  }
146
176
  ctx.set({ busy: false });
177
+ // New turns can create/rename sessions — refresh the picker.
178
+ api('/api/sessions').then(rows => { ctx.state.sessions = Array.isArray(rows) ? rows : []; ctx.rerender(); }).catch(() => { /* swallow: picker refresh is best-effort */ });
147
179
  } else if (f.type === 'error') {
148
180
  const c = cur();
149
181
  if (c && c.role === 'assistant') c.error = f.error;
@@ -191,11 +223,13 @@ export const chat = makePage((ctx) => {
191
223
  return;
192
224
  }
193
225
 
194
- if (!ensureWs() || !sendFrame({ type: 'prompt', text: t })) {
226
+ if (!ensureWs() || !sendFrame({ type: 'prompt', text: t, attachments: s().staged.map(f => ({ name: f.name, path: f.path })) })) {
195
227
  curMsg.error = 'agent workspace connection unavailable';
196
228
  delete curMsg._live;
197
229
  ctx.set({ busy: false });
230
+ return;
198
231
  }
232
+ ctx.set({ staged: [] });
199
233
  }
200
234
 
201
235
  function stop() {
@@ -208,6 +242,20 @@ export const chat = makePage((ctx) => {
208
242
  return () => {
209
243
  const st = s();
210
244
  return h('div', { class: 'fd-chat' },
245
+ h('div', { class: 'fd-chat-picker' },
246
+ st.sessions.length ? Select({
247
+ value: st.sessionId || '',
248
+ placeholder: 'new conversation',
249
+ 'aria-label': 'switch conversation',
250
+ options: st.sessions.map(row => ({ value: row.id, label: (row.title || '(untitled)').slice(0, 60) + (row.needsInput ? ' — needs input' : '') })),
251
+ onChange: switchSession,
252
+ }) : null,
253
+ h('label', { class: 'fd-chat-attach', title: 'attach files to the next message' },
254
+ 'attach',
255
+ h('input', { type: 'file', multiple: true, style: 'display:none', onchange: (e) => { attachFiles(e.target.files); e.target.value = ''; } })),
256
+ ...st.staged.map((f, i) => h('span', { key: 'st' + i, class: 'fd-chat-staged' },
257
+ f.name,
258
+ h('button', { type: 'button', class: 'fd-chat-staged-x', 'aria-label': 'remove ' + f.name, onclick: () => { st.staged = st.staged.filter((_, j) => j !== i); ctx.rerender(); } }, '×')))),
211
259
  AgentChat({
212
260
  messages: st.messages,
213
261
  busy: st.busy,