Files
apes-sidebar/src/client/icons.tsx
T
Atlas Zeng 22a5d372be feat(editor): 文件树「在应用中打开」子菜单(外部编辑器/图钉固定/SSH 远端/自定义编辑器) (#334)
* feat(editor): file tree 'open with' submenu (external editors, pin, SSH-remote, custom editors)

The file tree right-click menu gains an 'Open with >' submenu with the
resource manager (reveal in file manager), VS Code, Cursor and Zed, plus
user-defined custom editors (name + URL template + VSCode-family flag).
Each submenu row carries a pushpin that promotes the target to a direct
top-level menu item.

A configurable SSH host (settings: Files card gear) switches VSCode-family
openers to <scheme>://vscode-remote/ssh-remote+<host>/<path> and hides
host-local targets (file manager / Zed / non-VSCode custom editors).

External opens run through the new fenced /sidebar/api/open.external route
which spawns the platform opener with an argv array (no shell); the client
never navigates raw scheme URLs itself. The settings seam's render panel now
appends after the declarative rows instead of replacing them.

* feat(editor): polish open-with menu UI — brand icons, pushpin, chevron, row alignment

- Submenu rows: the custom label is now a block-level flex row filling the
  primitives itemLabel, so the pushpin docks at the row's far right (matching
  the trailing check of every other menu); menu row icons normalized to 16px
  (DSH native menus use 16, ours were 14).
- The submenu parent row gains a right-aligned chevron (the primitives Menu
  renders no arrow for submenu parents).
- Icons via react-icons: codicons VscPin/VscPinned pushpins (replacing the
  hand-drawn pin), VscFile/VscFolder/VscFolderOpened tree glyphs (replacing
  the </> file mark), VscLinkExternal for the open-with parent and the
  browser tab's open-external button (globally), SiCursor/SiZedindustries
  brand silhouettes; the VS Code mark is inlined from simple-icons@11.0.0
  (CC0) as IconVscode16 since later simple-icons releases dropped it. All
  monochrome currentColor to follow the active skin.
- tsdown: pin react-icons si/vsc to their ESM entries via resolve.alias —
  the package's exports map lists require before import, so the shared
  conditionNames picked the unshakeable CJS entry and bloated the core
  bundle by ~6.4 MB; tree-shaken it now adds only ~12 KB.
2026-08-22 23:19:17 +08:00

153 lines
9.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Icons the sidebar needs beyond the primitives set: a terminal glyph (the
* icon library has none), a diff glyph, and the two panel-toggle glyphs for
* the top-right cluster. Per-tab icons live on the tab descriptors
* (`descriptor.icon`), not in a type-keyed switch — the icon mapping was
* registry-ized with the tab types.
*/
import type { IconProps } from '@deepseek-ai/dsh-client-ui-primitives'
/**
* Right-panel toggle glyph (the "侧拉" button): a frame with a filled strip
* along its RIGHT edge, in the app's outline style (1.5px stroke,
* currentColor).
*/
export const IconPanelRightOutline16 = ({ size = 16, className }: IconProps) => (
<svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.5" y="2" width="13" height="12" rx="2.5" stroke="currentColor" strokeWidth="1.5" />
<rect x="10.5" y="3.25" width="2.75" height="9.5" rx="1" fill="currentColor" stroke="none" />
</svg>
)
/**
* Bottom-panel toggle glyph (the "底栏" button): a frame with a filled strip
* along its BOTTOM edge, in the app's outline style.
*/
export const IconPanelBottomOutline16 = ({ size = 16, className }: IconProps) => (
<svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.5" y="2" width="13" height="12" rx="2.5" stroke="currentColor" strokeWidth="1.5" />
<rect x="3.25" y="10" width="9.5" height="2.75" rx="1" fill="currentColor" stroke="none" />
</svg>
)
/**
* Terminal glyph in the app's outline style (1.5px stroke, currentColor):
* a rounded frame with a prompt chevron and underscore cursor.
*/
export const IconTerminalOutline16 = ({ size = 16, className }: IconProps) => (
<svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.5" y="2.5" width="13" height="11" rx="2" stroke="currentColor" strokeWidth="1.5" />
<path d="M4.5 6.25 6.75 8 4.5 9.75" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M8.5 10.4h3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
</svg>
)
/** Diff glyph in the app's outline style: a file frame with a plus and a minus row. */
export const IconDiffOutline16 = ({ size = 16, className }: IconProps) => (
<svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.5" y="1.5" width="13" height="13" rx="2.5" stroke="currentColor" strokeWidth="1.5" />
<path d="M4 5h3M5.5 3.5v3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
<path d="M9.5 12.5h2.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
</svg>
)
/**
* Stop glyph for the background-job kill button: a filled square in the
* app's outline scale (16), the universal "halt this work" mark.
*/
export const IconStopOutline16 = ({ size = 16, className }: IconProps) => (
<svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="4" y="4" width="8" height="8" rx="1.5" fill="currentColor" stroke="none" />
</svg>
)
/** Upload glyph in the app's outline style: an arrow rising into a tray
* (the file-manager "upload into the workspace" action). */
export const IconUploadOutline16 = ({ size = 16, className }: IconProps) => (
<svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 10V2.75M4.75 5.5 8 2.25 11.25 5.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M2.75 10.5v2.25A1.25 1.25 0 0 0 4 14h8a1.25 1.25 0 0 0 1.25-1.25V10.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
</svg>
)
// ── File-viewer inventory glyphs (Side card settings page) ────────────────
/** Image viewer glyph: a picture frame with a sun and a mountain. */
export const IconImageOutline16 = ({ size = 16, className }: IconProps) => (
<svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.5" y="2.5" width="13" height="11" rx="2" stroke="currentColor" strokeWidth="1.5" />
<circle cx="5.5" cy="6" r="1.2" stroke="currentColor" strokeWidth="1.5" />
<path d="m3.5 12 3-3 2.25 2.25L11.5 8.5 13 10.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
/** PDF viewer glyph: a document frame with the "PDF" label. */
export const IconPdfOutline16 = ({ size = 16, className }: IconProps) => (
<svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.5 1.5h6.5L13.5 5v9.5h-10z" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" />
<path d="M9.5 1.5V5h4" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" />
<path d="M5 13.5v-3h1.4c.75 0 1.1.32 1.1.85 0 .54-.35.85-1.1.85H5.3" stroke="currentColor" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" />
<path d="M8.3 13.5v-3h1.05c.8 0 1.35.5 1.35 1.5s-.55 1.5-1.35 1.5z" stroke="currentColor" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" />
<path d="M11.6 13.5v-3h1.3" stroke="currentColor" strokeWidth="1.25" strokeLinecap="round" />
</svg>
)
/** Markdown viewer glyph: the classic "M with a down arrow" badge. */
export const IconMarkdownOutline16 = ({ size = 16, className }: IconProps) => (
<svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.5" y="2.5" width="13" height="11" rx="2" stroke="currentColor" strokeWidth="1.5" />
<path d="M4 10.5V5.5l2 2.5 2-2.5v5M9.5 10.5v-5l2 2.5 2-2.5v5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
/** HTML viewer glyph: a document frame with a "‹/›" tag pair. */
export const IconHtmlOutline16 = ({ size = 16, className }: IconProps) => (
<svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.5 1.5h6.5L13.5 5v9.5h-10z" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" />
<path d="M9.5 1.5V5h4" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" />
<path d="M5.6 13.2 4.2 10l1.4-3.2M7.4 6.8 8.8 10l-1.4 3.2" stroke="currentColor" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
/** Browser tab glyph: a globe with meridians. */
export const IconGlobeOutline16 = ({ size = 16, className }: IconProps) => (
<svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="8" cy="8" r="6.5" stroke="currentColor" strokeWidth="1.5" />
<ellipse cx="8" cy="8" rx="2.8" ry="6.5" stroke="currentColor" strokeWidth="1.5" />
<path d="M1.5 8h13M8 1.5c-2.4 1.8-2.4 11.2 0 13M8 1.5c2.4 1.8 2.4 11.2 0 13" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
</svg>
)
/** History glyph (thread switcher): a clock with a counterclockwise arrow,
* in the app's outline style — the "past conversations" mark. */
export const IconHistoryOutline16 = ({ size = 16, className }: IconProps) => (
<svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.4 6.8A5.6 5.6 0 1 1 2.4 9.2" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
<path d="M2.2 3.4v3.4h3.4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M8 5.4V8l1.9 1.2" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
/** Save glyph (save-as-new-session): the classic floppy disk, in the app's
* outline style. */
export const IconSaveOutline16 = ({ size = 16, className }: IconProps) => (
<svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.2 14.5h7.6a1.2 1.2 0 0 0 1.2-1.2V4.9L10.6 2.5H4.2A1.2 1.2 0 0 0 3 3.7v9.6a1.2 1.2 0 0 0 1.2 1.2z" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" />
<path d="M10 2.5v2.6H5.6V2.5" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" />
<path d="M5.4 14.5v-4.2h5.2v4.2" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" />
</svg>
)
/**
* Visual Studio Code brand mark for the file-tree "open with" menu. The
* path is the Simple Icons `visualstudiocode` glyph (CC0 1.0,
* simple-icons@11.0.0 — later releases dropped it over Microsoft's brand
* policy, so it is inlined here rather than pulled from react-icons),
* rendered monochrome via currentColor to follow the active skin.
*/
export const IconVscode16 = ({ size = 16, className }: IconProps) => (
<svg width={size} height={size} className={className} viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M23.15 2.587L18.21.21a1.494 1.494 0 0 0-1.705.29l-9.46 8.63-4.12-3.128a.999.999 0 0 0-1.276.057L.327 7.261A1 1 0 0 0 .326 8.74L3.899 12 .326 15.26a1 1 0 0 0 .001 1.479L1.65 17.94a.999.999 0 0 0 1.276.057l4.12-3.128 9.46 8.63a1.492 1.492 0 0 0 1.704.29l4.942-2.377A1.5 1.5 0 0 0 24 20.06V3.939a1.5 1.5 0 0 0-.85-1.352zm-5.146 14.861L10.826 12l7.178-5.448v10.896z" />
</svg>
)