@flamingo-stack/openframe-frontend-core 0.0.302 → 0.0.303-snapshot.20260622160827
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.
- package/dist/{chunk-YLD4WH67.js → chunk-2X4HTRQ4.js} +10 -22
- package/dist/chunk-2X4HTRQ4.js.map +1 -0
- package/dist/{chunk-LFXZJZKO.js → chunk-45DC5AJC.js} +2 -2
- package/dist/{chunk-ZDN6OEWV.cjs → chunk-7KTSRZI4.cjs} +483 -549
- package/dist/chunk-7KTSRZI4.cjs.map +1 -0
- package/dist/{chunk-3I7T732P.cjs → chunk-A25ZI7HO.cjs} +12 -12
- package/dist/{chunk-3I7T732P.cjs.map → chunk-A25ZI7HO.cjs.map} +1 -1
- package/dist/{chunk-K4ZENCQE.cjs → chunk-BMNGBMSN.cjs} +26 -26
- package/dist/{chunk-K4ZENCQE.cjs.map → chunk-BMNGBMSN.cjs.map} +1 -1
- package/dist/{chunk-5D76BUKB.js → chunk-DOYOOBP4.js} +2 -2
- package/dist/{chunk-TTHXHSVU.cjs → chunk-FVLEE7YZ.cjs} +23 -35
- package/dist/chunk-FVLEE7YZ.cjs.map +1 -0
- package/dist/{chunk-JHNAQUWK.js → chunk-INZOAK77.js} +2 -2
- package/dist/{chunk-N7XM5HTI.js → chunk-JO6EUJGU.js} +21 -27
- package/dist/chunk-JO6EUJGU.js.map +1 -0
- package/dist/{chunk-LWJYIEPP.cjs → chunk-MZRNARMO.cjs} +37 -37
- package/dist/{chunk-LWJYIEPP.cjs.map → chunk-MZRNARMO.cjs.map} +1 -1
- package/dist/{chunk-BD4YJSRZ.cjs → chunk-O4TIFKDG.cjs} +7 -7
- package/dist/{chunk-BD4YJSRZ.cjs.map → chunk-O4TIFKDG.cjs.map} +1 -1
- package/dist/{chunk-TMLDWPTS.cjs → chunk-RNF2E736.cjs} +11 -10
- package/dist/chunk-RNF2E736.cjs.map +1 -0
- package/dist/{chunk-HLJLLNOB.cjs → chunk-UAJAJFI6.cjs} +44 -50
- package/dist/chunk-UAJAJFI6.cjs.map +1 -0
- package/dist/{chunk-A22ZO5SB.js → chunk-X5N6ANEO.js} +4 -3
- package/dist/{chunk-A22ZO5SB.js.map → chunk-X5N6ANEO.js.map} +1 -1
- package/dist/{chunk-HRDN27UP.js → chunk-Y2D2RJQX.js} +2701 -2767
- package/dist/chunk-Y2D2RJQX.js.map +1 -0
- package/dist/{chunk-IXCQFNF7.js → chunk-YV73VRRY.js} +2 -2
- package/dist/{chunk-RBZCSKFW.cjs → chunk-Z7322A4A.cjs} +5 -5
- package/dist/{chunk-RBZCSKFW.cjs.map → chunk-Z7322A4A.cjs.map} +1 -1
- package/dist/{chunk-ZUALRZHW.js → chunk-ZXIM2DJM.js} +2 -2
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/chat/hooks/use-chat-identity.d.ts +7 -1
- package/dist/components/chat/hooks/use-chat-identity.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-empty-state-config.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-slash-commands.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +2 -2
- package/dist/components/chat/index.js +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/doc-viewer.d.ts +3 -4
- package/dist/components/docs/doc-viewer.d.ts.map +1 -1
- package/dist/components/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/docs/use-docs-resolve-link.d.ts.map +1 -1
- package/dist/components/docs/use-document-tree.d.ts.map +1 -1
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/features/time-tracker/time-tracker-panel.d.ts.map +1 -1
- package/dist/components/index.cjs +172 -178
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +8 -14
- package/dist/components/index.js.map +1 -1
- package/dist/components/layout/page-layout.d.ts +1 -10
- package/dist/components/layout/page-layout.d.ts.map +1 -1
- package/dist/components/layout/title-block.d.ts +1 -17
- package/dist/components/layout/title-block.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/index.cjs +23 -23
- package/dist/components/onboarding-guides/index.js +3 -3
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/shared/dev-section/dev-section-page.d.ts +0 -9
- package/dist/components/shared/dev-section/dev-section-page.d.ts.map +1 -1
- package/dist/components/shared/dev-section/dev-section-view.d.ts.map +1 -1
- package/dist/components/shared/dev-section/index.d.ts +1 -1
- package/dist/components/shared/dev-section/index.d.ts.map +1 -1
- package/dist/components/shared/doc-search/use-doc-search.d.ts.map +1 -1
- package/dist/components/tickets/index.cjs +60 -60
- package/dist/components/tickets/index.js +3 -3
- package/dist/components/ui/index.cjs +2 -6
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.d.ts +0 -1
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +1 -5
- package/dist/index.cjs +2 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -5
- package/package.json +1 -1
- package/src/components/chat/embeddable-chat.tsx +25 -3
- package/src/components/chat/hooks/use-chat-identity.ts +13 -2
- package/src/components/chat/hooks/use-empty-state-config.ts +30 -16
- package/src/components/chat/hooks/use-slash-commands.ts +24 -8
- package/src/components/docs/doc-viewer.tsx +25 -22
- package/src/components/docs/use-docs-resolve-link.ts +2 -1
- package/src/components/docs/use-document-tree.ts +3 -2
- package/src/components/features/time-tracker/time-tracker-header-button.tsx +1 -1
- package/src/components/features/time-tracker/time-tracker-panel.tsx +9 -4
- package/src/components/layout/page-layout.tsx +28 -14
- package/src/components/layout/title-block.tsx +86 -40
- package/src/components/shared/dev-section/dev-section-page.tsx +1 -9
- package/src/components/shared/dev-section/dev-section-view.tsx +9 -14
- package/src/components/shared/dev-section/index.ts +1 -1
- package/src/components/shared/doc-search/use-doc-search.ts +2 -1
- package/src/components/ui/index.ts +0 -1
- package/dist/chunk-HLJLLNOB.cjs.map +0 -1
- package/dist/chunk-HRDN27UP.js.map +0 -1
- package/dist/chunk-N7XM5HTI.js.map +0 -1
- package/dist/chunk-TMLDWPTS.cjs.map +0 -1
- package/dist/chunk-TTHXHSVU.cjs.map +0 -1
- package/dist/chunk-YLD4WH67.js.map +0 -1
- package/dist/chunk-ZDN6OEWV.cjs.map +0 -1
- package/dist/components/layout/page-header.d.ts +0 -78
- package/dist/components/layout/page-header.d.ts.map +0 -1
- package/dist/components/layout/page-with-header.d.ts +0 -67
- package/dist/components/layout/page-with-header.d.ts.map +0 -1
- package/src/components/layout/page-header.tsx +0 -182
- package/src/components/layout/page-with-header.tsx +0 -110
- /package/dist/{chunk-LFXZJZKO.js.map → chunk-45DC5AJC.js.map} +0 -0
- /package/dist/{chunk-5D76BUKB.js.map → chunk-DOYOOBP4.js.map} +0 -0
- /package/dist/{chunk-JHNAQUWK.js.map → chunk-INZOAK77.js.map} +0 -0
- /package/dist/{chunk-IXCQFNF7.js.map → chunk-YV73VRRY.js.map} +0 -0
- /package/dist/{chunk-ZUALRZHW.js.map → chunk-ZXIM2DJM.js.map} +0 -0
|
@@ -62,14 +62,25 @@ export async function fetchSlashCommands(
|
|
|
62
62
|
signal: AbortSignal | undefined,
|
|
63
63
|
commandsUrl: string,
|
|
64
64
|
): Promise<SlashCommandSummary[]> {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
try {
|
|
66
|
+
const url = new URL(commandsUrl, window.location.origin);
|
|
67
|
+
if (prefix) url.searchParams.set("q", prefix);
|
|
68
|
+
// `headers: {}` opts out of the default `Content-Type: application/json`
|
|
69
|
+
// — this is a bare GET with no body, so no content-type is needed.
|
|
70
|
+
const res = await embedAuthedFetch(url.toString(), { signal, headers: {} });
|
|
71
|
+
if (!res.ok) return [];
|
|
72
|
+
const data = (await res.json()) as { commands?: SlashCommandSummary[] };
|
|
73
|
+
return data.commands ?? [];
|
|
74
|
+
} catch (err) {
|
|
75
|
+
// Cancellation (unmount / dep change) MUST propagate so react-query treats
|
|
76
|
+
// it as cancelled, not as a successful empty result. Every OTHER failure
|
|
77
|
+
// (network down, proxy reject, non-JSON body) degrades to "no commands" so
|
|
78
|
+
// a flaky commands endpoint can NEVER break the chat — the autocomplete /
|
|
79
|
+
// onboarding list just renders empty.
|
|
80
|
+
if ((err as Error)?.name === "AbortError") throw err;
|
|
81
|
+
console.warn("[chat] slash-commands fetch failed, showing none:", err);
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
73
84
|
}
|
|
74
85
|
|
|
75
86
|
/**
|
|
@@ -144,6 +155,11 @@ export function useSlashCommandRegistry(
|
|
|
144
155
|
enabled: options?.enabled ?? true,
|
|
145
156
|
staleTime: Infinity,
|
|
146
157
|
gcTime: Infinity,
|
|
158
|
+
// The commands registry is non-critical chrome — a failure degrades to an
|
|
159
|
+
// empty registry (handled in `fetchSlashCommands`). Don't retry: settle to
|
|
160
|
+
// the neutral empty state immediately so a flaky endpoint never holds the
|
|
161
|
+
// welcome UI in a loading spinner.
|
|
162
|
+
retry: false,
|
|
147
163
|
});
|
|
148
164
|
return {
|
|
149
165
|
commands: query.data ?? [],
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { useMemo } from "react"
|
|
4
4
|
import { MultiLevelNavigation, MobileNavigationDropdown } from "../navigation/multi-level-navigation"
|
|
5
|
-
import { PageHeader } from "../layout/page-header"
|
|
6
5
|
import { PageLayout } from "../layout/page-layout"
|
|
7
6
|
import { PageShell } from "../layout/article-detail-layout"
|
|
8
7
|
import { useRouter } from "../../embed-shims/next-navigation"
|
|
@@ -59,10 +58,9 @@ export interface DocViewerProps {
|
|
|
59
58
|
*/
|
|
60
59
|
chatSource: string
|
|
61
60
|
|
|
62
|
-
/** Page title — rendered
|
|
63
|
-
* so the doc-viewer chrome matches
|
|
64
|
-
*
|
|
65
|
-
* pixel-for-pixel. ReactNode is intentionally not supported here —
|
|
61
|
+
/** Page title — rendered as the inline hero `<h1>` (same DOM
|
|
62
|
+
* `<DevSectionView>`'s hero uses) so the doc-viewer chrome matches the
|
|
63
|
+
* dev-section pages. ReactNode is intentionally not supported here —
|
|
66
64
|
* every consumer renders the same typography. */
|
|
67
65
|
title?: string
|
|
68
66
|
/** Optional icon rendered inline before the title text — same slot
|
|
@@ -259,15 +257,10 @@ function DocViewerContent({
|
|
|
259
257
|
const resolvedEmptyText = emptyStateText || defaultEmptyText
|
|
260
258
|
|
|
261
259
|
return (
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
// `<PageHeader noTopPadding noBottomMargin>` renders the title section
|
|
267
|
-
// the same way `<DevSectionView>`'s hero does. This is the only way to
|
|
268
|
-
// guarantee /knowledge-base sits at pixel-identical vertical rhythm to
|
|
269
|
-
// /roadmap / /releases / /onboarding-guides — same components, same DOM,
|
|
270
|
-
// not "same CSS classes that look similar on paper."
|
|
260
|
+
// Render through the shared wrapper chain (PageShell → PageLayout →
|
|
261
|
+
// `gap-10 flex-col`). PageLayout owns the back-button row; the inner
|
|
262
|
+
// `gap-10` div holds an inline title hero (same DOM `<DevSectionView>`'s
|
|
263
|
+
// hero renders) followed by the search bar + content grid.
|
|
271
264
|
//
|
|
272
265
|
// `colorPalette` / `className` / `bgStyle` flow through PageShell's
|
|
273
266
|
// contentClassName + an inner style-passthrough wrapper so legacy
|
|
@@ -277,14 +270,24 @@ function DocViewerContent({
|
|
|
277
270
|
<div style={{ ...bgStyle, ...containerBgStyle }}>
|
|
278
271
|
<PageLayout backButton={backCfg ?? undefined}>
|
|
279
272
|
<div className="w-full flex flex-col gap-10">
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
273
|
+
{(title || titleIcon || subtitle) && (
|
|
274
|
+
<div className="space-y-4">
|
|
275
|
+
{(title || titleIcon) && (
|
|
276
|
+
<h1 className="text-h1 tracking-[-1.12px] text-ods-text-primary flex items-center gap-3">
|
|
277
|
+
{titleIcon}
|
|
278
|
+
{title && (
|
|
279
|
+
<span>
|
|
280
|
+
{title}
|
|
281
|
+
{accentDot && <span className="text-ods-accent">.</span>}
|
|
282
|
+
</span>
|
|
283
|
+
)}
|
|
284
|
+
</h1>
|
|
285
|
+
)}
|
|
286
|
+
<p className="font-['DM_Sans'] font-medium text-[18px] leading-[28px] text-ods-text-secondary max-w-3xl line-clamp-2 min-h-[56px]">
|
|
287
|
+
{subtitle || ' '}
|
|
288
|
+
</p>
|
|
289
|
+
</div>
|
|
290
|
+
)}
|
|
288
291
|
|
|
289
292
|
{showAIChat && (
|
|
290
293
|
<DocSearchBar
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback } from 'react'
|
|
2
2
|
import { useChatRuntime } from '../../contexts/chat-runtime-context'
|
|
3
3
|
import type { ResolveLinkResult } from '../../types/doc-source'
|
|
4
|
+
import { contentFetch } from '../../utils/embed-content-fetch'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* `useDocsResolveLink(sourceId, override?)` — POST `/api/docs/resolve-link`
|
|
@@ -30,7 +31,7 @@ export function useDocsResolveLink(
|
|
|
30
31
|
return useCallback(
|
|
31
32
|
async (href: string, currentPath: string): Promise<ResolveLinkResult> => {
|
|
32
33
|
try {
|
|
33
|
-
const response = await
|
|
34
|
+
const response = await contentFetch(resolvedResolveLinkEndpoint, {
|
|
34
35
|
method: 'POST',
|
|
35
36
|
headers: { 'Content-Type': 'application/json' },
|
|
36
37
|
body: JSON.stringify({ link: href, currentPath, source: sourceId }),
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
DEFAULT_FOLDER_INDEX_FILE,
|
|
10
10
|
} from '../../utils/doc-tree-nav'
|
|
11
11
|
import { useDocNavigation } from './doc-navigation-context'
|
|
12
|
+
import { contentFetch } from '../../utils/embed-content-fetch'
|
|
12
13
|
import { scrollElementIntoView } from '../../utils/scroll-into-view'
|
|
13
14
|
import { navigateSamePageHash, HUB_HEADER_OFFSET_PX } from '../../utils/same-page-hash-nav'
|
|
14
15
|
|
|
@@ -179,7 +180,7 @@ export function useDocumentTree(
|
|
|
179
180
|
setIsLoadingStructure(true)
|
|
180
181
|
setError(null)
|
|
181
182
|
|
|
182
|
-
const response = await
|
|
183
|
+
const response = await contentFetch(structureEndpoint)
|
|
183
184
|
|
|
184
185
|
if (!response.ok) {
|
|
185
186
|
throw new Error('Failed to load documentation structure')
|
|
@@ -232,7 +233,7 @@ export function useDocumentTree(
|
|
|
232
233
|
// returns early without writing to state. Clearing error here would
|
|
233
234
|
// briefly flicker the user-visible error message.
|
|
234
235
|
|
|
235
|
-
const response = await
|
|
236
|
+
const response = await contentFetch(`${contentEndpoint}?path=${encodeURIComponent(path)}`)
|
|
236
237
|
|
|
237
238
|
// Request-id guard: between awaits, `lastFetchedPath.current` may have
|
|
238
239
|
// been bumped by a newer fetch (the structure-arrives auto-select issues
|
|
@@ -70,7 +70,7 @@ export function TimeTrackerHeaderButton({ className, disabled }: TimeTrackerHead
|
|
|
70
70
|
<PopoverPrimitive.Portal>
|
|
71
71
|
<PopoverPrimitive.Content
|
|
72
72
|
align="end"
|
|
73
|
-
sideOffset={
|
|
73
|
+
sideOffset={8}
|
|
74
74
|
collisionPadding={8}
|
|
75
75
|
className={cn(
|
|
76
76
|
'z-[1300] w-[460px] max-w-[calc(100vw-1rem)] outline-none',
|
|
@@ -92,7 +92,7 @@ export function TimeTrackerPanel({
|
|
|
92
92
|
size="icon-sm"
|
|
93
93
|
onClick={onClose}
|
|
94
94
|
aria-label="Close time tracker"
|
|
95
|
-
className="absolute right-[var(--spacing-system-sf)] top-[var(--spacing-system-sf)] text-ods-text-secondary hover:text-ods-text-primary [&_svg]:!size-6"
|
|
95
|
+
className="absolute right-[var(--spacing-system-sf)] top-[var(--spacing-system-sf)] hidden text-ods-text-secondary hover:text-ods-text-primary md:inline-flex [&_svg]:!size-6"
|
|
96
96
|
>
|
|
97
97
|
<XmarkIcon />
|
|
98
98
|
</Button>
|
|
@@ -105,7 +105,7 @@ export function TimeTrackerPanel({
|
|
|
105
105
|
<Button
|
|
106
106
|
variant="transparent"
|
|
107
107
|
onClick={onCancel}
|
|
108
|
-
className="h-auto p-0 text-h6 font-medium text-ods-text-secondary underline hover:bg-transparent hover:text-ods-text-primary"
|
|
108
|
+
className="h-auto p-0 text-h6 font-medium text-ods-text-secondary underline hover:bg-transparent hover:text-ods-text-primary md:h-auto"
|
|
109
109
|
>
|
|
110
110
|
Cancel Entry
|
|
111
111
|
</Button>
|
|
@@ -130,7 +130,7 @@ export function TimeTrackerPanel({
|
|
|
130
130
|
onClick={status === 'tracking' ? onPause : status === 'paused' ? onResume : onStart}
|
|
131
131
|
disabled={isStarting}
|
|
132
132
|
aria-label={isRunning ? 'Pause tracking' : isActive ? 'Resume tracking' : 'Start tracking'}
|
|
133
|
-
className="h-auto w-auto rounded-none [&_svg]:!size-6"
|
|
133
|
+
className="h-auto w-auto rounded-none transition-none [&_svg]:!size-6"
|
|
134
134
|
>
|
|
135
135
|
{isRunning ? <PauseIcon /> : <PlayIcon />}
|
|
136
136
|
</Button>
|
|
@@ -157,7 +157,7 @@ export function TimeTrackerPanel({
|
|
|
157
157
|
placeholder="Assign Ticket"
|
|
158
158
|
loading={ticketsLoading}
|
|
159
159
|
invalid={showFieldError}
|
|
160
|
-
error={showFieldError ? '
|
|
160
|
+
error={showFieldError ? 'Required if no notes added' : undefined}
|
|
161
161
|
disableClientFilter={!!onTicketSearch}
|
|
162
162
|
onInputChange={(value, reason) => {
|
|
163
163
|
if (reason === 'input') onTicketSearch?.(value)
|
|
@@ -169,6 +169,7 @@ export function TimeTrackerPanel({
|
|
|
169
169
|
value={notes}
|
|
170
170
|
onChange={(e) => onNotesChange(e.target.value)}
|
|
171
171
|
invalid={showFieldError}
|
|
172
|
+
error={showFieldError ? 'Required if no ticket assigned' : undefined}
|
|
172
173
|
placeholder="Additional Notes (optional if ticket selected)"
|
|
173
174
|
className="max-h-[280px]"
|
|
174
175
|
/>
|
|
@@ -217,6 +218,10 @@ export function TimeTrackerPanel({
|
|
|
217
218
|
</SplitButton>
|
|
218
219
|
</div>
|
|
219
220
|
</div>
|
|
221
|
+
|
|
222
|
+
<Button variant="outline" fullWidth onClick={onClose} className="md:hidden">
|
|
223
|
+
Close
|
|
224
|
+
</Button>
|
|
220
225
|
</div>
|
|
221
226
|
)
|
|
222
227
|
}
|
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
/* ============================================================================
|
|
4
|
+
* ⛔️ FROZEN — DO NOT MODIFY (AI agents & contributors, read this first)
|
|
5
|
+
* ----------------------------------------------------------------------------
|
|
6
|
+
* `PageLayout` and its `TitleBlock` are a FINALIZED, locked component. They are
|
|
7
|
+
* the canonical, stable page chrome for OpenFrame surfaces and their visual +
|
|
8
|
+
* behavioral contract is intentionally complete. Treat this file as read-only.
|
|
9
|
+
*
|
|
10
|
+
* Do NOT: change the markup/CSS, swap the title typography (`text-h2`) or
|
|
11
|
+
* subtitle (`text-h6`), re-architect this to delegate to another primitive,
|
|
12
|
+
* add/rename props, or "unify"/"refactor"/"simplify" it. Do NOT restyle to
|
|
13
|
+
* match some other surface.
|
|
14
|
+
*
|
|
15
|
+
* Why this rule exists (the incident it prevents): a refactor once re-styled
|
|
16
|
+
* this layout (bumped the title to `text-h1`, rerouted it through a new
|
|
17
|
+
* `PageHeader`/`PageWithHeader` chain) to "unify" page chrome. That silently
|
|
18
|
+
* changed the look of every page rendered through `PageLayout` and had to be
|
|
19
|
+
* fully reverted. The current code IS the reverted, correct baseline.
|
|
20
|
+
*
|
|
21
|
+
* MANY consumers depend on the EXACT current output — not only OpenFrame pages
|
|
22
|
+
* but also `DevSectionPage`, `DocViewer`, and the multi-platform hub (through
|
|
23
|
+
* its own local `PageWithHeader`). A change here ripples across all of them.
|
|
24
|
+
*
|
|
25
|
+
* If a new design genuinely needs different chrome: build a SEPARATE new
|
|
26
|
+
* component for it. Never mutate this one. If you believe an edit here is
|
|
27
|
+
* unavoidable, STOP and get explicit human sign-off first.
|
|
28
|
+
* ========================================================================== */
|
|
29
|
+
|
|
3
30
|
import React from 'react'
|
|
4
31
|
import { cn } from '../../utils/cn'
|
|
5
32
|
import type { ActionsMenuGroup } from '../ui/actions-menu'
|
|
@@ -10,11 +37,6 @@ export interface PageLayoutProps {
|
|
|
10
37
|
children: React.ReactNode
|
|
11
38
|
title?: string
|
|
12
39
|
subtitle?: string
|
|
13
|
-
/** Inline icon rendered before the title text — forwarded to
|
|
14
|
-
* TitleBlock/PageHeader. Same shape as DevSectionPage's hero icon. */
|
|
15
|
-
titleIcon?: React.ReactNode
|
|
16
|
-
/** Yellow accent dot after the title — forwarded to TitleBlock/PageHeader. */
|
|
17
|
-
accentDot?: boolean
|
|
18
40
|
image?: { src: string; alt?: string }
|
|
19
41
|
backButton?: { label?: string; onClick: () => void }
|
|
20
42
|
actions?: PageActionButton[]
|
|
@@ -38,8 +60,6 @@ export function PageLayout({
|
|
|
38
60
|
children,
|
|
39
61
|
title,
|
|
40
62
|
subtitle,
|
|
41
|
-
titleIcon,
|
|
42
|
-
accentDot,
|
|
43
63
|
image,
|
|
44
64
|
backButton,
|
|
45
65
|
actions,
|
|
@@ -53,16 +73,14 @@ export function PageLayout({
|
|
|
53
73
|
}: PageLayoutProps) {
|
|
54
74
|
const hasActions = actions && actions.length > 0
|
|
55
75
|
const needsBottomPadding = hasActions && actionsVariant === 'primary-buttons'
|
|
56
|
-
const hasHeader = showHeader && (title || subtitle ||
|
|
76
|
+
const hasHeader = showHeader && (title || subtitle || image || backButton || hasActions || selector)
|
|
57
77
|
|
|
58
78
|
return (
|
|
59
79
|
<div className={cn('flex flex-col w-full', className)}>
|
|
60
80
|
{hasHeader && (
|
|
61
81
|
<TitleBlock
|
|
62
82
|
title={title}
|
|
63
|
-
titleIcon={titleIcon}
|
|
64
83
|
subtitle={subtitle}
|
|
65
|
-
accentDot={accentDot}
|
|
66
84
|
image={image}
|
|
67
85
|
backButton={backButton}
|
|
68
86
|
actions={actions}
|
|
@@ -83,8 +101,4 @@ export function PageLayout({
|
|
|
83
101
|
export type { PageActionButton } from '../ui/page-actions'
|
|
84
102
|
export { TitleBlock } from './title-block'
|
|
85
103
|
export type { TitleBlockProps } from './title-block'
|
|
86
|
-
export { PageHeader } from './page-header'
|
|
87
|
-
export type { PageHeaderProps } from './page-header'
|
|
88
|
-
export { PageWithHeader } from './page-with-header'
|
|
89
|
-
export type { PageWithHeaderProps } from './page-with-header'
|
|
90
104
|
export default PageLayout
|
|
@@ -1,29 +1,39 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
/* ============================================================================
|
|
4
|
+
* ⛔️ FROZEN — DO NOT MODIFY (AI agents & contributors, read this first)
|
|
5
|
+
* ----------------------------------------------------------------------------
|
|
6
|
+
* `TitleBlock` is the FINALIZED title/subtitle/back-button/actions chrome used
|
|
7
|
+
* by `PageLayout`. It is a locked, complete component — treat it as read-only.
|
|
8
|
+
*
|
|
9
|
+
* Do NOT: change the markup/CSS, alter the title typography (`text-h2`) or
|
|
10
|
+
* subtitle (`text-h6`), change the image/title 2-column layout, re-route this
|
|
11
|
+
* through a `PageHeader`/`PageWithHeader` primitive, or add/rename props. Do
|
|
12
|
+
* NOT "unify"/"refactor"/"simplify" it or restyle it to match another surface.
|
|
13
|
+
*
|
|
14
|
+
* Why this rule exists (the incident it prevents): a refactor once rewrote
|
|
15
|
+
* this to delegate to a new `PageHeader` (title bumped to `text-h1`, new
|
|
16
|
+
* subtitle styling) to "unify" page chrome — it silently changed every page
|
|
17
|
+
* using `PageLayout` and had to be fully reverted. This code IS that reverted,
|
|
18
|
+
* correct baseline.
|
|
19
|
+
*
|
|
20
|
+
* Downstream consumers (OpenFrame pages, `DevSectionPage`, `DocViewer`, and the
|
|
21
|
+
* multi-platform hub via its local `PageWithHeader`) depend on the EXACT
|
|
22
|
+
* current output. If a new design needs different chrome, build a SEPARATE new
|
|
23
|
+
* component — never mutate this one. If an edit here seems unavoidable, STOP
|
|
24
|
+
* and get explicit human sign-off first.
|
|
25
|
+
* ========================================================================== */
|
|
26
|
+
|
|
3
27
|
import React from 'react'
|
|
28
|
+
import { cn } from '../../utils/cn'
|
|
4
29
|
import type { ActionsMenuGroup } from '../ui/actions-menu'
|
|
30
|
+
import { EntityImage } from '../ui/entity-image'
|
|
5
31
|
import { PageActions, type PageActionButton } from '../ui/page-actions'
|
|
6
|
-
import {
|
|
32
|
+
import { BackButton } from './back-button'
|
|
7
33
|
|
|
8
|
-
/**
|
|
9
|
-
* `<TitleBlock>` — thin adapter over `<PageHeader>` that turns the
|
|
10
|
-
* `actions: PageActionButton[]` / `menuActions` / `selector` API into
|
|
11
|
-
* a `ReactNode` slot that PageHeader can render. Kept as a separate
|
|
12
|
-
* component for backwards compatibility (`PageLayout` consumes it,
|
|
13
|
-
* external callers may too) — all the DOM/CSS lives in PageHeader.
|
|
14
|
-
*
|
|
15
|
-
* If a new consumer doesn't need the `PageActions` wiring, prefer
|
|
16
|
-
* `<PageHeader>` directly.
|
|
17
|
-
*/
|
|
18
34
|
export interface TitleBlockProps {
|
|
19
35
|
title?: string
|
|
20
36
|
subtitle?: string
|
|
21
|
-
/** Inline icon rendered before the title text (e.g. HelpCircle on /faqs,
|
|
22
|
-
* BookOpen on /knowledge-base, Map on /roadmap). Forwarded verbatim to
|
|
23
|
-
* `<PageHeader>`. */
|
|
24
|
-
titleIcon?: React.ReactNode
|
|
25
|
-
/** Yellow accent dot after the title — same flag as PageHeader. */
|
|
26
|
-
accentDot?: boolean
|
|
27
37
|
image?: { src: string; alt?: string }
|
|
28
38
|
backButton?: { label?: string; onClick: () => void }
|
|
29
39
|
actions?: PageActionButton[]
|
|
@@ -43,8 +53,6 @@ export interface TitleBlockProps {
|
|
|
43
53
|
export function TitleBlock({
|
|
44
54
|
title,
|
|
45
55
|
subtitle,
|
|
46
|
-
titleIcon,
|
|
47
|
-
accentDot,
|
|
48
56
|
image,
|
|
49
57
|
backButton,
|
|
50
58
|
actions,
|
|
@@ -56,29 +64,67 @@ export function TitleBlock({
|
|
|
56
64
|
}: TitleBlockProps) {
|
|
57
65
|
const hasActions = actions && actions.length > 0
|
|
58
66
|
const hasMenuActions = !!menuActions && menuActions.some(g => g.items.length > 0)
|
|
59
|
-
const hasActionsSlot = hasActions || hasMenuActions || !!selector
|
|
60
|
-
|
|
61
|
-
const actionsNode = hasActionsSlot ? (
|
|
62
|
-
<PageActions
|
|
63
|
-
variant={actionsVariant}
|
|
64
|
-
actions={actions ?? []}
|
|
65
|
-
menuActions={menuActions}
|
|
66
|
-
selector={selector}
|
|
67
|
-
/>
|
|
68
|
-
) : undefined
|
|
69
67
|
|
|
70
68
|
return (
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
69
|
+
<div
|
|
70
|
+
className={cn(
|
|
71
|
+
'flex items-end justify-between gap-[var(--spacing-system-m)]',
|
|
72
|
+
'md:flex-col md:items-start md:justify-start lg:flex-row lg:items-end lg:justify-between',
|
|
73
|
+
'pt-[var(--spacing-system-l)]',
|
|
74
|
+
variant === 'card'
|
|
75
|
+
? cn(
|
|
76
|
+
'bg-ods-card border-b border-ods-border',
|
|
77
|
+
'px-[var(--spacing-system-l)] pb-[var(--spacing-system-l)]',
|
|
78
|
+
'md:bg-transparent md:border-b-0',
|
|
79
|
+
'md:px-0 md:pb-0',
|
|
80
|
+
'md:mb-[var(--spacing-system-l)]',
|
|
81
|
+
)
|
|
82
|
+
: 'mb-[var(--spacing-system-l)]',
|
|
83
|
+
className,
|
|
84
|
+
)}
|
|
85
|
+
>
|
|
86
|
+
<div className="flex flex-col gap-[var(--spacing-system-xs)] flex-1 min-w-0">
|
|
87
|
+
{backButton && (
|
|
88
|
+
<BackButton
|
|
89
|
+
onClick={backButton.onClick}
|
|
90
|
+
label={backButton.label}
|
|
91
|
+
className="hidden md:inline-flex"
|
|
92
|
+
/>
|
|
93
|
+
)}
|
|
94
|
+
{(image || subtitle) ? (
|
|
95
|
+
<div className="flex items-center gap-[var(--spacing-system-m)] min-w-0 w-full">
|
|
96
|
+
{image && (
|
|
97
|
+
<EntityImage
|
|
98
|
+
src={image.src}
|
|
99
|
+
alt={image.alt}
|
|
100
|
+
fallbackText={image.alt || title}
|
|
101
|
+
/>
|
|
102
|
+
)}
|
|
103
|
+
<div className="flex flex-col justify-center min-w-0 flex-1">
|
|
104
|
+
{title && (
|
|
105
|
+
<h1 className="text-h2 text-ods-text-primary truncate" title={title}>{title}</h1>
|
|
106
|
+
)}
|
|
107
|
+
{subtitle && (
|
|
108
|
+
<p className="text-h6 text-ods-text-secondary truncate" title={subtitle}>{subtitle}</p>
|
|
109
|
+
)}
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
) : (
|
|
113
|
+
title && <h1 className="text-h2 text-ods-text-primary">{title}</h1>
|
|
114
|
+
)}
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
{(hasActions || hasMenuActions || selector) && (
|
|
118
|
+
<div className="flex gap-2 items-center shrink-0">
|
|
119
|
+
<PageActions
|
|
120
|
+
variant={actionsVariant}
|
|
121
|
+
actions={actions ?? []}
|
|
122
|
+
menuActions={menuActions}
|
|
123
|
+
selector={selector}
|
|
124
|
+
/>
|
|
125
|
+
</div>
|
|
126
|
+
)}
|
|
127
|
+
</div>
|
|
82
128
|
)
|
|
83
129
|
}
|
|
84
130
|
|
|
@@ -25,15 +25,7 @@ import {
|
|
|
25
25
|
type OpenframeDevSectionKey,
|
|
26
26
|
} from '../../../utils/dev-sections/openframe-dev-sections';
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
* old import path. The canonical home is `src/utils/page-header-constants.ts`
|
|
30
|
-
* (NOT a `'use client'` module) so server modules can import it without
|
|
31
|
-
* Next.js turning it into a client reference proxy — that proxy is what
|
|
32
|
-
* blew up lucide's `mergeClasses().trim()` when used as
|
|
33
|
-
* `<Icon className={SECTION_HERO_ICON_CLASS} />` inside a hub
|
|
34
|
-
* server-component preset. */
|
|
35
|
-
import { SECTION_HERO_ICON_CLASS } from '../../../utils/page-header-constants';
|
|
36
|
-
export { SECTION_HERO_ICON_CLASS };
|
|
28
|
+
const SECTION_HERO_ICON_CLASS = 'h-10 w-10 text-ods-accent';
|
|
37
29
|
|
|
38
30
|
export interface DevSectionPageProps {
|
|
39
31
|
sectionKey: OpenframeDevSectionKey;
|
|
@@ -19,7 +19,6 @@ import { useState, useEffect } from 'react';
|
|
|
19
19
|
import { useRouter, useSearchParams, usePathname } from '../../../embed-shims';
|
|
20
20
|
import { SearchInput } from '../../ui';
|
|
21
21
|
import { StatusFilterComponent } from '../../features';
|
|
22
|
-
import { PageHeader } from '../../layout/page-header';
|
|
23
22
|
import {
|
|
24
23
|
OPENFRAME_DEV_SECTIONS,
|
|
25
24
|
type OpenframeDevSectionKey,
|
|
@@ -96,19 +95,15 @@ export function DevSectionView({ sectionKey, hero, preControls, children }: DevS
|
|
|
96
95
|
return (
|
|
97
96
|
<div className="w-full flex flex-col gap-10">
|
|
98
97
|
{hero ? (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
subtitle={hero.description}
|
|
109
|
-
noBottomMargin
|
|
110
|
-
noTopPadding
|
|
111
|
-
/>
|
|
98
|
+
<div className="space-y-4">
|
|
99
|
+
<h1 className="text-h1 tracking-[-1.12px] text-ods-text-primary flex items-center gap-3">
|
|
100
|
+
{hero.icon}
|
|
101
|
+
{hero.title ?? section.hero.title}
|
|
102
|
+
</h1>
|
|
103
|
+
<p className="font-['DM_Sans'] font-medium text-[18px] leading-[28px] text-ods-text-secondary max-w-3xl">
|
|
104
|
+
{hero.description}
|
|
105
|
+
</p>
|
|
106
|
+
</div>
|
|
112
107
|
) : (
|
|
113
108
|
<div className="flex items-center justify-between w-full">
|
|
114
109
|
<h2 className="font-['Azeret_Mono'] font-semibold text-[32px] md:text-[40px] lg:text-[48px] leading-[40px] md:leading-[48px] lg:leading-[56px] text-ods-text-primary tracking-[-0.64px] md:tracking-[-0.8px] lg:tracking-[-0.96px]">
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { DevSectionView, type DevSectionViewProps } from './dev-section-view';
|
|
2
|
-
export { DevSectionPage, type DevSectionPageProps
|
|
2
|
+
export { DevSectionPage, type DevSectionPageProps } from './dev-section-page';
|
|
3
3
|
export {
|
|
4
4
|
DevCardRowContent,
|
|
5
5
|
DevCardRowSkeleton,
|
|
@@ -32,6 +32,7 @@ import { useState, useEffect, useCallback } from 'react'
|
|
|
32
32
|
import { useRouter } from '../../../embed-shims'
|
|
33
33
|
import { useDebounce } from '../../../hooks/ui/use-debounce'
|
|
34
34
|
import { useChatRuntime } from '../../../contexts/chat-runtime-context'
|
|
35
|
+
import { contentFetch } from '../../../utils/embed-content-fetch'
|
|
35
36
|
import type { SearchResult } from '../../ui/search-input'
|
|
36
37
|
import {
|
|
37
38
|
resolveExternalNavigation,
|
|
@@ -118,7 +119,7 @@ export function useDocSearch(config: UseDocSearchConfig) {
|
|
|
118
119
|
})
|
|
119
120
|
if (tableIdsKey) params.set('tableIds', tableIdsKey)
|
|
120
121
|
|
|
121
|
-
const response = await
|
|
122
|
+
const response = await contentFetch(`${resolvedSearchEndpoint}?${params.toString()}`)
|
|
122
123
|
if (!response.ok) {
|
|
123
124
|
throw new Error(`Search request failed: ${response.status}`)
|
|
124
125
|
}
|
|
@@ -61,7 +61,6 @@ export * from './hover-dropdown'
|
|
|
61
61
|
export * from '../chat'
|
|
62
62
|
export * from '../layout/list-page-layout'
|
|
63
63
|
export * from '../layout/page-container'
|
|
64
|
-
export * from '../layout/page-header'
|
|
65
64
|
export * from '../layout/page-heading'
|
|
66
65
|
export * from '../layout/page-layout'
|
|
67
66
|
export * from '../layout/article-detail-layout'
|