@the-portland-company/shell 0.36.3 → 0.37.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 +18 -0
- package/dist/app-chrome.cjs +124 -16
- package/dist/app-chrome.cjs.map +1 -1
- package/dist/app-chrome.js +50 -15
- package/dist/app-chrome.js.map +1 -1
- package/dist/chunk-6UHJV2FH.js +63 -0
- package/dist/chunk-6UHJV2FH.js.map +1 -0
- package/dist/{chunk-LIGKGXFT.js → chunk-CGSS2PDG.js} +20 -6
- package/dist/chunk-CGSS2PDG.js.map +1 -0
- package/dist/native.cjs +18 -3
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +18 -1
- package/dist/native.d.ts +18 -1
- package/dist/native.js +1 -1
- package/dist/nav-preference.cjs +75 -0
- package/dist/nav-preference.cjs.map +1 -0
- package/dist/nav-preference.d.cts +18 -0
- package/dist/nav-preference.d.ts +18 -0
- package/dist/nav-preference.js +3 -0
- package/dist/nav-preference.js.map +1 -0
- package/package.json +6 -1
- package/dist/chunk-LIGKGXFT.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
## 0.37.0 — 2026-07-27
|
|
2
|
+
|
|
3
|
+
### Fixed — organization logo renders in every app, not just VRM core
|
|
4
|
+
|
|
5
|
+
`organizations.logo_url` is authored by VRM core and stored as a ROOT-RELATIVE
|
|
6
|
+
core route (`/api/storage/public?bucket=organization-logos&path=<org>/logo.png`
|
|
7
|
+
— core's signed-storage proxy). VRM core rendered it fine because the path
|
|
8
|
+
resolved against its own origin; every feature app resolved the same path
|
|
9
|
+
against ITS origin (`forms.politogyvrm.com/api/storage/public`), 404'd, and
|
|
10
|
+
showed a broken image in the sidebar org pill and the org dropdown.
|
|
11
|
+
|
|
12
|
+
`resolveOrgLogoUrl` (exported from `./native`) now absolutizes a relative logo
|
|
13
|
+
path against `CORE_APP_ORIGIN` at the point of render, for all three org-badge
|
|
14
|
+
sites (active pill, dropdown rows, non-switcher pill). Absolute, protocol-
|
|
15
|
+
relative and `data:` URLs pass through untouched; empty values still fall back
|
|
16
|
+
to initials. This is the same treatment `absolutizeAvatarUrl` already applied to
|
|
17
|
+
`profiles.avatar_url` in the chrome API — the org logo was simply missed.
|
|
18
|
+
|
|
1
19
|
## 0.36.0 — 2026-07-24
|
|
2
20
|
|
|
3
21
|
### Added — cross-subdomain active-org + favicon cache-busting
|
package/dist/app-chrome.cjs
CHANGED
|
@@ -150,6 +150,12 @@ var CORE_APP_ORIGIN = "https://app.politogyvrm.com";
|
|
|
150
150
|
var CORE_BILLING_PATH = "/organization-settings/subscription-billing/text-email";
|
|
151
151
|
var CORE_BILLING_URL = `${CORE_APP_ORIGIN}${CORE_BILLING_PATH}`;
|
|
152
152
|
var QUARANTINED_CONTACTS_URL = `${CORE_APP_ORIGIN}/relationship/contacts/quarantined`;
|
|
153
|
+
function resolveOrgLogoUrl(url) {
|
|
154
|
+
const raw = url?.trim();
|
|
155
|
+
if (!raw) return void 0;
|
|
156
|
+
if (/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(raw)) return raw;
|
|
157
|
+
return `${CORE_APP_ORIGIN}${raw.startsWith("/") ? "" : "/"}${raw}`;
|
|
158
|
+
}
|
|
153
159
|
function topBarIconButtonClass(tone = "default") {
|
|
154
160
|
const toneClass = tone === "danger" || tone === "warning" ? "text-[color:var(--color-race-red)] hover:text-[color:var(--color-race-red)]" : "text-[color:var(--topbar-icon-color,var(--muted))] hover:text-[color:var(--topbar-icon-hover-color,var(--foreground))] hover:bg-[color:var(--topbar-icon-hover-bg,transparent)]";
|
|
155
161
|
return [
|
|
@@ -1588,6 +1594,7 @@ function NavSidebar({
|
|
|
1588
1594
|
const activeFn = nav.isActive ?? isNavItemActive;
|
|
1589
1595
|
const ModeIcon = mode?.icon;
|
|
1590
1596
|
const orgBg = org?.brandColor || ORG_PILL_DEFAULT;
|
|
1597
|
+
const orgLogoSrc = resolveOrgLogoUrl(org?.logoUrl);
|
|
1591
1598
|
const activeGroupKey = react.useMemo(() => {
|
|
1592
1599
|
for (let i = 0; i < nav.items.length; i++) {
|
|
1593
1600
|
const item = nav.items[i];
|
|
@@ -1655,7 +1662,7 @@ function NavSidebar({
|
|
|
1655
1662
|
{
|
|
1656
1663
|
style: { backgroundColor: orgBg },
|
|
1657
1664
|
className: "flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-full text-[11px] font-bold text-white",
|
|
1658
|
-
children:
|
|
1665
|
+
children: orgLogoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: orgLogoSrc, alt: "", className: "h-full w-full object-cover" }) : (org.name ?? "Org").slice(0, 2).toUpperCase()
|
|
1659
1666
|
}
|
|
1660
1667
|
),
|
|
1661
1668
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1690,7 +1697,14 @@ function NavSidebar({
|
|
|
1690
1697
|
{
|
|
1691
1698
|
style: { backgroundColor: o.brandColor || ORG_PILL_DEFAULT },
|
|
1692
1699
|
className: "flex h-6 w-6 shrink-0 items-center justify-center overflow-hidden rounded-full text-[10px] font-bold text-white",
|
|
1693
|
-
children: o.logoUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1700
|
+
children: resolveOrgLogoUrl(o.logoUrl) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1701
|
+
"img",
|
|
1702
|
+
{
|
|
1703
|
+
src: resolveOrgLogoUrl(o.logoUrl),
|
|
1704
|
+
alt: "",
|
|
1705
|
+
className: "h-full w-full object-cover"
|
|
1706
|
+
}
|
|
1707
|
+
) : (o.name ?? "Org").slice(0, 2).toUpperCase()
|
|
1694
1708
|
}
|
|
1695
1709
|
),
|
|
1696
1710
|
/* @__PURE__ */ jsxRuntime.jsx("span", { title: o.name ?? o.id, className: "truncate", children: o.name ?? o.id.slice(0, 8) })
|
|
@@ -1712,7 +1726,7 @@ function NavSidebar({
|
|
|
1712
1726
|
{
|
|
1713
1727
|
style: { backgroundColor: orgBg },
|
|
1714
1728
|
className: "flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-full text-[11px] font-bold text-white",
|
|
1715
|
-
children:
|
|
1729
|
+
children: orgLogoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: orgLogoSrc, alt: "", className: "h-full w-full object-cover" }) : (org.name ?? "Org").slice(0, 2).toUpperCase()
|
|
1716
1730
|
}
|
|
1717
1731
|
),
|
|
1718
1732
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold", children: org.name ?? "No organizations" })
|
|
@@ -2535,6 +2549,66 @@ function resolveActiveOrg(search) {
|
|
|
2535
2549
|
return readActiveOrgCookie();
|
|
2536
2550
|
}
|
|
2537
2551
|
|
|
2552
|
+
// src/nav-preference.ts
|
|
2553
|
+
var NAV_DISPLAY_STORAGE_KEY = "politogy_nav_display";
|
|
2554
|
+
var NAV_DISPLAY_COOKIE = "politogy_nav_display";
|
|
2555
|
+
var LEGACY_NAV_DISPLAY_KEY = "vrm.navDisplay";
|
|
2556
|
+
var NAV_DISPLAY_DEFAULT = "both";
|
|
2557
|
+
var VALID2 = ["menu", "tabs", "both"];
|
|
2558
|
+
function isNavDisplayPreference(v) {
|
|
2559
|
+
return typeof v === "string" && VALID2.includes(v);
|
|
2560
|
+
}
|
|
2561
|
+
function readNavDisplayCookie() {
|
|
2562
|
+
if (typeof document === "undefined") return null;
|
|
2563
|
+
try {
|
|
2564
|
+
const m = document.cookie.match(
|
|
2565
|
+
new RegExp(`(?:^|;\\s*)${NAV_DISPLAY_COOKIE}=([^;]+)`)
|
|
2566
|
+
);
|
|
2567
|
+
const v = m ? decodeURIComponent(m[1]) : null;
|
|
2568
|
+
return isNavDisplayPreference(v) ? v : null;
|
|
2569
|
+
} catch {
|
|
2570
|
+
return null;
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
function writeNavDisplayCookie(pref) {
|
|
2574
|
+
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
2575
|
+
const host = window.location.hostname;
|
|
2576
|
+
const domainAttr = /(^|\.)politogyvrm\.com$/i.test(host) ? "; Domain=.politogyvrm.com" : "";
|
|
2577
|
+
const secure = window.location.protocol === "https:" ? "; Secure" : "";
|
|
2578
|
+
document.cookie = `${NAV_DISPLAY_COOKIE}=${encodeURIComponent(
|
|
2579
|
+
pref
|
|
2580
|
+
)}; Path=/; Max-Age=31536000; SameSite=Lax${domainAttr}${secure}`;
|
|
2581
|
+
}
|
|
2582
|
+
function readNavDisplayLocal() {
|
|
2583
|
+
if (typeof localStorage === "undefined") return null;
|
|
2584
|
+
try {
|
|
2585
|
+
const current = localStorage.getItem(NAV_DISPLAY_STORAGE_KEY);
|
|
2586
|
+
if (isNavDisplayPreference(current)) return current;
|
|
2587
|
+
const legacy = localStorage.getItem(LEGACY_NAV_DISPLAY_KEY);
|
|
2588
|
+
return isNavDisplayPreference(legacy) ? legacy : null;
|
|
2589
|
+
} catch {
|
|
2590
|
+
return null;
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
function writeNavDisplayLocal(pref) {
|
|
2594
|
+
if (typeof localStorage === "undefined") return;
|
|
2595
|
+
try {
|
|
2596
|
+
localStorage.setItem(NAV_DISPLAY_STORAGE_KEY, pref);
|
|
2597
|
+
} catch {
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
function resolveNavDisplay() {
|
|
2601
|
+
return readNavDisplayCookie() ?? readNavDisplayLocal() ?? NAV_DISPLAY_DEFAULT;
|
|
2602
|
+
}
|
|
2603
|
+
`(function(){try{
|
|
2604
|
+
var KEY=${JSON.stringify(NAV_DISPLAY_STORAGE_KEY)};
|
|
2605
|
+
var VALID=["menu","tabs","both"];
|
|
2606
|
+
function fromCookie(){try{var m=document.cookie.match(/(?:^|;\\s*)${NAV_DISPLAY_COOKIE}=([^;]+)/);return m?decodeURIComponent(m[1]):null;}catch(e){return null;}}
|
|
2607
|
+
var cookie=fromCookie();
|
|
2608
|
+
if(cookie&&VALID.indexOf(cookie)!==-1){var ls=null;try{ls=localStorage.getItem(KEY);}catch(e){}
|
|
2609
|
+
if(cookie!==ls){try{localStorage.setItem(KEY,cookie);}catch(e){}}}
|
|
2610
|
+
}catch(e){}})();`;
|
|
2611
|
+
|
|
2538
2612
|
// src/lib/breadcrumbs.ts
|
|
2539
2613
|
function deriveBreadcrumbs(items, pathname, opts) {
|
|
2540
2614
|
const search = opts?.search ?? (typeof window !== "undefined" ? window.location.search : "");
|
|
@@ -2927,7 +3001,6 @@ var standardNavIcons = {
|
|
|
2927
3001
|
};
|
|
2928
3002
|
var ORG_STORAGE_KEY = "vrm.activeOrg";
|
|
2929
3003
|
var MODE_STORAGE_KEY = "vrm.activeMode";
|
|
2930
|
-
var NAV_DISPLAY_KEY = "vrm.navDisplay";
|
|
2931
3004
|
var CORE_BASE_URL = "https://app.politogyvrm.com";
|
|
2932
3005
|
var ChromeStateContext = react.createContext(null);
|
|
2933
3006
|
function useChromeState() {
|
|
@@ -2938,18 +3011,46 @@ function ChromeStateProvider({
|
|
|
2938
3011
|
children
|
|
2939
3012
|
}) {
|
|
2940
3013
|
const { orgs, loading: orgsFetching, error: orgsError } = useChromeOrgs();
|
|
3014
|
+
const chromeMe = useChromeMe();
|
|
3015
|
+
const chromeFetch = useNavChromeFetcher();
|
|
2941
3016
|
const [orgId, setOrgIdState] = react.useState(null);
|
|
2942
|
-
const [navDisplay, setNavDisplayState] = react.useState(
|
|
3017
|
+
const [navDisplay, setNavDisplayState] = react.useState(NAV_DISPLAY_DEFAULT);
|
|
3018
|
+
const navReconciled = react.useRef(false);
|
|
2943
3019
|
react.useEffect(() => {
|
|
2944
3020
|
try {
|
|
2945
3021
|
const storedOrg = resolveActiveOrg() ?? localStorage.getItem(ORG_STORAGE_KEY);
|
|
2946
3022
|
if (storedOrg) setOrgIdState(storedOrg);
|
|
2947
|
-
const storedNav = localStorage.getItem(NAV_DISPLAY_KEY);
|
|
2948
|
-
if (storedNav === "menu" || storedNav === "tabs" || storedNav === "both") {
|
|
2949
|
-
setNavDisplayState(storedNav);
|
|
2950
|
-
}
|
|
2951
3023
|
} catch {
|
|
2952
3024
|
}
|
|
3025
|
+
setNavDisplayState(resolveNavDisplay());
|
|
3026
|
+
}, []);
|
|
3027
|
+
react.useEffect(() => {
|
|
3028
|
+
if (navReconciled.current || !chromeMe.me) return;
|
|
3029
|
+
const pref = chromeMe.me.navDisplay;
|
|
3030
|
+
if (!isNavDisplayPreference(pref)) return;
|
|
3031
|
+
navReconciled.current = true;
|
|
3032
|
+
setNavDisplayState(pref);
|
|
3033
|
+
writeNavDisplayCookie(pref);
|
|
3034
|
+
writeNavDisplayLocal(pref);
|
|
3035
|
+
}, [chromeMe]);
|
|
3036
|
+
react.useEffect(() => {
|
|
3037
|
+
const onStorage = (e) => {
|
|
3038
|
+
if (e.key === NAV_DISPLAY_STORAGE_KEY && isNavDisplayPreference(e.newValue)) {
|
|
3039
|
+
setNavDisplayState(e.newValue);
|
|
3040
|
+
}
|
|
3041
|
+
};
|
|
3042
|
+
const onWake = () => {
|
|
3043
|
+
const cookie = readNavDisplayCookie();
|
|
3044
|
+
if (cookie) setNavDisplayState(cookie);
|
|
3045
|
+
};
|
|
3046
|
+
window.addEventListener("storage", onStorage);
|
|
3047
|
+
window.addEventListener("focus", onWake);
|
|
3048
|
+
document.addEventListener("visibilitychange", onWake);
|
|
3049
|
+
return () => {
|
|
3050
|
+
window.removeEventListener("storage", onStorage);
|
|
3051
|
+
window.removeEventListener("focus", onWake);
|
|
3052
|
+
document.removeEventListener("visibilitychange", onWake);
|
|
3053
|
+
};
|
|
2953
3054
|
}, []);
|
|
2954
3055
|
react.useEffect(() => {
|
|
2955
3056
|
if (!orgs || orgs.length === 0) return;
|
|
@@ -2963,13 +3064,20 @@ function ChromeStateProvider({
|
|
|
2963
3064
|
}
|
|
2964
3065
|
writeActiveOrgCookie(id);
|
|
2965
3066
|
}, []);
|
|
2966
|
-
const setNavDisplay = react.useCallback(
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
3067
|
+
const setNavDisplay = react.useCallback(
|
|
3068
|
+
(m) => {
|
|
3069
|
+
setNavDisplayState(m);
|
|
3070
|
+
writeNavDisplayLocal(m);
|
|
3071
|
+
writeNavDisplayCookie(m);
|
|
3072
|
+
chromeFetch?.("me/preferences", {
|
|
3073
|
+
method: "PATCH",
|
|
3074
|
+
headers: { "Content-Type": "application/json" },
|
|
3075
|
+
body: JSON.stringify({ navDisplay: m })
|
|
3076
|
+
})?.catch(() => {
|
|
3077
|
+
});
|
|
3078
|
+
},
|
|
3079
|
+
[chromeFetch]
|
|
3080
|
+
);
|
|
2973
3081
|
const value = react.useMemo(() => {
|
|
2974
3082
|
if (controlledOrg) {
|
|
2975
3083
|
return {
|