@yh-ui/yh-ui-skill 1.0.39 → 1.0.42

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.
@@ -1,56 +1,162 @@
1
- # Deep Recipe: Flow
1
+ # Deep Recipe: Flow Editor
2
2
 
3
- Use this recipe for workflow editors, node graphs, BPMN-like diagrams, AI workflow canvases, and visual automation builders.
3
+ Use this recipe when building visual workflow editors, node graph layouts, BPMN diagram canvases, AI agent workflow charts, or visual automation tools.
4
4
 
5
5
  ## Default Choice
6
6
 
7
- Use `@yh-ui/flow` with `Flow`, `Controls`, `Minimap`, and `FlowBackground`.
7
+ Use `@yh-ui/flow` with the core `Flow` canvas, complemented by helper components like `Controls`, `Minimap`, and `FlowBackground`.
8
8
 
9
- Always set an explicit canvas height.
9
+ ## Custom Node & Edge Types
10
10
 
11
- ## Source-Aligned API Highlights
11
+ - **BPMN Nodes**: Call `registerBpmnNodes()` to enable elements like `BpmnStartEvent`, `BpmnEndEvent`, `BpmnTask`, `BpmnServiceTask`, `BpmnUserTask`, `BpmnExclusiveGateway`, and gateway routes.
12
+ - **AI Workflow Nodes**: Call `registerAiWorkflowNodes()` to enable AI building blocks like `AiLlmNode`, `AiPromptNode`, `AiAgentNode`, `AiToolNode`, `AiConditionNode`, `AiStartNode`, and `AiMemoryNode`.
13
+ - **Custom Editor Helpers**: Use `NodeResizer` (for interactive sizing anchors), `NodeToolbar` (message actions above nodes), and `NodeEditPanel` for sidebar updates.
12
14
 
13
- - Props: `nodes`, `edges`, `modelValue`, `minZoom`, `maxZoom`, `defaultNodeType`, `defaultEdgeType`, `nodesDraggable`, `edgesConnectable`, `selectable`, `background`, `snapToGrid`, `readonly`, `keyboardShortcuts`, `showControls`, `showMinimap`, `history`, `virtualized`, `isValidConnection`.
14
- - Events: `update:modelValue`, `update:nodes`, `update:edges`, `nodeClick`, `nodeDragStart`, `nodeDrag`, `nodeDragEnd`, `edgeClick`, `edgeConnect`, `selectionChange`, `historyChange`, `viewportChange`.
15
- - Slots: `node`, `edge`.
16
- - Use AI workflow nodes: `AiLlmNode`, `AiPromptNode`, `AiAgentNode`, `AiToolNode`, `AiConditionNode`, `AiStartNode`, `AiEndNode`, `AiMemoryNode`.
17
- - Use BPMN nodes: `BpmnStartEvent`, `BpmnEndEvent`, `BpmnTask`, `BpmnServiceTask`, `BpmnUserTask`, `BpmnExclusiveGateway`, `BpmnParallelGateway`, `BpmnInclusiveGateway`.
15
+ ## Pattern: Interactive AI Agent Flow Editor
18
16
 
19
- ## Pattern: Nuxt AI Workflow
17
+ This example showcases a full-featured flow editor with auto-alignment guides, grid snap, interactive node resizing, history management (undo/redo), and AI workflow preset registration:
20
18
 
21
19
  ```vue
22
20
  <script setup lang="ts">
23
- import { Controls, Flow, FlowBackground, Minimap } from '@yh-ui/flow'
24
- import type { Edge, Node } from '@yh-ui/flow'
25
-
26
- const nodes: Node[] = [
27
- { id: 'start', type: 'ai-start', label: 'Start', position: { x: 80, y: 120 } },
28
- { id: 'llm', type: 'ai-llm', label: 'LLM', position: { x: 320, y: 120 } },
29
- { id: 'end', type: 'ai-end', label: 'End', position: { x: 560, y: 120 } }
30
- ]
31
-
32
- const edges: Edge[] = [
33
- { id: 'e-start-llm', source: 'start', target: 'llm' },
34
- { id: 'e-llm-end', source: 'llm', target: 'end' }
35
- ]
21
+ import { ref } from 'vue'
22
+ import {
23
+ Flow,
24
+ Controls,
25
+ Minimap,
26
+ FlowBackground,
27
+ registerAiWorkflowNodes,
28
+ NodeResizer,
29
+ NodeToolbar,
30
+ type FlowNode,
31
+ type FlowEdge
32
+ } from '@yh-ui/flow'
33
+ import { YhButton, YhMessage } from '@yh-ui/components'
34
+
35
+ // 1. Register AI Workflow node presets on the engine
36
+ registerAiWorkflowNodes()
37
+
38
+ // 2. Setup nodes state
39
+ const nodes = ref<FlowNode[]>([
40
+ {
41
+ id: 'node-start',
42
+ type: 'ai-start',
43
+ label: 'Trigger Prompt',
44
+ position: { x: 100, y: 150 }
45
+ },
46
+ {
47
+ id: 'node-llm',
48
+ type: 'ai-llm',
49
+ label: 'Process text (DeepSeek)',
50
+ position: { x: 350, y: 150 },
51
+ // Custom data parameters
52
+ data: { model: 'deepseek-chat', temperature: 0.7 }
53
+ },
54
+ {
55
+ id: 'node-end',
56
+ type: 'ai-end',
57
+ label: 'Output Result',
58
+ position: { x: 600, y: 150 }
59
+ }
60
+ ])
61
+
62
+ // 3. Setup edges state
63
+ const edges = ref<FlowEdge[]>([
64
+ { id: 'e-start-llm', source: 'node-start', target: 'node-llm', type: 'smooth' },
65
+ { id: 'e-llm-end', source: 'node-llm', target: 'node-end', type: 'bezier' }
66
+ ])
67
+
68
+ const flowRef = ref<any>(null)
69
+
70
+ // History management helpers
71
+ function triggerUndo() {
72
+ if (flowRef.value?.history) {
73
+ flowRef.value.history.undo()
74
+ YhMessage.info('Action undone')
75
+ }
76
+ }
77
+
78
+ function triggerRedo() {
79
+ if (flowRef.value?.history) {
80
+ flowRef.value.history.redo()
81
+ YhMessage.info('Action redone')
82
+ }
83
+ }
84
+
85
+ function handleNodeClick(node: FlowNode) {
86
+ console.log('Clicked node config:', node.data)
87
+ }
36
88
  </script>
37
89
 
38
90
  <template>
39
- <ClientOnly>
40
- <Flow :nodes="nodes" :edges="edges" fit-view style="height: 640px">
41
- <Minimap />
42
- <Controls />
43
- <FlowBackground />
44
- </Flow>
45
- </ClientOnly>
91
+ <div class="editor-layout">
92
+ <div class="editor-header">
93
+ <YhButton size="small" icon="mdi:undo" @click="triggerUndo">Undo</YhButton>
94
+ <YhButton size="small" icon="mdi:redo" @click="triggerRedo">Redo</YhButton>
95
+ </div>
96
+
97
+ <!-- Explicit container dimensions are mandatory for Flow to compute sizes -->
98
+ <div class="canvas-container" style="height: 600px; border: 1px solid var(--yh-border-color)">
99
+ <Flow
100
+ ref="flowRef"
101
+ v-model:nodes="nodes"
102
+ v-model:edges="edges"
103
+ snap-to-grid
104
+ show-alignment-guides
105
+ :min-zoom="0.2"
106
+ :max-zoom="4"
107
+ :history="true"
108
+ @nodeClick="handleNodeClick"
109
+ >
110
+ <Minimap />
111
+ <Controls />
112
+ <FlowBackground color="#ccc" :grid-size="15" />
113
+
114
+ <!-- Custom slot overriding for customized node presentation -->
115
+ <template #node="{ node }">
116
+ <div class="custom-node-body">
117
+ <!-- NodeToolbar renders edit controls above the active node -->
118
+ <NodeToolbar :visible="node.selected" position="top">
119
+ <button class="node-action-btn">Configure</button>
120
+ </NodeToolbar>
121
+
122
+ <!-- NodeResizer renders adjustment handles around the box -->
123
+ <NodeResizer :min-width="100" :min-height="40" />
124
+
125
+ <div class="node-label">{{ node.label }}</div>
126
+ </div>
127
+ </template>
128
+ </Flow>
129
+ </div>
130
+ </div>
46
131
  </template>
132
+
133
+ <style scoped>
134
+ .canvas-container {
135
+ background-color: var(--yh-bg-color-page);
136
+ }
137
+ .custom-node-body {
138
+ padding: 10px;
139
+ border-radius: var(--yh-border-radius-base);
140
+ border: 1px solid var(--yh-border-color);
141
+ background: var(--yh-bg-color);
142
+ box-shadow: var(--yh-box-shadow-light);
143
+ min-width: 150px;
144
+ }
145
+ .node-action-btn {
146
+ background: var(--yh-color-primary);
147
+ color: #fff;
148
+ border: none;
149
+ font-size: 11px;
150
+ border-radius: 4px;
151
+ cursor: pointer;
152
+ padding: 2px 6px;
153
+ }
154
+ </style>
47
155
  ```
48
156
 
49
157
  ## Agent Rules
50
158
 
51
- - In Nuxt or SSR, wrap in `<ClientOnly>`.
52
- - Use serializable node `data` for persistence.
53
- - Use `isValidConnection` for business validation.
54
- - Use `update:nodes` and `update:edges` for controlled editing.
55
- - Use slots for custom node/edge rendering.
56
- - Do not render Flow in a zero-height container.
159
+ - **Explicit Height**: Always wrap the `Flow` canvas inside a container element with an explicit CSS height (e.g. `style="height: 600px"`). Flow cannot render inside collapsed grid elements.
160
+ - **ClientOnly in Nuxt**: Because flow editors heavily access browser SVG methods, always wrap `Flow` in `<ClientOnly>` when building Nuxt SSR applications.
161
+ - **Controlled Models**: Bind canvas state using Vue 3 two-way models `v-model:nodes` and `v-model:edges` to guarantee changes propagate between component nodes and state.
162
+ - **Durable data properties**: Save custom node parameter fields (like database queries, API targets) inside the serializable `node.data` object.
@@ -1,82 +1,187 @@
1
1
  # Deep Recipe: YhFormSchema
2
2
 
3
- Use this recipe for schema-driven forms, dynamic business forms, generated forms, and admin create/edit pages with conditional fields.
3
+ Use this recipe when building complex forms generated dynamically from schemas, including repeating item lists, custom field renderers, layout grids, or conditional field visibility/dependencies.
4
4
 
5
5
  ## Default Choice
6
6
 
7
- Use `YhFormSchema` when fields are generated from configuration or when the form needs dynamic visibility, async options, grouped fields, list fields, or custom field rendering.
7
+ Use `YhFormSchema` when forms are configuration-driven, require repeating group elements, or need layout grid scaling. Use `YhForm` + `YhFormItem` only for simple static fields.
8
8
 
9
- Use `YhForm` + `YhFormItem` for small hand-written forms.
9
+ ## Schema Item Configurations
10
10
 
11
- ## Source-Aligned API Highlights
11
+ Each field schema object supports:
12
12
 
13
- - Props: `modelValue`, `schema`, `formProps`, `gutter`.
14
- - Events: `update:modelValue`, `submit`, `change`, `validate`.
15
- - Schema item capabilities: `field`, `label`, `type`, `component`, `props`, `formItemProps`, `rules`, `required`, `hidden`, `disabled`, `slots`, `col`, `span`, `render`, `asyncOptions`, `optionProp`, `defaultValue`, `tooltip`.
16
- - Special item types: `divider`, `text`, `list`.
17
- - Expose: `validate`, `resetFields`, `clearValidate`, `scrollToField`, `getModel`, `setFieldValue`, `formRef`.
13
+ - `field`: The key name in the model object.
14
+ - `label`: Form label description.
15
+ - `component`: Native component identifier (e.g. `'input'`, `'select'`, `'switch'`, `'date-picker'`).
16
+ - `props`: Target component props passed dynamically.
17
+ - `formItemProps`: Props passed directly to the containing `YhFormItem` (e.g. `labelWidth`).
18
+ - `required` / `rules`: Form validation constraints.
19
+ - `hidden`: A boolean or function `(model) => boolean` determining field visibility.
20
+ - `disabled`: A boolean or function `(model) => boolean` determining disabled state.
21
+ - `span` / `col`: Control grid widths (e.g. `12` for half column, `24` for full width).
22
+ - `asyncOptions`: Fetch options dynamically from APIs.
23
+ - `render`: A custom function `(h, { model, field }) => VNode` for custom render logic.
24
+ - `type`: Set to `'list'` for repeatable sub-form fields.
18
25
 
19
- ## Pattern: Dynamic Form
26
+ ## Pattern: Advanced Dynamic Form
20
27
 
21
28
  ```vue
22
29
  <script setup lang="ts">
23
- import { ref } from 'vue'
24
- import { YhButton, YhFormSchema, YhMessage } from '@yh-ui/components'
30
+ import { ref, h } from 'vue'
31
+ import { YhFormSchema, YhButton, YhMessage, YhInput } from '@yh-ui/components'
25
32
  import type { FormSchema } from '@yh-ui/components'
26
33
 
27
- const model = ref({
28
- name: '',
29
- role: '',
30
- enabled: true
34
+ // Define typed form state
35
+ const formModel = ref({
36
+ projectName: '',
37
+ deploymentType: 'cloud',
38
+ cloudProvider: '',
39
+ scaleNodes: 3,
40
+ environments: [{ name: 'staging', domain: 'staging.company.com' }]
31
41
  })
32
42
 
43
+ // Define schema configuration
33
44
  const schema: FormSchema = [
34
45
  {
35
- field: 'name',
36
- label: 'Name',
46
+ field: 'projectName',
47
+ label: 'Project Name',
37
48
  component: 'input',
38
49
  required: true,
39
- props: { clearable: true, placeholder: 'User name' }
50
+ span: 24, // Full width row
51
+ props: {
52
+ placeholder: 'Enter project name',
53
+ clearable: true
54
+ }
40
55
  },
41
56
  {
42
- field: 'role',
43
- label: 'Role',
57
+ field: 'deploymentType',
58
+ label: 'Deployment Type',
44
59
  component: 'select',
45
60
  required: true,
61
+ span: 12,
62
+ props: {
63
+ options: [
64
+ { label: 'Cloud Hosted', value: 'cloud' },
65
+ { label: 'On-Premises', value: 'on-premise' }
66
+ ]
67
+ }
68
+ },
69
+ {
70
+ field: 'cloudProvider',
71
+ label: 'Cloud Provider',
72
+ component: 'select',
73
+ span: 12,
74
+ // Conditional visibility: only show if deploymentType is cloud
75
+ hidden: (model) => model.deploymentType !== 'cloud',
76
+ required: (model) => model.deploymentType === 'cloud',
46
77
  asyncOptions: async () => [
47
- { label: 'Admin', value: 'admin' },
48
- { label: 'Editor', value: 'editor' }
78
+ { label: 'AWS', value: 'aws' },
79
+ { label: 'Google Cloud Platform', value: 'gcp' },
80
+ { label: 'Microsoft Azure', value: 'azure' }
49
81
  ]
50
82
  },
51
83
  {
52
- field: 'enabled',
53
- label: 'Enabled',
54
- component: 'switch',
55
- defaultValue: true
84
+ field: 'scaleNodes',
85
+ label: 'Scale Nodes Count',
86
+ component: 'input-number',
87
+ span: 12,
88
+ props: {
89
+ min: 1,
90
+ max: 100
91
+ },
92
+ // Conditional disabled state: lock count if cloudProvider is not GCP
93
+ disabled: (model) => model.cloudProvider !== 'gcp'
94
+ },
95
+ {
96
+ field: 'customRenderer',
97
+ label: 'Custom Field',
98
+ span: 12,
99
+ // Custom render method returning standard VNode
100
+ render: (h, { model, field }) => {
101
+ return h(YhInput, {
102
+ modelValue: model[field],
103
+ 'onUpdate:modelValue': (val: string) => {
104
+ model[field] = val
105
+ },
106
+ placeholder: 'Generated via render()'
107
+ })
108
+ }
109
+ },
110
+ {
111
+ field: 'divider1',
112
+ type: 'divider',
113
+ span: 24
114
+ },
115
+ {
116
+ field: 'environments',
117
+ label: 'Target Environments',
118
+ type: 'list', // Repeating sub-form group
119
+ span: 24,
120
+ props: {
121
+ // Define schemas for elements inside list array rows
122
+ schema: [
123
+ {
124
+ field: 'name',
125
+ label: 'Env Name',
126
+ component: 'input',
127
+ required: true,
128
+ span: 8
129
+ },
130
+ {
131
+ field: 'domain',
132
+ label: 'Access Domain',
133
+ component: 'input',
134
+ required: true,
135
+ span: 16
136
+ }
137
+ ]
138
+ }
56
139
  }
57
140
  ]
58
141
 
59
- function submit() {
60
- YhMessage.success('Saved')
142
+ const formRef = ref<InstanceType<typeof YhFormSchema> | null>(null)
143
+
144
+ async function handleSave() {
145
+ if (!formRef.value) return
146
+
147
+ try {
148
+ const valid = await formRef.value.validate()
149
+ if (valid) {
150
+ YhMessage.success('Form configurations validated successfully!')
151
+ console.log('Final Model:', formModel.value)
152
+ }
153
+ } catch (err: any) {
154
+ YhMessage.error(`Validation failed: ${err.message || err}`)
155
+ }
156
+ }
157
+
158
+ function handleReset() {
159
+ formRef.value?.resetFields()
61
160
  }
62
161
  </script>
63
162
 
64
163
  <template>
65
- <YhFormSchema
66
- v-model="model"
67
- :schema="schema"
68
- :form-props="{ labelWidth: 120 }"
69
- @submit="submit"
70
- />
71
- <YhButton type="primary" @click="submit">Save</YhButton>
164
+ <div class="form-container">
165
+ <!-- gutter specifies spacing between form grid components -->
166
+ <YhFormSchema
167
+ ref="formRef"
168
+ v-model="formModel"
169
+ :schema="schema"
170
+ :gutter="20"
171
+ :form-props="{ labelPosition: 'top' }"
172
+ />
173
+
174
+ <div class="form-buttons">
175
+ <YhButton type="primary" @click="handleSave">Submit Configuration</YhButton>
176
+ <YhButton @click="handleReset">Reset Fields</YhButton>
177
+ </div>
178
+ </div>
72
179
  </template>
73
180
  ```
74
181
 
75
182
  ## Agent Rules
76
183
 
77
- - Use `v-model` with `modelValue`.
78
- - Use `required` for simple required rules and `rules` for custom validation.
79
- - Use `hidden`/`disabled` functions for dynamic behavior.
80
- - Use `asyncOptions` for remote options instead of manually wiring loading state for each field.
81
- - Use `type: 'list'` for repeatable field groups.
82
- - Do not use `YhFormSchema` for a tiny static form unless schema generation is useful.
184
+ - **Use Model Value**: Always bind data using `v-model` (equivalent to `v-model:modelValue`).
185
+ - **Define Render Returns**: Custom field `render` methods must accept `h` function parameters and return a valid VNode using actual components imported from `@yh-ui/components`.
186
+ - **List Sub-schema**: When using `type: 'list'`, nested schema arrays must be defined inside `props.schema`.
187
+ - **Gutter Config**: Set layout spacings using the `:gutter` prop on the wrapper `YhFormSchema`.
@@ -0,0 +1,126 @@
1
+ # Deep Recipe: YH-UI Icons
2
+
3
+ Use this recipe when integrating and rendering icons across YH-UI components. YH-UI uses a high-performance icon system based on Iconify, supporting thousands of icons with zero runtime overhead.
4
+
5
+ ## Default Choice
6
+
7
+ Use the `Icon` component (aliased as `YhIcon`) from `@yh-ui/icons/vue` or `@yh-ui/icons`.
8
+
9
+ ## Usage Patterns
10
+
11
+ ### 1. Iconify String Name (Recommended)
12
+
13
+ You can reference any icon using the standard `collection:icon` syntax. The package loads the icon asynchronously.
14
+
15
+ ```vue
16
+ <script setup lang="ts">
17
+ import { Icon } from '@yh-ui/icons/vue'
18
+ </script>
19
+
20
+ <template>
21
+ <!-- Material Design Icons collection -->
22
+ <Icon icon="mdi:home" />
23
+
24
+ <!-- Element Plus Icons collection -->
25
+ <Icon icon="ep:search" />
26
+
27
+ <!-- Lucide Icons collection -->
28
+ <Icon icon="lucide:settings" />
29
+ </template>
30
+ ```
31
+
32
+ ### 2. Pre-registered Aliases
33
+
34
+ You can use simplified short names for common UI actions (which resolve to pre-configured presets under the hood):
35
+
36
+ ```vue
37
+ <template>
38
+ <Icon name="home" />
39
+ <!-- Resolves to mdi:home -->
40
+ <Icon name="search" />
41
+ <!-- Resolves to mdi:magnify -->
42
+ <Icon name="loading" />
43
+ <!-- Resolves to mdi:loading -->
44
+ </template>
45
+ ```
46
+
47
+ ### 3. SVG Strings
48
+
49
+ If you need to render raw SVG data dynamically without loading it from an external collection:
50
+
51
+ ```vue
52
+ <script setup lang="ts">
53
+ import { Icon } from '@yh-ui/icons/vue'
54
+
55
+ const customSvg = '<path d="M12 2L2 22h20L12 2z" fill="currentColor"/>'
56
+ </script>
57
+
58
+ <template>
59
+ <Icon :svg="customSvg" />
60
+ </template>
61
+ ```
62
+
63
+ ### 4. Custom Icon Components
64
+
65
+ If you've imported raw Vue component icons from a local folder or another icon package:
66
+
67
+ ```vue
68
+ <script setup lang="ts">
69
+ import { Icon } from '@yh-ui/icons/vue'
70
+ import CustomStarComponent from './CustomStar.vue'
71
+ </script>
72
+
73
+ <template>
74
+ <Icon :component="CustomStarComponent" />
75
+ </template>
76
+ ```
77
+
78
+ ## Styling & Animations
79
+
80
+ ### Size and Color
81
+
82
+ Adjust sizing (takes either numbers for pixel dimensions or CSS string values) and fill colors:
83
+
84
+ ```vue
85
+ <template>
86
+ <Icon icon="mdi:heart" :size="24" color="var(--yh-color-danger)" />
87
+ <Icon icon="mdi:heart" size="2rem" color="#ff4d4f" />
88
+ </template>
89
+ ```
90
+
91
+ ### Rotating & Spin Animations
92
+
93
+ Apply static rotations or infinite spinning animations (highly useful for loading states):
94
+
95
+ ```vue
96
+ <template>
97
+ <!-- Rotated 90 degrees -->
98
+ <Icon icon="mdi:chevron-right" :rotate="90" />
99
+
100
+ <!-- Infinite spin animation (1s full rotation) -->
101
+ <Icon icon="mdi:loading" spin />
102
+ </template>
103
+ ```
104
+
105
+ ## Integration with YH-UI Components
106
+
107
+ Many YH-UI components support passing an icon name directly or via slots:
108
+
109
+ ```vue
110
+ <script setup lang="ts">
111
+ import { YhButton, YhInput } from '@yh-ui/components'
112
+ import { Icon } from '@yh-ui/icons/vue'
113
+ </script>
114
+
115
+ <template>
116
+ <!-- Passing icon prop -->
117
+ <YhButton type="primary" icon="mdi:plus">Add User</YhButton>
118
+
119
+ <!-- Custom slot rendering -->
120
+ <YhInput placeholder="Enter username">
121
+ <template #prefix>
122
+ <Icon icon="mdi:account" :size="16" />
123
+ </template>
124
+ </YhInput>
125
+ </template>
126
+ ```