weasyprint-tsx 0.1.0 → 0.1.1

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/ReadMe.pdf CHANGED
Binary file
package/bun.lock CHANGED
@@ -4,10 +4,13 @@
4
4
  "workspaces": {
5
5
  "": {
6
6
  "name": "@weasyprint-tsx/build",
7
+ "dependencies": {
8
+ "@fontsource-variable/public-sans": "^5.2.7",
9
+ },
7
10
  },
8
11
  "packages/build": {
9
12
  "name": "@weasyprint-tsx/build",
10
- "version": "0.0.1",
13
+ "version": "0.1.1",
11
14
  "bin": {
12
15
  "weasyprint-tsx": "./src/cli.ts",
13
16
  },
@@ -24,14 +27,21 @@
24
27
  },
25
28
  "packages/create": {
26
29
  "name": "@weasyprint-tsx/create",
27
- "version": "0.0.1",
30
+ "version": "0.1.0",
28
31
  "bin": {
29
- "create-weasyprint-tsx": "src/cli.ts",
32
+ "create-weasyprint-tsx": "cli.js",
33
+ },
34
+ "dependencies": {
35
+ "highlight.js": "11.11.1",
30
36
  },
31
37
  },
32
38
  "packages/ui": {
33
39
  "name": "@weasyprint-tsx/ui",
34
- "version": "0.0.1",
40
+ "version": "0.1.0",
41
+ "dependencies": {
42
+ "highlight.js": "^11.11.1",
43
+ "katex": "^0.16.47",
44
+ },
35
45
  },
36
46
  },
37
47
  "packages": {
@@ -45,6 +55,8 @@
45
55
 
46
56
  "@csstools/utilities": ["@csstools/utilities@3.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-etDqA/4jYvOGBM6yfKCOsEXfH96BKztZdgGmGqKi2xHnDe0ILIBraRspwgYatJH9JsCZ5HCGoCst8w18EKOAdg=="],
47
57
 
58
+ "@fontsource-variable/public-sans": ["@fontsource-variable/public-sans@5.2.7", "", {}, "sha512-4mvade2J3slKkvwRkS+p8T3szet/0vhWoSnuUJTVU81Uo2pRpSZY/Y8bSLRqpSwzIPxjVmRJ53oq6JKP/l/PSg=="],
59
+
48
60
  "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
49
61
 
50
62
  "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
@@ -95,14 +107,20 @@
95
107
 
96
108
  "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
97
109
 
110
+ "commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
111
+
98
112
  "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
99
113
 
100
114
  "enhanced-resolve": ["enhanced-resolve@5.24.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ=="],
101
115
 
102
116
  "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
103
117
 
118
+ "highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
119
+
104
120
  "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
105
121
 
122
+ "katex": ["katex@0.16.47", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg=="],
123
+
106
124
  "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
107
125
 
108
126
  "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="],
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "weasyprint-tsx",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
+ "bin": {
5
+ "create-weasyprint-tsx": "bun packages/create/cli.ts",
6
+ "build-weasyprint-tsx": "bun packages/build/src/cli.ts"
7
+ },
4
8
  "scripts": {
5
9
  "dev": "bun packages/build/src/cli.ts --watch",
6
- "init" : "bun packages/create/cli.ts"
10
+ "init": "bun packages/create/cli.ts",
11
+ "build": "bun packages/build/src/cli.ts"
7
12
  },
8
13
  "workspaces": [
9
14
  "packages/*"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weasyprint-tsx/build",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "main": "./src/index.ts",
5
5
  "exports": { ".": "./src/index.ts" },
6
6
  "types": "./src/index.ts",
@@ -1,12 +1,12 @@
1
- import { join } from "path";
2
- import { DConfig } from "./config";
1
+ import tailwindcss from "@tailwindcss/postcss";
3
2
  import { BunPlugin } from "bun";
3
+ import { join } from "path";
4
4
  import postcss from "postcss";
5
5
  import customProperties from "postcss-custom-properties";
6
- import tailwindcss from "@tailwindcss/postcss";
7
- import { injectDevScript } from "./server";
8
6
  import { h } from "preact";
9
7
  import { renderToStaticMarkup } from "preact-render-to-string";
8
+ import { DConfig } from "./config";
9
+ import { DEV_SCRIPT } from "./server";
10
10
 
11
11
  const tailwindPlugin: BunPlugin = {
12
12
  name: "tailwind-postcss",
@@ -49,17 +49,17 @@ export async function buildHTML(cfg: DConfig, dev: boolean = false) {
49
49
  join(BUILD_DIR, `index.js?t=${performance.now()}`)
50
50
  );
51
51
 
52
- const html = "<!DOCTYPE html>" + renderToStaticMarkup(h(Document, {}));
53
-
54
- await Bun.write(BUILD_PATH, html);
52
+ let html = "<!DOCTYPE html>" + renderToStaticMarkup(h(Document, {}));
55
53
 
56
54
  if (dev) {
57
- injectDevScript(BUILD_PATH);
55
+ html = html.replace("</html>", `${DEV_SCRIPT}</html>`);
58
56
  }
57
+ await Bun.write(BUILD_PATH, html);
59
58
  process.stdout.write(
60
59
  `Bun HTML build -- ${(performance.now() - t0).toFixed(2)}ms\n`,
61
60
  );
62
- return Bun.hash(html);
61
+ const css = await Bun.file(join(BUILD_DIR, "index.css")).text();
62
+ return Bun.hash(html + css);
63
63
  }
64
64
 
65
65
  function buildArgs(cfg: DConfig): string[] {
@@ -24,7 +24,7 @@ export interface Config {
24
24
  };
25
25
  dev?: {
26
26
  port?: number;
27
- watch?: string;
27
+ watch?: string[];
28
28
  };
29
29
  }
30
30
 
@@ -44,7 +44,7 @@ const defaultConfig = {
44
44
  },
45
45
  dev: {
46
46
  port: 3000,
47
- watch: "src",
47
+ watch: ["src"],
48
48
  },
49
49
  } satisfies Config;
50
50
 
@@ -1,8 +1,8 @@
1
1
  import { watch } from "fs";
2
+ import { join } from "path";
2
3
  import { buildHTML, buildPDF } from "./build";
3
4
  import { loadConfig } from "./config";
4
5
  import { bumpPing, startServer } from "./server";
5
- import { join } from "path";
6
6
 
7
7
  export async function buildOnce(output?: string) {
8
8
  const cfg = await loadConfig();
@@ -22,7 +22,7 @@ export async function buildOnce(output?: string) {
22
22
 
23
23
  export async function devMode(output?: string) {
24
24
  const cfg = await loadConfig();
25
- const WATCH_DIR = join(process.cwd(), cfg.dev.watch);
25
+ const WATCH_DIRS = cfg.dev.watch.map((p) => join(process.cwd(), p));
26
26
 
27
27
  if (output) cfg.io.output = cfg.io.output ?? output;
28
28
  process.stdout.write(`Dev: http://localhost:${cfg.dev.port}`);
@@ -52,10 +52,12 @@ export async function devMode(output?: string) {
52
52
 
53
53
  startServer(cfg.dev.port, cfg.io.buildDir);
54
54
 
55
- watch(WATCH_DIR, {}, async (ev, f) => {
56
- process.stdout.write(`${ev} : file changed ${f}`);
57
- await rebuild();
58
- });
55
+ WATCH_DIRS.forEach((dir) =>
56
+ watch(dir, { recursive: true }, async (_, f) => {
57
+ process.stdout.write(`file changed ${f}`);
58
+ await rebuild();
59
+ }),
60
+ );
59
61
 
60
62
  await Bun.sleep(Infinity);
61
63
  }
@@ -1,6 +1,6 @@
1
1
  import { join } from "path";
2
2
 
3
- const DEV_SCRIPT = `<script>var _t=null;setInterval(function(){fetch('/__dev_ping').then(function(r){return r.text();}).then(function(s){if(_t!==null&&s!==_t)location.reload();_t=s;}).catch(function(){});},300);</script>`;
3
+ export const DEV_SCRIPT = `<script>var _t=null;setInterval(function(){fetch('/__dev_ping').then(function(r){return r.text();}).then(function(s){if(_t!==null&&s!==_t)location.reload();_t=s;}).catch(function(){});},1000);</script>`;
4
4
 
5
5
  let pingCounter = 0;
6
6
 
@@ -19,8 +19,9 @@ export function startServer(port: number, buildDir: string): void {
19
19
  headers: { "Cache-Control": "no-store" },
20
20
  });
21
21
  }
22
-
23
- const file = Bun.file(join(process.cwd(), buildDir , "index.html"));
22
+ const pathname = url.pathname === "/" ? "/index.html" : url.pathname;
23
+ if (pathname == "/favicon.ico") return new Response(null);
24
+ const file = Bun.file(join(process.cwd(), buildDir, pathname));
24
25
  return new Response(file);
25
26
  },
26
27
  });
@@ -29,8 +30,6 @@ export function startServer(port: number, buildDir: string): void {
29
30
  export async function injectDevScript(htmlPath: string): Promise<void> {
30
31
  let html = await Bun.file(htmlPath).text();
31
32
  if (!html.includes("/__dev_ping")) {
32
- html = html.replace("</html>", `${DEV_SCRIPT}</html>`);
33
33
  await Bun.write(htmlPath, html);
34
34
  }
35
35
  }
36
-
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@weasyprint-tsx/ui",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "exports": {
5
5
  ".": "./src/index.ts"
6
6
  },
7
- "types": "./src/index.ts"
7
+ "types": "./src/index.ts",
8
+ "dependencies": {
9
+ "highlight.js": "11.11.1"
10
+ }
8
11
  }
@@ -16,8 +16,8 @@ interface BlockProps extends ComponentProps<"div"> {
16
16
  align?: "middle" | "top" | "bottom";
17
17
  }
18
18
 
19
- export function Block(_props: BlockProps): null {
20
- return null;
19
+ export function Block({ children }: BlockProps) {
20
+ return children;
21
21
  }
22
22
 
23
23
  export function BlockBox({
@@ -40,7 +40,10 @@ export function BlockBox({
40
40
  const child = blockList.map(
41
41
  ({ props: { style, ratio = 1, className, centered, align, ...props } }) => (
42
42
  <div
43
- style={mergeStyle(style, { "--ratio": ratio, "--block-box-align": align })}
43
+ style={mergeStyle(style, {
44
+ "--ratio": ratio,
45
+ "--block-box-align": align,
46
+ })}
44
47
  className={joinClasses(
45
48
  className,
46
49
  styles.block,
@@ -0,0 +1,4 @@
1
+ .pre {
2
+ background-color: var(--codeblock-bg, #0f172a);
3
+ color: var(--codeblock-color, #cbd5e1);
4
+ }
@@ -1,16 +1,34 @@
1
1
  import hljs from "highlight.js";
2
2
  import "highlight.js/styles/atom-one-dark.css";
3
3
  import { ComponentProps } from "preact";
4
+ import styles from "./CodeBlock.module.css";
5
+ import { joinClasses, mergeStyle } from "./utils";
4
6
 
5
7
  interface CodeBlockProps extends Omit<ComponentProps<"code">, "children"> {
6
8
  language: string;
7
9
  code: string;
10
+ bgColor?: string;
11
+ color?: string;
12
+
8
13
  }
9
14
 
10
- export function CodeBlock({ code, language, ...props }: CodeBlockProps) {
15
+ export function CodeBlock({
16
+ code,
17
+ language,
18
+ bgColor,
19
+ color,
20
+ style,
21
+ className,
22
+ ...props
23
+ }: CodeBlockProps) {
11
24
  const codeBlock = hljs.highlight(code, { language: language });
25
+ const css = mergeStyle(style, {
26
+ "--codeblock-bg": bgColor,
27
+ "--color-color" : color,
28
+ breakInside: "avoid",
29
+ });
12
30
  return (
13
- <pre style={{breakInside : "avoid"}}>
31
+ <pre style={css} className={joinClasses(className, styles.pre)}>
14
32
  <code {...props} dangerouslySetInnerHTML={{ __html: codeBlock.value }} />
15
33
  </pre>
16
34
  );
@@ -0,0 +1,9 @@
1
+ .stack {
2
+ & > * {
3
+ margin-left: var(--stack-ml, 0);
4
+ margin-right: var(--stack-mr, 0);
5
+ &:not(:last-child) {
6
+ margin-bottom: var(--stack-gap, 0);
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,24 @@
1
+ import { ComponentProps } from "preact";
2
+ import styles from "./Stack.module.css";
3
+ import { joinClasses, mergeStyle } from "./utils";
4
+
5
+ interface StackProps extends ComponentProps<"div"> {
6
+ gap?: number | string;
7
+ align?: "left" | "right";
8
+ }
9
+
10
+ export function Stack({ gap, align, className, style, ...props }: StackProps) {
11
+ const css = mergeStyle(style, {
12
+ "--stack-gap": gap,
13
+ "--stack-ml": align === "left" ? undefined : "auto",
14
+ "--stack-mr": align === "right" ? undefined : "auto",
15
+ });
16
+
17
+ return (
18
+ <div
19
+ style={css}
20
+ className={joinClasses(className, styles.stack)}
21
+ {...props}
22
+ />
23
+ );
24
+ }
@@ -1,28 +1,31 @@
1
-
2
- .h1,
3
- .h2,
4
- .h3,
5
- .h4,
6
- .h5,
7
- .h6 {
8
- &::before {
9
- float: left;
10
- clear: left;
11
- }
12
- &[data-marker]::before {
13
- content: attr(data-marker);
14
- }
1
+ .h1::before,
2
+ .h2::before,
3
+ .h3::before,
4
+ .h4::before,
5
+ .h5::before,
6
+ .h6::before {
7
+ float: left;
8
+ clear: left;
15
9
  }
16
10
 
11
+ .h1[data-marker]::before,
12
+ .h2[data-marker]::before,
13
+ .h3[data-marker]::before,
14
+ .h4[data-marker]::before,
15
+ .h5[data-marker]::before,
16
+ .h6[data-marker]::before {
17
+ content: attr(data-marker);
18
+ }
17
19
 
18
20
  .h1 {
19
21
  color: var(--h1-color, inherit);
20
22
  font-size: var(--h1-fontsize, inherit);
21
23
  counter-reset: count-h2 count-h3 count-h4 count-h5 count-h6;
22
24
  counter-increment: count-h1;
23
- &::before {
24
- content: counter(count-h1, upper-roman) ". ";
25
- }
25
+ }
26
+
27
+ :where(.h1)::before {
28
+ content: counter(count-h1, upper-roman) ". ";
26
29
  }
27
30
 
28
31
  .h2 {
@@ -30,9 +33,10 @@
30
33
  font-size: var(--h2-fontsize, inherit);
31
34
  counter-reset: count-h3 count-h4 count-h5 count-h6;
32
35
  counter-increment: count-h2;
33
- &::before {
34
- content: counter(count-h2, decimal) ". ";
35
- }
36
+ }
37
+
38
+ :where(.h2)::before {
39
+ content: counter(count-h2, decimal) ". ";
36
40
  }
37
41
 
38
42
  .h3 {
@@ -40,9 +44,10 @@
40
44
  font-size: var(--h3-fontsize, inherit);
41
45
  counter-reset: count-h4 count-h5 count-h6;
42
46
  counter-increment: count-h3;
43
- &::before {
44
- content: counter(count-h3, lower-alpha) ". ";
45
- }
47
+ }
48
+
49
+ :where(.h3)::before {
50
+ content: counter(count-h3, upper-alpha) ". ";
46
51
  }
47
52
 
48
53
  .h4 {
@@ -50,9 +55,10 @@
50
55
  font-size: var(--h4-fontsize, inherit);
51
56
  counter-reset: count-h5 count-h6;
52
57
  counter-increment: count-h4;
53
- &::before {
54
- content: counter(count-h4, lower-alpha) ". ";
55
- }
58
+ }
59
+
60
+ :where(.h4)::before {
61
+ content: counter(count-h4, decimal) ". ";
56
62
  }
57
63
 
58
64
  .h5 {
@@ -60,16 +66,18 @@
60
66
  font-size: var(--h5-fontsize, inherit);
61
67
  counter-reset: count-h6;
62
68
  counter-increment: count-h5;
63
- &::before {
64
- content: counter(count-h5, lower-alpha) ". ";
65
- }
69
+ }
70
+
71
+ :where(.h5)::before {
72
+ content: counter(count-h5, lower-alpha) ". ";
66
73
  }
67
74
 
68
75
  .h6 {
69
76
  color: var(--h6-color, inherit);
70
77
  font-size: var(--h6-fontsize, inherit);
71
78
  counter-increment: count-h6;
72
- &::before {
73
- content: counter(count-h6, lower-alpha) ". ";
74
- }
79
+ }
80
+
81
+ :where(.h6)::before {
82
+ content: counter(count-h6, lower-roman) ". ";
75
83
  }
@@ -4,6 +4,7 @@ export { DotLine } from "./DotLine";
4
4
  export { Equation } from "./Equation";
5
5
  export { LI, OL, UL } from "./List";
6
6
  export { Page, PageBreak } from "./Page";
7
+ export { Stack } from "./Stack";
7
8
  export { Entry, Table } from "./Table";
8
9
  export { H1, H2, H3, H4, H5, H6, ResetCounter, Title } from "./Titles";
9
10
 
package/src/index.css CHANGED
@@ -19,16 +19,21 @@ body {
19
19
  font-size: 10pt;
20
20
  line-height: 1.75;
21
21
  font-family: system-ui, sans-serif;
22
+ font-family: "Public Sans Variable", sans-serif;
22
23
  }
23
24
 
24
- h1, h2, h3 {
25
+ h1,
26
+ h2,
27
+ h3 {
25
28
  line-height: 1.2;
26
29
  }
27
30
 
31
+
32
+
28
33
  code {
29
34
  font-family: ui-monospace, monospace;
30
35
  font-size: 0.88em;
31
- background: #f1f5f9;
36
+ background: #4490dd;
32
37
  padding: 0.1em 0.3em;
33
38
  border-radius: 3px;
34
39
  }
package/src/index.tsx CHANGED
@@ -1,11 +1,16 @@
1
1
  import {
2
- H1, H2, H3,
3
- Page, PageBreak,
4
- UL, OL, LI,
5
- Table, Entry,
6
- BlockBox, Block,
7
2
  CodeBlock,
8
3
  DotLine,
4
+ Entry,
5
+ H1,
6
+ H2,
7
+ H3,
8
+ LI,
9
+ OL,
10
+ PageBreak,
11
+ Stack,
12
+ Table,
13
+ UL,
9
14
  } from "@weasyprint-tsx/ui";
10
15
  import "./index.css";
11
16
 
@@ -56,148 +61,286 @@ export default function Document() {
56
61
  <link rel="stylesheet" href="index.css" />
57
62
  </head>
58
63
  <body>
59
-
60
64
  {/* ── Cover ── */}
61
- <Page>
62
- <div style={{ marginBottom: "1.5cm" }}>
63
- <H1 fontSize="28pt" color="#1e40af">weasyprint-tsx</H1>
64
- <p style={{ fontSize: "13pt", color: "#475569", marginTop: "0.3cm" }}>
65
- Write PDFs in TSX Bun bundles your Preact components into HTML,
66
- WeasyPrint renders them as PDF.
67
- </p>
68
- <DotLine width="100%" color="#cbd5e1" />
69
- </div>
70
-
71
- <BlockBox gap="1cm">
72
- <Block ratio={1}>
73
- <H3 color="#1e40af">Requirements</H3>
74
- <UL>
75
- <LI>Bun ≥ 1.0</LI>
76
- <LI>WeasyPrint (pip install weasyprint)</LI>
77
- </UL>
78
- </Block>
79
- <Block ratio={1}>
80
- <H3 color="#1e40af">Quick start</H3>
81
- <OL>
82
- <LI>bunx create-weasyprint-tsx my-doc</LI>
83
- <LI>cd my-doc &amp;&amp; bun install</LI>
84
- <LI>bun run dev</LI>
85
- </OL>
86
- </Block>
87
- </BlockBox>
88
- </Page>
65
+ <div style={{ marginBottom: "1.5cm" }}>
66
+ <H1 fontSize="28pt" color="#1e40af">
67
+ weasyprint-tsx
68
+ </H1>
69
+ <p style={{ fontSize: "13pt", color: "#475569", marginTop: "0.3cm" }}>
70
+ Write PDFs in TSX — Bun bundles your Preact components into HTML,
71
+ WeasyPrint renders them as PDF.
72
+ </p>
73
+ <DotLine width="100%" color="#cbd5e1" />
74
+ </div>
89
75
 
76
+ <Stack gap="1cm">
77
+ <div className="w-1/2">
78
+ <H3 color="#1e40af">Requirements</H3>
79
+ <UL>
80
+ <LI>Bun ≥ 1.0</LI>
81
+ <LI>WeasyPrint (pip install weasyprint)</LI>
82
+ </UL>
83
+ </div>
84
+ <div className="w-1/2">
85
+ <H3 color="#1e40af">
86
+ Quick start
87
+ </H3>
88
+ <OL>
89
+ <LI>bunx create-weasyprint-tsx my-doc</LI>
90
+ <LI>cd my-doc &amp;&amp; bun install</LI>
91
+ <LI>bun run dev</LI>
92
+ </OL>
93
+ </div>
94
+ </Stack>
90
95
  <PageBreak />
91
96
 
92
97
  {/* ── Getting Started ── */}
93
- <Page>
94
- <H2 color="#1e40af">Getting Started</H2>
98
+ <H2 color="#1e40af">Getting Started</H2>
95
99
 
96
- <p>
97
- The <code>create-weasyprint-tsx</code> scaffolder generates a ready-to-use
98
- project. After installation, two scripts are available:
99
- </p>
100
+ <p>
101
+ The <code>create-weasyprint-tsx</code> scaffolder generates a
102
+ ready-to-use project. After installation, two scripts are available:
103
+ </p>
100
104
 
101
- <Table orientation="col" headerBg="#1e40af" headerFontSize="9pt" cellFontSize="9pt" borderColor="#e2e8f0">
102
- <Entry content={["Script", "Effect"]} />
103
- <Entry content={["bun run dev", "Watch src/, rebuild on change, serve preview at localhost:3000"]} />
104
- <Entry content={["bun run build", "Build once, write output.pdf"]} />
105
- </Table>
105
+ <Table
106
+ orientation="col"
107
+ headerBg="#1e40af"
108
+ headerFontSize="9pt"
109
+ cellFontSize="9pt"
110
+ borderColor="#e2e8f0"
111
+ >
112
+ <Entry content={["Script", "Effect"]} />
113
+ <Entry
114
+ content={[
115
+ "bun run dev",
116
+ "Watch src/, rebuild on change, serve preview at localhost:3000",
117
+ ]}
118
+ />
119
+ <Entry content={["bun run build", "Build once, write output.pdf"]} />
120
+ </Table>
106
121
 
107
- <H3>Project structure</H3>
108
- <CodeBlock language="plaintext" code={`my-doc/
122
+ <H3>Project structure</H3>
123
+ <CodeBlock
124
+ language="plaintext"
125
+ code={`my-doc/
109
126
  ├── src/
110
127
  │ ├── index.tsx # Document root — export a default Preact component
111
128
  │ └── index.css # Styles — must start with @import "tailwindcss"
112
129
  ├── weasyprint-tsx.config.ts
113
- └── package.json`} />
114
-
115
- <H3>Entry point</H3>
116
- <p>
117
- <code>src/index.tsx</code> must export a default component that renders a
118
- complete HTML document. WeasyPrint receives the full HTML string.
119
- </p>
120
- <CodeBlock language="tsx" code={indexCode} />
121
- </Page>
130
+ └── package.json`}
131
+ />
122
132
 
123
- <PageBreak />
133
+ <H3>Entry point</H3>
134
+ <p>
135
+ <code>src/index.tsx</code> must export a default component that
136
+ renders a complete HTML document. WeasyPrint receives the full HTML
137
+ string.
138
+ </p>
139
+ <CodeBlock language="tsx" code={indexCode} />
124
140
 
125
141
  {/* ── Configuration ── */}
126
- <Page>
127
- <H2 color="#1e40af">Configuration</H2>
128
- <p>
129
- Create <code>weasyprint-tsx.config.ts</code> at the project root to
130
- override any default:
131
- </p>
132
- <CodeBlock language="typescript" code={configCode} />
133
-
134
- <H3>All config fields</H3>
135
-
136
- <Table orientation="col" headerBg="#334155" headerFontSize="8.5pt" cellFontSize="8.5pt" borderColor="#e2e8f0">
137
- <Entry content={["Field", "Default", "Description"]} />
138
- <Entry content={["io.input", "src/index.tsx", "TSX entry point"]} />
139
- <Entry content={["io.output", "output.pdf", "Output PDF path"]} />
140
- <Entry content={["io.buildDir", ".build", "Intermediate HTML directory"]} />
141
- <Entry content={["io.media_type", "print", "CSS media type"]} />
142
- <Entry content={["weasyprint.path", "weasyprint", "Path to WeasyPrint binary"]} />
143
- <Entry content={["weasyprint.dpi", "—", "Output resolution"]} />
144
- <Entry content={["weasyprint.optimize_images", "—", "Compress images in output"]} />
145
- <Entry content={["weasyprint.pdf_forms", "—", "Enable PDF form fields"]} />
146
- <Entry content={["weasyprint.pdf_variant", "—", "pdf/a-1b, pdf/ua-1, …"]} />
147
- <Entry content={["weasyprint.full_fonts", "—", "Embed full font files"]} />
148
- <Entry content={["weasyprint.cache", "—", "Font/image cache folder"]} />
149
- <Entry content={["dev.port", "3000", "Dev server port"]} />
150
- <Entry content={["dev.watch", "src", "Directory to watch"]} />
151
- <Entry content={["pdf.stylesheets", "[]", "Extra CSS files via --stylesheet"]} />
152
- </Table>
153
- </Page>
142
+ <H2 color="#1e40af">Configuration</H2>
143
+ <p>
144
+ Create <code>weasyprint-tsx.config.ts</code> at the project root to
145
+ override any default:
146
+ </p>
147
+ <CodeBlock language="typescript" code={configCode} />
154
148
 
155
- <PageBreak />
149
+ <H3>All config fields</H3>
150
+
151
+ <Table
152
+ orientation="col"
153
+ headerBg="#334155"
154
+ headerFontSize="8.5pt"
155
+ cellFontSize="8.5pt"
156
+ borderColor="#e2e8f0"
157
+ >
158
+ <Entry content={["Field", "Default", "Description"]} />
159
+ <Entry content={["io.input", "src/index.tsx", "TSX entry point"]} />
160
+ <Entry content={["io.output", "output.pdf", "Output PDF path"]} />
161
+ <Entry
162
+ content={["io.buildDir", ".build", "Intermediate HTML directory"]}
163
+ />
164
+ <Entry content={["io.media_type", "print", "CSS media type"]} />
165
+ <Entry
166
+ content={[
167
+ "weasyprint.path",
168
+ "weasyprint",
169
+ "Path to WeasyPrint binary",
170
+ ]}
171
+ />
172
+ <Entry content={["weasyprint.dpi", "—", "Output resolution"]} />
173
+ <Entry
174
+ content={[
175
+ "weasyprint.optimize_images",
176
+ "—",
177
+ "Compress images in output",
178
+ ]}
179
+ />
180
+ <Entry
181
+ content={["weasyprint.pdf_forms", "—", "Enable PDF form fields"]}
182
+ />
183
+ <Entry
184
+ content={["weasyprint.pdf_variant", "—", "pdf/a-1b, pdf/ua-1, …"]}
185
+ />
186
+ <Entry
187
+ content={["weasyprint.full_fonts", "—", "Embed full font files"]}
188
+ />
189
+ <Entry
190
+ content={["weasyprint.cache", "—", "Font/image cache folder"]}
191
+ />
192
+ <Entry content={["dev.port", "3000", "Dev server port"]} />
193
+ <Entry content={["dev.watch", "src", "Directory to watch"]} />
194
+ <Entry
195
+ content={[
196
+ "pdf.stylesheets",
197
+ "[]",
198
+ "Extra CSS files via --stylesheet",
199
+ ]}
200
+ />
201
+ </Table>
156
202
 
157
203
  {/* ── UI Components ── */}
158
- <Page>
159
- <H2 color="#1e40af">UI Components</H2>
160
- <p>
161
- <code>@weasyprint-tsx/ui</code> provides print-optimized Preact components.
162
- Import everything from the package root:
163
- </p>
164
- <CodeBlock language="ts" code={`import { H1, H2, Page, PageBreak, UL, OL, LI, Table, Entry, BlockBox, Block, DotLine, CodeBlock, Equation } from "@weasyprint-tsx/ui";`} />
165
-
166
- <H3>Page &amp; Layout</H3>
167
- <Table orientation="row" headerBg="#f1f5f9" borderColor="#e2e8f0" headerFontSize="8.5pt" cellFontSize="8.5pt">
168
- <Entry content={["Page", "Wraps a page section (<section>). Accepts page?: string for named page rules."]} />
169
- <Entry content={["PageBreak", "Inserts a CSS page break. No props."]} />
170
- <Entry content={["BlockBox", "Multi-column flex container. Props: gap, basis, centered, align."]} />
171
- <Entry content={["Block", "Column inside BlockBox. Props: ratio, centered, align."]} />
172
- </Table>
173
-
174
- <H3>Headings</H3>
175
- <Table orientation="row" headerBg="#f1f5f9" borderColor="#e2e8f0" headerFontSize="8.5pt" cellFontSize="8.5pt">
176
- <Entry content={["H1 – H6", "Render h1–h6. Props: marker (data-marker for CSS counters), color, fontSize."]} />
177
- <Entry content={["ResetCounter", "Hidden div that resets CSS counters. Use at the top of numbered sections."]} />
178
- </Table>
179
-
180
- <H3>Lists</H3>
181
- <Table orientation="row" headerBg="#f1f5f9" borderColor="#e2e8f0" headerFontSize="8.5pt" cellFontSize="8.5pt">
182
- <Entry content={["UL", "Unordered list. Props: marker (bullet char), gap, pre."]} />
183
- <Entry content={["OL", "Ordered list. Props: start, counterType (decimal/lower-alpha/upper-roman/…), markerPre, markerPost, gap, pre."]} />
184
- <Entry content={["LI", "List item. Props: marker (override bullet), count (override counter)."]} />
185
- </Table>
186
-
187
- <H3>Table</H3>
188
- <Table orientation="row" headerBg="#f1f5f9" borderColor="#e2e8f0" headerFontSize="8.5pt" cellFontSize="8.5pt">
189
- <Entry content={["Table", `orientation "col" (default): first Entry is the header row. orientation "row": first cell of each Entry is the row header. Also: headerBg, cellBg, borderWidth, borderColor, headerFontSize, cellFontSize.`]} />
190
- <Entry content={["Entry", "Data row/column. content: ComponentChild[] holds cell values. Accepts per-entry overrides: headerBg, cellBg."]} />
191
- </Table>
192
-
193
- <H3>Inline elements</H3>
194
- <Table orientation="row" headerBg="#f1f5f9" borderColor="#e2e8f0" headerFontSize="8.5pt" cellFontSize="8.5pt">
195
- <Entry content={["DotLine", "Repeating dot fill line. Props: num, width, inline, color, lineHeight."]} />
196
- <Entry content={["CodeBlock", "Syntax-highlighted code (highlight.js). Props: language, code."]} />
197
- <Entry content={["Equation", "LaTeX via KaTeX. Props: tex, displayMode, aligned, chemical."]} />
198
- </Table>
199
- </Page>
204
+ <H2 color="#1e40af">UI Components</H2>
205
+ <p>
206
+ <code>@weasyprint-tsx/ui</code> provides print-optimized Preact
207
+ components. Import everything from the package root:
208
+ </p>
209
+ <CodeBlock
210
+ language="ts"
211
+ code={`import { H1, H2, Page, PageBreak, UL, OL, LI, Table, Entry, BlockBox, Block, DotLine, CodeBlock, Equation } from "@weasyprint-tsx/ui";`}
212
+ />
213
+
214
+ <H3>Page &amp; Layout</H3>
215
+ <Table
216
+ orientation="row"
217
+ headerBg="#f1f5f9"
218
+ borderColor="#e2e8f0"
219
+ headerFontSize="8.5pt"
220
+ cellFontSize="8.5pt"
221
+ >
222
+ <Entry
223
+ content={[
224
+ "Page",
225
+ "Wraps a page section (<section>). Accepts page?: string for named page rules.",
226
+ ]}
227
+ />
228
+ <Entry
229
+ content={["PageBreak", "Inserts a CSS page break. No props."]}
230
+ />
231
+ <Entry
232
+ content={[
233
+ "BlockBox",
234
+ "Multi-column flex container. Props: gap, basis, centered, align.",
235
+ ]}
236
+ />
237
+ <Entry
238
+ content={[
239
+ "Block",
240
+ "Column inside BlockBox. Props: ratio, centered, align.",
241
+ ]}
242
+ />
243
+ </Table>
244
+
245
+ <H3>Headings</H3>
246
+ <Table
247
+ orientation="row"
248
+ headerBg="#f1f5f9"
249
+ borderColor="#e2e8f0"
250
+ headerFontSize="8.5pt"
251
+ cellFontSize="8.5pt"
252
+ >
253
+ <Entry
254
+ content={[
255
+ "H1 – H6",
256
+ "Render h1–h6. Props: marker (data-marker for CSS counters), color, fontSize.",
257
+ ]}
258
+ />
259
+ <Entry
260
+ content={[
261
+ "ResetCounter",
262
+ "Hidden div that resets CSS counters. Use at the top of numbered sections.",
263
+ ]}
264
+ />
265
+ </Table>
266
+
267
+ <H3>Lists</H3>
268
+ <Table
269
+ orientation="row"
270
+ headerBg="#f1f5f9"
271
+ borderColor="#e2e8f0"
272
+ headerFontSize="8.5pt"
273
+ cellFontSize="8.5pt"
274
+ >
275
+ <Entry
276
+ content={[
277
+ "UL",
278
+ "Unordered list. Props: marker (bullet char), gap, pre.",
279
+ ]}
280
+ />
281
+ <Entry
282
+ content={[
283
+ "OL",
284
+ "Ordered list. Props: start, counterType (decimal/lower-alpha/upper-roman/…), markerPre, markerPost, gap, pre.",
285
+ ]}
286
+ />
287
+ <Entry
288
+ content={[
289
+ "LI",
290
+ "List item. Props: marker (override bullet), count (override counter).",
291
+ ]}
292
+ />
293
+ </Table>
294
+
295
+ <H3>Table</H3>
296
+ <Table
297
+ orientation="row"
298
+ headerBg="#f1f5f9"
299
+ borderColor="#e2e8f0"
300
+ headerFontSize="8.5pt"
301
+ cellFontSize="8.5pt"
302
+ >
303
+ <Entry
304
+ content={[
305
+ "Table",
306
+ `orientation "col" (default): first Entry is the header row. orientation "row": first cell of each Entry is the row header. Also: headerBg, cellBg, borderWidth, borderColor, headerFontSize, cellFontSize.`,
307
+ ]}
308
+ />
309
+ <Entry
310
+ content={[
311
+ "Entry",
312
+ "Data row/column. content: ComponentChild[] holds cell values. Accepts per-entry overrides: headerBg, cellBg.",
313
+ ]}
314
+ />
315
+ </Table>
200
316
 
317
+ <H3>Inline elements</H3>
318
+ <Table
319
+ orientation="row"
320
+ headerBg="#f1f5f9"
321
+ borderColor="#e2e8f0"
322
+ headerFontSize="8.5pt"
323
+ cellFontSize="8.5pt"
324
+ >
325
+ <Entry
326
+ content={[
327
+ "DotLine",
328
+ "Repeating dot fill line. Props: num, width, inline, color, lineHeight.",
329
+ ]}
330
+ />
331
+ <Entry
332
+ content={[
333
+ "CodeBlock",
334
+ "Syntax-highlighted code (highlight.js). Props: language, code.",
335
+ ]}
336
+ />
337
+ <Entry
338
+ content={[
339
+ "Equation",
340
+ "LaTeX via KaTeX. Props: tex, displayMode, aligned, chemical.",
341
+ ]}
342
+ />
343
+ </Table>
201
344
  </body>
202
345
  </html>
203
346
  );
@@ -0,0 +1 @@
1
+ @import "tailwindcss";
@@ -0,0 +1,20 @@
1
+ import { Stack } from "@weasyprint-tsx/ui";
2
+ import "./index.css";
3
+
4
+ export default function Document() {
5
+ return (
6
+ <html>
7
+ <head>
8
+ <meta charset="UTF-8" />
9
+ <title>weasyprint-tsx</title>
10
+ <link rel="stylesheet" href="index.css" />
11
+ </head>
12
+ <body>
13
+ <Stack gap={"1cm"} className="border-2 border-solid h-125 py-5">
14
+ <div className="bg-green-800 size-50"></div>
15
+ <div className="bg-blue-800 size-50"></div>
16
+ </Stack>
17
+ </body>
18
+ </html>
19
+ );
20
+ }
@@ -4,4 +4,12 @@ export default {
4
4
  io: {
5
5
  output: "ReadMe.pdf",
6
6
  },
7
+
8
+ // io: {
9
+ // output: "TestUI.pdf",
10
+ // input: "testUI/index.tsx",
11
+ // },
12
+ // dev: {
13
+ // watch: ["packages/ui/", "testUI"],
14
+ // },
7
15
  } satisfies Config;