@extrachill/chat 0.2.0
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/CHANGELOG.md +24 -0
- package/README.md +154 -0
- package/css/chat.css +552 -0
- package/dist/Chat.d.ts +73 -0
- package/dist/Chat.d.ts.map +1 -0
- package/dist/Chat.js +50 -0
- package/dist/api.d.ts +68 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +93 -0
- package/dist/components/AvailabilityGate.d.ts +19 -0
- package/dist/components/AvailabilityGate.d.ts.map +1 -0
- package/dist/components/AvailabilityGate.js +32 -0
- package/dist/components/ChatInput.d.ts +21 -0
- package/dist/components/ChatInput.d.ts.map +1 -0
- package/dist/components/ChatInput.js +52 -0
- package/dist/components/ChatMessage.d.ts +23 -0
- package/dist/components/ChatMessage.d.ts.map +1 -0
- package/dist/components/ChatMessage.js +34 -0
- package/dist/components/ChatMessages.d.ts +28 -0
- package/dist/components/ChatMessages.d.ts.map +1 -0
- package/dist/components/ChatMessages.js +121 -0
- package/dist/components/ErrorBoundary.d.ts +27 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.js +34 -0
- package/dist/components/SessionSwitcher.d.ts +25 -0
- package/dist/components/SessionSwitcher.d.ts.map +1 -0
- package/dist/components/SessionSwitcher.js +44 -0
- package/dist/components/ToolMessage.d.ts +34 -0
- package/dist/components/ToolMessage.d.ts.map +1 -0
- package/dist/components/ToolMessage.js +39 -0
- package/dist/components/TypingIndicator.d.ts +16 -0
- package/dist/components/TypingIndicator.d.ts.map +1 -0
- package/dist/components/TypingIndicator.js +14 -0
- package/dist/components/index.d.ts +9 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +8 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useChat.d.ts +102 -0
- package/dist/hooks/useChat.d.ts.map +1 -0
- package/dist/hooks/useChat.js +192 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/normalizer.d.ts +24 -0
- package/dist/normalizer.d.ts.map +1 -0
- package/dist/normalizer.js +96 -0
- package/dist/types/adapter.d.ts +151 -0
- package/dist/types/adapter.d.ts.map +1 -0
- package/dist/types/adapter.js +11 -0
- package/dist/types/api.d.ts +137 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/api.js +8 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/message.d.ts +62 -0
- package/dist/types/message.d.ts.map +1 -0
- package/dist/types/message.js +7 -0
- package/dist/types/session.d.ts +59 -0
- package/dist/types/session.d.ts.map +1 -0
- package/dist/types/session.js +7 -0
- package/package.json +61 -0
- package/src/Chat.tsx +157 -0
- package/src/api.ts +173 -0
- package/src/components/AvailabilityGate.tsx +85 -0
- package/src/components/ChatInput.tsx +114 -0
- package/src/components/ChatMessage.tsx +85 -0
- package/src/components/ChatMessages.tsx +193 -0
- package/src/components/ErrorBoundary.tsx +66 -0
- package/src/components/SessionSwitcher.tsx +129 -0
- package/src/components/ToolMessage.tsx +112 -0
- package/src/components/TypingIndicator.tsx +36 -0
- package/src/components/index.ts +8 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useChat.ts +310 -0
- package/src/index.ts +79 -0
- package/src/normalizer.ts +112 -0
- package/src/types/api.ts +146 -0
- package/src/types/index.ts +26 -0
- package/src/types/message.ts +66 -0
- package/src/types/session.ts +50 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
- **BREAKING:** Remove `ChatAdapter` interface and adapter pattern
|
|
6
|
+
- Package now speaks the standard chat REST API contract natively
|
|
7
|
+
- Add `api.ts` — built-in REST client (`sendMessage`, `continueResponse`, `listSessions`, `loadSession`, `deleteSession`)
|
|
8
|
+
- Add `normalizer.ts` — maps raw backend messages to `ChatMessage` format
|
|
9
|
+
- Add `types/api.ts` — typed REST request/response shapes
|
|
10
|
+
- `useChat` hook now takes `basePath`, `fetchFn`, and `agentId` instead of an adapter
|
|
11
|
+
- `Chat` component props updated: `basePath` + `fetchFn` + `agentId` replace `adapter`
|
|
12
|
+
- Add `showSessions` prop to `Chat` for optional session switcher
|
|
13
|
+
- `showTools` defaults to `true` (was `false`)
|
|
14
|
+
- Export API functions and normalizer for advanced use cases
|
|
15
|
+
- Remove `ChatCapabilities`, `SendMessageInput`, `SendMessageResult`, `ContinueResult`, `StreamChunk` types
|
|
16
|
+
|
|
17
|
+
## 0.1.0
|
|
18
|
+
|
|
19
|
+
- Initial release
|
|
20
|
+
- Adapter contract (`ChatAdapter` interface) with capability flags
|
|
21
|
+
- Normalized message model (`ChatMessage`, `ChatSession`, `ChatAvailability`)
|
|
22
|
+
- Core components: `ChatMessages`, `ChatMessage`, `ChatInput`, `TypingIndicator`, `ToolMessage`, `SessionSwitcher`, `ErrorBoundary`
|
|
23
|
+
- `useChat` hook — core state orchestrator with adapter integration
|
|
24
|
+
- Base CSS styles
|
package/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Extra Chill Chat
|
|
2
|
+
|
|
3
|
+
Generic frontend Gutenberg chat block for WordPress.
|
|
4
|
+
|
|
5
|
+
## Vision
|
|
6
|
+
|
|
7
|
+
`extra-chill/chat` is a shared chat UI primitive for WordPress.
|
|
8
|
+
|
|
9
|
+
The goal is to provide a **standard, frontend-first, dumb chat block** that can power multiple chat experiences cleanly across different plugins and products.
|
|
10
|
+
|
|
11
|
+
This repo should focus on the **best chat experience possible** while leaving backend-specific concerns to adapters and consuming plugins.
|
|
12
|
+
|
|
13
|
+
## Why This Exists
|
|
14
|
+
|
|
15
|
+
There are already multiple chat block implementations across different repos, including:
|
|
16
|
+
|
|
17
|
+
- `Extra-Chill/extrachill-chat`
|
|
18
|
+
- `Sarai-Chinwag/spawn`
|
|
19
|
+
|
|
20
|
+
Those implementations overlap heavily in UI and runtime behavior, but differ in backend details like:
|
|
21
|
+
|
|
22
|
+
- authentication
|
|
23
|
+
- session management
|
|
24
|
+
- billing / credits
|
|
25
|
+
- agent transport
|
|
26
|
+
- provisioning / availability states
|
|
27
|
+
- branding
|
|
28
|
+
|
|
29
|
+
This repo exists to separate the **shared chat experience** from the **product-specific business logic**.
|
|
30
|
+
|
|
31
|
+
## Goals
|
|
32
|
+
|
|
33
|
+
- Build a reusable Gutenberg chat block
|
|
34
|
+
- Keep the block frontend-focused and presentation-first
|
|
35
|
+
- Support multiple backend implementations through adapters
|
|
36
|
+
- Standardize chat UX across Extra Chill projects
|
|
37
|
+
- Make it easy for WordPress plugins to become AI-native without rebuilding chat UI every time
|
|
38
|
+
|
|
39
|
+
## Non-Goals
|
|
40
|
+
|
|
41
|
+
- Owning backend AI orchestration
|
|
42
|
+
- Owning product-specific business logic
|
|
43
|
+
- Owning billing, provisioning, or auth policy
|
|
44
|
+
- Becoming a monolithic app plugin
|
|
45
|
+
|
|
46
|
+
## Proposed Architecture
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
WordPress Plugin
|
|
50
|
+
|
|
|
51
|
+
| server-rendered wrapper + config
|
|
52
|
+
v
|
|
53
|
+
Shared Chat Block UI
|
|
54
|
+
|
|
|
55
|
+
| adapter contract
|
|
56
|
+
v
|
|
57
|
+
Backend-specific implementation
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Core idea
|
|
61
|
+
|
|
62
|
+
The shared block should own:
|
|
63
|
+
|
|
64
|
+
- message list rendering
|
|
65
|
+
- composer UX
|
|
66
|
+
- loading / error / retry states
|
|
67
|
+
- scrolling behavior
|
|
68
|
+
- streaming display
|
|
69
|
+
- session list UI (when enabled)
|
|
70
|
+
- accessibility and keyboard interactions
|
|
71
|
+
- mobile and responsive behavior
|
|
72
|
+
|
|
73
|
+
The consuming plugin should own:
|
|
74
|
+
|
|
75
|
+
- REST endpoints
|
|
76
|
+
- authentication
|
|
77
|
+
- pricing / credit logic
|
|
78
|
+
- agent lifecycle
|
|
79
|
+
- account state
|
|
80
|
+
- server availability logic
|
|
81
|
+
- branding and product-specific rules
|
|
82
|
+
|
|
83
|
+
## Adapter Model
|
|
84
|
+
|
|
85
|
+
The main abstraction in this repo should be a narrow adapter contract.
|
|
86
|
+
|
|
87
|
+
Example shape:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
interface ChatAdapter {
|
|
91
|
+
capabilities: {
|
|
92
|
+
sessions: boolean
|
|
93
|
+
history: boolean
|
|
94
|
+
streaming: boolean
|
|
95
|
+
tools: boolean
|
|
96
|
+
availabilityStates: boolean
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
loadInitialState?(): Promise<InitialState>
|
|
100
|
+
listSessions?(): Promise<Session[]>
|
|
101
|
+
loadMessages?(sessionId: string): Promise<Message[]>
|
|
102
|
+
createSession?(): Promise<Session>
|
|
103
|
+
sendMessage(input: SendMessageInput): Promise<SendMessageResult>
|
|
104
|
+
clearSession?(sessionId: string): Promise<void>
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
This keeps the UI generic while allowing:
|
|
109
|
+
|
|
110
|
+
- `extrachill-chat` to use WordPress REST endpoints
|
|
111
|
+
- `spawn` to use its own gateway / session model
|
|
112
|
+
- future implementations to plug in without forking the UI
|
|
113
|
+
|
|
114
|
+
## Initial Priorities
|
|
115
|
+
|
|
116
|
+
1. Define the adapter API and normalized state model
|
|
117
|
+
2. Build a minimal frontend-only chat block shell
|
|
118
|
+
3. Support a basic message flow end-to-end
|
|
119
|
+
4. Add optional session sidebar support
|
|
120
|
+
5. Port one real consumer first (`extrachill-chat` is likely the easiest)
|
|
121
|
+
6. Port `spawn` after the core boundaries are proven
|
|
122
|
+
|
|
123
|
+
## Migration Strategy
|
|
124
|
+
|
|
125
|
+
### Phase 1
|
|
126
|
+
- create core shared block
|
|
127
|
+
- define types and adapter interfaces
|
|
128
|
+
- implement minimal mount + send + render loop
|
|
129
|
+
|
|
130
|
+
### Phase 2
|
|
131
|
+
- adapt `extrachill-chat` to use shared UI
|
|
132
|
+
- validate API shape and message model
|
|
133
|
+
|
|
134
|
+
### Phase 3
|
|
135
|
+
- adapt `spawn`
|
|
136
|
+
- support richer session and availability states
|
|
137
|
+
|
|
138
|
+
### Phase 4
|
|
139
|
+
- refine styling, streaming, markdown, and extensibility
|
|
140
|
+
|
|
141
|
+
## Repo Status
|
|
142
|
+
|
|
143
|
+
This repo is currently in the **concept / planning** stage.
|
|
144
|
+
|
|
145
|
+
Implementation should start only after the core abstractions are clear enough that we do not just move duplication into a new place.
|
|
146
|
+
|
|
147
|
+
## Roadmap
|
|
148
|
+
|
|
149
|
+
See GitHub issues for the initial roadmap.
|
|
150
|
+
|
|
151
|
+
## Related Repositories
|
|
152
|
+
|
|
153
|
+
- https://github.com/Extra-Chill/extrachill-chat
|
|
154
|
+
- https://github.com/Sarai-Chinwag/spawn
|
package/css/chat.css
ADDED
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @extrachill/chat — Base styles
|
|
3
|
+
*
|
|
4
|
+
* These styles use CSS custom properties so consumers can theme
|
|
5
|
+
* the chat without overriding selectors. Override these variables
|
|
6
|
+
* at any level to customize appearance.
|
|
7
|
+
*
|
|
8
|
+
* All classes use the `ec-chat` prefix to avoid collisions.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/* ============================================
|
|
12
|
+
CSS Custom Properties (Theme API)
|
|
13
|
+
============================================ */
|
|
14
|
+
|
|
15
|
+
.ec-chat {
|
|
16
|
+
--ec-chat-font-family: inherit;
|
|
17
|
+
--ec-chat-font-size: 14px;
|
|
18
|
+
--ec-chat-line-height: 1.5;
|
|
19
|
+
|
|
20
|
+
/* Layout */
|
|
21
|
+
--ec-chat-max-width: 100%;
|
|
22
|
+
--ec-chat-padding: 16px;
|
|
23
|
+
--ec-chat-gap: 8px;
|
|
24
|
+
--ec-chat-border-radius: 12px;
|
|
25
|
+
|
|
26
|
+
/* Colors — light defaults */
|
|
27
|
+
--ec-chat-bg: #ffffff;
|
|
28
|
+
--ec-chat-text: #1a1a1a;
|
|
29
|
+
--ec-chat-text-muted: #6b7280;
|
|
30
|
+
--ec-chat-border: #e5e7eb;
|
|
31
|
+
|
|
32
|
+
/* User bubble */
|
|
33
|
+
--ec-chat-user-bg: #2563eb;
|
|
34
|
+
--ec-chat-user-text: #ffffff;
|
|
35
|
+
|
|
36
|
+
/* Assistant bubble */
|
|
37
|
+
--ec-chat-assistant-bg: #f3f4f6;
|
|
38
|
+
--ec-chat-assistant-text: #1a1a1a;
|
|
39
|
+
|
|
40
|
+
/* Input */
|
|
41
|
+
--ec-chat-input-bg: #ffffff;
|
|
42
|
+
--ec-chat-input-border: #d1d5db;
|
|
43
|
+
--ec-chat-input-focus-border: #2563eb;
|
|
44
|
+
|
|
45
|
+
/* Send button */
|
|
46
|
+
--ec-chat-send-bg: #2563eb;
|
|
47
|
+
--ec-chat-send-text: #ffffff;
|
|
48
|
+
--ec-chat-send-disabled-opacity: 0.4;
|
|
49
|
+
|
|
50
|
+
/* Tool messages */
|
|
51
|
+
--ec-chat-tool-bg: #f9fafb;
|
|
52
|
+
--ec-chat-tool-border: #e5e7eb;
|
|
53
|
+
--ec-chat-tool-success: #10b981;
|
|
54
|
+
--ec-chat-tool-error: #ef4444;
|
|
55
|
+
--ec-chat-tool-pending: #6b7280;
|
|
56
|
+
|
|
57
|
+
/* Sessions */
|
|
58
|
+
--ec-chat-session-active-bg: #eff6ff;
|
|
59
|
+
--ec-chat-session-hover-bg: #f9fafb;
|
|
60
|
+
|
|
61
|
+
/* Typing indicator */
|
|
62
|
+
--ec-chat-typing-dot: #9ca3af;
|
|
63
|
+
|
|
64
|
+
/* Error */
|
|
65
|
+
--ec-chat-error-bg: #fef2f2;
|
|
66
|
+
--ec-chat-error-text: #991b1b;
|
|
67
|
+
|
|
68
|
+
/* Availability */
|
|
69
|
+
--ec-chat-availability-bg: #f9fafb;
|
|
70
|
+
--ec-chat-availability-text: #374151;
|
|
71
|
+
|
|
72
|
+
font-family: var(--ec-chat-font-family);
|
|
73
|
+
font-size: var(--ec-chat-font-size);
|
|
74
|
+
line-height: var(--ec-chat-line-height);
|
|
75
|
+
color: var(--ec-chat-text);
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-direction: column;
|
|
78
|
+
max-width: var(--ec-chat-max-width);
|
|
79
|
+
height: 100%;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* ============================================
|
|
83
|
+
Chat Messages Container
|
|
84
|
+
============================================ */
|
|
85
|
+
|
|
86
|
+
.ec-chat-messages {
|
|
87
|
+
flex: 1;
|
|
88
|
+
overflow-y: auto;
|
|
89
|
+
padding: var(--ec-chat-padding);
|
|
90
|
+
display: flex;
|
|
91
|
+
flex-direction: column;
|
|
92
|
+
gap: var(--ec-chat-gap);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.ec-chat-messages__empty {
|
|
96
|
+
flex: 1;
|
|
97
|
+
display: flex;
|
|
98
|
+
align-items: center;
|
|
99
|
+
justify-content: center;
|
|
100
|
+
color: var(--ec-chat-text-muted);
|
|
101
|
+
text-align: center;
|
|
102
|
+
padding: 32px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* ============================================
|
|
106
|
+
Individual Message
|
|
107
|
+
============================================ */
|
|
108
|
+
|
|
109
|
+
.ec-chat-message {
|
|
110
|
+
display: flex;
|
|
111
|
+
flex-direction: column;
|
|
112
|
+
max-width: 80%;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.ec-chat-message--user {
|
|
116
|
+
align-self: flex-end;
|
|
117
|
+
align-items: flex-end;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.ec-chat-message--assistant {
|
|
121
|
+
align-self: flex-start;
|
|
122
|
+
align-items: flex-start;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.ec-chat-message__bubble {
|
|
126
|
+
padding: 10px 14px;
|
|
127
|
+
border-radius: var(--ec-chat-border-radius);
|
|
128
|
+
word-wrap: break-word;
|
|
129
|
+
overflow-wrap: break-word;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.ec-chat-message--user .ec-chat-message__bubble {
|
|
133
|
+
background: var(--ec-chat-user-bg);
|
|
134
|
+
color: var(--ec-chat-user-text);
|
|
135
|
+
border-bottom-right-radius: 4px;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.ec-chat-message--assistant .ec-chat-message__bubble {
|
|
139
|
+
background: var(--ec-chat-assistant-bg);
|
|
140
|
+
color: var(--ec-chat-assistant-text);
|
|
141
|
+
border-bottom-left-radius: 4px;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.ec-chat-message__bubble p {
|
|
145
|
+
margin: 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.ec-chat-message__bubble p + p {
|
|
149
|
+
margin-top: 8px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.ec-chat-message__timestamp {
|
|
153
|
+
font-size: 11px;
|
|
154
|
+
color: var(--ec-chat-text-muted);
|
|
155
|
+
margin-top: 2px;
|
|
156
|
+
opacity: 0;
|
|
157
|
+
transition: opacity 0.15s;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.ec-chat-message:hover .ec-chat-message__timestamp {
|
|
161
|
+
opacity: 1;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* ============================================
|
|
165
|
+
Chat Input
|
|
166
|
+
============================================ */
|
|
167
|
+
|
|
168
|
+
.ec-chat-input {
|
|
169
|
+
display: flex;
|
|
170
|
+
align-items: flex-end;
|
|
171
|
+
gap: 8px;
|
|
172
|
+
padding: var(--ec-chat-padding);
|
|
173
|
+
border-top: 1px solid var(--ec-chat-border);
|
|
174
|
+
background: var(--ec-chat-input-bg);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.ec-chat-input__textarea {
|
|
178
|
+
flex: 1;
|
|
179
|
+
resize: none;
|
|
180
|
+
border: 1px solid var(--ec-chat-input-border);
|
|
181
|
+
border-radius: var(--ec-chat-border-radius);
|
|
182
|
+
padding: 10px 14px;
|
|
183
|
+
font-family: inherit;
|
|
184
|
+
font-size: inherit;
|
|
185
|
+
line-height: inherit;
|
|
186
|
+
background: transparent;
|
|
187
|
+
color: inherit;
|
|
188
|
+
outline: none;
|
|
189
|
+
min-height: 40px;
|
|
190
|
+
max-height: 160px;
|
|
191
|
+
transition: border-color 0.15s;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.ec-chat-input__textarea:focus {
|
|
195
|
+
border-color: var(--ec-chat-input-focus-border);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.ec-chat-input__textarea::placeholder {
|
|
199
|
+
color: var(--ec-chat-text-muted);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.ec-chat-input__send {
|
|
203
|
+
display: flex;
|
|
204
|
+
align-items: center;
|
|
205
|
+
justify-content: center;
|
|
206
|
+
width: 40px;
|
|
207
|
+
height: 40px;
|
|
208
|
+
border: none;
|
|
209
|
+
border-radius: 50%;
|
|
210
|
+
background: var(--ec-chat-send-bg);
|
|
211
|
+
color: var(--ec-chat-send-text);
|
|
212
|
+
cursor: pointer;
|
|
213
|
+
flex-shrink: 0;
|
|
214
|
+
transition: opacity 0.15s;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.ec-chat-input__send:disabled {
|
|
218
|
+
opacity: var(--ec-chat-send-disabled-opacity);
|
|
219
|
+
cursor: not-allowed;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.ec-chat-input__send:not(:disabled):hover {
|
|
223
|
+
opacity: 0.9;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.ec-chat-input__send-icon {
|
|
227
|
+
display: block;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* ============================================
|
|
231
|
+
Typing Indicator
|
|
232
|
+
============================================ */
|
|
233
|
+
|
|
234
|
+
.ec-chat-typing {
|
|
235
|
+
display: flex;
|
|
236
|
+
align-items: center;
|
|
237
|
+
gap: 8px;
|
|
238
|
+
padding: 4px var(--ec-chat-padding);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.ec-chat-typing__dots {
|
|
242
|
+
display: flex;
|
|
243
|
+
align-items: center;
|
|
244
|
+
gap: 4px;
|
|
245
|
+
padding: 10px 14px;
|
|
246
|
+
background: var(--ec-chat-assistant-bg);
|
|
247
|
+
border-radius: var(--ec-chat-border-radius);
|
|
248
|
+
border-bottom-left-radius: 4px;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.ec-chat-typing__dot {
|
|
252
|
+
width: 6px;
|
|
253
|
+
height: 6px;
|
|
254
|
+
border-radius: 50%;
|
|
255
|
+
background: var(--ec-chat-typing-dot);
|
|
256
|
+
animation: ec-chat-typing-bounce 1.4s infinite ease-in-out both;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.ec-chat-typing__dot:nth-child(1) { animation-delay: -0.32s; }
|
|
260
|
+
.ec-chat-typing__dot:nth-child(2) { animation-delay: -0.16s; }
|
|
261
|
+
.ec-chat-typing__dot:nth-child(3) { animation-delay: 0s; }
|
|
262
|
+
|
|
263
|
+
@keyframes ec-chat-typing-bounce {
|
|
264
|
+
0%, 80%, 100% {
|
|
265
|
+
transform: scale(0.6);
|
|
266
|
+
opacity: 0.4;
|
|
267
|
+
}
|
|
268
|
+
40% {
|
|
269
|
+
transform: scale(1);
|
|
270
|
+
opacity: 1;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.ec-chat-typing__label {
|
|
275
|
+
font-size: 12px;
|
|
276
|
+
color: var(--ec-chat-text-muted);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/* ============================================
|
|
280
|
+
Tool Message
|
|
281
|
+
============================================ */
|
|
282
|
+
|
|
283
|
+
.ec-chat-tool {
|
|
284
|
+
margin: 4px var(--ec-chat-padding);
|
|
285
|
+
border: 1px solid var(--ec-chat-tool-border);
|
|
286
|
+
border-radius: 8px;
|
|
287
|
+
background: var(--ec-chat-tool-bg);
|
|
288
|
+
overflow: hidden;
|
|
289
|
+
font-size: 13px;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.ec-chat-tool__header {
|
|
293
|
+
display: flex;
|
|
294
|
+
align-items: center;
|
|
295
|
+
gap: 8px;
|
|
296
|
+
width: 100%;
|
|
297
|
+
padding: 8px 12px;
|
|
298
|
+
border: none;
|
|
299
|
+
background: transparent;
|
|
300
|
+
cursor: pointer;
|
|
301
|
+
font-family: inherit;
|
|
302
|
+
font-size: inherit;
|
|
303
|
+
color: inherit;
|
|
304
|
+
text-align: left;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.ec-chat-tool__header:hover {
|
|
308
|
+
background: var(--ec-chat-session-hover-bg);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.ec-chat-tool__icon {
|
|
312
|
+
flex-shrink: 0;
|
|
313
|
+
font-size: 14px;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.ec-chat-tool--success .ec-chat-tool__icon { color: var(--ec-chat-tool-success); }
|
|
317
|
+
.ec-chat-tool--error .ec-chat-tool__icon { color: var(--ec-chat-tool-error); }
|
|
318
|
+
.ec-chat-tool--pending .ec-chat-tool__icon { color: var(--ec-chat-tool-pending); }
|
|
319
|
+
|
|
320
|
+
.ec-chat-tool__name {
|
|
321
|
+
flex: 1;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.ec-chat-tool__chevron {
|
|
325
|
+
flex-shrink: 0;
|
|
326
|
+
font-size: 10px;
|
|
327
|
+
color: var(--ec-chat-text-muted);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.ec-chat-tool__details {
|
|
331
|
+
padding: 0 12px 12px;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.ec-chat-tool__section {
|
|
335
|
+
margin-top: 8px;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.ec-chat-tool__section-label {
|
|
339
|
+
font-size: 11px;
|
|
340
|
+
font-weight: 600;
|
|
341
|
+
text-transform: uppercase;
|
|
342
|
+
letter-spacing: 0.05em;
|
|
343
|
+
color: var(--ec-chat-text-muted);
|
|
344
|
+
margin-bottom: 4px;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.ec-chat-tool__json {
|
|
348
|
+
margin: 0;
|
|
349
|
+
padding: 8px;
|
|
350
|
+
background: var(--ec-chat-bg);
|
|
351
|
+
border: 1px solid var(--ec-chat-tool-border);
|
|
352
|
+
border-radius: 4px;
|
|
353
|
+
font-family: monospace;
|
|
354
|
+
font-size: 12px;
|
|
355
|
+
white-space: pre-wrap;
|
|
356
|
+
word-break: break-all;
|
|
357
|
+
overflow-x: auto;
|
|
358
|
+
max-height: 200px;
|
|
359
|
+
overflow-y: auto;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/* ============================================
|
|
363
|
+
Session Switcher
|
|
364
|
+
============================================ */
|
|
365
|
+
|
|
366
|
+
.ec-chat-sessions {
|
|
367
|
+
border-bottom: 1px solid var(--ec-chat-border);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.ec-chat-sessions__header {
|
|
371
|
+
display: flex;
|
|
372
|
+
align-items: center;
|
|
373
|
+
justify-content: space-between;
|
|
374
|
+
padding: 8px var(--ec-chat-padding);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.ec-chat-sessions__title {
|
|
378
|
+
font-size: 12px;
|
|
379
|
+
font-weight: 600;
|
|
380
|
+
text-transform: uppercase;
|
|
381
|
+
letter-spacing: 0.05em;
|
|
382
|
+
color: var(--ec-chat-text-muted);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.ec-chat-sessions__new {
|
|
386
|
+
display: flex;
|
|
387
|
+
align-items: center;
|
|
388
|
+
justify-content: center;
|
|
389
|
+
width: 24px;
|
|
390
|
+
height: 24px;
|
|
391
|
+
border: 1px solid var(--ec-chat-border);
|
|
392
|
+
border-radius: 4px;
|
|
393
|
+
background: transparent;
|
|
394
|
+
color: var(--ec-chat-text-muted);
|
|
395
|
+
cursor: pointer;
|
|
396
|
+
font-size: 16px;
|
|
397
|
+
line-height: 1;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.ec-chat-sessions__new:hover {
|
|
401
|
+
background: var(--ec-chat-session-hover-bg);
|
|
402
|
+
color: var(--ec-chat-text);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.ec-chat-sessions__loading {
|
|
406
|
+
padding: 8px var(--ec-chat-padding);
|
|
407
|
+
color: var(--ec-chat-text-muted);
|
|
408
|
+
font-size: 13px;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.ec-chat-sessions__list {
|
|
412
|
+
list-style: none;
|
|
413
|
+
margin: 0;
|
|
414
|
+
padding: 0;
|
|
415
|
+
max-height: 200px;
|
|
416
|
+
overflow-y: auto;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.ec-chat-sessions__item {
|
|
420
|
+
display: flex;
|
|
421
|
+
align-items: center;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.ec-chat-sessions__item--active {
|
|
425
|
+
background: var(--ec-chat-session-active-bg);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.ec-chat-sessions__item-button {
|
|
429
|
+
flex: 1;
|
|
430
|
+
display: flex;
|
|
431
|
+
flex-direction: column;
|
|
432
|
+
gap: 2px;
|
|
433
|
+
padding: 8px var(--ec-chat-padding);
|
|
434
|
+
border: none;
|
|
435
|
+
background: transparent;
|
|
436
|
+
cursor: pointer;
|
|
437
|
+
text-align: left;
|
|
438
|
+
font-family: inherit;
|
|
439
|
+
color: inherit;
|
|
440
|
+
min-width: 0;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.ec-chat-sessions__item-button:hover {
|
|
444
|
+
background: var(--ec-chat-session-hover-bg);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.ec-chat-sessions__item-title {
|
|
448
|
+
font-size: 13px;
|
|
449
|
+
white-space: nowrap;
|
|
450
|
+
overflow: hidden;
|
|
451
|
+
text-overflow: ellipsis;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.ec-chat-sessions__item-date {
|
|
455
|
+
font-size: 11px;
|
|
456
|
+
color: var(--ec-chat-text-muted);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.ec-chat-sessions__item-delete {
|
|
460
|
+
padding: 4px 8px;
|
|
461
|
+
border: none;
|
|
462
|
+
background: transparent;
|
|
463
|
+
color: var(--ec-chat-text-muted);
|
|
464
|
+
cursor: pointer;
|
|
465
|
+
font-size: 16px;
|
|
466
|
+
line-height: 1;
|
|
467
|
+
opacity: 0;
|
|
468
|
+
transition: opacity 0.15s;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.ec-chat-sessions__item:hover .ec-chat-sessions__item-delete {
|
|
472
|
+
opacity: 1;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.ec-chat-sessions__item-delete:hover {
|
|
476
|
+
color: var(--ec-chat-tool-error);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/* ============================================
|
|
480
|
+
Error Boundary
|
|
481
|
+
============================================ */
|
|
482
|
+
|
|
483
|
+
.ec-chat-error {
|
|
484
|
+
display: flex;
|
|
485
|
+
flex-direction: column;
|
|
486
|
+
align-items: center;
|
|
487
|
+
justify-content: center;
|
|
488
|
+
gap: 12px;
|
|
489
|
+
padding: 32px;
|
|
490
|
+
text-align: center;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.ec-chat-error__message {
|
|
494
|
+
color: var(--ec-chat-error-text);
|
|
495
|
+
margin: 0;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.ec-chat-error__retry {
|
|
499
|
+
padding: 8px 16px;
|
|
500
|
+
border: 1px solid var(--ec-chat-border);
|
|
501
|
+
border-radius: 6px;
|
|
502
|
+
background: transparent;
|
|
503
|
+
color: var(--ec-chat-text);
|
|
504
|
+
cursor: pointer;
|
|
505
|
+
font-family: inherit;
|
|
506
|
+
font-size: inherit;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.ec-chat-error__retry:hover {
|
|
510
|
+
background: var(--ec-chat-session-hover-bg);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/* ============================================
|
|
514
|
+
Availability Gate
|
|
515
|
+
============================================ */
|
|
516
|
+
|
|
517
|
+
.ec-chat-availability {
|
|
518
|
+
display: flex;
|
|
519
|
+
flex-direction: column;
|
|
520
|
+
align-items: center;
|
|
521
|
+
justify-content: center;
|
|
522
|
+
gap: 12px;
|
|
523
|
+
padding: 32px;
|
|
524
|
+
text-align: center;
|
|
525
|
+
background: var(--ec-chat-availability-bg);
|
|
526
|
+
color: var(--ec-chat-availability-text);
|
|
527
|
+
border-radius: var(--ec-chat-border-radius);
|
|
528
|
+
margin: var(--ec-chat-padding);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.ec-chat-availability p {
|
|
532
|
+
margin: 0;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.ec-chat-availability__action {
|
|
536
|
+
display: inline-block;
|
|
537
|
+
padding: 8px 16px;
|
|
538
|
+
border: 1px solid var(--ec-chat-border);
|
|
539
|
+
border-radius: 6px;
|
|
540
|
+
color: var(--ec-chat-send-bg);
|
|
541
|
+
text-decoration: none;
|
|
542
|
+
font-weight: 500;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.ec-chat-availability__action:hover {
|
|
546
|
+
background: var(--ec-chat-session-hover-bg);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.ec-chat-availability--error {
|
|
550
|
+
background: var(--ec-chat-error-bg);
|
|
551
|
+
color: var(--ec-chat-error-text);
|
|
552
|
+
}
|