@yh-ui/yh-ui-skill 1.0.51 β 1.0.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/metadata.json +1 -1
- package/assets/skills/yh-ui/SKILL.md +58 -155
- package/assets/skills/yh-ui/references/codegen-rubric.md +48 -28
- package/assets/skills/yh-ui/references/source-truth.md +2 -4
- package/assets/skills/yh-ui/references/vue-component-practices.md +183 -108
- package/package.json +1 -1
package/assets/metadata.json
CHANGED
|
@@ -25,9 +25,25 @@ Do not use this skill for:
|
|
|
25
25
|
|
|
26
26
|
## Core Rules
|
|
27
27
|
|
|
28
|
+
> [!IMPORTANT]
|
|
29
|
+
> **π« The Absolute Anti-Hallucination Standard (第δΈεε)**
|
|
30
|
+
> **DO NOT invent or guess any properties, events, slots, methods, CSS selectors, theme presets, locale files, sub-components, or package names.**
|
|
31
|
+
> All code generated **MUST align 100%** with the actual source code definitions in this repository (refer to `references/source-truth.md` for extracted AST data) and the official component library documentation (`https://1079161148.github.io/yh-ui/`).
|
|
32
|
+
> If a property, method, or event is not defined in the source code or documented in the official site, you **must not** guess it. Doing so will directly crash compiler or runtime systems and is strictly forbidden.
|
|
33
|
+
|
|
28
34
|
- **Strict YH-UI Prioritization**: Under no circumstances should you generate custom HTML/CSS controls (e.g. custom buttons, inputs, tables, dialogs, drawers, scrollbars, markdown cards) or manually construct network fetches/stream connections when YH-UI packages support them. You must 100% prioritize utilizing YH-UI components and utilities.
|
|
29
|
-
- **Extension & Re-encapsulation Principle**: If a YH-UI component does not fully meet a specific UI requirement, you must first try to extend it using slot
|
|
30
|
-
-
|
|
35
|
+
- **Extension & Re-encapsulation Principle**: If a YH-UI component does not fully meet a specific UI requirement, you must first try to extend it using slot customization, CSS overrides, or component composition. Writing custom elements from scratch is a last resort, and you must justify why YH-UI could not be extended.
|
|
36
|
+
- **On-Demand Loading (ζιε θ½½) by Default**: Unless configuring global imports, prioritize on-demand imports to ensure optimal bundle size (δ½η§―ζδΌ).
|
|
37
|
+
- Import components from `@yh-ui/components` and their styling:
|
|
38
|
+
```ts
|
|
39
|
+
import { YhButton, YhTable } from '@yh-ui/components'
|
|
40
|
+
import '@yh-ui/components/style.css'
|
|
41
|
+
```
|
|
42
|
+
- Import icons from `@yh-ui/icons/vue` or `@yh-ui/icons`:
|
|
43
|
+
```ts
|
|
44
|
+
import { Icon } from '@yh-ui/icons/vue'
|
|
45
|
+
```
|
|
46
|
+
- **Language Defaults (TypeScript & SCSS/Sass)**: By default, SFC files and code snippets must declare TypeScript (`lang="ts"`) for script blocks and SCSS/Sass (`lang="scss"`) for style blocks.
|
|
31
47
|
- Use `@yh-ui/yh-ui` for ordinary Vue apps that want the all-in-one entry.
|
|
32
48
|
- Use `@yh-ui/nuxt` for Nuxt apps and rely on auto-imported components/composables.
|
|
33
49
|
- Use `@yh-ui/components` when the user asks for component-only usage.
|
|
@@ -38,6 +54,46 @@ Do not use this skill for:
|
|
|
38
54
|
- Keep model API keys on the server. Never put provider secrets in browser code.
|
|
39
55
|
- In SSR/Nuxt, wrap browser-heavy flow editors in `<ClientOnly>`.
|
|
40
56
|
|
|
57
|
+
### Component-Level Best Practices & Source Alignment
|
|
58
|
+
|
|
59
|
+
Below are the detailed rules and best practices for core YH-UI components. Refer to `references/source-truth.md` for the complete API surface of all priority components.
|
|
60
|
+
|
|
61
|
+
#### 1. Data Tables: `YhTable` & `YhTableColumn`
|
|
62
|
+
|
|
63
|
+
- **Column Setup**: Bind column configurations via the `columns` property as a stable array (computed or constant). Do not write raw loop markups inside the component unless using column-slot extensions.
|
|
64
|
+
- **Feature Integration**: Use built-in properties like `pagination`, `resizable`, `loading`, `border` directly.
|
|
65
|
+
- **Exposed Methods**: Use typed refs (`InstanceType<typeof YhTable>`) to invoke functions like `exportData`, `importData`, `insertRow`, `removeRow`, `scrollTo`, and `clearSelection`. Do not guess names (e.g., do not write `.getSelectedRows()` if the source exposes `.getSelectionRows()`).
|
|
66
|
+
- **Events**: Handle events like `selection-change`, `page-change`, `row-click`, `sort-change`, `update:data` exactly as defined.
|
|
67
|
+
|
|
68
|
+
#### 2. Forms & Inputs: `YhForm`, `YhFormItem`, `YhFormSchema`
|
|
69
|
+
|
|
70
|
+
- **Form Validation**: Always bind `:model` and `:rules` on `YhForm`. `YhFormItem` must define the matching `prop` string for validation.
|
|
71
|
+
- **Form Schema**: Use `YhFormSchema` with `:schema` and `:formProps` to render dynamic schemas. Schema configurations must utilize real component names (e.g., `input`, `select`, `date-picker`).
|
|
72
|
+
- **Input Components**: Use `v-model` with native properties like `clearable`, `placeholder`, `disabled`, `show-word-limit`.
|
|
73
|
+
|
|
74
|
+
#### 3. AI UI Components: `YhAiChat`, `YhAiBubble`, `YhAiSender`, `YhAiThoughtChain`
|
|
75
|
+
|
|
76
|
+
- **State Binding**: Do not manage messages and loading states manually. Connect them directly to `useAIChat` or `useAIStream` from `@yh-ui/ai-sdk/vue`.
|
|
77
|
+
- **Sender Layout (`YhAiSender`)**: Bind `v-model` for input and listen to `@send`, `@upload`, and `@command` events. Custom action icons should be placed inside `#actions` or `#submit` slots.
|
|
78
|
+
- **Bubble Layout (`YhAiBubble`)**: Use `citations` array to display reference sources, and handle citation click logic. Ensure `role` (`'user'` or `'assistant'`), `streaming`, and `loading` are properly set.
|
|
79
|
+
- **Thought Chain (`YhAiThoughtChain`)**: Feed structured steps into the `items` property. Bind `@node-click` to let users view step details.
|
|
80
|
+
|
|
81
|
+
#### 4. Interactive Flow Canvas: `Flow` (from `@yh-ui/flow`)
|
|
82
|
+
|
|
83
|
+
- **Layout Sizing**: Always wrap the `Flow` component in a container with a defined height (e.g., `height: 600px;` or `h-screen`).
|
|
84
|
+
- **Canvas Extensions**: Place `Minimap`, `Controls`, and `FlowBackground` inside the `Flow` template body.
|
|
85
|
+
- **Gating in SSR**: Wrap the `Flow` component inside `<ClientOnly>` in SSR environments (such as Nuxt) to avoid canvas/ResizeObserver errors during server builds.
|
|
86
|
+
|
|
87
|
+
#### 5. Layout & Utilities: `YhScrollbar`, `YhConfigProvider`
|
|
88
|
+
|
|
89
|
+
- **Custom Scrollbars**: Use `YhScrollbar` with `height` or `max-height` instead of styling custom divs.
|
|
90
|
+
- **Global Provider**: Place `YhConfigProvider` at the root of the app to control global sizes and internationalization (`:locale`).
|
|
91
|
+
- **Locale Files**: Import locale languages using lowercase paths:
|
|
92
|
+
```ts
|
|
93
|
+
import zhCn from '@yh-ui/locale/lang/zh-cn'
|
|
94
|
+
import en from '@yh-ui/locale/lang/en'
|
|
95
|
+
```
|
|
96
|
+
|
|
41
97
|
## Agent Workflow
|
|
42
98
|
|
|
43
99
|
1. Classify the task: Vue app, Nuxt app, AI UI, request/data, flow/workflow, theme/locale, icon, or review.
|
|
@@ -47,159 +103,6 @@ Do not use this skill for:
|
|
|
47
103
|
5. Generate code with real package imports and YH-UI components.
|
|
48
104
|
6. Check the result against `references/codegen-rubric.md` before answering.
|
|
49
105
|
|
|
50
|
-
## Quick Package Decision
|
|
51
|
-
|
|
52
|
-
| Task | Package |
|
|
53
|
-
| ----------------------------------- | ------------------------------------- |
|
|
54
|
-
| Full Vue component library | `@yh-ui/yh-ui` |
|
|
55
|
-
| Component-only import | `@yh-ui/components` |
|
|
56
|
-
| Nuxt integration | `@yh-ui/nuxt` |
|
|
57
|
-
| AI chat UI and streams | `@yh-ui/components` + `@yh-ui/ai-sdk` |
|
|
58
|
-
| Request hooks and clients | `@yh-ui/request` |
|
|
59
|
-
| Flow editor and workflow canvas | `@yh-ui/flow` |
|
|
60
|
-
| Iconify runtime and icon components | `@yh-ui/icons` |
|
|
61
|
-
| Theme tokens and runtime theme | `@yh-ui/theme` |
|
|
62
|
-
| Locale files | `@yh-ui/locale` |
|
|
63
|
-
|
|
64
|
-
## High-Frequency Patterns
|
|
65
|
-
|
|
66
|
-
### Vue Install
|
|
67
|
-
|
|
68
|
-
```ts
|
|
69
|
-
import { createApp } from 'vue'
|
|
70
|
-
import YhUI from '@yh-ui/yh-ui'
|
|
71
|
-
import '@yh-ui/yh-ui/css'
|
|
72
|
-
import App from './App.vue'
|
|
73
|
-
|
|
74
|
-
createApp(App).use(YhUI).mount('#app')
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Global Config & i18n (Root Setup)
|
|
78
|
-
|
|
79
|
-
```vue
|
|
80
|
-
<script setup lang="ts">
|
|
81
|
-
import { ref } from 'vue'
|
|
82
|
-
import { YhConfigProvider } from '@yh-ui/components'
|
|
83
|
-
import zhCn from '@yh-ui/locale/lang/zh-cn'
|
|
84
|
-
import en from '@yh-ui/locale/lang/en'
|
|
85
|
-
|
|
86
|
-
const locale = ref(zhCn)
|
|
87
|
-
const currentSize = ref<'default' | 'small' | 'large'>('default')
|
|
88
|
-
|
|
89
|
-
function toggleLang() {
|
|
90
|
-
locale.value = locale.value.name === 'zh-cn' ? en : zhCn
|
|
91
|
-
}
|
|
92
|
-
</script>
|
|
93
|
-
|
|
94
|
-
<template>
|
|
95
|
-
<YhConfigProvider :locale="locale" :size="currentSize">
|
|
96
|
-
<button @click="toggleLang">Switch Language</button>
|
|
97
|
-
<AppLayout>
|
|
98
|
-
<router-view />
|
|
99
|
-
</AppLayout>
|
|
100
|
-
</YhConfigProvider>
|
|
101
|
-
</template>
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### On-Demand Components
|
|
105
|
-
|
|
106
|
-
```vue
|
|
107
|
-
<script setup lang="ts">
|
|
108
|
-
import { YhButton, YhInput, YhTable } from '@yh-ui/components'
|
|
109
|
-
import '@yh-ui/components/style.css'
|
|
110
|
-
</script>
|
|
111
|
-
|
|
112
|
-
<template>
|
|
113
|
-
<YhInput clearable placeholder="Search" />
|
|
114
|
-
<YhButton type="primary">Submit</YhButton>
|
|
115
|
-
<YhTable :data="rows" :columns="columns" />
|
|
116
|
-
</template>
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### Nuxt
|
|
120
|
-
|
|
121
|
-
```ts
|
|
122
|
-
export default defineNuxtConfig({
|
|
123
|
-
modules: ['@yh-ui/nuxt'],
|
|
124
|
-
yhUI: {
|
|
125
|
-
importStyle: true,
|
|
126
|
-
buildTranspile: true,
|
|
127
|
-
prefix: 'Yh'
|
|
128
|
-
}
|
|
129
|
-
})
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### AI Chat
|
|
133
|
-
|
|
134
|
-
```vue
|
|
135
|
-
<script setup lang="ts">
|
|
136
|
-
import { YhAiBubble, YhAiSender } from '@yh-ui/components'
|
|
137
|
-
import { useAIChat } from '@yh-ui/ai-sdk/vue'
|
|
138
|
-
|
|
139
|
-
const { messages, input, isLoading, sendMessage, stop } = useAIChat({
|
|
140
|
-
api: '/api/chat'
|
|
141
|
-
})
|
|
142
|
-
</script>
|
|
143
|
-
|
|
144
|
-
<template>
|
|
145
|
-
<YhAiBubble
|
|
146
|
-
v-for="message in messages"
|
|
147
|
-
:key="message.id"
|
|
148
|
-
:role="message.role"
|
|
149
|
-
:content="message.content"
|
|
150
|
-
streaming
|
|
151
|
-
/>
|
|
152
|
-
<YhAiSender v-model="input" :loading="isLoading" @send="sendMessage" @cancel="stop" />
|
|
153
|
-
</template>
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
### Flow Editor
|
|
157
|
-
|
|
158
|
-
```vue
|
|
159
|
-
<script setup lang="ts">
|
|
160
|
-
import { Controls, Flow, FlowBackground, Minimap } from '@yh-ui/flow'
|
|
161
|
-
import type { FlowEdge, FlowNode } from '@yh-ui/flow'
|
|
162
|
-
|
|
163
|
-
const nodes: FlowNode[] = [
|
|
164
|
-
{ id: 'start', type: 'input', label: 'Start', position: { x: 80, y: 80 } }
|
|
165
|
-
]
|
|
166
|
-
const edges: FlowEdge[] = []
|
|
167
|
-
</script>
|
|
168
|
-
|
|
169
|
-
<template>
|
|
170
|
-
<Flow :nodes="nodes" :edges="edges" fit-view style="height: 560px">
|
|
171
|
-
<Minimap />
|
|
172
|
-
<Controls />
|
|
173
|
-
<FlowBackground />
|
|
174
|
-
</Flow>
|
|
175
|
-
</template>
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### Theme
|
|
179
|
-
|
|
180
|
-
```ts
|
|
181
|
-
import { ThemePlugin } from '@yh-ui/theme'
|
|
182
|
-
|
|
183
|
-
app.use(ThemePlugin, {
|
|
184
|
-
preset: 'blue',
|
|
185
|
-
dark: false,
|
|
186
|
-
persist: true
|
|
187
|
-
})
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### Icons
|
|
191
|
-
|
|
192
|
-
```vue
|
|
193
|
-
<script setup lang="ts">
|
|
194
|
-
import { Icon } from '@yh-ui/icons/vue'
|
|
195
|
-
</script>
|
|
196
|
-
|
|
197
|
-
<template>
|
|
198
|
-
<Icon icon="mdi:home" :size="20" color="var(--yh-color-primary)" />
|
|
199
|
-
<Icon icon="mdi:loading" spin />
|
|
200
|
-
</template>
|
|
201
|
-
```
|
|
202
|
-
|
|
203
106
|
## Progressive References
|
|
204
107
|
|
|
205
108
|
Read only the file needed for the task:
|
|
@@ -1,40 +1,60 @@
|
|
|
1
|
-
# Code Generation Rubric
|
|
1
|
+
# Code Generation Rubric (Evolved Standard)
|
|
2
2
|
|
|
3
|
-
Use this checklist before returning YH-UI code.
|
|
3
|
+
Use this checklist before returning YH-UI code. It ensures generated code is 100% bug-free, aligned with source code interfaces, and conforms to modern Vue 3.5+ and Anthony Fu (antfu) styling standards.
|
|
4
|
+
|
|
5
|
+
---
|
|
4
6
|
|
|
5
7
|
## Must Pass
|
|
6
8
|
|
|
7
|
-
-
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
- **π« Absolute Zero Hallucination (第δΈεε)**: Any code that invents or guesses properties, event names, slots, methods, class names, or package paths **fails** code generation. All declarations must align 100% with the source code definitions (`references/source-truth.md`) and the official docs (`https://1079161148.github.io/yh-ui/`).
|
|
10
|
+
- **Prioritize YH-UI Native Features**: 100% of UI elements and utility actions (like tables, dropdowns, dialogs, scrolls, loading icons, fetch hooks) use YH-UI packages if supported. Writing custom elements when YH-UI supports them **fails** code generation.
|
|
11
|
+
- **On-Demand Importing (ζιε θ½½)**: Vue examples must import components directly from `@yh-ui/components` and include the CSS file:
|
|
12
|
+
```ts
|
|
13
|
+
import { YhButton } from '@yh-ui/components'
|
|
14
|
+
import '@yh-ui/components/style.css'
|
|
15
|
+
```
|
|
16
|
+
- **Antfu Import Formatting**: Imports must be grouped and sorted correctly:
|
|
17
|
+
1. Vue core (e.g., `ref`, `computed`).
|
|
18
|
+
2. Libraries (e.g., `@yh-ui/request`).
|
|
19
|
+
3. UI / Local (e.g., `@yh-ui/components`).
|
|
20
|
+
4. Types (`import type { ... }`).
|
|
21
|
+
- **Default Coding Languages**: Vue SFCs must declare TypeScript (`lang="ts"`) for script blocks and SCSS/Sass (`lang="scss"`) for style blocks by default.
|
|
22
|
+
- **Modern Reactivity & Props**:
|
|
23
|
+
- For Vue 3.5+, use reactive props destructuring: `const { size = 'default' } = defineProps<Props>()`.
|
|
24
|
+
- Use `defineModel` for two-way bindings.
|
|
25
|
+
- Prefer `ref()` over `reactive()` for standard state parameters.
|
|
26
|
+
- **TypeScript Strict Standards**:
|
|
27
|
+
- Component template refs must be typed using `InstanceType<typeof Component>` and defined without `null` initialization if bound:
|
|
28
|
+
```ts
|
|
29
|
+
const tableRef = ref<InstanceType<typeof YhTable>>()
|
|
30
|
+
```
|
|
31
|
+
- **Performance Allocations**:
|
|
32
|
+
- Heavy objects (Monaco editor, ECharts, Flow canvases) **must** be stored in `shallowRef()` to prevent performance degradation.
|
|
33
|
+
- **Zero SSR API Leaks**:
|
|
34
|
+
- Never read `window`, `document`, or `localStorage`/`sessionStorage` during initial setup initialization. Wrap them in `onMounted`.
|
|
35
|
+
- Never store request-specific reactive state in global singleton variables.
|
|
36
|
+
- **Mandatory Cleanups**:
|
|
37
|
+
- All active timers (`setTimeout`, `setInterval`), observers (`ResizeObserver`), and DOM/window event listeners **must** be cleared or unregistered in `onUnmounted` or `onBeforeUnmount`.
|
|
38
|
+
|
|
39
|
+
---
|
|
18
40
|
|
|
19
41
|
## Should Pass
|
|
20
42
|
|
|
21
|
-
-
|
|
22
|
-
-
|
|
43
|
+
- Custom composables should accept arguments as `MaybeRefOrGetter<T>` and parse them using `toValue()`.
|
|
44
|
+
- Custom composables should perform side-effect cleanups using `onScopeDispose()`.
|
|
45
|
+
- Use `v-once` for static templates and `v-memo` for complex grid iterations to maximize rendering performance.
|
|
23
46
|
- Include empty, loading, and error states for data-heavy pages.
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
|
|
47
|
+
- Pick the smallest YH-UI surface that solves the task.
|
|
48
|
+
- Keep templates clean of complex calculations: move logic into `computed` variables.
|
|
49
|
+
|
|
50
|
+
---
|
|
28
51
|
|
|
29
|
-
## Red Flags
|
|
52
|
+
## Red Flags (Auto-Fail)
|
|
30
53
|
|
|
31
54
|
- **Hallucinated Controls**: Reinventing standard visual controls (e.g. custom scroll elements, custom select components, custom modal popups) instead of using the corresponding YH-UI components.
|
|
32
55
|
- **Custom Fetches**: Initiating direct `fetch` / `axios` connections in views instead of utilizing `createRequest` or `useRequest` from `@yh-ui/request`.
|
|
33
|
-
- **
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
- Flow canvas without height.
|
|
39
|
-
- Provider secrets in Vite env variables or browser bundles.
|
|
40
|
-
- Direct prop mutation, untyped emits, inaccessible icon buttons, or browser-only APIs during SSR.
|
|
56
|
+
- **Guessing Component APIs**: Accessing non-existent component methods or passing non-existent properties (e.g., trying to call `tableRef.value.getSelected()` when only `getSelectionRows()` exists).
|
|
57
|
+
- **Untyped Ref Handles**: Declaring component template refs without type-casting them using `InstanceType<typeof ...>`.
|
|
58
|
+
- **SSR Hydration Failure**: Direct window/document access during the root setup execution scope.
|
|
59
|
+
- **SSR Memory Leak**: Global, shared mutable reactive state that persists across requests.
|
|
60
|
+
- **Missing Cleanup**: Leaving running intervals, observers, or window resize event listeners active after a component is unmounted.
|
|
@@ -149,9 +149,7 @@ Expose: `clearFilter`, `clearSelection`, `clearSort`, `doLayout`, `exportData`,
|
|
|
149
149
|
|
|
150
150
|
Use hooks from these source modules:
|
|
151
151
|
|
|
152
|
-
`useNamespace`, `useZIndex`, `useSKU`, `useCountdown`, `useLocale`, `useId`, `
|
|
153
|
-
|
|
154
|
-
> **Note**: `useYhId` is an alias for `useId` exported from `@yh-ui/hooks`. Use `useYhId` when naming conflicts with Vue 3.5+ native `useId` are a concern. In Nuxt, `useYhId` is also available as an auto-import from `@yh-ui/hooks`.
|
|
152
|
+
`useNamespace`, `useZIndex`, `useSKU`, `useCountdown`, `useLocale`, `useId`, `useFormItem`, `useVirtualScroll`, `useCache`, `useEventListener`, `useScrollLock`, `useClickOutside`, `useConfig`, AI hooks from `use-ai`, and reactive storage helpers from `storage`.
|
|
155
153
|
|
|
156
154
|
## `@yh-ui/flow` Exports
|
|
157
155
|
|
|
@@ -198,7 +196,7 @@ Use these real capabilities:
|
|
|
198
196
|
|
|
199
197
|
The Nuxt module registers component names using the configured prefix, default `Yh`. It also auto-imports common hooks and globals from `@yh-ui/hooks` and `@yh-ui/components`.
|
|
200
198
|
|
|
201
|
-
Important nuance: native `useId` is not overridden. YH-UI's hook is
|
|
199
|
+
Important nuance: native `useId` is not overridden. YH-UI's hook is imported as `useYhId`.
|
|
202
200
|
|
|
203
201
|
## Known Non-Goals
|
|
204
202
|
|
|
@@ -1,147 +1,222 @@
|
|
|
1
|
-
# Vue Component Practices
|
|
2
|
-
|
|
3
|
-
Use these rules when generating or reviewing YH-UI Vue 3 code. They combine Vue official guidance
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
1.
|
|
11
|
-
2.
|
|
12
|
-
3.
|
|
13
|
-
4.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
# Vue Component Practices (Evolved Standard)
|
|
2
|
+
|
|
3
|
+
Use these rules when generating or reviewing YH-UI Vue 3.5+ code. They combine Vue official core guidance, Anthony Fu (antfu) best practices, and industry-leading component-library standards.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Imports & SFC Code Structure (Antfu Style)
|
|
8
|
+
|
|
9
|
+
- **Strict Import Grouping & Sorting**:
|
|
10
|
+
1. **Core dependencies**: Vue core APIs (e.g. `ref`, `computed`, `watch`, `onMounted`).
|
|
11
|
+
2. **Third-party / Tooling libraries**: (e.g. `@yh-ui/request`, `@yh-ui/ai-sdk`).
|
|
12
|
+
3. **Local components and hooks**: (e.g. `@yh-ui/components`, `@yh-ui/hooks`).
|
|
13
|
+
4. **Types and Interfaces**: Always separate runtime imports from type-only imports using `import type`.
|
|
14
|
+
```ts
|
|
15
|
+
import { computed, onMounted, ref } from 'vue'
|
|
16
|
+
import { useRequest } from '@yh-ui/request'
|
|
17
|
+
import { YhTable } from '@yh-ui/components'
|
|
18
|
+
import type { TableColumn } from '@yh-ui/components'
|
|
19
|
+
```
|
|
20
|
+
- **SFC Block Ordering & Language Defaults**: Always write scripts in TypeScript (`lang="ts"`) and styles in SCSS/Sass (`lang="scss"`) by default. Layout files strictly as:
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<script setup lang="ts">
|
|
24
|
+
// TypeScript Logic
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<template>
|
|
28
|
+
<!-- HTML Structure -->
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<style scoped lang="scss">
|
|
32
|
+
/* SCSS Scoped Styling */
|
|
33
|
+
</style>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- **Scoped Styles Requirement**: Styles must always be `scoped` to prevent selector leaking. Prefer CSS custom properties (Variables) with sensible fallbacks:
|
|
37
|
+
```css
|
|
38
|
+
.yh-element {
|
|
39
|
+
color: var(--yh-color-primary, #1d4ed8);
|
|
40
|
+
background-color: var(--yh-bg-color, #ffffff);
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 2. Props, Emits, and v-model
|
|
47
|
+
|
|
48
|
+
- **Reactive Props Destructuring (Vue 3.5+)**:
|
|
49
|
+
Always use Vue 3.5 native reactive destructuring for props, supplying defaults directly in the destructure expression. Avoid verbose `withDefaults`.
|
|
50
|
+
|
|
25
51
|
```ts
|
|
26
|
-
|
|
52
|
+
interface Props {
|
|
53
|
+
size?: 'small' | 'default' | 'large'
|
|
54
|
+
disabled?: boolean
|
|
55
|
+
items?: string[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const { size = 'default', disabled = false, items = () => [] } = defineProps<Props>()
|
|
27
59
|
```
|
|
28
|
-
This is the official Vue 3.5+ best practice and avoids verbose `withDefaults`.
|
|
29
|
-
- For Vue < 3.5 or projects without destructure support, fallback to `withDefaults(defineProps<Props>(), defaults)`.
|
|
30
|
-
- Use typed `defineEmits` or `defineModel` for two-way bindings:
|
|
31
60
|
|
|
61
|
+
- **Two-way Bindings with `defineModel`**:
|
|
62
|
+
Use the Vue 3.4+ native `defineModel` macro for cleaner two-way data flows:
|
|
32
63
|
```ts
|
|
33
|
-
// Two-way binding model (Vue 3.4+)
|
|
34
64
|
const modelValue = defineModel<string>({ default: '' })
|
|
35
|
-
|
|
36
|
-
|
|
65
|
+
```
|
|
66
|
+
- **Typed Emits**:
|
|
67
|
+
Always declare emits with strict TypeScript tuple parameters instead of array strings:
|
|
68
|
+
```ts
|
|
37
69
|
const emit = defineEmits<{
|
|
38
70
|
change: [value: string]
|
|
39
71
|
submit: []
|
|
40
72
|
}>()
|
|
41
73
|
```
|
|
42
74
|
|
|
43
|
-
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 3. Slots & Composition Customization
|
|
78
|
+
|
|
79
|
+
- **Flexible Slot Design (`slots`)**:
|
|
80
|
+
Prefer using Vue `slots` for customizable templates over hard-coded markup. Always declare and use named slots (e.g. `header`, `footer`, `actions`, `empty`) to provide clear extension templates:
|
|
81
|
+
```html
|
|
82
|
+
<slot name="header">
|
|
83
|
+
<h3>Default Header Title</h3>
|
|
84
|
+
</slot>
|
|
85
|
+
```
|
|
86
|
+
- **Component Composition**:
|
|
87
|
+
Prefer composing multiple YH-UI library components together (e.g. nesting slots, wrapper components) before deciding to write raw custom controls.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 4. Reactivity and Composable Design (Antfu & Vueuse Standards)
|
|
92
|
+
|
|
93
|
+
- **Prefer `ref()` over `reactive()`**:
|
|
94
|
+
Use `ref()` for all state variables (including objects and arrays) to keep destructuring clean and avoid losing reactivity. Use `reactive()` only when managing deep, nested form states that must be passed as a single object.
|
|
95
|
+
- **MaybeRefOrGetter and toValue (Vue 3.3+)**:
|
|
96
|
+
When writing reusable hooks/composables that accept reactive inputs, declare them as `MaybeRefOrGetter<T>` to accept raw values, Refs, or Getter functions. Use `toValue()` to retrieve their current value reactively:
|
|
44
97
|
|
|
45
|
-
|
|
98
|
+
```ts
|
|
99
|
+
import { toValue } from 'vue'
|
|
100
|
+
import type { MaybeRefOrGetter } from 'vue'
|
|
46
101
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
102
|
+
export function useFeature(enabled: MaybeRefOrGetter<boolean>) {
|
|
103
|
+
const isActive = computed(() => toValue(enabled))
|
|
104
|
+
}
|
|
105
|
+
```
|
|
51
106
|
|
|
52
|
-
|
|
107
|
+
- **Scope Disposal for Composables**:
|
|
108
|
+
Always clean up active watchers, event listeners, and side-effects inside composables using `onScopeDispose` so they can be clean-run in non-component reactive scopes (like Pinia stores):
|
|
53
109
|
|
|
54
|
-
- Use `ref` for primitive values, lists/arrays, or objects that will be completely overwritten.
|
|
55
|
-
- Use `reactive` only for complex nested states that require deep property mutation.
|
|
56
|
-
- Use `computed` for all derived values instead of writing sync watchers or manually maintaining double state variables.
|
|
57
|
-
- Use `watch` or `watchEffect` solely for side-effects (e.g. storage sync, async API triggers). Always specify `immediate: true` or `deep: true` only if required.
|
|
58
|
-
- Use `shallowRef` for heavy objects, DOM nodes, third-party library instances (ECharts, Monco Editor, Flow canvases), as deep reactivity on these will degrade performance.
|
|
59
|
-
- **Critical Lifecycle Cleanups**: Always clean up timeouts, intervals, ResizeObservers, WebSocket event listeners, and custom event listeners in `onUnmounted`:
|
|
60
110
|
```ts
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
111
|
+
import { onScopeDispose, watch } from 'vue'
|
|
112
|
+
|
|
113
|
+
watch(source, (val) => {
|
|
114
|
+
// side effect
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
onScopeDispose(() => {
|
|
118
|
+
// clean up side effect if scope is destroyed
|
|
64
119
|
})
|
|
65
120
|
```
|
|
66
121
|
|
|
67
|
-
|
|
122
|
+
- **Shallow Ref for Performance (`shallowRef`)**:
|
|
123
|
+
Always wrap heavy non-reactive objects, DOM nodes, third-party libraries (e.g. Monaco Editor, ECharts, Flow canvas objects) in `shallowRef()` instead of `ref()` to bypass Vue's deep proxy traversal, saving significant CPU cycles:
|
|
124
|
+
```ts
|
|
125
|
+
import { shallowRef } from 'vue'
|
|
126
|
+
const editorInstance = shallowRef<any>(null)
|
|
127
|
+
```
|
|
68
128
|
|
|
69
|
-
|
|
70
|
-
- Ensure icon-only buttons have an accessible label.
|
|
71
|
-
- Keep form labels visible through `YhFormItem` or explicit `aria-label`.
|
|
72
|
-
- Preserve keyboard behavior for dialogs, drawers, dropdowns, popovers, tabs, and menus.
|
|
73
|
-
- Do not remove focus outlines unless replacing them with an equally visible focus style.
|
|
129
|
+
---
|
|
74
130
|
|
|
75
|
-
## Performance
|
|
131
|
+
## 5. Performance & DOM Optimization
|
|
76
132
|
|
|
77
|
-
-
|
|
78
|
-
|
|
79
|
-
- Avoid creating new object/function literals in hot template loops when they can be stable constants.
|
|
80
|
-
- Use `v-show` for frequent toggles and `v-if` for expensive conditional branches.
|
|
81
|
-
- Use stable `:key` values in `v-for`; never use array index when item identity exists.
|
|
82
|
-
- Keep expensive formatting in computed values or memoized helpers.
|
|
133
|
+
- **Template Ref Binding**:
|
|
134
|
+
Type template refs cleanly using Vue's component instance types. Keep the ref initializer empty (without passing `null`) since Vue will automatically assign it on mount:
|
|
83
135
|
|
|
84
|
-
|
|
136
|
+
```ts
|
|
137
|
+
import { ref } from 'vue'
|
|
138
|
+
import { YhTable } from '@yh-ui/components'
|
|
85
139
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
- Keep server-rendered initial state deterministic.
|
|
140
|
+
const tableRef = ref<InstanceType<typeof YhTable>>()
|
|
141
|
+
// Access: tableRef.value?.exportData()
|
|
142
|
+
```
|
|
90
143
|
|
|
91
|
-
|
|
144
|
+
- **v-once and v-memo**:
|
|
145
|
+
- Use `v-once` for complex static text, icons, or banners that never update after initial mount.
|
|
146
|
+
- Use `v-memo` for virtual lists, heavy table columns, or grids to selectively re-trigger patch loops only when specific criteria changes:
|
|
147
|
+
```html
|
|
148
|
+
<div v-for="item in list" :key="item.id" v-memo="[item.updatedAt, selectedId === item.id]">
|
|
149
|
+
<!-- Deep cell content -->
|
|
150
|
+
</div>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 6. Accessibility Standards (A11y)
|
|
156
|
+
|
|
157
|
+
- **Accessible Outlines & Semantic HTML**:
|
|
158
|
+
Never strip accessibility focus outlines without supplying custom visible focus states. Rely on semantic HTML structure (`<nav>`, `<main>`, `<article>`, `<header>`) when writing template wrappers.
|
|
159
|
+
- **Labels & Aria attributes**:
|
|
160
|
+
Ensure that icon-only buttons define a descriptive `aria-label` attribute. Keep inputs properly linked with visual labels or explicit `aria-label` placeholders to guarantee full screen-reader accessibility.
|
|
161
|
+
- **Keyboard Navigation**:
|
|
162
|
+
Maintain correct keyboard accessibility tab indexes and focus-trap logic inside overlay views (like dialogs, drawer panels, or search drop-downs).
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 7. SSR & Hydration Safeguards (Nuxt Compatibility)
|
|
167
|
+
|
|
168
|
+
- **Zero Browser API Calls during Setup**:
|
|
169
|
+
- Do not access `window`, `document`, `navigator`, or `localStorage`/`sessionStorage` directly in the setup scope.
|
|
170
|
+
- Wrap all browser-specific execution inside `onMounted` or gate it using `import.meta.client` (or `typeof window !== 'undefined'`):
|
|
171
|
+
```ts
|
|
172
|
+
onMounted(() => {
|
|
173
|
+
const token = localStorage.getItem('auth-token')
|
|
174
|
+
})
|
|
175
|
+
```
|
|
176
|
+
- **Memory Leak Prevention on Server**:
|
|
177
|
+
Never declare component-level or page-level mutable state in global singleton module-level variables. All reactive state must be wrapped inside `setup()` or Pinia boundaries to prevent cross-request state pollution during SSR.
|
|
178
|
+
- **ClientOnly Wrapping**:
|
|
179
|
+
For charts, code editors, and canvas components (like `@yh-ui/flow`), wrap them inside the `<ClientOnly>` component (in Nuxt) or gate them via dynamic mounting triggers:
|
|
180
|
+
```html
|
|
181
|
+
<ClientOnly fallback="Loading canvas...">
|
|
182
|
+
<Flow :nodes="nodes" :edges="edges" />
|
|
183
|
+
</ClientOnly>
|
|
184
|
+
```
|
|
92
185
|
|
|
93
|
-
|
|
94
|
-
- Keep search filters separate from committed query params when a page needs reset/search behavior.
|
|
95
|
-
- Include loading, error, empty, and success feedback for request-driven screens.
|
|
96
|
-
- Use `YhMessage`, `YhNotification`, `YhPopconfirm`, and `YhMessageBox` for user feedback instead of ad hoc alerts.
|
|
97
|
-
- Use `YhTable` columns as stable constants or computed values, not inline arrays in templates.
|
|
186
|
+
---
|
|
98
187
|
|
|
99
|
-
##
|
|
188
|
+
## 8. CSS BEM Namespace (YH-UI Library Rule)
|
|
100
189
|
|
|
101
|
-
-
|
|
102
|
-
- Do not rename props/events unless the user explicitly asks for a breaking change.
|
|
103
|
-
- Keep component props small and orthogonal.
|
|
104
|
-
- Prefer CSS variables and existing theme tokens over hard-coded colors.
|
|
105
|
-
- Use `useNamespace` for internal BEM-style component classes when editing package components.
|
|
106
|
-
- Use `useZIndex`, `useLocale`, `useId`/`useYhId`, and config hooks instead of local one-off implementations.
|
|
190
|
+
When modifying or building library components, use YH-UI's built-in namespace utility `useNamespace` to auto-generate standard classes following the BEM (Block-Element-Modifier) pattern:
|
|
107
191
|
|
|
108
|
-
|
|
192
|
+
```ts
|
|
193
|
+
import { useNamespace } from '@yh-ui/hooks'
|
|
109
194
|
|
|
110
|
-
|
|
195
|
+
const ns = useNamespace('my-button')
|
|
196
|
+
// ns.b() -> 'yh-my-button'
|
|
197
|
+
// ns.e('icon') -> 'yh-my-button__icon'
|
|
198
|
+
// ns.m('disabled') -> 'yh-my-button--disabled'
|
|
199
|
+
```
|
|
111
200
|
|
|
112
|
-
|
|
113
|
-
2. **Handle Feature Gaps (Encapsulation/Extension)**: If a component is missing a sub-feature, do not abandon the component. Apply extension patterns in order of priority:
|
|
114
|
-
- **Slot Customization**: Use slots (e.g., `#toolbar`, `#empty`, `#default` custom cells) to inject the custom logic.
|
|
115
|
-
- **Props & Event Listeners**: Use component event bindings and dynamic property configs to override behavior.
|
|
116
|
-
- **CSS Variable Injection**: Inject style variables (e.g. style overrides like `--yh-button-font-weight`) to alter theme aesthetics without altering core element code.
|
|
117
|
-
- **Composite Patterns**: Group multiple YH-UI components together (e.g. wrap a `YhTable` and `YhPagination` or nesting slots) before trying to write raw elements.
|
|
118
|
-
3. **Raw Component Last Resort**: Only build a custom element from scratch if the functionality is entirely absent in the UI framework and cannot be composed/wrapped. You must document this reasoning in the code comments.
|
|
201
|
+
---
|
|
119
202
|
|
|
120
|
-
##
|
|
203
|
+
## 9. Lifecycle Cleanups Checklist
|
|
121
204
|
|
|
122
|
-
To
|
|
205
|
+
To ensure absolute stability, every component **must** clean up all registered side-effects inside `onUnmounted` / `onBeforeUnmount`:
|
|
123
206
|
|
|
124
|
-
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
import type { FlowNode, FlowEdge } from '@yh-ui/flow'
|
|
128
|
-
import type { ThemeOptions } from '@yh-ui/theme'
|
|
129
|
-
```
|
|
130
|
-
- **Type Template Refs**: Never leave component template refs untyped (`const refVal = ref(null)`). Always use Vue's `InstanceType` utility to extract component exports:
|
|
207
|
+
- Clear all `setTimeout` and `setInterval` handles.
|
|
208
|
+
- Disconnect `ResizeObserver`, `IntersectionObserver`, and `MutationObserver` instances.
|
|
209
|
+
- Remove event listeners bound to `window`, `document`, or custom event emitter nodes:
|
|
131
210
|
|
|
132
211
|
```ts
|
|
133
|
-
|
|
134
|
-
|
|
212
|
+
const onResize = () => {
|
|
213
|
+
/* ... */
|
|
214
|
+
}
|
|
215
|
+
window.addEventListener('resize', onResize)
|
|
135
216
|
|
|
136
|
-
|
|
217
|
+
onUnmounted(() => {
|
|
218
|
+
window.removeEventListener('resize', onResize)
|
|
219
|
+
})
|
|
137
220
|
```
|
|
138
221
|
|
|
139
|
-
-
|
|
140
|
-
- **Data Models**: Strongly type response objects and row lists using `interface` declarations; never pass `any` into data bindings or request helper promises.
|
|
141
|
-
|
|
142
|
-
## Testing Expectations
|
|
143
|
-
|
|
144
|
-
- For reusable components, test props, emitted events, slots, keyboard/focus behavior, and important visual states.
|
|
145
|
-
- For composables, test state transitions and cleanup.
|
|
146
|
-
- For request code, test loading, success, error, retry, and cancellation when present.
|
|
147
|
-
- For Nuxt/SSR examples, check that browser-only code is gated.
|
|
222
|
+
- Clean up active audio/video elements, media recorders, or streaming controllers (like LangChain's `AbortController`).
|