diff --git a/package-lock.json b/package-lock.json index 703503e..171b3be 100755 --- a/package-lock.json +++ b/package-lock.json @@ -75,9 +75,9 @@ } }, "node_modules/@anthropic-ai/claude-code": { - "version": "1.0.24", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-1.0.24.tgz", - "integrity": "sha512-4S6ly2297ngNlto7IFZeEicS9u0yRDhocOzndWFovGBb+iUoEPKdZa/rhVk/tcyCADL6S+mMkiGQOlqFDrN3JQ==", + "version": "1.0.43", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-1.0.43.tgz", + "integrity": "sha512-VnuRK4s/R9ZRTkwH4gUjsp4SiBQXq7Y0B47OtgeXIZYVQYkhTW8m+E0IisFzXXFIyTQrE0SodGCpvgLhAYzGCg==", "hasInstallScript": true, "bin": { "claude": "cli.js" @@ -109,30 +109,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", - "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", - "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", + "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.4", - "@babel/parser": "^7.27.4", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", - "@babel/traverse": "^7.27.4", - "@babel/types": "^7.27.3", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -148,15 +148,15 @@ } }, "node_modules/@babel/generator": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", - "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", "dev": true, "dependencies": { - "@babel/parser": "^7.27.5", - "@babel/types": "^7.27.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -179,6 +179,15 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", @@ -259,12 +268,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", - "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", "dev": true, "dependencies": { - "@babel/types": "^7.27.3" + "@babel/types": "^7.28.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -326,27 +335,27 @@ } }, "node_modules/@babel/traverse": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", - "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", - "@babel/parser": "^7.27.4", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/types": "^7.28.0", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", - "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz", + "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -421,9 +430,9 @@ } }, "node_modules/@codemirror/lang-json": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz", - "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz", + "integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==", "dependencies": { "@codemirror/language": "^6.0.0", "@lezer/json": "^1.0.0" @@ -456,9 +465,9 @@ } }, "node_modules/@codemirror/language": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.1.tgz", - "integrity": "sha512-5kS1U7emOGV84vxC+ruBty5sUgcD0te6dyupyRVG2zaSjhTDM73LhVKUtVwiqSe6QwmEoA4SCiU8AKPFyumAWQ==", + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.2.tgz", + "integrity": "sha512-p44TsNArL4IVXDTbapUmEkAlvWs2CFQbcfc0ymDsis1kH2wh0gcY96AS29c/vp2d0y2Tquk1EDSaawpzilUiAw==", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", @@ -497,9 +506,9 @@ } }, "node_modules/@codemirror/theme-one-dark": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz", - "integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz", + "integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -508,9 +517,9 @@ } }, "node_modules/@codemirror/view": { - "version": "6.37.2", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.37.2.tgz", - "integrity": "sha512-XD3LdgQpxQs5jhOOZ2HRVT+Rj59O4Suc7g2ULvZ+Yi8eCkickrkZ5JFuoDhs2ST1mNI5zSsNYgR3NGa4OUrbnw==", + "version": "6.38.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.0.tgz", + "integrity": "sha512-yvSchUwHOdupXkd7xJ0ob36jdsSR/I+/C+VbY0ffBiL5NiSTEBDfB1ZGWbbIlDd5xgdUkody+lukAdOxYrOBeg==", "dependencies": { "@codemirror/state": "^6.5.0", "crelt": "^1.0.6", @@ -1298,16 +1307,12 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -1318,23 +1323,15 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1475,15 +1472,15 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.11", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.11.tgz", - "integrity": "sha512-L/gAA/hyCSuzTF1ftlzUSI/IKr2POHsv1Dd78GfqkR83KMNuswWD61JxGV2L7nRwBBBSDr6R1gCkdTmoN7W4ag==", + "version": "1.0.0-beta.19", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz", + "integrity": "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==", "dev": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.43.0.tgz", - "integrity": "sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.1.tgz", + "integrity": "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w==", "cpu": [ "arm" ], @@ -1494,9 +1491,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.43.0.tgz", - "integrity": "sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.1.tgz", + "integrity": "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ==", "cpu": [ "arm64" ], @@ -1507,9 +1504,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.43.0.tgz", - "integrity": "sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.1.tgz", + "integrity": "sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg==", "cpu": [ "arm64" ], @@ -1520,9 +1517,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.43.0.tgz", - "integrity": "sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.1.tgz", + "integrity": "sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw==", "cpu": [ "x64" ], @@ -1533,9 +1530,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.43.0.tgz", - "integrity": "sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.1.tgz", + "integrity": "sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA==", "cpu": [ "arm64" ], @@ -1546,9 +1543,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.43.0.tgz", - "integrity": "sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.1.tgz", + "integrity": "sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw==", "cpu": [ "x64" ], @@ -1559,9 +1556,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.43.0.tgz", - "integrity": "sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.1.tgz", + "integrity": "sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==", "cpu": [ "arm" ], @@ -1572,9 +1569,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.43.0.tgz", - "integrity": "sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.1.tgz", + "integrity": "sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==", "cpu": [ "arm" ], @@ -1585,9 +1582,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.43.0.tgz", - "integrity": "sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.1.tgz", + "integrity": "sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==", "cpu": [ "arm64" ], @@ -1598,9 +1595,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.43.0.tgz", - "integrity": "sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.1.tgz", + "integrity": "sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==", "cpu": [ "arm64" ], @@ -1611,9 +1608,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.43.0.tgz", - "integrity": "sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.1.tgz", + "integrity": "sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==", "cpu": [ "loong64" ], @@ -1624,9 +1621,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.43.0.tgz", - "integrity": "sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.1.tgz", + "integrity": "sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==", "cpu": [ "ppc64" ], @@ -1637,9 +1634,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.43.0.tgz", - "integrity": "sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.1.tgz", + "integrity": "sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==", "cpu": [ "riscv64" ], @@ -1650,9 +1647,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.43.0.tgz", - "integrity": "sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.1.tgz", + "integrity": "sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==", "cpu": [ "riscv64" ], @@ -1663,9 +1660,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.43.0.tgz", - "integrity": "sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.1.tgz", + "integrity": "sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==", "cpu": [ "s390x" ], @@ -1676,9 +1673,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.43.0.tgz", - "integrity": "sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.1.tgz", + "integrity": "sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==", "cpu": [ "x64" ], @@ -1689,9 +1686,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.43.0.tgz", - "integrity": "sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.1.tgz", + "integrity": "sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==", "cpu": [ "x64" ], @@ -1702,9 +1699,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.43.0.tgz", - "integrity": "sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.1.tgz", + "integrity": "sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==", "cpu": [ "arm64" ], @@ -1715,9 +1712,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.43.0.tgz", - "integrity": "sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.1.tgz", + "integrity": "sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A==", "cpu": [ "ia32" ], @@ -1728,9 +1725,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.43.0.tgz", - "integrity": "sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.1.tgz", + "integrity": "sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug==", "cpu": [ "x64" ], @@ -1754,18 +1751,6 @@ "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, - "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1816,9 +1801,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==" + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" }, "node_modules/@types/estree-jsx": { "version": "1.0.5", @@ -1878,9 +1863,9 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" }, "node_modules/@uiw/codemirror-extensions-basic-setup": { - "version": "4.23.13", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.23.13.tgz", - "integrity": "sha512-U1CnDFpq6ydNqrRDS5Bdnvgso8ezwwbrmKvmAD3hmoVyRDsDU6HTtmcV+w0rZ3kElUCkKI5lY0DMvTTQ4+L3RQ==", + "version": "4.23.14", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.23.14.tgz", + "integrity": "sha512-lCseubZqjN9bFwHJdQlZEKEo2yO1tCiMMVL0gu3ZXwhqMdfnd6ky/fUCYbn8aJkW+cXKVwjEVhpKjOphNiHoNw==", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/commands": "^6.0.0", @@ -1904,15 +1889,15 @@ } }, "node_modules/@uiw/react-codemirror": { - "version": "4.23.13", - "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.23.13.tgz", - "integrity": "sha512-y65ULzxOAfpxrA/8epoAOeCfmJXu9z0P62BbGOkITJTtU7WI59KfPbbwj35npSsMAkAmDE841qZo2I8jst/THg==", + "version": "4.23.14", + "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.23.14.tgz", + "integrity": "sha512-/CmlSh8LGUEZCxg/f78MEkEMehKnVklqJvJlL10AXXrO/2xOyPqHb8SK10GhwOqd0kHhHgVYp4+6oK5S+UIEuQ==", "dependencies": { "@babel/runtime": "^7.18.6", "@codemirror/commands": "^6.1.0", "@codemirror/state": "^6.1.1", "@codemirror/theme-one-dark": "^6.0.0", - "@uiw/codemirror-extensions-basic-setup": "4.23.13", + "@uiw/codemirror-extensions-basic-setup": "4.23.14", "codemirror": "^6.0.0" }, "funding": { @@ -1934,15 +1919,15 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==" }, "node_modules/@vitejs/plugin-react": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.5.2.tgz", - "integrity": "sha512-QNVT3/Lxx99nMQWJWF7K4N6apUEuT0KlZA3mx/mVaoGj3smm/8rc8ezz15J1pcbcjDK0V15rpHetVfya08r76Q==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.6.0.tgz", + "integrity": "sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ==", "dev": true, "dependencies": { "@babel/core": "^7.27.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.11", + "@rolldown/pluginutils": "1.0.0-beta.19", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, @@ -2179,9 +2164,9 @@ } }, "node_modules/browserslist": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", "dev": true, "funding": [ { @@ -2198,8 +2183,8 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, @@ -2254,9 +2239,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001723", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz", - "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==", + "version": "1.0.30001726", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", + "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==", "dev": true, "funding": [ { @@ -2452,9 +2437,9 @@ } }, "node_modules/codemirror": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", - "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", + "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/commands": "^6.0.0", @@ -2756,9 +2741,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.5.167", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz", - "integrity": "sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==", + "version": "1.5.179", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.179.tgz", + "integrity": "sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ==", "dev": true }, "node_modules/emoji-regex": { @@ -3156,15 +3141,6 @@ "node": ">=10.13.0" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -4442,9 +4418,9 @@ } }, "node_modules/postcss": { - "version": "8.5.5", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz", - "integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "funding": [ { "type": "opencollective", @@ -4560,7 +4536,7 @@ "postcss": "^8.2.14" } }, - "node_modules/postcss-selector-parser": { + "node_modules/postcss-nested/node_modules/postcss-selector-parser": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", @@ -4572,6 +4548,18 @@ "node": ">=4" } }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", @@ -4830,12 +4818,12 @@ } }, "node_modules/rollup": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.43.0.tgz", - "integrity": "sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==", + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.1.tgz", + "integrity": "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==", "dev": true, "dependencies": { - "@types/estree": "1.0.7" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -4845,26 +4833,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.43.0", - "@rollup/rollup-android-arm64": "4.43.0", - "@rollup/rollup-darwin-arm64": "4.43.0", - "@rollup/rollup-darwin-x64": "4.43.0", - "@rollup/rollup-freebsd-arm64": "4.43.0", - "@rollup/rollup-freebsd-x64": "4.43.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.43.0", - "@rollup/rollup-linux-arm-musleabihf": "4.43.0", - "@rollup/rollup-linux-arm64-gnu": "4.43.0", - "@rollup/rollup-linux-arm64-musl": "4.43.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.43.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.43.0", - "@rollup/rollup-linux-riscv64-gnu": "4.43.0", - "@rollup/rollup-linux-riscv64-musl": "4.43.0", - "@rollup/rollup-linux-s390x-gnu": "4.43.0", - "@rollup/rollup-linux-x64-gnu": "4.43.0", - "@rollup/rollup-linux-x64-musl": "4.43.0", - "@rollup/rollup-win32-arm64-msvc": "4.43.0", - "@rollup/rollup-win32-ia32-msvc": "4.43.0", - "@rollup/rollup-win32-x64-msvc": "4.43.0", + "@rollup/rollup-android-arm-eabi": "4.44.1", + "@rollup/rollup-android-arm64": "4.44.1", + "@rollup/rollup-darwin-arm64": "4.44.1", + "@rollup/rollup-darwin-x64": "4.44.1", + "@rollup/rollup-freebsd-arm64": "4.44.1", + "@rollup/rollup-freebsd-x64": "4.44.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.44.1", + "@rollup/rollup-linux-arm-musleabihf": "4.44.1", + "@rollup/rollup-linux-arm64-gnu": "4.44.1", + "@rollup/rollup-linux-arm64-musl": "4.44.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.44.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.44.1", + "@rollup/rollup-linux-riscv64-gnu": "4.44.1", + "@rollup/rollup-linux-riscv64-musl": "4.44.1", + "@rollup/rollup-linux-s390x-gnu": "4.44.1", + "@rollup/rollup-linux-x64-gnu": "4.44.1", + "@rollup/rollup-linux-x64-musl": "4.44.1", + "@rollup/rollup-win32-arm64-msvc": "4.44.1", + "@rollup/rollup-win32-ia32-msvc": "4.44.1", + "@rollup/rollup-win32-x64-msvc": "4.44.1", "fsevents": "~2.3.2" } }, @@ -5663,6 +5651,18 @@ "node": ">= 6" } }, + "node_modules/tailwindcss/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/tailwindcss/node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -6107,9 +6107,9 @@ } }, "node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "engines": { "node": ">=10.0.0" }, diff --git a/public/icons/claude-ai-icon.svg b/public/icons/claude-ai-icon.svg new file mode 100755 index 0000000..853a243 --- /dev/null +++ b/public/icons/claude-ai-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/server/index.js b/server/index.js index a3a308d..e87ad76 100755 --- a/server/index.js +++ b/server/index.js @@ -28,8 +28,11 @@ const fs = require('fs').promises; const { spawn } = require('child_process'); const os = require('os'); const pty = require('node-pty'); +const fetch = require('node-fetch'); + const { getProjects, getSessions, getSessionMessages, renameProject, deleteSession, deleteProject, addProjectManually } = require('./projects'); const { spawnClaude, abortClaudeSession } = require('./claude-cli'); +const gitRoutes = require('./routes/git'); // File system watcher for projects folder let projectsWatcher = null; @@ -144,6 +147,9 @@ app.use(cors()); app.use(express.json()); app.use(express.static(path.join(__dirname, '../dist'))); +// Git API Routes +app.use('/api/git', gitRoutes); + // API Routes app.get('/api/config', (req, res) => { // Always use the server's actual IP and port for WebSocket connections @@ -651,6 +657,156 @@ function handleShellConnection(ws) { console.error('❌ Shell WebSocket error:', error); }); } +// Audio transcription endpoint +app.post('/api/transcribe', async (req, res) => { + try { + const multer = require('multer'); + const upload = multer({ storage: multer.memoryStorage() }); + + // Handle multipart form data + upload.single('audio')(req, res, async (err) => { + if (err) { + return res.status(400).json({ error: 'Failed to process audio file' }); + } + + if (!req.file) { + return res.status(400).json({ error: 'No audio file provided' }); + } + + const apiKey = process.env.OPENAI_API_KEY; + if (!apiKey) { + return res.status(500).json({ error: 'OpenAI API key not configured. Please set OPENAI_API_KEY in server environment.' }); + } + + try { + // Create form data for OpenAI + const FormData = require('form-data'); + const formData = new FormData(); + formData.append('file', req.file.buffer, { + filename: req.file.originalname, + contentType: req.file.mimetype + }); + formData.append('model', 'whisper-1'); + formData.append('response_format', 'json'); + formData.append('language', 'en'); + + // Make request to OpenAI + const fetch = require('node-fetch'); + const response = await fetch('https://api.openai.com/v1/audio/transcriptions', { + method: 'POST', + headers: { + 'Authorization': `Bearer ${apiKey}`, + ...formData.getHeaders() + }, + body: formData + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.error?.message || `Whisper API error: ${response.status}`); + } + + const data = await response.json(); + let transcribedText = data.text || ''; + + // Check if enhancement mode is enabled + const mode = req.body.mode || 'default'; + + // If no transcribed text, return empty + if (!transcribedText) { + return res.json({ text: '' }); + } + + // If default mode, return transcribed text without enhancement + if (mode === 'default') { + return res.json({ text: transcribedText }); + } + + // Handle different enhancement modes + try { + const OpenAI = require('openai'); + const openai = new OpenAI({ apiKey }); + + let prompt, systemMessage, temperature = 0.7, maxTokens = 800; + + switch (mode) { + case 'prompt': + systemMessage = 'You are an expert prompt engineer who creates clear, detailed, and effective prompts.'; + prompt = `You are an expert prompt engineer. Transform the following rough instruction into a clear, detailed, and context-aware AI prompt. + +Your enhanced prompt should: +1. Be specific and unambiguous +2. Include relevant context and constraints +3. Specify the desired output format +4. Use clear, actionable language +5. Include examples where helpful +6. Consider edge cases and potential ambiguities + +Transform this rough instruction into a well-crafted prompt: +"${transcribedText}" + +Enhanced prompt:`; + break; + + case 'vibe': + case 'instructions': + case 'architect': + systemMessage = 'You are a helpful assistant that formats ideas into clear, actionable instructions for AI agents.'; + temperature = 0.5; // Lower temperature for more controlled output + prompt = `Transform the following idea into clear, well-structured instructions that an AI agent can easily understand and execute. + +IMPORTANT RULES: +- Format as clear, step-by-step instructions +- Add reasonable implementation details based on common patterns +- Only include details directly related to what was asked +- Do NOT add features or functionality not mentioned +- Keep the original intent and scope intact +- Use clear, actionable language an agent can follow + +Transform this idea into agent-friendly instructions: +"${transcribedText}" + +Agent instructions:`; + break; + + default: + // No enhancement needed + break; + } + + // Only make GPT call if we have a prompt + if (prompt) { + const completion = await openai.chat.completions.create({ + model: 'gpt-4o-mini', + messages: [ + { role: 'system', content: systemMessage }, + { role: 'user', content: prompt } + ], + temperature: temperature, + max_tokens: maxTokens + }); + + transcribedText = completion.choices[0].message.content || transcribedText; + } + + } catch (gptError) { + console.error('GPT processing error:', gptError); + // Fall back to original transcription if GPT fails + } + + res.json({ text: transcribedText }); + + } catch (error) { + console.error('Transcription error:', error); + res.status(500).json({ error: error.message }); + } + }); + } catch (error) { + console.error('Endpoint error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + // Serve React app for all other routes app.get('*', (req, res) => { diff --git a/server/routes/git.js b/server/routes/git.js new file mode 100755 index 0000000..ebe9794 --- /dev/null +++ b/server/routes/git.js @@ -0,0 +1,388 @@ +const express = require('express'); +const { exec } = require('child_process'); +const { promisify } = require('util'); +const path = require('path'); +const fs = require('fs').promises; + +const router = express.Router(); +const execAsync = promisify(exec); + +// Helper function to get the actual project path from the encoded project name +function getActualProjectPath(projectName) { + // Claude stores projects with dashes instead of slashes + // Convert "-Users-dmieloch-Dev-experiments-claudecodeui" to "/Users/dmieloch/Dev/experiments/claudecodeui" + return projectName.replace(/-/g, '/'); +} + +// Get git status for a project +router.get('/status', async (req, res) => { + const { project } = req.query; + + if (!project) { + return res.status(400).json({ error: 'Project name is required' }); + } + + try { + const projectPath = getActualProjectPath(project); + console.log('Git status for project:', project, '-> path:', projectPath); + + // Check if directory exists + try { + await fs.access(projectPath); + } catch { + console.error('Project path not found:', projectPath); + return res.json({ error: 'Project not found' }); + } + + // Check if it's a git repository + try { + await execAsync('git rev-parse --git-dir', { cwd: projectPath }); + } catch { + console.error('Not a git repository:', projectPath); + return res.json({ error: 'Not a git repository' }); + } + + // Get current branch + const { stdout: branch } = await execAsync('git rev-parse --abbrev-ref HEAD', { cwd: projectPath }); + + // Get git status + const { stdout: statusOutput } = await execAsync('git status --porcelain', { cwd: projectPath }); + + const modified = []; + const added = []; + const deleted = []; + const untracked = []; + + statusOutput.split('\n').forEach(line => { + if (!line.trim()) return; + + const status = line.substring(0, 2); + const file = line.substring(3); + + if (status === 'M ' || status === ' M' || status === 'MM') { + modified.push(file); + } else if (status === 'A ' || status === 'AM') { + added.push(file); + } else if (status === 'D ' || status === ' D') { + deleted.push(file); + } else if (status === '??') { + untracked.push(file); + } + }); + + res.json({ + branch: branch.trim(), + modified, + added, + deleted, + untracked + }); + } catch (error) { + console.error('Git status error:', error); + res.json({ error: error.message }); + } +}); + +// Get diff for a specific file +router.get('/diff', async (req, res) => { + const { project, file } = req.query; + + if (!project || !file) { + return res.status(400).json({ error: 'Project name and file path are required' }); + } + + try { + const projectPath = getActualProjectPath(project); + + // Check if file is untracked + const { stdout: statusOutput } = await execAsync(`git status --porcelain "${file}"`, { cwd: projectPath }); + const isUntracked = statusOutput.startsWith('??'); + + let diff; + if (isUntracked) { + // For untracked files, show the entire file content as additions + const fileContent = await fs.readFile(path.join(projectPath, file), 'utf-8'); + const lines = fileContent.split('\n'); + diff = `--- /dev/null\n+++ b/${file}\n@@ -0,0 +1,${lines.length} @@\n` + + lines.map(line => `+${line}`).join('\n'); + } else { + // Get diff for tracked files + const { stdout } = await execAsync(`git diff HEAD -- "${file}"`, { cwd: projectPath }); + diff = stdout || ''; + + // If no unstaged changes, check for staged changes + if (!diff) { + const { stdout: stagedDiff } = await execAsync(`git diff --cached -- "${file}"`, { cwd: projectPath }); + diff = stagedDiff; + } + } + + res.json({ diff }); + } catch (error) { + console.error('Git diff error:', error); + res.json({ error: error.message }); + } +}); + +// Commit changes +router.post('/commit', async (req, res) => { + const { project, message, files } = req.body; + + if (!project || !message || !files || files.length === 0) { + return res.status(400).json({ error: 'Project name, commit message, and files are required' }); + } + + try { + const projectPath = getActualProjectPath(project); + + // Stage selected files + for (const file of files) { + await execAsync(`git add "${file}"`, { cwd: projectPath }); + } + + // Commit with message + const { stdout } = await execAsync(`git commit -m "${message.replace(/"/g, '\\"')}"`, { cwd: projectPath }); + + res.json({ success: true, output: stdout }); + } catch (error) { + console.error('Git commit error:', error); + res.status(500).json({ error: error.message }); + } +}); + +// Get list of branches +router.get('/branches', async (req, res) => { + const { project } = req.query; + + if (!project) { + return res.status(400).json({ error: 'Project name is required' }); + } + + try { + const projectPath = getActualProjectPath(project); + console.log('Git branches for project:', project, '-> path:', projectPath); + + // Get all branches + const { stdout } = await execAsync('git branch -a', { cwd: projectPath }); + + // Parse branches + const branches = stdout + .split('\n') + .map(branch => branch.trim()) + .filter(branch => branch && !branch.includes('->')) // Remove empty lines and HEAD pointer + .map(branch => { + // Remove asterisk from current branch + if (branch.startsWith('* ')) { + return branch.substring(2); + } + // Remove remotes/ prefix + if (branch.startsWith('remotes/origin/')) { + return branch.substring(15); + } + return branch; + }) + .filter((branch, index, self) => self.indexOf(branch) === index); // Remove duplicates + + res.json({ branches }); + } catch (error) { + console.error('Git branches error:', error); + res.json({ error: error.message }); + } +}); + +// Checkout branch +router.post('/checkout', async (req, res) => { + const { project, branch } = req.body; + + if (!project || !branch) { + return res.status(400).json({ error: 'Project name and branch are required' }); + } + + try { + const projectPath = getActualProjectPath(project); + + // Checkout the branch + const { stdout } = await execAsync(`git checkout "${branch}"`, { cwd: projectPath }); + + res.json({ success: true, output: stdout }); + } catch (error) { + console.error('Git checkout error:', error); + res.status(500).json({ error: error.message }); + } +}); + +// Create new branch +router.post('/create-branch', async (req, res) => { + const { project, branch } = req.body; + + if (!project || !branch) { + return res.status(400).json({ error: 'Project name and branch name are required' }); + } + + try { + const projectPath = getActualProjectPath(project); + + // Create and checkout new branch + const { stdout } = await execAsync(`git checkout -b "${branch}"`, { cwd: projectPath }); + + res.json({ success: true, output: stdout }); + } catch (error) { + console.error('Git create branch error:', error); + res.status(500).json({ error: error.message }); + } +}); + +// Get recent commits +router.get('/commits', async (req, res) => { + const { project, limit = 10 } = req.query; + + if (!project) { + return res.status(400).json({ error: 'Project name is required' }); + } + + try { + const projectPath = getActualProjectPath(project); + + // Get commit log with stats + const { stdout } = await execAsync( + `git log --pretty=format:'%H|%an|%ae|%ad|%s' --date=relative -n ${limit}`, + { cwd: projectPath } + ); + + const commits = stdout + .split('\n') + .filter(line => line.trim()) + .map(line => { + const [hash, author, email, date, ...messageParts] = line.split('|'); + return { + hash, + author, + email, + date, + message: messageParts.join('|') + }; + }); + + // Get stats for each commit + for (const commit of commits) { + try { + const { stdout: stats } = await execAsync( + `git show --stat --format='' ${commit.hash}`, + { cwd: projectPath } + ); + commit.stats = stats.trim().split('\n').pop(); // Get the summary line + } catch (error) { + commit.stats = ''; + } + } + + res.json({ commits }); + } catch (error) { + console.error('Git commits error:', error); + res.json({ error: error.message }); + } +}); + +// Get diff for a specific commit +router.get('/commit-diff', async (req, res) => { + const { project, commit } = req.query; + + if (!project || !commit) { + return res.status(400).json({ error: 'Project name and commit hash are required' }); + } + + try { + const projectPath = getActualProjectPath(project); + + // Get diff for the commit + const { stdout } = await execAsync( + `git show ${commit}`, + { cwd: projectPath } + ); + + res.json({ diff: stdout }); + } catch (error) { + console.error('Git commit diff error:', error); + res.json({ error: error.message }); + } +}); + +// Generate commit message based on staged changes +router.post('/generate-commit-message', async (req, res) => { + const { project, files } = req.body; + + if (!project || !files || files.length === 0) { + return res.status(400).json({ error: 'Project name and files are required' }); + } + + try { + const projectPath = getActualProjectPath(project); + + // Get diff for selected files + let combinedDiff = ''; + for (const file of files) { + try { + const { stdout } = await execAsync( + `git diff HEAD -- "${file}"`, + { cwd: projectPath } + ); + if (stdout) { + combinedDiff += `\n--- ${file} ---\n${stdout}`; + } + } catch (error) { + console.error(`Error getting diff for ${file}:`, error); + } + } + + // Use AI to generate commit message (simple implementation) + // In a real implementation, you might want to use GPT or Claude API + const message = generateSimpleCommitMessage(files, combinedDiff); + + res.json({ message }); + } catch (error) { + console.error('Generate commit message error:', error); + res.status(500).json({ error: error.message }); + } +}); + +// Simple commit message generator (can be replaced with AI) +function generateSimpleCommitMessage(files, diff) { + const fileCount = files.length; + const isMultipleFiles = fileCount > 1; + + // Analyze the diff to determine the type of change + const additions = (diff.match(/^\+[^+]/gm) || []).length; + const deletions = (diff.match(/^-[^-]/gm) || []).length; + + // Determine the primary action + let action = 'Update'; + if (additions > 0 && deletions === 0) { + action = 'Add'; + } else if (deletions > 0 && additions === 0) { + action = 'Remove'; + } else if (additions > deletions * 2) { + action = 'Enhance'; + } else if (deletions > additions * 2) { + action = 'Refactor'; + } + + // Generate message based on files + if (isMultipleFiles) { + const components = new Set(files.map(f => { + const parts = f.split('/'); + return parts[parts.length - 2] || parts[0]; + })); + + if (components.size === 1) { + return `${action} ${[...components][0]} component`; + } else { + return `${action} multiple components`; + } + } else { + const fileName = files[0].split('/').pop(); + const componentName = fileName.replace(/\.(jsx?|tsx?|css|scss)$/, ''); + return `${action} ${componentName}`; + } +} + +module.exports = router; \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index dcfac64..c515fe3 100755 --- a/src/App.jsx +++ b/src/App.jsx @@ -53,6 +53,10 @@ function AppContent() { const saved = localStorage.getItem('showRawParameters'); return saved !== null ? JSON.parse(saved) : false; }); + const [autoScrollToBottom, setAutoScrollToBottom] = useState(() => { + const saved = localStorage.getItem('autoScrollToBottom'); + return saved !== null ? JSON.parse(saved) : true; + }); // Session Protection System: Track sessions with active conversations to prevent // automatic project updates from interrupting ongoing chats. When a user sends // a message, the session is marked as "active" and project updates are paused @@ -217,13 +221,18 @@ function AppContent() { // Handle URL-based session loading useEffect(() => { if (sessionId && projects.length > 0) { + // Only switch tabs on initial load, not on every project update + const shouldSwitchTab = !selectedSession || selectedSession.id !== sessionId; // Find the session across all projects for (const project of projects) { const session = project.sessions?.find(s => s.id === sessionId); if (session) { setSelectedProject(project); setSelectedSession(session); - setActiveTab('chat'); + // Only switch to chat tab if we're loading a different session + if (shouldSwitchTab) { + setActiveTab('chat'); + } return; } } @@ -245,7 +254,11 @@ function AppContent() { const handleSessionSelect = (session) => { setSelectedSession(session); - setActiveTab('chat'); + // Only switch to chat tab when user explicitly selects a session + // This prevents tab switching during automatic updates + if (activeTab !== 'git' && activeTab !== 'preview') { + setActiveTab('chat'); + } if (isMobile) { setSidebarOpen(false); } @@ -482,23 +495,29 @@ function AppContent() { isInputFocused={isInputFocused} /> )} - - {/* Quick Settings Panel */} - { - setAutoExpandTools(value); - localStorage.setItem('autoExpandTools', JSON.stringify(value)); - }} - showRawParameters={showRawParameters} - onShowRawParametersChange={(value) => { - setShowRawParameters(value); - localStorage.setItem('showRawParameters', JSON.stringify(value)); - }} - isMobile={isMobile} - /> + {/* Quick Settings Panel - Only show on chat tab */} + {activeTab === 'chat' && ( + { + setAutoExpandTools(value); + localStorage.setItem('autoExpandTools', JSON.stringify(value)); + }} + showRawParameters={showRawParameters} + onShowRawParametersChange={(value) => { + setShowRawParameters(value); + localStorage.setItem('showRawParameters', JSON.stringify(value)); + }} + autoScrollToBottom={autoScrollToBottom} + onAutoScrollChange={(value) => { + setAutoScrollToBottom(value); + localStorage.setItem('autoScrollToBottom', JSON.stringify(value)); + }} + isMobile={isMobile} + /> + )} {/* Tools Settings Modal */} { const isGrouped = prevMessage && prevMessage.type === message.type && prevMessage.type === 'assistant' && !prevMessage.isToolUse && !message.isToolUse; - const messageRef = React.useRef(null); const [isExpanded, setIsExpanded] = React.useState(false); - React.useEffect(() => { if (!autoExpandTools || !messageRef.current || !message.isToolUse) return; @@ -89,8 +91,8 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile ! ) : ( -
- C +
+
)}
@@ -195,7 +197,8 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile
))}
- {showRawParameters && ( + + {showRawParameters && (
View raw parameters @@ -205,7 +208,6 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile
)} - ); @@ -225,6 +227,101 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile ); })()} {message.toolInput && message.toolName !== 'Edit' && (() => { + // Debug log to see what we're dealing with + console.log('Tool display - name:', message.toolName, 'input type:', typeof message.toolInput); + + // Special handling for Write tool + if (message.toolName === 'Write') { + console.log('Write tool detected, toolInput:', message.toolInput); + try { + let input; + // Handle both JSON string and already parsed object + if (typeof message.toolInput === 'string') { + input = JSON.parse(message.toolInput); + } else { + input = message.toolInput; + } + + console.log('Parsed Write input:', input); + + if (input.file_path && input.content !== undefined) { + return ( +
+ + + + + 📄 Creating new file: + + +
+
+
+ + + New File + +
+
+ {createDiff('', input.content).map((diffLine, i) => ( +
+ + {diffLine.type === 'removed' ? '-' : '+'} + + + {diffLine.content} + +
+ ))} +
+
+ {showRawParameters && ( +
+ + View raw parameters + +
+                                    {message.toolInput}
+                                  
+
+ )} +
+
+ ); + } + } catch (e) { + // Fall back to regular display + } + } + // Special handling for TodoWrite tool if (message.toolName === 'TodoWrite') { try { @@ -251,7 +348,100 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile )} - + + ); + } + } catch (e) { + // Fall back to regular display + } + } + + // Special handling for Bash tool + if (message.toolName === 'Bash') { + try { + const input = JSON.parse(message.toolInput); + return ( +
+ + + + + Running command + +
+
+
+ + + + Terminal +
+
+ $ {input.command} +
+
+ {input.description && ( +
+ {input.description} +
+ )} + {showRawParameters && ( +
+ + View raw parameters + +
+                                  {message.toolInput}
+                                
+
+ )} +
+
+ ); + } catch (e) { + // Fall back to regular display + } + } + + // Special handling for Read tool + if (message.toolName === 'Read') { + try { + const input = JSON.parse(message.toolInput); + if (input.file_path) { + // Extract filename + const filename = input.file_path.split('/').pop(); + const pathParts = input.file_path.split('/'); + const directoryPath = pathParts.slice(0, -1).join('/'); + + // Simple heuristic to show only relevant path parts + // Show the last 2-3 directory parts before the filename + const relevantParts = pathParts.slice(-4, -1); // Get up to 3 directories before filename + const relativePath = relevantParts.length > 0 ? relevantParts.join('/') + '/' : ''; + + return ( +
+ + + + + + + + {relativePath} + {filename} + + {showRawParameters && ( +
+
+ + View raw parameters + +
+                                    {message.toolInput}
+                                  
+
+
+ )}
); } @@ -345,6 +535,106 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile // Fall through to regular handling } } + + // Special handling for interactive prompts + if (content.includes('Do you want to proceed?') && message.toolName === 'Bash') { + const lines = content.split('\n'); + const promptIndex = lines.findIndex(line => line.includes('Do you want to proceed?')); + const beforePrompt = lines.slice(0, promptIndex).join('\n'); + const promptLines = lines.slice(promptIndex); + + // Extract the question and options + const questionLine = promptLines.find(line => line.includes('Do you want to proceed?')) || ''; + const options = []; + + // Parse numbered options (1. Yes, 2. No, etc.) + promptLines.forEach(line => { + const optionMatch = line.match(/^\s*(\d+)\.\s+(.+)$/); + if (optionMatch) { + options.push({ + number: optionMatch[1], + text: optionMatch[2].trim() + }); + } + }); + + // Find which option was selected (usually indicated by "> 1" or similar) + const selectedMatch = content.match(/>\s*(\d+)/); + const selectedOption = selectedMatch ? selectedMatch[1] : null; + + return ( +
+ {beforePrompt && ( +
+
{beforePrompt}
+
+ )} +
+
+
+ + + +
+
+

+ Interactive Prompt +

+

+ {questionLine} +

+ + {/* Option buttons */} +
+ {options.map((option) => ( + + ))} +
+ + {selectedOption && ( +
+

+ ✓ Claude selected option {selectedOption} +

+

+ In the CLI, you would select this option interactively using arrow keys or by typing the number. +

+
+ )} +
+
+
+
+ ); + } const fileEditMatch = content.match(/The file (.+?) has been updated\./); if (fileEditMatch) { @@ -363,6 +653,43 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile ); } + // Handle Write tool output for file creation + const fileCreateMatch = content.match(/(?:The file|File) (.+?) has been (?:created|written)(?: successfully)?\.?/); + if (fileCreateMatch) { + return ( +
+
+ File created successfully +
+ +
+ ); + } + + // Special handling for Write tool - hide content if it's just the file content + if (message.toolName === 'Write' && !message.toolResult.isError) { + // For Write tool, the diff is already shown in the tool input section + // So we just show a success message here + return ( +
+
+ + + + File written successfully +
+

+ The file content is displayed in the diff view above +

+
+ ); + } + if (content.includes('cat -n') && content.includes('→')) { return (
@@ -407,17 +734,100 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile )} + ) : message.isInteractivePrompt ? ( + // Special handling for interactive prompts +
+
+
+ + + +
+
+

+ Interactive Prompt +

+ {(() => { + const lines = message.content.split('\n').filter(line => line.trim()); + const questionLine = lines.find(line => line.includes('?')) || lines[0] || ''; + const options = []; + + // Parse the menu options + lines.forEach(line => { + // Match lines like "❯ 1. Yes" or " 2. No" + const optionMatch = line.match(/[❯\s]*(\d+)\.\s+(.+)/); + if (optionMatch) { + const isSelected = line.includes('❯'); + options.push({ + number: optionMatch[1], + text: optionMatch[2].trim(), + isSelected + }); + } + }); + + return ( + <> +

+ {questionLine} +

+ + {/* Option buttons */} +
+ {options.map((option) => ( + + ))} +
+ +
+

+ ⏳ Waiting for your response in the CLI +

+

+ Please select an option in your terminal where Claude is running. +

+
+ + ); + })()} +
+
+
) : (
{message.type === 'assistant' ? ( -
+
{ return inline ? ( - + {children} - + ) : (
@@ -454,7 +864,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile
)} -
+
{new Date(message.timestamp).toLocaleTimeString()}
@@ -472,7 +882,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile // - onReplaceTemporarySession: Called to replace temporary session ID with real WebSocket session ID // // This ensures uninterrupted chat experience by pausing sidebar refreshes during conversations. -function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, messages, onFileOpen, onInputFocusChange, onSessionActive, onSessionInactive, onReplaceTemporarySession, onNavigateToSession, onShowSettings, autoExpandTools, showRawParameters }) { +function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, messages, onFileOpen, onInputFocusChange, onSessionActive, onSessionInactive, onReplaceTemporarySession, onNavigateToSession, onShowSettings, autoExpandTools, showRawParameters, autoScrollToBottom }) { const [input, setInput] = useState(() => { if (typeof window !== 'undefined' && selectedProject) { return localStorage.getItem(`draft_input_${selectedProject.name}`) || ''; @@ -504,6 +914,14 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess const [atSymbolPosition, setAtSymbolPosition] = useState(-1); const [canAbortSession, setCanAbortSession] = useState(false); const [isUserScrolledUp, setIsUserScrolledUp] = useState(false); + const scrollPositionRef = useRef({ height: 0, top: 0 }); + const [showCommandMenu, setShowCommandMenu] = useState(false); + const [slashCommands, setSlashCommands] = useState([]); + const [filteredCommands, setFilteredCommands] = useState([]); + const [isTextareaExpanded, setIsTextareaExpanded] = useState(false); + const [selectedCommandIndex, setSelectedCommandIndex] = useState(-1); + const [slashPosition, setSlashPosition] = useState(-1); + const [claudeStatus, setClaudeStatus] = useState(null); // Memoized diff calculation to prevent recalculating on every render @@ -718,8 +1136,10 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess const messages = await loadSessionMessages(selectedProject.name, selectedSession.id); setSessionMessages(messages); // convertedMessages will be automatically updated via useMemo - // Scroll to bottom after loading session messages - setTimeout(() => scrollToBottom(), 200); + // Scroll to bottom after loading session messages if auto-scroll is enabled + if (autoScrollToBottom) { + setTimeout(() => scrollToBottom(), 200); + } } else { // Reset the flag after handling system session change setIsSystemSessionChange(false); @@ -920,7 +1340,16 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess timestamp: new Date() }]); break; - + case 'claude-interactive-prompt': + // Handle interactive prompts from CLI + setChatMessages(prev => [...prev, { + type: 'assistant', + content: latestMessage.data, + timestamp: new Date(), + isInteractivePrompt: true + }]); + break; + case 'claude-error': setChatMessages(prev => [...prev, { type: 'error', @@ -932,6 +1361,8 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess case 'claude-complete': setIsLoading(false); setCanAbortSession(false); + setClaudeStatus(null); + // Session Protection: Mark session as inactive to re-enable automatic project updates // Conversation is complete, safe to allow project updates again @@ -952,6 +1383,7 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess case 'session-aborted': setIsLoading(false); setCanAbortSession(false); + setClaudeStatus(null); // Session Protection: Mark session as inactive when aborted // User or system aborted the conversation, re-enable project updates @@ -960,13 +1392,52 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess } setChatMessages(prev => [...prev, { - type: 'error', - content: latestMessage.success ? - 'Session aborted successfully' : - 'Failed to abort session - it may have already completed', + type: 'assistant', + content: 'Session interrupted by user.', timestamp: new Date() }]); break; + + case 'claude-status': + // Handle Claude working status messages + console.log('🔔 Received claude-status message:', latestMessage); + const statusData = latestMessage.data; + if (statusData) { + // Parse the status message to extract relevant information + let statusInfo = { + text: 'Working...', + tokens: 0, + can_interrupt: true + }; + + // Check for different status message formats + if (statusData.message) { + statusInfo.text = statusData.message; + } else if (statusData.status) { + statusInfo.text = statusData.status; + } else if (typeof statusData === 'string') { + statusInfo.text = statusData; + } + + // Extract token count + if (statusData.tokens) { + statusInfo.tokens = statusData.tokens; + } else if (statusData.token_count) { + statusInfo.tokens = statusData.token_count; + } + + // Check if can interrupt + if (statusData.can_interrupt !== undefined) { + statusInfo.can_interrupt = statusData.can_interrupt; + } + + console.log('📊 Setting claude status:', statusInfo); + setClaudeStatus(statusInfo); + setIsLoading(true); + setCanAbortSession(statusInfo.can_interrupt); + } + break; + } } }, [messages]); @@ -1057,21 +1528,48 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess return chatMessages.slice(-maxMessages); }, [chatMessages]); + // Capture scroll position before render when auto-scroll is disabled useEffect(() => { - // Only auto-scroll to bottom when new messages arrive if user hasn't scrolled up + if (!autoScrollToBottom && scrollContainerRef.current) { + const container = scrollContainerRef.current; + scrollPositionRef.current = { + height: container.scrollHeight, + top: container.scrollTop + }; + } + }); + + useEffect(() => { + // Only auto-scroll to bottom when new messages arrive if: + // 1. Auto-scroll is enabled in settings + // 2. User hasn't manually scrolled up if (scrollContainerRef.current && chatMessages.length > 0) { - if (!isUserScrolledUp) { - setTimeout(() => scrollToBottom(), 0); + if (autoScrollToBottom) { + if (!isUserScrolledUp) { + setTimeout(() => scrollToBottom(), 0); + } + } else { + // When auto-scroll is disabled, preserve the visual position + const container = scrollContainerRef.current; + const prevHeight = scrollPositionRef.current.height; + const prevTop = scrollPositionRef.current.top; + const newHeight = container.scrollHeight; + const heightDiff = newHeight - prevHeight; + + // If content was added above the current view, adjust scroll position + if (heightDiff > 0 && prevTop > 0) { + container.scrollTop = prevTop + heightDiff; + } } } - }, [chatMessages.length, isUserScrolledUp, scrollToBottom]); + }, [chatMessages.length, isUserScrolledUp, scrollToBottom, autoScrollToBottom]); // Scroll to bottom when component mounts with existing messages useEffect(() => { - if (scrollContainerRef.current && chatMessages.length > 0) { + if (scrollContainerRef.current && chatMessages.length > 0 && autoScrollToBottom) { setTimeout(() => scrollToBottom(), 100); // Small delay to ensure rendering } - }, [scrollToBottom]); + }, [scrollToBottom, autoScrollToBottom]); // Add scroll event listener to detect user scrolling useEffect(() => { @@ -1087,9 +1585,36 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess if (textareaRef.current) { textareaRef.current.style.height = 'auto'; textareaRef.current.style.height = textareaRef.current.scrollHeight + 'px'; + + // Check if initially expanded + const lineHeight = parseInt(window.getComputedStyle(textareaRef.current).lineHeight); + const isExpanded = textareaRef.current.scrollHeight > lineHeight * 2; + setIsTextareaExpanded(isExpanded); } }, []); // Only run once on mount + const handleTranscript = useCallback((text) => { + if (text.trim()) { + setInput(prevInput => { + const newInput = prevInput.trim() ? `${prevInput} ${text}` : text; + + // Update textarea height after setting new content + setTimeout(() => { + if (textareaRef.current) { + textareaRef.current.style.height = 'auto'; + textareaRef.current.style.height = textareaRef.current.scrollHeight + 'px'; + + // Check if expanded after transcript + const lineHeight = parseInt(window.getComputedStyle(textareaRef.current).lineHeight); + const isExpanded = textareaRef.current.scrollHeight > lineHeight * 2; + setIsTextareaExpanded(isExpanded); + } + }, 0); + + return newInput; + }); + } + }, []); const handleSubmit = (e) => { e.preventDefault(); @@ -1104,6 +1629,12 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess setChatMessages(prev => [...prev, userMessage]); setIsLoading(true); setCanAbortSession(true); + // Set a default status when starting + setClaudeStatus({ + text: 'Processing', + tokens: 0, + can_interrupt: true + }); // Always scroll to bottom when user sends a message (they're actively participating) setTimeout(() => scrollToBottom(), 0); @@ -1151,6 +1682,7 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess }); setInput(''); + setIsTextareaExpanded(false); // Clear the saved draft since message was sent if (selectedProject) { localStorage.removeItem(`draft_input_${selectedProject.name}`); @@ -1245,7 +1777,14 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess setCanAbortSession(false); }; - // Abort functionality is not yet implemented at the backend + const handleAbortSession = () => { + if (currentSessionId && canAbortSession) { + sendMessage({ + type: 'abort-session', + sessionId: currentSessionId + }); + } + }; // Don't render if no project is selected if (!selectedProject) { @@ -1281,11 +1820,13 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
) : chatMessages.length === 0 ? ( -
-

Start a conversation with Claude

-

- Ask questions about your code, request changes, or get help with development tasks -

+
+
+

Start a conversation with Claude

+

+ Ask questions about your code, request changes, or get help with development tasks +

+
) : ( <> @@ -1312,7 +1853,6 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess onShowSettings={onShowSettings} autoExpandTools={autoExpandTools} showRawParameters={showRawParameters} - /> ); })} @@ -1347,7 +1887,7 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess {isUserScrolledUp && chatMessages.length > 0 && (