@aglyn/plugins-mui 1.0.0-beta.150 → 1.0.0-beta.152

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/plugins-mui",
3
- "version": "1.0.0-beta.150",
3
+ "version": "1.0.0-beta.152",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -25,12 +25,12 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@aglyn/aglyn": "1.0.0-beta.150",
29
- "@aglyn/shared-data-enums": "1.0.0-beta.150",
30
- "@aglyn/shared-data-mdi": "1.0.0-beta.150",
31
- "@aglyn/shared-ui-jsx": "1.0.0-beta.150",
32
- "@aglyn/shared-ui-theme": "1.0.0-beta.150",
33
- "@aglyn/shared-util-vendor": "1.0.0-beta.150",
28
+ "@aglyn/aglyn": "1.0.0-beta.152",
29
+ "@aglyn/shared-data-enums": "1.0.0-beta.152",
30
+ "@aglyn/shared-data-mdi": "1.0.0-beta.152",
31
+ "@aglyn/shared-ui-jsx": "1.0.0-beta.152",
32
+ "@aglyn/shared-ui-theme": "1.0.0-beta.152",
33
+ "@aglyn/shared-util-vendor": "1.0.0-beta.152",
34
34
  "@swc/helpers": "0.5.23",
35
35
  "mobx": "^6.16.1",
36
36
  "mobx-react-lite": "^4.1.1"
@@ -1374,7 +1374,7 @@ export const collectionRelatedSchema = {
1374
1374
  {
1375
1375
  name: 'titleVariant',
1376
1376
  label: 'Title style',
1377
- description: 'Which type step from the site theme each post’s title reads at. ' + 'Default Subtitle 1; the title takes the surrounding text colour ' + 'rather than the link colour.',
1377
+ description: 'Which type step from the site theme each post’s title reads at. ' + 'Default Subtitle 1; the title takes the surrounding text color ' + 'rather than the link color.',
1378
1378
  component: Aglyn.FieldComponentType.SELECT,
1379
1379
  options: [
1380
1380
  ...typographyVariants
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/collection.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport {\n mdiAccountCircleOutline,\n mdiContentCopy,\n mdiEmail,\n mdiFacebook,\n mdiGithub,\n mdiInstagram,\n mdiLinkVariant,\n mdiLinkedin,\n mdiMagnify,\n mdiMastodon,\n mdiNewspaperVariantOutline,\n mdiPostOutline,\n mdiRss,\n mdiShareVariant,\n mdiTagMultipleOutline,\n mdiTagOutline,\n mdiTextLong,\n mdiTwitter,\n mdiWeb,\n mdiYoutube,\n} from '@aglyn/shared-data-mdi'\nimport { AppLink, MdiIcon } from '@aglyn/shared-ui-jsx'\n// The icon picker's fuzzy matcher (use-mdi-icons-fuzzy), not a re-implementation\n// (AGL-1516): search here has to feel like search does everywhere else in the\n// product, and two matchers is how they drift.\nimport { Fuse } from '@aglyn/shared-util-vendor/fuse'\nimport Box from '@mui/material/Box'\nimport Chip from '@mui/material/Chip'\nimport IconButton from '@mui/material/IconButton'\nimport InputBase from '@mui/material/InputBase'\nimport MuiLink from '@mui/material/Link'\nimport MuiStack, { type StackProps } from '@mui/material/Stack'\nimport Typography from '@mui/material/Typography'\nimport type { ReactNode } from 'react'\nimport { Children, forwardRef, useContext, useMemo, useState } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n// One coercion for every authored count in this bundle (AGL-1457) — number\n// fields round-trip as strings, and a second parser here would drift.\nimport { toCount } from './image-list'\n// The theme's own type steps (AGL-2486), shared with the Typography element\n// rather than a second list here — one rung list, one place to extend.\nimport { typographyVariants } from './typography'\n\n// Persisted component ids (AGL-551/582); the compose pipeline references\n// them through @aglyn/aglyn constants. Never rename.\nexport const ENTRIES_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_ENTRIES_COMPONENT_ID\nexport const ENTRY_BODY_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_ENTRY_BODY_COMPONENT_ID\nexport const RELATED_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_RELATED_COMPONENT_ID\nexport const SHARE_ID: Aglyn.ComponentId = Aglyn.COLLECTION_SHARE_COMPONENT_ID\nexport const ENTRY_META_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_ENTRY_META_COMPONENT_ID\nexport const ENTRY_AUTHOR_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_ENTRY_AUTHOR_COMPONENT_ID\nexport const CATEGORIES_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_CATEGORIES_COMPONENT_ID\nexport const SEARCH_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_SEARCH_COMPONENT_ID\nexport const AUTHOR_PROFILE_ID: Aglyn.ComponentId =\n Aglyn.CONTENT_AUTHOR_PROFILE_COMPONENT_ID\n\n/* ── Collection entries (repeater) ──────────────────────────────────────── */\n\nexport interface CollectionEntriesProps extends StackProps {\n /**\n * Collection to repeat over (compose-time, AGL-551). Blank = the\n * collection routed by the current URL on list-template screens.\n */\n collectionSlug?: string\n /** Maximum entries rendered (compose-time; blank = all, capped at 100). */\n entriesLimit?: number | string\n /**\n * Only entries in this category repeat (compose-time, AGL-582).\n * Matches the collection's category by stable id or display name.\n */\n filterCategory?: string\n /** Only entries carrying this tag repeat (compose-time, AGL-582). */\n filterTag?: string\n /**\n * Entries per page (compose-time, AGL-620). When set, the block renders one\n * page window instead of the top `entriesLimit`; the built-in collection\n * list uses this with the `page` from `/{collection}/page/{n}`.\n */\n perPage?: number | string\n /** 1-based page for `perPage` (compose-time, AGL-620). */\n page?: number | string\n /**\n * Render this block only on page 1 of the routed listing (compose-time,\n * AGL-1871). For a LEAD card — a block with no `perPage`, showing the top\n * of the set — every `/{collection}/page/{n}` past the first otherwise\n * repeats the identical entry above a page of different ones.\n *\n * Opt-in and default off: a block that legitimately belongs on every page\n * of a listing (a \"popular posts\" rail) has exactly the same shape.\n */\n firstPageOnly?: boolean\n /**\n * Show a search box that filters the RENDERED entries by title/excerpt as\n * the reader types (AGL-1516, Figma 494:1220). Opt-in and default off, so\n * every existing instance renders exactly as before.\n *\n * Client-evaluated over the entries this block already holds — the page is\n * ISR-cached, so a keystroke never costs a Firestore read. That set is\n * whatever the expansion left the block holding, which paging, an\n * `entriesLimit` or the 100-entry cap can each cut down. A truncated block\n * says so under its results either way — on a miss and on a hit\n * (AGL-1516, AGL-2569) — rather than pretending global search.\n */\n search?: boolean\n /**\n * What the search box DOES with a query (AGL-1525, Figma 496:1218).\n *\n * - `filter` (default, and the reading of an absent value) — hides the\n * non-matching cards in place. What AGL-1516 shipped.\n * - `suggest` — the card grid stays exactly as it was and a floating panel\n * of matching entries opens under the field, ending in a link to the\n * site-wide results page. The frame's toolbar behaviour: a reader\n * skimming the list is not made to lose it to a typo.\n */\n searchMode?: CollectionSearchMode\n /** Hint text inside the search box (blank = \"Search posts…\"). */\n searchPlaceholder?: string\n /**\n * Server-stamped matchable text per rendered entry\n * (`expandCollectionEntries`, AGL-1516); never set by hand.\n */\n searchIndex?: Aglyn.CollectionEntrySearchItem[]\n /**\n * How many entries `searchIndex` was drawn from — server-stamped by\n * `expandCollectionEntries` alongside it (AGL-1516), never set by hand.\n * `searchIndex.length < searchTotal` is the only honest test of whether\n * this block holds the whole set; `perPage` is not, because\n * `entriesLimit` and the 100-entry cap truncate too.\n */\n searchTotal?: number\n /**\n * Whether the read behind `searchTotal` reached its own bound — server-\n * stamped beside it (AGL-1516), never set by hand.\n *\n * `searchTotal` is a count of what the server SAW, and the collection read\n * is limited. Without this flag a block holding 100 of 400 posts satisfies\n * `searchIndex.length === searchTotal` and renders the one empty state that\n * claims to have looked everywhere.\n */\n searchCapped?: boolean\n}\n\n/** The two things the toolbar search box can do (AGL-1516/AGL-1525). */\nexport type CollectionSearchMode = 'filter' | 'suggest'\n\n/**\n * Author-facing choices for {@link CollectionEntriesProps.searchMode}.\n *\n * Both values are truthy on purpose (AGL-1453): `''` cannot survive a save,\n * so an author who tried the other mode would have no route back. \"Filter\"\n * carries the absent-value reading, which is what every block published\n * before AGL-1525 has.\n */\nexport const COLLECTION_SEARCH_MODE_OPTIONS: ReadonlyArray<{\n value: CollectionSearchMode\n label: string\n}> = [\n { value: 'filter', label: 'Filter the entries in place' },\n { value: 'suggest', label: 'Show a suggestions dropdown' },\n]\n\n/** The toolbar search field, from the frame (Figma 494:1220). */\nconst SEARCH_FIELD_WIDTH = 240\n\n/** Suggestion panel (Figma 496:1218). */\nconst SUGGESTION_PANEL_WIDTH = 420\n\n/**\n * Rows in the panel before it stops. The frame shows five; the sixth row is\n * always the \"View all results\" link, which is the honest overflow answer —\n * a scrolling suggestion panel is a worse results page.\n */\nconst SUGGESTION_LIMIT = 5\n\n/** Where \"View all results\" goes — the reserved site-search route (AGL-88). */\nconst searchResultsHref = (query: string) =>\n `/search?q=${encodeURIComponent(query)}`\n\n/**\n * The frame's compact filled field: magnify glyph + hint on the quiet\n * surface token, right-aligned so it sits where the toolbar row puts it.\n * Palette tokens only, so it follows the site theme in both modes.\n */\nconst searchFieldSx = {\n alignSelf: 'flex-end',\n display: 'flex',\n alignItems: 'center',\n gap: 1,\n px: 1.5,\n py: 0.75,\n borderRadius: 2,\n bgcolor: 'action.hover',\n color: 'text.secondary',\n width: SEARCH_FIELD_WIDTH,\n maxWidth: '100%',\n}\n\n/**\n * The positioned parent of the suggestion panel (AGL-1525). It takes over\n * `alignSelf` from the field so the toolbar row is unchanged — the panel\n * hangs off the field, it does not move it.\n */\nconst suggestionAnchorSx = {\n alignSelf: 'flex-end',\n position: 'relative',\n width: SEARCH_FIELD_WIDTH,\n maxWidth: '100%',\n}\n\n/**\n * The floating panel itself. Right-aligned to the field it drops from, and\n * wider than it, as the frame draws it — a suggestion row carries a title, a\n * chip, a date and a line of excerpt, none of which fit in 240px.\n *\n * Palette and shadow tokens only, so it follows the site theme in both\n * modes rather than pinning a light-mode card onto a dark page.\n */\nconst suggestionPanelSx = {\n position: 'absolute',\n top: 'calc(100% + 8px)',\n right: 0,\n zIndex: 10,\n width: SUGGESTION_PANEL_WIDTH,\n // Never wider than the viewport on a phone, where the anchor is narrow\n // and `right: 0` would otherwise hang the panel off the left edge.\n maxWidth: 'calc(100vw - 32px)',\n bgcolor: 'background.paper',\n color: 'text.primary',\n border: 1,\n borderColor: 'divider',\n borderRadius: 2,\n boxShadow: 6,\n overflow: 'hidden',\n textAlign: 'left',\n}\n\nconst suggestionRowLinkSx = {\n display: 'block',\n color: 'inherit',\n '&:hover': { bgcolor: 'action.hover' },\n}\n\nconst suggestionFooterSx = {\n display: 'block',\n px: 2,\n py: 1.25,\n borderTop: 1,\n borderColor: 'divider',\n bgcolor: 'action.hover',\n fontSize: 14,\n fontWeight: 500,\n}\n\n/** What a built index can be searched by — Fuse over the stamped rows. */\ntype EntryFuse = InstanceType<typeof Fuse<Aglyn.CollectionEntrySearchItem>>\n\n/**\n * The frame's search input (Figma 494:1220) — the magnify glyph and a bare\n * `InputBase` on the quiet surface token.\n *\n * Presentational and stateless, so the entries block's in-place filter and\n * the standalone toolbar box (AGL-1516) are the SAME field rather than two\n * that merely look alike. `inert` is the editing-surface rendering: read-only\n * and handler-free, so a click or stray keystroke in the besigner never edits\n * state the canvas cannot use — how Category Pills go inert there too.\n */\nconst SearchField = ({\n value,\n placeholder,\n inert,\n landmark,\n onQuery,\n onEscape,\n}: {\n value: string\n placeholder?: string\n inert?: boolean\n /**\n * Put `role=\"search\"` on the FIELD. False when a wrapping form carries it\n * instead — one search landmark per box, or a screen reader announces the\n * same field twice.\n */\n landmark?: boolean\n onQuery?: (next: string) => void\n onEscape?: () => void\n}) => (\n <Box {...(landmark ? { role: 'search' } : {})} sx={searchFieldSx}>\n <MdiIcon path={mdiMagnify.path} />\n <InputBase\n value={value}\n name=\"q\"\n placeholder={(placeholder ?? '').trim() || 'Search posts…'}\n inputProps={{ 'aria-label': 'Search entries' }}\n sx={{ flex: 1, fontSize: 14, color: 'text.primary' }}\n {...(inert\n ? { readOnly: true }\n : {\n onChange: (event) => onQuery?.(event.target.value),\n onKeyDown: (event) => {\n if (event.key === 'Escape') onEscape?.()\n },\n })}\n />\n </Box>\n)\n\n/**\n * The floating suggestion panel (AGL-1525, Figma 496:1218) — rows straight\n * off the server-stamped index, and a \"View all results\" link that is there\n * hits or no hits.\n *\n * `emptyText` is the caller's, not this component's, and that is the whole\n * point of it being a parameter: a page-windowed entries block and a\n * whole-collection toolbar box searched different sets, and a miss has to say\n * WHICH set came back empty. A shared \"No matches.\" would let one of them\n * claim a reach it never had.\n */\nconst SuggestionPanel = ({\n suggestions,\n trimmed,\n emptyText,\n}: {\n suggestions: readonly Aglyn.CollectionEntrySearchItem[]\n trimmed: string\n emptyText: string\n}) => (\n <Box sx={suggestionPanelSx}>\n {suggestions.length ? (\n suggestions.map((item, index) => {\n const row = (\n <MuiStack spacing={0.5} sx={{ px: 2, py: 1.25 }}>\n <MuiStack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"subtitle2\" sx={{ color: 'text.primary' }}>\n {item.title}\n </Typography>\n {item.category ? (\n <Chip label={item.category} size=\"small\" />\n ) : null}\n {item.date ? (\n <Typography variant=\"caption\" sx={{ color: 'text.secondary' }}>\n {item.date}\n </Typography>\n ) : null}\n </MuiStack>\n {item.excerpt ? (\n <Typography\n variant=\"body2\"\n sx={{\n color: 'text.secondary',\n // One line of excerpt, as the frame draws it. A row that\n // grows with the prose turns the panel into a page.\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {item.excerpt}\n </Typography>\n ) : null}\n </MuiStack>\n )\n // A row without a `url` is a page cached before AGL-1525 stamped one,\n // or a slugless entry. It renders as TEXT rather than as a link to\n // nowhere: a suggestion that navigates to the wrong page is worse\n // than one the reader has to read.\n return item.url ? (\n <AppLink\n key={index}\n href={item.url}\n sx={suggestionRowLinkSx}\n underline=\"none\"\n >\n {row}\n </AppLink>\n ) : (\n <Box key={index}>{row}</Box>\n )\n })\n ) : (\n <Typography\n variant=\"body2\"\n sx={{ color: 'text.secondary', px: 2, py: 1.25 }}\n >\n {emptyText}\n </Typography>\n )}\n {/*\n Always present, hits or no hits (Figma 496:1218). A box that searches\n one page, or one bounded read of a collection, cannot answer \"then\n where is it?\" on its own — the site-wide results page is the only\n honest answer, and it matters most in exactly the case where the panel\n came back empty.\n */}\n <AppLink\n href={searchResultsHref(trimmed)}\n sx={suggestionFooterSx}\n underline=\"none\"\n >\n {`View all results for “${trimmed}” →`}\n </AppLink>\n </Box>\n)\n\n/**\n * Field plus dropdown, in a REAL form (AGL-1525) — so Enter reaches the\n * results page and the box still works with the panel's JS doing nothing at\n * all. The panel is an enhancement over a working search box, not the search\n * box.\n *\n * Owns the query, because nothing outside it needs one: `suggest` never\n * touches the cards underneath. That is what lets the standalone toolbar\n * block exist at all (AGL-1516) — a box with no clones to hide is a complete\n * feature, not a crippled one.\n */\nconst SuggestSearchBox = ({\n fuzzy,\n items,\n placeholder,\n inert,\n emptyText,\n}: {\n fuzzy: EntryFuse | null\n items?: readonly Aglyn.CollectionEntrySearchItem[]\n placeholder?: string\n inert?: boolean\n /** The honest miss for the set THIS box searched. */\n emptyText: (query: string) => string\n}) => {\n const [query, setQuery] = useState('')\n /** Escape dismissed THIS answer (AGL-1525); typing brings it back. */\n const [closed, setClosed] = useState(false)\n const trimmed = query.trim()\n const suggestions =\n !inert && trimmed && fuzzy && items\n ? fuzzy\n .search(trimmed)\n .slice(0, SUGGESTION_LIMIT)\n .map((result) => items[result.refIndex])\n .filter(Boolean)\n : []\n const field = (\n <SearchField\n value={query}\n {...(placeholder === undefined ? {} : { placeholder })}\n // No form wraps the inert field, so the landmark belongs on it.\n {...(inert ? { inert: true, landmark: true } : {})}\n onQuery={(next) => {\n setQuery(next)\n // A new query reopens a panel the reader dismissed — Escape closes\n // THIS answer, it does not turn the feature off for the visit.\n setClosed(false)\n }}\n onEscape={() => setClosed(true)}\n />\n )\n if (inert) return field\n return (\n <Box\n component=\"form\"\n role=\"search\"\n action=\"/search\"\n method=\"get\"\n sx={suggestionAnchorSx}\n >\n {field}\n {trimmed && !closed ? (\n <SuggestionPanel\n suggestions={suggestions}\n trimmed={trimmed}\n emptyText={emptyText(trimmed)}\n />\n ) : null}\n </Box>\n )\n}\n\n/**\n * How much of the collection an entries block's search could actually see\n * (AGL-1516, AGL-2569) — `truncated` says the rendered window is smaller\n * than the set, `paginated` says the missing part lives on other pages of\n * this same list rather than merely outside the block.\n *\n * The test is whether the window is SMALLER THAN THE SET, which only the\n * server knows — `perPage` is the wrong proxy for it in both directions. A\n * block truncated by `entriesLimit` or by the 100-entry cap has no `perPage`\n * at all and would claim a global miss over 6 of 40 posts; a block whose\n * `perPage` exceeds its entry count is a single complete page and would\n * blame pages that do not exist. `searchTotal` is stamped with `searchIndex`,\n * so the two are always in step.\n *\n * Absent — a page cached before that stamp shipped — falls back to the old\n * `perPage` reading rather than guessing \"complete\": understating the scope\n * of a search is the safe direction to be wrong in.\n *\n * `searchCapped` is the second way a block can fail to hold the set, and the\n * invisible one: `searchTotal` counts the entries the server READ, and that\n * read is bounded. Past the bound `shown === searchTotal` is satisfied by a\n * block holding 100 of 400 posts, and the wording that claims to have looked\n * everywhere is exactly the branch it would take.\n *\n * Both the miss and the hit disclose off this one answer, so the two can\n * never disagree about the same block.\n */\nconst searchScope = ({\n perPage,\n shown,\n searchTotal,\n searchCapped,\n}: {\n perPage?: CollectionEntriesProps['perPage']\n /** Entries this block searched — the length of its stamped index. */\n shown: number\n searchTotal?: number\n searchCapped?: boolean\n}): { paginated: boolean; truncated: boolean } => {\n const paginated = (toCount(perPage, 0) ?? 0) > 0\n return {\n paginated,\n truncated:\n typeof searchTotal === 'number'\n ? shown < searchTotal || Boolean(searchCapped)\n : paginated || Boolean(searchCapped),\n }\n}\n\n/**\n * Repeats its children once per published entry of a content collection\n * (AGL-551) — the collections sibling of the dataset repeatable. The tenant\n * expands it at compose time with `{{entry.*}}` tokens; in the besigner the\n * template renders once with literal tokens, matching the repeatable UX.\n *\n * Search (AGL-1516): with `search` on, a toolbar search box filters the\n * rendered entry clones client-side against the server-stamped\n * `searchIndex` — fuzzy, via the same Fuse the icon picker uses. The clones\n * arrive as `children` in stamp order, one group of template roots per\n * entry, so group N of the children IS entry N of the index. Matches keep\n * the list's own order rather than Fuse's score order: a blog list is\n * chronological, and search narrows it, it does not reshuffle it. In the\n * besigner the field renders as an inert affordance (the template renders\n * once with literal tokens and there is no index to search), matching how\n * Category Pills go inert on editing surfaces.\n */\nconst CollectionEntries = forwardRef<HTMLDivElement, CollectionEntriesProps>(\n // collectionSlug/entriesLimit/filter*/search* are compose-time or\n // search-only attributes: strip so they never hit the DOM.\n (\n {\n collectionSlug,\n entriesLimit,\n filterCategory,\n filterTag,\n perPage,\n page,\n firstPageOnly,\n search,\n searchMode,\n searchPlaceholder,\n searchIndex,\n searchTotal,\n searchCapped,\n children,\n ...props\n },\n ref,\n ) => {\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n /** `filter` mode only — `suggest` owns its own query (AGL-1516). */\n const [query, setQuery] = useState('')\n const items = search ? searchIndex : undefined\n const fuzzy = useMemo(\n () =>\n items?.length\n ? // The ONE matcher config (AGL-1525), shared with the site-wide\n // results page this block's panel links to — a query the panel\n // forgave must not come back empty from \"View all results\".\n new Fuse(items, { ...Aglyn.COLLECTION_SEARCH_FUSE_OPTIONS })\n : null,\n [items],\n )\n if (!search) {\n return (\n <MuiStack ref={ref} spacing={4} {...props}>\n {children}\n </MuiStack>\n )\n }\n const childArray = Children.toArray(children)\n // Template roots per entry. Every entry clones the same template, so the\n // children divide evenly; anything else means the children are not the\n // stamped clones, and filtering blind would hide the wrong cards —\n // fail open and render everything.\n const groupSize =\n items?.length && childArray.length % items.length === 0\n ? childArray.length / items.length\n : 0\n // `suggest` draws its rows from the INDEX and never touches the clones,\n // so the clone-alignment precondition is a `filter` precondition only.\n // A block whose children do not divide evenly still suggests correctly;\n // requiring `groupSize` here would silently kill the panel on exactly\n // the templates that made the fail-open necessary.\n const mode: CollectionSearchMode =\n searchMode === 'suggest' ? 'suggest' : 'filter'\n const live =\n !suppressNavigation &&\n Boolean(fuzzy) &&\n (mode === 'suggest' || groupSize > 0)\n const trimmed = query.trim()\n let visible: ReactNode[] | ReactNode = children\n /**\n * The one line under the results that says what the search covered —\n * either a miss scoped to the set that came back empty, or a hit scoped\n * to the window it was drawn from. Never both.\n */\n let scopeNote: ReactNode = null\n if (mode === 'suggest') {\n // The grid is deliberately untouched (Figma 496:1218): the panel is an\n // overlay on a page the reader is still reading, not a filter.\n visible = children\n } else if (live && trimmed && fuzzy) {\n const matched = new Set(\n fuzzy.search(trimmed).map((result) => result.refIndex),\n )\n visible = childArray.filter((_, index) =>\n matched.has(Math.floor(index / groupSize)),\n )\n // HONEST scope (AGL-1516, AGL-2569): this block holds whatever window\n // the expansion gave it, and pretending the search was global would\n // turn every miss into a false \"this post does not exist\" — and every\n // hit into a result set that looks like the whole answer.\n const shown = items?.length ?? 0\n const { paginated, truncated } = searchScope({\n perPage,\n shown,\n searchTotal,\n searchCapped,\n })\n if (!(visible as ReactNode[]).length) {\n scopeNote = (\n <Typography variant=\"body2\" sx={{ color: 'text.secondary' }}>\n {!truncated\n ? `No matches for “${trimmed}”.`\n : paginated\n ? `No matches for “${trimmed}” on this page — other pages ` +\n 'are not searched.'\n : `No matches for “${trimmed}” in the ${shown} entries ` +\n 'shown here — the rest of the collection is not searched.'}\n </Typography>\n )\n } else if (truncated) {\n // The hit half of the same honesty (AGL-2569). Matches are drawn from\n // the same window the miss apologizes for, they carry no pager of\n // their own, and nothing else on screen says the pages behind this\n // one went unread — so a list that happens to be partial reads as\n // complete. A block that holds the whole collection says nothing\n // here: a disclaimer on a complete answer is noise, and every small\n // site would carry it.\n //\n // No route out to `/search?q=` (AGL-88): the suggestion panel links\n // there because it is the panel's only possible answer, but this\n // mode has a working list under the field and that page does not\n // exist yet. Trading a silent partial answer for a dead link is not\n // the trade.\n scopeNote = (\n <Typography variant=\"body2\" sx={{ color: 'text.secondary' }}>\n {paginated\n ? 'Showing matches on this page — other pages are not searched.'\n : `Showing matches in the ${shown} entries here — the rest of ` +\n 'the collection is not searched.'}\n </Typography>\n )\n }\n }\n // The field renders when there is something to search, and as an inert\n // affordance on editing surfaces so the author sees what they enabled.\n // A live surface with nothing stamped (unknown collection, zero entries)\n // renders no field at all — a search box over nothing is a lie.\n const showField = suppressNavigation || live\n const field = !showField ? null : mode === 'suggest' ? (\n // Rows in Fuse's SCORE order, the opposite of what `filter` does and\n // deliberately: filtering narrows a chronological feed, so reshuffling\n // it would be a second, unasked-for change to what the reader is\n // looking at. A suggestion list is not a feed — it is an answer to a\n // question, and the best answer belongs first.\n <SuggestSearchBox\n fuzzy={fuzzy}\n {...(items ? { items } : {})}\n {...(searchPlaceholder === undefined\n ? {}\n : { placeholder: searchPlaceholder })}\n {...(suppressNavigation ? { inert: true } : {})}\n // This block searched the entries IT rendered — a page window, an\n // `entriesLimit` slice, or a bounded read. Never \"this collection\".\n emptyText={(text) => `No matches for “${text}” on this page.`}\n />\n ) : (\n <SearchField\n value={query}\n landmark\n {...(searchPlaceholder === undefined ? {} : { placeholder: searchPlaceholder })}\n {...(suppressNavigation ? { inert: true } : {})}\n onQuery={setQuery}\n />\n )\n return (\n <MuiStack ref={ref} spacing={4} {...props}>\n {field}\n {visible}\n {scopeNote}\n </MuiStack>\n )\n },\n)\nCollectionEntries.displayName = 'AglynCollectionEntries'\n\nexport const collectionEntriesSchema: Aglyn.ComponentSchema<CollectionEntriesProps> =\n {\n $id: ENTRIES_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Collection Entries',\n description: 'Repeats its children once per entry in a content collection.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiPostOutline.path, sx: { color: 'secondary.main' } },\n attributes: [\n {\n name: 'collectionSlug',\n label: 'Collection slug',\n description:\n 'Content collection whose published entries the children repeat ' +\n 'over (e.g. \"blog\"). Leave blank on a list-template screen to use ' +\n 'the collection from the URL.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'entriesLimit',\n label: 'Entries limit',\n description: 'Maximum entries rendered (blank = all, capped at 100).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'filterCategory',\n label: 'Filter by category',\n description:\n 'Only entries in this category repeat — the category name or its ' +\n 'stable id both match (e.g. \"Guides\"). Blank = no category filter.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'filterTag',\n label: 'Filter by tag',\n description:\n 'Only entries carrying this tag repeat (e.g. \"nextjs\"). Blank = ' +\n 'no tag filter.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'perPage',\n label: 'Entries per page',\n description:\n 'Paginate the list: entries per page (blank = no pagination). ' +\n 'Pairs with the page from /{collection}/page/{n}.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'page',\n label: 'Page',\n description: '1-based page to render when Entries per page is set.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'firstPageOnly',\n label: 'Only on page 1',\n description:\n 'Show this block on the first page of the listing only. Use it ' +\n 'for a featured or lead card: without it the same entry repeats ' +\n 'at the top of every /page/{n}, above a page of different ones.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'search',\n label: 'Search',\n description:\n 'Show a search box that filters the rendered entries by title ' +\n 'and excerpt as the reader types. It searches the entries this ' +\n 'block rendered — whatever paging, an entry limit or the ' +\n '100-entry cap left it holding, and it says so on a miss.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'searchMode',\n label: 'When the reader types',\n description:\n 'Filter the entries in place, or leave the list alone and open a ' +\n 'dropdown of matching entries under the box — each one a link, ' +\n 'ending in \"View all results\" for a search across the whole site.',\n component: Aglyn.FieldComponentType.SELECT,\n options: COLLECTION_SEARCH_MODE_OPTIONS.map((option) => ({\n ...option,\n })),\n // Meaningless while there is no search box to type in.\n condition: { when: 'search', is: true },\n },\n {\n name: 'searchPlaceholder',\n label: 'Search placeholder',\n description:\n 'Hint text inside this block’s own search box — the one that ' +\n 'filters the entries below it as a reader types. Blank shows ' +\n '\"Search posts…\".',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n // Meaningless while there is no search box to hint in.\n condition: { when: 'search', is: true },\n },\n // `searchIndex` and `searchTotal` are deliberately NOT attributes: both\n // are server-stamped by expandCollectionEntries, like Category Pills'\n // `items` and Related Posts' `entries`.\n {\n name: 'spacing',\n label: 'Spacing',\n description: 'Defines the space/gap between entries.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n ],\n }\n\n/* ── Entry body (markdown) ──────────────────────────────────────────────── */\n\nexport interface CollectionEntryBodyProps {\n /**\n * Markdown-lite source. On entry-template screens `{{entry.body}}`\n * resolves to the rendered entry's body at compose time.\n */\n markdown?: string\n /**\n * The pixel pair of each library image this body names, keyed by the target\n * as the body writes it, stamped by the composition from the assets' DAM\n * records (AGL-3149) — never set by hand, and never on the template, where\n * `markdown` is still the `{{entry.body}}` token and names no asset.\n */\n intrinsicSizes?: Record<string, { width: number; height: number }>\n}\n\n/** A still-unresolved `{{token}}` (no entry context on this render). */\nconst UNRESOLVED_TOKEN = /^\\{\\{[^}]+\\}\\}$/\n\nconst renderInlines = (\n inlines: Aglyn.MarkdownInline[],\n links: { suppressNavigation?: boolean; screens?: Aglyn.ScreenRouteMap },\n): ReactNode[] =>\n inlines.map((item, index) => {\n if (item.type === 'bold') return <strong key={index}>{item.text}</strong>\n if (item.type === 'italic') return <em key={index}>{item.text}</em>\n if (item.type !== 'link') return <span key={index}>{item.text}</span>\n // Internal paths route through AppLink for client-side navigation\n // (AGL-582); external links stay plain anchors. Editing surfaces render\n // the link look without an href so clicks never navigate. A target named\n // by REFERENCE resolves against the routing map first (AGL-3118), and one\n // whose entry, listing or screen is gone renders as the words alone —\n // never as an anchor holding the stored value.\n const link = Aglyn.resolveMarkdownLink(item.href, links)\n if (link.kind === 'text') return <span key={index}>{item.text}</span>\n if (link.kind === 'inert') {\n return (\n <MuiLink key={index} component=\"span\" sx={{ cursor: 'default' }}>\n {item.text}\n </MuiLink>\n )\n }\n return link.kind === 'internal' ? (\n <AppLink key={index} href={link.href}>\n {item.text}\n </AppLink>\n ) : (\n <MuiLink key={index} href={link.href}>\n {item.text}\n </MuiLink>\n )\n })\n\n/**\n * Renders a content entry's markdown-lite body as themed MUI elements\n * (AGL-551): headings, paragraphs, lists, and images pick up the site\n * theme's typography instead of the old unthemed article HTML. Parsing is\n * pure, so the full body server-renders (SEO keeps the article text).\n */\nconst CollectionEntryBody = forwardRef<\n HTMLDivElement,\n CollectionEntryBodyProps\n>((props, ref) => {\n // `intrinsicSizes` is destructured out rather than left in `rest`: `rest` is\n // spread onto a Box, and a composition-stamped object would reach the DOM as\n // an attribute React has no idea what to do with.\n const { markdown, intrinsicSizes, ...rest } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n // The routing map travels with the suppression flag: both decide what a\n // link in this body renders as (AGL-3118).\n const { suppressNavigation, screens } = useContext(Aglyn.ScreenLinkContext)\n const links = useMemo(\n () => ({ suppressNavigation, screens }),\n [suppressNavigation, screens],\n )\n // The site being rendered, for image blocks (AGL-1686) — read once here\n // because hooks cannot run inside the block map below.\n const { hostId } = Aglyn.useSite()\n const source = (markdown ?? '').trim()\n const unresolved = !source || UNRESOLVED_TOKEN.test(source)\n const blocks = useMemo(\n () => (unresolved ? [] : Aglyn.parseMarkdownLite(source)),\n [source, unresolved],\n )\n // Heading anchors, on the same terms as the Markdown element (AGL-1162).\n // Without them a blog article could not carry an \"On this page\" aside at\n // all: the Table of Contents element emits `#slug` links, and there was\n // nothing on this page for them to land on.\n const slugs = useMemo(() => Aglyn.markdownHeadingSlugs(blocks), [blocks])\n if (unresolved) {\n // Editing surfaces get an affordance; the published site renders\n // nothing rather than a literal token.\n if (!suppressNavigation) return <Box ref={ref} {...rest} />\n return (\n <Box\n ref={ref}\n {...rest}\n sx={[\n {\n p: 2,\n border: '1px dashed',\n borderColor: 'divider',\n color: 'text.secondary',\n fontSize: 12,\n fontFamily: 'system-ui, sans-serif',\n },\n ...nodeSx,\n ]}\n >\n {'Entry body — the {{entry.body}} markdown renders here'}\n </Box>\n )\n }\n return (\n <Box ref={ref} {...rest}>\n {blocks.map((block, index) => {\n if (block.type === 'heading') {\n return (\n <Typography\n key={index}\n id={slugs[index]}\n variant={block.level === 2 ? 'h4' : 'h5'}\n component={block.level === 2 ? 'h2' : 'h3'}\n gutterBottom\n sx={{\n scrollMarginTop: `${Aglyn.HEADING_ANCHOR_SCROLL_MARGIN}px`,\n }}\n >\n {renderInlines(block.inlines, links)}\n </Typography>\n )\n }\n if (block.type === 'image') {\n return (\n <Box\n key={index}\n component=\"img\"\n // Resolved like the entry's cover image a few hundred lines\n // below (AGL-1686): a body image is the same kind of asset and\n // has no reason to be stored in a more fragile form.\n //\n // And sized like one too, as of AGL-3149: the same call gives\n // the Markdown element's body images their candidate list and\n // intrinsic pair, so the two bodies cannot disagree about how a\n // picture in prose is delivered.\n {...Aglyn.mediaBodyImageAttributes({\n src: block.src,\n hostId,\n size: intrinsicSizes?.[block.src],\n })}\n alt={block.alt}\n // Paired with the `width`/`height` above — see the Markdown\n // element's copy of this: without it the height hint outlives\n // the `maxWidth` cap and squashes the picture.\n sx={{ maxWidth: '100%', height: 'auto', borderRadius: 1, my: 1 }}\n // An image inside an entry body is below the fold by\n // construction — the title, byline and opening paragraphs are\n // above it. This one carried no loading hint at all, so it was\n // fetched EAGERLY, at default priority, competing with the\n // entry's own cover (AGL-2486).\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n )\n }\n if (block.type === 'list') {\n return (\n <Box key={index} component=\"ul\" sx={{ lineHeight: 1.7, pl: 3 }}>\n {block.items.map((item, itemIndex) => (\n <li key={itemIndex}>\n {renderInlines(item, links)}\n </li>\n ))}\n </Box>\n )\n }\n // A numbered list (AGL-1320) — a real `<ol>`, so the markers are the\n // browser's and `start` survives: a notice that resumes at 7 has to\n // read as 7 for the enumeration to mean anything.\n if (block.type === 'orderedList') {\n return (\n <Box\n key={index}\n component=\"ol\"\n start={block.start}\n sx={{ lineHeight: 1.7, pl: 3 }}\n >\n {block.items.map((item, itemIndex) => (\n <li key={itemIndex}>\n {renderInlines(item, links)}\n </li>\n ))}\n </Box>\n )\n }\n // Code blocks and tables (AGL-974). Both scroll rather than wrap:\n // a wrapped code line or a squeezed table column loses the very\n // structure that made the block worth writing.\n if (block.type === 'code') {\n return (\n <Box\n key={index}\n component=\"pre\"\n sx={{\n my: 2,\n p: 2,\n overflowX: 'auto',\n borderRadius: 1,\n bgcolor: 'action.hover',\n fontFamily: 'monospace',\n fontSize: 14,\n }}\n >\n <code>{block.text}</code>\n </Box>\n )\n }\n if (block.type === 'table') {\n return (\n <Box key={index} sx={{ my: 2, overflowX: 'auto' }}>\n <Box\n component=\"table\"\n sx={{\n borderCollapse: 'collapse',\n width: '100%',\n '& th, & td': {\n border: '1px solid',\n borderColor: 'divider',\n px: 1.5,\n py: 1,\n },\n '& th': { bgcolor: 'action.hover' },\n }}\n >\n <thead>\n <tr>\n {block.header.map((cell, cellIndex) => (\n <th\n key={cellIndex}\n style={{ textAlign: block.align[cellIndex] ?? 'left' }}\n >\n {renderInlines(cell, links)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {block.rows.map((row, rowIndex) => (\n <tr key={rowIndex}>\n {row.map((cell, cellIndex) => (\n <td\n key={cellIndex}\n style={{\n textAlign: block.align[cellIndex] ?? 'left',\n }}\n >\n {renderInlines(cell, links)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </Box>\n </Box>\n )\n }\n // The article template's pull-quote (AGL-1315, Figma 170:225):\n // larger italic prose behind a left accent. Palette tokens only, so\n // it follows the site theme in both modes.\n if (block.type === 'quote') {\n return (\n <Typography\n key={index}\n component=\"blockquote\"\n sx={{\n my: 3,\n mx: 0,\n pl: 2.5,\n borderLeft: '3px solid',\n borderColor: 'primary.main',\n fontStyle: 'italic',\n fontSize: '1.25em',\n lineHeight: 1.6,\n color: 'text.primary',\n }}\n >\n {renderInlines(block.inlines, links)}\n </Typography>\n )\n }\n return (\n <Typography\n key={index}\n variant=\"body1\"\n sx={{ lineHeight: 1.7 }}\n gutterBottom\n >\n {renderInlines(block.inlines, links)}\n </Typography>\n )\n })}\n </Box>\n )\n})\nCollectionEntryBody.displayName = 'AglynCollectionEntryBody'\n\nexport const collectionEntryBodySchema: Aglyn.ComponentSchema<CollectionEntryBodyProps> =\n {\n $id: ENTRY_BODY_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Entry Body',\n description: \"The current entry's markdown body, on an entry template.\",\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiTextLong.path, sx: { color: 'secondary.main' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'markdown',\n label: 'Markdown',\n description:\n 'Markdown-lite content. Keep {{entry.body}} on entry-template ' +\n \"screens so each entry's body renders here.\",\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n ],\n }\n\n/* ── Related posts (AGL-582) ────────────────────────────────────────────── */\n\n/** How the related posts are laid out (AGL-1457). */\nexport type CollectionRelatedLayout = 'list' | 'cards'\n\n/** One related post as stamped by the compose pipeline (AGL-582). */\nexport interface CollectionRelatedProps extends StackProps {\n /** Section heading; empty string hides it. */\n heading?: string\n /** Compose-time: most related posts listed (default 3). */\n limit?: number | string\n /**\n * Emit each entry's cover image (AGL-1457). OFF is the default and the\n * shipped behaviour — the block is live on every blog entry, so turning\n * covers on by default would restyle published pages nobody asked about.\n */\n showCover?: boolean\n /**\n * `list` (default) is the plain-link list that has always shipped; `cards`\n * is the article frame's grid of cover + category chip + title.\n */\n layout?: CollectionRelatedLayout\n /** Columns in the `cards` grid (default 3, the frame's 3-up). */\n columns?: number | string\n /**\n * Type step the section heading reads at (default `h5`). One of the\n * theme's own rungs, shared with the Typography element — a heading sized\n * by hand in the Styles panel is the pixel-typing the tokens exist to stop.\n */\n headingVariant?: string\n /**\n * Type step each card title reads at (default `subtitle1`, what the block\n * has always emitted). The title is the block's own markup, so the Styles\n * panel — which edits the node's root — cannot reach it; this is the only\n * handle on it.\n */\n titleVariant?: string\n /** Show each post's published date. On is the shipped behaviour. */\n showDate?: boolean\n /**\n * How each card's published date reads (AGL-1459) — a COMPOSE-TIME prop\n * like {@link limit}: it is answered in `expandCollectionRelated`, where\n * the timestamp still exists, and never reaches the DOM. Blank, or\n * `default`, is the locale date the block has always emitted.\n *\n * Read here too, but only to date the SAMPLE cards: an editing surface has\n * no routed entry and therefore no server fill, so the picker would\n * otherwise preview nothing.\n */\n dateFormat?: Aglyn.CollectionEntryDateFormat\n /** Show each post's category. On is the shipped behaviour. */\n showCategory?: boolean\n /**\n * Show each post's excerpt. OFF is the default and the shipped behaviour:\n * the excerpt has been stamped onto every related item since AGL-582 and\n * rendered by nothing, so turning it on by default would add a paragraph\n * to every published entry nobody asked about.\n */\n showExcerpt?: boolean\n /**\n * Server-stamped related posts (`expandCollectionRelated`); never set by\n * hand — the tenant computes it from the current entry's category/tags.\n */\n entries?: Aglyn.CollectionRelatedItem[]\n}\n\n/** The frame's 3-up (Figma 170:242) when nothing usable is authored. */\nconst RELATED_DEFAULT_COLUMNS = 3\n\n/**\n * The cover's PROPORTION in the card grid.\n *\n * ONE ratio for every card, rather than each record's own shape: the titles\n * under the covers only line up when the covers are the same height, and a\n * grid of honest shapes is a ragged grid.\n *\n * The ratio is the shape the console asks authors for — 1200 x 630, the same\n * image that heads the entry and doubles as its share card — so a cover\n * authored as instructed loses nothing to the crop. The frame's own card is\n * 445 x 180 (Figma 170:243), which is 2.47 against that art's 1.90: held\n * there, `objectFit: cover` discarded 23% of every card's picture at every\n * width. Cover art is usually a composed lockup, so what it discards is the\n * title set on it.\n *\n * Before the ratio it was a fixed 180px height, which was the frame's number\n * without the frame's shape: a card is 445 wide there, the grid's columns\n * grow with the viewport and 180 does not, so every extra pixel of width\n * flattened the box further — 3.57:1 by a 2000px viewport. A ratio holds the\n * shape at every width; this ratio holds the picture as well.\n */\nconst RELATED_COVER_ASPECT = '1200 / 630'\n\n/**\n * The card's category chip (AGL-1457). ONE fixed token pair for every\n * category: colour-coding a chip per category needs conditional styling,\n * which is not expressible yet (AGL-1307), and inventing a palette here\n * would have to be unpicked when it is. Same shape as the tag chips Entry\n * Meta already renders, so the two blocks read as one vocabulary.\n */\nconst relatedChipSx = {\n alignSelf: 'flex-start',\n borderColor: 'divider',\n color: 'text.secondary',\n '& .MuiChip-label': {\n // 9px, the frame's own inset on a 69 x 24 chip.\n px: 1.125,\n // The frame sets this label in the same uppercase, letter-spaced step as\n // the kicker above an article title, which is what `overline` IS in the\n // site theme. Naming the step keeps the chip on the theme's scale instead\n // of a hand-written size that drifts the moment the theme moves.\n typography: 'overline',\n // `size=\"small\"` gives the chip the frame's 24px height, and `overline`\n // carries a 2.66 line-height meant for a standalone kicker with room\n // around it. Inside a fixed-height chip the box sets the height, so the\n // line collapses to it and the label centres.\n lineHeight: 1,\n },\n}\n\n/** The heading step the block has always emitted. */\nconst RELATED_DEFAULT_HEADING_VARIANT = 'h5'\n\n/** The card-title step the block has always emitted. */\nconst RELATED_DEFAULT_TITLE_VARIANT = 'subtitle1'\n\n/**\n * The instant the sample cards are dated. A FIXED one, never `Date.now()`:\n * the canvas has to render the same bytes every time it opens, and this is\n * the date the Date-format labels use as their worked example — so the\n * dropdown and the cards it previews agree word for word.\n */\nconst RELATED_SAMPLE_PUBLISHED_AT = { seconds: Date.UTC(2026, 7, 9) / 1000 }\n\n/**\n * Titles for the sample cards. Deliberately unmistakable as copy: the author\n * is looking at a LAYOUT, and a plausible-looking headline here is one an\n * author can mistake for a post that exists — or worse, design around.\n */\nconst RELATED_SAMPLE_TITLES = [\n 'Sample related post',\n 'A second sample post',\n 'A third sample post',\n]\n\n/** Category chip text for the sample cards; names the field, not a taxonomy. */\nconst RELATED_SAMPLE_CATEGORY = 'Category'\n\n/** Excerpt text for the sample cards; one line, so the card keeps its shape. */\nconst RELATED_SAMPLE_EXCERPT =\n 'Each post’s own excerpt reads here, trimmed to a line or two.'\n\n/**\n * Stand-in posts for editing surfaces (AGL-2486).\n *\n * Related posts are resolved FROM the routed entry, and a besigner canvas has\n * no routed entry — so the block used to draw a one-line dashed strip and the\n * author styled a layout they could not see. The sample is the block's REAL\n * markup, at the author's own settings, which is the only way the canvas can\n * answer \"what will this look like\".\n *\n * It exists only where {@link ScreenLinkContext.suppressNavigation} is on —\n * the besigner canvas and Preview. A published render reaches this function\n * on no path at all, so no visitor can ever be shown a post that does not\n * exist.\n *\n * The count follows the author's own `limit` so a 6-post rail at 3 columns\n * previews as two rows, and is bounded by the same ceiling the server\n * applies rather than a second one.\n */\nconst sampleRelatedEntries = (\n limit: number | string | undefined,\n dateFormat: Aglyn.CollectionEntryDateFormat,\n): Aglyn.CollectionRelatedItem[] => {\n const count = Math.min(\n // `|| default` also rejects 0, which `toCount` would otherwise accept and\n // preview as an empty rail — the one thing the sample exists to avoid.\n toCount(limit, Aglyn.COLLECTION_RELATED_DEFAULT_LIMIT) ||\n Aglyn.COLLECTION_RELATED_DEFAULT_LIMIT,\n Aglyn.COLLECTION_RELATED_MAX,\n )\n const date = Aglyn.formatCollectionEntryDate(\n RELATED_SAMPLE_PUBLISHED_AT,\n dateFormat,\n )\n return Array.from({ length: count }, (_, index) => ({\n title: RELATED_SAMPLE_TITLES[index] ?? `Sample related post ${index + 1}`,\n // Empty, not a plausible path: every surface that renders the sample\n // suppresses navigation, so the title is text rather than an anchor and\n // this is never read. A real-looking href would be a link to nowhere the\n // moment that stopped being true.\n url: '',\n date,\n category: RELATED_SAMPLE_CATEGORY,\n excerpt: RELATED_SAMPLE_EXCERPT,\n }))\n}\n\n/**\n * Lists other entries of the same collection sharing the current entry's\n * category or a tag (AGL-582). The tenant stamps `entries` at compose time\n * on entry renders; without them the published site renders nothing and an\n * editing surface renders sample cards (AGL-2486).\n *\n * Two layouts (AGL-1457). `list` is the plain-link list that has always\n * shipped and stays the default — the block is live on every blog entry, so\n * a new default would restyle published pages nobody asked about. `cards` is\n * the article frame's grid: cover, category chip, title, at an author-set\n * column count.\n *\n * The block owns its markup, so nothing inside a card is a node the Styles\n * panel can select. Every part it renders therefore needs an attribute, or\n * it is unauthorable by construction — which is what the type steps and the\n * `show*` switches are (AGL-2486).\n */\nconst CollectionRelated = forwardRef<HTMLDivElement, CollectionRelatedProps>(\n (props, ref) => {\n // `limit` and `dateFormat` are compose-time: the tenant resolves them\n // while stamping `entries`; strip them so they never hit the DOM. The\n // rest are read here, so they must not reach it either.\n const {\n heading,\n limit,\n entries,\n showCover,\n layout,\n columns,\n headingVariant,\n titleVariant,\n showDate,\n dateFormat,\n showCategory,\n showExcerpt,\n ...rest\n } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n // The resolver every other surface shares (AGL-1215): a stamped `media:`\n // reference becomes a CDN URL HERE, not in the document, so one\n // reference keeps working across sites. Called before the early return —\n // it is a hook.\n const { hostId } = Aglyn.useSite()\n // An editing surface has no routed entry, so nothing is stamped and the\n // block used to be the one part of the page that was not WYSIWYG. The\n // sample stands in for exactly that gap, and only there.\n const sample = !entries?.length && Boolean(suppressNavigation)\n const items = entries?.length\n ? entries\n : sample\n ? sampleRelatedEntries(\n limit,\n Aglyn.normalizeCollectionEntryDateFormat(dateFormat),\n )\n : []\n if (!items.length) return <Box ref={ref} {...rest} />\n const title = heading ?? 'Related articles'\n const headingNode = title ? (\n <Typography\n variant={(headingVariant || RELATED_DEFAULT_HEADING_VARIANT) as 'h5'}\n component=\"h2\"\n >\n {title}\n </Typography>\n ) : null\n /**\n * The card title, as a HEADING in the site theme.\n *\n * It used to be a bare `AppLink`, which is MUI's `Link` — `primary.main`\n * and `underline=\"always\"` — so the one thing a related card is FOR read\n * as a default browser link on a themed page, at no heading level, and\n * with no attribute able to touch it. The type step now comes from the\n * theme's own rungs and the anchor inherits the colour, exactly as the\n * Entry Author card's byline link does.\n *\n * `linked` is false wherever the whole card is already the anchor: an\n * `<a>` inside an `<a>` is invalid markup that browsers silently unnest.\n */\n const titleNode = (entry: Aglyn.CollectionRelatedItem, linked: boolean) => (\n <Typography\n variant={(titleVariant || RELATED_DEFAULT_TITLE_VARIANT) as 'subtitle1'}\n component=\"h3\"\n >\n {linked && !suppressNavigation && entry.url ? (\n <AppLink href={entry.url} sx={{ color: 'inherit' }} underline=\"hover\">\n {entry.title}\n </AppLink>\n ) : (\n entry.title\n )}\n </Typography>\n )\n const dateNode = (entry: Aglyn.CollectionRelatedItem) =>\n showDate !== false && entry.date ? (\n <Typography variant=\"caption\" sx={{ color: 'text.secondary' }}>\n {entry.date}\n </Typography>\n ) : null\n const excerptNode = (entry: Aglyn.CollectionRelatedItem) =>\n showExcerpt && entry.excerpt ? (\n <Typography variant=\"body2\" sx={{ color: 'text.secondary' }}>\n {entry.excerpt}\n </Typography>\n ) : null\n /**\n * The cover, when the author asked for one AND the entry has one. An\n * entry without a cover gets no box at all rather than a placeholder:\n * the related list is a real feed, and a row of grey rectangles is worse\n * than a row of titles.\n *\n * The sample cards are the exception, and for the opposite reason: they\n * stand in for posts that do not exist yet, so the cover SLOT is what\n * the author is looking at. A card grid previewing without its images is\n * not the grid being styled.\n */\n const coverNode = (entry: Aglyn.CollectionRelatedItem) => {\n const src = showCover\n ? Aglyn.resolveMediaSrc(entry.coverImage, { hostId })\n : undefined\n if (!src) {\n if (!sample || !showCover) return null\n return (\n <Box\n aria-hidden\n sx={{\n width: '100%',\n aspectRatio: RELATED_COVER_ASPECT,\n borderRadius: 1,\n backgroundColor: 'action.hover',\n }}\n />\n )\n }\n return (\n <Box\n component=\"img\"\n src={src}\n // The author's own description wins, and the title stays the\n // fallback (AGL-2418). Unlike the entry hero and the event\n // thumbnail — which render empty when nothing is authored — this\n // cover is the LINK's own content, so it must carry an accessible\n // name rather than go silent.\n alt={Aglyn.renderedMediaAlt(entry.coverImageAlt, entry.title)}\n sx={{\n display: 'block',\n width: '100%',\n aspectRatio: RELATED_COVER_ASPECT,\n objectFit: 'cover',\n borderRadius: 1,\n }}\n // `lazy` ALONE was the bug in miniature (AGL-2486): a lazy image\n // at default priority still outranks a lazy image at `low`, so\n // this related-entries rail — which sits at the bottom of an entry\n // by construction — was beating the deferred Image elements in the\n // body above it. The hint only works as a set.\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n )\n }\n\n /**\n * The notice that says these cards are not posts (AGL-2486). Editing\n * surfaces only, and outside the card markup rather than inside it, so\n * the author is styling the same tree the site will render.\n */\n const sampleNotice = sample ? (\n <Typography\n variant=\"caption\"\n sx={{\n display: 'block',\n p: 1,\n border: '1px dashed',\n borderColor: 'divider',\n borderRadius: 1,\n color: 'text.secondary',\n fontFamily: 'system-ui, sans-serif',\n }}\n >\n {'Sample posts — entries sharing this entry’s category or tags ' +\n 'replace these on the published page'}\n </Typography>\n ) : null\n\n if (layout === 'cards') {\n // `toCount` rounds and rejects junk; `|| default` also rejects 0, which\n // it would otherwise accept as a column count and emit `repeat(0, …)`.\n const columnCount =\n toCount(columns, RELATED_DEFAULT_COLUMNS) || RELATED_DEFAULT_COLUMNS\n /**\n * One card's contents, always with a PLAIN title: the caller wraps the\n * whole card in the anchor, because a reader aiming at a 180px cover\n * should not have to hit the line of text under it — and a second\n * anchor around the title inside that one is markup browsers unnest.\n */\n const card = (entry: Aglyn.CollectionRelatedItem) => (\n // 12px between cover, chip and title — the frame's rhythm (170:243\n // puts the chip at y=192 under a cover ending at 180, and the title\n // at y=228 under a chip ending at 216).\n <MuiStack spacing={1.5}>\n {coverNode(entry)}\n {showCategory !== false && entry.category ? (\n <Chip\n label={entry.category}\n size=\"small\"\n variant=\"outlined\"\n sx={relatedChipSx}\n />\n ) : null}\n {titleNode(entry, false)}\n {excerptNode(entry)}\n {dateNode(entry)}\n </MuiStack>\n )\n return (\n <Box\n ref={ref}\n {...rest}\n // MERGE, never replace (AGL-1450) — the node's slice arrives as the\n // ARRAY `mergeSxProps` builds in leaf.tsx, so folding it into an\n // object spreads numeric keys and discards every authored property\n // while these defaults still apply. Defaults first, node's after.\n sx={[\n {\n display: 'grid',\n gridTemplateColumns: `repeat(${columnCount}, minmax(0, 1fr))`,\n alignItems: 'start',\n columnGap: 3,\n rowGap: 4,\n },\n ...nodeSx,\n ]}\n >\n {headingNode ? (\n <Box sx={{ gridColumn: '1 / -1' }}>{headingNode}</Box>\n ) : null}\n {sampleNotice ? (\n <Box sx={{ gridColumn: '1 / -1' }}>{sampleNotice}</Box>\n ) : null}\n {items.map((entry, index) => {\n const linked = !suppressNavigation && Boolean(entry.url)\n return linked ? (\n <AppLink\n key={index}\n href={entry.url}\n underline=\"none\"\n // A grid ITEM, so it has to be a block; `color: inherit`\n // keeps the cover, chip and date reading as themed content\n // rather than as the inside of a link.\n sx={{ color: 'inherit', display: 'block' }}\n >\n {card(entry)}\n </AppLink>\n ) : (\n <Box key={index}>{card(entry)}</Box>\n )\n })}\n </Box>\n )\n }\n return (\n <MuiStack ref={ref} spacing={1.5} {...rest}>\n {headingNode}\n {sampleNotice}\n {items.map((entry, index) => {\n const meta = [\n showDate !== false ? entry.date : '',\n showCategory !== false ? entry.category : '',\n ].filter(Boolean)\n return (\n <MuiStack key={index} spacing={0.25}>\n {coverNode(entry)}\n {titleNode(entry, true)}\n {excerptNode(entry)}\n {meta.length ? (\n <Typography variant=\"caption\" sx={{ color: 'text.secondary' }}>\n {meta.join(' · ')}\n </Typography>\n ) : null}\n </MuiStack>\n )\n })}\n </MuiStack>\n )\n },\n)\nCollectionRelated.displayName = 'AglynCollectionRelated'\n\nexport const collectionRelatedSchema: Aglyn.ComponentSchema<CollectionRelatedProps> =\n {\n $id: RELATED_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Related Posts',\n description: \"Other entries sharing this one's category or tags.\",\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiNewspaperVariantOutline.path, sx: { color: 'secondary.main' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'heading',\n label: 'Heading',\n description: 'Section heading (blank hides it).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'limit',\n label: 'Limit',\n description: 'Most related posts listed (default 3).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'layout',\n label: 'Layout',\n description:\n 'List keeps the plain links this block has always rendered. ' +\n 'Cards is the article layout: cover, category, title, in a grid.',\n component: Aglyn.FieldComponentType.SELECT,\n // Both values are REAL (AGL-1451/AGL-1453): `''` cannot survive a\n // save, so an author who switched to Cards would have no route back.\n // `list` is also the value the render falls back to, so naming it is\n // the same choice, not a second one.\n options: [\n { value: 'list', label: 'List (default)' },\n { value: 'cards', label: 'Card grid' },\n ],\n },\n {\n name: 'columns',\n label: 'Columns',\n description: 'Cards per row in the card grid. Default 3.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n // Meaningless on the list, where each post is its own row.\n condition: { when: 'layout', is: 'cards' },\n },\n {\n name: 'showCover',\n label: 'Show cover',\n description:\n 'Show each post’s cover image. Posts without one show their ' +\n 'title alone rather than an empty box.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'headingVariant',\n label: 'Heading style',\n description:\n 'Which type step from the site theme the section heading above ' +\n 'the posts reads at. Default Heading 5.',\n component: Aglyn.FieldComponentType.SELECT,\n // The theme's own rungs, shared with the Typography element rather\n // than a second list here: two lists is how a step added to the\n // theme becomes reachable in one place and not the other.\n options: [...typographyVariants],\n },\n {\n name: 'titleVariant',\n label: 'Title style',\n description:\n 'Which type step from the site theme each post’s title reads at. ' +\n 'Default Subtitle 1; the title takes the surrounding text colour ' +\n 'rather than the link colour.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [...typographyVariants],\n },\n {\n name: 'showDate',\n label: 'Show date',\n description:\n 'Off drops each post’s published date. The posts stay in ' +\n 'newest-first order either way — this is about the card, not the ' +\n 'ordering.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n {\n name: 'dateFormat',\n label: 'Date format',\n description:\n 'How each post’s published date reads on its card. Site default ' +\n 'is the format this block has always used.',\n component: Aglyn.FieldComponentType.SELECT,\n // The formats the pure layer knows how to produce, so the list\n // cannot offer a shape nothing renders. Every value is REAL,\n // including the do-nothing one (AGL-1451/AGL-1453).\n options: [...Aglyn.COLLECTION_ENTRY_DATE_FORMAT_OPTIONS],\n },\n {\n name: 'showCategory',\n label: 'Show category',\n description:\n 'Off drops the category — the chip above each card title, or the ' +\n 'second half of the line under each link on the list layout.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showExcerpt',\n label: 'Show excerpt',\n description:\n 'Adds each post’s excerpt under its title. Posts with no excerpt ' +\n 'are unchanged rather than leaving a gap.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n ],\n }\n\n/* ── Share bar (AGL-582) ────────────────────────────────────────────────── */\n\nexport interface CollectionShareProps extends StackProps {\n /** Heading before the buttons; empty string hides it. */\n heading?: string\n}\n\nconst SHARE_TARGETS = [\n {\n label: 'Share on X',\n path: mdiTwitter.path,\n href: (url: string) =>\n `https://twitter.com/intent/tweet?url=${encodeURIComponent(url)}`,\n },\n {\n label: 'Share on LinkedIn',\n path: mdiLinkedin.path,\n href: (url: string) =>\n 'https://www.linkedin.com/sharing/share-offsite/?url=' +\n encodeURIComponent(url),\n },\n {\n label: 'Share on Facebook',\n path: mdiFacebook.path,\n href: (url: string) =>\n `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`,\n },\n]\n\n/**\n * Share buttons for the CURRENT page URL (AGL-582): X, LinkedIn, Facebook,\n * and copy-link. Pure client behavior — the URL is read at click time so\n * SSR and besigner renders stay markup-identical; editing surfaces no-op.\n */\nconst CollectionShare = forwardRef<HTMLDivElement, CollectionShareProps>(\n (props, ref) => {\n const { heading, ...rest } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n const [copied, setCopied] = useState(false)\n const title = heading ?? 'Share'\n const open = (buildHref: (url: string) => string) => () => {\n if (suppressNavigation || typeof window === 'undefined') return\n window.open(\n buildHref(window.location.href),\n '_blank',\n 'noopener,noreferrer',\n )\n }\n const copy = async () => {\n if (suppressNavigation || typeof window === 'undefined') return\n try {\n await navigator.clipboard.writeText(window.location.href)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n } catch {\n // Clipboard unavailable (permissions, http) — silently skip.\n }\n }\n return (\n <MuiStack\n ref={ref}\n direction=\"row\"\n spacing={0.5}\n {...rest}\n // MERGE, never replace (AGL-1450) — see Entry Meta below.\n sx={[{ alignItems: 'center' }, ...nodeSx]}\n >\n {title ? (\n // `component=\"p\"` because this labels a row of buttons; it is not a\n // section heading (AGL-2486). MUI's `defaultVariantMapping` sends\n // `subtitle2` to `<h6>`, so without this the Share Bar puts a level-6\n // heading into the document outline of every page carrying it. On a\n // blog post the nearest preceding heading is an `h2`, which makes it\n // a skipped level and a `heading-order` failure.\n //\n // Said here rather than as a theme-wide `variantMapping` override: a\n // default would silently change the element under every `subtitle2`\n // in the codebase, including surfaces where an `h6` is correct.\n <Typography component=\"p\" variant=\"subtitle2\" sx={{ mr: 1 }}>\n {title}\n </Typography>\n ) : null}\n {SHARE_TARGETS.map((target) => (\n <IconButton\n key={target.label}\n aria-label={target.label}\n size=\"small\"\n onClick={open(target.href)}\n >\n <MdiIcon path={target.path} />\n </IconButton>\n ))}\n <IconButton\n aria-label={copied ? 'Link copied' : 'Copy link'}\n size=\"small\"\n color={copied ? 'success' : 'default'}\n onClick={copy}\n >\n <MdiIcon path={mdiContentCopy.path} />\n </IconButton>\n </MuiStack>\n )\n },\n)\nCollectionShare.displayName = 'AglynCollectionShare'\n\nexport const collectionShareSchema: Aglyn.ComponentSchema<CollectionShareProps> =\n {\n $id: SHARE_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Share Bar',\n description: 'Share buttons for the current page, plus a copy link.',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiShareVariant.path, sx: { color: 'secondary.main' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'heading',\n label: 'Heading',\n description: 'Text before the buttons (blank hides it).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n ],\n }\n\n/* ── Entry meta (AGL-582) ───────────────────────────────────────────────── */\n\nexport interface CollectionEntryMetaProps extends StackProps {\n /**\n * Published date. Server-filled from the routed entry on entry templates\n * (`expandCollectionEntryMeta`, AGL-1385); set it — or bind\n * `{{entry.date}}` — only to override.\n */\n date?: string\n /**\n * How {@link date} reads when the server fills it in (AGL-1459) — a\n * COMPOSE-TIME prop, like Related Posts' `limit`: it is answered where the\n * timestamp still exists (`expandCollectionEntryMeta`) and never reaches the\n * DOM. Blank, or `default`, is the locale date the block has always emitted.\n */\n dateFormat?: Aglyn.CollectionEntryDateFormat\n /**\n * Byline (AGL-1459). Server-filled from the entry's own author on entry\n * templates, exactly like {@link date}; set it — or bind `{{entry.author}}`\n * — only to override.\n */\n author?: string\n /** Category; server-filled, or bind `{{entry.category}}` to override. */\n category?: string\n /** Comma-joined tags; server-filled, or bind `{{entry.tags}}`. */\n tags?: string\n /**\n * Round avatar shown before the byline (AGL-1459) — a media-picker target,\n * so it holds a media reference (AGL-1215) as well as any URL form.\n *\n * A block-level pick, deliberately, and NOT a per-author image: entries\n * carry an author NAME (`authorName`) and no portrait field, so a per-author\n * avatar would need a schema decision on the entry model plus an editor\n * field to fill it. Until then this is the site's brand mark, chosen once on\n * the template — which is what the article frame actually asks for. Left\n * unset it renders nothing at all, never a broken image.\n */\n avatarImage?: string\n showDate?: boolean\n showAuthor?: boolean\n /**\n * Where the byline's NAME links — the author's page on this site\n * (AGL-2519). Server-filled from the routed entry's author, like the\n * avatar beside it; blank renders the name as plain text.\n *\n * Distinct from the author's own `url`, which the Entry Author card offers\n * as a link row: this is \"everything they wrote here\", that is \"their\n * site\", and a byline that conflated them would send a reader off-site from\n * every article.\n */\n authorPageUrl?: string\n /** Off leaves the byline as plain text even when the author has a page. */\n linkAuthor?: boolean\n showCategory?: boolean\n showTags?: boolean\n showAvatar?: boolean\n}\n\n/**\n * THE SAMPLE VALUES THE ENTRY BLOCKS PREVIEW WITH (AGL-2486).\n *\n * Entry Meta and Entry Author resolve FROM the routed entry, and a besigner\n * canvas has no routed entry — so both drew a one-line dashed strip and an\n * author styling a byline or an author card was styling something they could\n * not see. Related Posts had the same gap and was answered the same way: the\n * block's REAL markup, at the author's own settings, on editing surfaces\n * only.\n *\n * These two need no \"this is a sample\" notice the way the related CARDS do.\n * A card carries a title, a cover and a category, which is what a real post\n * looks like; a byline reading `Sample author` says what it is in the words\n * themselves, and a notice above a single line of caption text would be\n * taller than the thing it labels.\n *\n * The date is a FIXED instant, never `Date.now()`: the canvas has to render\n * the same bytes every time it opens, and it is the same instant the related\n * sample uses, so two blocks on one template never disagree about what day\n * their example is.\n */\nconst ENTRY_SAMPLE_PUBLISHED_AT = RELATED_SAMPLE_PUBLISHED_AT\n\n/** Byline name for the samples; names the field rather than a person. */\nconst ENTRY_SAMPLE_AUTHOR = 'Sample author'\n\n/** Category text for the samples; names the field, not a taxonomy. */\nconst ENTRY_SAMPLE_CATEGORY = RELATED_SAMPLE_CATEGORY\n\n/** Two chips, so the row previews its own wrapping and gap. */\nconst ENTRY_SAMPLE_TAGS = 'first tag, second tag'\n\n/** Bio line for the author-card sample; one sentence, as a real one is. */\nconst ENTRY_SAMPLE_BIO =\n 'The bio from this author’s record reads here, in a line or two.'\n\n/**\n * The portrait STAND-IN, for a sample that has no image to resolve.\n *\n * A neutral plate rather than nothing, and for the reason the related sample\n * draws its cover slot: on an entry template the portrait arrives from the\n * author record at render, so the slot is exactly what the author is sizing\n * and spacing. `aria-hidden` because it depicts nothing.\n */\nconst samplePortraitSx = (size: number) => ({\n width: size,\n height: size,\n flexShrink: 0,\n borderRadius: '50%',\n backgroundColor: 'action.hover',\n})\n\n/** Byline avatar, from the article frame (Figma 170:190). */\nconst ENTRY_AVATAR_SIZE = 36\n\n/** Unresolved tokens render empty on the site, literal in the besigner. */\nconst metaValue = (\n value: string | undefined,\n suppressNavigation: boolean | undefined,\n): string => {\n const trimmed = (value ?? '').trim()\n if (!trimmed) return ''\n if (UNRESOLVED_TOKEN.test(trimmed) && !suppressNavigation) return ''\n return trimmed\n}\n\n/**\n * \"{{entry.date}} · {{entry.category}}\" meta line plus tag chips\n * (AGL-582). Values arrive through entry tokens on entry renders; on other\n * surfaces unresolved tokens collapse to nothing instead of leaking.\n *\n * On an entry template the tenant now also FILLS the three values from the\n * routed entry when nothing is bound (`expandCollectionEntryMeta`, AGL-1385).\n * Before that, a block dragged from the palette rather than dropped as the\n * preset had no values at all — three \"Show\" switches gating nothing — and\n * rendered as the empty `<Box>` below, at height 0.\n */\nconst CollectionEntryMeta = forwardRef<\n HTMLDivElement,\n CollectionEntryMetaProps\n>((props, ref) => {\n const {\n date,\n // Compose-time (AGL-1459): read by `expandCollectionEntryMeta`, which has\n // the timestamp. Destructured so it never reaches the DOM.\n dateFormat: _dateFormat,\n author,\n authorPageUrl,\n linkAuthor,\n category,\n tags,\n avatarImage,\n showDate,\n showAuthor,\n showCategory,\n showTags,\n showAvatar,\n ...rest\n } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n // The resolver every other surface shares (AGL-1215), so one media\n // reference keeps working across sites. A hook — before any early return.\n const { hostId } = Aglyn.useSite()\n const dateValue = showDate !== false ? metaValue(date, suppressNavigation) : ''\n const authorValue =\n showAuthor !== false ? metaValue(author, suppressNavigation) : ''\n const categoryValue =\n showCategory !== false ? metaValue(category, suppressNavigation) : ''\n const tagsValue = showTags !== false ? metaValue(tags, suppressNavigation) : ''\n const tagList = tagsValue\n .split(',')\n .map((tag) => tag.trim())\n .filter(Boolean)\n // An unresolved token empties on EVERY surface here, unlike the text\n // fields: a literal `{{…}}` in a src is a broken image in the canvas, and a\n // byline that renders a broken avatar on every post is worse than no\n // avatar at all.\n const avatarRaw = (avatarImage ?? '').trim()\n const avatarSrc =\n showAvatar !== false && avatarRaw && !UNRESOLVED_TOKEN.test(avatarRaw)\n ? Aglyn.resolveMediaSrc(avatarRaw, { hostId })\n : ''\n // Author leads, so the frame's \"The Aglyn Team · Jul 2026\" reads in that\n // order. With no author the join is character-for-character what it was.\n const filledLine = [authorValue, dateValue, categoryValue]\n .filter(Boolean)\n .join(' · ')\n /*\n The rest of the line, WITHOUT the author (AGL-2519).\n\n The row is one string everywhere else, which is what a `·`-joined byline\n wants — but a link is an element, so the author has to come out of the\n join to be one. Split rather than reassembled from parts, so the\n separator, the order and the empty-field collapsing all stay in the one\n expression above: the linked row and the plain row cannot render a\n different sentence.\n */\n const authorHrefRaw = metaValue(authorPageUrl, suppressNavigation)\n const authorHref =\n linkAuthor !== false && SAFE_AUTHOR_HREF.test(authorHrefRaw)\n ? authorHrefRaw\n : ''\n const trailingLine = [dateValue, categoryValue].filter(Boolean).join(' · ')\n // Nothing to show and nothing to route from: an editing surface previews\n // the block's own markup at the author's own settings, and a published page\n // renders nothing at all (AGL-2486). The switches are honoured here exactly\n // as they are below, so a row with Show date off previews without one.\n const sample = !filledLine && !tagList.length && !avatarSrc && suppressNavigation\n const line = sample\n ? [\n showAuthor !== false ? ENTRY_SAMPLE_AUTHOR : '',\n showDate !== false\n ? Aglyn.formatCollectionEntryDate(\n ENTRY_SAMPLE_PUBLISHED_AT,\n Aglyn.normalizeCollectionEntryDateFormat(_dateFormat),\n )\n : '',\n showCategory !== false ? ENTRY_SAMPLE_CATEGORY : '',\n ]\n .filter(Boolean)\n .join(' · ')\n : filledLine\n const chips = sample\n ? showTags !== false\n ? ENTRY_SAMPLE_TAGS.split(',').map((tag) => tag.trim())\n : []\n : tagList\n // The portrait an entry's own author record supplies arrives at compose\n // time, so on the canvas there is none to resolve — the slot is drawn\n // instead, which is what a byline's spacing is actually built around.\n const samplePortrait = sample && showAvatar !== false\n if (!line && !chips.length && !avatarSrc && !samplePortrait) {\n return <Box ref={ref} {...rest} />\n }\n return (\n <MuiStack\n ref={ref}\n direction=\"row\"\n spacing={1}\n {...rest}\n // MERGE, never replace (AGL-1450, same class as AGL-1284). `rest.sx`\n // is the ARRAY `mergeSxProps` builds in leaf.tsx, so spreading it into\n // an object produced `{0: …, 1: …, 2: …}` — numeric keys emotion emits\n // as invalid selectors — and discarded EVERY authored property while\n // these two defaults still applied. The block looked deliberately\n // styled, so nothing suggested the value had been dropped. Defaults\n // go first; the node's slice comes after and can override them.\n sx={[{ alignItems: 'center', flexWrap: 'wrap' }, ...nodeSx]}\n >\n {avatarSrc ? (\n <Box\n component=\"img\"\n src={avatarSrc}\n // Decorative: the byline names the author in text right beside it,\n // so a screen reader announcing the mark again is noise.\n alt=\"\"\n sx={{\n display: 'block',\n width: ENTRY_AVATAR_SIZE,\n height: ENTRY_AVATAR_SIZE,\n borderRadius: '50%',\n objectFit: 'cover',\n // No background plate: a brand mark with a transparent ground\n // would sit on a grey disc nobody asked for.\n }}\n // `lazy` alone, same as the related rail above (AGL-2486). A\n // byline avatar is decorative and tiny; it has no business\n // outranking anything.\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n ) : samplePortrait ? (\n <Box aria-hidden sx={samplePortraitSx(ENTRY_AVATAR_SIZE)} />\n ) : null}\n {line ? (\n <Typography variant=\"caption\" sx={{ color: 'text.secondary' }}>\n {\n // Linked only on the real byline, never on the canvas sample: a\n // preview whose placeholder name navigates is a trap, and the\n // sample author has no page to go to.\n authorHref && authorValue && !sample ? (\n <>\n <AppLink\n href={authorHref}\n sx={{ color: 'inherit' }}\n underline=\"hover\"\n >\n {authorValue}\n </AppLink>\n {trailingLine ? ` · ${trailingLine}` : ''}\n </>\n ) : (\n line\n )\n }\n </Typography>\n ) : null}\n {chips.map((tag) => (\n <Chip key={tag} label={tag} size=\"small\" variant=\"outlined\" />\n ))}\n </MuiStack>\n )\n})\nCollectionEntryMeta.displayName = 'AglynCollectionEntryMeta'\n\nexport const collectionEntryMetaSchema: Aglyn.ComponentSchema<CollectionEntryMetaProps> =\n {\n $id: ENTRY_META_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Entry Meta',\n description:\n 'The byline row for an entry — author, date, category and tags.',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiTagOutline.path, sx: { color: 'secondary.main' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'date',\n label: 'Date',\n description:\n 'Blank shows the entry’s own published date on entry templates. ' +\n 'Type here (or bind {{entry.date}}) only to override it.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'dateFormat',\n label: 'Date format',\n description:\n 'How the published date reads. Site default is the format this ' +\n 'block has always used; it has no effect on a Date you typed in ' +\n 'yourself.',\n component: Aglyn.FieldComponentType.SELECT,\n // The formats the pure layer knows how to produce, so the list cannot\n // offer a shape nothing renders. Every value is REAL — including the\n // do-nothing one (AGL-1451/AGL-1453): `''` cannot survive a save, so\n // an author who tried a format would have no route back.\n options: [...Aglyn.COLLECTION_ENTRY_DATE_FORMAT_OPTIONS],\n },\n {\n name: 'author',\n label: 'Author',\n description:\n 'Blank shows the entry’s own author. Type here (or bind ' +\n '{{entry.author}}) only to override it.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'category',\n label: 'Category',\n description:\n 'Blank shows the entry’s own category. Type here (or bind ' +\n '{{entry.category}}) only to override it.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'tags',\n label: 'Tags',\n description:\n 'Comma-separated. Blank shows the entry’s own tags; type here (or ' +\n 'bind {{entry.tags}}) only to override them.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'avatarImage',\n label: 'Avatar',\n description:\n 'Round image before the byline — your brand mark, or any image ' +\n 'from the media library. Blank shows no avatar.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'showDate',\n label: 'Show date',\n description:\n 'Off hides the published date from the line without clearing what ' +\n 'the Date field would have shown.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showCategory',\n label: 'Show category',\n description:\n 'Off drops the category from the line. The entry keeps its ' +\n 'category — this is about the byline, not the taxonomy.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showTags',\n label: 'Show tags',\n description:\n 'Off hides the tag chips below the line. Leave it on and the ' +\n 'other three off for a tags-only row.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n {\n name: 'authorPageUrl',\n label: 'Author links to',\n description:\n 'Blank sends the byline to the author’s page on this site. Type ' +\n 'here (or bind {{entry.authorPageUrl}}) only to override it.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'linkAuthor',\n label: 'Link author',\n description:\n 'Off leaves the byline as plain text even when the author has a ' +\n 'page. On, the name links there and the date and category beside ' +\n 'it do not.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means LINKED in the renderer above, so the switch has to open\n // in that position or it lies about the block in front of the author\n // (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showAuthor',\n label: 'Show author',\n description:\n 'Off hides the byline. The avatar follows it: a block with no ' +\n 'author shown never fills in the author’s portrait.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showAvatar',\n label: 'Show avatar',\n description:\n 'Off hides the round image in front of the byline, including an ' +\n 'author’s own portrait.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n ],\n }\n\n/* ── Entry author card (AGL-2486) ───────────────────────────────────────── */\n\nexport interface CollectionEntryAuthorProps extends StackProps {\n /**\n * The byline. Server-filled from the routed entry's author record on entry\n * templates (`expandCollectionEntryAuthor`), exactly like Entry Meta's\n * fields; set it — or bind `{{entry.author}}` — only to override.\n */\n name?: string\n /**\n * The author's blurb, from their record's `bio`; bind\n * `{{entry.authorBio}}` to override.\n */\n bio?: string\n /**\n * Portrait or logo — a media reference (AGL-1215) or any URL. Server-filled\n * from the author record, so the face changes with the byline instead of\n * being picked once on the template the way Entry Meta's avatar is.\n */\n image?: string\n /** The author's own site; offered as a link row beside the profile ones. */\n url?: string\n /**\n * The author's page on THIS site (AGL-2519), server-filled from the routed\n * entry. The name links here.\n *\n * It wins over {@link url} for the NAME, and the ordering is the point: a\n * reader who clicks a byline at the end of an article is asking \"what else\n * has this person written here\", not \"take me off this site\". Their own\n * url is still reachable — it is one of the link rows — so nothing is lost\n * by giving the name the destination that answers the question asked.\n */\n pageUrl?: string\n /**\n * The author's profile links, server-filled from their record (AGL-2516).\n *\n * Not authorable as text: each row carries a platform or a picked icon, and\n * the console's author editor is where those are chosen. `Show links` is\n * the template's control over them.\n */\n links?: Aglyn.ContentAuthorLink[]\n showBio?: boolean\n showAvatar?: boolean\n showLinks?: boolean\n}\n\n/** The card's portrait, larger than the byline's 36px mark (Figma 170:190). */\nconst AUTHOR_AVATAR_SIZE = 48\n\n/**\n * The mark for each platform the author model knows (AGL-2516).\n *\n * Imported rather than resolved through the icon catalog, because this set is\n * CLOSED and known at build time — the catalog is ~2.9 MB and only picker\n * surfaces load it, so a renderer that reached for it to draw ten glyphs\n * would ship the whole thing to every visitor of every article.\n *\n * Keyed by the registry's `icon`, so adding a platform is one entry there and\n * one import here, and a platform whose mark was never imported falls through\n * to the generic link glyph rather than rendering nothing.\n */\nconst AUTHOR_LINK_ICON_PATHS: Record<string, string> = {\n twitter: mdiTwitter.path,\n linkedin: mdiLinkedin.path,\n github: mdiGithub.path,\n mastodon: mdiMastodon.path,\n youtube: mdiYoutube.path,\n instagram: mdiInstagram.path,\n facebook: mdiFacebook.path,\n web: mdiWeb.path,\n email: mdiEmail.path,\n rss: mdiRss.path,\n}\n\n/**\n * What one link row draws.\n *\n * A known platform takes its mark from the table above. A CUSTOM link takes\n * the path the console stored beside the picked id (AGL-1212) — and falls back\n * to a generic link glyph rather than to nothing, because a row with a label\n * and a url is still a working link whose icon simply failed to resolve.\n */\nfunction authorLinkIconPath(link: Aglyn.ContentAuthorLink): string {\n const platform = Aglyn.authorLinkPlatform(link.platform)\n if (platform) {\n return AUTHOR_LINK_ICON_PATHS[platform.icon] ?? mdiLinkVariant.path\n }\n return (link.iconPath ?? '').trim() || mdiLinkVariant.path\n}\n\n/**\n * Schemes a rendered author link may use — the model's own guard, restated at\n * the boundary that produces the `href`.\n *\n * `normalizeContentAuthorLinks` already drops everything else, and this is\n * deliberately not trusting that: the props on this component are authorable,\n * so a link can reach the renderer without ever passing through the store's\n * normalizer. Two checks, because only one of them is on the path an attacker\n * would use.\n */\nconst SAFE_AUTHOR_LINK_HREF = /^(https:\\/\\/|mailto:)/i\n\n/**\n * What the byline may link to — an absolute `https:` page, or a route on this\n * site. The Social Links block guards its hrefs the same way and for the same\n * reason: the value comes from a stored record, so `javascript:` and friends\n * have to be unreachable rather than merely unlikely.\n */\nconst SAFE_AUTHOR_HREF = /^(https:\\/\\/|\\/(?!\\/))/i\n\n/** An off-site author page opens in a new tab; a route on this site does not. */\nconst EXTERNAL_AUTHOR_HREF = /^https:\\/\\//i\n\n/**\n * The author's profile links as a row of marks (AGL-2516).\n *\n * Shared by the article's Entry Author card and the Author Profile block on\n * the author's own page (AGL-2518) — the one part of the two that would\n * genuinely drift, since an icon row is where the accessible-name rule and\n * the `rel` on an off-site profile live.\n *\n * Renders nothing for an empty list, so a caller can drop it in\n * unconditionally rather than guarding at every call site.\n */\nfunction AuthorLinkRows(props: {\n links: readonly Aglyn.ContentAuthorLink[]\n /** Glyph scale; the profile draws them a step larger than the card. */\n size?: number\n}) {\n if (!props.links.length) return null\n return (\n <MuiStack direction=\"row\" spacing={0.5} sx={{ flexWrap: 'wrap', ml: -1 }}>\n {props.links.map((link, index) => {\n const label = Aglyn.authorLinkLabel(link)\n return (\n <AppLink\n key={index}\n componentVariant=\"icon-button\"\n href={(link.url ?? '').trim()}\n // The name IS the icon here, so it has to be announced — a row of\n // unlabelled icon buttons is a row a screen reader reads as\n // \"link, link, link\".\n aria-label={label}\n title={label}\n size=\"small\"\n sx={{ color: 'text.secondary' }}\n // A profile lives off this site by definition, and `mailto:` must\n // not replace the article either.\n target=\"_blank\"\n rel=\"noopener noreferrer me\"\n >\n <MdiIcon path={authorLinkIconPath(link)} size={props.size ?? 0.85} />\n </AppLink>\n )\n })}\n </MuiStack>\n )\n}\n\n/**\n * The rows a card will actually draw, guarded at the boundary.\n *\n * These arrive as PROPS, so a link can reach a renderer without ever passing\n * through the store's normalizer — the reason the byline's own `url` is\n * checked twice as well.\n */\nfunction safeAuthorLinks(\n links: readonly Aglyn.ContentAuthorLink[] | undefined,\n show: boolean | undefined,\n): Aglyn.ContentAuthorLink[] {\n if (show === false) return []\n return (links ?? []).filter((link) =>\n SAFE_AUTHOR_LINK_HREF.test((link?.url ?? '').trim()),\n )\n}\n\n/**\n * The author card that closes an article (AGL-2486) — portrait, byline, bio.\n *\n * Entry Meta prints a NAME beside a date, which is all an entry carried when\n * it was written: one free-typed string. Custom authors made the byline a\n * record with a portrait, a bio and a url, and a template that wanted the\n * card the article frame draws still had to type the name and the blurb in as\n * literal text — text that keeps saying whatever it said under posts somebody\n * else wrote, and that no edit to the author record can ever reach. This is\n * that card, filled from the record.\n *\n * Every field is independently overridable and every one of them collapses\n * when empty: an author with no portrait renders the text alone rather than a\n * gap where a face would be, and an entry with no author at all renders\n * nothing — never an empty bordered box.\n */\nconst CollectionEntryAuthor = forwardRef<\n HTMLDivElement,\n CollectionEntryAuthorProps\n>((props, ref) => {\n // None of these are DOM attributes (`name` least of all), so they are\n // destructured rather than spread — the stack.ts pattern.\n const {\n name,\n bio,\n image,\n url,\n pageUrl,\n links,\n showBio,\n showAvatar,\n showLinks,\n ...rest\n } = props\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n const { hostId } = Aglyn.useSite()\n const nameValue = metaValue(name, suppressNavigation)\n const bioValue = showBio !== false ? metaValue(bio, suppressNavigation) : ''\n // Their page here first, their own site second (AGL-2519) — see `pageUrl`.\n const hrefRaw = metaValue(pageUrl, suppressNavigation) ||\n metaValue(url, suppressNavigation)\n const urlValue = SAFE_AUTHOR_HREF.test(hrefRaw) ? hrefRaw : ''\n // An unresolved token empties on EVERY surface, as in the byline: a literal\n // `{{…}}` in a src is a broken portrait in the canvas.\n const imageRaw = (image ?? '').trim()\n const imageSrc =\n showAvatar !== false && imageRaw && !UNRESOLVED_TOKEN.test(imageRaw)\n ? Aglyn.resolveMediaSrc(imageRaw, { hostId })\n : ''\n // Nothing to show and nothing to route from: an editing surface previews\n // the card's own markup at the author's own settings, and a published page\n // renders nothing at all (AGL-2486). Every part still answers its own\n // switch, so a card with Show bio off previews as portrait and name.\n const sample = !nameValue && !bioValue && !imageSrc && Boolean(suppressNavigation)\n const displayName = sample ? ENTRY_SAMPLE_AUTHOR : nameValue\n const displayBio = sample && showBio !== false ? ENTRY_SAMPLE_BIO : bioValue\n // The record's portrait arrives at compose time, so there is none to\n // resolve on the canvas — the card draws its slot, which is the thing its\n // spacing is built around.\n const samplePortrait = sample && showAvatar !== false\n /**\n * The rows this card will actually draw (AGL-2516).\n *\n * Guarded here rather than trusted from the store: these arrive as PROPS,\n * so a link can reach the renderer without passing the record normalizer,\n * and `javascript:` has to be unreachable rather than merely unlikely — the\n * same reason the byline's own `url` is checked twice.\n */\n const linkRows = safeAuthorLinks(links, showLinks)\n if (\n !displayName &&\n !displayBio &&\n !imageSrc &&\n !samplePortrait &&\n !linkRows.length\n ) {\n return <Box ref={ref} {...rest} />\n }\n return (\n <MuiStack\n ref={ref}\n direction=\"row\"\n spacing={2}\n {...rest}\n // MERGE, never replace (AGL-1450): `rest.sx` is the array leaf.tsx\n // builds, and spreading it into an object drops every authored value.\n sx={[\n {\n alignItems: 'flex-start',\n p: 2.5,\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n },\n ...nodeSx,\n ]}\n >\n {imageSrc ? (\n <Box\n component=\"img\"\n src={imageSrc}\n // Decorative: the card names the author in text right beside it.\n alt=\"\"\n sx={{\n display: 'block',\n flexShrink: 0,\n width: AUTHOR_AVATAR_SIZE,\n height: AUTHOR_AVATAR_SIZE,\n borderRadius: '50%',\n objectFit: 'cover',\n }}\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n ) : samplePortrait ? (\n <Box aria-hidden sx={samplePortraitSx(AUTHOR_AVATAR_SIZE)} />\n ) : null}\n <MuiStack spacing={0.5} sx={{ minWidth: 0 }}>\n {displayName ? (\n <Typography component=\"p\" variant=\"subtitle2\">\n {urlValue ? (\n <AppLink\n href={urlValue}\n sx={{ color: 'inherit' }}\n underline=\"hover\"\n {...(EXTERNAL_AUTHOR_HREF.test(urlValue)\n ? { target: '_blank', rel: 'noopener noreferrer' }\n : {})}\n >\n {displayName}\n </AppLink>\n ) : (\n displayName\n )}\n </Typography>\n ) : null}\n {displayBio ? (\n <Typography variant=\"body2\" sx={{ color: 'text.secondary' }}>\n {displayBio}\n </Typography>\n ) : null}\n <AuthorLinkRows links={linkRows} />\n </MuiStack>\n </MuiStack>\n )\n})\nCollectionEntryAuthor.displayName = 'AglynCollectionEntryAuthor'\n\nexport const collectionEntryAuthorSchema: Aglyn.ComponentSchema<CollectionEntryAuthorProps> =\n {\n $id: ENTRY_AUTHOR_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Entry Author',\n description: 'The author card for an entry — portrait, byline and bio.',\n category: Aglyn.ComponentCategory.TEXT,\n icon: {\n path: mdiAccountCircleOutline.path,\n sx: { color: 'secondary.main' },\n },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'name',\n label: 'Name',\n description:\n 'Blank shows the name from the entry’s author record. Type here ' +\n '(or bind {{entry.author}}) to sign this card differently from ' +\n 'the byline above the article.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'bio',\n label: 'Bio',\n description:\n 'Blank shows the bio from the author’s record. Type here (or bind ' +\n '{{entry.authorBio}}) only to override it.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n {\n name: 'image',\n label: 'Portrait',\n description:\n 'Blank shows the portrait from the author’s record. Pick from your ' +\n 'media library with \"Browse media\" only to override it.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'pageUrl',\n label: 'Name links to',\n description:\n 'Blank sends the name to the author’s page on this site — ' +\n 'everything they wrote. Falls back to their own url when they have ' +\n 'no page; type here to override both.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'url',\n label: 'Their own site',\n description:\n 'Blank uses the url on the author’s record. Used only when they ' +\n 'have no page on this site for the name to link to.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'showLinks',\n label: 'Show links',\n description:\n 'Off hides the author’s profile links. They come from the author ' +\n 'record — a known platform brings its own mark, and a custom link ' +\n 'brings the icon and label chosen there.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to open\n // in that position or it lies about the card in front of the author\n // (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showBio',\n label: 'Show bio',\n description:\n 'Off leaves the card as a portrait and a name — for a masthead ' +\n 'where the blurb would repeat what the page already says.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showAvatar',\n label: 'Show portrait',\n description:\n 'Off leaves the text alone, with no space held for a picture.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n ],\n }\n\n/* ── Author profile (AGL-2518) ──────────────────────────────────────────── */\n\nexport interface ContentAuthorProfileProps extends StackProps {\n /**\n * The byline. Server-filled from the routed author on their own page\n * (`expandContentAuthorProfile`); set it — or bind `{{author.name}}` —\n * only to override.\n */\n name?: string\n /** The blurb from their record; bind `{{author.bio}}` to override. */\n bio?: string\n /**\n * Portrait or logo — a media reference (AGL-1215) or any URL, filled from\n * the record so the face follows the person rather than the template.\n */\n image?: string\n /** Person only — what they do; blank on an Organization. */\n jobTitle?: string\n /** Person only — who they write for. */\n worksFor?: string\n /**\n * Their OWN site. The name links here; blank renders it as plain text.\n * This block already IS the author's page, so it does not link to itself.\n */\n url?: string\n /** Their profile links, server-filled from the record (AGL-2516). */\n links?: Aglyn.ContentAuthorLink[]\n showBio?: boolean\n showAvatar?: boolean\n showRole?: boolean\n showLinks?: boolean\n}\n\n/** The page subject's portrait — a step up from the card's 48px. */\nconst PROFILE_AVATAR_SIZE = 96\n\n/**\n * Who an author page is about (AGL-2518) — portrait, name, role, bio, links.\n *\n * The Entry Author card renders the same person as a footnote under an\n * article. This renders them as the SUBJECT of a page, which is why it has\n * the two fields that card has no room for: `jobTitle` and `worksFor` are\n * what tell a stranger who they are looking at, and they are already on the\n * record because `schema.org/Person` defines them (AGL-2486).\n *\n * Every field is independently overridable and every one collapses when\n * empty, exactly as the card's do — an author with no portrait renders the\n * text alone rather than a gap where a face would be. On an editing surface\n * the block previews its own shape with sample text, because a template is\n * designed before any author is routed through it.\n */\nconst ContentAuthorProfile = forwardRef<\n HTMLDivElement,\n ContentAuthorProfileProps\n>((props, ref) => {\n // None of these are DOM attributes (`name` least of all), so they are\n // destructured rather than spread — the stack.ts pattern.\n const {\n name,\n bio,\n image,\n jobTitle,\n worksFor,\n url,\n links,\n showBio,\n showAvatar,\n showRole,\n showLinks,\n ...rest\n } = props\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n const { hostId } = Aglyn.useSite()\n const nameValue = metaValue(name, suppressNavigation)\n const bioValue = showBio !== false ? metaValue(bio, suppressNavigation) : ''\n const jobValue = showRole !== false ? metaValue(jobTitle, suppressNavigation) : ''\n const worksForValue =\n showRole !== false ? metaValue(worksFor, suppressNavigation) : ''\n const urlRaw = metaValue(url, suppressNavigation)\n const urlValue = SAFE_AUTHOR_HREF.test(urlRaw) ? urlRaw : ''\n // An unresolved token empties on EVERY surface, as in the byline: a literal\n // `{{…}}` in a src is a broken portrait in the canvas.\n const imageRaw = (image ?? '').trim()\n const imageSrc =\n showAvatar !== false && imageRaw && !UNRESOLVED_TOKEN.test(imageRaw)\n ? Aglyn.resolveMediaSrc(imageRaw, { hostId })\n : ''\n const linkRows = safeAuthorLinks(links, showLinks)\n // Nothing routed and nothing typed: an editing surface previews the block's\n // own markup at its own settings, and a published page renders nothing at\n // all. Every part still answers its own switch, so a block with Show bio\n // off previews as portrait and name.\n const sample =\n !nameValue &&\n !bioValue &&\n !jobValue &&\n !imageSrc &&\n !linkRows.length &&\n Boolean(suppressNavigation)\n const displayName = sample ? ENTRY_SAMPLE_AUTHOR : nameValue\n const displayBio = sample && showBio !== false ? ENTRY_SAMPLE_BIO : bioValue\n const samplePortrait = sample && showAvatar !== false\n /**\n * \"Founder at Aglyn\" — one line, from two fields, and a middle dot would be\n * wrong here: this is a phrase, not a list of facts like the byline's\n * `name · date`. Either field alone still reads.\n */\n const roleLine = [jobValue, worksForValue].filter(Boolean).join(' at ')\n if (\n !displayName &&\n !displayBio &&\n !roleLine &&\n !imageSrc &&\n !samplePortrait &&\n !linkRows.length\n ) {\n return <Box ref={ref} {...rest} />\n }\n return (\n <MuiStack\n ref={ref}\n direction=\"row\"\n spacing={3}\n {...rest}\n // MERGE, never replace (AGL-1450): `rest.sx` is the array leaf.tsx\n // builds, and spreading it into an object drops every authored value.\n sx={[{ alignItems: 'flex-start' }, ...nodeSx]}\n >\n {imageSrc ? (\n <Box\n component=\"img\"\n src={imageSrc}\n // Decorative: the block names the author in text right beside it.\n alt=\"\"\n sx={{\n display: 'block',\n flexShrink: 0,\n width: PROFILE_AVATAR_SIZE,\n height: PROFILE_AVATAR_SIZE,\n borderRadius: '50%',\n objectFit: 'cover',\n }}\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n ) : samplePortrait ? (\n <Box aria-hidden sx={samplePortraitSx(PROFILE_AVATAR_SIZE)} />\n ) : null}\n <MuiStack spacing={1} sx={{ minWidth: 0 }}>\n {displayName ? (\n // `h1` because the author IS this page's subject — the one heading\n // rule a built-in body has to get right, since the template around\n // it cannot know what the block will be handed.\n <Typography component=\"h1\" variant=\"h4\">\n {urlValue ? (\n <AppLink\n href={urlValue}\n sx={{ color: 'inherit' }}\n underline=\"hover\"\n {...(EXTERNAL_AUTHOR_HREF.test(urlValue)\n ? { target: '_blank', rel: 'noopener noreferrer' }\n : {})}\n >\n {displayName}\n </AppLink>\n ) : (\n displayName\n )}\n </Typography>\n ) : null}\n {roleLine ? (\n <Typography variant=\"subtitle2\" sx={{ color: 'text.secondary' }}>\n {roleLine}\n </Typography>\n ) : null}\n {displayBio ? (\n <Typography variant=\"body1\" sx={{ color: 'text.secondary' }}>\n {displayBio}\n </Typography>\n ) : null}\n <AuthorLinkRows links={linkRows} size={1} />\n </MuiStack>\n </MuiStack>\n )\n})\nContentAuthorProfile.displayName = 'AglynContentAuthorProfile'\n\nexport { ContentAuthorProfile }\n\nexport const contentAuthorProfileSchema: Aglyn.ComponentSchema<ContentAuthorProfileProps> =\n {\n $id: AUTHOR_PROFILE_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Author Profile',\n description:\n 'Who an author page is about — portrait, name, role, bio and links.',\n category: Aglyn.ComponentCategory.TEXT,\n icon: {\n path: mdiAccountCircleOutline.path,\n sx: { color: 'secondary.main' },\n },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'name',\n label: 'Name',\n description:\n 'Blank shows the name of the author whose page this is. Type here ' +\n '(or bind {{author.name}}) only to override it.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'bio',\n label: 'Bio',\n description:\n 'Blank shows the bio from the author’s record. Type here (or bind ' +\n '{{author.bio}}) only to override it.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n {\n name: 'image',\n label: 'Portrait',\n description:\n 'Blank shows the portrait of the author whose page this is, at the ' +\n 'larger size a profile takes. Pick from your media library only to ' +\n 'override it.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'jobTitle',\n label: 'Role',\n description:\n 'Blank shows the job title from the author’s record. Only a person ' +\n 'has one — an author record set to Organization leaves it empty.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'worksFor',\n label: 'Organization',\n description:\n 'Blank shows who the author writes for, from their record. Shown ' +\n 'after the role as \"Role at Organization\".',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'url',\n label: 'Link',\n description:\n 'Blank uses the author’s own url. The name links here; with no url ' +\n 'it renders as plain text. This block is already the author’s page ' +\n 'on this site, so it never links to itself.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'showLinks',\n label: 'Show links',\n description:\n 'Off hides the row of profile links under the bio — the place a ' +\n 'reader follows this author elsewhere. Turning it off on a page ' +\n 'about them is rarely what you want.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to open\n // in that position or it lies about the block in front of the author\n // (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showBio',\n label: 'Show bio',\n description:\n 'Off leaves the block as a portrait, a name and a role.',\n component: Aglyn.FieldComponentType.SWITCH,\n initialValue: true,\n },\n {\n name: 'showRole',\n label: 'Show role',\n description:\n 'Off hides the \"Role at Organization\" line under the name.',\n component: Aglyn.FieldComponentType.SWITCH,\n initialValue: true,\n },\n {\n name: 'showAvatar',\n label: 'Show portrait',\n description:\n 'Off drops the portrait and lets the name and bio run the full ' +\n 'width, with no space held for a picture.',\n component: Aglyn.FieldComponentType.SWITCH,\n initialValue: true,\n },\n ],\n }\n\n/* ── Category pills (AGL-1321) ──────────────────────────────────────────── */\n\nexport interface CollectionCategoriesProps extends StackProps {\n /**\n * Collection whose taxonomy renders (compose-time). Blank = the collection\n * routed by the current URL on list-template screens.\n */\n collectionSlug?: string\n /**\n * Label for the unfiltered pill (default \"All\"). Clearing the field\n * persists `Aglyn.COLLECTION_ALL_PILL_NONE`, which omits the pill —\n * `''` cannot be stored at all (AGL-1336).\n */\n allLabel?: string\n /**\n * Server-stamped pill links (`expandCollectionCategories`); never set by\n * hand — the tenant builds them from the collection's categories and the\n * routed category.\n */\n items?: Aglyn.CollectionCategoryLink[]\n}\n\n/** Pill look, in palette tokens so it follows the site theme in both modes. */\nconst pillSx = (active: boolean) => ({\n px: 1.75,\n py: 0.75,\n borderRadius: 999,\n border: '1px solid',\n borderColor: active ? 'primary.main' : 'divider',\n bgcolor: active ? 'primary.main' : 'transparent',\n color: active ? 'primary.contrastText' : 'text.secondary',\n fontSize: 14,\n lineHeight: 1.2,\n whiteSpace: 'nowrap',\n '&:hover': {\n borderColor: active ? 'primary.main' : 'text.primary',\n color: active ? 'primary.contrastText' : 'text.primary',\n },\n})\n\n/**\n * The category filter row for a collection listing (AGL-1321): \"All\" plus one\n * pill per category, each a REAL anchor to `/{collection}/category/{slug}`.\n *\n * Anchors, not click handlers, and that is the whole design. A JS-only toggle\n * would be invisible to crawlers, unopenable in a new tab, unlinkable, and\n * unreachable without hydration — and it could not exist anyway, since the\n * filter is resolved server-side before the page is composed. The pill the\n * current URL selected carries `aria-current=\"page\"`, stamped on the server so\n * it is in the HTML rather than derived after hydration.\n */\nconst CollectionCategories = forwardRef<\n HTMLDivElement,\n CollectionCategoriesProps\n>((props, ref) => {\n // `collectionSlug`/`allLabel` are compose-time: the tenant resolves them\n // while stamping `items`; strip so they never hit the DOM.\n const { collectionSlug, allLabel, items, ...rest } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n if (!items?.length) {\n if (!suppressNavigation) return <Box ref={ref} {...rest} />\n return (\n <Box\n ref={ref}\n {...rest}\n sx={[\n {\n p: 2,\n border: '1px dashed',\n borderColor: 'divider',\n color: 'text.secondary',\n fontSize: 12,\n fontFamily: 'system-ui, sans-serif',\n },\n ...nodeSx,\n ]}\n >\n {'Category pills — All + one pill per collection category render here'}\n </Box>\n )\n }\n return (\n <MuiStack\n ref={ref}\n direction=\"row\"\n spacing={1}\n {...rest}\n // MERGE, never replace (AGL-1450) — see Entry Meta above.\n sx={[{ flexWrap: 'wrap', rowGap: 1 }, ...nodeSx]}\n >\n {items.map((item) =>\n // Editing surfaces render the pill look without an href, so a click\n // in the besigner never navigates the canvas away.\n suppressNavigation ? (\n <Box key={item.href} sx={pillSx(item.active)}>\n {item.label}\n </Box>\n ) : (\n <AppLink\n key={item.href}\n href={item.href}\n underline=\"none\"\n aria-current={item.active ? 'page' : undefined}\n sx={pillSx(item.active)}\n >\n {item.label}\n </AppLink>\n ),\n )}\n </MuiStack>\n )\n})\nCollectionCategories.displayName = 'AglynCollectionCategories'\n\nexport const collectionCategoriesSchema: Aglyn.ComponentSchema<CollectionCategoriesProps> =\n {\n $id: CATEGORIES_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Category Pills',\n description: 'A pill per collection category, each filtering the listing.',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiTagMultipleOutline.path, sx: { color: 'secondary.main' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'collectionSlug',\n label: 'Collection slug',\n description:\n 'Content collection whose categories render as pills (e.g. ' +\n '\"blog\"). Leave blank on a list-template screen to use the ' +\n 'collection from the URL.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'allLabel',\n label: 'All label',\n description:\n 'Label for the unfiltered pill, which links to the collection ' +\n 'root (default \"All\"). Clear the box to omit that pill — it then ' +\n 'reads \"none\", which is what makes the omission stick. Typing ' +\n '\"none\" does the same thing.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n // Without this the emptied field persists NOTHING (AGL-1336): ddf\n // maps an emptied value to `clearedValue`, final-form's parse turns\n // `''` into `undefined`, and the missing key takes the runtime\n // default — so \"clear it to omit that pill\" was undoable by\n // clicking. Same shape as FIELD_COLOR_ALT1's `'default'` (AGL-1191):\n // a value that means something has to BE something.\n //\n // ddf only substitutes `clearedValue` when the field HAD an initial\n // value (`enhancedOnChange`'s `typeof initial !== 'undefined'`),\n // which is why the preset below seeds `allLabel` and why the\n // sentinel is a word an author can also type. A block that never\n // carried the prop has an already-empty box and nothing to clear.\n clearedValue: Aglyn.COLLECTION_ALL_PILL_NONE,\n },\n ],\n }\n\n/* ── Collection Search (AGL-1516, Figma 494:1220) ───────────────────────── */\n\nexport interface CollectionSearchProps extends StackProps {\n /**\n * Collection whose entries this box searches (compose-time). Blank = the\n * collection routed by the current URL on list-template screens.\n */\n collectionSlug?: string\n /** Hint text inside the box (blank = \"Search posts…\"). */\n searchPlaceholder?: string\n /**\n * Server-stamped matchable text per entry (`expandCollectionSearch`);\n * never set by hand.\n */\n searchIndex?: Aglyn.CollectionEntrySearchItem[]\n /** How many entries the index was drawn from; never set by hand. */\n searchTotal?: number\n /** Whether that read reached its bound; never set by hand. */\n searchCapped?: boolean\n}\n\n/**\n * A search box for a collection that lives where the author puts it\n * (AGL-1516, Figma 494:1220).\n *\n * The entries block has had a search box since AGL-1516's first half, and it\n * renders inside that block — first child of its own stack. The frame puts\n * the field in the listing's TOOLBAR: category pills on the left, search and\n * RSS on the right, one row. That was unbuildable, and unbuildable in both\n * directions — a block's own child cannot be lifted out of it, and the pills\n * cannot be moved in, because every child of an entries block is cloned once\n * per entry. Three passes of authoring hit the same wall.\n *\n * So the field became its own block. It searches the collection the listing\n * beside it is drawn from, and answers with the suggestion panel (496:1218)\n * — the only behaviour a standalone box can honestly have, since it owns no\n * cards to hide. Enter submits to the site-wide results page, so the box\n * works with no JavaScript at all.\n *\n * Inert on editing surfaces, like Category Pills: the besigner has no index\n * to search, and a field that answered there would be answering about\n * nothing.\n */\nconst CollectionSearch = forwardRef<HTMLDivElement, CollectionSearchProps>(\n (props, ref) => {\n // Compose-time and search-only attributes: strip so they never hit the\n // DOM.\n const {\n collectionSlug,\n searchPlaceholder,\n searchIndex,\n searchTotal,\n searchCapped,\n ...rest\n } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n const fuzzy = useMemo(\n () =>\n searchIndex?.length\n ? new Fuse(searchIndex, { ...Aglyn.COLLECTION_SEARCH_FUSE_OPTIONS })\n : null,\n [searchIndex],\n )\n if (!suppressNavigation && !fuzzy) {\n // Nothing stamped on a live surface: an unknown collection, an empty\n // one, or a page composed before this block existed. Renders NOTHING\n // rather than a field that can only ever answer \"no matches\" — that\n // answer would read as a fact about the reader's query instead of\n // about the absent index behind it.\n return <Box ref={ref} {...rest} />\n }\n return (\n <MuiStack ref={ref} {...rest} sx={[{ alignItems: 'flex-end' }, ...nodeSx]}>\n <SuggestSearchBox\n fuzzy={fuzzy}\n {...(searchIndex ? { items: searchIndex } : {})}\n {...(searchPlaceholder === undefined\n ? {}\n : { placeholder: searchPlaceholder })}\n {...(suppressNavigation ? { inert: true } : {})}\n // The honest miss for THIS box. It searched one bounded read of the\n // collection — never \"the collection\", and on a category route not\n // even the whole of it, because the source arrives filtered. So it\n // reports the number it actually looked through, and says plainly\n // when that number was a ceiling rather than a total.\n emptyText={(text) =>\n searchCapped\n ? `No matches for “${text}” in the ${searchIndex?.length ?? 0} ` +\n 'entries searched here — the collection holds more, which ' +\n 'this box has not read.'\n : `No matches for “${text}” in the ${searchIndex?.length ?? 0} ` +\n 'entries searched here.'\n }\n />\n </MuiStack>\n )\n },\n)\nCollectionSearch.displayName = 'AglynCollectionSearch'\n\nexport const collectionSearchSchema: Aglyn.ComponentSchema<CollectionSearchProps> =\n {\n $id: SEARCH_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Collection Search',\n description:\n 'A search box for a content collection, with a suggestions dropdown.',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiMagnify.path, sx: { color: 'secondary.main' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'collectionSlug',\n label: 'Collection slug',\n description:\n 'Content collection this box searches (e.g. \"blog\"). Leave blank ' +\n 'on a list-template screen to use the collection from the URL.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'searchPlaceholder',\n label: 'Search placeholder',\n description:\n 'Hint text inside this box. Blank shows \"Search posts…\", which is ' +\n 'worth changing when the collection is not posts.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n // `searchIndex`, `searchTotal` and `searchCapped` are deliberately NOT\n // attributes: all three are server-stamped facts about a read, and an\n // author who could edit them could make the box lie about its own\n // reach.\n ],\n }\n\n/* ── Presets ────────────────────────────────────────────────────────────── */\n\n/**\n * Styling rides the node's own `sx`, never `props.sx` (AGL-1346): both\n * render, but only `node.sx` is the record the Styles panel can edit or\n * clear. `Leaf` composes `node.sx` last, so this is the same result.\n */\nconst entryText = (variant: string, children: string, extra?: object) => {\n const { sx, ...props } = (extra ?? {}) as Record<string, unknown>\n return {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: { variant, children, ...props },\n ...(sx ? { sx } : {}),\n }\n}\n\nexport const collectionPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ENTRIES_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Collection Entries',\n pluginId: BUNDLE_ID,\n description:\n 'Repeats a card (title, date, excerpt, Read more) per published ' +\n 'entry of a content collection',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiPostOutline.path, sx: { color: 'secondary.main' } },\n data: {\n $id: null,\n componentId: ENTRIES_ID,\n pluginId: BUNDLE_ID,\n props: { spacing: 4 },\n nodes: [\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { spacing: 0.5 },\n nodes: [\n entryText('h5', '{{entry.title}}', { component: 'h2' }),\n entryText('caption', '{{entry.date}}', {\n sx: { color: 'text.secondary' },\n }),\n entryText('body1', '{{entry.excerpt}}'),\n {\n $id: null,\n componentId: 'muiScreenLink',\n pluginId: BUNDLE_ID,\n props: {\n children: 'Read more',\n href: '{{entry.url}}',\n size: 'small',\n },\n sx: { alignSelf: 'flex-start' },\n },\n ],\n },\n ],\n },\n },\n {\n $id: generatePresetId(ENTRY_BODY_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Entry Body',\n pluginId: BUNDLE_ID,\n description: \"Renders the current entry's markdown body, themed\",\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiTextLong.path, sx: { color: 'secondary.main' } },\n data: {\n $id: null,\n componentId: ENTRY_BODY_ID,\n pluginId: BUNDLE_ID,\n props: { markdown: '{{entry.body}}' },\n },\n },\n {\n $id: generatePresetId(RELATED_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Related Posts',\n pluginId: BUNDLE_ID,\n description:\n \"Other entries sharing the current entry's category or tags\",\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiNewspaperVariantOutline.path, sx: { color: 'secondary.main' } },\n data: {\n $id: null,\n componentId: RELATED_ID,\n pluginId: BUNDLE_ID,\n // `layout`, the two type steps and `dateFormat` are seeded rather than\n // left to the runtime fallback so each dropdown opens on the value the\n // block is actually rendering, and an author who tries another has a\n // named route back (AGL-1457/AGL-1459). The `show*` switches are\n // deliberately absent: their unset state IS the shipped behaviour.\n props: {\n heading: 'Related articles',\n limit: 3,\n layout: 'list',\n headingVariant: 'h5',\n titleVariant: 'subtitle1',\n dateFormat: Aglyn.COLLECTION_ENTRY_DATE_FORMAT_DEFAULT,\n },\n },\n },\n {\n $id: generatePresetId(SHARE_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Share Bar',\n pluginId: BUNDLE_ID,\n description: 'X, LinkedIn, Facebook, and copy-link buttons for the page',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiShareVariant.path, sx: { color: 'secondary.main' } },\n data: {\n $id: null,\n componentId: SHARE_ID,\n pluginId: BUNDLE_ID,\n props: { heading: 'Share' },\n },\n },\n {\n $id: generatePresetId(CATEGORIES_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Category Pills',\n pluginId: BUNDLE_ID,\n description:\n \"Links to each of the collection's categories, filtering the listing\",\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiTagMultipleOutline.path, sx: { color: 'secondary.main' } },\n data: {\n $id: null,\n componentId: CATEGORIES_ID,\n pluginId: BUNDLE_ID,\n // Seeded, not left to the runtime default: an explicit initial value\n // is what lets the attributes form substitute the cleared sentinel\n // when the author empties the box (AGL-1336).\n props: { allLabel: Aglyn.COLLECTION_ALL_PILL_DEFAULT },\n },\n },\n {\n // Every author-placeable component needs a preset: the element drawer is\n // built from PRESETS alone (`ComponentManager.schemasByCategory` iterates\n // `this.presets`), so a component with only a schema renders correctly\n // once a node exists but can never be put on a canvas by clicking.\n //\n // This one earns its place in the drawer because the entries block's own\n // search field cannot leave that block, and the toolbar row wants the\n // search beside the category pills rather than above the entries.\n $id: generatePresetId(SEARCH_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Collection Search',\n pluginId: BUNDLE_ID,\n description:\n 'Search box for a collection, with a dropdown of matching entries',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiMagnify.path, sx: { color: 'secondary.main' } },\n data: {\n $id: null,\n componentId: SEARCH_ID,\n pluginId: BUNDLE_ID,\n // Deliberately no seeded props. Both attributes read blank as a real\n // choice — `collectionSlug` blank means \"take the collection from the\n // URL\", which is correct on the list templates this block is for, and\n // `searchPlaceholder` blank means the designed \"Search posts…\".\n props: {},\n },\n },\n {\n $id: generatePresetId(ENTRY_META_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Entry Meta',\n pluginId: BUNDLE_ID,\n description: 'Author · date · category line with tag chips for the entry',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiTagOutline.path, sx: { color: 'secondary.main' } },\n data: {\n $id: null,\n componentId: ENTRY_META_ID,\n pluginId: BUNDLE_ID,\n props: {\n date: '{{entry.date}}',\n // Named rather than left to the runtime fallback (AGL-1459): the\n // dropdown opens on the value the block is actually rendering, and\n // an author who tries a format has a named route back. `default` is\n // also the fallback, so naming it is the same choice, not a second.\n dateFormat: Aglyn.COLLECTION_ENTRY_DATE_FORMAT_DEFAULT,\n author: '{{entry.author}}',\n category: '{{entry.category}}',\n tags: '{{entry.tags}}',\n },\n },\n },\n {\n $id: generatePresetId(ENTRY_AUTHOR_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Entry Author',\n pluginId: BUNDLE_ID,\n description: 'Portrait, byline and bio card for the entry’s author',\n category: Aglyn.ComponentCategory.TEXT,\n icon: {\n path: mdiAccountCircleOutline.path,\n sx: { color: 'secondary.main' },\n },\n data: {\n $id: null,\n componentId: ENTRY_AUTHOR_ID,\n pluginId: BUNDLE_ID,\n // Seeded EMPTY, unlike Entry Meta's preset. Those bindings predate the\n // server fill and stay for compatibility; here the fill is the only\n // mechanism, and a seeded `{{entry.authorImage}}` would render a broken\n // portrait on every surface that is not an entry template.\n props: {},\n },\n },\n {\n $id: generatePresetId(AUTHOR_PROFILE_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Author Profile',\n pluginId: BUNDLE_ID,\n description: 'Portrait, name, role, bio and links for an author page',\n category: Aglyn.ComponentCategory.TEXT,\n icon: {\n path: mdiAccountCircleOutline.path,\n sx: { color: 'secondary.main' },\n },\n data: {\n $id: null,\n componentId: AUTHOR_PROFILE_ID,\n pluginId: BUNDLE_ID,\n // Seeded EMPTY, for the reason the card above it is: the server fill is\n // the only mechanism here, and a seeded `{{author.image}}` would render\n // a broken portrait on every surface that is not an author page.\n props: {},\n },\n },\n]\n\nexport {\n CollectionCategories,\n CollectionEntries,\n CollectionEntryAuthor,\n CollectionEntryBody,\n CollectionEntryMeta,\n CollectionRelated,\n CollectionSearch,\n CollectionShare,\n}\nexport default CollectionEntries\n"],"names":["Aglyn","mdiAccountCircleOutline","mdiContentCopy","mdiEmail","mdiFacebook","mdiGithub","mdiInstagram","mdiLinkVariant","mdiLinkedin","mdiMagnify","mdiMastodon","mdiNewspaperVariantOutline","mdiPostOutline","mdiRss","mdiShareVariant","mdiTagMultipleOutline","mdiTagOutline","mdiTextLong","mdiTwitter","mdiWeb","mdiYoutube","AppLink","MdiIcon","Fuse","Box","Chip","IconButton","InputBase","MuiLink","MuiStack","Typography","Children","forwardRef","useContext","useMemo","useState","BUNDLE_ID","generatePresetId","toCount","typographyVariants","ENTRIES_ID","COLLECTION_ENTRIES_COMPONENT_ID","ENTRY_BODY_ID","COLLECTION_ENTRY_BODY_COMPONENT_ID","RELATED_ID","COLLECTION_RELATED_COMPONENT_ID","SHARE_ID","COLLECTION_SHARE_COMPONENT_ID","ENTRY_META_ID","COLLECTION_ENTRY_META_COMPONENT_ID","ENTRY_AUTHOR_ID","COLLECTION_ENTRY_AUTHOR_COMPONENT_ID","CATEGORIES_ID","COLLECTION_CATEGORIES_COMPONENT_ID","SEARCH_ID","COLLECTION_SEARCH_COMPONENT_ID","AUTHOR_PROFILE_ID","CONTENT_AUTHOR_PROFILE_COMPONENT_ID","COLLECTION_SEARCH_MODE_OPTIONS","value","label","SEARCH_FIELD_WIDTH","SUGGESTION_PANEL_WIDTH","SUGGESTION_LIMIT","searchResultsHref","query","encodeURIComponent","searchFieldSx","alignSelf","display","alignItems","gap","px","py","borderRadius","bgcolor","color","width","maxWidth","suggestionAnchorSx","position","suggestionPanelSx","top","right","zIndex","border","borderColor","boxShadow","overflow","textAlign","suggestionRowLinkSx","suggestionFooterSx","borderTop","fontSize","fontWeight","SearchField","placeholder","inert","landmark","onQuery","onEscape","role","sx","path","name","trim","inputProps","flex","readOnly","onChange","event","target","onKeyDown","key","SuggestionPanel","suggestions","trimmed","emptyText","length","map","item","index","row","spacing","direction","variant","title","category","size","date","excerpt","textOverflow","whiteSpace","url","href","underline","SuggestSearchBox","fuzzy","items","setQuery","closed","setClosed","search","slice","result","refIndex","filter","Boolean","field","undefined","next","component","action","method","searchScope","perPage","shown","searchTotal","searchCapped","paginated","truncated","CollectionEntries","collectionSlug","entriesLimit","filterCategory","filterTag","page","firstPageOnly","searchMode","searchPlaceholder","searchIndex","children","props","ref","suppressNavigation","ScreenLinkContext","COLLECTION_SEARCH_FUSE_OPTIONS","childArray","toArray","groupSize","mode","live","visible","scopeNote","matched","Set","_","has","Math","floor","showField","text","displayName","collectionEntriesSchema","$id","pluginId","description","ComponentCategory","DATA_DISPLAY","icon","attributes","FieldComponentType","TEXT_FIELD","type","SWITCH","SELECT","options","option","condition","when","is","UNRESOLVED_TOKEN","renderInlines","inlines","links","strong","em","span","link","resolveMarkdownLink","kind","cursor","CollectionEntryBody","markdown","intrinsicSizes","rest","nodeSx","Array","isArray","screens","hostId","useSite","source","unresolved","test","blocks","parseMarkdownLite","slugs","markdownHeadingSlugs","p","fontFamily","block","id","level","gutterBottom","scrollMarginTop","HEADING_ANCHOR_SCROLL_MARGIN","mediaBodyImageAttributes","src","alt","height","my","DEFERRED_IMAGE_ATTRIBUTES","lineHeight","pl","itemIndex","li","start","overflowX","code","borderCollapse","thead","tr","header","cell","cellIndex","th","style","align","tbody","rows","rowIndex","td","mx","borderLeft","fontStyle","collectionEntryBodySchema","TEXT","flags","selfClosing","FEATURE_FLAG","ENABLED","RELATED_DEFAULT_COLUMNS","RELATED_COVER_ASPECT","relatedChipSx","typography","RELATED_DEFAULT_HEADING_VARIANT","RELATED_DEFAULT_TITLE_VARIANT","RELATED_SAMPLE_PUBLISHED_AT","seconds","Date","UTC","RELATED_SAMPLE_TITLES","RELATED_SAMPLE_CATEGORY","RELATED_SAMPLE_EXCERPT","sampleRelatedEntries","limit","dateFormat","count","min","COLLECTION_RELATED_DEFAULT_LIMIT","COLLECTION_RELATED_MAX","formatCollectionEntryDate","from","CollectionRelated","heading","entries","showCover","layout","columns","headingVariant","titleVariant","showDate","showCategory","showExcerpt","sample","normalizeCollectionEntryDateFormat","headingNode","titleNode","entry","linked","dateNode","excerptNode","coverNode","resolveMediaSrc","coverImage","aria-hidden","aspectRatio","backgroundColor","renderedMediaAlt","coverImageAlt","objectFit","sampleNotice","columnCount","card","gridTemplateColumns","columnGap","rowGap","gridColumn","meta","join","collectionRelatedSchema","initialValue","COLLECTION_ENTRY_DATE_FORMAT_OPTIONS","SHARE_TARGETS","CollectionShare","copied","setCopied","open","buildHref","window","location","copy","navigator","clipboard","writeText","setTimeout","mr","aria-label","onClick","collectionShareSchema","NAVIGATION","ENTRY_SAMPLE_PUBLISHED_AT","ENTRY_SAMPLE_AUTHOR","ENTRY_SAMPLE_CATEGORY","ENTRY_SAMPLE_TAGS","ENTRY_SAMPLE_BIO","samplePortraitSx","flexShrink","ENTRY_AVATAR_SIZE","metaValue","CollectionEntryMeta","_dateFormat","author","authorPageUrl","linkAuthor","tags","avatarImage","showAuthor","showTags","showAvatar","dateValue","authorValue","categoryValue","tagsValue","tagList","split","tag","avatarRaw","avatarSrc","filledLine","authorHrefRaw","authorHref","SAFE_AUTHOR_HREF","trailingLine","line","chips","samplePortrait","flexWrap","collectionEntryMetaSchema","AUTHOR_AVATAR_SIZE","AUTHOR_LINK_ICON_PATHS","twitter","linkedin","github","mastodon","youtube","instagram","facebook","web","email","rss","authorLinkIconPath","platform","authorLinkPlatform","iconPath","SAFE_AUTHOR_LINK_HREF","EXTERNAL_AUTHOR_HREF","AuthorLinkRows","ml","authorLinkLabel","componentVariant","rel","safeAuthorLinks","show","CollectionEntryAuthor","bio","image","pageUrl","showBio","showLinks","nameValue","bioValue","hrefRaw","urlValue","imageRaw","imageSrc","displayBio","linkRows","minWidth","collectionEntryAuthorSchema","TEXTAREA","PROFILE_AVATAR_SIZE","ContentAuthorProfile","jobTitle","worksFor","showRole","jobValue","worksForValue","urlRaw","roleLine","contentAuthorProfileSchema","pillSx","active","CollectionCategories","allLabel","aria-current","collectionCategoriesSchema","clearedValue","COLLECTION_ALL_PILL_NONE","CollectionSearch","collectionSearchSchema","entryText","extra","componentId","collectionPresets","NodeType","PRESET","BLOCKS","data","nodes","COLLECTION_ENTRY_DATE_FORMAT_DEFAULT","COLLECTION_ALL_PILL_DEFAULT"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/collection.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport {\n mdiAccountCircleOutline,\n mdiContentCopy,\n mdiEmail,\n mdiFacebook,\n mdiGithub,\n mdiInstagram,\n mdiLinkVariant,\n mdiLinkedin,\n mdiMagnify,\n mdiMastodon,\n mdiNewspaperVariantOutline,\n mdiPostOutline,\n mdiRss,\n mdiShareVariant,\n mdiTagMultipleOutline,\n mdiTagOutline,\n mdiTextLong,\n mdiTwitter,\n mdiWeb,\n mdiYoutube,\n} from '@aglyn/shared-data-mdi'\nimport { AppLink, MdiIcon } from '@aglyn/shared-ui-jsx'\n// The icon picker's fuzzy matcher (use-mdi-icons-fuzzy), not a re-implementation\n// (AGL-1516): search here has to feel like search does everywhere else in the\n// product, and two matchers is how they drift.\nimport { Fuse } from '@aglyn/shared-util-vendor/fuse'\nimport Box from '@mui/material/Box'\nimport Chip from '@mui/material/Chip'\nimport IconButton from '@mui/material/IconButton'\nimport InputBase from '@mui/material/InputBase'\nimport MuiLink from '@mui/material/Link'\nimport MuiStack, { type StackProps } from '@mui/material/Stack'\nimport Typography from '@mui/material/Typography'\nimport type { ReactNode } from 'react'\nimport { Children, forwardRef, useContext, useMemo, useState } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n// One coercion for every authored count in this bundle (AGL-1457) — number\n// fields round-trip as strings, and a second parser here would drift.\nimport { toCount } from './image-list'\n// The theme's own type steps (AGL-2486), shared with the Typography element\n// rather than a second list here — one rung list, one place to extend.\nimport { typographyVariants } from './typography'\n\n// Persisted component ids (AGL-551/582); the compose pipeline references\n// them through @aglyn/aglyn constants. Never rename.\nexport const ENTRIES_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_ENTRIES_COMPONENT_ID\nexport const ENTRY_BODY_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_ENTRY_BODY_COMPONENT_ID\nexport const RELATED_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_RELATED_COMPONENT_ID\nexport const SHARE_ID: Aglyn.ComponentId = Aglyn.COLLECTION_SHARE_COMPONENT_ID\nexport const ENTRY_META_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_ENTRY_META_COMPONENT_ID\nexport const ENTRY_AUTHOR_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_ENTRY_AUTHOR_COMPONENT_ID\nexport const CATEGORIES_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_CATEGORIES_COMPONENT_ID\nexport const SEARCH_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_SEARCH_COMPONENT_ID\nexport const AUTHOR_PROFILE_ID: Aglyn.ComponentId =\n Aglyn.CONTENT_AUTHOR_PROFILE_COMPONENT_ID\n\n/* ── Collection entries (repeater) ──────────────────────────────────────── */\n\nexport interface CollectionEntriesProps extends StackProps {\n /**\n * Collection to repeat over (compose-time, AGL-551). Blank = the\n * collection routed by the current URL on list-template screens.\n */\n collectionSlug?: string\n /** Maximum entries rendered (compose-time; blank = all, capped at 100). */\n entriesLimit?: number | string\n /**\n * Only entries in this category repeat (compose-time, AGL-582).\n * Matches the collection's category by stable id or display name.\n */\n filterCategory?: string\n /** Only entries carrying this tag repeat (compose-time, AGL-582). */\n filterTag?: string\n /**\n * Entries per page (compose-time, AGL-620). When set, the block renders one\n * page window instead of the top `entriesLimit`; the built-in collection\n * list uses this with the `page` from `/{collection}/page/{n}`.\n */\n perPage?: number | string\n /** 1-based page for `perPage` (compose-time, AGL-620). */\n page?: number | string\n /**\n * Render this block only on page 1 of the routed listing (compose-time,\n * AGL-1871). For a LEAD card — a block with no `perPage`, showing the top\n * of the set — every `/{collection}/page/{n}` past the first otherwise\n * repeats the identical entry above a page of different ones.\n *\n * Opt-in and default off: a block that legitimately belongs on every page\n * of a listing (a \"popular posts\" rail) has exactly the same shape.\n */\n firstPageOnly?: boolean\n /**\n * Show a search box that filters the RENDERED entries by title/excerpt as\n * the reader types (AGL-1516, Figma 494:1220). Opt-in and default off, so\n * every existing instance renders exactly as before.\n *\n * Client-evaluated over the entries this block already holds — the page is\n * ISR-cached, so a keystroke never costs a Firestore read. That set is\n * whatever the expansion left the block holding, which paging, an\n * `entriesLimit` or the 100-entry cap can each cut down. A truncated block\n * says so under its results either way — on a miss and on a hit\n * (AGL-1516, AGL-2569) — rather than pretending global search.\n */\n search?: boolean\n /**\n * What the search box DOES with a query (AGL-1525, Figma 496:1218).\n *\n * - `filter` (default, and the reading of an absent value) — hides the\n * non-matching cards in place. What AGL-1516 shipped.\n * - `suggest` — the card grid stays exactly as it was and a floating panel\n * of matching entries opens under the field, ending in a link to the\n * site-wide results page. The frame's toolbar behaviour: a reader\n * skimming the list is not made to lose it to a typo.\n */\n searchMode?: CollectionSearchMode\n /** Hint text inside the search box (blank = \"Search posts…\"). */\n searchPlaceholder?: string\n /**\n * Server-stamped matchable text per rendered entry\n * (`expandCollectionEntries`, AGL-1516); never set by hand.\n */\n searchIndex?: Aglyn.CollectionEntrySearchItem[]\n /**\n * How many entries `searchIndex` was drawn from — server-stamped by\n * `expandCollectionEntries` alongside it (AGL-1516), never set by hand.\n * `searchIndex.length < searchTotal` is the only honest test of whether\n * this block holds the whole set; `perPage` is not, because\n * `entriesLimit` and the 100-entry cap truncate too.\n */\n searchTotal?: number\n /**\n * Whether the read behind `searchTotal` reached its own bound — server-\n * stamped beside it (AGL-1516), never set by hand.\n *\n * `searchTotal` is a count of what the server SAW, and the collection read\n * is limited. Without this flag a block holding 100 of 400 posts satisfies\n * `searchIndex.length === searchTotal` and renders the one empty state that\n * claims to have looked everywhere.\n */\n searchCapped?: boolean\n}\n\n/** The two things the toolbar search box can do (AGL-1516/AGL-1525). */\nexport type CollectionSearchMode = 'filter' | 'suggest'\n\n/**\n * Author-facing choices for {@link CollectionEntriesProps.searchMode}.\n *\n * Both values are truthy on purpose (AGL-1453): `''` cannot survive a save,\n * so an author who tried the other mode would have no route back. \"Filter\"\n * carries the absent-value reading, which is what every block published\n * before AGL-1525 has.\n */\nexport const COLLECTION_SEARCH_MODE_OPTIONS: ReadonlyArray<{\n value: CollectionSearchMode\n label: string\n}> = [\n { value: 'filter', label: 'Filter the entries in place' },\n { value: 'suggest', label: 'Show a suggestions dropdown' },\n]\n\n/** The toolbar search field, from the frame (Figma 494:1220). */\nconst SEARCH_FIELD_WIDTH = 240\n\n/** Suggestion panel (Figma 496:1218). */\nconst SUGGESTION_PANEL_WIDTH = 420\n\n/**\n * Rows in the panel before it stops. The frame shows five; the sixth row is\n * always the \"View all results\" link, which is the honest overflow answer —\n * a scrolling suggestion panel is a worse results page.\n */\nconst SUGGESTION_LIMIT = 5\n\n/** Where \"View all results\" goes — the reserved site-search route (AGL-88). */\nconst searchResultsHref = (query: string) =>\n `/search?q=${encodeURIComponent(query)}`\n\n/**\n * The frame's compact filled field: magnify glyph + hint on the quiet\n * surface token, right-aligned so it sits where the toolbar row puts it.\n * Palette tokens only, so it follows the site theme in both modes.\n */\nconst searchFieldSx = {\n alignSelf: 'flex-end',\n display: 'flex',\n alignItems: 'center',\n gap: 1,\n px: 1.5,\n py: 0.75,\n borderRadius: 2,\n bgcolor: 'action.hover',\n color: 'text.secondary',\n width: SEARCH_FIELD_WIDTH,\n maxWidth: '100%',\n}\n\n/**\n * The positioned parent of the suggestion panel (AGL-1525). It takes over\n * `alignSelf` from the field so the toolbar row is unchanged — the panel\n * hangs off the field, it does not move it.\n */\nconst suggestionAnchorSx = {\n alignSelf: 'flex-end',\n position: 'relative',\n width: SEARCH_FIELD_WIDTH,\n maxWidth: '100%',\n}\n\n/**\n * The floating panel itself. Right-aligned to the field it drops from, and\n * wider than it, as the frame draws it — a suggestion row carries a title, a\n * chip, a date and a line of excerpt, none of which fit in 240px.\n *\n * Palette and shadow tokens only, so it follows the site theme in both\n * modes rather than pinning a light-mode card onto a dark page.\n */\nconst suggestionPanelSx = {\n position: 'absolute',\n top: 'calc(100% + 8px)',\n right: 0,\n zIndex: 10,\n width: SUGGESTION_PANEL_WIDTH,\n // Never wider than the viewport on a phone, where the anchor is narrow\n // and `right: 0` would otherwise hang the panel off the left edge.\n maxWidth: 'calc(100vw - 32px)',\n bgcolor: 'background.paper',\n color: 'text.primary',\n border: 1,\n borderColor: 'divider',\n borderRadius: 2,\n boxShadow: 6,\n overflow: 'hidden',\n textAlign: 'left',\n}\n\nconst suggestionRowLinkSx = {\n display: 'block',\n color: 'inherit',\n '&:hover': { bgcolor: 'action.hover' },\n}\n\nconst suggestionFooterSx = {\n display: 'block',\n px: 2,\n py: 1.25,\n borderTop: 1,\n borderColor: 'divider',\n bgcolor: 'action.hover',\n fontSize: 14,\n fontWeight: 500,\n}\n\n/** What a built index can be searched by — Fuse over the stamped rows. */\ntype EntryFuse = InstanceType<typeof Fuse<Aglyn.CollectionEntrySearchItem>>\n\n/**\n * The frame's search input (Figma 494:1220) — the magnify glyph and a bare\n * `InputBase` on the quiet surface token.\n *\n * Presentational and stateless, so the entries block's in-place filter and\n * the standalone toolbar box (AGL-1516) are the SAME field rather than two\n * that merely look alike. `inert` is the editing-surface rendering: read-only\n * and handler-free, so a click or stray keystroke in the besigner never edits\n * state the canvas cannot use — how Category Pills go inert there too.\n */\nconst SearchField = ({\n value,\n placeholder,\n inert,\n landmark,\n onQuery,\n onEscape,\n}: {\n value: string\n placeholder?: string\n inert?: boolean\n /**\n * Put `role=\"search\"` on the FIELD. False when a wrapping form carries it\n * instead — one search landmark per box, or a screen reader announces the\n * same field twice.\n */\n landmark?: boolean\n onQuery?: (next: string) => void\n onEscape?: () => void\n}) => (\n <Box {...(landmark ? { role: 'search' } : {})} sx={searchFieldSx}>\n <MdiIcon path={mdiMagnify.path} />\n <InputBase\n value={value}\n name=\"q\"\n placeholder={(placeholder ?? '').trim() || 'Search posts…'}\n inputProps={{ 'aria-label': 'Search entries' }}\n sx={{ flex: 1, fontSize: 14, color: 'text.primary' }}\n {...(inert\n ? { readOnly: true }\n : {\n onChange: (event) => onQuery?.(event.target.value),\n onKeyDown: (event) => {\n if (event.key === 'Escape') onEscape?.()\n },\n })}\n />\n </Box>\n)\n\n/**\n * The floating suggestion panel (AGL-1525, Figma 496:1218) — rows straight\n * off the server-stamped index, and a \"View all results\" link that is there\n * hits or no hits.\n *\n * `emptyText` is the caller's, not this component's, and that is the whole\n * point of it being a parameter: a page-windowed entries block and a\n * whole-collection toolbar box searched different sets, and a miss has to say\n * WHICH set came back empty. A shared \"No matches.\" would let one of them\n * claim a reach it never had.\n */\nconst SuggestionPanel = ({\n suggestions,\n trimmed,\n emptyText,\n}: {\n suggestions: readonly Aglyn.CollectionEntrySearchItem[]\n trimmed: string\n emptyText: string\n}) => (\n <Box sx={suggestionPanelSx}>\n {suggestions.length ? (\n suggestions.map((item, index) => {\n const row = (\n <MuiStack spacing={0.5} sx={{ px: 2, py: 1.25 }}>\n <MuiStack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"subtitle2\" sx={{ color: 'text.primary' }}>\n {item.title}\n </Typography>\n {item.category ? (\n <Chip label={item.category} size=\"small\" />\n ) : null}\n {item.date ? (\n <Typography variant=\"caption\" sx={{ color: 'text.secondary' }}>\n {item.date}\n </Typography>\n ) : null}\n </MuiStack>\n {item.excerpt ? (\n <Typography\n variant=\"body2\"\n sx={{\n color: 'text.secondary',\n // One line of excerpt, as the frame draws it. A row that\n // grows with the prose turns the panel into a page.\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {item.excerpt}\n </Typography>\n ) : null}\n </MuiStack>\n )\n // A row without a `url` is a page cached before AGL-1525 stamped one,\n // or a slugless entry. It renders as TEXT rather than as a link to\n // nowhere: a suggestion that navigates to the wrong page is worse\n // than one the reader has to read.\n return item.url ? (\n <AppLink\n key={index}\n href={item.url}\n sx={suggestionRowLinkSx}\n underline=\"none\"\n >\n {row}\n </AppLink>\n ) : (\n <Box key={index}>{row}</Box>\n )\n })\n ) : (\n <Typography\n variant=\"body2\"\n sx={{ color: 'text.secondary', px: 2, py: 1.25 }}\n >\n {emptyText}\n </Typography>\n )}\n {/*\n Always present, hits or no hits (Figma 496:1218). A box that searches\n one page, or one bounded read of a collection, cannot answer \"then\n where is it?\" on its own — the site-wide results page is the only\n honest answer, and it matters most in exactly the case where the panel\n came back empty.\n */}\n <AppLink\n href={searchResultsHref(trimmed)}\n sx={suggestionFooterSx}\n underline=\"none\"\n >\n {`View all results for “${trimmed}” →`}\n </AppLink>\n </Box>\n)\n\n/**\n * Field plus dropdown, in a REAL form (AGL-1525) — so Enter reaches the\n * results page and the box still works with the panel's JS doing nothing at\n * all. The panel is an enhancement over a working search box, not the search\n * box.\n *\n * Owns the query, because nothing outside it needs one: `suggest` never\n * touches the cards underneath. That is what lets the standalone toolbar\n * block exist at all (AGL-1516) — a box with no clones to hide is a complete\n * feature, not a crippled one.\n */\nconst SuggestSearchBox = ({\n fuzzy,\n items,\n placeholder,\n inert,\n emptyText,\n}: {\n fuzzy: EntryFuse | null\n items?: readonly Aglyn.CollectionEntrySearchItem[]\n placeholder?: string\n inert?: boolean\n /** The honest miss for the set THIS box searched. */\n emptyText: (query: string) => string\n}) => {\n const [query, setQuery] = useState('')\n /** Escape dismissed THIS answer (AGL-1525); typing brings it back. */\n const [closed, setClosed] = useState(false)\n const trimmed = query.trim()\n const suggestions =\n !inert && trimmed && fuzzy && items\n ? fuzzy\n .search(trimmed)\n .slice(0, SUGGESTION_LIMIT)\n .map((result) => items[result.refIndex])\n .filter(Boolean)\n : []\n const field = (\n <SearchField\n value={query}\n {...(placeholder === undefined ? {} : { placeholder })}\n // No form wraps the inert field, so the landmark belongs on it.\n {...(inert ? { inert: true, landmark: true } : {})}\n onQuery={(next) => {\n setQuery(next)\n // A new query reopens a panel the reader dismissed — Escape closes\n // THIS answer, it does not turn the feature off for the visit.\n setClosed(false)\n }}\n onEscape={() => setClosed(true)}\n />\n )\n if (inert) return field\n return (\n <Box\n component=\"form\"\n role=\"search\"\n action=\"/search\"\n method=\"get\"\n sx={suggestionAnchorSx}\n >\n {field}\n {trimmed && !closed ? (\n <SuggestionPanel\n suggestions={suggestions}\n trimmed={trimmed}\n emptyText={emptyText(trimmed)}\n />\n ) : null}\n </Box>\n )\n}\n\n/**\n * How much of the collection an entries block's search could actually see\n * (AGL-1516, AGL-2569) — `truncated` says the rendered window is smaller\n * than the set, `paginated` says the missing part lives on other pages of\n * this same list rather than merely outside the block.\n *\n * The test is whether the window is SMALLER THAN THE SET, which only the\n * server knows — `perPage` is the wrong proxy for it in both directions. A\n * block truncated by `entriesLimit` or by the 100-entry cap has no `perPage`\n * at all and would claim a global miss over 6 of 40 posts; a block whose\n * `perPage` exceeds its entry count is a single complete page and would\n * blame pages that do not exist. `searchTotal` is stamped with `searchIndex`,\n * so the two are always in step.\n *\n * Absent — a page cached before that stamp shipped — falls back to the old\n * `perPage` reading rather than guessing \"complete\": understating the scope\n * of a search is the safe direction to be wrong in.\n *\n * `searchCapped` is the second way a block can fail to hold the set, and the\n * invisible one: `searchTotal` counts the entries the server READ, and that\n * read is bounded. Past the bound `shown === searchTotal` is satisfied by a\n * block holding 100 of 400 posts, and the wording that claims to have looked\n * everywhere is exactly the branch it would take.\n *\n * Both the miss and the hit disclose off this one answer, so the two can\n * never disagree about the same block.\n */\nconst searchScope = ({\n perPage,\n shown,\n searchTotal,\n searchCapped,\n}: {\n perPage?: CollectionEntriesProps['perPage']\n /** Entries this block searched — the length of its stamped index. */\n shown: number\n searchTotal?: number\n searchCapped?: boolean\n}): { paginated: boolean; truncated: boolean } => {\n const paginated = (toCount(perPage, 0) ?? 0) > 0\n return {\n paginated,\n truncated:\n typeof searchTotal === 'number'\n ? shown < searchTotal || Boolean(searchCapped)\n : paginated || Boolean(searchCapped),\n }\n}\n\n/**\n * Repeats its children once per published entry of a content collection\n * (AGL-551) — the collections sibling of the dataset repeatable. The tenant\n * expands it at compose time with `{{entry.*}}` tokens; in the besigner the\n * template renders once with literal tokens, matching the repeatable UX.\n *\n * Search (AGL-1516): with `search` on, a toolbar search box filters the\n * rendered entry clones client-side against the server-stamped\n * `searchIndex` — fuzzy, via the same Fuse the icon picker uses. The clones\n * arrive as `children` in stamp order, one group of template roots per\n * entry, so group N of the children IS entry N of the index. Matches keep\n * the list's own order rather than Fuse's score order: a blog list is\n * chronological, and search narrows it, it does not reshuffle it. In the\n * besigner the field renders as an inert affordance (the template renders\n * once with literal tokens and there is no index to search), matching how\n * Category Pills go inert on editing surfaces.\n */\nconst CollectionEntries = forwardRef<HTMLDivElement, CollectionEntriesProps>(\n // collectionSlug/entriesLimit/filter*/search* are compose-time or\n // search-only attributes: strip so they never hit the DOM.\n (\n {\n collectionSlug,\n entriesLimit,\n filterCategory,\n filterTag,\n perPage,\n page,\n firstPageOnly,\n search,\n searchMode,\n searchPlaceholder,\n searchIndex,\n searchTotal,\n searchCapped,\n children,\n ...props\n },\n ref,\n ) => {\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n /** `filter` mode only — `suggest` owns its own query (AGL-1516). */\n const [query, setQuery] = useState('')\n const items = search ? searchIndex : undefined\n const fuzzy = useMemo(\n () =>\n items?.length\n ? // The ONE matcher config (AGL-1525), shared with the site-wide\n // results page this block's panel links to — a query the panel\n // forgave must not come back empty from \"View all results\".\n new Fuse(items, { ...Aglyn.COLLECTION_SEARCH_FUSE_OPTIONS })\n : null,\n [items],\n )\n if (!search) {\n return (\n <MuiStack ref={ref} spacing={4} {...props}>\n {children}\n </MuiStack>\n )\n }\n const childArray = Children.toArray(children)\n // Template roots per entry. Every entry clones the same template, so the\n // children divide evenly; anything else means the children are not the\n // stamped clones, and filtering blind would hide the wrong cards —\n // fail open and render everything.\n const groupSize =\n items?.length && childArray.length % items.length === 0\n ? childArray.length / items.length\n : 0\n // `suggest` draws its rows from the INDEX and never touches the clones,\n // so the clone-alignment precondition is a `filter` precondition only.\n // A block whose children do not divide evenly still suggests correctly;\n // requiring `groupSize` here would silently kill the panel on exactly\n // the templates that made the fail-open necessary.\n const mode: CollectionSearchMode =\n searchMode === 'suggest' ? 'suggest' : 'filter'\n const live =\n !suppressNavigation &&\n Boolean(fuzzy) &&\n (mode === 'suggest' || groupSize > 0)\n const trimmed = query.trim()\n let visible: ReactNode[] | ReactNode = children\n /**\n * The one line under the results that says what the search covered —\n * either a miss scoped to the set that came back empty, or a hit scoped\n * to the window it was drawn from. Never both.\n */\n let scopeNote: ReactNode = null\n if (mode === 'suggest') {\n // The grid is deliberately untouched (Figma 496:1218): the panel is an\n // overlay on a page the reader is still reading, not a filter.\n visible = children\n } else if (live && trimmed && fuzzy) {\n const matched = new Set(\n fuzzy.search(trimmed).map((result) => result.refIndex),\n )\n visible = childArray.filter((_, index) =>\n matched.has(Math.floor(index / groupSize)),\n )\n // HONEST scope (AGL-1516, AGL-2569): this block holds whatever window\n // the expansion gave it, and pretending the search was global would\n // turn every miss into a false \"this post does not exist\" — and every\n // hit into a result set that looks like the whole answer.\n const shown = items?.length ?? 0\n const { paginated, truncated } = searchScope({\n perPage,\n shown,\n searchTotal,\n searchCapped,\n })\n if (!(visible as ReactNode[]).length) {\n scopeNote = (\n <Typography variant=\"body2\" sx={{ color: 'text.secondary' }}>\n {!truncated\n ? `No matches for “${trimmed}”.`\n : paginated\n ? `No matches for “${trimmed}” on this page — other pages ` +\n 'are not searched.'\n : `No matches for “${trimmed}” in the ${shown} entries ` +\n 'shown here — the rest of the collection is not searched.'}\n </Typography>\n )\n } else if (truncated) {\n // The hit half of the same honesty (AGL-2569). Matches are drawn from\n // the same window the miss apologizes for, they carry no pager of\n // their own, and nothing else on screen says the pages behind this\n // one went unread — so a list that happens to be partial reads as\n // complete. A block that holds the whole collection says nothing\n // here: a disclaimer on a complete answer is noise, and every small\n // site would carry it.\n //\n // No route out to `/search?q=` (AGL-88): the suggestion panel links\n // there because it is the panel's only possible answer, but this\n // mode has a working list under the field and that page does not\n // exist yet. Trading a silent partial answer for a dead link is not\n // the trade.\n scopeNote = (\n <Typography variant=\"body2\" sx={{ color: 'text.secondary' }}>\n {paginated\n ? 'Showing matches on this page — other pages are not searched.'\n : `Showing matches in the ${shown} entries here — the rest of ` +\n 'the collection is not searched.'}\n </Typography>\n )\n }\n }\n // The field renders when there is something to search, and as an inert\n // affordance on editing surfaces so the author sees what they enabled.\n // A live surface with nothing stamped (unknown collection, zero entries)\n // renders no field at all — a search box over nothing is a lie.\n const showField = suppressNavigation || live\n const field = !showField ? null : mode === 'suggest' ? (\n // Rows in Fuse's SCORE order, the opposite of what `filter` does and\n // deliberately: filtering narrows a chronological feed, so reshuffling\n // it would be a second, unasked-for change to what the reader is\n // looking at. A suggestion list is not a feed — it is an answer to a\n // question, and the best answer belongs first.\n <SuggestSearchBox\n fuzzy={fuzzy}\n {...(items ? { items } : {})}\n {...(searchPlaceholder === undefined\n ? {}\n : { placeholder: searchPlaceholder })}\n {...(suppressNavigation ? { inert: true } : {})}\n // This block searched the entries IT rendered — a page window, an\n // `entriesLimit` slice, or a bounded read. Never \"this collection\".\n emptyText={(text) => `No matches for “${text}” on this page.`}\n />\n ) : (\n <SearchField\n value={query}\n landmark\n {...(searchPlaceholder === undefined ? {} : { placeholder: searchPlaceholder })}\n {...(suppressNavigation ? { inert: true } : {})}\n onQuery={setQuery}\n />\n )\n return (\n <MuiStack ref={ref} spacing={4} {...props}>\n {field}\n {visible}\n {scopeNote}\n </MuiStack>\n )\n },\n)\nCollectionEntries.displayName = 'AglynCollectionEntries'\n\nexport const collectionEntriesSchema: Aglyn.ComponentSchema<CollectionEntriesProps> =\n {\n $id: ENTRIES_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Collection Entries',\n description: 'Repeats its children once per entry in a content collection.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiPostOutline.path, sx: { color: 'secondary.main' } },\n attributes: [\n {\n name: 'collectionSlug',\n label: 'Collection slug',\n description:\n 'Content collection whose published entries the children repeat ' +\n 'over (e.g. \"blog\"). Leave blank on a list-template screen to use ' +\n 'the collection from the URL.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'entriesLimit',\n label: 'Entries limit',\n description: 'Maximum entries rendered (blank = all, capped at 100).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'filterCategory',\n label: 'Filter by category',\n description:\n 'Only entries in this category repeat — the category name or its ' +\n 'stable id both match (e.g. \"Guides\"). Blank = no category filter.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'filterTag',\n label: 'Filter by tag',\n description:\n 'Only entries carrying this tag repeat (e.g. \"nextjs\"). Blank = ' +\n 'no tag filter.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'perPage',\n label: 'Entries per page',\n description:\n 'Paginate the list: entries per page (blank = no pagination). ' +\n 'Pairs with the page from /{collection}/page/{n}.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'page',\n label: 'Page',\n description: '1-based page to render when Entries per page is set.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'firstPageOnly',\n label: 'Only on page 1',\n description:\n 'Show this block on the first page of the listing only. Use it ' +\n 'for a featured or lead card: without it the same entry repeats ' +\n 'at the top of every /page/{n}, above a page of different ones.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'search',\n label: 'Search',\n description:\n 'Show a search box that filters the rendered entries by title ' +\n 'and excerpt as the reader types. It searches the entries this ' +\n 'block rendered — whatever paging, an entry limit or the ' +\n '100-entry cap left it holding, and it says so on a miss.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'searchMode',\n label: 'When the reader types',\n description:\n 'Filter the entries in place, or leave the list alone and open a ' +\n 'dropdown of matching entries under the box — each one a link, ' +\n 'ending in \"View all results\" for a search across the whole site.',\n component: Aglyn.FieldComponentType.SELECT,\n options: COLLECTION_SEARCH_MODE_OPTIONS.map((option) => ({\n ...option,\n })),\n // Meaningless while there is no search box to type in.\n condition: { when: 'search', is: true },\n },\n {\n name: 'searchPlaceholder',\n label: 'Search placeholder',\n description:\n 'Hint text inside this block’s own search box — the one that ' +\n 'filters the entries below it as a reader types. Blank shows ' +\n '\"Search posts…\".',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n // Meaningless while there is no search box to hint in.\n condition: { when: 'search', is: true },\n },\n // `searchIndex` and `searchTotal` are deliberately NOT attributes: both\n // are server-stamped by expandCollectionEntries, like Category Pills'\n // `items` and Related Posts' `entries`.\n {\n name: 'spacing',\n label: 'Spacing',\n description: 'Defines the space/gap between entries.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n ],\n }\n\n/* ── Entry body (markdown) ──────────────────────────────────────────────── */\n\nexport interface CollectionEntryBodyProps {\n /**\n * Markdown-lite source. On entry-template screens `{{entry.body}}`\n * resolves to the rendered entry's body at compose time.\n */\n markdown?: string\n /**\n * The pixel pair of each library image this body names, keyed by the target\n * as the body writes it, stamped by the composition from the assets' DAM\n * records (AGL-3149) — never set by hand, and never on the template, where\n * `markdown` is still the `{{entry.body}}` token and names no asset.\n */\n intrinsicSizes?: Record<string, { width: number; height: number }>\n}\n\n/** A still-unresolved `{{token}}` (no entry context on this render). */\nconst UNRESOLVED_TOKEN = /^\\{\\{[^}]+\\}\\}$/\n\nconst renderInlines = (\n inlines: Aglyn.MarkdownInline[],\n links: { suppressNavigation?: boolean; screens?: Aglyn.ScreenRouteMap },\n): ReactNode[] =>\n inlines.map((item, index) => {\n if (item.type === 'bold') return <strong key={index}>{item.text}</strong>\n if (item.type === 'italic') return <em key={index}>{item.text}</em>\n if (item.type !== 'link') return <span key={index}>{item.text}</span>\n // Internal paths route through AppLink for client-side navigation\n // (AGL-582); external links stay plain anchors. Editing surfaces render\n // the link look without an href so clicks never navigate. A target named\n // by REFERENCE resolves against the routing map first (AGL-3118), and one\n // whose entry, listing or screen is gone renders as the words alone —\n // never as an anchor holding the stored value.\n const link = Aglyn.resolveMarkdownLink(item.href, links)\n if (link.kind === 'text') return <span key={index}>{item.text}</span>\n if (link.kind === 'inert') {\n return (\n <MuiLink key={index} component=\"span\" sx={{ cursor: 'default' }}>\n {item.text}\n </MuiLink>\n )\n }\n return link.kind === 'internal' ? (\n <AppLink key={index} href={link.href}>\n {item.text}\n </AppLink>\n ) : (\n <MuiLink key={index} href={link.href}>\n {item.text}\n </MuiLink>\n )\n })\n\n/**\n * Renders a content entry's markdown-lite body as themed MUI elements\n * (AGL-551): headings, paragraphs, lists, and images pick up the site\n * theme's typography instead of the old unthemed article HTML. Parsing is\n * pure, so the full body server-renders (SEO keeps the article text).\n */\nconst CollectionEntryBody = forwardRef<\n HTMLDivElement,\n CollectionEntryBodyProps\n>((props, ref) => {\n // `intrinsicSizes` is destructured out rather than left in `rest`: `rest` is\n // spread onto a Box, and a composition-stamped object would reach the DOM as\n // an attribute React has no idea what to do with.\n const { markdown, intrinsicSizes, ...rest } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n // The routing map travels with the suppression flag: both decide what a\n // link in this body renders as (AGL-3118).\n const { suppressNavigation, screens } = useContext(Aglyn.ScreenLinkContext)\n const links = useMemo(\n () => ({ suppressNavigation, screens }),\n [suppressNavigation, screens],\n )\n // The site being rendered, for image blocks (AGL-1686) — read once here\n // because hooks cannot run inside the block map below.\n const { hostId } = Aglyn.useSite()\n const source = (markdown ?? '').trim()\n const unresolved = !source || UNRESOLVED_TOKEN.test(source)\n const blocks = useMemo(\n () => (unresolved ? [] : Aglyn.parseMarkdownLite(source)),\n [source, unresolved],\n )\n // Heading anchors, on the same terms as the Markdown element (AGL-1162).\n // Without them a blog article could not carry an \"On this page\" aside at\n // all: the Table of Contents element emits `#slug` links, and there was\n // nothing on this page for them to land on.\n const slugs = useMemo(() => Aglyn.markdownHeadingSlugs(blocks), [blocks])\n if (unresolved) {\n // Editing surfaces get an affordance; the published site renders\n // nothing rather than a literal token.\n if (!suppressNavigation) return <Box ref={ref} {...rest} />\n return (\n <Box\n ref={ref}\n {...rest}\n sx={[\n {\n p: 2,\n border: '1px dashed',\n borderColor: 'divider',\n color: 'text.secondary',\n fontSize: 12,\n fontFamily: 'system-ui, sans-serif',\n },\n ...nodeSx,\n ]}\n >\n {'Entry body — the {{entry.body}} markdown renders here'}\n </Box>\n )\n }\n return (\n <Box ref={ref} {...rest}>\n {blocks.map((block, index) => {\n if (block.type === 'heading') {\n return (\n <Typography\n key={index}\n id={slugs[index]}\n variant={block.level === 2 ? 'h4' : 'h5'}\n component={block.level === 2 ? 'h2' : 'h3'}\n gutterBottom\n sx={{\n scrollMarginTop: `${Aglyn.HEADING_ANCHOR_SCROLL_MARGIN}px`,\n }}\n >\n {renderInlines(block.inlines, links)}\n </Typography>\n )\n }\n if (block.type === 'image') {\n return (\n <Box\n key={index}\n component=\"img\"\n // Resolved like the entry's cover image a few hundred lines\n // below (AGL-1686): a body image is the same kind of asset and\n // has no reason to be stored in a more fragile form.\n //\n // And sized like one too, as of AGL-3149: the same call gives\n // the Markdown element's body images their candidate list and\n // intrinsic pair, so the two bodies cannot disagree about how a\n // picture in prose is delivered.\n {...Aglyn.mediaBodyImageAttributes({\n src: block.src,\n hostId,\n size: intrinsicSizes?.[block.src],\n })}\n alt={block.alt}\n // Paired with the `width`/`height` above — see the Markdown\n // element's copy of this: without it the height hint outlives\n // the `maxWidth` cap and squashes the picture.\n sx={{ maxWidth: '100%', height: 'auto', borderRadius: 1, my: 1 }}\n // An image inside an entry body is below the fold by\n // construction — the title, byline and opening paragraphs are\n // above it. This one carried no loading hint at all, so it was\n // fetched EAGERLY, at default priority, competing with the\n // entry's own cover (AGL-2486).\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n )\n }\n if (block.type === 'list') {\n return (\n <Box key={index} component=\"ul\" sx={{ lineHeight: 1.7, pl: 3 }}>\n {block.items.map((item, itemIndex) => (\n <li key={itemIndex}>\n {renderInlines(item, links)}\n </li>\n ))}\n </Box>\n )\n }\n // A numbered list (AGL-1320) — a real `<ol>`, so the markers are the\n // browser's and `start` survives: a notice that resumes at 7 has to\n // read as 7 for the enumeration to mean anything.\n if (block.type === 'orderedList') {\n return (\n <Box\n key={index}\n component=\"ol\"\n start={block.start}\n sx={{ lineHeight: 1.7, pl: 3 }}\n >\n {block.items.map((item, itemIndex) => (\n <li key={itemIndex}>\n {renderInlines(item, links)}\n </li>\n ))}\n </Box>\n )\n }\n // Code blocks and tables (AGL-974). Both scroll rather than wrap:\n // a wrapped code line or a squeezed table column loses the very\n // structure that made the block worth writing.\n if (block.type === 'code') {\n return (\n <Box\n key={index}\n component=\"pre\"\n sx={{\n my: 2,\n p: 2,\n overflowX: 'auto',\n borderRadius: 1,\n bgcolor: 'action.hover',\n fontFamily: 'monospace',\n fontSize: 14,\n }}\n >\n <code>{block.text}</code>\n </Box>\n )\n }\n if (block.type === 'table') {\n return (\n <Box key={index} sx={{ my: 2, overflowX: 'auto' }}>\n <Box\n component=\"table\"\n sx={{\n borderCollapse: 'collapse',\n width: '100%',\n '& th, & td': {\n border: '1px solid',\n borderColor: 'divider',\n px: 1.5,\n py: 1,\n },\n '& th': { bgcolor: 'action.hover' },\n }}\n >\n <thead>\n <tr>\n {block.header.map((cell, cellIndex) => (\n <th\n key={cellIndex}\n style={{ textAlign: block.align[cellIndex] ?? 'left' }}\n >\n {renderInlines(cell, links)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {block.rows.map((row, rowIndex) => (\n <tr key={rowIndex}>\n {row.map((cell, cellIndex) => (\n <td\n key={cellIndex}\n style={{\n textAlign: block.align[cellIndex] ?? 'left',\n }}\n >\n {renderInlines(cell, links)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </Box>\n </Box>\n )\n }\n // The article template's pull-quote (AGL-1315, Figma 170:225):\n // larger italic prose behind a left accent. Palette tokens only, so\n // it follows the site theme in both modes.\n if (block.type === 'quote') {\n return (\n <Typography\n key={index}\n component=\"blockquote\"\n sx={{\n my: 3,\n mx: 0,\n pl: 2.5,\n borderLeft: '3px solid',\n borderColor: 'primary.main',\n fontStyle: 'italic',\n fontSize: '1.25em',\n lineHeight: 1.6,\n color: 'text.primary',\n }}\n >\n {renderInlines(block.inlines, links)}\n </Typography>\n )\n }\n return (\n <Typography\n key={index}\n variant=\"body1\"\n sx={{ lineHeight: 1.7 }}\n gutterBottom\n >\n {renderInlines(block.inlines, links)}\n </Typography>\n )\n })}\n </Box>\n )\n})\nCollectionEntryBody.displayName = 'AglynCollectionEntryBody'\n\nexport const collectionEntryBodySchema: Aglyn.ComponentSchema<CollectionEntryBodyProps> =\n {\n $id: ENTRY_BODY_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Entry Body',\n description: \"The current entry's markdown body, on an entry template.\",\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiTextLong.path, sx: { color: 'secondary.main' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'markdown',\n label: 'Markdown',\n description:\n 'Markdown-lite content. Keep {{entry.body}} on entry-template ' +\n \"screens so each entry's body renders here.\",\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n ],\n }\n\n/* ── Related posts (AGL-582) ────────────────────────────────────────────── */\n\n/** How the related posts are laid out (AGL-1457). */\nexport type CollectionRelatedLayout = 'list' | 'cards'\n\n/** One related post as stamped by the compose pipeline (AGL-582). */\nexport interface CollectionRelatedProps extends StackProps {\n /** Section heading; empty string hides it. */\n heading?: string\n /** Compose-time: most related posts listed (default 3). */\n limit?: number | string\n /**\n * Emit each entry's cover image (AGL-1457). OFF is the default and the\n * shipped behaviour — the block is live on every blog entry, so turning\n * covers on by default would restyle published pages nobody asked about.\n */\n showCover?: boolean\n /**\n * `list` (default) is the plain-link list that has always shipped; `cards`\n * is the article frame's grid of cover + category chip + title.\n */\n layout?: CollectionRelatedLayout\n /** Columns in the `cards` grid (default 3, the frame's 3-up). */\n columns?: number | string\n /**\n * Type step the section heading reads at (default `h5`). One of the\n * theme's own rungs, shared with the Typography element — a heading sized\n * by hand in the Styles panel is the pixel-typing the tokens exist to stop.\n */\n headingVariant?: string\n /**\n * Type step each card title reads at (default `subtitle1`, what the block\n * has always emitted). The title is the block's own markup, so the Styles\n * panel — which edits the node's root — cannot reach it; this is the only\n * handle on it.\n */\n titleVariant?: string\n /** Show each post's published date. On is the shipped behaviour. */\n showDate?: boolean\n /**\n * How each card's published date reads (AGL-1459) — a COMPOSE-TIME prop\n * like {@link limit}: it is answered in `expandCollectionRelated`, where\n * the timestamp still exists, and never reaches the DOM. Blank, or\n * `default`, is the locale date the block has always emitted.\n *\n * Read here too, but only to date the SAMPLE cards: an editing surface has\n * no routed entry and therefore no server fill, so the picker would\n * otherwise preview nothing.\n */\n dateFormat?: Aglyn.CollectionEntryDateFormat\n /** Show each post's category. On is the shipped behaviour. */\n showCategory?: boolean\n /**\n * Show each post's excerpt. OFF is the default and the shipped behaviour:\n * the excerpt has been stamped onto every related item since AGL-582 and\n * rendered by nothing, so turning it on by default would add a paragraph\n * to every published entry nobody asked about.\n */\n showExcerpt?: boolean\n /**\n * Server-stamped related posts (`expandCollectionRelated`); never set by\n * hand — the tenant computes it from the current entry's category/tags.\n */\n entries?: Aglyn.CollectionRelatedItem[]\n}\n\n/** The frame's 3-up (Figma 170:242) when nothing usable is authored. */\nconst RELATED_DEFAULT_COLUMNS = 3\n\n/**\n * The cover's PROPORTION in the card grid.\n *\n * ONE ratio for every card, rather than each record's own shape: the titles\n * under the covers only line up when the covers are the same height, and a\n * grid of honest shapes is a ragged grid.\n *\n * The ratio is the shape the console asks authors for — 1200 x 630, the same\n * image that heads the entry and doubles as its share card — so a cover\n * authored as instructed loses nothing to the crop. The frame's own card is\n * 445 x 180 (Figma 170:243), which is 2.47 against that art's 1.90: held\n * there, `objectFit: cover` discarded 23% of every card's picture at every\n * width. Cover art is usually a composed lockup, so what it discards is the\n * title set on it.\n *\n * Before the ratio it was a fixed 180px height, which was the frame's number\n * without the frame's shape: a card is 445 wide there, the grid's columns\n * grow with the viewport and 180 does not, so every extra pixel of width\n * flattened the box further — 3.57:1 by a 2000px viewport. A ratio holds the\n * shape at every width; this ratio holds the picture as well.\n */\nconst RELATED_COVER_ASPECT = '1200 / 630'\n\n/**\n * The card's category chip (AGL-1457). ONE fixed token pair for every\n * category: colour-coding a chip per category needs conditional styling,\n * which is not expressible yet (AGL-1307), and inventing a palette here\n * would have to be unpicked when it is. Same shape as the tag chips Entry\n * Meta already renders, so the two blocks read as one vocabulary.\n */\nconst relatedChipSx = {\n alignSelf: 'flex-start',\n borderColor: 'divider',\n color: 'text.secondary',\n '& .MuiChip-label': {\n // 9px, the frame's own inset on a 69 x 24 chip.\n px: 1.125,\n // The frame sets this label in the same uppercase, letter-spaced step as\n // the kicker above an article title, which is what `overline` IS in the\n // site theme. Naming the step keeps the chip on the theme's scale instead\n // of a hand-written size that drifts the moment the theme moves.\n typography: 'overline',\n // `size=\"small\"` gives the chip the frame's 24px height, and `overline`\n // carries a 2.66 line-height meant for a standalone kicker with room\n // around it. Inside a fixed-height chip the box sets the height, so the\n // line collapses to it and the label centres.\n lineHeight: 1,\n },\n}\n\n/** The heading step the block has always emitted. */\nconst RELATED_DEFAULT_HEADING_VARIANT = 'h5'\n\n/** The card-title step the block has always emitted. */\nconst RELATED_DEFAULT_TITLE_VARIANT = 'subtitle1'\n\n/**\n * The instant the sample cards are dated. A FIXED one, never `Date.now()`:\n * the canvas has to render the same bytes every time it opens, and this is\n * the date the Date-format labels use as their worked example — so the\n * dropdown and the cards it previews agree word for word.\n */\nconst RELATED_SAMPLE_PUBLISHED_AT = { seconds: Date.UTC(2026, 7, 9) / 1000 }\n\n/**\n * Titles for the sample cards. Deliberately unmistakable as copy: the author\n * is looking at a LAYOUT, and a plausible-looking headline here is one an\n * author can mistake for a post that exists — or worse, design around.\n */\nconst RELATED_SAMPLE_TITLES = [\n 'Sample related post',\n 'A second sample post',\n 'A third sample post',\n]\n\n/** Category chip text for the sample cards; names the field, not a taxonomy. */\nconst RELATED_SAMPLE_CATEGORY = 'Category'\n\n/** Excerpt text for the sample cards; one line, so the card keeps its shape. */\nconst RELATED_SAMPLE_EXCERPT =\n 'Each post’s own excerpt reads here, trimmed to a line or two.'\n\n/**\n * Stand-in posts for editing surfaces (AGL-2486).\n *\n * Related posts are resolved FROM the routed entry, and a besigner canvas has\n * no routed entry — so the block used to draw a one-line dashed strip and the\n * author styled a layout they could not see. The sample is the block's REAL\n * markup, at the author's own settings, which is the only way the canvas can\n * answer \"what will this look like\".\n *\n * It exists only where {@link ScreenLinkContext.suppressNavigation} is on —\n * the besigner canvas and Preview. A published render reaches this function\n * on no path at all, so no visitor can ever be shown a post that does not\n * exist.\n *\n * The count follows the author's own `limit` so a 6-post rail at 3 columns\n * previews as two rows, and is bounded by the same ceiling the server\n * applies rather than a second one.\n */\nconst sampleRelatedEntries = (\n limit: number | string | undefined,\n dateFormat: Aglyn.CollectionEntryDateFormat,\n): Aglyn.CollectionRelatedItem[] => {\n const count = Math.min(\n // `|| default` also rejects 0, which `toCount` would otherwise accept and\n // preview as an empty rail — the one thing the sample exists to avoid.\n toCount(limit, Aglyn.COLLECTION_RELATED_DEFAULT_LIMIT) ||\n Aglyn.COLLECTION_RELATED_DEFAULT_LIMIT,\n Aglyn.COLLECTION_RELATED_MAX,\n )\n const date = Aglyn.formatCollectionEntryDate(\n RELATED_SAMPLE_PUBLISHED_AT,\n dateFormat,\n )\n return Array.from({ length: count }, (_, index) => ({\n title: RELATED_SAMPLE_TITLES[index] ?? `Sample related post ${index + 1}`,\n // Empty, not a plausible path: every surface that renders the sample\n // suppresses navigation, so the title is text rather than an anchor and\n // this is never read. A real-looking href would be a link to nowhere the\n // moment that stopped being true.\n url: '',\n date,\n category: RELATED_SAMPLE_CATEGORY,\n excerpt: RELATED_SAMPLE_EXCERPT,\n }))\n}\n\n/**\n * Lists other entries of the same collection sharing the current entry's\n * category or a tag (AGL-582). The tenant stamps `entries` at compose time\n * on entry renders; without them the published site renders nothing and an\n * editing surface renders sample cards (AGL-2486).\n *\n * Two layouts (AGL-1457). `list` is the plain-link list that has always\n * shipped and stays the default — the block is live on every blog entry, so\n * a new default would restyle published pages nobody asked about. `cards` is\n * the article frame's grid: cover, category chip, title, at an author-set\n * column count.\n *\n * The block owns its markup, so nothing inside a card is a node the Styles\n * panel can select. Every part it renders therefore needs an attribute, or\n * it is unauthorable by construction — which is what the type steps and the\n * `show*` switches are (AGL-2486).\n */\nconst CollectionRelated = forwardRef<HTMLDivElement, CollectionRelatedProps>(\n (props, ref) => {\n // `limit` and `dateFormat` are compose-time: the tenant resolves them\n // while stamping `entries`; strip them so they never hit the DOM. The\n // rest are read here, so they must not reach it either.\n const {\n heading,\n limit,\n entries,\n showCover,\n layout,\n columns,\n headingVariant,\n titleVariant,\n showDate,\n dateFormat,\n showCategory,\n showExcerpt,\n ...rest\n } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n // The resolver every other surface shares (AGL-1215): a stamped `media:`\n // reference becomes a CDN URL HERE, not in the document, so one\n // reference keeps working across sites. Called before the early return —\n // it is a hook.\n const { hostId } = Aglyn.useSite()\n // An editing surface has no routed entry, so nothing is stamped and the\n // block used to be the one part of the page that was not WYSIWYG. The\n // sample stands in for exactly that gap, and only there.\n const sample = !entries?.length && Boolean(suppressNavigation)\n const items = entries?.length\n ? entries\n : sample\n ? sampleRelatedEntries(\n limit,\n Aglyn.normalizeCollectionEntryDateFormat(dateFormat),\n )\n : []\n if (!items.length) return <Box ref={ref} {...rest} />\n const title = heading ?? 'Related articles'\n const headingNode = title ? (\n <Typography\n variant={(headingVariant || RELATED_DEFAULT_HEADING_VARIANT) as 'h5'}\n component=\"h2\"\n >\n {title}\n </Typography>\n ) : null\n /**\n * The card title, as a HEADING in the site theme.\n *\n * It used to be a bare `AppLink`, which is MUI's `Link` — `primary.main`\n * and `underline=\"always\"` — so the one thing a related card is FOR read\n * as a default browser link on a themed page, at no heading level, and\n * with no attribute able to touch it. The type step now comes from the\n * theme's own rungs and the anchor inherits the colour, exactly as the\n * Entry Author card's byline link does.\n *\n * `linked` is false wherever the whole card is already the anchor: an\n * `<a>` inside an `<a>` is invalid markup that browsers silently unnest.\n */\n const titleNode = (entry: Aglyn.CollectionRelatedItem, linked: boolean) => (\n <Typography\n variant={(titleVariant || RELATED_DEFAULT_TITLE_VARIANT) as 'subtitle1'}\n component=\"h3\"\n >\n {linked && !suppressNavigation && entry.url ? (\n <AppLink href={entry.url} sx={{ color: 'inherit' }} underline=\"hover\">\n {entry.title}\n </AppLink>\n ) : (\n entry.title\n )}\n </Typography>\n )\n const dateNode = (entry: Aglyn.CollectionRelatedItem) =>\n showDate !== false && entry.date ? (\n <Typography variant=\"caption\" sx={{ color: 'text.secondary' }}>\n {entry.date}\n </Typography>\n ) : null\n const excerptNode = (entry: Aglyn.CollectionRelatedItem) =>\n showExcerpt && entry.excerpt ? (\n <Typography variant=\"body2\" sx={{ color: 'text.secondary' }}>\n {entry.excerpt}\n </Typography>\n ) : null\n /**\n * The cover, when the author asked for one AND the entry has one. An\n * entry without a cover gets no box at all rather than a placeholder:\n * the related list is a real feed, and a row of grey rectangles is worse\n * than a row of titles.\n *\n * The sample cards are the exception, and for the opposite reason: they\n * stand in for posts that do not exist yet, so the cover SLOT is what\n * the author is looking at. A card grid previewing without its images is\n * not the grid being styled.\n */\n const coverNode = (entry: Aglyn.CollectionRelatedItem) => {\n const src = showCover\n ? Aglyn.resolveMediaSrc(entry.coverImage, { hostId })\n : undefined\n if (!src) {\n if (!sample || !showCover) return null\n return (\n <Box\n aria-hidden\n sx={{\n width: '100%',\n aspectRatio: RELATED_COVER_ASPECT,\n borderRadius: 1,\n backgroundColor: 'action.hover',\n }}\n />\n )\n }\n return (\n <Box\n component=\"img\"\n src={src}\n // The author's own description wins, and the title stays the\n // fallback (AGL-2418). Unlike the entry hero and the event\n // thumbnail — which render empty when nothing is authored — this\n // cover is the LINK's own content, so it must carry an accessible\n // name rather than go silent.\n alt={Aglyn.renderedMediaAlt(entry.coverImageAlt, entry.title)}\n sx={{\n display: 'block',\n width: '100%',\n aspectRatio: RELATED_COVER_ASPECT,\n objectFit: 'cover',\n borderRadius: 1,\n }}\n // `lazy` ALONE was the bug in miniature (AGL-2486): a lazy image\n // at default priority still outranks a lazy image at `low`, so\n // this related-entries rail — which sits at the bottom of an entry\n // by construction — was beating the deferred Image elements in the\n // body above it. The hint only works as a set.\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n )\n }\n\n /**\n * The notice that says these cards are not posts (AGL-2486). Editing\n * surfaces only, and outside the card markup rather than inside it, so\n * the author is styling the same tree the site will render.\n */\n const sampleNotice = sample ? (\n <Typography\n variant=\"caption\"\n sx={{\n display: 'block',\n p: 1,\n border: '1px dashed',\n borderColor: 'divider',\n borderRadius: 1,\n color: 'text.secondary',\n fontFamily: 'system-ui, sans-serif',\n }}\n >\n {'Sample posts — entries sharing this entry’s category or tags ' +\n 'replace these on the published page'}\n </Typography>\n ) : null\n\n if (layout === 'cards') {\n // `toCount` rounds and rejects junk; `|| default` also rejects 0, which\n // it would otherwise accept as a column count and emit `repeat(0, …)`.\n const columnCount =\n toCount(columns, RELATED_DEFAULT_COLUMNS) || RELATED_DEFAULT_COLUMNS\n /**\n * One card's contents, always with a PLAIN title: the caller wraps the\n * whole card in the anchor, because a reader aiming at a 180px cover\n * should not have to hit the line of text under it — and a second\n * anchor around the title inside that one is markup browsers unnest.\n */\n const card = (entry: Aglyn.CollectionRelatedItem) => (\n // 12px between cover, chip and title — the frame's rhythm (170:243\n // puts the chip at y=192 under a cover ending at 180, and the title\n // at y=228 under a chip ending at 216).\n <MuiStack spacing={1.5}>\n {coverNode(entry)}\n {showCategory !== false && entry.category ? (\n <Chip\n label={entry.category}\n size=\"small\"\n variant=\"outlined\"\n sx={relatedChipSx}\n />\n ) : null}\n {titleNode(entry, false)}\n {excerptNode(entry)}\n {dateNode(entry)}\n </MuiStack>\n )\n return (\n <Box\n ref={ref}\n {...rest}\n // MERGE, never replace (AGL-1450) — the node's slice arrives as the\n // ARRAY `mergeSxProps` builds in leaf.tsx, so folding it into an\n // object spreads numeric keys and discards every authored property\n // while these defaults still apply. Defaults first, node's after.\n sx={[\n {\n display: 'grid',\n gridTemplateColumns: `repeat(${columnCount}, minmax(0, 1fr))`,\n alignItems: 'start',\n columnGap: 3,\n rowGap: 4,\n },\n ...nodeSx,\n ]}\n >\n {headingNode ? (\n <Box sx={{ gridColumn: '1 / -1' }}>{headingNode}</Box>\n ) : null}\n {sampleNotice ? (\n <Box sx={{ gridColumn: '1 / -1' }}>{sampleNotice}</Box>\n ) : null}\n {items.map((entry, index) => {\n const linked = !suppressNavigation && Boolean(entry.url)\n return linked ? (\n <AppLink\n key={index}\n href={entry.url}\n underline=\"none\"\n // A grid ITEM, so it has to be a block; `color: inherit`\n // keeps the cover, chip and date reading as themed content\n // rather than as the inside of a link.\n sx={{ color: 'inherit', display: 'block' }}\n >\n {card(entry)}\n </AppLink>\n ) : (\n <Box key={index}>{card(entry)}</Box>\n )\n })}\n </Box>\n )\n }\n return (\n <MuiStack ref={ref} spacing={1.5} {...rest}>\n {headingNode}\n {sampleNotice}\n {items.map((entry, index) => {\n const meta = [\n showDate !== false ? entry.date : '',\n showCategory !== false ? entry.category : '',\n ].filter(Boolean)\n return (\n <MuiStack key={index} spacing={0.25}>\n {coverNode(entry)}\n {titleNode(entry, true)}\n {excerptNode(entry)}\n {meta.length ? (\n <Typography variant=\"caption\" sx={{ color: 'text.secondary' }}>\n {meta.join(' · ')}\n </Typography>\n ) : null}\n </MuiStack>\n )\n })}\n </MuiStack>\n )\n },\n)\nCollectionRelated.displayName = 'AglynCollectionRelated'\n\nexport const collectionRelatedSchema: Aglyn.ComponentSchema<CollectionRelatedProps> =\n {\n $id: RELATED_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Related Posts',\n description: \"Other entries sharing this one's category or tags.\",\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiNewspaperVariantOutline.path, sx: { color: 'secondary.main' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'heading',\n label: 'Heading',\n description: 'Section heading (blank hides it).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'limit',\n label: 'Limit',\n description: 'Most related posts listed (default 3).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'layout',\n label: 'Layout',\n description:\n 'List keeps the plain links this block has always rendered. ' +\n 'Cards is the article layout: cover, category, title, in a grid.',\n component: Aglyn.FieldComponentType.SELECT,\n // Both values are REAL (AGL-1451/AGL-1453): `''` cannot survive a\n // save, so an author who switched to Cards would have no route back.\n // `list` is also the value the render falls back to, so naming it is\n // the same choice, not a second one.\n options: [\n { value: 'list', label: 'List (default)' },\n { value: 'cards', label: 'Card grid' },\n ],\n },\n {\n name: 'columns',\n label: 'Columns',\n description: 'Cards per row in the card grid. Default 3.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n // Meaningless on the list, where each post is its own row.\n condition: { when: 'layout', is: 'cards' },\n },\n {\n name: 'showCover',\n label: 'Show cover',\n description:\n 'Show each post’s cover image. Posts without one show their ' +\n 'title alone rather than an empty box.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'headingVariant',\n label: 'Heading style',\n description:\n 'Which type step from the site theme the section heading above ' +\n 'the posts reads at. Default Heading 5.',\n component: Aglyn.FieldComponentType.SELECT,\n // The theme's own rungs, shared with the Typography element rather\n // than a second list here: two lists is how a step added to the\n // theme becomes reachable in one place and not the other.\n options: [...typographyVariants],\n },\n {\n name: 'titleVariant',\n label: 'Title style',\n description:\n 'Which type step from the site theme each post’s title reads at. ' +\n 'Default Subtitle 1; the title takes the surrounding text color ' +\n 'rather than the link color.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [...typographyVariants],\n },\n {\n name: 'showDate',\n label: 'Show date',\n description:\n 'Off drops each post’s published date. The posts stay in ' +\n 'newest-first order either way — this is about the card, not the ' +\n 'ordering.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n {\n name: 'dateFormat',\n label: 'Date format',\n description:\n 'How each post’s published date reads on its card. Site default ' +\n 'is the format this block has always used.',\n component: Aglyn.FieldComponentType.SELECT,\n // The formats the pure layer knows how to produce, so the list\n // cannot offer a shape nothing renders. Every value is REAL,\n // including the do-nothing one (AGL-1451/AGL-1453).\n options: [...Aglyn.COLLECTION_ENTRY_DATE_FORMAT_OPTIONS],\n },\n {\n name: 'showCategory',\n label: 'Show category',\n description:\n 'Off drops the category — the chip above each card title, or the ' +\n 'second half of the line under each link on the list layout.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showExcerpt',\n label: 'Show excerpt',\n description:\n 'Adds each post’s excerpt under its title. Posts with no excerpt ' +\n 'are unchanged rather than leaving a gap.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n ],\n }\n\n/* ── Share bar (AGL-582) ────────────────────────────────────────────────── */\n\nexport interface CollectionShareProps extends StackProps {\n /** Heading before the buttons; empty string hides it. */\n heading?: string\n}\n\nconst SHARE_TARGETS = [\n {\n label: 'Share on X',\n path: mdiTwitter.path,\n href: (url: string) =>\n `https://twitter.com/intent/tweet?url=${encodeURIComponent(url)}`,\n },\n {\n label: 'Share on LinkedIn',\n path: mdiLinkedin.path,\n href: (url: string) =>\n 'https://www.linkedin.com/sharing/share-offsite/?url=' +\n encodeURIComponent(url),\n },\n {\n label: 'Share on Facebook',\n path: mdiFacebook.path,\n href: (url: string) =>\n `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`,\n },\n]\n\n/**\n * Share buttons for the CURRENT page URL (AGL-582): X, LinkedIn, Facebook,\n * and copy-link. Pure client behavior — the URL is read at click time so\n * SSR and besigner renders stay markup-identical; editing surfaces no-op.\n */\nconst CollectionShare = forwardRef<HTMLDivElement, CollectionShareProps>(\n (props, ref) => {\n const { heading, ...rest } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n const [copied, setCopied] = useState(false)\n const title = heading ?? 'Share'\n const open = (buildHref: (url: string) => string) => () => {\n if (suppressNavigation || typeof window === 'undefined') return\n window.open(\n buildHref(window.location.href),\n '_blank',\n 'noopener,noreferrer',\n )\n }\n const copy = async () => {\n if (suppressNavigation || typeof window === 'undefined') return\n try {\n await navigator.clipboard.writeText(window.location.href)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n } catch {\n // Clipboard unavailable (permissions, http) — silently skip.\n }\n }\n return (\n <MuiStack\n ref={ref}\n direction=\"row\"\n spacing={0.5}\n {...rest}\n // MERGE, never replace (AGL-1450) — see Entry Meta below.\n sx={[{ alignItems: 'center' }, ...nodeSx]}\n >\n {title ? (\n // `component=\"p\"` because this labels a row of buttons; it is not a\n // section heading (AGL-2486). MUI's `defaultVariantMapping` sends\n // `subtitle2` to `<h6>`, so without this the Share Bar puts a level-6\n // heading into the document outline of every page carrying it. On a\n // blog post the nearest preceding heading is an `h2`, which makes it\n // a skipped level and a `heading-order` failure.\n //\n // Said here rather than as a theme-wide `variantMapping` override: a\n // default would silently change the element under every `subtitle2`\n // in the codebase, including surfaces where an `h6` is correct.\n <Typography component=\"p\" variant=\"subtitle2\" sx={{ mr: 1 }}>\n {title}\n </Typography>\n ) : null}\n {SHARE_TARGETS.map((target) => (\n <IconButton\n key={target.label}\n aria-label={target.label}\n size=\"small\"\n onClick={open(target.href)}\n >\n <MdiIcon path={target.path} />\n </IconButton>\n ))}\n <IconButton\n aria-label={copied ? 'Link copied' : 'Copy link'}\n size=\"small\"\n color={copied ? 'success' : 'default'}\n onClick={copy}\n >\n <MdiIcon path={mdiContentCopy.path} />\n </IconButton>\n </MuiStack>\n )\n },\n)\nCollectionShare.displayName = 'AglynCollectionShare'\n\nexport const collectionShareSchema: Aglyn.ComponentSchema<CollectionShareProps> =\n {\n $id: SHARE_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Share Bar',\n description: 'Share buttons for the current page, plus a copy link.',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiShareVariant.path, sx: { color: 'secondary.main' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'heading',\n label: 'Heading',\n description: 'Text before the buttons (blank hides it).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n ],\n }\n\n/* ── Entry meta (AGL-582) ───────────────────────────────────────────────── */\n\nexport interface CollectionEntryMetaProps extends StackProps {\n /**\n * Published date. Server-filled from the routed entry on entry templates\n * (`expandCollectionEntryMeta`, AGL-1385); set it — or bind\n * `{{entry.date}}` — only to override.\n */\n date?: string\n /**\n * How {@link date} reads when the server fills it in (AGL-1459) — a\n * COMPOSE-TIME prop, like Related Posts' `limit`: it is answered where the\n * timestamp still exists (`expandCollectionEntryMeta`) and never reaches the\n * DOM. Blank, or `default`, is the locale date the block has always emitted.\n */\n dateFormat?: Aglyn.CollectionEntryDateFormat\n /**\n * Byline (AGL-1459). Server-filled from the entry's own author on entry\n * templates, exactly like {@link date}; set it — or bind `{{entry.author}}`\n * — only to override.\n */\n author?: string\n /** Category; server-filled, or bind `{{entry.category}}` to override. */\n category?: string\n /** Comma-joined tags; server-filled, or bind `{{entry.tags}}`. */\n tags?: string\n /**\n * Round avatar shown before the byline (AGL-1459) — a media-picker target,\n * so it holds a media reference (AGL-1215) as well as any URL form.\n *\n * A block-level pick, deliberately, and NOT a per-author image: entries\n * carry an author NAME (`authorName`) and no portrait field, so a per-author\n * avatar would need a schema decision on the entry model plus an editor\n * field to fill it. Until then this is the site's brand mark, chosen once on\n * the template — which is what the article frame actually asks for. Left\n * unset it renders nothing at all, never a broken image.\n */\n avatarImage?: string\n showDate?: boolean\n showAuthor?: boolean\n /**\n * Where the byline's NAME links — the author's page on this site\n * (AGL-2519). Server-filled from the routed entry's author, like the\n * avatar beside it; blank renders the name as plain text.\n *\n * Distinct from the author's own `url`, which the Entry Author card offers\n * as a link row: this is \"everything they wrote here\", that is \"their\n * site\", and a byline that conflated them would send a reader off-site from\n * every article.\n */\n authorPageUrl?: string\n /** Off leaves the byline as plain text even when the author has a page. */\n linkAuthor?: boolean\n showCategory?: boolean\n showTags?: boolean\n showAvatar?: boolean\n}\n\n/**\n * THE SAMPLE VALUES THE ENTRY BLOCKS PREVIEW WITH (AGL-2486).\n *\n * Entry Meta and Entry Author resolve FROM the routed entry, and a besigner\n * canvas has no routed entry — so both drew a one-line dashed strip and an\n * author styling a byline or an author card was styling something they could\n * not see. Related Posts had the same gap and was answered the same way: the\n * block's REAL markup, at the author's own settings, on editing surfaces\n * only.\n *\n * These two need no \"this is a sample\" notice the way the related CARDS do.\n * A card carries a title, a cover and a category, which is what a real post\n * looks like; a byline reading `Sample author` says what it is in the words\n * themselves, and a notice above a single line of caption text would be\n * taller than the thing it labels.\n *\n * The date is a FIXED instant, never `Date.now()`: the canvas has to render\n * the same bytes every time it opens, and it is the same instant the related\n * sample uses, so two blocks on one template never disagree about what day\n * their example is.\n */\nconst ENTRY_SAMPLE_PUBLISHED_AT = RELATED_SAMPLE_PUBLISHED_AT\n\n/** Byline name for the samples; names the field rather than a person. */\nconst ENTRY_SAMPLE_AUTHOR = 'Sample author'\n\n/** Category text for the samples; names the field, not a taxonomy. */\nconst ENTRY_SAMPLE_CATEGORY = RELATED_SAMPLE_CATEGORY\n\n/** Two chips, so the row previews its own wrapping and gap. */\nconst ENTRY_SAMPLE_TAGS = 'first tag, second tag'\n\n/** Bio line for the author-card sample; one sentence, as a real one is. */\nconst ENTRY_SAMPLE_BIO =\n 'The bio from this author’s record reads here, in a line or two.'\n\n/**\n * The portrait STAND-IN, for a sample that has no image to resolve.\n *\n * A neutral plate rather than nothing, and for the reason the related sample\n * draws its cover slot: on an entry template the portrait arrives from the\n * author record at render, so the slot is exactly what the author is sizing\n * and spacing. `aria-hidden` because it depicts nothing.\n */\nconst samplePortraitSx = (size: number) => ({\n width: size,\n height: size,\n flexShrink: 0,\n borderRadius: '50%',\n backgroundColor: 'action.hover',\n})\n\n/** Byline avatar, from the article frame (Figma 170:190). */\nconst ENTRY_AVATAR_SIZE = 36\n\n/** Unresolved tokens render empty on the site, literal in the besigner. */\nconst metaValue = (\n value: string | undefined,\n suppressNavigation: boolean | undefined,\n): string => {\n const trimmed = (value ?? '').trim()\n if (!trimmed) return ''\n if (UNRESOLVED_TOKEN.test(trimmed) && !suppressNavigation) return ''\n return trimmed\n}\n\n/**\n * \"{{entry.date}} · {{entry.category}}\" meta line plus tag chips\n * (AGL-582). Values arrive through entry tokens on entry renders; on other\n * surfaces unresolved tokens collapse to nothing instead of leaking.\n *\n * On an entry template the tenant now also FILLS the three values from the\n * routed entry when nothing is bound (`expandCollectionEntryMeta`, AGL-1385).\n * Before that, a block dragged from the palette rather than dropped as the\n * preset had no values at all — three \"Show\" switches gating nothing — and\n * rendered as the empty `<Box>` below, at height 0.\n */\nconst CollectionEntryMeta = forwardRef<\n HTMLDivElement,\n CollectionEntryMetaProps\n>((props, ref) => {\n const {\n date,\n // Compose-time (AGL-1459): read by `expandCollectionEntryMeta`, which has\n // the timestamp. Destructured so it never reaches the DOM.\n dateFormat: _dateFormat,\n author,\n authorPageUrl,\n linkAuthor,\n category,\n tags,\n avatarImage,\n showDate,\n showAuthor,\n showCategory,\n showTags,\n showAvatar,\n ...rest\n } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n // The resolver every other surface shares (AGL-1215), so one media\n // reference keeps working across sites. A hook — before any early return.\n const { hostId } = Aglyn.useSite()\n const dateValue = showDate !== false ? metaValue(date, suppressNavigation) : ''\n const authorValue =\n showAuthor !== false ? metaValue(author, suppressNavigation) : ''\n const categoryValue =\n showCategory !== false ? metaValue(category, suppressNavigation) : ''\n const tagsValue = showTags !== false ? metaValue(tags, suppressNavigation) : ''\n const tagList = tagsValue\n .split(',')\n .map((tag) => tag.trim())\n .filter(Boolean)\n // An unresolved token empties on EVERY surface here, unlike the text\n // fields: a literal `{{…}}` in a src is a broken image in the canvas, and a\n // byline that renders a broken avatar on every post is worse than no\n // avatar at all.\n const avatarRaw = (avatarImage ?? '').trim()\n const avatarSrc =\n showAvatar !== false && avatarRaw && !UNRESOLVED_TOKEN.test(avatarRaw)\n ? Aglyn.resolveMediaSrc(avatarRaw, { hostId })\n : ''\n // Author leads, so the frame's \"The Aglyn Team · Jul 2026\" reads in that\n // order. With no author the join is character-for-character what it was.\n const filledLine = [authorValue, dateValue, categoryValue]\n .filter(Boolean)\n .join(' · ')\n /*\n The rest of the line, WITHOUT the author (AGL-2519).\n\n The row is one string everywhere else, which is what a `·`-joined byline\n wants — but a link is an element, so the author has to come out of the\n join to be one. Split rather than reassembled from parts, so the\n separator, the order and the empty-field collapsing all stay in the one\n expression above: the linked row and the plain row cannot render a\n different sentence.\n */\n const authorHrefRaw = metaValue(authorPageUrl, suppressNavigation)\n const authorHref =\n linkAuthor !== false && SAFE_AUTHOR_HREF.test(authorHrefRaw)\n ? authorHrefRaw\n : ''\n const trailingLine = [dateValue, categoryValue].filter(Boolean).join(' · ')\n // Nothing to show and nothing to route from: an editing surface previews\n // the block's own markup at the author's own settings, and a published page\n // renders nothing at all (AGL-2486). The switches are honoured here exactly\n // as they are below, so a row with Show date off previews without one.\n const sample = !filledLine && !tagList.length && !avatarSrc && suppressNavigation\n const line = sample\n ? [\n showAuthor !== false ? ENTRY_SAMPLE_AUTHOR : '',\n showDate !== false\n ? Aglyn.formatCollectionEntryDate(\n ENTRY_SAMPLE_PUBLISHED_AT,\n Aglyn.normalizeCollectionEntryDateFormat(_dateFormat),\n )\n : '',\n showCategory !== false ? ENTRY_SAMPLE_CATEGORY : '',\n ]\n .filter(Boolean)\n .join(' · ')\n : filledLine\n const chips = sample\n ? showTags !== false\n ? ENTRY_SAMPLE_TAGS.split(',').map((tag) => tag.trim())\n : []\n : tagList\n // The portrait an entry's own author record supplies arrives at compose\n // time, so on the canvas there is none to resolve — the slot is drawn\n // instead, which is what a byline's spacing is actually built around.\n const samplePortrait = sample && showAvatar !== false\n if (!line && !chips.length && !avatarSrc && !samplePortrait) {\n return <Box ref={ref} {...rest} />\n }\n return (\n <MuiStack\n ref={ref}\n direction=\"row\"\n spacing={1}\n {...rest}\n // MERGE, never replace (AGL-1450, same class as AGL-1284). `rest.sx`\n // is the ARRAY `mergeSxProps` builds in leaf.tsx, so spreading it into\n // an object produced `{0: …, 1: …, 2: …}` — numeric keys emotion emits\n // as invalid selectors — and discarded EVERY authored property while\n // these two defaults still applied. The block looked deliberately\n // styled, so nothing suggested the value had been dropped. Defaults\n // go first; the node's slice comes after and can override them.\n sx={[{ alignItems: 'center', flexWrap: 'wrap' }, ...nodeSx]}\n >\n {avatarSrc ? (\n <Box\n component=\"img\"\n src={avatarSrc}\n // Decorative: the byline names the author in text right beside it,\n // so a screen reader announcing the mark again is noise.\n alt=\"\"\n sx={{\n display: 'block',\n width: ENTRY_AVATAR_SIZE,\n height: ENTRY_AVATAR_SIZE,\n borderRadius: '50%',\n objectFit: 'cover',\n // No background plate: a brand mark with a transparent ground\n // would sit on a grey disc nobody asked for.\n }}\n // `lazy` alone, same as the related rail above (AGL-2486). A\n // byline avatar is decorative and tiny; it has no business\n // outranking anything.\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n ) : samplePortrait ? (\n <Box aria-hidden sx={samplePortraitSx(ENTRY_AVATAR_SIZE)} />\n ) : null}\n {line ? (\n <Typography variant=\"caption\" sx={{ color: 'text.secondary' }}>\n {\n // Linked only on the real byline, never on the canvas sample: a\n // preview whose placeholder name navigates is a trap, and the\n // sample author has no page to go to.\n authorHref && authorValue && !sample ? (\n <>\n <AppLink\n href={authorHref}\n sx={{ color: 'inherit' }}\n underline=\"hover\"\n >\n {authorValue}\n </AppLink>\n {trailingLine ? ` · ${trailingLine}` : ''}\n </>\n ) : (\n line\n )\n }\n </Typography>\n ) : null}\n {chips.map((tag) => (\n <Chip key={tag} label={tag} size=\"small\" variant=\"outlined\" />\n ))}\n </MuiStack>\n )\n})\nCollectionEntryMeta.displayName = 'AglynCollectionEntryMeta'\n\nexport const collectionEntryMetaSchema: Aglyn.ComponentSchema<CollectionEntryMetaProps> =\n {\n $id: ENTRY_META_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Entry Meta',\n description:\n 'The byline row for an entry — author, date, category and tags.',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiTagOutline.path, sx: { color: 'secondary.main' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'date',\n label: 'Date',\n description:\n 'Blank shows the entry’s own published date on entry templates. ' +\n 'Type here (or bind {{entry.date}}) only to override it.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'dateFormat',\n label: 'Date format',\n description:\n 'How the published date reads. Site default is the format this ' +\n 'block has always used; it has no effect on a Date you typed in ' +\n 'yourself.',\n component: Aglyn.FieldComponentType.SELECT,\n // The formats the pure layer knows how to produce, so the list cannot\n // offer a shape nothing renders. Every value is REAL — including the\n // do-nothing one (AGL-1451/AGL-1453): `''` cannot survive a save, so\n // an author who tried a format would have no route back.\n options: [...Aglyn.COLLECTION_ENTRY_DATE_FORMAT_OPTIONS],\n },\n {\n name: 'author',\n label: 'Author',\n description:\n 'Blank shows the entry’s own author. Type here (or bind ' +\n '{{entry.author}}) only to override it.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'category',\n label: 'Category',\n description:\n 'Blank shows the entry’s own category. Type here (or bind ' +\n '{{entry.category}}) only to override it.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'tags',\n label: 'Tags',\n description:\n 'Comma-separated. Blank shows the entry’s own tags; type here (or ' +\n 'bind {{entry.tags}}) only to override them.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'avatarImage',\n label: 'Avatar',\n description:\n 'Round image before the byline — your brand mark, or any image ' +\n 'from the media library. Blank shows no avatar.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'showDate',\n label: 'Show date',\n description:\n 'Off hides the published date from the line without clearing what ' +\n 'the Date field would have shown.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showCategory',\n label: 'Show category',\n description:\n 'Off drops the category from the line. The entry keeps its ' +\n 'category — this is about the byline, not the taxonomy.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showTags',\n label: 'Show tags',\n description:\n 'Off hides the tag chips below the line. Leave it on and the ' +\n 'other three off for a tags-only row.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n {\n name: 'authorPageUrl',\n label: 'Author links to',\n description:\n 'Blank sends the byline to the author’s page on this site. Type ' +\n 'here (or bind {{entry.authorPageUrl}}) only to override it.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'linkAuthor',\n label: 'Link author',\n description:\n 'Off leaves the byline as plain text even when the author has a ' +\n 'page. On, the name links there and the date and category beside ' +\n 'it do not.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means LINKED in the renderer above, so the switch has to open\n // in that position or it lies about the block in front of the author\n // (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showAuthor',\n label: 'Show author',\n description:\n 'Off hides the byline. The avatar follows it: a block with no ' +\n 'author shown never fills in the author’s portrait.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showAvatar',\n label: 'Show avatar',\n description:\n 'Off hides the round image in front of the byline, including an ' +\n 'author’s own portrait.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n ],\n }\n\n/* ── Entry author card (AGL-2486) ───────────────────────────────────────── */\n\nexport interface CollectionEntryAuthorProps extends StackProps {\n /**\n * The byline. Server-filled from the routed entry's author record on entry\n * templates (`expandCollectionEntryAuthor`), exactly like Entry Meta's\n * fields; set it — or bind `{{entry.author}}` — only to override.\n */\n name?: string\n /**\n * The author's blurb, from their record's `bio`; bind\n * `{{entry.authorBio}}` to override.\n */\n bio?: string\n /**\n * Portrait or logo — a media reference (AGL-1215) or any URL. Server-filled\n * from the author record, so the face changes with the byline instead of\n * being picked once on the template the way Entry Meta's avatar is.\n */\n image?: string\n /** The author's own site; offered as a link row beside the profile ones. */\n url?: string\n /**\n * The author's page on THIS site (AGL-2519), server-filled from the routed\n * entry. The name links here.\n *\n * It wins over {@link url} for the NAME, and the ordering is the point: a\n * reader who clicks a byline at the end of an article is asking \"what else\n * has this person written here\", not \"take me off this site\". Their own\n * url is still reachable — it is one of the link rows — so nothing is lost\n * by giving the name the destination that answers the question asked.\n */\n pageUrl?: string\n /**\n * The author's profile links, server-filled from their record (AGL-2516).\n *\n * Not authorable as text: each row carries a platform or a picked icon, and\n * the console's author editor is where those are chosen. `Show links` is\n * the template's control over them.\n */\n links?: Aglyn.ContentAuthorLink[]\n showBio?: boolean\n showAvatar?: boolean\n showLinks?: boolean\n}\n\n/** The card's portrait, larger than the byline's 36px mark (Figma 170:190). */\nconst AUTHOR_AVATAR_SIZE = 48\n\n/**\n * The mark for each platform the author model knows (AGL-2516).\n *\n * Imported rather than resolved through the icon catalog, because this set is\n * CLOSED and known at build time — the catalog is ~2.9 MB and only picker\n * surfaces load it, so a renderer that reached for it to draw ten glyphs\n * would ship the whole thing to every visitor of every article.\n *\n * Keyed by the registry's `icon`, so adding a platform is one entry there and\n * one import here, and a platform whose mark was never imported falls through\n * to the generic link glyph rather than rendering nothing.\n */\nconst AUTHOR_LINK_ICON_PATHS: Record<string, string> = {\n twitter: mdiTwitter.path,\n linkedin: mdiLinkedin.path,\n github: mdiGithub.path,\n mastodon: mdiMastodon.path,\n youtube: mdiYoutube.path,\n instagram: mdiInstagram.path,\n facebook: mdiFacebook.path,\n web: mdiWeb.path,\n email: mdiEmail.path,\n rss: mdiRss.path,\n}\n\n/**\n * What one link row draws.\n *\n * A known platform takes its mark from the table above. A CUSTOM link takes\n * the path the console stored beside the picked id (AGL-1212) — and falls back\n * to a generic link glyph rather than to nothing, because a row with a label\n * and a url is still a working link whose icon simply failed to resolve.\n */\nfunction authorLinkIconPath(link: Aglyn.ContentAuthorLink): string {\n const platform = Aglyn.authorLinkPlatform(link.platform)\n if (platform) {\n return AUTHOR_LINK_ICON_PATHS[platform.icon] ?? mdiLinkVariant.path\n }\n return (link.iconPath ?? '').trim() || mdiLinkVariant.path\n}\n\n/**\n * Schemes a rendered author link may use — the model's own guard, restated at\n * the boundary that produces the `href`.\n *\n * `normalizeContentAuthorLinks` already drops everything else, and this is\n * deliberately not trusting that: the props on this component are authorable,\n * so a link can reach the renderer without ever passing through the store's\n * normalizer. Two checks, because only one of them is on the path an attacker\n * would use.\n */\nconst SAFE_AUTHOR_LINK_HREF = /^(https:\\/\\/|mailto:)/i\n\n/**\n * What the byline may link to — an absolute `https:` page, or a route on this\n * site. The Social Links block guards its hrefs the same way and for the same\n * reason: the value comes from a stored record, so `javascript:` and friends\n * have to be unreachable rather than merely unlikely.\n */\nconst SAFE_AUTHOR_HREF = /^(https:\\/\\/|\\/(?!\\/))/i\n\n/** An off-site author page opens in a new tab; a route on this site does not. */\nconst EXTERNAL_AUTHOR_HREF = /^https:\\/\\//i\n\n/**\n * The author's profile links as a row of marks (AGL-2516).\n *\n * Shared by the article's Entry Author card and the Author Profile block on\n * the author's own page (AGL-2518) — the one part of the two that would\n * genuinely drift, since an icon row is where the accessible-name rule and\n * the `rel` on an off-site profile live.\n *\n * Renders nothing for an empty list, so a caller can drop it in\n * unconditionally rather than guarding at every call site.\n */\nfunction AuthorLinkRows(props: {\n links: readonly Aglyn.ContentAuthorLink[]\n /** Glyph scale; the profile draws them a step larger than the card. */\n size?: number\n}) {\n if (!props.links.length) return null\n return (\n <MuiStack direction=\"row\" spacing={0.5} sx={{ flexWrap: 'wrap', ml: -1 }}>\n {props.links.map((link, index) => {\n const label = Aglyn.authorLinkLabel(link)\n return (\n <AppLink\n key={index}\n componentVariant=\"icon-button\"\n href={(link.url ?? '').trim()}\n // The name IS the icon here, so it has to be announced — a row of\n // unlabelled icon buttons is a row a screen reader reads as\n // \"link, link, link\".\n aria-label={label}\n title={label}\n size=\"small\"\n sx={{ color: 'text.secondary' }}\n // A profile lives off this site by definition, and `mailto:` must\n // not replace the article either.\n target=\"_blank\"\n rel=\"noopener noreferrer me\"\n >\n <MdiIcon path={authorLinkIconPath(link)} size={props.size ?? 0.85} />\n </AppLink>\n )\n })}\n </MuiStack>\n )\n}\n\n/**\n * The rows a card will actually draw, guarded at the boundary.\n *\n * These arrive as PROPS, so a link can reach a renderer without ever passing\n * through the store's normalizer — the reason the byline's own `url` is\n * checked twice as well.\n */\nfunction safeAuthorLinks(\n links: readonly Aglyn.ContentAuthorLink[] | undefined,\n show: boolean | undefined,\n): Aglyn.ContentAuthorLink[] {\n if (show === false) return []\n return (links ?? []).filter((link) =>\n SAFE_AUTHOR_LINK_HREF.test((link?.url ?? '').trim()),\n )\n}\n\n/**\n * The author card that closes an article (AGL-2486) — portrait, byline, bio.\n *\n * Entry Meta prints a NAME beside a date, which is all an entry carried when\n * it was written: one free-typed string. Custom authors made the byline a\n * record with a portrait, a bio and a url, and a template that wanted the\n * card the article frame draws still had to type the name and the blurb in as\n * literal text — text that keeps saying whatever it said under posts somebody\n * else wrote, and that no edit to the author record can ever reach. This is\n * that card, filled from the record.\n *\n * Every field is independently overridable and every one of them collapses\n * when empty: an author with no portrait renders the text alone rather than a\n * gap where a face would be, and an entry with no author at all renders\n * nothing — never an empty bordered box.\n */\nconst CollectionEntryAuthor = forwardRef<\n HTMLDivElement,\n CollectionEntryAuthorProps\n>((props, ref) => {\n // None of these are DOM attributes (`name` least of all), so they are\n // destructured rather than spread — the stack.ts pattern.\n const {\n name,\n bio,\n image,\n url,\n pageUrl,\n links,\n showBio,\n showAvatar,\n showLinks,\n ...rest\n } = props\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n const { hostId } = Aglyn.useSite()\n const nameValue = metaValue(name, suppressNavigation)\n const bioValue = showBio !== false ? metaValue(bio, suppressNavigation) : ''\n // Their page here first, their own site second (AGL-2519) — see `pageUrl`.\n const hrefRaw = metaValue(pageUrl, suppressNavigation) ||\n metaValue(url, suppressNavigation)\n const urlValue = SAFE_AUTHOR_HREF.test(hrefRaw) ? hrefRaw : ''\n // An unresolved token empties on EVERY surface, as in the byline: a literal\n // `{{…}}` in a src is a broken portrait in the canvas.\n const imageRaw = (image ?? '').trim()\n const imageSrc =\n showAvatar !== false && imageRaw && !UNRESOLVED_TOKEN.test(imageRaw)\n ? Aglyn.resolveMediaSrc(imageRaw, { hostId })\n : ''\n // Nothing to show and nothing to route from: an editing surface previews\n // the card's own markup at the author's own settings, and a published page\n // renders nothing at all (AGL-2486). Every part still answers its own\n // switch, so a card with Show bio off previews as portrait and name.\n const sample = !nameValue && !bioValue && !imageSrc && Boolean(suppressNavigation)\n const displayName = sample ? ENTRY_SAMPLE_AUTHOR : nameValue\n const displayBio = sample && showBio !== false ? ENTRY_SAMPLE_BIO : bioValue\n // The record's portrait arrives at compose time, so there is none to\n // resolve on the canvas — the card draws its slot, which is the thing its\n // spacing is built around.\n const samplePortrait = sample && showAvatar !== false\n /**\n * The rows this card will actually draw (AGL-2516).\n *\n * Guarded here rather than trusted from the store: these arrive as PROPS,\n * so a link can reach the renderer without passing the record normalizer,\n * and `javascript:` has to be unreachable rather than merely unlikely — the\n * same reason the byline's own `url` is checked twice.\n */\n const linkRows = safeAuthorLinks(links, showLinks)\n if (\n !displayName &&\n !displayBio &&\n !imageSrc &&\n !samplePortrait &&\n !linkRows.length\n ) {\n return <Box ref={ref} {...rest} />\n }\n return (\n <MuiStack\n ref={ref}\n direction=\"row\"\n spacing={2}\n {...rest}\n // MERGE, never replace (AGL-1450): `rest.sx` is the array leaf.tsx\n // builds, and spreading it into an object drops every authored value.\n sx={[\n {\n alignItems: 'flex-start',\n p: 2.5,\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n },\n ...nodeSx,\n ]}\n >\n {imageSrc ? (\n <Box\n component=\"img\"\n src={imageSrc}\n // Decorative: the card names the author in text right beside it.\n alt=\"\"\n sx={{\n display: 'block',\n flexShrink: 0,\n width: AUTHOR_AVATAR_SIZE,\n height: AUTHOR_AVATAR_SIZE,\n borderRadius: '50%',\n objectFit: 'cover',\n }}\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n ) : samplePortrait ? (\n <Box aria-hidden sx={samplePortraitSx(AUTHOR_AVATAR_SIZE)} />\n ) : null}\n <MuiStack spacing={0.5} sx={{ minWidth: 0 }}>\n {displayName ? (\n <Typography component=\"p\" variant=\"subtitle2\">\n {urlValue ? (\n <AppLink\n href={urlValue}\n sx={{ color: 'inherit' }}\n underline=\"hover\"\n {...(EXTERNAL_AUTHOR_HREF.test(urlValue)\n ? { target: '_blank', rel: 'noopener noreferrer' }\n : {})}\n >\n {displayName}\n </AppLink>\n ) : (\n displayName\n )}\n </Typography>\n ) : null}\n {displayBio ? (\n <Typography variant=\"body2\" sx={{ color: 'text.secondary' }}>\n {displayBio}\n </Typography>\n ) : null}\n <AuthorLinkRows links={linkRows} />\n </MuiStack>\n </MuiStack>\n )\n})\nCollectionEntryAuthor.displayName = 'AglynCollectionEntryAuthor'\n\nexport const collectionEntryAuthorSchema: Aglyn.ComponentSchema<CollectionEntryAuthorProps> =\n {\n $id: ENTRY_AUTHOR_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Entry Author',\n description: 'The author card for an entry — portrait, byline and bio.',\n category: Aglyn.ComponentCategory.TEXT,\n icon: {\n path: mdiAccountCircleOutline.path,\n sx: { color: 'secondary.main' },\n },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'name',\n label: 'Name',\n description:\n 'Blank shows the name from the entry’s author record. Type here ' +\n '(or bind {{entry.author}}) to sign this card differently from ' +\n 'the byline above the article.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'bio',\n label: 'Bio',\n description:\n 'Blank shows the bio from the author’s record. Type here (or bind ' +\n '{{entry.authorBio}}) only to override it.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n {\n name: 'image',\n label: 'Portrait',\n description:\n 'Blank shows the portrait from the author’s record. Pick from your ' +\n 'media library with \"Browse media\" only to override it.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'pageUrl',\n label: 'Name links to',\n description:\n 'Blank sends the name to the author’s page on this site — ' +\n 'everything they wrote. Falls back to their own url when they have ' +\n 'no page; type here to override both.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'url',\n label: 'Their own site',\n description:\n 'Blank uses the url on the author’s record. Used only when they ' +\n 'have no page on this site for the name to link to.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'showLinks',\n label: 'Show links',\n description:\n 'Off hides the author’s profile links. They come from the author ' +\n 'record — a known platform brings its own mark, and a custom link ' +\n 'brings the icon and label chosen there.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to open\n // in that position or it lies about the card in front of the author\n // (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showBio',\n label: 'Show bio',\n description:\n 'Off leaves the card as a portrait and a name — for a masthead ' +\n 'where the blurb would repeat what the page already says.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showAvatar',\n label: 'Show portrait',\n description:\n 'Off leaves the text alone, with no space held for a picture.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to\n // OPEN in that position or it lies about the block in front of\n // the author — and the first click, which writes the `true` that\n // was already in effect, changes nothing and reads as a dead\n // control (AGL-2506).\n initialValue: true,\n },\n ],\n }\n\n/* ── Author profile (AGL-2518) ──────────────────────────────────────────── */\n\nexport interface ContentAuthorProfileProps extends StackProps {\n /**\n * The byline. Server-filled from the routed author on their own page\n * (`expandContentAuthorProfile`); set it — or bind `{{author.name}}` —\n * only to override.\n */\n name?: string\n /** The blurb from their record; bind `{{author.bio}}` to override. */\n bio?: string\n /**\n * Portrait or logo — a media reference (AGL-1215) or any URL, filled from\n * the record so the face follows the person rather than the template.\n */\n image?: string\n /** Person only — what they do; blank on an Organization. */\n jobTitle?: string\n /** Person only — who they write for. */\n worksFor?: string\n /**\n * Their OWN site. The name links here; blank renders it as plain text.\n * This block already IS the author's page, so it does not link to itself.\n */\n url?: string\n /** Their profile links, server-filled from the record (AGL-2516). */\n links?: Aglyn.ContentAuthorLink[]\n showBio?: boolean\n showAvatar?: boolean\n showRole?: boolean\n showLinks?: boolean\n}\n\n/** The page subject's portrait — a step up from the card's 48px. */\nconst PROFILE_AVATAR_SIZE = 96\n\n/**\n * Who an author page is about (AGL-2518) — portrait, name, role, bio, links.\n *\n * The Entry Author card renders the same person as a footnote under an\n * article. This renders them as the SUBJECT of a page, which is why it has\n * the two fields that card has no room for: `jobTitle` and `worksFor` are\n * what tell a stranger who they are looking at, and they are already on the\n * record because `schema.org/Person` defines them (AGL-2486).\n *\n * Every field is independently overridable and every one collapses when\n * empty, exactly as the card's do — an author with no portrait renders the\n * text alone rather than a gap where a face would be. On an editing surface\n * the block previews its own shape with sample text, because a template is\n * designed before any author is routed through it.\n */\nconst ContentAuthorProfile = forwardRef<\n HTMLDivElement,\n ContentAuthorProfileProps\n>((props, ref) => {\n // None of these are DOM attributes (`name` least of all), so they are\n // destructured rather than spread — the stack.ts pattern.\n const {\n name,\n bio,\n image,\n jobTitle,\n worksFor,\n url,\n links,\n showBio,\n showAvatar,\n showRole,\n showLinks,\n ...rest\n } = props\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n const { hostId } = Aglyn.useSite()\n const nameValue = metaValue(name, suppressNavigation)\n const bioValue = showBio !== false ? metaValue(bio, suppressNavigation) : ''\n const jobValue = showRole !== false ? metaValue(jobTitle, suppressNavigation) : ''\n const worksForValue =\n showRole !== false ? metaValue(worksFor, suppressNavigation) : ''\n const urlRaw = metaValue(url, suppressNavigation)\n const urlValue = SAFE_AUTHOR_HREF.test(urlRaw) ? urlRaw : ''\n // An unresolved token empties on EVERY surface, as in the byline: a literal\n // `{{…}}` in a src is a broken portrait in the canvas.\n const imageRaw = (image ?? '').trim()\n const imageSrc =\n showAvatar !== false && imageRaw && !UNRESOLVED_TOKEN.test(imageRaw)\n ? Aglyn.resolveMediaSrc(imageRaw, { hostId })\n : ''\n const linkRows = safeAuthorLinks(links, showLinks)\n // Nothing routed and nothing typed: an editing surface previews the block's\n // own markup at its own settings, and a published page renders nothing at\n // all. Every part still answers its own switch, so a block with Show bio\n // off previews as portrait and name.\n const sample =\n !nameValue &&\n !bioValue &&\n !jobValue &&\n !imageSrc &&\n !linkRows.length &&\n Boolean(suppressNavigation)\n const displayName = sample ? ENTRY_SAMPLE_AUTHOR : nameValue\n const displayBio = sample && showBio !== false ? ENTRY_SAMPLE_BIO : bioValue\n const samplePortrait = sample && showAvatar !== false\n /**\n * \"Founder at Aglyn\" — one line, from two fields, and a middle dot would be\n * wrong here: this is a phrase, not a list of facts like the byline's\n * `name · date`. Either field alone still reads.\n */\n const roleLine = [jobValue, worksForValue].filter(Boolean).join(' at ')\n if (\n !displayName &&\n !displayBio &&\n !roleLine &&\n !imageSrc &&\n !samplePortrait &&\n !linkRows.length\n ) {\n return <Box ref={ref} {...rest} />\n }\n return (\n <MuiStack\n ref={ref}\n direction=\"row\"\n spacing={3}\n {...rest}\n // MERGE, never replace (AGL-1450): `rest.sx` is the array leaf.tsx\n // builds, and spreading it into an object drops every authored value.\n sx={[{ alignItems: 'flex-start' }, ...nodeSx]}\n >\n {imageSrc ? (\n <Box\n component=\"img\"\n src={imageSrc}\n // Decorative: the block names the author in text right beside it.\n alt=\"\"\n sx={{\n display: 'block',\n flexShrink: 0,\n width: PROFILE_AVATAR_SIZE,\n height: PROFILE_AVATAR_SIZE,\n borderRadius: '50%',\n objectFit: 'cover',\n }}\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n ) : samplePortrait ? (\n <Box aria-hidden sx={samplePortraitSx(PROFILE_AVATAR_SIZE)} />\n ) : null}\n <MuiStack spacing={1} sx={{ minWidth: 0 }}>\n {displayName ? (\n // `h1` because the author IS this page's subject — the one heading\n // rule a built-in body has to get right, since the template around\n // it cannot know what the block will be handed.\n <Typography component=\"h1\" variant=\"h4\">\n {urlValue ? (\n <AppLink\n href={urlValue}\n sx={{ color: 'inherit' }}\n underline=\"hover\"\n {...(EXTERNAL_AUTHOR_HREF.test(urlValue)\n ? { target: '_blank', rel: 'noopener noreferrer' }\n : {})}\n >\n {displayName}\n </AppLink>\n ) : (\n displayName\n )}\n </Typography>\n ) : null}\n {roleLine ? (\n <Typography variant=\"subtitle2\" sx={{ color: 'text.secondary' }}>\n {roleLine}\n </Typography>\n ) : null}\n {displayBio ? (\n <Typography variant=\"body1\" sx={{ color: 'text.secondary' }}>\n {displayBio}\n </Typography>\n ) : null}\n <AuthorLinkRows links={linkRows} size={1} />\n </MuiStack>\n </MuiStack>\n )\n})\nContentAuthorProfile.displayName = 'AglynContentAuthorProfile'\n\nexport { ContentAuthorProfile }\n\nexport const contentAuthorProfileSchema: Aglyn.ComponentSchema<ContentAuthorProfileProps> =\n {\n $id: AUTHOR_PROFILE_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Author Profile',\n description:\n 'Who an author page is about — portrait, name, role, bio and links.',\n category: Aglyn.ComponentCategory.TEXT,\n icon: {\n path: mdiAccountCircleOutline.path,\n sx: { color: 'secondary.main' },\n },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'name',\n label: 'Name',\n description:\n 'Blank shows the name of the author whose page this is. Type here ' +\n '(or bind {{author.name}}) only to override it.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'bio',\n label: 'Bio',\n description:\n 'Blank shows the bio from the author’s record. Type here (or bind ' +\n '{{author.bio}}) only to override it.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n {\n name: 'image',\n label: 'Portrait',\n description:\n 'Blank shows the portrait of the author whose page this is, at the ' +\n 'larger size a profile takes. Pick from your media library only to ' +\n 'override it.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'jobTitle',\n label: 'Role',\n description:\n 'Blank shows the job title from the author’s record. Only a person ' +\n 'has one — an author record set to Organization leaves it empty.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'worksFor',\n label: 'Organization',\n description:\n 'Blank shows who the author writes for, from their record. Shown ' +\n 'after the role as \"Role at Organization\".',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'url',\n label: 'Link',\n description:\n 'Blank uses the author’s own url. The name links here; with no url ' +\n 'it renders as plain text. This block is already the author’s page ' +\n 'on this site, so it never links to itself.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'showLinks',\n label: 'Show links',\n description:\n 'Off hides the row of profile links under the bio — the place a ' +\n 'reader follows this author elsewhere. Turning it off on a page ' +\n 'about them is rarely what you want.',\n component: Aglyn.FieldComponentType.SWITCH,\n // Unset means SHOWN in the renderer above, so the switch has to open\n // in that position or it lies about the block in front of the author\n // (AGL-2506).\n initialValue: true,\n },\n {\n name: 'showBio',\n label: 'Show bio',\n description:\n 'Off leaves the block as a portrait, a name and a role.',\n component: Aglyn.FieldComponentType.SWITCH,\n initialValue: true,\n },\n {\n name: 'showRole',\n label: 'Show role',\n description:\n 'Off hides the \"Role at Organization\" line under the name.',\n component: Aglyn.FieldComponentType.SWITCH,\n initialValue: true,\n },\n {\n name: 'showAvatar',\n label: 'Show portrait',\n description:\n 'Off drops the portrait and lets the name and bio run the full ' +\n 'width, with no space held for a picture.',\n component: Aglyn.FieldComponentType.SWITCH,\n initialValue: true,\n },\n ],\n }\n\n/* ── Category pills (AGL-1321) ──────────────────────────────────────────── */\n\nexport interface CollectionCategoriesProps extends StackProps {\n /**\n * Collection whose taxonomy renders (compose-time). Blank = the collection\n * routed by the current URL on list-template screens.\n */\n collectionSlug?: string\n /**\n * Label for the unfiltered pill (default \"All\"). Clearing the field\n * persists `Aglyn.COLLECTION_ALL_PILL_NONE`, which omits the pill —\n * `''` cannot be stored at all (AGL-1336).\n */\n allLabel?: string\n /**\n * Server-stamped pill links (`expandCollectionCategories`); never set by\n * hand — the tenant builds them from the collection's categories and the\n * routed category.\n */\n items?: Aglyn.CollectionCategoryLink[]\n}\n\n/** Pill look, in palette tokens so it follows the site theme in both modes. */\nconst pillSx = (active: boolean) => ({\n px: 1.75,\n py: 0.75,\n borderRadius: 999,\n border: '1px solid',\n borderColor: active ? 'primary.main' : 'divider',\n bgcolor: active ? 'primary.main' : 'transparent',\n color: active ? 'primary.contrastText' : 'text.secondary',\n fontSize: 14,\n lineHeight: 1.2,\n whiteSpace: 'nowrap',\n '&:hover': {\n borderColor: active ? 'primary.main' : 'text.primary',\n color: active ? 'primary.contrastText' : 'text.primary',\n },\n})\n\n/**\n * The category filter row for a collection listing (AGL-1321): \"All\" plus one\n * pill per category, each a REAL anchor to `/{collection}/category/{slug}`.\n *\n * Anchors, not click handlers, and that is the whole design. A JS-only toggle\n * would be invisible to crawlers, unopenable in a new tab, unlinkable, and\n * unreachable without hydration — and it could not exist anyway, since the\n * filter is resolved server-side before the page is composed. The pill the\n * current URL selected carries `aria-current=\"page\"`, stamped on the server so\n * it is in the HTML rather than derived after hydration.\n */\nconst CollectionCategories = forwardRef<\n HTMLDivElement,\n CollectionCategoriesProps\n>((props, ref) => {\n // `collectionSlug`/`allLabel` are compose-time: the tenant resolves them\n // while stamping `items`; strip so they never hit the DOM.\n const { collectionSlug, allLabel, items, ...rest } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n if (!items?.length) {\n if (!suppressNavigation) return <Box ref={ref} {...rest} />\n return (\n <Box\n ref={ref}\n {...rest}\n sx={[\n {\n p: 2,\n border: '1px dashed',\n borderColor: 'divider',\n color: 'text.secondary',\n fontSize: 12,\n fontFamily: 'system-ui, sans-serif',\n },\n ...nodeSx,\n ]}\n >\n {'Category pills — All + one pill per collection category render here'}\n </Box>\n )\n }\n return (\n <MuiStack\n ref={ref}\n direction=\"row\"\n spacing={1}\n {...rest}\n // MERGE, never replace (AGL-1450) — see Entry Meta above.\n sx={[{ flexWrap: 'wrap', rowGap: 1 }, ...nodeSx]}\n >\n {items.map((item) =>\n // Editing surfaces render the pill look without an href, so a click\n // in the besigner never navigates the canvas away.\n suppressNavigation ? (\n <Box key={item.href} sx={pillSx(item.active)}>\n {item.label}\n </Box>\n ) : (\n <AppLink\n key={item.href}\n href={item.href}\n underline=\"none\"\n aria-current={item.active ? 'page' : undefined}\n sx={pillSx(item.active)}\n >\n {item.label}\n </AppLink>\n ),\n )}\n </MuiStack>\n )\n})\nCollectionCategories.displayName = 'AglynCollectionCategories'\n\nexport const collectionCategoriesSchema: Aglyn.ComponentSchema<CollectionCategoriesProps> =\n {\n $id: CATEGORIES_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Category Pills',\n description: 'A pill per collection category, each filtering the listing.',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiTagMultipleOutline.path, sx: { color: 'secondary.main' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'collectionSlug',\n label: 'Collection slug',\n description:\n 'Content collection whose categories render as pills (e.g. ' +\n '\"blog\"). Leave blank on a list-template screen to use the ' +\n 'collection from the URL.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'allLabel',\n label: 'All label',\n description:\n 'Label for the unfiltered pill, which links to the collection ' +\n 'root (default \"All\"). Clear the box to omit that pill — it then ' +\n 'reads \"none\", which is what makes the omission stick. Typing ' +\n '\"none\" does the same thing.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n // Without this the emptied field persists NOTHING (AGL-1336): ddf\n // maps an emptied value to `clearedValue`, final-form's parse turns\n // `''` into `undefined`, and the missing key takes the runtime\n // default — so \"clear it to omit that pill\" was undoable by\n // clicking. Same shape as FIELD_COLOR_ALT1's `'default'` (AGL-1191):\n // a value that means something has to BE something.\n //\n // ddf only substitutes `clearedValue` when the field HAD an initial\n // value (`enhancedOnChange`'s `typeof initial !== 'undefined'`),\n // which is why the preset below seeds `allLabel` and why the\n // sentinel is a word an author can also type. A block that never\n // carried the prop has an already-empty box and nothing to clear.\n clearedValue: Aglyn.COLLECTION_ALL_PILL_NONE,\n },\n ],\n }\n\n/* ── Collection Search (AGL-1516, Figma 494:1220) ───────────────────────── */\n\nexport interface CollectionSearchProps extends StackProps {\n /**\n * Collection whose entries this box searches (compose-time). Blank = the\n * collection routed by the current URL on list-template screens.\n */\n collectionSlug?: string\n /** Hint text inside the box (blank = \"Search posts…\"). */\n searchPlaceholder?: string\n /**\n * Server-stamped matchable text per entry (`expandCollectionSearch`);\n * never set by hand.\n */\n searchIndex?: Aglyn.CollectionEntrySearchItem[]\n /** How many entries the index was drawn from; never set by hand. */\n searchTotal?: number\n /** Whether that read reached its bound; never set by hand. */\n searchCapped?: boolean\n}\n\n/**\n * A search box for a collection that lives where the author puts it\n * (AGL-1516, Figma 494:1220).\n *\n * The entries block has had a search box since AGL-1516's first half, and it\n * renders inside that block — first child of its own stack. The frame puts\n * the field in the listing's TOOLBAR: category pills on the left, search and\n * RSS on the right, one row. That was unbuildable, and unbuildable in both\n * directions — a block's own child cannot be lifted out of it, and the pills\n * cannot be moved in, because every child of an entries block is cloned once\n * per entry. Three passes of authoring hit the same wall.\n *\n * So the field became its own block. It searches the collection the listing\n * beside it is drawn from, and answers with the suggestion panel (496:1218)\n * — the only behaviour a standalone box can honestly have, since it owns no\n * cards to hide. Enter submits to the site-wide results page, so the box\n * works with no JavaScript at all.\n *\n * Inert on editing surfaces, like Category Pills: the besigner has no index\n * to search, and a field that answered there would be answering about\n * nothing.\n */\nconst CollectionSearch = forwardRef<HTMLDivElement, CollectionSearchProps>(\n (props, ref) => {\n // Compose-time and search-only attributes: strip so they never hit the\n // DOM.\n const {\n collectionSlug,\n searchPlaceholder,\n searchIndex,\n searchTotal,\n searchCapped,\n ...rest\n } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n const fuzzy = useMemo(\n () =>\n searchIndex?.length\n ? new Fuse(searchIndex, { ...Aglyn.COLLECTION_SEARCH_FUSE_OPTIONS })\n : null,\n [searchIndex],\n )\n if (!suppressNavigation && !fuzzy) {\n // Nothing stamped on a live surface: an unknown collection, an empty\n // one, or a page composed before this block existed. Renders NOTHING\n // rather than a field that can only ever answer \"no matches\" — that\n // answer would read as a fact about the reader's query instead of\n // about the absent index behind it.\n return <Box ref={ref} {...rest} />\n }\n return (\n <MuiStack ref={ref} {...rest} sx={[{ alignItems: 'flex-end' }, ...nodeSx]}>\n <SuggestSearchBox\n fuzzy={fuzzy}\n {...(searchIndex ? { items: searchIndex } : {})}\n {...(searchPlaceholder === undefined\n ? {}\n : { placeholder: searchPlaceholder })}\n {...(suppressNavigation ? { inert: true } : {})}\n // The honest miss for THIS box. It searched one bounded read of the\n // collection — never \"the collection\", and on a category route not\n // even the whole of it, because the source arrives filtered. So it\n // reports the number it actually looked through, and says plainly\n // when that number was a ceiling rather than a total.\n emptyText={(text) =>\n searchCapped\n ? `No matches for “${text}” in the ${searchIndex?.length ?? 0} ` +\n 'entries searched here — the collection holds more, which ' +\n 'this box has not read.'\n : `No matches for “${text}” in the ${searchIndex?.length ?? 0} ` +\n 'entries searched here.'\n }\n />\n </MuiStack>\n )\n },\n)\nCollectionSearch.displayName = 'AglynCollectionSearch'\n\nexport const collectionSearchSchema: Aglyn.ComponentSchema<CollectionSearchProps> =\n {\n $id: SEARCH_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Collection Search',\n description:\n 'A search box for a content collection, with a suggestions dropdown.',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiMagnify.path, sx: { color: 'secondary.main' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'collectionSlug',\n label: 'Collection slug',\n description:\n 'Content collection this box searches (e.g. \"blog\"). Leave blank ' +\n 'on a list-template screen to use the collection from the URL.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'searchPlaceholder',\n label: 'Search placeholder',\n description:\n 'Hint text inside this box. Blank shows \"Search posts…\", which is ' +\n 'worth changing when the collection is not posts.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n // `searchIndex`, `searchTotal` and `searchCapped` are deliberately NOT\n // attributes: all three are server-stamped facts about a read, and an\n // author who could edit them could make the box lie about its own\n // reach.\n ],\n }\n\n/* ── Presets ────────────────────────────────────────────────────────────── */\n\n/**\n * Styling rides the node's own `sx`, never `props.sx` (AGL-1346): both\n * render, but only `node.sx` is the record the Styles panel can edit or\n * clear. `Leaf` composes `node.sx` last, so this is the same result.\n */\nconst entryText = (variant: string, children: string, extra?: object) => {\n const { sx, ...props } = (extra ?? {}) as Record<string, unknown>\n return {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: { variant, children, ...props },\n ...(sx ? { sx } : {}),\n }\n}\n\nexport const collectionPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ENTRIES_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Collection Entries',\n pluginId: BUNDLE_ID,\n description:\n 'Repeats a card (title, date, excerpt, Read more) per published ' +\n 'entry of a content collection',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiPostOutline.path, sx: { color: 'secondary.main' } },\n data: {\n $id: null,\n componentId: ENTRIES_ID,\n pluginId: BUNDLE_ID,\n props: { spacing: 4 },\n nodes: [\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { spacing: 0.5 },\n nodes: [\n entryText('h5', '{{entry.title}}', { component: 'h2' }),\n entryText('caption', '{{entry.date}}', {\n sx: { color: 'text.secondary' },\n }),\n entryText('body1', '{{entry.excerpt}}'),\n {\n $id: null,\n componentId: 'muiScreenLink',\n pluginId: BUNDLE_ID,\n props: {\n children: 'Read more',\n href: '{{entry.url}}',\n size: 'small',\n },\n sx: { alignSelf: 'flex-start' },\n },\n ],\n },\n ],\n },\n },\n {\n $id: generatePresetId(ENTRY_BODY_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Entry Body',\n pluginId: BUNDLE_ID,\n description: \"Renders the current entry's markdown body, themed\",\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiTextLong.path, sx: { color: 'secondary.main' } },\n data: {\n $id: null,\n componentId: ENTRY_BODY_ID,\n pluginId: BUNDLE_ID,\n props: { markdown: '{{entry.body}}' },\n },\n },\n {\n $id: generatePresetId(RELATED_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Related Posts',\n pluginId: BUNDLE_ID,\n description:\n \"Other entries sharing the current entry's category or tags\",\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiNewspaperVariantOutline.path, sx: { color: 'secondary.main' } },\n data: {\n $id: null,\n componentId: RELATED_ID,\n pluginId: BUNDLE_ID,\n // `layout`, the two type steps and `dateFormat` are seeded rather than\n // left to the runtime fallback so each dropdown opens on the value the\n // block is actually rendering, and an author who tries another has a\n // named route back (AGL-1457/AGL-1459). The `show*` switches are\n // deliberately absent: their unset state IS the shipped behaviour.\n props: {\n heading: 'Related articles',\n limit: 3,\n layout: 'list',\n headingVariant: 'h5',\n titleVariant: 'subtitle1',\n dateFormat: Aglyn.COLLECTION_ENTRY_DATE_FORMAT_DEFAULT,\n },\n },\n },\n {\n $id: generatePresetId(SHARE_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Share Bar',\n pluginId: BUNDLE_ID,\n description: 'X, LinkedIn, Facebook, and copy-link buttons for the page',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiShareVariant.path, sx: { color: 'secondary.main' } },\n data: {\n $id: null,\n componentId: SHARE_ID,\n pluginId: BUNDLE_ID,\n props: { heading: 'Share' },\n },\n },\n {\n $id: generatePresetId(CATEGORIES_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Category Pills',\n pluginId: BUNDLE_ID,\n description:\n \"Links to each of the collection's categories, filtering the listing\",\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiTagMultipleOutline.path, sx: { color: 'secondary.main' } },\n data: {\n $id: null,\n componentId: CATEGORIES_ID,\n pluginId: BUNDLE_ID,\n // Seeded, not left to the runtime default: an explicit initial value\n // is what lets the attributes form substitute the cleared sentinel\n // when the author empties the box (AGL-1336).\n props: { allLabel: Aglyn.COLLECTION_ALL_PILL_DEFAULT },\n },\n },\n {\n // Every author-placeable component needs a preset: the element drawer is\n // built from PRESETS alone (`ComponentManager.schemasByCategory` iterates\n // `this.presets`), so a component with only a schema renders correctly\n // once a node exists but can never be put on a canvas by clicking.\n //\n // This one earns its place in the drawer because the entries block's own\n // search field cannot leave that block, and the toolbar row wants the\n // search beside the category pills rather than above the entries.\n $id: generatePresetId(SEARCH_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Collection Search',\n pluginId: BUNDLE_ID,\n description:\n 'Search box for a collection, with a dropdown of matching entries',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiMagnify.path, sx: { color: 'secondary.main' } },\n data: {\n $id: null,\n componentId: SEARCH_ID,\n pluginId: BUNDLE_ID,\n // Deliberately no seeded props. Both attributes read blank as a real\n // choice — `collectionSlug` blank means \"take the collection from the\n // URL\", which is correct on the list templates this block is for, and\n // `searchPlaceholder` blank means the designed \"Search posts…\".\n props: {},\n },\n },\n {\n $id: generatePresetId(ENTRY_META_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Entry Meta',\n pluginId: BUNDLE_ID,\n description: 'Author · date · category line with tag chips for the entry',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiTagOutline.path, sx: { color: 'secondary.main' } },\n data: {\n $id: null,\n componentId: ENTRY_META_ID,\n pluginId: BUNDLE_ID,\n props: {\n date: '{{entry.date}}',\n // Named rather than left to the runtime fallback (AGL-1459): the\n // dropdown opens on the value the block is actually rendering, and\n // an author who tries a format has a named route back. `default` is\n // also the fallback, so naming it is the same choice, not a second.\n dateFormat: Aglyn.COLLECTION_ENTRY_DATE_FORMAT_DEFAULT,\n author: '{{entry.author}}',\n category: '{{entry.category}}',\n tags: '{{entry.tags}}',\n },\n },\n },\n {\n $id: generatePresetId(ENTRY_AUTHOR_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Entry Author',\n pluginId: BUNDLE_ID,\n description: 'Portrait, byline and bio card for the entry’s author',\n category: Aglyn.ComponentCategory.TEXT,\n icon: {\n path: mdiAccountCircleOutline.path,\n sx: { color: 'secondary.main' },\n },\n data: {\n $id: null,\n componentId: ENTRY_AUTHOR_ID,\n pluginId: BUNDLE_ID,\n // Seeded EMPTY, unlike Entry Meta's preset. Those bindings predate the\n // server fill and stay for compatibility; here the fill is the only\n // mechanism, and a seeded `{{entry.authorImage}}` would render a broken\n // portrait on every surface that is not an entry template.\n props: {},\n },\n },\n {\n $id: generatePresetId(AUTHOR_PROFILE_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Author Profile',\n pluginId: BUNDLE_ID,\n description: 'Portrait, name, role, bio and links for an author page',\n category: Aglyn.ComponentCategory.TEXT,\n icon: {\n path: mdiAccountCircleOutline.path,\n sx: { color: 'secondary.main' },\n },\n data: {\n $id: null,\n componentId: AUTHOR_PROFILE_ID,\n pluginId: BUNDLE_ID,\n // Seeded EMPTY, for the reason the card above it is: the server fill is\n // the only mechanism here, and a seeded `{{author.image}}` would render\n // a broken portrait on every surface that is not an author page.\n props: {},\n },\n },\n]\n\nexport {\n CollectionCategories,\n CollectionEntries,\n CollectionEntryAuthor,\n CollectionEntryBody,\n CollectionEntryMeta,\n CollectionRelated,\n CollectionSearch,\n CollectionShare,\n}\nexport default CollectionEntries\n"],"names":["Aglyn","mdiAccountCircleOutline","mdiContentCopy","mdiEmail","mdiFacebook","mdiGithub","mdiInstagram","mdiLinkVariant","mdiLinkedin","mdiMagnify","mdiMastodon","mdiNewspaperVariantOutline","mdiPostOutline","mdiRss","mdiShareVariant","mdiTagMultipleOutline","mdiTagOutline","mdiTextLong","mdiTwitter","mdiWeb","mdiYoutube","AppLink","MdiIcon","Fuse","Box","Chip","IconButton","InputBase","MuiLink","MuiStack","Typography","Children","forwardRef","useContext","useMemo","useState","BUNDLE_ID","generatePresetId","toCount","typographyVariants","ENTRIES_ID","COLLECTION_ENTRIES_COMPONENT_ID","ENTRY_BODY_ID","COLLECTION_ENTRY_BODY_COMPONENT_ID","RELATED_ID","COLLECTION_RELATED_COMPONENT_ID","SHARE_ID","COLLECTION_SHARE_COMPONENT_ID","ENTRY_META_ID","COLLECTION_ENTRY_META_COMPONENT_ID","ENTRY_AUTHOR_ID","COLLECTION_ENTRY_AUTHOR_COMPONENT_ID","CATEGORIES_ID","COLLECTION_CATEGORIES_COMPONENT_ID","SEARCH_ID","COLLECTION_SEARCH_COMPONENT_ID","AUTHOR_PROFILE_ID","CONTENT_AUTHOR_PROFILE_COMPONENT_ID","COLLECTION_SEARCH_MODE_OPTIONS","value","label","SEARCH_FIELD_WIDTH","SUGGESTION_PANEL_WIDTH","SUGGESTION_LIMIT","searchResultsHref","query","encodeURIComponent","searchFieldSx","alignSelf","display","alignItems","gap","px","py","borderRadius","bgcolor","color","width","maxWidth","suggestionAnchorSx","position","suggestionPanelSx","top","right","zIndex","border","borderColor","boxShadow","overflow","textAlign","suggestionRowLinkSx","suggestionFooterSx","borderTop","fontSize","fontWeight","SearchField","placeholder","inert","landmark","onQuery","onEscape","role","sx","path","name","trim","inputProps","flex","readOnly","onChange","event","target","onKeyDown","key","SuggestionPanel","suggestions","trimmed","emptyText","length","map","item","index","row","spacing","direction","variant","title","category","size","date","excerpt","textOverflow","whiteSpace","url","href","underline","SuggestSearchBox","fuzzy","items","setQuery","closed","setClosed","search","slice","result","refIndex","filter","Boolean","field","undefined","next","component","action","method","searchScope","perPage","shown","searchTotal","searchCapped","paginated","truncated","CollectionEntries","collectionSlug","entriesLimit","filterCategory","filterTag","page","firstPageOnly","searchMode","searchPlaceholder","searchIndex","children","props","ref","suppressNavigation","ScreenLinkContext","COLLECTION_SEARCH_FUSE_OPTIONS","childArray","toArray","groupSize","mode","live","visible","scopeNote","matched","Set","_","has","Math","floor","showField","text","displayName","collectionEntriesSchema","$id","pluginId","description","ComponentCategory","DATA_DISPLAY","icon","attributes","FieldComponentType","TEXT_FIELD","type","SWITCH","SELECT","options","option","condition","when","is","UNRESOLVED_TOKEN","renderInlines","inlines","links","strong","em","span","link","resolveMarkdownLink","kind","cursor","CollectionEntryBody","markdown","intrinsicSizes","rest","nodeSx","Array","isArray","screens","hostId","useSite","source","unresolved","test","blocks","parseMarkdownLite","slugs","markdownHeadingSlugs","p","fontFamily","block","id","level","gutterBottom","scrollMarginTop","HEADING_ANCHOR_SCROLL_MARGIN","mediaBodyImageAttributes","src","alt","height","my","DEFERRED_IMAGE_ATTRIBUTES","lineHeight","pl","itemIndex","li","start","overflowX","code","borderCollapse","thead","tr","header","cell","cellIndex","th","style","align","tbody","rows","rowIndex","td","mx","borderLeft","fontStyle","collectionEntryBodySchema","TEXT","flags","selfClosing","FEATURE_FLAG","ENABLED","RELATED_DEFAULT_COLUMNS","RELATED_COVER_ASPECT","relatedChipSx","typography","RELATED_DEFAULT_HEADING_VARIANT","RELATED_DEFAULT_TITLE_VARIANT","RELATED_SAMPLE_PUBLISHED_AT","seconds","Date","UTC","RELATED_SAMPLE_TITLES","RELATED_SAMPLE_CATEGORY","RELATED_SAMPLE_EXCERPT","sampleRelatedEntries","limit","dateFormat","count","min","COLLECTION_RELATED_DEFAULT_LIMIT","COLLECTION_RELATED_MAX","formatCollectionEntryDate","from","CollectionRelated","heading","entries","showCover","layout","columns","headingVariant","titleVariant","showDate","showCategory","showExcerpt","sample","normalizeCollectionEntryDateFormat","headingNode","titleNode","entry","linked","dateNode","excerptNode","coverNode","resolveMediaSrc","coverImage","aria-hidden","aspectRatio","backgroundColor","renderedMediaAlt","coverImageAlt","objectFit","sampleNotice","columnCount","card","gridTemplateColumns","columnGap","rowGap","gridColumn","meta","join","collectionRelatedSchema","initialValue","COLLECTION_ENTRY_DATE_FORMAT_OPTIONS","SHARE_TARGETS","CollectionShare","copied","setCopied","open","buildHref","window","location","copy","navigator","clipboard","writeText","setTimeout","mr","aria-label","onClick","collectionShareSchema","NAVIGATION","ENTRY_SAMPLE_PUBLISHED_AT","ENTRY_SAMPLE_AUTHOR","ENTRY_SAMPLE_CATEGORY","ENTRY_SAMPLE_TAGS","ENTRY_SAMPLE_BIO","samplePortraitSx","flexShrink","ENTRY_AVATAR_SIZE","metaValue","CollectionEntryMeta","_dateFormat","author","authorPageUrl","linkAuthor","tags","avatarImage","showAuthor","showTags","showAvatar","dateValue","authorValue","categoryValue","tagsValue","tagList","split","tag","avatarRaw","avatarSrc","filledLine","authorHrefRaw","authorHref","SAFE_AUTHOR_HREF","trailingLine","line","chips","samplePortrait","flexWrap","collectionEntryMetaSchema","AUTHOR_AVATAR_SIZE","AUTHOR_LINK_ICON_PATHS","twitter","linkedin","github","mastodon","youtube","instagram","facebook","web","email","rss","authorLinkIconPath","platform","authorLinkPlatform","iconPath","SAFE_AUTHOR_LINK_HREF","EXTERNAL_AUTHOR_HREF","AuthorLinkRows","ml","authorLinkLabel","componentVariant","rel","safeAuthorLinks","show","CollectionEntryAuthor","bio","image","pageUrl","showBio","showLinks","nameValue","bioValue","hrefRaw","urlValue","imageRaw","imageSrc","displayBio","linkRows","minWidth","collectionEntryAuthorSchema","TEXTAREA","PROFILE_AVATAR_SIZE","ContentAuthorProfile","jobTitle","worksFor","showRole","jobValue","worksForValue","urlRaw","roleLine","contentAuthorProfileSchema","pillSx","active","CollectionCategories","allLabel","aria-current","collectionCategoriesSchema","clearedValue","COLLECTION_ALL_PILL_NONE","CollectionSearch","collectionSearchSchema","entryText","extra","componentId","collectionPresets","NodeType","PRESET","BLOCKS","data","nodes","COLLECTION_ENTRY_DATE_FORMAT_DEFAULT","COLLECTION_ALL_PILL_DEFAULT"],"mappings":"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"}
@@ -157,7 +157,7 @@ export const schema = {
157
157
  $id: ID,
158
158
  pluginId: BUNDLE_ID,
159
159
  displayName: 'Inline Text',
160
- description: 'One run of text inside a sentence — put several side by side in a ' + 'paragraph to emphasise a phrase without breaking the line.',
160
+ description: 'One run of text inside a sentence — put several side by side in a ' + 'paragraph to emphasize a phrase without breaking the line.',
161
161
  category: Aglyn.ComponentCategory.TEXT,
162
162
  icon: {
163
163
  path: mdiFormatColorText.path,
@@ -311,7 +311,7 @@ export const presets = [
311
311
  {
312
312
  $id: generatePresetId(ID, 'emphasis'),
313
313
  type: Aglyn.NodeType.PRESET,
314
- displayName: 'Emphasised Text',
314
+ displayName: 'Emphasized Text',
315
315
  pluginId: BUNDLE_ID,
316
316
  description: 'A phrase picked out of a muted sentence',
317
317
  category: Aglyn.ComponentCategory.TEXT,
@@ -329,7 +329,7 @@ export const presets = [
329
329
  element: 'strong',
330
330
  tone: 'primary',
331
331
  weight: 'bold',
332
- children: 'the emphasised phrase'
332
+ children: 'the emphasized phrase'
333
333
  }
334
334
  }
335
335
  },
@@ -341,7 +341,7 @@ export const presets = [
341
341
  type: Aglyn.NodeType.PRESET,
342
342
  displayName: 'Two-tone Statement',
343
343
  pluginId: BUNDLE_ID,
344
- description: 'A muted sentence with one phrase emphasised',
344
+ description: 'A muted sentence with one phrase emphasized',
345
345
  category: Aglyn.ComponentCategory.TEXT,
346
346
  icon: {
347
347
  path: mdiFormatColorHighlight.path,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/inline-text.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiFormatColorHighlight, mdiFormatColorText } from '@aglyn/shared-data-mdi'\nimport MuiBox from '@mui/material/Box'\nimport type { SxProps } from '@mui/material/styles'\nimport { forwardRef, type ReactNode } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { FIELD_TEXT_CONTENT } from '../constants/field-presets'\nimport { authorHtmlLabel } from '../utils/author-html-label'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'muiInlineText'\n\n/**\n * Inline elements the run may render as. An allow-list rather than a\n * free-text field, for the same reason Box's is (AGL-1201): `element` is\n * persisted and rendered verbatim into every visitor's page, so a typed\n * value would let an author put `script`/`iframe` there.\n *\n * All five are phrasing content, so a run is always legal inside a\n * paragraph — which is the whole point of the element.\n */\nexport const INLINE_TEXT_ELEMENTS = [\n 'span',\n 'strong',\n 'em',\n 'mark',\n 'small',\n] as const\nexport type InlineTextElement = (typeof INLINE_TEXT_ELEMENTS)[number]\n\n/**\n * Theme palette paths, never raw hexes: the design's `text/muted` and\n * `text/primary` tokens ARE `text.secondary` / `text.primary`, and a\n * hardcoded `#757575` would ignore the site's theme (and dark mode).\n *\n * Every value is a real sentinel — an option value of `''` is stripped\n * before save and the pick silently reverts on reload (AGL-1191), so\n * \"inherit\" has to be spelled out rather than left blank.\n */\nconst TONE_COLOR: Record<string, string> = {\n inherit: undefined,\n primary: 'text.primary',\n secondary: 'text.secondary',\n disabled: 'text.disabled',\n accent: 'primary.main',\n accentAlt: 'secondary.main',\n success: 'success.main',\n warning: 'warning.main',\n error: 'error.main',\n}\n\n/** Named weights so the field is a pick, not a number to remember. */\nconst FONT_WEIGHT: Record<string, number> = {\n inherit: undefined,\n regular: 400,\n medium: 500,\n semibold: 600,\n bold: 700,\n}\n\n/**\n * `text-decoration` PROPAGATES — it is not inherited. An ancestor's\n * underline (a Screen Link, a struck-through block) is painted straight\n * across every in-flow inline descendant, and `text-decoration: none` on\n * the descendant CANNOT remove it. Only an element that is not an in-flow\n * non-atomic inline box escapes the propagation, which is why \"None\" also\n * switches the run to `inline-block`: an atomic inline box is not painted\n * through.\n *\n * That costs line-breaking — an inline-block will not break across lines —\n * so it is opt-in rather than the default, and the field says so. The\n * default, `inherit`, is a plain `display: inline` run that flows and wraps\n * with the rest of the sentence.\n */\nconst DECORATION_SX: Record<string, Record<string, string>> = {\n inherit: {},\n none: { display: 'inline-block', textDecoration: 'none' },\n underline: { textDecoration: 'underline' },\n lineThrough: { textDecoration: 'line-through' },\n}\n\nexport interface InlineTextProps {\n /** The DOM element rendered; defaults to `span`. */\n element?: InlineTextElement\n /** Palette token key from {@link TONE_COLOR}. */\n tone?: string\n /** Named weight key from {@link FONT_WEIGHT}. */\n weight?: string\n /** Decoration behaviour key from {@link DECORATION_SX}. */\n decoration?: string\n /**\n * Authored node styles, handed over by the renderer rather than typed\n * into an attribute. Declared because the merge below reads it —\n * undeclared, no typed caller could style a run (AGL-1323).\n */\n sx?: SxProps\n /**\n * Sanitized emphasis markup for the run, written by the inline editor's\n * rich mode (AGL-2557). `children` keeps the plain-text reading of it, and\n * is what every surface that cannot render markup falls back to.\n */\n html?: string\n /** The text of this run; edited inline on the canvas. */\n children?: ReactNode\n}\n\n/**\n * One run of text inside a sentence (AGL-1235).\n *\n * The gap this fills: `muiTypography` is `textEditable`, therefore a leaf —\n * `nodeAcceptsChildren` reports false, so no span can be nested inside it.\n * A statement whose argument lives in an emphasised phrase (\"Aglyn commerce\n * is **part of the platform**\") could not be authored at all; the whole line\n * was one flat colour.\n *\n * This is a leaf too, deliberately: the shape that renders correctly is a\n * block container holding SIBLING inline runs, not a nested span. Sibling\n * runs flow and wrap as a single paragraph, so the sentence breaks\n * naturally with the emphasis landing in the right place. Drop a Box as\n * `p`, then one of these per style change.\n */\nconst InlineText = forwardRef<HTMLElement, InlineTextProps>((props, ref) => {\n const { element, tone, weight, decoration, children, sx, html, ...rest } =\n props\n const component = INLINE_TEXT_ELEMENTS.includes(element as InlineTextElement)\n ? (element as InlineTextElement)\n : 'span'\n // Node styles ride the renderer-merged sx; recompose rather than spread\n // (the stack.ts/link-box.tsx pattern). Writing an `sx` literal after the\n // props spread would REPLACE the author's styles silently — the defect\n // class `aglyn/no-sx-after-spread` guards (AGL-1240/1284).\n const nodeSx = Array.isArray(sx) ? sx : sx ? [sx] : []\n // A cleared attribute persists as null, so every lookup below must miss\n // rather than throw; `Record[null]` is simply undefined.\n const color = TONE_COLOR[tone]\n const fontWeight = FONT_WEIGHT[weight]\n return (\n <MuiBox\n ref={ref}\n component={component}\n {...rest}\n // Order matters: the baseline first, then the declared attributes,\n // then the author's node styles last so the Styles panel always wins.\n sx={[\n { display: 'inline' },\n color ? { color } : {},\n fontWeight ? { fontWeight } : {},\n DECORATION_SX[decoration] ?? {},\n ...nodeSx,\n ]}\n >\n {authorHtmlLabel(html) ?? children}\n </MuiBox>\n )\n})\nInlineText.displayName = 'AglynInlineText'\n\nexport const schema: Aglyn.ComponentSchema<InlineTextProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Inline Text',\n description:\n 'One run of text inside a sentence — put several side by side in a ' +\n 'paragraph to emphasise a phrase without breaking the line.',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiFormatColorText.path, sx: { color: '#057822' } },\n flags: {\n // The run IS its text: edited inline on the canvas, like Typography.\n textEditable: Aglyn.FEATURE_FLAG.ENABLED,\n richTextEditable: Aglyn.FEATURE_FLAG.ENABLED,\n },\n // Emphasis only (AGL-2557), for a different reason than the controls: this\n // run is not interactive, it is `display: inline`. Every element it may\n // render as is phrasing content — that is the guarantee INLINE_TEXT_ELEMENTS\n // exists to make — so a list inside one is a block in an inline box, which\n // the parser lifts straight back out of the enclosing paragraph. Links stay\n // with Screen Link, whose job is a resolved target rather than a typed href.\n richTextCommands: [Aglyn.RICH_TEXT_COMMANDS.EMPHASIS],\n attributes: [\n FIELD_TEXT_CONTENT,\n {\n name: 'element',\n label: 'Component',\n description:\n 'The DOM element this run renders as. `strong` and `em` carry ' +\n 'meaning for screen readers and search engines; `mark` marks a ' +\n 'highlighted phrase; `span` is purely visual.',\n component: Aglyn.FieldComponentType.SELECT,\n options: INLINE_TEXT_ELEMENTS.map((value) => ({ value, label: value })),\n },\n {\n name: 'tone',\n label: 'Color',\n description:\n \"Palette token for this run. Two-tone text is a muted sentence \" +\n 'with one run set to Primary (or Accent) — theme tokens, so it ' +\n 'still reads correctly in dark mode.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'inherit', label: 'Inherit' },\n { value: 'primary', label: 'Text primary' },\n { value: 'secondary', label: 'Text muted' },\n { value: 'disabled', label: 'Text disabled' },\n { value: 'accent', label: 'Accent' },\n { value: 'accentAlt', label: 'Accent alternate' },\n { value: 'success', label: 'Success' },\n { value: 'warning', label: 'Warning' },\n { value: 'error', label: 'Error' },\n ],\n },\n {\n name: 'weight',\n label: 'Weight',\n description: 'Font weight for this run only.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'inherit', label: 'Inherit' },\n { value: 'regular', label: 'Regular' },\n { value: 'medium', label: 'Medium' },\n { value: 'semibold', label: 'Semi bold' },\n { value: 'bold', label: 'Bold' },\n ],\n },\n {\n name: 'decoration',\n label: 'Underline / strike',\n description:\n 'Inherit follows the surrounding text, including a link\\'s ' +\n 'underline — an underline PROPAGATES down and cannot be switched ' +\n 'off from inside. None does switch it off, at the cost of the run ' +\n 'no longer breaking across lines, so keep it to a short phrase.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'inherit', label: 'Inherit' },\n { value: 'none', label: 'None (does not wrap)' },\n { value: 'underline', label: 'Underline' },\n { value: 'lineThrough', label: 'Strikethrough' },\n ],\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Inline Text',\n pluginId: BUNDLE_ID,\n description: 'One run of text inside a sentence',\n category: Aglyn.ComponentCategory.TEXT,\n icon: schema.icon,\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { element: 'span', children: 'Inline text' },\n },\n },\n {\n $id: generatePresetId(ID, 'emphasis'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Emphasised Text',\n pluginId: BUNDLE_ID,\n description: 'A phrase picked out of a muted sentence',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiFormatColorHighlight.path, sx: { color: '#057822' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'strong',\n tone: 'primary',\n weight: 'bold',\n children: 'the emphasised phrase',\n },\n },\n },\n {\n // The whole shape in one drop (AGL-1235): the reason the emphasis was\n // never authored is that assembling it by hand meant a container, three\n // runs and a colour on each. This is that assembly, ready to retype.\n $id: generatePresetId(ID, 'statement'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Two-tone Statement',\n pluginId: BUNDLE_ID,\n description: 'A muted sentence with one phrase emphasised',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiFormatColorHighlight.path, sx: { color: '#057822' } },\n data: {\n $id: null,\n // A real `<p>`, not a div: the runs are phrasing content, so the\n // paragraph is the honest container and screen readers get a\n // sentence rather than three unrelated fragments.\n componentId: 'muiBox',\n pluginId: BUNDLE_ID,\n props: { component: 'p' },\n // Node-level sx (AGL-1346) — the record the Styles panel edits.\n sx: { marginTop: 0, marginRight: 0, marginBottom: 0, marginLeft: 0 },\n nodes: [\n {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'span',\n tone: 'secondary',\n children: 'Everything you build is ',\n },\n },\n {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'strong',\n tone: 'primary',\n weight: 'bold',\n children: 'part of the platform',\n },\n },\n {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'span',\n tone: 'secondary',\n children: ', not bolted onto it.',\n },\n },\n ],\n },\n },\n]\n\nexport default InlineText\n"],"names":["Aglyn","mdiFormatColorHighlight","mdiFormatColorText","MuiBox","forwardRef","BUNDLE_ID","FIELD_TEXT_CONTENT","authorHtmlLabel","generatePresetId","ID","INLINE_TEXT_ELEMENTS","TONE_COLOR","inherit","undefined","primary","secondary","disabled","accent","accentAlt","success","warning","error","FONT_WEIGHT","regular","medium","semibold","bold","DECORATION_SX","none","display","textDecoration","underline","lineThrough","InlineText","props","ref","element","tone","weight","decoration","children","sx","html","rest","component","includes","nodeSx","Array","isArray","color","fontWeight","displayName","schema","$id","pluginId","description","category","ComponentCategory","TEXT","icon","path","flags","textEditable","FEATURE_FLAG","ENABLED","richTextEditable","richTextCommands","RICH_TEXT_COMMANDS","EMPHASIS","attributes","name","label","FieldComponentType","SELECT","options","map","value","presets","type","NodeType","PRESET","data","componentId","marginTop","marginRight","marginBottom","marginLeft","nodes"],"mappings":";;;AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SAASC,uBAAuB,EAAEC,kBAAkB,QAAQ,yBAAwB;AACpF,OAAOC,YAAY,oBAAmB;AAEtC,SAASC,UAAU,QAAwB,QAAO;AAClD,SAASC,SAAS,QAAQ,gCAA4B;AACtD,SAASC,kBAAkB,QAAQ,gCAA4B;AAC/D,SAASC,eAAe,QAAQ,gCAA4B;AAC5D,SAASC,gBAAgB,QAAQ,iCAA6B;AAE9D,iEAAiE;AACjE,OAAO,MAAMC,KAAwB,gBAAe;AAEpD;;;;;;;;CAQC,GACD,OAAO,MAAMC,uBAAuB;IAClC;IACA;IACA;IACA;IACA;CACD,CAAS;AAGV;;;;;;;;CAQC,GACD,MAAMC,aAAqC;IACzCC,SAASC;IACTC,SAAS;IACTC,WAAW;IACXC,UAAU;IACVC,QAAQ;IACRC,WAAW;IACXC,SAAS;IACTC,SAAS;IACTC,OAAO;AACT;AAEA,oEAAoE,GACpE,MAAMC,cAAsC;IAC1CV,SAASC;IACTU,SAAS;IACTC,QAAQ;IACRC,UAAU;IACVC,MAAM;AACR;AAEA;;;;;;;;;;;;;CAaC,GACD,MAAMC,gBAAwD;IAC5Df,SAAS,CAAC;IACVgB,MAAM;QAAEC,SAAS;QAAgBC,gBAAgB;IAAO;IACxDC,WAAW;QAAED,gBAAgB;IAAY;IACzCE,aAAa;QAAEF,gBAAgB;IAAe;AAChD;AA2BA;;;;;;;;;;;;;;CAcC,GACD,MAAMG,2BAAa7B,WAAyC,CAAC8B,OAAOC;QA0B5DR,2BAIDpB;IA7BL,MAAM,EAAE6B,OAAO,EAAEC,IAAI,EAAEC,MAAM,EAAEC,UAAU,EAAEC,QAAQ,EAAEC,EAAE,EAAEC,IAAI,EAAW,GACtER,OADgES,wCAChET;;;;;;;;;IACF,MAAMU,YAAYlC,qBAAqBmC,QAAQ,CAACT,WAC3CA,UACD;IACJ,wEAAwE;IACxE,yEAAyE;IACzE,uEAAuE;IACvE,2DAA2D;IAC3D,MAAMU,SAASC,MAAMC,OAAO,CAACP,MAAMA,KAAKA,KAAK;QAACA;KAAG,GAAG,EAAE;IACtD,wEAAwE;IACxE,yDAAyD;IACzD,MAAMQ,QAAQtC,UAAU,CAAC0B,KAAK;IAC9B,MAAMa,aAAa5B,WAAW,CAACgB,OAAO;IACtC,qBACE,KAACnC;QACCgC,KAAKA;QACLS,WAAWA;OACPD;QACJ,mEAAmE;QACnE,sEAAsE;QACtEF,IAAI;YACF;gBAAEZ,SAAS;YAAS;YACpBoB,QAAQ;gBAAEA;YAAM,IAAI,CAAC;YACrBC,aAAa;gBAAEA;YAAW,IAAI,CAAC;aAC/BvB,4BAAAA,aAAa,CAACY,WAAW,YAAzBZ,4BAA6B,CAAC;eAC3BmB;SACJ;mBAEAvC,mBAAAA,gBAAgBmC,iBAAhBnC,mBAAyBiC;;AAGhC;AACAP,WAAWkB,WAAW,GAAG;AAEzB,OAAO,MAAMC,SAAiD;IAC5DC,KAAK5C;IACL6C,UAAUjD;IACV8C,aAAa;IACbI,aACE,uEACA;IACFC,UAAUxD,MAAMyD,iBAAiB,CAACC,IAAI;IACtCC,MAAM;QAAEC,MAAM1D,mBAAmB0D,IAAI;QAAEnB,IAAI;YAAEQ,OAAO;QAAU;IAAE;IAChEY,OAAO;QACL,qEAAqE;QACrEC,cAAc9D,MAAM+D,YAAY,CAACC,OAAO;QACxCC,kBAAkBjE,MAAM+D,YAAY,CAACC,OAAO;IAC9C;IACA,2EAA2E;IAC3E,wEAAwE;IACxE,6EAA6E;IAC7E,2EAA2E;IAC3E,4EAA4E;IAC5E,6EAA6E;IAC7EE,kBAAkB;QAAClE,MAAMmE,kBAAkB,CAACC,QAAQ;KAAC;IACrDC,YAAY;QACV/D;QACA;YACEgE,MAAM;YACNC,OAAO;YACPhB,aACE,kEACA,mEACA;YACFX,WAAW5C,MAAMwE,kBAAkB,CAACC,MAAM;YAC1CC,SAAShE,qBAAqBiE,GAAG,CAAC,CAACC,QAAW,CAAA;oBAAEA;oBAAOL,OAAOK;gBAAM,CAAA;QACtE;QACA;YACEN,MAAM;YACNC,OAAO;YACPhB,aACE,mEACA,mEACA;YACFX,WAAW5C,MAAMwE,kBAAkB,CAACC,MAAM;YAC1CC,SAAS;gBACP;oBAAEE,OAAO;oBAAWL,OAAO;gBAAU;gBACrC;oBAAEK,OAAO;oBAAWL,OAAO;gBAAe;gBAC1C;oBAAEK,OAAO;oBAAaL,OAAO;gBAAa;gBAC1C;oBAAEK,OAAO;oBAAYL,OAAO;gBAAgB;gBAC5C;oBAAEK,OAAO;oBAAUL,OAAO;gBAAS;gBACnC;oBAAEK,OAAO;oBAAaL,OAAO;gBAAmB;gBAChD;oBAAEK,OAAO;oBAAWL,OAAO;gBAAU;gBACrC;oBAAEK,OAAO;oBAAWL,OAAO;gBAAU;gBACrC;oBAAEK,OAAO;oBAASL,OAAO;gBAAQ;aAClC;QACH;QACA;YACED,MAAM;YACNC,OAAO;YACPhB,aAAa;YACbX,WAAW5C,MAAMwE,kBAAkB,CAACC,MAAM;YAC1CC,SAAS;gBACP;oBAAEE,OAAO;oBAAWL,OAAO;gBAAU;gBACrC;oBAAEK,OAAO;oBAAWL,OAAO;gBAAU;gBACrC;oBAAEK,OAAO;oBAAUL,OAAO;gBAAS;gBACnC;oBAAEK,OAAO;oBAAYL,OAAO;gBAAY;gBACxC;oBAAEK,OAAO;oBAAQL,OAAO;gBAAO;aAChC;QACH;QACA;YACED,MAAM;YACNC,OAAO;YACPhB,aACE,+DACA,qEACA,sEACA;YACFX,WAAW5C,MAAMwE,kBAAkB,CAACC,MAAM;YAC1CC,SAAS;gBACP;oBAAEE,OAAO;oBAAWL,OAAO;gBAAU;gBACrC;oBAAEK,OAAO;oBAAQL,OAAO;gBAAuB;gBAC/C;oBAAEK,OAAO;oBAAaL,OAAO;gBAAY;gBACzC;oBAAEK,OAAO;oBAAeL,OAAO;gBAAgB;aAChD;QACH;KACD;AACH,EAAC;AAED,OAAO,MAAMM,UAAgC;IAC3C;QACExB,KAAK7C,iBAAiBC;QACtBqE,MAAM9E,MAAM+E,QAAQ,CAACC,MAAM;QAC3B7B,aAAa;QACbG,UAAUjD;QACVkD,aAAa;QACbC,UAAUxD,MAAMyD,iBAAiB,CAACC,IAAI;QACtCC,MAAMP,OAAOO,IAAI;QACjBsB,MAAM;YACJ5B,KAAK;YACL6B,aAAazE;YACb6C,UAAUjD;YACV6B,OAAO;gBAAEE,SAAS;gBAAQI,UAAU;YAAc;QACpD;IACF;IACA;QACEa,KAAK7C,iBAAiBC,IAAI;QAC1BqE,MAAM9E,MAAM+E,QAAQ,CAACC,MAAM;QAC3B7B,aAAa;QACbG,UAAUjD;QACVkD,aAAa;QACbC,UAAUxD,MAAMyD,iBAAiB,CAACC,IAAI;QACtCC,MAAM;YAAEC,MAAM3D,wBAAwB2D,IAAI;YAAEnB,IAAI;gBAAEQ,OAAO;YAAU;QAAE;QACrEgC,MAAM;YACJ5B,KAAK;YACL6B,aAAazE;YACb6C,UAAUjD;YACV6B,OAAO;gBACLE,SAAS;gBACTC,MAAM;gBACNC,QAAQ;gBACRE,UAAU;YACZ;QACF;IACF;IACA;QACE,sEAAsE;QACtE,wEAAwE;QACxE,qEAAqE;QACrEa,KAAK7C,iBAAiBC,IAAI;QAC1BqE,MAAM9E,MAAM+E,QAAQ,CAACC,MAAM;QAC3B7B,aAAa;QACbG,UAAUjD;QACVkD,aAAa;QACbC,UAAUxD,MAAMyD,iBAAiB,CAACC,IAAI;QACtCC,MAAM;YAAEC,MAAM3D,wBAAwB2D,IAAI;YAAEnB,IAAI;gBAAEQ,OAAO;YAAU;QAAE;QACrEgC,MAAM;YACJ5B,KAAK;YACL,iEAAiE;YACjE,6DAA6D;YAC7D,kDAAkD;YAClD6B,aAAa;YACb5B,UAAUjD;YACV6B,OAAO;gBAAEU,WAAW;YAAI;YACxB,gEAAgE;YAChEH,IAAI;gBAAE0C,WAAW;gBAAGC,aAAa;gBAAGC,cAAc;gBAAGC,YAAY;YAAE;YACnEC,OAAO;gBACL;oBACElC,KAAK;oBACL6B,aAAazE;oBACb6C,UAAUjD;oBACV6B,OAAO;wBACLE,SAAS;wBACTC,MAAM;wBACNG,UAAU;oBACZ;gBACF;gBACA;oBACEa,KAAK;oBACL6B,aAAazE;oBACb6C,UAAUjD;oBACV6B,OAAO;wBACLE,SAAS;wBACTC,MAAM;wBACNC,QAAQ;wBACRE,UAAU;oBACZ;gBACF;gBACA;oBACEa,KAAK;oBACL6B,aAAazE;oBACb6C,UAAUjD;oBACV6B,OAAO;wBACLE,SAAS;wBACTC,MAAM;wBACNG,UAAU;oBACZ;gBACF;aACD;QACH;IACF;CACD,CAAA;AAED,eAAeP,WAAU"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/inline-text.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiFormatColorHighlight, mdiFormatColorText } from '@aglyn/shared-data-mdi'\nimport MuiBox from '@mui/material/Box'\nimport type { SxProps } from '@mui/material/styles'\nimport { forwardRef, type ReactNode } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { FIELD_TEXT_CONTENT } from '../constants/field-presets'\nimport { authorHtmlLabel } from '../utils/author-html-label'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'muiInlineText'\n\n/**\n * Inline elements the run may render as. An allow-list rather than a\n * free-text field, for the same reason Box's is (AGL-1201): `element` is\n * persisted and rendered verbatim into every visitor's page, so a typed\n * value would let an author put `script`/`iframe` there.\n *\n * All five are phrasing content, so a run is always legal inside a\n * paragraph — which is the whole point of the element.\n */\nexport const INLINE_TEXT_ELEMENTS = [\n 'span',\n 'strong',\n 'em',\n 'mark',\n 'small',\n] as const\nexport type InlineTextElement = (typeof INLINE_TEXT_ELEMENTS)[number]\n\n/**\n * Theme palette paths, never raw hexes: the design's `text/muted` and\n * `text/primary` tokens ARE `text.secondary` / `text.primary`, and a\n * hardcoded `#757575` would ignore the site's theme (and dark mode).\n *\n * Every value is a real sentinel — an option value of `''` is stripped\n * before save and the pick silently reverts on reload (AGL-1191), so\n * \"inherit\" has to be spelled out rather than left blank.\n */\nconst TONE_COLOR: Record<string, string> = {\n inherit: undefined,\n primary: 'text.primary',\n secondary: 'text.secondary',\n disabled: 'text.disabled',\n accent: 'primary.main',\n accentAlt: 'secondary.main',\n success: 'success.main',\n warning: 'warning.main',\n error: 'error.main',\n}\n\n/** Named weights so the field is a pick, not a number to remember. */\nconst FONT_WEIGHT: Record<string, number> = {\n inherit: undefined,\n regular: 400,\n medium: 500,\n semibold: 600,\n bold: 700,\n}\n\n/**\n * `text-decoration` PROPAGATES — it is not inherited. An ancestor's\n * underline (a Screen Link, a struck-through block) is painted straight\n * across every in-flow inline descendant, and `text-decoration: none` on\n * the descendant CANNOT remove it. Only an element that is not an in-flow\n * non-atomic inline box escapes the propagation, which is why \"None\" also\n * switches the run to `inline-block`: an atomic inline box is not painted\n * through.\n *\n * That costs line-breaking — an inline-block will not break across lines —\n * so it is opt-in rather than the default, and the field says so. The\n * default, `inherit`, is a plain `display: inline` run that flows and wraps\n * with the rest of the sentence.\n */\nconst DECORATION_SX: Record<string, Record<string, string>> = {\n inherit: {},\n none: { display: 'inline-block', textDecoration: 'none' },\n underline: { textDecoration: 'underline' },\n lineThrough: { textDecoration: 'line-through' },\n}\n\nexport interface InlineTextProps {\n /** The DOM element rendered; defaults to `span`. */\n element?: InlineTextElement\n /** Palette token key from {@link TONE_COLOR}. */\n tone?: string\n /** Named weight key from {@link FONT_WEIGHT}. */\n weight?: string\n /** Decoration behaviour key from {@link DECORATION_SX}. */\n decoration?: string\n /**\n * Authored node styles, handed over by the renderer rather than typed\n * into an attribute. Declared because the merge below reads it —\n * undeclared, no typed caller could style a run (AGL-1323).\n */\n sx?: SxProps\n /**\n * Sanitized emphasis markup for the run, written by the inline editor's\n * rich mode (AGL-2557). `children` keeps the plain-text reading of it, and\n * is what every surface that cannot render markup falls back to.\n */\n html?: string\n /** The text of this run; edited inline on the canvas. */\n children?: ReactNode\n}\n\n/**\n * One run of text inside a sentence (AGL-1235).\n *\n * The gap this fills: `muiTypography` is `textEditable`, therefore a leaf —\n * `nodeAcceptsChildren` reports false, so no span can be nested inside it.\n * A statement whose argument lives in an emphasised phrase (\"Aglyn commerce\n * is **part of the platform**\") could not be authored at all; the whole line\n * was one flat colour.\n *\n * This is a leaf too, deliberately: the shape that renders correctly is a\n * block container holding SIBLING inline runs, not a nested span. Sibling\n * runs flow and wrap as a single paragraph, so the sentence breaks\n * naturally with the emphasis landing in the right place. Drop a Box as\n * `p`, then one of these per style change.\n */\nconst InlineText = forwardRef<HTMLElement, InlineTextProps>((props, ref) => {\n const { element, tone, weight, decoration, children, sx, html, ...rest } =\n props\n const component = INLINE_TEXT_ELEMENTS.includes(element as InlineTextElement)\n ? (element as InlineTextElement)\n : 'span'\n // Node styles ride the renderer-merged sx; recompose rather than spread\n // (the stack.ts/link-box.tsx pattern). Writing an `sx` literal after the\n // props spread would REPLACE the author's styles silently — the defect\n // class `aglyn/no-sx-after-spread` guards (AGL-1240/1284).\n const nodeSx = Array.isArray(sx) ? sx : sx ? [sx] : []\n // A cleared attribute persists as null, so every lookup below must miss\n // rather than throw; `Record[null]` is simply undefined.\n const color = TONE_COLOR[tone]\n const fontWeight = FONT_WEIGHT[weight]\n return (\n <MuiBox\n ref={ref}\n component={component}\n {...rest}\n // Order matters: the baseline first, then the declared attributes,\n // then the author's node styles last so the Styles panel always wins.\n sx={[\n { display: 'inline' },\n color ? { color } : {},\n fontWeight ? { fontWeight } : {},\n DECORATION_SX[decoration] ?? {},\n ...nodeSx,\n ]}\n >\n {authorHtmlLabel(html) ?? children}\n </MuiBox>\n )\n})\nInlineText.displayName = 'AglynInlineText'\n\nexport const schema: Aglyn.ComponentSchema<InlineTextProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Inline Text',\n description:\n 'One run of text inside a sentence — put several side by side in a ' +\n 'paragraph to emphasize a phrase without breaking the line.',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiFormatColorText.path, sx: { color: '#057822' } },\n flags: {\n // The run IS its text: edited inline on the canvas, like Typography.\n textEditable: Aglyn.FEATURE_FLAG.ENABLED,\n richTextEditable: Aglyn.FEATURE_FLAG.ENABLED,\n },\n // Emphasis only (AGL-2557), for a different reason than the controls: this\n // run is not interactive, it is `display: inline`. Every element it may\n // render as is phrasing content — that is the guarantee INLINE_TEXT_ELEMENTS\n // exists to make — so a list inside one is a block in an inline box, which\n // the parser lifts straight back out of the enclosing paragraph. Links stay\n // with Screen Link, whose job is a resolved target rather than a typed href.\n richTextCommands: [Aglyn.RICH_TEXT_COMMANDS.EMPHASIS],\n attributes: [\n FIELD_TEXT_CONTENT,\n {\n name: 'element',\n label: 'Component',\n description:\n 'The DOM element this run renders as. `strong` and `em` carry ' +\n 'meaning for screen readers and search engines; `mark` marks a ' +\n 'highlighted phrase; `span` is purely visual.',\n component: Aglyn.FieldComponentType.SELECT,\n options: INLINE_TEXT_ELEMENTS.map((value) => ({ value, label: value })),\n },\n {\n name: 'tone',\n label: 'Color',\n description:\n \"Palette token for this run. Two-tone text is a muted sentence \" +\n 'with one run set to Primary (or Accent) — theme tokens, so it ' +\n 'still reads correctly in dark mode.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'inherit', label: 'Inherit' },\n { value: 'primary', label: 'Text primary' },\n { value: 'secondary', label: 'Text muted' },\n { value: 'disabled', label: 'Text disabled' },\n { value: 'accent', label: 'Accent' },\n { value: 'accentAlt', label: 'Accent alternate' },\n { value: 'success', label: 'Success' },\n { value: 'warning', label: 'Warning' },\n { value: 'error', label: 'Error' },\n ],\n },\n {\n name: 'weight',\n label: 'Weight',\n description: 'Font weight for this run only.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'inherit', label: 'Inherit' },\n { value: 'regular', label: 'Regular' },\n { value: 'medium', label: 'Medium' },\n { value: 'semibold', label: 'Semi bold' },\n { value: 'bold', label: 'Bold' },\n ],\n },\n {\n name: 'decoration',\n label: 'Underline / strike',\n description:\n 'Inherit follows the surrounding text, including a link\\'s ' +\n 'underline — an underline PROPAGATES down and cannot be switched ' +\n 'off from inside. None does switch it off, at the cost of the run ' +\n 'no longer breaking across lines, so keep it to a short phrase.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'inherit', label: 'Inherit' },\n { value: 'none', label: 'None (does not wrap)' },\n { value: 'underline', label: 'Underline' },\n { value: 'lineThrough', label: 'Strikethrough' },\n ],\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Inline Text',\n pluginId: BUNDLE_ID,\n description: 'One run of text inside a sentence',\n category: Aglyn.ComponentCategory.TEXT,\n icon: schema.icon,\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { element: 'span', children: 'Inline text' },\n },\n },\n {\n $id: generatePresetId(ID, 'emphasis'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Emphasized Text',\n pluginId: BUNDLE_ID,\n description: 'A phrase picked out of a muted sentence',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiFormatColorHighlight.path, sx: { color: '#057822' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'strong',\n tone: 'primary',\n weight: 'bold',\n children: 'the emphasized phrase',\n },\n },\n },\n {\n // The whole shape in one drop (AGL-1235): the reason the emphasis was\n // never authored is that assembling it by hand meant a container, three\n // runs and a colour on each. This is that assembly, ready to retype.\n $id: generatePresetId(ID, 'statement'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Two-tone Statement',\n pluginId: BUNDLE_ID,\n description: 'A muted sentence with one phrase emphasized',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiFormatColorHighlight.path, sx: { color: '#057822' } },\n data: {\n $id: null,\n // A real `<p>`, not a div: the runs are phrasing content, so the\n // paragraph is the honest container and screen readers get a\n // sentence rather than three unrelated fragments.\n componentId: 'muiBox',\n pluginId: BUNDLE_ID,\n props: { component: 'p' },\n // Node-level sx (AGL-1346) — the record the Styles panel edits.\n sx: { marginTop: 0, marginRight: 0, marginBottom: 0, marginLeft: 0 },\n nodes: [\n {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'span',\n tone: 'secondary',\n children: 'Everything you build is ',\n },\n },\n {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'strong',\n tone: 'primary',\n weight: 'bold',\n children: 'part of the platform',\n },\n },\n {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'span',\n tone: 'secondary',\n children: ', not bolted onto it.',\n },\n },\n ],\n },\n },\n]\n\nexport default InlineText\n"],"names":["Aglyn","mdiFormatColorHighlight","mdiFormatColorText","MuiBox","forwardRef","BUNDLE_ID","FIELD_TEXT_CONTENT","authorHtmlLabel","generatePresetId","ID","INLINE_TEXT_ELEMENTS","TONE_COLOR","inherit","undefined","primary","secondary","disabled","accent","accentAlt","success","warning","error","FONT_WEIGHT","regular","medium","semibold","bold","DECORATION_SX","none","display","textDecoration","underline","lineThrough","InlineText","props","ref","element","tone","weight","decoration","children","sx","html","rest","component","includes","nodeSx","Array","isArray","color","fontWeight","displayName","schema","$id","pluginId","description","category","ComponentCategory","TEXT","icon","path","flags","textEditable","FEATURE_FLAG","ENABLED","richTextEditable","richTextCommands","RICH_TEXT_COMMANDS","EMPHASIS","attributes","name","label","FieldComponentType","SELECT","options","map","value","presets","type","NodeType","PRESET","data","componentId","marginTop","marginRight","marginBottom","marginLeft","nodes"],"mappings":"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"}