@lssm/module.ai-chat 1.41.1 → 1.42.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.
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/ai-chat.feature.d.ts +12 -0
- package/dist/ai-chat.feature.d.ts.map +1 -0
- package/dist/ai-chat.feature.js +95 -1
- package/dist/ai-chat.feature.js.map +1 -0
- package/dist/ai-chat.operations.d.ts +243 -0
- package/dist/ai-chat.operations.d.ts.map +1 -0
- package/dist/ai-chat.operations.js +174 -0
- package/dist/ai-chat.operations.js.map +1 -0
- package/dist/context/context-builder.d.ts +57 -0
- package/dist/context/context-builder.d.ts.map +1 -0
- package/dist/context/context-builder.js +148 -2
- package/dist/context/context-builder.js.map +1 -0
- package/dist/context/file-operations.d.ts +100 -0
- package/dist/context/file-operations.d.ts.map +1 -0
- package/dist/context/file-operations.js +175 -1
- package/dist/context/file-operations.js.map +1 -0
- package/dist/context/index.d.ts +4 -0
- package/dist/context/index.js +5 -1
- package/dist/context/workspace-context.d.ts +117 -0
- package/dist/context/workspace-context.d.ts.map +1 -0
- package/dist/context/workspace-context.js +124 -2
- package/dist/context/workspace-context.js.map +1 -0
- package/dist/core/chat-service.d.ts +73 -0
- package/dist/core/chat-service.d.ts.map +1 -0
- package/dist/core/chat-service.js +215 -2
- package/dist/core/chat-service.js.map +1 -0
- package/dist/core/conversation-store.d.ts +74 -0
- package/dist/core/conversation-store.d.ts.map +1 -0
- package/dist/core/conversation-store.js +109 -1
- package/dist/core/conversation-store.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +4 -1
- package/dist/core/message-types.d.ts +150 -0
- package/dist/core/message-types.d.ts.map +1 -0
- package/dist/events.d.ts +115 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +100 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +23 -1
- package/dist/libs/schema/dist/EnumType.js +2 -0
- package/dist/libs/schema/dist/FieldType.js +50 -0
- package/dist/libs/schema/dist/FieldType.js.map +1 -0
- package/dist/libs/schema/dist/ScalarTypeEnum.js +237 -0
- package/dist/libs/schema/dist/ScalarTypeEnum.js.map +1 -0
- package/dist/libs/schema/dist/SchemaModel.js +40 -0
- package/dist/libs/schema/dist/SchemaModel.js.map +1 -0
- package/dist/libs/schema/dist/entity/defineEntity.js +1 -0
- package/dist/libs/schema/dist/entity/index.js +2 -0
- package/dist/libs/schema/dist/entity/types.js +1 -0
- package/dist/libs/schema/dist/index.js +6 -0
- package/dist/presentation/components/ChatContainer.d.ts +21 -0
- package/dist/presentation/components/ChatContainer.d.ts.map +1 -0
- package/dist/presentation/components/ChatContainer.js +63 -1
- package/dist/presentation/components/ChatContainer.js.map +1 -0
- package/dist/presentation/components/ChatInput.d.ts +35 -0
- package/dist/presentation/components/ChatInput.d.ts.map +1 -0
- package/dist/presentation/components/ChatInput.js +149 -1
- package/dist/presentation/components/ChatInput.js.map +1 -0
- package/dist/presentation/components/ChatMessage.d.ts +24 -0
- package/dist/presentation/components/ChatMessage.d.ts.map +1 -0
- package/dist/presentation/components/ChatMessage.js +136 -1
- package/dist/presentation/components/ChatMessage.js.map +1 -0
- package/dist/presentation/components/CodePreview.d.ts +40 -0
- package/dist/presentation/components/CodePreview.d.ts.map +1 -0
- package/dist/presentation/components/CodePreview.js +127 -2
- package/dist/presentation/components/CodePreview.js.map +1 -0
- package/dist/presentation/components/ContextIndicator.d.ts +26 -0
- package/dist/presentation/components/ContextIndicator.d.ts.map +1 -0
- package/dist/presentation/components/ContextIndicator.js +97 -1
- package/dist/presentation/components/ContextIndicator.js.map +1 -0
- package/dist/presentation/components/ModelPicker.d.ts +39 -0
- package/dist/presentation/components/ModelPicker.d.ts.map +1 -0
- package/dist/presentation/components/ModelPicker.js +202 -1
- package/dist/presentation/components/ModelPicker.js.map +1 -0
- package/dist/presentation/components/index.d.ts +7 -0
- package/dist/presentation/components/index.js +8 -1
- package/dist/presentation/hooks/index.d.ts +3 -0
- package/dist/presentation/hooks/index.js +4 -1
- package/dist/presentation/hooks/useChat.d.ts +67 -0
- package/dist/presentation/hooks/useChat.d.ts.map +1 -0
- package/dist/presentation/hooks/useChat.js +172 -1
- package/dist/presentation/hooks/useChat.js.map +1 -0
- package/dist/presentation/hooks/useProviders.d.ts +38 -0
- package/dist/presentation/hooks/useProviders.d.ts.map +1 -0
- package/dist/presentation/hooks/useProviders.js +41 -1
- package/dist/presentation/hooks/useProviders.js.map +1 -0
- package/dist/presentation/index.d.ts +11 -0
- package/dist/presentation/index.js +12 -1
- package/dist/providers/chat-utilities.d.ts +15 -0
- package/dist/providers/chat-utilities.d.ts.map +1 -0
- package/dist/providers/chat-utilities.js +17 -1
- package/dist/providers/chat-utilities.js.map +1 -0
- package/dist/providers/index.d.ts +3 -0
- package/dist/providers/index.js +4 -1
- package/dist/schema.d.ts +222 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +102 -0
- package/dist/schema.js.map +1 -0
- package/package.json +29 -22
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { FieldType } from "./FieldType.js";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
import { Kind } from "graphql";
|
|
4
|
+
|
|
5
|
+
//#region ../../libs/schema/dist/ScalarTypeEnum.js
|
|
6
|
+
const localeRegex = /^[A-Za-z]{2}(?:-[A-Za-z0-9]{2,8})*$/;
|
|
7
|
+
const timezoneRegex = /^(?:UTC|[A-Za-z_]+\/[A-Za-z_]+)$/;
|
|
8
|
+
const phoneRegex = /^[+]?\d[\d\s().-]{3,}$/;
|
|
9
|
+
const currencyRegex = /^[A-Z]{3}$/;
|
|
10
|
+
const countryRegex = /^[A-Z]{2}$/;
|
|
11
|
+
const latMin = -90;
|
|
12
|
+
const latMax = 90;
|
|
13
|
+
const lonMin = -180;
|
|
14
|
+
const lonMax = 180;
|
|
15
|
+
/**
|
|
16
|
+
* Factory functions for common scalar FieldTypes with zod/GraphQL/JSON Schema.
|
|
17
|
+
*/
|
|
18
|
+
const ScalarTypeEnum = {
|
|
19
|
+
String_unsecure: () => new FieldType({
|
|
20
|
+
name: "String_unsecure",
|
|
21
|
+
description: "Unvalidated string scalar",
|
|
22
|
+
zod: z.string(),
|
|
23
|
+
parseValue: (v) => z.string().parse(v),
|
|
24
|
+
serialize: (v) => String(v),
|
|
25
|
+
parseLiteral: (ast) => {
|
|
26
|
+
if (ast.kind !== Kind.STRING) throw new TypeError("Invalid literal");
|
|
27
|
+
return ast.value;
|
|
28
|
+
},
|
|
29
|
+
jsonSchema: { type: "string" }
|
|
30
|
+
}),
|
|
31
|
+
Int_unsecure: () => new FieldType({
|
|
32
|
+
name: "Int_unsecure",
|
|
33
|
+
description: "Unvalidated integer scalar",
|
|
34
|
+
zod: z.number().int(),
|
|
35
|
+
parseValue: (v) => {
|
|
36
|
+
const num = typeof v === "number" ? v : Number(v);
|
|
37
|
+
return z.number().int().parse(num);
|
|
38
|
+
},
|
|
39
|
+
serialize: (v) => Math.trunc(typeof v === "number" ? v : Number(v)),
|
|
40
|
+
parseLiteral: (ast) => {
|
|
41
|
+
if (ast.kind !== Kind.INT) throw new TypeError("Invalid literal");
|
|
42
|
+
return Number(ast.value);
|
|
43
|
+
},
|
|
44
|
+
jsonSchema: { type: "integer" }
|
|
45
|
+
}),
|
|
46
|
+
Float_unsecure: () => new FieldType({
|
|
47
|
+
name: "Float_unsecure",
|
|
48
|
+
description: "Unvalidated float scalar",
|
|
49
|
+
zod: z.number(),
|
|
50
|
+
parseValue: (v) => {
|
|
51
|
+
const num = typeof v === "number" ? v : Number(v);
|
|
52
|
+
return z.number().parse(num);
|
|
53
|
+
},
|
|
54
|
+
serialize: (v) => Number(v),
|
|
55
|
+
parseLiteral: (ast) => {
|
|
56
|
+
if (ast.kind !== Kind.FLOAT && ast.kind !== Kind.INT) throw new TypeError("Invalid literal");
|
|
57
|
+
return Number(ast.value);
|
|
58
|
+
},
|
|
59
|
+
jsonSchema: { type: "number" }
|
|
60
|
+
}),
|
|
61
|
+
Boolean: () => new FieldType({
|
|
62
|
+
name: "Boolean",
|
|
63
|
+
description: "Unvalidated boolean scalar",
|
|
64
|
+
zod: z.boolean(),
|
|
65
|
+
parseValue: (v) => z.coerce.boolean().parse(v),
|
|
66
|
+
serialize: (v) => Boolean(v),
|
|
67
|
+
parseLiteral: (ast) => {
|
|
68
|
+
if (ast.kind !== Kind.BOOLEAN) throw new TypeError("Invalid literal");
|
|
69
|
+
return ast.value;
|
|
70
|
+
},
|
|
71
|
+
jsonSchema: { type: "boolean" }
|
|
72
|
+
}),
|
|
73
|
+
ID: () => new FieldType({
|
|
74
|
+
name: "ID",
|
|
75
|
+
description: "Unvalidated id scalar",
|
|
76
|
+
zod: z.string(),
|
|
77
|
+
parseValue: (v) => z.string().parse(v),
|
|
78
|
+
serialize: (v) => String(v),
|
|
79
|
+
parseLiteral: (ast) => {
|
|
80
|
+
if (ast.kind !== Kind.STRING) throw new TypeError("Invalid literal");
|
|
81
|
+
return ast.value;
|
|
82
|
+
},
|
|
83
|
+
jsonSchema: { type: "string" }
|
|
84
|
+
}),
|
|
85
|
+
JSON: () => new FieldType({
|
|
86
|
+
name: "JSON",
|
|
87
|
+
zod: z.any(),
|
|
88
|
+
parseValue: (v) => v,
|
|
89
|
+
serialize: (v) => v,
|
|
90
|
+
jsonSchema: {}
|
|
91
|
+
}),
|
|
92
|
+
JSONObject: () => new FieldType({
|
|
93
|
+
name: "JSONObject",
|
|
94
|
+
zod: z.record(z.string(), z.any()),
|
|
95
|
+
parseValue: (v) => z.record(z.string(), z.any()).parse(v),
|
|
96
|
+
serialize: (v) => v ?? {},
|
|
97
|
+
jsonSchema: { type: "object" }
|
|
98
|
+
}),
|
|
99
|
+
Date: () => new FieldType({
|
|
100
|
+
name: "Date",
|
|
101
|
+
zod: z.date(),
|
|
102
|
+
parseValue: (v) => v instanceof Date ? v : new Date(String(v)),
|
|
103
|
+
serialize: (v) => v instanceof Date ? v.toISOString().split("T")[0] : String(v),
|
|
104
|
+
jsonSchema: {
|
|
105
|
+
type: "string",
|
|
106
|
+
format: "date"
|
|
107
|
+
}
|
|
108
|
+
}),
|
|
109
|
+
DateTime: () => new FieldType({
|
|
110
|
+
name: "DateTime",
|
|
111
|
+
zod: z.date(),
|
|
112
|
+
parseValue: (v) => v instanceof Date ? v : new Date(String(v)),
|
|
113
|
+
serialize: (v) => {
|
|
114
|
+
return v instanceof Date ? v.toISOString() : String(v);
|
|
115
|
+
},
|
|
116
|
+
jsonSchema: {
|
|
117
|
+
type: "string",
|
|
118
|
+
format: "date-time"
|
|
119
|
+
}
|
|
120
|
+
}),
|
|
121
|
+
Time: () => new FieldType({
|
|
122
|
+
name: "Time",
|
|
123
|
+
zod: z.string().regex(/^\d{2}:\d{2}(:\d{2})?$/),
|
|
124
|
+
parseValue: (v) => z.string().regex(/^\d{2}:\d{2}(:\d{2})?$/).parse(v),
|
|
125
|
+
serialize: (v) => String(v),
|
|
126
|
+
jsonSchema: {
|
|
127
|
+
type: "string",
|
|
128
|
+
pattern: "^\\d{2}:\\d{2}(:\\d{2})?$"
|
|
129
|
+
}
|
|
130
|
+
}),
|
|
131
|
+
EmailAddress: () => new FieldType({
|
|
132
|
+
name: "EmailAddress",
|
|
133
|
+
zod: z.string().email(),
|
|
134
|
+
parseValue: (v) => z.string().email().parse(v),
|
|
135
|
+
serialize: (v) => String(v),
|
|
136
|
+
jsonSchema: {
|
|
137
|
+
type: "string",
|
|
138
|
+
format: "email"
|
|
139
|
+
}
|
|
140
|
+
}),
|
|
141
|
+
URL: () => new FieldType({
|
|
142
|
+
name: "URL",
|
|
143
|
+
zod: z.string().url(),
|
|
144
|
+
parseValue: (v) => z.string().url().parse(v),
|
|
145
|
+
serialize: (v) => String(v),
|
|
146
|
+
jsonSchema: {
|
|
147
|
+
type: "string",
|
|
148
|
+
format: "uri"
|
|
149
|
+
}
|
|
150
|
+
}),
|
|
151
|
+
PhoneNumber: () => new FieldType({
|
|
152
|
+
name: "PhoneNumber",
|
|
153
|
+
zod: z.string().regex(phoneRegex),
|
|
154
|
+
parseValue: (v) => z.string().regex(phoneRegex).parse(v),
|
|
155
|
+
serialize: (v) => String(v),
|
|
156
|
+
jsonSchema: {
|
|
157
|
+
type: "string",
|
|
158
|
+
pattern: phoneRegex.source
|
|
159
|
+
}
|
|
160
|
+
}),
|
|
161
|
+
NonEmptyString: () => new FieldType({
|
|
162
|
+
name: "NonEmptyString",
|
|
163
|
+
zod: z.string().min(1),
|
|
164
|
+
parseValue: (v) => z.string().min(1).parse(v),
|
|
165
|
+
serialize: (v) => String(v),
|
|
166
|
+
jsonSchema: {
|
|
167
|
+
type: "string",
|
|
168
|
+
minLength: 1
|
|
169
|
+
}
|
|
170
|
+
}),
|
|
171
|
+
Locale: () => new FieldType({
|
|
172
|
+
name: "Locale",
|
|
173
|
+
zod: z.string().regex(localeRegex),
|
|
174
|
+
parseValue: (v) => z.string().regex(localeRegex).parse(v),
|
|
175
|
+
serialize: (v) => String(v),
|
|
176
|
+
jsonSchema: {
|
|
177
|
+
type: "string",
|
|
178
|
+
pattern: localeRegex.source
|
|
179
|
+
}
|
|
180
|
+
}),
|
|
181
|
+
TimeZone: () => new FieldType({
|
|
182
|
+
name: "TimeZone",
|
|
183
|
+
zod: z.string().regex(timezoneRegex),
|
|
184
|
+
parseValue: (v) => z.string().regex(timezoneRegex).parse(v),
|
|
185
|
+
serialize: (v) => String(v),
|
|
186
|
+
jsonSchema: {
|
|
187
|
+
type: "string",
|
|
188
|
+
pattern: timezoneRegex.source
|
|
189
|
+
}
|
|
190
|
+
}),
|
|
191
|
+
Latitude: () => new FieldType({
|
|
192
|
+
name: "Latitude",
|
|
193
|
+
zod: z.number().min(latMin).max(latMax),
|
|
194
|
+
parseValue: (v) => z.coerce.number().min(latMin).max(latMax).parse(v),
|
|
195
|
+
serialize: (v) => Number(v),
|
|
196
|
+
jsonSchema: {
|
|
197
|
+
type: "number",
|
|
198
|
+
minimum: latMin,
|
|
199
|
+
maximum: latMax
|
|
200
|
+
}
|
|
201
|
+
}),
|
|
202
|
+
Longitude: () => new FieldType({
|
|
203
|
+
name: "Longitude",
|
|
204
|
+
zod: z.number().min(lonMin).max(lonMax),
|
|
205
|
+
parseValue: (v) => z.coerce.number().min(lonMin).max(lonMax).parse(v),
|
|
206
|
+
serialize: (v) => Number(v),
|
|
207
|
+
jsonSchema: {
|
|
208
|
+
type: "number",
|
|
209
|
+
minimum: lonMin,
|
|
210
|
+
maximum: lonMax
|
|
211
|
+
}
|
|
212
|
+
}),
|
|
213
|
+
Currency: () => new FieldType({
|
|
214
|
+
name: "Currency",
|
|
215
|
+
zod: z.string().regex(currencyRegex),
|
|
216
|
+
parseValue: (v) => z.string().regex(currencyRegex).parse(v),
|
|
217
|
+
serialize: (v) => String(v),
|
|
218
|
+
jsonSchema: {
|
|
219
|
+
type: "string",
|
|
220
|
+
pattern: currencyRegex.source
|
|
221
|
+
}
|
|
222
|
+
}),
|
|
223
|
+
CountryCode: () => new FieldType({
|
|
224
|
+
name: "CountryCode",
|
|
225
|
+
zod: z.string().regex(countryRegex),
|
|
226
|
+
parseValue: (v) => z.string().regex(countryRegex).parse(v),
|
|
227
|
+
serialize: (v) => String(v),
|
|
228
|
+
jsonSchema: {
|
|
229
|
+
type: "string",
|
|
230
|
+
pattern: countryRegex.source
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
//#endregion
|
|
236
|
+
export { ScalarTypeEnum };
|
|
237
|
+
//# sourceMappingURL=ScalarTypeEnum.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScalarTypeEnum.js","names":[],"sources":["../../../../../../libs/schema/dist/ScalarTypeEnum.js"],"sourcesContent":["import { FieldType } from \"./FieldType.js\";\nimport * as z from \"zod\";\nimport { Kind } from \"graphql\";\n\n//#region src/ScalarTypeEnum.ts\nconst localeRegex = /^[A-Za-z]{2}(?:-[A-Za-z0-9]{2,8})*$/;\nconst timezoneRegex = /^(?:UTC|[A-Za-z_]+\\/[A-Za-z_]+)$/;\nconst phoneRegex = /^[+]?\\d[\\d\\s().-]{3,}$/;\nconst currencyRegex = /^[A-Z]{3}$/;\nconst countryRegex = /^[A-Z]{2}$/;\nconst latMin = -90;\nconst latMax = 90;\nconst lonMin = -180;\nconst lonMax = 180;\n/**\n* Factory functions for common scalar FieldTypes with zod/GraphQL/JSON Schema.\n*/\nconst ScalarTypeEnum = {\n\tString_unsecure: () => new FieldType({\n\t\tname: \"String_unsecure\",\n\t\tdescription: \"Unvalidated string scalar\",\n\t\tzod: z.string(),\n\t\tparseValue: (v) => z.string().parse(v),\n\t\tserialize: (v) => String(v),\n\t\tparseLiteral: (ast) => {\n\t\t\tif (ast.kind !== Kind.STRING) throw new TypeError(\"Invalid literal\");\n\t\t\treturn ast.value;\n\t\t},\n\t\tjsonSchema: { type: \"string\" }\n\t}),\n\tInt_unsecure: () => new FieldType({\n\t\tname: \"Int_unsecure\",\n\t\tdescription: \"Unvalidated integer scalar\",\n\t\tzod: z.number().int(),\n\t\tparseValue: (v) => {\n\t\t\tconst num = typeof v === \"number\" ? v : Number(v);\n\t\t\treturn z.number().int().parse(num);\n\t\t},\n\t\tserialize: (v) => Math.trunc(typeof v === \"number\" ? v : Number(v)),\n\t\tparseLiteral: (ast) => {\n\t\t\tif (ast.kind !== Kind.INT) throw new TypeError(\"Invalid literal\");\n\t\t\treturn Number(ast.value);\n\t\t},\n\t\tjsonSchema: { type: \"integer\" }\n\t}),\n\tFloat_unsecure: () => new FieldType({\n\t\tname: \"Float_unsecure\",\n\t\tdescription: \"Unvalidated float scalar\",\n\t\tzod: z.number(),\n\t\tparseValue: (v) => {\n\t\t\tconst num = typeof v === \"number\" ? v : Number(v);\n\t\t\treturn z.number().parse(num);\n\t\t},\n\t\tserialize: (v) => Number(v),\n\t\tparseLiteral: (ast) => {\n\t\t\tif (ast.kind !== Kind.FLOAT && ast.kind !== Kind.INT) throw new TypeError(\"Invalid literal\");\n\t\t\treturn Number(ast.value);\n\t\t},\n\t\tjsonSchema: { type: \"number\" }\n\t}),\n\tBoolean: () => new FieldType({\n\t\tname: \"Boolean\",\n\t\tdescription: \"Unvalidated boolean scalar\",\n\t\tzod: z.boolean(),\n\t\tparseValue: (v) => z.coerce.boolean().parse(v),\n\t\tserialize: (v) => Boolean(v),\n\t\tparseLiteral: (ast) => {\n\t\t\tif (ast.kind !== Kind.BOOLEAN) throw new TypeError(\"Invalid literal\");\n\t\t\treturn ast.value;\n\t\t},\n\t\tjsonSchema: { type: \"boolean\" }\n\t}),\n\tID: () => new FieldType({\n\t\tname: \"ID\",\n\t\tdescription: \"Unvalidated id scalar\",\n\t\tzod: z.string(),\n\t\tparseValue: (v) => z.string().parse(v),\n\t\tserialize: (v) => String(v),\n\t\tparseLiteral: (ast) => {\n\t\t\tif (ast.kind !== Kind.STRING) throw new TypeError(\"Invalid literal\");\n\t\t\treturn ast.value;\n\t\t},\n\t\tjsonSchema: { type: \"string\" }\n\t}),\n\tJSON: () => new FieldType({\n\t\tname: \"JSON\",\n\t\tzod: z.any(),\n\t\tparseValue: (v) => v,\n\t\tserialize: (v) => v,\n\t\tjsonSchema: {}\n\t}),\n\tJSONObject: () => new FieldType({\n\t\tname: \"JSONObject\",\n\t\tzod: z.record(z.string(), z.any()),\n\t\tparseValue: (v) => z.record(z.string(), z.any()).parse(v),\n\t\tserialize: (v) => v ?? {},\n\t\tjsonSchema: { type: \"object\" }\n\t}),\n\tDate: () => new FieldType({\n\t\tname: \"Date\",\n\t\tzod: z.date(),\n\t\tparseValue: (v) => v instanceof Date ? v : new Date(String(v)),\n\t\tserialize: (v) => v instanceof Date ? v.toISOString().split(\"T\")[0] : String(v),\n\t\tjsonSchema: {\n\t\t\ttype: \"string\",\n\t\t\tformat: \"date\"\n\t\t}\n\t}),\n\tDateTime: () => new FieldType({\n\t\tname: \"DateTime\",\n\t\tzod: z.date(),\n\t\tparseValue: (v) => v instanceof Date ? v : new Date(String(v)),\n\t\tserialize: (v) => {\n\t\t\treturn v instanceof Date ? v.toISOString() : String(v);\n\t\t},\n\t\tjsonSchema: {\n\t\t\ttype: \"string\",\n\t\t\tformat: \"date-time\"\n\t\t}\n\t}),\n\tTime: () => new FieldType({\n\t\tname: \"Time\",\n\t\tzod: z.string().regex(/^\\d{2}:\\d{2}(:\\d{2})?$/),\n\t\tparseValue: (v) => z.string().regex(/^\\d{2}:\\d{2}(:\\d{2})?$/).parse(v),\n\t\tserialize: (v) => String(v),\n\t\tjsonSchema: {\n\t\t\ttype: \"string\",\n\t\t\tpattern: \"^\\\\d{2}:\\\\d{2}(:\\\\d{2})?$\"\n\t\t}\n\t}),\n\tEmailAddress: () => new FieldType({\n\t\tname: \"EmailAddress\",\n\t\tzod: z.string().email(),\n\t\tparseValue: (v) => z.string().email().parse(v),\n\t\tserialize: (v) => String(v),\n\t\tjsonSchema: {\n\t\t\ttype: \"string\",\n\t\t\tformat: \"email\"\n\t\t}\n\t}),\n\tURL: () => new FieldType({\n\t\tname: \"URL\",\n\t\tzod: z.string().url(),\n\t\tparseValue: (v) => z.string().url().parse(v),\n\t\tserialize: (v) => String(v),\n\t\tjsonSchema: {\n\t\t\ttype: \"string\",\n\t\t\tformat: \"uri\"\n\t\t}\n\t}),\n\tPhoneNumber: () => new FieldType({\n\t\tname: \"PhoneNumber\",\n\t\tzod: z.string().regex(phoneRegex),\n\t\tparseValue: (v) => z.string().regex(phoneRegex).parse(v),\n\t\tserialize: (v) => String(v),\n\t\tjsonSchema: {\n\t\t\ttype: \"string\",\n\t\t\tpattern: phoneRegex.source\n\t\t}\n\t}),\n\tNonEmptyString: () => new FieldType({\n\t\tname: \"NonEmptyString\",\n\t\tzod: z.string().min(1),\n\t\tparseValue: (v) => z.string().min(1).parse(v),\n\t\tserialize: (v) => String(v),\n\t\tjsonSchema: {\n\t\t\ttype: \"string\",\n\t\t\tminLength: 1\n\t\t}\n\t}),\n\tLocale: () => new FieldType({\n\t\tname: \"Locale\",\n\t\tzod: z.string().regex(localeRegex),\n\t\tparseValue: (v) => z.string().regex(localeRegex).parse(v),\n\t\tserialize: (v) => String(v),\n\t\tjsonSchema: {\n\t\t\ttype: \"string\",\n\t\t\tpattern: localeRegex.source\n\t\t}\n\t}),\n\tTimeZone: () => new FieldType({\n\t\tname: \"TimeZone\",\n\t\tzod: z.string().regex(timezoneRegex),\n\t\tparseValue: (v) => z.string().regex(timezoneRegex).parse(v),\n\t\tserialize: (v) => String(v),\n\t\tjsonSchema: {\n\t\t\ttype: \"string\",\n\t\t\tpattern: timezoneRegex.source\n\t\t}\n\t}),\n\tLatitude: () => new FieldType({\n\t\tname: \"Latitude\",\n\t\tzod: z.number().min(latMin).max(latMax),\n\t\tparseValue: (v) => z.coerce.number().min(latMin).max(latMax).parse(v),\n\t\tserialize: (v) => Number(v),\n\t\tjsonSchema: {\n\t\t\ttype: \"number\",\n\t\t\tminimum: latMin,\n\t\t\tmaximum: latMax\n\t\t}\n\t}),\n\tLongitude: () => new FieldType({\n\t\tname: \"Longitude\",\n\t\tzod: z.number().min(lonMin).max(lonMax),\n\t\tparseValue: (v) => z.coerce.number().min(lonMin).max(lonMax).parse(v),\n\t\tserialize: (v) => Number(v),\n\t\tjsonSchema: {\n\t\t\ttype: \"number\",\n\t\t\tminimum: lonMin,\n\t\t\tmaximum: lonMax\n\t\t}\n\t}),\n\tCurrency: () => new FieldType({\n\t\tname: \"Currency\",\n\t\tzod: z.string().regex(currencyRegex),\n\t\tparseValue: (v) => z.string().regex(currencyRegex).parse(v),\n\t\tserialize: (v) => String(v),\n\t\tjsonSchema: {\n\t\t\ttype: \"string\",\n\t\t\tpattern: currencyRegex.source\n\t\t}\n\t}),\n\tCountryCode: () => new FieldType({\n\t\tname: \"CountryCode\",\n\t\tzod: z.string().regex(countryRegex),\n\t\tparseValue: (v) => z.string().regex(countryRegex).parse(v),\n\t\tserialize: (v) => String(v),\n\t\tjsonSchema: {\n\t\t\ttype: \"string\",\n\t\t\tpattern: countryRegex.source\n\t\t}\n\t})\n};\n\n//#endregion\nexport { ScalarTypeEnum };\n//# sourceMappingURL=ScalarTypeEnum.js.map"],"mappings":";;;;;AAKA,MAAM,cAAc;AACpB,MAAM,gBAAgB;AACtB,MAAM,aAAa;AACnB,MAAM,gBAAgB;AACtB,MAAM,eAAe;AACrB,MAAM,SAAS;AACf,MAAM,SAAS;AACf,MAAM,SAAS;AACf,MAAM,SAAS;;;;AAIf,MAAM,iBAAiB;CACtB,uBAAuB,IAAI,UAAU;EACpC,MAAM;EACN,aAAa;EACb,KAAK,EAAE,QAAQ;EACf,aAAa,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE;EACtC,YAAY,MAAM,OAAO,EAAE;EAC3B,eAAe,QAAQ;AACtB,OAAI,IAAI,SAAS,KAAK,OAAQ,OAAM,IAAI,UAAU,kBAAkB;AACpE,UAAO,IAAI;;EAEZ,YAAY,EAAE,MAAM,UAAU;EAC9B,CAAC;CACF,oBAAoB,IAAI,UAAU;EACjC,MAAM;EACN,aAAa;EACb,KAAK,EAAE,QAAQ,CAAC,KAAK;EACrB,aAAa,MAAM;GAClB,MAAM,MAAM,OAAO,MAAM,WAAW,IAAI,OAAO,EAAE;AACjD,UAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,IAAI;;EAEnC,YAAY,MAAM,KAAK,MAAM,OAAO,MAAM,WAAW,IAAI,OAAO,EAAE,CAAC;EACnE,eAAe,QAAQ;AACtB,OAAI,IAAI,SAAS,KAAK,IAAK,OAAM,IAAI,UAAU,kBAAkB;AACjE,UAAO,OAAO,IAAI,MAAM;;EAEzB,YAAY,EAAE,MAAM,WAAW;EAC/B,CAAC;CACF,sBAAsB,IAAI,UAAU;EACnC,MAAM;EACN,aAAa;EACb,KAAK,EAAE,QAAQ;EACf,aAAa,MAAM;GAClB,MAAM,MAAM,OAAO,MAAM,WAAW,IAAI,OAAO,EAAE;AACjD,UAAO,EAAE,QAAQ,CAAC,MAAM,IAAI;;EAE7B,YAAY,MAAM,OAAO,EAAE;EAC3B,eAAe,QAAQ;AACtB,OAAI,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,IAAK,OAAM,IAAI,UAAU,kBAAkB;AAC5F,UAAO,OAAO,IAAI,MAAM;;EAEzB,YAAY,EAAE,MAAM,UAAU;EAC9B,CAAC;CACF,eAAe,IAAI,UAAU;EAC5B,MAAM;EACN,aAAa;EACb,KAAK,EAAE,SAAS;EAChB,aAAa,MAAM,EAAE,OAAO,SAAS,CAAC,MAAM,EAAE;EAC9C,YAAY,MAAM,QAAQ,EAAE;EAC5B,eAAe,QAAQ;AACtB,OAAI,IAAI,SAAS,KAAK,QAAS,OAAM,IAAI,UAAU,kBAAkB;AACrE,UAAO,IAAI;;EAEZ,YAAY,EAAE,MAAM,WAAW;EAC/B,CAAC;CACF,UAAU,IAAI,UAAU;EACvB,MAAM;EACN,aAAa;EACb,KAAK,EAAE,QAAQ;EACf,aAAa,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE;EACtC,YAAY,MAAM,OAAO,EAAE;EAC3B,eAAe,QAAQ;AACtB,OAAI,IAAI,SAAS,KAAK,OAAQ,OAAM,IAAI,UAAU,kBAAkB;AACpE,UAAO,IAAI;;EAEZ,YAAY,EAAE,MAAM,UAAU;EAC9B,CAAC;CACF,YAAY,IAAI,UAAU;EACzB,MAAM;EACN,KAAK,EAAE,KAAK;EACZ,aAAa,MAAM;EACnB,YAAY,MAAM;EAClB,YAAY,EAAE;EACd,CAAC;CACF,kBAAkB,IAAI,UAAU;EAC/B,MAAM;EACN,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC;EAClC,aAAa,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE;EACzD,YAAY,MAAM,KAAK,EAAE;EACzB,YAAY,EAAE,MAAM,UAAU;EAC9B,CAAC;CACF,YAAY,IAAI,UAAU;EACzB,MAAM;EACN,KAAK,EAAE,MAAM;EACb,aAAa,MAAM,aAAa,OAAO,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;EAC9D,YAAY,MAAM,aAAa,OAAO,EAAE,aAAa,CAAC,MAAM,IAAI,CAAC,KAAK,OAAO,EAAE;EAC/E,YAAY;GACX,MAAM;GACN,QAAQ;GACR;EACD,CAAC;CACF,gBAAgB,IAAI,UAAU;EAC7B,MAAM;EACN,KAAK,EAAE,MAAM;EACb,aAAa,MAAM,aAAa,OAAO,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;EAC9D,YAAY,MAAM;AACjB,UAAO,aAAa,OAAO,EAAE,aAAa,GAAG,OAAO,EAAE;;EAEvD,YAAY;GACX,MAAM;GACN,QAAQ;GACR;EACD,CAAC;CACF,YAAY,IAAI,UAAU;EACzB,MAAM;EACN,KAAK,EAAE,QAAQ,CAAC,MAAM,yBAAyB;EAC/C,aAAa,MAAM,EAAE,QAAQ,CAAC,MAAM,yBAAyB,CAAC,MAAM,EAAE;EACtE,YAAY,MAAM,OAAO,EAAE;EAC3B,YAAY;GACX,MAAM;GACN,SAAS;GACT;EACD,CAAC;CACF,oBAAoB,IAAI,UAAU;EACjC,MAAM;EACN,KAAK,EAAE,QAAQ,CAAC,OAAO;EACvB,aAAa,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE;EAC9C,YAAY,MAAM,OAAO,EAAE;EAC3B,YAAY;GACX,MAAM;GACN,QAAQ;GACR;EACD,CAAC;CACF,WAAW,IAAI,UAAU;EACxB,MAAM;EACN,KAAK,EAAE,QAAQ,CAAC,KAAK;EACrB,aAAa,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE;EAC5C,YAAY,MAAM,OAAO,EAAE;EAC3B,YAAY;GACX,MAAM;GACN,QAAQ;GACR;EACD,CAAC;CACF,mBAAmB,IAAI,UAAU;EAChC,MAAM;EACN,KAAK,EAAE,QAAQ,CAAC,MAAM,WAAW;EACjC,aAAa,MAAM,EAAE,QAAQ,CAAC,MAAM,WAAW,CAAC,MAAM,EAAE;EACxD,YAAY,MAAM,OAAO,EAAE;EAC3B,YAAY;GACX,MAAM;GACN,SAAS,WAAW;GACpB;EACD,CAAC;CACF,sBAAsB,IAAI,UAAU;EACnC,MAAM;EACN,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE;EACtB,aAAa,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE;EAC7C,YAAY,MAAM,OAAO,EAAE;EAC3B,YAAY;GACX,MAAM;GACN,WAAW;GACX;EACD,CAAC;CACF,cAAc,IAAI,UAAU;EAC3B,MAAM;EACN,KAAK,EAAE,QAAQ,CAAC,MAAM,YAAY;EAClC,aAAa,MAAM,EAAE,QAAQ,CAAC,MAAM,YAAY,CAAC,MAAM,EAAE;EACzD,YAAY,MAAM,OAAO,EAAE;EAC3B,YAAY;GACX,MAAM;GACN,SAAS,YAAY;GACrB;EACD,CAAC;CACF,gBAAgB,IAAI,UAAU;EAC7B,MAAM;EACN,KAAK,EAAE,QAAQ,CAAC,MAAM,cAAc;EACpC,aAAa,MAAM,EAAE,QAAQ,CAAC,MAAM,cAAc,CAAC,MAAM,EAAE;EAC3D,YAAY,MAAM,OAAO,EAAE;EAC3B,YAAY;GACX,MAAM;GACN,SAAS,cAAc;GACvB;EACD,CAAC;CACF,gBAAgB,IAAI,UAAU;EAC7B,MAAM;EACN,KAAK,EAAE,QAAQ,CAAC,IAAI,OAAO,CAAC,IAAI,OAAO;EACvC,aAAa,MAAM,EAAE,OAAO,QAAQ,CAAC,IAAI,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE;EACrE,YAAY,MAAM,OAAO,EAAE;EAC3B,YAAY;GACX,MAAM;GACN,SAAS;GACT,SAAS;GACT;EACD,CAAC;CACF,iBAAiB,IAAI,UAAU;EAC9B,MAAM;EACN,KAAK,EAAE,QAAQ,CAAC,IAAI,OAAO,CAAC,IAAI,OAAO;EACvC,aAAa,MAAM,EAAE,OAAO,QAAQ,CAAC,IAAI,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE;EACrE,YAAY,MAAM,OAAO,EAAE;EAC3B,YAAY;GACX,MAAM;GACN,SAAS;GACT,SAAS;GACT;EACD,CAAC;CACF,gBAAgB,IAAI,UAAU;EAC7B,MAAM;EACN,KAAK,EAAE,QAAQ,CAAC,MAAM,cAAc;EACpC,aAAa,MAAM,EAAE,QAAQ,CAAC,MAAM,cAAc,CAAC,MAAM,EAAE;EAC3D,YAAY,MAAM,OAAO,EAAE;EAC3B,YAAY;GACX,MAAM;GACN,SAAS,cAAc;GACvB;EACD,CAAC;CACF,mBAAmB,IAAI,UAAU;EAChC,MAAM;EACN,KAAK,EAAE,QAAQ,CAAC,MAAM,aAAa;EACnC,aAAa,MAAM,EAAE,QAAQ,CAAC,MAAM,aAAa,CAAC,MAAM,EAAE;EAC1D,YAAY,MAAM,OAAO,EAAE;EAC3B,YAAY;GACX,MAAM;GACN,SAAS,aAAa;GACtB;EACD,CAAC;CACF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import "./EnumType.js";
|
|
2
|
+
import "./FieldType.js";
|
|
3
|
+
import * as z from "zod";
|
|
4
|
+
|
|
5
|
+
//#region ../../libs/schema/dist/SchemaModel.js
|
|
6
|
+
/**
|
|
7
|
+
* Named object model built from FieldType/EnumType/SchemaModel fields.
|
|
8
|
+
* Provides zod and GraphQL input helpers, and supports arrays/optional fields.
|
|
9
|
+
*/
|
|
10
|
+
var SchemaModel = class {
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Build a typed ZodObject from the model fields, preserving each field's
|
|
16
|
+
* Zod schema and optionality at the type level when possible.
|
|
17
|
+
*/
|
|
18
|
+
getZod() {
|
|
19
|
+
const shape = Object.entries(this.config.fields).reduce((acc, [key, def]) => {
|
|
20
|
+
const base = def.type.getZod();
|
|
21
|
+
const withArray = def.isArray ? z.array(base) : base;
|
|
22
|
+
acc[key] = def.isOptional ? withArray.optional() : withArray;
|
|
23
|
+
return acc;
|
|
24
|
+
}, {});
|
|
25
|
+
return z.object(shape);
|
|
26
|
+
}
|
|
27
|
+
/** Input object name for GraphQL builder adapters. */
|
|
28
|
+
getPothosInput() {
|
|
29
|
+
return this.config.name;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Helper to define a SchemaModel with type inference.
|
|
34
|
+
* Equivalent to `new SchemaModel(config)` but with better ergonomics.
|
|
35
|
+
*/
|
|
36
|
+
const defineSchemaModel = (config) => new SchemaModel(config);
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
39
|
+
export { SchemaModel, defineSchemaModel };
|
|
40
|
+
//# sourceMappingURL=SchemaModel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SchemaModel.js","names":[],"sources":["../../../../../../libs/schema/dist/SchemaModel.js"],"sourcesContent":["import \"./EnumType.js\";\nimport \"./FieldType.js\";\nimport * as z from \"zod\";\n\n//#region src/SchemaModel.ts\n/**\n* Named object model built from FieldType/EnumType/SchemaModel fields.\n* Provides zod and GraphQL input helpers, and supports arrays/optional fields.\n*/\nvar SchemaModel = class {\n\tconstructor(config) {\n\t\tthis.config = config;\n\t}\n\t/**\n\t* Build a typed ZodObject from the model fields, preserving each field's\n\t* Zod schema and optionality at the type level when possible.\n\t*/\n\tgetZod() {\n\t\tconst shape = Object.entries(this.config.fields).reduce((acc, [key, def]) => {\n\t\t\tconst base = def.type.getZod();\n\t\t\tconst withArray = def.isArray ? z.array(base) : base;\n\t\t\tacc[key] = def.isOptional ? withArray.optional() : withArray;\n\t\t\treturn acc;\n\t\t}, {});\n\t\treturn z.object(shape);\n\t}\n\t/** Input object name for GraphQL builder adapters. */\n\tgetPothosInput() {\n\t\treturn this.config.name;\n\t}\n};\n/**\n* Helper to define a SchemaModel with type inference.\n* Equivalent to `new SchemaModel(config)` but with better ergonomics.\n*/\nconst defineSchemaModel = (config) => new SchemaModel(config);\n\n//#endregion\nexport { SchemaModel, defineSchemaModel };\n//# sourceMappingURL=SchemaModel.js.map"],"mappings":";;;;;;;;;AASA,IAAI,cAAc,MAAM;CACvB,YAAY,QAAQ;AACnB,OAAK,SAAS;;;;;;CAMf,SAAS;EACR,MAAM,QAAQ,OAAO,QAAQ,KAAK,OAAO,OAAO,CAAC,QAAQ,KAAK,CAAC,KAAK,SAAS;GAC5E,MAAM,OAAO,IAAI,KAAK,QAAQ;GAC9B,MAAM,YAAY,IAAI,UAAU,EAAE,MAAM,KAAK,GAAG;AAChD,OAAI,OAAO,IAAI,aAAa,UAAU,UAAU,GAAG;AACnD,UAAO;KACL,EAAE,CAAC;AACN,SAAO,EAAE,OAAO,MAAM;;;CAGvB,iBAAiB;AAChB,SAAO,KAAK,OAAO;;;;;;;AAOrB,MAAM,qBAAqB,WAAW,IAAI,YAAY,OAAO"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "zod";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "zod";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
3
|
+
|
|
4
|
+
//#region src/presentation/components/ChatContainer.d.ts
|
|
5
|
+
interface ChatContainerProps {
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
/** Show scroll-to-bottom button when scrolled up */
|
|
9
|
+
showScrollButton?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Container component for chat messages with scrolling
|
|
13
|
+
*/
|
|
14
|
+
declare function ChatContainer({
|
|
15
|
+
children,
|
|
16
|
+
className,
|
|
17
|
+
showScrollButton
|
|
18
|
+
}: ChatContainerProps): react_jsx_runtime0.JSX.Element;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { ChatContainer };
|
|
21
|
+
//# sourceMappingURL=ChatContainer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatContainer.d.ts","names":[],"sources":["../../../src/presentation/components/ChatContainer.tsx"],"sourcesContent":[],"mappings":";;;;UAMiB,kBAAA;YACL,KAAA,CAAM;;EADD;EAUD,gBAAa,CAAA,EAAA,OAAA;;;;;AAIR,iBAJL,aAAA,CAIK;EAAA,QAAA;EAAA,SAAA;EAAA;AAAA,CAAA,EAAlB,kBAAkB,CAAA,EAAA,kBAAA,CAAA,GAAA,CAAA,OAAA"}
|
|
@@ -1 +1,63 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { ScrollArea } from "@lssm/lib.ui-kit-web/ui/scroll-area";
|
|
5
|
+
import { cn } from "@lssm/lib.ui-kit-web/ui/utils";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
|
|
8
|
+
//#region src/presentation/components/ChatContainer.tsx
|
|
9
|
+
/**
|
|
10
|
+
* Container component for chat messages with scrolling
|
|
11
|
+
*/
|
|
12
|
+
function ChatContainer({ children, className, showScrollButton = true }) {
|
|
13
|
+
const scrollRef = React.useRef(null);
|
|
14
|
+
const [showScrollDown, setShowScrollDown] = React.useState(false);
|
|
15
|
+
React.useEffect(() => {
|
|
16
|
+
const container = scrollRef.current;
|
|
17
|
+
if (!container) return;
|
|
18
|
+
if (container.scrollHeight - container.scrollTop <= container.clientHeight + 100) container.scrollTop = container.scrollHeight;
|
|
19
|
+
}, [children]);
|
|
20
|
+
const handleScroll = React.useCallback((event) => {
|
|
21
|
+
const container = event.currentTarget;
|
|
22
|
+
setShowScrollDown(!(container.scrollHeight - container.scrollTop <= container.clientHeight + 100));
|
|
23
|
+
}, []);
|
|
24
|
+
const scrollToBottom = React.useCallback(() => {
|
|
25
|
+
const container = scrollRef.current;
|
|
26
|
+
if (container) container.scrollTo({
|
|
27
|
+
top: container.scrollHeight,
|
|
28
|
+
behavior: "smooth"
|
|
29
|
+
});
|
|
30
|
+
}, []);
|
|
31
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
32
|
+
className: cn("relative flex flex-1 flex-col", className),
|
|
33
|
+
children: [/* @__PURE__ */ jsx(ScrollArea, {
|
|
34
|
+
ref: scrollRef,
|
|
35
|
+
className: "flex-1",
|
|
36
|
+
onScroll: handleScroll,
|
|
37
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
38
|
+
className: "flex flex-col gap-4 p-4",
|
|
39
|
+
children
|
|
40
|
+
})
|
|
41
|
+
}), showScrollButton && showScrollDown && /* @__PURE__ */ jsxs("button", {
|
|
42
|
+
onClick: scrollToBottom,
|
|
43
|
+
className: cn("absolute bottom-4 left-1/2 -translate-x-1/2", "bg-primary text-primary-foreground", "rounded-full px-3 py-1.5 text-sm font-medium shadow-lg", "hover:bg-primary/90 transition-colors", "flex items-center gap-1.5"),
|
|
44
|
+
"aria-label": "Scroll to bottom",
|
|
45
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
46
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
47
|
+
width: "16",
|
|
48
|
+
height: "16",
|
|
49
|
+
viewBox: "0 0 24 24",
|
|
50
|
+
fill: "none",
|
|
51
|
+
stroke: "currentColor",
|
|
52
|
+
strokeWidth: "2",
|
|
53
|
+
strokeLinecap: "round",
|
|
54
|
+
strokeLinejoin: "round",
|
|
55
|
+
children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
|
|
56
|
+
}), "New messages"]
|
|
57
|
+
})]
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
export { ChatContainer };
|
|
63
|
+
//# sourceMappingURL=ChatContainer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatContainer.js","names":[],"sources":["../../../src/presentation/components/ChatContainer.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { ScrollArea } from '@lssm/lib.ui-kit-web/ui/scroll-area';\nimport { cn } from '@lssm/lib.ui-kit-web/ui/utils';\n\nexport interface ChatContainerProps {\n children: React.ReactNode;\n className?: string;\n /** Show scroll-to-bottom button when scrolled up */\n showScrollButton?: boolean;\n}\n\n/**\n * Container component for chat messages with scrolling\n */\nexport function ChatContainer({\n children,\n className,\n showScrollButton = true,\n}: ChatContainerProps) {\n const scrollRef = React.useRef<HTMLDivElement>(null);\n const [showScrollDown, setShowScrollDown] = React.useState(false);\n\n // Auto-scroll to bottom when children change\n React.useEffect(() => {\n const container = scrollRef.current;\n if (!container) return;\n\n // Check if user has scrolled up\n const isAtBottom =\n container.scrollHeight - container.scrollTop <=\n container.clientHeight + 100;\n\n if (isAtBottom) {\n container.scrollTop = container.scrollHeight;\n }\n }, [children]);\n\n // Track scroll position for scroll-to-bottom button\n const handleScroll = React.useCallback(\n (event: React.UIEvent<HTMLDivElement>) => {\n const container = event.currentTarget;\n const isAtBottom =\n container.scrollHeight - container.scrollTop <=\n container.clientHeight + 100;\n setShowScrollDown(!isAtBottom);\n },\n []\n );\n\n const scrollToBottom = React.useCallback(() => {\n const container = scrollRef.current;\n if (container) {\n container.scrollTo({\n top: container.scrollHeight,\n behavior: 'smooth',\n });\n }\n }, []);\n\n return (\n <div className={cn('relative flex flex-1 flex-col', className)}>\n <ScrollArea ref={scrollRef} className=\"flex-1\" onScroll={handleScroll}>\n <div className=\"flex flex-col gap-4 p-4\">{children}</div>\n </ScrollArea>\n\n {showScrollButton && showScrollDown && (\n <button\n onClick={scrollToBottom}\n className={cn(\n 'absolute bottom-4 left-1/2 -translate-x-1/2',\n 'bg-primary text-primary-foreground',\n 'rounded-full px-3 py-1.5 text-sm font-medium shadow-lg',\n 'hover:bg-primary/90 transition-colors',\n 'flex items-center gap-1.5'\n )}\n aria-label=\"Scroll to bottom\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n New messages\n </button>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;AAgBA,SAAgB,cAAc,EAC5B,UACA,WACA,mBAAmB,QACE;CACrB,MAAM,YAAY,MAAM,OAAuB,KAAK;CACpD,MAAM,CAAC,gBAAgB,qBAAqB,MAAM,SAAS,MAAM;AAGjE,OAAM,gBAAgB;EACpB,MAAM,YAAY,UAAU;AAC5B,MAAI,CAAC,UAAW;AAOhB,MAHE,UAAU,eAAe,UAAU,aACnC,UAAU,eAAe,IAGzB,WAAU,YAAY,UAAU;IAEjC,CAAC,SAAS,CAAC;CAGd,MAAM,eAAe,MAAM,aACxB,UAAyC;EACxC,MAAM,YAAY,MAAM;AAIxB,oBAAkB,EAFhB,UAAU,eAAe,UAAU,aACnC,UAAU,eAAe,KACG;IAEhC,EAAE,CACH;CAED,MAAM,iBAAiB,MAAM,kBAAkB;EAC7C,MAAM,YAAY,UAAU;AAC5B,MAAI,UACF,WAAU,SAAS;GACjB,KAAK,UAAU;GACf,UAAU;GACX,CAAC;IAEH,EAAE,CAAC;AAEN,QACE,qBAAC;EAAI,WAAW,GAAG,iCAAiC,UAAU;aAC5D,oBAAC;GAAW,KAAK;GAAW,WAAU;GAAS,UAAU;aACvD,oBAAC;IAAI,WAAU;IAA2B;KAAe;IAC9C,EAEZ,oBAAoB,kBACnB,qBAAC;GACC,SAAS;GACT,WAAW,GACT,+CACA,sCACA,0DACA,yCACA,4BACD;GACD,cAAW;cAEX,oBAAC;IACC,OAAM;IACN,OAAM;IACN,QAAO;IACP,SAAQ;IACR,MAAK;IACL,QAAO;IACP,aAAY;IACZ,eAAc;IACd,gBAAe;cAEf,oBAAC,UAAK,GAAE,iBAAiB;KACrB;IAEC;GAEP"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ChatAttachment } from "../../core/message-types.js";
|
|
2
|
+
import * as react_jsx_runtime2 from "react/jsx-runtime";
|
|
3
|
+
|
|
4
|
+
//#region src/presentation/components/ChatInput.d.ts
|
|
5
|
+
interface ChatInputProps {
|
|
6
|
+
/** Called when a message is sent */
|
|
7
|
+
onSend: (content: string, attachments?: ChatAttachment[]) => void;
|
|
8
|
+
/** Whether input is disabled (e.g., during streaming) */
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
/** Whether currently loading/streaming */
|
|
11
|
+
isLoading?: boolean;
|
|
12
|
+
/** Placeholder text */
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
/** Additional class name */
|
|
15
|
+
className?: string;
|
|
16
|
+
/** Show attachment button */
|
|
17
|
+
showAttachments?: boolean;
|
|
18
|
+
/** Max attachments allowed */
|
|
19
|
+
maxAttachments?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Chat input component with attachment support
|
|
23
|
+
*/
|
|
24
|
+
declare function ChatInput({
|
|
25
|
+
onSend,
|
|
26
|
+
disabled,
|
|
27
|
+
isLoading,
|
|
28
|
+
placeholder,
|
|
29
|
+
className,
|
|
30
|
+
showAttachments,
|
|
31
|
+
maxAttachments
|
|
32
|
+
}: ChatInputProps): react_jsx_runtime2.JSX.Element;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { ChatInput };
|
|
35
|
+
//# sourceMappingURL=ChatInput.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatInput.d.ts","names":[],"sources":["../../../src/presentation/components/ChatInput.tsx"],"sourcesContent":[],"mappings":";;;;UASiB,cAAA;;0CAEyB;EAFzB;EAoBD,QAAA,CAAA,EAAA,OAAS;EACvB;EACA,SAAA,CAAA,EAAA,OAAA;EACA;EACA,WAAA,CAAA,EAAA,MAAA;EACA;EACA,SAAA,CAAA,EAAA,MAAA;EACA;EACC,eAAA,CAAA,EAAA,OAAA;EAAc;EAAA,cAAA,CAAA,EAAA,MAAA;;;;;iBARD,SAAA;;;;;;;;GAQb,iBAAc,kBAAA,CAAA,GAAA,CAAA"}
|
|
@@ -1 +1,149 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "@lssm/lib.ui-kit-web/ui/utils";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { Code, FileText, Loader2, Paperclip, Send, X } from "lucide-react";
|
|
7
|
+
import { Button, Textarea } from "@lssm/lib.design-system";
|
|
8
|
+
|
|
9
|
+
//#region src/presentation/components/ChatInput.tsx
|
|
10
|
+
/**
|
|
11
|
+
* Chat input component with attachment support
|
|
12
|
+
*/
|
|
13
|
+
function ChatInput({ onSend, disabled = false, isLoading = false, placeholder = "Type a message...", className, showAttachments = true, maxAttachments = 5 }) {
|
|
14
|
+
const [content, setContent] = React.useState("");
|
|
15
|
+
const [attachments, setAttachments] = React.useState([]);
|
|
16
|
+
const textareaRef = React.useRef(null);
|
|
17
|
+
const fileInputRef = React.useRef(null);
|
|
18
|
+
const canSend = content.trim().length > 0 || attachments.length > 0;
|
|
19
|
+
const handleSubmit = React.useCallback((e) => {
|
|
20
|
+
e?.preventDefault();
|
|
21
|
+
if (!canSend || disabled || isLoading) return;
|
|
22
|
+
onSend(content.trim(), attachments.length > 0 ? attachments : void 0);
|
|
23
|
+
setContent("");
|
|
24
|
+
setAttachments([]);
|
|
25
|
+
textareaRef.current?.focus();
|
|
26
|
+
}, [
|
|
27
|
+
canSend,
|
|
28
|
+
content,
|
|
29
|
+
attachments,
|
|
30
|
+
disabled,
|
|
31
|
+
isLoading,
|
|
32
|
+
onSend
|
|
33
|
+
]);
|
|
34
|
+
const handleKeyDown = React.useCallback((e) => {
|
|
35
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
handleSubmit();
|
|
38
|
+
}
|
|
39
|
+
}, [handleSubmit]);
|
|
40
|
+
const handleFileSelect = React.useCallback(async (e) => {
|
|
41
|
+
const files = e.target.files;
|
|
42
|
+
if (!files) return;
|
|
43
|
+
const newAttachments = [];
|
|
44
|
+
for (const file of Array.from(files)) {
|
|
45
|
+
if (attachments.length + newAttachments.length >= maxAttachments) break;
|
|
46
|
+
const content$1 = await file.text();
|
|
47
|
+
const extension = file.name.split(".").pop()?.toLowerCase() ?? "";
|
|
48
|
+
const isCode = [
|
|
49
|
+
"ts",
|
|
50
|
+
"tsx",
|
|
51
|
+
"js",
|
|
52
|
+
"jsx",
|
|
53
|
+
"py",
|
|
54
|
+
"go",
|
|
55
|
+
"rs",
|
|
56
|
+
"java"
|
|
57
|
+
].includes(extension);
|
|
58
|
+
newAttachments.push({
|
|
59
|
+
id: `att_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
|
|
60
|
+
type: isCode ? "code" : "file",
|
|
61
|
+
name: file.name,
|
|
62
|
+
content: content$1,
|
|
63
|
+
mimeType: file.type,
|
|
64
|
+
size: file.size
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
setAttachments((prev) => [...prev, ...newAttachments]);
|
|
68
|
+
e.target.value = "";
|
|
69
|
+
}, [attachments.length, maxAttachments]);
|
|
70
|
+
const removeAttachment = React.useCallback((id) => {
|
|
71
|
+
setAttachments((prev) => prev.filter((a) => a.id !== id));
|
|
72
|
+
}, []);
|
|
73
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
74
|
+
className: cn("flex flex-col gap-2", className),
|
|
75
|
+
children: [
|
|
76
|
+
attachments.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
77
|
+
className: "flex flex-wrap gap-2",
|
|
78
|
+
children: attachments.map((attachment) => /* @__PURE__ */ jsxs("div", {
|
|
79
|
+
className: cn("flex items-center gap-1.5 rounded-md px-2 py-1", "bg-muted text-muted-foreground text-sm"),
|
|
80
|
+
children: [
|
|
81
|
+
attachment.type === "code" ? /* @__PURE__ */ jsx(Code, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(FileText, { className: "h-3.5 w-3.5" }),
|
|
82
|
+
/* @__PURE__ */ jsx("span", {
|
|
83
|
+
className: "max-w-[150px] truncate",
|
|
84
|
+
children: attachment.name
|
|
85
|
+
}),
|
|
86
|
+
/* @__PURE__ */ jsx("button", {
|
|
87
|
+
type: "button",
|
|
88
|
+
onClick: () => removeAttachment(attachment.id),
|
|
89
|
+
className: "hover:text-foreground",
|
|
90
|
+
"aria-label": `Remove ${attachment.name}`,
|
|
91
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
|
|
92
|
+
})
|
|
93
|
+
]
|
|
94
|
+
}, attachment.id))
|
|
95
|
+
}),
|
|
96
|
+
/* @__PURE__ */ jsxs("form", {
|
|
97
|
+
onSubmit: handleSubmit,
|
|
98
|
+
className: "flex items-end gap-2",
|
|
99
|
+
children: [
|
|
100
|
+
showAttachments && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("input", {
|
|
101
|
+
ref: fileInputRef,
|
|
102
|
+
type: "file",
|
|
103
|
+
multiple: true,
|
|
104
|
+
accept: ".ts,.tsx,.js,.jsx,.json,.md,.txt,.py,.go,.rs,.java,.yaml,.yml",
|
|
105
|
+
onChange: handleFileSelect,
|
|
106
|
+
className: "hidden",
|
|
107
|
+
"aria-label": "Attach files"
|
|
108
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
109
|
+
type: "button",
|
|
110
|
+
variant: "ghost",
|
|
111
|
+
size: "sm",
|
|
112
|
+
onPress: () => fileInputRef.current?.click(),
|
|
113
|
+
disabled: disabled || attachments.length >= maxAttachments,
|
|
114
|
+
"aria-label": "Attach files",
|
|
115
|
+
children: /* @__PURE__ */ jsx(Paperclip, { className: "h-4 w-4" })
|
|
116
|
+
})] }),
|
|
117
|
+
/* @__PURE__ */ jsx("div", {
|
|
118
|
+
className: "relative flex-1",
|
|
119
|
+
children: /* @__PURE__ */ jsx(Textarea, {
|
|
120
|
+
value: content,
|
|
121
|
+
onChange: (e) => setContent(e.target.value),
|
|
122
|
+
onKeyDown: handleKeyDown,
|
|
123
|
+
placeholder,
|
|
124
|
+
disabled,
|
|
125
|
+
className: cn("max-h-[200px] min-h-[44px] resize-none pr-12", "focus-visible:ring-1"),
|
|
126
|
+
rows: 1,
|
|
127
|
+
"aria-label": "Chat message"
|
|
128
|
+
})
|
|
129
|
+
}),
|
|
130
|
+
/* @__PURE__ */ jsx(Button, {
|
|
131
|
+
type: "submit",
|
|
132
|
+
disabled: !canSend || disabled || isLoading,
|
|
133
|
+
size: "sm",
|
|
134
|
+
"aria-label": isLoading ? "Sending..." : "Send message",
|
|
135
|
+
children: isLoading ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Send, { className: "h-4 w-4" })
|
|
136
|
+
})
|
|
137
|
+
]
|
|
138
|
+
}),
|
|
139
|
+
/* @__PURE__ */ jsx("p", {
|
|
140
|
+
className: "text-muted-foreground text-xs",
|
|
141
|
+
children: "Press Enter to send, Shift+Enter for new line"
|
|
142
|
+
})
|
|
143
|
+
]
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
148
|
+
export { ChatInput };
|
|
149
|
+
//# sourceMappingURL=ChatInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatInput.js","names":["newAttachments: ChatAttachment[]","content"],"sources":["../../../src/presentation/components/ChatInput.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cn } from '@lssm/lib.ui-kit-web/ui/utils';\nimport { Textarea } from '@lssm/lib.design-system';\nimport { Button } from '@lssm/lib.design-system';\nimport { Send, Paperclip, X, Loader2, FileText, Code } from 'lucide-react';\nimport type { ChatAttachment } from '../../core/message-types';\n\nexport interface ChatInputProps {\n /** Called when a message is sent */\n onSend: (content: string, attachments?: ChatAttachment[]) => void;\n /** Whether input is disabled (e.g., during streaming) */\n disabled?: boolean;\n /** Whether currently loading/streaming */\n isLoading?: boolean;\n /** Placeholder text */\n placeholder?: string;\n /** Additional class name */\n className?: string;\n /** Show attachment button */\n showAttachments?: boolean;\n /** Max attachments allowed */\n maxAttachments?: number;\n}\n\n/**\n * Chat input component with attachment support\n */\nexport function ChatInput({\n onSend,\n disabled = false,\n isLoading = false,\n placeholder = 'Type a message...',\n className,\n showAttachments = true,\n maxAttachments = 5,\n}: ChatInputProps) {\n const [content, setContent] = React.useState('');\n const [attachments, setAttachments] = React.useState<ChatAttachment[]>([]);\n const textareaRef = React.useRef<HTMLTextAreaElement>(null);\n const fileInputRef = React.useRef<HTMLInputElement>(null);\n\n const canSend = content.trim().length > 0 || attachments.length > 0;\n\n const handleSubmit = React.useCallback(\n (e?: React.FormEvent) => {\n e?.preventDefault();\n if (!canSend || disabled || isLoading) return;\n\n onSend(content.trim(), attachments.length > 0 ? attachments : undefined);\n setContent('');\n setAttachments([]);\n\n // Focus back on textarea\n textareaRef.current?.focus();\n },\n [canSend, content, attachments, disabled, isLoading, onSend]\n );\n\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent) => {\n // Submit on Enter (without Shift)\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n },\n [handleSubmit]\n );\n\n const handleFileSelect = React.useCallback(\n async (e: React.ChangeEvent<HTMLInputElement>) => {\n const files = e.target.files;\n if (!files) return;\n\n const newAttachments: ChatAttachment[] = [];\n\n for (const file of Array.from(files)) {\n if (attachments.length + newAttachments.length >= maxAttachments) break;\n\n const content = await file.text();\n const extension = file.name.split('.').pop()?.toLowerCase() ?? '';\n const isCode = [\n 'ts',\n 'tsx',\n 'js',\n 'jsx',\n 'py',\n 'go',\n 'rs',\n 'java',\n ].includes(extension);\n\n newAttachments.push({\n id: `att_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,\n type: isCode ? 'code' : 'file',\n name: file.name,\n content,\n mimeType: file.type,\n size: file.size,\n });\n }\n\n setAttachments((prev) => [...prev, ...newAttachments]);\n\n // Reset file input\n e.target.value = '';\n },\n [attachments.length, maxAttachments]\n );\n\n const removeAttachment = React.useCallback((id: string) => {\n setAttachments((prev) => prev.filter((a) => a.id !== id));\n }, []);\n\n return (\n <div className={cn('flex flex-col gap-2', className)}>\n {/* Attachments preview */}\n {attachments.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {attachments.map((attachment) => (\n <div\n key={attachment.id}\n className={cn(\n 'flex items-center gap-1.5 rounded-md px-2 py-1',\n 'bg-muted text-muted-foreground text-sm'\n )}\n >\n {attachment.type === 'code' ? (\n <Code className=\"h-3.5 w-3.5\" />\n ) : (\n <FileText className=\"h-3.5 w-3.5\" />\n )}\n <span className=\"max-w-[150px] truncate\">{attachment.name}</span>\n <button\n type=\"button\"\n onClick={() => removeAttachment(attachment.id)}\n className=\"hover:text-foreground\"\n aria-label={`Remove ${attachment.name}`}\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n ))}\n </div>\n )}\n\n {/* Input form */}\n <form onSubmit={handleSubmit} className=\"flex items-end gap-2\">\n {/* Attachment button */}\n {showAttachments && (\n <>\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n accept=\".ts,.tsx,.js,.jsx,.json,.md,.txt,.py,.go,.rs,.java,.yaml,.yml\"\n onChange={handleFileSelect}\n className=\"hidden\"\n aria-label=\"Attach files\"\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onPress={() => fileInputRef.current?.click()}\n disabled={disabled || attachments.length >= maxAttachments}\n aria-label=\"Attach files\"\n >\n <Paperclip className=\"h-4 w-4\" />\n </Button>\n </>\n )}\n\n {/* Text input */}\n <div className=\"relative flex-1\">\n <Textarea\n value={content}\n onChange={(e) => setContent(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n disabled={disabled}\n className={cn(\n 'max-h-[200px] min-h-[44px] resize-none pr-12',\n 'focus-visible:ring-1'\n )}\n rows={1}\n aria-label=\"Chat message\"\n />\n </div>\n\n {/* Send button */}\n <Button\n type=\"submit\"\n disabled={!canSend || disabled || isLoading}\n size=\"sm\"\n aria-label={isLoading ? 'Sending...' : 'Send message'}\n >\n {isLoading ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Send className=\"h-4 w-4\" />\n )}\n </Button>\n </form>\n\n {/* Helper text */}\n <p className=\"text-muted-foreground text-xs\">\n Press Enter to send, Shift+Enter for new line\n </p>\n </div>\n );\n}\n"],"mappings":"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"}
|