@flamingo-stack/openframe-frontend-core 0.0.240 → 0.0.242

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 (64) hide show
  1. package/dist/{chunk-TE6LSK2X.cjs → chunk-3RG2XZFE.cjs} +36 -27
  2. package/dist/chunk-3RG2XZFE.cjs.map +1 -0
  3. package/dist/{chunk-7BWJF43J.js → chunk-7ZHFCEJH.js} +2 -2
  4. package/dist/{chunk-5JTDFOYB.cjs → chunk-BKPS6M2F.cjs} +10 -10
  5. package/dist/{chunk-5JTDFOYB.cjs.map → chunk-BKPS6M2F.cjs.map} +1 -1
  6. package/dist/{chunk-NE2DYD6K.cjs → chunk-CZITXP77.cjs} +7 -3
  7. package/dist/chunk-CZITXP77.cjs.map +1 -0
  8. package/dist/{chunk-SI4LEFLR.cjs → chunk-DUZBTUXF.cjs} +25 -25
  9. package/dist/{chunk-SI4LEFLR.cjs.map → chunk-DUZBTUXF.cjs.map} +1 -1
  10. package/dist/{chunk-YH6KPNIH.js → chunk-FRRMXLU5.js} +6 -2
  11. package/dist/chunk-FRRMXLU5.js.map +1 -0
  12. package/dist/{chunk-XWUA273B.js → chunk-JJV3XGCY.js} +2 -2
  13. package/dist/{chunk-POZHDJSK.js → chunk-L6NR6POM.js} +14 -5
  14. package/dist/chunk-L6NR6POM.js.map +1 -0
  15. package/dist/{chunk-YY6S4T67.cjs → chunk-MP5HOP7N.cjs} +9 -9
  16. package/dist/{chunk-YY6S4T67.cjs.map → chunk-MP5HOP7N.cjs.map} +1 -1
  17. package/dist/{chunk-ZYPOIFBS.js → chunk-SEVJT2HP.js} +2 -2
  18. package/dist/{chunk-B3NI3KT4.js → chunk-V35PZSUA.js} +2 -2
  19. package/dist/{chunk-CDLMGHSB.cjs → chunk-VXP553WT.cjs} +7 -7
  20. package/dist/{chunk-CDLMGHSB.cjs.map → chunk-VXP553WT.cjs.map} +1 -1
  21. package/dist/components/chat/index.cjs +2 -2
  22. package/dist/components/chat/index.js +1 -1
  23. package/dist/components/contact/index.cjs +3 -3
  24. package/dist/components/contact/index.js +2 -2
  25. package/dist/components/features/index.cjs +2 -2
  26. package/dist/components/features/index.js +1 -1
  27. package/dist/components/index.cjs +75 -75
  28. package/dist/components/index.js +5 -5
  29. package/dist/components/navigation/index.cjs +2 -2
  30. package/dist/components/navigation/index.js +1 -1
  31. package/dist/components/onboarding-guides/index.cjs +23 -23
  32. package/dist/components/onboarding-guides/index.js +3 -3
  33. package/dist/components/related-content/index.cjs +3 -3
  34. package/dist/components/related-content/index.js +2 -2
  35. package/dist/components/related-content/related-content-section.d.ts +8 -2
  36. package/dist/components/related-content/related-content-section.d.ts.map +1 -1
  37. package/dist/components/tickets/index.cjs +64 -64
  38. package/dist/components/tickets/index.js +4 -4
  39. package/dist/components/ui/index.cjs +2 -2
  40. package/dist/components/ui/index.js +1 -1
  41. package/dist/index.cjs +4 -2
  42. package/dist/index.cjs.map +1 -1
  43. package/dist/index.js +3 -1
  44. package/dist/utils/index.cjs +5 -1
  45. package/dist/utils/index.cjs.map +1 -1
  46. package/dist/utils/index.d.ts +1 -1
  47. package/dist/utils/index.d.ts.map +1 -1
  48. package/dist/utils/index.js +5 -2
  49. package/dist/utils/index.js.map +1 -1
  50. package/dist/utils/list-url.d.ts +6 -0
  51. package/dist/utils/list-url.d.ts.map +1 -1
  52. package/package.json +1 -1
  53. package/src/components/related-content/related-content-section.tsx +28 -5
  54. package/src/utils/content-ref-groups.ts +1 -1
  55. package/src/utils/index.ts +1 -1
  56. package/src/utils/list-url.ts +9 -0
  57. package/dist/chunk-NE2DYD6K.cjs.map +0 -1
  58. package/dist/chunk-POZHDJSK.js.map +0 -1
  59. package/dist/chunk-TE6LSK2X.cjs.map +0 -1
  60. package/dist/chunk-YH6KPNIH.js.map +0 -1
  61. /package/dist/{chunk-7BWJF43J.js.map → chunk-7ZHFCEJH.js.map} +0 -0
  62. /package/dist/{chunk-XWUA273B.js.map → chunk-JJV3XGCY.js.map} +0 -0
  63. /package/dist/{chunk-ZYPOIFBS.js.map → chunk-SEVJT2HP.js.map} +0 -0
  64. /package/dist/{chunk-B3NI3KT4.js.map → chunk-V35PZSUA.js.map} +0 -0
@@ -27,6 +27,12 @@
27
27
  * Pure + server-safe (no React, no browser APIs) so the hub's server-side
28
28
  * mappers can import it from `@flamingo-stack/openframe-frontend-core/utils`.
29
29
  */
30
+ /** Resolve a ContentRef rail-vocab type to its canonical `documentType`
31
+ * (registry vocabulary) — `blog_post_existing` → `blog_post`, everything
32
+ * else passes through. Exported so consumers comparing rail group keys to
33
+ * registry entityTypes (e.g. the related-content rail's same-type-first
34
+ * ordering) share THIS alias map instead of re-declaring it. */
35
+ export declare function canonicalContentRefType(contentRefType: string): string;
30
36
  /**
31
37
  * Build a list-API URL that returns full rows for the given ids, or `null`
32
38
  * when the type has no list endpoint (the caller skips fetching rather than
@@ -1 +1 @@
1
- {"version":3,"file":"list-url.d.ts","sourceRoot":"","sources":["../../src/utils/list-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAqCH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,IAAI,SAAK,GAAG,MAAM,GAAG,IAAI,CAa5F"}
1
+ {"version":3,"file":"list-url.d.ts","sourceRoot":"","sources":["../../src/utils/list-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAWH;;;;iEAIiE;AACjE,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAEtE;AA4BD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,IAAI,SAAK,GAAG,MAAM,GAAG,IAAI,CAa5F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flamingo-stack/openframe-frontend-core",
3
- "version": "0.0.240",
3
+ "version": "0.0.242",
4
4
  "description": "Shared design system and components for all Flamingo platforms",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -52,7 +52,7 @@ import {
52
52
  import type { ContentRef, ContentRefWithReason } from '../../types/content-ref';
53
53
  import { useSelfFetch } from '../../hooks/use-self-fetch';
54
54
  import { extractItems } from '../../utils/extract-items';
55
- import { buildListUrl as libBuildListUrl } from '../../utils/list-url';
55
+ import { buildListUrl as libBuildListUrl, canonicalContentRefType } from '../../utils/list-url';
56
56
  import { buildSuggestionUrl } from '../../utils/suggestion-url';
57
57
  import { decideNewTab } from '../chat/utils/decide-new-tab';
58
58
  // DEEP card imports — NOT the `../chat` barrel (the barrel statically reaches
@@ -158,7 +158,10 @@ function renderSkeletonForType(
158
158
  case 'investor_update':
159
159
  return <InvestorUpdateCardSkeleton size={legacySize} />;
160
160
  case 'onboarding_guide':
161
- return <OnboardingGuideCardSkeleton size={legacySize} />;
161
+ // The rich catalog variant (hero + author grid, clamped description) —
162
+ // the step-numbered 'default' variant is for the guide detail page's
163
+ // "More in section" rail, not this full-width row.
164
+ return <OnboardingGuideCardSkeleton size={size === 'sm' ? 'sm' : 'catalog'} />;
162
165
  case 'marketing_campaign':
163
166
  return adminCampaignCard ? <adminCampaignCard.Skeleton size={legacySize} /> : null;
164
167
  case 'roadmap_item':
@@ -267,7 +270,9 @@ function CardForType({
267
270
  case 'investor_update':
268
271
  return <InvestorUpdateCard update={item} size={legacySize} href={href} targetPlatform={targetPlatform} placeholderUrl={placeholderUrl} {...anchorAttrs} />;
269
272
  case 'onboarding_guide':
270
- return <OnboardingGuideCard guide={item} size={legacySize} href={href} targetPlatform={targetPlatform} placeholderUrl={placeholderUrl} {...anchorAttrs} />;
273
+ // Catalog variant (see skeleton note) full-width rich card with a
274
+ // line-clamped description instead of the step-numbered rail card.
275
+ return <OnboardingGuideCard guide={item} size={size === 'sm' ? 'sm' : 'catalog'} href={href} targetPlatform={targetPlatform} placeholderUrl={placeholderUrl} {...anchorAttrs} />;
271
276
  case 'marketing_campaign':
272
277
  return adminCampaignCard ? <adminCampaignCard.Card campaign={item} /> : null;
273
278
  case 'roadmap_item':
@@ -441,9 +446,15 @@ export interface RelatedContentSectionProps {
441
446
  * `contentRefs` is provided. */
442
447
  entityType?: string;
443
448
  entityId?: number | string;
444
- /** Maps to the suggestion API's `count` param. Absent param not sent
449
+ /** Maps to the suggestion API's `count` param the PER-TYPE fill target
450
+ * for every candidate type EXCEPT the host's own. Absent → param not sent
445
451
  * (server default applies). */
446
452
  minResults?: number;
453
+ /** Maps to the suggestion API's `sameTypeCount` param — the budget for the
454
+ * candidate type MATCHING the host's own `entityType` (same-type boost:
455
+ * a blog post's rail leads with more blog posts). Absent → param not
456
+ * sent (host's type uses the server's `count`). */
457
+ sameTypeMinResults?: number;
447
458
  /** SSR hydrate for suggestion mode — the server page ran the engine and
448
459
  * drills the refs here; the first client fetch is skipped (useSelfFetch
449
460
  * initialData contract). */
@@ -493,6 +504,7 @@ export function RelatedContentSection({
493
504
  entityType,
494
505
  entityId,
495
506
  minResults,
507
+ sameTypeMinResults,
496
508
  initialItems,
497
509
  title = 'Related Content',
498
510
  columns = 2,
@@ -517,6 +529,7 @@ export function RelatedContentSection({
517
529
  entityId,
518
530
  count: minResults,
519
531
  extraParams: {
532
+ sameTypeCount: sameTypeMinResults !== undefined ? String(sameTypeMinResults) : undefined,
520
533
  excludeTypes: excludeTypes && excludeTypes.length > 0 ? excludeTypes.join(',') : undefined,
521
534
  },
522
535
  })
@@ -558,7 +571,17 @@ export function RelatedContentSection({
558
571
  // Registered types in CONTENT_REF_GROUPS order, then any unregistered
559
572
  // types appended (same shape the investor-email builder uses — both
560
573
  // consume `orderContentRefTypes` so cross-surface ordering matches).
561
- const orderedTypes = orderContentRefTypes(Object.keys(grouped));
574
+ // SAME-TYPE FIRST: when a host entityType is known (suggestion / SSR
575
+ // modes), its own content-type group is hoisted to the top — a blog
576
+ // post's rail leads with blog posts. Rail group keys are compared via
577
+ // the shared alias canonicalizer (blog_post_existing ↔ blog_post).
578
+ let orderedTypes = orderContentRefTypes(Object.keys(grouped));
579
+ if (entityType) {
580
+ const sameType = orderedTypes.filter((t) => canonicalContentRefType(t) === entityType);
581
+ if (sameType.length > 0) {
582
+ orderedTypes = [...sameType, ...orderedTypes.filter((t) => canonicalContentRefType(t) !== entityType)];
583
+ }
584
+ }
562
585
 
563
586
  return (
564
587
  <div className="space-y-8">
@@ -52,7 +52,7 @@ export const CONTENT_REF_GROUPS: Record<string, ContentRefGroupConfig> = {
52
52
  event: { label: 'Events', order: 6, layout: 'list', gridSize: 'default' },
53
53
  blog_post_existing: { label: 'Blog Posts', order: 7, layout: 'grid', gridSize: 'default' },
54
54
  customer_interview: { label: 'Customer Interviews', order: 8, layout: 'grid', gridSize: 'default' },
55
- onboarding_guide: { label: 'Onboarding Guides', order: 9, layout: 'grid', gridSize: 'default' },
55
+ onboarding_guide: { label: 'Onboarding Guides', order: 9, layout: 'list', gridSize: 'default' },
56
56
  }
57
57
 
58
58
  /** Human-readable label for a content_ref `type`. Returns null when the type
@@ -235,7 +235,7 @@ export {
235
235
  // delegate here (byte-parity test guards the migration); embedders wire
236
236
  // `endpoints.buildListUrl = (t, ids) => buildListUrl(t, ids, '/content')`.
237
237
  // Pure + server-safe (the hub imports it server-side from this barrel).
238
- export { buildListUrl } from './list-url'
238
+ export { buildListUrl, canonicalContentRefType } from './list-url'
239
239
 
240
240
  // Content-ref group registry (labels/order/layout per rail type) + list-API
241
241
  // response normalizers + the shared suggestion-fetch URL composer — all
@@ -37,6 +37,15 @@
37
37
  */
38
38
  const ALIASES: Record<string, string> = { blog_post_existing: 'blog_post' }
39
39
 
40
+ /** Resolve a ContentRef rail-vocab type to its canonical `documentType`
41
+ * (registry vocabulary) — `blog_post_existing` → `blog_post`, everything
42
+ * else passes through. Exported so consumers comparing rail group keys to
43
+ * registry entityTypes (e.g. the related-content rail's same-type-first
44
+ * ordering) share THIS alias map instead of re-declaring it. */
45
+ export function canonicalContentRefType(contentRefType: string): string {
46
+ return ALIASES[contentRefType] ?? contentRefType
47
+ }
48
+
40
49
  /**
41
50
  * One builder per fetch-mode `contentRefType`, keyed by the canonical
42
51
  * `documentType`. Each body is copied VERBATIM from the matching hub