@flamingo-stack/openframe-frontend-core 0.0.774 → 0.0.775

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.
Files changed (51) hide show
  1. package/dist/{chunk-NX7P2T2U.cjs → chunk-36GYLB3Z.cjs} +3 -3
  2. package/dist/{chunk-ZHGOXDA3.cjs → chunk-3XLDYEQD.cjs} +13 -6
  3. package/dist/{chunk-4JZC624S.cjs → chunk-42JGOJCB.cjs} +8 -7
  4. package/dist/{chunk-TTQHSSHF.cjs → chunk-4LFB4YY3.cjs} +3 -3
  5. package/dist/{chunk-5Z3XL2VU.cjs → chunk-5YJWT5ZQ.cjs} +26 -26
  6. package/dist/{chunk-U332FL4C.cjs → chunk-6SJYFUGD.cjs} +5 -5
  7. package/dist/{chunk-LEVA5WO3.cjs → chunk-F67SEXCW.cjs} +13 -13
  8. package/dist/{chunk-VWUQVJ2D.cjs → chunk-FHTTRWAK.cjs} +32 -32
  9. package/dist/{chunk-Q7EXOVMT.cjs → chunk-ODF3KCRK.cjs} +10 -10
  10. package/dist/{chunk-E5E7IE7X.cjs → chunk-PMX6QNNN.cjs} +2 -2
  11. package/dist/{chunk-Q6B2DC65.cjs → chunk-TF2XYDH3.cjs} +3 -3
  12. package/dist/{chunk-HTSRAQVG.cjs → chunk-VAGZJRYV.cjs} +2 -2
  13. package/dist/{chunk-UAKXZ2KD.cjs → chunk-VDXBACD3.cjs} +16 -16
  14. package/dist/{cloud-tenants-7RNZ6GHM.cjs → cloud-tenants-ISGAMTJD.cjs} +2 -2
  15. package/dist/components/authors/author-detail-view.d.ts.map +1 -1
  16. package/dist/components/authors/author-detail-view.js +12 -2
  17. package/dist/components/case-studies/index.cjs +12 -12
  18. package/dist/components/contact/index.cjs +3 -3
  19. package/dist/components/docs/index.cjs +5 -5
  20. package/dist/components/embeds/index.cjs +3 -3
  21. package/dist/components/faq/index.cjs +3 -3
  22. package/dist/components/features/index.cjs +2 -2
  23. package/dist/components/help-center-pages/index.cjs +49 -49
  24. package/dist/components/index.cjs +30 -20
  25. package/dist/components/meeting-scheduler/index.cjs +18 -18
  26. package/dist/components/navigation/index.cjs +3 -3
  27. package/dist/components/onboarding-guides/index.cjs +4 -4
  28. package/dist/components/product-demo/index.cjs +11 -11
  29. package/dist/components/related-content/card-registry.d.ts +5 -0
  30. package/dist/components/related-content/card-registry.d.ts.map +1 -1
  31. package/dist/components/related-content/card-registry.js +2 -0
  32. package/dist/components/related-content/index.cjs +2 -2
  33. package/dist/components/related-content/related-content-section.d.ts.map +1 -1
  34. package/dist/components/related-content/related-content-section.js +11 -6
  35. package/dist/components/tickets/index.cjs +5 -5
  36. package/dist/components/ui/index.cjs +2 -2
  37. package/dist/components/ui/photo-wall.d.ts +9 -1
  38. package/dist/components/ui/photo-wall.d.ts.map +1 -1
  39. package/dist/components/ui/photo-wall.js +8 -7
  40. package/dist/{devices-NNDBC6IH.cjs → devices-AHJX6ACJ.cjs} +3 -3
  41. package/dist/index.cjs +3 -3
  42. package/dist/{knowledge-DPUMWQED.cjs → knowledge-FH5GVRVP.cjs} +2 -2
  43. package/dist/{logs-EE25MWYM.cjs → logs-JBIHHLUQ.cjs} +10 -10
  44. package/dist/{policies-H5LPZWEM.cjs → policies-XLJ2ABY5.cjs} +6 -6
  45. package/dist/{scripts-YGO4LOES.cjs → scripts-CQLXM7PP.cjs} +7 -7
  46. package/dist/{software-update-5BEB62ZH.cjs → software-update-7NYZ7L76.cjs} +5 -5
  47. package/package.json +1 -1
  48. package/src/components/authors/author-detail-view.tsx +10 -2
  49. package/src/components/related-content/card-registry.tsx +7 -0
  50. package/src/components/related-content/related-content-section.tsx +12 -2
  51. package/src/components/ui/photo-wall.tsx +19 -8
@@ -8,7 +8,7 @@ var _chunkSTEJZPIOcjs = require('./chunk-STEJZPIO.cjs');
8
8
  var _chunkDKOLQPE7cjs = require('./chunk-DKOLQPE7.cjs');
9
9
 
10
10
 
11
- var _chunk4JZC624Scjs = require('./chunk-4JZC624S.cjs');
11
+ var _chunk42JGOJCBcjs = require('./chunk-42JGOJCB.cjs');
12
12
  require('./chunk-LJT2KRE2.cjs');
13
13
  require('./chunk-RB7VLVMD.cjs');
14
14
  require('./chunk-V3W56L2D.cjs');
@@ -1903,7 +1903,7 @@ function KnowledgeScreen(_props) {
1903
1903
  const cast = _chunkSTEJZPIOcjs.useProductDemoCast.call(void 0, );
1904
1904
  const fixture = _react.useMemo.call(void 0, () => buildKnowledgeFixture(cast), [cast]);
1905
1905
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full bg-ods-bg", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1906
- _chunk4JZC624Scjs.KnowledgeBaseArticleView,
1906
+ _chunk42JGOJCBcjs.KnowledgeBaseArticleView,
1907
1907
  {
1908
1908
  ...fixture.article,
1909
1909
  content: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkKWZ62LPZcjs.SimpleMarkdownRenderer, { content: fixture.content, textSize: "compact" })
@@ -16,7 +16,7 @@ var _chunkDKOLQPE7cjs = require('./chunk-DKOLQPE7.cjs');
16
16
 
17
17
 
18
18
 
19
- var _chunk4JZC624Scjs = require('./chunk-4JZC624S.cjs');
19
+ var _chunk42JGOJCBcjs = require('./chunk-42JGOJCB.cjs');
20
20
  require('./chunk-LJT2KRE2.cjs');
21
21
  require('./chunk-RB7VLVMD.cjs');
22
22
  require('./chunk-V3W56L2D.cjs');
@@ -1884,7 +1884,7 @@ function entry(logId, minutesAgo, severity, toolType, device2, summary) {
1884
1884
  id: logId,
1885
1885
  logId,
1886
1886
  timestamp: displayTime(_chunkDKOLQPE7cjs.demoMinutesAgo.call(void 0, minutesAgo)),
1887
- status: { label: severity, variant: _chunk4JZC624Scjs.logSeverityVariant.call(void 0, severity) },
1887
+ status: { label: severity, variant: _chunk42JGOJCBcjs.logSeverityVariant.call(void 0, severity) },
1888
1888
  source: { name: toolType, toolType },
1889
1889
  device: device2,
1890
1890
  description: { title: summary }
@@ -1956,13 +1956,13 @@ var NO_FILTERS = {};
1956
1956
  var ACTION_COLUMNS = ["copy", "quickView", "open"];
1957
1957
  var BESIDE_DRAWER_HIDDEN = [...ACTION_COLUMNS, "tool", "source"];
1958
1958
  var logHref = () => "#";
1959
- var copyAction = () => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk4JZC624Scjs.LogCopyIconButton, {});
1959
+ var copyAction = () => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk42JGOJCBcjs.LogCopyIconButton, {});
1960
1960
  function LogsScreen({ compact = false }) {
1961
1961
  const [dock, setDock] = _react.useState.call(void 0, null);
1962
1962
  const selected = LOGS_FIXTURE_SELECTED;
1963
1963
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex h-full bg-ods-bg", children: [
1964
1964
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "min-w-0 flex-1 overflow-clip", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1965
- _chunk4JZC624Scjs.LogsPageView,
1965
+ _chunk42JGOJCBcjs.LogsPageView,
1966
1966
  {
1967
1967
  showHeader: false,
1968
1968
  className: "p-[var(--spacing-system-l)]",
@@ -1970,7 +1970,7 @@ function LogsScreen({ compact = false }) {
1970
1970
  onSearchChange: noop,
1971
1971
  onOpenFilters: noop,
1972
1972
  children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1973
- _chunk4JZC624Scjs.LogsTableView,
1973
+ _chunk42JGOJCBcjs.LogsTableView,
1974
1974
  {
1975
1975
  logs: LOGS_FIXTURE_ENTRIES,
1976
1976
  facets: LOGS_FIXTURE_FACETS,
@@ -1984,8 +1984,8 @@ function LogsScreen({ compact = false }) {
1984
1984
  )
1985
1985
  }
1986
1986
  ) }),
1987
- !compact && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { ref: setDock, className: "relative w-[calc(400px+var(--spacing-system-m))] shrink-0", children: dock && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk4JZC624Scjs.AppLayoutDrawerContainerContext.Provider, { value: dock, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1988
- _chunk4JZC624Scjs.LogDrawer,
1987
+ !compact && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { ref: setDock, className: "relative w-[calc(400px+var(--spacing-system-m))] shrink-0", children: dock && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk42JGOJCBcjs.AppLayoutDrawerContainerContext.Provider, { value: dock, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1988
+ _chunk42JGOJCBcjs.LogDrawer,
1989
1989
  {
1990
1990
  docked: true,
1991
1991
  isOpen: true,
@@ -1997,12 +1997,12 @@ function LogsScreen({ compact = false }) {
1997
1997
  { label: "Log ID", value: selected.logId },
1998
1998
  {
1999
1999
  label: "Source",
2000
- value: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk4JZC624Scjs.ToolBadge, { toolType: _chunkBTVO676Bcjs.normalizeToolTypeWithFallback.call(void 0, selected.source.toolType) })
2000
+ value: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk42JGOJCBcjs.ToolBadge, { toolType: _chunkBTVO676Bcjs.normalizeToolTypeWithFallback.call(void 0, selected.source.toolType) })
2001
2001
  },
2002
- { label: "Device", value: _chunk4JZC624Scjs.logSourceLabels.call(void 0, selected.device).deviceName }
2002
+ { label: "Device", value: _chunk42JGOJCBcjs.logSourceLabels.call(void 0, selected.device).deviceName }
2003
2003
  ],
2004
2004
  deviceCard: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2005
- _chunk4JZC624Scjs.DeviceCard,
2005
+ _chunk42JGOJCBcjs.DeviceCard,
2006
2006
  {
2007
2007
  ...LOGS_FIXTURE_DEVICE,
2008
2008
  actions: {
@@ -10,7 +10,7 @@ var _chunkDKOLQPE7cjs = require('./chunk-DKOLQPE7.cjs');
10
10
 
11
11
 
12
12
 
13
- var _chunk4JZC624Scjs = require('./chunk-4JZC624S.cjs');
13
+ var _chunk42JGOJCBcjs = require('./chunk-42JGOJCB.cjs');
14
14
  require('./chunk-LJT2KRE2.cjs');
15
15
  require('./chunk-RB7VLVMD.cjs');
16
16
  require('./chunk-V3W56L2D.cjs');
@@ -1889,14 +1889,14 @@ var POLICIES = [
1889
1889
  policy("policy-local-admins", "No local admins", "No everyday account is a local administrator.", false, 0)
1890
1890
  ];
1891
1891
  var POLICIES_FIXTURE = {
1892
- summary: _chunk4JZC624Scjs.computePolicySummary.call(void 0, POLICIES),
1892
+ summary: _chunk42JGOJCBcjs.computePolicySummary.call(void 0, POLICIES),
1893
1893
  rows: POLICIES.map((item) => ({
1894
1894
  id: item.id,
1895
1895
  name: item.name,
1896
1896
  description: item.description,
1897
1897
  critical: item.critical,
1898
1898
  severityLabel: item.critical ? "Critical" : "Low",
1899
- status: _chunk4JZC624Scjs.getPolicyTableStatus.call(void 0, item),
1899
+ status: _chunk42JGOJCBcjs.getPolicyTableStatus.call(void 0, item),
1900
1900
  actions: [
1901
1901
  { label: "Policy Details", onClick: noop },
1902
1902
  { label: "Delete Policy", onClick: noop }
@@ -1910,16 +1910,16 @@ var POLICIES_FIXTURE = {
1910
1910
  var _jsxruntime = require('react/jsx-runtime');
1911
1911
  function PoliciesScreen({ compact = false }) {
1912
1912
  const table = /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1913
- _chunk4JZC624Scjs.PoliciesTable,
1913
+ _chunk42JGOJCBcjs.PoliciesTable,
1914
1914
  {
1915
1915
  rows: POLICIES_FIXTURE.rows,
1916
1916
  rowAsLink: true,
1917
- rightSlot: compact ? void 0 : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk4JZC624Scjs.DataTable.RowCount, {}),
1917
+ rightSlot: compact ? void 0 : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk42JGOJCBcjs.DataTable.RowCount, {}),
1918
1918
  emptyMessage: "No policies found."
1919
1919
  }
1920
1920
  );
1921
1921
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex h-full flex-col gap-[var(--spacing-system-l)] bg-ods-bg p-[var(--spacing-system-l)]", children: [
1922
- !compact && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk4JZC624Scjs.PolicySummaryCards, { summary: POLICIES_FIXTURE.summary, now: POLICIES_FIXTURE.now }),
1922
+ !compact && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk42JGOJCBcjs.PolicySummaryCards, { summary: POLICIES_FIXTURE.summary, now: POLICIES_FIXTURE.now }),
1923
1923
  table
1924
1924
  ] });
1925
1925
  }
@@ -13,7 +13,7 @@ var _chunkDKOLQPE7cjs = require('./chunk-DKOLQPE7.cjs');
13
13
 
14
14
 
15
15
 
16
- var _chunk4JZC624Scjs = require('./chunk-4JZC624S.cjs');
16
+ var _chunk42JGOJCBcjs = require('./chunk-42JGOJCB.cjs');
17
17
  require('./chunk-LJT2KRE2.cjs');
18
18
  require('./chunk-RB7VLVMD.cjs');
19
19
 
@@ -1993,8 +1993,8 @@ function buildScriptsFixture(cast) {
1993
1993
  var _jsxruntime = require('react/jsx-runtime');
1994
1994
  var WIDE_HIDDEN_COLUMNS = ["result", "open"];
1995
1995
  var COMPACT_COLUMNS = [
1996
- { ..._chunk4JZC624Scjs.EXECUTION_COLUMNS.machineId, width: "flex-1 min-w-0", hideAt: void 0 },
1997
- _chunk4JZC624Scjs.EXECUTION_COLUMNS.status
1996
+ { ..._chunk42JGOJCBcjs.EXECUTION_COLUMNS.machineId, width: "flex-1 min-w-0", hideAt: void 0 },
1997
+ _chunk42JGOJCBcjs.EXECUTION_COLUMNS.status
1998
1998
  ];
1999
1999
  var COMPACT_ROWS = 4;
2000
2000
  var TOKEN_CLASS = {
@@ -2053,7 +2053,7 @@ function ScriptCodePicture({ script }) {
2053
2053
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex min-w-0 items-center justify-between gap-[var(--spacing-system-m)]", children: [
2054
2054
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkV3W56L2Dcjs.TruncateText, { variant: "h6", tone: "secondary", children: `Written by ${provenance.author} \xB7 approved by ${provenance.approver}` }),
2055
2055
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2056
- _chunk4JZC624Scjs.ScriptShellBadge,
2056
+ _chunk42JGOJCBcjs.ScriptShellBadge,
2057
2057
  {
2058
2058
  value: script.shellType,
2059
2059
  className: "shrink-0 text-ods-text-secondary text-h6",
@@ -2077,7 +2077,7 @@ function ScriptsScreen({ compact = false }) {
2077
2077
  return (
2078
2078
  // Four runs of the card's row height fill the frame; the padding is what is left round them.
2079
2079
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full overflow-hidden bg-ods-bg p-[var(--spacing-system-xsf)]", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2080
- _chunk4JZC624Scjs.ScriptExecutionsTable,
2080
+ _chunk42JGOJCBcjs.ScriptExecutionsTable,
2081
2081
  {
2082
2082
  rows: fixture.executions.slice(0, COMPACT_ROWS),
2083
2083
  columns: COMPACT_COLUMNS,
@@ -2087,13 +2087,13 @@ function ScriptsScreen({ compact = false }) {
2087
2087
  ) })
2088
2088
  );
2089
2089
  }
2090
- const renderOutput = (row) => row.id === fixture.openExecutionId ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk4JZC624Scjs.ScriptExecutionResult, { result: row.result, empty: "\u2014" }) : null;
2090
+ const renderOutput = (row) => row.id === fixture.openExecutionId ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk42JGOJCBcjs.ScriptExecutionResult, { result: row.result, empty: "\u2014" }) : null;
2091
2091
  return (
2092
2092
  // The frame's tighter padding and no header row: the output opened under a run needs the height.
2093
2093
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex h-full gap-[var(--spacing-system-m)] overflow-hidden bg-ods-bg p-[var(--spacing-system-m)]", children: [
2094
2094
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-[39%] shrink-0", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ScriptCodePicture, { script: fixture.script }) }),
2095
2095
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "min-w-0 flex-1 overflow-hidden", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2096
- _chunk4JZC624Scjs.ScriptExecutionsTable,
2096
+ _chunk42JGOJCBcjs.ScriptExecutionsTable,
2097
2097
  {
2098
2098
  rows: fixture.executions,
2099
2099
  hideColumns: WIDE_HIDDEN_COLUMNS,
@@ -15,7 +15,7 @@ var _chunkDKOLQPE7cjs = require('./chunk-DKOLQPE7.cjs');
15
15
 
16
16
 
17
17
 
18
- var _chunk4JZC624Scjs = require('./chunk-4JZC624S.cjs');
18
+ var _chunk42JGOJCBcjs = require('./chunk-42JGOJCB.cjs');
19
19
  require('./chunk-LJT2KRE2.cjs');
20
20
  require('./chunk-RB7VLVMD.cjs');
21
21
 
@@ -1920,12 +1920,12 @@ function StaticPackageField({ value }) {
1920
1920
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1921
1921
  _chunkV3W56L2Dcjs.Autocomplete,
1922
1922
  {
1923
- label: _chunk4JZC624Scjs.PACKAGE_SEARCH_LABEL,
1923
+ label: _chunk42JGOJCBcjs.PACKAGE_SEARCH_LABEL,
1924
1924
  labelVariant: "large",
1925
1925
  options: value ? [{ value: value.id, label: value.name, description: _nullishCoalesce(value.description, () => ( void 0)) }] : [],
1926
1926
  value: _nullishCoalesce(_optionalChain([value, 'optionalAccess', _ => _.id]), () => ( null)),
1927
1927
  onChange: noop,
1928
- placeholder: _chunk4JZC624Scjs.PACKAGE_SEARCH_PLACEHOLDER
1928
+ placeholder: _chunk42JGOJCBcjs.PACKAGE_SEARCH_PLACEHOLDER
1929
1929
  }
1930
1930
  );
1931
1931
  }
@@ -1939,7 +1939,7 @@ function SoftwareUpdateScreen({ compact = false }) {
1939
1939
  [fixture.devices, fixture.selectedIds]
1940
1940
  );
1941
1941
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full bg-ods-bg pt-[var(--spacing-system-l)]", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1942
- _chunk4JZC624Scjs.SoftwareActionForm,
1942
+ _chunk42JGOJCBcjs.SoftwareActionForm,
1943
1943
  {
1944
1944
  action: fixture.action,
1945
1945
  showHeader: false,
@@ -1951,7 +1951,7 @@ function SoftwareUpdateScreen({ compact = false }) {
1951
1951
  initialValues: fixture.initialValues,
1952
1952
  renderPackageSearch,
1953
1953
  renderDevicePicker: () => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1954
- _chunk4JZC624Scjs.DeviceSelector,
1954
+ _chunk42JGOJCBcjs.DeviceSelector,
1955
1955
  {
1956
1956
  devices: selected,
1957
1957
  loading: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flamingo-stack/openframe-frontend-core",
3
- "version": "0.0.774",
3
+ "version": "0.0.775",
4
4
  "description": "Shared design system and components for all Flamingo platforms",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -25,7 +25,7 @@
25
25
 
26
26
  import type React from 'react';
27
27
  import Image from '../../embed-shims/next-image';
28
- import { AVATAR_SIZE } from '../../styles/component-sizes';
28
+ import { AVATAR_SIZE, AVATAR_SIZE_PX } from '../../styles/component-sizes';
29
29
  import type { AuthorProfile } from '../../types/entity-author';
30
30
  import { cn } from '../../utils/cn';
31
31
  import { formatBioText } from '../../utils/format';
@@ -77,7 +77,15 @@ export function AuthorDetailView({ author, proxyImageUrl, children, className }:
77
77
  ) : (
78
78
  <div className="relative h-full w-full overflow-hidden rounded-full border border-ods-border bg-ods-bg-surface">
79
79
  {proxiedAvatar ? (
80
- <Image src={proxiedAvatar} alt={author.fullName} fill className="object-cover" unoptimized />
80
+ <Image
81
+ src={proxiedAvatar}
82
+ alt={author.fullName}
83
+ fill
84
+ // The frame's size, so a host that resizes serves a 96 px picture, not the 2500 px original.
85
+ sizes={`${AVATAR_SIZE_PX.profile}px`}
86
+ className="object-cover"
87
+ unoptimized
88
+ />
81
89
  ) : (
82
90
  <div className="flex h-full w-full items-center justify-center text-ods-text-secondary text-h2">
83
91
  {author.fullName.charAt(0).toUpperCase()}
@@ -580,6 +580,11 @@ export interface RelatedCardContext {
580
580
  anchorAttrs: Pick<CardLinkAnchorProps, 'target' | 'rel'>;
581
581
  /** Branded OG fallback for cards whose row has no featured image. */
582
582
  placeholderUrl?: string;
583
+ /**
584
+ * The rail's first card: its image is likely the page's largest paint (an author page opens on it), so a card that
585
+ * can rank its image fetches it at once instead of lazily.
586
+ */
587
+ priority?: boolean;
583
588
  extras?: ChatCardDispatchExtras;
584
589
  }
585
590
 
@@ -645,6 +650,7 @@ function programEntry(configKey: 'podcast' | 'webinar' | 'event'): RelatedCardRe
645
650
  href={ctx.href}
646
651
  targetPlatform={ctx.targetPlatform}
647
652
  placeholderUrl={ctx.placeholderUrl}
653
+ priority={ctx.priority}
648
654
  {...ctx.anchorAttrs}
649
655
  />
650
656
  );
@@ -684,6 +690,7 @@ export const RELATED_CARD_REGISTRY: Record<string, RelatedCardRegistryEntry> = {
684
690
  href={ctx.href}
685
691
  targetPlatform={ctx.targetPlatform}
686
692
  placeholderUrl={ctx.placeholderUrl}
693
+ priority={ctx.priority}
687
694
  {...ctx.anchorAttrs}
688
695
  />
689
696
  ),
@@ -200,8 +200,11 @@ function CardForType({
200
200
  linkProps,
201
201
  extras,
202
202
  adminCampaignCard,
203
+ priority,
203
204
  }: {
204
205
  type: string;
206
+ /** The rail's first card: its image is fetched at once (see `RelatedCardContext.priority`). */
207
+ priority?: boolean;
205
208
  /** One fetched row, still unvalidated. Its shape is decided at RUNTIME by
206
209
  * `type` (each group fetches from its own per-type list API), so it stays
207
210
  * `unknown` right up to the registry entry for that type — which owns the
@@ -247,6 +250,7 @@ function CardForType({
247
250
  // a numeric title reached `buildOgPlaceholderUrl` as a number. `rowString`
248
251
  // makes the claim true.
249
252
  placeholderUrl: extras?.buildOgPlaceholderUrl?.(rowString(item, 'title') ?? '') ?? undefined,
253
+ priority,
250
254
  extras,
251
255
  });
252
256
  }
@@ -361,11 +365,14 @@ function ContentGroup({
361
365
  heading,
362
366
  onLoadingChange,
363
367
  seedRows,
368
+ leadsRail = false,
364
369
  }: {
365
370
  type: string;
366
371
  refs: ContentRef[];
367
372
  /** The rows of this group's FIRST page, read by the server (`initialRows`). */
368
373
  seedRows?: ReadonlyArray<unknown>;
374
+ /** The rail's first group: its first card's image is fetched at once. */
375
+ leadsRail?: boolean;
369
376
  columns: 2 | 3;
370
377
  buildUrl: (type: string, ids: string[]) => string | null;
371
378
  resolveHref: (ref: ContentRef) => { href: string | null; targetPlatform: string | null };
@@ -448,7 +455,7 @@ function ContentGroup({
448
455
  const itemById = new Map(items.map(it => [rowKey(it), it]));
449
456
 
450
457
  const cards = pageRefs
451
- .map(contentRef => {
458
+ .map((contentRef, index) => {
452
459
  const itemId = String(contentRef.id);
453
460
  const item = itemById.get(itemId);
454
461
  if (!item) return null;
@@ -471,6 +478,8 @@ function ContentGroup({
471
478
  linkProps={linkProps}
472
479
  extras={extras}
473
480
  adminCampaignCard={adminCampaignCard}
481
+ // The rail's first card, on its first page: the top of the rail, often the page's largest paint.
482
+ priority={leadsRail && page === 1 && index === 0}
474
483
  />
475
484
  )}
476
485
  </LinkProvider>
@@ -787,7 +796,7 @@ export function RelatedContentSection({
787
796
  return (
788
797
  <LoadingRegion as="section" label="Loading related content" announce={loadingTypes.size > 0} className={RAIL_CLASS}>
789
798
  <h2 className={RAIL_TITLE_CLASS}>{accentSentenceMarks(title)}</h2>
790
- {orderedTypes.map(type => (
799
+ {orderedTypes.map((type, groupIndex) => (
791
800
  <ContentGroup
792
801
  key={type}
793
802
  type={type}
@@ -801,6 +810,7 @@ export function RelatedContentSection({
801
810
  heading={<GroupHeading type={type} />}
802
811
  onLoadingChange={onGroupLoadingChange}
803
812
  seedRows={initialRows?.[type]}
813
+ leadsRail={groupIndex === 0}
804
814
  />
805
815
  ))}
806
816
  </LoadingRegion>
@@ -181,6 +181,14 @@ export interface PhotoWallProps {
181
181
  className?: string;
182
182
  /** The pictures are still being fetched: show the wall's own placeholder frames. */
183
183
  loading?: boolean;
184
+ /**
185
+ * The width a column is drawn at, as an image `sizes` value (e.g. `(min-width: 1024px) 25vw, 50vw`). With it, and a
186
+ * size on every picture, the wall is laid out at once, on the server too, and each picture is drawn with the host's
187
+ * `srcset`: the first frames are in the HTML and fetched before any script runs, instead of after the wall has been
188
+ * measured in the browser (CU-17tkuw5xp4q: the careers hero's largest paint waited for that). Two walls given the
189
+ * same pictures and `sizes` (one per breakpoint) ask for the same files.
190
+ */
191
+ sizes?: string;
184
192
  }
185
193
 
186
194
  export function PhotoWall({
@@ -190,6 +198,7 @@ export function PhotoWall({
190
198
  fadeColor,
191
199
  className,
192
200
  loading: loadingProp = false,
201
+ sizes,
193
202
  }: PhotoWallProps) {
194
203
  // In a page's loading state the pictures are read: the wall draws its placeholder frames and measures nothing.
195
204
  const inScope = useLoadingScope();
@@ -225,15 +234,18 @@ export function PhotoWall({
225
234
  : undefined;
226
235
  // One frame per picture: a url listed twice is its first entry. Nothing is
227
236
  // asked for before the wall knows its width.
237
+ // Laid out without measuring: every picture's size is stated and the caller states the column's width.
238
+ const sizedUpFront = !!sizes && images.length > 0 && images.every(image => image.width && image.height);
228
239
  const unique = useMemo(() => {
229
- if (requestWidth === undefined || inScope) return [];
240
+ if (inScope || (!sizedUpFront && requestWidth === undefined)) return [];
230
241
  const byUrl = new Map<string, PhotoWallImage>();
231
242
  for (const image of images) if (!byUrl.has(image.url)) byUrl.set(image.url, image);
232
243
  return Array.from(byUrl.values());
233
- }, [images, requestWidth, inScope]);
244
+ }, [images, requestWidth, inScope, sizedUpFront]);
234
245
  // Each picture is measured at the url it is drawn from (the boxed `Image`'s
235
246
  // `optimizedWidth`: the host's resizer at the column's width), so it is downloaded once.
236
- const drawnWidth = requestWidth ?? undefined;
247
+ // Laid out up front, a picture is drawn from its own url with the host's `srcset` (`sizes`), on the server too.
248
+ const drawnWidth = sizedUpFront ? undefined : (requestWidth ?? undefined);
237
249
  const drawnUrl = useCallback((url: string) => (drawnWidth ? resizedImageUrl(url, drawnWidth) : url), [drawnWidth]);
238
250
  // A picture whose size the host stated has its orientation now; only the others are measured.
239
251
  const known = useMemo(() => {
@@ -276,7 +288,7 @@ export function PhotoWall({
276
288
 
277
289
  if (images.length === 0 && !loading) return null;
278
290
  // Until the pictures arrive AND their sizes are known, the same placeholder frames.
279
- const columnsToDraw = loading || requestWidth === undefined ? null : layout;
291
+ const columnsToDraw = loading || (!sizedUpFront && requestWidth === undefined) ? null : layout;
280
292
 
281
293
  const gridStyle = {
282
294
  gridTemplateColumns: `repeat(${columnsToDraw?.length || columns}, minmax(0, 1fr))`,
@@ -312,10 +324,9 @@ export function PhotoWall({
312
324
  alt={photo.alt ?? ''}
313
325
  fill
314
326
  className="object-cover"
315
- // Asked for at the column's width. With no step that covers the column the
316
- // caller's url is drawn as it is (already sized by its own image service).
317
- optimizedWidth={drawnWidth}
318
- unoptimized
327
+ // Asked for at the column's width: through the host's `srcset` when the wall was laid
328
+ // out up front, else at the measured width (the caller's url when no step covers it).
329
+ {...(sizedUpFront ? { sizes } : { optimizedWidth: drawnWidth, unoptimized: true })}
319
330
  // A column's first frames are on screen when the wall appears (one of them is
320
331
  // usually the page's largest paint), so they are not left to lazy loading.
321
332
  loading={i < EAGER_PHOTOS_PER_COLUMN ? 'eager' : 'lazy'}