@scalemule/news-publication 0.0.9 → 0.0.10

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
@@ -868,6 +868,13 @@ var COOKIE_CATEGORIES = [
868
868
  duration: "1 year",
869
869
  type: "Local Storage"
870
870
  },
871
+ {
872
+ name: "news_publication_theme",
873
+ provider: "First-Party",
874
+ purpose: "Remembers your reading theme preference (light, dark, or system).",
875
+ duration: "1 year",
876
+ type: "HTTP Cookie"
877
+ },
871
878
  {
872
879
  name: "audio_narration_speed",
873
880
  provider: "First-Party",
@@ -1366,6 +1373,282 @@ function CookieSettingsView({
1366
1373
  ] });
1367
1374
  }
1368
1375
 
1376
+ // src/theme.ts
1377
+ var THEME_COOKIE = "news_publication_theme";
1378
+ var THEME_STORAGE_KEY = "news_publication_theme";
1379
+ var THEME_EVENT = "news_publication_theme_change";
1380
+ var THEME_MAX_AGE_SECONDS = 365 * 24 * 60 * 60;
1381
+ var memoryTheme = "system";
1382
+ function getSystemTheme() {
1383
+ if (typeof window === "undefined" || !window.matchMedia) return "light";
1384
+ try {
1385
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
1386
+ } catch {
1387
+ return "light";
1388
+ }
1389
+ }
1390
+ function resolveEffectiveTheme(mode) {
1391
+ if (mode === "system") return getSystemTheme();
1392
+ return mode;
1393
+ }
1394
+ function getStoredTheme() {
1395
+ if (typeof window === "undefined") return memoryTheme;
1396
+ try {
1397
+ const stored = window.localStorage?.getItem?.(THEME_STORAGE_KEY);
1398
+ if (stored === "light" || stored === "dark" || stored === "system") {
1399
+ memoryTheme = stored;
1400
+ return stored;
1401
+ }
1402
+ } catch {
1403
+ }
1404
+ if (typeof document !== "undefined" && document.cookie) {
1405
+ try {
1406
+ const match = document.cookie.match(new RegExp(`(?:^|; )${THEME_COOKIE}=([^;]*)`));
1407
+ if (match) {
1408
+ const decoded = decodeURIComponent(match[1]);
1409
+ if (decoded === "light" || decoded === "dark" || decoded === "system") {
1410
+ memoryTheme = decoded;
1411
+ return decoded;
1412
+ }
1413
+ }
1414
+ } catch {
1415
+ }
1416
+ }
1417
+ return memoryTheme;
1418
+ }
1419
+ function applyTheme(mode) {
1420
+ if (typeof document === "undefined") return;
1421
+ try {
1422
+ document.documentElement.setAttribute("data-theme", mode);
1423
+ const elements = document.querySelectorAll(".publication");
1424
+ elements.forEach((el) => el.setAttribute("data-theme", mode));
1425
+ } catch {
1426
+ }
1427
+ }
1428
+ var memoryListeners = /* @__PURE__ */ new Set();
1429
+ function setTheme(mode) {
1430
+ memoryTheme = mode;
1431
+ if (typeof window !== "undefined") {
1432
+ try {
1433
+ window.localStorage?.setItem?.(THEME_STORAGE_KEY, mode);
1434
+ } catch {
1435
+ }
1436
+ if (typeof document !== "undefined") {
1437
+ try {
1438
+ document.cookie = `${THEME_COOKIE}=${encodeURIComponent(mode)}; path=/; max-age=${THEME_MAX_AGE_SECONDS}; SameSite=Lax`;
1439
+ } catch {
1440
+ }
1441
+ }
1442
+ applyTheme(mode);
1443
+ const effective = resolveEffectiveTheme(mode);
1444
+ try {
1445
+ window.dispatchEvent(
1446
+ new CustomEvent(THEME_EVENT, {
1447
+ detail: { mode, effective }
1448
+ })
1449
+ );
1450
+ } catch {
1451
+ }
1452
+ }
1453
+ memoryListeners.forEach((listener) => {
1454
+ try {
1455
+ listener();
1456
+ } catch {
1457
+ }
1458
+ });
1459
+ }
1460
+ function clearTheme() {
1461
+ memoryTheme = "system";
1462
+ if (typeof document !== "undefined") {
1463
+ try {
1464
+ document.cookie = `${THEME_COOKIE}=; path=/; max-age=0; SameSite=Lax`;
1465
+ document.documentElement.removeAttribute("data-theme");
1466
+ } catch {
1467
+ }
1468
+ }
1469
+ if (typeof window !== "undefined") {
1470
+ try {
1471
+ window.localStorage?.removeItem?.(THEME_STORAGE_KEY);
1472
+ } catch {
1473
+ }
1474
+ }
1475
+ memoryListeners.forEach((listener) => {
1476
+ try {
1477
+ listener();
1478
+ } catch {
1479
+ }
1480
+ });
1481
+ }
1482
+ function subscribeTheme(listener) {
1483
+ memoryListeners.add(listener);
1484
+ if (typeof window === "undefined") {
1485
+ return () => {
1486
+ memoryListeners.delete(listener);
1487
+ };
1488
+ }
1489
+ const onStorage = (e) => {
1490
+ if (e.key === THEME_STORAGE_KEY) {
1491
+ applyTheme(getStoredTheme());
1492
+ listener();
1493
+ }
1494
+ };
1495
+ const onCustom = () => {
1496
+ listener();
1497
+ };
1498
+ window.addEventListener("storage", onStorage);
1499
+ window.addEventListener(THEME_EVENT, onCustom);
1500
+ let mediaQuery;
1501
+ let onMediaChange;
1502
+ if (window.matchMedia) {
1503
+ try {
1504
+ mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
1505
+ onMediaChange = () => {
1506
+ if (getStoredTheme() === "system") {
1507
+ applyTheme("system");
1508
+ listener();
1509
+ }
1510
+ };
1511
+ if (mediaQuery.addEventListener) {
1512
+ mediaQuery.addEventListener("change", onMediaChange);
1513
+ } else if (mediaQuery.addListener) {
1514
+ mediaQuery.addListener(onMediaChange);
1515
+ }
1516
+ } catch {
1517
+ }
1518
+ }
1519
+ return () => {
1520
+ window.removeEventListener("storage", onStorage);
1521
+ window.removeEventListener(THEME_EVENT, onCustom);
1522
+ if (mediaQuery && onMediaChange) {
1523
+ if (mediaQuery.removeEventListener) {
1524
+ mediaQuery.removeEventListener("change", onMediaChange);
1525
+ } else if (mediaQuery.removeListener) {
1526
+ mediaQuery.removeListener(onMediaChange);
1527
+ }
1528
+ }
1529
+ };
1530
+ }
1531
+ function themeInitScript() {
1532
+ return `(function(){try{var k='${THEME_STORAGE_KEY}',m=localStorage.getItem(k);if(!m){var c=document.cookie.match(/(?:^|; )news_publication_theme=([^;]*)/);m=c?decodeURIComponent(c[1]):'system';}if(m==='light'||m==='dark'||m==='system'){document.documentElement.setAttribute('data-theme',m);}}catch(e){}})();`;
1533
+ }
1534
+ function SunIcon() {
1535
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
1536
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "4" }),
1537
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41" })
1538
+ ] });
1539
+ }
1540
+ function MoonIcon() {
1541
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" }) });
1542
+ }
1543
+ function SystemIcon() {
1544
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
1545
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { width: "20", height: "14", x: "2", y: "3", rx: "2" }),
1546
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "8", x2: "16", y1: "21", y2: "21" }),
1547
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", x2: "12", y1: "17", y2: "21" })
1548
+ ] });
1549
+ }
1550
+ function ThemeToggle({
1551
+ variant = "compact",
1552
+ className = "",
1553
+ showPromotion = false,
1554
+ accountUrl = "/account"
1555
+ }) {
1556
+ const currentTheme = react.useSyncExternalStore(subscribeTheme, getStoredTheme, () => "system");
1557
+ const [mounted, setMounted] = react.useState(false);
1558
+ const [promoVisible, setPromoVisible] = react.useState(false);
1559
+ react.useEffect(() => {
1560
+ setMounted(true);
1561
+ }, []);
1562
+ const handleSelect = (mode) => {
1563
+ setTheme(mode);
1564
+ if (showPromotion) {
1565
+ setPromoVisible(true);
1566
+ }
1567
+ };
1568
+ const active = mounted ? currentTheme : "system";
1569
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `pub-theme-toggle-container ${className}`.trim(), children: [
1570
+ /* @__PURE__ */ jsxRuntime.jsxs(
1571
+ "div",
1572
+ {
1573
+ className: "pub-theme-toggle",
1574
+ "data-variant": variant,
1575
+ role: "group",
1576
+ "aria-label": "Display theme selection",
1577
+ children: [
1578
+ /* @__PURE__ */ jsxRuntime.jsxs(
1579
+ "button",
1580
+ {
1581
+ type: "button",
1582
+ onClick: () => handleSelect("light"),
1583
+ "aria-pressed": active === "light",
1584
+ "data-active": active === "light",
1585
+ title: "Light theme (broadsheet day edition)",
1586
+ "aria-label": "Light theme",
1587
+ children: [
1588
+ /* @__PURE__ */ jsxRuntime.jsx(SunIcon, {}),
1589
+ variant !== "compact" && /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Light" })
1590
+ ]
1591
+ }
1592
+ ),
1593
+ /* @__PURE__ */ jsxRuntime.jsxs(
1594
+ "button",
1595
+ {
1596
+ type: "button",
1597
+ onClick: () => handleSelect("dark"),
1598
+ "aria-pressed": active === "dark",
1599
+ "data-active": active === "dark",
1600
+ title: "Dark theme (broadsheet evening edition)",
1601
+ "aria-label": "Dark theme",
1602
+ children: [
1603
+ /* @__PURE__ */ jsxRuntime.jsx(MoonIcon, {}),
1604
+ variant !== "compact" && /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Dark" })
1605
+ ]
1606
+ }
1607
+ ),
1608
+ /* @__PURE__ */ jsxRuntime.jsxs(
1609
+ "button",
1610
+ {
1611
+ type: "button",
1612
+ onClick: () => handleSelect("system"),
1613
+ "aria-pressed": active === "system",
1614
+ "data-active": active === "system",
1615
+ title: "System preference (matches device setting)",
1616
+ "aria-label": "System theme",
1617
+ children: [
1618
+ /* @__PURE__ */ jsxRuntime.jsx(SystemIcon, {}),
1619
+ variant !== "compact" && /* @__PURE__ */ jsxRuntime.jsx("span", { children: "System" })
1620
+ ]
1621
+ }
1622
+ )
1623
+ ]
1624
+ }
1625
+ ),
1626
+ promoVisible && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pub-theme-promo", role: "status", children: [
1627
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Theme saved for this browser. " }),
1628
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: accountUrl, children: "Create a free reader account" }),
1629
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: " to sync preferences across all your devices." }),
1630
+ /* @__PURE__ */ jsxRuntime.jsx(
1631
+ "button",
1632
+ {
1633
+ type: "button",
1634
+ onClick: () => setPromoVisible(false),
1635
+ "aria-label": "Dismiss note",
1636
+ style: {
1637
+ marginLeft: "8px",
1638
+ background: "none",
1639
+ border: "none",
1640
+ cursor: "pointer",
1641
+ color: "var(--pub-muted)",
1642
+ fontSize: "12px",
1643
+ padding: "0 4px"
1644
+ },
1645
+ children: "\xD7"
1646
+ }
1647
+ )
1648
+ ] })
1649
+ ] });
1650
+ }
1651
+
1369
1652
  exports.ANALYTICS_COOKIE = ANALYTICS_COOKIE;
1370
1653
  exports.ANALYTICS_VERSION = ANALYTICS_VERSION;
1371
1654
  exports.BROADSHEET_THEME = BROADSHEET_THEME;
@@ -1387,6 +1670,11 @@ exports.MEET_ACTIVITIES = MEET_ACTIVITIES;
1387
1670
  exports.NEWS_CACHE = NEWS_CACHE;
1388
1671
  exports.NewsError = NewsError;
1389
1672
  exports.SESSION_IDLE_MS = SESSION_IDLE_MS;
1673
+ exports.THEME_COOKIE = THEME_COOKIE;
1674
+ exports.THEME_EVENT = THEME_EVENT;
1675
+ exports.THEME_MAX_AGE_SECONDS = THEME_MAX_AGE_SECONDS;
1676
+ exports.THEME_STORAGE_KEY = THEME_STORAGE_KEY;
1677
+ exports.ThemeToggle = ThemeToggle;
1390
1678
  exports.UTM_FIELDS = UTM_FIELDS;
1391
1679
  exports.UUID = UUID;
1392
1680
  exports.acceptAllCookies = acceptAllCookies;
@@ -1394,6 +1682,7 @@ exports.acceptRemoteRecord = acceptRemoteRecord;
1394
1682
  exports.acceptSnapshot = acceptSnapshot;
1395
1683
  exports.activeNearby = activeNearby;
1396
1684
  exports.activityLabel = activityLabel;
1685
+ exports.applyTheme = applyTheme;
1397
1686
  exports.articleCacheKey = articleCacheKey;
1398
1687
  exports.articleCacheOptions = articleCacheOptions;
1399
1688
  exports.articleMeta = articleMeta;
@@ -1405,6 +1694,7 @@ exports.categoryLabel = categoryLabel;
1405
1694
  exports.classifiedPath = classifiedPath;
1406
1695
  exports.classifiedSearchPath = classifiedSearchPath;
1407
1696
  exports.clearCookieConsent = clearCookieConsent;
1697
+ exports.clearTheme = clearTheme;
1408
1698
  exports.clipChars = clipChars;
1409
1699
  exports.composeEdition = composeEdition;
1410
1700
  exports.dealDollars = dealDollars;
@@ -1421,6 +1711,8 @@ exports.feedCacheKey = feedCacheKey;
1421
1711
  exports.feedCacheOptions = feedCacheOptions;
1422
1712
  exports.filterNews = filterNews;
1423
1713
  exports.getCookieConsent = getCookieConsent;
1714
+ exports.getStoredTheme = getStoredTheme;
1715
+ exports.getSystemTheme = getSystemTheme;
1424
1716
  exports.hasConsented = hasConsented;
1425
1717
  exports.inventoryCacheOptions = inventoryCacheOptions;
1426
1718
  exports.isAdult = isAdult;
@@ -1451,6 +1743,7 @@ exports.recordConsentToCompliance = recordConsentToCompliance;
1451
1743
  exports.referralCode = referralCode;
1452
1744
  exports.rejectOptionalCookies = rejectOptionalCookies;
1453
1745
  exports.resolveCoupon = resolveCoupon;
1746
+ exports.resolveEffectiveTheme = resolveEffectiveTheme;
1454
1747
  exports.resolveLayout = resolveLayout;
1455
1748
  exports.revalidateDecision = revalidateDecision;
1456
1749
  exports.screenMessage = screenMessage;
@@ -1459,13 +1752,16 @@ exports.sectionDefinition = sectionDefinition;
1459
1752
  exports.sectionId = sectionId;
1460
1753
  exports.sectionPath = sectionPath;
1461
1754
  exports.setCookieConsent = setCookieConsent;
1755
+ exports.setTheme = setTheme;
1462
1756
  exports.shortDeck = shortDeck;
1463
1757
  exports.shortText = shortText;
1464
1758
  exports.shouldServeLastKnown = shouldServeLastKnown;
1465
1759
  exports.snapshotFromRecord = snapshotFromRecord;
1466
1760
  exports.storyDate = storyDate;
1467
1761
  exports.storyTowns = storyTowns;
1762
+ exports.subscribeTheme = subscribeTheme;
1468
1763
  exports.syncPublication = syncPublication;
1764
+ exports.themeInitScript = themeInitScript;
1469
1765
  exports.townSlug = townSlug;
1470
1766
  exports.validCalendarDate = validCalendarDate;
1471
1767
  exports.walnutCreekTimes = walnutCreekTimes;
package/dist/index.d.cts CHANGED
@@ -4,6 +4,7 @@ export { M as MEET_ACTIVITIES, a as MeetActivity, b as MeetupInput, P as Prepare
4
4
  export { C as CLASSIFIED_CATEGORIES, a as ClassifiedCategory, b as ClassifiedListing, E as EmploymentType, J as JOB_EMPLOYMENT_TYPES, P as PreparedClassified, c as PreparedJob, d as PublicDeal, e as PublicDealOffer, f as PublicJob, g as categoryLabel, h as classifiedPath, i as classifiedSearchPath, j as clipChars, k as dealDollars, l as dealGoPath, m as dealsFeedPath, n as employmentLabel, o as isClassifiedCategory, p as isEmploymentType, q as jobEmployerBody, r as jobOpeningBody, s as jobPostingBody, t as prepareClassified, u as prepareJobPost, v as publicJobPath, w as publicJobsPath } from './marketplace-CpGWPbFk.cjs';
5
5
  export { CookieConsentBanner, CookieConsentBannerProps } from './cookie-consent-banner.cjs';
6
6
  export { CookieSettingsView, CookieSettingsViewProps } from './cookie-settings-view.cjs';
7
+ export { ThemeToggle, ThemeToggleProps } from './theme-toggle.cjs';
7
8
  import 'react';
8
9
 
9
10
  /** Walnut Creek Times broadsheet colors. A paper overrides only what it changes. */
@@ -314,4 +315,45 @@ declare function recordConsentToCompliance(record: CookieConsentRecord, options?
314
315
  publicationSlug?: string;
315
316
  }): Promise<void>;
316
317
 
317
- export { ANALYTICS_COOKIE, ANALYTICS_VERSION, type Attribution, BROADSHEET_THEME, COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, type CookieCategory, type CookieCategoryDefinition, type CookieConsentRecord, type CookieDisclosure, type CookiePreferences, CouponOffer, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EVENTS, EditorialSection, FeatureName, HomepageSelection, LAYOUT_COOKIE, NEWS_CACHE, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, SESSION_IDLE_MS, UTM_FIELDS, UUID, acceptAllCookies, activeNearby, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, clearCookieConsent, composeEdition, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getCookieConsent, hasConsented, inventoryCacheOptions, isCategoryAllowed, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, publicHttps, publicUrl, publicationJsonLd, publicationMeta, recordConsentToCompliance, referralCode, rejectOptionalCookies, resolveCoupon, resolveLayout, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setCookieConsent, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
318
+ type ThemeMode = "light" | "dark" | "system";
319
+ type EffectiveTheme = "light" | "dark";
320
+ declare const THEME_COOKIE = "news_publication_theme";
321
+ declare const THEME_STORAGE_KEY = "news_publication_theme";
322
+ declare const THEME_EVENT = "news_publication_theme_change";
323
+ declare const THEME_MAX_AGE_SECONDS: number;
324
+ /**
325
+ * Returns the current system color scheme preference.
326
+ */
327
+ declare function getSystemTheme(): EffectiveTheme;
328
+ /**
329
+ * Resolves the effective "light" or "dark" appearance from a chosen mode.
330
+ */
331
+ declare function resolveEffectiveTheme(mode: ThemeMode): EffectiveTheme;
332
+ /**
333
+ * Reads stored theme mode from localStorage, falling back to cookie, memory, then "system".
334
+ */
335
+ declare function getStoredTheme(): ThemeMode;
336
+ /**
337
+ * Applies the given theme mode to documentElement attribute and publication elements.
338
+ */
339
+ declare function applyTheme(mode: ThemeMode): void;
340
+ /**
341
+ * Persists the reader's theme mode in memory, localStorage, and cookie,
342
+ * applies the attribute, and broadcasts an event for other components and browser tabs.
343
+ */
344
+ declare function setTheme(mode: ThemeMode): void;
345
+ /**
346
+ * Resets stored theme to "system" and cleans cookies/storage.
347
+ */
348
+ declare function clearTheme(): void;
349
+ /**
350
+ * Subscribes to theme changes across in-memory calls, storage events, custom events, and system media queries.
351
+ */
352
+ declare function subscribeTheme(listener: () => void): () => void;
353
+ /**
354
+ * Inline JavaScript to inject into <head> to synchronously set data-theme before
355
+ * any DOM renders, eliminating any Flash of Unstyled Content (FOUC).
356
+ */
357
+ declare function themeInitScript(): string;
358
+
359
+ export { ANALYTICS_COOKIE, ANALYTICS_VERSION, type Attribution, BROADSHEET_THEME, COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, type CookieCategory, type CookieCategoryDefinition, type CookieConsentRecord, type CookieDisclosure, type CookiePreferences, CouponOffer, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EVENTS, EditorialSection, type EffectiveTheme, FeatureName, HomepageSelection, LAYOUT_COOKIE, NEWS_CACHE, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, SESSION_IDLE_MS, THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, type ThemeMode, UTM_FIELDS, UUID, acceptAllCookies, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, clearCookieConsent, clearTheme, composeEdition, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getCookieConsent, getStoredTheme, getSystemTheme, hasConsented, inventoryCacheOptions, isCategoryAllowed, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, publicHttps, publicUrl, publicationJsonLd, publicationMeta, recordConsentToCompliance, referralCode, rejectOptionalCookies, resolveCoupon, resolveEffectiveTheme, resolveLayout, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setCookieConsent, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export { M as MEET_ACTIVITIES, a as MeetActivity, b as MeetupInput, P as Prepare
4
4
  export { C as CLASSIFIED_CATEGORIES, a as ClassifiedCategory, b as ClassifiedListing, E as EmploymentType, J as JOB_EMPLOYMENT_TYPES, P as PreparedClassified, c as PreparedJob, d as PublicDeal, e as PublicDealOffer, f as PublicJob, g as categoryLabel, h as classifiedPath, i as classifiedSearchPath, j as clipChars, k as dealDollars, l as dealGoPath, m as dealsFeedPath, n as employmentLabel, o as isClassifiedCategory, p as isEmploymentType, q as jobEmployerBody, r as jobOpeningBody, s as jobPostingBody, t as prepareClassified, u as prepareJobPost, v as publicJobPath, w as publicJobsPath } from './marketplace-CpGWPbFk.js';
5
5
  export { CookieConsentBanner, CookieConsentBannerProps } from './cookie-consent-banner.js';
6
6
  export { CookieSettingsView, CookieSettingsViewProps } from './cookie-settings-view.js';
7
+ export { ThemeToggle, ThemeToggleProps } from './theme-toggle.js';
7
8
  import 'react';
8
9
 
9
10
  /** Walnut Creek Times broadsheet colors. A paper overrides only what it changes. */
@@ -314,4 +315,45 @@ declare function recordConsentToCompliance(record: CookieConsentRecord, options?
314
315
  publicationSlug?: string;
315
316
  }): Promise<void>;
316
317
 
317
- export { ANALYTICS_COOKIE, ANALYTICS_VERSION, type Attribution, BROADSHEET_THEME, COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, type CookieCategory, type CookieCategoryDefinition, type CookieConsentRecord, type CookieDisclosure, type CookiePreferences, CouponOffer, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EVENTS, EditorialSection, FeatureName, HomepageSelection, LAYOUT_COOKIE, NEWS_CACHE, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, SESSION_IDLE_MS, UTM_FIELDS, UUID, acceptAllCookies, activeNearby, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, clearCookieConsent, composeEdition, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getCookieConsent, hasConsented, inventoryCacheOptions, isCategoryAllowed, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, publicHttps, publicUrl, publicationJsonLd, publicationMeta, recordConsentToCompliance, referralCode, rejectOptionalCookies, resolveCoupon, resolveLayout, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setCookieConsent, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
318
+ type ThemeMode = "light" | "dark" | "system";
319
+ type EffectiveTheme = "light" | "dark";
320
+ declare const THEME_COOKIE = "news_publication_theme";
321
+ declare const THEME_STORAGE_KEY = "news_publication_theme";
322
+ declare const THEME_EVENT = "news_publication_theme_change";
323
+ declare const THEME_MAX_AGE_SECONDS: number;
324
+ /**
325
+ * Returns the current system color scheme preference.
326
+ */
327
+ declare function getSystemTheme(): EffectiveTheme;
328
+ /**
329
+ * Resolves the effective "light" or "dark" appearance from a chosen mode.
330
+ */
331
+ declare function resolveEffectiveTheme(mode: ThemeMode): EffectiveTheme;
332
+ /**
333
+ * Reads stored theme mode from localStorage, falling back to cookie, memory, then "system".
334
+ */
335
+ declare function getStoredTheme(): ThemeMode;
336
+ /**
337
+ * Applies the given theme mode to documentElement attribute and publication elements.
338
+ */
339
+ declare function applyTheme(mode: ThemeMode): void;
340
+ /**
341
+ * Persists the reader's theme mode in memory, localStorage, and cookie,
342
+ * applies the attribute, and broadcasts an event for other components and browser tabs.
343
+ */
344
+ declare function setTheme(mode: ThemeMode): void;
345
+ /**
346
+ * Resets stored theme to "system" and cleans cookies/storage.
347
+ */
348
+ declare function clearTheme(): void;
349
+ /**
350
+ * Subscribes to theme changes across in-memory calls, storage events, custom events, and system media queries.
351
+ */
352
+ declare function subscribeTheme(listener: () => void): () => void;
353
+ /**
354
+ * Inline JavaScript to inject into <head> to synchronously set data-theme before
355
+ * any DOM renders, eliminating any Flash of Unstyled Content (FOUC).
356
+ */
357
+ declare function themeInitScript(): string;
358
+
359
+ export { ANALYTICS_COOKIE, ANALYTICS_VERSION, type Attribution, BROADSHEET_THEME, COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, type CookieCategory, type CookieCategoryDefinition, type CookieConsentRecord, type CookieDisclosure, type CookiePreferences, CouponOffer, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EVENTS, EditorialSection, type EffectiveTheme, FeatureName, HomepageSelection, LAYOUT_COOKIE, NEWS_CACHE, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, SESSION_IDLE_MS, THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, type ThemeMode, UTM_FIELDS, UUID, acceptAllCookies, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, clearCookieConsent, clearTheme, composeEdition, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getCookieConsent, getStoredTheme, getSystemTheme, hasConsented, inventoryCacheOptions, isCategoryAllowed, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, publicHttps, publicUrl, publicationJsonLd, publicationMeta, recordConsentToCompliance, referralCode, rejectOptionalCookies, resolveCoupon, resolveEffectiveTheme, resolveLayout, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setCookieConsent, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
+ export { THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, ThemeToggle, applyTheme, clearTheme, getStoredTheme, getSystemTheme, resolveEffectiveTheme, setTheme, subscribeTheme, themeInitScript } from './chunk-NQD3M7ZY.js';
1
2
  import { definePublication } from './chunk-C7JTEHTH.js';
2
3
  export { BROADSHEET_THEME, FEATURE_NAMES, LAYOUT_COOKIE, acceptRemoteRecord, acceptSnapshot, definePublication, featureEnabled, parseSnapshot, publicationFromSnapshot, publicationRecord, resolveLayout, snapshotFromRecord, syncPublication } from './chunk-C7JTEHTH.js';
3
4
  export { CLASSIFIED_CATEGORIES, JOB_EMPLOYMENT_TYPES, categoryLabel, classifiedPath, classifiedSearchPath, clipChars, dealDollars, dealGoPath, dealsFeedPath, employmentLabel, isClassifiedCategory, isEmploymentType, jobEmployerBody, jobOpeningBody, jobPostingBody, prepareClassified, prepareJobPost, publicJobPath, publicJobsPath } from './chunk-C7ZPLH2K.js';
4
5
  export { MEET_ACTIVITIES, activityLabel, canHostMeetup, isAdult, prepareMeetup, screenMessage } from './chunk-GB36YM56.js';
5
- export { CookieConsentBanner } from './chunk-PYCU2T5O.js';
6
- export { CookieSettingsView } from './chunk-R33MJWND.js';
7
- export { COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, acceptAllCookies, clearCookieConsent, getCookieConsent, hasConsented, isCategoryAllowed, recordConsentToCompliance, rejectOptionalCookies, setCookieConsent } from './chunk-TJ7RL3PV.js';
6
+ export { CookieConsentBanner } from './chunk-5QHNNRVQ.js';
7
+ export { CookieSettingsView } from './chunk-AYSBXLOT.js';
8
+ export { COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, acceptAllCookies, clearCookieConsent, getCookieConsent, hasConsented, isCategoryAllowed, recordConsentToCompliance, rejectOptionalCookies, setCookieConsent } from './chunk-RNXTBN5Z.js';
8
9
 
9
10
  // src/preset.ts
10
11
  var walnutCreekTimes = definePublication({