@the-portland-company/shell 0.52.0 → 0.53.1
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/CHANGELOG.md +19 -0
- package/dist/app-chrome.cjs +312 -62
- package/dist/app-chrome.cjs.map +1 -1
- package/dist/app-chrome.d.cts +55 -2
- package/dist/app-chrome.d.ts +55 -2
- package/dist/app-chrome.js +314 -65
- package/dist/app-chrome.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
## 0.53.0 — 2026-09-29
|
|
2
|
+
|
|
3
|
+
### Added — Loading Screen boot surface (Specs/Loading-Screen)
|
|
4
|
+
|
|
5
|
+
`PolitogyAppFrame` now renders the platform's boot screen: a full-screen
|
|
6
|
+
`ink-900` surface with the Politogy VRM lockup, a determinate/monotonic
|
|
7
|
+
gradient progress bar (Race Blue → Race Red), and a throttled monospace
|
|
8
|
+
status line, shown on cold start / hard reload / organization switch / mode
|
|
9
|
+
switch — the four events Global-Rules' Boot Screen Exception names — and
|
|
10
|
+
never for an in-page fetch or navigation. New export
|
|
11
|
+
`LoadingScreen`/`BootProgress` from `@the-portland-company/shell/app-chrome`
|
|
12
|
+
lets an app report real pipeline stages; a new `PolitogyAppFrame` prop
|
|
13
|
+
`appReady` lets an app dismiss the screen explicitly. Neither is required:
|
|
14
|
+
an app that passes nothing gets a truthful two-stage report driven by the
|
|
15
|
+
same org/nav resolution signal the frame already computes, so all 14
|
|
16
|
+
consumer apps keep working unchanged. `prefers-reduced-motion` skips the
|
|
17
|
+
exit transition; the bar is `role="progressbar"` with `aria-live` announced
|
|
18
|
+
copy throttled to one per 1.5s (final "Ready." always announced).
|
|
19
|
+
|
|
1
20
|
## 0.52.0 — 2026-09-12
|
|
2
21
|
|
|
3
22
|
### Added — mobile navigation (GLOBAL)
|
package/dist/app-chrome.cjs
CHANGED
|
@@ -3256,6 +3256,216 @@ function watchLogout(onLogout, options = {}) {
|
|
|
3256
3256
|
}
|
|
3257
3257
|
return cleanup;
|
|
3258
3258
|
}
|
|
3259
|
+
var SYNTHETIC_STAGES = [
|
|
3260
|
+
{ pct: 20, label: "Connecting to your organization\u2026" },
|
|
3261
|
+
{ pct: 100, label: "Ready." }
|
|
3262
|
+
];
|
|
3263
|
+
var ANNOUNCE_THROTTLE_MS = 1500;
|
|
3264
|
+
var MIN_DWELL_MS = 450;
|
|
3265
|
+
var FILL_EASE_MS = 380;
|
|
3266
|
+
var HOLD_MS = 240;
|
|
3267
|
+
var FADE_MS = 300;
|
|
3268
|
+
function usePrefersReducedMotion() {
|
|
3269
|
+
const [reduced, setReduced] = react.useState(false);
|
|
3270
|
+
react.useEffect(() => {
|
|
3271
|
+
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
3272
|
+
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
3273
|
+
setReduced(mq.matches);
|
|
3274
|
+
const onChange = () => setReduced(mq.matches);
|
|
3275
|
+
mq.addEventListener("change", onChange);
|
|
3276
|
+
return () => mq.removeEventListener("change", onChange);
|
|
3277
|
+
}, []);
|
|
3278
|
+
return reduced;
|
|
3279
|
+
}
|
|
3280
|
+
function useThrottledAnnouncement(label, isFinal) {
|
|
3281
|
+
const [announced, setAnnounced] = react.useState(label);
|
|
3282
|
+
const lastAtRef = react.useRef(0);
|
|
3283
|
+
react.useEffect(() => {
|
|
3284
|
+
const now = Date.now();
|
|
3285
|
+
if (isFinal || now - lastAtRef.current >= ANNOUNCE_THROTTLE_MS) {
|
|
3286
|
+
lastAtRef.current = now;
|
|
3287
|
+
setAnnounced(label);
|
|
3288
|
+
return;
|
|
3289
|
+
}
|
|
3290
|
+
const wait = ANNOUNCE_THROTTLE_MS - (now - lastAtRef.current);
|
|
3291
|
+
const t = setTimeout(() => {
|
|
3292
|
+
lastAtRef.current = Date.now();
|
|
3293
|
+
setAnnounced(label);
|
|
3294
|
+
}, wait);
|
|
3295
|
+
return () => clearTimeout(t);
|
|
3296
|
+
}, [label, isFinal]);
|
|
3297
|
+
return announced;
|
|
3298
|
+
}
|
|
3299
|
+
function useDwelledLabel(label) {
|
|
3300
|
+
const [displayed, setDisplayed] = react.useState(label);
|
|
3301
|
+
const shownAtRef = react.useRef(0);
|
|
3302
|
+
const pendingRef = react.useRef(null);
|
|
3303
|
+
react.useEffect(() => {
|
|
3304
|
+
if (label === displayed) return;
|
|
3305
|
+
const elapsed = Date.now() - shownAtRef.current;
|
|
3306
|
+
if (pendingRef.current) clearTimeout(pendingRef.current);
|
|
3307
|
+
if (elapsed >= MIN_DWELL_MS) {
|
|
3308
|
+
shownAtRef.current = Date.now();
|
|
3309
|
+
setDisplayed(label);
|
|
3310
|
+
} else {
|
|
3311
|
+
pendingRef.current = setTimeout(() => {
|
|
3312
|
+
shownAtRef.current = Date.now();
|
|
3313
|
+
setDisplayed(label);
|
|
3314
|
+
}, MIN_DWELL_MS - elapsed);
|
|
3315
|
+
}
|
|
3316
|
+
return () => {
|
|
3317
|
+
if (pendingRef.current) clearTimeout(pendingRef.current);
|
|
3318
|
+
};
|
|
3319
|
+
}, [label]);
|
|
3320
|
+
return displayed;
|
|
3321
|
+
}
|
|
3322
|
+
function LoadingScreen({
|
|
3323
|
+
bootProgress,
|
|
3324
|
+
syntheticReady,
|
|
3325
|
+
visible,
|
|
3326
|
+
onExitComplete
|
|
3327
|
+
}) {
|
|
3328
|
+
const reducedMotion = usePrefersReducedMotion();
|
|
3329
|
+
const synthetic = react.useMemo(() => {
|
|
3330
|
+
const stage = syntheticReady ? SYNTHETIC_STAGES[1] : SYNTHETIC_STAGES[0];
|
|
3331
|
+
return {
|
|
3332
|
+
pct: stage.pct,
|
|
3333
|
+
label: stage.label,
|
|
3334
|
+
phase: syntheticReady ? "done" : "booting"
|
|
3335
|
+
};
|
|
3336
|
+
}, [syntheticReady]);
|
|
3337
|
+
const pct = bootProgress?.pct ?? synthetic.pct;
|
|
3338
|
+
const rawLabel = bootProgress?.label ?? synthetic.label;
|
|
3339
|
+
const phase = bootProgress?.phase ?? synthetic.phase;
|
|
3340
|
+
const isFinal = phase === "done" && pct >= 100;
|
|
3341
|
+
const highWaterRef = react.useRef(0);
|
|
3342
|
+
const clampedPct = Math.max(pct, highWaterRef.current);
|
|
3343
|
+
react.useEffect(() => {
|
|
3344
|
+
highWaterRef.current = clampedPct;
|
|
3345
|
+
}, [clampedPct]);
|
|
3346
|
+
const displayedLabel = useDwelledLabel(rawLabel);
|
|
3347
|
+
const announced = useThrottledAnnouncement(displayedLabel, isFinal);
|
|
3348
|
+
const [fading, setFading] = react.useState(false);
|
|
3349
|
+
react.useEffect(() => {
|
|
3350
|
+
if (!isFinal) return;
|
|
3351
|
+
const holdMs = reducedMotion ? 0 : HOLD_MS;
|
|
3352
|
+
const fadeMs = reducedMotion ? 0 : FADE_MS;
|
|
3353
|
+
const holdTimer = setTimeout(() => {
|
|
3354
|
+
setFading(true);
|
|
3355
|
+
const fadeTimer = setTimeout(onExitComplete, fadeMs);
|
|
3356
|
+
return () => clearTimeout(fadeTimer);
|
|
3357
|
+
}, holdMs);
|
|
3358
|
+
return () => clearTimeout(holdTimer);
|
|
3359
|
+
}, [isFinal, reducedMotion]);
|
|
3360
|
+
if (!visible) return null;
|
|
3361
|
+
const isError = phase === "error";
|
|
3362
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3363
|
+
"div",
|
|
3364
|
+
{
|
|
3365
|
+
"data-testid": "politogy-loading-screen",
|
|
3366
|
+
style: {
|
|
3367
|
+
position: "fixed",
|
|
3368
|
+
inset: 0,
|
|
3369
|
+
zIndex: 2147483e3,
|
|
3370
|
+
display: "flex",
|
|
3371
|
+
flexDirection: "column",
|
|
3372
|
+
alignItems: "center",
|
|
3373
|
+
justifyContent: "center",
|
|
3374
|
+
gap: "2rem",
|
|
3375
|
+
background: "var(--color-ink-900, #0a0a0c)",
|
|
3376
|
+
color: "#fff",
|
|
3377
|
+
opacity: fading ? 0 : 1,
|
|
3378
|
+
transition: reducedMotion ? void 0 : `opacity ${FADE_MS}ms ease-out`
|
|
3379
|
+
},
|
|
3380
|
+
children: [
|
|
3381
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3382
|
+
"div",
|
|
3383
|
+
{
|
|
3384
|
+
"aria-label": "Politogy",
|
|
3385
|
+
style: {
|
|
3386
|
+
display: "flex",
|
|
3387
|
+
alignItems: "center",
|
|
3388
|
+
gap: "0.6rem",
|
|
3389
|
+
fontFamily: "var(--font-sans, ui-sans-serif, system-ui, sans-serif)"
|
|
3390
|
+
},
|
|
3391
|
+
children: [
|
|
3392
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3393
|
+
"span",
|
|
3394
|
+
{
|
|
3395
|
+
"aria-hidden": "true",
|
|
3396
|
+
style: {
|
|
3397
|
+
display: "inline-flex",
|
|
3398
|
+
alignItems: "center",
|
|
3399
|
+
justifyContent: "center",
|
|
3400
|
+
width: 34,
|
|
3401
|
+
height: 26,
|
|
3402
|
+
borderRadius: 6,
|
|
3403
|
+
background: "#fff",
|
|
3404
|
+
color: "var(--color-ink-900, #0a0a0c)",
|
|
3405
|
+
fontSize: "0.8125rem",
|
|
3406
|
+
fontWeight: 800,
|
|
3407
|
+
letterSpacing: "-0.02em"
|
|
3408
|
+
},
|
|
3409
|
+
children: "vrm"
|
|
3410
|
+
}
|
|
3411
|
+
),
|
|
3412
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "1.375rem", fontWeight: 700, letterSpacing: "-0.02em" }, children: "Politogy" })
|
|
3413
|
+
]
|
|
3414
|
+
}
|
|
3415
|
+
),
|
|
3416
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { width: 320, maxWidth: "80vw" }, children: [
|
|
3417
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3418
|
+
"div",
|
|
3419
|
+
{
|
|
3420
|
+
role: "progressbar",
|
|
3421
|
+
"aria-valuemin": 0,
|
|
3422
|
+
"aria-valuemax": 100,
|
|
3423
|
+
"aria-valuenow": Math.round(clampedPct),
|
|
3424
|
+
"aria-label": "Loading your workspace",
|
|
3425
|
+
style: {
|
|
3426
|
+
height: 3,
|
|
3427
|
+
width: "100%",
|
|
3428
|
+
borderRadius: 2,
|
|
3429
|
+
background: "rgba(255,255,255,0.12)",
|
|
3430
|
+
overflow: "hidden"
|
|
3431
|
+
},
|
|
3432
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3433
|
+
"div",
|
|
3434
|
+
{
|
|
3435
|
+
style: {
|
|
3436
|
+
height: "100%",
|
|
3437
|
+
width: `${clampedPct}%`,
|
|
3438
|
+
background: "linear-gradient(90deg, var(--color-race-blue, #1d4ed8), var(--color-race-red, #dc2626))",
|
|
3439
|
+
transition: reducedMotion ? void 0 : `width ${FILL_EASE_MS}ms ease-out`
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
)
|
|
3443
|
+
}
|
|
3444
|
+
),
|
|
3445
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3446
|
+
"div",
|
|
3447
|
+
{
|
|
3448
|
+
role: "status",
|
|
3449
|
+
"aria-live": "polite",
|
|
3450
|
+
style: {
|
|
3451
|
+
marginTop: "0.75rem",
|
|
3452
|
+
fontFamily: "var(--font-mono, ui-monospace, monospace)",
|
|
3453
|
+
fontSize: "0.8125rem",
|
|
3454
|
+
color: isError ? "var(--color-race-red, #dc2626)" : "rgba(255,255,255,0.72)",
|
|
3455
|
+
textAlign: "center"
|
|
3456
|
+
},
|
|
3457
|
+
children: announced
|
|
3458
|
+
}
|
|
3459
|
+
),
|
|
3460
|
+
isError && (bootProgress?.onRetry || bootProgress?.onSignOut) && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: "1rem", display: "flex", gap: "0.75rem", justifyContent: "center" }, children: [
|
|
3461
|
+
bootProgress?.onRetry && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: bootProgress.onRetry, children: "Retry" }),
|
|
3462
|
+
bootProgress?.onSignOut && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: bootProgress.onSignOut, children: "Sign out" })
|
|
3463
|
+
] })
|
|
3464
|
+
] })
|
|
3465
|
+
]
|
|
3466
|
+
}
|
|
3467
|
+
);
|
|
3468
|
+
}
|
|
3259
3469
|
var CORE_APP = "vrm";
|
|
3260
3470
|
var DEFAULT_SIGN_OUT_URL = "/auth/signout";
|
|
3261
3471
|
var DEFAULT_BRAND_LOGO = "/brand/logos/relationship-mode-logo.png";
|
|
@@ -3306,6 +3516,8 @@ function PolitogyAppFrame({
|
|
|
3306
3516
|
signOutUrl = DEFAULT_SIGN_OUT_URL,
|
|
3307
3517
|
buildMeta,
|
|
3308
3518
|
data,
|
|
3519
|
+
bootProgress,
|
|
3520
|
+
appReady,
|
|
3309
3521
|
children
|
|
3310
3522
|
}) {
|
|
3311
3523
|
const meta = POLITOGY_APPS[app];
|
|
@@ -3334,6 +3546,8 @@ function PolitogyAppFrame({
|
|
|
3334
3546
|
signOutUrl,
|
|
3335
3547
|
buildMeta,
|
|
3336
3548
|
data,
|
|
3549
|
+
bootProgress,
|
|
3550
|
+
appReady,
|
|
3337
3551
|
children
|
|
3338
3552
|
}
|
|
3339
3553
|
)
|
|
@@ -3350,10 +3564,33 @@ function FrameBody({
|
|
|
3350
3564
|
signOutUrl,
|
|
3351
3565
|
buildMeta,
|
|
3352
3566
|
data,
|
|
3567
|
+
bootProgress,
|
|
3568
|
+
appReady,
|
|
3353
3569
|
children
|
|
3354
3570
|
}) {
|
|
3355
3571
|
const meta = POLITOGY_APPS[app];
|
|
3356
3572
|
const chrome = useChromeState();
|
|
3573
|
+
const bootAppMode = meta.mode;
|
|
3574
|
+
const bootOrgId = chrome?.orgId ?? null;
|
|
3575
|
+
const [bootVisible, setBootVisible] = react.useState(true);
|
|
3576
|
+
const [bootGeneration, setBootGeneration] = react.useState(0);
|
|
3577
|
+
const seenOrgRef = react.useRef(bootOrgId);
|
|
3578
|
+
const seenModeRef = react.useRef(bootAppMode);
|
|
3579
|
+
react.useEffect(() => {
|
|
3580
|
+
if (seenOrgRef.current !== null && seenOrgRef.current !== bootOrgId) {
|
|
3581
|
+
setBootVisible(true);
|
|
3582
|
+
setBootGeneration((g) => g + 1);
|
|
3583
|
+
}
|
|
3584
|
+
seenOrgRef.current = bootOrgId;
|
|
3585
|
+
}, [bootOrgId]);
|
|
3586
|
+
react.useEffect(() => {
|
|
3587
|
+
if (seenModeRef.current !== void 0 && seenModeRef.current !== bootAppMode) {
|
|
3588
|
+
setBootVisible(true);
|
|
3589
|
+
setBootGeneration((g) => g + 1);
|
|
3590
|
+
}
|
|
3591
|
+
seenModeRef.current = bootAppMode;
|
|
3592
|
+
}, [bootAppMode]);
|
|
3593
|
+
const chromeResolved = chrome == null || !chrome.orgsLoading;
|
|
3357
3594
|
const signOutRef = react.useRef(onSignOut);
|
|
3358
3595
|
signOutRef.current = onSignOut;
|
|
3359
3596
|
const signingOutRef = react.useRef(false);
|
|
@@ -3428,70 +3665,82 @@ function FrameBody({
|
|
|
3428
3665
|
[navItems, pathname, search, meta.origin]
|
|
3429
3666
|
);
|
|
3430
3667
|
const breadcrumbs = core ? core.page?.breadcrumbs : grants?.includes("breadcrumbs") ? granted("breadcrumbs") : derivedBreadcrumbs;
|
|
3431
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
badgeLabel: "P",
|
|
3440
|
-
title: "Politogy"
|
|
3441
|
-
},
|
|
3442
|
-
nav: {
|
|
3443
|
-
// A flat rail's sub-menu IS the nav, so the 'tabs only' preference
|
|
3444
|
-
// (which strips children) would leave one childless parent row —
|
|
3445
|
-
// flat apps ignore the stripping.
|
|
3446
|
-
items: navDisplay === "tabs" && meta.navShape !== "flat" ? navItems.map(({ children: _sub, ...item }) => item) : navItems,
|
|
3447
|
-
flatten: meta.navShape === "flat"
|
|
3448
|
-
},
|
|
3449
|
-
// The app's own workspace is NOT the organization — it is a per-app
|
|
3450
|
-
// bootstrap account with a different id in every app. Showing it while
|
|
3451
|
-
// the chrome API resolves flashed the wrong org on every load ("not
|
|
3452
|
-
// connected to the account"). Shimmer while in flight; only fall back
|
|
3453
|
-
// to the app label once the chrome lookup has actually failed.
|
|
3454
|
-
org: activeCoreOrg ? {
|
|
3455
|
-
name: activeCoreOrg.name,
|
|
3456
|
-
id: activeCoreOrg.id,
|
|
3457
|
-
logoUrl: activeCoreOrg.logoUrl,
|
|
3458
|
-
brandColor: activeCoreOrg.brandColor,
|
|
3459
|
-
organizations: coreOrgs,
|
|
3460
|
-
onSwitch: chrome?.setOrgId
|
|
3461
|
-
} : chrome?.orgsLoading ? { name: null, id: null, loading: true } : { name: session.accountName, id: session.accountId },
|
|
3462
|
-
mode,
|
|
3463
|
-
footer,
|
|
3464
|
-
linkComponent
|
|
3668
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3669
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3670
|
+
LoadingScreen,
|
|
3671
|
+
{
|
|
3672
|
+
visible: bootVisible,
|
|
3673
|
+
bootProgress,
|
|
3674
|
+
syntheticReady: appReady ?? chromeResolved,
|
|
3675
|
+
onExitComplete: () => setBootVisible(false)
|
|
3465
3676
|
},
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3677
|
+
bootGeneration
|
|
3678
|
+
),
|
|
3679
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3680
|
+
NavAppShell,
|
|
3681
|
+
{
|
|
3682
|
+
sidebar: {
|
|
3683
|
+
pathname,
|
|
3684
|
+
brand: {
|
|
3685
|
+
logoSrc: core?.brandLogoSrc ?? modeBrandLogo(appMode),
|
|
3686
|
+
logoAlt: "Politogy",
|
|
3687
|
+
badgeLabel: "P",
|
|
3688
|
+
title: "Politogy"
|
|
3689
|
+
},
|
|
3690
|
+
nav: {
|
|
3691
|
+
// A flat rail's sub-menu IS the nav, so the 'tabs only' preference
|
|
3692
|
+
// (which strips children) would leave one childless parent row —
|
|
3693
|
+
// flat apps ignore the stripping.
|
|
3694
|
+
items: navDisplay === "tabs" && meta.navShape !== "flat" ? navItems.map(({ children: _sub, ...item }) => item) : navItems,
|
|
3695
|
+
flatten: meta.navShape === "flat"
|
|
3696
|
+
},
|
|
3697
|
+
// The app's own workspace is NOT the organization — it is a per-app
|
|
3698
|
+
// bootstrap account with a different id in every app. Showing it while
|
|
3699
|
+
// the chrome API resolves flashed the wrong org on every load ("not
|
|
3700
|
+
// connected to the account"). Shimmer while in flight; only fall back
|
|
3701
|
+
// to the app label once the chrome lookup has actually failed.
|
|
3702
|
+
org: activeCoreOrg ? {
|
|
3703
|
+
name: activeCoreOrg.name,
|
|
3704
|
+
id: activeCoreOrg.id,
|
|
3705
|
+
logoUrl: activeCoreOrg.logoUrl,
|
|
3706
|
+
brandColor: activeCoreOrg.brandColor,
|
|
3707
|
+
organizations: coreOrgs,
|
|
3708
|
+
onSwitch: chrome?.setOrgId
|
|
3709
|
+
} : chrome?.orgsLoading ? { name: null, id: null, loading: true } : { name: session.accountName, id: session.accountId },
|
|
3710
|
+
mode,
|
|
3711
|
+
footer,
|
|
3712
|
+
linkComponent
|
|
3481
3713
|
},
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
core?.page?.
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3714
|
+
betaBanner: meta.beta === false ? void 0 : {},
|
|
3715
|
+
modeId: appMode,
|
|
3716
|
+
contentWrapper: core?.contentWrapper ?? granted("contentWrapper"),
|
|
3717
|
+
topBar: {
|
|
3718
|
+
// Core's pages publish their own identity; every other app is one
|
|
3719
|
+
// product surface and takes its registry title.
|
|
3720
|
+
title: core?.page?.title ?? (grants?.includes("title") ? data?.frame?.title : void 0) ?? meta.title,
|
|
3721
|
+
breadcrumbs,
|
|
3722
|
+
user: {
|
|
3723
|
+
name: identity.userName,
|
|
3724
|
+
email: identity.email,
|
|
3725
|
+
// The chrome API's avatar wins; the app may supply a fallback chain
|
|
3726
|
+
// (e.g. a stored memoji) for users with no uploaded photo.
|
|
3727
|
+
avatarUrl: identity.avatarUrl ?? data?.fallbackAvatarUrl ?? null,
|
|
3728
|
+
role: session.role
|
|
3729
|
+
},
|
|
3730
|
+
userMenu,
|
|
3731
|
+
onSignOut: handleSignOut,
|
|
3732
|
+
// The action cluster is IDENTICAL everywhere — core included. Page
|
|
3733
|
+
// actions sit BEFORE it, never inside it, so the Alerts bell keeps its
|
|
3734
|
+
// required position: farthest right, immediately before My Account.
|
|
3735
|
+
actions: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3736
|
+
core?.page?.actions,
|
|
3737
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChromeTopBarActions, {})
|
|
3738
|
+
] })
|
|
3739
|
+
},
|
|
3740
|
+
children
|
|
3741
|
+
}
|
|
3742
|
+
)
|
|
3743
|
+
] });
|
|
3495
3744
|
}
|
|
3496
3745
|
var standardNavIcons = {
|
|
3497
3746
|
home: fi.FiHome,
|
|
@@ -3969,6 +4218,7 @@ function useChromeMode(arg = {}) {
|
|
|
3969
4218
|
}
|
|
3970
4219
|
|
|
3971
4220
|
exports.ChromeTopBarActions = ChromeTopBarActions;
|
|
4221
|
+
exports.LoadingScreen = LoadingScreen;
|
|
3972
4222
|
exports.POLITOGY_APPS = POLITOGY_APPS;
|
|
3973
4223
|
exports.PolitogyAppChrome = PolitogyAppChrome;
|
|
3974
4224
|
exports.PolitogyAppFrame = PolitogyAppFrame;
|