feat: Enhance A2UI with RadioGroup and Markdown support

- Added support for RadioGroup component in A2UI, allowing single selection from multiple options.
- Implemented Markdown parsing in A2UIPopupCard for better content rendering.
- Updated A2UIPopupCard to handle different question types and improved layout for multi-select and single-select questions.
- Introduced new utility functions for handling disabled items during installation.
- Enhanced installation process to restore previously disabled skills and commands.
- Updated notification store and related tests to accommodate new features.
- Adjusted Vite configuration for better development experience.
This commit is contained in:
catlog22
2026-02-04 13:45:47 +08:00
parent 1a05551d00
commit 341331325c
15 changed files with 743 additions and 178 deletions

View File

@@ -104,6 +104,19 @@ export const CheckboxComponentSchema = z.object({
}),
});
/** RadioGroup component - single selection from multiple options */
export const RadioGroupComponentSchema = z.object({
RadioGroup: z.object({
options: z.array(z.object({
label: TextContentSchema,
value: z.string(),
description: TextContentSchema.optional(),
})),
selectedValue: TextContentSchema.optional(),
onChange: ActionSchema,
}),
});
/** Code block component */
export const CodeBlockComponentSchema = z.object({
CodeBlock: z.object({
@@ -160,6 +173,7 @@ export const ComponentSchema: z.ZodType<any> = z.union([
TextFieldComponentSchema,
TextAreaComponentSchema,
CheckboxComponentSchema,
RadioGroupComponentSchema,
CodeBlockComponentSchema,
ProgressComponentSchema,
CardComponentSchema,
@@ -202,6 +216,7 @@ export type DropdownComponent = z.infer<typeof DropdownComponentSchema>;
export type TextFieldComponent = z.infer<typeof TextFieldComponentSchema>;
export type TextAreaComponent = z.infer<typeof TextAreaComponentSchema>;
export type CheckboxComponent = z.infer<typeof CheckboxComponentSchema>;
export type RadioGroupComponent = z.infer<typeof RadioGroupComponentSchema>;
export type CodeBlockComponent = z.infer<typeof CodeBlockComponentSchema>;
export type ProgressComponent = z.infer<typeof ProgressComponentSchema>;
export type CardComponent = z.infer<typeof CardComponentSchema>;
@@ -223,6 +238,7 @@ export type A2UIComponentType =
| 'TextField'
| 'TextArea'
| 'Checkbox'
| 'RadioGroup'
| 'CodeBlock'
| 'Progress'
| 'Card'

View File

@@ -0,0 +1,83 @@
// ========================================
// A2UI RadioGroup Component Renderer
// ========================================
// Maps A2UI RadioGroup component to shadcn/ui RadioGroup
// Used for single-select questions with visible options
import React, { useState, useCallback } from 'react';
import { RadioGroup, RadioGroupItem } from '@/components/ui/RadioGroup';
import { Label } from '@/components/ui/Label';
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
import { resolveLiteralOrBinding, resolveTextContent } from '../A2UIRenderer';
import type { RadioGroupComponent } from '../../core/A2UITypes';
interface A2UIRadioGroupProps {
component: RadioGroupComponent;
state: Record<string, unknown>;
onAction: (actionId: string, params: Record<string, unknown>) => void | Promise<void>;
resolveBinding: (binding: { path: string }) => unknown;
}
/**
* A2UI RadioGroup Component Renderer
* Single selection from visible options with onChange handler
*/
export const A2UIRadioGroup: ComponentRenderer = ({ component, state, onAction, resolveBinding }) => {
const radioGroupComp = component as RadioGroupComponent;
const { RadioGroup: radioConfig } = radioGroupComp;
// Resolve initial selected value from binding
const getInitialValue = (): string | undefined => {
if (!radioConfig.selectedValue) return undefined;
const resolved = resolveLiteralOrBinding(radioConfig.selectedValue, resolveBinding);
return resolved ? String(resolved) : undefined;
};
// Local state for controlled radio group
const [selectedValue, setSelectedValue] = useState<string | undefined>(getInitialValue());
// Handle change with action dispatch
const handleChange = useCallback((newValue: string) => {
setSelectedValue(newValue);
// Trigger action with new selected value
onAction(radioConfig.onChange.actionId, {
value: newValue,
...(radioConfig.onChange.parameters || {}),
});
}, [radioConfig.onChange, onAction]);
return (
<RadioGroup value={selectedValue} onValueChange={handleChange} className="space-y-2">
{radioConfig.options.map((option, idx) => {
const labelText = resolveTextContent(option.label, resolveBinding);
const descriptionText = option.description
? resolveTextContent(option.description, resolveBinding)
: undefined;
return (
<div key={option.value || idx} className="flex items-start space-x-3 py-1">
<RadioGroupItem
value={option.value}
id={`radio-${option.value}`}
className="mt-0.5"
/>
<div className="flex flex-col">
<Label
htmlFor={`radio-${option.value}`}
className="text-sm font-medium leading-none cursor-pointer"
>
{labelText}
</Label>
{descriptionText && (
<span className="text-xs text-muted-foreground mt-1">
{descriptionText}
</span>
)}
</div>
</div>
);
})}
</RadioGroup>
);
};

View File

@@ -14,6 +14,7 @@ import { A2UIDropdown } from './A2UIDropdown';
import { A2UITextField } from './A2UITextField';
import { A2UITextArea } from './A2UITextArea';
import { A2UICheckbox } from './A2UICheckbox';
import { A2UIRadioGroup } from './A2UIRadioGroup';
import { A2UIProgress } from './A2UIProgress';
import { A2UICard } from './A2UICard';
import { A2UICLIOutput } from './A2UICLIOutput';
@@ -27,6 +28,7 @@ a2uiRegistry.register('Dropdown' as A2UIComponentType, A2UIDropdown);
a2uiRegistry.register('TextField' as A2UIComponentType, A2UITextField);
a2uiRegistry.register('TextArea' as A2UIComponentType, A2UITextArea);
a2uiRegistry.register('Checkbox' as A2UIComponentType, A2UICheckbox);
a2uiRegistry.register('RadioGroup' as A2UIComponentType, A2UIRadioGroup);
a2uiRegistry.register('Progress' as A2UIComponentType, A2UIProgress);
a2uiRegistry.register('Card' as A2UIComponentType, A2UICard);
a2uiRegistry.register('CLIOutput' as A2UIComponentType, A2UICLIOutput);
@@ -39,6 +41,7 @@ export * from './A2UIDropdown';
export * from './A2UITextField';
export * from './A2UITextArea';
export * from './A2UICheckbox';
export * from './A2UIRadioGroup';
export * from './A2UIProgress';
export * from './A2UICard';
export * from './A2UICLIOutput';