Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2264c93b6b |
@@ -23,7 +23,7 @@
|
|||||||
},
|
},
|
||||||
"packages/app": {
|
"packages/app": {
|
||||||
"name": "@opencode-ai/app",
|
"name": "@opencode-ai/app",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "catalog:",
|
"@kobalte/core": "catalog:",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
"@thisbeyond/solid-dnd": "0.7.5",
|
"@thisbeyond/solid-dnd": "0.7.5",
|
||||||
"diff": "catalog:",
|
"diff": "catalog:",
|
||||||
"fuzzysort": "catalog:",
|
"fuzzysort": "catalog:",
|
||||||
"ghostty-web": "0.4.0",
|
"ghostty-web": "0.3.0",
|
||||||
"luxon": "catalog:",
|
"luxon": "catalog:",
|
||||||
"marked": "catalog:",
|
"marked": "catalog:",
|
||||||
"marked-shiki": "catalog:",
|
"marked-shiki": "catalog:",
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/app": {
|
"packages/console/app": {
|
||||||
"name": "@opencode-ai/console-app",
|
"name": "@opencode-ai/console-app",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cloudflare/vite-plugin": "1.15.2",
|
"@cloudflare/vite-plugin": "1.15.2",
|
||||||
"@ibm/plex": "6.4.1",
|
"@ibm/plex": "6.4.1",
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/core": {
|
"packages/console/core": {
|
||||||
"name": "@opencode-ai/console-core",
|
"name": "@opencode-ai/console-core",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-sts": "3.782.0",
|
"@aws-sdk/client-sts": "3.782.0",
|
||||||
"@jsx-email/render": "1.1.1",
|
"@jsx-email/render": "1.1.1",
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/function": {
|
"packages/console/function": {
|
||||||
"name": "@opencode-ai/console-function",
|
"name": "@opencode-ai/console-function",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/anthropic": "2.0.0",
|
"@ai-sdk/anthropic": "2.0.0",
|
||||||
"@ai-sdk/openai": "2.0.2",
|
"@ai-sdk/openai": "2.0.2",
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/mail": {
|
"packages/console/mail": {
|
||||||
"name": "@opencode-ai/console-mail",
|
"name": "@opencode-ai/console-mail",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jsx-email/all": "2.2.3",
|
"@jsx-email/all": "2.2.3",
|
||||||
"@jsx-email/cli": "1.4.3",
|
"@jsx-email/cli": "1.4.3",
|
||||||
@@ -182,7 +182,7 @@
|
|||||||
},
|
},
|
||||||
"packages/desktop": {
|
"packages/desktop": {
|
||||||
"name": "@opencode-ai/desktop",
|
"name": "@opencode-ai/desktop",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/app": "workspace:*",
|
"@opencode-ai/app": "workspace:*",
|
||||||
"@opencode-ai/ui": "workspace:*",
|
"@opencode-ai/ui": "workspace:*",
|
||||||
@@ -213,7 +213,7 @@
|
|||||||
},
|
},
|
||||||
"packages/enterprise": {
|
"packages/enterprise": {
|
||||||
"name": "@opencode-ai/enterprise",
|
"name": "@opencode-ai/enterprise",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/ui": "workspace:*",
|
"@opencode-ai/ui": "workspace:*",
|
||||||
"@opencode-ai/util": "workspace:*",
|
"@opencode-ai/util": "workspace:*",
|
||||||
@@ -242,7 +242,7 @@
|
|||||||
},
|
},
|
||||||
"packages/function": {
|
"packages/function": {
|
||||||
"name": "@opencode-ai/function",
|
"name": "@opencode-ai/function",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/auth-app": "8.0.1",
|
"@octokit/auth-app": "8.0.1",
|
||||||
"@octokit/rest": "catalog:",
|
"@octokit/rest": "catalog:",
|
||||||
@@ -258,14 +258,14 @@
|
|||||||
},
|
},
|
||||||
"packages/opencode": {
|
"packages/opencode": {
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"bin": {
|
"bin": {
|
||||||
"opencode": "./bin/opencode",
|
"opencode": "./bin/opencode",
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "1.11.1",
|
"@actions/core": "1.11.1",
|
||||||
"@actions/github": "6.0.1",
|
"@actions/github": "6.0.1",
|
||||||
"@agentclientprotocol/sdk": "0.14.1",
|
"@agentclientprotocol/sdk": "0.13.0",
|
||||||
"@ai-sdk/amazon-bedrock": "3.0.74",
|
"@ai-sdk/amazon-bedrock": "3.0.74",
|
||||||
"@ai-sdk/anthropic": "2.0.58",
|
"@ai-sdk/anthropic": "2.0.58",
|
||||||
"@ai-sdk/azure": "2.0.91",
|
"@ai-sdk/azure": "2.0.91",
|
||||||
@@ -284,10 +284,9 @@
|
|||||||
"@ai-sdk/provider-utils": "3.0.20",
|
"@ai-sdk/provider-utils": "3.0.20",
|
||||||
"@ai-sdk/togetherai": "1.0.34",
|
"@ai-sdk/togetherai": "1.0.34",
|
||||||
"@ai-sdk/vercel": "1.0.33",
|
"@ai-sdk/vercel": "1.0.33",
|
||||||
"@ai-sdk/xai": "2.0.51",
|
"@ai-sdk/xai": "2.0.56",
|
||||||
"@clack/prompts": "1.0.0-alpha.1",
|
"@clack/prompts": "1.0.0-alpha.1",
|
||||||
"@gitlab/gitlab-ai-provider": "3.4.0",
|
"@gitlab/gitlab-ai-provider": "3.4.0",
|
||||||
"@gitlab/opencode-gitlab-auth": "1.3.2",
|
|
||||||
"@hono/standard-validator": "0.1.5",
|
"@hono/standard-validator": "0.1.5",
|
||||||
"@hono/zod-validator": "catalog:",
|
"@hono/zod-validator": "catalog:",
|
||||||
"@modelcontextprotocol/sdk": "1.25.2",
|
"@modelcontextprotocol/sdk": "1.25.2",
|
||||||
@@ -364,7 +363,7 @@
|
|||||||
},
|
},
|
||||||
"packages/plugin": {
|
"packages/plugin": {
|
||||||
"name": "@opencode-ai/plugin",
|
"name": "@opencode-ai/plugin",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"zod": "catalog:",
|
"zod": "catalog:",
|
||||||
@@ -384,7 +383,7 @@
|
|||||||
},
|
},
|
||||||
"packages/sdk/js": {
|
"packages/sdk/js": {
|
||||||
"name": "@opencode-ai/sdk",
|
"name": "@opencode-ai/sdk",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@hey-api/openapi-ts": "0.90.10",
|
"@hey-api/openapi-ts": "0.90.10",
|
||||||
"@tsconfig/node22": "catalog:",
|
"@tsconfig/node22": "catalog:",
|
||||||
@@ -395,7 +394,7 @@
|
|||||||
},
|
},
|
||||||
"packages/slack": {
|
"packages/slack": {
|
||||||
"name": "@opencode-ai/slack",
|
"name": "@opencode-ai/slack",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"@slack/bolt": "^3.17.1",
|
"@slack/bolt": "^3.17.1",
|
||||||
@@ -408,7 +407,7 @@
|
|||||||
},
|
},
|
||||||
"packages/ui": {
|
"packages/ui": {
|
||||||
"name": "@opencode-ai/ui",
|
"name": "@opencode-ai/ui",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "catalog:",
|
"@kobalte/core": "catalog:",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
@@ -450,7 +449,7 @@
|
|||||||
},
|
},
|
||||||
"packages/util": {
|
"packages/util": {
|
||||||
"name": "@opencode-ai/util",
|
"name": "@opencode-ai/util",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"zod": "catalog:",
|
"zod": "catalog:",
|
||||||
},
|
},
|
||||||
@@ -461,7 +460,7 @@
|
|||||||
},
|
},
|
||||||
"packages/web": {
|
"packages/web": {
|
||||||
"name": "@opencode-ai/web",
|
"name": "@opencode-ai/web",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/cloudflare": "12.6.3",
|
"@astrojs/cloudflare": "12.6.3",
|
||||||
"@astrojs/markdown-remark": "6.3.1",
|
"@astrojs/markdown-remark": "6.3.1",
|
||||||
@@ -496,6 +495,9 @@
|
|||||||
"web-tree-sitter",
|
"web-tree-sitter",
|
||||||
"tree-sitter-bash",
|
"tree-sitter-bash",
|
||||||
],
|
],
|
||||||
|
"patchedDependencies": {
|
||||||
|
"ghostty-web@0.3.0": "patches/ghostty-web@0.3.0.patch",
|
||||||
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@types/bun": "catalog:",
|
"@types/bun": "catalog:",
|
||||||
"@types/node": "catalog:",
|
"@types/node": "catalog:",
|
||||||
@@ -556,7 +558,7 @@
|
|||||||
|
|
||||||
"@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="],
|
"@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="],
|
||||||
|
|
||||||
"@agentclientprotocol/sdk": ["@agentclientprotocol/sdk@0.14.1", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-b6r3PS3Nly+Wyw9U+0nOr47bV8tfS476EgyEMhoKvJCZLbgqoDFN7DJwkxL88RR0aiOqOYV1ZnESHqb+RmdH8w=="],
|
"@agentclientprotocol/sdk": ["@agentclientprotocol/sdk@0.13.0", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Z6/Fp4cXLbYdMXr5AK752JM5qG2VKb6ShM0Ql6FimBSckMmLyK54OA20UhPYoH4C37FSFwUTARuwQOwQUToYrw=="],
|
||||||
|
|
||||||
"@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@3.0.74", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.58", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-q83HE3FBb/HPIvjXsehrHOgCuGHPorSMFt6BYnzIYZy8gNnSqV1OWX4oXVsCAuYPPMtYW/KMK35hmoIFV8QKoQ=="],
|
"@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@3.0.74", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.58", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-q83HE3FBb/HPIvjXsehrHOgCuGHPorSMFt6BYnzIYZy8gNnSqV1OWX4oXVsCAuYPPMtYW/KMK35hmoIFV8QKoQ=="],
|
||||||
|
|
||||||
@@ -602,7 +604,7 @@
|
|||||||
|
|
||||||
"@ai-sdk/vercel": ["@ai-sdk/vercel@1.0.33", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.32", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Qwjm+HdwKasu7L9bDUryBMGKDMscIEzMUkjw/33uGdJpktzyNW13YaNIObOZ2HkskqDMIQJSd4Ao2BBT8fEYLw=="],
|
"@ai-sdk/vercel": ["@ai-sdk/vercel@1.0.33", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.32", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Qwjm+HdwKasu7L9bDUryBMGKDMscIEzMUkjw/33uGdJpktzyNW13YaNIObOZ2HkskqDMIQJSd4Ao2BBT8fEYLw=="],
|
||||||
|
|
||||||
"@ai-sdk/xai": ["@ai-sdk/xai@2.0.51", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.30", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-AI3le03qiegkZvn9hpnpDwez49lOvQLj4QUBT8H41SMbrdTYOxn3ktTwrsSu90cNDdzKGMvoH0u2GHju1EdnCg=="],
|
"@ai-sdk/xai": ["@ai-sdk/xai@2.0.56", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.32", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-FGlqwWc3tAYqDHE8r8hQGQLcMiPUwgz90oU2QygUH930OWtCLapFkSu114DgVaIN/qoM1DUX+inv0Ee74Fgp5g=="],
|
||||||
|
|
||||||
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
||||||
|
|
||||||
@@ -918,22 +920,8 @@
|
|||||||
|
|
||||||
"@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=="],
|
"@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/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/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=="],
|
"@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=="],
|
||||||
@@ -948,8 +936,6 @@
|
|||||||
|
|
||||||
"@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/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=="],
|
"@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=="],
|
"@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=="],
|
||||||
@@ -1142,8 +1128,6 @@
|
|||||||
|
|
||||||
"@leichtgewicht/ip-codec": ["@leichtgewicht/ip-codec@2.0.5", "", {}, "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw=="],
|
"@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=="],
|
"@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=="],
|
"@mixmark-io/domino": ["@mixmark-io/domino@2.2.0", "", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="],
|
||||||
@@ -1384,8 +1368,6 @@
|
|||||||
|
|
||||||
"@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=="],
|
"@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=="],
|
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||||
|
|
||||||
"@planetscale/database": ["@planetscale/database@1.19.0", "", {}, "sha512-Tv4jcFUFAFjOWrGSio49H6R2ijALv0ZzVBfJKIdm+kl9X046Fh4LLawrF9OMsglVbK6ukqMJsUCeucGAFTBcMA=="],
|
"@planetscale/database": ["@planetscale/database@1.19.0", "", {}, "sha512-Tv4jcFUFAFjOWrGSio49H6R2ijALv0ZzVBfJKIdm+kl9X046Fh4LLawrF9OMsglVbK6ukqMJsUCeucGAFTBcMA=="],
|
||||||
@@ -1962,8 +1944,6 @@
|
|||||||
|
|
||||||
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
|
"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@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
|
"accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
|
||||||
|
|
||||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||||
@@ -2040,14 +2020,10 @@
|
|||||||
|
|
||||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
"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.23", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001760", "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-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA=="],
|
"autoprefixer": ["autoprefixer@10.4.23", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001760", "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-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA=="],
|
||||||
|
|
||||||
"available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
|
"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=="],
|
"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=="],
|
"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=="],
|
||||||
@@ -2490,26 +2466,16 @@
|
|||||||
|
|
||||||
"fast-content-type-parse": ["fast-content-type-parse@3.0.0", "", {}, "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg=="],
|
"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-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=="],
|
"fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="],
|
||||||
|
|
||||||
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
||||||
|
|
||||||
"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-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-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=="],
|
"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=="],
|
"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=="],
|
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||||
@@ -2524,8 +2490,6 @@
|
|||||||
|
|
||||||
"find-babel-config": ["find-babel-config@2.1.2", "", { "dependencies": { "json5": "^2.2.3" } }, "sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg=="],
|
"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@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
|
"find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
|
||||||
|
|
||||||
"finity": ["finity@0.5.4", "", {}, "sha512-3l+5/1tuw616Lgb0QBimxfdd2TqaDGpfCBpfX6EqtFmqUV3FtQnVEX4Aa62DagYEqnsTIjZcTfbq9msDbXYgyA=="],
|
"finity": ["finity@0.5.4", "", {}, "sha512-3l+5/1tuw616Lgb0QBimxfdd2TqaDGpfCBpfX6EqtFmqUV3FtQnVEX4Aa62DagYEqnsTIjZcTfbq9msDbXYgyA=="],
|
||||||
@@ -2602,7 +2566,7 @@
|
|||||||
|
|
||||||
"get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="],
|
"get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="],
|
||||||
|
|
||||||
"ghostty-web": ["ghostty-web@0.4.0", "", {}, "sha512-0puDBik2qapbD/QQBW9o5ZHfXnZBqZWx/ctBiVtKZ6ZLds4NYb+wZuw1cRLXZk9zYovIQ908z3rvFhexAvc5Hg=="],
|
"ghostty-web": ["ghostty-web@0.3.0", "", {}, "sha512-SAdSHWYF20GMZUB0n8kh1N6Z4ljMnuUqT8iTB2n5FAPswEV10MejEpLlhW/769GL5+BQa1NYwEg9y/XCckV5+A=="],
|
||||||
|
|
||||||
"gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="],
|
"gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="],
|
||||||
|
|
||||||
@@ -2892,8 +2856,6 @@
|
|||||||
|
|
||||||
"json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
|
"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-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=="],
|
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||||
@@ -2930,8 +2892,6 @@
|
|||||||
|
|
||||||
"leac": ["leac@0.6.0", "", {}, "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg=="],
|
"leac": ["leac@0.6.0", "", {}, "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg=="],
|
||||||
|
|
||||||
"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": ["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=="],
|
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="],
|
||||||
@@ -3234,8 +3194,6 @@
|
|||||||
|
|
||||||
"omggif": ["omggif@1.0.10", "", {}, "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw=="],
|
"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=="],
|
"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=="],
|
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||||
@@ -3342,12 +3300,6 @@
|
|||||||
|
|
||||||
"pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
|
"pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
|
||||||
|
|
||||||
"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=="],
|
"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=="],
|
"pixelmatch": ["pixelmatch@5.3.0", "", { "dependencies": { "pngjs": "^6.0.0" }, "bin": { "pixelmatch": "bin/pixelmatch" } }, "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q=="],
|
||||||
@@ -3400,8 +3352,6 @@
|
|||||||
|
|
||||||
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
|
"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=="],
|
"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=="],
|
"prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="],
|
||||||
@@ -3424,8 +3374,6 @@
|
|||||||
|
|
||||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
"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=="],
|
"radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="],
|
||||||
|
|
||||||
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
||||||
@@ -3460,8 +3408,6 @@
|
|||||||
|
|
||||||
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
"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-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=="],
|
"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=="],
|
||||||
@@ -3528,8 +3474,6 @@
|
|||||||
|
|
||||||
"restructure": ["restructure@3.0.2", "", {}, "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw=="],
|
"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": ["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=="],
|
"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=="],
|
||||||
@@ -3542,8 +3486,6 @@
|
|||||||
|
|
||||||
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
"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=="],
|
"rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="],
|
||||||
|
|
||||||
"rollup": ["rollup@4.57.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.0", "@rollup/rollup-android-arm64": "4.57.0", "@rollup/rollup-darwin-arm64": "4.57.0", "@rollup/rollup-darwin-x64": "4.57.0", "@rollup/rollup-freebsd-arm64": "4.57.0", "@rollup/rollup-freebsd-x64": "4.57.0", "@rollup/rollup-linux-arm-gnueabihf": "4.57.0", "@rollup/rollup-linux-arm-musleabihf": "4.57.0", "@rollup/rollup-linux-arm64-gnu": "4.57.0", "@rollup/rollup-linux-arm64-musl": "4.57.0", "@rollup/rollup-linux-loong64-gnu": "4.57.0", "@rollup/rollup-linux-loong64-musl": "4.57.0", "@rollup/rollup-linux-ppc64-gnu": "4.57.0", "@rollup/rollup-linux-ppc64-musl": "4.57.0", "@rollup/rollup-linux-riscv64-gnu": "4.57.0", "@rollup/rollup-linux-riscv64-musl": "4.57.0", "@rollup/rollup-linux-s390x-gnu": "4.57.0", "@rollup/rollup-linux-x64-gnu": "4.57.0", "@rollup/rollup-linux-x64-musl": "4.57.0", "@rollup/rollup-openbsd-x64": "4.57.0", "@rollup/rollup-openharmony-arm64": "4.57.0", "@rollup/rollup-win32-arm64-msvc": "4.57.0", "@rollup/rollup-win32-ia32-msvc": "4.57.0", "@rollup/rollup-win32-x64-gnu": "4.57.0", "@rollup/rollup-win32-x64-msvc": "4.57.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA=="],
|
"rollup": ["rollup@4.57.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.0", "@rollup/rollup-android-arm64": "4.57.0", "@rollup/rollup-darwin-arm64": "4.57.0", "@rollup/rollup-darwin-x64": "4.57.0", "@rollup/rollup-freebsd-arm64": "4.57.0", "@rollup/rollup-freebsd-x64": "4.57.0", "@rollup/rollup-linux-arm-gnueabihf": "4.57.0", "@rollup/rollup-linux-arm-musleabihf": "4.57.0", "@rollup/rollup-linux-arm64-gnu": "4.57.0", "@rollup/rollup-linux-arm64-musl": "4.57.0", "@rollup/rollup-linux-loong64-gnu": "4.57.0", "@rollup/rollup-linux-loong64-musl": "4.57.0", "@rollup/rollup-linux-ppc64-gnu": "4.57.0", "@rollup/rollup-linux-ppc64-musl": "4.57.0", "@rollup/rollup-linux-riscv64-gnu": "4.57.0", "@rollup/rollup-linux-riscv64-musl": "4.57.0", "@rollup/rollup-linux-s390x-gnu": "4.57.0", "@rollup/rollup-linux-x64-gnu": "4.57.0", "@rollup/rollup-linux-x64-musl": "4.57.0", "@rollup/rollup-openbsd-x64": "4.57.0", "@rollup/rollup-openharmony-arm64": "4.57.0", "@rollup/rollup-win32-arm64-msvc": "4.57.0", "@rollup/rollup-win32-ia32-msvc": "4.57.0", "@rollup/rollup-win32-x64-gnu": "4.57.0", "@rollup/rollup-win32-x64-msvc": "4.57.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA=="],
|
||||||
@@ -3566,10 +3508,6 @@
|
|||||||
|
|
||||||
"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-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=="],
|
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||||
|
|
||||||
"sax": ["sax@1.2.1", "", {}, "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="],
|
"sax": ["sax@1.2.1", "", {}, "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="],
|
||||||
@@ -3578,8 +3516,6 @@
|
|||||||
|
|
||||||
"section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="],
|
"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=="],
|
"selderee": ["selderee@0.11.0", "", { "dependencies": { "parseley": "^0.12.0" } }, "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA=="],
|
||||||
|
|
||||||
"semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
"semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||||
@@ -3594,8 +3530,6 @@
|
|||||||
|
|
||||||
"serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="],
|
"serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="],
|
||||||
|
|
||||||
"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-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=="],
|
"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=="],
|
||||||
@@ -3658,8 +3592,6 @@
|
|||||||
|
|
||||||
"solid-use": ["solid-use@0.9.1", "", { "peerDependencies": { "solid-js": "^1.7" } }, "sha512-UwvXDVPlrrbj/9ewG9ys5uL2IO4jSiwys2KPzK4zsnAcmEl7iDafZWW1Mo4BSEWOmQCGK6IvpmGHo1aou8iOFw=="],
|
"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": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
||||||
|
|
||||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||||
@@ -3668,8 +3600,6 @@
|
|||||||
|
|
||||||
"space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
|
"space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
|
||||||
|
|
||||||
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
|
|
||||||
|
|
||||||
"sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
|
"sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
|
||||||
|
|
||||||
"sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="],
|
"sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="],
|
||||||
@@ -3772,8 +3702,6 @@
|
|||||||
|
|
||||||
"thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="],
|
"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=="],
|
"three": ["three@0.177.0", "", {}, "sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg=="],
|
||||||
|
|
||||||
"thunky": ["thunky@1.1.0", "", {}, "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="],
|
"thunky": ["thunky@1.1.0", "", {}, "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="],
|
||||||
@@ -4088,7 +4016,7 @@
|
|||||||
|
|
||||||
"@ai-sdk/vercel/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.32", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-YspqqyJPzHjqWrjt4y/Wgc2aJgCcQj5uIJgZpq2Ar/lH30cEVhgE+keePDbjKpetD9UwNggCj7u6kO3unS23OQ=="],
|
"@ai-sdk/vercel/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.32", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-YspqqyJPzHjqWrjt4y/Wgc2aJgCcQj5uIJgZpq2Ar/lH30cEVhgE+keePDbjKpetD9UwNggCj7u6kO3unS23OQ=="],
|
||||||
|
|
||||||
"@ai-sdk/xai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.30", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-thubwhRtv9uicAxSWwNpinM7hiL/0CkhL/ymPaHuKvI494J7HIzn8KQZQ2ymRz284WTIZnI7VMyyejxW4RMM6w=="],
|
"@ai-sdk/xai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.32", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-YspqqyJPzHjqWrjt4y/Wgc2aJgCcQj5uIJgZpq2Ar/lH30cEVhgE+keePDbjKpetD9UwNggCj7u6kO3unS23OQ=="],
|
||||||
|
|
||||||
"@astrojs/cloudflare/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
"@astrojs/cloudflare/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
||||||
|
|
||||||
@@ -4156,8 +4084,6 @@
|
|||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
|
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
|
||||||
|
|
||||||
"@fastify/proxy-addr/ipaddr.js": ["ipaddr.js@2.3.0", "", {}, "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg=="],
|
|
||||||
|
|
||||||
"@gitlab/gitlab-ai-provider/openai": ["openai@6.17.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-NHRpPEUPzAvFOAFs9+9pC6+HCw/iWsYsKCMPXH5Kw7BpMxqd8g/A07/1o7Gx2TWtCnzevVRyKMRFqyiHyAlqcA=="],
|
"@gitlab/gitlab-ai-provider/openai": ["openai@6.17.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-NHRpPEUPzAvFOAFs9+9pC6+HCw/iWsYsKCMPXH5Kw7BpMxqd8g/A07/1o7Gx2TWtCnzevVRyKMRFqyiHyAlqcA=="],
|
||||||
|
|
||||||
"@gitlab/gitlab-ai-provider/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
"@gitlab/gitlab-ai-provider/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
@@ -4368,8 +4294,6 @@
|
|||||||
|
|
||||||
"ai-gateway-provider/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.32", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-YspqqyJPzHjqWrjt4y/Wgc2aJgCcQj5uIJgZpq2Ar/lH30cEVhgE+keePDbjKpetD9UwNggCj7u6kO3unS23OQ=="],
|
"ai-gateway-provider/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.32", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-YspqqyJPzHjqWrjt4y/Wgc2aJgCcQj5uIJgZpq2Ar/lH30cEVhgE+keePDbjKpetD9UwNggCj7u6kO3unS23OQ=="],
|
||||||
|
|
||||||
"ai-gateway-provider/@ai-sdk/xai": ["@ai-sdk/xai@2.0.56", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.32", "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-FGlqwWc3tAYqDHE8r8hQGQLcMiPUwgz90oU2QygUH930OWtCLapFkSu114DgVaIN/qoM1DUX+inv0Ee74Fgp5g=="],
|
|
||||||
|
|
||||||
"ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
"ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||||
|
|
||||||
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
@@ -4458,8 +4382,6 @@
|
|||||||
|
|
||||||
"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=="],
|
"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=="],
|
"lightningcss/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||||
|
|
||||||
"md-to-react-email/marked": ["marked@7.0.4", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ=="],
|
"md-to-react-email/marked": ["marked@7.0.4", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ=="],
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"nodeModules": {
|
"nodeModules": {
|
||||||
"x86_64-linux": "sha256-ufEpxjmlJeft9tI+WxxO+Zbh1pdAaLOURCDBpoQqR0w=",
|
"x86_64-linux": "sha256-Uc9UFWrG9bVROt+DmXduXoY409wBBLtBe0G7R41NF8Q=",
|
||||||
"aarch64-linux": "sha256-z3K6W5oYZNUdV0rjoAZjvNQcifM5bXamLIrD+ZvJ4kA=",
|
"aarch64-linux": "sha256-KTUsuPfWaw2qb26GmEa5tcSeF3+Kx2X5ZP5DE8jJuvQ=",
|
||||||
"aarch64-darwin": "sha256-+QikplmNhxGF2Nd4L1BG/xyl+24GVhDYMTtK6xCKy/s=",
|
"aarch64-darwin": "sha256-C650/LVIoeymKnRw9lVO3f5ve9xYZPrO0vOM5pqY2nE=",
|
||||||
"x86_64-darwin": "sha256-hAcrCT2X02ymwgj/0BAmD2gF66ylGYzbfcqPta/LVEU="
|
"x86_64-darwin": "sha256-xLLI2mNn222ktx6s8rwej3rMzQGl1S1jV/NXmLFg2DU="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
"packageManager": "bun@1.3.5",
|
"packageManager": "bun@1.3.5",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
|
"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",
|
"typecheck": "bun turbo typecheck",
|
||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
"random": "echo 'Random script'",
|
"random": "echo 'Random script'",
|
||||||
@@ -100,5 +98,7 @@
|
|||||||
"@types/bun": "catalog:",
|
"@types/bun": "catalog:",
|
||||||
"@types/node": "catalog:"
|
"@types/node": "catalog:"
|
||||||
},
|
},
|
||||||
"patchedDependencies": {}
|
"patchedDependencies": {
|
||||||
|
"ghostty-web@0.3.0": "patches/ghostty-web@0.3.0.patch"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,7 @@ import {
|
|||||||
import type { createSdk } from "./utils"
|
import type { createSdk } from "./utils"
|
||||||
|
|
||||||
export async function defocus(page: Page) {
|
export async function defocus(page: Page) {
|
||||||
await page
|
await page.mouse.click(5, 5)
|
||||||
.evaluate(() => {
|
|
||||||
const el = document.activeElement
|
|
||||||
if (el instanceof HTMLElement) el.blur()
|
|
||||||
})
|
|
||||||
.catch(() => undefined)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function openPalette(page: Page) {
|
export async function openPalette(page: Page) {
|
||||||
@@ -73,50 +68,14 @@ export async function toggleSidebar(page: Page) {
|
|||||||
|
|
||||||
export async function openSidebar(page: Page) {
|
export async function openSidebar(page: Page) {
|
||||||
if (!(await isSidebarClosed(page))) return
|
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 toggleSidebar(page)
|
||||||
await expect(main).not.toHaveClass(/xl:border-l/)
|
await expect(page.locator("main")).not.toHaveClass(/xl:border-l/)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function closeSidebar(page: Page) {
|
export async function closeSidebar(page: Page) {
|
||||||
if (await isSidebarClosed(page)) return
|
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 toggleSidebar(page)
|
||||||
await expect(main).toHaveClass(/xl:border-l/)
|
await expect(page.locator("main")).toHaveClass(/xl:border-l/)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function openSettings(page: Page) {
|
export async function openSettings(page: Page) {
|
||||||
@@ -223,30 +182,13 @@ export async function hoverSessionItem(page: Page, sessionID: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function openSessionMoreMenu(page: Page, sessionID: string) {
|
export async function openSessionMoreMenu(page: Page, sessionID: string) {
|
||||||
await expect(page).toHaveURL(new RegExp(`/session/${sessionID}(?:[/?#]|$)`))
|
const sessionEl = await hoverSessionItem(page, sessionID)
|
||||||
|
|
||||||
const scroller = page.locator(".session-scroller").first()
|
const menuTrigger = sessionEl.locator(dropdownMenuTriggerSelector).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 expect(menuTrigger).toBeVisible()
|
||||||
await menuTrigger.click()
|
await menuTrigger.click()
|
||||||
|
|
||||||
|
const menu = page.locator(dropdownMenuContentSelector).first()
|
||||||
await expect(menu).toBeVisible()
|
await expect(menu).toBeVisible()
|
||||||
return menu
|
return menu
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,98 +11,57 @@ import { sessionItemSelector, inlineInputSelector } from "../selectors"
|
|||||||
|
|
||||||
const shareDisabled = process.env.OPENCODE_DISABLE_SHARE === "true" || process.env.OPENCODE_DISABLE_SHARE === "1"
|
const shareDisabled = process.env.OPENCODE_DISABLE_SHARE === "true" || process.env.OPENCODE_DISABLE_SHARE === "1"
|
||||||
|
|
||||||
type Sdk = Parameters<typeof withSession>[0]
|
test("sidebar session can be renamed", async ({ page, sdk, gotoSession }) => {
|
||||||
|
|
||||||
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 stamp = Date.now()
|
||||||
const originalTitle = `e2e rename test ${stamp}`
|
const originalTitle = `e2e rename test ${stamp}`
|
||||||
const newTitle = `e2e renamed ${stamp}`
|
const newTitle = `e2e renamed ${stamp}`
|
||||||
|
|
||||||
await withSession(sdk, originalTitle, async (session) => {
|
await withSession(sdk, originalTitle, async (session) => {
|
||||||
await seedMessage(sdk, session.id)
|
|
||||||
await gotoSession(session.id)
|
await gotoSession(session.id)
|
||||||
|
await openSidebar(page)
|
||||||
|
|
||||||
const menu = await openSessionMoreMenu(page, session.id)
|
const menu = await openSessionMoreMenu(page, session.id)
|
||||||
await clickMenuItem(menu, /rename/i)
|
await clickMenuItem(menu, /rename/i)
|
||||||
|
|
||||||
const input = page.locator(".session-scroller").locator(inlineInputSelector).first()
|
const input = page.locator(sessionItemSelector(session.id)).locator(inlineInputSelector).first()
|
||||||
await expect(input).toBeVisible()
|
await expect(input).toBeVisible()
|
||||||
await input.fill(newTitle)
|
await input.fill(newTitle)
|
||||||
await input.press("Enter")
|
await input.press("Enter")
|
||||||
|
|
||||||
await expect(page.getByRole("heading", { level: 1 }).first()).toContainText(newTitle)
|
await expect(page.locator(sessionItemSelector(session.id)).locator("a").first()).toContainText(newTitle)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test("session can be archived via header menu", async ({ page, sdk, gotoSession }) => {
|
test("sidebar session can be archived", async ({ page, sdk, gotoSession }) => {
|
||||||
const stamp = Date.now()
|
const stamp = Date.now()
|
||||||
const title = `e2e archive test ${stamp}`
|
const title = `e2e archive test ${stamp}`
|
||||||
|
|
||||||
await withSession(sdk, title, async (session) => {
|
await withSession(sdk, title, async (session) => {
|
||||||
await seedMessage(sdk, session.id)
|
|
||||||
await gotoSession(session.id)
|
await gotoSession(session.id)
|
||||||
|
await openSidebar(page)
|
||||||
|
|
||||||
|
const sessionEl = page.locator(sessionItemSelector(session.id))
|
||||||
const menu = await openSessionMoreMenu(page, session.id)
|
const menu = await openSessionMoreMenu(page, session.id)
|
||||||
await clickMenuItem(menu, /archive/i)
|
await clickMenuItem(menu, /archive/i)
|
||||||
|
|
||||||
await expect
|
await expect(sessionEl).not.toBeVisible()
|
||||||
.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("session can be deleted via header menu", async ({ page, sdk, gotoSession }) => {
|
test("sidebar session can be deleted", async ({ page, sdk, gotoSession }) => {
|
||||||
const stamp = Date.now()
|
const stamp = Date.now()
|
||||||
const title = `e2e delete test ${stamp}`
|
const title = `e2e delete test ${stamp}`
|
||||||
|
|
||||||
await withSession(sdk, title, async (session) => {
|
await withSession(sdk, title, async (session) => {
|
||||||
await seedMessage(sdk, session.id)
|
|
||||||
await gotoSession(session.id)
|
await gotoSession(session.id)
|
||||||
|
await openSidebar(page)
|
||||||
|
|
||||||
|
const sessionEl = page.locator(sessionItemSelector(session.id))
|
||||||
const menu = await openSessionMoreMenu(page, session.id)
|
const menu = await openSessionMoreMenu(page, session.id)
|
||||||
await clickMenuItem(menu, /delete/i)
|
await clickMenuItem(menu, /delete/i)
|
||||||
await confirmDialog(page, /delete/i)
|
await confirmDialog(page, /delete/i)
|
||||||
|
|
||||||
await expect
|
await expect(sessionEl).not.toBeVisible()
|
||||||
.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)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -113,7 +72,6 @@ test("session can be shared and unshared via header button", async ({ page, sdk,
|
|||||||
const title = `e2e share test ${stamp}`
|
const title = `e2e share test ${stamp}`
|
||||||
|
|
||||||
await withSession(sdk, title, async (session) => {
|
await withSession(sdk, title, async (session) => {
|
||||||
await seedMessage(sdk, session.id)
|
|
||||||
await gotoSession(session.id)
|
await gotoSession(session.id)
|
||||||
|
|
||||||
const { rightSection, popoverBody } = await openSharePopover(page)
|
const { rightSection, popoverBody } = await openSharePopover(page)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { test, expect } from "../fixtures"
|
import { test, expect } from "../fixtures"
|
||||||
import { openStatusPopover } from "../actions"
|
import { openStatusPopover, defocus } from "../actions"
|
||||||
|
|
||||||
test("status popover opens and shows tabs", async ({ page, gotoSession }) => {
|
test("status popover opens and shows tabs", async ({ page, gotoSession }) => {
|
||||||
await gotoSession()
|
await gotoSession()
|
||||||
@@ -88,7 +88,7 @@ test("status popover closes when clicking outside", async ({ page, gotoSession }
|
|||||||
const { popoverBody } = await openStatusPopover(page)
|
const { popoverBody } = await openStatusPopover(page)
|
||||||
await expect(popoverBody).toBeVisible()
|
await expect(popoverBody).toBeVisible()
|
||||||
|
|
||||||
await page.getByRole("main").click({ position: { x: 5, y: 5 } })
|
await defocus(page)
|
||||||
|
|
||||||
await expect(popoverBody).toHaveCount(0)
|
await expect(popoverBody).toHaveCount(0)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/app",
|
"name": "@opencode-ai/app",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
"@thisbeyond/solid-dnd": "0.7.5",
|
"@thisbeyond/solid-dnd": "0.7.5",
|
||||||
"diff": "catalog:",
|
"diff": "catalog:",
|
||||||
"fuzzysort": "catalog:",
|
"fuzzysort": "catalog:",
|
||||||
"ghostty-web": "0.4.0",
|
"ghostty-web": "0.3.0",
|
||||||
"luxon": "catalog:",
|
"luxon": "catalog:",
|
||||||
"marked": "catalog:",
|
"marked": "catalog:",
|
||||||
"marked-shiki": "catalog:",
|
"marked-shiki": "catalog:",
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (store.authorization?.method === "code" && store.authorization?.url) {
|
if (store.authorization?.method === "code" && store.authorization?.url) {
|
||||||
platform.openLink(store.authorization.url)
|
void platform.openLink(store.authorization.url).catch(() => undefined)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -396,7 +396,7 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
onMount(() => {
|
onMount(() => {
|
||||||
void (async () => {
|
void (async () => {
|
||||||
if (store.authorization?.url) {
|
if (store.authorization?.url) {
|
||||||
platform.openLink(store.authorization.url)
|
void platform.openLink(store.authorization.url).catch(() => undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await globalSDK.client.provider.oauth
|
const result = await globalSDK.client.provider.oauth
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
|
|||||||
const filesOnly = () => props.mode === "files"
|
const filesOnly = () => props.mode === "files"
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
||||||
const tabs = createMemo(() => layout.tabs(sessionKey))
|
const tabs = createMemo(() => layout.tabs(sessionKey))
|
||||||
const view = createMemo(() => layout.view(sessionKey))
|
|
||||||
const state = { cleanup: undefined as (() => void) | void, committed: false }
|
const state = { cleanup: undefined as (() => void) | void, committed: false }
|
||||||
const [grouped, setGrouped] = createSignal(false)
|
const [grouped, setGrouped] = createSignal(false)
|
||||||
const common = [
|
const common = [
|
||||||
@@ -283,7 +282,6 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
|
|||||||
const value = file.tab(path)
|
const value = file.tab(path)
|
||||||
tabs().open(value)
|
tabs().open(value)
|
||||||
file.load(path)
|
file.load(path)
|
||||||
if (!view().reviewPanel.opened()) view().reviewPanel.open()
|
|
||||||
layout.fileTree.open()
|
layout.fileTree.open()
|
||||||
layout.fileTree.setTab("all")
|
layout.fileTree.setTab("all")
|
||||||
props.onOpenFile?.(path)
|
props.onOpenFile?.(path)
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ export default function FileTree(props: {
|
|||||||
: kind === "del"
|
: kind === "del"
|
||||||
? "color: var(--icon-diff-delete-base)"
|
? "color: var(--icon-diff-delete-base)"
|
||||||
: kind === "mix"
|
: kind === "mix"
|
||||||
? "color: var(--icon-warning-active)"
|
? "color: var(--icon-diff-modified-base)"
|
||||||
: undefined
|
: undefined
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
@@ -268,7 +268,7 @@ export default function FileTree(props: {
|
|||||||
? "color: var(--icon-diff-add-base)"
|
? "color: var(--icon-diff-add-base)"
|
||||||
: kind === "del"
|
: kind === "del"
|
||||||
? "color: var(--icon-diff-delete-base)"
|
? "color: var(--icon-diff-delete-base)"
|
||||||
: "color: var(--icon-warning-active)"
|
: "color: var(--icon-diff-modified-base)"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span class="shrink-0 w-4 text-center text-12-medium" style={color}>
|
<span class="shrink-0 w-4 text-center text-12-medium" style={color}>
|
||||||
@@ -283,7 +283,7 @@ export default function FileTree(props: {
|
|||||||
? "background-color: var(--icon-diff-add-base)"
|
? "background-color: var(--icon-diff-add-base)"
|
||||||
: kind === "del"
|
: kind === "del"
|
||||||
? "background-color: var(--icon-diff-delete-base)"
|
? "background-color: var(--icon-diff-delete-base)"
|
||||||
: "background-color: var(--icon-warning-active)"
|
: "background-color: var(--icon-diff-modified-base)"
|
||||||
|
|
||||||
return <div class="shrink-0 size-1.5 mr-1.5 rounded-full" style={color} />
|
return <div class="shrink-0 size-1.5 mr-1.5 rounded-full" style={color} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ export function Link(props: LinkProps) {
|
|||||||
const [local, rest] = splitProps(props, ["href", "children"])
|
const [local, rest] = splitProps(props, ["href", "children"])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button class="text-text-strong underline" onClick={() => platform.openLink(local.href)} {...rest}>
|
<button
|
||||||
|
class="text-text-strong underline"
|
||||||
|
onClick={() => void platform.openLink(local.href).catch(() => undefined)}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
{local.children}
|
{local.children}
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -172,7 +172,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
|
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
||||||
const tabs = createMemo(() => layout.tabs(sessionKey))
|
const tabs = createMemo(() => layout.tabs(sessionKey))
|
||||||
const view = createMemo(() => layout.view(sessionKey))
|
|
||||||
|
|
||||||
const commentInReview = (path: string) => {
|
const commentInReview = (path: string) => {
|
||||||
const sessionID = params.id
|
const sessionID = params.id
|
||||||
@@ -191,14 +190,12 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
|
|
||||||
const wantsReview = item.commentOrigin === "review" || (item.commentOrigin !== "file" && commentInReview(item.path))
|
const wantsReview = item.commentOrigin === "review" || (item.commentOrigin !== "file" && commentInReview(item.path))
|
||||||
if (wantsReview) {
|
if (wantsReview) {
|
||||||
if (!view().reviewPanel.opened()) view().reviewPanel.open()
|
|
||||||
layout.fileTree.open()
|
layout.fileTree.open()
|
||||||
layout.fileTree.setTab("changes")
|
layout.fileTree.setTab("changes")
|
||||||
requestAnimationFrame(() => comments.setFocus(focus))
|
requestAnimationFrame(() => comments.setFocus(focus))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!view().reviewPanel.opened()) view().reviewPanel.open()
|
|
||||||
layout.fileTree.open()
|
layout.fileTree.open()
|
||||||
layout.fileTree.setTab("all")
|
layout.fileTree.setTab("all")
|
||||||
const tab = files.tab(item.path)
|
const tab = files.tab(item.path)
|
||||||
@@ -1223,10 +1220,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
})
|
})
|
||||||
return undefined
|
return undefined
|
||||||
})
|
})
|
||||||
if (session) {
|
if (session) navigate(`/${base64Encode(sessionDirectory)}/session/${session.id}`)
|
||||||
layout.handoff.setTabs(base64Encode(sessionDirectory), session.id)
|
|
||||||
navigate(`/${base64Encode(sessionDirectory)}/session/${session.id}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!session) return
|
if (!session) return
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ export function SessionContextUsage(props: SessionContextUsageProps) {
|
|||||||
const variant = createMemo(() => props.variant ?? "button")
|
const variant = createMemo(() => props.variant ?? "button")
|
||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
||||||
const tabs = createMemo(() => layout.tabs(sessionKey))
|
const tabs = createMemo(() => layout.tabs(sessionKey))
|
||||||
const view = createMemo(() => layout.view(sessionKey))
|
|
||||||
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
|
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
|
||||||
|
|
||||||
const usd = createMemo(
|
const usd = createMemo(
|
||||||
@@ -58,7 +57,6 @@ export function SessionContextUsage(props: SessionContextUsageProps) {
|
|||||||
|
|
||||||
const openContext = () => {
|
const openContext = () => {
|
||||||
if (!params.id) return
|
if (!params.id) return
|
||||||
if (!view().reviewPanel.opened()) view().reviewPanel.open()
|
|
||||||
layout.fileTree.open()
|
layout.fileTree.open()
|
||||||
layout.fileTree.setTab("all")
|
layout.fileTree.setTab("all")
|
||||||
tabs().open("context")
|
tabs().open("context")
|
||||||
|
|||||||
@@ -6,31 +6,26 @@ import { useLayout } from "@/context/layout"
|
|||||||
import { useCommand } from "@/context/command"
|
import { useCommand } from "@/context/command"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
import { usePlatform } from "@/context/platform"
|
import { usePlatform } from "@/context/platform"
|
||||||
import { useServer } from "@/context/server"
|
|
||||||
import { useSync } from "@/context/sync"
|
import { useSync } from "@/context/sync"
|
||||||
import { useGlobalSDK } from "@/context/global-sdk"
|
import { useGlobalSDK } from "@/context/global-sdk"
|
||||||
import { getFilename } from "@opencode-ai/util/path"
|
import { getFilename } from "@opencode-ai/util/path"
|
||||||
import { decode64 } from "@/utils/base64"
|
import { decode64 } from "@/utils/base64"
|
||||||
import { Persist, persisted } from "@/utils/persist"
|
|
||||||
|
|
||||||
import { Icon } from "@opencode-ai/ui/icon"
|
import { Icon } from "@opencode-ai/ui/icon"
|
||||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
import { AppIcon } from "@opencode-ai/ui/app-icon"
|
|
||||||
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
|
|
||||||
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
||||||
import { Popover } from "@opencode-ai/ui/popover"
|
import { Popover } from "@opencode-ai/ui/popover"
|
||||||
import { TextField } from "@opencode-ai/ui/text-field"
|
import { TextField } from "@opencode-ai/ui/text-field"
|
||||||
import { Keybind } from "@opencode-ai/ui/keybind"
|
import { Keybind } from "@opencode-ai/ui/keybind"
|
||||||
import { showToast } from "@opencode-ai/ui/toast"
|
|
||||||
import { StatusPopover } from "../status-popover"
|
import { StatusPopover } from "../status-popover"
|
||||||
|
import { SessionOpenMenu } from "./session-open-menu"
|
||||||
|
|
||||||
export function SessionHeader() {
|
export function SessionHeader() {
|
||||||
const globalSDK = useGlobalSDK()
|
const globalSDK = useGlobalSDK()
|
||||||
const layout = useLayout()
|
const layout = useLayout()
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const command = useCommand()
|
const command = useCommand()
|
||||||
const server = useServer()
|
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
const platform = usePlatform()
|
const platform = usePlatform()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
@@ -54,117 +49,6 @@ export function SessionHeader() {
|
|||||||
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
|
||||||
const view = createMemo(() => layout.view(sessionKey))
|
const view = createMemo(() => layout.view(sessionKey))
|
||||||
|
|
||||||
const OPEN_APPS = [
|
|
||||||
"vscode",
|
|
||||||
"cursor",
|
|
||||||
"zed",
|
|
||||||
"textmate",
|
|
||||||
"antigravity",
|
|
||||||
"finder",
|
|
||||||
"terminal",
|
|
||||||
"iterm2",
|
|
||||||
"ghostty",
|
|
||||||
"xcode",
|
|
||||||
"android-studio",
|
|
||||||
"powershell",
|
|
||||||
] as const
|
|
||||||
type OpenApp = (typeof OPEN_APPS)[number]
|
|
||||||
|
|
||||||
const os = createMemo<"macos" | "windows" | "linux" | "unknown">(() => {
|
|
||||||
if (platform.platform === "desktop" && platform.os) return platform.os
|
|
||||||
if (typeof navigator !== "object") return "unknown"
|
|
||||||
const value = navigator.platform || navigator.userAgent
|
|
||||||
if (/Mac/i.test(value)) return "macos"
|
|
||||||
if (/Win/i.test(value)) return "windows"
|
|
||||||
if (/Linux/i.test(value)) return "linux"
|
|
||||||
return "unknown"
|
|
||||||
})
|
|
||||||
|
|
||||||
const options = createMemo(() => {
|
|
||||||
if (os() === "macos") {
|
|
||||||
return [
|
|
||||||
{ id: "vscode", label: "VS Code", icon: "vscode", openWith: "Visual Studio Code" },
|
|
||||||
{ id: "cursor", label: "Cursor", icon: "cursor", openWith: "Cursor" },
|
|
||||||
{ id: "zed", label: "Zed", icon: "zed", openWith: "Zed" },
|
|
||||||
{ id: "textmate", label: "TextMate", icon: "textmate", openWith: "TextMate" },
|
|
||||||
{ id: "antigravity", label: "Antigravity", icon: "antigravity", openWith: "Antigravity" },
|
|
||||||
{ id: "finder", label: "Finder", icon: "finder" },
|
|
||||||
{ id: "terminal", label: "Terminal", icon: "terminal", openWith: "Terminal" },
|
|
||||||
{ id: "iterm2", label: "iTerm2", icon: "iterm2", openWith: "iTerm" },
|
|
||||||
{ id: "ghostty", label: "Ghostty", icon: "ghostty", openWith: "Ghostty" },
|
|
||||||
{ id: "xcode", label: "Xcode", icon: "xcode", openWith: "Xcode" },
|
|
||||||
{ id: "android-studio", label: "Android Studio", icon: "android-studio", openWith: "Android Studio" },
|
|
||||||
] as const
|
|
||||||
}
|
|
||||||
|
|
||||||
if (os() === "windows") {
|
|
||||||
return [
|
|
||||||
{ id: "vscode", label: "VS Code", icon: "vscode", openWith: "code" },
|
|
||||||
{ id: "cursor", label: "Cursor", icon: "cursor", openWith: "cursor" },
|
|
||||||
{ id: "zed", label: "Zed", icon: "zed", openWith: "zed" },
|
|
||||||
{ id: "finder", label: "File Explorer", icon: "finder" },
|
|
||||||
{ id: "powershell", label: "PowerShell", icon: "powershell", openWith: "powershell" },
|
|
||||||
] as const
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
{ id: "vscode", label: "VS Code", icon: "vscode", openWith: "code" },
|
|
||||||
{ id: "cursor", label: "Cursor", icon: "cursor", openWith: "cursor" },
|
|
||||||
{ id: "zed", label: "Zed", icon: "zed", openWith: "zed" },
|
|
||||||
{ id: "finder", label: "File Manager", icon: "finder" },
|
|
||||||
] as const
|
|
||||||
})
|
|
||||||
|
|
||||||
const [prefs, setPrefs] = persisted(Persist.global("open.app"), createStore({ app: "finder" as OpenApp }))
|
|
||||||
|
|
||||||
const canOpen = createMemo(() => platform.platform === "desktop" && !!platform.openPath && server.isLocal())
|
|
||||||
const current = createMemo(() => options().find((o) => o.id === prefs.app) ?? options()[0])
|
|
||||||
|
|
||||||
createEffect(() => {
|
|
||||||
if (platform.platform !== "desktop") return
|
|
||||||
const value = prefs.app
|
|
||||||
if (options().some((o) => o.id === value)) return
|
|
||||||
setPrefs("app", options()[0]?.id ?? "finder")
|
|
||||||
})
|
|
||||||
|
|
||||||
const openDir = (app: OpenApp) => {
|
|
||||||
const directory = projectDirectory()
|
|
||||||
if (!directory) return
|
|
||||||
if (!canOpen()) return
|
|
||||||
|
|
||||||
const item = options().find((o) => o.id === app)
|
|
||||||
const openWith = item && "openWith" in item ? item.openWith : undefined
|
|
||||||
Promise.resolve(platform.openPath?.(directory, openWith)).catch((err: unknown) => {
|
|
||||||
showToast({
|
|
||||||
variant: "error",
|
|
||||||
title: language.t("common.requestFailed"),
|
|
||||||
description: err instanceof Error ? err.message : String(err),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const copyPath = () => {
|
|
||||||
const directory = projectDirectory()
|
|
||||||
if (!directory) return
|
|
||||||
navigator.clipboard
|
|
||||||
.writeText(directory)
|
|
||||||
.then(() => {
|
|
||||||
showToast({
|
|
||||||
variant: "success",
|
|
||||||
icon: "circle-check",
|
|
||||||
title: language.t("session.share.copy.copied"),
|
|
||||||
description: directory,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch((err: unknown) => {
|
|
||||||
showToast({
|
|
||||||
variant: "error",
|
|
||||||
title: language.t("common.requestFailed"),
|
|
||||||
description: err instanceof Error ? err.message : String(err),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const [state, setState] = createStore({
|
const [state, setState] = createStore({
|
||||||
share: false,
|
share: false,
|
||||||
unshare: false,
|
unshare: false,
|
||||||
@@ -234,7 +118,7 @@ export function SessionHeader() {
|
|||||||
function viewShare() {
|
function viewShare() {
|
||||||
const url = shareUrl()
|
const url = shareUrl()
|
||||||
if (!url) return
|
if (!url) return
|
||||||
platform.openLink(url)
|
void platform.openLink(url).catch(() => undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
const centerMount = createMemo(() => document.getElementById("opencode-titlebar-center"))
|
const centerMount = createMemo(() => document.getElementById("opencode-titlebar-center"))
|
||||||
@@ -267,76 +151,7 @@ export function SessionHeader() {
|
|||||||
{(mount) => (
|
{(mount) => (
|
||||||
<Portal mount={mount()}>
|
<Portal mount={mount()}>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<Show when={projectDirectory()}>
|
<SessionOpenMenu dir={projectDirectory()} />
|
||||||
<Show
|
|
||||||
when={canOpen()}
|
|
||||||
fallback={
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
class="rounded-sm h-[24px] py-1.5 pr-3 pl-2 gap-2 border-none shadow-none"
|
|
||||||
onClick={copyPath}
|
|
||||||
aria-label={language.t("session.header.open.copyPath")}
|
|
||||||
>
|
|
||||||
<Icon name="copy" size="small" class="text-icon-base" />
|
|
||||||
<span class="text-12-regular text-text-strong">{language.t("session.header.open.copyPath")}</span>
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div class="flex items-center">
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
class="rounded-sm h-[24px] py-1.5 pr-3 pl-2 gap-2 border-none shadow-none rounded-r-none"
|
|
||||||
onClick={() => openDir(current().id)}
|
|
||||||
aria-label={language.t("session.header.open.ariaLabel", { app: current().label })}
|
|
||||||
>
|
|
||||||
<AppIcon id={current().icon} class="size-5" />
|
|
||||||
<span class="text-12-regular text-text-strong">
|
|
||||||
{language.t("session.header.open.action", { app: current().label })}
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenu.Trigger
|
|
||||||
as={IconButton}
|
|
||||||
icon="chevron-down"
|
|
||||||
variant="ghost"
|
|
||||||
class="rounded-sm h-[24px] w-auto px-1.5 border-none shadow-none rounded-l-none data-[expanded]:bg-surface-raised-base-active"
|
|
||||||
aria-label={language.t("session.header.open.menu")}
|
|
||||||
/>
|
|
||||||
<DropdownMenu.Portal>
|
|
||||||
<DropdownMenu.Content placement="bottom-end" gutter={6}>
|
|
||||||
<DropdownMenu.Group>
|
|
||||||
<DropdownMenu.GroupLabel>{language.t("session.header.openIn")}</DropdownMenu.GroupLabel>
|
|
||||||
<DropdownMenu.RadioGroup
|
|
||||||
value={prefs.app}
|
|
||||||
onChange={(value) => {
|
|
||||||
if (!OPEN_APPS.includes(value as OpenApp)) return
|
|
||||||
setPrefs("app", value as OpenApp)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{options().map((o) => (
|
|
||||||
<DropdownMenu.RadioItem value={o.id} onSelect={() => openDir(o.id)}>
|
|
||||||
<AppIcon id={o.icon} class="size-5" />
|
|
||||||
<DropdownMenu.ItemLabel>{o.label}</DropdownMenu.ItemLabel>
|
|
||||||
<DropdownMenu.ItemIndicator>
|
|
||||||
<Icon name="check-small" size="small" class="text-icon-weak" />
|
|
||||||
</DropdownMenu.ItemIndicator>
|
|
||||||
</DropdownMenu.RadioItem>
|
|
||||||
))}
|
|
||||||
</DropdownMenu.RadioGroup>
|
|
||||||
</DropdownMenu.Group>
|
|
||||||
<DropdownMenu.Separator />
|
|
||||||
<DropdownMenu.Item onSelect={copyPath}>
|
|
||||||
<Icon name="copy" size="small" class="text-icon-weak" />
|
|
||||||
<DropdownMenu.ItemLabel>
|
|
||||||
{language.t("session.header.open.copyPath")}
|
|
||||||
</DropdownMenu.ItemLabel>
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
</DropdownMenu.Content>
|
|
||||||
</DropdownMenu.Portal>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
</Show>
|
|
||||||
<StatusPopover />
|
<StatusPopover />
|
||||||
<Show when={showShare()}>
|
<Show when={showShare()}>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
@@ -470,57 +285,27 @@ export function SessionHeader() {
|
|||||||
<TooltipKeybind title={language.t("command.review.toggle")} keybind={command.keybind("review.toggle")}>
|
<TooltipKeybind title={language.t("command.review.toggle")} keybind={command.keybind("review.toggle")}>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="group/review-toggle size-6 p-0"
|
class="group/file-tree-toggle size-6 p-0"
|
||||||
onClick={() => view().reviewPanel.toggle()}
|
onClick={() => layout.fileTree.toggle()}
|
||||||
aria-label={language.t("command.review.toggle")}
|
aria-label={language.t("command.review.toggle")}
|
||||||
aria-expanded={view().reviewPanel.opened()}
|
aria-expanded={layout.fileTree.opened()}
|
||||||
aria-controls="review-panel"
|
aria-controls="review-panel"
|
||||||
>
|
>
|
||||||
<div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
|
<div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
|
||||||
<Icon
|
<Icon
|
||||||
size="small"
|
size="small"
|
||||||
name={view().reviewPanel.opened() ? "layout-right-full" : "layout-right"}
|
name={layout.fileTree.opened() ? "layout-right-full" : "layout-right"}
|
||||||
class="group-hover/review-toggle:hidden"
|
class="group-hover/file-tree-toggle:hidden"
|
||||||
/>
|
/>
|
||||||
<Icon
|
<Icon
|
||||||
size="small"
|
size="small"
|
||||||
name="layout-right-partial"
|
name="layout-right-partial"
|
||||||
class="hidden group-hover/review-toggle:inline-block"
|
class="hidden group-hover/file-tree-toggle:inline-block"
|
||||||
/>
|
/>
|
||||||
<Icon
|
<Icon
|
||||||
size="small"
|
size="small"
|
||||||
name={view().reviewPanel.opened() ? "layout-right" : "layout-right-full"}
|
name={layout.fileTree.opened() ? "layout-right" : "layout-right-full"}
|
||||||
class="hidden group-active/review-toggle:inline-block"
|
class="hidden group-active/file-tree-toggle:inline-block"
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Button>
|
|
||||||
</TooltipKeybind>
|
|
||||||
</div>
|
|
||||||
<div class="hidden md:block shrink-0">
|
|
||||||
<TooltipKeybind
|
|
||||||
title={language.t("command.fileTree.toggle")}
|
|
||||||
keybind={command.keybind("fileTree.toggle")}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
class="group/file-tree-toggle size-6 p-0"
|
|
||||||
onClick={() => {
|
|
||||||
const opening = !layout.fileTree.opened()
|
|
||||||
if (opening && !view().reviewPanel.opened()) view().reviewPanel.open()
|
|
||||||
layout.fileTree.toggle()
|
|
||||||
}}
|
|
||||||
aria-label={language.t("command.fileTree.toggle")}
|
|
||||||
aria-expanded={layout.fileTree.opened()}
|
|
||||||
aria-controls="file-tree-panel"
|
|
||||||
>
|
|
||||||
<div class="relative flex items-center justify-center size-4">
|
|
||||||
<Icon
|
|
||||||
size="small"
|
|
||||||
name="bullet-list"
|
|
||||||
classList={{
|
|
||||||
"text-icon-strong": layout.fileTree.opened(),
|
|
||||||
"text-icon-weak": !layout.fileTree.opened(),
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import { createMemo, Show } from "solid-js"
|
||||||
|
import { useLanguage } from "@/context/language"
|
||||||
|
import { usePlatform } from "@/context/platform"
|
||||||
|
import { useServer } from "@/context/server"
|
||||||
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
|
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
|
||||||
|
import { FileTypeIcon } from "@opencode-ai/ui/file-type-icon"
|
||||||
|
import { Icon } from "@opencode-ai/ui/icon"
|
||||||
|
import { showToast } from "@opencode-ai/ui/toast"
|
||||||
|
|
||||||
|
export function SessionOpenMenu(props: { dir: string }) {
|
||||||
|
const platform = usePlatform()
|
||||||
|
const server = useServer()
|
||||||
|
const language = useLanguage()
|
||||||
|
|
||||||
|
const enabled = createMemo(
|
||||||
|
() => platform.platform === "desktop" && platform.os === "macos" && server.isLocal() && !!props.dir,
|
||||||
|
)
|
||||||
|
|
||||||
|
const open = (app?: string) => {
|
||||||
|
if (!props.dir) return
|
||||||
|
void platform.openLink(props.dir, app).catch((error) => {
|
||||||
|
showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: language.t("common.requestFailed"),
|
||||||
|
description: error instanceof Error ? error.message : String(error),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const copy = () => {
|
||||||
|
if (!props.dir) return
|
||||||
|
navigator.clipboard
|
||||||
|
.writeText(props.dir)
|
||||||
|
.then(() => {
|
||||||
|
showToast({
|
||||||
|
variant: "success",
|
||||||
|
icon: "check",
|
||||||
|
title: language.t("session.header.copyPath.copied"),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: language.t("session.header.copyPath.copyFailed"),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu modal={false}>
|
||||||
|
<DropdownMenu.Trigger
|
||||||
|
as={Button}
|
||||||
|
variant="ghost"
|
||||||
|
icon="folder"
|
||||||
|
class="rounded-sm h-[24px] py-1.5 pr-2 pl-2 gap-1.5 border-none shadow-none data-[expanded]:bg-surface-raised-base-active"
|
||||||
|
aria-label={language.t("session.header.open")}
|
||||||
|
>
|
||||||
|
<span class="text-12-regular text-text-strong">{language.t("session.header.open")}</span>
|
||||||
|
<Icon name="chevron-down" size="small" class="icon-base" />
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
<DropdownMenu.Portal>
|
||||||
|
<DropdownMenu.Content class="mt-1 w-60">
|
||||||
|
<Show when={enabled()}>
|
||||||
|
<DropdownMenu.Group>
|
||||||
|
<DropdownMenu.GroupLabel>{language.t("session.header.openIn")}</DropdownMenu.GroupLabel>
|
||||||
|
<DropdownMenu.Item onSelect={() => open("Visual Studio Code")}>
|
||||||
|
<FileTypeIcon id="Vscode" class="size-5" />
|
||||||
|
<DropdownMenu.ItemLabel>VS Code</DropdownMenu.ItemLabel>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item onSelect={() => open("Cursor")}>
|
||||||
|
<FileTypeIcon id="Cursor" class="size-5" />
|
||||||
|
<DropdownMenu.ItemLabel>Cursor</DropdownMenu.ItemLabel>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item onSelect={() => open("Finder")}>
|
||||||
|
<Icon name="folder" size="small" class="icon-base shrink-0" />
|
||||||
|
<DropdownMenu.ItemLabel>Finder</DropdownMenu.ItemLabel>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item onSelect={() => open("Terminal")}>
|
||||||
|
<FileTypeIcon id="Console" class="size-5" />
|
||||||
|
<DropdownMenu.ItemLabel>Terminal</DropdownMenu.ItemLabel>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item onSelect={() => open("iTerm")}>
|
||||||
|
<FileTypeIcon id="Console" class="size-5" />
|
||||||
|
<DropdownMenu.ItemLabel>iTerm2</DropdownMenu.ItemLabel>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item onSelect={() => open("Ghostty")}>
|
||||||
|
<FileTypeIcon id="Console" class="size-5" />
|
||||||
|
<DropdownMenu.ItemLabel>Ghostty</DropdownMenu.ItemLabel>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item onSelect={() => open("Xcode")}>
|
||||||
|
<FileTypeIcon id="Swift" class="size-5" />
|
||||||
|
<DropdownMenu.ItemLabel>Xcode</DropdownMenu.ItemLabel>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item onSelect={() => open("Android Studio")}>
|
||||||
|
<FileTypeIcon id="Android" class="size-5" />
|
||||||
|
<DropdownMenu.ItemLabel>Android Studio</DropdownMenu.ItemLabel>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</DropdownMenu.Group>
|
||||||
|
<DropdownMenu.Separator />
|
||||||
|
</Show>
|
||||||
|
<DropdownMenu.Item onSelect={copy}>
|
||||||
|
<Icon name="copy" size="small" class="icon-base shrink-0" />
|
||||||
|
<DropdownMenu.ItemLabel>{language.t("session.header.copyPath")}</DropdownMenu.ItemLabel>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Portal>
|
||||||
|
</DropdownMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { Ghostty, Terminal as Term, FitAddon } from "ghostty-web"
|
import type { Ghostty, Terminal as Term, FitAddon } from "ghostty-web"
|
||||||
import { ComponentProps, createEffect, createSignal, onCleanup, onMount, splitProps } from "solid-js"
|
import { ComponentProps, createEffect, createSignal, onCleanup, onMount, splitProps } from "solid-js"
|
||||||
import { usePlatform } from "@/context/platform"
|
|
||||||
import { useSDK } from "@/context/sdk"
|
import { useSDK } from "@/context/sdk"
|
||||||
import { monoFontFamily, useSettings } from "@/context/settings"
|
import { monoFontFamily, useSettings } from "@/context/settings"
|
||||||
import { SerializeAddon } from "@/addons/serialize"
|
import { SerializeAddon } from "@/addons/serialize"
|
||||||
@@ -53,7 +52,6 @@ const DEFAULT_TERMINAL_COLORS: Record<"light" | "dark", TerminalColors> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Terminal = (props: TerminalProps) => {
|
export const Terminal = (props: TerminalProps) => {
|
||||||
const platform = usePlatform()
|
|
||||||
const sdk = useSDK()
|
const sdk = useSDK()
|
||||||
const settings = useSettings()
|
const settings = useSettings()
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
@@ -70,7 +68,6 @@ export const Terminal = (props: TerminalProps) => {
|
|||||||
let handleTextareaBlur: () => void
|
let handleTextareaBlur: () => void
|
||||||
let disposed = false
|
let disposed = false
|
||||||
const cleanups: VoidFunction[] = []
|
const cleanups: VoidFunction[] = []
|
||||||
let tail = local.pty.tail ?? ""
|
|
||||||
|
|
||||||
const cleanup = () => {
|
const cleanup = () => {
|
||||||
if (!cleanups.length) return
|
if (!cleanups.length) return
|
||||||
@@ -138,22 +135,6 @@ export const Terminal = (props: TerminalProps) => {
|
|||||||
focusTerminal()
|
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(() => {
|
onMount(() => {
|
||||||
const run = async () => {
|
const run = async () => {
|
||||||
const loaded = await loadGhostty()
|
const loaded = await loadGhostty()
|
||||||
@@ -186,7 +167,6 @@ export const Terminal = (props: TerminalProps) => {
|
|||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontFamily: monoFontFamily(settings.appearance.font()),
|
fontFamily: monoFontFamily(settings.appearance.font()),
|
||||||
allowTransparency: true,
|
allowTransparency: true,
|
||||||
convertEol: true,
|
|
||||||
theme: terminalColors(),
|
theme: terminalColors(),
|
||||||
scrollback: 10_000,
|
scrollback: 10_000,
|
||||||
ghostty: g,
|
ghostty: g,
|
||||||
@@ -257,13 +237,9 @@ export const Terminal = (props: TerminalProps) => {
|
|||||||
serializeAddon = serializer
|
serializeAddon = serializer
|
||||||
|
|
||||||
t.open(container)
|
t.open(container)
|
||||||
|
|
||||||
container.addEventListener("pointerdown", handlePointerDown)
|
container.addEventListener("pointerdown", handlePointerDown)
|
||||||
cleanups.push(() => container.removeEventListener("pointerdown", handlePointerDown))
|
cleanups.push(() => container.removeEventListener("pointerdown", handlePointerDown))
|
||||||
|
|
||||||
container.addEventListener("click", handleLinkClick, { capture: true })
|
|
||||||
cleanups.push(() => container.removeEventListener("click", handleLinkClick, { capture: true }))
|
|
||||||
|
|
||||||
handleTextareaFocus = () => {
|
handleTextareaFocus = () => {
|
||||||
t.options.cursorBlink = true
|
t.options.cursorBlink = true
|
||||||
}
|
}
|
||||||
@@ -278,11 +254,15 @@ export const Terminal = (props: TerminalProps) => {
|
|||||||
|
|
||||||
focusTerminal()
|
focusTerminal()
|
||||||
|
|
||||||
fit.fit()
|
|
||||||
|
|
||||||
if (local.pty.buffer) {
|
if (local.pty.buffer) {
|
||||||
|
if (local.pty.rows && local.pty.cols) {
|
||||||
|
t.resize(local.pty.cols, local.pty.rows)
|
||||||
|
}
|
||||||
t.write(local.pty.buffer, () => {
|
t.write(local.pty.buffer, () => {
|
||||||
if (local.pty.scrollY) t.scrollToLine(local.pty.scrollY)
|
if (local.pty.scrollY) {
|
||||||
|
t.scrollToLine(local.pty.scrollY)
|
||||||
|
}
|
||||||
|
fitAddon.fit()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,19 +300,6 @@ export const Terminal = (props: TerminalProps) => {
|
|||||||
// console.log("Scroll position:", ydisp)
|
// console.log("Scroll position:", ydisp)
|
||||||
// })
|
// })
|
||||||
|
|
||||||
const limit = 16_384
|
|
||||||
const seed = tail
|
|
||||||
let sync = !!seed
|
|
||||||
|
|
||||||
const overlap = (data: string) => {
|
|
||||||
if (!seed) return 0
|
|
||||||
const max = Math.min(seed.length, data.length)
|
|
||||||
for (let i = max; i > 0; i--) {
|
|
||||||
if (seed.slice(-i) === data.slice(0, i)) return i
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleOpen = () => {
|
const handleOpen = () => {
|
||||||
local.onConnect?.()
|
local.onConnect?.()
|
||||||
sdk.client.pty
|
sdk.client.pty
|
||||||
@@ -349,25 +316,7 @@ export const Terminal = (props: TerminalProps) => {
|
|||||||
cleanups.push(() => socket.removeEventListener("open", handleOpen))
|
cleanups.push(() => socket.removeEventListener("open", handleOpen))
|
||||||
|
|
||||||
const handleMessage = (event: MessageEvent) => {
|
const handleMessage = (event: MessageEvent) => {
|
||||||
const data = typeof event.data === "string" ? event.data : ""
|
t.write(event.data)
|
||||||
if (!data) return
|
|
||||||
|
|
||||||
const next = (() => {
|
|
||||||
if (!sync) return data
|
|
||||||
const n = overlap(data)
|
|
||||||
if (!n) {
|
|
||||||
sync = false
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
const trimmed = data.slice(n)
|
|
||||||
if (trimmed) sync = false
|
|
||||||
return trimmed
|
|
||||||
})()
|
|
||||||
|
|
||||||
if (!next) return
|
|
||||||
|
|
||||||
t.write(next)
|
|
||||||
tail = next.length >= limit ? next.slice(-limit) : (tail + next).slice(-limit)
|
|
||||||
}
|
}
|
||||||
socket.addEventListener("message", handleMessage)
|
socket.addEventListener("message", handleMessage)
|
||||||
cleanups.push(() => socket.removeEventListener("message", handleMessage))
|
cleanups.push(() => socket.removeEventListener("message", handleMessage))
|
||||||
@@ -421,7 +370,6 @@ export const Terminal = (props: TerminalProps) => {
|
|||||||
props.onCleanup({
|
props.onCleanup({
|
||||||
...local.pty,
|
...local.pty,
|
||||||
buffer,
|
buffer,
|
||||||
tail,
|
|
||||||
rows: t.rows,
|
rows: t.rows,
|
||||||
cols: t.cols,
|
cols: t.cols,
|
||||||
scrollY: t.getViewportY(),
|
scrollY: t.getViewportY(),
|
||||||
|
|||||||
@@ -70,14 +70,6 @@ function createCommentSession(dir: string, id: string | undefined) {
|
|||||||
setFocus((current) => (current?.id === id ? null : current))
|
setFocus((current) => (current?.id === id ? null : current))
|
||||||
}
|
}
|
||||||
|
|
||||||
const clear = () => {
|
|
||||||
batch(() => {
|
|
||||||
setStore("comments", {})
|
|
||||||
setFocus(null)
|
|
||||||
setActive(null)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const all = createMemo(() => {
|
const all = createMemo(() => {
|
||||||
const files = Object.keys(store.comments)
|
const files = Object.keys(store.comments)
|
||||||
const items = files.flatMap((file) => store.comments[file] ?? [])
|
const items = files.flatMap((file) => store.comments[file] ?? [])
|
||||||
@@ -90,7 +82,6 @@ function createCommentSession(dir: string, id: string | undefined) {
|
|||||||
all,
|
all,
|
||||||
add,
|
add,
|
||||||
remove,
|
remove,
|
||||||
clear,
|
|
||||||
focus: createMemo(() => state.focus),
|
focus: createMemo(() => state.focus),
|
||||||
setFocus,
|
setFocus,
|
||||||
clearFocus: () => setFocus(null),
|
clearFocus: () => setFocus(null),
|
||||||
@@ -153,7 +144,6 @@ export const { use: useComments, provider: CommentsProvider } = createSimpleCont
|
|||||||
all: () => session().all(),
|
all: () => session().all(),
|
||||||
add: (input: Omit<LineComment, "id" | "time">) => session().add(input),
|
add: (input: Omit<LineComment, "id" | "time">) => session().add(input),
|
||||||
remove: (file: string, id: string) => session().remove(file, id),
|
remove: (file: string, id: string) => session().remove(file, id),
|
||||||
clear: () => session().clear(),
|
|
||||||
focus: () => session().focus(),
|
focus: () => session().focus(),
|
||||||
setFocus: (focus: CommentFocus | null) => session().setFocus(focus),
|
setFocus: (focus: CommentFocus | null) => session().setFocus(focus),
|
||||||
clearFocus: () => session().clearFocus(),
|
clearFocus: () => session().clearFocus(),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { batch, createEffect, createMemo, createRoot, onCleanup } from "solid-js"
|
import { createEffect, createMemo, createRoot, onCleanup } from "solid-js"
|
||||||
import { createStore, produce, reconcile } from "solid-js/store"
|
import { createStore, produce } from "solid-js/store"
|
||||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||||
import type { FileContent, FileNode } from "@opencode-ai/sdk/v2"
|
import type { FileContent, FileNode } from "@opencode-ai/sdk/v2"
|
||||||
import { showToast } from "@opencode-ai/ui/toast"
|
import { showToast } from "@opencode-ai/ui/toast"
|
||||||
@@ -277,8 +277,10 @@ export const { use: useFile, provider: FileProvider } = createSimpleContext({
|
|||||||
|
|
||||||
const scope = createMemo(() => sdk.directory)
|
const scope = createMemo(() => sdk.directory)
|
||||||
|
|
||||||
|
const directory = createMemo(() => sync.data.path.directory)
|
||||||
|
|
||||||
function normalize(input: string) {
|
function normalize(input: string) {
|
||||||
const root = scope()
|
const root = directory()
|
||||||
const prefix = root.endsWith("/") ? root : root + "/"
|
const prefix = root.endsWith("/") ? root : root + "/"
|
||||||
|
|
||||||
let path = unquoteGitPath(stripQueryAndHash(stripFileProtocol(input)))
|
let path = unquoteGitPath(stripQueryAndHash(stripFileProtocol(input)))
|
||||||
@@ -369,13 +371,9 @@ export const { use: useFile, provider: FileProvider } = createSimpleContext({
|
|||||||
inflight.clear()
|
inflight.clear()
|
||||||
treeInflight.clear()
|
treeInflight.clear()
|
||||||
contentLru.clear()
|
contentLru.clear()
|
||||||
|
setStore("file", {})
|
||||||
batch(() => {
|
setTree("node", {})
|
||||||
setStore("file", reconcile({}))
|
setTree("dir", { "": { expanded: true } })
|
||||||
setTree("node", reconcile({}))
|
|
||||||
setTree("dir", reconcile({}))
|
|
||||||
setTree("dir", "", { expanded: true })
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const viewCache = new Map<string, ViewCacheEntry>()
|
const viewCache = new Map<string, ViewCacheEntry>()
|
||||||
@@ -416,7 +414,7 @@ export const { use: useFile, provider: FileProvider } = createSimpleContext({
|
|||||||
return entry.value
|
return entry.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const view = createMemo(() => loadView(scope(), params.id))
|
const view = createMemo(() => loadView(params.dir!, params.id))
|
||||||
|
|
||||||
function ensure(path: string) {
|
function ensure(path: string) {
|
||||||
if (!path) return
|
if (!path) return
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import { dict as ar } from "@/i18n/ar"
|
|||||||
import { dict as no } from "@/i18n/no"
|
import { dict as no } from "@/i18n/no"
|
||||||
import { dict as br } from "@/i18n/br"
|
import { dict as br } from "@/i18n/br"
|
||||||
import { dict as th } from "@/i18n/th"
|
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 uiEn } from "@opencode-ai/ui/i18n/en"
|
||||||
import { dict as uiZh } from "@opencode-ai/ui/i18n/zh"
|
import { dict as uiZh } from "@opencode-ai/ui/i18n/zh"
|
||||||
import { dict as uiZht } from "@opencode-ai/ui/i18n/zht"
|
import { dict as uiZht } from "@opencode-ai/ui/i18n/zht"
|
||||||
@@ -34,7 +33,6 @@ import { dict as uiAr } from "@opencode-ai/ui/i18n/ar"
|
|||||||
import { dict as uiNo } from "@opencode-ai/ui/i18n/no"
|
import { dict as uiNo } from "@opencode-ai/ui/i18n/no"
|
||||||
import { dict as uiBr } from "@opencode-ai/ui/i18n/br"
|
import { dict as uiBr } from "@opencode-ai/ui/i18n/br"
|
||||||
import { dict as uiTh } from "@opencode-ai/ui/i18n/th"
|
import { dict as uiTh } from "@opencode-ai/ui/i18n/th"
|
||||||
import { dict as uiBs } from "@opencode-ai/ui/i18n/bs"
|
|
||||||
|
|
||||||
export type Locale =
|
export type Locale =
|
||||||
| "en"
|
| "en"
|
||||||
@@ -52,7 +50,6 @@ export type Locale =
|
|||||||
| "no"
|
| "no"
|
||||||
| "br"
|
| "br"
|
||||||
| "th"
|
| "th"
|
||||||
| "bs"
|
|
||||||
|
|
||||||
type RawDictionary = typeof en & typeof uiEn
|
type RawDictionary = typeof en & typeof uiEn
|
||||||
type Dictionary = i18n.Flatten<RawDictionary>
|
type Dictionary = i18n.Flatten<RawDictionary>
|
||||||
@@ -69,7 +66,6 @@ const LOCALES: readonly Locale[] = [
|
|||||||
"ja",
|
"ja",
|
||||||
"pl",
|
"pl",
|
||||||
"ru",
|
"ru",
|
||||||
"bs",
|
|
||||||
"ar",
|
"ar",
|
||||||
"no",
|
"no",
|
||||||
"br",
|
"br",
|
||||||
@@ -103,7 +99,6 @@ function detectLocale(): Locale {
|
|||||||
return "no"
|
return "no"
|
||||||
if (language.toLowerCase().startsWith("pt")) return "br"
|
if (language.toLowerCase().startsWith("pt")) return "br"
|
||||||
if (language.toLowerCase().startsWith("th")) return "th"
|
if (language.toLowerCase().startsWith("th")) return "th"
|
||||||
if (language.toLowerCase().startsWith("bs")) return "bs"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "en"
|
return "en"
|
||||||
@@ -134,7 +129,6 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
|
|||||||
if (store.locale === "no") return "no"
|
if (store.locale === "no") return "no"
|
||||||
if (store.locale === "br") return "br"
|
if (store.locale === "br") return "br"
|
||||||
if (store.locale === "th") return "th"
|
if (store.locale === "th") return "th"
|
||||||
if (store.locale === "bs") return "bs"
|
|
||||||
return "en"
|
return "en"
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -160,7 +154,6 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
|
|||||||
if (locale() === "no") return { ...base, ...i18n.flatten({ ...no, ...uiNo }) }
|
if (locale() === "no") return { ...base, ...i18n.flatten({ ...no, ...uiNo }) }
|
||||||
if (locale() === "br") return { ...base, ...i18n.flatten({ ...br, ...uiBr }) }
|
if (locale() === "br") return { ...base, ...i18n.flatten({ ...br, ...uiBr }) }
|
||||||
if (locale() === "th") return { ...base, ...i18n.flatten({ ...th, ...uiTh }) }
|
if (locale() === "th") return { ...base, ...i18n.flatten({ ...th, ...uiTh }) }
|
||||||
if (locale() === "bs") return { ...base, ...i18n.flatten({ ...bs, ...uiBs }) }
|
|
||||||
return { ...base, ...i18n.flatten({ ...ko, ...uiKo }) }
|
return { ...base, ...i18n.flatten({ ...ko, ...uiKo }) }
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -182,7 +175,6 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
|
|||||||
no: "language.no",
|
no: "language.no",
|
||||||
br: "language.br",
|
br: "language.br",
|
||||||
th: "language.th",
|
th: "language.th",
|
||||||
bs: "language.bs",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const label = (value: Locale) => t(labelKey[value])
|
const label = (value: Locale) => t(labelKey[value])
|
||||||
|
|||||||
@@ -33,14 +33,6 @@ type SessionTabs = {
|
|||||||
type SessionView = {
|
type SessionView = {
|
||||||
scroll: Record<string, SessionScroll>
|
scroll: Record<string, SessionScroll>
|
||||||
reviewOpen?: string[]
|
reviewOpen?: string[]
|
||||||
pendingMessage?: string
|
|
||||||
pendingMessageAt?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
type TabHandoff = {
|
|
||||||
dir: string
|
|
||||||
id: string
|
|
||||||
at: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LocalProject = Partial<Project> & { worktree: string; expanded: boolean }
|
export type LocalProject = Partial<Project> & { worktree: string; expanded: boolean }
|
||||||
@@ -71,7 +63,6 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
const review = value.review
|
|
||||||
const fileTree = value.fileTree
|
const fileTree = value.fileTree
|
||||||
const migratedFileTree = (() => {
|
const migratedFileTree = (() => {
|
||||||
if (!isRecord(fileTree)) return fileTree
|
if (!isRecord(fileTree)) return fileTree
|
||||||
@@ -86,22 +77,10 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
const migratedReview = (() => {
|
if (migratedSidebar === sidebar && migratedFileTree === fileTree) return value
|
||||||
if (!isRecord(review)) return review
|
|
||||||
if (typeof review.panelOpened === "boolean") return review
|
|
||||||
|
|
||||||
const opened = isRecord(fileTree) && typeof fileTree.opened === "boolean" ? fileTree.opened : true
|
|
||||||
return {
|
|
||||||
...review,
|
|
||||||
panelOpened: opened,
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
|
|
||||||
if (migratedSidebar === sidebar && migratedReview === review && migratedFileTree === fileTree) return value
|
|
||||||
return {
|
return {
|
||||||
...value,
|
...value,
|
||||||
sidebar: migratedSidebar,
|
sidebar: migratedSidebar,
|
||||||
review: migratedReview,
|
|
||||||
fileTree: migratedFileTree,
|
fileTree: migratedFileTree,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,7 +101,6 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
},
|
},
|
||||||
review: {
|
review: {
|
||||||
diffStyle: "split" as ReviewDiffStyle,
|
diffStyle: "split" as ReviewDiffStyle,
|
||||||
panelOpened: true,
|
|
||||||
},
|
},
|
||||||
fileTree: {
|
fileTree: {
|
||||||
opened: true,
|
opened: true,
|
||||||
@@ -137,14 +115,10 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
},
|
},
|
||||||
sessionTabs: {} as Record<string, SessionTabs>,
|
sessionTabs: {} as Record<string, SessionTabs>,
|
||||||
sessionView: {} as Record<string, SessionView>,
|
sessionView: {} as Record<string, SessionView>,
|
||||||
handoff: {
|
|
||||||
tabs: undefined as TabHandoff | undefined,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const MAX_SESSION_KEYS = 50
|
const MAX_SESSION_KEYS = 50
|
||||||
const PENDING_MESSAGE_TTL_MS = 2 * 60 * 1000
|
|
||||||
const meta = { active: undefined as string | undefined, pruned: false }
|
const meta = { active: undefined as string | undefined, pruned: false }
|
||||||
const used = new Map<string, number>()
|
const used = new Map<string, number>()
|
||||||
|
|
||||||
@@ -437,16 +411,6 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
ready,
|
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: {
|
projects: {
|
||||||
list,
|
list,
|
||||||
open(directory: string) {
|
open(directory: string) {
|
||||||
@@ -504,7 +468,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
diffStyle: createMemo(() => store.review?.diffStyle ?? "split"),
|
diffStyle: createMemo(() => store.review?.diffStyle ?? "split"),
|
||||||
setDiffStyle(diffStyle: ReviewDiffStyle) {
|
setDiffStyle(diffStyle: ReviewDiffStyle) {
|
||||||
if (!store.review) {
|
if (!store.review) {
|
||||||
setStore("review", { diffStyle, panelOpened: true })
|
setStore("review", { diffStyle })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setStore("review", "diffStyle", diffStyle)
|
setStore("review", "diffStyle", diffStyle)
|
||||||
@@ -572,49 +536,6 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
setStore("mobileSidebar", "opened", (x) => !x)
|
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<string>) {
|
view(sessionKey: string | Accessor<string>) {
|
||||||
const key = typeof sessionKey === "function" ? sessionKey : () => sessionKey
|
const key = typeof sessionKey === "function" ? sessionKey : () => sessionKey
|
||||||
|
|
||||||
@@ -634,7 +555,6 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
|
|
||||||
const s = createMemo(() => store.sessionView[key()] ?? { scroll: {} })
|
const s = createMemo(() => store.sessionView[key()] ?? { scroll: {} })
|
||||||
const terminalOpened = createMemo(() => store.terminal?.opened ?? false)
|
const terminalOpened = createMemo(() => store.terminal?.opened ?? false)
|
||||||
const reviewPanelOpened = createMemo(() => store.review?.panelOpened ?? true)
|
|
||||||
|
|
||||||
function setTerminalOpened(next: boolean) {
|
function setTerminalOpened(next: boolean) {
|
||||||
const current = store.terminal
|
const current = store.terminal
|
||||||
@@ -648,18 +568,6 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
setStore("terminal", "opened", next)
|
setStore("terminal", "opened", next)
|
||||||
}
|
}
|
||||||
|
|
||||||
function setReviewPanelOpened(next: boolean) {
|
|
||||||
const current = store.review
|
|
||||||
if (!current) {
|
|
||||||
setStore("review", { diffStyle: "split" as ReviewDiffStyle, panelOpened: next })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const value = current.panelOpened ?? true
|
|
||||||
if (value === next) return
|
|
||||||
setStore("review", "panelOpened", next)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
scroll(tab: string) {
|
scroll(tab: string) {
|
||||||
return scroll.scroll(key(), tab)
|
return scroll.scroll(key(), tab)
|
||||||
@@ -679,18 +587,6 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
setTerminalOpened(!terminalOpened())
|
setTerminalOpened(!terminalOpened())
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
reviewPanel: {
|
|
||||||
opened: reviewPanelOpened,
|
|
||||||
open() {
|
|
||||||
setReviewPanelOpened(true)
|
|
||||||
},
|
|
||||||
close() {
|
|
||||||
setReviewPanelOpened(false)
|
|
||||||
},
|
|
||||||
toggle() {
|
|
||||||
setReviewPanelOpened(!reviewPanelOpened())
|
|
||||||
},
|
|
||||||
},
|
|
||||||
review: {
|
review: {
|
||||||
open: createMemo(() => s().reviewOpen),
|
open: createMemo(() => s().reviewOpen),
|
||||||
setOpen(open: string[]) {
|
setOpen(open: string[]) {
|
||||||
@@ -728,10 +624,11 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
const tabs = createMemo(() => store.sessionTabs[key()] ?? { all: [] })
|
const tabs = createMemo(() => store.sessionTabs[key()] ?? { all: [] })
|
||||||
return {
|
return {
|
||||||
tabs,
|
tabs,
|
||||||
active: createMemo(() => tabs().active),
|
active: createMemo(() => (tabs().active === "review" ? undefined : tabs().active)),
|
||||||
all: createMemo(() => tabs().all.filter((tab) => tab !== "review")),
|
all: createMemo(() => tabs().all.filter((tab) => tab !== "review")),
|
||||||
setActive(tab: string | undefined) {
|
setActive(tab: string | undefined) {
|
||||||
const session = key()
|
const session = key()
|
||||||
|
if (tab === "review") return
|
||||||
if (!store.sessionTabs[session]) {
|
if (!store.sessionTabs[session]) {
|
||||||
setStore("sessionTabs", session, { all: [], active: tab })
|
setStore("sessionTabs", session, { all: [], active: tab })
|
||||||
} else {
|
} else {
|
||||||
@@ -748,18 +645,10 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async open(tab: string) {
|
async open(tab: string) {
|
||||||
|
if (tab === "review") return
|
||||||
const session = key()
|
const session = key()
|
||||||
const current = store.sessionTabs[session] ?? { all: [] }
|
const current = store.sessionTabs[session] ?? { all: [] }
|
||||||
|
|
||||||
if (tab === "review") {
|
|
||||||
if (!store.sessionTabs[session]) {
|
|
||||||
setStore("sessionTabs", session, { all: current.all.filter((x) => x !== "review"), active: tab })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
setStore("sessionTabs", session, "active", tab)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tab === "context") {
|
if (tab === "context") {
|
||||||
const all = [tab, ...current.all.filter((x) => x !== tab)]
|
const all = [tab, ...current.all.filter((x) => x !== tab)]
|
||||||
if (!store.sessionTabs[session]) {
|
if (!store.sessionTabs[session]) {
|
||||||
@@ -792,12 +681,6 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
|||||||
const current = store.sessionTabs[session]
|
const current = store.sessionTabs[session]
|
||||||
if (!current) return
|
if (!current) return
|
||||||
|
|
||||||
if (tab === "review") {
|
|
||||||
if (current.active !== tab) return
|
|
||||||
setStore("sessionTabs", session, "active", current.all[0])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const all = current.all.filter((x) => x !== tab)
|
const all = current.all.filter((x) => x !== tab)
|
||||||
if (current.active !== tab) {
|
if (current.active !== tab) {
|
||||||
setStore("sessionTabs", session, "all", all)
|
setStore("sessionTabs", session, "all", all)
|
||||||
|
|||||||
@@ -12,11 +12,8 @@ export type Platform = {
|
|||||||
/** App version */
|
/** App version */
|
||||||
version?: string
|
version?: string
|
||||||
|
|
||||||
/** Open a URL in the default browser */
|
/** Open a URL/path using the OS (optionally with a specific app) */
|
||||||
openLink(url: string): void
|
openLink(url: string, openWith?: string): Promise<void>
|
||||||
|
|
||||||
/** Open a local path in a local app (desktop only) */
|
|
||||||
openPath?(path: string, app?: string): Promise<void>
|
|
||||||
|
|
||||||
/** Restart the app */
|
/** Restart the app */
|
||||||
restart(): Promise<void>
|
restart(): Promise<void>
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2/client"
|
import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2/client"
|
||||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||||
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
||||||
import { createEffect, createMemo, onCleanup, type Accessor } from "solid-js"
|
import { createEffect, createMemo, onCleanup } from "solid-js"
|
||||||
import { useGlobalSDK } from "./global-sdk"
|
import { useGlobalSDK } from "./global-sdk"
|
||||||
import { usePlatform } from "./platform"
|
import { usePlatform } from "./platform"
|
||||||
|
|
||||||
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
||||||
name: "SDK",
|
name: "SDK",
|
||||||
init: (props: { directory: Accessor<string> }) => {
|
init: (props: { directory: string }) => {
|
||||||
const platform = usePlatform()
|
const platform = usePlatform()
|
||||||
const globalSDK = useGlobalSDK()
|
const globalSDK = useGlobalSDK()
|
||||||
|
|
||||||
const directory = createMemo(props.directory)
|
const directory = createMemo(() => props.directory)
|
||||||
const client = createMemo(() =>
|
const client = createMemo(() =>
|
||||||
createOpencodeClient({
|
createOpencodeClient({
|
||||||
baseUrl: globalSDK.url,
|
baseUrl: globalSDK.url,
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ export type LocalPTY = {
|
|||||||
cols?: number
|
cols?: number
|
||||||
buffer?: string
|
buffer?: string
|
||||||
scrollY?: number
|
scrollY?: number
|
||||||
tail?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const WORKSPACE_KEY = "__workspace__"
|
const WORKSPACE_KEY = "__workspace__"
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
|
|||||||
const platform: Platform = {
|
const platform: Platform = {
|
||||||
platform: "web",
|
platform: "web",
|
||||||
version: pkg.version,
|
version: pkg.version,
|
||||||
openLink(url: string) {
|
async openLink(url: string, _openWith?: string) {
|
||||||
window.open(url, "_blank")
|
window.open(url, "_blank")
|
||||||
},
|
},
|
||||||
back() {
|
back() {
|
||||||
|
|||||||
@@ -321,6 +321,22 @@ export const dict = {
|
|||||||
"context.usage.clickToView": "انقر لعرض السياق",
|
"context.usage.clickToView": "انقر لعرض السياق",
|
||||||
"context.usage.view": "عرض استخدام السياق",
|
"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.title": "لغة",
|
||||||
"toast.language.description": "تم التبديل إلى {{language}}",
|
"toast.language.description": "تم التبديل إلى {{language}}",
|
||||||
|
|
||||||
|
|||||||
@@ -320,6 +320,22 @@ export const dict = {
|
|||||||
"context.usage.clickToView": "Clique para ver o contexto",
|
"context.usage.clickToView": "Clique para ver o contexto",
|
||||||
"context.usage.view": "Ver uso do 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.title": "Idioma",
|
||||||
"toast.language.description": "Alterado para {{language}}",
|
"toast.language.description": "Alterado para {{language}}",
|
||||||
|
|
||||||
|
|||||||
@@ -1,747 +0,0 @@
|
|||||||
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 OpenCode",
|
|
||||||
"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 OpenCode-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 OpenCode-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 OpenCode-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 OpenCode 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 OpenCode-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 OpenCode 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: OpenCode 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": "OpenCode 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": "OpenCode 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 OpenCode-u",
|
|
||||||
"settings.general.row.appearance.title": "Izgled",
|
|
||||||
"settings.general.row.appearance.description": "Prilagodi kako OpenCode izgleda na tvom uređaju",
|
|
||||||
"settings.general.row.theme.title": "Tema",
|
|
||||||
"settings.general.row.theme.description": "Prilagodi temu OpenCode-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 OpenCode 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 OpenCode-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.",
|
|
||||||
}
|
|
||||||
@@ -322,6 +322,22 @@ export const dict = {
|
|||||||
"context.usage.clickToView": "Klik for at se kontekst",
|
"context.usage.clickToView": "Klik for at se kontekst",
|
||||||
"context.usage.view": "Se kontekstforbrug",
|
"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.title": "Sprog",
|
||||||
"toast.language.description": "Skiftede til {{language}}",
|
"toast.language.description": "Skiftede til {{language}}",
|
||||||
|
|
||||||
|
|||||||
@@ -328,6 +328,22 @@ export const dict = {
|
|||||||
"context.usage.clickToView": "Klicken, um Kontext anzuzeigen",
|
"context.usage.clickToView": "Klicken, um Kontext anzuzeigen",
|
||||||
"context.usage.view": "Kontextnutzung anzeigen",
|
"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.title": "Sprache",
|
||||||
"toast.language.description": "Zu {{language}} gewechselt",
|
"toast.language.description": "Zu {{language}} gewechselt",
|
||||||
|
|
||||||
|
|||||||
@@ -343,7 +343,6 @@ export const dict = {
|
|||||||
"language.ar": "العربية",
|
"language.ar": "العربية",
|
||||||
"language.no": "Norsk",
|
"language.no": "Norsk",
|
||||||
"language.br": "Português (Brasil)",
|
"language.br": "Português (Brasil)",
|
||||||
"language.bs": "Bosanski",
|
|
||||||
"language.th": "ไทย",
|
"language.th": "ไทย",
|
||||||
|
|
||||||
"toast.language.title": "Language",
|
"toast.language.title": "Language",
|
||||||
@@ -470,11 +469,11 @@ export const dict = {
|
|||||||
|
|
||||||
"session.header.search.placeholder": "Search {{project}}",
|
"session.header.search.placeholder": "Search {{project}}",
|
||||||
"session.header.searchFiles": "Search files",
|
"session.header.searchFiles": "Search files",
|
||||||
|
"session.header.open": "Open",
|
||||||
"session.header.openIn": "Open in",
|
"session.header.openIn": "Open in",
|
||||||
"session.header.open.action": "Open {{app}}",
|
"session.header.copyPath": "Copy Path",
|
||||||
"session.header.open.ariaLabel": "Open in {{app}}",
|
"session.header.copyPath.copied": "Copied path",
|
||||||
"session.header.open.menu": "Open options",
|
"session.header.copyPath.copyFailed": "Failed to copy path to clipboard",
|
||||||
"session.header.open.copyPath": "Copy Path",
|
|
||||||
|
|
||||||
"status.popover.trigger": "Status",
|
"status.popover.trigger": "Status",
|
||||||
"status.popover.ariaLabel": "Server configurations",
|
"status.popover.ariaLabel": "Server configurations",
|
||||||
|
|||||||
@@ -323,6 +323,22 @@ export const dict = {
|
|||||||
"context.usage.clickToView": "Haz clic para ver contexto",
|
"context.usage.clickToView": "Haz clic para ver contexto",
|
||||||
"context.usage.view": "Ver uso del 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.title": "Idioma",
|
||||||
"toast.language.description": "Cambiado a {{language}}",
|
"toast.language.description": "Cambiado a {{language}}",
|
||||||
|
|
||||||
|
|||||||
@@ -323,6 +323,22 @@ export const dict = {
|
|||||||
"context.usage.clickToView": "Cliquez pour voir le contexte",
|
"context.usage.clickToView": "Cliquez pour voir le contexte",
|
||||||
"context.usage.view": "Voir l'utilisation du 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.title": "Langue",
|
||||||
"toast.language.description": "Passé à {{language}}",
|
"toast.language.description": "Passé à {{language}}",
|
||||||
|
|
||||||
|
|||||||
@@ -321,6 +321,22 @@ export const dict = {
|
|||||||
"context.usage.clickToView": "クリックしてコンテキストを表示",
|
"context.usage.clickToView": "クリックしてコンテキストを表示",
|
||||||
"context.usage.view": "コンテキスト使用量を表示",
|
"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.title": "言語",
|
||||||
"toast.language.description": "{{language}}に切り替えました",
|
"toast.language.description": "{{language}}に切り替えました",
|
||||||
|
|
||||||
|
|||||||
@@ -324,6 +324,22 @@ export const dict = {
|
|||||||
"context.usage.clickToView": "컨텍스트를 보려면 클릭",
|
"context.usage.clickToView": "컨텍스트를 보려면 클릭",
|
||||||
"context.usage.view": "컨텍스트 사용량 보기",
|
"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.title": "언어",
|
||||||
"toast.language.description": "{{language}}(으)로 전환됨",
|
"toast.language.description": "{{language}}(으)로 전환됨",
|
||||||
|
|
||||||
|
|||||||
@@ -324,6 +324,22 @@ export const dict = {
|
|||||||
"context.usage.clickToView": "Klikk for å se kontekst",
|
"context.usage.clickToView": "Klikk for å se kontekst",
|
||||||
"context.usage.view": "Se kontekstforbruk",
|
"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.title": "Språk",
|
||||||
"toast.language.description": "Byttet til {{language}}",
|
"toast.language.description": "Byttet til {{language}}",
|
||||||
|
|
||||||
|
|||||||
@@ -322,6 +322,22 @@ export const dict = {
|
|||||||
"context.usage.clickToView": "Kliknij, aby zobaczyć kontekst",
|
"context.usage.clickToView": "Kliknij, aby zobaczyć kontekst",
|
||||||
"context.usage.view": "Pokaż użycie kontekstu",
|
"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.title": "Język",
|
||||||
"toast.language.description": "Przełączono na {{language}}",
|
"toast.language.description": "Przełączono na {{language}}",
|
||||||
|
|
||||||
|
|||||||
@@ -323,6 +323,22 @@ export const dict = {
|
|||||||
"context.usage.clickToView": "Нажмите для просмотра контекста",
|
"context.usage.clickToView": "Нажмите для просмотра контекста",
|
||||||
"context.usage.view": "Показать использование контекста",
|
"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.title": "Язык",
|
||||||
"toast.language.description": "Переключено на {{language}}",
|
"toast.language.description": "Переключено на {{language}}",
|
||||||
|
|
||||||
|
|||||||
@@ -326,6 +326,22 @@ export const dict = {
|
|||||||
"context.usage.clickToView": "คลิกเพื่อดูบริบท",
|
"context.usage.clickToView": "คลิกเพื่อดูบริบท",
|
||||||
"context.usage.view": "ดูการใช้บริบท",
|
"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.title": "ภาษา",
|
||||||
"toast.language.description": "สลับไปที่ {{language}}",
|
"toast.language.description": "สลับไปที่ {{language}}",
|
||||||
|
|
||||||
|
|||||||
@@ -324,6 +324,22 @@ export const dict = {
|
|||||||
"context.usage.clickToView": "点击查看上下文",
|
"context.usage.clickToView": "点击查看上下文",
|
||||||
"context.usage.view": "查看上下文用量",
|
"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.title": "语言",
|
||||||
"toast.language.description": "已切换到{{language}}",
|
"toast.language.description": "已切换到{{language}}",
|
||||||
|
|
||||||
|
|||||||
@@ -321,6 +321,22 @@ export const dict = {
|
|||||||
"context.usage.clickToView": "點擊查看上下文",
|
"context.usage.clickToView": "點擊查看上下文",
|
||||||
"context.usage.view": "檢視上下文用量",
|
"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.title": "語言",
|
||||||
"toast.language.description": "已切換到 {{language}}",
|
"toast.language.description": "已切換到 {{language}}",
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<Show when={directory()}>
|
<Show when={directory()}>
|
||||||
<SDKProvider directory={directory}>
|
<SDKProvider directory={directory()}>
|
||||||
<SyncProvider>
|
<SyncProvider>
|
||||||
{iife(() => {
|
{iife(() => {
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
|
|||||||
@@ -269,14 +269,14 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
|
|||||||
<div class="flex flex-col items-center gap-2">
|
<div class="flex flex-col items-center gap-2">
|
||||||
<div class="flex items-center justify-center gap-1">
|
<div class="flex items-center justify-center gap-1">
|
||||||
{language.t("error.page.report.prefix")}
|
{language.t("error.page.report.prefix")}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="flex items-center text-text-interactive-base gap-1"
|
class="flex items-center text-text-interactive-base gap-1"
|
||||||
onClick={() => platform.openLink("https://opencode.ai/desktop-feedback")}
|
onClick={() => void platform.openLink("https://opencode.ai/desktop-feedback").catch(() => undefined)}
|
||||||
>
|
>
|
||||||
<div>{language.t("error.page.report.discord")}</div>
|
<div>{language.t("error.page.report.discord")}</div>
|
||||||
<Icon name="discord" class="text-text-interactive-base" />
|
<Icon name="discord" class="text-text-interactive-base" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<Show when={platform.version}>
|
<Show when={platform.version}>
|
||||||
{(version) => (
|
{(version) => (
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ import { usePermission } from "@/context/permission"
|
|||||||
import { Binary } from "@opencode-ai/util/binary"
|
import { Binary } from "@opencode-ai/util/binary"
|
||||||
import { retry } from "@opencode-ai/util/retry"
|
import { retry } from "@opencode-ai/util/retry"
|
||||||
import { playSound, soundSrc } from "@/utils/sound"
|
import { playSound, soundSrc } from "@/utils/sound"
|
||||||
import { createAim } from "@/utils/aim"
|
|
||||||
import { Worktree as WorktreeState } from "@/utils/worktree"
|
import { Worktree as WorktreeState } from "@/utils/worktree"
|
||||||
import { agentColor } from "@/utils/agent"
|
import { agentColor } from "@/utils/agent"
|
||||||
|
|
||||||
@@ -147,20 +146,9 @@ export default function Layout(props: ParentProps) {
|
|||||||
|
|
||||||
const navLeave = { current: undefined as number | undefined }
|
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(() => {
|
onCleanup(() => {
|
||||||
if (navLeave.current !== undefined) clearTimeout(navLeave.current)
|
if (navLeave.current === undefined) return
|
||||||
aim.reset()
|
clearTimeout(navLeave.current)
|
||||||
})
|
})
|
||||||
|
|
||||||
const sidebarHovering = createMemo(() => !layout.sidebar.opened() && state.hoverProject !== undefined)
|
const sidebarHovering = createMemo(() => !layout.sidebar.opened() && state.hoverProject !== undefined)
|
||||||
@@ -174,22 +162,15 @@ export default function Layout(props: ParentProps) {
|
|||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (!layout.sidebar.opened()) return
|
if (!layout.sidebar.opened()) return
|
||||||
aim.reset()
|
|
||||||
setState("hoverProject", undefined)
|
setState("hoverProject", undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
createEffect(() => {
|
|
||||||
if (state.hoverProject !== undefined) return
|
|
||||||
aim.reset()
|
|
||||||
})
|
|
||||||
|
|
||||||
createEffect(
|
createEffect(
|
||||||
on(
|
on(
|
||||||
() => ({ dir: params.dir, id: params.id }),
|
() => ({ dir: params.dir, id: params.id }),
|
||||||
() => {
|
() => {
|
||||||
if (layout.sidebar.opened()) return
|
if (layout.sidebar.opened()) return
|
||||||
if (!state.hoverProject) return
|
if (!state.hoverProject) return
|
||||||
aim.reset()
|
|
||||||
setState("hoverSession", undefined)
|
setState("hoverSession", undefined)
|
||||||
setState("hoverProject", undefined)
|
setState("hoverProject", undefined)
|
||||||
},
|
},
|
||||||
@@ -1019,6 +1000,69 @@ 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<string>([session.id])
|
||||||
|
|
||||||
|
const byParent = new Map<string, string[]>()
|
||||||
|
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(() => {
|
command.register(() => {
|
||||||
const commands: CommandOption[] = [
|
const commands: CommandOption[] = [
|
||||||
{
|
{
|
||||||
@@ -1272,6 +1316,15 @@ export default function Layout(props: ParentProps) {
|
|||||||
globalSync.project.meta(project.worktree, { name })
|
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 renameWorkspace = (directory: string, next: string, projectId?: string, branch?: string) => {
|
||||||
const current = workspaceName(directory, projectId, branch) ?? branch ?? getFilename(directory)
|
const current = workspaceName(directory, projectId, branch) ?? branch ?? getFilename(directory)
|
||||||
if (current === next) return
|
if (current === next) return
|
||||||
@@ -1422,6 +1475,33 @@ export default function Layout(props: ParentProps) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DialogDeleteSession(props: { session: Session }) {
|
||||||
|
const handleDelete = async () => {
|
||||||
|
await deleteSession(props.session)
|
||||||
|
dialog.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog title={language.t("session.delete.title")} fit>
|
||||||
|
<div class="flex flex-col gap-4 pl-6 pr-2.5 pb-3">
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<span class="text-14-regular text-text-strong">
|
||||||
|
{language.t("session.delete.confirm", { name: props.session.title })}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end gap-2">
|
||||||
|
<Button variant="ghost" size="large" onClick={() => dialog.close()}>
|
||||||
|
{language.t("common.cancel")}
|
||||||
|
</Button>
|
||||||
|
<Button variant="primary" size="large" onClick={handleDelete}>
|
||||||
|
{language.t("session.delete.button")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function DialogDeleteWorkspace(props: { root: string; directory: string }) {
|
function DialogDeleteWorkspace(props: { root: string; directory: string }) {
|
||||||
const name = createMemo(() => getFilename(props.directory))
|
const name = createMemo(() => getFilename(props.directory))
|
||||||
const [data, setData] = createStore({
|
const [data, setData] = createStore({
|
||||||
@@ -1775,6 +1855,10 @@ export default function Layout(props: ParentProps) {
|
|||||||
const hoverAllowed = createMemo(() => !props.mobile && sidebarExpanded())
|
const hoverAllowed = createMemo(() => !props.mobile && sidebarExpanded())
|
||||||
const hoverEnabled = createMemo(() => (props.popover ?? true) && hoverAllowed())
|
const hoverEnabled = createMemo(() => (props.popover ?? true) && hoverAllowed())
|
||||||
const isActive = createMemo(() => props.session.id === params.id)
|
const isActive = createMemo(() => props.session.id === params.id)
|
||||||
|
const [menu, setMenu] = createStore({
|
||||||
|
open: false,
|
||||||
|
pendingRename: false,
|
||||||
|
})
|
||||||
|
|
||||||
const hoverPrefetch = { current: undefined as ReturnType<typeof setTimeout> | undefined }
|
const hoverPrefetch = { current: undefined as ReturnType<typeof setTimeout> | undefined }
|
||||||
const cancelHoverPrefetch = () => {
|
const cancelHoverPrefetch = () => {
|
||||||
@@ -1801,7 +1885,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
const item = (
|
const item = (
|
||||||
<A
|
<A
|
||||||
href={`${props.slug}/session/${props.session.id}`}
|
href={`${props.slug}/session/${props.session.id}`}
|
||||||
class={`flex items-center justify-between gap-3 min-w-0 text-left w-full focus:outline-none transition-[padding] ${props.mobile ? "pr-7" : ""} group-hover/session:pr-7 group-focus-within/session:pr-7 group-active/session:pr-7 ${props.dense ? "py-0.5" : "py-1"}`}
|
class={`flex items-center justify-between gap-3 min-w-0 text-left w-full focus:outline-none transition-[padding] ${menu.open ? "pr-7" : ""} group-hover/session:pr-7 group-focus-within/session:pr-7 group-active/session:pr-7 ${props.dense ? "py-0.5" : "py-1"}`}
|
||||||
onPointerEnter={scheduleHoverPrefetch}
|
onPointerEnter={scheduleHoverPrefetch}
|
||||||
onPointerLeave={cancelHoverPrefetch}
|
onPointerLeave={cancelHoverPrefetch}
|
||||||
onMouseEnter={scheduleHoverPrefetch}
|
onMouseEnter={scheduleHoverPrefetch}
|
||||||
@@ -1833,9 +1917,14 @@ export default function Layout(props: ParentProps) {
|
|||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-14-regular text-text-strong grow-1 min-w-0 overflow-hidden text-ellipsis truncate">
|
<InlineEditor
|
||||||
{props.session.title}
|
id={`session:${props.session.id}`}
|
||||||
</span>
|
value={() => 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
|
||||||
|
/>
|
||||||
<Show when={props.session.summary}>
|
<Show when={props.session.summary}>
|
||||||
{(summary) => (
|
{(summary) => (
|
||||||
<div class="group-hover/session:hidden group-active/session:hidden group-focus-within/session:hidden">
|
<div class="group-hover/session:hidden group-active/session:hidden group-focus-within/session:hidden">
|
||||||
@@ -1883,10 +1972,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
getLabel={messageLabel}
|
getLabel={messageLabel}
|
||||||
onMessageSelect={(message) => {
|
onMessageSelect={(message) => {
|
||||||
if (!isActive()) {
|
if (!isActive()) {
|
||||||
layout.pendingMessage.set(
|
sessionStorage.setItem("opencode.pendingMessage", `${props.session.id}|${message.id}`)
|
||||||
`${base64Encode(props.session.directory)}/${props.session.id}`,
|
|
||||||
message.id,
|
|
||||||
)
|
|
||||||
navigate(`${props.slug}/session/${props.session.id}`)
|
navigate(`${props.slug}/session/${props.session.id}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1903,25 +1989,49 @@ export default function Layout(props: ParentProps) {
|
|||||||
<div
|
<div
|
||||||
class={`absolute ${props.dense ? "top-0.5 right-0.5" : "top-1 right-1"} flex items-center gap-0.5 transition-opacity`}
|
class={`absolute ${props.dense ? "top-0.5 right-0.5" : "top-1 right-1"} flex items-center gap-0.5 transition-opacity`}
|
||||||
classList={{
|
classList={{
|
||||||
"opacity-100 pointer-events-auto": !!props.mobile,
|
"opacity-100 pointer-events-auto": menu.open,
|
||||||
"opacity-0 pointer-events-none": !props.mobile,
|
"opacity-0 pointer-events-none": !menu.open,
|
||||||
"group-hover/session:opacity-100 group-hover/session:pointer-events-auto": true,
|
"group-hover/session:opacity-100 group-hover/session:pointer-events-auto": true,
|
||||||
"group-focus-within/session:opacity-100 group-focus-within/session:pointer-events-auto": true,
|
"group-focus-within/session:opacity-100 group-focus-within/session:pointer-events-auto": true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tooltip value={language.t("common.archive")} placement="top">
|
<DropdownMenu modal={!sidebarHovering()} open={menu.open} onOpenChange={(open) => setMenu("open", open)}>
|
||||||
<IconButton
|
<Tooltip value={language.t("common.moreOptions")} placement="top">
|
||||||
icon="archive"
|
<DropdownMenu.Trigger
|
||||||
variant="ghost"
|
as={IconButton}
|
||||||
class="size-6 rounded-md"
|
icon="dot-grid"
|
||||||
aria-label={language.t("common.archive")}
|
variant="ghost"
|
||||||
onClick={(event) => {
|
class="size-6 rounded-md data-[expanded]:bg-surface-base-active"
|
||||||
event.preventDefault()
|
aria-label={language.t("common.moreOptions")}
|
||||||
event.stopPropagation()
|
/>
|
||||||
void archiveSession(props.session)
|
</Tooltip>
|
||||||
}}
|
<DropdownMenu.Portal mount={!props.mobile ? state.nav : undefined}>
|
||||||
/>
|
<DropdownMenu.Content
|
||||||
</Tooltip>
|
onCloseAutoFocus={(event) => {
|
||||||
|
if (!menu.pendingRename) return
|
||||||
|
event.preventDefault()
|
||||||
|
setMenu("pendingRename", false)
|
||||||
|
openEditor(`session:${props.session.id}`, props.session.title)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DropdownMenu.Item
|
||||||
|
onSelect={() => {
|
||||||
|
setMenu("pendingRename", true)
|
||||||
|
setMenu("open", false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DropdownMenu.ItemLabel>{language.t("common.rename")}</DropdownMenu.ItemLabel>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item onSelect={() => archiveSession(props.session)}>
|
||||||
|
<DropdownMenu.ItemLabel>{language.t("common.archive")}</DropdownMenu.ItemLabel>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Separator />
|
||||||
|
<DropdownMenu.Item onSelect={() => dialog.show(() => <DialogDeleteSession session={props.session} />)}>
|
||||||
|
<DropdownMenu.ItemLabel>{language.t("common.delete")}</DropdownMenu.ItemLabel>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Portal>
|
||||||
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -2055,10 +2165,9 @@ export default function Layout(props: ParentProps) {
|
|||||||
const open = createMemo(() => store.workspaceExpanded[props.directory] ?? local())
|
const open = createMemo(() => store.workspaceExpanded[props.directory] ?? local())
|
||||||
const boot = createMemo(() => open() || active())
|
const boot = createMemo(() => open() || active())
|
||||||
const booted = createMemo((prev) => prev || workspaceStore.status === "complete", false)
|
const booted = createMemo((prev) => prev || workspaceStore.status === "complete", false)
|
||||||
|
const loading = createMemo(() => open() && !booted() && sessions().length === 0)
|
||||||
const hasMore = createMemo(() => workspaceStore.sessionTotal > sessions().length)
|
const hasMore = createMemo(() => workspaceStore.sessionTotal > sessions().length)
|
||||||
const busy = createMemo(() => isBusy(props.directory))
|
const busy = createMemo(() => isBusy(props.directory))
|
||||||
const wasBusy = createMemo((prev) => prev || busy(), false)
|
|
||||||
const loading = createMemo(() => open() && !booted() && sessions().length === 0 && !wasBusy())
|
|
||||||
const loadMore = async () => {
|
const loadMore = async () => {
|
||||||
setWorkspaceStore("limit", (limit) => limit + 5)
|
setWorkspaceStore("limit", (limit) => limit + 5)
|
||||||
await globalSync.project.loadSessions(props.directory)
|
await globalSync.project.loadSessions(props.directory)
|
||||||
@@ -2331,17 +2440,17 @@ export default function Layout(props: ParentProps) {
|
|||||||
!selected() && !active(),
|
!selected() && !active(),
|
||||||
"bg-surface-base-hover border border-border-weak-base": !selected() && active(),
|
"bg-surface-base-hover border border-border-weak-base": !selected() && active(),
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(event: MouseEvent) => {
|
onMouseEnter={() => {
|
||||||
if (!overlay()) return
|
if (!overlay()) return
|
||||||
aim.enter(props.project.worktree, event)
|
globalSync.child(props.project.worktree)
|
||||||
}}
|
setState("hoverProject", props.project.worktree)
|
||||||
onMouseLeave={() => {
|
setState("hoverSession", undefined)
|
||||||
if (!overlay()) return
|
|
||||||
aim.leave(props.project.worktree)
|
|
||||||
}}
|
}}
|
||||||
onFocus={() => {
|
onFocus={() => {
|
||||||
if (!overlay()) return
|
if (!overlay()) return
|
||||||
aim.activate(props.project.worktree)
|
globalSync.child(props.project.worktree)
|
||||||
|
setState("hoverProject", props.project.worktree)
|
||||||
|
setState("hoverSession", undefined)
|
||||||
}}
|
}}
|
||||||
onClick={() => navigateToProject(props.project.worktree)}
|
onClick={() => navigateToProject(props.project.worktree)}
|
||||||
onBlur={() => setOpen(false)}
|
onBlur={() => setOpen(false)}
|
||||||
@@ -2489,22 +2598,17 @@ export default function Layout(props: ParentProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const LocalWorkspace = (props: { project: LocalProject; mobile?: boolean }): JSX.Element => {
|
const LocalWorkspace = (props: { project: LocalProject; mobile?: boolean }): JSX.Element => {
|
||||||
const workspace = createMemo(() => {
|
const [workspaceStore, setWorkspaceStore] = globalSync.child(props.project.worktree)
|
||||||
const [store, setStore] = globalSync.child(props.project.worktree)
|
|
||||||
return { store, setStore }
|
|
||||||
})
|
|
||||||
const slug = createMemo(() => base64Encode(props.project.worktree))
|
const slug = createMemo(() => base64Encode(props.project.worktree))
|
||||||
const sessions = createMemo(() => {
|
const sessions = createMemo(() =>
|
||||||
const store = workspace().store
|
workspaceStore.session
|
||||||
return store.session
|
.filter((session) => session.directory === workspaceStore.path.directory)
|
||||||
.filter((session) => session.directory === store.path.directory)
|
|
||||||
.filter((session) => !session.parentID && !session.time?.archived)
|
.filter((session) => !session.parentID && !session.time?.archived)
|
||||||
.toSorted(sortSessions(Date.now()))
|
.toSorted(sortSessions(Date.now())),
|
||||||
})
|
)
|
||||||
const children = createMemo(() => {
|
const children = createMemo(() => {
|
||||||
const store = workspace().store
|
|
||||||
const map = new Map<string, string[]>()
|
const map = new Map<string, string[]>()
|
||||||
for (const session of store.session) {
|
for (const session of workspaceStore.session) {
|
||||||
if (!session.parentID) continue
|
if (!session.parentID) continue
|
||||||
const existing = map.get(session.parentID)
|
const existing = map.get(session.parentID)
|
||||||
if (existing) {
|
if (existing) {
|
||||||
@@ -2515,11 +2619,11 @@ export default function Layout(props: ParentProps) {
|
|||||||
}
|
}
|
||||||
return map
|
return map
|
||||||
})
|
})
|
||||||
const booted = createMemo((prev) => prev || workspace().store.status === "complete", false)
|
const booted = createMemo((prev) => prev || workspaceStore.status === "complete", false)
|
||||||
const loading = createMemo(() => !booted() && sessions().length === 0)
|
const loading = createMemo(() => !booted() && sessions().length === 0)
|
||||||
const hasMore = createMemo(() => workspace().store.sessionTotal > sessions().length)
|
const hasMore = createMemo(() => workspaceStore.sessionTotal > sessions().length)
|
||||||
const loadMore = async () => {
|
const loadMore = async () => {
|
||||||
workspace().setStore("limit", (limit) => limit + 5)
|
setWorkspaceStore("limit", (limit) => limit + 5)
|
||||||
await globalSync.project.loadSessions(props.project.worktree)
|
await globalSync.project.loadSessions(props.project.worktree)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2575,8 +2679,6 @@ export default function Layout(props: ParentProps) {
|
|||||||
|
|
||||||
if (!created?.directory) return
|
if (!created?.directory) return
|
||||||
|
|
||||||
setWorkspaceName(created.directory, created.branch, project.id, created.branch)
|
|
||||||
|
|
||||||
const local = project.worktree
|
const local = project.worktree
|
||||||
const key = workspaceKey(created.directory)
|
const key = workspaceKey(created.directory)
|
||||||
const root = workspaceKey(local)
|
const root = workspaceKey(local)
|
||||||
@@ -2833,7 +2935,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex h-full w-full overflow-hidden">
|
<div class="flex h-full w-full overflow-hidden">
|
||||||
<div class="w-16 shrink-0 bg-background-base flex flex-col items-center overflow-hidden" onMouseMove={aim.move}>
|
<div class="w-16 shrink-0 bg-background-base flex flex-col items-center overflow-hidden">
|
||||||
<div class="flex-1 min-h-0 w-full">
|
<div class="flex-1 min-h-0 w-full">
|
||||||
<DragDropProvider
|
<DragDropProvider
|
||||||
onDragStart={handleDragStart}
|
onDragStart={handleDragStart}
|
||||||
@@ -2893,7 +2995,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
icon="help"
|
icon="help"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="large"
|
size="large"
|
||||||
onClick={() => platform.openLink("https://opencode.ai/desktop-feedback")}
|
onClick={() => void platform.openLink("https://opencode.ai/desktop-feedback").catch(() => undefined)}
|
||||||
aria-label={language.t("sidebar.help")}
|
aria-label={language.t("sidebar.help")}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -2928,7 +3030,6 @@ export default function Layout(props: ParentProps) {
|
|||||||
navLeave.current = undefined
|
navLeave.current = undefined
|
||||||
}}
|
}}
|
||||||
onMouseLeave={() => {
|
onMouseLeave={() => {
|
||||||
aim.reset()
|
|
||||||
if (!sidebarHovering()) return
|
if (!sidebarHovering()) return
|
||||||
|
|
||||||
if (navLeave.current !== undefined) clearTimeout(navLeave.current)
|
if (navLeave.current !== undefined) clearTimeout(navLeave.current)
|
||||||
@@ -2944,7 +3045,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
</div>
|
</div>
|
||||||
<Show when={!layout.sidebar.opened() ? hoverProjectData() : undefined} keyed>
|
<Show when={!layout.sidebar.opened() ? hoverProjectData() : undefined} keyed>
|
||||||
{(project) => (
|
{(project) => (
|
||||||
<div class="absolute inset-y-0 left-16 z-50 flex" onMouseEnter={aim.reset}>
|
<div class="absolute inset-y-0 left-16 z-50 flex">
|
||||||
<SidebarPanel project={project} />
|
<SidebarPanel project={project} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,138 +0,0 @@
|
|||||||
type Point = { x: number; y: number }
|
|
||||||
|
|
||||||
export function createAim(props: {
|
|
||||||
enabled: () => boolean
|
|
||||||
active: () => string | undefined
|
|
||||||
el: () => HTMLElement | undefined
|
|
||||||
onActivate: (id: string) => void
|
|
||||||
delay?: number
|
|
||||||
max?: number
|
|
||||||
tolerance?: number
|
|
||||||
edge?: number
|
|
||||||
}) {
|
|
||||||
const state = {
|
|
||||||
locs: [] as Point[],
|
|
||||||
timer: undefined as number | undefined,
|
|
||||||
pending: undefined as string | undefined,
|
|
||||||
over: undefined as string | undefined,
|
|
||||||
last: undefined as Point | undefined,
|
|
||||||
}
|
|
||||||
|
|
||||||
const delay = props.delay ?? 250
|
|
||||||
const max = props.max ?? 4
|
|
||||||
const tolerance = props.tolerance ?? 80
|
|
||||||
const edge = props.edge ?? 18
|
|
||||||
|
|
||||||
const cancel = () => {
|
|
||||||
if (state.timer !== undefined) clearTimeout(state.timer)
|
|
||||||
state.timer = undefined
|
|
||||||
state.pending = undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const reset = () => {
|
|
||||||
cancel()
|
|
||||||
state.over = undefined
|
|
||||||
state.last = undefined
|
|
||||||
state.locs.length = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const move = (event: MouseEvent) => {
|
|
||||||
if (!props.enabled()) return
|
|
||||||
const el = props.el()
|
|
||||||
if (!el) return
|
|
||||||
|
|
||||||
const rect = el.getBoundingClientRect()
|
|
||||||
const x = event.clientX
|
|
||||||
const y = event.clientY
|
|
||||||
if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) return
|
|
||||||
|
|
||||||
state.locs.push({ x, y })
|
|
||||||
if (state.locs.length > max) state.locs.shift()
|
|
||||||
}
|
|
||||||
|
|
||||||
const wait = () => {
|
|
||||||
if (!props.enabled()) return 0
|
|
||||||
if (!props.active()) return 0
|
|
||||||
|
|
||||||
const el = props.el()
|
|
||||||
if (!el) return 0
|
|
||||||
if (state.locs.length < 2) return 0
|
|
||||||
|
|
||||||
const rect = el.getBoundingClientRect()
|
|
||||||
const loc = state.locs[state.locs.length - 1]
|
|
||||||
if (!loc) return 0
|
|
||||||
|
|
||||||
const prev = state.locs[0] ?? loc
|
|
||||||
if (prev.x < rect.left || prev.x > rect.right || prev.y < rect.top || prev.y > rect.bottom) return 0
|
|
||||||
if (state.last && loc.x === state.last.x && loc.y === state.last.y) return 0
|
|
||||||
|
|
||||||
if (rect.right - loc.x <= edge) {
|
|
||||||
state.last = loc
|
|
||||||
return delay
|
|
||||||
}
|
|
||||||
|
|
||||||
const upper = { x: rect.right, y: rect.top - tolerance }
|
|
||||||
const lower = { x: rect.right, y: rect.bottom + tolerance }
|
|
||||||
const slope = (a: Point, b: Point) => (b.y - a.y) / (b.x - a.x)
|
|
||||||
|
|
||||||
const decreasing = slope(loc, upper)
|
|
||||||
const increasing = slope(loc, lower)
|
|
||||||
const prevDecreasing = slope(prev, upper)
|
|
||||||
const prevIncreasing = slope(prev, lower)
|
|
||||||
|
|
||||||
if (decreasing < prevDecreasing && increasing > prevIncreasing) {
|
|
||||||
state.last = loc
|
|
||||||
return delay
|
|
||||||
}
|
|
||||||
|
|
||||||
state.last = undefined
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const activate = (id: string) => {
|
|
||||||
cancel()
|
|
||||||
props.onActivate(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = (id: string) => {
|
|
||||||
if (!id) return
|
|
||||||
if (props.active() === id) return
|
|
||||||
|
|
||||||
if (!props.active()) {
|
|
||||||
activate(id)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const ms = wait()
|
|
||||||
if (ms === 0) {
|
|
||||||
activate(id)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cancel()
|
|
||||||
state.pending = id
|
|
||||||
state.timer = window.setTimeout(() => {
|
|
||||||
state.timer = undefined
|
|
||||||
if (state.pending !== id) return
|
|
||||||
state.pending = undefined
|
|
||||||
if (!props.enabled()) return
|
|
||||||
if (!props.active()) return
|
|
||||||
if (state.over !== id) return
|
|
||||||
props.onActivate(id)
|
|
||||||
}, ms)
|
|
||||||
}
|
|
||||||
|
|
||||||
const enter = (id: string, event: MouseEvent) => {
|
|
||||||
if (!props.enabled()) return
|
|
||||||
state.over = id
|
|
||||||
move(event)
|
|
||||||
request(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
const leave = (id: string) => {
|
|
||||||
if (state.over === id) state.over = undefined
|
|
||||||
if (state.pending === id) cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
return { move, enter, leave, activate, request, cancel, reset }
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-app",
|
"name": "@opencode-ai/console-app",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ export const oaCompatHelper: ProviderHelper = () => ({
|
|||||||
modifyUrl: (providerApi: string) => providerApi + "/chat/completions",
|
modifyUrl: (providerApi: string) => providerApi + "/chat/completions",
|
||||||
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
|
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
|
||||||
headers.set("authorization", `Bearer ${apiKey}`)
|
headers.set("authorization", `Bearer ${apiKey}`)
|
||||||
headers.set("x-session-affinity", headers.get("x-opencode-session") ?? "")
|
|
||||||
},
|
},
|
||||||
modifyBody: (body: Record<string, any>) => {
|
modifyBody: (body: Record<string, any>) => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"name": "@opencode-ai/console-core",
|
"name": "@opencode-ai/console-core",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -25,9 +25,9 @@
|
|||||||
"db": "sst shell drizzle-kit",
|
"db": "sst shell drizzle-kit",
|
||||||
"db-dev": "sst shell --stage dev -- drizzle-kit",
|
"db-dev": "sst shell --stage dev -- drizzle-kit",
|
||||||
"db-prod": "sst shell --stage production -- drizzle-kit",
|
"db-prod": "sst shell --stage production -- drizzle-kit",
|
||||||
"shell": "sst shell",
|
"shell": "sst shell -- bun",
|
||||||
"shell-dev": "sst shell --stage dev",
|
"shell-dev": "sst shell --stage dev -- bun",
|
||||||
"shell-prod": "sst shell --stage production",
|
"shell-prod": "sst shell --stage production -- bun",
|
||||||
"update-models": "script/update-models.ts",
|
"update-models": "script/update-models.ts",
|
||||||
"promote-models-to-dev": "script/promote-models.ts dev",
|
"promote-models-to-dev": "script/promote-models.ts dev",
|
||||||
"promote-models-to-prod": "script/promote-models.ts production",
|
"promote-models-to-prod": "script/promote-models.ts production",
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ const oldValues = Array.from({ length: PARTS }, (_, i) => {
|
|||||||
?.split("=")
|
?.split("=")
|
||||||
.slice(1)
|
.slice(1)
|
||||||
.join("=")
|
.join("=")
|
||||||
if (!value) throw new Error(`ZEN_MODELS${i + 1} not found`)
|
// TODO
|
||||||
return value
|
//if (!value) throw new Error(`ZEN_MODELS${i + 1} not found`)
|
||||||
|
//return value
|
||||||
|
return value ?? ""
|
||||||
})
|
})
|
||||||
|
|
||||||
// store the prettified json to a temp file
|
// store the prettified json to a temp file
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-function",
|
"name": "@opencode-ai/console-function",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-mail",
|
"name": "@opencode-ai/console-mail",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jsx-email/all": "2.2.3",
|
"@jsx-email/all": "2.2.3",
|
||||||
"@jsx-email/cli": "1.4.3",
|
"@jsx-email/cli": "1.4.3",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/desktop",
|
"name": "@opencode-ai/desktop",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -8,16 +8,7 @@
|
|||||||
"opener:default",
|
"opener:default",
|
||||||
{
|
{
|
||||||
"identifier": "opener:allow-open-path",
|
"identifier": "opener:allow-open-path",
|
||||||
"allow": [
|
"allow": [{ "path": "/**", "app": true }]
|
||||||
{ "path": "**/*" },
|
|
||||||
{ "path": "/**/*" },
|
|
||||||
{ "path": "**/.*/*/**" },
|
|
||||||
{ "path": "/**/.*/*/**" },
|
|
||||||
{ "path": "**/*", "app": true },
|
|
||||||
{ "path": "/**/*", "app": true },
|
|
||||||
{ "path": "**/.*/*/**", "app": true },
|
|
||||||
{ "path": "/**/.*/*/**", "app": true }
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"deep-link:default",
|
"deep-link:default",
|
||||||
"core:window:allow-start-dragging",
|
"core:window:allow-start-dragging",
|
||||||
|
|||||||
@@ -42,13 +42,6 @@
|
|||||||
"active": true,
|
"active": true,
|
||||||
"targets": ["deb", "rpm", "dmg", "nsis", "app"],
|
"targets": ["deb", "rpm", "dmg", "nsis", "app"],
|
||||||
"externalBin": ["sidecars/opencode-cli"],
|
"externalBin": ["sidecars/opencode-cli"],
|
||||||
"linux": {
|
|
||||||
"rpm": {
|
|
||||||
"compression": {
|
|
||||||
"type": "none"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"macOS": {
|
"macOS": {
|
||||||
"entitlements": "./entitlements.plist"
|
"entitlements": "./entitlements.plist"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,11 +21,6 @@
|
|||||||
"files": {
|
"files": {
|
||||||
"/usr/share/metainfo/ai.opencode.opencode.metainfo.xml": "release/appstream.metainfo.xml"
|
"/usr/share/metainfo/ai.opencode.opencode.metainfo.xml": "release/appstream.metainfo.xml"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"rpm": {
|
|
||||||
"compression": {
|
|
||||||
"type": "none"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
// This file has been generated by Tauri Specta. Do not edit this file manually.
|
// This file has been generated by Tauri Specta. Do not edit this file manually.
|
||||||
|
|
||||||
import { invoke as __TAURI_INVOKE, Channel } from "@tauri-apps/api/core"
|
import { invoke as __TAURI_INVOKE, Channel } from '@tauri-apps/api/core';
|
||||||
|
|
||||||
/** Commands */
|
/** Commands */
|
||||||
export const commands = {
|
export const commands = {
|
||||||
killSidecar: () => __TAURI_INVOKE<void>("kill_sidecar"),
|
killSidecar: () => __TAURI_INVOKE<void>("kill_sidecar"),
|
||||||
installCli: () => __TAURI_INVOKE<string>("install_cli"),
|
installCli: () => __TAURI_INVOKE<string>("install_cli"),
|
||||||
ensureServerReady: () => __TAURI_INVOKE<ServerReadyData>("ensure_server_ready"),
|
ensureServerReady: () => __TAURI_INVOKE<ServerReadyData>("ensure_server_ready"),
|
||||||
getDefaultServerUrl: () => __TAURI_INVOKE<string | null>("get_default_server_url"),
|
getDefaultServerUrl: () => __TAURI_INVOKE<string | null>("get_default_server_url"),
|
||||||
setDefaultServerUrl: (url: string | null) => __TAURI_INVOKE<null>("set_default_server_url", { url }),
|
setDefaultServerUrl: (url: string | null) => __TAURI_INVOKE<null>("set_default_server_url", { url }),
|
||||||
parseMarkdownCommand: (markdown: string) => __TAURI_INVOKE<string>("parse_markdown_command", { markdown }),
|
parseMarkdownCommand: (markdown: string) => __TAURI_INVOKE<string>("parse_markdown_command", { markdown }),
|
||||||
}
|
};
|
||||||
|
|
||||||
/* Types */
|
/* Types */
|
||||||
export type ServerReadyData = {
|
export type ServerReadyData = {
|
||||||
url: string
|
url: string,
|
||||||
password: string | null
|
password: string | null,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
export const dict = {
|
|
||||||
"desktop.menu.checkForUpdates": "Provjeri ažuriranja...",
|
|
||||||
"desktop.menu.installCli": "Instaliraj CLI...",
|
|
||||||
"desktop.menu.reloadWebview": "Ponovo učitavanje webview-a",
|
|
||||||
"desktop.menu.restart": "Restartuj",
|
|
||||||
|
|
||||||
"desktop.dialog.chooseFolder": "Odaberi folder",
|
|
||||||
"desktop.dialog.chooseFile": "Odaberi datoteku",
|
|
||||||
"desktop.dialog.saveFile": "Sačuvaj datoteku",
|
|
||||||
|
|
||||||
"desktop.updater.checkFailed.title": "Provjera ažuriranja nije uspjela",
|
|
||||||
"desktop.updater.checkFailed.message": "Nije moguće provjeriti ažuriranja",
|
|
||||||
"desktop.updater.none.title": "Nema dostupnog ažuriranja",
|
|
||||||
"desktop.updater.none.message": "Već koristiš najnoviju verziju OpenCode-a",
|
|
||||||
"desktop.updater.downloadFailed.title": "Ažuriranje nije uspjelo",
|
|
||||||
"desktop.updater.downloadFailed.message": "Neuspjelo preuzimanje ažuriranja",
|
|
||||||
"desktop.updater.downloaded.title": "Ažuriranje preuzeto",
|
|
||||||
"desktop.updater.downloaded.prompt":
|
|
||||||
"Verzija {{version}} OpenCode-a je preuzeta. Želiš li da je instaliraš i ponovo pokreneš aplikaciju?",
|
|
||||||
"desktop.updater.installFailed.title": "Ažuriranje nije uspjelo",
|
|
||||||
"desktop.updater.installFailed.message": "Neuspjela instalacija ažuriranja",
|
|
||||||
|
|
||||||
"desktop.cli.installed.title": "CLI instaliran",
|
|
||||||
"desktop.cli.installed.message":
|
|
||||||
"CLI je instaliran u {{path}}\n\nRestartuj terminal da bi koristio komandu 'opencode'.",
|
|
||||||
"desktop.cli.failed.title": "Instalacija nije uspjela",
|
|
||||||
"desktop.cli.failed.message": "Neuspjela instalacija CLI-a: {{error}}",
|
|
||||||
|
|
||||||
"desktop.error.serverStartFailed.title": "OpenCode se nije mogao pokrenuti",
|
|
||||||
"desktop.error.serverStartFailed.description":
|
|
||||||
"Lokalni OpenCode server se nije mogao pokrenuti. Restartuj aplikaciju ili provjeri mrežne postavke (VPN/proxy) i pokušaj ponovo.",
|
|
||||||
}
|
|
||||||
@@ -15,7 +15,6 @@ import { dict as desktopRu } from "./ru"
|
|||||||
import { dict as desktopAr } from "./ar"
|
import { dict as desktopAr } from "./ar"
|
||||||
import { dict as desktopNo } from "./no"
|
import { dict as desktopNo } from "./no"
|
||||||
import { dict as desktopBr } from "./br"
|
import { dict as desktopBr } from "./br"
|
||||||
import { dict as desktopBs } from "./bs"
|
|
||||||
|
|
||||||
import { dict as appEn } from "../../../app/src/i18n/en"
|
import { dict as appEn } from "../../../app/src/i18n/en"
|
||||||
import { dict as appZh } from "../../../app/src/i18n/zh"
|
import { dict as appZh } from "../../../app/src/i18n/zh"
|
||||||
@@ -31,45 +30,13 @@ import { dict as appRu } from "../../../app/src/i18n/ru"
|
|||||||
import { dict as appAr } from "../../../app/src/i18n/ar"
|
import { dict as appAr } from "../../../app/src/i18n/ar"
|
||||||
import { dict as appNo } from "../../../app/src/i18n/no"
|
import { dict as appNo } from "../../../app/src/i18n/no"
|
||||||
import { dict as appBr } from "../../../app/src/i18n/br"
|
import { dict as appBr } from "../../../app/src/i18n/br"
|
||||||
import { dict as appBs } from "../../../app/src/i18n/bs"
|
|
||||||
|
|
||||||
export type Locale =
|
export type Locale = "en" | "zh" | "zht" | "ko" | "de" | "es" | "fr" | "da" | "ja" | "pl" | "ru" | "ar" | "no" | "br"
|
||||||
| "en"
|
|
||||||
| "zh"
|
|
||||||
| "zht"
|
|
||||||
| "ko"
|
|
||||||
| "de"
|
|
||||||
| "es"
|
|
||||||
| "fr"
|
|
||||||
| "da"
|
|
||||||
| "ja"
|
|
||||||
| "pl"
|
|
||||||
| "ru"
|
|
||||||
| "ar"
|
|
||||||
| "no"
|
|
||||||
| "br"
|
|
||||||
| "bs"
|
|
||||||
|
|
||||||
type RawDictionary = typeof appEn & typeof desktopEn
|
type RawDictionary = typeof appEn & typeof desktopEn
|
||||||
type Dictionary = i18n.Flatten<RawDictionary>
|
type Dictionary = i18n.Flatten<RawDictionary>
|
||||||
|
|
||||||
const LOCALES: readonly Locale[] = [
|
const LOCALES: readonly Locale[] = ["en", "zh", "zht", "ko", "de", "es", "fr", "da", "ja", "pl", "ru", "ar", "no", "br"]
|
||||||
"en",
|
|
||||||
"zh",
|
|
||||||
"zht",
|
|
||||||
"ko",
|
|
||||||
"de",
|
|
||||||
"es",
|
|
||||||
"fr",
|
|
||||||
"da",
|
|
||||||
"ja",
|
|
||||||
"pl",
|
|
||||||
"ru",
|
|
||||||
"bs",
|
|
||||||
"ar",
|
|
||||||
"no",
|
|
||||||
"br",
|
|
||||||
]
|
|
||||||
|
|
||||||
function detectLocale(): Locale {
|
function detectLocale(): Locale {
|
||||||
if (typeof navigator !== "object") return "en"
|
if (typeof navigator !== "object") return "en"
|
||||||
@@ -97,7 +64,6 @@ function detectLocale(): Locale {
|
|||||||
)
|
)
|
||||||
return "no"
|
return "no"
|
||||||
if (language.toLowerCase().startsWith("pt")) return "br"
|
if (language.toLowerCase().startsWith("pt")) return "br"
|
||||||
if (language.toLowerCase().startsWith("bs")) return "bs"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "en"
|
return "en"
|
||||||
@@ -142,7 +108,6 @@ function build(locale: Locale): Dictionary {
|
|||||||
if (locale === "ar") return { ...base, ...i18n.flatten(appAr), ...i18n.flatten(desktopAr) }
|
if (locale === "ar") return { ...base, ...i18n.flatten(appAr), ...i18n.flatten(desktopAr) }
|
||||||
if (locale === "no") return { ...base, ...i18n.flatten(appNo), ...i18n.flatten(desktopNo) }
|
if (locale === "no") return { ...base, ...i18n.flatten(appNo), ...i18n.flatten(desktopNo) }
|
||||||
if (locale === "br") return { ...base, ...i18n.flatten(appBr), ...i18n.flatten(desktopBr) }
|
if (locale === "br") return { ...base, ...i18n.flatten(appBr), ...i18n.flatten(desktopBr) }
|
||||||
if (locale === "bs") return { ...base, ...i18n.flatten(appBs), ...i18n.flatten(desktopBs) }
|
|
||||||
return { ...base, ...i18n.flatten(appKo), ...i18n.flatten(desktopKo) }
|
return { ...base, ...i18n.flatten(appKo), ...i18n.flatten(desktopKo) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import { render } from "solid-js/web"
|
|||||||
import { AppBaseProviders, AppInterface, PlatformProvider, Platform } from "@opencode-ai/app"
|
import { AppBaseProviders, AppInterface, PlatformProvider, Platform } from "@opencode-ai/app"
|
||||||
import { open, save } from "@tauri-apps/plugin-dialog"
|
import { open, save } from "@tauri-apps/plugin-dialog"
|
||||||
import { getCurrent, onOpenUrl } from "@tauri-apps/plugin-deep-link"
|
import { getCurrent, onOpenUrl } from "@tauri-apps/plugin-deep-link"
|
||||||
import { openPath as openerOpenPath } from "@tauri-apps/plugin-opener"
|
import { openPath, openUrl } from "@tauri-apps/plugin-opener"
|
||||||
import { open as shellOpen } from "@tauri-apps/plugin-shell"
|
|
||||||
import { type as ostype } from "@tauri-apps/plugin-os"
|
import { type as ostype } from "@tauri-apps/plugin-os"
|
||||||
import { check, Update } from "@tauri-apps/plugin-updater"
|
import { check, Update } from "@tauri-apps/plugin-updater"
|
||||||
import { getCurrentWindow } from "@tauri-apps/api/window"
|
import { getCurrentWindow } from "@tauri-apps/api/window"
|
||||||
@@ -31,6 +30,17 @@ if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
|
|||||||
|
|
||||||
void initI18n()
|
void initI18n()
|
||||||
|
|
||||||
|
// Floating UI can call getComputedStyle with non-elements (e.g., null refs, virtual elements).
|
||||||
|
// This happens on all platforms (WebView2 on Windows, WKWebView on macOS), not just Windows.
|
||||||
|
const originalGetComputedStyle = window.getComputedStyle
|
||||||
|
window.getComputedStyle = ((elt: Element, pseudoElt?: string | null) => {
|
||||||
|
if (!(elt instanceof Element)) {
|
||||||
|
// Fall back to a safe element when a non-element is passed.
|
||||||
|
return originalGetComputedStyle(document.documentElement, pseudoElt ?? undefined)
|
||||||
|
}
|
||||||
|
return originalGetComputedStyle(elt, pseudoElt ?? undefined)
|
||||||
|
}) as typeof window.getComputedStyle
|
||||||
|
|
||||||
let update: Update | null = null
|
let update: Update | null = null
|
||||||
|
|
||||||
const deepLinkEvent = "opencode:deep-link"
|
const deepLinkEvent = "opencode:deep-link"
|
||||||
@@ -84,12 +94,10 @@ const createPlatform = (password: Accessor<string | null>): Platform => ({
|
|||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
|
|
||||||
openLink(url: string) {
|
openLink(url: string, openWith?: string) {
|
||||||
void shellOpen(url).catch(() => undefined)
|
const isUrl = /^(https?:|mailto:|tel:|opencode:)/.test(url)
|
||||||
},
|
if (isUrl) return openUrl(url, openWith)
|
||||||
|
return openPath(url, openWith)
|
||||||
openPath(path: string, app?: string) {
|
|
||||||
return openerOpenPath(path, app)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
back() {
|
back() {
|
||||||
@@ -353,7 +361,7 @@ render(() => {
|
|||||||
const link = (e.target as HTMLElement).closest("a.external-link") as HTMLAnchorElement | null
|
const link = (e.target as HTMLElement).closest("a.external-link") as HTMLAnchorElement | null
|
||||||
if (link?.href) {
|
if (link?.href) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
platform.openLink(link.href)
|
void platform.openLink(link.href).catch(() => undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/enterprise",
|
"name": "@opencode-ai/enterprise",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
id = "opencode"
|
id = "opencode"
|
||||||
name = "OpenCode"
|
name = "OpenCode"
|
||||||
description = "The open source coding agent."
|
description = "The open source coding agent."
|
||||||
version = "1.1.52"
|
version = "1.1.51"
|
||||||
schema_version = 1
|
schema_version = 1
|
||||||
authors = ["Anomaly"]
|
authors = ["Anomaly"]
|
||||||
repository = "https://github.com/anomalyco/opencode"
|
repository = "https://github.com/anomalyco/opencode"
|
||||||
@@ -11,26 +11,26 @@ name = "OpenCode"
|
|||||||
icon = "./icons/opencode.svg"
|
icon = "./icons/opencode.svg"
|
||||||
|
|
||||||
[agent_servers.opencode.targets.darwin-aarch64]
|
[agent_servers.opencode.targets.darwin-aarch64]
|
||||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.52/opencode-darwin-arm64.zip"
|
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.51/opencode-darwin-arm64.zip"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.darwin-x86_64]
|
[agent_servers.opencode.targets.darwin-x86_64]
|
||||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.52/opencode-darwin-x64.zip"
|
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.51/opencode-darwin-x64.zip"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.linux-aarch64]
|
[agent_servers.opencode.targets.linux-aarch64]
|
||||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.52/opencode-linux-arm64.tar.gz"
|
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.51/opencode-linux-arm64.tar.gz"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.linux-x86_64]
|
[agent_servers.opencode.targets.linux-x86_64]
|
||||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.52/opencode-linux-x64.tar.gz"
|
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.51/opencode-linux-x64.tar.gz"
|
||||||
cmd = "./opencode"
|
cmd = "./opencode"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|
||||||
[agent_servers.opencode.targets.windows-x86_64]
|
[agent_servers.opencode.targets.windows-x86_64]
|
||||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.52/opencode-windows-x64.zip"
|
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.51/opencode-windows-x64.zip"
|
||||||
cmd = "./opencode.exe"
|
cmd = "./opencode.exe"
|
||||||
args = ["acp"]
|
args = ["acp"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/function",
|
"name": "@opencode-ai/function",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "1.11.1",
|
"@actions/core": "1.11.1",
|
||||||
"@actions/github": "6.0.1",
|
"@actions/github": "6.0.1",
|
||||||
"@agentclientprotocol/sdk": "0.14.1",
|
"@agentclientprotocol/sdk": "0.13.0",
|
||||||
"@ai-sdk/amazon-bedrock": "3.0.74",
|
"@ai-sdk/amazon-bedrock": "3.0.74",
|
||||||
"@ai-sdk/anthropic": "2.0.58",
|
"@ai-sdk/anthropic": "2.0.58",
|
||||||
"@ai-sdk/azure": "2.0.91",
|
"@ai-sdk/azure": "2.0.91",
|
||||||
@@ -69,10 +69,9 @@
|
|||||||
"@ai-sdk/provider-utils": "3.0.20",
|
"@ai-sdk/provider-utils": "3.0.20",
|
||||||
"@ai-sdk/togetherai": "1.0.34",
|
"@ai-sdk/togetherai": "1.0.34",
|
||||||
"@ai-sdk/vercel": "1.0.33",
|
"@ai-sdk/vercel": "1.0.33",
|
||||||
"@ai-sdk/xai": "2.0.51",
|
"@ai-sdk/xai": "2.0.56",
|
||||||
"@clack/prompts": "1.0.0-alpha.1",
|
"@clack/prompts": "1.0.0-alpha.1",
|
||||||
"@gitlab/gitlab-ai-provider": "3.4.0",
|
"@gitlab/gitlab-ai-provider": "3.4.0",
|
||||||
"@gitlab/opencode-gitlab-auth": "1.3.2",
|
|
||||||
"@hono/standard-validator": "0.1.5",
|
"@hono/standard-validator": "0.1.5",
|
||||||
"@hono/zod-validator": "catalog:",
|
"@hono/zod-validator": "catalog:",
|
||||||
"@modelcontextprotocol/sdk": "1.25.2",
|
"@modelcontextprotocol/sdk": "1.25.2",
|
||||||
|
|||||||
@@ -95,7 +95,73 @@ if (!Script.preview) {
|
|||||||
"",
|
"",
|
||||||
].join("\n")
|
].join("\n")
|
||||||
|
|
||||||
for (const [pkg, pkgbuild] of [["opencode-bin", binaryPkgbuild]]) {
|
// Source-based PKGBUILD for opencode
|
||||||
|
const sourcePkgbuild = [
|
||||||
|
"# Maintainer: dax",
|
||||||
|
"# Maintainer: adam",
|
||||||
|
"",
|
||||||
|
"pkgname='opencode'",
|
||||||
|
`pkgver=${pkgver}`,
|
||||||
|
`_subver=${_subver}`,
|
||||||
|
"options=('!debug' '!strip')",
|
||||||
|
"pkgrel=1",
|
||||||
|
"pkgdesc='The AI coding agent built for the terminal.'",
|
||||||
|
"url='https://github.com/anomalyco/opencode'",
|
||||||
|
"arch=('aarch64' 'x86_64')",
|
||||||
|
"license=('MIT')",
|
||||||
|
"provides=('opencode')",
|
||||||
|
"conflicts=('opencode-bin')",
|
||||||
|
"depends=('ripgrep')",
|
||||||
|
"makedepends=('git' 'bun' 'go')",
|
||||||
|
"",
|
||||||
|
`source=("opencode-\${pkgver}.tar.gz::https://github.com/anomalyco/opencode/archive/v\${pkgver}\${_subver}.tar.gz")`,
|
||||||
|
`sha256sums=('SKIP')`,
|
||||||
|
"",
|
||||||
|
"build() {",
|
||||||
|
` cd "opencode-\${pkgver}"`,
|
||||||
|
` bun install`,
|
||||||
|
" cd ./packages/opencode",
|
||||||
|
` OPENCODE_CHANNEL=latest OPENCODE_VERSION=${pkgver} bun run ./script/build.ts --single`,
|
||||||
|
"}",
|
||||||
|
"",
|
||||||
|
"package() {",
|
||||||
|
` cd "opencode-\${pkgver}/packages/opencode"`,
|
||||||
|
' mkdir -p "${pkgdir}/usr/bin"',
|
||||||
|
' target_arch="x64"',
|
||||||
|
' case "$CARCH" in',
|
||||||
|
' x86_64) target_arch="x64" ;;',
|
||||||
|
' aarch64) target_arch="arm64" ;;',
|
||||||
|
' *) printf "unsupported architecture: %s\\n" "$CARCH" >&2 ; return 1 ;;',
|
||||||
|
" esac",
|
||||||
|
' libc=""',
|
||||||
|
" if command -v ldd >/dev/null 2>&1; then",
|
||||||
|
" if ldd --version 2>&1 | grep -qi musl; then",
|
||||||
|
' libc="-musl"',
|
||||||
|
" fi",
|
||||||
|
" fi",
|
||||||
|
' if [ -z "$libc" ] && ls /lib/ld-musl-* >/dev/null 2>&1; then',
|
||||||
|
' libc="-musl"',
|
||||||
|
" fi",
|
||||||
|
' base=""',
|
||||||
|
' if [ "$target_arch" = "x64" ]; then',
|
||||||
|
" if ! grep -qi avx2 /proc/cpuinfo 2>/dev/null; then",
|
||||||
|
' base="-baseline"',
|
||||||
|
" fi",
|
||||||
|
" fi",
|
||||||
|
' bin="dist/opencode-linux-${target_arch}${base}${libc}/bin/opencode"',
|
||||||
|
' if [ ! -f "$bin" ]; then',
|
||||||
|
' printf "unable to find binary for %s%s%s\\n" "$target_arch" "$base" "$libc" >&2',
|
||||||
|
" return 1",
|
||||||
|
" fi",
|
||||||
|
' install -Dm755 "$bin" "${pkgdir}/usr/bin/opencode"',
|
||||||
|
"}",
|
||||||
|
"",
|
||||||
|
].join("\n")
|
||||||
|
|
||||||
|
for (const [pkg, pkgbuild] of [
|
||||||
|
["opencode-bin", binaryPkgbuild],
|
||||||
|
["opencode", sourcePkgbuild],
|
||||||
|
]) {
|
||||||
for (let i = 0; i < 30; i++) {
|
for (let i = 0; i < 30; i++) {
|
||||||
try {
|
try {
|
||||||
await $`rm -rf ./dist/aur-${pkg}`
|
await $`rm -rf ./dist/aur-${pkg}`
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import {
|
|||||||
type SetSessionModeResponse,
|
type SetSessionModeResponse,
|
||||||
type ToolCallContent,
|
type ToolCallContent,
|
||||||
type ToolKind,
|
type ToolKind,
|
||||||
type Usage,
|
|
||||||
} from "@agentclientprotocol/sdk"
|
} from "@agentclientprotocol/sdk"
|
||||||
|
|
||||||
import { Log } from "../util/log"
|
import { Log } from "../util/log"
|
||||||
@@ -39,7 +38,7 @@ import { Config } from "@/config/config"
|
|||||||
import { Todo } from "@/session/todo"
|
import { Todo } from "@/session/todo"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { LoadAPIKeyError } from "ai"
|
import { LoadAPIKeyError } from "ai"
|
||||||
import type { AssistantMessage, Event, OpencodeClient, SessionMessageResponse } from "@opencode-ai/sdk/v2"
|
import type { Event, OpencodeClient, SessionMessageResponse } from "@opencode-ai/sdk/v2"
|
||||||
import { applyPatch } from "diff"
|
import { applyPatch } from "diff"
|
||||||
|
|
||||||
type ModeOption = { id: string; name: string; description?: string }
|
type ModeOption = { id: string; name: string; description?: string }
|
||||||
@@ -50,74 +49,6 @@ const DEFAULT_VARIANT_VALUE = "default"
|
|||||||
export namespace ACP {
|
export namespace ACP {
|
||||||
const log = Log.create({ service: "acp-agent" })
|
const log = Log.create({ service: "acp-agent" })
|
||||||
|
|
||||||
async function getContextLimit(
|
|
||||||
sdk: OpencodeClient,
|
|
||||||
providerID: string,
|
|
||||||
modelID: string,
|
|
||||||
directory: string,
|
|
||||||
): Promise<number | null> {
|
|
||||||
const providers = await sdk.config
|
|
||||||
.providers({ directory })
|
|
||||||
.then((x) => x.data?.providers ?? [])
|
|
||||||
.catch((error) => {
|
|
||||||
log.error("failed to get providers for context limit", { error })
|
|
||||||
return []
|
|
||||||
})
|
|
||||||
|
|
||||||
const provider = providers.find((p) => p.id === providerID)
|
|
||||||
const model = provider?.models[modelID]
|
|
||||||
return model?.limit.context ?? null
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendUsageUpdate(
|
|
||||||
connection: AgentSideConnection,
|
|
||||||
sdk: OpencodeClient,
|
|
||||||
sessionID: string,
|
|
||||||
directory: string,
|
|
||||||
): Promise<void> {
|
|
||||||
const messages = await sdk.session
|
|
||||||
.messages({ sessionID, directory }, { throwOnError: true })
|
|
||||||
.then((x) => x.data)
|
|
||||||
.catch((error) => {
|
|
||||||
log.error("failed to fetch messages for usage update", { error })
|
|
||||||
return undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!messages) return
|
|
||||||
|
|
||||||
const assistantMessages = messages.filter(
|
|
||||||
(m): m is { info: AssistantMessage; parts: SessionMessageResponse["parts"] } => m.info.role === "assistant",
|
|
||||||
)
|
|
||||||
|
|
||||||
const lastAssistant = assistantMessages[assistantMessages.length - 1]
|
|
||||||
if (!lastAssistant) return
|
|
||||||
|
|
||||||
const msg = lastAssistant.info
|
|
||||||
const size = await getContextLimit(sdk, msg.providerID, msg.modelID, directory)
|
|
||||||
|
|
||||||
if (!size) {
|
|
||||||
// Cannot calculate usage without known context size
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const used = msg.tokens.input + (msg.tokens.cache?.read ?? 0)
|
|
||||||
const totalCost = assistantMessages.reduce((sum, m) => sum + m.info.cost, 0)
|
|
||||||
|
|
||||||
await connection
|
|
||||||
.sessionUpdate({
|
|
||||||
sessionId: sessionID,
|
|
||||||
update: {
|
|
||||||
sessionUpdate: "usage_update",
|
|
||||||
used,
|
|
||||||
size,
|
|
||||||
cost: { amount: totalCost, currency: "USD" },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
log.error("failed to send usage update", { error })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function init({ sdk: _sdk }: { sdk: OpencodeClient }) {
|
export async function init({ sdk: _sdk }: { sdk: OpencodeClient }) {
|
||||||
return {
|
return {
|
||||||
create: (connection: AgentSideConnection, fullConfig: ACPConfig) => {
|
create: (connection: AgentSideConnection, fullConfig: ACPConfig) => {
|
||||||
@@ -615,8 +546,6 @@ export namespace ACP {
|
|||||||
await this.processMessage(msg)
|
await this.processMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
await sendUsageUpdate(this.connection, this.sdk, sessionId, directory)
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const error = MessageV2.fromError(e, {
|
const error = MessageV2.fromError(e, {
|
||||||
@@ -725,8 +654,6 @@ export namespace ACP {
|
|||||||
await this.processMessage(msg)
|
await this.processMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
await sendUsageUpdate(this.connection, this.sdk, sessionId, directory)
|
|
||||||
|
|
||||||
return mode
|
return mode
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const error = MessageV2.fromError(e, {
|
const error = MessageV2.fromError(e, {
|
||||||
@@ -750,15 +677,11 @@ export namespace ACP {
|
|||||||
|
|
||||||
log.info("resume_session", { sessionId, mcpServers: mcpServers.length })
|
log.info("resume_session", { sessionId, mcpServers: mcpServers.length })
|
||||||
|
|
||||||
const result = await this.loadSessionMode({
|
return this.loadSessionMode({
|
||||||
cwd: directory,
|
cwd: directory,
|
||||||
mcpServers,
|
mcpServers,
|
||||||
sessionId,
|
sessionId,
|
||||||
})
|
})
|
||||||
|
|
||||||
await sendUsageUpdate(this.connection, this.sdk, sessionId, directory)
|
|
||||||
|
|
||||||
return result
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const error = MessageV2.fromError(e, {
|
const error = MessageV2.fromError(e, {
|
||||||
providerID: this.config.defaultModel?.providerID ?? "unknown",
|
providerID: this.config.defaultModel?.providerID ?? "unknown",
|
||||||
@@ -1316,22 +1239,13 @@ export namespace ACP {
|
|||||||
return { name, args: rest.join(" ").trim() }
|
return { name, args: rest.join(" ").trim() }
|
||||||
})()
|
})()
|
||||||
|
|
||||||
const buildUsage = (msg: AssistantMessage): Usage => ({
|
const done = {
|
||||||
totalTokens:
|
stopReason: "end_turn" as const,
|
||||||
msg.tokens.input +
|
_meta: {},
|
||||||
msg.tokens.output +
|
}
|
||||||
msg.tokens.reasoning +
|
|
||||||
(msg.tokens.cache?.read ?? 0) +
|
|
||||||
(msg.tokens.cache?.write ?? 0),
|
|
||||||
inputTokens: msg.tokens.input,
|
|
||||||
outputTokens: msg.tokens.output,
|
|
||||||
thoughtTokens: msg.tokens.reasoning || undefined,
|
|
||||||
cachedReadTokens: msg.tokens.cache?.read || undefined,
|
|
||||||
cachedWriteTokens: msg.tokens.cache?.write || undefined,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!cmd) {
|
if (!cmd) {
|
||||||
const response = await this.sdk.session.prompt({
|
await this.sdk.session.prompt({
|
||||||
sessionID,
|
sessionID,
|
||||||
model: {
|
model: {
|
||||||
providerID: model.providerID,
|
providerID: model.providerID,
|
||||||
@@ -1342,22 +1256,14 @@ export namespace ACP {
|
|||||||
agent,
|
agent,
|
||||||
directory,
|
directory,
|
||||||
})
|
})
|
||||||
const msg = response.data?.info
|
return done
|
||||||
|
|
||||||
await sendUsageUpdate(this.connection, this.sdk, sessionID, directory)
|
|
||||||
|
|
||||||
return {
|
|
||||||
stopReason: "end_turn" as const,
|
|
||||||
usage: msg ? buildUsage(msg) : undefined,
|
|
||||||
_meta: {},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const command = await this.config.sdk.command
|
const command = await this.config.sdk.command
|
||||||
.list({ directory }, { throwOnError: true })
|
.list({ directory }, { throwOnError: true })
|
||||||
.then((x) => x.data!.find((c) => c.name === cmd.name))
|
.then((x) => x.data!.find((c) => c.name === cmd.name))
|
||||||
if (command) {
|
if (command) {
|
||||||
const response = await this.sdk.session.command({
|
await this.sdk.session.command({
|
||||||
sessionID,
|
sessionID,
|
||||||
command: command.name,
|
command: command.name,
|
||||||
arguments: cmd.args,
|
arguments: cmd.args,
|
||||||
@@ -1365,15 +1271,7 @@ export namespace ACP {
|
|||||||
agent,
|
agent,
|
||||||
directory,
|
directory,
|
||||||
})
|
})
|
||||||
const msg = response.data?.info
|
return done
|
||||||
|
|
||||||
await sendUsageUpdate(this.connection, this.sdk, sessionID, directory)
|
|
||||||
|
|
||||||
return {
|
|
||||||
stopReason: "end_turn" as const,
|
|
||||||
usage: msg ? buildUsage(msg) : undefined,
|
|
||||||
_meta: {},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (cmd.name) {
|
switch (cmd.name) {
|
||||||
@@ -1390,12 +1288,7 @@ export namespace ACP {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
await sendUsageUpdate(this.connection, this.sdk, sessionID, directory)
|
return done
|
||||||
|
|
||||||
return {
|
|
||||||
stopReason: "end_turn" as const,
|
|
||||||
_meta: {},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async cancel(params: CancelNotification) {
|
async cancel(params: CancelNotification) {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { NamedError } from "@opencode-ai/util/error"
|
|||||||
import { readableStreamToText } from "bun"
|
import { readableStreamToText } from "bun"
|
||||||
import { Lock } from "../util/lock"
|
import { Lock } from "../util/lock"
|
||||||
import { PackageRegistry } from "./registry"
|
import { PackageRegistry } from "./registry"
|
||||||
import { proxied } from "@/util/proxied"
|
|
||||||
|
|
||||||
export namespace BunProc {
|
export namespace BunProc {
|
||||||
const log = Log.create({ service: "bun" })
|
const log = Log.create({ service: "bun" })
|
||||||
@@ -87,13 +86,20 @@ export namespace BunProc {
|
|||||||
log.info("Cached version is outdated, proceeding with install", { pkg, cachedVersion })
|
log.info("Cached version is outdated, proceeding with install", { pkg, cachedVersion })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const proxied = !!(
|
||||||
|
process.env.HTTP_PROXY ||
|
||||||
|
process.env.HTTPS_PROXY ||
|
||||||
|
process.env.http_proxy ||
|
||||||
|
process.env.https_proxy
|
||||||
|
)
|
||||||
|
|
||||||
// Build command arguments
|
// Build command arguments
|
||||||
const args = [
|
const args = [
|
||||||
"add",
|
"add",
|
||||||
"--force",
|
"--force",
|
||||||
"--exact",
|
"--exact",
|
||||||
// TODO: get rid of this case (see: https://github.com/oven-sh/bun/issues/19936)
|
// TODO: get rid of this case (see: https://github.com/oven-sh/bun/issues/19936)
|
||||||
...(proxied() ? ["--no-cache"] : []),
|
...(proxied ? ["--no-cache"] : []),
|
||||||
"--cwd",
|
"--cwd",
|
||||||
Global.Path.cache,
|
Global.Path.cache,
|
||||||
pkg + "@" + version,
|
pkg + "@" + version,
|
||||||
|
|||||||
@@ -546,7 +546,6 @@ export function Session() {
|
|||||||
{
|
{
|
||||||
title: showThinking() ? "Hide thinking" : "Show thinking",
|
title: showThinking() ? "Hide thinking" : "Show thinking",
|
||||||
value: "session.toggle.thinking",
|
value: "session.toggle.thinking",
|
||||||
keybind: "display_thinking",
|
|
||||||
category: "Session",
|
category: "Session",
|
||||||
slash: {
|
slash: {
|
||||||
name: "thinking",
|
name: "thinking",
|
||||||
@@ -1625,7 +1624,6 @@ function BlockTool(props: {
|
|||||||
function Bash(props: ToolProps<typeof BashTool>) {
|
function Bash(props: ToolProps<typeof BashTool>) {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
const isRunning = createMemo(() => props.part.state.status === "running")
|
|
||||||
const output = createMemo(() => stripAnsi(props.metadata.output?.trim() ?? ""))
|
const output = createMemo(() => stripAnsi(props.metadata.output?.trim() ?? ""))
|
||||||
const [expanded, setExpanded] = createSignal(false)
|
const [expanded, setExpanded] = createSignal(false)
|
||||||
const lines = createMemo(() => output().split("\n"))
|
const lines = createMemo(() => output().split("\n"))
|
||||||
@@ -1666,7 +1664,6 @@ function Bash(props: ToolProps<typeof BashTool>) {
|
|||||||
<BlockTool
|
<BlockTool
|
||||||
title={title()}
|
title={title()}
|
||||||
part={props.part}
|
part={props.part}
|
||||||
spinner={isRunning()}
|
|
||||||
onClick={overflow() ? () => setExpanded((prev) => !prev) : undefined}
|
onClick={overflow() ? () => setExpanded((prev) => !prev) : undefined}
|
||||||
>
|
>
|
||||||
<box gap={1}>
|
<box gap={1}>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
import { createMemo, createSignal, For, Show } from "solid-js"
|
import { createMemo, For, Show } from "solid-js"
|
||||||
import { useKeyboard } from "@opentui/solid"
|
import { useKeyboard } from "@opentui/solid"
|
||||||
import type { TextareaRenderable } from "@opentui/core"
|
import type { TextareaRenderable } from "@opentui/core"
|
||||||
import { useKeybind } from "../../context/keybind"
|
import { useKeybind } from "../../context/keybind"
|
||||||
@@ -19,7 +19,6 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
|
|||||||
const questions = createMemo(() => props.request.questions)
|
const questions = createMemo(() => props.request.questions)
|
||||||
const single = createMemo(() => questions().length === 1 && questions()[0]?.multiple !== true)
|
const single = createMemo(() => questions().length === 1 && questions()[0]?.multiple !== true)
|
||||||
const tabs = createMemo(() => (single() ? 1 : questions().length + 1)) // questions + confirm tab (no confirm for single select)
|
const tabs = createMemo(() => (single() ? 1 : questions().length + 1)) // questions + confirm tab (no confirm for single select)
|
||||||
const [tabHover, setTabHover] = createSignal<number | "confirm" | null>(null)
|
|
||||||
const [store, setStore] = createStore({
|
const [store, setStore] = createStore({
|
||||||
tab: 0,
|
tab: 0,
|
||||||
answers: [] as QuestionAnswer[],
|
answers: [] as QuestionAnswer[],
|
||||||
@@ -270,15 +269,7 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
|
|||||||
<box
|
<box
|
||||||
paddingLeft={1}
|
paddingLeft={1}
|
||||||
paddingRight={1}
|
paddingRight={1}
|
||||||
backgroundColor={
|
backgroundColor={isActive() ? theme.accent : theme.backgroundElement}
|
||||||
isActive()
|
|
||||||
? theme.accent
|
|
||||||
: tabHover() === index()
|
|
||||||
? theme.backgroundElement
|
|
||||||
: theme.backgroundPanel
|
|
||||||
}
|
|
||||||
onMouseOver={() => setTabHover(index())}
|
|
||||||
onMouseOut={() => setTabHover(null)}
|
|
||||||
onMouseUp={() => selectTab(index())}
|
onMouseUp={() => selectTab(index())}
|
||||||
>
|
>
|
||||||
<text
|
<text
|
||||||
@@ -299,11 +290,7 @@ export function QuestionPrompt(props: { request: QuestionRequest }) {
|
|||||||
<box
|
<box
|
||||||
paddingLeft={1}
|
paddingLeft={1}
|
||||||
paddingRight={1}
|
paddingRight={1}
|
||||||
backgroundColor={
|
backgroundColor={confirm() ? theme.accent : theme.backgroundElement}
|
||||||
confirm() ? theme.accent : tabHover() === "confirm" ? theme.backgroundElement : theme.backgroundPanel
|
|
||||||
}
|
|
||||||
onMouseOver={() => setTabHover("confirm")}
|
|
||||||
onMouseOut={() => setTabHover(null)}
|
|
||||||
onMouseUp={() => selectTab(questions().length)}
|
onMouseUp={() => selectTab(questions().length)}
|
||||||
>
|
>
|
||||||
<text fg={confirm() ? selectedForeground(theme, theme.accent) : theme.textMuted}>Confirm</text>
|
<text fg={confirm() ? selectedForeground(theme, theme.accent) : theme.textMuted}>Confirm</text>
|
||||||
|
|||||||
@@ -24,14 +24,11 @@ import { LSPServer } from "../lsp/server"
|
|||||||
import { BunProc } from "@/bun"
|
import { BunProc } from "@/bun"
|
||||||
import { Installation } from "@/installation"
|
import { Installation } from "@/installation"
|
||||||
import { ConfigMarkdown } from "./markdown"
|
import { ConfigMarkdown } from "./markdown"
|
||||||
import { constants, existsSync } from "fs"
|
import { existsSync } from "fs"
|
||||||
import { Bus } from "@/bus"
|
import { Bus } from "@/bus"
|
||||||
import { GlobalBus } from "@/bus/global"
|
import { GlobalBus } from "@/bus/global"
|
||||||
import { Event } from "../server/event"
|
import { Event } from "../server/event"
|
||||||
import { PackageRegistry } from "@/bun/registry"
|
import { PackageRegistry } from "@/bun/registry"
|
||||||
import { proxied } from "@/util/proxied"
|
|
||||||
import { iife } from "@/util/iife"
|
|
||||||
import { BUILTIN_PLUGINS } from "@/plugin/builtin"
|
|
||||||
|
|
||||||
export namespace Config {
|
export namespace Config {
|
||||||
const log = Log.create({ service: "config" })
|
const log = Log.create({ service: "config" })
|
||||||
@@ -146,23 +143,7 @@ export namespace Config {
|
|||||||
log.debug("loading config from OPENCODE_CONFIG_DIR", { path: Flag.OPENCODE_CONFIG_DIR })
|
log.debug("loading config from OPENCODE_CONFIG_DIR", { path: Flag.OPENCODE_CONFIG_DIR })
|
||||||
}
|
}
|
||||||
|
|
||||||
const deps = []
|
|
||||||
|
|
||||||
// Collect npm plugins declared in each directory's config so we can
|
|
||||||
// install them into that directory (instead of ~/.cache/opencode/)
|
|
||||||
const dirPlugins = new Map<string, string[]>()
|
|
||||||
|
|
||||||
// Plugins from global/project configs that were loaded before the directory
|
|
||||||
// loop get assigned to the global config directory.
|
|
||||||
// Built-in plugins also get installed in the global config directory.
|
|
||||||
const preloopPlugins = [
|
|
||||||
...npmPlugins(result.plugin ?? []),
|
|
||||||
...(!Flag.OPENCODE_DISABLE_DEFAULT_PLUGINS ? BUILTIN_PLUGINS : []),
|
|
||||||
]
|
|
||||||
|
|
||||||
for (const dir of unique(directories)) {
|
for (const dir of unique(directories)) {
|
||||||
const pluginsBefore = [...(result.plugin ?? [])]
|
|
||||||
|
|
||||||
if (dir.endsWith(".opencode") || dir === Flag.OPENCODE_CONFIG_DIR) {
|
if (dir.endsWith(".opencode") || dir === Flag.OPENCODE_CONFIG_DIR) {
|
||||||
for (const file of ["opencode.jsonc", "opencode.json"]) {
|
for (const file of ["opencode.jsonc", "opencode.json"]) {
|
||||||
log.debug(`loading config from ${path.join(dir, file)}`)
|
log.debug(`loading config from ${path.join(dir, file)}`)
|
||||||
@@ -174,19 +155,10 @@ export namespace Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine which npm plugins this directory's config added
|
const shouldInstall = await needsInstall(dir)
|
||||||
const added = npmPlugins((result.plugin ?? []).filter((p) => !pluginsBefore.includes(p)))
|
if (shouldInstall) {
|
||||||
|
await installDependencies(dir)
|
||||||
// First directory (global config) also gets the pre-loop plugins
|
}
|
||||||
const plugins = dir === Global.Path.config ? [...preloopPlugins, ...added] : added
|
|
||||||
if (plugins.length) dirPlugins.set(dir, plugins)
|
|
||||||
|
|
||||||
deps.push(
|
|
||||||
iife(async () => {
|
|
||||||
const shouldInstall = await needsInstall(dir, dirPlugins.get(dir))
|
|
||||||
if (shouldInstall) await installDependencies(dir, dirPlugins.get(dir))
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
result.command = mergeDeep(result.command ?? {}, await loadCommand(dir))
|
result.command = mergeDeep(result.command ?? {}, await loadCommand(dir))
|
||||||
result.agent = mergeDeep(result.agent, await loadAgent(dir))
|
result.agent = mergeDeep(result.agent, await loadAgent(dir))
|
||||||
@@ -260,84 +232,31 @@ export namespace Config {
|
|||||||
return {
|
return {
|
||||||
config: result,
|
config: result,
|
||||||
directories,
|
directories,
|
||||||
deps,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export async function waitForDependencies() {
|
export async function installDependencies(dir: string) {
|
||||||
const deps = await state().then((x) => x.deps)
|
|
||||||
await Promise.all(deps)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Extract npm plugin specifiers (non-file:// plugins) */
|
|
||||||
function npmPlugins(plugins: string[]): string[] {
|
|
||||||
return plugins.filter((p) => !p.startsWith("file://"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Parse a plugin specifier into package name and version */
|
|
||||||
function parsePlugin(specifier: string): { pkg: string; version: string } {
|
|
||||||
const lastAt = specifier.lastIndexOf("@")
|
|
||||||
if (lastAt > 0) return { pkg: specifier.substring(0, lastAt), version: specifier.substring(lastAt + 1) }
|
|
||||||
return { pkg: specifier, version: "latest" }
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function installDependencies(dir: string, plugins?: string[]) {
|
|
||||||
const pkg = path.join(dir, "package.json")
|
const pkg = path.join(dir, "package.json")
|
||||||
const targetVersion = Installation.isLocal() ? "*" : Installation.VERSION
|
const targetVersion = Installation.isLocal() ? "latest" : Installation.VERSION
|
||||||
|
|
||||||
const json = await Bun.file(pkg)
|
if (!(await Bun.file(pkg).exists())) {
|
||||||
.json()
|
await Bun.write(pkg, "{}")
|
||||||
.catch(() => ({}))
|
|
||||||
json.dependencies = {
|
|
||||||
...json.dependencies,
|
|
||||||
"@opencode-ai/plugin": targetVersion,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add declared npm plugins to this directory's package.json
|
|
||||||
// so they get installed here instead of in ~/.cache/opencode/
|
|
||||||
if (plugins) {
|
|
||||||
for (const specifier of plugins) {
|
|
||||||
const parsed = parsePlugin(specifier)
|
|
||||||
json.dependencies[parsed.pkg] = parsed.version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await Bun.write(pkg, JSON.stringify(json, null, 2))
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 3000))
|
|
||||||
|
|
||||||
const gitignore = path.join(dir, ".gitignore")
|
const gitignore = path.join(dir, ".gitignore")
|
||||||
const hasGitIgnore = await Bun.file(gitignore).exists()
|
const hasGitIgnore = await Bun.file(gitignore).exists()
|
||||||
if (!hasGitIgnore) await Bun.write(gitignore, ["node_modules", "package.json", "bun.lock", ".gitignore"].join("\n"))
|
if (!hasGitIgnore) await Bun.write(gitignore, ["node_modules", "package.json", "bun.lock", ".gitignore"].join("\n"))
|
||||||
|
|
||||||
// Install all dependencies (including npm plugins) in this config directory
|
await BunProc.run(["add", `@opencode-ai/plugin@${targetVersion}`, "--exact"], {
|
||||||
await BunProc.run(
|
cwd: dir,
|
||||||
[
|
}).catch(() => {})
|
||||||
"install",
|
|
||||||
// TODO: get rid of this case (see: https://github.com/oven-sh/bun/issues/19936)
|
// Install any additional dependencies defined in the package.json
|
||||||
...(proxied() ? ["--no-cache"] : []),
|
// This allows local plugins and custom tools to use external packages
|
||||||
],
|
await BunProc.run(["install"], { cwd: dir }).catch(() => {})
|
||||||
{ cwd: dir },
|
|
||||||
).catch(() => {})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function isWritable(dir: string) {
|
async function needsInstall(dir: string) {
|
||||||
try {
|
|
||||||
await fs.access(dir, constants.W_OK)
|
|
||||||
return true
|
|
||||||
} catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function needsInstall(dir: string, plugins?: string[]) {
|
|
||||||
// Some config dirs may be read-only.
|
|
||||||
// Installing deps there will fail; skip installation in that case.
|
|
||||||
const writable = await isWritable(dir)
|
|
||||||
if (!writable) {
|
|
||||||
log.debug("config dir is not writable, skipping dependency install", { dir })
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodeModules = path.join(dir, "node_modules")
|
const nodeModules = path.join(dir, "node_modules")
|
||||||
if (!existsSync(nodeModules)) return true
|
if (!existsSync(nodeModules)) return true
|
||||||
|
|
||||||
@@ -354,27 +273,15 @@ export namespace Config {
|
|||||||
const targetVersion = Installation.isLocal() ? "latest" : Installation.VERSION
|
const targetVersion = Installation.isLocal() ? "latest" : Installation.VERSION
|
||||||
if (targetVersion === "latest") {
|
if (targetVersion === "latest") {
|
||||||
const isOutdated = await PackageRegistry.isOutdated("@opencode-ai/plugin", depVersion, dir)
|
const isOutdated = await PackageRegistry.isOutdated("@opencode-ai/plugin", depVersion, dir)
|
||||||
if (isOutdated) {
|
if (!isOutdated) return false
|
||||||
log.info("Cached version is outdated, proceeding with install", {
|
log.info("Cached version is outdated, proceeding with install", {
|
||||||
pkg: "@opencode-ai/plugin",
|
pkg: "@opencode-ai/plugin",
|
||||||
cachedVersion: depVersion,
|
cachedVersion: depVersion,
|
||||||
})
|
})
|
||||||
return true
|
|
||||||
}
|
|
||||||
} else if (depVersion !== targetVersion) {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if (depVersion === targetVersion) return false
|
||||||
// Check if any declared plugins are missing from the installed dependencies
|
return true
|
||||||
if (plugins) {
|
|
||||||
for (const specifier of plugins) {
|
|
||||||
const parsed = parsePlugin(specifier)
|
|
||||||
if (!dependencies[parsed.pkg]) return true
|
|
||||||
if (!existsSync(path.join(nodeModules, ...parsed.pkg.split("/")))) return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function rel(item: string, patterns: string[]) {
|
function rel(item: string, patterns: string[]) {
|
||||||
@@ -958,7 +865,6 @@ export namespace Config {
|
|||||||
terminal_suspend: z.string().optional().default("ctrl+z").describe("Suspend terminal"),
|
terminal_suspend: z.string().optional().default("ctrl+z").describe("Suspend terminal"),
|
||||||
terminal_title_toggle: z.string().optional().default("none").describe("Toggle terminal title"),
|
terminal_title_toggle: z.string().optional().default("none").describe("Toggle terminal title"),
|
||||||
tips_toggle: z.string().optional().default("<leader>h").describe("Toggle tips on home screen"),
|
tips_toggle: z.string().optional().default("<leader>h").describe("Toggle tips on home screen"),
|
||||||
display_thinking: z.string().optional().default("none").describe("Toggle thinking blocks visibility"),
|
|
||||||
})
|
})
|
||||||
.strict()
|
.strict()
|
||||||
.meta({
|
.meta({
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
/** Built-in npm plugins that are installed by default (unless OPENCODE_DISABLE_DEFAULT_PLUGINS is set) */
|
|
||||||
export const BUILTIN_PLUGINS = ["opencode-anthropic-auth@0.0.13"]
|
|
||||||
@@ -361,7 +361,6 @@ export async function CodexAuthPlugin(input: PluginInput): Promise<Hooks> {
|
|||||||
"gpt-5.1-codex-mini",
|
"gpt-5.1-codex-mini",
|
||||||
"gpt-5.2",
|
"gpt-5.2",
|
||||||
"gpt-5.2-codex",
|
"gpt-5.2-codex",
|
||||||
"gpt-5.3-codex",
|
|
||||||
"gpt-5.1-codex",
|
"gpt-5.1-codex",
|
||||||
])
|
])
|
||||||
for (const modelId of Object.keys(provider.models)) {
|
for (const modelId of Object.keys(provider.models)) {
|
||||||
|
|||||||
@@ -301,20 +301,17 @@ export async function CopilotAuthPlugin(input: PluginInput): Promise<Hooks> {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"chat.headers": async (incoming, output) => {
|
"chat.headers": async (input, output) => {
|
||||||
if (!incoming.model.providerID.includes("github-copilot")) return
|
if (!input.model.providerID.includes("github-copilot")) return
|
||||||
|
|
||||||
if (incoming.model.api.npm === "@ai-sdk/anthropic") {
|
if (input.model.api.npm === "@ai-sdk/anthropic") {
|
||||||
output.headers["anthropic-beta"] = "interleaved-thinking-2025-05-14"
|
output.headers["anthropic-beta"] = "interleaved-thinking-2025-05-14"
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await sdk.session
|
const session = await sdk.session
|
||||||
.get({
|
.get({
|
||||||
path: {
|
path: {
|
||||||
id: incoming.sessionID,
|
id: input.sessionID,
|
||||||
},
|
|
||||||
query: {
|
|
||||||
directory: input.directory,
|
|
||||||
},
|
},
|
||||||
throwOnError: true,
|
throwOnError: true,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,32 +4,21 @@ import { Bus } from "../bus"
|
|||||||
import { Log } from "../util/log"
|
import { Log } from "../util/log"
|
||||||
import { createOpencodeClient } from "@opencode-ai/sdk"
|
import { createOpencodeClient } from "@opencode-ai/sdk"
|
||||||
import { Server } from "../server/server"
|
import { Server } from "../server/server"
|
||||||
|
import { BunProc } from "../bun"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
import { Flag } from "../flag/flag"
|
import { Flag } from "../flag/flag"
|
||||||
import { CodexAuthPlugin } from "./codex"
|
import { CodexAuthPlugin } from "./codex"
|
||||||
import { Session } from "../session"
|
import { Session } from "../session"
|
||||||
import { NamedError } from "@opencode-ai/util/error"
|
import { NamedError } from "@opencode-ai/util/error"
|
||||||
import { CopilotAuthPlugin } from "./copilot"
|
import { CopilotAuthPlugin } from "./copilot"
|
||||||
import { gitlabAuthPlugin as GitlabAuthPlugin } from "@gitlab/opencode-gitlab-auth"
|
|
||||||
import path from "path"
|
|
||||||
import { existsSync } from "fs"
|
|
||||||
import { BUILTIN_PLUGINS } from "./builtin"
|
|
||||||
|
|
||||||
export namespace Plugin {
|
export namespace Plugin {
|
||||||
const log = Log.create({ service: "plugin" })
|
const log = Log.create({ service: "plugin" })
|
||||||
|
|
||||||
export const BUILTIN = BUILTIN_PLUGINS
|
const BUILTIN = ["opencode-anthropic-auth@0.0.13", "@gitlab/opencode-gitlab-auth@1.3.2"]
|
||||||
|
|
||||||
// Built-in plugins that are directly imported (not installed from npm)
|
// Built-in plugins that are directly imported (not installed from npm)
|
||||||
const INTERNAL_PLUGINS: PluginInstance[] = [CodexAuthPlugin, CopilotAuthPlugin, GitlabAuthPlugin]
|
const INTERNAL_PLUGINS: PluginInstance[] = [CodexAuthPlugin, CopilotAuthPlugin]
|
||||||
|
|
||||||
/** Resolve an npm plugin from the config directories' node_modules */
|
|
||||||
function resolve(pkg: string, directories: string[]): string | undefined {
|
|
||||||
for (const dir of directories) {
|
|
||||||
const mod = path.join(dir, "node_modules", ...pkg.split("/"))
|
|
||||||
if (existsSync(mod)) return mod
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = Instance.state(async () => {
|
const state = Instance.state(async () => {
|
||||||
const client = createOpencodeClient({
|
const client = createOpencodeClient({
|
||||||
@@ -55,15 +44,10 @@ export namespace Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const plugins = [...(config.plugin ?? [])]
|
const plugins = [...(config.plugin ?? [])]
|
||||||
if (plugins.length) await Config.waitForDependencies()
|
|
||||||
if (!Flag.OPENCODE_DISABLE_DEFAULT_PLUGINS) {
|
if (!Flag.OPENCODE_DISABLE_DEFAULT_PLUGINS) {
|
||||||
plugins.push(...BUILTIN)
|
plugins.push(...BUILTIN)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for dependencies so npm plugins are installed in their config directories
|
|
||||||
await Config.waitForDependencies()
|
|
||||||
const directories = await Config.directories()
|
|
||||||
|
|
||||||
for (let plugin of plugins) {
|
for (let plugin of plugins) {
|
||||||
// ignore old codex plugin since it is supported first party now
|
// ignore old codex plugin since it is supported first party now
|
||||||
if (plugin.includes("opencode-openai-codex-auth") || plugin.includes("opencode-copilot-auth")) continue
|
if (plugin.includes("opencode-openai-codex-auth") || plugin.includes("opencode-copilot-auth")) continue
|
||||||
@@ -71,24 +55,26 @@ export namespace Plugin {
|
|||||||
if (!plugin.startsWith("file://")) {
|
if (!plugin.startsWith("file://")) {
|
||||||
const lastAtIndex = plugin.lastIndexOf("@")
|
const lastAtIndex = plugin.lastIndexOf("@")
|
||||||
const pkg = lastAtIndex > 0 ? plugin.substring(0, lastAtIndex) : plugin
|
const pkg = lastAtIndex > 0 ? plugin.substring(0, lastAtIndex) : plugin
|
||||||
|
const version = lastAtIndex > 0 ? plugin.substring(lastAtIndex + 1) : "latest"
|
||||||
const builtin = BUILTIN.some((x) => x.startsWith(pkg + "@"))
|
const builtin = BUILTIN.some((x) => x.startsWith(pkg + "@"))
|
||||||
|
plugin = await BunProc.install(pkg, version).catch((err) => {
|
||||||
|
if (!builtin) throw err
|
||||||
|
|
||||||
// Resolve the plugin from config directories' node_modules
|
const message = err instanceof Error ? err.message : String(err)
|
||||||
const resolved = resolve(pkg, directories)
|
log.error("failed to install builtin plugin", {
|
||||||
if (!resolved) {
|
pkg,
|
||||||
const message = `Plugin ${plugin} not found in any config directory`
|
version,
|
||||||
if (!builtin) {
|
error: message,
|
||||||
log.error("plugin not found", { plugin, directories })
|
|
||||||
throw new Error(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.error("failed to resolve builtin plugin", { plugin })
|
|
||||||
Bus.publish(Session.Event.Error, {
|
|
||||||
error: new NamedError.Unknown({ message }).toObject(),
|
|
||||||
})
|
})
|
||||||
continue
|
Bus.publish(Session.Event.Error, {
|
||||||
}
|
error: new NamedError.Unknown({
|
||||||
plugin = resolved
|
message: `Failed to install built-in plugin ${pkg}@${version}: ${message}`,
|
||||||
|
}).toObject(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return ""
|
||||||
|
})
|
||||||
|
if (!plugin) continue
|
||||||
}
|
}
|
||||||
const mod = await import(plugin)
|
const mod = await import(plugin)
|
||||||
// Prevent duplicate initialization when plugins export the same function
|
// Prevent duplicate initialization when plugins export the same function
|
||||||
|
|||||||
@@ -457,29 +457,6 @@ export namespace Provider {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cloudflare-workers-ai": async (input) => {
|
|
||||||
const accountId = Env.get("CLOUDFLARE_ACCOUNT_ID")
|
|
||||||
if (!accountId) return { autoload: false }
|
|
||||||
|
|
||||||
const apiKey = await iife(async () => {
|
|
||||||
const envToken = Env.get("CLOUDFLARE_API_KEY")
|
|
||||||
if (envToken) return envToken
|
|
||||||
const auth = await Auth.get(input.id)
|
|
||||||
if (auth?.type === "api") return auth.key
|
|
||||||
return undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
autoload: !!apiKey,
|
|
||||||
options: {
|
|
||||||
apiKey,
|
|
||||||
baseURL: `https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/v1`,
|
|
||||||
},
|
|
||||||
async getModel(sdk: any, modelID: string) {
|
|
||||||
return sdk.languageModel(modelID)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cloudflare-ai-gateway": async (input) => {
|
"cloudflare-ai-gateway": async (input) => {
|
||||||
const accountId = Env.get("CLOUDFLARE_ACCOUNT_ID")
|
const accountId = Env.get("CLOUDFLARE_ACCOUNT_ID")
|
||||||
const gateway = Env.get("CLOUDFLARE_GATEWAY_ID")
|
const gateway = Env.get("CLOUDFLARE_GATEWAY_ID")
|
||||||
|
|||||||
@@ -375,8 +375,7 @@ export namespace ProviderTransform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const copilotEfforts = iife(() => {
|
const copilotEfforts = iife(() => {
|
||||||
if (id.includes("5.1-codex-max") || id.includes("5.2") || id.includes("5.3"))
|
if (id.includes("5.1-codex-max") || id.includes("5.2")) return [...WIDELY_SUPPORTED_EFFORTS, "xhigh"]
|
||||||
return [...WIDELY_SUPPORTED_EFFORTS, "xhigh"]
|
|
||||||
return WIDELY_SUPPORTED_EFFORTS
|
return WIDELY_SUPPORTED_EFFORTS
|
||||||
})
|
})
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
@@ -423,7 +422,7 @@ export namespace ProviderTransform {
|
|||||||
if (id === "gpt-5-pro") return {}
|
if (id === "gpt-5-pro") return {}
|
||||||
const openaiEfforts = iife(() => {
|
const openaiEfforts = iife(() => {
|
||||||
if (id.includes("codex")) {
|
if (id.includes("codex")) {
|
||||||
if (id.includes("5.2") || id.includes("5.3")) return [...WIDELY_SUPPORTED_EFFORTS, "xhigh"]
|
if (id.includes("5.2")) return [...WIDELY_SUPPORTED_EFFORTS, "xhigh"]
|
||||||
return WIDELY_SUPPORTED_EFFORTS
|
return WIDELY_SUPPORTED_EFFORTS
|
||||||
}
|
}
|
||||||
const arr = [...WIDELY_SUPPORTED_EFFORTS]
|
const arr = [...WIDELY_SUPPORTED_EFFORTS]
|
||||||
@@ -631,18 +630,6 @@ export namespace ProviderTransform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable thinking by default for kimi-k2.5/k2p5 models using anthropic SDK
|
|
||||||
const modelId = input.model.api.id.toLowerCase()
|
|
||||||
if (
|
|
||||||
(input.model.api.npm === "@ai-sdk/anthropic" || input.model.api.npm === "@ai-sdk/google-vertex/anthropic") &&
|
|
||||||
(modelId.includes("k2p5") || modelId.includes("kimi-k2.5") || modelId.includes("kimi-k2p5"))
|
|
||||||
) {
|
|
||||||
result["thinking"] = {
|
|
||||||
type: "enabled",
|
|
||||||
budgetTokens: Math.min(16_000, Math.floor(input.model.limit.output / 2 - 1)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.model.api.id.includes("gpt-5") && !input.model.api.id.includes("gpt-5-chat")) {
|
if (input.model.api.id.includes("gpt-5") && !input.model.api.id.includes("gpt-5-chat")) {
|
||||||
if (!input.model.api.id.includes("gpt-5-pro")) {
|
if (!input.model.api.id.includes("gpt-5-pro")) {
|
||||||
result["reasoningEffort"] = "medium"
|
result["reasoningEffort"] = "medium"
|
||||||
|
|||||||
@@ -438,26 +438,6 @@ export namespace MessageV2 {
|
|||||||
export function toModelMessages(input: WithParts[], model: Provider.Model): ModelMessage[] {
|
export function toModelMessages(input: WithParts[], model: Provider.Model): ModelMessage[] {
|
||||||
const result: UIMessage[] = []
|
const result: UIMessage[] = []
|
||||||
const toolNames = new Set<string>()
|
const toolNames = new Set<string>()
|
||||||
// Track media from tool results that need to be injected as user messages
|
|
||||||
// for providers that don't support media in tool results.
|
|
||||||
//
|
|
||||||
// OpenAI-compatible APIs only support string content in tool results, so we need
|
|
||||||
// to extract media and inject as user messages. Other SDKs (anthropic, google,
|
|
||||||
// bedrock) handle type: "content" with media parts natively.
|
|
||||||
//
|
|
||||||
// Only apply this workaround if the model actually supports image input -
|
|
||||||
// otherwise there's no point extracting images.
|
|
||||||
const supportsMediaInToolResults = (() => {
|
|
||||||
if (model.api.npm === "@ai-sdk/anthropic") return true
|
|
||||||
if (model.api.npm === "@ai-sdk/openai") return true
|
|
||||||
if (model.api.npm === "@ai-sdk/amazon-bedrock") return true
|
|
||||||
if (model.api.npm === "@ai-sdk/google-vertex/anthropic") return true
|
|
||||||
if (model.api.npm === "@ai-sdk/google") {
|
|
||||||
const id = model.api.id.toLowerCase()
|
|
||||||
return id.includes("gemini-3") && !id.includes("gemini-2")
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})()
|
|
||||||
|
|
||||||
const toModelOutput = (output: unknown) => {
|
const toModelOutput = (output: unknown) => {
|
||||||
if (typeof output === "string") {
|
if (typeof output === "string") {
|
||||||
@@ -534,7 +514,6 @@ export namespace MessageV2 {
|
|||||||
|
|
||||||
if (msg.info.role === "assistant") {
|
if (msg.info.role === "assistant") {
|
||||||
const differentModel = `${model.providerID}/${model.id}` !== `${msg.info.providerID}/${msg.info.modelID}`
|
const differentModel = `${model.providerID}/${model.id}` !== `${msg.info.providerID}/${msg.info.modelID}`
|
||||||
const media: Array<{ mime: string; url: string }> = []
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
msg.info.error &&
|
msg.info.error &&
|
||||||
@@ -566,23 +545,11 @@ export namespace MessageV2 {
|
|||||||
if (part.state.status === "completed") {
|
if (part.state.status === "completed") {
|
||||||
const outputText = part.state.time.compacted ? "[Old tool result content cleared]" : part.state.output
|
const outputText = part.state.time.compacted ? "[Old tool result content cleared]" : part.state.output
|
||||||
const attachments = part.state.time.compacted ? [] : (part.state.attachments ?? [])
|
const attachments = part.state.time.compacted ? [] : (part.state.attachments ?? [])
|
||||||
|
|
||||||
// For providers that don't support media in tool results, extract media files
|
|
||||||
// (images, PDFs) to be sent as a separate user message
|
|
||||||
const isMediaAttachment = (a: { mime: string }) =>
|
|
||||||
a.mime.startsWith("image/") || a.mime === "application/pdf"
|
|
||||||
const mediaAttachments = attachments.filter(isMediaAttachment)
|
|
||||||
const nonMediaAttachments = attachments.filter((a) => !isMediaAttachment(a))
|
|
||||||
if (!supportsMediaInToolResults && mediaAttachments.length > 0) {
|
|
||||||
media.push(...mediaAttachments)
|
|
||||||
}
|
|
||||||
const finalAttachments = supportsMediaInToolResults ? attachments : nonMediaAttachments
|
|
||||||
|
|
||||||
const output =
|
const output =
|
||||||
finalAttachments.length > 0
|
attachments.length > 0
|
||||||
? {
|
? {
|
||||||
text: outputText,
|
text: outputText,
|
||||||
attachments: finalAttachments,
|
attachments,
|
||||||
}
|
}
|
||||||
: outputText
|
: outputText
|
||||||
|
|
||||||
@@ -626,25 +593,6 @@ export namespace MessageV2 {
|
|||||||
}
|
}
|
||||||
if (assistantMessage.parts.length > 0) {
|
if (assistantMessage.parts.length > 0) {
|
||||||
result.push(assistantMessage)
|
result.push(assistantMessage)
|
||||||
// Inject pending media as a user message for providers that don't support
|
|
||||||
// media (images, PDFs) in tool results
|
|
||||||
if (media.length > 0) {
|
|
||||||
result.push({
|
|
||||||
id: Identifier.ascending("message"),
|
|
||||||
role: "user",
|
|
||||||
parts: [
|
|
||||||
{
|
|
||||||
type: "text" as const,
|
|
||||||
text: "Attached image(s) from tool result:",
|
|
||||||
},
|
|
||||||
...media.map((attachment) => ({
|
|
||||||
type: "file" as const,
|
|
||||||
url: attachment.url,
|
|
||||||
mediaType: attachment.mime,
|
|
||||||
})),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -968,11 +968,9 @@ export namespace SessionPrompt {
|
|||||||
// have to normalize, symbol search returns absolute paths
|
// have to normalize, symbol search returns absolute paths
|
||||||
// Decode the pathname since URL constructor doesn't automatically decode it
|
// Decode the pathname since URL constructor doesn't automatically decode it
|
||||||
const filepath = fileURLToPath(part.url)
|
const filepath = fileURLToPath(part.url)
|
||||||
const stat = await Bun.file(filepath)
|
const stat = await Bun.file(filepath).stat()
|
||||||
.stat()
|
|
||||||
.catch(() => undefined)
|
|
||||||
|
|
||||||
if (stat?.isDirectory()) {
|
if (stat.isDirectory()) {
|
||||||
part.mime = "application/x-directory"
|
part.mime = "application/x-directory"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -991,7 +989,7 @@ export namespace SessionPrompt {
|
|||||||
// workspace/symbol searches, so we'll try to find the
|
// workspace/symbol searches, so we'll try to find the
|
||||||
// symbol in the document to get the full range
|
// symbol in the document to get the full range
|
||||||
if (start === end) {
|
if (start === end) {
|
||||||
const symbols = await LSP.documentSymbol(filePathURI).catch(() => [])
|
const symbols = await LSP.documentSymbol(filePathURI)
|
||||||
for (const symbol of symbols) {
|
for (const symbol of symbols) {
|
||||||
let range: LSP.Range | undefined
|
let range: LSP.Range | undefined
|
||||||
if ("range" in symbol) {
|
if ("range" in symbol) {
|
||||||
|
|||||||
@@ -35,15 +35,18 @@ export namespace ToolRegistry {
|
|||||||
const custom = [] as Tool.Info[]
|
const custom = [] as Tool.Info[]
|
||||||
const glob = new Bun.Glob("{tool,tools}/*.{js,ts}")
|
const glob = new Bun.Glob("{tool,tools}/*.{js,ts}")
|
||||||
|
|
||||||
const matches = await Config.directories().then((dirs) =>
|
for (const dir of await Config.directories()) {
|
||||||
dirs.flatMap((dir) => [...glob.scanSync({ cwd: dir, absolute: true, followSymlinks: true, dot: true })]),
|
for await (const match of glob.scan({
|
||||||
)
|
cwd: dir,
|
||||||
if (matches.length) await Config.waitForDependencies()
|
absolute: true,
|
||||||
for (const match of matches) {
|
followSymlinks: true,
|
||||||
const namespace = path.basename(match, path.extname(match))
|
dot: true,
|
||||||
const mod = await import(match)
|
})) {
|
||||||
for (const [id, def] of Object.entries<ToolDefinition>(mod)) {
|
const namespace = path.basename(match, path.extname(match))
|
||||||
custom.push(fromPlugin(id === "default" ? namespace : `${namespace}_${id}`, def))
|
const mod = await import(match)
|
||||||
|
for (const [id, def] of Object.entries<ToolDefinition>(mod)) {
|
||||||
|
custom.push(fromPlugin(id === "default" ? namespace : `${namespace}_${id}`, def))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,7 @@ const parameters = z.object({
|
|||||||
description: z.string().describe("A short (3-5 words) description of the task"),
|
description: z.string().describe("A short (3-5 words) description of the task"),
|
||||||
prompt: z.string().describe("The task for the agent to perform"),
|
prompt: z.string().describe("The task for the agent to perform"),
|
||||||
subagent_type: z.string().describe("The type of specialized agent to use for this task"),
|
subagent_type: z.string().describe("The type of specialized agent to use for this task"),
|
||||||
task_id: z
|
session_id: z.string().describe("Existing Task session to continue").optional(),
|
||||||
.string()
|
|
||||||
.describe(
|
|
||||||
"This should only be set if you mean to resume a previous task (you can pass a prior task_id and the task will continue the same subagent session as before instead of creating a fresh one)",
|
|
||||||
)
|
|
||||||
.optional(),
|
|
||||||
command: z.string().describe("The command that triggered this task").optional(),
|
command: z.string().describe("The command that triggered this task").optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -65,8 +60,8 @@ export const TaskTool = Tool.define("task", async (ctx) => {
|
|||||||
const hasTaskPermission = agent.permission.some((rule) => rule.permission === "task")
|
const hasTaskPermission = agent.permission.some((rule) => rule.permission === "task")
|
||||||
|
|
||||||
const session = await iife(async () => {
|
const session = await iife(async () => {
|
||||||
if (params.task_id) {
|
if (params.session_id) {
|
||||||
const found = await Session.get(params.task_id).catch(() => {})
|
const found = await Session.get(params.session_id).catch(() => {})
|
||||||
if (found) return found
|
if (found) return found
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,13 +176,7 @@ export const TaskTool = Tool.define("task", async (ctx) => {
|
|||||||
}))
|
}))
|
||||||
const text = result.parts.findLast((x) => x.type === "text")?.text ?? ""
|
const text = result.parts.findLast((x) => x.type === "text")?.text ?? ""
|
||||||
|
|
||||||
const output = [
|
const output = text + "\n\n" + ["<task_metadata>", `session_id: ${session.id}`, "</task_metadata>"].join("\n")
|
||||||
`task_id: ${session.id} (for resuming to continue this task if needed)`,
|
|
||||||
"",
|
|
||||||
"<task_result>",
|
|
||||||
text,
|
|
||||||
"</task_result>",
|
|
||||||
].join("\n")
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: params.description,
|
title: params.description,
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ When NOT to use the Task tool:
|
|||||||
|
|
||||||
Usage notes:
|
Usage notes:
|
||||||
1. Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses
|
1. Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses
|
||||||
2. When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result. The output includes a task_id you can reuse later to continue the same subagent session.
|
2. When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.
|
||||||
3. Each agent invocation starts with a fresh context unless you provide task_id to resume the same subagent session (which continues with its previous messages and tool outputs). When starting fresh, your prompt should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final and only message to you.
|
3. Each agent invocation is stateless unless you provide a session_id. Your prompt should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final and only message to you.
|
||||||
4. The agent's outputs should generally be trusted
|
4. The agent's outputs should generally be trusted
|
||||||
5. Clearly tell the agent whether you expect it to write code or just to do research (search, file reads, web fetches, etc.), since it is not aware of the user's intent. Tell it how to verify its work if possible (e.g., relevant test commands).
|
5. Clearly tell the agent whether you expect it to write code or just to do research (search, file reads, web fetches, etc.), since it is not aware of the user's intent
|
||||||
6. If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.
|
6. If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.
|
||||||
|
|
||||||
Example usage (NOTE: The agents below are fictional examples for illustration only - use the actual agents listed above):
|
Example usage (NOTE: The agents below are fictional examples for illustration only - use the actual agents listed above):
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
export function proxied() {
|
|
||||||
return !!(process.env.HTTP_PROXY || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.https_proxy)
|
|
||||||
}
|
|
||||||
@@ -566,68 +566,6 @@ test("gets config directories", async () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test("does not try to install dependencies in read-only OPENCODE_CONFIG_DIR", async () => {
|
|
||||||
if (process.platform === "win32") return
|
|
||||||
|
|
||||||
await using tmp = await tmpdir<string>({
|
|
||||||
init: async (dir) => {
|
|
||||||
const ro = path.join(dir, "readonly")
|
|
||||||
await fs.mkdir(ro, { recursive: true })
|
|
||||||
await fs.chmod(ro, 0o555)
|
|
||||||
return ro
|
|
||||||
},
|
|
||||||
dispose: async (dir) => {
|
|
||||||
const ro = path.join(dir, "readonly")
|
|
||||||
await fs.chmod(ro, 0o755).catch(() => {})
|
|
||||||
return ro
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const prev = process.env.OPENCODE_CONFIG_DIR
|
|
||||||
process.env.OPENCODE_CONFIG_DIR = tmp.extra
|
|
||||||
|
|
||||||
try {
|
|
||||||
await Instance.provide({
|
|
||||||
directory: tmp.path,
|
|
||||||
fn: async () => {
|
|
||||||
await Config.get()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
if (prev === undefined) delete process.env.OPENCODE_CONFIG_DIR
|
|
||||||
else process.env.OPENCODE_CONFIG_DIR = prev
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
test("installs dependencies in writable OPENCODE_CONFIG_DIR", async () => {
|
|
||||||
await using tmp = await tmpdir<string>({
|
|
||||||
init: async (dir) => {
|
|
||||||
const cfg = path.join(dir, "configdir")
|
|
||||||
await fs.mkdir(cfg, { recursive: true })
|
|
||||||
return cfg
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const prev = process.env.OPENCODE_CONFIG_DIR
|
|
||||||
process.env.OPENCODE_CONFIG_DIR = tmp.extra
|
|
||||||
|
|
||||||
try {
|
|
||||||
await Instance.provide({
|
|
||||||
directory: tmp.path,
|
|
||||||
fn: async () => {
|
|
||||||
await Config.get()
|
|
||||||
await Config.waitForDependencies()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(await Bun.file(path.join(tmp.extra, "package.json")).exists()).toBe(true)
|
|
||||||
expect(await Bun.file(path.join(tmp.extra, ".gitignore")).exists()).toBe(true)
|
|
||||||
} finally {
|
|
||||||
if (prev === undefined) delete process.env.OPENCODE_CONFIG_DIR
|
|
||||||
else process.env.OPENCODE_CONFIG_DIR = prev
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
test("resolves scoped npm plugins in config", async () => {
|
test("resolves scoped npm plugins in config", async () => {
|
||||||
await using tmp = await tmpdir({
|
await using tmp = await tmpdir({
|
||||||
init: async (dir) => {
|
init: async (dir) => {
|
||||||
|
|||||||
@@ -1,12 +1,46 @@
|
|||||||
import { test, expect, describe } from "bun:test"
|
import { test, expect, mock, describe } from "bun:test"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { unlink } from "fs/promises"
|
import { unlink } from "fs/promises"
|
||||||
|
|
||||||
import { tmpdir } from "../fixture/fixture"
|
// === Mocks ===
|
||||||
import { Instance } from "../../src/project/instance"
|
// These mocks are required because Provider.list() triggers:
|
||||||
import { Provider } from "../../src/provider/provider"
|
// 1. BunProc.install("@aws-sdk/credential-providers") - in bedrock custom loader
|
||||||
import { Env } from "../../src/env"
|
// 2. Plugin.list() which calls BunProc.install() for default plugins
|
||||||
import { Global } from "../../src/global"
|
// Without mocks, these would attempt real package installations that timeout in tests.
|
||||||
|
|
||||||
|
mock.module("../../src/bun/index", () => ({
|
||||||
|
BunProc: {
|
||||||
|
install: async (pkg: string, _version?: string) => {
|
||||||
|
// Return package name without version for mocking
|
||||||
|
const lastAtIndex = pkg.lastIndexOf("@")
|
||||||
|
return lastAtIndex > 0 ? pkg.substring(0, lastAtIndex) : pkg
|
||||||
|
},
|
||||||
|
run: async () => {
|
||||||
|
throw new Error("BunProc.run should not be called in tests")
|
||||||
|
},
|
||||||
|
which: () => process.execPath,
|
||||||
|
InstallFailedError: class extends Error {},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
mock.module("@aws-sdk/credential-providers", () => ({
|
||||||
|
fromNodeProviderChain: () => async () => ({
|
||||||
|
accessKeyId: "mock-access-key-id",
|
||||||
|
secretAccessKey: "mock-secret-access-key",
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const mockPlugin = () => ({})
|
||||||
|
mock.module("opencode-copilot-auth", () => ({ default: mockPlugin }))
|
||||||
|
mock.module("opencode-anthropic-auth", () => ({ default: mockPlugin }))
|
||||||
|
mock.module("@gitlab/opencode-gitlab-auth", () => ({ default: mockPlugin }))
|
||||||
|
|
||||||
|
// Import after mocks are set up
|
||||||
|
const { tmpdir } = await import("../fixture/fixture")
|
||||||
|
const { Instance } = await import("../../src/project/instance")
|
||||||
|
const { Provider } = await import("../../src/provider/provider")
|
||||||
|
const { Env } = await import("../../src/env")
|
||||||
|
const { Global } = await import("../../src/global")
|
||||||
|
|
||||||
test("Bedrock: config region takes precedence over AWS_REGION env var", async () => {
|
test("Bedrock: config region takes precedence over AWS_REGION env var", async () => {
|
||||||
await using tmp = await tmpdir({
|
await using tmp = await tmpdir({
|
||||||
|
|||||||
@@ -1,11 +1,35 @@
|
|||||||
import { test, expect } from "bun:test"
|
import { test, expect, mock } from "bun:test"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
|
||||||
import { tmpdir } from "../fixture/fixture"
|
// === Mocks ===
|
||||||
import { Instance } from "../../src/project/instance"
|
// These mocks prevent real package installations during tests
|
||||||
import { Provider } from "../../src/provider/provider"
|
|
||||||
import { Env } from "../../src/env"
|
mock.module("../../src/bun/index", () => ({
|
||||||
import { Global } from "../../src/global"
|
BunProc: {
|
||||||
|
install: async (pkg: string, _version?: string) => {
|
||||||
|
// Return package name without version for mocking
|
||||||
|
const lastAtIndex = pkg.lastIndexOf("@")
|
||||||
|
return lastAtIndex > 0 ? pkg.substring(0, lastAtIndex) : pkg
|
||||||
|
},
|
||||||
|
run: async () => {
|
||||||
|
throw new Error("BunProc.run should not be called in tests")
|
||||||
|
},
|
||||||
|
which: () => process.execPath,
|
||||||
|
InstallFailedError: class extends Error {},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const mockPlugin = () => ({})
|
||||||
|
mock.module("opencode-copilot-auth", () => ({ default: mockPlugin }))
|
||||||
|
mock.module("opencode-anthropic-auth", () => ({ default: mockPlugin }))
|
||||||
|
mock.module("@gitlab/opencode-gitlab-auth", () => ({ default: mockPlugin }))
|
||||||
|
|
||||||
|
// Import after mocks are set up
|
||||||
|
const { tmpdir } = await import("../fixture/fixture")
|
||||||
|
const { Instance } = await import("../../src/project/instance")
|
||||||
|
const { Provider } = await import("../../src/provider/provider")
|
||||||
|
const { Env } = await import("../../src/env")
|
||||||
|
const { Global } = await import("../../src/global")
|
||||||
|
|
||||||
test("GitLab Duo: loads provider with API key from environment", async () => {
|
test("GitLab Duo: loads provider with API key from environment", async () => {
|
||||||
await using tmp = await tmpdir({
|
await using tmp = await tmpdir({
|
||||||
|
|||||||
@@ -1,6 +1,27 @@
|
|||||||
import { test, expect } from "bun:test"
|
import { test, expect, mock } from "bun:test"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
|
||||||
|
// Mock BunProc and default plugins to prevent actual installations during tests
|
||||||
|
mock.module("../../src/bun/index", () => ({
|
||||||
|
BunProc: {
|
||||||
|
install: async (pkg: string, _version?: string) => {
|
||||||
|
// Return package name without version for mocking
|
||||||
|
const lastAtIndex = pkg.lastIndexOf("@")
|
||||||
|
return lastAtIndex > 0 ? pkg.substring(0, lastAtIndex) : pkg
|
||||||
|
},
|
||||||
|
run: async () => {
|
||||||
|
throw new Error("BunProc.run should not be called in tests")
|
||||||
|
},
|
||||||
|
which: () => process.execPath,
|
||||||
|
InstallFailedError: class extends Error {},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const mockPlugin = () => ({})
|
||||||
|
mock.module("opencode-copilot-auth", () => ({ default: mockPlugin }))
|
||||||
|
mock.module("opencode-anthropic-auth", () => ({ default: mockPlugin }))
|
||||||
|
mock.module("@gitlab/opencode-gitlab-auth", () => ({ default: mockPlugin }))
|
||||||
|
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { Provider } from "../../src/provider/provider"
|
import { Provider } from "../../src/provider/provider"
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
import path from "path"
|
|
||||||
import { describe, expect, test } from "bun:test"
|
|
||||||
import { Instance } from "../../src/project/instance"
|
|
||||||
import { Session } from "../../src/session"
|
|
||||||
import { SessionPrompt } from "../../src/session/prompt"
|
|
||||||
import { tmpdir } from "../fixture/fixture"
|
|
||||||
|
|
||||||
describe("session.prompt missing file", () => {
|
|
||||||
test("does not fail the prompt when a file part is missing", async () => {
|
|
||||||
await using tmp = await tmpdir({
|
|
||||||
git: true,
|
|
||||||
config: {
|
|
||||||
agent: {
|
|
||||||
build: {
|
|
||||||
model: "openai/gpt-5.2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
await Instance.provide({
|
|
||||||
directory: tmp.path,
|
|
||||||
fn: async () => {
|
|
||||||
const session = await Session.create({})
|
|
||||||
|
|
||||||
const missing = path.join(tmp.path, "does-not-exist.ts")
|
|
||||||
const msg = await SessionPrompt.prompt({
|
|
||||||
sessionID: session.id,
|
|
||||||
agent: "build",
|
|
||||||
noReply: true,
|
|
||||||
parts: [
|
|
||||||
{ type: "text", text: "please review @does-not-exist.ts" },
|
|
||||||
{
|
|
||||||
type: "file",
|
|
||||||
mime: "text/plain",
|
|
||||||
url: `file://${missing}`,
|
|
||||||
filename: "does-not-exist.ts",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
if (msg.info.role !== "user") throw new Error("expected user message")
|
|
||||||
|
|
||||||
const hasFailure = msg.parts.some(
|
|
||||||
(part) => part.type === "text" && part.synthetic && part.text.includes("Read tool failed to read"),
|
|
||||||
)
|
|
||||||
expect(hasFailure).toBe(true)
|
|
||||||
|
|
||||||
await Session.remove(session.id)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -73,50 +73,4 @@ describe("tool.registry", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test("loads tools with external dependencies without crashing", async () => {
|
|
||||||
await using tmp = await tmpdir({
|
|
||||||
init: async (dir) => {
|
|
||||||
const opencodeDir = path.join(dir, ".opencode")
|
|
||||||
await fs.mkdir(opencodeDir, { recursive: true })
|
|
||||||
|
|
||||||
const toolsDir = path.join(opencodeDir, "tools")
|
|
||||||
await fs.mkdir(toolsDir, { recursive: true })
|
|
||||||
|
|
||||||
await Bun.write(
|
|
||||||
path.join(opencodeDir, "package.json"),
|
|
||||||
JSON.stringify({
|
|
||||||
name: "custom-tools",
|
|
||||||
dependencies: {
|
|
||||||
"@opencode-ai/plugin": "^0.0.0",
|
|
||||||
cowsay: "^1.6.0",
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
await Bun.write(
|
|
||||||
path.join(toolsDir, "cowsay.ts"),
|
|
||||||
[
|
|
||||||
"import { say } from 'cowsay'",
|
|
||||||
"export default {",
|
|
||||||
" description: 'tool that imports cowsay at top level',",
|
|
||||||
" args: { text: { type: 'string' } },",
|
|
||||||
" execute: async ({ text }: { text: string }) => {",
|
|
||||||
" return say({ text })",
|
|
||||||
" },",
|
|
||||||
"}",
|
|
||||||
"",
|
|
||||||
].join("\n"),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
await Instance.provide({
|
|
||||||
directory: tmp.path,
|
|
||||||
fn: async () => {
|
|
||||||
const ids = await ToolRegistry.ids()
|
|
||||||
expect(ids).toContain("cowsay")
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"name": "@opencode-ai/plugin",
|
"name": "@opencode-ai/plugin",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"name": "@opencode-ai/sdk",
|
"name": "@opencode-ai/sdk",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1309,10 +1309,6 @@ export type KeybindsConfig = {
|
|||||||
* Toggle tips on home screen
|
* Toggle tips on home screen
|
||||||
*/
|
*/
|
||||||
tips_toggle?: string
|
tips_toggle?: string
|
||||||
/**
|
|
||||||
* Toggle thinking blocks visibility
|
|
||||||
*/
|
|
||||||
display_thinking?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8883,11 +8883,6 @@
|
|||||||
"description": "Toggle tips on home screen",
|
"description": "Toggle tips on home screen",
|
||||||
"default": "<leader>h",
|
"default": "<leader>h",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
|
||||||
"display_thinking": {
|
|
||||||
"description": "Toggle thinking blocks visibility",
|
|
||||||
"default": "none",
|
|
||||||
"type": "string"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/slack",
|
"name": "@opencode-ai/slack",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/ui",
|
"name": "@opencode-ai/ui",
|
||||||
"version": "1.1.52",
|
"version": "1.1.51",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"exports": {
|
"exports": {
|
||||||
@@ -18,7 +18,6 @@
|
|||||||
"./theme/context": "./src/theme/context.tsx",
|
"./theme/context": "./src/theme/context.tsx",
|
||||||
"./icons/provider": "./src/components/provider-icons/types.ts",
|
"./icons/provider": "./src/components/provider-icons/types.ts",
|
||||||
"./icons/file-type": "./src/components/file-icons/types.ts",
|
"./icons/file-type": "./src/components/file-icons/types.ts",
|
||||||
"./icons/app": "./src/components/app-icons/types.ts",
|
|
||||||
"./fonts/*": "./src/assets/fonts/*",
|
"./fonts/*": "./src/assets/fonts/*",
|
||||||
"./audio/*": "./src/assets/audio/*"
|
"./audio/*": "./src/assets/audio/*"
|
||||||
},
|
},
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 8.0 KiB |
@@ -1 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?><svg id="cursor_light__Ebene_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 466.73 532.09"><!--Generator: Adobe Illustrator 29.6.1, SVG Export Plug-In . SVG Version: 2.1.1 Build 9)--><defs><style>.cursor_light__st0{fill:#26251e}</style></defs><path class="cursor_light__st0" d="M457.43,125.94L244.42,2.96c-6.84-3.95-15.28-3.95-22.12,0L9.3,125.94c-5.75,3.32-9.3,9.46-9.3,16.11v247.99c0,6.65,3.55,12.79,9.3,16.11l213.01,122.98c6.84,3.95,15.28,3.95,22.12,0l213.01-122.98c5.75-3.32,9.3-9.46,9.3-16.11v-247.99c0-6.65-3.55-12.79-9.3-16.11h-.01ZM444.05,151.99l-205.63,356.16c-1.39,2.4-5.06,1.42-5.06-1.36v-233.21c0-4.66-2.49-8.97-6.53-11.31L24.87,145.67c-2.4-1.39-1.42-5.06,1.36-5.06h411.26c5.84,0,9.49,6.33,6.57,11.39h-.01Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 782 B |
|
Before Width: | Height: | Size: 513 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 27 32"><path fill="#3551F3" d="M20.395 32a6.35 6.35 0 0 1-3.516-1.067A6.355 6.355 0 0 1 13.362 32c-1.249 0-2.48-.375-3.516-1.067A6.265 6.265 0 0 1 6.372 32h-.038a6.255 6.255 0 0 1-4.5-1.906 6.377 6.377 0 0 1-1.836-4.482v-12.25C0 5.995 5.994 0 13.362 0c7.369 0 13.363 5.994 13.363 13.363v12.253c0 3.393-2.626 6.192-5.978 6.375-.117.007-.234.009-.352.009Z"/><path fill="#000" d="M20.395 30.593a4.932 4.932 0 0 1-3.08-1.083.656.656 0 0 0-.42-.145.784.784 0 0 0-.487.176 4.939 4.939 0 0 1-3.046 1.055 4.939 4.939 0 0 1-3.045-1.055.751.751 0 0 0-.942 0 4.883 4.883 0 0 1-3.01 1.055h-.033a4.852 4.852 0 0 1-3.49-1.482 4.982 4.982 0 0 1-1.436-3.498V13.367c0-6.597 5.364-11.96 11.957-11.96 6.592 0 11.956 5.363 11.956 11.956v12.253c0 2.645-2.042 4.827-4.65 4.97a5.342 5.342 0 0 1-.274.007Z"/><path fill="#fff" d="M23.912 13.363v12.253c0 1.876-1.447 3.463-3.32 3.566a3.503 3.503 0 0 1-2.398-.769c-.778-.626-1.873-.598-2.658.021a3.5 3.5 0 0 1-2.176.753 3.494 3.494 0 0 1-2.173-.753 2.153 2.153 0 0 0-2.684 0 3.498 3.498 0 0 1-2.15.753c-1.948.014-3.54-1.627-3.54-3.575v-12.25c0-5.825 4.724-10.549 10.55-10.549 5.825 0 10.549 4.724 10.549 10.55Z"/><path fill="#000" d="m11.28 12.437-3.93-2.27a1.072 1.072 0 0 0-1.463.392 1.072 1.072 0 0 0 .391 1.463l2.326 1.343-2.326 1.343a1.072 1.072 0 0 0 1.071 1.855l3.932-2.27a1.071 1.071 0 0 0 0-1.854v-.002ZM20.182 12.291h-5.164a1.071 1.071 0 1 0 0 2.143h5.164a1.071 1.071 0 1 0 0-2.143Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,25 +0,0 @@
|
|||||||
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<g filter="url(#filter0_d)">
|
|
||||||
<rect x="100" y="100" width="824" height="824" rx="179" fill="url(#paint0_linear)"/>
|
|
||||||
</g>
|
|
||||||
<rect x="121.788" y="121.789" width="780.423" height="780.423" rx="156" fill="black"/>
|
|
||||||
<rect x="183.192" y="183.192" width="657.615" height="657.615" rx="94" fill="#202A2F"/>
|
|
||||||
<rect x="367.404" y="226.769" width="89.1346" height="178.269" fill="#0EE827" fill-opacity="0.35"/>
|
|
||||||
<path d="M274.468 374.622C269.807 374.227 265.438 373.568 261.36 372.645C257.427 371.59 253.786 370.47 250.436 369.284C247.232 368.097 244.392 366.977 241.916 365.922C239.586 364.736 237.838 363.813 236.673 363.154L246.067 345.754C247.086 346.413 248.834 347.335 251.31 348.522C253.786 349.708 256.553 350.96 259.612 352.279C262.816 353.465 266.093 354.52 269.443 355.442C272.793 356.365 275.924 356.827 278.837 356.827C293.402 356.827 300.684 351.356 300.684 340.415C300.684 337.778 300.174 335.603 299.154 333.89C298.281 332.176 296.897 330.726 295.004 329.54C293.256 328.221 291.071 327.101 288.45 326.178C285.974 325.124 283.134 324.069 279.929 323.015C273.812 320.905 268.351 318.73 263.544 316.489C258.884 314.117 254.878 311.48 251.529 308.58C248.179 305.68 245.63 302.385 243.882 298.694C242.135 295.003 241.261 290.784 241.261 286.039C241.261 282.348 242.062 278.789 243.664 275.361C245.266 271.934 247.523 268.902 250.436 266.266C253.349 263.498 256.845 261.191 260.923 259.345C265.001 257.368 269.516 255.984 274.468 255.193V226.769H292.382V254.797C296.169 255.193 299.81 255.786 303.305 256.577C306.801 257.368 309.932 258.225 312.699 259.147C315.467 260.07 317.797 260.993 319.69 261.916C321.729 262.707 323.186 263.3 324.06 263.695L315.321 279.909C314.156 279.382 312.481 278.723 310.296 277.932C308.257 277.009 305.927 276.086 303.305 275.164C300.684 274.241 297.844 273.45 294.785 272.791C291.727 272.132 288.668 271.802 285.61 271.802C280.658 271.802 276.215 272.725 272.283 274.57C268.496 276.284 266.603 279.25 266.603 283.468C266.603 286.105 267.113 288.478 268.132 290.587C269.297 292.564 270.899 294.344 272.938 295.925C275.123 297.507 277.745 299.023 280.803 300.473C284.007 301.791 287.649 303.11 291.727 304.428C297.115 306.405 301.922 308.448 306.145 310.558C310.369 312.667 313.937 315.039 316.85 317.676C319.763 320.312 321.948 323.344 323.404 326.771C325.006 330.199 325.807 334.219 325.807 338.833C325.807 342.788 325.079 346.61 323.623 350.301C322.312 353.992 320.2 357.42 317.287 360.583C314.52 363.747 311.025 366.515 306.801 368.888C302.723 371.129 297.916 372.777 292.382 373.831V403.058H274.468V374.622Z" fill="#0EE827"/>
|
|
||||||
<defs>
|
|
||||||
<filter id="filter0_d" x="78" y="86" width="868" height="868" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
||||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
||||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
|
||||||
<feMorphology radius="2" operator="dilate" in="SourceAlpha" result="effect1_dropShadow"/>
|
|
||||||
<feOffset dy="8"/>
|
|
||||||
<feGaussianBlur stdDeviation="10"/>
|
|
||||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
|
||||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
|
||||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
|
||||||
</filter>
|
|
||||||
<linearGradient id="paint0_linear" x1="512" y1="100" x2="512" y2="924" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#D4E6E8"/>
|
|
||||||
<stop offset="1" stop-color="#767573"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 3.5 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 128 128"><linearGradient id="powershell__a" x1="96.306" x2="25.454" y1="35.144" y2="98.431" gradientTransform="matrix(1 0 0 -1 0 128)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#a9c8ff"/><stop offset="1" stop-color="#c7e6ff"/></linearGradient><path fill="url(#powershell__a)" fill-rule="evenodd" d="M7.2 110.5c-1.7 0-3.1-.7-4.1-1.9-1-1.2-1.3-2.9-.9-4.6l18.6-80.5c.8-3.4 4-6 7.4-6h92.6c1.7 0 3.1.7 4.1 1.9 1 1.2 1.3 2.9.9 4.6l-18.6 80.5c-.8 3.4-4 6-7.4 6H7.2z" clip-rule="evenodd" opacity=".8"/><linearGradient id="powershell__b" x1="25.336" x2="94.569" y1="98.33" y2="36.847" gradientTransform="matrix(1 0 0 -1 0 128)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#2d4664"/><stop offset=".169" stop-color="#29405b"/><stop offset=".445" stop-color="#1e2f43"/><stop offset=".79" stop-color="#0c131b"/><stop offset="1"/></linearGradient><path fill="url(#powershell__b)" fill-rule="evenodd" d="M120.3 18.5H28.5c-2.9 0-5.7 2.3-6.4 5.2L3.7 104.3c-.7 2.9 1.1 5.2 4 5.2h91.8c2.9 0 5.7-2.3 6.4-5.2l18.4-80.5c.7-2.9-1.1-5.3-4-5.3z" clip-rule="evenodd"/><path fill="#2C5591" fill-rule="evenodd" d="M64.2 88.3h22.3c2.6 0 4.7 2.2 4.7 4.9s-2.1 4.9-4.7 4.9H64.2c-2.6 0-4.7-2.2-4.7-4.9s2.1-4.9 4.7-4.9zM78.7 66.5c-.4.8-1.2 1.6-2.6 2.6L34.6 98.9c-2.3 1.6-5.5 1-7.3-1.4-1.7-2.4-1.3-5.7.9-7.3l37.4-27.1v-.6l-23.5-25c-1.9-2-1.7-5.3.4-7.4 2.2-2 5.5-2 7.4 0l28.2 30c1.7 1.9 1.8 4.5.6 6.4z" clip-rule="evenodd"/><path fill="#FFF" fill-rule="evenodd" d="M77.6 65.5c-.4.8-1.2 1.6-2.6 2.6L33.6 97.9c-2.3 1.6-5.5 1-7.3-1.4-1.7-2.4-1.3-5.7.9-7.3l37.4-27.1v-.6l-23.5-25c-1.9-2-1.7-5.3.4-7.4 2.2-2 5.5-2 7.4 0l28.2 30c1.7 1.8 1.8 4.4.5 6.4zM63.5 87.8h22.3c2.6 0 4.7 2.1 4.7 4.6 0 2.6-2.1 4.6-4.7 4.6H63.5c-2.6 0-4.7-2.1-4.7-4.6 0-2.6 2.1-4.6 4.7-4.6z" clip-rule="evenodd"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.8 KiB |