anentrypoint-design 0.0.26 → 0.0.28
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.app.js +4 -3
- package/dist/247420.css +213 -7
- package/dist/247420.js +6 -5
- package/package.json +1 -1
- package/src/components.js +140 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anentrypoint-design",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.28",
|
|
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",
|
package/src/components.js
CHANGED
|
@@ -278,23 +278,154 @@ export function HomeView({ state, onNav, onToggleWork, works, posts, manifesto,
|
|
|
278
278
|
|
|
279
279
|
// ---------- chat ----------
|
|
280
280
|
|
|
281
|
-
|
|
281
|
+
function fmtBytes(n) {
|
|
282
|
+
if (n == null) return '';
|
|
283
|
+
if (n < 1024) return n + ' B';
|
|
284
|
+
if (n < 1024 * 1024) return (n / 1024).toFixed(1) + ' KB';
|
|
285
|
+
if (n < 1024 * 1024 * 1024) return (n / (1024 * 1024)).toFixed(1) + ' MB';
|
|
286
|
+
return (n / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function renderInline(text) {
|
|
290
|
+
if (text == null) return [];
|
|
291
|
+
const out = [];
|
|
292
|
+
const re = /(\*\*([^*]+)\*\*|\*([^*]+)\*|`([^`]+)`|\[([^\]]+)\]\(([^)]+)\))/g;
|
|
293
|
+
let last = 0;
|
|
294
|
+
let m;
|
|
295
|
+
let i = 0;
|
|
296
|
+
const push = (node) => out.push(node);
|
|
297
|
+
while ((m = re.exec(text)) !== null) {
|
|
298
|
+
if (m.index > last) push(h('span', { key: 's' + i + 'a' }, text.slice(last, m.index)));
|
|
299
|
+
if (m[2] != null) push(h('strong', { key: 's' + i }, m[2]));
|
|
300
|
+
else if (m[3] != null) push(h('em', { key: 's' + i }, m[3]));
|
|
301
|
+
else if (m[4] != null) push(h('code', { key: 's' + i, class: 'chat-tick' }, m[4]));
|
|
302
|
+
else if (m[5] != null) push(h('a', { key: 's' + i, href: m[6], target: '_blank', rel: 'noopener' }, m[5]));
|
|
303
|
+
last = m.index + m[0].length;
|
|
304
|
+
i += 1;
|
|
305
|
+
}
|
|
306
|
+
if (last < text.length) push(h('span', { key: 's' + i + 'a' }, text.slice(last)));
|
|
307
|
+
return out;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function renderMd(text) {
|
|
311
|
+
// markdown-lite block: blank line = paragraph, line starting with "- " = bullet.
|
|
312
|
+
const lines = String(text || '').split('\n');
|
|
313
|
+
const blocks = [];
|
|
314
|
+
let para = [];
|
|
315
|
+
let bullets = [];
|
|
316
|
+
const flush = () => {
|
|
317
|
+
if (para.length) { blocks.push({ kind: 'p', text: para.join(' ') }); para = []; }
|
|
318
|
+
if (bullets.length) { blocks.push({ kind: 'ul', items: bullets.slice() }); bullets = []; }
|
|
319
|
+
};
|
|
320
|
+
lines.forEach((raw) => {
|
|
321
|
+
const ln = raw.trim();
|
|
322
|
+
if (!ln) { flush(); return; }
|
|
323
|
+
if (/^[-*]\s+/.test(ln)) { if (para.length) flush(); bullets.push(ln.replace(/^[-*]\s+/, '')); return; }
|
|
324
|
+
if (bullets.length) flush();
|
|
325
|
+
para.push(ln);
|
|
326
|
+
});
|
|
327
|
+
flush();
|
|
328
|
+
return blocks.map((b, i) =>
|
|
329
|
+
b.kind === 'p'
|
|
330
|
+
? h('p', { key: 'b' + i }, ...renderInline(b.text))
|
|
331
|
+
: h('ul', { key: 'b' + i }, ...b.items.map((it, j) => h('li', { key: 'l' + j }, ...renderInline(it))))
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const FILE_GLYPHS = { pdf: '▤', zip: '▦', tar: '▦', gz: '▦', mp4: '▶', mp3: '♪', wav: '♪', csv: '⊞', json: '{}', md: '§', txt: '§', default: '◫' };
|
|
336
|
+
function fileGlyph(name) {
|
|
337
|
+
const ext = String(name || '').split('.').pop().toLowerCase();
|
|
338
|
+
return FILE_GLYPHS[ext] || FILE_GLYPHS.default;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const PART_RENDERERS = {
|
|
342
|
+
text: (p) => h('div', { class: 'chat-bubble' }, ...renderInline(p.text || '')),
|
|
343
|
+
md: (p) => h('div', { class: 'chat-bubble chat-md' }, ...renderMd(p.text || '')),
|
|
344
|
+
code: (p) => h('div', { class: 'chat-bubble chat-code' },
|
|
345
|
+
h('div', { class: 'chat-code-head' },
|
|
346
|
+
h('span', { class: 'lang' }, p.lang || 'code'),
|
|
347
|
+
p.filename ? h('span', { class: 'name' }, p.filename) : null
|
|
348
|
+
),
|
|
349
|
+
h('pre', {}, h('code', { class: p.lang ? 'lang-' + p.lang : '' }, p.code || ''))
|
|
350
|
+
),
|
|
351
|
+
image: (p) => h('a', { class: 'chat-image', href: p.href || p.src, target: '_blank', rel: 'noopener' },
|
|
352
|
+
h('img', { src: p.src, alt: p.alt || '', loading: 'lazy' }),
|
|
353
|
+
p.caption ? h('span', { class: 'cap' }, p.caption) : null
|
|
354
|
+
),
|
|
355
|
+
pdf: (p) => h('div', { class: 'chat-pdf' },
|
|
356
|
+
h('div', { class: 'chat-pdf-head' },
|
|
357
|
+
h('span', { class: 'glyph' }, '▤'),
|
|
358
|
+
h('span', { class: 'name' }, p.name || 'document.pdf'),
|
|
359
|
+
p.size != null ? h('span', { class: 'size' }, fmtBytes(p.size)) : null,
|
|
360
|
+
h('a', { class: 'open', href: p.src, target: '_blank', rel: 'noopener' }, 'open ↗')
|
|
361
|
+
),
|
|
362
|
+
h('embed', { src: p.src, type: 'application/pdf' })
|
|
363
|
+
),
|
|
364
|
+
file: (p) => h('a', { class: 'chat-file', href: p.src, target: '_blank', rel: 'noopener', download: p.name || true },
|
|
365
|
+
h('span', { class: 'glyph' }, fileGlyph(p.name)),
|
|
366
|
+
h('span', { class: 'meta' },
|
|
367
|
+
h('span', { class: 'name' }, p.name || 'attachment'),
|
|
368
|
+
h('span', { class: 'size' }, [p.kindLabel || (p.name || '').split('.').pop().toUpperCase(), p.size != null ? fmtBytes(p.size) : null].filter(Boolean).join(' · '))
|
|
369
|
+
),
|
|
370
|
+
h('span', { class: 'go' }, '↓')
|
|
371
|
+
),
|
|
372
|
+
link: (p) => h('a', { class: 'chat-link', href: p.href, target: '_blank', rel: 'noopener' },
|
|
373
|
+
p.thumb ? h('img', { class: 'thumb', src: p.thumb, alt: '' }) : null,
|
|
374
|
+
h('span', { class: 'meta' },
|
|
375
|
+
h('span', { class: 'host' }, p.host || (() => { try { return new URL(p.href).host; } catch { return ''; } })()),
|
|
376
|
+
h('span', { class: 'title' }, p.title || p.href),
|
|
377
|
+
p.desc ? h('span', { class: 'desc' }, p.desc) : null
|
|
378
|
+
)
|
|
379
|
+
)
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
function renderPart(p, key) {
|
|
383
|
+
const fn = PART_RENDERERS[p.kind] || PART_RENDERERS.text;
|
|
384
|
+
const node = fn(p);
|
|
385
|
+
if (node && typeof node === 'object') node.props = { ...(node.props || {}), key: 'p' + key };
|
|
386
|
+
return node;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export function ChatMessage({ who = 'them', avatar, text, parts, time, typing, key, aicat, reactions, receipt, name }) {
|
|
282
390
|
const cls = 'chat-msg ' + who + (aicat && who === 'them' ? ' aicat' : '');
|
|
283
391
|
const av = h('span', { class: 'chat-avatar' }, avatar || (who === 'you' ? 'u' : '?'));
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
392
|
+
|
|
393
|
+
let bodyNodes;
|
|
394
|
+
if (typing) {
|
|
395
|
+
bodyNodes = [h('span', { class: 'chat-typing', key: 'typ' }, h('span'), h('span'), h('span'))];
|
|
396
|
+
} else if (parts && parts.length) {
|
|
397
|
+
bodyNodes = parts.map((p, i) => renderPart(p, i));
|
|
398
|
+
} else {
|
|
399
|
+
bodyNodes = [h('div', { class: 'chat-bubble', key: 't' }, ...renderInline(text || ''))];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const reactionRow = reactions && reactions.length
|
|
403
|
+
? h('div', { class: 'chat-reactions' },
|
|
404
|
+
...reactions.map((r, i) => h('span', { class: 'rxn' + (r.you ? ' you' : ''), key: 'r' + i },
|
|
405
|
+
h('span', { class: 'e' }, r.emoji),
|
|
406
|
+
h('span', { class: 'n' }, String(r.count))
|
|
407
|
+
)))
|
|
408
|
+
: null;
|
|
409
|
+
|
|
410
|
+
const receiptMark = who === 'you' && receipt
|
|
411
|
+
? h('span', { class: 'tick' + (receipt === 'read' ? ' read' : '') }, receipt === 'read' ? '✓✓' : '✓')
|
|
412
|
+
: null;
|
|
413
|
+
|
|
414
|
+
const metaItems = [];
|
|
415
|
+
if (name && who === 'them') metaItems.push(h('span', { class: 'who', key: 'w' }, name));
|
|
416
|
+
if (time) metaItems.push(h('span', { class: 't', key: 'ti' }, time));
|
|
417
|
+
if (receiptMark) metaItems.push(receiptMark);
|
|
418
|
+
const meta = metaItems.length ? h('div', { class: 'chat-meta' }, ...metaItems) : null;
|
|
419
|
+
|
|
420
|
+
const stack = h('div', { class: 'chat-stack' }, ...bodyNodes, reactionRow, meta);
|
|
292
421
|
return h('div', { key, class: cls },
|
|
293
422
|
who === 'you' ? stack : av,
|
|
294
423
|
who === 'you' ? av : stack
|
|
295
424
|
);
|
|
296
425
|
}
|
|
297
426
|
|
|
427
|
+
export { renderInline, renderMd, fmtBytes };
|
|
428
|
+
|
|
298
429
|
export function ChatComposer({ value, onInput, onSend, placeholder = 'message…', disabled }) {
|
|
299
430
|
const send = () => {
|
|
300
431
|
const v = (value || '').trim();
|