@flamingo-stack/openframe-frontend-core 0.0.568-1914.5171.1 → 0.0.568
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-LYP43VO3.js → chunk-2LKVYI7H.js} +4 -4
- package/dist/{chunk-WZW5TXKM.cjs → chunk-6I76UK2Z.cjs} +3 -3
- package/dist/{chunk-WZW5TXKM.cjs.map → chunk-6I76UK2Z.cjs.map} +1 -1
- package/dist/{chunk-H6JEWGJN.cjs → chunk-6PWUUWCO.cjs} +39 -39
- package/dist/{chunk-H6JEWGJN.cjs.map → chunk-6PWUUWCO.cjs.map} +1 -1
- package/dist/{chunk-SWPLMGKK.cjs → chunk-6RSKK4E7.cjs} +479 -526
- package/dist/chunk-6RSKK4E7.cjs.map +1 -0
- package/dist/{chunk-PA47Q3JE.js → chunk-6TDWCBQM.js} +6 -6
- package/dist/{chunk-RDKJCZHY.js → chunk-B44VZF2Z.js} +9 -8
- package/dist/chunk-B44VZF2Z.js.map +1 -0
- package/dist/{chunk-OW4OF7UP.js → chunk-BZ7B255K.js} +6 -6
- package/dist/{chunk-BMHU5V5T.cjs → chunk-DHTT77AG.cjs} +62 -62
- package/dist/{chunk-BMHU5V5T.cjs.map → chunk-DHTT77AG.cjs.map} +1 -1
- package/dist/{chunk-TN4KUALQ.js → chunk-DZ2Z7HRF.js} +2 -2
- package/dist/{chunk-KVCOILLR.js → chunk-EYXZF46V.js} +1032 -1079
- package/dist/chunk-EYXZF46V.js.map +1 -0
- package/dist/{chunk-HISA2T34.js → chunk-G2PAUMV6.js} +2 -2
- package/dist/{chunk-LRFCOGYE.js → chunk-HB3IG2MC.js} +3 -3
- package/dist/{chunk-NFVQ77GG.cjs → chunk-HKG7BZON.cjs} +9 -9
- package/dist/{chunk-NFVQ77GG.cjs.map → chunk-HKG7BZON.cjs.map} +1 -1
- package/dist/{chunk-MTVLA67G.cjs → chunk-KZ3PIUCH.cjs} +14 -14
- package/dist/{chunk-MTVLA67G.cjs.map → chunk-KZ3PIUCH.cjs.map} +1 -1
- package/dist/{chunk-EWSLKRSP.cjs → chunk-KZS2DFC2.cjs} +31 -31
- package/dist/{chunk-EWSLKRSP.cjs.map → chunk-KZS2DFC2.cjs.map} +1 -1
- package/dist/{chunk-QZQUXNVF.cjs → chunk-L22CSZZU.cjs} +11 -11
- package/dist/{chunk-QZQUXNVF.cjs.map → chunk-L22CSZZU.cjs.map} +1 -1
- package/dist/{chunk-ADBY2WCW.cjs → chunk-LNUXUOPN.cjs} +4 -4
- package/dist/{chunk-ADBY2WCW.cjs.map → chunk-LNUXUOPN.cjs.map} +1 -1
- package/dist/{chunk-NQI5BF7S.cjs → chunk-LXWQLUBR.cjs} +7 -7
- package/dist/{chunk-NQI5BF7S.cjs.map → chunk-LXWQLUBR.cjs.map} +1 -1
- package/dist/{chunk-NRU3AUUP.js → chunk-MQQM4NCN.js} +2 -2
- package/dist/{chunk-I5J7RE5E.js → chunk-O3JCKEVG.js} +2 -2
- package/dist/{chunk-XOFFASWS.cjs → chunk-P7QGTNEW.cjs} +97 -97
- package/dist/{chunk-XOFFASWS.cjs.map → chunk-P7QGTNEW.cjs.map} +1 -1
- package/dist/{chunk-FXEEH6GY.js → chunk-QVQLNKFS.js} +2 -2
- package/dist/{chunk-ANDU6MPG.cjs → chunk-RBYMJF2V.cjs} +26 -26
- package/dist/{chunk-ANDU6MPG.cjs.map → chunk-RBYMJF2V.cjs.map} +1 -1
- package/dist/{chunk-7GFUYIKG.cjs → chunk-T7AE7CKV.cjs} +5 -5
- package/dist/{chunk-7GFUYIKG.cjs.map → chunk-T7AE7CKV.cjs.map} +1 -1
- package/dist/{chunk-TCVAF576.js → chunk-VEXTUGUA.js} +4 -4
- package/dist/{chunk-4LHEQMOV.cjs → chunk-WPPBPRFU.cjs} +9 -8
- package/dist/chunk-WPPBPRFU.cjs.map +1 -0
- package/dist/{chunk-ED6F54AE.cjs → chunk-Y2BYHTEN.cjs} +29 -29
- package/dist/{chunk-ED6F54AE.cjs.map → chunk-Y2BYHTEN.cjs.map} +1 -1
- package/dist/{chunk-A3FU7F26.js → chunk-ZARYIFCI.js} +2 -2
- package/dist/{chunk-RGEFDRKL.js → chunk-ZIZJDXUI.js} +2 -2
- package/dist/{chunk-WC4CFOYB.js → chunk-ZXASQL3E.js} +2 -2
- package/dist/components/case-studies/index.cjs +9 -9
- package/dist/components/case-studies/index.js +3 -3
- package/dist/components/chat/index.cjs +3 -3
- package/dist/components/chat/index.js +2 -2
- package/dist/components/contact/index.cjs +4 -4
- package/dist/components/contact/index.js +3 -3
- package/dist/components/docs/index.cjs +6 -6
- package/dist/components/docs/index.js +5 -5
- package/dist/components/embeds/claude-embed.d.ts +1 -9
- package/dist/components/embeds/claude-embed.d.ts.map +1 -1
- package/dist/components/embeds/embed-viewer-frame.d.ts +1 -8
- package/dist/components/embeds/embed-viewer-frame.d.ts.map +1 -1
- package/dist/components/embeds/index.cjs +4 -4
- package/dist/components/embeds/index.js +3 -3
- package/dist/components/faq/index.cjs +4 -4
- package/dist/components/faq/index.js +3 -3
- package/dist/components/features/command-box.d.ts +5 -1
- package/dist/components/features/command-box.d.ts.map +1 -1
- package/dist/components/features/index.cjs +3 -3
- package/dist/components/features/index.js +2 -2
- package/dist/components/help-center-pages/index.cjs +23 -23
- package/dist/components/help-center-pages/index.js +14 -14
- package/dist/components/index.cjs +144 -144
- package/dist/components/index.js +12 -12
- package/dist/components/meeting-scheduler/index.cjs +60 -60
- package/dist/components/meeting-scheduler/index.js +4 -4
- package/dist/components/navigation/index.cjs +3 -3
- package/dist/components/navigation/index.js +2 -2
- package/dist/components/onboarding-guides/index.cjs +6 -6
- package/dist/components/onboarding-guides/index.js +5 -5
- package/dist/components/related-content/index.cjs +4 -4
- package/dist/components/related-content/index.js +3 -3
- package/dist/components/tickets/index.cjs +7 -7
- package/dist/components/tickets/index.js +6 -6
- package/dist/components/ui/autocomplete.d.ts +2 -0
- package/dist/components/ui/autocomplete.d.ts.map +1 -1
- package/dist/components/ui/field-wrapper.d.ts +7 -0
- package/dist/components/ui/field-wrapper.d.ts.map +1 -1
- package/dist/components/ui/file-manager/index.cjs +36 -36
- package/dist/components/ui/file-manager/index.js +1 -1
- package/dist/components/ui/index.cjs +3 -3
- package/dist/components/ui/index.js +2 -2
- package/dist/components/ui/markdown/rich/embed-overrides.d.ts.map +1 -1
- package/dist/components/ui/markdown/rich/shortcodes.d.ts.map +1 -1
- package/dist/components/ui/markdown/sanitize.d.ts.map +1 -1
- package/dist/components/ui/select.d.ts +2 -0
- package/dist/components/ui/select.d.ts.map +1 -1
- package/dist/components/ui/tab-selector.d.ts.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.js +2 -2
- package/dist/types/design-doc.d.ts +0 -7
- package/dist/types/design-doc.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/embeds/claude-embed.tsx +7 -38
- package/src/components/embeds/embed-viewer-frame.tsx +4 -78
- package/src/components/features/command-box.tsx +36 -10
- package/src/components/ui/autocomplete.tsx +4 -1
- package/src/components/ui/field-wrapper.tsx +15 -7
- package/src/components/ui/markdown/rich/__tests__/shortcodes.test.ts +0 -42
- package/src/components/ui/markdown/rich/embed-overrides.tsx +0 -1
- package/src/components/ui/markdown/rich/shortcodes.ts +6 -20
- package/src/components/ui/markdown/sanitize.ts +0 -5
- package/src/components/ui/select.tsx +4 -2
- package/src/components/ui/tab-selector.tsx +6 -2
- package/src/types/design-doc.ts +0 -7
- package/dist/chunk-4LHEQMOV.cjs.map +0 -1
- package/dist/chunk-KVCOILLR.js.map +0 -1
- package/dist/chunk-RDKJCZHY.js.map +0 -1
- package/dist/chunk-SWPLMGKK.cjs.map +0 -1
- /package/dist/{chunk-LYP43VO3.js.map → chunk-2LKVYI7H.js.map} +0 -0
- /package/dist/{chunk-PA47Q3JE.js.map → chunk-6TDWCBQM.js.map} +0 -0
- /package/dist/{chunk-OW4OF7UP.js.map → chunk-BZ7B255K.js.map} +0 -0
- /package/dist/{chunk-TN4KUALQ.js.map → chunk-DZ2Z7HRF.js.map} +0 -0
- /package/dist/{chunk-HISA2T34.js.map → chunk-G2PAUMV6.js.map} +0 -0
- /package/dist/{chunk-LRFCOGYE.js.map → chunk-HB3IG2MC.js.map} +0 -0
- /package/dist/{chunk-NRU3AUUP.js.map → chunk-MQQM4NCN.js.map} +0 -0
- /package/dist/{chunk-I5J7RE5E.js.map → chunk-O3JCKEVG.js.map} +0 -0
- /package/dist/{chunk-FXEEH6GY.js.map → chunk-QVQLNKFS.js.map} +0 -0
- /package/dist/{chunk-TCVAF576.js.map → chunk-VEXTUGUA.js.map} +0 -0
- /package/dist/{chunk-A3FU7F26.js.map → chunk-ZARYIFCI.js.map} +0 -0
- /package/dist/{chunk-RGEFDRKL.js.map → chunk-ZIZJDXUI.js.map} +0 -0
- /package/dist/{chunk-WC4CFOYB.js.map → chunk-ZXASQL3E.js.map} +0 -0
|
@@ -19,14 +19,6 @@ export interface ClaudeEmbedProps {
|
|
|
19
19
|
height?: string
|
|
20
20
|
/** iframe loading strategy. Defaults to `lazy`, as `FigmaEmbed` does. */
|
|
21
21
|
loading?: 'eager' | 'lazy'
|
|
22
|
-
/** Self-hosted mirror of the artifact to FRAME instead of claude.ai.
|
|
23
|
-
* claude.ai frame-locks Claude CODE artifact urls entirely and serves
|
|
24
|
-
* public artifacts only to author-allow-listed hosts — when the host
|
|
25
|
-
* app keeps its own serving copy (e.g. the hub's design-briefs store),
|
|
26
|
-
* it passes that path here. The frame shows the mirror; the "Open in
|
|
27
|
-
* Claude" action still targets `url`, so commenting stays on
|
|
28
|
-
* claude.ai. Wins over `toClaudeEmbedUrl(url)` when set. */
|
|
29
|
-
srcOverride?: string | null
|
|
30
22
|
}
|
|
31
23
|
|
|
32
24
|
const KIND_HEADING: Record<ClaudeEmbedKind, string> = {
|
|
@@ -51,16 +43,8 @@ const KIND_HEADING: Record<ClaudeEmbedKind, string> = {
|
|
|
51
43
|
* (no embed route), an artifact whose author has not allow-listed this host,
|
|
52
44
|
* or an http host (mixed content — it will not paint on `http://localhost`).
|
|
53
45
|
*/
|
|
54
|
-
export function ClaudeEmbed({ url, kind = 'artifact', title, height, loading = 'lazy'
|
|
55
|
-
|
|
56
|
-
// '//…' or a full url) — anything else is discarded, because whatever
|
|
57
|
-
// this component frames wears Claude-branded chrome, and every other
|
|
58
|
-
// frame source here is allow-listed (`toClaudeEmbedUrl` pins
|
|
59
|
-
// claude.ai/claude.site). Shape-checked again here even though the
|
|
60
|
-
// shortcode parser applies the same rule: the prop is public API and
|
|
61
|
-
// callers can pass it directly.
|
|
62
|
-
const mirrorSrc = srcOverride && /^\/(?!\/)/.test(srcOverride) ? srcOverride : null
|
|
63
|
-
const embedUrl = mirrorSrc ?? toClaudeEmbedUrl(url)
|
|
46
|
+
export function ClaudeEmbed({ url, kind = 'artifact', title, height, loading = 'lazy' }: ClaudeEmbedProps) {
|
|
47
|
+
const embedUrl = toClaudeEmbedUrl(url)
|
|
64
48
|
return (
|
|
65
49
|
<EmbedViewerFrame
|
|
66
50
|
className="my-6 space-y-3"
|
|
@@ -81,10 +65,6 @@ export function ClaudeEmbed({ url, kind = 'artifact', title, height, loading = '
|
|
|
81
65
|
</Button>
|
|
82
66
|
}
|
|
83
67
|
src={embedUrl}
|
|
84
|
-
// The framed artifact has no internal fullscreen button of its own
|
|
85
|
-
// (unlike Figma's player) — the shell provides the toggle for both
|
|
86
|
-
// the claude.ai `/embed` route and a mirror.
|
|
87
|
-
fullscreenControl
|
|
88
68
|
loading={loading}
|
|
89
69
|
height={height}
|
|
90
70
|
// Same frame contract Figma gets — an artifact is a real app: it copies
|
|
@@ -92,22 +72,11 @@ export function ClaudeEmbed({ url, kind = 'artifact', title, height, loading = '
|
|
|
92
72
|
allow="clipboard-write; clipboard-read; fullscreen"
|
|
93
73
|
// …but UNLIKE Figma, an artifact is USER-AUTHORED HTML and JS, so it is
|
|
94
74
|
// sandboxed. Omitting `allow-top-navigation` is the point: an artifact
|
|
95
|
-
// cannot navigate the page it is embedded in.
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
// needs it (storage, postMessage handshake).
|
|
101
|
-
// - a `srcOverride` mirror is served from the HOST's OWN origin —
|
|
102
|
-
// `allow-same-origin` + `allow-scripts` there would be a no-op
|
|
103
|
-
// sandbox handing the artifact first-party cookies and
|
|
104
|
-
// `window.parent`. A static mirror renders fine from an opaque
|
|
105
|
-
// origin, so the token is dropped.
|
|
106
|
-
sandbox={
|
|
107
|
-
mirrorSrc
|
|
108
|
-
? 'allow-scripts allow-popups allow-forms'
|
|
109
|
-
: 'allow-scripts allow-same-origin allow-popups allow-forms'
|
|
110
|
-
}
|
|
75
|
+
// cannot navigate the page it is embedded in. `allow-same-origin` beside
|
|
76
|
+
// `allow-scripts` is what Anthropic's own embed snippet uses and is safe
|
|
77
|
+
// on a CROSS-ORIGIN frame — it grants the frame claude.ai's origin, never
|
|
78
|
+
// ours.
|
|
79
|
+
sandbox="allow-scripts allow-same-origin allow-popups allow-forms"
|
|
111
80
|
allowFullScreen
|
|
112
81
|
emptyIcon={<ClaudeIcon className="w-16 h-16 mb-4" />}
|
|
113
82
|
emptyMessage="Open this one in Claude · it has no embeddable view"
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import React
|
|
4
|
-
import { Maximize2, Minimize2 } from 'lucide-react'
|
|
3
|
+
import React from 'react'
|
|
5
4
|
import { cn } from '../../utils/cn'
|
|
6
|
-
import { Button } from '../ui/button/button'
|
|
7
5
|
import { EmbedIframe } from './embed-iframe'
|
|
8
6
|
|
|
9
7
|
/**
|
|
@@ -49,13 +47,6 @@ export interface EmbedViewerFrameProps {
|
|
|
49
47
|
sandbox?: string
|
|
50
48
|
/** Wrapper classes; defaults to the sheets/pdf `space-y-4`. */
|
|
51
49
|
className?: string
|
|
52
|
-
/** Opt-in fullscreen toggle in the header row (rendered after `actions`,
|
|
53
|
-
* only when there is a `src`). Fullscreens the WHOLE frame — header
|
|
54
|
-
* included — so the exit control stays reachable (Esc works too).
|
|
55
|
-
* Exists because some embedded contents ship their own fullscreen
|
|
56
|
-
* button (the Figma player) and some cannot (a self-hosted Claude
|
|
57
|
-
* artifact mirror) — the shell provides the affordance uniformly. */
|
|
58
|
-
fullscreenControl?: boolean
|
|
59
50
|
}
|
|
60
51
|
|
|
61
52
|
export function EmbedViewerFrame({
|
|
@@ -72,51 +63,9 @@ export function EmbedViewerFrame({
|
|
|
72
63
|
loading,
|
|
73
64
|
sandbox,
|
|
74
65
|
className,
|
|
75
|
-
fullscreenControl,
|
|
76
66
|
}: EmbedViewerFrameProps) {
|
|
77
|
-
const frameRef = useRef<HTMLDivElement>(null)
|
|
78
|
-
const [isFullscreen, setIsFullscreen] = useState(false)
|
|
79
|
-
|
|
80
|
-
// Track the DOCUMENT's fullscreen element rather than local intent —
|
|
81
|
-
// Esc, F11, and programmatic exits all land here, so the icon can
|
|
82
|
-
// never desync from reality.
|
|
83
|
-
useEffect(() => {
|
|
84
|
-
if (!fullscreenControl) return
|
|
85
|
-
const onChange = () => setIsFullscreen(document.fullscreenElement === frameRef.current)
|
|
86
|
-
document.addEventListener('fullscreenchange', onChange)
|
|
87
|
-
return () => document.removeEventListener('fullscreenchange', onChange)
|
|
88
|
-
}, [fullscreenControl])
|
|
89
|
-
|
|
90
|
-
const toggleFullscreen = useCallback(() => {
|
|
91
|
-
const el = frameRef.current
|
|
92
|
-
if (!el) return
|
|
93
|
-
// Denials arrive BOTH ways: `requestFullscreen` throws synchronously
|
|
94
|
-
// on some engines and returns a REJECTED promise on others (e.g. the
|
|
95
|
-
// Fullscreen permissions policy when this component is itself inside
|
|
96
|
-
// an embedder's iframe without `allow="fullscreen"`). Swallow both —
|
|
97
|
-
// the button is a convenience; an unhandled rejection is not.
|
|
98
|
-
try {
|
|
99
|
-
if (document.fullscreenElement === el) {
|
|
100
|
-
document.exitFullscreen().catch(() => {})
|
|
101
|
-
} else {
|
|
102
|
-
el.requestFullscreen().catch(() => {})
|
|
103
|
-
}
|
|
104
|
-
} catch {
|
|
105
|
-
// Fullscreen API unavailable (old WebKit) — fail silently.
|
|
106
|
-
}
|
|
107
|
-
}, [])
|
|
108
|
-
|
|
109
67
|
return (
|
|
110
|
-
<div
|
|
111
|
-
ref={frameRef}
|
|
112
|
-
className={cn(
|
|
113
|
-
'space-y-4',
|
|
114
|
-
className,
|
|
115
|
-
// Fullscreen paints its own ground (the fullscreened element
|
|
116
|
-
// otherwise sits on the UA's black backdrop) and scrolls itself.
|
|
117
|
-
isFullscreen && 'overflow-auto bg-ods-bg p-[var(--spacing-system-mf)]',
|
|
118
|
-
)}
|
|
119
|
-
>
|
|
68
|
+
<div className={cn('space-y-4', className)}>
|
|
120
69
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
121
70
|
<div className="flex items-center gap-2 min-w-0">
|
|
122
71
|
{icon}
|
|
@@ -126,36 +75,13 @@ export function EmbedViewerFrame({
|
|
|
126
75
|
<span className="text-h6 font-semibold text-ods-text-primary truncate">{title}</span>
|
|
127
76
|
)}
|
|
128
77
|
</div>
|
|
129
|
-
{
|
|
130
|
-
and every viewer's own action: full-width stacked on mobile,
|
|
131
|
-
inline row from `sm:` up, SAME Button idiom (outline /
|
|
132
|
-
small-legacy / w-4 icon / w-full sm:w-auto). Without the
|
|
133
|
-
fullscreen toggle, `actions` renders EXACTLY as before — the
|
|
134
|
-
wrapper exists only when the toggle joins it, so pre-existing
|
|
135
|
-
viewers (figma/pdf/sheets) are byte-identical. */}
|
|
136
|
-
{fullscreenControl && src ? (
|
|
137
|
-
<div className="flex flex-col gap-[var(--spacing-system-xsf)] sm:flex-row sm:items-center">
|
|
138
|
-
{actions}
|
|
139
|
-
<Button
|
|
140
|
-
variant="outline"
|
|
141
|
-
size="small-legacy"
|
|
142
|
-
onClick={toggleFullscreen}
|
|
143
|
-
aria-label={isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'}
|
|
144
|
-
leftIcon={isFullscreen ? <Minimize2 className="w-4 h-4" /> : <Maximize2 className="w-4 h-4" />}
|
|
145
|
-
className="w-full sm:w-auto"
|
|
146
|
-
>
|
|
147
|
-
{isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}
|
|
148
|
-
</Button>
|
|
149
|
-
</div>
|
|
150
|
-
) : (
|
|
151
|
-
actions
|
|
152
|
-
)}
|
|
78
|
+
{actions}
|
|
153
79
|
</div>
|
|
154
80
|
{src ? (
|
|
155
81
|
<EmbedIframe
|
|
156
82
|
src={src}
|
|
157
83
|
title={title}
|
|
158
|
-
height={
|
|
84
|
+
height={height}
|
|
159
85
|
allow={allow}
|
|
160
86
|
allowFullScreen={allowFullScreen}
|
|
161
87
|
loading={loading}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React from 'react'
|
|
4
4
|
import { Button } from '../ui/button'
|
|
5
|
+
import { Copy02Icon } from '../icons-v2-generated/documents/copy-02-icon'
|
|
5
6
|
import { cn } from '../../utils/cn'
|
|
6
7
|
|
|
7
8
|
export interface CommandBoxAction {
|
|
@@ -34,6 +35,10 @@ export interface CommandBoxProps {
|
|
|
34
35
|
commandClassName?: string
|
|
35
36
|
/** Maximum lines to show (uses line-clamp, 0 for unlimited) */
|
|
36
37
|
maxLines?: number
|
|
38
|
+
/** When set, shows a copy icon button in the top-right corner of the box */
|
|
39
|
+
onCopy?: () => void
|
|
40
|
+
/** Accessible label for the corner copy button */
|
|
41
|
+
copyAriaLabel?: string
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
/**
|
|
@@ -86,11 +91,26 @@ export function CommandBox({
|
|
|
86
91
|
secondaryAction,
|
|
87
92
|
className,
|
|
88
93
|
commandClassName,
|
|
89
|
-
maxLines = 0
|
|
94
|
+
maxLines = 0,
|
|
95
|
+
onCopy,
|
|
96
|
+
copyAriaLabel = 'Copy command'
|
|
90
97
|
}: CommandBoxProps) {
|
|
91
98
|
// Get static line-clamp class or undefined for unlimited
|
|
92
99
|
const lineClampClass = maxLines > 0 ? lineClampClasses[maxLines] : undefined
|
|
93
100
|
|
|
101
|
+
const commandText = (
|
|
102
|
+
<div
|
|
103
|
+
className={cn(
|
|
104
|
+
'text-ods-text-primary text-code break-all',
|
|
105
|
+
onCopy && 'flex-1 min-w-0',
|
|
106
|
+
lineClampClass,
|
|
107
|
+
commandClassName
|
|
108
|
+
)}
|
|
109
|
+
>
|
|
110
|
+
{command}
|
|
111
|
+
</div>
|
|
112
|
+
)
|
|
113
|
+
|
|
94
114
|
return (
|
|
95
115
|
<div className={cn('flex flex-col gap-2', className)}>
|
|
96
116
|
{title && (
|
|
@@ -99,15 +119,21 @@ export function CommandBox({
|
|
|
99
119
|
</div>
|
|
100
120
|
)}
|
|
101
121
|
<div className="bg-ods-bg border border-ods-border rounded-[6px] p-4">
|
|
102
|
-
|
|
103
|
-
className=
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
122
|
+
{onCopy ? (
|
|
123
|
+
<div className="flex items-start gap-4">
|
|
124
|
+
{commandText}
|
|
125
|
+
<button
|
|
126
|
+
type="button"
|
|
127
|
+
onClick={onCopy}
|
|
128
|
+
aria-label={copyAriaLabel}
|
|
129
|
+
className="shrink-0 rounded-md text-ods-text-secondary transition-colors hover:text-ods-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ods-focus"
|
|
130
|
+
>
|
|
131
|
+
<Copy02Icon className="w-5 h-5" />
|
|
132
|
+
</button>
|
|
133
|
+
</div>
|
|
134
|
+
) : (
|
|
135
|
+
commandText
|
|
136
|
+
)}
|
|
111
137
|
{(primaryAction || secondaryAction) && (
|
|
112
138
|
<div className="flex flex-col md:flex-row gap-3 md:justify-end mt-4">
|
|
113
139
|
{secondaryAction && (
|
|
@@ -58,6 +58,8 @@ interface AutocompleteBaseProps<T = string> {
|
|
|
58
58
|
freeSolo?: boolean
|
|
59
59
|
/** Label for the input */
|
|
60
60
|
label?: string
|
|
61
|
+
/** Label scale forwarded to FieldWrapper ("large" = text-h4 for designs with body-scale field titles) */
|
|
62
|
+
labelVariant?: "default" | "large"
|
|
61
63
|
/** Error message displayed below the field */
|
|
62
64
|
error?: string
|
|
63
65
|
/** Custom filter function */
|
|
@@ -143,6 +145,7 @@ function AutocompleteInner<T = string>(
|
|
|
143
145
|
dropdownClassName,
|
|
144
146
|
freeSolo = false,
|
|
145
147
|
label,
|
|
148
|
+
labelVariant,
|
|
146
149
|
error,
|
|
147
150
|
filterOptions,
|
|
148
151
|
renderOption,
|
|
@@ -685,7 +688,7 @@ function AutocompleteInner<T = string>(
|
|
|
685
688
|
)
|
|
686
689
|
|
|
687
690
|
return (
|
|
688
|
-
<FieldWrapper label={label} error={error} className={className}>
|
|
691
|
+
<FieldWrapper label={label} labelVariant={labelVariant} error={error} className={className}>
|
|
689
692
|
<div className="relative" ref={containerRef}>
|
|
690
693
|
{popover}
|
|
691
694
|
|
|
@@ -27,6 +27,13 @@ export interface FieldWrapperProps {
|
|
|
27
27
|
error?: string
|
|
28
28
|
/** Color variant for the message: "error" (red), "warning" (yellow), "success" (green) or "muted" (grey) */
|
|
29
29
|
errorVariant?: "error" | "warning" | "success" | "muted"
|
|
30
|
+
/**
|
|
31
|
+
* Label scale. Default is the standard form-label scale (text-h6).
|
|
32
|
+
* "large" (text-h4) is for screens whose design specifies body-scale field
|
|
33
|
+
* titles (e.g. the devices New Device page) — opt in per call site, the
|
|
34
|
+
* default stays the converged small scale.
|
|
35
|
+
*/
|
|
36
|
+
labelVariant?: "default" | "large"
|
|
30
37
|
/** Additional className for the outer wrapper */
|
|
31
38
|
className?: string
|
|
32
39
|
children: React.ReactNode
|
|
@@ -40,18 +47,19 @@ const errorVariantClasses = {
|
|
|
40
47
|
} as const
|
|
41
48
|
|
|
42
49
|
const FieldWrapper = React.forwardRef<HTMLDivElement, FieldWrapperProps>(
|
|
43
|
-
({ label, htmlFor, error, errorVariant = "error", className, children }, ref) => {
|
|
50
|
+
({ label, htmlFor, error, errorVariant = "error", labelVariant = "default", className, children }, ref) => {
|
|
44
51
|
const hasChrome = label != null || error != null
|
|
45
52
|
|
|
46
53
|
return (
|
|
47
54
|
<div ref={ref} className={cn(hasChrome ? "relative flex w-full flex-col" : "contents", className)}>
|
|
48
55
|
{label && (
|
|
49
|
-
// text-h6, NOT text-h4: `Field` (ui/field.tsx) renders its
|
|
50
|
-
// `Label variant="small"` = text-h6, and every form that
|
|
51
|
-
// Field-wrapped controls with `label=`-prop controls showed two
|
|
52
|
-
// sizes side by side — the exact inconsistency the admin
|
|
53
|
-
// reporting. One label scale, owned here and in Field
|
|
54
|
-
|
|
56
|
+
// Default is text-h6, NOT text-h4: `Field` (ui/field.tsx) renders its
|
|
57
|
+
// label via `Label variant="small"` = text-h6, and every form that
|
|
58
|
+
// mixes Field-wrapped controls with `label=`-prop controls showed two
|
|
59
|
+
// label sizes side by side — the exact inconsistency the admin
|
|
60
|
+
// editors kept reporting. One label scale, owned here and in Field
|
|
61
|
+
// together; `labelVariant` is the per-design opt-out.
|
|
62
|
+
<Label className="mb-1" htmlFor={htmlFor} variant={labelVariant}>
|
|
55
63
|
{label}
|
|
56
64
|
</Label>
|
|
57
65
|
)}
|
|
@@ -91,48 +91,6 @@ describe('processShortcodes design-doc embeds', () => {
|
|
|
91
91
|
expect(design).toContain('data-kind="design"');
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
it('threads the optional title and mirror segments into data attributes', () => {
|
|
95
|
-
const titled = processShortcodes('{{claude-artifact:https://claude.ai/a|Brief doc}}');
|
|
96
|
-
expect(titled).toContain('data-title="Brief doc"');
|
|
97
|
-
expect(titled).not.toContain('data-embed-src');
|
|
98
|
-
|
|
99
|
-
const mirrored = processShortcodes(
|
|
100
|
-
'{{claude-artifact:https://claude.ai/a|Brief doc|/api/storage/view/design-briefs/x.html}}',
|
|
101
|
-
);
|
|
102
|
-
expect(mirrored).toContain('data-title="Brief doc"');
|
|
103
|
-
expect(mirrored).toContain('data-embed-src="/api/storage/view/design-briefs/x.html"');
|
|
104
|
-
|
|
105
|
-
// Untitled mirror: the empty title slot is skipped, the mirror still rides.
|
|
106
|
-
const untitled = processShortcodes(
|
|
107
|
-
'{{claude-artifact:https://claude.ai/a||/api/storage/view/design-briefs/x.html}}',
|
|
108
|
-
);
|
|
109
|
-
expect(untitled).not.toContain('data-title');
|
|
110
|
-
expect(untitled).toContain('data-embed-src="/api/storage/view/design-briefs/x.html"');
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('recognizes the mirror segment by path SHAPE, never by pipe position', () => {
|
|
114
|
-
// A pre-existing title containing `|` stays a title — nothing is framed.
|
|
115
|
-
const pipeTitle = processShortcodes('{{claude-artifact:https://claude.ai/a|Design | v2}}');
|
|
116
|
-
expect(pipeTitle).toContain('data-title="Design | v2"');
|
|
117
|
-
expect(pipeTitle).not.toContain('data-embed-src');
|
|
118
|
-
|
|
119
|
-
// Pipe title AND a mirror: the path-shaped tail is the mirror, the rest is the title.
|
|
120
|
-
const both = processShortcodes(
|
|
121
|
-
'{{claude-artifact:https://claude.ai/a|Design | v2|/api/storage/view/design-briefs/x.html}}',
|
|
122
|
-
);
|
|
123
|
-
expect(both).toContain('data-title="Design | v2"');
|
|
124
|
-
expect(both).toContain('data-embed-src="/api/storage/view/design-briefs/x.html"');
|
|
125
|
-
|
|
126
|
-
// Non-path tails (absolute urls, protocol-relative, schemes) are NEVER a
|
|
127
|
-
// frame source — they fold into the title as plain text.
|
|
128
|
-
const evil = processShortcodes('{{claude-artifact:https://claude.ai/a|T|https://evil.example}}');
|
|
129
|
-
expect(evil).not.toContain('data-embed-src');
|
|
130
|
-
const protoRelative = processShortcodes('{{claude-artifact:https://claude.ai/a|T|//evil.example}}');
|
|
131
|
-
expect(protoRelative).not.toContain('data-embed-src');
|
|
132
|
-
const scheme = processShortcodes('{{claude-artifact:https://claude.ai/a|T|javascript:alert(1)}}');
|
|
133
|
-
expect(scheme).not.toContain('data-embed-src');
|
|
134
|
-
});
|
|
135
|
-
|
|
136
94
|
it('escapes the url attribute so a crafted link cannot break out of the block', () => {
|
|
137
95
|
const output = processShortcodes('{{claude-artifact:https://claude.ai/a"><script>alert(1)</script>}}');
|
|
138
96
|
expect(output).not.toContain('<script>');
|
|
@@ -68,26 +68,12 @@ export const processShortcodes = (content: string): string => {
|
|
|
68
68
|
// Claude artifact / Claude Design: {{claude-artifact:URL}} / {{claude-design:URL}}
|
|
69
69
|
// — the same shape as figma, so a Claude link is a markdown BLOCK wherever
|
|
70
70
|
// markdown renders, never a bespoke card one surface hand-assembles.
|
|
71
|
-
// `{{claude-artifact:URL}}`
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// serving copy thread it through here and there is ONE Claude mount for
|
|
78
|
-
// rails and prose alike). The mirror segment is recognized by SHAPE — a
|
|
79
|
-
// same-origin absolute path (`/…`, not `//…`) — never by position, so a
|
|
80
|
-
// pre-existing title containing `|` keeps rendering as a title instead of
|
|
81
|
-
// being silently swallowed as a bogus frame source.
|
|
82
|
-
.replace(/\{\{claude-(artifact|design):([^}]+)\}\}/g, (match, kind, rest) => {
|
|
83
|
-
const parts = String(rest).split('|');
|
|
84
|
-
const url = parts[0];
|
|
85
|
-
const last = parts.length > 1 ? parts[parts.length - 1].trim() : '';
|
|
86
|
-
const embedSrc = /^\/(?!\/)/.test(last) ? parts.pop()!.trim() : '';
|
|
87
|
-
const title = parts.slice(1).join('|').trim();
|
|
88
|
-
const titleAttr = title ? ` data-title="${escapeAttr(title)}"` : '';
|
|
89
|
-
const embedSrcAttr = embedSrc ? ` data-embed-src="${escapeAttr(embedSrc)}"` : '';
|
|
90
|
-
return `\n\n<div class="claude-embed" data-url="${escapeAttr(url.trim())}" data-kind="${kind}"${titleAttr}${embedSrcAttr}></div>\n\n`;
|
|
71
|
+
// `{{claude-artifact:URL}}` or `{{claude-artifact:URL|Name}}` — the optional
|
|
72
|
+
// name after the pipe is the ONLY source of a real title (claude.ai serves
|
|
73
|
+
// one og:title for every artifact), so the block carries it through.
|
|
74
|
+
.replace(/\{\{claude-(artifact|design):([^|}]+)(?:\|([^}]*))?\}\}/g, (match, kind, url, title) => {
|
|
75
|
+
const titleAttr = title && title.trim() ? ` data-title="${escapeAttr(title.trim())}"` : '';
|
|
76
|
+
return `\n\n<div class="claude-embed" data-url="${escapeAttr(url.trim())}" data-kind="${kind}"${titleAttr}></div>\n\n`;
|
|
91
77
|
})
|
|
92
78
|
// LinkedIn embeds: {{linkedin:POST_URL}}
|
|
93
79
|
.replace(/\{\{linkedin:([^}]+)\}\}/g, (match, url) => {
|
|
@@ -395,11 +395,6 @@ const URL_ATTRS = new Set([
|
|
|
395
395
|
'data',
|
|
396
396
|
'action',
|
|
397
397
|
'background',
|
|
398
|
-
// The claude shortcode's mirror path — it becomes an <iframe src>
|
|
399
|
-
// (`ClaudeEmbed srcOverride`), so it gets the same scheme check as
|
|
400
|
-
// every other URL sink even though the component ALSO shape-validates
|
|
401
|
-
// it to a same-origin path (defense in depth).
|
|
402
|
-
'data-embed-src',
|
|
403
398
|
])
|
|
404
399
|
|
|
405
400
|
/**
|
|
@@ -20,9 +20,11 @@ const SelectTrigger = React.forwardRef<
|
|
|
20
20
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & {
|
|
21
21
|
invalid?: boolean
|
|
22
22
|
label?: string
|
|
23
|
+
/** Label scale forwarded to FieldWrapper ("large" = text-h4 for designs with body-scale field titles) */
|
|
24
|
+
labelVariant?: "default" | "large"
|
|
23
25
|
error?: string
|
|
24
26
|
}
|
|
25
|
-
>(({ className, children, invalid, label, error, ...props }, ref) => {
|
|
27
|
+
>(({ className, children, invalid, label, labelVariant, error, ...props }, ref) => {
|
|
26
28
|
const isInvalid = invalid || !!error
|
|
27
29
|
|
|
28
30
|
const trigger = (
|
|
@@ -68,7 +70,7 @@ const SelectTrigger = React.forwardRef<
|
|
|
68
70
|
)
|
|
69
71
|
|
|
70
72
|
return (
|
|
71
|
-
<FieldWrapper label={label} error={error}>
|
|
73
|
+
<FieldWrapper label={label} labelVariant={labelVariant} error={error}>
|
|
72
74
|
{trigger}
|
|
73
75
|
</FieldWrapper>
|
|
74
76
|
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import React from 'react'
|
|
4
|
+
import { Label } from './label'
|
|
4
5
|
import { cn } from '../../utils/cn'
|
|
5
6
|
|
|
6
7
|
export interface TabSelectorItem {
|
|
@@ -59,9 +60,12 @@ export function TabSelector({
|
|
|
59
60
|
aria-disabled={disabled || undefined}
|
|
60
61
|
>
|
|
61
62
|
{label && (
|
|
62
|
-
|
|
63
|
+
// Label for family/color; "large" (text-h4) is this component's
|
|
64
|
+
// long-standing label scale — neighbours opt into the same scale via
|
|
65
|
+
// their `labelVariant="large"` when a design puts them side by side.
|
|
66
|
+
<Label variant="large">
|
|
63
67
|
{label}
|
|
64
|
-
</
|
|
68
|
+
</Label>
|
|
65
69
|
)}
|
|
66
70
|
<div
|
|
67
71
|
className={cn(
|
package/src/types/design-doc.ts
CHANGED
|
@@ -141,13 +141,6 @@ export interface DesignDocLink {
|
|
|
141
141
|
title: string | null
|
|
142
142
|
display_order: number | null
|
|
143
143
|
created_at: string
|
|
144
|
-
/** View-proxy path of THIS link's self-hosted artifact mirror
|
|
145
|
-
* (`/api/storage/view/design-briefs/<artifact-uuid>.html`) when one
|
|
146
|
-
* exists — resolved per artifact id from the link's url, so two
|
|
147
|
-
* claude links on one doc each frame their OWN mirror. Transparent
|
|
148
|
-
* frame source for `ClaudeEmbed srcOverride` (claude.ai cannot be
|
|
149
|
-
* iframed). Null / absent = no mirror ingested for that artifact. */
|
|
150
|
-
embed_src?: string | null
|
|
151
144
|
/**
|
|
152
145
|
* A `roadmap_task` link, hydrated from the mirror as the ROADMAP item it is —
|
|
153
146
|
* so it renders with the roadmap card, the same as `/roadmap` and the chat.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-4LHEQMOV.cjs","../src/components/ui/label.tsx","../src/components/ui/field-wrapper.tsx","../src/components/ui/input.tsx","../src/components/ui/checkbox.tsx","../src/components/ui/dropdown-menu.tsx","../src/components/ui/portal-container.tsx","../src/components/ui/skeleton.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-4LHEQMOV.cjs","sourcesContent":[null,"\"use client\"\n\nimport * as React from \"react\"\nimport * as LabelPrimitive from \"@radix-ui/react-label\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"../../utils/cn\"\n\n/**\n * THE form label. One treatment, stated once:\n *\n * - Scale + color live in the BASE (`text-h6 text-ods-text-primary`), so call\n * sites never restate them — before this, ~100 call sites carried a\n * `className=\"text-ods-text-primary\"` override and ~115 cargo-culted\n * `variant=\"small\"` because the base under-specified.\n * - NO margin. Layout belongs to the CONTAINER (`Field`'s gap, a `space-y-2`\n * wrapper) — a margin inside a text primitive stacks with container gaps,\n * which is exactly how `Field` rows ended up 12px label→control while the\n * hand-rolled rows were 8px, and how labels sat 2px off their inline badges\n * in `items-center` rows. Opt into `spacing` ONLY when the label truly has\n * no spacing container.\n *\n * Raw `<label>` elements remain correct for CLICK-SURFACES (a field's adorned\n * chrome, checkbox/radio rows, upload dropzones) — those are interaction\n * wrappers, not text labels, and must not inherit this typography.\n */\nconst labelVariants = cva(\n \"block text-h6 text-ods-text-primary\",\n {\n variants: {\n variant: {\n default: \"\",\n /** @deprecated alias of `default` — the three small scales converged on text-h6. */\n small: \"\",\n /** @deprecated alias of `default`. */\n medium: \"\",\n large: \"text-h4\"\n },\n spacing: {\n none: \"\",\n tight: \"mb-0.5\",\n normal: \"mb-2\",\n loose: \"mb-3\"\n }\n },\n defaultVariants: {\n variant: \"default\",\n spacing: \"none\"\n }\n }\n)\n\nconst Label = React.forwardRef<\n React.ElementRef<typeof LabelPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>\n>(({ className, variant, spacing, ...props }, ref) => (\n <LabelPrimitive.Root\n ref={ref}\n className={cn(labelVariants({ variant, spacing }), className)}\n {...props}\n />\n))\nLabel.displayName = LabelPrimitive.Root.displayName\n\nexport { Label }\n","\"use client\"\n\nimport { Label } from './label'\nimport * as React from \"react\"\nimport { cn } from \"../../utils/cn\"\n\nexport interface FieldWrapperProps {\n /** Label text displayed above the field */\n label?: string\n /**\n * `id` of the control the label names. Without it the label is decoration:\n * clicking it focuses nothing and assistive tech reads the field unnamed,\n * which is why every caller that cared was rendering its own <Label> instead\n * of using `label` here.\n */\n htmlFor?: string\n /**\n * Status message displayed below the field, on ONE line, ellipsized on\n * overflow (full text via the native `title`).\n *\n * It is positioned OUT OF FLOW (absolute, hanging below the wrapper), so it\n * never changes the field's height when it appears — the same treatment\n * `CheckboxBlock` uses. The trade is that it overlaps whatever sits directly\n * below: a form stacking fields needs at least ~20px of vertical gap (e.g.\n * `gap-[var(--spacing-system-lf)]`) for the message to land in clear space.\n */\n error?: string\n /** Color variant for the message: \"error\" (red), \"warning\" (yellow), \"success\" (green) or \"muted\" (grey) */\n errorVariant?: \"error\" | \"warning\" | \"success\" | \"muted\"\n /** Additional className for the outer wrapper */\n className?: string\n children: React.ReactNode\n}\n\nconst errorVariantClasses = {\n error: \"text-ods-error\",\n warning: \"text-ods-warning\",\n success: \"text-ods-success\",\n muted: \"text-ods-text-secondary\",\n} as const\n\nconst FieldWrapper = React.forwardRef<HTMLDivElement, FieldWrapperProps>(\n ({ label, htmlFor, error, errorVariant = \"error\", className, children }, ref) => {\n const hasChrome = label != null || error != null\n\n return (\n <div ref={ref} className={cn(hasChrome ? \"relative flex w-full flex-col\" : \"contents\", className)}>\n {label && (\n // text-h6, NOT text-h4: `Field` (ui/field.tsx) renders its label via\n // `Label variant=\"small\"` = text-h6, and every form that mixes\n // Field-wrapped controls with `label=`-prop controls showed two label\n // sizes side by side — the exact inconsistency the admin editors kept\n // reporting. One label scale, owned here and in Field together.\n <Label className=\"mb-1\" htmlFor={htmlFor}>\n {label}\n </Label>\n )}\n {children}\n {error && (\n <p className={cn(\"absolute bottom-0 left-0 right-0 translate-y-full text-h6 truncate\", errorVariantClasses[errorVariant])} title={error}>\n {error}\n </p>\n )}\n </div>\n )\n }\n)\nFieldWrapper.displayName = \"FieldWrapper\"\n\nexport { FieldWrapper }\n","\"use client\"\n\nimport * as React from \"react\";\n\nimport { Loader2 } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport { FieldWrapper } from \"./field-wrapper\";\n\nexport interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {\n /** When true, renders error border & ring */\n invalid?: boolean;\n /** Element displayed at the start (left) of the input */\n startAdornment?: React.ReactNode;\n /** Element displayed at the end (right) of the input */\n endAdornment?: React.ReactNode;\n /** Label text displayed above the input */\n label?: string;\n /** Status message displayed below the input */\n error?: string;\n /** Color variant for the message: \"error\" (red), \"warning\" (yellow), \"success\" (green) or \"muted\" (grey).\n Only \"error\" and \"warning\" mark the field invalid (colored border). */\n errorVariant?: \"error\" | \"warning\" | \"success\" | \"muted\";\n /** When true, shows a loading spinner as end adornment */\n loading?: boolean;\n}\n\nconst invalidBorderClasses = {\n error: \"border-ods-error hover:border-ods-error has-[:focus]:border-ods-error\",\n warning: \"!border-ods-warning hover:!border-ods-warning has-[:focus]:!border-ods-warning\",\n} as const;\n\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n ({ className, type, invalid = false, startAdornment, endAdornment, label, error, errorVariant = \"error\", loading = false, ...props }, ref) => {\n // success/muted are informational — they never paint the invalid border\n const variantIsInvalid = errorVariant === \"error\" || errorVariant === \"warning\"\n const isInvalid = invalid || (!!error && variantIsInvalid)\n\n // Range inputs get a clean slider rendering — no label wrapper, borders, or adornments\n if (type === 'range') {\n const rangeInput = (\n <input\n type=\"range\"\n className={cn(\n \"w-full cursor-pointer appearance-none rounded-full bg-white/30 h-1\",\n // Webkit (Chrome/Safari) thumb\n \"[&::-webkit-slider-thumb]:appearance-none\",\n \"[&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3\",\n \"[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-white\",\n \"[&::-webkit-slider-thumb]:cursor-pointer [&::-webkit-slider-thumb]:shadow-sm\",\n // Firefox thumb\n \"[&::-moz-range-thumb]:w-3 [&::-moz-range-thumb]:h-3\",\n \"[&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:bg-white\",\n \"[&::-moz-range-thumb]:border-0 [&::-moz-range-thumb]:cursor-pointer\",\n // Firefox track\n \"[&::-moz-range-track]:bg-transparent\",\n // Disabled\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n ref={ref}\n {...props}\n />\n )\n return label ? (\n <FieldWrapper label={label} error={error} errorVariant={errorVariant}>\n {rangeInput}\n </FieldWrapper>\n ) : rangeInput\n }\n\n const content = (\n <label\n data-invalid={isInvalid || undefined}\n className={cn(\n // Layout & spacing\n \"flex w-full items-center gap-2 rounded-[6px] border px-3 h-11 md:h-12 cursor-text\",\n // Focus-within states\n \"has-[:focus-visible]:outline-none\",\n \"group\",\n // Animations & touch UX\n \"transition-colors duration-200\",\n // Theme palette\n \"bg-ods-card border-ods-border has-[:focus]:border-ods-accent\",\n // Hover & active (not disabled)\n !props.disabled && \"hover:bg-ods-bg-hover hover:border-ods-border-hover active:bg-ods-bg-active active:border-ods-border-active\",\n // Disabled. The adornments (and any icon inside them) carry their own\n // `text-ods-text-secondary`, so grey them from here — a disabled field\n // must read as one flat colour, not grey text next to a live icon.\n // Scoped to the DIRECT span children (the two adornment wrappers): a\n // descendant rule would also repaint whatever the caller renders INSIDE\n // an adornment, which is passed verbatim precisely so it can own its\n // colour (a `Tag` there went invisible against its own fill).\n props.disabled && \"!cursor-not-allowed bg-ods-bg\",\n \"has-[:disabled]:[&>span]:text-ods-text-disabled has-[:disabled]:[&_svg]:text-ods-text-disabled\",\n // Invalid\n isInvalid && invalidBorderClasses[variantIsInvalid ? (errorVariant as \"error\" | \"warning\") : \"error\"],\n className\n )}\n >\n {startAdornment && (\n <span className=\"text-h6 flex-shrink-0 text-ods-text-secondary transition-colors duration-200 group-has-[:focus]:text-ods-accent group-data-[invalid]:text-ods-error [&_svg]:size-4 md:[&_svg]:size-6\">\n {startAdornment}\n </span>\n )}\n <input\n type={type}\n className={cn(\n // Layout\n \"flex-1 min-w-0 bg-transparent border-none outline-none\",\n // Typography\n \"text-h4\",\n // Colors\n \"text-ods-text-primary placeholder:text-ods-text-secondary\",\n // File input adjustments\n \"file:border-0 file:bg-transparent\",\n // Disabled\n \"disabled:cursor-not-allowed disabled:text-ods-text-disabled disabled:placeholder:text-ods-border\",\n // Touch\n \"touch-manipulation\",\n // Autofill override\n \"[&:-webkit-autofill]:[-webkit-box-shadow:0_0_0_9999px_transparent_inset] [&:-webkit-autofill]:[-webkit-text-fill-color:var(--color-text-primary)] [&:-webkit-autofill]:[caret-color:var(--color-text-primary)] [&:-webkit-autofill]:[transition:background-color_9999s_ease-in-out_0s]\"\n )}\n ref={ref}\n {...props}\n />\n {loading && (\n <Loader2 className=\"animate-spin flex-shrink-0 text-ods-text-secondary size-4 md:size-6\" />\n )}\n {!loading && endAdornment && (\n <span className=\"text-h6 flex-shrink-0 text-ods-text-secondary transition-colors duration-200 group-has-[:focus]:text-ods-accent group-data-[invalid]:text-ods-error [&_svg]:size-4 md:[&_svg]:size-6\">\n {endAdornment}\n </span>\n )}\n </label>\n )\n\n return (\n <FieldWrapper label={label} error={error} errorVariant={errorVariant}>\n {content}\n </FieldWrapper>\n )\n }\n)\nInput.displayName = \"Input\"\n\nexport { Input };\n","\"use client\"\n\nimport * as React from \"react\"\nimport * as CheckboxPrimitive from \"@radix-ui/react-checkbox\"\n\nimport { cn } from \"../../utils/cn\"\nimport { CheckboxCheckmarkIcon } from \"../icons-v2-generated/signs-and-symbols/checkbox-checkmark-icon\"\n\nconst Checkbox = React.forwardRef<\n React.ElementRef<typeof CheckboxPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>\n>(({ className, ...props }, ref) => (\n <CheckboxPrimitive.Root\n ref={ref}\n className={cn(\n \"peer h-4 w-4 shrink-0 rounded-sm border border-ods-border bg-ods-card focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-ods-accent data-[state=checked]:border-ods-accent\",\n className\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n className={cn(\"flex items-center justify-center text-ods-text-on-accent\")}\n >\n <CheckboxCheckmarkIcon size={10} />\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n))\nCheckbox.displayName = CheckboxPrimitive.Root.displayName\n\nexport { Checkbox }\n","\"use client\"\n\nimport * as React from \"react\"\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimport { Check, ChevronRight, Circle } from \"lucide-react\"\n\nimport { cn } from \"../../utils/cn\"\nimport {\n COLLISION_PADDING_PX,\n useCollisionBoundary,\n usePortalContainer,\n} from \"./portal-container\"\n\nconst DropdownMenu = DropdownMenuPrimitive.Root\n\nconst DropdownMenuTrigger = DropdownMenuPrimitive.Trigger\n\nconst DropdownMenuGroup = DropdownMenuPrimitive.Group\n\nconst DropdownMenuPortal = DropdownMenuPrimitive.Portal\n\nconst DropdownMenuSub = DropdownMenuPrimitive.Sub\n\nconst DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean\n }\n>(({ className, inset, children, ...props }, ref) => (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n className={cn(\n \"flex cursor-pointer gap-2 select-none items-center rounded-sm px-2 py-1.5 text-h6 outline-none hover:bg-ods-bg-hover focus:bg-ods-bg-hover data-[state=open]:bg-ods-bg-hover [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n inset && \"pl-8\",\n className\n )}\n {...props}\n >\n {children}\n <ChevronRight className=\"ml-auto\" />\n </DropdownMenuPrimitive.SubTrigger>\n))\nDropdownMenuSubTrigger.displayName =\n DropdownMenuPrimitive.SubTrigger.displayName\n\nconst DropdownMenuSubContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, collisionBoundary, collisionPadding, hideWhenDetached, ...props }, ref) => {\n // Same container as the root content — `DropdownMenuContent` portals, and a\n // submenu left un-portaled inherits whatever clipping/stacking context its\n // parent sits in instead of the one the host published. (The hand-rolled\n // submenu in `actions-menu` already portals; this keeps the shared primitive\n // consistent with both.)\n const container = usePortalContainer()\n // Same boundary as the root content — a submenu must not escape the surface\n // its parent menu is confined to.\n const boundary = useCollisionBoundary()\n const resolvedBoundary = collisionBoundary ?? boundary ?? undefined\n return (\n <DropdownMenuPrimitive.Portal container={container ?? undefined}>\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n collisionBoundary={resolvedBoundary}\n collisionPadding={collisionPadding ?? (resolvedBoundary ? COLLISION_PADDING_PX : undefined)}\n // Only defaulted ON where a boundary was published (i.e. inside a panel\n // that owns its menus) — see `DropdownMenuContent`.\n hideWhenDetached={hideWhenDetached ?? (resolvedBoundary ? true : undefined)}\n className={cn(\n \"z-50 min-w-[8rem] overflow-hidden rounded-md border border-ods-border bg-ods-card p-1 text-ods-text-primary shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n className\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n )\n})\nDropdownMenuSubContent.displayName =\n DropdownMenuPrimitive.SubContent.displayName\n\nconst DropdownMenuContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>\n>(({\n className,\n sideOffset = 4,\n collisionBoundary,\n collisionPadding,\n hideWhenDetached,\n ...props\n}, ref) => {\n // Portal into the active container (e.g. a drawer) so the menu inherits its\n // stacking context; falls back to `document.body` when none is provided.\n const container = usePortalContainer()\n // Keep flip/shift inside the surface that owns the menu. Without this Radix\n // collides against the VIEWPORT, so a menu opened near the top of the chat\n // panel grows upward past the panel and lands over the app header. An\n // explicit prop still wins; no provider → viewport, Radix's own default.\n const boundary = useCollisionBoundary()\n const resolvedBoundary = collisionBoundary ?? boundary ?? undefined\n return (\n <DropdownMenuPrimitive.Portal container={container ?? undefined}>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n collisionBoundary={resolvedBoundary}\n collisionPadding={collisionPadding ?? (resolvedBoundary ? COLLISION_PADDING_PX : undefined)}\n // A menu tracks its trigger for as long as it is open. When the trigger\n // scrolls out of its clipping ancestor — a chat card carried away by the\n // thread's auto-scroll while the menu is open — Radix keeps the menu\n // pinned to that off-screen anchor, so it slides to the surface edge and\n // reads as a detached popup floating over unrelated chrome.\n // `hideWhenDetached` makes it disappear with its trigger instead.\n //\n // Defaulted ON only where a boundary was published. This is a SHARED\n // primitive: flipping it on globally would change dismissal behavior for\n // every dropdown in every consumer, and the detached-popup problem it\n // solves is specific to a menu living inside a scrolling panel.\n hideWhenDetached={hideWhenDetached ?? (resolvedBoundary ? true : undefined)}\n className={cn(\n \"z-50 min-w-[8rem] overflow-hidden rounded-md border border-ods-border bg-ods-card p-1 text-ods-text-primary shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n className\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n )\n})\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName\n\nconst DropdownMenuItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {\n inset?: boolean\n }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-h6 outline-none transition-colors text-ods-text-primary hover:bg-ods-bg-hover hover:text-ods-text-primary focus:bg-ods-bg-hover focus:text-ods-text-primary data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n inset && \"pl-8\",\n className\n )}\n {...props}\n />\n))\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>\n>(({ className, children, checked, ...props }, ref) => (\n <DropdownMenuPrimitive.CheckboxItem\n ref={ref}\n className={cn(\n \"relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-h6 outline-none transition-colors text-ods-text-primary hover:bg-ods-bg-hover hover:text-ods-text-primary focus:bg-ods-bg-hover focus:text-ods-text-primary data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className\n )}\n checked={checked}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <Check className=\"h-4 w-4\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n))\nDropdownMenuCheckboxItem.displayName =\n DropdownMenuPrimitive.CheckboxItem.displayName\n\nconst DropdownMenuRadioItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n <DropdownMenuPrimitive.RadioItem\n ref={ref}\n className={cn(\n \"relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-h6 outline-none transition-colors text-ods-text-primary hover:bg-ods-bg-hover hover:text-ods-text-primary focus:bg-ods-bg-hover focus:text-ods-text-primary data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <Circle className=\"h-2 w-2 fill-ods-accent\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.RadioItem>\n))\nDropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName\n\nconst DropdownMenuLabel = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {\n inset?: boolean\n }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Label\n ref={ref}\n className={cn(\n \"px-2 py-1.5 text-h6 font-semibold text-ods-text-secondary\",\n inset && \"pl-8\",\n className\n )}\n {...props}\n />\n))\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName\n\nconst DropdownMenuSeparator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-ods-border\", className)}\n {...props}\n />\n))\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName\n\nconst DropdownMenuShortcut = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLSpanElement>) => {\n return (\n <span\n className={cn(\"ml-auto text-h6 text-ods-text-secondary opacity-60\", className)}\n {...props}\n />\n )\n}\nDropdownMenuShortcut.displayName = \"DropdownMenuShortcut\"\n\nexport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuRadioItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuGroup,\n DropdownMenuPortal,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuRadioGroup,\n}\n","'use client'\n\nimport * as React from 'react'\n\n/**\n * The DOM node that Radix overlays (dropdown menus, tooltips) should portal\n * into, instead of the default `document.body`.\n *\n * Portaling to `document.body` lifts content out of every local stacking\n * context, which forces overlays opened *inside* a high-z surface (e.g. the\n * chat drawer) to escalate their own z-index to compete at the document\n * root. Pointing the portal at a node *inside* that surface instead\n * lets the content inherit the surface's stacking context — small, local\n * z-indices then \"just work\" and no escalation is needed. Radix positions\n * content with `strategy: \"fixed\"`, so it still escapes ancestor\n * `overflow: hidden` clipping regardless of where it is portaled.\n *\n * Default `null` → Radix falls back to `document.body` (unchanged behaviour\n * everywhere a provider isn't present).\n */\nexport const PortalContainerContext = React.createContext<HTMLElement | null>(\n null,\n)\n\n/** Read the active portal container (or `null` to use `document.body`). */\nexport function usePortalContainer(): HTMLElement | null {\n return React.useContext(PortalContainerContext)\n}\n\n/**\n * The element Radix overlays should treat as their COLLISION boundary — the\n * surface they must stay inside of.\n *\n * Separate from {@link PortalContainerContext} on purpose: the portal target is\n * a `display: contents` node (no box, so it can't be measured), while a\n * collision boundary must be a real, laid-out element.\n *\n * Why it matters: Radix positions overlays `position: fixed` and, by default,\n * flips/shifts them against the VIEWPORT. A menu opened near the top of a\n * panel therefore happily grows upward past the panel's edge and lands on top\n * of the app header — visually detached from the surface it belongs to. Handing\n * Radix the panel element instead keeps flip/shift (and the\n * `--radix-popper-available-height` a menu sizes itself by) inside that panel.\n *\n * Default `null` → viewport, i.e. Radix's own default, unchanged everywhere a\n * provider isn't present.\n */\nexport const CollisionBoundaryContext = React.createContext<HTMLElement | null>(\n null,\n)\n\n/** Read the active collision boundary (or `null` for the viewport). */\nexport function useCollisionBoundary(): HTMLElement | null {\n return React.useContext(CollisionBoundaryContext)\n}\n\n/** Padding (px) kept between an overlay and its boundary edge. Matches the\n * panel's own gutter so a shifted menu never sits flush against the edge. */\nexport const COLLISION_PADDING_PX = 8\n","\"use client\"\n\nimport * as React from 'react'\nimport { cn } from \"../../utils/cn\"\n\ninterface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {\n className?: string\n}\n\nconst Skeleton = React.forwardRef<HTMLDivElement, SkeletonProps>(\n ({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={cn(\n \"animate-pulse rounded-md bg-ods-border\",\n className\n )}\n {...props}\n />\n )\n }\n)\nSkeleton.displayName = 'Skeleton'\n\ninterface SkeletonTextProps extends React.HTMLAttributes<HTMLDivElement> {\n lines?: number\n className?: string\n}\n\nconst SkeletonText = React.forwardRef<HTMLDivElement, SkeletonTextProps>(\n ({ lines = 1, className, ...props }, ref) => {\n return (\n <div ref={ref} className={cn(\"space-y-2\", className)} {...props}>\n {Array.from({ length: lines }).map((_, i) => (\n <Skeleton \n key={i} \n className={cn(\n \"h-4\",\n i === lines - 1 && lines > 1 && \"w-3/4\" // Last line shorter for multi-line\n )} \n />\n ))}\n </div>\n )\n }\n)\nSkeletonText.displayName = 'SkeletonText'\n\ninterface SkeletonCardProps extends React.HTMLAttributes<HTMLDivElement> {\n showImage?: boolean\n className?: string\n}\n\nconst SkeletonCard = React.forwardRef<HTMLDivElement, SkeletonCardProps>(\n ({ showImage = false, className, ...props }, ref) => {\n return (\n <div \n ref={ref}\n className={cn(\n \"rounded-lg border border-ods-border overflow-hidden\",\n className\n )}\n {...props}\n >\n {showImage && (\n <Skeleton className=\"h-48 w-full\" />\n )}\n <div className=\"p-6\">\n <Skeleton className=\"h-6 w-3/4 mb-4\" />\n <SkeletonText lines={3} />\n </div>\n </div>\n )\n }\n)\nSkeletonCard.displayName = 'SkeletonCard'\n\ninterface SkeletonGridProps extends React.HTMLAttributes<HTMLDivElement> {\n columns?: number\n items?: number\n showImages?: boolean\n className?: string\n}\n\nconst SkeletonGrid = React.forwardRef<HTMLDivElement, SkeletonGridProps>(\n ({ columns = 3, items = 6, showImages = false, className, ...props }, ref) => {\n return (\n <div \n ref={ref}\n className={cn(\n `grid grid-cols-1 gap-6`,\n columns === 2 && \"md:grid-cols-2\",\n columns === 3 && \"md:grid-cols-3\",\n columns === 4 && \"md:grid-cols-4\",\n className\n )}\n {...props}\n >\n {Array.from({ length: items }).map((_, i) => (\n <SkeletonCard key={i} showImage={showImages} />\n ))}\n </div>\n )\n }\n)\nSkeletonGrid.displayName = 'SkeletonGrid'\n\ninterface SkeletonButtonProps extends React.HTMLAttributes<HTMLDivElement> {\n size?: 'sm' | 'default' | 'lg'\n className?: string\n}\n\nconst SkeletonButton = React.forwardRef<HTMLDivElement, SkeletonButtonProps>(\n ({ size = 'default', className, ...props }, ref) => {\n const sizeClasses = {\n sm: 'h-8 w-20',\n default: 'h-10 w-32',\n lg: 'h-12 w-40'\n }\n \n return (\n <Skeleton \n ref={ref}\n className={cn(\n \"rounded-lg\",\n sizeClasses[size],\n className\n )}\n {...props}\n />\n )\n }\n)\nSkeletonButton.displayName = 'SkeletonButton'\n\ninterface SkeletonHeadingProps extends React.HTMLAttributes<HTMLDivElement> {\n level?: 1 | 2 | 3 | 4 | 5 | 6\n className?: string\n}\n\nconst SkeletonHeading = React.forwardRef<HTMLDivElement, SkeletonHeadingProps>(\n ({ level = 1, className, ...props }, ref) => {\n const heightClasses = {\n 1: 'h-12',\n 2: 'h-10',\n 3: 'h-8',\n 4: 'h-7',\n 5: 'h-6',\n 6: 'h-5'\n }\n \n return (\n <Skeleton \n ref={ref}\n className={cn(\n heightClasses[level],\n \"w-3/4\",\n className\n )}\n {...props}\n />\n )\n }\n)\nSkeletonHeading.displayName = 'SkeletonHeading'\n\ninterface SkeletonListProps extends React.HTMLAttributes<HTMLDivElement> {\n items?: number\n className?: string\n}\n\nconst SkeletonList = React.forwardRef<HTMLDivElement, SkeletonListProps>(\n ({ items = 5, className, ...props }, ref) => {\n return (\n <div ref={ref} className={cn(\"space-y-3\", className)} {...props}>\n {Array.from({ length: items }).map((_, i) => (\n <div key={i} className=\"flex items-center gap-3\">\n <Skeleton className=\"h-10 w-10 rounded-full flex-shrink-0\" />\n <div className=\"flex-1\">\n <Skeleton className=\"h-4 w-1/3 mb-1\" />\n <Skeleton className=\"h-3 w-1/2\" />\n </div>\n </div>\n ))}\n </div>\n )\n }\n)\nSkeletonList.displayName = 'SkeletonList'\n\ninterface SkeletonNavigationProps extends React.HTMLAttributes<HTMLDivElement> {\n items?: number\n className?: string\n}\n\nconst SkeletonNavigation = React.forwardRef<HTMLDivElement, SkeletonNavigationProps>(\n ({ items = 6, className, ...props }, ref) => {\n return (\n <div ref={ref} className={cn(\"flex items-center gap-6\", className)} {...props}>\n {Array.from({ length: items }).map((_, i) => (\n <Skeleton key={i} className=\"h-5 w-20\" />\n ))}\n </div>\n )\n }\n)\nSkeletonNavigation.displayName = 'SkeletonNavigation'\n\nexport { \n Skeleton,\n SkeletonText,\n SkeletonCard,\n SkeletonGrid,\n SkeletonButton,\n SkeletonHeading,\n SkeletonList,\n SkeletonNavigation\n}"]}
|