anentrypoint-design 0.0.462 → 0.0.464
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/dist/247420.css +63 -0
- package/dist/247420.js +27 -27
- package/package.json +1 -1
- package/src/components/freddie/pages-chat.js +30 -5
- package/src/components/shell/app-shell.js +7 -0
- package/src/css/app-shell/base.css +23 -0
- package/src/css/app-shell/hero-content.css +7 -0
- package/src/css/app-shell/states-interactions.css +34 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anentrypoint-design",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.464",
|
|
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",
|
|
@@ -86,13 +86,30 @@ 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', sessions: [] });
|
|
89
|
+
Object.assign(ctx.state, { loading: false, messages: [], draft: '', busy: false, error: null, sessionId: null, ws: null, conn: 'closed', sessions: [], staged: [] });
|
|
90
90
|
|
|
91
91
|
// Session picker (kimi web's sessions sidebar, compact form): recent
|
|
92
92
|
// conversations from /api/sessions, needsInput badges included. Picking
|
|
93
93
|
// one reconnects the WS under that id and rebuilds from server replay.
|
|
94
94
|
api('/api/sessions').then(rows => { ctx.state.sessions = Array.isArray(rows) ? rows : []; ctx.rerender(); }).catch(() => { /* swallow: picker degrades to new-chat-only */ });
|
|
95
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
|
+
|
|
96
113
|
function switchSession(id) {
|
|
97
114
|
const st = ctx.state;
|
|
98
115
|
if (!id || id === st.sessionId) return;
|
|
@@ -206,11 +223,13 @@ export const chat = makePage((ctx) => {
|
|
|
206
223
|
return;
|
|
207
224
|
}
|
|
208
225
|
|
|
209
|
-
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 })) })) {
|
|
210
227
|
curMsg.error = 'agent workspace connection unavailable';
|
|
211
228
|
delete curMsg._live;
|
|
212
229
|
ctx.set({ busy: false });
|
|
230
|
+
return;
|
|
213
231
|
}
|
|
232
|
+
ctx.set({ staged: [] });
|
|
214
233
|
}
|
|
215
234
|
|
|
216
235
|
function stop() {
|
|
@@ -223,14 +242,20 @@ export const chat = makePage((ctx) => {
|
|
|
223
242
|
return () => {
|
|
224
243
|
const st = s();
|
|
225
244
|
return h('div', { class: 'fd-chat' },
|
|
226
|
-
|
|
227
|
-
Select({
|
|
245
|
+
h('div', { class: 'fd-chat-picker' },
|
|
246
|
+
st.sessions.length ? Select({
|
|
228
247
|
value: st.sessionId || '',
|
|
229
248
|
placeholder: 'new conversation',
|
|
230
249
|
'aria-label': 'switch conversation',
|
|
231
250
|
options: st.sessions.map(row => ({ value: row.id, label: (row.title || '(untitled)').slice(0, 60) + (row.needsInput ? ' — needs input' : '') })),
|
|
232
251
|
onChange: switchSession,
|
|
233
|
-
})
|
|
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(); } }, '×')))),
|
|
234
259
|
AgentChat({
|
|
235
260
|
messages: st.messages,
|
|
236
261
|
busy: st.busy,
|
|
@@ -40,6 +40,13 @@ export function Topbar({ brand = '247420', leaf = '', items = [], active = '', o
|
|
|
40
40
|
if (!String(href).startsWith('http') && onNav) {
|
|
41
41
|
e.preventDefault();
|
|
42
42
|
onNav(cleanLabel);
|
|
43
|
+
// preventDefault above blocks the browser's own
|
|
44
|
+
// anchor-jump, so a #-target must be scrolled to
|
|
45
|
+
// manually or the click looks purely decorative.
|
|
46
|
+
if (String(href).startsWith('#')) {
|
|
47
|
+
const target = document.getElementById(href.slice(1));
|
|
48
|
+
if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
49
|
+
}
|
|
43
50
|
}
|
|
44
51
|
}
|
|
45
52
|
}, label);
|
|
@@ -186,6 +186,29 @@ h3 + .lede, h3 + .t-lede,
|
|
|
186
186
|
margin-top: var(--space-2);
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
/* A lede immediately followed by the article body is a named pairing too --
|
|
190
|
+
same rationale as the heading+lede gap above. Without this, .lede's
|
|
191
|
+
margin:0 leaves zero space above the first paragraph while every
|
|
192
|
+
paragraph-to-paragraph gap below it uses --space-4 (.ds-prose p), so the
|
|
193
|
+
dek reads as glued to the body it introduces. */
|
|
194
|
+
.lede + .post-body, .t-lede + .post-body,
|
|
195
|
+
.lede + .ds-prose, .t-lede + .ds-prose {
|
|
196
|
+
margin-top: var(--space-4);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* Same gap-ownership problem again for .ds-section (hero-content.css):
|
|
200
|
+
`* + .ds-section { margin-top: 0 }` deliberately relies on the PRECEDING
|
|
201
|
+
sibling supplying the lead-in space, so two spacing primitives can
|
|
202
|
+
coordinate into one gap instead of stacking. .lede supplies none (margin:0
|
|
203
|
+
by design), so a lede-then-section pairing (e.g. project_page's h1 -> lede
|
|
204
|
+
-> "install" section) collapses to a flush 0px gap instead of inheriting
|
|
205
|
+
.ds-section's own --space-8 lead. Measured live on project_page: the
|
|
206
|
+
"install" h3 sat ~4px from the lede above it (padding only) vs the
|
|
207
|
+
intended --space-8/2 gap other section-adjacent siblings get. */
|
|
208
|
+
.lede + .ds-section, .t-lede + .ds-section {
|
|
209
|
+
margin-top: var(--space-8);
|
|
210
|
+
}
|
|
211
|
+
|
|
189
212
|
.eyebrow {
|
|
190
213
|
font-family: var(--ff-mono);
|
|
191
214
|
font-size: var(--fs-tiny);
|
|
@@ -299,6 +299,13 @@ table th {
|
|
|
299
299
|
color: var(--fg-3); text-transform: uppercase; letter-spacing: var(--tr-caps);
|
|
300
300
|
background: var(--bg-2);
|
|
301
301
|
}
|
|
302
|
+
/* letter-spacing adds trailing space AFTER the last glyph too, but the table's
|
|
303
|
+
own border-radius+overflow:hidden (above) clips flush to the card edge --
|
|
304
|
+
the last header's positive tracking bleeds past its own padding box and
|
|
305
|
+
gets cropped mid-word ("STATUS" -> "STATI"). Absorb that bleed with a bit
|
|
306
|
+
of extra right padding on the last column only, so no header text sits
|
|
307
|
+
closer to the clip edge than --tr-caps can push it. */
|
|
308
|
+
table th:last-child { padding-right: calc(var(--space-2-75) + var(--tr-caps, 0px)); }
|
|
302
309
|
table td { padding: calc(var(--space-2) * var(--density)) var(--space-2-75); border-top: 1px solid var(--rule); }
|
|
303
310
|
table tr.clickable { cursor: pointer; }
|
|
304
311
|
table tr.clickable:hover td { background: var(--bg-2); }
|
|
@@ -422,6 +422,40 @@ hr.rule-dotted { border-top: 1px dotted var(--rule-strong); }
|
|
|
422
422
|
border-bottom-color: var(--accent-ink);
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
+
/* Switch primitive (form-primitives.js Toggle) -- track + sliding knob.
|
|
426
|
+
Previously unstyled anywhere in this repo (a bare button with no visible
|
|
427
|
+
affordance), so every real consumer resorted to a local literal-bracket-
|
|
428
|
+
text reimplementation ('[x] on'/'[ ] off') instead. Sized off --space
|
|
429
|
+
tokens: a 40x22 track (2x11 knob diameter + 2px inset each side) is the
|
|
430
|
+
standard iOS/Material switch proportion, not an arbitrary literal. */
|
|
431
|
+
.ds-toggle {
|
|
432
|
+
appearance: none; -webkit-appearance: none;
|
|
433
|
+
display: inline-flex; align-items: center; flex-shrink: 0;
|
|
434
|
+
width: 40px; height: 22px; padding: 2px;
|
|
435
|
+
border: 1px solid var(--rule-strong); border-radius: var(--r-pill, 999px);
|
|
436
|
+
background: var(--bg-3); box-shadow: none; cursor: pointer;
|
|
437
|
+
transition: background-color var(--dur-snap) var(--ease), border-color var(--dur-snap) var(--ease);
|
|
438
|
+
}
|
|
439
|
+
.ds-toggle-knob {
|
|
440
|
+
width: 16px; height: 16px; border-radius: 50%;
|
|
441
|
+
background: var(--fg-3);
|
|
442
|
+
transition: transform var(--dur-snap) var(--ease), background-color var(--dur-snap) var(--ease);
|
|
443
|
+
}
|
|
444
|
+
.ds-toggle[aria-checked="true"] {
|
|
445
|
+
background: var(--accent-ink); border-color: var(--accent-ink);
|
|
446
|
+
}
|
|
447
|
+
.ds-toggle[aria-checked="true"] .ds-toggle-knob {
|
|
448
|
+
background: var(--panel-0, #fff);
|
|
449
|
+
transform: translateX(18px);
|
|
450
|
+
}
|
|
451
|
+
.ds-toggle:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
452
|
+
.ds-toggle:focus-visible {
|
|
453
|
+
outline: var(--focus-w) solid var(--focus-color);
|
|
454
|
+
outline-offset: var(--focus-offset);
|
|
455
|
+
}
|
|
456
|
+
.ds-toggle-row { display: inline-flex; align-items: center; gap: var(--space-2); cursor: pointer; }
|
|
457
|
+
.ds-toggle-row .ds-toggle-label { color: inherit; }
|
|
458
|
+
|
|
425
459
|
/* -- Field controls: themed base for TextField / Select / SearchInput --
|
|
426
460
|
Root fix: previously only `transition` was set, so themed apps got
|
|
427
461
|
browser-default white boxes in dark mode and labels collided with inputs
|