@tradalab/lyra 1.7.0 → 1.7.2

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.
@@ -140,7 +140,7 @@ function DataTable({
140
140
  ) : null
141
141
  ] }) : null
142
142
  ] }) : null,
143
- /* @__PURE__ */ jsx("div", { className: cn("overflow-hidden rounded-md border", className), children: /* @__PURE__ */ jsxs(Table, { children: [
143
+ /* @__PURE__ */ jsx("div", { className: cn("overflow-hidden border", className), children: /* @__PURE__ */ jsxs(Table, { children: [
144
144
  /* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((hg) => /* @__PURE__ */ jsx(TableRow, { children: hg.headers.map((header) => /* @__PURE__ */ jsx(TableHead, { children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext()) }, header.id)) }, hg.id)) }),
145
145
  /* @__PURE__ */ jsx(TableBody, { children: loading && rows.length === 0 ? (
146
146
  // Only replace the body with a loading row on the FIRST load; on a
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data-table/index.tsx"],"names":["TableHead","TableHeader","TableCell","TableRow","TableBody"],"mappings":"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","file":"index.js","sourcesContent":["import type { Cell, Column, ColumnDef, Header, HeaderGroup, Row, SortingState, Table } from \"@tanstack/react-table\"\nimport { flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from \"@tanstack/react-table\"\nimport { ArrowDownIcon, ArrowUpIcon, ChevronsUpDownIcon, RefreshCwIcon, SearchIcon } from \"lucide-react\"\nimport type { HTMLAttributes, ReactNode } from \"react\"\nimport { createContext, memo, useCallback, useContext, useState } from \"react\"\nimport { Button } from \"~/ui/primitives/button.js\"\nimport { Input } from \"~/ui/primitives/input.js\"\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from \"~/ui/primitives/dropdown-menu.js\"\nimport {\n TableBody as TableBodyRaw,\n TableCell as TableCellRaw,\n TableHeader as TableHeaderRaw,\n TableHead as TableHeadRaw,\n Table as TableRaw,\n TableRow as TableRowRaw,\n} from \"~/ui/primitives/table.js\"\nimport { cn } from \"~/utils/cn.js\"\n\nexport type { ColumnDef } from \"@tanstack/react-table\"\n\n\nexport const TableContext = createContext<{\n data: unknown[]\n columns: ColumnDef<unknown, unknown>[]\n table: Table<unknown> | null\n}>({\n data: [],\n columns: [],\n table: null,\n})\n\nexport type TableProviderProps<TData, TValue> = {\n columns: ColumnDef<TData, TValue>[]\n data: TData[]\n children: ReactNode\n className?: string\n}\n\nexport function TableProvider<TData, TValue>({ columns, data, children, className }: TableProviderProps<TData, TValue>) {\n const [sorting, setSorting] = useState<SortingState>([])\n const table = useReactTable({\n data,\n columns,\n getCoreRowModel: getCoreRowModel(),\n getSortedRowModel: getSortedRowModel(),\n // useState's setter is itself a valid TanStack OnChangeFn (it accepts a\n // value or an updater fn), so it can be wired directly — type-safe, no cast.\n onSortingChange: setSorting,\n state: {\n sorting,\n },\n })\n\n return (\n <TableContext.Provider\n value={{\n data,\n columns: columns as never,\n table: table as never,\n }}\n >\n <TableRaw className={className}>{children}</TableRaw>\n </TableContext.Provider>\n )\n}\n\nexport type TableHeadProps = {\n header: Header<unknown, unknown>\n className?: string\n}\n\nexport const TableHead = memo(({ header, className }: TableHeadProps) => (\n <TableHeadRaw className={className} key={header.id}>\n {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}\n </TableHeadRaw>\n))\n\nTableHead.displayName = \"TableHead\"\n\nexport type TableHeaderGroupProps = {\n headerGroup: HeaderGroup<unknown>\n children: (props: { header: Header<unknown, unknown> }) => ReactNode\n}\n\nexport const TableHeaderGroup = ({ headerGroup, children }: TableHeaderGroupProps) => (\n <TableRowRaw key={headerGroup.id}>{headerGroup.headers.map(header => children({ header }))}</TableRowRaw>\n)\n\nexport type TableHeaderProps = {\n className?: string\n children: (props: { headerGroup: HeaderGroup<unknown> }) => ReactNode\n}\n\nexport const TableHeader = ({ className, children }: TableHeaderProps) => {\n const { table } = useContext(TableContext)\n\n return <TableHeaderRaw className={className}>{table?.getHeaderGroups().map(headerGroup => children({ headerGroup }))}</TableHeaderRaw>\n}\n\nexport interface TableColumnHeaderProps<TData, TValue> extends HTMLAttributes<HTMLDivElement> {\n column: Column<TData, TValue>\n title: string\n}\n\nexport function TableColumnHeader<TData, TValue>({ column, title, className }: TableColumnHeaderProps<TData, TValue>) {\n // Extract inline event handlers to prevent unnecessary re-renders\n const handleSortAsc = useCallback(() => {\n column.toggleSorting(false)\n }, [column])\n\n const handleSortDesc = useCallback(() => {\n column.toggleSorting(true)\n }, [column])\n\n if (!column.getCanSort()) {\n return <div className={cn(className)}>{title}</div>\n }\n\n return (\n <div className={cn(\"flex items-center space-x-2\", className)}>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button className=\"-ml-3 h-8 data-[state=open]:bg-accent\" size=\"sm\" variant=\"ghost\">\n <span>{title}</span>\n {column.getIsSorted() === \"desc\" ? (\n <ArrowDownIcon className=\"ml-2 h-4 w-4\" />\n ) : column.getIsSorted() === \"asc\" ? (\n <ArrowUpIcon className=\"ml-2 h-4 w-4\" />\n ) : (\n <ChevronsUpDownIcon className=\"ml-2 h-4 w-4\" />\n )}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\">\n <DropdownMenuItem onClick={handleSortAsc}>\n <ArrowUpIcon className=\"mr-2 h-3.5 w-3.5 text-muted-foreground/70\" />\n Asc\n </DropdownMenuItem>\n <DropdownMenuItem onClick={handleSortDesc}>\n <ArrowDownIcon className=\"mr-2 h-3.5 w-3.5 text-muted-foreground/70\" />\n Desc\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n )\n}\n\nexport type TableCellProps = {\n cell: Cell<unknown, unknown>\n className?: string\n}\n\nexport const TableCell = ({ cell, className }: TableCellProps) => (\n <TableCellRaw className={className}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</TableCellRaw>\n)\n\nexport type TableRowProps = {\n row: Row<unknown>\n children: (props: { cell: Cell<unknown, unknown> }) => ReactNode\n className?: string\n}\n\nexport const TableRow = ({ row, children, className }: TableRowProps) => (\n <TableRowRaw className={className} data-state={row.getIsSelected() && \"selected\"} key={row.id}>\n {row.getVisibleCells().map(cell => children({ cell }))}\n </TableRowRaw>\n)\n\nexport type TableBodyProps = {\n children: (props: { row: Row<unknown> }) => ReactNode\n className?: string\n}\n\nexport const TableBody = ({ children, className }: TableBodyProps) => {\n const { columns, table } = useContext(TableContext)\n const rows = table?.getRowModel().rows\n\n return (\n <TableBodyRaw className={className}>\n {rows?.length ? (\n rows.map(row => children({ row }))\n ) : (\n <TableRowRaw>\n <TableCellRaw className=\"h-24 text-center\" colSpan={columns.length}>\n No results.\n </TableCellRaw>\n </TableRowRaw>\n )}\n </TableBodyRaw>\n )\n}\n\n// ---------------------------------------------------------------------------\n// DataTable — a batteries-included, self-contained paginated table. Unlike the\n// composition primitives above (TableProvider + render-prop parts), this is a\n// drop-in: pass `columns` + `data` and get header, body, empty/loading states\n// and Prev/Next pagination. All user-facing labels are props (English\n// defaults) so apps can feed i18n strings; the component itself ships no copy.\n// ---------------------------------------------------------------------------\n\nexport interface DataTableProps<TData, TValue> {\n columns: ColumnDef<TData, TValue>[]\n data: TData[]\n /** Show a single-row loading state instead of the body. */\n loading?: boolean\n /** Row shown when there are no results. */\n emptyText?: string\n /** Row shown while `loading`. */\n loadingText?: string\n /** Previous-page button label. */\n previousLabel?: string\n /** Next-page button label. */\n nextLabel?: string\n /** Rows per page (default 10). Pagination controls appear only when needed. */\n pageSize?: number\n /** Extra classes on the bordered table wrapper. */\n className?: string\n // --- control bar (all optional; the bar renders only when one is set) ---\n /** Show a search box that globally filters rows across all accessor columns. */\n searchable?: boolean\n /** Placeholder for the search box. */\n searchPlaceholder?: string\n /** Show a reload button; called on click. Its icon spins while `loading`. */\n onReload?: () => void\n /** Accessible label / tooltip for the reload button. */\n reloadLabel?: string\n /** Custom controls rendered on the right of the bar (e.g. a \"New\" button). */\n toolbar?: ReactNode\n}\n\nexport function DataTable<TData, TValue>({\n columns,\n data,\n loading,\n emptyText = \"No results.\",\n loadingText = \"Loading…\",\n previousLabel = \"Previous\",\n nextLabel = \"Next\",\n pageSize = 10,\n className,\n searchable,\n searchPlaceholder = \"Search…\",\n onReload,\n reloadLabel = \"Reload\",\n toolbar,\n}: DataTableProps<TData, TValue>) {\n const [globalFilter, setGlobalFilter] = useState(\"\")\n const table = useReactTable({\n data,\n columns,\n state: { globalFilter },\n onGlobalFilterChange: setGlobalFilter,\n getCoreRowModel: getCoreRowModel(),\n getFilteredRowModel: getFilteredRowModel(),\n getPaginationRowModel: getPaginationRowModel(),\n initialState: { pagination: { pageSize } },\n })\n\n const rows = table.getRowModel().rows\n const showBar = Boolean(searchable || onReload || toolbar)\n\n return (\n <div>\n {showBar ? (\n <div className=\"flex items-center gap-2 pb-4\">\n {searchable ? (\n <div className=\"relative w-full max-w-xs\">\n <SearchIcon className=\"text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2\" />\n <Input\n value={globalFilter}\n onChange={e => table.setGlobalFilter(e.target.value)}\n placeholder={searchPlaceholder}\n className=\"pl-8\"\n />\n </div>\n ) : null}\n {onReload || toolbar ? (\n <div className=\"ml-auto flex items-center gap-2\">\n {toolbar}\n {onReload ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon-sm\"\n onClick={onReload}\n disabled={loading}\n aria-label={reloadLabel}\n title={reloadLabel}\n >\n <RefreshCwIcon className={cn(\"size-4\", loading && \"animate-spin\")} />\n </Button>\n ) : null}\n </div>\n ) : null}\n </div>\n ) : null}\n <div className={cn(\"overflow-hidden rounded-md border\", className)}>\n <TableRaw>\n <TableHeaderRaw>\n {table.getHeaderGroups().map(hg => (\n <TableRowRaw key={hg.id}>\n {hg.headers.map(header => (\n <TableHeadRaw key={header.id}>\n {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}\n </TableHeadRaw>\n ))}\n </TableRowRaw>\n ))}\n </TableHeaderRaw>\n <TableBodyRaw>\n {loading && rows.length === 0 ? (\n // Only replace the body with a loading row on the FIRST load; on a\n // refetch keep the (stale) rows visible while the reload icon spins.\n <TableRowRaw>\n <TableCellRaw className=\"h-24 text-center\" colSpan={columns.length}>\n {loadingText}\n </TableCellRaw>\n </TableRowRaw>\n ) : rows.length ? (\n rows.map(row => (\n <TableRowRaw key={row.id} data-state={row.getIsSelected() && \"selected\"}>\n {row.getVisibleCells().map(cell => (\n <TableCellRaw key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</TableCellRaw>\n ))}\n </TableRowRaw>\n ))\n ) : (\n <TableRowRaw>\n <TableCellRaw className=\"text-muted-foreground h-24 text-center\" colSpan={columns.length}>\n {emptyText}\n </TableCellRaw>\n </TableRowRaw>\n )}\n </TableBodyRaw>\n </TableRaw>\n </div>\n {table.getPageCount() > 1 ? (\n <div className=\"flex items-center justify-end gap-2 py-4\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}>\n {previousLabel}\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}>\n {nextLabel}\n </Button>\n </div>\n ) : null}\n </div>\n )\n}\n"]}
1
+ {"version":3,"sources":["../../src/data-table/index.tsx"],"names":["TableHead","TableHeader","TableCell","TableRow","TableBody"],"mappings":"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","file":"index.js","sourcesContent":["import type { Cell, Column, ColumnDef, Header, HeaderGroup, Row, SortingState, Table } from \"@tanstack/react-table\"\nimport { flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from \"@tanstack/react-table\"\nimport { ArrowDownIcon, ArrowUpIcon, ChevronsUpDownIcon, RefreshCwIcon, SearchIcon } from \"lucide-react\"\nimport type { HTMLAttributes, ReactNode } from \"react\"\nimport { createContext, memo, useCallback, useContext, useState } from \"react\"\nimport { Button } from \"~/ui/primitives/button.js\"\nimport { Input } from \"~/ui/primitives/input.js\"\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from \"~/ui/primitives/dropdown-menu.js\"\nimport {\n TableBody as TableBodyRaw,\n TableCell as TableCellRaw,\n TableHeader as TableHeaderRaw,\n TableHead as TableHeadRaw,\n Table as TableRaw,\n TableRow as TableRowRaw,\n} from \"~/ui/primitives/table.js\"\nimport { cn } from \"~/utils/cn.js\"\n\nexport type { ColumnDef } from \"@tanstack/react-table\"\n\n\nexport const TableContext = createContext<{\n data: unknown[]\n columns: ColumnDef<unknown, unknown>[]\n table: Table<unknown> | null\n}>({\n data: [],\n columns: [],\n table: null,\n})\n\nexport type TableProviderProps<TData, TValue> = {\n columns: ColumnDef<TData, TValue>[]\n data: TData[]\n children: ReactNode\n className?: string\n}\n\nexport function TableProvider<TData, TValue>({ columns, data, children, className }: TableProviderProps<TData, TValue>) {\n const [sorting, setSorting] = useState<SortingState>([])\n const table = useReactTable({\n data,\n columns,\n getCoreRowModel: getCoreRowModel(),\n getSortedRowModel: getSortedRowModel(),\n // useState's setter is itself a valid TanStack OnChangeFn (it accepts a\n // value or an updater fn), so it can be wired directly — type-safe, no cast.\n onSortingChange: setSorting,\n state: {\n sorting,\n },\n })\n\n return (\n <TableContext.Provider\n value={{\n data,\n columns: columns as never,\n table: table as never,\n }}\n >\n <TableRaw className={className}>{children}</TableRaw>\n </TableContext.Provider>\n )\n}\n\nexport type TableHeadProps = {\n header: Header<unknown, unknown>\n className?: string\n}\n\nexport const TableHead = memo(({ header, className }: TableHeadProps) => (\n <TableHeadRaw className={className} key={header.id}>\n {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}\n </TableHeadRaw>\n))\n\nTableHead.displayName = \"TableHead\"\n\nexport type TableHeaderGroupProps = {\n headerGroup: HeaderGroup<unknown>\n children: (props: { header: Header<unknown, unknown> }) => ReactNode\n}\n\nexport const TableHeaderGroup = ({ headerGroup, children }: TableHeaderGroupProps) => (\n <TableRowRaw key={headerGroup.id}>{headerGroup.headers.map(header => children({ header }))}</TableRowRaw>\n)\n\nexport type TableHeaderProps = {\n className?: string\n children: (props: { headerGroup: HeaderGroup<unknown> }) => ReactNode\n}\n\nexport const TableHeader = ({ className, children }: TableHeaderProps) => {\n const { table } = useContext(TableContext)\n\n return <TableHeaderRaw className={className}>{table?.getHeaderGroups().map(headerGroup => children({ headerGroup }))}</TableHeaderRaw>\n}\n\nexport interface TableColumnHeaderProps<TData, TValue> extends HTMLAttributes<HTMLDivElement> {\n column: Column<TData, TValue>\n title: string\n}\n\nexport function TableColumnHeader<TData, TValue>({ column, title, className }: TableColumnHeaderProps<TData, TValue>) {\n // Extract inline event handlers to prevent unnecessary re-renders\n const handleSortAsc = useCallback(() => {\n column.toggleSorting(false)\n }, [column])\n\n const handleSortDesc = useCallback(() => {\n column.toggleSorting(true)\n }, [column])\n\n if (!column.getCanSort()) {\n return <div className={cn(className)}>{title}</div>\n }\n\n return (\n <div className={cn(\"flex items-center space-x-2\", className)}>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button className=\"-ml-3 h-8 data-[state=open]:bg-accent\" size=\"sm\" variant=\"ghost\">\n <span>{title}</span>\n {column.getIsSorted() === \"desc\" ? (\n <ArrowDownIcon className=\"ml-2 h-4 w-4\" />\n ) : column.getIsSorted() === \"asc\" ? (\n <ArrowUpIcon className=\"ml-2 h-4 w-4\" />\n ) : (\n <ChevronsUpDownIcon className=\"ml-2 h-4 w-4\" />\n )}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\">\n <DropdownMenuItem onClick={handleSortAsc}>\n <ArrowUpIcon className=\"mr-2 h-3.5 w-3.5 text-muted-foreground/70\" />\n Asc\n </DropdownMenuItem>\n <DropdownMenuItem onClick={handleSortDesc}>\n <ArrowDownIcon className=\"mr-2 h-3.5 w-3.5 text-muted-foreground/70\" />\n Desc\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n )\n}\n\nexport type TableCellProps = {\n cell: Cell<unknown, unknown>\n className?: string\n}\n\nexport const TableCell = ({ cell, className }: TableCellProps) => (\n <TableCellRaw className={className}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</TableCellRaw>\n)\n\nexport type TableRowProps = {\n row: Row<unknown>\n children: (props: { cell: Cell<unknown, unknown> }) => ReactNode\n className?: string\n}\n\nexport const TableRow = ({ row, children, className }: TableRowProps) => (\n <TableRowRaw className={className} data-state={row.getIsSelected() && \"selected\"} key={row.id}>\n {row.getVisibleCells().map(cell => children({ cell }))}\n </TableRowRaw>\n)\n\nexport type TableBodyProps = {\n children: (props: { row: Row<unknown> }) => ReactNode\n className?: string\n}\n\nexport const TableBody = ({ children, className }: TableBodyProps) => {\n const { columns, table } = useContext(TableContext)\n const rows = table?.getRowModel().rows\n\n return (\n <TableBodyRaw className={className}>\n {rows?.length ? (\n rows.map(row => children({ row }))\n ) : (\n <TableRowRaw>\n <TableCellRaw className=\"h-24 text-center\" colSpan={columns.length}>\n No results.\n </TableCellRaw>\n </TableRowRaw>\n )}\n </TableBodyRaw>\n )\n}\n\n// ---------------------------------------------------------------------------\n// DataTable — a batteries-included, self-contained paginated table. Unlike the\n// composition primitives above (TableProvider + render-prop parts), this is a\n// drop-in: pass `columns` + `data` and get header, body, empty/loading states\n// and Prev/Next pagination. All user-facing labels are props (English\n// defaults) so apps can feed i18n strings; the component itself ships no copy.\n// ---------------------------------------------------------------------------\n\nexport interface DataTableProps<TData, TValue> {\n columns: ColumnDef<TData, TValue>[]\n data: TData[]\n /** Show a single-row loading state instead of the body. */\n loading?: boolean\n /** Row shown when there are no results. */\n emptyText?: string\n /** Row shown while `loading`. */\n loadingText?: string\n /** Previous-page button label. */\n previousLabel?: string\n /** Next-page button label. */\n nextLabel?: string\n /** Rows per page (default 10). Pagination controls appear only when needed. */\n pageSize?: number\n /** Extra classes on the bordered table wrapper. */\n className?: string\n // --- control bar (all optional; the bar renders only when one is set) ---\n /** Show a search box that globally filters rows across all accessor columns. */\n searchable?: boolean\n /** Placeholder for the search box. */\n searchPlaceholder?: string\n /** Show a reload button; called on click. Its icon spins while `loading`. */\n onReload?: () => void\n /** Accessible label / tooltip for the reload button. */\n reloadLabel?: string\n /** Custom controls rendered on the right of the bar (e.g. a \"New\" button). */\n toolbar?: ReactNode\n}\n\nexport function DataTable<TData, TValue>({\n columns,\n data,\n loading,\n emptyText = \"No results.\",\n loadingText = \"Loading…\",\n previousLabel = \"Previous\",\n nextLabel = \"Next\",\n pageSize = 10,\n className,\n searchable,\n searchPlaceholder = \"Search…\",\n onReload,\n reloadLabel = \"Reload\",\n toolbar,\n}: DataTableProps<TData, TValue>) {\n const [globalFilter, setGlobalFilter] = useState(\"\")\n const table = useReactTable({\n data,\n columns,\n state: { globalFilter },\n onGlobalFilterChange: setGlobalFilter,\n getCoreRowModel: getCoreRowModel(),\n getFilteredRowModel: getFilteredRowModel(),\n getPaginationRowModel: getPaginationRowModel(),\n initialState: { pagination: { pageSize } },\n })\n\n const rows = table.getRowModel().rows\n const showBar = Boolean(searchable || onReload || toolbar)\n\n return (\n <div>\n {showBar ? (\n <div className=\"flex items-center gap-2 pb-4\">\n {searchable ? (\n <div className=\"relative w-full max-w-xs\">\n <SearchIcon className=\"text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2\" />\n <Input\n value={globalFilter}\n onChange={e => table.setGlobalFilter(e.target.value)}\n placeholder={searchPlaceholder}\n className=\"pl-8\"\n />\n </div>\n ) : null}\n {onReload || toolbar ? (\n <div className=\"ml-auto flex items-center gap-2\">\n {toolbar}\n {onReload ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon-sm\"\n onClick={onReload}\n disabled={loading}\n aria-label={reloadLabel}\n title={reloadLabel}\n >\n <RefreshCwIcon className={cn(\"size-4\", loading && \"animate-spin\")} />\n </Button>\n ) : null}\n </div>\n ) : null}\n </div>\n ) : null}\n <div className={cn(\"overflow-hidden border\", className)}>\n <TableRaw>\n <TableHeaderRaw>\n {table.getHeaderGroups().map(hg => (\n <TableRowRaw key={hg.id}>\n {hg.headers.map(header => (\n <TableHeadRaw key={header.id}>\n {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}\n </TableHeadRaw>\n ))}\n </TableRowRaw>\n ))}\n </TableHeaderRaw>\n <TableBodyRaw>\n {loading && rows.length === 0 ? (\n // Only replace the body with a loading row on the FIRST load; on a\n // refetch keep the (stale) rows visible while the reload icon spins.\n <TableRowRaw>\n <TableCellRaw className=\"h-24 text-center\" colSpan={columns.length}>\n {loadingText}\n </TableCellRaw>\n </TableRowRaw>\n ) : rows.length ? (\n rows.map(row => (\n <TableRowRaw key={row.id} data-state={row.getIsSelected() && \"selected\"}>\n {row.getVisibleCells().map(cell => (\n <TableCellRaw key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</TableCellRaw>\n ))}\n </TableRowRaw>\n ))\n ) : (\n <TableRowRaw>\n <TableCellRaw className=\"text-muted-foreground h-24 text-center\" colSpan={columns.length}>\n {emptyText}\n </TableCellRaw>\n </TableRowRaw>\n )}\n </TableBodyRaw>\n </TableRaw>\n </div>\n {table.getPageCount() > 1 ? (\n <div className=\"flex items-center justify-end gap-2 py-4\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}>\n {previousLabel}\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}>\n {nextLabel}\n </Button>\n </div>\n ) : null}\n </div>\n )\n}\n"]}
@@ -343,9 +343,25 @@ interface AppGridItem {
343
343
  /** Opens on a different origin (adds `target="_blank"` + `rel`). */
344
344
  external?: boolean;
345
345
  }
346
- interface AppGridProps {
347
- /** Apps to render — already filtered by the caller. */
346
+ /** A titled section of apps for {@link AppGrid}'s grouped mode. */
347
+ interface AppGridGroup {
348
+ /** Stable React list key. */
349
+ key: string;
350
+ /** Section heading. */
351
+ label: string;
352
+ /** Optional sub-heading under the label. */
353
+ description?: string;
354
+ /** Apps in this group — already filtered by the caller. */
348
355
  apps: AppGridItem[];
356
+ }
357
+ interface AppGridProps {
358
+ /** Flat mode: apps to render (already filtered). Ignored when `groups` is set. */
359
+ apps?: AppGridItem[];
360
+ /**
361
+ * Grouped mode: titled sections (already filtered). When provided, renders a
362
+ * heading + tile grid per group; empty groups are dropped.
363
+ */
364
+ groups?: AppGridGroup[];
349
365
  /** Controlled search value. Pass together with `onQueryChange` to show the search box. */
350
366
  query?: string;
351
367
  onQueryChange?: (query: string) => void;
@@ -353,7 +369,7 @@ interface AppGridProps {
353
369
  searchPlaceholder?: string;
354
370
  /** Autofocus the search box on mount. Default `false`. */
355
371
  autoFocusSearch?: boolean;
356
- /** Message shown when `apps` is empty. */
372
+ /** Message shown when there are no apps. */
357
373
  emptyLabel?: string;
358
374
  className?: string;
359
375
  }
@@ -381,7 +397,7 @@ declare const defaultAppGridLabels: {
381
397
  * searchPlaceholder={t("search")} emptyLabel={t("empty")}
382
398
  * />
383
399
  */
384
- declare function AppGrid({ apps, query, onQueryChange, searchPlaceholder, autoFocusSearch, emptyLabel, className, }: AppGridProps): React.JSX.Element;
400
+ declare function AppGrid({ apps, groups, query, onQueryChange, searchPlaceholder, autoFocusSearch, emptyLabel, className, }: AppGridProps): React.JSX.Element;
385
401
  /**
386
402
  * A single application tile: the icon in a tinted rounded chip above the name +
387
403
  * description. Exported for callers that render their own grid.
@@ -390,4 +406,4 @@ declare function AppTile({ app }: {
390
406
  app: AppGridItem;
391
407
  }): React.JSX.Element;
392
408
 
393
- export { AppGrid, type AppGridItem, type AppGridProps, AppShell, AppShellBody, AppShellHeader, AppShellMain, type AppShellProps, AppTile, Breadcrumbs, type BreadcrumbsProps, type MatchMode, NavBrand, type NavBrandApp, type NavBrandProps, NavGroup, type NavGroupProps, type NavIcon, NavItem, type NavItemProps, NavSection, type NavSectionProps, NavUser, type NavUserProps, type NavUserThemeLabels, SidebarDock, SidebarPanel, StackedSidebar, TabBar, type TabBarLabels, type TabBarProps, type TabItem, type UseActiveRoute, defaultAppGridLabels, defaultNavBrandAppsLabel, defaultNavUserThemeLabels, defaultTabBarLabels, useActiveRoute };
409
+ export { AppGrid, type AppGridGroup, type AppGridItem, type AppGridProps, AppShell, AppShellBody, AppShellHeader, AppShellMain, type AppShellProps, AppTile, Breadcrumbs, type BreadcrumbsProps, type MatchMode, NavBrand, type NavBrandApp, type NavBrandProps, NavGroup, type NavGroupProps, type NavIcon, NavItem, type NavItemProps, NavSection, type NavSectionProps, NavUser, type NavUserProps, type NavUserThemeLabels, SidebarDock, SidebarPanel, StackedSidebar, TabBar, type TabBarLabels, type TabBarProps, type TabItem, type UseActiveRoute, defaultAppGridLabels, defaultNavBrandAppsLabel, defaultNavUserThemeLabels, defaultTabBarLabels, useActiveRoute };
@@ -507,6 +507,7 @@ var defaultAppGridLabels = {
507
507
  };
508
508
  function AppGrid({
509
509
  apps,
510
+ groups,
510
511
  query,
511
512
  onQueryChange,
512
513
  searchPlaceholder = defaultAppGridLabels.searchPlaceholder,
@@ -514,6 +515,10 @@ function AppGrid({
514
515
  emptyLabel = defaultAppGridLabels.empty,
515
516
  className
516
517
  }) {
518
+ const grouped = groups != null;
519
+ const sections = grouped ? groups.filter((g) => g.apps.length > 0) : [];
520
+ const flat = apps ?? [];
521
+ const isEmpty = grouped ? sections.length === 0 : flat.length === 0;
517
522
  return /* @__PURE__ */ jsxs("div", { className: cn("space-y-8", className), children: [
518
523
  onQueryChange ? /* @__PURE__ */ jsxs(InputGroup, { className: "mx-auto h-10 max-w-md", children: [
519
524
  /* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(Search, {}) }),
@@ -529,9 +534,18 @@ function AppGrid({
529
534
  }
530
535
  )
531
536
  ] }) : null,
532
- apps.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground py-16 text-center text-sm", children: emptyLabel }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4", children: apps.map((app) => /* @__PURE__ */ jsx(AppTile, { app }, app.key)) })
537
+ isEmpty ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground py-16 text-center text-sm", children: emptyLabel }) : grouped ? /* @__PURE__ */ jsx("div", { className: "space-y-10", children: sections.map((g) => /* @__PURE__ */ jsxs("section", { className: "space-y-4", children: [
538
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
539
+ /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold tracking-tight", children: g.label }),
540
+ g.description ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: g.description }) : null
541
+ ] }),
542
+ /* @__PURE__ */ jsx(AppTileGrid, { apps: g.apps })
543
+ ] }, g.key)) }) : /* @__PURE__ */ jsx(AppTileGrid, { apps: flat })
533
544
  ] });
534
545
  }
546
+ function AppTileGrid({ apps }) {
547
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4", children: apps.map((app) => /* @__PURE__ */ jsx(AppTile, { app }, app.key)) });
548
+ }
535
549
  function AppTile({ app }) {
536
550
  return /* @__PURE__ */ jsx(
537
551
  "a",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/shell/app-shell.tsx","../../src/shell/stacked-sidebar.tsx","../../src/shell/nav-group.tsx","../../src/shell/use-active-route.ts","../../src/shell/nav-item.tsx","../../src/shell/nav-section.tsx","../../src/shell/nav-brand.tsx","../../src/shell/nav-user.tsx","../../src/shell/tab-bar.tsx","../../src/shell/breadcrumbs.tsx","../../src/shell/app-grid.tsx"],"names":["jsx","jsxs","Fragment","ChevronsUpDown","Link"],"mappings":"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","file":"index.js","sourcesContent":["\"use client\"\n\nimport type { ComponentProps, ReactNode } from \"react\"\nimport {\n SidebarInset,\n SidebarProvider,\n} from \"~/ui/primitives/sidebar.js\"\nimport { cn } from \"~/utils/cn.js\"\n\nexport interface AppShellProps\n extends Omit<ComponentProps<typeof SidebarProvider>, \"children\"> {\n children: ReactNode\n}\n\n/**\n * App shell composition root. Wires `SidebarProvider`. Pair with\n * `<Sidebar>` from `@tradalab/lyra/ui` and `<AppShellMain>` for the\n * right-hand pane.\n *\n * @example\n * import { AppShell, AppShellMain, AppShellHeader, AppShellBody, NavGroup, NavItem, Breadcrumbs } from \"@tradalab/lyra/shell\"\n * import { Sidebar, SidebarHeader, SidebarContent, SidebarFooter, SidebarTrigger } from \"@tradalab/lyra/ui\"\n *\n * <AppShell>\n * <Sidebar collapsible=\"icon\">\n * <SidebarHeader>{logo}</SidebarHeader>\n * <SidebarContent>\n * <NavGroup label=\"Primitives\">{...}</NavGroup>\n * </SidebarContent>\n * <SidebarFooter>{user}</SidebarFooter>\n * </Sidebar>\n * <AppShellMain>\n * <AppShellHeader><SidebarTrigger /><Breadcrumbs /></AppShellHeader>\n * <AppShellBody>{children}</AppShellBody>\n * </AppShellMain>\n * </AppShell>\n */\nexport function AppShell({ children, ...rest }: AppShellProps) {\n return <SidebarProvider {...rest}>{children}</SidebarProvider>\n}\n\n/** Right-hand pane container. Alias for `SidebarInset`. */\nexport const AppShellMain = SidebarInset\n\n/** Top bar inside `<AppShellMain>`. Standardizes h-12 + border-b + flex row. */\nexport function AppShellHeader({\n className,\n children,\n ...rest\n}: ComponentProps<\"header\">) {\n return (\n <header\n className={cn(\n \"flex h-12 shrink-0 items-center gap-2 border-b px-3\",\n className,\n )}\n {...rest}\n >\n {children}\n </header>\n )\n}\n\n/** Scroll body inside `<AppShellMain>`. Sibling of `<AppShellHeader>`. */\nexport function AppShellBody({\n className,\n children,\n ...rest\n}: ComponentProps<\"div\">) {\n return (\n <div className={cn(\"flex-1 overflow-auto\", className)} {...rest}>\n {children}\n </div>\n )\n}\n","\"use client\"\n\nimport type { ComponentProps } from \"react\"\nimport { Sidebar } from \"~/ui/primitives/sidebar.js\"\nimport { cn } from \"~/utils/cn.js\"\n\n/**\n * Dual / stacked sidebar wrapper — an icon rail plus one or more content\n * panels rendered side-by-side inside a single `<Sidebar>`. This is the\n * shadcn \"sidebar-07\" pattern.\n *\n * The `*:data-[sidebar=sidebar]:flex-row` trick lays the inner\n * `[data-sidebar=sidebar]` wrappers of the nested {@link SidebarDock} /\n * {@link SidebarPanel} columns out horizontally. `variant`/`collapsible`\n * default to `\"sidebar\"`/`\"icon\"` but can be overridden.\n *\n * @example\n * import { AppShell, AppShellMain, StackedSidebar, SidebarDock, SidebarPanel } from \"@tradalab/lyra/shell\"\n * import { SidebarHeader, SidebarContent, SidebarMenu, SidebarMenuItem, SidebarMenuButton } from \"@tradalab/lyra/ui\"\n *\n * <AppShell>\n * <StackedSidebar>\n * <SidebarDock>\n * <SidebarHeader>{logo}</SidebarHeader>\n * <SidebarContent>{iconButtons}</SidebarContent>\n * </SidebarDock>\n * {tab === \"files\" ? <SidebarPanel>{tree}</SidebarPanel> : null}\n * </StackedSidebar>\n * <AppShellMain>{children}</AppShellMain>\n * </AppShell>\n */\nexport function StackedSidebar({\n className,\n ...rest\n}: ComponentProps<typeof Sidebar>) {\n return (\n <Sidebar\n variant=\"sidebar\"\n collapsible=\"icon\"\n {...rest}\n className={cn(\n \"overflow-hidden *:data-[sidebar=sidebar]:flex-row\",\n className,\n )}\n />\n )\n}\n\n/**\n * Narrow icon-rail column inside a {@link StackedSidebar}. Fixed to the icon\n * width, never collapses on its own (the parent `StackedSidebar` owns the\n * icon-mode toggle).\n *\n * Named `SidebarDock` — not `SidebarRail` — because `SidebarRail` already\n * exists in `@tradalab/lyra/ui` as shadcn's drag-to-resize edge handle.\n */\nexport function SidebarDock({\n className,\n ...rest\n}: ComponentProps<typeof Sidebar>) {\n return (\n <Sidebar\n {...rest}\n collapsible=\"none\"\n className={cn(\n \"w-[calc(var(--sidebar-width-icon)+1px)]! overflow-hidden border-r\",\n className,\n )}\n />\n )\n}\n\n/**\n * Wider content column inside a {@link StackedSidebar} (tree, list, …).\n * Fills remaining width; render conditionally to show/hide per app state.\n */\nexport function SidebarPanel({\n className,\n ...rest\n}: ComponentProps<typeof Sidebar>) {\n return (\n <Sidebar\n {...rest}\n collapsible=\"none\"\n className={cn(\"flex-1 overflow-hidden\", className)}\n />\n )\n}\n","\"use client\"\n\nimport type { ReactNode } from \"react\"\nimport {\n SidebarGroup,\n SidebarGroupContent,\n SidebarGroupLabel,\n SidebarMenu,\n} from \"~/ui/primitives/sidebar.js\"\n\nexport interface NavGroupProps {\n label?: ReactNode\n children: ReactNode\n}\n\nexport function NavGroup({ label, children }: NavGroupProps) {\n return (\n <SidebarGroup>\n {label != null ? <SidebarGroupLabel>{label}</SidebarGroupLabel> : null}\n <SidebarGroupContent>\n <SidebarMenu>{children}</SidebarMenu>\n </SidebarGroupContent>\n </SidebarGroup>\n )\n}\n","\"use client\"\n\nimport { usePathname } from \"next/navigation\"\n\nexport type MatchMode = \"exact\" | \"prefix\"\n\nexport interface UseActiveRoute {\n pathname: string\n isActive: (href: string, mode?: MatchMode) => boolean\n}\n\n/**\n * App-Router pathname helper. Default `mode` is `\"prefix\"`.\n *\n * Root (`/`) is always exact-matched regardless of `mode` — otherwise the\n * root nav item would light up on every nested page.\n */\nexport function useActiveRoute(): UseActiveRoute {\n const pathname = usePathname() ?? \"/\"\n return {\n pathname,\n isActive: (href, mode = \"prefix\") => {\n if (mode === \"exact\" || href === \"/\") return pathname === href\n return pathname === href || pathname.startsWith(`${href}/`)\n },\n }\n}\n","\"use client\"\n\nimport Link from \"next/link\"\nimport type { ReactNode } from \"react\"\nimport {\n SidebarMenuButton,\n SidebarMenuItem,\n} from \"~/ui/primitives/sidebar.js\"\nimport { useActiveRoute, type MatchMode } from \"./use-active-route.js\"\n\nexport type NavIcon = ReactNode\n\nexport interface NavItemProps {\n href: string\n label: string\n /** Pre-rendered icon element, e.g. `<Globe />`. Element form so the prop is serializable across the RSC boundary. */\n icon?: NavIcon\n /** Sidebar tooltip; visible in collapsed icon mode. */\n tooltip?: string\n /** Default `\"prefix\"` — matches `href` and any nested path. */\n matchMode?: MatchMode\n}\n\n/** Sidebar nav row. Must be rendered inside a {@link NavGroup}. */\nexport function NavItem({\n href,\n label,\n icon,\n tooltip,\n matchMode = \"prefix\",\n}: NavItemProps) {\n const { isActive } = useActiveRoute()\n return (\n <SidebarMenuItem>\n <SidebarMenuButton\n asChild\n isActive={isActive(href, matchMode)}\n tooltip={tooltip}\n >\n <Link href={href}>\n {icon}\n <span>{label}</span>\n </Link>\n </SidebarMenuButton>\n </SidebarMenuItem>\n )\n}\n","\"use client\"\n\nimport type { ReactNode } from \"react\"\nimport { NavGroup } from \"./nav-group.js\"\nimport { NavItem, type NavItemProps } from \"./nav-item.js\"\n\nexport interface NavSectionProps {\n /** Optional group heading. */\n label?: ReactNode\n /** Nav rows — each rendered as a {@link NavItem}. */\n items: NavItemProps[]\n}\n\n/**\n * Config-driven convenience over {@link NavGroup} + {@link NavItem}: pass a\n * data array instead of hand-mapping JSX. For dynamic or conditional rows,\n * compose `NavGroup`/`NavItem` directly.\n *\n * @example\n * const NAV = [\n * { href: \"/files\", label: \"Files\", icon: <Folder /> },\n * { href: \"/trash\", label: \"Trash\", icon: <Trash2 /> },\n * ]\n * <NavSection label=\"Workspace\" items={NAV} />\n */\nexport function NavSection({ label, items }: NavSectionProps) {\n return (\n <NavGroup label={label}>\n {items.map((item) => (\n <NavItem key={item.href} {...item} />\n ))}\n </NavGroup>\n )\n}\n","\"use client\"\n\nimport type { ReactNode } from \"react\"\nimport { ChevronsUpDown } from \"lucide-react\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"~/ui/primitives/dropdown-menu.js\"\nimport {\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n useSidebar,\n} from \"~/ui/primitives/sidebar.js\"\n\n/** An application shown in the {@link NavBrand} header / switcher. */\nexport interface NavBrandApp {\n /** Display name. */\n name: string\n /**\n * Pre-rendered icon/logo element, e.g. `<Boxes />`. Element form (not a\n * component reference) so the prop stays serializable across the RSC\n * boundary — same convention as {@link NavItem}'s `icon`.\n */\n icon: ReactNode\n /** Navigation target. Usually an absolute cross-app URL (full navigation). */\n href: string\n /** Optional secondary line under the name (tagline / domain). */\n description?: string\n}\n\nexport interface NavBrandProps {\n /** The current app, rendered in the sidebar header. */\n app: NavBrandApp\n /**\n * Other apps to switch to. When omitted or empty the header renders as a\n * plain link to `app.href` (no dropdown, no chevron).\n */\n apps?: NavBrandApp[]\n /** Heading above the app list in the dropdown. Default `\"Applications\"`. */\n appsLabel?: string\n /** Extra menu content appended below the app list (e.g. an \"Add app\" row). */\n children?: ReactNode\n}\n\n/** Default English heading for the switcher's app list — override for i18n. */\nexport const defaultNavBrandAppsLabel = \"Applications\"\n\n/** Icon tile + name/description shown inside the header button. */\nfunction BrandContent({ app }: { app: NavBrandApp }) {\n return (\n <>\n <div className=\"bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-none [&_svg]:size-4\">\n {app.icon}\n </div>\n <div className=\"grid flex-1 text-left text-sm leading-tight\">\n <span className=\"truncate font-semibold\">{app.name}</span>\n {app.description ? (\n <span className=\"text-muted-foreground truncate text-xs\">\n {app.description}\n </span>\n ) : null}\n </div>\n </>\n )\n}\n\n/**\n * Sidebar-header brand block with an optional app switcher — the standardized\n * `SidebarHeader` content for app shells (pairs with {@link NavUser} in the\n * footer). Presentational: the app owns the config, icons, and labels (same\n * split as {@link TabBar}).\n *\n * With `apps`, the header is a dropdown trigger (chevron) listing the other\n * apps as links; without it, the header is a plain link to `app.href`. Cross-app\n * links use a plain `<a>` so switching does a full navigation.\n *\n * Must render inside an `AppShell`/`SidebarProvider`, and below a\n * `TooltipProvider` (the collapsed-mode tooltip needs one — app root providers\n * normally mount it).\n *\n * @example\n * import { NavBrand } from \"@tradalab/lyra/shell\"\n * import { SidebarHeader } from \"@tradalab/lyra/ui\"\n * import { Boxes, HardDrive, ShieldCheck } from \"lucide-react\"\n *\n * <SidebarHeader>\n * <NavBrand\n * app={{ name: \"Admin\", description: \"Platform console\", icon: <ShieldCheck />, href: \"/\" }}\n * apps={[\n * { name: \"Drive\", icon: <HardDrive />, href: \"https://drive.tradalab.com\" },\n * { name: \"Toolbox\", icon: <Boxes />, href: \"https://toolbox.tradalab.com\" },\n * ]}\n * />\n * </SidebarHeader>\n */\nexport function NavBrand({\n app,\n apps,\n appsLabel = defaultNavBrandAppsLabel,\n children,\n}: NavBrandProps) {\n const { isMobile } = useSidebar()\n const hasSwitcher = (apps?.length ?? 0) > 0 || children != null\n\n return (\n <SidebarMenu>\n <SidebarMenuItem>\n {hasSwitcher ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <SidebarMenuButton\n size=\"lg\"\n tooltip={app.name}\n className=\"data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground\"\n >\n <BrandContent app={app} />\n <ChevronsUpDown className=\"ml-auto size-4\" />\n </SidebarMenuButton>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n className=\"w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-none\"\n align=\"start\"\n side={isMobile ? \"bottom\" : \"right\"}\n sideOffset={4}\n >\n <DropdownMenuLabel className=\"text-muted-foreground text-xs\">\n {appsLabel}\n </DropdownMenuLabel>\n {apps?.map((item) => (\n <DropdownMenuItem key={item.href} asChild className=\"gap-2 p-2\">\n <a href={item.href}>\n <div className=\"flex size-6 items-center justify-center rounded-none border [&_svg]:size-3.5\">\n {item.icon}\n </div>\n <span className=\"truncate\">{item.name}</span>\n </a>\n </DropdownMenuItem>\n ))}\n {children ? (\n <>\n <DropdownMenuSeparator />\n {children}\n </>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n ) : (\n <SidebarMenuButton size=\"lg\" asChild tooltip={app.name}>\n <a href={app.href}>\n <BrandContent app={app} />\n </a>\n </SidebarMenuButton>\n )}\n </SidebarMenuItem>\n </SidebarMenu>\n )\n}\n","\"use client\"\n\nimport type { ReactNode } from \"react\"\nimport {\n ChevronsUpDown,\n CircleUser,\n LogIn,\n LogOut,\n Monitor,\n Moon,\n Palette,\n Sun,\n UserRoundCog,\n} from \"lucide-react\"\nimport { useTheme } from \"next-themes\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"~/ui/primitives/avatar.js\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from \"~/ui/primitives/dropdown-menu.js\"\nimport {\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n useSidebar,\n} from \"~/ui/primitives/sidebar.js\"\nimport {\n defaultAccountMenuLabels,\n initialsOf,\n type AccountMenuLabels,\n type AccountMenuUser,\n} from \"~/ui/account-menu.js\"\n\nexport interface NavUserThemeLabels {\n theme: string\n light: string\n dark: string\n system: string\n}\n\n/** Default English theme-submenu labels — spread + override for i18n. */\nexport const defaultNavUserThemeLabels: NavUserThemeLabels = {\n theme: \"Theme\",\n light: \"Light\",\n dark: \"Dark\",\n system: \"System\",\n}\n\nexport interface NavUserProps {\n /**\n * Signed-in user. When `null`/`undefined`, renders a guest menu (icon +\n * theme + Sign in) if `onSignIn` is set, otherwise renders nothing.\n */\n user?: AccountMenuUser | null\n /** Account-action labels — reuses lyra's {@link AccountMenuLabels}. */\n labels?: Partial<AccountMenuLabels>\n /** Switch-account handler (signed-in only). Item hidden if omitted. */\n onSwitchAccount?: () => void\n /** Sign-out handler (signed-in only). Item hidden if omitted. */\n onSignOut?: () => void\n /**\n * Sign-in handler (guest only). When set, a null `user` renders the guest\n * menu with a Sign in action instead of rendering nothing.\n */\n onSignIn?: () => void\n /** Render the light/dark/system theme submenu. Default `true`. */\n showTheme?: boolean\n /** Theme-submenu labels. */\n themeLabels?: Partial<NavUserThemeLabels>\n /** Extra menu content inserted below the theme submenu. */\n children?: ReactNode\n}\n\nconst THEMES = [\n { value: \"light\", key: \"light\", icon: Sun },\n { value: \"dark\", key: \"dark\", icon: Moon },\n { value: \"system\", key: \"system\", icon: Monitor },\n] as const\n\n/**\n * Sidebar-footer account menu — the standardized NavUser for app shells.\n * Avatar trigger (size `lg`) + name/email opening a dropdown with an optional\n * theme switcher and switch-account / sign-out actions. Auth flows stay in the\n * app (callbacks); labels are props for i18n.\n *\n * Differs from {@link AccountMenu} in `@tradalab/lyra/ui`: that one is a\n * generic header dropdown; this is styled for the collapsible sidebar footer\n * and follows `useSidebar()` for mobile placement.\n *\n * Must render inside an `AppShell`/SidebarProvider, and below a\n * `TooltipProvider` (the avatar button's collapsed-mode tooltip requires one —\n * app root providers normally mount it). The theme submenu needs a\n * `next-themes` provider mounted (it no-ops without one); pass\n * `showTheme={false}` for apps with a fixed theme.\n *\n * @example\n * import { NavUser } from \"@tradalab/lyra/shell\"\n * import { SidebarFooter } from \"@tradalab/lyra/ui\"\n *\n * <SidebarFooter>\n * <NavUser\n * user={session.user}\n * onSwitchAccount={() => signIn(\"zitadel\", { redirectTo: \"/\" }, { prompt: \"select_account\" })}\n * onSignOut={() => signOut({ redirectTo: \"/\" })}\n * />\n * </SidebarFooter>\n */\nexport function NavUser({\n user,\n labels,\n onSwitchAccount,\n onSignOut,\n onSignIn,\n showTheme = true,\n themeLabels,\n children,\n}: NavUserProps) {\n const { isMobile } = useSidebar()\n const { theme, setTheme } = useTheme()\n const l = { ...defaultAccountMenuLabels, ...labels }\n const t = { ...defaultNavUserThemeLabels, ...themeLabels }\n\n // Guests render nothing unless a sign-in handler opts them into a guest menu.\n if (!user && !onSignIn) return null\n\n const name = user ? (user.name ?? user.email ?? l.account) : l.guest\n const email = user?.email ?? \"\"\n\n const avatar = (\n <Avatar className=\"size-8 rounded-lg\">\n {user?.image ? <AvatarImage src={user.image} alt={name} /> : null}\n <AvatarFallback className=\"rounded-lg\">\n {user ? initialsOf(name) : <CircleUser className=\"size-4\" />}\n </AvatarFallback>\n </Avatar>\n )\n\n const themeSubmenu = showTheme ? (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuSub>\n <DropdownMenuSubTrigger>\n <Palette className=\"mr-2 size-4\" />\n {t.theme}\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent>\n <DropdownMenuRadioGroup value={theme ?? \"system\"} onValueChange={setTheme}>\n {THEMES.map(({ value, key, icon: Icon }) => (\n <DropdownMenuRadioItem key={value} value={value}>\n <Icon className=\"mr-2 size-4\" />\n {t[key]}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n </>\n ) : null\n\n return (\n <SidebarMenu>\n <SidebarMenuItem>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <SidebarMenuButton\n size=\"lg\"\n tooltip={name}\n className=\"data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground\"\n >\n {avatar}\n <div className=\"grid flex-1 text-left text-sm leading-tight\">\n <span className=\"truncate font-medium\">{name}</span>\n {email ? (\n <span className=\"text-muted-foreground truncate text-xs\">\n {email}\n </span>\n ) : null}\n </div>\n <ChevronsUpDown className=\"ml-auto size-4\" />\n </SidebarMenuButton>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n className=\"w-(--radix-dropdown-menu-trigger-width) min-w-56\"\n side={isMobile ? \"bottom\" : \"right\"}\n align=\"end\"\n sideOffset={4}\n >\n <DropdownMenuLabel className=\"flex items-center gap-2\">\n {avatar}\n <div className=\"grid flex-1 text-left leading-tight\">\n <span className=\"truncate text-sm font-medium\">{name}</span>\n {email ? (\n <span className=\"text-muted-foreground truncate text-xs font-normal\">\n {email}\n </span>\n ) : null}\n </div>\n </DropdownMenuLabel>\n\n {themeSubmenu}\n\n {children ? (\n <>\n <DropdownMenuSeparator />\n {children}\n </>\n ) : null}\n\n {user ? (\n <>\n {onSwitchAccount || onSignOut ? <DropdownMenuSeparator /> : null}\n {onSwitchAccount ? (\n <DropdownMenuItem onSelect={onSwitchAccount}>\n <UserRoundCog className=\"mr-2 size-4\" />\n {l.switchAccount}\n </DropdownMenuItem>\n ) : null}\n {onSignOut ? (\n <DropdownMenuItem variant=\"destructive\" onSelect={onSignOut}>\n <LogOut className=\"mr-2 size-4\" />\n {l.signOut}\n </DropdownMenuItem>\n ) : null}\n </>\n ) : (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem onSelect={onSignIn}>\n <LogIn className=\"mr-2 size-4\" />\n {l.signIn}\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </SidebarMenuItem>\n </SidebarMenu>\n )\n}\n","\"use client\"\n\nimport { useEffect, useRef, type ReactNode, type WheelEvent } from \"react\"\nimport { ChevronDown, ListX, Pin, PinOff, Trash2, X } from \"lucide-react\"\nimport { Button } from \"~/ui/primitives/button.js\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"~/ui/primitives/dropdown-menu.js\"\nimport { ScrollArea, ScrollBar } from \"~/ui/primitives/scroll-area.js\"\nimport {\n ContextMenu,\n ContextMenuContent,\n ContextMenuItem,\n ContextMenuSeparator,\n ContextMenuTrigger,\n} from \"~/ui/primitives/context-menu.js\"\nimport { cn } from \"~/utils/cn.js\"\n\nexport interface TabItem {\n id: string\n title: string\n /** Secondary line under the title (e.g. connection/db, file path). */\n subtitle?: ReactNode\n /** Leading icon element — the app maps its own tab type → icon. */\n icon?: ReactNode\n pinned?: boolean\n}\n\nexport interface TabBarLabels {\n close: string\n pinTab: string\n unpinTab: string\n closeOthers: string\n closeAll: string\n}\n\n/** Default English labels — spread + override for i18n. */\nexport const defaultTabBarLabels: TabBarLabels = {\n close: \"Close\",\n pinTab: \"Pin tab\",\n unpinTab: \"Unpin tab\",\n closeOthers: \"Close others\",\n closeAll: \"Close all\",\n}\n\nexport interface TabBarProps {\n tabs: TabItem[]\n activeId: string | null\n onSelect: (id: string) => void\n onClose: (id: string) => void\n /** Pin row in the context menu is hidden when omitted. */\n onTogglePin?: (id: string) => void\n /** \"Close others\" row is hidden when omitted. */\n onCloseOthers?: (id: string) => void\n /** \"Close all\" row is hidden when omitted. */\n onCloseAll?: () => void\n labels?: Partial<TabBarLabels>\n}\n\n/**\n * Horizontal tab strip for the content area of stacked-sidebar apps\n * (redishub, loom). Presentational + store-agnostic: the app owns tab state\n * and passes `tabs` + callbacks; `icon`/`subtitle` are per-tab so apps map\n * their own tab types, and labels are props for i18n. Renders nothing when\n * `tabs` is empty.\n *\n * Sizes rows from the `--tab-height` CSS var — set it in your globals.\n *\n * @example\n * const s = useTabStore()\n * <TabBar\n * tabs={s.tabs.map(t => ({ id: t.id, title: t.title, pinned: t.pinned, icon: <Icon t={t.type}/>, subtitle: fmt(t) }))}\n * activeId={s.activeTabId}\n * onSelect={s.setActiveTabId}\n * onClose={s.removeTab}\n * onTogglePin={s.togglePin}\n * onCloseOthers={s.closeOthers}\n * onCloseAll={s.closeAll}\n * />\n */\nexport function TabBar({\n tabs,\n activeId,\n onSelect,\n onClose,\n onTogglePin,\n onCloseOthers,\n onCloseAll,\n labels,\n}: TabBarProps) {\n const l = { ...defaultTabBarLabels, ...labels }\n const activeTabRef = useRef<HTMLDivElement>(null)\n\n // Scroll the active tab into view when it changes.\n useEffect(() => {\n if (activeId && activeTabRef.current) {\n activeTabRef.current.scrollIntoView({\n behavior: \"smooth\",\n block: \"nearest\",\n inline: \"nearest\",\n })\n }\n }, [activeId])\n\n // Map vertical wheel scroll to horizontal scroll over the tab strip.\n const handleWheel = (e: WheelEvent<HTMLDivElement>) => {\n if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) {\n const viewport = e.currentTarget.querySelector(\n '[data-slot=\"scroll-area-viewport\"]',\n )\n if (viewport) viewport.scrollLeft += e.deltaY * 0.5\n }\n }\n\n if (tabs.length === 0) return null\n\n const hasContextActions = Boolean(onTogglePin || onCloseOthers || onCloseAll)\n\n return (\n <div\n className=\"group/tabbar flex w-full shrink-0 items-center overflow-hidden border-b bg-muted/30\"\n style={{ height: \"var(--tab-height)\" }}\n >\n <ScrollArea\n type=\"hover\"\n className=\"min-w-0 flex-1 overflow-hidden [&_[data-orientation=vertical]]:hidden\"\n onWheel={handleWheel}\n >\n <div\n className=\"flex w-max items-center gap-0\"\n style={{ height: \"var(--tab-height)\" }}\n >\n {tabs.map((tab) => {\n const isActive = activeId === tab.id\n return (\n <ContextMenu key={tab.id}>\n <ContextMenuTrigger asChild>\n <div\n ref={isActive ? activeTabRef : null}\n onClick={() => onSelect(tab.id)}\n title={tab.title}\n className={cn(\n \"group relative flex min-w-[140px] max-w-[240px] shrink-0 cursor-pointer select-none items-center gap-2 border-r px-3 transition-colors\",\n isActive\n ? \"border-t-2 border-t-primary bg-background\"\n : \"border-t-2 border-t-transparent bg-transparent text-muted-foreground hover:bg-muted/50\",\n tab.pinned && \"min-w-[100px] bg-primary/5\",\n )}\n style={{ height: \"var(--tab-height)\" }}\n >\n {tab.icon ? (\n <div className=\"relative shrink-0\">\n <span\n className={cn(\n \"flex [&_svg]:size-3.5\",\n isActive && \"text-primary\",\n )}\n >\n {tab.icon}\n </span>\n {tab.pinned ? (\n <div className=\"absolute -right-1.5 -top-1.5 rounded-full bg-background p-0.5 shadow-sm\">\n <Pin className=\"size-2 fill-primary text-primary\" />\n </div>\n ) : null}\n </div>\n ) : null}\n <div className=\"flex min-w-0 flex-1 flex-col items-start justify-center overflow-hidden py-1\">\n <span\n className={cn(\n \"w-full truncate text-xs leading-tight\",\n isActive && \"font-medium text-foreground\",\n )}\n >\n {tab.title}\n </span>\n {tab.subtitle ? (\n <span className=\"mt-0.5 w-full truncate text-[10px] leading-none text-muted-foreground/70\">\n {tab.subtitle}\n </span>\n ) : null}\n </div>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n title={l.close}\n className={cn(\n \"size-5 shrink-0 rounded-md opacity-0 transition-opacity hover:bg-muted-foreground/20 group-hover:opacity-100\",\n isActive && \"opacity-100\",\n )}\n onClick={(e) => {\n e.stopPropagation()\n onClose(tab.id)\n }}\n >\n <X className=\"size-3\" />\n </Button>\n {isActive ? (\n <div className=\"absolute bottom-0 left-0 right-0 z-10 h-[2px] bg-background\" />\n ) : null}\n </div>\n </ContextMenuTrigger>\n {hasContextActions ? (\n <ContextMenuContent className=\"w-48\">\n {onTogglePin ? (\n <>\n <ContextMenuItem onClick={() => onTogglePin(tab.id)}>\n {tab.pinned ? (\n <>\n <PinOff className=\"mr-2 size-4\" />\n <span>{l.unpinTab}</span>\n </>\n ) : (\n <>\n <Pin className=\"mr-2 size-4\" />\n <span>{l.pinTab}</span>\n </>\n )}\n </ContextMenuItem>\n <ContextMenuSeparator />\n </>\n ) : null}\n <ContextMenuItem onClick={() => onClose(tab.id)}>\n <X className=\"mr-2 size-4\" />\n <span>{l.close}</span>\n </ContextMenuItem>\n {onCloseOthers ? (\n <ContextMenuItem onClick={() => onCloseOthers(tab.id)}>\n <ListX className=\"mr-2 size-4\" />\n <span>{l.closeOthers}</span>\n </ContextMenuItem>\n ) : null}\n {onCloseAll ? (\n <ContextMenuItem onClick={() => onCloseAll()}>\n <Trash2 className=\"mr-2 size-4\" />\n <span>{l.closeAll}</span>\n </ContextMenuItem>\n ) : null}\n </ContextMenuContent>\n ) : null}\n </ContextMenu>\n )\n })}\n </div>\n <ScrollBar orientation=\"horizontal\" className=\"z-20 h-1.5 hover:h-2\" />\n </ScrollArea>\n\n <div\n className=\"flex shrink-0 items-center border-l bg-muted/30 px-1\"\n style={{ height: \"var(--tab-height)\" }}\n >\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" className=\"size-8 shrink-0 rounded-sm\">\n <ChevronDown className=\"size-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n align=\"end\"\n className=\"max-h-[400px] w-[260px] overflow-y-auto\"\n >\n {tabs.map((tab) => (\n <DropdownMenuItem\n key={tab.id}\n onClick={() => onSelect(tab.id)}\n title={tab.title}\n className={cn(\n \"cursor-pointer gap-2 py-2\",\n activeId === tab.id && \"bg-accent\",\n )}\n >\n {tab.icon ? (\n <div className=\"relative shrink-0\">\n <span className=\"flex text-muted-foreground [&_svg]:size-4\">\n {tab.icon}\n </span>\n {tab.pinned ? (\n <Pin className=\"absolute -right-1 -top-1 size-2 fill-primary text-primary\" />\n ) : null}\n </div>\n ) : null}\n <div className=\"flex flex-1 flex-col items-start overflow-hidden leading-tight\">\n <span className=\"w-full flex-1 truncate text-sm\">{tab.title}</span>\n {tab.subtitle ? (\n <span className=\"w-full truncate text-[10px] text-muted-foreground\">\n {tab.subtitle}\n </span>\n ) : null}\n </div>\n <X\n className=\"size-4 shrink-0 rounded-sm p-0.5 hover:bg-red-50 hover:text-red-500\"\n onClick={(e) => {\n e.stopPropagation()\n onClose(tab.id)\n }}\n />\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n )\n}\n","\"use client\"\n\nimport { Fragment, type ComponentProps, type ReactNode } from \"react\"\nimport Link from \"next/link\"\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from \"~/ui/primitives/breadcrumb.js\"\nimport { useActiveRoute } from \"./use-active-route.js\"\n\nexport interface BreadcrumbsProps extends ComponentProps<typeof Breadcrumb> {\n /** Separator content. Falls back to shadcn's chevron when omitted. */\n separator?: ReactNode\n /** Segment-id → display-label map. Unmapped segments render raw. */\n labels?: Record<string, string>\n}\n\n/** Pathname-derived breadcrumbs. Renders nothing on the root route. */\nexport function Breadcrumbs({ separator, labels, ...rest }: BreadcrumbsProps) {\n const { pathname } = useActiveRoute()\n const segments = pathname.split(\"/\").filter(Boolean)\n if (segments.length === 0) return null\n const last = segments.length - 1\n return (\n <Breadcrumb {...rest}>\n <BreadcrumbList>\n {segments.map((seg, i) => {\n const href = \"/\" + segments.slice(0, i + 1).join(\"/\")\n const label = labels?.[seg] ?? seg\n return (\n <Fragment key={href}>\n {i > 0 ? (\n <BreadcrumbSeparator>{separator}</BreadcrumbSeparator>\n ) : null}\n <BreadcrumbItem>\n {i === last ? (\n <BreadcrumbPage>{label}</BreadcrumbPage>\n ) : (\n <BreadcrumbLink asChild>\n <Link href={href}>{label}</Link>\n </BreadcrumbLink>\n )}\n </BreadcrumbItem>\n </Fragment>\n )\n })}\n </BreadcrumbList>\n </Breadcrumb>\n )\n}\n","import type { ReactNode } from \"react\"\nimport { Search } from \"lucide-react\"\n\nimport { cn } from \"~/utils/cn.js\"\nimport {\n Card,\n CardDescription,\n CardTitle,\n} from \"~/ui/primitives/card.js\"\nimport {\n InputGroup,\n InputGroupAddon,\n InputGroupInput,\n} from \"~/ui/primitives/input-group.js\"\n\n/** One application in the {@link AppGrid} — rendered as an {@link AppTile}. */\nexport interface AppGridItem {\n /** Stable React list key. */\n key: string\n /** Display name. */\n name: string\n /** Optional tagline shown under the name. */\n description?: string\n /** Navigation target (usually an absolute cross-app URL). */\n href: string\n /**\n * Pre-rendered icon element (RSC-serializable), e.g. `<ToolCase />`. Sized by\n * the tile (`[&_svg]:size-7`) — same convention as {@link NavBrandApp}'s icon.\n */\n icon: ReactNode\n /**\n * Tailwind class string tinting the icon tile — `text-…` (colors the icon via\n * currentColor) + `bg-…` (tile background). Pass full class strings, not\n * interpolated fragments, so the JIT keeps them.\n */\n accent?: string\n /** Opens on a different origin (adds `target=\"_blank\"` + `rel`). */\n external?: boolean\n}\n\nexport interface AppGridProps {\n /** Apps to render — already filtered by the caller. */\n apps: AppGridItem[]\n /** Controlled search value. Pass together with `onQueryChange` to show the search box. */\n query?: string\n onQueryChange?: (query: string) => void\n /** Search input placeholder. */\n searchPlaceholder?: string\n /** Autofocus the search box on mount. Default `false`. */\n autoFocusSearch?: boolean\n /** Message shown when `apps` is empty. */\n emptyLabel?: string\n className?: string\n}\n\n/** Default English labels — override for i18n. */\nexport const defaultAppGridLabels = {\n searchPlaceholder: \"Search apps…\",\n empty: \"No apps found.\",\n}\n\n/**\n * A responsive grid of application tiles with an optional search box — the \"app\n * grid\" / launcher surface (a portal home / \"all apps\" page). Presentational:\n * the app owns the registry, i18n, icons, and the filter — pass the already-\n * filtered `apps` plus a controlled `query`. Same app/lyra split as\n * {@link NavBrand} and {@link TabBar}.\n *\n * @example\n * const [q, setQ] = useState(\"\")\n * const filtered = APPS.filter(a => match(a, q))\n * <AppGrid\n * apps={filtered.map(a => ({\n * key: a.slug, name: t(a.slug), description: t(`${a.slug}.desc`),\n * href: a.href, icon: <a.Icon />, accent: a.accent, external: true,\n * }))}\n * query={q} onQueryChange={setQ}\n * searchPlaceholder={t(\"search\")} emptyLabel={t(\"empty\")}\n * />\n */\nexport function AppGrid({\n apps,\n query,\n onQueryChange,\n searchPlaceholder = defaultAppGridLabels.searchPlaceholder,\n autoFocusSearch = false,\n emptyLabel = defaultAppGridLabels.empty,\n className,\n}: AppGridProps) {\n return (\n <div className={cn(\"space-y-8\", className)}>\n {onQueryChange ? (\n <InputGroup className=\"mx-auto h-10 max-w-md\">\n <InputGroupAddon>\n <Search />\n </InputGroupAddon>\n <InputGroupInput\n type=\"search\"\n value={query ?? \"\"}\n onChange={(e) => onQueryChange(e.target.value)}\n placeholder={searchPlaceholder}\n aria-label={searchPlaceholder}\n autoFocus={autoFocusSearch}\n />\n </InputGroup>\n ) : null}\n\n {apps.length === 0 ? (\n <p className=\"text-muted-foreground py-16 text-center text-sm\">\n {emptyLabel}\n </p>\n ) : (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4\">\n {apps.map((app) => (\n <AppTile key={app.key} app={app} />\n ))}\n </div>\n )}\n </div>\n )\n}\n\n/**\n * A single application tile: the icon in a tinted rounded chip above the name +\n * description. Exported for callers that render their own grid.\n */\nexport function AppTile({ app }: { app: AppGridItem }) {\n return (\n <a\n href={app.href}\n target={app.external ? \"_blank\" : undefined}\n rel={app.external ? \"noreferrer\" : undefined}\n className=\"focus-visible:ring-ring group rounded-none outline-none focus-visible:ring-2\"\n >\n <Card className=\"h-full items-center gap-4 px-6 py-8 text-center transition-all duration-200 group-hover:-translate-y-0.5 group-hover:shadow-md group-hover:ring-foreground/30\">\n {/* Tinted icon tile (sharp — matches lyra's rounded-none aesthetic);\n `accent` carries the text-… (icon, via currentColor) + bg-… tint. */}\n <div\n className={cn(\n \"flex size-14 items-center justify-center rounded-none ring-1 ring-foreground/5 ring-inset transition-transform duration-200 group-hover:scale-105 [&_svg]:size-7\",\n app.accent,\n )}\n >\n {app.icon}\n </div>\n <div className=\"space-y-1\">\n <CardTitle>{app.name}</CardTitle>\n {app.description ? (\n <CardDescription className=\"line-clamp-2\">\n {app.description}\n </CardDescription>\n ) : null}\n </div>\n </Card>\n </a>\n )\n}\n"]}
1
+ {"version":3,"sources":["../../src/shell/app-shell.tsx","../../src/shell/stacked-sidebar.tsx","../../src/shell/nav-group.tsx","../../src/shell/use-active-route.ts","../../src/shell/nav-item.tsx","../../src/shell/nav-section.tsx","../../src/shell/nav-brand.tsx","../../src/shell/nav-user.tsx","../../src/shell/tab-bar.tsx","../../src/shell/breadcrumbs.tsx","../../src/shell/app-grid.tsx"],"names":["jsx","jsxs","Fragment","ChevronsUpDown","Link"],"mappings":"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","file":"index.js","sourcesContent":["\"use client\"\n\nimport type { ComponentProps, ReactNode } from \"react\"\nimport {\n SidebarInset,\n SidebarProvider,\n} from \"~/ui/primitives/sidebar.js\"\nimport { cn } from \"~/utils/cn.js\"\n\nexport interface AppShellProps\n extends Omit<ComponentProps<typeof SidebarProvider>, \"children\"> {\n children: ReactNode\n}\n\n/**\n * App shell composition root. Wires `SidebarProvider`. Pair with\n * `<Sidebar>` from `@tradalab/lyra/ui` and `<AppShellMain>` for the\n * right-hand pane.\n *\n * @example\n * import { AppShell, AppShellMain, AppShellHeader, AppShellBody, NavGroup, NavItem, Breadcrumbs } from \"@tradalab/lyra/shell\"\n * import { Sidebar, SidebarHeader, SidebarContent, SidebarFooter, SidebarTrigger } from \"@tradalab/lyra/ui\"\n *\n * <AppShell>\n * <Sidebar collapsible=\"icon\">\n * <SidebarHeader>{logo}</SidebarHeader>\n * <SidebarContent>\n * <NavGroup label=\"Primitives\">{...}</NavGroup>\n * </SidebarContent>\n * <SidebarFooter>{user}</SidebarFooter>\n * </Sidebar>\n * <AppShellMain>\n * <AppShellHeader><SidebarTrigger /><Breadcrumbs /></AppShellHeader>\n * <AppShellBody>{children}</AppShellBody>\n * </AppShellMain>\n * </AppShell>\n */\nexport function AppShell({ children, ...rest }: AppShellProps) {\n return <SidebarProvider {...rest}>{children}</SidebarProvider>\n}\n\n/** Right-hand pane container. Alias for `SidebarInset`. */\nexport const AppShellMain = SidebarInset\n\n/** Top bar inside `<AppShellMain>`. Standardizes h-12 + border-b + flex row. */\nexport function AppShellHeader({\n className,\n children,\n ...rest\n}: ComponentProps<\"header\">) {\n return (\n <header\n className={cn(\n \"flex h-12 shrink-0 items-center gap-2 border-b px-3\",\n className,\n )}\n {...rest}\n >\n {children}\n </header>\n )\n}\n\n/** Scroll body inside `<AppShellMain>`. Sibling of `<AppShellHeader>`. */\nexport function AppShellBody({\n className,\n children,\n ...rest\n}: ComponentProps<\"div\">) {\n return (\n <div className={cn(\"flex-1 overflow-auto\", className)} {...rest}>\n {children}\n </div>\n )\n}\n","\"use client\"\n\nimport type { ComponentProps } from \"react\"\nimport { Sidebar } from \"~/ui/primitives/sidebar.js\"\nimport { cn } from \"~/utils/cn.js\"\n\n/**\n * Dual / stacked sidebar wrapper — an icon rail plus one or more content\n * panels rendered side-by-side inside a single `<Sidebar>`. This is the\n * shadcn \"sidebar-07\" pattern.\n *\n * The `*:data-[sidebar=sidebar]:flex-row` trick lays the inner\n * `[data-sidebar=sidebar]` wrappers of the nested {@link SidebarDock} /\n * {@link SidebarPanel} columns out horizontally. `variant`/`collapsible`\n * default to `\"sidebar\"`/`\"icon\"` but can be overridden.\n *\n * @example\n * import { AppShell, AppShellMain, StackedSidebar, SidebarDock, SidebarPanel } from \"@tradalab/lyra/shell\"\n * import { SidebarHeader, SidebarContent, SidebarMenu, SidebarMenuItem, SidebarMenuButton } from \"@tradalab/lyra/ui\"\n *\n * <AppShell>\n * <StackedSidebar>\n * <SidebarDock>\n * <SidebarHeader>{logo}</SidebarHeader>\n * <SidebarContent>{iconButtons}</SidebarContent>\n * </SidebarDock>\n * {tab === \"files\" ? <SidebarPanel>{tree}</SidebarPanel> : null}\n * </StackedSidebar>\n * <AppShellMain>{children}</AppShellMain>\n * </AppShell>\n */\nexport function StackedSidebar({\n className,\n ...rest\n}: ComponentProps<typeof Sidebar>) {\n return (\n <Sidebar\n variant=\"sidebar\"\n collapsible=\"icon\"\n {...rest}\n className={cn(\n \"overflow-hidden *:data-[sidebar=sidebar]:flex-row\",\n className,\n )}\n />\n )\n}\n\n/**\n * Narrow icon-rail column inside a {@link StackedSidebar}. Fixed to the icon\n * width, never collapses on its own (the parent `StackedSidebar` owns the\n * icon-mode toggle).\n *\n * Named `SidebarDock` — not `SidebarRail` — because `SidebarRail` already\n * exists in `@tradalab/lyra/ui` as shadcn's drag-to-resize edge handle.\n */\nexport function SidebarDock({\n className,\n ...rest\n}: ComponentProps<typeof Sidebar>) {\n return (\n <Sidebar\n {...rest}\n collapsible=\"none\"\n className={cn(\n \"w-[calc(var(--sidebar-width-icon)+1px)]! overflow-hidden border-r\",\n className,\n )}\n />\n )\n}\n\n/**\n * Wider content column inside a {@link StackedSidebar} (tree, list, …).\n * Fills remaining width; render conditionally to show/hide per app state.\n */\nexport function SidebarPanel({\n className,\n ...rest\n}: ComponentProps<typeof Sidebar>) {\n return (\n <Sidebar\n {...rest}\n collapsible=\"none\"\n className={cn(\"flex-1 overflow-hidden\", className)}\n />\n )\n}\n","\"use client\"\n\nimport type { ReactNode } from \"react\"\nimport {\n SidebarGroup,\n SidebarGroupContent,\n SidebarGroupLabel,\n SidebarMenu,\n} from \"~/ui/primitives/sidebar.js\"\n\nexport interface NavGroupProps {\n label?: ReactNode\n children: ReactNode\n}\n\nexport function NavGroup({ label, children }: NavGroupProps) {\n return (\n <SidebarGroup>\n {label != null ? <SidebarGroupLabel>{label}</SidebarGroupLabel> : null}\n <SidebarGroupContent>\n <SidebarMenu>{children}</SidebarMenu>\n </SidebarGroupContent>\n </SidebarGroup>\n )\n}\n","\"use client\"\n\nimport { usePathname } from \"next/navigation\"\n\nexport type MatchMode = \"exact\" | \"prefix\"\n\nexport interface UseActiveRoute {\n pathname: string\n isActive: (href: string, mode?: MatchMode) => boolean\n}\n\n/**\n * App-Router pathname helper. Default `mode` is `\"prefix\"`.\n *\n * Root (`/`) is always exact-matched regardless of `mode` — otherwise the\n * root nav item would light up on every nested page.\n */\nexport function useActiveRoute(): UseActiveRoute {\n const pathname = usePathname() ?? \"/\"\n return {\n pathname,\n isActive: (href, mode = \"prefix\") => {\n if (mode === \"exact\" || href === \"/\") return pathname === href\n return pathname === href || pathname.startsWith(`${href}/`)\n },\n }\n}\n","\"use client\"\n\nimport Link from \"next/link\"\nimport type { ReactNode } from \"react\"\nimport {\n SidebarMenuButton,\n SidebarMenuItem,\n} from \"~/ui/primitives/sidebar.js\"\nimport { useActiveRoute, type MatchMode } from \"./use-active-route.js\"\n\nexport type NavIcon = ReactNode\n\nexport interface NavItemProps {\n href: string\n label: string\n /** Pre-rendered icon element, e.g. `<Globe />`. Element form so the prop is serializable across the RSC boundary. */\n icon?: NavIcon\n /** Sidebar tooltip; visible in collapsed icon mode. */\n tooltip?: string\n /** Default `\"prefix\"` — matches `href` and any nested path. */\n matchMode?: MatchMode\n}\n\n/** Sidebar nav row. Must be rendered inside a {@link NavGroup}. */\nexport function NavItem({\n href,\n label,\n icon,\n tooltip,\n matchMode = \"prefix\",\n}: NavItemProps) {\n const { isActive } = useActiveRoute()\n return (\n <SidebarMenuItem>\n <SidebarMenuButton\n asChild\n isActive={isActive(href, matchMode)}\n tooltip={tooltip}\n >\n <Link href={href}>\n {icon}\n <span>{label}</span>\n </Link>\n </SidebarMenuButton>\n </SidebarMenuItem>\n )\n}\n","\"use client\"\n\nimport type { ReactNode } from \"react\"\nimport { NavGroup } from \"./nav-group.js\"\nimport { NavItem, type NavItemProps } from \"./nav-item.js\"\n\nexport interface NavSectionProps {\n /** Optional group heading. */\n label?: ReactNode\n /** Nav rows — each rendered as a {@link NavItem}. */\n items: NavItemProps[]\n}\n\n/**\n * Config-driven convenience over {@link NavGroup} + {@link NavItem}: pass a\n * data array instead of hand-mapping JSX. For dynamic or conditional rows,\n * compose `NavGroup`/`NavItem` directly.\n *\n * @example\n * const NAV = [\n * { href: \"/files\", label: \"Files\", icon: <Folder /> },\n * { href: \"/trash\", label: \"Trash\", icon: <Trash2 /> },\n * ]\n * <NavSection label=\"Workspace\" items={NAV} />\n */\nexport function NavSection({ label, items }: NavSectionProps) {\n return (\n <NavGroup label={label}>\n {items.map((item) => (\n <NavItem key={item.href} {...item} />\n ))}\n </NavGroup>\n )\n}\n","\"use client\"\n\nimport type { ReactNode } from \"react\"\nimport { ChevronsUpDown } from \"lucide-react\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"~/ui/primitives/dropdown-menu.js\"\nimport {\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n useSidebar,\n} from \"~/ui/primitives/sidebar.js\"\n\n/** An application shown in the {@link NavBrand} header / switcher. */\nexport interface NavBrandApp {\n /** Display name. */\n name: string\n /**\n * Pre-rendered icon/logo element, e.g. `<Boxes />`. Element form (not a\n * component reference) so the prop stays serializable across the RSC\n * boundary — same convention as {@link NavItem}'s `icon`.\n */\n icon: ReactNode\n /** Navigation target. Usually an absolute cross-app URL (full navigation). */\n href: string\n /** Optional secondary line under the name (tagline / domain). */\n description?: string\n}\n\nexport interface NavBrandProps {\n /** The current app, rendered in the sidebar header. */\n app: NavBrandApp\n /**\n * Other apps to switch to. When omitted or empty the header renders as a\n * plain link to `app.href` (no dropdown, no chevron).\n */\n apps?: NavBrandApp[]\n /** Heading above the app list in the dropdown. Default `\"Applications\"`. */\n appsLabel?: string\n /** Extra menu content appended below the app list (e.g. an \"Add app\" row). */\n children?: ReactNode\n}\n\n/** Default English heading for the switcher's app list — override for i18n. */\nexport const defaultNavBrandAppsLabel = \"Applications\"\n\n/** Icon tile + name/description shown inside the header button. */\nfunction BrandContent({ app }: { app: NavBrandApp }) {\n return (\n <>\n <div className=\"bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-none [&_svg]:size-4\">\n {app.icon}\n </div>\n <div className=\"grid flex-1 text-left text-sm leading-tight\">\n <span className=\"truncate font-semibold\">{app.name}</span>\n {app.description ? (\n <span className=\"text-muted-foreground truncate text-xs\">\n {app.description}\n </span>\n ) : null}\n </div>\n </>\n )\n}\n\n/**\n * Sidebar-header brand block with an optional app switcher — the standardized\n * `SidebarHeader` content for app shells (pairs with {@link NavUser} in the\n * footer). Presentational: the app owns the config, icons, and labels (same\n * split as {@link TabBar}).\n *\n * With `apps`, the header is a dropdown trigger (chevron) listing the other\n * apps as links; without it, the header is a plain link to `app.href`. Cross-app\n * links use a plain `<a>` so switching does a full navigation.\n *\n * Must render inside an `AppShell`/`SidebarProvider`, and below a\n * `TooltipProvider` (the collapsed-mode tooltip needs one — app root providers\n * normally mount it).\n *\n * @example\n * import { NavBrand } from \"@tradalab/lyra/shell\"\n * import { SidebarHeader } from \"@tradalab/lyra/ui\"\n * import { Boxes, HardDrive, ShieldCheck } from \"lucide-react\"\n *\n * <SidebarHeader>\n * <NavBrand\n * app={{ name: \"Admin\", description: \"Platform console\", icon: <ShieldCheck />, href: \"/\" }}\n * apps={[\n * { name: \"Drive\", icon: <HardDrive />, href: \"https://drive.tradalab.com\" },\n * { name: \"Toolbox\", icon: <Boxes />, href: \"https://toolbox.tradalab.com\" },\n * ]}\n * />\n * </SidebarHeader>\n */\nexport function NavBrand({\n app,\n apps,\n appsLabel = defaultNavBrandAppsLabel,\n children,\n}: NavBrandProps) {\n const { isMobile } = useSidebar()\n const hasSwitcher = (apps?.length ?? 0) > 0 || children != null\n\n return (\n <SidebarMenu>\n <SidebarMenuItem>\n {hasSwitcher ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <SidebarMenuButton\n size=\"lg\"\n tooltip={app.name}\n className=\"data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground\"\n >\n <BrandContent app={app} />\n <ChevronsUpDown className=\"ml-auto size-4\" />\n </SidebarMenuButton>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n className=\"w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-none\"\n align=\"start\"\n side={isMobile ? \"bottom\" : \"right\"}\n sideOffset={4}\n >\n <DropdownMenuLabel className=\"text-muted-foreground text-xs\">\n {appsLabel}\n </DropdownMenuLabel>\n {apps?.map((item) => (\n <DropdownMenuItem key={item.href} asChild className=\"gap-2 p-2\">\n <a href={item.href}>\n <div className=\"flex size-6 items-center justify-center rounded-none border [&_svg]:size-3.5\">\n {item.icon}\n </div>\n <span className=\"truncate\">{item.name}</span>\n </a>\n </DropdownMenuItem>\n ))}\n {children ? (\n <>\n <DropdownMenuSeparator />\n {children}\n </>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n ) : (\n <SidebarMenuButton size=\"lg\" asChild tooltip={app.name}>\n <a href={app.href}>\n <BrandContent app={app} />\n </a>\n </SidebarMenuButton>\n )}\n </SidebarMenuItem>\n </SidebarMenu>\n )\n}\n","\"use client\"\n\nimport type { ReactNode } from \"react\"\nimport {\n ChevronsUpDown,\n CircleUser,\n LogIn,\n LogOut,\n Monitor,\n Moon,\n Palette,\n Sun,\n UserRoundCog,\n} from \"lucide-react\"\nimport { useTheme } from \"next-themes\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"~/ui/primitives/avatar.js\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from \"~/ui/primitives/dropdown-menu.js\"\nimport {\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n useSidebar,\n} from \"~/ui/primitives/sidebar.js\"\nimport {\n defaultAccountMenuLabels,\n initialsOf,\n type AccountMenuLabels,\n type AccountMenuUser,\n} from \"~/ui/account-menu.js\"\n\nexport interface NavUserThemeLabels {\n theme: string\n light: string\n dark: string\n system: string\n}\n\n/** Default English theme-submenu labels — spread + override for i18n. */\nexport const defaultNavUserThemeLabels: NavUserThemeLabels = {\n theme: \"Theme\",\n light: \"Light\",\n dark: \"Dark\",\n system: \"System\",\n}\n\nexport interface NavUserProps {\n /**\n * Signed-in user. When `null`/`undefined`, renders a guest menu (icon +\n * theme + Sign in) if `onSignIn` is set, otherwise renders nothing.\n */\n user?: AccountMenuUser | null\n /** Account-action labels — reuses lyra's {@link AccountMenuLabels}. */\n labels?: Partial<AccountMenuLabels>\n /** Switch-account handler (signed-in only). Item hidden if omitted. */\n onSwitchAccount?: () => void\n /** Sign-out handler (signed-in only). Item hidden if omitted. */\n onSignOut?: () => void\n /**\n * Sign-in handler (guest only). When set, a null `user` renders the guest\n * menu with a Sign in action instead of rendering nothing.\n */\n onSignIn?: () => void\n /** Render the light/dark/system theme submenu. Default `true`. */\n showTheme?: boolean\n /** Theme-submenu labels. */\n themeLabels?: Partial<NavUserThemeLabels>\n /** Extra menu content inserted below the theme submenu. */\n children?: ReactNode\n}\n\nconst THEMES = [\n { value: \"light\", key: \"light\", icon: Sun },\n { value: \"dark\", key: \"dark\", icon: Moon },\n { value: \"system\", key: \"system\", icon: Monitor },\n] as const\n\n/**\n * Sidebar-footer account menu — the standardized NavUser for app shells.\n * Avatar trigger (size `lg`) + name/email opening a dropdown with an optional\n * theme switcher and switch-account / sign-out actions. Auth flows stay in the\n * app (callbacks); labels are props for i18n.\n *\n * Differs from {@link AccountMenu} in `@tradalab/lyra/ui`: that one is a\n * generic header dropdown; this is styled for the collapsible sidebar footer\n * and follows `useSidebar()` for mobile placement.\n *\n * Must render inside an `AppShell`/SidebarProvider, and below a\n * `TooltipProvider` (the avatar button's collapsed-mode tooltip requires one —\n * app root providers normally mount it). The theme submenu needs a\n * `next-themes` provider mounted (it no-ops without one); pass\n * `showTheme={false}` for apps with a fixed theme.\n *\n * @example\n * import { NavUser } from \"@tradalab/lyra/shell\"\n * import { SidebarFooter } from \"@tradalab/lyra/ui\"\n *\n * <SidebarFooter>\n * <NavUser\n * user={session.user}\n * onSwitchAccount={() => signIn(\"zitadel\", { redirectTo: \"/\" }, { prompt: \"select_account\" })}\n * onSignOut={() => signOut({ redirectTo: \"/\" })}\n * />\n * </SidebarFooter>\n */\nexport function NavUser({\n user,\n labels,\n onSwitchAccount,\n onSignOut,\n onSignIn,\n showTheme = true,\n themeLabels,\n children,\n}: NavUserProps) {\n const { isMobile } = useSidebar()\n const { theme, setTheme } = useTheme()\n const l = { ...defaultAccountMenuLabels, ...labels }\n const t = { ...defaultNavUserThemeLabels, ...themeLabels }\n\n // Guests render nothing unless a sign-in handler opts them into a guest menu.\n if (!user && !onSignIn) return null\n\n const name = user ? (user.name ?? user.email ?? l.account) : l.guest\n const email = user?.email ?? \"\"\n\n const avatar = (\n <Avatar className=\"size-8 rounded-lg\">\n {user?.image ? <AvatarImage src={user.image} alt={name} /> : null}\n <AvatarFallback className=\"rounded-lg\">\n {user ? initialsOf(name) : <CircleUser className=\"size-4\" />}\n </AvatarFallback>\n </Avatar>\n )\n\n const themeSubmenu = showTheme ? (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuSub>\n <DropdownMenuSubTrigger>\n <Palette className=\"mr-2 size-4\" />\n {t.theme}\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent>\n <DropdownMenuRadioGroup value={theme ?? \"system\"} onValueChange={setTheme}>\n {THEMES.map(({ value, key, icon: Icon }) => (\n <DropdownMenuRadioItem key={value} value={value}>\n <Icon className=\"mr-2 size-4\" />\n {t[key]}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n </>\n ) : null\n\n return (\n <SidebarMenu>\n <SidebarMenuItem>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <SidebarMenuButton\n size=\"lg\"\n tooltip={name}\n className=\"data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground\"\n >\n {avatar}\n <div className=\"grid flex-1 text-left text-sm leading-tight\">\n <span className=\"truncate font-medium\">{name}</span>\n {email ? (\n <span className=\"text-muted-foreground truncate text-xs\">\n {email}\n </span>\n ) : null}\n </div>\n <ChevronsUpDown className=\"ml-auto size-4\" />\n </SidebarMenuButton>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n className=\"w-(--radix-dropdown-menu-trigger-width) min-w-56\"\n side={isMobile ? \"bottom\" : \"right\"}\n align=\"end\"\n sideOffset={4}\n >\n <DropdownMenuLabel className=\"flex items-center gap-2\">\n {avatar}\n <div className=\"grid flex-1 text-left leading-tight\">\n <span className=\"truncate text-sm font-medium\">{name}</span>\n {email ? (\n <span className=\"text-muted-foreground truncate text-xs font-normal\">\n {email}\n </span>\n ) : null}\n </div>\n </DropdownMenuLabel>\n\n {themeSubmenu}\n\n {children ? (\n <>\n <DropdownMenuSeparator />\n {children}\n </>\n ) : null}\n\n {user ? (\n <>\n {onSwitchAccount || onSignOut ? <DropdownMenuSeparator /> : null}\n {onSwitchAccount ? (\n <DropdownMenuItem onSelect={onSwitchAccount}>\n <UserRoundCog className=\"mr-2 size-4\" />\n {l.switchAccount}\n </DropdownMenuItem>\n ) : null}\n {onSignOut ? (\n <DropdownMenuItem variant=\"destructive\" onSelect={onSignOut}>\n <LogOut className=\"mr-2 size-4\" />\n {l.signOut}\n </DropdownMenuItem>\n ) : null}\n </>\n ) : (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem onSelect={onSignIn}>\n <LogIn className=\"mr-2 size-4\" />\n {l.signIn}\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </SidebarMenuItem>\n </SidebarMenu>\n )\n}\n","\"use client\"\n\nimport { useEffect, useRef, type ReactNode, type WheelEvent } from \"react\"\nimport { ChevronDown, ListX, Pin, PinOff, Trash2, X } from \"lucide-react\"\nimport { Button } from \"~/ui/primitives/button.js\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"~/ui/primitives/dropdown-menu.js\"\nimport { ScrollArea, ScrollBar } from \"~/ui/primitives/scroll-area.js\"\nimport {\n ContextMenu,\n ContextMenuContent,\n ContextMenuItem,\n ContextMenuSeparator,\n ContextMenuTrigger,\n} from \"~/ui/primitives/context-menu.js\"\nimport { cn } from \"~/utils/cn.js\"\n\nexport interface TabItem {\n id: string\n title: string\n /** Secondary line under the title (e.g. connection/db, file path). */\n subtitle?: ReactNode\n /** Leading icon element — the app maps its own tab type → icon. */\n icon?: ReactNode\n pinned?: boolean\n}\n\nexport interface TabBarLabels {\n close: string\n pinTab: string\n unpinTab: string\n closeOthers: string\n closeAll: string\n}\n\n/** Default English labels — spread + override for i18n. */\nexport const defaultTabBarLabels: TabBarLabels = {\n close: \"Close\",\n pinTab: \"Pin tab\",\n unpinTab: \"Unpin tab\",\n closeOthers: \"Close others\",\n closeAll: \"Close all\",\n}\n\nexport interface TabBarProps {\n tabs: TabItem[]\n activeId: string | null\n onSelect: (id: string) => void\n onClose: (id: string) => void\n /** Pin row in the context menu is hidden when omitted. */\n onTogglePin?: (id: string) => void\n /** \"Close others\" row is hidden when omitted. */\n onCloseOthers?: (id: string) => void\n /** \"Close all\" row is hidden when omitted. */\n onCloseAll?: () => void\n labels?: Partial<TabBarLabels>\n}\n\n/**\n * Horizontal tab strip for the content area of stacked-sidebar apps\n * (redishub, loom). Presentational + store-agnostic: the app owns tab state\n * and passes `tabs` + callbacks; `icon`/`subtitle` are per-tab so apps map\n * their own tab types, and labels are props for i18n. Renders nothing when\n * `tabs` is empty.\n *\n * Sizes rows from the `--tab-height` CSS var — set it in your globals.\n *\n * @example\n * const s = useTabStore()\n * <TabBar\n * tabs={s.tabs.map(t => ({ id: t.id, title: t.title, pinned: t.pinned, icon: <Icon t={t.type}/>, subtitle: fmt(t) }))}\n * activeId={s.activeTabId}\n * onSelect={s.setActiveTabId}\n * onClose={s.removeTab}\n * onTogglePin={s.togglePin}\n * onCloseOthers={s.closeOthers}\n * onCloseAll={s.closeAll}\n * />\n */\nexport function TabBar({\n tabs,\n activeId,\n onSelect,\n onClose,\n onTogglePin,\n onCloseOthers,\n onCloseAll,\n labels,\n}: TabBarProps) {\n const l = { ...defaultTabBarLabels, ...labels }\n const activeTabRef = useRef<HTMLDivElement>(null)\n\n // Scroll the active tab into view when it changes.\n useEffect(() => {\n if (activeId && activeTabRef.current) {\n activeTabRef.current.scrollIntoView({\n behavior: \"smooth\",\n block: \"nearest\",\n inline: \"nearest\",\n })\n }\n }, [activeId])\n\n // Map vertical wheel scroll to horizontal scroll over the tab strip.\n const handleWheel = (e: WheelEvent<HTMLDivElement>) => {\n if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) {\n const viewport = e.currentTarget.querySelector(\n '[data-slot=\"scroll-area-viewport\"]',\n )\n if (viewport) viewport.scrollLeft += e.deltaY * 0.5\n }\n }\n\n if (tabs.length === 0) return null\n\n const hasContextActions = Boolean(onTogglePin || onCloseOthers || onCloseAll)\n\n return (\n <div\n className=\"group/tabbar flex w-full shrink-0 items-center overflow-hidden border-b bg-muted/30\"\n style={{ height: \"var(--tab-height)\" }}\n >\n <ScrollArea\n type=\"hover\"\n className=\"min-w-0 flex-1 overflow-hidden [&_[data-orientation=vertical]]:hidden\"\n onWheel={handleWheel}\n >\n <div\n className=\"flex w-max items-center gap-0\"\n style={{ height: \"var(--tab-height)\" }}\n >\n {tabs.map((tab) => {\n const isActive = activeId === tab.id\n return (\n <ContextMenu key={tab.id}>\n <ContextMenuTrigger asChild>\n <div\n ref={isActive ? activeTabRef : null}\n onClick={() => onSelect(tab.id)}\n title={tab.title}\n className={cn(\n \"group relative flex min-w-[140px] max-w-[240px] shrink-0 cursor-pointer select-none items-center gap-2 border-r px-3 transition-colors\",\n isActive\n ? \"border-t-2 border-t-primary bg-background\"\n : \"border-t-2 border-t-transparent bg-transparent text-muted-foreground hover:bg-muted/50\",\n tab.pinned && \"min-w-[100px] bg-primary/5\",\n )}\n style={{ height: \"var(--tab-height)\" }}\n >\n {tab.icon ? (\n <div className=\"relative shrink-0\">\n <span\n className={cn(\n \"flex [&_svg]:size-3.5\",\n isActive && \"text-primary\",\n )}\n >\n {tab.icon}\n </span>\n {tab.pinned ? (\n <div className=\"absolute -right-1.5 -top-1.5 rounded-full bg-background p-0.5 shadow-sm\">\n <Pin className=\"size-2 fill-primary text-primary\" />\n </div>\n ) : null}\n </div>\n ) : null}\n <div className=\"flex min-w-0 flex-1 flex-col items-start justify-center overflow-hidden py-1\">\n <span\n className={cn(\n \"w-full truncate text-xs leading-tight\",\n isActive && \"font-medium text-foreground\",\n )}\n >\n {tab.title}\n </span>\n {tab.subtitle ? (\n <span className=\"mt-0.5 w-full truncate text-[10px] leading-none text-muted-foreground/70\">\n {tab.subtitle}\n </span>\n ) : null}\n </div>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n title={l.close}\n className={cn(\n \"size-5 shrink-0 rounded-md opacity-0 transition-opacity hover:bg-muted-foreground/20 group-hover:opacity-100\",\n isActive && \"opacity-100\",\n )}\n onClick={(e) => {\n e.stopPropagation()\n onClose(tab.id)\n }}\n >\n <X className=\"size-3\" />\n </Button>\n {isActive ? (\n <div className=\"absolute bottom-0 left-0 right-0 z-10 h-[2px] bg-background\" />\n ) : null}\n </div>\n </ContextMenuTrigger>\n {hasContextActions ? (\n <ContextMenuContent className=\"w-48\">\n {onTogglePin ? (\n <>\n <ContextMenuItem onClick={() => onTogglePin(tab.id)}>\n {tab.pinned ? (\n <>\n <PinOff className=\"mr-2 size-4\" />\n <span>{l.unpinTab}</span>\n </>\n ) : (\n <>\n <Pin className=\"mr-2 size-4\" />\n <span>{l.pinTab}</span>\n </>\n )}\n </ContextMenuItem>\n <ContextMenuSeparator />\n </>\n ) : null}\n <ContextMenuItem onClick={() => onClose(tab.id)}>\n <X className=\"mr-2 size-4\" />\n <span>{l.close}</span>\n </ContextMenuItem>\n {onCloseOthers ? (\n <ContextMenuItem onClick={() => onCloseOthers(tab.id)}>\n <ListX className=\"mr-2 size-4\" />\n <span>{l.closeOthers}</span>\n </ContextMenuItem>\n ) : null}\n {onCloseAll ? (\n <ContextMenuItem onClick={() => onCloseAll()}>\n <Trash2 className=\"mr-2 size-4\" />\n <span>{l.closeAll}</span>\n </ContextMenuItem>\n ) : null}\n </ContextMenuContent>\n ) : null}\n </ContextMenu>\n )\n })}\n </div>\n <ScrollBar orientation=\"horizontal\" className=\"z-20 h-1.5 hover:h-2\" />\n </ScrollArea>\n\n <div\n className=\"flex shrink-0 items-center border-l bg-muted/30 px-1\"\n style={{ height: \"var(--tab-height)\" }}\n >\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" className=\"size-8 shrink-0 rounded-sm\">\n <ChevronDown className=\"size-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n align=\"end\"\n className=\"max-h-[400px] w-[260px] overflow-y-auto\"\n >\n {tabs.map((tab) => (\n <DropdownMenuItem\n key={tab.id}\n onClick={() => onSelect(tab.id)}\n title={tab.title}\n className={cn(\n \"cursor-pointer gap-2 py-2\",\n activeId === tab.id && \"bg-accent\",\n )}\n >\n {tab.icon ? (\n <div className=\"relative shrink-0\">\n <span className=\"flex text-muted-foreground [&_svg]:size-4\">\n {tab.icon}\n </span>\n {tab.pinned ? (\n <Pin className=\"absolute -right-1 -top-1 size-2 fill-primary text-primary\" />\n ) : null}\n </div>\n ) : null}\n <div className=\"flex flex-1 flex-col items-start overflow-hidden leading-tight\">\n <span className=\"w-full flex-1 truncate text-sm\">{tab.title}</span>\n {tab.subtitle ? (\n <span className=\"w-full truncate text-[10px] text-muted-foreground\">\n {tab.subtitle}\n </span>\n ) : null}\n </div>\n <X\n className=\"size-4 shrink-0 rounded-sm p-0.5 hover:bg-red-50 hover:text-red-500\"\n onClick={(e) => {\n e.stopPropagation()\n onClose(tab.id)\n }}\n />\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n )\n}\n","\"use client\"\n\nimport { Fragment, type ComponentProps, type ReactNode } from \"react\"\nimport Link from \"next/link\"\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from \"~/ui/primitives/breadcrumb.js\"\nimport { useActiveRoute } from \"./use-active-route.js\"\n\nexport interface BreadcrumbsProps extends ComponentProps<typeof Breadcrumb> {\n /** Separator content. Falls back to shadcn's chevron when omitted. */\n separator?: ReactNode\n /** Segment-id → display-label map. Unmapped segments render raw. */\n labels?: Record<string, string>\n}\n\n/** Pathname-derived breadcrumbs. Renders nothing on the root route. */\nexport function Breadcrumbs({ separator, labels, ...rest }: BreadcrumbsProps) {\n const { pathname } = useActiveRoute()\n const segments = pathname.split(\"/\").filter(Boolean)\n if (segments.length === 0) return null\n const last = segments.length - 1\n return (\n <Breadcrumb {...rest}>\n <BreadcrumbList>\n {segments.map((seg, i) => {\n const href = \"/\" + segments.slice(0, i + 1).join(\"/\")\n const label = labels?.[seg] ?? seg\n return (\n <Fragment key={href}>\n {i > 0 ? (\n <BreadcrumbSeparator>{separator}</BreadcrumbSeparator>\n ) : null}\n <BreadcrumbItem>\n {i === last ? (\n <BreadcrumbPage>{label}</BreadcrumbPage>\n ) : (\n <BreadcrumbLink asChild>\n <Link href={href}>{label}</Link>\n </BreadcrumbLink>\n )}\n </BreadcrumbItem>\n </Fragment>\n )\n })}\n </BreadcrumbList>\n </Breadcrumb>\n )\n}\n","import type { ReactNode } from \"react\"\nimport { Search } from \"lucide-react\"\n\nimport { cn } from \"~/utils/cn.js\"\nimport {\n Card,\n CardDescription,\n CardTitle,\n} from \"~/ui/primitives/card.js\"\nimport {\n InputGroup,\n InputGroupAddon,\n InputGroupInput,\n} from \"~/ui/primitives/input-group.js\"\n\n/** One application in the {@link AppGrid} — rendered as an {@link AppTile}. */\nexport interface AppGridItem {\n /** Stable React list key. */\n key: string\n /** Display name. */\n name: string\n /** Optional tagline shown under the name. */\n description?: string\n /** Navigation target (usually an absolute cross-app URL). */\n href: string\n /**\n * Pre-rendered icon element (RSC-serializable), e.g. `<ToolCase />`. Sized by\n * the tile (`[&_svg]:size-7`) — same convention as {@link NavBrandApp}'s icon.\n */\n icon: ReactNode\n /**\n * Tailwind class string tinting the icon tile — `text-…` (colors the icon via\n * currentColor) + `bg-…` (tile background). Pass full class strings, not\n * interpolated fragments, so the JIT keeps them.\n */\n accent?: string\n /** Opens on a different origin (adds `target=\"_blank\"` + `rel`). */\n external?: boolean\n}\n\n/** A titled section of apps for {@link AppGrid}'s grouped mode. */\nexport interface AppGridGroup {\n /** Stable React list key. */\n key: string\n /** Section heading. */\n label: string\n /** Optional sub-heading under the label. */\n description?: string\n /** Apps in this group — already filtered by the caller. */\n apps: AppGridItem[]\n}\n\nexport interface AppGridProps {\n /** Flat mode: apps to render (already filtered). Ignored when `groups` is set. */\n apps?: AppGridItem[]\n /**\n * Grouped mode: titled sections (already filtered). When provided, renders a\n * heading + tile grid per group; empty groups are dropped.\n */\n groups?: AppGridGroup[]\n /** Controlled search value. Pass together with `onQueryChange` to show the search box. */\n query?: string\n onQueryChange?: (query: string) => void\n /** Search input placeholder. */\n searchPlaceholder?: string\n /** Autofocus the search box on mount. Default `false`. */\n autoFocusSearch?: boolean\n /** Message shown when there are no apps. */\n emptyLabel?: string\n className?: string\n}\n\n/** Default English labels — override for i18n. */\nexport const defaultAppGridLabels = {\n searchPlaceholder: \"Search apps…\",\n empty: \"No apps found.\",\n}\n\n/**\n * A responsive grid of application tiles with an optional search box — the \"app\n * grid\" / launcher surface (a portal home / \"all apps\" page). Presentational:\n * the app owns the registry, i18n, icons, and the filter — pass the already-\n * filtered `apps` plus a controlled `query`. Same app/lyra split as\n * {@link NavBrand} and {@link TabBar}.\n *\n * @example\n * const [q, setQ] = useState(\"\")\n * const filtered = APPS.filter(a => match(a, q))\n * <AppGrid\n * apps={filtered.map(a => ({\n * key: a.slug, name: t(a.slug), description: t(`${a.slug}.desc`),\n * href: a.href, icon: <a.Icon />, accent: a.accent, external: true,\n * }))}\n * query={q} onQueryChange={setQ}\n * searchPlaceholder={t(\"search\")} emptyLabel={t(\"empty\")}\n * />\n */\nexport function AppGrid({\n apps,\n groups,\n query,\n onQueryChange,\n searchPlaceholder = defaultAppGridLabels.searchPlaceholder,\n autoFocusSearch = false,\n emptyLabel = defaultAppGridLabels.empty,\n className,\n}: AppGridProps) {\n const grouped = groups != null\n const sections = grouped ? groups.filter((g) => g.apps.length > 0) : []\n const flat = apps ?? []\n const isEmpty = grouped ? sections.length === 0 : flat.length === 0\n\n return (\n <div className={cn(\"space-y-8\", className)}>\n {onQueryChange ? (\n <InputGroup className=\"mx-auto h-10 max-w-md\">\n <InputGroupAddon>\n <Search />\n </InputGroupAddon>\n <InputGroupInput\n type=\"search\"\n value={query ?? \"\"}\n onChange={(e) => onQueryChange(e.target.value)}\n placeholder={searchPlaceholder}\n aria-label={searchPlaceholder}\n autoFocus={autoFocusSearch}\n />\n </InputGroup>\n ) : null}\n\n {isEmpty ? (\n <p className=\"text-muted-foreground py-16 text-center text-sm\">\n {emptyLabel}\n </p>\n ) : grouped ? (\n <div className=\"space-y-10\">\n {sections.map((g) => (\n <section key={g.key} className=\"space-y-4\">\n <div className=\"space-y-1\">\n <h2 className=\"text-sm font-semibold tracking-tight\">{g.label}</h2>\n {g.description ? (\n <p className=\"text-muted-foreground text-xs\">{g.description}</p>\n ) : null}\n </div>\n <AppTileGrid apps={g.apps} />\n </section>\n ))}\n </div>\n ) : (\n <AppTileGrid apps={flat} />\n )}\n </div>\n )\n}\n\n/** The responsive tile grid shared by {@link AppGrid}'s flat and grouped modes. */\nfunction AppTileGrid({ apps }: { apps: AppGridItem[] }) {\n return (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4\">\n {apps.map((app) => (\n <AppTile key={app.key} app={app} />\n ))}\n </div>\n )\n}\n\n/**\n * A single application tile: the icon in a tinted rounded chip above the name +\n * description. Exported for callers that render their own grid.\n */\nexport function AppTile({ app }: { app: AppGridItem }) {\n return (\n <a\n href={app.href}\n target={app.external ? \"_blank\" : undefined}\n rel={app.external ? \"noreferrer\" : undefined}\n className=\"focus-visible:ring-ring group rounded-none outline-none focus-visible:ring-2\"\n >\n <Card className=\"h-full items-center gap-4 px-6 py-8 text-center transition-all duration-200 group-hover:-translate-y-0.5 group-hover:shadow-md group-hover:ring-foreground/30\">\n {/* Tinted icon tile (sharp — matches lyra's rounded-none aesthetic);\n `accent` carries the text-… (icon, via currentColor) + bg-… tint. */}\n <div\n className={cn(\n \"flex size-14 items-center justify-center rounded-none ring-1 ring-foreground/5 ring-inset transition-transform duration-200 group-hover:scale-105 [&_svg]:size-7\",\n app.accent,\n )}\n >\n {app.icon}\n </div>\n <div className=\"space-y-1\">\n <CardTitle>{app.name}</CardTitle>\n {app.description ? (\n <CardDescription className=\"line-clamp-2\">\n {app.description}\n </CardDescription>\n ) : null}\n </div>\n </Card>\n </a>\n )\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradalab/lyra",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "Tradalab UI kit — shadcn (radix-lyra) primitives, composed components, and app shell for React 19 / Next 16 / Tailwind v4.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,