@columenlabs/lumen-ui 0.1.170 → 0.1.172
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/nav.d.ts +7 -0
- package/dist/nav.js +18 -1
- package/dist/notifications.d.ts +6 -1
- package/dist/notifications.js +17 -5
- package/dist/shell.d.ts +4 -1
- package/dist/shell.js +3 -2
- package/dist/status-bar.js +1 -1
- package/dist/theme.css +8 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -45,7 +45,7 @@ export { RenameField, type RenameFieldProps } from './rename-field.js';
|
|
|
45
45
|
export { LumenStackedList, type LumenStackedListProps, LumenStackedRow, type LumenStackedRowProps, type StackedColumn, } from './lumen-stacked-list.js';
|
|
46
46
|
export { ResetButton, type ResetButtonProps } from './reset-button.js';
|
|
47
47
|
export { type ChartBand, type ChartMarker, GrowthChart, KEY_DATE_KINDS, KeyDateChips, type KeyDateKind, type KeyDateMarker, PALETTE, type Scale, type Series, benchColour, keyDateMarkers, paletteAt, } from './growth-chart.js';
|
|
48
|
-
export { FoldGroup, NavGroup, type NavItem, type SectionItem, type SectionSubItem, SidebarSection, } from './nav.js';
|
|
48
|
+
export { FoldGroup, LumenTabBar, NavGroup, type NavItem, type SectionItem, type SectionSubItem, SidebarSection, } from './nav.js';
|
|
49
49
|
export { type OverviewPageProps, type OverviewStage, type OverviewTone, type OverviewWidget, OverviewList, OverviewPage, ToneDot, } from './overview.js';
|
|
50
50
|
export { type Crumb, IconButton, type IconButtonProps, Page, PageBody, PageHeader, type PageHeaderProps, } from './page.js';
|
|
51
51
|
export { DEFAULT_PREFS, deviceTimeZone, type Prefs, TIME_ZONES, type Theme, type TimeZoneId, getPrefs, resolvedTheme, setPref, timeZoneLabel, usePrefs, zoneAbbr, ASSISTANT_WIDTH, } from './prefs.js';
|
package/dist/index.js
CHANGED
|
@@ -52,7 +52,7 @@ export { RenameField } from './rename-field.js';
|
|
|
52
52
|
export { LumenStackedList, LumenStackedRow, } from './lumen-stacked-list.js';
|
|
53
53
|
export { ResetButton } from './reset-button.js';
|
|
54
54
|
export { GrowthChart, KEY_DATE_KINDS, KeyDateChips, PALETTE, benchColour, keyDateMarkers, paletteAt, } from './growth-chart.js';
|
|
55
|
-
export { FoldGroup, NavGroup, SidebarSection, } from './nav.js';
|
|
55
|
+
export { FoldGroup, LumenTabBar, NavGroup, SidebarSection, } from './nav.js';
|
|
56
56
|
export { OverviewList, OverviewPage, ToneDot, } from './overview.js';
|
|
57
57
|
export { IconButton, Page, PageBody, PageHeader, } from './page.js';
|
|
58
58
|
export { DEFAULT_PREFS, deviceTimeZone, TIME_ZONES, getPrefs, resolvedTheme, setPref, timeZoneLabel, usePrefs, zoneAbbr, ASSISTANT_WIDTH, } from './prefs.js';
|
package/dist/nav.d.ts
CHANGED
|
@@ -79,3 +79,10 @@ export declare function SidebarSection({ label, items, onAdd, addLabel, onRemove
|
|
|
79
79
|
/** What its nested items stand for; plain pages by default. */
|
|
80
80
|
assistantSubType?: AssistantPageType;
|
|
81
81
|
}): import("react").JSX.Element;
|
|
82
|
+
/**
|
|
83
|
+
* The phone's bottom tab bar (`LumenShell tabs`): up to four of the app's pages, then More, which
|
|
84
|
+
* opens the sidebar with every page. Phone only; the desktop keeps the sidebar.
|
|
85
|
+
*/
|
|
86
|
+
export declare function LumenTabBar({ items }: {
|
|
87
|
+
items: NavItem[];
|
|
88
|
+
}): import("react").JSX.Element | null;
|
package/dist/nav.js
CHANGED
|
@@ -5,7 +5,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
5
5
|
// A section header folds its items: it lights up on hover with a chevron after
|
|
6
6
|
// the name, a click folds it, the fold is remembered in the browser, and `+` never folds.
|
|
7
7
|
import { Link, useRouterState } from '@tanstack/react-router';
|
|
8
|
-
import { ChevronRightIcon, PlusIcon, Trash2Icon } from 'lucide-react';
|
|
8
|
+
import { ChevronRightIcon, MenuIcon, PlusIcon, Trash2Icon } from 'lucide-react';
|
|
9
9
|
import { useAssistantPages } from './assistant-screen.js';
|
|
10
10
|
import { LumenConfirm } from './confirm.js';
|
|
11
11
|
import { useFolded } from './fold.js';
|
|
@@ -112,3 +112,20 @@ export function SidebarSection({ label, items, onAdd, addLabel = `Add to ${label
|
|
|
112
112
|
? [0, 1].map((i) => (_jsx(SidebarMenuItem, { children: _jsx(SidebarMenuSkeleton, { showIcon: true }) }, i)))
|
|
113
113
|
: items.map((item) => (_jsx(SectionRow, { item: item, activeId: activeId, onRemove: onRemove, removeLabel: removeLabel }, item.id))), !loading && !items.length && empty && (_jsx("li", { className: "px-2 py-1 text-xs text-muted-foreground group-data-[collapsible=icon]:hidden", children: empty }))] }), children && _jsx("div", { className: "group-data-[collapsible=icon]:hidden", children: children })] }) }));
|
|
114
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* The phone's bottom tab bar (`LumenShell tabs`): up to four of the app's pages, then More, which
|
|
117
|
+
* opens the sidebar with every page. Phone only; the desktop keeps the sidebar.
|
|
118
|
+
*/
|
|
119
|
+
export function LumenTabBar({ items }) {
|
|
120
|
+
const { isMobile, openMobile, setOpenMobile } = useSidebar();
|
|
121
|
+
const match = useMatch();
|
|
122
|
+
if (!isMobile || !items.length)
|
|
123
|
+
return null;
|
|
124
|
+
const tabs = items.slice(0, 4);
|
|
125
|
+
const scores = tabs.map((t) => match(t));
|
|
126
|
+
const best = Math.max(...scores);
|
|
127
|
+
return (_jsxs("nav", { "data-slot": "shell-tabbar", "aria-label": "Main", className: "sticky bottom-0 z-20 grid h-[calc(var(--shell-tabbar-height)+env(safe-area-inset-bottom))] shrink-0 grid-cols-5 border-t bg-background pb-[env(safe-area-inset-bottom)]", children: [tabs.map((t, i) => {
|
|
128
|
+
const on = best >= 0 && scores[i] === best;
|
|
129
|
+
return (_jsxs(Link, { to: t.to, "aria-current": on ? 'page' : undefined, className: cn('flex min-w-0 flex-col items-center justify-center gap-0.5 text-xs text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none', on && 'font-semibold text-primary'), children: [_jsx(t.icon, { className: "size-5" }), _jsx("span", { className: "max-w-full truncate px-1", children: t.title })] }, t.to));
|
|
130
|
+
}), _jsxs("button", { type: "button", "aria-expanded": openMobile, onClick: () => setOpenMobile(true), className: cn('flex min-w-0 flex-col items-center justify-center gap-0.5 text-xs text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none', best < 0 && 'font-semibold text-primary'), children: [_jsx(MenuIcon, { className: "size-5" }), _jsx("span", { children: "More" })] })] }));
|
|
131
|
+
}
|
package/dist/notifications.d.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/** The Settings ▸ Notifications section's body. */
|
|
2
|
-
export declare function NotificationsSettings({ api: base, product, sender, }: {
|
|
2
|
+
export declare function NotificationsSettings({ api: base, product, sender, parts, }: {
|
|
3
3
|
/** The app's Notifications API base, e.g. `/api/notifications`. */
|
|
4
4
|
api: string;
|
|
5
5
|
/** The product's name in the copy ("Get Acme alerts by email"). */
|
|
6
6
|
product: string;
|
|
7
7
|
/** The address email comes from. */
|
|
8
8
|
sender: string;
|
|
9
|
+
/**
|
|
10
|
+
* What to draw: the channels and the events grid (default), only the channels (where messages
|
|
11
|
+
* go) or only the events grid (what goes where), so an app can set them in two sections.
|
|
12
|
+
*/
|
|
13
|
+
parts?: 'all' | 'channels' | 'events';
|
|
9
14
|
}): import("react").JSX.Element;
|
package/dist/notifications.js
CHANGED
|
@@ -147,12 +147,22 @@ function Events({ status, api }) {
|
|
|
147
147
|
...status.webhooks.map((h) => ({ id: h.id, name: h.name })),
|
|
148
148
|
];
|
|
149
149
|
if (!targets.length)
|
|
150
|
-
return (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nothing to send to yet: turn on email or ntfy, or add a webhook,
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
return (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nothing to send to yet: turn on email or ntfy, or add a webhook, first." }));
|
|
151
|
+
// The event column gives way first, so a phone shows the checkboxes; its detail waits in the hover.
|
|
152
|
+
const grid = { gridTemplateColumns: `minmax(7rem, 1fr) repeat(${targets.length}, 5.5rem)` };
|
|
153
|
+
// Groups in the order they first appear; events without one come first, unlabelled.
|
|
154
|
+
const groups = [];
|
|
155
|
+
for (const e of status.events) {
|
|
156
|
+
const g = groups.find((x) => x.label === e.group);
|
|
157
|
+
if (g)
|
|
158
|
+
g.events.push(e);
|
|
159
|
+
else
|
|
160
|
+
groups.push({ label: e.group, events: [e] });
|
|
161
|
+
}
|
|
162
|
+
return (_jsxs("div", { className: "grid grid-cols-1 gap-2", children: [_jsx("div", { className: "overflow-x-auto", children: _jsxs("div", { "aria-label": "Events", className: "grid min-w-max items-center", style: grid, children: [_jsxs("div", { className: "contents", children: [_jsx("span", { className: "pb-1 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Event" }), targets.map((t) => (_jsx("span", { className: "truncate pb-1 text-center text-xs font-semibold tracking-wide text-muted-foreground uppercase", title: t.name, children: t.name }, t.id)))] }), groups.map((g) => (_jsxs("div", { className: "contents", children: [g.label ? (_jsx("span", { className: "border-t pt-3 pb-1 text-xs font-semibold tracking-wide text-muted-foreground uppercase", style: { gridColumn: '1 / -1' }, children: g.label })) : null, g.events.map((e) => (_jsxs("div", { className: "contents", children: [_jsxs("span", { className: "flex min-w-0 items-baseline gap-2 border-t py-2 pr-3", title: `${e.label}: ${e.detail}`, children: [_jsx("span", { className: "truncate text-sm whitespace-nowrap sm:shrink-0", children: e.label }), _jsx("span", { className: "hidden truncate text-xs text-muted-foreground sm:inline", children: e.detail })] }), targets.map((t) => (_jsx("span", { className: "self-stretch border-t py-2", children: _jsx("span", { className: "flex h-full items-center justify-center", children: _jsx(EventCell, { event: e, target: t.id, name: t.name, api: api }) }) }, t.id)))] }, e.key)))] }, g.label ?? '')))] }) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "A new target starts with every event on." })] }));
|
|
153
163
|
}
|
|
154
164
|
/** The Settings ▸ Notifications section's body. */
|
|
155
|
-
export function NotificationsSettings({ api: base, product, sender, }) {
|
|
165
|
+
export function NotificationsSettings({ api: base, product, sender, parts = 'all', }) {
|
|
156
166
|
const api = useNotifications(base, product, sender);
|
|
157
167
|
const [open, setOpen] = useState(null);
|
|
158
168
|
const toggle = (id) => (o) => setOpen(o ? id : null);
|
|
@@ -161,7 +171,9 @@ export function NotificationsSettings({ api: base, product, sender, }) {
|
|
|
161
171
|
if (api.q.isError)
|
|
162
172
|
return _jsx(ErrorLine, { error: api.q.error });
|
|
163
173
|
const s = api.q.data;
|
|
174
|
+
if (parts === 'events')
|
|
175
|
+
return _jsx(Events, { status: s, api: api });
|
|
164
176
|
return (_jsxs("div", { className: "grid grid-cols-1 gap-6", children: [_jsxs("div", { className: "grid grid-cols-1", children: [_jsx(IntegrationRow, { name: "Email", meta: "To your sign-in address", open: open === 'email', onOpenChange: toggle('email'), badge: s.email.connected ? (_jsx(TestBadge, { test: s.email.last_test })) : (_jsx(Badge, { variant: "outline", children: "Off" })), children: _jsx(Email, { status: s.email, api: api }) }), _jsx(IntegrationRow, { name: "ntfy", meta: "Phone notifications through the ntfy app", open: open === 'ntfy', onOpenChange: toggle('ntfy'), badge: s.ntfy.connected ? (_jsx(TestBadge, { test: s.ntfy.last_test })) : (_jsx(Badge, { variant: "outline", children: "Off" })), children: _jsx(Ntfy, { status: s.ntfy, api: api }) }), _jsx(IntegrationRow, { name: "Webhooks", meta: s.webhooks.length
|
|
165
177
|
? s.webhooks.map((h) => h.name).join(' · ')
|
|
166
|
-
: 'Events posted to your own addresses', open: open === 'webhooks', onOpenChange: toggle('webhooks'), badge: s.webhooks.length ? (s.webhooks.some((h) => h.last_test?.ok === false) ? (_jsxs(Badge, { variant: "outline", className: "text-loss", children: [_jsx(AlertTriangleIcon, {}), "Failed"] })) : (_jsxs(Badge, { variant: "outline", children: [s.webhooks.length, " webhook", s.webhooks.length === 1 ? '' : 's'] }))) : (_jsx(Badge, { variant: "outline", children: "None" })), children: _jsx(Webhooks, { hooks: s.webhooks, api: api }) })] }), _jsxs("section", { className: "grid grid-cols-1 gap-2", children: [_jsx("h4", { className: "font-medium", children: "Events" }), _jsx(Events, { status: s, api: api })] })] }));
|
|
178
|
+
: 'Events posted to your own addresses', open: open === 'webhooks', onOpenChange: toggle('webhooks'), badge: s.webhooks.length ? (s.webhooks.some((h) => h.last_test?.ok === false) ? (_jsxs(Badge, { variant: "outline", className: "text-loss", children: [_jsx(AlertTriangleIcon, {}), "Failed"] })) : (_jsxs(Badge, { variant: "outline", children: [s.webhooks.length, " webhook", s.webhooks.length === 1 ? '' : 's'] }))) : (_jsx(Badge, { variant: "outline", children: "None" })), children: _jsx(Webhooks, { hooks: s.webhooks, api: api }) })] }), parts === 'all' ? (_jsxs("section", { className: "grid grid-cols-1 gap-2", children: [_jsx("h4", { className: "font-medium", children: "Events" }), _jsx(Events, { status: s, api: api })] })) : null] }));
|
|
167
179
|
}
|
package/dist/shell.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import { type LumenFinderConfig } from './lumen-finder.js';
|
|
3
|
+
import { type NavItem } from './nav.js';
|
|
3
4
|
import { type SettingsGroupDef, type SettingsSectionDef } from './settings.js';
|
|
4
5
|
import { type StatusBarModel } from './status-bar.js';
|
|
5
6
|
interface ShellContext {
|
|
@@ -54,6 +55,8 @@ export interface LumenShellProps {
|
|
|
54
55
|
brand: ReactNode;
|
|
55
56
|
/** The app's sidebar content: `NavGroup`s and `SidebarSection`s. */
|
|
56
57
|
nav: ReactNode;
|
|
58
|
+
/** The phone's bottom tab bar: up to four main pages, then More (the sidebar). None by default. */
|
|
59
|
+
tabs?: NavItem[];
|
|
57
60
|
/** The app's status bar (DESIGN.md, Status bar): its sync and messages as data; pages add theirs
|
|
58
61
|
* with `useStatus`. Running Tasks of this app show by themselves. */
|
|
59
62
|
bar?: StatusBarModel;
|
|
@@ -101,7 +104,7 @@ export interface LumenShellProps {
|
|
|
101
104
|
/** The routed page (usually `<Outlet />`). */
|
|
102
105
|
children: ReactNode;
|
|
103
106
|
}
|
|
104
|
-
export declare function LumenShell({ app, appLabel, brand, nav, bar, status, health, statusEnd, releases, releaseApps, settings, shared, sharedLabel, settingsGroups, user, signOutUrl, profile, security, onRefresh, hideAmounts, onLogs, headerActions, panel, finder, wrap, children, }: LumenShellProps): import("react").JSX.Element;
|
|
107
|
+
export declare function LumenShell({ app, appLabel, brand, nav, tabs, bar, status, health, statusEnd, releases, releaseApps, settings, shared, sharedLabel, settingsGroups, user, signOutUrl, profile, security, onRefresh, hideAmounts, onLogs, headerActions, panel, finder, wrap, children, }: LumenShellProps): import("react").JSX.Element;
|
|
105
108
|
/** A status bar entry: an optional health dot, a label and its value ("Mail · Up to date"). */
|
|
106
109
|
export declare function StatusItem({ label, value, ok, className, }: {
|
|
107
110
|
label: ReactNode;
|
package/dist/shell.js
CHANGED
|
@@ -10,6 +10,7 @@ import { createContext, useCallback, useContext, useEffect, useMemo, useState, }
|
|
|
10
10
|
import { lazyPart } from './lazy-part.js';
|
|
11
11
|
import { cn } from './lib/utils.js';
|
|
12
12
|
import { LumenFinder } from './lumen-finder.js';
|
|
13
|
+
import { LumenTabBar } from './nav.js';
|
|
13
14
|
import { usePrefs } from './prefs.js';
|
|
14
15
|
import { withRefreshToken } from './refresh.js';
|
|
15
16
|
import { LumenSettings, ThemeToggle, allSettings, } from './settings.js';
|
|
@@ -96,7 +97,7 @@ const settingsFromUrl = (() => {
|
|
|
96
97
|
window.history.replaceState(window.history.state, '', url);
|
|
97
98
|
return at || 'profile';
|
|
98
99
|
})();
|
|
99
|
-
export function LumenShell({ app, appLabel, brand, nav, bar, status, health, statusEnd, releases, releaseApps, settings, shared, sharedLabel, settingsGroups, user = null, signOutUrl, profile, security, onRefresh, hideAmounts = false, onLogs, headerActions = null, panel, finder, wrap = (frame) => frame, children, }) {
|
|
100
|
+
export function LumenShell({ app, appLabel, brand, nav, tabs, bar, status, health, statusEnd, releases, releaseApps, settings, shared, sharedLabel, settingsGroups, user = null, signOutUrl, profile, security, onRefresh, hideAmounts = false, onLogs, headerActions = null, panel, finder, wrap = (frame) => frame, children, }) {
|
|
100
101
|
const [statusSlot, setStatusSlot] = useState(null);
|
|
101
102
|
const [dockSlot, setDockSlot] = useState(null);
|
|
102
103
|
const [refreshing, setRefreshing] = useState(false);
|
|
@@ -142,7 +143,7 @@ export function LumenShell({ app, appLabel, brand, nav, bar, status, health, sta
|
|
|
142
143
|
refreshing,
|
|
143
144
|
hideAmounts,
|
|
144
145
|
headerActions,
|
|
145
|
-
}, children: _jsx(StatusBarProvider, { children: _jsx(DockSlot.Provider, { value: dockSlot, children: _jsx(SourceHealthProvider, { children: wrap(_jsx(TooltipProvider, { delayDuration: 200, children: _jsx(SidebarProvider, { defaultOpen: sidebarOpen(), children: _jsxs(LumenFinder, { app: app, settings: findSettings, openSettings: openSettings, config: finder, children: [_jsx(CloseDrawerOnNavigate, {}), _jsxs(Sidebar, { collapsible: "icon", children: [_jsx(SidebarHeader, { children: brand }), _jsx(SidebarContent, { children: nav }), _jsx(SidebarFooter, { children: _jsx(NavUser, { who: user, signOutUrl: signOutUrl, onSettings: () => openSettings(), onLogs: onLogs }) }), _jsx(SidebarRail, {})] }), _jsxs(SidebarInset, { className: "flex min-h-svh min-w-0 flex-col", children: [_jsx("div", { className: "flex min-w-0 flex-1 flex-col", children: children }, epoch), _jsx(LumenStatusBar, { app: app, model: bar, legacy: status, slot: setStatusSlot, end: _jsxs(_Fragment, { children: [health, releases && _jsx(WhatsNew, { api: releases, apps: releaseApps }), statusEnd, _jsx(StatusTimeZone, {})] }) })] }), _jsx("div", { ref: setDockSlot, "data-slot": "shell-dock", className: "contents" }), panel, _jsx(LumenSettings, { open: settingsAt !== null, section: settingsAt ?? lastSection, onOpen: openSettings, onClose: () => setSettingsAt(null), appLabel: appLabel, profile: profile, security: security, sections: settings, shared: shared, sharedLabel: sharedLabel, groups: settingsGroups }), _jsx(Toaster, { position: "bottom-right" })] }) }) })) }) }) }) }));
|
|
146
|
+
}, children: _jsx(StatusBarProvider, { children: _jsx(DockSlot.Provider, { value: dockSlot, children: _jsx(SourceHealthProvider, { children: wrap(_jsx(TooltipProvider, { delayDuration: 200, children: _jsx(SidebarProvider, { defaultOpen: sidebarOpen(), children: _jsxs(LumenFinder, { app: app, settings: findSettings, openSettings: openSettings, config: finder, children: [_jsx(CloseDrawerOnNavigate, {}), _jsxs(Sidebar, { collapsible: "icon", children: [_jsx(SidebarHeader, { children: brand }), _jsx(SidebarContent, { children: nav }), _jsx(SidebarFooter, { children: _jsx(NavUser, { who: user, signOutUrl: signOutUrl, onSettings: () => openSettings(), onLogs: onLogs }) }), _jsx(SidebarRail, {})] }), _jsxs(SidebarInset, { className: "flex min-h-svh min-w-0 flex-col", children: [_jsx("div", { className: "flex min-w-0 flex-1 flex-col", children: children }, epoch), _jsx(LumenStatusBar, { app: app, model: bar, legacy: status, slot: setStatusSlot, end: _jsxs(_Fragment, { children: [health, releases && _jsx(WhatsNew, { api: releases, apps: releaseApps }), statusEnd, _jsx(StatusTimeZone, {})] }) }), tabs && _jsx(LumenTabBar, { items: tabs })] }), _jsx("div", { ref: setDockSlot, "data-slot": "shell-dock", className: "contents" }), panel, _jsx(LumenSettings, { open: settingsAt !== null, section: settingsAt ?? lastSection, onOpen: openSettings, onClose: () => setSettingsAt(null), appLabel: appLabel, profile: profile, security: security, sections: settings, shared: shared, sharedLabel: sharedLabel, groups: settingsGroups }), _jsx(Toaster, { position: "bottom-right" })] }) }) })) }) }) }) }));
|
|
146
147
|
}
|
|
147
148
|
/** A status bar entry: an optional health dot, a label and its value ("Mail · Up to date"). */
|
|
148
149
|
export function StatusItem({ label, value, ok, className, }) {
|
package/dist/status-bar.js
CHANGED
|
@@ -218,7 +218,7 @@ export function LumenStatusBar({ app, model, legacy, slot, end, }) {
|
|
|
218
218
|
const entries = useEntries(model, app);
|
|
219
219
|
const [open, setOpen] = useState(false);
|
|
220
220
|
const urgent = [...entries].sort((a, b) => a.rank - b.rank)[0];
|
|
221
|
-
return (_jsx("footer", { "data-slot": "shell-status", "aria-label": "Status", className: "sticky bottom-
|
|
221
|
+
return (_jsx("footer", { "data-slot": "shell-status", "aria-label": "Status", className: "sticky bottom-(--shell-tabbar-offset) z-10 flex h-(--shell-status-height) shrink-0 items-center gap-3 overflow-hidden border-t bg-background px-(--shell-gutter) text-xs whitespace-nowrap text-muted-foreground", children: mobile ? (_jsxs(_Fragment, { children: [urgent && (_jsx("span", { "aria-live": "polite", className: "flex min-w-0 items-center", children: _jsx(EntryLine, { e: { ...urgent, bar: undefined, detail: undefined } }) })), _jsx("div", { ref: slot, hidden: true }), entries.length > 0 ? (_jsxs(Sheet, { open: open, onOpenChange: setOpen, children: [_jsxs(SheetTrigger, { className: cn(STATUS_TRIGGER, 'num ml-auto shrink-0 border text-foreground/80'), "aria-label": `Status, ${entries.length} items`, children: [_jsx(InfoIcon, { className: "size-3.5" }), entries.length] }), _jsxs(SheetContent, { side: "bottom", className: "max-h-[75svh] gap-0 rounded-t-2xl p-0 text-sm", onOpenAutoFocus: (e) => e.preventDefault(), children: [_jsxs(SheetHeader, { className: "border-b px-4 py-3", children: [_jsx(SheetTitle, { children: "Status" }), _jsx(SheetDescription, { className: "sr-only", children: "What is true right now" })] }), _jsx("ul", { className: "overflow-y-auto px-4", children: [...entries]
|
|
222
222
|
.sort((a, b) => a.rank - b.rank)
|
|
223
223
|
.map((e) => (_jsx(SheetRow, { e: e }, e.id))) }), _jsx("div", { className: "flex items-center justify-end gap-1 border-t px-4 py-2 text-xs text-muted-foreground", children: end })] })] })) : (_jsx("span", { className: "ml-auto flex shrink-0 items-center gap-1", children: end }))] })) : (_jsxs(_Fragment, { children: [_jsx("span", { "aria-live": "polite", className: "flex min-w-0 items-center gap-3", children: entries.map((e) => (_jsx(EntryLine, { e: e }, e.id))) }), legacy, _jsx("div", { ref: slot, className: "flex shrink-0 items-center gap-4" }), _jsx("span", { className: "ml-auto flex shrink-0 items-center gap-1", children: end })] })) }));
|
|
224
224
|
}
|
package/dist/theme.css
CHANGED
|
@@ -89,6 +89,9 @@
|
|
|
89
89
|
--shell-assistant-width: 26rem;
|
|
90
90
|
--shell-header-height: 48px;
|
|
91
91
|
--shell-status-height: 32px;
|
|
92
|
+
/* The phone tab bar (LumenShell tabs); 0 until one shows, so the status bar sits above it. */
|
|
93
|
+
--shell-tabbar-height: 56px;
|
|
94
|
+
--shell-tabbar-offset: 0px;
|
|
92
95
|
/* A status bar message ends in an ellipsis here (StatusMessages), so it fits beside the bar's
|
|
93
96
|
other items; the full text opens on hover or tap. */
|
|
94
97
|
--status-message-max: 55vw;
|
|
@@ -851,3 +854,8 @@ body[data-spotlight="nav"] [data-spotlit] {
|
|
|
851
854
|
filter: none;
|
|
852
855
|
opacity: 1;
|
|
853
856
|
}
|
|
857
|
+
|
|
858
|
+
/* With a phone tab bar under it, the status bar rides above the bar instead of the screen's edge. */
|
|
859
|
+
[data-slot="sidebar-inset"]:has(> [data-slot="shell-tabbar"]) {
|
|
860
|
+
--shell-tabbar-offset: calc(var(--shell-tabbar-height) + env(safe-area-inset-bottom));
|
|
861
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@columenlabs/lumen-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.172",
|
|
4
4
|
"description": "Lumen: shared React components (frame, views, widgets, charts, drawers).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"@codemirror/lang-javascript": "6.2.5",
|
|
50
50
|
"@codemirror/lang-json": "6.0.2",
|
|
51
51
|
"@codemirror/lang-sql": "6.10.0",
|
|
52
|
-
"@columenlabs/lumen-ui-types": "0.1.
|
|
52
|
+
"@columenlabs/lumen-ui-types": "0.1.172",
|
|
53
53
|
"@dnd-kit/core": "6.3.1",
|
|
54
54
|
"@dnd-kit/sortable": "10.0.0",
|
|
55
55
|
"@dnd-kit/utilities": "3.2.2",
|