The tray's right-click menu is drawn by the app: one tree from tray.rs (stable submenu keys), one frameless always-on-top menu-N window per panel rendered by tray-menu.html with the design system, placed inside the monitor's work area. A pure panel book keeps transient and pinned panels apart; pinned panels (pin button, Ctrl+click, middle click) are draggable, step aside from each other and are saved in state.json (menuPins) and restored at start. tray_update pushes the tree to the open panels, which update in place; closed panels are destroyed. The native menu, rendered from the same tree, stays as the fallback and on Linux. Only menu-* windows get the menu_* commands (menu.json). In-app context menus get the same behaviour through a pure menu stack (ui/menu-stack.ts): pin a menu, tear off a submenu, pinned panels survive the next menu, are draggable and re-read their entries from live sources so data changes update them in place. Also drops a stray diff3 marker line from CHANGELOG.md.
115 lines
8.4 KiB
TypeScript
115 lines
8.4 KiB
TypeScript
// Icons come from Phosphor (@phosphor-icons/react, MIT, https://phosphoricons.com): one per-icon import each
|
|
// (dist/csr/<Name>) so Vite bundles only the icons used. The names below are the app's own (call sites use
|
|
// <Icon name="..." />); each maps to its closest Phosphor icon. One weight everywhere ("regular"); "fill" only where it
|
|
// marks state (the active nav item, notices). Decorative unless a title is given. No hand-drawn SVG in the app: see
|
|
// docs/desktop.md.
|
|
import { ArrowCounterClockwiseIcon } from '@phosphor-icons/react/dist/csr/ArrowCounterClockwise';
|
|
import { ArrowLeftIcon } from '@phosphor-icons/react/dist/csr/ArrowLeft';
|
|
import { ArrowRightIcon } from '@phosphor-icons/react/dist/csr/ArrowRight';
|
|
import { ArrowSquareOutIcon } from '@phosphor-icons/react/dist/csr/ArrowSquareOut';
|
|
import { ArrowsClockwiseIcon } from '@phosphor-icons/react/dist/csr/ArrowsClockwise';
|
|
import { BellIcon } from '@phosphor-icons/react/dist/csr/Bell';
|
|
import { BuildingsIcon } from '@phosphor-icons/react/dist/csr/Buildings';
|
|
import { CaretDownIcon } from '@phosphor-icons/react/dist/csr/CaretDown';
|
|
import { CaretRightIcon } from '@phosphor-icons/react/dist/csr/CaretRight';
|
|
import { ChatTextIcon } from '@phosphor-icons/react/dist/csr/ChatText';
|
|
import { CheckIcon } from '@phosphor-icons/react/dist/csr/Check';
|
|
import { ClipboardTextIcon } from '@phosphor-icons/react/dist/csr/ClipboardText';
|
|
import { ClockIcon } from '@phosphor-icons/react/dist/csr/Clock';
|
|
import { CloudIcon } from '@phosphor-icons/react/dist/csr/Cloud';
|
|
import { CoinsIcon } from '@phosphor-icons/react/dist/csr/Coins';
|
|
import { CommandIcon } from '@phosphor-icons/react/dist/csr/Command';
|
|
import { CopyIcon } from '@phosphor-icons/react/dist/csr/Copy';
|
|
import { CubeIcon } from '@phosphor-icons/react/dist/csr/Cube';
|
|
import { DatabaseIcon } from '@phosphor-icons/react/dist/csr/Database';
|
|
import { DeviceMobileIcon } from '@phosphor-icons/react/dist/csr/DeviceMobile';
|
|
import { DotIcon } from '@phosphor-icons/react/dist/csr/Dot';
|
|
import { DownloadSimpleIcon } from '@phosphor-icons/react/dist/csr/DownloadSimple';
|
|
import { ExportIcon } from '@phosphor-icons/react/dist/csr/Export';
|
|
import { DotsThreeIcon } from '@phosphor-icons/react/dist/csr/DotsThree';
|
|
import { GaugeIcon } from '@phosphor-icons/react/dist/csr/Gauge';
|
|
import { EyeIcon } from '@phosphor-icons/react/dist/csr/Eye';
|
|
import { EyeSlashIcon } from '@phosphor-icons/react/dist/csr/EyeSlash';
|
|
import { FileTextIcon } from '@phosphor-icons/react/dist/csr/FileText';
|
|
import { FlaskIcon } from '@phosphor-icons/react/dist/csr/Flask';
|
|
import { FolderIcon } from '@phosphor-icons/react/dist/csr/Folder';
|
|
import { FolderPlusIcon } from '@phosphor-icons/react/dist/csr/FolderPlus';
|
|
import { FunnelIcon } from '@phosphor-icons/react/dist/csr/Funnel';
|
|
import { GearSixIcon } from '@phosphor-icons/react/dist/csr/GearSix';
|
|
import { GlobeIcon } from '@phosphor-icons/react/dist/csr/Globe';
|
|
import { GridFourIcon } from '@phosphor-icons/react/dist/csr/GridFour';
|
|
import { ImageIcon } from '@phosphor-icons/react/dist/csr/Image';
|
|
import { InfoIcon } from '@phosphor-icons/react/dist/csr/Info';
|
|
import { KeyIcon } from '@phosphor-icons/react/dist/csr/Key';
|
|
import { KeyboardIcon } from '@phosphor-icons/react/dist/csr/Keyboard';
|
|
import { LinkIcon } from '@phosphor-icons/react/dist/csr/Link';
|
|
import { LinkBreakIcon } from '@phosphor-icons/react/dist/csr/LinkBreak';
|
|
import { LockIcon } from '@phosphor-icons/react/dist/csr/Lock';
|
|
import { MagicWandIcon } from '@phosphor-icons/react/dist/csr/MagicWand';
|
|
import { MagnifyingGlassIcon } from '@phosphor-icons/react/dist/csr/MagnifyingGlass';
|
|
import { MoonIcon } from '@phosphor-icons/react/dist/csr/Moon';
|
|
import { PackageIcon } from '@phosphor-icons/react/dist/csr/Package';
|
|
import { PaperPlaneRightIcon } from '@phosphor-icons/react/dist/csr/PaperPlaneRight';
|
|
import { PaperPlaneTiltIcon } from '@phosphor-icons/react/dist/csr/PaperPlaneTilt';
|
|
import { PauseIcon } from '@phosphor-icons/react/dist/csr/Pause';
|
|
import { PencilSimpleIcon } from '@phosphor-icons/react/dist/csr/PencilSimple';
|
|
import { PlayIcon } from '@phosphor-icons/react/dist/csr/Play';
|
|
import { PlusIcon } from '@phosphor-icons/react/dist/csr/Plus';
|
|
import { PushPinIcon } from '@phosphor-icons/react/dist/csr/PushPin';
|
|
import { PushPinSlashIcon } from '@phosphor-icons/react/dist/csr/PushPinSlash';
|
|
import { PulseIcon } from '@phosphor-icons/react/dist/csr/Pulse';
|
|
import { QuestionIcon } from '@phosphor-icons/react/dist/csr/Question';
|
|
import { RocketLaunchIcon } from '@phosphor-icons/react/dist/csr/RocketLaunch';
|
|
import { ShieldCheckIcon } from '@phosphor-icons/react/dist/csr/ShieldCheck';
|
|
import { SlidersHorizontalIcon } from '@phosphor-icons/react/dist/csr/SlidersHorizontal';
|
|
import { SparkleIcon } from '@phosphor-icons/react/dist/csr/Sparkle';
|
|
import { SquaresFourIcon } from '@phosphor-icons/react/dist/csr/SquaresFour';
|
|
import { StackIcon } from '@phosphor-icons/react/dist/csr/Stack';
|
|
import { StarIcon } from '@phosphor-icons/react/dist/csr/Star';
|
|
import { SunIcon } from '@phosphor-icons/react/dist/csr/Sun';
|
|
import { TagIcon } from '@phosphor-icons/react/dist/csr/Tag';
|
|
import { TerminalWindowIcon } from '@phosphor-icons/react/dist/csr/TerminalWindow';
|
|
import { TrashIcon } from '@phosphor-icons/react/dist/csr/Trash';
|
|
import { TrendUpIcon } from '@phosphor-icons/react/dist/csr/TrendUp';
|
|
import { UploadSimpleIcon } from '@phosphor-icons/react/dist/csr/UploadSimple';
|
|
import { UsersIcon } from '@phosphor-icons/react/dist/csr/Users';
|
|
import { WarningIcon } from '@phosphor-icons/react/dist/csr/Warning';
|
|
import { WarningCircleIcon } from '@phosphor-icons/react/dist/csr/WarningCircle';
|
|
import { XIcon } from '@phosphor-icons/react/dist/csr/X';
|
|
import type { Icon as PhosphorIcon, IconWeight } from '@phosphor-icons/react/lib';
|
|
|
|
const ICONS = {
|
|
portfolio: SquaresFourIcon, folder: FolderIcon, folderPlus: FolderPlusIcon, key: KeyIcon, package: PackageIcon,
|
|
settings: GearSixIcon, search: MagnifyingGlassIcon, refresh: ArrowsClockwiseIcon, chevronRight: CaretRightIcon,
|
|
chevronDown: CaretDownIcon, plus: PlusIcon, x: XIcon, check: CheckIcon, alert: WarningIcon, alertCircle: WarningCircleIcon,
|
|
info: InfoIcon, external: ArrowSquareOutIcon, download: DownloadSimpleIcon, trash: TrashIcon, star: StarIcon,
|
|
message: ChatTextIcon, activity: PulseIcon, tag: TagIcon, text: FileTextIcon, plane: PaperPlaneTiltIcon,
|
|
rocket: RocketLaunchIcon, shield: ShieldCheckIcon, clock: ClockIcon, command: CommandIcon, globe: GlobeIcon, moon: MoonIcon,
|
|
sun: SunIcon, terminal: TerminalWindowIcon, copy: CopyIcon, image: ImageIcon, users: UsersIcon, layers: StackIcon,
|
|
sparkle: SparkleIcon, pause: PauseIcon, cube: CubeIcon, arrowRight: ArrowRightIcon, arrowLeft: ArrowLeftIcon, dot: DotIcon,
|
|
phone: DeviceMobileIcon, keyboard: KeyboardIcon, database: DatabaseIcon, wand: MagicWandIcon, building: BuildingsIcon,
|
|
more: DotsThreeIcon, gauge: GaugeIcon, eye: EyeIcon, eyeOff: EyeSlashIcon, link: LinkIcon, unlink: LinkBreakIcon, bell: BellIcon, filter: FunnelIcon, play: PlayIcon,
|
|
send: PaperPlaneRightIcon, edit: PencilSimpleIcon, upload: UploadSimpleIcon, sliders: SlidersHorizontalIcon,
|
|
rotate: ArrowCounterClockwiseIcon, cloud: CloudIcon, coins: CoinsIcon, chart: TrendUpIcon, flask: FlaskIcon, grid: GridFourIcon,
|
|
clipboard: ClipboardTextIcon, share: ExportIcon, lock: LockIcon, question: QuestionIcon, pin: PushPinIcon, pinOff: PushPinSlashIcon,
|
|
} satisfies Record<string, PhosphorIcon>;
|
|
|
|
export type IconName = keyof typeof ICONS;
|
|
export type { IconWeight };
|
|
|
|
export function Icon({ name, className, title, weight = 'regular' }: { name: IconName; className?: string; title?: string; weight?: IconWeight }) {
|
|
const C = ICONS[name];
|
|
return title
|
|
? <C className={className} weight={weight} role="img" aria-label={title}><title>{title}</title></C>
|
|
: <C className={className} weight={weight} aria-hidden="true" />;
|
|
}
|
|
|
|
/** The stores' own marks (simple-icons data, CC0; vendored by tools/site-icons.ts into ./store-marks and shown as a
|
|
* CSS mask in the text colour, styles.css .store-mark). They only name the store next to its name, in the app's
|
|
* neutral ink, never as storebound's own branding. App Store is a trademark of Apple Inc.; Google Play is a trademark
|
|
* of Google LLC. */
|
|
export function StoreGlyph({ store, className }: { store: 'ios' | 'android'; className?: string }) {
|
|
return <span className={`store-mark ${store === 'ios' ? 'sm-appstore' : 'sm-googleplay'}${className ? ` ${className}` : ''}`} aria-hidden="true" />;
|
|
}
|
|
|
|
export { Mark } from './brand';
|