summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-05 13:55:49 -0600
committerGitHub <[email protected]>2026-02-05 19:55:49 +0000
commitb738d88ec4c49efdf37ecf09058e70f1c3574b6b (patch)
tree524219619c6e5ce0159679073183e2c3930c4ce6
parent83646e0366c47a3bccb5135d40628176a6776f33 (diff)
downloadopencode-b738d88ec4c49efdf37ecf09058e70f1c3574b6b.tar.gz
opencode-b738d88ec4c49efdf37ecf09058e70f1c3574b6b.zip
feat(app): open in <app> button (#12322)
-rw-r--r--packages/app/src/components/session/session-header.tsx187
-rw-r--r--packages/app/src/context/platform.tsx3
-rw-r--r--packages/app/src/i18n/en.ts5
-rw-r--r--packages/desktop/src-tauri/capabilities/default.json13
-rw-r--r--packages/desktop/src/bindings.ts23
-rw-r--r--packages/desktop/src/index.tsx5
-rw-r--r--packages/ui/package.json1
-rw-r--r--packages/ui/src/assets/icons/app/android-studio.svg1
-rw-r--r--packages/ui/src/assets/icons/app/antigravity.svg1
-rw-r--r--packages/ui/src/assets/icons/app/cursor.svg1
-rw-r--r--packages/ui/src/assets/icons/app/finder.pngbin0 -> 525196 bytes
-rw-r--r--packages/ui/src/assets/icons/app/ghostty.svg1
-rw-r--r--packages/ui/src/assets/icons/app/iterm2.svg25
-rw-r--r--packages/ui/src/assets/icons/app/powershell.svg1
-rw-r--r--packages/ui/src/assets/icons/app/terminal.pngbin0 -> 43815 bytes
-rw-r--r--packages/ui/src/assets/icons/app/textmate.pngbin0 -> 104374 bytes
-rw-r--r--packages/ui/src/assets/icons/app/vscode.svg1
-rw-r--r--packages/ui/src/assets/icons/app/xcode.pngbin0 -> 866979 bytes
-rw-r--r--packages/ui/src/assets/icons/app/zed.svg1
-rw-r--r--packages/ui/src/components/app-icon.css9
-rw-r--r--packages/ui/src/components/app-icon.tsx52
-rw-r--r--packages/ui/src/components/app-icons/sprite.svg114
-rw-r--r--packages/ui/src/components/app-icons/types.ts18
-rw-r--r--packages/ui/src/styles/index.css1
24 files changed, 452 insertions, 11 deletions
diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx
index f2bfc8d25..43057d63b 100644
--- a/packages/app/src/components/session/session-header.tsx
+++ b/packages/app/src/components/session/session-header.tsx
@@ -6,18 +6,23 @@ import { useLayout } from "@/context/layout"
import { useCommand } from "@/context/command"
import { useLanguage } from "@/context/language"
import { usePlatform } from "@/context/platform"
+import { useServer } from "@/context/server"
import { useSync } from "@/context/sync"
import { useGlobalSDK } from "@/context/global-sdk"
import { getFilename } from "@opencode-ai/util/path"
import { decode64 } from "@/utils/base64"
+import { Persist, persisted } from "@/utils/persist"
import { Icon } from "@opencode-ai/ui/icon"
import { IconButton } from "@opencode-ai/ui/icon-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 { Popover } from "@opencode-ai/ui/popover"
import { TextField } from "@opencode-ai/ui/text-field"
import { Keybind } from "@opencode-ai/ui/keybind"
+import { showToast } from "@opencode-ai/ui/toast"
import { StatusPopover } from "../status-popover"
export function SessionHeader() {
@@ -25,6 +30,7 @@ export function SessionHeader() {
const layout = useLayout()
const params = useParams()
const command = useCommand()
+ const server = useServer()
const sync = useSync()
const platform = usePlatform()
const language = useLanguage()
@@ -48,6 +54,117 @@ export function SessionHeader() {
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
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({
share: false,
unshare: false,
@@ -150,6 +267,76 @@ export function SessionHeader() {
{(mount) => (
<Portal mount={mount()}>
<div class="flex items-center gap-3">
+ <Show when={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 />
<Show when={showShare()}>
<div class="flex items-center">
diff --git a/packages/app/src/context/platform.tsx b/packages/app/src/context/platform.tsx
index 591bd9c9f..f5d20ff8e 100644
--- a/packages/app/src/context/platform.tsx
+++ b/packages/app/src/context/platform.tsx
@@ -15,6 +15,9 @@ export type Platform = {
/** Open a URL in the default browser */
openLink(url: string): void
+ /** Open a local path in a local app (desktop only) */
+ openPath?(path: string, app?: string): Promise<void>
+
/** Restart the app */
restart(): Promise<void>
diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts
index 7c4d3a44a..32c4695db 100644
--- a/packages/app/src/i18n/en.ts
+++ b/packages/app/src/i18n/en.ts
@@ -470,6 +470,11 @@ export const dict = {
"session.header.search.placeholder": "Search {{project}}",
"session.header.searchFiles": "Search files",
+ "session.header.openIn": "Open in",
+ "session.header.open.action": "Open {{app}}",
+ "session.header.open.ariaLabel": "Open in {{app}}",
+ "session.header.open.menu": "Open options",
+ "session.header.open.copyPath": "Copy Path",
"status.popover.trigger": "Status",
"status.popover.ariaLabel": "Server configurations",
diff --git a/packages/desktop/src-tauri/capabilities/default.json b/packages/desktop/src-tauri/capabilities/default.json
index 66f068af8..e895cdf78 100644
--- a/packages/desktop/src-tauri/capabilities/default.json
+++ b/packages/desktop/src-tauri/capabilities/default.json
@@ -6,6 +6,19 @@
"permissions": [
"core:default",
"opener:default",
+ {
+ "identifier": "opener:allow-open-path",
+ "allow": [
+ { "path": "**/*" },
+ { "path": "/**/*" },
+ { "path": "**/.*/*/**" },
+ { "path": "/**/.*/*/**" },
+ { "path": "**/*", "app": true },
+ { "path": "/**/*", "app": true },
+ { "path": "**/.*/*/**", "app": true },
+ { "path": "/**/.*/*/**", "app": true }
+ ]
+ },
"deep-link:default",
"core:window:allow-start-dragging",
"core:window:allow-set-theme",
diff --git a/packages/desktop/src/bindings.ts b/packages/desktop/src/bindings.ts
index 440e138b4..eb5498fa6 100644
--- a/packages/desktop/src/bindings.ts
+++ b/packages/desktop/src/bindings.ts
@@ -1,19 +1,20 @@
// 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 */
export const commands = {
- killSidecar: () => __TAURI_INVOKE<void>("kill_sidecar"),
- installCli: () => __TAURI_INVOKE<string>("install_cli"),
- ensureServerReady: () => __TAURI_INVOKE<ServerReadyData>("ensure_server_ready"),
- getDefaultServerUrl: () => __TAURI_INVOKE<string | null>("get_default_server_url"),
- setDefaultServerUrl: (url: string | null) => __TAURI_INVOKE<null>("set_default_server_url", { url }),
- parseMarkdownCommand: (markdown: string) => __TAURI_INVOKE<string>("parse_markdown_command", { markdown }),
-}
+ killSidecar: () => __TAURI_INVOKE<void>("kill_sidecar"),
+ installCli: () => __TAURI_INVOKE<string>("install_cli"),
+ ensureServerReady: () => __TAURI_INVOKE<ServerReadyData>("ensure_server_ready"),
+ getDefaultServerUrl: () => __TAURI_INVOKE<string | null>("get_default_server_url"),
+ setDefaultServerUrl: (url: string | null) => __TAURI_INVOKE<null>("set_default_server_url", { url }),
+ parseMarkdownCommand: (markdown: string) => __TAURI_INVOKE<string>("parse_markdown_command", { markdown }),
+};
/* Types */
export type ServerReadyData = {
- url: string
- password: string | null
-}
+ url: string,
+ password: string | null,
+ };
+
diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx
index b54e1f79f..30cb7ba7a 100644
--- a/packages/desktop/src/index.tsx
+++ b/packages/desktop/src/index.tsx
@@ -4,6 +4,7 @@ import { render } from "solid-js/web"
import { AppBaseProviders, AppInterface, PlatformProvider, Platform } from "@opencode-ai/app"
import { open, save } from "@tauri-apps/plugin-dialog"
import { getCurrent, onOpenUrl } from "@tauri-apps/plugin-deep-link"
+import { openPath as openerOpenPath } from "@tauri-apps/plugin-opener"
import { open as shellOpen } from "@tauri-apps/plugin-shell"
import { type as ostype } from "@tauri-apps/plugin-os"
import { check, Update } from "@tauri-apps/plugin-updater"
@@ -87,6 +88,10 @@ const createPlatform = (password: Accessor<string | null>): Platform => ({
void shellOpen(url).catch(() => undefined)
},
+ openPath(path: string, app?: string) {
+ return openerOpenPath(path, app)
+ },
+
back() {
window.history.back()
},
diff --git a/packages/ui/package.json b/packages/ui/package.json
index c2462c489..cdc8baa0b 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -18,6 +18,7 @@
"./theme/context": "./src/theme/context.tsx",
"./icons/provider": "./src/components/provider-icons/types.ts",
"./icons/file-type": "./src/components/file-icons/types.ts",
+ "./icons/app": "./src/components/app-icons/types.ts",
"./fonts/*": "./src/assets/fonts/*",
"./audio/*": "./src/assets/audio/*"
},
diff --git a/packages/ui/src/assets/icons/app/android-studio.svg b/packages/ui/src/assets/icons/app/android-studio.svg
new file mode 100644
index 000000000..6b545e27a
--- /dev/null
+++ b/packages/ui/src/assets/icons/app/android-studio.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" fill="none"><g clip-path="url(#a)"><path fill="#000" d="M22.927 59.746c-.99-.234-1.817-.783-2.976-1.971-1.935-1.986-3.506-2.812-6.236-3.282-2.831-.488-4.09-1.54-4.846-4.054-.934-3.104-1.91-4.59-4.055-6.174-2.433-1.797-3.057-3.381-2.463-6.253.55-2.66.34-4.55-.778-6.982-1.332-2.9-1.168-4.45.725-6.827 1.735-2.18 2.28-3.62 2.54-6.716.262-3.125 1.132-4.407 3.692-5.445 2.138-.866 3.966-2.37 4.98-4.094 1.172-1.995 1.343-2.223 2.069-2.765 1.172-.874 2.12-1.117 3.924-1.004 3.135.197 4.815-.18 6.92-1.55 2.745-1.787 4.409-1.787 7.154 0 2.105 1.37 3.785 1.747 6.92 1.55 2.964-.185 4.197.593 6.004 3.789.896 1.584 2.726 3.125 4.73 3.982 2.852 1.221 3.682 2.336 3.892 5.232.228 3.136.871 4.89 2.575 7.02 1.897 2.373 2.062 3.925.728 6.828-1.117 2.431-1.327 4.322-.777 6.982.593 2.872-.03 4.456-2.464 6.253-2.144 1.584-3.12 3.07-4.054 6.174-.757 2.514-2.015 3.566-4.847 4.054-2.735.47-4.308 1.3-6.235 3.288-2.137 2.204-3.67 2.567-6.657 1.58-2.443-.808-4.325-.811-6.759-.013-1.704.56-2.613.657-3.706.398Z" opacity=".2"/><path fill="#fff" d="M22.927 59.16c-.99-.234-1.817-.783-2.976-1.971-1.935-1.986-3.506-2.812-6.236-3.282-2.831-.488-4.09-1.54-4.846-4.054-.934-3.104-1.91-4.59-4.055-6.174-2.433-1.797-3.057-3.381-2.463-6.253.55-2.66.34-4.55-.778-6.982-1.332-2.9-1.168-4.45.725-6.827 1.735-2.18 2.28-3.62 2.54-6.716.262-3.125 1.132-4.407 3.692-5.445 2.138-.866 3.966-2.37 4.98-4.094 1.172-1.995 1.343-2.223 2.069-2.765 1.172-.874 2.12-1.117 3.924-1.004 3.135.197 4.815-.18 6.92-1.55 2.745-1.787 4.409-1.787 7.154 0 2.105 1.37 3.785 1.747 6.92 1.55 2.964-.185 4.197.593 6.004 3.789.896 1.584 2.726 3.125 4.73 3.982 2.852 1.221 3.682 2.336 3.892 5.232.228 3.136.871 4.89 2.575 7.02 1.897 2.373 2.062 3.925.728 6.828-1.117 2.432-1.327 4.323-.777 6.982.593 2.872-.03 4.456-2.464 6.253-2.144 1.584-3.12 3.07-4.054 6.174-.757 2.514-2.015 3.566-4.847 4.054-2.735.47-4.308 1.3-6.235 3.288-2.137 2.204-3.67 2.567-6.657 1.58-2.443-.808-4.325-.811-6.759-.013-1.704.56-2.613.657-3.706.398Z"/><path fill="#034ECA" d="M43.424 47.509a.731.731 0 0 1-1.34.569l-1.761-3.27a1.076 1.076 0 1 1 1.972-.837l1.129 3.538Z"/><path fill="url(#b)" d="M43.424 47.509a.731.731 0 0 1-1.34.569l-1.761-3.27a1.076 1.076 0 1 1 1.972-.837l1.129 3.538Z"/><mask id="c" width="11" height="14" x="37" y="23" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#fff" d="M42.048 35.775a6.204 6.204 0 0 0 1.69-1.636 6.252 6.252 0 0 0 .992-2.246l.013.002.74.14a1829.466 1829.466 0 0 0 1.24.233.58.58 0 0 0 .49-.14.567.567 0 0 0 .087-.745.57.57 0 0 0-.365-.239 359.59 359.59 0 0 0-.519-.097l-.721-.136-.74-.14-.08-.014.002-.062a6.232 6.232 0 0 0-.357-2.235l-.022-.06a6.241 6.241 0 0 0-1.137-1.941c.017-.02.033-.04.05-.058l.488-.572.476-.558.342-.401a.578.578 0 0 0 .135-.416.596.596 0 0 0-.049-.19.569.569 0 0 0-.567-.334.573.573 0 0 0-.39.198l-.342.401-.476.559-.488.571-.005.005a6.298 6.298 0 0 0-3.723-1.377 6.246 6.246 0 0 0-1.628.163l4.144 11.729c.247-.12.489-.254.72-.404Z"/></mask><g mask="url(#c)"><path fill="#4FAF53" d="m48.373 33.769-4.42-12.526-7.047 2.487 4.42 12.525 7.047-2.486Z"/><g filter="url(#d)" opacity=".8"><path fill="url(#e)" fill-opacity=".3" d="M44.17 29.065c-.438 1.55-1.767-1.91-4.157-2.584-2.39-.675-5.331 1.58-4.894.03.78-1.138 2.872-2.007 5.262-1.332 2.39.674 3.81 2.694 3.789 3.886Z"/></g><g filter="url(#f)" opacity=".7"><path fill="url(#g)" fill-opacity=".4" d="M43.869 28.213c-1.314-.933-.177 2.595-1.614 4.62-1.438 2.025-5.143 2.116-3.83 3.049 1.322.395 3.496-.24 4.933-2.265 1.437-2.025 1.275-4.49.51-5.404Z"/></g><g filter="url(#h)" opacity=".6"><path fill="#8BD8A0" d="M38.761 23.716c-1.668.052-3.016.252-3.01.445.006.194 1.364.308 3.032.255 1.668-.052 3.998 1.338 3.992 1.144-.006-.193-2.346-1.897-4.014-1.844Z"/></g><g filter="url(#i)" opacity=".5"><path fill="#8BD8A0" d="M42.99 35.697c-1.333 1.006-2.507 1.696-2.624 1.542-.116-.155.869-1.095 2.2-2.102 1.332-1.006 2.274-3.55 2.39-3.396.117.155-.635 2.95-1.967 3.956Z"/></g><g filter="url(#j)" opacity=".7"><path fill="#0D652D" d="M43.605 24.693c-.61.746-.906 1.083-.972 1.03-.066-.055.124-.48.733-1.225.375-.935 1.49-.48 1.556-.426.066.054-.537-.443-1.317.621Z"/></g><g filter="url(#k)" opacity=".1"><path fill="#000" d="M42.517 25.66c.057.043.508-.451 1.007-1.105.5-.653.858-1.218.801-1.261-.057-.044-.508.45-1.007 1.104-.5.654-.858 1.218-.801 1.262Z"/></g><g filter="url(#l)" opacity=".3"><path fill="#fff" d="M44.207 25.143c-.637.78-.924 1.152-.871 1.195.053.043.425-.259 1.062-1.04.916-.858-.034-1.489-.122-1.362-.26.277.845.174-.069 1.207Z"/></g><path fill="url(#m)" fill-opacity=".9" d="M43.915 23.421c-.276.198-.312.622-.08.947.234.326.646.43.923.231.276-.198.312-.622.08-.947-.234-.325-.646-.428-.923-.23Z" opacity=".15"/><path fill="url(#n)" fill-opacity=".9" d="M46.648 30.881c-.276.198-.312.622-.079.947.233.326.645.43.922.231.276-.197.312-.621.08-.947-.233-.325-.646-.428-.923-.23Z" opacity=".15"/><g filter="url(#o)" opacity=".1"><path fill="#000" d="M44.664 31.875c.016-.07.68.022 1.481.204.802.182 1.44.387 1.423.456-.016.07-.678-.021-1.48-.203-.802-.183-1.44-.387-1.424-.457Z"/></g><mask id="p" width="2" height="3" x="44" y="30" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M44.685 31.969c.075-.34.149-1.079.176-1.406l.727-.047-.09 2.084c-.302-.069-.888-.292-.813-.631Z"/></mask><g mask="url(#p)"><g filter="url(#q)" opacity=".3" style="mix-blend-mode:multiply"><path fill="#0D652D" d="M44.85 32.118c.036-.57.006-1.037-.065-1.042-.072-.004-.113.435-.148 1.006-.035.571-.05 1.059.022 1.063.071.004.157-.455.192-1.027Z"/></g><g filter="url(#r)" opacity=".3" style="mix-blend-mode:screen"><path fill="#81C995" d="M44.888 30.961c.02-.162.01-.297-.022-.3-.032-.005-.054.12-.073.282-.019.163-.029.302.003.306.032.003.073-.125.092-.288Z"/></g></g><mask id="s" width="2" height="3" x="42" y="24" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M42.368 25.576c.275.21.808.73 1.04.962l.585-.431-1.41-1.537c-.186.247-.491.794-.215 1.006Z"/></mask><g mask="url(#s)"><g filter="url(#t)" opacity=".3" style="mix-blend-mode:multiply"><path fill="#0D652D" d="M42.398 25.355c.395.415.672.79.62.84-.051.05-.366-.26-.76-.675-.394-.415-.72-.778-.667-.827.052-.05.413.247.807.662Z"/></g><g filter="url(#u)" opacity=".3" style="mix-blend-mode:screen"><path fill="#81C995" d="M43.172 26.216c.119.112.198.222.176.245-.022.023-.118-.058-.237-.17-.12-.112-.217-.212-.195-.236.022-.023.137.05.256.161Z"/></g></g><g filter="url(#v)" opacity=".7"><path fill="#0D652D" d="M46.147 32.003c-.947-.176-1.391-.242-1.407-.159-.015.084.404.286 1.35.462.89.472 1.448-.595 1.464-.68.016-.083-.125.687-1.407.377Z"/></g><g filter="url(#w)" opacity=".3"><path fill="#fff" d="M46.311 31.245c-.963-.18-1.41-.277-1.396-.348.013-.07.48-.086 1.444.093 1.576.036 1.76 1.145.94 1.242-.36-.025.341-.798-.988-.987Z"/></g></g><path fill="url(#x)" fill-opacity=".7" d="M40.676 26.848c-.078.192-.057.384.05.427.105.043.255-.078.334-.27.078-.193.056-.384-.05-.428-.105-.043-.255.078-.334.27Z"/><path fill="url(#y)" fill-opacity=".7" d="M42.527 32.114c-.182-.1-.285-.263-.23-.363.055-.1.248-.1.43 0s.285.263.23.363c-.055.1-.248.1-.43 0Z"/><g filter="url(#z)" opacity=".09" style="mix-blend-mode:multiply"><path fill="#011B04" d="M40.168 27.407c.5.022.707-.26.747-.404.293-.476-.182-.81-.404-.881-.222-.072-.598-.157-.955.181-.356.338-.013 1.076.612 1.104Z"/></g><g filter="url(#A)" opacity=".09" style="mix-blend-mode:multiply"><path fill="#011B04" d="M41.781 31.999c.373-.33.71-.24.831-.154.524.188.366.745.239.94-.127.195-.365.496-.853.456-.487-.04-.683-.83-.217-1.242Z"/></g><path fill="#000" d="M41.859 33.096c.28.21.714.107.968-.23.254-.338.232-.782-.048-.993-.28-.21-.714-.107-.968.23-.254.338-.233.782.048.993Z"/><g filter="url(#B)"><path fill="#000" d="M40.132 27.302c.41.104.81-.08.894-.413.084-.332-.18-.685-.59-.788-.41-.104-.81.08-.894.413-.084.332.18.685.59.788Z"/></g><g filter="url(#C)"><path fill="#000" d="M41.82 32.1c.253-.338.681-.445.955-.239.274.206.29.646.036.983-.254.338-.682.445-.956.24-.274-.206-.29-.647-.036-.984Z"/></g><mask id="D" width="3" height="2" x="39" y="26" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#202124" d="M40.132 27.302c.41.104.81-.081.894-.413.084-.332-.18-.685-.59-.789-.41-.103-.81.082-.894.414-.084.332.18.685.59.788Z"/></mask><g mask="url(#D)"><g filter="url(#E)"><path fill="#D8D8D8" fill-opacity=".29" d="M40.481 26.173c-.333-.12-.643-.037-.762.034.231-.142.484-.21.802-.092a.898.898 0 0 1 .425.372c.018.03.032.054.04.072l-.04-.072a1.13 1.13 0 0 0-.034-.05c-.06-.077-.172-.171-.43-.264Z"/></g><g filter="url(#F)" opacity=".8"><path fill="url(#G)" d="M40.754 27.148c.335-.217.268-.529.193-.658.094.101.103.339.031.466a.513.513 0 0 1-.224.192Z"/></g></g><mask id="H" width="2" height="3" x="41" y="31" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#202124" d="M41.82 32.1c.254-.338.682-.445.956-.239.274.206.29.646.036.983-.254.338-.682.445-.956.24-.274-.206-.29-.647-.036-.984Z"/></mask><g mask="url(#H)"><path fill="url(#I)" d="M42.86 32.124a.466.466 0 0 0-.536-.294.517.517 0 0 1 .389.249c.106.185-.029.529-.11.677l.146.06a.681.681 0 0 0 .11-.692Z"/><g filter="url(#J)"><path fill="#D8D8D8" fill-opacity=".29" d="M42.798 32.761c-.184.303-.477.433-.615.452.27-.035.508-.14.682-.431a.897.897 0 0 0 .097-.557.652.652 0 0 0-.014-.08c.003.023.01.049.014.08l.006.06c.002.098-.027.242-.17.476Z"/></g></g><g filter="url(#K)" opacity=".8"><path fill="#E2DCE1" d="M40.68 26.417c-.069-.114-.23-.16-.302-.17l-.07.094a.57.57 0 0 1 .155.049c.066.032.145.087.177.11l.04-.083Z"/></g><g filter="url(#L)" opacity=".8"><path fill="#E2DCE1" d="M42.801 32.452c.02.13-.072.264-.12.316l-.11-.03a.55.55 0 0 0 .086-.133c.03-.065.055-.156.064-.193l.08.04Z"/></g><g fill="#E2DCE1" filter="url(#M)" opacity=".8"><path d="M40.193 27.155a2.826 2.826 0 0 1-.158-.156h-.12a.53.53 0 0 0 .172.147l.106.01ZM40.723 27.175a.591.591 0 0 0 .079-.069l-.042.015a.496.496 0 0 1-.102.077c.01 0 .038-.005.065-.023Z"/></g><g fill="#E2DCE1" filter="url(#N)" opacity=".8"><path d="M41.952 32.171c-.012.07-.022.177-.025.221l-.093.075a.531.531 0 0 1 .041-.222l.077-.074ZM42.352 31.823a.597.597 0 0 1 .105.004l-.041.015a.487.487 0 0 0-.128.004.133.133 0 0 1 .064-.023Z"/></g><g filter="url(#O)" opacity=".3"><path fill="url(#P)" fill-opacity=".4" d="M43.092 33.947c-1.52.536.542-2.544-.284-4.886-.826-2.342-4.364-3.446-2.845-3.982 1.379-.018 3.294 1.19 4.12 3.532.827 2.341-.005 4.666-.99 5.336Z"/></g><g filter="url(#Q)" opacity=".2"><path fill="url(#R)" fill-opacity=".4" d="M43.092 33.947c-1.52.536.542-2.544-.284-4.886-.826-2.342-4.364-3.446-2.845-3.982 1.379-.018 3.294 1.19 4.12 3.532.827 2.341-.005 4.666-.99 5.336Z"/></g><path fill="#4386F5" d="M17.716 27.587a2.408 2.408 0 0 0-1.549-1.147 2.776 2.776 0 0 0-2.005.296c-.617.35-1.079.913-1.285 1.567-.206.653-.083 1.443.242 2.017l2.378-1.227 2.22-1.506Z"/><path fill="url(#S)" fill-rule="evenodd" d="M17.695 27.552c3.44 5.696 10.644 8.134 16.94 5.444l2.102 4.917c-8.782 3.753-18.82.35-23.62-7.596l4.578-2.765Z" clip-rule="evenodd"/><path fill="#034ECA" d="M16.689 47.509a.731.731 0 0 0 1.34.569l1.76-3.27a1.076 1.076 0 1 0-1.971-.837l-1.129 3.538Z"/><path fill="url(#T)" d="M16.689 47.509a.731.731 0 0 0 1.34.569l1.76-3.27a1.076 1.076 0 1 0-1.971-.837l-1.129 3.538Z"/><path fill="#4285F4" d="M25.674 16.685a3.027 3.027 0 1 1 5.689 2.071l-8.884 24.41a3.027 3.027 0 1 1-5.69-2.07l8.884-24.41Z"/><path fill="url(#U)" d="M19.407 45.143a3.018 3.018 0 0 0 3.14-1.97l7.216-19.828a3.018 3.018 0 0 0-.785-3.247l-9.572 25.045Z"/><path fill="#4285F4" d="M31.962 11.175a1.904 1.904 0 0 0-3.809 0v3.613a1.904 1.904 0 0 0 3.809 0v-3.613Z"/><path fill="url(#V)" d="M31.962 11.175a1.904 1.904 0 0 0-3.809 0v3.613a1.904 1.904 0 0 0 3.809 0v-3.613Z"/><g filter="url(#W)" opacity=".5"><path fill="#044FCB" d="M33.83 38.743c-.744.064.456-.857.273-2.999-.124-1.444.38-2.667 1.124-2.73.744-.064 1.448 1.055 1.572 2.5.617 2.313-2.224 3.166-2.968 3.23Z"/></g><path fill="url(#X)" d="M28.691 18.728a2.993 2.993 0 0 1 5.62-2.06l8.961 24.456a2.993 2.993 0 1 1-5.62 2.06l-8.96-24.456Z"/><path fill="url(#Y)" d="M27.665 15.716a3.517 3.517 0 0 0-2.441 2.208l-8.422 23.129c-.514 1.41 0 2.934 1.097 3.618l9.766-28.955Z"/><path fill="url(#Z)" fill-opacity=".9" d="M28.5 11.473c.426.427 1.195.35 1.718-.173.522-.522.6-1.292.173-1.719-.427-.426-1.196-.349-1.719.173-.522.523-.6 1.292-.173 1.719Z" opacity=".3"/><path fill="url(#aa)" fill-opacity=".9" d="M24.507 24.556c1.276.737 3.243-.28 4.393-2.272 1.15-1.992 1.048-4.204-.228-4.94-1.277-.738-3.243.28-4.393 2.272-1.15 1.992-1.048 4.204.228 4.94Z" opacity=".3"/><path fill="#fff" d="M30.009 22.064a3.71 3.71 0 1 0 0-7.422 3.71 3.71 0 0 0 0 7.422Z"/><path fill="#202124" fill-rule="evenodd" d="M30.009 15.618a2.734 2.734 0 1 0 0 5.47 2.734 2.734 0 0 0 0-5.47Zm-4.688 2.735a4.687 4.687 0 1 1 9.375 0 4.687 4.687 0 0 1-9.375 0Z" clip-rule="evenodd"/></g><defs><filter id="d" width="9.563" height="4.987" x="34.841" y="24.708" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".117"/></filter><filter id="f" width="6.998" height="8.636" x="37.797" y="27.708" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".176"/></filter><filter id="h" width="7.375" height="2.215" x="35.575" y="23.539" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".088"/></filter><filter id="i" width="4.963" height="5.877" x="40.181" y="31.559" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".088"/></filter><filter id="j" width="2.775" height="2.282" x="42.386" y="23.68" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".117"/></filter><filter id="k" width="1.937" height="2.488" x="42.453" y="23.233" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".029"/></filter><filter id="l" width="1.815" height="2.774" x="43.154" y="23.744" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".088"/></filter><filter id="o" width="3.021" height=".824" x="44.606" y="31.793" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".029"/></filter><filter id="q" width=".492" height="2.303" x="44.491" y="30.959" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".059"/></filter><filter id="r" width=".263" height=".729" x="44.705" y="30.59" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".035"/></filter><filter id="t" width="1.673" height="1.747" x="41.468" y="24.57" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".059"/></filter><filter id="u" width=".579" height=".555" x="42.843" y="25.981" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".035"/></filter><filter id="v" width="3.283" height="1.274" x="44.506" y="31.386" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".117"/></filter><filter id="w" width="3.215" height="1.735" x="44.739" y="30.672" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".088"/></filter><filter id="z" width="1.666" height="1.421" x="39.377" y="26.021" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".018"/></filter><filter id="A" width="1.531" height="1.551" x="41.48" y="31.73" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".018"/></filter><filter id="B" width="1.539" height="1.283" x="39.508" y="26.072" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dx="-.006" dy=".012"/><feGaussianBlur stdDeviation=".006"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0.0156863 0 0 0 0 0.231373 0 0 0 0 0.0666667 0 0 0 0.7 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1970_8019"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1970_8019" result="shape"/></filter><filter id="C" width="1.376" height="1.455" x="41.621" y="31.756" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dx="-.006" dy=".012"/><feGaussianBlur stdDeviation=".006"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0.0156863 0 0 0 0 0.231373 0 0 0 0 0.0666667 0 0 0 0.7 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1970_8019"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1970_8019" result="shape"/></filter><filter id="E" width="1.291" height=".527" x="39.707" y="26.044" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".006"/></filter><filter id="F" width=".277" height=".663" x="40.751" y="26.487" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".001"/></filter><filter id="J" width=".81" height="1.092" x="42.171" y="32.133" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".006"/></filter><filter id="K" width=".395" height=".276" x="40.296" y="26.236" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".006"/></filter><filter id="L" width=".256" height=".379" x="42.559" y="32.4" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".006"/></filter><filter id="M" width=".898" height=".211" x="39.91" y="26.993" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".003"/></filter><filter id="N" width=".635" height=".656" x="41.828" y="31.816" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".003"/></filter><filter id="O" width="5.301" height="9.398" x="39.354" y="24.844" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".117"/></filter><filter id="Q" width="5.301" height="9.398" x="39.354" y="24.844" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".117"/></filter><filter id="W" width="3.887" height="6.322" x="33.292" y="32.718" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_1970_8019" stdDeviation=".146"/></filter><linearGradient id="b" x1="42.948" x2="41.02" y1="48.281" y2="47.187" gradientUnits="userSpaceOnUse"><stop stop-color="#4285F4"/><stop offset="1" stop-color="#034ECA"/></linearGradient><linearGradient id="e" x1="40.404" x2="39.793" y1="25.095" y2="27.261" gradientUnits="userSpaceOnUse"><stop stop-color="#A8F0B9"/><stop offset="1" stop-color="#ADEEBC" stop-opacity="0"/></linearGradient><linearGradient id="g" x1="43.428" x2="41.593" y1="33.666" y2="32.364" gradientUnits="userSpaceOnUse"><stop stop-color="#A8F0B9"/><stop offset="1" stop-color="#ADEEBC" stop-opacity="0"/></linearGradient><linearGradient id="G" x1="41.106" x2="40.595" y1="26.476" y2="26.682" gradientUnits="userSpaceOnUse"><stop stop-color="#E2DDE2"/><stop offset="1" stop-color="#E2DDE2" stop-opacity="0"/></linearGradient><linearGradient id="I" x1="42.888" x2="42.46" y1="32.199" y2="32.35" gradientUnits="userSpaceOnUse"><stop stop-color="#373637"/><stop offset="1" stop-color="#373637" stop-opacity="0"/></linearGradient><linearGradient id="P" x1="44.165" x2="42.043" y1="28.582" y2="29.331" gradientUnits="userSpaceOnUse"><stop stop-color="#A8F0B9"/><stop offset="1" stop-color="#ADEEBC" stop-opacity="0"/></linearGradient><linearGradient id="R" x1="44.165" x2="42.043" y1="28.582" y2="29.331" gradientUnits="userSpaceOnUse"><stop stop-color="#A8F0B9"/><stop offset="1" stop-color="#ADEEBC" stop-opacity="0"/></linearGradient><linearGradient id="S" x1="28.105" x2="37.382" y1="35.54" y2="34.808" gradientUnits="userSpaceOnUse"><stop stop-color="#4285F4"/><stop offset=".703" stop-color="#044FCB"/></linearGradient><linearGradient id="T" x1="17.509" x2="19.509" y1="47.792" y2="46.612" gradientUnits="userSpaceOnUse"><stop stop-color="#4285F4"/><stop offset="1" stop-color="#034ECA"/></linearGradient><linearGradient id="U" x1="26.686" x2="25.62" y1="31" y2="30.63" gradientUnits="userSpaceOnUse"><stop stop-color="#73A7FF"/><stop offset="1" stop-color="#5893F6" stop-opacity="0"/></linearGradient><linearGradient id="V" x1="30.058" x2="31.734" y1="11.401" y2="12.909" gradientUnits="userSpaceOnUse"><stop stop-color="#4285F4"/><stop offset="1" stop-color="#034ECA"/></linearGradient><linearGradient id="X" x1="31.933" x2="37.002" y1="21.266" y2="31.86" gradientUnits="userSpaceOnUse"><stop stop-color="#034DC9"/><stop offset="1" stop-color="#4285F4"/></linearGradient><linearGradient id="Y" x1="19.667" x2="22.337" y1="29.995" y2="30.912" gradientUnits="userSpaceOnUse"><stop stop-color="#9BC0FF"/><stop offset="1" stop-color="#5893F6" stop-opacity="0"/></linearGradient><radialGradient id="m" cx="0" cy="0" r="1" gradientTransform="matrix(-.26822 -.36786 .26657 -.19436 44.337 24.01)" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset=".948" stop-color="#fff" stop-opacity="0"/></radialGradient><radialGradient id="n" cx="0" cy="0" r="1" gradientTransform="rotate(-126.097 31.536 3.769) scale(.45526 .3299)" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset=".948" stop-color="#fff" stop-opacity="0"/></radialGradient><radialGradient id="x" cx="0" cy="0" r="1" gradientTransform="rotate(-156.205 23.27 9.157) scale(.17761 .3235)" gradientUnits="userSpaceOnUse"><stop stop-color="#93E19F"/><stop offset="1" stop-color="#93E19F" stop-opacity="0"/></radialGradient><radialGradient id="y" cx="0" cy="0" r="1" gradientTransform="matrix(-.0815 .15773 -.28727 -.14844 42.627 31.933)" gradientUnits="userSpaceOnUse"><stop stop-color="#93E19F"/><stop offset="1" stop-color="#93E19F" stop-opacity="0"/></radialGradient><radialGradient id="Z" cx="0" cy="0" r="1" gradientTransform="matrix(-.58937 .5997 -.4178 -.4106 29.445 10.527)" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset=".948" stop-color="#fff" stop-opacity="0"/></radialGradient><radialGradient id="aa" cx="0" cy="0" r="1" gradientTransform="rotate(119.61 7.2 18.211) scale(2.61787 1.43021)" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset=".948" stop-color="#fff" stop-opacity="0"/></radialGradient><clipPath id="a"><rect width="60" height="60" fill="#fff" rx="2.344"/></clipPath></defs></svg> \ No newline at end of file
diff --git a/packages/ui/src/assets/icons/app/antigravity.svg b/packages/ui/src/assets/icons/app/antigravity.svg
new file mode 100644
index 000000000..3c3554af7
--- /dev/null
+++ b/packages/ui/src/assets/icons/app/antigravity.svg
@@ -0,0 +1 @@
+<svg width="16" height="15" viewBox="0 0 16 15" fill="none" xmlns="http://www.w3.org/2000/svg"><mask id="antigravity__mask0_111_52" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="16" height="15"><path d="M14.0777 13.984C14.945 14.6345 16.2458 14.2008 15.0533 13.0084C11.476 9.53949 12.2349 0 7.79033 0C3.34579 0 4.10461 9.53949 0.527295 13.0084C-0.773543 14.3092 0.635692 14.6345 1.50293 13.984C4.86344 11.7076 4.64663 7.69664 7.79033 7.69664C10.934 7.69664 10.7172 11.7076 14.0777 13.984Z" fill="black"/></mask><g mask="url(#antigravity__mask0_111_52)"><g filter="url(#antigravity__filter0_f_111_52)"><path d="M-0.658907 -3.2306C-0.922679 -0.906781 1.07986 1.22861 3.81388 1.53894C6.54791 1.84927 8.97811 0.217009 9.24188 -2.10681C9.50565 -4.43063 7.50312 -6.56602 4.76909 -6.87635C2.03506 -7.18667 -0.395135 -5.55442 -0.658907 -3.2306Z" fill="#FFE432"/></g><g filter="url(#antigravity__filter1_f_111_52)"><path d="M9.88233 4.36642C10.5673 7.31568 13.566 9.13902 16.5801 8.43896C19.5942 7.73891 21.4823 4.78056 20.7973 1.83131C20.1123 -1.11795 17.1136 -2.94128 14.0995 -2.24123C11.0854 -1.54118 9.19733 1.41717 9.88233 4.36642Z" fill="#FC413D"/></g><g filter="url(#antigravity__filter2_f_111_52)"><path d="M-8.05291 6.34512C-7.18736 9.38883 -3.28925 10.9473 0.653774 9.82598C4.5968 8.7047 7.09158 5.32829 6.22603 2.28458C5.36048 -0.759142 1.46236 -2.31758 -2.48066 -1.19629C-6.42368 -0.0750048 -8.91846 3.3014 -8.05291 6.34512Z" fill="#00B95C"/></g><g filter="url(#antigravity__filter3_f_111_52)"><path d="M-8.05291 6.34512C-7.18736 9.38883 -3.28925 10.9473 0.653774 9.82598C4.5968 8.7047 7.09158 5.32829 6.22603 2.28458C5.36048 -0.759142 1.46236 -2.31758 -2.48066 -1.19629C-6.42368 -0.0750048 -8.91846 3.3014 -8.05291 6.34512Z" fill="#00B95C"/></g><g filter="url(#antigravity__filter4_f_111_52)"><path d="M-4.92402 8.86746C-2.75421 11.0837 0.982691 10.9438 3.42257 8.55507C5.86246 6.1663 6.08139 2.43321 3.91158 0.216963C1.74177 -1.99928 -1.99513 -1.85942 -4.43501 0.529349C-6.87489 2.91812 -7.09383 6.65122 -4.92402 8.86746Z" fill="#00B95C"/></g><g filter="url(#antigravity__filter5_f_111_52)"><path d="M6.42819 17.2263C7.10197 20.1273 9.91278 21.953 12.7063 21.3042C15.4998 20.6553 17.2182 17.7777 16.5444 14.8767C15.8707 11.9757 13.0599 10.15 10.2663 10.7988C7.47281 11.4477 5.75441 14.3253 6.42819 17.2263Z" fill="#3186FF"/></g><g filter="url(#antigravity__filter6_f_111_52)"><path d="M1.66508 -5.94539C0.254213 -2.80254 1.7978 0.951609 5.11277 2.43973C8.42774 3.92785 12.2588 2.58642 13.6696 -0.556431C15.0805 -3.69928 13.5369 -7.45343 10.222 -8.94155C6.90699 -10.4297 3.07594 -9.08824 1.66508 -5.94539Z" fill="#FBBC04"/></g><g filter="url(#antigravity__filter7_f_111_52)"><path d="M-2.11428 24.3903C-5.52984 23.0496 0.307266 12.0177 1.75874 8.32038C3.21024 4.62304 7.15576 2.71272 10.5713 4.05357C13.9869 5.39442 18.0354 12.7796 16.5838 16.477C15.1323 20.1743 1.30129 25.7311 -2.11428 24.3903Z" fill="#3186FF"/></g><g filter="url(#antigravity__filter8_f_111_52)"><path d="M18.5814 10.6598C17.6669 11.727 15.2806 11.1828 13.2514 9.44417C11.2222 7.70556 10.3185 5.43097 11.2329 4.3637C12.1473 3.29646 14.5336 3.84069 16.5628 5.57928C18.592 7.31789 19.4958 9.59249 18.5814 10.6598Z" fill="#749BFF"/></g><g filter="url(#antigravity__filter9_f_111_52)"><path d="M11.7552 5.22715C15.5162 7.77124 19.8471 7.93838 21.4286 5.60045C23.0101 3.26253 21.2433 -0.695128 17.4823 -3.23922C13.7213 -5.78331 9.39044 -5.95044 7.80896 -3.61252C6.22747 -1.27459 7.99428 2.68306 11.7552 5.22715Z" fill="#FC413D"/></g><g filter="url(#antigravity__filter10_f_111_52)"><path d="M-0.592149 1.08896C-1.5239 3.33663 -1.21959 5.59799 0.0875457 6.13985C1.39468 6.68171 3.20966 5.29888 4.14141 3.05121C5.07316 0.803541 4.76885 -1.45782 3.46171 -1.99968C2.15458 -2.54154 0.339602 -1.15871 -0.592149 1.08896Z" fill="#FFEE48"/></g></g><defs><filter id="antigravity__filter0_f_111_52" x="-2.12817" y="-8.35998" width="12.8393" height="11.383" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur stdDeviation="0.722959" result="effect1_foregroundBlur_111_52"/></filter><filter id="antigravity__filter1_f_111_52" x="2.75168" y="-9.38089" width="25.1763" height="24.96" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur stdDeviation="3.49513" result="effect1_foregroundBlur_111_52"/></filter><filter id="antigravity__filter2_f_111_52" x="-14.1669" y="-7.50196" width="26.5068" height="23.6338" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur stdDeviation="2.97119" result="effect1_foregroundBlur_111_52"/></filter><filter id="antigravity__filter3_f_111_52" x="-14.1669" y="-7.50196" width="26.5068" height="23.6338" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur stdDeviation="2.97119" result="effect1_foregroundBlur_111_52"/></filter><filter id="antigravity__filter4_f_111_52" x="-12.3607" y="-7.29981" width="23.709" height="23.6846" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur stdDeviation="2.97119" result="effect1_foregroundBlur_111_52"/></filter><filter id="antigravity__filter5_f_111_52" x="0.634962" y="5.02095" width="21.7027" height="22.0616" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur stdDeviation="2.82351" result="effect1_foregroundBlur_111_52"/></filter><filter id="antigravity__filter6_f_111_52" x="-3.97547" y="-14.6666" width="23.2857" height="22.8313" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur stdDeviation="2.5589" result="effect1_foregroundBlur_111_52"/></filter><filter id="antigravity__filter7_f_111_52" x="-7.7407" y="-0.945408" width="29.1982" height="30.1105" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur stdDeviation="2.2852" result="effect1_foregroundBlur_111_52"/></filter><filter id="antigravity__filter8_f_111_52" x="6.78641" y="-0.27231" width="16.2415" height="15.5681" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur stdDeviation="2.04485" result="effect1_foregroundBlur_111_52"/></filter><filter id="antigravity__filter9_f_111_52" x="3.77526" y="-8.71693" width="21.687" height="19.4212" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur stdDeviation="1.72712" result="effect1_foregroundBlur_111_52"/></filter><filter id="antigravity__filter10_f_111_52" x="-5.40727" y="-6.39238" width="14.3639" height="16.9254" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur stdDeviation="2.1376" result="effect1_foregroundBlur_111_52"/></filter></defs></svg> \ No newline at end of file
diff --git a/packages/ui/src/assets/icons/app/cursor.svg b/packages/ui/src/assets/icons/app/cursor.svg
new file mode 100644
index 000000000..c2c8c1819
--- /dev/null
+++ b/packages/ui/src/assets/icons/app/cursor.svg
@@ -0,0 +1 @@
+<?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> \ No newline at end of file
diff --git a/packages/ui/src/assets/icons/app/finder.png b/packages/ui/src/assets/icons/app/finder.png
new file mode 100644
index 000000000..4edf53bca
--- /dev/null
+++ b/packages/ui/src/assets/icons/app/finder.png
Binary files differ
diff --git a/packages/ui/src/assets/icons/app/ghostty.svg b/packages/ui/src/assets/icons/app/ghostty.svg
new file mode 100644
index 000000000..1dc652aac
--- /dev/null
+++ b/packages/ui/src/assets/icons/app/ghostty.svg
@@ -0,0 +1 @@
+<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> \ No newline at end of file
diff --git a/packages/ui/src/assets/icons/app/iterm2.svg b/packages/ui/src/assets/icons/app/iterm2.svg
new file mode 100644
index 000000000..0b00a1b7a
--- /dev/null
+++ b/packages/ui/src/assets/icons/app/iterm2.svg
@@ -0,0 +1,25 @@
+<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>
diff --git a/packages/ui/src/assets/icons/app/powershell.svg b/packages/ui/src/assets/icons/app/powershell.svg
new file mode 100644
index 000000000..fa0c70f0b
--- /dev/null
+++ b/packages/ui/src/assets/icons/app/powershell.svg
@@ -0,0 +1 @@
+<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> \ No newline at end of file
diff --git a/packages/ui/src/assets/icons/app/terminal.png b/packages/ui/src/assets/icons/app/terminal.png
new file mode 100644
index 000000000..43857b632
--- /dev/null
+++ b/packages/ui/src/assets/icons/app/terminal.png
Binary files differ
diff --git a/packages/ui/src/assets/icons/app/textmate.png b/packages/ui/src/assets/icons/app/textmate.png
new file mode 100644
index 000000000..1eee73c5e
--- /dev/null
+++ b/packages/ui/src/assets/icons/app/textmate.png
Binary files differ
diff --git a/packages/ui/src/assets/icons/app/vscode.svg b/packages/ui/src/assets/icons/app/vscode.svg
new file mode 100644
index 000000000..aba7e19f6
--- /dev/null
+++ b/packages/ui/src/assets/icons/app/vscode.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 100 100"><mask id="vscode__a" width="100" height="100" x="0" y="0" mask-type="alpha" maskUnits="userSpaceOnUse"><path fill="#fff" fill-rule="evenodd" d="M70.912 99.317a6.223 6.223 0 0 0 4.96-.19l20.589-9.907A6.25 6.25 0 0 0 100 83.587V16.413a6.25 6.25 0 0 0-3.54-5.632L75.874.874a6.226 6.226 0 0 0-7.104 1.21L29.355 38.04 12.187 25.01a4.162 4.162 0 0 0-5.318.236l-5.506 5.009a4.168 4.168 0 0 0-.004 6.162L16.247 50 1.36 63.583a4.168 4.168 0 0 0 .004 6.162l5.506 5.01a4.162 4.162 0 0 0 5.318.236l17.168-13.032L68.77 97.917a6.217 6.217 0 0 0 2.143 1.4ZM75.015 27.3 45.11 50l29.906 22.701V27.3Z" clip-rule="evenodd"/></mask><g mask="url(#vscode__a)"><path fill="#0065A9" d="M96.461 10.796 75.857.876a6.23 6.23 0 0 0-7.107 1.207l-67.451 61.5a4.167 4.167 0 0 0 .004 6.162l5.51 5.009a4.167 4.167 0 0 0 5.32.236l81.228-61.62c2.725-2.067 6.639-.124 6.639 3.297v-.24a6.25 6.25 0 0 0-3.539-5.63Z"/><g filter="url(#vscode__b)"><path fill="#007ACC" d="m96.461 89.204-20.604 9.92a6.229 6.229 0 0 1-7.107-1.207l-67.451-61.5a4.167 4.167 0 0 1 .004-6.162l5.51-5.009a4.167 4.167 0 0 1 5.32-.236l81.228 61.62c2.725 2.067 6.639.124 6.639-3.297v.24a6.25 6.25 0 0 1-3.539 5.63Z"/></g><g filter="url(#vscode__c)"><path fill="#1F9CF0" d="M75.858 99.126a6.232 6.232 0 0 1-7.108-1.21c2.306 2.307 6.25.674 6.25-2.588V4.672c0-3.262-3.944-4.895-6.25-2.589a6.232 6.232 0 0 1 7.108-1.21l20.6 9.908A6.25 6.25 0 0 1 100 16.413v67.174a6.25 6.25 0 0 1-3.541 5.633l-20.601 9.906Z"/></g><path fill="url(#vscode__d)" fill-rule="evenodd" d="M70.851 99.317a6.224 6.224 0 0 0 4.96-.19L96.4 89.22a6.25 6.25 0 0 0 3.54-5.633V16.413a6.25 6.25 0 0 0-3.54-5.632L75.812.874a6.226 6.226 0 0 0-7.104 1.21L29.294 38.04 12.126 25.01a4.162 4.162 0 0 0-5.317.236l-5.507 5.009a4.168 4.168 0 0 0-.004 6.162L16.186 50 1.298 63.583a4.168 4.168 0 0 0 .004 6.162l5.507 5.009a4.162 4.162 0 0 0 5.317.236L29.294 61.96l39.414 35.958a6.218 6.218 0 0 0 2.143 1.4ZM74.954 27.3 45.048 50l29.906 22.701V27.3Z" clip-rule="evenodd" opacity=".25" style="mix-blend-mode:overlay"/></g><defs><filter id="vscode__b" width="116.727" height="92.246" x="-8.394" y="15.829" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="4.167"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" mode="overlay" result="effect1_dropShadow"/><feBlend in="SourceGraphic" in2="effect1_dropShadow" result="shape"/></filter><filter id="vscode__c" width="47.917" height="116.151" x="60.417" y="-8.076" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="4.167"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="BackgroundImageFix" mode="overlay" result="effect1_dropShadow"/><feBlend in="SourceGraphic" in2="effect1_dropShadow" result="shape"/></filter><linearGradient id="vscode__d" x1="49.939" x2="49.939" y1=".258" y2="99.742" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient></defs></svg> \ No newline at end of file
diff --git a/packages/ui/src/assets/icons/app/xcode.png b/packages/ui/src/assets/icons/app/xcode.png
new file mode 100644
index 000000000..c37d9f176
--- /dev/null
+++ b/packages/ui/src/assets/icons/app/xcode.png
Binary files differ
diff --git a/packages/ui/src/assets/icons/app/zed.svg b/packages/ui/src/assets/icons/app/zed.svg
new file mode 100644
index 000000000..7c9a0e591
--- /dev/null
+++ b/packages/ui/src/assets/icons/app/zed.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" fill="none" viewBox="0 0 96 96"><g clip-path="url(#zed_light__a)"><path fill="currentColor" fill-rule="evenodd" d="M9 6a3 3 0 0 0-3 3v66H0V9a9 9 0 0 1 9-9h80.379c4.009 0 6.016 4.847 3.182 7.682L43.055 57.187H57V51h6v7.688a4.5 4.5 0 0 1-4.5 4.5H37.055L26.743 73.5H73.5V36h6v37.5a6 6 0 0 1-6 6H20.743L10.243 90H87a3 3 0 0 0 3-3V21h6v66a9 9 0 0 1-9 9H6.621c-4.009 0-6.016-4.847-3.182-7.682L52.757 39H39v6h-6v-7.5a4.5 4.5 0 0 1 4.5-4.5h21.257l10.5-10.5H22.5V60h-6V22.5a6 6 0 0 1 6-6h52.757L85.757 6H9Z" clip-rule="evenodd"/></g><defs><clipPath id="zed_light__a"><path fill="#fff" d="M0 0h96v96H0z"/></clipPath></defs></svg> \ No newline at end of file
diff --git a/packages/ui/src/components/app-icon.css b/packages/ui/src/components/app-icon.css
new file mode 100644
index 000000000..edcdbcceb
--- /dev/null
+++ b/packages/ui/src/components/app-icon.css
@@ -0,0 +1,9 @@
+img[data-component="app-icon"] {
+ display: block;
+ box-sizing: border-box;
+ padding: 2px;
+ border-radius: 0.125rem;
+ background: var(--smoke-light-2);
+ border: 1px solid var(--smoke-light-alpha-4);
+ object-fit: contain;
+}
diff --git a/packages/ui/src/components/app-icon.tsx b/packages/ui/src/components/app-icon.tsx
new file mode 100644
index 000000000..f58b5d38c
--- /dev/null
+++ b/packages/ui/src/components/app-icon.tsx
@@ -0,0 +1,52 @@
+import type { Component, ComponentProps } from "solid-js"
+import { splitProps } from "solid-js"
+import type { IconName } from "./app-icons/types"
+
+import androidStudio from "../assets/icons/app/android-studio.svg"
+import antigravity from "../assets/icons/app/antigravity.svg"
+import cursor from "../assets/icons/app/cursor.svg"
+import finder from "../assets/icons/app/finder.png"
+import ghostty from "../assets/icons/app/ghostty.svg"
+import iterm2 from "../assets/icons/app/iterm2.svg"
+import powershell from "../assets/icons/app/powershell.svg"
+import terminal from "../assets/icons/app/terminal.png"
+import textmate from "../assets/icons/app/textmate.png"
+import vscode from "../assets/icons/app/vscode.svg"
+import xcode from "../assets/icons/app/xcode.png"
+import zed from "../assets/icons/app/zed.svg"
+
+const icons = {
+ vscode,
+ cursor,
+ zed,
+ finder,
+ terminal,
+ iterm2,
+ ghostty,
+ xcode,
+ "android-studio": androidStudio,
+ antigravity,
+ textmate,
+ powershell,
+} satisfies Record<IconName, string>
+
+export type AppIconProps = Omit<ComponentProps<"img">, "src"> & {
+ id: IconName
+}
+
+export const AppIcon: Component<AppIconProps> = (props) => {
+ const [local, rest] = splitProps(props, ["id", "class", "classList", "alt", "draggable"])
+ return (
+ <img
+ data-component="app-icon"
+ {...rest}
+ src={icons[local.id]}
+ alt={local.alt ?? ""}
+ draggable={local.draggable ?? false}
+ classList={{
+ ...(local.classList ?? {}),
+ [local.class ?? ""]: !!local.class,
+ }}
+ />
+ )
+}
diff --git a/packages/ui/src/components/app-icons/sprite.svg b/packages/ui/src/components/app-icons/sprite.svg
new file mode 100644
index 000000000..68361f413
--- /dev/null
+++ b/packages/ui/src/components/app-icons/sprite.svg
@@ -0,0 +1,114 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="0" height="0">
+ <defs>
+ <symbol id="vscode" viewBox="0 0 24 24">
+ <rect width="24" height="24" rx="5" fill="#007ACC" />
+ <g transform="scale(1.5)">
+ <path
+ fill="#fff"
+ d="M11.5 11.19V4.8L7.3 7.99M1.17 6.07a.6.6 0 0 1-.01-.81L2 4.48c.14-.13.48-.18.73 0l2.39 1.83 5.55-5.09c.22-.22.61-.32 1.05-.08l2.8 1.34c.25.15.49.38.49.81v9.49c0 .28-.2.58-.42.7l-3.08 1.48c-.22.09-.64 0-.79-.14L5.11 9.69l-2.38 1.83c-.27.18-.6.13-.74 0l-.84-.77c-.22-.23-.2-.61.04-.84l2.1-1.9"
+ />
+ </g>
+ </symbol>
+
+ <symbol id="cursor" viewBox="0 0 24 24">
+ <rect width="24" height="24" rx="5" fill="#111827" />
+ <path
+ fill="#fff"
+ d="M11.503.131 1.891 5.678a.84.84 0 0 0-.42.726v11.188c0 .3.162.575.42.724l9.609 5.55a1 1 0 0 0 .998 0l9.61-5.55a.84.84 0 0 0 .42-.724V6.404a.84.84 0 0 0-.42-.726L12.497.131a1.01 1.01 0 0 0-.996 0M2.657 6.338h18.55c.263 0 .43.287.297.515L12.23 22.918c-.062.107-.229.064-.229-.06V12.335a.59.59 0 0 0-.295-.51l-9.11-5.257c-.109-.063-.064-.23.061-.23"
+ />
+ </symbol>
+
+ <symbol id="zed" viewBox="0 0 24 24">
+ <rect width="24" height="24" rx="5" fill="#084CCF" />
+ <g transform="translate(12 12) scale(0.9) translate(-12 -12)">
+ <path
+ fill="#fff"
+ d="M2.25 1.5a.75.75 0 0 0-.75.75v16.5H0V2.25A2.25 2.25 0 0 1 2.25 0h20.095c1.002 0 1.504 1.212.795 1.92L10.764 14.298h3.486V12.75h1.5v1.922a1.125 1.125 0 0 1-1.125 1.125H9.264l-2.578 2.578h11.689V9h1.5v9.375a1.5 1.5 0 0 1-1.5 1.5H5.185L2.562 22.5H21.75a.75.75 0 0 0 .75-.75V5.25H24v16.5A2.25 2.25 0 0 1 21.75 24H1.655C.653 24 .151 22.788.86 22.08L13.19 9.75H9.75v1.5h-1.5V9.375A1.125 1.125 0 0 1 9.375 8.25h5.314l2.625-2.625H5.625V15h-1.5V5.625a1.5 1.5 0 0 1 1.5-1.5h13.19L21.438 1.5z"
+ />
+ </g>
+ </symbol>
+
+ <symbol id="finder" viewBox="0 0 24 24">
+ <rect width="24" height="24" rx="5" fill="#8ED0FF" />
+ <path d="M12 0H19a5 5 0 0 1 5 5V19a5 5 0 0 1-5 5H12Z" fill="#2D7BF7" />
+ <path d="M12 3v18" stroke="#0B2A4A" stroke-opacity="0.35" stroke-width="1.5" />
+ <circle cx="8.3" cy="9.2" r="1.1" fill="#0B2A4A" />
+ <circle cx="15.7" cy="9.2" r="1.1" fill="#0B2A4A" />
+ <path
+ d="M7.3 15c1.2 1.55 2.9 2.4 4.7 2.4s3.5-.85 4.7-2.4"
+ stroke="#0B2A4A"
+ stroke-width="1.5"
+ fill="none"
+ stroke-linecap="round"
+ />
+ </symbol>
+
+ <symbol id="terminal" viewBox="0 0 24 24">
+ <rect width="24" height="24" rx="5" fill="#111827" />
+ <rect
+ x="3.5"
+ y="4.5"
+ width="17"
+ height="15"
+ rx="2.5"
+ fill="#0B1220"
+ stroke="#334155"
+ stroke-opacity="0.5"
+ />
+ <path
+ d="M7.8 9.2 11 12 7.8 14.8"
+ stroke="#34D399"
+ stroke-width="1.8"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ <rect x="12.2" y="14.2" width="5.4" height="1.6" rx="0.8" fill="#34D399" />
+ </symbol>
+
+ <symbol id="iterm2" viewBox="0 0 24 24">
+ <rect width="24" height="24" rx="5" fill="#0B0B0B" />
+ <rect x="3.2" y="4.2" width="17.6" height="15.6" rx="2.4" fill="#000" stroke="#60A5FA" stroke-width="1.2" />
+ <circle cx="5.5" cy="6.3" r="0.75" fill="#F87171" />
+ <circle cx="7.6" cy="6.3" r="0.75" fill="#FBBF24" />
+ <circle cx="9.7" cy="6.3" r="0.75" fill="#34D399" />
+ <path
+ d="M7.9 10.2 10.6 12 7.9 13.8"
+ stroke="#34D399"
+ stroke-width="1.6"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ <rect x="11.6" y="13.3" width="5" height="1.4" rx="0.7" fill="#34D399" />
+ </symbol>
+
+ <symbol id="ghostty" viewBox="0 0 24 24">
+ <rect width="24" height="24" rx="5" fill="#3551F3" />
+ <g transform="translate(12 12) scale(0.9) translate(-12 -12)">
+ <path
+ fill="#fff"
+ d="M12 0C6.7 0 2.4 4.3 2.4 9.6v11.146c0 1.772 1.45 3.267 3.222 3.254a3.18 3.18 0 0 0 1.955-.686 1.96 1.96 0 0 1 2.444 0 3.18 3.18 0 0 0 1.976.686c.75 0 1.436-.257 1.98-.686.715-.563 1.71-.587 2.419-.018.59.476 1.355.743 2.182.699 1.705-.094 3.022-1.537 3.022-3.244V9.601C21.6 4.3 17.302 0 12 0M6.069 6.562a1 1 0 0 1 .46.131l3.578 2.065v.002a.974.974 0 0 1 0 1.687L6.53 12.512a.975.975 0 0 1-.976-1.687L7.67 9.602 5.553 8.38a.975.975 0 0 1 .515-1.818m7.438 2.063h4.7a.975.975 0 1 1 0 1.95h-4.7a.975.975 0 0 1 0-1.95"
+ />
+ </g>
+ </symbol>
+
+ <symbol id="xcode" viewBox="0 0 24 24">
+ <rect width="24" height="24" rx="5" fill="#147EFB" />
+ <path d="M6 8H18" stroke="#fff" stroke-opacity="0.18" stroke-width="1.2" stroke-linecap="round" />
+ <path d="M8 6V18" stroke="#fff" stroke-opacity="0.18" stroke-width="1.2" stroke-linecap="round" />
+ <path d="M6 18H18" stroke="#fff" stroke-opacity="0.18" stroke-width="1.2" stroke-linecap="round" />
+ <path d="M18 6V18" stroke="#fff" stroke-opacity="0.18" stroke-width="1.2" stroke-linecap="round" />
+ <g transform="translate(12 12) rotate(-35) translate(-12 -12)">
+ <rect x="11.1" y="6.2" width="2" height="12.6" rx="1" fill="#fff" />
+ <rect x="9.2" y="5.3" width="5.6" height="2.7" rx="1" fill="#fff" />
+ </g>
+ </symbol>
+
+ <symbol id="android-studio" viewBox="0 0 24 24">
+ <rect width="24" height="24" rx="5" fill="#3DDC84" />
+ <circle cx="12" cy="12.2" r="6.8" fill="#3B82F6" />
+ <circle cx="12" cy="12.2" r="4.8" fill="none" stroke="#fff" stroke-width="1.6" />
+ <path d="M12 9.4l2.2 5-2.2-1.3-2.2 1.3z" fill="#fff" />
+ <circle cx="12" cy="12.2" r="0.9" fill="#fff" />
+ </symbol>
+ </defs>
+</svg>
diff --git a/packages/ui/src/components/app-icons/types.ts b/packages/ui/src/components/app-icons/types.ts
new file mode 100644
index 000000000..81964b8da
--- /dev/null
+++ b/packages/ui/src/components/app-icons/types.ts
@@ -0,0 +1,18 @@
+// This file is generated by icon spritesheet generator
+
+export const iconNames = [
+ "vscode",
+ "cursor",
+ "zed",
+ "finder",
+ "terminal",
+ "iterm2",
+ "ghostty",
+ "xcode",
+ "android-studio",
+ "antigravity",
+ "textmate",
+ "powershell",
+] as const
+
+export type IconName = (typeof iconNames)[number]
diff --git a/packages/ui/src/styles/index.css b/packages/ui/src/styles/index.css
index c038f69f6..c85df7ba3 100644
--- a/packages/ui/src/styles/index.css
+++ b/packages/ui/src/styles/index.css
@@ -7,6 +7,7 @@
@import "katex/dist/katex.min.css" layer(base);
@import "../components/accordion.css" layer(components);
+@import "../components/app-icon.css" layer(components);
@import "../components/avatar.css" layer(components);
@import "../components/basic-tool.css" layer(components);
@import "../components/button.css" layer(components);