@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.
@@ -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
+ }