@scalemule/news-publication 0.0.57 → 0.0.60
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/{chunk-3HPE2Y7V.js → chunk-7HXSQY4T.js} +28 -2
- package/dist/index.cjs +154 -0
- package/dist/index.d.cts +108 -1
- package/dist/index.d.ts +108 -1
- package/dist/index.js +151 -1
- package/dist/pets-forms.js +1 -1
- package/dist/pets-plugin.js +1 -1
- package/dist/pets-publication.cjs +29 -2
- package/dist/pets-publication.js +2 -1
- package/dist/pets-view.cjs +28 -2
- package/dist/pets-view.d.cts +10 -2
- package/dist/pets-view.d.ts +10 -2
- package/dist/pets-view.js +1 -1
- package/images/README.md +15 -3
- package/package.json +1 -1
- package/pets.css +5 -2
- package/images/paw-jasper-mobile.webp +0 -0
|
@@ -512,9 +512,28 @@ function SponsorAcknowledgment2({
|
|
|
512
512
|
/* @__PURE__ */ jsx("small", { children: "Paid community sponsorship. Sponsorship does not influence editorial selections and is not an endorsement." })
|
|
513
513
|
] });
|
|
514
514
|
}
|
|
515
|
+
var DEFAULT_COVER = {
|
|
516
|
+
url: "https://cdn-public.scalemule.com/01a1382e-6aff-7091-a757-12433a04eed1/anonymous/photos/01a13b52-a168-7131-90f6-a216dbe8dc3b/w800_h0_scaledown_q85_webp.webp",
|
|
517
|
+
alt: "A white Labrador retriever resting on a sunlit lawn",
|
|
518
|
+
credit: "Boyko Mihaylov"
|
|
519
|
+
};
|
|
520
|
+
function safeCoverUrl(url) {
|
|
521
|
+
if (!url || !/^https:\/\/[^\s"\\]+$/.test(url)) return null;
|
|
522
|
+
try {
|
|
523
|
+
return new URL(url).protocol === "https:" ? url : null;
|
|
524
|
+
} catch {
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
function resolveCover(cover) {
|
|
529
|
+
if (!cover || !safeCoverUrl(cover.url) || !cover.alt || !cover.credit)
|
|
530
|
+
return DEFAULT_COVER;
|
|
531
|
+
return cover;
|
|
532
|
+
}
|
|
515
533
|
function PetsSection({
|
|
516
534
|
name = "Bay Area Paw",
|
|
517
535
|
tagline = "Happier pets. Closer communities.",
|
|
536
|
+
coverPhoto,
|
|
518
537
|
community,
|
|
519
538
|
communities,
|
|
520
539
|
resources,
|
|
@@ -533,6 +552,8 @@ function PetsSection({
|
|
|
533
552
|
eventsUrl,
|
|
534
553
|
accountUrl
|
|
535
554
|
}) {
|
|
555
|
+
const cover = resolveCover(coverPhoto);
|
|
556
|
+
const coverCreditUrl = safeCoverUrl(cover.credit_url);
|
|
536
557
|
const location = community?.name || regionLabel || "the Bay Area";
|
|
537
558
|
const link = (id) => `${resourceBasePath || basePath}/${encodeURIComponent(id)}`;
|
|
538
559
|
const local = (id) => communities.find((c) => c.id === id)?.name || "Our community";
|
|
@@ -636,10 +657,15 @@ function PetsSection({
|
|
|
636
657
|
"div",
|
|
637
658
|
{
|
|
638
659
|
role: "img",
|
|
639
|
-
"aria-label":
|
|
660
|
+
"aria-label": cover.alt,
|
|
661
|
+
style: { backgroundImage: `url("${cover.url}")` }
|
|
640
662
|
}
|
|
641
663
|
),
|
|
642
|
-
/* @__PURE__ */
|
|
664
|
+
/* @__PURE__ */ jsxs("figcaption", { children: [
|
|
665
|
+
"Cover photograph by",
|
|
666
|
+
" ",
|
|
667
|
+
coverCreditUrl ? /* @__PURE__ */ jsx("a", { href: coverCreditUrl, rel: "noopener noreferrer", children: cover.credit }) : cover.credit
|
|
668
|
+
] })
|
|
643
669
|
] })
|
|
644
670
|
] }),
|
|
645
671
|
!unavailable && /* @__PURE__ */ jsxs("nav", { className: "paw-shortcuts", "aria-label": "Useful pet resources", children: [
|
package/dist/index.cjs
CHANGED
|
@@ -633,6 +633,156 @@ function defaultRevalidateTargets() {
|
|
|
633
633
|
};
|
|
634
634
|
}
|
|
635
635
|
|
|
636
|
+
// src/navigation.ts
|
|
637
|
+
var NAVIGATION_GROUPS = [
|
|
638
|
+
{ id: "news-knowledge", label: "News & knowledge" },
|
|
639
|
+
{ id: "local-life", label: "Local life" },
|
|
640
|
+
{ id: "marketplace", label: "Marketplace" },
|
|
641
|
+
{ id: "publication", label: "Publication" }
|
|
642
|
+
];
|
|
643
|
+
function editorial(id, section, overrides = {}) {
|
|
644
|
+
return {
|
|
645
|
+
id,
|
|
646
|
+
section,
|
|
647
|
+
kind: "editorial",
|
|
648
|
+
label: sectionDefinition(section).label,
|
|
649
|
+
href: sectionPath(section),
|
|
650
|
+
order: 0,
|
|
651
|
+
priority: 0,
|
|
652
|
+
...overrides
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
var NETWORK_NAVIGATION = [
|
|
656
|
+
// Primary editorial row. "News" is the Civic life desk; the section slug and story classifications are unchanged.
|
|
657
|
+
editorial("news", "Civic life", { label: "News", order: 10, priority: 100, primary: true }),
|
|
658
|
+
editorial("community", "Community", { order: 20, priority: 90, primary: true }),
|
|
659
|
+
editorial("food-drink", "Food & places", { order: 30, priority: 85, primary: true }),
|
|
660
|
+
editorial("things-to-do", "Events", { order: 40, priority: 80, primary: true }),
|
|
661
|
+
editorial("health", "Health", { order: 50, priority: 75, primary: true }),
|
|
662
|
+
editorial("real-estate", "Real Estate", { order: 60, priority: 70, primary: true }),
|
|
663
|
+
{ id: "autos", label: "Autos", href: "/autos", kind: "marketplace", group: "marketplace", order: 65, priority: 65, primary: true, more: true, enabled: false },
|
|
664
|
+
editorial("business", "Local businesses", { order: 70, priority: 60, primary: true }),
|
|
665
|
+
{ id: "meet", label: "Meet", href: "/meet", kind: "product", order: 80, priority: 50, primary: true, feature: "chat" },
|
|
666
|
+
{ id: "jokes", label: "Jokes", href: "/smile", kind: "product", order: 90, priority: 40, primary: true, feature: "smile" },
|
|
667
|
+
// More — News & knowledge.
|
|
668
|
+
editorial("ai-technology", "AI & Technology", { label: "AI & technology", group: "news-knowledge", order: 10, more: true }),
|
|
669
|
+
editorial("schools", "Schools", { label: "Schools & education", group: "news-knowledge", order: 20, more: true }),
|
|
670
|
+
editorial("civic-life", "Civic life", { group: "news-knowledge", order: 30, more: true }),
|
|
671
|
+
editorial("environment", "Environment", { group: "news-knowledge", order: 40, more: true }),
|
|
672
|
+
editorial("transportation", "Transportation", { group: "news-knowledge", order: 50, more: true }),
|
|
673
|
+
// More — Local life.
|
|
674
|
+
{ id: "pets", label: "Pets", href: "/pets", kind: "product", group: "local-life", order: 10, more: true },
|
|
675
|
+
{ id: "celebrations", label: "Celebrations", href: "/sections/celebrations", kind: "product", group: "local-life", order: 20, more: true, enabled: false },
|
|
676
|
+
{ id: "community-maps", label: "Community maps", href: "/community-maps", kind: "product", group: "local-life", order: 30, more: true },
|
|
677
|
+
{ id: "town-chat", label: "Town chat", href: "/chat", kind: "product", group: "local-life", order: 40, more: true, feature: "chat" },
|
|
678
|
+
{ id: "neighborhood-guides", label: "Neighborhood guides", href: "/guides", kind: "product", group: "local-life", order: 50, more: true, enabled: false },
|
|
679
|
+
{ id: "poll", label: "Poll", href: "/polls", kind: "product", group: "local-life", order: 60, more: true, publications: ["walnutcreektimes"] },
|
|
680
|
+
// More — Marketplace.
|
|
681
|
+
{ id: "jobs", label: "Jobs", href: "/jobs", kind: "marketplace", group: "marketplace", order: 10, more: true, feature: "jobs" },
|
|
682
|
+
{ id: "classifieds", label: "Classifieds", href: "/classifieds", kind: "marketplace", group: "marketplace", order: 20, more: true, feature: "classifieds" },
|
|
683
|
+
{ id: "local-services", label: "Local services", href: "/services", kind: "marketplace", group: "marketplace", order: 40, more: true, enabled: false },
|
|
684
|
+
// More — Publication.
|
|
685
|
+
{ id: "latest", label: "Latest", href: "/news", kind: "publication", group: "publication", order: 10, more: true },
|
|
686
|
+
{ id: "this-week", label: "This week", href: "/this-week", kind: "publication", group: "publication", order: 20, more: true },
|
|
687
|
+
{ id: "newsletter", label: "Newsletter", href: "/newsletter", kind: "publication", group: "publication", order: 30, more: true },
|
|
688
|
+
{ id: "about", label: "About", href: "/about", kind: "publication", group: "publication", order: 40, more: true },
|
|
689
|
+
{ id: "advertise", label: "Advertise", href: "/advertise", kind: "publication", group: "publication", order: 50, more: true },
|
|
690
|
+
{ id: "contact", label: "Contact", href: "/contact", kind: "publication", group: "publication", order: 60, more: true }
|
|
691
|
+
];
|
|
692
|
+
var MAX_LABEL = 60;
|
|
693
|
+
function safeHref(href) {
|
|
694
|
+
if (typeof href !== "string") return null;
|
|
695
|
+
const value = href.trim();
|
|
696
|
+
if (!value.startsWith("/") || value.startsWith("//") || /[\s\\]/.test(value)) return null;
|
|
697
|
+
return value;
|
|
698
|
+
}
|
|
699
|
+
function safeLabel(label) {
|
|
700
|
+
if (typeof label !== "string") return null;
|
|
701
|
+
const value = label.trim();
|
|
702
|
+
return value && value.length <= MAX_LABEL ? value : null;
|
|
703
|
+
}
|
|
704
|
+
var GROUP_IDS = new Set(NAVIGATION_GROUPS.map((group) => group.id));
|
|
705
|
+
var KINDS = /* @__PURE__ */ new Set(["editorial", "product", "marketplace", "publication"]);
|
|
706
|
+
function applyNavigationConfig(defaults, config) {
|
|
707
|
+
const merged = new Map(defaults.map((destination) => [destination.id, { ...destination }]));
|
|
708
|
+
for (const override of config?.destinations ?? []) {
|
|
709
|
+
if (!override || typeof override.id !== "string" || !override.id) continue;
|
|
710
|
+
const current = merged.get(override.id);
|
|
711
|
+
const label = safeLabel(override.label);
|
|
712
|
+
const href = safeHref(override.href);
|
|
713
|
+
if (!current && !(label && href)) continue;
|
|
714
|
+
const next = current ? { ...current } : {
|
|
715
|
+
id: override.id,
|
|
716
|
+
label,
|
|
717
|
+
href,
|
|
718
|
+
kind: "product",
|
|
719
|
+
order: 1e3,
|
|
720
|
+
priority: 0
|
|
721
|
+
};
|
|
722
|
+
if (label) next.label = label;
|
|
723
|
+
if (href) next.href = href;
|
|
724
|
+
if (typeof override.order === "number" && Number.isFinite(override.order)) next.order = override.order;
|
|
725
|
+
if (typeof override.priority === "number" && Number.isFinite(override.priority)) next.priority = override.priority;
|
|
726
|
+
if (typeof override.enabled === "boolean") next.enabled = override.enabled;
|
|
727
|
+
if (typeof override.primary === "boolean") next.primary = override.primary;
|
|
728
|
+
if (typeof override.more === "boolean") next.more = override.more;
|
|
729
|
+
if (override.group && GROUP_IDS.has(override.group)) next.group = override.group;
|
|
730
|
+
if (override.kind && KINDS.has(override.kind)) next.kind = override.kind;
|
|
731
|
+
if (typeof override.section === "string") next.section = override.section;
|
|
732
|
+
if (typeof override.feature === "string") next.feature = override.feature;
|
|
733
|
+
if (Array.isArray(override.publications)) next.publications = override.publications.filter((slug) => typeof slug === "string");
|
|
734
|
+
merged.set(next.id, next);
|
|
735
|
+
}
|
|
736
|
+
return Array.from(merged.values());
|
|
737
|
+
}
|
|
738
|
+
function activeFeatured(config, now) {
|
|
739
|
+
if (config?.featured === null) return null;
|
|
740
|
+
const featured = config?.featured;
|
|
741
|
+
if (!featured) return void 0;
|
|
742
|
+
const label = safeLabel(featured.label);
|
|
743
|
+
const href = safeHref(featured.href);
|
|
744
|
+
if (!label || !href) return void 0;
|
|
745
|
+
const time = now.getTime();
|
|
746
|
+
if (featured.from && time < Date.parse(featured.from)) return void 0;
|
|
747
|
+
if (featured.until && time >= Date.parse(featured.until)) return void 0;
|
|
748
|
+
return { label, href };
|
|
749
|
+
}
|
|
750
|
+
function resolveNavigation(context) {
|
|
751
|
+
const now = context.now ?? /* @__PURE__ */ new Date();
|
|
752
|
+
const sections2 = /* @__PURE__ */ new Set([...context.sections ?? [], ...context.discovered ?? []]);
|
|
753
|
+
const featureEnabled2 = context.featureEnabled ?? (() => false);
|
|
754
|
+
const destinations = applyNavigationConfig(NETWORK_NAVIGATION, context.config);
|
|
755
|
+
const visible = destinations.filter((destination) => {
|
|
756
|
+
if (destination.enabled === false) return false;
|
|
757
|
+
if (destination.feature && !featureEnabled2(destination.feature)) return false;
|
|
758
|
+
if (destination.section && !sections2.has(destination.section)) return false;
|
|
759
|
+
if (destination.publications && !destination.publications.includes(context.publication)) return false;
|
|
760
|
+
return true;
|
|
761
|
+
});
|
|
762
|
+
const link = (destination) => ({
|
|
763
|
+
id: destination.id,
|
|
764
|
+
label: destination.label,
|
|
765
|
+
href: destination.href,
|
|
766
|
+
kind: destination.kind,
|
|
767
|
+
priority: destination.priority ?? 0
|
|
768
|
+
});
|
|
769
|
+
const byOrder = (left, right) => left.order - right.order || left.label.localeCompare(right.label);
|
|
770
|
+
const primary = visible.filter((destination) => destination.primary).sort(byOrder).map(link);
|
|
771
|
+
const covered = new Set(destinations.map((destination) => destination.section).filter(Boolean));
|
|
772
|
+
const stray = [...new Set(context.discovered ?? [])].filter((name) => name !== "Local news" && !covered.has(name)).sort((left, right) => left.localeCompare(right)).map((name) => {
|
|
773
|
+
const definition = sectionDefinition(name);
|
|
774
|
+
return { id: definition.id, label: definition.label, href: sectionPath(name), kind: "editorial", priority: 0 };
|
|
775
|
+
});
|
|
776
|
+
const groups = NAVIGATION_GROUPS.map((group) => ({
|
|
777
|
+
...group,
|
|
778
|
+
links: [
|
|
779
|
+
...visible.filter((destination) => destination.more && destination.group === group.id).sort(byOrder).map(link),
|
|
780
|
+
...group.id === "news-knowledge" ? stray : []
|
|
781
|
+
]
|
|
782
|
+
})).filter((group) => group.links.length > 0);
|
|
783
|
+
return { primary, groups, featured: activeFeatured(context.config, now) };
|
|
784
|
+
}
|
|
785
|
+
|
|
636
786
|
// src/analytics.ts
|
|
637
787
|
var ANALYTICS_VERSION = 1;
|
|
638
788
|
var ANALYTICS_COOKIE = "napsite_analytics_v1";
|
|
@@ -3464,6 +3614,8 @@ exports.LAYOUT_COOKIE = LAYOUT_COOKIE;
|
|
|
3464
3614
|
exports.LOCAL_BRIEFING_POLICY_VERSION = LOCAL_BRIEFING_POLICY_VERSION;
|
|
3465
3615
|
exports.LocalNetworkBar = LocalNetworkBar;
|
|
3466
3616
|
exports.MEET_ACTIVITIES = MEET_ACTIVITIES;
|
|
3617
|
+
exports.NAVIGATION_GROUPS = NAVIGATION_GROUPS;
|
|
3618
|
+
exports.NETWORK_NAVIGATION = NETWORK_NAVIGATION;
|
|
3467
3619
|
exports.NETWORK_PARENT = NETWORK_PARENT;
|
|
3468
3620
|
exports.NEWS_CACHE = NEWS_CACHE;
|
|
3469
3621
|
exports.NetworkCopyrightNotice = NetworkCopyrightNotice;
|
|
@@ -3484,6 +3636,7 @@ exports.acceptRemoteRecord = acceptRemoteRecord;
|
|
|
3484
3636
|
exports.acceptSnapshot = acceptSnapshot;
|
|
3485
3637
|
exports.activeNearby = activeNearby;
|
|
3486
3638
|
exports.activityLabel = activityLabel;
|
|
3639
|
+
exports.applyNavigationConfig = applyNavigationConfig;
|
|
3487
3640
|
exports.applyTheme = applyTheme;
|
|
3488
3641
|
exports.articleCacheKey = articleCacheKey;
|
|
3489
3642
|
exports.articleCacheOptions = articleCacheOptions;
|
|
@@ -3562,6 +3715,7 @@ exports.renderArticleBlocksToHtml = renderArticleBlocksToHtml;
|
|
|
3562
3715
|
exports.resolveCoupon = resolveCoupon;
|
|
3563
3716
|
exports.resolveEffectiveTheme = resolveEffectiveTheme;
|
|
3564
3717
|
exports.resolveLayout = resolveLayout;
|
|
3718
|
+
exports.resolveNavigation = resolveNavigation;
|
|
3565
3719
|
exports.resolveSection = resolveSection;
|
|
3566
3720
|
exports.revalidateDecision = revalidateDecision;
|
|
3567
3721
|
exports.safeSponsorUrl = safeSponsorUrl;
|
package/dist/index.d.cts
CHANGED
|
@@ -274,6 +274,113 @@ declare function nearbyUpdates(items: NewsItem[], selection?: HomepageSelection,
|
|
|
274
274
|
declare function shortDeck(value: string | null | undefined, limit?: number): string;
|
|
275
275
|
declare function storyDate(value: string): string | null;
|
|
276
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Shared network navigation.
|
|
279
|
+
*
|
|
280
|
+
* One configuration drives the primary row, the More menu and the hamburger
|
|
281
|
+
* directory on every Bay Area News Network paper. The defaults below ship in
|
|
282
|
+
* this package; day-to-day changes (labels, order, enabling Autos, a featured
|
|
283
|
+
* seasonal destination) are `NavigationConfig` overrides stored in each
|
|
284
|
+
* paper's site record and picked up at request time — no release required.
|
|
285
|
+
*/
|
|
286
|
+
/** Editorial sections, community products and marketplaces stay distinct in config even where readers see one row. */
|
|
287
|
+
type NavigationKind = "editorial" | "product" | "marketplace" | "publication";
|
|
288
|
+
type NavigationGroup = "news-knowledge" | "local-life" | "marketplace" | "publication";
|
|
289
|
+
declare const NAVIGATION_GROUPS: {
|
|
290
|
+
id: NavigationGroup;
|
|
291
|
+
label: string;
|
|
292
|
+
}[];
|
|
293
|
+
type NavigationDestination = {
|
|
294
|
+
/** Stable identifier; overrides and additions merge by id. */
|
|
295
|
+
id: string;
|
|
296
|
+
label: string;
|
|
297
|
+
href: string;
|
|
298
|
+
kind: NavigationKind;
|
|
299
|
+
/** More-menu column; destinations without a group never render in More. */
|
|
300
|
+
group?: NavigationGroup;
|
|
301
|
+
/** Reading order inside the primary row and inside each More group. */
|
|
302
|
+
order: number;
|
|
303
|
+
/** Responsive weight: when the row runs out of width, lower priority collapses into More first. */
|
|
304
|
+
priority?: number;
|
|
305
|
+
/** Renders in the primary editorial row. */
|
|
306
|
+
primary?: boolean;
|
|
307
|
+
/** Renders in its group inside the More menu. */
|
|
308
|
+
more?: boolean;
|
|
309
|
+
/** Hidden everywhere until enabled (default true). */
|
|
310
|
+
enabled?: boolean;
|
|
311
|
+
/** Publication feature flag that must be on (e.g. "jobs", "chat", "smile", "classifieds"). */
|
|
312
|
+
feature?: string;
|
|
313
|
+
/** Canonical section name: shown only when the publication configures or publishes the section. */
|
|
314
|
+
section?: string;
|
|
315
|
+
/** Restricts the destination to the listed publication slugs. */
|
|
316
|
+
publications?: string[];
|
|
317
|
+
};
|
|
318
|
+
/** Record-stored delta applied over the shipped defaults. Unknown ids with a label and href are additions. */
|
|
319
|
+
type NavigationOverride = {
|
|
320
|
+
id: string;
|
|
321
|
+
} & Partial<Omit<NavigationDestination, "id">>;
|
|
322
|
+
type FeaturedDestination = {
|
|
323
|
+
label: string;
|
|
324
|
+
href: string;
|
|
325
|
+
/** ISO instants limiting when the promotion shows. */
|
|
326
|
+
from?: string;
|
|
327
|
+
until?: string;
|
|
328
|
+
};
|
|
329
|
+
/** Stored in the site record under `render.navigation`. Everything is optional: absent fields keep defaults. */
|
|
330
|
+
type NavigationConfig = {
|
|
331
|
+
version?: 1;
|
|
332
|
+
destinations?: NavigationOverride[];
|
|
333
|
+
/** Overrides the seasonal featured slot; `null` suppresses it entirely. */
|
|
334
|
+
featured?: FeaturedDestination | null;
|
|
335
|
+
};
|
|
336
|
+
/**
|
|
337
|
+
* The network-wide default navigation. Primary order and collapse priority
|
|
338
|
+
* follow the 2026 information architecture: editorial sections first, then
|
|
339
|
+
* the community products. "Front page" is the masthead's job, so it is not a
|
|
340
|
+
* destination. Autos ships disabled; enabling it is a record override.
|
|
341
|
+
*/
|
|
342
|
+
declare const NETWORK_NAVIGATION: NavigationDestination[];
|
|
343
|
+
type NavigationLink = {
|
|
344
|
+
id: string;
|
|
345
|
+
label: string;
|
|
346
|
+
href: string;
|
|
347
|
+
kind: NavigationKind;
|
|
348
|
+
priority: number;
|
|
349
|
+
};
|
|
350
|
+
type NavigationGroupLinks = {
|
|
351
|
+
id: NavigationGroup;
|
|
352
|
+
label: string;
|
|
353
|
+
links: NavigationLink[];
|
|
354
|
+
};
|
|
355
|
+
type ResolvedNavigation = {
|
|
356
|
+
/** Editorial row in reading order; collapse into More by ascending priority. */
|
|
357
|
+
primary: NavigationLink[];
|
|
358
|
+
/** More-menu columns in canonical group order; empty groups are dropped. */
|
|
359
|
+
groups: NavigationGroupLinks[];
|
|
360
|
+
/** Active featured promotion from config, if any. `null` means "configured off". */
|
|
361
|
+
featured?: {
|
|
362
|
+
label: string;
|
|
363
|
+
href: string;
|
|
364
|
+
} | null;
|
|
365
|
+
};
|
|
366
|
+
type NavigationContext = {
|
|
367
|
+
/** This paper's slug. */
|
|
368
|
+
publication: string;
|
|
369
|
+
/** Sections the publication configures (profile taxonomy). */
|
|
370
|
+
sections?: string[];
|
|
371
|
+
/** Sections observed in the live feed; they satisfy a section gate too. */
|
|
372
|
+
discovered?: string[];
|
|
373
|
+
/** Publication feature flags. */
|
|
374
|
+
featureEnabled?: (feature: string) => boolean;
|
|
375
|
+
/** Record-stored overrides. Invalid entries are ignored, never fatal. */
|
|
376
|
+
config?: NavigationConfig | null;
|
|
377
|
+
now?: Date;
|
|
378
|
+
};
|
|
379
|
+
/** Apply record overrides defensively: the record is data, never trusted markup or routing. */
|
|
380
|
+
declare function applyNavigationConfig(defaults: NavigationDestination[], config: NavigationConfig | null | undefined): NavigationDestination[];
|
|
381
|
+
/** The one place that decides what every reader-facing menu shows. */
|
|
382
|
+
declare function resolveNavigation(context: NavigationContext): ResolvedNavigation;
|
|
383
|
+
|
|
277
384
|
/** https URL with no userinfo. Anything else is dropped from public markup. */
|
|
278
385
|
declare function publicHttps(value?: string | null): string | undefined;
|
|
279
386
|
declare function articleUrl(canonical: string, slug: string): string;
|
|
@@ -514,4 +621,4 @@ interface SponsorshipReport {
|
|
|
514
621
|
/** Shared by the newsroom print/export view and the repeatable PDF renderer. */
|
|
515
622
|
declare function campaignReportHtml(report: SponsorshipReport): string;
|
|
516
623
|
|
|
517
|
-
export { ANALYTICS_COOKIE, ANALYTICS_VERSION, ArticleProvenance, type Attribution, BROADSHEET_THEME, type BriefingCandidate, type BriefingPreferences, type BriefingReason, type BriefingSection, type BriefingSource, type BriefingVerification, COOKIE_CATEGORIES, CURATED_COMMUNITY_CARDS, CouponOffer, EDITORIAL_SECTION_ORDER, EVENTS, EditorialSection, type EffectiveTheme, FeatureName, HomepageSelection, LAYOUT_COOKIE, LOCAL_BRIEFING_POLICY_VERSION, type ListeningArticle, type LocalBriefing, type LocalBriefingItem, type LocalBriefingOptions, Measurement, NEWS_CACHE, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, SESSION_IDLE_MS, type SponsorshipReport, THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, type ThemeMode, UTM_FIELDS, UUID, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, campaignReportHtml, clearTheme, composeEdition, composeLocalBriefing, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getStoredTheme, getSystemTheme, inventoryCacheOptions, llmsText, matchesStoryTag, measurementConsent, nearbyUpdates, newsArticleJsonLd, normalizedText, orderedSections, publicHttps, publicUrl, publicationJsonLd, publicationListeningCandidates, publicationListeningTrack, publicationMeta, readerSources, recordConsentToCompliance, referralCode, resolveCoupon, resolveEffectiveTheme, resolveLayout, resolveSection, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
|
|
624
|
+
export { ANALYTICS_COOKIE, ANALYTICS_VERSION, ArticleProvenance, type Attribution, BROADSHEET_THEME, type BriefingCandidate, type BriefingPreferences, type BriefingReason, type BriefingSection, type BriefingSource, type BriefingVerification, COOKIE_CATEGORIES, CURATED_COMMUNITY_CARDS, CouponOffer, EDITORIAL_SECTION_ORDER, EVENTS, EditorialSection, type EffectiveTheme, FeatureName, type FeaturedDestination, HomepageSelection, LAYOUT_COOKIE, LOCAL_BRIEFING_POLICY_VERSION, type ListeningArticle, type LocalBriefing, type LocalBriefingItem, type LocalBriefingOptions, Measurement, NAVIGATION_GROUPS, NETWORK_NAVIGATION, NEWS_CACHE, type NavigationConfig, type NavigationContext, type NavigationDestination, type NavigationGroup, type NavigationGroupLinks, type NavigationKind, type NavigationLink, type NavigationOverride, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, type ResolvedNavigation, SESSION_IDLE_MS, type SponsorshipReport, THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, type ThemeMode, UTM_FIELDS, UUID, activeNearby, applyNavigationConfig, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, campaignReportHtml, clearTheme, composeEdition, composeLocalBriefing, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getStoredTheme, getSystemTheme, inventoryCacheOptions, llmsText, matchesStoryTag, measurementConsent, nearbyUpdates, newsArticleJsonLd, normalizedText, orderedSections, publicHttps, publicUrl, publicationJsonLd, publicationListeningCandidates, publicationListeningTrack, publicationMeta, readerSources, recordConsentToCompliance, referralCode, resolveCoupon, resolveEffectiveTheme, resolveLayout, resolveNavigation, resolveSection, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
|
package/dist/index.d.ts
CHANGED
|
@@ -274,6 +274,113 @@ declare function nearbyUpdates(items: NewsItem[], selection?: HomepageSelection,
|
|
|
274
274
|
declare function shortDeck(value: string | null | undefined, limit?: number): string;
|
|
275
275
|
declare function storyDate(value: string): string | null;
|
|
276
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Shared network navigation.
|
|
279
|
+
*
|
|
280
|
+
* One configuration drives the primary row, the More menu and the hamburger
|
|
281
|
+
* directory on every Bay Area News Network paper. The defaults below ship in
|
|
282
|
+
* this package; day-to-day changes (labels, order, enabling Autos, a featured
|
|
283
|
+
* seasonal destination) are `NavigationConfig` overrides stored in each
|
|
284
|
+
* paper's site record and picked up at request time — no release required.
|
|
285
|
+
*/
|
|
286
|
+
/** Editorial sections, community products and marketplaces stay distinct in config even where readers see one row. */
|
|
287
|
+
type NavigationKind = "editorial" | "product" | "marketplace" | "publication";
|
|
288
|
+
type NavigationGroup = "news-knowledge" | "local-life" | "marketplace" | "publication";
|
|
289
|
+
declare const NAVIGATION_GROUPS: {
|
|
290
|
+
id: NavigationGroup;
|
|
291
|
+
label: string;
|
|
292
|
+
}[];
|
|
293
|
+
type NavigationDestination = {
|
|
294
|
+
/** Stable identifier; overrides and additions merge by id. */
|
|
295
|
+
id: string;
|
|
296
|
+
label: string;
|
|
297
|
+
href: string;
|
|
298
|
+
kind: NavigationKind;
|
|
299
|
+
/** More-menu column; destinations without a group never render in More. */
|
|
300
|
+
group?: NavigationGroup;
|
|
301
|
+
/** Reading order inside the primary row and inside each More group. */
|
|
302
|
+
order: number;
|
|
303
|
+
/** Responsive weight: when the row runs out of width, lower priority collapses into More first. */
|
|
304
|
+
priority?: number;
|
|
305
|
+
/** Renders in the primary editorial row. */
|
|
306
|
+
primary?: boolean;
|
|
307
|
+
/** Renders in its group inside the More menu. */
|
|
308
|
+
more?: boolean;
|
|
309
|
+
/** Hidden everywhere until enabled (default true). */
|
|
310
|
+
enabled?: boolean;
|
|
311
|
+
/** Publication feature flag that must be on (e.g. "jobs", "chat", "smile", "classifieds"). */
|
|
312
|
+
feature?: string;
|
|
313
|
+
/** Canonical section name: shown only when the publication configures or publishes the section. */
|
|
314
|
+
section?: string;
|
|
315
|
+
/** Restricts the destination to the listed publication slugs. */
|
|
316
|
+
publications?: string[];
|
|
317
|
+
};
|
|
318
|
+
/** Record-stored delta applied over the shipped defaults. Unknown ids with a label and href are additions. */
|
|
319
|
+
type NavigationOverride = {
|
|
320
|
+
id: string;
|
|
321
|
+
} & Partial<Omit<NavigationDestination, "id">>;
|
|
322
|
+
type FeaturedDestination = {
|
|
323
|
+
label: string;
|
|
324
|
+
href: string;
|
|
325
|
+
/** ISO instants limiting when the promotion shows. */
|
|
326
|
+
from?: string;
|
|
327
|
+
until?: string;
|
|
328
|
+
};
|
|
329
|
+
/** Stored in the site record under `render.navigation`. Everything is optional: absent fields keep defaults. */
|
|
330
|
+
type NavigationConfig = {
|
|
331
|
+
version?: 1;
|
|
332
|
+
destinations?: NavigationOverride[];
|
|
333
|
+
/** Overrides the seasonal featured slot; `null` suppresses it entirely. */
|
|
334
|
+
featured?: FeaturedDestination | null;
|
|
335
|
+
};
|
|
336
|
+
/**
|
|
337
|
+
* The network-wide default navigation. Primary order and collapse priority
|
|
338
|
+
* follow the 2026 information architecture: editorial sections first, then
|
|
339
|
+
* the community products. "Front page" is the masthead's job, so it is not a
|
|
340
|
+
* destination. Autos ships disabled; enabling it is a record override.
|
|
341
|
+
*/
|
|
342
|
+
declare const NETWORK_NAVIGATION: NavigationDestination[];
|
|
343
|
+
type NavigationLink = {
|
|
344
|
+
id: string;
|
|
345
|
+
label: string;
|
|
346
|
+
href: string;
|
|
347
|
+
kind: NavigationKind;
|
|
348
|
+
priority: number;
|
|
349
|
+
};
|
|
350
|
+
type NavigationGroupLinks = {
|
|
351
|
+
id: NavigationGroup;
|
|
352
|
+
label: string;
|
|
353
|
+
links: NavigationLink[];
|
|
354
|
+
};
|
|
355
|
+
type ResolvedNavigation = {
|
|
356
|
+
/** Editorial row in reading order; collapse into More by ascending priority. */
|
|
357
|
+
primary: NavigationLink[];
|
|
358
|
+
/** More-menu columns in canonical group order; empty groups are dropped. */
|
|
359
|
+
groups: NavigationGroupLinks[];
|
|
360
|
+
/** Active featured promotion from config, if any. `null` means "configured off". */
|
|
361
|
+
featured?: {
|
|
362
|
+
label: string;
|
|
363
|
+
href: string;
|
|
364
|
+
} | null;
|
|
365
|
+
};
|
|
366
|
+
type NavigationContext = {
|
|
367
|
+
/** This paper's slug. */
|
|
368
|
+
publication: string;
|
|
369
|
+
/** Sections the publication configures (profile taxonomy). */
|
|
370
|
+
sections?: string[];
|
|
371
|
+
/** Sections observed in the live feed; they satisfy a section gate too. */
|
|
372
|
+
discovered?: string[];
|
|
373
|
+
/** Publication feature flags. */
|
|
374
|
+
featureEnabled?: (feature: string) => boolean;
|
|
375
|
+
/** Record-stored overrides. Invalid entries are ignored, never fatal. */
|
|
376
|
+
config?: NavigationConfig | null;
|
|
377
|
+
now?: Date;
|
|
378
|
+
};
|
|
379
|
+
/** Apply record overrides defensively: the record is data, never trusted markup or routing. */
|
|
380
|
+
declare function applyNavigationConfig(defaults: NavigationDestination[], config: NavigationConfig | null | undefined): NavigationDestination[];
|
|
381
|
+
/** The one place that decides what every reader-facing menu shows. */
|
|
382
|
+
declare function resolveNavigation(context: NavigationContext): ResolvedNavigation;
|
|
383
|
+
|
|
277
384
|
/** https URL with no userinfo. Anything else is dropped from public markup. */
|
|
278
385
|
declare function publicHttps(value?: string | null): string | undefined;
|
|
279
386
|
declare function articleUrl(canonical: string, slug: string): string;
|
|
@@ -514,4 +621,4 @@ interface SponsorshipReport {
|
|
|
514
621
|
/** Shared by the newsroom print/export view and the repeatable PDF renderer. */
|
|
515
622
|
declare function campaignReportHtml(report: SponsorshipReport): string;
|
|
516
623
|
|
|
517
|
-
export { ANALYTICS_COOKIE, ANALYTICS_VERSION, ArticleProvenance, type Attribution, BROADSHEET_THEME, type BriefingCandidate, type BriefingPreferences, type BriefingReason, type BriefingSection, type BriefingSource, type BriefingVerification, COOKIE_CATEGORIES, CURATED_COMMUNITY_CARDS, CouponOffer, EDITORIAL_SECTION_ORDER, EVENTS, EditorialSection, type EffectiveTheme, FeatureName, HomepageSelection, LAYOUT_COOKIE, LOCAL_BRIEFING_POLICY_VERSION, type ListeningArticle, type LocalBriefing, type LocalBriefingItem, type LocalBriefingOptions, Measurement, NEWS_CACHE, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, SESSION_IDLE_MS, type SponsorshipReport, THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, type ThemeMode, UTM_FIELDS, UUID, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, campaignReportHtml, clearTheme, composeEdition, composeLocalBriefing, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getStoredTheme, getSystemTheme, inventoryCacheOptions, llmsText, matchesStoryTag, measurementConsent, nearbyUpdates, newsArticleJsonLd, normalizedText, orderedSections, publicHttps, publicUrl, publicationJsonLd, publicationListeningCandidates, publicationListeningTrack, publicationMeta, readerSources, recordConsentToCompliance, referralCode, resolveCoupon, resolveEffectiveTheme, resolveLayout, resolveSection, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
|
|
624
|
+
export { ANALYTICS_COOKIE, ANALYTICS_VERSION, ArticleProvenance, type Attribution, BROADSHEET_THEME, type BriefingCandidate, type BriefingPreferences, type BriefingReason, type BriefingSection, type BriefingSource, type BriefingVerification, COOKIE_CATEGORIES, CURATED_COMMUNITY_CARDS, CouponOffer, EDITORIAL_SECTION_ORDER, EVENTS, EditorialSection, type EffectiveTheme, FeatureName, type FeaturedDestination, HomepageSelection, LAYOUT_COOKIE, LOCAL_BRIEFING_POLICY_VERSION, type ListeningArticle, type LocalBriefing, type LocalBriefingItem, type LocalBriefingOptions, Measurement, NAVIGATION_GROUPS, NETWORK_NAVIGATION, NEWS_CACHE, type NavigationConfig, type NavigationContext, type NavigationDestination, type NavigationGroup, type NavigationGroupLinks, type NavigationKind, type NavigationLink, type NavigationOverride, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, type ResolvedNavigation, SESSION_IDLE_MS, type SponsorshipReport, THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, type ThemeMode, UTM_FIELDS, UUID, activeNearby, applyNavigationConfig, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, campaignReportHtml, clearTheme, composeEdition, composeLocalBriefing, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getStoredTheme, getSystemTheme, inventoryCacheOptions, llmsText, matchesStoryTag, measurementConsent, nearbyUpdates, newsArticleJsonLd, normalizedText, orderedSections, publicHttps, publicUrl, publicationJsonLd, publicationListeningCandidates, publicationListeningTrack, publicationMeta, readerSources, recordConsentToCompliance, referralCode, resolveCoupon, resolveEffectiveTheme, resolveLayout, resolveNavigation, resolveSection, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
|
package/dist/index.js
CHANGED
|
@@ -576,6 +576,156 @@ function defaultRevalidateTargets() {
|
|
|
576
576
|
};
|
|
577
577
|
}
|
|
578
578
|
|
|
579
|
+
// src/navigation.ts
|
|
580
|
+
var NAVIGATION_GROUPS = [
|
|
581
|
+
{ id: "news-knowledge", label: "News & knowledge" },
|
|
582
|
+
{ id: "local-life", label: "Local life" },
|
|
583
|
+
{ id: "marketplace", label: "Marketplace" },
|
|
584
|
+
{ id: "publication", label: "Publication" }
|
|
585
|
+
];
|
|
586
|
+
function editorial(id, section, overrides = {}) {
|
|
587
|
+
return {
|
|
588
|
+
id,
|
|
589
|
+
section,
|
|
590
|
+
kind: "editorial",
|
|
591
|
+
label: sectionDefinition(section).label,
|
|
592
|
+
href: sectionPath(section),
|
|
593
|
+
order: 0,
|
|
594
|
+
priority: 0,
|
|
595
|
+
...overrides
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
var NETWORK_NAVIGATION = [
|
|
599
|
+
// Primary editorial row. "News" is the Civic life desk; the section slug and story classifications are unchanged.
|
|
600
|
+
editorial("news", "Civic life", { label: "News", order: 10, priority: 100, primary: true }),
|
|
601
|
+
editorial("community", "Community", { order: 20, priority: 90, primary: true }),
|
|
602
|
+
editorial("food-drink", "Food & places", { order: 30, priority: 85, primary: true }),
|
|
603
|
+
editorial("things-to-do", "Events", { order: 40, priority: 80, primary: true }),
|
|
604
|
+
editorial("health", "Health", { order: 50, priority: 75, primary: true }),
|
|
605
|
+
editorial("real-estate", "Real Estate", { order: 60, priority: 70, primary: true }),
|
|
606
|
+
{ id: "autos", label: "Autos", href: "/autos", kind: "marketplace", group: "marketplace", order: 65, priority: 65, primary: true, more: true, enabled: false },
|
|
607
|
+
editorial("business", "Local businesses", { order: 70, priority: 60, primary: true }),
|
|
608
|
+
{ id: "meet", label: "Meet", href: "/meet", kind: "product", order: 80, priority: 50, primary: true, feature: "chat" },
|
|
609
|
+
{ id: "jokes", label: "Jokes", href: "/smile", kind: "product", order: 90, priority: 40, primary: true, feature: "smile" },
|
|
610
|
+
// More — News & knowledge.
|
|
611
|
+
editorial("ai-technology", "AI & Technology", { label: "AI & technology", group: "news-knowledge", order: 10, more: true }),
|
|
612
|
+
editorial("schools", "Schools", { label: "Schools & education", group: "news-knowledge", order: 20, more: true }),
|
|
613
|
+
editorial("civic-life", "Civic life", { group: "news-knowledge", order: 30, more: true }),
|
|
614
|
+
editorial("environment", "Environment", { group: "news-knowledge", order: 40, more: true }),
|
|
615
|
+
editorial("transportation", "Transportation", { group: "news-knowledge", order: 50, more: true }),
|
|
616
|
+
// More — Local life.
|
|
617
|
+
{ id: "pets", label: "Pets", href: "/pets", kind: "product", group: "local-life", order: 10, more: true },
|
|
618
|
+
{ id: "celebrations", label: "Celebrations", href: "/sections/celebrations", kind: "product", group: "local-life", order: 20, more: true, enabled: false },
|
|
619
|
+
{ id: "community-maps", label: "Community maps", href: "/community-maps", kind: "product", group: "local-life", order: 30, more: true },
|
|
620
|
+
{ id: "town-chat", label: "Town chat", href: "/chat", kind: "product", group: "local-life", order: 40, more: true, feature: "chat" },
|
|
621
|
+
{ id: "neighborhood-guides", label: "Neighborhood guides", href: "/guides", kind: "product", group: "local-life", order: 50, more: true, enabled: false },
|
|
622
|
+
{ id: "poll", label: "Poll", href: "/polls", kind: "product", group: "local-life", order: 60, more: true, publications: ["walnutcreektimes"] },
|
|
623
|
+
// More — Marketplace.
|
|
624
|
+
{ id: "jobs", label: "Jobs", href: "/jobs", kind: "marketplace", group: "marketplace", order: 10, more: true, feature: "jobs" },
|
|
625
|
+
{ id: "classifieds", label: "Classifieds", href: "/classifieds", kind: "marketplace", group: "marketplace", order: 20, more: true, feature: "classifieds" },
|
|
626
|
+
{ id: "local-services", label: "Local services", href: "/services", kind: "marketplace", group: "marketplace", order: 40, more: true, enabled: false },
|
|
627
|
+
// More — Publication.
|
|
628
|
+
{ id: "latest", label: "Latest", href: "/news", kind: "publication", group: "publication", order: 10, more: true },
|
|
629
|
+
{ id: "this-week", label: "This week", href: "/this-week", kind: "publication", group: "publication", order: 20, more: true },
|
|
630
|
+
{ id: "newsletter", label: "Newsletter", href: "/newsletter", kind: "publication", group: "publication", order: 30, more: true },
|
|
631
|
+
{ id: "about", label: "About", href: "/about", kind: "publication", group: "publication", order: 40, more: true },
|
|
632
|
+
{ id: "advertise", label: "Advertise", href: "/advertise", kind: "publication", group: "publication", order: 50, more: true },
|
|
633
|
+
{ id: "contact", label: "Contact", href: "/contact", kind: "publication", group: "publication", order: 60, more: true }
|
|
634
|
+
];
|
|
635
|
+
var MAX_LABEL = 60;
|
|
636
|
+
function safeHref(href) {
|
|
637
|
+
if (typeof href !== "string") return null;
|
|
638
|
+
const value = href.trim();
|
|
639
|
+
if (!value.startsWith("/") || value.startsWith("//") || /[\s\\]/.test(value)) return null;
|
|
640
|
+
return value;
|
|
641
|
+
}
|
|
642
|
+
function safeLabel(label) {
|
|
643
|
+
if (typeof label !== "string") return null;
|
|
644
|
+
const value = label.trim();
|
|
645
|
+
return value && value.length <= MAX_LABEL ? value : null;
|
|
646
|
+
}
|
|
647
|
+
var GROUP_IDS = new Set(NAVIGATION_GROUPS.map((group) => group.id));
|
|
648
|
+
var KINDS = /* @__PURE__ */ new Set(["editorial", "product", "marketplace", "publication"]);
|
|
649
|
+
function applyNavigationConfig(defaults, config) {
|
|
650
|
+
const merged = new Map(defaults.map((destination) => [destination.id, { ...destination }]));
|
|
651
|
+
for (const override of config?.destinations ?? []) {
|
|
652
|
+
if (!override || typeof override.id !== "string" || !override.id) continue;
|
|
653
|
+
const current = merged.get(override.id);
|
|
654
|
+
const label = safeLabel(override.label);
|
|
655
|
+
const href = safeHref(override.href);
|
|
656
|
+
if (!current && !(label && href)) continue;
|
|
657
|
+
const next = current ? { ...current } : {
|
|
658
|
+
id: override.id,
|
|
659
|
+
label,
|
|
660
|
+
href,
|
|
661
|
+
kind: "product",
|
|
662
|
+
order: 1e3,
|
|
663
|
+
priority: 0
|
|
664
|
+
};
|
|
665
|
+
if (label) next.label = label;
|
|
666
|
+
if (href) next.href = href;
|
|
667
|
+
if (typeof override.order === "number" && Number.isFinite(override.order)) next.order = override.order;
|
|
668
|
+
if (typeof override.priority === "number" && Number.isFinite(override.priority)) next.priority = override.priority;
|
|
669
|
+
if (typeof override.enabled === "boolean") next.enabled = override.enabled;
|
|
670
|
+
if (typeof override.primary === "boolean") next.primary = override.primary;
|
|
671
|
+
if (typeof override.more === "boolean") next.more = override.more;
|
|
672
|
+
if (override.group && GROUP_IDS.has(override.group)) next.group = override.group;
|
|
673
|
+
if (override.kind && KINDS.has(override.kind)) next.kind = override.kind;
|
|
674
|
+
if (typeof override.section === "string") next.section = override.section;
|
|
675
|
+
if (typeof override.feature === "string") next.feature = override.feature;
|
|
676
|
+
if (Array.isArray(override.publications)) next.publications = override.publications.filter((slug) => typeof slug === "string");
|
|
677
|
+
merged.set(next.id, next);
|
|
678
|
+
}
|
|
679
|
+
return Array.from(merged.values());
|
|
680
|
+
}
|
|
681
|
+
function activeFeatured(config, now) {
|
|
682
|
+
if (config?.featured === null) return null;
|
|
683
|
+
const featured = config?.featured;
|
|
684
|
+
if (!featured) return void 0;
|
|
685
|
+
const label = safeLabel(featured.label);
|
|
686
|
+
const href = safeHref(featured.href);
|
|
687
|
+
if (!label || !href) return void 0;
|
|
688
|
+
const time = now.getTime();
|
|
689
|
+
if (featured.from && time < Date.parse(featured.from)) return void 0;
|
|
690
|
+
if (featured.until && time >= Date.parse(featured.until)) return void 0;
|
|
691
|
+
return { label, href };
|
|
692
|
+
}
|
|
693
|
+
function resolveNavigation(context) {
|
|
694
|
+
const now = context.now ?? /* @__PURE__ */ new Date();
|
|
695
|
+
const sections2 = /* @__PURE__ */ new Set([...context.sections ?? [], ...context.discovered ?? []]);
|
|
696
|
+
const featureEnabled2 = context.featureEnabled ?? (() => false);
|
|
697
|
+
const destinations = applyNavigationConfig(NETWORK_NAVIGATION, context.config);
|
|
698
|
+
const visible = destinations.filter((destination) => {
|
|
699
|
+
if (destination.enabled === false) return false;
|
|
700
|
+
if (destination.feature && !featureEnabled2(destination.feature)) return false;
|
|
701
|
+
if (destination.section && !sections2.has(destination.section)) return false;
|
|
702
|
+
if (destination.publications && !destination.publications.includes(context.publication)) return false;
|
|
703
|
+
return true;
|
|
704
|
+
});
|
|
705
|
+
const link = (destination) => ({
|
|
706
|
+
id: destination.id,
|
|
707
|
+
label: destination.label,
|
|
708
|
+
href: destination.href,
|
|
709
|
+
kind: destination.kind,
|
|
710
|
+
priority: destination.priority ?? 0
|
|
711
|
+
});
|
|
712
|
+
const byOrder = (left, right) => left.order - right.order || left.label.localeCompare(right.label);
|
|
713
|
+
const primary = visible.filter((destination) => destination.primary).sort(byOrder).map(link);
|
|
714
|
+
const covered = new Set(destinations.map((destination) => destination.section).filter(Boolean));
|
|
715
|
+
const stray = [...new Set(context.discovered ?? [])].filter((name) => name !== "Local news" && !covered.has(name)).sort((left, right) => left.localeCompare(right)).map((name) => {
|
|
716
|
+
const definition = sectionDefinition(name);
|
|
717
|
+
return { id: definition.id, label: definition.label, href: sectionPath(name), kind: "editorial", priority: 0 };
|
|
718
|
+
});
|
|
719
|
+
const groups = NAVIGATION_GROUPS.map((group) => ({
|
|
720
|
+
...group,
|
|
721
|
+
links: [
|
|
722
|
+
...visible.filter((destination) => destination.more && destination.group === group.id).sort(byOrder).map(link),
|
|
723
|
+
...group.id === "news-knowledge" ? stray : []
|
|
724
|
+
]
|
|
725
|
+
})).filter((group) => group.links.length > 0);
|
|
726
|
+
return { primary, groups, featured: activeFeatured(context.config, now) };
|
|
727
|
+
}
|
|
728
|
+
|
|
579
729
|
// src/analytics.ts
|
|
580
730
|
var ANALYTICS_VERSION = 1;
|
|
581
731
|
var ANALYTICS_COOKIE = "napsite_analytics_v1";
|
|
@@ -837,4 +987,4 @@ function campaignReportHtml(report) {
|
|
|
837
987
|
<footer><p>${escape(report.reporting_note)}</p><p>${escape(report.editorial_independence)}</p><p>Campaign reference: ${escape(report.campaign_id)}</p></footer></body></html>`;
|
|
838
988
|
}
|
|
839
989
|
|
|
840
|
-
export { ANALYTICS_COOKIE, ANALYTICS_VERSION, ArticleProvenance, EDITORIAL_SECTION_ORDER, EVENTS, LOCAL_BRIEFING_POLICY_VERSION, NEWS_CACHE, NewsError, SESSION_IDLE_MS, UTM_FIELDS, UUID, activeNearby, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, campaignReportHtml, composeEdition, composeLocalBriefing, defaultRevalidateTargets, displayHeadline, edgeCacheControl, editorialSections, feedCacheKey, feedCacheOptions, filterNews, inventoryCacheOptions, llmsText, matchesStoryTag, measurementConsent, nearbyUpdates, newsArticleJsonLd, normalizedText, orderedSections, publicHttps, publicUrl, publicationJsonLd, publicationListeningCandidates, publicationListeningTrack, publicationMeta, readerSources, referralCode, resolveCoupon, resolveSection, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
|
|
990
|
+
export { ANALYTICS_COOKIE, ANALYTICS_VERSION, ArticleProvenance, EDITORIAL_SECTION_ORDER, EVENTS, LOCAL_BRIEFING_POLICY_VERSION, NAVIGATION_GROUPS, NETWORK_NAVIGATION, NEWS_CACHE, NewsError, SESSION_IDLE_MS, UTM_FIELDS, UUID, activeNearby, applyNavigationConfig, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, campaignReportHtml, composeEdition, composeLocalBriefing, defaultRevalidateTargets, displayHeadline, edgeCacheControl, editorialSections, feedCacheKey, feedCacheOptions, filterNews, inventoryCacheOptions, llmsText, matchesStoryTag, measurementConsent, nearbyUpdates, newsArticleJsonLd, normalizedText, orderedSections, publicHttps, publicUrl, publicationJsonLd, publicationListeningCandidates, publicationListeningTrack, publicationMeta, readerSources, referralCode, resolveCoupon, resolveNavigation, resolveSection, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
|
package/dist/pets-forms.js
CHANGED
package/dist/pets-plugin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LostPetNotice, PetCelebrationCard, AdoptionListingCard, PetEventCard, PetProfileCard, PawMark } from './chunk-
|
|
1
|
+
import { LostPetNotice, PetCelebrationCard, AdoptionListingCard, PetEventCard, PetProfileCard, PawMark } from './chunk-7HXSQY4T.js';
|
|
2
2
|
import './chunk-4JRWZXAP.js';
|
|
3
3
|
import './chunk-66GI2IX6.js';
|
|
4
4
|
import { petRequest } from './chunk-ABPJMDUG.js';
|
|
@@ -479,9 +479,28 @@ function SponsorAcknowledgment2({
|
|
|
479
479
|
/* @__PURE__ */ jsxRuntime.jsx("small", { children: "Paid community sponsorship. Sponsorship does not influence editorial selections and is not an endorsement." })
|
|
480
480
|
] });
|
|
481
481
|
}
|
|
482
|
+
var DEFAULT_COVER = {
|
|
483
|
+
url: "https://cdn-public.scalemule.com/01a1382e-6aff-7091-a757-12433a04eed1/anonymous/photos/01a13b52-a168-7131-90f6-a216dbe8dc3b/w800_h0_scaledown_q85_webp.webp",
|
|
484
|
+
alt: "A white Labrador retriever resting on a sunlit lawn",
|
|
485
|
+
credit: "Boyko Mihaylov"
|
|
486
|
+
};
|
|
487
|
+
function safeCoverUrl(url) {
|
|
488
|
+
if (!url || !/^https:\/\/[^\s"\\]+$/.test(url)) return null;
|
|
489
|
+
try {
|
|
490
|
+
return new URL(url).protocol === "https:" ? url : null;
|
|
491
|
+
} catch {
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function resolveCover(cover) {
|
|
496
|
+
if (!cover || !safeCoverUrl(cover.url) || !cover.alt || !cover.credit)
|
|
497
|
+
return DEFAULT_COVER;
|
|
498
|
+
return cover;
|
|
499
|
+
}
|
|
482
500
|
function PetsSection({
|
|
483
501
|
name = "Bay Area Paw",
|
|
484
502
|
tagline = "Happier pets. Closer communities.",
|
|
503
|
+
coverPhoto,
|
|
485
504
|
community,
|
|
486
505
|
communities,
|
|
487
506
|
resources,
|
|
@@ -500,6 +519,8 @@ function PetsSection({
|
|
|
500
519
|
eventsUrl,
|
|
501
520
|
accountUrl
|
|
502
521
|
}) {
|
|
522
|
+
const cover = resolveCover(coverPhoto);
|
|
523
|
+
const coverCreditUrl = safeCoverUrl(cover.credit_url);
|
|
503
524
|
const location = community?.name || regionLabel || "the Bay Area";
|
|
504
525
|
const link = (id) => `${resourceBasePath || basePath}/${encodeURIComponent(id)}`;
|
|
505
526
|
const local = (id) => communities.find((c) => c.id === id)?.name || "Our community";
|
|
@@ -603,10 +624,15 @@ function PetsSection({
|
|
|
603
624
|
"div",
|
|
604
625
|
{
|
|
605
626
|
role: "img",
|
|
606
|
-
"aria-label":
|
|
627
|
+
"aria-label": cover.alt,
|
|
628
|
+
style: { backgroundImage: `url("${cover.url}")` }
|
|
607
629
|
}
|
|
608
630
|
),
|
|
609
|
-
/* @__PURE__ */ jsxRuntime.
|
|
631
|
+
/* @__PURE__ */ jsxRuntime.jsxs("figcaption", { children: [
|
|
632
|
+
"Cover photograph by",
|
|
633
|
+
" ",
|
|
634
|
+
coverCreditUrl ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: coverCreditUrl, rel: "noopener noreferrer", children: cover.credit }) : cover.credit
|
|
635
|
+
] })
|
|
610
636
|
] })
|
|
611
637
|
] }),
|
|
612
638
|
!unavailable && /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "paw-shortcuts", "aria-label": "Useful pet resources", children: [
|
|
@@ -900,6 +926,7 @@ async function PublicationPetsSection({
|
|
|
900
926
|
PetsSection,
|
|
901
927
|
{
|
|
902
928
|
basePath: "/pets",
|
|
929
|
+
coverPhoto: collection.config?.presentation?.cover_photo,
|
|
903
930
|
regionLabel: region,
|
|
904
931
|
resourceBasePath: native ? "/pets" : canonicalOrigin + "/pets",
|
|
905
932
|
accountUrl: native ? "/pets/account" : void 0,
|
package/dist/pets-publication.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PetsSection, PawOfTheWeekCard, AdoptionListingCard, PetEventCard, PetCelebrationCard, LostPetNotice, PetProfileCard } from './chunk-
|
|
1
|
+
import { PetsSection, PawOfTheWeekCard, AdoptionListingCard, PetEventCard, PetCelebrationCard, LostPetNotice, PetProfileCard } from './chunk-7HXSQY4T.js';
|
|
2
2
|
import './chunk-4JRWZXAP.js';
|
|
3
3
|
import './chunk-66GI2IX6.js';
|
|
4
4
|
import { petRequest, petAcknowledgments, resolvePetReferences } from './chunk-ABPJMDUG.js';
|
|
@@ -92,6 +92,7 @@ async function PublicationPetsSection({
|
|
|
92
92
|
PetsSection,
|
|
93
93
|
{
|
|
94
94
|
basePath: "/pets",
|
|
95
|
+
coverPhoto: collection.config?.presentation?.cover_photo,
|
|
95
96
|
regionLabel: region,
|
|
96
97
|
resourceBasePath: native ? "/pets" : canonicalOrigin + "/pets",
|
|
97
98
|
accountUrl: native ? "/pets/account" : void 0,
|
package/dist/pets-view.cjs
CHANGED
|
@@ -602,9 +602,28 @@ function SponsorAcknowledgment2({
|
|
|
602
602
|
/* @__PURE__ */ jsxRuntime.jsx("small", { children: "Paid community sponsorship. Sponsorship does not influence editorial selections and is not an endorsement." })
|
|
603
603
|
] });
|
|
604
604
|
}
|
|
605
|
+
var DEFAULT_COVER = {
|
|
606
|
+
url: "https://cdn-public.scalemule.com/01a1382e-6aff-7091-a757-12433a04eed1/anonymous/photos/01a13b52-a168-7131-90f6-a216dbe8dc3b/w800_h0_scaledown_q85_webp.webp",
|
|
607
|
+
alt: "A white Labrador retriever resting on a sunlit lawn",
|
|
608
|
+
credit: "Boyko Mihaylov"
|
|
609
|
+
};
|
|
610
|
+
function safeCoverUrl(url) {
|
|
611
|
+
if (!url || !/^https:\/\/[^\s"\\]+$/.test(url)) return null;
|
|
612
|
+
try {
|
|
613
|
+
return new URL(url).protocol === "https:" ? url : null;
|
|
614
|
+
} catch {
|
|
615
|
+
return null;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function resolveCover(cover) {
|
|
619
|
+
if (!cover || !safeCoverUrl(cover.url) || !cover.alt || !cover.credit)
|
|
620
|
+
return DEFAULT_COVER;
|
|
621
|
+
return cover;
|
|
622
|
+
}
|
|
605
623
|
function PetsSection({
|
|
606
624
|
name = "Bay Area Paw",
|
|
607
625
|
tagline = "Happier pets. Closer communities.",
|
|
626
|
+
coverPhoto,
|
|
608
627
|
community,
|
|
609
628
|
communities,
|
|
610
629
|
resources,
|
|
@@ -623,6 +642,8 @@ function PetsSection({
|
|
|
623
642
|
eventsUrl,
|
|
624
643
|
accountUrl
|
|
625
644
|
}) {
|
|
645
|
+
const cover = resolveCover(coverPhoto);
|
|
646
|
+
const coverCreditUrl = safeCoverUrl(cover.credit_url);
|
|
626
647
|
const location = community?.name || regionLabel || "the Bay Area";
|
|
627
648
|
const link = (id) => `${resourceBasePath || basePath}/${encodeURIComponent(id)}`;
|
|
628
649
|
const local = (id) => communities.find((c) => c.id === id)?.name || "Our community";
|
|
@@ -726,10 +747,15 @@ function PetsSection({
|
|
|
726
747
|
"div",
|
|
727
748
|
{
|
|
728
749
|
role: "img",
|
|
729
|
-
"aria-label":
|
|
750
|
+
"aria-label": cover.alt,
|
|
751
|
+
style: { backgroundImage: `url("${cover.url}")` }
|
|
730
752
|
}
|
|
731
753
|
),
|
|
732
|
-
/* @__PURE__ */ jsxRuntime.
|
|
754
|
+
/* @__PURE__ */ jsxRuntime.jsxs("figcaption", { children: [
|
|
755
|
+
"Cover photograph by",
|
|
756
|
+
" ",
|
|
757
|
+
coverCreditUrl ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: coverCreditUrl, rel: "noopener noreferrer", children: cover.credit }) : cover.credit
|
|
758
|
+
] })
|
|
733
759
|
] })
|
|
734
760
|
] }),
|
|
735
761
|
!unavailable && /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "paw-shortcuts", "aria-label": "Useful pet resources", children: [
|
package/dist/pets-view.d.cts
CHANGED
|
@@ -35,9 +35,17 @@ declare function SponsorAcknowledgment({ acknowledgment, now, }: {
|
|
|
35
35
|
acknowledgment: PublicAcknowledgment;
|
|
36
36
|
now?: number;
|
|
37
37
|
}): React__default.JSX.Element | null;
|
|
38
|
+
/** Collection-managed cover photograph (config.presentation.cover_photo). */
|
|
39
|
+
type PetCoverPhoto = {
|
|
40
|
+
url: string;
|
|
41
|
+
alt: string;
|
|
42
|
+
credit: string;
|
|
43
|
+
credit_url?: string | null;
|
|
44
|
+
};
|
|
38
45
|
type PetsSectionProps = {
|
|
39
46
|
name?: string;
|
|
40
47
|
tagline?: string;
|
|
48
|
+
coverPhoto?: PetCoverPhoto | null;
|
|
41
49
|
community?: PetCommunity;
|
|
42
50
|
communities: PetCommunity[];
|
|
43
51
|
resources: PetResource[];
|
|
@@ -56,6 +64,6 @@ type PetsSectionProps = {
|
|
|
56
64
|
eventsUrl?: string;
|
|
57
65
|
accountUrl?: string;
|
|
58
66
|
};
|
|
59
|
-
declare function PetsSection({ name, tagline, community, communities, resources, spotlight, acknowledgments, basePath, mediaUrl, contributeUrl, placesUrl, walksUrl, nextUrl, unavailable, regionLabel, resourceBasePath, browseBasePath, eventsUrl, accountUrl, }: PetsSectionProps): React__default.JSX.Element;
|
|
67
|
+
declare function PetsSection({ name, tagline, coverPhoto, community, communities, resources, spotlight, acknowledgments, basePath, mediaUrl, contributeUrl, placesUrl, walksUrl, nextUrl, unavailable, regionLabel, resourceBasePath, browseBasePath, eventsUrl, accountUrl, }: PetsSectionProps): React__default.JSX.Element;
|
|
60
68
|
|
|
61
|
-
export { AdoptionListingCard, LostPetNotice, PawMark, PawNeighborhoodIllustration, PawOfTheWeekCard, PetCelebrationCard, PetEventCard, PetFriendlyPlaceCard, PetProfileCard, PetsSection, type PetsSectionProps, SponsorAcknowledgment };
|
|
69
|
+
export { AdoptionListingCard, LostPetNotice, PawMark, PawNeighborhoodIllustration, PawOfTheWeekCard, PetCelebrationCard, type PetCoverPhoto, PetEventCard, PetFriendlyPlaceCard, PetProfileCard, PetsSection, type PetsSectionProps, SponsorAcknowledgment };
|
package/dist/pets-view.d.ts
CHANGED
|
@@ -35,9 +35,17 @@ declare function SponsorAcknowledgment({ acknowledgment, now, }: {
|
|
|
35
35
|
acknowledgment: PublicAcknowledgment;
|
|
36
36
|
now?: number;
|
|
37
37
|
}): React__default.JSX.Element | null;
|
|
38
|
+
/** Collection-managed cover photograph (config.presentation.cover_photo). */
|
|
39
|
+
type PetCoverPhoto = {
|
|
40
|
+
url: string;
|
|
41
|
+
alt: string;
|
|
42
|
+
credit: string;
|
|
43
|
+
credit_url?: string | null;
|
|
44
|
+
};
|
|
38
45
|
type PetsSectionProps = {
|
|
39
46
|
name?: string;
|
|
40
47
|
tagline?: string;
|
|
48
|
+
coverPhoto?: PetCoverPhoto | null;
|
|
41
49
|
community?: PetCommunity;
|
|
42
50
|
communities: PetCommunity[];
|
|
43
51
|
resources: PetResource[];
|
|
@@ -56,6 +64,6 @@ type PetsSectionProps = {
|
|
|
56
64
|
eventsUrl?: string;
|
|
57
65
|
accountUrl?: string;
|
|
58
66
|
};
|
|
59
|
-
declare function PetsSection({ name, tagline, community, communities, resources, spotlight, acknowledgments, basePath, mediaUrl, contributeUrl, placesUrl, walksUrl, nextUrl, unavailable, regionLabel, resourceBasePath, browseBasePath, eventsUrl, accountUrl, }: PetsSectionProps): React__default.JSX.Element;
|
|
67
|
+
declare function PetsSection({ name, tagline, coverPhoto, community, communities, resources, spotlight, acknowledgments, basePath, mediaUrl, contributeUrl, placesUrl, walksUrl, nextUrl, unavailable, regionLabel, resourceBasePath, browseBasePath, eventsUrl, accountUrl, }: PetsSectionProps): React__default.JSX.Element;
|
|
60
68
|
|
|
61
|
-
export { AdoptionListingCard, LostPetNotice, PawMark, PawNeighborhoodIllustration, PawOfTheWeekCard, PetCelebrationCard, PetEventCard, PetFriendlyPlaceCard, PetProfileCard, PetsSection, type PetsSectionProps, SponsorAcknowledgment };
|
|
69
|
+
export { AdoptionListingCard, LostPetNotice, PawMark, PawNeighborhoodIllustration, PawOfTheWeekCard, PetCelebrationCard, type PetCoverPhoto, PetEventCard, PetFriendlyPlaceCard, PetProfileCard, PetsSection, type PetsSectionProps, SponsorAcknowledgment };
|
package/dist/pets-view.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AdoptionListingCard, LostPetNotice, PawMark, PawNeighborhoodIllustration, PawOfTheWeekCard, PetCelebrationCard, PetEventCard, PetFriendlyPlaceCard, PetProfileCard, PetsSection, SponsorAcknowledgment } from './chunk-
|
|
1
|
+
export { AdoptionListingCard, LostPetNotice, PawMark, PawNeighborhoodIllustration, PawOfTheWeekCard, PetCelebrationCard, PetEventCard, PetFriendlyPlaceCard, PetProfileCard, PetsSection, SponsorAcknowledgment } from './chunk-7HXSQY4T.js';
|
|
2
2
|
import './chunk-4JRWZXAP.js';
|
|
3
3
|
import './chunk-66GI2IX6.js';
|
|
4
4
|
import './chunk-AFJ6S5WJ.js';
|
package/images/README.md
CHANGED
|
@@ -4,8 +4,20 @@ Photograph: Boyko Mihaylov, “Jasper,” a white Labrador retriever resting on
|
|
|
4
4
|
sunlit lawn. Supplied directly by the publisher on October 14, 2026 as
|
|
5
5
|
`jasper.png`; used with the photographer's permission via the publisher.
|
|
6
6
|
|
|
7
|
+
The photograph is not stored in this repository or npm package. It is uploaded
|
|
8
|
+
to ScaleMule storage under the Bay Area Paw application as an
|
|
9
|
+
anonymous-visible, scan-gated object and delivered from the anonymous CDN with
|
|
10
|
+
photo-service `hero` renditions:
|
|
11
|
+
|
|
12
|
+
- storage file: `01a13b52-99ca-7c41-bd8b-61868d700678`
|
|
13
|
+
- photo manifest: `01a13b52-a168-7131-90f6-a216dbe8dc3b` (preset `hero`,
|
|
14
|
+
WebP 400w/800w)
|
|
15
|
+
- referenced from `pets.css` (`.paw-cover-photo`), 800w WebP rendition
|
|
16
|
+
- upload record: Bay Area Paw workspace `previews/editorial-cover-cdn.json`
|
|
17
|
+
|
|
7
18
|
The photograph illustrates life with pets. It is not a Bay Area resident
|
|
8
19
|
submission, an adoption listing, a featured winner or an endorsement. Keep the
|
|
9
|
-
visible photographer credit. The
|
|
10
|
-
supplied image
|
|
11
|
-
editing was applied by
|
|
20
|
+
visible photographer credit in `src/pets-view.tsx`. The uploaded source is a
|
|
21
|
+
portrait crop of the supplied image; the platform pipeline strips metadata and
|
|
22
|
+
generates the optimized renditions. No generative image editing was applied by
|
|
23
|
+
this repository.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scalemule/news-publication",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.60",
|
|
4
4
|
"description": "Shared server framework for ScaleMule local news publications. Walnut Creek Times is the broadsheet default; each paper supplies its own identity and feature flags.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
package/pets.css
CHANGED
|
@@ -1147,8 +1147,11 @@
|
|
|
1147
1147
|
}
|
|
1148
1148
|
.paw-cover-photo > div {
|
|
1149
1149
|
height: 100%;
|
|
1150
|
-
|
|
1151
|
-
|
|
1150
|
+
/* The photograph is collection-managed content: the component sets
|
|
1151
|
+
background-image inline from config.presentation.cover_photo. */
|
|
1152
|
+
background-position: center 57%;
|
|
1153
|
+
background-size: cover;
|
|
1154
|
+
background-repeat: no-repeat;
|
|
1152
1155
|
}
|
|
1153
1156
|
.paw-cover-photo::after {
|
|
1154
1157
|
content: "";
|
|
Binary file
|