diff --git a/bun.lock b/bun.lock index d1297ff61e7..4c84c810477 100644 --- a/bun.lock +++ b/bun.lock @@ -373,6 +373,7 @@ "@ai-sdk/xai": "2.0.56", "@clack/prompts": "1.0.0-alpha.1", "@gitlab/gitlab-ai-provider": "3.4.0", + "@gitlab/opencode-gitlab-auth": "1.3.2", "@hono/standard-validator": "0.1.5", "@hono/zod-validator": "catalog:", "@kilocode/kilo-gateway": "workspace:*", @@ -397,7 +398,7 @@ "ai": "catalog:", "ai-gateway-provider": "2.3.1", "bonjour-service": "1.3.0", - "bun-pty": "0.4.4", + "bun-pty": "0.4.8", "chokidar": "4.0.3", "clipboardy": "4.0.0", "decimal.js": "10.5.0", @@ -1039,8 +1040,22 @@ "@expressive-code/plugin-text-markers": ["@expressive-code/plugin-text-markers@0.41.6", "", { "dependencies": { "@expressive-code/core": "^0.41.6" } }, "sha512-PBFa1wGyYzRExMDzBmAWC6/kdfG1oLn4pLpBeTfIRrALPjcGA/59HP3e7q9J0Smk4pC7U+lWkA2LHR8FYV8U7Q=="], + "@fastify/ajv-compiler": ["@fastify/ajv-compiler@4.0.5", "", { "dependencies": { "ajv": "^8.12.0", "ajv-formats": "^3.0.1", "fast-uri": "^3.0.0" } }, "sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A=="], + "@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="], + "@fastify/error": ["@fastify/error@4.2.0", "", {}, "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ=="], + + "@fastify/fast-json-stringify-compiler": ["@fastify/fast-json-stringify-compiler@5.0.3", "", { "dependencies": { "fast-json-stringify": "^6.0.0" } }, "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ=="], + + "@fastify/forwarded": ["@fastify/forwarded@3.0.1", "", {}, "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw=="], + + "@fastify/merge-json-schemas": ["@fastify/merge-json-schemas@0.2.1", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A=="], + + "@fastify/proxy-addr": ["@fastify/proxy-addr@5.1.0", "", { "dependencies": { "@fastify/forwarded": "^3.0.0", "ipaddr.js": "^2.1.0" } }, "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw=="], + + "@fastify/rate-limit": ["@fastify/rate-limit@10.3.0", "", { "dependencies": { "@lukeed/ms": "^2.0.2", "fastify-plugin": "^5.0.0", "toad-cache": "^3.7.0" } }, "sha512-eIGkG9XKQs0nyynatApA3EVrojHOuq4l6fhB4eeCk4PIOeadvOJz9/4w3vGI44Go17uaXOWEcPkaD8kuKm7g6Q=="], + "@floating-ui/core": ["@floating-ui/core@1.7.4", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg=="], "@floating-ui/dom": ["@floating-ui/dom@1.7.5", "", { "dependencies": { "@floating-ui/core": "^1.7.4", "@floating-ui/utils": "^0.2.10" } }, "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg=="], @@ -1055,6 +1070,8 @@ "@gitlab/gitlab-ai-provider": ["@gitlab/gitlab-ai-provider@3.4.0", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=2.0.0", "@ai-sdk/provider-utils": ">=3.0.0" } }, "sha512-1fEZgqjSZ0WLesftw/J5UtFuJCYFDvCZCHhTH5PZAmpDEmCwllJBoe84L3+vIk38V2FGDMTW128iKTB2mVzr3A=="], + "@gitlab/opencode-gitlab-auth": ["@gitlab/opencode-gitlab-auth@1.3.2", "", { "dependencies": { "@fastify/rate-limit": "^10.2.0", "@opencode-ai/plugin": "*", "fastify": "^5.2.0", "open": "^10.0.0" } }, "sha512-pvGrC+aDVLY8bRCC/fZaG/Qihvt2r4by5xbTo5JTSz9O7yIcR6xG2d9Wkuu4bcXFz674z2C+i5bUk+J/RSdBpg=="], + "@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="], "@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.0.11", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.11" } }, "sha512-GqNqiShBT/lzkHTMC/slKBrvN0DsD4Di8ssBk4aDaVgEn+2WMzE6DXxq701ndSXj7/0cJ8mNT71pM7Bnrr6JRw=="], @@ -1271,6 +1288,8 @@ "@leichtgewicht/ip-codec": ["@leichtgewicht/ip-codec@2.0.5", "", {}, "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw=="], + "@lukeed/ms": ["@lukeed/ms@2.0.2", "", {}, "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA=="], + "@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="], "@mixmark-io/domino": ["@mixmark-io/domino@2.2.0", "", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="], @@ -1359,8 +1378,12 @@ "@opencode-ai/function": ["@opencode-ai/function@workspace:packages/function"], + "@opencode-ai/plugin": ["@opencode-ai/plugin@1.1.52", "", { "dependencies": { "@opencode-ai/sdk": "1.1.52", "zod": "4.1.8" } }, "sha512-xmh4aKLgcUDcVdtTKdUXtX1ts8RngLLbwcIMUDEc/fOq0Y+3Dh52iMc13Daj/AhkR9j5Zs/+QIZnAo49QMSPgg=="], + "@opencode-ai/script": ["@opencode-ai/script@workspace:packages/script"], + "@opencode-ai/sdk": ["@opencode-ai/sdk@1.1.52", "", {}, "sha512-WtyQRGv1xdfMxVXySyKB7NbzmEcR+sqyHwWvndeY3dnxN+kxyezOjVatCB10zxznE9dz3qWIMwemVB0FwiXo/A=="], + "@opencode-ai/slack": ["@opencode-ai/slack@workspace:packages/slack"], "@opencode-ai/ui": ["@opencode-ai/ui@workspace:packages/ui"], @@ -1523,6 +1546,8 @@ "@pierre/diffs": ["@pierre/diffs@1.0.2", "", { "dependencies": { "@shikijs/core": "^3.0.0", "@shikijs/engine-javascript": "3.19.0", "@shikijs/transformers": "3.19.0", "diff": "8.0.2", "hast-util-to-html": "9.0.5", "lru_map": "0.4.1", "shiki": "3.19.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-RkFSDD5X/U+8QjyilPViYGJfmJNWXR17zTL8zw48+DcVC1Ujbh6I1edyuRnFfgRzpft05x2DSCkz2cjoIAxPvQ=="], + "@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="], + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], "@planetscale/database": ["@planetscale/database@1.19.0", "", {}, "sha512-Tv4jcFUFAFjOWrGSio49H6R2ijALv0ZzVBfJKIdm+kl9X046Fh4LLawrF9OMsglVbK6ukqMJsUCeucGAFTBcMA=="], @@ -2131,6 +2156,8 @@ "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], + "abstract-logging": ["abstract-logging@2.0.1", "", {}, "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="], + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], @@ -2207,10 +2234,14 @@ "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + "atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="], + "autoprefixer": ["autoprefixer@10.4.24", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001766", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw=="], "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + "avvio": ["avvio@9.1.0", "", { "dependencies": { "@fastify/error": "^4.0.0", "fastq": "^1.17.1" } }, "sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw=="], + "await-to-js": ["await-to-js@3.0.0", "", {}, "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g=="], "aws-sdk": ["aws-sdk@2.1692.0", "", { "dependencies": { "buffer": "4.9.2", "events": "1.1.1", "ieee754": "1.1.13", "jmespath": "0.16.0", "querystring": "0.2.0", "sax": "1.2.1", "url": "0.10.3", "util": "^0.12.4", "uuid": "8.0.0", "xml2js": "0.6.2" } }, "sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw=="], @@ -2297,7 +2328,7 @@ "bun-ffi-structs": ["bun-ffi-structs@0.1.2", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w=="], - "bun-pty": ["bun-pty@0.4.4", "", {}, "sha512-WK4G6uWsZgu1v4hKIlw6G1q2AOf8Rbga2Yr7RnxArVjjyb+mtVa/CFc9GOJf+OYSJSH8k7LonAtQOVeNAddRyg=="], + "bun-pty": ["bun-pty@0.4.8", "", {}, "sha512-rO70Mrbr13+jxHHHu2YBkk2pNqrJE5cJn29WE++PUr+GFA0hq/VgtQPZANJ8dJo6d7XImvBk37Innt8GM7O28w=="], "bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="], @@ -2689,6 +2720,8 @@ "fast-content-type-parse": ["fast-content-type-parse@3.0.0", "", {}, "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg=="], + "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="], @@ -2697,12 +2730,20 @@ "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + "fast-json-stringify": ["fast-json-stringify@6.2.0", "", { "dependencies": { "@fastify/merge-json-schemas": "^0.2.0", "ajv": "^8.12.0", "ajv-formats": "^3.0.1", "fast-uri": "^3.0.0", "json-schema-ref-resolver": "^3.0.0", "rfdc": "^1.2.0" } }, "sha512-Eaf/KNIDwHkzfyeQFNfLXJnQ7cl1XQI3+zRqmPlvtkMigbXnAcasTrvJQmquBSxKfFGeRA6PFog8t+hFmpDoWw=="], + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + "fast-querystring": ["fast-querystring@1.1.2", "", { "dependencies": { "fast-decode-uri-component": "^1.0.1" } }, "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg=="], + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], "fast-xml-parser": ["fast-xml-parser@4.4.1", "", { "dependencies": { "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw=="], + "fastify": ["fastify@5.7.4", "", { "dependencies": { "@fastify/ajv-compiler": "^4.0.5", "@fastify/error": "^4.0.0", "@fastify/fast-json-stringify-compiler": "^5.0.0", "@fastify/proxy-addr": "^5.0.0", "abstract-logging": "^2.0.1", "avvio": "^9.0.0", "fast-json-stringify": "^6.0.0", "find-my-way": "^9.0.0", "light-my-request": "^6.0.0", "pino": "^10.1.0", "process-warning": "^5.0.0", "rfdc": "^1.3.1", "secure-json-parse": "^4.0.0", "semver": "^7.6.0", "toad-cache": "^3.7.0" } }, "sha512-e6l5NsRdaEP8rdD8VR0ErJASeyaRbzXYpmkrpr2SuvuMq6Si3lvsaVy5C+7gLanEkvjpMDzBXWE5HPeb/hgTxA=="], + + "fastify-plugin": ["fastify-plugin@5.1.0", "", {}, "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw=="], + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], @@ -2719,6 +2760,8 @@ "find-babel-config": ["find-babel-config@2.1.2", "", { "dependencies": { "json5": "^2.2.3" } }, "sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg=="], + "find-my-way": ["find-my-way@9.4.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-querystring": "^1.0.0", "safe-regex2": "^5.0.0" } }, "sha512-5Ye4vHsypZRYtS01ob/iwHzGRUDELlsoCftI/OZFhcLs1M0tkGPcXldE80TAZC5yYuJMBPJQQ43UHlqbJWiX2w=="], + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], "finity": ["finity@0.5.4", "", {}, "sha512-3l+5/1tuw616Lgb0QBimxfdd2TqaDGpfCBpfX6EqtFmqUV3FtQnVEX4Aa62DagYEqnsTIjZcTfbq9msDbXYgyA=="], @@ -2965,7 +3008,7 @@ "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], - "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + "ipaddr.js": ["ipaddr.js@2.3.0", "", {}, "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg=="], "iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="], @@ -3117,6 +3160,8 @@ "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], + "json-schema-ref-resolver": ["json-schema-ref-resolver@3.0.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A=="], + "json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="], "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], @@ -3165,6 +3210,8 @@ "lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="], + "light-my-request": ["light-my-request@6.6.0", "", { "dependencies": { "cookie": "^1.0.1", "process-warning": "^4.0.0", "set-cookie-parser": "^2.6.0" } }, "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A=="], + "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], @@ -3489,6 +3536,8 @@ "omggif": ["omggif@1.0.10", "", {}, "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw=="], + "on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="], + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], @@ -3605,6 +3654,12 @@ "pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="], + "pino": ["pino@10.3.0", "", { "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^3.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^4.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-0GNPNzHXBKw6U/InGe79A3Crzyk9bcSyObF9/Gfo9DLEf5qj5RF50RSjsu0W1rZ6ZqRGdzDFCRBQvi9/rSGPtA=="], + + "pino-abstract-transport": ["pino-abstract-transport@3.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg=="], + + "pino-std-serializers": ["pino-std-serializers@7.1.0", "", {}, "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw=="], + "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], "pixelmatch": ["pixelmatch@5.3.0", "", { "dependencies": { "pngjs": "^6.0.0" }, "bin": { "pixelmatch": "bin/pixelmatch" } }, "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q=="], @@ -3661,6 +3716,8 @@ "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], + "process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="], + "promise.allsettled": ["promise.allsettled@1.0.7", "", { "dependencies": { "array.prototype.map": "^1.0.5", "call-bind": "^1.0.2", "define-properties": "^1.2.0", "es-abstract": "^1.22.1", "get-intrinsic": "^1.2.1", "iterate-value": "^1.0.2" } }, "sha512-hezvKvQQmsFkOdrZfYxUxkyxl8mgFQeT259Ajj9PXdbg9VzBCWrItOev72JyWxkCD5VSSqAeHmlN3tWx4DlmsA=="], "prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="], @@ -3683,6 +3740,8 @@ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + "quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="], + "radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="], "randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="], @@ -3721,6 +3780,8 @@ "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + "real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="], + "recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="], "recma-jsx": ["recma-jsx@1.0.1", "", { "dependencies": { "acorn-jsx": "^5.0.0", "estree-util-to-js": "^2.0.0", "recma-parse": "^1.0.0", "recma-stringify": "^1.0.0", "unified": "^11.0.0" }, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w=="], @@ -3791,6 +3852,8 @@ "restructure": ["restructure@3.0.2", "", {}, "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw=="], + "ret": ["ret@0.5.0", "", {}, "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw=="], + "retext": ["retext@9.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "retext-latin": "^4.0.0", "retext-stringify": "^4.0.0", "unified": "^11.0.0" } }, "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA=="], "retext-latin": ["retext-latin@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "parse-latin": "^7.0.0", "unified": "^11.0.0" } }, "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA=="], @@ -3803,6 +3866,8 @@ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], + "rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="], "rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="], @@ -3825,6 +3890,10 @@ "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + "safe-regex2": ["safe-regex2@5.0.0", "", { "dependencies": { "ret": "~0.5.0" } }, "sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw=="], + + "safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="], + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], "sax": ["sax@1.2.1", "", {}, "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="], @@ -3833,6 +3902,8 @@ "section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="], + "secure-json-parse": ["secure-json-parse@4.1.0", "", {}, "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA=="], + "selderee": ["selderee@0.11.0", "", { "dependencies": { "parseley": "^0.12.0" } }, "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA=="], "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], @@ -3849,6 +3920,8 @@ "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], @@ -3913,6 +3986,8 @@ "solid-use": ["solid-use@0.9.1", "", { "peerDependencies": { "solid-js": "^1.7" } }, "sha512-UwvXDVPlrrbj/9ewG9ys5uL2IO4jSiwys2KPzK4zsnAcmEl7iDafZWW1Mo4BSEWOmQCGK6IvpmGHo1aou8iOFw=="], + "sonic-boom": ["sonic-boom@4.2.0", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww=="], + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], @@ -3929,6 +4004,8 @@ "spdx-license-ids": ["spdx-license-ids@3.0.22", "", {}, "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ=="], + "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], "sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="], @@ -4041,6 +4118,8 @@ "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], + "thread-stream": ["thread-stream@4.0.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA=="], + "three": ["three@0.177.0", "", {}, "sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg=="], "thunky": ["thunky@1.1.0", "", {}, "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="], @@ -4781,6 +4860,8 @@ "lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "light-my-request/process-warning": ["process-warning@4.0.1", "", {}, "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q=="], + "lightningcss/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], "log-symbols/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -4871,6 +4952,8 @@ "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], + "proxy-addr/ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + "read-cache/pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="], "readable-stream/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], diff --git a/package.json b/package.json index 951b3ffcfa4..c89e9820450 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,8 @@ "packageManager": "bun@1.3.5", "scripts": { "dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts", + "dev:desktop": "bun --cwd packages/desktop tauri dev", + "dev:web": "bun --cwd packages/app dev", "typecheck": "bun turbo typecheck", "prepare": "husky", "random": "echo 'Random script'", @@ -18,8 +20,8 @@ "packages/*", "packages/console/*", "packages/sdk/js", - "packages/slack", - "script/upstream" + "script/upstream", + "packages/slack" ], "catalog": { "@types/bun": "1.3.5", @@ -75,8 +77,8 @@ "dependencies": { "@aws-sdk/client-s3": "3.933.0", "@kilocode/plugin": "workspace:*", - "@kilocode/sdk": "workspace:*", "@opencode-ai/script": "workspace:*", + "@kilocode/sdk": "workspace:*", "typescript": "catalog:" }, "repository": { @@ -102,5 +104,6 @@ "patchedDependencies": { "ghostty-web@0.3.0": "patches/ghostty-web@0.3.0.patch" }, - "version": "1.0.14" + "version": "1.0.14", + "peerDependencies": {} } diff --git a/packages/app/e2e/actions.ts b/packages/app/e2e/actions.ts index 5f80d67c24d..3467effa6b3 100644 --- a/packages/app/e2e/actions.ts +++ b/packages/app/e2e/actions.ts @@ -21,7 +21,12 @@ import { import type { createSdk } from "./utils" export async function defocus(page: Page) { - await page.mouse.click(5, 5) + await page + .evaluate(() => { + const el = document.activeElement + if (el instanceof HTMLElement) el.blur() + }) + .catch(() => undefined) } export async function openPalette(page: Page) { @@ -68,14 +73,50 @@ export async function toggleSidebar(page: Page) { export async function openSidebar(page: Page) { if (!(await isSidebarClosed(page))) return + + const button = page.getByRole("button", { name: /toggle sidebar/i }).first() + const visible = await button + .isVisible() + .then((x) => x) + .catch(() => false) + + if (visible) await button.click() + if (!visible) await toggleSidebar(page) + + const main = page.locator("main") + const opened = await expect(main) + .not.toHaveClass(/xl:border-l/, { timeout: 1500 }) + .then(() => true) + .catch(() => false) + + if (opened) return + await toggleSidebar(page) - await expect(page.locator("main")).not.toHaveClass(/xl:border-l/) + await expect(main).not.toHaveClass(/xl:border-l/) } export async function closeSidebar(page: Page) { if (await isSidebarClosed(page)) return + + const button = page.getByRole("button", { name: /toggle sidebar/i }).first() + const visible = await button + .isVisible() + .then((x) => x) + .catch(() => false) + + if (visible) await button.click() + if (!visible) await toggleSidebar(page) + + const main = page.locator("main") + const closed = await expect(main) + .toHaveClass(/xl:border-l/, { timeout: 1500 }) + .then(() => true) + .catch(() => false) + + if (closed) return + await toggleSidebar(page) - await expect(page.locator("main")).toHaveClass(/xl:border-l/) + await expect(main).toHaveClass(/xl:border-l/) } export async function openSettings(page: Page) { @@ -182,13 +223,30 @@ export async function hoverSessionItem(page: Page, sessionID: string) { } export async function openSessionMoreMenu(page: Page, sessionID: string) { - const sessionEl = await hoverSessionItem(page, sessionID) + await expect(page).toHaveURL(new RegExp(`/session/${sessionID}(?:[/?#]|$)`)) - const menuTrigger = sessionEl.locator(dropdownMenuTriggerSelector).first() + const scroller = page.locator(".session-scroller").first() + await expect(scroller).toBeVisible() + await expect(scroller.getByRole("heading", { level: 1 }).first()).toBeVisible({ timeout: 30_000 }) + + const menu = page + .locator(dropdownMenuContentSelector) + .filter({ has: page.getByRole("menuitem", { name: /rename/i }) }) + .filter({ has: page.getByRole("menuitem", { name: /archive/i }) }) + .filter({ has: page.getByRole("menuitem", { name: /delete/i }) }) + .first() + + const opened = await menu + .isVisible() + .then((x) => x) + .catch(() => false) + + if (opened) return menu + + const menuTrigger = scroller.getByRole("button", { name: /more options/i }).first() await expect(menuTrigger).toBeVisible() await menuTrigger.click() - const menu = page.locator(dropdownMenuContentSelector).first() await expect(menu).toBeVisible() return menu } diff --git a/packages/app/e2e/session/session.spec.ts b/packages/app/e2e/session/session.spec.ts index 05984bbeee4..4610fb33152 100644 --- a/packages/app/e2e/session/session.spec.ts +++ b/packages/app/e2e/session/session.spec.ts @@ -11,57 +11,98 @@ import { sessionItemSelector, inlineInputSelector } from "../selectors" const shareDisabled = process.env.OPENCODE_DISABLE_SHARE === "true" || process.env.OPENCODE_DISABLE_SHARE === "1" -test("sidebar session can be renamed", async ({ page, sdk, gotoSession }) => { +type Sdk = Parameters[0] + +async function seedMessage(sdk: Sdk, sessionID: string) { + await sdk.session.promptAsync({ + sessionID, + noReply: true, + parts: [{ type: "text", text: "e2e seed" }], + }) + + await expect + .poll( + async () => { + const messages = await sdk.session.messages({ sessionID, limit: 1 }).then((r) => r.data ?? []) + return messages.length + }, + { timeout: 30_000 }, + ) + .toBeGreaterThan(0) +} + +test("session can be renamed via header menu", async ({ page, sdk, gotoSession }) => { const stamp = Date.now() const originalTitle = `e2e rename test ${stamp}` const newTitle = `e2e renamed ${stamp}` await withSession(sdk, originalTitle, async (session) => { + await seedMessage(sdk, session.id) await gotoSession(session.id) - await openSidebar(page) const menu = await openSessionMoreMenu(page, session.id) await clickMenuItem(menu, /rename/i) - const input = page.locator(sessionItemSelector(session.id)).locator(inlineInputSelector).first() + const input = page.locator(".session-scroller").locator(inlineInputSelector).first() await expect(input).toBeVisible() await input.fill(newTitle) await input.press("Enter") - await expect(page.locator(sessionItemSelector(session.id)).locator("a").first()).toContainText(newTitle) + await expect(page.getByRole("heading", { level: 1 }).first()).toContainText(newTitle) }) }) -test("sidebar session can be archived", async ({ page, sdk, gotoSession }) => { +test("session can be archived via header menu", async ({ page, sdk, gotoSession }) => { const stamp = Date.now() const title = `e2e archive test ${stamp}` await withSession(sdk, title, async (session) => { + await seedMessage(sdk, session.id) await gotoSession(session.id) - await openSidebar(page) - - const sessionEl = page.locator(sessionItemSelector(session.id)) const menu = await openSessionMoreMenu(page, session.id) await clickMenuItem(menu, /archive/i) - await expect(sessionEl).not.toBeVisible() + await expect + .poll( + async () => { + const data = await sdk.session.get({ sessionID: session.id }).then((r) => r.data) + return data?.time?.archived + }, + { timeout: 30_000 }, + ) + .not.toBeUndefined() + + await openSidebar(page) + await expect(page.locator(sessionItemSelector(session.id))).toHaveCount(0) }) }) -test("sidebar session can be deleted", async ({ page, sdk, gotoSession }) => { +test("session can be deleted via header menu", async ({ page, sdk, gotoSession }) => { const stamp = Date.now() const title = `e2e delete test ${stamp}` await withSession(sdk, title, async (session) => { + await seedMessage(sdk, session.id) await gotoSession(session.id) - await openSidebar(page) - - const sessionEl = page.locator(sessionItemSelector(session.id)) const menu = await openSessionMoreMenu(page, session.id) await clickMenuItem(menu, /delete/i) await confirmDialog(page, /delete/i) - await expect(sessionEl).not.toBeVisible() + await expect + .poll( + async () => { + const data = await sdk.session + .get({ sessionID: session.id }) + .then((r) => r.data) + .catch(() => undefined) + return data?.id + }, + { timeout: 30_000 }, + ) + .toBeUndefined() + + await openSidebar(page) + await expect(page.locator(sessionItemSelector(session.id))).toHaveCount(0) }) }) @@ -72,6 +113,7 @@ test("session can be shared and unshared via header button", async ({ page, sdk, const title = `e2e share test ${stamp}` await withSession(sdk, title, async (session) => { + await seedMessage(sdk, session.id) await gotoSession(session.id) const { rightSection, popoverBody } = await openSharePopover(page) diff --git a/packages/app/e2e/status/status-popover.spec.ts b/packages/app/e2e/status/status-popover.spec.ts index 4334cecb44d..d53578a4910 100644 --- a/packages/app/e2e/status/status-popover.spec.ts +++ b/packages/app/e2e/status/status-popover.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from "../fixtures" -import { openStatusPopover, defocus } from "../actions" +import { openStatusPopover } from "../actions" test("status popover opens and shows tabs", async ({ page, gotoSession }) => { await gotoSession() @@ -88,7 +88,7 @@ test("status popover closes when clicking outside", async ({ page, gotoSession } const { popoverBody } = await openStatusPopover(page) await expect(popoverBody).toBeVisible() - await defocus(page) + await page.getByRole("main").click({ position: { x: 5, y: 5 } }) await expect(popoverBody).toHaveCount(0) }) diff --git a/packages/app/package.json b/packages/app/package.json index cf4e0623e07..920e03b31a1 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -66,5 +66,6 @@ "virtua": "catalog:", "zod": "catalog:", "@kilocode/kilo-i18n": "workspace:*" - } + }, + "peerDependencies": {} } diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 76abc18e574..c25a845c690 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -1220,7 +1220,10 @@ export const PromptInput: Component = (props) => { }) return undefined }) - if (session) navigate(`/${base64Encode(sessionDirectory)}/session/${session.id}`) + if (session) { + layout.handoff.setTabs(base64Encode(sessionDirectory), session.id) + navigate(`/${base64Encode(sessionDirectory)}/session/${session.id}`) + } } if (!session) return diff --git a/packages/app/src/components/terminal.tsx b/packages/app/src/components/terminal.tsx index d388448024b..e87670f59bf 100644 --- a/packages/app/src/components/terminal.tsx +++ b/packages/app/src/components/terminal.tsx @@ -1,5 +1,6 @@ import type { Ghostty, Terminal as Term, FitAddon } from "ghostty-web" import { ComponentProps, createEffect, createSignal, onCleanup, onMount, splitProps } from "solid-js" +import { usePlatform } from "@/context/platform" import { useSDK } from "@/context/sdk" import { monoFontFamily, useSettings } from "@/context/settings" import { SerializeAddon } from "@/addons/serialize" @@ -52,6 +53,7 @@ const DEFAULT_TERMINAL_COLORS: Record<"light" | "dark", TerminalColors> = { } export const Terminal = (props: TerminalProps) => { + const platform = usePlatform() const sdk = useSDK() const settings = useSettings() const theme = useTheme() @@ -135,6 +137,22 @@ export const Terminal = (props: TerminalProps) => { focusTerminal() } + const handleLinkClick = (event: MouseEvent) => { + if (!event.shiftKey && !event.ctrlKey && !event.metaKey) return + if (event.altKey) return + if (event.button !== 0) return + + const t = term + if (!t) return + + const link = (t as unknown as { currentHoveredLink?: { text: string } }).currentHoveredLink + if (!link?.text) return + + event.preventDefault() + event.stopImmediatePropagation() + platform.openLink(link.text) + } + onMount(() => { const run = async () => { const loaded = await loadGhostty() @@ -146,6 +164,7 @@ export const Terminal = (props: TerminalProps) => { const once = { value: false } const url = new URL(sdk.url + `/pty/${local.pty.id}/connect?directory=${encodeURIComponent(sdk.directory)}`) + url.protocol = url.protocol === "https:" ? "wss:" : "ws:" if (window.__OPENCODE__?.serverPassword) { url.username = "opencode" url.password = window.__OPENCODE__?.serverPassword @@ -166,6 +185,7 @@ export const Terminal = (props: TerminalProps) => { fontSize: 14, fontFamily: monoFontFamily(settings.appearance.font()), allowTransparency: true, + convertEol: true, theme: terminalColors(), scrollback: 10_000, ghostty: g, @@ -239,6 +259,9 @@ export const Terminal = (props: TerminalProps) => { container.addEventListener("pointerdown", handlePointerDown) cleanups.push(() => container.removeEventListener("pointerdown", handlePointerDown)) + container.addEventListener("click", handleLinkClick, { capture: true }) + cleanups.push(() => container.removeEventListener("click", handleLinkClick, { capture: true })) + handleTextareaFocus = () => { t.options.cursorBlink = true } diff --git a/packages/app/src/context/comments.tsx b/packages/app/src/context/comments.tsx index f5551458748..d51c1635248 100644 --- a/packages/app/src/context/comments.tsx +++ b/packages/app/src/context/comments.tsx @@ -70,6 +70,14 @@ function createCommentSession(dir: string, id: string | undefined) { setFocus((current) => (current?.id === id ? null : current)) } + const clear = () => { + batch(() => { + setStore("comments", {}) + setFocus(null) + setActive(null) + }) + } + const all = createMemo(() => { const files = Object.keys(store.comments) const items = files.flatMap((file) => store.comments[file] ?? []) @@ -82,6 +90,7 @@ function createCommentSession(dir: string, id: string | undefined) { all, add, remove, + clear, focus: createMemo(() => state.focus), setFocus, clearFocus: () => setFocus(null), @@ -144,6 +153,7 @@ export const { use: useComments, provider: CommentsProvider } = createSimpleCont all: () => session().all(), add: (input: Omit) => session().add(input), remove: (file: string, id: string) => session().remove(file, id), + clear: () => session().clear(), focus: () => session().focus(), setFocus: (focus: CommentFocus | null) => session().setFocus(focus), clearFocus: () => session().clearFocus(), diff --git a/packages/app/src/context/file.tsx b/packages/app/src/context/file.tsx index 6fbbe6a90e5..c5f5ac6fc83 100644 --- a/packages/app/src/context/file.tsx +++ b/packages/app/src/context/file.tsx @@ -1,5 +1,5 @@ -import { createEffect, createMemo, createRoot, onCleanup } from "solid-js" -import { createStore, produce } from "solid-js/store" +import { batch, createEffect, createMemo, createRoot, onCleanup } from "solid-js" +import { createStore, produce, reconcile } from "solid-js/store" import { createSimpleContext } from "@opencode-ai/ui/context" import type { FileContent, FileNode } from "@kilocode/sdk/v2" import { showToast } from "@opencode-ai/ui/toast" @@ -277,10 +277,8 @@ export const { use: useFile, provider: FileProvider } = createSimpleContext({ const scope = createMemo(() => sdk.directory) - const directory = createMemo(() => sync.data.path.directory) - function normalize(input: string) { - const root = directory() + const root = scope() const prefix = root.endsWith("/") ? root : root + "/" let path = unquoteGitPath(stripQueryAndHash(stripFileProtocol(input))) @@ -371,9 +369,13 @@ export const { use: useFile, provider: FileProvider } = createSimpleContext({ inflight.clear() treeInflight.clear() contentLru.clear() - setStore("file", {}) - setTree("node", {}) - setTree("dir", { "": { expanded: true } }) + + batch(() => { + setStore("file", reconcile({})) + setTree("node", reconcile({})) + setTree("dir", reconcile({})) + setTree("dir", "", { expanded: true }) + }) }) const viewCache = new Map() @@ -414,7 +416,7 @@ export const { use: useFile, provider: FileProvider } = createSimpleContext({ return entry.value } - const view = createMemo(() => loadView(params.dir!, params.id)) + const view = createMemo(() => loadView(scope(), params.id)) function ensure(path: string) { if (!path) return diff --git a/packages/app/src/context/language.tsx b/packages/app/src/context/language.tsx index ca30c517a0b..3d6daa768cc 100644 --- a/packages/app/src/context/language.tsx +++ b/packages/app/src/context/language.tsx @@ -18,6 +18,7 @@ import { dict as ar } from "@/i18n/ar" import { dict as no } from "@/i18n/no" import { dict as br } from "@/i18n/br" import { dict as th } from "@/i18n/th" +import { dict as bs } from "@/i18n/bs" import { dict as uiEn } from "@opencode-ai/ui/i18n/en" import { dict as uiZh } from "@opencode-ai/ui/i18n/zh" import { dict as uiZht } from "@opencode-ai/ui/i18n/zht" @@ -33,6 +34,8 @@ import { dict as uiAr } from "@opencode-ai/ui/i18n/ar" import { dict as uiNo } from "@opencode-ai/ui/i18n/no" import { dict as uiBr } from "@opencode-ai/ui/i18n/br" import { dict as uiTh } from "@opencode-ai/ui/i18n/th" +import { dict as uiBs } from "@opencode-ai/ui/i18n/bs" + // kilocode_change start import { dict as kiloEn } from "@kilocode/kilo-i18n/en" import { dict as kiloZh } from "@kilocode/kilo-i18n/zh" @@ -49,6 +52,7 @@ import { dict as kiloAr } from "@kilocode/kilo-i18n/ar" import { dict as kiloNo } from "@kilocode/kilo-i18n/no" import { dict as kiloBr } from "@kilocode/kilo-i18n/br" import { dict as kiloTh } from "@kilocode/kilo-i18n/th" +import { dict as kiloBs } from "@kilocode/kilo-i18n/bs" // kilocode_change end export type Locale = @@ -67,6 +71,7 @@ export type Locale = | "no" | "br" | "th" + | "bs" type RawDictionary = typeof en & typeof uiEn & typeof kiloEn // kilocode_change type Dictionary = i18n.Flatten @@ -83,6 +88,7 @@ const LOCALES: readonly Locale[] = [ "ja", "pl", "ru", + "bs", "ar", "no", "br", @@ -116,6 +122,7 @@ function detectLocale(): Locale { return "no" if (language.toLowerCase().startsWith("pt")) return "br" if (language.toLowerCase().startsWith("th")) return "th" + if (language.toLowerCase().startsWith("bs")) return "bs" } return "en" @@ -146,6 +153,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont if (store.locale === "no") return "no" if (store.locale === "br") return "br" if (store.locale === "th") return "th" + if (store.locale === "bs") return "bs" return "en" }) @@ -172,6 +180,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont if (locale() === "no") return { ...base, ...i18n.flatten({ ...no, ...uiNo, ...kiloNo }) } if (locale() === "br") return { ...base, ...i18n.flatten({ ...br, ...uiBr, ...kiloBr }) } if (locale() === "th") return { ...base, ...i18n.flatten({ ...th, ...uiTh, ...kiloTh }) } + if (locale() === "bs") return { ...base, ...i18n.flatten({ ...bs, ...uiBs, ...kiloBs }) } return { ...base, ...i18n.flatten({ ...ko, ...uiKo, ...kiloKo }) } }) // kilocode_change end @@ -194,6 +203,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont no: "language.no", br: "language.br", th: "language.th", + bs: "language.bs", } const label = (value: Locale) => t(labelKey[value]) diff --git a/packages/app/src/context/layout.tsx b/packages/app/src/context/layout.tsx index dadc3bacfa2..87d2feca22b 100644 --- a/packages/app/src/context/layout.tsx +++ b/packages/app/src/context/layout.tsx @@ -33,6 +33,14 @@ type SessionTabs = { type SessionView = { scroll: Record reviewOpen?: string[] + pendingMessage?: string + pendingMessageAt?: number +} + +type TabHandoff = { + dir: string + id: string + at: number } export type LocalProject = Partial & { worktree: string; expanded: boolean } @@ -115,10 +123,14 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( }, sessionTabs: {} as Record, sessionView: {} as Record, + handoff: { + tabs: undefined as TabHandoff | undefined, + }, }), ) const MAX_SESSION_KEYS = 50 + const PENDING_MESSAGE_TTL_MS = 2 * 60 * 1000 const meta = { active: undefined as string | undefined, pruned: false } const used = new Map() @@ -411,6 +423,16 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( return { ready, + handoff: { + tabs: createMemo(() => store.handoff?.tabs), + setTabs(dir: string, id: string) { + setStore("handoff", "tabs", { dir, id, at: Date.now() }) + }, + clearTabs() { + if (!store.handoff?.tabs) return + setStore("handoff", "tabs", undefined) + }, + }, projects: { list, open(directory: string) { @@ -536,6 +558,49 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( setStore("mobileSidebar", "opened", (x) => !x) }, }, + pendingMessage: { + set(sessionKey: string, messageID: string) { + const at = Date.now() + touch(sessionKey) + const current = store.sessionView[sessionKey] + if (!current) { + setStore("sessionView", sessionKey, { + scroll: {}, + pendingMessage: messageID, + pendingMessageAt: at, + }) + prune(meta.active ?? sessionKey) + return + } + + setStore( + "sessionView", + sessionKey, + produce((draft) => { + draft.pendingMessage = messageID + draft.pendingMessageAt = at + }), + ) + }, + consume(sessionKey: string) { + const current = store.sessionView[sessionKey] + const message = current?.pendingMessage + const at = current?.pendingMessageAt + if (!message || !at) return + + setStore( + "sessionView", + sessionKey, + produce((draft) => { + delete draft.pendingMessage + delete draft.pendingMessageAt + }), + ) + + if (Date.now() - at > PENDING_MESSAGE_TTL_MS) return + return message + }, + }, view(sessionKey: string | Accessor) { const key = typeof sessionKey === "function" ? sessionKey : () => sessionKey diff --git a/packages/app/src/context/sdk.tsx b/packages/app/src/context/sdk.tsx index 9705605b5bf..e8e43b18a53 100644 --- a/packages/app/src/context/sdk.tsx +++ b/packages/app/src/context/sdk.tsx @@ -1,17 +1,17 @@ import { createOpencodeClient, type Event } from "@kilocode/sdk/v2/client" import { createSimpleContext } from "@opencode-ai/ui/context" import { createGlobalEmitter } from "@solid-primitives/event-bus" -import { createEffect, createMemo, onCleanup } from "solid-js" +import { createEffect, createMemo, onCleanup, type Accessor } from "solid-js" import { useGlobalSDK } from "./global-sdk" import { usePlatform } from "./platform" export const { use: useSDK, provider: SDKProvider } = createSimpleContext({ name: "SDK", - init: (props: { directory: string }) => { + init: (props: { directory: Accessor }) => { const platform = usePlatform() const globalSDK = useGlobalSDK() - const directory = createMemo(() => props.directory) + const directory = createMemo(props.directory) const client = createMemo(() => createOpencodeClient({ baseUrl: globalSDK.url, diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index d9ca8aa860c..6be69520686 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -321,22 +321,6 @@ export const dict = { "context.usage.clickToView": "انقر لعرض السياق", "context.usage.view": "عرض استخدام السياق", - "language.en": "English", - "language.zh": "简体中文", - "language.zht": "繁體中文", - "language.ko": "한국어", - "language.de": "Deutsch", - "language.es": "Español", - "language.fr": "Français", - "language.da": "Dansk", - "language.ja": "日本語", - "language.pl": "Polski", - "language.ru": "Русский", - "language.ar": "العربية", - "language.no": "Norsk", - "language.br": "Português (Brasil)", - "language.th": "ไทย", - "toast.language.title": "لغة", "toast.language.description": "تم التبديل إلى {{language}}", diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts index 2c40eddfa32..75215c71c2c 100644 --- a/packages/app/src/i18n/br.ts +++ b/packages/app/src/i18n/br.ts @@ -320,22 +320,6 @@ export const dict = { "context.usage.clickToView": "Clique para ver o contexto", "context.usage.view": "Ver uso do contexto", - "language.en": "English", - "language.zh": "简体中文", - "language.zht": "繁體中文", - "language.ko": "한국어", - "language.de": "Deutsch", - "language.es": "Español", - "language.fr": "Français", - "language.da": "Dansk", - "language.ja": "日本語", - "language.pl": "Polski", - "language.ru": "Русский", - "language.ar": "العربية", - "language.no": "Norsk", - "language.br": "Português (Brasil)", - "language.th": "ไทย", - "toast.language.title": "Idioma", "toast.language.description": "Alterado para {{language}}", diff --git a/packages/app/src/i18n/bs.ts b/packages/app/src/i18n/bs.ts new file mode 100644 index 00000000000..a48c60e969c --- /dev/null +++ b/packages/app/src/i18n/bs.ts @@ -0,0 +1,747 @@ +export const dict = { + "command.category.suggested": "Predloženo", + "command.category.view": "Prikaz", + "command.category.project": "Projekat", + "command.category.provider": "Provajder", + "command.category.server": "Server", + "command.category.session": "Sesija", + "command.category.theme": "Tema", + "command.category.language": "Jezik", + "command.category.file": "Datoteka", + "command.category.context": "Kontekst", + "command.category.terminal": "Terminal", + "command.category.model": "Model", + "command.category.mcp": "MCP", + "command.category.agent": "Agent", + "command.category.permissions": "Dozvole", + "command.category.workspace": "Radni prostor", + "command.category.settings": "Postavke", + + "theme.scheme.system": "Sistem", + "theme.scheme.light": "Svijetlo", + "theme.scheme.dark": "Tamno", + + "command.sidebar.toggle": "Prikaži/sakrij bočnu traku", + "command.project.open": "Otvori projekat", + "command.provider.connect": "Poveži provajdera", + "command.server.switch": "Promijeni server", + "command.settings.open": "Otvori postavke", + "command.session.previous": "Prethodna sesija", + "command.session.next": "Sljedeća sesija", + "command.session.previous.unseen": "Prethodna nepročitana sesija", + "command.session.next.unseen": "Sljedeća nepročitana sesija", + "command.session.archive": "Arhiviraj sesiju", + + "command.palette": "Paleta komandi", + + "command.theme.cycle": "Promijeni temu", + "command.theme.set": "Koristi temu: {{theme}}", + "command.theme.scheme.cycle": "Promijeni šemu boja", + "command.theme.scheme.set": "Koristi šemu boja: {{scheme}}", + + "command.language.cycle": "Promijeni jezik", + "command.language.set": "Koristi jezik: {{language}}", + + "command.session.new": "Nova sesija", + "command.file.open": "Otvori datoteku", + "command.tab.close": "Zatvori karticu", + "command.context.addSelection": "Dodaj odabir u kontekst", + "command.context.addSelection.description": "Dodaj odabrane linije iz trenutne datoteke", + "command.terminal.toggle": "Prikaži/sakrij terminal", + "command.fileTree.toggle": "Prikaži/sakrij stablo datoteka", + "command.review.toggle": "Prikaži/sakrij pregled", + "command.terminal.new": "Novi terminal", + "command.terminal.new.description": "Kreiraj novu karticu terminala", + "command.steps.toggle": "Prikaži/sakrij korake", + "command.steps.toggle.description": "Prikaži ili sakrij korake za trenutnu poruku", + "command.message.previous": "Prethodna poruka", + "command.message.previous.description": "Idi na prethodnu korisničku poruku", + "command.message.next": "Sljedeća poruka", + "command.message.next.description": "Idi na sljedeću korisničku poruku", + "command.model.choose": "Odaberi model", + "command.model.choose.description": "Odaberi drugi model", + "command.mcp.toggle": "Prikaži/sakrij MCP-ove", + "command.mcp.toggle.description": "Prikaži/sakrij MCP-ove", + "command.agent.cycle": "Promijeni agenta", + "command.agent.cycle.description": "Prebaci na sljedećeg agenta", + "command.agent.cycle.reverse": "Promijeni agenta unazad", + "command.agent.cycle.reverse.description": "Prebaci na prethodnog agenta", + "command.model.variant.cycle": "Promijeni nivo razmišljanja", + "command.model.variant.cycle.description": "Prebaci na sljedeći nivo", + "command.permissions.autoaccept.enable": "Automatski prihvataj izmjene", + "command.permissions.autoaccept.disable": "Zaustavi automatsko prihvatanje izmjena", + "command.workspace.toggle": "Prikaži/sakrij radne prostore", + "command.workspace.toggle.description": "Omogući ili onemogući više radnih prostora u bočnoj traci", + "command.session.undo": "Poništi", + "command.session.undo.description": "Poništi posljednju poruku", + "command.session.redo": "Vrati", + "command.session.redo.description": "Vrati posljednju poništenu poruku", + "command.session.compact": "Sažmi sesiju", + "command.session.compact.description": "Sažmi sesiju kako bi se smanjio kontekst", + "command.session.fork": "Fork iz poruke", + "command.session.fork.description": "Kreiraj novu sesiju iz prethodne poruke", + "command.session.share": "Podijeli sesiju", + "command.session.share.description": "Podijeli ovu sesiju i kopiraj URL u međuspremnik", + "command.session.unshare": "Ukini dijeljenje sesije", + "command.session.unshare.description": "Zaustavi dijeljenje ove sesije", + + "palette.search.placeholder": "Pretraži datoteke, komande i sesije", + "palette.empty": "Nema rezultata", + "palette.group.commands": "Komande", + "palette.group.files": "Datoteke", + + "dialog.provider.search.placeholder": "Pretraži provajdere", + "dialog.provider.empty": "Nema pronađenih provajdera", + "dialog.provider.group.popular": "Popularno", + "dialog.provider.group.other": "Ostalo", + "dialog.provider.tag.recommended": "Preporučeno", + "dialog.provider.opencode.note": "Kurirani modeli uključujući Claude, GPT, Gemini i druge", + "dialog.provider.anthropic.note": "Direktan pristup Claude modelima, uključujući Pro i Max", + "dialog.provider.copilot.note": "Claude modeli za pomoć pri kodiranju", + "dialog.provider.openai.note": "GPT modeli za brze, sposobne opšte AI zadatke", + "dialog.provider.google.note": "Gemini modeli za brze, strukturirane odgovore", + "dialog.provider.openrouter.note": "Pristup svim podržanim modelima preko jednog provajdera", + "dialog.provider.vercel.note": "Jedinstven pristup AI modelima uz pametno rutiranje", + + "dialog.model.select.title": "Odaberi model", + "dialog.model.search.placeholder": "Pretraži modele", + "dialog.model.empty": "Nema rezultata za modele", + "dialog.model.manage": "Upravljaj modelima", + "dialog.model.manage.description": "Prilagodi koji se modeli prikazuju u izborniku modela.", + + "dialog.model.unpaid.freeModels.title": "Besplatni modeli koje obezbjeđuje Kilo", + "dialog.model.unpaid.addMore.title": "Dodaj još modela od popularnih provajdera", + + "dialog.provider.viewAll": "Prikaži više provajdera", + + "provider.connect.title": "Poveži {{provider}}", + "provider.connect.title.anthropicProMax": "Prijavi se putem Claude Pro/Max", + "provider.connect.selectMethod": "Odaberi način prijave za {{provider}}.", + "provider.connect.method.apiKey": "API ključ", + "provider.connect.status.inProgress": "Autorizacija je u toku...", + "provider.connect.status.waiting": "Čekanje na autorizaciju...", + "provider.connect.status.failed": "Autorizacija nije uspjela: {{error}}", + "provider.connect.apiKey.description": + "Unesi svoj {{provider}} API ključ da povežeš račun i koristiš {{provider}} modele u Kilo-u.", + "provider.connect.apiKey.label": "{{provider}} API ključ", + "provider.connect.apiKey.placeholder": "API ključ", + "provider.connect.apiKey.required": "API ključ je obavezan", + "provider.connect.opencodeZen.line1": + "OpenCode Zen ti daje pristup kuriranom skupu pouzdanih, optimizovanih modela za coding agente.", + "provider.connect.opencodeZen.line2": + "Sa jednim API ključem dobijaš pristup modelima kao što su Claude, GPT, Gemini, GLM i drugi.", + "provider.connect.opencodeZen.visit.prefix": "Posjeti ", + "provider.connect.opencodeZen.visit.link": "opencode.ai/zen", + "provider.connect.opencodeZen.visit.suffix": " da preuzmeš svoj API ključ.", + "provider.connect.oauth.code.visit.prefix": "Posjeti ", + "provider.connect.oauth.code.visit.link": "ovaj link", + "provider.connect.oauth.code.visit.suffix": + " da preuzmeš autorizacijski kod i povežeš račun te koristiš {{provider}} modele u Kilo-u.", + "provider.connect.oauth.code.label": "{{method}} autorizacijski kod", + "provider.connect.oauth.code.placeholder": "Autorizacijski kod", + "provider.connect.oauth.code.required": "Autorizacijski kod je obavezan", + "provider.connect.oauth.code.invalid": "Nevažeći autorizacijski kod", + "provider.connect.oauth.auto.visit.prefix": "Posjeti ", + "provider.connect.oauth.auto.visit.link": "ovaj link", + "provider.connect.oauth.auto.visit.suffix": + " i unesi kod ispod da povežeš račun i koristiš {{provider}} modele u Kilo-u.", + "provider.connect.oauth.auto.confirmationCode": "Kod za potvrdu", + "provider.connect.toast.connected.title": "{{provider}} povezan", + "provider.connect.toast.connected.description": "{{provider}} modeli su sada dostupni za korištenje.", + + "provider.disconnect.toast.disconnected.title": "{{provider}} odspojen", + "provider.disconnect.toast.disconnected.description": "{{provider}} modeli više nisu dostupni.", + + "model.tag.free": "Besplatno", + "model.tag.latest": "Najnovije", + "model.provider.anthropic": "Anthropic", + "model.provider.openai": "OpenAI", + "model.provider.google": "Google", + "model.provider.xai": "xAI", + "model.provider.meta": "Meta", + "model.input.text": "tekst", + "model.input.image": "slika", + "model.input.audio": "zvuk", + "model.input.video": "video", + "model.input.pdf": "pdf", + "model.tooltip.allows": "Podržava: {{inputs}}", + "model.tooltip.reasoning.allowed": "Podržava rasuđivanje", + "model.tooltip.reasoning.none": "Bez rasuđivanja", + "model.tooltip.context": "Limit konteksta {{limit}}", + + "common.search.placeholder": "Pretraži", + "common.goBack": "Nazad", + "common.goForward": "Naprijed", + "common.loading": "Učitavanje", + "common.loading.ellipsis": "...", + "common.cancel": "Otkaži", + "common.connect": "Poveži", + "common.disconnect": "Prekini vezu", + "common.submit": "Pošalji", + "common.save": "Sačuvaj", + "common.saving": "Čuvanje...", + "common.default": "Podrazumijevano", + "common.attachment": "prilog", + + "prompt.placeholder.shell": "Unesi shell naredbu...", + "prompt.placeholder.normal": 'Pitaj bilo šta... "{{example}}"', + "prompt.placeholder.summarizeComments": "Sažmi komentare…", + "prompt.placeholder.summarizeComment": "Sažmi komentar…", + "prompt.mode.shell": "Shell", + "prompt.mode.shell.exit": "esc za izlaz", + + "prompt.example.1": "Popravi TODO u bazi koda", + "prompt.example.2": "Koji je tehnološki stack ovog projekta?", + "prompt.example.3": "Popravi pokvarene testove", + "prompt.example.4": "Objasni kako radi autentifikacija", + "prompt.example.5": "Pronađi i popravi sigurnosne ranjivosti", + "prompt.example.6": "Dodaj jedinične testove za servis korisnika", + "prompt.example.7": "Refaktoriši ovu funkciju da bude čitljivija", + "prompt.example.8": "Šta znači ova greška?", + "prompt.example.9": "Pomozi mi da otklonim ovu grešku", + "prompt.example.10": "Generiši API dokumentaciju", + "prompt.example.11": "Optimizuj upite prema bazi podataka", + "prompt.example.12": "Dodaj validaciju ulaza", + "prompt.example.13": "Napravi novu komponentu za...", + "prompt.example.14": "Kako da deployam ovaj projekat?", + "prompt.example.15": "Pregledaj moj kod prema najboljim praksama", + "prompt.example.16": "Dodaj obradu grešaka u ovu funkciju", + "prompt.example.17": "Objasni ovaj regex obrazac", + "prompt.example.18": "Pretvori ovo u TypeScript", + "prompt.example.19": "Dodaj logovanje kroz cijelu bazu koda", + "prompt.example.20": "Koje su zavisnosti zastarjele?", + "prompt.example.21": "Pomozi mi da napišem migracijsku skriptu", + "prompt.example.22": "Implementiraj keširanje za ovaj endpoint", + "prompt.example.23": "Dodaj paginaciju u ovu listu", + "prompt.example.24": "Napravi CLI komandu za...", + "prompt.example.25": "Kako ovdje rade varijable okruženja?", + + "prompt.popover.emptyResults": "Nema rezultata", + "prompt.popover.emptyCommands": "Nema komandi", + "prompt.dropzone.label": "Spusti slike ili PDF-ove ovdje", + "prompt.slash.badge.custom": "prilagođeno", + "prompt.slash.badge.skill": "skill", + "prompt.slash.badge.mcp": "mcp", + "prompt.context.active": "aktivno", + "prompt.context.includeActiveFile": "Uključi aktivnu datoteku", + "prompt.context.removeActiveFile": "Ukloni aktivnu datoteku iz konteksta", + "prompt.context.removeFile": "Ukloni datoteku iz konteksta", + "prompt.action.attachFile": "Priloži datoteku", + "prompt.attachment.remove": "Ukloni prilog", + "prompt.action.send": "Pošalji", + "prompt.action.stop": "Zaustavi", + + "prompt.toast.pasteUnsupported.title": "Nepodržano lijepljenje", + "prompt.toast.pasteUnsupported.description": "Ovdje se mogu zalijepiti samo slike ili PDF-ovi.", + "prompt.toast.modelAgentRequired.title": "Odaberi agenta i model", + "prompt.toast.modelAgentRequired.description": "Odaberi agenta i model prije slanja upita.", + "prompt.toast.worktreeCreateFailed.title": "Neuspješno kreiranje worktree-a", + "prompt.toast.sessionCreateFailed.title": "Neuspješno kreiranje sesije", + "prompt.toast.shellSendFailed.title": "Neuspješno slanje shell naredbe", + "prompt.toast.commandSendFailed.title": "Neuspješno slanje komande", + "prompt.toast.promptSendFailed.title": "Neuspješno slanje upita", + + "dialog.mcp.title": "MCP-ovi", + "dialog.mcp.description": "{{enabled}} od {{total}} omogućeno", + "dialog.mcp.empty": "Nema konfigurisnih MCP-ova", + + "dialog.lsp.empty": "LSP-ovi se automatski otkrivaju prema tipu datoteke", + "dialog.plugins.empty": "Plugini su konfigurisani u opencode.json", + + "mcp.status.connected": "povezano", + "mcp.status.failed": "neuspjelo", + "mcp.status.needs_auth": "potrebna autentifikacija", + "mcp.status.disabled": "onemogućeno", + + "dialog.fork.empty": "Nema poruka za fork", + + "dialog.directory.search.placeholder": "Pretraži foldere", + "dialog.directory.empty": "Nema pronađenih foldera", + + "dialog.server.title": "Serveri", + "dialog.server.description": "Promijeni na koji se Kilo server ova aplikacija povezuje.", + "dialog.server.search.placeholder": "Pretraži servere", + "dialog.server.empty": "Još nema servera", + "dialog.server.add.title": "Dodaj server", + "dialog.server.add.url": "URL servera", + "dialog.server.add.placeholder": "http://localhost:4096", + "dialog.server.add.error": "Nije moguće povezati se na server", + "dialog.server.add.checking": "Provjera...", + "dialog.server.add.button": "Dodaj server", + "dialog.server.default.title": "Podrazumijevani server", + "dialog.server.default.description": + "Poveži se na ovaj server pri pokretanju aplikacije umjesto pokretanja lokalnog servera. Potreban je restart.", + "dialog.server.default.none": "Nije odabran server", + "dialog.server.default.set": "Postavi trenutni server kao podrazumijevani", + "dialog.server.default.clear": "Očisti", + "dialog.server.action.remove": "Ukloni server", + + "dialog.server.menu.edit": "Uredi", + "dialog.server.menu.default": "Postavi kao podrazumijevano", + "dialog.server.menu.defaultRemove": "Ukloni podrazumijevano", + "dialog.server.menu.delete": "Izbriši", + "dialog.server.current": "Trenutni server", + "dialog.server.status.default": "Podrazumijevano", + + "dialog.project.edit.title": "Uredi projekat", + "dialog.project.edit.name": "Naziv", + "dialog.project.edit.icon": "Ikonica", + "dialog.project.edit.icon.alt": "Ikonica projekta", + "dialog.project.edit.icon.hint": "Klikni ili prevuci sliku", + "dialog.project.edit.icon.recommended": "Preporučeno: 128x128px", + "dialog.project.edit.color": "Boja", + "dialog.project.edit.color.select": "Odaberi boju {{color}}", + "dialog.project.edit.worktree.startup": "Skripta za pokretanje radnog prostora", + "dialog.project.edit.worktree.startup.description": "Pokreće se nakon kreiranja novog radnog prostora (worktree).", + "dialog.project.edit.worktree.startup.placeholder": "npr. bun install", + + "context.breakdown.title": "Razlaganje konteksta", + "context.breakdown.note": + 'Približna raspodjela ulaznih tokena. "Ostalo" uključuje definicije alata i dodatni overhead.', + "context.breakdown.system": "Sistem", + "context.breakdown.user": "Korisnik", + "context.breakdown.assistant": "Asistent", + "context.breakdown.tool": "Pozivi alata", + "context.breakdown.other": "Ostalo", + + "context.systemPrompt.title": "Sistemski prompt", + "context.rawMessages.title": "Sirove poruke", + + "context.stats.session": "Sesija", + "context.stats.messages": "Poruke", + "context.stats.provider": "Provajder", + "context.stats.model": "Model", + "context.stats.limit": "Limit konteksta", + "context.stats.totalTokens": "Ukupno tokena", + "context.stats.usage": "Korištenje", + "context.stats.inputTokens": "Ulazni tokeni", + "context.stats.outputTokens": "Izlazni tokeni", + "context.stats.reasoningTokens": "Tokeni za rasuđivanje", + "context.stats.cacheTokens": "Cache tokeni (čitanje/pisanje)", + "context.stats.userMessages": "Korisničke poruke", + "context.stats.assistantMessages": "Poruke asistenta", + "context.stats.totalCost": "Ukupni trošak", + "context.stats.sessionCreated": "Sesija kreirana", + "context.stats.lastActivity": "Posljednja aktivnost", + + "context.usage.tokens": "Tokeni", + "context.usage.usage": "Korištenje", + "context.usage.cost": "Trošak", + "context.usage.clickToView": "Klikni da vidiš kontekst", + "context.usage.view": "Prikaži korištenje konteksta", + + "language.en": "English", + "language.zh": "简体中文", + "language.zht": "繁體中文", + "language.ko": "한국어", + "language.de": "Deutsch", + "language.es": "Español", + "language.fr": "Français", + "language.da": "Dansk", + "language.ja": "日本語", + "language.pl": "Polski", + "language.ru": "Русский", + "language.ar": "العربية", + "language.no": "Norsk", + "language.br": "Português (Brasil)", + "language.bs": "Bosanski", + "language.th": "ไทย", + + "toast.language.title": "Jezik", + "toast.language.description": "Prebačeno na {{language}}", + + "toast.theme.title": "Tema promijenjena", + "toast.scheme.title": "Šema boja", + + "toast.workspace.enabled.title": "Radni prostori omogućeni", + "toast.workspace.enabled.description": "Više worktree-ova se sada prikazuje u bočnoj traci", + "toast.workspace.disabled.title": "Radni prostori onemogućeni", + "toast.workspace.disabled.description": "Samo glavni worktree se prikazuje u bočnoj traci", + + "toast.permissions.autoaccept.on.title": "Automatsko prihvatanje izmjena", + "toast.permissions.autoaccept.on.description": "Dozvole za izmjene i pisanje biće automatski odobrene", + "toast.permissions.autoaccept.off.title": "Zaustavljeno automatsko prihvatanje izmjena", + "toast.permissions.autoaccept.off.description": "Dozvole za izmjene i pisanje zahtijevaće odobrenje", + + "toast.model.none.title": "Nije odabran model", + "toast.model.none.description": "Poveži provajdera da sažmeš ovu sesiju", + + "toast.file.loadFailed.title": "Neuspjelo učitavanje datoteke", + "toast.file.listFailed.title": "Neuspješno listanje datoteka", + + "toast.context.noLineSelection.title": "Nema odabranih linija", + "toast.context.noLineSelection.description": "Prvo odaberi raspon linija u kartici datoteke.", + + "toast.session.share.copyFailed.title": "Neuspjelo kopiranje URL-a u međuspremnik", + "toast.session.share.success.title": "Sesija podijeljena", + "toast.session.share.success.description": "URL za dijeljenje je kopiran u međuspremnik!", + "toast.session.share.failed.title": "Neuspjelo dijeljenje sesije", + "toast.session.share.failed.description": "Došlo je do greške prilikom dijeljenja sesije", + + "toast.session.unshare.success.title": "Dijeljenje sesije ukinuto", + "toast.session.unshare.success.description": "Dijeljenje sesije je uspješno ukinuto!", + "toast.session.unshare.failed.title": "Neuspjelo ukidanje dijeljenja", + "toast.session.unshare.failed.description": "Došlo je do greške prilikom ukidanja dijeljenja", + + "toast.session.listFailed.title": "Neuspjelo učitavanje sesija za {{project}}", + + "toast.update.title": "Dostupno ažuriranje", + "toast.update.description": "Nova verzija Kilo-a ({{version}}) je dostupna za instalaciju.", + "toast.update.action.installRestart": "Instaliraj i restartuj", + "toast.update.action.notYet": "Ne još", + + "error.page.title": "Nešto je pošlo po zlu", + "error.page.description": "Došlo je do greške prilikom učitavanja aplikacije.", + "error.page.details.label": "Detalji greške", + "error.page.action.restart": "Restartuj", + "error.page.action.checking": "Provjera...", + "error.page.action.checkUpdates": "Provjeri ažuriranja", + "error.page.action.updateTo": "Ažuriraj na {{version}}", + "error.page.report.prefix": "Molimo prijavi ovu grešku Kilo timu", + "error.page.report.discord": "na Discordu", + "error.page.version": "Verzija: {{version}}", + + "error.dev.rootNotFound": + "Korijenski element nije pronađen. Da li si zaboravio da ga dodaš u index.html? Ili je možda id atribut pogrešno napisan?", + + "error.globalSync.connectFailed": "Nije moguće povezati se na server. Da li server radi na `{{url}}`?", + + "error.chain.unknown": "Nepoznata greška", + "error.chain.causedBy": "Uzrok:", + "error.chain.apiError": "API greška", + "error.chain.status": "Status: {{status}}", + "error.chain.retryable": "Može se ponoviti: {{retryable}}", + "error.chain.responseBody": "Tijelo odgovora:\n{{body}}", + "error.chain.didYouMean": "Da li si mislio: {{suggestions}}", + "error.chain.modelNotFound": "Model nije pronađen: {{provider}}/{{model}}", + "error.chain.checkConfig": "Provjeri konfiguraciju (opencode.json) - nazive provajdera/modela", + "error.chain.mcpFailed": 'MCP server "{{name}}" nije uspio. Napomena: Kilo još ne podržava MCP autentifikaciju.', + "error.chain.providerAuthFailed": "Autentifikacija provajdera nije uspjela ({{provider}}): {{message}}", + "error.chain.providerInitFailed": + 'Neuspjelo inicijalizovanje provajdera "{{provider}}". Provjeri kredencijale i konfiguraciju.', + "error.chain.configJsonInvalid": "Konfiguracijska datoteka na {{path}} nije važeći JSON(C)", + "error.chain.configJsonInvalidWithMessage": "Konfiguracijska datoteka na {{path}} nije važeći JSON(C): {{message}}", + "error.chain.configDirectoryTypo": + 'Direktorij "{{dir}}" u {{path}} nije ispravan. Preimenuj direktorij u "{{suggestion}}" ili ga ukloni. Ovo je česta greška u kucanju.', + "error.chain.configFrontmatterError": "Neuspjelo parsiranje frontmatter-a u {{path}}:\n{{message}}", + "error.chain.configInvalid": "Konfiguracijska datoteka na {{path}} nije ispravna", + "error.chain.configInvalidWithMessage": "Konfiguracijska datoteka na {{path}} nije ispravna: {{message}}", + + "notification.permission.title": "Potrebna dozvola", + "notification.permission.description": "{{sessionTitle}} u {{projectName}} traži dozvolu", + "notification.question.title": "Pitanje", + "notification.question.description": "{{sessionTitle}} u {{projectName}} ima pitanje", + "notification.action.goToSession": "Idi na sesiju", + + "notification.session.responseReady.title": "Odgovor je spreman", + "notification.session.error.title": "Greška sesije", + "notification.session.error.fallbackDescription": "Došlo je do greške", + + "home.recentProjects": "Nedavni projekti", + "home.empty.title": "Nema nedavnih projekata", + "home.empty.description": "Kreni tako što ćeš otvoriti lokalni projekat", + + "session.tab.session": "Sesija", + "session.tab.review": "Pregled", + "session.tab.context": "Kontekst", + "session.panel.reviewAndFiles": "Pregled i datoteke", + "session.review.filesChanged": "Izmijenjeno {{count}} datoteka", + "session.review.change.one": "Izmjena", + "session.review.change.other": "Izmjene", + "session.review.loadingChanges": "Učitavanje izmjena...", + "session.review.empty": "Još nema izmjena u ovoj sesiji", + "session.review.noChanges": "Nema izmjena", + + "session.files.selectToOpen": "Odaberi datoteku za otvaranje", + "session.files.all": "Sve datoteke", + "session.files.binaryContent": "Binarna datoteka (sadržaj se ne može prikazati)", + + "session.messages.renderEarlier": "Prikaži ranije poruke", + "session.messages.loadingEarlier": "Učitavanje ranijih poruka...", + "session.messages.loadEarlier": "Učitaj ranije poruke", + "session.messages.loading": "Učitavanje poruka...", + "session.messages.jumpToLatest": "Idi na najnovije", + + "session.context.addToContext": "Dodaj {{selection}} u kontekst", + + "session.new.worktree.main": "Glavna grana", + "session.new.worktree.mainWithBranch": "Glavna grana ({{branch}})", + "session.new.worktree.create": "Kreiraj novi worktree", + "session.new.lastModified": "Posljednja izmjena", + + "session.header.search.placeholder": "Pretraži {{project}}", + "session.header.searchFiles": "Pretraži datoteke", + + "status.popover.trigger": "Status", + "status.popover.ariaLabel": "Konfiguracije servera", + "status.popover.tab.servers": "Serveri", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "Plugini", + "status.popover.action.manageServers": "Upravljaj serverima", + + "session.share.popover.title": "Objavi na webu", + "session.share.popover.description.shared": "Ova sesija je javna na webu. Dostupna je svima koji imaju link.", + "session.share.popover.description.unshared": "Podijeli sesiju javno na webu. Biće dostupna svima koji imaju link.", + "session.share.action.share": "Podijeli", + "session.share.action.publish": "Objavi", + "session.share.action.publishing": "Objavljivanje...", + "session.share.action.unpublish": "Poništi objavu", + "session.share.action.unpublishing": "Poništavanje objave...", + "session.share.action.view": "Prikaži", + "session.share.copy.copied": "Kopirano", + "session.share.copy.copyLink": "Kopiraj link", + + "lsp.tooltip.none": "Nema LSP servera", + "lsp.label.connected": "{{count}} LSP", + + "prompt.loading": "Učitavanje upita...", + "terminal.loading": "Učitavanje terminala...", + "terminal.title": "Terminal", + "terminal.title.numbered": "Terminal {{number}}", + "terminal.close": "Zatvori terminal", + "terminal.connectionLost.title": "Veza prekinuta", + "terminal.connectionLost.description": + "Veza s terminalom je prekinuta. Ovo se može desiti kada se server restartuje.", + + "common.closeTab": "Zatvori karticu", + "common.dismiss": "Odbaci", + "common.requestFailed": "Zahtjev nije uspio", + "common.moreOptions": "Više opcija", + "common.learnMore": "Saznaj više", + "common.rename": "Preimenuj", + "common.reset": "Resetuj", + "common.archive": "Arhiviraj", + "common.delete": "Izbriši", + "common.close": "Zatvori", + "common.edit": "Uredi", + "common.loadMore": "Učitaj još", + "common.key.esc": "ESC", + + "sidebar.menu.toggle": "Prikaži/sakrij meni", + "sidebar.nav.projectsAndSessions": "Projekti i sesije", + "sidebar.settings": "Postavke", + "sidebar.help": "Pomoć", + "sidebar.workspaces.enable": "Omogući radne prostore", + "sidebar.workspaces.disable": "Onemogući radne prostore", + "sidebar.gettingStarted.title": "Početak", + "sidebar.gettingStarted.line1": "Kilo uključuje besplatne modele, tako da možeš odmah početi.", + "sidebar.gettingStarted.line2": "Poveži bilo kojeg provajdera da koristiš modele, npr. Claude, GPT, Gemini itd.", + "sidebar.project.recentSessions": "Nedavne sesije", + "sidebar.project.viewAllSessions": "Prikaži sve sesije", + + "app.name.desktop": "Kilo Desktop", + + "settings.section.desktop": "Desktop", + "settings.section.server": "Server", + "settings.tab.general": "Opšte", + "settings.tab.shortcuts": "Prečice", + + "settings.general.section.appearance": "Izgled", + "settings.general.section.notifications": "Sistemske obavijesti", + "settings.general.section.updates": "Ažuriranja", + "settings.general.section.sounds": "Zvučni efekti", + + "settings.general.row.language.title": "Jezik", + "settings.general.row.language.description": "Promijeni jezik prikaza u Kilo-u", + "settings.general.row.appearance.title": "Izgled", + "settings.general.row.appearance.description": "Prilagodi kako Kilo izgleda na tvom uređaju", + "settings.general.row.theme.title": "Tema", + "settings.general.row.theme.description": "Prilagodi temu Kilo-a.", + "settings.general.row.font.title": "Font", + "settings.general.row.font.description": "Prilagodi monospace font koji se koristi u blokovima koda", + + "settings.general.row.releaseNotes.title": "Bilješke o izdanju", + "settings.general.row.releaseNotes.description": 'Prikaži iskačuće prozore "Šta je novo" nakon ažuriranja', + + "settings.updates.row.startup.title": "Provjeri ažuriranja pri pokretanju", + "settings.updates.row.startup.description": "Automatski provjerava ažuriranja kada se Kilo pokrene", + "settings.updates.row.check.title": "Provjeri ažuriranja", + "settings.updates.row.check.description": "Ručno provjeri ažuriranja i instaliraj ako su dostupna", + "settings.updates.action.checkNow": "Provjeri sada", + "settings.updates.action.checking": "Provjera...", + "settings.updates.toast.latest.title": "Sve je ažurno", + "settings.updates.toast.latest.description": "Koristiš najnoviju verziju Kilo-a.", + "font.option.ibmPlexMono": "IBM Plex Mono", + "font.option.cascadiaCode": "Cascadia Code", + "font.option.firaCode": "Fira Code", + "font.option.hack": "Hack", + "font.option.inconsolata": "Inconsolata", + "font.option.intelOneMono": "Intel One Mono", + "font.option.iosevka": "Iosevka", + "font.option.jetbrainsMono": "JetBrains Mono", + "font.option.mesloLgs": "Meslo LGS", + "font.option.robotoMono": "Roboto Mono", + "font.option.sourceCodePro": "Source Code Pro", + "font.option.ubuntuMono": "Ubuntu Mono", + "sound.option.alert01": "Upozorenje 01", + "sound.option.alert02": "Upozorenje 02", + "sound.option.alert03": "Upozorenje 03", + "sound.option.alert04": "Upozorenje 04", + "sound.option.alert05": "Upozorenje 05", + "sound.option.alert06": "Upozorenje 06", + "sound.option.alert07": "Upozorenje 07", + "sound.option.alert08": "Upozorenje 08", + "sound.option.alert09": "Upozorenje 09", + "sound.option.alert10": "Upozorenje 10", + "sound.option.bipbop01": "Bip-bop 01", + "sound.option.bipbop02": "Bip-bop 02", + "sound.option.bipbop03": "Bip-bop 03", + "sound.option.bipbop04": "Bip-bop 04", + "sound.option.bipbop05": "Bip-bop 05", + "sound.option.bipbop06": "Bip-bop 06", + "sound.option.bipbop07": "Bip-bop 07", + "sound.option.bipbop08": "Bip-bop 08", + "sound.option.bipbop09": "Bip-bop 09", + "sound.option.bipbop10": "Bip-bop 10", + "sound.option.staplebops01": "Staplebops 01", + "sound.option.staplebops02": "Staplebops 02", + "sound.option.staplebops03": "Staplebops 03", + "sound.option.staplebops04": "Staplebops 04", + "sound.option.staplebops05": "Staplebops 05", + "sound.option.staplebops06": "Staplebops 06", + "sound.option.staplebops07": "Staplebops 07", + "sound.option.nope01": "Ne 01", + "sound.option.nope02": "Ne 02", + "sound.option.nope03": "Ne 03", + "sound.option.nope04": "Ne 04", + "sound.option.nope05": "Ne 05", + "sound.option.nope06": "Ne 06", + "sound.option.nope07": "Ne 07", + "sound.option.nope08": "Ne 08", + "sound.option.nope09": "Ne 09", + "sound.option.nope10": "Ne 10", + "sound.option.nope11": "Ne 11", + "sound.option.nope12": "Ne 12", + "sound.option.yup01": "Da 01", + "sound.option.yup02": "Da 02", + "sound.option.yup03": "Da 03", + "sound.option.yup04": "Da 04", + "sound.option.yup05": "Da 05", + "sound.option.yup06": "Da 06", + + "settings.general.notifications.agent.title": "Agent", + "settings.general.notifications.agent.description": + "Prikaži sistemsku obavijest kada agent završi ili zahtijeva pažnju", + "settings.general.notifications.permissions.title": "Dozvole", + "settings.general.notifications.permissions.description": "Prikaži sistemsku obavijest kada je potrebna dozvola", + "settings.general.notifications.errors.title": "Greške", + "settings.general.notifications.errors.description": "Prikaži sistemsku obavijest kada dođe do greške", + + "settings.general.sounds.agent.title": "Agent", + "settings.general.sounds.agent.description": "Pusti zvuk kada agent završi ili zahtijeva pažnju", + "settings.general.sounds.permissions.title": "Dozvole", + "settings.general.sounds.permissions.description": "Pusti zvuk kada je potrebna dozvola", + "settings.general.sounds.errors.title": "Greške", + "settings.general.sounds.errors.description": "Pusti zvuk kada dođe do greške", + + "settings.shortcuts.title": "Prečice na tastaturi", + "settings.shortcuts.reset.button": "Vrati na podrazumijevano", + "settings.shortcuts.reset.toast.title": "Prečice resetovane", + "settings.shortcuts.reset.toast.description": "Prečice na tastaturi su vraćene na podrazumijevane.", + "settings.shortcuts.conflict.title": "Prečica je već u upotrebi", + "settings.shortcuts.conflict.description": "{{keybind}} je već dodijeljeno za {{titles}}.", + "settings.shortcuts.unassigned": "Nedodijeljeno", + "settings.shortcuts.pressKeys": "Pritisni tastere", + "settings.shortcuts.search.placeholder": "Pretraži prečice", + "settings.shortcuts.search.empty": "Nema pronađenih prečica", + + "settings.shortcuts.group.general": "Opšte", + "settings.shortcuts.group.session": "Sesija", + "settings.shortcuts.group.navigation": "Navigacija", + "settings.shortcuts.group.modelAndAgent": "Model i agent", + "settings.shortcuts.group.terminal": "Terminal", + "settings.shortcuts.group.prompt": "Upit", + + "settings.providers.title": "Provajderi", + "settings.providers.description": "Postavke provajdera će se ovdje moći podešavati.", + "settings.providers.section.connected": "Povezani provajderi", + "settings.providers.connected.empty": "Nema povezanih provajdera", + "settings.providers.section.popular": "Popularni provajderi", + "settings.providers.tag.environment": "Okruženje", + "settings.providers.tag.config": "Konfiguracija", + "settings.providers.tag.custom": "Prilagođeno", + "settings.providers.tag.other": "Ostalo", + "settings.models.title": "Modeli", + "settings.models.description": "Postavke modela će se ovdje moći podešavati.", + "settings.agents.title": "Agenti", + "settings.agents.description": "Postavke agenata će se ovdje moći podešavati.", + "settings.commands.title": "Komande", + "settings.commands.description": "Postavke komandi će se ovdje moći podešavati.", + "settings.mcp.title": "MCP", + "settings.mcp.description": "MCP postavke će se ovdje moći podešavati.", + + "settings.permissions.title": "Dozvole", + "settings.permissions.description": "Kontroliši koje alate server smije koristiti po defaultu.", + "settings.permissions.section.tools": "Alati", + "settings.permissions.toast.updateFailed.title": "Neuspjelo ažuriranje dozvola", + + "settings.permissions.action.allow": "Dozvoli", + "settings.permissions.action.ask": "Pitaj", + "settings.permissions.action.deny": "Zabrani", + + "settings.permissions.tool.read.title": "Čitanje", + "settings.permissions.tool.read.description": "Čitanje datoteke (podudara se s putanjom datoteke)", + "settings.permissions.tool.edit.title": "Uređivanje", + "settings.permissions.tool.edit.description": + "Mijenjanje datoteka, uključujući izmjene, pisanja, patch-eve i multi-izmjene", + "settings.permissions.tool.glob.title": "Glob", + "settings.permissions.tool.glob.description": "Podudaranje datoteka pomoću glob šablona", + "settings.permissions.tool.grep.title": "Grep", + "settings.permissions.tool.grep.description": "Pretraživanje sadržaja datoteka pomoću regularnih izraza", + "settings.permissions.tool.list.title": "Lista", + "settings.permissions.tool.list.description": "Listanje datoteka unutar direktorija", + "settings.permissions.tool.bash.title": "Bash", + "settings.permissions.tool.bash.description": "Pokretanje shell komandi", + "settings.permissions.tool.task.title": "Zadatak", + "settings.permissions.tool.task.description": "Pokretanje pod-agenta", + "settings.permissions.tool.skill.title": "Vještina", + "settings.permissions.tool.skill.description": "Učitaj vještinu po nazivu", + "settings.permissions.tool.lsp.title": "LSP", + "settings.permissions.tool.lsp.description": "Pokreni upite jezičnog servera", + "settings.permissions.tool.todoread.title": "Čitanje liste zadataka", + "settings.permissions.tool.todoread.description": "Čitanje liste zadataka", + "settings.permissions.tool.todowrite.title": "Ažuriranje liste zadataka", + "settings.permissions.tool.todowrite.description": "Ažuriraj listu zadataka", + "settings.permissions.tool.webfetch.title": "Web preuzimanje", + "settings.permissions.tool.webfetch.description": "Preuzmi sadržaj sa URL-a", + "settings.permissions.tool.websearch.title": "Web pretraga", + "settings.permissions.tool.websearch.description": "Pretražuj web", + "settings.permissions.tool.codesearch.title": "Pretraga koda", + "settings.permissions.tool.codesearch.description": "Pretraži kod na webu", + "settings.permissions.tool.external_directory.title": "Vanjski direktorij", + "settings.permissions.tool.external_directory.description": "Pristup datotekama izvan direktorija projekta", + "settings.permissions.tool.doom_loop.title": "Beskonačna petlja", + "settings.permissions.tool.doom_loop.description": "Otkriva ponovljene pozive alata sa identičnim unosom", + + "session.delete.failed.title": "Neuspjelo brisanje sesije", + "session.delete.title": "Izbriši sesiju", + "session.delete.confirm": 'Izbriši sesiju "{{name}}"?', + "session.delete.button": "Izbriši sesiju", + + "workspace.new": "Novi radni prostor", + "workspace.type.local": "lokalno", + "workspace.type.sandbox": "sandbox", + "workspace.create.failed.title": "Neuspješno kreiranje radnog prostora", + "workspace.delete.failed.title": "Neuspješno brisanje radnog prostora", + "workspace.resetting.title": "Resetovanje radnog prostora", + "workspace.resetting.description": "Ovo može potrajati minut.", + "workspace.reset.failed.title": "Neuspješno resetovanje radnog prostora", + "workspace.reset.success.title": "Radni prostor resetovan", + "workspace.reset.success.description": "Radni prostor sada odgovara podrazumijevanoj grani.", + "workspace.error.stillPreparing": "Radni prostor se još priprema", + "workspace.status.checking": "Provjera neobjedinjenih promjena...", + "workspace.status.error": "Nije moguće provjeriti git status.", + "workspace.status.clean": "Nisu pronađene neobjedinjene promjene.", + "workspace.status.dirty": "Pronađene su neobjedinjene promjene u ovom radnom prostoru.", + "workspace.delete.title": "Izbriši radni prostor", + "workspace.delete.confirm": 'Izbriši radni prostor "{{name}}"?', + "workspace.delete.button": "Izbriši radni prostor", + "workspace.reset.title": "Resetuj radni prostor", + "workspace.reset.confirm": 'Resetuj radni prostor "{{name}}"?', + "workspace.reset.button": "Resetuj radni prostor", + "workspace.reset.archived.none": "Nijedna aktivna sesija neće biti arhivirana.", + "workspace.reset.archived.one": "1 sesija će biti arhivirana.", + "workspace.reset.archived.many": "Biće arhivirano {{count}} sesija.", + "workspace.reset.note": "Ovo će resetovati radni prostor da odgovara podrazumijevanoj grani.", +} diff --git a/packages/app/src/i18n/da.ts b/packages/app/src/i18n/da.ts index b2abe6d4a2e..59f6c4f9a27 100644 --- a/packages/app/src/i18n/da.ts +++ b/packages/app/src/i18n/da.ts @@ -322,22 +322,6 @@ export const dict = { "context.usage.clickToView": "Klik for at se kontekst", "context.usage.view": "Se kontekstforbrug", - "language.en": "English", - "language.zh": "简体中文", - "language.zht": "繁體中文", - "language.ko": "한국어", - "language.de": "Deutsch", - "language.es": "Español", - "language.fr": "Français", - "language.da": "Dansk", - "language.ja": "日本語", - "language.pl": "Polski", - "language.ru": "Русский", - "language.ar": "العربية", - "language.no": "Norsk", - "language.br": "Português (Brasil)", - "language.th": "ไทย", - "toast.language.title": "Sprog", "toast.language.description": "Skiftede til {{language}}", diff --git a/packages/app/src/i18n/de.ts b/packages/app/src/i18n/de.ts index 4ad3e86ba3e..7c24966ce2e 100644 --- a/packages/app/src/i18n/de.ts +++ b/packages/app/src/i18n/de.ts @@ -328,22 +328,6 @@ export const dict = { "context.usage.clickToView": "Klicken, um Kontext anzuzeigen", "context.usage.view": "Kontextnutzung anzeigen", - "language.en": "English", - "language.zh": "简体中文", - "language.zht": "繁體中文", - "language.ko": "한국어", - "language.de": "Deutsch", - "language.es": "Español", - "language.fr": "Français", - "language.da": "Dansk", - "language.ja": "日本語", - "language.pl": "Polski", - "language.ru": "Русский", - "language.ar": "العربية", - "language.no": "Norsk", - "language.br": "Português (Brasil)", - "language.th": "ไทย", - "toast.language.title": "Sprache", "toast.language.description": "Zu {{language}} gewechselt", diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index 5734b332106..6c3f067eeae 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -343,6 +343,7 @@ export const dict = { "language.ar": "العربية", "language.no": "Norsk", "language.br": "Português (Brasil)", + "language.bs": "Bosanski", "language.th": "ไทย", "toast.language.title": "Language", diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts index d8c7861dcaf..940ba0b5712 100644 --- a/packages/app/src/i18n/es.ts +++ b/packages/app/src/i18n/es.ts @@ -323,22 +323,6 @@ export const dict = { "context.usage.clickToView": "Haz clic para ver contexto", "context.usage.view": "Ver uso del contexto", - "language.en": "English", - "language.zh": "简体中文", - "language.zht": "繁體中文", - "language.ko": "한국어", - "language.de": "Deutsch", - "language.es": "Español", - "language.fr": "Français", - "language.da": "Dansk", - "language.ja": "日本語", - "language.pl": "Polski", - "language.ru": "Русский", - "language.ar": "العربية", - "language.no": "Norsk", - "language.br": "Português (Brasil)", - "language.th": "ไทย", - "toast.language.title": "Idioma", "toast.language.description": "Cambiado a {{language}}", diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts index 9cd9b8bb956..cf6b60238ca 100644 --- a/packages/app/src/i18n/fr.ts +++ b/packages/app/src/i18n/fr.ts @@ -323,22 +323,6 @@ export const dict = { "context.usage.clickToView": "Cliquez pour voir le contexte", "context.usage.view": "Voir l'utilisation du contexte", - "language.en": "English", - "language.zh": "简体中文", - "language.zht": "繁體中文", - "language.ko": "한국어", - "language.de": "Deutsch", - "language.es": "Español", - "language.fr": "Français", - "language.da": "Dansk", - "language.ja": "日本語", - "language.pl": "Polski", - "language.ru": "Русский", - "language.ar": "العربية", - "language.no": "Norsk", - "language.br": "Português (Brasil)", - "language.th": "ไทย", - "toast.language.title": "Langue", "toast.language.description": "Passé à {{language}}", diff --git a/packages/app/src/i18n/ja.ts b/packages/app/src/i18n/ja.ts index 7e076aa0ce2..6eb61ad8c04 100644 --- a/packages/app/src/i18n/ja.ts +++ b/packages/app/src/i18n/ja.ts @@ -321,22 +321,6 @@ export const dict = { "context.usage.clickToView": "クリックしてコンテキストを表示", "context.usage.view": "コンテキスト使用量を表示", - "language.en": "English", - "language.zh": "简体中文", - "language.zht": "繁體中文", - "language.ko": "한국어", - "language.de": "Deutsch", - "language.es": "Español", - "language.fr": "Français", - "language.da": "Dansk", - "language.ja": "日本語", - "language.pl": "Polski", - "language.ru": "Русский", - "language.ar": "العربية", - "language.no": "Norsk", - "language.br": "Português (Brasil)", - "language.th": "ไทย", - "toast.language.title": "言語", "toast.language.description": "{{language}}に切り替えました", diff --git a/packages/app/src/i18n/ko.ts b/packages/app/src/i18n/ko.ts index 4e1a5dec353..25cdf34e0d0 100644 --- a/packages/app/src/i18n/ko.ts +++ b/packages/app/src/i18n/ko.ts @@ -324,22 +324,6 @@ export const dict = { "context.usage.clickToView": "컨텍스트를 보려면 클릭", "context.usage.view": "컨텍스트 사용량 보기", - "language.en": "English", - "language.zh": "简体中文", - "language.zht": "繁體中文", - "language.ko": "한국어", - "language.de": "Deutsch", - "language.es": "Español", - "language.fr": "Français", - "language.da": "Dansk", - "language.ja": "日本語", - "language.pl": "Polski", - "language.ru": "Русский", - "language.ar": "العربية", - "language.no": "Norsk", - "language.br": "Português (Brasil)", - "language.th": "ไทย", - "toast.language.title": "언어", "toast.language.description": "{{language}}(으)로 전환됨", diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts index 6bdce1ad862..b7d9c18eb39 100644 --- a/packages/app/src/i18n/no.ts +++ b/packages/app/src/i18n/no.ts @@ -324,22 +324,6 @@ export const dict = { "context.usage.clickToView": "Klikk for å se kontekst", "context.usage.view": "Se kontekstforbruk", - "language.en": "English", - "language.zh": "简体中文", - "language.zht": "繁體中文", - "language.ko": "한국어", - "language.de": "Deutsch", - "language.es": "Español", - "language.fr": "Français", - "language.da": "Dansk", - "language.ja": "日本語", - "language.pl": "Polski", - "language.ru": "Русский", - "language.ar": "العربية", - "language.no": "Norsk", - "language.br": "Português (Brasil)", - "language.th": "ไทย", - "toast.language.title": "Språk", "toast.language.description": "Byttet til {{language}}", diff --git a/packages/app/src/i18n/pl.ts b/packages/app/src/i18n/pl.ts index 8c7c0f5c371..0369b064ba2 100644 --- a/packages/app/src/i18n/pl.ts +++ b/packages/app/src/i18n/pl.ts @@ -322,22 +322,6 @@ export const dict = { "context.usage.clickToView": "Kliknij, aby zobaczyć kontekst", "context.usage.view": "Pokaż użycie kontekstu", - "language.en": "English", - "language.zh": "简体中文", - "language.zht": "繁體中文", - "language.ko": "한국어", - "language.de": "Deutsch", - "language.es": "Español", - "language.fr": "Français", - "language.da": "Dansk", - "language.ja": "日本語", - "language.pl": "Polski", - "language.ru": "Русский", - "language.ar": "العربية", - "language.no": "Norsk", - "language.br": "Português (Brasil)", - "language.th": "ไทย", - "toast.language.title": "Język", "toast.language.description": "Przełączono na {{language}}", diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts index 77b404dc4a2..1838994b8c4 100644 --- a/packages/app/src/i18n/ru.ts +++ b/packages/app/src/i18n/ru.ts @@ -323,22 +323,6 @@ export const dict = { "context.usage.clickToView": "Нажмите для просмотра контекста", "context.usage.view": "Показать использование контекста", - "language.en": "English", - "language.zh": "简体中文", - "language.zht": "繁體中文", - "language.ko": "한국어", - "language.de": "Deutsch", - "language.es": "Español", - "language.fr": "Français", - "language.da": "Dansk", - "language.ja": "日本語", - "language.pl": "Polski", - "language.ru": "Русский", - "language.ar": "العربية", - "language.no": "Norsk", - "language.br": "Português (Brasil)", - "language.th": "ไทย", - "toast.language.title": "Язык", "toast.language.description": "Переключено на {{language}}", diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts index 4d9254b9e68..30e6f2116c5 100644 --- a/packages/app/src/i18n/th.ts +++ b/packages/app/src/i18n/th.ts @@ -326,22 +326,6 @@ export const dict = { "context.usage.clickToView": "คลิกเพื่อดูบริบท", "context.usage.view": "ดูการใช้บริบท", - "language.en": "English", - "language.zh": "简体中文", - "language.zht": "繁體中文", - "language.ko": "한국어", - "language.de": "Deutsch", - "language.es": "Español", - "language.fr": "Français", - "language.da": "Dansk", - "language.ja": "日本語", - "language.pl": "Polski", - "language.ru": "Русский", - "language.ar": "العربية", - "language.no": "Norsk", - "language.br": "Português (Brasil)", - "language.th": "ไทย", - "toast.language.title": "ภาษา", "toast.language.description": "สลับไปที่ {{language}}", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index 26607e11380..ff01bdd8b5d 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -324,22 +324,6 @@ export const dict = { "context.usage.clickToView": "点击查看上下文", "context.usage.view": "查看上下文用量", - "language.en": "English", - "language.zh": "简体中文", - "language.zht": "繁體中文", - "language.ko": "한국어", - "language.de": "Deutsch", - "language.es": "Español", - "language.fr": "Français", - "language.da": "Dansk", - "language.ja": "日本語", - "language.pl": "Polski", - "language.ru": "Русский", - "language.ar": "العربية", - "language.no": "Norsk", - "language.br": "Português (Brasil)", - "language.th": "ไทย", - "toast.language.title": "语言", "toast.language.description": "已切换到{{language}}", diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index 9386f540a92..28677dfdaee 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -321,22 +321,6 @@ export const dict = { "context.usage.clickToView": "點擊查看上下文", "context.usage.view": "檢視上下文用量", - "language.en": "English", - "language.zh": "简体中文", - "language.zht": "繁體中文", - "language.ko": "한국어", - "language.de": "Deutsch", - "language.es": "Español", - "language.fr": "Français", - "language.da": "Dansk", - "language.ja": "日本語", - "language.pl": "Polski", - "language.ru": "Русский", - "language.ar": "العربية", - "language.no": "Norsk", - "language.br": "Português (Brasil)", - "language.th": "ไทย", - "toast.language.title": "語言", "toast.language.description": "已切換到 {{language}}", diff --git a/packages/app/src/pages/directory-layout.tsx b/packages/app/src/pages/directory-layout.tsx index e9591c78502..923bb44877b 100644 --- a/packages/app/src/pages/directory-layout.tsx +++ b/packages/app/src/pages/directory-layout.tsx @@ -31,7 +31,7 @@ export default function Layout(props: ParentProps) { }) return ( - + {iife(() => { const sync = useSync() diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index 8a8b50ba403..b9de6b269be 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -58,6 +58,7 @@ import { usePermission } from "@/context/permission" import { Binary } from "@opencode-ai/util/binary" import { retry } from "@opencode-ai/util/retry" import { playSound, soundSrc } from "@/utils/sound" +import { createAim } from "@/utils/aim" import { Worktree as WorktreeState } from "@/utils/worktree" import { agentColor } from "@/utils/agent" @@ -146,9 +147,20 @@ export default function Layout(props: ParentProps) { const navLeave = { current: undefined as number | undefined } + const aim = createAim({ + enabled: () => !layout.sidebar.opened(), + active: () => state.hoverProject, + el: () => state.nav, + onActivate: (directory) => { + globalSync.child(directory) + setState("hoverProject", directory) + setState("hoverSession", undefined) + }, + }) + onCleanup(() => { - if (navLeave.current === undefined) return - clearTimeout(navLeave.current) + if (navLeave.current !== undefined) clearTimeout(navLeave.current) + aim.reset() }) const sidebarHovering = createMemo(() => !layout.sidebar.opened() && state.hoverProject !== undefined) @@ -162,15 +174,22 @@ export default function Layout(props: ParentProps) { createEffect(() => { if (!layout.sidebar.opened()) return + aim.reset() setState("hoverProject", undefined) }) + createEffect(() => { + if (state.hoverProject !== undefined) return + aim.reset() + }) + createEffect( on( () => ({ dir: params.dir, id: params.id }), () => { if (layout.sidebar.opened()) return if (!state.hoverProject) return + aim.reset() setState("hoverSession", undefined) setState("hoverProject", undefined) }, @@ -1000,69 +1019,6 @@ export default function Layout(props: ParentProps) { } } - async function deleteSession(session: Session) { - const [store, setStore] = globalSync.child(session.directory) - const sessions = (store.session ?? []).filter((s) => !s.parentID && !s.time?.archived) - const index = sessions.findIndex((s) => s.id === session.id) - const nextSession = sessions[index + 1] ?? sessions[index - 1] - - const result = await globalSDK.client.session - .delete({ directory: session.directory, sessionID: session.id }) - .then((x) => x.data) - .catch((err) => { - showToast({ - title: language.t("session.delete.failed.title"), - description: errorMessage(err), - }) - return false - }) - - if (!result) return - - setStore( - produce((draft) => { - const removed = new Set([session.id]) - - const byParent = new Map() - for (const item of draft.session) { - const parentID = item.parentID - if (!parentID) continue - const existing = byParent.get(parentID) - if (existing) { - existing.push(item.id) - continue - } - byParent.set(parentID, [item.id]) - } - - const stack = [session.id] - while (stack.length) { - const parentID = stack.pop() - if (!parentID) continue - - const children = byParent.get(parentID) - if (!children) continue - - for (const child of children) { - if (removed.has(child)) continue - removed.add(child) - stack.push(child) - } - } - - draft.session = draft.session.filter((s) => !removed.has(s.id)) - }), - ) - - if (session.id === params.id) { - if (nextSession) { - navigate(`/${params.dir}/session/${nextSession.id}`) - } else { - navigate(`/${params.dir}/session`) - } - } - } - command.register(() => { const commands: CommandOption[] = [ { @@ -1316,15 +1272,6 @@ export default function Layout(props: ParentProps) { globalSync.project.meta(project.worktree, { name }) } - async function renameSession(session: Session, next: string) { - if (next === session.title) return - await globalSDK.client.session.update({ - directory: session.directory, - sessionID: session.id, - title: next, - }) - } - const renameWorkspace = (directory: string, next: string, projectId?: string, branch?: string) => { const current = workspaceName(directory, projectId, branch) ?? branch ?? getFilename(directory) if (current === next) return @@ -1475,33 +1422,6 @@ export default function Layout(props: ParentProps) { }) } - function DialogDeleteSession(props: { session: Session }) { - const handleDelete = async () => { - await deleteSession(props.session) - dialog.close() - } - - return ( - -
-
- - {language.t("session.delete.confirm", { name: props.session.title })} - -
-
- - -
-
-
- ) - } - function DialogDeleteWorkspace(props: { root: string; directory: string }) { const name = createMemo(() => getFilename(props.directory)) const [data, setData] = createStore({ @@ -1855,10 +1775,6 @@ export default function Layout(props: ParentProps) { const hoverAllowed = createMemo(() => !props.mobile && sidebarExpanded()) const hoverEnabled = createMemo(() => (props.popover ?? true) && hoverAllowed()) const isActive = createMemo(() => props.session.id === params.id) - const [menu, setMenu] = createStore({ - open: false, - pendingRename: false, - }) const hoverPrefetch = { current: undefined as ReturnType | undefined } const cancelHoverPrefetch = () => { @@ -1885,7 +1801,7 @@ export default function Layout(props: ParentProps) { const item = ( - props.session.title} - onSave={(next) => renameSession(props.session, next)} - class="text-14-regular text-text-strong grow-1 min-w-0 overflow-hidden text-ellipsis truncate" - displayClass="text-14-regular text-text-strong grow-1 min-w-0 overflow-hidden text-ellipsis truncate" - stopPropagation - /> + + {props.session.title} + {(summary) => (
@@ -1972,7 +1883,10 @@ export default function Layout(props: ParentProps) { getLabel={messageLabel} onMessageSelect={(message) => { if (!isActive()) { - sessionStorage.setItem("opencode.pendingMessage", `${props.session.id}|${message.id}`) + layout.pendingMessage.set( + `${base64Encode(props.session.directory)}/${props.session.id}`, + message.id, + ) navigate(`${props.slug}/session/${props.session.id}`) return } @@ -1989,49 +1903,25 @@ export default function Layout(props: ParentProps) {
- setMenu("open", open)}> - - - - - { - if (!menu.pendingRename) return - event.preventDefault() - setMenu("pendingRename", false) - openEditor(`session:${props.session.id}`, props.session.title) - }} - > - { - setMenu("pendingRename", true) - setMenu("open", false) - }} - > - {language.t("common.rename")} - - archiveSession(props.session)}> - {language.t("common.archive")} - - - dialog.show(() => )}> - {language.t("common.delete")} - - - - + + { + event.preventDefault() + event.stopPropagation() + void archiveSession(props.session) + }} + /> +
) @@ -2440,17 +2330,17 @@ export default function Layout(props: ParentProps) { !selected() && !active(), "bg-surface-base-hover border border-border-weak-base": !selected() && active(), }} - onMouseEnter={() => { + onMouseEnter={(event: MouseEvent) => { if (!overlay()) return - globalSync.child(props.project.worktree) - setState("hoverProject", props.project.worktree) - setState("hoverSession", undefined) + aim.enter(props.project.worktree, event) + }} + onMouseLeave={() => { + if (!overlay()) return + aim.leave(props.project.worktree) }} onFocus={() => { if (!overlay()) return - globalSync.child(props.project.worktree) - setState("hoverProject", props.project.worktree) - setState("hoverSession", undefined) + aim.activate(props.project.worktree) }} onClick={() => navigateToProject(props.project.worktree)} onBlur={() => setOpen(false)} @@ -2598,17 +2488,22 @@ export default function Layout(props: ParentProps) { } const LocalWorkspace = (props: { project: LocalProject; mobile?: boolean }): JSX.Element => { - const [workspaceStore, setWorkspaceStore] = globalSync.child(props.project.worktree) + const workspace = createMemo(() => { + const [store, setStore] = globalSync.child(props.project.worktree) + return { store, setStore } + }) const slug = createMemo(() => base64Encode(props.project.worktree)) - const sessions = createMemo(() => - workspaceStore.session - .filter((session) => session.directory === workspaceStore.path.directory) + const sessions = createMemo(() => { + const store = workspace().store + return store.session + .filter((session) => session.directory === store.path.directory) .filter((session) => !session.parentID && !session.time?.archived) - .toSorted(sortSessions(Date.now())), - ) + .toSorted(sortSessions(Date.now())) + }) const children = createMemo(() => { + const store = workspace().store const map = new Map() - for (const session of workspaceStore.session) { + for (const session of store.session) { if (!session.parentID) continue const existing = map.get(session.parentID) if (existing) { @@ -2619,11 +2514,11 @@ export default function Layout(props: ParentProps) { } return map }) - const booted = createMemo((prev) => prev || workspaceStore.status === "complete", false) + const booted = createMemo((prev) => prev || workspace().store.status === "complete", false) const loading = createMemo(() => !booted() && sessions().length === 0) - const hasMore = createMemo(() => workspaceStore.sessionTotal > sessions().length) + const hasMore = createMemo(() => workspace().store.sessionTotal > sessions().length) const loadMore = async () => { - setWorkspaceStore("limit", (limit) => limit + 5) + workspace().setStore("limit", (limit) => limit + 5) await globalSync.project.loadSessions(props.project.worktree) } @@ -2935,7 +2830,7 @@ export default function Layout(props: ParentProps) { return (
-
+
{ + aim.reset() if (!sidebarHovering()) return if (navLeave.current !== undefined) clearTimeout(navLeave.current) @@ -3045,7 +2941,7 @@ export default function Layout(props: ParentProps) {
{(project) => ( -
+
)} diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 3e6310d8867..4ed288f5e9c 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -16,15 +16,19 @@ import { createResizeObserver } from "@solid-primitives/resize-observer" import { Dynamic } from "solid-js/web" import { useLocal } from "@/context/local" import { selectionFromLines, useFile, type FileSelection, type SelectedLineRange } from "@/context/file" -import { createStore } from "solid-js/store" +import { createStore, produce } from "solid-js/store" import { PromptInput } from "@/components/prompt-input" import { SessionContextUsage } from "@/components/session-context-usage" import { IconButton } from "@opencode-ai/ui/icon-button" import { Button } from "@opencode-ai/ui/button" import { Icon } from "@opencode-ai/ui/icon" import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip" +import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" +import { Dialog } from "@opencode-ai/ui/dialog" +import { InlineInput } from "@opencode-ai/ui/inline-input" import { ResizeHandle } from "@opencode-ai/ui/resize-handle" import { Tabs } from "@opencode-ai/ui/tabs" +import { Select } from "@opencode-ai/ui/select" import { useCodeComponent } from "@opencode-ai/ui/context/code" import { LineComment as LineCommentView, LineCommentEditor } from "@opencode-ai/ui/line-comment" import { SessionTurn } from "@opencode-ai/ui/session-turn" @@ -51,7 +55,7 @@ import { useCommand } from "@/context/command" import { useLanguage } from "@/context/language" import { useNavigate, useParams } from "@solidjs/router" import { UserMessage } from "@kilocode/sdk/v2" -import type { FileDiff } from "@kilocode/sdk/v2/client" +import type { FileDiff } from "@kilocode/sdk/v2" import { useSDK } from "@/context/sdk" import { usePrompt } from "@/context/prompt" import { useComments, type LineComment } from "@/context/comments" @@ -73,13 +77,36 @@ import { same } from "@/utils/same" type DiffStyle = "unified" | "split" +type HandoffSession = { + prompt: string + files: Record +} + +const HANDOFF_MAX = 40 + const handoff = { - prompt: "", - terminals: [] as string[], - files: {} as Record, + session: new Map(), + terminal: new Map(), +} + +const touch = (map: Map, key: K, value: V) => { + map.delete(key) + map.set(key, value) + while (map.size > HANDOFF_MAX) { + const first = map.keys().next().value + if (first === undefined) return + map.delete(first) + } +} + +const setSessionHandoff = (key: string, patch: Partial) => { + const prev = handoff.session.get(key) ?? { prompt: "", files: {} } + touch(handoff.session, key, { ...prev, ...patch }) } interface SessionReviewTabProps { + title?: JSX.Element + empty?: JSX.Element diffs: () => FileDiff[] view: () => ReturnType["view"]> diffStyle: DiffStyle @@ -196,6 +223,8 @@ function SessionReviewTab(props: SessionReviewTabProps) { return ( { scroll = el props.onScrollRef?.(el) @@ -255,6 +284,10 @@ export default function Page() { pendingMessage: undefined as string | undefined, scrollGesture: 0, autoCreated: false, + scroll: { + overflow: false, + bottom: true, + }, }) createEffect( @@ -280,9 +313,47 @@ export default function Page() { .finally(() => setUi("responding", false)) } const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`) + const workspaceKey = createMemo(() => params.dir ?? "") + const workspaceTabs = createMemo(() => layout.tabs(workspaceKey)) const tabs = createMemo(() => layout.tabs(sessionKey)) const view = createMemo(() => layout.view(sessionKey)) + createEffect( + on( + () => params.id, + (id, prev) => { + if (!id) return + if (prev) return + + const pending = layout.handoff.tabs() + if (!pending) return + if (Date.now() - pending.at > 60_000) { + layout.handoff.clearTabs() + return + } + + if (pending.id !== id) return + layout.handoff.clearTabs() + if (pending.dir !== (params.dir ?? "")) return + + const from = workspaceTabs().tabs() + if (from.all.length === 0 && !from.active) return + + const current = tabs().tabs() + if (current.all.length > 0 || current.active) return + + const all = normalizeTabs(from.all) + const active = from.active ? normalizeTab(from.active) : undefined + tabs().setAll(all) + tabs().setActive(active && all.includes(active) ? active : all[0]) + + workspaceTabs().setAll([]) + workspaceTabs().setActive(undefined) + }, + { defer: true }, + ), + ) + if (import.meta.env.DEV) { createEffect( on( @@ -398,6 +469,213 @@ export default function Page() { if (!id) return false return sync.session.history.loading(id) }) + + const [title, setTitle] = createStore({ + draft: "", + editing: false, + saving: false, + menuOpen: false, + pendingRename: false, + }) + let titleRef: HTMLInputElement | undefined + + const errorMessage = (err: unknown) => { + if (err && typeof err === "object" && "data" in err) { + const data = (err as { data?: { message?: string } }).data + if (data?.message) return data.message + } + if (err instanceof Error) return err.message + return language.t("common.requestFailed") + } + + createEffect( + on( + sessionKey, + () => setTitle({ draft: "", editing: false, saving: false, menuOpen: false, pendingRename: false }), + { defer: true }, + ), + ) + + const openTitleEditor = () => { + if (!params.id) return + setTitle({ editing: true, draft: info()?.title ?? "" }) + requestAnimationFrame(() => { + titleRef?.focus() + titleRef?.select() + }) + } + + const closeTitleEditor = () => { + if (title.saving) return + setTitle({ editing: false, saving: false }) + } + + const saveTitleEditor = async () => { + const sessionID = params.id + if (!sessionID) return + if (title.saving) return + + const next = title.draft.trim() + if (!next || next === (info()?.title ?? "")) { + setTitle({ editing: false, saving: false }) + return + } + + setTitle("saving", true) + await sdk.client.session + .update({ sessionID, title: next }) + .then(() => { + sync.set( + produce((draft) => { + const index = draft.session.findIndex((s) => s.id === sessionID) + if (index !== -1) draft.session[index].title = next + }), + ) + setTitle({ editing: false, saving: false }) + }) + .catch((err) => { + setTitle("saving", false) + showToast({ + title: language.t("common.requestFailed"), + description: errorMessage(err), + }) + }) + } + + async function archiveSession(sessionID: string) { + const session = sync.session.get(sessionID) + if (!session) return + + const sessions = sync.data.session ?? [] + const index = sessions.findIndex((s) => s.id === sessionID) + const nextSession = index === -1 ? undefined : (sessions[index + 1] ?? sessions[index - 1]) + + await sdk.client.session + .update({ sessionID, time: { archived: Date.now() } }) + .then(() => { + sync.set( + produce((draft) => { + const index = draft.session.findIndex((s) => s.id === sessionID) + if (index !== -1) draft.session.splice(index, 1) + }), + ) + + if (params.id !== sessionID) return + if (session.parentID) { + navigate(`/${params.dir}/session/${session.parentID}`) + return + } + if (nextSession) { + navigate(`/${params.dir}/session/${nextSession.id}`) + return + } + navigate(`/${params.dir}/session`) + }) + .catch((err) => { + showToast({ + title: language.t("common.requestFailed"), + description: errorMessage(err), + }) + }) + } + + async function deleteSession(sessionID: string) { + const session = sync.session.get(sessionID) + if (!session) return false + + const sessions = (sync.data.session ?? []).filter((s) => !s.parentID && !s.time?.archived) + const index = sessions.findIndex((s) => s.id === sessionID) + const nextSession = index === -1 ? undefined : (sessions[index + 1] ?? sessions[index - 1]) + + const result = await sdk.client.session + .delete({ sessionID }) + .then((x) => x.data) + .catch((err) => { + showToast({ + title: language.t("session.delete.failed.title"), + description: errorMessage(err), + }) + return false + }) + + if (!result) return false + + sync.set( + produce((draft) => { + const removed = new Set([sessionID]) + + const byParent = new Map() + for (const item of draft.session) { + const parentID = item.parentID + if (!parentID) continue + const existing = byParent.get(parentID) + if (existing) { + existing.push(item.id) + continue + } + byParent.set(parentID, [item.id]) + } + + const stack = [sessionID] + while (stack.length) { + const parentID = stack.pop() + if (!parentID) continue + + const children = byParent.get(parentID) + if (!children) continue + + for (const child of children) { + if (removed.has(child)) continue + removed.add(child) + stack.push(child) + } + } + + draft.session = draft.session.filter((s) => !removed.has(s.id)) + }), + ) + + if (params.id !== sessionID) return true + if (session.parentID) { + navigate(`/${params.dir}/session/${session.parentID}`) + return true + } + if (nextSession) { + navigate(`/${params.dir}/session/${nextSession.id}`) + return true + } + navigate(`/${params.dir}/session`) + return true + } + + function DialogDeleteSession(props: { sessionID: string }) { + const title = createMemo(() => sync.session.get(props.sessionID)?.title ?? language.t("command.session.new")) + const handleDelete = async () => { + await deleteSession(props.sessionID) + dialog.close() + } + + return ( + +
+
+ + {language.t("session.delete.confirm", { name: title() })} + +
+
+ + +
+
+
+ ) + } + const emptyUserMessages: UserMessage[] = [] const userMessages = createMemo( () => messages().filter((m) => m.role === "user") as UserMessage[], @@ -436,10 +714,14 @@ export default function Page() { messageId: undefined as string | undefined, turnStart: 0, mobileTab: "session" as "session" | "changes", + changes: "session" as "session" | "turn", newSessionWorktree: "main", promptHeight: 0, }) + const turnDiffs = createMemo(() => lastUserMessage()?.summary?.diffs ?? []) + const reviewDiffs = createMemo(() => (store.changes === "session" ? diffs() : turnDiffs())) + const renderedUserMessages = createMemo( () => { const msgs = visibleUserMessages() @@ -526,6 +808,7 @@ export default function Page() { let inputRef!: HTMLDivElement let promptDock: HTMLDivElement | undefined let scroller: HTMLDivElement | undefined + let content: HTMLDivElement | undefined const scrollGestureWindowMs = 250 @@ -545,8 +828,10 @@ export default function Page() { const hasScrollGesture = () => Date.now() - ui.scrollGesture < scrollGestureWindowMs createEffect(() => { - if (!params.id) return - sync.session.sync(params.id) + sdk.directory + const id = params.id + if (!id) return + sync.session.sync(id) }) createEffect(() => { @@ -614,10 +899,23 @@ export default function Page() { createEffect( on( - () => params.id, + sessionKey, () => { setStore("messageId", undefined) setStore("expanded", {}) + setStore("changes", "session") + setUi("autoCreated", false) + }, + { defer: true }, + ), + ) + + createEffect( + on( + () => params.dir, + (dir) => { + if (!dir) return + setStore("newSessionWorktree", "main") }, { defer: true }, ), @@ -1125,34 +1423,84 @@ export default function Page() { activeDiff: undefined as string | undefined, }) - const reviewScroll = () => tree.reviewScroll - const setReviewScroll = (value: HTMLDivElement | undefined) => setTree("reviewScroll", value) - const pendingDiff = () => tree.pendingDiff - const setPendingDiff = (value: string | undefined) => setTree("pendingDiff", value) - const activeDiff = () => tree.activeDiff - const setActiveDiff = (value: string | undefined) => setTree("activeDiff", value) + createEffect( + on( + sessionKey, + () => { + setTree({ reviewScroll: undefined, pendingDiff: undefined, activeDiff: undefined }) + }, + { defer: true }, + ), + ) const showAllFiles = () => { if (fileTreeTab() !== "changes") return setFileTreeTab("all") } + const changesOptions = ["session", "turn"] as const + const changesOptionsList = [...changesOptions] + + const changesTitle = () => ( +