anentrypoint-design 0.0.385 → 0.0.387
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 +33 -0
- package/dist/247420.js +53 -51
- package/package.json +1 -1
- package/src/components/agent-chat/controls.js +143 -0
- package/src/components/agent-chat/empty-state.js +63 -0
- package/src/components/agent-chat/message-rows.js +141 -0
- package/src/components/agent-chat/surface.js +206 -0
- package/src/components/agent-chat/thread-behaviour.js +50 -0
- package/src/components/agent-chat.js +6 -546
- package/src/components/chat/composer-affordances.js +109 -0
- package/src/components/chat/composer.js +256 -0
- package/src/components/chat/threads.js +105 -0
- package/src/components/chat-message-parts/agent-nodes.js +103 -0
- package/src/components/chat-message-parts/inline.js +106 -0
- package/src/components/chat-message-parts/prose-nodes.js +133 -0
- package/src/components/chat-message-parts/renderers.js +89 -0
- package/src/components/chat-message-parts.js +12 -408
- package/src/components/chat-minimap/minimap.js +167 -0
- package/src/components/chat-minimap/paint.js +73 -0
- package/src/components/chat-minimap/preview.js +41 -0
- package/src/components/chat-minimap.js +7 -263
- package/src/components/chat.js +20 -628
- package/src/components/community/chrome.js +63 -0
- package/src/components/community/navigation.js +167 -0
- package/src/components/community/presence.js +106 -0
- package/src/components/community/shell.js +17 -0
- package/src/components/community/views.js +131 -0
- package/src/components/community.js +18 -447
- package/src/components/files/chrome.js +140 -0
- package/src/components/files/entries.js +156 -0
- package/src/components/files/grid-controls.js +63 -0
- package/src/components/files/grid.js +177 -0
- package/src/components/files/types.js +69 -0
- package/src/components/files-modals/dialogs.js +118 -0
- package/src/components/files-modals/modal-shell.js +153 -0
- package/src/components/files-modals/preview-bodies.js +129 -0
- package/src/components/files-modals/preview-containers.js +96 -0
- package/src/components/files-modals.js +14 -475
- package/src/components/files.js +15 -564
- package/src/components/freddie/pages-config.js +3 -94
- package/src/components/freddie/pages-models.js +97 -0
- package/src/components/freddie.js +2 -1
- package/src/components/interaction-primitives/mobile.js +25 -0
- package/src/components/interaction-primitives/pointer.js +214 -0
- package/src/components/interaction-primitives/reorderable.js +47 -0
- package/src/components/interaction-primitives/shortcuts.js +119 -0
- package/src/components/interaction-primitives.js +16 -388
- package/src/components/sessions/conversation-list.js +230 -0
- package/src/components/sessions/dashboard.js +202 -0
- package/src/components/sessions/detail-bits.js +49 -0
- package/src/components/sessions/format.js +42 -0
- package/src/components/sessions/session-card.js +92 -0
- package/src/components/sessions.js +16 -579
- package/src/components/voice/audio-cue.js +39 -0
- package/src/components/voice/capture.js +90 -0
- package/src/components/voice/playback.js +75 -0
- package/src/components/voice/settings-modal.js +104 -0
- package/src/components/voice.js +16 -293
- package/src/css/app-shell/base.css +23 -0
- package/src/css/app-shell/states-interactions.css +10 -0
- package/src/kits/os/freddie/chat-protocol.js +32 -0
- package/src/kits/os/freddie/chat-transport.js +178 -0
- package/src/kits/os/freddie/pages-chat.js +10 -180
- package/src/kits/os/shell-chrome.js +163 -0
- package/src/kits/os/shell-geometry.js +59 -0
- package/src/kits/os/shell.js +178 -335
- package/src/page-html/client-script.js +151 -0
- package/src/page-html/head-tags.js +59 -0
- package/src/page-html/markdown.js +68 -0
- package/src/page-html/page-styles.js +44 -0
- package/src/page-html.js +14 -291
package/src/page-html.js
CHANGED
|
@@ -15,114 +15,18 @@
|
|
|
15
15
|
// theme: 'auto' | 'light' | 'ink',
|
|
16
16
|
// cssHref, headExtra,
|
|
17
17
|
// })
|
|
18
|
+
//
|
|
19
|
+
// This module is a barrel over ./page-html/: the server-side markdown+href
|
|
20
|
+
// helpers, the <head> tag builders, the inline <style> block, and the client
|
|
21
|
+
// mount script string. The public export surface here is unchanged — no
|
|
22
|
+
// consumer import needs to move.
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export function inlineMd(s) {
|
|
25
|
-
return s
|
|
26
|
-
.replace(/`([^`]+)`/g, '<code>$1</code>')
|
|
27
|
-
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
|
|
28
|
-
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// GitHub-flavored heading slug: lowercase, strip non-word/non-space/non-hyphen,
|
|
32
|
-
// collapse whitespace to hyphens. Matches the fallback anchor target a hero/nav
|
|
33
|
-
// CTA's `#slug` href expects to resolve against a `## slug text` body heading.
|
|
34
|
-
export function slugify(s) {
|
|
35
|
-
return String(s || '').trim().toLowerCase()
|
|
36
|
-
.replace(/[^\w\s-]/g, '')
|
|
37
|
-
.replace(/\s+/g, '-');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Raw-HTML passthrough block, opt-in via ```html fences. Distinct from a
|
|
41
|
-
// plain ``` code fence (which still escapes+<pre>-wraps its contents) — this
|
|
42
|
-
// is for SSR call sites that are trusted, repo-authored content (a theme.mjs
|
|
43
|
-
// page body sourced from the project's own YAML, never end-user input) and
|
|
44
|
-
// need to emit real markup (e.g. an <iframe> demo embed) that must NOT be
|
|
45
|
-
// escaped. There is no sanitization here by design: the caller owns trust.
|
|
46
|
-
// A consumer rendering untrusted content must not route it through this
|
|
47
|
-
// path — use markdown.js's DOMPurify-backed renderMarkdown for that instead.
|
|
48
|
-
export function renderMarkdown(md) {
|
|
49
|
-
const lines = String(md || '').split('\n');
|
|
50
|
-
const out = [];
|
|
51
|
-
let inCode = false, inList = false, inRawHtml = false;
|
|
52
|
-
for (const line of lines) {
|
|
53
|
-
if (line.trim() === '```html') { if (!inCode) { inRawHtml = true; continue; } }
|
|
54
|
-
if (line.startsWith('```')) {
|
|
55
|
-
if (inRawHtml) { inRawHtml = false; continue; }
|
|
56
|
-
if (inCode) { out.push('</pre>'); inCode = false; } else { out.push('<pre>'); inCode = true; }
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (inRawHtml) { out.push(line); continue; }
|
|
60
|
-
if (inCode) { out.push(escape(line)); continue; }
|
|
61
|
-
if (line.startsWith('# ')) { const t = line.slice(2); out.push(`<h1 id="${slugify(t)}">${escape(t)}</h1>`); }
|
|
62
|
-
else if (line.startsWith('## ')) { const t = line.slice(3); out.push(`<h2 id="${slugify(t)}">${escape(t)}</h2>`); }
|
|
63
|
-
else if (line.startsWith('### ')) { const t = line.slice(4); out.push(`<h3 id="${slugify(t)}">${escape(t)}</h3>`); }
|
|
64
|
-
else if (line.startsWith('- ')) { if (!inList) { out.push('<ul>'); inList = true; } out.push(`<li>${inlineMd(escape(line.slice(2)))}</li>`); }
|
|
65
|
-
else { if (inList) { out.push('</ul>'); inList = false; } if (line.trim()) out.push(`<p>${inlineMd(escape(line))}</p>`); }
|
|
66
|
-
}
|
|
67
|
-
if (inList) out.push('</ul>');
|
|
68
|
-
if (inCode) out.push('</pre>');
|
|
69
|
-
return out.join('\n');
|
|
70
|
-
}
|
|
24
|
+
import { escape, inlineMd, slugify, renderMarkdown, joinHref } from './page-html/markdown.js';
|
|
25
|
+
import { renderSeoTags, renderFaviconTags, renderCssLink } from './page-html/head-tags.js';
|
|
26
|
+
import { PAGE_INLINE_STYLES } from './page-html/page-styles.js';
|
|
27
|
+
import { CLIENT_SCRIPT } from './page-html/client-script.js';
|
|
71
28
|
|
|
72
|
-
|
|
73
|
-
// through unchanged; leading-slash paths get the prefix.
|
|
74
|
-
function joinHref(basePath, href) {
|
|
75
|
-
if (!href) return '#';
|
|
76
|
-
const h = String(href);
|
|
77
|
-
if (/^([a-z]+:|#|\/\/)/i.test(h)) return h;
|
|
78
|
-
if (!basePath) return h;
|
|
79
|
-
const base = basePath.replace(/\/+$/, '');
|
|
80
|
-
if (h.startsWith('/')) return base + h;
|
|
81
|
-
return base + '/' + h.replace(/^\.?\//, '');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Full SEO/OG/twitter/schema.org meta block, extracted so renderPageHtml
|
|
85
|
-
// consumers (design's own marketing site, thebird's landing) can opt in
|
|
86
|
-
// instead of hand-rolling ~40 lines of <meta> tags per theme.mjs.
|
|
87
|
-
function renderSeoTags({ title, siteName, seo }) {
|
|
88
|
-
const desc = escape(seo.description || '');
|
|
89
|
-
const lang = escape(seo.lang || 'en');
|
|
90
|
-
const url = escape(seo.url || '');
|
|
91
|
-
const image = escape(seo.image || '');
|
|
92
|
-
const author = escape(seo.author || siteName);
|
|
93
|
-
const twitter = escape(seo.twitter || '');
|
|
94
|
-
const locale = escape(seo.locale || 'en_US');
|
|
95
|
-
const keywords = escape(Array.isArray(seo.keywords) ? seo.keywords.join(', ') : (seo.keywords || ''));
|
|
96
|
-
const ogTitle = escape(title);
|
|
97
|
-
const ogDesc = escape(seo.description || siteName);
|
|
98
|
-
let out = `
|
|
99
|
-
<meta name="description" content="${desc}">
|
|
100
|
-
${keywords ? `<meta name="keywords" content="${keywords}">` : ''}
|
|
101
|
-
<meta name="author" content="${author}">
|
|
102
|
-
<meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1">
|
|
103
|
-
<meta name="generator" content="anentrypoint-design">
|
|
104
|
-
${url ? `<link rel="canonical" href="${url}">` : ''}
|
|
105
|
-
<meta property="og:type" content="website">
|
|
106
|
-
<meta property="og:title" content="${ogTitle}">
|
|
107
|
-
<meta property="og:description" content="${ogDesc}">
|
|
108
|
-
${url ? `<meta property="og:url" content="${url}">` : ''}
|
|
109
|
-
<meta property="og:site_name" content="${escape(siteName)}">
|
|
110
|
-
<meta property="og:locale" content="${locale}">
|
|
111
|
-
${image ? `<meta property="og:image" content="${image}">` : ''}
|
|
112
|
-
<meta name="twitter:card" content="summary_large_image">
|
|
113
|
-
<meta name="twitter:title" content="${ogTitle}">
|
|
114
|
-
<meta name="twitter:description" content="${ogDesc}">
|
|
115
|
-
${twitter ? `<meta name="twitter:site" content="${twitter}">` : ''}
|
|
116
|
-
${image ? `<meta name="twitter:image" content="${image}">` : ''}`;
|
|
117
|
-
if (seo.ldJson !== false && (seo.description || url)) {
|
|
118
|
-
const ld = JSON.stringify({
|
|
119
|
-
'@context': 'https://schema.org', '@type': 'WebSite',
|
|
120
|
-
name: title, url: seo.url || '', description: seo.description || '', inLanguage: seo.lang || 'en',
|
|
121
|
-
}).replace(/</g, '\\u003c');
|
|
122
|
-
out += `\n<script type="application/ld+json">${ld}</script>`;
|
|
123
|
-
}
|
|
124
|
-
return out;
|
|
125
|
-
}
|
|
29
|
+
export { escape, inlineMd, slugify, renderMarkdown };
|
|
126
30
|
|
|
127
31
|
export function renderPageHtml({
|
|
128
32
|
title = '247420', slug = 'index', siteName = '247420',
|
|
@@ -147,9 +51,7 @@ export function renderPageHtml({
|
|
|
147
51
|
// instead of @latest (e.g. '0.0.320'); omitted -> default @latest behavior.
|
|
148
52
|
} = {}) {
|
|
149
53
|
const pkgVersion = version || 'latest';
|
|
150
|
-
const cssLink = cssHref
|
|
151
|
-
? `<link rel="stylesheet" href="${cssHref}">`
|
|
152
|
-
: `<link rel="stylesheet" href="https://unpkg.com/anentrypoint-design@${pkgVersion}/dist/247420.css">`;
|
|
54
|
+
const cssLink = renderCssLink({ cssHref, pkgVersion });
|
|
153
55
|
|
|
154
56
|
// Resolve nav hrefs server-side against basePath. Client receives final URLs.
|
|
155
57
|
const navResolved = (Array.isArray(navItems) ? navItems : []).map(([label, href]) =>
|
|
@@ -173,11 +75,7 @@ export function renderPageHtml({
|
|
|
173
75
|
};
|
|
174
76
|
|
|
175
77
|
const seoTags = seo ? renderSeoTags({ title, siteName, seo }) : '';
|
|
176
|
-
const faviconTags = faviconHref
|
|
177
|
-
? `<link rel="icon" href="${escape(faviconHref)}">`
|
|
178
|
-
: (faviconGlyph
|
|
179
|
-
? `<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Ctext y='26' font-size='26'%3E${encodeURIComponent(faviconGlyph)}%3C/text%3E%3C/svg%3E">`
|
|
180
|
-
: '');
|
|
78
|
+
const faviconTags = renderFaviconTags({ faviconHref, faviconGlyph });
|
|
181
79
|
|
|
182
80
|
// Theme attribute co-location is CORRECT here: dist/247420.css keys every
|
|
183
81
|
// theme block off the COMPOUND selector `.ds-247420[data-theme="X"]`
|
|
@@ -201,42 +99,7 @@ ${cssLink}
|
|
|
201
99
|
{ "imports": { "anentrypoint-design": "https://unpkg.com/anentrypoint-design@${pkgVersion}/dist/247420.js" } }
|
|
202
100
|
</script>
|
|
203
101
|
<style>
|
|
204
|
-
|
|
205
|
-
@media (max-width: 768px) { .app-stage { padding: var(--space-4, 24px) var(--space-3, 16px) var(--space-6, 48px); gap: var(--space-5, 32px) } }
|
|
206
|
-
.page-body > :first-child { margin-top: 0 }
|
|
207
|
-
.page-body h1 { margin-top: 0 } .page-body h2 { margin-top: var(--space-5, 32px) } .page-body h3 { margin-top: var(--space-4, 24px) }
|
|
208
|
-
.page-body > * + * { margin-top: var(--space-3, 16px) }
|
|
209
|
-
.page-body pre { margin: var(--space-3, 16px) 0; background: var(--panel-2); padding: var(--space-3, 16px); border-radius: var(--r-1, 10px); overflow-x: auto }
|
|
210
|
-
/* .app-stage owns inter-block rhythm via grid gap; sections/hero must not double it.
|
|
211
|
-
These selectors carry !important because this inline block loads before the
|
|
212
|
-
unpkg CSS bundle, which would otherwise win on load-order for equal specificity. */
|
|
213
|
-
.ds-247420 .app-stage > .ds-hero { margin: 0 !important; padding: var(--space-4, 24px) 0 0 !important; max-width: none !important; gap: var(--space-4, 24px) !important }
|
|
214
|
-
.ds-247420 .app-stage > .ds-section { margin: 0 !important }
|
|
215
|
-
.app-stage .row + .row { margin-top: var(--space-1, 4px) }
|
|
216
|
-
.app-stage .ds-section .row { margin-top: var(--space-2, 8px) }
|
|
217
|
-
.app-stage .ds-section > p.ds-lede { margin: 0 0 var(--space-3, 16px); max-width: var(--measure, 68ch); color: var(--fg-2) }
|
|
218
|
-
.row-benefit { font-style: italic; color: var(--fg-3); font-size: var(--fs-sm); margin-top: var(--space-1, 4px) }
|
|
219
|
-
.ds-row-arrow { margin-left: auto; opacity: .5; transition: opacity var(--dur-snap, 80ms) var(--ease) }
|
|
220
|
-
a.row:hover .ds-row-arrow { opacity: 1 }
|
|
221
|
-
/* hero stat strip — all badges as a wrapping inline rhythm, not one empty panel */
|
|
222
|
-
.ds-hero-stats { display: flex; flex-wrap: wrap; gap: var(--space-3, 16px) var(--space-5, 32px); margin-top: var(--space-2, 8px) }
|
|
223
|
-
.ds-hero-stat { display: flex; align-items: baseline; gap: var(--space-2, 8px) }
|
|
224
|
-
.ds-hero-stat-n { font-family: var(--ff-body); font-weight: 700; font-size: var(--fs-lg, 18px); color: var(--fg) }
|
|
225
|
-
.ds-hero-stat-l { font-size: var(--fs-sm, 15px); color: var(--fg-3) }
|
|
226
|
-
/* accent sits on its own line, muted, so it reads as a distinct aside instead
|
|
227
|
-
of running on from the hero body sentence. */
|
|
228
|
-
.ds-hero-accent { display: block; margin-top: var(--space-2, 8px); color: var(--fg-3) }
|
|
229
|
-
/* feature rows — single-column stack (the dashboard .row grid forces a 3-col
|
|
230
|
-
code/title/meta layout that mangles title+desc+benefit) */
|
|
231
|
-
/* background uses a theme-neutral panel token (resolves per data-theme) so dark
|
|
232
|
-
mode doesn't flash a literal white card before/independent of the bundle.
|
|
233
|
-
Flat tonal fill only — no border-left rail accent (house style: no bespoke
|
|
234
|
-
tile chrome, no shadows, no borders; see ui_kits/gallery/app.js). */
|
|
235
|
-
.ds-feature { padding: var(--space-3, 16px) var(--space-4, 24px); background: var(--panel-1, var(--bg)); border-radius: var(--r-2, 14px); display: grid; gap: var(--space-1, 4px) }
|
|
236
|
-
.ds-feature + .ds-feature { margin-top: var(--space-2, 8px) }
|
|
237
|
-
.ds-feature-title { font-weight: 600; font-size: var(--fs-lg, 18px); color: var(--fg) }
|
|
238
|
-
.ds-feature-desc { font-size: var(--fs-sm, 15px); color: var(--fg-2); line-height: 1.5; overflow-wrap: anywhere }
|
|
239
|
-
.ds-feature-benefit { font-style: italic; font-size: var(--fs-sm, 15px); color: var(--fg-3); margin-top: var(--space-1, 4px) }
|
|
102
|
+
${PAGE_INLINE_STYLES}
|
|
240
103
|
</style>
|
|
241
104
|
<script id="__site__" type="application/json">${JSON.stringify(pageData).replace(/</g, '\\u003c')}</script>
|
|
242
105
|
${headExtra}
|
|
@@ -244,147 +107,7 @@ ${headExtra}
|
|
|
244
107
|
<body>
|
|
245
108
|
<div id="app"></div>
|
|
246
109
|
<script type="module">
|
|
247
|
-
|
|
248
|
-
const data = JSON.parse(document.getElementById('__site__').textContent);
|
|
249
|
-
|
|
250
|
-
function heroNode(hero) {
|
|
251
|
-
if (!hero) return null;
|
|
252
|
-
const badges = Array.isArray(hero.badges) ? hero.badges.filter(Boolean) : [];
|
|
253
|
-
const badgeRow = badges.length
|
|
254
|
-
? h('div', { class: 'ds-hero-stats' }, ...badges.map((b, i) =>
|
|
255
|
-
h('span', { key: i, class: 'ds-hero-stat' },
|
|
256
|
-
h('strong', { class: 'ds-hero-stat-n' }, String(b.label != null ? b.label : b)),
|
|
257
|
-
b.desc ? h('span', { class: 'ds-hero-stat-l' }, String(b.desc)) : null,
|
|
258
|
-
)))
|
|
259
|
-
: null;
|
|
260
|
-
return h('div', { class: 'ds-hero' },
|
|
261
|
-
hero.eyebrow ? h('span', { class: 'eyebrow' }, hero.eyebrow) : null,
|
|
262
|
-
h('h1', { class: 'ds-hero-title' }, hero.heading || hero.title || data.title),
|
|
263
|
-
(hero.body || hero.subheading) ? h('p', { class: 'ds-hero-body' },
|
|
264
|
-
hero.body || hero.subheading,
|
|
265
|
-
hero.accent ? h('span', { class: 'ds-hero-accent' }, ' ' + hero.accent) : null,
|
|
266
|
-
) : null,
|
|
267
|
-
Array.isArray(hero.ctas) && hero.ctas.length
|
|
268
|
-
? h('div', { class: 'ds-hero-actions' }, ...hero.ctas.map((c, i) =>
|
|
269
|
-
h('a', { key: i, class: i === 0 ? 'btn btn-accent' : 'btn btn-ghost', href: c.href || '#' }, c.label || c.cta || 'go')))
|
|
270
|
-
: null,
|
|
271
|
-
badgeRow,
|
|
272
|
-
);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function sectionNode(sec, idx) {
|
|
276
|
-
const features = sec.features || sec.items || [];
|
|
277
|
-
const rows = features.map((f, i) => {
|
|
278
|
-
const kids = [h('div', { key: 't', class: 'ds-feature-title' }, String(f.name || ''))];
|
|
279
|
-
if (f.desc) kids.push(h('div', { key: 'd', class: 'ds-feature-desc', innerHTML: String(f.desc).replace(/\`([^\`]+)\`/g, '<code>$1</code>') }));
|
|
280
|
-
if (f.benefit) kids.push(h('div', { key: 'b', class: 'ds-feature-benefit' }, String(f.benefit)));
|
|
281
|
-
return h('div', { key: i, class: 'ds-feature' }, ...kids);
|
|
282
|
-
});
|
|
283
|
-
return C.Section({
|
|
284
|
-
id: sec.id || null,
|
|
285
|
-
title: sec.name || sec.title || sec.id,
|
|
286
|
-
children: [
|
|
287
|
-
sec.lede ? h('p', { class: 'ds-lede' }, sec.lede) : null,
|
|
288
|
-
...rows,
|
|
289
|
-
sec.body && String(sec.body).trim() ? h('div', { class: 'page-body', innerHTML: __md(sec.body) }) : null,
|
|
290
|
-
].filter(Boolean),
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
function examplesNode(examples) {
|
|
295
|
-
if (!examples || !examples.length) return null;
|
|
296
|
-
return C.Section({
|
|
297
|
-
title: 'explore',
|
|
298
|
-
children: examples.map((e, i) => {
|
|
299
|
-
const kids = [
|
|
300
|
-
h('span', { key: 'c', class: 'code' }, String(i + 1).padStart(2, '0')),
|
|
301
|
-
h('span', { key: 't', class: 'title' }, String(e.label || e.name || e.href || '')),
|
|
302
|
-
];
|
|
303
|
-
if (e.desc) kids.push(h('span', { key: 'm', class: 'meta dim' }, ' — ' + e.desc));
|
|
304
|
-
kids.push(h('span', { key: 'a', class: 'ds-row-arrow' }, '->'));
|
|
305
|
-
return h('a', { key: i, class: 'row', href: e.href || '#' }, ...kids);
|
|
306
|
-
}),
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function panelNode(panel, idx) {
|
|
311
|
-
const items = Array.isArray(panel.items) ? panel.items : [];
|
|
312
|
-
if (!items.length) return null;
|
|
313
|
-
const rows = items.map((it, i) => {
|
|
314
|
-
const kids = [
|
|
315
|
-
h('span', { key: 'c', class: 'code' }, String(it.code || String(i + 1).padStart(2, '0'))),
|
|
316
|
-
h('span', { key: 't', class: 'title' }, String(it.title || it.name || '')),
|
|
317
|
-
];
|
|
318
|
-
if (it.sub || it.desc) kids.push(h('span', { key: 'm', class: 'meta dim' }, ' — ' + (it.sub || it.desc)));
|
|
319
|
-
kids.push(h('span', { key: 'a', class: 'ds-row-arrow' }, it.meta || '->'));
|
|
320
|
-
return h('a', { key: i, class: 'row', href: it.href || '#' }, ...kids);
|
|
321
|
-
});
|
|
322
|
-
return C.Panel({ id: panel.id || null, title: panel.title || panel.name || '', count: panel.count || items.length, children: rows });
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function marqueeNode(marquee) {
|
|
326
|
-
if (!marquee || !Array.isArray(marquee.items) || !marquee.items.length) return null;
|
|
327
|
-
return C.Marquee ? C.Marquee({ items: marquee.items, sep: marquee.sep || '/' }) : null;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function quickstartNode(quickstart) {
|
|
331
|
-
if (!quickstart || !Array.isArray(quickstart.lines) || !quickstart.lines.length) return null;
|
|
332
|
-
const lineNodes = quickstart.lines.map((l, i) => h('div', { key: 'q' + i, class: 'cli' },
|
|
333
|
-
h('span', { class: 'prompt' }, l.kind === 'cmt' ? '#' : '$'),
|
|
334
|
-
h('span', { class: 'cmd' }, l.text)
|
|
335
|
-
));
|
|
336
|
-
return C.Panel({ title: quickstart.heading || 'quick start', children: h('div', { class: 'ds-quickstart' }, ...lineNodes) });
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function sideNode(sidebar) {
|
|
340
|
-
if (!sidebar || !Array.isArray(sidebar.sections) || !sidebar.sections.length || !C.Side) return null;
|
|
341
|
-
return C.Side({ sections: sidebar.sections });
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// minimal client-side markdown renderer matching server-side renderer (idempotent for already-html bodies)
|
|
345
|
-
function __slug(s) { return String(s || '').trim().toLowerCase().replace(/[^\\w\\s-]/g, '').replace(/\\s+/g, '-'); }
|
|
346
|
-
function __md(md) {
|
|
347
|
-
const lines = String(md || '').split('\\n');
|
|
348
|
-
const out = []; let inCode = false, inList = false;
|
|
349
|
-
const esc = (s) => String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
350
|
-
const inl = (s) => s.replace(/\`([^\`]+)\`/g, '<code>$1</code>').replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>').replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href="$2">$1</a>');
|
|
351
|
-
for (const line of lines) {
|
|
352
|
-
if (line.startsWith('\`\`\`')) { if (inCode) { out.push('</pre>'); inCode = false; } else { out.push('<pre>'); inCode = true; } continue; }
|
|
353
|
-
if (inCode) { out.push(esc(line)); continue; }
|
|
354
|
-
if (line.startsWith('# ')) { const t = line.slice(2); out.push('<h1 id="' + __slug(t) + '">' + esc(t) + '</h1>'); }
|
|
355
|
-
else if (line.startsWith('## ')) { const t = line.slice(3); out.push('<h2 id="' + __slug(t) + '">' + esc(t) + '</h2>'); }
|
|
356
|
-
else if (line.startsWith('### ')) { const t = line.slice(4); out.push('<h3 id="' + __slug(t) + '">' + esc(t) + '</h3>'); }
|
|
357
|
-
else if (line.startsWith('- ')) { if (!inList) { out.push('<ul>'); inList = true; } out.push('<li>' + inl(esc(line.slice(2))) + '</li>'); }
|
|
358
|
-
else { if (inList) { out.push('</ul>'); inList = false; } if (line.trim()) out.push('<p>' + inl(esc(line)) + '</p>'); }
|
|
359
|
-
}
|
|
360
|
-
if (inList) out.push('</ul>');
|
|
361
|
-
if (inCode) out.push('</pre>');
|
|
362
|
-
return out.join('\\n');
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
const bodyNode = data.bodyHtml ? C.Section({ children: h('div', { class: 'page-body', innerHTML: data.bodyHtml }) }) : null;
|
|
366
|
-
|
|
367
|
-
const mainChildren = [
|
|
368
|
-
heroNode(data.hero),
|
|
369
|
-
marqueeNode(data.marquee),
|
|
370
|
-
...data.sections.map(sectionNode),
|
|
371
|
-
...(data.panels || []).map(panelNode),
|
|
372
|
-
quickstartNode(data.quickstart),
|
|
373
|
-
examplesNode(data.examples),
|
|
374
|
-
bodyNode,
|
|
375
|
-
].filter(Boolean);
|
|
376
|
-
|
|
377
|
-
mount(document.getElementById('app'), () => C.AppShell({
|
|
378
|
-
topbar: C.Topbar({ brand: data.siteName, items: data.navItems, active: data.title }),
|
|
379
|
-
crumb: C.Crumb({ leaf: data.title }),
|
|
380
|
-
side: sideNode(data.sidebar),
|
|
381
|
-
main: h('div', { class: 'app-stage' }, ...mainChildren),
|
|
382
|
-
status: C.Status({
|
|
383
|
-
left: data.statusLeft || [data.siteName.toLowerCase(), data.slug],
|
|
384
|
-
right: data.statusRight || ['live'],
|
|
385
|
-
}),
|
|
386
|
-
}));
|
|
387
|
-
${clientScriptExtra}
|
|
110
|
+
${CLIENT_SCRIPT}${clientScriptExtra}
|
|
388
111
|
</script>
|
|
389
112
|
</body>
|
|
390
113
|
</html>`;
|