diff --git a/package-lock.json b/package-lock.json
index 8d685752..8285e15f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -40,10 +40,14 @@
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"date-fns": "^4.1.0",
+ "dom-to-image-more": "^3.7.2",
+ "html2canvas": "^1.4.1",
"immer": "^11.0.1",
+ "jspdf": "^4.0.0",
"lucide-react": "^0.552.0",
"next": "^15.5.9",
"next-intl": "^4.4.0",
+ "puppeteer": "^24.36.0",
"react": "^19.2.3",
"react-day-picker": "^9.11.1",
"react-dom": "^19.2.3",
@@ -82,6 +86,38 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/@babel/code-frame": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz",
+ "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.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/@capacitor/app": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@capacitor/app/-/app-8.0.0.tgz",
@@ -1498,6 +1534,27 @@
"node": ">=18"
}
},
+ "node_modules/@puppeteer/browsers": {
+ "version": "2.11.1",
+ "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.11.1.tgz",
+ "integrity": "sha512-YmhAxs7XPuxN0j7LJloHpfD1ylhDuFmmwMvfy/+6nBSrETT2ycL53LrhgPtR+f+GcPSybQVuQ5inWWu5MrWCpA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "debug": "^4.4.3",
+ "extract-zip": "^2.0.1",
+ "progress": "^2.0.3",
+ "proxy-agent": "^6.5.0",
+ "semver": "^7.7.3",
+ "tar-fs": "^3.1.1",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "browsers": "lib/cjs/main-cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@radix-ui/number": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
@@ -3712,6 +3769,12 @@
"url": "https://github.com/sponsors/ueberdosis"
}
},
+ "node_modules/@tootallnate/quickjs-emscripten": {
+ "version": "0.23.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
+ "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
+ "license": "MIT"
+ },
"node_modules/@tybys/wasm-util": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
@@ -3840,12 +3903,25 @@
"version": "20.19.27",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.27.tgz",
"integrity": "sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"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/react": {
"version": "19.2.7",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
@@ -3864,12 +3940,29 @@
"@types/react": "^19.2.0"
}
},
+ "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/@types/use-sync-external-store": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
"license": "MIT"
},
+ "node_modules/@types/yauzl": {
+ "version": "2.10.3",
+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
+ "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.52.0.tgz",
@@ -4427,6 +4520,15 @@
"node": ">=0.8"
}
},
+ "node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -4444,11 +4546,19 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
@@ -4648,6 +4758,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/ast-types": {
+ "version": "0.13.4",
+ "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
+ "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/ast-types-flow": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
@@ -4701,6 +4823,20 @@
"node": ">= 0.4"
}
},
+ "node_modules/b4a": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz",
+ "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react-native-b4a": "*"
+ },
+ "peerDependenciesMeta": {
+ "react-native-b4a": {
+ "optional": true
+ }
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -4708,6 +4844,115 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/bare-events": {
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz",
+ "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "bare-abort-controller": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-abort-controller": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-fs": {
+ "version": "4.5.3",
+ "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.3.tgz",
+ "integrity": "sha512-9+kwVx8QYvt3hPWnmb19tPnh38c6Nihz8Lx3t0g9+4GoIf3/fTgYwM4Z6NxgI+B9elLQA7mLE9PpqcWtOMRDiQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "bare-events": "^2.5.4",
+ "bare-path": "^3.0.0",
+ "bare-stream": "^2.6.4",
+ "bare-url": "^2.2.2",
+ "fast-fifo": "^1.3.2"
+ },
+ "engines": {
+ "bare": ">=1.16.0"
+ },
+ "peerDependencies": {
+ "bare-buffer": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-buffer": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-os": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz",
+ "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "bare": ">=1.14.0"
+ }
+ },
+ "node_modules/bare-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz",
+ "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "bare-os": "^3.0.1"
+ }
+ },
+ "node_modules/bare-stream": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz",
+ "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "streamx": "^2.21.0"
+ },
+ "peerDependencies": {
+ "bare-buffer": "*",
+ "bare-events": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-buffer": {
+ "optional": true
+ },
+ "bare-events": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-url": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz",
+ "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "bare-path": "^3.0.0"
+ }
+ },
+ "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/basic-ftp": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz",
+ "integrity": "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
"node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
@@ -4731,6 +4976,15 @@
"node": ">=8"
}
},
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
@@ -4785,7 +5039,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -4811,6 +5064,26 @@
],
"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/cfb": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
@@ -4841,6 +5114,28 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
+ "node_modules/chromium-bidi": {
+ "version": "13.0.1",
+ "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-13.0.1.tgz",
+ "integrity": "sha512-c+RLxH0Vg2x2syS9wPw378oJgiJNXtYXUvnVAldUlt5uaHekn0CCU7gPksNgHjrH1qFhmjVXQj4esvuthuC7OQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "mitt": "^3.0.1",
+ "zod": "^3.24.1"
+ },
+ "peerDependencies": {
+ "devtools-protocol": "*"
+ }
+ },
+ "node_modules/chromium-bidi/node_modules/zod": {
+ "version": "3.25.76",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
"node_modules/class-variance-authority": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
@@ -4859,6 +5154,20 @@
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
"license": "MIT"
},
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@@ -4897,7 +5206,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
@@ -4910,7 +5218,6 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
"license": "MIT"
},
"node_modules/concat-map": {
@@ -4920,6 +5227,44 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/core-js": {
+ "version": "3.48.0",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz",
+ "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/cosmiconfig": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
+ "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
+ "license": "MIT",
+ "dependencies": {
+ "env-paths": "^2.2.1",
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/d-fischer"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.9.5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
"node_modules/crc-32": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
@@ -4953,6 +5298,15 @@
"node": ">= 8"
}
},
+ "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/csstype": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
@@ -5087,6 +5441,15 @@
"dev": true,
"license": "BSD-2-Clause"
},
+ "node_modules/data-uri-to-buffer": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
+ "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/data-view-buffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
@@ -5161,7 +5524,6 @@
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -5230,6 +5592,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/degenerator": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
+ "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ast-types": "^0.13.4",
+ "escodegen": "^2.1.0",
+ "esprima": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
@@ -5246,6 +5622,12 @@
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
"license": "MIT"
},
+ "node_modules/devtools-protocol": {
+ "version": "0.0.1551306",
+ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1551306.tgz",
+ "integrity": "sha512-CFx8QdSim8iIv+2ZcEOclBKTQY6BI1IEDa7Tm9YkwAXzEWFndTEzpTo5jAUhSnq24IC7xaDw0wvGcm96+Y3PEg==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
@@ -5259,6 +5641,22 @@
"node": ">=0.10.0"
}
},
+ "node_modules/dom-to-image-more": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/dom-to-image-more/-/dom-to-image-more-3.7.2.tgz",
+ "integrity": "sha512-uQf+pHv6eQhgfI8t2bFuinV0KsPyT8TZgCLwcSU8uBVgN9v6leb0mMpvp6HQAlAcplP3NCcGjxbdqef6pTzvmw==",
+ "license": "MIT"
+ },
+ "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/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -5281,6 +5679,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
"node_modules/enhanced-resolve": {
"version": "5.18.4",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz",
@@ -5307,6 +5714,24 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
+ "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
"node_modules/es-abstract": {
"version": "1.24.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
@@ -5494,6 +5919,15 @@
"benchmarks"
]
},
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -5506,6 +5940,27 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/escodegen": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
"node_modules/eslint": {
"version": "9.39.2",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
@@ -5885,6 +6340,19 @@
"url": "https://opencollective.com/eslint"
}
},
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/esquery": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
@@ -5915,7 +6383,6 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
@@ -5925,7 +6392,6 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
- "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
@@ -5937,6 +6403,35 @@
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
"license": "MIT"
},
+ "node_modules/events-universal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
+ "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-events": "^2.7.0"
+ }
+ },
+ "node_modules/extract-zip": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "get-stream": "^5.1.0",
+ "yauzl": "^2.10.0"
+ },
+ "bin": {
+ "extract-zip": "cli.js"
+ },
+ "engines": {
+ "node": ">= 10.17.0"
+ },
+ "optionalDependencies": {
+ "@types/yauzl": "^2.9.1"
+ }
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -5953,6 +6448,12 @@
"node": ">=6.0.0"
}
},
+ "node_modules/fast-fifo": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
+ "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
+ "license": "MIT"
+ },
"node_modules/fast-glob": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
@@ -5997,6 +6498,17 @@
"dev": true,
"license": "MIT"
},
+ "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/fastq": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
@@ -6007,6 +6519,21 @@
"reusify": "^1.0.4"
}
},
+ "node_modules/fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "license": "MIT",
+ "dependencies": {
+ "pend": "~1.2.0"
+ }
+ },
+ "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/file-entry-cache": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@@ -6161,6 +6688,15 @@
"node": ">= 0.4"
}
},
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -6209,6 +6745,21 @@
"node": ">= 0.4"
}
},
+ "node_modules/get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "license": "MIT",
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/get-symbol-description": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
@@ -6240,6 +6791,20 @@
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
+ "node_modules/get-uri": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz",
+ "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==",
+ "license": "MIT",
+ "dependencies": {
+ "basic-ftp": "^5.0.2",
+ "data-uri-to-buffer": "^6.0.2",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -6397,6 +6962,45 @@
"node": ">= 0.4"
}
},
+ "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/http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -6421,7 +7025,6 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"parent-module": "^1.0.0",
@@ -6480,6 +7083,21 @@
"tslib": "^2.8.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/ip-address": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
+ "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/is-array-buffer": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
@@ -6498,6 +7116,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "license": "MIT"
+ },
"node_modules/is-async-function": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
@@ -6650,6 +7274,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-generator-function": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
@@ -6925,14 +7558,12 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -6948,6 +7579,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "license": "MIT"
+ },
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -6975,6 +7612,23 @@
"json5": "lib/cli.js"
}
},
+ "node_modules/jspdf": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-4.0.0.tgz",
+ "integrity": "sha512-w12U97Z6edKd2tXDn3LzTLg7C7QLJlx0BPfM3ecjK2BckUl9/81vZ+r5gK4/3KQdhAcEZhENUxRhtgYBj75MqQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.28.4",
+ "fast-png": "^6.2.0",
+ "fflate": "^0.8.1"
+ },
+ "optionalDependencies": {
+ "canvg": "^3.0.11",
+ "core-js": "^3.6.0",
+ "dompurify": "^3.2.4",
+ "html2canvas": "^1.0.0-rc.5"
+ }
+ },
"node_modules/jsx-ast-utils": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
@@ -7296,6 +7950,12 @@
"url": "https://opencollective.com/parcel"
}
},
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "license": "MIT"
+ },
"node_modules/linkify-it": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
@@ -7347,6 +8007,15 @@
"loose-envify": "cli.js"
}
},
+ "node_modules/lru-cache": {
+ "version": "7.18.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/lucide-react": {
"version": "0.552.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.552.0.tgz",
@@ -7445,11 +8114,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "license": "MIT"
+ },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
"license": "MIT"
},
"node_modules/nanoid": {
@@ -7502,6 +8176,15 @@
"node": ">= 0.6"
}
},
+ "node_modules/netmask": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
+ "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
"node_modules/next": {
"version": "15.5.9",
"resolved": "https://registry.npmjs.org/next/-/next-15.5.9.tgz",
@@ -7797,6 +8480,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -7871,11 +8563,48 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/pac-proxy-agent": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz",
+ "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==",
+ "license": "MIT",
+ "dependencies": {
+ "@tootallnate/quickjs-emscripten": "^0.23.0",
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "get-uri": "^6.0.1",
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.6",
+ "pac-resolver": "^7.0.1",
+ "socks-proxy-agent": "^8.0.5"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/pac-resolver": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
+ "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==",
+ "license": "MIT",
+ "dependencies": {
+ "degenerator": "^5.0.0",
+ "netmask": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "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/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"callsites": "^3.0.0"
@@ -7884,6 +8613,24 @@
"node": ">=6"
}
},
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -7911,6 +8658,19 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+ "license": "MIT"
+ },
+ "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": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -8016,6 +8776,15 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -8230,6 +8999,41 @@
"prosemirror-transform": "^1.1.0"
}
},
+ "node_modules/proxy-agent": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz",
+ "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "http-proxy-agent": "^7.0.1",
+ "https-proxy-agent": "^7.0.6",
+ "lru-cache": "^7.14.1",
+ "pac-proxy-agent": "^7.1.0",
+ "proxy-from-env": "^1.1.0",
+ "socks-proxy-agent": "^8.0.5"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "license": "MIT"
+ },
+ "node_modules/pump": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
+ "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -8249,6 +9053,45 @@
"node": ">=6"
}
},
+ "node_modules/puppeteer": {
+ "version": "24.36.0",
+ "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.36.0.tgz",
+ "integrity": "sha512-BD/VCyV/Uezvd6o7Fd1DmEJSxTzofAKplzDy6T9d4WbLTQ5A+06zY7VwO91ZlNU22vYE8sidVEsTpTrKc+EEnQ==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@puppeteer/browsers": "2.11.1",
+ "chromium-bidi": "13.0.1",
+ "cosmiconfig": "^9.0.0",
+ "devtools-protocol": "0.0.1551306",
+ "puppeteer-core": "24.36.0",
+ "typed-query-selector": "^2.12.0"
+ },
+ "bin": {
+ "puppeteer": "lib/cjs/puppeteer/node/cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/puppeteer-core": {
+ "version": "24.36.0",
+ "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.36.0.tgz",
+ "integrity": "sha512-P3Ou0MAFDCQ0dK1d9F9+8jTrg6JvXjUacgG0YkJQP4kbEnUOGokSDEMmMId5ZhXD5HwsHM202E9VwEpEjWfwxg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@puppeteer/browsers": "2.11.1",
+ "chromium-bidi": "13.0.1",
+ "debug": "^4.4.3",
+ "devtools-protocol": "0.0.1551306",
+ "typed-query-selector": "^2.12.0",
+ "webdriver-bidi-protocol": "0.4.0",
+ "ws": "^8.19.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -8270,6 +9113,16 @@
],
"license": "MIT"
},
+ "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/react": {
"version": "19.2.3",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
@@ -8505,6 +9358,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "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/regexp.prototype.flags": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
@@ -8526,6 +9386,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
@@ -8557,7 +9426,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -8584,6 +9452,16 @@
"node": ">=0.10.0"
}
},
+ "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/rope-sequence": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz",
@@ -8679,7 +9557,6 @@
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
- "devOptional": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -8881,6 +9758,44 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/smart-buffer": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/socks": {
+ "version": "2.8.7",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
+ "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
+ "license": "MIT",
+ "dependencies": {
+ "ip-address": "^10.0.1",
+ "smart-buffer": "^4.2.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/socks-proxy-agent": {
+ "version": "8.0.5",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
+ "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "socks": "^2.8.3"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/sonner": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz",
@@ -8891,6 +9806,16 @@
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
}
},
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -8919,6 +9844,16 @@
"dev": true,
"license": "MIT"
},
+ "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/stop-iteration-iterator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
@@ -8933,6 +9868,37 @@
"node": ">= 0.4"
}
},
+ "node_modules/streamx": {
+ "version": "2.23.0",
+ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz",
+ "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==",
+ "license": "MIT",
+ "dependencies": {
+ "events-universal": "^1.0.0",
+ "fast-fifo": "^1.3.2",
+ "text-decoder": "^1.1.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
"node_modules/string.prototype.includes": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
@@ -9046,6 +10012,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
@@ -9118,6 +10096,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "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/tailwind-merge": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
@@ -9149,6 +10137,49 @@
"url": "https://opencollective.com/webpack"
}
},
+ "node_modules/tar-fs": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz",
+ "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==",
+ "license": "MIT",
+ "dependencies": {
+ "pump": "^3.0.0",
+ "tar-stream": "^3.1.5"
+ },
+ "optionalDependencies": {
+ "bare-fs": "^4.0.1",
+ "bare-path": "^3.0.0"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
+ "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
+ "license": "MIT",
+ "dependencies": {
+ "b4a": "^1.6.4",
+ "fast-fifo": "^1.2.0",
+ "streamx": "^2.15.0"
+ }
+ },
+ "node_modules/text-decoder": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz",
+ "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "b4a": "^1.6.4"
+ }
+ },
+ "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/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
@@ -9338,6 +10369,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/typed-query-selector": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz",
+ "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==",
+ "license": "MIT"
+ },
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
@@ -9381,7 +10418,7 @@
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/unrs-resolver": {
@@ -9495,6 +10532,15 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
+ "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/vaul": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz",
@@ -9536,6 +10582,12 @@
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
"license": "MIT"
},
+ "node_modules/webdriver-bidi-protocol": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.0.tgz",
+ "integrity": "sha512-U9VIlNRrq94d1xxR9JrCEAx5Gv/2W7ERSv8oWRoNe/QYbfccS0V3h/H6qeNeCRJxXGMhhnkqvwNrvPAYeuP9VA==",
+ "license": "Apache-2.0"
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -9669,6 +10721,50 @@
"node": ">=0.10.0"
}
},
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "license": "ISC"
+ },
+ "node_modules/ws": {
+ "version": "8.19.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
+ "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
"node_modules/xlsx": {
"version": "0.18.5",
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
@@ -9690,6 +10786,52 @@
"node": ">=0.8"
}
},
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/package.json b/package.json
index b5c62fef..6f873c77 100644
--- a/package.json
+++ b/package.json
@@ -46,10 +46,14 @@
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"date-fns": "^4.1.0",
+ "dom-to-image-more": "^3.7.2",
+ "html2canvas": "^1.4.1",
"immer": "^11.0.1",
+ "jspdf": "^4.0.0",
"lucide-react": "^0.552.0",
"next": "^15.5.9",
"next-intl": "^4.4.0",
+ "puppeteer": "^24.36.0",
"react": "^19.2.3",
"react-day-picker": "^9.11.1",
"react-dom": "^19.2.3",
diff --git a/src/app/api/pdf/generate/route.ts b/src/app/api/pdf/generate/route.ts
new file mode 100644
index 00000000..a70abf4e
--- /dev/null
+++ b/src/app/api/pdf/generate/route.ts
@@ -0,0 +1,180 @@
+import { NextRequest, NextResponse } from 'next/server';
+import puppeteer from 'puppeteer';
+
+/**
+ * PDF 생성 API
+ * POST /api/pdf/generate
+ *
+ * Body: {
+ * html: string,
+ * styles: string,
+ * title?: string,
+ * orientation?: 'portrait' | 'landscape',
+ * documentNumber?: string,
+ * createdDate?: string,
+ * showHeaderFooter?: boolean
+ * }
+ * Response: PDF blob
+ */
+export async function POST(request: NextRequest) {
+ try {
+ const {
+ html,
+ styles = '',
+ title = '문서',
+ orientation = 'portrait',
+ documentNumber = '',
+ createdDate = '',
+ showHeaderFooter = true,
+ } = await request.json();
+
+ if (!html) {
+ return NextResponse.json(
+ { error: 'HTML content is required' },
+ { status: 400 }
+ );
+ }
+
+ // Puppeteer 브라우저 실행
+ const browser = await puppeteer.launch({
+ headless: true,
+ args: [
+ '--no-sandbox',
+ '--disable-setuid-sandbox',
+ '--disable-dev-shm-usage',
+ '--disable-gpu',
+ ],
+ });
+
+ const page = await browser.newPage();
+
+ // 전체 HTML 문서 구성 (인라인 스타일 포함)
+ const fullHtml = `
+
+
+
+
+
+ ${title}
+
+
+
+
+ ${html}
+
+
+
+ `;
+
+ // 뷰포트 설정 (문서 전체가 보이도록 넓게)
+ await page.setViewport({
+ width: 1200,
+ height: 1600,
+ deviceScaleFactor: 2,
+ });
+
+ // HTML 설정
+ await page.setContent(fullHtml, {
+ waitUntil: 'networkidle0',
+ });
+
+ // 헤더 템플릿 (문서번호, 생성일)
+ const headerTemplate = showHeaderFooter
+ ? `
+
+
+ ${documentNumber ? `문서번호: ${documentNumber}` : ''}
+ ${createdDate ? `생성일: ${createdDate}` : ''}
+
+
+ `
+ : '';
+
+ // 푸터 템플릿 (라인 + 페이지번호)
+ const footerTemplate = showHeaderFooter
+ ? `
+
+
+ ${title}
+ Page /
+
+
+ `
+ : '';
+
+ // PDF 생성 (자동 스케일로 A4에 맞춤)
+ const pdfBuffer = await page.pdf({
+ format: 'A4',
+ landscape: orientation === 'landscape',
+ printBackground: true,
+ preferCSSPageSize: false,
+ scale: 0.75, // 문서를 75%로 축소하여 A4에 맞춤
+ displayHeaderFooter: showHeaderFooter,
+ headerTemplate: headerTemplate,
+ footerTemplate: footerTemplate,
+ margin: {
+ top: showHeaderFooter ? '20mm' : '10mm',
+ right: '10mm',
+ bottom: showHeaderFooter ? '20mm' : '10mm',
+ left: '10mm',
+ },
+ });
+
+ // 브라우저 종료
+ await browser.close();
+
+ // PDF 응답
+ return new NextResponse(pdfBuffer, {
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/pdf',
+ 'Content-Disposition': `attachment; filename="${encodeURIComponent(title)}.pdf"`,
+ },
+ });
+ } catch (error) {
+ console.error('PDF 생성 오류:', error);
+ return NextResponse.json(
+ { error: 'PDF 생성 중 오류가 발생했습니다.' },
+ { status: 500 }
+ );
+ }
+}
diff --git a/src/components/document-system/presets/index.ts b/src/components/document-system/presets/index.ts
index cbd3db73..b0543307 100644
--- a/src/components/document-system/presets/index.ts
+++ b/src/components/document-system/presets/index.ts
@@ -16,7 +16,7 @@ export const DOCUMENT_PRESETS: Record = {
print: true,
download: true,
},
- actions: ['print', 'download'],
+ actions: ['pdf', 'print', 'download'],
},
// 건설 프로젝트용 (CRUD)
@@ -27,7 +27,7 @@ export const DOCUMENT_PRESETS: Record = {
print: true,
download: false,
},
- actions: ['edit', 'delete', 'print'],
+ actions: ['edit', 'delete', 'pdf', 'print'],
},
// 결재 문서용 (기본)
@@ -38,7 +38,7 @@ export const DOCUMENT_PRESETS: Record = {
print: true,
download: false,
},
- actions: ['edit', 'submit', 'print'],
+ actions: ['edit', 'submit', 'pdf', 'print'],
},
// 결재 문서용 - 기안함 모드 (임시저장 상태: 복제, 상신, 인쇄)
@@ -49,7 +49,7 @@ export const DOCUMENT_PRESETS: Record = {
print: true,
download: false,
},
- actions: ['copy', 'submit', 'print'],
+ actions: ['copy', 'submit', 'pdf', 'print'],
},
// 결재 문서용 - 결재함 모드 (수정, 반려, 승인, 인쇄)
@@ -60,7 +60,7 @@ export const DOCUMENT_PRESETS: Record = {
print: true,
download: false,
},
- actions: ['edit', 'reject', 'approve', 'print'],
+ actions: ['edit', 'reject', 'approve', 'pdf', 'print'],
},
// 조회 전용
@@ -71,7 +71,7 @@ export const DOCUMENT_PRESETS: Record = {
print: true,
download: false,
},
- actions: ['print'],
+ actions: ['pdf', 'print'],
},
// 견적서/문서 전송용 (PDF, 이메일, 팩스, 카카오톡, 인쇄)
diff --git a/src/components/document-system/types.ts b/src/components/document-system/types.ts
index 92c39667..ef5cf358 100644
--- a/src/components/document-system/types.ts
+++ b/src/components/document-system/types.ts
@@ -144,6 +144,20 @@ export interface CustomBlock {
// DocumentViewer Props
// ============================================================
+// ============================================================
+// PDF Meta Types
+// ============================================================
+
+export interface PdfMeta {
+ documentNumber?: string;
+ createdDate?: string;
+ showHeaderFooter?: boolean;
+}
+
+// ============================================================
+// DocumentViewer Props
+// ============================================================
+
export interface DocumentViewerProps {
// Config 기반 (권장)
config?: DocumentConfig;
@@ -158,6 +172,9 @@ export interface DocumentViewerProps {
// 데이터
data?: any;
+ // PDF 메타 정보 (헤더/푸터용)
+ pdfMeta?: PdfMeta;
+
// 액션 핸들러
onPrint?: () => void;
onDownload?: () => void;
diff --git a/src/components/document-system/viewer/DocumentViewer.tsx b/src/components/document-system/viewer/DocumentViewer.tsx
index 2b8706e0..acb7b9ad 100644
--- a/src/components/document-system/viewer/DocumentViewer.tsx
+++ b/src/components/document-system/viewer/DocumentViewer.tsx
@@ -1,12 +1,13 @@
'use client';
-import React, { useEffect, ReactNode } from 'react';
+import React, { useEffect, ReactNode, useCallback } from 'react';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
+import { toast } from 'sonner';
import { printArea } from '@/lib/print-utils';
import { DocumentToolbar } from './DocumentToolbar';
import { DocumentContent } from './DocumentContent';
@@ -17,6 +18,7 @@ import {
DocumentViewerProps,
ActionType,
DocumentFeatures,
+ PdfMeta,
} from '../types';
/**
@@ -59,6 +61,9 @@ export function DocumentViewer({
// 데이터
data,
+ // PDF 메타 정보
+ pdfMeta,
+
// 액션 핸들러
onPrint: propOnPrint,
onDownload,
@@ -120,6 +125,118 @@ export function DocumentViewer({
}
};
+ // 인라인 스타일 적용된 HTML 복제 생성
+ const cloneWithInlineStyles = (element: HTMLElement): HTMLElement => {
+ const clone = element.cloneNode(true) as HTMLElement;
+
+ // 원본 요소들과 복제 요소들 매칭
+ const originalElements = element.querySelectorAll('*');
+ const clonedElements = clone.querySelectorAll('*');
+
+ // 루트 요소 스타일 적용
+ const rootStyle = window.getComputedStyle(element);
+ applyStyles(clone, rootStyle);
+
+ // 모든 하위 요소 스타일 적용
+ originalElements.forEach((orig, index) => {
+ const cloned = clonedElements[index] as HTMLElement;
+ if (cloned) {
+ const computedStyle = window.getComputedStyle(orig);
+ applyStyles(cloned, computedStyle);
+ }
+ });
+
+ return clone;
+ };
+
+ // 계산된 스타일을 인라인으로 적용
+ const applyStyles = (element: HTMLElement, computedStyle: CSSStyleDeclaration) => {
+ const importantStyles = [
+ 'display', 'position', 'width', 'height', 'min-width', 'min-height', 'max-width', 'max-height',
+ 'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
+ 'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left',
+ 'border', 'border-width', 'border-style', 'border-color',
+ 'border-top', 'border-right', 'border-bottom', 'border-left',
+ 'border-collapse', 'border-spacing',
+ 'background', 'background-color',
+ 'color', 'font-family', 'font-size', 'font-weight', 'font-style',
+ 'text-align', 'text-decoration', 'vertical-align', 'line-height', 'white-space',
+ 'flex', 'flex-direction', 'flex-wrap', 'justify-content', 'align-items', 'gap',
+ 'grid-template-columns', 'grid-template-rows', 'grid-gap',
+ 'table-layout', 'overflow', 'visibility', 'opacity',
+ ];
+
+ importantStyles.forEach((prop) => {
+ const value = computedStyle.getPropertyValue(prop);
+ if (value && value !== 'none' && value !== 'normal' && value !== 'auto') {
+ element.style.setProperty(prop, value);
+ }
+ });
+ };
+
+ // PDF 핸들러 (서버사이드 Puppeteer로 생성)
+ const handlePdf = useCallback(async () => {
+ if (onPdf) {
+ onPdf();
+ return;
+ }
+
+ try {
+ toast.loading('PDF 생성 중...', { id: 'pdf-generating' });
+
+ // print-area 영역 찾기
+ const printAreaEl = document.querySelector('.print-area') as HTMLElement;
+ if (!printAreaEl) {
+ toast.error('PDF 생성 실패: 문서 영역을 찾을 수 없습니다.', { id: 'pdf-generating' });
+ return;
+ }
+
+ // 인라인 스타일 적용된 HTML 복제
+ const clonedElement = cloneWithInlineStyles(printAreaEl);
+ const html = clonedElement.outerHTML;
+
+ // 서버 API 호출
+ const response = await fetch('/api/pdf/generate', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ html,
+ title,
+ orientation: 'portrait',
+ documentNumber: pdfMeta?.documentNumber || '',
+ createdDate: pdfMeta?.createdDate || new Date().toISOString().slice(0, 10),
+ showHeaderFooter: pdfMeta?.showHeaderFooter !== false,
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error('PDF 생성 API 오류');
+ }
+
+ // PDF Blob 다운로드
+ const blob = await response.blob();
+ const url = window.URL.createObjectURL(blob);
+ const link = document.createElement('a');
+ link.href = url;
+
+ // 파일명 생성 (날짜 포함)
+ const date = new Date().toISOString().slice(0, 10).replace(/-/g, '');
+ link.download = `${title}_${date}.pdf`;
+
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ window.URL.revokeObjectURL(url);
+
+ toast.success('PDF가 생성되었습니다.', { id: 'pdf-generating' });
+ } catch (error) {
+ console.error('PDF 생성 오류:', error);
+ toast.error('PDF 생성 중 오류가 발생했습니다.', { id: 'pdf-generating' });
+ }
+ }, [onPdf, title, pdfMeta]);
+
// 콘텐츠 렌더링
const renderContent = (): ReactNode => {
// 1. children이 있으면 children 사용 (정적 모드)
@@ -179,7 +296,7 @@ export function DocumentViewer({
onApprove={onApprove}
onReject={onReject}
onCopy={onCopy}
- onPdf={onPdf}
+ onPdf={handlePdf}
onEmail={onEmail}
onFax={onFax}
onKakao={onKakao}
diff --git a/src/components/hr/AttendanceManagement/index.tsx b/src/components/hr/AttendanceManagement/index.tsx
index 12aa8ea2..ff7a6ad2 100644
--- a/src/components/hr/AttendanceManagement/index.tsx
+++ b/src/components/hr/AttendanceManagement/index.tsx
@@ -7,12 +7,12 @@ import {
UserCheck,
AlertCircle,
Calendar,
- Download,
Plus,
FileText,
Edit,
Search,
} from 'lucide-react';
+import type { ExcelColumn } from '@/lib/utils/excel-download';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -351,10 +351,23 @@ export function AttendanceManagement() {
router.push(`/ko/hr/documents/new?type=${data.reasonType}`);
}, [router]);
- const handleExcelDownload = useCallback(() => {
- console.log('Excel download');
- // TODO: 엑셀 다운로드 기능 구현
- }, []);
+ // ===== 엑셀 컬럼 정의 =====
+ const excelColumns: ExcelColumn[] = useMemo(() => [
+ { header: '부서', key: 'department' },
+ { header: '직책', key: 'position' },
+ { header: '이름', key: 'employeeName' },
+ { header: '직급', key: 'rank' },
+ { header: '기준일', key: 'baseDate' },
+ { header: '출근', key: 'checkIn', transform: (value) => value ? String(value).substring(0, 5) : '-' },
+ { header: '퇴근', key: 'checkOut', transform: (value) => value ? String(value).substring(0, 5) : '-' },
+ { header: '휴게', key: 'breakTime', transform: (value) => value ? String(value) : '-' },
+ { header: '연장근무', key: 'overtimeHours', transform: (value) => value ? String(value) : '-' },
+ { header: '상태', key: 'status', transform: (value) => ATTENDANCE_STATUS_LABELS[value as AttendanceStatus] },
+ { header: '사유', key: 'reason', transform: (value) => {
+ const reason = value as AttendanceRecord['reason'];
+ return reason?.label || '-';
+ }},
+ ], []);
const handleReasonClick = useCallback((record: AttendanceRecord) => {
if (record.reason?.documentId) {
@@ -458,12 +471,15 @@ export function AttendanceManagement() {
searchPlaceholder: '이름, 부서 검색...',
+ // 엑셀 다운로드 설정 (클라이언트 사이드 필터링이므로 filteredData 사용)
+ excelDownload: {
+ columns: excelColumns,
+ filename: '근태현황',
+ sheetName: '근태',
+ },
+
extraFilters: (
-