@anionex/dsh-pinned-sessions 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/index.tsx","../node_modules/shared/src/utils.ts","../node_modules/lucide-react/src/defaultAttributes.ts","../node_modules/lucide-react/src/Icon.ts","../node_modules/lucide-react/src/createLucideIcon.ts","../node_modules/lucide-react/src/icons/pin.ts","../node_modules/lucide-react/src/icons/x.ts","../node_modules/lucide/src/defaultAttributes.ts","../node_modules/lucide/src/icons/pin-off.ts","../node_modules/lucide/src/icons/pin.ts","../src/client/dom-bridge.ts","../src/client/pin-store.ts","../src/client/presentation.ts"],"sourcesContent":["import {\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type ReactNode,\n} from 'react'\nimport { Pin, X } from 'lucide-react'\nimport { createPortal } from 'react-dom'\nimport {\n attachSessionMenuHost,\n captureSessionId,\n ensurePinnedHost,\n findSessionActionTarget,\n findUnclaimedPortalMenu,\n focusAfterPinnedRemoval,\n listPortalMenus,\n MENU_OWNER_ATTRIBUTE,\n removeBridgeArtifacts,\n SIDEBAR_SLOT_SELECTOR,\n updateSessionMenuItem,\n type SessionMenuTarget,\n type TriggerRect,\n type SessionsForCapture,\n} from './dom-bridge.js'\nimport { PIN_STORAGE_KEY, PinStore } from './pin-store.js'\nimport { sessionDisplayTitle } from './presentation.js'\n\ninterface SessionSummaryLike {\n readonly id: string\n readonly title?: string | undefined\n readonly cwd?: string | undefined\n readonly blank?: boolean | undefined\n readonly running?: boolean | undefined\n readonly completed?: boolean | undefined\n}\n\ninterface SessionsSnapshotLike {\n readonly ids: readonly string[]\n readonly byId: Readonly<Record<string, SessionSummaryLike | undefined>>\n readonly current?: string | undefined\n readonly phase: string\n}\n\ninterface WorkspaceLike {\n readonly workspaceId: string\n readonly title: string\n readonly sessionIds: readonly string[]\n}\n\ninterface WorkspacesSnapshotLike {\n readonly items: readonly WorkspaceLike[]\n readonly archivedSessionIds: readonly string[]\n readonly phase: string\n}\n\ntype SelectorHook<T> = <S>(selector: (snapshot: T) => S, equal?: (left: S, right: S) => boolean) => S\ntype Translate = (key: string, params?: Readonly<Record<string, unknown>>) => string\n\ninterface SlotsLike {\n inject(name: string, install: () => unknown): void\n register<I, P>(\n entry: {\n readonly name: string\n readonly id: string\n readonly order?: number\n readonly locale?: string\n readonly inject?: () => I\n },\n component: (props: P) => ReactNode,\n ): () => void\n}\n\ninterface LocaleLike {\n register(namespace: string, dictionaries: Readonly<Record<string, Readonly<Record<string, string>>>>): () => void\n}\n\ninterface ClientContextLike {\n readonly slots: SlotsLike\n readonly sessions: SessionsForCapture\n readonly locale: LocaleLike\n effect(setup: () => (() => void), label?: string): unknown\n}\n\ninterface BridgeProps {\n readonly store: PinStore\n readonly sessions: SessionsForCapture\n readonly useSessions: SelectorHook<SessionsSnapshotLike>\n readonly useWorkspaces: SelectorHook<WorkspacesSnapshotLike>\n readonly t: Translate\n}\n\ninterface PinIconProps {\n readonly size?: number\n}\n\ninterface PendingMenuRequest {\n readonly sessionId: string\n readonly createdAt: number\n readonly existingMenus: ReadonlySet<HTMLElement>\n readonly triggerRect: TriggerRect\n}\n\ninterface ActiveMenuBinding {\n readonly target: SessionMenuTarget\n readonly onClick: (event: MouseEvent) => void\n}\n\nconst NS = 'pinnedSessions'\nconst STYLE_ID = '@anionex/dsh-pinned-sessions'\nconst MENU_ASSOCIATION_MS = 2_000\n\nconst zh = {\n 'menu.pin': '置顶会话',\n 'menu.unpin': '取消置顶',\n 'section.label': '置顶会话',\n 'session.open': '打开置顶会话“{name}”',\n 'session.unpin': '取消置顶“{name}”',\n 'workspace.ungrouped': '未分组',\n}\n\nconst en = {\n 'menu.pin': 'Pin session',\n 'menu.unpin': 'Unpin session',\n 'section.label': 'Pinned sessions',\n 'session.open': 'Open pinned session “{name}”',\n 'session.unpin': 'Unpin “{name}”',\n 'workspace.ungrouped': 'Ungrouped',\n}\n\nconst styles = `\n[${'data-dsh-pinned-sessions-host'}]{box-sizing:border-box;min-width:0;flex:none}\n.dsh-pins-section{box-sizing:border-box;min-width:0;margin:0 4px 6px;padding:0 0 6px;border-bottom:1px solid var(--dsw-alias-border-l1)}\n.dsh-pins-header{display:flex;align-items:center;gap:6px;height:28px;padding:0 8px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;font-weight:500}\n.dsh-pins-header svg{flex:none;color:var(--dsw-alias-state-business-primary)}\n.dsh-pins-list{box-sizing:border-box;max-height:192px;min-width:0;margin:0;padding:0;overflow-y:auto;overscroll-behavior:contain;list-style:none}\n.dsh-pins-row{box-sizing:border-box;display:flex;align-items:center;gap:0;height:32px;min-width:0;border-radius:6px;color:var(--dsw-alias-label-primary)}\n.dsh-pins-row:hover,.dsh-pins-row[data-current=\"true\"]{background:var(--dsw-alias-interactive-bg-hover)}\n.dsh-pins-open{box-sizing:border-box;display:flex;align-items:center;gap:6px;min-width:0;height:32px;padding:0 4px 0 8px;border:0;background:transparent;color:inherit;cursor:pointer;flex:1;text-align:left}\n.dsh-pins-open:focus-visible,.dsh-pins-remove:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}\n.dsh-pins-open>svg{flex:none;color:var(--dsw-alias-state-business-primary)}\n.dsh-pins-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;line-height:20px;flex:1}\n.dsh-pins-workspace{max-width:34%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:18px;flex:none}\n.dsh-pins-remove{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;margin-right:3px;padding:0;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;opacity:0;flex:none}\n.dsh-pins-row:hover .dsh-pins-remove,.dsh-pins-remove:focus-visible{opacity:1}\n.dsh-pins-remove:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}\n.dsh-pins-menu-button{letter-spacing:0}\n@media (max-width:767px){.dsh-pins-workspace{display:none}.dsh-pins-remove{opacity:1}}\n`\n\nexport const inject = ['slots', 'sessions', 'locale']\n\n/** Register the lifecycle bridge in the additive frame overlay slot. */\nexport function apply(ctx: ClientContextLike): void {\n let storage: Storage | undefined\n try {\n storage = window.localStorage\n } catch {\n storage = undefined\n }\n const store = new PinStore(storage)\n\n ctx.effect(() => {\n const onStorage = (event: StorageEvent): void => {\n if (event.key === PIN_STORAGE_KEY || event.key === null) store.reload()\n }\n window.addEventListener('storage', onStorage)\n return () => { window.removeEventListener('storage', onStorage) }\n }, 'pinned-sessions: cross-tab state')\n\n ctx.effect(() => {\n if (document.querySelector(`style[data-plugin-css=\"${STYLE_ID}\"]`) !== null) return () => {}\n const tag = document.createElement('style')\n tag.dataset.plugin = '@anionex/dsh-pinned-sessions'\n tag.dataset.pluginCss = STYLE_ID\n tag.textContent = styles\n document.head.appendChild(tag)\n return () => { tag.remove() }\n }, 'pinned-sessions: styles')\n\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'pinned-sessions: dictionaries')\n\n ctx.slots.inject('shell.overlay', () => ctx.slots.register({\n name: 'shell.overlay',\n id: 'pinned-sessions-bridge',\n order: 40,\n locale: NS,\n inject: () => ({ store, sessions: ctx.sessions }),\n }, PinnedSessionsBridge))\n}\n\n/** Keep native behavior intact while mounting the sidebar portal and unmanaged menu item. */\nexport function PinnedSessionsBridge({ store, sessions, useSessions, useWorkspaces, t }: BridgeProps): ReactNode {\n const sessionSnapshot = useSessions(snapshot => snapshot)\n const workspaceSnapshot = useWorkspaces(snapshot => snapshot)\n const pins = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot)\n const [sidebarHost, setSidebarHost] = useState<HTMLDivElement | null>(null)\n const [sidebarWide, setSidebarWide] = useState(true)\n const pending = useRef<PendingMenuRequest | null>(null)\n const activeMenu = useRef<ActiveMenuBinding | null>(null)\n const focusAfterUnpin = useRef<number | null>(null)\n\n useLayoutEffect(() => {\n let active = true\n let queued = false\n let settleTimer: number | undefined\n let observedSidebar: Element | null = null\n const sidebarObserver = new MutationObserver(() => { queueRefresh() })\n\n const disposeActiveMenu = (): void => {\n const binding = activeMenu.current\n if (binding === null) return\n activeMenu.current = null\n binding.target.button.removeEventListener('click', binding.onClick)\n binding.target.host.remove()\n if (binding.target.menu.getAttribute(MENU_OWNER_ATTRIBUTE) === binding.target.sessionId) {\n binding.target.menu.removeAttribute(MENU_OWNER_ATTRIBUTE)\n }\n }\n const updateActiveMenu = (): void => {\n const binding = activeMenu.current\n if (binding === null) return\n const pinned = store.isPinned(binding.target.sessionId)\n updateSessionMenuItem(binding.target, pinned, t(pinned ? 'menu.unpin' : 'menu.pin'))\n }\n const observeSidebar = (): void => {\n const slot = document.querySelector(SIDEBAR_SLOT_SELECTOR)\n if (slot === observedSidebar) return\n sidebarObserver.disconnect()\n observedSidebar = slot\n if (slot !== null) {\n sidebarObserver.observe(slot, {\n attributes: true,\n attributeFilter: ['class', 'style'],\n childList: true,\n subtree: true,\n })\n }\n }\n const refresh = (): void => {\n if (!active) return\n const nextHost = ensurePinnedHost(document)\n setSidebarHost(current => current === nextHost ? current : nextHost)\n observeSidebar()\n const rootWidth = nextHost?.parentElement?.getBoundingClientRect().width\n if (rootWidth !== undefined) setSidebarWide(rootWidth >= 160)\n\n const binding = activeMenu.current\n if (binding !== null && (!binding.target.host.isConnected || !binding.target.menu.isConnected)) {\n disposeActiveMenu()\n }\n const request = pending.current\n if (request === null) return\n if (Date.now() - request.createdAt > MENU_ASSOCIATION_MS) {\n pending.current = null\n return\n }\n const menu = findUnclaimedPortalMenu(document, request.existingMenus, request.triggerRect)\n if (menu === null) return\n const target = attachSessionMenuHost(menu, request.sessionId)\n if (target === null) return\n const onClick = (event: MouseEvent): void => {\n event.preventDefault()\n event.stopPropagation()\n store.toggle(target.sessionId)\n disposeActiveMenu()\n document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))\n }\n target.button.addEventListener('click', onClick)\n activeMenu.current = { target, onClick }\n pending.current = null\n updateActiveMenu()\n }\n function queueRefresh(): void {\n if (!active) return\n if (settleTimer === undefined) {\n settleTimer = window.setTimeout(() => {\n settleTimer = undefined\n refresh()\n }, 250)\n }\n if (queued) return\n queued = true\n queueMicrotask(() => {\n queued = false\n refresh()\n })\n }\n const onClickCapture = (event: MouseEvent): void => {\n pending.current = null\n const target = findSessionActionTarget(event.target)\n if (target === null) return\n const sessionId = captureSessionId(target.row, sessions)\n if (sessionId === null) return\n const rect = target.button.getBoundingClientRect()\n pending.current = {\n sessionId,\n createdAt: Date.now(),\n existingMenus: new Set(listPortalMenus(document)),\n triggerRect: {\n top: rect.top,\n right: rect.right,\n bottom: rect.bottom,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n },\n }\n queueRefresh()\n }\n const onKeyDownCapture = (): void => { pending.current = null }\n\n refresh()\n document.addEventListener('click', onClickCapture, true)\n document.addEventListener('keydown', onKeyDownCapture, true)\n const bodyObserver = new MutationObserver(() => { queueRefresh() })\n bodyObserver.observe(document.body, { childList: true })\n const unsubscribe = store.subscribe(updateActiveMenu)\n return () => {\n active = false\n pending.current = null\n if (settleTimer !== undefined) window.clearTimeout(settleTimer)\n unsubscribe()\n bodyObserver.disconnect()\n sidebarObserver.disconnect()\n document.removeEventListener('click', onClickCapture, true)\n document.removeEventListener('keydown', onKeyDownCapture, true)\n disposeActiveMenu()\n removeBridgeArtifacts(document)\n }\n }, [sessions, store, t])\n\n useLayoutEffect(() => {\n if (sidebarHost === null) return\n const root = sidebarHost.parentElement\n if (root === null) return\n const update = (): void => { setSidebarWide(root.getBoundingClientRect().width >= 160) }\n update()\n if (typeof ResizeObserver === 'undefined') return\n const observer = new ResizeObserver(update)\n observer.observe(root)\n return () => { observer.disconnect() }\n }, [sidebarHost])\n\n useEffect(() => {\n if (sessionSnapshot.phase !== 'ready' || workspaceSnapshot.phase !== 'ready') return\n const valid = new Set(sessionSnapshot.ids)\n const archived = new Set(workspaceSnapshot.archivedSessionIds)\n store.prune(sessionId => valid.has(sessionId) && !archived.has(sessionId))\n }, [sessionSnapshot, store, workspaceSnapshot])\n\n const rows = useMemo(() => {\n const workspaceBySession = new Map<string, string>()\n for (const workspace of workspaceSnapshot.items) {\n for (const sessionId of workspace.sessionIds) {\n if (!workspaceBySession.has(sessionId)) workspaceBySession.set(sessionId, workspace.title)\n }\n }\n return pins.flatMap(pin => {\n const session = sessionSnapshot.byId[pin.id]\n if (session === undefined || session.blank === true) return []\n const workspaceTitle = workspaceBySession.get(pin.id)\n const workspace = workspaceTitle ?? t('workspace.ungrouped')\n return [{ session, title: sessionDisplayTitle(session, workspaceTitle), workspace }]\n })\n }, [pins, sessionSnapshot.byId, t, workspaceSnapshot.items])\n\n useLayoutEffect(() => {\n const removedIndex = focusAfterUnpin.current\n if (removedIndex === null) return\n focusAfterUnpin.current = null\n focusAfterPinnedRemoval(document, sidebarHost, removedIndex)\n }, [rows, sidebarHost])\n\n return sidebarHost !== null && sidebarHost.isConnected && sidebarWide && rows.length > 0\n ? createPortal(\n <PinnedSessionSection\n currentId={sessionSnapshot.current}\n rows={rows}\n open={sessionId => { sessions.open(sessionId) }}\n unpin={(sessionId, index) => {\n focusAfterUnpin.current = index\n store.toggle(sessionId)\n }}\n t={t}\n />,\n sidebarHost,\n )\n : null\n}\n\ninterface PinnedSessionSectionProps {\n readonly currentId?: string | undefined\n readonly rows: readonly { readonly session: SessionSummaryLike; readonly title: string; readonly workspace: string }[]\n readonly open: (sessionId: string) => void\n readonly unpin: (sessionId: string, index: number) => void\n readonly t: Translate\n}\n\nfunction PinnedSessionSection({ currentId, rows, open, unpin, t }: PinnedSessionSectionProps): ReactNode {\n return <section className=\"dsh-pins-section\" aria-label={t('section.label')}>\n <div className=\"dsh-pins-header\">\n <PinIcon size={13} />\n <span>{t('section.label')}</span>\n </div>\n <ul className=\"dsh-pins-list\">\n {rows.map(({ session, title, workspace }, index) => <li\n className=\"dsh-pins-row\"\n data-current={session.id === currentId ? 'true' : 'false'}\n key={session.id}\n >\n <button\n type=\"button\"\n className=\"dsh-pins-open\"\n aria-current={session.id === currentId ? 'page' : undefined}\n aria-label={t('session.open', { name: title })}\n onClick={() => { open(session.id) }}\n >\n <PinIcon size={13} />\n <span className=\"dsh-pins-title\">{title}</span>\n <span className=\"dsh-pins-workspace\">{workspace}</span>\n </button>\n <button\n type=\"button\"\n className=\"dsh-pins-remove\"\n aria-label={t('session.unpin', { name: title })}\n title={t('session.unpin', { name: title })}\n onClick={() => { unpin(session.id, index) }}\n >\n <CloseIcon />\n </button>\n </li>)}\n </ul>\n </section>\n}\n\nfunction PinIcon({ size = 16 }: PinIconProps): ReactNode {\n return <Pin size={size} strokeWidth={1.8} aria-hidden=\"true\" />\n}\n\nfunction CloseIcon(): ReactNode {\n return <X size={14} strokeWidth={2} aria-hidden=\"true\" />\n}\n","import { CamelToPascal } from './utility-types';\n\n/**\n * Converts string to kebab case\n *\n * @param {string} string\n * @returns {string} A kebabized string\n */\nexport const toKebabCase = (string: string) =>\n string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();\n\n/**\n * Converts string to camel case\n *\n * @param {string} string\n * @returns {string} A camelized string\n */\nexport const toCamelCase = <T extends string>(string: T) =>\n string.replace(/^([A-Z])|[\\s-_]+(\\w)/g, (match, p1, p2) =>\n p2 ? p2.toUpperCase() : p1.toLowerCase(),\n );\n\n/**\n * Converts string to pascal case\n *\n * @param {string} string\n * @returns {string} A pascalized string\n */\nexport const toPascalCase = <T extends string>(string: T): CamelToPascal<T> => {\n const camelCase = toCamelCase(string);\n\n return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal<T>;\n};\n\n/**\n * Merges classes into a single string\n *\n * @param {array} classes\n * @returns {string} A string of classes\n */\nexport const mergeClasses = <ClassType = string | undefined | null>(...classes: ClassType[]) =>\n classes\n .filter((className, index, array) => {\n return (\n Boolean(className) &&\n (className as string).trim() !== '' &&\n array.indexOf(className) === index\n );\n })\n .join(' ')\n .trim();\n","export default {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 24,\n height: 24,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n};\n","import { createElement, forwardRef } from 'react';\nimport defaultAttributes from './defaultAttributes';\nimport { IconNode, LucideProps } from './types';\nimport { mergeClasses } from '@lucide/shared';\n\ninterface IconComponentProps extends LucideProps {\n iconNode: IconNode;\n}\n\n/**\n * Lucide icon component\n *\n * @component Icon\n * @param {object} props\n * @param {string} props.color - The color of the icon\n * @param {number} props.size - The size of the icon\n * @param {number} props.strokeWidth - The stroke width of the icon\n * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width\n * @param {string} props.className - The class name of the icon\n * @param {IconNode} props.children - The children of the icon\n * @param {IconNode} props.iconNode - The icon node of the icon\n *\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst Icon = forwardRef<SVGSVGElement, IconComponentProps>(\n (\n {\n color = 'currentColor',\n size = 24,\n strokeWidth = 2,\n absoluteStrokeWidth,\n className = '',\n children,\n iconNode,\n ...rest\n },\n ref,\n ) => {\n return createElement(\n 'svg',\n {\n ref,\n ...defaultAttributes,\n width: size,\n height: size,\n stroke: color,\n strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,\n className: mergeClasses('lucide', className),\n ...rest,\n },\n [\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...(Array.isArray(children) ? children : [children]),\n ],\n );\n },\n);\n\nexport default Icon;\n","import { createElement, forwardRef } from 'react';\nimport { mergeClasses, toKebabCase } from '@lucide/shared';\nimport { IconNode, LucideProps } from './types';\nimport Icon from './Icon';\n\n/**\n * Create a Lucide icon component\n * @param {string} iconName\n * @param {array} iconNode\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst createLucideIcon = (iconName: string, iconNode: IconNode) => {\n const Component = forwardRef<SVGSVGElement, LucideProps>(({ className, ...props }, ref) =>\n createElement(Icon, {\n ref,\n iconNode,\n className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className),\n ...props,\n }),\n );\n\n Component.displayName = `${iconName}`;\n\n return Component;\n};\n\nexport default createLucideIcon;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Pin\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNMTIgMTd2NSIgLz4KICA8cGF0aCBkPSJNOSAxMC43NmEyIDIgMCAwIDEtMS4xMSAxLjc5bC0xLjc4LjlBMiAyIDAgMCAwIDUgMTUuMjRWMTZhMSAxIDAgMCAwIDEgMWgxMmExIDEgMCAwIDAgMS0xdi0uNzZhMiAyIDAgMCAwLTEuMTEtMS43OWwtMS43OC0uOUEyIDIgMCAwIDEgMTUgMTAuNzZWN2ExIDEgMCAwIDEgMS0xIDIgMiAwIDAgMCAwLTRIOGEyIDIgMCAwIDAgMCA0IDEgMSAwIDAgMSAxIDF6IiAvPgo8L3N2Zz4K) - https://lucide.dev/icons/pin\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Pin = createLucideIcon('Pin', [\n ['path', { d: 'M12 17v5', key: 'bb1du9' }],\n [\n 'path',\n {\n d: 'M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z',\n key: '1nkz8b',\n },\n ],\n]);\n\nexport default Pin;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name X\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNMTggNiA2IDE4IiAvPgogIDxwYXRoIGQ9Im02IDYgMTIgMTIiIC8+Cjwvc3ZnPgo=) - https://lucide.dev/icons/x\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst X = createLucideIcon('X', [\n ['path', { d: 'M18 6 6 18', key: '1bl5f8' }],\n ['path', { d: 'm6 6 12 12', key: 'd8bk6v' }],\n]);\n\nexport default X;\n","import { SVGProps } from './types';\n\nconst defaultAttributes: SVGProps = {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 24,\n height: 24,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': 2,\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n};\n\nexport default defaultAttributes;\n","import defaultAttributes from '../defaultAttributes';\nimport type { IconNode } from '../types';\n\n/**\n * @name pin-off\n * @description Lucide SVG icon node.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNMTIgMTd2NSIgLz4KICA8cGF0aCBkPSJNMTUgOS4zNFY3YTEgMSAwIDAgMSAxLTEgMiAyIDAgMCAwIDAtNEg3Ljg5IiAvPgogIDxwYXRoIGQ9Im0yIDIgMjAgMjAiIC8+CiAgPHBhdGggZD0iTTkgOXYxLjc2YTIgMiAwIDAgMS0xLjExIDEuNzlsLTEuNzguOUEyIDIgMCAwIDAgNSAxNS4yNFYxNmExIDEgMCAwIDAgMSAxaDExIiAvPgo8L3N2Zz4K) - https://lucide.dev/icons/pin-off\n * @see https://lucide.dev/guide/packages/lucide - Documentation\n *\n * @returns {Array}\n *\n */\nconst PinOff: IconNode = [\n 'svg',\n defaultAttributes,\n [\n ['path', { d: 'M12 17v5' }],\n ['path', { d: 'M15 9.34V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H7.89' }],\n ['path', { d: 'm2 2 20 20' }],\n ['path', { d: 'M9 9v1.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h11' }],\n ],\n];\n\nexport default PinOff;\n","import defaultAttributes from '../defaultAttributes';\nimport type { IconNode } from '../types';\n\n/**\n * @name pin\n * @description Lucide SVG icon node.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogIHdpZHRoPSIyNCIKICBoZWlnaHQ9IjI0IgogIHZpZXdCb3g9IjAgMCAyNCAyNCIKICBmaWxsPSJub25lIgogIHN0cm9rZT0iIzAwMCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmY7IGJvcmRlci1yYWRpdXM6IDJweCIKICBzdHJva2Utd2lkdGg9IjIiCiAgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIgogIHN0cm9rZS1saW5lam9pbj0icm91bmQiCj4KICA8cGF0aCBkPSJNMTIgMTd2NSIgLz4KICA8cGF0aCBkPSJNOSAxMC43NmEyIDIgMCAwIDEtMS4xMSAxLjc5bC0xLjc4LjlBMiAyIDAgMCAwIDUgMTUuMjRWMTZhMSAxIDAgMCAwIDEgMWgxMmExIDEgMCAwIDAgMS0xdi0uNzZhMiAyIDAgMCAwLTEuMTEtMS43OWwtMS43OC0uOUEyIDIgMCAwIDEgMTUgMTAuNzZWN2ExIDEgMCAwIDEgMS0xIDIgMiAwIDAgMCAwLTRIOGEyIDIgMCAwIDAgMCA0IDEgMSAwIDAgMSAxIDF6IiAvPgo8L3N2Zz4K) - https://lucide.dev/icons/pin\n * @see https://lucide.dev/guide/packages/lucide - Documentation\n *\n * @returns {Array}\n *\n */\nconst Pin: IconNode = [\n 'svg',\n defaultAttributes,\n [\n ['path', { d: 'M12 17v5' }],\n [\n 'path',\n {\n d: 'M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z',\n },\n ],\n ],\n];\n\nexport default Pin;\n","import { Pin as PinNode, PinOff as PinOffNode, type IconNode } from 'lucide'\n\nexport const SIDEBAR_SLOT_SELECTOR = '[data-slot=\"sidebar.workspaces\"]'\nexport const PINNED_HOST_ATTRIBUTE = 'data-dsh-pinned-sessions-host'\nexport const MENU_HOST_ATTRIBUTE = 'data-dsh-pinned-session-menu-host'\nexport const MENU_OWNER_ATTRIBUTE = 'data-dsh-pinned-session-menu'\n\nexport interface SessionsForCapture {\n readonly list: {\n getSnapshot(): { readonly current?: string | undefined }\n }\n open(sessionId: string): void\n}\n\nexport interface SessionActionTarget {\n readonly button: HTMLButtonElement\n readonly row: HTMLElement\n}\n\nexport interface SessionMenuTarget {\n readonly host: HTMLDivElement\n readonly menu: HTMLElement\n readonly sessionId: string\n readonly button: HTMLButtonElement\n readonly icon: HTMLSpanElement\n readonly label: HTMLSpanElement\n}\n\nexport interface TriggerRect {\n readonly top: number\n readonly right: number\n readonly bottom: number\n readonly left: number\n readonly width: number\n readonly height: number\n}\n\nfunction elementFromTarget(target: EventTarget | null): Element | null {\n return target !== null && (target as Node).nodeType === 1 ? target as Element : null\n}\n\n/** Identify a native ellipsis trigger inside a real Session row. */\nexport function findSessionActionTarget(target: EventTarget | null): SessionActionTarget | null {\n const element = elementFromTarget(target)\n const button = element?.closest('button')\n if (!(button instanceof HTMLButtonElement)) return null\n const row = button.closest<HTMLElement>('[role=\"treeitem\"][aria-selected]')\n if (row === null || row === button) return null\n if (row.closest(SIDEBAR_SLOT_SELECTOR) === null) return null\n if (row.closest(`[${PINNED_HOST_ATTRIBUTE}]`) !== null) return null\n return { button, row }\n}\n\n/**\n * Ask the native row closure for its Session ID without changing selection.\n * The Workspace browser's row callback calls sessions.open(node.id), so a\n * synchronous, verified interceptor captures the exact ID and is restored\n * before the user's original ellipsis click continues.\n */\nexport function captureSessionId(row: HTMLElement, sessions: SessionsForCapture): string | null {\n const hadOwn = Object.prototype.hasOwnProperty.call(sessions, 'open')\n const ownDescriptor = hadOwn ? Object.getOwnPropertyDescriptor(sessions, 'open') : undefined\n const original = sessions.open\n let captured: string | null = null\n const interceptor = (sessionId: string): void => {\n if (typeof sessionId === 'string' && sessionId.length > 0) captured = sessionId\n }\n\n try {\n Object.defineProperty(sessions, 'open', {\n configurable: true,\n enumerable: ownDescriptor?.enumerable ?? false,\n writable: true,\n value: interceptor,\n })\n if (sessions.open !== interceptor) return null\n row.click()\n } catch {\n return null\n } finally {\n try {\n if (hadOwn && ownDescriptor !== undefined) Object.defineProperty(sessions, 'open', ownDescriptor)\n else delete (sessions as { open?: SessionsForCapture['open'] }).open\n } catch {\n try {\n Object.defineProperty(sessions, 'open', {\n configurable: true,\n writable: true,\n value: original,\n })\n } catch {\n // The verified patch path is configurable; this is a defensive fallback.\n }\n }\n }\n return captured\n}\n\n/** Insert the pinned region immediately after the native Workspace header. */\nexport function ensurePinnedHost(doc: Document): HTMLDivElement | null {\n const slot = doc.querySelector<HTMLElement>(SIDEBAR_SLOT_SELECTOR)\n const root = slot?.firstElementChild\n if (!(root instanceof HTMLElement)) return null\n const existing = root.querySelector<HTMLDivElement>(`:scope > [${PINNED_HOST_ATTRIBUTE}]`)\n if (existing !== null) return existing\n const header = root.firstElementChild\n if (header === null) return null\n const host = doc.createElement('div')\n host.setAttribute(PINNED_HOST_ATTRIBUTE, '')\n header.insertAdjacentElement('afterend', host)\n return host\n}\n\n/** List direct-body portal menus so one trigger can exclude every pre-existing menu. */\nexport function listPortalMenus(doc: Document): readonly HTMLElement[] {\n return [...doc.body.children].filter((node): node is HTMLElement => (\n node instanceof HTMLElement && node.getAttribute('role') === 'menu'\n ))\n}\n\nfunction belongsToTrigger(menu: HTMLElement, trigger: TriggerRect | undefined): boolean {\n if (trigger === undefined || trigger.width === 0 || trigger.height === 0) return true\n const menuRect = menu.getBoundingClientRect()\n if (menuRect.width === 0 || menuRect.height === 0) return true\n const horizontalGap = Math.min(\n Math.abs(menuRect.left - trigger.left),\n Math.abs(menuRect.right - trigger.right),\n )\n const verticalGap = Math.max(\n 0,\n menuRect.top - trigger.bottom,\n trigger.top - menuRect.bottom,\n )\n return horizontalGap <= 64 && verticalGap <= 32\n}\n\n/** Find only a new, geometrically associated portal menu after one trigger click. */\nexport function findUnclaimedPortalMenu(\n doc: Document,\n excluded: ReadonlySet<HTMLElement> = new Set(),\n trigger?: TriggerRect,\n): HTMLElement | null {\n const menus = listPortalMenus(doc).filter(menu => (\n !excluded.has(menu)\n && !menu.hasAttribute(MENU_OWNER_ATTRIBUTE)\n && belongsToTrigger(menu, trigger)\n ))\n return menus.at(-1) ?? null\n}\n\nfunction createLucideIcon(doc: Document, node: IconNode, name: string): SVGSVGElement {\n const [, attributes, children = []] = node\n const svg = doc.createElementNS('http://www.w3.org/2000/svg', 'svg')\n for (const [key, value] of Object.entries(attributes)) svg.setAttribute(key, String(value))\n svg.setAttribute('width', '16')\n svg.setAttribute('height', '16')\n svg.setAttribute('stroke-width', '1.8')\n svg.setAttribute('class', `lucide lucide-${name}`)\n svg.setAttribute('aria-hidden', 'true')\n for (const [tag, childAttributes] of children) {\n const child = doc.createElementNS('http://www.w3.org/2000/svg', tag)\n for (const [key, value] of Object.entries(childAttributes)) child.setAttribute(key, String(value))\n svg.appendChild(child)\n }\n return svg\n}\n\n/** Update the unmanaged item without moving it into the plugin's React tree. */\nexport function updateSessionMenuItem(target: SessionMenuTarget, pinned: boolean, label: string): void {\n target.button.setAttribute('aria-label', label)\n target.label.textContent = label\n target.icon.replaceChildren(createLucideIcon(\n target.menu.ownerDocument,\n pinned ? PinOffNode : PinNode,\n pinned ? 'pin-off' : 'pin',\n ))\n}\n\n/** Add an unmanaged native-styled row before the Session menu's archive tail. */\nexport function attachSessionMenuHost(menu: HTMLElement, sessionId: string): SessionMenuTarget | null {\n if (menu.hasAttribute(MENU_OWNER_ATTRIBUTE)) return null\n const viewport = [...menu.children].find((node): node is HTMLElement => (\n node instanceof HTMLElement && node.getAttribute('role') === 'presentation'\n ))\n if (viewport === undefined) return null\n const wrappers = [...viewport.children].filter((node): node is HTMLElement => (\n node instanceof HTMLElement\n && node.querySelector(':scope > button[role=\"menuitem\"]') instanceof HTMLButtonElement\n ))\n if (wrappers.length < 3 || wrappers.length > 4) return null\n const templateButton = wrappers[0]?.querySelector<HTMLButtonElement>(':scope > button[role=\"menuitem\"]')\n if (templateButton === null || templateButton === undefined) return null\n const spans = [...templateButton.children].filter((node): node is HTMLSpanElement => node instanceof HTMLSpanElement)\n if (spans.length < 2) return null\n\n const doc = menu.ownerDocument\n const host = doc.createElement('div')\n host.className = wrappers[0]?.className ?? ''\n host.setAttribute(MENU_HOST_ATTRIBUTE, '')\n const button = doc.createElement('button')\n button.type = 'button'\n button.setAttribute('role', 'menuitem')\n button.className = `${templateButton.className} dsh-pins-menu-button`\n const icon = doc.createElement('span')\n icon.className = spans[0]?.className ?? ''\n icon.setAttribute('aria-hidden', 'true')\n const label = doc.createElement('span')\n label.className = spans.at(-1)?.className ?? ''\n button.append(icon, label)\n host.appendChild(button)\n viewport.insertBefore(host, wrappers[2] ?? null)\n menu.setAttribute(MENU_OWNER_ATTRIBUTE, sessionId)\n\n const target = { host, menu, sessionId, button, icon, label }\n const view = doc.defaultView\n if (view !== null) view.dispatchEvent(new view.Event('resize'))\n return target\n}\n\n/** Restore useful keyboard focus after a pinned row removes itself. */\nexport function focusAfterPinnedRemoval(doc: Document, host: HTMLElement | null, removedIndex: number): boolean {\n const remaining = host === null ? [] : [...host.querySelectorAll<HTMLButtonElement>('.dsh-pins-open')]\n const next = remaining[Math.min(removedIndex, remaining.length - 1)]\n if (next !== undefined) {\n next.focus({ preventScroll: true })\n return true\n }\n const nativeRow = doc.querySelector<HTMLElement>(`${SIDEBAR_SLOT_SELECTOR} [role=\"treeitem\"][aria-selected=\"true\"]`)\n ?? doc.querySelector<HTMLElement>(`${SIDEBAR_SLOT_SELECTOR} [role=\"treeitem\"]`)\n if (nativeRow === null) return false\n const hadTabIndex = nativeRow.hasAttribute('tabindex')\n if (!hadTabIndex) nativeRow.tabIndex = -1\n nativeRow.focus({ preventScroll: true })\n if (doc.activeElement === nativeRow) return true\n if (!hadTabIndex) nativeRow.removeAttribute('tabindex')\n return false\n}\n\nexport function removeBridgeArtifacts(doc: Document): void {\n for (const node of doc.querySelectorAll(`[${PINNED_HOST_ATTRIBUTE}], [${MENU_HOST_ATTRIBUTE}]`)) node.remove()\n for (const menu of doc.querySelectorAll(`[${MENU_OWNER_ATTRIBUTE}]`)) menu.removeAttribute(MENU_OWNER_ATTRIBUTE)\n}\n","export const PIN_STORAGE_KEY = 'dsh.pinned-sessions.v1'\nconst MAX_PINS = 500\n\nexport interface PinRecord {\n readonly id: string\n readonly pinnedAt: number\n}\n\ninterface PersistedPins {\n readonly version: 1\n readonly pins: readonly PinRecord[]\n}\n\nexport interface StorageLike {\n getItem(key: string): string | null\n setItem(key: string, value: string): void\n}\n\nfunction isPinRecord(value: unknown): value is PinRecord {\n if (typeof value !== 'object' || value === null) return false\n const row = value as Record<string, unknown>\n return typeof row.id === 'string'\n && row.id.length > 0\n && Number.isFinite(row.pinnedAt)\n && (row.pinnedAt as number) >= 0\n}\n\n/** Decode, de-duplicate, and normalize one persisted pin snapshot. */\nexport function decodePins(raw: string | null): readonly PinRecord[] {\n if (raw === null) return []\n try {\n const parsed = JSON.parse(raw) as Partial<PersistedPins>\n if (parsed.version !== 1 || !Array.isArray(parsed.pins)) return []\n const byId = new Map<string, PinRecord>()\n for (const candidate of parsed.pins) {\n if (!isPinRecord(candidate)) continue\n const previous = byId.get(candidate.id)\n if (previous === undefined || candidate.pinnedAt > previous.pinnedAt) {\n byId.set(candidate.id, { id: candidate.id, pinnedAt: candidate.pinnedAt })\n }\n }\n return [...byId.values()]\n .sort((left, right) => right.pinnedAt - left.pinnedAt || left.id.localeCompare(right.id))\n .slice(0, MAX_PINS)\n } catch {\n return []\n }\n}\n\n/** Browser-local, versioned pin state with React-compatible subscriptions. */\nexport class PinStore {\n private snapshot: readonly PinRecord[]\n private readonly listeners = new Set<() => void>()\n\n constructor(private readonly storage: StorageLike | undefined) {\n this.snapshot = this.read()\n }\n\n readonly getSnapshot = (): readonly PinRecord[] => this.snapshot\n\n readonly subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n isPinned(id: string): boolean {\n return this.snapshot.some(row => row.id === id)\n }\n\n toggle(id: string, now = Date.now()): void {\n if (id.length === 0) return\n const without = this.snapshot.filter(row => row.id !== id)\n this.publish(this.isPinned(id) ? without : [{ id, pinnedAt: now }, ...without])\n }\n\n prune(keep: (id: string) => boolean): void {\n const next = this.snapshot.filter(row => keep(row.id))\n if (next.length !== this.snapshot.length) this.publish(next)\n }\n\n reload(): void {\n const next = this.read()\n if (samePins(this.snapshot, next)) return\n this.snapshot = next\n this.emit()\n }\n\n private read(): readonly PinRecord[] {\n try {\n return decodePins(this.storage?.getItem(PIN_STORAGE_KEY) ?? null)\n } catch {\n return []\n }\n }\n\n private publish(next: readonly PinRecord[]): void {\n this.snapshot = [...next]\n .sort((left, right) => right.pinnedAt - left.pinnedAt || left.id.localeCompare(right.id))\n .slice(0, MAX_PINS)\n try {\n this.storage?.setItem(PIN_STORAGE_KEY, JSON.stringify({ version: 1, pins: this.snapshot }))\n } catch {\n // Storage can be unavailable in hardened or quota-constrained browser profiles.\n }\n this.emit()\n }\n\n private emit(): void {\n for (const listener of [...this.listeners]) listener()\n }\n}\n\nfunction samePins(left: readonly PinRecord[], right: readonly PinRecord[]): boolean {\n return left.length === right.length\n && left.every((row, index) => row.id === right[index]?.id && row.pinnedAt === right[index]?.pinnedAt)\n}\n","interface SessionPresentationInput {\n readonly id: string\n readonly title?: string | undefined\n readonly cwd?: string | undefined\n}\n\nfunction nonBlank(value: string | undefined): string | undefined {\n const trimmed = value?.trim()\n return trimmed === undefined || trimmed.length === 0 ? undefined : trimmed\n}\n\n/** Match the native sidebar's useful fallback for sessions without a title. */\nexport function sessionDisplayTitle(\n session: SessionPresentationInput,\n workspaceTitle: string | undefined,\n): string {\n const explicit = nonBlank(session.title)\n if (explicit !== undefined) return explicit\n const workspace = nonBlank(workspaceTitle)\n if (workspace !== undefined) return workspace\n const cwd = nonBlank(session.cwd)\n if (cwd !== undefined) {\n const leaf = cwd.replace(/[\\\\/]+$/u, '').split(/[\\\\/]/u).at(-1)\n if (leaf !== undefined && leaf.length > 0) return leaf\n }\n return session.id\n}\n"],"mappings":"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","names":["import_react","defaultAttributes","defaultAttributes","Pin","defaultAttributes","createLucideIcon","Pin"],"file":"client.js"}
package/lib/index.js ADDED
@@ -0,0 +1,3 @@
1
+ /** Pure-UI host marker for the DSH Pinned Sessions profile bundle. */
2
+ export function apply() { }
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,MAAM,UAAU,KAAK,KAAU,CAAC"}
@@ -0,0 +1,54 @@
1
+ export declare const SIDEBAR_SLOT_SELECTOR = "[data-slot=\"sidebar.workspaces\"]";
2
+ export declare const PINNED_HOST_ATTRIBUTE = "data-dsh-pinned-sessions-host";
3
+ export declare const MENU_HOST_ATTRIBUTE = "data-dsh-pinned-session-menu-host";
4
+ export declare const MENU_OWNER_ATTRIBUTE = "data-dsh-pinned-session-menu";
5
+ export interface SessionsForCapture {
6
+ readonly list: {
7
+ getSnapshot(): {
8
+ readonly current?: string | undefined;
9
+ };
10
+ };
11
+ open(sessionId: string): void;
12
+ }
13
+ export interface SessionActionTarget {
14
+ readonly button: HTMLButtonElement;
15
+ readonly row: HTMLElement;
16
+ }
17
+ export interface SessionMenuTarget {
18
+ readonly host: HTMLDivElement;
19
+ readonly menu: HTMLElement;
20
+ readonly sessionId: string;
21
+ readonly button: HTMLButtonElement;
22
+ readonly icon: HTMLSpanElement;
23
+ readonly label: HTMLSpanElement;
24
+ }
25
+ export interface TriggerRect {
26
+ readonly top: number;
27
+ readonly right: number;
28
+ readonly bottom: number;
29
+ readonly left: number;
30
+ readonly width: number;
31
+ readonly height: number;
32
+ }
33
+ /** Identify a native ellipsis trigger inside a real Session row. */
34
+ export declare function findSessionActionTarget(target: EventTarget | null): SessionActionTarget | null;
35
+ /**
36
+ * Ask the native row closure for its Session ID without changing selection.
37
+ * The Workspace browser's row callback calls sessions.open(node.id), so a
38
+ * synchronous, verified interceptor captures the exact ID and is restored
39
+ * before the user's original ellipsis click continues.
40
+ */
41
+ export declare function captureSessionId(row: HTMLElement, sessions: SessionsForCapture): string | null;
42
+ /** Insert the pinned region immediately after the native Workspace header. */
43
+ export declare function ensurePinnedHost(doc: Document): HTMLDivElement | null;
44
+ /** List direct-body portal menus so one trigger can exclude every pre-existing menu. */
45
+ export declare function listPortalMenus(doc: Document): readonly HTMLElement[];
46
+ /** Find only a new, geometrically associated portal menu after one trigger click. */
47
+ export declare function findUnclaimedPortalMenu(doc: Document, excluded?: ReadonlySet<HTMLElement>, trigger?: TriggerRect): HTMLElement | null;
48
+ /** Update the unmanaged item without moving it into the plugin's React tree. */
49
+ export declare function updateSessionMenuItem(target: SessionMenuTarget, pinned: boolean, label: string): void;
50
+ /** Add an unmanaged native-styled row before the Session menu's archive tail. */
51
+ export declare function attachSessionMenuHost(menu: HTMLElement, sessionId: string): SessionMenuTarget | null;
52
+ /** Restore useful keyboard focus after a pinned row removes itself. */
53
+ export declare function focusAfterPinnedRemoval(doc: Document, host: HTMLElement | null, removedIndex: number): boolean;
54
+ export declare function removeBridgeArtifacts(doc: Document): void;
@@ -0,0 +1,61 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type SessionsForCapture } from './dom-bridge.js';
3
+ import { PinStore } from './pin-store.js';
4
+ interface SessionSummaryLike {
5
+ readonly id: string;
6
+ readonly title?: string | undefined;
7
+ readonly cwd?: string | undefined;
8
+ readonly blank?: boolean | undefined;
9
+ readonly running?: boolean | undefined;
10
+ readonly completed?: boolean | undefined;
11
+ }
12
+ interface SessionsSnapshotLike {
13
+ readonly ids: readonly string[];
14
+ readonly byId: Readonly<Record<string, SessionSummaryLike | undefined>>;
15
+ readonly current?: string | undefined;
16
+ readonly phase: string;
17
+ }
18
+ interface WorkspaceLike {
19
+ readonly workspaceId: string;
20
+ readonly title: string;
21
+ readonly sessionIds: readonly string[];
22
+ }
23
+ interface WorkspacesSnapshotLike {
24
+ readonly items: readonly WorkspaceLike[];
25
+ readonly archivedSessionIds: readonly string[];
26
+ readonly phase: string;
27
+ }
28
+ type SelectorHook<T> = <S>(selector: (snapshot: T) => S, equal?: (left: S, right: S) => boolean) => S;
29
+ type Translate = (key: string, params?: Readonly<Record<string, unknown>>) => string;
30
+ interface SlotsLike {
31
+ inject(name: string, install: () => unknown): void;
32
+ register<I, P>(entry: {
33
+ readonly name: string;
34
+ readonly id: string;
35
+ readonly order?: number;
36
+ readonly locale?: string;
37
+ readonly inject?: () => I;
38
+ }, component: (props: P) => ReactNode): () => void;
39
+ }
40
+ interface LocaleLike {
41
+ register(namespace: string, dictionaries: Readonly<Record<string, Readonly<Record<string, string>>>>): () => void;
42
+ }
43
+ interface ClientContextLike {
44
+ readonly slots: SlotsLike;
45
+ readonly sessions: SessionsForCapture;
46
+ readonly locale: LocaleLike;
47
+ effect(setup: () => (() => void), label?: string): unknown;
48
+ }
49
+ interface BridgeProps {
50
+ readonly store: PinStore;
51
+ readonly sessions: SessionsForCapture;
52
+ readonly useSessions: SelectorHook<SessionsSnapshotLike>;
53
+ readonly useWorkspaces: SelectorHook<WorkspacesSnapshotLike>;
54
+ readonly t: Translate;
55
+ }
56
+ export declare const inject: string[];
57
+ /** Register the lifecycle bridge in the additive frame overlay slot. */
58
+ export declare function apply(ctx: ClientContextLike): void;
59
+ /** Keep native behavior intact while mounting the sidebar portal and unmanaged menu item. */
60
+ export declare function PinnedSessionsBridge({ store, sessions, useSessions, useWorkspaces, t }: BridgeProps): ReactNode;
61
+ export {};
@@ -0,0 +1,27 @@
1
+ export declare const PIN_STORAGE_KEY = "dsh.pinned-sessions.v1";
2
+ export interface PinRecord {
3
+ readonly id: string;
4
+ readonly pinnedAt: number;
5
+ }
6
+ export interface StorageLike {
7
+ getItem(key: string): string | null;
8
+ setItem(key: string, value: string): void;
9
+ }
10
+ /** Decode, de-duplicate, and normalize one persisted pin snapshot. */
11
+ export declare function decodePins(raw: string | null): readonly PinRecord[];
12
+ /** Browser-local, versioned pin state with React-compatible subscriptions. */
13
+ export declare class PinStore {
14
+ private readonly storage;
15
+ private snapshot;
16
+ private readonly listeners;
17
+ constructor(storage: StorageLike | undefined);
18
+ readonly getSnapshot: () => readonly PinRecord[];
19
+ readonly subscribe: (listener: () => void) => (() => void);
20
+ isPinned(id: string): boolean;
21
+ toggle(id: string, now?: number): void;
22
+ prune(keep: (id: string) => boolean): void;
23
+ reload(): void;
24
+ private read;
25
+ private publish;
26
+ private emit;
27
+ }
@@ -0,0 +1,8 @@
1
+ interface SessionPresentationInput {
2
+ readonly id: string;
3
+ readonly title?: string | undefined;
4
+ readonly cwd?: string | undefined;
5
+ }
6
+ /** Match the native sidebar's useful fallback for sessions without a title. */
7
+ export declare function sessionDisplayTitle(session: SessionPresentationInput, workspaceTitle: string | undefined): string;
8
+ export {};
@@ -0,0 +1,2 @@
1
+ /** Pure-UI host marker for the DSH Pinned Sessions profile bundle. */
2
+ export declare function apply(): void;
package/package.json ADDED
@@ -0,0 +1,97 @@
1
+ {
2
+ "name": "@anionex/dsh-pinned-sessions",
3
+ "version": "0.1.0",
4
+ "description": "Pin important DeepSeek Harness sessions at the top of the workspace sidebar",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/Anionex/dsh-pinned-sessions.git"
8
+ },
9
+ "bugs": {
10
+ "url": "https://github.com/Anionex/dsh-pinned-sessions/issues"
11
+ },
12
+ "type": "module",
13
+ "main": "lib/index.js",
14
+ "types": "lib/types/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./lib/types/index.d.ts",
18
+ "default": "./lib/index.js"
19
+ },
20
+ "./client": {
21
+ "types": "./lib/types/client/index.d.ts",
22
+ "default": "./lib/client.js"
23
+ },
24
+ "./package.json": "./package.json"
25
+ },
26
+ "files": [
27
+ "lib",
28
+ "src",
29
+ "scripts",
30
+ "cordis.patch.yml",
31
+ "README.md",
32
+ "README.zh.md",
33
+ "LICENSE"
34
+ ],
35
+ "dsh": {
36
+ "bundle": {
37
+ "patch": "./cordis.patch.yml"
38
+ },
39
+ "client": {
40
+ "platform": "web",
41
+ "inject": [
42
+ "@deepseek-ai/dsh-client-ui-renderer",
43
+ "@deepseek-ai/dsh-client-ui-layout",
44
+ "@deepseek-ai/dsh-client-ui-session",
45
+ "@deepseek-ai/dsh-client-ui-workspace",
46
+ "@deepseek-ai/dsh-client-locale"
47
+ ]
48
+ },
49
+ "compatibility": {
50
+ "dsh": ">=0.1.0-rc.8 <0.2.0",
51
+ "dshReleases": {
52
+ "0.1.1-rc.1": "compatible",
53
+ "0.1.2-alpha.1": "compatible"
54
+ },
55
+ "profiles": [
56
+ "web",
57
+ "desktop"
58
+ ]
59
+ }
60
+ },
61
+ "scripts": {
62
+ "build": "node scripts/clean.mjs && tsc -p tsconfig.build.json && tsc -p tsconfig.client.json && esbuild src/client/index.tsx --bundle --platform=browser --format=cjs --jsx=automatic --external:react --external:react/jsx-runtime --external:react-dom --sourcemap --outfile=.client-build/index.js && node scripts/wrap-client.mjs",
63
+ "check": "tsc -p tsconfig.json && tsc -p tsconfig.client.json --noEmit false --emitDeclarationOnly true && pnpm run build && vitest run && node --test tests/package-layout.test.mjs && pnpm pack --dry-run",
64
+ "prepack": "pnpm run build",
65
+ "test": "vitest run && node --test tests/package-layout.test.mjs"
66
+ },
67
+ "keywords": [
68
+ "deepseek-harness",
69
+ "dsh",
70
+ "sessions",
71
+ "sidebar",
72
+ "productivity"
73
+ ],
74
+ "license": "MIT",
75
+ "peerDependencies": {
76
+ "@deepseek-ai/cordis": "^4.0.1",
77
+ "react": "^18.2.0",
78
+ "react-dom": "^18.2.0"
79
+ },
80
+ "devDependencies": {
81
+ "@types/node": "^24.0.0",
82
+ "@types/react": "~18.3.1",
83
+ "@types/react-dom": "~18.3.1",
84
+ "esbuild": "^0.25.9",
85
+ "jsdom": "^26.1.0",
86
+ "lucide": "0.468.0",
87
+ "lucide-react": "^0.468.0",
88
+ "react": "18.3.1",
89
+ "react-dom": "18.3.1",
90
+ "typescript": "^5.9.2",
91
+ "vitest": "^4.0.0"
92
+ },
93
+ "engines": {
94
+ "node": "^22.19.0 || >=24.0.0"
95
+ },
96
+ "packageManager": "pnpm@11.8.0"
97
+ }
@@ -0,0 +1,6 @@
1
+ import { rm } from 'node:fs/promises'
2
+
3
+ await Promise.all([
4
+ rm(new URL('../lib/', import.meta.url), { recursive: true, force: true }),
5
+ rm(new URL('../.client-build/', import.meta.url), { recursive: true, force: true }),
6
+ ])
@@ -0,0 +1,18 @@
1
+ import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'
2
+ import { join } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+
5
+ const packageId = '@anionex/dsh-pinned-sessions'
6
+ const built = new URL('../.client-build/index.js', import.meta.url)
7
+ const builtMap = new URL('../.client-build/index.js.map', import.meta.url)
8
+ const lib = new URL('../lib/', import.meta.url)
9
+ const source = (await readFile(built, 'utf8')).replace(/\n?\/\/# sourceMappingURL=.*\n?$/u, '\n')
10
+ const sourceMap = JSON.parse(await readFile(builtMap, 'utf8'))
11
+ sourceMap.file = 'client.js'
12
+ const banner = `window.__ModuleLoader__.load({ id: ${JSON.stringify(packageId)}, factory: (require) => {\nvar module = { exports: {} }; var exports = module.exports;\n`
13
+ const footer = '\nreturn module.exports; } });\n//# sourceMappingURL=client.js.map\n'
14
+
15
+ await mkdir(lib, { recursive: true })
16
+ await writeFile(join(fileURLToPath(lib), 'client.js'), `${banner}${source}${footer}`)
17
+ await writeFile(join(fileURLToPath(lib), 'client.js.map'), `${JSON.stringify(sourceMap)}\n`)
18
+ await rm(new URL('../.client-build/', import.meta.url), { recursive: true, force: true })