@bytesbrains/weblocks 0.6.1 → 0.7.0
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/AGENT.md +5 -5
- package/CATALOG.md +66 -2
- package/CHANGELOG.md +68 -0
- package/README.md +54 -5
- package/catalog.json +412 -1
- package/lib/anchors.d.ts +11 -0
- package/lib/anchors.js +77 -0
- package/lib/blocks/booking.d.ts +2 -0
- package/lib/blocks/booking.js +117 -0
- package/lib/blocks/hours.d.ts +2 -0
- package/lib/blocks/hours.js +97 -0
- package/lib/blocks/menu.d.ts +2 -0
- package/lib/blocks/menu.js +93 -0
- package/lib/blocks/nav.js +6 -3
- package/lib/blocks/product.d.ts +2 -0
- package/lib/blocks/product.js +92 -0
- package/lib/blocks/reviews.d.ts +2 -0
- package/lib/blocks/reviews.js +96 -0
- package/lib/generate.d.ts +19 -2
- package/lib/generate.js +43 -3
- package/lib/index.d.ts +4 -2
- package/lib/index.js +3 -1
- package/lib/islands/hours.d.ts +1 -0
- package/lib/islands/hours.js +69 -0
- package/lib/registry.d.ts +6 -0
- package/lib/registry.js +9 -4
- package/lib/render.js +9 -3
- package/lib/schema.d.ts +6 -0
- package/lib/schema.js +15 -0
- package/lib/templates.d.ts +37 -0
- package/lib/templates.js +450 -0
- package/lib/verticals.d.ts +40 -0
- package/lib/verticals.js +117 -0
- package/package.json +2 -1
package/lib/registry.js
CHANGED
|
@@ -12,6 +12,8 @@ import { split } from './blocks/split.js';
|
|
|
12
12
|
import { steps } from './blocks/steps.js';
|
|
13
13
|
import { stats } from './blocks/stats.js';
|
|
14
14
|
import { services } from './blocks/services.js';
|
|
15
|
+
import { menu } from './blocks/menu.js';
|
|
16
|
+
import { product } from './blocks/product.js';
|
|
15
17
|
import { pricing } from './blocks/pricing.js';
|
|
16
18
|
import { logos } from './blocks/logos.js';
|
|
17
19
|
import { team } from './blocks/team.js';
|
|
@@ -24,10 +26,12 @@ import { timeline } from './blocks/timeline.js';
|
|
|
24
26
|
import { tabs } from './blocks/tabs.js';
|
|
25
27
|
import { accordion } from './blocks/accordion.js';
|
|
26
28
|
import { testimonials } from './blocks/testimonials.js';
|
|
29
|
+
import { reviews } from './blocks/reviews.js';
|
|
27
30
|
import { faq } from './blocks/faq.js';
|
|
28
31
|
import { blogList } from './blocks/blogList.js';
|
|
29
32
|
import { blogPost } from './blocks/blogPost.js';
|
|
30
33
|
import { feed } from './blocks/feed.js';
|
|
34
|
+
import { booking } from './blocks/booking.js';
|
|
31
35
|
import { contactForm } from './blocks/contactForm.js';
|
|
32
36
|
import { newsletter } from './blocks/newsletter.js';
|
|
33
37
|
import { search } from './blocks/search.js';
|
|
@@ -35,6 +39,7 @@ import { auth } from './blocks/auth.js';
|
|
|
35
39
|
import { cta } from './blocks/cta.js';
|
|
36
40
|
import { socialLinks } from './blocks/socialLinks.js';
|
|
37
41
|
import { contactDetails } from './blocks/contactDetails.js';
|
|
42
|
+
import { hours } from './blocks/hours.js';
|
|
38
43
|
import { directions } from './blocks/directions.js';
|
|
39
44
|
import { legal } from './blocks/legal.js';
|
|
40
45
|
import { divider } from './blocks/divider.js';
|
|
@@ -54,17 +59,17 @@ const SPECS = [
|
|
|
54
59
|
profileHeader, experience, skills,
|
|
55
60
|
// content
|
|
56
61
|
features, about, richText, split, steps, stats,
|
|
57
|
-
services, pricing, logos, team,
|
|
62
|
+
services, menu, product, pricing, logos, team,
|
|
58
63
|
// media
|
|
59
64
|
gallery, carousel, video, videoGallery, map,
|
|
60
65
|
// structured content
|
|
61
|
-
timeline, tabs, accordion, testimonials, faq,
|
|
66
|
+
timeline, tabs, accordion, testimonials, reviews, faq,
|
|
62
67
|
// collections
|
|
63
68
|
blogList, blogPost, feed,
|
|
64
69
|
// dynamic / powered
|
|
65
|
-
contactForm, newsletter, search, auth,
|
|
70
|
+
booking, contactForm, newsletter, search, auth,
|
|
66
71
|
// conversion / contact
|
|
67
|
-
cta, socialLinks, contactDetails, directions, legal,
|
|
72
|
+
cta, socialLinks, contactDetails, hours, directions, legal,
|
|
68
73
|
// rhythm
|
|
69
74
|
divider, spacer,
|
|
70
75
|
copyright, footer,
|
package/lib/render.js
CHANGED
|
@@ -13,6 +13,7 @@ import { escapeAttr, escapeHtml, parse, sanitizeUrl } from './schema.js';
|
|
|
13
13
|
import { getSpec, needsIsland, REGISTRY } from './registry.js';
|
|
14
14
|
import { normalizeTokens, sectionOverrideCss, tokensToCss } from './tokens.js';
|
|
15
15
|
import { NOOP_RUNTIME } from './runtime.js';
|
|
16
|
+
import { buildAnchors, injectAnchorId } from './anchors.js';
|
|
16
17
|
const RESET_CSS = `
|
|
17
18
|
*,*::before,*::after{box-sizing:border-box}
|
|
18
19
|
body{margin:0;font-family:var(--font);font-size:var(--fs-base);color:var(--text);background:var(--bg);line-height:1.5;-webkit-font-smoothing:antialiased}
|
|
@@ -25,14 +26,18 @@ img{max-width:100%}
|
|
|
25
26
|
// un-stick fixed chrome, and avoid splitting entries across pages.
|
|
26
27
|
const PRINT_CSS = `@media print{*{-webkit-print-color-adjust:exact;print-color-adjust:exact}[data-wl-noprint]{display:none!important}.blk-nav,.blk-app-shell,.blk-announcement-bar,.blk-sidebar{position:static!important}.blk-profile-header,.blk-experience .entry,.blk-skills .group,.blk-timeline li{break-inside:avoid}a[href]{text-decoration:none}@page{margin:1.4cm}}`;
|
|
27
28
|
/** Render one block: normalize its config, then hand markup to the brick. */
|
|
28
|
-
function renderBlock(block, manifest, runtime) {
|
|
29
|
+
function renderBlock(block, manifest, runtime, anchors) {
|
|
29
30
|
const spec = getSpec(block.type);
|
|
30
31
|
if (!spec)
|
|
31
32
|
return ''; // unknown type → skip (closed vocabulary; total)
|
|
32
33
|
const { value } = parse(spec.schema, block.config ?? {});
|
|
33
34
|
const tokens = normalizeTokens(manifest.design);
|
|
34
|
-
const ctx = { id: block.id, runtime };
|
|
35
|
+
const ctx = { id: block.id, runtime, resolveLink: anchors.resolve };
|
|
35
36
|
let html = spec.render(value, tokens, ctx);
|
|
37
|
+
// In-page anchor: emit a stable id on the section so nav links can scroll to it.
|
|
38
|
+
const anchorId = anchors.idFor.get(block.id);
|
|
39
|
+
if (anchorId)
|
|
40
|
+
html = injectAnchorId(html, anchorId);
|
|
36
41
|
// Opt-in per-section overrides: scope palette/radius/spacing as inherited CSS
|
|
37
42
|
// vars on a wrapper (escaped for the style attribute).
|
|
38
43
|
const style = sectionOverrideCss(block.overrides);
|
|
@@ -52,7 +57,8 @@ export function renderSite(manifest, options = {}) {
|
|
|
52
57
|
.filter((s) => usedTypes.has(s.type) && s.css)
|
|
53
58
|
.map((s) => s.css)
|
|
54
59
|
.join('\n');
|
|
55
|
-
const
|
|
60
|
+
const anchors = buildAnchors(blocks);
|
|
61
|
+
const body = blocks.map((b) => renderBlock(b, manifest, runtime, anchors)).filter(Boolean).join('\n');
|
|
56
62
|
// Islands to hydrate. Static-only pages emit none.
|
|
57
63
|
const islands = new Set();
|
|
58
64
|
for (const b of blocks) {
|
package/lib/schema.d.ts
CHANGED
|
@@ -65,3 +65,9 @@ export declare function escapeAttr(s: unknown): string;
|
|
|
65
65
|
* attribute context.
|
|
66
66
|
*/
|
|
67
67
|
export declare function sanitizeUrl(url: unknown): string;
|
|
68
|
+
/**
|
|
69
|
+
* A URL-fragment slug from arbitrary text: `'About Us!'` → `'about-us'`.
|
|
70
|
+
* Diacritics are folded (`résumé` → `resume`). Used for section anchor ids and
|
|
71
|
+
* nav in-page link resolution.
|
|
72
|
+
*/
|
|
73
|
+
export declare function slugify(s: unknown): string;
|
package/lib/schema.js
CHANGED
|
@@ -148,3 +148,18 @@ export function sanitizeUrl(url) {
|
|
|
148
148
|
return '#';
|
|
149
149
|
return s;
|
|
150
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* A URL-fragment slug from arbitrary text: `'About Us!'` → `'about-us'`.
|
|
153
|
+
* Diacritics are folded (`résumé` → `resume`). Used for section anchor ids and
|
|
154
|
+
* nav in-page link resolution.
|
|
155
|
+
*/
|
|
156
|
+
export function slugify(s) {
|
|
157
|
+
return String(s ?? '')
|
|
158
|
+
.normalize('NFKD').replace(/[̀-ͯ]/g, '') // fold accents
|
|
159
|
+
.toLowerCase()
|
|
160
|
+
.replace(/[^a-z0-9\s_-]/g, '') // drop other punctuation
|
|
161
|
+
.trim()
|
|
162
|
+
.replace(/[\s_]+/g, '-') // spaces/underscores → hyphen
|
|
163
|
+
.replace(/-+/g, '-') // collapse
|
|
164
|
+
.replace(/^-+|-+$/g, ''); // trim hyphens
|
|
165
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Named **starter templates** (§ weblocks#31) — complete, valid `SiteManifest`s
|
|
3
|
+
* per vertical, with realistic placeholder copy and a fitting preset baked in.
|
|
4
|
+
*
|
|
5
|
+
* They serve two callers from ONE source of truth: a host can render a template
|
|
6
|
+
* as an instant, zero-LLM starter/preview a user picks from, and generation can
|
|
7
|
+
* seed a template as a scaffold to personalise ("keep this structure, rewrite
|
|
8
|
+
* the copy for THIS business"). Every manifest here passes `validateManifest`
|
|
9
|
+
* (asserted in `templates.test.ts`) and renders to a complete document
|
|
10
|
+
* (`example-templates.ts` writes them all out).
|
|
11
|
+
*
|
|
12
|
+
* Additive + stable: add templates/variants freely; don't rename existing ids.
|
|
13
|
+
* Design tokens come from the vertical's fitting preset (`getPreset`) so a
|
|
14
|
+
* started site is coherent out of the box. Placeholder images use a neutral
|
|
15
|
+
* remote service so a rendered starter looks real; hosts/users swap them.
|
|
16
|
+
*
|
|
17
|
+
* This ships one template per vertical (rollout step 1 of weblocks#31); add
|
|
18
|
+
* 2–3 variants for the high-traffic verticals next.
|
|
19
|
+
*/
|
|
20
|
+
import type { SiteManifest } from './types.js';
|
|
21
|
+
export interface Template {
|
|
22
|
+
/** Stable id, e.g. `restaurant-modern`. */
|
|
23
|
+
id: string;
|
|
24
|
+
/** A `verticalNames()` value this template belongs to. */
|
|
25
|
+
vertical: string;
|
|
26
|
+
/** Human label for a picker. */
|
|
27
|
+
label: string;
|
|
28
|
+
/** Complete, `validateManifest`-clean manifest with realistic copy. */
|
|
29
|
+
manifest: SiteManifest;
|
|
30
|
+
}
|
|
31
|
+
export declare const TEMPLATES: Readonly<Record<string, Template>>;
|
|
32
|
+
/** All template ids. */
|
|
33
|
+
export declare function templateNames(): string[];
|
|
34
|
+
/** Templates for a vertical id (may be empty until more are authored). */
|
|
35
|
+
export declare function templatesForVertical(vertical: string): Template[];
|
|
36
|
+
/** A template by id, or `undefined`. */
|
|
37
|
+
export declare function getTemplate(id: string): Template | undefined;
|