diff --git a/docs/nav/development/release-notes.md b/docs/nav/development/release-notes.md index 4587880..5421a24 100644 --- a/docs/nav/development/release-notes.md +++ b/docs/nav/development/release-notes.md @@ -1,5 +1,10 @@ # Release Notes +## v0.4.0 + +- [📦 Marketplace - Build 0.4.0](https://marketplace.visualstudio.com/items?itemName=fernandocelmer.pycodeloop) +- [⚙️ Feature: Replace tool-card emoji with codicons, go-to-file, and auto-approved diffs (#21)](https://github.com/dotflow-io/vscodeloop/pull/22) + ## v0.3.5 - [📦 Marketplace - Build 0.3.5](https://marketplace.visualstudio.com/items?itemName=fernandocelmer.pycodeloop) @@ -7,7 +12,6 @@ ## v0.3.4 -- [📦 Marketplace - Build 0.3.4](https://marketplace.visualstudio.com/items?itemName=fernandocelmer.pycodeloop) - [🪲 Bug: Fix messages blending together across tool calls (#18)](https://github.com/dotflow-io/vscodeloop/pull/18) ## v0.3.3 diff --git a/media/app.js b/media/app.js index 9d01911..0727c9d 100644 --- a/media/app.js +++ b/media/app.js @@ -48,7 +48,7 @@ window.addEventListener("message", (event) => { resolveToolResult(message.name, message.result, message.isError); break; case "autoApproved": - markToolAutoApproved(message.name); + markToolAutoApproved(message.name, message.preview); break; case "confirmRequest": renderConfirmRequest(message); diff --git a/media/chat-tools.js b/media/chat-tools.js index 68e83df..b64922c 100644 --- a/media/chat-tools.js +++ b/media/chat-tools.js @@ -1,22 +1,24 @@ const TOOL_ICONS = { - read_file: "📄", - write_file: "📝", - edit_file: "✏️", - delete_file: "🗑", - list_dir: "📁", - grep: "🔍", - glob: "🔍", - bash: "❯_", - http_request: "🌐", - web_fetch: "🌐", - todo: "☑", - delegate: "🤖", + read_file: "file-text", + write_file: "new-file", + edit_file: "edit", + delete_file: "trash", + list_dir: "folder", + grep: "search", + glob: "search", + bash: "terminal", + http_request: "globe", + web_fetch: "globe", + todo: "checklist", + delegate: "type-hierarchy-sub", }; function toolIcon(name) { - return TOOL_ICONS[name] || "🔧"; + return TOOL_ICONS[name] || "tools"; } +const FILE_TOOLS = new Set(["read_file", "write_file", "edit_file", "delete_file"]); + function argsPreview(args) { try { return JSON.stringify(args); @@ -39,8 +41,7 @@ function renderToolCall(name, args) { header.className = "tool-card-header"; const icon = document.createElement("span"); - icon.className = "tool-icon"; - icon.textContent = toolIcon(name); + icon.className = "tool-icon codicon codicon-" + toolIcon(name); const nameEl = document.createElement("span"); nameEl.className = "tool-name"; @@ -50,13 +51,27 @@ function renderToolCall(name, args) { preview.className = "tool-args-preview"; preview.textContent = argsPreview(args); - const status = document.createElement("span"); - status.className = "tool-status pending"; - status.textContent = "●"; - header.appendChild(icon); header.appendChild(nameEl); header.appendChild(preview); + + if (args && typeof args.path === "string" && FILE_TOOLS.has(name)) { + const openFile = document.createElement("button"); + openFile.className = "tool-open-file codicon codicon-go-to-file"; + openFile.title = "Open " + args.path; + openFile.addEventListener("click", (event) => { + event.stopPropagation(); + vscode.postMessage({ type: "openFile", path: args.path }); + }); + header.appendChild(openFile); + } + + const status = document.createElement("span"); + status.className = "tool-status pending"; + const statusIcon = document.createElement("span"); + statusIcon.className = "tool-status-icon codicon codicon-loading codicon-modifier-spin"; + status.appendChild(statusIcon); + header.appendChild(status); header.addEventListener("click", () => card.classList.toggle("expanded")); @@ -77,6 +92,7 @@ function renderToolCall(name, args) { scrollToBottom(); card.__status = status; + card.__statusIcon = statusIcon; card.__body = body; card.__diffPreview = null; @@ -95,8 +111,9 @@ function resolveToolResult(name, result, isError) { return; } - card.__status.textContent = isError ? "✗" : "✓"; card.__status.className = "tool-status " + (isError ? "error" : "ok"); + card.__statusIcon.className = + "tool-status-icon codicon " + (isError ? "codicon-close" : "codicon-check"); if (card.__diffPreview && !isError) { const { el, adds, removes } = renderDiffBlock(card.__diffPreview); @@ -148,8 +165,8 @@ function renderHistory(messages) { if (!card) { continue; } - card.__status.textContent = "✓"; card.__status.className = "tool-status ok"; + card.__statusIcon.className = "tool-status-icon codicon codicon-check"; const resultLabel = document.createElement("div"); resultLabel.className = "section-label"; resultLabel.textContent = "Result"; @@ -164,7 +181,7 @@ function renderHistory(messages) { scrollToBottom(); } -function markToolAutoApproved(name) { +function markToolAutoApproved(name, preview) { const queue = pendingToolCards.get(name); const card = queue && queue[0]; if (!card) { @@ -172,6 +189,9 @@ function markToolAutoApproved(name) { } card.__status.title = "auto-approved"; card.__status.classList.add("auto-approved"); + if (DIFF_TOOLS.has(name) && preview) { + card.__diffPreview = preview; + } } function setBusy(busy) { diff --git a/media/codicon/codicon.css b/media/codicon/codicon.css new file mode 100644 index 0000000..33998bb --- /dev/null +++ b/media/codicon/codicon.css @@ -0,0 +1,805 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +@font-face { + font-family: "codicon"; + font-display: block; + src: url("./codicon.ttf?9aab6318a6710999273bab9c78a9fd71") format("truetype"); +} + +.codicon[class*='codicon-'] { + font: normal normal normal 16px/1 codicon; + display: inline-block; + text-decoration: none; + text-rendering: auto; + text-align: center; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + user-select: none; + -webkit-user-select: none; + -ms-user-select: none; +} + +/*--------------------- + * Modifiers + *-------------------*/ + +@keyframes codicon-spin { + 100% { + transform:rotate(360deg); + } +} + +.codicon-sync.codicon-modifier-spin, +.codicon-loading.codicon-modifier-spin, +.codicon-gear.codicon-modifier-spin { + /* Use steps to throttle FPS to reduce CPU usage */ + animation: codicon-spin 1.5s steps(30) infinite; +} + +.codicon-modifier-disabled { + opacity: 0.5; +} + +.codicon-modifier-hidden { + opacity: 0; +} + +/* custom speed & easing for loading icon */ +.codicon-loading { + animation-duration: 1s !important; + animation-timing-function: cubic-bezier(0.53, 0.21, 0.29, 0.67) !important; +} + +/*--------------------- + * Icons + *-------------------*/ + +.codicon-add:before { content: "\ea60" } +.codicon-plus:before { content: "\ea60" } +.codicon-gist-new:before { content: "\ea60" } +.codicon-repo-create:before { content: "\ea60" } +.codicon-lightbulb:before { content: "\ea61" } +.codicon-light-bulb:before { content: "\ea61" } +.codicon-repo:before { content: "\ea62" } +.codicon-repo-delete:before { content: "\ea62" } +.codicon-gist-fork:before { content: "\ea63" } +.codicon-repo-forked:before { content: "\ea63" } +.codicon-git-pull-request:before { content: "\ea64" } +.codicon-git-pull-request-abandoned:before { content: "\ea64" } +.codicon-record-keys:before { content: "\ea65" } +.codicon-keyboard:before { content: "\ea65" } +.codicon-tag:before { content: "\ea66" } +.codicon-git-pull-request-label:before { content: "\ea66" } +.codicon-tag-add:before { content: "\ea66" } +.codicon-tag-remove:before { content: "\ea66" } +.codicon-person:before { content: "\ea67" } +.codicon-person-follow:before { content: "\ea67" } +.codicon-person-outline:before { content: "\ea67" } +.codicon-person-filled:before { content: "\ea67" } +.codicon-source-control:before { content: "\ea68" } +.codicon-mirror:before { content: "\ea69" } +.codicon-mirror-public:before { content: "\ea69" } +.codicon-star:before { content: "\ea6a" } +.codicon-star-add:before { content: "\ea6a" } +.codicon-star-delete:before { content: "\ea6a" } +.codicon-star-empty:before { content: "\ea6a" } +.codicon-comment:before { content: "\ea6b" } +.codicon-comment-add:before { content: "\ea6b" } +.codicon-alert:before { content: "\ea6c" } +.codicon-warning:before { content: "\ea6c" } +.codicon-search:before { content: "\ea6d" } +.codicon-search-save:before { content: "\ea6d" } +.codicon-log-out:before { content: "\ea6e" } +.codicon-sign-out:before { content: "\ea6e" } +.codicon-log-in:before { content: "\ea6f" } +.codicon-sign-in:before { content: "\ea6f" } +.codicon-eye:before { content: "\ea70" } +.codicon-eye-unwatch:before { content: "\ea70" } +.codicon-eye-watch:before { content: "\ea70" } +.codicon-circle-filled:before { content: "\ea71" } +.codicon-primitive-dot:before { content: "\ea71" } +.codicon-close-dirty:before { content: "\ea71" } +.codicon-debug-breakpoint:before { content: "\ea71" } +.codicon-debug-breakpoint-disabled:before { content: "\ea71" } +.codicon-debug-hint:before { content: "\ea71" } +.codicon-terminal-decoration-success:before { content: "\ea71" } +.codicon-primitive-square:before { content: "\ea72" } +.codicon-edit:before { content: "\ea73" } +.codicon-pencil:before { content: "\ea73" } +.codicon-info:before { content: "\ea74" } +.codicon-issue-opened:before { content: "\ea74" } +.codicon-gist-private:before { content: "\ea75" } +.codicon-git-fork-private:before { content: "\ea75" } +.codicon-lock:before { content: "\ea75" } +.codicon-mirror-private:before { content: "\ea75" } +.codicon-close:before { content: "\ea76" } +.codicon-remove-close:before { content: "\ea76" } +.codicon-x:before { content: "\ea76" } +.codicon-repo-sync:before { content: "\ea77" } +.codicon-sync:before { content: "\ea77" } +.codicon-clone:before { content: "\ea78" } +.codicon-desktop-download:before { content: "\ea78" } +.codicon-beaker:before { content: "\ea79" } +.codicon-microscope:before { content: "\ea79" } +.codicon-vm:before { content: "\ea7a" } +.codicon-device-desktop:before { content: "\ea7a" } +.codicon-file:before { content: "\ea7b" } +.codicon-more:before { content: "\ea7c" } +.codicon-ellipsis:before { content: "\ea7c" } +.codicon-kebab-horizontal:before { content: "\ea7c" } +.codicon-mail-reply:before { content: "\ea7d" } +.codicon-reply:before { content: "\ea7d" } +.codicon-organization:before { content: "\ea7e" } +.codicon-organization-filled:before { content: "\ea7e" } +.codicon-organization-outline:before { content: "\ea7e" } +.codicon-new-file:before { content: "\ea7f" } +.codicon-file-add:before { content: "\ea7f" } +.codicon-new-folder:before { content: "\ea80" } +.codicon-file-directory-create:before { content: "\ea80" } +.codicon-trash:before { content: "\ea81" } +.codicon-trashcan:before { content: "\ea81" } +.codicon-history:before { content: "\ea82" } +.codicon-clock:before { content: "\ea82" } +.codicon-folder:before { content: "\ea83" } +.codicon-file-directory:before { content: "\ea83" } +.codicon-symbol-folder:before { content: "\ea83" } +.codicon-logo-github:before { content: "\ea84" } +.codicon-mark-github:before { content: "\ea84" } +.codicon-github:before { content: "\ea84" } +.codicon-terminal:before { content: "\ea85" } +.codicon-console:before { content: "\ea85" } +.codicon-repl:before { content: "\ea85" } +.codicon-zap:before { content: "\ea86" } +.codicon-symbol-event:before { content: "\ea86" } +.codicon-error:before { content: "\ea87" } +.codicon-stop:before { content: "\ea87" } +.codicon-variable:before { content: "\ea88" } +.codicon-symbol-variable:before { content: "\ea88" } +.codicon-array:before { content: "\ea8a" } +.codicon-symbol-array:before { content: "\ea8a" } +.codicon-symbol-module:before { content: "\ea8b" } +.codicon-symbol-package:before { content: "\ea8b" } +.codicon-symbol-namespace:before { content: "\ea8b" } +.codicon-symbol-object:before { content: "\ea8b" } +.codicon-symbol-method:before { content: "\ea8c" } +.codicon-symbol-function:before { content: "\ea8c" } +.codicon-symbol-constructor:before { content: "\ea8c" } +.codicon-symbol-boolean:before { content: "\ea8f" } +.codicon-symbol-null:before { content: "\ea8f" } +.codicon-symbol-numeric:before { content: "\ea90" } +.codicon-symbol-number:before { content: "\ea90" } +.codicon-symbol-structure:before { content: "\ea91" } +.codicon-symbol-struct:before { content: "\ea91" } +.codicon-symbol-parameter:before { content: "\ea92" } +.codicon-symbol-type-parameter:before { content: "\ea92" } +.codicon-symbol-key:before { content: "\ea93" } +.codicon-symbol-text:before { content: "\ea93" } +.codicon-symbol-reference:before { content: "\ea94" } +.codicon-go-to-file:before { content: "\ea94" } +.codicon-symbol-enum:before { content: "\ea95" } +.codicon-symbol-value:before { content: "\ea95" } +.codicon-symbol-ruler:before { content: "\ea96" } +.codicon-symbol-unit:before { content: "\ea96" } +.codicon-activate-breakpoints:before { content: "\ea97" } +.codicon-archive:before { content: "\ea98" } +.codicon-arrow-both:before { content: "\ea99" } +.codicon-arrow-down:before { content: "\ea9a" } +.codicon-arrow-left:before { content: "\ea9b" } +.codicon-arrow-right:before { content: "\ea9c" } +.codicon-arrow-small-down:before { content: "\ea9d" } +.codicon-arrow-small-left:before { content: "\ea9e" } +.codicon-arrow-small-right:before { content: "\ea9f" } +.codicon-arrow-small-up:before { content: "\eaa0" } +.codicon-arrow-up:before { content: "\eaa1" } +.codicon-bell:before { content: "\eaa2" } +.codicon-bold:before { content: "\eaa3" } +.codicon-book:before { content: "\eaa4" } +.codicon-bookmark:before { content: "\eaa5" } +.codicon-debug-breakpoint-conditional-unverified:before { content: "\eaa6" } +.codicon-debug-breakpoint-conditional:before { content: "\eaa7" } +.codicon-debug-breakpoint-conditional-disabled:before { content: "\eaa7" } +.codicon-debug-breakpoint-data-unverified:before { content: "\eaa8" } +.codicon-debug-breakpoint-data:before { content: "\eaa9" } +.codicon-debug-breakpoint-data-disabled:before { content: "\eaa9" } +.codicon-debug-breakpoint-log-unverified:before { content: "\eaaa" } +.codicon-debug-breakpoint-log:before { content: "\eaab" } +.codicon-debug-breakpoint-log-disabled:before { content: "\eaab" } +.codicon-briefcase:before { content: "\eaac" } +.codicon-broadcast:before { content: "\eaad" } +.codicon-browser:before { content: "\eaae" } +.codicon-bug:before { content: "\eaaf" } +.codicon-calendar:before { content: "\eab0" } +.codicon-case-sensitive:before { content: "\eab1" } +.codicon-check:before { content: "\eab2" } +.codicon-checklist:before { content: "\eab3" } +.codicon-chevron-down:before { content: "\eab4" } +.codicon-chevron-left:before { content: "\eab5" } +.codicon-chevron-right:before { content: "\eab6" } +.codicon-chevron-up:before { content: "\eab7" } +.codicon-chrome-close:before { content: "\eab8" } +.codicon-chrome-maximize:before { content: "\eab9" } +.codicon-chrome-minimize:before { content: "\eaba" } +.codicon-chrome-restore:before { content: "\eabb" } +.codicon-circle-outline:before { content: "\eabc" } +.codicon-circle:before { content: "\eabc" } +.codicon-debug-breakpoint-unverified:before { content: "\eabc" } +.codicon-terminal-decoration-incomplete:before { content: "\eabc" } +.codicon-circle-slash:before { content: "\eabd" } +.codicon-circuit-board:before { content: "\eabe" } +.codicon-clear-all:before { content: "\eabf" } +.codicon-clippy:before { content: "\eac0" } +.codicon-close-all:before { content: "\eac1" } +.codicon-cloud-download:before { content: "\eac2" } +.codicon-cloud-upload:before { content: "\eac3" } +.codicon-code:before { content: "\eac4" } +.codicon-collapse-all:before { content: "\eac5" } +.codicon-color-mode:before { content: "\eac6" } +.codicon-comment-discussion:before { content: "\eac7" } +.codicon-credit-card:before { content: "\eac9" } +.codicon-dash:before { content: "\eacc" } +.codicon-dashboard:before { content: "\eacd" } +.codicon-database:before { content: "\eace" } +.codicon-debug-continue:before { content: "\eacf" } +.codicon-debug-disconnect:before { content: "\ead0" } +.codicon-debug-pause:before { content: "\ead1" } +.codicon-debug-restart:before { content: "\ead2" } +.codicon-debug-start:before { content: "\ead3" } +.codicon-debug-step-into:before { content: "\ead4" } +.codicon-debug-step-out:before { content: "\ead5" } +.codicon-debug-step-over:before { content: "\ead6" } +.codicon-debug-stop:before { content: "\ead7" } +.codicon-debug:before { content: "\ead8" } +.codicon-device-camera-video:before { content: "\ead9" } +.codicon-device-camera:before { content: "\eada" } +.codicon-device-mobile:before { content: "\eadb" } +.codicon-diff-added:before { content: "\eadc" } +.codicon-diff-ignored:before { content: "\eadd" } +.codicon-diff-modified:before { content: "\eade" } +.codicon-diff-removed:before { content: "\eadf" } +.codicon-diff-renamed:before { content: "\eae0" } +.codicon-diff:before { content: "\eae1" } +.codicon-diff-sidebyside:before { content: "\eae1" } +.codicon-discard:before { content: "\eae2" } +.codicon-editor-layout:before { content: "\eae3" } +.codicon-empty-window:before { content: "\eae4" } +.codicon-exclude:before { content: "\eae5" } +.codicon-extensions:before { content: "\eae6" } +.codicon-eye-closed:before { content: "\eae7" } +.codicon-file-binary:before { content: "\eae8" } +.codicon-file-code:before { content: "\eae9" } +.codicon-file-media:before { content: "\eaea" } +.codicon-file-pdf:before { content: "\eaeb" } +.codicon-file-submodule:before { content: "\eaec" } +.codicon-file-symlink-directory:before { content: "\eaed" } +.codicon-file-symlink-file:before { content: "\eaee" } +.codicon-file-zip:before { content: "\eaef" } +.codicon-files:before { content: "\eaf0" } +.codicon-filter:before { content: "\eaf1" } +.codicon-flame:before { content: "\eaf2" } +.codicon-fold-down:before { content: "\eaf3" } +.codicon-fold-up:before { content: "\eaf4" } +.codicon-fold:before { content: "\eaf5" } +.codicon-folder-active:before { content: "\eaf6" } +.codicon-folder-opened:before { content: "\eaf7" } +.codicon-gear:before { content: "\eaf8" } +.codicon-gift:before { content: "\eaf9" } +.codicon-gist-secret:before { content: "\eafa" } +.codicon-gist:before { content: "\eafb" } +.codicon-git-commit:before { content: "\eafc" } +.codicon-git-compare:before { content: "\eafd" } +.codicon-compare-changes:before { content: "\eafd" } +.codicon-git-merge:before { content: "\eafe" } +.codicon-github-action:before { content: "\eaff" } +.codicon-github-alt:before { content: "\eb00" } +.codicon-globe:before { content: "\eb01" } +.codicon-grabber:before { content: "\eb02" } +.codicon-graph:before { content: "\eb03" } +.codicon-gripper:before { content: "\eb04" } +.codicon-heart:before { content: "\eb05" } +.codicon-home:before { content: "\eb06" } +.codicon-horizontal-rule:before { content: "\eb07" } +.codicon-hubot:before { content: "\eb08" } +.codicon-inbox:before { content: "\eb09" } +.codicon-issue-reopened:before { content: "\eb0b" } +.codicon-issues:before { content: "\eb0c" } +.codicon-italic:before { content: "\eb0d" } +.codicon-jersey:before { content: "\eb0e" } +.codicon-json:before { content: "\eb0f" } +.codicon-bracket:before { content: "\eb0f" } +.codicon-kebab-vertical:before { content: "\eb10" } +.codicon-key:before { content: "\eb11" } +.codicon-law:before { content: "\eb12" } +.codicon-lightbulb-autofix:before { content: "\eb13" } +.codicon-link-external:before { content: "\eb14" } +.codicon-link:before { content: "\eb15" } +.codicon-list-ordered:before { content: "\eb16" } +.codicon-list-unordered:before { content: "\eb17" } +.codicon-live-share:before { content: "\eb18" } +.codicon-loading:before { content: "\eb19" } +.codicon-location:before { content: "\eb1a" } +.codicon-mail-read:before { content: "\eb1b" } +.codicon-mail:before { content: "\eb1c" } +.codicon-markdown:before { content: "\eb1d" } +.codicon-megaphone:before { content: "\eb1e" } +.codicon-mention:before { content: "\eb1f" } +.codicon-milestone:before { content: "\eb20" } +.codicon-git-pull-request-milestone:before { content: "\eb20" } +.codicon-mortar-board:before { content: "\eb21" } +.codicon-move:before { content: "\eb22" } +.codicon-multiple-windows:before { content: "\eb23" } +.codicon-mute:before { content: "\eb24" } +.codicon-no-newline:before { content: "\eb25" } +.codicon-note:before { content: "\eb26" } +.codicon-octoface:before { content: "\eb27" } +.codicon-open-preview:before { content: "\eb28" } +.codicon-package:before { content: "\eb29" } +.codicon-paintcan:before { content: "\eb2a" } +.codicon-pin:before { content: "\eb2b" } +.codicon-play:before { content: "\eb2c" } +.codicon-run:before { content: "\eb2c" } +.codicon-plug:before { content: "\eb2d" } +.codicon-preserve-case:before { content: "\eb2e" } +.codicon-preview:before { content: "\eb2f" } +.codicon-project:before { content: "\eb30" } +.codicon-pulse:before { content: "\eb31" } +.codicon-question:before { content: "\eb32" } +.codicon-quote:before { content: "\eb33" } +.codicon-radio-tower:before { content: "\eb34" } +.codicon-reactions:before { content: "\eb35" } +.codicon-references:before { content: "\eb36" } +.codicon-refresh:before { content: "\eb37" } +.codicon-regex:before { content: "\eb38" } +.codicon-remote-explorer:before { content: "\eb39" } +.codicon-remote:before { content: "\eb3a" } +.codicon-remove:before { content: "\eb3b" } +.codicon-replace-all:before { content: "\eb3c" } +.codicon-replace:before { content: "\eb3d" } +.codicon-repo-clone:before { content: "\eb3e" } +.codicon-repo-force-push:before { content: "\eb3f" } +.codicon-repo-pull:before { content: "\eb40" } +.codicon-repo-push:before { content: "\eb41" } +.codicon-report:before { content: "\eb42" } +.codicon-request-changes:before { content: "\eb43" } +.codicon-rocket:before { content: "\eb44" } +.codicon-root-folder-opened:before { content: "\eb45" } +.codicon-root-folder:before { content: "\eb46" } +.codicon-rss:before { content: "\eb47" } +.codicon-ruby:before { content: "\eb48" } +.codicon-save-all:before { content: "\eb49" } +.codicon-save-as:before { content: "\eb4a" } +.codicon-save:before { content: "\eb4b" } +.codicon-screen-full:before { content: "\eb4c" } +.codicon-screen-normal:before { content: "\eb4d" } +.codicon-search-stop:before { content: "\eb4e" } +.codicon-server:before { content: "\eb50" } +.codicon-settings-gear:before { content: "\eb51" } +.codicon-settings:before { content: "\eb52" } +.codicon-shield:before { content: "\eb53" } +.codicon-smiley:before { content: "\eb54" } +.codicon-sort-precedence:before { content: "\eb55" } +.codicon-split-horizontal:before { content: "\eb56" } +.codicon-split-vertical:before { content: "\eb57" } +.codicon-squirrel:before { content: "\eb58" } +.codicon-star-full:before { content: "\eb59" } +.codicon-star-half:before { content: "\eb5a" } +.codicon-symbol-class:before { content: "\eb5b" } +.codicon-symbol-color:before { content: "\eb5c" } +.codicon-symbol-constant:before { content: "\eb5d" } +.codicon-symbol-enum-member:before { content: "\eb5e" } +.codicon-symbol-field:before { content: "\eb5f" } +.codicon-symbol-file:before { content: "\eb60" } +.codicon-symbol-interface:before { content: "\eb61" } +.codicon-symbol-keyword:before { content: "\eb62" } +.codicon-symbol-misc:before { content: "\eb63" } +.codicon-symbol-operator:before { content: "\eb64" } +.codicon-symbol-property:before { content: "\eb65" } +.codicon-wrench:before { content: "\eb65" } +.codicon-wrench-subaction:before { content: "\eb65" } +.codicon-symbol-snippet:before { content: "\eb66" } +.codicon-tasklist:before { content: "\eb67" } +.codicon-telescope:before { content: "\eb68" } +.codicon-text-size:before { content: "\eb69" } +.codicon-three-bars:before { content: "\eb6a" } +.codicon-thumbsdown:before { content: "\eb6b" } +.codicon-thumbsup:before { content: "\eb6c" } +.codicon-tools:before { content: "\eb6d" } +.codicon-triangle-down:before { content: "\eb6e" } +.codicon-triangle-left:before { content: "\eb6f" } +.codicon-triangle-right:before { content: "\eb70" } +.codicon-triangle-up:before { content: "\eb71" } +.codicon-twitter:before { content: "\eb72" } +.codicon-unfold:before { content: "\eb73" } +.codicon-unlock:before { content: "\eb74" } +.codicon-unmute:before { content: "\eb75" } +.codicon-unverified:before { content: "\eb76" } +.codicon-verified:before { content: "\eb77" } +.codicon-versions:before { content: "\eb78" } +.codicon-vm-active:before { content: "\eb79" } +.codicon-vm-outline:before { content: "\eb7a" } +.codicon-vm-running:before { content: "\eb7b" } +.codicon-watch:before { content: "\eb7c" } +.codicon-whitespace:before { content: "\eb7d" } +.codicon-whole-word:before { content: "\eb7e" } +.codicon-window:before { content: "\eb7f" } +.codicon-word-wrap:before { content: "\eb80" } +.codicon-zoom-in:before { content: "\eb81" } +.codicon-zoom-out:before { content: "\eb82" } +.codicon-list-filter:before { content: "\eb83" } +.codicon-list-flat:before { content: "\eb84" } +.codicon-list-selection:before { content: "\eb85" } +.codicon-selection:before { content: "\eb85" } +.codicon-list-tree:before { content: "\eb86" } +.codicon-debug-breakpoint-function-unverified:before { content: "\eb87" } +.codicon-debug-breakpoint-function:before { content: "\eb88" } +.codicon-debug-breakpoint-function-disabled:before { content: "\eb88" } +.codicon-debug-stackframe-active:before { content: "\eb89" } +.codicon-circle-small-filled:before { content: "\eb8a" } +.codicon-debug-stackframe-dot:before { content: "\eb8a" } +.codicon-terminal-decoration-mark:before { content: "\eb8a" } +.codicon-debug-stackframe:before { content: "\eb8b" } +.codicon-debug-stackframe-focused:before { content: "\eb8b" } +.codicon-debug-breakpoint-unsupported:before { content: "\eb8c" } +.codicon-symbol-string:before { content: "\eb8d" } +.codicon-debug-reverse-continue:before { content: "\eb8e" } +.codicon-debug-step-back:before { content: "\eb8f" } +.codicon-debug-restart-frame:before { content: "\eb90" } +.codicon-debug-alt:before { content: "\eb91" } +.codicon-call-incoming:before { content: "\eb92" } +.codicon-call-outgoing:before { content: "\eb93" } +.codicon-menu:before { content: "\eb94" } +.codicon-expand-all:before { content: "\eb95" } +.codicon-feedback:before { content: "\eb96" } +.codicon-git-pull-request-reviewer:before { content: "\eb96" } +.codicon-group-by-ref-type:before { content: "\eb97" } +.codicon-ungroup-by-ref-type:before { content: "\eb98" } +.codicon-account:before { content: "\eb99" } +.codicon-git-pull-request-assignee:before { content: "\eb99" } +.codicon-bell-dot:before { content: "\eb9a" } +.codicon-debug-console:before { content: "\eb9b" } +.codicon-library:before { content: "\eb9c" } +.codicon-output:before { content: "\eb9d" } +.codicon-run-all:before { content: "\eb9e" } +.codicon-sync-ignored:before { content: "\eb9f" } +.codicon-pinned:before { content: "\eba0" } +.codicon-github-inverted:before { content: "\eba1" } +.codicon-server-process:before { content: "\eba2" } +.codicon-server-environment:before { content: "\eba3" } +.codicon-pass:before { content: "\eba4" } +.codicon-issue-closed:before { content: "\eba4" } +.codicon-stop-circle:before { content: "\eba5" } +.codicon-play-circle:before { content: "\eba6" } +.codicon-record:before { content: "\eba7" } +.codicon-debug-alt-small:before { content: "\eba8" } +.codicon-vm-connect:before { content: "\eba9" } +.codicon-cloud:before { content: "\ebaa" } +.codicon-merge:before { content: "\ebab" } +.codicon-export:before { content: "\ebac" } +.codicon-graph-left:before { content: "\ebad" } +.codicon-magnet:before { content: "\ebae" } +.codicon-notebook:before { content: "\ebaf" } +.codicon-redo:before { content: "\ebb0" } +.codicon-check-all:before { content: "\ebb1" } +.codicon-pinned-dirty:before { content: "\ebb2" } +.codicon-pass-filled:before { content: "\ebb3" } +.codicon-circle-large-filled:before { content: "\ebb4" } +.codicon-circle-large:before { content: "\ebb5" } +.codicon-circle-large-outline:before { content: "\ebb5" } +.codicon-combine:before { content: "\ebb6" } +.codicon-gather:before { content: "\ebb6" } +.codicon-table:before { content: "\ebb7" } +.codicon-variable-group:before { content: "\ebb8" } +.codicon-type-hierarchy:before { content: "\ebb9" } +.codicon-type-hierarchy-sub:before { content: "\ebba" } +.codicon-type-hierarchy-super:before { content: "\ebbb" } +.codicon-git-pull-request-create:before { content: "\ebbc" } +.codicon-run-above:before { content: "\ebbd" } +.codicon-run-below:before { content: "\ebbe" } +.codicon-notebook-template:before { content: "\ebbf" } +.codicon-debug-rerun:before { content: "\ebc0" } +.codicon-workspace-trusted:before { content: "\ebc1" } +.codicon-workspace-untrusted:before { content: "\ebc2" } +.codicon-workspace-unknown:before { content: "\ebc3" } +.codicon-terminal-cmd:before { content: "\ebc4" } +.codicon-terminal-debian:before { content: "\ebc5" } +.codicon-terminal-linux:before { content: "\ebc6" } +.codicon-terminal-powershell:before { content: "\ebc7" } +.codicon-terminal-tmux:before { content: "\ebc8" } +.codicon-terminal-ubuntu:before { content: "\ebc9" } +.codicon-terminal-bash:before { content: "\ebca" } +.codicon-arrow-swap:before { content: "\ebcb" } +.codicon-copy:before { content: "\ebcc" } +.codicon-person-add:before { content: "\ebcd" } +.codicon-filter-filled:before { content: "\ebce" } +.codicon-wand:before { content: "\ebcf" } +.codicon-debug-line-by-line:before { content: "\ebd0" } +.codicon-inspect:before { content: "\ebd1" } +.codicon-layers:before { content: "\ebd2" } +.codicon-layers-dot:before { content: "\ebd3" } +.codicon-layers-active:before { content: "\ebd4" } +.codicon-compass:before { content: "\ebd5" } +.codicon-compass-dot:before { content: "\ebd6" } +.codicon-compass-active:before { content: "\ebd7" } +.codicon-azure:before { content: "\ebd8" } +.codicon-issue-draft:before { content: "\ebd9" } +.codicon-git-pull-request-closed:before { content: "\ebda" } +.codicon-git-pull-request-draft:before { content: "\ebdb" } +.codicon-debug-all:before { content: "\ebdc" } +.codicon-debug-coverage:before { content: "\ebdd" } +.codicon-run-errors:before { content: "\ebde" } +.codicon-folder-library:before { content: "\ebdf" } +.codicon-debug-continue-small:before { content: "\ebe0" } +.codicon-beaker-stop:before { content: "\ebe1" } +.codicon-graph-line:before { content: "\ebe2" } +.codicon-graph-scatter:before { content: "\ebe3" } +.codicon-pie-chart:before { content: "\ebe4" } +.codicon-bracket-dot:before { content: "\ebe5" } +.codicon-bracket-error:before { content: "\ebe6" } +.codicon-lock-small:before { content: "\ebe7" } +.codicon-azure-devops:before { content: "\ebe8" } +.codicon-verified-filled:before { content: "\ebe9" } +.codicon-newline:before { content: "\ebea" } +.codicon-layout:before { content: "\ebeb" } +.codicon-layout-activitybar-left:before { content: "\ebec" } +.codicon-layout-activitybar-right:before { content: "\ebed" } +.codicon-layout-panel-left:before { content: "\ebee" } +.codicon-layout-panel-center:before { content: "\ebef" } +.codicon-layout-panel-justify:before { content: "\ebf0" } +.codicon-layout-panel-right:before { content: "\ebf1" } +.codicon-layout-panel:before { content: "\ebf2" } +.codicon-layout-sidebar-left:before { content: "\ebf3" } +.codicon-layout-sidebar-right:before { content: "\ebf4" } +.codicon-layout-statusbar:before { content: "\ebf5" } +.codicon-layout-menubar:before { content: "\ebf6" } +.codicon-layout-centered:before { content: "\ebf7" } +.codicon-target:before { content: "\ebf8" } +.codicon-indent:before { content: "\ebf9" } +.codicon-record-small:before { content: "\ebfa" } +.codicon-error-small:before { content: "\ebfb" } +.codicon-terminal-decoration-error:before { content: "\ebfb" } +.codicon-arrow-circle-down:before { content: "\ebfc" } +.codicon-arrow-circle-left:before { content: "\ebfd" } +.codicon-arrow-circle-right:before { content: "\ebfe" } +.codicon-arrow-circle-up:before { content: "\ebff" } +.codicon-layout-sidebar-right-off:before { content: "\ec00" } +.codicon-layout-panel-off:before { content: "\ec01" } +.codicon-layout-sidebar-left-off:before { content: "\ec02" } +.codicon-blank:before { content: "\ec03" } +.codicon-heart-filled:before { content: "\ec04" } +.codicon-map:before { content: "\ec05" } +.codicon-map-horizontal:before { content: "\ec05" } +.codicon-fold-horizontal:before { content: "\ec05" } +.codicon-map-filled:before { content: "\ec06" } +.codicon-map-horizontal-filled:before { content: "\ec06" } +.codicon-fold-horizontal-filled:before { content: "\ec06" } +.codicon-circle-small:before { content: "\ec07" } +.codicon-bell-slash:before { content: "\ec08" } +.codicon-bell-slash-dot:before { content: "\ec09" } +.codicon-comment-unresolved:before { content: "\ec0a" } +.codicon-git-pull-request-go-to-changes:before { content: "\ec0b" } +.codicon-git-pull-request-new-changes:before { content: "\ec0c" } +.codicon-search-fuzzy:before { content: "\ec0d" } +.codicon-comment-draft:before { content: "\ec0e" } +.codicon-send:before { content: "\ec0f" } +.codicon-sparkle:before { content: "\ec10" } +.codicon-insert:before { content: "\ec11" } +.codicon-mic:before { content: "\ec12" } +.codicon-thumbsdown-filled:before { content: "\ec13" } +.codicon-thumbsup-filled:before { content: "\ec14" } +.codicon-coffee:before { content: "\ec15" } +.codicon-snake:before { content: "\ec16" } +.codicon-game:before { content: "\ec17" } +.codicon-vr:before { content: "\ec18" } +.codicon-chip:before { content: "\ec19" } +.codicon-piano:before { content: "\ec1a" } +.codicon-music:before { content: "\ec1b" } +.codicon-mic-filled:before { content: "\ec1c" } +.codicon-repo-fetch:before { content: "\ec1d" } +.codicon-copilot:before { content: "\ec1e" } +.codicon-lightbulb-sparkle:before { content: "\ec1f" } +.codicon-robot:before { content: "\ec20" } +.codicon-sparkle-filled:before { content: "\ec21" } +.codicon-diff-single:before { content: "\ec22" } +.codicon-diff-multiple:before { content: "\ec23" } +.codicon-surround-with:before { content: "\ec24" } +.codicon-share:before { content: "\ec25" } +.codicon-git-stash:before { content: "\ec26" } +.codicon-git-stash-apply:before { content: "\ec27" } +.codicon-git-stash-pop:before { content: "\ec28" } +.codicon-vscode:before { content: "\ec29" } +.codicon-vscode-insiders:before { content: "\ec2a" } +.codicon-code-oss:before { content: "\ec2b" } +.codicon-run-coverage:before { content: "\ec2c" } +.codicon-run-all-coverage:before { content: "\ec2d" } +.codicon-coverage:before { content: "\ec2e" } +.codicon-github-project:before { content: "\ec2f" } +.codicon-map-vertical:before { content: "\ec30" } +.codicon-fold-vertical:before { content: "\ec30" } +.codicon-map-vertical-filled:before { content: "\ec31" } +.codicon-fold-vertical-filled:before { content: "\ec31" } +.codicon-go-to-search:before { content: "\ec32" } +.codicon-percentage:before { content: "\ec33" } +.codicon-sort-percentage:before { content: "\ec33" } +.codicon-attach:before { content: "\ec34" } +.codicon-go-to-editing-session:before { content: "\ec35" } +.codicon-edit-session:before { content: "\ec36" } +.codicon-code-review:before { content: "\ec37" } +.codicon-copilot-warning:before { content: "\ec38" } +.codicon-python:before { content: "\ec39" } +.codicon-copilot-large:before { content: "\ec3a" } +.codicon-copilot-warning-large:before { content: "\ec3b" } +.codicon-keyboard-tab:before { content: "\ec3c" } +.codicon-copilot-blocked:before { content: "\ec3d" } +.codicon-copilot-not-connected:before { content: "\ec3e" } +.codicon-flag:before { content: "\ec3f" } +.codicon-lightbulb-empty:before { content: "\ec40" } +.codicon-symbol-method-arrow:before { content: "\ec41" } +.codicon-copilot-unavailable:before { content: "\ec42" } +.codicon-repo-pinned:before { content: "\ec43" } +.codicon-keyboard-tab-above:before { content: "\ec44" } +.codicon-keyboard-tab-below:before { content: "\ec45" } +.codicon-git-pull-request-done:before { content: "\ec46" } +.codicon-mcp:before { content: "\ec47" } +.codicon-extensions-large:before { content: "\ec48" } +.codicon-layout-panel-dock:before { content: "\ec49" } +.codicon-layout-sidebar-left-dock:before { content: "\ec4a" } +.codicon-layout-sidebar-right-dock:before { content: "\ec4b" } +.codicon-copilot-in-progress:before { content: "\ec4c" } +.codicon-copilot-error:before { content: "\ec4d" } +.codicon-copilot-success:before { content: "\ec4e" } +.codicon-chat-sparkle:before { content: "\ec4f" } +.codicon-search-sparkle:before { content: "\ec50" } +.codicon-edit-sparkle:before { content: "\ec51" } +.codicon-copilot-snooze:before { content: "\ec52" } +.codicon-send-to-remote-agent:before { content: "\ec53" } +.codicon-comment-discussion-sparkle:before { content: "\ec54" } +.codicon-chat-sparkle-warning:before { content: "\ec55" } +.codicon-chat-sparkle-error:before { content: "\ec56" } +.codicon-collection:before { content: "\ec57" } +.codicon-new-collection:before { content: "\ec58" } +.codicon-thinking:before { content: "\ec59" } +.codicon-build:before { content: "\ec5a" } +.codicon-comment-discussion-quote:before { content: "\ec5b" } +.codicon-cursor:before { content: "\ec5c" } +.codicon-eraser:before { content: "\ec5d" } +.codicon-file-text:before { content: "\ec5e" } +.codicon-quotes:before { content: "\ec60" } +.codicon-rename:before { content: "\ec61" } +.codicon-run-with-deps:before { content: "\ec62" } +.codicon-debug-connected:before { content: "\ec63" } +.codicon-strikethrough:before { content: "\ec64" } +.codicon-open-in-product:before { content: "\ec65" } +.codicon-index-zero:before { content: "\ec66" } +.codicon-agent:before { content: "\ec67" } +.codicon-edit-code:before { content: "\ec68" } +.codicon-repo-selected:before { content: "\ec69" } +.codicon-skip:before { content: "\ec6a" } +.codicon-merge-into:before { content: "\ec6b" } +.codicon-git-branch-changes:before { content: "\ec6c" } +.codicon-git-branch-staged-changes:before { content: "\ec6d" } +.codicon-git-branch-conflicts:before { content: "\ec6e" } +.codicon-git-branch:before { content: "\ec6f" } +.codicon-git-branch-create:before { content: "\ec6f" } +.codicon-git-branch-delete:before { content: "\ec6f" } +.codicon-search-large:before { content: "\ec70" } +.codicon-terminal-git-bash:before { content: "\ec71" } +.codicon-window-active:before { content: "\ec72" } +.codicon-forward:before { content: "\ec73" } +.codicon-download:before { content: "\ec74" } +.codicon-clockface:before { content: "\ec75" } +.codicon-unarchive:before { content: "\ec76" } +.codicon-session-in-progress:before { content: "\ec77" } +.codicon-collection-small:before { content: "\ec78" } +.codicon-vm-small:before { content: "\ec79" } +.codicon-cloud-small:before { content: "\ec7a" } +.codicon-add-small:before { content: "\ec7b" } +.codicon-remove-small:before { content: "\ec7c" } +.codicon-worktree-small:before { content: "\ec7d" } +.codicon-worktree:before { content: "\ec7e" } +.codicon-screen-cut:before { content: "\ec7f" } +.codicon-ask:before { content: "\ec80" } +.codicon-openai:before { content: "\ec81" } +.codicon-claude:before { content: "\ec82" } +.codicon-open-in-window:before { content: "\ec83" } +.codicon-new-session:before { content: "\ec84" } +.codicon-terminal-secure:before { content: "\ec85" } +.codicon-chat-import:before { content: "\ec86" } +.codicon-chat-export:before { content: "\ec87" } +.codicon-share-window:before { content: "\ec88" } +.codicon-circle-slash-compact:before { content: "\ec89" } +.codicon-copilot-compact:before { content: "\ec8a" } +.codicon-folder-opened-compact:before { content: "\ec8b" } +.codicon-folder-compact:before { content: "\ec8c" } +.codicon-gear-compact:before { content: "\ec8d" } +.codicon-git-branch-compact:before { content: "\ec8e" } +.codicon-library-compact:before { content: "\ec8f" } +.codicon-record-keys-compact:before { content: "\ec90" } +.codicon-remote-compact:before { content: "\ec91" } +.codicon-repo-forked-compact:before { content: "\ec92" } +.codicon-repo-compact:before { content: "\ec93" } +.codicon-shield-compact:before { content: "\ec94" } +.codicon-sparkle-compact:before { content: "\ec95" } +.codicon-symbol-color-compact:before { content: "\ec96" } +.codicon-window-compact:before { content: "\ec97" } +.codicon-error-compact:before { content: "\ec98" } +.codicon-warning-compact:before { content: "\ec99" } +.codicon-pass-compact:before { content: "\ec9a" } +.codicon-important:before { content: "\ec9b" } +.codicon-important-compact:before { content: "\ec9c" } +.codicon-rocket-compact:before { content: "\ec9d" } +.codicon-unpin:before { content: "\ec9e" } +.codicon-add-compact:before { content: "\ec9f" } +.codicon-attach-compact:before { content: "\eca0" } +.codicon-beaker-compact:before { content: "\eca1" } +.codicon-check-compact:before { content: "\eca2" } +.codicon-checklist-compact:before { content: "\eca3" } +.codicon-chevron-down-compact:before { content: "\eca4" } +.codicon-chevron-left-compact:before { content: "\eca5" } +.codicon-chevron-right-compact:before { content: "\eca6" } +.codicon-chevron-up-compact:before { content: "\eca7" } +.codicon-circle-filled-compact:before { content: "\eca8" } +.codicon-circle-small-filled-compact:before { content: "\eca9" } +.codicon-close-compact:before { content: "\ecaa" } +.codicon-collapse-all-compact:before { content: "\ecab" } +.codicon-comment-compact:before { content: "\ecac" } +.codicon-comment-unresolved-compact:before { content: "\ecad" } +.codicon-debug-connected-compact:before { content: "\ecae" } +.codicon-debug-disconnect-compact:before { content: "\ecaf" } +.codicon-edit-compact:before { content: "\ecb0" } +.codicon-file-media-compact:before { content: "\ecb1" } +.codicon-git-fetch:before { content: "\ecb2" } +.codicon-lightbulb-compact:before { content: "\ecb3" } +.codicon-loading-compact:before { content: "\ecb4" } +.codicon-pass-filled-compact:before { content: "\ecb5" } +.codicon-project-compact:before { content: "\ecb6" } +.codicon-refresh-compact:before { content: "\ecb7" } +.codicon-search-compact:before { content: "\ecb8" } +.codicon-session-in-progress-compact:before { content: "\ecb9" } +.codicon-sync-compact:before { content: "\ecba" } +.codicon-terminal-compact:before { content: "\ecbb" } +.codicon-vm-pending:before { content: "\ecbc" } +.codicon-worktree-compact:before { content: "\ecbd" } +.codicon-developer-tools:before { content: "\ecbe" } +.codicon-cloud-compact:before { content: "\ecbf" } +.codicon-agent-compact:before { content: "\ecc0" } +.codicon-ask-compact:before { content: "\ecc1" } +.codicon-settings-compact:before { content: "\ecc2" } +.codicon-vm-compact:before { content: "\ecc3" } +.codicon-run-compact:before { content: "\ecc4" } +.codicon-git-pull-request-comment:before { content: "\ecc5" } +.codicon-git-pull-request-error:before { content: "\ecc6" } +.codicon-right-panel-hide:before { content: "\ecc7" } +.codicon-right-panel-show:before { content: "\ecc8" } +.codicon-vscode-insiders-outline:before { content: "\ecc9" } +.codicon-vscode-outline:before { content: "\ecca" } +.codicon-voice-mode:before { content: "\eccb" } +.codicon-voice-mode-compact:before { content: "\eccc" } +.codicon-mic-download:before { content: "\eccd" } +.codicon-mic-download-compact:before { content: "\ecce" } +.codicon-voice-mode-download:before { content: "\eccf" } +.codicon-voice-mode-download-compact:before { content: "\ecd0" } +.codicon-google-gemini:before { content: "\ecd1" } +.codicon-kimi:before { content: "\ecd2" } +.codicon-microsoft:before { content: "\ecd3" } +.codicon-fish1-happy:before { content: "\ecd4" } +.codicon-fish1-neutral:before { content: "\ecd5" } +.codicon-fish1-sad:before { content: "\ecd6" } +.codicon-fish1-very-sad:before { content: "\ecd7" } +.codicon-fish2-happy:before { content: "\ecd8" } +.codicon-fish2-neutral:before { content: "\ecd9" } +.codicon-fish2-sad:before { content: "\ecda" } +.codicon-fish2-very-sad:before { content: "\ecdb" } +.codicon-fish3-happy:before { content: "\ecdc" } +.codicon-fish3-neutral:before { content: "\ecdd" } +.codicon-fish3-sad:before { content: "\ecde" } +.codicon-fish3-very-sad:before { content: "\ecdf" } +.codicon-fish4-happy:before { content: "\ece0" } +.codicon-fish4-neutral:before { content: "\ece1" } +.codicon-fish4-sad:before { content: "\ece2" } +.codicon-fish4-very-sad:before { content: "\ece3" } +.codicon-person-voice:before { content: "\ece4" } +.codicon-person-voice-compact:before { content: "\ece5" } +.codicon-person-voice-filled:before { content: "\ece6" } +.codicon-person-voice-filled-compact:before { content: "\ece7" } diff --git a/media/codicon/codicon.ttf b/media/codicon/codicon.ttf new file mode 100644 index 0000000..77b19ba Binary files /dev/null and b/media/codicon/codicon.ttf differ diff --git a/media/composer.js b/media/composer.js index c3e15ea..8189a69 100644 --- a/media/composer.js +++ b/media/composer.js @@ -15,8 +15,7 @@ function renderAttachmentsPreview() { const img = document.createElement("img"); img.src = dataUrl; const remove = document.createElement("button"); - remove.className = "remove"; - remove.textContent = "✕"; + remove.className = "remove codicon codicon-close"; remove.title = "Remove"; remove.addEventListener("click", () => { pendingImages.splice(index, 1); diff --git a/media/main.css b/media/main.css index fc477b1..1295eee 100644 --- a/media/main.css +++ b/media/main.css @@ -365,16 +365,20 @@ body { cursor: pointer; font-family: var(--vscode-editor-font-family); background: var(--vscode-editorWidget-background); + overflow: hidden; } .tool-card-header:hover { background: var(--vscode-list-hoverBackground); } -.tool-icon { +.tool-card-header .tool-icon { width: 14px; + font-size: 14px; + line-height: 1; text-align: center; flex-shrink: 0; + color: var(--vscode-descriptionForeground); } .tool-name { @@ -393,28 +397,48 @@ body { min-width: 0; } -.tool-status { +.tool-card-header .tool-open-file { + flex-shrink: 0; + border: none; + background: transparent; + color: var(--vscode-descriptionForeground); + font-size: 13px; + line-height: 1; + padding: 0 2px; + cursor: pointer; +} + +.tool-card-header .tool-open-file:hover { + color: var(--vscode-foreground); +} + +.tool-card-header .tool-status { + display: inline-flex; + align-items: center; margin-left: auto; - font-size: 0.9em; flex-shrink: 0; } -.tool-status.pending { +.tool-card-header .tool-status-icon { + font-size: 14px; + line-height: 1; +} + +.tool-status.pending .tool-status-icon { color: var(--vscode-focusBorder); - animation: thinking-pulse 1.1s ease-in-out infinite; } @media (prefers-reduced-motion: reduce) { - .tool-status.pending { + .tool-status-icon.codicon-modifier-spin { animation: none; } } -.tool-status.ok { +.tool-status.ok .tool-status-icon { color: var(--vscode-charts-green, var(--vscode-testing-iconPassed, #2ea043)); } -.tool-status.error { +.tool-status.error .tool-status-icon { color: var(--vscode-errorForeground); } @@ -862,7 +886,7 @@ body { height: 16px; line-height: 16px; text-align: center; - font-size: 11px; + font-size: 12px; padding: 0; background: rgba(0, 0, 0, 0.6); color: #fff; diff --git a/package-lock.json b/package-lock.json index 49800e7..a991051 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,18 @@ { "name": "pycodeloop", - "version": "0.3.5", + "version": "0.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pycodeloop", - "version": "0.3.5", + "version": "0.4.0", "license": "MIT", "devDependencies": { "@eslint/js": "^9.39.5", "@types/node": "^20.11.0", "@types/vscode": "^1.85.0", + "@vscode/codicons": "^0.0.46-24", "eslint": "^9.39.5", "globals": "^15.15.0", "typescript": "^5.4.0", @@ -558,6 +559,13 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@vscode/codicons": { + "version": "0.0.46-24", + "resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.46-24.tgz", + "integrity": "sha512-KIDWUYxG6eh+DHRg+3G4bS487T+g1ZkLfB4HylOBbCcHsvLdfLMRM/m92DS1Ljhi8nglLr9ucwwWhHTEAC+DpA==", + "dev": true, + "license": "CC-BY-4.0" + }, "node_modules/acorn": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", diff --git a/package.json b/package.json index 471c7eb..1b1c00d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "pycodeloop", "displayName": "CodeLoop — AI Coding Agent", "description": "Agentic AI coding assistant in your VS Code sidebar — chat with Anthropic Claude, OpenAI, Gemini, Grok, Groq, AWS Bedrock, Moonshot Kimi, DeepSeek, Together AI Llama, Alibaba Qwen, NVIDIA NIM, or a local Ollama/LM Studio model to read, edit, and run code in your workspace.", - "version": "0.3.5", + "version": "0.4.0", "publisher": "fernandocelmer", "repository": { "type": "git", @@ -196,6 +196,7 @@ "@eslint/js": "^9.39.5", "@types/node": "^20.11.0", "@types/vscode": "^1.85.0", + "@vscode/codicons": "^0.0.46-24", "eslint": "^9.39.5", "globals": "^15.15.0", "typescript": "^5.4.0", diff --git a/src/vscode/sidebar/chatSidebarProvider.ts b/src/vscode/sidebar/chatSidebarProvider.ts index a1c6f4d..124047a 100644 --- a/src/vscode/sidebar/chatSidebarProvider.ts +++ b/src/vscode/sidebar/chatSidebarProvider.ts @@ -1,5 +1,6 @@ import * as vscode from "vscode"; import * as crypto from "crypto"; +import * as path from "path"; import { resolveWorkspacePath } from "../../services/workspace.service"; import { currentWorkspaceFolder } from "../../services/settings.service"; import { ChatController } from "../../features/chat/chat.controller"; @@ -149,7 +150,26 @@ export class ChatViewProvider implements vscode.WebviewViewProvider { case "openSettings": vscode.commands.executeCommand("workbench.action.openSettings", "pycodeloop"); break; + case "openFile": + if (typeof message.path === "string" && message.path.length > 0) { + this.openFile(message.path); + } + break; + } + } + + private openFile(filePath: string): void { + const root = currentWorkspaceFolder(); + const resolved = path.isAbsolute(filePath) ? filePath : path.join(root, filePath); + const normalized = path.normalize(resolved); + if (!normalized.startsWith(path.normalize(root) + path.sep)) { + vscode.window.showErrorMessage(`Path outside workspace: ${filePath}`); + return; } + const uri = vscode.Uri.file(normalized); + vscode.window.showTextDocument(uri).then(undefined, () => { + vscode.window.showErrorMessage(`Couldn't open ${filePath}`); + }); } private post(message: { type: string } & Record): void { @@ -173,11 +193,15 @@ export class ChatViewProvider implements vscode.WebviewViewProvider { webview.asWebviewUri(vscode.Uri.joinPath(this.context.extensionUri, "media", file)).toString() ); const styleUri = webview.asWebviewUri(vscode.Uri.joinPath(this.context.extensionUri, "media", "main.css")); + const codiconUri = webview.asWebviewUri( + vscode.Uri.joinPath(this.context.extensionUri, "media", "codicon", "codicon.css") + ); return renderChatHtml({ cspSource: webview.cspSource, scriptUris, styleUri: styleUri.toString(), + codiconUri: codiconUri.toString(), nonce: crypto.randomBytes(16).toString("hex"), }); } diff --git a/src/vscode/webview/html.ts b/src/vscode/webview/html.ts index 9638fc8..38f6983 100644 --- a/src/vscode/webview/html.ts +++ b/src/vscode/webview/html.ts @@ -2,11 +2,12 @@ export interface ChatHtmlOptions { cspSource: string; scriptUris: string[]; styleUri: string; + codiconUri: string; nonce: string; } export function renderChatHtml(options: ChatHtmlOptions): string { - const { cspSource, scriptUris, styleUri, nonce } = options; + const { cspSource, scriptUris, styleUri, codiconUri, nonce } = options; const scriptTags = scriptUris .map((uri) => ` `) .join("\n"); @@ -15,8 +16,9 @@ export function renderChatHtml(options: ChatHtmlOptions): string { - + + CodeLoop diff --git a/src/vscode/webview/messages.ts b/src/vscode/webview/messages.ts index 2a0508b..498bed0 100644 --- a/src/vscode/webview/messages.ts +++ b/src/vscode/webview/messages.ts @@ -22,4 +22,5 @@ export type WebviewMessage = | { type: "reload" } | { type: "installCli" } | { type: "updateCli" } - | { type: "openSettings" }; + | { type: "openSettings" } + | { type: "openFile"; path: string }; diff --git a/test/html.test.ts b/test/html.test.ts index cc61020..71a4fed 100644 --- a/test/html.test.ts +++ b/test/html.test.ts @@ -6,6 +6,7 @@ const OPTIONS = { cspSource: "vscode-webview://abc", scriptUris: ["vscode-webview://abc/dom.js", "vscode-webview://abc/app.js"], styleUri: "vscode-webview://abc/main.css", + codiconUri: "vscode-webview://abc/codicon/codicon.css", nonce: "test-nonce", }; @@ -23,10 +24,16 @@ test("keeps script tags in the given order so shared globals are defined before assert.ok(html.indexOf("dom.js") < html.indexOf("app.js")); }); -test("CSP restricts style-src and img-src to the given cspSource", () => { +test("CSP restricts style-src, img-src, and font-src to the given cspSource", () => { const html = renderChatHtml(OPTIONS); assert.ok(html.includes("style-src vscode-webview://abc;")); assert.ok(html.includes("img-src vscode-webview://abc data:;")); + assert.ok(html.includes("font-src vscode-webview://abc;")); +}); + +test("wires the codicon stylesheet", () => { + const html = renderChatHtml(OPTIONS); + assert.ok(html.includes('href="vscode-webview://abc/codicon/codicon.css"')); }); test("includes the elements main.js expects to find by id", () => {