@pmate/chat-sdk 0.1.21 → 0.2.1

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.
@@ -168,8 +168,8 @@ function DesktopInner(props) {
168
168
  display: "inline-flex",
169
169
  alignItems: "center",
170
170
  justifyContent: "center",
171
- width: 32,
172
- height: 32,
171
+ width: tokens.sizing.desktopSideAction,
172
+ height: tokens.sizing.desktopSideAction,
173
173
  border: 0,
174
174
  borderRadius: 999,
175
175
  background: "transparent",
@@ -207,7 +207,7 @@ function DesktopInner(props) {
207
207
  backdropFilter: "blur(18px)",
208
208
  zIndex: 3,
209
209
  }, children: [_jsxs("button", { type: "button", style: attachmentActionStyle, disabled: props.disabled || isUploadingImage, onClick: () => imageInputRef.current?.click(), children: [_jsx("span", { style: attachmentIconStyle, children: _jsx(ImageOutlineIcon, { size: 24 }) }), "\u56FE\u7247"] }), _jsxs("button", { type: "button", style: attachmentActionStyle, disabled: props.disabled, onClick: () => fileInputRef.current?.click(), children: [_jsx("span", { style: attachmentIconStyle, children: _jsx(FileOutlineIcon, { size: 24 }) }), "\u6587\u4EF6"] })] })) : null, voice.status === "recording" ? (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(WaveformFeedback, { levels: voice.waveformLevels, tokens: tokens, compact: true }), _jsxs("span", { style: { color: tokens.color.textSecondary, fontSize: 12 }, children: [Math.max(1, Math.round(voice.durationMs / 1000)), "s"] })] })) : null, props.multiline ? (_jsxs(InputSurface, { tokens: tokens, status: state.status, styleOverride: {
210
- minHeight: 144,
210
+ minHeight: tokens.sizing.multilineMinHeight,
211
211
  padding: "16px 18px 12px",
212
212
  display: "flex",
213
213
  flexDirection: "column",
@@ -227,12 +227,12 @@ function DesktopInner(props) {
227
227
  props.onChange?.(event.target.value);
228
228
  }, style: {
229
229
  flex: 1,
230
- minHeight: 64,
231
- maxHeight: 180,
230
+ minHeight: tokens.sizing.multilineTextMinHeight,
231
+ maxHeight: tokens.sizing.multilineTextMaxHeight,
232
232
  padding: 0,
233
233
  overflowY: "auto",
234
234
  caretColor: tokens.color.brandPrimary,
235
- } }), _jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }, children: [_jsx(MoreTrigger, { "data-icon-size": 24, onClick: () => { setIsEmojiOpen(false); setIsMoreOpen((prev) => !prev); }, disabled: props.disabled, style: { ...sideActionStyle, color: isMoreOpen ? tokens.color.iconActive : tokens.color.iconDefault, opacity: props.disabled ? 0.4 : 1 } }), _jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [_jsx("button", { type: "button", onClick: () => {
235
+ } }), _jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }, children: [_jsx(MoreTrigger, { "data-icon-size": tokens.sizing.desktopIcon, onClick: () => { setIsEmojiOpen(false); setIsMoreOpen((prev) => !prev); }, disabled: props.disabled, style: { ...sideActionStyle, color: isMoreOpen ? tokens.color.iconActive : tokens.color.iconDefault, opacity: props.disabled ? 0.4 : 1 } }), _jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [_jsx("button", { type: "button", onClick: () => {
236
236
  if (props.disabled || props.enableVoice === false)
237
237
  return;
238
238
  if (voice.status === "recording") {
@@ -248,7 +248,7 @@ function DesktopInner(props) {
248
248
  color: voice.status === "recording" || voice.status === "transcribing" ? tokens.color.iconActive : tokens.color.iconDefault,
249
249
  cursor: props.disabled || props.enableVoice === false ? "not-allowed" : "pointer",
250
250
  opacity: props.disabled || props.enableVoice === false ? 0.4 : 1,
251
- }, "aria-label": voice.status === "recording" ? "停止录音并转文字" : voice.status === "transcribing" ? "正在转写语音" : "开始语音输入", children: voice.status === "recording" ? _jsx(StopCircleOutlineIcon, { size: 27 }) : _jsx(MicOutlineIcon, { size: 24, strokeWidth: 2 }) }), _jsx(EmojiTrigger, { ref: emojiAnchorRef, "data-icon-size": 24, style: sideActionStyle, onClick: () => { setIsMoreOpen(false); setIsEmojiOpen((prev) => !prev); } }), _jsx(SendAction, { tokens: tokens, disabled: props.disabled || state.status === "sending" || (!state.text.trim() && state.attachments.length === 0), "aria-label": "\u53D1\u9001\u6D88\u606F", onClick: () => void onSend(), style: { width: 40, height: 40, padding: 0, borderRadius: 999, fontSize: 24, lineHeight: "40px" }, children: "\u2191" })] })] })] })) : _jsxs("div", { style: {
251
+ }, "aria-label": voice.status === "recording" ? "停止录音并转文字" : voice.status === "transcribing" ? "正在转写语音" : "开始语音输入", children: voice.status === "recording" ? _jsx(StopCircleOutlineIcon, { size: tokens.sizing.desktopIcon }) : _jsx(MicOutlineIcon, { size: tokens.sizing.desktopIcon, strokeWidth: 2 }) }), _jsx(EmojiTrigger, { ref: emojiAnchorRef, "data-icon-size": tokens.sizing.desktopIcon, style: sideActionStyle, onClick: () => { setIsMoreOpen(false); setIsEmojiOpen((prev) => !prev); } }), _jsx(SendAction, { tokens: tokens, disabled: props.disabled || state.status === "sending" || (!state.text.trim() && state.attachments.length === 0), "aria-label": "\u53D1\u9001\u6D88\u606F", onClick: () => void onSend(), style: { width: tokens.sizing.desktopSendAction, height: tokens.sizing.desktopSendAction, padding: 0, borderRadius: 999, fontSize: tokens.sizing.desktopSendIcon, lineHeight: `${tokens.sizing.desktopSendAction}px` }, children: "\u2191" })] })] })] })) : _jsxs("div", { style: {
252
252
  display: "grid",
253
253
  gridTemplateColumns: "32px minmax(0, 1fr) 32px 32px",
254
254
  alignItems: "center",
package/lib/index.d.ts CHANGED
@@ -14,7 +14,7 @@ export * from "./hooks/useAttachmentState";
14
14
  export * from "./hooks/useChatInputState";
15
15
  export * from "./hooks/useTranscriptComposer";
16
16
  export * from "./hooks/useVoiceInputController";
17
- export * from "./services/agentSdkASRClient";
17
+ export * from "./services/functionASRClient";
18
18
  export * from "./services/authTokenResolver";
19
19
  export * from "./runtime/botAuth";
20
20
  export * from "./runtime/botKey";
package/lib/index.js CHANGED
@@ -14,7 +14,7 @@ export * from "./hooks/useAttachmentState";
14
14
  export * from "./hooks/useChatInputState";
15
15
  export * from "./hooks/useTranscriptComposer";
16
16
  export * from "./hooks/useVoiceInputController";
17
- export * from "./services/agentSdkASRClient";
17
+ export * from "./services/functionASRClient";
18
18
  export * from "./services/authTokenResolver";
19
19
  export * from "./runtime/botAuth";
20
20
  export * from "./runtime/botKey";
@@ -1,7 +1,7 @@
1
1
  import { AsyncStream } from "./AsyncStream";
2
- export type AgentSdkStreamLike = {
2
+ export type FunctionStreamLike = {
3
3
  stream<TFinal = unknown, TChunk = Blob>(request: {
4
- agentId: string;
4
+ functionId: string;
5
5
  stream: AsyncStream<TChunk>;
6
6
  params?: Record<string, unknown>;
7
7
  minChunkSizeBytes?: number;
@@ -9,17 +9,17 @@ export type AgentSdkStreamLike = {
9
9
  finish(): Promise<TFinal | null>;
10
10
  };
11
11
  };
12
- export type AgentSdkASRClientOptions = {
13
- client: AgentSdkStreamLike;
14
- agentId: string;
12
+ export type FunctionASRClientOptions = {
13
+ client: FunctionStreamLike;
14
+ functionId: string;
15
15
  tokenResolver?: {
16
16
  getToken(): string;
17
17
  };
18
18
  minChunkSizeBytes?: number;
19
19
  };
20
- export declare class AgentSdkASRClient {
20
+ export declare class FunctionASRClient {
21
21
  private readonly options;
22
- constructor(options: AgentSdkASRClientOptions);
22
+ constructor(options: FunctionASRClientOptions);
23
23
  transcribe(blob: Blob, input?: {
24
24
  lang?: string;
25
25
  }): Promise<{
@@ -1,5 +1,5 @@
1
1
  import { AsyncStream } from "./AsyncStream";
2
- export class AgentSdkASRClient {
2
+ export class FunctionASRClient {
3
3
  options;
4
4
  constructor(options) {
5
5
  this.options = options;
@@ -8,7 +8,7 @@ export class AgentSdkASRClient {
8
8
  const token = this.options.tokenResolver?.getToken() ?? "";
9
9
  const stream = new AsyncStream();
10
10
  const task = this.options.client.stream({
11
- agentId: this.options.agentId,
11
+ functionId: this.options.functionId,
12
12
  stream,
13
13
  params: {
14
14
  lang: input?.lang ?? "auto",
@@ -1,4 +1,4 @@
1
- export type ChatInputThemeName = "pmate" | "dark" | "cli" | "compact";
1
+ export type ChatInputThemeName = "pmate" | "dark" | "issues" | "cli" | "compact";
2
2
  export declare const chatInputThemes: {
3
3
  pmate: {};
4
4
  dark: {
@@ -29,6 +29,77 @@ export declare const chatInputThemes: {
29
29
  recordingActive: string;
30
30
  };
31
31
  };
32
+ issues: {
33
+ color: {
34
+ brandPrimary: string;
35
+ surfaceInputDefault: string;
36
+ surfaceInputFocus: string;
37
+ surfaceInputRecording: string;
38
+ surfaceInputError: string;
39
+ textPrimary: string;
40
+ textSecondary: string;
41
+ iconDefault: string;
42
+ iconActive: string;
43
+ actionSendDefault: string;
44
+ actionSendDisabled: string;
45
+ actionSendTextOnPrimary: string;
46
+ borderDefault: string;
47
+ borderFocus: string;
48
+ borderError: string;
49
+ statusError: string;
50
+ statusWarning: string;
51
+ statusSuccess: string;
52
+ voiceWaveformActive: string;
53
+ voiceWaveformBackground: string;
54
+ };
55
+ spacing: {
56
+ inputOuter: number;
57
+ inputInner: number;
58
+ actionGap: number;
59
+ desktopToolbarGap: number;
60
+ };
61
+ radius: {
62
+ inputSurface: number;
63
+ desktopInputSurface: number;
64
+ actionButton: number;
65
+ };
66
+ sizing: {
67
+ desktopIcon: number;
68
+ desktopSideAction: number;
69
+ desktopSendAction: number;
70
+ desktopSendIcon: number;
71
+ multilineMinHeight: number;
72
+ multilineTextMinHeight: number;
73
+ multilineTextMaxHeight: number;
74
+ };
75
+ shadow: {
76
+ inputElevated: string;
77
+ };
78
+ typography: {
79
+ familyBase: string;
80
+ familyMono: string;
81
+ inputText: {
82
+ size: number;
83
+ lineHeight: number;
84
+ weight: number;
85
+ };
86
+ statusHint: {
87
+ size: number;
88
+ lineHeight: number;
89
+ weight: number;
90
+ };
91
+ actionLabel: {
92
+ size: number;
93
+ lineHeight: number;
94
+ weight: number;
95
+ };
96
+ voiceHoldToTalk: {
97
+ size: number;
98
+ lineHeight: number;
99
+ weight: number;
100
+ };
101
+ };
102
+ };
32
103
  cli: {
33
104
  color: {
34
105
  brandPrimary: string;
@@ -28,6 +28,50 @@ export const chatInputThemes = {
28
28
  recordingActive: "0 0 0 4px rgba(139, 124, 246, 0.18)",
29
29
  },
30
30
  },
31
+ issues: {
32
+ color: {
33
+ brandPrimary: "#8B7CF6",
34
+ surfaceInputDefault: "#171C23",
35
+ surfaceInputFocus: "#1D242D",
36
+ surfaceInputRecording: "#202839",
37
+ surfaceInputError: "#2B191D",
38
+ textPrimary: "#D7DCE3",
39
+ textSecondary: "#77818D",
40
+ iconDefault: "#737E8A",
41
+ iconActive: "#A99FFF",
42
+ actionSendDefault: "#4A5060",
43
+ actionSendDisabled: "#282E38",
44
+ actionSendTextOnPrimary: "#F3F5F8",
45
+ borderDefault: "#303844",
46
+ borderFocus: "#665CBF",
47
+ borderError: "#A84D59",
48
+ statusError: "#FF7D87",
49
+ statusWarning: "#E5A85E",
50
+ statusSuccess: "#45D495",
51
+ voiceWaveformActive: "#A99FFF",
52
+ voiceWaveformBackground: "#3B365F",
53
+ },
54
+ spacing: { inputOuter: 8, inputInner: 9, actionGap: 6, desktopToolbarGap: 6 },
55
+ radius: { inputSurface: 12, desktopInputSurface: 12, actionButton: 999 },
56
+ sizing: {
57
+ desktopIcon: 18,
58
+ desktopSideAction: 28,
59
+ desktopSendAction: 32,
60
+ desktopSendIcon: 18,
61
+ multilineMinHeight: 112,
62
+ multilineTextMinHeight: 46,
63
+ multilineTextMaxHeight: 144,
64
+ },
65
+ shadow: { inputElevated: "none" },
66
+ typography: {
67
+ familyBase: '"SF Mono", "JetBrains Mono", monospace',
68
+ familyMono: '"SF Mono", "JetBrains Mono", monospace',
69
+ inputText: { size: 13, lineHeight: 19, weight: 400 },
70
+ statusHint: { size: 10, lineHeight: 14, weight: 500 },
71
+ actionLabel: { size: 11, lineHeight: 15, weight: 600 },
72
+ voiceHoldToTalk: { size: 12, lineHeight: 17, weight: 600 },
73
+ },
74
+ },
31
75
  cli: {
32
76
  color: {
33
77
  brandPrimary: "#25D6A0",
@@ -49,6 +49,15 @@ export const defaultChatInputTokens = {
49
49
  inputElevated: "0 1px 2px rgba(15, 23, 42, 0.04)",
50
50
  recordingActive: "0 0 0 4px rgba(18, 184, 134, 0.14)",
51
51
  },
52
+ sizing: {
53
+ desktopIcon: 24,
54
+ desktopSideAction: 32,
55
+ desktopSendAction: 40,
56
+ desktopSendIcon: 24,
57
+ multilineMinHeight: 144,
58
+ multilineTextMinHeight: 64,
59
+ multilineTextMaxHeight: 180,
60
+ },
52
61
  typography: {
53
62
  familyBase: '"SF Pro Text", "PingFang SC", "Helvetica Neue", sans-serif',
54
63
  familyMono: '"SF Mono", "JetBrains Mono", monospace',
@@ -67,6 +76,7 @@ export function mergeChatInputTokens(overrides) {
67
76
  radius: { ...defaultChatInputTokens.radius, ...overrides?.radius },
68
77
  motion: { ...defaultChatInputTokens.motion, ...overrides?.motion },
69
78
  shadow: { ...defaultChatInputTokens.shadow, ...overrides?.shadow },
79
+ sizing: { ...defaultChatInputTokens.sizing, ...overrides?.sizing },
70
80
  typography: {
71
81
  ...defaultChatInputTokens.typography,
72
82
  ...overrides?.typography,
@@ -56,6 +56,15 @@ export type ChatInputShadowTokens = {
56
56
  inputElevated: string;
57
57
  recordingActive: string;
58
58
  };
59
+ export type ChatInputSizingTokens = {
60
+ desktopIcon: number;
61
+ desktopSideAction: number;
62
+ desktopSendAction: number;
63
+ desktopSendIcon: number;
64
+ multilineMinHeight: number;
65
+ multilineTextMinHeight: number;
66
+ multilineTextMaxHeight: number;
67
+ };
59
68
  export type ChatInputTypographyScale = {
60
69
  size: number;
61
70
  lineHeight: number;
@@ -75,6 +84,7 @@ export type ChatInputTokens = {
75
84
  radius: ChatInputRadiusTokens;
76
85
  motion: ChatInputMotionTokens;
77
86
  shadow: ChatInputShadowTokens;
87
+ sizing: ChatInputSizingTokens;
78
88
  typography: ChatInputTypographyTokens;
79
89
  };
80
90
  export type ChatInputSlotProps = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmate/chat-sdk",
3
- "version": "0.1.21",
3
+ "version": "0.2.1",
4
4
  "description": "Cross-platform PMate chat input components, voice input primitives, and ASR helpers",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -29,10 +29,18 @@
29
29
  "publishConfig": {
30
30
  "access": "public"
31
31
  },
32
+ "scripts": {
33
+ "clean": "rimraf lib",
34
+ "build": "pnpm run clean && tsc -p tsconfig.build.json && cp src/voice/recorder-processor.js lib/voice/recorder-processor.js",
35
+ "prepack": "pnpm run build",
36
+ "npm:publish": "pnpm run build && npm publish --access public --registry https://registry.npmjs.org/",
37
+ "test": "vitest run",
38
+ "storybook": "storybook dev -p 6009",
39
+ "build-storybook": "storybook build --output-dir dist"
40
+ },
32
41
  "dependencies": {
33
42
  "@emoji-mart/data": "^1.2.1",
34
43
  "@emoji-mart/react": "^1.1.1",
35
- "@pmate/agent-sdk": "^1.1.2",
36
44
  "@msgpack/msgpack": "^3.1.2",
37
45
  "jotai": "^2.16.1"
38
46
  },
@@ -43,21 +51,15 @@
43
51
  "devDependencies": {
44
52
  "@storybook/addon-docs": "^9.0.17",
45
53
  "@storybook/react-vite": "9.0.17",
46
- "@tailwindcss/vite": "^4.1.3",
47
- "@types/react": "^19.2.7",
48
- "@types/react-dom": "^19.2.3",
49
- "react": "^19.1.1",
50
- "react-dom": "^19.1.1",
54
+ "@tailwindcss/vite": "catalog:",
55
+ "@types/react": "catalog:",
56
+ "@types/react-dom": "catalog:",
57
+ "react": "catalog:",
58
+ "react-dom": "catalog:",
59
+ "rimraf": "^6.1.2",
51
60
  "storybook": "^9.0.17",
52
- "tailwindcss": "^4.0.14",
61
+ "tailwindcss": "catalog:",
53
62
  "typescript": "5.9.2",
54
63
  "vitest": "^4.0.17"
55
- },
56
- "scripts": {
57
- "build": "tsc -p tsconfig.build.json && cp src/voice/recorder-processor.js lib/voice/recorder-processor.js",
58
- "npm:publish": "pnpm run build && npm publish --access public --registry https://registry.npmjs.org/",
59
- "test": "vitest run",
60
- "storybook": "storybook dev -p 6009",
61
- "build-storybook": "storybook build --output-dir dist"
62
64
  }
63
- }
65
+ }
@@ -1,20 +0,0 @@
1
- class RecorderProcessor extends AudioWorkletProcessor {
2
- process(inputs) {
3
- const input = inputs[0]
4
- if (!input || !input[0]) {
5
- return true
6
- }
7
-
8
- const samples = input[0]
9
- const pcm = new Int16Array(samples.length)
10
- for (let i = 0; i < samples.length; i += 1) {
11
- const sample = Math.max(-1, Math.min(1, samples[i]))
12
- pcm[i] = sample < 0 ? sample * 0x8000 : sample * 0x7fff
13
- }
14
-
15
- this.port.postMessage({ type: "pcm", data: pcm.buffer }, [pcm.buffer])
16
- return true
17
- }
18
- }
19
-
20
- registerProcessor("pmate-mic-recorder-processor", RecorderProcessor)
@@ -1,15 +0,0 @@
1
- import type { StoryObj } from "@storybook/react-vite";
2
- declare const meta: {
3
- title: string;
4
- parameters: {
5
- layout: string;
6
- };
7
- };
8
- export default meta;
9
- type Story = StoryObj<typeof meta>;
10
- export declare const MobileIdle: Story;
11
- export declare const MobileVoiceReady: Story;
12
- export declare const MobileRecording: Story;
13
- export declare const DesktopTyping: Story;
14
- export declare const DesktopAttachment: Story;
15
- export declare const WaveformActive: Story;
@@ -1,38 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { ChatInputDesktop } from "../components/ChatInputDesktop";
3
- import { ChatInputMobile } from "../components/ChatInputMobile";
4
- import { WaveformFeedback } from "../components/WaveformFeedback";
5
- import { mergeChatInputTokens } from "../tokens/defaultTokens";
6
- const meta = {
7
- title: "@pmate/chat-sdk/ChatInput",
8
- parameters: {
9
- layout: "centered",
10
- },
11
- };
12
- export default meta;
13
- export const MobileIdle = {
14
- render: () => _jsx("div", { style: { width: 360 }, children: _jsx(ChatInputMobile, {}) }),
15
- };
16
- export const MobileVoiceReady = {
17
- render: () => (_jsx("div", { style: { width: 360 }, children: _jsx(ChatInputMobile, { initialState: { mode: "voice", status: "voice-mode-ready" } }) })),
18
- };
19
- export const MobileRecording = {
20
- render: () => (_jsx("div", { style: { width: 360 }, children: _jsx(ChatInputMobile, { initialState: {
21
- mode: "voice",
22
- status: "recording",
23
- waveformLevels: [0.25, 0.5, 0.88, 0.52, 0.28],
24
- } }) })),
25
- };
26
- export const DesktopTyping = {
27
- render: () => (_jsx("div", { style: { width: 720 }, children: _jsx(ChatInputDesktop, { initialState: { text: "Ship chat-sdk storybook first", status: "typing" } }) })),
28
- };
29
- export const DesktopAttachment = {
30
- render: () => (_jsx("div", { style: { width: 720 }, children: _jsx(ChatInputDesktop, { initialState: {
31
- text: "Desktop attachment state",
32
- status: "attachment-selected",
33
- attachments: [{ id: "1", kind: "image", name: "spec.png" }],
34
- } }) })),
35
- };
36
- export const WaveformActive = {
37
- render: () => (_jsx(WaveformFeedback, { levels: [0.2, 0.5, 0.9, 0.55, 0.3], tokens: mergeChatInputTokens() })),
38
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1,39 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { AgentSdkASRClient, } from "../services/agentSdkASRClient";
3
- describe("AgentSdkASRClient", () => {
4
- it("streams audio blobs through agent-sdk and returns final text", async () => {
5
- const seen = {
6
- blobSizes: [],
7
- };
8
- const client = new AgentSdkASRClient({
9
- client: {
10
- stream(request) {
11
- seen.agentId = request.agentId;
12
- seen.lang = String(request.params?.lang ?? "");
13
- seen.token = String(request.params?.token ?? "");
14
- return {
15
- async finish() {
16
- for await (const chunk of request.stream) {
17
- seen.blobSizes.push(chunk.size);
18
- }
19
- return "transcribed text";
20
- },
21
- };
22
- },
23
- },
24
- agentId: "agent:transcribe",
25
- tokenResolver: {
26
- getToken: () => "user-token",
27
- },
28
- minChunkSizeBytes: 0,
29
- });
30
- const result = await client.transcribe(new Blob(["voice-bytes"]), {
31
- lang: "en",
32
- });
33
- expect(result.text).toBe("transcribed text");
34
- expect(seen.agentId).toBe("agent:transcribe");
35
- expect(seen.lang).toBe("en");
36
- expect(seen.token).toBe("user-token");
37
- expect(seen.blobSizes).toEqual([11]);
38
- });
39
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,16 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { defaultChatInputTokens, mergeChatInputTokens } from "../tokens/defaultTokens";
3
- describe("chat-sdk tokens", () => {
4
- it("keeps purple brand as default", () => {
5
- expect(defaultChatInputTokens.color.brandPrimary).toBe("#6D4AFF");
6
- });
7
- it("merges nested token overrides", () => {
8
- const merged = mergeChatInputTokens({
9
- color: {
10
- brandPrimary: "#000000",
11
- },
12
- });
13
- expect(merged.color.brandPrimary).toBe("#000000");
14
- expect(merged.color.voiceWaveformActive).toBe(defaultChatInputTokens.color.voiceWaveformActive);
15
- });
16
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,10 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { insertTranscriptText } from "../hooks/useTranscriptComposer";
3
- describe("insertTranscriptText", () => {
4
- it("returns transcript when current text is empty", () => {
5
- expect(insertTranscriptText("", "hello")).toBe("hello");
6
- });
7
- it("appends transcript to existing text", () => {
8
- expect(insertTranscriptText("draft", "hello")).toBe("draft hello");
9
- });
10
- });