banhaten 0.4.0 → 0.6.0
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/CHANGELOG.md +78 -1
- package/CONTRIBUTING.md +6 -4
- package/README.md +4 -2
- package/docs/design-spacing.md +13 -0
- package/docs/design-system/README.md +2 -0
- package/docs/design-system/appearances/default.md +1 -1
- package/docs/design-system/appearances/rounded.md +1 -1
- package/docs/design-system/appearances/sharp.md +1 -1
- package/docs/design-system/component-showcase-consistency-report.md +5 -5
- package/docs/design-system/components/README.md +15 -0
- package/docs/design-system/components/accordion.md +1 -1
- package/docs/design-system/components/activity-feed.md +1 -1
- package/docs/design-system/components/activity-heatmap.md +1 -1
- package/docs/design-system/components/agent-input.md +85 -0
- package/docs/design-system/components/alert-dialog.md +1 -1
- package/docs/design-system/components/alert.md +10 -4
- package/docs/design-system/components/app-header.md +97 -0
- package/docs/design-system/components/approval-card.md +134 -0
- package/docs/design-system/components/attribute.md +14 -2
- package/docs/design-system/components/avatar.md +1 -1
- package/docs/design-system/components/badge.md +1 -1
- package/docs/design-system/components/banner.md +1 -1
- package/docs/design-system/components/breadcrumbs.md +8 -3
- package/docs/design-system/components/button-group.md +1 -1
- package/docs/design-system/components/button.md +8 -5
- package/docs/design-system/components/card.md +15 -1
- package/docs/design-system/components/catalog-components.md +1 -1
- package/docs/design-system/components/chart.md +95 -24
- package/docs/design-system/components/checkbox.md +1 -1
- package/docs/design-system/components/collapsible.md +20 -6
- package/docs/design-system/components/command.md +1 -1
- package/docs/design-system/components/context-menu.md +1 -1
- package/docs/design-system/components/date-picker.md +1 -1
- package/docs/design-system/components/file-upload.md +1 -1
- package/docs/design-system/components/hover-card.md +1 -1
- package/docs/design-system/components/inline-citations.md +93 -0
- package/docs/design-system/components/input-group.md +1 -1
- package/docs/design-system/components/input.md +59 -3
- package/docs/design-system/components/item.md +1 -1
- package/docs/design-system/components/kbd.md +6 -4
- package/docs/design-system/components/menu.md +35 -4
- package/docs/design-system/components/menubar.md +1 -1
- package/docs/design-system/components/modal.md +2 -2
- package/docs/design-system/components/native-select.md +1 -1
- package/docs/design-system/components/navigation-menu.md +1 -1
- package/docs/design-system/components/onboarding-step-list-item.md +1 -1
- package/docs/design-system/components/page-header.md +58 -17
- package/docs/design-system/components/pagination.md +2 -2
- package/docs/design-system/components/popover.md +1 -1
- package/docs/design-system/components/progress-slider.md +1 -1
- package/docs/design-system/components/radio-card.md +1 -1
- package/docs/design-system/components/radio-group.md +1 -1
- package/docs/design-system/components/resizable.md +1 -1
- package/docs/design-system/components/rolling-count.md +61 -0
- package/docs/design-system/components/scroll-area.md +1 -1
- package/docs/design-system/components/select.md +12 -2
- package/docs/design-system/components/sheet.md +1 -1
- package/docs/design-system/components/shimmer-text.md +67 -0
- package/docs/design-system/components/sidebar.md +75 -8
- package/docs/design-system/components/skeleton.md +2 -2
- package/docs/design-system/components/slideout.md +1 -1
- package/docs/design-system/components/slider.md +2 -2
- package/docs/design-system/components/social-button.md +2 -2
- package/docs/design-system/components/streaming-text.md +82 -0
- package/docs/design-system/components/table.md +103 -21
- package/docs/design-system/components/tabs.md +25 -6
- package/docs/design-system/components/tag.md +1 -1
- package/docs/design-system/components/task-list.md +95 -0
- package/docs/design-system/components/textarea.md +1 -1
- package/docs/design-system/components/thinking-reasoning.md +72 -0
- package/docs/design-system/components/thinking-state.md +67 -0
- package/docs/design-system/components/timeline.md +1 -1
- package/docs/design-system/components/toast.md +1 -1
- package/docs/design-system/components/toggle.md +2 -2
- package/docs/design-system/components/toolbar.md +11 -6
- package/docs/design-system/components/tooltip.md +6 -4
- package/docs/design-system/density-and-elevation.md +16 -16
- package/docs/design-system/design-system-test-missing-items.md +8 -8
- package/docs/design-system/installation.md +2 -0
- package/docs/design-system/tabs.md +2 -1
- package/docs/design-system/template-gap-audit.md +32 -0
- package/docs/design-system/templates.md +59 -0
- package/docs/design-system/token-governance.md +15 -2
- package/package.json +2 -2
- package/registry/components/accordion.tsx +2 -2
- package/registry/components/agent-input-parts.tsx +552 -0
- package/registry/components/agent-input-state.ts +156 -0
- package/registry/components/agent-input.css +307 -0
- package/registry/components/agent-input.tsx +382 -0
- package/registry/components/alert-dialog.tsx +2 -2
- package/registry/components/alert.tsx +23 -7
- package/registry/components/app-header.tsx +151 -0
- package/registry/components/approval-card.css +409 -0
- package/registry/components/approval-card.tsx +401 -0
- package/registry/components/approval-frame.tsx +108 -0
- package/registry/components/approval-questions.tsx +559 -0
- package/registry/components/attribute.tsx +48 -13
- package/registry/components/autocomplete.tsx +15 -68
- package/registry/components/avatar.tsx +2 -2
- package/registry/components/badge.tsx +8 -7
- package/registry/components/button-group.tsx +2 -2
- package/registry/components/button.tsx +19 -14
- package/registry/components/card.tsx +18 -5
- package/registry/components/chart-block-data.ts +177 -0
- package/registry/components/chart-block.css +171 -0
- package/registry/components/chart-block.tsx +260 -0
- package/registry/components/chart-interactions.ts +123 -0
- package/registry/components/chart-options.cartesian.ts +162 -0
- package/registry/components/chart-options.paint.ts +99 -0
- package/registry/components/chart-options.parts.ts +72 -0
- package/registry/components/chart-options.radial.ts +109 -0
- package/registry/components/chart-options.ts +44 -0
- package/registry/components/chart-options.types.ts +67 -0
- package/registry/components/chart.tsx +1 -1
- package/registry/components/checkbox.tsx +6 -6
- package/registry/components/collapsible.tsx +43 -3
- package/registry/components/command.tsx +1 -1
- package/registry/components/context-menu.tsx +1 -1
- package/registry/components/data-chart.tsx +40 -0
- package/registry/components/date-picker.tsx +9 -9
- package/registry/components/echart-theme.ts +246 -0
- package/registry/components/echart.css +49 -0
- package/registry/components/echart.tsx +178 -0
- package/registry/components/expanded/Breadcrumbs.tsx +5 -1
- package/registry/components/expanded/PageHeader.tsx +67 -24
- package/registry/components/expanded/Table.tsx +60 -22
- package/registry/components/expanded/activityFeed.css +2 -2
- package/registry/components/expanded/activityHeatmap.css +1 -1
- package/registry/components/expanded/banner.css +2 -2
- package/registry/components/expanded/breadcrumbs.css +19 -2
- package/registry/components/expanded/catalogComponentsShowcase.css +1 -1
- package/registry/components/expanded/fileUpload.css +7 -7
- package/registry/components/expanded/onboardingStepListItem.css +2 -2
- package/registry/components/expanded/pageHeader.css +38 -44
- package/registry/components/expanded/slideout.css +4 -4
- package/registry/components/expanded/table.css +58 -9
- package/registry/components/expanded/timeline.css +1 -1
- package/registry/components/hover-card.tsx +1 -1
- package/registry/components/inline-citations.css +126 -0
- package/registry/components/inline-citations.tsx +292 -0
- package/registry/components/input-group.tsx +4 -4
- package/registry/components/input.tsx +150 -137
- package/registry/components/item.tsx +1 -1
- package/registry/components/kbd.tsx +3 -2
- package/registry/components/menu.tsx +117 -12
- package/registry/components/menubar.tsx +2 -2
- package/registry/components/modal.tsx +3 -3
- package/registry/components/native-select.tsx +4 -4
- package/registry/components/navigation-menu.tsx +2 -2
- package/registry/components/pagination.tsx +4 -4
- package/registry/components/popover.tsx +3 -3
- package/registry/components/progress-slider.tsx +1 -1
- package/registry/components/radio-card.tsx +7 -7
- package/registry/components/radio-group.tsx +2 -2
- package/registry/components/resizable.tsx +2 -2
- package/registry/components/rolling-count.css +37 -0
- package/registry/components/rolling-count.tsx +110 -0
- package/registry/components/scroll-area.tsx +1 -1
- package/registry/components/select.tsx +15 -10
- package/registry/components/sheet.tsx +2 -2
- package/registry/components/shimmer-text.css +69 -0
- package/registry/components/shimmer-text.tsx +43 -0
- package/registry/components/sidebar.tsx +528 -223
- package/registry/components/skeleton.tsx +1 -1
- package/registry/components/slider.tsx +2 -2
- package/registry/components/social-button.tsx +1 -1
- package/registry/components/streaming-text.css +45 -0
- package/registry/components/streaming-text.tsx +165 -0
- package/registry/components/table-elements.tsx +164 -5
- package/registry/components/tabs.css +32 -4
- package/registry/components/tabs.tsx +5 -1
- package/registry/components/tag.tsx +1 -1
- package/registry/components/task-list.css +328 -0
- package/registry/components/task-list.tsx +429 -0
- package/registry/components/textarea.tsx +4 -4
- package/registry/components/thinking-reasoning.css +178 -0
- package/registry/components/thinking-reasoning.tsx +253 -0
- package/registry/components/thinking-state.css +8 -0
- package/registry/components/thinking-state.tsx +46 -0
- package/registry/components/toast.tsx +1 -1
- package/registry/components/toggle.tsx +2 -2
- package/registry/components/toolbar.tsx +6 -6
- package/registry/components/tooltip.tsx +6 -4
- package/registry/examples/agent-input-demo.tsx +90 -0
- package/registry/examples/app-header-demo.tsx +40 -0
- package/registry/examples/approval-card-demo.tsx +83 -0
- package/registry/examples/button-demo.tsx +3 -0
- package/registry/examples/chart-block-demo.tsx +5 -0
- package/registry/examples/chart-demo.tsx +6 -21
- package/registry/examples/collection-filter-bar-demo.tsx +1 -1
- package/registry/examples/control-density-contract-demo.tsx +5 -1
- package/registry/examples/inline-citations-demo.tsx +31 -0
- package/registry/examples/input-actions-demo.tsx +36 -0
- package/registry/examples/menu-demo.tsx +1 -1
- package/registry/examples/popover-demo.tsx +1 -0
- package/registry/examples/rolling-count-demo.tsx +39 -0
- package/registry/examples/shimmer-text-demo.tsx +20 -0
- package/registry/examples/streaming-text-demo.tsx +28 -0
- package/registry/examples/task-list-demo.tsx +74 -0
- package/registry/examples/thinking-reasoning-demo.tsx +74 -0
- package/registry/examples/thinking-state-demo.tsx +13 -0
- package/registry/guidance/charts.LICENSE.txt +24 -0
- package/registry/hooks/use-controllable-state.ts +33 -0
- package/registry/hooks/use-listbox-navigation.ts +73 -0
- package/registry/hooks/use-motion-end.ts +55 -0
- package/registry/hooks/use-reduced-motion.ts +28 -0
- package/registry/index.json +1808 -190
- package/registry/styles/globals.css +992 -1088
- package/registry.json +385 -10
- package/schema/registry.schema.json +5 -1
- package/schema/tokens.schema.json +8 -4
- package/src/cli/file-transaction.js +15 -8
- package/src/cli/index.js +22 -29
- package/tokens/banhaten.tokens.json +490 -192
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { ArrowUpIcon } from "lucide-react"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
import { Button } from "@/components/ui/button"
|
|
8
|
+
import { ShimmerText } from "@/components/ui/shimmer-text"
|
|
9
|
+
import { Spinner } from "@/components/ui/spinner"
|
|
10
|
+
import { useControllableState } from "@/hooks/use-controllable-state"
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
AddMenu,
|
|
14
|
+
ChipRow,
|
|
15
|
+
SlashPalette,
|
|
16
|
+
useChipPresence,
|
|
17
|
+
useEnhancePill,
|
|
18
|
+
useFilePicker,
|
|
19
|
+
usePromptEnhancement,
|
|
20
|
+
useSlashPalette,
|
|
21
|
+
} from "./agent-input-parts"
|
|
22
|
+
import {
|
|
23
|
+
attachmentKind,
|
|
24
|
+
chipsOf,
|
|
25
|
+
withoutSlashToken,
|
|
26
|
+
type AgentInputAttachment,
|
|
27
|
+
type AgentInputLabels,
|
|
28
|
+
type AgentInputModel,
|
|
29
|
+
type AgentInputPayload,
|
|
30
|
+
type AgentInputSkill,
|
|
31
|
+
} from "./agent-input-state"
|
|
32
|
+
|
|
33
|
+
import "./agent-input.css"
|
|
34
|
+
|
|
35
|
+
type AgentInputProps = Omit<
|
|
36
|
+
React.ComponentProps<"div">,
|
|
37
|
+
"defaultValue" | "onSubmit"
|
|
38
|
+
> & {
|
|
39
|
+
/** Controlled attachment chips. Omit to let the field own picked files. */
|
|
40
|
+
attachments?: readonly AgentInputAttachment[]
|
|
41
|
+
defaultModel?: string
|
|
42
|
+
defaultSelectedSkills?: readonly string[]
|
|
43
|
+
defaultValue?: string
|
|
44
|
+
disabled?: boolean
|
|
45
|
+
labels?: Partial<AgentInputLabels>
|
|
46
|
+
model?: string
|
|
47
|
+
models?: readonly AgentInputModel[]
|
|
48
|
+
onAttachmentsChange?: (attachments: AgentInputAttachment[]) => void
|
|
49
|
+
/** Enables the Enhance Prompt action. Resolve to the rewritten prompt. */
|
|
50
|
+
onEnhance?: (prompt: string, signal: AbortSignal) => Promise<string>
|
|
51
|
+
onModelChange?: (model: string) => void
|
|
52
|
+
onSelectedSkillsChange?: (skills: string[]) => void
|
|
53
|
+
onSend?: (payload: AgentInputPayload) => void
|
|
54
|
+
onValueChange?: (value: string) => void
|
|
55
|
+
selectedSkills?: readonly string[]
|
|
56
|
+
skills?: readonly AgentInputSkill[]
|
|
57
|
+
value?: string
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const defaultLabels: AgentInputLabels = {
|
|
61
|
+
addFiles: "Attach files",
|
|
62
|
+
addMenu: "Add attachment or switch model",
|
|
63
|
+
addPhotos: "Add photos",
|
|
64
|
+
enhance: "Enhance prompt",
|
|
65
|
+
enhancing: "Enhancing prompt",
|
|
66
|
+
model: "Model",
|
|
67
|
+
noSkills: "No matching skills",
|
|
68
|
+
placeholder: "Ask AI Agent",
|
|
69
|
+
removeAttachment: (name) => `Remove ${name}`,
|
|
70
|
+
removeSkill: (name) => `Remove ${name}`,
|
|
71
|
+
revert: "Revert",
|
|
72
|
+
send: "Send",
|
|
73
|
+
skills: "Skills",
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Prompt composer for agent conversations: attachments, skills, model choice,
|
|
78
|
+
* a slash-command palette, and prompt enhancement inside one field.
|
|
79
|
+
*/
|
|
80
|
+
const AgentInput = React.forwardRef<HTMLDivElement, AgentInputProps>(
|
|
81
|
+
function AgentInput(
|
|
82
|
+
{
|
|
83
|
+
attachments: controlledAttachments,
|
|
84
|
+
className,
|
|
85
|
+
defaultModel,
|
|
86
|
+
defaultSelectedSkills = [],
|
|
87
|
+
defaultValue = "",
|
|
88
|
+
disabled = false,
|
|
89
|
+
labels: labelOverrides,
|
|
90
|
+
model: controlledModel,
|
|
91
|
+
models = [],
|
|
92
|
+
onAttachmentsChange,
|
|
93
|
+
onEnhance,
|
|
94
|
+
onModelChange,
|
|
95
|
+
onSelectedSkillsChange,
|
|
96
|
+
onSend,
|
|
97
|
+
onValueChange,
|
|
98
|
+
selectedSkills: controlledSkills,
|
|
99
|
+
skills = [],
|
|
100
|
+
value: controlledValue,
|
|
101
|
+
...props
|
|
102
|
+
},
|
|
103
|
+
ref
|
|
104
|
+
) {
|
|
105
|
+
const labels = { ...defaultLabels, ...labelOverrides }
|
|
106
|
+
const [value, setValue] = useControllableState({
|
|
107
|
+
defaultValue,
|
|
108
|
+
onChange: onValueChange,
|
|
109
|
+
value: controlledValue,
|
|
110
|
+
})
|
|
111
|
+
const [selectedModel, setModel] = useControllableState({
|
|
112
|
+
defaultValue: defaultModel ?? "",
|
|
113
|
+
onChange: onModelChange,
|
|
114
|
+
value: controlledModel,
|
|
115
|
+
})
|
|
116
|
+
// An empty id means nothing is picked yet. Fall back to the first model at
|
|
117
|
+
// read time so a list that arrives after mount still yields a selection
|
|
118
|
+
// in the payload and the menu.
|
|
119
|
+
const model = selectedModel || models[0]?.id
|
|
120
|
+
// State stays readonly; the change callbacks receive fresh arrays.
|
|
121
|
+
const [attachments, setAttachments] = useControllableState<
|
|
122
|
+
readonly AgentInputAttachment[]
|
|
123
|
+
>({
|
|
124
|
+
defaultValue: [],
|
|
125
|
+
onChange: (next) => onAttachmentsChange?.([...next]),
|
|
126
|
+
value: controlledAttachments,
|
|
127
|
+
})
|
|
128
|
+
const [selectedSkills, setSelectedSkills] = useControllableState<readonly string[]>({
|
|
129
|
+
defaultValue: defaultSelectedSkills,
|
|
130
|
+
onChange: (next) => onSelectedSkillsChange?.([...next]),
|
|
131
|
+
value: controlledSkills,
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
const fieldRef = React.useRef<HTMLTextAreaElement>(null)
|
|
135
|
+
const frameRef = React.useRef<HTMLDivElement>(null)
|
|
136
|
+
const nextId = React.useRef(1)
|
|
137
|
+
// Differs per mount, so picked ids stay unique when the field remounts
|
|
138
|
+
// while the host still holds earlier attachments.
|
|
139
|
+
const attachmentIdPrefix = React.useId()
|
|
140
|
+
|
|
141
|
+
const focusField = () => window.requestAnimationFrame(() => fieldRef.current?.focus())
|
|
142
|
+
// The editor unmounts while enhancing, so focus returns to it afterwards
|
|
143
|
+
// unless the user has meanwhile moved to a control outside the field.
|
|
144
|
+
const refocusField = () => {
|
|
145
|
+
const active = document.activeElement
|
|
146
|
+
if (active && active !== document.body && !frameRef.current?.contains(active)) return
|
|
147
|
+
focusField()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const slash = useSlashPalette(skills)
|
|
151
|
+
const enhancement = usePromptEnhancement({
|
|
152
|
+
frameRef,
|
|
153
|
+
onEnhance,
|
|
154
|
+
onFinish: refocusField,
|
|
155
|
+
onStart: slash.close,
|
|
156
|
+
setValue,
|
|
157
|
+
value,
|
|
158
|
+
})
|
|
159
|
+
const hasText = value.trim().length > 0
|
|
160
|
+
const sendActive = hasText && !enhancement.enhancing && !disabled
|
|
161
|
+
const pill = useEnhancePill(
|
|
162
|
+
Boolean(onEnhance) && hasText && !enhancement.enhancing && !disabled,
|
|
163
|
+
enhancement.enhancing
|
|
164
|
+
)
|
|
165
|
+
const presence = useChipPresence(chipsOf(selectedSkills, attachments))
|
|
166
|
+
const picker = useFilePicker((files, pickerKind) => {
|
|
167
|
+
setAttachments([
|
|
168
|
+
...attachments,
|
|
169
|
+
...files.map((file) => ({
|
|
170
|
+
file,
|
|
171
|
+
id: `${attachmentIdPrefix}-${nextId.current++}`,
|
|
172
|
+
kind: attachmentKind(file, pickerKind),
|
|
173
|
+
name: file.name,
|
|
174
|
+
})),
|
|
175
|
+
])
|
|
176
|
+
focusField()
|
|
177
|
+
})
|
|
178
|
+
const skillName = (id: string) =>
|
|
179
|
+
skills.find((skill) => skill.id === id)?.name ?? id
|
|
180
|
+
|
|
181
|
+
// Auto-grow the editor with its content up to the token max height.
|
|
182
|
+
React.useLayoutEffect(() => {
|
|
183
|
+
const field = fieldRef.current
|
|
184
|
+
if (!field) return
|
|
185
|
+
field.style.height = "auto"
|
|
186
|
+
field.style.height = `${field.scrollHeight}px`
|
|
187
|
+
}, [value, enhancement.enhancing])
|
|
188
|
+
|
|
189
|
+
// Picking from the palette swaps the "/query" token for a skill chip.
|
|
190
|
+
const applySlash = (skill: AgentInputSkill) => {
|
|
191
|
+
const field = fieldRef.current
|
|
192
|
+
const token = slash.token.current
|
|
193
|
+
if (field && token) {
|
|
194
|
+
setValue(withoutSlashToken(value, token))
|
|
195
|
+
window.requestAnimationFrame(() => {
|
|
196
|
+
field.focus()
|
|
197
|
+
field.setSelectionRange(token.start, token.start)
|
|
198
|
+
})
|
|
199
|
+
}
|
|
200
|
+
if (!selectedSkills.includes(skill.id)) {
|
|
201
|
+
setSelectedSkills([...selectedSkills, skill.id])
|
|
202
|
+
}
|
|
203
|
+
slash.close()
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const removeChip = (key: string) => {
|
|
207
|
+
const chip = presence.leave(key)
|
|
208
|
+
if (chip?.kind === "skill") {
|
|
209
|
+
setSelectedSkills(selectedSkills.filter((id) => id !== chip.skillId))
|
|
210
|
+
} else if (chip?.kind === "attachment") {
|
|
211
|
+
setAttachments(attachments.filter((item) => item.id !== chip.attachment.id))
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const toggleSkill = (skillId: string) => {
|
|
216
|
+
if (selectedSkills.includes(skillId)) {
|
|
217
|
+
removeChip(`skill-${skillId}`)
|
|
218
|
+
return
|
|
219
|
+
}
|
|
220
|
+
setSelectedSkills([...selectedSkills, skillId])
|
|
221
|
+
focusField()
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const send = () => {
|
|
225
|
+
if (!sendActive) return
|
|
226
|
+
onSend?.({ attachments, model, skills: selectedSkills, text: value.trim() })
|
|
227
|
+
setValue("")
|
|
228
|
+
if (attachments.length > 0) setAttachments([])
|
|
229
|
+
if (selectedSkills.length > 0) setSelectedSkills([])
|
|
230
|
+
enhancement.reset()
|
|
231
|
+
slash.close()
|
|
232
|
+
focusField()
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
236
|
+
// Keys that pick or confirm an IME candidate (Japanese, Chinese, Korean)
|
|
237
|
+
// belong to the composition, so they never send or move the palette.
|
|
238
|
+
// Safari reports the confirming Enter with keyCode 229 instead.
|
|
239
|
+
if (event.nativeEvent.isComposing || event.keyCode === 229) return
|
|
240
|
+
if (slash.handleKey(event, applySlash)) return
|
|
241
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
242
|
+
event.preventDefault()
|
|
243
|
+
send()
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<div
|
|
249
|
+
data-disabled={disabled ? "true" : "false"}
|
|
250
|
+
data-phase={enhancement.phase}
|
|
251
|
+
data-slot="agent-input"
|
|
252
|
+
ref={ref}
|
|
253
|
+
className={cn("min-w-0 max-w-full", className)}
|
|
254
|
+
{...props}
|
|
255
|
+
>
|
|
256
|
+
<input {...picker.inputProps} />
|
|
257
|
+
{/* Stays mounted so the text it gains is announced: a live region
|
|
258
|
+
inserted together with its text is often skipped. */}
|
|
259
|
+
<span className="sr-only" data-slot="agent-input-status" role="status">
|
|
260
|
+
{enhancement.enhancing ? labels.enhancing : null}
|
|
261
|
+
</span>
|
|
262
|
+
|
|
263
|
+
<div
|
|
264
|
+
data-enhancing={enhancement.enhancing ? "true" : "false"}
|
|
265
|
+
data-slot="agent-input-frame"
|
|
266
|
+
ref={frameRef}
|
|
267
|
+
>
|
|
268
|
+
<ChipRow
|
|
269
|
+
chips={presence.chips}
|
|
270
|
+
disabled={disabled}
|
|
271
|
+
labels={labels}
|
|
272
|
+
onExited={presence.settle}
|
|
273
|
+
onRemove={removeChip}
|
|
274
|
+
skillName={skillName}
|
|
275
|
+
/>
|
|
276
|
+
|
|
277
|
+
<div data-slot="agent-input-editor">
|
|
278
|
+
{enhancement.enhancing ? (
|
|
279
|
+
<div data-slot="agent-input-enhancing">
|
|
280
|
+
<ShimmerText tone="default">{value}</ShimmerText>
|
|
281
|
+
</div>
|
|
282
|
+
) : (
|
|
283
|
+
<textarea
|
|
284
|
+
aria-activedescendant={slash.nav.activeOptionId}
|
|
285
|
+
aria-autocomplete="list"
|
|
286
|
+
aria-controls={slash.open ? slash.id : undefined}
|
|
287
|
+
aria-haspopup="listbox"
|
|
288
|
+
aria-label={labels.placeholder}
|
|
289
|
+
data-slot="agent-input-field"
|
|
290
|
+
disabled={disabled}
|
|
291
|
+
onBlur={slash.close}
|
|
292
|
+
onChange={(event) => {
|
|
293
|
+
setValue(event.target.value)
|
|
294
|
+
if (enhancement.phase === "enhanced") enhancement.reset()
|
|
295
|
+
slash.detect(event.target.value, event.target.selectionStart)
|
|
296
|
+
}}
|
|
297
|
+
onClick={(event) =>
|
|
298
|
+
slash.detect(event.currentTarget.value, event.currentTarget.selectionStart)
|
|
299
|
+
}
|
|
300
|
+
onKeyDown={handleKeyDown}
|
|
301
|
+
onKeyUp={(event) => {
|
|
302
|
+
if (["ArrowUp", "ArrowDown", "Enter", "Tab", "Escape"].includes(event.key))
|
|
303
|
+
return
|
|
304
|
+
slash.detect(event.currentTarget.value, event.currentTarget.selectionStart)
|
|
305
|
+
}}
|
|
306
|
+
placeholder={labels.placeholder}
|
|
307
|
+
ref={fieldRef}
|
|
308
|
+
rows={1}
|
|
309
|
+
value={value}
|
|
310
|
+
/>
|
|
311
|
+
)}
|
|
312
|
+
|
|
313
|
+
{slash.open && !enhancement.enhancing ? (
|
|
314
|
+
<SlashPalette labels={labels} onPick={applySlash} slash={slash} />
|
|
315
|
+
) : null}
|
|
316
|
+
</div>
|
|
317
|
+
|
|
318
|
+
<div data-slot="agent-input-row">
|
|
319
|
+
<AddMenu
|
|
320
|
+
disabled={disabled || enhancement.enhancing}
|
|
321
|
+
labels={labels}
|
|
322
|
+
model={model}
|
|
323
|
+
models={models}
|
|
324
|
+
onModelChange={setModel}
|
|
325
|
+
onPickFiles={picker.open}
|
|
326
|
+
onToggleSkill={toggleSkill}
|
|
327
|
+
selectedSkills={selectedSkills}
|
|
328
|
+
skills={skills}
|
|
329
|
+
/>
|
|
330
|
+
|
|
331
|
+
<div data-slot="agent-input-actions">
|
|
332
|
+
{enhancement.enhancing ? (
|
|
333
|
+
<span data-slot="agent-input-spinner">
|
|
334
|
+
<Spinner />
|
|
335
|
+
</span>
|
|
336
|
+
) : pill.mounted ? (
|
|
337
|
+
<Button
|
|
338
|
+
data-exit={pill.exiting ? "true" : "false"}
|
|
339
|
+
data-slot="agent-input-enhance"
|
|
340
|
+
onClick={() => {
|
|
341
|
+
if (enhancement.phase === "enhanced") {
|
|
342
|
+
enhancement.revert()
|
|
343
|
+
focusField()
|
|
344
|
+
return
|
|
345
|
+
}
|
|
346
|
+
void enhancement.run()
|
|
347
|
+
}}
|
|
348
|
+
ref={pill.ref}
|
|
349
|
+
size="xs"
|
|
350
|
+
variant="soft"
|
|
351
|
+
>
|
|
352
|
+
{enhancement.phase === "enhanced" ? labels.revert : labels.enhance}
|
|
353
|
+
</Button>
|
|
354
|
+
) : null}
|
|
355
|
+
<Button
|
|
356
|
+
aria-label={labels.send}
|
|
357
|
+
data-slot="agent-input-send"
|
|
358
|
+
disabled={!sendActive}
|
|
359
|
+
onClick={send}
|
|
360
|
+
size="icon-xs"
|
|
361
|
+
variant={sendActive ? "default" : "soft"}
|
|
362
|
+
>
|
|
363
|
+
<ArrowUpIcon />
|
|
364
|
+
</Button>
|
|
365
|
+
</div>
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
</div>
|
|
369
|
+
)
|
|
370
|
+
}
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
export { AgentInput }
|
|
374
|
+
export type {
|
|
375
|
+
AgentInputAttachment,
|
|
376
|
+
AgentInputLabels,
|
|
377
|
+
AgentInputModel,
|
|
378
|
+
AgentInputPayload,
|
|
379
|
+
AgentInputPhase,
|
|
380
|
+
AgentInputSkill,
|
|
381
|
+
} from "./agent-input-state"
|
|
382
|
+
export type { AgentInputProps }
|
|
@@ -15,8 +15,8 @@ const alertDialogContentVariants = cva(
|
|
|
15
15
|
[
|
|
16
16
|
"fixed left-1/2 top-1/2 z-[var(--bh-z-overlay)] flex max-h-[calc(100vh-(var(--bh-space-8xl-48)*2))] w-[var(--bh-alert-dialog-width)]",
|
|
17
17
|
"max-w-[calc(100vw-(var(--bh-space-4xl-20)*2))] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden",
|
|
18
|
-
"rounded-[var(--bh-radius-2xl-12)] bg-[var(--bh-bg-raised)] text-[var(--bh-content-default)] shadow-[var(--
|
|
19
|
-
"[--bh-alert-dialog-border:var(--bh-border-subtle)] [--
|
|
18
|
+
"rounded-[var(--bh-radius-2xl-12)] bg-[var(--bh-bg-raised)] text-[var(--bh-content-default)] shadow-[var(--elevation-modal)] outline-none",
|
|
19
|
+
"[--bh-alert-dialog-border:var(--bh-border-subtle)] [--elevation-modal:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-alert-dialog-border),var(--elevation-06)]",
|
|
20
20
|
],
|
|
21
21
|
{
|
|
22
22
|
variants: {
|
|
@@ -23,13 +23,13 @@ const alertVariants = cva(
|
|
|
23
23
|
variants: {
|
|
24
24
|
status: {
|
|
25
25
|
danger:
|
|
26
|
-
"[--alert-bg:var(--bh-bg-danger-subtle)] [--alert-border:var(--bh-border-danger-subtle)] [--alert-title:var(--bh-content-danger-strong)] [--alert-description:var(--bh-content-danger-
|
|
26
|
+
"[--alert-bg:var(--bh-bg-danger-subtle)] [--alert-border:var(--bh-border-danger-subtle)] [--alert-title:var(--bh-content-danger-strong)] [--alert-description:var(--bh-content-danger-default)] [--alert-icon:var(--bh-content-danger-default)]",
|
|
27
27
|
success:
|
|
28
|
-
"[--alert-bg:var(--bh-bg-success-subtle)] [--alert-border:var(--bh-border-success-subtle)] [--alert-title:var(--bh-content-success-strong)] [--alert-description:var(--bh-content-success-
|
|
28
|
+
"[--alert-bg:var(--bh-bg-success-subtle)] [--alert-border:var(--bh-border-success-subtle)] [--alert-title:var(--bh-content-success-strong)] [--alert-description:var(--bh-content-success-default)] [--alert-icon:var(--bh-content-success-default)]",
|
|
29
29
|
warning:
|
|
30
|
-
"[--alert-bg:var(--bh-bg-warning-subtle)] [--alert-border:var(--bh-border-warning-subtle)] [--alert-title:var(--bh-content-warning-strong)] [--alert-description:var(--bh-content-warning-
|
|
30
|
+
"[--alert-bg:var(--bh-bg-warning-subtle)] [--alert-border:var(--bh-border-warning-subtle)] [--alert-title:var(--bh-content-warning-strong)] [--alert-description:var(--bh-content-warning-default)] [--alert-icon:var(--bh-content-warning-default)]",
|
|
31
31
|
info:
|
|
32
|
-
"[--alert-bg:var(--bh-bg-info-subtle)] [--alert-border:var(--bh-border-info-subtle)] [--alert-title:var(--bh-content-brand-strong)] [--alert-description:var(--bh-content-brand-
|
|
32
|
+
"[--alert-bg:var(--bh-bg-info-subtle)] [--alert-border:var(--bh-border-info-subtle)] [--alert-title:var(--bh-content-brand-strong)] [--alert-description:var(--bh-content-brand-default)] [--alert-icon:var(--bh-content-brand-default)]",
|
|
33
33
|
neutral:
|
|
34
34
|
"[--alert-bg:var(--bh-bg-neutral-subtle)] [--alert-border:var(--bh-border-subtle)] [--alert-title:var(--bh-content-default)] [--alert-description:var(--bh-content-default)] [--alert-icon:var(--bh-content-default)]",
|
|
35
35
|
},
|
|
@@ -48,6 +48,7 @@ const alertVariants = cva(
|
|
|
48
48
|
)
|
|
49
49
|
|
|
50
50
|
type AlertStatus = "danger" | "success" | "warning" | "info" | "neutral"
|
|
51
|
+
type AlertDescriptionColor = "status" | "default"
|
|
51
52
|
type AlertDirection = "ltr" | "rtl"
|
|
52
53
|
|
|
53
54
|
type AlertMessages = {
|
|
@@ -75,7 +76,11 @@ type AlertProps = Omit<
|
|
|
75
76
|
actions?: AlertAction[]
|
|
76
77
|
closeLabel?: string
|
|
77
78
|
description?: React.ReactNode
|
|
79
|
+
/** "default" sets the description in content/default instead of the status colour. */
|
|
80
|
+
descriptionColor?: AlertDescriptionColor
|
|
78
81
|
dir?: AlertDirection
|
|
82
|
+
/** Replaces the status icon. */
|
|
83
|
+
icon?: React.ReactNode
|
|
79
84
|
messages?: Partial<AlertMessages>
|
|
80
85
|
onClose?: () => void
|
|
81
86
|
showActions?: boolean
|
|
@@ -91,7 +96,9 @@ const Alert = React.forwardRef<HTMLElement, AlertProps>(function Alert(
|
|
|
91
96
|
className,
|
|
92
97
|
closeLabel,
|
|
93
98
|
description,
|
|
99
|
+
descriptionColor = "status",
|
|
94
100
|
dir,
|
|
101
|
+
icon,
|
|
95
102
|
isExpandable = false,
|
|
96
103
|
messages: messageOverrides,
|
|
97
104
|
onClose,
|
|
@@ -119,7 +126,10 @@ const Alert = React.forwardRef<HTMLElement, AlertProps>(function Alert(
|
|
|
119
126
|
const requestedActions = (showActions ?? Boolean(actions?.length))
|
|
120
127
|
? (actions ?? [])
|
|
121
128
|
: []
|
|
122
|
-
|
|
129
|
+
// The expanded layout shows the description, with or without actions.
|
|
130
|
+
const expanded = Boolean(
|
|
131
|
+
isExpandable && (description !== undefined || requestedActions.length > 0)
|
|
132
|
+
)
|
|
123
133
|
const visibleActions = requestedActions.slice(0, expanded ? 2 : 1)
|
|
124
134
|
const visibleCloseButton = showCloseButton ?? Boolean(onClose)
|
|
125
135
|
const resolvedCloseLabel =
|
|
@@ -161,7 +171,7 @@ const Alert = React.forwardRef<HTMLElement, AlertProps>(function Alert(
|
|
|
161
171
|
data-slot="alert-icon"
|
|
162
172
|
className="inline-flex size-[var(--bh-alert-icon-slot-size)] shrink-0 items-center justify-center text-[var(--alert-icon)]"
|
|
163
173
|
>
|
|
164
|
-
<AlertStatusIcon status={resolvedStatus} />
|
|
174
|
+
{icon ?? <AlertStatusIcon status={resolvedStatus} />}
|
|
165
175
|
</span>
|
|
166
176
|
<div
|
|
167
177
|
data-slot="alert-copy"
|
|
@@ -195,7 +205,12 @@ const Alert = React.forwardRef<HTMLElement, AlertProps>(function Alert(
|
|
|
195
205
|
<p
|
|
196
206
|
data-slot="alert-description"
|
|
197
207
|
dir="auto"
|
|
198
|
-
className=
|
|
208
|
+
className={cn(
|
|
209
|
+
"m-0 w-full break-words text-[length:var(--bh-text-body-sm-regular-font-size)] font-[var(--bh-font-weight-regular)] leading-[var(--bh-text-body-sm-regular-line-height)] tracking-[var(--bh-text-body-sm-regular-letter-spacing)]",
|
|
210
|
+
descriptionColor === "default"
|
|
211
|
+
? "text-[var(--bh-content-default)]"
|
|
212
|
+
: "text-[var(--alert-description)]"
|
|
213
|
+
)}
|
|
199
214
|
>
|
|
200
215
|
{description}
|
|
201
216
|
</p>
|
|
@@ -286,6 +301,7 @@ export {
|
|
|
286
301
|
Alert,
|
|
287
302
|
alertVariants,
|
|
288
303
|
type AlertAction,
|
|
304
|
+
type AlertDescriptionColor,
|
|
289
305
|
type AlertDirection,
|
|
290
306
|
type AlertMessages,
|
|
291
307
|
type AlertProps,
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
|
|
8
|
+
type AppHeaderProps = React.ComponentProps<"header">
|
|
9
|
+
type AppHeaderSectionProps = React.ComponentProps<"div">
|
|
10
|
+
type AppHeaderSearchProps = React.ComponentProps<"form">
|
|
11
|
+
type AppHeaderActionSize = "sm" | "md"
|
|
12
|
+
type AppHeaderActionProps = React.ComponentProps<"button"> & {
|
|
13
|
+
asChild?: boolean
|
|
14
|
+
icon?: React.ReactNode
|
|
15
|
+
size?: AppHeaderActionSize
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Workspace bar above page content: a location trail at the start and workspace tools at the end. */
|
|
19
|
+
function AppHeader({ className, ...props }: AppHeaderProps) {
|
|
20
|
+
return (
|
|
21
|
+
<header
|
|
22
|
+
data-slot="app-header"
|
|
23
|
+
className={cn(
|
|
24
|
+
// The rule is an inset shadow, like a Figma inside stroke, so content centers on the full height.
|
|
25
|
+
"flex min-h-[var(--bh-app-header-height)] w-full min-w-0 shrink-0 items-center gap-[var(--bh-app-header-gap)] bg-[var(--bh-app-header-bg)] px-[var(--bh-app-header-padding-x)] text-[var(--bh-content-default)] shadow-[inset_0_calc(var(--bh-border-width-default)*-1)_0_0_var(--bh-app-header-border)]",
|
|
26
|
+
"[--bh-breadcrumbs-current-content:var(--bh-app-header-location-content)]",
|
|
27
|
+
className
|
|
28
|
+
)}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Flexible leading region for Breadcrumbs or a short location label. It keeps a minimum width so search shrinks first. */
|
|
35
|
+
function AppHeaderStart({ className, ...props }: AppHeaderSectionProps) {
|
|
36
|
+
return (
|
|
37
|
+
<div
|
|
38
|
+
data-slot="app-header-start"
|
|
39
|
+
className={cn(
|
|
40
|
+
"flex min-w-[min(var(--bh-app-header-start-min-width),100%)] flex-1 items-center gap-[var(--bh-app-header-start-gap)]",
|
|
41
|
+
"text-[length:var(--bh-text-body-sm-medium-font-size)] font-[var(--bh-text-body-sm-medium-font-weight)] leading-[var(--bh-text-body-sm-medium-line-height)] tracking-[var(--bh-text-body-sm-medium-letter-spacing)] text-[var(--bh-app-header-location-content)]",
|
|
42
|
+
className
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Trailing group of search and actions, spaced on the header gap. */
|
|
50
|
+
function AppHeaderEnd({ className, ...props }: AppHeaderSectionProps) {
|
|
51
|
+
return (
|
|
52
|
+
<div
|
|
53
|
+
data-slot="app-header-end"
|
|
54
|
+
className={cn(
|
|
55
|
+
"flex min-w-0 items-center gap-[var(--bh-app-header-gap)]",
|
|
56
|
+
className
|
|
57
|
+
)}
|
|
58
|
+
{...props}
|
|
59
|
+
/>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Search landmark that sizes a nested Input to the header search width. */
|
|
64
|
+
function AppHeaderSearch({
|
|
65
|
+
className,
|
|
66
|
+
role = "search",
|
|
67
|
+
...props
|
|
68
|
+
}: AppHeaderSearchProps) {
|
|
69
|
+
return (
|
|
70
|
+
<form
|
|
71
|
+
data-slot="app-header-search"
|
|
72
|
+
role={role}
|
|
73
|
+
className={cn(
|
|
74
|
+
"flex w-[var(--bh-app-header-search-width)] min-w-0 max-w-full shrink items-center [--bh-input-width:100%]",
|
|
75
|
+
className
|
|
76
|
+
)}
|
|
77
|
+
{...props}
|
|
78
|
+
/>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Flush header action. At rest it occupies only its icon or label, as in the
|
|
84
|
+
* workspace bar design; padding and a matching negative margin extend the
|
|
85
|
+
* hover surface and pointer target past that footprint without moving it.
|
|
86
|
+
*/
|
|
87
|
+
const AppHeaderAction = React.forwardRef<
|
|
88
|
+
HTMLButtonElement,
|
|
89
|
+
AppHeaderActionProps
|
|
90
|
+
>(function AppHeaderAction(
|
|
91
|
+
{
|
|
92
|
+
asChild = false,
|
|
93
|
+
children,
|
|
94
|
+
className,
|
|
95
|
+
icon,
|
|
96
|
+
size = "md",
|
|
97
|
+
type = "button",
|
|
98
|
+
...props
|
|
99
|
+
},
|
|
100
|
+
ref
|
|
101
|
+
) {
|
|
102
|
+
const Comp = asChild ? Slot : "button"
|
|
103
|
+
const iconOnly = icon != null && children == null
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<Comp
|
|
107
|
+
data-icon-only={iconOnly ? "true" : undefined}
|
|
108
|
+
data-size={size}
|
|
109
|
+
data-slot="app-header-action"
|
|
110
|
+
ref={ref}
|
|
111
|
+
type={asChild ? undefined : type}
|
|
112
|
+
className={cn(
|
|
113
|
+
"m-[calc(var(--bh-app-header-action-hit-inset)*-1)] inline-flex shrink-0 cursor-pointer items-center gap-[var(--bh-space-xs-4)] whitespace-nowrap rounded-[var(--bh-app-header-action-radius)] border-0 bg-transparent p-[var(--bh-app-header-action-hit-inset)] no-underline outline-none transition-[background-color,box-shadow]",
|
|
114
|
+
"hover:bg-[var(--bh-app-header-action-bg-hover)] focus-visible:shadow-[var(--elevation-button-focus)]",
|
|
115
|
+
"disabled:cursor-not-allowed disabled:bg-transparent disabled:text-[var(--bh-content-disabled)]",
|
|
116
|
+
"[&_svg]:size-[var(--bh-app-header-icon-size)] [&_svg]:shrink-0 [&>img]:size-[var(--bh-app-header-icon-size)] [&>img]:shrink-0",
|
|
117
|
+
iconOnly
|
|
118
|
+
? "justify-center text-[var(--bh-app-header-action-content)]"
|
|
119
|
+
: size === "sm"
|
|
120
|
+
? "min-w-[calc(var(--bh-app-header-action-sm-min-width)+var(--bh-app-header-action-hit-inset)*2)] justify-end text-[length:var(--bh-text-body-sm-medium-font-size)] font-[var(--bh-text-body-sm-medium-font-weight)] leading-[var(--bh-text-body-sm-medium-line-height)] tracking-[var(--bh-text-body-sm-medium-letter-spacing)] text-[var(--bh-app-header-action-sm-content)]"
|
|
121
|
+
: "justify-center text-[length:var(--bh-text-body-md-medium-font-size)] font-[var(--bh-text-body-md-medium-font-weight)] leading-[var(--bh-text-body-md-medium-line-height)] tracking-[var(--bh-text-body-md-medium-letter-spacing)] text-[var(--bh-app-header-action-content)]",
|
|
122
|
+
className
|
|
123
|
+
)}
|
|
124
|
+
{...props}
|
|
125
|
+
>
|
|
126
|
+
{asChild ? (
|
|
127
|
+
children
|
|
128
|
+
) : (
|
|
129
|
+
<>
|
|
130
|
+
{icon}
|
|
131
|
+
{children}
|
|
132
|
+
</>
|
|
133
|
+
)}
|
|
134
|
+
</Comp>
|
|
135
|
+
)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
export {
|
|
139
|
+
AppHeader,
|
|
140
|
+
AppHeaderAction,
|
|
141
|
+
AppHeaderEnd,
|
|
142
|
+
AppHeaderSearch,
|
|
143
|
+
AppHeaderStart,
|
|
144
|
+
}
|
|
145
|
+
export type {
|
|
146
|
+
AppHeaderActionProps,
|
|
147
|
+
AppHeaderActionSize,
|
|
148
|
+
AppHeaderProps,
|
|
149
|
+
AppHeaderSearchProps,
|
|
150
|
+
AppHeaderSectionProps,
|
|
151
|
+
}
|