@flamingo-stack/openframe-frontend-core 0.0.718 → 0.0.719

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 (86) hide show
  1. package/dist/{chunk-N2PF5CFX.js → chunk-2JLP2X2M.js} +1 -1
  2. package/dist/{chunk-YEMCCYIF.js → chunk-4D6NMPOO.js} +1 -1
  3. package/dist/{chunk-MIQLUUVF.cjs → chunk-4JIGEK7C.cjs} +6 -6
  4. package/dist/{chunk-JIWMBIVZ.js → chunk-5U3KWQXP.js} +3 -3
  5. package/dist/{chunk-67QBMN4R.js → chunk-5YAGX5DY.js} +1 -1
  6. package/dist/{chunk-GEDY7HQI.cjs → chunk-6X3O67OL.cjs} +4 -0
  7. package/dist/{chunk-DEZDMTBM.js → chunk-7BCY77W6.js} +5 -5
  8. package/dist/{chunk-BWIN42ZA.cjs → chunk-7BREFV7G.cjs} +19 -19
  9. package/dist/{chunk-45F7PXUI.js → chunk-AJHSQ5XK.js} +2 -2
  10. package/dist/{chunk-TBCZCRQ5.js → chunk-CU6NWO2M.js} +5 -5
  11. package/dist/{chunk-DCD5WZUF.js → chunk-D6PMVWDI.js} +2 -2
  12. package/dist/{chunk-2RKFMUNW.cjs → chunk-DFEPUYTF.cjs} +24 -21
  13. package/dist/{chunk-BOTM373R.cjs → chunk-E2V2EH2G.cjs} +2 -2
  14. package/dist/{chunk-PQ6HBLZ7.js → chunk-EAHOB64X.js} +3 -3
  15. package/dist/{chunk-SGFTTJKO.js → chunk-FVQJBII6.js} +1 -1
  16. package/dist/{chunk-APJZBDQN.js → chunk-H5V7GRN5.js} +24 -21
  17. package/dist/{chunk-6A43ZDWV.cjs → chunk-H5VIMBRN.cjs} +2 -2
  18. package/dist/{chunk-DXCBOZVT.cjs → chunk-HCFNEKO3.cjs} +13 -13
  19. package/dist/{chunk-Z5UWW3Z2.cjs → chunk-LCLDFMDF.cjs} +66 -50
  20. package/dist/{chunk-NDZ5ZXOQ.cjs → chunk-LOZLHG2U.cjs} +6 -6
  21. package/dist/{chunk-Z3CVSPO3.cjs → chunk-NQVS4G7G.cjs} +134 -134
  22. package/dist/{chunk-EZC2F5OT.cjs → chunk-NYWESHOQ.cjs} +15 -15
  23. package/dist/{chunk-YBQQNDC7.js → chunk-RR6PY34K.js} +4 -0
  24. package/dist/{chunk-GN4A7PMB.cjs → chunk-SWAVO7BB.cjs} +23 -23
  25. package/dist/{chunk-S2FUFMLW.cjs → chunk-TDZ76NCB.cjs} +16 -16
  26. package/dist/{chunk-QXZCLQNV.js → chunk-TELAANY6.js} +1 -1
  27. package/dist/{chunk-MM7MCIXL.js → chunk-TTEFN6VO.js} +4 -4
  28. package/dist/{chunk-YC5COB5V.cjs → chunk-WS7WRR5Z.cjs} +32 -32
  29. package/dist/{chunk-LOYPRJWA.js → chunk-Y4MB25KW.js} +66 -50
  30. package/dist/{chunk-WALCXC7J.cjs → chunk-ZKNZ2CXW.cjs} +3 -3
  31. package/dist/{cloud-tenants-TAKOWXLD.js → cloud-tenants-DNBPNQNP.js} +2 -2
  32. package/dist/{cloud-tenants-PFDF7O5U.cjs → cloud-tenants-FBUO66GW.cjs} +3 -3
  33. package/dist/components/case-studies/index.cjs +11 -11
  34. package/dist/components/case-studies/index.js +3 -3
  35. package/dist/components/contact/index.cjs +4 -4
  36. package/dist/components/contact/index.js +3 -3
  37. package/dist/components/docs/index.cjs +6 -6
  38. package/dist/components/docs/index.js +5 -5
  39. package/dist/components/embeds/index.cjs +4 -4
  40. package/dist/components/embeds/index.js +3 -3
  41. package/dist/components/faq/index.cjs +5 -5
  42. package/dist/components/faq/index.js +4 -4
  43. package/dist/components/features/index.cjs +3 -3
  44. package/dist/components/features/index.js +2 -2
  45. package/dist/components/features/remote-session/remote-desktop-view.d.ts +6 -2
  46. package/dist/components/features/remote-session/remote-desktop-view.d.ts.map +1 -1
  47. package/dist/components/help-center-pages/index.cjs +48 -48
  48. package/dist/components/help-center-pages/index.js +13 -13
  49. package/dist/components/index.cjs +23 -23
  50. package/dist/components/index.js +12 -12
  51. package/dist/components/meeting-scheduler/index.cjs +18 -18
  52. package/dist/components/meeting-scheduler/index.js +2 -2
  53. package/dist/components/navigation/index.cjs +4 -4
  54. package/dist/components/navigation/index.js +3 -3
  55. package/dist/components/onboarding-guides/index.cjs +7 -7
  56. package/dist/components/onboarding-guides/index.js +6 -6
  57. package/dist/components/product-demo/index.cjs +7 -7
  58. package/dist/components/product-demo/index.js +7 -7
  59. package/dist/components/related-content/index.cjs +3 -3
  60. package/dist/components/related-content/index.js +2 -2
  61. package/dist/components/related-content/related-content-section.d.ts +6 -6
  62. package/dist/components/related-content/related-content-section.d.ts.map +1 -1
  63. package/dist/components/tickets/index.cjs +6 -6
  64. package/dist/components/tickets/index.js +5 -5
  65. package/dist/components/ui/index.cjs +3 -3
  66. package/dist/components/ui/index.js +2 -2
  67. package/dist/{devices-KRCGZMGA.cjs → devices-5A5TY4JD.cjs} +4 -4
  68. package/dist/{devices-LHEX7O5T.js → devices-PLDAEUSL.js} +2 -2
  69. package/dist/hooks/use-self-fetch.d.ts +3 -0
  70. package/dist/hooks/use-self-fetch.d.ts.map +1 -1
  71. package/dist/index.cjs +4 -4
  72. package/dist/index.js +3 -3
  73. package/dist/{knowledge-KLVPVEYZ.cjs → knowledge-FKTJ723Z.cjs} +3 -3
  74. package/dist/{knowledge-RZSFNR33.js → knowledge-G6Q5KRWV.js} +2 -2
  75. package/dist/{logs-OX3XTVL2.cjs → logs-CJCXUBU6.cjs} +11 -11
  76. package/dist/{logs-E5DOV5JO.js → logs-QMV6XIC4.js} +2 -2
  77. package/dist/{policies-MC2S4S5G.js → policies-G6D7AX6F.js} +2 -2
  78. package/dist/{policies-FOSCIZGK.cjs → policies-QMRTK3CY.cjs} +7 -7
  79. package/dist/{remote-session-DRFE5XZE.cjs → remote-session-2JVI7RVA.cjs} +3 -3
  80. package/dist/{remote-session-JQ2BPIE3.js → remote-session-DBD6OSTX.js} +1 -1
  81. package/dist/{software-update-D4OEIH65.js → software-update-6BQM6BKH.js} +2 -2
  82. package/dist/{software-update-HOBSTWVO.cjs → software-update-PNZMRDBT.cjs} +7 -7
  83. package/package.json +1 -1
  84. package/src/components/features/remote-session/remote-desktop-view.tsx +86 -63
  85. package/src/components/related-content/related-content-section.tsx +46 -47
  86. package/src/hooks/use-self-fetch.ts +7 -0
@@ -28,12 +28,12 @@
28
28
  * `renderSkeletonForType` so the placeholder height matches the loaded card
29
29
  * exactly (zero layout shift on resolve).
30
30
  *
31
- * One API call per content type via the shared list-URL builder
32
- * (`buildListUrl` — injectable; defaults to the lib's byte-parity-tested
33
- * builder prefixed with `apiBaseUrl`). Fetching uses `useSelfFetch` (plain
34
- * fetch, NO react-query) so third-party embedders need no QueryClientProvider;
35
- * cards are imported via DEEP module paths (not the chat barrel) so this
36
- * chunk never reaches `@tanstack/react-query`.
31
+ * One API call per page of a content type, carrying only that page's ids, via
32
+ * the shared list-URL builder (`buildListUrl` — injectable; defaults to the
33
+ * lib's byte-parity-tested builder prefixed with `apiBaseUrl`). Fetching uses
34
+ * `useSelfFetch` (plain fetch, NO react-query) so third-party embedders need
35
+ * no QueryClientProvider; cards are imported via DEEP module paths (not the
36
+ * chat barrel) so this chunk never reaches `@tanstack/react-query`.
37
37
  *
38
38
  * The per-type card + skeleton dispatch lives in `./card-registry`
39
39
  * (`RELATED_CARD_REGISTRY`): ONE entry per content type, each carrying that
@@ -49,7 +49,7 @@
49
49
  */
50
50
 
51
51
  import type React from 'react';
52
- import { useEffect, useMemo, useRef, useState } from 'react';
52
+ import { useMemo, useState } from 'react';
53
53
  import { useSelfFetch } from '../../hooks/use-self-fetch';
54
54
  import type { ContentRef, ContentRefWithReason } from '../../types/content-ref';
55
55
  import {
@@ -246,23 +246,29 @@ function CardForType({
246
246
  }
247
247
 
248
248
  // =============================================================================
249
- // Fetch all items for a type in ONE server-sorted call, via the injectable
250
- // list-URL builder. `useSelfFetch` (URL = cache key) replaces the hub's old
251
- // react-query usage: `enabled` ≙ `url === null`, `!res.ok`/network error ≙
252
- // `error → items null → group renders nothing`. Accepted deltas vs
253
- // react-query: no retry/backoff, no focus refetch, no cross-mount cache.
249
+ // Fetch ONE page of a group — only the ids on screen go into the list URL.
250
+ // Pages already fetched are kept by url, so paging back renders without a
251
+ // request. `useSelfFetch` replaces the hub's old react-query usage: `enabled`
252
+ // ≙ `url === null` (no list API → `items: null`), `!res.ok`/network error ≙
253
+ // an empty page. Accepted deltas vs react-query: no retry/backoff, no focus
254
+ // refetch, no cross-mount cache.
254
255
  // =============================================================================
255
256
 
256
257
  function useGroupItems(type: string, refs: ContentRef[], buildUrl: (type: string, ids: string[]) => string | null) {
257
258
  const ids = refs.map(r => r.id);
258
259
  const url = ids.length > 0 ? buildUrl(type, ids) : null;
259
- const { data, isLoading } = useSelfFetch<unknown>(url);
260
- const items = data != null ? extractItems(data) : null;
261
- return { items, isLoading };
260
+ const [pages, setPages] = useState<ReadonlyMap<string, unknown[]>>(() => new Map());
261
+ const cached = url !== null ? pages.get(url) : undefined;
262
+ const { data, dataUrl, error } = useSelfFetch<unknown>(cached ? null : url);
263
+ if (data != null && dataUrl !== null && !pages.has(dataUrl)) {
264
+ setPages(new Map(pages).set(dataUrl, extractItems(data)));
265
+ }
266
+ if (url === null) return { items: null, isLoading: false };
267
+ return { items: cached ?? (error ? [] : null), isLoading: !cached && !error };
262
268
  }
263
269
 
264
270
  // =============================================================================
265
- // Per-group renderer — one API call, server-sorted, then render cards via the
271
+ // Per-group renderer — one API call per page, then render cards via the
266
272
  // dispatcher with per-type skeletons + per-type layout from CONTENT_REF_GROUPS.
267
273
  // =============================================================================
268
274
 
@@ -325,41 +331,33 @@ function ContentGroup({
325
331
  * its heading too — no orphaned titles. */
326
332
  heading: React.ReactNode;
327
333
  }) {
328
- const { items, isLoading } = useGroupItems(type, refs, buildUrl);
329
334
  const config = resolveGroupConfig(type);
330
335
  const isListLayout = config.layout === 'list';
331
336
  const cardSize = config.gridSize;
332
337
 
333
338
  // Per-group pagination for big groups (author pages): GROUP_PAGE_SIZE items
334
- // per page with the standard Pagination control below the group. Client-side
335
- // slicing — useGroupItems already fetched every row in one batched call, so
336
- // page flips are instant. Hooks live above every early return (file
337
- // convention). Page is clamped so a shrinking refs array (suggestion
338
- // refetch) can never strand the view past the last page, and RESET when the
339
- // ref set actually changes (shrink→grow must not return to a stale page).
339
+ // per page with the standard Pagination control below the group, each page
340
+ // fetched on its own. Back to page 1 whenever the ref set changes (a
341
+ // suggestion refetch can shrink it) — reset while rendering, so the stale
342
+ // page is never requested.
340
343
  const [page, setPage] = useState(1);
341
344
  const refsKey = refs.map(r => r.id).join('|');
342
- const prevRefsKeyRef = useRef(refsKey);
343
- useEffect(() => {
344
- if (prevRefsKeyRef.current !== refsKey) {
345
- prevRefsKeyRef.current = refsKey;
346
- setPage(1);
347
- }
348
- }, [refsKey]);
345
+ const [pagedRefsKey, setPagedRefsKey] = useState(refsKey);
346
+ if (pagedRefsKey !== refsKey) {
347
+ setPagedRefsKey(refsKey);
348
+ setPage(1);
349
+ }
349
350
  const totalGroupPages = pageCount(refs.length, GROUP_PAGE_SIZE);
350
- const safePage = Math.min(page, totalGroupPages);
351
- const visibleGroupRefs =
352
- refs.length > GROUP_PAGE_SIZE ? refs.slice((safePage - 1) * GROUP_PAGE_SIZE, safePage * GROUP_PAGE_SIZE) : refs;
351
+ const pageRefs = refs.slice((page - 1) * GROUP_PAGE_SIZE, page * GROUP_PAGE_SIZE);
352
+ const { items, isLoading } = useGroupItems(type, pageRefs, buildUrl);
353
353
  const groupPagination =
354
- totalGroupPages > 1 ? (
355
- <Pagination currentPage={safePage} totalPages={totalGroupPages} onPageChange={setPage} />
356
- ) : null;
357
-
358
- // Skeleton gate: `isLoading && !items` — SSR HTML and the client's first
359
- // paint render identical skeletons (useSelfFetch starts isLoading=true on
360
- // both sides), and once items exist they are never replaced by skeletons.
361
- if (isLoading && !items) {
362
- const skeletons = visibleGroupRefs.map(r => (
354
+ totalGroupPages > 1 ? <Pagination currentPage={page} totalPages={totalGroupPages} onPageChange={setPage} /> : null;
355
+
356
+ // Skeletons until this page's rows land — SSR HTML and the client's first
357
+ // paint render identical skeletons (useSelfFetch starts loading on both
358
+ // sides). The pager stays in place while an unfetched page loads.
359
+ if (isLoading) {
360
+ const skeletons = pageRefs.map(r => (
363
361
  <div key={r.id}>{renderSkeletonForType(type, cardSize, adminCampaignCard)}</div>
364
362
  ));
365
363
  return (
@@ -370,11 +368,12 @@ function ContentGroup({
370
368
  ) : (
371
369
  <div className={gridClassFor(columns)}>{skeletons}</div>
372
370
  )}
371
+ {groupPagination}
373
372
  </div>
374
373
  );
375
374
  }
376
375
 
377
- if (!items || items.length === 0) return null;
376
+ if (!items) return null;
378
377
 
379
378
  // Index fetched rows by id, then render in REF order — refs carry the
380
379
  // intended sequence (suggestion mode: the engine's tier order, so
@@ -393,7 +392,7 @@ function ContentGroup({
393
392
  };
394
393
  const itemById = new Map(items.map(it => [rowKey(it), it]));
395
394
 
396
- const cards = visibleGroupRefs
395
+ const cards = pageRefs
397
396
  .map(contentRef => {
398
397
  const itemId = String(contentRef.id);
399
398
  const item = itemById.get(itemId);
@@ -426,9 +425,9 @@ function ContentGroup({
426
425
  .filter(Boolean);
427
426
 
428
427
  if (cards.length === 0) {
429
- // Current PAGE resolved zero cards (rows deleted between the ref fetch
430
- // and the group fetch, or a stricter list-API gate dropped them). When a
431
- // pager exists the user must keep the controls to navigate back —
428
+ // Current PAGE resolved zero cards (its fetch failed, rows were deleted
429
+ // between the ref fetch and the page fetch, or a stricter list-API gate
430
+ // dropped them). When a pager exists the user must keep the controls —
432
431
  // dropping the whole group would strand them. A genuinely empty group
433
432
  // (no pager) still vanishes with its heading.
434
433
  if (groupPagination) {
@@ -5,6 +5,9 @@ import { contentFetch } from '../utils/embed-content-fetch';
5
5
 
6
6
  export interface UseSelfFetchResult<T> {
7
7
  data: T | null;
8
+ /** The url `data` came from (`null` until something lands). While a new url
9
+ * loads, `data` still holds the previous one — compare before trusting it. */
10
+ dataUrl: string | null;
8
11
  /** Imperatively patch the fetched data (e.g. optimistic vote updates). */
9
12
  setData: Dispatch<SetStateAction<T | null>>;
10
13
  isLoading: boolean;
@@ -48,6 +51,7 @@ export function useSelfFetch<T>(
48
51
  const initialData = options?.initialData;
49
52
  const revalidateOnVisibleAfterMs = options?.revalidateOnVisibleAfterMs;
50
53
  const [data, setData] = useState<T | null>(initialData ?? null);
54
+ const [dataUrl, setDataUrl] = useState<string | null>(initialData !== undefined ? url : null);
51
55
  const [isLoading, setIsLoading] = useState(initialData === undefined && url !== null);
52
56
  const [error, setError] = useState(false);
53
57
  const [reloadKey, setReloadKey] = useState(0);
@@ -78,6 +82,7 @@ export function useSelfFetch<T>(
78
82
  // `initialData` is `undefined`.
79
83
  if (syncedProps.initialData !== initialData && initialData !== undefined) {
80
84
  setData(initialData);
85
+ setDataUrl(url);
81
86
  }
82
87
  // Fetching just got disabled — nothing will land to clear the spinner.
83
88
  if (syncedProps.url !== url && url === null) {
@@ -101,6 +106,7 @@ export function useSelfFetch<T>(
101
106
  const json = (await res.json()) as T;
102
107
  if (!cancelled) {
103
108
  setData(json);
109
+ setDataUrl(url);
104
110
  dataUrlRef.current = url; // remember the url we now hold data for
105
111
  dataAtRef.current = Date.now();
106
112
  }
@@ -149,6 +155,7 @@ export function useSelfFetch<T>(
149
155
 
150
156
  return {
151
157
  data,
158
+ dataUrl,
152
159
  setData,
153
160
  isLoading,
154
161
  error,