@tenphi/renderer 0.26.0 → 0.27.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.
@@ -2,7 +2,7 @@
2
2
  import { messages, requestLanguage } from "../localization.js";
3
3
  import { buildPreview } from "../preview-document.js";
4
4
  import { content } from "virtual:cookbook/config";
5
- import { PreviewRoot } from "./Preview.styles.js";
5
+ import { PreviewRoot, previewHeightStyles } from "./Preview.styles.js";
6
6
 
7
7
  import type { PreviewProps as Props } from "./props.js";
8
8
 
@@ -18,7 +18,12 @@ const ui = messages(
18
18
  );
19
19
  ---
20
20
 
21
- <PreviewRoot className="td-preview" data-not-content="" aria-label={title}>
21
+ <PreviewRoot
22
+ className="td-preview"
23
+ data-not-content=""
24
+ aria-label={title}
25
+ styles={preview.height && previewHeightStyles(preview.height)}
26
+ >
22
27
  <PreviewRoot.Caption><strong>{title}</strong></PreviewRoot.Caption>
23
28
  <PreviewRoot.Stage>
24
29
  <PreviewRoot.Frame
@@ -78,3 +78,8 @@ export const PreviewRoot = defineComponent("Preview", {
78
78
  },
79
79
  elements: previewElements,
80
80
  });
81
+
82
+ /** Render-time styles for one preview's `height` prop, a validated CSS length. */
83
+ export function previewHeightStyles(height) {
84
+ return { Frame: { height: `min ${height}` } };
85
+ }
@@ -15,6 +15,8 @@ interface PreviewProps {
15
15
  javascript?: string;
16
16
  /** Public-directory paths, such as `/demo/button.js`, inlined as classic scripts before `javascript`. */
17
17
  scripts?: string[];
18
+ /** The frame's minimum height as a CSS length, such as `18rem`; defaults to `10rem`. */
19
+ height?: string;
18
20
  }
19
21
  type StepsProps = Record<string, never>;
20
22
  interface TabsProps {
@@ -1 +1 @@
1
- {"version":3,"file":"props.d.ts","names":[],"sources":["../../src/components/props.ts"],"mappings":";UAAiB;EACf;EACA;;UAEe;EACf;EACA;EACA;;UAEe;EACf;EACA;EACA;EACA;;EAEA;;KAEU,aAAa;UACR;EACf;;UAEe;EACf;;UAEe;EACf;EACA;;UAEe;EACf;EACA,OAAO;IAAQ;IAAe;IAAc"}
1
+ {"version":3,"file":"props.d.ts","names":[],"sources":["../../src/components/props.ts"],"mappings":";UAAiB;EACf;EACA;;UAEe;EACf;EACA;EACA;;UAEe;EACf;EACA;EACA;EACA;;EAEA;;EAEA;;KAEU,aAAa;UACR;EACf;;UAEe;EACf;;UAEe;EACf;EACA;;UAEe;EACf;EACA,OAAO;IAAQ;IAAe;IAAc"}
@@ -14,6 +14,8 @@ export interface PreviewProps {
14
14
  javascript?: string;
15
15
  /** Public-directory paths, such as `/demo/button.js`, inlined as classic scripts before `javascript`. */
16
16
  scripts?: string[];
17
+ /** The frame's minimum height as a CSS length, such as `18rem`; defaults to `10rem`. */
18
+ height?: string;
17
19
  }
18
20
  export type StepsProps = Record<string, never>;
19
21
  export interface TabsProps {
@@ -1,13 +1,15 @@
1
1
  import { PreviewProps } from "./components/props.js";
2
2
  //#region src/preview-document.d.ts
3
3
  /**
4
- * Build a Preview's frame document and its "View source" text. Public scripts
5
- * are inlined so they run in the opaque-origin sandbox without a request.
4
+ * Build a Preview's frame document, its "View source" text and the frame's
5
+ * settings. Public scripts are inlined so they run in the opaque-origin sandbox
6
+ * without a request.
6
7
  */
7
- declare function buildPreview({ html, css, javascript, scripts }: Omit<PreviewProps, "title">, publicDir: string, page: string): Promise<{
8
+ declare function buildPreview({ html, css, javascript, scripts, height }: Omit<PreviewProps, "title">, publicDir: string, page: string): Promise<{
8
9
  document: string;
9
10
  source: string;
10
11
  sandbox: string;
12
+ height: string | undefined;
11
13
  }>;
12
14
  //#endregion
13
15
  export { buildPreview };
@@ -1 +1 @@
1
- {"version":3,"file":"preview-document.d.ts","names":[],"sources":["../src/preview-document.ts"],"mappings":";;;;;;iBAQsB,eAElB,MACA,KACA,YACA,WACC,KAAK,wBACR,mBACA,eAAY"}
1
+ {"version":3,"file":"preview-document.d.ts","names":[],"sources":["../src/preview-document.ts"],"mappings":";;;;;;;iBAcsB,eAElB,MACA,KACA,YACA,SACA,UACC,KAAK,wBACR,mBACA,eAAY"}
@@ -1,18 +1,22 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  //#region src/preview-document.ts
4
+ const CSS_LENGTH = /^(?:\d+|\d*\.\d+)(?:px|r?em|ch|ex|r?lh|[dls]?v(?:h|w|min|max)|cm|mm|in|pt|pc)$/;
4
5
  /**
5
- * Build a Preview's frame document and its "View source" text. Public scripts
6
- * are inlined so they run in the opaque-origin sandbox without a request.
6
+ * Build a Preview's frame document, its "View source" text and the frame's
7
+ * settings. Public scripts are inlined so they run in the opaque-origin sandbox
8
+ * without a request.
7
9
  */
8
- async function buildPreview({ html = "", css = "", javascript, scripts = [] }, publicDir, page) {
10
+ async function buildPreview({ html = "", css = "", javascript, scripts = [], height }, publicDir, page) {
11
+ if (height !== void 0 && !CSS_LENGTH.test(height)) throw Error(`Preview on ${page}: height ${height} must be a CSS length, such as 18rem or 320px.`);
9
12
  const files = await Promise.all(scripts.map((src) => readPublicScript(src, publicDir, page)));
10
13
  const markup = `<!doctype html><meta charset="utf-8"><style>${css}</style>${html}`;
11
14
  const authored = javascript ? inlineScript(javascript) : "";
12
15
  return {
13
16
  document: markup + files.map(inlineScript).join("") + authored,
14
17
  source: markup + scripts.map((src) => `<script src="${src}"><\/script>`).join("") + authored,
15
- sandbox: scripts.length || javascript ? "allow-scripts" : ""
18
+ sandbox: scripts.length || javascript ? "allow-forms allow-scripts" : "allow-forms",
19
+ height
16
20
  };
17
21
  }
18
22
  function inlineScript(code) {
@@ -1 +1 @@
1
- {"version":3,"file":"preview-document.js","names":[],"sources":["../src/preview-document.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { PreviewProps } from \"./components/props.js\";\n\n/**\n * Build a Preview's frame document and its \"View source\" text. Public scripts\n * are inlined so they run in the opaque-origin sandbox without a request.\n */\nexport async function buildPreview(\n {\n html = \"\",\n css = \"\",\n javascript,\n scripts = [],\n }: Omit<PreviewProps, \"title\">,\n publicDir: string,\n page: string,\n) {\n const files = await Promise.all(\n scripts.map((src) => readPublicScript(src, publicDir, page)),\n );\n const markup = `<!doctype html><meta charset=\"utf-8\"><style>${css}</style>${html}`;\n const authored = javascript ? inlineScript(javascript) : \"\";\n return {\n document: markup + files.map(inlineScript).join(\"\") + authored,\n source:\n markup +\n scripts.map((src) => `<script src=\"${src}\"></script>`).join(\"\") +\n authored,\n sandbox: scripts.length || javascript ? \"allow-scripts\" : \"\",\n };\n}\n\n// The HTML parser ends a script at the first `</script`, even inside a string.\nfunction inlineScript(code: string) {\n return `<script>${code.replace(/<\\/(script)/gi, \"<\\\\/$1\")}</script>`;\n}\n\nasync function readPublicScript(src: string, publicDir: string, page: string) {\n const error = `Preview on ${page}: script ${src} must be a readable file in the site's public directory, such as /demo/button.js.`;\n if (!/^\\/(?!\\/)/.test(src) || src.split(/[\\\\/]/).includes(\"..\"))\n throw Error(error);\n try {\n return await readFile(join(publicDir, src), \"utf8\");\n } catch {\n throw Error(error);\n }\n}\n"],"mappings":";;;;;;;AAQA,eAAsB,aACpB,EACE,OAAO,IACP,MAAM,IACN,YACA,UAAU,CAAC,KAEb,WACA,MACA;CACA,MAAM,QAAQ,MAAM,QAAQ,IAC1B,QAAQ,KAAK,QAAQ,iBAAiB,KAAK,WAAW,IAAI,CAAC,CAC7D;CACA,MAAM,SAAS,+CAA+C,IAAI,UAAU;CAC5E,MAAM,WAAW,aAAa,aAAa,UAAU,IAAI;CACzD,OAAO;EACL,UAAU,SAAS,MAAM,IAAI,YAAY,CAAC,CAAC,KAAK,EAAE,IAAI;EACtD,QACE,SACA,QAAQ,KAAK,QAAQ,gBAAgB,IAAI,aAAY,CAAC,CAAC,KAAK,EAAE,IAC9D;EACF,SAAS,QAAQ,UAAU,aAAa,kBAAkB;CAC5D;AACF;AAGA,SAAS,aAAa,MAAc;CAClC,OAAO,WAAW,KAAK,QAAQ,iBAAiB,QAAQ,EAAE;AAC5D;AAEA,eAAe,iBAAiB,KAAa,WAAmB,MAAc;CAC5E,MAAM,QAAQ,cAAc,KAAK,WAAW,IAAI;CAChD,IAAI,CAAC,YAAY,KAAK,GAAG,KAAK,IAAI,MAAM,OAAO,CAAC,CAAC,SAAS,IAAI,GAC5D,MAAM,MAAM,KAAK;CACnB,IAAI;EACF,OAAO,MAAM,SAAS,KAAK,WAAW,GAAG,GAAG,MAAM;CACpD,QAAQ;EACN,MAAM,MAAM,KAAK;CACnB;AACF"}
1
+ {"version":3,"file":"preview-document.js","names":[],"sources":["../src/preview-document.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { PreviewProps } from \"./components/props.js\";\n\n// The frame's height becomes generated CSS, so accept only a plain length.\n// Percentages are rejected because the frame's container has no fixed height.\nconst CSS_LENGTH =\n /^(?:\\d+|\\d*\\.\\d+)(?:px|r?em|ch|ex|r?lh|[dls]?v(?:h|w|min|max)|cm|mm|in|pt|pc)$/;\n\n/**\n * Build a Preview's frame document, its \"View source\" text and the frame's\n * settings. Public scripts are inlined so they run in the opaque-origin sandbox\n * without a request.\n */\nexport async function buildPreview(\n {\n html = \"\",\n css = \"\",\n javascript,\n scripts = [],\n height,\n }: Omit<PreviewProps, \"title\">,\n publicDir: string,\n page: string,\n) {\n if (height !== undefined && !CSS_LENGTH.test(height))\n throw Error(\n `Preview on ${page}: height ${height} must be a CSS length, such as 18rem or 320px.`,\n );\n const files = await Promise.all(\n scripts.map((src) => readPublicScript(src, publicDir, page)),\n );\n const markup = `<!doctype html><meta charset=\"utf-8\"><style>${css}</style>${html}`;\n const authored = javascript ? inlineScript(javascript) : \"\";\n return {\n document: markup + files.map(inlineScript).join(\"\") + authored,\n source:\n markup +\n scripts.map((src) => `<script src=\"${src}\"></script>`).join(\"\") +\n authored,\n // A submitted form can navigate only the frame itself, as a link can.\n sandbox:\n scripts.length || javascript\n ? \"allow-forms allow-scripts\"\n : \"allow-forms\",\n height,\n };\n}\n\n// The HTML parser ends a script at the first `</script`, even inside a string.\nfunction inlineScript(code: string) {\n return `<script>${code.replace(/<\\/(script)/gi, \"<\\\\/$1\")}</script>`;\n}\n\nasync function readPublicScript(src: string, publicDir: string, page: string) {\n const error = `Preview on ${page}: script ${src} must be a readable file in the site's public directory, such as /demo/button.js.`;\n if (!/^\\/(?!\\/)/.test(src) || src.split(/[\\\\/]/).includes(\"..\"))\n throw Error(error);\n try {\n return await readFile(join(publicDir, src), \"utf8\");\n } catch {\n throw Error(error);\n }\n}\n"],"mappings":";;;AAMA,MAAM,aACJ;;;;;;AAOF,eAAsB,aACpB,EACE,OAAO,IACP,MAAM,IACN,YACA,UAAU,CAAC,GACX,UAEF,WACA,MACA;CACA,IAAI,WAAW,KAAA,KAAa,CAAC,WAAW,KAAK,MAAM,GACjD,MAAM,MACJ,cAAc,KAAK,WAAW,OAAO,+CACvC;CACF,MAAM,QAAQ,MAAM,QAAQ,IAC1B,QAAQ,KAAK,QAAQ,iBAAiB,KAAK,WAAW,IAAI,CAAC,CAC7D;CACA,MAAM,SAAS,+CAA+C,IAAI,UAAU;CAC5E,MAAM,WAAW,aAAa,aAAa,UAAU,IAAI;CACzD,OAAO;EACL,UAAU,SAAS,MAAM,IAAI,YAAY,CAAC,CAAC,KAAK,EAAE,IAAI;EACtD,QACE,SACA,QAAQ,KAAK,QAAQ,gBAAgB,IAAI,aAAY,CAAC,CAAC,KAAK,EAAE,IAC9D;EAEF,SACE,QAAQ,UAAU,aACd,8BACA;EACN;CACF;AACF;AAGA,SAAS,aAAa,MAAc;CAClC,OAAO,WAAW,KAAK,QAAQ,iBAAiB,QAAQ,EAAE;AAC5D;AAEA,eAAe,iBAAiB,KAAa,WAAmB,MAAc;CAC5E,MAAM,QAAQ,cAAc,KAAK,WAAW,IAAI;CAChD,IAAI,CAAC,YAAY,KAAK,GAAG,KAAK,IAAI,MAAM,OAAO,CAAC,CAAC,SAAS,IAAI,GAC5D,MAAM,MAAM,KAAK;CACnB,IAAI;EACF,OAAO,MAAM,SAAS,KAAK,WAAW,GAAG,GAAG,MAAM;CACpD,QAAQ;EACN,MAAM,MAAM,KAAK;CACnB;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenphi/renderer",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "Astro renderer and Tasty theme for Cookbook",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -61,7 +61,7 @@
61
61
  "@fontsource-variable/jetbrains-mono": "^5.3.0",
62
62
  "@fontsource-variable/onest": "^5.3.0",
63
63
  "@pagefind/default-ui": "1.5.2",
64
- "@tenphi/docs": "0.26.0",
64
+ "@tenphi/docs": "0.27.0",
65
65
  "@tenphi/eslint-plugin-tasty": "1.6.0",
66
66
  "@tenphi/glaze": "2.0.1",
67
67
  "@tenphi/tasty": "3.9.10",