@flamingo-stack/openframe-frontend-core 0.0.203 → 0.0.204-snapshot.20260523020255

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 (70) hide show
  1. package/dist/chunk-27APPAJN.cjs +24 -0
  2. package/dist/chunk-27APPAJN.cjs.map +1 -0
  3. package/dist/{chunk-XGL5FKIK.js → chunk-D6BNDYZK.js} +114 -4
  4. package/dist/chunk-D6BNDYZK.js.map +1 -0
  5. package/dist/chunk-MJNXIEV2.js +24 -0
  6. package/dist/chunk-MJNXIEV2.js.map +1 -0
  7. package/dist/{chunk-25LVV26X.cjs → chunk-NLXY4QZE.cjs} +114 -4
  8. package/dist/chunk-NLXY4QZE.cjs.map +1 -0
  9. package/dist/{chunk-E6Q6UGDK.js → chunk-OFCRX24Y.js} +383 -569
  10. package/dist/chunk-OFCRX24Y.js.map +1 -0
  11. package/dist/{chunk-3YH2M76N.cjs → chunk-RDXOQWBY.cjs} +556 -742
  12. package/dist/chunk-RDXOQWBY.cjs.map +1 -0
  13. package/dist/components/announcement-bar.d.ts.map +1 -1
  14. package/dist/components/features/index.cjs +4 -3
  15. package/dist/components/features/index.cjs.map +1 -1
  16. package/dist/components/features/index.js +3 -2
  17. package/dist/components/index.cjs +4 -3
  18. package/dist/components/index.cjs.map +1 -1
  19. package/dist/components/index.js +3 -2
  20. package/dist/components/navigation/index.cjs +4 -3
  21. package/dist/components/navigation/index.cjs.map +1 -1
  22. package/dist/components/navigation/index.js +3 -2
  23. package/dist/components/navigation/navigation-sidebar.d.ts.map +1 -1
  24. package/dist/components/shared/product-release/product-release-card-skeleton.d.ts +1 -1
  25. package/dist/components/shared/product-release/product-release-card-skeleton.d.ts.map +1 -1
  26. package/dist/components/shared/product-release/product-release-card.d.ts +19 -12
  27. package/dist/components/shared/product-release/product-release-card.d.ts.map +1 -1
  28. package/dist/components/ui/index.cjs +4 -3
  29. package/dist/components/ui/index.cjs.map +1 -1
  30. package/dist/components/ui/index.js +3 -2
  31. package/dist/contexts/endpoints-runtime-context.d.ts +28 -0
  32. package/dist/contexts/endpoints-runtime-context.d.ts.map +1 -0
  33. package/dist/contexts/index.cjs +13 -0
  34. package/dist/contexts/index.cjs.map +1 -0
  35. package/dist/contexts/index.d.ts +24 -0
  36. package/dist/contexts/index.d.ts.map +1 -0
  37. package/dist/contexts/index.js +13 -0
  38. package/dist/contexts/index.js.map +1 -0
  39. package/dist/hooks/index.cjs +5 -2
  40. package/dist/hooks/index.cjs.map +1 -1
  41. package/dist/hooks/index.d.ts +1 -0
  42. package/dist/hooks/index.d.ts.map +1 -1
  43. package/dist/hooks/index.js +4 -1
  44. package/dist/hooks/use-access-code-integration.d.ts +48 -0
  45. package/dist/hooks/use-access-code-integration.d.ts.map +1 -0
  46. package/dist/hooks/use-contact-submission.d.ts.map +1 -1
  47. package/dist/index.cjs +4 -3
  48. package/dist/index.cjs.map +1 -1
  49. package/dist/index.js +7 -6
  50. package/dist/utils/access-code-client.d.ts +21 -37
  51. package/dist/utils/access-code-client.d.ts.map +1 -1
  52. package/dist/utils/index.cjs +10 -51
  53. package/dist/utils/index.cjs.map +1 -1
  54. package/dist/utils/index.js +11 -47
  55. package/dist/utils/index.js.map +1 -1
  56. package/package.json +7 -1
  57. package/src/components/announcement-bar.tsx +25 -3
  58. package/src/components/navigation/navigation-sidebar.tsx +3 -1
  59. package/src/components/shared/product-release/product-release-card-skeleton.tsx +66 -70
  60. package/src/components/shared/product-release/product-release-card.tsx +194 -242
  61. package/src/contexts/endpoints-runtime-context.tsx +68 -0
  62. package/src/contexts/index.ts +29 -0
  63. package/src/hooks/index.ts +6 -0
  64. package/src/hooks/use-access-code-integration.ts +107 -0
  65. package/src/hooks/use-contact-submission.ts +6 -2
  66. package/src/utils/access-code-client.ts +32 -75
  67. package/dist/chunk-25LVV26X.cjs.map +0 -1
  68. package/dist/chunk-3YH2M76N.cjs.map +0 -1
  69. package/dist/chunk-E6Q6UGDK.js.map +0 -1
  70. package/dist/chunk-XGL5FKIK.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flamingo-stack/openframe-frontend-core",
3
- "version": "0.0.203",
3
+ "version": "0.0.204-snapshot.20260523020255",
4
4
  "description": "Shared design system and components for all Flamingo platforms",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,6 +18,12 @@
18
18
  "require": "./dist/index.cjs",
19
19
  "default": "./dist/index.js"
20
20
  },
21
+ "./contexts": {
22
+ "types": "./dist/contexts/index.d.ts",
23
+ "import": "./dist/contexts/index.js",
24
+ "require": "./dist/contexts/index.cjs",
25
+ "default": "./dist/contexts/index.js"
26
+ },
21
27
  "./components": {
22
28
  "types": "./dist/components/index.d.ts",
23
29
  "import": "./dist/components/index.js",
@@ -12,6 +12,7 @@ import {
12
12
  } from '../utils/announcement-storage';
13
13
  import { Announcement } from '../types/announcement';
14
14
  import { getAppType } from '../utils/app-config';
15
+ import { useEndpointsRuntime } from '../contexts/endpoints-runtime-context';
15
16
 
16
17
  // Helper that defers to renderSvgIcon so we don't need local icon imports
17
18
  const getSvgIcon = (
@@ -33,6 +34,15 @@ export function AnnouncementBar() {
33
34
  // Get the platform type for platform-specific localStorage keys
34
35
  const platform = getAppType();
35
36
 
37
+ // Optional endpoint runtime: when no provider is mounted (e.g. on a
38
+ // bare React-tree page that doesn't wrap with HubRuntimeProvider),
39
+ // the bar silently skips its fetch instead of throwing. Apps that DO
40
+ // mount the provider get the configured URL — typically
41
+ // '/api/announcements/active' in the hub, or a proxied path in an
42
+ // embedded host.
43
+ const endpoints = useEndpointsRuntime();
44
+ const announcementsUrl = endpoints?.announcementsUrl;
45
+
36
46
  // Helper to determine dismissal key for localStorage
37
47
  const getDismissKey = (id: string) => `${platform}-announcement-${id}-dismissed`;
38
48
 
@@ -41,9 +51,12 @@ export function AnnouncementBar() {
41
51
 
42
52
  // Fetch active announcement from API and update state + LS
43
53
  const fetchActiveAnnouncement = async () => {
54
+ // No provider mounted → no URL configured → skip fetch silently.
55
+ // Cached announcement from previous sessions still renders if present.
56
+ if (!announcementsUrl) return;
44
57
  try {
45
58
  // Server-side platform injection - no URL parameter needed
46
- const response = await fetch(`/api/announcements/active`);
59
+ const response = await fetch(announcementsUrl);
47
60
 
48
61
  if (response.ok) {
49
62
  const data = await response.json();
@@ -92,13 +105,22 @@ export function AnnouncementBar() {
92
105
  setIsVisible(!isDismissed);
93
106
  }
94
107
 
108
+ // No provider mounted → no URL → no fetch / no polling. Cached
109
+ // announcement still painted above. Skip scheduling the 5-min
110
+ // interval entirely to avoid an idle timer + repeated short-circuit
111
+ // calls.
112
+ if (!announcementsUrl) return;
113
+
95
114
  // Always fetch latest on mount
96
115
  fetchActiveAnnouncement();
97
116
 
98
- // Schedule refresh every 5 minutes
117
+ // Schedule refresh every 5 minutes. When announcementsUrl flips
118
+ // (e.g. provider value swap), the effect re-runs and restarts the
119
+ // interval against the new URL — no stale captured fetch.
99
120
  const interval = setInterval(fetchActiveAnnouncement, 300_000);
100
121
  return () => clearInterval(interval);
101
- }, []);
122
+ // eslint-disable-next-line react-hooks/exhaustive-deps
123
+ }, [announcementsUrl]);
102
124
 
103
125
  // helpers
104
126
  const handleDismiss = () => {
@@ -83,7 +83,9 @@ export function NavigationSidebar({ config, disabled = false }: NavigationSideba
83
83
  } else if (item.path) {
84
84
  config.onNavigate?.(item.path)
85
85
  }
86
- }, [config])
86
+
87
+ if (isTablet) setTabletMinimized(true)
88
+ }, [config, isTablet])
87
89
 
88
90
  const { primaryItems, secondaryItems } = useMemo(() => ({
89
91
  primaryItems: config.items.filter(item => item.section !== 'secondary'),
@@ -8,23 +8,20 @@ export interface ProductReleaseCardSkeletonProps {
8
8
  className?: string
9
9
  /** Card density. Must match the loaded card's `size` prop so the loading
10
10
  * height matches the resolved height (no layout shift on resolve). */
11
- size?: 'default' | 'sm' | 'catalog'
11
+ size?: 'lg' | 'sm'
12
12
  }
13
13
 
14
- export function ProductReleaseCardSkeleton({ className, size = 'default' }: ProductReleaseCardSkeletonProps) {
15
- // ----- CATALOG branch — must match ProductReleaseCard size='catalog'.
14
+ export function ProductReleaseCardSkeleton({ className, size = 'lg' }: ProductReleaseCardSkeletonProps) {
15
+ // ----- LG branch — must match ProductReleaseCard size='lg'.
16
16
  // Same outer frame (`bg-ods-system-greys-black border border-ods-border …
17
17
  // p-6 gap-4`). Inner: hero (16:9 cover + version pill + title + summary),
18
- // changelog strip placeholder (always rendered see note), metadata-grid
19
- // footer (4 cells via grid). Heights chosen to match the loaded card's
20
- // rendered metrics so the 5-card slot grid in `ReleasesList` doesn't
21
- // jump on resolve.
22
- //
23
- // Note: the loaded card hides the changelog strip when total === 0
24
- // (rare most releases have at least one feature/fix/improvement).
25
- // The skeleton always renders the placeholder; net effect is a ~28px
26
- // shrink on empty-changelog releases. Documented tradeoff.
27
- if (size === 'catalog') {
18
+ // changelog strip placeholder, metadata-grid footer (4 cells via grid).
19
+ // Heights chosen to match the loaded card's rendered metrics so the
20
+ // 5-card slot grid in `ReleasesList` doesn't jump on resolve. The
21
+ // loaded card ALSO always renders the changelog strip + a fixed 4-cell
22
+ // grid (with em-dash placeholders for missing values), so this
23
+ // skeleton's shape matches exactly with zero load-to-resolve reflow.
24
+ if (size === 'lg') {
28
25
  return (
29
26
  <div
30
27
  className={cn(
@@ -34,27 +31,62 @@ export function ProductReleaseCardSkeleton({ className, size = 'default' }: Prod
34
31
  className,
35
32
  )}
36
33
  >
37
- {/* HERO */}
34
+ {/* HERO — placeholders use `bg-ods-border` (#3a3a3a) so they
35
+ contrast against the card's `bg-ods-system-greys-black`
36
+ (#212121) container. The metadata grid cells below use
37
+ `bg-ods-card` containers so `bg-ods-bg` placeholders work
38
+ there, but in the hero the card IS `bg-ods-card`-equivalent —
39
+ `bg-ods-bg` (#161616) is only 6 hex points darker than the
40
+ card and renders nearly invisible.
41
+
42
+ CRITICAL: title + summary use the SAME min-h containers as
43
+ the loaded card so total card height is byte-identical
44
+ between skeleton state and loaded state. Without this,
45
+ individual placeholder heights underrun the loaded card's
46
+ min-h reservations and the page jumps on resolve. */}
38
47
  <div className="flex flex-col md:flex-row gap-4 md:gap-6">
39
- <div className="w-full md:w-[256px] aspect-[16/9] bg-ods-bg rounded-lg flex-shrink-0" />
48
+ <div className="w-full md:w-[256px] aspect-[16/9] bg-ods-border rounded-lg flex-shrink-0" />
40
49
  <div className="flex-1 min-w-0 flex flex-col">
41
- {/* Version pill */}
42
- <div className="h-6 w-20 bg-ods-bg rounded mb-3" />
43
- {/* Title 2 lines */}
44
- <div className="h-7 w-3/4 bg-ods-bg rounded mb-2" />
45
- <div className="h-7 w-1/2 bg-ods-bg rounded mb-3" />
46
- {/* Summary 2 lines */}
47
- <div className="h-3 w-full bg-ods-bg/60 rounded mb-1" />
48
- <div className="h-3 w-5/6 bg-ods-bg/60 rounded" />
50
+ {/* Version pill — mirrors `flex items-center gap-3 mb-3` in
51
+ the loaded card. The loaded `<span text-lg>` renders at
52
+ line-height 28 px (Tailwind text-lg = 18 px / 28 px LH);
53
+ placeholder uses `h-7` (28 px) to match exactly. */}
54
+ <div className="flex items-center gap-3 mb-3">
55
+ <div className="h-7 w-20 bg-ods-border rounded" />
56
+ </div>
57
+ {/* Title container — SAME min-h as the loaded card so the
58
+ card height contributed by this region matches exactly. */}
59
+ <div className="min-h-[60px] md:min-h-[72px] flex flex-col gap-1.5 justify-start mb-3">
60
+ <div className="h-[25px] md:h-[30px] w-3/4 bg-ods-border rounded" />
61
+ <div className="h-[25px] md:h-[30px] w-1/2 bg-ods-border rounded" />
62
+ </div>
63
+ {/* Summary container — SAME min-h as the loaded card. The
64
+ 3 placeholder lines mirror the rendered 3-line clamp;
65
+ `bg-ods-border/70` keeps summary placeholders slightly
66
+ dimmer than title placeholders (primary vs secondary
67
+ text hierarchy). */}
68
+ <div className="min-h-[68px] md:min-h-[78px] flex flex-col gap-2 justify-start">
69
+ <div className="h-3 w-full bg-ods-border/70 rounded" />
70
+ <div className="h-3 w-11/12 bg-ods-border/70 rounded" />
71
+ <div className="h-3 w-5/6 bg-ods-border/70 rounded" />
72
+ </div>
49
73
  </div>
50
74
  </div>
51
75
 
52
- {/* CHANGELOG strip placeholder — always rendered */}
76
+ {/* CHANGELOG strip placeholder — always rendered. Inner
77
+ placeholder `h-5` mirrors the loaded strip's `text-sm`
78
+ line-height (20 px) so total height is consistent with the
79
+ loaded `border-t pt-3 + text content` (~32-33 px). */}
53
80
  <div className="border-t border-ods-border pt-3">
54
- <div className="h-4 w-2/3 bg-ods-bg/60 rounded" />
81
+ <div className="h-5 w-2/3 bg-ods-border/70 rounded" />
55
82
  </div>
56
83
 
57
- {/* METADATA GRID — 4-cell placeholder */}
84
+ {/* METADATA GRID — 4-cell placeholder. The grid cells use
85
+ `bg-ods-card` containers and `bg-ods-bg` placeholders, which
86
+ DO contrast correctly because the cells are brighter than
87
+ the placeholders. Inner content heights mirror the loaded
88
+ cells (`text-h4` ≈ 28 px + `DM_Sans 14px leading-20`) so
89
+ total grid height matches the loaded ~86 px. */}
58
90
  <div className="grid grid-cols-1 md:grid-cols-4 border border-ods-border rounded-md overflow-hidden w-full">
59
91
  {[0, 1, 2].map((i) => (
60
92
  <div
@@ -62,8 +94,8 @@ export function ProductReleaseCardSkeleton({ className, size = 'default' }: Prod
62
94
  className="bg-ods-card p-4 flex flex-col gap-3 border-b md:border-b-0 md:border-r border-ods-border"
63
95
  >
64
96
  <div className="flex flex-col gap-2">
65
- <div className="h-6 w-24 bg-ods-bg rounded" />
66
- <div className="h-3 w-16 bg-ods-bg/60 rounded" />
97
+ <div className="h-7 w-24 bg-ods-bg rounded" />
98
+ <div className="h-4 w-16 bg-ods-bg/60 rounded" />
67
99
  </div>
68
100
  </div>
69
101
  ))}
@@ -71,8 +103,8 @@ export function ProductReleaseCardSkeleton({ className, size = 'default' }: Prod
71
103
  <div className="bg-ods-card p-4 flex items-center gap-3">
72
104
  <div className="h-10 w-10 rounded-full bg-ods-bg shrink-0" />
73
105
  <div className="flex flex-col gap-2 flex-1 min-w-0">
74
- <div className="h-4 w-3/4 bg-ods-bg rounded" />
75
- <div className="h-3 w-1/2 bg-ods-bg/60 rounded" />
106
+ <div className="h-5 w-3/4 bg-ods-bg rounded" />
107
+ <div className="h-4 w-1/2 bg-ods-bg/60 rounded" />
76
108
  </div>
77
109
  </div>
78
110
  </div>
@@ -119,44 +151,8 @@ export function ProductReleaseCardSkeleton({ className, size = 'default' }: Prod
119
151
  )
120
152
  }
121
153
 
122
- return (
123
- <div
124
- className={cn(
125
- 'bg-ods-card border border-ods-border rounded-[6px]',
126
- 'flex flex-col md:flex-row',
127
- 'items-start md:items-center',
128
- 'gap-3 md:gap-4',
129
- 'p-4',
130
- 'animate-pulse',
131
- className
132
- )}
133
- >
134
- {/* Left column - content */}
135
- <div className="flex-1 w-full md:w-auto min-w-0 flex flex-col justify-center gap-2">
136
- {/* Title skeleton - 2 lines height */}
137
- <div className="min-h-[48px] flex items-center">
138
- <div className="flex flex-col gap-1 w-full">
139
- <div className="h-[24px] w-3/4 bg-ods-border rounded" />
140
- <div className="h-[24px] w-1/2 bg-ods-border rounded" />
141
- </div>
142
- </div>
143
- {/* Description skeleton - 3 lines */}
144
- <div className="flex flex-col gap-1">
145
- <div className="h-[24px] w-full bg-ods-border rounded" />
146
- <div className="h-[24px] w-full bg-ods-border rounded" />
147
- <div className="h-[24px] w-2/3 bg-ods-border rounded" />
148
- </div>
149
- </div>
150
-
151
- {/* Right column - version + date */}
152
- <div className="flex items-center gap-2 w-full md:w-auto justify-start md:justify-end shrink-0">
153
- <div className="w-[200px] flex flex-col justify-center gap-2">
154
- <div className="h-[24px] w-20 bg-ods-border rounded" />
155
- <div className="h-[20px] w-32 bg-ods-border rounded" />
156
- </div>
157
- {/* Icon column */}
158
- <div className="h-6 w-6 bg-ods-border rounded shrink-0 mx-3" />
159
- </div>
160
- </div>
161
- )
154
+ // Unreachable — `size` is typed `'lg' | 'sm'` and both branches return
155
+ // above. Kept as a defensive throw so a future variant addition that
156
+ // forgets to return doesn't silently render `undefined`.
157
+ throw new Error(`ProductReleaseCardSkeleton: unsupported size '${size as string}'`)
162
158
  }