@tutti-os/workbench-launchpad 0.0.27
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.
- package/LICENSE +202 -0
- package/README.md +3 -0
- package/dist/index.d.ts +178 -0
- package/dist/index.js +816 -0
- package/dist/index.js.map +1 -0
- package/dist/styles/workbench-launchpad.css +485 -0
- package/package.json +48 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/WorkbenchLaunchpadOverlay.tsx","../src/launchpadModel.ts","../src/launchpadWheelNavigation.ts","../src/workbenchLaunchpadDockEntry.ts"],"sourcesContent":["import type * as React from \"react\";\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState\n} from \"react\";\nimport type { WorkbenchDockPlacement } from \"@tutti-os/workbench-surface\";\nimport {\n CloseIcon,\n NavApplicationsFilledIcon,\n SearchIcon\n} from \"@tutti-os/ui-system\";\nimport {\n filterWorkbenchLaunchpadItems,\n paginateWorkbenchLaunchpadItems,\n resolveWorkbenchLaunchpadGrid,\n type WorkbenchLaunchpadGridMetrics,\n type WorkbenchLaunchpadItem\n} from \"./launchpadModel.ts\";\nimport {\n createWorkbenchLaunchpadWheelNavigationState,\n resolveWorkbenchLaunchpadWheelNavigation\n} from \"./launchpadWheelNavigation.ts\";\n\nexport interface WorkbenchLaunchpadOverlayCopy {\n clearSearch: string;\n empty: string;\n installAction?: string;\n installingAction?: string;\n label: string;\n pageDot?: (input: { page: number; pageCount: number }) => string;\n pages?: string;\n refreshAction?: string;\n searchPlaceholder: string;\n syncAction?: string;\n unavailableItem?: (input: { reason: string; title: string }) => string;\n}\n\nexport interface WorkbenchLaunchpadOverlayProps<\n TProvider extends string = string\n> {\n copy: WorkbenchLaunchpadOverlayCopy;\n dockPlacement?: WorkbenchDockPlacement;\n getAgentActionPending?: (\n item: Extract<WorkbenchLaunchpadItem<TProvider>, { kind: \"agent\" }>,\n actionId: string\n ) => boolean;\n getAgentActionLabel?: (actionId: string) => string;\n getAgentReason?: (\n item: Extract<WorkbenchLaunchpadItem<TProvider>, { kind: \"agent\" }>\n ) => string | null;\n items: readonly WorkbenchLaunchpadItem<TProvider>[];\n onClose: () => void;\n onLaunchItem: (item: WorkbenchLaunchpadItem<TProvider>) => void;\n onPageChange?: (input: { pageIndex: number; totalPages: number }) => void;\n onRunAgentAction?: (\n item: Extract<WorkbenchLaunchpadItem<TProvider>, { kind: \"agent\" }>,\n actionId: string\n ) => void;\n onSearch?: (input: { queryLength: number; resultCount: number }) => void;\n open: boolean;\n}\n\nconst defaultLaunchpadGrid: WorkbenchLaunchpadGridMetrics = {\n columns: 5,\n pageSize: 15,\n rows: 3\n};\nconst launchpadExitAnimationMs = 180;\nconst launchpadHoverPanelViewportInsetPx = 16;\n\ntype WorkbenchLaunchpadAgentItem<TProvider extends string = string> = Extract<\n WorkbenchLaunchpadItem<TProvider>,\n { kind: \"agent\" }\n>;\n\nexport function WorkbenchLaunchpadOverlay<TProvider extends string = string>({\n copy,\n dockPlacement = \"bottom\",\n getAgentActionLabel,\n getAgentActionPending,\n getAgentReason,\n items,\n onClose,\n onLaunchItem,\n onPageChange,\n onRunAgentAction,\n onSearch,\n open\n}: WorkbenchLaunchpadOverlayProps<TProvider>) {\n const [query, setQuery] = useState(\"\");\n const [pageIndex, setPageIndex] = useState(0);\n const [shouldRender, setShouldRender] = useState(open);\n const searchInputRef = useRef<HTMLInputElement | null>(null);\n const wheelNavigationRef = useRef(\n createWorkbenchLaunchpadWheelNavigationState()\n );\n const { grid, ref: gridViewportRef } = useLaunchpadGridMetrics(open);\n const isClosing = shouldRender && !open;\n const filteredItems = useMemo(\n () => filterWorkbenchLaunchpadItems(items, query),\n [items, query]\n );\n const page = useMemo(\n () =>\n paginateWorkbenchLaunchpadItems(filteredItems, {\n page: pageIndex,\n pageSize: grid.pageSize\n }),\n [filteredItems, grid.pageSize, pageIndex]\n );\n\n useEffect(() => {\n if (open) {\n setShouldRender(true);\n return undefined;\n }\n if (!shouldRender) {\n return undefined;\n }\n const timeoutId = window.setTimeout(() => {\n setShouldRender(false);\n }, launchpadExitAnimationMs);\n return () => {\n window.clearTimeout(timeoutId);\n };\n }, [open, shouldRender]);\n\n useEffect(() => {\n if (!open) {\n wheelNavigationRef.current =\n createWorkbenchLaunchpadWheelNavigationState();\n return;\n }\n setQuery(\"\");\n setPageIndex(0);\n window.setTimeout(() => searchInputRef.current?.focus(), 0);\n }, [open]);\n\n useEffect(() => {\n setPageIndex(0);\n wheelNavigationRef.current = createWorkbenchLaunchpadWheelNavigationState();\n }, [query]);\n\n useEffect(() => {\n if (!open || !onSearch) {\n return undefined;\n }\n const queryLength = query.trim().length;\n if (queryLength === 0) {\n return undefined;\n }\n const timeoutId = window.setTimeout(() => {\n onSearch({\n queryLength,\n resultCount: filteredItems.length\n });\n }, 300);\n return () => {\n window.clearTimeout(timeoutId);\n };\n }, [filteredItems.length, onSearch, open, query]);\n\n const changePage = useCallback(\n (nextPageIndex: number) => {\n setPageIndex((currentPageIndex) => {\n const clampedPageIndex = Math.max(\n 0,\n Math.min(nextPageIndex, page.pageCount - 1)\n );\n if (clampedPageIndex !== currentPageIndex) {\n onPageChange?.({\n pageIndex: clampedPageIndex,\n totalPages: page.pageCount\n });\n }\n return clampedPageIndex;\n });\n },\n [onPageChange, page.pageCount]\n );\n\n useEffect(() => {\n if (!open) {\n return undefined;\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n onClose();\n return;\n }\n if (isTextInputTarget(event.target)) {\n return;\n }\n if (event.key === \"ArrowRight\" && page.pageCount > 1) {\n event.preventDefault();\n changePage(page.currentPage + 1);\n return;\n }\n if (event.key === \"ArrowLeft\" && page.pageCount > 1) {\n event.preventDefault();\n changePage(page.currentPage - 1);\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n window.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [changePage, onClose, open, page.currentPage, page.pageCount]);\n\n const handleOverlayClick = useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n if (isClosing || isLaunchpadInteractiveTarget(event.target)) {\n return;\n }\n onClose();\n },\n [isClosing, onClose]\n );\n const handleOverlayWheel = useCallback(\n (event: React.WheelEvent<HTMLDivElement>) => {\n if (!open || isClosing || isLaunchpadWheelIgnoredTarget(event.target)) {\n return;\n }\n\n const result = resolveWorkbenchLaunchpadWheelNavigation({\n currentPage: page.currentPage,\n deltaMode: event.deltaMode,\n deltaX: event.deltaX,\n deltaY: event.deltaY,\n pageCount: page.pageCount,\n state: wheelNavigationRef.current,\n timestamp: event.timeStamp\n });\n wheelNavigationRef.current = result.state;\n if (result.shouldPreventDefault) {\n event.preventDefault();\n }\n if (result.nextPageIndex !== null) {\n changePage(result.nextPageIndex);\n }\n },\n [changePage, isClosing, open, page.currentPage, page.pageCount]\n );\n\n if (!shouldRender) {\n return null;\n }\n\n return (\n <div\n aria-label={copy.label}\n aria-modal=\"false\"\n className={classNames(\n \"workspace-launchpad-overlay\",\n isClosing && \"workspace-launchpad-overlay--closing\"\n )}\n role=\"dialog\"\n onClick={handleOverlayClick}\n onWheel={handleOverlayWheel}\n >\n <button\n aria-hidden=\"true\"\n className=\"workspace-launchpad-overlay__dismiss\"\n tabIndex={-1}\n type=\"button\"\n onClick={onClose}\n />\n <div\n className=\"workspace-launchpad-overlay__content\"\n data-dock-placement={dockPlacement}\n >\n <div className=\"workspace-launchpad-overlay__topbar\">\n <div className=\"workspace-launchpad-search\">\n <span\n aria-hidden=\"true\"\n className=\"workspace-launchpad-search__icon\"\n >\n <SearchIcon />\n </span>\n <input\n ref={searchInputRef}\n aria-label={copy.searchPlaceholder}\n className=\"workspace-launchpad-search__input\"\n placeholder={copy.searchPlaceholder}\n type=\"search\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n />\n {query ? (\n <button\n aria-label={copy.clearSearch}\n className=\"workspace-launchpad-search__clear\"\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n setQuery(\"\");\n searchInputRef.current?.focus();\n }}\n onMouseDown={(event) => {\n event.preventDefault();\n event.stopPropagation();\n }}\n >\n <CloseIcon />\n </button>\n ) : null}\n </div>\n </div>\n <div\n ref={gridViewportRef}\n className=\"workspace-launchpad-grid-viewport\"\n >\n {filteredItems.length === 0 ? (\n <div className=\"workspace-launchpad-empty\">{copy.empty}</div>\n ) : (\n <div\n className=\"workspace-launchpad-grid\"\n style={{\n gridTemplateColumns: `repeat(${grid.columns}, minmax(96px, 136px))`\n }}\n >\n {page.pageItems.map((item, index) => (\n <WorkbenchLaunchpadItemButton\n key={item.id}\n animationIndex={index}\n animationRisePx={18 + (index % grid.columns) * 3}\n copy={copy}\n getAgentActionLabel={getAgentActionLabel}\n getAgentReason={getAgentReason}\n isAgentActionPending={getAgentActionPending}\n item={item}\n onLaunch={onLaunchItem}\n onRunAgentAction={onRunAgentAction}\n />\n ))}\n </div>\n )}\n </div>\n {page.pageCount > 1 ? (\n <div\n aria-label={copy.pages ?? \"Pages\"}\n className=\"workspace-launchpad-pages\"\n >\n {Array.from({ length: page.pageCount }, (_, index) => (\n <button\n key={index}\n aria-label={\n copy.pageDot?.({\n page: index + 1,\n pageCount: page.pageCount\n }) ?? `${index + 1} / ${page.pageCount}`\n }\n className=\"workspace-launchpad-page-dot\"\n data-active={index === page.currentPage ? \"true\" : undefined}\n type=\"button\"\n onClick={() => changePage(index)}\n />\n ))}\n </div>\n ) : (\n <div aria-hidden=\"true\" className=\"workspace-launchpad-pages\" />\n )}\n </div>\n </div>\n );\n}\n\nfunction WorkbenchLaunchpadItemButton<TProvider extends string = string>({\n animationIndex,\n animationRisePx,\n copy,\n getAgentActionLabel,\n getAgentReason,\n isAgentActionPending,\n item,\n onLaunch,\n onRunAgentAction\n}: {\n animationIndex: number;\n animationRisePx: number;\n copy: WorkbenchLaunchpadOverlayCopy;\n getAgentActionLabel?: (actionId: string) => string;\n getAgentReason?: (\n item: WorkbenchLaunchpadAgentItem<TProvider>\n ) => string | null;\n isAgentActionPending?: (\n item: WorkbenchLaunchpadAgentItem<TProvider>,\n actionId: string\n ) => boolean;\n item: WorkbenchLaunchpadItem<TProvider>;\n onLaunch: (item: WorkbenchLaunchpadItem<TProvider>) => void;\n onRunAgentAction?: (\n item: WorkbenchLaunchpadAgentItem<TProvider>,\n actionId: string\n ) => void;\n}) {\n const disabledLabel =\n item.disabledReason === undefined\n ? null\n : (copy.unavailableItem?.({\n reason: item.disabledReason,\n title: item.label\n }) ?? item.disabledReason);\n const [hoverPanelOpen, setHoverPanelOpen] = useState(false);\n const [hoverPanelShift, setHoverPanelShift] = useState(0);\n const hoverPanelRef = useRef<HTMLDivElement | null>(null);\n const hoverTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const agentActions =\n item.kind === \"agent\" && !item.launchEnabled\n ? resolveLaunchpadAgentActions(item)\n : [];\n const agentReason =\n item.kind === \"agent\"\n ? (getAgentReason?.(item) ?? item.reason ?? null)\n : null;\n const showComingSoonTooltip =\n item.kind === \"agent\" && item.comingSoon === true && agentReason !== null;\n const showAgentHoverPanel =\n item.kind === \"agent\" &&\n item.comingSoon !== true &&\n !item.launchEnabled &&\n hoverPanelOpen &&\n (agentActions.length > 0 || agentReason !== null);\n const clearHoverTimer = useCallback(() => {\n if (hoverTimerRef.current === null) {\n return;\n }\n clearTimeout(hoverTimerRef.current);\n hoverTimerRef.current = null;\n }, []);\n\n useEffect(\n () => () => {\n clearHoverTimer();\n },\n [clearHoverTimer]\n );\n\n useLayoutEffect(() => {\n if (!showAgentHoverPanel || typeof window === \"undefined\") {\n setHoverPanelShift(0);\n return undefined;\n }\n\n const frameId = window.requestAnimationFrame(() => {\n const panel = hoverPanelRef.current;\n if (!panel) {\n return;\n }\n\n const rect = panel.getBoundingClientRect();\n const maxRight = window.innerWidth - launchpadHoverPanelViewportInsetPx;\n const nextShift =\n rect.left < launchpadHoverPanelViewportInsetPx\n ? launchpadHoverPanelViewportInsetPx - rect.left\n : rect.right > maxRight\n ? maxRight - rect.right\n : 0;\n\n setHoverPanelShift((current) =>\n current === nextShift ? current : nextShift\n );\n });\n\n return () => {\n window.cancelAnimationFrame(frameId);\n };\n }, [agentActions.length, agentReason, showAgentHoverPanel]);\n\n const handlePointerEnter = useCallback(() => {\n if (item.kind !== \"agent\" || item.launchEnabled || item.comingSoon) {\n return;\n }\n clearHoverTimer();\n hoverTimerRef.current = setTimeout(() => {\n hoverTimerRef.current = null;\n setHoverPanelOpen(true);\n }, 500);\n }, [clearHoverTimer, item]);\n const handlePointerLeave = useCallback(() => {\n clearHoverTimer();\n setHoverPanelOpen(false);\n }, [clearHoverTimer]);\n const animationStyle = {\n \"--workspace-launchpad-item-delay\": `${Math.min(animationIndex, 24) * 28}ms`,\n \"--workspace-launchpad-item-rise\": `${animationRisePx}px`\n } as React.CSSProperties;\n const itemNode = (\n <div\n className=\"workspace-launchpad-item-wrap\"\n style={animationStyle}\n onPointerEnter={handlePointerEnter}\n onPointerLeave={handlePointerLeave}\n >\n <button\n aria-label={disabledLabel ?? item.label}\n className={classNames(\n \"workspace-launchpad-item\",\n !item.launchEnabled && \"workspace-launchpad-item--disabled\"\n )}\n disabled={!item.launchEnabled}\n title={\n showComingSoonTooltip ? undefined : (disabledLabel ?? item.label)\n }\n type=\"button\"\n onClick={() => onLaunch(item)}\n >\n <span\n aria-hidden=\"true\"\n className=\"workspace-launchpad-item__icon\"\n data-workspace-app-icon={item.iconUrl ? \"true\" : undefined}\n >\n {item.iconUrl ? (\n <img alt=\"\" draggable={false} src={item.iconUrl} />\n ) : (\n <NavApplicationsFilledIcon className=\"size-10\" />\n )}\n </span>\n <span className=\"workspace-launchpad-item__label\">{item.label}</span>\n </button>\n {showAgentHoverPanel ? (\n <div\n ref={hoverPanelRef}\n className=\"desktop-dock__hover-panel workspace-launchpad-dock-hover-panel\"\n role=\"group\"\n style={\n {\n \"--workspace-launchpad-hover-panel-shift\": `${Math.round(\n hoverPanelShift\n )}px`\n } as React.CSSProperties\n }\n >\n <div className=\"desktop-dock__hover-panel-title\">{item.label}</div>\n {agentReason ? (\n <div className=\"desktop-dock__hover-panel-description\">\n {agentReason}\n </div>\n ) : null}\n {agentActions.length ? (\n <div className=\"desktop-dock__hover-actions\">\n {agentActions.map((action) => {\n const isPending =\n isAgentActionPending?.(item, action.id) ?? false;\n return (\n <button\n key={action.id}\n aria-busy={isPending || undefined}\n className=\"desktop-dock__hover-action\"\n disabled={isPending}\n type=\"button\"\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n if (isPending) {\n return;\n }\n onRunAgentAction?.(item, action.id);\n }}\n >\n {resolveAgentActionLabel(\n isPending && action.id === \"install\"\n ? \"installing\"\n : action.id,\n copy,\n getAgentActionLabel\n )}\n </button>\n );\n })}\n </div>\n ) : null}\n </div>\n ) : null}\n </div>\n );\n\n return showComingSoonTooltip ? (\n <span\n className=\"workspace-launchpad-tooltip-host\"\n title={agentReason ?? undefined}\n >\n {itemNode}\n </span>\n ) : (\n itemNode\n );\n}\n\nfunction useLaunchpadGridMetrics(open: boolean): {\n grid: WorkbenchLaunchpadGridMetrics;\n ref: React.RefObject<HTMLDivElement | null>;\n} {\n const ref = useRef<HTMLDivElement | null>(null);\n const [grid, setGrid] =\n useState<WorkbenchLaunchpadGridMetrics>(defaultLaunchpadGrid);\n\n useEffect(() => {\n if (!open) {\n return undefined;\n }\n const element = ref.current;\n if (!element) {\n return undefined;\n }\n\n const update = () => {\n const rect = element.getBoundingClientRect();\n setGrid(\n resolveWorkbenchLaunchpadGrid({\n height: rect.height,\n width: rect.width\n })\n );\n };\n update();\n\n const resizeObserver = new ResizeObserver(update);\n resizeObserver.observe(element);\n return () => {\n resizeObserver.disconnect();\n };\n }, [open]);\n\n return { grid, ref };\n}\n\nfunction resolveLaunchpadAgentActions<TProvider extends string>(\n item: WorkbenchLaunchpadAgentItem<TProvider>\n) {\n if (item.comingSoon) {\n return [];\n }\n if (item.actions?.length) {\n return item.actions;\n }\n if (item.action) {\n return [{ id: item.action }];\n }\n return [];\n}\n\nfunction resolveAgentActionLabel(\n actionId: string,\n copy: WorkbenchLaunchpadOverlayCopy,\n getAgentActionLabel: ((actionId: string) => string) | undefined\n): string {\n if (getAgentActionLabel) {\n return getAgentActionLabel(actionId);\n }\n switch (actionId) {\n case \"install\":\n return copy.installAction ?? actionId;\n case \"installing\":\n return copy.installingAction ?? copy.installAction ?? actionId;\n case \"sync\":\n return copy.syncAction ?? actionId;\n default:\n return copy.refreshAction ?? actionId;\n }\n}\n\nfunction isTextInputTarget(target: EventTarget | null): boolean {\n return (\n target instanceof HTMLInputElement ||\n target instanceof HTMLTextAreaElement ||\n (target instanceof HTMLElement && target.isContentEditable)\n );\n}\n\nfunction isLaunchpadInteractiveTarget(target: EventTarget | null): boolean {\n return (\n target instanceof HTMLElement &&\n Boolean(\n target.closest(\n \".workspace-launchpad-overlay__topbar, .workspace-launchpad-search, .workspace-launchpad-item, .desktop-dock__hover-panel, .workspace-launchpad-pages\"\n )\n )\n );\n}\n\nfunction isLaunchpadWheelIgnoredTarget(target: EventTarget | null): boolean {\n return (\n isTextInputTarget(target) ||\n (target instanceof HTMLElement &&\n Boolean(\n target.closest(\n \".workspace-launchpad-overlay__topbar, .workspace-launchpad-search, .desktop-dock__hover-panel, .workspace-launchpad-pages\"\n )\n ))\n );\n}\n\nfunction classNames(\n ...values: readonly (string | false | null | undefined)[]\n): string {\n return values.filter(Boolean).join(\" \");\n}\n","export const workbenchLaunchpadDockActionId = \"open-launchpad\";\nexport const workbenchLaunchpadDockEntryId = \"workspace-launchpad\";\n\nexport interface WorkbenchLaunchpadAppDescriptor {\n appId: string;\n disabledReason?: string;\n iconUrl?: string | null;\n id?: string;\n label: string;\n launchEnabled: boolean;\n}\n\nexport interface WorkbenchLaunchpadNodeDescriptor {\n dockEntryId: string;\n disabledReason?: string;\n iconUrl: string;\n id: string;\n label: string;\n launchEnabled?: boolean;\n typeId: string;\n}\n\nexport interface WorkbenchLaunchpadAgentDescriptor<\n TProvider extends string = string\n> {\n action?: string | null;\n actions?: readonly WorkbenchLaunchpadAgentAction[];\n comingSoon?: boolean;\n disabledReason?: string;\n iconUrl: string;\n id?: string;\n label: string;\n launchEnabled: boolean;\n provider: TProvider;\n reason?: string | null;\n}\n\nexport interface WorkbenchLaunchpadAgentAction {\n id: string;\n}\n\nexport type WorkbenchLaunchpadItem<TProvider extends string = string> =\n | {\n appId: string;\n disabledReason?: string;\n iconUrl?: string | null;\n id: string;\n kind: \"app\";\n label: string;\n launchEnabled: boolean;\n }\n | {\n dockEntryId: string;\n disabledReason?: string;\n iconUrl: string;\n id: string;\n kind: \"node\";\n label: string;\n launchEnabled: boolean;\n typeId: string;\n }\n | {\n action?: string | null;\n actions?: readonly WorkbenchLaunchpadAgentAction[];\n comingSoon?: boolean;\n disabledReason?: string;\n iconUrl: string;\n id: string;\n kind: \"agent\";\n label: string;\n launchEnabled: boolean;\n provider: TProvider;\n reason?: string | null;\n };\n\nexport interface WorkbenchLaunchpadGridMetrics {\n columns: number;\n pageSize: number;\n rows: number;\n}\n\nexport interface WorkbenchLaunchpadPage<TItem> {\n currentPage: number;\n pageCount: number;\n pageItems: readonly TItem[];\n}\n\nconst launchpadPreviewIconCount = 4;\nconst launchpadMinColumns = 2;\nconst launchpadMaxColumns = 7;\nconst launchpadMinRows = 1;\nconst launchpadMaxRows = 5;\nconst launchpadTileWidth = 136;\nconst launchpadTileHeight = 138;\n\nexport function buildWorkbenchLaunchpadItems<\n TProvider extends string = string\n>(input: {\n agentDescriptors?: readonly WorkbenchLaunchpadAgentDescriptor<TProvider>[];\n apps?: readonly WorkbenchLaunchpadAppDescriptor[];\n nodeDescriptors?: readonly WorkbenchLaunchpadNodeDescriptor[];\n}): WorkbenchLaunchpadItem<TProvider>[] {\n const nodeItems = (input.nodeDescriptors ?? []).map((node) => ({\n disabledReason: node.disabledReason,\n dockEntryId: node.dockEntryId,\n iconUrl: node.iconUrl,\n id: `node:${node.id}`,\n kind: \"node\" as const,\n label: node.label,\n launchEnabled: node.launchEnabled ?? true,\n typeId: node.typeId\n }));\n const pinnedNodeItems = nodeItems.slice(0, 2);\n const remainingNodeItems = nodeItems.slice(2);\n return [\n ...pinnedNodeItems,\n ...(input.apps ?? []).map((app) => ({\n appId: app.appId,\n disabledReason: app.disabledReason,\n iconUrl: app.iconUrl,\n id: app.id ?? `app:${app.appId}`,\n kind: \"app\" as const,\n label: app.label,\n launchEnabled: app.launchEnabled\n })),\n ...remainingNodeItems,\n ...(input.agentDescriptors ?? []).map((agent) => ({\n action: agent.action,\n actions: agent.actions,\n comingSoon: agent.comingSoon,\n disabledReason: agent.disabledReason,\n iconUrl: agent.iconUrl,\n id: agent.id ?? `agent:${agent.provider}`,\n kind: \"agent\" as const,\n label: agent.label,\n launchEnabled: agent.launchEnabled,\n provider: agent.provider,\n reason: agent.reason\n }))\n ];\n}\n\nexport function filterWorkbenchLaunchpadItems<TItem extends { label: string }>(\n items: readonly TItem[],\n query: string\n): TItem[] {\n const normalizedQuery = normalizeLaunchpadSearchText(query);\n if (!normalizedQuery) {\n return [...items];\n }\n return items.filter((item) =>\n normalizeLaunchpadSearchText(item.label).includes(normalizedQuery)\n );\n}\n\nexport function paginateWorkbenchLaunchpadItems<TItem>(\n items: readonly TItem[],\n input: {\n page: number;\n pageSize: number;\n }\n): WorkbenchLaunchpadPage<TItem> {\n const pageSize = Math.max(1, Math.floor(input.pageSize));\n const pageCount = Math.max(1, Math.ceil(items.length / pageSize));\n const currentPage = clampInteger(input.page, 0, pageCount - 1);\n const pageStart = currentPage * pageSize;\n return {\n currentPage,\n pageCount,\n pageItems: items.slice(pageStart, pageStart + pageSize)\n };\n}\n\nexport function resolveWorkbenchLaunchpadGrid(input: {\n height: number;\n width: number;\n}): WorkbenchLaunchpadGridMetrics {\n const columns = clampInteger(\n Math.floor(input.width / launchpadTileWidth),\n launchpadMinColumns,\n launchpadMaxColumns\n );\n const rows = clampInteger(\n Math.floor(input.height / launchpadTileHeight),\n launchpadMinRows,\n launchpadMaxRows\n );\n return {\n columns,\n pageSize: Math.max(1, columns * rows),\n rows\n };\n}\n\nexport function resolveWorkbenchLaunchpadPreviewIconUrls(input: {\n agentIcons?: readonly string[];\n appIcons?: readonly (string | null | undefined)[];\n fallbackIconUrl: string;\n nodeIconUrls?: readonly string[];\n}): string[] {\n const icons = uniqueLaunchpadPreviewIconUrls([\n ...(input.appIcons ?? []),\n ...(input.nodeIconUrls ?? []),\n ...(input.agentIcons ?? [])\n ]).slice(0, launchpadPreviewIconCount);\n while (icons.length < launchpadPreviewIconCount) {\n icons.push(input.fallbackIconUrl);\n }\n return icons;\n}\n\nfunction uniqueLaunchpadPreviewIconUrls(\n iconUrls: readonly (string | null | undefined)[]\n): string[] {\n const seen = new Set<string>();\n const result: string[] = [];\n for (const iconUrl of iconUrls) {\n const normalizedIconUrl = iconUrl?.trim();\n if (!normalizedIconUrl || seen.has(normalizedIconUrl)) {\n continue;\n }\n seen.add(normalizedIconUrl);\n result.push(normalizedIconUrl);\n }\n return result;\n}\n\nfunction normalizeLaunchpadSearchText(value: string): string {\n return value.trim().toLocaleLowerCase();\n}\n\nfunction clampInteger(value: number, min: number, max: number): number {\n if (!Number.isFinite(value)) {\n return min;\n }\n return Math.min(Math.max(Math.floor(value), min), max);\n}\n","const wheelDeltaLinePx = 16;\nconst wheelDeltaPagePx = 360;\nconst launchpadWheelNavigationThresholdPx = 64;\nconst launchpadWheelNavigationCooldownMs = 420;\nconst launchpadWheelHorizontalIntentRatio = 1.2;\n\nexport interface WorkbenchLaunchpadWheelNavigationState {\n accumulatedDeltaX: number;\n lastNavigationAt: number;\n}\n\nexport interface WorkbenchLaunchpadWheelNavigationResult {\n nextPageIndex: number | null;\n shouldPreventDefault: boolean;\n state: WorkbenchLaunchpadWheelNavigationState;\n}\n\nexport function createWorkbenchLaunchpadWheelNavigationState(): WorkbenchLaunchpadWheelNavigationState {\n return {\n accumulatedDeltaX: 0,\n lastNavigationAt: 0\n };\n}\n\nexport function resolveWorkbenchLaunchpadWheelNavigation(input: {\n currentPage: number;\n deltaMode?: number;\n deltaX: number;\n deltaY: number;\n pageCount: number;\n state: WorkbenchLaunchpadWheelNavigationState;\n timestamp: number;\n}): WorkbenchLaunchpadWheelNavigationResult {\n if (input.pageCount <= 1) {\n return {\n nextPageIndex: null,\n shouldPreventDefault: false,\n state: createWorkbenchLaunchpadWheelNavigationState()\n };\n }\n\n const delta = normalizeWorkbenchLaunchpadWheelDelta(input);\n const absDeltaX = Math.abs(delta.x);\n const absDeltaY = Math.abs(delta.y);\n const isHorizontalIntent =\n absDeltaX > 0 &&\n absDeltaX >= absDeltaY * launchpadWheelHorizontalIntentRatio;\n\n if (!isHorizontalIntent) {\n return {\n nextPageIndex: null,\n shouldPreventDefault: false,\n state: {\n accumulatedDeltaX: 0,\n lastNavigationAt: input.state.lastNavigationAt\n }\n };\n }\n\n const direction = delta.x > 0 ? 1 : -1;\n const previousDirection = input.state.accumulatedDeltaX > 0 ? 1 : -1;\n const accumulatedDeltaX =\n input.state.accumulatedDeltaX !== 0 && previousDirection === direction\n ? input.state.accumulatedDeltaX + delta.x\n : delta.x;\n\n if (\n input.timestamp - input.state.lastNavigationAt <\n launchpadWheelNavigationCooldownMs\n ) {\n return {\n nextPageIndex: null,\n shouldPreventDefault: true,\n state: {\n accumulatedDeltaX: 0,\n lastNavigationAt: input.state.lastNavigationAt\n }\n };\n }\n\n if (Math.abs(accumulatedDeltaX) < launchpadWheelNavigationThresholdPx) {\n return {\n nextPageIndex: null,\n shouldPreventDefault: true,\n state: {\n accumulatedDeltaX,\n lastNavigationAt: input.state.lastNavigationAt\n }\n };\n }\n\n const nextPageIndex = input.currentPage + direction;\n if (nextPageIndex < 0 || nextPageIndex >= input.pageCount) {\n return {\n nextPageIndex: null,\n shouldPreventDefault: true,\n state: {\n accumulatedDeltaX: 0,\n lastNavigationAt: input.timestamp\n }\n };\n }\n\n return {\n nextPageIndex,\n shouldPreventDefault: true,\n state: {\n accumulatedDeltaX: 0,\n lastNavigationAt: input.timestamp\n }\n };\n}\n\nfunction normalizeWorkbenchLaunchpadWheelDelta(input: {\n deltaMode?: number;\n deltaX: number;\n deltaY: number;\n}): { x: number; y: number } {\n const unit =\n input.deltaMode === 1\n ? wheelDeltaLinePx\n : input.deltaMode === 2\n ? wheelDeltaPagePx\n : 1;\n return {\n x: input.deltaX * unit,\n y: input.deltaY * unit\n };\n}\n","import { createElement, type ReactNode } from \"react\";\nimport type { WorkbenchHostDockEntry } from \"@tutti-os/workbench-surface\";\nimport {\n workbenchLaunchpadDockActionId,\n workbenchLaunchpadDockEntryId\n} from \"./launchpadModel.ts\";\n\nexport function createWorkbenchLaunchpadDockIcon(input: {\n tileIconUrls: readonly string[];\n}): ReactNode {\n return createElement(\n \"span\",\n {\n \"aria-hidden\": \"true\",\n className: \"workspace-launchpad-dock-icon\"\n },\n input.tileIconUrls.map((src, index) =>\n createElement(\n \"span\",\n {\n className: \"workspace-launchpad-dock-icon__tile\",\n key: `${src}:${index}`\n },\n createElement(\"img\", {\n alt: \"\",\n draggable: false,\n src\n })\n )\n )\n );\n}\n\nexport function createWorkbenchLaunchpadDockEntry(input: {\n label: string;\n order?: number;\n tileIconUrls: readonly string[];\n}): WorkbenchHostDockEntry {\n return {\n clickActionId: workbenchLaunchpadDockActionId,\n icon: createWorkbenchLaunchpadDockIcon({\n tileIconUrls: input.tileIconUrls\n }),\n id: workbenchLaunchpadDockEntryId,\n label: input.label,\n launchBehavior: \"enabled\",\n order: input.order ?? 0,\n sectionId: \"launchpad\",\n typeId: workbenchLaunchpadDockEntryId,\n visibility: \"always\"\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
.workspace-launchpad-dock-icon {
|
|
2
|
+
--workspace-launchpad-dock-icon-bg: var(--transparency-block);
|
|
3
|
+
--workspace-launchpad-dock-icon-border: var(--line-1);
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
display: grid;
|
|
6
|
+
width: 44px;
|
|
7
|
+
height: 44px;
|
|
8
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
9
|
+
gap: 2px;
|
|
10
|
+
padding: 3px;
|
|
11
|
+
border: 1px solid var(--workspace-launchpad-dock-icon-border);
|
|
12
|
+
border-radius: 8px;
|
|
13
|
+
background: var(--workspace-launchpad-dock-icon-bg);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.desktop-dock__slot[data-wallpaper-tone="dark"] .workspace-launchpad-dock-icon {
|
|
17
|
+
--workspace-launchpad-dock-icon-bg: rgb(255 255 255 / 18%);
|
|
18
|
+
--workspace-launchpad-dock-icon-border: rgb(255 255 255 / 15%);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.desktop-dock__slot[data-wallpaper-tone="light"]
|
|
22
|
+
.workspace-launchpad-dock-icon {
|
|
23
|
+
--workspace-launchpad-dock-icon-bg: rgb(0 0 0 / 10%);
|
|
24
|
+
--workspace-launchpad-dock-icon-border: rgb(0 0 0 / 10%);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.workspace-launchpad-dock-icon__tile {
|
|
28
|
+
display: block;
|
|
29
|
+
overflow: visible;
|
|
30
|
+
min-width: 0;
|
|
31
|
+
min-height: 0;
|
|
32
|
+
border-radius: 4px;
|
|
33
|
+
background: transparent;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.workspace-launchpad-dock-icon__tile > img {
|
|
37
|
+
display: block;
|
|
38
|
+
width: 100%;
|
|
39
|
+
height: 100%;
|
|
40
|
+
object-fit: contain;
|
|
41
|
+
object-position: center;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.workspace-launchpad-overlay {
|
|
45
|
+
position: absolute;
|
|
46
|
+
inset: 0;
|
|
47
|
+
z-index: 10;
|
|
48
|
+
overflow: hidden;
|
|
49
|
+
color: var(--text-primary);
|
|
50
|
+
pointer-events: auto;
|
|
51
|
+
-webkit-app-region: no-drag;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.workspace-launchpad-overlay--closing {
|
|
55
|
+
pointer-events: none;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.workspace-launchpad-overlay__dismiss {
|
|
59
|
+
position: absolute;
|
|
60
|
+
inset: 0;
|
|
61
|
+
z-index: 0;
|
|
62
|
+
border: 0;
|
|
63
|
+
background: var(--backdrop);
|
|
64
|
+
backdrop-filter: blur(24px) saturate(1.18);
|
|
65
|
+
animation: workspace-launchpad-backdrop-enter 160ms ease-out both;
|
|
66
|
+
opacity: 1;
|
|
67
|
+
transition: opacity 180ms ease;
|
|
68
|
+
-webkit-backdrop-filter: blur(24px) saturate(1.18);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.workspace-launchpad-overlay--closing .workspace-launchpad-overlay__dismiss {
|
|
72
|
+
opacity: 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.workspace-launchpad-overlay__content {
|
|
76
|
+
position: relative;
|
|
77
|
+
z-index: 1;
|
|
78
|
+
--workspace-launchpad-content-offset-x: 0px;
|
|
79
|
+
display: grid;
|
|
80
|
+
height: 100%;
|
|
81
|
+
min-height: 0;
|
|
82
|
+
grid-template-rows: 64px minmax(0, 1fr) 26px;
|
|
83
|
+
row-gap: 24px;
|
|
84
|
+
padding: 24px clamp(24px, 5vw, 76px) 106px;
|
|
85
|
+
opacity: 1;
|
|
86
|
+
transform: translateX(var(--workspace-launchpad-content-offset-x))
|
|
87
|
+
translateY(0) scale(1);
|
|
88
|
+
transition:
|
|
89
|
+
opacity 180ms ease,
|
|
90
|
+
transform 180ms cubic-bezier(0.4, 0, 1, 1);
|
|
91
|
+
-webkit-app-region: drag;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.workspace-launchpad-overlay__content[data-dock-placement="left"] {
|
|
95
|
+
--workspace-launchpad-content-offset-x: 24px;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@media (max-height: 900px) {
|
|
99
|
+
.workspace-launchpad-overlay__content {
|
|
100
|
+
padding-bottom: clamp(52px, 8vh, 84px);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.workspace-launchpad-overlay--closing .workspace-launchpad-overlay__content {
|
|
105
|
+
opacity: 0;
|
|
106
|
+
transform: translateX(var(--workspace-launchpad-content-offset-x))
|
|
107
|
+
translateY(10px) scale(0.985);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.workspace-workbench-shell--launchpad-open .workbench-dock-frame {
|
|
111
|
+
z-index: 20;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.workspace-launchpad-overlay__topbar {
|
|
115
|
+
position: relative;
|
|
116
|
+
z-index: 1;
|
|
117
|
+
display: flex;
|
|
118
|
+
min-width: 0;
|
|
119
|
+
align-items: flex-start;
|
|
120
|
+
justify-content: center;
|
|
121
|
+
-webkit-app-region: drag;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.workspace-launchpad-search {
|
|
125
|
+
position: relative;
|
|
126
|
+
display: block;
|
|
127
|
+
justify-self: center;
|
|
128
|
+
width: min(330px, calc(100vw - 80px));
|
|
129
|
+
-webkit-app-region: no-drag;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.workspace-launchpad-search__icon {
|
|
133
|
+
position: absolute;
|
|
134
|
+
inset-block: 0;
|
|
135
|
+
left: 11px;
|
|
136
|
+
width: 16px;
|
|
137
|
+
height: 16px;
|
|
138
|
+
margin-block: auto;
|
|
139
|
+
color: var(--text-secondary);
|
|
140
|
+
pointer-events: none;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.workspace-launchpad-search__icon svg {
|
|
144
|
+
display: block;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.workspace-launchpad-search__input {
|
|
148
|
+
box-sizing: border-box;
|
|
149
|
+
width: 100%;
|
|
150
|
+
height: 36px;
|
|
151
|
+
border: 1px solid var(--border-1);
|
|
152
|
+
border-radius: 8px;
|
|
153
|
+
padding: 0 40px 0 34px;
|
|
154
|
+
color: var(--text-primary);
|
|
155
|
+
background: var(--background-fronted);
|
|
156
|
+
box-shadow: none;
|
|
157
|
+
font: inherit;
|
|
158
|
+
font-size: 13px;
|
|
159
|
+
outline: none;
|
|
160
|
+
text-shadow: none;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.workspace-launchpad-search__input::placeholder {
|
|
164
|
+
color: var(--text-tertiary);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.workspace-launchpad-search__input::-webkit-search-cancel-button {
|
|
168
|
+
display: none;
|
|
169
|
+
-webkit-appearance: none;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.workspace-launchpad-search__input:focus,
|
|
173
|
+
.workspace-launchpad-search__input:focus-visible {
|
|
174
|
+
border-color: var(--border-focus);
|
|
175
|
+
box-shadow: none;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.workspace-launchpad-search__clear {
|
|
179
|
+
position: absolute;
|
|
180
|
+
inset-block: 0;
|
|
181
|
+
right: 8px;
|
|
182
|
+
display: grid;
|
|
183
|
+
width: 24px;
|
|
184
|
+
height: 24px;
|
|
185
|
+
margin-block: auto;
|
|
186
|
+
place-items: center;
|
|
187
|
+
border: 0;
|
|
188
|
+
border-radius: 999px;
|
|
189
|
+
background: transparent;
|
|
190
|
+
color: var(--text-tertiary);
|
|
191
|
+
cursor: pointer;
|
|
192
|
+
transition: color 120ms ease;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.workspace-launchpad-search__clear > svg {
|
|
196
|
+
width: 16px;
|
|
197
|
+
height: 16px;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.workspace-launchpad-search__clear:hover,
|
|
201
|
+
.workspace-launchpad-search__clear:active {
|
|
202
|
+
background: transparent;
|
|
203
|
+
color: var(--text-primary);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.workspace-launchpad-search__clear:focus-visible {
|
|
207
|
+
outline: 2px solid var(--border-focus);
|
|
208
|
+
outline-offset: 1px;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.workspace-launchpad-grid-viewport {
|
|
212
|
+
display: grid;
|
|
213
|
+
min-height: 0;
|
|
214
|
+
align-content: center;
|
|
215
|
+
justify-items: center;
|
|
216
|
+
overflow: visible;
|
|
217
|
+
-webkit-app-region: no-drag;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.workspace-launchpad-grid {
|
|
221
|
+
display: grid;
|
|
222
|
+
width: min(100%, 1040px);
|
|
223
|
+
align-content: center;
|
|
224
|
+
justify-content: center;
|
|
225
|
+
column-gap: clamp(20px, 4vw, 56px);
|
|
226
|
+
row-gap: clamp(22px, 3.5vh, 44px);
|
|
227
|
+
animation: workspace-launchpad-grid-enter 160ms ease-out;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.workspace-launchpad-item {
|
|
231
|
+
display: flex;
|
|
232
|
+
width: 136px;
|
|
233
|
+
min-width: 0;
|
|
234
|
+
min-height: 124px;
|
|
235
|
+
flex-direction: column;
|
|
236
|
+
align-items: center;
|
|
237
|
+
gap: 8px;
|
|
238
|
+
border: 0;
|
|
239
|
+
background: transparent;
|
|
240
|
+
color: var(--text-primary);
|
|
241
|
+
cursor: pointer;
|
|
242
|
+
text-align: center;
|
|
243
|
+
text-shadow: none;
|
|
244
|
+
transition:
|
|
245
|
+
opacity 140ms ease,
|
|
246
|
+
transform 150ms cubic-bezier(0.22, 1, 0.36, 1),
|
|
247
|
+
filter 140ms ease;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.workspace-launchpad-item-wrap {
|
|
251
|
+
position: relative;
|
|
252
|
+
width: 136px;
|
|
253
|
+
min-width: 0;
|
|
254
|
+
animation: workspace-launchpad-item-enter 420ms cubic-bezier(0.16, 1, 0.3, 1)
|
|
255
|
+
both;
|
|
256
|
+
animation-delay: var(--workspace-launchpad-item-delay, 0ms);
|
|
257
|
+
transform-origin: center bottom;
|
|
258
|
+
will-change: opacity, transform, filter;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.workspace-launchpad-item:hover {
|
|
262
|
+
transform: translateY(-2px);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.workspace-launchpad-item:active {
|
|
266
|
+
transform: translateY(0) scale(0.98);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.workspace-launchpad-item:focus-visible {
|
|
270
|
+
outline: 2px solid var(--border-focus);
|
|
271
|
+
outline-offset: 6px;
|
|
272
|
+
border-radius: 8px;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.workspace-launchpad-item--disabled {
|
|
276
|
+
cursor: default;
|
|
277
|
+
filter: grayscale(0.45);
|
|
278
|
+
opacity: 0.42;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.workspace-launchpad-item--disabled:hover,
|
|
282
|
+
.workspace-launchpad-item--disabled:active {
|
|
283
|
+
transform: none;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.workspace-launchpad-item__icon {
|
|
287
|
+
display: grid;
|
|
288
|
+
width: 54px;
|
|
289
|
+
height: 54px;
|
|
290
|
+
place-items: center;
|
|
291
|
+
overflow: visible;
|
|
292
|
+
border-radius: 0;
|
|
293
|
+
background: transparent;
|
|
294
|
+
box-shadow: none;
|
|
295
|
+
color: var(--text-primary);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.workspace-launchpad-item__icon > img {
|
|
299
|
+
display: block;
|
|
300
|
+
width: 100%;
|
|
301
|
+
height: 100%;
|
|
302
|
+
filter: none;
|
|
303
|
+
object-fit: contain;
|
|
304
|
+
object-position: center;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.workspace-launchpad-item__icon > svg {
|
|
308
|
+
width: 28px;
|
|
309
|
+
height: 28px;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.workspace-launchpad-item__label {
|
|
313
|
+
display: -webkit-box;
|
|
314
|
+
width: 100%;
|
|
315
|
+
min-height: 34px;
|
|
316
|
+
overflow: hidden;
|
|
317
|
+
-webkit-box-orient: vertical;
|
|
318
|
+
-webkit-line-clamp: 2;
|
|
319
|
+
color: inherit;
|
|
320
|
+
font-size: 13px;
|
|
321
|
+
font-weight: 400;
|
|
322
|
+
line-height: 17px;
|
|
323
|
+
overflow-wrap: anywhere;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.workspace-launchpad-dock-hover-panel {
|
|
327
|
+
--desktop-dock-tooltip-gap: 12px;
|
|
328
|
+
--workspace-launchpad-hover-panel-shift: 0px;
|
|
329
|
+
|
|
330
|
+
animation: workspace-launchpad-hover-panel-enter 160ms
|
|
331
|
+
cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
332
|
+
bottom: calc(100% + var(--desktop-dock-tooltip-gap, 12px));
|
|
333
|
+
left: 50%;
|
|
334
|
+
right: auto;
|
|
335
|
+
top: auto;
|
|
336
|
+
z-index: 45;
|
|
337
|
+
max-width: min(240px, calc(100vw - 32px));
|
|
338
|
+
opacity: 1;
|
|
339
|
+
pointer-events: auto;
|
|
340
|
+
transform: translateX(
|
|
341
|
+
calc(-50% + var(--workspace-launchpad-hover-panel-shift))
|
|
342
|
+
)
|
|
343
|
+
translateY(0);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.workspace-launchpad-empty {
|
|
347
|
+
align-self: center;
|
|
348
|
+
justify-self: center;
|
|
349
|
+
padding: 0;
|
|
350
|
+
border: 0;
|
|
351
|
+
background: transparent;
|
|
352
|
+
color: rgb(255 255 255 / 82%);
|
|
353
|
+
font-size: 14px;
|
|
354
|
+
line-height: 20px;
|
|
355
|
+
box-shadow: none;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.workspace-launchpad-pages {
|
|
359
|
+
display: flex;
|
|
360
|
+
min-height: 18px;
|
|
361
|
+
align-items: center;
|
|
362
|
+
justify-content: center;
|
|
363
|
+
gap: 9px;
|
|
364
|
+
-webkit-app-region: no-drag;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.workspace-launchpad-page-dot {
|
|
368
|
+
width: 8px;
|
|
369
|
+
height: 8px;
|
|
370
|
+
padding: 0;
|
|
371
|
+
border: 0;
|
|
372
|
+
border-radius: 999px;
|
|
373
|
+
background: rgb(255 255 255 / 28%);
|
|
374
|
+
cursor: pointer;
|
|
375
|
+
transition:
|
|
376
|
+
background-color 120ms ease,
|
|
377
|
+
transform 120ms ease;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.workspace-launchpad-page-dot[data-active="true"] {
|
|
381
|
+
background: rgb(255 255 255 / 92%);
|
|
382
|
+
transform: scale(1.12);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.workspace-launchpad-page-dot:focus-visible {
|
|
386
|
+
outline: 2px solid rgb(255 255 255 / 74%);
|
|
387
|
+
outline-offset: 4px;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.workspace-launchpad-tooltip-host {
|
|
391
|
+
display: block;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
@keyframes workspace-launchpad-grid-enter {
|
|
395
|
+
from {
|
|
396
|
+
opacity: 0;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
to {
|
|
400
|
+
opacity: 1;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
@keyframes workspace-launchpad-backdrop-enter {
|
|
405
|
+
from {
|
|
406
|
+
opacity: 0;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
to {
|
|
410
|
+
opacity: 1;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
@keyframes workspace-launchpad-hover-panel-enter {
|
|
415
|
+
from {
|
|
416
|
+
opacity: 0;
|
|
417
|
+
transform: translateX(-50%) translateY(10px);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
to {
|
|
421
|
+
opacity: 1;
|
|
422
|
+
transform: translateX(-50%) translateY(0);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
@keyframes workspace-launchpad-item-enter {
|
|
427
|
+
from {
|
|
428
|
+
opacity: 0;
|
|
429
|
+
filter: blur(2px);
|
|
430
|
+
transform: translateY(var(--workspace-launchpad-item-rise, 18px))
|
|
431
|
+
scale(0.96);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
58% {
|
|
435
|
+
opacity: 1;
|
|
436
|
+
filter: blur(0);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
to {
|
|
440
|
+
opacity: 1;
|
|
441
|
+
filter: blur(0);
|
|
442
|
+
transform: translateY(0) scale(1);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
@media (prefers-reduced-motion: reduce) {
|
|
447
|
+
.workspace-launchpad-grid,
|
|
448
|
+
.workspace-launchpad-item-wrap {
|
|
449
|
+
animation: none;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.workspace-launchpad-overlay__content,
|
|
453
|
+
.workspace-launchpad-overlay__dismiss {
|
|
454
|
+
animation: none;
|
|
455
|
+
transition: none;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.workspace-launchpad-item-wrap {
|
|
459
|
+
opacity: 1;
|
|
460
|
+
transform: none;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
@media (max-width: 720px) {
|
|
465
|
+
.workspace-launchpad-overlay__content {
|
|
466
|
+
row-gap: 18px;
|
|
467
|
+
padding-inline: 20px;
|
|
468
|
+
padding-bottom: 100px;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.workspace-launchpad-grid {
|
|
472
|
+
column-gap: 18px;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.workspace-launchpad-item,
|
|
476
|
+
.workspace-launchpad-item-wrap {
|
|
477
|
+
width: 112px;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.workspace-launchpad-item__icon {
|
|
481
|
+
width: 54px;
|
|
482
|
+
height: 54px;
|
|
483
|
+
border-radius: 16px;
|
|
484
|
+
}
|
|
485
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tutti-os/workbench-launchpad",
|
|
3
|
+
"version": "0.0.27",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./styles.css": "./dist/styles/workbench-launchpad.css"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/tutti-os/tutti.git",
|
|
21
|
+
"directory": "packages/workbench/launchpad"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@tutti-os/ui-system": "0.0.27",
|
|
25
|
+
"@tutti-os/workbench-surface": "0.0.27"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^24.0.1",
|
|
29
|
+
"@types/react": "^19.1.6",
|
|
30
|
+
"@types/react-dom": "^19.1.5",
|
|
31
|
+
"react": "^19.1.0",
|
|
32
|
+
"react-dom": "^19.1.0",
|
|
33
|
+
"typescript": "^5.8.3",
|
|
34
|
+
"@tutti-os/config-tsconfig": "0.0.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"react": "^19.1.0",
|
|
38
|
+
"react-dom": "^19.1.0"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsup --config tsup.config.ts && node ../../../tools/scripts/copy-package-assets.mjs src/styles dist/styles",
|
|
45
|
+
"test": "node --test --experimental-strip-types ./src/**/*.test.ts",
|
|
46
|
+
"typecheck": "node ../../../tools/scripts/run-tsgo-typecheck.mjs"
|
|
47
|
+
}
|
|
48
|
+
}
|