feat: jsPDF
This commit is contained in:
parent
ec5a00fa94
commit
f32bbe59b3
|
|
@ -13,13 +13,15 @@
|
||||||
"chokidar": "^5.0.0",
|
"chokidar": "^5.0.0",
|
||||||
"commander": "^12.1.0",
|
"commander": "^12.1.0",
|
||||||
"csv-parse": "^5.5.6",
|
"csv-parse": "^5.5.6",
|
||||||
|
"html2canvas": "^1.4.1",
|
||||||
|
"jspdf": "^4.2.0",
|
||||||
"marked": "^14.1.0",
|
"marked": "^14.1.0",
|
||||||
"marked-directive": "^1.0.7",
|
"marked-directive": "^1.0.7",
|
||||||
"solid-element": "^1.9.1",
|
"solid-element": "^1.9.1",
|
||||||
"solid-js": "^1.9.3"
|
"solid-js": "^1.9.3"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"ttrpg": "dist/cli.js"
|
"ttrpg": "dist/cli/index.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rsbuild/core": "^1.1.8",
|
"@rsbuild/core": "^1.1.8",
|
||||||
|
|
@ -483,6 +485,15 @@
|
||||||
"@babel/core": "^7.0.0-0"
|
"@babel/core": "^7.0.0-0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/runtime": {
|
||||||
|
"version": "7.28.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
|
||||||
|
"integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/template": {
|
"node_modules/@babel/template": {
|
||||||
"version": "7.28.6",
|
"version": "7.28.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
|
||||||
|
|
@ -2218,6 +2229,26 @@
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/pako": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/raf": {
|
||||||
|
"version": "3.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
|
||||||
|
"integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/trusted-types": {
|
||||||
|
"version": "2.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||||
|
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.16.0",
|
"version": "8.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
||||||
|
|
@ -2309,6 +2340,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/base64-arraybuffer": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/baseline-browser-mapping": {
|
"node_modules/baseline-browser-mapping": {
|
||||||
"version": "2.10.0",
|
"version": "2.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
|
||||||
|
|
@ -2377,6 +2417,26 @@
|
||||||
],
|
],
|
||||||
"license": "CC-BY-4.0"
|
"license": "CC-BY-4.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/canvg": {
|
||||||
|
"version": "3.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz",
|
||||||
|
"integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.12.5",
|
||||||
|
"@types/raf": "^3.4.0",
|
||||||
|
"core-js": "^3.8.3",
|
||||||
|
"raf": "^3.4.1",
|
||||||
|
"regenerator-runtime": "^0.13.7",
|
||||||
|
"rgbcolor": "^1.0.1",
|
||||||
|
"stackblur-canvas": "^2.0.0",
|
||||||
|
"svg-pathdata": "^6.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
|
||||||
|
|
@ -2418,7 +2478,7 @@
|
||||||
"version": "3.47.0",
|
"version": "3.47.0",
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz",
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz",
|
||||||
"integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==",
|
"integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
|
@ -2433,6 +2493,15 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/css-line-break": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"utrie": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cssesc": {
|
"node_modules/cssesc": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||||
|
|
@ -2506,6 +2575,16 @@
|
||||||
"node": ">=0.3.1"
|
"node": ">=0.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dompurify": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==",
|
||||||
|
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||||
|
"optional": true,
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@types/trusted-types": "^2.0.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.302",
|
"version": "1.5.302",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz",
|
||||||
|
|
@ -2593,6 +2672,17 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-png": {
|
||||||
|
"version": "6.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz",
|
||||||
|
"integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/pako": "^2.0.3",
|
||||||
|
"iobuffer": "^5.3.2",
|
||||||
|
"pako": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fdir": {
|
"node_modules/fdir": {
|
||||||
"version": "6.5.0",
|
"version": "6.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||||
|
|
@ -2612,6 +2702,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fflate": {
|
||||||
|
"version": "0.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
|
||||||
|
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
|
|
@ -2652,6 +2748,25 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/html2canvas": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"css-line-break": "^2.1.0",
|
||||||
|
"text-segmentation": "^1.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/iobuffer": {
|
||||||
|
"version": "5.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz",
|
||||||
|
"integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/jiti": {
|
"node_modules/jiti": {
|
||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
|
||||||
|
|
@ -2704,6 +2819,23 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jspdf": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-hR/hnRevAXXlrjeqU5oahOE+Ln9ORJUB5brLHHqH67A+RBQZuFr5GkbI9XQI8OUFSEezKegsi45QRpc4bGj75Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.28.6",
|
||||||
|
"fast-png": "^6.2.0",
|
||||||
|
"fflate": "^0.8.1"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"canvg": "^3.0.11",
|
||||||
|
"core-js": "^3.6.0",
|
||||||
|
"dompurify": "^3.3.1",
|
||||||
|
"html2canvas": "^1.0.0-rc.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lightningcss": {
|
"node_modules/lightningcss": {
|
||||||
"version": "1.31.1",
|
"version": "1.31.1",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz",
|
||||||
|
|
@ -3055,6 +3187,12 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/pako": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
|
||||||
|
"license": "(MIT AND Zlib)"
|
||||||
|
},
|
||||||
"node_modules/parse5": {
|
"node_modules/parse5": {
|
||||||
"version": "7.3.0",
|
"version": "7.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
|
||||||
|
|
@ -3068,6 +3206,13 @@
|
||||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/performance-now": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
|
|
@ -3132,6 +3277,16 @@
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/raf": {
|
||||||
|
"version": "3.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
|
||||||
|
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"performance-now": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/readdirp": {
|
"node_modules/readdirp": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
|
||||||
|
|
@ -3152,6 +3307,23 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/regenerator-runtime": {
|
||||||
|
"version": "0.13.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||||
|
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/rgbcolor": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
|
||||||
|
"license": "MIT OR SEE LICENSE IN FEEL-FREE.md",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.15"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.59.0",
|
"version": "4.59.0",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
|
||||||
|
|
@ -3277,6 +3449,26 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/stackblur-canvas": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.1.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svg-pathdata": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "4.2.1",
|
"version": "4.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz",
|
||||||
|
|
@ -3298,6 +3490,15 @@
|
||||||
"url": "https://opencollective.com/webpack"
|
"url": "https://opencollective.com/webpack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/text-segmentation": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"utrie": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tinyglobby": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.15",
|
"version": "0.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||||
|
|
@ -3437,6 +3638,15 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/utrie": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-arraybuffer": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/v8-compile-cache-lib": {
|
"node_modules/v8-compile-cache-lib": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@
|
||||||
"chokidar": "^5.0.0",
|
"chokidar": "^5.0.0",
|
||||||
"commander": "^12.1.0",
|
"commander": "^12.1.0",
|
||||||
"csv-parse": "^5.5.6",
|
"csv-parse": "^5.5.6",
|
||||||
|
"html2canvas": "^1.4.1",
|
||||||
|
"jspdf": "^4.2.0",
|
||||||
"marked": "^14.1.0",
|
"marked": "^14.1.0",
|
||||||
"marked-directive": "^1.0.7",
|
"marked-directive": "^1.0.7",
|
||||||
"solid-element": "^1.9.1",
|
"solid-element": "^1.9.1",
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,12 @@ export function DeckHeader(props: DeckHeaderProps) {
|
||||||
{store.state.isEditing ? '✓ 编辑中' : '✏️ 编辑'}
|
{store.state.isEditing ? '✓ 编辑中' : '✏️ 编辑'}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* 打印按钮 */}
|
{/* 导出 PDF 按钮 */}
|
||||||
<button
|
<button
|
||||||
onClick={() => store.actions.printDeck()}
|
onClick={() => store.actions.exportDeck()}
|
||||||
class="px-2 py-1 rounded text-xs font-medium transition-colors cursor-pointer bg-green-100 text-green-600 hover:bg-green-200"
|
class="px-2 py-1 rounded text-xs font-medium transition-colors cursor-pointer bg-green-100 text-green-600 hover:bg-green-200"
|
||||||
>
|
>
|
||||||
🖨️ 打印
|
📥 导出 PDF
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Tab 选择器 */}
|
{/* Tab 选择器 */}
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,12 @@ import { For, createMemo } from 'solid-js';
|
||||||
import { marked } from '../../markdown';
|
import { marked } from '../../markdown';
|
||||||
import { getLayerStyle } from './hooks/dimensions';
|
import { getLayerStyle } from './hooks/dimensions';
|
||||||
import type { DeckStore } from './hooks/deckStore';
|
import type { DeckStore } from './hooks/deckStore';
|
||||||
|
import jsPDF from 'jspdf';
|
||||||
|
|
||||||
export interface PrintPreviewProps {
|
export interface PrintPreviewProps {
|
||||||
store: DeckStore;
|
store: DeckStore;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onPrint: () => void;
|
onExport: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -179,6 +180,120 @@ export function PrintPreview(props: PrintPreviewProps) {
|
||||||
|
|
||||||
const visibleLayers = createMemo(() => store.state.layerConfigs.filter((l) => l.visible));
|
const visibleLayers = createMemo(() => store.state.layerConfigs.filter((l) => l.visible));
|
||||||
|
|
||||||
|
// 导出 PDF
|
||||||
|
const handleExportPDF = async () => {
|
||||||
|
const pagesData = pages();
|
||||||
|
const a4Size = getA4Size();
|
||||||
|
|
||||||
|
// 创建 jsPDF 实例
|
||||||
|
const pdf = new jsPDF({
|
||||||
|
orientation: orientation() === 'landscape' ? 'landscape' : 'portrait',
|
||||||
|
unit: 'mm',
|
||||||
|
format: 'a4'
|
||||||
|
});
|
||||||
|
|
||||||
|
const cardWidth = store.state.dimensions?.cardWidth || 56;
|
||||||
|
const cardHeight = store.state.dimensions?.cardHeight || 88;
|
||||||
|
const gridOriginX = store.state.dimensions?.gridOriginX || 0;
|
||||||
|
const gridOriginY = store.state.dimensions?.gridOriginY || 0;
|
||||||
|
const gridAreaWidth = store.state.dimensions?.gridAreaWidth || cardWidth;
|
||||||
|
const gridAreaHeight = store.state.dimensions?.gridAreaHeight || cardHeight;
|
||||||
|
const fontSize = store.state.dimensions?.fontSize || 3;
|
||||||
|
|
||||||
|
// 为每页生成内容
|
||||||
|
for (let i = 0; i < pagesData.length; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
pdf.addPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
const page = pagesData()[i];
|
||||||
|
const cropData = cropMarks()[i];
|
||||||
|
|
||||||
|
// 绘制外围边框
|
||||||
|
const frameMargin = cropData.frameBoundsWithMargin;
|
||||||
|
pdf.setDrawColor(0);
|
||||||
|
pdf.setLineWidth(0.2);
|
||||||
|
pdf.rect(frameMargin.x, frameMargin.y, frameMargin.width, frameMargin.height);
|
||||||
|
|
||||||
|
// 绘制水平裁切线
|
||||||
|
for (const line of cropData.horizontalLines) {
|
||||||
|
pdf.setDrawColor(136);
|
||||||
|
pdf.setLineWidth(0.1);
|
||||||
|
// 左侧裁切线
|
||||||
|
pdf.line(line.xStart, line.y, page.frameBounds.minX, line.y);
|
||||||
|
// 右侧裁切线
|
||||||
|
pdf.line(page.frameBounds.maxX, line.y, line.xEnd, line.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制垂直裁切线
|
||||||
|
for (const line of cropData.verticalLines) {
|
||||||
|
pdf.setDrawColor(136);
|
||||||
|
pdf.setLineWidth(0.1);
|
||||||
|
// 上方裁切线
|
||||||
|
pdf.line(line.x, line.yStart, line.x, page.frameBounds.minY);
|
||||||
|
// 下方裁切线
|
||||||
|
pdf.line(line.x, page.frameBounds.maxY, line.x, line.yEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 渲染卡牌内容
|
||||||
|
for (const card of page.cards) {
|
||||||
|
// 创建临时容器渲染卡牌内容
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.style.position = 'absolute';
|
||||||
|
container.style.left = '-9999px';
|
||||||
|
container.style.top = '-9999px';
|
||||||
|
container.style.width = `${cardWidth}mm`;
|
||||||
|
container.style.height = `${cardHeight}mm`;
|
||||||
|
container.style.background = 'white';
|
||||||
|
|
||||||
|
// 网格区域容器
|
||||||
|
const gridContainer = document.createElement('div');
|
||||||
|
gridContainer.style.position = 'absolute';
|
||||||
|
gridContainer.style.left = `${gridOriginX}mm`;
|
||||||
|
gridContainer.style.top = `${gridOriginY}mm`;
|
||||||
|
gridContainer.style.width = `${gridAreaWidth}mm`;
|
||||||
|
gridContainer.style.height = `${gridAreaHeight}mm`;
|
||||||
|
|
||||||
|
// 渲染每个 layer
|
||||||
|
for (const layer of visibleLayers()) {
|
||||||
|
const layerEl = document.createElement('div');
|
||||||
|
layerEl.className = 'absolute flex items-center justify-center text-center prose prose-sm';
|
||||||
|
Object.assign(layerEl.style, getLayerStyle(layer, store.state.dimensions!));
|
||||||
|
layerEl.style.fontSize = `${fontSize}mm`;
|
||||||
|
layerEl.innerHTML = renderLayerContent(layer, card.data);
|
||||||
|
gridContainer.appendChild(layerEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
container.appendChild(gridContainer);
|
||||||
|
document.body.appendChild(container);
|
||||||
|
|
||||||
|
// 使用 html2canvas 渲染
|
||||||
|
try {
|
||||||
|
const html2canvas = (await import('html2canvas')).default;
|
||||||
|
const canvas = await html2canvas(container, {
|
||||||
|
scale: 2,
|
||||||
|
backgroundColor: null,
|
||||||
|
logging: false,
|
||||||
|
useCORS: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const imgData = canvas.toDataURL('image/png');
|
||||||
|
pdf.addImage(imgData, 'PNG', card.x, card.y, cardWidth, cardHeight);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('渲染卡牌内容失败:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.removeChild(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存 PDF 文件
|
||||||
|
pdf.save('deck.pdf');
|
||||||
|
|
||||||
|
// 关闭预览
|
||||||
|
props.onClose();
|
||||||
|
};
|
||||||
|
|
||||||
// 渲染单个卡片的 SVG 内容(使用 foreignObject)
|
// 渲染单个卡片的 SVG 内容(使用 foreignObject)
|
||||||
const renderCardInSvg = (card: { data: typeof store.state.cards[0]; x: number; y: number }, pageIndex: number) => {
|
const renderCardInSvg = (card: { data: typeof store.state.cards[0]; x: number; y: number }, pageIndex: number) => {
|
||||||
const cardWidth = store.state.dimensions?.cardWidth || 56;
|
const cardWidth = store.state.dimensions?.cardWidth || 56;
|
||||||
|
|
@ -230,17 +345,8 @@ export function PrintPreview(props: PrintPreviewProps) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="fixed inset-0 bg-black/50 z-50 overflow-auto print:overflow-visible print:absolute">
|
<div class="fixed inset-0 bg-black/50 z-50 overflow-auto">
|
||||||
{/* 打印样式:根据方向设置 @page 规则 */}
|
<div class="min-h-screen py-20 px-4">
|
||||||
<style>{`
|
|
||||||
@media print {
|
|
||||||
@page {
|
|
||||||
size: A4 ${orientation() === 'landscape' ? 'landscape' : 'portrait'};
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
<div class="min-h-screen py-20 px-4 print:p-0">
|
|
||||||
{/* 打印预览控制栏 */}
|
{/* 打印预览控制栏 */}
|
||||||
<div class="fixed top-0 left-0 right-0 z-50 bg-white shadow-lg rounded-lg mx-4 mt-4 px-4 py-3 flex items-center justify-between gap-4">
|
<div class="fixed top-0 left-0 right-0 z-50 bg-white shadow-lg rounded-lg mx-4 mt-4 px-4 py-3 flex items-center justify-between gap-4">
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
|
|
@ -303,11 +409,11 @@ export function PrintPreview(props: PrintPreviewProps) {
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={props.onPrint}
|
onClick={handleExportPDF}
|
||||||
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-1.5 rounded text-sm font-medium cursor-pointer flex items-center gap-2"
|
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-1.5 rounded text-sm font-medium cursor-pointer flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<span>🖨️</span>
|
<span>📥</span>
|
||||||
<span>打印</span>
|
<span>导出 PDF</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={props.onClose}
|
onClick={props.onClose}
|
||||||
|
|
@ -319,11 +425,11 @@ export function PrintPreview(props: PrintPreviewProps) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* A4 纸张预览:每页都是一个完整的 SVG */}
|
{/* A4 纸张预览:每页都是一个完整的 SVG */}
|
||||||
<div class="flex flex-col items-center gap-8 print-root">
|
<div class="flex flex-col items-center gap-8">
|
||||||
<For each={pages()}>
|
<For each={pages()}>
|
||||||
{(page) => (
|
{(page) => (
|
||||||
<svg
|
<svg
|
||||||
class="bg-white shadow-xl print:shadow-none"
|
class="bg-white shadow-xl"
|
||||||
viewBox={`0 0 ${getA4Size().width}mm ${getA4Size().height}mm`}
|
viewBox={`0 0 ${getA4Size().width}mm ${getA4Size().height}mm`}
|
||||||
style={{
|
style={{
|
||||||
width: `${getA4Size().width}mm`,
|
width: `${getA4Size().width}mm`,
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,8 @@ export interface DeckState {
|
||||||
// 错误状态
|
// 错误状态
|
||||||
error: string | null;
|
error: string | null;
|
||||||
|
|
||||||
// 打印状态
|
// 导出状态
|
||||||
isPrinting: boolean;
|
isExporting: boolean;
|
||||||
|
|
||||||
// 打印设置
|
// 打印设置
|
||||||
printOrientation: 'portrait' | 'landscape';
|
printOrientation: 'portrait' | 'landscape';
|
||||||
|
|
@ -103,9 +103,9 @@ export interface DeckActions {
|
||||||
generateCode: () => string;
|
generateCode: () => string;
|
||||||
copyCode: () => Promise<void>;
|
copyCode: () => Promise<void>;
|
||||||
|
|
||||||
// 打印操作
|
// 导出操作
|
||||||
setPrinting: (printing: boolean) => void;
|
setExporting: (exporting: boolean) => void;
|
||||||
printDeck: () => void;
|
exportDeck: () => void;
|
||||||
|
|
||||||
// 打印设置
|
// 打印设置
|
||||||
setPrintOrientation: (orientation: 'portrait' | 'landscape') => void;
|
setPrintOrientation: (orientation: 'portrait' | 'landscape') => void;
|
||||||
|
|
@ -146,7 +146,7 @@ export function createDeckStore(
|
||||||
selectEnd: null,
|
selectEnd: null,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: null,
|
error: null,
|
||||||
isPrinting: false,
|
isExporting: false,
|
||||||
printOrientation: 'portrait',
|
printOrientation: 'portrait',
|
||||||
printOddPageOffsetX: 0,
|
printOddPageOffsetX: 0,
|
||||||
printOddPageOffsetY: 0
|
printOddPageOffsetY: 0
|
||||||
|
|
@ -286,10 +286,10 @@ export function createDeckStore(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const setPrinting = (printing: boolean) => setState({ isPrinting: printing });
|
const setExporting = (exporting: boolean) => setState({ isExporting: exporting });
|
||||||
|
|
||||||
const printDeck = () => {
|
const exportDeck = () => {
|
||||||
setState({ isPrinting: true });
|
setState({ isExporting: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
const setPrintOrientation = (orientation: 'portrait' | 'landscape') => {
|
const setPrintOrientation = (orientation: 'portrait' | 'landscape') => {
|
||||||
|
|
@ -330,8 +330,8 @@ export function createDeckStore(
|
||||||
clearError,
|
clearError,
|
||||||
generateCode,
|
generateCode,
|
||||||
copyCode,
|
copyCode,
|
||||||
setPrinting,
|
setExporting,
|
||||||
printDeck,
|
exportDeck,
|
||||||
setPrintOrientation,
|
setPrintOrientation,
|
||||||
setPrintOddPageOffsetX,
|
setPrintOddPageOffsetX,
|
||||||
setPrintOddPageOffsetY
|
setPrintOddPageOffsetY
|
||||||
|
|
|
||||||
|
|
@ -103,12 +103,12 @@ customElement<DeckProps>('md-deck', {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="md-deck mb-4">
|
<div class="md-deck mb-4">
|
||||||
{/* 打印预览弹窗 */}
|
{/* 导出 PDF 预览弹窗 */}
|
||||||
<Show when={store.state.isPrinting}>
|
<Show when={store.state.isExporting}>
|
||||||
<PrintPreview
|
<PrintPreview
|
||||||
store={store}
|
store={store}
|
||||||
onClose={() => store.actions.setPrinting(false)}
|
onClose={() => store.actions.setExporting(false)}
|
||||||
onPrint={() => window.print()}
|
onExport={() => {}}
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue