takumi-paint 0.0.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.md +90 -1
- package/bundlers/browser.mjs +7 -0
- package/bundlers/bun.mjs +9 -0
- package/bundlers/next.mjs +11 -0
- package/bundlers/node.cjs +10 -0
- package/bundlers/node.d.cts +3 -0
- package/bundlers/node.d.ts +2 -0
- package/bundlers/node.mjs +9 -0
- package/bundlers/vite.mjs +46 -0
- package/bundlers/wasm-url.cjs +4 -0
- package/bundlers/wasm-url.d.cts +3 -0
- package/bundlers/wasm-url.d.ts +3 -0
- package/bundlers/wasm-url.mjs +1 -0
- package/bundlers/workerd.mjs +7 -0
- package/dist/export.cjs +790 -0
- package/dist/export.d.cts +435 -0
- package/dist/export.d.mts +435 -0
- package/dist/export.mjs +783 -0
- package/package.json +149 -5
- package/pkg/takumi_paint_wasm_bg.wasm +0 -0
- package/pkg/takumi_paint_wasm_bg.wasm.d.ts +19 -0
package/README.md
CHANGED
|
@@ -1,3 +1,92 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://takumi.kane.tw/logo.svg" alt="Takumi" width="64" />
|
|
3
|
+
|
|
1
4
|
# takumi-paint
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
**Lay out JSX, HTML, and CSS. Get back what to draw instead of pixels.**
|
|
7
|
+
|
|
8
|
+
Every box, gradient, shadow, text run, and image, in paint order, with each CSS value resolved.
|
|
9
|
+
|
|
10
|
+
[Documentation](https://takumi.kane.tw/docs/paint) · [Reference](https://takumi.kane.tw/docs/paint/reference)
|
|
11
|
+
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
## What it is for
|
|
15
|
+
|
|
16
|
+
Takumi's other packages end in a PNG, an SVG, or a PDF. Use `takumi-paint` when you own the output:
|
|
17
|
+
|
|
18
|
+
- Write slides where text stays editable, such as PPTX.
|
|
19
|
+
- Draw onto Canvas, Skia, or a native drawing API.
|
|
20
|
+
- Read where layout put each box and line of text.
|
|
21
|
+
|
|
22
|
+
It runs the same layout as the image, SVG, and PDF renderers, in WebAssembly. Where they draw, it records the **paint tree**.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install takumi-paint
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { paint } from "takumi-paint";
|
|
34
|
+
|
|
35
|
+
const tree = await paint(
|
|
36
|
+
<div id="card" style={{ width: 240, padding: 16, background: "#F7F3EC" }}>
|
|
37
|
+
Hello <b>world</b>
|
|
38
|
+
</div>,
|
|
39
|
+
{ width: 320 },
|
|
40
|
+
);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`tree.nodes` lists every node. Each node lists what it draws:
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
const card = tree.nodes.find((node) => node.type === "box" && node.element?.id === "card");
|
|
47
|
+
console.log(card?.drawables[0]); // { type: "fill", role: "background", shape: { type: "rect", … }, … }
|
|
48
|
+
|
|
49
|
+
for (const node of tree.nodes) {
|
|
50
|
+
if (node.type !== "text") continue;
|
|
51
|
+
for (const run of node.runs) console.log(run.text, run.font.family, run.fontSize);
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`tree.steps` is the draw order. Replay it from the bottom up:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
for (const step of tree.steps) {
|
|
59
|
+
if (step.type !== "draw") continue; // begin-group, end-group, begin-clip, and end-clip wrap draws
|
|
60
|
+
ctx.setTransform(...step.node.transform);
|
|
61
|
+
for (const drawable of step.drawables) draw(ctx, drawable);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`paint()` also takes an HTML string or a Takumi node tree.
|
|
66
|
+
|
|
67
|
+
## What the tree holds
|
|
68
|
+
|
|
69
|
+
| Part | Contents |
|
|
70
|
+
| --------- | --------------------------------------------------------------------------- |
|
|
71
|
+
| Nodes | `box`, `text`, and `image`, each placed on the canvas by its `transform` |
|
|
72
|
+
| Drawables | Fills, strokes, shadows, glyph runs, and images, tagged with their CSS role |
|
|
73
|
+
| Paints | Colors, gradients, images, and tiled patterns |
|
|
74
|
+
| Text runs | Text, font, size, line height, and baseline, plus glyphs and outlines |
|
|
75
|
+
| Steps | Draws, clips, and groups for opacity, blending, filters, and masks |
|
|
76
|
+
|
|
77
|
+
Lengths are device pixels.
|
|
78
|
+
|
|
79
|
+
## Learn more
|
|
80
|
+
|
|
81
|
+
| Need | Read |
|
|
82
|
+
| ------------------------------ | -------------------------------------------------------- |
|
|
83
|
+
| See the tree, step by step | [Paint](https://takumi.kane.tw/docs/paint) |
|
|
84
|
+
| Draw nodes in order | [Drawing](https://takumi.kane.tw/docs/paint/drawing) |
|
|
85
|
+
| Place text or lay it out again | [Text](https://takumi.kane.tw/docs/paint/text) |
|
|
86
|
+
| Check options and field types | [Reference](https://takumi.kane.tw/docs/paint/reference) |
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
MIT or Apache-2.0
|
|
91
|
+
|
|
92
|
+
Bundled third-party code is listed in the [third-party notices](https://github.com/kane50613/takumi/blob/master/THIRD-PARTY-NOTICES.md).
|
package/bundlers/bun.mjs
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as wasm from "../dist/export.mjs";
|
|
2
|
+
import wasmPath from "../pkg/takumi_paint_wasm_bg.wasm";
|
|
3
|
+
|
|
4
|
+
const wasmBytes = await Bun.file(new URL(wasmPath, import.meta.url)).arrayBuffer();
|
|
5
|
+
|
|
6
|
+
wasm.initSync({ module: wasmBytes });
|
|
7
|
+
|
|
8
|
+
export * from "../dist/export.mjs";
|
|
9
|
+
export default wasm.default;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import module from "../pkg/takumi_paint_wasm_bg.wasm?module";
|
|
2
|
+
import * as wasm from "../dist/export.mjs";
|
|
3
|
+
|
|
4
|
+
// typeof module says its Module { } but instanceof WebAssembly.Module is false
|
|
5
|
+
// Have to force override the prototype to be WebAssembly.Module
|
|
6
|
+
Object.setPrototypeOf(module, WebAssembly.Module.prototype);
|
|
7
|
+
|
|
8
|
+
wasm.initSync({ module });
|
|
9
|
+
|
|
10
|
+
export * from "../dist/export.mjs";
|
|
11
|
+
export default wasm.default;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const wasm = require("../dist/export.cjs");
|
|
2
|
+
const { readFileSync } = require("node:fs");
|
|
3
|
+
const { join } = require("node:path");
|
|
4
|
+
|
|
5
|
+
const wasmPath = join(__dirname, "../pkg/takumi_paint_wasm_bg.wasm");
|
|
6
|
+
const wasmBytes = readFileSync(wasmPath);
|
|
7
|
+
|
|
8
|
+
wasm.initSync({ module: wasmBytes });
|
|
9
|
+
|
|
10
|
+
module.exports = wasm;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import * as wasm from "../dist/export.mjs";
|
|
3
|
+
|
|
4
|
+
const wasmBytes = readFileSync(new URL("../pkg/takumi_paint_wasm_bg.wasm", import.meta.url));
|
|
5
|
+
|
|
6
|
+
wasm.initSync({ module: wasmBytes });
|
|
7
|
+
|
|
8
|
+
export * from "../dist/export.mjs";
|
|
9
|
+
export default wasm.default;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import url from "../pkg/takumi_paint_wasm_bg.wasm?url";
|
|
2
|
+
import * as wasm from "../dist/export.mjs";
|
|
3
|
+
|
|
4
|
+
// `?url` is a browser path; map it to a file for server reads. Vite emits the asset next to the
|
|
5
|
+
// importing server chunk, so resolve relative to import.meta.url, not the framework output dir.
|
|
6
|
+
async function resolveWasm() {
|
|
7
|
+
if (typeof process !== "undefined" && process.versions?.node != null) {
|
|
8
|
+
const { readFile } = process.getBuiltinModule("node:fs/promises");
|
|
9
|
+
const path = decodeURIComponent(url.replace(/[?#].*$/, ""));
|
|
10
|
+
|
|
11
|
+
// Dev SSR serves an `/@fs/<abs-path>` URL.
|
|
12
|
+
if (path.startsWith("/@fs/")) {
|
|
13
|
+
return readFile(path.slice("/@fs".length));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!path.startsWith("/")) {
|
|
17
|
+
return readFile(new URL(path, import.meta.url));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const basename = path.slice(path.lastIndexOf("/") + 1);
|
|
21
|
+
const candidates = [`./${basename}`, `.${path}`, `../client${path}`, `../../client${path}`];
|
|
22
|
+
|
|
23
|
+
let lastError;
|
|
24
|
+
for (const candidate of candidates) {
|
|
25
|
+
try {
|
|
26
|
+
return await readFile(new URL(candidate, import.meta.url));
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
29
|
+
lastError = error;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
throw new Error(`Unable to locate Takumi WASM asset for SSR: ${url}`, { cause: lastError });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return fetch(new URL(url, import.meta.url)).then((response) => response.arrayBuffer());
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
wasm.initSync({ module: await resolveWasm() });
|
|
44
|
+
|
|
45
|
+
export * from "../dist/export.mjs";
|
|
46
|
+
export default wasm.default;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default new URL("../pkg/takumi_paint_wasm_bg.wasm", import.meta.url);
|