@contractspec/module.ai-chat 0.0.0-canary-20260113170453
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 +169 -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 +102 -0
- 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 +172 -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 -0
- 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 -0
- package/dist/context/file-operations.js.map +1 -0
- package/dist/context/index.d.ts +4 -0
- package/dist/context/index.js +5 -0
- 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 -0
- 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 +227 -0
- 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 -0
- package/dist/core/conversation-store.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +4 -0
- 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 +98 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +23 -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 -0
- 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 -0
- 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 -0
- 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 -0
- 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 -0
- 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 -0
- 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 -0
- package/dist/presentation/hooks/index.d.ts +3 -0
- package/dist/presentation/hooks/index.js +4 -0
- 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 -0
- 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 -0
- package/dist/presentation/hooks/useProviders.js.map +1 -0
- package/dist/presentation/index.d.ts +11 -0
- package/dist/presentation/index.js +12 -0
- 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 -0
- package/dist/providers/chat-utilities.js.map +1 -0
- package/dist/providers/index.d.ts +3 -0
- package/dist/providers/index.js +4 -0
- package/dist/schema.d.ts +222 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +100 -0
- package/dist/schema.js.map +1 -0
- package/package.json +87 -0
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as _contractspec_lib_contracts0 from "@contractspec/lib.contracts";
|
|
2
|
+
import * as _contractspec_lib_schema0 from "@contractspec/lib.schema";
|
|
3
|
+
|
|
4
|
+
//#region src/events.d.ts
|
|
5
|
+
declare const MessageSentEvent: _contractspec_lib_contracts0.EventSpec<_contractspec_lib_schema0.SchemaModel<{
|
|
6
|
+
id: {
|
|
7
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
8
|
+
isOptional: false;
|
|
9
|
+
};
|
|
10
|
+
role: {
|
|
11
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
12
|
+
isOptional: false;
|
|
13
|
+
};
|
|
14
|
+
content: {
|
|
15
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
16
|
+
isOptional: false;
|
|
17
|
+
};
|
|
18
|
+
status: {
|
|
19
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
20
|
+
isOptional: false;
|
|
21
|
+
};
|
|
22
|
+
createdAt: {
|
|
23
|
+
type: _contractspec_lib_schema0.FieldType<Date, string>;
|
|
24
|
+
isOptional: false;
|
|
25
|
+
};
|
|
26
|
+
}>>;
|
|
27
|
+
declare const MessageReceivedEvent: _contractspec_lib_contracts0.EventSpec<_contractspec_lib_schema0.SchemaModel<{
|
|
28
|
+
id: {
|
|
29
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
30
|
+
isOptional: false;
|
|
31
|
+
};
|
|
32
|
+
role: {
|
|
33
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
34
|
+
isOptional: false;
|
|
35
|
+
};
|
|
36
|
+
content: {
|
|
37
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
38
|
+
isOptional: false;
|
|
39
|
+
};
|
|
40
|
+
status: {
|
|
41
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
42
|
+
isOptional: false;
|
|
43
|
+
};
|
|
44
|
+
createdAt: {
|
|
45
|
+
type: _contractspec_lib_schema0.FieldType<Date, string>;
|
|
46
|
+
isOptional: false;
|
|
47
|
+
};
|
|
48
|
+
}>>;
|
|
49
|
+
declare const ConversationCreatedEvent: _contractspec_lib_contracts0.EventSpec<_contractspec_lib_schema0.SchemaModel<{
|
|
50
|
+
id: {
|
|
51
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
52
|
+
isOptional: false;
|
|
53
|
+
};
|
|
54
|
+
title: {
|
|
55
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
56
|
+
isOptional: true;
|
|
57
|
+
};
|
|
58
|
+
status: {
|
|
59
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
60
|
+
isOptional: false;
|
|
61
|
+
};
|
|
62
|
+
messages: {
|
|
63
|
+
type: _contractspec_lib_schema0.SchemaModel<{
|
|
64
|
+
id: {
|
|
65
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
66
|
+
isOptional: false;
|
|
67
|
+
};
|
|
68
|
+
role: {
|
|
69
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
70
|
+
isOptional: false;
|
|
71
|
+
};
|
|
72
|
+
content: {
|
|
73
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
74
|
+
isOptional: false;
|
|
75
|
+
};
|
|
76
|
+
status: {
|
|
77
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
78
|
+
isOptional: false;
|
|
79
|
+
};
|
|
80
|
+
createdAt: {
|
|
81
|
+
type: _contractspec_lib_schema0.FieldType<Date, string>;
|
|
82
|
+
isOptional: false;
|
|
83
|
+
};
|
|
84
|
+
}>;
|
|
85
|
+
isArray: true;
|
|
86
|
+
isOptional: false;
|
|
87
|
+
};
|
|
88
|
+
provider: {
|
|
89
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
90
|
+
isOptional: false;
|
|
91
|
+
};
|
|
92
|
+
model: {
|
|
93
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
94
|
+
isOptional: false;
|
|
95
|
+
};
|
|
96
|
+
}>>;
|
|
97
|
+
declare const ConversationDeletedEvent: _contractspec_lib_contracts0.EventSpec<_contractspec_lib_schema0.SchemaModel<{
|
|
98
|
+
id: {
|
|
99
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
100
|
+
isOptional: false;
|
|
101
|
+
};
|
|
102
|
+
}>>;
|
|
103
|
+
declare const ChatErrorEvent: _contractspec_lib_contracts0.EventSpec<_contractspec_lib_schema0.SchemaModel<{
|
|
104
|
+
code: {
|
|
105
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
106
|
+
isOptional: false;
|
|
107
|
+
};
|
|
108
|
+
message: {
|
|
109
|
+
type: _contractspec_lib_schema0.FieldType<string, string>;
|
|
110
|
+
isOptional: false;
|
|
111
|
+
};
|
|
112
|
+
}>>;
|
|
113
|
+
//#endregion
|
|
114
|
+
export { ChatErrorEvent, ConversationCreatedEvent, ConversationDeletedEvent, MessageReceivedEvent, MessageSentEvent };
|
|
115
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","names":[],"sources":["../src/events.ts"],"sourcesContent":[],"mappings":";;;;cAIa,kBAAgB,4BAAA,CAAA,oCAAA;;UAU3B,yBAAA,CAAA;;EAVW,CAAA;EAUX,IAAA,EAAA;;;;;;;EAV2B,CAAA;EAAA,MAAA,EAAA;IAYhB,IAAA,qCAUX,CAAA,MAAA,EAAA,MAAA,CAAA;IAAA,UAAA,EAAA,KAAA;;;;;;;AAV+B,cAApB,oBAAoB,EAAA,4BAAA,CAAA,SAAA,2BAAA,WAAA,CAAA;EAAA,EAAA,EAAA;IAYpB,IAAA,EAFX,yBAAA,CAAA,SAYA,CAAA,MAAA,EAAA,MAAA,CAAA;IAAA,UAAA,EAAA,KAAA;;;;;;;;;;;;;EAVmC,CAAA;EAAA,SAAA,EAAA;IAYxB,IAAA,qCAeX,KAAA,EAAA,MAAA,CAAA;IAAA,UAAA,EAAA,KAAA;;CAfmC,CAAA,CAAA;AAAA,cAZxB,wBAYwB,EAZA,4BAAA,CAAA,SAYA,2BAZA,WAYA,CAAA;EAiBxB,EAAA,EAAA;IAgBX,IAAA,EAnCA,yBAAA,CAAA,SAmCA,CAAA,MAAA,EAAA,MAAA,CAAA;;;EAhByB,KAAA,EAAA;IAAA,IAAA,qCAAA,CAAA,MAAA,EAAA,MAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAjBd,0BAAwB,4BAAA,CAAA,oCAAA;;UAenC,yBAAA,CAAA;;;;cAEW,gBAAc,4BAAA,CAAA,oCAAA;;UAgBzB,yBAAA,CAAA"}
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { ChatConversationModel, ChatMessageModel } from "./schema.js";
|
|
2
|
+
import { defineEvent } from "@contractspec/lib.contracts";
|
|
3
|
+
import { ScalarTypeEnum, defineSchemaModel } from "@contractspec/lib.schema";
|
|
4
|
+
|
|
5
|
+
//#region src/events.ts
|
|
6
|
+
const MessageSentEvent = defineEvent({
|
|
7
|
+
meta: {
|
|
8
|
+
key: "ai-chat.message.sent",
|
|
9
|
+
version: "1.0.0",
|
|
10
|
+
description: "Message sent by user",
|
|
11
|
+
stability: "stable",
|
|
12
|
+
owners: ["@ai-chat"],
|
|
13
|
+
tags: [
|
|
14
|
+
"ai-chat",
|
|
15
|
+
"message",
|
|
16
|
+
"sent"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
payload: ChatMessageModel
|
|
20
|
+
});
|
|
21
|
+
const MessageReceivedEvent = defineEvent({
|
|
22
|
+
meta: {
|
|
23
|
+
key: "ai-chat.message.received",
|
|
24
|
+
version: "1.0.0",
|
|
25
|
+
description: "Message received from AI",
|
|
26
|
+
stability: "stable",
|
|
27
|
+
owners: ["@ai-chat"],
|
|
28
|
+
tags: [
|
|
29
|
+
"ai-chat",
|
|
30
|
+
"message",
|
|
31
|
+
"received"
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
payload: ChatMessageModel
|
|
35
|
+
});
|
|
36
|
+
const ConversationCreatedEvent = defineEvent({
|
|
37
|
+
meta: {
|
|
38
|
+
key: "ai-chat.conversation.created",
|
|
39
|
+
version: "1.0.0",
|
|
40
|
+
description: "New conversation created",
|
|
41
|
+
stability: "stable",
|
|
42
|
+
owners: ["@ai-chat"],
|
|
43
|
+
tags: [
|
|
44
|
+
"ai-chat",
|
|
45
|
+
"conversation",
|
|
46
|
+
"created"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
payload: ChatConversationModel
|
|
50
|
+
});
|
|
51
|
+
const ConversationDeletedEvent = defineEvent({
|
|
52
|
+
meta: {
|
|
53
|
+
key: "ai-chat.conversation.deleted",
|
|
54
|
+
version: "1.0.0",
|
|
55
|
+
description: "Conversation deleted",
|
|
56
|
+
stability: "stable",
|
|
57
|
+
owners: ["@ai-chat"],
|
|
58
|
+
tags: [
|
|
59
|
+
"ai-chat",
|
|
60
|
+
"conversation",
|
|
61
|
+
"deleted"
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
payload: defineSchemaModel({
|
|
65
|
+
name: "ConversationDeletedPayload",
|
|
66
|
+
fields: { id: {
|
|
67
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
68
|
+
isOptional: false
|
|
69
|
+
} }
|
|
70
|
+
})
|
|
71
|
+
});
|
|
72
|
+
const ChatErrorEvent = defineEvent({
|
|
73
|
+
meta: {
|
|
74
|
+
key: "ai-chat.error",
|
|
75
|
+
version: "1.0.0",
|
|
76
|
+
description: "Chat error occurred",
|
|
77
|
+
stability: "stable",
|
|
78
|
+
owners: ["@ai-chat"],
|
|
79
|
+
tags: ["ai-chat", "error"]
|
|
80
|
+
},
|
|
81
|
+
payload: defineSchemaModel({
|
|
82
|
+
name: "ChatErrorPayload",
|
|
83
|
+
fields: {
|
|
84
|
+
code: {
|
|
85
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
86
|
+
isOptional: false
|
|
87
|
+
},
|
|
88
|
+
message: {
|
|
89
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
90
|
+
isOptional: false
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
//#endregion
|
|
97
|
+
export { ChatErrorEvent, ConversationCreatedEvent, ConversationDeletedEvent, MessageReceivedEvent, MessageSentEvent };
|
|
98
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","names":[],"sources":["../src/events.ts"],"sourcesContent":["import { defineEvent } from '@contractspec/lib.contracts';\nimport { defineSchemaModel, ScalarTypeEnum } from '@contractspec/lib.schema';\nimport { ChatMessageModel, ChatConversationModel } from './schema';\n\nexport const MessageSentEvent = defineEvent({\n meta: {\n key: 'ai-chat.message.sent',\n version: '1.0.0',\n description: 'Message sent by user',\n stability: 'stable',\n owners: ['@ai-chat'],\n tags: ['ai-chat', 'message', 'sent'],\n },\n payload: ChatMessageModel,\n});\n\nexport const MessageReceivedEvent = defineEvent({\n meta: {\n key: 'ai-chat.message.received',\n version: '1.0.0',\n description: 'Message received from AI',\n stability: 'stable',\n owners: ['@ai-chat'],\n tags: ['ai-chat', 'message', 'received'],\n },\n payload: ChatMessageModel,\n});\n\nexport const ConversationCreatedEvent = defineEvent({\n meta: {\n key: 'ai-chat.conversation.created',\n version: '1.0.0',\n description: 'New conversation created',\n stability: 'stable',\n owners: ['@ai-chat'],\n tags: ['ai-chat', 'conversation', 'created'],\n },\n payload: ChatConversationModel,\n});\n\nexport const ConversationDeletedEvent = defineEvent({\n meta: {\n key: 'ai-chat.conversation.deleted',\n version: '1.0.0',\n description: 'Conversation deleted',\n stability: 'stable',\n owners: ['@ai-chat'],\n tags: ['ai-chat', 'conversation', 'deleted'],\n },\n payload: defineSchemaModel({\n name: 'ConversationDeletedPayload',\n fields: {\n id: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },\n },\n }),\n});\n\nexport const ChatErrorEvent = defineEvent({\n meta: {\n key: 'ai-chat.error',\n version: '1.0.0',\n description: 'Chat error occurred',\n stability: 'stable',\n owners: ['@ai-chat'],\n tags: ['ai-chat', 'error'],\n },\n payload: defineSchemaModel({\n name: 'ChatErrorPayload',\n fields: {\n code: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },\n message: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },\n },\n }),\n});\n"],"mappings":";;;;;AAIA,MAAa,mBAAmB,YAAY;CAC1C,MAAM;EACJ,KAAK;EACL,SAAS;EACT,aAAa;EACb,WAAW;EACX,QAAQ,CAAC,WAAW;EACpB,MAAM;GAAC;GAAW;GAAW;GAAO;EACrC;CACD,SAAS;CACV,CAAC;AAEF,MAAa,uBAAuB,YAAY;CAC9C,MAAM;EACJ,KAAK;EACL,SAAS;EACT,aAAa;EACb,WAAW;EACX,QAAQ,CAAC,WAAW;EACpB,MAAM;GAAC;GAAW;GAAW;GAAW;EACzC;CACD,SAAS;CACV,CAAC;AAEF,MAAa,2BAA2B,YAAY;CAClD,MAAM;EACJ,KAAK;EACL,SAAS;EACT,aAAa;EACb,WAAW;EACX,QAAQ,CAAC,WAAW;EACpB,MAAM;GAAC;GAAW;GAAgB;GAAU;EAC7C;CACD,SAAS;CACV,CAAC;AAEF,MAAa,2BAA2B,YAAY;CAClD,MAAM;EACJ,KAAK;EACL,SAAS;EACT,aAAa;EACb,WAAW;EACX,QAAQ,CAAC,WAAW;EACpB,MAAM;GAAC;GAAW;GAAgB;GAAU;EAC7C;CACD,SAAS,kBAAkB;EACzB,MAAM;EACN,QAAQ,EACN,IAAI;GAAE,MAAM,eAAe,iBAAiB;GAAE,YAAY;GAAO,EAClE;EACF,CAAC;CACH,CAAC;AAEF,MAAa,iBAAiB,YAAY;CACxC,MAAM;EACJ,KAAK;EACL,SAAS;EACT,aAAa;EACb,WAAW;EACX,QAAQ,CAAC,WAAW;EACpB,MAAM,CAAC,WAAW,QAAQ;EAC3B;CACD,SAAS,kBAAkB;EACzB,MAAM;EACN,QAAQ;GACN,MAAM;IAAE,MAAM,eAAe,iBAAiB;IAAE,YAAY;IAAO;GACnE,SAAS;IAAE,MAAM,eAAe,iBAAiB;IAAE,YAAY;IAAO;GACvE;EACF,CAAC;CACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { FileInfo, SpecInfo, WorkspaceContext, WorkspaceContextConfig, WorkspaceSummary, createWorkspaceContext } from "./context/workspace-context.js";
|
|
2
|
+
import { BuiltContext, ContextBuilder, ContextBuilderOptions, ContextEntry, createContextBuilder } from "./context/context-builder.js";
|
|
3
|
+
import { FileOperation, FileOperationResult, FileOperations, FileReadResult, FileSystem, FileWriteResult, createNodeFileOperations } from "./context/file-operations.js";
|
|
4
|
+
import { AiChatFeature } from "./ai-chat.feature.js";
|
|
5
|
+
import { ChatContainer } from "./presentation/components/ChatContainer.js";
|
|
6
|
+
import { ChatMessage } from "./presentation/components/ChatMessage.js";
|
|
7
|
+
import { ChatInput } from "./presentation/components/ChatInput.js";
|
|
8
|
+
import { ModelPicker } from "./presentation/components/ModelPicker.js";
|
|
9
|
+
import { ContextIndicator } from "./presentation/components/ContextIndicator.js";
|
|
10
|
+
import { CodePreview } from "./presentation/components/CodePreview.js";
|
|
11
|
+
import "./presentation/components/index.js";
|
|
12
|
+
import { UseChatOptions, UseChatReturn, useChat } from "./presentation/hooks/useChat.js";
|
|
13
|
+
import { UseProvidersReturn, useProviders } from "./presentation/hooks/useProviders.js";
|
|
14
|
+
import "./presentation/hooks/index.js";
|
|
15
|
+
import "./presentation/index.js";
|
|
16
|
+
import { isStudioAvailable, supportsLocalMode } from "./providers/chat-utilities.js";
|
|
17
|
+
import { ChatModelInfo, ChatProvider, ChatProviderConfig, ChatProviderMode, ChatProviderName, DEFAULT_MODELS, MODELS, ModelCapabilities, ProviderAvailability, createProvider, createProviderFromEnv, getAvailableProviders, getDefaultModel, getEnvVarName, getModelInfo, getModelsForProvider, getRecommendedModels, hasCredentials, isOllamaRunning, listOllamaModels, validateProvider } from "./providers/index.js";
|
|
18
|
+
import { ChatConversationModel, ChatMessageModel, ListConversationsOutputModel, SendMessageInputModel, SendMessageOutputModel } from "./schema.js";
|
|
19
|
+
import { DeleteConversationContract, GetConversationContract, ListConversationsContract, ListProvidersContract, ScanContextContract, SendMessageContract, StreamMessageContract } from "./ai-chat.operations.js";
|
|
20
|
+
import { ChatErrorEvent, ConversationCreatedEvent, ConversationDeletedEvent, MessageReceivedEvent, MessageSentEvent } from "./events.js";
|
|
21
|
+
export { AiChatFeature, BuiltContext, ChatContainer, ChatConversationModel, ChatErrorEvent, ChatInput, ChatMessage, ChatMessage as ChatMessageComponent, ChatMessageModel, ChatModelInfo, ChatProvider, ChatProviderConfig, ChatProviderMode, ChatProviderName, CodePreview, ContextBuilder, ContextBuilderOptions, ContextEntry, ContextIndicator, ConversationCreatedEvent, ConversationDeletedEvent, DEFAULT_MODELS, DeleteConversationContract, FileInfo, FileOperation, FileOperationResult, FileOperations, FileReadResult, FileSystem, FileWriteResult, GetConversationContract, ListConversationsContract, ListConversationsOutputModel, ListProvidersContract, MODELS, MessageReceivedEvent, MessageSentEvent, ModelCapabilities, ModelPicker, ProviderAvailability, ScanContextContract, SendMessageContract, SendMessageInputModel, SendMessageOutputModel, SpecInfo, StreamMessageContract, UseChatOptions, UseChatReturn, UseProvidersReturn, WorkspaceContext, WorkspaceContextConfig, WorkspaceSummary, createContextBuilder, createNodeFileOperations, createProvider, createProviderFromEnv, createWorkspaceContext, getAvailableProviders, getDefaultModel, getEnvVarName, getModelInfo, getModelsForProvider, getRecommendedModels, hasCredentials, isOllamaRunning, isStudioAvailable, listOllamaModels, supportsLocalMode, useChat, useProviders, validateProvider };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { AiChatFeature } from "./ai-chat.feature.js";
|
|
2
|
+
import { ChatContainer } from "./presentation/components/ChatContainer.js";
|
|
3
|
+
import { CodePreview } from "./presentation/components/CodePreview.js";
|
|
4
|
+
import { ChatMessage } from "./presentation/components/ChatMessage.js";
|
|
5
|
+
import { ChatInput } from "./presentation/components/ChatInput.js";
|
|
6
|
+
import { ModelPicker } from "./presentation/components/ModelPicker.js";
|
|
7
|
+
import { ContextIndicator } from "./presentation/components/ContextIndicator.js";
|
|
8
|
+
import "./presentation/components/index.js";
|
|
9
|
+
import { useChat } from "./presentation/hooks/useChat.js";
|
|
10
|
+
import { useProviders } from "./presentation/hooks/useProviders.js";
|
|
11
|
+
import "./presentation/hooks/index.js";
|
|
12
|
+
import "./presentation/index.js";
|
|
13
|
+
import { isStudioAvailable, supportsLocalMode } from "./providers/chat-utilities.js";
|
|
14
|
+
import { DEFAULT_MODELS, MODELS, createProvider, createProviderFromEnv, getAvailableProviders, getDefaultModel, getEnvVarName, getModelInfo, getModelsForProvider, getRecommendedModels, hasCredentials, isOllamaRunning, listOllamaModels, validateProvider } from "./providers/index.js";
|
|
15
|
+
import { WorkspaceContext, createWorkspaceContext } from "./context/workspace-context.js";
|
|
16
|
+
import { ContextBuilder, createContextBuilder } from "./context/context-builder.js";
|
|
17
|
+
import { FileOperations, createNodeFileOperations } from "./context/file-operations.js";
|
|
18
|
+
import "./context/index.js";
|
|
19
|
+
import { ChatConversationModel, ChatMessageModel, ListConversationsOutputModel, SendMessageInputModel, SendMessageOutputModel } from "./schema.js";
|
|
20
|
+
import { DeleteConversationContract, GetConversationContract, ListConversationsContract, ListProvidersContract, ScanContextContract, SendMessageContract, StreamMessageContract } from "./ai-chat.operations.js";
|
|
21
|
+
import { ChatErrorEvent, ConversationCreatedEvent, ConversationDeletedEvent, MessageReceivedEvent, MessageSentEvent } from "./events.js";
|
|
22
|
+
|
|
23
|
+
export { AiChatFeature, ChatContainer, ChatConversationModel, ChatErrorEvent, ChatInput, ChatMessage, ChatMessage as ChatMessageComponent, ChatMessageModel, CodePreview, ContextBuilder, ContextIndicator, ConversationCreatedEvent, ConversationDeletedEvent, DEFAULT_MODELS, DeleteConversationContract, FileOperations, GetConversationContract, ListConversationsContract, ListConversationsOutputModel, ListProvidersContract, MODELS, MessageReceivedEvent, MessageSentEvent, ModelPicker, ScanContextContract, SendMessageContract, SendMessageInputModel, SendMessageOutputModel, StreamMessageContract, WorkspaceContext, createContextBuilder, createNodeFileOperations, createProvider, createProviderFromEnv, createWorkspaceContext, getAvailableProviders, getDefaultModel, getEnvVarName, getModelInfo, getModelsForProvider, getRecommendedModels, hasCredentials, isOllamaRunning, isStudioAvailable, listOllamaModels, supportsLocalMode, useChat, useProviders, validateProvider };
|
|
@@ -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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { ScrollArea } from "@contractspec/lib.ui-kit-web/ui/scroll-area";
|
|
5
|
+
import { cn } from "@contractspec/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 '@contractspec/lib.ui-kit-web/ui/scroll-area';\nimport { cn } from '@contractspec/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"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "@contractspec/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 "@contractspec/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":["content"],"sources":["../../../src/presentation/components/ChatInput.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cn } from '@contractspec/lib.ui-kit-web/ui/utils';\nimport { Textarea } from '@contractspec/lib.design-system';\nimport { Button } from '@contractspec/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":";;;;;;;;;;;;AA6BA,SAAgB,UAAU,EACxB,QACA,WAAW,OACX,YAAY,OACZ,cAAc,qBACd,WACA,kBAAkB,MAClB,iBAAiB,KACA;CACjB,MAAM,CAAC,SAAS,cAAc,MAAM,SAAS,GAAG;CAChD,MAAM,CAAC,aAAa,kBAAkB,MAAM,SAA2B,EAAE,CAAC;CAC1E,MAAM,cAAc,MAAM,OAA4B,KAAK;CAC3D,MAAM,eAAe,MAAM,OAAyB,KAAK;CAEzD,MAAM,UAAU,QAAQ,MAAM,CAAC,SAAS,KAAK,YAAY,SAAS;CAElE,MAAM,eAAe,MAAM,aACxB,MAAwB;AACvB,KAAG,gBAAgB;AACnB,MAAI,CAAC,WAAW,YAAY,UAAW;AAEvC,SAAO,QAAQ,MAAM,EAAE,YAAY,SAAS,IAAI,cAAc,OAAU;AACxE,aAAW,GAAG;AACd,iBAAe,EAAE,CAAC;AAGlB,cAAY,SAAS,OAAO;IAE9B;EAAC;EAAS;EAAS;EAAa;EAAU;EAAW;EAAO,CAC7D;CAED,MAAM,gBAAgB,MAAM,aACzB,MAA2B;AAE1B,MAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,UAAU;AACpC,KAAE,gBAAgB;AAClB,iBAAc;;IAGlB,CAAC,aAAa,CACf;CAED,MAAM,mBAAmB,MAAM,YAC7B,OAAO,MAA2C;EAChD,MAAM,QAAQ,EAAE,OAAO;AACvB,MAAI,CAAC,MAAO;EAEZ,MAAM,iBAAmC,EAAE;AAE3C,OAAK,MAAM,QAAQ,MAAM,KAAK,MAAM,EAAE;AACpC,OAAI,YAAY,SAAS,eAAe,UAAU,eAAgB;GAElE,MAAMA,YAAU,MAAM,KAAK,MAAM;GACjC,MAAM,YAAY,KAAK,KAAK,MAAM,IAAI,CAAC,KAAK,EAAE,aAAa,IAAI;GAC/D,MAAM,SAAS;IACb;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,SAAS,UAAU;AAErB,kBAAe,KAAK;IAClB,IAAI,OAAO,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE;IAC/D,MAAM,SAAS,SAAS;IACxB,MAAM,KAAK;IACX;IACA,UAAU,KAAK;IACf,MAAM,KAAK;IACZ,CAAC;;AAGJ,kBAAgB,SAAS,CAAC,GAAG,MAAM,GAAG,eAAe,CAAC;AAGtD,IAAE,OAAO,QAAQ;IAEnB,CAAC,YAAY,QAAQ,eAAe,CACrC;CAED,MAAM,mBAAmB,MAAM,aAAa,OAAe;AACzD,kBAAgB,SAAS,KAAK,QAAQ,MAAM,EAAE,OAAO,GAAG,CAAC;IACxD,EAAE,CAAC;AAEN,QACE,qBAAC;EAAI,WAAW,GAAG,uBAAuB,UAAU;;GAEjD,YAAY,SAAS,KACpB,oBAAC;IAAI,WAAU;cACZ,YAAY,KAAK,eAChB,qBAAC;KAEC,WAAW,GACT,kDACA,yCACD;;MAEA,WAAW,SAAS,SACnB,oBAAC,QAAK,WAAU,gBAAgB,GAEhC,oBAAC,YAAS,WAAU,gBAAgB;MAEtC,oBAAC;OAAK,WAAU;iBAA0B,WAAW;QAAY;MACjE,oBAAC;OACC,MAAK;OACL,eAAe,iBAAiB,WAAW,GAAG;OAC9C,WAAU;OACV,cAAY,UAAU,WAAW;iBAEjC,oBAAC,KAAE,WAAU,gBAAgB;QACtB;;OAnBJ,WAAW,GAoBZ,CACN;KACE;GAIR,qBAAC;IAAK,UAAU;IAAc,WAAU;;KAErC,mBACC,4CACE,oBAAC;MACC,KAAK;MACL,MAAK;MACL;MACA,QAAO;MACP,UAAU;MACV,WAAU;MACV,cAAW;OACX,EACF,oBAAC;MACC,MAAK;MACL,SAAQ;MACR,MAAK;MACL,eAAe,aAAa,SAAS,OAAO;MAC5C,UAAU,YAAY,YAAY,UAAU;MAC5C,cAAW;gBAEX,oBAAC,aAAU,WAAU,YAAY;OAC1B,IACR;KAIL,oBAAC;MAAI,WAAU;gBACb,oBAAC;OACC,OAAO;OACP,WAAW,MAAM,WAAW,EAAE,OAAO,MAAM;OAC3C,WAAW;OACE;OACH;OACV,WAAW,GACT,gDACA,uBACD;OACD,MAAM;OACN,cAAW;QACX;OACE;KAGN,oBAAC;MACC,MAAK;MACL,UAAU,CAAC,WAAW,YAAY;MAClC,MAAK;MACL,cAAY,YAAY,eAAe;gBAEtC,YACC,oBAAC,WAAQ,WAAU,yBAAyB,GAE5C,oBAAC,QAAK,WAAU,YAAY;OAEvB;;KACJ;GAGP,oBAAC;IAAE,WAAU;cAAgC;KAEzC;;GACA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ChatMessage as ChatMessage$1 } from "../../core/message-types.js";
|
|
2
|
+
import * as react_jsx_runtime1 from "react/jsx-runtime";
|
|
3
|
+
|
|
4
|
+
//#region src/presentation/components/ChatMessage.d.ts
|
|
5
|
+
interface ChatMessageProps {
|
|
6
|
+
message: ChatMessage$1;
|
|
7
|
+
className?: string;
|
|
8
|
+
/** Show copy button */
|
|
9
|
+
showCopy?: boolean;
|
|
10
|
+
/** Show avatar */
|
|
11
|
+
showAvatar?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Chat message component
|
|
15
|
+
*/
|
|
16
|
+
declare function ChatMessage({
|
|
17
|
+
message,
|
|
18
|
+
className,
|
|
19
|
+
showCopy,
|
|
20
|
+
showAvatar
|
|
21
|
+
}: ChatMessageProps): react_jsx_runtime1.JSX.Element;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { ChatMessage };
|
|
24
|
+
//# sourceMappingURL=ChatMessage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatMessage.d.ts","names":[],"sources":["../../../src/presentation/components/ChatMessage.tsx"],"sourcesContent":[],"mappings":";;;;UAWiB,gBAAA;WACN;;EADM;EA+ED,QAAA,CAAA,EAAA,OAAW;EACzB;EACA,UAAA,CAAA,EAAA,OAAA;;;;;AAGiB,iBALH,WAAA,CAKG;EAAA,OAAA;EAAA,SAAA;EAAA,QAAA;EAAA;AAAA,CAAA,EAAhB,gBAAgB,CAAA,EAAA,kBAAA,CAAA,GAAA,CAAA,OAAA"}
|