Compare commits

...

5 Commits

Author SHA1 Message Date
David Hill
953af23920 tui: replace help and settings-gear icons with cleaner SVG paths 2026-03-13 10:07:34 +00:00
David Hill
8e5d00e8f3 feat(app): open status as modal
Replace the status popover with a dialog-based modal from the sidebar rail and tune the layout (top offset, max height, transitions, and tab panel sizing).
2026-03-12 23:00:36 +00:00
David Hill
8ba2a78aad feat(ui): support top-positioned dialogs
Add a dialog position option and allow customizing the top offset via --dialog-top, plus pass-through style support.
2026-03-12 23:00:29 +00:00
David Hill
ef08f6df0a fix(app): align status badge and popover spacing
Offset the status health dot/mask to match the sidebar rail placement and increase the popover gutter to 8px.
2026-03-12 22:06:38 +00:00
David Hill
59090bc1b3 feat(app): move status button to sidebar rail
Relocate the status popover trigger from the title bar into the left project rail above Settings/Help, keeping the health dot indicator.
2026-03-12 21:39:44 +00:00
7 changed files with 129 additions and 84 deletions

View File

@@ -21,7 +21,6 @@ import { focusTerminalById } from "@/pages/session/helpers"
import { useSessionLayout } from "@/pages/session/session-layout"
import { decode64 } from "@/utils/base64"
import { Persist, persisted } from "@/utils/persist"
import { StatusPopover } from "../status-popover"
const OPEN_APPS = [
"vscode",
@@ -409,9 +408,6 @@ export function SessionHeader() {
</div>
</Show>
<div class="flex items-center gap-1">
<Tooltip placement="bottom" value={language.t("status.popover.trigger")}>
<StatusPopover />
</Tooltip>
<TooltipKeybind
title={language.t("command.terminal.toggle")}
keybind={command.keybind("terminal.toggle")}

View File

@@ -1,7 +1,7 @@
import { Button } from "@opencode-ai/ui/button"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
import { Icon } from "@opencode-ai/ui/icon"
import { Popover } from "@opencode-ai/ui/popover"
import { Switch } from "@opencode-ai/ui/switch"
import { Tabs } from "@opencode-ai/ui/tabs"
import { showToast } from "@opencode-ai/ui/toast"
@@ -11,9 +11,9 @@ import { createStore, reconcile } from "solid-js/store"
import { ServerHealthIndicator, ServerRow } from "@/components/server/server-row"
import { useLanguage } from "@/context/language"
import { usePlatform } from "@/context/platform"
import { useSDK } from "@/context/sdk"
import { useGlobalSDK } from "@/context/global-sdk"
import { useGlobalSync } from "@/context/global-sync"
import { normalizeServerUrl, ServerConnection, useServer } from "@/context/server"
import { useSync } from "@/context/sync"
import { useCheckServerHealth, type ServerHealth } from "@/utils/server-health"
import { DialogSelectServer } from "./dialog-select-server"
@@ -129,8 +129,10 @@ const useDefaultServerKey = (
}
const useMcpToggle = (input: {
sync: ReturnType<typeof useSync>
sdk: ReturnType<typeof useSDK>
get: () =>
| { store: ReturnType<ReturnType<typeof useGlobalSync>["child"]>[0]; set: (...args: unknown[]) => void }
| undefined
client: () => ReturnType<ReturnType<typeof useGlobalSDK>["createClient"]> | undefined
language: ReturnType<typeof useLanguage>
}) => {
const [loading, setLoading] = createSignal<string | null>(null)
@@ -139,13 +141,18 @@ const useMcpToggle = (input: {
if (loading()) return
setLoading(name)
const child = input.get()
const cli = input.client()
if (!child || !cli) {
setLoading(null)
return
}
try {
const status = input.sync.data.mcp[name]
await (status?.status === "connected"
? input.sdk.client.mcp.disconnect({ name })
: input.sdk.client.mcp.connect({ name }))
const result = await input.sdk.client.mcp.status()
if (result.data) input.sync.set("mcp", result.data)
const status = child.store.mcp[name]
await (status?.status === "connected" ? cli.mcp.disconnect({ name }) : cli.mcp.connect({ name }))
const result = await cli.mcp.status()
if (result.data) child.set("mcp", result.data)
} catch (err) {
showToast({
variant: "error",
@@ -160,76 +167,36 @@ const useMcpToggle = (input: {
return { loading, toggle }
}
export function StatusPopover() {
const sync = useSync()
const sdk = useSDK()
export function StatusModal(props: { directory: string }) {
const globalSDK = useGlobalSDK()
const globalSync = useGlobalSync()
const server = useServer()
const platform = usePlatform()
const dialog = useDialog()
const language = useLanguage()
const navigate = useNavigate()
const [shown, setShown] = createSignal(false)
const servers = createMemo(() => {
const current = server.current
const list = server.list
if (!current) return list
if (list.every((item) => ServerConnection.key(item) !== ServerConnection.key(current))) return [current, ...list]
return [current, ...list.filter((item) => ServerConnection.key(item) !== ServerConnection.key(current))]
})
const health = useServerHealth(servers)
const sortedServers = createMemo(() => listServersByHealth(servers(), server.key, health))
const mcp = useMcpToggle({ sync, sdk, language })
const defaultServer = useDefaultServerKey(platform.getDefaultServer)
const mcpNames = createMemo(() => Object.keys(sync.data.mcp ?? {}).sort((a, b) => a.localeCompare(b)))
const mcpStatus = (name: string) => sync.data.mcp?.[name]?.status
const mcpConnected = createMemo(() => mcpNames().filter((name) => mcpStatus(name) === "connected").length)
const lspItems = createMemo(() => sync.data.lsp ?? [])
const lspCount = createMemo(() => lspItems().length)
const plugins = createMemo(() => sync.data.config.plugin ?? [])
const pluginCount = createMemo(() => plugins().length)
const pluginEmpty = createMemo(() => pluginEmptyMessage(language.t("dialog.plugins.empty"), "opencode.json"))
const overallHealthy = createMemo(() => {
const serverHealthy = server.healthy() === true
const anyMcpIssue = mcpNames().some((name) => {
const status = mcpStatus(name)
return status !== "connected" && status !== "disabled"
})
return serverHealthy && !anyMcpIssue
const child = createMemo(() => {
if (!props.directory) return
const [store, set] = globalSync.child(props.directory)
return { store, set }
})
return (
<Popover
open={shown()}
onOpenChange={setShown}
triggerAs={Button}
triggerProps={{
variant: "ghost",
class: "titlebar-icon w-8 h-6 p-0 box-border",
"aria-label": language.t("status.popover.trigger"),
style: { scale: 1 },
}}
trigger={
<div class="relative size-4">
<div class="badge-mask-tight size-4 flex items-center justify-center">
<Icon name={shown() ? "status-active" : "status"} size="small" />
</div>
<div
classList={{
"absolute -top-px -right-px size-1.5 rounded-full": true,
"bg-icon-success-base": overallHealthy(),
"bg-icon-critical-base": !overallHealthy() && server.healthy() !== undefined,
"bg-border-weak-base": server.healthy() === undefined,
}}
/>
</div>
}
class="[&_[data-slot=popover-body]]:p-0 w-[360px] max-w-[calc(100vw-40px)] bg-transparent border-0 shadow-none rounded-xl"
gutter={4}
placement="bottom-end"
shift={-168}
>
<div class="flex items-center gap-1 w-[360px] rounded-xl shadow-[var(--shadow-lg-border-base)]">
const client = createMemo(() => {
if (!props.directory) return
return globalSDK.createClient({ directory: props.directory, throwOnError: true })
})
const open = () =>
dialog.show(() => (
<Dialog
title={language.t("status.popover.trigger")}
position="top"
style={{ "--dialog-top": "33vh" } as any}
fit
class="!max-h-[33vh]"
transition
>
<Tabs
aria-label={language.t("status.popover.ariaLabel")}
class="tabs bg-background-strong rounded-xl overflow-hidden"
@@ -259,7 +226,7 @@ export function StatusPopover() {
<Tabs.Content value="servers">
<div class="flex flex-col px-2 pb-2">
<div class="flex flex-col p-3 bg-background-base rounded-sm min-h-14">
<div class="flex flex-col p-3 rounded-sm min-h-[160px]">
<For each={sortedServers()}>
{(s) => {
const key = ServerConnection.key(s)
@@ -277,6 +244,7 @@ export function StatusPopover() {
if (isBlocked()) return
server.setActive(key)
navigate("/")
dialog.close()
}}
>
<ServerHealthIndicator health={health[key]} />
@@ -318,7 +286,7 @@ export function StatusPopover() {
<Tabs.Content value="mcp">
<div class="flex flex-col px-2 pb-2">
<div class="flex flex-col p-3 bg-background-base rounded-sm min-h-14">
<div class="flex flex-col p-3 rounded-sm min-h-[160px]">
<Show
when={mcpNames().length > 0}
fallback={
@@ -367,7 +335,7 @@ export function StatusPopover() {
<Tabs.Content value="lsp">
<div class="flex flex-col px-2 pb-2">
<div class="flex flex-col p-3 bg-background-base rounded-sm min-h-14">
<div class="flex flex-col p-3 rounded-sm min-h-[160px]">
<Show
when={lspItems().length > 0}
fallback={
@@ -397,7 +365,7 @@ export function StatusPopover() {
<Tabs.Content value="plugins">
<div class="flex flex-col px-2 pb-2">
<div class="flex flex-col p-3 bg-background-base rounded-sm min-h-14">
<div class="flex flex-col p-3 rounded-sm min-h-[160px]">
<Show
when={plugins().length > 0}
fallback={<div class="text-14-regular text-text-base text-center my-auto">{pluginEmpty()}</div>}
@@ -415,7 +383,69 @@ export function StatusPopover() {
</div>
</Tabs.Content>
</Tabs>
</Dialog>
))
const servers = createMemo(() => {
const current = server.current
const list = server.list
if (!current) return list
if (list.every((item) => ServerConnection.key(item) !== ServerConnection.key(current))) return [current, ...list]
return [current, ...list.filter((item) => ServerConnection.key(item) !== ServerConnection.key(current))]
})
const health = useServerHealth(servers)
const sortedServers = createMemo(() => listServersByHealth(servers(), server.key, health))
const mcp = useMcpToggle({
language,
get: () => child(),
client: () => client(),
})
const defaultServer = useDefaultServerKey(platform.getDefaultServer)
const mcpNames = createMemo(() => Object.keys(child()?.store.mcp ?? {}).sort((a, b) => a.localeCompare(b)))
const mcpStatus = (name: string) => child()?.store.mcp?.[name]?.status
const mcpConnected = createMemo(() => mcpNames().filter((name) => mcpStatus(name) === "connected").length)
const lspItems = createMemo(() => child()?.store.lsp ?? [])
const lspCount = createMemo(() => lspItems().length)
const plugins = createMemo(() => child()?.store.config.plugin ?? [])
const pluginCount = createMemo(() => plugins().length)
const pluginEmpty = createMemo(() => pluginEmptyMessage(language.t("dialog.plugins.empty"), "opencode.json"))
const overallHealthy = createMemo(() => {
const serverHealthy = server.healthy() === true
const anyMcpIssue = mcpNames().some((name) => {
const status = mcpStatus(name)
return status !== "connected" && status !== "disabled"
})
return serverHealthy && !anyMcpIssue
})
return (
<button
type="button"
data-component="icon-button"
data-icon="status"
data-variant="ghost"
data-size="large"
aria-label={language.t("status.popover.trigger")}
onClick={open}
>
<div class="relative size-full flex items-center justify-center">
<div
class="size-full flex items-center justify-center"
style={{
"-webkit-mask-image": "radial-gradient(circle 5px at calc(100% - 8px) 8px, transparent 5px, black 5.5px)",
"mask-image": "radial-gradient(circle 5px at calc(100% - 8px) 8px, transparent 5px, black 5.5px)",
}}
>
<Icon name="status" size="normal" />
</div>
<div
classList={{
"absolute top-[5px] right-[5px] size-1.5 rounded-full z-10": true,
"bg-icon-success-base": overallHealthy(),
"bg-icon-critical-base": !overallHealthy() && server.healthy() !== undefined,
"bg-border-weak-base": server.healthy() === undefined,
}}
/>
</div>
</Popover>
</button>
)
}

View File

@@ -2181,6 +2181,7 @@ export default function Layout(props: ParentProps) {
const sidebarContent = (mobile?: boolean) => (
<SidebarContent
mobile={mobile}
dir={currentDir()}
opened={() => layout.sidebar.opened()}
aimMove={aim.move}
projects={projects}
@@ -2199,6 +2200,7 @@ export default function Layout(props: ParentProps) {
onOpenSettings={openSettings}
helpLabel={() => language.t("sidebar.help")}
onOpenHelp={() => platform.openLink("https://opencode.ai/desktop-feedback")}
statusLabel={() => language.t("status.popover.trigger")}
renderPanel={() =>
mobile ? (
<SidebarPanel project={currentProject()} mobile />

View File

@@ -11,9 +11,11 @@ import { ConstrainDragXAxis } from "@/utils/solid-dnd"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { type LocalProject } from "@/context/layout"
import { StatusModal } from "@/components/status-popover"
export const SidebarContent = (props: {
mobile?: boolean
dir: string
opened: Accessor<boolean>
aimMove: (event: MouseEvent) => void
projects: Accessor<LocalProject[]>
@@ -30,6 +32,7 @@ export const SidebarContent = (props: {
onOpenSettings: () => void
helpLabel: Accessor<string>
onOpenHelp: () => void
statusLabel: Accessor<string>
renderPanel: () => JSX.Element
}): JSX.Element => {
const expanded = createMemo(() => !!props.mobile || props.opened())
@@ -90,6 +93,11 @@ export const SidebarContent = (props: {
</DragDropProvider>
</div>
<div class="shrink-0 w-full pt-3 pb-6 flex flex-col items-center gap-2">
<Show when={!!props.dir}>
<Tooltip placement={placement()} value={props.statusLabel()}>
<StatusModal directory={props.dir} />
</Tooltip>
</Show>
<TooltipKeybind placement={placement()} title={props.settingsLabel()} keybind={props.settingsKeybind() ?? ""}>
<IconButton
icon="settings-gear"

View File

@@ -16,6 +16,11 @@
justify-content: center;
pointer-events: none;
&[data-position="top"] {
align-items: flex-start;
padding-top: var(--dialog-top, 16px);
}
[data-slot="dialog-container"] {
position: relative;
z-index: 50;

View File

@@ -8,6 +8,8 @@ export interface DialogProps extends ParentProps {
description?: JSXElement
action?: JSXElement
size?: "normal" | "large" | "x-large"
position?: "center" | "top"
style?: ComponentProps<"div">["style"]
class?: ComponentProps<"div">["class"]
classList?: ComponentProps<"div">["classList"]
fit?: boolean
@@ -21,7 +23,9 @@ export function Dialog(props: DialogProps) {
data-component="dialog"
data-fit={props.fit ? true : undefined}
data-size={props.size || "normal"}
data-position={props.position && props.position !== "center" ? props.position : undefined}
data-transition={props.transition ? true : undefined}
style={props.style}
>
<div data-slot="dialog-container">
<Kobalte.Content

View File

@@ -87,8 +87,8 @@ const icons = {
server: `<rect x="3.35547" y="1.92969" width="13.2857" height="16.1429" stroke="currentColor"/><rect x="3.35547" y="11.9297" width="13.2857" height="6.14286" stroke="currentColor"/><rect x="12.8555" y="14.2852" width="1.42857" height="1.42857" fill="currentColor"/><rect x="10" y="14.2852" width="1.42857" height="1.42857" fill="currentColor"/>`,
branch: `<path d="M14.2036 7.19987L14.2079 6.69989L13.2079 6.69132L13.2036 7.1913L13.7036 7.19559L14.2036 7.19987ZM8.14804 5.09032H7.64804C7.64804 5.75797 7.06861 6.34471 6.29619 6.34471V6.84471V7.34471C7.56926 7.34471 8.64804 6.36051 8.64804 5.09032H8.14804ZM6.29619 6.84471V6.34471C5.52376 6.34471 4.94434 5.75797 4.94434 5.09032H4.44434H3.94434C3.94434 6.36051 5.02311 7.34471 6.29619 7.34471V6.84471ZM4.44434 5.09032H4.94434C4.94434 4.42267 5.52376 3.83594 6.29619 3.83594V3.33594V2.83594C5.02311 2.83594 3.94434 3.82013 3.94434 5.09032H4.44434ZM6.29619 3.33594V3.83594C7.06861 3.83594 7.64804 4.42267 7.64804 5.09032H8.14804H8.64804C8.64804 3.82013 7.56926 2.83594 6.29619 2.83594V3.33594ZM8.14804 14.9149H7.64804C7.64804 15.5825 7.06861 16.1693 6.29619 16.1693V16.6693V17.1693C7.56926 17.1693 8.64804 16.1851 8.64804 14.9149H8.14804ZM6.29619 16.6693V16.1693C5.52376 16.1693 4.94434 15.5825 4.94434 14.9149H4.44434H3.94434C3.94434 16.1851 5.02311 17.1693 6.29619 17.1693V16.6693ZM4.44434 14.9149H4.94434C4.94434 14.2472 5.52376 13.6605 6.29619 13.6605V13.1605V12.6605C5.02311 12.6605 3.94434 13.6447 3.94434 14.9149H4.44434ZM6.29619 13.1605V13.6605C7.06861 13.6605 7.64804 14.2472 7.64804 14.9149H8.14804H8.64804C8.64804 13.6447 7.56926 12.6605 6.29619 12.6605V13.1605ZM15.5554 5.09032H15.0554C15.0554 5.75797 14.476 6.34471 13.7036 6.34471V6.84471V7.34471C14.9767 7.34471 16.0554 6.36051 16.0554 5.09032H15.5554ZM13.7036 6.84471V6.34471C12.9312 6.34471 12.3517 5.75797 12.3517 5.09032H11.8517H11.3517C11.3517 6.36051 12.4305 7.34471 13.7036 7.34471V6.84471ZM11.8517 5.09032H12.3517C12.3517 4.42267 12.9312 3.83594 13.7036 3.83594V3.33594V2.83594C12.4305 2.83594 11.3517 3.82013 11.3517 5.09032H11.8517ZM13.7036 3.33594V3.83594C14.476 3.83594 15.0554 4.42267 15.0554 5.09032H15.5554H16.0554C16.0554 3.82013 14.9767 2.83594 13.7036 2.83594V3.33594ZM13.7036 7.19559L13.2036 7.1913L13.1544 12.9277L13.6544 12.932L14.1544 12.9363L14.2036 7.19987L13.7036 7.19559ZM6.29619 6.84471H5.79619V13.1605H6.29619H6.79619V6.84471H6.29619ZM11.6545 14.9149V14.4149H8.14804V14.9149V15.4149H11.6545V14.9149ZM13.6544 12.932L13.1544 12.9277C13.1474 13.7511 12.4779 14.4149 11.6545 14.4149V14.9149V15.4149C13.0269 15.4149 14.1426 14.3086 14.1544 12.9363L13.6544 12.932Z" fill="currentColor"/>`,
edit: `<path d="M17.0832 17.0807V17.5807H17.5832V17.0807H17.0832ZM2.9165 17.0807H2.4165V17.5807H2.9165V17.0807ZM2.9165 2.91406V2.41406H2.4165V2.91406H2.9165ZM9.58317 3.41406H10.0832V2.41406H9.58317V2.91406V3.41406ZM17.5832 10.4141V9.91406H16.5832V10.4141H17.0832H17.5832ZM6.24984 11.2474L5.89628 10.8938L5.74984 11.0403V11.2474H6.24984ZM6.24984 13.7474H5.74984V14.2474H6.24984V13.7474ZM8.74984 13.7474V14.2474H8.95694L9.10339 14.101L8.74984 13.7474ZM15.2082 2.28906L15.5617 1.93551L15.2082 1.58196L14.8546 1.93551L15.2082 2.28906ZM17.7082 4.78906L18.0617 5.14262L18.4153 4.78906L18.0617 4.43551L17.7082 4.78906ZM17.0832 17.0807V16.5807H2.9165V17.0807V17.5807H17.0832V17.0807ZM2.9165 17.0807H3.4165V2.91406H2.9165H2.4165V17.0807H2.9165ZM2.9165 2.91406V3.41406H9.58317V2.91406V2.41406H2.9165V2.91406ZM17.0832 10.4141H16.5832V17.0807H17.0832H17.5832V10.4141H17.0832ZM6.24984 11.2474H5.74984V13.7474H6.24984H6.74984V11.2474H6.24984ZM6.24984 13.7474V14.2474H8.74984V13.7474V13.2474H6.24984V13.7474ZM6.24984 11.2474L6.60339 11.6009L15.5617 2.64262L15.2082 2.28906L14.8546 1.93551L5.89628 10.8938L6.24984 11.2474ZM15.2082 2.28906L14.8546 2.64262L17.3546 5.14262L17.7082 4.78906L18.0617 4.43551L15.5617 1.93551L15.2082 2.28906ZM17.7082 4.78906L17.3546 4.43551L8.39628 13.3938L8.74984 13.7474L9.10339 14.101L18.0617 5.14262L17.7082 4.78906Z" fill="currentColor"/>`,
help: `<path d="M7.91683 7.91927V6.2526H12.0835V8.7526L10.0002 10.0026V12.0859M10.0002 13.7526V13.7609M17.9168 10.0026C17.9168 14.3749 14.3724 17.9193 10.0002 17.9193C5.62791 17.9193 2.0835 14.3749 2.0835 10.0026C2.0835 5.63035 5.62791 2.08594 10.0002 2.08594C14.3724 2.08594 17.9168 5.63035 17.9168 10.0026Z" stroke="currentColor" stroke-linecap="square"/>`,
"settings-gear": `<path d="M7.62516 4.46094L5.05225 3.86719L3.86475 5.05469L4.4585 7.6276L2.0835 9.21094V10.7943L4.4585 12.3776L3.86475 14.9505L5.05225 16.138L7.62516 15.5443L9.2085 17.9193H10.7918L12.3752 15.5443L14.9481 16.138L16.1356 14.9505L15.5418 12.3776L17.9168 10.7943V9.21094L15.5418 7.6276L16.1356 5.05469L14.9481 3.86719L12.3752 4.46094L10.7918 2.08594H9.2085L7.62516 4.46094Z" stroke="currentColor"/><path d="M12.5002 10.0026C12.5002 11.3833 11.3809 12.5026 10.0002 12.5026C8.61945 12.5026 7.50016 11.3833 7.50016 10.0026C7.50016 8.62189 8.61945 7.5026 10.0002 7.5026C11.3809 7.5026 12.5002 8.62189 12.5002 10.0026Z" stroke="currentColor"/>`,
help: `<path d="M17 10C17 6.13401 13.866 3 10 3C6.13401 3 3 6.13401 3 10C3 13.866 6.13401 17 10 17C13.866 17 17 13.866 17 10ZM18 10C18 14.4183 14.4183 18 10 18C5.58172 18 2 14.4183 2 10C2 5.58172 5.58172 2 10 2C14.4183 2 18 5.58172 18 10Z" fill="currentColor"/><path d="M9.26936 13.6022H10.6094V15H9.26936V13.6022ZM7 8.21909C7 7.25134 7.27385 6.47401 7.82155 5.8871C8.36925 5.2957 9.12121 5 10.0774 5C10.9618 5 11.6689 5.25314 12.1987 5.75941C12.7329 6.2612 13 6.90412 13 7.68817C13 8.16308 12.9012 8.54839 12.7037 8.84409C12.5107 9.13979 12.1178 9.57437 11.5253 10.1478C11.0943 10.5645 10.8137 10.9185 10.6835 11.2097C10.5578 11.4964 10.4949 11.922 10.4949 12.4866H9.2963C9.2963 11.8459 9.37261 11.3306 9.52525 10.9409C9.67789 10.5466 10.0123 10.0963 10.5286 9.59005L11.0673 9.05914C11.229 8.90681 11.3591 8.74776 11.4579 8.58199C11.6375 8.29077 11.7273 7.98835 11.7273 7.67473C11.7273 7.23566 11.5948 6.85484 11.33 6.53226C11.0696 6.20968 10.6364 6.04839 10.0303 6.04839C9.28058 6.04839 8.76207 6.32616 8.47475 6.88172C8.31313 7.19086 8.2211 7.63665 8.19865 8.21909H7Z" fill="currentColor"/>`,
"settings-gear": `<path d="M10.0556 1.5L17.6112 5.74998V14.2501L10.0556 18.5L2.5 14.2503L2.5 5.74996L10.0556 1.5Z" stroke="currentColor" stroke-linecap="square"/><path d="M12.889 10.0001C12.889 11.5649 11.6205 12.8334 10.0556 12.8334C8.49083 12.8334 7.22229 11.5649 7.22229 10.0001C7.22229 8.43523 8.49083 7.1667 10.0556 7.1667C11.6205 7.1667 12.889 8.43523 12.889 10.0001Z" stroke="currentColor" stroke-linecap="square"/>`,
dash: `<rect x="5" y="9.5" width="10" height="1" fill="currentColor"/>`,
"cloud-upload": `<path d="M12.0833 16.25H15C17.0711 16.25 18.75 14.5711 18.75 12.5C18.75 10.5649 17.2843 8.97217 15.4025 8.77133C15.2 6.13103 12.8586 4.08333 10 4.08333C7.71532 4.08333 5.76101 5.49781 4.96501 7.49881C2.84892 7.90461 1.25 9.76559 1.25 11.6667C1.25 13.9813 3.30203 16.25 5.83333 16.25H7.91667M10 16.25V10.4167M12.0833 11.875L10 9.79167L7.91667 11.875" stroke="currentColor" stroke-linecap="square"/>`,
trash: `<path d="M4.58342 17.9134L4.58369 17.4134L4.22787 17.5384L4.22766 18.0384H4.58342V17.9134ZM15.4167 17.9134V18.0384H15.7725L15.7723 17.5384L15.4167 17.9134ZM2.08342 3.95508V3.45508H1.58342V3.95508H2.08342V4.45508V3.95508ZM17.9167 4.45508V4.95508H18.4167V4.45508H17.9167V3.95508V4.45508ZM4.16677 4.58008L3.66701 4.5996L4.22816 17.5379L4.72792 17.4934L5.22767 17.4489L4.66652 4.54055L4.16677 4.58008ZM4.58342 18.0384V17.9134H15.4167V18.0384V18.5384H4.58342V18.0384ZM15.4167 17.9134L15.8332 17.5379L16.2498 4.5996L15.7501 4.58008L15.2503 4.56055L14.8337 17.4989L15.4167 17.9134ZM15.8334 4.58008V4.08008H4.16677V4.58008V5.08008H15.8334V4.58008ZM2.08342 4.45508V4.95508H4.16677V4.58008V4.08008H2.08342V4.45508ZM15.8334 4.58008V5.08008H17.9167V4.45508V3.95508H15.8334V4.58008ZM6.83951 4.35149L7.432 4.55047C7.79251 3.47701 8.80699 2.70508 10.0001 2.70508V2.20508V1.70508C8.25392 1.70508 6.77335 2.83539 6.24702 4.15251L6.83951 4.35149ZM10.0001 2.20508V2.70508C11.1932 2.70508 12.2077 3.47701 12.5682 4.55047L13.1607 4.35149L13.7532 4.15251C13.2269 2.83539 11.7463 1.70508 10.0001 1.70508V2.20508Z" fill="currentColor"/>`,