@the-portland-company/shell 0.59.0 → 0.60.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/dist/app-chrome.cjs +98 -1
- package/dist/app-chrome.cjs.map +1 -1
- package/dist/app-chrome.js +1 -1
- package/dist/{chunk-BV2QF7NY.js → chunk-AZB46QJH.js} +100 -3
- package/dist/chunk-AZB46QJH.js.map +1 -0
- package/dist/native.cjs +98 -1
- package/dist/native.cjs.map +1 -1
- package/dist/native.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-BV2QF7NY.js.map +0 -1
package/dist/app-chrome.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { broadcastLogout, watchLogout } from './chunk-LL6LSJAH.js';
|
|
3
|
-
import { clearBetaBannerDismissal, standardPolitogyNav, isNavItemActive, NavAppShell, NavChromeProvider, FuelAction, NavDisplayAction, TopBarWidgetSlot, SpecOverlayAction, SupportAction, NotificationsAction, useChromeMe, useThemePreference, useNavChromeFetcher, useChromeOrgs } from './chunk-
|
|
3
|
+
import { clearBetaBannerDismissal, standardPolitogyNav, isNavItemActive, NavAppShell, NavChromeProvider, FuelAction, NavDisplayAction, TopBarWidgetSlot, SpecOverlayAction, SupportAction, NotificationsAction, useChromeMe, useThemePreference, useNavChromeFetcher, useChromeOrgs } from './chunk-AZB46QJH.js';
|
|
4
4
|
import './chunk-U4RZPZET.js';
|
|
5
5
|
import { NAV_DISPLAY_DEFAULT, resolveNavDisplay, isNavDisplayPreference, writeNavDisplayCookie, writeNavDisplayLocal, NAV_DISPLAY_STORAGE_KEY, readNavDisplayCookie } from './chunk-6UHJV2FH.js';
|
|
6
6
|
import { resolveActiveOrg, isOrgId, writeActiveOrgCookie, ACTIVE_ORG_QUERY_PARAM, readActiveOrgCookie } from './chunk-W2HITMV5.js';
|
|
@@ -1280,10 +1280,68 @@ function specOverlayFetchJson(path) {
|
|
|
1280
1280
|
return res.json();
|
|
1281
1281
|
});
|
|
1282
1282
|
}
|
|
1283
|
+
function currentChromeTheme() {
|
|
1284
|
+
const root = document.documentElement;
|
|
1285
|
+
if (root.classList.contains("dark") || root.getAttribute("data-theme") === "dark") return "dark";
|
|
1286
|
+
if (root.classList.contains("light") || root.getAttribute("data-theme") === "light") return "light";
|
|
1287
|
+
return window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
1288
|
+
}
|
|
1289
|
+
async function captureMyView(status, orgId) {
|
|
1290
|
+
const md = navigator.mediaDevices;
|
|
1291
|
+
const stream = await md.getDisplayMedia({
|
|
1292
|
+
video: { displaySurface: "browser" },
|
|
1293
|
+
audio: false,
|
|
1294
|
+
preferCurrentTab: true,
|
|
1295
|
+
selfBrowserSurface: "include",
|
|
1296
|
+
surfaceSwitching: "exclude"
|
|
1297
|
+
});
|
|
1298
|
+
try {
|
|
1299
|
+
const video = document.createElement("video");
|
|
1300
|
+
video.muted = true;
|
|
1301
|
+
video.srcObject = stream;
|
|
1302
|
+
await video.play();
|
|
1303
|
+
await new Promise((r) => setTimeout(r, 600));
|
|
1304
|
+
const w = window.innerWidth;
|
|
1305
|
+
const h = window.innerHeight;
|
|
1306
|
+
const canvas = document.createElement("canvas");
|
|
1307
|
+
canvas.width = w;
|
|
1308
|
+
canvas.height = h;
|
|
1309
|
+
const ctx = canvas.getContext("2d");
|
|
1310
|
+
if (!ctx) throw new Error("canvas unavailable");
|
|
1311
|
+
ctx.drawImage(video, 0, 0, w, h);
|
|
1312
|
+
const blob = await new Promise((r) => canvas.toBlob(r, "image/png"));
|
|
1313
|
+
if (!blob) throw new Error("frame encode failed");
|
|
1314
|
+
let crop = null;
|
|
1315
|
+
const el = status.live_selector ? document.querySelector(status.live_selector) : null;
|
|
1316
|
+
if (el) {
|
|
1317
|
+
const b = el.getBoundingClientRect();
|
|
1318
|
+
const x = Math.max(0, Math.round(b.left));
|
|
1319
|
+
const y = Math.max(0, Math.round(b.top));
|
|
1320
|
+
crop = { x, y, width: Math.min(w - x, Math.round(b.width)), height: Math.min(h - y, Math.round(b.height)) };
|
|
1321
|
+
}
|
|
1322
|
+
const form = new FormData();
|
|
1323
|
+
form.append("image", blob, "live.png");
|
|
1324
|
+
form.append(
|
|
1325
|
+
"meta",
|
|
1326
|
+
JSON.stringify({ url: window.location.href, theme: currentChromeTheme(), org_id: orgId ?? null, crop })
|
|
1327
|
+
);
|
|
1328
|
+
const res = await fetch(status.capture_url ?? `${SPECS_ORIGIN}/api/overlay/capture`, {
|
|
1329
|
+
method: "POST",
|
|
1330
|
+
credentials: "include",
|
|
1331
|
+
headers: { "X-Spec-Capture": "1" },
|
|
1332
|
+
body: form
|
|
1333
|
+
});
|
|
1334
|
+
if (!res.ok) throw new Error(`capture ${res.status}: ${(await res.text()).slice(0, 160)}`);
|
|
1335
|
+
} finally {
|
|
1336
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1283
1339
|
function SpecOverlayAction({ orgId } = {}) {
|
|
1284
1340
|
const [access, setAccess] = useState(() => readSpecOverlayAccessCache());
|
|
1285
1341
|
const [status, setStatus] = useState(null);
|
|
1286
1342
|
const [open, setOpen] = useState(false);
|
|
1343
|
+
const [capture, setCapture] = useState({ state: "idle" });
|
|
1344
|
+
const [frameKey, setFrameKey] = useState(0);
|
|
1287
1345
|
const href = useLocationHref();
|
|
1288
1346
|
useEffect(() => {
|
|
1289
1347
|
if (access) return;
|
|
@@ -1311,6 +1369,32 @@ function SpecOverlayAction({ orgId } = {}) {
|
|
|
1311
1369
|
}, 300);
|
|
1312
1370
|
return () => window.clearTimeout(timer);
|
|
1313
1371
|
}, [access?.allowed, href, orgId]);
|
|
1372
|
+
const onCaptureMyView = async () => {
|
|
1373
|
+
if (!status || !status.matched) return;
|
|
1374
|
+
const before = status.finished_at;
|
|
1375
|
+
setCapture({ state: "busy" });
|
|
1376
|
+
setOpen(false);
|
|
1377
|
+
try {
|
|
1378
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
1379
|
+
await captureMyView(status, orgId);
|
|
1380
|
+
setOpen(true);
|
|
1381
|
+
const url = `/api/overlay/status?url=${encodeURIComponent(window.location.href)}`;
|
|
1382
|
+
for (let i = 0; i < 45; i++) {
|
|
1383
|
+
await new Promise((r) => setTimeout(r, 4e3));
|
|
1384
|
+
const next = await specOverlayFetchJson(url).catch(() => null);
|
|
1385
|
+
if (next && next.matched && next.finished_at !== before) {
|
|
1386
|
+
setStatus(next);
|
|
1387
|
+
setFrameKey((k) => k + 1);
|
|
1388
|
+
setCapture({ state: "idle" });
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
setCapture({ state: "error", message: "Compare did not finish in 3 min." });
|
|
1393
|
+
} catch (e) {
|
|
1394
|
+
setOpen(true);
|
|
1395
|
+
setCapture({ state: "error", message: e instanceof Error ? e.message : String(e) });
|
|
1396
|
+
}
|
|
1397
|
+
};
|
|
1314
1398
|
if (!access?.allowed) return null;
|
|
1315
1399
|
const badgeLabel = status && status.matched ? `${Math.round(status.mismatch_pct)}%` : status ? "\u2014" : void 0;
|
|
1316
1400
|
const badgeTone = status && status.matched ? status.passed ? "default" : "danger" : "default";
|
|
@@ -1397,15 +1481,28 @@ function SpecOverlayAction({ orgId } = {}) {
|
|
|
1397
1481
|
className: "text-[color:var(--color-accent)] hover:underline",
|
|
1398
1482
|
children: "Compare tab \u2197"
|
|
1399
1483
|
}
|
|
1484
|
+
),
|
|
1485
|
+
/* @__PURE__ */ jsx(
|
|
1486
|
+
"button",
|
|
1487
|
+
{
|
|
1488
|
+
type: "button",
|
|
1489
|
+
disabled: capture.state === "busy",
|
|
1490
|
+
onClick: onCaptureMyView,
|
|
1491
|
+
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",
|
|
1492
|
+
title: "Compare the design against this tab exactly as you see it (your org, data and theme).",
|
|
1493
|
+
children: capture.state === "busy" ? "Comparing\u2026" : "Capture my view"
|
|
1494
|
+
}
|
|
1400
1495
|
)
|
|
1401
1496
|
] }),
|
|
1497
|
+
capture.state === "error" ? /* @__PURE__ */ jsx("p", { className: "border-b border-[color:var(--line)] px-4 py-2 text-xs text-[color:var(--chrome-danger)]", children: capture.message }) : null,
|
|
1402
1498
|
/* @__PURE__ */ jsx(
|
|
1403
1499
|
"iframe",
|
|
1404
1500
|
{
|
|
1405
1501
|
src: status.overlay_url,
|
|
1406
1502
|
title: "Spec overlay",
|
|
1407
1503
|
className: "h-full w-full flex-1 border-0"
|
|
1408
|
-
}
|
|
1504
|
+
},
|
|
1505
|
+
frameKey
|
|
1409
1506
|
)
|
|
1410
1507
|
] }) : status && !status.matched ? /* @__PURE__ */ jsxs("div", { className: "p-4 text-sm text-[color:var(--muted)]", children: [
|
|
1411
1508
|
/* @__PURE__ */ jsx("p", { children: "No compare target covers this page." }),
|
|
@@ -3220,5 +3317,5 @@ function NavAppShell({
|
|
|
3220
3317
|
}
|
|
3221
3318
|
|
|
3222
3319
|
export { BETA_BANNER_DISMISS_KEY, CAMPAIGN_FEATURE_AVAILABILITY, CORE_APP_ORIGIN, CORE_BILLING_PATH, FuelAction, NavAppShell, NavBetaBanner, NavChromeProvider, NavDisplayAction, NavSidebar, NavTabStrip, NavThemeControl, NavTopBar, NotificationsAction, SPECS_ORIGIN, SpecOverlayAction, SupportAction, TopBarCountBadge, TopBarPanelArrow, TopBarPopover, TopBarTooltip, TopBarWidgetSlot, clearBetaBannerDismissal, envChipClasses, isNavItemActive, resolveOrgLogoUrl, standardPolitogyNav, topBarIconButtonClass, useChromeMe, useChromeOrgs, useNavChromeFetcher, useThemePreference };
|
|
3223
|
-
//# sourceMappingURL=chunk-
|
|
3224
|
-
//# sourceMappingURL=chunk-
|
|
3320
|
+
//# sourceMappingURL=chunk-AZB46QJH.js.map
|
|
3321
|
+
//# sourceMappingURL=chunk-AZB46QJH.js.map
|