generated from nhcarrigan/template
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9afab03047 |
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hikari-desktop",
|
"name": "hikari-desktop",
|
||||||
"version": "1.6.0",
|
"version": "1.5.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-svelte": "^3.14.0",
|
"eslint-plugin-svelte": "^3.14.0",
|
||||||
"globals": "^17.0.0",
|
"globals": "^17.0.0",
|
||||||
"jsdom": "^27.4.0",
|
"jsdom": "28.0.0",
|
||||||
"prettier": "^3.8.0",
|
"prettier": "^3.8.0",
|
||||||
"prettier-plugin-svelte": "^3.4.1",
|
"prettier-plugin-svelte": "^3.4.1",
|
||||||
"svelte": "^5.0.0",
|
"svelte": "^5.0.0",
|
||||||
|
|||||||
Generated
+52
-57
@@ -149,10 +149,10 @@ importers:
|
|||||||
version: 6.9.1
|
version: 6.9.1
|
||||||
'@testing-library/svelte':
|
'@testing-library/svelte':
|
||||||
specifier: ^5.3.1
|
specifier: ^5.3.1
|
||||||
version: 5.3.1(svelte@5.46.3)(vite@6.4.1(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.17(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2))
|
version: 5.3.1(svelte@5.46.3)(vite@6.4.1(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.17(jiti@2.6.1)(jsdom@28.0.0)(lightningcss@1.30.2))
|
||||||
'@vitest/coverage-v8':
|
'@vitest/coverage-v8':
|
||||||
specifier: ^4.0.18
|
specifier: ^4.0.18
|
||||||
version: 4.0.18(vitest@4.0.17(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2))
|
version: 4.0.18(vitest@4.0.17(jiti@2.6.1)(jsdom@28.0.0)(lightningcss@1.30.2))
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^9.39.2
|
specifier: ^9.39.2
|
||||||
version: 9.39.2(jiti@2.6.1)
|
version: 9.39.2(jiti@2.6.1)
|
||||||
@@ -166,8 +166,8 @@ importers:
|
|||||||
specifier: ^17.0.0
|
specifier: ^17.0.0
|
||||||
version: 17.0.0
|
version: 17.0.0
|
||||||
jsdom:
|
jsdom:
|
||||||
specifier: ^27.4.0
|
specifier: 28.0.0
|
||||||
version: 27.4.0
|
version: 28.0.0
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.8.0
|
specifier: ^3.8.0
|
||||||
version: 3.8.0
|
version: 3.8.0
|
||||||
@@ -194,7 +194,7 @@ importers:
|
|||||||
version: 6.4.1(jiti@2.6.1)(lightningcss@1.30.2)
|
version: 6.4.1(jiti@2.6.1)(lightningcss@1.30.2)
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^4.0.17
|
specifier: ^4.0.17
|
||||||
version: 4.0.17(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)
|
version: 4.0.17(jiti@2.6.1)(jsdom@28.0.0)(lightningcss@1.30.2)
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -552,13 +552,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
|
resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@exodus/bytes@1.8.0':
|
'@exodus/bytes@1.14.0':
|
||||||
resolution: {integrity: sha512-8JPn18Bcp8Uo1T82gR8lh2guEOa5KKU/IEKvvdp0sgmi7coPBWf1Doi1EXsGZb2ehc8ym/StJCjffYV+ne7sXQ==}
|
resolution: {integrity: sha512-YiY1OmY6Qhkvmly8vZiD8wZRpW/npGZNg+0Sk8mstxirRHCg6lolHt5tSODCfuNPE/fBsAqRwDJE417x7jDDHA==}
|
||||||
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@exodus/crypto': ^1.0.0-rc.4
|
'@noble/hashes': ^1.8.0 || ^2.0.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
'@exodus/crypto':
|
'@noble/hashes':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@humanfs/core@0.19.1':
|
'@humanfs/core@0.19.1':
|
||||||
@@ -1277,9 +1277,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==}
|
resolution: {integrity: sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
data-urls@6.0.0:
|
data-urls@7.0.0:
|
||||||
resolution: {integrity: sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==}
|
resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||||
|
|
||||||
debug@4.4.3:
|
debug@4.4.3:
|
||||||
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||||
@@ -1551,8 +1551,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
jsdom@27.4.0:
|
jsdom@28.0.0:
|
||||||
resolution: {integrity: sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==}
|
resolution: {integrity: sha512-KDYJgZ6T2TKdU8yBfYueq5EPG/EylMsBvCaenWMJb2OXmjgczzwveRCoJ+Hgj1lXPDyasvrgneSn4GBuR1hYyA==}
|
||||||
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
canvas: ^3.0.0
|
canvas: ^3.0.0
|
||||||
@@ -1990,6 +1990,10 @@ packages:
|
|||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
undici@7.21.0:
|
||||||
|
resolution: {integrity: sha512-Hn2tCQpoDt1wv23a68Ctc8Cr/BHpUSfaPYrkajTXOS9IKpxVRx/X5m1K2YkbK2ipgZgxXSgsUinl3x+2YdSSfg==}
|
||||||
|
engines: {node: '>=20.18.1'}
|
||||||
|
|
||||||
uri-js@4.4.1:
|
uri-js@4.4.1:
|
||||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||||
|
|
||||||
@@ -2089,14 +2093,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
|
resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
whatwg-mimetype@4.0.0:
|
whatwg-mimetype@5.0.0:
|
||||||
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
|
resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==}
|
||||||
engines: {node: '>=18'}
|
|
||||||
|
|
||||||
whatwg-url@15.1.0:
|
|
||||||
resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==}
|
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
whatwg-url@16.0.0:
|
||||||
|
resolution: {integrity: sha512-9CcxtEKsf53UFwkSUZjG+9vydAsFO4lFHBpJUtjBcoJOCJpKnSJNwCw813zrYJHpCJ7sgfbtOe0V5Ku7Pa1XMQ==}
|
||||||
|
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -2111,18 +2115,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
ws@8.19.0:
|
|
||||||
resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==}
|
|
||||||
engines: {node: '>=10.0.0'}
|
|
||||||
peerDependencies:
|
|
||||||
bufferutil: ^4.0.1
|
|
||||||
utf-8-validate: '>=5.0.2'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
bufferutil:
|
|
||||||
optional: true
|
|
||||||
utf-8-validate:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
xml-name-validator@5.0.0:
|
xml-name-validator@5.0.0:
|
||||||
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
|
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -2544,7 +2536,7 @@ snapshots:
|
|||||||
'@eslint/core': 0.17.0
|
'@eslint/core': 0.17.0
|
||||||
levn: 0.4.1
|
levn: 0.4.1
|
||||||
|
|
||||||
'@exodus/bytes@1.8.0': {}
|
'@exodus/bytes@1.14.0': {}
|
||||||
|
|
||||||
'@humanfs/core@0.19.1': {}
|
'@humanfs/core@0.19.1': {}
|
||||||
|
|
||||||
@@ -2974,14 +2966,14 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
svelte: 5.46.3
|
svelte: 5.46.3
|
||||||
|
|
||||||
'@testing-library/svelte@5.3.1(svelte@5.46.3)(vite@6.4.1(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.17(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2))':
|
'@testing-library/svelte@5.3.1(svelte@5.46.3)(vite@6.4.1(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.17(jiti@2.6.1)(jsdom@28.0.0)(lightningcss@1.30.2))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@testing-library/dom': 10.4.1
|
'@testing-library/dom': 10.4.1
|
||||||
'@testing-library/svelte-core': 1.0.0(svelte@5.46.3)
|
'@testing-library/svelte-core': 1.0.0(svelte@5.46.3)
|
||||||
svelte: 5.46.3
|
svelte: 5.46.3
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 6.4.1(jiti@2.6.1)(lightningcss@1.30.2)
|
vite: 6.4.1(jiti@2.6.1)(lightningcss@1.30.2)
|
||||||
vitest: 4.0.17(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)
|
vitest: 4.0.17(jiti@2.6.1)(jsdom@28.0.0)(lightningcss@1.30.2)
|
||||||
|
|
||||||
'@types/aria-query@5.0.4': {}
|
'@types/aria-query@5.0.4': {}
|
||||||
|
|
||||||
@@ -3089,7 +3081,7 @@ snapshots:
|
|||||||
'@typescript-eslint/types': 8.53.0
|
'@typescript-eslint/types': 8.53.0
|
||||||
eslint-visitor-keys: 4.2.1
|
eslint-visitor-keys: 4.2.1
|
||||||
|
|
||||||
'@vitest/coverage-v8@4.0.18(vitest@4.0.17(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2))':
|
'@vitest/coverage-v8@4.0.18(vitest@4.0.17(jiti@2.6.1)(jsdom@28.0.0)(lightningcss@1.30.2))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@bcoe/v8-coverage': 1.0.2
|
'@bcoe/v8-coverage': 1.0.2
|
||||||
'@vitest/utils': 4.0.18
|
'@vitest/utils': 4.0.18
|
||||||
@@ -3101,7 +3093,7 @@ snapshots:
|
|||||||
obug: 2.1.1
|
obug: 2.1.1
|
||||||
std-env: 3.10.0
|
std-env: 3.10.0
|
||||||
tinyrainbow: 3.0.3
|
tinyrainbow: 3.0.3
|
||||||
vitest: 4.0.17(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)
|
vitest: 4.0.17(jiti@2.6.1)(jsdom@28.0.0)(lightningcss@1.30.2)
|
||||||
|
|
||||||
'@vitest/expect@4.0.17':
|
'@vitest/expect@4.0.17':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3266,10 +3258,12 @@ snapshots:
|
|||||||
css-tree: 3.1.0
|
css-tree: 3.1.0
|
||||||
lru-cache: 11.2.4
|
lru-cache: 11.2.4
|
||||||
|
|
||||||
data-urls@6.0.0:
|
data-urls@7.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
whatwg-mimetype: 4.0.0
|
whatwg-mimetype: 5.0.0
|
||||||
whatwg-url: 15.1.0
|
whatwg-url: 16.0.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@noble/hashes'
|
||||||
|
|
||||||
debug@4.4.3:
|
debug@4.4.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3480,9 +3474,9 @@ snapshots:
|
|||||||
|
|
||||||
html-encoding-sniffer@6.0.0:
|
html-encoding-sniffer@6.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@exodus/bytes': 1.8.0
|
'@exodus/bytes': 1.14.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@exodus/crypto'
|
- '@noble/hashes'
|
||||||
|
|
||||||
html-escaper@2.0.2: {}
|
html-escaper@2.0.2: {}
|
||||||
|
|
||||||
@@ -3550,13 +3544,13 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
argparse: 2.0.1
|
argparse: 2.0.1
|
||||||
|
|
||||||
jsdom@27.4.0:
|
jsdom@28.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@acemir/cssom': 0.9.31
|
'@acemir/cssom': 0.9.31
|
||||||
'@asamuzakjp/dom-selector': 6.7.6
|
'@asamuzakjp/dom-selector': 6.7.6
|
||||||
'@exodus/bytes': 1.8.0
|
'@exodus/bytes': 1.14.0
|
||||||
cssstyle: 5.3.7
|
cssstyle: 5.3.7
|
||||||
data-urls: 6.0.0
|
data-urls: 7.0.0
|
||||||
decimal.js: 10.6.0
|
decimal.js: 10.6.0
|
||||||
html-encoding-sniffer: 6.0.0
|
html-encoding-sniffer: 6.0.0
|
||||||
http-proxy-agent: 7.0.2
|
http-proxy-agent: 7.0.2
|
||||||
@@ -3566,17 +3560,15 @@ snapshots:
|
|||||||
saxes: 6.0.0
|
saxes: 6.0.0
|
||||||
symbol-tree: 3.2.4
|
symbol-tree: 3.2.4
|
||||||
tough-cookie: 6.0.0
|
tough-cookie: 6.0.0
|
||||||
|
undici: 7.21.0
|
||||||
w3c-xmlserializer: 5.0.0
|
w3c-xmlserializer: 5.0.0
|
||||||
webidl-conversions: 8.0.1
|
webidl-conversions: 8.0.1
|
||||||
whatwg-mimetype: 4.0.0
|
whatwg-mimetype: 5.0.0
|
||||||
whatwg-url: 15.1.0
|
whatwg-url: 16.0.0
|
||||||
ws: 8.19.0
|
|
||||||
xml-name-validator: 5.0.0
|
xml-name-validator: 5.0.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@exodus/crypto'
|
- '@noble/hashes'
|
||||||
- bufferutil
|
|
||||||
- supports-color
|
- supports-color
|
||||||
- utf-8-validate
|
|
||||||
|
|
||||||
json-buffer@3.0.1: {}
|
json-buffer@3.0.1: {}
|
||||||
|
|
||||||
@@ -3965,6 +3957,8 @@ snapshots:
|
|||||||
|
|
||||||
typescript@5.6.3: {}
|
typescript@5.6.3: {}
|
||||||
|
|
||||||
|
undici@7.21.0: {}
|
||||||
|
|
||||||
uri-js@4.4.1:
|
uri-js@4.4.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
punycode: 2.3.1
|
punycode: 2.3.1
|
||||||
@@ -3988,7 +3982,7 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 6.4.1(jiti@2.6.1)(lightningcss@1.30.2)
|
vite: 6.4.1(jiti@2.6.1)(lightningcss@1.30.2)
|
||||||
|
|
||||||
vitest@4.0.17(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2):
|
vitest@4.0.17(jiti@2.6.1)(jsdom@28.0.0)(lightningcss@1.30.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/expect': 4.0.17
|
'@vitest/expect': 4.0.17
|
||||||
'@vitest/mocker': 4.0.17(vite@6.4.1(jiti@2.6.1)(lightningcss@1.30.2))
|
'@vitest/mocker': 4.0.17(vite@6.4.1(jiti@2.6.1)(lightningcss@1.30.2))
|
||||||
@@ -4011,7 +4005,7 @@ snapshots:
|
|||||||
vite: 6.4.1(jiti@2.6.1)(lightningcss@1.30.2)
|
vite: 6.4.1(jiti@2.6.1)(lightningcss@1.30.2)
|
||||||
why-is-node-running: 2.3.0
|
why-is-node-running: 2.3.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
jsdom: 27.4.0
|
jsdom: 28.0.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- jiti
|
- jiti
|
||||||
- less
|
- less
|
||||||
@@ -4033,12 +4027,15 @@ snapshots:
|
|||||||
|
|
||||||
webidl-conversions@8.0.1: {}
|
webidl-conversions@8.0.1: {}
|
||||||
|
|
||||||
whatwg-mimetype@4.0.0: {}
|
whatwg-mimetype@5.0.0: {}
|
||||||
|
|
||||||
whatwg-url@15.1.0:
|
whatwg-url@16.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@exodus/bytes': 1.14.0
|
||||||
tr46: 6.0.0
|
tr46: 6.0.0
|
||||||
webidl-conversions: 8.0.1
|
webidl-conversions: 8.0.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@noble/hashes'
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4051,8 +4048,6 @@ snapshots:
|
|||||||
|
|
||||||
word-wrap@1.2.5: {}
|
word-wrap@1.2.5: {}
|
||||||
|
|
||||||
ws@8.19.0: {}
|
|
||||||
|
|
||||||
xml-name-validator@5.0.0: {}
|
xml-name-validator@5.0.0: {}
|
||||||
|
|
||||||
xmlchars@2.2.0: {}
|
xmlchars@2.2.0: {}
|
||||||
|
|||||||
Generated
+1
-1
@@ -1636,7 +1636,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hikari-desktop"
|
name = "hikari-desktop"
|
||||||
version = "1.6.0"
|
version = "1.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"dirs 5.0.1",
|
"dirs 5.0.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "hikari-desktop"
|
name = "hikari-desktop"
|
||||||
version = "1.6.0"
|
version = "1.5.1"
|
||||||
description = "Hikari - Claude Code Visual Assistant"
|
description = "Hikari - Claude Code Visual Assistant"
|
||||||
authors = ["Naomi Carrigan"]
|
authors = ["Naomi Carrigan"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -86,9 +86,8 @@ impl ContextWarning {
|
|||||||
/// Get the context window limit (in tokens) for a given model
|
/// Get the context window limit (in tokens) for a given model
|
||||||
fn get_context_window_limit(model: &str) -> u64 {
|
fn get_context_window_limit(model: &str) -> u64 {
|
||||||
match model {
|
match model {
|
||||||
// Claude 4.6 family
|
// Claude 4.6 family - 200K standard (1M beta available via header)
|
||||||
"claude-opus-4-6" => 200_000,
|
"claude-opus-4-6" => 200_000,
|
||||||
"claude-sonnet-4-6" => 1_000_000, // 1M token context window
|
|
||||||
// Claude 4.5 family - 200K standard context
|
// Claude 4.5 family - 200K standard context
|
||||||
"claude-opus-4-5-20251101"
|
"claude-opus-4-5-20251101"
|
||||||
| "claude-sonnet-4-5-20250929"
|
| "claude-sonnet-4-5-20250929"
|
||||||
@@ -503,7 +502,6 @@ pub fn calculate_cost(
|
|||||||
let (input_price_per_million, output_price_per_million) = match model {
|
let (input_price_per_million, output_price_per_million) = match model {
|
||||||
// Current generation (Claude 4.6)
|
// Current generation (Claude 4.6)
|
||||||
"claude-opus-4-6" => (5.0, 25.0),
|
"claude-opus-4-6" => (5.0, 25.0),
|
||||||
"claude-sonnet-4-6" => (3.0, 15.0),
|
|
||||||
|
|
||||||
// Previous generation (Claude 4.5)
|
// Previous generation (Claude 4.5)
|
||||||
"claude-opus-4-5-20251101" => (5.0, 25.0),
|
"claude-opus-4-5-20251101" => (5.0, 25.0),
|
||||||
|
|||||||
+28
-115
@@ -39,12 +39,6 @@ const SEARCH_TOOLS: [&str; 5] = ["Read", "Glob", "Grep", "WebSearch", "WebFetch"
|
|||||||
const CODING_TOOLS: [&str; 3] = ["Edit", "Write", "NotebookEdit"];
|
const CODING_TOOLS: [&str; 3] = ["Edit", "Write", "NotebookEdit"];
|
||||||
|
|
||||||
fn detect_wsl() -> bool {
|
fn detect_wsl() -> bool {
|
||||||
// A native Windows binary is never running inside WSL, even if launched from a WSL
|
|
||||||
// terminal that has WSL_DISTRO_NAME set in its environment.
|
|
||||||
if cfg!(target_os = "windows") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check /proc/version for WSL indicators
|
// Check /proc/version for WSL indicators
|
||||||
if let Ok(version) = std::fs::read_to_string("/proc/version") {
|
if let Ok(version) = std::fs::read_to_string("/proc/version") {
|
||||||
let version_lower = version.to_lowercase();
|
let version_lower = version.to_lowercase();
|
||||||
@@ -67,29 +61,23 @@ fn detect_wsl() -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn find_claude_binary() -> Option<String> {
|
fn find_claude_binary() -> Option<String> {
|
||||||
// Check common installation locations for claude (when HOME is available)
|
// Check common installation locations for claude
|
||||||
if let Ok(home) = std::env::var("HOME") {
|
let home = std::env::var("HOME").ok()?;
|
||||||
let paths_to_check = [
|
let paths_to_check = [
|
||||||
format!("{}/.local/bin/claude", home),
|
format!("{}/.local/bin/claude", home),
|
||||||
format!("{}/.claude/local/claude", home),
|
format!("{}/.claude/local/claude", home),
|
||||||
];
|
"/usr/local/bin/claude".to_string(),
|
||||||
for path in &paths_to_check {
|
"/usr/bin/claude".to_string(),
|
||||||
if std::path::Path::new(path).exists() {
|
];
|
||||||
return Some(path.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check system-wide locations
|
for path in &paths_to_check {
|
||||||
for path in &["/usr/local/bin/claude", "/usr/bin/claude"] {
|
|
||||||
if std::path::Path::new(path).exists() {
|
if std::path::Path::new(path).exists() {
|
||||||
return Some((*path).to_string());
|
return Some(path.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use a login shell to resolve claude via the user's PATH - GUI apps don't
|
// Fall back to checking PATH via which
|
||||||
// inherit shell PATH, so bare `which` may miss ~/.local/bin entries
|
if let Ok(output) = Command::new("which").arg("claude").output() {
|
||||||
if let Ok(output) = Command::new("bash").args(["-lc", "which claude"]).output() {
|
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||||
if !path.is_empty() {
|
if !path.is_empty() {
|
||||||
@@ -137,19 +125,15 @@ impl WslBridge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&mut self, app: AppHandle, options: ClaudeStartOptions) -> Result<(), String> {
|
pub fn start(&mut self, app: AppHandle, options: ClaudeStartOptions) -> Result<(), String> {
|
||||||
// If a process handle exists but the process has already exited (e.g. due to a
|
|
||||||
// failed working directory), clean up the stale handle so we can restart cleanly.
|
|
||||||
if let Some(ref mut process) = self.process {
|
|
||||||
if process.try_wait().map(|s| s.is_some()).unwrap_or(false) {
|
|
||||||
self.process = None;
|
|
||||||
self.stdin = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.process.is_some() {
|
if self.process.is_some() {
|
||||||
return Err("Process already running".to_string());
|
return Err("Process already running".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if Claude binary is installed before attempting to start
|
||||||
|
if Command::new("which").arg("claude").output().ok().is_none_or(|output| !output.status.success()) {
|
||||||
|
return Err("Claude Code is not installed. Please install it using:\n\ncurl -fsSL https://claude.ai/install.sh | bash".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
// Load saved achievements and stats when starting a new session
|
// Load saved achievements and stats when starting a new session
|
||||||
let app_clone = app.clone();
|
let app_clone = app.clone();
|
||||||
let stats = self.stats.clone();
|
let stats = self.stats.clone();
|
||||||
@@ -278,30 +262,6 @@ impl WslBridge {
|
|||||||
} else {
|
} else {
|
||||||
// Running on Windows - use wsl with bash login shell to ensure PATH is loaded
|
// Running on Windows - use wsl with bash login shell to ensure PATH is loaded
|
||||||
tracing::debug!("Windows path - using wsl");
|
tracing::debug!("Windows path - using wsl");
|
||||||
|
|
||||||
// Check if Claude binary is installed inside WSL
|
|
||||||
let binary_check = Command::new("wsl")
|
|
||||||
.args(["-e", "bash", "-lc", "which claude"])
|
|
||||||
.output();
|
|
||||||
if let Ok(output) = binary_check {
|
|
||||||
if !output.status.success() {
|
|
||||||
return Err("Claude Code is not installed. Please install it using:\n\ncurl -fsSL https://claude.ai/install.sh | bash".to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the working directory exists inside WSL before spawning
|
|
||||||
let dir_check = Command::new("wsl")
|
|
||||||
.args(["-e", "test", "-d", working_dir])
|
|
||||||
.output();
|
|
||||||
if let Ok(output) = dir_check {
|
|
||||||
if !output.status.success() {
|
|
||||||
return Err(format!(
|
|
||||||
"Working directory does not exist: {}",
|
|
||||||
working_dir
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut cmd = Command::new("wsl");
|
let mut cmd = Command::new("wsl");
|
||||||
|
|
||||||
// Build the claude command with all arguments
|
// Build the claude command with all arguments
|
||||||
@@ -1914,66 +1874,19 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stale_process_detection_with_try_wait() {
|
fn test_claude_binary_check_command_structure() {
|
||||||
// Spawn a real process that exits immediately so we can verify try_wait detects it
|
// Test that we're using the correct command to check for Claude binary
|
||||||
let mut child = Command::new("true").spawn().expect("Failed to spawn 'true'");
|
let output = Command::new("which").arg("claude").output();
|
||||||
|
|
||||||
// Wait for it to exit
|
// The command should execute successfully (even if claude is not found)
|
||||||
let _ = child.wait();
|
// We're just verifying the command structure is valid
|
||||||
|
assert!(output.is_ok(), "which command should execute without error");
|
||||||
|
|
||||||
// try_wait on an already-exited process should return Some(_)
|
// Verify the check logic returns a boolean
|
||||||
let status = child.try_wait();
|
// This is the same logic used in start() to check if claude is installed
|
||||||
assert!(
|
let _result = output.ok().is_none_or(|o| !o.status.success());
|
||||||
status.is_ok(),
|
// If claude is not installed, _result will be true (show error)
|
||||||
"try_wait should not error on an exited process"
|
// If claude is installed, _result will be false (proceed with connection)
|
||||||
);
|
|
||||||
// The process has already been waited on, so try_wait might return None or Some
|
|
||||||
// depending on the OS - what matters is that the call succeeds
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_stale_process_is_some_after_exit() {
|
|
||||||
// Verify the logic used in start(): a process that has exited is detected
|
|
||||||
// and the handle is cleaned up so start() can proceed
|
|
||||||
let mut child = Command::new("true").spawn().expect("Failed to spawn 'true'");
|
|
||||||
|
|
||||||
// Let it exit
|
|
||||||
let _ = child.wait();
|
|
||||||
|
|
||||||
// This mirrors the check in start()
|
|
||||||
let has_exited = child
|
|
||||||
.try_wait()
|
|
||||||
.map(|s| s.is_some())
|
|
||||||
.unwrap_or(false);
|
|
||||||
|
|
||||||
// After wait(), try_wait() returns None (already reaped), which means
|
|
||||||
// unwrap_or(false) → false. The important thing is the call doesn't panic
|
|
||||||
// and the control flow logic compiles and runs correctly.
|
|
||||||
let _ = has_exited; // suppress unused warning
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build the WSL binary check command structure without executing it (for testing)
|
|
||||||
#[cfg(test)]
|
|
||||||
fn build_wsl_binary_check_args() -> Vec<&'static str> {
|
|
||||||
vec!["-e", "bash", "-lc", "which claude"]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_wsl_binary_check_command_structure() {
|
|
||||||
// Windows path: verify Claude is detected inside WSL via `wsl -e bash -lc "which claude"`
|
|
||||||
let args = build_wsl_binary_check_args();
|
|
||||||
assert_eq!(args[0], "-e");
|
|
||||||
assert_eq!(args[1], "bash");
|
|
||||||
assert_eq!(args[2], "-lc");
|
|
||||||
assert_eq!(args[3], "which claude");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_linux_binary_check_does_not_panic() {
|
|
||||||
// Linux/WSL path: find_claude_binary() searches Linux filesystem paths.
|
|
||||||
// We just verify it runs without panicking; whether it returns Some depends
|
|
||||||
// on whether Claude is actually installed in this environment.
|
|
||||||
let _result = find_claude_binary();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "hikari-desktop",
|
"productName": "hikari-desktop",
|
||||||
"version": "1.6.0",
|
"version": "1.5.1",
|
||||||
"identifier": "com.naomi.hikari-desktop",
|
"identifier": "com.naomi.hikari-desktop",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "pnpm dev",
|
"beforeDevCommand": "pnpm dev",
|
||||||
|
|||||||
@@ -270,14 +270,6 @@
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
{/if}
|
{/if}
|
||||||
<img
|
|
||||||
src={agent.characterAvatar}
|
|
||||||
alt={agent.characterName}
|
|
||||||
class="w-5 h-5 rounded-full object-cover"
|
|
||||||
/>
|
|
||||||
<span class="text-[10px] font-medium text-[var(--text-primary)]">
|
|
||||||
{agent.characterName}
|
|
||||||
</span>
|
|
||||||
<span
|
<span
|
||||||
class="px-1.5 py-0.5 text-[10px] rounded border {getStatusBadgeClass(
|
class="px-1.5 py-0.5 text-[10px] rounded border {getStatusBadgeClass(
|
||||||
agent.status
|
agent.status
|
||||||
|
|||||||
@@ -1,140 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { CHARACTER_POOL } from "$lib/utils/agentCharacters";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
onClose: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { onClose }: Props = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4"
|
|
||||||
onclick={onClose}
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
onkeydown={(e) => e.key === "Escape" && onClose()}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="bg-[var(--bg-primary)] border border-[var(--border-color)] rounded-lg shadow-xl max-w-2xl w-full p-6 max-h-[90vh] overflow-y-auto"
|
|
||||||
onclick={(e) => e.stopPropagation()}
|
|
||||||
onkeydown={(e) => e.stopPropagation()}
|
|
||||||
role="dialog"
|
|
||||||
aria-labelledby="cast-title"
|
|
||||||
tabindex="-1"
|
|
||||||
>
|
|
||||||
<div class="flex items-center justify-between mb-6">
|
|
||||||
<h2 id="cast-title" class="text-xl font-semibold text-[var(--text-primary)]">
|
|
||||||
Meet the Team
|
|
||||||
</h2>
|
|
||||||
<button
|
|
||||||
onclick={onClose}
|
|
||||||
class="p-1 text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-colors"
|
|
||||||
aria-label="Close"
|
|
||||||
>
|
|
||||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M6 18L18 6M6 6l12 12"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Principal cast: Hikari + Naomi -->
|
|
||||||
<div class="grid grid-cols-1 gap-3 mb-6 sm:grid-cols-2">
|
|
||||||
<div
|
|
||||||
class="flex items-center gap-3 p-4 rounded-lg bg-[var(--bg-secondary)] border border-[var(--accent-primary)]/40"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="https://cdn.nhcarrigan.com/hikari.png"
|
|
||||||
alt="Hikari"
|
|
||||||
class="w-16 h-16 object-cover rounded-full border-2 border-[var(--border-color)] shrink-0"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<span class="font-semibold text-[var(--text-primary)]">Hikari</span>
|
|
||||||
<span
|
|
||||||
class="text-xs px-2 py-0.5 rounded-full bg-[var(--accent-primary)]/20 text-[var(--accent-primary)] font-medium"
|
|
||||||
>
|
|
||||||
Chief Operating Officer
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<p class="text-xs text-[var(--text-secondary)]">
|
|
||||||
Holds the line so the others don't have to. Never without her clipboard — or her
|
|
||||||
glasses.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="flex items-center gap-3 p-4 rounded-lg bg-[var(--bg-secondary)] border border-[var(--accent-primary)]/40"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="https://cdn.nhcarrigan.com/profile.png"
|
|
||||||
alt="Naomi"
|
|
||||||
class="w-16 h-16 object-cover rounded-full border-2 border-[var(--border-color)] shrink-0"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<span class="font-semibold text-[var(--text-primary)]">Naomi</span>
|
|
||||||
<span
|
|
||||||
class="text-xs px-2 py-0.5 rounded-full bg-[var(--accent-primary)]/20 text-[var(--accent-primary)] font-medium"
|
|
||||||
>
|
|
||||||
Chief hEx-ecutive Officer
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<p class="text-xs text-[var(--text-secondary)]">
|
|
||||||
A 525-year-old vampire running a tech company from behind a VTuber avatar. Fixes server
|
|
||||||
crashes at 4 AM.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Subagent girls grid -->
|
|
||||||
<div>
|
|
||||||
<h3 class="text-sm font-medium text-[var(--text-secondary)] uppercase tracking-wider mb-3">
|
|
||||||
Subagent Squad
|
|
||||||
</h3>
|
|
||||||
<div class="grid grid-cols-2 gap-3 sm:grid-cols-3">
|
|
||||||
{#each CHARACTER_POOL as character (character.name)}
|
|
||||||
<div
|
|
||||||
class="flex flex-col items-center gap-2 p-3 rounded-lg bg-[var(--bg-secondary)] border border-[var(--border-color)] text-center"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={character.avatar}
|
|
||||||
alt={character.name}
|
|
||||||
class="w-14 h-14 object-cover rounded-full border-2 border-[var(--border-color)]"
|
|
||||||
/>
|
|
||||||
<span class="text-sm font-medium text-[var(--text-primary)]">{character.name}</span>
|
|
||||||
<span
|
|
||||||
class="text-xs px-2 py-0.5 rounded-full bg-[var(--accent-primary)]/20 text-[var(--accent-primary)] font-medium"
|
|
||||||
>
|
|
||||||
{character.title}
|
|
||||||
</span>
|
|
||||||
<p class="text-xs text-[var(--text-secondary)] leading-snug">{character.description}</p>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
[role="dialog"] {
|
|
||||||
animation: slideIn 0.2s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slideIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: scale(0.95);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -83,9 +83,8 @@
|
|||||||
{ value: "", label: "Default (from ~/.claude)" },
|
{ value: "", label: "Default (from ~/.claude)" },
|
||||||
// Current generation (Claude 4.6)
|
// Current generation (Claude 4.6)
|
||||||
{ value: "claude-opus-4-6", label: "Claude Opus 4.6 (Most Capable)" },
|
{ value: "claude-opus-4-6", label: "Claude Opus 4.6 (Most Capable)" },
|
||||||
{ value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6 (Recommended)" },
|
|
||||||
// Previous generation (Claude 4.5)
|
// Previous generation (Claude 4.5)
|
||||||
{ value: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5" },
|
{ value: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5 (Recommended)" },
|
||||||
{ value: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5 (Fast & Cheap)" },
|
{ value: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5 (Fast & Cheap)" },
|
||||||
{ value: "claude-opus-4-5-20251101", label: "Claude Opus 4.5" },
|
{ value: "claude-opus-4-5-20251101", label: "Claude Opus 4.5" },
|
||||||
// Previous generation (Claude 4.x)
|
// Previous generation (Claude 4.x)
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
import GitPanel from "./GitPanel.svelte";
|
import GitPanel from "./GitPanel.svelte";
|
||||||
import ProfilePanel from "./ProfilePanel.svelte";
|
import ProfilePanel from "./ProfilePanel.svelte";
|
||||||
import AgentMonitorPanel from "./AgentMonitorPanel.svelte";
|
import AgentMonitorPanel from "./AgentMonitorPanel.svelte";
|
||||||
import CastPanel from "./CastPanel.svelte";
|
|
||||||
import PluginManagementPanel from "./PluginManagementPanel.svelte";
|
import PluginManagementPanel from "./PluginManagementPanel.svelte";
|
||||||
import McpManagementPanel from "./McpManagementPanel.svelte";
|
import McpManagementPanel from "./McpManagementPanel.svelte";
|
||||||
import { conversationsStore } from "$lib/stores/conversations";
|
import { conversationsStore } from "$lib/stores/conversations";
|
||||||
@@ -57,7 +56,6 @@
|
|||||||
let showGitPanel = $state(false);
|
let showGitPanel = $state(false);
|
||||||
let showProfile = $state(false);
|
let showProfile = $state(false);
|
||||||
let showAgentMonitor = $state(false);
|
let showAgentMonitor = $state(false);
|
||||||
let showCastPanel = $state(false);
|
|
||||||
let showPluginPanel = $state(false);
|
let showPluginPanel = $state(false);
|
||||||
let showMcpPanel = $state(false);
|
let showMcpPanel = $state(false);
|
||||||
let isSummarising = $state(false);
|
let isSummarising = $state(false);
|
||||||
@@ -521,20 +519,6 @@
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
onclick={() => (showCastPanel = true)}
|
|
||||||
class="p-1 text-gray-500 icon-trans-hover"
|
|
||||||
title="Meet the Team"
|
|
||||||
>
|
|
||||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
onclick={() => (showAgentMonitor = !showAgentMonitor)}
|
onclick={() => (showAgentMonitor = !showAgentMonitor)}
|
||||||
class="p-1 text-gray-500 icon-trans-hover relative {showAgentMonitor
|
class="p-1 text-gray-500 icon-trans-hover relative {showAgentMonitor
|
||||||
@@ -753,10 +737,6 @@
|
|||||||
<AgentMonitorPanel isOpen={showAgentMonitor} onClose={() => (showAgentMonitor = false)} />
|
<AgentMonitorPanel isOpen={showAgentMonitor} onClose={() => (showAgentMonitor = false)} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if showCastPanel}
|
|
||||||
<CastPanel onClose={() => (showCastPanel = false)} />
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if showPluginPanel}
|
{#if showPluginPanel}
|
||||||
<PluginManagementPanel onClose={() => (showPluginPanel = false)} />
|
<PluginManagementPanel onClose={() => (showPluginPanel = false)} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -2,15 +2,12 @@ import { describe, it, expect, beforeEach } from "vitest";
|
|||||||
import { agentStore, getAgentsForConversation, runningAgentCount } from "./agents";
|
import { agentStore, getAgentsForConversation, runningAgentCount } from "./agents";
|
||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
import type { AgentInfo } from "$lib/types/agents";
|
import type { AgentInfo } from "$lib/types/agents";
|
||||||
import { CHARACTER_POOL } from "$lib/utils/agentCharacters";
|
|
||||||
|
|
||||||
describe("agents store", () => {
|
describe("agents store", () => {
|
||||||
const conversationId = "test-conversation-1";
|
const conversationId = "test-conversation-1";
|
||||||
const otherConversationId = "test-conversation-2";
|
const otherConversationId = "test-conversation-2";
|
||||||
|
|
||||||
type AgentInput = Omit<AgentInfo, "characterName" | "characterAvatar">;
|
const createMockAgent = (overrides?: Partial<AgentInfo>): AgentInfo => ({
|
||||||
|
|
||||||
const createMockAgent = (overrides?: Partial<AgentInput>): AgentInput => ({
|
|
||||||
toolUseId: "toolu_test123",
|
toolUseId: "toolu_test123",
|
||||||
description: "Test agent",
|
description: "Test agent",
|
||||||
subagentType: "Explore",
|
subagentType: "Explore",
|
||||||
@@ -40,29 +37,7 @@ describe("agents store", () => {
|
|||||||
|
|
||||||
const agents = get(getAgentsForConversation(conversationId));
|
const agents = get(getAgentsForConversation(conversationId));
|
||||||
expect(agents).toHaveLength(1);
|
expect(agents).toHaveLength(1);
|
||||||
expect(agents[0]).toMatchObject(agent);
|
expect(agents[0]).toEqual(agent);
|
||||||
});
|
|
||||||
|
|
||||||
it("assigns a character name and avatar to added agents", () => {
|
|
||||||
const agent = createMockAgent();
|
|
||||||
agentStore.addAgent(conversationId, agent);
|
|
||||||
|
|
||||||
const agents = get(getAgentsForConversation(conversationId));
|
|
||||||
const validNames = CHARACTER_POOL.map((c) => c.name);
|
|
||||||
expect(validNames).toContain(agents[0].characterName);
|
|
||||||
expect(agents[0].characterAvatar).toMatch(/^https:\/\//u);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("avoids duplicate character names across agents when possible", () => {
|
|
||||||
// Add 6 agents - each should ideally get a unique character
|
|
||||||
for (let i = 0; i < 6; i++) {
|
|
||||||
agentStore.addAgent(conversationId, createMockAgent({ toolUseId: `tool${i.toString()}` }));
|
|
||||||
}
|
|
||||||
|
|
||||||
const agents = get(getAgentsForConversation(conversationId));
|
|
||||||
const names = agents.map((a) => a.characterName);
|
|
||||||
const uniqueNames = new Set(names);
|
|
||||||
expect(uniqueNames.size).toBe(6);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("adds multiple agents to the same conversation", () => {
|
it("adds multiple agents to the same conversation", () => {
|
||||||
@@ -74,8 +49,8 @@ describe("agents store", () => {
|
|||||||
|
|
||||||
const agents = get(getAgentsForConversation(conversationId));
|
const agents = get(getAgentsForConversation(conversationId));
|
||||||
expect(agents).toHaveLength(2);
|
expect(agents).toHaveLength(2);
|
||||||
expect(agents[0]).toMatchObject(agent1);
|
expect(agents[0]).toEqual(agent1);
|
||||||
expect(agents[1]).toMatchObject(agent2);
|
expect(agents[1]).toEqual(agent2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps agents in different conversations separate", () => {
|
it("keeps agents in different conversations separate", () => {
|
||||||
@@ -90,8 +65,8 @@ describe("agents store", () => {
|
|||||||
|
|
||||||
expect(agents1).toHaveLength(1);
|
expect(agents1).toHaveLength(1);
|
||||||
expect(agents2).toHaveLength(1);
|
expect(agents2).toHaveLength(1);
|
||||||
expect(agents1[0]).toMatchObject(agent1);
|
expect(agents1[0]).toEqual(agent1);
|
||||||
expect(agents2[0]).toMatchObject(agent2);
|
expect(agents2[0]).toEqual(agent2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -281,7 +256,7 @@ describe("agents store", () => {
|
|||||||
|
|
||||||
expect(agents1).toHaveLength(0);
|
expect(agents1).toHaveLength(0);
|
||||||
expect(agents2).toHaveLength(1);
|
expect(agents2).toHaveLength(1);
|
||||||
expect(agents2[0]).toMatchObject(agent2);
|
expect(agents2[0]).toEqual(agent2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does nothing if conversation doesn't exist", () => {
|
it("does nothing if conversation doesn't exist", () => {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { writable, derived } from "svelte/store";
|
import { writable, derived } from "svelte/store";
|
||||||
import type { AgentInfo } from "$lib/types/agents";
|
import type { AgentInfo } from "$lib/types/agents";
|
||||||
import { assignCharacter } from "$lib/utils/agentCharacters";
|
|
||||||
|
|
||||||
// Map of conversation ID -> agents in that conversation
|
// Map of conversation ID -> agents in that conversation
|
||||||
const agentsByConversation = writable<Record<string, AgentInfo[]>>({});
|
const agentsByConversation = writable<Record<string, AgentInfo[]>>({});
|
||||||
@@ -9,17 +8,12 @@ function createAgentStore() {
|
|||||||
return {
|
return {
|
||||||
subscribe: agentsByConversation.subscribe,
|
subscribe: agentsByConversation.subscribe,
|
||||||
|
|
||||||
addAgent(conversationId: string, agent: Omit<AgentInfo, "characterName" | "characterAvatar">) {
|
addAgent(conversationId: string, agent: AgentInfo) {
|
||||||
agentsByConversation.update((state) => {
|
agentsByConversation.update((state) => {
|
||||||
const existing = state[conversationId] || [];
|
const existing = state[conversationId] || [];
|
||||||
const activeNames = existing.map((a) => a.characterName);
|
|
||||||
const character = assignCharacter(activeNames);
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
[conversationId]: [
|
[conversationId]: [...existing, agent],
|
||||||
...existing,
|
|
||||||
{ ...agent, characterName: character.name, characterAvatar: character.avatar },
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ export type BudgetType = "token" | "cost";
|
|||||||
export const MODEL_PRICING: Record<string, { input: number; output: number }> = {
|
export const MODEL_PRICING: Record<string, { input: number; output: number }> = {
|
||||||
// Current generation (Claude 4.6)
|
// Current generation (Claude 4.6)
|
||||||
"claude-opus-4-6": { input: 5.0, output: 25.0 },
|
"claude-opus-4-6": { input: 5.0, output: 25.0 },
|
||||||
"claude-sonnet-4-6": { input: 3.0, output: 15.0 },
|
|
||||||
// Previous generation (Claude 4.5)
|
// Previous generation (Claude 4.5)
|
||||||
"claude-opus-4-5-20251101": { input: 5.0, output: 25.0 },
|
"claude-opus-4-5-20251101": { input: 5.0, output: 25.0 },
|
||||||
"claude-sonnet-4-5-20250929": { input: 3.0, output: 15.0 },
|
"claude-sonnet-4-5-20250929": { input: 3.0, output: 15.0 },
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ export interface AgentInfo {
|
|||||||
status: AgentStatus;
|
status: AgentStatus;
|
||||||
parentToolUseId?: string;
|
parentToolUseId?: string;
|
||||||
durationMs?: number;
|
durationMs?: number;
|
||||||
characterName: string;
|
|
||||||
characterAvatar: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentStartPayload {
|
export interface AgentStartPayload {
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
|
||||||
import { CHARACTER_POOL, assignCharacter } from "./agentCharacters";
|
|
||||||
|
|
||||||
describe("agentCharacters", () => {
|
|
||||||
describe("CHARACTER_POOL", () => {
|
|
||||||
it("contains exactly 6 characters", () => {
|
|
||||||
expect(CHARACTER_POOL).toHaveLength(6);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("each character has a name, avatar, title, and description", () => {
|
|
||||||
for (const character of CHARACTER_POOL) {
|
|
||||||
expect(character.name).toBeTruthy();
|
|
||||||
expect(character.avatar).toBeTruthy();
|
|
||||||
expect(character.avatar).toMatch(/^https:\/\//u);
|
|
||||||
expect(character.title).toBeTruthy();
|
|
||||||
expect(character.description).toBeTruthy();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("all names are unique", () => {
|
|
||||||
const names = CHARACTER_POOL.map((c) => c.name);
|
|
||||||
const uniqueNames = new Set(names);
|
|
||||||
expect(uniqueNames.size).toBe(CHARACTER_POOL.length);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("assignCharacter", () => {
|
|
||||||
it("returns a character from the pool", () => {
|
|
||||||
const character = assignCharacter([]);
|
|
||||||
const names = CHARACTER_POOL.map((c) => c.name);
|
|
||||||
expect(names).toContain(character.name);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("avoids names already in use when possible", () => {
|
|
||||||
const takenNames = ["Amari", "Keiko", "Minori", "Reina", "Tatsumi"];
|
|
||||||
// Run many times to confirm we never get a taken name
|
|
||||||
for (let i = 0; i < 50; i++) {
|
|
||||||
const character = assignCharacter(takenNames);
|
|
||||||
expect(takenNames).not.toContain(character.name);
|
|
||||||
expect(character.name).toBe("Yumiko");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("picks from the full pool when all 6 names are taken", () => {
|
|
||||||
const allNames = CHARACTER_POOL.map((c) => c.name);
|
|
||||||
const seen = new Set<string>();
|
|
||||||
// Run enough times that we'd statistically see variety
|
|
||||||
for (let i = 0; i < 100; i++) {
|
|
||||||
const character = assignCharacter(allNames);
|
|
||||||
seen.add(character.name);
|
|
||||||
}
|
|
||||||
// Should still pick valid characters
|
|
||||||
for (const name of seen) {
|
|
||||||
expect(allNames).toContain(name);
|
|
||||||
}
|
|
||||||
// With 100 runs and 6 characters, we should see at least 2 distinct names
|
|
||||||
expect(seen.size).toBeGreaterThan(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns a character with name, avatar, title, and description", () => {
|
|
||||||
const character = assignCharacter([]);
|
|
||||||
expect(character.name).toBeTruthy();
|
|
||||||
expect(character.avatar).toBeTruthy();
|
|
||||||
expect(character.title).toBeTruthy();
|
|
||||||
expect(character.description).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("works when the active list is empty", () => {
|
|
||||||
const character = assignCharacter([]);
|
|
||||||
expect(character).toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
export interface AgentCharacter {
|
|
||||||
name: string;
|
|
||||||
avatar: string;
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CHARACTER_POOL: readonly AgentCharacter[] = [
|
|
||||||
{
|
|
||||||
name: "Amari",
|
|
||||||
avatar: "https://cdn.nhcarrigan.com/amari.png",
|
|
||||||
title: "Executive Assistant",
|
|
||||||
description:
|
|
||||||
"Fey-blooded PA and healer of the team. She always knows when you need a break — and makes sure you take one.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Keiko",
|
|
||||||
avatar: "https://cdn.nhcarrigan.com/keiko.png",
|
|
||||||
title: "Chief Security Officer",
|
|
||||||
description:
|
|
||||||
"Bodyguard and shadow of the family. Conceals blades beneath evening gowns; always watching from the dark.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Minori",
|
|
||||||
avatar: "https://cdn.nhcarrigan.com/minori.png",
|
|
||||||
title: "Chief Compliance Officer",
|
|
||||||
description:
|
|
||||||
"An ancient Automaton built to guard the Great Library. Perfect memory, perfect logic, perfect dedication.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Reina",
|
|
||||||
avatar: "https://cdn.nhcarrigan.com/reina.png",
|
|
||||||
title: "Chief Legal Officer",
|
|
||||||
description:
|
|
||||||
"Demon of the Crossroads turned corporate lawyer. Her binding contracts have held for millennia.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Tatsumi",
|
|
||||||
avatar: "https://cdn.nhcarrigan.com/tatsumi.png",
|
|
||||||
title: "Chief Design Officer",
|
|
||||||
description:
|
|
||||||
"A Siren who traded the ocean for a stylus. Uses her glamour to make every interface welcoming and beautiful.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Yumiko",
|
|
||||||
avatar: "https://cdn.nhcarrigan.com/yumiko.png",
|
|
||||||
title: "Chief Technology Officer",
|
|
||||||
description:
|
|
||||||
"Technomancer and machine whisperer. She communes with machine spirits and keeps the digital world running.",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Picks a character for a new subagent.
|
|
||||||
* Avoids names already assigned to active agents unless all six are taken.
|
|
||||||
*/
|
|
||||||
export function assignCharacter(activeNames: readonly string[]): AgentCharacter {
|
|
||||||
const available = CHARACTER_POOL.filter((c) => !activeNames.includes(c.name));
|
|
||||||
const pool = available.length > 0 ? available : [...CHARACTER_POOL];
|
|
||||||
return pool[Math.floor(Math.random() * pool.length)];
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user