@yh-ui/hooks 0.1.10 → 0.1.15

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/dist/index.d.cts CHANGED
@@ -73,17 +73,14 @@ type UseZIndexReturn = ReturnType<typeof useZIndex>;
73
73
  declare const getDayjsLocale: (localeCode: string) => string;
74
74
  /**
75
75
  * 动态加载并设置 dayjs locale
76
- * 使用动态导入来按需加载,避免打包所有语言
77
76
  */
78
77
  declare const setDayjsLocale: (localeCode: string) => Promise<void>;
79
78
  /**
80
- * 同步设置 dayjs locale(不推荐,会阻塞)
81
- * 用于需要立即同步的场景
79
+ * 同步设置 dayjs locale(立即生效,异步加载后会更新)
82
80
  */
83
81
  declare const setDayjsLocaleSync: (localeCode: string) => void;
84
82
  /**
85
83
  * 使用自定义月份名称更新 dayjs locale
86
- * 这样可以确保 dayjs 使用我们语言包中定义的月份名称
87
84
  */
88
85
  declare const updateDayjsMonths: (localeCode: string, months: {
89
86
  jan: string;
@@ -149,7 +146,7 @@ interface FormContext {
149
146
  layout?: string;
150
147
  addField: (field: FormItemContext) => void;
151
148
  removeField: (field: FormItemContext) => void;
152
- themeOverrides?: Record<string, unknown>;
149
+ themeOverrides?: Record<string, string | undefined>;
153
150
  }
154
151
  interface FormItemContext {
155
152
  prop: string;
@@ -255,5 +252,448 @@ declare const useConfig: () => {
255
252
  globalLocale: ComputedRef<Language | undefined>;
256
253
  };
257
254
 
258
- export { FormContextKey, FormItemContextKey, configProviderContextKey, createZIndexCounter, defaultNamespace, getDayjsLocale, getNextZIndex, idInjectionKey, namespaceContextKey, resetZIndex, setDayjsLocale, setDayjsLocaleSync, updateDayjsMonths, useCache, useClickOutside, useConfig, useEventListener, useFormItem, useGlobalNamespace, useId, useIdInjection, useLocale, useNamespace, useScrollLock, useVirtualScroll, useZIndex, zIndexContextKey, zIndexCounterKey };
259
- export type { ConfigProviderContext, FormContext, FormItemContext, UseCacheReturn, UseFormItemReturn, UseIdReturn, UseLocaleReturn, UseNamespaceReturn, UseZIndexReturn, VirtualScrollOptions, VirtualScrollReturn };
255
+ type StreamChunkParser = (raw: string) => string | null;
256
+ /**
257
+ * OpenAI / DeepSeek 格式解析器
258
+ * data: {"choices":[{"delta":{"content":"hello"}}]}
259
+ */
260
+ declare const openaiParser: StreamChunkParser;
261
+ /**
262
+ * 文心一言 / ERNIE 格式解析器
263
+ * data: {"result":"hello","is_end":false}
264
+ */
265
+ declare const ernieParser: StreamChunkParser;
266
+ /**
267
+ * 通义千问 / Qwen 格式解析器
268
+ * data: {"output":{"text":"hello"},"finish_reason":null}
269
+ */
270
+ declare const qwenParser: StreamChunkParser;
271
+ /**
272
+ * Anthropic / Claude 格式解析器
273
+ * data: {"type":"content_block_delta","delta":{"type":"text_delta","text":"hello"}}
274
+ */
275
+ declare const claudeParser: StreamChunkParser;
276
+ /**
277
+ * Google / Gemini 格式解析器
278
+ * data: {"candidates":[{"content":{"parts":[{"text":"hello"}]}}]}
279
+ */
280
+ declare const geminiParser: StreamChunkParser;
281
+ /**
282
+ * 纯文本流解析器(AsyncGenerator 输出的原始字符串)
283
+ */
284
+ declare const plainTextParser: StreamChunkParser;
285
+ interface AiStreamOptions {
286
+ /**
287
+ * 请求适配器,返回 AsyncGenerator 或 fetch Response
288
+ */
289
+ request: (query: string, ...args: unknown[]) => Promise<Response | AsyncGenerator<string, void, unknown>> | AsyncGenerator<string, void, unknown>;
290
+ /**
291
+ * 流式块解析器,用于适配不同厂商的格式
292
+ * @default plainTextParser(直接输出原始字符串)
293
+ */
294
+ parser?: StreamChunkParser;
295
+ /**
296
+ * 是否启用打字机平滑节流效果
297
+ * @default true
298
+ */
299
+ typewriter?: boolean;
300
+ /**
301
+ * 每帧渲染的字符数(打字机速度控制)
302
+ * @default 3
303
+ */
304
+ charsPerFrame?: number;
305
+ onUpdate?: (chunk: string, fullContent: string) => void;
306
+ onFinish?: (content: string) => void;
307
+ onError?: (err: Error) => void;
308
+ }
309
+ /**
310
+ * useAiStream - 多厂商兼容流式请求引擎
311
+ *
312
+ * 特性:
313
+ * - 支持 OpenAI / DeepSeek / 文心一言 / 通义千问 等主流格式(Adapter 模式)
314
+ * - 内置 rAF 打字机节流,保证平滑输出体验
315
+ * - 完整的 AbortController 取消支持
316
+ */
317
+ declare function useAiStream(options: AiStreamOptions): {
318
+ isStreaming: vue.Ref<boolean, boolean>;
319
+ currentContent: vue.Ref<string, string>;
320
+ fetchStream: (query: string, ...args: unknown[]) => Promise<void>;
321
+ stop: () => void;
322
+ parsers: {
323
+ openaiParser: StreamChunkParser;
324
+ ernieParser: StreamChunkParser;
325
+ qwenParser: StreamChunkParser;
326
+ claudeParser: StreamChunkParser;
327
+ geminiParser: StreamChunkParser;
328
+ plainTextParser: StreamChunkParser;
329
+ };
330
+ };
331
+
332
+ interface AiChatMessage {
333
+ /** 唯一标识,避免使用 index 做 key */
334
+ id: string;
335
+ /** 消息发送方 */
336
+ role: 'user' | 'assistant' | 'system';
337
+ /** 消息内容 */
338
+ content: string;
339
+ /**
340
+ * 消息状态
341
+ * - loading: 初始加载中(占位)
342
+ * - generating: 流式生成中
343
+ * - success: 已成功完成
344
+ * - error: 发生错误
345
+ * - stopped: 被用户中途打断
346
+ */
347
+ status?: 'loading' | 'generating' | 'success' | 'error' | 'stopped';
348
+ /** 消息时间戳(ms) */
349
+ createAt: number;
350
+ /** 用于展示的时间字符串(可选,不传则自动格式化) */
351
+ time?: string;
352
+ /** 扩展字段 */
353
+ [key: string]: unknown;
354
+ }
355
+ interface UseAiChatOptions {
356
+ /** 初始化的消息列表 */
357
+ initialMessages?: AiChatMessage[];
358
+ /** 自定义生成 ID 的函数 */
359
+ idGenerator?: () => string;
360
+ /**
361
+ * 请求适配器
362
+ * 支持:
363
+ * - AsyncGenerator<string>:最原始的字符流,每次 yield 一段字符
364
+ * - Promise<string>:直接返回完整回复
365
+ * - Promise<Response>:SSE 流式响应
366
+ */
367
+ request?: (message: string, history: AiChatMessage[], abortSignal: AbortSignal) => AsyncGenerator<string, void, unknown> | Promise<string | Response>;
368
+ /**
369
+ * SSE / 流式块的解析器(适配不同厂商格式)
370
+ * 传入各厂商对应的 parser(来自 useAiStream 模块)
371
+ * @default plainTextParser
372
+ */
373
+ parser?: StreamChunkParser;
374
+ /**
375
+ * 是否启用打字机平滑输出效果
376
+ * @default true
377
+ */
378
+ typewriter?: boolean;
379
+ /**
380
+ * 打字机每帧输出字符数(越大越快)
381
+ * @default 3
382
+ */
383
+ charsPerFrame?: number;
384
+ /**
385
+ * 系统 Prompt(会自动插入到历史的首位)
386
+ */
387
+ systemPrompt?: string;
388
+ /** 出现错误时的回调 */
389
+ onError?: (err: Error) => void;
390
+ /** 消息发送完成回调 */
391
+ onFinish?: (message: AiChatMessage) => void;
392
+ }
393
+ /**
394
+ * useAiChat - 核心 AI 会话管理 Hook
395
+ *
396
+ * 特性:
397
+ * - 消息列表 CRUD + 状态机管理
398
+ * - 支持流式 / 非流式响应
399
+ * - 内置多厂商适配器接口(通过 parser 选项传入)
400
+ * - rAF 打字机平滑效果(可关闭)
401
+ * - 完整的 AbortController 取消支持
402
+ * - 系统 Prompt 自动注入
403
+ */
404
+ declare function useAiChat(options?: UseAiChatOptions): {
405
+ /** 会话历史 */
406
+ messages: vue.Ref<{
407
+ [x: string]: unknown;
408
+ id: string;
409
+ role: "user" | "assistant" | "system";
410
+ content: string;
411
+ status?: "loading" | "generating" | "success" | "error" | "stopped"
412
+ /** 消息时间戳(ms) */
413
+ | undefined;
414
+ createAt: number;
415
+ time?: string
416
+ /** 扩展字段 */
417
+ | undefined;
418
+ }[], AiChatMessage[] | {
419
+ [x: string]: unknown;
420
+ id: string;
421
+ role: "user" | "assistant" | "system";
422
+ content: string;
423
+ status?: "loading" | "generating" | "success" | "error" | "stopped"
424
+ /** 消息时间戳(ms) */
425
+ | undefined;
426
+ createAt: number;
427
+ time?: string
428
+ /** 扩展字段 */
429
+ | undefined;
430
+ }[]>;
431
+ /** 是否正在生成(等同 isSending,别名友好) */
432
+ isGenerating: vue.Ref<boolean, boolean>;
433
+ /** 同 isGenerating,语义别名 */
434
+ isSending: vue.ComputedRef<boolean>;
435
+ /** 触发发送(自动处理流、打字机) */
436
+ sendMessage: (content: string) => Promise<void>;
437
+ /** 停止/中断当前生成 */
438
+ stop: () => void;
439
+ /** 移除单条消息 */
440
+ removeMessage: (id: string) => void;
441
+ /** 修改单条消息内容 */
442
+ updateMessage: (id: string, patch: Partial<AiChatMessage>) => void;
443
+ /** 重置清空所有会话 */
444
+ clear: () => void;
445
+ };
446
+
447
+ interface AiConversation {
448
+ id: string;
449
+ title: string;
450
+ /**
451
+ * 最近更新时间(时间戳 ms)
452
+ */
453
+ updatedAt: number;
454
+ /**
455
+ * 对话预览/最后一条消息内容
456
+ */
457
+ excerpt?: string;
458
+ /**
459
+ * 是否置顶
460
+ */
461
+ pinned?: boolean;
462
+ /**
463
+ * 自定义数据(例如知识库 ID 等业务数据)
464
+ */
465
+ meta?: Record<string, unknown>;
466
+ }
467
+ interface StorageAdapter {
468
+ getItem(key: string): string | null | Promise<string | null>;
469
+ setItem(key: string, value: string): void | Promise<void>;
470
+ removeItem(key: string): void | Promise<void>;
471
+ }
472
+ /**
473
+ * localStorage 适配器(默认)
474
+ */
475
+ declare const localStorageAdapter: StorageAdapter;
476
+ /**
477
+ * IndexedDB 适配器(适合大量数据持久化)
478
+ */
479
+ declare class IndexedDBAdapter implements StorageAdapter {
480
+ private db;
481
+ private dbName;
482
+ private storeName;
483
+ private ready;
484
+ constructor(dbName?: string);
485
+ private init;
486
+ getItem(key: string): Promise<string | null>;
487
+ setItem(key: string, value: string): Promise<void>;
488
+ removeItem(key: string): Promise<void>;
489
+ }
490
+ type ConversationGroup = {
491
+ label: string;
492
+ items: AiConversation[];
493
+ };
494
+ interface UseAiConversationsOptions {
495
+ /** 初始化数据(或从后端的直出) */
496
+ initialConversations?: AiConversation[];
497
+ /** 自定义生成 ID 的函数 */
498
+ idGenerator?: () => string;
499
+ /**
500
+ * 持久化存储适配器
501
+ * - false: 不持久化(仅内存)
502
+ * - 'localStorage': 使用 localStorage(默认)
503
+ * - 'indexedDB': 使用 IndexedDB
504
+ * - StorageAdapter: 自定义适配器
505
+ */
506
+ storage?: false | 'localStorage' | 'indexedDB' | StorageAdapter;
507
+ /** 持久化 key 前缀 */
508
+ storageKey?: string;
509
+ /**
510
+ * 每次加载的数量(用于分页 / 懒加载)
511
+ * @default 20
512
+ */
513
+ pageSize?: number;
514
+ }
515
+ /**
516
+ * useAiConversations - 会话历史管理 Hook
517
+ *
518
+ * 特性:
519
+ * - 完整的 CRUD + 置顶操作
520
+ * - 可插拔持久化(localStorage / IndexedDB / 自定义)
521
+ * - 按时间自动分组(今天 / 最近 7 天 / 最近 30 天 / 更早)
522
+ * - 分页懒加载
523
+ */
524
+ declare function useAiConversations(options?: UseAiConversationsOptions): {
525
+ /** 完整会话列表 */
526
+ conversations: vue.Ref<{
527
+ id: string;
528
+ title: string;
529
+ updatedAt: number;
530
+ excerpt?: string
531
+ /**
532
+ * 是否置顶
533
+ */
534
+ | undefined;
535
+ pinned?: boolean
536
+ /**
537
+ * 自定义数据(例如知识库 ID 等业务数据)
538
+ */
539
+ | undefined;
540
+ meta?: Record<string, unknown> | undefined;
541
+ }[], AiConversation[] | {
542
+ id: string;
543
+ title: string;
544
+ updatedAt: number;
545
+ excerpt?: string
546
+ /**
547
+ * 是否置顶
548
+ */
549
+ | undefined;
550
+ pinned?: boolean
551
+ /**
552
+ * 自定义数据(例如知识库 ID 等业务数据)
553
+ */
554
+ | undefined;
555
+ meta?: Record<string, unknown> | undefined;
556
+ }[]>;
557
+ /** 按时间分组后的列表(置顶 / 今天 / 最近 7 天 / 更早) */
558
+ groupedConversations: vue.ComputedRef<ConversationGroup[]>;
559
+ /** 分页后的列表 */
560
+ pagedConversations: vue.ComputedRef<{
561
+ id: string;
562
+ title: string;
563
+ updatedAt: number;
564
+ excerpt?: string
565
+ /**
566
+ * 是否置顶
567
+ */
568
+ | undefined;
569
+ pinned?: boolean
570
+ /**
571
+ * 自定义数据(例如知识库 ID 等业务数据)
572
+ */
573
+ | undefined;
574
+ meta?: Record<string, unknown> | undefined;
575
+ }[]>;
576
+ /** 是否还有更多数据 */
577
+ hasMore: vue.ComputedRef<boolean>;
578
+ /** 加载更多 */
579
+ loadMore: () => Promise<void>;
580
+ /** 加载更多状态 */
581
+ isLoadingMore: vue.Ref<boolean, boolean>;
582
+ /** 等待初始化完成(SSR 场景使用) */
583
+ ready: Promise<void>;
584
+ /** 新建会话 */
585
+ createConversation: (title: string, meta?: Record<string, unknown>) => Promise<AiConversation>;
586
+ /** 删除会话 */
587
+ removeConversation: (id: string) => Promise<void>;
588
+ /** 更新会话属性 */
589
+ updateConversation: (id: string, updates: Partial<Omit<AiConversation, "id">>) => Promise<void>;
590
+ /** 置顶/取消置顶 */
591
+ pinConversation: (id: string, pinned?: boolean) => Promise<void>;
592
+ /** 清空全部 */
593
+ clear: () => Promise<void>;
594
+ };
595
+
596
+ interface AiRequestOptions {
597
+ /**
598
+ * 请求函数
599
+ */
600
+ request: (query: string, ...args: unknown[]) => Promise<string> | string;
601
+ onSuccess?: (content: string) => void;
602
+ onError?: (err: Error) => void;
603
+ }
604
+ /**
605
+ * useAiRequest - 简单的 AI 非流式请求 Hook
606
+ */
607
+ declare function useAiRequest(options: AiRequestOptions): {
608
+ loading: vue.Ref<boolean, boolean>;
609
+ data: vue.Ref<string, string>;
610
+ error: vue.Ref<Error | null, Error | null>;
611
+ send: (query: string, ...args: unknown[]) => Promise<string>;
612
+ };
613
+
614
+ interface UseAiVoiceOptions {
615
+ /**
616
+ * 语言代码 (用于 SpeechRecognition)
617
+ * @default 'zh-CN'
618
+ */
619
+ language?: string;
620
+ /**
621
+ * 是否需要临时结果(在说话过程中实时返回)
622
+ * @default true
623
+ */
624
+ interimResults?: boolean;
625
+ /**
626
+ * 是否连续识别
627
+ * @default false
628
+ */
629
+ continuous?: boolean;
630
+ /**
631
+ * 智能静音检测(VAD)
632
+ * 开启后,当检测到长时间无声会自动停止录音
633
+ * @default true
634
+ */
635
+ vad?: boolean;
636
+ /**
637
+ * 静音检测阈值 (ms)
638
+ * @default 2000
639
+ */
640
+ vadThreshold?: number;
641
+ /**
642
+ * 音量变化敏感度 (0-1)
643
+ * @default 0.05
644
+ */
645
+ volumeThreshold?: number;
646
+ /**
647
+ * 返回波形柱的数量(对应 AiVoiceTrigger 的 amplitudes)
648
+ * @default 20
649
+ */
650
+ waveCount?: number;
651
+ /**
652
+ * 是否在开始时自动执行浏览器语音识别 (SpeechRecognition)
653
+ * 如果关闭,则只进行物理音频录制
654
+ * @default true
655
+ */
656
+ useSTT?: boolean;
657
+ /** 回调事件 */
658
+ onStart?: () => void;
659
+ /** 停止回调,包含最终转写文本和录音文件 Blob */
660
+ onStop?: (transcript: string, blob: Blob | null) => void;
661
+ onResult?: (transcript: string) => void;
662
+ onPartialResult?: (transcript: string) => void;
663
+ onError?: (error: unknown) => void;
664
+ }
665
+ interface UseAiVoiceReturn {
666
+ /** 是否正在录音 */
667
+ isRecording: vue.Ref<boolean>;
668
+ /** 最终转写文本 */
669
+ transcript: vue.Ref<string>;
670
+ /** 过程中的临时文本 */
671
+ interimTranscript: vue.Ref<string>;
672
+ /** 实时波形数据 */
673
+ amplitudes: vue.Ref<number[]>;
674
+ /** 实时音量 (0-100) */
675
+ volume: vue.Ref<number>;
676
+ /** 录音文件的 Blob */
677
+ audioBlob: vue.Ref<Blob | null>;
678
+ /** 开始录音 */
679
+ start: () => Promise<void>;
680
+ /** 停止录音 */
681
+ stop: () => void;
682
+ /** 取消并放弃当前结果 */
683
+ cancel: () => void;
684
+ /** 浏览器是否支持 SpeechRecognition (用于显示警告) */
685
+ sttSupported: boolean;
686
+ }
687
+ /**
688
+ * useAiVoice - 专业级 AI 语音交互 Hook
689
+ *
690
+ * 核心能力:
691
+ * 1. 【音频录制】:通过 MediaRecorder 真实录制音频并导出 Blob 文件。
692
+ * 2. 【视觉分析】:通过 Web Audio API 实时输出驱动 AiVoiceTrigger 的波形数组。
693
+ * 3. 【智能 VAD】:多维检测静音状态,支持自动停顿结束。
694
+ * 4. 【语音转写】:内置 Web Speech API 实时转写及临时结果反馈。
695
+ */
696
+ declare function useAiVoice(options?: UseAiVoiceOptions): UseAiVoiceReturn;
697
+
698
+ export { FormContextKey, FormItemContextKey, IndexedDBAdapter, claudeParser, configProviderContextKey, createZIndexCounter, defaultNamespace, ernieParser, geminiParser, getDayjsLocale, getNextZIndex, idInjectionKey, localStorageAdapter, namespaceContextKey, openaiParser, plainTextParser, qwenParser, resetZIndex, setDayjsLocale, setDayjsLocaleSync, updateDayjsMonths, useAiChat, useAiConversations, useAiRequest, useAiStream, useAiVoice, useCache, useClickOutside, useConfig, useEventListener, useFormItem, useGlobalNamespace, useId, useIdInjection, useLocale, useNamespace, useScrollLock, useVirtualScroll, useZIndex, zIndexContextKey, zIndexCounterKey };
699
+ export type { AiChatMessage, AiConversation, AiRequestOptions, AiStreamOptions, ConfigProviderContext, ConversationGroup, FormContext, FormItemContext, StorageAdapter, StreamChunkParser, UseAiChatOptions, UseAiConversationsOptions, UseAiVoiceOptions, UseAiVoiceReturn, UseCacheReturn, UseFormItemReturn, UseIdReturn, UseLocaleReturn, UseNamespaceReturn, UseZIndexReturn, VirtualScrollOptions, VirtualScrollReturn };