@artstorefronts/arthelper-nav 0.1.0-alpha.1 → 0.1.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,13 +7,172 @@ This document is for an engineer wiring the bar into a host app that lives **out
7
7
  this monorepo. If you're working inside this repo, see `docs/llms/suite-nav.md` for how
8
8
  the pieces fit together end to end.
9
9
 
10
+ ## Agent implementation prompt
11
+
12
+ Copy the block below and hand it to a coding agent working in the host app. It is written
13
+ to be self-contained apart from this README, which it tells the agent to read — the agent
14
+ will have it locally once the package is installed.
15
+
16
+ ````markdown
17
+ # Task: mount the ArtHelper suite nav bar in this app
18
+
19
+ This app is part of the ArtHelper suite. Add the shared suite bar so the suite reads as one
20
+ product. The bar is a published package — you are wiring it in, not building it.
21
+
22
+ ## First
23
+
24
+ ```bash
25
+ npm install @artstorefronts/arthelper-nav
26
+ ```
27
+
28
+ Then read `node_modules/@artstorefronts/arthelper-nav/README.md` in full before writing any
29
+ code. It is the authoritative API reference and explains the layout contract, the collapsed
30
+ cookie, persistence and degradation behaviour. Peer dependencies are `react`, `react-dom`
31
+ and `lucide-react` — install them yourself if this app doesn't already have them.
32
+
33
+ ## Find this app's catalog id
34
+
35
+ `activeAppId` must be the id the shared catalog uses for this app — not this repo's name.
36
+ List them:
37
+
38
+ ```bash
39
+ curl -s https://api.arthelper.ai/rest/nav-config | jq -r '.apps[].id'
40
+ ```
41
+
42
+ Pick the one that is this app and use it verbatim. A wrong value means the bar cannot
43
+ indicate where the user is and the collapse control has no chip to dock. If none of the ids
44
+ is obviously this app, stop and ask — an id is added by an operator, not by you.
45
+
46
+ ## Wire it in
47
+
48
+ 1. **Import the stylesheet** by deep path in your global CSS entry:
49
+ `@import '@artstorefronts/arthelper-nav/dist/index.css';`
50
+ Under Tailwind v4 every `@import` must precede other rules. The CSS is scoped with an
51
+ `ahsuite-` prefix and uses no Tailwind utilities, so it needs no `@source` entry and
52
+ cannot be purged.
53
+
54
+ 2. **Wrap the app root in `NavProvider`**, passing `configUrl` and `activeAppId`.
55
+
56
+ 3. **Render `NavBar` as the first child of the app shell, in normal document flow.** It
57
+ occupies real layout space; the page should shift down exactly once, at first paint.
58
+ Never wrap it in a fixed/absolute container and never portal it.
59
+
60
+ 4. **Put `NavTrigger` in this app's own header, beside its logo.** This is not optional:
61
+ when the bar is collapsed, `NavTrigger` is the only way to restore it. Ship without it
62
+ and a user who collapses the bar can never get it back. It hides itself while the bar is
63
+ expanded, so you just place it.
64
+
65
+ 5. **If this app server-renders**, read the collapsed cookie on the server and pass it as
66
+ `initialCollapsed`. Import the cookie's name from the package rather than retyping it.
67
+ Without this, a user who collapsed the bar gets an expanded bar in the first byte and a
68
+ 56px jump after hydration.
69
+
70
+ 6. **Optionally pass `onAppClick`** to emit your own analytics. The package imports no
71
+ analytics library, and one callback covers both the desktop tiles and the mobile dropdown.
72
+
73
+ ## `configUrl` — the mistake to avoid
74
+
75
+ It is the ArtHelper API's **REST** endpoint, ending `/rest/nav-config`:
76
+
77
+ - production: `https://api.arthelper.ai/rest/nav-config`
78
+ - local: your ArtHelper stack's API port, same path
79
+
80
+ Read it from this app's environment config with a sensible default; do not hardcode one
81
+ value. **It is not the GraphQL endpoint.** Pointing it at `/graphql` returns 404 and the bar
82
+ falls back _silently_ to its compiled-in catalog — no console error, no visible failure,
83
+ just a bar that never reflects the admin screen. If tiles never update, curl the URL before
84
+ debugging anything else.
85
+
86
+ The endpoint is public, unauthenticated and CORS-open. **Do not send credentials** — the
87
+ response carries a literal `Access-Control-Allow-Origin: *`, which browsers reject for
88
+ credentialed requests.
89
+
90
+ ## Sticky, and the audit that goes with it
91
+
92
+ Decide whether the bar is sticky:
93
+
94
+ - **This app's document scrolls** (a normal page): use `<NavBar sticky />`, and offset your
95
+ own sticky header's `top` by the bar's height. **That offset must react to collapse** —
96
+ read `collapsed` from `useNav()` and use `collapsed ? 0 : <bar height>`. A constant offset
97
+ leaves a visible void at scroll-top once the bar collapses.
98
+ - **The shell is a fixed-height flex column** that never scrolls the document: do not pass
99
+ `sticky` and change no offsets.
100
+
101
+ **Then audit the whole app for surfaces that assume the header's bottom edge, or that they
102
+ can occupy the full viewport height.** Grep for `top-14`, `top: 3.5rem`, `h-screen`,
103
+ `h-svh`, `h-dvh`, `100vh`, `100svh`, `100dvh`, and `calc(100vh - N)` patterns. Each one is
104
+ now short by the bar's height while the bar is expanded, and will sit _behind_ your header
105
+ or run off the bottom of the screen.
106
+
107
+ This is the single largest source of breakage when adopting the bar. The two apps inside the
108
+ ArtHelper monorepo needed **nine** such fixes between them, including a `position: fixed`
109
+ sidebar rail, two pages subtracting a hardcoded pixel count, and an overlay panel rendered
110
+ through a portal that could not inherit a CSS variable at all. Derive them all from one
111
+ shared value rather than sprinkling `collapsed ?` checks through components, and remember
112
+ that a portalled surface may need the value passed to it another way.
113
+
114
+ Judge each hit: a surface inside a scroll container that never touches the viewport edge is
115
+ unaffected, and a modal meant to cover the chrome should keep covering it. Say which you
116
+ changed and which you deliberately did not.
117
+
118
+ ## What is not configurable
119
+
120
+ The bar's **look** — colour, alignment, accent, the wordmark — is compiled into the package,
121
+ not served in the payload. Do not look for theme props or try to restyle it from the host;
122
+ changing it is a package release. Only the app catalog is configurable.
123
+
124
+ ## Expected behaviour, so you don't chase non-bugs
125
+
126
+ - **Only some apps appear.** An app renders only once an operator has both enabled it and
127
+ given it a destination in ArtHelper's admin screen.
128
+ - **This app's own chip always renders**, even if its catalog entry is disabled or has no
129
+ destination. With no destination it is a non-navigating indicator chip. That is correct —
130
+ it tells the user where they are.
131
+ - **The bar survives an endpoint outage** by falling back to a catalog compiled into the
132
+ package, so it renders at full height rather than blank and never blocks first paint.
133
+ - **The catalog is persisted** after the first successful fetch, so repeat loads paint the
134
+ last known catalog rather than the compiled-in one.
135
+
136
+ ## Verify in a real browser, not by reasoning
137
+
138
+ 1. The bar renders above your header; measure its height with `getBoundingClientRect()` and
139
+ confirm it matches the documented height exactly.
140
+ 2. Collapse it: height 0, your header rises to the very top with no gap, chip appears beside
141
+ your logo. Restore it: back to full height.
142
+ 3. Reload while collapsed — it stays collapsed.
143
+ 4. Click another app's tile — the address bar becomes that destination.
144
+ 5. If sticky: scroll a few hundred pixels in both bar states and confirm your header is
145
+ neither covered nor floating above a void.
146
+ 6. Below the `md` breakpoint the tiles become a labelled dropdown; every app is reachable and
147
+ it dismisses without leaving the page.
148
+ 7. Block the config endpoint in devtools — the bar still renders at full height with this
149
+ app's own chip, not blank.
150
+ 8. If this app server-renders, check the console for hydration warnings. There must be none.
151
+
152
+ ## Do not
153
+
154
+ - Do not fork, vendor or patch the package. Report upstream problems instead.
155
+ - Do not hardcode any app's destination — destinations are config.
156
+ - Do not infer `activeAppId` from the URL; the host declares it.
157
+ - Do not send credentials with the config request.
158
+ - Do not render a second bar if this app is ever framed inside another ArtHelper surface.
159
+ - Avoid `yarn link` unless necessary; if you use it, make sure the bundler resolves a single
160
+ copy of React or you will hit the duplicate-React hooks error. `npm pack` plus installing
161
+ the tarball avoids that and resolves more like the real thing.
162
+ ````
163
+
10
164
  ## Architecture in one line
11
165
 
12
- Configure in data, render in code. The bar's content — theme, logo, and the app catalog
13
- — lives in a single database row, served as JSON over a public HTTP endpoint. Every
14
- mounted host fetches that JSON on load. An edit made in the admin editor is live for
15
- every app in the suite on their next page load — no deploy, no version bump, no
16
- coordinated release across repos.
166
+ Configure the catalog in data, ship everything else in code. The **app catalog** lives in
167
+ a single database row, served as JSON over a public HTTP endpoint. Every mounted host
168
+ fetches that JSON on load, so a catalog edit made in the admin editor is live for every
169
+ app in the suite on their next page load — no deploy, no version bump, no coordinated
170
+ release across repos.
171
+
172
+ The payload is `{ schema_version, apps }` and nothing else. The bar's look and its
173
+ wordmark are compiled in, on purpose — see [Theming and the wordmark](#theming-and-the-wordmark).
174
+ The last good catalog is persisted per browser so a return visit paints it rather than the
175
+ compiled-in fallback — see [Persistence](#persistence).
17
176
 
18
177
  ## Install
19
178
 
@@ -101,7 +260,7 @@ wrap `NavBar`, `NavTrigger`, and any component calling `useNav()`.
101
260
 
102
261
  ### `NavBar`
103
262
 
104
- Renders the bar itself: waffle toggle, wordmark/logo, the desktop tile row (or, below the
263
+ Renders the bar itself: waffle toggle, wordmark, the desktop tile row (or, below the
105
264
  768px breakpoint, a labelled dropdown trigger and panel with the same entries as rows).
106
265
 
107
266
  | Prop | Type | Default | Description |
@@ -123,9 +282,8 @@ mounting `NavTrigger` ships a bar a viewer can collapse and never recover.
123
282
 
124
283
  ### Exported types
125
284
 
126
- `NavContextValue`, `NavBarProps`, `NavTriggerProps`, `NavProviderProps`, `NavAlignment`,
127
- `NavApp`, `NavAppClickArgs`, `NavConfigPayload`, `NavLogo`, `NavTheme`, `NavThemeMode`,
128
- `ResolvedNavApp` — all exported from the package root (`src/index.ts`), also useful if
285
+ `NavContextValue`, `NavBarProps`, `NavTriggerProps`, `NavProviderProps`, `NavApp`,
286
+ `NavAppClickArgs`, `NavConfigPayload`, `ResolvedNavApp` — all exported from the package root (`src/index.ts`), also useful if
129
287
  you're typing `fallbackConfig` or `onAppClick` yourself.
130
288
 
131
289
  `useNav()` is also exported, for a component nested under `NavProvider` that needs
@@ -206,12 +364,59 @@ The cookie is **per-origin**. `app.example.com` and `www.example.com` are differ
206
364
  registrable domains and share no cookie scope, so collapsing the bar on one origin does
207
365
  not sync to another — each origin's viewer sets their own collapsed state independently.
208
366
 
209
- ## Theming
210
-
211
- `theme.mode` (`'dark' | 'light'`), `theme.accent_color`, and `theme.alignment`
212
- (`'left' | 'centered'`) all come from the config payload, set by an admin in the nav
213
- editor. Changing any of them is a config edit, not a package release or a host
214
- deployment — every mounted app picks it up on its next fetch.
367
+ ## Theming and the wordmark
368
+
369
+ There is no theming, deliberately. The bar ships one look — dark "Forest", tiles centred
370
+ on the bar — baked into `dist/index.css`, and the wordmark (`arthelper`, linking to
371
+ `https://www.arthelper.com`) is baked into the component. No part of either can be set by
372
+ a host or by the payload.
373
+
374
+ Both used to be configurable: the payload carried a `theme` block (`mode`,
375
+ `accent_color`, `alignment`) and a `logo` block (`label`, `href`) that an admin edited in
376
+ the nav editor. That was a mistake, and the reason generalizes to every configurable
377
+ field. The bar paints the package's compiled-in fallback before the fetch resolves and
378
+ swaps in the served config afterwards, so **any** configured value that disagrees with the
379
+ fallback's guess produces a visible change on every page load — a dark bar turning light,
380
+ tiles jumping from left to centre, a wordmark rewriting itself. Colour, layout and the
381
+ wordmark are release decisions, reviewed once and then stable. The app catalog is the one
382
+ genuine operational switch, and it is all the payload carries now.
383
+
384
+ The wordmark's href is the site root, not the resolved `home` catalog entry. Deriving it
385
+ was considered and rejected: it would put a static piece of chrome back on config, and
386
+ `home` points at `/community` while the wordmark has always gone to the root.
387
+
388
+ Changing the look or the wordmark now means a package release. A host that wants the bar
389
+ on differently-coloured chrome should change its own surrounding chrome, not the bar.
390
+
391
+ A payload that still carries `theme` or `logo` has them dropped during normalization like
392
+ any other unrecognized field, and an older renderer that still expects them degrades to
393
+ its own compiled-in defaults rather than throwing.
394
+
395
+ ## Persistence
396
+
397
+ The catalog is the one field that can still differ from the compiled-in fallback, and
398
+ because the tile row is centred on the bar, that difference is expensive: every tile the
399
+ fetch adds or removes shifts the whole row by half a tile, so a two-tile fallback resolving
400
+ to a seven-tile config moves the Home tile 230px.
401
+
402
+ So a successful fetch persists the normalized payload to `localStorage` under
403
+ `ahsuite-nav-config-v1`, and on mount the bar prefers that over the compiled-in fallback.
404
+ The fallback becomes the first-ever-visit case.
405
+
406
+ Things worth knowing if you are wiring this into a host:
407
+
408
+ - **The persisted blob is re-validated on read**, through the same `normalize` a network
409
+ body goes through. A blob written months ago by an older renderer is untrusted input.
410
+ - **Entries expire after 30 days**, so a long-dormant browser revalidates rather than
411
+ painting something ancient.
412
+ - **Every storage access is wrapped**, including the `localStorage` property read itself —
413
+ Safari's private mode and Chrome with site data blocked throw on _access_, not just on
414
+ write. An unreachable store simply means the fallback paints.
415
+ - **It cannot help a server-rendered host's first frame.** The server has no access to the
416
+ viewer's `localStorage`, so its HTML is always the compiled-in fallback. The read runs in
417
+ a client-only layout effect — before the browser paints, after hydration has committed —
418
+ which removes the hydration-time swap but not the SSR one. If that first frame matters to
419
+ you, pass the catalog you want server-rendered as `fallbackConfig`.
215
420
 
216
421
  ## The layout contract
217
422
 
@@ -248,6 +453,8 @@ The bar is designed to never render blank and never block first paint:
248
453
  during normalization, never carried through and never thrown on.
249
454
  - **A higher `schema_version` than this renderer understands** — ignored the same way;
250
455
  the renderer keeps working on the fields it does know.
456
+ - **A corrupt, stale or unreachable `localStorage`** — treated as "nothing persisted": the
457
+ compiled-in fallback paints and the fetch revalidates. Never throws.
251
458
 
252
459
  ## The `schema_version` contract
253
460
 
@@ -257,3 +464,10 @@ fields it doesn't recognize rather than throw. That's what lets the suite's
257
464
  asynchronously-deployed satellite fleet run mixed renderer versions against one shared
258
465
  payload — an older `@artstorefronts/arthelper-nav` keeps working after a newer field ships on the
259
466
  config row. The version number itself is bumped only for a genuinely breaking change.
467
+
468
+ **The one exception so far:** `theme` was _removed_ from the payload without bumping
469
+ `schema_version` off 1. Removal is breaking under this contract, and it was allowed only
470
+ because no stable version had ever been released against the old shape — the two
471
+ `0.1.0-alpha.*` publishes both tolerate the field's absence and degrade rather than
472
+ throw, which was verified against the actual tarballs. After the first stable publish the
473
+ same removal would require a version bump and a transition period.
package/dist/config.d.ts CHANGED
@@ -7,17 +7,34 @@ import type { NavConfigPayload } from './types.js';
7
7
  * `null` is reserved for a body that could not be used, because that is the signal
8
8
  * the caller reads as "keep showing what you have". An empty `apps` array is NOT such
9
9
  * a body: it is what the endpoint serves when an operator has disabled every
10
- * destination, and treating it as a failure discarded the operator's `theme` and
11
- * `logo` edits along with it and reverted every host to the compiled-in fallback —
12
- * the exact inverse of the instruction.
10
+ * destination, and treating it as a failure reverted every host to the compiled-in
11
+ * fallback and painted a hardcoded Home tile — the exact inverse of the instruction.
12
+ *
13
+ * A payload that still carries the retired `theme` or `logo` blocks drops them here like
14
+ * any other unrecognised field, which is what lets a stage serving the old shape run
15
+ * against this renderer while the endpoint change lands separately.
16
+ *
17
+ * This is also the gate every PERSISTED payload passes through (see `restored`). A blob
18
+ * out of localStorage is untrusted input in exactly the way a network body is — it may
19
+ * have been written months ago by a renderer with a different shape — so it gets the same
20
+ * coercion rather than a JSON.parse and a cast.
13
21
  */
14
22
  declare function normalize(raw: unknown): NavConfigPayload | null;
15
23
  declare function cached(url: string): NavConfigPayload | undefined;
24
+ /**
25
+ * The persisted last-good payload, re-validated. Null when there is nothing stored, the
26
+ * store is unreachable, the blob is corrupt, or the entry is past its staleness cap —
27
+ * every one of which means "paint the compiled-in fallback and wait for the fetch".
28
+ *
29
+ * The dependency runs config -> storage and never back, which is why `writeStored` is
30
+ * called from `fetchConfig` here rather than storage.ts reaching for `normalize` itself.
31
+ */
32
+ declare function restored(): NavConfigPayload | null;
16
33
  /**
17
34
  * Fetches the config with a hard timeout. Resolves to null on any failure —
18
35
  * network, timeout, non-2xx, or an unparseable body — and the caller keeps
19
36
  * whatever it is already showing.
20
37
  */
21
38
  declare function fetchConfig(url: string, timeoutMs?: number): Promise<NavConfigPayload | null>;
22
- export { cached, fetchConfig, normalize };
39
+ export { cached, fetchConfig, normalize, restored };
23
40
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAwB,gBAAgB,EAAgB,MAAM,YAAY,CAAC;AAsEvF;;;;;;;;;;;GAWG;AACH,iBAAS,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAuBxD;AAED,iBAAS,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAEzD;AAED;;;;GAIG;AACH,iBAAe,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,GAAE,MAAmB,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA0BxG;AAED,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAU,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA0D3D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,iBAAS,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAYxD;AAED,iBAAS,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAEzD;AAED;;;;;;;GAOG;AACH,iBAAS,QAAQ,IAAI,gBAAgB,GAAG,IAAI,CAE3C;AAED;;;;GAIG;AACH,iBAAe,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,GAAE,MAAmB,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA2BxG;AAED,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC"}
package/dist/config.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { FALLBACK_CONFIG } from './fallback.js';
2
+ import { readStored, writeStored } from './storage.js';
2
3
  /** Short enough that a slow endpoint never delays the bar past first paint. */
3
4
  const TIMEOUT_MS = 2500;
4
5
  /** Last good payload per URL, so a remount paints the fresh config, not the fallback. */
@@ -9,15 +10,6 @@ function isRecord(value) {
9
10
  function text(value, fallback) {
10
11
  return typeof value === 'string' ? value : fallback;
11
12
  }
12
- function mode(value) {
13
- return value === 'light' ? 'light' : 'dark';
14
- }
15
- function alignment(value) {
16
- return value === 'centered' ? 'centered' : 'left';
17
- }
18
- function accent(value) {
19
- return typeof value === 'string' && value.trim().length > 0 ? value : null;
20
- }
21
13
  // Unknown fields are dropped rather than carried, so a newer payload can never
22
14
  // reach the renderer as a shape it does not understand.
23
15
  function app(raw) {
@@ -66,37 +58,45 @@ function apps(raw) {
66
58
  * `null` is reserved for a body that could not be used, because that is the signal
67
59
  * the caller reads as "keep showing what you have". An empty `apps` array is NOT such
68
60
  * a body: it is what the endpoint serves when an operator has disabled every
69
- * destination, and treating it as a failure discarded the operator's `theme` and
70
- * `logo` edits along with it and reverted every host to the compiled-in fallback —
71
- * the exact inverse of the instruction.
61
+ * destination, and treating it as a failure reverted every host to the compiled-in
62
+ * fallback and painted a hardcoded Home tile — the exact inverse of the instruction.
63
+ *
64
+ * A payload that still carries the retired `theme` or `logo` blocks drops them here like
65
+ * any other unrecognised field, which is what lets a stage serving the old shape run
66
+ * against this renderer while the endpoint change lands separately.
67
+ *
68
+ * This is also the gate every PERSISTED payload passes through (see `restored`). A blob
69
+ * out of localStorage is untrusted input in exactly the way a network body is — it may
70
+ * have been written months ago by a renderer with a different shape — so it gets the same
71
+ * coercion rather than a JSON.parse and a cast.
72
72
  */
73
73
  function normalize(raw) {
74
74
  if (!isRecord(raw)) {
75
75
  return null;
76
76
  }
77
- const theme = isRecord(raw.theme) ? raw.theme : {};
78
- const logo = isRecord(raw.logo) ? raw.logo : {};
79
77
  const parsed = apps(raw.apps);
80
78
  if (!parsed) {
81
79
  return null;
82
80
  }
83
81
  return {
84
82
  schema_version: typeof raw.schema_version === 'number' ? raw.schema_version : FALLBACK_CONFIG.schema_version,
85
- theme: {
86
- mode: mode(theme.mode),
87
- accent_color: accent(theme.accent_color),
88
- alignment: alignment(theme.alignment),
89
- },
90
- logo: {
91
- label: text(logo.label, FALLBACK_CONFIG.logo.label),
92
- href: text(logo.href, FALLBACK_CONFIG.logo.href),
93
- },
94
83
  apps: parsed,
95
84
  };
96
85
  }
97
86
  function cached(url) {
98
87
  return memo.get(url);
99
88
  }
89
+ /**
90
+ * The persisted last-good payload, re-validated. Null when there is nothing stored, the
91
+ * store is unreachable, the blob is corrupt, or the entry is past its staleness cap —
92
+ * every one of which means "paint the compiled-in fallback and wait for the fetch".
93
+ *
94
+ * The dependency runs config -> storage and never back, which is why `writeStored` is
95
+ * called from `fetchConfig` here rather than storage.ts reaching for `normalize` itself.
96
+ */
97
+ function restored() {
98
+ return normalize(readStored());
99
+ }
100
100
  /**
101
101
  * Fetches the config with a hard timeout. Resolves to null on any failure —
102
102
  * network, timeout, non-2xx, or an unparseable body — and the caller keeps
@@ -121,6 +121,7 @@ async function fetchConfig(url, timeoutMs = TIMEOUT_MS) {
121
121
  return null;
122
122
  }
123
123
  memo.set(url, next);
124
+ writeStored(next);
124
125
  return next;
125
126
  }
126
127
  catch {
@@ -130,5 +131,5 @@ async function fetchConfig(url, timeoutMs = TIMEOUT_MS) {
130
131
  clearTimeout(timer);
131
132
  }
132
133
  }
133
- export { cached, fetchConfig, normalize };
134
+ export { cached, fetchConfig, normalize, restored };
134
135
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,12 @@ import type { NavConfigPayload } from './types.js';
15
15
  * runs against this list as against the fetched payload, so a disabled entry contributes
16
16
  * its label, icon, order and destination without appearing as a tile.
17
17
  *
18
+ * It carries no look and no wordmark. Colour, layout and the wordmark are compiled in
19
+ * (index.css and nav-bar.tsx), so there is nothing for a fetched payload to disagree with
20
+ * and nothing to flash between first paint and the fetch landing. The catalog is the one
21
+ * field that can still differ — which is why the last good payload is persisted; see
22
+ * storage.ts.
23
+ *
18
24
  * These are the production domains, which is why they can live in compiled code at all.
19
25
  * Changing this list requires a package release, so it is deliberately the stable
20
26
  * first-party set — anything situational (a stage host, a locally-run satellite) belongs
@@ -1 +1 @@
1
- {"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../src/fallback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,QAAA,MAAM,eAAe,EAAE,gBAyBtB,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../src/fallback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,QAAA,MAAM,eAAe,EAAE,gBAuBtB,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,CAAC"}
package/dist/fallback.js CHANGED
@@ -14,6 +14,12 @@
14
14
  * runs against this list as against the fetched payload, so a disabled entry contributes
15
15
  * its label, icon, order and destination without appearing as a tile.
16
16
  *
17
+ * It carries no look and no wordmark. Colour, layout and the wordmark are compiled in
18
+ * (index.css and nav-bar.tsx), so there is nothing for a fetched payload to disagree with
19
+ * and nothing to flash between first paint and the fetch landing. The catalog is the one
20
+ * field that can still differ — which is why the last good payload is persisted; see
21
+ * storage.ts.
22
+ *
17
23
  * These are the production domains, which is why they can live in compiled code at all.
18
24
  * Changing this list requires a package release, so it is deliberately the stable
19
25
  * first-party set — anything situational (a stage host, a locally-run satellite) belongs
@@ -23,8 +29,6 @@
23
29
  */
24
30
  const FALLBACK_CONFIG = {
25
31
  schema_version: 1,
26
- theme: { mode: 'dark', accent_color: '#2aa34f', alignment: 'left' },
27
- logo: { label: 'ArtHelper', href: 'https://www.arthelper.com' },
28
32
  apps: [
29
33
  { id: 'home', label: 'Home', icon: 'house', href: 'https://www.arthelper.com/community', enabled: true },
30
34
  { id: 'the-daily', label: 'The Daily', icon: 'sunrise', href: 'https://thedaily.arthelper.com', enabled: false },
@@ -1 +1 @@
1
- {"version":3,"file":"fallback.js","sourceRoot":"","sources":["../src/fallback.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,eAAe,GAAqB;IACxC,cAAc,EAAE,CAAC;IACjB,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE;IACnE,IAAI,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,2BAA2B,EAAE;IAC/D,IAAI,EAAE;QACJ,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,qCAAqC,EAAE,OAAO,EAAE,IAAI,EAAE;QACxG,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,gCAAgC,EAAE,OAAO,EAAE,KAAK,EAAE;QAChH,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iCAAiC,EAAE,OAAO,EAAE,IAAI,EAAE;QAC/G;YACE,EAAE,EAAE,iBAAiB;YACrB,KAAK,EAAE,iBAAiB;YACxB,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,8BAA8B;YACpC,OAAO,EAAE,KAAK;SACf;QACD,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,+BAA+B,EAAE,OAAO,EAAE,KAAK,EAAE;QACzG,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,gCAAgC,EAAE,OAAO,EAAE,KAAK,EAAE;QAClH;YACE,EAAE,EAAE,gBAAgB;YACpB,KAAK,EAAE,gBAAgB;YACvB,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,+BAA+B;YACrC,OAAO,EAAE,KAAK;SACf;KACF;CACF,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"fallback.js","sourceRoot":"","sources":["../src/fallback.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,eAAe,GAAqB;IACxC,cAAc,EAAE,CAAC;IACjB,IAAI,EAAE;QACJ,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,qCAAqC,EAAE,OAAO,EAAE,IAAI,EAAE;QACxG,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,gCAAgC,EAAE,OAAO,EAAE,KAAK,EAAE;QAChH,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iCAAiC,EAAE,OAAO,EAAE,IAAI,EAAE;QAC/G;YACE,EAAE,EAAE,iBAAiB;YACrB,KAAK,EAAE,iBAAiB;YACxB,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,8BAA8B;YACpC,OAAO,EAAE,KAAK;SACf;QACD,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,+BAA+B,EAAE,OAAO,EAAE,KAAK,EAAE;QACzG,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,gCAAgC,EAAE,OAAO,EAAE,KAAK,EAAE;QAClH;YACE,EAAE,EAAE,gBAAgB;YACpB,KAAK,EAAE,gBAAgB;YACvB,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,+BAA+B;YACrC,OAAO,EAAE,KAAK;SACf;KACF;CACF,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,CAAC"}
package/dist/index.css CHANGED
@@ -15,9 +15,20 @@
15
15
  */
16
16
 
17
17
  /* ---------------------------------------------------------------------------
18
- * Tokens. `.ahsuite-panel` inherits from `.ahsuite-bar`; `.ahsuite-docked` lives
19
- * in the host's own header and so carries its own copy.
20
- * Dark "Forest" is the default.
18
+ * Tokens — ONE shipped look, dark "Forest". `.ahsuite-panel` inherits from
19
+ * `.ahsuite-bar`; `.ahsuite-docked` lives in the host's own header and so carries
20
+ * its own copy.
21
+ *
22
+ * These values were briefly served from the config endpoint, as a mode /
23
+ * accent_color / alignment block on the payload, and are baked into the release on
24
+ * purpose now. The bar paints a compiled-in fallback before the fetch resolves, so
25
+ * every served value that disagreed with the fallback's guess flashed on EVERY page
26
+ * load: a dark bar turning light, tiles jumping from left to centre. Colour and
27
+ * layout are release decisions, not operational switches. Only the app catalog is
28
+ * still configurable.
29
+ *
30
+ * There is no light palette and no `data-theme` hook any more. A second look would
31
+ * come back as a second release, not a second runtime branch.
21
32
  * ------------------------------------------------------------------------- */
22
33
  .ahsuite-bar,
23
34
  .ahsuite-docked {
@@ -32,8 +43,6 @@
32
43
  --ahsuite-chip-icon: #c4d2c8;
33
44
  --ahsuite-chip-hover-bg: #ffffff2e;
34
45
  --ahsuite-chip-hover-icon: #9fd9b4;
35
- --ahsuite-chip-active-bg: transparent;
36
- --ahsuite-chip-active-image: linear-gradient(180deg, #3dbe66, #2aa34f);
37
46
  --ahsuite-chip-active-icon: #fff;
38
47
  --ahsuite-label: #c4d2c8;
39
48
  --ahsuite-label-active: #fff;
@@ -41,30 +50,7 @@
41
50
  --ahsuite-surface-border: #ffffff1f;
42
51
  --ahsuite-control-border: #ffffff2e;
43
52
  --ahsuite-row-hover-bg: #ffffff1f;
44
- --ahsuite-accent-text: #9fd9b4;
45
- --ahsuite-tiles-justify: flex-start;
46
- }
47
-
48
- .ahsuite-bar[data-theme='light'],
49
- .ahsuite-docked[data-theme='light'] {
50
- --ahsuite-bar-bg: #fff;
51
- --ahsuite-bar-border: #e6e1d5;
52
- --ahsuite-waffle-icon: #4a5951;
53
- --ahsuite-waffle-hover-bg: #f1ede2;
54
- --ahsuite-waffle-hover-icon: #2e9e58;
55
- --ahsuite-wordmark: #12211a;
56
- --ahsuite-divider: #e6e1d5;
57
- --ahsuite-chip-bg: #f1ede2;
58
- --ahsuite-chip-icon: #4a5951;
59
- --ahsuite-chip-hover-bg: #e4f4e8;
60
- --ahsuite-chip-hover-icon: #2e9e58;
61
- --ahsuite-label: #4a5951;
62
- --ahsuite-label-active: #12211a;
63
- --ahsuite-surface-bg: #fff;
64
- --ahsuite-surface-border: #e6e1d5;
65
- --ahsuite-control-border: #e6e1d5;
66
- --ahsuite-row-hover-bg: #f1ede2;
67
- --ahsuite-accent-text: #2e9e58;
53
+ --ahsuite-eyebrow: #9fd9b4;
68
54
  }
69
55
 
70
56
  /* ---------------------------------------------------------------------------
@@ -143,6 +129,40 @@
143
129
  visibility 0s linear 180ms;
144
130
  }
145
131
 
132
+ /* ---------------------------------------------------------------------------
133
+ * True centring, in three flex children: brand | tiles | balance.
134
+ *
135
+ * The tile row used to be a direct child that grew into the row's leftover space and
136
+ * centred inside it. Because that leftover space starts AFTER the waffle, wordmark and
137
+ * divider, the tiles landed a constant 60.8px right of the bar's real centre — exactly
138
+ * half the width of that left block, at every viewport.
139
+ *
140
+ * So the left block is one flex child and an empty `.ahsuite-balance` mirrors it on the
141
+ * right. Both take `flex: 1 1 0%`, so they split the free space equally whatever the
142
+ * brand actually measures, and the content-sized tile row between them lands on the
143
+ * bar's centre line. The bar's own 10px gap appears once on each side of the row, so it
144
+ * cancels too — the maths is symmetric, not tuned.
145
+ *
146
+ * Neither balancing child may shrink the row's content out of the bar: their 0% basis
147
+ * gives them a scaled shrink factor of 0, so every pixel of negative free space comes
148
+ * off `.ahsuite-tiles` (which clips, see below) and the brand keeps its min-content
149
+ * width. Squeeze harder and the equal shares fall under the brand's min-content, the
150
+ * brand is clamped there, and the balance takes the remainder: the row slides toward
151
+ * the left rather than sitting under the wordmark. Clipped or left-ish, never overlapped.
152
+ * ------------------------------------------------------------------------- */
153
+ .ahsuite-brand {
154
+ display: flex;
155
+ align-items: center;
156
+ gap: 10px;
157
+ flex: 1 1 0%;
158
+ }
159
+
160
+ .ahsuite-balance {
161
+ display: block;
162
+ flex: 1 1 0%;
163
+ min-width: 0;
164
+ }
165
+
146
166
  .ahsuite-waffle {
147
167
  display: inline-flex;
148
168
  align-items: center;
@@ -193,26 +213,26 @@
193
213
  }
194
214
 
195
215
  /* ---------------------------------------------------------------------------
196
- * Desktop tile row. `alignment` drives justify-content and nothing else.
216
+ * Desktop tile row. Sized to its own content and centred by the two balancing
217
+ * siblings above, never by a justify-content it distributes itself.
197
218
  * ------------------------------------------------------------------------- */
198
219
  .ahsuite-tiles {
199
220
  display: flex;
200
221
  gap: 4px;
201
222
  align-items: center;
202
- justify-content: var(--ahsuite-tiles-justify);
203
- /* Grow into the row's leftover space. Without this the row is sized to its own
204
- * content, so `justify-content` has nothing to distribute and `alignment: centered`
205
- * renders identically to `left` — the variable was set correctly all along, the
206
- * layout simply could not express it. `min-width: 0` lets the row shrink below its
207
- * content so `overflow: hidden` clips rather than overflowing a narrow bar. */
208
- flex: 1 1 auto;
223
+ /* Content-sized: `flex-grow: 0` is what makes the row the thing being centred rather
224
+ * than a slot that centres its own contents. Growing it would work out to the same
225
+ * centre line while the tiles fit, and clip symmetrically — half a tile at each end —
226
+ * the moment they do not. `flex-shrink: 1` with `min-width: 0` lets the row give up
227
+ * width under pressure so `overflow: hidden` clips the trailing tiles instead of
228
+ * pushing the row over the wordmark. */
229
+ flex: 0 1 auto;
230
+ justify-content: flex-start;
209
231
  min-width: 0;
210
232
  overflow: hidden;
211
233
  /* 100%, not a fixed cap. This is the property the collapse animates (to 0), so it has
212
- * to stay — but a 1200px cap stopped the row growing on a wide viewport, which left
213
- * `alignment: centered` visibly left-of-centre (546px out at 2560px wide). The cap was
214
- * invisible while the row was content-sized and never affected the left-aligned case,
215
- * since tiles sit left whatever the row's width. */
234
+ * to stay — but a fixed cap (it was 1200px) would stop the row reaching its content
235
+ * width on a wide viewport and drag it off centre. */
216
236
  max-width: 100%;
217
237
  opacity: 1;
218
238
  transform: translateY(0);
@@ -293,13 +313,14 @@ span.ahsuite-row {
293
313
  white-space: nowrap;
294
314
  }
295
315
 
296
- /* accent_color arrives as a single flat fill: --ahsuite-chip-active-bg is the
297
- * colour and --ahsuite-chip-active-image is switched to none. */
316
+ /* The active chip's two-stop gradient is fixed. It used to be overridable by a single
317
+ * flat accent_color from the payload, which meant one config value could not describe
318
+ * the shipped look at all — it could only replace it, and did so a frame late. */
298
319
  .ahsuite-chip--active,
299
320
  .ahsuite-tile--active .ahsuite-chip--active,
300
321
  .ahsuite-tile--active:hover .ahsuite-chip--active {
301
- background-color: var(--ahsuite-chip-active-bg);
302
- background-image: var(--ahsuite-chip-active-image);
322
+ background-color: transparent;
323
+ background-image: linear-gradient(180deg, #3dbe66, #2aa34f);
303
324
  color: var(--ahsuite-chip-active-icon);
304
325
  }
305
326
 
@@ -414,7 +435,7 @@ span.ahsuite-row {
414
435
  font-size: 11px;
415
436
  font-weight: 600;
416
437
  letter-spacing: 0.06em;
417
- color: var(--ahsuite-accent-text);
438
+ color: var(--ahsuite-eyebrow);
418
439
  }
419
440
 
420
441
  .ahsuite-close {
@@ -471,8 +492,8 @@ span.ahsuite-row {
471
492
  }
472
493
 
473
494
  .ahsuite-row:hover .ahsuite-chip--active {
474
- background-color: var(--ahsuite-chip-active-bg);
475
- background-image: var(--ahsuite-chip-active-image);
495
+ background-color: transparent;
496
+ background-image: linear-gradient(180deg, #3dbe66, #2aa34f);
476
497
  color: var(--ahsuite-chip-active-icon);
477
498
  }
478
499
 
@@ -497,6 +518,17 @@ span.ahsuite-row {
497
518
  display: inline-flex;
498
519
  }
499
520
 
521
+ /* Nothing to centre once the tile row is gone, so the balancing pair stands down and
522
+ * the trigger sits beside the wordmark instead of floating in the middle of the bar.
523
+ * `.ahsuite-balance` keeps growing purely to hold the right-hand slack. */
524
+ .ahsuite-brand {
525
+ flex: 0 0 auto;
526
+ }
527
+
528
+ .ahsuite-balance {
529
+ flex: 1 1 auto;
530
+ }
531
+
500
532
  .ahsuite-panel {
501
533
  display: block;
502
534
  }
package/dist/index.d.ts CHANGED
@@ -9,5 +9,5 @@ export type { NavContextValue } from './context.js';
9
9
  export type { NavBarProps } from './nav-bar.js';
10
10
  export type { NavTriggerProps } from './nav-trigger.js';
11
11
  export type { NavProviderProps } from './provider.js';
12
- export type { NavAlignment, NavApp, NavAppClickArgs, NavConfigPayload, NavLogo, NavTheme, NavThemeMode, ResolvedNavApp, } from './types.js';
12
+ export type { NavApp, NavAppClickArgs, NavConfigPayload, ResolvedNavApp } from './types.js';
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,YAAY,EACV,YAAY,EACZ,MAAM,EACN,eAAe,EACf,gBAAgB,EAChB,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,cAAc,GACf,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,YAAY,EAAE,MAAM,EAAE,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"nav-bar.d.ts","sourceRoot":"","sources":["../src/nav-bar.tsx"],"names":[],"mappings":"AASA,UAAU,WAAW;IACnB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAsED,iBAAS,MAAM,CAAC,EAAE,MAAc,EAAE,SAAS,EAAE,EAAE,WAAW,2CA6HzD;AAED,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,YAAY,EAAE,WAAW,EAAE,CAAC"}
1
+ {"version":3,"file":"nav-bar.d.ts","sourceRoot":"","sources":["../src/nav-bar.tsx"],"names":[],"mappings":"AAsBA,UAAU,WAAW;IACnB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAsED,iBAAS,MAAM,CAAC,EAAE,MAAc,EAAE,SAAS,EAAE,EAAE,WAAW,2CAkIzD;AAED,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,YAAY,EAAE,WAAW,EAAE,CAAC"}
package/dist/nav-bar.js CHANGED
@@ -4,7 +4,19 @@ import { ChevronDown, X } from 'lucide-react';
4
4
  import { useCallback, useEffect, useRef, useState } from 'react';
5
5
  import { useNav } from './context.js';
6
6
  import { NavIcon, WaffleIcon } from './icons.js';
7
- import { themeVars } from './theme.js';
7
+ /**
8
+ * The wordmark, compiled in. It used to come from the payload's `logo` block, which meant
9
+ * a static piece of chrome could disagree with the compiled-in fallback and shift on load
10
+ * like everything else fetched. Nobody renames the ArtHelper wordmark from an admin
11
+ * screen, so it belongs in the release.
12
+ *
13
+ * The href is the site root, NOT the resolved `home` catalog entry. Deriving it was
14
+ * considered and rejected twice over: it would put this back on config, and Home points at
15
+ * /community while the wordmark has always gone to the root — they are different
16
+ * destinations that happen to share a domain, not one destination expressed twice.
17
+ */
18
+ const WORDMARK_LABEL = 'arthelper';
19
+ const WORDMARK_HREF = 'https://www.arthelper.com';
8
20
  function chipClass(entry) {
9
21
  return entry.active ? 'ahsuite-chip ahsuite-chip--active' : 'ahsuite-chip';
10
22
  }
@@ -32,7 +44,7 @@ function Row({ entry, onActivate }) {
32
44
  return (_jsxs("a", { "aria-current": entry.active ? 'page' : undefined, className: className, href: entry.app.href, onClick: () => onActivate(entry), children: [_jsx(Chip, { entry: entry }), _jsx("span", { className: "ahsuite-row-label", children: entry.app.label })] }));
33
45
  }
34
46
  function NavBar({ sticky = false, className }) {
35
- const { config, apps, activeApp, collapsed, toggle, activate } = useNav();
47
+ const { apps, activeApp, collapsed, toggle, activate } = useNav();
36
48
  const [open, setOpen] = useState(false);
37
49
  const panelRef = useRef(null);
38
50
  const triggerRef = useRef(null);
@@ -85,7 +97,7 @@ function NavBar({ sticky = false, className }) {
85
97
  setOpen(false);
86
98
  }, [activate]);
87
99
  const classes = ['ahsuite-bar', sticky ? 'ahsuite-bar--sticky' : '', className ?? ''].filter(Boolean).join(' ');
88
- return (_jsxs("div", { "aria-label": "ArtHelper suite", className: classes, "data-collapsed": collapsed ? 'true' : 'false', "data-theme": config.theme.mode, role: "navigation", style: themeVars(config.theme), children: [_jsx("button", { "aria-expanded": !collapsed, "aria-label": collapsed ? 'Show the suite bar' : 'Hide the suite bar', className: "ahsuite-waffle", onClick: toggle, type: "button", children: _jsx(WaffleIcon, {}) }), config.logo.href ? (_jsx("a", { className: "ahsuite-wordmark", href: config.logo.href, children: config.logo.label })) : (_jsx("span", { className: "ahsuite-wordmark", children: config.logo.label })), _jsx("span", { "aria-hidden": "true", className: "ahsuite-divider" }), _jsx("div", { className: "ahsuite-tiles", children: apps.map((entry) => (_jsx(Tile, { entry: entry, onActivate: onActivate }, entry.app.id))) }), _jsxs("button", { "aria-controls": "ahsuite-panel", "aria-expanded": open, "aria-haspopup": "true", className: "ahsuite-trigger", onClick: () => setOpen((current) => !current), ref: triggerRef, type: "button", children: [activeApp ? _jsx(Chip, { entry: activeApp }) : null, _jsx("span", { className: "ahsuite-trigger-label", children: activeApp?.app.label ?? 'Apps' }), _jsx(ChevronDown, { "aria-hidden": "true", className: "ahsuite-chevron", height: 18, strokeWidth: 2, width: 18 })] }), _jsxs("div", { "aria-label": "Apps", className: "ahsuite-panel", hidden: !open, id: "ahsuite-panel", ref: panelRef, role: "dialog", children: [_jsxs("div", { className: "ahsuite-panel-head", children: [_jsx("span", { className: "ahsuite-panel-title", children: "Apps" }), _jsxs("button", { "aria-label": "Close", className: "ahsuite-close", onClick: () => setOpen(false), type: "button", children: [_jsx(X, { "aria-hidden": "true", className: "ahsuite-icon", height: 14, strokeWidth: 2, width: 14 }), _jsx("span", { children: "Close" })] })] }), apps.map((entry) => (_jsx(Row, { entry: entry, onActivate: onActivate }, entry.app.id)))] })] }));
100
+ return (_jsxs("div", { "aria-label": "ArtHelper suite", className: classes, "data-collapsed": collapsed ? 'true' : 'false', role: "navigation", children: [_jsxs("div", { className: "ahsuite-brand", children: [_jsx("button", { "aria-expanded": !collapsed, "aria-label": collapsed ? 'Show the suite bar' : 'Hide the suite bar', className: "ahsuite-waffle", onClick: toggle, type: "button", children: _jsx(WaffleIcon, {}) }), _jsx("a", { className: "ahsuite-wordmark", href: WORDMARK_HREF, children: WORDMARK_LABEL }), _jsx("span", { "aria-hidden": "true", className: "ahsuite-divider" })] }), _jsx("div", { className: "ahsuite-tiles", children: apps.map((entry) => (_jsx(Tile, { entry: entry, onActivate: onActivate }, entry.app.id))) }), _jsxs("button", { "aria-controls": "ahsuite-panel", "aria-expanded": open, "aria-haspopup": "true", className: "ahsuite-trigger", onClick: () => setOpen((current) => !current), ref: triggerRef, type: "button", children: [activeApp ? _jsx(Chip, { entry: activeApp }) : null, _jsx("span", { className: "ahsuite-trigger-label", children: activeApp?.app.label ?? 'Apps' }), _jsx(ChevronDown, { "aria-hidden": "true", className: "ahsuite-chevron", height: 18, strokeWidth: 2, width: 18 })] }), _jsx("span", { "aria-hidden": "true", className: "ahsuite-balance" }), _jsxs("div", { "aria-label": "Apps", className: "ahsuite-panel", hidden: !open, id: "ahsuite-panel", ref: panelRef, role: "dialog", children: [_jsxs("div", { className: "ahsuite-panel-head", children: [_jsx("span", { className: "ahsuite-panel-title", children: "Apps" }), _jsxs("button", { "aria-label": "Close", className: "ahsuite-close", onClick: () => setOpen(false), type: "button", children: [_jsx(X, { "aria-hidden": "true", className: "ahsuite-icon", height: 14, strokeWidth: 2, width: 14 }), _jsx("span", { children: "Close" })] })] }), apps.map((entry) => (_jsx(Row, { entry: entry, onActivate: onActivate }, entry.app.id)))] })] }));
89
101
  }
90
102
  export { NavBar };
91
103
  //# sourceMappingURL=nav-bar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"nav-bar.js","sourceRoot":"","sources":["../src/nav-bar.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"nav-bar.js","sourceRoot":"","sources":["../src/nav-bar.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"nav-trigger.d.ts","sourceRoot":"","sources":["../src/nav-trigger.tsx"],"names":[],"mappings":"AAMA,UAAU,eAAe;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,EAAE,eAAe,2CAuBjD;AAED,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,YAAY,EAAE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"nav-trigger.d.ts","sourceRoot":"","sources":["../src/nav-trigger.tsx"],"names":[],"mappings":"AAKA,UAAU,eAAe;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,EAAE,eAAe,2CAqBjD;AAED,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,YAAY,EAAE,eAAe,EAAE,CAAC"}
@@ -2,7 +2,6 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useNav } from './context.js';
4
4
  import { NavIcon, WaffleIcon } from './icons.js';
5
- import { themeVars } from './theme.js';
6
5
  /**
7
6
  * The waffle plus active-app chip a host places by hand in its own header, beside
8
7
  * its logo. This is the RESTORE control: while the bar is collapsed it is the only
@@ -13,9 +12,9 @@ import { themeVars } from './theme.js';
13
12
  * than swapped — the bar's own waffle is the hide control.
14
13
  */
15
14
  function NavTrigger({ className }) {
16
- const { config, activeApp, collapsed, toggle } = useNav();
15
+ const { activeApp, collapsed, toggle } = useNav();
17
16
  const classes = ['ahsuite-docked', className ?? ''].filter(Boolean).join(' ');
18
- return (_jsxs("button", { "aria-expanded": false, "aria-label": "Show the suite bar", className: classes, "data-collapsed": collapsed ? 'true' : 'false', "data-theme": config.theme.mode, onClick: toggle, style: themeVars(config.theme), title: "Show the suite bar", type: "button", children: [_jsx("span", { className: "ahsuite-chip ahsuite-chip--active", children: _jsx(NavIcon, { name: activeApp?.app.icon ?? '' }) }), _jsx(WaffleIcon, { className: "ahsuite-waffle-mini" })] }));
17
+ return (_jsxs("button", { "aria-expanded": false, "aria-label": "Show the suite bar", className: classes, "data-collapsed": collapsed ? 'true' : 'false', onClick: toggle, title: "Show the suite bar", type: "button", children: [_jsx("span", { className: "ahsuite-chip ahsuite-chip--active", children: _jsx(NavIcon, { name: activeApp?.app.icon ?? '' }) }), _jsx(WaffleIcon, { className: "ahsuite-waffle-mini" })] }));
19
18
  }
20
19
  export { NavTrigger };
21
20
  //# sourceMappingURL=nav-trigger.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"nav-trigger.js","sourceRoot":"","sources":["../src/nav-trigger.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAMvC;;;;;;;;GAQG;AACH,SAAS,UAAU,CAAC,EAAE,SAAS,EAAmB;IAChD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IAE1D,MAAM,OAAO,GAAG,CAAC,gBAAgB,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE9E,OAAO,CACL,mCACiB,KAAK,gBACT,oBAAoB,EAC/B,SAAS,EAAE,OAAO,oBACF,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,gBAChC,MAAM,CAAC,KAAK,CAAC,IAAI,EAC7B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,EAC9B,KAAK,EAAC,oBAAoB,EAC1B,IAAI,EAAC,QAAQ,aAEb,eAAM,SAAS,EAAC,mCAAmC,YACjD,KAAC,OAAO,IAAC,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,GAAI,GACvC,EACP,KAAC,UAAU,IAAC,SAAS,EAAC,qBAAqB,GAAG,IACvC,CACV,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"nav-trigger.js","sourceRoot":"","sources":["../src/nav-trigger.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAMjD;;;;;;;;GAQG;AACH,SAAS,UAAU,CAAC,EAAE,SAAS,EAAmB;IAChD,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IAElD,MAAM,OAAO,GAAG,CAAC,gBAAgB,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE9E,OAAO,CACL,mCACiB,KAAK,gBACT,oBAAoB,EAC/B,SAAS,EAAE,OAAO,oBACF,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAC5C,OAAO,EAAE,MAAM,EACf,KAAK,EAAC,oBAAoB,EAC1B,IAAI,EAAC,QAAQ,aAEb,eAAM,SAAS,EAAC,mCAAmC,YACjD,KAAC,OAAO,IAAC,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,GAAI,GACvC,EACP,KAAC,UAAU,IAAC,SAAS,EAAC,qBAAqB,GAAG,IACvC,CACV,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA6C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAMlF,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAkB,MAAM,YAAY,CAAC;AAEpF,UAAU,gBAAgB;IACxB,uFAAuF;IACvF,SAAS,EAAE,MAAM,CAAC;IAClB,iGAAiG;IACjG,WAAW,EAAE,MAAM,CAAC;IACpB,+FAA+F;IAC/F,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IAC7C,iGAAiG;IACjG,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,wGAAwG;IACxG,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,WAAW,EACX,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,QAAQ,GACT,EAAE,gBAAgB,2CAiDlB;AAED,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA8D,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAMnG,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAkB,MAAM,YAAY,CAAC;AAsBpF,UAAU,gBAAgB;IACxB,uFAAuF;IACvF,SAAS,EAAE,MAAM,CAAC;IAClB,iGAAiG;IACjG,WAAW,EAAE,MAAM,CAAC;IACpB,+FAA+F;IAC/F,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IAC7C,iGAAiG;IACjG,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,wGAAwG;IACxG,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,WAAW,EACX,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,QAAQ,GACT,EAAE,gBAAgB,2CA+DlB;AAED,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
package/dist/provider.js CHANGED
@@ -1,17 +1,49 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { useCallback, useEffect, useMemo, useState } from 'react';
4
- import { cached, fetchConfig } from './config.js';
3
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react';
4
+ import { cached, fetchConfig, restored } from './config.js';
5
5
  import { NavContext } from './context.js';
6
6
  import { readCollapsed, writeCollapsed } from './cookie.js';
7
7
  import { FALLBACK_CONFIG } from './fallback.js';
8
8
  import { resolveApps } from './resolve.js';
9
+ /**
10
+ * useLayoutEffect on the client, useEffect on the server — the standard isomorphic-layout
11
+ * pattern, and it is load-bearing in BOTH directions here.
12
+ *
13
+ * It cannot be the useState initialiser. town-square server-renders this bar, the server
14
+ * cannot read the viewer's localStorage, and a client first render that disagrees with the
15
+ * server HTML is a hydration mismatch — a tested-clean property of that mount today.
16
+ *
17
+ * It cannot be a plain useEffect either. Passive effects are flushed on a scheduler
18
+ * callback that can land AFTER the browser has painted, which puts the wrong catalog on
19
+ * screen for a frame — precisely the flash this whole change exists to remove. Layout
20
+ * effects run synchronously after commit and before paint, so the state swap and its
21
+ * re-render are both done before anything is shown.
22
+ *
23
+ * Picking the hook by `typeof window` keeps React from warning that useLayoutEffect does
24
+ * nothing on the server. Hydration builds the client fiber tree fresh, so the two paths
25
+ * never have to agree on which effect flavour occupied the slot.
26
+ */
27
+ const useBeforePaint = typeof window === 'undefined' ? useEffect : useLayoutEffect;
9
28
  function NavProvider({ configUrl, activeAppId, onAppClick, fallbackConfig, initialCollapsed, children, }) {
10
29
  const fallback = fallbackConfig ?? FALLBACK_CONFIG;
11
30
  // Stale-while-revalidate: paint the last good payload (or the embedded fallback)
12
31
  // synchronously, then swap in the fetched one. First paint never waits on the network.
13
32
  const [config, setConfig] = useState(() => cached(configUrl) ?? fallback);
14
33
  const [collapsed, setCollapsedState] = useState(() => initialCollapsed ?? readCollapsed() ?? false);
34
+ // Before the first paint, upgrade the compiled-in fallback to the last payload this
35
+ // browser actually saw. `cached` wins when it is populated: an in-memory hit means a
36
+ // fetch already landed in THIS page session, which is strictly fresher than the store.
37
+ useBeforePaint(() => {
38
+ if (cached(configUrl)) {
39
+ return;
40
+ }
41
+ const stored = restored();
42
+ if (!stored) {
43
+ return;
44
+ }
45
+ setConfig(stored);
46
+ }, [configUrl]);
15
47
  useEffect(() => {
16
48
  let live = true;
17
49
  void fetchConfig(configUrl).then((next) => {
@@ -1 +1 @@
1
- {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The last good payload, persisted per browser.
3
+ *
4
+ * Why this exists: the bar paints the compiled-in FALLBACK_CONFIG before the fetch
5
+ * resolves, and the catalog is the one field that can still legitimately differ from it —
6
+ * `enabled` states drift from the config row by design (see fallback.ts). Because the tile
7
+ * row is centred on the bar, that drift is expensive: every tile the fetch adds or removes
8
+ * shifts the whole row by half a tile, so a 2-tile fallback resolving to a 7-tile config
9
+ * moves the Home tile 230px. Persisting the last good payload makes the second and every
10
+ * subsequent visit paint the catalog the viewer actually saw last time.
11
+ *
12
+ * It cannot help the FIRST visit, and it cannot help the first paint of a server-rendered
13
+ * host: the server has no access to the browser's localStorage, so its HTML is always the
14
+ * compiled-in fallback. What this removes is the hydration-time swap on every return
15
+ * visit. See provider.tsx for how the read is sequenced against paint.
16
+ *
17
+ * EVERY access is wrapped, including the `localStorage` property read itself. Safari's
18
+ * private mode and Chrome with site data blocked throw a SecurityError on ACCESS, not just
19
+ * on setItem, so `typeof localStorage !== 'undefined'` is not a sufficient guard.
20
+ */
21
+ /**
22
+ * Versioned on the RENDERER's expected shape, not on the payload's `schema_version`. A
23
+ * renderer that changes what it stores bumps this key rather than trying to migrate, so
24
+ * an old blob is orphaned and ignored instead of misread. localStorage is origin-scoped
25
+ * and a host has exactly one configUrl, so the key needs no URL in it.
26
+ */
27
+ declare const STORAGE_KEY = "ahsuite-nav-config-v1";
28
+ /** Beyond this a dormant browser revalidates rather than painting something ancient. */
29
+ declare const MAX_AGE_MS: number;
30
+ /**
31
+ * The stored payload, or null. Returns the RAW payload deliberately — the caller runs it
32
+ * through `normalize` exactly as it would a network body, because a blob written by an
33
+ * older renderer is untrusted input of precisely the same kind.
34
+ */
35
+ declare function readStored(): unknown;
36
+ /** Best effort. A full quota or a blocked store is not a reason to fail the fetch. */
37
+ declare function writeStored(payload: unknown): void;
38
+ export { MAX_AGE_MS, STORAGE_KEY, readStored, writeStored };
39
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;GAKG;AACH,QAAA,MAAM,WAAW,0BAA0B,CAAC;AAE5C,wFAAwF;AACxF,QAAA,MAAM,UAAU,QAA2B,CAAC;AAU5C;;;;GAIG;AACH,iBAAS,UAAU,IAAI,OAAO,CAuB7B;AAED,sFAAsF;AACtF,iBAAS,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAM3C;AAED,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The last good payload, persisted per browser.
3
+ *
4
+ * Why this exists: the bar paints the compiled-in FALLBACK_CONFIG before the fetch
5
+ * resolves, and the catalog is the one field that can still legitimately differ from it —
6
+ * `enabled` states drift from the config row by design (see fallback.ts). Because the tile
7
+ * row is centred on the bar, that drift is expensive: every tile the fetch adds or removes
8
+ * shifts the whole row by half a tile, so a 2-tile fallback resolving to a 7-tile config
9
+ * moves the Home tile 230px. Persisting the last good payload makes the second and every
10
+ * subsequent visit paint the catalog the viewer actually saw last time.
11
+ *
12
+ * It cannot help the FIRST visit, and it cannot help the first paint of a server-rendered
13
+ * host: the server has no access to the browser's localStorage, so its HTML is always the
14
+ * compiled-in fallback. What this removes is the hydration-time swap on every return
15
+ * visit. See provider.tsx for how the read is sequenced against paint.
16
+ *
17
+ * EVERY access is wrapped, including the `localStorage` property read itself. Safari's
18
+ * private mode and Chrome with site data blocked throw a SecurityError on ACCESS, not just
19
+ * on setItem, so `typeof localStorage !== 'undefined'` is not a sufficient guard.
20
+ */
21
+ /**
22
+ * Versioned on the RENDERER's expected shape, not on the payload's `schema_version`. A
23
+ * renderer that changes what it stores bumps this key rather than trying to migrate, so
24
+ * an old blob is orphaned and ignored instead of misread. localStorage is origin-scoped
25
+ * and a host has exactly one configUrl, so the key needs no URL in it.
26
+ */
27
+ const STORAGE_KEY = 'ahsuite-nav-config-v1';
28
+ /** Beyond this a dormant browser revalidates rather than painting something ancient. */
29
+ const MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;
30
+ function store() {
31
+ try {
32
+ return globalThis.localStorage ?? null;
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
38
+ /**
39
+ * The stored payload, or null. Returns the RAW payload deliberately — the caller runs it
40
+ * through `normalize` exactly as it would a network body, because a blob written by an
41
+ * older renderer is untrusted input of precisely the same kind.
42
+ */
43
+ function readStored() {
44
+ try {
45
+ const raw = store()?.getItem(STORAGE_KEY);
46
+ if (!raw) {
47
+ return null;
48
+ }
49
+ const envelope = JSON.parse(raw);
50
+ if (typeof envelope !== 'object' || envelope === null) {
51
+ return null;
52
+ }
53
+ const { saved_at: savedAt, payload } = envelope;
54
+ if (typeof savedAt !== 'number') {
55
+ return null;
56
+ }
57
+ // Absolute difference, so a clock moved BACKWARDS discards the entry rather than
58
+ // treating a future timestamp as permanently fresh.
59
+ if (Math.abs(Date.now() - savedAt) > MAX_AGE_MS) {
60
+ return null;
61
+ }
62
+ return payload ?? null;
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
68
+ /** Best effort. A full quota or a blocked store is not a reason to fail the fetch. */
69
+ function writeStored(payload) {
70
+ try {
71
+ store()?.setItem(STORAGE_KEY, JSON.stringify({ saved_at: Date.now(), payload }));
72
+ }
73
+ catch {
74
+ return;
75
+ }
76
+ }
77
+ export { MAX_AGE_MS, STORAGE_KEY, readStored, writeStored };
78
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,GAAG,uBAAuB,CAAC;AAE5C,wFAAwF;AACxF,MAAM,UAAU,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE5C,SAAS,KAAK;IACZ,IAAI,CAAC;QACH,OAAO,UAAU,CAAC,YAAY,IAAI,IAAI,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,KAAK,EAAE,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;QAC1C,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,QAAQ,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,QAAqD,CAAC;QAC7F,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,iFAAiF;QACjF,oDAAoD;QACpD,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,UAAU,EAAE,CAAC;YAChD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,OAAO,IAAI,IAAI,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,SAAS,WAAW,CAAC,OAAgB;IACnC,IAAI,CAAC;QACH,KAAK,EAAE,EAAE,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IACnF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;AACH,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC"}
package/dist/types.d.ts CHANGED
@@ -5,18 +5,21 @@
5
5
  * never removed or repurposed. Renderers ignore fields they do not recognise and
6
6
  * degrade rather than throw on a higher schema_version, so an asynchronously
7
7
  * deployed fleet can run mixed renderer versions against one payload.
8
+ *
9
+ * The payload used to carry a `theme` block (mode, accent_color, alignment) and a `logo`
10
+ * block (label, href). Both are gone, for one reason: the bar paints the compiled-in
11
+ * fallback before the fetch resolves, so EVERY configurable field is a field that can
12
+ * disagree with the fallback's guess and flash on load. The look and the wordmark are
13
+ * release decisions — nobody renames the ArtHelper wordmark from an admin screen — so
14
+ * they are baked in (see index.css and nav-bar.tsx). The app catalog is the one genuine
15
+ * operational switch, and it is all that is left here.
16
+ *
17
+ * Dropping a field IS breaking under the additive contract, so schema_version stays at 1
18
+ * only because no stable consumer was ever released against the old shape — the only
19
+ * publishes are 0.1.0-alpha.0 and 0.1.0-alpha.1, and both tolerate the fields' absence.
20
+ * AFTER THE FIRST STABLE PUBLISH the same removal would have to bump schema_version and
21
+ * keep the old branch alive until the fleet has rolled.
8
22
  */
9
- type NavThemeMode = 'dark' | 'light';
10
- type NavAlignment = 'left' | 'centered';
11
- interface NavTheme {
12
- mode: NavThemeMode;
13
- accent_color: string | null;
14
- alignment: NavAlignment;
15
- }
16
- interface NavLogo {
17
- label: string;
18
- href: string;
19
- }
20
23
  interface NavApp {
21
24
  id: string;
22
25
  label: string;
@@ -26,8 +29,6 @@ interface NavApp {
26
29
  }
27
30
  interface NavConfigPayload {
28
31
  schema_version: number;
29
- theme: NavTheme;
30
- logo: NavLogo;
31
32
  apps: NavApp[];
32
33
  }
33
34
  /**
@@ -46,5 +47,5 @@ interface NavAppClickArgs {
46
47
  activeAppId: string;
47
48
  collapsed: boolean;
48
49
  }
49
- export type { NavAlignment, NavApp, NavAppClickArgs, NavConfigPayload, NavLogo, NavTheme, NavThemeMode, ResolvedNavApp, };
50
+ export type { NavApp, NavAppClickArgs, NavConfigPayload, ResolvedNavApp };
50
51
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,KAAK,YAAY,GAAG,MAAM,GAAG,OAAO,CAAC;AAErC,KAAK,YAAY,GAAG,MAAM,GAAG,UAAU,CAAC;AAExC,UAAU,QAAQ;IAChB,IAAI,EAAE,YAAY,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,YAAY,CAAC;CACzB;AAED,UAAU,OAAO;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,MAAM;IACd,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,QAAQ,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;GAIG;AACH,UAAU,cAAc;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,0FAA0F;AAC1F,UAAU,eAAe;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,YAAY,EACV,YAAY,EACZ,MAAM,EACN,eAAe,EACf,gBAAgB,EAChB,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,cAAc,GACf,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,UAAU,MAAM;IACd,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;GAIG;AACH,UAAU,cAAc;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,0FAA0F;AAC1F,UAAU,eAAe;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,YAAY,EAAE,MAAM,EAAE,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,CAAC"}
package/dist/types.js CHANGED
@@ -5,6 +5,20 @@
5
5
  * never removed or repurposed. Renderers ignore fields they do not recognise and
6
6
  * degrade rather than throw on a higher schema_version, so an asynchronously
7
7
  * deployed fleet can run mixed renderer versions against one payload.
8
+ *
9
+ * The payload used to carry a `theme` block (mode, accent_color, alignment) and a `logo`
10
+ * block (label, href). Both are gone, for one reason: the bar paints the compiled-in
11
+ * fallback before the fetch resolves, so EVERY configurable field is a field that can
12
+ * disagree with the fallback's guess and flash on load. The look and the wordmark are
13
+ * release decisions — nobody renames the ArtHelper wordmark from an admin screen — so
14
+ * they are baked in (see index.css and nav-bar.tsx). The app catalog is the one genuine
15
+ * operational switch, and it is all that is left here.
16
+ *
17
+ * Dropping a field IS breaking under the additive contract, so schema_version stays at 1
18
+ * only because no stable consumer was ever released against the old shape — the only
19
+ * publishes are 0.1.0-alpha.0 and 0.1.0-alpha.1, and both tolerate the fields' absence.
20
+ * AFTER THE FIRST STABLE PUBLISH the same removal would have to bump schema_version and
21
+ * keep the old branch alive until the fleet has rolled.
8
22
  */
9
23
  export {};
10
24
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@artstorefronts/arthelper-nav",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.2",
4
4
  "description": "The ArtHelper suite navigation bar — a config-driven, cross-origin app switcher.",
5
5
  "license": "ISC",
6
6
  "type": "module",