@flyos/design-system 3.5.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flyos/design-system",
3
- "version": "3.5.0",
3
+ "version": "3.7.0",
4
4
  "description": "FlyOS design system — shared components, directives, pipes, services, and models for Business App developers.",
5
5
  "keywords": [
6
6
  "flyos",
@@ -63,9 +63,10 @@
63
63
  // compiled output, and this partial's whole contract is that it emits nothing.
64
64
  // stylelint-disable scss/at-mixin-pattern, scss/at-function-pattern
65
65
 
66
- // The three sanctioned blur tiers (skill §4) — chrome 36 / content panel 72 /
67
- // thin 16. Selected BY NAME; a fourth tier would have to be added here, in the
68
- // open, where `nova-glass.spec.ts` counts them.
66
+ // The four sanctioned blur tiers (skill §4) — chrome 36 / content panel 72 /
67
+ // thin 16 / window 50 (added when the window plate left the chrome family
68
+ // see `window` below). Selected BY NAME; a fifth tier would have to be added
69
+ // here, in the open, where `nova-glass.spec.ts` counts them.
69
70
  @mixin _backdrop($tier) {
70
71
  @if $tier == chrome {
71
72
  -webkit-backdrop-filter: blur(36px) saturate(180%);
@@ -77,8 +78,14 @@
77
78
  // The ratified quartet value: blur(16px) saturate(180%) brightness(.95).
78
79
  -webkit-backdrop-filter: var(--glass2-blur);
79
80
  backdrop-filter: var(--glass2-blur);
81
+ } @else if $tier == window {
82
+ // Ratified from the window's own recipe (`window` mixin below) — 50px, one
83
+ // step heavier than chrome, because it is the single largest glass surface
84
+ // in the shell and carries the richer specular/edge treatment alongside it.
85
+ -webkit-backdrop-filter: blur(50px) saturate(180%);
86
+ backdrop-filter: blur(50px) saturate(180%);
80
87
  } @else {
81
- @error 'nova-glass: unknown blur tier `#{$tier}`. The three sanctioned tiers are `chrome` (36px), `panel` (72px), `thin` (16px) — skill §4 says do not invent a fourth.';
88
+ @error 'nova-glass: unknown blur tier `#{$tier}`. The four sanctioned tiers are `chrome` (36px), `panel` (72px), `thin` (16px), `window` (50px) — skill §4 says do not invent a fifth.';
82
89
  }
83
90
  }
84
91
 
@@ -297,6 +304,76 @@
297
304
  @include _degrade;
298
305
  }
299
306
 
307
+ // ── Window ───────────────────────────────────────────────────────────────────
308
+ // The shared window plate (added when the one-glass-family ruling of
309
+ // 2026-08-17 was superseded — skill §2/§4 carry the dated note). Originally a
310
+ // bespoke, dark-theme-only material built by the Circles External App for its
311
+ // own embedded window (conic-gradient specular rim, a layered gradient fill,
312
+ // a distinct top/bottom edge split); promoted here as the platform default for
313
+ // every window, in both themes, so it lives in ONE recipe instead of being
314
+ // re-invented per app.
315
+ // Consumes `--window-bg` / `--window-border` / `--window-shadow`
316
+ // (`_theme-light.scss` / `_theme-dark.scss`, "Window chrome" section) — the
317
+ // SAME names the pre-existing window family always used, not a parallel set:
318
+ // `contextual-help-drawer`, `agent-overlay` and `agent-input` all deliberately
319
+ // borrow those three tokens to read as extensions of the window's own glass,
320
+ // so keeping the names carries the new recipe to them for free. Only
321
+ // `--window-bg` (the fill) diverges per theme; border/shadow are one value in
322
+ // both, exactly like before. The edge/rim treatment is new — a genuinely new
323
+ // concept, so it gets new token names (`--window-edge-*`) nothing else reads.
324
+ // This mixin does no Sass-side theme branching, same shape as `chrome`/`panel`
325
+ // above; blur/saturate are a Sass-level literal via `_backdrop(window)`, same
326
+ // as every other recipe here — `.window` no longer spends its own token pair
327
+ // for them (retired; nothing else read those two names either).
328
+ // Radius is deliberately NOT set here — `_window-shell.scss` keeps its own
329
+ // `border-radius: var(--shell-window-radius)`. Glass material and window
330
+ // corner geometry are separable; a 46px radius was tried once before (see that
331
+ // file's own header comment) and reverted as a footgun, independent of this
332
+ // mixin entirely.
333
+ @mixin window {
334
+ border: 1px solid var(--window-border);
335
+ background-image: var(--window-bg);
336
+ box-shadow: var(--window-shadow);
337
+
338
+ @include _backdrop(window);
339
+
340
+ // Refractive rim — the conic specular stroke. Same masked-ring technique
341
+ // `material()` uses for its own rim (hazard 2 in the header: the
342
+ // `backdrop-filter` above already makes this element a containing block, so
343
+ // the pseudo resolves with no `position: relative` of its own needed here).
344
+ &::before {
345
+ content: '';
346
+ position: absolute;
347
+ z-index: 0;
348
+ inset: 0;
349
+ padding: 1px;
350
+ border-radius: inherit;
351
+ background: var(--window-edge-gradient);
352
+ pointer-events: none;
353
+ -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
354
+ mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
355
+ -webkit-mask-composite: xor;
356
+ mask-composite: exclude;
357
+ }
358
+
359
+ // Top/bottom edge split — catches light along the top, grounds along the
360
+ // bottom, painted above content (z-index 3) so it reads at the outer 1px
361
+ // edge regardless of what the app renders underneath.
362
+ &::after {
363
+ content: '';
364
+ position: absolute;
365
+ z-index: 3;
366
+ inset: 0;
367
+ border-radius: inherit;
368
+ box-shadow:
369
+ inset 0 0.5px 0 var(--window-edge-top),
370
+ inset 0 -0.5px 0 var(--window-edge-bottom);
371
+ pointer-events: none;
372
+ }
373
+
374
+ @include _degrade;
375
+ }
376
+
300
377
  // ── Content panel ────────────────────────────────────────────────────────────
301
378
  // The surface holding app content — the strongest blur in the language (72px),
302
379
  // on the quartet's layer stack with the `--mat-panel` fill.
@@ -144,28 +144,87 @@ html.dark-theme {
144
144
  // family. No consumers in either theme; see `_theme-light.scss`.
145
145
 
146
146
  // ── Window chrome ───────────────────────────────────────────────────────────
147
- // ONE GLASS FAMILY (RULED 2026-08-17, second window ruling of the day): the
148
- // window wears the CHROME material the magic bar / nav rail recipe — not the
149
- // mock's content-panel plate. The first fidelity pass shipped `panelStyle`
150
- // verbatim (quartet glow + sheen over `--mat-panel` .55, blur 72), and it WAS
151
- // byte-exact a pixel-accurate render over `tropical.jpg` reproduced the shell
152
- // to the digit but the .55 plate over the darker wallpaper midfield read as a
153
- // separate, muddier glass beside the .38 chrome, and the owner ruled the shell
154
- // shows one family: flat `--mat-rail` fill, `--w16` hairline, the `--w2`
155
- // half-pixel inset, `blur(36px) saturate(180%)`. The drop pair stays the
156
- // design's window shadow (16/40 + 4/10) a floating window still needs a
157
- // window's elevation, and the shadow does not touch the plate colour.
158
- // The AA ladder re-measures on the thinner plate; sub-AA cells ride the exempt
159
- // rails in `theme-contrast.ts` as measured rows, same as every other pin.
160
- --window-bg: var(--mat-rail);
147
+ // SUPERSEDED 2026-08-2X the window leaves the chrome family. The
148
+ // one-glass-family ruling below (2026-08-17) held that a window sharing the
149
+ // rail/header's `--mat-rail` plate reads as one coherent material. That call
150
+ // is now explicitly reversed: the window (both themes) takes its own
151
+ // `glass.window` recipe (`_nova-glass.scss`) the visionOS-style rich
152
+ // material (conic specular rim, layered gradient fill, its own
153
+ // `--window-rich-*` token family below) originally built bespoke by the
154
+ // Circles External App for its dark-theme embedded window, promoted to the
155
+ // platform default. The rail/header/bottom-nav keep the flat chrome material
156
+ // unchanged; only the window plate diverges from them now the opposite
157
+ // call from 2026-08-17, made deliberately, by informed choice, after the
158
+ // accessibility floor this ruling protected was re-measured against the new
159
+ // fill (`theme-contrast.spec.ts`'s SURFACES rows for window text). The old
160
+ // `'the window wears the CHROME dressing'` identity test is retired along
161
+ // with the identity it asserted.
162
+
163
+ // Original ruling, kept for history (RULED 2026-08-17, second window ruling
164
+ // of that day): the window wears the CHROME material — the magic bar / nav
165
+ // rail recipe — not the mock's content-panel plate. The first fidelity pass
166
+ // shipped `panelStyle` verbatim (quartet glow + sheen over `--mat-panel` .55,
167
+ // blur 72), and it WAS byte-exact — a pixel-accurate render over
168
+ // `tropical.jpg` reproduced the shell to the digit — but the .55 plate over
169
+ // the darker wallpaper midfield read as a separate, muddier glass beside the
170
+ // .38 chrome, and the owner ruled the shell shows one family: flat
171
+ // `--mat-rail` fill, `--w16` hairline, the `--w2` half-pixel inset,
172
+ // `blur(36px) saturate(180%)`.
161
173
  --window-radius: 24px;
162
- --window-blur: 36px;
163
- --window-backdrop-saturate: 180%;
164
- --window-shadow: 0 16px 40px rgb(0 0 0 / 20%), 0 4px 10px rgb(0 0 0 / 10%);
165
- --window-border: var(--w16);
166
- --window-specular: inset 0 0.5px 0 var(--w2);
167
174
  --window-content-scrim: transparent;
168
175
 
176
+ // `--window-bg` / `--window-border` / `--window-shadow` are NOT renamed —
177
+ // `contextual-help-drawer.component.scss`, `agent-overlay.component.scss`
178
+ // and `agent-input.component.scss` all deliberately borrow these three to
179
+ // read as extensions of the window's own glass, so the shared names carry
180
+ // the new recipe forward to them for free. Only the FILL and the border step
181
+ // actually change value; the elevation shadow is untouched (Circles never
182
+ // redefined window-level elevation, only the plate).
183
+ --window-bg-alpha: 0.58;
184
+ --window-bg:
185
+ linear-gradient(180deg, var(--w05), transparent 20%),
186
+ linear-gradient(
187
+ rgb(22 22 24 / calc(var(--window-bg-alpha) * 100%)),
188
+ rgb(22 22 24 / calc(var(--window-bg-alpha) * 100%))
189
+ );
190
+ --window-border: var(--w2);
191
+ --window-shadow: 0 16px 40px rgb(0 0 0 / 20%), 0 4px 10px rgb(0 0 0 / 10%);
192
+
193
+ // `--window-blur` / `--window-backdrop-saturate` are RETIRED: `glass.window`
194
+ // (`_nova-glass.scss`) hardcodes its own tier via `_backdrop(window)`, same
195
+ // as every other nova-glass recipe (chrome/panel/thin never spent a token
196
+ // for their blur either) — `.window` no longer has its own hand-rolled
197
+ // backdrop-filter for a token to feed. Nothing else in the shell reads
198
+ // either name (`raw-backdrop-filter.spec.ts` confirms `_window-shell.scss`
199
+ // held the only consumer).
200
+
201
+ // `--window-specular` is RETIRED, replaced by the two-token edge split
202
+ // below — nothing else in the shell consumed it.
203
+
204
+ // ── Window edge / rim (`glass.window` only) ───────────────────────────────
205
+ // The conic specular rim + top/bottom edge split, extracted from Circles'
206
+ // shipped `circles-theme.scss` dark material and snapped to the nearest
207
+ // `--w*` ramp step (ramp law, skill §3: pick by name, never mint a new
208
+ // rgba) — Circles' stops (8/38/12/7/28/9%) land on exact or nearest-step
209
+ // matches (w08/w4/w12/w07/w28/w09). One value in both themes (mirrored
210
+ // verbatim in `_theme-light.scss`) — the ramp itself flips polarity per
211
+ // theme, which is the point of picking by step name.
212
+ --window-edge-gradient: conic-gradient(
213
+ from 210deg,
214
+ var(--w08),
215
+ var(--w4) 14%,
216
+ var(--w12) 34%,
217
+ var(--w07) 52%,
218
+ var(--w28) 74%,
219
+ var(--w09)
220
+ );
221
+ --window-edge-top: var(--w3);
222
+
223
+ // Grounding shadow, not ink — a literal, not a ramp step (the ramp is the
224
+ // ink/hairline family; this is a drop shadow, same convention `chrome`'s own
225
+ // box-shadow literals already use).
226
+ --window-edge-bottom: rgb(0 0 0 / 18%);
227
+
169
228
  // ── Titlebar ────────────────────────────────────────────────────────────────
170
229
  // The design's in-window header row (`MainContent.dc.html`): a `--w06` strip
171
230
  // over a `--w1` bottom hairline (the hairline is authored in
@@ -202,8 +261,14 @@ html.dark-theme {
202
261
  --glass-bg: #161618;
203
262
  --glass-bg-elevated: #1e1e22;
204
263
  --glass-blur: 0px;
264
+
265
+ // `--window-bg` still needs a flat opaque fallback here — the drawer/
266
+ // overlay/agent-input consumers above read this token directly, without
267
+ // going through `glass.window`'s own `_degrade()` ladder. `.window`
268
+ // itself is covered separately (the mixin's `_degrade()` flattens it to
269
+ // the generic `light-dark(#eef2f8, #2c2c2e)` plate, same as every other
270
+ // nova-glass recipe). `--window-blur` / `--window-backdrop-saturate` are
271
+ // retired above — nothing reads them any more, so no fallback needed.
205
272
  --window-bg: #141416;
206
- --window-blur: 0px;
207
- --window-backdrop-saturate: 100%;
208
273
  }
209
274
  }
@@ -161,38 +161,68 @@ html.light-theme {
161
161
  // the language actually has.
162
162
 
163
163
  // ── Window chrome ───────────────────────────────────────────────────────────
164
- // ONE GLASS FAMILY, light half (2026-08-18). Dark's ruling put the window on
165
- // the CHROME material — the magic bar / nav rail recipe and this block kept
166
- // the panel family whole: `.46`-class fill, the 72px panel blur, the `--w18`
167
- // rim and the `--w22` inset. Only the FILL ever had a reason.
168
- // —
169
- // The fill genuinely diverges and stays: the mock's static `--mat-panel` plate
170
- // (.46 pale slate) measures 2.74:1 for window BODY text over the dark-wallpaper
171
- // majority 2.0:1 for tertiary which is app content unreadable in the light
172
- // theme's common case, not a label on an adverse pairing. The exemption
173
- // mechanism is for edges, not for the main reading surface, so the adaptive
174
- // white `--fly-glass-alpha` composition stays: the thinnest white plate that
175
- // holds the weakest body ink at AA over the CURRENT wallpaper (0.6 floor, one
176
- // step more white over a dark room). Over bright rooms the pairing the mock
177
- // actually draws the two plates are near-identical anyway.
178
- //
179
- // The other three carried NO contrast argument. Blur radius, rim step and
180
- // specular step do not enter a contrast calculation at all they came along
181
- // because the panel recipe was copied whole and while they stayed, a light
182
- // window read as a different glass from the light rail beside it: the exact
183
- // complaint the dark ruling answered, still live on the other theme. They now
184
- // match `glass.chrome`: 36px tier, `--w16` hairline, `--w2` inset.
185
- // —
186
- // What is left is one honest divergence (the fill, with a measured floor)
187
- // instead of four, three of which nobody had decided.
188
- --window-bg: rgb(255 255 255 / calc(var(--fly-glass-alpha) * 100%));
164
+ // SUPERSEDED 2026-08-2X see `_theme-dark.scss`'s "Window chrome" section
165
+ // for the full supersession note; this is the light half of the same
166
+ // reversal. The window now takes `glass.window` in both themes instead of
167
+ // matching the rail/header chrome material. Only `--window-bg` diverges from
168
+ // dark the adaptive `--fly-glass-alpha` white plate, kept for the same
169
+ // measured reason the pre-supersession note below records (kept for
170
+ // history) everything else (border/shadow/edge/rim) is the identical
171
+ // value dark carries, mirrored verbatim rather than shared across files,
172
+ // matching how `--window-border`/`--window-shadow` always worked here.
173
+
174
+ // Original ruling, kept for history: ONE GLASS FAMILY, light half
175
+ // (2026-08-18). Dark's ruling put the window on the CHROME material the
176
+ // magic bar / nav rail recipe and this block kept the panel family whole:
177
+ // `.46`-class fill, the 72px panel blur, the `--w18` rim and the `--w22`
178
+ // inset. Only the FILL ever had a reason. The fill genuinely diverged and
179
+ // stayed: the mock's static `--mat-panel` plate (.46 pale slate) measured
180
+ // 2.74:1 for window BODY text over the dark-wallpaper majority2.0:1 for
181
+ // tertiary which is app content unreadable in the light theme's common
182
+ // case, not a label on an adverse pairing, so the adaptive white
183
+ // `--fly-glass-alpha` composition stayed: the thinnest white plate that
184
+ // holds the weakest body ink at AA over the CURRENT wallpaper.
189
185
  --window-radius: 24px;
190
- --window-blur: 36px;
191
- --window-backdrop-saturate: 180%;
192
- --window-shadow: 0 16px 40px rgb(0 0 0 / 20%), 0 4px 10px rgb(0 0 0 / 10%);
193
- --window-border: var(--w16);
194
- --window-specular: inset 0 0.5px 0 var(--w2);
195
186
  --window-content-scrim: transparent;
187
+
188
+ // `--window-bg` / `--window-border` / `--window-shadow` are NOT renamed —
189
+ // see the identical note in `_theme-dark.scss`. `--window-blur` /
190
+ // `--window-backdrop-saturate` are RETIRED (same reason: `glass.window`
191
+ // hardcodes its own tier, nothing else reads either name).
192
+
193
+ // UNCHANGED from before this recipe existed — deliberately, not an
194
+ // oversight: an earlier draft added dark's top-wash gradient layer here
195
+ // too (matching its two-layer shape), and it measured a real AA
196
+ // regression (`theme-contrast.spec.ts`, "window secondary text" dropped to
197
+ // 4.38:1 over the dark-wallpaper majority, needs 4.5) and broke the
198
+ // adaptive-alpha resolution `wallpaper-glass.service.ts` depends on (the
199
+ // knob test expects `--window-bg`'s alpha to resolve directly to
200
+ // `--fly-glass-alpha`, which a multi-layer composite no longer does
201
+ // simply). There is no Circles source for a light wash to extract in the
202
+ // first place (§ above), so the honest synthesis is: keep the proven-safe
203
+ // single-layer fill, and let the border/edge/rim carry the "richer in
204
+ // light too" story instead — none of those touch text contrast.
205
+ --window-bg: rgb(255 255 255 / calc(var(--fly-glass-alpha) * 100%));
206
+ --window-border: var(--w2);
207
+ --window-shadow: 0 16px 40px rgb(0 0 0 / 20%), 0 4px 10px rgb(0 0 0 / 10%);
208
+
209
+ // ── Window edge / rim (`glass.window` only) ───────────────────────────────
210
+ // No Circles source exists for light (its bespoke material only ever shipped
211
+ // scoped to `html.dark-theme`) — this is new design work, not extraction:
212
+ // the SAME `--w*` step names dark uses, letting the ramp's own per-theme
213
+ // polarity flip do the work (skill §3 — pick by name, never re-derive).
214
+ // `--window-specular` is RETIRED, replaced by the two-token edge split.
215
+ --window-edge-gradient: conic-gradient(
216
+ from 210deg,
217
+ var(--w08),
218
+ var(--w4) 14%,
219
+ var(--w12) 34%,
220
+ var(--w07) 52%,
221
+ var(--w28) 74%,
222
+ var(--w09)
223
+ );
224
+ --window-edge-top: var(--w3);
225
+ --window-edge-bottom: rgb(0 0 0 / 18%);
196
226
  --app-content-plate: transparent;
197
227
  --titlebar-bg: var(--w06);
198
228
  --titlebar-height: 44px;
@@ -269,8 +299,9 @@ html.light-theme {
269
299
  --glass-bg: #f7f8fa;
270
300
  --glass-bg-elevated: #fff;
271
301
  --glass-blur: 0px;
302
+
303
+ // `--window-blur` / `--window-backdrop-saturate` are retired — see the
304
+ // identical note in `_theme-dark.scss`'s reduced-transparency block.
272
305
  --window-bg: #f7f8fa;
273
- --window-blur: 0px;
274
- --window-backdrop-saturate: 100%;
275
306
  }
276
307
  }
@@ -8790,10 +8790,41 @@ declare class FlyMagicActionsComponent {
8790
8790
  * string instead — {@link MagicBarActionSpec.iconPath} stays `string`.
8791
8791
  */
8792
8792
  declare const FLY_MAGIC_BAR_ICONS: {
8793
+ /**
8794
+ * "Download …" — an action that hands the user a FILE (Export to Excel/PDF, a
8795
+ * generated report). Arrow down onto a baseline, the Material-style download mark.
8796
+ *
8797
+ * Distinct from {@link VERBATIM_FROM_UX_DROP.export} on purpose, and the distinction
8798
+ * is the point of this entry. The drop's Export/Import pair reads the tray as the
8799
+ * APP — data leaves it upward on export — which is coherent but inverts the reading
8800
+ * users bring to a file transfer, where down means "arriving on my machine". Every
8801
+ * consumer of `export` on the platform is in fact a download, so the up arrow was
8802
+ * being read as "upload" on all of them. `export` keeps its drop geometry for a
8803
+ * genuine data-exchange pair; anything that produces a file for the user takes this.
8804
+ *
8805
+ * The baseline rather than a tray also keeps it apart from `import`, which is the
8806
+ * tray-and-down-arrow shape a download glyph would otherwise collide with.
8807
+ */
8808
+ readonly download: "<path d=\"M12 4v11M7.5 10.5l4.5 4.5 4.5-4.5\"/><path d=\"M5 20h14\"/>";
8793
8809
  /** Signal-detail "View trace" — no standalone path in the drop; an activity/pulse line. */
8794
8810
  readonly viewTrace: "<path d=\"M3 12h4l2-7 4 14 2-7h6\"/>";
8795
8811
  /** Signal-detail "Archive". */
8796
8812
  readonly archive: "<rect x=\"3\" y=\"4\" width=\"18\" height=\"4\" rx=\"1\"/><path d=\"M5 8v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8\"/><path d=\"M10 13h4\"/>";
8813
+ /**
8814
+ * "Reopen …" / "Resubmit …" — reversing a terminal state, the counterpart to
8815
+ * {@link ORIGINAL_SAME_CONVENTION.archive}. Same lidded box, with an up arrow where
8816
+ * archive draws its flat line: the Material archive/unarchive pairing.
8817
+ *
8818
+ * Deliberately NOT a counter-clockwise arc, which is the other common restore mark:
8819
+ * it would be a near-mirror of {@link VERBATIM_FROM_UX_DROP.refresh} and the two are
8820
+ * indistinguishable at the 15px these render at. Pairing with `archive` also earns its
8821
+ * keep in context — Thoughts' idea lifecycle menu draws archive and resubmit as
8822
+ * adjacent rows, so the two glyphs read as one opposed pair rather than two arrows.
8823
+ *
8824
+ * The box is closed with a lid, which is what keeps it apart from
8825
+ * {@link VERBATIM_FROM_UX_DROP.export}: that arrow rises out of an open-topped TRAY.
8826
+ */
8827
+ readonly restore: "<rect x=\"3\" y=\"4\" width=\"18\" height=\"4\" rx=\"1\"/><path d=\"M5 8v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8\"/><path d=\"M12 17.5v-5.5M9.5 14.5l2.5-2.5 2.5 2.5\"/>";
8797
8828
  /** Task-list "Sort by" trigger. */
8798
8829
  readonly sort: "<path d=\"M7 4v16\"/><path d=\"M3 8l4-4 4 4\"/><path d=\"M17 20V4\"/><path d=\"M13 16l4 4 4-4\"/>";
8799
8830
  /** Task-detail "Add audience". */
@@ -8829,6 +8860,15 @@ declare const FLY_MAGIC_BAR_ICONS: {
8829
8860
  readonly export: "<path d=\"M12 16V7M8 11l4-4 4 4\"/><path d=\"M4 15v3a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-3\"/>";
8830
8861
  /** `Home.dc.html:616`. */
8831
8862
  readonly import: "<path d=\"M12 4v9M8 9l4 4 4-4\"/><path d=\"M4 15v3a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-3\"/>";
8863
+ /**
8864
+ * `Home.dc.html:362` — the chrome's own reload arrow, reused verbatim so a
8865
+ * contributed "Refresh" action reads identically to the shell's reload button
8866
+ * sitting a few pixels away in the same bar.
8867
+ *
8868
+ * NEVER mirror this under RTL. Its arrow is circular, and the mirrored form reads
8869
+ * as "undo" (design §9 rule 7: only directional arrow/chevron glyphs mirror).
8870
+ */
8871
+ readonly refresh: "<path d=\"M20.2 12a8.2 8.2 0 1 1-2.7-6.1\"/><path d=\"M20.4 4.1v4.6h-4.6\"/>";
8832
8872
  /** `Home.dc.html:547` — "Mark as complete". */
8833
8873
  readonly check: "<circle cx=\"12\" cy=\"12\" r=\"8.6\"/><path d=\"M8.4 12.3l2.6 2.6 4.6-5\"/>";
8834
8874
  /** `Home.dc.html:550` — "Watch task". */
@@ -12737,10 +12777,15 @@ declare class FlyModuleIconDirective {
12737
12777
  * Standalone (no dock anywhere above), nothing happens and the bar renders as the
12738
12778
  * app's own first row, exactly as before.
12739
12779
  *
12740
- * a11y: the trigger is `aria-haspopup="menu"` + `aria-expanded`; the popover is
12741
- * `role="menu"` with `role="menuitem"` rows carrying `aria-current="page"` on
12742
- * the active one. Arrow keys rove (RTL-aware, wrapping, skipping disabled),
12743
- * Home/End jump, Escape closes and returns focus to the trigger, and a click
12780
+ * The trigger is split in two: the anchor (icon + label) is a breadcrumb —
12781
+ * click it and it re-activates the current module, the same "back to the
12782
+ * register" nav a popover row for it already performs, without opening the
12783
+ * popover. The chevron alone is the switcher's `aria-haspopup="menu"` +
12784
+ * `aria-expanded` trigger. The popover is `role="menu"` with `role="menuitem"`
12785
+ * rows carrying `aria-current="page"` on the active one.
12786
+ *
12787
+ * a11y: arrow keys rove the popover (RTL-aware, wrapping, skipping disabled),
12788
+ * Home/End jump, Escape closes and returns focus to the chevron, and a click
12744
12789
  * outside closes. Only the topmost overlay answers Escape, so a switcher opened
12745
12790
  * over a drawer closes alone.
12746
12791
  */
@@ -12784,12 +12829,21 @@ declare class FlyAppTopbarComponent {
12784
12829
  protected readonly activeModule: _angular_core.Signal<FlyAppModule | null>;
12785
12830
  protected readonly brandAriaLabel: _angular_core.Signal<string>;
12786
12831
  protected readonly switchAriaLabel: _angular_core.Signal<string>;
12787
- protected readonly triggerAriaLabel: _angular_core.Signal<string>;
12788
12832
  constructor();
12789
12833
  private dockIntoHostChrome;
12790
12834
  protected iconFor(key: string): TemplateRef<unknown> | null;
12791
12835
  protected indexOf(mod: FlyAppModule): number;
12792
12836
  protected toggle(): void;
12837
+ /**
12838
+ * The anchor (icon + label) is a breadcrumb crumb, not the switcher's
12839
+ * trigger — that's the chevron's job now. Clicking it re-activates the
12840
+ * CURRENT module, the same "back to the register" navigation a click on its
12841
+ * own row in the popover already performs, without opening the popover to
12842
+ * get there. Only when nothing is active yet (the neutral "Select a
12843
+ * module" state, nothing to go back to) does it fall back to opening the
12844
+ * switcher, matching the old combined-trigger behaviour for that state.
12845
+ */
12846
+ protected onAnchorClick(): void;
12793
12847
  private openMenu;
12794
12848
  protected close(restoreFocus?: boolean): void;
12795
12849
  protected select(mod: FlyAppModule): void;