diff --git a/icons.config.js b/icons.config.js
index 2ae7ef796..49deb2153 100644
--- a/icons.config.js
+++ b/icons.config.js
@@ -6,6 +6,7 @@ const config = {
icons: [
"rocket_launch",
"deployed_code_update",
+ "difference",
"adjust",
"add",
"piano",
diff --git a/package.json b/package.json
index 314ba348e..ba83c111c 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
"build:tauri": "tauri build",
"tauri": "tauri",
"test": "vitest run --coverage",
+ "test:chord-sync": "vitest chord-sync",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"minify-icons": "node src/tools/minify-icon-font.js",
@@ -35,25 +36,28 @@
},
"devDependencies": {
"@codemirror/autocomplete": "^6.20.0",
+ "@codemirror/collab": "^6.1.1",
"@codemirror/commands": "^6.10.1",
"@codemirror/lang-javascript": "^6.2.4",
- "@codemirror/language": "^6.11.3",
+ "@codemirror/language": "^6.12.1",
+ "@codemirror/lint": "^6.9.2",
"@codemirror/merge": "^6.11.2",
- "@codemirror/state": "^6.5.2",
- "@codemirror/view": "^6.39.4",
+ "@codemirror/search": "^6.6.0",
+ "@codemirror/state": "^6.5.3",
+ "@codemirror/view": "^6.39.9",
"@fontsource-variable/material-symbols-rounded": "^5.2.30",
"@fontsource-variable/noto-sans-mono": "^5.2.10",
- "@lezer/common": "^1.4.0",
+ "@lezer/common": "^1.5.0",
"@lezer/generator": "^1.8.0",
"@lezer/highlight": "^1.2.3",
- "@lezer/lr": "^1.4.5",
+ "@lezer/lr": "^1.4.7",
"@material/material-color-utilities": "^0.3.0",
"@melt-ui/pp": "^0.3.2",
"@melt-ui/svelte": "^0.86.6",
"@modyfi/vite-plugin-yaml": "^1.1.1",
"@sveltejs/adapter-static": "^3.0.10",
- "@sveltejs/kit": "^2.49.2",
- "@sveltejs/vite-plugin-svelte": "^6.2.1",
+ "@sveltejs/kit": "^2.49.3",
+ "@sveltejs/vite-plugin-svelte": "^6.2.3",
"@tauri-apps/api": "^1.6.0",
"@tauri-apps/cli": "^1.6.0",
"@types/dom-view-transitions": "^1.0.6",
@@ -76,29 +80,29 @@
"matrix-js-sdk": "^37.12.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.7.4",
- "prettier-plugin-css-order": "^2.1.2",
+ "prettier-plugin-css-order": "^2.2.0",
"prettier-plugin-svelte": "^3.4.1",
"rxjs": "^7.8.2",
- "sass": "^1.97.0",
+ "sass": "^1.97.2",
"semver": "^7.7.3",
- "socket.io-client": "^4.8.1",
+ "socket.io-client": "^4.8.3",
"stylelint": "^16.26.1",
"stylelint-config-html": "^1.1.0",
"stylelint-config-prettier-scss": "^1.0.0",
"stylelint-config-recommended-scss": "^16.0.2",
"stylelint-config-standard-scss": "^16.0.0",
- "svelte": "5.37.1",
- "svelte-check": "^4.3.4",
+ "svelte": "5.46.1",
+ "svelte-check": "^4.3.5",
"svelte-preprocess": "^6.0.3",
"tippy.js": "^6.3.7",
"typesafe-i18n": "^5.26.2",
- "typescript": "^5.8.3",
- "vite": "^7.0.6",
+ "typescript": "^5.9.3",
+ "vite": "^7.3.1",
"vite-plugin-mkcert": "^1.17.9",
- "vite-plugin-pwa": "^1.0.2",
+ "vite-plugin-pwa": "^1.2.0",
"vitest": "^4.0.16",
"web-serial-polyfill": "^1.0.15",
- "workbox-window": "^7.3.0"
+ "workbox-window": "^7.4.0"
},
"type": "module"
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3b2cdb3cc..3a0290e8b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -11,6 +11,9 @@ importers:
'@codemirror/autocomplete':
specifier: ^6.20.0
version: 6.20.0
+ '@codemirror/collab':
+ specifier: ^6.1.1
+ version: 6.1.1
'@codemirror/commands':
specifier: ^6.10.1
version: 6.10.1
@@ -18,17 +21,23 @@ importers:
specifier: ^6.2.4
version: 6.2.4
'@codemirror/language':
- specifier: ^6.11.3
- version: 6.11.3
+ specifier: ^6.12.1
+ version: 6.12.1
+ '@codemirror/lint':
+ specifier: ^6.9.2
+ version: 6.9.2
'@codemirror/merge':
specifier: ^6.11.2
version: 6.11.2
+ '@codemirror/search':
+ specifier: ^6.6.0
+ version: 6.6.0
'@codemirror/state':
- specifier: ^6.5.2
- version: 6.5.2
+ specifier: ^6.5.3
+ version: 6.5.3
'@codemirror/view':
- specifier: ^6.39.4
- version: 6.39.4
+ specifier: ^6.39.9
+ version: 6.39.9
'@fontsource-variable/material-symbols-rounded':
specifier: ^5.2.30
version: 5.2.30
@@ -36,8 +45,8 @@ importers:
specifier: ^5.2.10
version: 5.2.10
'@lezer/common':
- specifier: ^1.4.0
- version: 1.4.0
+ specifier: ^1.5.0
+ version: 1.5.0
'@lezer/generator':
specifier: ^1.8.0
version: 1.8.0
@@ -45,29 +54,29 @@ importers:
specifier: ^1.2.3
version: 1.2.3
'@lezer/lr':
- specifier: ^1.4.5
- version: 1.4.5
+ specifier: ^1.4.7
+ version: 1.4.7
'@material/material-color-utilities':
specifier: ^0.3.0
version: 0.3.0
'@melt-ui/pp':
specifier: ^0.3.2
- version: 0.3.2(@melt-ui/svelte@0.86.6(svelte@5.37.1))(svelte@5.37.1)
+ version: 0.3.2(@melt-ui/svelte@0.86.6(svelte@5.46.1))(svelte@5.46.1)
'@melt-ui/svelte':
specifier: ^0.86.6
- version: 0.86.6(svelte@5.37.1)
+ version: 0.86.6(svelte@5.46.1)
'@modyfi/vite-plugin-yaml':
specifier: ^1.1.1
- version: 1.1.1(rollup@2.79.2)(vite@7.0.6(sass@1.97.0)(terser@5.44.1))
+ version: 1.1.1(rollup@2.79.2)(vite@7.3.1(sass@1.97.2)(terser@5.44.1))
'@sveltejs/adapter-static':
specifier: ^3.0.10
- version: 3.0.10(@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1)))(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1)))
+ version: 3.0.10(@sveltejs/kit@2.49.3(@sveltejs/vite-plugin-svelte@6.2.3(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1)))(svelte@5.46.1)(typescript@5.9.3)(vite@7.3.1(sass@1.97.2)(terser@5.44.1)))
'@sveltejs/kit':
- specifier: ^2.49.2
- version: 2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1)))(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1))
+ specifier: ^2.49.3
+ version: 2.49.3(@sveltejs/vite-plugin-svelte@6.2.3(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1)))(svelte@5.46.1)(typescript@5.9.3)(vite@7.3.1(sass@1.97.2)(terser@5.44.1))
'@sveltejs/vite-plugin-svelte':
- specifier: ^6.2.1
- version: 6.2.1(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1))
+ specifier: ^6.2.3
+ version: 6.2.3(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1))
'@tauri-apps/api':
specifier: ^1.6.0
version: 1.6.0
@@ -94,7 +103,7 @@ importers:
version: 2023.10.7
'@vite-pwa/sveltekit':
specifier: ^1.1.0
- version: 1.1.0(@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1)))(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1)))(vite@7.0.6(sass@1.97.0)(terser@5.44.1))(workbox-build@7.1.1)(workbox-window@7.3.0)
+ version: 1.1.0(@sveltejs/kit@2.49.3(@sveltejs/vite-plugin-svelte@6.2.3(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1)))(svelte@5.46.1)(typescript@5.9.3)(vite@7.3.1(sass@1.97.2)(terser@5.44.1)))(vite@7.3.1(sass@1.97.2)(terser@5.44.1))(workbox-build@7.1.1)(workbox-window@7.4.0)
autoprefixer:
specifier: ^10.4.23
version: 10.4.23(postcss@8.5.6)
@@ -135,81 +144,77 @@ importers:
specifier: ^3.7.4
version: 3.7.4
prettier-plugin-css-order:
- specifier: ^2.1.2
- version: 2.1.2(postcss@8.5.6)(prettier@3.7.4)
+ specifier: ^2.2.0
+ version: 2.2.0(postcss@8.5.6)(prettier@3.7.4)
prettier-plugin-svelte:
specifier: ^3.4.1
- version: 3.4.1(prettier@3.7.4)(svelte@5.37.1)
+ version: 3.4.1(prettier@3.7.4)(svelte@5.46.1)
rxjs:
specifier: ^7.8.2
version: 7.8.2
sass:
- specifier: ^1.97.0
- version: 1.97.0
+ specifier: ^1.97.2
+ version: 1.97.2
semver:
specifier: ^7.7.3
version: 7.7.3
socket.io-client:
- specifier: ^4.8.1
- version: 4.8.1
+ specifier: ^4.8.3
+ version: 4.8.3
stylelint:
specifier: ^16.26.1
- version: 16.26.1(typescript@5.8.3)
+ version: 16.26.1(typescript@5.9.3)
stylelint-config-html:
specifier: ^1.1.0
- version: 1.1.0(postcss-html@1.7.0)(stylelint@16.26.1(typescript@5.8.3))
+ version: 1.1.0(postcss-html@1.7.0)(stylelint@16.26.1(typescript@5.9.3))
stylelint-config-prettier-scss:
specifier: ^1.0.0
- version: 1.0.0(stylelint@16.26.1(typescript@5.8.3))
+ version: 1.0.0(stylelint@16.26.1(typescript@5.9.3))
stylelint-config-recommended-scss:
specifier: ^16.0.2
- version: 16.0.2(postcss@8.5.6)(stylelint@16.26.1(typescript@5.8.3))
+ version: 16.0.2(postcss@8.5.6)(stylelint@16.26.1(typescript@5.9.3))
stylelint-config-standard-scss:
specifier: ^16.0.0
- version: 16.0.0(postcss@8.5.6)(stylelint@16.26.1(typescript@5.8.3))
+ version: 16.0.0(postcss@8.5.6)(stylelint@16.26.1(typescript@5.9.3))
svelte:
- specifier: 5.37.1
- version: 5.37.1
+ specifier: 5.46.1
+ version: 5.46.1
svelte-check:
- specifier: ^4.3.4
- version: 4.3.4(picomatch@4.0.3)(svelte@5.37.1)(typescript@5.8.3)
+ specifier: ^4.3.5
+ version: 4.3.5(picomatch@4.0.3)(svelte@5.46.1)(typescript@5.9.3)
svelte-preprocess:
specifier: ^6.0.3
- version: 6.0.3(@babel/core@7.28.5)(postcss@8.5.6)(sass@1.97.0)(svelte@5.37.1)(typescript@5.8.3)
+ version: 6.0.3(@babel/core@7.28.5)(postcss@8.5.6)(sass@1.97.2)(svelte@5.46.1)(typescript@5.9.3)
tippy.js:
specifier: ^6.3.7
version: 6.3.7
typesafe-i18n:
specifier: ^5.26.2
- version: 5.26.2(typescript@5.8.3)
+ version: 5.26.2(typescript@5.9.3)
typescript:
- specifier: ^5.8.3
- version: 5.8.3
+ specifier: ^5.9.3
+ version: 5.9.3
vite:
- specifier: ^7.0.6
- version: 7.0.6(sass@1.97.0)(terser@5.44.1)
+ specifier: ^7.3.1
+ version: 7.3.1(sass@1.97.2)(terser@5.44.1)
vite-plugin-mkcert:
specifier: ^1.17.9
- version: 1.17.9(vite@7.0.6(sass@1.97.0)(terser@5.44.1))
+ version: 1.17.9(vite@7.3.1(sass@1.97.2)(terser@5.44.1))
vite-plugin-pwa:
- specifier: ^1.0.2
- version: 1.0.2(vite@7.0.6(sass@1.97.0)(terser@5.44.1))(workbox-build@7.1.1)(workbox-window@7.3.0)
+ specifier: ^1.2.0
+ version: 1.2.0(vite@7.3.1(sass@1.97.2)(terser@5.44.1))(workbox-build@7.1.1)(workbox-window@7.4.0)
vitest:
specifier: ^4.0.16
- version: 4.0.16(jsdom@26.1.0)(sass@1.97.0)(terser@5.44.1)
+ version: 4.0.16(jsdom@26.1.0)(sass@1.97.2)(terser@5.44.1)
web-serial-polyfill:
specifier: ^1.0.15
version: 1.0.15
workbox-window:
- specifier: ^7.3.0
- version: 7.3.0
+ specifier: ^7.4.0
+ version: 7.4.0
packages:
- '@ampproject/remapping@2.3.0':
- resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
- engines: {node: '>=6.0.0'}
-
'@apideck/better-ajv-errors@0.3.6':
resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==}
engines: {node: '>=10'}
@@ -739,29 +744,32 @@ packages:
'@codemirror/autocomplete@6.20.0':
resolution: {integrity: sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==}
+ '@codemirror/collab@6.1.1':
+ resolution: {integrity: sha512-tkIn9Jguh98ie12dbBuba3lE8LHUkaMrIFuCVeVGhncSczFdKmX25vC12+58+yqQW5AXi3py6jWY0W+jelyglA==}
+
'@codemirror/commands@6.10.1':
resolution: {integrity: sha512-uWDWFypNdQmz2y1LaNJzK7fL7TYKLeUAU0npEC685OKTF3KcQ2Vu3klIM78D7I6wGhktme0lh3CuQLv0ZCrD9Q==}
'@codemirror/lang-javascript@6.2.4':
resolution: {integrity: sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==}
- '@codemirror/language@6.11.3':
- resolution: {integrity: sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==}
+ '@codemirror/language@6.12.1':
+ resolution: {integrity: sha512-Fa6xkSiuGKc8XC8Cn96T+TQHYj4ZZ7RdFmXA3i9xe/3hLHfwPZdM+dqfX0Cp0zQklBKhVD8Yzc8LS45rkqcwpQ==}
- '@codemirror/lint@6.8.1':
- resolution: {integrity: sha512-IZ0Y7S4/bpaunwggW2jYqwLuHj0QtESf5xcROewY6+lDNwZ/NzvR4t+vpYgg9m7V8UXLPYqG+lu3DF470E5Oxg==}
+ '@codemirror/lint@6.9.2':
+ resolution: {integrity: sha512-sv3DylBiIyi+xKwRCJAAsBZZZWo82shJ/RTMymLabAdtbkV5cSKwWDeCgtUq3v8flTaXS2y1kKkICuRYtUswyQ==}
'@codemirror/merge@6.11.2':
resolution: {integrity: sha512-NO5EJd2rLRbwVWLgMdhIntDIhfDtMOKYEZgqV5WnkNUS2oXOCVWLPjG/kgl/Jth2fGiOuG947bteqxP9nBXmMg==}
- '@codemirror/search@6.5.6':
- resolution: {integrity: sha512-rpMgcsh7o0GuCDUXKPvww+muLA1pDJaFrpq/CCHtpQJYz8xopu4D1hPcKRoDD0YlF8gZaqTNIRa4VRBWyhyy7Q==}
+ '@codemirror/search@6.6.0':
+ resolution: {integrity: sha512-koFuNXcDvyyotWcgOnZGmY7LZqEOXZaaxD/j6n18TCLx2/9HieZJ5H6hs1g8FiRxBD0DNfs0nXn17g872RmYdw==}
- '@codemirror/state@6.5.2':
- resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==}
+ '@codemirror/state@6.5.3':
+ resolution: {integrity: sha512-MerMzJzlXogk2fxWFU1nKp36bY5orBG59HnPiz0G9nLRebWa0zXuv2siH6PLIHBvv5TH8CkQRqjBs0MlxCZu+A==}
- '@codemirror/view@6.39.4':
- resolution: {integrity: sha512-xMF6OfEAUVY5Waega4juo1QGACfNkNF+aJLqpd8oUJz96ms2zbfQ9Gh35/tI3y8akEV31FruKfj7hBnIU/nkqA==}
+ '@codemirror/view@6.39.9':
+ resolution: {integrity: sha512-miGSIfBOKC1s2oHoa80dp+BjtsL8sXsrgGlQnQuOcfvaedcQUtqddTmKbJSDkLl4mkgPvZyXuKic2HDNYcJLYA==}
'@csstools/color-helpers@5.0.2':
resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==}
@@ -828,152 +836,158 @@ packages:
'@dual-bundle/import-meta-resolve@4.2.1':
resolution: {integrity: sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==}
- '@esbuild/aix-ppc64@0.25.2':
- resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==}
+ '@esbuild/aix-ppc64@0.27.2':
+ resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
- '@esbuild/android-arm64@0.25.2':
- resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==}
+ '@esbuild/android-arm64@0.27.2':
+ resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
- '@esbuild/android-arm@0.25.2':
- resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==}
+ '@esbuild/android-arm@0.27.2':
+ resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
- '@esbuild/android-x64@0.25.2':
- resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==}
+ '@esbuild/android-x64@0.27.2':
+ resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
- '@esbuild/darwin-arm64@0.25.2':
- resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==}
+ '@esbuild/darwin-arm64@0.27.2':
+ resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
- '@esbuild/darwin-x64@0.25.2':
- resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==}
+ '@esbuild/darwin-x64@0.27.2':
+ resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
- '@esbuild/freebsd-arm64@0.25.2':
- resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==}
+ '@esbuild/freebsd-arm64@0.27.2':
+ resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
- '@esbuild/freebsd-x64@0.25.2':
- resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==}
+ '@esbuild/freebsd-x64@0.27.2':
+ resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
- '@esbuild/linux-arm64@0.25.2':
- resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==}
+ '@esbuild/linux-arm64@0.27.2':
+ resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
- '@esbuild/linux-arm@0.25.2':
- resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==}
+ '@esbuild/linux-arm@0.27.2':
+ resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
- '@esbuild/linux-ia32@0.25.2':
- resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==}
+ '@esbuild/linux-ia32@0.27.2':
+ resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
- '@esbuild/linux-loong64@0.25.2':
- resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==}
+ '@esbuild/linux-loong64@0.27.2':
+ resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
- '@esbuild/linux-mips64el@0.25.2':
- resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==}
+ '@esbuild/linux-mips64el@0.27.2':
+ resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
- '@esbuild/linux-ppc64@0.25.2':
- resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==}
+ '@esbuild/linux-ppc64@0.27.2':
+ resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
- '@esbuild/linux-riscv64@0.25.2':
- resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==}
+ '@esbuild/linux-riscv64@0.27.2':
+ resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
- '@esbuild/linux-s390x@0.25.2':
- resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==}
+ '@esbuild/linux-s390x@0.27.2':
+ resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
- '@esbuild/linux-x64@0.25.2':
- resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==}
+ '@esbuild/linux-x64@0.27.2':
+ resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
- '@esbuild/netbsd-arm64@0.25.2':
- resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==}
+ '@esbuild/netbsd-arm64@0.27.2':
+ resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
- '@esbuild/netbsd-x64@0.25.2':
- resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==}
+ '@esbuild/netbsd-x64@0.27.2':
+ resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
- '@esbuild/openbsd-arm64@0.25.2':
- resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==}
+ '@esbuild/openbsd-arm64@0.27.2':
+ resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
- '@esbuild/openbsd-x64@0.25.2':
- resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==}
+ '@esbuild/openbsd-x64@0.27.2':
+ resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
- '@esbuild/sunos-x64@0.25.2':
- resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==}
+ '@esbuild/openharmony-arm64@0.27.2':
+ resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.27.2':
+ resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
- '@esbuild/win32-arm64@0.25.2':
- resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==}
+ '@esbuild/win32-arm64@0.27.2':
+ resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
- '@esbuild/win32-ia32@0.25.2':
- resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==}
+ '@esbuild/win32-ia32@0.27.2':
+ resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
- '@esbuild/win32-x64@0.25.2':
- resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==}
+ '@esbuild/win32-x64@0.27.2':
+ resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
@@ -1011,10 +1025,6 @@ packages:
'@jridgewell/gen-mapping@0.3.13':
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
- '@jridgewell/gen-mapping@0.3.5':
- resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
- engines: {node: '>=6.0.0'}
-
'@jridgewell/remapping@2.3.5':
resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
@@ -1022,25 +1032,15 @@ packages:
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/source-map@0.3.11':
resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==}
'@jridgewell/sourcemap-codec@1.4.15':
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
- '@jridgewell/sourcemap-codec@1.5.0':
- resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
-
'@jridgewell/sourcemap-codec@1.5.5':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
- '@jridgewell/trace-mapping@0.3.25':
- resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
-
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
@@ -1053,8 +1053,8 @@ packages:
'@keyv/serialize@1.1.1':
resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==}
- '@lezer/common@1.4.0':
- resolution: {integrity: sha512-DVeMRoGrgn/k45oQNu189BoW4SZwgZFzJ1+1TV5j2NJ/KFC83oa/enRqZSGshyeMk5cPWMhsKs9nx+8o0unwGg==}
+ '@lezer/common@1.5.0':
+ resolution: {integrity: sha512-PNGcolp9hr4PJdXR4ix7XtixDrClScvtSCYW3rQG106oVMOOI+jFb+0+J3mbeL/53g1Zd6s0kJzaw6Ri68GmAA==}
'@lezer/generator@1.8.0':
resolution: {integrity: sha512-/SF4EDWowPqV1jOgoGSGTIFsE7Ezdr7ZYxyihl5eMKVO5tlnpIhFcDavgm1hHY5GEonoOAEnJ0CU0x+tvuAuUg==}
@@ -1066,8 +1066,8 @@ packages:
'@lezer/javascript@1.4.17':
resolution: {integrity: sha512-bYW4ctpyGK+JMumDApeUzuIezX01H76R1foD6LcRX224FWfyYit/HYxiPGDjXXe/wQWASjCvVGoukTH68+0HIA==}
- '@lezer/lr@1.4.5':
- resolution: {integrity: sha512-/YTRKP5yPPSo1xImYQk7AZZMAgap0kegzqCSYHjAL9x1AZ0ZQW+IpcEzMKagCsbTsLnVeWkxYrCNeXG8xEPrjg==}
+ '@lezer/lr@1.4.7':
+ resolution: {integrity: sha512-wNIFWdSUfX9Jc6ePMzxSPVgTVB4EOfDIwLQLWASyiUdHKaMsiilj9bYiGkGQCKVodd0x6bgQCV207PILGFCF9Q==}
'@marijn/find-cluster-break@1.0.2':
resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==}
@@ -1372,18 +1372,21 @@ packages:
peerDependencies:
'@sveltejs/kit': ^2.0.0
- '@sveltejs/kit@2.49.2':
- resolution: {integrity: sha512-Vp3zX/qlwerQmHMP6x0Ry1oY7eKKRcOWGc2P59srOp4zcqyn+etJyQpELgOi4+ZSUgteX8Y387NuwruLgGXLUQ==}
+ '@sveltejs/kit@2.49.3':
+ resolution: {integrity: sha512-luTmE2Isk9GRJnitqanLoByKBiyLdfLpV2qV9a25JMxjbQt919TVqG8pibJDkxTvX9+w2k/9IL7o+/RtG++3QA==}
engines: {node: '>=18.13'}
hasBin: true
peerDependencies:
'@opentelemetry/api': ^1.0.0
'@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0
svelte: ^4.0.0 || ^5.0.0-next.0
+ typescript: ^5.3.3
vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0
peerDependenciesMeta:
'@opentelemetry/api':
optional: true
+ typescript:
+ optional: true
'@sveltejs/vite-plugin-svelte-inspector@5.0.0':
resolution: {integrity: sha512-iwQ8Z4ET6ZFSt/gC+tVfcsSBHwsqc6RumSaiLUkAurW3BCpJam65cmHw0oOlDMTO0u+PZi9hilBRYN+LZNHTUQ==}
@@ -1393,8 +1396,8 @@ packages:
svelte: ^5.0.0
vite: ^6.3.0 || ^7.0.0
- '@sveltejs/vite-plugin-svelte@6.2.1':
- resolution: {integrity: sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==}
+ '@sveltejs/vite-plugin-svelte@6.2.3':
+ resolution: {integrity: sha512-a+uxqQ9j6Lxmq4plbGaNdM9hgDCZyxAv/yvuyF5iWoA2H5icZkqD3rdK155ZQgFLX2lc3NvahHG4OgKpYqYPiQ==}
engines: {node: ^20.19 || ^22.12 || >=24}
peerDependencies:
svelte: ^5.0.0
@@ -1493,9 +1496,6 @@ packages:
'@types/estree@1.0.6':
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
- '@types/estree@1.0.7':
- resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
-
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
@@ -1577,11 +1577,6 @@ packages:
'@vitest/utils@4.0.16':
resolution: {integrity: sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA==}
- acorn@8.12.1:
- resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==}
- engines: {node: '>=0.4.0'}
- hasBin: true
-
acorn@8.15.0:
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
engines: {node: '>=0.4.0'}
@@ -1961,8 +1956,8 @@ packages:
resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
engines: {node: '>=8'}
- css-declaration-sorter@7.2.0:
- resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==}
+ css-declaration-sorter@7.3.1:
+ resolution: {integrity: sha512-gz6x+KkgNCjxq3Var03pRYLhyNfwhkKF1g/yoLgDNtFvVu0/fOLV9C8fFEZRjACp/XQLumjAYo7JVjzH3wLbxA==}
engines: {node: ^14 || ^16 || >=18}
peerDependencies:
postcss: ^8.0.9
@@ -2345,8 +2340,8 @@ packages:
resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
engines: {node: '>= 0.4'}
- esbuild@0.25.2:
- resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==}
+ esbuild@0.27.2:
+ resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==}
engines: {node: '>=18'}
hasBin: true
@@ -2364,8 +2359,8 @@ packages:
esptool-js@0.5.7:
resolution: {integrity: sha512-k3pkXU9OTySCd58OUDjuJWNnFjM+QpPWAghxyWPm3zNfaLiP4ex2jNd7Rj0jWPu3/fgvwau236tetsTZrh4x5g==}
- esrap@2.1.0:
- resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==}
+ esrap@2.2.1:
+ resolution: {integrity: sha512-GiYWG34AN/4CUyaWAgunGt0Rxvr1PTMlGC0vvEov/uOQYWne2bpN03Um+k8jT+q3op33mKouP2zeJ6OlM+qeUg==}
estree-walker@1.0.1:
resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==}
@@ -2434,14 +2429,6 @@ packages:
fd-slicer@1.1.0:
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
- fdir@6.4.2:
- resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==}
- peerDependencies:
- picomatch: ^3 || ^4
- peerDependenciesMeta:
- picomatch:
- optional: true
-
fdir@6.4.6:
resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
peerDependencies:
@@ -3144,9 +3131,6 @@ packages:
magic-string@0.30.10:
resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==}
- magic-string@0.30.17:
- resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
-
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
@@ -3390,10 +3374,6 @@ packages:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
- picomatch@4.0.2:
- resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
- engines: {node: '>=12'}
-
picomatch@4.0.3:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
@@ -3464,8 +3444,8 @@ packages:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14}
- prettier-plugin-css-order@2.1.2:
- resolution: {integrity: sha512-vomxPjHI6pOMYcBuouSJHxxQClJXaUpU9rsV9IAO2wrSTZILRRlrxAAR8t9UF6wtczLkLfNRFUwM+ZbGXOONUA==}
+ prettier-plugin-css-order@2.2.0:
+ resolution: {integrity: sha512-GCkwEgQ2roT7le+zpUFQThPDO4x5EXcZmY9Rj6rvO++I/nATTGBWdZdsooha/BlvIBbZclJzXsgJdlKWrys9+w==}
engines: {node: '>=16'}
peerDependencies:
prettier: 3.x
@@ -3658,8 +3638,8 @@ packages:
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
- sass@1.97.0:
- resolution: {integrity: sha512-KR0igP1z4avUJetEuIeOdDlwaUDvkH8wSx7FdSjyYBS3dpyX3TzHfAMO0G1Q4/3cdjcmi3r7idh+KCmKqS+KeQ==}
+ sass@1.97.2:
+ resolution: {integrity: sha512-y5LWb0IlbO4e97Zr7c3mlpabcbBtS+ieiZ9iwDooShpFKWXf62zz5pEPdwrLYm+Bxn1fnbwFGzHuCLSA9tBmrw==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -3770,8 +3750,8 @@ packages:
smob@1.5.0:
resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==}
- socket.io-client@4.8.1:
- resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==}
+ socket.io-client@4.8.3:
+ resolution: {integrity: sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==}
engines: {node: '>=10.0.0'}
socket.io-parser@4.2.4:
@@ -3964,8 +3944,8 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
- svelte-check@4.3.4:
- resolution: {integrity: sha512-DVWvxhBrDsd+0hHWKfjP99lsSXASeOhHJYyuKOFYJcP7ThfSCKgjVarE8XfuMWpS5JV3AlDf+iK1YGGo2TACdw==}
+ svelte-check@4.3.5:
+ resolution: {integrity: sha512-e4VWZETyXaKGhpkxOXP+B/d0Fp/zKViZoJmneZWe/05Y2aqSKj3YN2nLfYPJBQ87WEiY4BQCQ9hWGu9mPT1a1Q==}
engines: {node: '>= 18.0.0'}
hasBin: true
peerDependencies:
@@ -4009,8 +3989,8 @@ packages:
typescript:
optional: true
- svelte@5.37.1:
- resolution: {integrity: sha512-h8arWpQZ+3z8eahyBT5KkiBOUsG6xvI5Ykg0ozRr9xEdImgSMUPUlOFWRNkUsT7Ti0DSUCTEbPoped0aoxFyWA==}
+ svelte@5.46.1:
+ resolution: {integrity: sha512-ynjfCHD3nP2el70kN5Pmg37sSi0EjOm9FgHYQdC4giWG/hzO3AatzXXJJgP305uIhGQxSufJLuYWtkY8uK/8RA==}
engines: {node: '>=18'}
svg-tags@1.0.0:
@@ -4055,10 +4035,6 @@ packages:
resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==}
engines: {node: '>=18'}
- tinyglobby@0.2.10:
- resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==}
- engines: {node: '>=12.0.0'}
-
tinyglobby@0.2.14:
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
engines: {node: '>=12.0.0'}
@@ -4175,8 +4151,8 @@ packages:
peerDependencies:
typescript: '>=3.5.1'
- typescript@5.8.3:
- resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
+ typescript@5.9.3:
+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
hasBin: true
@@ -4264,18 +4240,6 @@ packages:
peerDependencies:
vite: '>=3'
- vite-plugin-pwa@1.0.2:
- resolution: {integrity: sha512-O3UwjsCnoDclgJANoOgzzqW7SFgwXE/th2OmUP/ILxHKwzWxxKDBu+B/Xa9Cv4IgSVSnj2HgRVIJ7F15+vQFkA==}
- engines: {node: '>=16.0.0'}
- peerDependencies:
- '@vite-pwa/assets-generator': ^1.0.0
- vite: ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
- workbox-build: ^7.3.0
- workbox-window: ^7.3.0
- peerDependenciesMeta:
- '@vite-pwa/assets-generator':
- optional: true
-
vite-plugin-pwa@1.2.0:
resolution: {integrity: sha512-a2xld+SJshT9Lgcv8Ji4+srFJL4k/1bVbd1x06JIkvecpQkwkvCncD1+gSzcdm3s+owWLpMJerG3aN5jupJEVw==}
engines: {node: '>=16.0.0'}
@@ -4288,8 +4252,8 @@ packages:
'@vite-pwa/assets-generator':
optional: true
- vite@7.0.6:
- resolution: {integrity: sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==}
+ vite@7.3.1:
+ resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -4455,8 +4419,8 @@ packages:
workbox-core@7.1.0:
resolution: {integrity: sha512-5KB4KOY8rtL31nEF7BfvU7FMzKT4B5TkbYa2tzkS+Peqj0gayMT9SytSFtNzlrvMaWgv6y/yvP9C0IbpFjV30Q==}
- workbox-core@7.3.0:
- resolution: {integrity: sha512-Z+mYrErfh4t3zi7NVTvOuACB0A/jA3bgxUN3PwtAVHvfEsZxV9Iju580VEETug3zYJRc0Dmii/aixI/Uxj8fmw==}
+ workbox-core@7.4.0:
+ resolution: {integrity: sha512-6BMfd8tYEnN4baG4emG9U0hdXM4gGuDU3ectXuVHnj71vwxTFI7WOpQJC4siTOlVtGqCUtj0ZQNsrvi6kZZTAQ==}
workbox-expiration@7.1.0:
resolution: {integrity: sha512-m5DcMY+A63rJlPTbbBNtpJ20i3enkyOtSgYfv/l8h+D6YbbNiA0zKEkCUaMsdDlxggla1oOfRkyqTvl5Ni5KQQ==}
@@ -4491,8 +4455,8 @@ packages:
workbox-window@7.1.0:
resolution: {integrity: sha512-ZHeROyqR+AS5UPzholQRDttLFqGMwP0Np8MKWAdyxsDETxq3qOAyXvqessc3GniohG6e0mAqSQyKOHmT8zPF7g==}
- workbox-window@7.3.0:
- resolution: {integrity: sha512-qW8PDy16OV1UBaUNGlTVcepzrlzyzNW/ZJvFQQs2j2TzGsg6IKjcpZC1RSquqQnTOafl5pCj5bGfAHlCjOOjdA==}
+ workbox-window@7.4.0:
+ resolution: {integrity: sha512-/bIYdBLAVsNR3v7gYGaV4pQW3M3kEPx5E8vDxGvxo6khTrGtSSCS7QiFKv9ogzBgZiy0OXLP9zO28U/1nF1mfw==}
wrap-ansi@6.2.0:
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
@@ -4559,11 +4523,6 @@ packages:
snapshots:
- '@ampproject/remapping@2.3.0':
- dependencies:
- '@jridgewell/gen-mapping': 0.3.5
- '@jridgewell/trace-mapping': 0.3.25
-
'@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)':
dependencies:
ajv: 8.17.1
@@ -5265,64 +5224,68 @@ snapshots:
'@codemirror/autocomplete@6.20.0':
dependencies:
- '@codemirror/language': 6.11.3
- '@codemirror/state': 6.5.2
- '@codemirror/view': 6.39.4
- '@lezer/common': 1.4.0
+ '@codemirror/language': 6.12.1
+ '@codemirror/state': 6.5.3
+ '@codemirror/view': 6.39.9
+ '@lezer/common': 1.5.0
+
+ '@codemirror/collab@6.1.1':
+ dependencies:
+ '@codemirror/state': 6.5.3
'@codemirror/commands@6.10.1':
dependencies:
- '@codemirror/language': 6.11.3
- '@codemirror/state': 6.5.2
- '@codemirror/view': 6.39.4
- '@lezer/common': 1.4.0
+ '@codemirror/language': 6.12.1
+ '@codemirror/state': 6.5.3
+ '@codemirror/view': 6.39.9
+ '@lezer/common': 1.5.0
'@codemirror/lang-javascript@6.2.4':
dependencies:
'@codemirror/autocomplete': 6.20.0
- '@codemirror/language': 6.11.3
- '@codemirror/lint': 6.8.1
- '@codemirror/state': 6.5.2
- '@codemirror/view': 6.39.4
- '@lezer/common': 1.4.0
+ '@codemirror/language': 6.12.1
+ '@codemirror/lint': 6.9.2
+ '@codemirror/state': 6.5.3
+ '@codemirror/view': 6.39.9
+ '@lezer/common': 1.5.0
'@lezer/javascript': 1.4.17
- '@codemirror/language@6.11.3':
+ '@codemirror/language@6.12.1':
dependencies:
- '@codemirror/state': 6.5.2
- '@codemirror/view': 6.39.4
- '@lezer/common': 1.4.0
+ '@codemirror/state': 6.5.3
+ '@codemirror/view': 6.39.9
+ '@lezer/common': 1.5.0
'@lezer/highlight': 1.2.3
- '@lezer/lr': 1.4.5
+ '@lezer/lr': 1.4.7
style-mod: 4.1.2
- '@codemirror/lint@6.8.1':
+ '@codemirror/lint@6.9.2':
dependencies:
- '@codemirror/state': 6.5.2
- '@codemirror/view': 6.39.4
+ '@codemirror/state': 6.5.3
+ '@codemirror/view': 6.39.9
crelt: 1.0.6
'@codemirror/merge@6.11.2':
dependencies:
- '@codemirror/language': 6.11.3
- '@codemirror/state': 6.5.2
- '@codemirror/view': 6.39.4
+ '@codemirror/language': 6.12.1
+ '@codemirror/state': 6.5.3
+ '@codemirror/view': 6.39.9
'@lezer/highlight': 1.2.3
style-mod: 4.1.2
- '@codemirror/search@6.5.6':
+ '@codemirror/search@6.6.0':
dependencies:
- '@codemirror/state': 6.5.2
- '@codemirror/view': 6.39.4
+ '@codemirror/state': 6.5.3
+ '@codemirror/view': 6.39.9
crelt: 1.0.6
- '@codemirror/state@6.5.2':
+ '@codemirror/state@6.5.3':
dependencies:
'@marijn/find-cluster-break': 1.0.2
- '@codemirror/view@6.39.4':
+ '@codemirror/view@6.39.9':
dependencies:
- '@codemirror/state': 6.5.2
+ '@codemirror/state': 6.5.3
crelt: 1.0.6
style-mod: 4.1.2
w3c-keyname: 2.2.8
@@ -5394,79 +5357,82 @@ snapshots:
'@dual-bundle/import-meta-resolve@4.2.1': {}
- '@esbuild/aix-ppc64@0.25.2':
+ '@esbuild/aix-ppc64@0.27.2':
+ optional: true
+
+ '@esbuild/android-arm64@0.27.2':
optional: true
- '@esbuild/android-arm64@0.25.2':
+ '@esbuild/android-arm@0.27.2':
optional: true
- '@esbuild/android-arm@0.25.2':
+ '@esbuild/android-x64@0.27.2':
optional: true
- '@esbuild/android-x64@0.25.2':
+ '@esbuild/darwin-arm64@0.27.2':
optional: true
- '@esbuild/darwin-arm64@0.25.2':
+ '@esbuild/darwin-x64@0.27.2':
optional: true
- '@esbuild/darwin-x64@0.25.2':
+ '@esbuild/freebsd-arm64@0.27.2':
optional: true
- '@esbuild/freebsd-arm64@0.25.2':
+ '@esbuild/freebsd-x64@0.27.2':
optional: true
- '@esbuild/freebsd-x64@0.25.2':
+ '@esbuild/linux-arm64@0.27.2':
optional: true
- '@esbuild/linux-arm64@0.25.2':
+ '@esbuild/linux-arm@0.27.2':
optional: true
- '@esbuild/linux-arm@0.25.2':
+ '@esbuild/linux-ia32@0.27.2':
optional: true
- '@esbuild/linux-ia32@0.25.2':
+ '@esbuild/linux-loong64@0.27.2':
optional: true
- '@esbuild/linux-loong64@0.25.2':
+ '@esbuild/linux-mips64el@0.27.2':
optional: true
- '@esbuild/linux-mips64el@0.25.2':
+ '@esbuild/linux-ppc64@0.27.2':
optional: true
- '@esbuild/linux-ppc64@0.25.2':
+ '@esbuild/linux-riscv64@0.27.2':
optional: true
- '@esbuild/linux-riscv64@0.25.2':
+ '@esbuild/linux-s390x@0.27.2':
optional: true
- '@esbuild/linux-s390x@0.25.2':
+ '@esbuild/linux-x64@0.27.2':
optional: true
- '@esbuild/linux-x64@0.25.2':
+ '@esbuild/netbsd-arm64@0.27.2':
optional: true
- '@esbuild/netbsd-arm64@0.25.2':
+ '@esbuild/netbsd-x64@0.27.2':
optional: true
- '@esbuild/netbsd-x64@0.25.2':
+ '@esbuild/openbsd-arm64@0.27.2':
optional: true
- '@esbuild/openbsd-arm64@0.25.2':
+ '@esbuild/openbsd-x64@0.27.2':
optional: true
- '@esbuild/openbsd-x64@0.25.2':
+ '@esbuild/openharmony-arm64@0.27.2':
optional: true
- '@esbuild/sunos-x64@0.25.2':
+ '@esbuild/sunos-x64@0.27.2':
optional: true
- '@esbuild/win32-arm64@0.25.2':
+ '@esbuild/win32-arm64@0.27.2':
optional: true
- '@esbuild/win32-ia32@0.25.2':
+ '@esbuild/win32-ia32@0.27.2':
optional: true
- '@esbuild/win32-x64@0.25.2':
+ '@esbuild/win32-x64@0.27.2':
optional: true
'@floating-ui/core@1.6.7':
@@ -5508,12 +5474,6 @@ snapshots:
'@jridgewell/sourcemap-codec': 1.5.5
'@jridgewell/trace-mapping': 0.3.31
- '@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/remapping@2.3.5':
dependencies:
'@jridgewell/gen-mapping': 0.3.13
@@ -5521,8 +5481,6 @@ snapshots:
'@jridgewell/resolve-uri@3.1.2': {}
- '@jridgewell/set-array@1.2.1': {}
-
'@jridgewell/source-map@0.3.11':
dependencies:
'@jridgewell/gen-mapping': 0.3.13
@@ -5530,15 +5488,8 @@ snapshots:
'@jridgewell/sourcemap-codec@1.4.15': {}
- '@jridgewell/sourcemap-codec@1.5.0': {}
-
'@jridgewell/sourcemap-codec@1.5.5': {}
- '@jridgewell/trace-mapping@0.3.25':
- dependencies:
- '@jridgewell/resolve-uri': 3.1.2
- '@jridgewell/sourcemap-codec': 1.5.0
-
'@jridgewell/trace-mapping@0.3.31':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
@@ -5552,26 +5503,26 @@ snapshots:
'@keyv/serialize@1.1.1': {}
- '@lezer/common@1.4.0': {}
+ '@lezer/common@1.5.0': {}
'@lezer/generator@1.8.0':
dependencies:
- '@lezer/common': 1.4.0
- '@lezer/lr': 1.4.5
+ '@lezer/common': 1.5.0
+ '@lezer/lr': 1.4.7
'@lezer/highlight@1.2.3':
dependencies:
- '@lezer/common': 1.4.0
+ '@lezer/common': 1.5.0
'@lezer/javascript@1.4.17':
dependencies:
- '@lezer/common': 1.4.0
+ '@lezer/common': 1.5.0
'@lezer/highlight': 1.2.3
- '@lezer/lr': 1.4.5
+ '@lezer/lr': 1.4.7
- '@lezer/lr@1.4.5':
+ '@lezer/lr@1.4.7':
dependencies:
- '@lezer/common': 1.4.0
+ '@lezer/common': 1.5.0
'@marijn/find-cluster-break@1.0.2': {}
@@ -5579,14 +5530,14 @@ snapshots:
'@matrix-org/matrix-sdk-crypto-wasm@15.1.0': {}
- '@melt-ui/pp@0.3.2(@melt-ui/svelte@0.86.6(svelte@5.37.1))(svelte@5.37.1)':
+ '@melt-ui/pp@0.3.2(@melt-ui/svelte@0.86.6(svelte@5.46.1))(svelte@5.46.1)':
dependencies:
- '@melt-ui/svelte': 0.86.6(svelte@5.37.1)
+ '@melt-ui/svelte': 0.86.6(svelte@5.46.1)
estree-walker: 3.0.3
magic-string: 0.30.10
- svelte: 5.37.1
+ svelte: 5.46.1
- '@melt-ui/svelte@0.86.6(svelte@5.37.1)':
+ '@melt-ui/svelte@0.86.6(svelte@5.46.1)':
dependencies:
'@floating-ui/core': 1.6.7
'@floating-ui/dom': 1.6.10
@@ -5594,14 +5545,14 @@ snapshots:
dequal: 2.0.3
focus-trap: 7.5.4
nanoid: 5.0.7
- svelte: 5.37.1
+ svelte: 5.46.1
- '@modyfi/vite-plugin-yaml@1.1.1(rollup@2.79.2)(vite@7.0.6(sass@1.97.0)(terser@5.44.1))':
+ '@modyfi/vite-plugin-yaml@1.1.1(rollup@2.79.2)(vite@7.3.1(sass@1.97.2)(terser@5.44.1))':
dependencies:
'@rollup/pluginutils': 5.1.0(rollup@2.79.2)
js-yaml: 4.1.0
tosource: 2.0.0-alpha.3
- vite: 7.0.6(sass@1.97.0)(terser@5.44.1)
+ vite: 7.3.1(sass@1.97.2)(terser@5.44.1)
transitivePeerDependencies:
- rollup
@@ -5809,55 +5760,53 @@ snapshots:
magic-string: 0.25.9
string.prototype.matchall: 4.0.12
- '@sveltejs/acorn-typescript@1.0.5(acorn@8.12.1)':
- dependencies:
- acorn: 8.12.1
-
'@sveltejs/acorn-typescript@1.0.5(acorn@8.15.0)':
dependencies:
acorn: 8.15.0
- '@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1)))(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1)))':
+ '@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.49.3(@sveltejs/vite-plugin-svelte@6.2.3(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1)))(svelte@5.46.1)(typescript@5.9.3)(vite@7.3.1(sass@1.97.2)(terser@5.44.1)))':
dependencies:
- '@sveltejs/kit': 2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1)))(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1))
+ '@sveltejs/kit': 2.49.3(@sveltejs/vite-plugin-svelte@6.2.3(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1)))(svelte@5.46.1)(typescript@5.9.3)(vite@7.3.1(sass@1.97.2)(terser@5.44.1))
- '@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1)))(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1))':
+ '@sveltejs/kit@2.49.3(@sveltejs/vite-plugin-svelte@6.2.3(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1)))(svelte@5.46.1)(typescript@5.9.3)(vite@7.3.1(sass@1.97.2)(terser@5.44.1))':
dependencies:
'@standard-schema/spec': 1.1.0
'@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
- '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1))
+ '@sveltejs/vite-plugin-svelte': 6.2.3(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1))
'@types/cookie': 0.6.0
acorn: 8.15.0
cookie: 0.6.0
devalue: 5.6.1
esm-env: 1.2.2
kleur: 4.1.5
- magic-string: 0.30.17
+ magic-string: 0.30.21
mrmime: 2.0.0
sade: 1.8.1
set-cookie-parser: 2.6.0
sirv: 3.0.0
- svelte: 5.37.1
- vite: 7.0.6(sass@1.97.0)(terser@5.44.1)
+ svelte: 5.46.1
+ vite: 7.3.1(sass@1.97.2)(terser@5.44.1)
+ optionalDependencies:
+ typescript: 5.9.3
- '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1)))(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1))':
+ '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.2.3(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1)))(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1))
- debug: 4.4.1(supports-color@8.1.1)
- svelte: 5.37.1
- vite: 7.0.6(sass@1.97.0)(terser@5.44.1)
+ '@sveltejs/vite-plugin-svelte': 6.2.3(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1))
+ debug: 4.4.3
+ svelte: 5.46.1
+ vite: 7.3.1(sass@1.97.2)(terser@5.44.1)
transitivePeerDependencies:
- supports-color
- '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1))':
+ '@sveltejs/vite-plugin-svelte@6.2.3(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1))':
dependencies:
- '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1)))(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1))
- debug: 4.4.1(supports-color@8.1.1)
+ '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.2.3(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1)))(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1))
deepmerge: 4.3.1
- magic-string: 0.30.17
- svelte: 5.37.1
- vite: 7.0.6(sass@1.97.0)(terser@5.44.1)
- vitefu: 1.1.1(vite@7.0.6(sass@1.97.0)(terser@5.44.1))
+ magic-string: 0.30.21
+ obug: 2.1.1
+ svelte: 5.46.1
+ vite: 7.3.1(sass@1.97.2)(terser@5.44.1)
+ vitefu: 1.1.1(vite@7.3.1(sass@1.97.2)(terser@5.44.1))
transitivePeerDependencies:
- supports-color
@@ -5928,8 +5877,6 @@ snapshots:
'@types/estree@1.0.6': {}
- '@types/estree@1.0.7': {}
-
'@types/estree@1.0.8': {}
'@types/events@3.0.3': {}
@@ -5964,12 +5911,12 @@ snapshots:
'@types/node': 20.14.10
optional: true
- '@vite-pwa/sveltekit@1.1.0(@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1)))(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1)))(vite@7.0.6(sass@1.97.0)(terser@5.44.1))(workbox-build@7.1.1)(workbox-window@7.3.0)':
+ '@vite-pwa/sveltekit@1.1.0(@sveltejs/kit@2.49.3(@sveltejs/vite-plugin-svelte@6.2.3(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1)))(svelte@5.46.1)(typescript@5.9.3)(vite@7.3.1(sass@1.97.2)(terser@5.44.1)))(vite@7.3.1(sass@1.97.2)(terser@5.44.1))(workbox-build@7.1.1)(workbox-window@7.4.0)':
dependencies:
- '@sveltejs/kit': 2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1)))(svelte@5.37.1)(vite@7.0.6(sass@1.97.0)(terser@5.44.1))
+ '@sveltejs/kit': 2.49.3(@sveltejs/vite-plugin-svelte@6.2.3(svelte@5.46.1)(vite@7.3.1(sass@1.97.2)(terser@5.44.1)))(svelte@5.46.1)(typescript@5.9.3)(vite@7.3.1(sass@1.97.2)(terser@5.44.1))
kolorist: 1.8.0
tinyglobby: 0.2.14
- vite-plugin-pwa: 1.2.0(vite@7.0.6(sass@1.97.0)(terser@5.44.1))(workbox-build@7.1.1)(workbox-window@7.3.0)
+ vite-plugin-pwa: 1.2.0(vite@7.3.1(sass@1.97.2)(terser@5.44.1))(workbox-build@7.1.1)(workbox-window@7.4.0)
transitivePeerDependencies:
- supports-color
- vite
@@ -5985,13 +5932,13 @@ snapshots:
chai: 6.2.1
tinyrainbow: 3.0.3
- '@vitest/mocker@4.0.16(vite@7.0.6(sass@1.97.0)(terser@5.44.1))':
+ '@vitest/mocker@4.0.16(vite@7.3.1(sass@1.97.2)(terser@5.44.1))':
dependencies:
'@vitest/spy': 4.0.16
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
- vite: 7.0.6(sass@1.97.0)(terser@5.44.1)
+ vite: 7.3.1(sass@1.97.2)(terser@5.44.1)
'@vitest/pretty-format@4.0.16':
dependencies:
@@ -6015,8 +5962,6 @@ snapshots:
'@vitest/pretty-format': 4.0.16
tinyrainbow: 3.0.3
- acorn@8.12.1: {}
-
acorn@8.15.0: {}
agent-base@7.1.3: {}
@@ -6315,11 +6260,11 @@ snapshots:
dependencies:
'@codemirror/autocomplete': 6.20.0
'@codemirror/commands': 6.10.1
- '@codemirror/language': 6.11.3
- '@codemirror/lint': 6.8.1
- '@codemirror/search': 6.5.6
- '@codemirror/state': 6.5.2
- '@codemirror/view': 6.39.4
+ '@codemirror/language': 6.12.1
+ '@codemirror/lint': 6.9.2
+ '@codemirror/search': 6.6.0
+ '@codemirror/state': 6.5.3
+ '@codemirror/view': 6.39.9
color-convert@1.9.3:
dependencies:
@@ -6366,14 +6311,14 @@ snapshots:
core-util-is@1.0.2: {}
- cosmiconfig@9.0.0(typescript@5.8.3):
+ cosmiconfig@9.0.0(typescript@5.9.3):
dependencies:
env-paths: 2.2.1
import-fresh: 3.3.0
js-yaml: 4.1.1
parse-json: 5.2.0
optionalDependencies:
- typescript: 5.8.3
+ typescript: 5.9.3
crelt@1.0.6: {}
@@ -6399,7 +6344,7 @@ snapshots:
crypto-random-string@2.0.0: {}
- css-declaration-sorter@7.2.0(postcss@8.5.6):
+ css-declaration-sorter@7.3.1(postcss@8.5.6):
dependencies:
postcss: 8.5.6
@@ -6945,33 +6890,34 @@ snapshots:
is-date-object: 1.1.0
is-symbol: 1.1.1
- esbuild@0.25.2:
+ esbuild@0.27.2:
optionalDependencies:
- '@esbuild/aix-ppc64': 0.25.2
- '@esbuild/android-arm': 0.25.2
- '@esbuild/android-arm64': 0.25.2
- '@esbuild/android-x64': 0.25.2
- '@esbuild/darwin-arm64': 0.25.2
- '@esbuild/darwin-x64': 0.25.2
- '@esbuild/freebsd-arm64': 0.25.2
- '@esbuild/freebsd-x64': 0.25.2
- '@esbuild/linux-arm': 0.25.2
- '@esbuild/linux-arm64': 0.25.2
- '@esbuild/linux-ia32': 0.25.2
- '@esbuild/linux-loong64': 0.25.2
- '@esbuild/linux-mips64el': 0.25.2
- '@esbuild/linux-ppc64': 0.25.2
- '@esbuild/linux-riscv64': 0.25.2
- '@esbuild/linux-s390x': 0.25.2
- '@esbuild/linux-x64': 0.25.2
- '@esbuild/netbsd-arm64': 0.25.2
- '@esbuild/netbsd-x64': 0.25.2
- '@esbuild/openbsd-arm64': 0.25.2
- '@esbuild/openbsd-x64': 0.25.2
- '@esbuild/sunos-x64': 0.25.2
- '@esbuild/win32-arm64': 0.25.2
- '@esbuild/win32-ia32': 0.25.2
- '@esbuild/win32-x64': 0.25.2
+ '@esbuild/aix-ppc64': 0.27.2
+ '@esbuild/android-arm': 0.27.2
+ '@esbuild/android-arm64': 0.27.2
+ '@esbuild/android-x64': 0.27.2
+ '@esbuild/darwin-arm64': 0.27.2
+ '@esbuild/darwin-x64': 0.27.2
+ '@esbuild/freebsd-arm64': 0.27.2
+ '@esbuild/freebsd-x64': 0.27.2
+ '@esbuild/linux-arm': 0.27.2
+ '@esbuild/linux-arm64': 0.27.2
+ '@esbuild/linux-ia32': 0.27.2
+ '@esbuild/linux-loong64': 0.27.2
+ '@esbuild/linux-mips64el': 0.27.2
+ '@esbuild/linux-ppc64': 0.27.2
+ '@esbuild/linux-riscv64': 0.27.2
+ '@esbuild/linux-s390x': 0.27.2
+ '@esbuild/linux-x64': 0.27.2
+ '@esbuild/netbsd-arm64': 0.27.2
+ '@esbuild/netbsd-x64': 0.27.2
+ '@esbuild/openbsd-arm64': 0.27.2
+ '@esbuild/openbsd-x64': 0.27.2
+ '@esbuild/openharmony-arm64': 0.27.2
+ '@esbuild/sunos-x64': 0.27.2
+ '@esbuild/win32-arm64': 0.27.2
+ '@esbuild/win32-ia32': 0.27.2
+ '@esbuild/win32-x64': 0.27.2
escalade@3.2.0: {}
@@ -6985,9 +6931,9 @@ snapshots:
pako: 2.1.0
tslib: 2.6.3
- esrap@2.1.0:
+ esrap@2.2.1:
dependencies:
- '@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/sourcemap-codec': 1.5.5
estree-walker@1.0.1: {}
@@ -7059,10 +7005,6 @@ snapshots:
dependencies:
pend: 1.2.0
- fdir@6.4.2(picomatch@4.0.2):
- optionalDependencies:
- picomatch: 4.0.2
-
fdir@6.4.6(picomatch@4.0.3):
optionalDependencies:
picomatch: 4.0.3
@@ -7536,7 +7478,7 @@ snapshots:
is-reference@3.0.3:
dependencies:
- '@types/estree': 1.0.7
+ '@types/estree': 1.0.8
is-regex@1.1.4:
dependencies:
@@ -7780,10 +7722,6 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.4.15
- magic-string@0.30.17:
- dependencies:
- '@jridgewell/sourcemap-codec': 1.5.0
-
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -8011,8 +7949,6 @@ snapshots:
picomatch@2.3.1: {}
- picomatch@4.0.2: {}
-
picomatch@4.0.3: {}
pidtree@0.3.1: {}
@@ -8065,19 +8001,19 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
- prettier-plugin-css-order@2.1.2(postcss@8.5.6)(prettier@3.7.4):
+ prettier-plugin-css-order@2.2.0(postcss@8.5.6)(prettier@3.7.4):
dependencies:
- css-declaration-sorter: 7.2.0(postcss@8.5.6)
+ css-declaration-sorter: 7.3.1(postcss@8.5.6)
postcss-less: 6.0.0(postcss@8.5.6)
postcss-scss: 4.0.9(postcss@8.5.6)
prettier: 3.7.4
transitivePeerDependencies:
- postcss
- prettier-plugin-svelte@3.4.1(prettier@3.7.4)(svelte@5.37.1):
+ prettier-plugin-svelte@3.4.1(prettier@3.7.4)(svelte@5.46.1):
dependencies:
prettier: 3.7.4
- svelte: 5.37.1
+ svelte: 5.46.1
prettier@3.7.4: {}
@@ -8289,7 +8225,7 @@ snapshots:
safer-buffer@2.1.2: {}
- sass@1.97.0:
+ sass@1.97.2:
dependencies:
chokidar: 4.0.1
immutable: 5.1.1
@@ -8414,10 +8350,10 @@ snapshots:
smob@1.5.0: {}
- socket.io-client@4.8.1:
+ socket.io-client@4.8.3:
dependencies:
'@socket.io/component-emitter': 3.1.2
- debug: 4.3.6
+ debug: 4.4.3
engine.io-client: 6.6.2
socket.io-parser: 4.2.4
transitivePeerDependencies:
@@ -8575,42 +8511,42 @@ snapshots:
style-mod@4.1.2: {}
- stylelint-config-html@1.1.0(postcss-html@1.7.0)(stylelint@16.26.1(typescript@5.8.3)):
+ stylelint-config-html@1.1.0(postcss-html@1.7.0)(stylelint@16.26.1(typescript@5.9.3)):
dependencies:
postcss-html: 1.7.0
- stylelint: 16.26.1(typescript@5.8.3)
+ stylelint: 16.26.1(typescript@5.9.3)
- stylelint-config-prettier-scss@1.0.0(stylelint@16.26.1(typescript@5.8.3)):
+ stylelint-config-prettier-scss@1.0.0(stylelint@16.26.1(typescript@5.9.3)):
dependencies:
- stylelint: 16.26.1(typescript@5.8.3)
+ stylelint: 16.26.1(typescript@5.9.3)
- stylelint-config-recommended-scss@16.0.2(postcss@8.5.6)(stylelint@16.26.1(typescript@5.8.3)):
+ stylelint-config-recommended-scss@16.0.2(postcss@8.5.6)(stylelint@16.26.1(typescript@5.9.3)):
dependencies:
postcss-scss: 4.0.9(postcss@8.5.6)
- stylelint: 16.26.1(typescript@5.8.3)
- stylelint-config-recommended: 17.0.0(stylelint@16.26.1(typescript@5.8.3))
- stylelint-scss: 6.12.1(stylelint@16.26.1(typescript@5.8.3))
+ stylelint: 16.26.1(typescript@5.9.3)
+ stylelint-config-recommended: 17.0.0(stylelint@16.26.1(typescript@5.9.3))
+ stylelint-scss: 6.12.1(stylelint@16.26.1(typescript@5.9.3))
optionalDependencies:
postcss: 8.5.6
- stylelint-config-recommended@17.0.0(stylelint@16.26.1(typescript@5.8.3)):
+ stylelint-config-recommended@17.0.0(stylelint@16.26.1(typescript@5.9.3)):
dependencies:
- stylelint: 16.26.1(typescript@5.8.3)
+ stylelint: 16.26.1(typescript@5.9.3)
- stylelint-config-standard-scss@16.0.0(postcss@8.5.6)(stylelint@16.26.1(typescript@5.8.3)):
+ stylelint-config-standard-scss@16.0.0(postcss@8.5.6)(stylelint@16.26.1(typescript@5.9.3)):
dependencies:
- stylelint: 16.26.1(typescript@5.8.3)
- stylelint-config-recommended-scss: 16.0.2(postcss@8.5.6)(stylelint@16.26.1(typescript@5.8.3))
- stylelint-config-standard: 39.0.1(stylelint@16.26.1(typescript@5.8.3))
+ stylelint: 16.26.1(typescript@5.9.3)
+ stylelint-config-recommended-scss: 16.0.2(postcss@8.5.6)(stylelint@16.26.1(typescript@5.9.3))
+ stylelint-config-standard: 39.0.1(stylelint@16.26.1(typescript@5.9.3))
optionalDependencies:
postcss: 8.5.6
- stylelint-config-standard@39.0.1(stylelint@16.26.1(typescript@5.8.3)):
+ stylelint-config-standard@39.0.1(stylelint@16.26.1(typescript@5.9.3)):
dependencies:
- stylelint: 16.26.1(typescript@5.8.3)
- stylelint-config-recommended: 17.0.0(stylelint@16.26.1(typescript@5.8.3))
+ stylelint: 16.26.1(typescript@5.9.3)
+ stylelint-config-recommended: 17.0.0(stylelint@16.26.1(typescript@5.9.3))
- stylelint-scss@6.12.1(stylelint@16.26.1(typescript@5.8.3)):
+ stylelint-scss@6.12.1(stylelint@16.26.1(typescript@5.9.3)):
dependencies:
css-tree: 3.1.0
is-plain-object: 5.0.0
@@ -8620,9 +8556,9 @@ snapshots:
postcss-resolve-nested-selector: 0.1.6
postcss-selector-parser: 7.1.0
postcss-value-parser: 4.2.0
- stylelint: 16.26.1(typescript@5.8.3)
+ stylelint: 16.26.1(typescript@5.9.3)
- stylelint@16.26.1(typescript@5.8.3):
+ stylelint@16.26.1(typescript@5.9.3):
dependencies:
'@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
'@csstools/css-syntax-patches-for-csstree': 1.0.21
@@ -8632,7 +8568,7 @@ snapshots:
'@dual-bundle/import-meta-resolve': 4.2.1
balanced-match: 2.0.0
colord: 2.9.3
- cosmiconfig: 9.0.0(typescript@5.8.3)
+ cosmiconfig: 9.0.0(typescript@5.9.3)
css-functions-list: 3.2.3
css-tree: 3.1.0
debug: 4.4.3
@@ -8686,42 +8622,43 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
- svelte-check@4.3.4(picomatch@4.0.3)(svelte@5.37.1)(typescript@5.8.3):
+ svelte-check@4.3.5(picomatch@4.0.3)(svelte@5.46.1)(typescript@5.9.3):
dependencies:
- '@jridgewell/trace-mapping': 0.3.25
+ '@jridgewell/trace-mapping': 0.3.31
chokidar: 4.0.1
- fdir: 6.4.6(picomatch@4.0.3)
+ fdir: 6.5.0(picomatch@4.0.3)
picocolors: 1.1.1
sade: 1.8.1
- svelte: 5.37.1
- typescript: 5.8.3
+ svelte: 5.46.1
+ typescript: 5.9.3
transitivePeerDependencies:
- picomatch
- svelte-preprocess@6.0.3(@babel/core@7.28.5)(postcss@8.5.6)(sass@1.97.0)(svelte@5.37.1)(typescript@5.8.3):
+ svelte-preprocess@6.0.3(@babel/core@7.28.5)(postcss@8.5.6)(sass@1.97.2)(svelte@5.46.1)(typescript@5.9.3):
dependencies:
- svelte: 5.37.1
+ svelte: 5.46.1
optionalDependencies:
'@babel/core': 7.28.5
postcss: 8.5.6
- sass: 1.97.0
- typescript: 5.8.3
+ sass: 1.97.2
+ typescript: 5.9.3
- svelte@5.37.1:
+ svelte@5.46.1:
dependencies:
- '@ampproject/remapping': 2.3.0
- '@jridgewell/sourcemap-codec': 1.5.0
- '@sveltejs/acorn-typescript': 1.0.5(acorn@8.12.1)
- '@types/estree': 1.0.7
- acorn: 8.12.1
+ '@jridgewell/remapping': 2.3.5
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
+ '@types/estree': 1.0.8
+ acorn: 8.15.0
aria-query: 5.3.2
axobject-query: 4.1.0
clsx: 2.1.1
+ devalue: 5.6.1
esm-env: 1.2.2
- esrap: 2.1.0
+ esrap: 2.2.1
is-reference: 3.0.3
locate-character: 3.0.0
- magic-string: 0.30.17
+ magic-string: 0.30.21
zimmerframe: 1.1.2
svg-tags@1.0.0: {}
@@ -8764,11 +8701,6 @@ snapshots:
tinyexec@1.0.2: {}
- tinyglobby@0.2.10:
- dependencies:
- fdir: 6.4.2(picomatch@4.0.2)
- picomatch: 4.0.2
-
tinyglobby@0.2.14:
dependencies:
fdir: 6.4.6(picomatch@4.0.3)
@@ -8898,11 +8830,11 @@ snapshots:
possible-typed-array-names: 1.1.0
reflect.getprototypeof: 1.0.10
- typesafe-i18n@5.26.2(typescript@5.8.3):
+ typesafe-i18n@5.26.2(typescript@5.9.3):
dependencies:
- typescript: 5.8.3
+ typescript: 5.9.3
- typescript@5.8.3: {}
+ typescript@5.9.3: {}
unbox-primitive@1.0.2:
dependencies:
@@ -8981,58 +8913,47 @@ snapshots:
core-util-is: 1.0.2
extsprintf: 1.3.0
- vite-plugin-mkcert@1.17.9(vite@7.0.6(sass@1.97.0)(terser@5.44.1)):
+ vite-plugin-mkcert@1.17.9(vite@7.3.1(sass@1.97.2)(terser@5.44.1)):
dependencies:
axios: 1.13.2(debug@4.4.3)
debug: 4.4.3
picocolors: 1.1.1
- vite: 7.0.6(sass@1.97.0)(terser@5.44.1)
- transitivePeerDependencies:
- - supports-color
-
- vite-plugin-pwa@1.0.2(vite@7.0.6(sass@1.97.0)(terser@5.44.1))(workbox-build@7.1.1)(workbox-window@7.3.0):
- dependencies:
- debug: 4.4.0(supports-color@8.1.1)
- pretty-bytes: 6.1.1
- tinyglobby: 0.2.10
- vite: 7.0.6(sass@1.97.0)(terser@5.44.1)
- workbox-build: 7.1.1
- workbox-window: 7.3.0
+ vite: 7.3.1(sass@1.97.2)(terser@5.44.1)
transitivePeerDependencies:
- supports-color
- vite-plugin-pwa@1.2.0(vite@7.0.6(sass@1.97.0)(terser@5.44.1))(workbox-build@7.1.1)(workbox-window@7.3.0):
+ vite-plugin-pwa@1.2.0(vite@7.3.1(sass@1.97.2)(terser@5.44.1))(workbox-build@7.1.1)(workbox-window@7.4.0):
dependencies:
- debug: 4.4.1(supports-color@8.1.1)
+ debug: 4.4.3
pretty-bytes: 6.1.1
- tinyglobby: 0.2.14
- vite: 7.0.6(sass@1.97.0)(terser@5.44.1)
+ tinyglobby: 0.2.15
+ vite: 7.3.1(sass@1.97.2)(terser@5.44.1)
workbox-build: 7.1.1
- workbox-window: 7.3.0
+ workbox-window: 7.4.0
transitivePeerDependencies:
- supports-color
- vite@7.0.6(sass@1.97.0)(terser@5.44.1):
+ vite@7.3.1(sass@1.97.2)(terser@5.44.1):
dependencies:
- esbuild: 0.25.2
- fdir: 6.4.6(picomatch@4.0.3)
+ esbuild: 0.27.2
+ fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.6
rollup: 4.46.1
- tinyglobby: 0.2.14
+ tinyglobby: 0.2.15
optionalDependencies:
fsevents: 2.3.3
- sass: 1.97.0
+ sass: 1.97.2
terser: 5.44.1
- vitefu@1.1.1(vite@7.0.6(sass@1.97.0)(terser@5.44.1)):
+ vitefu@1.1.1(vite@7.3.1(sass@1.97.2)(terser@5.44.1)):
optionalDependencies:
- vite: 7.0.6(sass@1.97.0)(terser@5.44.1)
+ vite: 7.3.1(sass@1.97.2)(terser@5.44.1)
- vitest@4.0.16(jsdom@26.1.0)(sass@1.97.0)(terser@5.44.1):
+ vitest@4.0.16(jsdom@26.1.0)(sass@1.97.2)(terser@5.44.1):
dependencies:
'@vitest/expect': 4.0.16
- '@vitest/mocker': 4.0.16(vite@7.0.6(sass@1.97.0)(terser@5.44.1))
+ '@vitest/mocker': 4.0.16(vite@7.3.1(sass@1.97.2)(terser@5.44.1))
'@vitest/pretty-format': 4.0.16
'@vitest/runner': 4.0.16
'@vitest/snapshot': 4.0.16
@@ -9049,7 +8970,7 @@ snapshots:
tinyexec: 1.0.2
tinyglobby: 0.2.15
tinyrainbow: 3.0.3
- vite: 7.0.6(sass@1.97.0)(terser@5.44.1)
+ vite: 7.3.1(sass@1.97.2)(terser@5.44.1)
why-is-node-running: 2.3.0
optionalDependencies:
jsdom: 26.1.0
@@ -9223,7 +9144,7 @@ snapshots:
workbox-core@7.1.0: {}
- workbox-core@7.3.0: {}
+ workbox-core@7.4.0: {}
workbox-expiration@7.1.0:
dependencies:
@@ -9280,10 +9201,10 @@ snapshots:
'@types/trusted-types': 2.0.7
workbox-core: 7.1.0
- workbox-window@7.3.0:
+ workbox-window@7.4.0:
dependencies:
'@types/trusted-types': 2.0.7
- workbox-core: 7.3.0
+ workbox-core: 7.4.0
wrap-ansi@6.2.0:
dependencies:
diff --git a/src/lib/backup/backup.ts b/src/lib/backup/backup.ts
index 2bfb9012c..0c34e0f67 100644
--- a/src/lib/backup/backup.ts
+++ b/src/lib/backup/backup.ts
@@ -6,15 +6,9 @@ import type {
CharaSettingsFile,
} from "$lib/share/chara-file.js";
import type { Change } from "$lib/undo-redo.js";
-import {
- changes,
- ChangeType,
- chords,
- layout,
- settings,
-} from "$lib/undo-redo.js";
+import { changes, ChangeType, layout, settings } from "$lib/undo-redo.js";
import { get } from "svelte/store";
-import { activeProfile, serialPort } from "../serial/connection";
+import { activeProfile, deviceChords, serialPort } from "../serial/connection";
import { csvLayoutToJson, isCsvLayout } from "$lib/backup/compat/legacy-layout";
import { isCsvChords, csvChordsToJson } from "./compat/legacy-chords";
@@ -60,7 +54,7 @@ export function createChordBackup(): CharaChordFile {
return {
charaVersion: 1,
type: "chords",
- chords: get(chords).map((it) => [it.actions, it.phrase]),
+ chords: get(deviceChords).map((it) => [it.actions, it.phrase]),
};
}
@@ -168,7 +162,9 @@ export function restoreFromFile(
export function getChangesFromChordFile(file: CharaChordFile) {
const changes: Change[] = [];
const existingChords = new Set(
- get(chords).map(({ phrase, actions }) => JSON.stringify([actions, phrase])),
+ get(deviceChords).map(({ phrase, actions }) =>
+ JSON.stringify([actions, phrase]),
+ ),
);
for (const [input, output] of file.chords) {
if (existingChords.has(JSON.stringify([input, output]))) {
diff --git a/src/lib/chord-editor/ChangesPanel.svelte b/src/lib/chord-editor/ChangesPanel.svelte
new file mode 100644
index 000000000..cc9955379
--- /dev/null
+++ b/src/lib/chord-editor/ChangesPanel.svelte
@@ -0,0 +1,149 @@
+
+
+
+ {#if added + changed + removed !== 0 || $syncStatus === "uploading" || $syncStatus === "error"}
+
+
+ save
+ {$LL.saveActions.SAVE()}
+
+
+ {/if}
+
+
+ {#if added}
+ +{added}
+ {/if}
+ {#if changed}
+ ~{changed}
+ {/if}
+ {#if removed}
+ -{removed}
+ {/if}
+
+
+ {#if parsed.aliases.size > 0}
+
+ content_copy
+ {parsed.aliases.size}
+
+ {/if}
+
+
+
diff --git a/src/lib/chord-editor/action-linter.ts b/src/lib/chord-editor/action-linter.ts
new file mode 100644
index 000000000..3930eb4be
--- /dev/null
+++ b/src/lib/chord-editor/action-linter.ts
@@ -0,0 +1,156 @@
+import { linter, type Diagnostic } from "@codemirror/lint";
+import { parsedChordsField } from "./parsed-chords-plugin";
+
+export function actionLinter(config?: Parameters[1]) {
+ const finalConfig: Parameters[1] = {
+ ...config,
+ needsRefresh(update) {
+ return (
+ update.startState.field(parsedChordsField) !==
+ update.state.field(parsedChordsField)
+ );
+ },
+ };
+ return linter((view) => {
+ console.log("lint");
+ const diagnostics: Diagnostic[] = [];
+ const parsed = view.state.field(parsedChordsField);
+
+ for (const chord of parsed.chords) {
+ if (chord.disabled) {
+ diagnostics.push({
+ from: chord.range[0],
+ to: chord.range[1],
+ severity: "info",
+ markClass: "chord-ignored",
+ message: `Chord disabled`,
+ });
+ }
+ if (chord.compounds) {
+ for (const compound of chord.compounds) {
+ if (compound.actions.length === 0 && compound.parent) {
+ const replacement = view.state.doc.sliceString(
+ compound.parent.range[0],
+ compound.parent.input!.range[1],
+ );
+ diagnostics.push({
+ from: compound.range[0],
+ to: compound.range[1],
+ severity: "warning",
+ message: `Compound literal can be replaced with "${replacement}"`,
+ actions: [
+ {
+ name: "Replace",
+ apply(view, from, to) {
+ view.dispatch({
+ changes: {
+ from,
+ to,
+ insert: replacement + "|",
+ },
+ });
+ },
+ },
+ ],
+ });
+ }
+ }
+ const lastCompound = chord.compounds.at(-1);
+ if (lastCompound) {
+ const from = chord.range[0];
+ const to = lastCompound.range[1];
+ if (lastCompound.parent) {
+ diagnostics.push({
+ from,
+ to,
+ severity: "info",
+ markClass: "chord-child",
+ message: `Child of ${view.state.doc.sliceString(lastCompound.parent.range[0], lastCompound.parent.range[1])}`,
+ actions: [
+ {
+ name: "Select Parent",
+ apply(view) {
+ view.dispatch({
+ selection: {
+ anchor: lastCompound.parent!.range[0],
+ },
+ scrollIntoView: true,
+ });
+ },
+ },
+ ],
+ });
+ } else {
+ diagnostics.push({
+ from,
+ to,
+ severity: "warning",
+ message: `Orphan compound`,
+ });
+ }
+ }
+ }
+ if (chord.children) {
+ diagnostics.push({
+ from: chord.range[0],
+ to: chord.range[1],
+ severity: "info",
+ markClass: "chord-parent",
+ message: `Parent of ${chord.children.length} compound(s)`,
+ actions: chord.children.map((child) => ({
+ name: `Go to ${view.state.doc.sliceString(child.range[0], child.range[1])}`,
+ apply(view) {
+ view.dispatch({
+ selection: {
+ anchor: child.range[0],
+ },
+ scrollIntoView: true,
+ });
+ },
+ })),
+ });
+ }
+ if (chord.phrase) {
+ if (!chord.phrase.originalValue) {
+ diagnostics.push({
+ from: chord.range[0],
+ to: chord.range[1],
+ severity: "info",
+ markClass: "chord-new",
+ message: `New Chord`,
+ });
+ } else if (chord.phrase.originalValue !== chord.phrase.value) {
+ diagnostics.push({
+ from: chord.range[0],
+ to: chord.range[1],
+ severity: "info",
+ markClass: "chord-unchanged",
+ message: `Phrase changed`,
+ });
+ }
+
+ if (chord.aliases) {
+ diagnostics.push({
+ from: chord.phrase.range[0],
+ to: chord.phrase.range[1],
+ severity: "warning",
+ markClass: "chord-alias",
+ message: `Alias of ${chord.aliases.length} chord(s)`,
+ actions: chord.aliases.map((alias) => ({
+ name: `Go to ${view.state.doc.sliceString(alias.range[0], alias.input?.range[1] ?? alias.range[1])}`,
+ apply(view) {
+ view.dispatch({
+ selection: {
+ anchor: alias.range[0],
+ },
+ scrollIntoView: true,
+ });
+ },
+ })),
+ });
+ }
+ }
+ }
+ return diagnostics;
+ }, finalConfig);
+}
diff --git a/src/lib/chord-editor/action-meta-plugin.ts b/src/lib/chord-editor/action-meta-plugin.ts
new file mode 100644
index 000000000..446ec2c4c
--- /dev/null
+++ b/src/lib/chord-editor/action-meta-plugin.ts
@@ -0,0 +1,10 @@
+import { KEYMAP_CODES, KEYMAP_IDS } from "$lib/serial/keymap-codes";
+import { derived } from "svelte/store";
+import { reactiveStateField } from "./store-state-field";
+
+const actionMeta = derived([KEYMAP_IDS, KEYMAP_CODES], ([ids, codes]) => ({
+ ids,
+ codes,
+}));
+
+export const actionMetaPlugin = reactiveStateField(actionMeta);
diff --git a/src/lib/chord-editor/action-plugin.ts b/src/lib/chord-editor/action-plugin.ts
index cbf8a10a1..b2377fcbd 100644
--- a/src/lib/chord-editor/action-plugin.ts
+++ b/src/lib/chord-editor/action-plugin.ts
@@ -7,33 +7,34 @@ import {
} from "@codemirror/view";
import { mount, unmount } from "svelte";
import Action from "$lib/components/Action.svelte";
-import { syntaxTree } from "@codemirror/language";
import type { Range } from "@codemirror/state";
+import { parsedChordsField } from "./parsed-chords-plugin";
+import { iterActions } from "./parse-meta";
export class ActionWidget extends WidgetType {
component?: {};
- element?: HTMLElement;
constructor(readonly id: string | number) {
super();
this.id = id;
}
- override eq(other: ActionWidget) {
+ /*override eq(other: ActionWidget) {
return this.id == other.id;
- }
+ }*/
toDOM() {
- if (!this.element) {
- this.element = document.createElement("span");
- this.element.style.paddingInline = "2px";
-
- this.component = mount(Action, {
- target: this.element,
- props: { action: this.id, display: "keys", inText: true },
- });
+ if (this.component) {
+ unmount(this.component);
}
- return this.element;
+ const element = document.createElement("span");
+ element.style.paddingInline = "2px";
+
+ this.component = mount(Action, {
+ target: element,
+ props: { action: this.id, display: "keys", inText: true },
+ });
+ return element;
}
override ignoreEvent() {
@@ -50,29 +51,24 @@ export class ActionWidget extends WidgetType {
function actionWidgets(view: EditorView) {
const widgets: Range[] = [];
for (const { from, to } of view.visibleRanges) {
- syntaxTree(view.state).iterate({
- from,
- to,
- enter: (node) => {
- if (node.name !== "ExplicitAction") return;
- const value =
- node.node.getChild("ActionId") ??
- node.node.getChild("HexNumber") ??
- node.node.getChild("DecimalNumber");
- if (!value) return;
- if (!node.node.getChild("ExplicitDelimEnd")) {
+ for (const chord of view.state.field(parsedChordsField).chords) {
+ if (chord.range[1] < from || chord.range[0] > to) continue;
+ iterActions(chord, (action) => {
+ if (
+ view.state.selection.ranges.some(
+ (r) => r.from <= action.range[1] && r.to > action.range[0],
+ )
+ ) {
return;
}
-
- const id = view.state.doc.sliceString(value.from, value.to);
- let deco = Decoration.replace({
- widget: new ActionWidget(
- value.name === "ActionId" ? id : parseInt(id),
- ),
- });
- widgets.push(deco.range(node.from, node.to));
- },
- });
+ if (action.info && action.explicit) {
+ const deco = Decoration.replace({
+ widget: new ActionWidget(action.code),
+ });
+ widgets.push(deco.range(action.range[0], action.range[1]));
+ }
+ });
+ }
}
return Decoration.set(widgets);
}
@@ -89,7 +85,9 @@ export const actionPlugin = ViewPlugin.fromClass(
if (
update.docChanged ||
update.viewportChanged ||
- syntaxTree(update.startState) != syntaxTree(update.state)
+ update.selectionSet ||
+ update.startState.field(parsedChordsField) !=
+ update.state.field(parsedChordsField)
)
this.decorations = actionWidgets(update.view);
}
diff --git a/src/lib/chord-editor/action-serializer.ts b/src/lib/chord-editor/action-serializer.ts
index d2781d637..7e4b746ac 100644
--- a/src/lib/chord-editor/action-serializer.ts
+++ b/src/lib/chord-editor/action-serializer.ts
@@ -1,16 +1,263 @@
-import { KEYMAP_CODES, type KeyInfo } from "$lib/serial/keymap-codes";
-import { get } from "svelte/store";
+import type { KeyInfo } from "$lib/serial/keymap-codes";
+import type { CharaChordFile } from "$lib/share/chara-file";
+import {
+ composeChordInput,
+ hasConcatenator,
+ hashChord,
+ willBeValidChordInput,
+} from "$lib/serial/chord";
+import type {
+ ActionMeta,
+ ChordMeta,
+ MetaRange,
+ ParseResult,
+} from "./parse-meta";
+import type { Tree } from "@lezer/common";
-export function canUseIdAsString(info: KeyInfo): boolean {
- return !!info.id && /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(info.id);
+function parseChordMeta(
+ tree: Tree,
+ ids: Map,
+ codes: Map,
+ sliceString: (from: number, to: number) => string,
+): ChordMeta[] {
+ console.time("parseChordTree");
+ const result: ChordMeta[] = [];
+
+ let current: ChordMeta = { range: [0, 0], valid: false };
+ let actions: ActionMeta[] = [];
+ let actionRange: MetaRange | undefined = undefined;
+
+ tree.cursor().iterate(
+ (node) => {
+ if (node.name === "Action") {
+ actionRange = [node.from, node.to];
+ } else if (node.name === "ChordPhrase") {
+ current.phrase = {
+ range: [node.from, node.to],
+ value: [],
+ valid: true,
+ actions: [],
+ hasConcatenator: false,
+ };
+ } else if (node.name === "Chord") {
+ current = { range: [node.from, node.to], valid: false };
+ } else if (node.name === "ActionString") {
+ actions = [];
+ } else if (node.name === "HexNumber") {
+ const hexString = sliceString(node.from, node.to);
+ const code = Number.parseInt(hexString, 16);
+ const parentNode = node.node.parent;
+ if (parentNode?.type.name === "CompoundLiteral") {
+ current.compounds ??= [];
+ current.compounds.push({
+ range: [parentNode.from, parentNode.to],
+ value: code,
+ actions: [],
+ valid: true, // TODO: validate compound literal
+ });
+ } else {
+ const valid = !(Number.isNaN(code) || code < 0 || code > 1023);
+ actions.push({
+ code,
+ info: codes.get(code),
+ explicit: true,
+ valid,
+ range: actionRange!,
+ });
+ }
+ } else if (
+ node.name === "ActionId" ||
+ node.name === "SingleLetter" ||
+ node.name === "EscapedLetter"
+ ) {
+ const id = sliceString(node.from, node.to);
+ const info = ids.get(id);
+ const value: ActionMeta = {
+ code: info?.code ?? Number.NaN,
+ info,
+ valid: info !== undefined,
+ range: actionRange!,
+ };
+ if (node.name === "ActionId") {
+ value.explicit = true;
+ }
+ actions.push(value);
+ }
+ },
+ (node) => {
+ if (node.name === "Chord") {
+ result.push(current);
+ if (current.phrase) {
+ current.phrase.actions = actions;
+ current.phrase.value = actions.map(({ code }) => code);
+ current.phrase.valid = actions.every(({ valid }) => valid);
+ current.phrase.hasConcatenator = hasConcatenator(
+ current.phrase.value,
+ codes,
+ );
+ }
+ current.valid =
+ (current.phrase?.valid ?? false) && (current.input?.valid ?? false);
+ if (!current.valid) {
+ current.disabled = true;
+ }
+ } else if (node.name === "CompoundInput") {
+ const lastCompound = current.compounds?.at(-1);
+ current.compounds ??= [];
+ current.compounds.push({
+ range: [node.from, node.to],
+ value: hashChord(
+ composeChordInput(
+ actions.map(({ code }) => code),
+ lastCompound?.value,
+ ),
+ ),
+ actions,
+ valid:
+ willBeValidChordInput(actions.length, lastCompound !== undefined) &&
+ actions.every(({ valid }) => valid),
+ });
+ } else if (node.name === "ChordInput") {
+ const lastCompound = current.compounds?.at(-1);
+ current.input = {
+ range: [node.from, node.to],
+ value: composeChordInput(
+ actions.map(({ code }) => code),
+ lastCompound?.value,
+ ),
+ valid:
+ willBeValidChordInput(actions.length, lastCompound !== undefined) &&
+ actions.every(({ valid }) => valid),
+ actions,
+ };
+ }
+ },
+ );
+
+ console.timeEnd("parseChordTree");
+ return result;
+}
+
+function resolveChordOverrides(chords: ChordMeta[]): Map {
+ console.time("resolveOverrides");
+ const seen = new Map();
+ for (const info of chords) {
+ if (!info.input || info.disabled) continue;
+ const key = JSON.stringify(info.input.value);
+ const override = seen.get(key);
+ if (override) {
+ override.overrides ??= [];
+ override.overrides.push(info);
+ info.overriddenBy = override;
+ info.disabled = true;
+ } else {
+ seen.set(key, info);
+ }
+ }
+ console.timeEnd("resolveOverrides");
+ return seen;
+}
+
+function resolveChordAliases(chords: ChordMeta[]): Map {
+ console.time("resolveAliases");
+ const aliases = new Map();
+ for (const info of chords) {
+ if (!info.phrase) continue;
+ const key = JSON.stringify(info.phrase.value);
+ const list = aliases.get(key) ?? [];
+ list.push(info);
+ aliases.set(key, list);
+ }
+ for (const [key, value] of aliases) {
+ if (value.length <= 1) {
+ aliases.delete(key);
+ } else {
+ for (const info of value) {
+ info.aliases = value.filter((i) => i !== info);
+ }
+ }
+ }
+ console.timeEnd("resolveAliases");
+ return aliases;
}
-export function actionToValue(action: number | KeyInfo) {
- const info =
- typeof action === "number" ? get(KEYMAP_CODES).get(action) : action;
- if (info && info.id?.length === 1)
- return /^[<>\\\s]$/.test(info.id) ? `\\${info.id}` : info.id;
- if (!info || !canUseIdAsString(info))
- return `<0x${(info?.code ?? action).toString(16).padStart(2, "0")}>`;
- return `<${info.id}>`;
+function resolveCompoundParents(chords: ChordMeta[]): Map {
+ console.time("resolveCompoundParents");
+ const compounds = new Map();
+ for (const chord of chords) {
+ if (chord.input && !chord.disabled) {
+ compounds.set(hashChord(chord.input.value), chord);
+ }
+ }
+ for (const chord of chords) {
+ if (chord.compounds) {
+ for (const compound of chord.compounds) {
+ const parent = compounds.get(compound.value);
+ if (parent) {
+ compound.parent = parent;
+ }
+ }
+ const lastCompound = chord.compounds?.at(-1);
+ if (lastCompound && lastCompound.parent) {
+ lastCompound.parent.children ??= [];
+ lastCompound.parent.children.push(chord);
+ }
+ }
+ }
+ console.timeEnd("resolveCompoundParents");
+ return compounds;
+}
+
+export function resolveChanges(
+ chords: ChordMeta[],
+ inputs: Map,
+ deviceChords: CharaChordFile["chords"],
+): [CharaChordFile["chords"], Map] {
+ console.time("resolveChanges");
+ const removed: CharaChordFile["chords"] = [];
+ const exact = new Map();
+ for (const chord of chords) {
+ if (chord.input && chord.phrase && !chord.disabled) {
+ exact.set(
+ JSON.stringify([chord.input.value, chord.phrase?.value ?? []]),
+ chord,
+ );
+ }
+ }
+ for (const deviceChord of deviceChords) {
+ const exactMatch = exact.get(JSON.stringify(deviceChord));
+ if (exactMatch) {
+ exactMatch.phrase!.originalValue = exactMatch.phrase!.value;
+ continue;
+ }
+ const byInput = inputs.get(JSON.stringify(deviceChord[0]));
+ if (byInput) {
+ byInput.phrase!.originalValue = deviceChord[1];
+ continue;
+ }
+ removed.push(deviceChord);
+ }
+
+ console.timeEnd("resolveChanges");
+ return [removed, exact];
+}
+
+export function parseCharaChords(
+ tree: Tree,
+ ids: Map,
+ codes: Map,
+ deviceChords: CharaChordFile["chords"],
+ sliceString: (from: number, to: number) => string,
+): ParseResult {
+ console.time("parseTotal");
+
+ const chords = parseChordMeta(tree, ids, codes, sliceString);
+ const inputs = resolveChordOverrides(chords);
+ const aliases = resolveChordAliases(chords);
+ const compounds = resolveCompoundParents(chords);
+ const [removed, exact] = resolveChanges(chords, inputs, deviceChords);
+
+ console.timeEnd("parseTotal");
+
+ return { chords, removed, aliases, compounds, inputs, exact };
}
diff --git a/src/lib/chord-editor/autocomplete.ts b/src/lib/chord-editor/autocomplete.ts
index abb796e53..42697d6d6 100644
--- a/src/lib/chord-editor/autocomplete.ts
+++ b/src/lib/chord-editor/autocomplete.ts
@@ -1,72 +1,39 @@
-import { KEYMAP_CATEGORIES, KEYMAP_CODES } from "$lib/serial/keymap-codes";
-import type {
- Completion,
- CompletionSection,
- CompletionSource,
-} from "@codemirror/autocomplete";
-import { derived, get } from "svelte/store";
-import { actionToValue, canUseIdAsString } from "./action-serializer";
+import {
+ EditorView,
+ ViewPlugin,
+ ViewUpdate,
+ type PluginValue,
+} from "@codemirror/view";
+import { syntaxTree } from "@codemirror/language";
+import type { EditorState } from "@codemirror/state";
-const completionSections = derived(
- KEYMAP_CATEGORIES,
- (categories) =>
- new Map(
- categories.map(
- (category) =>
- [
- category,
- {
- name: category.name,
- } satisfies CompletionSection,
- ] as const,
- ),
- ),
-);
+export function actionAutocompletePlugin(
+ query: (query: string | undefined) => void,
+) {
+ return ViewPlugin.fromClass(
+ class implements PluginValue {
+ constructor(readonly view: EditorView) {}
-export const actionAutocompleteItems = derived(
- [KEYMAP_CODES, completionSections],
- ([codes, sections]) =>
- codes
- .values()
- .map((info) => {
- const canUseId = canUseIdAsString(info);
- const completionValue =
- (canUseId && info.id) ||
- `0x${info.code.toString(16).padStart(2, "0")}`;
- return {
- label:
- [
- canUseId || !info.id ? undefined : `"${info.id}"`,
- info.title,
- info.variant?.replace(/^[a-z]/g, (c) => c.toUpperCase()),
- ]
- .filter(Boolean)
- .join(" ") || completionValue,
- detail: actionToValue(info),
- section: info.category ? sections.get(info.category) : undefined,
- info: info.description,
- type: "keyword",
- apply: completionValue + ">",
- } satisfies Completion;
- })
- .filter(
- (item) => typeof item.label === "string" && item.apply !== undefined,
- )
- .toArray(),
-);
+ update(update: ViewUpdate) {
+ query(this.resolveAutocomplete(update.state));
+ }
-export const actionAutocomplete = ((context) => {
- let word = context.tokenBefore([
- "ExplicitDelimStart",
- "ActionId",
- "HexNumber",
- "DecimalNumber",
- ]);
- if (!word) return null;
- console.log(get(actionAutocompleteItems));
- return {
- from: word.type.name === "ExplicitDelimStart" ? word.to : word.from,
- validFor: /^[a-zA-Z0-9_]*$/,
- options: get(actionAutocompleteItems),
- };
-}) satisfies CompletionSource;
+ resolveAutocomplete(state: EditorState): string | undefined {
+ if (state.selection.ranges.length !== 1) return;
+ const from = state.selection.ranges[0]!.from;
+ const to = state.selection.ranges[0]!.to;
+ if (from !== to) return;
+ const tree = syntaxTree(state);
+ const node = tree.resolveInner(from, -1).parent;
+ if (node?.name !== "ExplicitAction") return;
+ if (node.getChild("ExplicitDelimEnd")) return;
+ const queryNode = node.getChild("ExplicitDelimStart")?.nextSibling;
+ return (
+ (queryNode
+ ? state.doc.sliceString(queryNode.from, queryNode.to)
+ : undefined) || undefined
+ );
+ }
+ },
+ );
+}
diff --git a/src/lib/chord-editor/changes-panel.svelte.ts b/src/lib/chord-editor/changes-panel.svelte.ts
new file mode 100644
index 000000000..9c34ed252
--- /dev/null
+++ b/src/lib/chord-editor/changes-panel.svelte.ts
@@ -0,0 +1,44 @@
+import { EditorView, showPanel, type Panel } from "@codemirror/view";
+import { parsedChordsField } from "./parsed-chords-plugin";
+import { mount, unmount } from "svelte";
+import ChangesPanel from "./ChangesPanel.svelte";
+
+function changesPanelFunc(view: EditorView): Panel {
+ let dom = document.createElement("div");
+ dom.style.display = "contents";
+ let viewState = $state.raw(view);
+ let parsed = $state.raw(view.state.field(parsedChordsField));
+ let component: {};
+ return {
+ dom,
+ mount() {
+ component = mount(ChangesPanel, {
+ target: dom,
+ props: {
+ get parsed() {
+ return parsed;
+ },
+ get view() {
+ return viewState;
+ },
+ },
+ });
+ },
+ update: (update) => {
+ if (
+ update.startState.field(parsedChordsField) !==
+ update.state.field(parsedChordsField)
+ ) {
+ console.log("update changes panel");
+ parsed = update.state.field(parsedChordsField);
+ }
+ },
+ destroy() {
+ unmount(component);
+ },
+ };
+}
+
+export function changesPanel() {
+ return showPanel.of(changesPanelFunc);
+}
diff --git a/src/lib/chord-editor/changes-plugin.ts b/src/lib/chord-editor/changes-plugin.ts
deleted file mode 100644
index 7fd73597c..000000000
--- a/src/lib/chord-editor/changes-plugin.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import {
- EditorView,
- ViewPlugin,
- ViewUpdate,
- type PluginValue,
-} from "@codemirror/view";
-
-export const changesPlugin = ViewPlugin.fromClass(
- class implements PluginValue {
- constructor(readonly view: EditorView) {}
-
- update(update: ViewUpdate) {}
- },
- {
- eventHandlers: {},
- },
-);
diff --git a/src/lib/chord-editor/chord-delim-plugin.ts b/src/lib/chord-editor/chord-delim-plugin.ts
index 089d35b44..2a8444872 100644
--- a/src/lib/chord-editor/chord-delim-plugin.ts
+++ b/src/lib/chord-editor/chord-delim-plugin.ts
@@ -26,7 +26,7 @@ export class DelimWidget extends WidgetType {
toDOM() {
if (!this.element) {
- this.element = document.createElement("span");
+ /*this.element = document.createElement("span");
this.element.innerHTML =
" ⇛" + (this.hasConcatenator ? "" : " ");
this.element.style.scale = "1.8";
@@ -41,7 +41,9 @@ export class DelimWidget extends WidgetType {
props: { action: 574, display: "keys", inText: true, ghost: true },
});
this.element.appendChild(button);
- }
+ }*/
+ this.element = document.createElement("div");
+ this.element.style.breakAfter = "column";
}
return this.element;
}
diff --git a/src/lib/chord-editor/chord-sync-plugin.ts b/src/lib/chord-editor/chord-sync-plugin.ts
new file mode 100644
index 000000000..7406d79ef
--- /dev/null
+++ b/src/lib/chord-editor/chord-sync-plugin.ts
@@ -0,0 +1,44 @@
+import type { CharaChordFile } from "$lib/share/chara-file";
+import { StateEffect, StateField } from "@codemirror/state";
+import { actionMetaPlugin } from "./action-meta-plugin";
+import { syncCharaChords } from "./chord-sync";
+import type { EditorView } from "@codemirror/view";
+
+const chordSyncEffect = StateEffect.define();
+
+export function editorSyncChords(
+ view: EditorView,
+ newDeviceChords: CharaChordFile["chords"],
+) {
+ const { ids, codes } = view.state.field(actionMetaPlugin.field);
+ const oldDeviceChords = view.state.field(deviceChordField);
+ const changes = syncCharaChords(
+ oldDeviceChords,
+ newDeviceChords,
+ ids,
+ codes,
+ view.state.doc.toString(),
+ );
+ view.dispatch({
+ effects: chordSyncEffect.of(newDeviceChords),
+ changes,
+ });
+}
+
+export const deviceChordField = StateField.define({
+ create() {
+ return [];
+ },
+ update(value, transaction) {
+ return (
+ transaction.effects.findLast((it) => it.is(chordSyncEffect))?.value ??
+ value
+ );
+ },
+ toJSON(value) {
+ return value;
+ },
+ fromJSON(value) {
+ return value;
+ },
+});
diff --git a/src/lib/chord-editor/chord-sync.spec.ts b/src/lib/chord-editor/chord-sync.spec.ts
new file mode 100644
index 000000000..f739655d8
--- /dev/null
+++ b/src/lib/chord-editor/chord-sync.spec.ts
@@ -0,0 +1,135 @@
+import type { KeyInfo } from "$lib/serial/keymap-codes";
+import type { CharaChordFile } from "$lib/share/chara-file";
+import { describe, it, expect } from "vitest";
+import { parseCharaChords } from "./action-serializer";
+import { parser } from "./chords.grammar";
+import { syncCharaChords } from "./chord-sync";
+import { Text } from "@codemirror/state";
+
+const asciiInfo: KeyInfo[] = Array.from(
+ { length: 0x7f - 0x20 },
+ (_, i) =>
+ ({
+ code: i + 0x20,
+ id: String.fromCharCode(i + 0x20),
+ }) satisfies KeyInfo,
+);
+const asciiCodes = new Map(
+ asciiInfo.map((info) => [info.code, info]),
+);
+const asciiIds = new Map(
+ asciiInfo.map((info) => [info.id!, info]),
+);
+
+function chords(...strings: string[]): string {
+ return strings.join("\n");
+}
+
+function backup(doc: string): CharaChordFile["chords"] {
+ const tree = parser.parse(doc);
+ const result = parseCharaChords(tree, asciiIds, asciiCodes, [], (from, to) =>
+ doc.slice(from, to),
+ );
+ return result.chords
+ .filter((chord) => !chord.disabled)
+ .map((chord) => [chord.input?.value ?? [], chord.phrase?.value ?? []]);
+}
+
+function expectSync(options: {
+ org: string[];
+ mod: string[];
+ cur: string[];
+ exp: string[];
+}) {
+ expect(
+ syncCharaChords(
+ backup(chords(...options.org)),
+ backup(chords(...options.mod)),
+ asciiIds,
+ asciiCodes,
+ chords(...options.cur),
+ )
+ .apply(Text.of(options.cur))
+ .toString()
+ .replace(/\n$/, ""),
+ ).toEqual(chords(...options.exp));
+}
+
+describe("chord sync", function () {
+ it("should not do anything when no changes happened", function () {
+ expectSync({
+ org: ["abc=>def", "def=>ghi", "jkl=>mno"],
+ mod: ["abc=>def", "def=>ghi", "jkl=>mno"],
+ cur: ["abc=>def", "def=>ghi", "jkl=>mno"],
+ exp: ["abc=>def", "def=>ghi", "jkl=>mno"],
+ });
+ });
+
+ it("should not touch the doc if device chords are unchanged", function () {
+ expectSync({
+ org: ["abc=>def", "def=>ghi", "jkl=>mno"],
+ mod: ["abc=>def", "def=>ghi", "jkl=>mno"],
+ cur: ["ab=>def", "def=>gh"],
+ exp: ["ab=>def", "def=>gh"],
+ });
+ });
+
+ it("should apply removals to unchanged chords only", function () {
+ expectSync({
+ org: ["abc=>def", "def=>ghi", "jkl=>mno", "mno=>pqr"],
+ mod: ["abc=>def"],
+ cur: ["abc=>def", "def=>ghij", "jkl=>mno", "mno=>pqr"],
+ exp: ["abc=>def", "def=>ghij"],
+ });
+ });
+
+ it("should keep user modifications over device modifications", function () {
+ expectSync({
+ org: ["abc=>def", "def=>ghi", "jkl=>mno", "mno=>pqr"],
+ mod: ["abc=>def", "def=>ghijk", "jkl=>mnop", "mno=>pqr"],
+ cur: ["abc=>def", "def=>ghij", "jkl=>mno", "mno=>pqr"],
+ exp: ["abc=>def", "def=>ghij", "jkl=>mnop", "mno=>pqr"],
+ });
+ });
+
+ it("should handle complex changes", function () {
+ expectSync({
+ org: [
+ "unchanged=>unchanged",
+ "usermod=>usermod",
+ "devmod=>devmod",
+ "userremoval=>userremoval",
+ "devremoval=>devremoval",
+ "devremusermod=>devremusermod",
+ ],
+ mod: [
+ "unchanged=>unchanged",
+ "devadd=>devadd",
+ "usermod=>usermod",
+ "userremoval=>userremoval",
+ "devmod=>devmod1",
+ "sameadd=>sameadd",
+ ],
+ cur: [
+ "useradd1=>useradd1",
+ "unchanged=>unchanged",
+ "usermod=>use",
+ "devremusermod=>xyz",
+ "devmod=>devmod",
+ "sameadd=>sameadd",
+ "devremoval=>devremoval",
+ "useradd=>useradd",
+ ],
+ exp: [
+ "devadd=>devadd",
+ "useradd1=>useradd1",
+ "unchanged=>unchanged",
+ "usermod=>use",
+ "devremusermod=>xyz",
+ "devmod=>devmod1",
+ "sameadd=>sameadd",
+ "useradd=>useradd",
+ ],
+ });
+ });
+});
diff --git a/src/lib/chord-editor/chord-sync.ts b/src/lib/chord-editor/chord-sync.ts
new file mode 100644
index 000000000..8446dfa8a
--- /dev/null
+++ b/src/lib/chord-editor/chord-sync.ts
@@ -0,0 +1,130 @@
+import type { KeyInfo } from "$lib/serial/keymap-codes";
+import { ChangeSet, type ChangeSpec } from "@codemirror/state";
+import { parseCharaChords } from "./action-serializer";
+import { parser } from "./chords.grammar";
+import type { CharaChordFile } from "$lib/share/chara-file";
+import { splitCompound } from "$lib/serial/chord";
+
+function canUseIdAsString(info: KeyInfo): boolean {
+ return !!info.id && /^[^>\n]+$/.test(info.id);
+}
+
+export function actionToValue(code: number, info?: KeyInfo) {
+ if (info && info.id?.length === 1)
+ return /^[<>|\\\s]$/.test(info.id) ? `\\${info.id}` : info.id;
+ if (!info || !canUseIdAsString(info))
+ return `<0x${code.toString(16).padStart(2, "0")}>`;
+ return `<${info.id}>`;
+}
+
+function canonicalInputSorting(input: number[], phrase: number[]): number[] {
+ const tail = [...input];
+ const prefix = phrase.filter((code) => {
+ const index = tail.indexOf(code);
+ if (index !== -1) {
+ tail.splice(index, 1);
+ return true;
+ }
+ return false;
+ });
+ return [...prefix, ...tail];
+}
+
+export interface ChangeType {
+ from: number;
+ to: number;
+ insert: string;
+}
+
+export function syncCharaChords(
+ originalDeviceChords: CharaChordFile["chords"],
+ newDeviceChords: CharaChordFile["chords"],
+ ids: Map,
+ codes: Map,
+ doc: string,
+): ChangeSet {
+ const tree = parser.parse(doc);
+ const result = parseCharaChords(
+ tree,
+ ids,
+ codes,
+ originalDeviceChords,
+ (from, to) => doc.slice(from, to),
+ );
+
+ const exactChords = new Map();
+ for (const chord of originalDeviceChords) {
+ const key = JSON.stringify(chord);
+ const count = exactChords.get(key) ?? 0;
+ exactChords.set(key, count + 1);
+ }
+
+ const changes: ChangeType[] = [];
+
+ const inputModified = new Set();
+ for (const chord of newDeviceChords) {
+ const key = JSON.stringify(chord);
+ const count = exactChords.get(key) ?? 0;
+ if (count > 0) {
+ exactChords.set(key, count - 1);
+ continue;
+ }
+
+ const inputKey = JSON.stringify(chord[0]);
+ inputModified.add(inputKey);
+ const byInput = result.inputs.get(inputKey);
+ if (byInput) {
+ if (
+ byInput.phrase?.originalValue &&
+ byInput.phrase.originalValue === byInput.phrase.value
+ ) {
+ changes.push({
+ from: byInput.phrase.range[0],
+ to: byInput.phrase.range[1],
+ insert: chord[1]
+ .map((code) => actionToValue(code, codes.get(code)))
+ .join(""),
+ });
+ }
+ } else {
+ const [inputs, compound] = splitCompound(chord[0]);
+ const sortedInput = canonicalInputSorting(inputs, chord[1]);
+ changes.push({
+ from: 0,
+ to: 0,
+ insert:
+ (compound ? `|0x${compound.toString(16)}|` : "") +
+ sortedInput
+ .map((code) => actionToValue(code, codes.get(code)))
+ .join("") +
+ "=>" +
+ chord[1]
+ .map((code) => actionToValue(code, codes.get(code)))
+ .join("") +
+ "\n",
+ });
+ }
+ }
+
+ changes.push(
+ ...exactChords
+ .entries()
+ .filter(([, count]) => count > 0)
+ .map(([key]) => result.exact.get(key))
+ .filter((chord) => chord !== undefined)
+ .filter(
+ (chord) =>
+ chord.input && !inputModified.has(JSON.stringify(chord.input.value)),
+ )
+ .map(
+ (chord) =>
+ ({
+ from: chord.range[0],
+ to: chord.range[1],
+ insert: "",
+ }) satisfies ChangeSpec,
+ ),
+ );
+
+ return ChangeSet.of(changes, doc.length);
+}
diff --git a/src/lib/chord-editor/chords-grammar-plugin.ts b/src/lib/chord-editor/chords-grammar-plugin.ts
index 6d524be59..071be7f13 100644
--- a/src/lib/chord-editor/chords-grammar-plugin.ts
+++ b/src/lib/chord-editor/chords-grammar-plugin.ts
@@ -5,7 +5,6 @@ import {
HighlightStyle,
} from "@codemirror/language";
import { styleTags, tags } from "@lezer/highlight";
-import { actionAutocomplete } from "./autocomplete";
export const chordHighlightStyle = HighlightStyle.define([
{
@@ -51,7 +50,5 @@ export const chordLanguage = LRLanguage.define({
});
export function chordLanguageSupport() {
- return new LanguageSupport(chordLanguage, [
- chordLanguage.data.of({ autocomplete: actionAutocomplete }),
- ]);
+ return new LanguageSupport(chordLanguage, [chordLanguage.data.of({})]);
}
diff --git a/src/lib/chord-editor/chords.grammar b/src/lib/chord-editor/chords.grammar
index c63d99525..c0283079d 100644
--- a/src/lib/chord-editor/chords.grammar
+++ b/src/lib/chord-editor/chords.grammar
@@ -1,27 +1,43 @@
@top Program { Chord* }
-ExplicitAction { ExplicitDelimStart (HexNumber | DecimalNumber | ActionId) ExplicitDelimEnd }
+ExplicitAction { ExplicitDelimStart (HexNumber | ActionId) ExplicitDelimEnd }
EscapedSingleAction { Escape EscapedLetter }
Action { SingleLetter | ExplicitAction | EscapedSingleAction }
-ActionString { Action* }
-ChordInput { (ActionString CompoundDelim)* ActionString }
+
+ActionString { Action+ }
+
+CompoundLiteral { CompoundDelim HexNumber CompoundDelim }
+CompoundInput { ActionString CompoundDelim }
+
+ChordInput { CompoundLiteral? CompoundInput* ActionString }
ChordPhrase { ActionString }
+
Chord { ChordInput PhraseDelim ChordPhrase ChordDelim }
+@skip {
+ Space
+}
+
@tokens {
- @precedence {HexNumber, DecimalNumber}
- @precedence {CompoundDelim, PhraseDelim, ExplicitDelimStart, ChordDelim, SingleLetter}
- @precedence {EscapedLetter}
+ @precedence { HexNumber, ActionId }
+ @precedence { Space, Escape }
+ @precedence { Space, SingleLetter }
+ @precedence { Escape, SingleLetter }
+ @precedence { CompoundDelim, SingleLetter }
+ @precedence { ActionId, Space }
+ @precedence { EscapedLetter, Space }
+
+ Space {" "}
ExplicitDelimStart {"<"}
ExplicitDelimEnd {">"}
- CompoundDelim {"+>"}
+ CompoundDelim {"|"}
PhraseDelim {"=>"}
Escape { "\\" }
HexNumber { "0x" $[a-fA-F0-9]+ }
- DecimalNumber { $[0-9]+ }
- ActionId { $[a-zA-Z_]$[a-zA-Z0-9_]* }
- SingleLetter { ![\\] }
- EscapedLetter { ![] }
- ChordDelim { ($[\n] | @eof) }
+ ActionId { ![\n>]+ }
+ SingleLetter { ![\n<] }
+ EscapedLetter { ![\n] }
+ ChordDelim { ("\n" | @eof) }
}
+@detectDelim
diff --git a/src/lib/chord-editor/parse-meta.ts b/src/lib/chord-editor/parse-meta.ts
new file mode 100644
index 000000000..3f503113f
--- /dev/null
+++ b/src/lib/chord-editor/parse-meta.ts
@@ -0,0 +1,176 @@
+import type { KeyInfo } from "$lib/serial/keymap-codes";
+import type { CharaChordFile } from "$lib/share/chara-file";
+import type { ChangeDesc } from "@codemirror/state";
+
+export type MetaRange = [from: number, to: number];
+
+function mapMetaRange(range: MetaRange, change: ChangeDesc): MetaRange {
+ const newFrom = change.mapPos(range[0]);
+ const newTo = change.mapPos(range[1]);
+ if (newFrom === range[0] && newTo === range[1]) {
+ return range;
+ }
+ return [newFrom, newTo];
+}
+
+export interface ActionMeta {
+ code: number;
+ info?: KeyInfo;
+ explicit?: boolean;
+ range: MetaRange;
+ valid: boolean;
+}
+
+function mapActionMeta(action: ActionMeta, change: ChangeDesc): ActionMeta {
+ const newRange = mapMetaRange(action.range, change);
+ if (newRange === action.range) {
+ return action;
+ }
+ return {
+ ...action,
+ range: newRange,
+ };
+}
+
+function mapArray(
+ array: T[],
+ change: ChangeDesc,
+ mapFn: (action: T, change: ChangeDesc) => T,
+): T[] {
+ let changed = false;
+ const newArray = array.map((value) => {
+ const newValue = mapFn(value, change);
+ if (newValue !== value) {
+ changed = true;
+ return newValue;
+ }
+ return value;
+ });
+ if (changed) {
+ return newArray;
+ }
+ return array;
+}
+
+export interface ActionStringMeta {
+ range: MetaRange;
+ value: T;
+ valid: boolean;
+ actions: ActionMeta[];
+}
+
+function mapActionStringMeta>(
+ actionString: T,
+ change: ChangeDesc,
+) {
+ const newRange = mapMetaRange(actionString.range, change);
+ const newActions = mapArray(actionString.actions, change, mapActionMeta);
+ if (newRange === actionString.range && newActions === actionString.actions) {
+ return actionString;
+ }
+ return {
+ ...actionString,
+ range: newRange,
+ actions: newActions,
+ };
+}
+
+export interface PhraseMeta extends ActionStringMeta {
+ hasConcatenator: boolean;
+ originalValue?: number[];
+}
+
+export interface CompoundMeta extends ActionStringMeta {
+ parent?: ChordMeta;
+}
+
+export interface InputMeta extends ActionStringMeta {}
+
+export interface ChordMeta {
+ range: MetaRange;
+ valid: boolean;
+ disabled?: boolean;
+ compounds?: CompoundMeta[];
+ input?: InputMeta;
+ phrase?: PhraseMeta;
+ children?: ChordMeta[];
+ overrides?: ChordMeta[];
+ aliases?: ChordMeta[];
+ overriddenBy?: ChordMeta;
+}
+
+export function mapChordMeta(chord: ChordMeta, change: ChangeDesc): ChordMeta {
+ const newRange = mapMetaRange(chord.range, change);
+ const newCompounds = chord.compounds
+ ? mapArray(chord.compounds, change, mapActionStringMeta)
+ : undefined;
+ const newInput = chord.input
+ ? mapActionStringMeta(chord.input, change)
+ : undefined;
+ const newPhrase = chord.phrase
+ ? mapActionStringMeta(chord.phrase, change)
+ : undefined;
+ if (
+ newRange === chord.range &&
+ newCompounds === chord.compounds &&
+ newInput === chord.input &&
+ newPhrase === chord.phrase
+ ) {
+ return chord;
+ }
+
+ const newChord: ChordMeta = {
+ ...chord,
+ range: newRange,
+ };
+ if (newCompounds) newChord.compounds = newCompounds;
+ if (newInput) newChord.input = newInput;
+ if (newPhrase) newChord.phrase = newPhrase;
+ return newChord;
+}
+
+export interface ParseResult {
+ chords: ChordMeta[];
+ removed: CharaChordFile["chords"];
+ aliases: Map;
+ compounds: Map;
+ inputs: Map;
+ exact: Map;
+}
+
+export function mapParseResult(
+ result: ParseResult,
+ change: ChangeDesc,
+): ParseResult {
+ const newChords = mapArray(result.chords, change, mapChordMeta);
+ if (newChords === result.chords) {
+ return result;
+ }
+ return {
+ ...result,
+ chords: newChords,
+ };
+}
+
+export function iterActions(
+ chord: ChordMeta,
+ callback: (action: ActionMeta) => void,
+) {
+ if (chord.input) {
+ for (const action of chord.input.actions) {
+ callback(action);
+ }
+ }
+ if (chord.compounds) {
+ for (const compound of chord.compounds) {
+ for (const action of compound.actions) {
+ callback(action);
+ }
+ }
+ }
+ if (chord.phrase) {
+ for (const action of chord.phrase.actions) {
+ callback(action);
+ }
+ }
+}
diff --git a/src/lib/chord-editor/parsed-chords-plugin.ts b/src/lib/chord-editor/parsed-chords-plugin.ts
new file mode 100644
index 000000000..9e9004439
--- /dev/null
+++ b/src/lib/chord-editor/parsed-chords-plugin.ts
@@ -0,0 +1,40 @@
+import { StateField } from "@codemirror/state";
+import { parseCharaChords } from "./action-serializer";
+import { actionMetaPlugin } from "./action-meta-plugin";
+import { syntaxTree } from "@codemirror/language";
+import { deviceChordField } from "./chord-sync-plugin";
+import { mapParseResult, type ParseResult } from "./parse-meta";
+
+export const parsedChordsField = StateField.define({
+ create() {
+ return {
+ chords: [],
+ removed: [],
+ aliases: new Map(),
+ compounds: new Map(),
+ inputs: new Map(),
+ exact: new Map(),
+ };
+ },
+ update(value, transaction) {
+ const tree = syntaxTree(transaction.state);
+ const ids = transaction.state.field(actionMetaPlugin.field).ids;
+ const codes = transaction.state.field(actionMetaPlugin.field).codes;
+ const deviceChords = transaction.state.field(deviceChordField);
+ if (
+ tree !== syntaxTree(transaction.startState) ||
+ ids !== transaction.startState.field(actionMetaPlugin.field).ids ||
+ codes !== transaction.startState.field(actionMetaPlugin.field).codes ||
+ deviceChords !== transaction.startState.field(deviceChordField)
+ ) {
+ return parseCharaChords(
+ syntaxTree(transaction.state),
+ ids,
+ codes,
+ deviceChords,
+ (from, to) => transaction.state.doc.sliceString(from, to),
+ );
+ }
+ return mapParseResult(value, transaction.changes);
+ },
+});
diff --git a/src/lib/chord-editor/persistent-state-plugin.ts b/src/lib/chord-editor/persistent-state-plugin.ts
new file mode 100644
index 000000000..1af0660ab
--- /dev/null
+++ b/src/lib/chord-editor/persistent-state-plugin.ts
@@ -0,0 +1,185 @@
+import {
+ EditorView,
+ highlightActiveLine,
+ keymap,
+ lineNumbers,
+ ViewPlugin,
+ ViewUpdate,
+} from "@codemirror/view";
+import {
+ history,
+ historyField,
+ historyKeymap,
+ standardKeymap,
+} from "@codemirror/commands";
+import { debounceTime, mergeMap, Subject } from "rxjs";
+import { EditorState, type EditorStateConfig } from "@codemirror/state";
+import { lintGutter } from "@codemirror/lint";
+import {
+ chordHighlightStyle,
+ chordLanguageSupport,
+} from "./chords-grammar-plugin";
+import { actionLinter } from "./action-linter";
+import { actionAutocompletePlugin } from "./autocomplete";
+import { delimPlugin } from "./chord-delim-plugin";
+import { actionPlugin } from "./action-plugin";
+import { syntaxHighlighting } from "@codemirror/language";
+import { deviceChordField } from "./chord-sync-plugin";
+import { actionMetaPlugin } from "./action-meta-plugin";
+import { parsedChordsField } from "./parsed-chords-plugin";
+import { changesPanel } from "./changes-panel.svelte";
+import { searchKeymap } from "@codemirror/search";
+
+const serializedFields = {
+ history: historyField,
+ deviceChords: deviceChordField,
+};
+
+export interface EditorConfig {
+ rawCode?: boolean;
+ storeName: string;
+ autocomplete(query: string | undefined): void;
+}
+
+export function createConfig(params: EditorConfig) {
+ return {
+ extensions: [
+ actionMetaPlugin.plugin,
+ deviceChordField,
+ parsedChordsField,
+ changesPanel(),
+ lintGutter(),
+ params.rawCode ? [lineNumbers()] : [delimPlugin, actionPlugin],
+ chordLanguageSupport(),
+ actionLinter({
+ delay: 100,
+ markerFilter(diagnostics) {
+ return diagnostics.filter((it) => it.from !== it.to);
+ },
+ }),
+ actionAutocompletePlugin(params.autocomplete),
+ persistentStatePlugin(params.storeName),
+ history(),
+ syntaxHighlighting(chordHighlightStyle),
+ highlightActiveLine(),
+ EditorView.theme({
+ ".cm-line": {
+ borderBottom: "1px solid transparent",
+ caretColor: "var(--md-sys-color-on-surface)",
+ },
+ ".cm-scroller": {
+ overflow: "auto",
+ width: "100%",
+ fontFamily: "inherit !important",
+ gap: "8px",
+ },
+ ".cm-content": {
+ flex: 1,
+ },
+ ".cm-cursor": {
+ borderColor: "var(--md-sys-color-on-surface)",
+ },
+ }),
+ keymap.of([...standardKeymap, ...historyKeymap, ...searchKeymap]),
+ ],
+ } satisfies EditorStateConfig;
+}
+
+export async function loadPersistentState(
+ params: EditorConfig,
+): Promise {
+ const stored = await getState(params.storeName);
+ const config = createConfig(params);
+
+ if (stored) {
+ try {
+ return EditorState.fromJSON(stored, config, serializedFields);
+ } catch (e) {
+ console.error("Failed to parse persistent state:", e);
+ }
+ }
+ return EditorState.create(config);
+}
+
+export function persistentStatePlugin(storeName: string) {
+ return ViewPlugin.fromClass(
+ class {
+ updateSubject = new Subject();
+ subscription = this.updateSubject
+ .pipe(
+ debounceTime(500),
+ mergeMap(() =>
+ storeState(storeName, this.view.state.toJSON(serializedFields)),
+ ),
+ )
+ .subscribe(() => {});
+
+ constructor(readonly view: EditorView) {}
+
+ update(update: ViewUpdate) {
+ if (update.state !== update.startState) {
+ this.updateSubject.next();
+ }
+ }
+
+ destroy() {
+ this.subscription.unsubscribe();
+ }
+ },
+ );
+}
+
+const dbName = "chord-state";
+const dbVersion = 1;
+const storeName = "state";
+
+async function openDb(): Promise {
+ const dbRequest = indexedDB.open(dbName, dbVersion);
+ return new Promise((resolve, reject) => {
+ dbRequest.onsuccess = () => resolve(dbRequest.result);
+ dbRequest.onerror = () => reject(dbRequest.error);
+ dbRequest.onupgradeneeded = () => {
+ const db = dbRequest.result;
+ if (!db.objectStoreNames.contains(storeName)) {
+ db.createObjectStore(storeName);
+ }
+ };
+ });
+}
+
+async function getState(name: string): Promise {
+ const db = await openDb();
+ try {
+ const readTransaction = db.transaction([storeName], "readonly");
+ const store = readTransaction.objectStore(storeName);
+ const itemRequest = store.get(name);
+ const result = await new Promise((resolve) => {
+ itemRequest.onsuccess = () => resolve(itemRequest.result);
+ itemRequest.onerror = () => resolve(undefined);
+ });
+ return result;
+ } catch (e) {
+ console.error(e);
+ return undefined;
+ } finally {
+ db.close();
+ }
+}
+
+async function storeState(name: string, state: T): Promise {
+ const db = await openDb();
+ try {
+ const putTransaction = db.transaction([storeName], "readwrite");
+ const putStore = putTransaction.objectStore(storeName);
+ const putRequest = putStore.put(state, name);
+ await new Promise((resolve, reject) => {
+ putRequest.onsuccess = () => resolve();
+ putRequest.onerror = () => reject(putRequest.error);
+ });
+ putTransaction.commit();
+ } catch (e) {
+ console.error(e);
+ } finally {
+ db.close();
+ }
+}
diff --git a/src/lib/chord-editor/save-chords.ts b/src/lib/chord-editor/save-chords.ts
new file mode 100644
index 000000000..efc82a564
--- /dev/null
+++ b/src/lib/chord-editor/save-chords.ts
@@ -0,0 +1,58 @@
+import type { EditorView } from "@codemirror/view";
+import { parser } from "./chords.grammar";
+import { parseCharaChords } from "./action-serializer";
+import { actionMetaPlugin } from "./action-meta-plugin";
+import { deviceChordField } from "./chord-sync-plugin";
+import type { CharaChordFile } from "$lib/share/chara-file";
+
+export interface SaveChordsTask {
+ remove: number[][];
+ set: [number[], number[]][];
+}
+
+export function createSaveTask(view: EditorView): SaveChordsTask {
+ const tree = parser.parse(view.state.doc.toString());
+ const { ids, codes } = view.state.field(actionMetaPlugin.field);
+ const deviceChords = view.state.field(deviceChordField);
+ const result = parseCharaChords(tree, ids, codes, deviceChords, (from, to) =>
+ view.state.doc.sliceString(from, to),
+ );
+
+ return {
+ remove: result.removed.map((chord) => chord[0]),
+ set: result.chords
+ .filter(
+ (chord) =>
+ !chord.disabled &&
+ (!chord.phrase ||
+ chord.phrase?.originalValue !== chord.phrase?.value),
+ )
+ .map((chord) => [chord.input?.value ?? [], chord.phrase?.value ?? []]),
+ };
+}
+
+export function applySaveTask(
+ backup: CharaChordFile["chords"],
+ task: SaveChordsTask,
+): CharaChordFile["chords"] {
+ const newBackup = [...backup];
+ for (const input of task.remove) {
+ const index = newBackup.findIndex((chord) => {
+ return JSON.stringify(chord[0]) === JSON.stringify(input);
+ });
+ if (index !== -1) {
+ newBackup.splice(index, 1);
+ }
+ }
+ for (const [input, phrase] of task.set) {
+ const index = newBackup.findIndex((chord) => {
+ return JSON.stringify(chord[0]) === JSON.stringify(input);
+ });
+ if (index !== -1) {
+ newBackup[index] = [input, phrase];
+ } else {
+ newBackup.push([input, phrase]);
+ }
+ }
+ return newBackup;
+}
diff --git a/src/lib/chord-editor/store-state-field.ts b/src/lib/chord-editor/store-state-field.ts
new file mode 100644
index 000000000..42ff80983
--- /dev/null
+++ b/src/lib/chord-editor/store-state-field.ts
@@ -0,0 +1,35 @@
+import { StateEffect, StateField } from "@codemirror/state";
+import { EditorView, ViewPlugin } from "@codemirror/view";
+import { get, type Readable } from "svelte/store";
+
+export function reactiveStateField(store: Readable) {
+ const effect = StateEffect.define();
+ const field = StateField.define({
+ create() {
+ return get(store);
+ },
+ update(value, transaction) {
+ return (
+ transaction.effects.findLast((it) => it.is(effect))?.value ?? value
+ );
+ },
+ });
+ const plugin = ViewPlugin.fromClass(
+ class {
+ unsubscribe: () => void;
+
+ constructor(readonly view: EditorView) {
+ this.unsubscribe = store.subscribe((value) => {
+ setTimeout(() => {
+ view.dispatch({ effects: effect.of(value) });
+ });
+ });
+ }
+
+ destroy() {
+ this.unsubscribe();
+ }
+ },
+ );
+ return { field, plugin: [field, plugin] };
+}
diff --git a/src/lib/chord-editor/test.txt b/src/lib/chord-editor/test.txt
index dd377111e..3bafe340a 100644
--- a/src/lib/chord-editor/test.txt
+++ b/src/lib/chord-editor/test.txt
@@ -1,16 +1,16 @@
-.= => =>
-;ims => <0x219>
--; => <0x23e>_<0x23e>
-.;g => <0x23e>...<0x23e>
-'dg => <0x23e>'<0x23e>
-'gl => <0x23e>'ll<0x23e>
-'ar => <0x23e>'re<0x23e>
-'gs => <0x23e>'s<0x23e>
-'ev => <0x23e>'ve<0x23e>
--; => <0x23e><0x223>-<0x223>
-; => <0x23e><0x223><0x23d><0x223>
-;g => <0x23e><0x223><0x223>
-deg => <0x23e>ed<0x23e>
-;gr => <0x23e>er<0x23e>
-;es => <0x23e>es<0x23e>
-;est => <0x23e>est<0x23e>
+a|.==>t=t
+;ims=<0x219>
+-;=><0x23e>_<0x23e>
+.;g=><0x23e>...<0x23e>
+'dg=><0x23e>'<0x23e>
+'gl=><0x23e>'ll<0x23e>
+'ar=><0x23e>'re<0x23e>
+'gs=><0x23e>'s<0x23e>
+'ev=><0x23e>'ve<0x23e>
+-;=><0x23e><0x223>-<0x223>
+;=><0x23e><0x223><0x23d><0x223>
+;g=><0x23e><0x223><0x223>
+deg=><0x23e>ed<0x23e>
+;gr=><0x23e>er<0x23e>
+;es=><0x23e>es<0x23e>
+;est=><0x23e>est<0x23e>
diff --git a/src/lib/components/Action.svelte b/src/lib/components/Action.svelte
index 3fa99fe39..82abc83dc 100644
--- a/src/lib/components/Action.svelte
+++ b/src/lib/components/Action.svelte
@@ -8,10 +8,12 @@
let {
action,
display,
+ ignoreIcon = false,
inText = false,
}: {
action: string | number | KeyInfo;
display: "inline-keys" | "keys" | "verbose";
+ ignoreIcon?: boolean;
inText?: boolean;
} = $props();
@@ -30,6 +32,7 @@
? ({ code: 1024, id: action } satisfies KeyInfo)
: action),
);
+ let icon = $derived(ignoreIcon ? undefined : info.icon);
let dynamicMapping = $derived(info.keyCode && $osLayout.get(info.keyCode));
let hasPopover = $derived(
!retrievedInfo || !info.id || info.title || info.description,
@@ -63,7 +66,7 @@
{#snippet kbdText()}
{dynamicMapping ??
- info.icon ??
+ icon ??
info.display ??
info.id ??
`0x${info.code.toString(16)}`}
@@ -71,7 +74,7 @@
{#snippet kbdSnippet(withPopover = true)}
1023}
@@ -91,7 +94,7 @@
class:left={info.variant === "left"}
class:right={info.variant === "right"}>{dynamicMapping}
- {:else if !info.icon && info.id?.length === 1}
+ {:else if !icon && info.id?.length === 1}
1023}
{@attach hasPopover ? actionTooltip(popover) : null}
@@ -155,21 +158,50 @@
text-decoration: line-through;
}
- $variant-offset: 12px;
- $variant-padding: calc(2px + $variant-offset);
$variant-color: color-mix(
in srgb,
var(--md-sys-color-on-surface) 50%,
transparent
);
+ .left,
+ .right {
+ background-color: transparent;
+
+ &::before {
+ position: absolute;
+ inset: 0;
+ outline: 2px dashed
+ color-mix(in srgb, var(--bg-color), var(--md-sys-color-outline) 40%);
+ outline-offset: -2px;
+ border-radius: var(--border-radius);
+ content: "";
+ }
+ }
+
+ $cutoff: 60%;
+
.left {
- padding-inline-end: $variant-padding;
- text-shadow: $variant-offset 0 2px $variant-color;
+ background-image: linear-gradient(
+ to right,
+ var(--bg-color) $cutoff,
+ transparent $cutoff
+ );
+
+ &::before {
+ clip-path: inset(0 0 0 $cutoff);
+ }
}
.right {
- padding-inline-start: $variant-padding;
- text-shadow: -$variant-offset 0 2px $variant-color;
+ background-image: linear-gradient(
+ to left,
+ var(--bg-color) $cutoff,
+ transparent $cutoff
+ );
+
+ &::before {
+ clip-path: inset(0 $cutoff 0 0);
+ }
}
.inline-kbd {
diff --git a/src/lib/components/layout/ActionList.svelte b/src/lib/components/layout/ActionList.svelte
index 15d116afd..03905e5c5 100644
--- a/src/lib/components/layout/ActionList.svelte
+++ b/src/lib/components/layout/ActionList.svelte
@@ -14,18 +14,22 @@
import type { KeymapCategory } from "$lib/meta/types/actions";
import Action from "../Action.svelte";
import { isVerbose } from "../verbose-action";
- import { actionToValue } from "$lib/chord-editor/action-serializer";
+ import { actionToValue } from "$lib/chord-editor/chord-sync";
let {
currentAction = undefined,
nextAction = undefined,
+ queryFilter = undefined,
+ ignoreIcon,
autofocus = false,
onselect,
onclose,
}: {
currentAction?: number;
+ queryFilter?: string;
nextAction?: number;
autofocus?: boolean;
+ ignoreIcon?: boolean;
onselect?: (id: number) => void;
onclose?: () => void;
} = $props();
@@ -43,6 +47,14 @@
createIndex($KEYMAP_CODES);
});
+ let didClear = true;
+ $effect(() => {
+ if (queryFilter !== undefined || !didClear) {
+ searchBox.value = queryFilter ?? "";
+ search();
+ }
+ });
+
async function createIndex(codes: Map) {
for (const [, action] of codes) {
await index?.addAsync(
@@ -60,6 +72,7 @@
(category) => [category, []] as [KeymapCategory, KeyInfo[]],
),
);
+ didClear = searchBox.value === "";
const result =
searchBox.value === ""
? Array.from($KEYMAP_CODES.keys())
@@ -167,7 +180,7 @@
Action code is out of range
{/if}
{/if}
- {#each results as [category, actions] (category)}
+ {#each results as [category, actions] (actions)}
{#if actions.length > 0}
{category.name}
@@ -191,7 +204,7 @@
}
: undefined}
>
-
+
{/each}
diff --git a/src/lib/learn/chords.ts b/src/lib/learn/chords.ts
deleted file mode 100644
index d191eb924..000000000
--- a/src/lib/learn/chords.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-import { osLayout } from "$lib/os-layout";
-import { KEYMAP_CODES } from "$lib/serial/keymap-codes";
-import { persistentWritable } from "$lib/storage";
-import { type ChordInfo, chords } from "$lib/undo-redo";
-import { derived } from "svelte/store";
-
-export const words = derived(
- [chords, osLayout, KEYMAP_CODES],
- ([chords, layout, KEYMAP_CODES]) =>
- new Map
(
- chords
- .map((chord) => ({
- chord,
- output: chord.phrase.map((action) =>
- layout.get(KEYMAP_CODES.get(action)?.keyCode ?? ""),
- ),
- }))
- .filter(({ output }) => output.every((it) => !!it))
- .map(({ chord, output }) => [output.join("").trim(), chord] as const),
- ),
-);
-
-interface Score {
- lastTyped: number;
- score: number;
- total: number;
-}
-
-export const scores = persistentWritable>("scores", {});
-
-export const learnConfigDefault = {
- maxScore: 3,
- minScore: -3,
- scoreBlend: 0.5,
- weakRate: 0.8,
- weakBoost: 0.5,
- maxWeak: 3,
- newRate: 0.3,
- initialNewRate: 0.9,
- initialCount: 10,
-};
-export const learnConfigStored = persistentWritable<
- Partial
->("learn-config", {});
-export const learnConfig = derived(learnConfigStored, (config) => ({
- ...learnConfigDefault,
- ...config,
-}));
-
-let lastWord: string | undefined;
-
-function shuffle(array: T[]): T[] {
- for (let i = array.length - 1; i > 0; i--) {
- const j = Math.floor(Math.random() * (i + 1));
- [array[i], array[j]] = [array[j]!, array[i]!];
- }
- return array;
-}
-
-function randomLog2(array: T[], max = array.length): T | undefined {
- return array[
- Math.floor(Math.pow(2, Math.log2(Math.random() * Math.log2(max))))
- ];
-}
-
-export const nextWord = derived(
- [words, scores, learnConfig],
- ([words, scores, config]) => {
- const values = Object.entries(scores).filter(([it]) => it !== lastWord);
-
- values.sort(([, a], [, b]) => a.score - b.score);
- const weakCount =
- (values.findIndex(([, { score }]) => score > 0) + 1 ||
- values.length + 1) - 1;
- const weak = randomLog2(values, weakCount);
- if (weak && Math.random() / weakCount < config.weakRate) {
- lastWord = weak[0];
- return weak[0];
- }
-
- values.sort(([, { lastTyped: a }], [, { lastTyped: b }]) => a - b);
- const recent = randomLog2(values);
- const newRate =
- values.length < config.initialCount
- ? config.initialNewRate
- : config.newRate;
- if (
- recent &&
- (Math.random() < Math.min(1, Math.max(0, weakCount / config.maxWeak)) ||
- Math.random() > newRate)
- ) {
- lastWord = recent[0];
- return recent[0];
- }
-
- const newWord = shuffle(Array.from(words.keys())).find((it) => !scores[it]);
- const word = newWord || recent?.[0] || weak?.[0];
- lastWord = word;
- return word;
- },
-);
diff --git a/src/lib/learn/stats.ts b/src/lib/learn/stats.ts
deleted file mode 100644
index 05df68a2c..000000000
--- a/src/lib/learn/stats.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { persistentWritable } from "$lib/storage";
-
-interface ChordStats {
- level: number;
- lastUprank: number;
-}
-
-export const chordStats = persistentWritable>(
- "chord-stats",
- {},
-);
diff --git a/src/lib/serial/chord.ts b/src/lib/serial/chord.ts
index 532e2ca35..8b18030ff 100644
--- a/src/lib/serial/chord.ts
+++ b/src/lib/serial/chord.ts
@@ -1,4 +1,5 @@
import { compressActions, decompressActions } from "../serialization/actions";
+import type { KeyInfo } from "./keymap-codes";
export interface Chord {
actions: number[];
@@ -56,6 +57,103 @@ export function deserializeActions(native: bigint): number[] {
return actions;
}
+const compoundHashItems = 3;
+const maxChordInputItems = 12;
+const actionBits = 10;
+const actionMask = (1 << actionBits) - 1;
+
+/**
+ * Applies the compound value to a **valid** chord input
+ */
+export function applyCompound(actions: number[], compound: number): number[] {
+ const result = [...actions];
+ for (let i = 0; i < compoundHashItems; i++) {
+ result[i] = (compound >>> (i * actionBits)) & actionMask;
+ }
+ result[compoundHashItems] = 0;
+ return result;
+}
+
+/**
+ * Extracts the compound value from a chord input, if present
+ */
+export function splitCompound(
+ actions: number[],
+): [inputs: number[], compound: number | undefined] {
+ if (actions[compoundHashItems] != 0) {
+ return [
+ actions.slice(
+ Math.max(
+ 0,
+ actions.findIndex((it) => it !== 0),
+ ),
+ ),
+ undefined,
+ ];
+ }
+
+ let compound = 0;
+ for (let i = 0; i < compoundHashItems; i++) {
+ compound |= (actions[i] ?? 0) << (i * actionBits);
+ }
+
+ return [
+ actions.slice(
+ actions.findIndex((it, i) => i > compoundHashItems && it !== 0),
+ ),
+ compound === 0 ? undefined : compound,
+ ];
+}
+
+export function willBeValidChordInput(
+ inputCount: number,
+ hasCompound: boolean,
+): boolean {
+ return (
+ inputCount > 0 &&
+ inputCount <= maxChordInputItems - (hasCompound ? compoundHashItems + 1 : 0)
+ );
+}
+
+const ACTION_JOIN = 574;
+const ACTION_KSC_00 = 256;
+
+export function hasConcatenator(
+ actions: number[],
+ ids: Map,
+): boolean {
+ const lastAction = actions.at(-1);
+ for (const action of actions) {
+ if (!ids.get(action)?.printable) {
+ if (actions.length == 0) {
+ return false;
+ }
+ return lastAction == ACTION_JOIN;
+ }
+ }
+ return lastAction != ACTION_KSC_00;
+}
+
+/**
+ * Composes a chord input from a list of actions and an optional compound value
+ * to a valid chord input
+ */
+export function composeChordInput(
+ actions: number[],
+ compound?: number,
+): number[] {
+ const result = [
+ ...Array.from(
+ {
+ length: Math.max(0, maxChordInputItems - actions.length),
+ },
+ () => 0,
+ ),
+ ...actions.slice(0, maxChordInputItems).sort((a, b) => a - b),
+ ];
+ return compound !== undefined ? applyCompound(result, compound) : result;
+}
+
/**
* Hashes a chord input the same way as CCOS
*/
@@ -72,5 +170,6 @@ export function hashChord(actions: number[]) {
if ((hash & 0xff) === 0xff) {
hash ^= 0xff;
}
- return hash & 0x3fff_ffff;
+ hash &= 0x3fff_ffff;
+ return hash === 0 ? 1 : hash;
}
diff --git a/src/lib/serial/connection.ts b/src/lib/serial/connection.ts
index f0196aa1b..55ecf933f 100644
--- a/src/lib/serial/connection.ts
+++ b/src/lib/serial/connection.ts
@@ -55,7 +55,10 @@ export const syncStatus: Writable<
"done" | "error" | "downloading" | "uploading"
> = writable("done");
-export const deviceMeta = writable(undefined);
+export const deviceMeta = persistentWritable(
+ "current-meta",
+ undefined,
+);
export interface ProgressInfo {
max: number;
diff --git a/src/lib/style/_kbd.scss b/src/lib/style/_kbd.scss
index 0ab2e5dab..a4ae2ba46 100644
--- a/src/lib/style/_kbd.scss
+++ b/src/lib/style/_kbd.scss
@@ -1,22 +1,22 @@
kbd {
- display: inline-flex;
- justify-content: center;
- align-items: center;
- margin-block: 6px;
- border-radius: 4px;
-
- //border: 1px solid currentcolor;
- background: color-mix(
+ --bg-color: color-mix(
in srgb,
var(--md-sys-color-surface-variant) 50%,
transparent
);
- padding: 4px;
+ --border-radius: 4px;
+ display: inline-flex;
+ position: relative;
+ justify-content: center;
+ align-items: center;
+ margin-block: 6px;
+ border-radius: var(--border-radius);
+ background: var(--bg-color);
+ padding: 4px;
height: 20px;
color: currentcolor;
font-weight: normal;
-
font-size: 14px;
&.icon {
diff --git a/src/lib/undo-redo.ts b/src/lib/undo-redo.ts
index cfbf743ad..e89582464 100644
--- a/src/lib/undo-redo.ts
+++ b/src/lib/undo-redo.ts
@@ -1,16 +1,9 @@
import { persistentWritable } from "$lib/storage";
import { derived } from "svelte/store";
-import { hashChord, type Chord } from "$lib/serial/chord";
-import {
- deviceChords,
- deviceLayout,
- deviceSettings,
-} from "$lib/serial/connection";
-import { KEYMAP_CODES } from "$lib/serial/keymap-codes";
+import { deviceLayout, deviceSettings } from "$lib/serial/connection";
export enum ChangeType {
Layout,
- Chord,
Setting,
}
@@ -22,14 +15,6 @@ export interface LayoutChange {
profile?: number;
}
-export interface ChordChange {
- type: ChangeType.Chord;
- deleted?: true;
- id: number[];
- actions: number[];
- phrase: number[];
-}
-
export interface SettingChange {
type: ChangeType.Setting;
id: number;
@@ -42,20 +27,18 @@ export interface ChangeInfo {
isCommitted?: boolean;
}
-export type Change = LayoutChange | ChordChange | SettingChange;
+export type Change = LayoutChange | SettingChange;
export const changes = persistentWritable("changes", []);
export interface Overlay {
layout: Array | undefined> | undefined>;
- chords: Map;
settings: Array