@the-portland-company/shell 0.16.7 → 0.17.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/index.cjs CHANGED
@@ -1284,6 +1284,142 @@ function resolveModeConfig(mode) {
1284
1284
  }
1285
1285
  return modes.find((m) => m.id === mode.currentMode) ?? modes[0] ?? null;
1286
1286
  }
1287
+
1288
+ // src/theme-preference.ts
1289
+ var THEME_STORAGE_KEY = "politogy_theme";
1290
+ var THEME_COOKIE = "politogy_theme";
1291
+ var VALID = ["light", "dark", "system"];
1292
+ function isThemePreference(v) {
1293
+ return typeof v === "string" && VALID.includes(v);
1294
+ }
1295
+ function readThemeCookie() {
1296
+ if (typeof document === "undefined") return null;
1297
+ try {
1298
+ const m = document.cookie.match(
1299
+ new RegExp(`(?:^|;\\s*)${THEME_COOKIE}=([^;]+)`)
1300
+ );
1301
+ const v = m ? decodeURIComponent(m[1]) : null;
1302
+ return isThemePreference(v) ? v : null;
1303
+ } catch {
1304
+ return null;
1305
+ }
1306
+ }
1307
+ function writeThemeCookie(pref) {
1308
+ if (typeof document === "undefined" || typeof window === "undefined") return;
1309
+ const host = window.location.hostname;
1310
+ const onParentDomain = /(^|\.)politogyvrm\.com$/i.test(host);
1311
+ const domainAttr = onParentDomain ? "; Domain=.politogyvrm.com" : "";
1312
+ const secure = window.location.protocol === "https:" ? "; Secure" : "";
1313
+ document.cookie = `${THEME_COOKIE}=${encodeURIComponent(
1314
+ pref
1315
+ )}; Path=/; Max-Age=31536000; SameSite=Lax${domainAttr}${secure}`;
1316
+ }
1317
+ function resolveTheme(pref) {
1318
+ if (pref === "system") {
1319
+ try {
1320
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
1321
+ } catch {
1322
+ return "light";
1323
+ }
1324
+ }
1325
+ return pref;
1326
+ }
1327
+ function applyThemePreference(pref) {
1328
+ if (typeof document === "undefined") return;
1329
+ const resolved = resolveTheme(pref);
1330
+ const d = document.documentElement;
1331
+ d.classList.toggle("dark", resolved === "dark");
1332
+ try {
1333
+ d.style.colorScheme = resolved;
1334
+ } catch {
1335
+ }
1336
+ }
1337
+ `(function(){try{
1338
+ var KEY=${JSON.stringify(THEME_STORAGE_KEY)};
1339
+ var VALID=["light","dark","system"];
1340
+ function fromCookie(){try{var m=document.cookie.match(/(?:^|;\\s*)${THEME_COOKIE}=([^;]+)/);return m?decodeURIComponent(m[1]):null;}catch(e){return null;}}
1341
+ var cookie=fromCookie();
1342
+ var ls=null;try{ls=localStorage.getItem(KEY);}catch(e){}
1343
+ var pref=cookie||ls||"system";
1344
+ if(VALID.indexOf(pref)===-1)pref="system";
1345
+ if(cookie&&cookie!==ls){try{localStorage.setItem(KEY,cookie);}catch(e){}}
1346
+ var resolved=pref;
1347
+ if(pref==="system"){try{resolved=window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";}catch(e){resolved="light";}}
1348
+ var d=document.documentElement;
1349
+ if(resolved==="dark"){d.classList.add("dark");}else{d.classList.remove("dark");}
1350
+ try{d.style.colorScheme=resolved;}catch(e){}
1351
+ }catch(e){}})();`;
1352
+ function formatFirstLast(name) {
1353
+ const parts = name.split(/\s+/).filter(Boolean);
1354
+ if (parts.length <= 1) return name;
1355
+ return `${parts[0]} ${parts[parts.length - 1][0].toUpperCase()}.`;
1356
+ }
1357
+ var THEME_OPTIONS = [
1358
+ { value: "light", label: "Light" },
1359
+ { value: "system", label: "System" },
1360
+ { value: "dark", label: "Dark" }
1361
+ ];
1362
+ function AppearanceControl() {
1363
+ const [value, setValue] = react$1.useState("system");
1364
+ react$1.useEffect(() => {
1365
+ const cookie = readThemeCookie();
1366
+ let ls = null;
1367
+ try {
1368
+ ls = localStorage.getItem(THEME_STORAGE_KEY);
1369
+ } catch {
1370
+ }
1371
+ setValue(cookie ?? (isThemePreference(ls) ? ls : null) ?? "system");
1372
+ }, []);
1373
+ const pick = (next) => {
1374
+ setValue(next);
1375
+ writeThemeCookie(next);
1376
+ try {
1377
+ localStorage.setItem(THEME_STORAGE_KEY, next);
1378
+ } catch {
1379
+ }
1380
+ applyThemePreference(next);
1381
+ try {
1382
+ window.dispatchEvent(
1383
+ new CustomEvent("politogy:theme-preference", { detail: next })
1384
+ );
1385
+ } catch {
1386
+ }
1387
+ };
1388
+ return /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { px: 3, py: 2, children: [
1389
+ /* @__PURE__ */ jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "gray.500", fontWeight: "semibold", mb: 2, children: "APPEARANCE" }),
1390
+ /* @__PURE__ */ jsxRuntime.jsx(
1391
+ react.HStack,
1392
+ {
1393
+ spacing: 0,
1394
+ borderWidth: "1px",
1395
+ borderColor: "var(--line, #E2E8F0)",
1396
+ borderRadius: "full",
1397
+ overflow: "hidden",
1398
+ children: THEME_OPTIONS.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
1399
+ react.Box,
1400
+ {
1401
+ as: "button",
1402
+ type: "button",
1403
+ role: "radio",
1404
+ "aria-checked": value === opt.value,
1405
+ onClick: () => pick(opt.value),
1406
+ flex: 1,
1407
+ px: 3,
1408
+ py: 1.5,
1409
+ fontSize: "xs",
1410
+ fontWeight: "semibold",
1411
+ bg: value === opt.value ? "var(--color-race-red, #E63946)" : "transparent",
1412
+ color: value === opt.value ? "white" : "gray.500",
1413
+ _hover: value === opt.value ? {} : { bg: "var(--panel, #F7FAFC)" },
1414
+ transition: "background 0.15s",
1415
+ children: opt.label
1416
+ },
1417
+ opt.value
1418
+ ))
1419
+ }
1420
+ )
1421
+ ] });
1422
+ }
1287
1423
  function avatarUrlFromUser(user) {
1288
1424
  if (!user) return void 0;
1289
1425
  const meta = user.raw.user_metadata;
@@ -1306,7 +1442,9 @@ function UserMenu() {
1306
1442
  const menuListBg = "var(--topbar-bg, #FFFFFF)";
1307
1443
  const menuListBorderColor = react.useColorModeValue("gray.200", "gray.700");
1308
1444
  if (status !== "authenticated" || !user) return null;
1309
- const displayName = getUserDisplayName(user.raw);
1445
+ const rawDisplayName = getUserDisplayName(user.raw);
1446
+ const hasRealName = !!rawDisplayName && !rawDisplayName.includes("@");
1447
+ const displayName = hasRealName ? formatFirstLast(rawDisplayName) : user.email ?? rawDisplayName;
1310
1448
  const avatarUrl = userAvatarUrl ?? avatarUrlFromUser(user);
1311
1449
  const modeConfig = resolveModeConfig(mode);
1312
1450
  const currentOrg = org?.currentOrg ?? null;
@@ -1332,7 +1470,7 @@ function UserMenu() {
1332
1470
  ml: "2",
1333
1471
  children: [
1334
1472
  /* @__PURE__ */ jsxRuntime.jsx(react.HStack, { spacing: 2, children: /* @__PURE__ */ jsxRuntime.jsx(react.Text, { fontSize: "sm", children: displayName }) }),
1335
- /* @__PURE__ */ jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "gray.500", children: user.email })
1473
+ hasRealName ? /* @__PURE__ */ jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "gray.500", children: user.email }) : null
1336
1474
  ]
1337
1475
  }
1338
1476
  ),
@@ -1452,6 +1590,8 @@ function UserMenu() {
1452
1590
  )
1453
1591
  ] }),
1454
1592
  /* @__PURE__ */ jsxRuntime.jsx(react.MenuDivider, {}),
1593
+ /* @__PURE__ */ jsxRuntime.jsx(AppearanceControl, {}),
1594
+ /* @__PURE__ */ jsxRuntime.jsx(react.MenuDivider, {}),
1455
1595
  /* @__PURE__ */ jsxRuntime.jsx(react.MenuItem, { icon: /* @__PURE__ */ jsxRuntime.jsx(fi.FiLogOut, {}), onClick: () => void signOut(), children: "Sign out" })
1456
1596
  ] }) })
1457
1597
  ] }) });