@the-portland-company/shell 0.3.1 → 0.3.4
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 +27 -0
- package/README.md +198 -27
- package/dist/css-modules.d.ts +6 -0
- package/dist/index.cjs +14 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +14 -8
- package/dist/index.js.map +1 -1
- package/package.json +10 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.4 — 2026-05-14
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- `package.json` `exports` for `./css` and `./chrome.css` now ship a `types` entry pointing at a tiny ambient module declaration (`dist/css-modules.d.ts`). Fixes TS2882 errors in fresh consumers importing the side-effect CSS subpaths.
|
|
8
|
+
|
|
9
|
+
### Documentation
|
|
10
|
+
|
|
11
|
+
- README install command now pins peer-dep versions explicitly (`react@^18`, `@chakra-ui/react@^2.8`, etc.) so Vite-scaffolded apps on React 19 / Chakra v3 don't silently install incompatible versions.
|
|
12
|
+
- Clarified `@the-portland-company/devnotes` is optional.
|
|
13
|
+
|
|
14
|
+
## 0.3.3 — 2026-05-14
|
|
15
|
+
|
|
16
|
+
### Documentation
|
|
17
|
+
|
|
18
|
+
- Rewrote `README.md` to cover the v0.3.x chrome contract: every component / hook / helper / type, the `<ShellChrome>` binding pattern with a copy-pasteable bridge example, the `chrome.css` + `css` import paths, multi-SPA routing notes for the politogy edge router, and a pointer to `politogy-vrm/react/app/src/providers/ShellChromeBridge.tsx` as the reference implementation. No code changes.
|
|
19
|
+
|
|
20
|
+
## 0.3.2 — 2026-05-13
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- `<ShellChrome userAvatarUrl />` (and matching `<ShellProvider userAvatarUrl />` prop) — app-supplied avatar URL override for the header `<UserMenu />` avatar. When provided, takes precedence over the avatar URL on the Supabase session's `user_metadata.avatar_url`.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- `<UserMenu />` was rendering only `user.raw.user_metadata.avatar_url`, which missed app-specific avatar sources like `profile.avatar_url`, memoji selections persisted to localStorage, and branded default URLs. Apps with these fallback chains were seeing the initials placeholder instead of the user's portrait. Pass the resolved URL via `userAvatarUrl` to restore parity with the legacy chrome.
|
|
29
|
+
|
|
3
30
|
## 0.3.1 — 2026-05-13
|
|
4
31
|
|
|
5
32
|
Closes the last two visual gaps in the shell-v3 chrome vs production.
|
package/README.md
CHANGED
|
@@ -2,47 +2,203 @@
|
|
|
2
2
|
|
|
3
3
|
Shared chrome for politogy apps. One install, one wrapper, identical look.
|
|
4
4
|
|
|
5
|
+
Every politogy product (Email Blast, Contacts, Forms, future apps…) installs this package and gets the full branded experience — sidebar with VRM logo, organization switcher, mode selector, full nav list, header with notifications and support, branded footer with commit info — without rebuilding any of it. Path-based routing lets multiple separately-deployed Vite SPAs feel like a single continuous app on `app.politogy.com`.
|
|
6
|
+
|
|
7
|
+
> **v0.3.x is a major rewrite.** See the [CHANGELOG](./CHANGELOG.md) for the chrome component, hook, and prop additions over v0.2.x. The minimum viable boot below works for new apps; existing v0.2.x consumers can keep using `OrgSelector`, `useShellAuth`, etc. without changes.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
5
11
|
## Install
|
|
6
12
|
|
|
13
|
+
Pin peer dep versions explicitly — Vite scaffolds React 19 + Chakra v3 by default, neither of which the shell supports:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @the-portland-company/shell \
|
|
17
|
+
react@^18 react-dom@^18 \
|
|
18
|
+
@chakra-ui/react@^2.8 @emotion/react@^11 @emotion/styled@^11 \
|
|
19
|
+
framer-motion@^11 \
|
|
20
|
+
@supabase/supabase-js@~2.95 \
|
|
21
|
+
react-router-dom@^6 react-icons@^5
|
|
7
22
|
```
|
|
8
|
-
npm install @the-portland-company/shell @chakra-ui/react @emotion/react @emotion/styled @supabase/supabase-js react react-dom
|
|
9
|
-
```
|
|
10
23
|
|
|
11
|
-
|
|
24
|
+
`@the-portland-company/devnotes` is **optional** — only needed if you want the dev-notes menu in the header. Skip it if your app doesn't use it.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Minimum viable boot
|
|
29
|
+
|
|
30
|
+
The simplest possible startup renders the branded chrome with empty defaults — the sidebar appears, the header renders, but the org switcher / mode selector / nav list show "no data" states until you wire them up via `<ShellChrome>` (next section).
|
|
12
31
|
|
|
13
32
|
```tsx
|
|
14
|
-
|
|
15
|
-
import '
|
|
33
|
+
// src/main.tsx
|
|
34
|
+
import React from 'react'
|
|
35
|
+
import ReactDOM from 'react-dom/client'
|
|
36
|
+
import { BrowserRouter } from 'react-router-dom'
|
|
37
|
+
import { ShellProvider, AppLayout } from '@the-portland-company/shell'
|
|
38
|
+
import '@the-portland-company/shell/chrome.css'
|
|
39
|
+
import { createClient } from '@supabase/supabase-js'
|
|
40
|
+
import App from './App'
|
|
41
|
+
|
|
42
|
+
const supabase = createClient(
|
|
43
|
+
import.meta.env.VITE_SUPABASE_URL,
|
|
44
|
+
import.meta.env.VITE_SUPABASE_ANON_KEY,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
48
|
+
<ShellProvider currentApp="email" supabaseClient={supabase}>
|
|
49
|
+
<BrowserRouter>
|
|
50
|
+
<AppLayout>
|
|
51
|
+
<App />
|
|
52
|
+
</AppLayout>
|
|
53
|
+
</BrowserRouter>
|
|
54
|
+
</ShellProvider>,
|
|
55
|
+
)
|
|
56
|
+
```
|
|
16
57
|
|
|
17
|
-
|
|
58
|
+
`<AppLayout>` composes the shell `<Header>`, `<Sidebar>`, your routed content, and `<Footer>`. `<ShellProvider>` owns the Supabase auth lifecycle and exposes `useShellAuth()` for status / user / sign-out.
|
|
18
59
|
|
|
19
|
-
|
|
20
|
-
{ id: 'crm', label: 'CRM', path: '/' },
|
|
21
|
-
{ id: 'messaging', label: 'Messaging', path: '/messaging' },
|
|
22
|
-
]
|
|
60
|
+
---
|
|
23
61
|
|
|
24
|
-
|
|
62
|
+
## Real-world boot: feeding the chrome real data
|
|
63
|
+
|
|
64
|
+
Most apps have an `<AuthProvider>`, `<OrganizationProvider>`, `<ModeProvider>`, etc. that live *below* `<ShellProvider>` in the tree. To push their data up into the shell's chrome contexts, drop a `<ShellChrome>` binding component as deep in the tree as the data is available:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import {
|
|
68
|
+
ShellProvider,
|
|
69
|
+
ShellChrome,
|
|
70
|
+
AppLayout,
|
|
71
|
+
} from '@the-portland-company/shell'
|
|
72
|
+
|
|
73
|
+
function ChromeBridge({ children }) {
|
|
74
|
+
const { currentMode, switchMode, getModeConfig, getAvailableModes } = useMode()
|
|
75
|
+
const { currentOrg, organizations, switchOrganization } = useOrganization()
|
|
76
|
+
const { linkedAccounts, ... } = useLinkedAccounts()
|
|
77
|
+
// …compute the nav-items list for the active mode here…
|
|
25
78
|
return (
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
79
|
+
<ShellChrome
|
|
80
|
+
mode={{ currentMode, modes: getAvailableModes(), switchMode, getModeConfig }}
|
|
81
|
+
organization={{ currentOrg, organizations, switchOrganization }}
|
|
82
|
+
linkedAccounts={{ linkedAccounts, switchToAccount, onAddAccount, onRemoveAccount }}
|
|
83
|
+
navItems={navItems}
|
|
84
|
+
role={{ activeRoleConfig, isSuperAdmin }}
|
|
85
|
+
footer={{ links, commit, version, releaseName, environmentLabel }}
|
|
86
|
+
support={{ submitSupportRequest }}
|
|
87
|
+
brandLogoSrc="/brand/logos/email-mode-logo.png"
|
|
88
|
+
headerIconsSlot={<MyAppSpecificIcons />}
|
|
89
|
+
userAvatarUrl={profile.avatar_url ?? storedMemojiUrl ?? defaultAvatarUrl}
|
|
90
|
+
myProfileHref="/my-profile"
|
|
91
|
+
organizationSettingsHref={`/organization-settings?org=${currentOrg.id}`}
|
|
92
|
+
>
|
|
93
|
+
{children}
|
|
94
|
+
</ShellChrome>
|
|
29
95
|
)
|
|
30
96
|
}
|
|
97
|
+
|
|
98
|
+
// Then in main.tsx:
|
|
99
|
+
<ShellProvider currentApp="email" supabaseClient={supabase}>
|
|
100
|
+
<BrowserRouter>
|
|
101
|
+
<AuthProvider>
|
|
102
|
+
<OrganizationProvider>
|
|
103
|
+
<ModeProvider>
|
|
104
|
+
<ChromeBridge>
|
|
105
|
+
<AppLayout>
|
|
106
|
+
<Routes>…</Routes>
|
|
107
|
+
</AppLayout>
|
|
108
|
+
</ChromeBridge>
|
|
109
|
+
</ModeProvider>
|
|
110
|
+
</OrganizationProvider>
|
|
111
|
+
</AuthProvider>
|
|
112
|
+
</BrowserRouter>
|
|
113
|
+
</ShellProvider>
|
|
31
114
|
```
|
|
32
115
|
|
|
33
|
-
|
|
116
|
+
**Reference implementation:** `politogy-vrm`'s `react/app/src/providers/ShellChromeBridge.tsx` (~600 lines, every chrome wiring pattern). Copy as a starting point for new apps.
|
|
117
|
+
|
|
118
|
+
All `<ShellChrome>` props are optional — only supply what your app has. The chrome gracefully omits the bits it doesn't get data for (e.g., no `support` prop → no support icon rendered).
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## What the shell exports
|
|
123
|
+
|
|
124
|
+
### Components
|
|
125
|
+
|
|
126
|
+
| Export | Use |
|
|
127
|
+
|---|---|
|
|
128
|
+
| `<ShellProvider>` | Top-level provider. Wraps Chakra + auth + meta contexts. |
|
|
129
|
+
| `<ShellChrome>` | Mid-tree binding component. Feeds chrome contexts from app data. |
|
|
130
|
+
| `<AppLayout>` | Composes Header + Sidebar + main content + Footer. |
|
|
131
|
+
| `<Header>` | Header bar (logo, page title, breadcrumbs, icon stack, user menu). |
|
|
132
|
+
| `<Sidebar>` | Sidebar (branded logo, org pill, mode pill, nav list, footer). |
|
|
133
|
+
| `<Footer>` | Below main content. Usually not rendered separately. |
|
|
134
|
+
| `<UserMenu>` | Avatar dropdown (profile, theme, sign out, switch account). |
|
|
135
|
+
| `<OrgPill>` (alias `<OrgSelector>`) | Organization switcher pill rendered in the sidebar. |
|
|
136
|
+
| `<ModePill>` | Mode (Relationship / Campaign / Petition) switcher. |
|
|
137
|
+
| `<NavItem>` | Single sidebar nav row with active state + hover gradient. |
|
|
138
|
+
| `<SupportRequestButton>` | Header life-buoy icon that opens a support modal. |
|
|
139
|
+
| `<BrandLogo>` | `<img>` wrapper for the branded mode logo. |
|
|
140
|
+
| `<BrandIcon name="...">` | Renders `/brand/icons/<name>.svg` from your `public/`. |
|
|
141
|
+
| `<CommitCopyButton>` | Footer commit/version display with copy-to-clipboard. |
|
|
142
|
+
| `<Card>`, `<PageContainer>`, `<PageHeader>`, `<BreadcrumbBar>`, `<HeaderTabBar>` | Page-level layout primitives. |
|
|
143
|
+
| `<GlobalUiIdProvider>` | Assigns stable UI IDs (for testing/analytics). |
|
|
144
|
+
|
|
145
|
+
### Hooks
|
|
146
|
+
|
|
147
|
+
| Hook | Returns |
|
|
148
|
+
|---|---|
|
|
149
|
+
| `useShellAuth()` | `{ status, user, session, signOut }` |
|
|
150
|
+
| `useShellOrganization()` | `{ currentOrg, organizations, switchOrganization, createOrgHref } \| null` |
|
|
151
|
+
| `useShellMode()` | `{ currentMode, modes, switchMode, getModeConfig } \| null` |
|
|
152
|
+
| `useShellNavItems()` | `{ items: ShellNavItem[] } \| null` |
|
|
153
|
+
| `useShellLinkedAccounts()` | `{ linkedAccounts, switchToAccount, onAddAccount, onRemoveAccount } \| null` |
|
|
154
|
+
| `useShellSupport()` | `{ submitSupportRequest } \| null` |
|
|
155
|
+
| `useShellRole()` | `{ activeRoleConfig, isSuperAdmin } \| null` |
|
|
156
|
+
| `useShellFooter()` | `{ links, version, commit, releaseName, … } \| null` |
|
|
157
|
+
| `useShellNavigationPreference()` | `{ showSubMenu } \| null` |
|
|
158
|
+
| `useShellChrome()` | Chrome configuration (`devNotesMenu`, `myProfileHref`, `userAvatarUrl`, `pageTitle`, `breadcrumbs`, `headerActions`, etc.) |
|
|
159
|
+
| `useAppRegistry()`, `useCurrentApp()` | App switcher metadata for cross-app speculation rules. |
|
|
160
|
+
|
|
161
|
+
### Helpers
|
|
34
162
|
|
|
35
|
-
|
|
36
|
-
|---|---|---|
|
|
37
|
-
| `headerExtrasSlot` | Right of header, before user menu | App-specific header widgets |
|
|
38
|
-
| `modalsSlot` | Above main content, position absolute | Modal dialogs |
|
|
39
|
-
| `notificationsSlot` | Inside main, top-right | Toasts, badges |
|
|
40
|
-
| `sidebarFooterSlot` | Bottom of sidebar | Org switcher, status |
|
|
41
|
-
| `overlaySlot` | Top of everything | Always-on-top indicators |
|
|
163
|
+
`normalizeHexColor`, `lightenHexColor`, `hexToRgba`, `getOrganizationBrandColor`, `getOrganizationBadgeLetters`, `getUserDisplayName`.
|
|
42
164
|
|
|
43
|
-
|
|
165
|
+
### Theme
|
|
44
166
|
|
|
45
|
-
|
|
167
|
+
`politogyTheme` — Chakra theme with brand palette, mode-aware semantic tokens, and component overrides.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## CSS imports
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
import '@the-portland-company/shell/chrome.css' // :root design tokens (--app-color-brand-*, --mode-*, --app-tab-*, --form-control-*)
|
|
175
|
+
import '@the-portland-company/shell/css' // view-transitions CSS for cross-app polish
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`chrome.css` is required for theme variables. `css` (view-transitions) is optional polish.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Multi-SPA routing on `app.politogy.com`
|
|
183
|
+
|
|
184
|
+
Multiple politogy apps live behind one domain via a Cloudflare Worker edge router (in `packages/edge-router` of the monorepo).
|
|
185
|
+
|
|
186
|
+
When you deploy a new app to Cloudflare Pages, add its path prefix to the edge router's `routes.ts`:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
{
|
|
190
|
+
pathPrefix: '/email',
|
|
191
|
+
origin: 'https://politogy-email.pages.dev',
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
After deploy, users hitting `app.politogy.com/email/*` get transparently proxied to your app. Other paths keep going to whichever app owns them. The shared shell + same Supabase session means it feels like one app.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Service worker (optional polish)
|
|
200
|
+
|
|
201
|
+
The package ships a precache service worker that caches the shell chrome on first visit so cross-app navigation feels instant.
|
|
46
202
|
|
|
47
203
|
```js
|
|
48
204
|
// scripts/copy-shell-sw.mjs
|
|
@@ -53,11 +209,13 @@ await copyFile(
|
|
|
53
209
|
)
|
|
54
210
|
```
|
|
55
211
|
|
|
56
|
-
|
|
212
|
+
Wire it into your build (`"postinstall": "node scripts/copy-shell-sw.mjs"`) and call `registerShellPrecache()` once on app boot.
|
|
213
|
+
|
|
214
|
+
---
|
|
57
215
|
|
|
58
|
-
## Speculation Rules
|
|
216
|
+
## Speculation Rules (optional polish)
|
|
59
217
|
|
|
60
|
-
|
|
218
|
+
Cross-app prerendering hints for the browser:
|
|
61
219
|
|
|
62
220
|
```tsx
|
|
63
221
|
import { useAppRegistry, useCurrentApp, buildSpeculationRules } from '@the-portland-company/shell'
|
|
@@ -70,3 +228,16 @@ function SpeculationRules() {
|
|
|
70
228
|
return <script type="speculationrules" dangerouslySetInnerHTML={{ __html: json }} />
|
|
71
229
|
}
|
|
72
230
|
```
|
|
231
|
+
|
|
232
|
+
Render once near the app root.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Reference + support
|
|
237
|
+
|
|
238
|
+
- **Reference bridge:** `politogy-vrm/react/app/src/providers/ShellChromeBridge.tsx` — full chrome wiring for the main politogy app.
|
|
239
|
+
- **Architectural decisions, gotchas, session log:** `politogy-vrm/docs/superpowers/`.
|
|
240
|
+
- **Type definitions:** `node_modules/@the-portland-company/shell/dist/index.d.ts` — every prop and hook is fully typed.
|
|
241
|
+
- **CHANGELOG:** [CHANGELOG.md](./CHANGELOG.md) — every export added per version.
|
|
242
|
+
|
|
243
|
+
Questions or missing features: file an issue or ping the politogy team.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Side-effect type declarations for the CSS subpath exports so TS consumers
|
|
2
|
+
// can `import '@the-portland-company/shell/css'` and
|
|
3
|
+
// `import '@the-portland-company/shell/chrome.css'` without ambient-shim
|
|
4
|
+
// boilerplate.
|
|
5
|
+
declare module '@the-portland-company/shell/css'
|
|
6
|
+
declare module '@the-portland-company/shell/chrome.css'
|
package/dist/index.cjs
CHANGED
|
@@ -1153,7 +1153,8 @@ function ShellProvider({
|
|
|
1153
1153
|
linkedAccounts,
|
|
1154
1154
|
support,
|
|
1155
1155
|
devNotesMenu,
|
|
1156
|
-
myProfileHref
|
|
1156
|
+
myProfileHref,
|
|
1157
|
+
userAvatarUrl
|
|
1157
1158
|
}) {
|
|
1158
1159
|
const [status, setStatus] = react$1.useState("loading");
|
|
1159
1160
|
const [session, setSession] = react$1.useState(null);
|
|
@@ -1195,9 +1196,10 @@ function ShellProvider({
|
|
|
1195
1196
|
const chrome = react$1.useMemo(
|
|
1196
1197
|
() => ({
|
|
1197
1198
|
devNotesMenu: devNotesMenu ?? null,
|
|
1198
|
-
myProfileHref: myProfileHref ?? "/my-profile"
|
|
1199
|
+
myProfileHref: myProfileHref ?? "/my-profile",
|
|
1200
|
+
userAvatarUrl
|
|
1199
1201
|
}),
|
|
1200
|
-
[devNotesMenu, myProfileHref]
|
|
1202
|
+
[devNotesMenu, myProfileHref, userAvatarUrl]
|
|
1201
1203
|
);
|
|
1202
1204
|
return /* @__PURE__ */ jsxRuntime.jsx(ShellAuthContext.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsx(ShellMetaContext.Provider, { value: meta, children: /* @__PURE__ */ jsxRuntime.jsx(ShellOrgContext.Provider, { value: organization ?? null, children: /* @__PURE__ */ jsxRuntime.jsx(ShellLinkedAccountsContext.Provider, { value: linkedAccounts ?? null, children: /* @__PURE__ */ jsxRuntime.jsx(ShellSupportProviderContext.Provider, { value: support ?? null, children: /* @__PURE__ */ jsxRuntime.jsx(ShellModeContext.Provider, { value: null, children: /* @__PURE__ */ jsxRuntime.jsx(ShellNavItemsContext.Provider, { value: null, children: /* @__PURE__ */ jsxRuntime.jsx(ShellNavigationPreferenceContext.Provider, { value: null, children: /* @__PURE__ */ jsxRuntime.jsx(ShellRoleContext.Provider, { value: null, children: /* @__PURE__ */ jsxRuntime.jsx(ShellFooterContext.Provider, { value: null, children: /* @__PURE__ */ jsxRuntime.jsx(ShellChromeContext.Provider, { value: chrome, children: /* @__PURE__ */ jsxRuntime.jsx(react.ChakraProvider, { theme: politogyTheme, children }) }) }) }) }) }) }) }) }) }) }) });
|
|
1203
1205
|
}
|
|
@@ -1284,7 +1286,8 @@ function UserMenu() {
|
|
|
1284
1286
|
myProfileHref,
|
|
1285
1287
|
organizationSettingsHref,
|
|
1286
1288
|
showContinueOnboarding,
|
|
1287
|
-
onContinueOnboarding
|
|
1289
|
+
onContinueOnboarding,
|
|
1290
|
+
userAvatarUrl
|
|
1288
1291
|
} = useShellChrome();
|
|
1289
1292
|
const linked = useShellLinkedAccounts();
|
|
1290
1293
|
const mode = useShellMode();
|
|
@@ -1295,7 +1298,7 @@ function UserMenu() {
|
|
|
1295
1298
|
const menuListBorderColor = react.useColorModeValue("gray.200", "gray.700");
|
|
1296
1299
|
if (status !== "authenticated" || !user) return null;
|
|
1297
1300
|
const displayName = getUserDisplayName(user.raw);
|
|
1298
|
-
const avatarUrl = avatarUrlFromUser(user);
|
|
1301
|
+
const avatarUrl = userAvatarUrl ?? avatarUrlFromUser(user);
|
|
1299
1302
|
const modeConfig = mode?.getModeConfig() ?? null;
|
|
1300
1303
|
const currentOrg = org?.currentOrg ?? null;
|
|
1301
1304
|
const linkedAccounts = linked?.linkedAccounts ?? [];
|
|
@@ -2911,7 +2914,8 @@ function ShellChrome({
|
|
|
2911
2914
|
onContinueOnboarding,
|
|
2912
2915
|
pageTitle,
|
|
2913
2916
|
breadcrumbs,
|
|
2914
|
-
headerActions
|
|
2917
|
+
headerActions,
|
|
2918
|
+
userAvatarUrl
|
|
2915
2919
|
}) {
|
|
2916
2920
|
const chrome = react$1.useMemo(
|
|
2917
2921
|
() => ({
|
|
@@ -2924,7 +2928,8 @@ function ShellChrome({
|
|
|
2924
2928
|
onContinueOnboarding,
|
|
2925
2929
|
pageTitle,
|
|
2926
2930
|
breadcrumbs,
|
|
2927
|
-
headerActions
|
|
2931
|
+
headerActions,
|
|
2932
|
+
userAvatarUrl
|
|
2928
2933
|
}),
|
|
2929
2934
|
[
|
|
2930
2935
|
devNotesMenu,
|
|
@@ -2936,7 +2941,8 @@ function ShellChrome({
|
|
|
2936
2941
|
onContinueOnboarding,
|
|
2937
2942
|
pageTitle,
|
|
2938
2943
|
breadcrumbs,
|
|
2939
|
-
headerActions
|
|
2944
|
+
headerActions,
|
|
2945
|
+
userAvatarUrl
|
|
2940
2946
|
]
|
|
2941
2947
|
);
|
|
2942
2948
|
const navItemsValue = react$1.useMemo(
|