diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index 5d47c21..0000000
--- a/.editorconfig
+++ /dev/null
@@ -1,12 +0,0 @@
-# EditorConfig is awesome: https://EditorConfig.org
-
-# top-most EditorConfig file
-root = true
-
-[*]
-indent_style = space
-indent_size = 2
-end_of_line = lf
-charset = utf-8
-trim_trailing_whitespace = true
-insert_final_newline = true
diff --git a/.gitignore b/.gitignore
index 542369d..9dd999f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -188,3 +188,9 @@ Icon
Network Trash Folder
Temporary Items
.apdisk
+
+# dependencies
+node_modules/
+
+# dist
+web/
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 0000000..5ee7abd
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1 @@
+pnpm exec lint-staged
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..8da9e7e
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,12 @@
+{
+ "printWidth": 80,
+ "tabWidth": 2,
+ "useTabs": false,
+ "singleQuote": true,
+ "trailingComma": "all",
+ "endOfLine": "lf",
+ "semi": false,
+ "plugins": [
+ "prettier-plugin-organize-imports"
+ ]
+}
\ No newline at end of file
diff --git a/.prettierrc.json b/.prettierrc.json
deleted file mode 100644
index a253ce4..0000000
--- a/.prettierrc.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "printWidth": 80,
- "tabWidth": 2,
- "useTabs": false,
- "singleQuote": true,
- "trailingComma": "all"
-}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index f2c3f14..66ffc70 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,20 +1,23 @@
{
- "cSpell.words": [
- "apng",
- "Civitai",
- "ckpt",
- "comfyui",
- "FYUIKMNVB",
- "gguf",
- "gligen",
- "jfif",
- "locon",
- "loras",
- "noimage",
- "onnx",
- "rfilename",
- "unet",
- "upscaler"
- ],
- "editor.defaultFormatter": "esbenp.prettier-vscode"
+ "cSpell.words": [
+ "apng",
+ "Civitai",
+ "ckpt",
+ "comfyui",
+ "FYUIKMNVB",
+ "gguf",
+ "gligen",
+ "jfif",
+ "locon",
+ "loras",
+ "noimage",
+ "onnx",
+ "rfilename",
+ "unet",
+ "upscaler"
+ ],
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "files.associations": {
+ "*.css": "tailwindcss"
+ }
}
\ No newline at end of file
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..a9322a5
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,32 @@
+import globals from 'globals'
+import pluginJs from '@eslint/js'
+import tsEslint from 'typescript-eslint'
+import pluginVue from 'eslint-plugin-vue'
+
+export default [
+ {
+ files: ['src/**/*.{js,mjs,cjs,ts,vue}'],
+ },
+ {
+ ignores: [
+ 'src/scripts/*',
+ 'src/extensions/core/*',
+ 'src/types/vue-shim.d.ts',
+ ],
+ },
+ { languageOptions: { globals: globals.browser } },
+ pluginJs.configs.recommended,
+ ...tsEslint.configs.recommended,
+ ...pluginVue.configs['flat/essential'],
+ {
+ files: ['src/**/*.vue'],
+ languageOptions: { parserOptions: { parser: tsEslint.parser } },
+ },
+ {
+ rules: {
+ '@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/no-unused-vars': 'off',
+ '@typescript-eslint/prefer-as-const': 'off',
+ },
+ },
+]
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..5500a50
--- /dev/null
+++ b/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+ ComfyUI-Model-Manager
+
+
+
+
+
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..4b3fab8
--- /dev/null
+++ b/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "comfyui-model-manager",
+ "private": true,
+ "version": "1.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite build --watch",
+ "build": "vite build",
+ "prepare": "husky"
+ },
+ "devDependencies": {
+ "@types/node": "^22.5.5",
+ "@vitejs/plugin-vue": "^5.1.4",
+ "autoprefixer": "^10.4.20",
+ "eslint": "^9.10.0",
+ "eslint-plugin-vue": "^9.28.0",
+ "husky": "^9.1.6",
+ "less": "^4.2.0",
+ "lint-staged": "^15.2.10",
+ "postcss": "^8.4.47",
+ "prettier": "^3.3.3",
+ "prettier-plugin-organize-imports": "^4.1.0",
+ "tailwindcss": "^3.4.12",
+ "typescript": "^5.6.2",
+ "typescript-eslint": "^8.6.0",
+ "vite": "^5.4.6"
+ },
+ "dependencies": {
+ "primevue": "^4.0.7",
+ "vue": "^3.4.31",
+ "vue-i18n": "^9.13.1"
+ },
+ "lint-staged": {
+ "./**/*.{js,ts,tsx,vue}": [
+ "prettier --write",
+ "git add"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..f75ee96
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,2953 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ primevue:
+ specifier: ^4.0.7
+ version: 4.0.7(vue@3.5.6(typescript@5.6.2))
+ vue:
+ specifier: ^3.4.31
+ version: 3.5.6(typescript@5.6.2)
+ vue-i18n:
+ specifier: ^9.13.1
+ version: 9.14.0(vue@3.5.6(typescript@5.6.2))
+ devDependencies:
+ '@types/node':
+ specifier: ^22.5.5
+ version: 22.5.5
+ '@vitejs/plugin-vue':
+ specifier: ^5.1.4
+ version: 5.1.4(vite@5.4.6(@types/node@22.5.5)(less@4.2.0))(vue@3.5.6(typescript@5.6.2))
+ autoprefixer:
+ specifier: ^10.4.20
+ version: 10.4.20(postcss@8.4.47)
+ eslint:
+ specifier: ^9.10.0
+ version: 9.10.0(jiti@1.21.6)
+ eslint-plugin-vue:
+ specifier: ^9.28.0
+ version: 9.28.0(eslint@9.10.0(jiti@1.21.6))
+ husky:
+ specifier: ^9.1.6
+ version: 9.1.6
+ less:
+ specifier: ^4.2.0
+ version: 4.2.0
+ lint-staged:
+ specifier: ^15.2.10
+ version: 15.2.10
+ postcss:
+ specifier: ^8.4.47
+ version: 8.4.47
+ prettier:
+ specifier: ^3.3.3
+ version: 3.3.3
+ prettier-plugin-organize-imports:
+ specifier: ^4.1.0
+ version: 4.1.0(prettier@3.3.3)(typescript@5.6.2)
+ tailwindcss:
+ specifier: ^3.4.12
+ version: 3.4.12
+ typescript:
+ specifier: ^5.6.2
+ version: 5.6.2
+ typescript-eslint:
+ specifier: ^8.6.0
+ version: 8.6.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)
+ vite:
+ specifier: ^5.4.6
+ version: 5.4.6(@types/node@22.5.5)(less@4.2.0)
+
+packages:
+
+ '@alloc/quick-lru@5.2.0':
+ resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+ engines: {node: '>=10'}
+
+ '@babel/helper-string-parser@7.24.8':
+ resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.24.7':
+ resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.25.6':
+ resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/types@7.25.6':
+ resolution: {integrity: sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==}
+ engines: {node: '>=6.9.0'}
+
+ '@esbuild/aix-ppc64@0.21.5':
+ resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.21.5':
+ resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.21.5':
+ resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.21.5':
+ resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.21.5':
+ resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.21.5':
+ resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.21.5':
+ resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.21.5':
+ resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.21.5':
+ resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.21.5':
+ resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.21.5':
+ resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.21.5':
+ resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
+ engines: {node: '>=12'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.21.5':
+ resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.21.5':
+ resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.21.5':
+ resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.21.5':
+ resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.21.5':
+ resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-x64@0.21.5':
+ resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-x64@0.21.5':
+ resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/sunos-x64@0.21.5':
+ resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.21.5':
+ resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.21.5':
+ resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.21.5':
+ resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+
+ '@eslint-community/eslint-utils@4.4.0':
+ resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.11.1':
+ resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/config-array@0.18.0':
+ resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/eslintrc@3.1.0':
+ resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/js@9.10.0':
+ resolution: {integrity: sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/object-schema@2.1.4':
+ resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/plugin-kit@0.1.0':
+ resolution: {integrity: sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/retry@0.3.0':
+ resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==}
+ engines: {node: '>=18.18'}
+
+ '@intlify/core-base@9.14.0':
+ resolution: {integrity: sha512-zJn0imh9HIsZZUtt9v8T16PeVstPv6bP2YzlrYJwoF8F30gs4brZBwW2KK6EI5WYKFi3NeqX6+UU4gniz5TkGg==}
+ engines: {node: '>= 16'}
+
+ '@intlify/message-compiler@9.14.0':
+ resolution: {integrity: sha512-sXNsoMI0YsipSXW8SR75drmVK56tnJHoYbPXUv2Cf9lz6FzvwsosFm6JtC1oQZI/kU+n7qx0qRrEWkeYFTgETA==}
+ engines: {node: '>= 16'}
+
+ '@intlify/shared@9.14.0':
+ resolution: {integrity: sha512-r+N8KRQL7LgN1TMTs1A2svfuAU0J94Wu9wWdJVJqYsoMMLIeJxrPjazihfHpmJqfgZq0ah3Y9Q4pgWV2O90Fyg==}
+ engines: {node: '>= 16'}
+
+ '@isaacs/cliui@8.0.2':
+ resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+ engines: {node: '>=12'}
+
+ '@jridgewell/gen-mapping@0.3.5':
+ resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/set-array@1.2.1':
+ resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.0':
+ resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@pkgjs/parseargs@0.11.0':
+ resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+ engines: {node: '>=14'}
+
+ '@primeuix/styled@0.0.5':
+ resolution: {integrity: sha512-pVoGn/uPkVm/DyF3TR3EmH/pL/dP4nR42FcYbVduFq9VfO3KVeOEqvcCULHXos66RZO9MCbCFUoLy6ctf9GUGQ==}
+ engines: {node: '>=12.11.0'}
+
+ '@primeuix/utils@0.0.5':
+ resolution: {integrity: sha512-ntUiUgtRtkF8KuaxHffzhYxQxoXk6LAPHm7CVlFjdqS8Rx8xRkLkZVyo84E+pO2hcNFkOGVP/GxHhQ2s94O8zA==}
+ engines: {node: '>=12.11.0'}
+
+ '@primevue/core@4.0.7':
+ resolution: {integrity: sha512-SvWiNBEeR6hm4wjnze+rITUjHMFLwIzpRFlq+GqmJyZmjJy4h8UUksi0EoyqAWCAwKgmwlxY6XNqGJmMVyOguQ==}
+ engines: {node: '>=12.11.0'}
+ peerDependencies:
+ vue: ^3.0.0
+
+ '@primevue/icons@4.0.7':
+ resolution: {integrity: sha512-tj4dfRdV5iN6O0mbkpjhMsGlT3wZTqOPL779ndY5gKuCwN5zcFmKmABWVQmr/ClRivnMkw6Yr1x6gRTV/N0ydg==}
+ engines: {node: '>=12.11.0'}
+
+ '@rollup/rollup-android-arm-eabi@4.22.0':
+ resolution: {integrity: sha512-/IZQvg6ZR0tAkEi4tdXOraQoWeJy9gbQ/cx4I7k9dJaCk9qrXEcdouxRVz5kZXt5C2bQ9pILoAA+KB4C/d3pfw==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.22.0':
+ resolution: {integrity: sha512-ETHi4bxrYnvOtXeM7d4V4kZWixib2jddFacJjsOjwbgYSRsyXYtZHC4ht134OsslPIcnkqT+TKV4eU8rNBKyyQ==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.22.0':
+ resolution: {integrity: sha512-ZWgARzhSKE+gVUX7QWaECoRQsPwaD8ZR0Oxb3aUpzdErTvlEadfQpORPXkKSdKbFci9v8MJfkTtoEHnnW9Ulng==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.22.0':
+ resolution: {integrity: sha512-h0ZAtOfHyio8Az6cwIGS+nHUfRMWBDO5jXB8PQCARVF6Na/G6XS2SFxDl8Oem+S5ZsHQgtsI7RT4JQnI1qrlaw==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.22.0':
+ resolution: {integrity: sha512-9pxQJSPwFsVi0ttOmqLY4JJ9pg9t1gKhK0JDbV1yUEETSx55fdyCjt39eBQ54OQCzAF0nVGO6LfEH1KnCPvelA==}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.22.0':
+ resolution: {integrity: sha512-YJ5Ku5BmNJZb58A4qSEo3JlIG4d3G2lWyBi13ABlXzO41SsdnUKi3HQHe83VpwBVG4jHFTW65jOQb8qyoR+qzg==}
+ cpu: [arm]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-arm64-gnu@4.22.0':
+ resolution: {integrity: sha512-U4G4u7f+QCqHlVg1Nlx+qapZy+QoG+NV6ux+upo/T7arNGwKvKP2kmGM4W5QTbdewWFgudQxi3kDNST9GT1/mg==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-arm64-musl@4.22.0':
+ resolution: {integrity: sha512-aQpNlKmx3amwkA3a5J6nlXSahE1ijl0L9KuIjVOUhfOh7uw2S4piR3mtpxpRtbnK809SBtyPsM9q15CPTsY7HQ==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.22.0':
+ resolution: {integrity: sha512-9fx6Zj/7vve/Fp4iexUFRKb5+RjLCff6YTRQl4CoDhdMfDoobWmhAxQWV3NfShMzQk1Q/iCnageFyGfqnsmeqQ==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.22.0':
+ resolution: {integrity: sha512-VWQiCcN7zBgZYLjndIEh5tamtnKg5TGxyZPWcN9zBtXBwfcGSZ5cHSdQZfQH/GB4uRxk0D3VYbOEe/chJhPGLQ==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-s390x-gnu@4.22.0':
+ resolution: {integrity: sha512-EHmPnPWvyYqncObwqrosb/CpH3GOjE76vWVs0g4hWsDRUVhg61hBmlVg5TPXqF+g+PvIbqkC7i3h8wbn4Gp2Fg==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-x64-gnu@4.22.0':
+ resolution: {integrity: sha512-tsSWy3YQzmpjDKnQ1Vcpy3p9Z+kMFbSIesCdMNgLizDWFhrLZIoN21JSq01g+MZMDFF+Y1+4zxgrlqPjid5ohg==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-x64-musl@4.22.0':
+ resolution: {integrity: sha512-anr1Y11uPOQrpuU8XOikY5lH4Qu94oS6j0xrulHk3NkLDq19MlX8Ng/pVipjxBJ9a2l3+F39REZYyWQFkZ4/fw==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-win32-arm64-msvc@4.22.0':
+ resolution: {integrity: sha512-7LB+Bh+Ut7cfmO0m244/asvtIGQr5pG5Rvjz/l1Rnz1kDzM02pSX9jPaS0p+90H5I1x4d1FkCew+B7MOnoatNw==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.22.0':
+ resolution: {integrity: sha512-+3qZ4rer7t/QsC5JwMpcvCVPRcJt1cJrYS/TMJZzXIJbxWFQEVhrIc26IhB+5Z9fT9umfVc+Es2mOZgl+7jdJQ==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.22.0':
+ resolution: {integrity: sha512-YdicNOSJONVx/vuPkgPTyRoAPx3GbknBZRCOUkK84FJ/YTfs/F0vl/YsMscrB6Y177d+yDRcj+JWMPMCgshwrA==}
+ cpu: [x64]
+ os: [win32]
+
+ '@types/estree@1.0.5':
+ resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
+
+ '@types/node@22.5.5':
+ resolution: {integrity: sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==}
+
+ '@typescript-eslint/eslint-plugin@8.6.0':
+ resolution: {integrity: sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@typescript-eslint/parser@8.6.0':
+ resolution: {integrity: sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@typescript-eslint/scope-manager@8.6.0':
+ resolution: {integrity: sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/type-utils@8.6.0':
+ resolution: {integrity: sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@typescript-eslint/types@8.6.0':
+ resolution: {integrity: sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.6.0':
+ resolution: {integrity: sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@typescript-eslint/utils@8.6.0':
+ resolution: {integrity: sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+
+ '@typescript-eslint/visitor-keys@8.6.0':
+ resolution: {integrity: sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@vitejs/plugin-vue@5.1.4':
+ resolution: {integrity: sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ peerDependencies:
+ vite: ^5.0.0
+ vue: ^3.2.25
+
+ '@vue/compiler-core@3.5.6':
+ resolution: {integrity: sha512-r+gNu6K4lrvaQLQGmf+1gc41p3FO2OUJyWmNqaIITaJU6YFiV5PtQSFZt8jfztYyARwqhoCayjprC7KMvT3nRA==}
+
+ '@vue/compiler-dom@3.5.6':
+ resolution: {integrity: sha512-xRXqxDrIqK8v8sSScpistyYH0qYqxakpsIvqMD2e5sV/PXQ1mTwtXp4k42yHK06KXxKSmitop9e45Ui/3BrTEw==}
+
+ '@vue/compiler-sfc@3.5.6':
+ resolution: {integrity: sha512-pjWJ8Kj9TDHlbF5LywjVso+BIxCY5wVOLhkEXRhuCHDxPFIeX1zaFefKs8RYoHvkSMqRWt93a0f2gNJVJixHwg==}
+
+ '@vue/compiler-ssr@3.5.6':
+ resolution: {integrity: sha512-VpWbaZrEOCqnmqjE83xdwegtr5qO/2OPUC6veWgvNqTJ3bYysz6vY3VqMuOijubuUYPRpG3OOKIh9TD0Stxb9A==}
+
+ '@vue/devtools-api@6.6.4':
+ resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
+
+ '@vue/reactivity@3.5.6':
+ resolution: {integrity: sha512-shZ+KtBoHna5GyUxWfoFVBCVd7k56m6lGhk5e+J9AKjheHF6yob5eukssHRI+rzvHBiU1sWs/1ZhNbLExc5oYQ==}
+
+ '@vue/runtime-core@3.5.6':
+ resolution: {integrity: sha512-FpFULR6+c2lI+m1fIGONLDqPQO34jxV8g6A4wBOgne8eSRHP6PQL27+kWFIx5wNhhjkO7B4rgtsHAmWv7qKvbg==}
+
+ '@vue/runtime-dom@3.5.6':
+ resolution: {integrity: sha512-SDPseWre45G38ENH2zXRAHL1dw/rr5qp91lS4lt/nHvMr0MhsbCbihGAWLXNB/6VfFOJe2O+RBRkXU+CJF7/sw==}
+
+ '@vue/server-renderer@3.5.6':
+ resolution: {integrity: sha512-zivnxQnOnwEXVaT9CstJ64rZFXMS5ZkKxCjDQKiMSvUhXRzFLWZVbaBiNF4HGDqGNNsTgmjcCSmU6TB/0OOxLA==}
+ peerDependencies:
+ vue: 3.5.6
+
+ '@vue/shared@3.5.6':
+ resolution: {integrity: sha512-eidH0HInnL39z6wAt6SFIwBrvGOpDWsDxlw3rCgo1B+CQ1781WzQUSU3YjxgdkcJo9Q8S6LmXTkvI+cLHGkQfA==}
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.12.1:
+ resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ ajv@6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+ ansi-escapes@7.0.0:
+ resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==}
+ engines: {node: '>=18'}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@6.1.0:
+ resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
+ engines: {node: '>=12'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansi-styles@6.2.1:
+ resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
+ engines: {node: '>=12'}
+
+ any-promise@1.3.0:
+ resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
+
+ anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+
+ arg@5.0.2:
+ resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ autoprefixer@10.4.20:
+ resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==}
+ engines: {node: ^10 || ^12 || >=14}
+ hasBin: true
+ peerDependencies:
+ postcss: ^8.1.0
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ binary-extensions@2.3.0:
+ resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+ engines: {node: '>=8'}
+
+ boolbase@1.0.0:
+ resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+ brace-expansion@1.1.11:
+ resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+
+ brace-expansion@2.0.1:
+ resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ browserslist@4.23.3:
+ resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ camelcase-css@2.0.1:
+ resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
+ engines: {node: '>= 6'}
+
+ caniuse-lite@1.0.30001662:
+ resolution: {integrity: sha512-sgMUVwLmGseH8ZIrm1d51UbrhqMCH3jvS7gF/M6byuHOnKyLOBL7W8yz5V02OHwgLGA36o/AFhWzzh4uc5aqTA==}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ chalk@5.3.0:
+ resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
+ engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
+
+ chokidar@3.6.0:
+ resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+ engines: {node: '>= 8.10.0'}
+
+ cli-cursor@5.0.0:
+ resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
+ engines: {node: '>=18'}
+
+ cli-truncate@4.0.0:
+ resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==}
+ engines: {node: '>=18'}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ colorette@2.0.20:
+ resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
+
+ commander@12.1.0:
+ resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
+ engines: {node: '>=18'}
+
+ commander@4.1.1:
+ resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
+ engines: {node: '>= 6'}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ copy-anything@2.0.6:
+ resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==}
+
+ cross-spawn@7.0.3:
+ resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
+ engines: {node: '>= 8'}
+
+ cssesc@3.0.0:
+ resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+ debug@4.3.7:
+ resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ didyoumean@1.2.2:
+ resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
+
+ dlv@1.1.3:
+ resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+
+ eastasianwidth@0.2.0:
+ resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+ electron-to-chromium@1.5.25:
+ resolution: {integrity: sha512-kMb204zvK3PsSlgvvwzI3wBIcAw15tRkYk+NQdsjdDtcQWTp2RABbMQ9rUBy8KNEOM+/E6ep+XC3AykiWZld4g==}
+
+ emoji-regex@10.4.0:
+ resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
+
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+ emoji-regex@9.2.2:
+ resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
+ environment@1.1.0:
+ resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
+ engines: {node: '>=18'}
+
+ errno@0.1.8:
+ resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
+ hasBin: true
+
+ esbuild@0.21.5:
+ resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
+ engines: {node: '>=12'}
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ eslint-plugin-vue@9.28.0:
+ resolution: {integrity: sha512-ShrihdjIhOTxs+MfWun6oJWuk+g/LAhN+CiuOl/jjkG3l0F2AuK5NMTaWqyvBgkFtpYmyks6P4603mLmhNJW8g==}
+ engines: {node: ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
+
+ eslint-scope@7.2.2:
+ resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-scope@8.0.2:
+ resolution: {integrity: sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@4.0.0:
+ resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint@9.10.0:
+ resolution: {integrity: sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ espree@10.1.0:
+ resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ espree@9.6.1:
+ resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ esquery@1.6.0:
+ resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ estree-walker@2.0.2:
+ resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ eventemitter3@5.0.1:
+ resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
+
+ execa@8.0.1:
+ resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
+ engines: {node: '>=16.17'}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-glob@3.3.2:
+ resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
+ engines: {node: '>=8.6.0'}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fastq@1.17.1:
+ resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
+
+ file-entry-cache@8.0.0:
+ resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+ engines: {node: '>=16.0.0'}
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+ engines: {node: '>=16'}
+
+ flatted@3.3.1:
+ resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
+
+ foreground-child@3.3.0:
+ resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
+ engines: {node: '>=14'}
+
+ fraction.js@4.3.7:
+ resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ get-east-asian-width@1.2.0:
+ resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==}
+ engines: {node: '>=18'}
+
+ get-stream@8.0.1:
+ resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
+ engines: {node: '>=16'}
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ glob@10.4.5:
+ resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
+ hasBin: true
+
+ globals@13.24.0:
+ resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
+ engines: {node: '>=8'}
+
+ globals@14.0.0:
+ resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+ engines: {node: '>=18'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ graphemer@1.4.0:
+ resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ human-signals@5.0.0:
+ resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
+ engines: {node: '>=16.17.0'}
+
+ husky@9.1.6:
+ resolution: {integrity: sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ image-size@0.5.5:
+ resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==}
+ engines: {node: '>=0.10.0'}
+ hasBin: true
+
+ import-fresh@3.3.0:
+ resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
+ engines: {node: '>=6'}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ is-binary-path@2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+
+ is-core-module@2.15.1:
+ resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==}
+ engines: {node: '>= 0.4'}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
+ is-fullwidth-code-point@4.0.0:
+ resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
+ engines: {node: '>=12'}
+
+ is-fullwidth-code-point@5.0.0:
+ resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==}
+ engines: {node: '>=18'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-path-inside@3.0.3:
+ resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
+ engines: {node: '>=8'}
+
+ is-stream@3.0.0:
+ resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ is-what@3.14.1:
+ resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ jackspeak@3.4.3:
+ resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+
+ jiti@1.21.6:
+ resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==}
+ hasBin: true
+
+ js-yaml@4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ less@4.2.0:
+ resolution: {integrity: sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ lilconfig@2.1.0:
+ resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
+ engines: {node: '>=10'}
+
+ lilconfig@3.1.2:
+ resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==}
+ engines: {node: '>=14'}
+
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ lint-staged@15.2.10:
+ resolution: {integrity: sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==}
+ engines: {node: '>=18.12.0'}
+ hasBin: true
+
+ listr2@8.2.4:
+ resolution: {integrity: sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==}
+ engines: {node: '>=18.0.0'}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash.merge@4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+ lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+ log-update@6.1.0:
+ resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
+ engines: {node: '>=18'}
+
+ lru-cache@10.4.3:
+ resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+
+ magic-string@0.30.11:
+ resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==}
+
+ make-dir@2.1.0:
+ resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
+ engines: {node: '>=6'}
+
+ merge-stream@2.0.0:
+ resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ mime@1.6.0:
+ resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ mimic-fn@4.0.0:
+ resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
+ engines: {node: '>=12'}
+
+ mimic-function@5.0.1:
+ resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
+ engines: {node: '>=18'}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ mz@2.7.0:
+ resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+
+ nanoid@3.3.7:
+ resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ needle@3.3.1:
+ resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==}
+ engines: {node: '>= 4.4.x'}
+ hasBin: true
+
+ node-releases@2.0.18:
+ resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==}
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ normalize-range@0.1.2:
+ resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
+ engines: {node: '>=0.10.0'}
+
+ npm-run-path@5.3.0:
+ resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ nth-check@2.1.1:
+ resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+ object-assign@4.1.1:
+ resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+ engines: {node: '>=0.10.0'}
+
+ object-hash@3.0.0:
+ resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
+ engines: {node: '>= 6'}
+
+ onetime@6.0.0:
+ resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
+ engines: {node: '>=12'}
+
+ onetime@7.0.0:
+ resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
+ engines: {node: '>=18'}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ package-json-from-dist@1.0.0:
+ resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ parse-node-version@1.0.1:
+ resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==}
+ engines: {node: '>= 0.10'}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-key@4.0.0:
+ resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
+ engines: {node: '>=12'}
+
+ path-parse@1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+ path-scurry@1.11.1:
+ resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
+ engines: {node: '>=16 || 14 >=14.18'}
+
+ picocolors@1.1.0:
+ resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ pidtree@0.6.0:
+ resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
+ engines: {node: '>=0.10'}
+ hasBin: true
+
+ pify@2.3.0:
+ resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+ engines: {node: '>=0.10.0'}
+
+ pify@4.0.1:
+ resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
+ engines: {node: '>=6'}
+
+ pirates@4.0.6:
+ resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
+ engines: {node: '>= 6'}
+
+ postcss-import@15.1.0:
+ resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ postcss: ^8.0.0
+
+ postcss-js@4.0.1:
+ resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
+ engines: {node: ^12 || ^14 || >= 16}
+ peerDependencies:
+ postcss: ^8.4.21
+
+ postcss-load-config@4.0.2:
+ resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
+ engines: {node: '>= 14'}
+ peerDependencies:
+ postcss: '>=8.0.9'
+ ts-node: '>=9.0.0'
+ peerDependenciesMeta:
+ postcss:
+ optional: true
+ ts-node:
+ optional: true
+
+ postcss-nested@6.2.0:
+ resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
+ engines: {node: '>=12.0'}
+ peerDependencies:
+ postcss: ^8.2.14
+
+ postcss-selector-parser@6.1.2:
+ resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
+ engines: {node: '>=4'}
+
+ postcss-value-parser@4.2.0:
+ resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+ postcss@8.4.47:
+ resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ prettier-plugin-organize-imports@4.1.0:
+ resolution: {integrity: sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==}
+ peerDependencies:
+ prettier: '>=2.0'
+ typescript: '>=2.9'
+ vue-tsc: ^2.1.0
+ peerDependenciesMeta:
+ vue-tsc:
+ optional: true
+
+ prettier@3.3.3:
+ resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ primevue@4.0.7:
+ resolution: {integrity: sha512-88qazHqldkqsCxvhjnjO65XMBfJyHQoFW3BQvrJYO6RqPheHB4f7cY61eqtBpJAjnM5x+YKTZiWx/gBuUzqT7Q==}
+ engines: {node: '>=12.11.0'}
+
+ prr@1.0.1:
+ resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ read-cache@1.0.0:
+ resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
+
+ readdirp@3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ resolve@1.22.8:
+ resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
+ hasBin: true
+
+ restore-cursor@5.1.0:
+ resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
+ engines: {node: '>=18'}
+
+ reusify@1.0.4:
+ resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ rfdc@1.4.1:
+ resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+ rollup@4.22.0:
+ resolution: {integrity: sha512-W21MUIFPZ4+O2Je/EU+GP3iz7PH4pVPUXSbEZdatQnxo29+3rsUjgrJmzuAZU24z7yRAnFN6ukxeAhZh/c7hzg==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ sax@1.4.1:
+ resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
+
+ semver@5.7.2:
+ resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
+ hasBin: true
+
+ semver@7.6.3:
+ resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ slice-ansi@5.0.0:
+ resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
+ engines: {node: '>=12'}
+
+ slice-ansi@7.1.0:
+ resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==}
+ engines: {node: '>=18'}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ source-map@0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+
+ string-argv@0.3.2:
+ resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
+ engines: {node: '>=0.6.19'}
+
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ string-width@5.1.2:
+ resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+ engines: {node: '>=12'}
+
+ string-width@7.2.0:
+ resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
+ engines: {node: '>=18'}
+
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-ansi@7.1.0:
+ resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
+ engines: {node: '>=12'}
+
+ strip-final-newline@3.0.0:
+ resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
+ engines: {node: '>=12'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ sucrase@3.35.0:
+ resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ hasBin: true
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ tailwindcss@3.4.12:
+ resolution: {integrity: sha512-Htf/gHj2+soPb9UayUNci/Ja3d8pTmu9ONTfh4QY8r3MATTZOzmv6UYWF7ZwikEIC8okpfqmGqrmDehua8mF8w==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+
+ text-table@0.2.0:
+ resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
+
+ thenify-all@1.6.0:
+ resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
+ engines: {node: '>=0.8'}
+
+ thenify@3.3.1:
+ resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+
+ to-fast-properties@2.0.0:
+ resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
+ engines: {node: '>=4'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ ts-api-utils@1.3.0:
+ resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
+ engines: {node: '>=16'}
+ peerDependencies:
+ typescript: '>=4.2.0'
+
+ ts-interface-checker@0.1.13:
+ resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+
+ tslib@2.7.0:
+ resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ type-fest@0.20.2:
+ resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
+ engines: {node: '>=10'}
+
+ typescript-eslint@8.6.0:
+ resolution: {integrity: sha512-eEhhlxCEpCd4helh3AO1hk0UP2MvbRi9CtIAJTVPQjuSXOOO2jsEacNi4UdcJzZJbeuVg1gMhtZ8UYb+NFYPrA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ typescript@5.6.2:
+ resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ undici-types@6.19.8:
+ resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
+
+ update-browserslist-db@1.1.0:
+ resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ vite@5.4.6:
+ resolution: {integrity: sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || >=20.0.0
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.4.0
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+
+ vue-eslint-parser@9.4.3:
+ resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==}
+ engines: {node: ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '>=6.0.0'
+
+ vue-i18n@9.14.0:
+ resolution: {integrity: sha512-LxmpRuCt2rI8gqU+kxeflRZMQn4D5+4M3oP3PWZdowW/ePJraHqhF7p4CuaME52mUxdw3Mmy2yAUKgfZYgCRjA==}
+ engines: {node: '>= 16'}
+ peerDependencies:
+ vue: ^3.0.0
+
+ vue@3.5.6:
+ resolution: {integrity: sha512-zv+20E2VIYbcJOzJPUWp03NOGFhMmpCKOfSxVTmCYyYFFko48H9tmuQFzYj7tu4qX1AeXlp9DmhIP89/sSxxhw==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+
+ wrap-ansi@8.1.0:
+ resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+ engines: {node: '>=12'}
+
+ wrap-ansi@9.0.0:
+ resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==}
+ engines: {node: '>=18'}
+
+ xml-name-validator@4.0.0:
+ resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
+ engines: {node: '>=12'}
+
+ yaml@2.5.1:
+ resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==}
+ engines: {node: '>= 14'}
+ hasBin: true
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+snapshots:
+
+ '@alloc/quick-lru@5.2.0': {}
+
+ '@babel/helper-string-parser@7.24.8': {}
+
+ '@babel/helper-validator-identifier@7.24.7': {}
+
+ '@babel/parser@7.25.6':
+ dependencies:
+ '@babel/types': 7.25.6
+
+ '@babel/types@7.25.6':
+ dependencies:
+ '@babel/helper-string-parser': 7.24.8
+ '@babel/helper-validator-identifier': 7.24.7
+ to-fast-properties: 2.0.0
+
+ '@esbuild/aix-ppc64@0.21.5':
+ optional: true
+
+ '@esbuild/android-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/android-arm@0.21.5':
+ optional: true
+
+ '@esbuild/android-x64@0.21.5':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/darwin-x64@0.21.5':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-arm@0.21.5':
+ optional: true
+
+ '@esbuild/linux-ia32@0.21.5':
+ optional: true
+
+ '@esbuild/linux-loong64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.21.5':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-s390x@0.21.5':
+ optional: true
+
+ '@esbuild/linux-x64@0.21.5':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/sunos-x64@0.21.5':
+ optional: true
+
+ '@esbuild/win32-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/win32-ia32@0.21.5':
+ optional: true
+
+ '@esbuild/win32-x64@0.21.5':
+ optional: true
+
+ '@eslint-community/eslint-utils@4.4.0(eslint@9.10.0(jiti@1.21.6))':
+ dependencies:
+ eslint: 9.10.0(jiti@1.21.6)
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.11.1': {}
+
+ '@eslint/config-array@0.18.0':
+ dependencies:
+ '@eslint/object-schema': 2.1.4
+ debug: 4.3.7
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/eslintrc@3.1.0':
+ dependencies:
+ ajv: 6.12.6
+ debug: 4.3.7
+ espree: 10.1.0
+ globals: 14.0.0
+ ignore: 5.3.2
+ import-fresh: 3.3.0
+ js-yaml: 4.1.0
+ minimatch: 3.1.2
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/js@9.10.0': {}
+
+ '@eslint/object-schema@2.1.4': {}
+
+ '@eslint/plugin-kit@0.1.0':
+ dependencies:
+ levn: 0.4.1
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.3.0': {}
+
+ '@intlify/core-base@9.14.0':
+ dependencies:
+ '@intlify/message-compiler': 9.14.0
+ '@intlify/shared': 9.14.0
+
+ '@intlify/message-compiler@9.14.0':
+ dependencies:
+ '@intlify/shared': 9.14.0
+ source-map-js: 1.2.1
+
+ '@intlify/shared@9.14.0': {}
+
+ '@isaacs/cliui@8.0.2':
+ dependencies:
+ string-width: 5.1.2
+ string-width-cjs: string-width@4.2.3
+ strip-ansi: 7.1.0
+ strip-ansi-cjs: strip-ansi@6.0.1
+ wrap-ansi: 8.1.0
+ wrap-ansi-cjs: wrap-ansi@7.0.0
+
+ '@jridgewell/gen-mapping@0.3.5':
+ dependencies:
+ '@jridgewell/set-array': 1.2.1
+ '@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/set-array@1.2.1': {}
+
+ '@jridgewell/sourcemap-codec@1.5.0': {}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.17.1
+
+ '@pkgjs/parseargs@0.11.0':
+ optional: true
+
+ '@primeuix/styled@0.0.5':
+ dependencies:
+ '@primeuix/utils': 0.0.5
+
+ '@primeuix/utils@0.0.5': {}
+
+ '@primevue/core@4.0.7(vue@3.5.6(typescript@5.6.2))':
+ dependencies:
+ '@primeuix/styled': 0.0.5
+ '@primeuix/utils': 0.0.5
+ vue: 3.5.6(typescript@5.6.2)
+
+ '@primevue/icons@4.0.7(vue@3.5.6(typescript@5.6.2))':
+ dependencies:
+ '@primeuix/utils': 0.0.5
+ '@primevue/core': 4.0.7(vue@3.5.6(typescript@5.6.2))
+ transitivePeerDependencies:
+ - vue
+
+ '@rollup/rollup-android-arm-eabi@4.22.0':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.22.0':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.22.0':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.22.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.22.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.22.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.22.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.22.0':
+ optional: true
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.22.0':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.22.0':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.22.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.22.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.22.0':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.22.0':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.22.0':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.22.0':
+ optional: true
+
+ '@types/estree@1.0.5': {}
+
+ '@types/node@22.5.5':
+ dependencies:
+ undici-types: 6.19.8
+
+ '@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)':
+ dependencies:
+ '@eslint-community/regexpp': 4.11.1
+ '@typescript-eslint/parser': 8.6.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)
+ '@typescript-eslint/scope-manager': 8.6.0
+ '@typescript-eslint/type-utils': 8.6.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)
+ '@typescript-eslint/utils': 8.6.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)
+ '@typescript-eslint/visitor-keys': 8.6.0
+ eslint: 9.10.0(jiti@1.21.6)
+ graphemer: 1.4.0
+ ignore: 5.3.2
+ natural-compare: 1.4.0
+ ts-api-utils: 1.3.0(typescript@5.6.2)
+ optionalDependencies:
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.6.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.6.0
+ '@typescript-eslint/types': 8.6.0
+ '@typescript-eslint/typescript-estree': 8.6.0(typescript@5.6.2)
+ '@typescript-eslint/visitor-keys': 8.6.0
+ debug: 4.3.7
+ eslint: 9.10.0(jiti@1.21.6)
+ optionalDependencies:
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.6.0':
+ dependencies:
+ '@typescript-eslint/types': 8.6.0
+ '@typescript-eslint/visitor-keys': 8.6.0
+
+ '@typescript-eslint/type-utils@8.6.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)':
+ dependencies:
+ '@typescript-eslint/typescript-estree': 8.6.0(typescript@5.6.2)
+ '@typescript-eslint/utils': 8.6.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)
+ debug: 4.3.7
+ ts-api-utils: 1.3.0(typescript@5.6.2)
+ optionalDependencies:
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - eslint
+ - supports-color
+
+ '@typescript-eslint/types@8.6.0': {}
+
+ '@typescript-eslint/typescript-estree@8.6.0(typescript@5.6.2)':
+ dependencies:
+ '@typescript-eslint/types': 8.6.0
+ '@typescript-eslint/visitor-keys': 8.6.0
+ debug: 4.3.7
+ fast-glob: 3.3.2
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.6.3
+ ts-api-utils: 1.3.0(typescript@5.6.2)
+ optionalDependencies:
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.6.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6))
+ '@typescript-eslint/scope-manager': 8.6.0
+ '@typescript-eslint/types': 8.6.0
+ '@typescript-eslint/typescript-estree': 8.6.0(typescript@5.6.2)
+ eslint: 9.10.0(jiti@1.21.6)
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@typescript-eslint/visitor-keys@8.6.0':
+ dependencies:
+ '@typescript-eslint/types': 8.6.0
+ eslint-visitor-keys: 3.4.3
+
+ '@vitejs/plugin-vue@5.1.4(vite@5.4.6(@types/node@22.5.5)(less@4.2.0))(vue@3.5.6(typescript@5.6.2))':
+ dependencies:
+ vite: 5.4.6(@types/node@22.5.5)(less@4.2.0)
+ vue: 3.5.6(typescript@5.6.2)
+
+ '@vue/compiler-core@3.5.6':
+ dependencies:
+ '@babel/parser': 7.25.6
+ '@vue/shared': 3.5.6
+ entities: 4.5.0
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
+
+ '@vue/compiler-dom@3.5.6':
+ dependencies:
+ '@vue/compiler-core': 3.5.6
+ '@vue/shared': 3.5.6
+
+ '@vue/compiler-sfc@3.5.6':
+ dependencies:
+ '@babel/parser': 7.25.6
+ '@vue/compiler-core': 3.5.6
+ '@vue/compiler-dom': 3.5.6
+ '@vue/compiler-ssr': 3.5.6
+ '@vue/shared': 3.5.6
+ estree-walker: 2.0.2
+ magic-string: 0.30.11
+ postcss: 8.4.47
+ source-map-js: 1.2.1
+
+ '@vue/compiler-ssr@3.5.6':
+ dependencies:
+ '@vue/compiler-dom': 3.5.6
+ '@vue/shared': 3.5.6
+
+ '@vue/devtools-api@6.6.4': {}
+
+ '@vue/reactivity@3.5.6':
+ dependencies:
+ '@vue/shared': 3.5.6
+
+ '@vue/runtime-core@3.5.6':
+ dependencies:
+ '@vue/reactivity': 3.5.6
+ '@vue/shared': 3.5.6
+
+ '@vue/runtime-dom@3.5.6':
+ dependencies:
+ '@vue/reactivity': 3.5.6
+ '@vue/runtime-core': 3.5.6
+ '@vue/shared': 3.5.6
+ csstype: 3.1.3
+
+ '@vue/server-renderer@3.5.6(vue@3.5.6(typescript@5.6.2))':
+ dependencies:
+ '@vue/compiler-ssr': 3.5.6
+ '@vue/shared': 3.5.6
+ vue: 3.5.6(typescript@5.6.2)
+
+ '@vue/shared@3.5.6': {}
+
+ acorn-jsx@5.3.2(acorn@8.12.1):
+ dependencies:
+ acorn: 8.12.1
+
+ acorn@8.12.1: {}
+
+ ajv@6.12.6:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ ansi-escapes@7.0.0:
+ dependencies:
+ environment: 1.1.0
+
+ ansi-regex@5.0.1: {}
+
+ ansi-regex@6.1.0: {}
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@6.2.1: {}
+
+ any-promise@1.3.0: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+
+ arg@5.0.2: {}
+
+ argparse@2.0.1: {}
+
+ autoprefixer@10.4.20(postcss@8.4.47):
+ dependencies:
+ browserslist: 4.23.3
+ caniuse-lite: 1.0.30001662
+ fraction.js: 4.3.7
+ normalize-range: 0.1.2
+ picocolors: 1.1.0
+ postcss: 8.4.47
+ postcss-value-parser: 4.2.0
+
+ balanced-match@1.0.2: {}
+
+ binary-extensions@2.3.0: {}
+
+ boolbase@1.0.0: {}
+
+ brace-expansion@1.1.11:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.0.1:
+ dependencies:
+ balanced-match: 1.0.2
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ browserslist@4.23.3:
+ dependencies:
+ caniuse-lite: 1.0.30001662
+ electron-to-chromium: 1.5.25
+ node-releases: 2.0.18
+ update-browserslist-db: 1.1.0(browserslist@4.23.3)
+
+ callsites@3.1.0: {}
+
+ camelcase-css@2.0.1: {}
+
+ caniuse-lite@1.0.30001662: {}
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ chalk@5.3.0: {}
+
+ chokidar@3.6.0:
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.3
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ cli-cursor@5.0.0:
+ dependencies:
+ restore-cursor: 5.1.0
+
+ cli-truncate@4.0.0:
+ dependencies:
+ slice-ansi: 5.0.0
+ string-width: 7.2.0
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.4: {}
+
+ colorette@2.0.20: {}
+
+ commander@12.1.0: {}
+
+ commander@4.1.1: {}
+
+ concat-map@0.0.1: {}
+
+ copy-anything@2.0.6:
+ dependencies:
+ is-what: 3.14.1
+
+ cross-spawn@7.0.3:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ cssesc@3.0.0: {}
+
+ csstype@3.1.3: {}
+
+ debug@4.3.7:
+ dependencies:
+ ms: 2.1.3
+
+ deep-is@0.1.4: {}
+
+ didyoumean@1.2.2: {}
+
+ dlv@1.1.3: {}
+
+ eastasianwidth@0.2.0: {}
+
+ electron-to-chromium@1.5.25: {}
+
+ emoji-regex@10.4.0: {}
+
+ emoji-regex@8.0.0: {}
+
+ emoji-regex@9.2.2: {}
+
+ entities@4.5.0: {}
+
+ environment@1.1.0: {}
+
+ errno@0.1.8:
+ dependencies:
+ prr: 1.0.1
+ optional: true
+
+ esbuild@0.21.5:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.21.5
+ '@esbuild/android-arm': 0.21.5
+ '@esbuild/android-arm64': 0.21.5
+ '@esbuild/android-x64': 0.21.5
+ '@esbuild/darwin-arm64': 0.21.5
+ '@esbuild/darwin-x64': 0.21.5
+ '@esbuild/freebsd-arm64': 0.21.5
+ '@esbuild/freebsd-x64': 0.21.5
+ '@esbuild/linux-arm': 0.21.5
+ '@esbuild/linux-arm64': 0.21.5
+ '@esbuild/linux-ia32': 0.21.5
+ '@esbuild/linux-loong64': 0.21.5
+ '@esbuild/linux-mips64el': 0.21.5
+ '@esbuild/linux-ppc64': 0.21.5
+ '@esbuild/linux-riscv64': 0.21.5
+ '@esbuild/linux-s390x': 0.21.5
+ '@esbuild/linux-x64': 0.21.5
+ '@esbuild/netbsd-x64': 0.21.5
+ '@esbuild/openbsd-x64': 0.21.5
+ '@esbuild/sunos-x64': 0.21.5
+ '@esbuild/win32-arm64': 0.21.5
+ '@esbuild/win32-ia32': 0.21.5
+ '@esbuild/win32-x64': 0.21.5
+
+ escalade@3.2.0: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ eslint-plugin-vue@9.28.0(eslint@9.10.0(jiti@1.21.6)):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6))
+ eslint: 9.10.0(jiti@1.21.6)
+ globals: 13.24.0
+ natural-compare: 1.4.0
+ nth-check: 2.1.1
+ postcss-selector-parser: 6.1.2
+ semver: 7.6.3
+ vue-eslint-parser: 9.4.3(eslint@9.10.0(jiti@1.21.6))
+ xml-name-validator: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-scope@7.2.2:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-scope@8.0.2:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@4.0.0: {}
+
+ eslint@9.10.0(jiti@1.21.6):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6))
+ '@eslint-community/regexpp': 4.11.1
+ '@eslint/config-array': 0.18.0
+ '@eslint/eslintrc': 3.1.0
+ '@eslint/js': 9.10.0
+ '@eslint/plugin-kit': 0.1.0
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.3.0
+ '@nodelib/fs.walk': 1.2.8
+ ajv: 6.12.6
+ chalk: 4.1.2
+ cross-spawn: 7.0.3
+ debug: 4.3.7
+ escape-string-regexp: 4.0.0
+ eslint-scope: 8.0.2
+ eslint-visitor-keys: 4.0.0
+ espree: 10.1.0
+ esquery: 1.6.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ is-path-inside: 3.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.2
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ strip-ansi: 6.0.1
+ text-table: 0.2.0
+ optionalDependencies:
+ jiti: 1.21.6
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@10.1.0:
+ dependencies:
+ acorn: 8.12.1
+ acorn-jsx: 5.3.2(acorn@8.12.1)
+ eslint-visitor-keys: 4.0.0
+
+ espree@9.6.1:
+ dependencies:
+ acorn: 8.12.1
+ acorn-jsx: 5.3.2(acorn@8.12.1)
+ eslint-visitor-keys: 3.4.3
+
+ esquery@1.6.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ estree-walker@2.0.2: {}
+
+ esutils@2.0.3: {}
+
+ eventemitter3@5.0.1: {}
+
+ execa@8.0.1:
+ dependencies:
+ cross-spawn: 7.0.3
+ get-stream: 8.0.1
+ human-signals: 5.0.0
+ is-stream: 3.0.0
+ merge-stream: 2.0.0
+ npm-run-path: 5.3.0
+ onetime: 6.0.0
+ signal-exit: 4.1.0
+ strip-final-newline: 3.0.0
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-glob@3.3.2:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fastq@1.17.1:
+ dependencies:
+ reusify: 1.0.4
+
+ file-entry-cache@8.0.0:
+ dependencies:
+ flat-cache: 4.0.1
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@4.0.1:
+ dependencies:
+ flatted: 3.3.1
+ keyv: 4.5.4
+
+ flatted@3.3.1: {}
+
+ foreground-child@3.3.0:
+ dependencies:
+ cross-spawn: 7.0.3
+ signal-exit: 4.1.0
+
+ fraction.js@4.3.7: {}
+
+ fsevents@2.3.3:
+ optional: true
+
+ function-bind@1.1.2: {}
+
+ get-east-asian-width@1.2.0: {}
+
+ get-stream@8.0.1: {}
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob@10.4.5:
+ dependencies:
+ foreground-child: 3.3.0
+ jackspeak: 3.4.3
+ minimatch: 9.0.5
+ minipass: 7.1.2
+ package-json-from-dist: 1.0.0
+ path-scurry: 1.11.1
+
+ globals@13.24.0:
+ dependencies:
+ type-fest: 0.20.2
+
+ globals@14.0.0: {}
+
+ graceful-fs@4.2.11:
+ optional: true
+
+ graphemer@1.4.0: {}
+
+ has-flag@4.0.0: {}
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ human-signals@5.0.0: {}
+
+ husky@9.1.6: {}
+
+ iconv-lite@0.6.3:
+ dependencies:
+ safer-buffer: 2.1.2
+ optional: true
+
+ ignore@5.3.2: {}
+
+ image-size@0.5.5:
+ optional: true
+
+ import-fresh@3.3.0:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ imurmurhash@0.1.4: {}
+
+ is-binary-path@2.1.0:
+ dependencies:
+ binary-extensions: 2.3.0
+
+ is-core-module@2.15.1:
+ dependencies:
+ hasown: 2.0.2
+
+ is-extglob@2.1.1: {}
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-fullwidth-code-point@4.0.0: {}
+
+ is-fullwidth-code-point@5.0.0:
+ dependencies:
+ get-east-asian-width: 1.2.0
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-number@7.0.0: {}
+
+ is-path-inside@3.0.3: {}
+
+ is-stream@3.0.0: {}
+
+ is-what@3.14.1: {}
+
+ isexe@2.0.0: {}
+
+ jackspeak@3.4.3:
+ dependencies:
+ '@isaacs/cliui': 8.0.2
+ optionalDependencies:
+ '@pkgjs/parseargs': 0.11.0
+
+ jiti@1.21.6: {}
+
+ js-yaml@4.1.0:
+ dependencies:
+ argparse: 2.0.1
+
+ json-buffer@3.0.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ less@4.2.0:
+ dependencies:
+ copy-anything: 2.0.6
+ parse-node-version: 1.0.1
+ tslib: 2.7.0
+ optionalDependencies:
+ errno: 0.1.8
+ graceful-fs: 4.2.11
+ image-size: 0.5.5
+ make-dir: 2.1.0
+ mime: 1.6.0
+ needle: 3.3.1
+ source-map: 0.6.1
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ lilconfig@2.1.0: {}
+
+ lilconfig@3.1.2: {}
+
+ lines-and-columns@1.2.4: {}
+
+ lint-staged@15.2.10:
+ dependencies:
+ chalk: 5.3.0
+ commander: 12.1.0
+ debug: 4.3.7
+ execa: 8.0.1
+ lilconfig: 3.1.2
+ listr2: 8.2.4
+ micromatch: 4.0.8
+ pidtree: 0.6.0
+ string-argv: 0.3.2
+ yaml: 2.5.1
+ transitivePeerDependencies:
+ - supports-color
+
+ listr2@8.2.4:
+ dependencies:
+ cli-truncate: 4.0.0
+ colorette: 2.0.20
+ eventemitter3: 5.0.1
+ log-update: 6.1.0
+ rfdc: 1.4.1
+ wrap-ansi: 9.0.0
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash.merge@4.6.2: {}
+
+ lodash@4.17.21: {}
+
+ log-update@6.1.0:
+ dependencies:
+ ansi-escapes: 7.0.0
+ cli-cursor: 5.0.0
+ slice-ansi: 7.1.0
+ strip-ansi: 7.1.0
+ wrap-ansi: 9.0.0
+
+ lru-cache@10.4.3: {}
+
+ magic-string@0.30.11:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ make-dir@2.1.0:
+ dependencies:
+ pify: 4.0.1
+ semver: 5.7.2
+ optional: true
+
+ merge-stream@2.0.0: {}
+
+ merge2@1.4.1: {}
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ mime@1.6.0:
+ optional: true
+
+ mimic-fn@4.0.0: {}
+
+ mimic-function@5.0.1: {}
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.11
+
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.1
+
+ minipass@7.1.2: {}
+
+ ms@2.1.3: {}
+
+ mz@2.7.0:
+ dependencies:
+ any-promise: 1.3.0
+ object-assign: 4.1.1
+ thenify-all: 1.6.0
+
+ nanoid@3.3.7: {}
+
+ natural-compare@1.4.0: {}
+
+ needle@3.3.1:
+ dependencies:
+ iconv-lite: 0.6.3
+ sax: 1.4.1
+ optional: true
+
+ node-releases@2.0.18: {}
+
+ normalize-path@3.0.0: {}
+
+ normalize-range@0.1.2: {}
+
+ npm-run-path@5.3.0:
+ dependencies:
+ path-key: 4.0.0
+
+ nth-check@2.1.1:
+ dependencies:
+ boolbase: 1.0.0
+
+ object-assign@4.1.1: {}
+
+ object-hash@3.0.0: {}
+
+ onetime@6.0.0:
+ dependencies:
+ mimic-fn: 4.0.0
+
+ onetime@7.0.0:
+ dependencies:
+ mimic-function: 5.0.1
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ package-json-from-dist@1.0.0: {}
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ parse-node-version@1.0.1: {}
+
+ path-exists@4.0.0: {}
+
+ path-key@3.1.1: {}
+
+ path-key@4.0.0: {}
+
+ path-parse@1.0.7: {}
+
+ path-scurry@1.11.1:
+ dependencies:
+ lru-cache: 10.4.3
+ minipass: 7.1.2
+
+ picocolors@1.1.0: {}
+
+ picomatch@2.3.1: {}
+
+ pidtree@0.6.0: {}
+
+ pify@2.3.0: {}
+
+ pify@4.0.1:
+ optional: true
+
+ pirates@4.0.6: {}
+
+ postcss-import@15.1.0(postcss@8.4.47):
+ dependencies:
+ postcss: 8.4.47
+ postcss-value-parser: 4.2.0
+ read-cache: 1.0.0
+ resolve: 1.22.8
+
+ postcss-js@4.0.1(postcss@8.4.47):
+ dependencies:
+ camelcase-css: 2.0.1
+ postcss: 8.4.47
+
+ postcss-load-config@4.0.2(postcss@8.4.47):
+ dependencies:
+ lilconfig: 3.1.2
+ yaml: 2.5.1
+ optionalDependencies:
+ postcss: 8.4.47
+
+ postcss-nested@6.2.0(postcss@8.4.47):
+ dependencies:
+ postcss: 8.4.47
+ postcss-selector-parser: 6.1.2
+
+ postcss-selector-parser@6.1.2:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss-value-parser@4.2.0: {}
+
+ postcss@8.4.47:
+ dependencies:
+ nanoid: 3.3.7
+ picocolors: 1.1.0
+ source-map-js: 1.2.1
+
+ prelude-ls@1.2.1: {}
+
+ prettier-plugin-organize-imports@4.1.0(prettier@3.3.3)(typescript@5.6.2):
+ dependencies:
+ prettier: 3.3.3
+ typescript: 5.6.2
+
+ prettier@3.3.3: {}
+
+ primevue@4.0.7(vue@3.5.6(typescript@5.6.2)):
+ dependencies:
+ '@primeuix/styled': 0.0.5
+ '@primeuix/utils': 0.0.5
+ '@primevue/core': 4.0.7(vue@3.5.6(typescript@5.6.2))
+ '@primevue/icons': 4.0.7(vue@3.5.6(typescript@5.6.2))
+ transitivePeerDependencies:
+ - vue
+
+ prr@1.0.1:
+ optional: true
+
+ punycode@2.3.1: {}
+
+ queue-microtask@1.2.3: {}
+
+ read-cache@1.0.0:
+ dependencies:
+ pify: 2.3.0
+
+ readdirp@3.6.0:
+ dependencies:
+ picomatch: 2.3.1
+
+ resolve-from@4.0.0: {}
+
+ resolve@1.22.8:
+ dependencies:
+ is-core-module: 2.15.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ restore-cursor@5.1.0:
+ dependencies:
+ onetime: 7.0.0
+ signal-exit: 4.1.0
+
+ reusify@1.0.4: {}
+
+ rfdc@1.4.1: {}
+
+ rollup@4.22.0:
+ dependencies:
+ '@types/estree': 1.0.5
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.22.0
+ '@rollup/rollup-android-arm64': 4.22.0
+ '@rollup/rollup-darwin-arm64': 4.22.0
+ '@rollup/rollup-darwin-x64': 4.22.0
+ '@rollup/rollup-linux-arm-gnueabihf': 4.22.0
+ '@rollup/rollup-linux-arm-musleabihf': 4.22.0
+ '@rollup/rollup-linux-arm64-gnu': 4.22.0
+ '@rollup/rollup-linux-arm64-musl': 4.22.0
+ '@rollup/rollup-linux-powerpc64le-gnu': 4.22.0
+ '@rollup/rollup-linux-riscv64-gnu': 4.22.0
+ '@rollup/rollup-linux-s390x-gnu': 4.22.0
+ '@rollup/rollup-linux-x64-gnu': 4.22.0
+ '@rollup/rollup-linux-x64-musl': 4.22.0
+ '@rollup/rollup-win32-arm64-msvc': 4.22.0
+ '@rollup/rollup-win32-ia32-msvc': 4.22.0
+ '@rollup/rollup-win32-x64-msvc': 4.22.0
+ fsevents: 2.3.3
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ safer-buffer@2.1.2:
+ optional: true
+
+ sax@1.4.1:
+ optional: true
+
+ semver@5.7.2:
+ optional: true
+
+ semver@7.6.3: {}
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ signal-exit@4.1.0: {}
+
+ slice-ansi@5.0.0:
+ dependencies:
+ ansi-styles: 6.2.1
+ is-fullwidth-code-point: 4.0.0
+
+ slice-ansi@7.1.0:
+ dependencies:
+ ansi-styles: 6.2.1
+ is-fullwidth-code-point: 5.0.0
+
+ source-map-js@1.2.1: {}
+
+ source-map@0.6.1:
+ optional: true
+
+ string-argv@0.3.2: {}
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string-width@5.1.2:
+ dependencies:
+ eastasianwidth: 0.2.0
+ emoji-regex: 9.2.2
+ strip-ansi: 7.1.0
+
+ string-width@7.2.0:
+ dependencies:
+ emoji-regex: 10.4.0
+ get-east-asian-width: 1.2.0
+ strip-ansi: 7.1.0
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-ansi@7.1.0:
+ dependencies:
+ ansi-regex: 6.1.0
+
+ strip-final-newline@3.0.0: {}
+
+ strip-json-comments@3.1.1: {}
+
+ sucrase@3.35.0:
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.5
+ commander: 4.1.1
+ glob: 10.4.5
+ lines-and-columns: 1.2.4
+ mz: 2.7.0
+ pirates: 4.0.6
+ ts-interface-checker: 0.1.13
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-preserve-symlinks-flag@1.0.0: {}
+
+ tailwindcss@3.4.12:
+ dependencies:
+ '@alloc/quick-lru': 5.2.0
+ arg: 5.0.2
+ chokidar: 3.6.0
+ didyoumean: 1.2.2
+ dlv: 1.1.3
+ fast-glob: 3.3.2
+ glob-parent: 6.0.2
+ is-glob: 4.0.3
+ jiti: 1.21.6
+ lilconfig: 2.1.0
+ micromatch: 4.0.8
+ normalize-path: 3.0.0
+ object-hash: 3.0.0
+ picocolors: 1.1.0
+ postcss: 8.4.47
+ postcss-import: 15.1.0(postcss@8.4.47)
+ postcss-js: 4.0.1(postcss@8.4.47)
+ postcss-load-config: 4.0.2(postcss@8.4.47)
+ postcss-nested: 6.2.0(postcss@8.4.47)
+ postcss-selector-parser: 6.1.2
+ resolve: 1.22.8
+ sucrase: 3.35.0
+ transitivePeerDependencies:
+ - ts-node
+
+ text-table@0.2.0: {}
+
+ thenify-all@1.6.0:
+ dependencies:
+ thenify: 3.3.1
+
+ thenify@3.3.1:
+ dependencies:
+ any-promise: 1.3.0
+
+ to-fast-properties@2.0.0: {}
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ ts-api-utils@1.3.0(typescript@5.6.2):
+ dependencies:
+ typescript: 5.6.2
+
+ ts-interface-checker@0.1.13: {}
+
+ tslib@2.7.0: {}
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ type-fest@0.20.2: {}
+
+ typescript-eslint@8.6.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2):
+ dependencies:
+ '@typescript-eslint/eslint-plugin': 8.6.0(@typescript-eslint/parser@8.6.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)
+ '@typescript-eslint/parser': 8.6.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)
+ '@typescript-eslint/utils': 8.6.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.6.2)
+ optionalDependencies:
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - eslint
+ - supports-color
+
+ typescript@5.6.2: {}
+
+ undici-types@6.19.8: {}
+
+ update-browserslist-db@1.1.0(browserslist@4.23.3):
+ dependencies:
+ browserslist: 4.23.3
+ escalade: 3.2.0
+ picocolors: 1.1.0
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ util-deprecate@1.0.2: {}
+
+ vite@5.4.6(@types/node@22.5.5)(less@4.2.0):
+ dependencies:
+ esbuild: 0.21.5
+ postcss: 8.4.47
+ rollup: 4.22.0
+ optionalDependencies:
+ '@types/node': 22.5.5
+ fsevents: 2.3.3
+ less: 4.2.0
+
+ vue-eslint-parser@9.4.3(eslint@9.10.0(jiti@1.21.6)):
+ dependencies:
+ debug: 4.3.7
+ eslint: 9.10.0(jiti@1.21.6)
+ eslint-scope: 7.2.2
+ eslint-visitor-keys: 3.4.3
+ espree: 9.6.1
+ esquery: 1.6.0
+ lodash: 4.17.21
+ semver: 7.6.3
+ transitivePeerDependencies:
+ - supports-color
+
+ vue-i18n@9.14.0(vue@3.5.6(typescript@5.6.2)):
+ dependencies:
+ '@intlify/core-base': 9.14.0
+ '@intlify/shared': 9.14.0
+ '@vue/devtools-api': 6.6.4
+ vue: 3.5.6(typescript@5.6.2)
+
+ vue@3.5.6(typescript@5.6.2):
+ dependencies:
+ '@vue/compiler-dom': 3.5.6
+ '@vue/compiler-sfc': 3.5.6
+ '@vue/runtime-dom': 3.5.6
+ '@vue/server-renderer': 3.5.6(vue@3.5.6(typescript@5.6.2))
+ '@vue/shared': 3.5.6
+ optionalDependencies:
+ typescript: 5.6.2
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ word-wrap@1.2.5: {}
+
+ wrap-ansi@7.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrap-ansi@8.1.0:
+ dependencies:
+ ansi-styles: 6.2.1
+ string-width: 5.1.2
+ strip-ansi: 7.1.0
+
+ wrap-ansi@9.0.0:
+ dependencies:
+ ansi-styles: 6.2.1
+ string-width: 7.2.0
+ strip-ansi: 7.1.0
+
+ xml-name-validator@4.0.0: {}
+
+ yaml@2.5.1: {}
+
+ yocto-queue@0.1.0: {}
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000..2e7af2b
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000..5c8bc02
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,8 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: [],
+ theme: {
+ extend: {},
+ },
+ plugins: [],
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..cb8982b
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,33 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "module": "ESNext",
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "skipLibCheck": true,
+ "incremental": true,
+ "sourceMap": true,
+ "esModuleInterop": true,
+ "moduleResolution": "Node",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+
+ /* Linting */
+ "strict": false,
+ "strictNullChecks": true,
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noFallthroughCasesInSwitch": true,
+ "downlevelIteration": true,
+
+ /* AllowJs during migration phase */
+ "allowJs": true,
+ "baseUrl": ".",
+ "outDir": "./web",
+ "rootDir": "./"
+ },
+ "include": [
+ "src/**/*",
+ "src/**/*.vue",
+ ]
+}
\ No newline at end of file
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..b392268
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,25 @@
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+
+export default defineConfig({
+ plugins: [vue()],
+
+ build: {
+ outDir: 'web',
+ minify: 'esbuild',
+ target: 'es2022',
+ sourcemap: true,
+ rollupOptions: {
+ // Disabling tree-shaking
+ // Prevent vite remove unused exports
+ treeshake: true,
+ },
+ },
+
+ esbuild: {
+ minifyIdentifiers: false,
+ keepNames: true,
+ minifySyntax: true,
+ minifyWhitespace: true,
+ },
+})
diff --git a/web/downshow.js b/web/downshow.js
deleted file mode 100644
index 8c36e2d..0000000
--- a/web/downshow.js
+++ /dev/null
@@ -1,231 +0,0 @@
-/**
- * downshow.js -- A javascript library to convert HTML to markdown.
- *
- * Copyright (c) 2013 Alex Cornejo.
- *
- * Original Markdown Copyright (c) 2004-2005 John Gruber
- *
- *
- * Redistributable under a BSD-style open source license.
- *
- * downshow has no external dependencies. It has been tested in chrome and
- * firefox, it probably works in internet explorer, but YMMV.
- *
- * Basic Usage:
- *
- * downshow(document.getElementById('#yourid').innerHTML);
- *
- * TODO:
- * - Remove extra whitespace between words in headers and other places.
- */
-
-(function () {
- var doc;
-
- // Use browser DOM with jsdom as a fallback (for node.js)
- try {
- doc = document;
- } catch(e) {
- var jsdom = require("jsdom").jsdom;
- doc = jsdom("");
- }
-
- /**
- * Returns every element in root in their bfs traversal order.
- *
- * In the process it transforms any nested lists to conform to the w3c
- * standard, see: http://www.w3.org/wiki/HTML_lists#Nesting_lists
- */
- function bfsOrder(root) {
- var inqueue = [root], outqueue = [];
- root._bfs_parent = null;
- while (inqueue.length > 0) {
- var elem = inqueue.shift();
- outqueue.push(elem);
- var children = elem.childNodes;
- var liParent = null;
- for (var i=0 ; i 0) {
- if (prefix && suffix)
- node._bfs_text = prefix + content + suffix;
- else
- node._bfs_text = content;
- } else
- node._bfs_text = '';
- }
-
- /**
- * Get a node's content.
- */
- function getContent(node) {
- var text = '', atom;
- for (var i = 0; i 0)
- setContent(node, '[' + text + '](' + href + (title ? ' "' + title + '"' : '') + ')');
- else
- setContent(node, '');
- } else if (node.tagName === 'IMG') {
- var src = node.getAttribute('src') ? nltrim(node.getAttribute('src')) : '', alt = node.alt ? nltrim(node.alt) : '', caption = node.title ? nltrim(node.title) : '';
- if (src.length > 0)
- setContent(node, ' + ')');
- else
- setContent(node, '');
- } else if (node.tagName === 'BLOCKQUOTE') {
- var block_content = getContent(node);
- if (block_content.length > 0)
- setContent(node, prefixBlock('> ', block_content), '\n\n', '\n\n');
- else
- setContent(node, '');
- } else if (node.tagName === 'CODE') {
- if (node._bfs_parent.tagName === 'PRE' && node._bfs_parent._bfs_parent !== null)
- setContent(node, prefixBlock(' ', getContent(node)));
- else
- setContent(node, nltrim(getContent(node)), '`', '`');
- } else if (node.tagName === 'LI') {
- var list_content = getContent(node);
- if (list_content.length > 0)
- if (node._bfs_parent.tagName === 'OL')
- setContent(node, trim(prefixBlock(' ', list_content, true)), '1. ', '\n\n');
- else
- setContent(node, trim(prefixBlock(' ', list_content, true)), '- ', '\n\n');
- else
- setContent(node, '');
- } else
- setContent(node, getContent(node));
- }
-
- function downshow(html, options) {
- var root = doc.createElement('pre');
- root.innerHTML = html;
- var nodes = bfsOrder(root).reverse(), i;
-
- if (options && options.nodeParser) {
- for (i = 0; i )+[^\n]*)\n+(\n(?:> )+)/g, "$1\n$2")
- // remove empty blockquotes
- .replace(/\n((?:> )+[ ]*\n)+/g, '\n\n')
- // remove extra newlines
- .replace(/\n[ \t]*(?:\n[ \t]*)+\n/g,'\n\n')
- // remove trailing whitespace
- .replace(/\s\s*$/, '')
- // convert lists to inline when not using paragraphs
- .replace(/^([ \t]*(?:\d+\.|\+|\-)[^\n]*)\n\n+(?=[ \t]*(?:\d+\.|\+|\-|\*)[^\n]*)/gm, "$1\n")
- // remove starting newlines
- .replace(/^\n\n*/, '');
- }
-
- // Export for use in server and client.
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')
- module.exports = downshow;
- else if (typeof define === 'function' && define.amd)
- define([], function () {return downshow;});
- else
- window.downshow = downshow;
- })();
\ No newline at end of file
diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs
deleted file mode 100644
index 2edbef8..0000000
--- a/web/eslint.config.mjs
+++ /dev/null
@@ -1,8 +0,0 @@
-import globals from "globals";
-import pluginJs from "@eslint/js";
-
-
-export default [
- {languageOptions: { globals: globals.browser }},
- pluginJs.configs.recommended,
-];
\ No newline at end of file
diff --git a/web/marked.js b/web/marked.js
deleted file mode 100644
index f5cea94..0000000
--- a/web/marked.js
+++ /dev/null
@@ -1,2498 +0,0 @@
-/**
- * marked v14.1.0 - a markdown parser
- * Copyright (c) 2011-2024, Christopher Jeffrey. (MIT Licensed)
- * https://github.com/markedjs/marked
- */
-
-/**
- * DO NOT EDIT THIS FILE
- * The code in this file is generated from files in ./src/
- */
-
-/**
- * Gets the original marked default options.
- */
-function _getDefaults() {
- return {
- async: false,
- breaks: false,
- extensions: null,
- gfm: true,
- hooks: null,
- pedantic: false,
- renderer: null,
- silent: false,
- tokenizer: null,
- walkTokens: null,
- };
-}
-let _defaults = _getDefaults();
-function changeDefaults(newDefaults) {
- _defaults = newDefaults;
-}
-
-/**
- * Helpers
- */
-const escapeTest = /[&<>"']/;
-const escapeReplace = new RegExp(escapeTest.source, 'g');
-const escapeTestNoEncode = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/;
-const escapeReplaceNoEncode = new RegExp(escapeTestNoEncode.source, 'g');
-const escapeReplacements = {
- '&': '&',
- '<': '<',
- '>': '>',
- '"': '"',
- "'": ''',
-};
-const getEscapeReplacement = (ch) => escapeReplacements[ch];
-function escape$1(html, encode) {
- if (encode) {
- if (escapeTest.test(html)) {
- return html.replace(escapeReplace, getEscapeReplacement);
- }
- }
- else {
- if (escapeTestNoEncode.test(html)) {
- return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
- }
- }
- return html;
-}
-const caret = /(^|[^\[])\^/g;
-function edit(regex, opt) {
- let source = typeof regex === 'string' ? regex : regex.source;
- opt = opt || '';
- const obj = {
- replace: (name, val) => {
- let valSource = typeof val === 'string' ? val : val.source;
- valSource = valSource.replace(caret, '$1');
- source = source.replace(name, valSource);
- return obj;
- },
- getRegex: () => {
- return new RegExp(source, opt);
- },
- };
- return obj;
-}
-function cleanUrl(href) {
- try {
- href = encodeURI(href).replace(/%25/g, '%');
- }
- catch {
- return null;
- }
- return href;
-}
-const noopTest = { exec: () => null };
-function splitCells(tableRow, count) {
- // ensure that every cell-delimiting pipe has a space
- // before it to distinguish it from an escaped pipe
- const row = tableRow.replace(/\|/g, (match, offset, str) => {
- let escaped = false;
- let curr = offset;
- while (--curr >= 0 && str[curr] === '\\')
- escaped = !escaped;
- if (escaped) {
- // odd number of slashes means | is escaped
- // so we leave it alone
- return '|';
- }
- else {
- // add space before unescaped |
- return ' |';
- }
- }), cells = row.split(/ \|/);
- let i = 0;
- // First/last cell in a row cannot be empty if it has no leading/trailing pipe
- if (!cells[0].trim()) {
- cells.shift();
- }
- if (cells.length > 0 && !cells[cells.length - 1].trim()) {
- cells.pop();
- }
- if (count) {
- if (cells.length > count) {
- cells.splice(count);
- }
- else {
- while (cells.length < count)
- cells.push('');
- }
- }
- for (; i < cells.length; i++) {
- // leading or trailing whitespace is ignored per the gfm spec
- cells[i] = cells[i].trim().replace(/\\\|/g, '|');
- }
- return cells;
-}
-/**
- * Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
- * /c*$/ is vulnerable to REDOS.
- *
- * @param str
- * @param c
- * @param invert Remove suffix of non-c chars instead. Default falsey.
- */
-function rtrim(str, c, invert) {
- const l = str.length;
- if (l === 0) {
- return '';
- }
- // Length of suffix matching the invert condition.
- let suffLen = 0;
- // Step left until we fail to match the invert condition.
- while (suffLen < l) {
- const currChar = str.charAt(l - suffLen - 1);
- if (currChar === c && !invert) {
- suffLen++;
- }
- else if (currChar !== c && invert) {
- suffLen++;
- }
- else {
- break;
- }
- }
- return str.slice(0, l - suffLen);
-}
-function findClosingBracket(str, b) {
- if (str.indexOf(b[1]) === -1) {
- return -1;
- }
- let level = 0;
- for (let i = 0; i < str.length; i++) {
- if (str[i] === '\\') {
- i++;
- }
- else if (str[i] === b[0]) {
- level++;
- }
- else if (str[i] === b[1]) {
- level--;
- if (level < 0) {
- return i;
- }
- }
- }
- return -1;
-}
-
-function outputLink(cap, link, raw, lexer) {
- const href = link.href;
- const title = link.title ? escape$1(link.title) : null;
- const text = cap[1].replace(/\\([\[\]])/g, '$1');
- if (cap[0].charAt(0) !== '!') {
- lexer.state.inLink = true;
- const token = {
- type: 'link',
- raw,
- href,
- title,
- text,
- tokens: lexer.inlineTokens(text),
- };
- lexer.state.inLink = false;
- return token;
- }
- return {
- type: 'image',
- raw,
- href,
- title,
- text: escape$1(text),
- };
-}
-function indentCodeCompensation(raw, text) {
- const matchIndentToCode = raw.match(/^(\s+)(?:```)/);
- if (matchIndentToCode === null) {
- return text;
- }
- const indentToCode = matchIndentToCode[1];
- return text
- .split('\n')
- .map(node => {
- const matchIndentInNode = node.match(/^\s+/);
- if (matchIndentInNode === null) {
- return node;
- }
- const [indentInNode] = matchIndentInNode;
- if (indentInNode.length >= indentToCode.length) {
- return node.slice(indentToCode.length);
- }
- return node;
- })
- .join('\n');
-}
-/**
- * Tokenizer
- */
-class _Tokenizer {
- options;
- rules; // set by the lexer
- lexer; // set by the lexer
- constructor(options) {
- this.options = options || _defaults;
- }
- space(src) {
- const cap = this.rules.block.newline.exec(src);
- if (cap && cap[0].length > 0) {
- return {
- type: 'space',
- raw: cap[0],
- };
- }
- }
- code(src) {
- const cap = this.rules.block.code.exec(src);
- if (cap) {
- const text = cap[0].replace(/^ {1,4}/gm, '');
- return {
- type: 'code',
- raw: cap[0],
- codeBlockStyle: 'indented',
- text: !this.options.pedantic
- ? rtrim(text, '\n')
- : text,
- };
- }
- }
- fences(src) {
- const cap = this.rules.block.fences.exec(src);
- if (cap) {
- const raw = cap[0];
- const text = indentCodeCompensation(raw, cap[3] || '');
- return {
- type: 'code',
- raw,
- lang: cap[2] ? cap[2].trim().replace(this.rules.inline.anyPunctuation, '$1') : cap[2],
- text,
- };
- }
- }
- heading(src) {
- const cap = this.rules.block.heading.exec(src);
- if (cap) {
- let text = cap[2].trim();
- // remove trailing #s
- if (/#$/.test(text)) {
- const trimmed = rtrim(text, '#');
- if (this.options.pedantic) {
- text = trimmed.trim();
- }
- else if (!trimmed || / $/.test(trimmed)) {
- // CommonMark requires space before trailing #s
- text = trimmed.trim();
- }
- }
- return {
- type: 'heading',
- raw: cap[0],
- depth: cap[1].length,
- text,
- tokens: this.lexer.inline(text),
- };
- }
- }
- hr(src) {
- const cap = this.rules.block.hr.exec(src);
- if (cap) {
- return {
- type: 'hr',
- raw: rtrim(cap[0], '\n'),
- };
- }
- }
- blockquote(src) {
- const cap = this.rules.block.blockquote.exec(src);
- if (cap) {
- let lines = rtrim(cap[0], '\n').split('\n');
- let raw = '';
- let text = '';
- const tokens = [];
- while (lines.length > 0) {
- let inBlockquote = false;
- const currentLines = [];
- let i;
- for (i = 0; i < lines.length; i++) {
- // get lines up to a continuation
- if (/^ {0,3}>/.test(lines[i])) {
- currentLines.push(lines[i]);
- inBlockquote = true;
- }
- else if (!inBlockquote) {
- currentLines.push(lines[i]);
- }
- else {
- break;
- }
- }
- lines = lines.slice(i);
- const currentRaw = currentLines.join('\n');
- const currentText = currentRaw
- // precede setext continuation with 4 spaces so it isn't a setext
- .replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g, '\n $1')
- .replace(/^ {0,3}>[ \t]?/gm, '');
- raw = raw ? `${raw}\n${currentRaw}` : currentRaw;
- text = text ? `${text}\n${currentText}` : currentText;
- // parse blockquote lines as top level tokens
- // merge paragraphs if this is a continuation
- const top = this.lexer.state.top;
- this.lexer.state.top = true;
- this.lexer.blockTokens(currentText, tokens, true);
- this.lexer.state.top = top;
- // if there is no continuation then we are done
- if (lines.length === 0) {
- break;
- }
- const lastToken = tokens[tokens.length - 1];
- if (lastToken?.type === 'code') {
- // blockquote continuation cannot be preceded by a code block
- break;
- }
- else if (lastToken?.type === 'blockquote') {
- // include continuation in nested blockquote
- const oldToken = lastToken;
- const newText = oldToken.raw + '\n' + lines.join('\n');
- const newToken = this.blockquote(newText);
- tokens[tokens.length - 1] = newToken;
- raw = raw.substring(0, raw.length - oldToken.raw.length) + newToken.raw;
- text = text.substring(0, text.length - oldToken.text.length) + newToken.text;
- break;
- }
- else if (lastToken?.type === 'list') {
- // include continuation in nested list
- const oldToken = lastToken;
- const newText = oldToken.raw + '\n' + lines.join('\n');
- const newToken = this.list(newText);
- tokens[tokens.length - 1] = newToken;
- raw = raw.substring(0, raw.length - lastToken.raw.length) + newToken.raw;
- text = text.substring(0, text.length - oldToken.raw.length) + newToken.raw;
- lines = newText.substring(tokens[tokens.length - 1].raw.length).split('\n');
- continue;
- }
- }
- return {
- type: 'blockquote',
- raw,
- tokens,
- text,
- };
- }
- }
- list(src) {
- let cap = this.rules.block.list.exec(src);
- if (cap) {
- let bull = cap[1].trim();
- const isordered = bull.length > 1;
- const list = {
- type: 'list',
- raw: '',
- ordered: isordered,
- start: isordered ? +bull.slice(0, -1) : '',
- loose: false,
- items: [],
- };
- bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`;
- if (this.options.pedantic) {
- bull = isordered ? bull : '[*+-]';
- }
- // Get next list item
- const itemRegex = new RegExp(`^( {0,3}${bull})((?:[\t ][^\\n]*)?(?:\\n|$))`);
- let endsWithBlankLine = false;
- // Check if current bullet point can start a new List Item
- while (src) {
- let endEarly = false;
- let raw = '';
- let itemContents = '';
- if (!(cap = itemRegex.exec(src))) {
- break;
- }
- if (this.rules.block.hr.test(src)) { // End list if bullet was actually HR (possibly move into itemRegex?)
- break;
- }
- raw = cap[0];
- src = src.substring(raw.length);
- let line = cap[2].split('\n', 1)[0].replace(/^\t+/, (t) => ' '.repeat(3 * t.length));
- let nextLine = src.split('\n', 1)[0];
- let blankLine = !line.trim();
- let indent = 0;
- if (this.options.pedantic) {
- indent = 2;
- itemContents = line.trimStart();
- }
- else if (blankLine) {
- indent = cap[1].length + 1;
- }
- else {
- indent = cap[2].search(/[^ ]/); // Find first non-space char
- indent = indent > 4 ? 1 : indent; // Treat indented code blocks (> 4 spaces) as having only 1 indent
- itemContents = line.slice(indent);
- indent += cap[1].length;
- }
- if (blankLine && /^ *$/.test(nextLine)) { // Items begin with at most one blank line
- raw += nextLine + '\n';
- src = src.substring(nextLine.length + 1);
- endEarly = true;
- }
- if (!endEarly) {
- const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`);
- const hrRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`);
- const fencesBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:\`\`\`|~~~)`);
- const headingBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}#`);
- // Check if following lines should be included in List Item
- while (src) {
- const rawLine = src.split('\n', 1)[0];
- nextLine = rawLine;
- // Re-align to follow commonmark nesting rules
- if (this.options.pedantic) {
- nextLine = nextLine.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' ');
- }
- // End list item if found code fences
- if (fencesBeginRegex.test(nextLine)) {
- break;
- }
- // End list item if found start of new heading
- if (headingBeginRegex.test(nextLine)) {
- break;
- }
- // End list item if found start of new bullet
- if (nextBulletRegex.test(nextLine)) {
- break;
- }
- // Horizontal rule found
- if (hrRegex.test(src)) {
- break;
- }
- if (nextLine.search(/[^ ]/) >= indent || !nextLine.trim()) { // Dedent if possible
- itemContents += '\n' + nextLine.slice(indent);
- }
- else {
- // not enough indentation
- if (blankLine) {
- break;
- }
- // paragraph continuation unless last line was a different block level element
- if (line.search(/[^ ]/) >= 4) { // indented code block
- break;
- }
- if (fencesBeginRegex.test(line)) {
- break;
- }
- if (headingBeginRegex.test(line)) {
- break;
- }
- if (hrRegex.test(line)) {
- break;
- }
- itemContents += '\n' + nextLine;
- }
- if (!blankLine && !nextLine.trim()) { // Check if current line is blank
- blankLine = true;
- }
- raw += rawLine + '\n';
- src = src.substring(rawLine.length + 1);
- line = nextLine.slice(indent);
- }
- }
- if (!list.loose) {
- // If the previous item ended with a blank line, the list is loose
- if (endsWithBlankLine) {
- list.loose = true;
- }
- else if (/\n *\n *$/.test(raw)) {
- endsWithBlankLine = true;
- }
- }
- let istask = null;
- let ischecked;
- // Check for task list items
- if (this.options.gfm) {
- istask = /^\[[ xX]\] /.exec(itemContents);
- if (istask) {
- ischecked = istask[0] !== '[ ] ';
- itemContents = itemContents.replace(/^\[[ xX]\] +/, '');
- }
- }
- list.items.push({
- type: 'list_item',
- raw,
- task: !!istask,
- checked: ischecked,
- loose: false,
- text: itemContents,
- tokens: [],
- });
- list.raw += raw;
- }
- // Do not consume newlines at end of final item. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic
- list.items[list.items.length - 1].raw = list.items[list.items.length - 1].raw.trimEnd();
- list.items[list.items.length - 1].text = list.items[list.items.length - 1].text.trimEnd();
- list.raw = list.raw.trimEnd();
- // Item child tokens handled here at end because we needed to have the final item to trim it first
- for (let i = 0; i < list.items.length; i++) {
- this.lexer.state.top = false;
- list.items[i].tokens = this.lexer.blockTokens(list.items[i].text, []);
- if (!list.loose) {
- // Check if list should be loose
- const spacers = list.items[i].tokens.filter(t => t.type === 'space');
- const hasMultipleLineBreaks = spacers.length > 0 && spacers.some(t => /\n.*\n/.test(t.raw));
- list.loose = hasMultipleLineBreaks;
- }
- }
- // Set all items to loose if list is loose
- if (list.loose) {
- for (let i = 0; i < list.items.length; i++) {
- list.items[i].loose = true;
- }
- }
- return list;
- }
- }
- html(src) {
- const cap = this.rules.block.html.exec(src);
- if (cap) {
- const token = {
- type: 'html',
- block: true,
- raw: cap[0],
- pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
- text: cap[0],
- };
- return token;
- }
- }
- def(src) {
- const cap = this.rules.block.def.exec(src);
- if (cap) {
- const tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
- const href = cap[2] ? cap[2].replace(/^<(.*)>$/, '$1').replace(this.rules.inline.anyPunctuation, '$1') : '';
- const title = cap[3] ? cap[3].substring(1, cap[3].length - 1).replace(this.rules.inline.anyPunctuation, '$1') : cap[3];
- return {
- type: 'def',
- tag,
- raw: cap[0],
- href,
- title,
- };
- }
- }
- table(src) {
- const cap = this.rules.block.table.exec(src);
- if (!cap) {
- return;
- }
- if (!/[:|]/.test(cap[2])) {
- // delimiter row must have a pipe (|) or colon (:) otherwise it is a setext heading
- return;
- }
- const headers = splitCells(cap[1]);
- const aligns = cap[2].replace(/^\||\| *$/g, '').split('|');
- const rows = cap[3] && cap[3].trim() ? cap[3].replace(/\n[ \t]*$/, '').split('\n') : [];
- const item = {
- type: 'table',
- raw: cap[0],
- header: [],
- align: [],
- rows: [],
- };
- if (headers.length !== aligns.length) {
- // header and align columns must be equal, rows can be different.
- return;
- }
- for (const align of aligns) {
- if (/^ *-+: *$/.test(align)) {
- item.align.push('right');
- }
- else if (/^ *:-+: *$/.test(align)) {
- item.align.push('center');
- }
- else if (/^ *:-+ *$/.test(align)) {
- item.align.push('left');
- }
- else {
- item.align.push(null);
- }
- }
- for (let i = 0; i < headers.length; i++) {
- item.header.push({
- text: headers[i],
- tokens: this.lexer.inline(headers[i]),
- header: true,
- align: item.align[i],
- });
- }
- for (const row of rows) {
- item.rows.push(splitCells(row, item.header.length).map((cell, i) => {
- return {
- text: cell,
- tokens: this.lexer.inline(cell),
- header: false,
- align: item.align[i],
- };
- }));
- }
- return item;
- }
- lheading(src) {
- const cap = this.rules.block.lheading.exec(src);
- if (cap) {
- return {
- type: 'heading',
- raw: cap[0],
- depth: cap[2].charAt(0) === '=' ? 1 : 2,
- text: cap[1],
- tokens: this.lexer.inline(cap[1]),
- };
- }
- }
- paragraph(src) {
- const cap = this.rules.block.paragraph.exec(src);
- if (cap) {
- const text = cap[1].charAt(cap[1].length - 1) === '\n'
- ? cap[1].slice(0, -1)
- : cap[1];
- return {
- type: 'paragraph',
- raw: cap[0],
- text,
- tokens: this.lexer.inline(text),
- };
- }
- }
- text(src) {
- const cap = this.rules.block.text.exec(src);
- if (cap) {
- return {
- type: 'text',
- raw: cap[0],
- text: cap[0],
- tokens: this.lexer.inline(cap[0]),
- };
- }
- }
- escape(src) {
- const cap = this.rules.inline.escape.exec(src);
- if (cap) {
- return {
- type: 'escape',
- raw: cap[0],
- text: escape$1(cap[1]),
- };
- }
- }
- tag(src) {
- const cap = this.rules.inline.tag.exec(src);
- if (cap) {
- if (!this.lexer.state.inLink && /^/i.test(cap[0])) {
- this.lexer.state.inLink = false;
- }
- if (!this.lexer.state.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
- this.lexer.state.inRawBlock = true;
- }
- else if (this.lexer.state.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
- this.lexer.state.inRawBlock = false;
- }
- return {
- type: 'html',
- raw: cap[0],
- inLink: this.lexer.state.inLink,
- inRawBlock: this.lexer.state.inRawBlock,
- block: false,
- text: cap[0],
- };
- }
- }
- link(src) {
- const cap = this.rules.inline.link.exec(src);
- if (cap) {
- const trimmedUrl = cap[2].trim();
- if (!this.options.pedantic && /^$/.test(trimmedUrl))) {
- return;
- }
- // ending angle bracket cannot be escaped
- const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\');
- if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
- return;
- }
- }
- else {
- // find closing parenthesis
- const lastParenIndex = findClosingBracket(cap[2], '()');
- if (lastParenIndex > -1) {
- const start = cap[0].indexOf('!') === 0 ? 5 : 4;
- const linkLen = start + cap[1].length + lastParenIndex;
- cap[2] = cap[2].substring(0, lastParenIndex);
- cap[0] = cap[0].substring(0, linkLen).trim();
- cap[3] = '';
- }
- }
- let href = cap[2];
- let title = '';
- if (this.options.pedantic) {
- // split pedantic href and title
- const link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
- if (link) {
- href = link[1];
- title = link[3];
- }
- }
- else {
- title = cap[3] ? cap[3].slice(1, -1) : '';
- }
- href = href.trim();
- if (/^$/.test(trimmedUrl))) {
- // pedantic allows starting angle bracket without ending angle bracket
- href = href.slice(1);
- }
- else {
- href = href.slice(1, -1);
- }
- }
- return outputLink(cap, {
- href: href ? href.replace(this.rules.inline.anyPunctuation, '$1') : href,
- title: title ? title.replace(this.rules.inline.anyPunctuation, '$1') : title,
- }, cap[0], this.lexer);
- }
- }
- reflink(src, links) {
- let cap;
- if ((cap = this.rules.inline.reflink.exec(src))
- || (cap = this.rules.inline.nolink.exec(src))) {
- const linkString = (cap[2] || cap[1]).replace(/\s+/g, ' ');
- const link = links[linkString.toLowerCase()];
- if (!link) {
- const text = cap[0].charAt(0);
- return {
- type: 'text',
- raw: text,
- text,
- };
- }
- return outputLink(cap, link, cap[0], this.lexer);
- }
- }
- emStrong(src, maskedSrc, prevChar = '') {
- let match = this.rules.inline.emStrongLDelim.exec(src);
- if (!match)
- return;
- // _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
- if (match[3] && prevChar.match(/[\p{L}\p{N}]/u))
- return;
- const nextChar = match[1] || match[2] || '';
- if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) {
- // unicode Regex counts emoji as 1 char; spread into array for proper count (used multiple times below)
- const lLength = [...match[0]].length - 1;
- let rDelim, rLength, delimTotal = lLength, midDelimTotal = 0;
- const endReg = match[0][0] === '*' ? this.rules.inline.emStrongRDelimAst : this.rules.inline.emStrongRDelimUnd;
- endReg.lastIndex = 0;
- // Clip maskedSrc to same section of string as src (move to lexer?)
- maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
- while ((match = endReg.exec(maskedSrc)) != null) {
- rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
- if (!rDelim)
- continue; // skip single * in __abc*abc__
- rLength = [...rDelim].length;
- if (match[3] || match[4]) { // found another Left Delim
- delimTotal += rLength;
- continue;
- }
- else if (match[5] || match[6]) { // either Left or Right Delim
- if (lLength % 3 && !((lLength + rLength) % 3)) {
- midDelimTotal += rLength;
- continue; // CommonMark Emphasis Rules 9-10
- }
- }
- delimTotal -= rLength;
- if (delimTotal > 0)
- continue; // Haven't found enough closing delimiters
- // Remove extra characters. *a*** -> *a*
- rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
- // char length can be >1 for unicode characters;
- const lastCharLength = [...match[0]][0].length;
- const raw = src.slice(0, lLength + match.index + lastCharLength + rLength);
- // Create `em` if smallest delimiter has odd char count. *a***
- if (Math.min(lLength, rLength) % 2) {
- const text = raw.slice(1, -1);
- return {
- type: 'em',
- raw,
- text,
- tokens: this.lexer.inlineTokens(text),
- };
- }
- // Create 'strong' if smallest delimiter has even char count. **a***
- const text = raw.slice(2, -2);
- return {
- type: 'strong',
- raw,
- text,
- tokens: this.lexer.inlineTokens(text),
- };
- }
- }
- }
- codespan(src) {
- const cap = this.rules.inline.code.exec(src);
- if (cap) {
- let text = cap[2].replace(/\n/g, ' ');
- const hasNonSpaceChars = /[^ ]/.test(text);
- const hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);
- if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
- text = text.substring(1, text.length - 1);
- }
- text = escape$1(text, true);
- return {
- type: 'codespan',
- raw: cap[0],
- text,
- };
- }
- }
- br(src) {
- const cap = this.rules.inline.br.exec(src);
- if (cap) {
- return {
- type: 'br',
- raw: cap[0],
- };
- }
- }
- del(src) {
- const cap = this.rules.inline.del.exec(src);
- if (cap) {
- return {
- type: 'del',
- raw: cap[0],
- text: cap[2],
- tokens: this.lexer.inlineTokens(cap[2]),
- };
- }
- }
- autolink(src) {
- const cap = this.rules.inline.autolink.exec(src);
- if (cap) {
- let text, href;
- if (cap[2] === '@') {
- text = escape$1(cap[1]);
- href = 'mailto:' + text;
- }
- else {
- text = escape$1(cap[1]);
- href = text;
- }
- return {
- type: 'link',
- raw: cap[0],
- text,
- href,
- tokens: [
- {
- type: 'text',
- raw: text,
- text,
- },
- ],
- };
- }
- }
- url(src) {
- let cap;
- if (cap = this.rules.inline.url.exec(src)) {
- let text, href;
- if (cap[2] === '@') {
- text = escape$1(cap[0]);
- href = 'mailto:' + text;
- }
- else {
- // do extended autolink path validation
- let prevCapZero;
- do {
- prevCapZero = cap[0];
- cap[0] = this.rules.inline._backpedal.exec(cap[0])?.[0] ?? '';
- } while (prevCapZero !== cap[0]);
- text = escape$1(cap[0]);
- if (cap[1] === 'www.') {
- href = 'http://' + cap[0];
- }
- else {
- href = cap[0];
- }
- }
- return {
- type: 'link',
- raw: cap[0],
- text,
- href,
- tokens: [
- {
- type: 'text',
- raw: text,
- text,
- },
- ],
- };
- }
- }
- inlineText(src) {
- const cap = this.rules.inline.text.exec(src);
- if (cap) {
- let text;
- if (this.lexer.state.inRawBlock) {
- text = cap[0];
- }
- else {
- text = escape$1(cap[0]);
- }
- return {
- type: 'text',
- raw: cap[0],
- text,
- };
- }
- }
-}
-
-/**
- * Block-Level Grammar
- */
-const newline = /^(?: *(?:\n|$))+/;
-const blockCode = /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/;
-const fences = /^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/;
-const hr = /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/;
-const heading = /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/;
-const bullet = /(?:[*+-]|\d{1,9}[.)])/;
-const lheading = edit(/^(?!bull |blockCode|fences|blockquote|heading|html)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html))+?)\n {0,3}(=+|-+) *(?:\n+|$)/)
- .replace(/bull/g, bullet) // lists can interrupt
- .replace(/blockCode/g, / {4}/) // indented code blocks can interrupt
- .replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/) // fenced code blocks can interrupt
- .replace(/blockquote/g, / {0,3}>/) // blockquote can interrupt
- .replace(/heading/g, / {0,3}#{1,6}/) // ATX heading can interrupt
- .replace(/html/g, / {0,3}<[^\n>]+>\n/) // block html can interrupt
- .getRegex();
-const _paragraph = /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/;
-const blockText = /^[^\n]+/;
-const _blockLabel = /(?!\s*\])(?:\\.|[^\[\]\\])+/;
-const def = edit(/^ {0,3}\[(label)\]: *(?:\n *)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/)
- .replace('label', _blockLabel)
- .replace('title', /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/)
- .getRegex();
-const list = edit(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/)
- .replace(/bull/g, bullet)
- .getRegex();
-const _tag = 'address|article|aside|base|basefont|blockquote|body|caption'
- + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
- + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
- + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
- + '|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title'
- + '|tr|track|ul';
-const _comment = /|$))/;
-const html = edit('^ {0,3}(?:' // optional indentation
- + '<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:\\1>[^\\n]*\\n+|$)' // (1)
- + '|comment[^\\n]*(\\n+|$)' // (2)
- + '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3)
- + '|\\n*|$)' // (4)
- + '|\\n*|$)' // (5)
- + '|?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (6)
- + '|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) open tag
- + '|(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag
- + ')', 'i')
- .replace('comment', _comment)
- .replace('tag', _tag)
- .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
- .getRegex();
-const paragraph = edit(_paragraph)
- .replace('hr', hr)
- .replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
- .replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs
- .replace('|table', '')
- .replace('blockquote', ' {0,3}>')
- .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
- .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
- .replace('html', '?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
- .replace('tag', _tag) // pars can be interrupted by type (6) html blocks
- .getRegex();
-const blockquote = edit(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/)
- .replace('paragraph', paragraph)
- .getRegex();
-/**
- * Normal Block Grammar
- */
-const blockNormal = {
- blockquote,
- code: blockCode,
- def,
- fences,
- heading,
- hr,
- html,
- lheading,
- list,
- newline,
- paragraph,
- table: noopTest,
- text: blockText,
-};
-/**
- * GFM Block Grammar
- */
-const gfmTable = edit('^ *([^\\n ].*)\\n' // Header
- + ' {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)' // Align
- + '(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)') // Cells
- .replace('hr', hr)
- .replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
- .replace('blockquote', ' {0,3}>')
- .replace('code', ' {4}[^\\n]')
- .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
- .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
- .replace('html', '?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
- .replace('tag', _tag) // tables can be interrupted by type (6) html blocks
- .getRegex();
-const blockGfm = {
- ...blockNormal,
- table: gfmTable,
- paragraph: edit(_paragraph)
- .replace('hr', hr)
- .replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
- .replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs
- .replace('table', gfmTable) // interrupt paragraphs with table
- .replace('blockquote', ' {0,3}>')
- .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
- .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
- .replace('html', '?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
- .replace('tag', _tag) // pars can be interrupted by type (6) html blocks
- .getRegex(),
-};
-/**
- * Pedantic grammar (original John Gruber's loose markdown specification)
- */
-const blockPedantic = {
- ...blockNormal,
- html: edit('^ *(?:comment *(?:\\n|\\s*$)'
- + '|<(tag)[\\s\\S]+?\\1> *(?:\\n{2,}|\\s*$)' // closed tag
- + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
- .replace('comment', _comment)
- .replace(/tag/g, '(?!(?:'
- + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
- + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
- + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
- .getRegex(),
- def: /^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
- heading: /^(#{1,6})(.*)(?:\n+|$)/,
- fences: noopTest, // fences not supported
- lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,
- paragraph: edit(_paragraph)
- .replace('hr', hr)
- .replace('heading', ' *#{1,6} *[^\n]')
- .replace('lheading', lheading)
- .replace('|table', '')
- .replace('blockquote', ' {0,3}>')
- .replace('|fences', '')
- .replace('|list', '')
- .replace('|html', '')
- .replace('|tag', '')
- .getRegex(),
-};
-/**
- * Inline-Level Grammar
- */
-const escape = /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/;
-const inlineCode = /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/;
-const br = /^( {2,}|\\)\n(?!\s*$)/;
-const inlineText = /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\
-const blockSkip = /\[[^[\]]*?\]\([^\(\)]*?\)|`[^`]*?`|<[^<>]*?>/g;
-const emStrongLDelim = edit(/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/, 'u')
- .replace(/punct/g, _punctuation)
- .getRegex();
-const emStrongRDelimAst = edit('^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)' // Skip orphan inside strong
- + '|[^*]+(?=[^*])' // Consume to delim
- + '|(?!\\*)[punct](\\*+)(?=[\\s]|$)' // (1) #*** can only be a Right Delimiter
- + '|[^punct\\s](\\*+)(?!\\*)(?=[punct\\s]|$)' // (2) a***#, a*** can only be a Right Delimiter
- + '|(?!\\*)[punct\\s](\\*+)(?=[^punct\\s])' // (3) #***a, ***a can only be Left Delimiter
- + '|[\\s](\\*+)(?!\\*)(?=[punct])' // (4) ***# can only be Left Delimiter
- + '|(?!\\*)[punct](\\*+)(?!\\*)(?=[punct])' // (5) #***# can be either Left or Right Delimiter
- + '|[^punct\\s](\\*+)(?=[^punct\\s])', 'gu') // (6) a***a can be either Left or Right Delimiter
- .replace(/punct/g, _punctuation)
- .getRegex();
-// (6) Not allowed for _
-const emStrongRDelimUnd = edit('^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)' // Skip orphan inside strong
- + '|[^_]+(?=[^_])' // Consume to delim
- + '|(?!_)[punct](_+)(?=[\\s]|$)' // (1) #___ can only be a Right Delimiter
- + '|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)' // (2) a___#, a___ can only be a Right Delimiter
- + '|(?!_)[punct\\s](_+)(?=[^punct\\s])' // (3) #___a, ___a can only be Left Delimiter
- + '|[\\s](_+)(?!_)(?=[punct])' // (4) ___# can only be Left Delimiter
- + '|(?!_)[punct](_+)(?!_)(?=[punct])', 'gu') // (5) #___# can be either Left or Right Delimiter
- .replace(/punct/g, _punctuation)
- .getRegex();
-const anyPunctuation = edit(/\\([punct])/, 'gu')
- .replace(/punct/g, _punctuation)
- .getRegex();
-const autolink = edit(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/)
- .replace('scheme', /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/)
- .replace('email', /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/)
- .getRegex();
-const _inlineComment = edit(_comment).replace('(?:-->|$)', '-->').getRegex();
-const tag = edit('^comment'
- + '|^[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
- + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
- + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g.
- + '|^' // declaration, e.g.
- + '|^') // CDATA section
- .replace('comment', _inlineComment)
- .replace('attribute', /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/)
- .getRegex();
-const _inlineLabel = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
-const link = edit(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/)
- .replace('label', _inlineLabel)
- .replace('href', /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/)
- .replace('title', /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/)
- .getRegex();
-const reflink = edit(/^!?\[(label)\]\[(ref)\]/)
- .replace('label', _inlineLabel)
- .replace('ref', _blockLabel)
- .getRegex();
-const nolink = edit(/^!?\[(ref)\](?:\[\])?/)
- .replace('ref', _blockLabel)
- .getRegex();
-const reflinkSearch = edit('reflink|nolink(?!\\()', 'g')
- .replace('reflink', reflink)
- .replace('nolink', nolink)
- .getRegex();
-/**
- * Normal Inline Grammar
- */
-const inlineNormal = {
- _backpedal: noopTest, // only used for GFM url
- anyPunctuation,
- autolink,
- blockSkip,
- br,
- code: inlineCode,
- del: noopTest,
- emStrongLDelim,
- emStrongRDelimAst,
- emStrongRDelimUnd,
- escape,
- link,
- nolink,
- punctuation,
- reflink,
- reflinkSearch,
- tag,
- text: inlineText,
- url: noopTest,
-};
-/**
- * Pedantic Inline Grammar
- */
-const inlinePedantic = {
- ...inlineNormal,
- link: edit(/^!?\[(label)\]\((.*?)\)/)
- .replace('label', _inlineLabel)
- .getRegex(),
- reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
- .replace('label', _inlineLabel)
- .getRegex(),
-};
-/**
- * GFM Inline Grammar
- */
-const inlineGfm = {
- ...inlineNormal,
- escape: edit(escape).replace('])', '~|])').getRegex(),
- url: edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, 'i')
- .replace('email', /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/)
- .getRegex(),
- _backpedal: /(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,
- del: /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,
- text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\ {
- return leading + ' '.repeat(tabs.length);
- });
- }
- let token;
- let lastToken;
- let cutSrc;
- while (src) {
- if (this.options.extensions
- && this.options.extensions.block
- && this.options.extensions.block.some((extTokenizer) => {
- if (token = extTokenizer.call({ lexer: this }, src, tokens)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- return true;
- }
- return false;
- })) {
- continue;
- }
- // newline
- if (token = this.tokenizer.space(src)) {
- src = src.substring(token.raw.length);
- if (token.raw.length === 1 && tokens.length > 0) {
- // if there's a single \n as a spacer, it's terminating the last line,
- // so move it there so that we don't get unnecessary paragraph tags
- tokens[tokens.length - 1].raw += '\n';
- }
- else {
- tokens.push(token);
- }
- continue;
- }
- // code
- if (token = this.tokenizer.code(src)) {
- src = src.substring(token.raw.length);
- lastToken = tokens[tokens.length - 1];
- // An indented code block cannot interrupt a paragraph.
- if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) {
- lastToken.raw += '\n' + token.raw;
- lastToken.text += '\n' + token.text;
- this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
- }
- else {
- tokens.push(token);
- }
- continue;
- }
- // fences
- if (token = this.tokenizer.fences(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // heading
- if (token = this.tokenizer.heading(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // hr
- if (token = this.tokenizer.hr(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // blockquote
- if (token = this.tokenizer.blockquote(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // list
- if (token = this.tokenizer.list(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // html
- if (token = this.tokenizer.html(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // def
- if (token = this.tokenizer.def(src)) {
- src = src.substring(token.raw.length);
- lastToken = tokens[tokens.length - 1];
- if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) {
- lastToken.raw += '\n' + token.raw;
- lastToken.text += '\n' + token.raw;
- this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
- }
- else if (!this.tokens.links[token.tag]) {
- this.tokens.links[token.tag] = {
- href: token.href,
- title: token.title,
- };
- }
- continue;
- }
- // table (gfm)
- if (token = this.tokenizer.table(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // lheading
- if (token = this.tokenizer.lheading(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // top-level paragraph
- // prevent paragraph consuming extensions by clipping 'src' to extension start
- cutSrc = src;
- if (this.options.extensions && this.options.extensions.startBlock) {
- let startIndex = Infinity;
- const tempSrc = src.slice(1);
- let tempStart;
- this.options.extensions.startBlock.forEach((getStartIndex) => {
- tempStart = getStartIndex.call({ lexer: this }, tempSrc);
- if (typeof tempStart === 'number' && tempStart >= 0) {
- startIndex = Math.min(startIndex, tempStart);
- }
- });
- if (startIndex < Infinity && startIndex >= 0) {
- cutSrc = src.substring(0, startIndex + 1);
- }
- }
- if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) {
- lastToken = tokens[tokens.length - 1];
- if (lastParagraphClipped && lastToken?.type === 'paragraph') {
- lastToken.raw += '\n' + token.raw;
- lastToken.text += '\n' + token.text;
- this.inlineQueue.pop();
- this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
- }
- else {
- tokens.push(token);
- }
- lastParagraphClipped = (cutSrc.length !== src.length);
- src = src.substring(token.raw.length);
- continue;
- }
- // text
- if (token = this.tokenizer.text(src)) {
- src = src.substring(token.raw.length);
- lastToken = tokens[tokens.length - 1];
- if (lastToken && lastToken.type === 'text') {
- lastToken.raw += '\n' + token.raw;
- lastToken.text += '\n' + token.text;
- this.inlineQueue.pop();
- this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
- }
- else {
- tokens.push(token);
- }
- continue;
- }
- if (src) {
- const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
- if (this.options.silent) {
- console.error(errMsg);
- break;
- }
- else {
- throw new Error(errMsg);
- }
- }
- }
- this.state.top = true;
- return tokens;
- }
- inline(src, tokens = []) {
- this.inlineQueue.push({ src, tokens });
- return tokens;
- }
- /**
- * Lexing/Compiling
- */
- inlineTokens(src, tokens = []) {
- let token, lastToken, cutSrc;
- // String with links masked to avoid interference with em and strong
- let maskedSrc = src;
- let match;
- let keepPrevChar, prevChar;
- // Mask out reflinks
- if (this.tokens.links) {
- const links = Object.keys(this.tokens.links);
- if (links.length > 0) {
- while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
- if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {
- maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
- }
- }
- }
- }
- // Mask out other blocks
- while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {
- maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
- }
- // Mask out escaped characters
- while ((match = this.tokenizer.rules.inline.anyPunctuation.exec(maskedSrc)) != null) {
- maskedSrc = maskedSrc.slice(0, match.index) + '++' + maskedSrc.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);
- }
- while (src) {
- if (!keepPrevChar) {
- prevChar = '';
- }
- keepPrevChar = false;
- // extensions
- if (this.options.extensions
- && this.options.extensions.inline
- && this.options.extensions.inline.some((extTokenizer) => {
- if (token = extTokenizer.call({ lexer: this }, src, tokens)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- return true;
- }
- return false;
- })) {
- continue;
- }
- // escape
- if (token = this.tokenizer.escape(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // tag
- if (token = this.tokenizer.tag(src)) {
- src = src.substring(token.raw.length);
- lastToken = tokens[tokens.length - 1];
- if (lastToken && token.type === 'text' && lastToken.type === 'text') {
- lastToken.raw += token.raw;
- lastToken.text += token.text;
- }
- else {
- tokens.push(token);
- }
- continue;
- }
- // link
- if (token = this.tokenizer.link(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // reflink, nolink
- if (token = this.tokenizer.reflink(src, this.tokens.links)) {
- src = src.substring(token.raw.length);
- lastToken = tokens[tokens.length - 1];
- if (lastToken && token.type === 'text' && lastToken.type === 'text') {
- lastToken.raw += token.raw;
- lastToken.text += token.text;
- }
- else {
- tokens.push(token);
- }
- continue;
- }
- // em & strong
- if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // code
- if (token = this.tokenizer.codespan(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // br
- if (token = this.tokenizer.br(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // del (gfm)
- if (token = this.tokenizer.del(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // autolink
- if (token = this.tokenizer.autolink(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // url (gfm)
- if (!this.state.inLink && (token = this.tokenizer.url(src))) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // text
- // prevent inlineText consuming extensions by clipping 'src' to extension start
- cutSrc = src;
- if (this.options.extensions && this.options.extensions.startInline) {
- let startIndex = Infinity;
- const tempSrc = src.slice(1);
- let tempStart;
- this.options.extensions.startInline.forEach((getStartIndex) => {
- tempStart = getStartIndex.call({ lexer: this }, tempSrc);
- if (typeof tempStart === 'number' && tempStart >= 0) {
- startIndex = Math.min(startIndex, tempStart);
- }
- });
- if (startIndex < Infinity && startIndex >= 0) {
- cutSrc = src.substring(0, startIndex + 1);
- }
- }
- if (token = this.tokenizer.inlineText(cutSrc)) {
- src = src.substring(token.raw.length);
- if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started
- prevChar = token.raw.slice(-1);
- }
- keepPrevChar = true;
- lastToken = tokens[tokens.length - 1];
- if (lastToken && lastToken.type === 'text') {
- lastToken.raw += token.raw;
- lastToken.text += token.text;
- }
- else {
- tokens.push(token);
- }
- continue;
- }
- if (src) {
- const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
- if (this.options.silent) {
- console.error(errMsg);
- break;
- }
- else {
- throw new Error(errMsg);
- }
- }
- }
- return tokens;
- }
-}
-
-/**
- * Renderer
- */
-class _Renderer {
- options;
- parser; // set by the parser
- constructor(options) {
- this.options = options || _defaults;
- }
- space(token) {
- return '';
- }
- code({ text, lang, escaped }) {
- const langString = (lang || '').match(/^\S*/)?.[0];
- const code = text.replace(/\n$/, '') + '\n';
- if (!langString) {
- return ''
- + (escaped ? code : escape$1(code, true))
- + '
\n';
- }
- return ''
- + (escaped ? code : escape$1(code, true))
- + '
\n';
- }
- blockquote({ tokens }) {
- const body = this.parser.parse(tokens);
- return `\n${body}
\n`;
- }
- html({ text }) {
- return text;
- }
- heading({ tokens, depth }) {
- return `${this.parser.parseInline(tokens)}\n`;
- }
- hr(token) {
- return '
\n';
- }
- list(token) {
- const ordered = token.ordered;
- const start = token.start;
- let body = '';
- for (let j = 0; j < token.items.length; j++) {
- const item = token.items[j];
- body += this.listitem(item);
- }
- const type = ordered ? 'ol' : 'ul';
- const startAttr = (ordered && start !== 1) ? (' start="' + start + '"') : '';
- return '<' + type + startAttr + '>\n' + body + '' + type + '>\n';
- }
- listitem(item) {
- let itemBody = '';
- if (item.task) {
- const checkbox = this.checkbox({ checked: !!item.checked });
- if (item.loose) {
- if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') {
- item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
- if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
- item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
- }
- }
- else {
- item.tokens.unshift({
- type: 'text',
- raw: checkbox + ' ',
- text: checkbox + ' ',
- });
- }
- }
- else {
- itemBody += checkbox + ' ';
- }
- }
- itemBody += this.parser.parse(item.tokens, !!item.loose);
- return `${itemBody}\n`;
- }
- checkbox({ checked }) {
- return '';
- }
- paragraph({ tokens }) {
- return `${this.parser.parseInline(tokens)}
\n`;
- }
- table(token) {
- let header = '';
- // header
- let cell = '';
- for (let j = 0; j < token.header.length; j++) {
- cell += this.tablecell(token.header[j]);
- }
- header += this.tablerow({ text: cell });
- let body = '';
- for (let j = 0; j < token.rows.length; j++) {
- const row = token.rows[j];
- cell = '';
- for (let k = 0; k < row.length; k++) {
- cell += this.tablecell(row[k]);
- }
- body += this.tablerow({ text: cell });
- }
- if (body)
- body = `${body}`;
- return '\n'
- + '\n'
- + header
- + '\n'
- + body
- + '
\n';
- }
- tablerow({ text }) {
- return `\n${text}
\n`;
- }
- tablecell(token) {
- const content = this.parser.parseInline(token.tokens);
- const type = token.header ? 'th' : 'td';
- const tag = token.align
- ? `<${type} align="${token.align}">`
- : `<${type}>`;
- return tag + content + `${type}>\n`;
- }
- /**
- * span level renderer
- */
- strong({ tokens }) {
- return `${this.parser.parseInline(tokens)}`;
- }
- em({ tokens }) {
- return `${this.parser.parseInline(tokens)}`;
- }
- codespan({ text }) {
- return `${text}`;
- }
- br(token) {
- return '
';
- }
- del({ tokens }) {
- return `${this.parser.parseInline(tokens)}`;
- }
- link({ href, title, tokens }) {
- const text = this.parser.parseInline(tokens);
- const cleanHref = cleanUrl(href);
- if (cleanHref === null) {
- return text;
- }
- href = cleanHref;
- let out = '' + text + '';
- return out;
- }
- image({ href, title, text }) {
- const cleanHref = cleanUrl(href);
- if (cleanHref === null) {
- return text;
- }
- href = cleanHref;
- let out = `
';
- return out;
- }
- text(token) {
- return 'tokens' in token && token.tokens ? this.parser.parseInline(token.tokens) : token.text;
- }
-}
-
-/**
- * TextRenderer
- * returns only the textual part of the token
- */
-class _TextRenderer {
- // no need for block level renderers
- strong({ text }) {
- return text;
- }
- em({ text }) {
- return text;
- }
- codespan({ text }) {
- return text;
- }
- del({ text }) {
- return text;
- }
- html({ text }) {
- return text;
- }
- text({ text }) {
- return text;
- }
- link({ text }) {
- return '' + text;
- }
- image({ text }) {
- return '' + text;
- }
- br() {
- return '';
- }
-}
-
-/**
- * Parsing & Compiling
- */
-class _Parser {
- options;
- renderer;
- textRenderer;
- constructor(options) {
- this.options = options || _defaults;
- this.options.renderer = this.options.renderer || new _Renderer();
- this.renderer = this.options.renderer;
- this.renderer.options = this.options;
- this.renderer.parser = this;
- this.textRenderer = new _TextRenderer();
- }
- /**
- * Static Parse Method
- */
- static parse(tokens, options) {
- const parser = new _Parser(options);
- return parser.parse(tokens);
- }
- /**
- * Static Parse Inline Method
- */
- static parseInline(tokens, options) {
- const parser = new _Parser(options);
- return parser.parseInline(tokens);
- }
- /**
- * Parse Loop
- */
- parse(tokens, top = true) {
- let out = '';
- for (let i = 0; i < tokens.length; i++) {
- const anyToken = tokens[i];
- // Run any renderer extensions
- if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[anyToken.type]) {
- const genericToken = anyToken;
- const ret = this.options.extensions.renderers[genericToken.type].call({ parser: this }, genericToken);
- if (ret !== false || !['space', 'hr', 'heading', 'code', 'table', 'blockquote', 'list', 'html', 'paragraph', 'text'].includes(genericToken.type)) {
- out += ret || '';
- continue;
- }
- }
- const token = anyToken;
- switch (token.type) {
- case 'space': {
- out += this.renderer.space(token);
- continue;
- }
- case 'hr': {
- out += this.renderer.hr(token);
- continue;
- }
- case 'heading': {
- out += this.renderer.heading(token);
- continue;
- }
- case 'code': {
- out += this.renderer.code(token);
- continue;
- }
- case 'table': {
- out += this.renderer.table(token);
- continue;
- }
- case 'blockquote': {
- out += this.renderer.blockquote(token);
- continue;
- }
- case 'list': {
- out += this.renderer.list(token);
- continue;
- }
- case 'html': {
- out += this.renderer.html(token);
- continue;
- }
- case 'paragraph': {
- out += this.renderer.paragraph(token);
- continue;
- }
- case 'text': {
- let textToken = token;
- let body = this.renderer.text(textToken);
- while (i + 1 < tokens.length && tokens[i + 1].type === 'text') {
- textToken = tokens[++i];
- body += '\n' + this.renderer.text(textToken);
- }
- if (top) {
- out += this.renderer.paragraph({
- type: 'paragraph',
- raw: body,
- text: body,
- tokens: [{ type: 'text', raw: body, text: body }],
- });
- }
- else {
- out += body;
- }
- continue;
- }
- default: {
- const errMsg = 'Token with "' + token.type + '" type was not found.';
- if (this.options.silent) {
- console.error(errMsg);
- return '';
- }
- else {
- throw new Error(errMsg);
- }
- }
- }
- }
- return out;
- }
- /**
- * Parse Inline Tokens
- */
- parseInline(tokens, renderer) {
- renderer = renderer || this.renderer;
- let out = '';
- for (let i = 0; i < tokens.length; i++) {
- const anyToken = tokens[i];
- // Run any renderer extensions
- if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[anyToken.type]) {
- const ret = this.options.extensions.renderers[anyToken.type].call({ parser: this }, anyToken);
- if (ret !== false || !['escape', 'html', 'link', 'image', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(anyToken.type)) {
- out += ret || '';
- continue;
- }
- }
- const token = anyToken;
- switch (token.type) {
- case 'escape': {
- out += renderer.text(token);
- break;
- }
- case 'html': {
- out += renderer.html(token);
- break;
- }
- case 'link': {
- out += renderer.link(token);
- break;
- }
- case 'image': {
- out += renderer.image(token);
- break;
- }
- case 'strong': {
- out += renderer.strong(token);
- break;
- }
- case 'em': {
- out += renderer.em(token);
- break;
- }
- case 'codespan': {
- out += renderer.codespan(token);
- break;
- }
- case 'br': {
- out += renderer.br(token);
- break;
- }
- case 'del': {
- out += renderer.del(token);
- break;
- }
- case 'text': {
- out += renderer.text(token);
- break;
- }
- default: {
- const errMsg = 'Token with "' + token.type + '" type was not found.';
- if (this.options.silent) {
- console.error(errMsg);
- return '';
- }
- else {
- throw new Error(errMsg);
- }
- }
- }
- }
- return out;
- }
-}
-
-class _Hooks {
- options;
- block;
- constructor(options) {
- this.options = options || _defaults;
- }
- static passThroughHooks = new Set([
- 'preprocess',
- 'postprocess',
- 'processAllTokens',
- ]);
- /**
- * Process markdown before marked
- */
- preprocess(markdown) {
- return markdown;
- }
- /**
- * Process HTML after marked is finished
- */
- postprocess(html) {
- return html;
- }
- /**
- * Process all tokens before walk tokens
- */
- processAllTokens(tokens) {
- return tokens;
- }
- /**
- * Provide function to tokenize markdown
- */
- provideLexer() {
- return this.block ? _Lexer.lex : _Lexer.lexInline;
- }
- /**
- * Provide function to parse tokens
- */
- provideParser() {
- return this.block ? _Parser.parse : _Parser.parseInline;
- }
-}
-
-class Marked {
- defaults = _getDefaults();
- options = this.setOptions;
- parse = this.parseMarkdown(true);
- parseInline = this.parseMarkdown(false);
- Parser = _Parser;
- Renderer = _Renderer;
- TextRenderer = _TextRenderer;
- Lexer = _Lexer;
- Tokenizer = _Tokenizer;
- Hooks = _Hooks;
- constructor(...args) {
- this.use(...args);
- }
- /**
- * Run callback for every token
- */
- walkTokens(tokens, callback) {
- let values = [];
- for (const token of tokens) {
- values = values.concat(callback.call(this, token));
- switch (token.type) {
- case 'table': {
- const tableToken = token;
- for (const cell of tableToken.header) {
- values = values.concat(this.walkTokens(cell.tokens, callback));
- }
- for (const row of tableToken.rows) {
- for (const cell of row) {
- values = values.concat(this.walkTokens(cell.tokens, callback));
- }
- }
- break;
- }
- case 'list': {
- const listToken = token;
- values = values.concat(this.walkTokens(listToken.items, callback));
- break;
- }
- default: {
- const genericToken = token;
- if (this.defaults.extensions?.childTokens?.[genericToken.type]) {
- this.defaults.extensions.childTokens[genericToken.type].forEach((childTokens) => {
- const tokens = genericToken[childTokens].flat(Infinity);
- values = values.concat(this.walkTokens(tokens, callback));
- });
- }
- else if (genericToken.tokens) {
- values = values.concat(this.walkTokens(genericToken.tokens, callback));
- }
- }
- }
- }
- return values;
- }
- use(...args) {
- const extensions = this.defaults.extensions || { renderers: {}, childTokens: {} };
- args.forEach((pack) => {
- // copy options to new object
- const opts = { ...pack };
- // set async to true if it was set to true before
- opts.async = this.defaults.async || opts.async || false;
- // ==-- Parse "addon" extensions --== //
- if (pack.extensions) {
- pack.extensions.forEach((ext) => {
- if (!ext.name) {
- throw new Error('extension name required');
- }
- if ('renderer' in ext) { // Renderer extensions
- const prevRenderer = extensions.renderers[ext.name];
- if (prevRenderer) {
- // Replace extension with func to run new extension but fall back if false
- extensions.renderers[ext.name] = function (...args) {
- let ret = ext.renderer.apply(this, args);
- if (ret === false) {
- ret = prevRenderer.apply(this, args);
- }
- return ret;
- };
- }
- else {
- extensions.renderers[ext.name] = ext.renderer;
- }
- }
- if ('tokenizer' in ext) { // Tokenizer Extensions
- if (!ext.level || (ext.level !== 'block' && ext.level !== 'inline')) {
- throw new Error("extension level must be 'block' or 'inline'");
- }
- const extLevel = extensions[ext.level];
- if (extLevel) {
- extLevel.unshift(ext.tokenizer);
- }
- else {
- extensions[ext.level] = [ext.tokenizer];
- }
- if (ext.start) { // Function to check for start of token
- if (ext.level === 'block') {
- if (extensions.startBlock) {
- extensions.startBlock.push(ext.start);
- }
- else {
- extensions.startBlock = [ext.start];
- }
- }
- else if (ext.level === 'inline') {
- if (extensions.startInline) {
- extensions.startInline.push(ext.start);
- }
- else {
- extensions.startInline = [ext.start];
- }
- }
- }
- }
- if ('childTokens' in ext && ext.childTokens) { // Child tokens to be visited by walkTokens
- extensions.childTokens[ext.name] = ext.childTokens;
- }
- });
- opts.extensions = extensions;
- }
- // ==-- Parse "overwrite" extensions --== //
- if (pack.renderer) {
- const renderer = this.defaults.renderer || new _Renderer(this.defaults);
- for (const prop in pack.renderer) {
- if (!(prop in renderer)) {
- throw new Error(`renderer '${prop}' does not exist`);
- }
- if (['options', 'parser'].includes(prop)) {
- // ignore options property
- continue;
- }
- const rendererProp = prop;
- const rendererFunc = pack.renderer[rendererProp];
- const prevRenderer = renderer[rendererProp];
- // Replace renderer with func to run extension, but fall back if false
- renderer[rendererProp] = (...args) => {
- let ret = rendererFunc.apply(renderer, args);
- if (ret === false) {
- ret = prevRenderer.apply(renderer, args);
- }
- return ret || '';
- };
- }
- opts.renderer = renderer;
- }
- if (pack.tokenizer) {
- const tokenizer = this.defaults.tokenizer || new _Tokenizer(this.defaults);
- for (const prop in pack.tokenizer) {
- if (!(prop in tokenizer)) {
- throw new Error(`tokenizer '${prop}' does not exist`);
- }
- if (['options', 'rules', 'lexer'].includes(prop)) {
- // ignore options, rules, and lexer properties
- continue;
- }
- const tokenizerProp = prop;
- const tokenizerFunc = pack.tokenizer[tokenizerProp];
- const prevTokenizer = tokenizer[tokenizerProp];
- // Replace tokenizer with func to run extension, but fall back if false
- // @ts-expect-error cannot type tokenizer function dynamically
- tokenizer[tokenizerProp] = (...args) => {
- let ret = tokenizerFunc.apply(tokenizer, args);
- if (ret === false) {
- ret = prevTokenizer.apply(tokenizer, args);
- }
- return ret;
- };
- }
- opts.tokenizer = tokenizer;
- }
- // ==-- Parse Hooks extensions --== //
- if (pack.hooks) {
- const hooks = this.defaults.hooks || new _Hooks();
- for (const prop in pack.hooks) {
- if (!(prop in hooks)) {
- throw new Error(`hook '${prop}' does not exist`);
- }
- if (['options', 'block'].includes(prop)) {
- // ignore options and block properties
- continue;
- }
- const hooksProp = prop;
- const hooksFunc = pack.hooks[hooksProp];
- const prevHook = hooks[hooksProp];
- if (_Hooks.passThroughHooks.has(prop)) {
- // @ts-expect-error cannot type hook function dynamically
- hooks[hooksProp] = (arg) => {
- if (this.defaults.async) {
- return Promise.resolve(hooksFunc.call(hooks, arg)).then(ret => {
- return prevHook.call(hooks, ret);
- });
- }
- const ret = hooksFunc.call(hooks, arg);
- return prevHook.call(hooks, ret);
- };
- }
- else {
- // @ts-expect-error cannot type hook function dynamically
- hooks[hooksProp] = (...args) => {
- let ret = hooksFunc.apply(hooks, args);
- if (ret === false) {
- ret = prevHook.apply(hooks, args);
- }
- return ret;
- };
- }
- }
- opts.hooks = hooks;
- }
- // ==-- Parse WalkTokens extensions --== //
- if (pack.walkTokens) {
- const walkTokens = this.defaults.walkTokens;
- const packWalktokens = pack.walkTokens;
- opts.walkTokens = function (token) {
- let values = [];
- values.push(packWalktokens.call(this, token));
- if (walkTokens) {
- values = values.concat(walkTokens.call(this, token));
- }
- return values;
- };
- }
- this.defaults = { ...this.defaults, ...opts };
- });
- return this;
- }
- setOptions(opt) {
- this.defaults = { ...this.defaults, ...opt };
- return this;
- }
- lexer(src, options) {
- return _Lexer.lex(src, options ?? this.defaults);
- }
- parser(tokens, options) {
- return _Parser.parse(tokens, options ?? this.defaults);
- }
- parseMarkdown(blockType) {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const parse = (src, options) => {
- const origOpt = { ...options };
- const opt = { ...this.defaults, ...origOpt };
- const throwError = this.onError(!!opt.silent, !!opt.async);
- // throw error if an extension set async to true but parse was called with async: false
- if (this.defaults.async === true && origOpt.async === false) {
- return throwError(new Error('marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise.'));
- }
- // throw error in case of non string input
- if (typeof src === 'undefined' || src === null) {
- return throwError(new Error('marked(): input parameter is undefined or null'));
- }
- if (typeof src !== 'string') {
- return throwError(new Error('marked(): input parameter is of type '
- + Object.prototype.toString.call(src) + ', string expected'));
- }
- if (opt.hooks) {
- opt.hooks.options = opt;
- opt.hooks.block = blockType;
- }
- const lexer = opt.hooks ? opt.hooks.provideLexer() : (blockType ? _Lexer.lex : _Lexer.lexInline);
- const parser = opt.hooks ? opt.hooks.provideParser() : (blockType ? _Parser.parse : _Parser.parseInline);
- if (opt.async) {
- return Promise.resolve(opt.hooks ? opt.hooks.preprocess(src) : src)
- .then(src => lexer(src, opt))
- .then(tokens => opt.hooks ? opt.hooks.processAllTokens(tokens) : tokens)
- .then(tokens => opt.walkTokens ? Promise.all(this.walkTokens(tokens, opt.walkTokens)).then(() => tokens) : tokens)
- .then(tokens => parser(tokens, opt))
- .then(html => opt.hooks ? opt.hooks.postprocess(html) : html)
- .catch(throwError);
- }
- try {
- if (opt.hooks) {
- src = opt.hooks.preprocess(src);
- }
- let tokens = lexer(src, opt);
- if (opt.hooks) {
- tokens = opt.hooks.processAllTokens(tokens);
- }
- if (opt.walkTokens) {
- this.walkTokens(tokens, opt.walkTokens);
- }
- let html = parser(tokens, opt);
- if (opt.hooks) {
- html = opt.hooks.postprocess(html);
- }
- return html;
- }
- catch (e) {
- return throwError(e);
- }
- };
- return parse;
- }
- onError(silent, async) {
- return (e) => {
- e.message += '\nPlease report this to https://github.com/markedjs/marked.';
- if (silent) {
- const msg = 'An error occurred:
'
- + escape$1(e.message + '', true)
- + '
';
- if (async) {
- return Promise.resolve(msg);
- }
- return msg;
- }
- if (async) {
- return Promise.reject(e);
- }
- throw e;
- };
- }
-}
-
-const markedInstance = new Marked();
-function marked(src, opt) {
- return markedInstance.parse(src, opt);
-}
-/**
- * Sets the default options.
- *
- * @param options Hash of options
- */
-marked.options =
- marked.setOptions = function (options) {
- markedInstance.setOptions(options);
- marked.defaults = markedInstance.defaults;
- changeDefaults(marked.defaults);
- return marked;
- };
-/**
- * Gets the original marked default options.
- */
-marked.getDefaults = _getDefaults;
-marked.defaults = _defaults;
-/**
- * Use Extension
- */
-marked.use = function (...args) {
- markedInstance.use(...args);
- marked.defaults = markedInstance.defaults;
- changeDefaults(marked.defaults);
- return marked;
-};
-/**
- * Run callback for every token
- */
-marked.walkTokens = function (tokens, callback) {
- return markedInstance.walkTokens(tokens, callback);
-};
-/**
- * Compiles markdown to HTML without enclosing `p` tag.
- *
- * @param src String of markdown source to be compiled
- * @param options Hash of options
- * @return String of compiled HTML
- */
-marked.parseInline = markedInstance.parseInline;
-/**
- * Expose
- */
-marked.Parser = _Parser;
-marked.parser = _Parser.parse;
-marked.Renderer = _Renderer;
-marked.TextRenderer = _TextRenderer;
-marked.Lexer = _Lexer;
-marked.lexer = _Lexer.lex;
-marked.Tokenizer = _Tokenizer;
-marked.Hooks = _Hooks;
-marked.parse = marked;
-const options = marked.options;
-const setOptions = marked.setOptions;
-const use = marked.use;
-const walkTokens = marked.walkTokens;
-const parseInline = marked.parseInline;
-const parse = marked;
-const parser = _Parser.parse;
-const lexer = _Lexer.lex;
-
-export { _Hooks as Hooks, _Lexer as Lexer, Marked, _Parser as Parser, _Renderer as Renderer, _TextRenderer as TextRenderer, _Tokenizer as Tokenizer, _defaults as defaults, _getDefaults as getDefaults, lexer, marked, options, parse, parseInline, parser, setOptions, use, walkTokens };
-//# sourceMappingURL=marked.esm.js.map
\ No newline at end of file
diff --git a/web/model-manager.css b/web/model-manager.css
deleted file mode 100644
index 305ced2..0000000
--- a/web/model-manager.css
+++ /dev/null
@@ -1,698 +0,0 @@
-/* model manager */
-.model-manager {
- background-color: var(--comfy-menu-bg);
- box-sizing: border-box;
- color: var(--bg-color);
- font-family: monospace;
- font-size: 15px;
- height: 100%;
- padding: 8px;
- position: fixed;
- overflow: hidden;
- width: 100%;
- z-index: 2000;
-
- /*override comfy-modal settings*/
- border-radius: 0;
- box-shadow: none;
- justify-content: unset;
- max-height: 100vh;
- max-width: 100vw;
- transform: none;
- /*disable double-tap zoom on model manager*/
- touch-action: manipulation;
-}
-
-.model-manager .comfy-modal-content {
- width: 100%;
- gap: 16px;
-}
-
-.model-manager .no-highlight {
- user-select: none;
- -moz-user-select: none;
- -webkit-text-select: none;
- -webkit-user-select: none;
-}
-
-.model-manager label:has(> *){
- pointer-events: none;
-}
-
-.model-manager label > * {
- pointer-events: auto;
-}
-
-/* sidebar */
-
-.model-manager {
- --model-manager-sidebar-width-left: 50vw;
- --model-manager-sidebar-width-right: 50vw;
- --model-manager-sidebar-height-top: 50vh;
- --model-manager-sidebar-height-bottom: 50vh;
-
- --model-manager-left: 0;
- --model-manager-right: 0;
- --model-manager-top: 0;
- --model-manager-bottom: 0;
-
- left: var(--model-manager-left);
- top: var(--model-manager-right);
- right: var(--model-manager-top);
- bottom: var(--model-manager-bottom);
-}
-
-.model-manager.cursor-drag-left,
-.model-manager.cursor-drag-right {
- cursor: ew-resize;
-}
-
-.model-manager.cursor-drag-top,
-.model-manager.cursor-drag-bottom {
- cursor: ns-resize;
-}
-
-.model-manager.cursor-drag-top.cursor-drag-left,
-.model-manager.cursor-drag-bottom.cursor-drag-right {
- cursor: nwse-resize;
-}
-
-.model-manager.cursor-drag-top.cursor-drag-right,
-.model-manager.cursor-drag-bottom.cursor-drag-left {
- cursor: nesw-resize;
-}
-
-/* sidebar buttons */
-.model-manager .sidebar-buttons {
- overflow: hidden;
- color: var(--input-text);
- display: flex;
- flex-direction: row-reverse;
- flex-wrap: wrap;
-}
-
-.model-manager .sidebar-buttons .radio-button-group-active {
- border-color: var(--fg-color);
- color: var(--fg-color);
- overflow: hidden;
-}
-
-.model-manager[data-sidebar-state="left"] {
- width: var(--model-manager-sidebar-width-left);
- max-width: 95vw;
- min-width: 22vw;
- right: auto;
- border-right: solid var(--border-color) 2px;
-}
-
-.model-manager[data-sidebar-state="top"] {
- height: var(--model-manager-sidebar-height-top);
- max-height: 95vh;
- min-height: 22vh;
- bottom: auto;
- border-bottom: solid var(--border-color) 2px;
-}
-
-.model-manager[data-sidebar-state="bottom"] {
- height: var(--model-manager-sidebar-height-bottom);
- max-height: 95vh;
- min-height: 22vh;
- top: auto;
- border-top: solid var(--border-color) 2px;
-}
-
-.model-manager[data-sidebar-state="right"] {
- width: var(--model-manager-sidebar-width-right);
- max-width: 95vw;
- min-width: 22vw;
- left: auto;
- border-left: solid var(--border-color) 2px;
-}
-
-/* common */
-.model-manager h1 {
- min-width: 0;
- overflow-wrap: break-word;
-}
-
-.model-manager textarea {
- border: solid 2px var(--border-color);
- border-radius: 8px;
- font-size: 1.2em;
- resize: vertical;
- width: 100%;
- height: 100%;
-}
-
-.model-manager input[type="file"] {
- width: 100%;
-}
-
-.model-manager button {
- margin: 0;
- border: 2px solid var(--border-color);
-}
-
-.model-manager button:not(.icon-button),
-.model-manager select,
-.model-manager input {
- padding: 4px 8px;
- margin: 0;
-}
-
-.model-manager button:disabled,
-.model-manager select:disabled,
-.model-manager input:disabled {
- background-color: var(--comfy-menu-bg);
- filter: brightness(1.2);
- cursor: not-allowed;
-}
-
-.model-manager button.block {
- width: 100%;
-}
-
-.model-manager ::-webkit-scrollbar {
- width: 16px;
-}
-
-.model-manager ::-webkit-scrollbar-track {
- background-color: var(--comfy-input-bg);
- border-right: 1px solid var(--border-color);
- border-bottom: 1px solid var(--border-color);
-}
-
-.model-manager ::-webkit-scrollbar-thumb {
- background-color: var(--fg-color);
- border-radius: 3px;
-}
-
-.model-manager .search-text-area::-webkit-input-placeholder {
- font-style: italic;
-}
-.model-manager .search-text-area:-moz-placeholder {
- font-style: italic;
-}
-.model-manager .search-text-area::-moz-placeholder {
- font-style: italic;
-}
-.model-manager .search-text-area:-ms-input-placeholder {
- font-style: italic;
-}
-
-.model-manager .icon-button {
- height: 40px;
- width: 40px;
- line-height: 1.15;
-}
-
-.model-manager .row {
- display: flex;
- min-width: 0;
- gap: 8px;
-}
-
-.model-manager .tab-header {
- display: flex;
- padding: 8px 0px;
- flex-direction: column;
- background-color: var(--bg-color);
-}
-
-.model-manager .tab-header-flex-block {
- width: 100%;
- min-width: 0;
-}
-
-.model-manager .comfy-button-success {
- color: green;
- border-color: green;
-}
-
-.model-manager .comfy-button-failure {
- color: darkred;
- border-color: darkred;
-}
-
-.model-manager .no-select {
- -webkit-user-select: none;
- -ms-user-select: none;
- user-select: none;
-}
-
-/* main content */
-.model-manager .model-manager-panel {
- color: var(--fg-color);
-}
-
-.model-manager .model-tab-group {
- display: flex;
- gap: 4px;
- height: 40px;
-}
-
-.model-manager .model-tab-group .tab-button {
- background-color: var(--comfy-menu-bg);
- border: 2px solid var(--border-color);
- border-bottom: none;
- border-radius: 8px 8px 0px 0px;
- cursor: pointer;
- padding: 8px 12px;
- margin-bottom: 0px;
- z-index: 1;
-}
-
-.model-manager .model-tab-group .tab-button.active {
- background-color: var(--bg-color);
- cursor: default;
- position: relative;
- z-index: 1;
- pointer-events: none;
-}
-
-.model-manager .model-manager-body {
- background-color: var(--bg-color);
- border: 2px solid var(--border-color);
-}
-
-.model-manager .model-manager-panel {
- flex: 1;
- display: flex;
- flex-direction: column;
- overflow: hidden;
-}
-
-.model-manager .model-manager-body {
- flex: 1;
- overflow: hidden;
- padding: 8px 0px 8px 16px;
-}
-
-.model-manager .model-manager-body .tab-contents {
- position: relative;
- display: flex;
- flex-direction: column;
- height: 100%;
- width: auto;
- overflow-x: auto;
- overflow-y: hidden;
-}
-
-.model-manager .model-manager-body .tab-content {
- display: flex;
- flex-direction: column;
- height: 100%;
- overflow-y: auto;
- padding-right: 16px;
-}
-
-/* model info view */
-.model-manager .model-info-container {
- background-color: var(--bg-color);
- border-radius: 16px;
- color: var(--fg-color);
- width: auto;
-}
-
-.model-manager .model-metadata {
- table-layout: fixed;
- text-align: left;
- width: 100%;
-}
-
-.model-manager .model-metadata-key {
- overflow-wrap: break-word;
- width: 20%;
-}
-
-.model-manager .model-metadata-value {
- overflow-wrap: anywhere;
- width: 80%;
-}
-
-.model-manager table {
- border-collapse: collapse;
-}
-
-.model-manager th {
- border: 1px solid;
- padding: 4px 8px;
-}
-
-/* download tab */
-
-.model-manager .download-model-infos {
- display: flex;
- flex-direction: column;
- padding: 0;
- row-gap: 10px;
-}
-
-.model-manager .download-details summary {
- background-color: var(--comfy-menu-bg);
- border-radius: 16px;
- padding: 16px;
- word-wrap: break-word;
-}
-
-.model-manager .download-details[open] summary {
- background-color: var(--border-color);
-}
-
-.model-manager .download-details > div {
- column-gap: 8px;
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- padding: 8px;
- row-gap: 16px;
-}
-
-.model-manager [data-name="Download"] .download-settings-wrapper {
- flex: 1;
-}
-
-.model-manager [data-name="Download"] .download-settings {
- display: flex;
- flex-direction: column;
- row-gap: 16px;
-}
-
-.model-manager .download-button {
- max-width: fit-content;
-}
-
-/* models tab */
-.model-manager [data-name="Models"] .row {
- position: sticky;
- z-index: 1;
- top: 0;
-}
-
-/* preview image */
-.model-manager .item {
- position: relative;
- width: 240px;
- height: 360px;
- text-align: center;
- overflow: hidden;
- border-radius: 8px;
-}
-
-.model-manager .item img {
- width: 100%;
- height: 100%;
- object-fit: cover;
- border-radius: 8px;
-}
-
-.model-manager .model-info-container .item {
- width: auto;
- height: auto;
-}
-.model-manager .model-info-container .item img {
- height: auto;
- width: auto;
- max-width: 100%;
- max-height: 50vh;
-}
-
-.model-manager .model-preview-button-left,
-.model-manager .model-preview-button-right {
- position: absolute;
- top: 0;
- bottom: 0;
- margin: auto;
- border-radius: 20px;
-}
-
-.model-manager .model-preview-button-right {
- right: 4px;
-}
-
-.model-manager .model-preview-button-left {
- left: 4px;
-}
-
-.model-manager .item .model-preview-overlay {
- position: absolute;
- top: 0;
- left: 0;
- height: 100%;
- width: 100%;
- background-color: rgba(0, 0, 0, 0);
-}
-
-/* grid */
-.model-manager .comfy-grid {
- display: flex;
- flex-wrap: wrap;
- gap: 16px;
-}
-
-.model-manager .comfy-grid .model-label {
- background-color: rgb(from var(--content-hover-bg) r g b / 0.5);
- width: 100%;
- height: 2.2rem;
- position: absolute;
- bottom: 0;
- text-align: center;
- line-height: 2.2rem;
-}
-
-.model-manager .comfy-grid .model-label > p {
- width: calc(100% - 2rem);
- overflow-x: scroll;
- white-space: nowrap;
- display: inline-block;
- vertical-align: middle;
- margin: 0;
-}
-
-.model-manager .comfy-grid .model-label {
- scrollbar-width: none;
- -ms-overflow-style: none;
-}
-
-.model-manager .comfy-grid .model-label ::-webkit-scrollbar {
- width: 0;
- height: 0;
-}
-
-.model-manager .comfy-grid .model-preview-top-right,
-.model-manager .comfy-grid .model-preview-top-left {
- position: absolute;
- display: flex;
- flex-direction: column;
- gap: 8px;
- top: 8px;
-}
-
-.model-manager .comfy-grid .model-preview-top-right {
- right: 8px;
-}
-
-.model-manager .comfy-grid .model-preview-top-left {
- left: 8px;
-}
-
-.model-manager .comfy-grid .model-button {
- opacity: 0.65;
-}
-
-.model-manager .comfy-grid .model-button:hover {
- opacity: 1;
-}
-
-.model-manager .comfy-grid .model-label {
- user-select: text;
-}
-
-/* radio */
-.model-manager .comfy-radio-group {
- display: flex;
- gap: 8px;
- flex-wrap: wrap;
- min-width: 0;
-}
-
-.model-manager .comfy-radio {
- display: flex;
- gap: 4px;
- padding: 4px 16px;
- color: var(--input-text);
- border: 2px solid var(--border-color);
- border-radius: 16px;
- background-color: var(--comfy-input-bg);
- font-size: 18px;
-}
-
-.model-manager .comfy-radio:has(> input[type="radio"]:checked) {
- border-color: var(--border-color);
- background-color: var(--comfy-menu-bg);
-}
-
-.model-manager .comfy-radio input[type="radio"]:checked + label {
- color: var(--fg-color);
-}
-
-.model-manager .radio-input {
- opacity: 0;
- position: absolute;
-}
-
-/* model preview select */
-.model-manager .model-preview-select-radio-container {
- min-width: 0;
- flex: 1;
-}
-
-.model-manager .model-preview-select-radio-inputs > div {
- padding: 16px 0 8px 0;
-}
-
-.model-manager .model-preview-select-radio-container img {
- position: relative;
- width: 230px;
- height: 345px;
- text-align: center;
- overflow: hidden;
- border-radius: 8px;
- object-fit: cover;
-}
-
-/* topbar */
-.model-manager .topbar-buttons {
- display: flex;
- float: right;
-}
-
-.model-manager .topbar-buttons button {
- height: 33px;
- padding: 1px 6px;
- width: 33px;
-}
-
-.model-manager .model-manager-head .topbar-left {
- display: flex;
- float: left;
-}
-
-.model-manager .model-manager-head .topbar-right {
- column-gap: 4px;
- display: flex;
- flex-direction: row-reverse;
- float: right;
-}
-
-.model-manager .model-manager-head .topbar-right select {
- position: relative;
- top: 0;
- bottom: 0;
- font-size: 24px;
- -o-appearance: none;
- -ms-appearance: none;
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
-}
-
-/* search dropdown */
-.model-manager .input-dropdown-container {
- position: relative;
-}
-
-.model-manager .search-models {
- display: flex;
- flex: 1;
- flex-direction: row;
- min-width: 0;
-}
-
-.model-manager .model-select-dropdown {
- min-width: 0;
- overflow: auto;
-}
-
-.model-manager .search-text-area,
-.model-manager .plain-text-area,
-.model-manager .model-select-dropdown {
- flex: 1;
- min-height: 36px;
- padding-block: 0;
- min-width: 36px;
-}
-
-.model-manager .model-select-dropdown {
- min-height: 40px;
-}
-
-.model-manager .search-directory-dropdown {
- background-color: var(--bg-color);
- border: 2px var(--border-color) solid;
- border-radius: 10px;
- color: var(--fg-color);
- max-height: 40vh;
- overflow: auto;
- position: absolute;
- z-index: 1;
-}
-
-@media (pointer:none), (pointer:coarse) {
- .model-manager .search-directory-dropdown {
- max-height: 17.5vh;
- }
-}
-
-.model-manager .search-directory-dropdown:empty {
- display: none;
-}
-
-.model-manager .search-directory-dropdown > p {
- margin: 0;
- padding: 0.85em 20px;
- min-width: 0;
-}
-.model-manager .search-directory-dropdown > p {
- -ms-overflow-style: none; /* Internet Explorer 10+ */
- scrollbar-width: none; /* Firefox */
-}
-.model-manager .search-directory-dropdown > p::-webkit-scrollbar {
- display: none; /* Safari and Chrome */
-}
-
-.model-manager .search-directory-dropdown > p.search-directory-dropdown-key-selected,
-.model-manager .search-directory-dropdown > p.search-directory-dropdown-mouse-selected {
- background-color: var(--border-color);
-}
-
-.model-manager .search-directory-dropdown > p.search-directory-dropdown-key-selected {
- border-left: 1mm solid var(--input-text);
-}
-
-/* model manager settings */
-.model-manager .model-manager-settings > div,
-.model-manager .model-manager-settings > label,
-.model-manager .tag-generator-settings > label,
-.model-manager .tag-generator-settings > div {
- display: flex;
- flex-direction: row;
- align-items: center;
- gap: 8px;
- margin: 16px 0;
-}
-
-.model-manager .model-manager-settings button {
- height: 40px;
- min-width: 120px;
- justify-content: center;
-}
-
-.model-manager .model-manager-settings input[type="number"],
-.model-manager .tag-generator-settings input[type="number"]{
- width: 50px;
-}
-
-.model-manager .search-settings-text {
- width: 100%;
-}
diff --git a/web/model-manager.js b/web/model-manager.js
deleted file mode 100644
index fd55c67..0000000
--- a/web/model-manager.js
+++ /dev/null
@@ -1,5545 +0,0 @@
-import { app } from '../../scripts/app.js';
-import { api } from '../../scripts/api.js';
-import { ComfyDialog, $el } from '../../scripts/ui.js';
-import { ComfyButton } from '../../scripts/ui/components/button.js';
-import { marked } from './marked.js';
-import('./downshow.js');
-
-function clamp(x, min, max) {
- return Math.min(Math.max(x, min), max);
-}
-
-/**
- * @param {string} url
- * @param {any} [options=undefined]
- * @returns {Promise}
- */
-function comfyRequest(url, options = undefined) {
- return new Promise((resolve, reject) => {
- api
- .fetchApi(url, options)
- .then((response) => response.json())
- .then(resolve)
- .catch(reject);
- });
-}
-
-/**
- * @param {(...args) => Promise} callback
- * @param {number | undefined} delay
- * @returns {(...args) => void}
- */
-function debounce(callback, delay) {
- let timeoutId = null;
- return (...args) => {
- window.clearTimeout(timeoutId);
- timeoutId = window.setTimeout(() => {
- callback(...args);
- }, delay);
- };
-}
-
-class KeyComboListener {
- /** @type {string[]} */
- #keyCodes = [];
-
- /** @type {() => Promise} */
- action;
-
- /** @type {Element} */
- element;
-
- /** @type {string[]} */
- #combo = [];
-
- /**
- * @param {string[]} keyCodes
- * @param {() => Promise} action
- * @param {Element} element
- */
- constructor(keyCodes, action, element) {
- this.#keyCodes = keyCodes;
- this.action = action;
- this.element = element;
-
- document.addEventListener('keydown', (e) => {
- const code = e.code;
- const keyCodes = this.#keyCodes;
- const combo = this.#combo;
- if (keyCodes.includes(code) && !combo.includes(code)) {
- combo.push(code);
- }
- if (combo.length === 0 || keyCodes.length !== combo.length) {
- return;
- }
- for (let i = 0; i < combo.length; i++) {
- if (keyCodes[i] !== combo[i]) {
- return;
- }
- }
- if (document.activeElement !== this.element) {
- return;
- }
- e.preventDefault();
- e.stopPropagation();
- this.action();
- this.#combo.length = 0;
- });
- document.addEventListener('keyup', (e) => {
- // Mac keyup doesn't fire when meta key is held: https://stackoverflow.com/a/73419500
- const code = e.code;
- if (code === 'MetaLeft' || code === 'MetaRight') {
- this.#combo.length = 0;
- } else {
- this.#combo = this.#combo.filter((x) => x !== code);
- }
- });
- }
-}
-
-/**
- * Handles Firefox's drag event, which returns different coordinates and then fails when calling `elementFromPoint`.
- * @param {DragEvent} event
- * @returns {[Number, Number, HTMLElement]} [clientX, clientY, targetElement]
- */
-function elementFromDragEvent(event) {
- let clientX = null;
- let clientY = null;
- let target;
- const userAgentString = navigator.userAgent;
- if (userAgentString.indexOf('Firefox') > -1) {
- clientX = event.clientX;
- clientY = event.clientY;
- const screenOffsetX = window.screenLeft;
- if (clientX >= screenOffsetX) {
- clientX = clientX - screenOffsetX;
- }
- const screenOffsetY = window.screenTop;
- if (clientY >= screenOffsetY) {
- clientY = clientY - screenOffsetY;
- }
- target = document.elementFromPoint(clientX, clientY);
- } else {
- clientX = event.clientX;
- clientY = event.clientY;
- target = document.elementFromPoint(event.clientX, event.clientY);
- }
- return [clientX, clientY, target];
-}
-
-/**
- * @param {string} url
- */
-async function loadWorkflow(url) {
- const uri = new URL(url).searchParams.get('uri');
- const fileNameIndex =
- Math.max(uri.lastIndexOf('/'), uri.lastIndexOf('\\')) + 1;
- const fileName = uri.substring(fileNameIndex);
- const response = await fetch(url);
- const data = await response.blob();
- const file = new File([data], fileName, { type: data.type });
- app.handleFile(file);
-}
-
-const modelNodeType = {
- checkpoints: 'CheckpointLoaderSimple',
- clip: 'CLIPLoader',
- clip_vision: 'CLIPVisionLoader',
- controlnet: 'ControlNetLoader',
- diffusers: 'DiffusersLoader',
- embeddings: 'Embedding',
- gligen: 'GLIGENLoader',
- hypernetworks: 'HypernetworkLoader',
- photomaker: 'PhotoMakerLoader',
- loras: 'LoraLoader',
- style_models: 'StyleModelLoader',
- unet: 'UNETLoader',
- upscale_models: 'UpscaleModelLoader',
- vae: 'VAELoader',
- vae_approx: undefined,
-};
-
-const MODEL_EXTENSIONS = [
- '.bin',
- '.ckpt',
- 'gguf',
- '.onnx',
- '.pt',
- '.pth',
- '.safetensors',
-]; // TODO: ask server for?
-const IMAGE_EXTENSIONS = [
- '.png',
- '.webp',
- '.jpeg',
- '.jpg',
- '.jfif',
- '.gif',
- '.apng',
-
- '.preview.png',
- '.preview.webp',
- '.preview.jpeg',
- '.preview.jpg',
- '.preview.jfif',
- '.preview.gif',
- '.preview.apng',
-]; // TODO: /model-manager/image/extensions
-
-/**
- * @param {string} s
- * @param {string} prefix
- * @returns {string}
- */
-function removePrefix(s, prefix) {
- if (s.length >= prefix.length && s.startsWith(prefix)) {
- return s.substring(prefix.length);
- }
- return s;
-}
-
-/**
- * @param {string} s
- * @param {string} suffix
- * @returns {string}
- */
-function removeSuffix(s, suffix) {
- if (s.length >= suffix.length && s.endsWith(suffix)) {
- return s.substring(0, s.length - suffix.length);
- }
- return s;
-}
-
-class SearchPath {
- /**
- * @param {string} path
- * @returns {[string, string]}
- */
- static split(path) {
- const i = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')) + 1;
- return [path.slice(0, i), path.slice(i)];
- }
-
- /**
- * @param {string} path
- * @param {string[]} extensions
- * @returns {[string, string]}
- */
- static splitExtension(path) {
- const i = path.lastIndexOf('.');
- if (i === -1) {
- return [path, ''];
- }
- return [path.slice(0, i), path.slice(i)];
- }
-
- /**
- * @param {string} path
- * @returns {string}
- */
- static systemPath(path, searchSeparator, systemSeparator) {
- const i1 = path.indexOf(searchSeparator, 1);
- const i2 = path.indexOf(searchSeparator, i1 + 1);
- return path.slice(i2 + 1).replaceAll(searchSeparator, systemSeparator);
- }
-}
-
-/**
- * @param {string | undefined} [searchPath=undefined]
- * @param {string | undefined} [dateImageModified=undefined]
- * @param {string | undefined} [width=undefined]
- * @param {string | undefined} [height=undefined]
- * @param {string | undefined} [imageFormat=undefined]
- * @returns {string}
- */
-function imageUri(
- imageSearchPath = undefined,
- dateImageModified = undefined,
- width = undefined,
- height = undefined,
- imageFormat = undefined,
-) {
- const path = imageSearchPath ?? 'no-preview';
- const date = dateImageModified;
- let uri = `/model-manager/preview/get?uri=${path}`;
- if (width !== undefined && width !== null) {
- uri += `&width=${width}`;
- }
- if (height !== undefined && height !== null) {
- uri += `&height=${height}`;
- }
- if (date !== undefined && date !== null) {
- uri += `&v=${date}`;
- }
- if (imageFormat !== undefined && imageFormat !== null) {
- uri += `&image-format=${imageFormat}`;
- }
- return uri;
-}
-const PREVIEW_NONE_URI = imageUri();
-const PREVIEW_THUMBNAIL_WIDTH = 320;
-const PREVIEW_THUMBNAIL_HEIGHT = 480;
-
-/**
- *
- * @param {HTMLButtonElement} element
- * @returns {[HTMLButtonElement | undefined, HTMLElement | undefined, HTMLSpanElement | undefined]} [button, icon, span]
- */
-function comfyButtonDisambiguate(element) {
- // TODO: This likely can be removed by using a css rule that disables clicking on the inner elements of the button.
- let button = undefined;
- let icon = undefined;
- let span = undefined;
- const nodeName = element.nodeName.toLowerCase();
- if (nodeName === 'button') {
- button = element;
- icon = button.getElementsByTagName('i')[0];
- span = button.getElementsByTagName('span')[0];
- } else if (nodeName === 'i') {
- icon = element;
- button = element.parentElement;
- span = button.getElementsByTagName('span')[0];
- } else if (nodeName === 'span') {
- button = element.parentElement;
- icon = button.getElementsByTagName('i')[0];
- span = element;
- }
- return [button, icon, span];
-}
-
-/**
- * @param {HTMLButtonElement} element
- * @param {boolean} success
- * @param {string?} successClassName
- * @param {string?} failureClassName
- * @param {boolean?} [disableCallback=false]
- */
-function comfyButtonAlert(
- element,
- success,
- successClassName = undefined,
- failureClassName = undefined,
- disableCallback = false,
-) {
- if (element === undefined || element === null) {
- return;
- }
-
- const [button, icon, span] = comfyButtonDisambiguate(element);
- if (button === undefined) {
- console.warn('Unable to find button element!');
- console.warn(element);
- return;
- }
-
- // TODO: debounce would be nice, but needs some sort of "global" to avoid creating/destroying many objects
-
- const colorClassName = success
- ? 'comfy-button-success'
- : 'comfy-button-failure';
-
- if (icon) {
- const iconClassName = (success ? successClassName : failureClassName) ?? '';
- if (iconClassName !== '') {
- icon.classList.add(iconClassName);
- }
- icon.classList.add(colorClassName);
- if (!disableCallback) {
- window.setTimeout(
- (element, iconClassName, colorClassName) => {
- if (iconClassName !== '') {
- element.classList.remove(iconClassName);
- }
- element.classList.remove(colorClassName);
- },
- 1000,
- icon,
- iconClassName,
- colorClassName,
- );
- }
- }
-
- button.classList.add(colorClassName);
- if (!disableCallback) {
- window.setTimeout(
- (element, colorClassName) => {
- element.classList.remove(colorClassName);
- },
- 1000,
- button,
- colorClassName,
- );
- }
-}
-
-/**
- *
- * @param {string} modelPath
- * @param {string} newValue
- * @returns {Promise}
- */
-async function saveNotes(modelPath, newValue) {
- const timestamp = await comfyRequest('/model-manager/timestamp').catch(
- (err) => {
- console.warn(err);
- return false;
- },
- );
- return await comfyRequest('/model-manager/notes/save', {
- method: 'POST',
- body: JSON.stringify({
- path: modelPath,
- notes: newValue,
- }),
- timestamp: timestamp,
- })
- .then((result) => {
- const saved = result['success'];
- const message = result['alert'];
- if (message !== undefined) {
- window.alert(message);
- }
- return saved;
- })
- .catch((err) => {
- console.warn(err);
- return false;
- });
-}
-
-/**
- * @returns {HTMLLabelElement}
- */
-function $checkbox(x = { $: (el) => {}, textContent: '', checked: false }) {
- const text = x.textContent;
- const input = $el('input', {
- type: 'checkbox',
- name: text ?? 'checkbox',
- checked: x.checked ?? false,
- });
- const label = $el('label', [
- input,
- text === '' || text === undefined || text === null ? '' : ' ' + text,
- ]);
- if (x.$ !== undefined) {
- x.$(input);
- }
- return label;
-}
-
-/**
- * @returns {HTMLLabelElement}
- */
-function $select(x = { $: (el) => {}, textContent: '', options: [''] }) {
- const text = x.textContent;
- const select = $el(
- 'select',
- {
- name: text ?? 'select',
- },
- x.options.map((option) => {
- return $el(
- 'option',
- {
- value: option,
- },
- option,
- );
- }),
- );
- const label = $el('label', [
- text === '' || text === undefined || text === null ? '' : ' ' + text,
- select,
- ]);
- if (x.$ !== undefined) {
- x.$(select);
- }
- return label;
-}
-
-/**
- * @param {Any} attr
- * @returns {HTMLDivElement}
- */
-function $radioGroup(attr) {
- const { name = Date.now(), onchange, options = [], $ } = attr;
-
- /** @type {HTMLDivElement[]} */
- const radioGroup = options.map((item, index) => {
- const inputRef = { value: null };
-
- return $el('div.comfy-radio', { onclick: () => inputRef.value.click() }, [
- $el('input.radio-input', {
- type: 'radio',
- name: name,
- value: item.value,
- checked: index === 0,
- $: (el) => (inputRef.value = el),
- }),
- $el('label.no-highlight', item.label ?? item.value),
- ]);
- });
-
- const element = $el('input', {
- name: name + '-group',
- value: options[0]?.value,
- });
- $?.(element);
-
- radioGroup.forEach((radio) => {
- radio.addEventListener('change', (event) => {
- const selectedValue = event.target.value;
- element.value = selectedValue;
- onchange?.(selectedValue);
- });
- });
-
- return $el('div.comfy-radio-group', radioGroup);
-}
-
-/**
- * @param {{name: string, icon: string, tabContent: HTMLDivElement}[]} tabData
- * @returns {[HTMLDivElement[], HTMLDivElement[]]}
- */
-function GenerateTabGroup(tabData) {
- const ACTIVE_TAB_CLASS = 'active';
-
- /** @type {HTMLDivElement[]} */
- const tabButtons = [];
-
- /** @type {HTMLDivElement[]} */
- const tabContents = [];
-
- tabData.forEach((data) => {
- const name = data.name;
- const icon = data.icon;
- /** @type {HTMLDivElement} */
- const tab = new ComfyButton({
- icon: icon,
- tooltip: 'Open ' + name.toLowerCase() + ' tab',
- classList: 'comfyui-button tab-button',
- content: name,
- action: () => {
- tabButtons.forEach((tabButton) => {
- if (name === tabButton.getAttribute('data-name')) {
- tabButton.classList.add(ACTIVE_TAB_CLASS);
- } else {
- tabButton.classList.remove(ACTIVE_TAB_CLASS);
- }
- });
- tabContents.forEach((tabContent) => {
- if (name === tabContent.getAttribute('data-name')) {
- tabContent.scrollTop = tabContent.dataset['scrollTop'] ?? 0;
- tabContent.style.display = '';
- } else {
- tabContent.dataset['scrollTop'] = tabContent.scrollTop;
- tabContent.style.display = 'none';
- }
- });
- },
- }).element;
- tab.dataset.name = name;
- const content = $el(
- 'div.tab-content',
- {
- dataset: {
- name: data.name,
- },
- },
- [data.tabContent],
- );
- tabButtons.push(tab);
- tabContents.push(content);
- });
-
- return [tabButtons, tabContents];
-}
-
-/**
- * @param {HTMLDivElement} element
- * @param {Record[]} tabButtons
- */
-function GenerateDynamicTabTextCallback(element, tabButtons, minWidth) {
- return () => {
- if (element.style.display === 'none') {
- return;
- }
- const managerRect = element.getBoundingClientRect();
- const isIcon = managerRect.width < minWidth; // TODO: `minWidth` is a magic value
- const iconDisplay = isIcon ? '' : 'none';
- const spanDisplay = isIcon ? 'none' : '';
- tabButtons.forEach((tabButton) => {
- tabButton.getElementsByTagName('i')[0].style.display = iconDisplay;
- tabButton.getElementsByTagName('span')[0].style.display = spanDisplay;
- });
- };
-}
-
-/**
- * @param {[String, int][]} map
- * @returns {String}
- */
-function TagCountMapToParagraph(map) {
- let text = '';
- for (let i = 0; i < map.length; i++) {
- const v = map[i];
- const tag = v[0];
- const count = v[1];
- text += tag + ' (' + count + ')';
- if (i !== map.length - 1) {
- text += ', ';
- }
- }
- text += '
';
- return text;
-}
-
-/**
- * @param {String} p
- * @returns {[String, int][]}
- */
-function ParseTagParagraph(p) {
- return p.split(',').map((x) => {
- const text = x.endsWith(', ') ? x.substring(0, x.length - 2) : x;
- const i = text.lastIndexOf('(');
- const tag = text.substring(0, i).trim();
- const frequency = parseInt(text.substring(i + 1, text.length - 1));
- return [tag, frequency];
- });
-}
-
-class ImageSelect {
- /** @constant {string} */ #PREVIEW_DEFAULT = 'Default';
- /** @constant {string} */ #PREVIEW_UPLOAD = 'Upload';
- /** @constant {string} */ #PREVIEW_URL = 'URL';
- /** @constant {string} */ #PREVIEW_NONE = 'No Preview';
-
- elements = {
- /** @type {HTMLDivElement} */ radioGroup: null,
- /** @type {HTMLDivElement} */ radioButtons: null,
- /** @type {HTMLDivElement} */ previews: null,
-
- /** @type {HTMLImageElement} */ defaultPreviewNoImage: null,
- /** @type {HTMLDivElement} */ defaultPreviews: null,
- /** @type {HTMLDivElement} */ defaultUrl: null,
-
- /** @type {HTMLImageElement} */ customUrlPreview: null,
- /** @type {HTMLInputElement} */ customUrl: null,
- /** @type {HTMLDivElement} */ custom: null,
-
- /** @type {HTMLImageElement} */ uploadPreview: null,
- /** @type {HTMLInputElement} */ uploadFile: null,
- /** @type {HTMLDivElement} */ upload: null,
- };
-
- /** @type {string} */
- #name = null;
-
- /** @returns {Promise | Promise} */
- async getImage() {
- const name = this.#name;
- const value = document.querySelector(`input[name="${name}"]:checked`).value;
- const elements = this.elements;
- switch (value) {
- case this.#PREVIEW_DEFAULT: {
- const children = elements.defaultPreviews.children;
- const noImage = PREVIEW_NONE_URI;
- let url = '';
- for (let i = 0; i < children.length; i++) {
- const child = children[i];
- if (
- child.style.display !== 'none' &&
- child.nodeName === 'IMG' &&
- !child.src.endsWith(noImage)
- ) {
- url = child.src;
- }
- }
- if (url.startsWith(Civitai.imageUrlPrefix())) {
- url = await Civitai.getFullSizeImageUrl(url).catch((err) => {
- console.warn(err);
- return url;
- });
- }
- return url;
- }
- case this.#PREVIEW_URL: {
- const value = elements.customUrl.value;
- if (value.startsWith(Civitai.imagePostUrlPrefix())) {
- try {
- const imageInfo = await Civitai.getImageInfo(value);
- const items = imageInfo['items'];
- if (items.length === 0) {
- console.warn('Civitai /api/v1/images returned 0 items.');
- return value;
- }
- return items[0]['url'];
- } catch (error) {
- console.error('Failed to get image info from Civitai!', error);
- return value;
- }
- }
- return value;
- }
- case this.#PREVIEW_UPLOAD:
- return elements.uploadFile.files[0] ?? '';
- case this.#PREVIEW_NONE:
- return PREVIEW_NONE_URI;
- }
- return '';
- }
-
- /** @returns {void} */
- resetModelInfoPreview() {
- let noimage = this.elements.defaultUrl.dataset.noimage;
- [
- this.elements.defaultPreviewNoImage,
- this.elements.defaultPreviews,
- this.elements.customUrlPreview,
- this.elements.uploadPreview,
- ].forEach((el) => {
- el.style.display = 'none';
- if (this.elements.defaultPreviewNoImage !== el) {
- if (el.nodeName === 'IMG') {
- el.src = noimage;
- } else {
- el.children[0].src = noimage;
- }
- } else {
- el.src = PREVIEW_NONE_URI;
- }
- });
- this.checkDefault();
- this.elements.uploadFile.value = '';
- this.elements.customUrl.value = '';
- this.elements.upload.style.display = 'none';
- this.elements.custom.style.display = 'none';
- }
-
- /** @returns {boolean} */
- defaultIsChecked() {
- const children = this.elements.radioButtons.children;
- for (let i = 0; i < children.length; i++) {
- const child = children[i];
- const radioButton = child.children[0];
- if (radioButton.value === this.#PREVIEW_DEFAULT) {
- return radioButton.checked;
- }
- }
- return false;
- }
-
- /** @returns {void} */
- checkDefault() {
- const children = this.elements.radioButtons.children;
- for (let i = 0; i < children.length; i++) {
- const child = children[i];
- const radioButton = child.children[0];
- if (radioButton.value === this.#PREVIEW_DEFAULT) {
- this.elements.defaultPreviews.style.display = 'block';
- radioButton.checked = true;
- break;
- }
- }
- }
-
- /**
- * @param {1 | -1} step
- */
- stepDefaultPreviews(step) {
- const children = this.elements.defaultPreviews.children;
- if (children.length === 0) {
- return;
- }
- let currentIndex = -step;
- for (let i = 0; i < children.length; i++) {
- const previewImage = children[i];
- const display = previewImage.style.display;
- if (display !== 'none') {
- currentIndex = i;
- }
- previewImage.style.display = 'none';
- }
- currentIndex = currentIndex + step;
- if (currentIndex >= children.length) {
- currentIndex = 0;
- } else if (currentIndex < 0) {
- currentIndex = children.length - 1;
- }
- children[currentIndex].style.display = 'block';
- }
-
- /**
- * @param {string} radioGroupName - Should be unique for every radio group.
- * @param {string[]|undefined} defaultPreviews
- */
- constructor(radioGroupName, defaultPreviews = []) {
- if (
- (defaultPreviews === undefined) |
- (defaultPreviews === null) |
- (defaultPreviews.length === 0)
- ) {
- defaultPreviews = [PREVIEW_NONE_URI];
- }
- this.#name = radioGroupName;
-
- const el_defaultUri = $el('div', {
- $: (el) => (this.elements.defaultUrl = el),
- style: { display: 'none' },
- 'data-noimage': PREVIEW_NONE_URI,
- });
-
- const el_defaultPreviewNoImage = $el('img', {
- $: (el) => (this.elements.defaultPreviewNoImage = el),
- loading:
- 'lazy' /* `loading` BEFORE `src`; Known bug in Firefox 124.0.2 and Safari for iOS 17.4.1 (https://stackoverflow.com/a/76252772) */,
- src: PREVIEW_NONE_URI,
- style: { display: 'none' },
- });
-
- const el_defaultPreviews = $el(
- 'div',
- {
- $: (el) => (this.elements.defaultPreviews = el),
- style: {
- width: '100%',
- height: '100%',
- },
- },
- (() => {
- const imgs = defaultPreviews.map((url) => {
- return $el('img', {
- loading:
- 'lazy' /* `loading` BEFORE `src`; Known bug in Firefox 124.0.2 and Safari for iOS 17.4.1 (https://stackoverflow.com/a/76252772) */,
- src: url,
- style: { display: 'none' },
- onerror: (e) => {
- e.target.src = el_defaultUri.dataset.noimage ?? PREVIEW_NONE_URI;
- },
- });
- });
- if (imgs.length > 0) {
- imgs[0].style.display = 'block';
- }
- return imgs;
- })(),
- );
-
- const el_uploadPreview = $el('img', {
- $: (el) => (this.elements.uploadPreview = el),
- src: PREVIEW_NONE_URI,
- style: { display: 'none' },
- onerror: (e) => {
- e.target.src = el_defaultUri.dataset.noimage ?? PREVIEW_NONE_URI;
- },
- });
- const el_uploadFile = $el('input', {
- $: (el) => (this.elements.uploadFile = el),
- type: 'file',
- name: 'upload preview image',
- accept: IMAGE_EXTENSIONS.join(', '),
- onchange: (e) => {
- const file = e.target.files[0];
- if (file) {
- el_uploadPreview.src = URL.createObjectURL(file);
- } else {
- el_uploadPreview.src = el_defaultUri.dataset.noimage;
- }
- },
- });
- const el_upload = $el(
- 'div.row.tab-header-flex-block',
- {
- $: (el) => (this.elements.upload = el),
- style: { display: 'none' },
- },
- [el_uploadFile],
- );
-
- /**
- * @param {string} url
- * @returns {Promise}
- */
- const getCustomPreviewUrl = async (url) => {
- if (url.startsWith(Civitai.imagePostUrlPrefix())) {
- return await Civitai.getImageInfo(url)
- .then((imageInfo) => {
- const items = imageInfo['items'];
- if (items.length > 0) {
- return items[0]['url'];
- } else {
- console.warn('Civitai /api/v1/images returned 0 items.');
- return url;
- }
- })
- .catch((error) => {
- console.error('Failed to get image info from Civitai!', error);
- return url;
- });
- } else {
- return url;
- }
- };
-
- const el_customUrlPreview = $el('img', {
- $: (el) => (this.elements.customUrlPreview = el),
- src: PREVIEW_NONE_URI,
- style: { display: 'none' },
- onerror: (e) => {
- e.target.src = el_defaultUri.dataset.noimage ?? PREVIEW_NONE_URI;
- },
- });
- const el_customUrl = $el('input.search-text-area', {
- $: (el) => (this.elements.customUrl = el),
- type: 'text',
- name: 'custom preview image url',
- autocomplete: 'off',
- placeholder: 'https://custom-image-preview.png',
- onkeydown: async (e) => {
- if (e.key === 'Enter') {
- const value = e.target.value;
- el_customUrlPreview.src = await getCustomPreviewUrl(value);
- e.stopPropagation();
- e.target.blur();
- }
- },
- });
- const el_custom = $el(
- 'div.row.tab-header-flex-block',
- {
- $: (el) => (this.elements.custom = el),
- style: { display: 'none' },
- },
- [
- el_customUrl,
- new ComfyButton({
- icon: 'magnify',
- tooltip: 'Search models',
- classList: 'comfyui-button icon-button',
- action: async (e) => {
- const [button, icon, span] = comfyButtonDisambiguate(e.target);
- button.disabled = true;
- const value = el_customUrl.value;
- el_customUrlPreview.src = await getCustomPreviewUrl(value);
- e.stopPropagation();
- el_customUrl.blur();
- button.disabled = false;
- },
- }).element,
- ],
- );
-
- const el_previewButtons = $el(
- 'div.model-preview-overlay',
- {
- style: {
- display: el_defaultPreviews.children.length > 1 ? 'block' : 'none',
- },
- },
- [
- new ComfyButton({
- icon: 'arrow-left',
- tooltip: 'Previous image',
- classList: 'comfyui-button icon-button model-preview-button-left',
- action: () => this.stepDefaultPreviews(-1),
- }).element,
- new ComfyButton({
- icon: 'arrow-right',
- tooltip: 'Next image',
- classList: 'comfyui-button icon-button model-preview-button-right',
- action: () => this.stepDefaultPreviews(1),
- }).element,
- ],
- );
- const el_previews = $el(
- 'div.item',
- {
- $: (el) => (this.elements.previews = el),
- },
- [
- $el(
- 'div',
- {
- style: {
- width: '100%',
- height: '100%',
- },
- },
- [
- el_defaultPreviewNoImage,
- el_defaultPreviews,
- el_customUrlPreview,
- el_uploadPreview,
- ],
- ),
- el_previewButtons,
- ],
- );
-
- const el_radioButtons = $radioGroup({
- name: radioGroupName,
- onchange: (value) => {
- el_custom.style.display = 'none';
- el_upload.style.display = 'none';
-
- el_defaultPreviews.style.display = 'none';
- el_previewButtons.style.display = 'none';
-
- el_defaultPreviewNoImage.style.display = 'none';
- el_uploadPreview.style.display = 'none';
- el_customUrlPreview.style.display = 'none';
-
- switch (value) {
- case this.#PREVIEW_DEFAULT:
- el_defaultPreviews.style.display = 'block';
- el_previewButtons.style.display =
- el_defaultPreviews.children.length > 1 ? 'block' : 'none';
- break;
- case this.#PREVIEW_UPLOAD:
- el_upload.style.display = 'flex';
- el_uploadPreview.style.display = 'block';
- break;
- case this.#PREVIEW_URL:
- el_custom.style.display = 'flex';
- el_customUrlPreview.style.display = 'block';
- break;
- case this.#PREVIEW_NONE:
- default:
- el_defaultPreviewNoImage.style.display = 'block';
- break;
- }
- },
- options: [
- this.#PREVIEW_DEFAULT,
- this.#PREVIEW_URL,
- this.#PREVIEW_UPLOAD,
- this.#PREVIEW_NONE,
- ].map((value) => {
- return { value: value };
- }),
- });
- this.elements.radioButtons = el_radioButtons;
-
- const children = el_radioButtons.children;
- for (let i = 0; i < children.length; i++) {
- const child = children[i];
- const radioButton = child.children[0];
- if (radioButton.value === this.#PREVIEW_DEFAULT) {
- radioButton.checked = true;
- break;
- }
- }
-
- const el_radioGroup = $el(
- 'div.model-preview-select-radio-container',
- {
- $: (el) => (this.elements.radioGroup = el),
- },
- [
- $el('div.row.tab-header-flex-block', [el_radioButtons]),
- $el('div.model-preview-select-radio-inputs', [el_custom, el_upload]),
- ],
- );
- }
-}
-
-/**
- * @typedef {Object} DirectoryItem
- * @property {String} name
- * @property {number | undefined} childCount
- * @property {number | undefined} childIndex
- */
-
-class ModelDirectories {
- /** @type {DirectoryItem[]} */
- data = [];
-
- /**
- * @returns {number}
- */
- rootIndex() {
- return 0;
- }
-
- /**
- * @param {any} index
- * @returns {boolean}
- */
- isValidIndex(index) {
- return typeof index === 'number' && 0 <= index && index < this.data.length;
- }
-
- /**
- * @param {number} index
- * @returns {DirectoryItem}
- */
- getItem(index) {
- if (!this.isValidIndex(index)) {
- throw new Error(`Index '${index}' is not valid!`);
- }
- return this.data[index];
- }
-
- /**
- * @param {DirectoryItem | number} item
- * @returns {boolean}
- */
- isDirectory(item) {
- if (typeof item === 'number') {
- item = this.getItem(item);
- }
- const childCount = item.childCount;
- return childCount !== undefined && childCount != null;
- }
-
- /**
- * @param {DirectoryItem | number} item
- * @returns {boolean}
- */
- isEmpty(item) {
- if (typeof item === 'number') {
- item = this.getItem(item);
- }
- if (!this.isDirectory(item)) {
- throw new Error('Item is not a directory!');
- }
- return item.childCount === 0;
- }
-
- /**
- * Returns a slice of children from the directory list.
- * @param {DirectoryItem | number} item
- * @returns {DirectoryItem[]}
- */
- getChildren(item) {
- if (typeof item === 'number') {
- item = this.getItem(item);
- if (!this.isDirectory(item)) {
- throw new Error('Item is not a directory!');
- }
- } else if (!this.isDirectory(item)) {
- throw new Error('Item is not a directory!');
- }
- const count = item.childCount;
- const index = item.childIndex;
- return this.data.slice(index, index + count);
- }
-
- /**
- * Returns index of child in parent directory. Returns -1 if DNE.
- * @param {DirectoryItem | number} parent
- * @param {string} name
- * @returns {number}
- */
- findChildIndex(parent, name) {
- const item = this.getItem(parent);
- if (!this.isDirectory(item)) {
- throw new Error('Item is not a directory!');
- }
- const start = item.childIndex;
- const children = this.getChildren(item);
- const index = children.findIndex((item) => {
- return item.name === name;
- });
- if (index === -1) {
- return -1;
- }
- return index + start;
- }
-
- /**
- * Returns a list of matching search results and valid path.
- * @param {string} filter
- * @param {string} searchSeparator
- * @param {boolean} directoriesOnly
- * @returns {[string[], string]}
- */
- search(filter, searchSeparator, directoriesOnly) {
- let cwd = this.rootIndex();
- let indexLastWord = 1;
- while (true) {
- const indexNextWord = filter.indexOf(searchSeparator, indexLastWord);
- if (indexNextWord === -1) {
- // end of filter
- break;
- }
-
- const item = this.getItem(cwd);
- if (!this.isDirectory(item) || this.isEmpty(item)) {
- break;
- }
-
- const word = filter.substring(indexLastWord, indexNextWord);
- cwd = this.findChildIndex(cwd, word);
- if (!this.isValidIndex(cwd)) {
- return [[], ''];
- }
- indexLastWord = indexNextWord + 1;
- }
- //const cwdPath = filter.substring(0, indexLastWord);
-
- const lastWord = filter.substring(indexLastWord);
- const children = this.getChildren(cwd);
- if (directoriesOnly) {
- let indexPathEnd = indexLastWord;
- const results = children
- .filter((child) => {
- return this.isDirectory(child) && child.name.startsWith(lastWord);
- })
- .map((directory) => {
- const children = this.getChildren(directory);
- const hasChildren = children.some((item) => {
- return this.isDirectory(item);
- });
- const suffix = hasChildren ? searchSeparator : '';
- //const suffix = searchSeparator;
- if (directory.name == lastWord) {
- indexPathEnd += searchSeparator.length + directory.name.length + 1;
- }
- return directory.name + suffix;
- });
- const path = filter.substring(0, indexPathEnd);
- return [results, path];
- } else {
- let indexPathEnd = indexLastWord;
- const results = children
- .filter((child) => {
- return child.name.startsWith(lastWord);
- })
- .map((item) => {
- const isDir = this.isDirectory(item);
- const isNonEmptyDirectory = isDir && item.childCount > 0;
- const suffix = isNonEmptyDirectory ? searchSeparator : '';
- //const suffix = isDir ? searchSeparator : "";
- if (!isDir && item.name == lastWord) {
- indexPathEnd += searchSeparator.length + item.name.length + 1;
- }
- return item.name + suffix;
- });
- const path = filter.substring(0, indexPathEnd);
- return [results, path];
- }
- }
-}
-
-const DROPDOWN_DIRECTORY_SELECTION_KEY_CLASS =
- 'search-directory-dropdown-key-selected';
-const DROPDOWN_DIRECTORY_SELECTION_MOUSE_CLASS =
- 'search-directory-dropdown-mouse-selected';
-
-class ModelData {
- /** @type {string} */
- searchSeparator = '/'; // TODO: other client or server code may be assuming this to always be "/"
-
- /** @type {string} */
- systemSeparator = null;
-
- /** @type {Object} */
- models = {};
-
- /** @type {ModelDirectories} */
- directories = null;
-
- constructor() {
- this.directories = new ModelDirectories();
- }
-}
-
-class DirectoryDropdown {
- /** @type {HTMLDivElement} */
- element = null;
-
- /** @type {Boolean} */
- showDirectoriesOnly = false;
-
- /** @type {HTMLInputElement} */
- #input = null;
-
- /** @type {() => string} */
- #getModelType = null;
-
- /** @type {ModelData} */
- #modelData = null; // READ ONLY
-
- /** @type {() => void} */
- #updateCallback = null;
-
- /** @type {() => Promise} */
- #submitCallback = null;
-
- /** @type {string} */
- #deepestPreviousPath = '/';
-
- /** @type {Any} */
- #touchSelectionStart = null;
-
- /** @type {() => Boolean} */
- #isDynamicSearch = () => {
- return false;
- };
-
- /**
- * @param {ModelData} modelData
- * @param {HTMLInputElement} input
- * @param {Boolean} [showDirectoriesOnly=false]
- * @param {() => string} [getModelType= () => { return ""; }]
- * @param {() => void} [updateCallback= () => {}]
- * @param {() => Promise} [submitCallback= () => {}]
- * @param {() => Boolean} [isDynamicSearch= () => { return false; }]
- */
- constructor(
- modelData,
- input,
- showDirectoriesOnly = false,
- getModelType = () => {
- return '';
- },
- updateCallback = () => {},
- submitCallback = () => {},
- isDynamicSearch = () => {
- return false;
- },
- ) {
- /** @type {HTMLDivElement} */
- const dropdown = $el('div.search-directory-dropdown', {
- style: {
- display: 'none',
- },
- });
- this.element = dropdown;
- this.#modelData = modelData;
- this.#input = input;
- this.#getModelType = getModelType;
- this.#updateCallback = updateCallback;
- this.#submitCallback = submitCallback;
- this.showDirectoriesOnly = showDirectoriesOnly;
- this.#isDynamicSearch = isDynamicSearch;
-
- input.addEventListener('input', async (e) => {
- const path = this.#updateOptions();
- if (path !== undefined) {
- this.#restoreSelectedOption(path);
- this.#updateDeepestPath(path);
- }
- updateCallback();
- if (isDynamicSearch()) {
- await submitCallback();
- }
- });
- input.addEventListener('focus', () => {
- const path = this.#updateOptions();
- if (path !== undefined) {
- this.#deepestPreviousPath = path;
- this.#restoreSelectedOption(path);
- }
- updateCallback();
- });
- input.addEventListener('blur', () => {
- dropdown.style.display = 'none';
- });
- input.addEventListener('keydown', async (e) => {
- const options = dropdown.children;
- let iSelection;
- for (iSelection = 0; iSelection < options.length; iSelection++) {
- const selection = options[iSelection];
- if (
- selection.classList.contains(DROPDOWN_DIRECTORY_SELECTION_KEY_CLASS)
- ) {
- break;
- }
- }
- if (e.key === 'Escape') {
- e.stopPropagation();
- if (iSelection < options.length) {
- const selection = options[iSelection];
- selection.classList.remove(DROPDOWN_DIRECTORY_SELECTION_KEY_CLASS);
- } else {
- e.target.blur();
- }
- } else if (e.key === 'ArrowRight' && dropdown.style.display !== 'none') {
- const selection = options[iSelection];
- if (selection !== undefined && selection !== null) {
- e.stopPropagation();
- e.preventDefault(); // prevent cursor move
- const input = e.target;
- const searchSeparator = modelData.searchSeparator;
- DirectoryDropdown.selectionToInput(
- input,
- selection,
- searchSeparator,
- DROPDOWN_DIRECTORY_SELECTION_KEY_CLASS,
- );
- const path = this.#updateOptions();
- if (path !== undefined) {
- this.#restoreSelectedOption(path);
- this.#updateDeepestPath(path);
- }
- updateCallback();
- if (isDynamicSearch()) {
- await submitCallback();
- }
- }
- } else if (e.key === 'ArrowLeft' && dropdown.style.display !== 'none') {
- const input = e.target;
- const oldFilterText = input.value;
- const searchSeparator = modelData.searchSeparator;
- const iSep = oldFilterText.lastIndexOf(
- searchSeparator,
- oldFilterText.length - 2,
- );
- const newFilterText = oldFilterText.substring(0, iSep + 1);
- if (oldFilterText !== newFilterText) {
- const delta = oldFilterText.substring(iSep + 1);
- let isMatch = delta[delta.length - 1] === searchSeparator;
- if (!isMatch) {
- const options = dropdown.children;
- for (let i = 0; i < options.length; i++) {
- const option = options[i];
- if (option.innerText.startsWith(delta)) {
- isMatch = true;
- break;
- }
- }
- }
- if (isMatch) {
- e.stopPropagation();
- e.preventDefault(); // prevent cursor move
- input.value = newFilterText;
- const path = this.#updateOptions();
- if (path !== undefined) {
- this.#restoreSelectedOption(path);
- this.#updateDeepestPath(path);
- }
- updateCallback();
- if (isDynamicSearch()) {
- await submitCallback();
- }
- }
- }
- } else if (e.key === 'Enter') {
- e.stopPropagation();
- const input = e.target;
- if (dropdown.style.display !== 'none') {
- /*
- // This is WAY too confusing.
- const selection = options[iSelection];
- if (selection !== undefined && selection !== null) {
- DirectoryDropdown.selectionToInput(
- input,
- selection,
- modelData.searchSeparator,
- DROPDOWN_DIRECTORY_SELECTION_KEY_CLASS
- );
- const path = this.#updateOptions();
- if (path !== undefined) {
- this.#updateDeepestPath(path);
- }
- updateCallback();
- }
- */
- }
- await submitCallback();
- input.blur();
- } else if (
- (e.key === 'ArrowDown' || e.key === 'ArrowUp') &&
- dropdown.style.display !== 'none'
- ) {
- e.stopPropagation();
- e.preventDefault(); // prevent cursor move
- let iNext = options.length;
- if (iSelection < options.length) {
- const selection = options[iSelection];
- selection.classList.remove(DROPDOWN_DIRECTORY_SELECTION_KEY_CLASS);
- const delta = e.key === 'ArrowDown' ? 1 : -1;
- iNext = iSelection + delta;
- if (iNext < 0) {
- iNext = options.length - 1;
- } else if (iNext >= options.length) {
- iNext = 0;
- }
- const selectionNext = options[iNext];
- selectionNext.classList.add(DROPDOWN_DIRECTORY_SELECTION_KEY_CLASS);
- } else if (iSelection === options.length) {
- // none
- iNext = e.key === 'ArrowDown' ? 0 : options.length - 1;
- const selection = options[iNext];
- selection.classList.add(DROPDOWN_DIRECTORY_SELECTION_KEY_CLASS);
- }
- if (0 <= iNext && iNext < options.length) {
- DirectoryDropdown.#clampDropdownScrollTop(dropdown, options[iNext]);
- } else {
- dropdown.scrollTop = 0;
- const options = dropdown.children;
- for (iSelection = 0; iSelection < options.length; iSelection++) {
- const selection = options[iSelection];
- if (
- selection.classList.contains(
- DROPDOWN_DIRECTORY_SELECTION_KEY_CLASS,
- )
- ) {
- selection.classList.remove(
- DROPDOWN_DIRECTORY_SELECTION_KEY_CLASS,
- );
- }
- }
- }
- }
- });
- }
-
- /**
- * @param {HTMLInputElement} input
- * @param {HTMLParagraphElement | undefined | null} selection
- * @param {String} searchSeparator
- * @param {String} className
- * @returns {boolean} changed
- */
- static selectionToInput(input, selection, searchSeparator, className) {
- selection.classList.remove(className);
- const selectedText = selection.innerText;
- const oldFilterText = input.value;
- const iSep = oldFilterText.lastIndexOf(searchSeparator);
- const previousPath = oldFilterText.substring(0, iSep + 1);
- const newFilterText = previousPath + selectedText;
- input.value = newFilterText;
- return newFilterText !== oldFilterText;
- }
-
- /**
- * @param {string} path
- */
- #updateDeepestPath = (path) => {
- const deepestPath = this.#deepestPreviousPath;
- if (path.length > deepestPath.length || !deepestPath.startsWith(path)) {
- this.#deepestPreviousPath = path;
- }
- };
-
- /**
- * @param {HTMLDivElement} dropdown
- * @param {HTMLParagraphElement} selection
- */
- static #clampDropdownScrollTop = (dropdown, selection) => {
- let dropdownTop = dropdown.scrollTop;
- const dropdownHeight = dropdown.offsetHeight;
- const selectionHeight = selection.offsetHeight;
- const selectionTop = selection.offsetTop;
- dropdownTop = Math.max(
- dropdownTop,
- selectionTop - dropdownHeight + selectionHeight,
- );
- dropdownTop = Math.min(dropdownTop, selectionTop);
- dropdown.scrollTop = dropdownTop;
- };
-
- /**
- * @param {string} path
- */
- #restoreSelectedOption(path) {
- const searchSeparator = this.#modelData.searchSeparator;
- const deepest = this.#deepestPreviousPath;
- if (deepest.length >= path.length && deepest.startsWith(path)) {
- let name = deepest.substring(path.length);
- name = removePrefix(name, searchSeparator);
- const i1 = name.indexOf(searchSeparator);
- if (i1 !== -1) {
- name = name.substring(0, i1);
- }
-
- const dropdown = this.element;
- const options = dropdown.children;
- let iSelection;
- for (iSelection = 0; iSelection < options.length; iSelection++) {
- const selection = options[iSelection];
- let text = removeSuffix(selection.innerText, searchSeparator);
- if (text === name) {
- selection.classList.add(DROPDOWN_DIRECTORY_SELECTION_KEY_CLASS);
- dropdown.scrollTop = dropdown.scrollHeight; // snap to top
- DirectoryDropdown.#clampDropdownScrollTop(dropdown, selection);
- break;
- }
- }
- if (iSelection === options.length) {
- dropdown.scrollTop = 0;
- }
- }
- }
-
- /**
- * Returns path if update was successful.
- * @returns {string | undefined}
- */
- #updateOptions() {
- const dropdown = this.element;
- const input = this.#input;
-
- const searchSeparator = this.#modelData.searchSeparator;
- const filter = input.value;
- if (filter[0] !== searchSeparator) {
- dropdown.style.display = 'none';
- return undefined;
- }
-
- const modelType = this.#getModelType();
- const searchPrefix = modelType !== '' ? searchSeparator + modelType : '';
- const directories = this.#modelData.directories;
- const [options, path] = directories.search(
- searchPrefix + filter,
- searchSeparator,
- this.showDirectoriesOnly,
- );
- if (options.length === 0) {
- dropdown.style.display = 'none';
- return undefined;
- }
-
- const mouse_selection_select = (e) => {
- const selection = e.target;
- if (e.movementX === 0 && e.movementY === 0) {
- return;
- }
- if (
- !selection.classList.contains(DROPDOWN_DIRECTORY_SELECTION_MOUSE_CLASS)
- ) {
- // assumes only one will ever selected at a time
- e.stopPropagation();
- const children = dropdown.children;
- for (let iChild = 0; iChild < children.length; iChild++) {
- const child = children[iChild];
- child.classList.remove(DROPDOWN_DIRECTORY_SELECTION_MOUSE_CLASS);
- }
- selection.classList.add(DROPDOWN_DIRECTORY_SELECTION_MOUSE_CLASS);
- }
- };
- const mouse_selection_deselect = (e) => {
- e.stopPropagation();
- e.target.classList.remove(DROPDOWN_DIRECTORY_SELECTION_MOUSE_CLASS);
- };
- const selection_submit = async (e) => {
- e.stopPropagation();
- e.preventDefault();
- const selection = e.target;
- const changed = DirectoryDropdown.selectionToInput(
- input,
- selection,
- searchSeparator,
- DROPDOWN_DIRECTORY_SELECTION_MOUSE_CLASS,
- );
- if (!changed) {
- dropdown.style.display = 'none';
- input.blur();
- } else {
- const path = this.#updateOptions(); // TODO: is this needed?
- if (path !== undefined) {
- this.#updateDeepestPath(path);
- }
- }
- this.#updateCallback();
- if (this.#isDynamicSearch()) {
- await this.#submitCallback();
- }
- };
- const touch_selection_select = async (e) => {
- const [startX, startY] = this.#touchSelectionStart;
- const [endX, endY] = [
- e.changedTouches[0].clientX,
- e.changedTouches[0].clientY,
- ];
- if (startX === endX && startY === endY) {
- const touch = e.changedTouches[0];
- const box = dropdown.getBoundingClientRect();
- if (
- touch.clientX >= box.left &&
- touch.clientX <= box.right &&
- touch.clientY >= box.top &&
- touch.clientY <= box.bottom
- ) {
- selection_submit(e);
- }
- }
- };
- const touch_start = (e) => {
- this.#touchSelectionStart = [
- e.changedTouches[0].clientX,
- e.changedTouches[0].clientY,
- ];
- };
- dropdown.innerHTML = '';
- dropdown.append.apply(
- dropdown,
- options.map((text) => {
- /** @type {HTMLParagraphElement} */
- const p = $el(
- 'p',
- {
- onmouseenter: (e) => mouse_selection_select(e),
- onmousemove: (e) => mouse_selection_select(e),
- onmouseleave: (e) => mouse_selection_deselect(e),
- onmousedown: (e) => selection_submit(e),
- ontouchstart: (e) => touch_start(e),
- ontouchmove: (e) => touch_move(e),
- ontouchend: (e) => touch_selection_select(e),
- },
- [text],
- );
- return p;
- }),
- );
- // TODO: handle when dropdown is near the bottom of the window
- const inputRect = input.getBoundingClientRect();
- dropdown.style.width = inputRect.width + 'px';
- dropdown.style.top = input.offsetTop + inputRect.height + 'px';
- dropdown.style.left = input.offsetLeft + 'px';
- dropdown.style.display = 'block';
-
- return path;
- }
-}
-
-const MODEL_SORT_DATE_CREATED = 'dateCreated';
-const MODEL_SORT_DATE_MODIFIED = 'dateModified';
-const MODEL_SORT_SIZE_BYTES = 'sizeBytes';
-const MODEL_SORT_DATE_NAME = 'name';
-
-class ModelGrid {
- /**
- * @param {string} nodeType
- * @returns {int}
- */
- static modelWidgetIndex(nodeType) {
- return nodeType === undefined ? -1 : 0;
- }
-
- /**
- * @param {string} text
- * @param {string} file
- * @param {boolean} removeExtension
- * @returns {string}
- */
- static insertEmbeddingIntoText(text, file, removeExtension) {
- let name = file;
- if (removeExtension) {
- name = SearchPath.splitExtension(name)[0];
- }
- const sep = text.length === 0 || text.slice(-1).match(/\s/) ? '' : ' ';
- return text + sep + '(embedding:' + name + ':1.0)';
- }
-
- /**
- * @param {Array} list
- * @param {string} searchString
- * @returns {Array}
- */
- static #filter(list, searchString) {
- /** @type {string[]} */
- const keywords = searchString
- //.replace("*", " ") // TODO: this is wrong for wildcards
- .split(/(-?".*?"|[^\s"]+)+/g)
- .map((item) =>
- item
- .trim()
- .replace(/(?:")+/g, '')
- .toLowerCase(),
- )
- .filter(Boolean);
-
- const regexSHA256 = /^[a-f0-9]{64}$/gi;
- const fields = ['name', 'path'];
- return list.filter((element) => {
- const text = fields
- .reduce((memo, field) => memo + ' ' + element[field], '')
- .toLowerCase();
- return keywords.reduce((memo, target) => {
- const excludeTarget = target[0] === '-';
- if (excludeTarget && target.length === 1) {
- return memo;
- }
- const filteredTarget = excludeTarget ? target.slice(1) : target;
- if (
- element['SHA256'] !== undefined &&
- regexSHA256.test(filteredTarget)
- ) {
- return (
- memo && excludeTarget !== (filteredTarget === element['SHA256'])
- );
- } else {
- return memo && excludeTarget !== text.includes(filteredTarget);
- }
- }, true);
- });
- }
-
- /**
- * In-place sort. Returns an array alias.
- * @param {Array} list
- * @param {string} sortBy
- * @param {bool} [reverse=false]
- * @returns {Array}
- */
- static #sort(list, sortBy, reverse = false) {
- let compareFn = null;
- switch (sortBy) {
- case MODEL_SORT_DATE_NAME:
- compareFn = (a, b) => {
- return a[MODEL_SORT_DATE_NAME].localeCompare(b[MODEL_SORT_DATE_NAME]);
- };
- break;
- case MODEL_SORT_DATE_MODIFIED:
- compareFn = (a, b) => {
- return b[MODEL_SORT_DATE_MODIFIED] - a[MODEL_SORT_DATE_MODIFIED];
- };
- break;
- case MODEL_SORT_DATE_CREATED:
- compareFn = (a, b) => {
- return b[MODEL_SORT_DATE_CREATED] - a[MODEL_SORT_DATE_CREATED];
- };
- break;
- case MODEL_SORT_SIZE_BYTES:
- compareFn = (a, b) => {
- return b[MODEL_SORT_SIZE_BYTES] - a[MODEL_SORT_SIZE_BYTES];
- };
- break;
- default:
- console.warn("Invalid filter sort value: '" + sortBy + "'");
- return list;
- }
- const sorted = list.sort(compareFn);
- return reverse ? sorted.reverse() : sorted;
- }
-
- /**
- * @param {Event} event
- * @param {string} modelType
- * @param {string} path
- * @param {boolean} removeEmbeddingExtension
- * @param {int} addOffset
- */
- static #addModel(
- event,
- modelType,
- path,
- removeEmbeddingExtension,
- addOffset,
- ) {
- let success = false;
- if (modelType !== 'embeddings') {
- const nodeType = modelNodeType[modelType];
- const widgetIndex = ModelGrid.modelWidgetIndex(nodeType);
- let node = LiteGraph.createNode(nodeType, null, []);
- if (widgetIndex !== -1 && node) {
- node.widgets[widgetIndex].value = path;
- const selectedNodes = app.canvas.selected_nodes;
- let isSelectedNode = false;
- for (var i in selectedNodes) {
- const selectedNode = selectedNodes[i];
- node.pos[0] = selectedNode.pos[0] + addOffset;
- node.pos[1] = selectedNode.pos[1] + addOffset;
- isSelectedNode = true;
- break;
- }
- if (!isSelectedNode) {
- const graphMouse = app.canvas.graph_mouse;
- node.pos[0] = graphMouse[0];
- node.pos[1] = graphMouse[1];
- }
- app.graph.add(node, { doProcessChange: true });
- app.canvas.selectNode(node);
- success = true;
- }
- event.stopPropagation();
- } else if (modelType === 'embeddings') {
- const [embeddingDirectory, embeddingFile] = SearchPath.split(path);
- const selectedNodes = app.canvas.selected_nodes;
- for (var i in selectedNodes) {
- const selectedNode = selectedNodes[i];
- const nodeType = modelNodeType[modelType];
- const widgetIndex = ModelGrid.modelWidgetIndex(nodeType);
- const target = selectedNode?.widgets[widgetIndex]?.element;
- if (target && target.type === 'textarea') {
- target.value = ModelGrid.insertEmbeddingIntoText(
- target.value,
- embeddingFile,
- removeEmbeddingExtension,
- );
- success = true;
- }
- }
- if (!success) {
- console.warn('Try selecting a node before adding the embedding.');
- }
- event.stopPropagation();
- }
- comfyButtonAlert(
- event.target,
- success,
- 'mdi-check-bold',
- 'mdi-close-thick',
- );
- }
-
- static #getWidgetComboIndices(node, value) {
- const widgetIndices = [];
- node?.widgets?.forEach((widget, index) => {
- if (widget.type === 'combo' && widget.options.values?.includes(value)) {
- widgetIndices.push(index);
- }
- });
- return widgetIndices;
- }
-
- /**
- * @param {DragEvent} event
- * @param {string} modelType
- * @param {string} path
- * @param {boolean} removeEmbeddingExtension
- * @param {boolean} strictlyOnWidget
- */
- static #dragAddModel(
- event,
- modelType,
- path,
- removeEmbeddingExtension,
- strictlyOnWidget,
- ) {
- const [clientX, clientY, target] = elementFromDragEvent(event);
- if (modelType !== 'embeddings' && target.id === 'graph-canvas') {
- //const pos = app.canvas.convertEventToCanvasOffset(event);
- const pos = app.canvas.convertEventToCanvasOffset({
- clientX: clientX,
- clientY: clientY,
- });
-
- const node = app.graph.getNodeOnPos(
- pos[0],
- pos[1],
- app.canvas.visible_nodes,
- );
-
- let widgetIndex = -1;
- if (widgetIndex === -1) {
- const widgetIndices = this.#getWidgetComboIndices(node, path);
- if (widgetIndices.length === 0) {
- widgetIndex = -1;
- } else if (widgetIndices.length === 1) {
- widgetIndex = widgetIndices[0];
- if (strictlyOnWidget) {
- const draggedWidget = app.canvas.processNodeWidgets(
- node,
- pos,
- event,
- );
- const widget = node.widgets[widgetIndex];
- if (draggedWidget != widget) {
- // != check NOT same object
- widgetIndex = -1;
- }
- }
- } else {
- // ambiguous widget (strictlyOnWidget always true)
- const draggedWidget = app.canvas.processNodeWidgets(node, pos, event);
- widgetIndex = widgetIndices.findIndex((index) => {
- return draggedWidget == node.widgets[index]; // == check same object
- });
- }
- }
-
- if (widgetIndex !== -1) {
- node.widgets[widgetIndex].value = path;
- app.canvas.selectNode(node);
- } else {
- const expectedNodeType = modelNodeType[modelType];
- const newNode = LiteGraph.createNode(expectedNodeType, null, []);
- let newWidgetIndex = ModelGrid.modelWidgetIndex(expectedNodeType);
- if (newWidgetIndex === -1) {
- newWidgetIndex = this.#getWidgetComboIndices(newNode, path)[0] ?? -1;
- }
- if (
- newNode !== undefined &&
- newNode !== null &&
- newWidgetIndex !== -1
- ) {
- newNode.pos[0] = pos[0];
- newNode.pos[1] = pos[1];
- newNode.widgets[newWidgetIndex].value = path;
- app.graph.add(newNode, { doProcessChange: true });
- app.canvas.selectNode(newNode);
- }
- }
- event.stopPropagation();
- } else if (modelType === 'embeddings' && target.type === 'textarea') {
- const pos = app.canvas.convertEventToCanvasOffset(event);
- const nodeAtPos = app.graph.getNodeOnPos(
- pos[0],
- pos[1],
- app.canvas.visible_nodes,
- );
- if (nodeAtPos) {
- app.canvas.selectNode(nodeAtPos);
- const [embeddingDirectory, embeddingFile] = SearchPath.split(path);
- target.value = ModelGrid.insertEmbeddingIntoText(
- target.value,
- embeddingFile,
- removeEmbeddingExtension,
- );
- event.stopPropagation();
- }
- }
- }
-
- /**
- * @param {Event} event
- * @param {string} modelType
- * @param {string} path
- * @param {boolean} removeEmbeddingExtension
- */
- static #copyModelToClipboard(
- event,
- modelType,
- path,
- removeEmbeddingExtension,
- ) {
- const nodeType = modelNodeType[modelType];
- let success = false;
- if (nodeType === 'Embedding') {
- if (navigator.clipboard) {
- const [embeddingDirectory, embeddingFile] = SearchPath.split(path);
- const embeddingText = ModelGrid.insertEmbeddingIntoText(
- '',
- embeddingFile,
- removeEmbeddingExtension,
- );
- navigator.clipboard.writeText(embeddingText);
- success = true;
- } else {
- console.warn(
- 'Cannot copy the embedding to the system clipboard; Try dragging it instead.',
- );
- }
- } else if (nodeType) {
- const node = LiteGraph.createNode(nodeType, null, []);
- const widgetIndex = ModelGrid.modelWidgetIndex(nodeType);
- if (widgetIndex !== -1) {
- node.widgets[widgetIndex].value = path;
- app.canvas.copyToClipboard([node]);
- success = true;
- }
- } else {
- console.warn(`Unable to copy unknown model type '${modelType}.`);
- }
- comfyButtonAlert(
- event.target,
- success,
- 'mdi-check-bold',
- 'mdi-close-thick',
- );
- }
-
- /**
- * @param {Array} models
- * @param {string} modelType
- * @param {Object.} settingsElements
- * @param {String} searchSeparator
- * @param {String} systemSeparator
- * @param {(searchPath: string) => Promise} showModelInfo
- * @returns {HTMLElement[]}
- */
- static #generateInnerHtml(
- models,
- modelType,
- settingsElements,
- searchSeparator,
- systemSeparator,
- showModelInfo,
- ) {
- // TODO: separate text and model logic; getting too messy
- // TODO: fallback on button failure to copy text?
- const canShowButtons = modelNodeType[modelType] !== undefined;
- const showAddButton =
- canShowButtons && settingsElements['model-show-add-button'].checked;
- const showCopyButton =
- canShowButtons && settingsElements['model-show-copy-button'].checked;
- const showLoadWorkflowButton =
- canShowButtons &&
- settingsElements['model-show-load-workflow-button'].checked;
- const strictDragToAdd =
- settingsElements['model-add-drag-strict-on-field'].checked;
- const addOffset = parseInt(settingsElements['model-add-offset'].value);
- const showModelExtension =
- settingsElements['model-show-label-extensions'].checked;
- const modelInfoButtonOnLeft =
- !settingsElements['model-info-button-on-left'].checked;
- const removeEmbeddingExtension =
- !settingsElements['model-add-embedding-extension'].checked;
- const previewThumbnailFormat =
- settingsElements['model-preview-thumbnail-type'].value;
- if (models.length > 0) {
- return models.map((item) => {
- const previewInfo = item.preview;
- const previewThumbnail = $el('img.model-preview', {
- loading:
- 'lazy' /* `loading` BEFORE `src`; Known bug in Firefox 124.0.2 and Safari for iOS 17.4.1 (https://stackoverflow.com/a/76252772) */,
- src: imageUri(
- previewInfo?.path,
- previewInfo?.dateModified,
- PREVIEW_THUMBNAIL_WIDTH,
- PREVIEW_THUMBNAIL_HEIGHT,
- previewThumbnailFormat,
- ),
- draggable: false,
- });
- const searchPath = item.path;
- const path = SearchPath.systemPath(
- searchPath,
- searchSeparator,
- systemSeparator,
- );
- let actionButtons = [];
- if (
- showAddButton &&
- !(modelType === 'embeddings' && !navigator.clipboard)
- ) {
- actionButtons.push(
- new ComfyButton({
- icon: 'content-copy',
- tooltip: 'Copy model to clipboard',
- classList: 'comfyui-button icon-button model-button',
- action: (e) =>
- ModelGrid.#copyModelToClipboard(
- e,
- modelType,
- path,
- removeEmbeddingExtension,
- ),
- }).element,
- );
- }
- if (showCopyButton) {
- actionButtons.push(
- new ComfyButton({
- icon: 'plus-box-outline',
- tooltip: 'Add model to node grid',
- classList: 'comfyui-button icon-button model-button',
- action: (e) =>
- ModelGrid.#addModel(
- e,
- modelType,
- path,
- removeEmbeddingExtension,
- addOffset,
- ),
- }).element,
- );
- }
- if (showLoadWorkflowButton) {
- actionButtons.push(
- new ComfyButton({
- icon: 'arrow-bottom-left-bold-box-outline',
- tooltip: 'Load preview workflow',
- classList: 'comfyui-button icon-button model-button',
- action: async (e) => {
- const urlString = previewThumbnail.src;
- const url = new URL(urlString);
- const urlSearchParams = url.searchParams;
- const uri = urlSearchParams.get('uri');
- const v = urlSearchParams.get('v');
- const urlFull =
- urlString.substring(0, urlString.indexOf('?')) +
- '?uri=' +
- uri +
- '&v=' +
- v;
- await loadWorkflow(urlFull);
- },
- }).element,
- );
- }
- const infoButtons = [
- new ComfyButton({
- icon: 'information-outline',
- tooltip: 'View model information',
- classList: 'comfyui-button icon-button model-button',
- action: async () => {
- await showModelInfo(searchPath);
- },
- }).element,
- ];
- const dragAdd = (e) =>
- ModelGrid.#dragAddModel(
- e,
- modelType,
- path,
- removeEmbeddingExtension,
- strictDragToAdd,
- );
- return $el('div.item', {}, [
- previewThumbnail,
- $el('div.model-preview-overlay', {
- ondragend: (e) => dragAdd(e),
- draggable: true,
- }),
- $el(
- 'div.model-preview-top-right',
- {
- draggable: false,
- },
- modelInfoButtonOnLeft ? infoButtons : actionButtons,
- ),
- $el(
- 'div.model-preview-top-left',
- {
- draggable: false,
- },
- modelInfoButtonOnLeft ? actionButtons : infoButtons,
- ),
- $el(
- 'div.model-label',
- {
- draggable: false,
- },
- [
- $el('p', [
- showModelExtension
- ? item.name
- : SearchPath.splitExtension(item.name)[0],
- ]),
- ],
- ),
- ]);
- });
- } else {
- return [$el('h2', ['No Models'])];
- }
- }
-
- /**
- * @param {HTMLDivElement} modelGrid
- * @param {ModelData} modelData
- * @param {HTMLSelectElement} modelSelect
- * @param {Object.<{value: string}>} previousModelType
- * @param {Object} settings
- * @param {string} sortBy
- * @param {boolean} reverseSort
- * @param {Array} previousModelFilters
- * @param {HTMLInputElement} modelFilter
- * @param {(searchPath: string) => Promise} showModelInfo
- */
- static update(
- modelGrid,
- modelData,
- modelSelect,
- previousModelType,
- settings,
- sortBy,
- reverseSort,
- previousModelFilters,
- modelFilter,
- showModelInfo,
- ) {
- const models = modelData.models;
- let modelType = modelSelect.value;
- if (models[modelType] === undefined) {
- modelType = settings['model-default-browser-model-type'].value;
- }
- if (models[modelType] === undefined) {
- modelType = 'checkpoints'; // panic fallback
- }
-
- if (modelType !== previousModelType.value) {
- if (settings['model-persistent-search'].checked) {
- previousModelFilters.splice(0, previousModelFilters.length); // TODO: make sure this actually worked!
- } else {
- // cache previous filter text
- previousModelFilters[previousModelType.value] = modelFilter.value;
- // read cached filter text
- modelFilter.value = previousModelFilters[modelType] ?? '';
- }
- previousModelType.value = modelType;
- }
-
- let modelTypeOptions = [];
- for (const [key, value] of Object.entries(models)) {
- const el = $el('option', [key]);
- modelTypeOptions.push(el);
- }
- modelSelect.innerHTML = '';
- modelTypeOptions.forEach((option) => modelSelect.add(option));
- modelSelect.value = modelType;
-
- const searchAppend = settings['model-search-always-append'].value;
- const searchText = modelFilter.value + ' ' + searchAppend;
- const modelList = ModelGrid.#filter(models[modelType], searchText);
- ModelGrid.#sort(modelList, sortBy, reverseSort);
-
- modelGrid.innerHTML = '';
- const modelGridModels = ModelGrid.#generateInnerHtml(
- modelList,
- modelType,
- settings,
- modelData.searchSeparator,
- modelData.systemSeparator,
- showModelInfo,
- );
- modelGrid.append.apply(modelGrid, modelGridModels);
- }
-}
-
-class ModelInfo {
- /** @type {HTMLDivElement} */
- element = null;
-
- elements = {
- /** @type {HTMLDivElement[]} */ tabButtons: null,
- /** @type {HTMLDivElement[]} */ tabContents: null,
- /** @type {HTMLDivElement} */ info: null,
- /** @type {HTMLTextAreaElement} */ notes: null,
- /** @type {HTMLButtonElement} */ setPreviewButton: null,
- /** @type {HTMLInputElement} */ moveDestinationInput: null,
- };
-
- /** @type {ImageSelect} */
- previewSelect = null;
-
- /** @type {string} */
- #savedNotesValue = null;
-
- /** @type {[HTMLElement][]} */
- #settingsElements = null;
-
- /**
- * @param {ModelData} modelData
- * @param {() => Promise} updateModels
- * @param {any} settingsElements
- */
- constructor(modelData, updateModels, settingsElements) {
- this.#settingsElements = settingsElements;
- const moveDestinationInput = $el('input.search-text-area', {
- name: 'move directory',
- autocomplete: 'off',
- placeholder: modelData.searchSeparator,
- value: modelData.searchSeparator,
- });
- this.elements.moveDestinationInput = moveDestinationInput;
-
- const searchDropdown = new DirectoryDropdown(
- modelData,
- moveDestinationInput,
- true,
- );
-
- const previewSelect = new ImageSelect('model-info-preview-model-FYUIKMNVB');
- this.previewSelect = previewSelect;
- previewSelect.elements.previews.style.display = 'flex';
-
- const setPreviewButton = new ComfyButton({
- tooltip: 'Overwrite current preview with selected image',
- content: 'Set as Preview',
- action: async (e) => {
- const [button, icon, span] = comfyButtonDisambiguate(e.target);
- button.disabled = true;
- const confirmation = window.confirm(
- 'Change preview image(s) PERMANENTLY?',
- );
- let updatedPreview = false;
- if (confirmation) {
- const container = this.elements.info;
- const path = container.dataset.path;
- const imageUrl = await previewSelect.getImage();
- if (imageUrl === PREVIEW_NONE_URI) {
- const encodedPath = encodeURIComponent(path);
- updatedPreview = await comfyRequest(
- `/model-manager/preview/delete?path=${encodedPath}`,
- {
- method: 'POST',
- body: JSON.stringify({}),
- },
- )
- .then((result) => {
- const message = result['alert'];
- if (message !== undefined) {
- window.alert(message);
- }
- return result['success'];
- })
- .catch((err) => {
- return false;
- });
- } else {
- const formData = new FormData();
- formData.append('path', path);
- const image = imageUrl[0] == '/' ? '' : imageUrl;
- formData.append('image', image);
- updatedPreview = await comfyRequest(`/model-manager/preview/set`, {
- method: 'POST',
- body: formData,
- })
- .then((result) => {
- const message = result['alert'];
- if (message !== undefined) {
- window.alert(message);
- }
- return result['success'];
- })
- .catch((err) => {
- return false;
- });
- }
- if (updatedPreview) {
- updateModels();
- const previewSelect = this.previewSelect;
- previewSelect.elements.defaultUrl.dataset.noimage =
- PREVIEW_NONE_URI;
- previewSelect.resetModelInfoPreview();
- this.element.style.display = 'none';
- }
- }
- comfyButtonAlert(e.target, updatedPreview);
- button.disabled = false;
- },
- }).element;
- this.elements.setPreviewButton = setPreviewButton;
- previewSelect.elements.radioButtons.addEventListener('change', (e) => {
- setPreviewButton.style.display = previewSelect.defaultIsChecked()
- ? 'none'
- : 'block';
- });
-
- this.element = $el(
- 'div',
- {
- style: { display: 'none' },
- },
- [
- $el(
- 'div.row.tab-header',
- {
- display: 'block',
- },
- [
- $el('div.row.tab-header-flex-block', [
- new ComfyButton({
- icon: 'trash-can-outline',
- tooltip: 'Delete model FOREVER',
- classList: 'comfyui-button icon-button',
- action: async (e) => {
- const [button, icon, span] = comfyButtonDisambiguate(
- e.target,
- );
- button.disabled = true;
- const affirmation = 'delete';
- const confirmation = window.prompt(
- 'Type "' +
- affirmation +
- '" to delete the model PERMANENTLY.\n\nThis includes all image or text files.',
- );
- let deleted = false;
- if (confirmation === affirmation) {
- const container = this.elements.info;
- const path = encodeURIComponent(container.dataset.path);
- deleted = await comfyRequest(
- `/model-manager/model/delete?path=${path}`,
- {
- method: 'POST',
- },
- )
- .then((result) => {
- const deleted = result['success'];
- const message = result['alert'];
- if (message !== undefined) {
- window.alert(message);
- }
- if (deleted) {
- container.innerHTML = '';
- this.element.style.display = 'none';
- updateModels();
- }
- return deleted;
- })
- .catch((err) => {
- return false;
- });
- }
- if (!deleted) {
- comfyButtonAlert(e.target, false);
- }
- button.disabled = false;
- },
- }).element,
- $el('div.search-models.input-dropdown-container', [
- // TODO: magic class
- moveDestinationInput,
- searchDropdown.element,
- ]),
- new ComfyButton({
- icon: 'file-move-outline',
- tooltip: 'Move file',
- action: async (e) => {
- const [button, icon, span] = comfyButtonDisambiguate(
- e.target,
- );
- button.disabled = true;
- const confirmation = window.confirm('Move this file?');
- let moved = false;
- if (confirmation) {
- const container = this.elements.info;
- const oldFile = container.dataset.path;
- const [oldFilePath, oldFileName] =
- SearchPath.split(oldFile);
- const newFile =
- moveDestinationInput.value +
- modelData.searchSeparator +
- oldFileName;
- moved = await comfyRequest(`/model-manager/model/move`, {
- method: 'POST',
- body: JSON.stringify({
- oldFile: oldFile,
- newFile: newFile,
- }),
- })
- .then((result) => {
- const moved = result['success'];
- const message = result['alert'];
- if (message !== undefined) {
- window.alert(message);
- }
- if (moved) {
- moveDestinationInput.value = '';
- container.innerHTML = '';
- this.element.style.display = 'none';
- updateModels();
- }
- return moved;
- })
- .catch((err) => {
- return false;
- });
- }
- comfyButtonAlert(e.target, moved);
- button.disabled = false;
- },
- }).element,
- ]),
- ],
- ),
- $el('div.model-info-container', {
- $: (el) => (this.elements.info = el),
- 'data-path': '',
- }),
- ],
- );
-
- [this.elements.tabButtons, this.elements.tabContents] = GenerateTabGroup([
- {
- name: 'Overview',
- icon: 'information-box-outline',
- tabContent: this.element,
- },
- {
- name: 'Metadata',
- icon: 'file-document-outline',
- tabContent: $el('div', ['Metadata']),
- },
- {
- name: 'Tags',
- icon: 'tag-outline',
- tabContent: $el('div', ['Tags']),
- },
- {
- name: 'Notes',
- icon: 'pencil-outline',
- tabContent: $el('div', ['Notes']),
- },
- ]);
- }
-
- /** @returns {void} */
- show() {
- this.element.style = '';
- this.element.scrollTop = 0;
- }
-
- /**
- * @param {boolean} promptUser
- * @returns {Promise}
- */
- async trySave(promptUser) {
- if (this.element.style.display === 'none') {
- return true;
- }
-
- const noteValue = this.elements.notes.value;
- const savedNotesValue = this.#savedNotesValue;
- if (noteValue.trim() === savedNotesValue.trim()) {
- return true;
- }
- const saveChanges = !promptUser || window.confirm('Save notes?');
- if (saveChanges) {
- const path = this.elements.info.dataset.path;
- const saved = await saveNotes(path, noteValue);
- if (!saved) {
- window.alert('Failed to save notes!');
- return false;
- }
- this.#savedNotesValue = noteValue;
- this.elements.markdown.innerHTML = marked.parse(noteValue);
- } else {
- const discardChanges = window.confirm('Discard changes?');
- if (!discardChanges) {
- return false;
- } else {
- this.elements.notes.value = savedNotesValue;
- }
- }
- return true;
- }
-
- /**
- * @param {boolean?} promptSave
- * @returns {Promise}
- */
- async tryHide(promptSave = true) {
- const notes = this.elements.notes;
- if (promptSave && notes !== undefined && notes !== null) {
- const saved = await this.trySave(promptSave);
- if (!saved) {
- return false;
- }
- this.#savedNotesValue = '';
- this.elements.notes.value = '';
- }
- this.element.style.display = 'none';
- return true;
- }
-
- /**
- * @param {string} searchPath
- * @param {() => Promise} updateModels
- * @param {string} searchSeparator
- */
- async update(searchPath, updateModels, searchSeparator) {
- const path = encodeURIComponent(searchPath);
- const [info, metadata, tags, noteText] = await comfyRequest(
- `/model-manager/model/info?path=${path}`,
- )
- .then((result) => {
- const success = result['success'];
- const message = result['alert'];
- if (message !== undefined) {
- window.alert(message);
- }
- if (!success) {
- return undefined;
- }
- return [
- result['info'],
- result['metadata'],
- result['tags'],
- result['notes'],
- ];
- })
- .catch((err) => {
- console.log(err);
- return undefined;
- });
- if (info === undefined || info === null) {
- return;
- }
- const infoHtml = this.elements.info;
- infoHtml.innerHTML = '';
- infoHtml.dataset.path = searchPath;
- const innerHtml = [];
- const filename = info['File Name'];
- if (filename !== undefined && filename !== null && filename !== '') {
- innerHtml.push(
- $el(
- 'div.row',
- {
- style: { margin: '8px 0 16px 0' },
- },
- [
- $el(
- 'h1',
- {
- style: { margin: '0' },
- },
- [filename],
- ),
- $el('div', [
- new ComfyButton({
- icon: 'pencil',
- tooltip: 'Change file name',
- classList: 'comfyui-button icon-button',
- action: async (e) => {
- const [button, icon, span] = comfyButtonDisambiguate(
- e.target,
- );
- button.disabled = true;
- const container = this.elements.info;
- const oldFile = container.dataset.path;
- const [oldFilePath, oldFileName] = SearchPath.split(oldFile);
- const oldName = SearchPath.splitExtension(oldFileName)[0];
- const newName = window.prompt('New model name:', oldName);
- let renamed = false;
- if (
- newName !== null &&
- newName !== '' &&
- newName != oldName
- ) {
- const newFile =
- oldFilePath +
- searchSeparator +
- newName +
- SearchPath.splitExtension(oldFile)[1];
- renamed = await comfyRequest(`/model-manager/model/move`, {
- method: 'POST',
- body: JSON.stringify({
- oldFile: oldFile,
- newFile: newFile,
- }),
- })
- .then((result) => {
- const renamed = result['success'];
- const message = result['alert'];
- if (message !== undefined) {
- window.alert(message);
- }
- if (renamed) {
- container.innerHTML = '';
- this.element.style.display = 'none';
- updateModels();
- }
- return renamed;
- })
- .catch((err) => {
- console.log(err);
- return false;
- });
- }
- comfyButtonAlert(e.target, renamed);
- button.disabled = false;
- },
- }).element,
- ]),
- ],
- ),
- );
- }
-
- const fileDirectory = info['File Directory'];
- if (
- fileDirectory !== undefined &&
- fileDirectory !== null &&
- fileDirectory !== ''
- ) {
- this.elements.moveDestinationInput.placeholder = fileDirectory;
- this.elements.moveDestinationInput.value = fileDirectory; // TODO: noise vs convenience
- } else {
- this.elements.moveDestinationInput.placeholder = searchSeparator;
- this.elements.moveDestinationInput.value = searchSeparator;
- }
-
- const previewSelect = this.previewSelect;
- const defaultUrl = previewSelect.elements.defaultUrl;
- if (info['Preview']) {
- const imagePath = info['Preview']['path'];
- const imageDateModified = info['Preview']['dateModified'];
- defaultUrl.dataset.noimage = imageUri(imagePath, imageDateModified);
- } else {
- defaultUrl.dataset.noimage = PREVIEW_NONE_URI;
- }
- previewSelect.resetModelInfoPreview();
- const setPreviewButton = this.elements.setPreviewButton;
- setPreviewButton.style.display = previewSelect.defaultIsChecked()
- ? 'none'
- : 'block';
-
- innerHtml.push(
- $el('div', [
- previewSelect.elements.previews,
- $el('div.row.tab-header', [
- $el('div', [
- new ComfyButton({
- content: 'Load Workflow',
- tooltip: 'Attempt to load preview image workflow',
- action: async () => {
- const urlString =
- previewSelect.elements.defaultPreviews.children[0].src;
- await loadWorkflow(urlString);
- },
- }).element,
- ]),
- $el('div.row.tab-header-flex-block', [
- previewSelect.elements.radioGroup,
- ]),
- $el('div.row.tab-header-flex-block', [setPreviewButton]),
- ]),
- $el('h2', ['File Info:']),
- $el(
- 'div',
- (() => {
- const elements = [];
- for (const [key, value] of Object.entries(info)) {
- if (value === undefined || value === null) {
- continue;
- }
-
- if (Array.isArray(value)) {
- // currently only used for "Bucket Resolutions"
- if (value.length > 0) {
- elements.push($el('h2', [key + ':']));
- const text = TagCountMapToParagraph(value);
- const div = $el('div');
- div.innerHTML = text;
- elements.push(div);
- }
- } else {
- if (key === 'Description') {
- if (value !== '') {
- elements.push($el('h2', [key + ':']));
- elements.push($el('p', [value]));
- }
- } else if (key === 'Preview') {
- //
- } else {
- if (value !== '') {
- elements.push($el('p', [key + ': ' + value]));
- }
- }
- }
- }
- return elements;
- })(),
- ),
- ]),
- );
- infoHtml.append.apply(infoHtml, innerHtml);
- // TODO: set default value of dropdown and value to model type?
-
- /** @type {HTMLDivElement} */
- const metadataElement = this.elements.tabContents[1]; // TODO: remove magic value
- const isMetadata =
- typeof metadata === 'object' &&
- metadata !== null &&
- Object.keys(metadata).length > 0;
- metadataElement.innerHTML = '';
- metadataElement.append.apply(metadataElement, [
- $el('h1', ['Metadata']),
- $el(
- 'div',
- (() => {
- const tableRows = [];
- if (isMetadata) {
- for (const [key, value] of Object.entries(metadata)) {
- if (value === undefined || value === null) {
- continue;
- }
- if (value !== '') {
- tableRows.push(
- $el('tr', [
- $el('th.model-metadata-key', [key]),
- $el('th.model-metadata-value', [value]),
- ]),
- );
- }
- }
- }
- return $el('table.model-metadata', tableRows);
- })(),
- ),
- ]);
- const metadataButton = this.elements.tabButtons[1]; // TODO: remove magic value
- metadataButton.style.display = isMetadata ? '' : 'none';
-
- /** @type {HTMLDivElement} */
- const tagsElement = this.elements.tabContents[2]; // TODO: remove magic value
- const isTags = Array.isArray(tags) && tags.length > 0;
- const tagsParagraph = $el(
- 'div',
- (() => {
- const elements = [];
- if (isTags) {
- let text = TagCountMapToParagraph(tags);
- const div = $el('div');
- div.innerHTML = text;
- elements.push(div);
- }
- return elements;
- })(),
- );
- const tagGeneratorRandomizedOutput = $el('textarea.comfy-multiline-input', {
- name: 'random tag generator output',
- rows: 4,
- });
- const TAG_GENERATOR_SAMPLER_NAME = 'model manager tag generator sampler';
- const tagGenerationCount = $el('input', {
- type: 'number',
- name: 'tag generator count',
- step: 1,
- min: 1,
- value: this.#settingsElements['tag-generator-count'].value,
- });
- const tagGenerationThreshold = $el('input', {
- type: 'number',
- name: 'tag generator threshold',
- step: 1,
- min: 1,
- value: this.#settingsElements['tag-generator-threshold'].value,
- });
- const selectedSamplerOption =
- this.#settingsElements['tag-generator-sampler-method'].value;
- const samplerOptions = ['Frequency', 'Uniform'];
- const samplerRadioGroup = $radioGroup({
- name: TAG_GENERATOR_SAMPLER_NAME,
- onchange: (value) => {},
- options: samplerOptions.map((option) => {
- return { value: option };
- }),
- });
- const samplerOptionInputs = samplerRadioGroup.getElementsByTagName('input');
- for (let i = 0; i < samplerOptionInputs.length; i++) {
- const samplerOptionInput = samplerOptionInputs[i];
- if (samplerOptionInput.value === selectedSamplerOption) {
- samplerOptionInput.click();
- break;
- }
- }
- const tagGenerator = $el('div', [
- $el('h1', ['Tags']),
- $el('h2', { style: { margin: '0px 0px 16px 0px' } }, [
- 'Random Tag Generator',
- ]),
- $el('div', [
- $el(
- 'details.tag-generator-settings',
- {
- style: { margin: '10px 0', display: 'none' },
- open: false,
- },
- [
- $el('summary', ['Settings']),
- $el('div', ['Sampling Method', samplerRadioGroup]),
- $el('label', ['Count', tagGenerationCount]),
- $el('label', ['Threshold', tagGenerationThreshold]),
- ],
- ),
- tagGeneratorRandomizedOutput,
- new ComfyButton({
- content: 'Randomize',
- tooltip: 'Randomly generate subset of tags',
- action: () => {
- const samplerName = document.querySelector(
- `input[name="${TAG_GENERATOR_SAMPLER_NAME}"]:checked`,
- ).value;
- const sampler =
- samplerName === 'Frequency'
- ? ModelInfo.ProbabilisticTagSampling
- : ModelInfo.UniformTagSampling;
- const sampleCount = tagGenerationCount.value;
- const frequencyThreshold = tagGenerationThreshold.value;
- const tags = ParseTagParagraph(tagsParagraph.innerText);
- const sampledTags = sampler(tags, sampleCount, frequencyThreshold);
- tagGeneratorRandomizedOutput.value = sampledTags.join(', ');
- },
- }).element,
- ]),
- ]);
- tagsElement.innerHTML = '';
- tagsElement.append.apply(tagsElement, [
- tagGenerator,
- $el('div', [
- $el(
- 'h2',
- {
- style: {
- margin: '24px 0px 8px 0px',
- },
- },
- ['Tags'],
- ),
- tagsParagraph,
- ]),
- ]);
- const tagButton = this.elements.tabButtons[2]; // TODO: remove magic value
- tagButton.style.display = isTags ? '' : 'none';
-
- const saveIcon = 'content-save';
- const savingIcon = 'cloud-upload-outline';
-
- const saveNotesButton = new ComfyButton({
- icon: saveIcon,
- tooltip: 'Save note',
- classList: 'comfyui-button icon-button',
- action: async (e) => {
- const [button, icon, span] = comfyButtonDisambiguate(e.target);
- button.disabled = true;
- const saved = await this.trySave(false);
- comfyButtonAlert(e.target, saved);
- button.disabled = false;
- },
- }).element;
-
- const saveDebounce = debounce(async () => {
- const saveIconClass = 'mdi-' + saveIcon;
- const savingIconClass = 'mdi-' + savingIcon;
- const iconElement = saveNotesButton.getElementsByTagName('i')[0];
- iconElement.classList.remove(saveIconClass);
- iconElement.classList.add(savingIconClass);
- const saved = await this.trySave(false);
- iconElement.classList.remove(savingIconClass);
- iconElement.classList.add(saveIconClass);
- }, 1000);
-
- /** @type {HTMLDivElement} */
- const notesElement = this.elements.tabContents[3]; // TODO: remove magic value
- notesElement.innerHTML = '';
- const markdown = $el('div', {}, '');
- markdown.innerHTML = marked.parse(noteText);
-
- notesElement.append.apply(
- notesElement,
- (() => {
- const notes = $el('textarea.comfy-multiline-input', {
- name: 'model notes',
- value: noteText,
- oninput: (e) => {
- if (this.#settingsElements['model-info-autosave-notes'].checked) {
- saveDebounce();
- }
- },
- });
-
- if (navigator.userAgent.includes('Mac')) {
- new KeyComboListener(['MetaLeft', 'KeyS'], saveDebounce, notes);
- new KeyComboListener(['MetaRight', 'KeyS'], saveDebounce, notes);
- } else {
- new KeyComboListener(['ControlLeft', 'KeyS'], saveDebounce, notes);
- new KeyComboListener(['ControlRight', 'KeyS'], saveDebounce, notes);
- }
-
- this.elements.notes = notes;
- this.elements.markdown = markdown;
- this.#savedNotesValue = noteText;
-
- const notes_editor = $el(
- 'div',
- {
- style: {
- display: noteText == '' ? 'flex' : 'none',
- height: '100%',
- 'min-height': '60px',
- },
- },
- notes,
- );
- const notes_viewer = $el(
- 'div',
- {
- style: {
- display: noteText == '' ? 'none' : 'flex',
- height: '100%',
- 'min-height': '60px',
- overflow: 'scroll',
- 'overflow-wrap': 'anywhere',
- },
- },
- markdown,
- );
-
- const editNotesButton = new ComfyButton({
- icon: 'pencil',
- tooltip: 'Change file name',
- classList: 'comfyui-button icon-button',
- action: async () => {
- notes_editor.style.display =
- notes_editor.style.display == 'flex' ? 'none' : 'flex';
- notes_viewer.style.display =
- notes_viewer.style.display == 'none' ? 'flex' : 'none';
- },
- }).element;
-
- return [
- $el(
- 'div.row',
- {
- style: { 'align-items': 'center' },
- },
- [$el('h1', ['Notes']), saveNotesButton, editNotesButton],
- ),
- notes_editor,
- notes_viewer,
- ];
- })(),
- );
- }
-
- static UniformTagSampling(
- tagsAndCounts,
- sampleCount,
- frequencyThreshold = 0,
- ) {
- const data = tagsAndCounts.filter((x) => x[1] >= frequencyThreshold);
- let count = data.length;
- const samples = [];
- for (let i = 0; i < sampleCount; i++) {
- if (count === 0) {
- break;
- }
- const index = Math.floor(Math.random() * count);
- const pair = data.splice(index, 1)[0];
- samples.push(pair);
- count -= 1;
- }
- const sortedSamples = samples.sort((x1, x2) => {
- return parseInt(x2[1]) - parseInt(x1[1]);
- });
- return sortedSamples.map((x) => x[0]);
- }
-
- static ProbabilisticTagSampling(
- tagsAndCounts,
- sampleCount,
- frequencyThreshold = 0,
- ) {
- const data = tagsAndCounts.filter((x) => x[1] >= frequencyThreshold);
- let tagFrequenciesSum = data.reduce(
- (accumulator, x) => accumulator + x[1],
- 0,
- );
- let count = data.length;
- const samples = [];
- for (let i = 0; i < sampleCount; i++) {
- if (count === 0) {
- break;
- }
- const index = (() => {
- let frequencyIndex = Math.floor(Math.random() * tagFrequenciesSum);
- return data.findIndex((x) => {
- const frequency = x[1];
- if (frequency > frequencyIndex) {
- return true;
- }
- frequencyIndex = frequencyIndex - frequency;
- return false;
- });
- })();
- const pair = data.splice(index, 1)[0];
- samples.push(pair);
- tagFrequenciesSum -= pair[1];
- count -= 1;
- }
- const sortedSamples = samples.sort((x1, x2) => {
- return parseInt(x2[1]) - parseInt(x1[1]);
- });
- return sortedSamples.map((x) => x[0]);
- }
-}
-
-class Civitai {
- /**
- * Get model info from Civitai.
- *
- * @param {string} id - Model ID.
- * @param {string} apiPath - Civitai request subdirectory. "models" for 'model' urls. "model-version" for 'api' urls.
- *
- * @returns {Promise