@cboxdk/cbox-ui 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -4
- package/dist/index.d.ts +68 -12
- package/dist/index.js +112 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/tokens/cbox.css +7 -0
package/README.md
CHANGED
|
@@ -200,12 +200,12 @@ grant) and the component renders its answer, refreshes it when `refresh_after` e
|
|
|
200
200
|
while the panel is open, and offers the way back to Cbox ID when the grant is missing.
|
|
201
201
|
|
|
202
202
|
```tsx
|
|
203
|
-
<CboxAppCatalog endpoint={appCatalogUrl} currentAppKey="infra"
|
|
203
|
+
<CboxAppCatalog endpoint={appCatalogUrl} currentAppKey="infra" />
|
|
204
204
|
```
|
|
205
205
|
|
|
206
206
|
The endpoint contract (`parseAppCatalog` is exported, so a host can test its own answer):
|
|
207
207
|
|
|
208
|
-
- `200 { apps: [{ key, name, launch_url, icon_url?, description? }], refresh_after?: seconds }`
|
|
208
|
+
- `200 { apps: [{ key, name, launch_url, icon_url?, icon_dark_url?, description? }], refresh_after?: seconds }`
|
|
209
209
|
— `refresh_after` is clamped to 1–60 s; apps without an `http(s)` launch URL are dropped.
|
|
210
210
|
- `401`/`403` with `{ authorize_url }` or `{ state: 'authorization_required' }` — the panel
|
|
211
211
|
says the apps could not be loaded and links "Reconnect to Cbox ID" to `authorize_url` with
|
|
@@ -213,8 +213,15 @@ The endpoint contract (`parseAppCatalog` is exported, so a host can test its own
|
|
|
213
213
|
- Anything else — "Apps could not be loaded." with a retry.
|
|
214
214
|
|
|
215
215
|
`endpoint={null}` renders the control disabled, so the chrome keeps its shape on pages
|
|
216
|
-
without an organization.
|
|
217
|
-
|
|
216
|
+
without an organization.
|
|
217
|
+
|
|
218
|
+
App icons come from Cbox ID's catalog: `icon_url` for light surfaces and `icon_dark_url` for
|
|
219
|
+
dark ones. The tiles render both and the token stylesheet shows the one for the current theme
|
|
220
|
+
(`.dark` or `[data-theme="dark"]` on the root); an app without an icon shows its initials. Apps
|
|
221
|
+
keep no local copy of the marks. Each answer is kept in this browser (`cbox-app-catalog`), so
|
|
222
|
+
`<CboxServiceMark appKey="infra" />` and the collapsed `CboxBrand` in the rail paint the app's
|
|
223
|
+
own icon at once on the next page. A host that loads the catalog itself calls
|
|
224
|
+
`rememberAppCatalog(apps)`; `CboxAppIcon` renders any single app's icon.
|
|
218
225
|
|
|
219
226
|
### Workspace navigation (current design contract)
|
|
220
227
|
|
package/dist/index.d.ts
CHANGED
|
@@ -62,8 +62,10 @@ type CboxAppLink = {
|
|
|
62
62
|
* Defaults to the first letter of `name`.
|
|
63
63
|
*/
|
|
64
64
|
monogram?: string;
|
|
65
|
-
/**
|
|
66
|
-
iconUrl?: string;
|
|
65
|
+
/** Cbox ID's catalog `icon_url`: the app's icon for light surfaces (`https:` only). */
|
|
66
|
+
iconUrl?: string | null;
|
|
67
|
+
/** Cbox ID's catalog `icon_dark_url`: the app's icon for dark surfaces. */
|
|
68
|
+
iconDarkUrl?: string | null;
|
|
67
69
|
/**
|
|
68
70
|
* Accent colour for the monogram tile (and current-app highlight).
|
|
69
71
|
* Hex string or any CSS colour the browser accepts. Falls back to
|
|
@@ -394,7 +396,9 @@ type CboxBrandProps = {
|
|
|
394
396
|
* `light` on dark / coloured surfaces. */
|
|
395
397
|
tone?: 'dark' | 'light';
|
|
396
398
|
/** Collapsed mode renders only the square icon — used in the
|
|
397
|
-
* collapsed-sidebar state.
|
|
399
|
+
* collapsed-sidebar state. The icon is the app's own icon from
|
|
400
|
+
* Cbox ID's catalog (light and dark) once this browser has seen
|
|
401
|
+
* the catalog, and the Cbox icon until then. */
|
|
398
402
|
collapsed?: boolean;
|
|
399
403
|
className?: string;
|
|
400
404
|
};
|
|
@@ -504,7 +508,10 @@ type CboxCatalogApp = {
|
|
|
504
508
|
key: string;
|
|
505
509
|
name: string;
|
|
506
510
|
launch_url: string;
|
|
511
|
+
/** Cbox ID's uploaded icon for light surfaces. */
|
|
507
512
|
icon_url?: string | null;
|
|
513
|
+
/** Cbox ID's uploaded icon for dark surfaces. */
|
|
514
|
+
icon_dark_url?: string | null;
|
|
508
515
|
description?: string | null;
|
|
509
516
|
};
|
|
510
517
|
type CboxAppCatalogState = {
|
|
@@ -528,17 +535,12 @@ type CboxAppCatalogProps = {
|
|
|
528
535
|
endpoint: string | null;
|
|
529
536
|
/** The app key of this app; its tile is marked current and does not navigate. */
|
|
530
537
|
currentAppKey: string;
|
|
531
|
-
/**
|
|
532
|
-
* Marks per app key, rendered instead of the catalog's `icon_url` (a host that bundles the
|
|
533
|
-
* family's service marks). An app without one shows its `icon_url`, then its initials.
|
|
534
|
-
*/
|
|
535
|
-
icons?: Record<string, ReactNode>;
|
|
536
538
|
className?: string;
|
|
537
539
|
};
|
|
538
540
|
/**
|
|
539
541
|
* Interpret the catalog endpoint's answer. The contract every Cbox app serves:
|
|
540
542
|
*
|
|
541
|
-
* - `200 { apps: [{ key, name, launch_url, icon_url?, description? }], refresh_after?: seconds }`
|
|
543
|
+
* - `200 { apps: [{ key, name, launch_url, icon_url?, icon_dark_url?, description? }], refresh_after?: seconds }`
|
|
542
544
|
* - `401`/`403 { authorize_url?: string }` or `{ state: 'authorization_required' }`: the
|
|
543
545
|
* person has to (re)connect their catalog grant; `authorize_url` is where.
|
|
544
546
|
* - anything else: unavailable right now.
|
|
@@ -556,9 +558,63 @@ declare function authorizeHref(authorizeUrl: string | null, location: {
|
|
|
556
558
|
* The Switch-app control: a grid of the Cbox apps this person can open, read live from the
|
|
557
559
|
* host's catalog endpoint. The host owns the endpoint and the grant; this renders the answer,
|
|
558
560
|
* refreshes it when it expires while the panel is open, and offers the way back to Cbox ID
|
|
559
|
-
* when the grant is missing.
|
|
561
|
+
* when the grant is missing. Each tile shows the app's icon from the catalog (light and dark),
|
|
562
|
+
* and the answer is kept so `CboxServiceMark` paints the app's own mark from it.
|
|
563
|
+
*/
|
|
564
|
+
declare function CboxAppCatalog({ endpoint, currentAppKey, className }: CboxAppCatalogProps): react_jsx_runtime.JSX.Element;
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Every Cbox app's icon comes from Cbox ID's catalog: one light and one dark upload per app
|
|
568
|
+
* (`icon_url`, `icon_dark_url`). There is no local copy of the marks in this package or in an
|
|
569
|
+
* app. The last catalog the browser saw is kept here so an app's own mark and the switcher
|
|
570
|
+
* paint at once on the next page; the live answer replaces it when it arrives.
|
|
560
571
|
*/
|
|
561
|
-
|
|
572
|
+
type CboxKnownApp = {
|
|
573
|
+
key: string;
|
|
574
|
+
name: string;
|
|
575
|
+
icon_url: string | null;
|
|
576
|
+
icon_dark_url: string | null;
|
|
577
|
+
};
|
|
578
|
+
/** Keep the catalog the person just loaded, so every mark on this origin repaints from it. */
|
|
579
|
+
declare function rememberAppCatalog(apps: ReadonlyArray<{
|
|
580
|
+
key: string;
|
|
581
|
+
name: string;
|
|
582
|
+
icon_url?: string | null;
|
|
583
|
+
icon_dark_url?: string | null;
|
|
584
|
+
}>): void;
|
|
585
|
+
/** The last catalog this browser kept; empty on the server and before the first answer. */
|
|
586
|
+
declare function useKnownAppCatalog(): CboxKnownApp[];
|
|
587
|
+
/** Only `https:` icon URLs are rendered: the icons are hosted by Cbox ID. */
|
|
588
|
+
declare function secureIconUrl(url: unknown): string | null;
|
|
589
|
+
type CboxAppIconProps = {
|
|
590
|
+
/** The app's name; its first two letters stand in when it has no icon. */
|
|
591
|
+
name: string;
|
|
592
|
+
/** Catalog `icon_url`: the icon for light surfaces (or the only one). */
|
|
593
|
+
iconUrl?: string | null;
|
|
594
|
+
/** Catalog `icon_dark_url`: the icon for dark surfaces. */
|
|
595
|
+
iconDarkUrl?: string | null;
|
|
596
|
+
/** Size and shape of the icon box, e.g. `h-6 w-6`. */
|
|
597
|
+
className?: string;
|
|
598
|
+
/** Rendered instead of initials when the app has no usable icon. */
|
|
599
|
+
fallback?: ReactNode;
|
|
600
|
+
};
|
|
601
|
+
/**
|
|
602
|
+
* One app's icon from the catalog. With both uploads it renders two images and the token
|
|
603
|
+
* stylesheet shows the one for the current theme (`.dark` or `[data-theme="dark"]` on the
|
|
604
|
+
* root, the same switch the tokens use). Without an icon, or when it fails to load, it shows
|
|
605
|
+
* the app's initials.
|
|
606
|
+
*/
|
|
607
|
+
declare function CboxAppIcon({ name, iconUrl, iconDarkUrl, className, fallback }: CboxAppIconProps): react_jsx_runtime.JSX.Element;
|
|
608
|
+
type CboxServiceMarkProps = {
|
|
609
|
+
/** The app key in Cbox ID's catalog (`signals`, `infra`, …). */
|
|
610
|
+
appKey: string;
|
|
611
|
+
/** Size of the mark. Default `h-6 w-6`. */
|
|
612
|
+
className?: string;
|
|
613
|
+
/** Shown while the catalog has not told this browser the app's icon yet. Default nothing. */
|
|
614
|
+
fallback?: ReactNode;
|
|
615
|
+
};
|
|
616
|
+
/** An app's own mark (its rail or brand icon), read from the catalog this browser last kept. */
|
|
617
|
+
declare function CboxServiceMark({ appKey, className, fallback }: CboxServiceMarkProps): react_jsx_runtime.JSX.Element;
|
|
562
618
|
|
|
563
619
|
type AppSwitcherProps = {
|
|
564
620
|
className?: string;
|
|
@@ -1524,4 +1580,4 @@ declare function Sparkline({ values, color, width, height }: {
|
|
|
1524
1580
|
height?: number;
|
|
1525
1581
|
}): react_jsx_runtime.JSX.Element | null;
|
|
1526
1582
|
|
|
1527
|
-
export { AppContent, AppLauncher, AppShell, AppSwitcher, CHART_PALETTE, CboxAppCatalog, type CboxAppCatalogProps, type CboxAppCatalogState, type CboxAppKey, type CboxAppLink, CboxAppShell, type CboxAppShellProps, CboxAppSidebar, type CboxAppSidebarProps, CboxBrand, type CboxBrandProps, type CboxCatalogApp, type CboxIdContextValue, CboxIdProvider, CboxLocalizationProvider, CboxMobileNavDrawer, type CboxMobileNavDrawerProps, type CboxNavArea, type CboxNavItem, type CboxNavSection, type CboxNotificationPreviewItem, type CboxNotificationsState, CboxOrgContextBar, type CboxOrgContextBarProps, type CboxOrganization, CboxSurfaces, type CboxSurfacesProps, type CboxTranslationValues, type CboxTranslator, type CboxUser, CboxWorkspaceShell, type CboxWorkspaceShellProps, Combobox, type ComboboxAvatar, type ComboboxGroup, type ComboboxOption, type ComboboxProps, type ContextAvatar, ContextAvatarChip, ContextBar, type ContextCrumb, type ContextItem, DataPanel, EmptyState, Heading, type InboxState, type InferUrlState, KeyValue, KeyValueList, type LinkComponent, NotificationsBell, OrgSwitcher, PageHeader, PageShell, type PageTabSpec, PageTabs, type PageTabsProps, type PageWidth, PanelBody, type RealtimeState, type RealtimeStatus, RowLink, type RowLinkProps, SearchTrigger, SectionHeader, Sparkline, StatTile, StatusPill, type SurfaceArgs, type SurfaceConfig, type SurfaceController, type SurfaceId, TopBar, type Transport, type UrlField, type UrlSchema, UserMenu, activeNavArea, activeNavItem, authorizeHref, buildUrlSearch, cn, defineUrlSchema, interpolate, navMatchLength, parseAppCatalog, parseUrlSearch, urlBoolean, urlEnum, urlNumber, urlString, urlStringArray, useCboxId, useCboxIdOptional, useCboxLocalization, useCboxSurfaces, useCboxSurfacesOptional };
|
|
1583
|
+
export { AppContent, AppLauncher, AppShell, AppSwitcher, CHART_PALETTE, CboxAppCatalog, type CboxAppCatalogProps, type CboxAppCatalogState, CboxAppIcon, type CboxAppIconProps, type CboxAppKey, type CboxAppLink, CboxAppShell, type CboxAppShellProps, CboxAppSidebar, type CboxAppSidebarProps, CboxBrand, type CboxBrandProps, type CboxCatalogApp, type CboxIdContextValue, CboxIdProvider, type CboxKnownApp, CboxLocalizationProvider, CboxMobileNavDrawer, type CboxMobileNavDrawerProps, type CboxNavArea, type CboxNavItem, type CboxNavSection, type CboxNotificationPreviewItem, type CboxNotificationsState, CboxOrgContextBar, type CboxOrgContextBarProps, type CboxOrganization, CboxServiceMark, type CboxServiceMarkProps, CboxSurfaces, type CboxSurfacesProps, type CboxTranslationValues, type CboxTranslator, type CboxUser, CboxWorkspaceShell, type CboxWorkspaceShellProps, Combobox, type ComboboxAvatar, type ComboboxGroup, type ComboboxOption, type ComboboxProps, type ContextAvatar, ContextAvatarChip, ContextBar, type ContextCrumb, type ContextItem, DataPanel, EmptyState, Heading, type InboxState, type InferUrlState, KeyValue, KeyValueList, type LinkComponent, NotificationsBell, OrgSwitcher, PageHeader, PageShell, type PageTabSpec, PageTabs, type PageTabsProps, type PageWidth, PanelBody, type RealtimeState, type RealtimeStatus, RowLink, type RowLinkProps, SearchTrigger, SectionHeader, Sparkline, StatTile, StatusPill, type SurfaceArgs, type SurfaceConfig, type SurfaceController, type SurfaceId, TopBar, type Transport, type UrlField, type UrlSchema, UserMenu, activeNavArea, activeNavItem, authorizeHref, buildUrlSearch, cn, defineUrlSchema, interpolate, navMatchLength, parseAppCatalog, parseUrlSearch, rememberAppCatalog, secureIconUrl, urlBoolean, urlEnum, urlNumber, urlString, urlStringArray, useCboxId, useCboxIdOptional, useCboxLocalization, useCboxSurfaces, useCboxSurfacesOptional, useKnownAppCatalog };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from './chunk-OFTNLF7H.js';
|
|
2
2
|
export { cn, niceTicks, timeTicks } from './chunk-OFTNLF7H.js';
|
|
3
|
-
import { createContext, useContext, useMemo,
|
|
4
|
-
import { jsx,
|
|
3
|
+
import { createContext, useContext, useMemo, useSyncExternalStore, useState, useEffect, useRef, useCallback, useId } from 'react';
|
|
4
|
+
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
5
5
|
import { X, Menu, PanelLeftOpen, PanelLeftClose, PinOff, Pin } from 'lucide-react';
|
|
6
6
|
|
|
7
7
|
var CboxIdContext = createContext(null);
|
|
@@ -60,6 +60,97 @@ function useCboxLocalization() {
|
|
|
60
60
|
function defaultDate(value) {
|
|
61
61
|
return new Date(value).toLocaleString();
|
|
62
62
|
}
|
|
63
|
+
var KEPT = "cbox-app-catalog";
|
|
64
|
+
var EVENT = "cbox-app-catalog";
|
|
65
|
+
var EMPTY = [];
|
|
66
|
+
var cache = { raw: null, apps: EMPTY };
|
|
67
|
+
function storage() {
|
|
68
|
+
try {
|
|
69
|
+
return typeof window === "undefined" ? null : window.localStorage;
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function readKnownAppCatalog() {
|
|
75
|
+
let raw = null;
|
|
76
|
+
try {
|
|
77
|
+
raw = storage()?.getItem(KEPT) ?? null;
|
|
78
|
+
} catch {
|
|
79
|
+
raw = null;
|
|
80
|
+
}
|
|
81
|
+
if (raw === cache.raw) return cache.apps;
|
|
82
|
+
let apps = EMPTY;
|
|
83
|
+
try {
|
|
84
|
+
const value = raw === null ? [] : JSON.parse(raw);
|
|
85
|
+
if (Array.isArray(value)) {
|
|
86
|
+
apps = value.flatMap((entry) => {
|
|
87
|
+
if (typeof entry !== "object" || entry === null) return [];
|
|
88
|
+
const app = entry;
|
|
89
|
+
if (typeof app.key !== "string" || typeof app.name !== "string") return [];
|
|
90
|
+
return [{ key: app.key, name: app.name, icon_url: secureIconUrl(app.icon_url), icon_dark_url: secureIconUrl(app.icon_dark_url) }];
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
apps = EMPTY;
|
|
95
|
+
}
|
|
96
|
+
cache = { raw, apps };
|
|
97
|
+
return apps;
|
|
98
|
+
}
|
|
99
|
+
function subscribe(change) {
|
|
100
|
+
if (typeof window === "undefined") return () => void 0;
|
|
101
|
+
const storageChange = (event) => {
|
|
102
|
+
if (event.key === KEPT) change();
|
|
103
|
+
};
|
|
104
|
+
document.addEventListener(EVENT, change);
|
|
105
|
+
window.addEventListener("storage", storageChange);
|
|
106
|
+
return () => {
|
|
107
|
+
document.removeEventListener(EVENT, change);
|
|
108
|
+
window.removeEventListener("storage", storageChange);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function rememberAppCatalog(apps) {
|
|
112
|
+
const kept = apps.map(({ key, name, icon_url, icon_dark_url }) => ({ key, name, icon_url: icon_url ?? null, icon_dark_url: icon_dark_url ?? null }));
|
|
113
|
+
try {
|
|
114
|
+
storage()?.setItem(KEPT, JSON.stringify(kept));
|
|
115
|
+
} catch {
|
|
116
|
+
}
|
|
117
|
+
if (typeof document !== "undefined") document.dispatchEvent(new CustomEvent(EVENT));
|
|
118
|
+
}
|
|
119
|
+
function useKnownAppCatalog() {
|
|
120
|
+
return useSyncExternalStore(subscribe, readKnownAppCatalog, () => EMPTY);
|
|
121
|
+
}
|
|
122
|
+
function secureIconUrl(url) {
|
|
123
|
+
if (typeof url !== "string" || url === "") return null;
|
|
124
|
+
try {
|
|
125
|
+
const parsed = new URL(url);
|
|
126
|
+
return parsed.protocol === "https:" ? parsed.href : null;
|
|
127
|
+
} catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function initials(name) {
|
|
132
|
+
return name.replace(/^Cbox\s*·\s*/i, "").slice(0, 2);
|
|
133
|
+
}
|
|
134
|
+
function CboxAppIcon({ name, iconUrl, iconDarkUrl, className = "h-6 w-6", fallback }) {
|
|
135
|
+
const light = secureIconUrl(iconUrl);
|
|
136
|
+
const dark = secureIconUrl(iconDarkUrl);
|
|
137
|
+
const [failed, setFailed] = useState(null);
|
|
138
|
+
const key = `${light ?? ""}|${dark ?? ""}`;
|
|
139
|
+
if (light === null || failed === key) {
|
|
140
|
+
if (fallback !== void 0) return /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
141
|
+
return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cn("grid shrink-0 place-items-center rounded bg-secondary text-[11px] font-semibold text-foreground", className), children: initials(name) });
|
|
142
|
+
}
|
|
143
|
+
const lost = () => setFailed(key);
|
|
144
|
+
return /* @__PURE__ */ jsxs("span", { "aria-hidden": true, className: cn("cbx-app-icon-box inline-flex shrink-0 items-center justify-center", className), children: [
|
|
145
|
+
/* @__PURE__ */ jsx("img", { src: light, alt: "", decoding: "async", onError: lost, className: cn("cbx-app-icon", dark === null ? "cbx-app-icon-single" : "cbx-app-icon-light", "h-full w-full") }),
|
|
146
|
+
dark !== null && /* @__PURE__ */ jsx("img", { src: dark, alt: "", decoding: "async", onError: lost, className: "cbx-app-icon cbx-app-icon-dark h-full w-full" })
|
|
147
|
+
] });
|
|
148
|
+
}
|
|
149
|
+
function CboxServiceMark({ appKey, className = "h-6 w-6", fallback = null }) {
|
|
150
|
+
const app = useKnownAppCatalog().find((entry) => entry.key === appKey);
|
|
151
|
+
if (app === void 0 || app.icon_url === null) return /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
152
|
+
return /* @__PURE__ */ jsx(CboxAppIcon, { name: app.name, iconUrl: app.icon_url, iconDarkUrl: app.icon_dark_url, className, fallback: fallback ?? void 0 });
|
|
153
|
+
}
|
|
63
154
|
function CboxBrand({
|
|
64
155
|
appKey,
|
|
65
156
|
submark,
|
|
@@ -69,7 +160,7 @@ function CboxBrand({
|
|
|
69
160
|
className
|
|
70
161
|
}) {
|
|
71
162
|
if (collapsed) {
|
|
72
|
-
|
|
163
|
+
const cboxIcon = /* @__PURE__ */ jsx(
|
|
73
164
|
"img",
|
|
74
165
|
{
|
|
75
166
|
src: `${assetBase}/cbox-icon.png`,
|
|
@@ -80,6 +171,7 @@ function CboxBrand({
|
|
|
80
171
|
className: cn("h-9 w-9 rounded-md", className)
|
|
81
172
|
}
|
|
82
173
|
);
|
|
174
|
+
return /* @__PURE__ */ jsx(CboxServiceMark, { appKey, className: cn("h-9 w-9", className), fallback: cboxIcon });
|
|
83
175
|
}
|
|
84
176
|
const wordmark = tone === "light" ? `${assetBase}/cbox-logo-white.png` : `${assetBase}/cbox-logo.png`;
|
|
85
177
|
const wordmarkRetina = tone === "light" ? `${assetBase}/cbox-logo-white@2x.png` : `${assetBase}/cbox-logo@2x.png`;
|
|
@@ -467,21 +559,13 @@ function AppSwitcher({ className, onLockedAppClick }) {
|
|
|
467
559
|
/* @__PURE__ */ jsx(
|
|
468
560
|
"span",
|
|
469
561
|
{
|
|
470
|
-
style: triggerAccent !== void 0 ? {
|
|
562
|
+
style: triggerAccent !== void 0 && secureIconUrl(currentApp?.iconUrl) === null ? {
|
|
471
563
|
backgroundColor: triggerAccent,
|
|
472
564
|
color: "#fff",
|
|
473
565
|
borderColor: triggerAccent
|
|
474
566
|
} : void 0,
|
|
475
567
|
className: "flex h-5 w-5 shrink-0 items-center justify-center rounded-sm border border-border bg-secondary text-[10px] font-bold",
|
|
476
|
-
children: currentApp
|
|
477
|
-
"img",
|
|
478
|
-
{
|
|
479
|
-
src: currentApp.iconUrl,
|
|
480
|
-
alt: "",
|
|
481
|
-
className: "h-3.5 w-3.5",
|
|
482
|
-
loading: "eager"
|
|
483
|
-
}
|
|
484
|
-
) : triggerMonogram
|
|
568
|
+
children: currentApp ? /* @__PURE__ */ jsx(CboxAppIcon, { name: currentApp.name, iconUrl: currentApp.iconUrl, iconDarkUrl: currentApp.iconDarkUrl, className: "h-3.5 w-3.5", fallback: /* @__PURE__ */ jsx(Fragment, { children: triggerMonogram }) }) : triggerMonogram
|
|
485
569
|
}
|
|
486
570
|
),
|
|
487
571
|
/* @__PURE__ */ jsx("span", { className: "max-w-[140px] truncate", children: triggerName }),
|
|
@@ -581,7 +665,7 @@ function AppRow({
|
|
|
581
665
|
const isLocked = app.entitled === false || isComingSoon;
|
|
582
666
|
const monogram = app.monogram ?? app.name.slice(0, 1).toUpperCase();
|
|
583
667
|
const accent = app.accentColor;
|
|
584
|
-
const tileStyle = accent !== void 0 && app.iconUrl ===
|
|
668
|
+
const tileStyle = accent !== void 0 && secureIconUrl(app.iconUrl) === null ? { backgroundColor: accent, color: "#fff", borderColor: accent } : void 0;
|
|
585
669
|
const href = orgSlug !== void 0 && app.current !== true ? buildOrgUrl(app.url, orgSlug) : app.url;
|
|
586
670
|
const tile = /* @__PURE__ */ jsx(
|
|
587
671
|
"div",
|
|
@@ -592,7 +676,7 @@ function AppRow({
|
|
|
592
676
|
app.current && "ring-2 ring-accent-edge ring-offset-1 ring-offset-popover",
|
|
593
677
|
isLocked && "opacity-60 grayscale"
|
|
594
678
|
),
|
|
595
|
-
children:
|
|
679
|
+
children: /* @__PURE__ */ jsx(CboxAppIcon, { name: app.name, iconUrl: app.iconUrl, iconDarkUrl: app.iconDarkUrl, className: "h-5 w-5", fallback: /* @__PURE__ */ jsx("span", { children: monogram }) })
|
|
596
680
|
}
|
|
597
681
|
);
|
|
598
682
|
const body = /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col leading-tight", children: [
|
|
@@ -1491,7 +1575,7 @@ function UserMenu({ extras, className, placement = "bottom-end", LinkComponent:
|
|
|
1491
1575
|
const { t } = useCboxLocalization();
|
|
1492
1576
|
const { user, currentOrganization, urls } = useCboxId();
|
|
1493
1577
|
const [open, setOpen] = useState(false);
|
|
1494
|
-
const
|
|
1578
|
+
const initials2 = user.name.split(/\s+/).filter(Boolean).slice(0, 2).map((n) => n[0]?.toUpperCase() ?? "").join("");
|
|
1495
1579
|
return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), onKeyDown: (event) => {
|
|
1496
1580
|
if (event.key === "Escape") setOpen(false);
|
|
1497
1581
|
}, onBlur: (event) => {
|
|
@@ -1515,7 +1599,7 @@ function UserMenu({ extras, className, placement = "bottom-end", LinkComponent:
|
|
|
1515
1599
|
alt: "",
|
|
1516
1600
|
className: "h-7 w-7 rounded-full object-cover"
|
|
1517
1601
|
}
|
|
1518
|
-
) : /* @__PURE__ */ jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-full bg-accent-soft text-[11px] font-semibold text-foreground", children:
|
|
1602
|
+
) : /* @__PURE__ */ jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-full bg-accent-soft text-[11px] font-semibold text-foreground", children: initials2 || "?" })
|
|
1519
1603
|
}
|
|
1520
1604
|
),
|
|
1521
1605
|
open ? /* @__PURE__ */ jsxs("div", { className: cn("absolute z-50 w-64 rounded-lg border border-border bg-popover p-1 shadow-md", placement === "top-start" ? "bottom-full left-0 mb-2" : "right-0 top-full mt-1"), children: [
|
|
@@ -1946,7 +2030,7 @@ function ContextAvatarChip({
|
|
|
1946
2030
|
fallbackLabel,
|
|
1947
2031
|
size = "md"
|
|
1948
2032
|
}) {
|
|
1949
|
-
const
|
|
2033
|
+
const initials2 = avatar.initials ?? deriveInitials(fallbackLabel);
|
|
1950
2034
|
const sizeClass = size === "sm" ? "h-[26px] w-[26px] text-[10px]" : "h-7 w-7 text-[11px]";
|
|
1951
2035
|
const style = avatar.src === void 0 && avatar.accent !== void 0 ? {
|
|
1952
2036
|
backgroundColor: avatar.accent,
|
|
@@ -1975,7 +2059,7 @@ function ContextAvatarChip({
|
|
|
1975
2059
|
avatar.accent === void 0 && "bg-info-soft text-info",
|
|
1976
2060
|
sizeClass
|
|
1977
2061
|
),
|
|
1978
|
-
children:
|
|
2062
|
+
children: initials2
|
|
1979
2063
|
}
|
|
1980
2064
|
);
|
|
1981
2065
|
}
|
|
@@ -2432,7 +2516,7 @@ function AppRow2({ app, orgSlug, onLockedAppClick }) {
|
|
|
2432
2516
|
const isLocked = app.entitled === false || isComingSoon;
|
|
2433
2517
|
const monogram = app.monogram ?? app.name.slice(0, 1).toUpperCase();
|
|
2434
2518
|
const accent = app.accentColor;
|
|
2435
|
-
const tileStyle = accent !== void 0 && app.iconUrl ===
|
|
2519
|
+
const tileStyle = accent !== void 0 && secureIconUrl(app.iconUrl) === null ? { backgroundColor: accent, color: "#fff", borderColor: accent } : void 0;
|
|
2436
2520
|
const href = orgSlug !== void 0 && app.current !== true ? buildOrgUrl2(app.url, orgSlug) : app.url;
|
|
2437
2521
|
const tile = /* @__PURE__ */ jsx(
|
|
2438
2522
|
"div",
|
|
@@ -2443,7 +2527,7 @@ function AppRow2({ app, orgSlug, onLockedAppClick }) {
|
|
|
2443
2527
|
app.current && "ring-2 ring-accent-edge ring-offset-1 ring-offset-popover",
|
|
2444
2528
|
isLocked && "opacity-60 grayscale"
|
|
2445
2529
|
),
|
|
2446
|
-
children:
|
|
2530
|
+
children: /* @__PURE__ */ jsx(CboxAppIcon, { name: app.name, iconUrl: app.iconUrl, iconDarkUrl: app.iconDarkUrl, className: "h-5 w-5", fallback: /* @__PURE__ */ jsx("span", { children: monogram }) })
|
|
2447
2531
|
}
|
|
2448
2532
|
);
|
|
2449
2533
|
const body = /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col leading-tight", children: [
|
|
@@ -2597,6 +2681,7 @@ function parseAppCatalog(ok, body) {
|
|
|
2597
2681
|
name: app.name,
|
|
2598
2682
|
launch_url: app.launch_url,
|
|
2599
2683
|
icon_url: typeof app.icon_url === "string" ? app.icon_url : null,
|
|
2684
|
+
icon_dark_url: typeof app.icon_dark_url === "string" ? app.icon_dark_url : null,
|
|
2600
2685
|
description: typeof app.description === "string" ? app.description : null
|
|
2601
2686
|
});
|
|
2602
2687
|
}
|
|
@@ -2614,7 +2699,7 @@ function authorizeHref(authorizeUrl, location) {
|
|
|
2614
2699
|
return null;
|
|
2615
2700
|
}
|
|
2616
2701
|
}
|
|
2617
|
-
function CboxAppCatalog({ endpoint, currentAppKey,
|
|
2702
|
+
function CboxAppCatalog({ endpoint, currentAppKey, className }) {
|
|
2618
2703
|
const { t } = useCboxLocalization();
|
|
2619
2704
|
const [open, setOpen] = useState(false);
|
|
2620
2705
|
const [state, setState] = useState({ status: "idle" });
|
|
@@ -2638,6 +2723,7 @@ function CboxAppCatalog({ endpoint, currentAppKey, icons, className }) {
|
|
|
2638
2723
|
const next = parseAppCatalog(response.ok, body);
|
|
2639
2724
|
expires.current = next.status === "ready" ? Date.now() + next.refreshAfter * 1e3 : 0;
|
|
2640
2725
|
setState(next);
|
|
2726
|
+
if (next.status === "ready") rememberAppCatalog(next.apps);
|
|
2641
2727
|
} catch {
|
|
2642
2728
|
if (!request.signal.aborted) {
|
|
2643
2729
|
expires.current = 0;
|
|
@@ -2722,7 +2808,7 @@ function CboxAppCatalog({ endpoint, currentAppKey, icons, className }) {
|
|
|
2722
2808
|
onClick: () => setOpen(false),
|
|
2723
2809
|
className: cn("relative flex min-h-[60px] min-w-0 flex-col items-center justify-center gap-1.5 rounded-md p-2 text-center text-[13px] font-medium leading-tight text-foreground hover:bg-secondary", current && "bg-accent-soft"),
|
|
2724
2810
|
children: [
|
|
2725
|
-
/* @__PURE__ */ jsx(
|
|
2811
|
+
/* @__PURE__ */ jsx(CboxAppIcon, { name: app.name, iconUrl: app.icon_url, iconDarkUrl: app.icon_dark_url }),
|
|
2726
2812
|
/* @__PURE__ */ jsx("span", { className: "max-w-full break-words", children: app.name.replace(/^Cbox\s*·\s*/i, "") }),
|
|
2727
2813
|
current && /* @__PURE__ */ jsx(Check2, {})
|
|
2728
2814
|
]
|
|
@@ -2734,16 +2820,6 @@ function CboxAppCatalog({ endpoint, currentAppKey, icons, className }) {
|
|
|
2734
2820
|
] }) : null
|
|
2735
2821
|
] });
|
|
2736
2822
|
}
|
|
2737
|
-
function AppMark({ app, icon }) {
|
|
2738
|
-
const [failed, setFailed] = useState(false);
|
|
2739
|
-
if (icon !== void 0) {
|
|
2740
|
-
return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "flex h-6 w-6 items-center justify-center", children: icon });
|
|
2741
|
-
}
|
|
2742
|
-
if (typeof app.icon_url === "string" && app.icon_url !== "" && !failed) {
|
|
2743
|
-
return /* @__PURE__ */ jsx("img", { src: app.icon_url, alt: "", className: "h-6 w-6 rounded object-contain", loading: "lazy", onError: () => setFailed(true) });
|
|
2744
|
-
}
|
|
2745
|
-
return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "grid h-6 w-6 place-items-center rounded bg-secondary text-[11px] font-semibold text-foreground", children: app.name.slice(0, 2) });
|
|
2746
|
-
}
|
|
2747
2823
|
function Grid2() {
|
|
2748
2824
|
return /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "currentColor", "aria-hidden": true, children: [3, 9, 15].flatMap((y) => [3, 9, 15].map((x) => /* @__PURE__ */ jsx("circle", { cx: x, cy: y, r: "1.65" }, `${x}-${y}`))) });
|
|
2749
2825
|
}
|
|
@@ -3145,7 +3221,7 @@ function ComboboxAvatarChip({
|
|
|
3145
3221
|
avatar,
|
|
3146
3222
|
fallbackLabel
|
|
3147
3223
|
}) {
|
|
3148
|
-
const
|
|
3224
|
+
const initials2 = avatar.initials ?? deriveInitials3(fallbackLabel);
|
|
3149
3225
|
const style = avatar.src === void 0 && avatar.accent !== void 0 ? { backgroundColor: avatar.accent, color: "#fff" } : void 0;
|
|
3150
3226
|
if (avatar.src !== void 0) {
|
|
3151
3227
|
return /* @__PURE__ */ jsx(
|
|
@@ -3166,7 +3242,7 @@ function ComboboxAvatarChip({
|
|
|
3166
3242
|
"flex h-5 w-5 shrink-0 items-center justify-center rounded-sm text-[9px] font-semibold tracking-tight",
|
|
3167
3243
|
avatar.accent === void 0 && "bg-info-soft text-info"
|
|
3168
3244
|
),
|
|
3169
|
-
children:
|
|
3245
|
+
children: initials2
|
|
3170
3246
|
}
|
|
3171
3247
|
);
|
|
3172
3248
|
}
|
|
@@ -4011,6 +4087,6 @@ function Sparkline({ values, color = "var(--chart-1)", width = 90, height = 20 }
|
|
|
4011
4087
|
] });
|
|
4012
4088
|
}
|
|
4013
4089
|
|
|
4014
|
-
export { AppContent, AppLauncher, AppShell, AppSwitcher, CHART_PALETTE, CboxAppCatalog, CboxAppShell, CboxAppSidebar, CboxBrand, CboxIdProvider, CboxLocalizationProvider, CboxMobileNavDrawer, CboxOrgContextBar, CboxSurfaces, CboxWorkspaceShell, Combobox, ContextAvatarChip, ContextBar, DataPanel, EmptyState, Heading, KeyValue, KeyValueList, NotificationsBell, OrgSwitcher, PageHeader, PageShell, PageTabs, PanelBody, RowLink, SearchTrigger, SectionHeader, Sparkline, StatTile, StatusPill, TopBar, UserMenu, activeNavArea, activeNavItem, authorizeHref, buildUrlSearch, defineUrlSchema, interpolate, navMatchLength, parseAppCatalog, parseUrlSearch, urlBoolean, urlEnum, urlNumber, urlString, urlStringArray, useCboxId, useCboxIdOptional, useCboxLocalization, useCboxSurfaces, useCboxSurfacesOptional };
|
|
4090
|
+
export { AppContent, AppLauncher, AppShell, AppSwitcher, CHART_PALETTE, CboxAppCatalog, CboxAppIcon, CboxAppShell, CboxAppSidebar, CboxBrand, CboxIdProvider, CboxLocalizationProvider, CboxMobileNavDrawer, CboxOrgContextBar, CboxServiceMark, CboxSurfaces, CboxWorkspaceShell, Combobox, ContextAvatarChip, ContextBar, DataPanel, EmptyState, Heading, KeyValue, KeyValueList, NotificationsBell, OrgSwitcher, PageHeader, PageShell, PageTabs, PanelBody, RowLink, SearchTrigger, SectionHeader, Sparkline, StatTile, StatusPill, TopBar, UserMenu, activeNavArea, activeNavItem, authorizeHref, buildUrlSearch, defineUrlSchema, interpolate, navMatchLength, parseAppCatalog, parseUrlSearch, rememberAppCatalog, secureIconUrl, urlBoolean, urlEnum, urlNumber, urlString, urlStringArray, useCboxId, useCboxIdOptional, useCboxLocalization, useCboxSurfaces, useCboxSurfacesOptional, useKnownAppCatalog };
|
|
4015
4091
|
//# sourceMappingURL=index.js.map
|
|
4016
4092
|
//# sourceMappingURL=index.js.map
|