Initial commit
This commit is contained in:
197
docs/dial/api-notes.md
Normal file
197
docs/dial/api-notes.md
Normal file
@@ -0,0 +1,197 @@
|
||||
1. [Home](/)
|
||||
1. [Dial](/dial)
|
||||
1. Api Notes
|
||||
|
||||
# API Design and Building Blocks
|
||||
|
||||
|
||||
## DialPanel Component
|
||||
|
||||
|
||||
The `DialPanel` component renders dial controls based on schemas. It accepts the following props:
|
||||
|
||||
|
||||
### Props
|
||||
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
| `schemas` | `DialSchema[]` | Required | Array of dial schemas to render |
|
||||
| `groups` | `DialGroupConfig[]` | `undefined` | Optional group configurations |
|
||||
| `labelLayout` | `LabelPositionT` | `undefined` | Default label position layout for all inputs ("top", "left", "right", "inline", etc.) |
|
||||
|
||||
|
||||
|
||||
### Label Position Priority
|
||||
|
||||
|
||||
The label position for inputs is determined by the following priority order:
|
||||
|
||||
|
||||
|
||||
1. **Component-specific**: Label position tags on individual properties (highest priority)
|
||||
|
||||
- `@dial-label-top`, `@dial-label-left`, `@dial-label-right`, `@dial-label-inline`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1. **Panel labelLayout**: `labelLayout` prop on DialPanel
|
||||
|
||||
1. **Component default**: Individual input component's default behavior (lowest priority)
|
||||
|
||||
|
||||
|
||||
### Example Usage
|
||||
|
||||
|
||||
```tsx
|
||||
import { DialPanel, DialProvider } from '@vuer-ai/vuer-uikit';
|
||||
|
||||
// With default label layout for all inputs
|
||||
<DialProvider schemas={schemas}>
|
||||
<DialPanel
|
||||
schemas={schemas}
|
||||
labelLayout="top" // All inputs will have top-aligned labels by default
|
||||
/>
|
||||
</DialProvider>
|
||||
|
||||
// Without specifying (components use their own defaults)
|
||||
<DialProvider schemas={schemas}>
|
||||
<DialPanel schemas={schemas} />
|
||||
</DialProvider>
|
||||
|
||||
```
|
||||
|
||||
|
||||
Individual components can still override the panel's default label layout:
|
||||
|
||||
|
||||
```tsx
|
||||
interface Props {
|
||||
/**
|
||||
* @dial-label-left // This overrides the panel's labelLayout
|
||||
*/
|
||||
specialField: number;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Building Blocks
|
||||
|
||||
|
||||
We want to specify the property menu without duplicating the code. Here are the basic building blocks:
|
||||
|
||||
|
||||
| Control Entry | Control Group |
|
||||
| ```jsx
|
||||
const controlEntry = {
|
||||
dtype: 'number',
|
||||
value: 10,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
| ```jsx
|
||||
const controlGroup = {
|
||||
tag: 'group',
|
||||
children: [
|
||||
controlEntry,
|
||||
],
|
||||
layout: 'row',
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
|
||||
|
||||
|
||||
|
||||
Now, let's convert this into a react schema that we can create using react.createElement:
|
||||
|
||||
|
||||
```jsx
|
||||
<Dial.Provider>
|
||||
<Dial.Row>
|
||||
<DialInput label="Position" column type="number" key="prop-name" value={10} min={0} max={100} step={1}/>
|
||||
<DialInput label="Rotation" column type="number" key="prop-name" value={10} min={0} max={100} step={1}/>
|
||||
<DialInput label="Scale" column type="number" key="prop-name" value={10} min={0} max={100} step={1}/>
|
||||
</Dial.Row>
|
||||
</Dial.Provider>
|
||||
|
||||
```
|
||||
|
||||
|
||||
this can be written via react createElement below:
|
||||
|
||||
|
||||
```jsx
|
||||
React.createElement(
|
||||
Dial.Provider,
|
||||
null,
|
||||
React.createElement(
|
||||
Dial.Row,
|
||||
null,
|
||||
React.createElement(DialInput, {
|
||||
label: "Position",
|
||||
column: true,
|
||||
type: "number",
|
||||
key: "prop-name",
|
||||
value: 10,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1
|
||||
}),
|
||||
React.createElement(DialInput, {
|
||||
label: "Rotation",
|
||||
column: true,
|
||||
type: "number",
|
||||
key: "prop-name",
|
||||
value: 10,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1
|
||||
}),
|
||||
React.createElement(DialInput, {
|
||||
label: "Scale",
|
||||
column: true,
|
||||
type: "number",
|
||||
key: "prop-name",
|
||||
value: 10,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
|
||||
We can do this via a nested json object using a convenient helper function:
|
||||
|
||||
|
||||
```jsx
|
||||
function build({tag, children, ...props}) {
|
||||
return React.createElement(tag, props, children)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
We can then rewrite the schema as:
|
||||
|
||||
|
||||
```jsx
|
||||
{ name: 'position', dtype: 'vector3', value: [0, 0, 0], min: 0, max: 100, options: [10, 20, 30, 40, 50],
|
||||
tags: { grouping: 'transform', col: true } }
|
||||
{ name: 'rotation', dtype: 'euler', value: [0, 0, 0], min: 0, max: 100, options: [10, 20, 30, 40, 50],
|
||||
tags: { grouping: 'transform', col: true } }
|
||||
{ name: 'scale', dtype: 'vector3', value: [1, 1, 1], min: 0, max: 100, options: [10, 20, 30, 40, 50],
|
||||
tags: { grouping: 'transform', col: true } }
|
||||
|
||||
```
|
||||
777
docs/dial/cli-details.md
Normal file
777
docs/dial/cli-details.md
Normal file
@@ -0,0 +1,777 @@
|
||||
1. [Home](/)
|
||||
1. [Dial](/dial)
|
||||
1. Cli Details
|
||||
|
||||
# Dial CLI Reference
|
||||
|
||||
|
||||
The `dial-cli` tool is a powerful command-line utility for generating UI schemas from TypeScript interfaces with Dial annotations. This page provides a complete reference for all CLI features and options.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
The dial-cli is now available as a standalone package for cleaner installation:
|
||||
|
||||
|
||||
```bash
|
||||
# Install globally (recommended for CLI tools)
|
||||
npm install -g @vuer-ai/dial-cli
|
||||
# or
|
||||
pnpm install -g @vuer-ai/dial-cli
|
||||
|
||||
# Or install as a dev dependency
|
||||
npm install --save-dev @vuer-ai/dial-cli
|
||||
# or
|
||||
pnpm add -D @vuer-ai/dial-cli
|
||||
|
||||
```
|
||||
|
||||
|
||||
Once installed, the CLI is available directly:
|
||||
|
||||
|
||||
```bash
|
||||
# If installed globally
|
||||
dial-cli --help
|
||||
|
||||
# If installed as dev dependency
|
||||
npx dial-cli --help
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Benefits of Standalone Package
|
||||
|
||||
|
||||
|
||||
- **Minimal dependencies** - Only includes what's needed for CLI operation (typescript, react-docgen-typescript)
|
||||
|
||||
- **No peer dependency warnings** - Doesn't require React, Tailwind, or other UI dependencies
|
||||
|
||||
- **Smaller install size** - ~32KB unpacked vs entire UI kit
|
||||
|
||||
- **Independent versioning** - CLI updates don't require UI kit updates
|
||||
|
||||
|
||||
|
||||
## Command Line Options
|
||||
|
||||
|
||||
### Basic Usage
|
||||
|
||||
|
||||
```bash
|
||||
dial-cli [options] <files...>
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Options Reference
|
||||
|
||||
|
||||
| Option | Alias | Description | Default |
|
||||
| `--output <dir>` | `-o` | Output directory for generated files | `./metadata` |
|
||||
| `--verbose` | | Enable verbose output with detailed information | `false` |
|
||||
| `--quiet` | | Suppress all output except errors | `false` |
|
||||
| `--remove` | | Remove generated metadata files | `false` |
|
||||
| `--ignore <props>` | `-i` | Comma-separated list of properties to ignore | - |
|
||||
| `--help` | `-h` | Display help information | - |
|
||||
| `--version` | `-v` | Display version information | - |
|
||||
|
||||
|
||||
|
||||
### Verbose Mode
|
||||
|
||||
|
||||
The `--verbose` flag enables detailed output and generates additional files:
|
||||
|
||||
|
||||
```bash
|
||||
dial-cli --verbose MyComponent.tsx
|
||||
|
||||
```
|
||||
|
||||
|
||||
In verbose mode, dial-cli generates:
|
||||
|
||||
|
||||
|
||||
- `schema.dial` - Combined dial schema for all components (main output)
|
||||
|
||||
- `debug/` - Debug directory containing:
|
||||
|
||||
- `component-raw.json` - Raw docgen output
|
||||
|
||||
- `component-combined.json` - Enhanced metadata
|
||||
|
||||
- `component-schemas.json` - Component schemas
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Debug files are organized in a `debug/` subdirectory to keep the main output clean. These files are only generated in verbose mode and are useful for debugging schema generation issues.
|
||||
|
||||
|
||||
### Quiet Mode
|
||||
|
||||
|
||||
The `--quiet` flag suppresses all output except errors, useful for CI/CD:
|
||||
|
||||
|
||||
```bash
|
||||
dial-cli --quiet src/components/*.tsx
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Remove Mode
|
||||
|
||||
|
||||
The `--remove` flag cleans up generated metadata files:
|
||||
|
||||
|
||||
```bash
|
||||
# Remove metadata for specific component
|
||||
dial-cli --remove MyComponent.tsx
|
||||
|
||||
# Remove all metadata in output directory
|
||||
dial-cli --remove --output ./metadata
|
||||
|
||||
```
|
||||
|
||||
|
||||
**Important:** The remove command cleans up both the main `schema.dial` file and any debug files in the `debug/` directory. It supports both the current debug directory structure and legacy file locations for backward compatibility.
|
||||
|
||||
|
||||
### Ignore Properties
|
||||
|
||||
|
||||
Exclude specific properties from schema generation:
|
||||
|
||||
|
||||
```bash
|
||||
# Ignore single property
|
||||
dial-cli --ignore ref MyComponent.tsx
|
||||
|
||||
# Ignore multiple properties
|
||||
dial-cli --ignore "ref,key,children" MyComponent.tsx
|
||||
|
||||
# Using short alias
|
||||
dial-cli -i "internalProp,debugValue" Component.tsx
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Output Files
|
||||
|
||||
|
||||
### Standard Output (Default)
|
||||
|
||||
|
||||
Without verbose mode, only generates:
|
||||
|
||||
|
||||
|
||||
- `schema.dial` - Combined schema for all processed components
|
||||
|
||||
|
||||
|
||||
### Verbose Output
|
||||
|
||||
|
||||
With `--verbose` flag, generates additional files per component:
|
||||
|
||||
|
||||
|
||||
- `[component]-schemas.json` - Component-specific schema
|
||||
|
||||
- `[component]-raw.json` - Raw react-docgen output
|
||||
|
||||
- `[component]-combined.json` - Enhanced metadata with dial annotations
|
||||
|
||||
- `schema.dial` - Combined schema (always generated)
|
||||
|
||||
|
||||
|
||||
## Group-Level Configuration
|
||||
|
||||
|
||||
You can apply configuration to entire groups of properties using interface-level JSDoc comments:
|
||||
|
||||
|
||||
### Using @dial-no-wrap
|
||||
|
||||
|
||||
The `@dial-no-wrap` annotation prevents line wrapping for all properties in a group:
|
||||
|
||||
|
||||
```tsx
|
||||
interface ComponentProps {
|
||||
/**
|
||||
* Layout configuration for transform properties
|
||||
* @dial transform @dial-no-wrap
|
||||
*/
|
||||
|
||||
/** @dial transform @dial-dtype vector3 */
|
||||
position: number[];
|
||||
|
||||
/** @dial transform @dial-dtype euler */
|
||||
rotation: number[];
|
||||
|
||||
/** @dial transform @dial-dtype vector3 */
|
||||
scale: number[];
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
In this example, all properties in the "transform" group (position, rotation, scale) will be displayed on a single line without wrapping.
|
||||
|
||||
|
||||
### Groups in Output Schema
|
||||
|
||||
|
||||
The CLI now generates a `groups` section in the output schema:
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"component": "ExampleBox",
|
||||
"schema": [...],
|
||||
"groups": [
|
||||
{
|
||||
"name": "transform",
|
||||
"noWrap": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
This allows the UI to apply group-specific styling and layout configuration.
|
||||
|
||||
|
||||
## Excluding Properties
|
||||
|
||||
|
||||
There are two ways to exclude properties from dial schema generation:
|
||||
|
||||
|
||||
### 1. Using @dial-ignore Annotation
|
||||
|
||||
|
||||
Add `@dial-ignore` to any property's JSDoc comment to exclude it from the generated schema:
|
||||
|
||||
|
||||
```tsx
|
||||
interface ComponentProps {
|
||||
/**
|
||||
* Public property - included in schema
|
||||
* @dial transform
|
||||
* @dial-dtype vector3
|
||||
*/
|
||||
position: number[];
|
||||
|
||||
/**
|
||||
* Internal state - excluded from schema
|
||||
* @dial-ignore
|
||||
*/
|
||||
_internalCache?: any;
|
||||
|
||||
/**
|
||||
* React ref - excluded from schema
|
||||
* @dial-ignore
|
||||
*/
|
||||
ref?: React.Ref<HTMLDivElement>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 2. Using CLI --ignore Option
|
||||
|
||||
|
||||
The `-i` or `--ignore` option allows you to exclude properties by name or pattern at runtime:
|
||||
|
||||
|
||||
```bash
|
||||
# Exclude specific properties
|
||||
dial-cli -i className -i style Component.tsx
|
||||
|
||||
# Exclude using comma-separated list
|
||||
dial-cli --ignore ref,key,id Component.tsx
|
||||
|
||||
# Exclude using glob patterns
|
||||
dial-cli -i "*Style" -i "on*" -i "_*" Component.tsx
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### Glob Pattern Examples
|
||||
|
||||
|
||||
| Pattern | Matches | Example Properties |
|
||||
| `*Style` | Ends with "Style" | `containerStyle`, `buttonStyle`, `textStyle` |
|
||||
| `on*` | Starts with "on" | `onClick`, `onChange`, `onSubmit` |
|
||||
| `_*` | Starts with underscore | `_internal`, `_cache`, `_private` |
|
||||
| `*Ref` | Ends with "Ref" | `inputRef`, `containerRef`, `buttonRef` |
|
||||
| `data*` | Starts with "data" | `dataSource`, `dataProvider`, `dataKey` |
|
||||
|
||||
|
||||
|
||||
## Class/Interface Level Suppression
|
||||
|
||||
|
||||
The `@dial-ignore` annotation can be used at the class or interface level to completely suppress dial schema generation for an entire component and all its properties:
|
||||
|
||||
|
||||
```tsx
|
||||
/**
|
||||
* Internal configuration component
|
||||
* @dial-ignore
|
||||
*/
|
||||
interface InternalSettingsProps {
|
||||
apiKey: string;
|
||||
debugMode: boolean;
|
||||
serverUrl: string;
|
||||
// All properties will be excluded from dial schema
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin-only component
|
||||
* @dial-ignore
|
||||
*/
|
||||
export const AdminPanel: FC<AdminPanelProps> = ({ ... }) => {
|
||||
// This component won't appear in dial UI
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
This is useful for:
|
||||
|
||||
|
||||
|
||||
- Internal/utility components that shouldn't be exposed in the UI
|
||||
|
||||
- Admin-only or developer-only components
|
||||
|
||||
- Components that are still under development
|
||||
|
||||
- Helper components that are only used internally
|
||||
|
||||
|
||||
|
||||
When `@dial-ignore` is used at the class/interface level:
|
||||
|
||||
|
||||
|
||||
- The entire component is skipped during dial schema generation
|
||||
|
||||
- No properties from that component will appear in the dial UI
|
||||
|
||||
- Any child properties or nested interfaces are also excluded
|
||||
|
||||
|
||||
|
||||
## Custom Property Labels
|
||||
|
||||
|
||||
The `@dial-label` annotation allows you to specify custom labels for properties that will be displayed in the UI instead of the default auto-generated labels from the property name:
|
||||
|
||||
|
||||
```tsx
|
||||
interface ComponentProps {
|
||||
/**
|
||||
* Position in 3D space
|
||||
* @dial transform
|
||||
* @dial-dtype vector3
|
||||
* @dial-label 3D Position
|
||||
*/
|
||||
pos3d: [number, number, number];
|
||||
|
||||
/**
|
||||
* Background color
|
||||
* @dial appearance
|
||||
* @dial-dtype color
|
||||
* @dial-label Background Color
|
||||
*/
|
||||
bgColor: string;
|
||||
|
||||
/**
|
||||
* Enable shadows
|
||||
* @dial-dtype boolean
|
||||
* @dial-label Enable Shadow Rendering
|
||||
*/
|
||||
shadowsOn: boolean;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
In the generated UI:
|
||||
|
||||
|
||||
|
||||
- `pos3d` will display as "3D Position" instead of "Pos3d"
|
||||
|
||||
- `bgColor` will display as "Background Color" instead of "BgColor"
|
||||
|
||||
- `shadowsOn` will display as "Enable Shadow Rendering" instead of "ShadowsOn"
|
||||
|
||||
|
||||
|
||||
This is particularly useful when property names follow coding conventions (camelCase, abbreviations) but you want more user-friendly labels in the UI.
|
||||
|
||||
|
||||
## Complete Example
|
||||
|
||||
|
||||
Here's a complete example showing exclusion methods and custom labels:
|
||||
|
||||
|
||||
### Component Definition
|
||||
|
||||
|
||||
```tsx
|
||||
// Box3D.tsx
|
||||
interface Box3DProps {
|
||||
/**
|
||||
* Box dimensions
|
||||
* @dial geometry
|
||||
* @dial-dtype vector3
|
||||
* @dial-min 0.1
|
||||
* @dial-max 10
|
||||
* @dial-step 0.1
|
||||
*/
|
||||
size: [number, number, number];
|
||||
|
||||
/**
|
||||
* Position in 3D space
|
||||
* @dial transform
|
||||
* @dial-dtype vector3
|
||||
* @dial-min -100
|
||||
* @dial-max 100
|
||||
* @dial-step 0.5
|
||||
*/
|
||||
position: [number, number, number];
|
||||
|
||||
/**
|
||||
* Material color
|
||||
* @dial appearance
|
||||
* @dial-dtype color
|
||||
*/
|
||||
color: string;
|
||||
|
||||
/**
|
||||
* Opacity level
|
||||
* @dial appearance
|
||||
* @dial-min 0
|
||||
* @dial-max 1
|
||||
* @dial-step 0.01
|
||||
*/
|
||||
opacity: number;
|
||||
|
||||
// Properties to exclude:
|
||||
|
||||
/**
|
||||
* Internal mesh reference
|
||||
* @dial-ignore
|
||||
*/
|
||||
_meshRef?: THREE.Mesh;
|
||||
|
||||
/**
|
||||
* React className - will be excluded via CLI
|
||||
*/
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* React style - will be excluded via CLI
|
||||
*/
|
||||
style?: React.CSSProperties;
|
||||
|
||||
/**
|
||||
* Click handler - will be excluded via CLI pattern
|
||||
*/
|
||||
onClick?: () => void;
|
||||
|
||||
/**
|
||||
* Change handler - will be excluded via CLI pattern
|
||||
*/
|
||||
onChange?: (value: any) => void;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
### CLI Commands
|
||||
|
||||
|
||||
```bash
|
||||
# Generate schema excluding React-specific props and handlers
|
||||
dial-cli -i className -i style -i "on*" Box3D.tsx
|
||||
|
||||
# Or using comma-separated list
|
||||
dial-cli --ignore className,style,onClick,onChange Box3D.tsx
|
||||
|
||||
# Exclude all private properties and event handlers
|
||||
dial-cli -i "_*" -i "on*" Box3D.tsx
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Generated Schema
|
||||
|
||||
|
||||
The generated schema will only include:
|
||||
|
||||
|
||||
|
||||
- `size` - Box dimensions control
|
||||
|
||||
- `position` - Position control
|
||||
|
||||
- `color` - Color picker
|
||||
|
||||
- `opacity` - Opacity slider
|
||||
|
||||
|
||||
|
||||
Excluded properties:
|
||||
|
||||
|
||||
|
||||
- `_meshRef` - Excluded by `@dial-ignore` annotation
|
||||
|
||||
- `className`, `style` - Excluded by CLI `-i` option
|
||||
|
||||
- `onClick`, `onChange` - Excluded by CLI pattern `"on*"`
|
||||
|
||||
|
||||
|
||||
## CLI Options Reference
|
||||
|
||||
|
||||
```bash
|
||||
dial-cli [options] <files...>
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Options
|
||||
|
||||
|
||||
| Option | Short | Description | Example |
|
||||
| `--output <dir>` | `-o` | Output directory for generated files | `-o ./schemas` |
|
||||
| `--ignore <prop>` | `-i` | Properties to exclude (supports glob) | `-i className -i "on*"` |
|
||||
| `--verbose` | | Enable verbose output | `--verbose` |
|
||||
| `--quiet` | | Suppress output except errors | `--quiet` |
|
||||
| `--help` | `-h` | Display help message | `--help` |
|
||||
| `--version` | `-v` | Display version | `--version` |
|
||||
|
||||
|
||||
|
||||
## Output Files
|
||||
|
||||
|
||||
The CLI generates files with a clean directory structure:
|
||||
|
||||
|
||||
**Main Output:**
|
||||
|
||||
|
||||
|
||||
- **`schema.dial`** - Combined schemas for all components, ready for UI generation
|
||||
|
||||
|
||||
|
||||
**Debug Output (verbose mode only):**
|
||||
|
||||
|
||||
|
||||
- **`debug/component-raw.json`** - Raw AST and JSDoc extraction
|
||||
|
||||
- **`debug/component-combined.json`** - Enhanced metadata with dial schemas
|
||||
|
||||
- **`debug/component-schemas.json`** - Individual component schemas
|
||||
|
||||
|
||||
|
||||
This structure keeps your main output directory clean while providing detailed debug information when needed.
|
||||
|
||||
|
||||
## Best Practices
|
||||
|
||||
|
||||
### 1. Combine Both Exclusion Methods
|
||||
|
||||
|
||||
Use `@dial-ignore` for properties that should never be exposed in the UI:
|
||||
|
||||
|
||||
```tsx
|
||||
/**
|
||||
* @dial-ignore
|
||||
*/
|
||||
_internalState?: any;
|
||||
|
||||
```
|
||||
|
||||
|
||||
Use CLI `--ignore` for context-specific exclusions:
|
||||
|
||||
|
||||
```bash
|
||||
# Exclude React-specific props when generating for non-React environments
|
||||
dial-cli -i className -i style Component.tsx
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 2. Use Patterns for Consistency
|
||||
|
||||
|
||||
Create consistent naming conventions and use patterns:
|
||||
|
||||
|
||||
```bash
|
||||
# Exclude all private properties, refs, and handlers
|
||||
dial-cli -i "_*" -i "*Ref" -i "on*" Component.tsx
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 3. Create Build Scripts
|
||||
|
||||
|
||||
Add scripts to your `package.json`:
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"dial:generate": "dial-cli -o ./schemas -i className,style,key,ref src/components/*.tsx",
|
||||
"dial:generate:clean": "dial-cli -o ./schemas -i '_*,on*,*Ref,className,style' src/components/*.tsx"
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 4. Document Excluded Properties
|
||||
|
||||
|
||||
When using `@dial-ignore`, add a comment explaining why:
|
||||
|
||||
|
||||
```tsx
|
||||
/**
|
||||
* Internal cache for performance optimization
|
||||
* Not meant to be modified by users
|
||||
* @dial-ignore
|
||||
*/
|
||||
_cache?: Map<string, any>;
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Integration Example
|
||||
|
||||
|
||||
Here's how to use the generated schemas in your application:
|
||||
|
||||
|
||||
```tsx
|
||||
import { DialProvider, DialPanel } from '@vuer-ai/vuer-uikit';
|
||||
import allSchemas from './schemas/schema.dial';
|
||||
|
||||
function App() {
|
||||
const [props, setProps] = useState({
|
||||
size: [1, 1, 1],
|
||||
position: [0, 0, 0],
|
||||
color: '#ff0000',
|
||||
opacity: 1
|
||||
});
|
||||
|
||||
const handleValueChange = (name: string, value: any) => {
|
||||
setProps(prev => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
// Get the specific component schema from the combined schema file
|
||||
const box3DSchema = allSchemas.find(s => s.component === 'Box3D');
|
||||
|
||||
return (
|
||||
<DialProvider schemas={[box3DSchema]} onValueChange={handleValueChange}>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<Canvas>
|
||||
<Box3D {...props} />
|
||||
</Canvas>
|
||||
<DialPanel schemas={[box3DSchema]} />
|
||||
</div>
|
||||
</DialProvider>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
### Properties Not Being Excluded
|
||||
|
||||
|
||||
|
||||
1. **Check annotation syntax**: Ensure `@dial-ignore` is on its own line
|
||||
|
||||
1. **Check pattern syntax**: Use quotes for patterns with wildcards: `-i "*Style"`
|
||||
|
||||
1. **Check property names**: Property names are case-sensitive
|
||||
|
||||
|
||||
|
||||
### Generated Schema Is Empty
|
||||
|
||||
|
||||
|
||||
1. Ensure your interface has JSDoc comments with `@dial` annotations
|
||||
|
||||
1. Check that the TypeScript file exports the interface
|
||||
|
||||
1. Verify the file path is correct
|
||||
|
||||
|
||||
|
||||
### Build Errors
|
||||
|
||||
|
||||
|
||||
1. Ensure `@vuer-ai/dial-cli` is installed
|
||||
|
||||
1. Try using the full path: `node_modules/.bin/dial-cli` (if installed locally)
|
||||
|
||||
1. Check Node.js version (requires Node 14+)
|
||||
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
|
||||
The dial-cli tool provides flexible property exclusion through:
|
||||
|
||||
|
||||
|
||||
- **`@dial-ignore` annotation** - Permanent, code-level exclusion
|
||||
|
||||
- **`--ignore` CLI option** - Runtime, context-specific exclusion
|
||||
|
||||
- **Glob patterns** - Flexible pattern-based exclusion
|
||||
|
||||
|
||||
|
||||
Combine these features to create clean, focused UI schemas that expose only the properties users need to control.
|
||||
187
docs/dial/controlled-dials.md
Normal file
187
docs/dial/controlled-dials.md
Normal file
@@ -0,0 +1,187 @@
|
||||
1. [Home](/)
|
||||
1. [Dial](/dial)
|
||||
1. Controlled Dials
|
||||
|
||||
# Controlled Dials
|
||||
|
||||
|
||||
The `DialProvider` component supports both controlled and uncontrolled modes, giving you flexibility in how you manage dial state.
|
||||
|
||||
|
||||
## Simple 3D Object Example
|
||||
|
||||
|
||||
transformPositionxyzRenderOrderResetExamplePosition: [0.0, 5.0, 0.0]Render Order: 0```jsx
|
||||
const schemas = [
|
||||
{
|
||||
name: "position",
|
||||
dtype: "vector3",
|
||||
value: [1, 2, 3],
|
||||
//...
|
||||
}
|
||||
];
|
||||
|
||||
const [values, setValues] = useState({
|
||||
position: [0, 5, 0],
|
||||
renderOrder: 0
|
||||
});
|
||||
|
||||
|
||||
const handleChange = (name, value) => {
|
||||
setValues(prev => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
return (
|
||||
<DialProvider
|
||||
schemas={schemas}
|
||||
values={values}
|
||||
onValueChange={handleChange}
|
||||
>
|
||||
<DialPanel schemas={schemas} />
|
||||
</DialProvider>
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Full Schema Example from JSON
|
||||
|
||||
|
||||
FooBar Object PropertiestransformPositionxyzRotationxyzScalexyzdisplayVisibleOpacityReset All```
|
||||
{
|
||||
"position": [
|
||||
0,
|
||||
5,
|
||||
0
|
||||
],
|
||||
"rotation": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"scale": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"visible": true,
|
||||
"opacity": 1
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
// Schema data structure (could be loaded from JSON)
|
||||
const schemaData = {
|
||||
"FooBar": {
|
||||
"schemas": [
|
||||
{
|
||||
"name": "position",
|
||||
"dtype": "vector3",
|
||||
"value": [0, 5, 0],
|
||||
"min": -10,
|
||||
"max": 10,
|
||||
"step": 0.1,
|
||||
"tags": {
|
||||
"grouping": "transform",
|
||||
"noWrap": true
|
||||
}
|
||||
},
|
||||
// ... more schemas
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize values from schemas
|
||||
const [values, setValues] = useState(() => {
|
||||
const initial = {};
|
||||
schemaData.FooBar.schemas.forEach(schema => {
|
||||
initial[schema.name] = schema.value;
|
||||
});
|
||||
return initial;
|
||||
});
|
||||
|
||||
const handleChange = (name, value) => {
|
||||
setValues(prev => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
return (
|
||||
<DialProvider
|
||||
schemas={schemaData.FooBar.schemas}
|
||||
values={values}
|
||||
onValueChange={handleChange}
|
||||
>
|
||||
<DialPanel schemas={schemaData.FooBar.schemas} />
|
||||
</DialProvider>
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
|
||||
## API Reference
|
||||
|
||||
|
||||
### DialProvider Props
|
||||
|
||||
|
||||
| Prop | Type | Description |
|
||||
| `schemas` | `DialSchema[]` | Array of dial schemas defining the dials |
|
||||
| `values` | `Record<string, DialValue>` | Current values for controlled mode |
|
||||
| `initialValues` | `Record<string, DialValue>` | Initial values for uncontrolled mode |
|
||||
| `onValueChange` | `(name: string, value: DialValue) => void` | Callback when values change |
|
||||
| `children` | `ReactNode` | Child components |
|
||||
|
||||
|
||||
|
||||
### Schema Structure
|
||||
|
||||
|
||||
```typescript
|
||||
interface DialSchema {
|
||||
name: string; // Unique identifier
|
||||
dtype: string; // "number", "boolean", "vector3", "select", etc.
|
||||
value?: any; // Default value
|
||||
min?: number; // For number/vector inputs
|
||||
max?: number; // For number/vector inputs
|
||||
step?: number; // Step size for number/vector inputs
|
||||
options?: string[]; // For select inputs
|
||||
tags?: {
|
||||
grouping?: string; // Group related controls
|
||||
noWrap?: boolean; // Prevent wrapping in group
|
||||
// ... other tags
|
||||
};
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Common Data Types
|
||||
|
||||
|
||||
|
||||
- **`number`**: Single numeric value
|
||||
|
||||
- **`boolean`**: True/false toggle
|
||||
|
||||
- **`vector3`**: 3D vector [x, y, z]
|
||||
|
||||
- **`select`**: Dropdown with options
|
||||
|
||||
- **`string`**: Text input
|
||||
|
||||
|
||||
|
||||
### Grouping with Tags
|
||||
|
||||
|
||||
The `tags.grouping` property allows you to organize related controls together. Controls with the same `grouping` value will be visually grouped in the UI.
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"tags": {
|
||||
"grouping": "transform",
|
||||
"noWrap": true
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
129
docs/dial/getting-started.md
Normal file
129
docs/dial/getting-started.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Getting Started with Dial
|
||||
|
||||
## Overview
|
||||
|
||||
Dial is a schema-driven system that "parses JSDoc annotations in your TypeScript/React components" and then "generates UI schemas that describe controls and their properties."
|
||||
|
||||
The system automates interactive control creation from TypeScript interfaces, supporting complex types like vectors, tuples, and nested objects.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the CLI tool using your package manager:
|
||||
|
||||
```bash
|
||||
# Global installation
|
||||
pnpm install -g @vuer-ai/dial-cli
|
||||
|
||||
# Or as a dev dependency
|
||||
pnpm add -D @vuer-ai/dial-cli
|
||||
```
|
||||
|
||||
## Basic Workflow
|
||||
|
||||
### Step 1: Annotate Your Component
|
||||
|
||||
Add JSDoc comments with `@dial` tags to your TypeScript component properties:
|
||||
|
||||
```typescript
|
||||
interface BoxProps {
|
||||
/**
|
||||
* Box dimensions
|
||||
* @dial geometry
|
||||
* @dial-dtype vector3
|
||||
* @dial-min 0.1
|
||||
* @dial-max 10
|
||||
* @dial-step 0.1
|
||||
*/
|
||||
size: [number, number, number];
|
||||
|
||||
/**
|
||||
* Material color
|
||||
* @dial appearance
|
||||
* @dial-dtype color
|
||||
*/
|
||||
color: string;
|
||||
|
||||
/**
|
||||
* Visibility toggle
|
||||
* @dial visibility
|
||||
* @dial-dtype boolean
|
||||
*/
|
||||
visible: boolean;
|
||||
|
||||
/**
|
||||
* Opacity level
|
||||
* @dial appearance
|
||||
* @dial-dtype number
|
||||
* @dial-min 0
|
||||
* @dial-max 1
|
||||
* @dial-step 0.01
|
||||
*/
|
||||
opacity: number;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Generate the Schema
|
||||
|
||||
```bash
|
||||
dial-cli Box.tsx
|
||||
# Outputs: metadata/schema.dial
|
||||
```
|
||||
|
||||
### Step 3: Use the Generated Schema
|
||||
|
||||
```typescript
|
||||
import { DialProvider, DialPanel } from '@vuer-ai/vuer-uikit';
|
||||
import allSchemas from './metadata/schema.dial';
|
||||
|
||||
function App() {
|
||||
const boxSchema = allSchemas.find(s => s.component === 'Box');
|
||||
|
||||
return (
|
||||
<DialProvider schemas={[boxSchema]}>
|
||||
<DialPanel schemas={[boxSchema]} />
|
||||
</DialProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Core Annotation Tags
|
||||
|
||||
| Tag | Purpose |
|
||||
|-----|---------|
|
||||
| `@dial <group>` | Groups related properties together |
|
||||
| `@dial-dtype <type>` | Specifies control data type |
|
||||
| `@dial-min/max/step` | Sets numeric constraints |
|
||||
| `@dial-icon <name>` | Adds Lucide icon to control |
|
||||
| `@dial-ignore` | Excludes property from schema |
|
||||
|
||||
## Supported Data Types
|
||||
|
||||
| Type | Purpose | Example |
|
||||
|------|---------|---------|
|
||||
| `number` | Basic numeric input | `42` |
|
||||
| `boolean` | Toggle switch | `true/false` |
|
||||
| `string` | Text input | `"hello"` |
|
||||
| `color` | Color picker | `"#ff0000"` |
|
||||
| `vector3` | 3D vector | `[1, 2, 3]` |
|
||||
| `euler` | Rotation angles | `[0, 90, 180]` |
|
||||
| `select` | Dropdown menu | `"option1"` |
|
||||
|
||||
## Property Grouping
|
||||
|
||||
Organize properties by adding the same group name across multiple fields:
|
||||
|
||||
```typescript
|
||||
interface Props {
|
||||
/** @dial transform */
|
||||
position: [number, number, number];
|
||||
|
||||
/** @dial transform */
|
||||
rotation: [number, number, number];
|
||||
|
||||
/** @dial appearance */
|
||||
color: string;
|
||||
|
||||
/** @dial appearance */
|
||||
metalness: number;
|
||||
}
|
||||
```
|
||||
1113
docs/dial/input-types.md
Normal file
1113
docs/dial/input-types.md
Normal file
File diff suppressed because it is too large
Load Diff
60
docs/dial/overview.md
Normal file
60
docs/dial/overview.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Dial: A Schema-Driven Menu System
|
||||
|
||||
## Overview
|
||||
|
||||
Dial represents a system that generates user interface controls automatically from TypeScript interfaces using JSDoc annotations. This approach eliminates manual UI creation by defining control properties directly in code comments.
|
||||
|
||||
## Core Annotation Syntax
|
||||
|
||||
The system uses JSDoc comments to specify UI behavior:
|
||||
|
||||
**Grouping Controls:**
|
||||
- `@dial <grouping>` organizes related properties
|
||||
- `@dial <grouping> @dial-no-wrap` applies group-level settings
|
||||
|
||||
**Property Configuration:**
|
||||
- `@dial-<property> <value>` sets control attributes using hyphen notation
|
||||
- `@dial-col-<n>` arranges elements in n-column layouts
|
||||
- `@dial-dtype <type>` specifies data types (vector3, euler, boolean, int, etc.)
|
||||
- `@dial-min <number>` and `@dial-max <number>` establish value bounds
|
||||
- `@dial-step <number>` defines increment sizes
|
||||
- `@dial-options [...]` provides preset values
|
||||
- `@dial-icon <name>` assigns Lucide icon names
|
||||
- `@dial-label <text>` customizes property display names
|
||||
- `@dial-ignore` excludes properties from schema generation
|
||||
|
||||
## Example Implementation
|
||||
|
||||
A TypeScript interface can be annotated to define a 3D box geometry with transformation controls:
|
||||
|
||||
```typescript
|
||||
interface ExampleBoxProps {
|
||||
args: ExampleBoxArgs;
|
||||
position: number[] | null;
|
||||
rotation: number[] | null;
|
||||
scale: number[] | null;
|
||||
hide: boolean;
|
||||
alphaTest: boolean;
|
||||
depthTest: boolean;
|
||||
renderOrder: number;
|
||||
_internalState?: any;
|
||||
}
|
||||
```
|
||||
|
||||
Annotations specify constraints, groupings, and visual representation for each property.
|
||||
|
||||
## Key Features
|
||||
|
||||
**Schema-Driven Architecture:** UI controls generate automatically from annotated TypeScript code, eliminating manual interface creation.
|
||||
|
||||
**Grouped Layout System:** Properties organize automatically into sections based on their grouping annotations.
|
||||
|
||||
**Integrated State Management:** Built-in state tracking with value change callbacks enables seamless application integration.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- **Tutorial:** Step-by-step guide for using dial-cli
|
||||
- **Input Types:** Complete reference of supported input types
|
||||
- **Controlled Dials:** Advanced usage patterns
|
||||
- **API Notes:** Detailed API reference
|
||||
- **CLI Details:** Advanced command-line options
|
||||
443
docs/dial/testing.md
Normal file
443
docs/dial/testing.md
Normal file
@@ -0,0 +1,443 @@
|
||||
1. [Home](/)
|
||||
1. [Dial](/dial)
|
||||
1. Testing
|
||||
|
||||
# Testing Dial Annotations
|
||||
|
||||
|
||||
|
||||
Testing Guidedial-cli[v0.0.22](https://www.npmjs.com/package/@vuer-ai/dial-cli/v/0.0.22)
|
||||
This guide covers testing strategies for Dial annotations, running the dial-cli test suite, and contributing to dial-cli development.
|
||||
|
||||
|
||||
## Testing Your Dial Annotations
|
||||
|
||||
|
||||
### 1. Validate Generated Schemas
|
||||
|
||||
|
||||
After annotating your components, validate the generated schemas:
|
||||
|
||||
|
||||
```bash
|
||||
# Generate with verbose output for inspection
|
||||
dial-cli --verbose MyComponent.tsx
|
||||
|
||||
# Check the generated files
|
||||
cat metadata/schema.dial | jq '.'
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 2. Common Validation Checks
|
||||
|
||||
|
||||
#### Check Property Types
|
||||
|
||||
|
||||
Ensure properties have the correct dtype:
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "position",
|
||||
"dtype": "vector3", // Should match your annotation
|
||||
"value": [0, 0, 0]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### Verify Constraints
|
||||
|
||||
|
||||
Check min/max/step values are applied:
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "opacity",
|
||||
"dtype": "number",
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"step": 0.01
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### Validate Grouping
|
||||
|
||||
|
||||
Ensure properties are correctly grouped:
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "rotation",
|
||||
"dtype": "euler",
|
||||
"tags": {
|
||||
"grouping": "transform"
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 3. Test Type Inheritance
|
||||
|
||||
|
||||
When using interface inheritance or type intersections:
|
||||
|
||||
|
||||
```bash
|
||||
# Generate schema for inherited types
|
||||
dial-cli --verbose ExtendedComponent.tsx
|
||||
|
||||
# Verify all parent properties are included
|
||||
jq '.[] | select(.component == "ExtendedComponent") | .schemas[].name' \
|
||||
metadata/schema.dial
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 4. Automated Testing
|
||||
|
||||
|
||||
Create a test script to validate your schemas:
|
||||
|
||||
|
||||
```javascript
|
||||
// test-dial-schemas.js
|
||||
const fs = require('fs');
|
||||
const schema = require('./metadata/schema.dial');
|
||||
|
||||
describe('Dial Schemas', () => {
|
||||
test('should have required properties', () => {
|
||||
const component = schema.find(c => c.component === 'MyComponent');
|
||||
expect(component).toBeDefined();
|
||||
|
||||
const propNames = component.schemas.map(s => s.name);
|
||||
expect(propNames).toContain('position');
|
||||
expect(propNames).toContain('rotation');
|
||||
expect(propNames).toContain('scale');
|
||||
});
|
||||
|
||||
test('should have correct types', () => {
|
||||
const component = schema.find(c => c.component === 'MyComponent');
|
||||
const position = component.schemas.find(s => s.name === 'position');
|
||||
|
||||
expect(position.dtype).toBe('vector3');
|
||||
expect(position.value).toHaveLength(3);
|
||||
});
|
||||
|
||||
test('should inherit group configurations', () => {
|
||||
const component = schema.find(c => c.component === 'AnimatedBox');
|
||||
const duration = component.schemas.find(s => s.name === 'duration');
|
||||
|
||||
expect(duration.tags?.noWrap).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Running Dial CLI Tests
|
||||
|
||||
|
||||
The dial-cli package includes a comprehensive test suite for validation.
|
||||
|
||||
|
||||
### Prerequisites
|
||||
|
||||
|
||||
```bash
|
||||
# Navigate to dial-cli directory
|
||||
cd packages/vuer-uikit/dial-cli
|
||||
|
||||
# Install dependencies
|
||||
pnpm install
|
||||
|
||||
# Build the CLI
|
||||
pnpm build
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Running Tests
|
||||
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
pnpm test
|
||||
|
||||
# Run tests in watch mode for development
|
||||
pnpm test:watch
|
||||
|
||||
# Run tests with coverage
|
||||
pnpm test:coverage
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Test Output
|
||||
|
||||
|
||||
Successful test run shows:
|
||||
|
||||
|
||||
```
|
||||
PASS spec/inheritance.test.ts
|
||||
dial-cli inheritance tests
|
||||
Interface Inheritance
|
||||
✓ should resolve properties from extended interfaces
|
||||
✓ should handle deep inheritance chains
|
||||
Type Inheritance
|
||||
✓ should resolve properties from type intersections
|
||||
✓ should handle utility types (Pick, Omit, Partial)
|
||||
Mixed Inheritance
|
||||
✓ should handle interface extending type
|
||||
✓ should handle type intersecting interface
|
||||
Group configurations
|
||||
✓ should inherit group-level @dial-no-wrap
|
||||
dial-cli remove functionality
|
||||
✓ should remove specific component metadata
|
||||
✓ should remove all metadata files
|
||||
|
||||
Test Suites: 1 passed, 1 total
|
||||
Tests: 9 passed, 9 total
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Contributing to Dial CLI
|
||||
|
||||
|
||||
### Development Setup
|
||||
|
||||
|
||||
|
||||
1. **Fork and Clone**
|
||||
|
||||
|
||||
|
||||
```bash
|
||||
git clone https://github.com/your-username/vuer-uikit.git
|
||||
cd vuer-uikit/packages/vuer-uikit/dial-cli
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
1. **Install Dependencies**
|
||||
|
||||
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
1. **Start Development Mode**
|
||||
|
||||
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
# This watches for changes and rebuilds automatically
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Writing New Tests
|
||||
|
||||
|
||||
Tests are located in the `spec/` directory:
|
||||
|
||||
|
||||
```
|
||||
spec/
|
||||
├── inheritance.test.ts # Main test suite
|
||||
├── fixtures/ # Test TypeScript files
|
||||
│ ├── InterfaceInheritance.tsx
|
||||
│ ├── TypeInheritance.tsx
|
||||
│ └── MixedInheritance.tsx
|
||||
└── outputs/ # Expected outputs for comparison
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### Adding a Test Case
|
||||
|
||||
|
||||
```typescript
|
||||
// spec/inheritance.test.ts
|
||||
test('should handle new feature', () => {
|
||||
const fixture = join(FIXTURES_DIR, 'NewFeature.tsx');
|
||||
|
||||
// Generate schema
|
||||
execSync(`node "${DIAL_CLI}" --verbose --output "${OUTPUT_DIR}" "${fixture}"`);
|
||||
|
||||
// Read and validate
|
||||
const schemaPath = join(OUTPUT_DIR, 'schema.dial');
|
||||
const schemas = JSON.parse(readFileSync(schemaPath, 'utf-8'));
|
||||
|
||||
// Assertions
|
||||
expect(schemas).toBeDefined();
|
||||
expect(schemas[0].component).toBe('NewFeature');
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### Creating Test Fixtures
|
||||
|
||||
|
||||
```tsx
|
||||
// spec/fixtures/NewFeature.tsx
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Test component for new feature
|
||||
* @dial config @dial-new-feature
|
||||
*/
|
||||
interface NewFeatureProps {
|
||||
/**
|
||||
* Test property
|
||||
* @dial config
|
||||
* @dial-dtype string
|
||||
* @dial-new-annotation value
|
||||
*/
|
||||
testProp: string;
|
||||
}
|
||||
|
||||
export const NewFeature: React.FC<NewFeatureProps> = ({ testProp }) => {
|
||||
return <div>{testProp}</div>;
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Running Tests During Development
|
||||
|
||||
|
||||
```bash
|
||||
# Run specific test file
|
||||
pnpm test inheritance.test.ts
|
||||
|
||||
# Run tests matching pattern
|
||||
pnpm test -- --testNamePattern="type intersection"
|
||||
|
||||
# Run with debugging
|
||||
NODE_OPTIONS="--inspect" pnpm test
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Debugging Tips
|
||||
|
||||
|
||||
|
||||
1. **Use Verbose Output**
|
||||
|
||||
|
||||
|
||||
```bash
|
||||
dial-cli --verbose TestComponent.tsx
|
||||
# Check all generated files for debugging
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
1. **Inspect AST**
|
||||
Add debug logging to see TypeScript AST:
|
||||
|
||||
|
||||
|
||||
```typescript
|
||||
console.log('AST Node:', node.kind, node.getText());
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
1. **Test Individual Components**
|
||||
|
||||
|
||||
|
||||
```bash
|
||||
# Test a specific fixture
|
||||
node dist/dial-cli.js --verbose spec/fixtures/TypeInheritance.tsx
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Common Issues and Solutions
|
||||
|
||||
|
||||
### Issue: Properties Not Appearing in Schema
|
||||
|
||||
|
||||
**Cause:** Missing `@dial` annotation
|
||||
**Solution:** Ensure property has at least one `@dial` tag
|
||||
|
||||
|
||||
```tsx
|
||||
// Won't appear in schema
|
||||
/** Just a comment */
|
||||
prop: string;
|
||||
|
||||
// Will appear in schema
|
||||
/** @dial control */
|
||||
prop: string;
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Issue: Type Inheritance Not Working
|
||||
|
||||
|
||||
**Cause:** Types not properly exported or resolved
|
||||
**Solution:** Ensure all types are exported and accessible
|
||||
|
||||
|
||||
```tsx
|
||||
// Export types for proper resolution
|
||||
export type BaseType = { ... };
|
||||
export interface ExtendedInterface extends BaseType { ... }
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Issue: Group Configuration Not Applied
|
||||
|
||||
|
||||
**Cause:** Group-level annotations at wrong position
|
||||
**Solution:** Place group annotations in interface/type JSDoc
|
||||
|
||||
|
||||
```tsx
|
||||
/**
|
||||
* @dial transform @dial-no-wrap // Correct position
|
||||
*/
|
||||
interface Props {
|
||||
// Properties here inherit the configuration
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Next Steps
|
||||
|
||||
|
||||
|
||||
- Review [Type Inheritance](/dial/type-inheritance) patterns
|
||||
|
||||
- Check [CLI Reference](/dial/cli-reference) for all options
|
||||
|
||||
- See [Examples](/dial/examples) for real-world usage
|
||||
|
||||
- Read [Troubleshooting](/dial/cli-reference/troubleshooting) for common issues
|
||||
374
docs/dial/tutorial.md
Normal file
374
docs/dial/tutorial.md
Normal file
@@ -0,0 +1,374 @@
|
||||
1. [Home](/)
|
||||
1. [Dial](/dial)
|
||||
1. Tutorial
|
||||
|
||||
# Dial Tutorial: Using dial-cli
|
||||
|
||||
|
||||
|
||||
Dial CLI Tutorialdial-cli[v0.0.22](https://www.npmjs.com/package/@vuer-ai/dial-cli/v/0.0.22)
|
||||
This tutorial will guide you through using the dial-cli tool to generate UI controls from TypeScript interfaces with Dial annotations.
|
||||
|
||||
|
||||
## Using the dial-cli Tool
|
||||
|
||||
|
||||
The `dial-cli` is now available as a standalone package for generating Dial schemas from TypeScript files, providing a cleaner installation experience without UI dependencies.
|
||||
|
||||
|
||||
### Installation
|
||||
|
||||
|
||||
```bash
|
||||
# Install globally (recommended for CLI tools)
|
||||
npm install -g @vuer-ai/dial-cli
|
||||
# or
|
||||
pnpm install -g @vuer-ai/dial-cli
|
||||
|
||||
# Check CLI is available
|
||||
dial-cli --help
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Basic Usage
|
||||
|
||||
|
||||
```bash
|
||||
# Generate schemas from a TypeScript file
|
||||
dial-cli <input-file> [input-file2...]
|
||||
|
||||
# Examples:
|
||||
dial-cli ./src/components/Box.tsx
|
||||
# Creates schema.dial in ./metadata directory
|
||||
|
||||
dial-cli ./src/components/Box.tsx -o ./schemas
|
||||
# Outputs to specified directory
|
||||
|
||||
# Process multiple files
|
||||
dial-cli Component1.tsx Component2.tsx
|
||||
|
||||
# Specify output directory
|
||||
dial-cli -o ./metadata MyComponent.tsx
|
||||
|
||||
```
|
||||
|
||||
|
||||
### What the CLI Does
|
||||
|
||||
|
||||
The dial-cli tool will:
|
||||
|
||||
|
||||
|
||||
1. Parse your TypeScript file using the TypeScript compiler API
|
||||
|
||||
1. Extract all interfaces and types with Dial annotations
|
||||
|
||||
1. Process JSDoc comments following the `@dial` convention
|
||||
|
||||
1. Generate JSON schema files that can be directly used with DialPanel
|
||||
|
||||
|
||||
|
||||
### Output Files
|
||||
|
||||
|
||||
The CLI generates files with a clean directory structure:
|
||||
|
||||
|
||||
**Main Output:**
|
||||
|
||||
|
||||
|
||||
- `schema.dial` - Combined schemas for all components, ready for UI generation
|
||||
|
||||
|
||||
|
||||
**Debug Output (verbose mode only):**
|
||||
|
||||
|
||||
|
||||
- `debug/component-raw.json` - Raw output from react-docgen-typescript
|
||||
|
||||
- `debug/component-combined.json` - Enhanced metadata with dial schema information
|
||||
|
||||
- `debug/component-schemas.json` - Individual component schemas for debugging
|
||||
|
||||
|
||||
|
||||
### Local Script
|
||||
|
||||
|
||||
This documentation includes a convenience script for generating metadata:
|
||||
|
||||
|
||||
```bash
|
||||
# From the dial directory
|
||||
./generate-dial-metadata.sh
|
||||
|
||||
```
|
||||
|
||||
|
||||
This will process the `BoxExample.tsx` file and output metadata to the `metadata/` directory.
|
||||
|
||||
|
||||
## Using Generated Schemas
|
||||
|
||||
|
||||
Once you've generated schemas using dial-cli, you can use them in your application.
|
||||
The Dial system consists of three main components:
|
||||
|
||||
|
||||
|
||||
1. **DialProvider** - Manages state for all controls
|
||||
|
||||
1. **DialPanel** - Converts schemas to UI components
|
||||
|
||||
1. **Input Components** - Individual control types (number, vector, boolean, etc.)
|
||||
|
||||
|
||||
|
||||
### Component API
|
||||
|
||||
|
||||
**DialPanel** accepts the following props:
|
||||
|
||||
|
||||
```tsx
|
||||
interface DialPanelProps {
|
||||
schemas: DialSchema[]; // Array of control schemas
|
||||
groups?: DialGroupConfig[]; // Optional group configurations
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
Usage:
|
||||
|
||||
|
||||
```tsx
|
||||
// Basic usage with just schemas
|
||||
<DialPanel schemas={schemas} />
|
||||
|
||||
// With group configuration for layout control
|
||||
<DialPanel schemas={schemas} groups={groups} />
|
||||
|
||||
```
|
||||
|
||||
|
||||
### TypeScript Interfaces
|
||||
|
||||
|
||||
The Dial system uses the following main interfaces:
|
||||
|
||||
|
||||
```tsx
|
||||
// Schema for individual controls
|
||||
interface DialSchema {
|
||||
name: string;
|
||||
dtype: string;
|
||||
value?: DialValue;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
options?: Array<string | number | { label: string; value: string | number }>;
|
||||
// ... other properties
|
||||
tags?: {
|
||||
grouping?: string;
|
||||
col?: boolean | number;
|
||||
row?: number;
|
||||
layout?: string;
|
||||
labelPosition?: LabelPositionT;
|
||||
noWrap?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
// Group configuration for styling and layout
|
||||
interface DialGroupConfig {
|
||||
name: string;
|
||||
noWrap?: boolean;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
// Complete schema with groups (output from dial-cli)
|
||||
interface DialSchemaGroup {
|
||||
component: string;
|
||||
schema: DialSchema[];
|
||||
groups?: DialGroupConfig[];
|
||||
}
|
||||
|
||||
// Valid value types
|
||||
type DialValue = string | number | boolean | number[] | string[] | null | undefined;
|
||||
|
||||
```
|
||||
|
||||
|
||||
```tsx
|
||||
import { DialProvider, DialPanel } from './dial';
|
||||
|
||||
const schemas = [
|
||||
{
|
||||
name: 'position',
|
||||
dtype: 'vector3',
|
||||
value: [0, 0, 0],
|
||||
min: -10,
|
||||
max: 10,
|
||||
tags: { grouping: 'transform', col: true }
|
||||
},
|
||||
// ... more schemas
|
||||
];
|
||||
|
||||
function MyComponent() {
|
||||
const handleValueChange = (name, value) => {
|
||||
console.log(`${name} changed to`, value);
|
||||
};
|
||||
|
||||
return (
|
||||
<DialProvider
|
||||
schemas={schemas}
|
||||
onValueChange={handleValueChange}
|
||||
>
|
||||
<DialPanel schemas={schemas} />
|
||||
</DialProvider>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Complete Example with dial-cli
|
||||
|
||||
|
||||
|
||||
1. **Create a TypeScript file with Dial annotations:**
|
||||
|
||||
|
||||
|
||||
```tsx
|
||||
// Box.tsx
|
||||
interface BoxProps {
|
||||
/**
|
||||
* Transform properties displayed on single line
|
||||
* @dial transform @dial-no-wrap
|
||||
*/
|
||||
|
||||
/** @dial transform @dial-dtype vector3 */
|
||||
position: [number, number, number];
|
||||
|
||||
/** @dial transform @dial-dtype euler */
|
||||
rotation: [number, number, number];
|
||||
|
||||
/**
|
||||
* Box dimensions
|
||||
* @dial geometry
|
||||
* @dial-dtype vector3
|
||||
* @dial-min 0.1
|
||||
* @dial-max 10
|
||||
* @dial-step 0.1
|
||||
*/
|
||||
size: [number, number, number];
|
||||
|
||||
/**
|
||||
* Box color
|
||||
* @dial appearance
|
||||
* @dial-dtype color
|
||||
*/
|
||||
color: string;
|
||||
}
|
||||
|
||||
export const Box: React.FC<BoxProps> = ({ size, position, rotation, color }) => {
|
||||
// Component implementation
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
1. **Generate the schema:**
|
||||
|
||||
|
||||
|
||||
```bash
|
||||
dial-cli Box.tsx -o ./schemas
|
||||
# Creates schemas/schema.dial with groups configuration
|
||||
|
||||
```
|
||||
|
||||
|
||||
The generated schema includes group-level settings:
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"component": "Box",
|
||||
"schema": [
|
||||
{ "name": "position", "dtype": "vector3", "tags": { "grouping": "transform", "noWrap": true } },
|
||||
{ "name": "rotation", "dtype": "euler", "tags": { "grouping": "transform", "noWrap": true } },
|
||||
{ "name": "size", "dtype": "vector3", "tags": { "grouping": "geometry" } },
|
||||
{ "name": "color", "dtype": "color", "tags": { "grouping": "appearance" } }
|
||||
],
|
||||
"groups": [
|
||||
{ "name": "transform", "noWrap": true }
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
1. **Use the generated schema in your app:**
|
||||
|
||||
|
||||
|
||||
```tsx
|
||||
import { DialProvider, DialPanel, DialSchemaGroup, DialValue } from '@vuer-ai/vuer-uikit';
|
||||
import allSchemas from './schemas/schema.dial';
|
||||
|
||||
// Get the Box component schema from the combined schema file
|
||||
const boxSchema = allSchemas.find(s => s.component === 'Box');
|
||||
|
||||
function App() {
|
||||
const [boxProps, setBoxProps] = useState({
|
||||
position: [0, 0, 0],
|
||||
rotation: [0, 0, 0],
|
||||
size: [1, 1, 1],
|
||||
color: '#ff0000'
|
||||
});
|
||||
|
||||
const handleValueChange = (name: string, value: DialValue) => {
|
||||
setBoxProps(prev => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
return (
|
||||
<DialProvider
|
||||
schemas={[boxSchema]}
|
||||
onValueChange={handleValueChange}
|
||||
>
|
||||
<div style={{ display: 'flex' }}>
|
||||
{/* Your 3D scene */}
|
||||
<Box {...boxProps} />
|
||||
|
||||
{/* Auto-generated controls with group configuration */}
|
||||
<DialPanel schemas={[boxSchema]} />
|
||||
</div>
|
||||
</DialProvider>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Next Steps
|
||||
|
||||
|
||||
|
||||
- Learn more about [Dial Annotation Syntax](/dial/overview) in the overview
|
||||
|
||||
- Explore all [Input Types](/dial/input-types) available in Dial
|
||||
|
||||
- See [Controlled Dials](/dial/controlled-dials) for advanced usage
|
||||
|
||||
- Check out the [API Notes](/dial/api-notes) for detailed reference
|
||||
|
||||
- Read the [CLI Details](/dial/cli-details) for advanced CLI options
|
||||
352
docs/dial/type-inheritance.md
Normal file
352
docs/dial/type-inheritance.md
Normal file
@@ -0,0 +1,352 @@
|
||||
1. [Home](/)
|
||||
1. [Dial](/dial)
|
||||
1. Type Inheritance
|
||||
|
||||
# Type Inheritance in Dial
|
||||
|
||||
|
||||
|
||||
Type Inheritance & Compositiondial-cli[v0.0.22](https://www.npmjs.com/package/@vuer-ai/dial-cli/v/0.0.22)
|
||||
Dial CLI fully supports TypeScript's type system, including interface inheritance, type intersections, and utility types. This allows you to create reusable, composable type definitions while maintaining all Dial annotations and configurations.
|
||||
|
||||
|
||||
## Interface Inheritance
|
||||
|
||||
|
||||
When interfaces extend others, all properties and group configurations are inherited:
|
||||
|
||||
|
||||
### Basic Interface Extension
|
||||
|
||||
|
||||
```tsx
|
||||
// Base interface with common properties
|
||||
interface BaseProps {
|
||||
/**
|
||||
* Unique identifier
|
||||
* @dial common
|
||||
* @dial-dtype string
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Visibility control
|
||||
* @dial common
|
||||
* @dial-dtype boolean
|
||||
*/
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
// Extended interface inherits all base properties
|
||||
interface BoxProps extends BaseProps {
|
||||
/**
|
||||
* Box dimensions
|
||||
* @dial geometry
|
||||
* @dial-dtype vector3
|
||||
*/
|
||||
size: [number, number, number];
|
||||
|
||||
/**
|
||||
* Material color
|
||||
* @dial appearance
|
||||
* @dial-dtype color
|
||||
*/
|
||||
color: string;
|
||||
}
|
||||
|
||||
// BoxProps will have: id, visible, size, color
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Deep Inheritance Chains
|
||||
|
||||
|
||||
```tsx
|
||||
interface Level1 {
|
||||
/** @dial level1 */
|
||||
prop1: string;
|
||||
}
|
||||
|
||||
interface Level2 extends Level1 {
|
||||
/** @dial level2 */
|
||||
prop2: number;
|
||||
}
|
||||
|
||||
interface Level3 extends Level2 {
|
||||
/** @dial level3 */
|
||||
prop3: boolean;
|
||||
}
|
||||
|
||||
// Level3 has all properties from Level1, Level2, and its own
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Type Intersections
|
||||
|
||||
|
||||
Type intersections combine multiple types while preserving all annotations and group configurations:
|
||||
|
||||
|
||||
### Basic Type Intersection
|
||||
|
||||
|
||||
```tsx
|
||||
type TransformType = {
|
||||
/** @dial transform @dial-dtype vector3 */
|
||||
position: [number, number, number];
|
||||
/** @dial transform @dial-dtype euler */
|
||||
rotation: [number, number, number];
|
||||
};
|
||||
|
||||
type AppearanceType = {
|
||||
/** @dial appearance @dial-dtype color */
|
||||
color: string;
|
||||
/** @dial appearance @dial-dtype number @dial-min 0 @dial-max 1 */
|
||||
opacity: number;
|
||||
};
|
||||
|
||||
// Combine multiple types
|
||||
type GameObject = TransformType & AppearanceType & {
|
||||
/** @dial metadata @dial-dtype string */
|
||||
name: string;
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Group Configuration Inheritance
|
||||
|
||||
|
||||
The `@dial-no-wrap` configuration is inherited through type intersections:
|
||||
|
||||
|
||||
```tsx
|
||||
/**
|
||||
* Animation properties that should display on one line
|
||||
* @dial animation @dial-no-wrap
|
||||
*/
|
||||
type AnimationType = {
|
||||
/** @dial animation @dial-dtype number @dial-min 0 @dial-max 10 */
|
||||
duration: number;
|
||||
|
||||
/** @dial animation @dial-dtype string */
|
||||
easing: string;
|
||||
|
||||
/** @dial animation @dial-dtype number @dial-min 0 @dial-max 5 */
|
||||
delay: number;
|
||||
};
|
||||
|
||||
// Properties from AnimationType inherit noWrap: true
|
||||
type AnimatedObject = BaseType & AnimationType & {
|
||||
/** @dial control @dial-dtype boolean */
|
||||
playing: boolean;
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Utility Types
|
||||
|
||||
|
||||
Dial supports TypeScript's built-in utility types:
|
||||
|
||||
|
||||
### Pick - Select Specific Properties
|
||||
|
||||
|
||||
```tsx
|
||||
type FullTransform = {
|
||||
/** @dial position @dial-dtype number */
|
||||
x: number;
|
||||
/** @dial position @dial-dtype number */
|
||||
y: number;
|
||||
/** @dial position @dial-dtype number */
|
||||
z: number;
|
||||
/** @dial rotation @dial-dtype number-deg */
|
||||
rotationX: number;
|
||||
/** @dial rotation @dial-dtype number-deg */
|
||||
rotationY: number;
|
||||
/** @dial rotation @dial-dtype number-deg */
|
||||
rotationZ: number;
|
||||
};
|
||||
|
||||
// Only position properties
|
||||
type Position2D = Pick<FullTransform, 'x' | 'y'>;
|
||||
// Result: { x: number, y: number } with annotations
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Omit - Exclude Properties
|
||||
|
||||
|
||||
```tsx
|
||||
// Everything except rotation
|
||||
type TranslateOnly = Omit<FullTransform, 'rotationX' | 'rotationY' | 'rotationZ'>;
|
||||
// Result: { x, y, z } with all dial annotations preserved
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Partial - Make Properties Optional
|
||||
|
||||
|
||||
```tsx
|
||||
type RequiredConfig = {
|
||||
/** @dial config @dial-dtype string */
|
||||
apiKey: string;
|
||||
/** @dial config @dial-dtype string */
|
||||
endpoint: string;
|
||||
/** @dial config @dial-dtype number */
|
||||
timeout: number;
|
||||
};
|
||||
|
||||
// All properties become optional
|
||||
type OptionalConfig = Partial<RequiredConfig>;
|
||||
// Result: { apiKey?: string, endpoint?: string, timeout?: number }
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Property Override
|
||||
|
||||
|
||||
Child types can override parent property annotations:
|
||||
|
||||
|
||||
```tsx
|
||||
interface BaseComponent {
|
||||
/**
|
||||
* Base color property
|
||||
* @dial appearance
|
||||
* @dial-dtype string
|
||||
*/
|
||||
color: string;
|
||||
}
|
||||
|
||||
interface AdvancedComponent extends BaseComponent {
|
||||
/**
|
||||
* Enhanced color with picker
|
||||
* @dial appearance
|
||||
* @dial-dtype color
|
||||
* @dial-icon Palette
|
||||
*/
|
||||
color: string; // Overrides with color picker
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Complex Example
|
||||
|
||||
|
||||
Here's a real-world example combining multiple inheritance patterns:
|
||||
|
||||
|
||||
```tsx
|
||||
// Base types with group configs
|
||||
type PhysicsProps = {
|
||||
/** @dial physics @dial-dtype number @dial-min 0 @dial-max 100 */
|
||||
mass: number;
|
||||
/** @dial physics @dial-dtype number @dial-min 0 @dial-max 1 */
|
||||
friction: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @dial animation @dial-no-wrap
|
||||
*/
|
||||
type AnimationProps = {
|
||||
/** @dial animation */
|
||||
duration: number;
|
||||
/** @dial animation */
|
||||
loop: boolean;
|
||||
};
|
||||
|
||||
// Interface extending a type intersection
|
||||
interface GameObject extends PhysicsProps, AnimationProps {
|
||||
/** @dial metadata */
|
||||
id: string;
|
||||
/** @dial transform @dial-dtype vector3 */
|
||||
position: [number, number, number];
|
||||
}
|
||||
|
||||
// Further composition
|
||||
type InteractiveGameObject = GameObject & {
|
||||
/** @dial interaction */
|
||||
onClick: () => void;
|
||||
/** @dial interaction @dial-dtype boolean */
|
||||
hoverable: boolean;
|
||||
};
|
||||
|
||||
// Using utility types
|
||||
type StaticObject = Omit<InteractiveGameObject, 'onClick' | 'hoverable'>;
|
||||
type PreviewObject = Partial<InteractiveGameObject>;
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Generated Schema
|
||||
|
||||
|
||||
The dial-cli correctly resolves all inheritance and generates complete schemas:
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"component": "InteractiveGameObject",
|
||||
"schemas": [
|
||||
{ "name": "mass", "dtype": "number", "min": 0, "max": 100, "tags": { "grouping": "physics" } },
|
||||
{ "name": "friction", "dtype": "number", "min": 0, "max": 1, "tags": { "grouping": "physics" } },
|
||||
{ "name": "duration", "dtype": "number", "tags": { "grouping": "animation", "noWrap": true } },
|
||||
{ "name": "loop", "dtype": "boolean", "tags": { "grouping": "animation", "noWrap": true } },
|
||||
{ "name": "id", "dtype": "string", "tags": { "grouping": "metadata" } },
|
||||
{ "name": "position", "dtype": "vector3", "tags": { "grouping": "transform" } },
|
||||
{ "name": "hoverable", "dtype": "boolean", "tags": { "grouping": "interaction" } }
|
||||
],
|
||||
"groups": [
|
||||
{ "name": "animation", "noWrap": true }
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Best Practices
|
||||
|
||||
|
||||
|
||||
1. **Use Base Interfaces** for common properties across components
|
||||
|
||||
1. **Group Related Types** with type aliases for reusability
|
||||
|
||||
1. **Apply Group Configs** at the type level for consistent layout
|
||||
|
||||
1. **Document Overrides** when child types change parent behavior
|
||||
|
||||
1. **Test Inheritance** by running dial-cli with `--verbose` to see full resolution
|
||||
|
||||
|
||||
|
||||
## Limitations
|
||||
|
||||
|
||||
|
||||
- Generic types with type parameters require concrete types for dial-cli to process
|
||||
|
||||
- Conditional types are not fully supported
|
||||
|
||||
- Mapped types need explicit property definitions
|
||||
|
||||
|
||||
|
||||
## Next Steps
|
||||
|
||||
|
||||
|
||||
- Learn about [Group Configurations](/dial/annotations/grouping) for layout control
|
||||
|
||||
- Explore [Advanced Annotations](/dial/annotations/advanced) for complex types
|
||||
|
||||
- See [Complete Examples](/dial/examples) using inheritance patterns
|
||||
Reference in New Issue
Block a user