@the-portland-company/shell 0.96.1 → 0.97.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 +52 -628
- package/dist/app-chrome.cjs.map +1 -1
- package/dist/app-chrome.d.cts +6 -3
- package/dist/app-chrome.d.ts +6 -3
- package/dist/app-chrome.js +18 -23
- package/dist/app-chrome.js.map +1 -1
- package/dist/{chunk-5OTXPYZU.js → chunk-VAQNZD37.js} +32 -4
- package/dist/chunk-VAQNZD37.js.map +1 -0
- package/dist/{chunk-V2OFN2S2.js → chunk-WQR42AGC.js} +14 -10
- package/dist/chunk-WQR42AGC.js.map +1 -0
- package/dist/{index-CqHKy3vG.d.ts → index-BEMGfLpI.d.ts} +26 -1
- package/dist/{index-3YFy4JHI.d.cts → index-BaUIPQpN.d.cts} +26 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/native.cjs +42 -7
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +1 -1
- package/dist/native.d.ts +1 -1
- package/dist/native.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-5OTXPYZU.js.map +0 -1
- package/dist/chunk-V2OFN2S2.js.map +0 -1
package/dist/app-chrome.cjs
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
var react = require('react');
|
|
5
5
|
var clsx = require('clsx');
|
|
6
6
|
var fi = require('react-icons/fi');
|
|
7
|
-
|
|
8
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
require('react-dom');
|
|
9
8
|
var lu = require('react-icons/lu');
|
|
9
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
10
10
|
var devnotes = require('@the-portland-company/devnotes');
|
|
11
11
|
|
|
12
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -575,7 +575,6 @@ var SETTINGS_APP_ORIGIN = "https://settings.politogyvrm.com";
|
|
|
575
575
|
var CORE_BILLING_PATH = "/organization-settings/subscription-billing";
|
|
576
576
|
var CORE_BILLING_URL = `${SETTINGS_APP_ORIGIN}${CORE_BILLING_PATH}`;
|
|
577
577
|
var QUARANTINED_CONTACTS_URL = `https://contacts.politogyvrm.com/duplicates`;
|
|
578
|
-
var SPECS_ORIGIN = "https://specs.politogyvrm.com";
|
|
579
578
|
function withOrgParam(url, orgId) {
|
|
580
579
|
if (!orgId) return url;
|
|
581
580
|
return `${url}${url.includes("?") ? "&" : "?"}org=${orgId}`;
|
|
@@ -653,16 +652,6 @@ function TopBarPanelArrow() {
|
|
|
653
652
|
}
|
|
654
653
|
);
|
|
655
654
|
}
|
|
656
|
-
function TopBarWidgetSlot({
|
|
657
|
-
title,
|
|
658
|
-
description,
|
|
659
|
-
children
|
|
660
|
-
}) {
|
|
661
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "topbar-widget-slot group/tbtip relative inline-flex", children: [
|
|
662
|
-
children,
|
|
663
|
-
/* @__PURE__ */ jsxRuntime.jsx(TopBarTooltip, { title, description })
|
|
664
|
-
] });
|
|
665
|
-
}
|
|
666
655
|
var THEME_OPTIONS = [
|
|
667
656
|
{ value: "light", label: "Light" }
|
|
668
657
|
];
|
|
@@ -946,23 +935,6 @@ function BellOffGlyph({ size = 14, className }) {
|
|
|
946
935
|
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "1", y1: "1", x2: "23", y2: "23" })
|
|
947
936
|
] });
|
|
948
937
|
}
|
|
949
|
-
function LifeBuoyGlyph({ size = TOPBAR_GLYPH_SIZE, className }) {
|
|
950
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
|
|
951
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
952
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "4" }),
|
|
953
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "4.93", y1: "4.93", x2: "9.17", y2: "9.17" }),
|
|
954
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "14.83", y1: "14.83", x2: "19.07", y2: "19.07" }),
|
|
955
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "14.83", y1: "9.17", x2: "19.07", y2: "4.93" }),
|
|
956
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "4.93", y1: "19.07", x2: "9.17", y2: "14.83" })
|
|
957
|
-
] });
|
|
958
|
-
}
|
|
959
|
-
function LayoutGlyph({ size = TOPBAR_GLYPH_SIZE, className }) {
|
|
960
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
|
|
961
|
-
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
962
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }),
|
|
963
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "21", x2: "9", y2: "9" })
|
|
964
|
-
] });
|
|
965
|
-
}
|
|
966
938
|
function PlusGlyph({ size = 14, className }) {
|
|
967
939
|
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
|
|
968
940
|
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
|
|
@@ -1596,586 +1568,39 @@ function NotificationsAction({ orgId }) {
|
|
|
1596
1568
|
] })
|
|
1597
1569
|
);
|
|
1598
1570
|
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
}
|
|
1609
|
-
function SupportAction({
|
|
1610
|
-
orgId,
|
|
1611
|
-
orgs: knownOrgs
|
|
1612
|
-
} = {}) {
|
|
1613
|
-
const { fetchJson } = useNavChrome("SupportAction");
|
|
1614
|
-
const { me, loaded } = useChromeMe();
|
|
1615
|
-
const { orgs: fetchedOrgs } = useChromeOrgs({ enabled: !knownOrgs });
|
|
1616
|
-
const orgs = knownOrgs ?? fetchedOrgs;
|
|
1617
|
-
const [open, setOpen] = react.useState(false);
|
|
1618
|
-
const [message, setMessage] = react.useState("");
|
|
1619
|
-
const [email, setEmail] = react.useState("");
|
|
1620
|
-
const [phone, setPhone] = react.useState("");
|
|
1621
|
-
const [showEmailError, setShowEmailError] = react.useState(false);
|
|
1622
|
-
const [state, setState] = react.useState("idle");
|
|
1623
|
-
const meEmail = me?.email ?? null;
|
|
1624
|
-
const mePhone = me?.phone ?? null;
|
|
1625
|
-
const missingEmail = loaded && !meEmail;
|
|
1626
|
-
const missingPhone = loaded && !mePhone;
|
|
1627
|
-
const activeOrg = (orgId ? orgs?.find((o) => o.id === orgId) : null) ?? (orgs && orgs.length === 1 ? orgs[0] : null);
|
|
1628
|
-
const orgName = activeOrg?.name ?? null;
|
|
1629
|
-
const close = react.useCallback(() => {
|
|
1630
|
-
setOpen(false);
|
|
1631
|
-
setState("idle");
|
|
1632
|
-
setMessage("");
|
|
1633
|
-
setShowEmailError(false);
|
|
1634
|
-
}, []);
|
|
1635
|
-
react.useEffect(() => {
|
|
1636
|
-
if (!open) return;
|
|
1637
|
-
setEmail(meEmail ?? "");
|
|
1638
|
-
setPhone(mePhone ?? "");
|
|
1639
|
-
setShowEmailError(false);
|
|
1640
|
-
}, [open, meEmail, mePhone]);
|
|
1641
|
-
react.useEffect(() => {
|
|
1642
|
-
if (!open) return;
|
|
1643
|
-
const onKey = (e) => {
|
|
1644
|
-
if (e.key === "Escape") close();
|
|
1645
|
-
};
|
|
1646
|
-
document.addEventListener("keydown", onKey);
|
|
1647
|
-
return () => document.removeEventListener("keydown", onKey);
|
|
1648
|
-
}, [open, close]);
|
|
1649
|
-
const submit = async () => {
|
|
1650
|
-
if (!message.trim() || state === "sending") return;
|
|
1651
|
-
const resolvedEmail = (missingEmail ? email : meEmail ?? "").trim();
|
|
1652
|
-
const resolvedPhone = (missingPhone ? phone : mePhone ?? "").trim();
|
|
1653
|
-
if (missingEmail && !resolvedEmail) {
|
|
1654
|
-
setShowEmailError(true);
|
|
1655
|
-
return;
|
|
1656
|
-
}
|
|
1657
|
-
setShowEmailError(false);
|
|
1658
|
-
const collectedContact = missingEmail || missingPhone;
|
|
1659
|
-
const fullMessage = composeSupportMessage({
|
|
1660
|
-
note: message,
|
|
1661
|
-
pageTitle: typeof document !== "undefined" ? document.title : "",
|
|
1662
|
-
url: typeof window !== "undefined" ? window.location.href : "",
|
|
1663
|
-
orgName,
|
|
1664
|
-
replyEmail: collectedContact ? resolvedEmail : void 0,
|
|
1665
|
-
replyPhone: collectedContact ? resolvedPhone : void 0
|
|
1666
|
-
});
|
|
1667
|
-
setState("sending");
|
|
1668
|
-
try {
|
|
1669
|
-
await fetchJson("support", {
|
|
1670
|
-
method: "POST",
|
|
1671
|
-
headers: { "Content-Type": "application/json" },
|
|
1672
|
-
body: JSON.stringify({ message: fullMessage })
|
|
1673
|
-
});
|
|
1674
|
-
setState("sent");
|
|
1675
|
-
} catch {
|
|
1676
|
-
setState("error");
|
|
1677
|
-
}
|
|
1678
|
-
};
|
|
1679
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1680
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1681
|
-
"button",
|
|
1682
|
-
{
|
|
1683
|
-
type: "button",
|
|
1684
|
-
"aria-label": "Support",
|
|
1685
|
-
className: topBarIconButtonClass(),
|
|
1686
|
-
onClick: () => setOpen(true),
|
|
1687
|
-
children: [
|
|
1688
|
-
/* @__PURE__ */ jsxRuntime.jsx(SlotGlyph, { slot: "support", fallback: LifeBuoyGlyph }),
|
|
1689
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1690
|
-
TopBarTooltip,
|
|
1691
|
-
{
|
|
1692
|
-
title: "Support",
|
|
1693
|
-
description: "Send a support request to the Politogy team."
|
|
1694
|
-
}
|
|
1695
|
-
)
|
|
1696
|
-
]
|
|
1697
|
-
}
|
|
1698
|
-
),
|
|
1699
|
-
open && typeof document !== "undefined" ? reactDom.createPortal(
|
|
1700
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1701
|
-
"div",
|
|
1702
|
-
{
|
|
1703
|
-
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",
|
|
1704
|
-
role: "dialog",
|
|
1705
|
-
"aria-modal": "true",
|
|
1706
|
-
"aria-label": "Support request",
|
|
1707
|
-
onMouseDown: (e) => {
|
|
1708
|
-
if (e.target === e.currentTarget) close();
|
|
1709
|
-
},
|
|
1710
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-md rounded-2xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] p-6 shadow-[var(--shadow-soft)]", children: [
|
|
1711
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-bold tracking-[-0.01em] text-[color:var(--foreground)]", children: "Contact Support" }),
|
|
1712
|
-
state === "sent" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1713
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-3 text-sm text-[color:var(--muted)]", children: "Your request was sent. The team will reply by email." }),
|
|
1714
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-5 flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1715
|
-
"button",
|
|
1716
|
-
{
|
|
1717
|
-
type: "button",
|
|
1718
|
-
onClick: close,
|
|
1719
|
-
className: "rounded-full bg-[color:var(--color-accent-bg-safe,var(--color-accent))] px-4 py-2 text-sm font-semibold text-[#FFFFFF] hover:opacity-90",
|
|
1720
|
-
children: "Done"
|
|
1721
|
-
}
|
|
1722
|
-
) })
|
|
1723
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1724
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-[color:var(--muted)]", children: "Describe the problem or question. Your account details ride along automatically." }),
|
|
1725
|
-
missingEmail ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "mt-4 block", children: [
|
|
1726
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block text-xs font-semibold text-[color:var(--foreground)]", children: "Your email" }),
|
|
1727
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1728
|
-
"input",
|
|
1729
|
-
{
|
|
1730
|
-
type: "email",
|
|
1731
|
-
value: email,
|
|
1732
|
-
onChange: (e) => setEmail(e.target.value),
|
|
1733
|
-
placeholder: "you@example.com",
|
|
1734
|
-
className: "w-full rounded-lg border border-[color:var(--line)] bg-[color:var(--panel)] px-3 py-2 text-sm text-[color:var(--foreground)] placeholder:text-[color:var(--muted)] focus:outline-none focus:ring-2 focus:ring-[color:var(--color-accent)]"
|
|
1735
|
-
}
|
|
1736
|
-
),
|
|
1737
|
-
showEmailError ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block text-xs text-[color:var(--chrome-danger)]", children: "Enter an email so support can reply." }) : null
|
|
1738
|
-
] }) : null,
|
|
1739
|
-
missingPhone ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "mt-3 block", children: [
|
|
1740
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-1 block text-xs font-semibold text-[color:var(--foreground)]", children: "Your phone \u2014 optional" }),
|
|
1741
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1742
|
-
"input",
|
|
1743
|
-
{
|
|
1744
|
-
type: "tel",
|
|
1745
|
-
value: phone,
|
|
1746
|
-
onChange: (e) => setPhone(e.target.value),
|
|
1747
|
-
placeholder: "(503) 555-1212",
|
|
1748
|
-
className: "w-full rounded-lg border border-[color:var(--line)] bg-[color:var(--panel)] px-3 py-2 text-sm text-[color:var(--foreground)] placeholder:text-[color:var(--muted)] focus:outline-none focus:ring-2 focus:ring-[color:var(--color-accent)]"
|
|
1749
|
-
}
|
|
1750
|
-
)
|
|
1751
|
-
] }) : null,
|
|
1752
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1753
|
-
"textarea",
|
|
1754
|
-
{
|
|
1755
|
-
value: message,
|
|
1756
|
-
onChange: (e) => setMessage(e.target.value),
|
|
1757
|
-
rows: 5,
|
|
1758
|
-
placeholder: "How can we help?",
|
|
1759
|
-
className: "mt-4 w-full resize-y rounded-lg border border-[color:var(--line)] bg-[color:var(--panel)] px-3 py-2 text-sm text-[color:var(--foreground)] placeholder:text-[color:var(--muted)] focus:outline-none focus:ring-2 focus:ring-[color:var(--color-accent)]"
|
|
1760
|
-
}
|
|
1761
|
-
),
|
|
1762
|
-
state === "error" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-[color:var(--chrome-danger)]", children: "Sending failed \u2014 try again." }) : null,
|
|
1763
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 flex justify-end gap-2", children: [
|
|
1764
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1765
|
-
"button",
|
|
1766
|
-
{
|
|
1767
|
-
type: "button",
|
|
1768
|
-
onClick: close,
|
|
1769
|
-
className: "rounded-full border border-[color:var(--line)] px-4 py-2 text-sm font-semibold text-[color:var(--foreground)] hover:bg-[color:var(--panel)]",
|
|
1770
|
-
children: "Cancel"
|
|
1771
|
-
}
|
|
1772
|
-
),
|
|
1773
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1774
|
-
"button",
|
|
1775
|
-
{
|
|
1776
|
-
type: "button",
|
|
1777
|
-
onClick: submit,
|
|
1778
|
-
disabled: !message.trim() || state === "sending",
|
|
1779
|
-
className: "rounded-full bg-[color:var(--color-accent-bg-safe,var(--color-accent))] px-4 py-2 text-sm font-semibold text-[#FFFFFF] transition hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50",
|
|
1780
|
-
children: state === "sending" ? "Sending\u2026" : "Send request"
|
|
1781
|
-
}
|
|
1782
|
-
)
|
|
1783
|
-
] })
|
|
1784
|
-
] })
|
|
1785
|
-
] })
|
|
1786
|
-
}
|
|
1787
|
-
),
|
|
1788
|
-
document.body
|
|
1789
|
-
) : null
|
|
1571
|
+
var NAV_DISPLAY_OPTIONS = [
|
|
1572
|
+
{ value: "menu", label: "Sub Menu" },
|
|
1573
|
+
{ value: "tabs", label: "Tabs Only" },
|
|
1574
|
+
{ value: "both", label: "Both" }
|
|
1575
|
+
];
|
|
1576
|
+
function TopBarLinkAction({ label, description, href, icon: Icon }) {
|
|
1577
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("a", { href, "aria-label": label, className: topBarIconButtonClass(), children: [
|
|
1578
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { size: TOPBAR_GLYPH_SIZE, className: "text-current" }),
|
|
1579
|
+
/* @__PURE__ */ jsxRuntime.jsx(TopBarTooltip, { title: label, description })
|
|
1790
1580
|
] });
|
|
1791
1581
|
}
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
}
|
|
1802
|
-
return null;
|
|
1803
|
-
}
|
|
1804
|
-
function writeSpecOverlayAccessCache(access) {
|
|
1805
|
-
if (typeof window === "undefined") return;
|
|
1806
|
-
try {
|
|
1807
|
-
window.sessionStorage.setItem(SPEC_OVERLAY_ACCESS_CACHE_KEY, JSON.stringify(access));
|
|
1808
|
-
} catch {
|
|
1809
|
-
}
|
|
1810
|
-
}
|
|
1811
|
-
var locationChangeListeners = /* @__PURE__ */ new Set();
|
|
1812
|
-
var historyPatched = false;
|
|
1813
|
-
function ensureHistoryPatched() {
|
|
1814
|
-
if (historyPatched || typeof window === "undefined") return;
|
|
1815
|
-
historyPatched = true;
|
|
1816
|
-
const notify = () => locationChangeListeners.forEach((fn) => fn());
|
|
1817
|
-
const originalPushState = window.history.pushState.bind(window.history);
|
|
1818
|
-
const originalReplaceState = window.history.replaceState.bind(window.history);
|
|
1819
|
-
window.history.pushState = (...args) => {
|
|
1820
|
-
originalPushState(...args);
|
|
1821
|
-
notify();
|
|
1822
|
-
};
|
|
1823
|
-
window.history.replaceState = (...args) => {
|
|
1824
|
-
originalReplaceState(...args);
|
|
1825
|
-
notify();
|
|
1826
|
-
};
|
|
1827
|
-
window.addEventListener("popstate", notify);
|
|
1828
|
-
}
|
|
1829
|
-
function useLocationHref() {
|
|
1830
|
-
const [href, setHref] = react.useState(
|
|
1831
|
-
() => typeof window === "undefined" ? "" : window.location.href
|
|
1832
|
-
);
|
|
1833
|
-
react.useEffect(() => {
|
|
1834
|
-
ensureHistoryPatched();
|
|
1835
|
-
const onChange = () => setHref(window.location.href);
|
|
1836
|
-
locationChangeListeners.add(onChange);
|
|
1837
|
-
return () => {
|
|
1838
|
-
locationChangeListeners.delete(onChange);
|
|
1839
|
-
};
|
|
1840
|
-
}, []);
|
|
1841
|
-
return href;
|
|
1842
|
-
}
|
|
1843
|
-
function specOverlayFetchJson(path) {
|
|
1844
|
-
return fetch(`${SPECS_ORIGIN}${path}`, { credentials: "include", signal: AbortSignal.timeout(1e4) }).then((res) => {
|
|
1845
|
-
if (!res.ok) throw new Error(`spec overlay ${path}: ${res.status}`);
|
|
1846
|
-
return res.json();
|
|
1847
|
-
});
|
|
1848
|
-
}
|
|
1849
|
-
function isAutomatedCaptureSession() {
|
|
1850
|
-
return typeof navigator !== "undefined" && navigator.webdriver === true;
|
|
1851
|
-
}
|
|
1852
|
-
function currentChromeTheme() {
|
|
1853
|
-
const root = document.documentElement;
|
|
1854
|
-
if (root.classList.contains("dark") || root.getAttribute("data-theme") === "dark") return "dark";
|
|
1855
|
-
if (root.classList.contains("light") || root.getAttribute("data-theme") === "light") return "light";
|
|
1856
|
-
return "light";
|
|
1857
|
-
}
|
|
1858
|
-
var SPEC_OVERLAY_ROOT_ATTR = "data-spec-overlay-root";
|
|
1859
|
-
async function captureMyView(status, orgId, orgName) {
|
|
1860
|
-
const { domToCanvas } = await import('modern-screenshot');
|
|
1861
|
-
const w = window.innerWidth;
|
|
1862
|
-
const h = window.innerHeight;
|
|
1863
|
-
const theme = currentChromeTheme();
|
|
1864
|
-
const bg = getComputedStyle(document.body).backgroundColor || (theme === "dark" ? "#0b0b0c" : "#ffffff");
|
|
1865
|
-
const full = await domToCanvas(document.body, {
|
|
1866
|
-
width: w,
|
|
1867
|
-
height: h,
|
|
1868
|
-
scale: 1,
|
|
1869
|
-
backgroundColor: bg,
|
|
1870
|
-
// Cross-origin images that refuse CORS render blank instead of failing.
|
|
1871
|
-
fetch: { requestInit: { mode: "cors", credentials: "omit" }, placeholderImage: "" },
|
|
1872
|
-
// Drop the Spec drawer and everything fixed (sidebar, toasts, popovers):
|
|
1873
|
-
// the design is the page body only, so chrome would only add noise.
|
|
1874
|
-
filter: (node) => !(node instanceof Element) || !node.hasAttribute(SPEC_OVERLAY_ROOT_ATTR) && getComputedStyle(node).position !== "fixed",
|
|
1875
|
-
style: { transform: `translate(${-window.scrollX}px, ${-window.scrollY}px)` }
|
|
1876
|
-
});
|
|
1877
|
-
const el = (status.live_selector ? document.querySelector(status.live_selector) : null) ?? document.querySelector("[data-shell-content]");
|
|
1878
|
-
let region = { x: 0, y: 0, width: w, height: h };
|
|
1879
|
-
if (el) {
|
|
1880
|
-
const r = el.getBoundingClientRect();
|
|
1881
|
-
const x = Math.max(0, Math.round(r.left));
|
|
1882
|
-
const y = Math.max(0, Math.round(r.top));
|
|
1883
|
-
region = { x, y, width: Math.min(w - x, Math.round(r.width)), height: Math.min(h - y, Math.round(r.height)) };
|
|
1884
|
-
}
|
|
1885
|
-
const out = document.createElement("canvas");
|
|
1886
|
-
out.width = region.width;
|
|
1887
|
-
out.height = region.height;
|
|
1888
|
-
const ctx = out.getContext("2d");
|
|
1889
|
-
if (!ctx) throw new Error("page raster failed");
|
|
1890
|
-
ctx.drawImage(full, region.x, region.y, region.width, region.height, 0, 0, region.width, region.height);
|
|
1891
|
-
const blob = await new Promise((resolve) => out.toBlob(resolve, "image/png"));
|
|
1892
|
-
if (!blob) throw new Error("page raster failed");
|
|
1893
|
-
const form = new FormData();
|
|
1894
|
-
form.append("image", blob, "live.png");
|
|
1895
|
-
form.append(
|
|
1896
|
-
"meta",
|
|
1897
|
-
JSON.stringify({ url: window.location.href, theme, org_id: orgId ?? null, org_name: orgName ?? null, crop: null })
|
|
1582
|
+
function OrganizationSettingsAction({ href }) {
|
|
1583
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1584
|
+
TopBarLinkAction,
|
|
1585
|
+
{
|
|
1586
|
+
label: "Organization settings",
|
|
1587
|
+
description: "Users, Brand Kit, modes and billing for this organization.",
|
|
1588
|
+
href,
|
|
1589
|
+
icon: lu.LuSettings
|
|
1590
|
+
}
|
|
1898
1591
|
);
|
|
1899
|
-
const res = await fetch(status.capture_url ?? `${SPECS_ORIGIN}/api/overlay/capture`, {
|
|
1900
|
-
method: "POST",
|
|
1901
|
-
credentials: "include",
|
|
1902
|
-
headers: { "X-Spec-Capture": "1" },
|
|
1903
|
-
body: form,
|
|
1904
|
-
signal: AbortSignal.timeout(1e4)
|
|
1905
|
-
});
|
|
1906
|
-
if (!res.ok) throw new Error(`capture ${res.status}: ${(await res.text()).slice(0, 160)}`);
|
|
1907
1592
|
}
|
|
1908
|
-
function
|
|
1909
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
}
|
|
1917
|
-
),
|
|
1918
|
-
"Capturing your view and comparing\u2026"
|
|
1919
|
-
] }),
|
|
1920
|
-
["Live \u2014 your view", "Diff"].map((label) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4", children: [
|
|
1921
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-1 text-xs font-semibold uppercase tracking-wide text-[color:var(--muted)]", children: label }),
|
|
1922
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", className: "h-40 w-full animate-pulse rounded border border-[color:var(--line)] bg-[color:var(--panel)] motion-reduce:animate-none" })
|
|
1923
|
-
] }, label))
|
|
1924
|
-
] });
|
|
1925
|
-
}
|
|
1926
|
-
function specOverlayStatusPath() {
|
|
1927
|
-
return `/api/overlay/status?url=${encodeURIComponent(window.location.href)}&theme=${currentChromeTheme()}`;
|
|
1928
|
-
}
|
|
1929
|
-
function SpecOverlayAction({ orgId, orgName } = {}) {
|
|
1930
|
-
const [access, setAccess] = react.useState(() => readSpecOverlayAccessCache());
|
|
1931
|
-
const [status, setStatus] = react.useState(null);
|
|
1932
|
-
const [open, setOpen] = react.useState(false);
|
|
1933
|
-
const [capture, setCapture] = react.useState({ state: "idle" });
|
|
1934
|
-
const [frameKey, setFrameKey] = react.useState(0);
|
|
1935
|
-
const [copied, setCopied] = react.useState("idle");
|
|
1936
|
-
const href = useLocationHref();
|
|
1937
|
-
const automated = isAutomatedCaptureSession();
|
|
1938
|
-
react.useEffect(() => {
|
|
1939
|
-
if (access || automated) return;
|
|
1940
|
-
let cancelled = false;
|
|
1941
|
-
specOverlayFetchJson("/api/overlay/access").then((data) => {
|
|
1942
|
-
if (cancelled) return;
|
|
1943
|
-
const next = data && typeof data.allowed === "boolean" ? data : { allowed: false };
|
|
1944
|
-
setAccess(next);
|
|
1945
|
-
writeSpecOverlayAccessCache(next);
|
|
1946
|
-
}).catch(() => {
|
|
1947
|
-
if (cancelled) return;
|
|
1948
|
-
const next = { allowed: false };
|
|
1949
|
-
setAccess(next);
|
|
1950
|
-
writeSpecOverlayAccessCache(next);
|
|
1951
|
-
});
|
|
1952
|
-
return () => {
|
|
1953
|
-
cancelled = true;
|
|
1954
|
-
};
|
|
1955
|
-
}, [access]);
|
|
1956
|
-
react.useEffect(() => {
|
|
1957
|
-
if (automated || !access?.allowed || typeof window === "undefined") return;
|
|
1958
|
-
const timer = window.setTimeout(() => {
|
|
1959
|
-
specOverlayFetchJson(specOverlayStatusPath()).then((data) => setStatus(data)).catch(() => setStatus(null));
|
|
1960
|
-
}, 300);
|
|
1961
|
-
return () => window.clearTimeout(timer);
|
|
1962
|
-
}, [access?.allowed, href, orgId]);
|
|
1963
|
-
const captureRun = react.useRef(0);
|
|
1964
|
-
const accessRef = react.useRef(access);
|
|
1965
|
-
accessRef.current = access;
|
|
1966
|
-
const runCapture = async (s) => {
|
|
1967
|
-
const run = ++captureRun.current;
|
|
1968
|
-
const before = s.live_source === "viewer" ? s.finished_at : null;
|
|
1969
|
-
setCapture({ state: "busy" });
|
|
1970
|
-
try {
|
|
1971
|
-
await new Promise((r) => setTimeout(r, 150));
|
|
1972
|
-
await captureMyView(s, orgId, orgName);
|
|
1973
|
-
for (let i = 0; i < 45; i++) {
|
|
1974
|
-
await new Promise((r) => setTimeout(r, 3e3));
|
|
1975
|
-
if (run !== captureRun.current) return;
|
|
1976
|
-
if (!accessRef.current?.allowed) {
|
|
1977
|
-
setCapture({ state: "idle" });
|
|
1978
|
-
return;
|
|
1979
|
-
}
|
|
1980
|
-
const next = await specOverlayFetchJson(specOverlayStatusPath()).catch(() => null);
|
|
1981
|
-
if (next && next.matched && next.live_source === "viewer" && next.finished_at !== before) {
|
|
1982
|
-
setStatus(next);
|
|
1983
|
-
setFrameKey((k) => k + 1);
|
|
1984
|
-
setCapture({ state: "idle" });
|
|
1985
|
-
return;
|
|
1986
|
-
}
|
|
1987
|
-
}
|
|
1988
|
-
setCapture({ state: "error", message: "Compare did not finish in 2 min." });
|
|
1989
|
-
} catch (e) {
|
|
1990
|
-
if (run === captureRun.current) setCapture({ state: "error", message: e instanceof Error ? e.message : String(e) });
|
|
1593
|
+
function MyProfileAction({ href }) {
|
|
1594
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1595
|
+
TopBarLinkAction,
|
|
1596
|
+
{
|
|
1597
|
+
label: "My profile",
|
|
1598
|
+
description: "Your photo, organizations, security and notifications.",
|
|
1599
|
+
href,
|
|
1600
|
+
icon: lu.LuCircleUser
|
|
1991
1601
|
}
|
|
1992
|
-
|
|
1993
|
-
react.useEffect(() => {
|
|
1994
|
-
if (automated || !open || !status || !status.matched) return;
|
|
1995
|
-
void runCapture(status);
|
|
1996
|
-
return () => {
|
|
1997
|
-
captureRun.current++;
|
|
1998
|
-
};
|
|
1999
|
-
}, [open, status && status.matched ? status.target_id : null]);
|
|
2000
|
-
if (automated || !access?.allowed) return null;
|
|
2001
|
-
const hasScore = status && status.matched && typeof status.mismatch_pct === "number";
|
|
2002
|
-
const badgeLabel = hasScore ? `${Math.round(status.mismatch_pct)}%` : status ? "\u2014" : void 0;
|
|
2003
|
-
const badgeTone = hasScore ? status && status.matched && status.passed ? "default" : "danger" : "default";
|
|
2004
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2005
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2006
|
-
"button",
|
|
2007
|
-
{
|
|
2008
|
-
type: "button",
|
|
2009
|
-
"aria-label": "Spec",
|
|
2010
|
-
"aria-expanded": open,
|
|
2011
|
-
className: topBarIconButtonClass(badgeTone),
|
|
2012
|
-
onClick: () => setOpen(true),
|
|
2013
|
-
children: [
|
|
2014
|
-
/* @__PURE__ */ jsxRuntime.jsx(LayoutGlyph, { size: TOPBAR_GLYPH_SIZE, className: "text-current" }),
|
|
2015
|
-
badgeLabel ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2016
|
-
"span",
|
|
2017
|
-
{
|
|
2018
|
-
className: [
|
|
2019
|
-
"absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] font-normal leading-none text-[#FFFFFF]",
|
|
2020
|
-
hasScore ? status && status.matched && status.passed ? "bg-[color:var(--chrome-success,#2f855a)]" : "bg-[color:var(--chrome-danger)]" : "bg-[color:var(--muted)]"
|
|
2021
|
-
].join(" "),
|
|
2022
|
-
children: badgeLabel
|
|
2023
|
-
}
|
|
2024
|
-
) : null,
|
|
2025
|
-
!open ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2026
|
-
TopBarTooltip,
|
|
2027
|
-
{
|
|
2028
|
-
title: "Spec",
|
|
2029
|
-
description: "Spec design + visual regression for this page."
|
|
2030
|
-
}
|
|
2031
|
-
) : null
|
|
2032
|
-
]
|
|
2033
|
-
}
|
|
2034
|
-
),
|
|
2035
|
-
open && typeof document !== "undefined" ? reactDom.createPortal(
|
|
2036
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2037
|
-
"div",
|
|
2038
|
-
{
|
|
2039
|
-
className: "fixed inset-0 z-50 flex justify-end bg-black/40",
|
|
2040
|
-
...{ [SPEC_OVERLAY_ROOT_ATTR]: "" },
|
|
2041
|
-
role: "dialog",
|
|
2042
|
-
"aria-modal": "true",
|
|
2043
|
-
"aria-label": "Spec overlay",
|
|
2044
|
-
onMouseDown: (e) => {
|
|
2045
|
-
if (e.target === e.currentTarget) setOpen(false);
|
|
2046
|
-
},
|
|
2047
|
-
onKeyDown: (e) => {
|
|
2048
|
-
if (e.key === "Escape") setOpen(false);
|
|
2049
|
-
},
|
|
2050
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full w-full max-w-[480px] flex-col border-l border-[color:var(--line)] bg-[color:var(--panel-elevated)] shadow-[var(--shadow-soft)]", children: [
|
|
2051
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-[color:var(--line)] px-4 py-3", children: [
|
|
2052
|
-
/* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "truncate text-sm font-bold tracking-[-0.01em] text-[color:var(--foreground)]", children: [
|
|
2053
|
-
"Spec",
|
|
2054
|
-
status && status.matched ? `: ${status.spec_title}` : ""
|
|
2055
|
-
] }),
|
|
2056
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2057
|
-
"button",
|
|
2058
|
-
{
|
|
2059
|
-
type: "button",
|
|
2060
|
-
"aria-label": "Close",
|
|
2061
|
-
onClick: () => setOpen(false),
|
|
2062
|
-
className: "rounded-full p-1 text-[color:var(--muted)] hover:bg-[color:var(--panel)] hover:text-[color:var(--foreground)]",
|
|
2063
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(CloseGlyph, { size: 16 })
|
|
2064
|
-
}
|
|
2065
|
-
)
|
|
2066
|
-
] }),
|
|
2067
|
-
status && status.matched ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2068
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 border-b border-[color:var(--line)] px-4 py-2 text-xs font-semibold", children: [
|
|
2069
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2070
|
-
"a",
|
|
2071
|
-
{
|
|
2072
|
-
href: status.spec_url,
|
|
2073
|
-
target: "_blank",
|
|
2074
|
-
rel: "noreferrer",
|
|
2075
|
-
className: "text-[color:var(--color-accent-fg,var(--color-accent))] hover:underline",
|
|
2076
|
-
children: "Open spec \u2197"
|
|
2077
|
-
}
|
|
2078
|
-
),
|
|
2079
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2080
|
-
"a",
|
|
2081
|
-
{
|
|
2082
|
-
href: status.compare_url,
|
|
2083
|
-
target: "_blank",
|
|
2084
|
-
rel: "noreferrer",
|
|
2085
|
-
className: "text-[color:var(--color-accent-fg,var(--color-accent))] hover:underline",
|
|
2086
|
-
children: "Compare tab \u2197"
|
|
2087
|
-
}
|
|
2088
|
-
),
|
|
2089
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2090
|
-
"button",
|
|
2091
|
-
{
|
|
2092
|
-
type: "button",
|
|
2093
|
-
disabled: capture.state === "busy",
|
|
2094
|
-
onClick: () => void runCapture(status),
|
|
2095
|
-
className: "ml-auto rounded-md border border-[color:var(--line)] px-2 py-1 text-[color:var(--foreground)] hover:bg-[color:var(--panel)] disabled:opacity-60",
|
|
2096
|
-
title: "Your view is captured when the drawer opens. Recapture after changing the page.",
|
|
2097
|
-
children: capture.state === "busy" ? "Capturing\u2026" : "Recapture"
|
|
2098
|
-
}
|
|
2099
|
-
),
|
|
2100
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2101
|
-
"button",
|
|
2102
|
-
{
|
|
2103
|
-
type: "button",
|
|
2104
|
-
"data-spec-copy-prompt": "",
|
|
2105
|
-
disabled: capture.state === "busy" || !status.prompt,
|
|
2106
|
-
onClick: () => {
|
|
2107
|
-
const text = status.prompt;
|
|
2108
|
-
if (!text) return;
|
|
2109
|
-
navigator.clipboard.writeText(text).then(() => setCopied("copied")).catch(() => setCopied("failed")).finally(() => window.setTimeout(() => setCopied("idle"), 2e3));
|
|
2110
|
-
},
|
|
2111
|
-
className: "rounded-md border border-[color:var(--line)] px-2 py-1 text-[color:var(--foreground)] hover:bg-[color:var(--panel)] disabled:opacity-60",
|
|
2112
|
-
title: "Copy a repair prompt for an AI agent: spec, score, image links, top mismatched regions and instructions.",
|
|
2113
|
-
children: copied === "copied" ? "Copied \u2713" : copied === "failed" ? "Copy failed" : "Copy prompt"
|
|
2114
|
-
}
|
|
2115
|
-
)
|
|
2116
|
-
] }),
|
|
2117
|
-
status.live_size && status.design_size && capture.state !== "busy" ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2118
|
-
"p",
|
|
2119
|
-
{
|
|
2120
|
-
"data-spec-sizes": "",
|
|
2121
|
-
className: [
|
|
2122
|
-
"border-b border-[color:var(--line)] px-4 py-1.5 text-xs",
|
|
2123
|
-
status.live_size.width === status.design_size.width && status.live_size.height === status.design_size.height ? "text-[color:var(--muted)]" : "font-semibold text-[color:var(--chrome-danger)]"
|
|
2124
|
-
].join(" "),
|
|
2125
|
-
children: [
|
|
2126
|
-
"Live ",
|
|
2127
|
-
status.live_size.width,
|
|
2128
|
-
"\xD7",
|
|
2129
|
-
status.live_size.height,
|
|
2130
|
-
" \xB7 Design ",
|
|
2131
|
-
status.design_size.width,
|
|
2132
|
-
"\xD7",
|
|
2133
|
-
status.design_size.height,
|
|
2134
|
-
status.live_size.width === status.design_size.width && status.live_size.height === status.design_size.height ? " \xB7 sizes match" : " \xB7 SIZE MISMATCH"
|
|
2135
|
-
]
|
|
2136
|
-
}
|
|
2137
|
-
) : null,
|
|
2138
|
-
capture.state === "error" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "border-b border-[color:var(--line)] px-4 py-2 text-xs text-[color:var(--chrome-danger)]", children: capture.message }) : null,
|
|
2139
|
-
capture.state === "busy" ? /* @__PURE__ */ jsxRuntime.jsx(SpecCaptureLoading, {}) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2140
|
-
"iframe",
|
|
2141
|
-
{
|
|
2142
|
-
src: status.overlay_url,
|
|
2143
|
-
title: "Spec overlay",
|
|
2144
|
-
className: "h-full w-full flex-1 border-0"
|
|
2145
|
-
},
|
|
2146
|
-
frameKey
|
|
2147
|
-
)
|
|
2148
|
-
] }) : status && !status.matched ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 text-sm text-[color:var(--muted)]", children: [
|
|
2149
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { children: "No compare target covers this page." }),
|
|
2150
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2151
|
-
"a",
|
|
2152
|
-
{
|
|
2153
|
-
href: status.search_url,
|
|
2154
|
-
target: "_blank",
|
|
2155
|
-
rel: "noreferrer",
|
|
2156
|
-
className: "mt-2 inline-block font-semibold text-[color:var(--color-accent-fg,var(--color-accent))] hover:underline",
|
|
2157
|
-
children: "Search specs \u2197"
|
|
2158
|
-
}
|
|
2159
|
-
)
|
|
2160
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx(ValueSkeleton, { className: "h-4 w-full" }) })
|
|
2161
|
-
] })
|
|
2162
|
-
}
|
|
2163
|
-
),
|
|
2164
|
-
document.body
|
|
2165
|
-
) : null
|
|
2166
|
-
] });
|
|
2167
|
-
}
|
|
2168
|
-
function CloseGlyph({ size = 16, className }) {
|
|
2169
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
|
|
2170
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
2171
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
2172
|
-
] });
|
|
1602
|
+
);
|
|
2173
1603
|
}
|
|
2174
|
-
var NAV_DISPLAY_OPTIONS = [
|
|
2175
|
-
{ value: "menu", label: "Sub Menu" },
|
|
2176
|
-
{ value: "tabs", label: "Tabs Only" },
|
|
2177
|
-
{ value: "both", label: "Both" }
|
|
2178
|
-
];
|
|
2179
1604
|
function parseCssUrl(value) {
|
|
2180
1605
|
if (!value) return null;
|
|
2181
1606
|
const m = /^url\(\s*(['"]?)(.*?)\1\s*\)$/i.exec(value.trim());
|
|
@@ -2455,13 +1880,14 @@ function standardPolitogyNav(opts) {
|
|
|
2455
1880
|
});
|
|
2456
1881
|
};
|
|
2457
1882
|
const accountRows = () => [
|
|
2458
|
-
{ kind: "section", label: "Account" },
|
|
1883
|
+
{ kind: "section", label: "Account", railHidden: true },
|
|
2459
1884
|
// My Profile moved to the Politogy ID account app (auth.politogyvrm.com)
|
|
2460
1885
|
// with the retirement of app.politogyvrm.com.
|
|
2461
|
-
{ label: "My profile", icon: icon("circleUser"), href: withOrg(`${AUTH_ORIGIN}my-profile`) },
|
|
1886
|
+
{ label: "My profile", icon: icon("circleUser"), href: withOrg(`${AUTH_ORIGIN}my-profile`), railHidden: true },
|
|
2462
1887
|
{
|
|
2463
1888
|
label: "Organization settings",
|
|
2464
1889
|
icon: icon("settings"),
|
|
1890
|
+
railHidden: true,
|
|
2465
1891
|
// Organization settings moved to the standalone Settings app
|
|
2466
1892
|
// (settings.politogyvrm.com, Specs/Organization-Settings v12).
|
|
2467
1893
|
href: withOrg(`${SETTINGS_ORIGIN}organization-settings`),
|
|
@@ -2985,6 +2411,8 @@ function NavSidebar({
|
|
|
2985
2411
|
] }) : null,
|
|
2986
2412
|
filterOrgs(org.organizations, orgFilter).map((o) => {
|
|
2987
2413
|
const selected = o.id === org.id;
|
|
2414
|
+
const iconSrc = resolveOrgLogoUrl(o.iconUrl);
|
|
2415
|
+
const logoSrc = iconSrc ? void 0 : resolveOrgLogoUrl(o.logoUrl);
|
|
2988
2416
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2989
2417
|
"button",
|
|
2990
2418
|
{
|
|
@@ -3003,12 +2431,13 @@ function NavSidebar({
|
|
|
3003
2431
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3004
2432
|
"span",
|
|
3005
2433
|
{
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
2434
|
+
"data-org-option-mark": iconSrc ? "icon" : logoSrc ? "logo" : "initials",
|
|
2435
|
+
style: iconSrc ? void 0 : { backgroundColor: o.brandColor || ORG_PILL_DEFAULT },
|
|
2436
|
+
className: `flex h-6 w-6 shrink-0 items-center justify-center overflow-hidden text-[10px] font-bold text-[#FFFFFF] ${iconSrc ? "rounded-[4px] bg-[#FFFFFF]" : "rounded-full"}`,
|
|
2437
|
+
children: iconSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: iconSrc, alt: "", className: "max-h-full max-w-full object-contain" }) : logoSrc ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3009
2438
|
"img",
|
|
3010
2439
|
{
|
|
3011
|
-
src:
|
|
2440
|
+
src: logoSrc,
|
|
3012
2441
|
alt: "",
|
|
3013
2442
|
className: "h-full w-full object-cover"
|
|
3014
2443
|
}
|
|
@@ -3105,7 +2534,7 @@ function NavSidebar({
|
|
|
3105
2534
|
] }),
|
|
3106
2535
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 flex-col overflow-y-auto px-[10px] py-3", children: /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "flex flex-col gap-0.5", children: [
|
|
3107
2536
|
nav.groupLabel ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: `${NAV_EYEBROW_CLASS} px-2 pb-1.5 pt-1 ${rc("max-md:hidden")}`, children: nav.groupLabel }) : null,
|
|
3108
|
-
nav.items.map((item, idx) => {
|
|
2537
|
+
nav.items.filter((item) => !item.railHidden).map((item, idx) => {
|
|
3109
2538
|
if (item.kind === "section") {
|
|
3110
2539
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3111
2540
|
NavSectionRow,
|
|
@@ -5072,6 +4501,7 @@ function FrameBody({
|
|
|
5072
4501
|
name: activeCoreOrg.name,
|
|
5073
4502
|
id: activeCoreOrg.id,
|
|
5074
4503
|
logoUrl: activeCoreOrg.logoUrl,
|
|
4504
|
+
iconUrl: activeCoreOrg.iconUrl ?? null,
|
|
5075
4505
|
brandColor: activeCoreOrg.brandColor,
|
|
5076
4506
|
organizations: coreOrgs,
|
|
5077
4507
|
onSwitch: chrome?.setOrgId
|
|
@@ -5175,6 +4605,7 @@ function TokenChromeStateProvider({
|
|
|
5175
4605
|
id: o.id,
|
|
5176
4606
|
name: o.name,
|
|
5177
4607
|
logoUrl: o.logoUrl,
|
|
4608
|
+
iconUrl: o.iconUrl ?? null,
|
|
5178
4609
|
brandColor: o.brandColor,
|
|
5179
4610
|
role: o.role,
|
|
5180
4611
|
rawRole: o.rawRole ?? null
|
|
@@ -5435,6 +4866,7 @@ function ChromeStateProvider({
|
|
|
5435
4866
|
id: o.id,
|
|
5436
4867
|
name: o.name,
|
|
5437
4868
|
logoUrl: o.logoUrl,
|
|
4869
|
+
iconUrl: o.iconUrl ?? null,
|
|
5438
4870
|
brandColor: o.brandColor,
|
|
5439
4871
|
role: o.role,
|
|
5440
4872
|
rawRole: o.rawRole ?? null
|
|
@@ -5452,6 +4884,7 @@ function ChromeStateProvider({
|
|
|
5452
4884
|
id: o.id,
|
|
5453
4885
|
name: o.name,
|
|
5454
4886
|
logoUrl: o.logoUrl,
|
|
4887
|
+
iconUrl: o.iconUrl ?? null,
|
|
5455
4888
|
brandColor: o.brandColor,
|
|
5456
4889
|
role: o.role,
|
|
5457
4890
|
rawRole: o.rawRole ?? null
|
|
@@ -5644,26 +5077,15 @@ function ChromeTopBarActions() {
|
|
|
5644
5077
|
const state = useChromeState();
|
|
5645
5078
|
if (!state) return null;
|
|
5646
5079
|
const settledOrgId = state.orgsLoading ? null : state.orgId;
|
|
5080
|
+
const tail = orgIdTail(state.orgId);
|
|
5081
|
+
const orgQuery = tail ? `?${ORG_ID_QUERY_PARAM}=${tail}` : "";
|
|
5647
5082
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5648
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5649
|
-
TopBarWidgetSlot,
|
|
5650
|
-
{
|
|
5651
|
-
title: "DevNotes",
|
|
5652
|
-
description: "Capture bugs and notes, filed straight to Forge.",
|
|
5653
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(ChromeDevNotesMenu, {})
|
|
5654
|
-
}
|
|
5655
|
-
),
|
|
5656
5083
|
/* @__PURE__ */ jsxRuntime.jsx(FuelAction, { orgId: settledOrgId }),
|
|
5657
|
-
/* @__PURE__ */ jsxRuntime.jsx(SpecOverlayAction, { orgId: state.orgId, orgName: state.orgs.find((o) => o.id === state.orgId)?.name ?? null }),
|
|
5658
5084
|
/* @__PURE__ */ jsxRuntime.jsx(NotificationsAction, { orgId: settledOrgId }),
|
|
5659
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5085
|
+
/* @__PURE__ */ jsxRuntime.jsx(OrganizationSettingsAction, { href: `${settingsBaseUrl()}/organization-settings${orgQuery}` }),
|
|
5086
|
+
/* @__PURE__ */ jsxRuntime.jsx(MyProfileAction, { href: `${authBaseUrl()}/my-profile${orgQuery}` })
|
|
5660
5087
|
] });
|
|
5661
5088
|
}
|
|
5662
|
-
function ChromeDevNotesMenu() {
|
|
5663
|
-
const { ready, openTasks, activate } = react.useContext(DevNotesTasksContext);
|
|
5664
|
-
if (!ready) return null;
|
|
5665
|
-
return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "contents" }, onPointerDownCapture: activate, onFocusCapture: activate, onKeyDownCapture: activate, children: /* @__PURE__ */ jsxRuntime.jsx(devnotes.DevNotesMenu, { position: "top-right", icon: lu.LuNotebookPen, onViewTasks: openTasks }) });
|
|
5666
|
-
}
|
|
5667
5089
|
function useIsSuperAdmin() {
|
|
5668
5090
|
const state = useChromeState();
|
|
5669
5091
|
return (state?.orgs ?? []).some((o) => /super/i.test(o.rawRole ?? ""));
|
|
@@ -5713,11 +5135,13 @@ function useChromeUserMenu({
|
|
|
5713
5135
|
organizationSettingsHref: `${settings}/organization-settings${orgSuffix}`,
|
|
5714
5136
|
roles: (state?.orgs ?? []).filter((o) => o.rawRole || o.role).map((o) => ({ role: o.rawRole ?? o.role, orgName: o.name })),
|
|
5715
5137
|
accounts: [{ id: userId, name: userName, email, avatarUrl: null, active: true }],
|
|
5716
|
-
addAccountHref: `${auth}/my-profile#accounts
|
|
5138
|
+
addAccountHref: `${auth}/my-profile#accounts`
|
|
5717
5139
|
// No theme control: the product is light only (Reference/UI-Platform
|
|
5718
5140
|
// "Theme — light only"; shell 0.73.0 stopped applying dark). The account
|
|
5719
5141
|
// menu rendered an "Appearance" radio with one option until 0.77.0.
|
|
5720
|
-
|
|
5142
|
+
// Ben 2026-11-04: no Navigation display option in the account menu. The
|
|
5143
|
+
// preference itself (state.navDisplay, its storage and the frame's use of
|
|
5144
|
+
// it) is unchanged; only the menu control is gone.
|
|
5721
5145
|
};
|
|
5722
5146
|
}
|
|
5723
5147
|
var MODE_DEFS = [
|