anentrypoint-design 0.0.106 → 0.0.108

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.106",
3
+ "version": "0.0.108",
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/page-html.js CHANGED
@@ -1,10 +1,20 @@
1
- // Static-site page HTML renderer. Consumes structured page YAML
2
- // (hero/sections/examples/body) and emits a complete <!doctype html>
3
- // scaffold with rail-coloured sections and SDK styles loaded from unpkg.
4
- // Used by 247420-flavoured doc sites (e.g. flatspace consumers).
5
-
6
- const RAILS = ['rail-green', 'rail-purple', 'rail-mascot', 'rail-sun', 'rail-flame', 'rail-sky'];
7
- const DOTS = ['green', 'purple', 'mascot', 'sun', 'flame', 'sky'];
1
+ // Static-site page HTML renderer. Emits a thin SDK-shell document that mounts
2
+ // the page using the SDK's own kit components (Hero, Section, Panel, Row, etc.)
3
+ // the kit is the single source of truth for visual design; this file only
4
+ // declares the data shape and the mount entry point.
5
+ //
6
+ // Consumer contract:
7
+ // renderPageHtml({
8
+ // title, slug, siteName,
9
+ // navItems: [[label, href], ...], // hrefs are joined with basePath
10
+ // basePath: '/freddie/', // prefix for relative nav hrefs
11
+ // hero: { heading, body, accent, badges, ctas },
12
+ // sections: [{ id, name, lede, body, features: [{name, desc, benefit}] }, ...],
13
+ // examples: [{ label, desc, href }, ...],
14
+ // body: markdown-string,
15
+ // theme: 'auto' | 'light' | 'ink',
16
+ // cssHref, headExtra,
17
+ // })
8
18
 
9
19
  export function escape(s) {
10
20
  return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
@@ -35,66 +45,43 @@ export function renderMarkdown(md) {
35
45
  return out.join('\n');
36
46
  }
37
47
 
38
- function heroBlock(hero, title) {
39
- if (!hero) return '';
40
- const badges = Array.isArray(hero.badges) && hero.badges.length
41
- ? `<div class="ds-hero-badges">${hero.badges.map(b => `<span class="chip"><strong>${escape(b.label || '')}</strong>${b.desc ? `<span class="dim"> ${escape(b.desc)}</span>` : ''}</span>`).join(' ')}</div>` : '';
42
- const ctas = Array.isArray(hero.ctas) && hero.ctas.length
43
- ? `<div class="ds-hero-ctas">${hero.ctas.map((c, i) => `<a class="${i === 0 ? 'btn-primary' : 'btn'}" href="${escape(c.href || '#')}">${escape(c.label || c.cta || 'go')}</a>`).join('')}</div>` : '';
44
- return `<div class="ds-hero">
45
- <h1 class="ds-hero-title">${escape(hero.heading || hero.title || title)}</h1>
46
- ${hero.subheading ? `<p class="ds-hero-body">${escape(hero.subheading)}${hero.accent ? ` <span class="ds-hero-accent">${escape(hero.accent)}</span>` : ''}</p>` : ''}
47
- ${hero.body ? `<p class="ds-hero-body">${escape(hero.body)}</p>` : ''}
48
- ${badges}
49
- ${ctas}
50
- </div>`;
51
- }
52
-
53
- function sectionBlocks(sections) {
54
- return (Array.isArray(sections) ? sections : []).map((sec, idx) => {
55
- const rail = RAILS[idx % RAILS.length];
56
- const dot = DOTS[idx % DOTS.length];
57
- const items = (sec.features || sec.items || []).map((f) => {
58
- const benefit = f.benefit ? `<div class="row-benefit">${escape(f.benefit)}</div>` : '';
59
- return `<div class="row ${rail}">
60
- <span class="dot dot-${dot}" aria-hidden="true"></span>
61
- <span class="title">${escape(f.name)}</span>
62
- ${f.desc ? `<div class="sub">${inlineMd(escape(f.desc))}</div>` : ''}
63
- ${benefit}
64
- </div>`;
65
- }).join('\n');
66
- const lede = sec.lede || (sec.body && sec.body.length < 240 ? sec.body : '');
67
- return `<section class="ds-section ${rail}" id="${escape(sec.id || '')}">
68
- <h2 class="ds-section-title">${escape(sec.name || sec.title || sec.id)}</h2>
69
- ${lede ? `<p class="ds-lede">${escape(lede)}</p>` : ''}
70
- ${items}
71
- ${sec.body && sec.body.length >= 240 ? renderMarkdown(sec.body) : ''}
72
- </section>`;
73
- }).join('\n');
74
- }
75
-
76
- function examplesBlock(examples) {
77
- if (!Array.isArray(examples) || !examples.length) return '';
78
- return `<section class="ds-section">
79
- <h2 class="ds-section-title">explore</h2>
80
- ${examples.map((e, i) => {
81
- const rail = RAILS[(i + 1) % RAILS.length];
82
- return `<a class="row ${rail}" href="${escape(e.href || '#')}">
83
- <span class="code">${String(i + 1).padStart(2, '0')}</span>
84
- <span class="title">${escape(e.label || e.name || e.href)}</span>
85
- ${e.desc ? `<span class="meta dim"> — ${escape(e.desc)}</span>` : ''}
86
- <span class="ds-row-arrow">↗</span>
87
- </a>`;
88
- }).join('\n')}
89
- </section>`;
48
+ // Join a basePath prefix to a nav href. Absolute URLs and hash links pass
49
+ // through unchanged; leading-slash paths get the prefix.
50
+ function joinHref(basePath, href) {
51
+ if (!href) return '#';
52
+ const h = String(href);
53
+ if (/^([a-z]+:|#|\/\/)/i.test(h)) return h;
54
+ if (!basePath) return h;
55
+ const base = basePath.replace(/\/+$/, '');
56
+ if (h.startsWith('/')) return base + h;
57
+ return base + '/' + h.replace(/^\.?\//, '');
90
58
  }
91
59
 
92
- export function renderPageHtml({ title = '247420', slug = 'index', hero, sections, examples, body, navItems = [], siteName = '247420', theme = 'auto', cssHref, headExtra = '' } = {}) {
93
- const main = heroBlock(hero, title) + sectionBlocks(sections) + examplesBlock(examples)
94
- + (body ? `<section class="ds-section page-body">${renderMarkdown(body)}</section>` : '');
60
+ export function renderPageHtml({
61
+ title = '247420', slug = 'index', siteName = '247420',
62
+ navItems = [], basePath = '',
63
+ hero, sections, examples, body,
64
+ theme = 'auto', cssHref, headExtra = ''
65
+ } = {}) {
95
66
  const cssLink = cssHref
96
67
  ? `<link rel="stylesheet" href="${cssHref}">`
97
68
  : `<link rel="stylesheet" href="https://unpkg.com/anentrypoint-design@latest/dist/247420.css">`;
69
+
70
+ // Resolve nav hrefs server-side against basePath. Client receives final URLs.
71
+ const navResolved = (Array.isArray(navItems) ? navItems : []).map(([label, href]) =>
72
+ [label, joinHref(basePath, href)]
73
+ );
74
+
75
+ // Data the client mount needs. Markdown body is parsed server-side into
76
+ // HTML so the client can innerHTML it inside a Section.
77
+ const pageData = {
78
+ title, slug, siteName, navItems: navResolved, theme,
79
+ hero: hero || null,
80
+ sections: Array.isArray(sections) ? sections : [],
81
+ examples: Array.isArray(examples) ? examples : [],
82
+ bodyHtml: body ? renderMarkdown(body) : '',
83
+ };
84
+
98
85
  return `<!doctype html>
99
86
  <html lang="en" class="ds-247420" data-theme="${theme}">
100
87
  <head>
@@ -106,22 +93,102 @@ ${cssLink}
106
93
  { "imports": { "anentrypoint-design": "https://unpkg.com/anentrypoint-design@latest/dist/247420.js" } }
107
94
  </script>
108
95
  <style>
109
- .app-stage { max-width: 1100px; margin: 0 auto; padding: 24px }
96
+ .app-stage { max-width: 1100px; margin: 0 auto; padding: 24px; display: grid; gap: 24px }
110
97
  .page-body h1 { margin-top: 0 } .page-body h2 { margin-top: 32px } .page-body h3 { margin-top: 24px }
111
98
  .page-body pre { margin: 12px 0; background: var(--panel-2); padding: 12px; border-radius: 8px; overflow-x: auto }
112
99
  </style>
100
+ <script id="__site__" type="application/json">${JSON.stringify(pageData).replace(/</g, '\\u003c')}</script>
113
101
  ${headExtra}
114
102
  </head>
115
103
  <body>
116
104
  <div id="app"></div>
117
105
  <script type="module">
118
- import { mount, components as C } from 'anentrypoint-design';
119
- const navItems = ${JSON.stringify(navItems)};
106
+ import { mount, components as C, h } from 'anentrypoint-design';
107
+ const data = JSON.parse(document.getElementById('__site__').textContent);
108
+ const RAILS = ['rail-green', 'rail-purple', 'rail-mascot', 'rail-sun', 'rail-flame', 'rail-sky'];
109
+
110
+ function heroNode(hero) {
111
+ if (!hero) return null;
112
+ return C.Hero({
113
+ eyebrow: hero.eyebrow,
114
+ title: hero.heading || hero.title || data.title,
115
+ body: hero.body || hero.subheading || '',
116
+ accent: hero.accent,
117
+ badge: Array.isArray(hero.badges) && hero.badges[0] ? hero.badges[0].label : undefined,
118
+ actions: Array.isArray(hero.ctas) ? hero.ctas.map((c, i) => h('a', { key: i, class: i === 0 ? 'btn btn-accent' : 'btn btn-ghost', href: c.href || '#' }, c.label || c.cta || 'go')) : null,
119
+ });
120
+ }
121
+
122
+ function sectionNode(sec, idx) {
123
+ const rail = RAILS[idx % RAILS.length];
124
+ const features = sec.features || sec.items || [];
125
+ const rows = features.map((f, i) => {
126
+ const kids = [h('span', { key: 't', class: 'title' }, String(f.name || ''))];
127
+ if (f.desc) kids.push(h('div', { key: 'd', class: 'sub', innerHTML: String(f.desc).replace(/\`([^\`]+)\`/g, '<code>$1</code>') }));
128
+ if (f.benefit) kids.push(h('div', { key: 'b', class: 'row-benefit' }, String(f.benefit)));
129
+ return h('div', { key: i, class: 'row ' + rail }, ...kids);
130
+ });
131
+ return C.Section({
132
+ title: sec.name || sec.title || sec.id,
133
+ children: [
134
+ sec.lede ? h('p', { class: 'ds-lede' }, sec.lede) : null,
135
+ ...rows,
136
+ sec.body && sec.body.length >= 240 ? h('div', { class: 'page-body', innerHTML: __md(sec.body) }) : null,
137
+ ].filter(Boolean),
138
+ });
139
+ }
140
+
141
+ function examplesNode(examples) {
142
+ if (!examples || !examples.length) return null;
143
+ return C.Section({
144
+ title: 'explore',
145
+ children: examples.map((e, i) => {
146
+ const rail = RAILS[(i + 1) % RAILS.length];
147
+ const kids = [
148
+ h('span', { key: 'c', class: 'code' }, String(i + 1).padStart(2, '0')),
149
+ h('span', { key: 't', class: 'title' }, String(e.label || e.name || e.href || '')),
150
+ ];
151
+ if (e.desc) kids.push(h('span', { key: 'm', class: 'meta dim' }, ' — ' + e.desc));
152
+ kids.push(h('span', { key: 'a', class: 'ds-row-arrow' }, '↗'));
153
+ return h('a', { key: i, class: 'row ' + rail, href: e.href || '#' }, ...kids);
154
+ }),
155
+ });
156
+ }
157
+
158
+ // minimal client-side markdown renderer matching server-side renderer (idempotent for already-html bodies)
159
+ function __md(md) {
160
+ const lines = String(md || '').split('\\n');
161
+ const out = []; let inCode = false, inList = false;
162
+ const esc = (s) => String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
163
+ const inl = (s) => s.replace(/\`([^\`]+)\`/g, '<code>$1</code>').replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>').replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href="$2">$1</a>');
164
+ for (const line of lines) {
165
+ if (line.startsWith('\`\`\`')) { if (inCode) { out.push('</pre>'); inCode = false; } else { out.push('<pre>'); inCode = true; } continue; }
166
+ if (inCode) { out.push(esc(line)); continue; }
167
+ if (line.startsWith('# ')) out.push('<h1>' + esc(line.slice(2)) + '</h1>');
168
+ else if (line.startsWith('## ')) out.push('<h2>' + esc(line.slice(3)) + '</h2>');
169
+ else if (line.startsWith('### ')) out.push('<h3>' + esc(line.slice(4)) + '</h3>');
170
+ else if (line.startsWith('- ')) { if (!inList) { out.push('<ul>'); inList = true; } out.push('<li>' + inl(esc(line.slice(2))) + '</li>'); }
171
+ else { if (inList) { out.push('</ul>'); inList = false; } if (line.trim()) out.push('<p>' + inl(esc(line)) + '</p>'); }
172
+ }
173
+ if (inList) out.push('</ul>');
174
+ if (inCode) out.push('</pre>');
175
+ return out.join('\\n');
176
+ }
177
+
178
+ const bodyNode = data.bodyHtml ? C.Section({ children: h('div', { class: 'page-body', innerHTML: data.bodyHtml }) }) : null;
179
+
180
+ const mainChildren = [
181
+ heroNode(data.hero),
182
+ ...data.sections.map(sectionNode),
183
+ examplesNode(data.examples),
184
+ bodyNode,
185
+ ].filter(Boolean);
186
+
120
187
  mount(document.getElementById('app'), () => C.AppShell({
121
- topbar: C.Topbar({ brand: ${JSON.stringify(siteName)}, items: navItems, active: ${JSON.stringify(title)} }),
122
- crumb: C.Crumb({ leaf: ${JSON.stringify(title)} }),
123
- main: C.h('div', { class: 'app-stage', innerHTML: ${JSON.stringify(main)} }),
124
- status: C.Status({ left: [${JSON.stringify(siteName.toLowerCase())}, ${JSON.stringify(slug)}], right: ['live'] })
188
+ topbar: C.Topbar({ brand: data.siteName, items: data.navItems, active: data.title }),
189
+ crumb: C.Crumb({ leaf: data.title }),
190
+ main: h('div', { class: 'app-stage' }, ...mainChildren),
191
+ status: C.Status({ left: [data.siteName.toLowerCase(), data.slug], right: ['live'] }),
125
192
  }));
126
193
  </script>
127
194
  </body>