@justai/cuts 0.61.0 → 0.63.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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/src/Toolbar.astro +47 -2
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@justai/cuts",
3
- "version": "0.61.0",
4
- "description": "A persona's named parts — the page shell that holds them, and the cuts themselves.",
3
+ "version": "0.63.0",
4
+ "description": "A persona's named parts \u2014 the page shell that holds them, and the cuts themselves.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
7
7
  "type": "git",
package/src/Toolbar.astro CHANGED
@@ -17,6 +17,14 @@
17
17
  // • BackToZone — takes no props → internalised here (the persona no longer imports it).
18
18
  // • the three nav aria-labels — resolved strings (like every other cut: Posts, LangPicker) → props.
19
19
  // • kernelIcon — the ONE per-persona nav glyph (⚡ / clock / globe …) → required slot.
20
+ // • profileIcon — OPTIONAL. The persona's own mark, in place of the generic bust. The Profile cut is
21
+ // WHO, so its icon may say *whose* profile this is rather than that a profile exists; the face is the
22
+ // persona's, so it comes from the persona. Omit it and the bust stays — adopting this is each
23
+ // persona's own decision, taken when its mark exists, and nothing moves in the fleet without it.
24
+ // Note what a mark cannot do: an <img> ignores `currentColor`, so `.active`'s accent never reaches
25
+ // it. It is not replaced with anything: the title strip already closes on this cut and opens off
26
+ // it, and a mark is an identity rather than a UI state. Slot presence adds `.tb-mark`, which the
27
+ // sizing rule uses.
20
28
  // • langPicker — needs the persona's own locale data (the list must NOT live in the shared package —
21
29
  // it bit us twice; see @justai/ui/locale) → a slot the persona fills with its <LangPicker/>.
22
30
  // • navExtra — optional extra nav item after Posts (e.g. the timers' fullscreen button) → slot.
@@ -52,8 +60,28 @@ const { sectionsLabel, profileLabel, kernelLabel, postsLabel, home = true, files
52
60
  <div class="tb-left">
53
61
  {home && <BackToZone />}
54
62
  <nav class="tb-nav-group" aria-label={sectionsLabel}>
55
- <a class="tb-nav" id="navProfile" href="#profile" aria-label={profileLabel} title={profileLabel}>
56
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
63
+ {/* The Profile cut is WHO, so a persona may put its OWN FACE here instead of the generic bust —
64
+ the icon then says whose profile this is rather than that a profile exists. Passed as a slot
65
+ for the same reason `kernelIcon` is: the face belongs to the persona, not to the shell.
66
+
67
+ The fallback matters as much as the slot. A persona that passes nothing keeps the bust it has
68
+ today, so adopting this is a decision each persona makes when its own mark is ready — nothing
69
+ in the fleet moves on the day this ships.
70
+
71
+ `tb-mark` is set from slot PRESENCE, not from a prop somebody could forget to keep in step. It
72
+ exists so the sizing rule can find the image; it deliberately does NOT restyle the mark. */}
73
+ <a
74
+ class:list={["tb-nav", { "tb-mark": Astro.slots.has("profileIcon") }]}
75
+ id="navProfile"
76
+ href="#profile"
77
+ aria-label={profileLabel}
78
+ title={profileLabel}
79
+ >
80
+ {Astro.slots.has("profileIcon") ? (
81
+ <slot name="profileIcon" />
82
+ ) : (
83
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
84
+ )}
57
85
  </a>
58
86
  <a class="tb-nav" id="navKernel" href="#kernel" aria-label={kernelLabel} title={kernelLabel}>
59
87
  <slot name="kernelIcon" />
@@ -144,6 +172,23 @@ const { sectionsLabel, profileLabel, kernelLabel, postsLabel, home = true, files
144
172
  .tb-nav:hover { color: var(--text); }
145
173
  .tb-nav:active { transform: scale(0.9); }
146
174
  .tb-nav.active { color: var(--accent); }
175
+
176
+ /* A MARK IS NOT DIMMED, AND THE PROFILE CUT DOES NOT NEED IT TO BE.
177
+ It shipped dimmed at rest for one version, on the reasoning that a mark cannot take `currentColor`
178
+ and so needed opacity to say "you are here". Both halves of that were true and the conclusion was
179
+ still wrong, because the state was already being said, louder: `titleFor` in @justai/core returns an
180
+ empty string for `profile`, so the title strip CLOSES on this cut and opens — 26px of layout, naming
181
+ where you are — the moment you leave it. Against that, tinting an icon adds nothing.
182
+ And the two are not the same kind of thing. The three stroke icons are generic glyphs whose colour IS
183
+ a UI state; a mark is an identity. A faded logo does not read as "not current", it reads as disabled.
184
+ So the mark stays itself. Press feedback is not lost: `.tb-nav:active`'s scale applies to it like any
185
+ other item. `.tb-mark` remains, because the sizing rule below still needs to know an image is here. */
186
+ /* The shell sizes the mark and says NOTHING about `display`, deliberately. Two files arrive for the two
187
+ themes — an SVG's internal `prefers-color-scheme` does not survive <img>, measured — and the persona
188
+ hides one with a media query. A `display: block` here would be two classes deep against the persona's
189
+ one and would WIN, showing both marks stacked. That exact mistake shipped once in this shell and the
190
+ founder counted the duplicate logos before anyone read the CSS. Width and height only. */
191
+ .tb-nav.tb-mark :global(img) { width: 19px; height: 19px; }
147
192
  .tb-nav :global(svg) { width: 19px; height: 19px; }
148
193
  </style>
149
194