/** * The sidebar shell: ONE surface — the bottom workbench docked to the * conversation column. * * DSH 0.1.5 owns the right column: its native Sidebar hosts every tab type * this plugin registers (client/native/), so this shell renders no right * panel of its own. What remains plugin-owned is the bottom workbench: it * spans ONLY the AppFrame's center column (the agent output area), from the * app shell's own left sidebar to the details column's left edge, so neither * sidebar gives up any position. Its height drags from its top edge, and the * expand/collapse control is a header button registered into DSH's * `conversation.session.header.utilities` list slot * (sidebar/bottom-toggle.tsx) — the header's right corner belongs to the * native sidebar's own expand control. * * The panel is mounted inside the unified panel host — a fixed, * viewport-sized containing block ([data-dsh-panel-host]) appended to * document.body — instead of a fixed-position element, so a desktop shell's * intermediate wrapper transforms can never hijack its containing block. * The whole layout lives in the per-session store, so switching * conversations swaps the workbench. * * The shell binds the workbench actions to the store and dispatches tab * content to the views. New tabs come from the + menu (explorer / git / * terminal; editors open from the explorer). */ import { useCallback, useEffect, useMemo, useRef, useState, type DragEvent as ReactDragEvent, type ReactNode } from 'react' import { useSyncExternalStore } from 'react' import clsx from 'clsx' import { IconCloseFill14, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives' import type { Context } from '../context-types.ts' import { referenceInChat as referenceInChatShared } from './reference-in-chat.ts' import { BOTTOM_MIN, CONVERSATION_MIN, agentUuidOf, firstLeaf, isAgentTabId, leafWithTab, moveTab, moveTabToEdge, openDiffTab, resizeSplitIn, setBottomHeight, setTabPin, toggleBottomPanel, toggleExpanded, type DropZone, type SidebarStore, type SidebarTab, } from './state.ts' import { getPinnedHomeScope } from './pinned.ts' import { IconPanelBottomOutline16 } from './icons.tsx' import { Workbench, type WorkbenchActions } from './split-pane.tsx' import { useViewportSize } from './breakpoints.ts' import { bottomPushHeight } from './layout-push.ts' import { parseDesktopEnv } from './desktop-env.ts' import { getWcoSnapshot, subscribeWco } from './wco.ts' import { getShellPreset } from './shell-presets.ts' import { computeTitleBarStrip } from './titlebar-strip.ts' import { TabContent, buildNewTabOptions } from './sidebar/TabContent.tsx' import { useCenterColumn } from './sidebar/use-center-column.ts' import { useHostFeeds } from './sidebar/use-host-feeds.ts' import { usePinnedTabs } from './sidebar/use-pinned-tabs.ts' import type { TabDragPayload } from './TabBar.tsx' import { t } from './locales.ts' import { api } from './api.ts' import css from './sidebar.module.css' /** * OS file drags over the sidebar belong to the sidebar, not to the chat: * DSH's composer (InputBar) listens for file drags on the DOCUMENT and * answers with a full-screen "drop image here" mask plus image intake on * drop. The panel host swallows the whole event quartet — * enter/over/leave/drop — so the region is a black hole to that document * listener. All four must be stopped: InputBar keeps an enter/leave depth * counter, and a leave that escapes without its matching enter unbalances * the count (this was the full-screen mask flickering over the sidebar). * The conversation column keeps DSH's native overlay and intake untouched; * gated on the 'Files' type so in-app drags (tab reorder, split zones) * propagate exactly as before. */ const swallowOsFileDrag = (event: ReactDragEvent): void => { if (!(event.dataTransfer?.types.includes('Files') ?? false)) return event.preventDefault() event.stopPropagation() } /** The four drag events a file drag must never carry past the panel host. */ const osFileDragShield = { onDragEnter: swallowOsFileDrag, onDragOver: swallowOsFileDrag, onDragLeave: swallowOsFileDrag, onDrop: swallowOsFileDrag, } /** * Append one user-space stylesheet (preset or custom CSS) as a tagged * `