This commit is contained in:
2026-05-06 18:16:28 +02:00
parent 441643a3d6
commit b525980db9
12 changed files with 7442 additions and 356 deletions

117
bun.lock
View File

@@ -9,14 +9,15 @@
"name": "dare-client", "name": "dare-client",
"version": "2.0.0", "version": "2.0.0",
"dependencies": { "dependencies": {
"@tailwindcss/vite": "^4.2.1", "@tailwindcss/vite": "^4.2.4",
"sigpro": "^1.2.24", "sigpro": "git+http://gitea:3000/natxocc/sigpro",
"sigpro-ui": "^1.2.1", "sigpro-ui": "git+http://gitea:3000/natxocc/sigpro-ui",
}, },
"devDependencies": { "devDependencies": {
"@iconify/json": "^2.2.443", "@iconify/json": "^2.2.469",
"@iconify/tailwind4": "^1.2.1", "@iconify/tailwind4": "^1.2.3",
"vite": "^8.0.0", "tailwindcss": "^4.2.4",
"vite": "^8.0.10",
}, },
}, },
"server": { "server": {
@@ -34,15 +35,15 @@
"packages": { "packages": {
"@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="], "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
"@cyberalien/svg-utils": ["@cyberalien/svg-utils@1.2.9", "", { "dependencies": { "@iconify/types": "^2.0.0" } }, "sha512-bd5I3TQ2k/xqcNB6P6GpFKeid3OgqgGSRz704+XHoGNBAsI7wwYKS6nuxAn26ciUtRjGKovfEy66ryEY9UGUwQ=="], "@cyberalien/svg-utils": ["@cyberalien/svg-utils@1.2.15", "", { "dependencies": { "@iconify/types": "^2.0.0" } }, "sha512-ZbKU6npzW5PNocdoLVJYfKzaP+c/RpT6JUkoaKrW1DOcw6lyXub8XtcNpI3xok6FnyNjS6ZbsrrtjTnS9yeZAQ=="],
"@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="], "@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
"@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
"@iconify/json": ["@iconify/json@2.2.463", "", { "dependencies": { "@iconify/types": "*", "pathe": "^2.0.3" } }, "sha512-VZ0n+99OWe9677b04KPF0NajDbFEyWNxMalXZA/4j8HrqyVvY+N1XN/EIER4ceQlKQJ501w9UxLJZjZ5mga0xA=="], "@iconify/json": ["@iconify/json@2.2.469", "", { "dependencies": { "@iconify/types": "*", "pathe": "^2.0.3" } }, "sha512-ARAC23HXrR1QpoR/z+tjGqI3kWgkYsYKJwezWLc8m47dvGpuvZTmQYXSIJVpnHGtUPKylC3jqWb0udXfoDLWeg=="],
"@iconify/tailwind4": ["@iconify/tailwind4@1.2.3", "", { "dependencies": { "@iconify/tools": "^5.0.5", "@iconify/types": "^2.0.0", "@iconify/utils": "^3.1.0" }, "peerDependencies": { "tailwindcss": ">= 4.0.0" } }, "sha512-z8SKiMHRASJKF/IY//87MF88lcB7ulxh8vlhQXXLWsBkNtOh6ese9R41MyGpQeqXdRvQVt+/fX2glQtHFjQ+MA=="], "@iconify/tailwind4": ["@iconify/tailwind4@1.2.3", "", { "dependencies": { "@iconify/tools": "^5.0.5", "@iconify/types": "^2.0.0", "@iconify/utils": "^3.1.0" }, "peerDependencies": { "tailwindcss": ">= 4.0.0" } }, "sha512-z8SKiMHRASJKF/IY//87MF88lcB7ulxh8vlhQXXLWsBkNtOh6ese9R41MyGpQeqXdRvQVt+/fX2glQtHFjQ+MA=="],
@@ -50,7 +51,7 @@
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
"@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="], "@iconify/utils": ["@iconify/utils@3.1.1", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.2" } }, "sha512-MwzoDtw9rO1x+qfgLTV/IVXsHDBqeYZoMIQC8SfxfYSlaSUG+oWiAcoiB1yajAda6mqblm4/1/w2E8tRu7a7Tw=="],
"@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="],
@@ -116,69 +117,69 @@
"@nodable/entities": ["@nodable/entities@2.1.0", "", {}, "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA=="], "@nodable/entities": ["@nodable/entities@2.1.0", "", {}, "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA=="],
"@oxc-project/types": ["@oxc-project/types@0.124.0", "", {}, "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg=="], "@oxc-project/types": ["@oxc-project/types@0.127.0", "", {}, "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ=="],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.15", "", { "os": "android", "cpu": "arm64" }, "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA=="], "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.17", "", { "os": "android", "cpu": "arm64" }, "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ=="],
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg=="], "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw=="],
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw=="], "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw=="],
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.15", "", { "os": "freebsd", "cpu": "x64" }, "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw=="], "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.17", "", { "os": "freebsd", "cpu": "x64" }, "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw=="],
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm" }, "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA=="], "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17", "", { "os": "linux", "cpu": "arm" }, "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ=="],
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w=="], "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q=="],
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ=="], "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg=="],
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "ppc64" }, "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ=="], "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA=="],
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "s390x" }, "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ=="], "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "s390x" }, "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA=="],
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA=="], "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "x64" }, "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA=="],
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw=="], "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.17", "", { "os": "linux", "cpu": "x64" }, "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw=="],
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.15", "", { "os": "none", "cpu": "arm64" }, "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg=="], "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.17", "", { "os": "none", "cpu": "arm64" }, "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA=="],
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.15", "", { "dependencies": { "@emnapi/core": "1.9.2", "@emnapi/runtime": "1.9.2", "@napi-rs/wasm-runtime": "^1.1.3" }, "cpu": "none" }, "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q=="], "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.17", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA=="],
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA=="], "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17", "", { "os": "win32", "cpu": "arm64" }, "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA=="],
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "x64" }, "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g=="], "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.17", "", { "os": "win32", "cpu": "x64" }, "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.15", "", {}, "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g=="], "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.17", "", {}, "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg=="],
"@tailwindcss/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="], "@tailwindcss/node": ["@tailwindcss/node@4.2.4", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.4" } }, "sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA=="],
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="], "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.4", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.4", "@tailwindcss/oxide-darwin-arm64": "4.2.4", "@tailwindcss/oxide-darwin-x64": "4.2.4", "@tailwindcss/oxide-freebsd-x64": "4.2.4", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.4", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.4", "@tailwindcss/oxide-linux-arm64-musl": "4.2.4", "@tailwindcss/oxide-linux-x64-gnu": "4.2.4", "@tailwindcss/oxide-linux-x64-musl": "4.2.4", "@tailwindcss/oxide-wasm32-wasi": "4.2.4", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.4", "@tailwindcss/oxide-win32-x64-msvc": "4.2.4" } }, "sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q=="],
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.2", "", { "os": "android", "cpu": "arm64" }, "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg=="], "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.4", "", { "os": "android", "cpu": "arm64" }, "sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g=="],
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg=="], "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg=="],
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw=="], "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg=="],
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ=="], "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw=="],
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2", "", { "os": "linux", "cpu": "arm" }, "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ=="], "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA=="],
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw=="], "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw=="],
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag=="], "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g=="],
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg=="], "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA=="],
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ=="], "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA=="],
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.2", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q=="], "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.4", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw=="],
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ=="], "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ=="],
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA=="], "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw=="],
"@tailwindcss/vite": ["@tailwindcss/vite@4.2.2", "", { "dependencies": { "@tailwindcss/node": "4.2.2", "@tailwindcss/oxide": "4.2.2", "tailwindcss": "4.2.2" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w=="], "@tailwindcss/vite": ["@tailwindcss/vite@4.2.4", "", { "dependencies": { "@tailwindcss/node": "4.2.4", "@tailwindcss/oxide": "4.2.4", "tailwindcss": "4.2.4" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-pCvohwOCspk3ZFn6eJzrrX3g4n2JY73H6MmYC87XfGPyTty4YsCjYTMArRZm/zOI8dIt3+EcrLHAFPe5A4bgtw=="],
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
@@ -218,13 +219,13 @@
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
"enhanced-resolve": ["enhanced-resolve@5.20.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="], "enhanced-resolve": ["enhanced-resolve@5.21.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA=="],
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"fast-xml-builder": ["fast-xml-builder@1.1.5", "", { "dependencies": { "path-expression-matcher": "^1.1.3" } }, "sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA=="], "fast-xml-builder": ["fast-xml-builder@1.1.5", "", { "dependencies": { "path-expression-matcher": "^1.1.3" } }, "sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA=="],
"fast-xml-parser": ["fast-xml-parser@5.7.0", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.1.5", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-MTcrUoRQ1GSQ9iG3QJzBGquYYYeA7piZaJoIWbPFGbRn6Jj6z7xgoAyi4DrZX4y2ZIQQBF59gc/zmvvejjgoFQ=="], "fast-xml-parser": ["fast-xml-parser@5.7.2", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.1.5", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w=="],
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
@@ -234,7 +235,7 @@
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"hono": ["hono@4.12.14", "", {}, "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w=="], "hono": ["hono@4.12.16", "", {}, "sha512-jN0ZewiNAWSe5khM3EyCmBb250+b40wWbwNILNfEvq84VREWwOIkuUsFONk/3i3nqkz7Oe1PcpM2mwQEK2L9Kg=="],
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
@@ -276,9 +277,9 @@
"modern-tar": ["modern-tar@0.7.6", "", {}, "sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg=="], "modern-tar": ["modern-tar@0.7.6", "", {}, "sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg=="],
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="],
"nodemailer": ["nodemailer@8.0.5", "", {}, "sha512-0PF8Yb1yZuQfQbq+5/pZJrtF6WQcjTd5/S4JOHs9PGFxuTqoB/icwuB44pOdURHJbRKX1PPoJZtY7R4VUoCC8w=="], "nodemailer": ["nodemailer@8.0.7", "", {}, "sha512-pkjE4mkBzQjdJT4/UmlKl3pX0rC9fZmjh7c6C9o7lv66Ac6w9WCnzPzhbPNxwZAzlF4mdq4CSWB5+FbK6FWCow=="],
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
@@ -294,9 +295,9 @@
"pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
"postcss": ["postcss@8.5.10", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ=="], "postcss": ["postcss@8.5.13", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag=="],
"rolldown": ["rolldown@1.0.0-rc.15", "", { "dependencies": { "@oxc-project/types": "=0.124.0", "@rolldown/pluginutils": "1.0.0-rc.15" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-x64": "1.0.0-rc.15", "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g=="], "rolldown": ["rolldown@1.0.0-rc.17", "", { "dependencies": { "@oxc-project/types": "=0.127.0", "@rolldown/pluginutils": "1.0.0-rc.17" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.17", "@rolldown/binding-darwin-arm64": "1.0.0-rc.17", "@rolldown/binding-darwin-x64": "1.0.0-rc.17", "@rolldown/binding-freebsd-x64": "1.0.0-rc.17", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA=="],
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
@@ -306,9 +307,9 @@
"sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="],
"sigpro": ["sigpro@1.2.24", "", {}, "sha512-0EtkWBzkUX1+GzHiCxthHOSOFatgxkNuogpdP4yEO/aSDCiNLt6NiVTU84dBUQ0tQM7erdsGdqHk9Hp/mzwX/w=="], "sigpro": ["sigpro@git+http://gitea:3000/natxocc/sigpro#ab0e6e06974308b28cc54229cf0301ab81d7c132", {}, "ab0e6e06974308b28cc54229cf0301ab81d7c132"],
"sigpro-ui": ["sigpro-ui@1.2.1", "", {}, "sha512-mtUfLHhP/T1msiVG0T1YxR+4L4N+zB9nazPrY9Ypw1d97YhP7QW6T1gPp/TbhLnFOdIgIkNGz04J5gpbkdtQaA=="], "sigpro-ui": ["sigpro-ui@git+http://gitea:3000/natxocc/sigpro-ui#2832e1df1e66e3f7d8ba02342729e8d534b9b1a1", {}, "2832e1df1e66e3f7d8ba02342729e8d534b9b1a1"],
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
@@ -316,25 +317,23 @@
"svgo": ["svgo@4.0.1", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.5.0" }, "bin": "./bin/svgo.js" }, "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w=="], "svgo": ["svgo@4.0.1", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.5.0" }, "bin": "./bin/svgo.js" }, "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w=="],
"tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="], "tailwindcss": ["tailwindcss@4.2.4", "", {}, "sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA=="],
"tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="], "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="],
"tinyexec": ["tinyexec@1.1.1", "", {}, "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg=="], "tinyexec": ["tinyexec@1.1.2", "", {}, "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA=="],
"tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], "tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="], "ufo": ["ufo@1.6.4", "", {}, "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA=="],
"undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], "undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="],
"vite": ["vite@8.0.8", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.15", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw=="], "vite": ["vite@8.0.10", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.10", "rolldown": "1.0.0-rc.17", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw=="],
"@rolldown/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],

286
client/App.js Normal file
View File

@@ -0,0 +1,286 @@
// App.js
import { $, watch, h, when } from "./sigpro.js";
import {
Navbar,
Drawer,
DrawerToggle,
DrawerContent,
DrawerSide,
DrawerOverlay,
Menu,
Tabs,
Swap,
SwapToggle,
SwapOn,
SwapOff,
Icon,
Avatar,
Dropdown,
DropdownButton,
DropdownContent,
Modal,
Fieldset,
Input,
Button
} from "./sigpro-ui.js";
import './sigpro-ui.css';
export const App = () => {
// Tema oscuro/claro
const isDark = $(localStorage.getItem("theme") === "dark"
|| (!localStorage.getItem("theme") && window.matchMedia("(prefers-color-scheme: dark)").matches));
// Sincronizar el tema con el atributo data-theme del HTML
watch(isDark, (dark) => {
const theme = dark ? "dark" : "light";
document.documentElement.setAttribute("data-theme", theme);
localStorage.setItem("theme", theme);
});
// Activar tema inicial
document.documentElement.setAttribute("data-theme", isDark() ? "dark" : "light");
// Estado de login persistente
const logged = $(false, "logged");
// Estado para buscador
const searchQuery = $("");
// Estado para modal de login
const showLoginModal = $(false);
const loginForm = {
username: $(""),
password: $("")
};
// Pestañas: la primera (Escritorio) no es cerrable
const tabs = $([
{
label: "Escritorio",
content: () => `¡Bienvenido al escritorio!`,
closable: false
}
]);
const activeTab = $(0);
const openDrawer = $(false);
// Elementos del menú en el drawer
const menuItems = [
{
label: "Clientes",
children: [
{ label: "Buscar Cliente", onclick: () => openTab("Clientes") },
]
},
{
label: "Recibos",
children: [
{ label: "Buscar Recibo" },
{ label: "Pendientes" },
{ label: "Extornos" },
],
},
{
label: "Polizas",
children: [
{ label: "Buscar Póliza", onclick: () => openTab("Polizas") },
{ label: "Nueva producción", onclick: () => openTab("Polizas") },
{ label: "Anulaciones", onclick: () => openTab("Polizas") },
{ label: "Renovación Cartera", onclick: () => openTab("Polizas") }
]
},
{
label: "Comercial",
children: [
{ label: "Oportunidades" },
],
},
{
label: "Siniestros",
children: [
{ label: "Nuevo Siniestro" },
{ label: "Buscar Siniestro" },
]
},
{
label: "Soporte",
children: [
{ label: "Tickets" },
{ label: "Reportes" },
]
}
];
// Referencia al contenedor de pestañas para manejar el foco
let tabsContainerRef = null;
let drawerToggleRef = null;
// Abre o crea una pestaña, cierra el drawer
const openTab = (label) => {
const currentTabs = tabs();
if (currentTabs.length >= 15) return;
const newTab = {
label,
content: () => `¡Bienvenido al escritorio!`,
closable: true
};
tabs([...currentTabs, newTab]);
activeTab(tabs().length - 1);
closeDrawer();
};
const closeDrawer = () => {
openDrawer(false);
if (drawerToggleRef) drawerToggleRef.checked = false;
};
// Manejo del login
const handleLogin = () => {
logged(true);
showLoginModal(false);
loginForm.username("");
loginForm.password("");
};
const handleLogout = () => {
logged(false);
};
return [
Drawer({}, [
// Control oculto del drawer
DrawerToggle({
id: "app-drawer",
ref: (el) => drawerToggleRef = el,
checked: openDrawer,
onchange: (e) => openDrawer(e.target.checked)
}),
// Contenido principal
DrawerContent({}, [
Navbar({ class: "bg-base-100 shadow-lg align-center" }, [
// Botón hamburguesa
div({ class: "flex-none" }, [
label({ for: "app-drawer", class: "btn btn-ghost btn-square" }, [
Icon({}, "icon-[lucide--menu]")
])
]),
// Buscador
div({ class: "flex-1 max-w-md mx-4" }, [
Input({
type: "search",
placeholder: "Buscar...",
value: searchQuery,
left: span({ class: "icon-[lucide--search]" }),
oninput: (e) => console.log(e.target.value)
})
]),
// Espaciador central
div({ class: "flex-1" }, []),
// Swap para tema claro/oscuro
Swap({ class: "text-xl" }, [
SwapToggle({ value: isDark, class: "swap-rotate" }),
SwapOn({}, span({ class: "icon-[lucide--moon]" })),
SwapOff({}, span({ class: "icon-[lucide--sun]" })),
]),
// Avatar con dropdown o botón de login
when(logged,
() => Dropdown({ class: "flex-none ml-2 dropdown-bottom dropdown-end" }, [
DropdownButton({ class: "btn-circle btn btn-ghost", tabindex: "0", role: "button" }, [
div({ class: "w-10 rounded-full flex items-center justify-center" }, [
Icon({}, "icon-[lucide--user] text-xl")
])
]),
DropdownContent(
{ class: "menu bg-base-100 rounded-box w-52 p-2 shadow" },
[
Menu({
class: "bg-base-100 max-w-xs w-full",
items: [
{ label: "Mis mensajes", onclick: () => hide() },
{ label: "Delete", onclick: () => hide() },
{ label: "Cerrar Sesión", onclick: handleLogout },
],
}),
],
),
]),
() => Button({
class: "flex-none ml-2 btn btn-ghost btn-circle relative",
onclick: () => showLoginModal(true)
}, [
Icon({}, "icon-[lucide--user] text-xl"),
])
)
]),
// Área principal con las pestañas
div({
class: "p-4",
ref: (el) => tabsContainerRef = el
}, [
Tabs({
class: 'tabs-box',
items: tabs,
activeIndex: activeTab
})
])
]),
// Lateral del drawer
DrawerSide({ class: "z-50" }, [
DrawerOverlay({ for: "app-drawer" }),
div({
class: "menu bg-base-200 text-base-content min-h-full w-80 p-4"
}, [
h2({ class: "text-lg font-bold mb-4" }, ["Menú"]),
Menu({ items: menuItems, class: "bg-base-200 max-w-xs w-full" })
])
])
]),
// Modal de login
Modal({
open: showLoginModal,
actions: [
Button({
class: "btn btn-ghost",
onclick: () => showLoginModal(false)
}, "Cancelar"),
Button({
class: "btn btn-primary",
onclick: handleLogin
}, "Entrar")
]
}, [
Fieldset({ label: "Iniciar sesión", class: "bg-base-200 border-base-300 rounded-box border gap-3 p-4", }, [
Input({
class: "w-full",
type: "text",
label: "Usuario",
float: true,
placeholder: "Nombre de usuario",
value: loginForm.username
})
,
Input({
class: "w-full",
type: "password",
label: "Contraseña",
float: true,
placeholder: "Contraseña",
value: loginForm.password
})
])
])
];
};

15
client/app.css Normal file
View File

@@ -0,0 +1,15 @@
@import "tailwindcss";
@plugin "@iconify/tailwind4";
@font-face {
font-family: "Plus Jakarta Sans";
src: url("/jakarta.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
}
:root {
font-size: 14px;
/* font-family: "Plus Jakarta Sans", ui-sans-serif, system-ui, sans-serif; */
}

4
client/main.js Normal file
View File

@@ -0,0 +1,4 @@
import { mount } from './sigpro.js';
import './app.css';
import { App } from './App.js';
mount(App, '#app');

View File

@@ -3,20 +3,21 @@
"version": "2.0.0", "version": "2.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"del": "bun pm cache rm && rm -f bun.lockb $$ rm -f bun.lock && rm node_modules/.vite/deps -r",
"kill": "pkill -9 bun && pkill -9 node || true", "kill": "pkill -9 bun && pkill -9 node || true",
"del": "rm node_modules/.vite/deps -r",
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@tailwindcss/vite": "^4.2.1", "@tailwindcss/vite": "^4.2.4",
"sigpro": "^1.2.24", "sigpro": "git+http://gitea:3000/natxocc/sigpro",
"sigpro-ui": "^1.2.1" "sigpro-ui": "git+http://gitea:3000/natxocc/sigpro-ui"
}, },
"devDependencies": { "devDependencies": {
"@iconify/json": "^2.2.443", "@iconify/json": "^2.2.469",
"@iconify/tailwind4": "^1.2.1", "@iconify/tailwind4": "^1.2.3",
"vite": "^8.0.0" "tailwindcss": "^4.2.4",
"vite": "^8.0.10"
} }
} }

View File

@@ -1,247 +0,0 @@
// App.js
/**
* Vistas de la aplicación (pueden ir en archivos separados luego)
*/
import { Navbar, Swap, Menu, Checkbox, Input, Button, Autocomplete, Datepicker, Colorpicker, Fileinput, Toast } from "sigpro-ui";
const toggle = $(false);
// const consoleToggle = $(()=>console.log(toggle()))
const Home = () => {
const valor = $("jjj");
const miCheck = $(false); // Creamos la señal
return Div({ class: "gap-3 flex flex-col" }, [
H1("Dashboard Principal"),
Button({ tooltip: "tooltip", badge: "22" }, "Hola"),
Input({
label: "Correo Electrónico",
type: "email",
placeholder: "ejemplo@correo.com",
value: valor, // Binding automático
}),
Span({ class: "gap-4 text-4xl" }, () => valor()),
P("Bienvenido a la interfaz reactiva de SigPro. Aquí puedes ver el estado global."),
Checkbox({ tooltip: "Tooltip", toggle: toggle, value: miCheck, label: "Checkbox" }),
miCheck,
toggle,
Button(
{
class: "btn-primary",
onclick: () => {
Toast("Cambio de toggle");
toggle(!toggle());
},
},
"Lanzar Toast",
),
]);
};
const Profile = (params) => {
const miFecha = $();
const miRango = $();
const selectedFruit = $("Apple");
const fruits = ["Apple", "Banana", "Cherry", "Dragonfruit", "Elderberry"];
const colorFondo = $("#ef4444");
const misArchivos = $([]);
const textoInput = $(() => {
const f = miFecha;
if (!f.start) return "";
return f.end ? `${f.start} - ${f.end}` : `${f.start}...`;
});
return Div({ class: "p-4 space-y-4" }, [
H2({ class: "text-xl font-bold" }, `Perfil: ${params.id}`),
Autocomplete({
label: "Selecciona una fruta",
value: selectedFruit,
options: fruits,
onSelect: (val) => console.log("Seleccionado:", val),
}),
Datepicker({ value: miFecha, label: "Fecha", placeholder: textoInput, hour: true }),
Datepicker({ value: miRango, label: "Fecha", placeholder: textoInput, range: true, hour: true }),
Colorpicker({ show: true, label: "Color del tema", value: colorFondo }),
Input({ type: "number", label: "Number" }),
Input({ type: "email", label: "Email" }),
Input({ label: "Text" }),
Input({ type: "date", label: "Date" }),
Input({ type: "password", label: "Password" }),
$html("p", {}, () => `Has elegido: ${selectedFruit()}`),
Fileinput({
tooltip: "Formatos: PDF, JPG",
max: 5,
// Cada vez que la lista cambia, se dispara esto
onSelect: async (files) => {
if (files.length === 0) return;
console.log("Subiendo archivos automáticamente...", files);
const formData = new FormData();
files.forEach((file) => formData.append("files[]", file));
// await fetch('/api/upload', { method: 'POST', body: formData });
},
}),
Div({ class: "pt-4" }, [
Button(
{
class: "btn-sm btn-outline",
onclick: () => $router.to("/"),
},
"Volver",
),
]),
]);
};
/**
* Componente Principal
*/
export const App = () => {
// Estado local de la App (ejemplo: tema o usuario)
const isDark = $(false, "sigpro-theme");
// Efecto para cambiar el tema en el HTML
$watch(() => {
document.documentElement.setAttribute("data-theme", isDark() ? "dark" : "light");
});
const menuItems = [
{ label: "Inicio", onclick: () => $router.to("/") },
{ label: "Mi Perfil", onclick: () => $router.to("/profile/42") },
{ label: "Tareas TODO", onclick: () => $router.to("/todo") }, // <-- Nueva opción
{ label: "Ajustes", onclick: () => Toast("Ajustes no disponibles", "alert-error") },
];
return Div({ class: "min-h-screen flex flex-col" }, [
Navbar({ class: "sticky top-0 z-50 bg-base-100/80 backdrop-blur border-b border-base-300" }, [
Div({ class: "flex-1" }, [
A(
{
class: "btn btn-ghost text-xl font-black tracking-tighter",
onclick: () => $router.to("/"),
},
"SIGPRO",
),
]),
Button({ disabled: true }, "Disabled"),
Div({ class: "flex items-center gap-2" }, [
Swap({
class: "swap-rotate",
value: isDark,
on: "🌙",
off: "☀️",
}),
Button({ class: "btn-circle btn-ghost", icon: "👤" }),
]),
]),
// --- LAYOUT PRINCIPAL ---
Div({ class: "flex flex-1" }, [
// Sidebar Lateral
Aside({ class: "w-64 bg-base-200 p-4 hidden md:block" }, [Menu({ items: menuItems, class: "bg-transparent" })]),
// Contenido Dinámico (Router)
Main({ class: "flex-1 p-6 bg-base-100" }, [
$router([
{ path: "/", component: Home },
{ path: "/profile/:id", component: Profile },
{ path: "/todo", component: TodoPage },
{
path: "*",
component: () =>
Div({ class: "text-center py-20" }, [H1({ class: "text-9xl font-bold opacity-20" }, "404"), P("La página que buscas no existe.")]),
},
]),
]),
]),
// --- FOOTER ---
Footer({ class: "footer footer-center p-4 bg-base-300 text-base-content text-xs" }, [P("© 2026 - Built with SigPro Engine")]),
]);
};
const TodoPage = () => {
const newTask = $("");
// Persistencia automática en localStorage gracias a tu Core
const tasks = $([], "sigpro-todo-list");
const addTask = () => {
const val = newTask().trim();
if (!val) return Toast("Escribe algo...", "alert-warning");
tasks([...tasks(), { id: crypto.randomUUID(), text: val, done: false }]);
newTask(""); // Limpia el input
};
const removeTask = (id) => {
tasks(tasks().filter((t) => t.id !== id));
};
const toggleTask = (id) => {
tasks(tasks().map((t) => (t.id === id ? { ...t, done: !t.done } : t)));
};
// Señal computada para el contador
const pendingCount = $(() => tasks().filter((t) => !t.done).length);
const misDatos = $([{ id: 1 }]);
return Div({ class: "max-w-md mx-auto space-y-6" }, [
H1({ class: "text-3xl font-black italic" }, "TODO LIST"),
// Input Group
Div({ class: "join w-full" }, [
Input({
class: "join-item w-full",
placeholder: "Nueva tarea...",
value: newTask,
onkeydown: (e) => e.key === "Enter" && addTask(),
}),
Button({ class: "btn-primary join-item", onclick: addTask }, "Añadir"),
]),
// Stats
Div({ class: "flex justify-between items-center opacity-70 text-sm" }, [
Span(() => `Total: ${tasks().length}`),
Span({ class: "badge badge-secondary" }, () => `${pendingCount()} pendientes`),
]),
// Lista Reactiva (Aquí evaluamos tu append con sweep)
Ul({ class: "menu bg-base-200 rounded-box w-full p-2" }, () =>
tasks().length === 0
? Li({ class: "p-4 text-center opacity-50" }, "No hay tareas pendientes")
: tasks().map((task) =>
Li({ class: "flex flex-row items-center gap-2 p-2 border-b border-base-300 last:border-0" }, [
Checkbox({
class: "checkbox-sm",
value: () => task.done,
onclick: () => toggleTask(task.id),
}),
Span(
{
class: `flex-1 ${task.done ? "line-through opacity-40" : ""}`,
onclick: () => toggleTask(task.id),
},
task.text,
),
Button(
{
class: "btn-ghost btn-xs text-error",
onclick: () => removeTask(task.id),
},
"✕",
),
]),
),
),
Button(
{
class: "btn-link btn-xs text-error p-0",
onclick: () => tasks([]),
},
"Limpiar todo",
),
]);
};

View File

@@ -1,38 +0,0 @@
@import "tailwindcss";
@plugin "@iconify/tailwind4";
@plugin "daisyui" {
themes:
light --default,
dark --prefersdark;
include:
alert, avatar, badge, button, card, checkbox, collapse, drawer, dropdown, fab, fieldset, loading, indicator, input, kbd, label, list, menu, modal,
navbar, radio, range, select, skeleton, tab, textarea, toast, toggle, tooltip, validator, rating, mask, swap;
}
@font-face {
font-family: "Plus Jakarta Sans";
src: url("/jakarta.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
}
:root {
font-size: 14px;
/* font-family: "Plus Jakarta Sans", ui-sans-serif, system-ui, sans-serif; */
}
.btn-ghost {
border-color: transparent !important;
}
.floating-label > span {
font-size: 1.2rem;
}
@utility input {
@apply transition-all duration-300 ease-in-out outline-none shrink appearance-none items-center;
&:hover {
background-color: var(--color-base-300);
}
}

View File

@@ -1,4 +0,0 @@
import {$mount} from 'sigpro';
import { App } from './App.js';
import './app.css';
$mount(App, '#app');

6103
client/sigpro-ui.css Normal file

File diff suppressed because it is too large Load Diff

534
client/sigpro-ui.js Normal file
View File

@@ -0,0 +1,534 @@
import { $, watch, h, mount, when, each, isFunc } from "./sigpro.js"
export const val = val => typeof val === "function" ? val() : val;
export const getBy = (item, field = 'label') => (item && typeof item === 'object') ? item[field] : item;
export const cls = (...classes) => classes.filter(Boolean).join(' ').trim();
export const filterBy = (items, query, field = 'label') => {
const q = String(val(query) || '').toLowerCase();
const list = (val(items) || []).map(i => typeof i === 'object' ? i : { label: i, value: i });
return !q ? list : list.filter(item => String(item[field] || '').toLowerCase().includes(q));
};
export const rand = (r) => `${r}-${Math.random().toString(36).slice(2, 9)}`
export const hide = () => document.activeElement?.blur()
const currentLocale = $("en");
export const lang = {
es: { uploadFiles: "Arrastrar y soltar o click para seleccionar..." },
en: { uploadFiles: "Drag and drop or click to select" }
};
export const setLocale = (locale) => { if (lang[locale]) currentLocale(locale) }
export const t = t => () => lang[currentLocale()][t] || t;
const c1 = (tag, cls) => (p) => h(tag, { ...p, class: `${cls} ${p?.class || ''}`.trim() })
const c2 = (tag, cls) => (p, c) => h(tag, { ...p, class: `${cls} ${p?.class || ''}`.trim() }, c)
const ct = (tag, cls, type) => (p) => h(tag, { type, ...p, class: `${cls} ${p?.class || ''}`.trim() })
export const Alert = c2("div", "alert")
export const Avatar = (p, c) => h("div", { class: "avatar" }, h('div', { class: p.class }, c))
export const AvatarGroup = c2("div", "avatar-group -space-x-6")
export const Badge = c2("span", "badge")
export const Breadcrumbs = c2("div", "breadcrumbs")
export const Button = c2("button", "btn")
export const Card = c2("div", "card")
export const CardTitle = c2("div", "card-title")
export const CardBody = c2("div", "card-body")
export const CardActions = c2("div", "card-actions")
export const Carousel = c2("div", "carousel")
export const CarouselItem = c2("div", "carousel-item")
export const Chat = c2("div", "chat")
export const ChatBubble = c2("div", "chat-bubble")
export const ChatFooter = c2("div", "chat-footer")
export const ChatHeader = c2("div", "chat-header")
export const ChatImage = (p, c) => h("div", { ...p, class: cls("chat-image avatar", p.class) }, h("div", { class: "w-10 rounded-full" }, typeof c === "string" ? h("img", { src: c, alt: "avatar" }) : c))
export const Checkbox = ct("input", "checkbox", "checkbox")
export const Drawer = c2("div", "drawer")
export const DrawerToggle = (p) => input({ ...p, type: 'checkbox', class: 'drawer-toggle', checked: () => val(p.checked), onchange: (e) => isFunc(p.checked) && p.checked(e.target.checked) })
export const DrawerContent = c2("div", "drawer-content")
export const DrawerSide = c2("div", "drawer-side")
export const DrawerOverlay = (p) => label({ ...p, for: p.for, class: cls('drawer-overlay', p.class) })
export const Divider = c1("div", "divider")
export const Dropdown = c2("div", "dropdown")
export const DropdownButton = (p, c) => (h('div', { ...p, tabindex: '0', role: 'button', class: cls('btn', p.class) }, c))
export const DropdownContent = (p, c) => (h('div', { ...p, tabindex: '0', class: cls('dropdown-content', p.class) }, c))
export const Fab = (p, c) => h("div", { class: "fab" }, [h('div', { tabindex: "0", role: "button", class: cls('btn', p.class) }, Icon({},p.icon)), c])
export const Fieldset = (p, c) => h("fieldset", { class: cls("fieldset", p.class) }, [h("legend", { class: "fieldset-legend" }, p.label), c])
export const Icon = (p, c) => h("span", { ...p, class: cls(c, p.class) })
export const Indicator = (p, c) => h("div", { ...p, class: cls("indicator", p.class) }, [p.value && h("span", { class: cls("indicator-item badge", p.class) }, p.value), c])
export const Kbd = c2("kbd", "kbd")
export const List = c2("ul", "list")
export const ListRows = (p) => () => (val(p.items) || []).map((item, idx) => h('li', { class: cls('list-row', p.class, item?.class) }, typeof p.render === 'function' ? p.render(item, idx) : item))
export const Loading = c2("span", "loading loading-spinner")
export const Navbar = c2("div", "navbar")
export const Progress = c1("progress", "progress")
export const Radial = (p, c) => h("div", { class: cls("radial-progress", p.class), style: `--value:${val(p.value) ?? 0};`, role: "progressbar", "aria-valuenow": p.value }, c)
export const Radio = ct("input", "radio", "radio")
export const Range = ct("input", "range", "range")
export const Rating = c2("div", "rating")
export const RatingItems = (p) => [...Array(p.count)].map((_, i) => h('input', { class: cls('mask', p.class), name: p.name, type: 'radio', checked: () => val(p.value) === i, onchange: () => isFunc(p.value) ? p.value(i) : p.onchange?.(i) }))
export const Skeleton = c1("div", "skeleton")
export const SkeletonText = c1("span", "skeleton skeleton-text")
export const Stack = c2("div", "stack")
export const Stats = c2("div", "stats shadow")
export const Steps = c2("ul", "steps")
export const Step = (p, c) => h("li", { ...p, class: cls("step", p.class), "data-content": p.dataContent }, c)
export const Swap = c2("label", "swap")
export const SwapToggle = (p) => h('input', { type: 'checkbox', checked: () => val(p.value), onchange: (e) => isFunc(p.value) && p.value(e.target.checked), class: p.class })
export const SwapOn = c2("div", "swap-on")
export const SwapOff = c2("div", "swap-off")
export const Table = c2("table", "table")
export const Textarea = c1("textarea", "textarea")
export const Textrotate = (p, c) => h('span', { ...p, class: cls('text-rotate', p.class) }, h('span', {}, c))
export const Timeline = c2("ul", "timeline")
export const Toggle = ct("input", "toggle", "checkbox")
export const Tooltip = (p, c) => h("div", { ...p, class: cls("tooltip", p.class), "data-tip": p.tip }, c)
export const Accordion = (p) => {
const name = p.name || rand('acc')
return each(p.items, (it) => {
return h('div', { class: cls('collapse', p.class) }, [
h('input', { type: 'radio', name, checked: it.open || undefined }),
it.title ? h('div', { class: cls("collapse-title", `${it.classTitle ?? ' font-semibold'}`) }, it.title) : null,
it.content ? h('div', { class: cls("collapse-content text-sm", `${it.classContent ?? ' font-semibold'}`) }, it.content) : null,
]);
});
};
export const Autocomplete = ({ items, value, onselect, placeholder = '...', ...props }) => {
const query = $(val(value) || '')
const filtered = $(() => filterBy(items, query()))
const pick = (item) => {
const display = getBy(item)
const actual = typeof item === 'string' ? item : item.value
query(display)
if (isFunc(value)) value(actual)
onselect?.(item)
hide()
}
return Dropdown({ class: 'w-80' }, [
h('div', { tabindex: '0', role: 'button', class: 'w-full' }, Input({ ...props, placeholder, value: query, left: Icon({},'icon-[lucide--search]') })),
DropdownContent({ class: 'p-2 bg-base-100 rounded-box shadow-xl w-full max-h-60 overflow-y-auto border border-base-300 z-50' },
h('ul', { class: 'menu flex-col flex-nowrap w-full p-0' }, [
each(filtered, (item) => h('li', {}, [h('a', { onmousedown: (e) => e.preventDefault(), onclick: () => pick(item) }, getBy(item))]), 'value'),
() => filtered().length === 0 ? h('li', { class: 'p-4 opacity-50 text-center' }, 'Sin resultados') : null
])
)
])
};
export const Calendar = (p) => {
const internalDate = $(new Date())
const hoverDate = $(null)
const startHour = $(0)
const endHour = $(0)
const now = new Date()
const todayStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
const fmt = d => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
const rangeMode = () => val(p.range) === true
const current = () => val(p.value)
const selectDate = (date) => {
const s = fmt(date)
const v = current()
if (rangeMode()) {
if (!v?.start || (v.start && v.end)) {
p.onChange?.({ start: s, end: null, ...(p.hour && { startHour: startHour() }) })
} else {
const start = v.start
const nv = s < start ? { start: s, end: start } : { start, end: s }
if (p.hour) { nv.startHour = v.startHour ?? startHour(); nv.endHour = endHour() }
p.onChange?.(nv)
}
} else {
p.onChange?.(p.hour ? `${s}T${String(startHour()).padStart(2, '0')}:00:00` : s)
}
}
const move = (m) => { const d = internalDate(); internalDate(new Date(d.getFullYear(), d.getMonth() + m, 1)) }
const moveYear = (y) => { const d = internalDate(); internalDate(new Date(d.getFullYear() + y, d.getMonth(), 1)) }
const HourSlider = ({ value: hVal, onChange: onH }) => h('div', { class: 'flex-1' }, [
h('div', { class: 'flex gap-2 items-center' }, [
h('input', { type: 'range', min: 0, max: 23, value: hVal, class: 'range range-xs flex-1', oninput: e => onH(+e.target.value) }),
h('span', { class: 'text-sm font-mono min-w-[48px] text-center' }, () => String(val(hVal)).padStart(2, '0') + ':00')
])
])
return h('div', {
class: cls('p-4 bg-base-100 border border-base-300 shadow-2xl rounded-box w-80 select-none', p.class)
}, [
h('div', { class: 'flex justify-between items-center mb-4 gap-1' }, [
h('div', { class: 'flex gap-0.5' }, [
h('button', { type: 'button', class: 'btn btn-ghost btn-xs px-1', onclick: () => moveYear(-1) }, h('span', { class: 'icon-[lucide--chevrons-left]' })),
h('button', { type: 'button', class: 'btn btn-ghost btn-xs px-1', onclick: () => move(-1) }, h('span', { class: 'icon-[lucide--chevron-left]' }))
]),
h('span', { class: 'font-bold uppercase flex-1 text-center' }, () => internalDate().toLocaleString('es-ES', { month: 'short', year: 'numeric' })),
h('div', { class: 'flex gap-0.5' }, [
h('button', { type: 'button', class: 'btn btn-ghost btn-xs px-1', onclick: () => move(1) }, h('span', { class: 'icon-[lucide--chevron-right]' })),
h('button', { type: 'button', class: 'btn btn-ghost btn-xs px-1', onclick: () => moveYear(1) }, h('span', { class: 'icon-[lucide--chevrons-right]' }))
])
]),
h('div', { class: 'grid grid-cols-7 gap-1', onmouseleave: () => hoverDate(null) }, [
...['L', 'M', 'X', 'J', 'V', 'S', 'D'].map(d => h('div', { class: 'text-[10px] opacity-40 font-bold text-center' }, d)),
() => {
const d = internalDate(), y = d.getFullYear(), m = d.getMonth()
const firstDay = new Date(y, m, 1).getDay()
const offset = firstDay === 0 ? 6 : firstDay - 1
const dim = new Date(y, m + 1, 0).getDate()
const cells = []
for (let i = 0; i < offset; i++) cells.push(h('div'))
for (let i = 1; i <= dim; i++) {
const date = new Date(y, m, i), ds = fmt(date)
cells.push(h('button', {
type: 'button',
class: () => {
const v = current(), h = hoverDate()
const isStart = typeof v === 'string' ? v.split('T')[0] === ds : v?.start === ds
const isEnd = v?.end === ds
let inRange = false
if (rangeMode() && v?.start) {
const start = v.start
if (!v.end && h) inRange = (ds > start && ds <= h) || (ds < start && ds >= h)
else if (v.end) inRange = ds > start && ds < v.end
}
const base = 'btn btn-xs p-0 aspect-square min-h-0 h-auto font-normal relative'
const st = isStart || isEnd ? 'btn-primary z-10' : inRange ? 'bg-primary/20 border-none rounded-none' : 'btn-ghost'
const today = ds === todayStr ? 'ring-1 ring-primary ring-inset font-black text-primary' : ''
return cls(base, st, today)
},
onmouseenter: () => rangeMode() && hoverDate(ds),
onclick: () => selectDate(date)
}, i.toString()))
}
return cells
}
]),
p.hour ? h('div', { class: 'mt-3 pt-2 border-t border-base-300' },
rangeMode()
? h('div', { class: 'flex gap-4' }, [HourSlider({ value: startHour, onChange: h => startHour(h) }), HourSlider({ value: endHour, onChange: h => endHour(h) })])
: HourSlider({ value: startHour, onChange: h => startHour(h) })
) : null
])
};
export const Colorpicker = (p) => {
const current = () => val(p.value) || '#000000'
return Dropdown({}, [
DropdownButton({ class: 'btn' }, [
h('div', { class: 'size-5 rounded-sm', style: () => `background-color: ${current()}` }),
p.label && h('span', {}, p.label)
]),
DropdownContent({ class: 'p-0' },
ColorPalette({ value: p.value, onchange: (c) => { isFunc(p.value) ? p.value(c) : p.onchange?.(c) } })
)
])
};
export const ColorPalette = (p) => {
const current = () => val(p.value) || '#000000'
const palette = [
'#000', '#1A1A1A', '#333', '#4D4D4D', '#666', '#808080', '#B3B3B3', '#FFF',
'#450a0a', '#7f1d1d', '#991b1b', '#b91c1c', '#dc2626', '#ef4444', '#f87171', '#fca5a5',
'#431407', '#7c2d12', '#9a3412', '#c2410c', '#ea580c', '#f97316', '#fb923c', '#ffedd5',
'#713f12', '#a16207', '#ca8a04', '#eab308', '#facc15', '#fde047', '#fef08a', '#fff9c4',
'#064e3b', '#065f46', '#059669', '#10b981', '#34d399', '#4ade80', '#84cc16', '#d9f99d',
'#082f49', '#075985', '#0284c7', '#0ea5e9', '#38bdf8', '#7dd3fc', '#22d3ee', '#cffafe',
'#1e1b4b', '#312e81', '#4338ca', '#4f46e5', '#6366f1', '#818cf8', '#a5b4fc', '#e0e7ff',
'#2e1065', '#4c1d95', '#6d28d9', '#7c3aed', '#8b5cf6', '#a855f7', '#d946ef', '#fae8ff'
]
const pick = (c) => {
isFunc(p.value) ? p.value(c) : p.onchange?.(c)
hide()
}
return h('div', {
class: cls('p-3 bg-base-100 rounded-box shadow w-64', p.class)
}, h('div', { class: 'grid grid-cols-8 gap-1' },
palette.map(c => h('button', {
type: 'button',
style: `background-color: ${c}`,
class: () => {
const act = current().toLowerCase() === c.toLowerCase()
return `size-6 rounded-sm cursor-pointer transition-all hover:scale-125 hover:z-10 active:scale-95 outline-none border border-black/5 p-0 min-h-0 ${act ? 'ring-2 ring-offset-1 ring-primary z-10 scale-110' : ''}`
},
onclick: () => { pick(c) }
}))
))
};
export const Datepicker = (p) => {
const displayValue = $("")
const rangeMode = () => val(p.range) === true
watch(() => {
const v = val(p.value)
if (!v) return displayValue("")
let text = ""
if (typeof v === "string") {
text = p.hour && v.includes("T") ? v.replace("T", " ") : v
} else if (v.start && v.end) {
const startStr = p.hour && v.startHour != null ? `${v.start} ${String(v.startHour).padStart(2, "0")}:00` : v.start
const endStr = p.hour && v.endHour != null ? `${v.end} ${String(v.endHour).padStart(2, "0")}:00` : v.end
text = `${startStr} - ${endStr}`
} else if (v.start) {
const startStr = p.hour && v.startHour != null ? `${v.start} ${String(v.startHour).padStart(2, "0")}:00` : v.start
text = `${startStr}...`
}
displayValue(text)
})
const handleChange = (val) => {
if (isFunc(p.value)) p.value(val)
else p.onChange?.(val)
if (!rangeMode() || val?.end != null) hide()
}
return Dropdown({ class: cls('w-full', p.class) }, [
h('label', {
tabindex: '0',
role: 'button',
class: 'input input-bordered flex items-center gap-2 cursor-pointer'
}, [
h('span', { class: 'icon-[lucide--calendar] shrink-0' }),
h('span', {
class: () => `grow text-left truncate ${!displayValue() ? 'opacity-50' : ''}`,
}, () => displayValue() || p.placeholder || (rangeMode() ? 'Seleccionar rango...' : 'Seleccionar fecha...')),
() => displayValue() ? h('button', {
type: 'button',
class: 'btn btn-ghost btn-xs btn-circle -mr-2',
onmousedown: (e) => {
e.preventDefault()
e.stopPropagation()
if (isFunc(p.value)) p.value(null)
else p.onChange?.(null)
displayValue("") // Forzar limpieza visual inmediata
}
}, h('span', { class: 'icon-[lucide--x] opacity-50' })) : null
]),
DropdownContent({ class: 'p-0' },
Calendar({ value: p.value, range: rangeMode(), hour: p.hour, onChange: handleChange })
)
])
};
export const Fileinput = (p) => {
const files = $([])
const drag = $(false)
const error = $(null)
const maxBytes = (p.max || 2) * 1024 * 1024
const process = (fileList) => {
const arr = Array.from(fileList)
error(null)
if (arr.some(f => f.size > maxBytes)) {
error(`Máx ${p.max || 2}MB`)
return
}
const updated = [...files(), ...arr]
files(updated)
if (isFunc(p.onselect)) p.onselect(updated)
else if (isFunc(p.value)) p.value(updated)
}
const remove = (idx) => {
const updated = files().filter((_, i) => i !== idx)
files(updated)
if (isFunc(p.onselect)) p.onselect(updated)
else if (isFunc(p.value)) p.value(updated)
}
return h('div', { class: cls('fieldset w-full p-0', p.class) }, [
h('label', {
class: () => `relative flex items-center justify-between w-full h-12 px-4 border-2 border-dashed rounded-lg cursor-pointer transition-all duration-200 ${drag() ? 'border-primary bg-primary/10' : 'border-base-content/20 bg-base-100 hover:bg-base-200'}`,
ondragover: (e) => { e.preventDefault(); drag(true) },
ondragleave: () => drag(false),
ondrop: (e) => { e.preventDefault(); drag(false); process(e.dataTransfer.files) }
}, [
h('div', { class: 'flex items-center gap-3 w-full' }, [
h('span', { class: 'icon-[lucide--upload]' }),
h('span', { class: 'text-sm opacity-70 truncate grow text-left' }, t("uploadFiles")),
h('span', { class: 'text-[10px] opacity-40 shrink-0' }, `Máx ${p.max || 2}MB`)
]),
h('input', {
type: 'file',
multiple: true,
accept: p.accept || '*',
class: 'hidden',
onchange: (e) => process(e.target.files)
})
]),
() => error() && h('span', { class: 'text-[10px] text-error mt-1 px-1 font-medium' }, error()),
when(() => files().length > 0, () =>
h('ul', { class: 'mt-2 space-y-1' },
each(files, (file, idx) =>
h('li', { class: 'flex items-center justify-between p-1.5 pl-3 text-xs bg-base-200/50 rounded-md border border-base-300' }, [
h('div', { class: 'flex items-center gap-2 truncate' }, [
h('span', { class: 'opacity-50' }, '📄'),
h('span', { class: 'truncate font-medium max-w-[200px]' }, file.name),
h('span', { class: 'text-[9px] opacity-40' }, `(${(file.size / 1024).toFixed(0)} KB)`)
]),
h('button', {
type: 'button',
class: 'btn btn-ghost btn-xs btn-circle',
onclick: (e) => { e.preventDefault(); remove(idx) }
}, h('span', { class: 'icon-[lucide--x]' }))
])
)
)
)
])
};
export const Input = (p) => {
const { label, icon, float, placeholder, value, left, right, rule, hint, content, ...rest } = p;
const showPassword = $(false);
const isPassword = p.type === 'password';
const pattern = rule ?? null;
const inputType = () => isPassword ? (val(showPassword) ? 'text' : 'password') : (p.type || 'search');
return h("label", { class: float ? 'floating-label' : '' }, [
float ? h("span", {}, label) : null,
h("label", { pattern: pattern, class: () => cls('input validator', p.class) },
[
label && !float ? h('span', { class: 'label' }, label) : null,
left ?? null,
h('input', { ...rest, type: inputType, class: 'grow', pattern: pattern, placeholder: placeholder || label || ' ', value: value }),
right ?? null,
isPassword ? Swap({ class: 'ml-2' }, [
SwapToggle({ value: showPassword, class: "swap-rotate" }),
SwapOn({}, Icon({},'icon-[lucide--eye]')),
SwapOff({}, Icon({},'icon-[lucide--eye-off]')),
]) : null
]),
hint ? h('div', { class: "validator-hint" }, hint) : null,
]);
};
export const Menu = (p) => {
if (p.children !== undefined) return h('ul', { class: cls('menu', p.class), ...p }, p.children)
const { items } = p
const render = (item) => item.children
? h('li', {}, h('details', { open: item.open || undefined }, [
h('summary', {}, getBy(item)),
h('ul', {}, each(() => val(item.children) || [], render))
]))
: h('li', {}, h('a', {
href: item.href,
onclick: item.onclick ? (e) => { if (!item.href) e.preventDefault(); item.onclick(e) } : null
}, getBy(item)))
return h('ul', { class: cls('menu', p.class) },
each(() => val(items) || [], render)
)
};
export const Modal = (p, c) => {
let dialogRef = null;
watch(() => { const isOpen = val(p.open); if (!dialogRef) return; isOpen ? dialogRef.showModal() : dialogRef.close(); });
const close = () => isFunc(p.open) && p.open(false);
return h("dialog", { ...p, ref: el => dialogRef = el, class: cls('modal', p.class), onclose: close, oncancel: close }, [
h("div", { class: "modal-box" }, [
p.title && h("h3", { class: "text-lg font-bold" }, p.title),
c,
h("div", { class: "modal-action" }, [
p.actions || Button({ class: 'btn', onclick: close }, 'Cerrar')
])
]),
h("form", { method: "dialog", class: "modal-backdrop" }, [
h("button", {}, "close")
])
]);
};
export const Select = (p, c) => {
if (c !== undefined) return h('select', { class: cls('select', p.class), ...p }, c)
const { label, float, placeholder, placeholderDisabled = true, value, left, right, hint, items, keyFn, ...rest } = p
const opts = () => {
const raw = val(items) || []
const ph = placeholder ? [{ disabled: placeholderDisabled, label: placeholder, value: '' }] : []
return [...ph, ...raw]
}
return h('label', { class: float ? 'floating-label' : '' }, [
float ? h('span', {}, label) : null,
h('label', { class: cls('select', rest.class) }, [
(!float && label) ? h('span', { class: 'label' }, label) : null,
left ?? null,
h('select', {
value: () => val(value),
onchange: (e) => isFunc(value) ? value(e.target.value) : rest.onchange?.(e)
},
each(opts, (item) => {
const val = getBy(item, item.value !== undefined ? 'value' : undefined)
const lab = getBy(item, 'label')
return h('option', { value: val, disabled: item.disabled || undefined }, lab)
})
),
right ?? null
]),
hint ? h('div', { class: 'validator-hint' }, hint) : null
])
};
export const Stat = (p) => h('div', { ...p, class: cls('stat', p.class) }, [
p.title ? h('div', { class: 'stat-title' }, p.title) : null,
p.value ? h('div', { class: 'stat-value' }, p.value) : null,
p.desc ? h('div', { class: 'stat-desc' }, p.desc) : null
]);
export const TableItems = ({ items, columns = [], header = true }) => {
const head = header !== false && columns.some(c => c.label) ? h('thead', {}, h('tr', {}, columns.map(c => h('th', { class: c.class }, c.label)))) : null
const body = h('tbody', {}, () => {
const list = val(items) || []
return list.map((it, idx) => h('tr', {}, columns.map(c => { const v = c.render ? c.render(it, idx) : it[c.key]; return h('td', { class: c.class }, v) })))
})
return [head, body].filter(Boolean)
};
export const Tabs = (p, c) => {
if (!p.items) {
const { class: className, ...rest } = p
return h('div', { ...rest, class: cls('tabs', className) }, c)
}
const { items, activeIndex, onClose, class: className, ...rest } = p
const get = x => (isFunc(x) ? x() : x)
const closeH = onClose || (isFunc(items) ? (idx, item) => {
const arr = val(items)
const newArr = arr.filter((_, i) => i !== idx)
items(newArr)
if (activeIndex() >= newArr.length) activeIndex(Math.max(0, newArr.length - 1))
} : null)
return h('div', { ...rest, class: cls('tabs', className) }, () => {
const list = val(items) || []
return list.flatMap((it, idx) => {
const isActive = () => activeIndex() === idx
const button = h('button', {
class: () => `tab ${isActive() ? 'tab-active' : ''} ${it.class || ''}`,
onclick: (e) => { e.preventDefault(); activeIndex(idx); it.onclick?.(e) }
}, [
getBy(it),
it.closable ? h('span', {
class: 'ml-1 inline-flex items-center justify-center w-4 h-4 rounded-full hover:bg-base-300 text-base-content/60 hover:text-base-content cursor-pointer',
onclick: (e) => { e.stopPropagation(); closeH?.(idx, it) }
}, h('span', { class: 'icon-[lucide--x] w-3 h-3' })) : null
])
const contentDiv = h('div', {
class: 'tab-content bg-base-100 border-base-300 p-6',
style: () => `display: ${isActive() ? 'block' : 'none'};`
}, isFunc(it.content) ? it.content() : it.content)
return [button, contentDiv]
})
})
};
export const Toast = (message, type = "alert-success", duration = 3500) => {
let container = document.getElementById("sigpro-toast-container");
if (!container) {
container = h("div", { id: "sigpro-toast-container", class: "fixed top-0 right-0 z-[9999] p-4 flex flex-col items-end gap-2 pointer-events-none" });
document.body.appendChild(container);
}
const host = h("div", { style: "display: contents" });
container.appendChild(host);
let closeFn, timer, enterTimer;
const ToastComponent = () => {
const visible = $(false);
const leaving = $(false);
closeFn = () => {
if (leaving()) return;
clearTimeout(timer);
clearTimeout(enterTimer);
leaving(true);
setTimeout(() => { instance.destroy(); host.remove(); if (!container.hasChildNodes()) container.remove(); }, 300);
};
enterTimer = setTimeout(() => visible(true), 0);
const content = typeof message === 'function' ? val(message) : message;
const msgNode = typeof content === 'string' ? h("span", {}, content) : content;
return h("div", {
class: () => {
const base = `alert alert-soft ${type} shadow-lg transition-all duration-300 inline-flex w-auto whitespace-nowrap pointer-events-auto`;
if (leaving()) return `${base} translate-x-full opacity-0`;
if (visible()) return `${base} translate-x-0 opacity-100`;
return `${base} translate-x-10 opacity-0`;
}
}, [
msgNode,
h("button", {
class: "btn btn-xs btn-circle btn-ghost",
onclick: closeFn
}, h("span", { class: "icon-[lucide--x]" }))
]);
};
const instance = mount(ToastComponent, host);
if (duration > 0) timer = setTimeout(closeFn, duration);
return closeFn;
};

433
client/sigpro.js Normal file
View File

@@ -0,0 +1,433 @@
const isFunc = f => typeof f === "function"
const isObj = o => o && typeof o === "object"
const isArr = Array.isArray
const doc = typeof document !== "undefined" ? document : null
const ensureNode = n => n?._isRuntime ? n.container : (n instanceof Node ? n : doc.createTextNode(n == null ? "" : String(n)))
let activeEffect = null
let activeOwner = null
let isFlushing = false
let batchDepth = 0
const effectQueue = new Set()
const MOUNTED_NODES = new WeakMap()
const SVG_NS = "http://www.w3.org/2000/svg"
const XLINK_NS = "http://www.w3.org/1999/xlink"
const SVG_TAGS = new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(","))
const dispose = eff => {
if (!eff || eff._disposed) return
eff._disposed = true
const stack = [eff]
while (stack.length) {
const e = stack.pop()
if (e._cleanups) {
e._cleanups.forEach(fn => fn())
e._cleanups.clear()
}
if (e._children) {
e._children.forEach(child => stack.push(child))
e._children.clear()
}
if (e._deps) {
e._deps.forEach(depSet => depSet.delete(e))
e._deps.clear()
}
}
}
const onUnmount = fn => {
if (activeOwner) (activeOwner._cleanups ||= new Set()).add(fn)
}
const untrack = fn => {
const p = activeEffect
activeEffect = null
try { return fn() } finally { activeEffect = p }
}
const createEffect = (fn, isComputed = false) => {
const effect = () => {
if (effect._disposed) return
if (effect._deps) effect._deps.forEach(s => s.delete(effect))
if (effect._cleanups) {
effect._cleanups.forEach(c => c())
effect._cleanups.clear()
}
const prevEffect = activeEffect
const prevOwner = activeOwner
activeEffect = activeOwner = effect
try {
return effect._result = fn()
} catch (e) {
console.error("[SigPro]", e)
} finally {
activeEffect = prevEffect
activeOwner = prevOwner
}
}
effect._deps = effect._cleanups = effect._children = null
effect._disposed = false
effect._isComputed = isComputed
effect._depth = activeEffect ? activeEffect._depth + 1 : 0
effect._mounts = []
effect._parent = activeOwner
if (activeOwner) (activeOwner._children ||= new Set()).add(effect)
return effect
}
const flush = () => {
if (isFlushing) return
isFlushing = true
const sorted = Array.from(effectQueue).sort((a, b) => a._depth - b._depth)
effectQueue.clear()
for (const e of sorted) if (!e._disposed) e()
isFlushing = false
}
const batch = fn => {
batchDepth++
try {
return fn()
} finally {
batchDepth--
if (batchDepth === 0 && effectQueue.size > 0 && !isFlushing) flush()
}
}
const trackUpdate = (subs, trigger = false) => {
if (!trigger && activeEffect && !activeEffect._disposed) {
subs.add(activeEffect);
(activeEffect._deps ||= new Set()).add(subs)
} else if (trigger && subs.size > 0) {
let hasQueue = false
for (const e of subs) {
if (e === activeEffect || e._disposed) continue
if (e._isComputed) {
e._dirty = true
if (e._subs) trackUpdate(e._subs, true)
} else {
effectQueue.add(e)
hasQueue = true
}
}
if (hasQueue && !isFlushing && batchDepth === 0) queueMicrotask(flush)
}
}
const $ = (val, key = null) => {
const subs = new Set()
if (isFunc(val)) {
let cache
const computed = () => {
if (computed._dirty) {
const prev = activeEffect
activeEffect = computed
try {
const next = val()
if (!Object.is(cache, next)) {
cache = next
trackUpdate(subs, true)
}
} finally {
activeEffect = prev
}
computed._dirty = false
}
trackUpdate(subs)
return cache
}
computed._isComputed = true
computed._subs = subs
computed._dirty = true
computed._deps = null
computed._disposed = false
return computed
}
if (key) try { val = JSON.parse(localStorage.getItem(key)) ?? val } catch (e) { }
return (...args) => {
if (args.length) {
const next = isFunc(args[0]) ? args[0](val) : args[0]
if (!Object.is(val, next)) {
val = next
if (key) localStorage.setItem(key, JSON.stringify(val))
trackUpdate(subs, true)
}
}
trackUpdate(subs)
return val
}
}
const watch = (sources, cb) => {
if (cb === undefined) {
const effect = createEffect(sources)
effect()
return () => dispose(effect)
}
const effect = createEffect(() => {
const vals = isArr(sources) ? sources.map(s => s()) : sources()
untrack(() => cb(vals))
})
effect()
return () => dispose(effect)
}
const cleanupNode = (node) => {
if (!node) return;
if (node._cleanups) {
node._cleanups.forEach(fn => fn());
node._cleanups.clear();
}
if (node._ownerEffect) dispose(node._ownerEffect);
if (node.childNodes) node.childNodes.forEach(n => cleanupNode(n));
};
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
var DANGEROUS_URI_ATTRS = new Set(["src", "href", "formaction", "action", "background", "code", "archive"]);
var isDangerousAttr = (key) => DANGEROUS_URI_ATTRS.has(key) || key.startsWith("on");
const validateAttr = (key, val) => {
if (val == null || val === false) return null
if (isDangerousAttr(key)) {
const sVal = String(val)
if (DANGEROUS_PROTOCOL.test(sVal)) return '#'
}
return val
}
const h = (tag, props = {}, children = []) => {
if (props instanceof Node || isArr(props) || !isObj(props)) {
children = props
props = {}
}
if (isFunc(tag)) {
const effect = createEffect(() => {
const result = tag(props, {
children,
emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args)
})
effect._result = result
return result
})
effect()
const result = effect._result
if (result == null) return null
const node = (result instanceof Node || (isArr(result) && result.every(n => n instanceof Node)))
? result
: doc.createTextNode(String(result))
const attach = n => {
if (isObj(n) && !n._isRuntime) {
n._mounts = effect._mounts || []
n._cleanups = effect._cleanups || new Set()
n._ownerEffect = effect
}
}
isArr(node) ? node.forEach(attach) : attach(node)
return node
}
const isSVG = SVG_TAGS.has(tag)
const el = isSVG ? doc.createElementNS(SVG_NS, tag) : doc.createElement(tag)
el._cleanups = new Set()
for (const k of Object.keys(props)) {
let v = props[k]
if (k === "ref") {
isFunc(v) ? v(el) : (v.current = el)
continue
}
if (isSVG && k.startsWith("xlink:")) {
const cleanVal = validateAttr(k.slice(6), v)
cleanVal == null
? el.removeAttributeNS(XLINK_NS, k.slice(6))
: el.setAttributeNS(XLINK_NS, k.slice(6), cleanVal)
continue
}
if (k.startsWith("on")) {
const ev = k.slice(2).toLowerCase()
el.addEventListener(ev, v)
const off = () => el.removeEventListener(ev, v)
el._cleanups.add(off)
onUnmount(off)
} else if (isFunc(v)) {
const effect = createEffect(() => {
const val = validateAttr(k, v())
if (k === "class") el.className = val || ""
else if (val == null) el.removeAttribute(k)
else if (k === "style" && typeof val === "string") el.setAttribute("style", val)
else if (k in el && !isSVG) el[k] = val
else el.setAttribute(k, val === true ? "" : val)
})
effect()
el._cleanups.add(() => dispose(effect))
onUnmount(() => dispose(effect))
if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === "value" || k === "checked")) {
const evType = k === "checked" ? "change" : "input"
el.addEventListener(evType, ev => v(ev.target[k]))
}
} else {
const val = validateAttr(k, v)
if (val != null) {
if (k === "style" && typeof val === "string") el.setAttribute("style", val)
else if (k in el && !isSVG) el[k] = val
else el.setAttribute(k, val === true ? "" : val)
}
}
}
const append = c => {
if (isArr(c)) return c.forEach(append)
if (isFunc(c)) {
const anchor = doc.createTextNode("")
el.appendChild(anchor)
let currentNodes = []
const effect = createEffect(() => {
const res = c()
const next = (isArr(res) ? res : [res]).map(ensureNode)
currentNodes.forEach(n => {
if (n._isRuntime) n.destroy()
else cleanupNode(n)
if (n.parentNode) n.remove()
})
let ref = anchor
for (let i = next.length - 1; i >= 0; i--) {
const node = next[i]
if (node.parentNode !== ref.parentNode) ref.parentNode?.insertBefore(node, ref)
if (node._mounts) node._mounts.forEach(fn => fn())
ref = node
}
currentNodes = next
})
effect()
el._cleanups.add(() => dispose(effect))
onUnmount(() => dispose(effect))
} else {
const node = ensureNode(c)
el.appendChild(node)
if (node._mounts) node._mounts.forEach(fn => fn())
}
}
append(children)
return el
}
const render = renderFn => {
const cleanups = new Set()
const previousOwner = activeOwner
const previousEffect = activeEffect
const container = doc.createElement("div")
container.style.display = "contents"
container.setAttribute("role", "presentation")
activeOwner = { _cleanups: cleanups }
activeEffect = null
const processResult = result => {
if (!result) return
if (result._isRuntime) {
cleanups.add(result.destroy)
container.appendChild(result.container)
} else if (isArr(result)) {
result.forEach(processResult)
} else {
container.appendChild(result instanceof Node ? result : doc.createTextNode(String(result == null ? "" : result)))
}
}
try {
processResult(renderFn({ onCleanup: fn => cleanups.add(fn) }))
} finally {
activeOwner = previousOwner
activeEffect = previousEffect
}
return {
_isRuntime: true,
container,
destroy: () => {
cleanups.forEach(fn => fn())
cleanupNode(container)
container.remove()
}
}
}
const when = (cond, SIP, NOP = null) => {
const anchor = doc.createTextNode("")
const root = h("div", { style: "display:contents" }, [anchor])
let currentView = null
watch(
() => !!(isFunc(cond) ? cond() : cond),
show => {
if (currentView) {
currentView.destroy()
currentView = null
}
const content = show ? SIP : NOP
if (content) {
currentView = render(() => isFunc(content) ? content() : content)
root.insertBefore(currentView.container, anchor)
}
}
)
onUnmount(() => currentView?.destroy())
return root
}
const each = (src, itemFn, keyField) => {
const anchor = doc.createTextNode("")
const root = h("div", { style: "display:contents" }, [anchor])
let cache = new Map()
watch(() => (isFunc(src) ? src() : src) || [], items => {
const nextCache = new Map()
const nextOrder = []
const newItems = items || []
for (let i = 0; i < newItems.length; i++) {
const item = newItems[i]
const key = keyField ? (item?.[keyField] ?? i) : (item?.id ?? i)
let view = cache.get(key)
if (!view) view = render(() => itemFn(item, i))
else cache.delete(key)
nextCache.set(key, view)
nextOrder.push(view)
}
cache.forEach(view => view.destroy())
let lastRef = anchor
for (let i = nextOrder.length - 1; i >= 0; i--) {
const view = nextOrder[i]
const node = view.container
if (node.nextSibling !== lastRef) root.insertBefore(node, lastRef)
lastRef = node
}
cache = nextCache
})
return root
}
const Fragment = (props) => props.children;
const mount = (comp, target) => {
const t = typeof target === "string" ? doc.querySelector(target) : target
if (!t) return
if (MOUNTED_NODES.has(t)) MOUNTED_NODES.get(t).destroy()
const inst = render(isFunc(comp) ? comp : () => comp)
t.replaceChildren(inst.container)
MOUNTED_NODES.set(t, inst)
return inst
}
if (typeof window !== "undefined") {
"a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video"
.split(" ")
.forEach(tag => { window[tag] = (props, children) => h(tag, props, children) })
}
export { $, watch, batch, h, Fragment, render, mount, when, each, onUnmount, isArr, isFunc, isObj }

View File

@@ -134,5 +134,5 @@ export const actions = {
: null : null
}); });
return { totalProcesado: total }; return { totalProcesado: total };
} },
}; };