@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/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 body = blocks.map((b) => renderBlock(b, manifest, runtime)).filter(Boolean).join('\n');
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;