@refrakt-md/cli 0.21.0 → 0.23.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.
@@ -1,135 +1,36 @@
1
1
  /**
2
- * Built-in Markdoc fixture strings for the inspect command.
2
+ * Fixture resolution for the inspect/gallery commands.
3
3
  *
4
- * Each fixture exercises the rune's full structure with realistic content.
5
- * The key is the primary rune tag name (as used in Markdoc).
4
+ * The example content itself is the unified, generated `RUNE_EXAMPLES` manifest
5
+ * from `@refrakt-md/runes` (source: `packages/runes/fixtures/*.md` SPEC-102 /
6
+ * WORK-411). Plugin fixtures layer on top at the call site; this module just
7
+ * resolves a rune name to a source string and applies attribute overrides.
6
8
  */
7
- export const fixtures = {
8
- hint: `{% hint type="note" %}
9
- The identity transform runs after the rune schema transform and before rendering. It applies BEM classes, injects structural elements, and resolves design tokens.
10
- {% /hint %}`,
11
- accordion: `{% accordion %}
12
- ## Getting Started
13
- Follow these steps to set up your development environment and build your first theme.
14
-
15
- ## Configuration
16
- Theme configuration is defined in a TypeScript file that exports a ThemeConfig object.
17
-
18
- ## Deployment
19
- Once your theme is ready, publish it to npm and register it in the theme marketplace.
20
- {% /accordion %}`,
21
- details: `{% details %}
22
- ## Implementation Notes
23
- This section contains additional technical details about how the transform pipeline processes rune content. The identity transform reads meta tags, applies BEM classes, and injects structural elements before the content reaches the renderer.
24
- {% /details %}`,
25
- grid: `{% grid %}
26
- First column content with enough text to demonstrate layout behavior.
27
-
28
- ---
29
-
30
- Second column content showing how grid cells are separated by horizontal rules.
31
-
32
- ---
33
-
34
- Third column content completing the three-column grid layout example.
35
- {% /grid %}`,
36
- figure: `{% figure size="large" align="center" %}
37
- ![Mountain landscape](/images/mountain.jpg)
38
-
39
- A panoramic view of the Norwegian fjords at sunset, captured from the summit of Trolltunga.
40
- {% /figure %}`,
41
- tabs: `{% tabs %}
42
- # JavaScript
43
- \`\`\`js
44
- const greeting = "Hello, world!";
45
- console.log(greeting);
46
- \`\`\`
47
-
48
- # TypeScript
49
- \`\`\`ts
50
- const greeting: string = "Hello, world!";
51
- console.log(greeting);
52
- \`\`\`
53
-
54
- # Python
55
- \`\`\`python
56
- greeting = "Hello, world!"
57
- print(greeting)
58
- \`\`\`
59
- {% /tabs %}`,
60
- conversation: `{% conversation %}
61
- > **Alice** — Have you tried the new inspect command?
62
-
63
- > **Bob** — Yes! It shows exactly what HTML the identity transform produces. No more guessing which selectors to target.
64
-
65
- > **Alice** — The variant expansion is my favorite part. You can see every modifier value at once.
66
- {% /conversation %}`,
67
- sidenote: `{% sidenote variant="sidenote" %}
68
- This is a margin note that appears alongside the main content, providing additional context without interrupting the reading flow.
69
- {% /sidenote %}`,
70
- diff: `{% diff mode="unified" %}
71
- \`\`\`ts
72
- const config = {
73
- prefix: 'rf',
74
- runes: {}
75
- };
76
- \`\`\`
77
-
78
- \`\`\`ts
79
- const config = {
80
- prefix: 'rf',
81
- tokenPrefix: '--rf',
82
- icons: {},
83
- runes: {}
84
- };
85
- \`\`\`
86
- {% /diff %}`,
87
- datatable: `{% datatable %}
88
- | Name | Role | Department | Status |
89
- |------|------|------------|--------|
90
- | Alice | Engineer | Platform | Active |
91
- | Bob | Designer | Product | Active |
92
- | Carol | Manager | Engineering | On Leave |
93
- {% /datatable %}`,
94
- form: `{% form method="POST" variant="stacked" %}
95
- - Name
96
- - Email
97
- - Message (textarea)
98
-
99
- > How did you hear about us?
100
- - Search engine
101
- - Social media
102
- - Friend referral
103
- - Other
104
-
105
- **Submit**
106
- {% /form %}`,
107
- nav: `{% nav %}
108
- - [Docs](/docs)
109
- - [Blog](/blog)
110
-
111
- ## Product
112
- - pricing
113
- - features
114
-
115
- ## Resources
116
- - about
117
- - changelog
118
- {% /nav %}`,
119
- pagination: `{% pagination prev="install" next="configuration" /%}`,
120
- };
9
+ import { RUNE_EXAMPLES } from '@refrakt-md/runes';
10
+ /** Built-in fixture strings, keyed by rune name. Generated from fixtures/*.md. */
11
+ export const fixtures = RUNE_EXAMPLES;
12
+ /** Whether a rune has a real fixture (plugin fixture or the unified manifest),
13
+ * i.e. `getFixture` would return authored content rather than the stub. */
14
+ export function hasFixture(runeName, packageFixtures) {
15
+ return Boolean(packageFixtures?.[runeName] ?? fixtures[runeName]);
16
+ }
121
17
  /** Get a fixture for a rune, with optional attribute overrides applied to the source */
122
18
  export function getFixture(runeName, attrOverrides) {
123
- // Look up by primary name or try as-is
124
19
  const source = fixtures[runeName];
125
20
  if (!source) {
126
21
  // Generate a minimal fixture for unknown runes
127
22
  return `{% ${runeName} %}\nSample content for the ${runeName} rune.\n{% /${runeName} %}`;
128
23
  }
24
+ return applyFixtureOverrides(source, runeName, attrOverrides);
25
+ }
26
+ /** Apply attribute overrides to an arbitrary fixture source (e.g. a plugin
27
+ * fixture). Returns the source unchanged when there are no overrides. Shared by
28
+ * `getFixture` and the inspect/gallery variant matrices so plugin fixtures get
29
+ * the same variant expansion as core ones. */
30
+ export function applyFixtureOverrides(source, runeName, attrOverrides) {
129
31
  if (!attrOverrides || Object.keys(attrOverrides).length === 0) {
130
32
  return source;
131
33
  }
132
- // Apply attribute overrides by modifying the opening tag
133
34
  return applyOverrides(source, runeName, attrOverrides);
134
35
  }
135
36
  /** Replace or add attributes in the opening tag of a fixture */
@@ -1 +1 @@
1
- {"version":3,"file":"fixtures.js","sourceRoot":"","sources":["../../src/lib/fixtures.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,QAAQ,GAA2B;IAC/C,IAAI,EAAE;;YAEK;IAEX,SAAS,EAAE;;;;;;;;;iBASK;IAEhB,OAAO,EAAE;;;eAGK;IAEd,IAAI,EAAE;;;;;;;;;;YAUK;IAEX,MAAM,EAAE;;;;cAIK;IAEb,IAAI,EAAE;;;;;;;;;;;;;;;;;;YAkBK;IAEX,YAAY,EAAE;;;;;;oBAMK;IAEnB,QAAQ,EAAE;;gBAEK;IAEf,IAAI,EAAE;;;;;;;;;;;;;;;;YAgBK;IAEX,SAAS,EAAE;;;;;;iBAMK;IAEhB,IAAI,EAAE;;;;;;;;;;;;YAYK;IAEX,GAAG,EAAE;;;;;;;;;;;WAWK;IAEV,UAAU,EAAE,uDAAuD;CACnE,CAAC;AAEF,wFAAwF;AACxF,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,aAAsC;IAClF,uCAAuC;IACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,+CAA+C;QAC/C,OAAO,MAAM,QAAQ,+BAA+B,QAAQ,eAAe,QAAQ,KAAK,CAAC;IAC1F,CAAC;IAED,IAAI,CAAC,aAAa,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/D,OAAO,MAAM,CAAC;IACf,CAAC;IAED,yDAAyD;IACzD,OAAO,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;AACxD,CAAC;AAED,gEAAgE;AAChE,SAAS,cAAc,CAAC,MAAc,EAAE,OAAe,EAAE,SAAiC;IACzF,iEAAiE;IACjE,MAAM,cAAc,GAAG,IAAI,MAAM,CAAC,YAAY,WAAW,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACvF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC3C,IAAI,CAAC,KAAK;QAAE,OAAO,MAAM,CAAC;IAE1B,qFAAqF;IACrF,IAAI,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAEzB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACtD,oCAAoC;QACpC,MAAM,WAAW,GAAG,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC9D,IAAI,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAClC,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,GAAG,KAAK,KAAK,GAAG,CAAC,CAAC;QACnE,CAAC;aAAM,CAAC;YACP,oBAAoB;YACpB,UAAU,GAAG,UAAU,CAAC,OAAO,EAAE,GAAG,IAAI,GAAG,KAAK,KAAK,IAAI,CAAC;QAC3D,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,UAAU,GAAG,OAAO,EAAE,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACjD,CAAC"}
1
+ {"version":3,"file":"fixtures.js","sourceRoot":"","sources":["../../src/lib/fixtures.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,kFAAkF;AAClF,MAAM,CAAC,MAAM,QAAQ,GAA2B,aAAa,CAAC;AAE9D;4EAC4E;AAC5E,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,eAAwC;IACpF,OAAO,OAAO,CAAC,eAAe,EAAE,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,aAAsC;IAClF,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,+CAA+C;QAC/C,OAAO,MAAM,QAAQ,+BAA+B,QAAQ,eAAe,QAAQ,KAAK,CAAC;IAC1F,CAAC;IAED,OAAO,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;AAC/D,CAAC;AAED;;;+CAG+C;AAC/C,MAAM,UAAU,qBAAqB,CAAC,MAAc,EAAE,QAAgB,EAAE,aAAsC;IAC7G,IAAI,CAAC,aAAa,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/D,OAAO,MAAM,CAAC;IACf,CAAC;IACD,OAAO,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;AACxD,CAAC;AAED,gEAAgE;AAChE,SAAS,cAAc,CAAC,MAAc,EAAE,OAAe,EAAE,SAAiC;IACzF,iEAAiE;IACjE,MAAM,cAAc,GAAG,IAAI,MAAM,CAAC,YAAY,WAAW,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACvF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC3C,IAAI,CAAC,KAAK;QAAE,OAAO,MAAM,CAAC;IAE1B,qFAAqF;IACrF,IAAI,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAEzB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACtD,oCAAoC;QACpC,MAAM,WAAW,GAAG,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC9D,IAAI,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAClC,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,GAAG,KAAK,KAAK,GAAG,CAAC,CAAC;QACnE,CAAC;aAAM,CAAC;YACP,oBAAoB;YACpB,UAAU,GAAG,UAAU,CAAC,OAAO,EAAE,GAAG,IAAI,GAAG,KAAK,KAAK,IAAI,CAAC;QAC3D,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,UAAU,GAAG,OAAO,EAAE,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Recursively inline `@import` statements into a single CSS string so the gallery
3
+ * is self-contained (no path/serving concerns, deterministic for screenshots).
4
+ *
5
+ * Handles three forms so the skeleton/skin split (SPEC-094 §3) renders faithfully:
6
+ * - relative imports (`@import './x.css';`) — inlined from disk;
7
+ * - bare package specifiers (`@import '@refrakt-md/skeleton';`) — resolved via
8
+ * node (the package's CSS `.` export) and inlined, so the shared structural
9
+ * layer is part of the gallery;
10
+ * - layered imports (`@import './x.css' layer(skeleton);`) — inlined and wrapped
11
+ * in `@layer <name> { … }` so the cascade-layer ordering survives flattening
12
+ * (otherwise layered structure inlines as unlayered and would beat the skin).
13
+ *
14
+ * A `seen` set guards against import cycles; genuinely-remote imports (web fonts)
15
+ * are left untouched.
16
+ */
17
+ export declare function flattenCssImports(entryPath: string, seen?: Set<string>): string;
18
+ /** One rendered rune variant in the gallery. */
19
+ export interface GalleryCell {
20
+ /** Rune name (e.g. `hint`). */
21
+ rune: string;
22
+ /** Variant key (e.g. `default`, `type-warning`). */
23
+ variant: string;
24
+ /** Rendered HTML for this variant. */
25
+ html: string;
26
+ }
27
+ export interface GalleryDocumentOptions {
28
+ mode: 'light' | 'dark';
29
+ /** Flattened, self-contained theme CSS. */
30
+ themeCss: string;
31
+ cells: GalleryCell[];
32
+ /** Optional override for the font `<link>` tags injected into `<head>`. */
33
+ fontLinks?: string;
34
+ /** Optional bundled behaviors IIFE, inlined before `</body>` so interactive /
35
+ * lifecycle runes (tabs, diagram, chart, nav) enhance/render. */
36
+ behaviorScript?: string;
37
+ }
38
+ export interface LayoutDocumentOptions {
39
+ mode: 'light' | 'dark';
40
+ themeCss: string;
41
+ /** The layout's rendered HTML (from `layoutTransform` → `renderToHtml`). */
42
+ bodyHtml: string;
43
+ /** Layout name, for the document title. */
44
+ name: string;
45
+ fontLinks?: string;
46
+ behaviorScript?: string;
47
+ }
48
+ /**
49
+ * Render a standalone full-page document for a layout fixture — the layout's
50
+ * own chrome *is* the page (no gallery cell grid), so the harness can shoot it
51
+ * whole-page at multiple viewports.
52
+ */
53
+ export declare function renderLayoutDocument(opts: LayoutDocumentOptions): string;
54
+ /**
55
+ * Render the full self-contained gallery HTML document for one mode. Cells are
56
+ * grouped by rune; each carries a stable `data-gallery-cell="<rune>--<variant>"`
57
+ * anchor so the harness ({% WORK-409 %}) can clip per variant.
58
+ */
59
+ export declare function renderGalleryDocument(opts: GalleryDocumentOptions): string;
60
+ //# sourceMappingURL=gallery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gallery.d.ts","sourceRoot":"","sources":["../../src/lib/gallery.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,GAAG,CAAC,MAAM,CAAa,GAAG,MAAM,CAyB1F;AAED,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC3B,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;sEACkE;IAClE,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAyED,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,qBAAqB,GAAG,MAAM,CAsBxE;AAMD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,sBAAsB,GAAG,MAAM,CA8C1E"}
@@ -0,0 +1,192 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ const requireFrom = createRequire(import.meta.url);
5
+ /**
6
+ * Recursively inline `@import` statements into a single CSS string so the gallery
7
+ * is self-contained (no path/serving concerns, deterministic for screenshots).
8
+ *
9
+ * Handles three forms so the skeleton/skin split (SPEC-094 §3) renders faithfully:
10
+ * - relative imports (`@import './x.css';`) — inlined from disk;
11
+ * - bare package specifiers (`@import '@refrakt-md/skeleton';`) — resolved via
12
+ * node (the package's CSS `.` export) and inlined, so the shared structural
13
+ * layer is part of the gallery;
14
+ * - layered imports (`@import './x.css' layer(skeleton);`) — inlined and wrapped
15
+ * in `@layer <name> { … }` so the cascade-layer ordering survives flattening
16
+ * (otherwise layered structure inlines as unlayered and would beat the skin).
17
+ *
18
+ * A `seen` set guards against import cycles; genuinely-remote imports (web fonts)
19
+ * are left untouched.
20
+ */
21
+ export function flattenCssImports(entryPath, seen = new Set()) {
22
+ const abs = resolve(entryPath);
23
+ if (seen.has(abs))
24
+ return '';
25
+ seen.add(abs);
26
+ const dir = dirname(abs);
27
+ const css = readFileSync(abs, 'utf-8');
28
+ return css.replace(/@import\s+(?:url\()?['"]([^'"]+)['"]\)?\s*(?:layer\(([^)]*)\))?\s*;/g, (match, importPath, layerName) => {
29
+ let resolved;
30
+ if (importPath.startsWith('.')) {
31
+ resolved = resolve(dir, importPath);
32
+ }
33
+ else {
34
+ try {
35
+ resolved = requireFrom.resolve(importPath);
36
+ }
37
+ catch {
38
+ return match; // leave unresolvable/remote imports as-is
39
+ }
40
+ }
41
+ const inlined = flattenCssImports(resolved, seen);
42
+ return layerName !== undefined ? `@layer ${layerName.trim()} {\n${inlined}\n}` : inlined;
43
+ });
44
+ }
45
+ /** Default web-font links — tactically Lumina's families (WORK-407 defers the
46
+ * theme-owned-font system). The harness needs fonts actually loaded for stable
47
+ * screenshots. */
48
+ const DEFAULT_FONT_LINKS = [
49
+ '<link rel="preconnect" href="https://fonts.googleapis.com">',
50
+ '<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>',
51
+ '<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300..700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">',
52
+ ].join('\n');
53
+ /** Gallery chrome + determinism CSS. Kills animation/transition/caret so
54
+ * screenshots are stable; lays out runes in labelled cells. */
55
+ const GALLERY_CSS = `
56
+ *, *::before, *::after {
57
+ animation-duration: 0s !important;
58
+ animation-delay: 0s !important;
59
+ transition-duration: 0s !important;
60
+ transition-delay: 0s !important;
61
+ caret-color: transparent !important;
62
+ scroll-behavior: auto !important;
63
+ }
64
+ body.rf-gallery {
65
+ background: var(--rf-color-bg);
66
+ color: var(--rf-color-text);
67
+ font-family: var(--rf-font-sans, sans-serif);
68
+ margin: 0;
69
+ padding: 2.5rem 1.5rem;
70
+ }
71
+ .rf-gallery__inner { max-width: 72rem; margin: 0 auto; }
72
+ .rf-gallery__rune { margin: 0 0 3.5rem; }
73
+ .rf-gallery__rune-title {
74
+ font-family: var(--rf-font-mono, monospace);
75
+ font-size: 0.75rem;
76
+ text-transform: uppercase;
77
+ letter-spacing: 0.08em;
78
+ color: var(--rf-color-muted, #888);
79
+ border-bottom: 1px solid var(--rf-color-border, #ddd);
80
+ padding-bottom: 0.5rem;
81
+ margin: 0 0 1.5rem;
82
+ }
83
+ .rf-gallery__cell { margin: 0 0 2rem; }
84
+ .rf-gallery__gap {
85
+ font-family: var(--rf-font-mono, monospace);
86
+ font-size: 0.75rem;
87
+ color: var(--rf-color-muted, #999);
88
+ border: 1px dashed var(--rf-color-border, #ccc);
89
+ border-radius: var(--rf-radius-sm, 4px);
90
+ padding: 0.75rem 1rem;
91
+ }
92
+ .rf-gallery__cell-label {
93
+ display: block;
94
+ font-family: var(--rf-font-mono, monospace);
95
+ font-size: 0.6875rem;
96
+ color: var(--rf-color-muted, #999);
97
+ margin: 0 0 0.5rem;
98
+ }
99
+ `.trim();
100
+ /** Determinism reset for layout fixtures (no gallery chrome — the layout *is*
101
+ * the page). Kills animation/transition/caret so screenshots are stable. */
102
+ const LAYOUT_RESET_CSS = `
103
+ *, *::before, *::after {
104
+ animation-duration: 0s !important;
105
+ animation-delay: 0s !important;
106
+ transition-duration: 0s !important;
107
+ transition-delay: 0s !important;
108
+ caret-color: transparent !important;
109
+ scroll-behavior: auto !important;
110
+ }
111
+ body { margin: 0; background: var(--rf-color-bg); color: var(--rf-color-text); font-family: var(--rf-font-sans, sans-serif); }
112
+ `.trim();
113
+ /**
114
+ * Render a standalone full-page document for a layout fixture — the layout's
115
+ * own chrome *is* the page (no gallery cell grid), so the harness can shoot it
116
+ * whole-page at multiple viewports.
117
+ */
118
+ export function renderLayoutDocument(opts) {
119
+ const { mode, themeCss, bodyHtml, name, fontLinks = DEFAULT_FONT_LINKS, behaviorScript } = opts;
120
+ const htmlAttr = mode === 'dark' ? ' data-theme="dark"' : '';
121
+ const tail = behaviorScript
122
+ ? `<script type="application/json" id="rf-context">{"pages":[],"currentUrl":"/"}</script>\n<script>${behaviorScript}</script>\n`
123
+ : '';
124
+ return `<!DOCTYPE html>
125
+ <html lang="en"${htmlAttr}>
126
+ <head>
127
+ <meta charset="utf-8">
128
+ <meta name="viewport" content="width=device-width, initial-scale=1">
129
+ <meta name="color-scheme" content="light dark">
130
+ <title>refrakt layout — ${name} (${mode})</title>
131
+ ${fontLinks}
132
+ <style>${themeCss}</style>
133
+ <style>${LAYOUT_RESET_CSS}</style>
134
+ </head>
135
+ <body>
136
+ ${bodyHtml}
137
+ ${tail}</body>
138
+ </html>
139
+ `;
140
+ }
141
+ function escapeHtml(s) {
142
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
143
+ }
144
+ /**
145
+ * Render the full self-contained gallery HTML document for one mode. Cells are
146
+ * grouped by rune; each carries a stable `data-gallery-cell="<rune>--<variant>"`
147
+ * anchor so the harness ({% WORK-409 %}) can clip per variant.
148
+ */
149
+ export function renderGalleryDocument(opts) {
150
+ const { mode, themeCss, cells, fontLinks = DEFAULT_FONT_LINKS, behaviorScript } = opts;
151
+ // Group cells by rune, preserving insertion order.
152
+ const byRune = new Map();
153
+ for (const cell of cells) {
154
+ const list = byRune.get(cell.rune) ?? [];
155
+ list.push(cell);
156
+ byRune.set(cell.rune, list);
157
+ }
158
+ const sections = [];
159
+ for (const [rune, runeCells] of byRune) {
160
+ const cellHtml = runeCells.map(cell => {
161
+ const anchor = `${cell.rune}--${cell.variant}`;
162
+ return ` <div class="rf-gallery__cell" data-gallery-cell="${escapeHtml(anchor)}" data-rune="${escapeHtml(cell.rune)}" data-variant="${escapeHtml(cell.variant)}">
163
+ <span class="rf-gallery__cell-label">${escapeHtml(cell.variant)}</span>
164
+ ${cell.html}
165
+ </div>`;
166
+ }).join('\n');
167
+ sections.push(` <section class="rf-gallery__rune" data-gallery-rune="${escapeHtml(rune)}">
168
+ <h2 class="rf-gallery__rune-title">${escapeHtml(rune)}</h2>
169
+ ${cellHtml}
170
+ </section>`);
171
+ }
172
+ const htmlAttr = mode === 'dark' ? ' data-theme="dark"' : '';
173
+ return `<!DOCTYPE html>
174
+ <html lang="en"${htmlAttr}>
175
+ <head>
176
+ <meta charset="utf-8">
177
+ <meta name="viewport" content="width=device-width, initial-scale=1">
178
+ <meta name="color-scheme" content="light dark">
179
+ <title>refrakt gallery (${mode})</title>
180
+ ${fontLinks}
181
+ <style>${themeCss}</style>
182
+ <style>${GALLERY_CSS}</style>
183
+ </head>
184
+ <body class="rf-gallery">
185
+ <div class="rf-gallery__inner">
186
+ ${sections.join('\n')}
187
+ </div>
188
+ ${behaviorScript ? `<script type="application/json" id="rf-context">{"pages":[],"currentUrl":"/"}</script>\n<script>${behaviorScript}</script>\n` : ''}</body>
189
+ </html>
190
+ `;
191
+ }
192
+ //# sourceMappingURL=gallery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gallery.js","sourceRoot":"","sources":["../../src/lib/gallery.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@refrakt-md/cli",
3
3
  "description": "CLI tools for refrakt.md",
4
- "version": "0.21.0",
4
+ "version": "0.23.0",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -36,10 +36,12 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@markdoc/markdoc": "0.4.0",
39
- "@refrakt-md/ai": "0.21.0",
40
- "@refrakt-md/editor": "0.21.0",
41
- "@refrakt-md/runes": "0.21.0",
42
- "@refrakt-md/transform": "0.21.0",
39
+ "@refrakt-md/ai": "0.23.0",
40
+ "@refrakt-md/editor": "0.23.0",
41
+ "@refrakt-md/html": "0.23.0",
42
+ "@refrakt-md/runes": "0.23.0",
43
+ "@refrakt-md/transform": "0.23.0",
44
+ "esbuild": "^0.27.5",
43
45
  "postcss": "^8.4.0"
44
46
  }
45
47
  }