@the-portland-company/shell 0.97.0 → 0.98.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/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ## 0.98.0
2
+
3
+ The one VRM Verified badge (Contacts v59 §4, decided 2026-11-05, Ben: "Every badge is the brand `verifiedIcon`, drawn by one shared shell component"; Data-System/VRM-Verified v7 §2, §2.6).
4
+
5
+ - **New `VrmVerifiedBadge`** (on `.` and `./native`): `tier` (`verified | identified | anonymous | stale | inactive | null`), `size`, `showLabel`. Verified draws the Brand `assets.verifiedIcon` read at runtime from the token sheet (`--asset-verified-icon`), never a bundled copy; until the sheet loads its box is reserved and nothing stands in. Other tiers draw a dot (colour from `--vrm-tier-<tier>`, defaults as the apps draw them today per Contacts §4.1) and the tier name. The Verified tooltip is the spec's sentence, "Politogy has confirmed this is a real, registered voter."; no tooltip suggests permission to text or email. Also exports `VRM_VERIFIED_TOOLTIP`, `VRM_TIER_LABELS`, types `VrmVerifiedTier`, `VrmVerifiedBadgeProps`.
6
+ - **New `useBrandAsset(kind)`** and `BrandAssetKind` gains `'verified-icon'`.
7
+ - Tier dot colours per VRM-Verified v8 (decided 2026-11-05, Ben): Identified purple, Anonymous black, Stale yellow, Inactive gray; Verified is the brand badge. They are CSS variables (`--vrm-tier-<tier>`).
8
+ - Tests: `__tests__/vrmVerifiedBadge.test.tsx` (new).
9
+
10
+ ## 0.97.1
11
+
12
+ Two left-bar and top-bar fixes (Ben 2026-11-05, typed in the UI debugging box; specs updated with his go).
13
+
14
+ - **Top-bar tooltips stay on screen.** `TopBarTooltip` gains `align?: 'center' | 'end'` (default `center`). `end` lines the body up with the trigger's right edge and keeps the arrow on the trigger's center. `TopBarLinkAction` (Organization settings, My profile) uses `end`. Measured at 1547px before the fix: the centered 224px body ran 75px past the viewport for My profile and 35px for Organization settings.
15
+ - **One quiet build line.** The left-bar footer's two build rows (`env | commit | version`, `date | author`) are now one line, `v<version> · <Mon D>`, 10px mono in the inactive-row gray (`--sidebar-row-text`: 66% white, `#F0CFD2` on the Campaign rail; both clear 4.5:1). The full build info (env, commit, version, date, author) is the line's hover title. New export `buildLineParts()`. Specs/Left-Navigation §10, Reference/UI-Platform → Debug info.
16
+ - Tests: `__tests__/tooltipEdge.test.tsx` (new), `__tests__/native.test.tsx`, `__tests__/sidebarContrast.test.ts` (the contrast check now targets the build line's `--sidebar-row-text`).
17
+
1
18
  ## 0.97.0
2
19
 
3
20
  Top bar and left bar rework (Ben 2026-11-04, typed in the UI debugging box; specs updated with his go).
@@ -622,19 +622,26 @@ function TopBarCountBadge({ count }) {
622
622
  }
623
623
  function TopBarTooltip({
624
624
  title,
625
- description
625
+ description,
626
+ align = "center"
626
627
  }) {
628
+ const bodyPosition = align === "end" ? "right-0" : "left-1/2 -translate-x-1/2";
629
+ const arrowPosition = align === "end" ? "" : "left-1/2 -translate-x-1/2";
630
+ const arrowStyle = align === "end" ? { right: "calc(var(--topbar-icon-button-size, 34px) / 2 - 5px)" } : void 0;
627
631
  return /* @__PURE__ */ jsxRuntime.jsxs(
628
632
  "span",
629
633
  {
630
634
  role: "tooltip",
631
- className: "pointer-events-none absolute left-1/2 top-full z-50 mt-2 hidden w-max max-w-56 -translate-x-1/2 flex-col rounded-lg border border-[color:var(--app-btn-tooltip-bg,#2F3136)] bg-[color:var(--app-btn-tooltip-bg,#2F3136)] px-3 py-2 text-left shadow-[var(--shadow-soft)] group-hover/tbtip:flex group-focus-visible/tbtip:flex",
635
+ "data-tooltip-align": align,
636
+ className: `pointer-events-none absolute ${bodyPosition} top-full z-50 mt-2 hidden w-max max-w-56 flex-col rounded-lg border border-[color:var(--app-btn-tooltip-bg,#2F3136)] bg-[color:var(--app-btn-tooltip-bg,#2F3136)] px-3 py-2 text-left shadow-[var(--shadow-soft)] group-hover/tbtip:flex group-focus-visible/tbtip:flex`,
632
637
  children: [
633
638
  /* @__PURE__ */ jsxRuntime.jsx(
634
639
  "span",
635
640
  {
636
641
  "aria-hidden": true,
637
- className: "absolute -top-[5px] left-1/2 h-2.5 w-2.5 -translate-x-1/2 rotate-45 border-l border-t border-[color:var(--app-btn-tooltip-bg,#2F3136)] bg-[color:var(--app-btn-tooltip-bg,#2F3136)]"
642
+ "data-tooltip-arrow": true,
643
+ style: arrowStyle,
644
+ className: `absolute -top-[5px] ${arrowPosition} h-2.5 w-2.5 rotate-45 border-l border-t border-[color:var(--app-btn-tooltip-bg,#2F3136)] bg-[color:var(--app-btn-tooltip-bg,#2F3136)]`
638
645
  }
639
646
  ),
640
647
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-[color:var(--app-btn-tooltip-fg,#FFFFFF)]", children: title }),
@@ -1576,7 +1583,7 @@ var NAV_DISPLAY_OPTIONS = [
1576
1583
  function TopBarLinkAction({ label, description, href, icon: Icon }) {
1577
1584
  return /* @__PURE__ */ jsxRuntime.jsxs("a", { href, "aria-label": label, className: topBarIconButtonClass(), children: [
1578
1585
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: TOPBAR_GLYPH_SIZE, className: "text-current" }),
1579
- /* @__PURE__ */ jsxRuntime.jsx(TopBarTooltip, { title: label, description })
1586
+ /* @__PURE__ */ jsxRuntime.jsx(TopBarTooltip, { title: label, description, align: "end" })
1580
1587
  ] });
1581
1588
  }
1582
1589
  function OrganizationSettingsAction({ href }) {
@@ -2123,6 +2130,33 @@ function shortEnvLabel(label) {
2123
2130
  if (l === "development") return "dev";
2124
2131
  return l;
2125
2132
  }
2133
+ function buildLineParts(meta) {
2134
+ const version = meta.version ? `v${meta.version.replace(/^v/, "")}` : null;
2135
+ const parsed = meta.commitDate ? new Date(meta.commitDate) : null;
2136
+ const date = parsed && !Number.isNaN(parsed.getTime()) ? parsed.toLocaleDateString(void 0, { month: "short", day: "numeric" }) : null;
2137
+ const line = [version, date].filter(Boolean).join(" \xB7 ");
2138
+ const detail = [
2139
+ meta.envLabel ? shortEnvLabel(meta.envLabel) : null,
2140
+ meta.commit ? meta.commit.slice(0, 12) : null,
2141
+ version,
2142
+ date,
2143
+ meta.commitAuthor ? meta.commitAuthor.split(/\s+/)[0] : null
2144
+ ].filter(Boolean).join(" | ");
2145
+ return { line, detail };
2146
+ }
2147
+ function ShellBuildLine({ footer, hidden }) {
2148
+ const { line, detail } = buildLineParts(footer);
2149
+ if (!line && !detail) return null;
2150
+ return /* @__PURE__ */ jsxRuntime.jsx(
2151
+ "div",
2152
+ {
2153
+ "data-shell-meta": true,
2154
+ title: detail,
2155
+ className: `mt-1.5 truncate text-center font-[family-name:var(--font-mono,ui-monospace,monospace)] text-[10px] leading-[1.2] text-[color:var(--sidebar-row-text,rgba(255,255,255,0.66))] ${hidden}`,
2156
+ children: line || detail
2157
+ }
2158
+ );
2159
+ }
2126
2160
  var ORG_PILL_DEFAULT = "var(--color-accent-bg-safe,var(--color-accent))";
2127
2161
  var RailCollapsibleContext = react.createContext(false);
2128
2162
  function useRailCollapse() {
@@ -2615,31 +2649,7 @@ function NavSidebar({
2615
2649
  }
2616
2650
  )
2617
2651
  ] }, l.label)) }) : null,
2618
- footer.envLabel || footer.commit || footer.version ? /* @__PURE__ */ jsxRuntime.jsxs(
2619
- "div",
2620
- {
2621
- "data-shell-meta": true,
2622
- className: `mt-1.5 flex flex-col items-center gap-0.5 font-[family-name:var(--font-mono,ui-monospace,monospace)] text-[11px] text-[color:var(--sidebar-meta-text,rgba(255,255,255,0.72))] ${rc("max-md:hidden")}`,
2623
- children: [
2624
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1.5", children: [
2625
- footer.envLabel ? shortEnvLabel(footer.envLabel) : null,
2626
- footer.commit ? footer.commit.slice(-4) : null,
2627
- footer.version ? `v${footer.version.replace(/^v/, "")}` : null
2628
- ].filter((v) => Boolean(v)).map((v, i) => /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
2629
- i > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: "|" }) : null,
2630
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: v })
2631
- ] }, i)) }),
2632
- footer.commitDate || footer.commitAuthor ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
2633
- footer.commitDate ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: new Date(footer.commitDate).toLocaleDateString(void 0, {
2634
- month: "short",
2635
- day: "numeric"
2636
- }) }) : null,
2637
- footer.commitDate && footer.commitAuthor ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: "|" }) : null,
2638
- footer.commitAuthor ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: footer.commitAuthor.split(/\s+/)[0] }) : null
2639
- ] }) : null
2640
- ]
2641
- }
2642
- ) : null
2652
+ footer.envLabel || footer.commit || footer.version ? /* @__PURE__ */ jsxRuntime.jsx(ShellBuildLine, { footer, hidden: rc("max-md:hidden") }) : null
2643
2653
  ] }) : null
2644
2654
  ]
2645
2655
  }