@the-portland-company/shell 0.93.0 → 0.95.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 +20 -0
- package/dist/app-chrome.cjs +105 -46
- package/dist/app-chrome.cjs.map +1 -1
- package/dist/app-chrome.d.cts +2 -1
- package/dist/app-chrome.d.ts +2 -1
- package/dist/app-chrome.js +6 -50
- package/dist/app-chrome.js.map +1 -1
- package/dist/app-icons.cjs +9 -0
- package/dist/app-icons.cjs.map +1 -1
- package/dist/app-icons.d.cts +5 -3
- package/dist/app-icons.d.ts +5 -3
- package/dist/app-icons.js +9 -1
- package/dist/app-icons.js.map +1 -1
- package/dist/brand-favicon.cjs +87 -0
- package/dist/brand-favicon.cjs.map +1 -0
- package/dist/brand-favicon.d.cts +18 -0
- package/dist/brand-favicon.d.ts +18 -0
- package/dist/brand-favicon.js +4 -0
- package/dist/brand-favicon.js.map +1 -0
- package/dist/{chunk-EN5ANFMB.js → chunk-BA3GITRQ.js} +4 -4
- package/dist/{chunk-EN5ANFMB.js.map → chunk-BA3GITRQ.js.map} +1 -1
- package/dist/chunk-BMR5WAWH.js +111 -0
- package/dist/chunk-BMR5WAWH.js.map +1 -0
- package/dist/{chunk-Y66PVCZO.js → chunk-JQ53VZO5.js} +68 -3
- package/dist/chunk-JQ53VZO5.js.map +1 -0
- package/dist/{index-BAFJZuRA.d.cts → index-AIzWNzl-.d.cts} +22 -1
- package/dist/{index-DoSs3sQY.d.ts → index-Byjq0Whp.d.ts} +22 -1
- package/dist/index.cjs +147 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/native.cjs +148 -3
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +2 -1
- package/dist/native.d.ts +2 -1
- package/dist/native.js +4 -3
- package/package.json +6 -1
- package/dist/chunk-Y66PVCZO.js.map +0 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
// src/lib/brandFavicon.ts
|
|
4
|
+
function parseCssUrl(value) {
|
|
5
|
+
if (!value) return null;
|
|
6
|
+
const m = /^url\(\s*(['"]?)(.*?)\1\s*\)$/i.exec(value.trim());
|
|
7
|
+
const url = m?.[2]?.trim();
|
|
8
|
+
return url && /^https:\/\//i.test(url) ? url : null;
|
|
9
|
+
}
|
|
10
|
+
function readBrandAsset(kind) {
|
|
11
|
+
if (typeof document === "undefined") return null;
|
|
12
|
+
try {
|
|
13
|
+
return parseCssUrl(
|
|
14
|
+
window.getComputedStyle(document.documentElement).getPropertyValue(`--asset-${kind}`)
|
|
15
|
+
);
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
var EMPTY = { logoHorizontal: null, logoSquare: null };
|
|
21
|
+
var RETRY_MS = [0, 250, 1e3, 3e3];
|
|
22
|
+
function useBrandAssets() {
|
|
23
|
+
const [assets, setAssets] = useState(EMPTY);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
let done = false;
|
|
26
|
+
const read = () => {
|
|
27
|
+
if (done) return;
|
|
28
|
+
const next = {
|
|
29
|
+
logoHorizontal: readBrandAsset("logo-horizontal"),
|
|
30
|
+
logoSquare: readBrandAsset("logo-square")
|
|
31
|
+
};
|
|
32
|
+
setAssets(
|
|
33
|
+
(prev) => prev.logoHorizontal === next.logoHorizontal && prev.logoSquare === next.logoSquare ? prev : next
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
const links = Array.from(
|
|
37
|
+
document.querySelectorAll('link[rel="stylesheet"][href*="api/tokens"]')
|
|
38
|
+
);
|
|
39
|
+
links.forEach((l) => l.addEventListener("load", read));
|
|
40
|
+
const timers = RETRY_MS.map((ms) => window.setTimeout(read, ms));
|
|
41
|
+
read();
|
|
42
|
+
return () => {
|
|
43
|
+
done = true;
|
|
44
|
+
links.forEach((l) => l.removeEventListener("load", read));
|
|
45
|
+
timers.forEach((t) => window.clearTimeout(t));
|
|
46
|
+
};
|
|
47
|
+
}, []);
|
|
48
|
+
return assets;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/lib/brandFavicon.ts
|
|
52
|
+
var BRAND_FAVICON_LINK_ID = "politogy-brand-favicon";
|
|
53
|
+
var BRAND_APPLE_ICON_LINK_ID = "politogy-brand-apple-icon";
|
|
54
|
+
function upsertLink(id, rel, href) {
|
|
55
|
+
let link = document.getElementById(id);
|
|
56
|
+
if (!link) {
|
|
57
|
+
link = document.createElement("link");
|
|
58
|
+
link.id = id;
|
|
59
|
+
link.rel = rel;
|
|
60
|
+
}
|
|
61
|
+
if (link.getAttribute("href") !== href) link.href = href;
|
|
62
|
+
const icons = document.head.querySelectorAll(`link[rel~="${rel.split(" ")[0]}"]`);
|
|
63
|
+
if (icons[icons.length - 1] !== link) document.head.appendChild(link);
|
|
64
|
+
}
|
|
65
|
+
function applyBrandFavicon() {
|
|
66
|
+
if (typeof document === "undefined") return false;
|
|
67
|
+
const favicon = readBrandAsset("favicon");
|
|
68
|
+
if (!favicon) return false;
|
|
69
|
+
upsertLink(BRAND_FAVICON_LINK_ID, "icon", favicon);
|
|
70
|
+
const apple = readBrandAsset("apple-icon");
|
|
71
|
+
if (apple) upsertLink(BRAND_APPLE_ICON_LINK_ID, "apple-touch-icon", apple);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
var RETRY_MS2 = [0, 250, 1e3, 3e3];
|
|
75
|
+
function watchBrandFavicon() {
|
|
76
|
+
if (typeof document === "undefined") return () => {
|
|
77
|
+
};
|
|
78
|
+
let done = false;
|
|
79
|
+
const apply = () => {
|
|
80
|
+
if (!done) applyBrandFavicon();
|
|
81
|
+
};
|
|
82
|
+
const sheets = /* @__PURE__ */ new Set();
|
|
83
|
+
const listen = () => {
|
|
84
|
+
document.querySelectorAll('link[rel="stylesheet"][href*="api/tokens"]').forEach((l) => {
|
|
85
|
+
if (sheets.has(l)) return;
|
|
86
|
+
sheets.add(l);
|
|
87
|
+
l.addEventListener("load", apply);
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
listen();
|
|
91
|
+
const timers = RETRY_MS2.map((ms) => window.setTimeout(apply, ms));
|
|
92
|
+
const observer = typeof MutationObserver === "undefined" ? null : new MutationObserver(() => {
|
|
93
|
+
listen();
|
|
94
|
+
apply();
|
|
95
|
+
});
|
|
96
|
+
observer?.observe(document.head, { childList: true });
|
|
97
|
+
return () => {
|
|
98
|
+
done = true;
|
|
99
|
+
sheets.forEach((l) => l.removeEventListener("load", apply));
|
|
100
|
+
timers.forEach((t) => window.clearTimeout(t));
|
|
101
|
+
observer?.disconnect();
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function BrandFavicon() {
|
|
105
|
+
useEffect(() => watchBrandFavicon(), []);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export { BRAND_APPLE_ICON_LINK_ID, BRAND_FAVICON_LINK_ID, BrandFavicon, applyBrandFavicon, useBrandAssets, watchBrandFavicon };
|
|
110
|
+
//# sourceMappingURL=chunk-BMR5WAWH.js.map
|
|
111
|
+
//# sourceMappingURL=chunk-BMR5WAWH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/brandAssets.ts","../src/lib/brandFavicon.ts"],"names":["RETRY_MS","useEffect"],"mappings":"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","file":"chunk-BMR5WAWH.js","sourcesContent":["// Brand assets — logo URLs read at RUNTIME from the Brand token sheet.\n//\n// Brand guideline \"Logo standard: use the uploaded Brand assets as-is\": apps\n// (and the shell) never bundle their own copy of a logo; they read the URLs\n// from the Brand `assets` group. `/api/tokens?format=css` emits each set asset\n// as `--asset-<kebab>: url(\"…\")` on `:root`, and every app already loads that\n// sheet at runtime (see ../runtime-tokens.ts), so the rail logo is resolved\n// from the same live sheet as every other chrome value — a Brand upload swaps\n// the logo in every app without a deploy, and there is nothing to fall back to\n// that could drift.\n\n'use client'\n\nimport { useEffect, useState } from 'react'\n\n/** Brand `assets` keys the chrome renders, by their `--asset-<kebab>` name. */\nexport type BrandAssetKind = 'logo-horizontal' | 'logo-square' | 'favicon' | 'apple-icon'\n\n/** `url(\"https://…\")` / `url(https://…)` → the bare https URL, else null. */\nexport function parseCssUrl(value: string | null | undefined): string | null {\n if (!value) return null\n const m = /^url\\(\\s*(['\"]?)(.*?)\\1\\s*\\)$/i.exec(value.trim())\n const url = m?.[2]?.trim()\n return url && /^https:\\/\\//i.test(url) ? url : null\n}\n\n/** One Brand asset URL from the live token sheet, or null while it is unset. */\nexport function readBrandAsset(kind: BrandAssetKind): string | null {\n if (typeof document === 'undefined') return null\n try {\n return parseCssUrl(\n window.getComputedStyle(document.documentElement).getPropertyValue(`--asset-${kind}`),\n )\n } catch {\n return null\n }\n}\n\nexport type BrandAssets = {\n /** `assets.logoHorizontal` — the all-white lockup at the top of the rail. */\n logoHorizontal: string | null\n /** `assets.logoSquare` — the all-white vrm tile on the collapsed rail. */\n logoSquare: string | null\n}\n\nconst EMPTY: BrandAssets = { logoHorizontal: null, logoSquare: null }\n\n// The token sheet is a <link> the app appends at startup, so it may still be\n// in flight on first paint. Re-read when it loads, and a few times after, to\n// cover a link that is inserted after this effect ran.\nconst RETRY_MS = [0, 250, 1000, 3000]\n\n/**\n * The Brand logo URLs, resolved after mount (null on the server and until the\n * runtime token sheet has loaded). Callers reserve the logo's box and draw\n * nothing while a value is null — never a stand-in image.\n */\nexport function useBrandAssets(): BrandAssets {\n const [assets, setAssets] = useState<BrandAssets>(EMPTY)\n useEffect(() => {\n let done = false\n const read = () => {\n if (done) return\n const next = {\n logoHorizontal: readBrandAsset('logo-horizontal'),\n logoSquare: readBrandAsset('logo-square'),\n }\n setAssets((prev) =>\n prev.logoHorizontal === next.logoHorizontal && prev.logoSquare === next.logoSquare\n ? prev\n : next,\n )\n }\n const links = Array.from(\n document.querySelectorAll<HTMLLinkElement>('link[rel=\"stylesheet\"][href*=\"api/tokens\"]'),\n )\n links.forEach((l) => l.addEventListener('load', read))\n const timers = RETRY_MS.map((ms) => window.setTimeout(read, ms))\n read()\n return () => {\n done = true\n links.forEach((l) => l.removeEventListener('load', read))\n timers.forEach((t) => window.clearTimeout(t))\n }\n }, [])\n return assets\n}\n","// Browser-tab icon — Brand `assets.favicon` read at RUNTIME from the Brand\n// token sheet, the same way the rail logo is (./brandAssets).\n//\n// `politogyAppIcons()` (../app-icons) resolves the icon on the SERVER from\n// specs.politogyvrm.com/api/icons/<app>. In production that request comes from\n// a Worker, not a browser, and Cloudflare's bot check answers it with a\n// challenge page, so the metadata comes back with no icon and the tab shows the\n// browser default (or an app's leftover Next.js `favicon.ico` triangle). The\n// token sheet, by contrast, is loaded by the BROWSER in every app, and emits\n// `--asset-favicon` / `--asset-apple-icon` from Brand `assets.*`. Reading those\n// gives every app the Brand's VRM tile with no static file in any repo (Global\n// Rules → App Icons) and no extra request.\n//\n// The link carries a fixed id and is kept the LAST icon link in <head>, so a\n// stale icon from metadata or an old static file never wins over the Brand's.\n\n'use client'\n\nimport { useEffect } from 'react'\n\nimport { readBrandAsset } from './brandAssets'\n\nexport const BRAND_FAVICON_LINK_ID = 'politogy-brand-favicon'\nexport const BRAND_APPLE_ICON_LINK_ID = 'politogy-brand-apple-icon'\n\nfunction upsertLink(id: string, rel: string, href: string): void {\n let link = document.getElementById(id) as HTMLLinkElement | null\n if (!link) {\n link = document.createElement('link')\n link.id = id\n link.rel = rel\n }\n if (link.getAttribute('href') !== href) link.href = href\n // Last icon link in <head> wins. Re-appending an already-last node is a no-op\n // for the browser, so only move it when something was added after it.\n const icons = document.head.querySelectorAll(`link[rel~=\"${rel.split(' ')[0]}\"]`)\n if (icons[icons.length - 1] !== link) document.head.appendChild(link)\n}\n\n/**\n * Point the tab icon (and the iOS home-screen icon) at the Brand's uploaded\n * assets. Returns true once the favicon was set; false while the token sheet\n * has not defined `--asset-favicon` yet. Never draws a stand-in.\n */\nexport function applyBrandFavicon(): boolean {\n if (typeof document === 'undefined') return false\n const favicon = readBrandAsset('favicon')\n if (!favicon) return false\n upsertLink(BRAND_FAVICON_LINK_ID, 'icon', favicon)\n const apple = readBrandAsset('apple-icon')\n if (apple) upsertLink(BRAND_APPLE_ICON_LINK_ID, 'apple-touch-icon', apple)\n return true\n}\n\n// The token sheet is a <link> the app appends at startup, so it may still be in\n// flight on first paint. Re-read when it loads and a few times after.\nconst RETRY_MS = [0, 250, 1000, 3000]\n\n/**\n * Apply the Brand favicon now and keep it applied: on token sheet load, on a\n * few retries, and whenever <head> changes (a client navigation can re-insert\n * metadata icon links after ours). Returns a cleanup function.\n */\nexport function watchBrandFavicon(): () => void {\n if (typeof document === 'undefined') return () => {}\n let done = false\n const apply = () => {\n if (!done) applyBrandFavicon()\n }\n // A token sheet inserted after mount is picked up by the observer below.\n const sheets = new Set<HTMLLinkElement>()\n const listen = () => {\n document\n .querySelectorAll<HTMLLinkElement>('link[rel=\"stylesheet\"][href*=\"api/tokens\"]')\n .forEach((l) => {\n if (sheets.has(l)) return\n sheets.add(l)\n l.addEventListener('load', apply)\n })\n }\n listen()\n const timers = RETRY_MS.map((ms) => window.setTimeout(apply, ms))\n const observer =\n typeof MutationObserver === 'undefined'\n ? null\n : new MutationObserver(() => {\n listen()\n apply()\n })\n observer?.observe(document.head, { childList: true })\n return () => {\n done = true\n sheets.forEach((l) => l.removeEventListener('load', apply))\n timers.forEach((t) => window.clearTimeout(t))\n observer?.disconnect()\n }\n}\n\n/** Renders nothing; sets the Brand favicon after mount. PolitogyAppFrame renders it. */\nexport function BrandFavicon(): null {\n useEffect(() => watchBrandFavicon(), [])\n return null\n}\n"]}
|
|
@@ -27,6 +27,71 @@ function Card({
|
|
|
27
27
|
}
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
|
+
var SETTINGS_ORIGIN = "https://settings.politogyvrm.com";
|
|
31
|
+
var WALLET_PATH = "/organization-settings/subscription-billing/wallet";
|
|
32
|
+
function aiCreditsWalletUrl(orgId) {
|
|
33
|
+
const url = new URL(WALLET_PATH, SETTINGS_ORIGIN);
|
|
34
|
+
if (orgId) url.searchParams.set("org", orgId);
|
|
35
|
+
return url.toString();
|
|
36
|
+
}
|
|
37
|
+
var AI_CREDITS_STATE_TITLES = {
|
|
38
|
+
out: "Out of AI Credits",
|
|
39
|
+
paused: "Paused \u2014 out of AI Credits",
|
|
40
|
+
waiting: "Waiting for AI Credits"
|
|
41
|
+
};
|
|
42
|
+
var MUTED = { color: "var(--color-text-muted, #6b7280)" };
|
|
43
|
+
var BUTTON = {
|
|
44
|
+
display: "inline-block",
|
|
45
|
+
borderRadius: 6,
|
|
46
|
+
padding: "6px 12px",
|
|
47
|
+
fontSize: 12,
|
|
48
|
+
fontWeight: 600,
|
|
49
|
+
textDecoration: "none"
|
|
50
|
+
};
|
|
51
|
+
var PRIMARY = { ...BUTTON, background: "var(--color-text, #2F3136)", color: "#fff" };
|
|
52
|
+
var SECONDARY = {
|
|
53
|
+
...BUTTON,
|
|
54
|
+
border: "1px solid var(--color-border, #e5e7eb)",
|
|
55
|
+
color: "var(--color-text, #2F3136)"
|
|
56
|
+
};
|
|
57
|
+
var defaultFormat = (at) => at.toLocaleString(void 0, { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" });
|
|
58
|
+
function detail(kind, lastOutputAt, format) {
|
|
59
|
+
if (kind === "paused") return lastOutputAt ? `Last updated ${format(lastOutputAt)}.` : "Nothing has been generated yet.";
|
|
60
|
+
if (kind === "waiting") return "This step is kept and starts on its own once AI Credits are added.";
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function AiCreditsState({
|
|
64
|
+
kind,
|
|
65
|
+
orgId,
|
|
66
|
+
isAccountAdmin,
|
|
67
|
+
lastOutputAt,
|
|
68
|
+
onCheckAgain,
|
|
69
|
+
formatTime = defaultFormat,
|
|
70
|
+
className,
|
|
71
|
+
style
|
|
72
|
+
}) {
|
|
73
|
+
const at = lastOutputAt == null ? null : lastOutputAt instanceof Date ? lastOutputAt : new Date(lastOutputAt);
|
|
74
|
+
const last = at && !Number.isNaN(at.getTime()) ? at : null;
|
|
75
|
+
const text = detail(kind, last, formatTime);
|
|
76
|
+
const wallet = aiCreditsWalletUrl(orgId);
|
|
77
|
+
return /* @__PURE__ */ jsxs("div", { role: "status", "data-ai-credits-state": kind, className, style: { fontSize: 14, ...style }, children: [
|
|
78
|
+
/* @__PURE__ */ jsx("p", { style: { margin: 0, fontWeight: 600 }, children: AI_CREDITS_STATE_TITLES[kind] }),
|
|
79
|
+
text && /* @__PURE__ */ jsx("p", { style: { margin: "4px 0 0", ...MUTED }, children: text }),
|
|
80
|
+
isAccountAdmin ? /* @__PURE__ */ jsxs("div", { style: { marginTop: 8, display: "flex", flexWrap: "wrap", gap: 8 }, children: [
|
|
81
|
+
/* @__PURE__ */ jsx("a", { href: wallet, style: PRIMARY, children: "Buy AI Credits" }),
|
|
82
|
+
/* @__PURE__ */ jsx("a", { href: wallet, style: SECONDARY, children: "Turn on auto top-off" })
|
|
83
|
+
] }) : /* @__PURE__ */ jsx("p", { style: { margin: "4px 0 0", ...MUTED }, children: "Ask an Account Admin to add AI Credits." }),
|
|
84
|
+
onCheckAgain && /* @__PURE__ */ jsx(
|
|
85
|
+
"button",
|
|
86
|
+
{
|
|
87
|
+
type: "button",
|
|
88
|
+
onClick: onCheckAgain,
|
|
89
|
+
style: { marginTop: 8, padding: 0, border: 0, background: "none", fontSize: 12, textDecoration: "underline", cursor: "pointer", ...MUTED },
|
|
90
|
+
children: "Check again"
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
] });
|
|
94
|
+
}
|
|
30
95
|
function FormColumn({ children, className }) {
|
|
31
96
|
return /* @__PURE__ */ jsx("div", { "data-shell-form-column": "", className: clsx2("w-full max-w-[960px]", className), children });
|
|
32
97
|
}
|
|
@@ -2381,6 +2446,6 @@ function BreadcrumbBarView({
|
|
|
2381
2446
|
);
|
|
2382
2447
|
}
|
|
2383
2448
|
|
|
2384
|
-
export { BreadcrumbBarView, CORE_APP_ORIGIN, CORE_BILLING_PATH, CREDIT_TYPE_LABELS, CREDIT_TYPE_ORDER, Card, CreditsAction, FormColumn, FuelAction, HeaderTabBar, KNOWN_TAB_GLYPHS, NAV_DISPLAY_OPTIONS, NavChromeProvider, NavDisplayAction, NavThemeControl, NotificationsAction, PageHeaderView, SPECS_ORIGIN, SpecOverlayAction, SpeculationRulesScript, SupportAction, TOPBAR_GLYPH_SIZE, TopBarCountBadge, TopBarPanelArrow, TopBarPopover, TopBarTooltip, TopBarWidgetSlot, buildNavSpeculationRules, buildSpeculationRules, collectSiblingAppUrls, createCommsNotificationsAdapter, creditGaugeRatio, creditStateWord, creditTooltipLine, creditsAriaLabel, creditsIconState, emptyNotificationsFeed, normalizeNotificationsFeed, resolveBrandGlyph, resolveOrgLogoUrl, resolveTabGlyph, topBarIconButtonClass, useChromeMe, useChromeOrgs, useNavChromeDedupedFetch, useNavChromeFetcher, useThemePreference, useTopBarIconSlotGlyph, warnDeprecated };
|
|
2385
|
-
//# sourceMappingURL=chunk-
|
|
2386
|
-
//# sourceMappingURL=chunk-
|
|
2449
|
+
export { AI_CREDITS_STATE_TITLES, AiCreditsState, BreadcrumbBarView, CORE_APP_ORIGIN, CORE_BILLING_PATH, CREDIT_TYPE_LABELS, CREDIT_TYPE_ORDER, Card, CreditsAction, FormColumn, FuelAction, HeaderTabBar, KNOWN_TAB_GLYPHS, NAV_DISPLAY_OPTIONS, NavChromeProvider, NavDisplayAction, NavThemeControl, NotificationsAction, PageHeaderView, SPECS_ORIGIN, SpecOverlayAction, SpeculationRulesScript, SupportAction, TOPBAR_GLYPH_SIZE, TopBarCountBadge, TopBarPanelArrow, TopBarPopover, TopBarTooltip, TopBarWidgetSlot, aiCreditsWalletUrl, buildNavSpeculationRules, buildSpeculationRules, collectSiblingAppUrls, createCommsNotificationsAdapter, creditGaugeRatio, creditStateWord, creditTooltipLine, creditsAriaLabel, creditsIconState, emptyNotificationsFeed, normalizeNotificationsFeed, resolveBrandGlyph, resolveOrgLogoUrl, resolveTabGlyph, topBarIconButtonClass, useChromeMe, useChromeOrgs, useNavChromeDedupedFetch, useNavChromeFetcher, useThemePreference, useTopBarIconSlotGlyph, warnDeprecated };
|
|
2450
|
+
//# sourceMappingURL=chunk-JQ53VZO5.js.map
|
|
2451
|
+
//# sourceMappingURL=chunk-JQ53VZO5.js.map
|