@kine-design/core 0.0.1-beta.11 → 0.0.1-beta.13

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.
Files changed (67) hide show
  1. package/components/base/image/__tests__/useImage.test.ts +61 -0
  2. package/components/base/image/api.ts +2 -2
  3. package/components/base/image/index.ts +2 -2
  4. package/components/base/image/props.d.ts +15 -4
  5. package/components/base/image/useImage.ts +27 -16
  6. package/components/base/tooltip/api.ts +3 -2
  7. package/components/base/tooltip/index.ts +3 -2
  8. package/components/base/tooltip/props.d.ts +12 -6
  9. package/components/base/tooltip/useTooltip.ts +175 -56
  10. package/compositions/commandPalette/index.ts +11 -0
  11. package/compositions/commandPalette/types.ts +29 -0
  12. package/compositions/commandPalette/useCommandPalette.ts +135 -0
  13. package/compositions/contextMenu/index.ts +10 -0
  14. package/compositions/contextMenu/types.ts +21 -0
  15. package/compositions/contextMenu/useContextMenu.ts +101 -0
  16. package/compositions/editor/index.ts +18 -0
  17. package/compositions/editor/types.ts +147 -0
  18. package/compositions/editor/useEditor.ts +224 -0
  19. package/compositions/index.ts +15 -0
  20. package/compositions/overlay/index.ts +14 -0
  21. package/compositions/overlay/useOverlayStack.ts +146 -0
  22. package/compositions/peekView/index.ts +10 -0
  23. package/compositions/peekView/usePeekView.ts +99 -0
  24. package/compositions/theme/index.ts +17 -0
  25. package/compositions/theme/presets/compact.ts +117 -0
  26. package/compositions/theme/presets/dark.ts +113 -0
  27. package/compositions/theme/presets/index.ts +11 -0
  28. package/compositions/theme/presets/light.ts +113 -0
  29. package/compositions/theme/types.ts +46 -0
  30. package/compositions/theme/useTheme.ts +269 -0
  31. package/compositions/toast/index.ts +10 -0
  32. package/compositions/toast/useToast.ts +176 -0
  33. package/compositions/tooltip/index.ts +9 -0
  34. package/compositions/tooltip/useTooltip.ts +10 -0
  35. package/dist/components/base/image/index.d.ts +2 -2
  36. package/dist/components/base/image/useImage.d.ts +7 -0
  37. package/dist/components/base/tooltip/index.d.ts +1 -0
  38. package/dist/components/base/tooltip/useTooltip.d.ts +20 -17
  39. package/dist/compositions/commandPalette/index.d.ts +11 -0
  40. package/dist/compositions/commandPalette/types.d.ts +20 -0
  41. package/dist/compositions/commandPalette/useCommandPalette.d.ts +32 -0
  42. package/dist/compositions/contextMenu/index.d.ts +10 -0
  43. package/dist/compositions/contextMenu/types.d.ts +12 -0
  44. package/dist/compositions/contextMenu/useContextMenu.d.ts +52 -0
  45. package/dist/compositions/editor/index.d.ts +10 -0
  46. package/dist/compositions/editor/types.d.ts +132 -0
  47. package/dist/compositions/editor/useEditor.d.ts +13 -0
  48. package/dist/compositions/index.d.ts +15 -0
  49. package/dist/compositions/overlay/index.d.ts +10 -0
  50. package/dist/compositions/overlay/useOverlayStack.d.ts +188 -0
  51. package/dist/compositions/peekView/index.d.ts +10 -0
  52. package/dist/compositions/peekView/usePeekView.d.ts +16 -0
  53. package/dist/compositions/theme/index.d.ts +11 -0
  54. package/dist/compositions/theme/presets/compact.d.ts +6 -0
  55. package/dist/compositions/theme/presets/dark.d.ts +2 -0
  56. package/dist/compositions/theme/presets/index.d.ts +11 -0
  57. package/dist/compositions/theme/presets/light.d.ts +2 -0
  58. package/dist/compositions/theme/types.d.ts +41 -0
  59. package/dist/compositions/theme/useTheme.d.ts +167 -0
  60. package/dist/compositions/toast/index.d.ts +10 -0
  61. package/dist/compositions/toast/useToast.d.ts +71 -0
  62. package/dist/compositions/tooltip/index.d.ts +9 -0
  63. package/dist/compositions/tooltip/useTooltip.d.ts +10 -0
  64. package/dist/core.js +836 -56
  65. package/dist/index.d.ts +1 -0
  66. package/index.ts +2 -0
  67. package/package.json +13 -2
@@ -0,0 +1,269 @@
1
+ /**
2
+ * @description theme store — runtime design token engine with CSS variable injection
3
+ * @author kine-design
4
+ * @date 2026/5/22
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { defineStore } from 'pinia';
10
+ import { computed, ref, watch } from 'vue';
11
+ import type {
12
+ ThemeDefinition,
13
+ ThemeTokenCategory,
14
+ ThemeOverrides,
15
+ PersistedThemeState,
16
+ ThemeTokens,
17
+ } from './types';
18
+ import { lightTheme, darkTheme, compactTheme } from './presets';
19
+
20
+ const STORAGE_KEY = 'kine-theme';
21
+ const TRANSITION_CLASS = 'kine-theme-transitioning';
22
+
23
+ /**
24
+ * Detect OS-level dark mode preference.
25
+ * Returns 'dark' if the user prefers dark color scheme, 'light' otherwise.
26
+ */
27
+ function detectColorScheme(): 'light' | 'dark' {
28
+ if (typeof window === 'undefined') return 'light';
29
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
30
+ }
31
+
32
+ /**
33
+ * Load persisted theme state from localStorage.
34
+ */
35
+ function loadPersistedState(): PersistedThemeState | null {
36
+ if (typeof window === 'undefined') return null;
37
+ try {
38
+ const raw = localStorage.getItem(STORAGE_KEY);
39
+ if (!raw) return null;
40
+ return JSON.parse(raw) as PersistedThemeState;
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Save theme state to localStorage.
48
+ */
49
+ function persistState(state: PersistedThemeState): void {
50
+ if (typeof window === 'undefined') return;
51
+ try {
52
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
53
+ } catch {
54
+ // storage full or unavailable — silently ignore
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Apply a flat map of CSS variables to document.documentElement.style.
60
+ * Variables follow the naming convention `--kine-{category}-{key}`.
61
+ */
62
+ function applyCSSVariables(tokens: Partial<ThemeTokens>): void {
63
+ if (typeof document === 'undefined') return;
64
+ const root = document.documentElement;
65
+
66
+ const categories = Object.keys(tokens) as ThemeTokenCategory[];
67
+ for (const category of categories) {
68
+ const entries = tokens[category];
69
+ if (!entries) continue;
70
+ for (const [key, value] of Object.entries(entries)) {
71
+ root.style.setProperty(`--kine-${category}-${key}`, value);
72
+ }
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Add a brief transition to body so theme switch feels smooth.
78
+ */
79
+ function enableTransition(): void {
80
+ if (typeof document === 'undefined') return;
81
+ const el = document.documentElement;
82
+ el.classList.add(TRANSITION_CLASS);
83
+
84
+ // inject the transition style once
85
+ let styleEl = document.getElementById('kine-theme-transition');
86
+ if (!styleEl) {
87
+ styleEl = document.createElement('style');
88
+ styleEl.id = 'kine-theme-transition';
89
+ styleEl.textContent = `
90
+ .${TRANSITION_CLASS},
91
+ .${TRANSITION_CLASS} *,
92
+ .${TRANSITION_CLASS} *::before,
93
+ .${TRANSITION_CLASS} *::after {
94
+ transition: background-color 0.3s, color 0.3s, border-color 0.3s, box-shadow 0.3s !important;
95
+ }
96
+ `;
97
+ document.head.appendChild(styleEl);
98
+ }
99
+
100
+ // remove transition class after animation completes to avoid perf overhead
101
+ setTimeout(() => {
102
+ el.classList.remove(TRANSITION_CLASS);
103
+ }, 350);
104
+ }
105
+
106
+ /**
107
+ * Toggle the `dark` attribute on <html> to coordinate with base.css html[dark] selectors.
108
+ */
109
+ function toggleDarkAttribute(isDark: boolean): void {
110
+ if (typeof document === 'undefined') return;
111
+ if (isDark) {
112
+ document.documentElement.setAttribute('dark', '');
113
+ } else {
114
+ document.documentElement.removeAttribute('dark');
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Merge base theme tokens with user overrides (overrides win).
120
+ */
121
+ function mergeTokens(base: Partial<ThemeTokens>, overrides: ThemeOverrides): Partial<ThemeTokens> {
122
+ const merged: Partial<ThemeTokens> = {};
123
+ const allCategories = new Set<ThemeTokenCategory>([
124
+ ...Object.keys(base) as ThemeTokenCategory[],
125
+ ...Object.keys(overrides) as ThemeTokenCategory[],
126
+ ]);
127
+
128
+ for (const cat of allCategories) {
129
+ merged[cat] = {
130
+ ...(base[cat] ?? {}),
131
+ ...(overrides[cat] ?? {}),
132
+ };
133
+ }
134
+ return merged;
135
+ }
136
+
137
+ // ---------------------------------------------------------------------------
138
+ // Store
139
+ // ---------------------------------------------------------------------------
140
+
141
+ export const useThemeStore = defineStore('kine-theme', () => {
142
+ // --- state ---
143
+ const themes = ref<Map<string, ThemeDefinition>>(new Map());
144
+ const currentThemeId = ref<string>('light');
145
+ const overrides = ref<ThemeOverrides>({});
146
+
147
+ // --- getters ---
148
+ const currentTheme = computed<ThemeDefinition | undefined>(
149
+ () => themes.value.get(currentThemeId.value),
150
+ );
151
+
152
+ const resolvedTokens = computed<Partial<ThemeTokens>>(() => {
153
+ const base = currentTheme.value?.tokens ?? {};
154
+ return mergeTokens(base, overrides.value);
155
+ });
156
+
157
+ const isDark = computed(() => currentThemeId.value === 'dark');
158
+
159
+ const themeList = computed<ThemeDefinition[]>(() => Array.from(themes.value.values()));
160
+
161
+ // --- actions ---
162
+
163
+ /**
164
+ * Register a theme preset. If a theme with the same id exists, it is replaced.
165
+ */
166
+ function registerTheme(definition: ThemeDefinition): void {
167
+ // use a new Map so Vue detects the change
168
+ const next = new Map(themes.value);
169
+ next.set(definition.id, definition);
170
+ themes.value = next;
171
+ }
172
+
173
+ /**
174
+ * Switch to a registered theme by id.
175
+ */
176
+ function setTheme(id: string): void {
177
+ if (!themes.value.has(id)) {
178
+ console.warn(`[kine-theme] theme "${id}" is not registered`);
179
+ return;
180
+ }
181
+ enableTransition();
182
+ currentThemeId.value = id;
183
+ }
184
+
185
+ /**
186
+ * Set a single token override (survives theme switches).
187
+ */
188
+ function setTokenOverride(category: ThemeTokenCategory, key: string, value: string): void {
189
+ const prev = overrides.value[category] ?? {};
190
+ overrides.value = {
191
+ ...overrides.value,
192
+ [category]: { ...prev, [key]: value },
193
+ };
194
+ }
195
+
196
+ /**
197
+ * Clear all user-level token overrides.
198
+ */
199
+ function resetOverrides(): void {
200
+ overrides.value = {};
201
+ }
202
+
203
+ /**
204
+ * Initialize the theme system: register built-in presets, restore persisted
205
+ * state (or detect OS preference), and apply CSS variables.
206
+ * Called automatically by KThemeProvider or can be called manually.
207
+ */
208
+ function init(): void {
209
+ // register built-in presets
210
+ registerTheme(lightTheme);
211
+ registerTheme(darkTheme);
212
+ registerTheme(compactTheme);
213
+
214
+ // restore persisted state
215
+ const persisted = loadPersistedState();
216
+ if (persisted && themes.value.has(persisted.currentTheme)) {
217
+ currentThemeId.value = persisted.currentTheme;
218
+ overrides.value = persisted.overrides ?? {};
219
+ } else {
220
+ // no saved preference — use OS color scheme
221
+ currentThemeId.value = detectColorScheme();
222
+ }
223
+
224
+ // apply immediately (no transition on first paint)
225
+ applyCurrentTheme();
226
+ }
227
+
228
+ /**
229
+ * Apply the resolved tokens as CSS variables and sync dark attribute.
230
+ */
231
+ function applyCurrentTheme(): void {
232
+ applyCSSVariables(resolvedTokens.value);
233
+ toggleDarkAttribute(isDark.value);
234
+ }
235
+
236
+ // --- persistence & side-effect watchers ---
237
+
238
+ watch(
239
+ [currentThemeId, overrides],
240
+ () => {
241
+ applyCurrentTheme();
242
+ persistState({
243
+ currentTheme: currentThemeId.value,
244
+ overrides: overrides.value,
245
+ });
246
+ },
247
+ { deep: true },
248
+ );
249
+
250
+ return {
251
+ // state
252
+ currentThemeId,
253
+ themes,
254
+ overrides,
255
+
256
+ // getters
257
+ currentTheme,
258
+ resolvedTokens,
259
+ isDark,
260
+ themeList,
261
+
262
+ // actions
263
+ registerTheme,
264
+ setTheme,
265
+ setTokenOverride,
266
+ resetOverrides,
267
+ init,
268
+ };
269
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @description toast composition 导出
3
+ * @author 阿怪
4
+ * @date 2026/5/22
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ export { useToast } from './useToast';
10
+ export type { ToastType, ToastItem, ToastShowOptions, ToastPromiseOptions } from './useToast';
@@ -0,0 +1,176 @@
1
+ /**
2
+ * @description toast 队列 composable — Promise-based 通知系统
3
+ * @author 阿怪
4
+ * @date 2026/5/22
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { ref } from 'vue';
10
+
11
+ /**
12
+ * Toast 类型
13
+ */
14
+ export type ToastType = 'success' | 'error' | 'warning' | 'info' | 'loading';
15
+
16
+ /**
17
+ * Toast 队列中每一条 toast 的内部结构
18
+ */
19
+ export interface ToastItem {
20
+ id: number;
21
+ type: ToastType;
22
+ title: string;
23
+ message?: string;
24
+ duration: number;
25
+ createdAt: number;
26
+ }
27
+
28
+ /**
29
+ * 创建 toast 时的配置参数
30
+ */
31
+ export interface ToastShowOptions {
32
+ type?: ToastType;
33
+ title: string;
34
+ message?: string;
35
+ duration?: number;
36
+ }
37
+
38
+ /**
39
+ * promise() 方法的三阶段配置
40
+ */
41
+ export interface ToastPromiseOptions<T> {
42
+ loading: string | { title: string; message?: string };
43
+ success: string | { title: string; message?: string } | ((data: T) => string | { title: string; message?: string });
44
+ error: string | { title: string; message?: string } | ((err: unknown) => string | { title: string; message?: string });
45
+ }
46
+
47
+ /** 各类型默认自动关闭时间(毫秒) */
48
+ const DEFAULT_DURATIONS: Record<ToastType, number> = {
49
+ success: 3000,
50
+ error: 5000,
51
+ warning: 4000,
52
+ info: 3000,
53
+ loading: 0, // loading never auto-dismiss
54
+ };
55
+
56
+ /** 最大可见 toast 数量 */
57
+ const MAX_VISIBLE = 3;
58
+
59
+ let nextId = 0;
60
+
61
+ /**
62
+ * Toast 队列 composable
63
+ * 管理 toast 的增删、自动关闭定时器与 Promise 模式
64
+ */
65
+ export function useToast() {
66
+ const toasts = ref<ToastItem[]>([]);
67
+ const timers = new Map<number, ReturnType<typeof setTimeout>>();
68
+
69
+ /**
70
+ * 展示一条 toast,返回 id
71
+ */
72
+ const show = (options: ToastShowOptions): number => {
73
+ const id = nextId++;
74
+ const type = options.type ?? 'info';
75
+ const duration = options.duration ?? DEFAULT_DURATIONS[type];
76
+
77
+ const item: ToastItem = {
78
+ id,
79
+ type,
80
+ title: options.title,
81
+ message: options.message,
82
+ duration,
83
+ createdAt: Date.now(),
84
+ };
85
+
86
+ toasts.value.push(item);
87
+
88
+ // enforce max visible — remove oldest (non-loading) if exceeding limit
89
+ while (toasts.value.length > MAX_VISIBLE) {
90
+ const oldest = toasts.value.find(t => t.type !== 'loading');
91
+ if (oldest) {
92
+ dismiss(oldest.id);
93
+ } else {
94
+ // all are loading, remove the very first
95
+ dismiss(toasts.value[0].id);
96
+ }
97
+ }
98
+
99
+ if (duration > 0) {
100
+ const timer = setTimeout(() => dismiss(id), duration);
101
+ timers.set(id, timer);
102
+ }
103
+
104
+ return id;
105
+ };
106
+
107
+ /**
108
+ * 按 id 移除一条 toast
109
+ */
110
+ const dismiss = (id: number): void => {
111
+ const idx = toasts.value.findIndex(t => t.id === id);
112
+ if (idx !== -1) {
113
+ toasts.value.splice(idx, 1);
114
+ }
115
+ const timer = timers.get(id);
116
+ if (timer) {
117
+ clearTimeout(timer);
118
+ timers.delete(id);
119
+ }
120
+ };
121
+
122
+ /**
123
+ * 将 stage 配置标准化为 { title, message }
124
+ */
125
+ function normalizeStage(stage: string | { title: string; message?: string }): { title: string; message?: string } {
126
+ if (typeof stage === 'string') {
127
+ return { title: stage };
128
+ }
129
+ return stage;
130
+ }
131
+
132
+ /**
133
+ * Promise toast pattern:
134
+ * - 立即展示 loading toast
135
+ * - resolve → 替换为 success toast
136
+ * - reject → 替换为 error toast
137
+ */
138
+ const promise = <T>(
139
+ p: Promise<T>,
140
+ options: ToastPromiseOptions<T>,
141
+ ): Promise<T> => {
142
+ const loadingStage = normalizeStage(options.loading);
143
+ const loadingId = show({
144
+ type: 'loading',
145
+ title: loadingStage.title,
146
+ message: loadingStage.message,
147
+ });
148
+
149
+ return p.then(
150
+ (data) => {
151
+ dismiss(loadingId);
152
+ const raw = typeof options.success === 'function' ? options.success(data) : options.success;
153
+ const successStage = normalizeStage(raw);
154
+ show({
155
+ type: 'success',
156
+ title: successStage.title,
157
+ message: successStage.message,
158
+ });
159
+ return data;
160
+ },
161
+ (err: unknown) => {
162
+ dismiss(loadingId);
163
+ const raw = typeof options.error === 'function' ? options.error(err) : options.error;
164
+ const errorStage = normalizeStage(raw);
165
+ show({
166
+ type: 'error',
167
+ title: errorStage.title,
168
+ message: errorStage.message,
169
+ });
170
+ throw err;
171
+ },
172
+ );
173
+ };
174
+
175
+ return { toasts, show, dismiss, promise };
176
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @description tooltip compositions barrel export
3
+ * @author 阿怪
4
+ * @date 2026/5/22
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ export { useTooltip, type TooltipPlacement, type TooltipPositionData } from './useTooltip';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @description tooltip composable re-export from component layer
3
+ * @author 阿怪
4
+ * @date 2026/5/22
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ export { useTooltip } from '../../components/base/tooltip/useTooltip';
10
+ export type { TooltipPlacement, TooltipPositionData } from '../../components/base/tooltip/useTooltip';
@@ -3,5 +3,5 @@ export declare const ImageCore: {
3
3
  props: import('../../types/props').MCOPO<import('./props').ImageProps>;
4
4
  useImage: typeof useImage;
5
5
  };
6
- export type { ImageProps, ImageEvents } from './props';
7
- export type { ImageLoadStatus } from './useImage';
6
+ export type { ImageProps, ImageEvents, PreviewItem, PreviewSrcListItem } from './props';
7
+ export type { ImageLoadStatus, NormalizedPreviewItem } from './useImage';
@@ -1,12 +1,19 @@
1
1
  import { ImageProps } from './props';
2
2
  /** 图片加载状态 */
3
3
  export type ImageLoadStatus = 'loading' | 'loaded' | 'error';
4
+ /** 正规化后的预览项 */
5
+ export type NormalizedPreviewItem = {
6
+ src: string;
7
+ type: 'image' | 'video';
8
+ };
4
9
  export declare function useImage(props: Required<ImageProps>, emit: (event: 'load' | 'error' | 'switch', payload: Event | number) => void): {
5
10
  status: import('vue').Ref<ImageLoadStatus, ImageLoadStatus>;
6
11
  previewVisible: import('vue').Ref<boolean, boolean>;
7
12
  previewIndex: import('vue').Ref<number, number>;
8
13
  previewScale: import('vue').Ref<number, number>;
9
14
  previewRotate: import('vue').Ref<number, number>;
15
+ currentPreviewItem: import('vue').ComputedRef<NormalizedPreviewItem>;
16
+ currentPreviewIsVideo: import('vue').ComputedRef<boolean>;
10
17
  handleLoad: (e: Event) => void;
11
18
  handleError: (e: Event) => void;
12
19
  openPreview: () => void;
@@ -4,3 +4,4 @@ export declare const TooltipCore: {
4
4
  useTooltip: typeof useTooltip;
5
5
  };
6
6
  export type { TooltipProps } from './props';
7
+ export type { TooltipPlacement, TooltipPositionData } from './useTooltip';
@@ -1,25 +1,28 @@
1
+ import { Placement } from '../../../compositions/popper/usePopper';
1
2
  import { TooltipProps } from './props';
2
- export type TooltipStyle = {
3
- position: string;
4
- left: string;
5
- top: string;
6
- zIndex: string;
3
+ export type TooltipPlacement = Placement;
4
+ export type TooltipPositionData = {
5
+ style: Record<string, string>;
6
+ arrowStyle: Record<string, string>;
7
+ placement: Placement;
7
8
  };
8
9
  export declare function useTooltip(props: Required<TooltipProps>): {
9
10
  visible: import('vue').Ref<boolean, boolean>;
10
- tooltipStyle: import('vue').Ref<Record<string, never> | {
11
- position: string;
12
- left: string;
13
- top: string;
14
- zIndex: string;
15
- }, TooltipStyle | Record<string, never> | {
16
- position: string;
17
- left: string;
18
- top: string;
19
- zIndex: string;
20
- }>;
11
+ positionData: import('vue').Ref<{
12
+ style: Record<string, string>;
13
+ arrowStyle: Record<string, string>;
14
+ placement: Placement;
15
+ } | null, TooltipPositionData | {
16
+ style: Record<string, string>;
17
+ arrowStyle: Record<string, string>;
18
+ placement: Placement;
19
+ } | null>;
21
20
  triggerRef: import('vue').Ref<HTMLElement | null, HTMLElement | null>;
22
21
  tooltipRef: import('vue').Ref<HTMLElement | null, HTMLElement | null>;
23
- show: () => Promise<void>;
22
+ arrowRef: import('vue').Ref<HTMLElement | null, HTMLElement | null>;
23
+ tooltipId: string;
24
+ show: () => void;
24
25
  hide: () => void;
26
+ hideImmediate: () => void;
27
+ initPopper: () => void;
25
28
  };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @description commandPalette composition barrel export
3
+ * @author kine-design
4
+ * @date 2026/5/22
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ export { useCommandPalette } from './useCommandPalette';
10
+ export type { UseCommandPaletteReturn } from './useCommandPalette';
11
+ export type { Command } from './types';
@@ -0,0 +1,20 @@
1
+ import { VNode } from 'vue';
2
+ /**
3
+ * A single command registered in the palette.
4
+ */
5
+ export interface Command {
6
+ /** Unique identifier */
7
+ id: string;
8
+ /** Display label (also used for search) */
9
+ label: string;
10
+ /** Optional icon render function */
11
+ icon?: () => VNode;
12
+ /** Additional search terms beyond the label */
13
+ keywords?: string[];
14
+ /** Grouping category (e.g. "Navigation", "Actions") */
15
+ group?: string;
16
+ /** Shortcut hint text displayed on the right side of the item */
17
+ shortcut?: string;
18
+ /** Callback executed when the command is selected */
19
+ action: () => void;
20
+ }
@@ -0,0 +1,32 @@
1
+ import { Ref, ComputedRef } from 'vue';
2
+ import { Command } from './types';
3
+ export interface UseCommandPaletteReturn {
4
+ /** Whether the palette is open */
5
+ isOpen: Ref<boolean>;
6
+ /** Current search query */
7
+ query: Ref<string>;
8
+ /** Index of the currently highlighted item */
9
+ activeIndex: Ref<number>;
10
+ /** All registered commands */
11
+ commands: Ref<Command[]>;
12
+ /** Filtered commands based on query */
13
+ filtered: ComputedRef<Command[]>;
14
+ /** Open the palette */
15
+ open: () => void;
16
+ /** Close the palette */
17
+ close: () => void;
18
+ /** Toggle open/close */
19
+ toggle: () => void;
20
+ /** Register a command */
21
+ registerCommand: (cmd: Command) => void;
22
+ /** Unregister a command by id */
23
+ unregisterCommand: (id: string) => void;
24
+ /** Execute the command at a given index in the filtered list, then close */
25
+ execute: (index: number) => void;
26
+ }
27
+ /**
28
+ * CommandPalette composable
29
+ *
30
+ * Provides reactive state for opening, searching, and executing commands.
31
+ */
32
+ export declare function useCommandPalette(): UseCommandPaletteReturn;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @description context menu composables barrel export
3
+ * @author kine-design
4
+ * @date 2026/5/22
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ export { useContextMenu } from './useContextMenu';
10
+ export type { MenuItem } from './types';
@@ -0,0 +1,12 @@
1
+ import { VNode } from 'vue';
2
+ export interface MenuItem {
3
+ key: string;
4
+ label: string;
5
+ icon?: () => VNode;
6
+ action: () => void;
7
+ visible?: boolean;
8
+ disabled?: boolean;
9
+ danger?: boolean;
10
+ children?: MenuItem[];
11
+ separator?: boolean;
12
+ }