diff --git a/.changeset/show-sandbox-network.md b/.changeset/show-sandbox-network.md new file mode 100644 index 00000000000..28fefe64b35 --- /dev/null +++ b/.changeset/show-sandbox-network.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Show filesystem and network restriction states in the sandbox tooltip. diff --git a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/prompt-input/sandbox-tooltip-disabled-chromium-linux.png b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/prompt-input/sandbox-tooltip-disabled-chromium-linux.png new file mode 100644 index 00000000000..dc0021f5018 --- /dev/null +++ b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/prompt-input/sandbox-tooltip-disabled-chromium-linux.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:47770a3c0f31c816f0dc21fc75dc9c591209c7599e3f69df31f5b1e267546c62 +size 1167 diff --git a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/prompt-input/sandbox-tooltip-enabled-chromium-linux.png b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/prompt-input/sandbox-tooltip-enabled-chromium-linux.png new file mode 100644 index 00000000000..0e8674567cd --- /dev/null +++ b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/prompt-input/sandbox-tooltip-enabled-chromium-linux.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c000d3be4d59f1baf63415abecfe1ffb470ef3e9068876fd5c0d1ee0b12f6c0 +size 1336 diff --git a/packages/kilo-ui/src/components/icon.tsx b/packages/kilo-ui/src/components/icon.tsx index 01fb920cf51..773d17d1f5c 100644 --- a/packages/kilo-ui/src/components/icon.tsx +++ b/packages/kilo-ui/src/components/icon.tsx @@ -10,6 +10,10 @@ const icons: Record = { viewBox: "0 0 16 16", path: ``, }, + globe: { + viewBox: "0 0 20 20", + path: ``, + }, organization: { viewBox: "0 0 16 16", path: ``, diff --git a/packages/kilo-vscode/tests/unit/prompt-input-connection-guard.test.ts b/packages/kilo-vscode/tests/unit/prompt-input-connection-guard.test.ts index 4fab1cf5a3d..e69d50cfadb 100644 --- a/packages/kilo-vscode/tests/unit/prompt-input-connection-guard.test.ts +++ b/packages/kilo-vscode/tests/unit/prompt-input-connection-guard.test.ts @@ -4,6 +4,8 @@ import { join } from "node:path" const path = join(__dirname, "..", "..", "webview-ui", "src", "components", "chat", "PromptInput.tsx") const src = readFileSync(path, "utf8") +const iconPath = join(__dirname, "..", "..", "..", "kilo-ui", "src", "components", "icon.tsx") +const icons = readFileSync(iconPath, "utf8") describe("PromptInput connection guard", () => { it("rechecks the connection after resolving async attachments and before clearing the draft", () => { @@ -67,4 +69,18 @@ describe("PromptInput sandbox toggle", () => { expect(src).toContain("if (sandboxRequest() && target === null) return") expect(src).not.toContain("if (state === current) return true") }) + + it("explains filesystem and network state without changing the lock icon", () => { + expect(src).toContain( + "const sandboxNetworkEnabled = () => config().experimental?.sandbox_restrict_network !== false", + ) + expect(src).toContain('') + expect(src).toContain("") + expect(src).toContain('') + expect(src).toContain('') + expect(src).toContain("props.enabled && props.network") + expect(src).not.toContain('class="prompt-sandbox-network"') + expect(src).not.toContain('class="prompt-sandbox-icon"') + expect(icons).toContain("globe: {") + }) }) diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx index 51daa8f9776..5f3c29776fe 100644 --- a/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx @@ -83,6 +83,49 @@ interface PromptInputProps { pendingSessionID?: string } +export const SandboxTooltipContent: Component<{ enabled: boolean; network: boolean }> = (props) => { + const language = useLanguage() + + return ( +
+
+ {language.t(props.enabled ? "prompt.action.sandbox.status.enabled" : "prompt.action.sandbox.status.disabled")} +
+
+ + {language.t("prompt.action.sandbox.filesystem")} + + {language.t( + props.enabled ? "prompt.action.sandbox.filesystem.restricted" : "prompt.action.sandbox.unrestricted", + )} + +
+
+ + {language.t("prompt.action.sandbox.network")} + + {language.t( + props.enabled && props.network + ? "prompt.action.sandbox.network.blocked" + : props.enabled + ? "prompt.action.sandbox.network.allowed" + : "prompt.action.sandbox.unrestricted", + )} + +
+
+ {language.t( + props.enabled + ? "prompt.action.sandbox.description.enabled" + : props.network + ? "prompt.action.sandbox.description.disabled" + : "prompt.action.sandbox.description.disabledNetworkAllowed", + )} +
+
+ ) +} + export const PromptInput: Component = (props) => { const session = useSession() const server = useServer() @@ -169,6 +212,7 @@ export const PromptInput: Component = (props) => { return state?.sessionID === sandboxID() ? state : undefined } const sandboxEnabled = () => sandbox()?.enabled ?? (!sandboxID() && config().experimental?.sandbox === true) + const sandboxNetworkEnabled = () => config().experimental?.sandbox_restrict_network !== false const sandboxReady = () => !sandboxID() || sandbox() !== undefined const requestSandbox = () => { const sessionID = sandboxID() @@ -1217,12 +1261,13 @@ export const PromptInput: Component = (props) => { + ) } + contentClass="prompt-sandbox-tooltip-content" placement="top" > + + + + ), +} + +export const SandboxTooltipDisabled: Story = { + name: "Sandbox tooltip — disabled", + render: () => ( + +
+ } + contentClass="prompt-sandbox-tooltip-content" + placement="top" + > + + +
+
+ ), +} + // --------------------------------------------------------------------------- // Stories — model with thinking-effort variants (ThinkingSelector visible) // --------------------------------------------------------------------------- diff --git a/packages/kilo-vscode/webview-ui/src/styles/prompt-input.css b/packages/kilo-vscode/webview-ui/src/styles/prompt-input.css index 0eb470ffc2c..9866ca33191 100644 --- a/packages/kilo-vscode/webview-ui/src/styles/prompt-input.css +++ b/packages/kilo-vscode/webview-ui/src/styles/prompt-input.css @@ -564,6 +564,48 @@ position: relative; } +.prompt-sandbox-tooltip-content { + width: 260px; + padding: 10px 12px; + white-space: normal; +} + +.prompt-sandbox-tooltip { + display: flex; + flex-direction: column; + gap: 8px; +} + +.prompt-sandbox-tooltip-title { + color: var(--text-invert-strong); + font-weight: var(--font-weight-medium); +} + +.prompt-sandbox-tooltip-row { + display: grid; + grid-template-columns: 16px 1fr auto; + align-items: center; + gap: 8px; + color: var(--text-invert-base); + + [data-component="icon"] { + width: 16px; + height: 16px; + } +} + +.prompt-sandbox-tooltip-state { + color: var(--text-invert-strong); + font-weight: var(--font-weight-medium); +} + +.prompt-sandbox-tooltip-description { + padding-top: 8px; + border-top: 1px solid var(--border-weak-base); + color: var(--text-invert-base); + font-weight: var(--font-weight-normal); +} + .prompt-status-button--active { color: var(--vscode-testing-iconPassed, #73c991);