@cogenta/theme-kit 0.3.2

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.
Files changed (66) hide show
  1. package/LICENSE +373 -0
  2. package/dist/actions.d.ts +17 -0
  3. package/dist/actions.d.ts.map +1 -0
  4. package/dist/actions.js +43 -0
  5. package/dist/actions.js.map +1 -0
  6. package/dist/archive.d.ts +85 -0
  7. package/dist/archive.d.ts.map +1 -0
  8. package/dist/archive.js +21 -0
  9. package/dist/archive.js.map +1 -0
  10. package/dist/chrome.d.ts +161 -0
  11. package/dist/chrome.d.ts.map +1 -0
  12. package/dist/chrome.js +198 -0
  13. package/dist/chrome.js.map +1 -0
  14. package/dist/comments.d.ts +54 -0
  15. package/dist/comments.d.ts.map +1 -0
  16. package/dist/comments.js +68 -0
  17. package/dist/comments.js.map +1 -0
  18. package/dist/contract.d.ts +97 -0
  19. package/dist/contract.d.ts.map +1 -0
  20. package/dist/contract.js +31 -0
  21. package/dist/contract.js.map +1 -0
  22. package/dist/entry-header.d.ts +62 -0
  23. package/dist/entry-header.d.ts.map +1 -0
  24. package/dist/entry-header.js +65 -0
  25. package/dist/entry-header.js.map +1 -0
  26. package/dist/entry.d.ts +21 -0
  27. package/dist/entry.d.ts.map +1 -0
  28. package/dist/entry.js +65 -0
  29. package/dist/entry.js.map +1 -0
  30. package/dist/heading.d.ts +14 -0
  31. package/dist/heading.d.ts.map +1 -0
  32. package/dist/heading.js +34 -0
  33. package/dist/heading.js.map +1 -0
  34. package/dist/html.d.ts +42 -0
  35. package/dist/html.d.ts.map +1 -0
  36. package/dist/html.js +94 -0
  37. package/dist/html.js.map +1 -0
  38. package/dist/icons.d.ts +37 -0
  39. package/dist/icons.d.ts.map +1 -0
  40. package/dist/icons.js +187 -0
  41. package/dist/icons.js.map +1 -0
  42. package/dist/index.d.ts +27 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +27 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/media.d.ts +45 -0
  47. package/dist/media.d.ts.map +1 -0
  48. package/dist/media.js +70 -0
  49. package/dist/media.js.map +1 -0
  50. package/dist/page.d.ts +104 -0
  51. package/dist/page.d.ts.map +1 -0
  52. package/dist/page.js +134 -0
  53. package/dist/page.js.map +1 -0
  54. package/dist/rich-text.d.ts +5 -0
  55. package/dist/rich-text.d.ts.map +1 -0
  56. package/dist/rich-text.js +125 -0
  57. package/dist/rich-text.js.map +1 -0
  58. package/dist/strings.d.ts +30 -0
  59. package/dist/strings.d.ts.map +1 -0
  60. package/dist/strings.js +124 -0
  61. package/dist/strings.js.map +1 -0
  62. package/dist/theme-toggle.d.ts +69 -0
  63. package/dist/theme-toggle.d.ts.map +1 -0
  64. package/dist/theme-toggle.js +136 -0
  65. package/dist/theme-toggle.js.map +1 -0
  66. package/package.json +43 -0
package/dist/icons.js ADDED
@@ -0,0 +1,187 @@
1
+ import { h } from './html.js';
2
+ /**
3
+ * Inline icons for the block vocabulary's `icon: string` fields (contract B —
4
+ * `featureGrid` items, and any future block that names one the same way).
5
+ *
6
+ * Contract B stores a *symbol name*, never markup (R3) — a block cannot carry
7
+ * its own `<svg>`, so until this module existed every theme rendered
8
+ * `<span data-icon="…">` with nothing inside it: the name reached the page,
9
+ * but nothing visible ever did. This is the shared, zero-dependency mapping
10
+ * from that name to a real icon, written once here rather than five times
11
+ * (one per theme package) the way `renderSocialLinks`/`renderBrandMark`
12
+ * already are for their own corner of a page's chrome.
13
+ *
14
+ * Every path is authored directly for this file — outline style, a single
15
+ * `stroke-width`, no copied icon-library data, no external font or sprite
16
+ * sheet (R9/R10). `renderIcon` returns `null` for a name outside the closed
17
+ * list below, which is a *theme's* signal to fall back to whatever it
18
+ * rendered before this module existed — never a placeholder glyph that would
19
+ * itself need explaining.
20
+ */
21
+ export const ICON_NAMES = [
22
+ 'check',
23
+ 'star',
24
+ 'bolt',
25
+ 'shield',
26
+ 'chart',
27
+ 'users',
28
+ 'user',
29
+ 'globe',
30
+ 'clock',
31
+ 'heart',
32
+ 'leaf',
33
+ 'mail',
34
+ 'phone',
35
+ 'map-pin',
36
+ 'calendar',
37
+ 'book',
38
+ 'code',
39
+ 'cloud',
40
+ 'lock',
41
+ 'search',
42
+ 'settings',
43
+ 'sparkles',
44
+ 'truck',
45
+ 'credit-card',
46
+ 'gift',
47
+ 'coffee',
48
+ 'utensils',
49
+ 'wine',
50
+ 'camera',
51
+ 'pen',
52
+ 'layers',
53
+ 'rocket',
54
+ 'trending-up',
55
+ 'award',
56
+ 'smile',
57
+ 'sun',
58
+ 'moon',
59
+ 'arrow-right',
60
+ 'arrow-up-right',
61
+ 'external-link',
62
+ 'download',
63
+ 'play',
64
+ 'quote',
65
+ 'tag',
66
+ 'briefcase',
67
+ 'home',
68
+ 'message',
69
+ 'bell',
70
+ 'refresh',
71
+ 'image',
72
+ 'zap',
73
+ ];
74
+ const NAME_SET = new Set(ICON_NAMES);
75
+ /** A circle, as two path arcs — `<path>` is the only shape this module emits (see the module comment). */
76
+ function circle(cx, cy, r) {
77
+ return `M${cx - r} ${cy}a${r} ${r} 0 1 0 ${2 * r} 0a${r} ${r} 0 1 0 ${-2 * r} 0`;
78
+ }
79
+ /** One `<path>` per icon, built from the `d` strings below — most icons need only one; a few need two or three distinct strokes. */
80
+ const PATHS = {
81
+ check: ['M4 13l5 5L20 6'],
82
+ star: ['M12 3l2.6 5.6 6.1.6-4.6 4.1 1.3 6-5.4-3.1-5.4 3.1 1.3-6-4.6-4.1 6.1-.6z'],
83
+ bolt: ['M13 2 4 14h6l-1 8 9-12h-6z'],
84
+ shield: ['M12 2 20 5.5v5.5c0 5-3.5 9-8 11-4.5-2-8-6-8-11V5.5z'],
85
+ chart: ['M4 20V12M10 20V7M16 20V4M3 20h18'],
86
+ users: [
87
+ circle(9, 8, 3),
88
+ circle(17.5, 9.5, 2.3),
89
+ 'M2.5 20a6.5 6.5 0 0 1 13 0M14.8 14.5c2.2.4 4 2.3 4.6 5.5',
90
+ ],
91
+ user: [circle(12, 8, 3.5), 'M4.5 20a7.5 7.5 0 0 1 15 0'],
92
+ globe: [circle(12, 12, 9), 'M3 12h18', 'M12 3c-3.3 4-3.3 14 0 18c3.3-4 3.3-14 0-18'],
93
+ clock: [circle(12, 12, 9), 'M12 7v5l3.5 2'],
94
+ heart: [
95
+ 'M12 20S3 14.5 3 8.5C3 5.5 5.2 3.5 8 3.5c1.8 0 3.2 1 4 2.4c.8-1.4 2.2-2.4 4-2.4c2.8 0 5 2 5 5c0 6-9 11.5-9 11.5z',
96
+ ],
97
+ leaf: ['M20 4c-9 0-16 7-16 16c9 0 16-7 16-16z', 'M4 20 12 12'],
98
+ mail: ['M3 5h18v14H3z', 'M4 6l8 7 8-7'],
99
+ phone: [
100
+ 'M5 3h4l2 5-2.5 2.4a12 12 0 0 0 6.1 6.1L17 14l5 2v4a2 2 0 0 1-2.2 2C10.6 21.6 2.4 13.4 2 4.2A2 2 0 0 1 4 2z',
101
+ ],
102
+ 'map-pin': ['M12 22s7-7.4 7-12.8a7 7 0 0 0-14 0C5 14.6 12 22 12 22z', circle(12, 9.2, 2.4)],
103
+ calendar: ['M4 5h16v16H4z', 'M4 10h16', 'M8 3v4', 'M16 3v4'],
104
+ book: ['M4 4h7a3 3 0 0 1 3 3v13a3 3 0 0 0-3-3H4z', 'M20 4h-7a3 3 0 0 0-3 3v13a3 3 0 0 1 3-3h7z'],
105
+ code: ['M8 6 2 12l6 6', 'M16 6l6 6-6 6'],
106
+ cloud: ['M6.5 18a4.2 4.2 0 0 1 .5-8.4A5.6 5.6 0 0 1 17.7 9.9A4.1 4.1 0 0 1 17 18z'],
107
+ lock: ['M5 11h14v10H5z', 'M7.5 11V7a4.5 4.5 0 0 1 9 0v4'],
108
+ search: [circle(11, 11, 7), 'M21 21l-5-5'],
109
+ settings: [circle(12, 12, 3), circle(12, 12, 8)],
110
+ sparkles: [
111
+ 'M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5z',
112
+ 'M19 14l.6 1.8 1.9.6-1.9.6L19 19l-.6-1.8-1.9-.6 1.9-.6z',
113
+ ],
114
+ truck: ['M1 7h13v10H1z', 'M14 10h4l4 3.5V17h-8z', circle(6, 19, 1.8), circle(17.5, 19, 1.8)],
115
+ 'credit-card': ['M2 5h20v14H2z', 'M2 10h20'],
116
+ gift: ['M3 9h18v12H3z', 'M12 9v12', 'M6 9c0-3 4.5-4 6-1c1.5-3 6-2 6 1z'],
117
+ coffee: ['M4 9h12v7a4 4 0 0 1-4 4H8a4 4 0 0 1-4-4z', 'M16 11h1a2.5 2.5 0 0 1 0 5h-1', 'M8 3v5'],
118
+ utensils: [
119
+ 'M6 2v9M4 2v5a2 2 0 0 0 4 0V2M6 11v11',
120
+ 'M18 2c-1.5 3-1.5 6 0 8c0 4-1 12-1 12',
121
+ 'M18 2v8',
122
+ ],
123
+ wine: ['M7 3h10l-1 8a4 4 0 0 1-8 0z', 'M12 15v6', 'M8 21h8'],
124
+ camera: ['M3 8h4l2-3h6l2 3h4v12H3z', circle(12, 14, 4)],
125
+ pen: ['M3 21l4-1 12-12-3-3L4 17z', 'M14 5l3 3'],
126
+ layers: ['M12 3 3 8l9 5 9-5z', 'M3 13l9 5 9-5', 'M3 18l9 5 9-5'],
127
+ rocket: [
128
+ 'M12 2c3.5 1 6 4.5 6 9c0 3-1.5 6-3 8l-3-2-3 2c-1.5-2-3-5-3-8c0-4.5 2.5-8 6-9z',
129
+ 'M9 17l-3 4M15 17l3 4',
130
+ circle(12, 10, 2),
131
+ ],
132
+ 'trending-up': ['M3 17l6-6 4 4 8-9', 'M15 6h6v6'],
133
+ award: [circle(12, 8, 5), 'M9 12.5 7 22l5-3 5 3-2-9.5'],
134
+ smile: [circle(12, 12, 9), 'M8 14c1.2 1.5 2.6 2.2 4 2.2s2.8-.7 4-2.2', 'M8.5 9h.01M15.5 9h.01'],
135
+ sun: [
136
+ circle(12, 12, 4.5),
137
+ 'M12 2v3M12 19v3M4.2 4.2l2.1 2.1M17.7 17.7l2.1 2.1M2 12h3M19 12h3M4.2 19.8l2.1-2.1M17.7 6.3l2.1-2.1',
138
+ ],
139
+ moon: ['M20 14.5A8.5 8.5 0 1 1 9.5 4a7 7 0 0 0 10.5 10.5z'],
140
+ 'arrow-right': ['M4 12h16', 'M13 5l7 7-7 7'],
141
+ 'arrow-up-right': ['M6 18 18 6', 'M8 6h10v10'],
142
+ 'external-link': ['M9 5H5v14h14v-4', 'M13 3h8v8', 'M21 3 11 13'],
143
+ download: ['M12 3v13', 'M6 11l6 6 6-6', 'M4 21h16'],
144
+ play: ['M6 3l16 9-16 9z'],
145
+ quote: [
146
+ 'M4 8c0-3 2-5 5-5v3c-1.5 0-2.5 1-2.5 2.5H9v6H4z',
147
+ 'M13 8c0-3 2-5 5-5v3c-1.5 0-2.5 1-2.5 2.5H18v6h-5z',
148
+ ],
149
+ tag: ['M3 12 12 3h6a3 3 0 0 1 3 3v6l-9 9z', circle(15, 9, 1.6)],
150
+ briefcase: ['M3 8h18v11H3z', 'M8 8V6a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2'],
151
+ home: ['M4 11 12 4l8 7', 'M6 10v10h12V10'],
152
+ message: ['M4 4h16v11H9l-5 4z'],
153
+ bell: ['M6 10a6 6 0 0 1 12 0c0 5 2 6 2 6H4s2-1 2-6z', 'M10 20a2 2 0 0 0 4 0'],
154
+ refresh: ['M4 4v6h6', 'M20 20v-6h-6', 'M4.5 15a8 8 0 0 0 14.6 2.5M19.5 9A8 8 0 0 0 4.9 6.5'],
155
+ image: ['M3 4h18v16H3z', circle(9, 10, 2), 'M3 17l5-5 4 4 5-6 4 5'],
156
+ zap: ['M13 2 4 14h6l-1 8 9-12h-6z'],
157
+ };
158
+ /**
159
+ * One `<svg viewBox="0 0 24 24">` per known name, `null` for anything else —
160
+ * a theme's own fallback (usually the pre-icon `<span data-icon>`, kept for
161
+ * the page builder to key off) applies exactly as it did before this module
162
+ * existed.
163
+ *
164
+ * `aria-hidden`/`focusable="false"`: the icon is always beside a text label
165
+ * (a feature's own title) that already carries the accessible name — an icon
166
+ * that duplicated it would be announced twice.
167
+ */
168
+ export function renderIcon(name, options = {}) {
169
+ if (!NAME_SET.has(name))
170
+ return null;
171
+ const paths = PATHS[name];
172
+ const size = options.size ?? 24;
173
+ return h('svg', {
174
+ class: options.className,
175
+ viewBox: '0 0 24 24',
176
+ width: size,
177
+ height: size,
178
+ fill: 'none',
179
+ stroke: 'currentColor',
180
+ 'stroke-width': 1.75,
181
+ 'stroke-linecap': 'round',
182
+ 'stroke-linejoin': 'round',
183
+ 'aria-hidden': 'true',
184
+ focusable: 'false',
185
+ }, paths.map((d) => h('path', { d })));
186
+ }
187
+ //# sourceMappingURL=icons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons.js","sourceRoot":"","sources":["../src/icons.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * `@cogenta/theme-kit` — the shared contract D primitives every Cogenta theme
3
+ * package implements against.
4
+ *
5
+ * Moved out of `@cogenta/theme-canonical` (which had carried a "temporary
6
+ * home, must be replaced... not duplicated" copy of most of this since L3)
7
+ * the day a second theme package was first written: five independent copies
8
+ * of the same escaping/rich-text/comments logic is exactly the drift that
9
+ * comment warned about, and this package is what makes "one copy, five
10
+ * consumers" true instead.
11
+ */
12
+ export * from './actions.js';
13
+ export * from './archive.js';
14
+ export * from './chrome.js';
15
+ export * from './comments.js';
16
+ export * from './contract.js';
17
+ export * from './entry.js';
18
+ export * from './entry-header.js';
19
+ export * from './heading.js';
20
+ export * from './html.js';
21
+ export * from './icons.js';
22
+ export * from './media.js';
23
+ export * from './page.js';
24
+ export * from './rich-text.js';
25
+ export * from './strings.js';
26
+ export * from './theme-toggle.js';
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,mBAAmB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * `@cogenta/theme-kit` — the shared contract D primitives every Cogenta theme
3
+ * package implements against.
4
+ *
5
+ * Moved out of `@cogenta/theme-canonical` (which had carried a "temporary
6
+ * home, must be replaced... not duplicated" copy of most of this since L3)
7
+ * the day a second theme package was first written: five independent copies
8
+ * of the same escaping/rich-text/comments logic is exactly the drift that
9
+ * comment warned about, and this package is what makes "one copy, five
10
+ * consumers" true instead.
11
+ */
12
+ export * from './actions.js';
13
+ export * from './archive.js';
14
+ export * from './chrome.js';
15
+ export * from './comments.js';
16
+ export * from './contract.js';
17
+ export * from './entry.js';
18
+ export * from './entry-header.js';
19
+ export * from './heading.js';
20
+ export * from './html.js';
21
+ export * from './icons.js';
22
+ export * from './media.js';
23
+ export * from './page.js';
24
+ export * from './rich-text.js';
25
+ export * from './strings.js';
26
+ export * from './theme-toggle.js';
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,mBAAmB,CAAA"}
@@ -0,0 +1,45 @@
1
+ import type { ImageOptions, ImageSource, MediaReference, RenderContext } from './contract.js';
2
+ import { type HtmlElement } from './html.js';
3
+ export interface ImageRenderOptions {
4
+ /** The `sizes` hint. Layout knowledge, so it belongs to the theme, not the block. */
5
+ readonly sizes?: string;
6
+ /** `eager` only for what is above the fold — the hero, and nothing else. */
7
+ readonly loading?: 'lazy' | 'eager';
8
+ readonly className?: string;
9
+ /**
10
+ * An accessible name the *block* provides — a logo's organisation name, for
11
+ * instance. It never invents alt text: it is used only where contract B says
12
+ * the block already carries the name.
13
+ */
14
+ readonly altFrom?: string;
15
+ readonly variant?: ImageOptions;
16
+ }
17
+ /**
18
+ * The image (or video) a theme emits — shared so `alt`/`kind` handling cannot
19
+ * drift between theme packages.
20
+ *
21
+ * `alt` is always written, never omitted: an image with no `alt` attribute is
22
+ * announced by its file name, while `alt=""` is announced as decorative. The
23
+ * difference is the whole of WCAG 1.1.1 here, and it cannot be left to whether
24
+ * a caller remembered.
25
+ *
26
+ * The alt text itself comes from the media entity through `ctx.image` — a
27
+ * theme has no business inventing one, and correcting it in the media library
28
+ * must fix every page at once.
29
+ *
30
+ * `kind: 'video'` (contract D `theme@1.1`) renders a `<video>` with its
31
+ * poster instead of a broken `<img>` — the gap the very first theme built
32
+ * against the pre-1.1 copy of this contract had, closed here once for every
33
+ * theme rather than per theme.
34
+ */
35
+ export declare function image(ctx: RenderContext, media: MediaReference, options?: ImageRenderOptions): HtmlElement;
36
+ /**
37
+ * The same markup `image()` builds, from a source already resolved (by
38
+ * `entryImage`, say) rather than a raw `MediaReference` — for a caller that
39
+ * has an `ImageSource` in hand and no `RenderContext.image` to call again
40
+ * (`renderEntryHeader`'s cover, contract D `theme@1.4`).
41
+ */
42
+ export declare function renderImageSource(source: ImageSource, options?: Omit<ImageRenderOptions, 'variant'>): HtmlElement;
43
+ /** Contract B's framing values, as a CSS `aspect-ratio`. `original` means none. */
44
+ export declare function aspectRatio(ratio: string | undefined): string | undefined;
45
+ //# sourceMappingURL=media.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7F,OAAO,EAAE,KAAK,WAAW,EAAK,MAAM,WAAW,CAAA;AAE/C,MAAM,WAAW,kBAAkB;IACjC,qFAAqF;IACrF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAA;CAChC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,KAAK,CACnB,GAAG,EAAE,aAAa,EAClB,KAAK,EAAE,cAAc,EACrB,OAAO,GAAE,kBAAuB,GAC/B,WAAW,CAEb;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAM,GAChD,WAAW,CAiCb;AAED,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAKzE"}
package/dist/media.js ADDED
@@ -0,0 +1,70 @@
1
+ import { h } from './html.js';
2
+ /**
3
+ * The image (or video) a theme emits — shared so `alt`/`kind` handling cannot
4
+ * drift between theme packages.
5
+ *
6
+ * `alt` is always written, never omitted: an image with no `alt` attribute is
7
+ * announced by its file name, while `alt=""` is announced as decorative. The
8
+ * difference is the whole of WCAG 1.1.1 here, and it cannot be left to whether
9
+ * a caller remembered.
10
+ *
11
+ * The alt text itself comes from the media entity through `ctx.image` — a
12
+ * theme has no business inventing one, and correcting it in the media library
13
+ * must fix every page at once.
14
+ *
15
+ * `kind: 'video'` (contract D `theme@1.1`) renders a `<video>` with its
16
+ * poster instead of a broken `<img>` — the gap the very first theme built
17
+ * against the pre-1.1 copy of this contract had, closed here once for every
18
+ * theme rather than per theme.
19
+ */
20
+ export function image(ctx, media, options = {}) {
21
+ return renderImageSource(ctx.image(media, options.variant), options);
22
+ }
23
+ /**
24
+ * The same markup `image()` builds, from a source already resolved (by
25
+ * `entryImage`, say) rather than a raw `MediaReference` — for a caller that
26
+ * has an `ImageSource` in hand and no `RenderContext.image` to call again
27
+ * (`renderEntryHeader`'s cover, contract D `theme@1.4`).
28
+ */
29
+ export function renderImageSource(source, options = {}) {
30
+ if (source.kind === 'video') {
31
+ return h('video', {
32
+ class: options.className,
33
+ src: source.src,
34
+ poster: source.poster,
35
+ width: source.width,
36
+ height: source.height,
37
+ controls: true,
38
+ preload: 'metadata',
39
+ style: source.focal === null
40
+ ? undefined
41
+ : `object-position:${source.focal.x * 100}% ${source.focal.y * 100}%`,
42
+ });
43
+ }
44
+ return h('img', {
45
+ class: options.className,
46
+ src: source.src,
47
+ srcset: source.srcset === '' ? undefined : source.srcset,
48
+ sizes: options.sizes,
49
+ width: source.width,
50
+ height: source.height,
51
+ alt: source.alt !== '' ? source.alt : (options.altFrom ?? ''),
52
+ loading: options.loading ?? 'lazy',
53
+ decoding: 'async',
54
+ // The focal point is content data, not a style value: it says which part of
55
+ // the picture must survive a crop. Nothing else can carry it per-image.
56
+ style: source.focal === null
57
+ ? undefined
58
+ : `object-position:${source.focal.x * 100}% ${source.focal.y * 100}%`,
59
+ });
60
+ }
61
+ /** Contract B's framing values, as a CSS `aspect-ratio`. `original` means none. */
62
+ export function aspectRatio(ratio) {
63
+ if (ratio === undefined || ratio === 'original')
64
+ return undefined;
65
+ const [width, height] = ratio.split(':');
66
+ if (width === undefined || height === undefined)
67
+ return undefined;
68
+ return `${width} / ${height}`;
69
+ }
70
+ //# sourceMappingURL=media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.js","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,CAAC,EAAE,MAAM,WAAW,CAAA;AAiB/C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,KAAK,CACnB,GAAkB,EAClB,KAAqB,EACrB,OAAO,GAAuB,EAAE;IAEhC,OAAO,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAA;AACtE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAmB,EACnB,OAAO,GAAwC,EAAE;IAEjD,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5B,OAAO,CAAC,CAAC,OAAO,EAAE;YAChB,KAAK,EAAE,OAAO,CAAC,SAAS;YACxB,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,QAAQ,EAAE,IAAI;YACd,OAAO,EAAE,UAAU;YACnB,KAAK,EACH,MAAM,CAAC,KAAK,KAAK,IAAI;gBACnB,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,mBAAmB,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,GAAG;SAC1E,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,CAAC,CAAC,KAAK,EAAE;QACd,KAAK,EAAE,OAAO,CAAC,SAAS;QACxB,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,MAAM,EAAE,MAAM,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM;QACxD,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,GAAG,EAAE,MAAM,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;QAC7D,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,MAAM;QAClC,QAAQ,EAAE,OAAO;QACjB,4EAA4E;QAC5E,wEAAwE;QACxE,KAAK,EACH,MAAM,CAAC,KAAK,KAAK,IAAI;YACnB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,mBAAmB,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,GAAG;KAC1E,CAAC,CAAA;AACJ,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,WAAW,CAAC,KAAyB;IACnD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,UAAU;QAAE,OAAO,SAAS,CAAA;IACjE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACxC,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACjE,OAAO,GAAG,KAAK,MAAM,MAAM,EAAE,CAAA;AAC/B,CAAC"}
package/dist/page.d.ts ADDED
@@ -0,0 +1,104 @@
1
+ import { type BlockRegistry, type BlockVariant, type CollectionListBlock, type UnknownPlacedBlock, type VocabularyBlock } from '@cogenta/blocks';
2
+ import type { ContentEntry, QueryRequest } from './contract.js';
3
+ import type { PageEntryMeta } from './entry-header.js';
4
+ import type { HtmlElement } from './html.js';
5
+ export interface PageContent {
6
+ /** The entry's title. Rendered as the `h1` unless a hero already carries one. */
7
+ readonly title: string;
8
+ readonly blocks: readonly VocabularyBlock[];
9
+ /**
10
+ * The article furniture a `richText`/blocks entry carries beyond its
11
+ * blocks — cover image, byline, date, taxonomy terms, reading time
12
+ * (contract D `theme@1.4`, optional and additive). Absent means the
13
+ * pre-1.4 page: a bare title and nothing else, exactly as before. See
14
+ * `renderEntryHeader` for the one shared way a theme turns this into
15
+ * markup.
16
+ */
17
+ readonly entry?: PageEntryMeta;
18
+ }
19
+ /**
20
+ * Entries already fetched for the `collectionList` blocks on the page, by the
21
+ * block's `_key`. Fetching happens before any markup is built — a renderer
22
+ * that could await would make the number of round trips depend on the
23
+ * markup, which no theme can be trusted to keep constant across edits.
24
+ */
25
+ export type FetchedEntries = Readonly<Record<string, readonly ContentEntry[]>>;
26
+ /**
27
+ * A page has exactly one `h1`.
28
+ *
29
+ * A `hero` declares `headingLevel: 'h1'` and renders the title itself, so a
30
+ * theme's page layout must not render a second one; without a hero, nothing
31
+ * else on the page would — `prose` starts at `h2` — and the page would have
32
+ * no `h1` at all.
33
+ */
34
+ export declare function pageHasOwnHeading(blocks: readonly VocabularyBlock[]): boolean;
35
+ /**
36
+ * Stamps the block's own `_key` onto the element it rendered to.
37
+ *
38
+ * The key is contract B's identity for a placed block — minted once, surviving
39
+ * reorder, translation and version restore — so it is the one honest way for a
40
+ * *reader* of the finished HTML to say "this piece of the page came from that
41
+ * block". The visual page builder (L16) is such a reader: it shows the real
42
+ * server-rendered page in an iframe and needs to map a clicked element back to
43
+ * the block that produced it. Shared across every theme so that mapping works
44
+ * identically regardless of which theme a site has installed.
45
+ *
46
+ * Emitted on every render, never only in a builder mode — a preview that is
47
+ * assembled differently from the published page is exactly the divergence the
48
+ * builder exists to rule out.
49
+ */
50
+ export declare function withBlockKey(element: HtmlElement | null, key: string): HtmlElement | null;
51
+ /**
52
+ * Stamps a placed block's `variant` (`blocks@2.0`, RFC 0002) onto the
53
+ * element it rendered to, as one `data-variant-<axis>` attribute per axis
54
+ * actually set. A theme's CSS then selects `[data-variant-background="muted"]`
55
+ * and resolves it to its own token, exactly the indirection `theme.tokens.json`
56
+ * already uses for colour — no second place a variant value lives.
57
+ *
58
+ * An axis left unset by the author emits no attribute at all, which is what
59
+ * makes "unset" and "this theme's default" the same thing on the render
60
+ * side: a theme that implements no variant at all needs zero code changes,
61
+ * and one that implements only `background` can ignore the rest safely.
62
+ *
63
+ * Applied once, inside every theme's `renderBlock`, rather than by each of
64
+ * the seventeen block renderers — the same shared-envelope reasoning that
65
+ * put `variant` on `BlockIdentity` instead of on each block's own schema.
66
+ */
67
+ export declare function withBlockVariant(element: HtmlElement | null, variant: BlockVariant | undefined): HtmlElement | null;
68
+ /**
69
+ * Resolves any placed block — one of the shared vocabulary, or a block a
70
+ * theme ships of its own — to something a theme's `renderBlock` (an
71
+ * exhaustive `switch` over `VocabularyBlock`, deliberately, so a thirteenth
72
+ * shared type still fails to compile until every theme handles it) can
73
+ * actually take.
74
+ *
75
+ * `BlockRegistry.resolveRenderable` already carries the anti-lock-in half of
76
+ * contract B — a theme's private block must name a `fallback`, walked until
77
+ * something the active theme actually implements is reached — but before
78
+ * this function nothing on the render path ever called it: a stored block
79
+ * whose exact type the active theme did not implement rendered as `null`, a
80
+ * silently blank slot rather than the degraded-but-present block the
81
+ * contract promises (fiche 43, sous-chantier C(ii)).
82
+ *
83
+ * `knownNames` is what the caller's own `renderBlock` switch actually
84
+ * handles — `VOCABULARY_NAMES` for every one of the five in-house themes
85
+ * today, since none of them ships a block of its own yet. A theme (or a
86
+ * theme-shipping plugin) that does passes its own, wider list.
87
+ *
88
+ * Only ever widens what a caller can *pass in*, never what contract B's
89
+ * closed vocabulary is: the fallback's own shape is what gets rendered, and
90
+ * only when the stored data actually validates as an instance of it. A
91
+ * private block earns real, undegraded rendering by an active theme that
92
+ * implements it directly; this is the safety net for every other case, not a
93
+ * data-mapping engine — data that does not fit the fallback's shape yields
94
+ * `null` for that one block, never a thrown error that would take the whole
95
+ * page down with it.
96
+ */
97
+ export declare function resolveBlockForRender(block: VocabularyBlock | UnknownPlacedBlock, knownNames: readonly string[], registry?: BlockRegistry): UnknownPlacedBlock | null;
98
+ /**
99
+ * The `QueryRequest` a `collectionList` block's own fields describe — pure
100
+ * data derived from contract B, never an editorial choice a theme makes, so
101
+ * every theme resolves it identically rather than re-deriving it per layout.
102
+ */
103
+ export declare function buildCollectionListQuery(block: CollectionListBlock): QueryRequest;
104
+ //# sourceMappingURL=page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../src/page.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EAErB,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAE5C,MAAM,WAAW,WAAW;IAC1B,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAA;IAC3C;;;;;;;OAOG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAA;CAC/B;AAED;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAC,CAAC,CAAA;AAE9E;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,GAAG,OAAO,CAE7E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAGzF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,WAAW,GAAG,IAAI,EAC3B,OAAO,EAAE,YAAY,GAAG,SAAS,GAChC,WAAW,GAAG,IAAI,CASpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,eAAe,GAAG,kBAAkB,EAC3C,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,QAAQ,GAAE,aAAkC,GAC3C,kBAAkB,GAAG,IAAI,CAwB3B;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,mBAAmB,GAAG,YAAY,CAQjF"}
package/dist/page.js ADDED
@@ -0,0 +1,134 @@
1
+ import { vocabularyRegistry, } from '@cogenta/blocks';
2
+ /**
3
+ * A page has exactly one `h1`.
4
+ *
5
+ * A `hero` declares `headingLevel: 'h1'` and renders the title itself, so a
6
+ * theme's page layout must not render a second one; without a hero, nothing
7
+ * else on the page would — `prose` starts at `h2` — and the page would have
8
+ * no `h1` at all.
9
+ */
10
+ export function pageHasOwnHeading(blocks) {
11
+ return blocks.some((block) => block._type === 'hero');
12
+ }
13
+ /**
14
+ * Stamps the block's own `_key` onto the element it rendered to.
15
+ *
16
+ * The key is contract B's identity for a placed block — minted once, surviving
17
+ * reorder, translation and version restore — so it is the one honest way for a
18
+ * *reader* of the finished HTML to say "this piece of the page came from that
19
+ * block". The visual page builder (L16) is such a reader: it shows the real
20
+ * server-rendered page in an iframe and needs to map a clicked element back to
21
+ * the block that produced it. Shared across every theme so that mapping works
22
+ * identically regardless of which theme a site has installed.
23
+ *
24
+ * Emitted on every render, never only in a builder mode — a preview that is
25
+ * assembled differently from the published page is exactly the divergence the
26
+ * builder exists to rule out.
27
+ */
28
+ export function withBlockKey(element, key) {
29
+ if (element === null)
30
+ return null;
31
+ return { ...element, attrs: { ...element.attrs, 'data-block-key': key } };
32
+ }
33
+ /**
34
+ * Stamps a placed block's `variant` (`blocks@2.0`, RFC 0002) onto the
35
+ * element it rendered to, as one `data-variant-<axis>` attribute per axis
36
+ * actually set. A theme's CSS then selects `[data-variant-background="muted"]`
37
+ * and resolves it to its own token, exactly the indirection `theme.tokens.json`
38
+ * already uses for colour — no second place a variant value lives.
39
+ *
40
+ * An axis left unset by the author emits no attribute at all, which is what
41
+ * makes "unset" and "this theme's default" the same thing on the render
42
+ * side: a theme that implements no variant at all needs zero code changes,
43
+ * and one that implements only `background` can ignore the rest safely.
44
+ *
45
+ * Applied once, inside every theme's `renderBlock`, rather than by each of
46
+ * the seventeen block renderers — the same shared-envelope reasoning that
47
+ * put `variant` on `BlockIdentity` instead of on each block's own schema.
48
+ */
49
+ export function withBlockVariant(element, variant) {
50
+ if (element === null || variant === undefined)
51
+ return element;
52
+ const attrs = {};
53
+ if (variant.background !== undefined)
54
+ attrs['data-variant-background'] = variant.background;
55
+ if (variant.spacing !== undefined)
56
+ attrs['data-variant-spacing'] = variant.spacing;
57
+ if (variant.align !== undefined)
58
+ attrs['data-variant-align'] = variant.align;
59
+ if (variant.width !== undefined)
60
+ attrs['data-variant-width'] = variant.width;
61
+ if (Object.keys(attrs).length === 0)
62
+ return element;
63
+ return { ...element, attrs: { ...element.attrs, ...attrs } };
64
+ }
65
+ /**
66
+ * Resolves any placed block — one of the shared vocabulary, or a block a
67
+ * theme ships of its own — to something a theme's `renderBlock` (an
68
+ * exhaustive `switch` over `VocabularyBlock`, deliberately, so a thirteenth
69
+ * shared type still fails to compile until every theme handles it) can
70
+ * actually take.
71
+ *
72
+ * `BlockRegistry.resolveRenderable` already carries the anti-lock-in half of
73
+ * contract B — a theme's private block must name a `fallback`, walked until
74
+ * something the active theme actually implements is reached — but before
75
+ * this function nothing on the render path ever called it: a stored block
76
+ * whose exact type the active theme did not implement rendered as `null`, a
77
+ * silently blank slot rather than the degraded-but-present block the
78
+ * contract promises (fiche 43, sous-chantier C(ii)).
79
+ *
80
+ * `knownNames` is what the caller's own `renderBlock` switch actually
81
+ * handles — `VOCABULARY_NAMES` for every one of the five in-house themes
82
+ * today, since none of them ships a block of its own yet. A theme (or a
83
+ * theme-shipping plugin) that does passes its own, wider list.
84
+ *
85
+ * Only ever widens what a caller can *pass in*, never what contract B's
86
+ * closed vocabulary is: the fallback's own shape is what gets rendered, and
87
+ * only when the stored data actually validates as an instance of it. A
88
+ * private block earns real, undegraded rendering by an active theme that
89
+ * implements it directly; this is the safety net for every other case, not a
90
+ * data-mapping engine — data that does not fit the fallback's shape yields
91
+ * `null` for that one block, never a thrown error that would take the whole
92
+ * page down with it.
93
+ */
94
+ export function resolveBlockForRender(block, knownNames, registry = vocabularyRegistry) {
95
+ if (knownNames.includes(block._type)) {
96
+ // A member of the closed `VocabularyBlock` union does not structurally
97
+ // satisfy `UnknownPlacedBlock`'s index signature (each member is built
98
+ // from a mapped type, which TypeScript never treats as implicitly
99
+ // indexable) — the cast states what is already true at the value level:
100
+ // every field of a placed block is `unknown` to a caller this generic.
101
+ return block;
102
+ }
103
+ let definition;
104
+ try {
105
+ definition = registry.resolveRenderable(block._type, knownNames);
106
+ }
107
+ catch {
108
+ // Not registered at all, or its fallback chain loops back on itself —
109
+ // nothing safe to guess. The block is dropped, not the page.
110
+ return null;
111
+ }
112
+ if (definition === undefined)
113
+ return null;
114
+ const candidate = { ...block, _type: definition.name };
115
+ const parsed = definition.validator.safeParse(candidate);
116
+ if (!parsed.success)
117
+ return null;
118
+ return parsed.data;
119
+ }
120
+ /**
121
+ * The `QueryRequest` a `collectionList` block's own fields describe — pure
122
+ * data derived from contract B, never an editorial choice a theme makes, so
123
+ * every theme resolves it identically rather than re-deriving it per layout.
124
+ */
125
+ export function buildCollectionListQuery(block) {
126
+ return {
127
+ collection: block.collection,
128
+ ...(block.filter === undefined ? {} : { filter: block.filter }),
129
+ ...(block.sort === undefined ? {} : { sort: block.sort }),
130
+ // Capped by contract B at 100; an absent limit still must not mean "all".
131
+ limit: block.limit ?? 10,
132
+ };
133
+ }
134
+ //# sourceMappingURL=page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page.js","sourceRoot":"","sources":["../src/page.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { RichTextDocument } from '@cogenta/blocks';
2
+ import type { RenderContext } from './contract.js';
3
+ import { type HtmlNode } from './html.js';
4
+ export declare function renderRichText(ctx: RenderContext, document: RichTextDocument): readonly HtmlNode[];
5
+ //# sourceMappingURL=rich-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rich-text.d.ts","sourceRoot":"","sources":["../src/rich-text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,gBAAgB,EAAsB,MAAM,iBAAiB,CAAA;AAC3F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,KAAK,QAAQ,EAAW,MAAM,WAAW,CAAA;AA6HlD,wBAAgB,cAAc,CAC5B,GAAG,EAAE,aAAa,EAClB,QAAQ,EAAE,gBAAgB,GACzB,SAAS,QAAQ,EAAE,CA0CrB"}