Skip to content

Commit d3cd5a4

Browse files
authored
Merge pull request #1651 from Mai-with-u/dev
Dev
2 parents 16b560e + 16ece26 commit d3cd5a4

58 files changed

Lines changed: 2916 additions & 759 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

dashboard/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "maibot-dashboard",
33
"private": true,
4-
"version": "1.0.6",
4+
"version": "1.0.7",
55
"type": "module",
66
"main": "./out/main/index.js",
77
"scripts": {

dashboard/src/components/dynamic-form/DynamicConfigForm.tsx

Lines changed: 81 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { Button } from '@/components/ui/button'
55
import {
66
Card,
77
CardContent,
8-
CardDescription,
98
CardHeader,
109
CardTitle,
1110
} from '@/components/ui/card'
@@ -31,20 +30,10 @@ function buildFieldPath(basePath: string, fieldName: string) {
3130
return basePath ? `${basePath}.${fieldName}` : fieldName
3231
}
3332

34-
function hasTopLevelAdvancedFields(schema: ConfigSchema) {
35-
return schema.fields.some((field) => field.advanced && !schema.nested?.[field.name])
36-
}
37-
3833
function resolveSectionTitle(schema: ConfigSchema) {
3934
return schema.uiLabel || schema.classDoc || schema.className
4035
}
4136

42-
function resolveSectionDescription(schema: ConfigSchema, sectionTitle: string) {
43-
return schema.classDoc && schema.classDoc !== sectionTitle
44-
? schema.classDoc
45-
: undefined
46-
}
47-
4837
function SectionIcon({ iconName }: { iconName?: string }) {
4938
if (!iconName) return null
5039
const IconComponent = LucideIcons[iconName as keyof typeof LucideIcons] as
@@ -54,7 +43,7 @@ function SectionIcon({ iconName }: { iconName?: string }) {
5443
return <IconComponent className="h-5 w-5 text-muted-foreground" />
5544
}
5645

57-
function AdvancedSettingsButton({
46+
export function AdvancedSettingsButton({
5847
active,
5948
onClick,
6049
}: {
@@ -74,59 +63,47 @@ function AdvancedSettingsButton({
7463
}
7564

7665
function DynamicConfigSection({
66+
advancedVisible,
7767
basePath,
7868
hooks,
7969
level,
8070
nestedSchema,
8171
onChange,
82-
sectionDescription,
8372
sectionKey,
8473
sectionTitle,
8574
values,
8675
}: {
76+
advancedVisible: boolean
8777
basePath: string
8878
hooks: FieldHookRegistry
8979
level: number
9080
nestedSchema: ConfigSchema
9181
onChange: (field: string, value: unknown) => void
92-
sectionDescription?: string
9382
sectionKey: string
9483
sectionTitle: string
9584
values: Record<string, unknown>
9685
}) {
97-
const [advancedVisible, setAdvancedVisible] = React.useState(false)
98-
const hasAdvanced = hasTopLevelAdvancedFields(nestedSchema)
99-
10086
return (
101-
<Card>
102-
<CardHeader className="pb-4">
87+
<Card className="min-w-0">
88+
<CardHeader className="border-b border-border/50 pb-4">
10389
<div className="flex items-start justify-between gap-4">
10490
<div className="space-y-1">
10591
<div className="flex items-center gap-2">
10692
<SectionIcon iconName={nestedSchema.uiIcon} />
107-
<CardTitle className="text-lg">{sectionTitle}</CardTitle>
93+
<CardTitle className="text-lg text-primary">{sectionTitle}</CardTitle>
10894
</div>
109-
{sectionDescription && (
110-
<CardDescription>{sectionDescription}</CardDescription>
111-
)}
11295
</div>
113-
{hasAdvanced && (
114-
<AdvancedSettingsButton
115-
active={advancedVisible}
116-
onClick={() => setAdvancedVisible((current) => !current)}
117-
/>
118-
)}
11996
</div>
12097
</CardHeader>
121-
<CardContent>
98+
<CardContent className="pt-4">
12299
<DynamicConfigForm
123100
schema={nestedSchema}
124101
values={values}
125102
onChange={(field, value) => onChange(`${sectionKey}.${field}`, value)}
126103
basePath={basePath}
127104
hooks={hooks}
128105
level={level}
129-
advancedVisible={hasAdvanced ? advancedVisible : undefined}
106+
advancedVisible={advancedVisible}
130107
sectionColumns={1}
131108
/>
132109
</CardContent>
@@ -154,8 +131,7 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
154131
advancedVisible,
155132
sectionColumns = 1,
156133
}) => {
157-
const [localAdvancedVisible, setLocalAdvancedVisible] = React.useState(false)
158-
const resolvedAdvancedVisible = advancedVisible ?? localAdvancedVisible
134+
const resolvedAdvancedVisible = advancedVisible ?? false
159135

160136
const fieldMap = React.useMemo(
161137
() => new Map(schema.fields.map((field) => [field.name, field])),
@@ -230,6 +206,51 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
230206
return hooks.get(fieldPath)?.type === 'replace'
231207
}
232208

209+
const schemaHasVisibleContent = React.useCallback(
210+
(targetSchema: ConfigSchema, targetBasePath: string): boolean => {
211+
const targetFields = targetSchema.fields ?? []
212+
const hasVisibleInlineField = targetFields.some((field) => {
213+
const fieldPath = buildFieldPath(targetBasePath, field.name)
214+
const hookEntry = hooks.get(fieldPath)
215+
216+
if (hookEntry?.type === 'hidden') {
217+
return false
218+
}
219+
220+
if (targetSchema.nested?.[field.name] && hookEntry?.type !== 'replace') {
221+
return false
222+
}
223+
224+
return resolvedAdvancedVisible || !field.advanced
225+
})
226+
227+
if (hasVisibleInlineField) {
228+
return true
229+
}
230+
231+
return Object.entries(targetSchema.nested ?? {}).some(([key, nestedSchema]) => {
232+
const nestedField = targetFields.find((field) => field.name === key)
233+
const nestedFieldPath = buildFieldPath(targetBasePath, key)
234+
const hookEntry = hooks.get(nestedFieldPath)
235+
236+
if (hookEntry?.type === 'hidden') {
237+
return false
238+
}
239+
240+
if (nestedField?.advanced && !resolvedAdvancedVisible) {
241+
return false
242+
}
243+
244+
if (hookEntry?.type === 'replace') {
245+
return true
246+
}
247+
248+
return schemaHasVisibleContent(nestedSchema, nestedFieldPath)
249+
})
250+
},
251+
[hooks, resolvedAdvancedVisible],
252+
)
253+
233254
const inlineFields = schema.fields.filter(shouldRenderFieldInline)
234255
const inlineNestedFieldNames = new Set(
235256
inlineFields
@@ -275,15 +296,15 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
275296
row.length > 1 ? (
276297
<div
277298
key={row.map((field) => field.name).join('|')}
278-
className="grid gap-4 py-1 md:grid-cols-[repeat(var(--field-row-count),minmax(0,1fr))]"
299+
className="grid min-w-0 gap-4 py-1 md:grid-cols-[repeat(var(--field-row-count),minmax(0,1fr))]"
279300
style={{ '--field-row-count': row.length } as React.CSSProperties}
280301
>
281302
{row.map((field) => (
282-
<div key={field.name}>{renderField(field)}</div>
303+
<div key={field.name} className="min-w-0">{renderField(field)}</div>
283304
))}
284305
</div>
285306
) : (
286-
<div key={row[0].name} className="py-1">{renderField(row[0])}</div>
307+
<div key={row[0].name} className="min-w-0 py-1">{renderField(row[0])}</div>
287308
)
288309
))}
289310
</>
@@ -301,17 +322,9 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
301322
)
302323

303324
return (
304-
<div className="space-y-6">
305-
{inlineFields.length > 0 && (
325+
<div className="min-w-0 space-y-6">
326+
{visibleFields.length > 0 && (
306327
<div>
307-
{advancedVisible === undefined && advancedFields.length > 0 && (
308-
<div className="flex justify-end pb-2">
309-
<AdvancedSettingsButton
310-
active={localAdvancedVisible}
311-
onClick={() => setLocalAdvancedVisible((current) => !current)}
312-
/>
313-
</div>
314-
)}
315328
{renderFieldList(visibleFields)}
316329
</div>
317330
)}
@@ -327,11 +340,20 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
327340
if (hooks.has(nestedFieldPath)) {
328341
const hookEntry = hooks.get(nestedFieldPath)
329342
if (!hookEntry) return null
343+
if (hookEntry.type === 'hidden') return null
344+
if (nestedField?.advanced && !resolvedAdvancedVisible) return null
345+
if (
346+
hookEntry.type !== 'replace' &&
347+
nestedSchema &&
348+
!schemaHasVisibleContent(nestedSchema, nestedFieldPath)
349+
) {
350+
return null
351+
}
330352

331353
const HookComponent = hookEntry.component
332354
if (hookEntry.type === 'replace') {
333355
return (
334-
<div key={key}>
356+
<div key={key} className="min-w-0">
335357
<HookComponent
336358
fieldPath={nestedFieldPath}
337359
value={values[key]}
@@ -346,7 +368,7 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
346368
}
347369

348370
return (
349-
<div key={key}>
371+
<div key={key} className="min-w-0">
350372
<HookComponent
351373
fieldPath={nestedFieldPath}
352374
value={values[key]}
@@ -363,6 +385,7 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
363385
basePath={nestedFieldPath}
364386
hooks={hooks}
365387
level={level + 1}
388+
advancedVisible={resolvedAdvancedVisible}
366389
sectionColumns={1}
367390
/>
368391
</HookComponent>
@@ -371,12 +394,15 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
371394
}
372395

373396
const sectionTitle = resolveSectionTitle(nestedSchema)
374-
const sectionDescription = resolveSectionDescription(nestedSchema, sectionTitle)
397+
if (!schemaHasVisibleContent(nestedSchema, nestedFieldPath)) {
398+
return null
399+
}
375400

376401
if (level === 0) {
377402
return (
378403
<DynamicConfigSection
379404
key={key}
405+
advancedVisible={resolvedAdvancedVisible}
380406
nestedSchema={nestedSchema}
381407
values={(values[key] as Record<string, unknown>) || {}}
382408
onChange={onChange}
@@ -385,36 +411,31 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
385411
level={level + 1}
386412
sectionKey={key}
387413
sectionTitle={sectionTitle}
388-
sectionDescription={sectionDescription}
389414
/>
390415
)
391416
}
392417

393418
return (
394-
<Card key={key} className="border-border/70 bg-muted/20 shadow-none">
395-
<CardHeader className="px-4 py-3">
419+
<Card key={key} className="min-w-0 border-border/70 bg-muted/20 shadow-none">
420+
<CardHeader className="border-b border-border/50 px-4 py-3">
396421
<div className="flex items-start justify-between gap-4">
397422
<div className="space-y-1">
398423
<div className="flex items-center gap-2">
399424
<SectionIcon iconName={nestedSchema.uiIcon} />
400-
<CardTitle className="text-sm">{sectionTitle}</CardTitle>
425+
<CardTitle className="text-sm text-primary">{sectionTitle}</CardTitle>
401426
</div>
402-
{sectionDescription && (
403-
<CardDescription className="text-xs">
404-
{sectionDescription}
405-
</CardDescription>
406-
)}
407427
</div>
408428
</div>
409429
</CardHeader>
410-
<CardContent className="px-4 pb-4 pt-0">
430+
<CardContent className="px-4 pb-4 pt-4">
411431
<DynamicConfigForm
412432
schema={nestedSchema}
413433
values={(values[key] as Record<string, unknown>) || {}}
414434
onChange={(field, value) => onChange(`${key}.${field}`, value)}
415435
basePath={nestedFieldPath}
416436
hooks={hooks}
417437
level={level + 1}
438+
advancedVisible={resolvedAdvancedVisible}
418439
sectionColumns={1}
419440
/>
420441
</CardContent>
@@ -428,7 +449,7 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
428449

429450
if (level === 0 && sectionColumns === 2 && visibleNestedSections.length > 1) {
430451
return (
431-
<div className="grid gap-4 md:grid-cols-2">
452+
<div className="grid min-w-0 gap-4 md:grid-cols-2">
432453
{visibleNestedSections}
433454
</div>
434455
)

dashboard/src/components/dynamic-form/DynamicField.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,15 +158,15 @@ export const DynamicField: React.FC<DynamicFieldProps> = ({
158158
const label = (
159159
<Label
160160
className={cn(
161-
"inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap text-[15px] font-semibold leading-6",
161+
"inline-flex min-w-0 items-center gap-1.5 text-[15px] leading-6",
162162
descriptionDisplay === 'label-hover' && fieldDescription && "cursor-help",
163163
schema.advanced
164-
? "text-amber-800 dark:text-amber-200"
164+
? "text-sky-700 dark:text-sky-300"
165165
: "text-foreground",
166166
)}
167167
>
168168
{renderIcon()}
169-
<span>{fieldLabel}</span>
169+
<span className="break-words">{fieldLabel}</span>
170170
{schema.required && <span className="text-destructive">*</span>}
171171
</Label>
172172
)
@@ -281,8 +281,8 @@ export const DynamicField: React.FC<DynamicFieldProps> = ({
281281
const renderSwitch = () => {
282282
const checked = Boolean(value)
283283
return (
284-
<div className="flex items-center justify-between gap-4 py-2">
285-
<div className="pr-4">
284+
<div className="flex min-w-0 items-center justify-between gap-4 py-2">
285+
<div className="min-w-0 pr-4">
286286
{renderFieldHeader()}
287287
</div>
288288
<Switch
@@ -303,7 +303,7 @@ export const DynamicField: React.FC<DynamicFieldProps> = ({
303303
const step = schema.step ?? 1
304304

305305
return (
306-
<div className="space-y-2">
306+
<div className="min-w-0 space-y-2">
307307
<Slider
308308
value={[numValue]}
309309
onValueChange={(values) => onChange(values[0])}
@@ -466,7 +466,7 @@ export const DynamicField: React.FC<DynamicFieldProps> = ({
466466
className="flex flex-col gap-2 py-2 sm:flex-row sm:items-center"
467467
style={{ '--field-input-width': schema['x-input-width'] ?? '12rem' } as React.CSSProperties}
468468
>
469-
<div className="shrink-0">
469+
<div className="min-w-0 sm:shrink-0">
470470
{renderFieldHeader()}
471471
</div>
472472
<div className="min-w-20 flex-1 sm:ml-auto sm:max-w-[var(--field-input-width)]">
@@ -477,7 +477,7 @@ export const DynamicField: React.FC<DynamicFieldProps> = ({
477477
}
478478

479479
return (
480-
<div className="space-y-2">
480+
<div className="min-w-0 space-y-2">
481481
{renderFieldHeader()}
482482

483483
{/* Input component */}

0 commit comments

Comments
 (0)