@flamingo-stack/openframe-frontend-core 0.0.207 → 0.0.208

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 (132) hide show
  1. package/dist/{chunk-Z3GQGR5E.js → chunk-2HMZSCJY.js} +3158 -2074
  2. package/dist/chunk-2HMZSCJY.js.map +1 -0
  3. package/dist/chunk-4XLJWX2N.js +12 -0
  4. package/dist/chunk-4XLJWX2N.js.map +1 -0
  5. package/dist/{chunk-APM6KBPU.cjs → chunk-C5EC5AZM.cjs} +1644 -560
  6. package/dist/chunk-C5EC5AZM.cjs.map +1 -0
  7. package/dist/chunk-VFKQMAUF.cjs +12 -0
  8. package/dist/chunk-VFKQMAUF.cjs.map +1 -0
  9. package/dist/components/chat/embeddable-chat.d.ts +35 -2
  10. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  11. package/dist/components/chat/hooks/index.d.ts +3 -0
  12. package/dist/components/chat/hooks/index.d.ts.map +1 -1
  13. package/dist/components/chat/hooks/use-embedded-chat.d.ts +10 -169
  14. package/dist/components/chat/hooks/use-embedded-chat.d.ts.map +1 -1
  15. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +85 -0
  16. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -0
  17. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +124 -0
  18. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -0
  19. package/dist/components/chat/hooks/use-unified-chat.d.ts +33 -0
  20. package/dist/components/chat/hooks/use-unified-chat.d.ts.map +1 -0
  21. package/dist/components/chat/index.cjs +8 -2
  22. package/dist/components/chat/index.cjs.map +1 -1
  23. package/dist/components/chat/index.js +11 -5
  24. package/dist/components/chat/types/index.d.ts +1 -0
  25. package/dist/components/chat/types/index.d.ts.map +1 -1
  26. package/dist/components/chat/types/unified-chat-state.types.d.ts +185 -0
  27. package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -0
  28. package/dist/components/features/index.cjs +3 -2
  29. package/dist/components/features/index.cjs.map +1 -1
  30. package/dist/components/features/index.js +2 -1
  31. package/dist/components/index.cjs +26 -2
  32. package/dist/components/index.cjs.map +1 -1
  33. package/dist/components/index.d.ts +4 -0
  34. package/dist/components/index.d.ts.map +1 -1
  35. package/dist/components/index.js +27 -3
  36. package/dist/components/navigation/index.cjs +3 -2
  37. package/dist/components/navigation/index.cjs.map +1 -1
  38. package/dist/components/navigation/index.js +2 -1
  39. package/dist/components/shared/delivery/delivery-lists.d.ts +16 -0
  40. package/dist/components/shared/delivery/delivery-lists.d.ts.map +1 -0
  41. package/dist/components/shared/delivery/delivery-table.d.ts +12 -0
  42. package/dist/components/shared/delivery/delivery-table.d.ts.map +1 -0
  43. package/dist/components/shared/delivery/index.d.ts +3 -0
  44. package/dist/components/shared/delivery/index.d.ts.map +1 -0
  45. package/dist/components/shared/dev-section/dev-section-page.d.ts +31 -0
  46. package/dist/components/shared/dev-section/dev-section-page.d.ts.map +1 -0
  47. package/dist/components/shared/dev-section/dev-section-view.d.ts +34 -0
  48. package/dist/components/shared/dev-section/dev-section-view.d.ts.map +1 -0
  49. package/dist/components/shared/dev-section/index.d.ts +3 -0
  50. package/dist/components/shared/dev-section/index.d.ts.map +1 -0
  51. package/dist/components/shared/legal-document/index.d.ts +10 -0
  52. package/dist/components/shared/legal-document/index.d.ts.map +1 -0
  53. package/dist/components/shared/legal-document/legal-document-page.d.ts +66 -0
  54. package/dist/components/shared/legal-document/legal-document-page.d.ts.map +1 -0
  55. package/dist/components/shared/legal-document/use-legal-docs.d.ts +40 -0
  56. package/dist/components/shared/legal-document/use-legal-docs.d.ts.map +1 -0
  57. package/dist/components/shared/product-release/index.d.ts +2 -1
  58. package/dist/components/shared/product-release/index.d.ts.map +1 -1
  59. package/dist/components/shared/product-release/release-detail-page.d.ts +11 -7
  60. package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
  61. package/dist/components/shared/roadmap/index.d.ts +18 -0
  62. package/dist/components/shared/roadmap/index.d.ts.map +1 -0
  63. package/dist/components/shared/roadmap/roadmap-grid-skeleton.d.ts +24 -0
  64. package/dist/components/shared/roadmap/roadmap-grid-skeleton.d.ts.map +1 -0
  65. package/dist/components/shared/roadmap/roadmap-grid.d.ts +18 -0
  66. package/dist/components/shared/roadmap/roadmap-grid.d.ts.map +1 -0
  67. package/dist/components/shared/roadmap/use-roadmap-voting.d.ts +25 -0
  68. package/dist/components/shared/roadmap/use-roadmap-voting.d.ts.map +1 -0
  69. package/dist/components/ui/index.cjs +8 -2
  70. package/dist/components/ui/index.cjs.map +1 -1
  71. package/dist/components/ui/index.js +11 -5
  72. package/dist/components/ui/release-changelog-section.d.ts +13 -2
  73. package/dist/components/ui/release-changelog-section.d.ts.map +1 -1
  74. package/dist/embed-shims/index.cjs +1 -6
  75. package/dist/embed-shims/index.cjs.map +1 -1
  76. package/dist/embed-shims/index.js +1 -6
  77. package/dist/embed-shims/index.js.map +1 -1
  78. package/dist/index.cjs +18 -2
  79. package/dist/index.cjs.map +1 -1
  80. package/dist/index.js +19 -3
  81. package/dist/types/delivery.d.ts +49 -0
  82. package/dist/types/delivery.d.ts.map +1 -0
  83. package/dist/types/index.cjs +13 -0
  84. package/dist/types/index.cjs.map +1 -1
  85. package/dist/types/index.d.ts +1 -0
  86. package/dist/types/index.d.ts.map +1 -1
  87. package/dist/types/index.js +12 -1
  88. package/dist/types/index.js.map +1 -1
  89. package/dist/utils/dev-sections/index.d.ts +11 -0
  90. package/dist/utils/dev-sections/index.d.ts.map +1 -0
  91. package/dist/utils/dev-sections/openframe-dev-sections.d.ts +209 -0
  92. package/dist/utils/dev-sections/openframe-dev-sections.d.ts.map +1 -0
  93. package/dist/utils/index.cjs +82 -0
  94. package/dist/utils/index.cjs.map +1 -1
  95. package/dist/utils/index.d.ts +1 -0
  96. package/dist/utils/index.d.ts.map +1 -1
  97. package/dist/utils/index.js +81 -2
  98. package/dist/utils/index.js.map +1 -1
  99. package/package.json +1 -1
  100. package/src/components/chat/embeddable-chat.tsx +123 -8
  101. package/src/components/chat/hooks/index.ts +9 -2
  102. package/src/components/chat/hooks/use-embedded-chat.ts +18 -1016
  103. package/src/components/chat/hooks/use-nats-chat-adapter.ts +372 -0
  104. package/src/components/chat/hooks/use-sse-chat-adapter.ts +1058 -0
  105. package/src/components/chat/hooks/use-unified-chat.ts +171 -0
  106. package/src/components/chat/types/index.ts +1 -0
  107. package/src/components/chat/types/unified-chat-state.types.ts +215 -0
  108. package/src/components/index.ts +8 -0
  109. package/src/components/shared/delivery/delivery-lists.tsx +199 -0
  110. package/src/components/shared/delivery/delivery-table.tsx +174 -0
  111. package/src/components/shared/delivery/index.ts +9 -0
  112. package/src/components/shared/dev-section/dev-section-page.tsx +72 -0
  113. package/src/components/shared/dev-section/dev-section-view.tsx +129 -0
  114. package/src/components/shared/dev-section/index.ts +2 -0
  115. package/src/components/shared/legal-document/index.ts +19 -0
  116. package/src/components/shared/legal-document/legal-document-page.tsx +178 -0
  117. package/src/components/shared/legal-document/use-legal-docs.ts +123 -0
  118. package/src/components/shared/product-release/index.ts +14 -3
  119. package/src/components/shared/product-release/release-detail-page.tsx +45 -7
  120. package/src/components/shared/roadmap/index.ts +23 -0
  121. package/src/components/shared/roadmap/roadmap-grid-skeleton.tsx +74 -0
  122. package/src/components/shared/roadmap/roadmap-grid.tsx +106 -0
  123. package/src/components/shared/roadmap/use-roadmap-voting.ts +163 -0
  124. package/src/components/ui/release-changelog-section.tsx +113 -32
  125. package/src/stories/EmbeddableChat.stories.tsx +186 -0
  126. package/src/types/delivery.ts +54 -0
  127. package/src/types/index.ts +1 -0
  128. package/src/utils/dev-sections/index.ts +17 -0
  129. package/src/utils/dev-sections/openframe-dev-sections.ts +148 -0
  130. package/src/utils/index.ts +6 -1
  131. package/dist/chunk-APM6KBPU.cjs.map +0 -1
  132. package/dist/chunk-Z3GQGR5E.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  "use client"
2
2
 
3
- import React, { useState } from 'react';
3
+ import React, { useState, useRef, useEffect } from 'react';
4
4
  import { Badge } from './badge';
5
5
  import { ChevronDown } from 'lucide-react';
6
6
  import type { ChangelogEntry } from '../../types/product-release';
@@ -10,10 +10,21 @@ interface ReleaseChangelogSectionProps {
10
10
  entries: ChangelogEntry[];
11
11
  isBreaking?: boolean;
12
12
  hideTitle?: boolean;
13
- /** When true, section starts collapsed and can be toggled open/closed */
13
+ /** When true, section starts collapsed and can be toggled open/closed
14
+ * via a button on the title row. Mutually exclusive with `previewFirst`. */
14
15
  collapsible?: boolean;
15
16
  /** Initial collapsed state (only used when collapsible=true). Defaults to true (collapsed). */
16
17
  defaultCollapsed?: boolean;
18
+ /** When true, render the first entry in full and fade-mask the rest
19
+ * with a "Show N more / Show less" toggle below the list. Hides the
20
+ * fade + toggle when there's only one entry. Mutually exclusive with
21
+ * `collapsible` — when both are passed, `collapsible` wins.
22
+ *
23
+ * This is the same progressive-disclosure pattern used on the
24
+ * investor-update detail page's Key Highlights / Financial Notes
25
+ * sections (formerly a duplicated `FadedHighlightSection` component
26
+ * in the hub — unified here). */
27
+ previewFirst?: boolean;
17
28
  /** Optional lucide icon rendered inline before the title text. Matches the
18
29
  * catalog card's changelog-strip icons (Sparkles for Features, Wrench for
19
30
  * Fixes, TrendingUp for Improvements, AlertTriangle for Breaking) — same
@@ -23,6 +34,10 @@ interface ReleaseChangelogSectionProps {
23
34
  SimpleMarkdownRenderer: React.ComponentType<{ content: string }>;
24
35
  }
25
36
 
37
+ // Collapsed height for the preview-first mode. ~120px shows the first
38
+ // entry's title + the start of its description before the mask kicks in.
39
+ const PREVIEW_COLLAPSED_HEIGHT = 120;
40
+
26
41
  export function ReleaseChangelogSection({
27
42
  title,
28
43
  entries,
@@ -30,13 +45,29 @@ export function ReleaseChangelogSection({
30
45
  hideTitle = false,
31
46
  collapsible = false,
32
47
  defaultCollapsed = true,
48
+ previewFirst = false,
33
49
  icon,
34
50
  SimpleMarkdownRenderer
35
51
  }: ReleaseChangelogSectionProps) {
36
52
  const [collapsed, setCollapsed] = useState(collapsible ? defaultCollapsed : false);
53
+ const [previewExpanded, setPreviewExpanded] = useState(false);
54
+ const previewContentRef = useRef<HTMLDivElement>(null);
55
+
56
+ // Reset preview-expanded state when the entries set changes — otherwise
57
+ // a parent that refetches and shrinks entries from N → 1 would leave
58
+ // the user with a stale "expanded" state and a momentarily-wrong
59
+ // "Show 0 more" button before the `previewNeedsFade` gate hides it.
60
+ // Keyed on `entries.length` (not identity) so re-renders with the same
61
+ // length don't churn state unnecessarily.
62
+ useEffect(() => {
63
+ setPreviewExpanded(false);
64
+ }, [entries.length]);
37
65
 
38
66
  if (!entries || entries.length === 0) return null;
39
67
 
68
+ // collapsible wins when both flags are passed (documented in JSDoc).
69
+ const inPreviewMode = previewFirst && !collapsible;
70
+ const previewNeedsFade = inPreviewMode && entries.length > 1;
40
71
  const showEntries = !collapsible || !collapsed;
41
72
 
42
73
  return (
@@ -68,37 +99,87 @@ export function ReleaseChangelogSection({
68
99
  )
69
100
  )}
70
101
  {showEntries && (
71
- <ul className="space-y-6">
72
- {entries.map((entry, index) => (
73
- <li key={index} className="border-l-2 border-ods-border pl-4 ml-0">
74
- {/* Entry title `text-h3` is body family + BOLD weight (per
75
- ODS tokens: `--font-h3-weight: var(--font-weight-bold)`)
76
- at 14/18px responsive. Same body size as the description
77
- below, distinguished by weight clean visual hierarchy
78
- without inflating the body scale. */}
79
- <p className="text-h3 text-ods-text-primary mb-2">{entry.title}</p>
80
- {entry.description && (
81
- /* Entry description — body text matches the main release
82
- summary (release-detail-page.tsx:321) at the SAME 14/18px
83
- responsive `text-h4` scale. The `SimpleMarkdownRenderer`
84
- forces its own `<p>` typography
85
- (`text-[16px] md:text-[18px] lg:text-[20px]`) which
86
- overrides the wrapper's `text-h4` on `lg+` viewports and
87
- inflates the changelog body to 20px — larger than the
88
- main summary AND larger than the entry title.
89
- The `[&_p]:!` overrides pin every descendant `<p>` back
90
- to the h4 responsive tokens (`var(--font-size-h4-body)`
91
- + `var(--font-line-space-h4-body)`) — same variables
92
- `text-h4` itself uses, so the responsive breakpoints
93
- stay aligned with the rest of the page. */
94
- <div className="text-h4 text-ods-text-primary [&_p]:!text-[length:var(--font-size-h4-body)] [&_p]:!leading-[var(--font-line-space-h4-body)] [&_p]:!font-medium">
95
- <SimpleMarkdownRenderer content={entry.description} />
96
- </div>
97
- )}
98
- </li>
99
- ))}
100
- </ul>
102
+ inPreviewMode ? (
103
+ /* Preview-first mode: render the list in a height-clamped +
104
+ mask-faded wrapper. The CSS mask creates the soft fade-out
105
+ at the bottom of the collapsed region; the inline maxHeight
106
+ + transition animate the open/close. When `previewExpanded`
107
+ flips, the wrapper falls back to its natural scrollHeight
108
+ (or 2000px on first render before the ref measures). */
109
+ <div className="relative">
110
+ <div
111
+ ref={previewContentRef}
112
+ className="overflow-hidden transition-[max-height] duration-500"
113
+ style={{
114
+ transitionTimingFunction: 'cubic-bezier(0.33, 1, 0.68, 1)',
115
+ maxHeight: previewExpanded || !previewNeedsFade
116
+ ? previewContentRef.current?.scrollHeight ?? 2000
117
+ : PREVIEW_COLLAPSED_HEIGHT,
118
+ ...(previewNeedsFade && !previewExpanded ? {
119
+ maskImage: 'linear-gradient(to bottom, black 30%, transparent 100%)',
120
+ WebkitMaskImage: 'linear-gradient(to bottom, black 30%, transparent 100%)',
121
+ } : {}),
122
+ }}
123
+ >
124
+ <ChangelogEntryList entries={entries} SimpleMarkdownRenderer={SimpleMarkdownRenderer} />
125
+ </div>
126
+ {previewNeedsFade && (
127
+ <button
128
+ type="button"
129
+ onClick={() => setPreviewExpanded(!previewExpanded)}
130
+ className="mt-4 flex items-center gap-1.5 text-sm text-ods-text-secondary hover:text-ods-accent transition-colors duration-200"
131
+ >
132
+ <span>{previewExpanded ? 'Show less' : `Show ${entries.length - 1} more`}</span>
133
+ <ChevronDown
134
+ className={`w-3.5 h-3.5 transition-transform duration-300 ${previewExpanded ? 'rotate-180' : ''}`}
135
+ />
136
+ </button>
137
+ )}
138
+ </div>
139
+ ) : (
140
+ <ChangelogEntryList entries={entries} SimpleMarkdownRenderer={SimpleMarkdownRenderer} />
141
+ )
101
142
  )}
102
143
  </div>
103
144
  );
104
145
  }
146
+
147
+ /**
148
+ * Internal list renderer — shared by the default and preview-first
149
+ * branches. Each entry is a bordered-left list item with bold title
150
+ * + markdown-rendered description body.
151
+ */
152
+ function ChangelogEntryList({
153
+ entries,
154
+ SimpleMarkdownRenderer,
155
+ }: {
156
+ entries: ChangelogEntry[];
157
+ SimpleMarkdownRenderer: React.ComponentType<{ content: string }>;
158
+ }) {
159
+ return (
160
+ <ul className="space-y-6">
161
+ {entries.map((entry, index) => (
162
+ <li key={index} className="border-l-2 border-ods-border pl-4 ml-0">
163
+ {/* Entry title — `text-h3` is body family + BOLD weight (per
164
+ ODS tokens: `--font-h3-weight: var(--font-weight-bold)`)
165
+ at 14/18px responsive. Same body size as the description
166
+ below, distinguished by weight — clean visual hierarchy
167
+ without inflating the body scale. */}
168
+ <p className="text-h3 text-ods-text-primary mb-2">{entry.title}</p>
169
+ {entry.description && (
170
+ /* Entry description — body text matches the main release
171
+ summary at the SAME 14/18px responsive `text-h4` scale.
172
+ The `SimpleMarkdownRenderer` forces its own `<p>` typography
173
+ which would override `text-h4` on `lg+` viewports and
174
+ inflate the changelog body to 20px. The `[&_p]:!` overrides
175
+ pin every descendant `<p>` back to the h4 responsive tokens
176
+ so the breakpoints stay aligned with the rest of the page. */
177
+ <div className="text-h4 text-ods-text-primary [&_p]:!text-[length:var(--font-size-h4-body)] [&_p]:!leading-[var(--font-line-space-h4-body)] [&_p]:!font-medium">
178
+ <SimpleMarkdownRenderer content={entry.description} />
179
+ </div>
180
+ )}
181
+ </li>
182
+ ))}
183
+ </ul>
184
+ );
185
+ }
@@ -0,0 +1,186 @@
1
+ import type { Meta, StoryObj } from '@storybook/nextjs-vite'
2
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
3
+ import React, { useMemo } from 'react'
4
+ import {
5
+ ChatRuntimeContext,
6
+ type ChatRuntime,
7
+ } from '../contexts/chat-runtime-context'
8
+ import { EmbeddableChat } from '../components/chat/embeddable-chat'
9
+ import type { UseNatsChatAdapterConfig } from '../components/chat/hooks/use-nats-chat-adapter'
10
+
11
+ // =============================================================================
12
+ // Shared mocks
13
+ // =============================================================================
14
+
15
+ /**
16
+ * Minimal mock runtime — endpoints point at non-existent paths so that
17
+ * an accidental fetch in the active mode resolves to a 404 rather than
18
+ * hitting a real server. Inactive-mode hooks never call out. Memoised
19
+ * with `useMemo` per the runtime's embedder contract.
20
+ */
21
+ function createMockRuntime(): ChatRuntime {
22
+ return {
23
+ endpoints: {
24
+ chatStreamUrl: '/__story__/chat',
25
+ approvalToolUrl: '/__story__/confirm-tool',
26
+ commandsUrl: '/__story__/commands',
27
+ buildListUrl: () => null,
28
+ attachmentUploadUrl: '/__story__/upload',
29
+ attachmentViewUrlPrefix: '/__story__/view/',
30
+ chatIdentityUrl: '/__story__/identity',
31
+ },
32
+ navigation: {
33
+ mode: 'embed',
34
+ defaultContentOrigin: 'https://example.com',
35
+ },
36
+ source: 'storybook',
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Stub Mingo config — `getNatsWsUrl` returns null so the NATS dialog
42
+ * subscription stays idle (no WS handshake attempted). `publishUserMessage`
43
+ * just logs; the story exists to demonstrate UI layout, not transport
44
+ * behaviour.
45
+ */
46
+ function createMockMingoConfig(): UseNatsChatAdapterConfig {
47
+ return {
48
+ dialogId: 'story-dialog-id',
49
+ getNatsWsUrl: () => null,
50
+ publishUserMessage: (text, options) => {
51
+ // eslint-disable-next-line no-console
52
+ console.log('[story] mingo publish', { text, options })
53
+ },
54
+ }
55
+ }
56
+
57
+ // =============================================================================
58
+ // Decorator — runtime + react-query + isolated drawer slot
59
+ // =============================================================================
60
+
61
+ function StoryShell({ children }: { children: React.ReactNode }) {
62
+ const runtime = useMemo(() => createMockRuntime(), [])
63
+ const queryClient = useMemo(() => new QueryClient(), [])
64
+ return (
65
+ <QueryClientProvider client={queryClient}>
66
+ <ChatRuntimeContext.Provider value={runtime}>
67
+ {/* Filler content under the drawer so the floating overlay
68
+ has visual context to slide over. */}
69
+ <div className="min-h-[100dvh] bg-ods-bg p-8">
70
+ <div className="text-ods-text-secondary text-sm">
71
+ Storybook canvas — drawer opens on the right →
72
+ </div>
73
+ </div>
74
+ {children}
75
+ </ChatRuntimeContext.Provider>
76
+ </QueryClientProvider>
77
+ )
78
+ }
79
+
80
+ // =============================================================================
81
+ // Meta
82
+ // =============================================================================
83
+
84
+ const meta = {
85
+ title: 'Chat/EmbeddableChat',
86
+ component: EmbeddableChat,
87
+ parameters: {
88
+ layout: 'fullscreen',
89
+ },
90
+ decorators: [
91
+ (Story) => (
92
+ <StoryShell>
93
+ <Story />
94
+ </StoryShell>
95
+ ),
96
+ ],
97
+ } satisfies Meta<typeof EmbeddableChat>
98
+
99
+ export default meta
100
+ type Story = StoryObj<typeof meta>
101
+
102
+ // =============================================================================
103
+ // 1. Guide-only (legacy / multi-platform-hub)
104
+ // =============================================================================
105
+
106
+ /**
107
+ * Legacy / single-mode Guide consumer. No `modes` prop — the lib
108
+ * synthesises `modes.guide` from the top-level legacy props. The
109
+ * header has NO mode toggle since only one transport is configured.
110
+ *
111
+ * This is exactly how `multi-platform-hub` mounts the chat today.
112
+ */
113
+ export const GuideOnly: Story = {
114
+ args: {
115
+ defaultOpen: true,
116
+ showInternalTrigger: false,
117
+ emptyStateGreeting: 'Ask me anything about the OpenFrame docs.',
118
+ suggestedQueries: [
119
+ 'How do I install the agent?',
120
+ 'What integrations are supported?',
121
+ 'Where do I configure SSO?',
122
+ ],
123
+ },
124
+ }
125
+
126
+ // =============================================================================
127
+ // 2. Mingo-only (NATS agent surface)
128
+ // =============================================================================
129
+
130
+ /**
131
+ * Mingo-only consumer — the unified chat surface running just the NATS
132
+ * agent transport. Header has NO toggle (only one mode configured).
133
+ * No floating "Ask AI" trigger; the drawer opens uncontrolled.
134
+ *
135
+ * `getNatsWsUrl` returns null in the mock config so no real NATS
136
+ * handshake is attempted — the story renders the empty Mingo panel.
137
+ */
138
+ export const MingoOnly: Story = {
139
+ render: (args) => (
140
+ <EmbeddableChat
141
+ {...args}
142
+ modes={{
143
+ mingo: createMockMingoConfig(),
144
+ }}
145
+ defaultActiveMode="mingo"
146
+ defaultOpen
147
+ showInternalTrigger={false}
148
+ emptyStateGreeting="Hi — I'm the Mingo agent. What would you like me to help with?"
149
+ />
150
+ ),
151
+ args: {},
152
+ }
153
+
154
+ // =============================================================================
155
+ // 3. Both modes (toggle visible — openframe-frontend target)
156
+ // =============================================================================
157
+
158
+ /**
159
+ * Dual-mode consumer — Guide + Mingo configured side-by-side. The header
160
+ * shows the segmented toggle `Mingo | Guide`. Clicking the toggle flips
161
+ * the active adapter; each side keeps its own history so the user picks
162
+ * up where they left off.
163
+ *
164
+ * This is the target shape for `openframe-frontend`. Both mocks are
165
+ * idle (no real backend) — toggle and layout are the visible deliverable.
166
+ */
167
+ export const BothModes: Story = {
168
+ render: (args) => (
169
+ <EmbeddableChat
170
+ {...args}
171
+ modes={{
172
+ guide: {},
173
+ mingo: createMockMingoConfig(),
174
+ }}
175
+ defaultActiveMode="mingo"
176
+ defaultOpen
177
+ showInternalTrigger={false}
178
+ emptyStateGreeting="Switch between Mingo (agent) and Guide (docs) via the header toggle."
179
+ suggestedQueries={[
180
+ 'How do I onboard a new device?',
181
+ 'Show me the audit log',
182
+ ]}
183
+ />
184
+ ),
185
+ args: {},
186
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Delivery (ClickUp Bug-fixes & Enhancements) wire types — shared between the
3
+ * hub's `/api/delivery*` route shapes and the lib's `DeliveryTable`/`DeliveryLists`/
4
+ * `DeliverySection` components.
5
+ *
6
+ * Lifted from hub `types/delivery.ts` so embedders consuming the lib's
7
+ * delivery surfaces and the lib's own `ReleaseDetailPage` (which renders
8
+ * related delivery items) share one canonical shape.
9
+ */
10
+
11
+ export interface DeliveryItem {
12
+ id: string;
13
+ title: string;
14
+ description: string;
15
+ status: string;
16
+ statusColor: string; // ClickUp status color
17
+ taskType: 'Request' | 'Bug' | string; // ClickUp task type
18
+ /**
19
+ * Canonical ClickUp custom_item_id (1008 = Bug, 1009 = Request, …).
20
+ * Surfaced here so the chat's compact card can render a type-specific
21
+ * lucide icon via `TaskTypeIcon` instead of the two-letter initials
22
+ * fallback. Single source of truth lives in
23
+ * `lib/utils/clickup-task-type-utils.ts` (hub-side).
24
+ */
25
+ customItemId: number | null;
26
+ /**
27
+ * Every ClickUp list the task is associated with (home list + ClickUp's
28
+ * "Tasks in Multiple Lists" locations). UI joins these for display.
29
+ * Falls back to a single-element array containing the home list when
30
+ * there are no additional locations.
31
+ */
32
+ listNames: string[];
33
+ dateOpened: number; // Unix timestamp
34
+ dateUpdated: number; // Unix timestamp
35
+ dateClosed: number | null; // Unix timestamp or null if not closed
36
+ clickupUrl: string;
37
+ }
38
+
39
+ export interface DeliveryResponse {
40
+ completed: DeliveryItem[];
41
+ inProgress: DeliveryItem[];
42
+ }
43
+
44
+ // Task type to badge label mapping
45
+ export const TASK_TYPE_LABELS = {
46
+ Request: 'ENHANCEMENT',
47
+ Bug: 'BUG-FIX',
48
+ } as const;
49
+
50
+ // Task type to badge text-color mapping (ODS attention-red for Bug; default for others)
51
+ export const TASK_TYPE_TEXT_COLORS = {
52
+ Request: '', // Default white/grey
53
+ Bug: 'text-[var(--ods-attention-red-error)]',
54
+ } as const;
@@ -21,6 +21,7 @@ export * from './customer-interview'
21
21
  export * from './customer-interview-ai.types'
22
22
  export * from './luma'
23
23
  export * from './product-release'
24
+ export * from './delivery'
24
25
  export * from './vendor'
25
26
  export * from './vendor-links'
26
27
  export * from './video-processing'
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Dev-sections registry barrel.
3
+ *
4
+ * `RELEASE_STATUS_OPTIONS` (the hub's old alias) is deliberately NOT
5
+ * re-exported — embedders that need the release-status options should
6
+ * import `releaseStatusOptions` from `@flamingo-stack/openframe-frontend-core/types`
7
+ * directly. That matches lib's existing canonical export name and
8
+ * avoids one-way alias drift.
9
+ */
10
+
11
+ export {
12
+ OPENFRAME_DEV_SECTIONS,
13
+ ROADMAP_STATUS_OPTIONS,
14
+ DELIVERY_TASK_TYPE_OPTIONS,
15
+ type OpenframeDevSection,
16
+ type OpenframeDevSectionKey,
17
+ } from './openframe-dev-sections'
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Canonical per-section metadata for the OpenFrame dev-center surfaces
3
+ * (Roadmap / Bug-fixes & Enhancements / Releases / Onboarding Guides).
4
+ *
5
+ * One file co-locates every per-section value the openframe homepage
6
+ * navigator card AND the destination full-page hero both consume —
7
+ * icon, brief navigator description, longer hero paragraph, search
8
+ * config (placeholder + paramKey), filter config (paramKey + options).
9
+ *
10
+ * Adding a 5th section is one edit here and zero edits across the
11
+ * page files / navigator grid.
12
+ *
13
+ * EMBEDDER NOTE — `onboarding`:
14
+ * The `onboarding` entry is INERT on `hero`/`search`/`filter` because
15
+ * `/onboarding-guides` is owned by `OnboardingGuidesCatalogView` (a
16
+ * hub-side full-page view), not the shared `DevSectionView` chrome.
17
+ * This entry exists ONLY to drive the homepage navigator card. Any
18
+ * embedder iterating the registry must SKIP `onboarding` if they
19
+ * don't host the `/onboarding-guides` route — e.g.
20
+ * `Object.values(OPENFRAME_DEV_SECTIONS).filter(s => s.href !== '/onboarding-guides')`.
21
+ *
22
+ * SERVER-BUNDLE SAFETY:
23
+ * This module imports lucide-react icon components as JSX values but
24
+ * never RENDERS them at module load. Lib's tsup config places `utils/`
25
+ * in the server bundle (no `"use client"` banner), which is safe
26
+ * because component-function references don't execute on import.
27
+ * Precedent: `src/utils/platform-config.tsx` (also lucide imports,
28
+ * also server-bundle, also imported by route-page `metadata` exports).
29
+ */
30
+
31
+ import { Map as MapIcon, Wrench, Rocket, GraduationCap, type LucideIcon } from 'lucide-react'
32
+ import { releaseStatusOptions } from '../../types'
33
+
34
+ // Roadmap status options — `as const` preserves readonly tuple typing
35
+ // across the registry boundary.
36
+ export const ROADMAP_STATUS_OPTIONS = [
37
+ { value: 'all', label: 'All' },
38
+ { value: 'completed', label: 'Completed' },
39
+ { value: 'in_progress', label: 'In Progress' },
40
+ ] as const
41
+
42
+ // Delivery (ClickUp custom item type) filter options. `Bug` and `Request`
43
+ // are the ClickUp `custom_item_id` labels — 1008 / 1009.
44
+ export const DELIVERY_TASK_TYPE_OPTIONS = [
45
+ { value: 'all', label: 'All' },
46
+ { value: 'Bug', label: 'Bug-fix' },
47
+ { value: 'Request', label: 'Enhancement' },
48
+ ] as const
49
+
50
+ export interface OpenframeDevSection {
51
+ /** Route href the navigator card and any internal cross-link composes. */
52
+ href: string
53
+ icon: LucideIcon
54
+ /** Brief copy for the homepage navigator card (≈50 char teaser). */
55
+ navigator: {
56
+ title: string
57
+ description: string
58
+ }
59
+ /** Longer copy for the destination page hero (≈120-150 char marketing paragraph). */
60
+ hero: {
61
+ title: string
62
+ description: string
63
+ }
64
+ /** Inline search bar configuration. */
65
+ search: {
66
+ /** Placeholder text shown in the search input. */
67
+ placeholder: string
68
+ /** URL search param the input writes on submit and the list reads on fetch. */
69
+ paramKey: string
70
+ } | null
71
+ /** Filter pill row configuration. `null` when the section has no filter (e.g. onboarding-guides). */
72
+ filter: {
73
+ label: string
74
+ /** URL search param the pills write and the list reads. */
75
+ paramKey: string
76
+ /** The option value that maps to "no filter applied". When selected,
77
+ * the URL param is removed instead of being set. Defaults to the
78
+ * first option's value if omitted — explicit field guards against
79
+ * brittleness if `options` is later reordered. */
80
+ defaultValue: string
81
+ options: readonly { value: string; label: string }[]
82
+ } | null
83
+ }
84
+
85
+ export const OPENFRAME_DEV_SECTIONS = {
86
+ roadmap: {
87
+ href: '/roadmap',
88
+ icon: MapIcon,
89
+ navigator: {
90
+ title: 'Development Roadmap',
91
+ description: "What we're building next — vote on upcoming features.",
92
+ },
93
+ hero: {
94
+ title: 'Development Roadmap',
95
+ description:
96
+ "See what's in flight, what's planned, and what's up for community vote. The entire OpenFrame roadmap is public.",
97
+ },
98
+ search: { placeholder: 'Search roadmap items...', paramKey: 'search' },
99
+ filter: { label: 'Status', paramKey: 'status', defaultValue: 'all', options: ROADMAP_STATUS_OPTIONS },
100
+ },
101
+ delivery: {
102
+ href: '/bug-fixes-and-enhancements',
103
+ icon: Wrench,
104
+ navigator: {
105
+ title: 'Bug-fixes & Enhancements',
106
+ description: 'Recently shipped fixes and improvements.',
107
+ },
108
+ hero: {
109
+ title: 'Bug-fixes & Enhancements',
110
+ description:
111
+ 'A running log of fixes and improvements shipping into OpenFrame — recently completed and actively in progress.',
112
+ },
113
+ search: { placeholder: 'Search tasks...', paramKey: 'search' },
114
+ filter: { label: 'Type', paramKey: 'task_type', defaultValue: 'all', options: DELIVERY_TASK_TYPE_OPTIONS },
115
+ },
116
+ releases: {
117
+ href: '/releases',
118
+ icon: Rocket,
119
+ navigator: {
120
+ title: 'Product Releases',
121
+ description: 'Version history and release notes.',
122
+ },
123
+ hero: {
124
+ title: 'Product Releases',
125
+ description:
126
+ 'Version notes, change summaries, and stability tier (alpha / beta / stable) for every OpenFrame release.',
127
+ },
128
+ search: { placeholder: 'Search releases...', paramKey: 'search' },
129
+ filter: { label: 'Status', paramKey: 'release_status', defaultValue: 'all', options: releaseStatusOptions },
130
+ },
131
+ onboarding: {
132
+ href: '/onboarding-guides',
133
+ icon: GraduationCap,
134
+ navigator: {
135
+ title: 'Onboarding Guides',
136
+ description: 'Step-by-step product walkthroughs.',
137
+ },
138
+ // `hero` / `search` / `filter` are intentionally inert here —
139
+ // /onboarding-guides is owned by OnboardingGuidesCatalogView, not
140
+ // DevSectionView. This entry exists ONLY to drive the homepage
141
+ // navigator card so all 4 cards stay in one registry.
142
+ hero: { title: '', description: '' },
143
+ search: null,
144
+ filter: null,
145
+ },
146
+ } as const satisfies Record<string, OpenframeDevSection>
147
+
148
+ export type OpenframeDevSectionKey = keyof typeof OPENFRAME_DEV_SECTIONS
@@ -168,4 +168,9 @@ export { isCrossOriginUrl } from '../components/chat/utils/is-cross-origin-url'
168
168
  // React-version-aware `fetchpriority` prop builder — spread into `<img>`
169
169
  // / `<iframe>` so the rendered DOM attribute is correct under React 18
170
170
  // (lowercase) AND React 19 (camelCase) without console warnings.
171
- export { fetchPriorityProp, type FetchPriorityValue } from './fetch-priority'
171
+ export { fetchPriorityProp, type FetchPriorityValue } from './fetch-priority'
172
+
173
+ // Dev-center section registry (Roadmap / Delivery / Releases / Onboarding) —
174
+ // server-safe (no JSX, no contexts/* imports); imported by route-page
175
+ // `metadata` exports + the shared `<DevSectionView>` chrome.
176
+ export * from './dev-sections'