weasyprint-tsx 0.0.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.
Files changed (41) hide show
  1. package/README.md +1 -0
  2. package/ReadMe.pdf +0 -0
  3. package/bun.lock +166 -0
  4. package/global.d.ts +16 -0
  5. package/package.json +11 -0
  6. package/packages/build/package.json +20 -0
  7. package/packages/build/src/build.ts +100 -0
  8. package/packages/build/src/cli.ts +13 -0
  9. package/packages/build/src/config.ts +90 -0
  10. package/packages/build/src/orchestrator.ts +61 -0
  11. package/packages/build/src/server.ts +36 -0
  12. package/packages/create/cli.js +28 -0
  13. package/packages/create/package.json +10 -0
  14. package/packages/create/template/global.d.ts +16 -0
  15. package/packages/create/template/package.json +18 -0
  16. package/packages/create/template/src/index.css +21 -0
  17. package/packages/create/template/src/index.tsx +25 -0
  18. package/packages/create/template/tsconfig.json +11 -0
  19. package/packages/create/template/weasyprint-tsx.config.ts +17 -0
  20. package/packages/ui/package.json +8 -0
  21. package/packages/ui/src/BlockBox.module.css +16 -0
  22. package/packages/ui/src/BlockBox.tsx +66 -0
  23. package/packages/ui/src/CodeBlock.tsx +17 -0
  24. package/packages/ui/src/DotLine.module.css +24 -0
  25. package/packages/ui/src/DotLine.tsx +45 -0
  26. package/packages/ui/src/Equation.module.css +4 -0
  27. package/packages/ui/src/Equation.tsx +38 -0
  28. package/packages/ui/src/List.module.css +53 -0
  29. package/packages/ui/src/List.tsx +120 -0
  30. package/packages/ui/src/Page.module.css +14 -0
  31. package/packages/ui/src/Page.tsx +33 -0
  32. package/packages/ui/src/Table.module.css +55 -0
  33. package/packages/ui/src/Table.tsx +164 -0
  34. package/packages/ui/src/Title.module.css +75 -0
  35. package/packages/ui/src/Titles.tsx +105 -0
  36. package/packages/ui/src/index.ts +9 -0
  37. package/packages/ui/src/utils.tsx +26 -0
  38. package/src/index.css +0 -0
  39. package/src/index.tsx +16 -0
  40. package/tsconfig.json +16 -0
  41. package/weasyprint-tsx.config.ts +7 -0
@@ -0,0 +1,25 @@
1
+ import "./index.css";
2
+
3
+ import { H1, H2, LI, UL } from "@weasyprint-tsx/ui";
4
+
5
+ export default function Document() {
6
+ return (
7
+ <html lang="en">
8
+ <head>
9
+ <meta charSet="utf-8" />
10
+ <title>My Document</title>
11
+ <link rel="stylesheet" href="index.css" />
12
+ </head>
13
+ <body>
14
+ <H1>My Document</H1>
15
+ <H2>Introduction</H2>
16
+ <p>Edit <code>src/index.tsx</code> to start writing your document.</p>
17
+ <UL>
18
+ <LI>Use components from <code>@weasyprint-tsx/ui</code></LI>
19
+ <LI>Style with Tailwind utility classes or plain CSS in <code>src/index.css</code></LI>
20
+ <LI>Run <code>bun run dev</code> for live preview</LI>
21
+ </UL>
22
+ </body>
23
+ </html>
24
+ );
25
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "compilerOptions": {
3
+ "jsx": "react-jsx",
4
+ "jsxImportSource": "preact",
5
+ "types": ["bun-types"],
6
+ "strict": true,
7
+ "module": "ESNext",
8
+ "moduleResolution": "bundler",
9
+ "target": "ESNext"
10
+ }
11
+ }
@@ -0,0 +1,17 @@
1
+ import type { Config } from "@weasyprint-tsx/build";
2
+
3
+ const config: Config = {
4
+ io: {
5
+ engine: "weasyprint",
6
+ output: "output.pdf",
7
+ },
8
+ weasyprint: {
9
+ pdf_forms: true,
10
+ },
11
+ watchdog: {
12
+ path: "./src",
13
+ debounce: 500,
14
+ },
15
+ };
16
+
17
+ export default config;
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@weasyprint-tsx/ui",
3
+ "version" : "0.0.1",
4
+ "private": true,
5
+ "workspaces": [
6
+ "packages/*"
7
+ ]
8
+ }
@@ -0,0 +1,16 @@
1
+ .container {
2
+ margin-left: calc(-1 * var(--block-box-gap, 0));
3
+ }
4
+
5
+ .block {
6
+ display: inline-block;
7
+ box-sizing: border-box;
8
+ width: calc(var(--ratio, 1) * 100% / var(--block-box-basis, 1));
9
+ vertical-align: var(--block-box-align, middle);
10
+ padding-left: var(--block-box-gap, 0);
11
+ }
12
+
13
+ .centered > * {
14
+ display: block;
15
+ margin: 0 auto;
16
+ }
@@ -0,0 +1,66 @@
1
+ import { ComponentProps, toChildArray, VNode } from "preact";
2
+ import styles from "./BlockBox.module.css";
3
+ import { joinClasses, mergeStyle } from "./utils";
4
+
5
+ interface BlockBoxProps extends ComponentProps<"div"> {
6
+ children: VNode<BlockProps>[] | VNode<BlockProps>;
7
+ gap?: string;
8
+ basis?: number;
9
+ centered?: boolean;
10
+ align?: "middle" | "top" | "bottom";
11
+ }
12
+
13
+ interface BlockProps extends ComponentProps<"div"> {
14
+ ratio?: number;
15
+ centered?: boolean;
16
+ align?: "middle" | "top" | "bottom";
17
+ }
18
+
19
+ export function Block(_props: BlockProps): null {
20
+ return null;
21
+ }
22
+
23
+ export function BlockBox({
24
+ children,
25
+ className,
26
+ basis,
27
+ gap,
28
+ align,
29
+ centered: parentCentered = true,
30
+ style,
31
+ ...props
32
+ }: BlockBoxProps) {
33
+ const blockList = toChildArray(children).filter(
34
+ (child) => (child as VNode).type === Block,
35
+ ) as VNode<BlockProps>[];
36
+
37
+ const blockBasis =
38
+ basis ?? blockList.reduce((acc, b) => acc + (b.props.ratio ?? 1), 0);
39
+
40
+ const child = blockList.map(
41
+ ({ props: { style, ratio = 1, className, centered, align, ...props } }) => (
42
+ <div
43
+ style={mergeStyle(style, { "--ratio": ratio, "--block-box-align": align })}
44
+ className={joinClasses(
45
+ className,
46
+ styles.block,
47
+ (centered ?? parentCentered) ? styles.centered : undefined,
48
+ )}
49
+ {...props}
50
+ />
51
+ ),
52
+ );
53
+
54
+ return (
55
+ <div
56
+ children={child}
57
+ className={joinClasses(className, styles.container)}
58
+ style={mergeStyle(style, {
59
+ "--block-box-basis": blockBasis,
60
+ "--block-box-gap": gap,
61
+ "--block-box-align": align,
62
+ })}
63
+ {...props}
64
+ />
65
+ );
66
+ }
@@ -0,0 +1,17 @@
1
+ import hljs from "highlight.js";
2
+ import "highlight.js/styles/atom-one-dark.css";
3
+ import { ComponentProps } from "preact";
4
+
5
+ interface CodeBlockProps extends Omit<ComponentProps<"code">, "children"> {
6
+ language: string;
7
+ code: string;
8
+ }
9
+
10
+ export function CodeBlock({ code, language, ...props }: CodeBlockProps) {
11
+ const codeBlock = hljs.highlight(code, { language: language });
12
+ return (
13
+ <pre style={{breakInside : "avoid"}}>
14
+ <code {...props} dangerouslySetInnerHTML={{ __html: codeBlock.value }} />
15
+ </pre>
16
+ );
17
+ }
@@ -0,0 +1,24 @@
1
+ /* block mode: stacked answer lines */
2
+ .block {
3
+ display: block;
4
+ }
5
+
6
+ .line {
7
+ display: block;
8
+ min-height: var(--dotline-height, 6mm);
9
+
10
+ margin-bottom: 2mm;
11
+ border-bottom: 1px dotted var(--dotline-color, #808080);
12
+ &:is(:last-child) {
13
+ margin-bottom: 3mm;
14
+ }
15
+ }
16
+
17
+ /* inline mode: fill-in-the-blank inside <p> */
18
+ .inline {
19
+ display: inline-block;
20
+ vertical-align: baseline;
21
+ height: 0;
22
+ margin: 0 1mm;
23
+ border-bottom: 1px dotted var(--dotline-color, #808080);
24
+ }
@@ -0,0 +1,45 @@
1
+ import { ComponentProps } from "preact";
2
+ import styles from "./DotLine.module.css";
3
+ import { joinClasses, mergeStyle } from "./utils";
4
+
5
+ interface DotLineProps extends ComponentProps<"span"> {
6
+ num?: number;
7
+ width?: number | string;
8
+ inline?: boolean;
9
+ color?: string;
10
+ lineHeight?: string;
11
+ }
12
+
13
+ export function DotLine({
14
+ num = 1,
15
+ children,
16
+ width = "100%",
17
+ style,
18
+ className = "",
19
+ inline,
20
+ color,
21
+ lineHeight,
22
+ ...props
23
+ }: DotLineProps) {
24
+ if (num === 0) return;
25
+ const css = mergeStyle(style, { width, "--dotline-color": color, "--dotline-height": lineHeight });
26
+
27
+ if (inline)
28
+ return (
29
+ <span
30
+ className={joinClasses(className, styles.inline)}
31
+ style={css}
32
+ {...props}
33
+ />
34
+ );
35
+
36
+ return (
37
+ <span className={joinClasses(className, styles.block)} style={css} {...props}>
38
+ {Array.from({ length: num }, (_, k) => (
39
+ <span key={k} className={styles.line}>
40
+ {k === 0 ? children : null}
41
+ </span>
42
+ ))}
43
+ </span>
44
+ );
45
+ }
@@ -0,0 +1,4 @@
1
+ .equation {
2
+ /* display: block; */
3
+ padding: 2px;
4
+ }
@@ -0,0 +1,38 @@
1
+ import { renderToString } from "katex";
2
+ import "katex/contrib/mhchem";
3
+ import "katex/dist/katex.min.css";
4
+ import { ComponentProps } from "preact";
5
+ import styles from "./Equation.module.css";
6
+ import { joinClasses } from "./utils";
7
+
8
+ interface EquationProps extends Omit<ComponentProps<"span">, "children"> {
9
+ tex: string;
10
+ displayMode?: boolean;
11
+ aligned?: boolean;
12
+ chemical?: boolean;
13
+ }
14
+ export function Equation({
15
+ tex,
16
+ displayMode,
17
+ className = "",
18
+ aligned = false,
19
+ chemical = false,
20
+ ...props
21
+ }: EquationProps) {
22
+ const alignedCode = aligned
23
+ ? `\\begin{aligned}
24
+ ${tex}
25
+ \\end{aligned}`
26
+ : tex;
27
+ const chemCode = chemical ? `\\ce{${alignedCode}}` : alignedCode;
28
+
29
+ return (
30
+ <span
31
+ dangerouslySetInnerHTML={{
32
+ __html: renderToString(chemCode, { displayMode }),
33
+ }}
34
+ className={joinClasses(className, styles.equation)}
35
+ {...props}
36
+ />
37
+ );
38
+ }
@@ -0,0 +1,53 @@
1
+ .ol {
2
+ & > .li {
3
+ counter-increment: list-items;
4
+ &::before {
5
+ content: var(--ol-marker-pre, "") counter(list-items, decimal)
6
+ var(--ol-marker-post, ". ");
7
+ margin-right: 1mm;
8
+ }
9
+ }
10
+ &[data-counter="lower-alpha"] .li::before {
11
+ content: var(--ol-marker-pre, "") counter(list-items, lower-alpha)
12
+ var(--ol-marker-post, ". ");
13
+ }
14
+ &[data-counter="upper-alpha"] .li::before {
15
+ content: var(--ol-marker-pre, "") counter(list-items, upper-alpha)
16
+ var(--ol-marker-post, ". ");
17
+ }
18
+ &[data-counter="lower-roman"] .li::before {
19
+ content: var(--ol-marker-pre, "") counter(list-items, lower-roman)
20
+ var(--ol-marker-post, ". ");
21
+ }
22
+ &[data-counter="upper-roman"] .li::before {
23
+ content: var(--ol-marker-pre, "") counter(list-items, upper-roman)
24
+ var(--ol-marker-post, ". ");
25
+ }
26
+ }
27
+
28
+ .ul {
29
+ .li {
30
+ &::before {
31
+ content: var(--ul-marker, "•");
32
+ margin-right: 1mm;
33
+ }
34
+ }
35
+ }
36
+
37
+ /* hanging indent so wrapped text aligns under content, not marker */
38
+ .li {
39
+ &:not(:last-child) {
40
+ margin-bottom: var(--list-gap, 0);
41
+ }
42
+ }
43
+
44
+ /* pre+body side-by-side on an inner wrapper — outer div stays unstyled */
45
+ .preWrapper {
46
+ display: grid;
47
+ grid-template-columns: max-content 1fr;
48
+ align-items: start;
49
+ }
50
+
51
+ .pre {
52
+ padding-right: 1mm;
53
+ }
@@ -0,0 +1,120 @@
1
+ import { ComponentChildren, ComponentProps } from "preact";
2
+ import styles from "./List.module.css";
3
+ import { cssString, joinClasses, mergeStyle } from "./utils";
4
+
5
+ export type CounterType =
6
+ | "decimal"
7
+ | "lower-alpha"
8
+ | "upper-alpha"
9
+ | "lower-roman"
10
+ | "upper-roman";
11
+
12
+ interface ListItemProps extends ComponentProps<"div"> {
13
+ count?: number;
14
+ marker?: string;
15
+ }
16
+
17
+ interface ListProps extends ComponentProps<"div"> {
18
+ pre?: ComponentChildren;
19
+ gap?: string | number;
20
+ }
21
+
22
+ interface OLProps extends ListProps {
23
+ start?: number;
24
+ counterType?: CounterType;
25
+ markerPre?: string;
26
+ markerPost?: string;
27
+ }
28
+
29
+ interface ULProps extends ListProps {
30
+ marker?: string;
31
+ }
32
+
33
+ export function LI({
34
+ className = "",
35
+ count = NaN,
36
+ style,
37
+ marker,
38
+ ...props
39
+ }: ListItemProps) {
40
+ const css = mergeStyle(style, {
41
+ counterReset: count ? `list-items ${count - 1}` : undefined,
42
+ "--ul-marker": cssString(marker),
43
+ });
44
+ return (
45
+ <div style={css} {...props} className={joinClasses(className, styles.li)} />
46
+ );
47
+ }
48
+
49
+ function Body({
50
+ pre,
51
+ children,
52
+ }: {
53
+ pre?: ComponentChildren;
54
+ children: ComponentChildren;
55
+ }) {
56
+ if (!pre) return <>{children}</>;
57
+ return (
58
+ <div className={styles.preWrapper}>
59
+ <div className={styles.pre}>{pre}</div>
60
+ <div>{children}</div>
61
+ </div>
62
+ );
63
+ }
64
+
65
+ export function OL({
66
+ className = "",
67
+ start = 1,
68
+ counterType = "decimal",
69
+ markerPre,
70
+ markerPost,
71
+ style,
72
+ pre,
73
+ gap,
74
+ children,
75
+ ...props
76
+ }: OLProps) {
77
+ const css = mergeStyle(style, {
78
+ counterReset: `list-items ${start - 1}`,
79
+ "--ol-marker-pre": cssString(markerPre),
80
+ "--ol-marker-post": cssString(markerPost),
81
+ "--list-gap": gap,
82
+ });
83
+ return (
84
+ <div
85
+ {...props}
86
+ className={joinClasses(className, styles.ol)}
87
+ style={css}
88
+ data-counter={counterType}
89
+ >
90
+ <Body pre={pre}>{children}</Body>
91
+ </div>
92
+ );
93
+ }
94
+
95
+ export function UL({
96
+ className = "",
97
+ marker,
98
+ style,
99
+ children,
100
+ pre,
101
+ gap,
102
+ ...props
103
+ }: ULProps) {
104
+ const css = mergeStyle(style, {
105
+ "--ul-marker": cssString(marker),
106
+ "--list-gap": gap,
107
+ });
108
+ return (
109
+ <div {...props} className={joinClasses(className, styles.ul)} style={css}>
110
+ <Body pre={pre}>{children}</Body>
111
+ </div>
112
+ );
113
+ }
114
+
115
+ export function List({
116
+ kind,
117
+ ...props
118
+ }: ListProps & { kind: "ul" | "ol"; start?: number }) {
119
+ return kind === "ul" ? <UL {...props} /> : <OL {...props} />;
120
+ }
@@ -0,0 +1,14 @@
1
+ .pagebreak {
2
+ break-before: always;
3
+ height: 0;
4
+ width: 0;
5
+ margin: 0;
6
+ padding: 0;
7
+ }
8
+
9
+ .page {
10
+ min-height: 100%;
11
+ width: 100%;
12
+ break-before: always;
13
+ break-after: always;
14
+ }
@@ -0,0 +1,33 @@
1
+ import { ComponentProps } from "preact";
2
+ import styles from "./Page.module.css";
3
+ import { joinClasses, mergeStyle } from "./utils";
4
+ interface PageProps {
5
+ page?: string;
6
+ }
7
+
8
+ export function PageBreak({ page }: PageProps) {
9
+ return (
10
+ <div
11
+ style={{
12
+ page,
13
+ }}
14
+ className={styles.pagebreak}
15
+ />
16
+ );
17
+ }
18
+ export function Page({
19
+ page,
20
+ style,
21
+ className = "",
22
+ ...props
23
+ }: PageProps & ComponentProps<"section">) {
24
+ const css = mergeStyle(style, { page });
25
+
26
+ return (
27
+ <section
28
+ style={css}
29
+ className={joinClasses(className, styles.page)}
30
+ {...props}
31
+ />
32
+ );
33
+ }
@@ -0,0 +1,55 @@
1
+ .table {
2
+ --table-border-width: 1px;
3
+ --table-border-color: currentColor;
4
+ border-collapse: collapse;
5
+ }
6
+
7
+ .table th,
8
+ .table td {
9
+ text-align: center;
10
+ vertical-align: middle;
11
+ }
12
+
13
+ .table th {
14
+ background-color: var(--table-header-color, transparent);
15
+ font-size: var(--table-header-fontsize, inherit);
16
+ }
17
+
18
+ .table td {
19
+ background-color: var(--table-cell-color, transparent);
20
+ font-size: var(--table-cell-fontsize, inherit);
21
+ }
22
+
23
+ /* ── Row orientation ──────────────────────────────────────────── */
24
+ .row td,
25
+ .row th {
26
+ border-right: var(--table-border-width, 1px) solid var(--table-border-color, currentColor);
27
+ border-bottom: var(--table-border-width, 1px) solid var(--table-border-color, currentColor);
28
+ }
29
+
30
+ .row tr td:last-child,
31
+ .row tr th:last-child {
32
+ border-right: none;
33
+ }
34
+
35
+ .row tr:last-child td,
36
+ .row tr:last-child th {
37
+ border-bottom: none;
38
+ }
39
+
40
+ /* ── Col orientation ──────────────────────────────────────────── */
41
+ .col td,
42
+ .col th {
43
+ border-right: var(--table-border-width, 1px) solid var(--table-border-color, currentColor);
44
+ border-bottom: var(--table-border-width, 1px) solid var(--table-border-color, currentColor);
45
+ }
46
+
47
+ .col tr td:last-child,
48
+ .col tr th:last-child {
49
+ border-right: none;
50
+ }
51
+
52
+ .col tbody tr:last-child td,
53
+ .col tbody tr:last-child th {
54
+ border-bottom: none;
55
+ }