@the-portland-company/shell 0.96.2 → 0.97.1
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 +79 -652
- 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 +14 -23
- package/dist/app-chrome.js.map +1 -1
- package/dist/{chunk-SKXBGQ33.js → chunk-5IRZD6GF.js} +36 -32
- package/dist/chunk-5IRZD6GF.js.map +1 -0
- package/dist/{chunk-537NZZVP.js → chunk-XPTC47TY.js} +42 -7
- package/dist/chunk-XPTC47TY.js.map +1 -0
- package/dist/{index-DJtKw0Fd.d.cts → index-SHcw15A4.d.cts} +54 -2
- package/dist/{index-HwDXhFOA.d.ts → index-smsLP_tM.d.ts} +54 -2
- package/dist/index.cjs +10 -3
- 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 +74 -31
- 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-537NZZVP.js.map +0 -1
- package/dist/chunk-SKXBGQ33.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}`;
|
|
@@ -623,19 +622,26 @@ function TopBarCountBadge({ count }) {
|
|
|
623
622
|
}
|
|
624
623
|
function TopBarTooltip({
|
|
625
624
|
title,
|
|
626
|
-
description
|
|
625
|
+
description,
|
|
626
|
+
align = "center"
|
|
627
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;
|
|
628
631
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
629
632
|
"span",
|
|
630
633
|
{
|
|
631
634
|
role: "tooltip",
|
|
632
|
-
|
|
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`,
|
|
633
637
|
children: [
|
|
634
638
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
635
639
|
"span",
|
|
636
640
|
{
|
|
637
641
|
"aria-hidden": true,
|
|
638
|
-
|
|
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)]`
|
|
639
645
|
}
|
|
640
646
|
),
|
|
641
647
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-[color:var(--app-btn-tooltip-fg,#FFFFFF)]", children: title }),
|
|
@@ -653,16 +659,6 @@ function TopBarPanelArrow() {
|
|
|
653
659
|
}
|
|
654
660
|
);
|
|
655
661
|
}
|
|
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
662
|
var THEME_OPTIONS = [
|
|
667
663
|
{ value: "light", label: "Light" }
|
|
668
664
|
];
|
|
@@ -946,23 +942,6 @@ function BellOffGlyph({ size = 14, className }) {
|
|
|
946
942
|
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "1", y1: "1", x2: "23", y2: "23" })
|
|
947
943
|
] });
|
|
948
944
|
}
|
|
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
945
|
function PlusGlyph({ size = 14, className }) {
|
|
967
946
|
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
947
|
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
|
|
@@ -1596,586 +1575,39 @@ function NotificationsAction({ orgId }) {
|
|
|
1596
1575
|
] })
|
|
1597
1576
|
);
|
|
1598
1577
|
}
|
|
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
|
|
1578
|
+
var NAV_DISPLAY_OPTIONS = [
|
|
1579
|
+
{ value: "menu", label: "Sub Menu" },
|
|
1580
|
+
{ value: "tabs", label: "Tabs Only" },
|
|
1581
|
+
{ value: "both", label: "Both" }
|
|
1582
|
+
];
|
|
1583
|
+
function TopBarLinkAction({ label, description, href, icon: Icon }) {
|
|
1584
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("a", { href, "aria-label": label, className: topBarIconButtonClass(), children: [
|
|
1585
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { size: TOPBAR_GLYPH_SIZE, className: "text-current" }),
|
|
1586
|
+
/* @__PURE__ */ jsxRuntime.jsx(TopBarTooltip, { title: label, description, align: "end" })
|
|
1790
1587
|
] });
|
|
1791
1588
|
}
|
|
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 })
|
|
1589
|
+
function OrganizationSettingsAction({ href }) {
|
|
1590
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1591
|
+
TopBarLinkAction,
|
|
1592
|
+
{
|
|
1593
|
+
label: "Organization settings",
|
|
1594
|
+
description: "Users, Brand Kit, modes and billing for this organization.",
|
|
1595
|
+
href,
|
|
1596
|
+
icon: lu.LuSettings
|
|
1597
|
+
}
|
|
1898
1598
|
);
|
|
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
1599
|
}
|
|
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) });
|
|
1600
|
+
function MyProfileAction({ href }) {
|
|
1601
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1602
|
+
TopBarLinkAction,
|
|
1603
|
+
{
|
|
1604
|
+
label: "My profile",
|
|
1605
|
+
description: "Your photo, organizations, security and notifications.",
|
|
1606
|
+
href,
|
|
1607
|
+
icon: lu.LuCircleUser
|
|
1991
1608
|
}
|
|
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
|
-
] });
|
|
1609
|
+
);
|
|
2173
1610
|
}
|
|
2174
|
-
var NAV_DISPLAY_OPTIONS = [
|
|
2175
|
-
{ value: "menu", label: "Sub Menu" },
|
|
2176
|
-
{ value: "tabs", label: "Tabs Only" },
|
|
2177
|
-
{ value: "both", label: "Both" }
|
|
2178
|
-
];
|
|
2179
1611
|
function parseCssUrl(value) {
|
|
2180
1612
|
if (!value) return null;
|
|
2181
1613
|
const m = /^url\(\s*(['"]?)(.*?)\1\s*\)$/i.exec(value.trim());
|
|
@@ -2455,13 +1887,14 @@ function standardPolitogyNav(opts) {
|
|
|
2455
1887
|
});
|
|
2456
1888
|
};
|
|
2457
1889
|
const accountRows = () => [
|
|
2458
|
-
{ kind: "section", label: "Account" },
|
|
1890
|
+
{ kind: "section", label: "Account", railHidden: true },
|
|
2459
1891
|
// My Profile moved to the Politogy ID account app (auth.politogyvrm.com)
|
|
2460
1892
|
// with the retirement of app.politogyvrm.com.
|
|
2461
|
-
{ label: "My profile", icon: icon("circleUser"), href: withOrg(`${AUTH_ORIGIN}my-profile`) },
|
|
1893
|
+
{ label: "My profile", icon: icon("circleUser"), href: withOrg(`${AUTH_ORIGIN}my-profile`), railHidden: true },
|
|
2462
1894
|
{
|
|
2463
1895
|
label: "Organization settings",
|
|
2464
1896
|
icon: icon("settings"),
|
|
1897
|
+
railHidden: true,
|
|
2465
1898
|
// Organization settings moved to the standalone Settings app
|
|
2466
1899
|
// (settings.politogyvrm.com, Specs/Organization-Settings v12).
|
|
2467
1900
|
href: withOrg(`${SETTINGS_ORIGIN}organization-settings`),
|
|
@@ -2697,6 +2130,33 @@ function shortEnvLabel(label) {
|
|
|
2697
2130
|
if (l === "development") return "dev";
|
|
2698
2131
|
return l;
|
|
2699
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
|
+
}
|
|
2700
2160
|
var ORG_PILL_DEFAULT = "var(--color-accent-bg-safe,var(--color-accent))";
|
|
2701
2161
|
var RailCollapsibleContext = react.createContext(false);
|
|
2702
2162
|
function useRailCollapse() {
|
|
@@ -3108,7 +2568,7 @@ function NavSidebar({
|
|
|
3108
2568
|
] }),
|
|
3109
2569
|
/* @__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: [
|
|
3110
2570
|
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,
|
|
3111
|
-
nav.items.map((item, idx) => {
|
|
2571
|
+
nav.items.filter((item) => !item.railHidden).map((item, idx) => {
|
|
3112
2572
|
if (item.kind === "section") {
|
|
3113
2573
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3114
2574
|
NavSectionRow,
|
|
@@ -3189,31 +2649,7 @@ function NavSidebar({
|
|
|
3189
2649
|
}
|
|
3190
2650
|
)
|
|
3191
2651
|
] }, l.label)) }) : null,
|
|
3192
|
-
footer.envLabel || footer.commit || footer.version ? /* @__PURE__ */ jsxRuntime.
|
|
3193
|
-
"div",
|
|
3194
|
-
{
|
|
3195
|
-
"data-shell-meta": true,
|
|
3196
|
-
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")}`,
|
|
3197
|
-
children: [
|
|
3198
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1.5", children: [
|
|
3199
|
-
footer.envLabel ? shortEnvLabel(footer.envLabel) : null,
|
|
3200
|
-
footer.commit ? footer.commit.slice(-4) : null,
|
|
3201
|
-
footer.version ? `v${footer.version.replace(/^v/, "")}` : null
|
|
3202
|
-
].filter((v) => Boolean(v)).map((v, i) => /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
|
|
3203
|
-
i > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: "|" }) : null,
|
|
3204
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: v })
|
|
3205
|
-
] }, i)) }),
|
|
3206
|
-
footer.commitDate || footer.commitAuthor ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
3207
|
-
footer.commitDate ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: new Date(footer.commitDate).toLocaleDateString(void 0, {
|
|
3208
|
-
month: "short",
|
|
3209
|
-
day: "numeric"
|
|
3210
|
-
}) }) : null,
|
|
3211
|
-
footer.commitDate && footer.commitAuthor ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: "|" }) : null,
|
|
3212
|
-
footer.commitAuthor ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: footer.commitAuthor.split(/\s+/)[0] }) : null
|
|
3213
|
-
] }) : null
|
|
3214
|
-
]
|
|
3215
|
-
}
|
|
3216
|
-
) : null
|
|
2652
|
+
footer.envLabel || footer.commit || footer.version ? /* @__PURE__ */ jsxRuntime.jsx(ShellBuildLine, { footer, hidden: rc("max-md:hidden") }) : null
|
|
3217
2653
|
] }) : null
|
|
3218
2654
|
]
|
|
3219
2655
|
}
|
|
@@ -5651,26 +5087,15 @@ function ChromeTopBarActions() {
|
|
|
5651
5087
|
const state = useChromeState();
|
|
5652
5088
|
if (!state) return null;
|
|
5653
5089
|
const settledOrgId = state.orgsLoading ? null : state.orgId;
|
|
5090
|
+
const tail = orgIdTail(state.orgId);
|
|
5091
|
+
const orgQuery = tail ? `?${ORG_ID_QUERY_PARAM}=${tail}` : "";
|
|
5654
5092
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5655
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5656
|
-
TopBarWidgetSlot,
|
|
5657
|
-
{
|
|
5658
|
-
title: "DevNotes",
|
|
5659
|
-
description: "Capture bugs and notes, filed straight to Forge.",
|
|
5660
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(ChromeDevNotesMenu, {})
|
|
5661
|
-
}
|
|
5662
|
-
),
|
|
5663
5093
|
/* @__PURE__ */ jsxRuntime.jsx(FuelAction, { orgId: settledOrgId }),
|
|
5664
|
-
/* @__PURE__ */ jsxRuntime.jsx(SpecOverlayAction, { orgId: state.orgId, orgName: state.orgs.find((o) => o.id === state.orgId)?.name ?? null }),
|
|
5665
5094
|
/* @__PURE__ */ jsxRuntime.jsx(NotificationsAction, { orgId: settledOrgId }),
|
|
5666
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5095
|
+
/* @__PURE__ */ jsxRuntime.jsx(OrganizationSettingsAction, { href: `${settingsBaseUrl()}/organization-settings${orgQuery}` }),
|
|
5096
|
+
/* @__PURE__ */ jsxRuntime.jsx(MyProfileAction, { href: `${authBaseUrl()}/my-profile${orgQuery}` })
|
|
5667
5097
|
] });
|
|
5668
5098
|
}
|
|
5669
|
-
function ChromeDevNotesMenu() {
|
|
5670
|
-
const { ready, openTasks, activate } = react.useContext(DevNotesTasksContext);
|
|
5671
|
-
if (!ready) return null;
|
|
5672
|
-
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 }) });
|
|
5673
|
-
}
|
|
5674
5099
|
function useIsSuperAdmin() {
|
|
5675
5100
|
const state = useChromeState();
|
|
5676
5101
|
return (state?.orgs ?? []).some((o) => /super/i.test(o.rawRole ?? ""));
|
|
@@ -5720,11 +5145,13 @@ function useChromeUserMenu({
|
|
|
5720
5145
|
organizationSettingsHref: `${settings}/organization-settings${orgSuffix}`,
|
|
5721
5146
|
roles: (state?.orgs ?? []).filter((o) => o.rawRole || o.role).map((o) => ({ role: o.rawRole ?? o.role, orgName: o.name })),
|
|
5722
5147
|
accounts: [{ id: userId, name: userName, email, avatarUrl: null, active: true }],
|
|
5723
|
-
addAccountHref: `${auth}/my-profile#accounts
|
|
5148
|
+
addAccountHref: `${auth}/my-profile#accounts`
|
|
5724
5149
|
// No theme control: the product is light only (Reference/UI-Platform
|
|
5725
5150
|
// "Theme — light only"; shell 0.73.0 stopped applying dark). The account
|
|
5726
5151
|
// menu rendered an "Appearance" radio with one option until 0.77.0.
|
|
5727
|
-
|
|
5152
|
+
// Ben 2026-11-04: no Navigation display option in the account menu. The
|
|
5153
|
+
// preference itself (state.navDisplay, its storage and the frame's use of
|
|
5154
|
+
// it) is unchanged; only the menu control is gone.
|
|
5728
5155
|
};
|
|
5729
5156
|
}
|
|
5730
5157
|
var MODE_DEFS = [
|