@social.io/catalog 1.3.0 → 2.1.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/.smartconfig.json +51 -0
- package/dist_bundle/bundle.js +5455 -3248
- package/dist_ts_web/00_commitinfo_data.js +3 -3
- package/dist_ts_web/elements/index.d.ts +21 -3
- package/dist_ts_web/elements/index.js +28 -7
- package/dist_ts_web/elements/sio-ai-card.d.ts +39 -0
- package/dist_ts_web/elements/sio-ai-card.js +229 -0
- package/dist_ts_web/elements/sio-avatar.d.ts +46 -0
- package/dist_ts_web/elements/sio-avatar.js +379 -0
- package/dist_ts_web/elements/sio-bucket-header.d.ts +36 -0
- package/dist_ts_web/elements/sio-bucket-header.js +197 -0
- package/dist_ts_web/elements/sio-button.d.ts +7 -3
- package/dist_ts_web/elements/sio-button.js +253 -152
- package/dist_ts_web/elements/sio-channel-rail.d.ts +38 -0
- package/dist_ts_web/elements/sio-channel-rail.js +288 -0
- package/dist_ts_web/elements/sio-chip.d.ts +25 -0
- package/dist_ts_web/elements/sio-chip.js +188 -0
- package/dist_ts_web/elements/sio-cmdk.d.ts +59 -0
- package/dist_ts_web/elements/sio-cmdk.js +471 -0
- package/dist_ts_web/elements/sio-combox.d.ts +55 -1
- package/dist_ts_web/elements/sio-combox.js +458 -261
- package/dist_ts_web/elements/sio-composer.d.ts +48 -0
- package/dist_ts_web/elements/sio-composer.js +357 -0
- package/dist_ts_web/elements/sio-conversation-selector.d.ts +41 -1
- package/dist_ts_web/elements/sio-conversation-selector.js +426 -238
- package/dist_ts_web/elements/sio-conversation-view.d.ts +34 -6
- package/dist_ts_web/elements/sio-conversation-view.js +738 -564
- package/dist_ts_web/elements/sio-dropdown-menu.d.ts +6 -6
- package/dist_ts_web/elements/sio-dropdown-menu.js +223 -169
- package/dist_ts_web/elements/sio-fab.d.ts +23 -5
- package/dist_ts_web/elements/sio-fab.js +259 -235
- package/dist_ts_web/elements/sio-glass-toolbar.d.ts +22 -0
- package/dist_ts_web/elements/sio-glass-toolbar.js +178 -0
- package/dist_ts_web/elements/sio-icon.d.ts +1 -0
- package/dist_ts_web/elements/sio-icon.js +254 -94
- package/dist_ts_web/elements/sio-image-lightbox.d.ts +3 -4
- package/dist_ts_web/elements/sio-image-lightbox.js +282 -191
- package/dist_ts_web/elements/sio-inset-list.d.ts +28 -0
- package/dist_ts_web/elements/sio-inset-list.js +191 -0
- package/dist_ts_web/elements/sio-kbd.d.ts +16 -0
- package/dist_ts_web/elements/sio-kbd.js +117 -0
- package/dist_ts_web/elements/sio-list-row.d.ts +55 -0
- package/dist_ts_web/elements/sio-list-row.js +462 -0
- package/dist_ts_web/elements/sio-logo-render.d.ts +42 -0
- package/dist_ts_web/elements/sio-logo-render.js +306 -0
- package/dist_ts_web/elements/sio-logo.d.ts +64 -0
- package/dist_ts_web/elements/sio-logo.js +393 -0
- package/dist_ts_web/elements/sio-message-input.d.ts +25 -4
- package/dist_ts_web/elements/sio-message-input.js +231 -226
- package/dist_ts_web/elements/sio-paper-thumb.d.ts +24 -0
- package/dist_ts_web/elements/sio-paper-thumb.js +293 -0
- package/dist_ts_web/elements/sio-pdf-viewer.d.ts +2 -0
- package/dist_ts_web/elements/sio-pdf-viewer.js +344 -244
- package/dist_ts_web/elements/sio-segmented.d.ts +32 -0
- package/dist_ts_web/elements/sio-segmented.js +217 -0
- package/dist_ts_web/elements/sio-step-timeline.d.ts +28 -0
- package/dist_ts_web/elements/sio-step-timeline.js +222 -0
- package/dist_ts_web/elements/sio-tab-bar.d.ts +36 -0
- package/dist_ts_web/elements/sio-tab-bar.js +232 -0
- package/dist_ts_web/elements/sio-unread-badge.d.ts +25 -0
- package/dist_ts_web/elements/sio-unread-badge.js +203 -0
- package/dist_ts_web/elements/sio-waveform.d.ts +34 -0
- package/dist_ts_web/elements/sio-waveform.js +304 -0
- package/dist_ts_web/elements/tokens.d.ts +70 -0
- package/dist_ts_web/elements/tokens.js +277 -0
- package/dist_ts_web/pages/firstpage.d.ts +5 -0
- package/dist_ts_web/pages/firstpage.js +21 -2
- package/dist_ts_web/pages/mainpage.d.ts +6 -0
- package/dist_ts_web/pages/mainpage.js +111 -39
- package/{license → license.md} +3 -1
- package/package.json +23 -29
- package/readme.hints.md +82 -0
- package/readme.md +113 -146
- package/ts_web/00_commitinfo_data.ts +2 -2
- package/ts_web/elements/index.ts +31 -7
- package/ts_web/elements/sio-ai-card.ts +175 -0
- package/ts_web/elements/sio-avatar.ts +295 -0
- package/ts_web/elements/sio-bucket-header.ts +157 -0
- package/ts_web/elements/sio-button.ts +149 -127
- package/ts_web/elements/sio-channel-rail.ts +256 -0
- package/ts_web/elements/sio-chip.ts +134 -0
- package/ts_web/elements/sio-cmdk.ts +461 -0
- package/ts_web/elements/sio-combox.ts +327 -204
- package/ts_web/elements/sio-composer.ts +306 -0
- package/ts_web/elements/sio-conversation-selector.ts +350 -228
- package/ts_web/elements/sio-conversation-view.ts +573 -457
- package/ts_web/elements/sio-dropdown-menu.ts +112 -130
- package/ts_web/elements/sio-fab.ts +192 -227
- package/ts_web/elements/sio-glass-toolbar.ts +140 -0
- package/ts_web/elements/sio-icon.ts +147 -25
- package/ts_web/elements/sio-image-lightbox.ts +62 -64
- package/ts_web/elements/sio-inset-list.ts +140 -0
- package/ts_web/elements/sio-kbd.ts +80 -0
- package/ts_web/elements/sio-list-row.ts +393 -0
- package/ts_web/elements/sio-logo-render.ts +367 -0
- package/ts_web/elements/sio-logo.ts +311 -0
- package/ts_web/elements/sio-message-input.ts +142 -221
- package/ts_web/elements/sio-paper-thumb.ts +244 -0
- package/ts_web/elements/sio-pdf-viewer.ts +58 -60
- package/ts_web/elements/sio-segmented.ts +183 -0
- package/ts_web/elements/sio-step-timeline.ts +179 -0
- package/ts_web/elements/sio-tab-bar.ts +195 -0
- package/ts_web/elements/sio-unread-badge.ts +147 -0
- package/ts_web/elements/sio-waveform.ts +239 -0
- package/ts_web/elements/tokens.ts +297 -0
- package/ts_web/pages/firstpage.ts +20 -1
- package/ts_web/pages/mainpage.ts +111 -39
- package/dist_bundle/bundle.js.map +0 -7
- package/dist_ts_web/elements/00colors.d.ts +0 -108
- package/dist_ts_web/elements/00colors.js +0 -137
- package/dist_ts_web/elements/00fonts.d.ts +0 -60
- package/dist_ts_web/elements/00fonts.js +0 -129
- package/dist_ts_web/elements/00tokens.d.ts +0 -123
- package/dist_ts_web/elements/00tokens.js +0 -176
- package/dist_ts_web/elements/sio-conversation-list.d.ts +0 -20
- package/dist_ts_web/elements/sio-conversation-list.js +0 -495
- package/dist_ts_web/elements/sio-recorder.d.ts +0 -60
- package/dist_ts_web/elements/sio-recorder.js +0 -173
- package/dist_ts_web/elements/sio-subwidget-conversations.d.ts +0 -16
- package/dist_ts_web/elements/sio-subwidget-conversations.js +0 -350
- package/dist_ts_web/elements/sio-subwidget-onboardme.d.ts +0 -7
- package/dist_ts_web/elements/sio-subwidget-onboardme.js +0 -243
- package/dist_watch/bundle.js +0 -81694
- package/dist_watch/bundle.js.map +0 -7
- package/dist_watch/index.html +0 -25
- package/npmextra.json +0 -19
- package/ts_web/elements/00colors.ts +0 -164
- package/ts_web/elements/00fonts.ts +0 -145
- package/ts_web/elements/00tokens.ts +0 -197
- package/ts_web/elements/sio-recorder.ts +0 -213
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DeesElement,
|
|
3
|
-
property,
|
|
4
3
|
html,
|
|
4
|
+
property,
|
|
5
|
+
state,
|
|
5
6
|
customElement,
|
|
6
|
-
type TemplateResult,
|
|
7
|
-
cssManager,
|
|
8
7
|
css,
|
|
9
|
-
|
|
10
|
-
state,
|
|
8
|
+
type TemplateResult,
|
|
11
9
|
} from '@design.estate/dees-element';
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
import { colors, bdTheme } from './00colors.js';
|
|
15
|
-
import { spacing, radius, shadows, transitions } from './00tokens.js';
|
|
16
|
-
import { fontFamilies, typography } from './00fonts.js';
|
|
11
|
+
import { sioElementStyles, sioToolbarGlass } from './tokens.js';
|
|
17
12
|
import { SioDropdownMenu, type IDropdownMenuItem } from './sio-dropdown-menu.js';
|
|
18
13
|
import { SioMessageInput } from './sio-message-input.js';
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
import './sio-icon.js';
|
|
15
|
+
import './sio-avatar.js';
|
|
21
16
|
SioDropdownMenu;
|
|
22
17
|
SioMessageInput;
|
|
23
18
|
|
|
@@ -44,6 +39,12 @@ export interface IConversationData {
|
|
|
44
39
|
id: string;
|
|
45
40
|
title: string;
|
|
46
41
|
messages: IMessage[];
|
|
42
|
+
/** agent initials for the header + bubble avatars (falls back to title initials) */
|
|
43
|
+
agentInitials?: string;
|
|
44
|
+
/** agent accent color (data-driven) */
|
|
45
|
+
agentTint?: string;
|
|
46
|
+
/** centered date stamp above the thread, e.g. "Today, 14:02" */
|
|
47
|
+
dateStamp?: string;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
declare global {
|
|
@@ -52,245 +53,359 @@ declare global {
|
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
const demoConversation: IConversationData = {
|
|
57
|
+
id: 'orbit-launch',
|
|
58
|
+
title: 'Lena · Orbit',
|
|
59
|
+
agentInitials: 'LP',
|
|
60
|
+
agentTint: '#FF2D55',
|
|
61
|
+
dateStamp: 'Today, 14:02',
|
|
62
|
+
messages: [
|
|
63
|
+
{
|
|
64
|
+
id: '1',
|
|
65
|
+
text: 'Hey! 👋 You wrote about the launch banner — what would you like to change?',
|
|
66
|
+
sender: 'support',
|
|
67
|
+
time: '14:02',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: '2',
|
|
71
|
+
text: 'The headline is cut off on tablet. Can we shorten it before Thursday?',
|
|
72
|
+
sender: 'user',
|
|
73
|
+
time: '14:03',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: '3',
|
|
77
|
+
text: "Good catch. We can ship the short variant today — I'll send a preview here in ~10 min.",
|
|
78
|
+
sender: 'support',
|
|
79
|
+
time: '14:04',
|
|
80
|
+
},
|
|
81
|
+
{ id: '4', text: 'Perfect, thanks!', sender: 'user', time: '14:04' },
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* sio-conversation-view — the widget's conversation state
|
|
87
|
+
* (v7 SocialIOWidget.jsx SIOWidgetChat).
|
|
88
|
+
*
|
|
89
|
+
* Glass header (back chevron in tint, 30px agent avatar, name 14/600,
|
|
90
|
+
* status line green "Online now" / orange "Away · replies by email",
|
|
91
|
+
* overflow dots), centered 11px date stamp, bubbles at max-width 76%
|
|
92
|
+
* (mine: solid tint right; theirs: grouped fill left with 24px avatar),
|
|
93
|
+
* typing indicator, the orange mail-fallback notice when away, and the
|
|
94
|
+
* composer via sio-message-input (email capture while away).
|
|
95
|
+
*
|
|
96
|
+
* Events (bubbles, composed):
|
|
97
|
+
* back — back chevron
|
|
98
|
+
* send-message { message } — user sent a message
|
|
99
|
+
* email-submitted { email } — away email capture
|
|
100
|
+
* open-image / open-file { attachment } — attachment click
|
|
101
|
+
* conversation-action { action, conversationId } — overflow menu
|
|
102
|
+
*/
|
|
55
103
|
@customElement('sio-conversation-view')
|
|
56
104
|
export class SioConversationView extends DeesElement {
|
|
57
105
|
public static demo = () => html`
|
|
58
|
-
<
|
|
106
|
+
<div style="display: flex; gap: 24px; align-items: flex-start; flex-wrap: wrap;">
|
|
107
|
+
<div
|
|
108
|
+
style="width: 380px; height: 620px; border-radius: 20px; overflow: hidden; background: var(--sio-surface); box-shadow: 0 24px 64px rgba(0, 0, 0, 0.22), 0 0 0 0.5px rgba(0, 0, 0, 0.08);"
|
|
109
|
+
>
|
|
110
|
+
<sio-conversation-view .conversation=${demoConversation} typing></sio-conversation-view>
|
|
111
|
+
</div>
|
|
112
|
+
<div
|
|
113
|
+
style="width: 380px; height: 620px; border-radius: 20px; overflow: hidden; background: var(--sio-surface); box-shadow: 0 24px 64px rgba(0, 0, 0, 0.22), 0 0 0 0.5px rgba(0, 0, 0, 0.08);"
|
|
114
|
+
>
|
|
115
|
+
<sio-conversation-view
|
|
116
|
+
away
|
|
117
|
+
.conversation=${{
|
|
118
|
+
...demoConversation,
|
|
119
|
+
dateStamp: 'Today, 18:36',
|
|
120
|
+
messages: [
|
|
121
|
+
{
|
|
122
|
+
id: '1',
|
|
123
|
+
text: 'Hi — my invoice from March still shows the old VAT ID. Can you re-issue it?',
|
|
124
|
+
sender: 'user',
|
|
125
|
+
time: '18:36',
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
} satisfies IConversationData}
|
|
129
|
+
></sio-conversation-view>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
59
132
|
`;
|
|
60
133
|
|
|
61
134
|
@property({ type: Object })
|
|
62
135
|
public accessor conversation: IConversationData | null = null;
|
|
63
136
|
|
|
64
|
-
|
|
65
|
-
|
|
137
|
+
/** team is away — orange status, mail-fallback notice, email composer */
|
|
138
|
+
@property({ type: Boolean, reflect: true })
|
|
139
|
+
public accessor away = false;
|
|
66
140
|
|
|
67
|
-
|
|
68
|
-
|
|
141
|
+
/** show the agent typing indicator */
|
|
142
|
+
@property({ type: Boolean })
|
|
143
|
+
public accessor typing = false;
|
|
69
144
|
|
|
70
145
|
@state()
|
|
71
|
-
private accessor
|
|
146
|
+
private accessor isDragging: boolean = false;
|
|
72
147
|
|
|
73
148
|
@state()
|
|
74
149
|
private accessor pendingAttachments: IAttachment[] = [];
|
|
75
150
|
|
|
76
151
|
private dropdownMenuItems: IDropdownMenuItem[] = [
|
|
77
152
|
{ id: 'mute', label: 'Mute notifications', icon: 'bell-off' },
|
|
78
|
-
{ id: 'pin', label: 'Pin conversation', icon: 'pin' },
|
|
79
153
|
{ id: 'search', label: 'Search in chat', icon: 'search' },
|
|
80
154
|
{ id: 'divider1', label: '', divider: true },
|
|
81
155
|
{ id: 'export', label: 'Export chat', icon: 'download' },
|
|
82
|
-
{ id: 'archive', label: 'Archive conversation', icon: 'archive' },
|
|
83
156
|
{ id: 'divider2', label: '', divider: true },
|
|
84
|
-
{ id: 'clear', label: 'Clear history', icon: 'trash-2', destructive: true }
|
|
157
|
+
{ id: 'clear', label: 'Clear history', icon: 'trash-2', destructive: true },
|
|
85
158
|
];
|
|
86
159
|
|
|
87
160
|
public static styles = [
|
|
88
|
-
|
|
161
|
+
...sioElementStyles,
|
|
89
162
|
css`
|
|
90
163
|
:host {
|
|
91
164
|
display: flex;
|
|
92
165
|
flex-direction: column;
|
|
93
166
|
height: 100%;
|
|
94
|
-
background:
|
|
95
|
-
font-family:
|
|
167
|
+
background: var(--sio-surface);
|
|
168
|
+
font-family: var(--sio-font-sf);
|
|
169
|
+
overflow: hidden;
|
|
96
170
|
}
|
|
97
171
|
|
|
172
|
+
/* ── glass header ────────────────────────────────────────── */
|
|
98
173
|
.header {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
174
|
+
${sioToolbarGlass}
|
|
175
|
+
flex-shrink: 0;
|
|
176
|
+
padding: 12px 16px;
|
|
102
177
|
display: flex;
|
|
103
178
|
align-items: center;
|
|
104
|
-
gap:
|
|
105
|
-
|
|
106
|
-
-webkit-backdrop-filter: blur(10px);
|
|
107
|
-
position: sticky;
|
|
108
|
-
top: 0;
|
|
179
|
+
gap: 10px;
|
|
180
|
+
position: relative;
|
|
109
181
|
z-index: 10;
|
|
110
|
-
overflow: visible;
|
|
111
182
|
}
|
|
112
183
|
|
|
113
|
-
.
|
|
114
|
-
|
|
184
|
+
.backButton {
|
|
185
|
+
border: none;
|
|
186
|
+
background: none;
|
|
187
|
+
padding: 0;
|
|
188
|
+
margin: 0;
|
|
189
|
+
display: flex;
|
|
190
|
+
align-items: center;
|
|
191
|
+
color: var(--sio-tint);
|
|
192
|
+
cursor: pointer;
|
|
115
193
|
}
|
|
116
194
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
display: block;
|
|
120
|
-
}
|
|
195
|
+
.backButton:hover {
|
|
196
|
+
color: var(--sio-tint-hover);
|
|
121
197
|
}
|
|
122
198
|
|
|
123
|
-
.
|
|
124
|
-
font-size: 1.125rem;
|
|
125
|
-
line-height: 1.5;
|
|
126
|
-
font-weight: 600;
|
|
127
|
-
margin: 0;
|
|
128
|
-
color: ${bdTheme('foreground')};
|
|
199
|
+
.headerMain {
|
|
129
200
|
flex: 1;
|
|
201
|
+
min-width: 0;
|
|
130
202
|
}
|
|
131
203
|
|
|
132
|
-
.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
204
|
+
.headerName {
|
|
205
|
+
font-size: 14px;
|
|
206
|
+
font-weight: 600;
|
|
207
|
+
color: var(--sio-label);
|
|
208
|
+
letter-spacing: -0.2px;
|
|
209
|
+
white-space: nowrap;
|
|
210
|
+
overflow: hidden;
|
|
211
|
+
text-overflow: ellipsis;
|
|
137
212
|
}
|
|
138
213
|
|
|
139
|
-
.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
display: flex;
|
|
144
|
-
flex-direction: column;
|
|
145
|
-
gap: ${unsafeCSS(spacing["3"])};
|
|
214
|
+
.headerStatus {
|
|
215
|
+
font-size: 11.5px;
|
|
216
|
+
font-weight: 500;
|
|
217
|
+
color: var(--sio-green);
|
|
146
218
|
}
|
|
147
219
|
|
|
148
|
-
.
|
|
220
|
+
:host([away]) .headerStatus {
|
|
221
|
+
color: var(--sio-orange);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.overflowButton {
|
|
225
|
+
border: none;
|
|
226
|
+
background: none;
|
|
227
|
+
padding: 4px;
|
|
228
|
+
margin: 0;
|
|
149
229
|
display: flex;
|
|
150
|
-
align-items:
|
|
151
|
-
|
|
152
|
-
|
|
230
|
+
align-items: center;
|
|
231
|
+
color: var(--sio-label-3);
|
|
232
|
+
cursor: pointer;
|
|
153
233
|
}
|
|
154
234
|
|
|
155
|
-
.
|
|
156
|
-
|
|
157
|
-
flex-direction: row-reverse;
|
|
235
|
+
.overflowButton:hover {
|
|
236
|
+
color: var(--sio-label-2);
|
|
158
237
|
}
|
|
159
238
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
line-height: 1.6;
|
|
239
|
+
/* ── messages ────────────────────────────────────────────── */
|
|
240
|
+
.messages {
|
|
241
|
+
flex: 1;
|
|
242
|
+
min-height: 0;
|
|
165
243
|
position: relative;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
244
|
+
padding: 14px 14px 6px;
|
|
245
|
+
display: flex;
|
|
246
|
+
flex-direction: column;
|
|
247
|
+
gap: 8px;
|
|
248
|
+
overflow-y: auto;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.dateStamp {
|
|
252
|
+
text-align: center;
|
|
253
|
+
font-size: 11px;
|
|
254
|
+
color: var(--sio-label-3);
|
|
255
|
+
margin-bottom: 2px;
|
|
256
|
+
font-variant-numeric: tabular-nums;
|
|
257
|
+
flex-shrink: 0;
|
|
169
258
|
}
|
|
170
259
|
|
|
171
|
-
.
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
260
|
+
.row {
|
|
261
|
+
display: flex;
|
|
262
|
+
justify-content: flex-start;
|
|
263
|
+
align-items: flex-end;
|
|
264
|
+
gap: 8px;
|
|
265
|
+
flex-shrink: 0;
|
|
176
266
|
}
|
|
177
267
|
|
|
178
|
-
.
|
|
179
|
-
|
|
180
|
-
color: ${bdTheme('primaryForeground')};
|
|
181
|
-
border-bottom-right-radius: ${unsafeCSS(spacing["1"])};
|
|
268
|
+
.row.mine {
|
|
269
|
+
justify-content: flex-end;
|
|
182
270
|
}
|
|
183
271
|
|
|
184
|
-
.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
272
|
+
.bubble {
|
|
273
|
+
max-width: 76%;
|
|
274
|
+
padding: 8px 12px;
|
|
275
|
+
border-radius: 16px;
|
|
276
|
+
font-size: 13.5px;
|
|
277
|
+
line-height: 1.4;
|
|
278
|
+
letter-spacing: -0.1px;
|
|
279
|
+
background: var(--sio-surface-2);
|
|
280
|
+
color: var(--sio-label);
|
|
281
|
+
word-wrap: break-word;
|
|
189
282
|
}
|
|
190
283
|
|
|
191
|
-
.
|
|
192
|
-
|
|
284
|
+
.row.mine .bubble {
|
|
285
|
+
background: var(--sio-tint);
|
|
286
|
+
color: #fff; /* on-tint constant (v7 outgoing bubble) */
|
|
193
287
|
}
|
|
194
288
|
|
|
195
|
-
|
|
289
|
+
/* typing indicator bubble */
|
|
290
|
+
.typingBubble {
|
|
291
|
+
padding: 10px 13px;
|
|
292
|
+
border-radius: 16px;
|
|
293
|
+
background: var(--sio-surface-2);
|
|
196
294
|
display: flex;
|
|
197
|
-
|
|
198
|
-
gap: ${unsafeCSS(spacing["1"])};
|
|
199
|
-
padding: ${unsafeCSS(spacing["2"])} ${unsafeCSS(spacing["3"])};
|
|
200
|
-
background: ${bdTheme('muted')};
|
|
201
|
-
border-radius: ${unsafeCSS(radius.lg)};
|
|
202
|
-
width: fit-content;
|
|
295
|
+
gap: 4px;
|
|
203
296
|
}
|
|
204
297
|
|
|
205
|
-
.
|
|
206
|
-
width:
|
|
207
|
-
height:
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
animation:
|
|
298
|
+
.typingBubble .dot {
|
|
299
|
+
width: 6px;
|
|
300
|
+
height: 6px;
|
|
301
|
+
border-radius: 3px;
|
|
302
|
+
background: var(--sio-label-3);
|
|
303
|
+
animation: typingPulse 1.2s ease-in-out infinite;
|
|
211
304
|
}
|
|
212
305
|
|
|
213
|
-
.
|
|
214
|
-
|
|
306
|
+
.typingBubble .dot:nth-child(1) {
|
|
307
|
+
opacity: 0.5;
|
|
215
308
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
animation-delay: 0.
|
|
309
|
+
.typingBubble .dot:nth-child(2) {
|
|
310
|
+
opacity: 0.7;
|
|
311
|
+
animation-delay: 0.15s;
|
|
312
|
+
}
|
|
313
|
+
.typingBubble .dot:nth-child(3) {
|
|
314
|
+
opacity: 0.9;
|
|
315
|
+
animation-delay: 0.3s;
|
|
219
316
|
}
|
|
220
317
|
|
|
221
|
-
@keyframes
|
|
222
|
-
0%,
|
|
223
|
-
|
|
224
|
-
|
|
318
|
+
@keyframes typingPulse {
|
|
319
|
+
0%,
|
|
320
|
+
60%,
|
|
321
|
+
100% {
|
|
322
|
+
transform: translateY(0);
|
|
225
323
|
}
|
|
226
324
|
30% {
|
|
227
|
-
|
|
228
|
-
transform: scale(1);
|
|
325
|
+
transform: translateY(-2px);
|
|
229
326
|
}
|
|
230
327
|
}
|
|
231
328
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
-
|
|
329
|
+
/* mail-fallback notice — the social.io magic moment */
|
|
330
|
+
.awayNotice {
|
|
331
|
+
flex-shrink: 0;
|
|
332
|
+
margin-top: 4px;
|
|
333
|
+
padding: 10px 12px;
|
|
334
|
+
border-radius: var(--sio-radius-md);
|
|
335
|
+
background: color-mix(in oklab, var(--sio-orange) 10%, transparent);
|
|
336
|
+
display: flex;
|
|
337
|
+
gap: 9px;
|
|
338
|
+
align-items: flex-start;
|
|
238
339
|
}
|
|
239
340
|
|
|
240
|
-
.
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
justify-content: center;
|
|
245
|
-
flex-direction: column;
|
|
246
|
-
gap: ${unsafeCSS(spacing["4"])};
|
|
247
|
-
padding: ${unsafeCSS(spacing["8"])};
|
|
248
|
-
text-align: center;
|
|
249
|
-
color: ${bdTheme('mutedForeground')};
|
|
250
|
-
animation: fadeIn 500ms ease-out;
|
|
341
|
+
.awayNotice sio-icon {
|
|
342
|
+
margin-top: 1px;
|
|
343
|
+
flex-shrink: 0;
|
|
344
|
+
color: var(--sio-orange);
|
|
251
345
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
to {
|
|
258
|
-
opacity: 1;
|
|
259
|
-
}
|
|
346
|
+
|
|
347
|
+
.awayNotice .noticeText {
|
|
348
|
+
font-size: 12px;
|
|
349
|
+
line-height: 1.45;
|
|
350
|
+
color: var(--sio-label);
|
|
260
351
|
}
|
|
261
352
|
|
|
262
|
-
.
|
|
263
|
-
font-
|
|
264
|
-
opacity: 0.5;
|
|
353
|
+
.awayNotice .noticeText b {
|
|
354
|
+
font-weight: 600;
|
|
265
355
|
}
|
|
266
356
|
|
|
267
|
-
/*
|
|
268
|
-
.
|
|
269
|
-
|
|
357
|
+
/* ── attachments in bubbles ──────────────────────────────── */
|
|
358
|
+
.attachments {
|
|
359
|
+
margin-top: 6px;
|
|
360
|
+
display: flex;
|
|
361
|
+
flex-wrap: wrap;
|
|
362
|
+
gap: 6px;
|
|
270
363
|
}
|
|
271
364
|
|
|
272
|
-
.
|
|
273
|
-
|
|
365
|
+
.attachmentImage {
|
|
366
|
+
max-width: 180px;
|
|
367
|
+
border-radius: var(--sio-radius-md);
|
|
368
|
+
overflow: hidden;
|
|
369
|
+
cursor: pointer;
|
|
274
370
|
}
|
|
275
371
|
|
|
276
|
-
.
|
|
277
|
-
|
|
278
|
-
|
|
372
|
+
.attachmentImage img {
|
|
373
|
+
display: block;
|
|
374
|
+
width: 100%;
|
|
375
|
+
height: auto;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.attachmentFile {
|
|
379
|
+
display: flex;
|
|
380
|
+
align-items: center;
|
|
381
|
+
gap: 8px;
|
|
382
|
+
padding: 7px 10px;
|
|
383
|
+
background: color-mix(in oklab, var(--sio-label) 6%, transparent);
|
|
384
|
+
border-radius: var(--sio-radius);
|
|
385
|
+
font-size: 12px;
|
|
386
|
+
cursor: pointer;
|
|
279
387
|
}
|
|
280
388
|
|
|
281
|
-
.
|
|
282
|
-
background:
|
|
389
|
+
.row.mine .attachmentFile {
|
|
390
|
+
background: rgba(255, 255, 255, 0.16); /* on-tint scrim */
|
|
391
|
+
color: #fff;
|
|
283
392
|
}
|
|
284
393
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
position: relative;
|
|
394
|
+
.attachmentName {
|
|
395
|
+
font-weight: 500;
|
|
288
396
|
}
|
|
289
|
-
|
|
290
|
-
.
|
|
397
|
+
|
|
398
|
+
.attachmentSize {
|
|
399
|
+
font-size: 11px;
|
|
400
|
+
opacity: 0.7;
|
|
401
|
+
font-variant-numeric: tabular-nums;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* ── drop overlay ────────────────────────────────────────── */
|
|
405
|
+
.dropOverlay {
|
|
291
406
|
position: absolute;
|
|
292
407
|
inset: 0;
|
|
293
|
-
background:
|
|
408
|
+
background: color-mix(in oklab, var(--sio-surface) 88%, transparent);
|
|
294
409
|
backdrop-filter: blur(8px);
|
|
295
410
|
-webkit-backdrop-filter: blur(8px);
|
|
296
411
|
display: flex;
|
|
@@ -299,147 +414,110 @@ export class SioConversationView extends DeesElement {
|
|
|
299
414
|
z-index: 100;
|
|
300
415
|
pointer-events: none;
|
|
301
416
|
opacity: 0;
|
|
302
|
-
transition: opacity
|
|
417
|
+
transition: opacity 160ms ease;
|
|
303
418
|
}
|
|
304
419
|
|
|
305
|
-
.
|
|
420
|
+
.dropOverlay.active {
|
|
306
421
|
opacity: 1;
|
|
307
422
|
pointer-events: all;
|
|
308
423
|
}
|
|
309
424
|
|
|
310
|
-
.
|
|
311
|
-
padding:
|
|
312
|
-
border:
|
|
313
|
-
border-radius:
|
|
314
|
-
background:
|
|
425
|
+
.dropZone {
|
|
426
|
+
padding: 24px 28px;
|
|
427
|
+
border: 1.5px dashed var(--sio-tint);
|
|
428
|
+
border-radius: var(--sio-radius-lg);
|
|
429
|
+
background: var(--sio-tint-soft);
|
|
315
430
|
text-align: center;
|
|
316
|
-
|
|
431
|
+
color: var(--sio-tint);
|
|
317
432
|
}
|
|
318
433
|
|
|
319
|
-
.
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
.drop-icon {
|
|
326
|
-
font-size: 48px;
|
|
327
|
-
color: ${bdTheme('primary')};
|
|
328
|
-
margin-bottom: ${unsafeCSS(spacing["4"])};
|
|
434
|
+
.dropText {
|
|
435
|
+
margin-top: 8px;
|
|
436
|
+
font-size: 13.5px;
|
|
437
|
+
font-weight: 600;
|
|
438
|
+
color: var(--sio-label);
|
|
329
439
|
}
|
|
330
440
|
|
|
331
|
-
.
|
|
332
|
-
|
|
333
|
-
font-
|
|
334
|
-
color:
|
|
335
|
-
margin-bottom: ${unsafeCSS(spacing["2"])};
|
|
441
|
+
.dropHint {
|
|
442
|
+
margin-top: 2px;
|
|
443
|
+
font-size: 11.5px;
|
|
444
|
+
color: var(--sio-label-3);
|
|
336
445
|
}
|
|
337
446
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
447
|
+
/* ── composer area ───────────────────────────────────────── */
|
|
448
|
+
.composerArea {
|
|
449
|
+
flex-shrink: 0;
|
|
341
450
|
}
|
|
342
451
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
margin-top: ${unsafeCSS(spacing["2"])};
|
|
452
|
+
.pendingAttachments {
|
|
453
|
+
padding: 8px 14px 0;
|
|
346
454
|
display: flex;
|
|
347
|
-
flex-
|
|
348
|
-
gap:
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
.attachment-image {
|
|
352
|
-
max-width: 200px;
|
|
353
|
-
max-height: 200px;
|
|
354
|
-
border-radius: ${unsafeCSS(radius.lg)};
|
|
355
|
-
overflow: hidden;
|
|
356
|
-
cursor: pointer;
|
|
357
|
-
transition: ${unsafeCSS(transitions.all)};
|
|
358
|
-
box-shadow: ${unsafeCSS(shadows.sm)};
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
.attachment-image:hover {
|
|
362
|
-
transform: scale(1.02);
|
|
363
|
-
box-shadow: ${unsafeCSS(shadows.md)};
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
.attachment-image img {
|
|
367
|
-
width: 100%;
|
|
368
|
-
height: 100%;
|
|
369
|
-
object-fit: cover;
|
|
455
|
+
flex-direction: column;
|
|
456
|
+
gap: 6px;
|
|
370
457
|
}
|
|
371
458
|
|
|
372
|
-
.
|
|
459
|
+
.pendingAttachment {
|
|
373
460
|
display: flex;
|
|
374
461
|
align-items: center;
|
|
375
|
-
gap:
|
|
376
|
-
padding:
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
font-size:
|
|
381
|
-
cursor: pointer;
|
|
382
|
-
transition: ${unsafeCSS(transitions.all)};
|
|
462
|
+
gap: 8px;
|
|
463
|
+
padding: 6px 10px;
|
|
464
|
+
border-radius: var(--sio-radius);
|
|
465
|
+
background: var(--sio-surface-2);
|
|
466
|
+
color: var(--sio-label-2);
|
|
467
|
+
font-size: 12px;
|
|
383
468
|
}
|
|
384
469
|
|
|
385
|
-
.
|
|
386
|
-
|
|
470
|
+
.pendingAttachment .info {
|
|
471
|
+
flex: 1;
|
|
472
|
+
min-width: 0;
|
|
387
473
|
}
|
|
388
474
|
|
|
389
|
-
.
|
|
475
|
+
.pendingAttachment .name {
|
|
390
476
|
font-weight: 500;
|
|
391
|
-
color:
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
color: ${bdTheme('mutedForeground')};
|
|
396
|
-
font-size: 0.75rem;
|
|
477
|
+
color: var(--sio-label);
|
|
478
|
+
white-space: nowrap;
|
|
479
|
+
overflow: hidden;
|
|
480
|
+
text-overflow: ellipsis;
|
|
397
481
|
}
|
|
398
482
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
border: 1px solid ${bdTheme('border')};
|
|
404
|
-
border-radius: ${unsafeCSS(radius.lg)};
|
|
405
|
-
margin-bottom: ${unsafeCSS(spacing["2"])};
|
|
483
|
+
.pendingAttachment .size {
|
|
484
|
+
font-size: 11px;
|
|
485
|
+
color: var(--sio-label-3);
|
|
486
|
+
font-variant-numeric: tabular-nums;
|
|
406
487
|
}
|
|
407
488
|
|
|
408
|
-
.
|
|
489
|
+
.pendingAttachment .remove {
|
|
409
490
|
display: flex;
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
.pending-attachment-info {
|
|
416
|
-
flex: 1;
|
|
417
|
-
min-width: 0;
|
|
491
|
+
padding: 2px;
|
|
492
|
+
color: var(--sio-label-3);
|
|
493
|
+
cursor: pointer;
|
|
418
494
|
}
|
|
419
495
|
|
|
420
|
-
.
|
|
421
|
-
|
|
422
|
-
font-weight: 500;
|
|
423
|
-
color: ${bdTheme('foreground')};
|
|
424
|
-
white-space: nowrap;
|
|
425
|
-
overflow: hidden;
|
|
426
|
-
text-overflow: ellipsis;
|
|
496
|
+
.pendingAttachment .remove:hover {
|
|
497
|
+
color: var(--sio-red);
|
|
427
498
|
}
|
|
428
499
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
500
|
+
/* ── empty state ─────────────────────────────────────────── */
|
|
501
|
+
.emptyState {
|
|
502
|
+
flex: 1;
|
|
503
|
+
display: flex;
|
|
504
|
+
flex-direction: column;
|
|
505
|
+
align-items: center;
|
|
506
|
+
justify-content: center;
|
|
507
|
+
gap: 10px;
|
|
508
|
+
padding: 32px;
|
|
509
|
+
text-align: center;
|
|
510
|
+
color: var(--sio-label-3);
|
|
432
511
|
}
|
|
433
512
|
|
|
434
|
-
.
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
color:
|
|
438
|
-
transition: ${unsafeCSS(transitions.all)};
|
|
513
|
+
.emptyState .emptyTitle {
|
|
514
|
+
font-size: 14px;
|
|
515
|
+
font-weight: 600;
|
|
516
|
+
color: var(--sio-label);
|
|
439
517
|
}
|
|
440
518
|
|
|
441
|
-
.
|
|
442
|
-
|
|
519
|
+
.emptyState .emptyText {
|
|
520
|
+
font-size: 12.5px;
|
|
443
521
|
}
|
|
444
522
|
`,
|
|
445
523
|
];
|
|
@@ -447,123 +525,159 @@ export class SioConversationView extends DeesElement {
|
|
|
447
525
|
public render(): TemplateResult {
|
|
448
526
|
if (!this.conversation) {
|
|
449
527
|
return html`
|
|
450
|
-
<div class="
|
|
451
|
-
<sio-icon
|
|
452
|
-
<
|
|
453
|
-
<
|
|
528
|
+
<div class="emptyState">
|
|
529
|
+
<sio-icon icon="message-circle" size="40"></sio-icon>
|
|
530
|
+
<div class="emptyTitle">No conversation selected</div>
|
|
531
|
+
<div class="emptyText">Pick a recent thread or send us a new message.</div>
|
|
454
532
|
</div>
|
|
455
533
|
`;
|
|
456
534
|
}
|
|
457
535
|
|
|
536
|
+
const initials =
|
|
537
|
+
this.conversation.agentInitials || this.deriveInitials(this.conversation.title);
|
|
538
|
+
const tint = this.conversation.agentTint || 'var(--sio-tint)';
|
|
539
|
+
const showTyping = !this.away && this.typing;
|
|
540
|
+
|
|
458
541
|
return html`
|
|
459
542
|
<div class="header">
|
|
460
|
-
<
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
<
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
<div class="header-actions">
|
|
470
|
-
<sio-button type="ghost" size="sm">
|
|
471
|
-
<sio-icon icon="phone" size="16"></sio-icon>
|
|
472
|
-
</sio-button>
|
|
473
|
-
<sio-dropdown-menu
|
|
474
|
-
.items=${this.dropdownMenuItems}
|
|
475
|
-
@item-selected=${this.handleDropdownAction}
|
|
476
|
-
>
|
|
477
|
-
<sio-button type="ghost" size="sm">
|
|
478
|
-
<sio-icon icon="more-vertical" size="16"></sio-icon>
|
|
479
|
-
</sio-button>
|
|
480
|
-
</sio-dropdown-menu>
|
|
543
|
+
<button class="backButton" aria-label="Back" @click=${this.handleBack}>
|
|
544
|
+
<sio-icon icon="chevron-left" size="18" .strokeWidth=${2.2}></sio-icon>
|
|
545
|
+
</button>
|
|
546
|
+
<sio-avatar initials=${initials} tint=${tint} size="30"></sio-avatar>
|
|
547
|
+
<div class="headerMain">
|
|
548
|
+
<div class="headerName">${this.conversation.title}</div>
|
|
549
|
+
<div class="headerStatus">
|
|
550
|
+
${this.away ? 'Away · replies by email' : 'Online now'}
|
|
551
|
+
</div>
|
|
481
552
|
</div>
|
|
553
|
+
<sio-dropdown-menu .items=${this.dropdownMenuItems} @item-selected=${this.handleDropdownAction}>
|
|
554
|
+
<button class="overflowButton" aria-label="Conversation actions">
|
|
555
|
+
<sio-icon icon="ellipsis" size="16"></sio-icon>
|
|
556
|
+
</button>
|
|
557
|
+
</sio-dropdown-menu>
|
|
482
558
|
</div>
|
|
483
559
|
|
|
484
|
-
<div
|
|
560
|
+
<div
|
|
561
|
+
class="messages"
|
|
562
|
+
id="messages"
|
|
485
563
|
@dragover=${this.handleDragOver}
|
|
564
|
+
@dragenter=${this.handleDragOver}
|
|
486
565
|
@dragleave=${this.handleDragLeave}
|
|
487
566
|
@drop=${this.handleDrop}
|
|
488
|
-
@dragenter=${this.handleDragOver}
|
|
489
567
|
>
|
|
490
|
-
<div
|
|
568
|
+
<div
|
|
569
|
+
class="dropOverlay ${this.isDragging ? 'active' : ''}"
|
|
491
570
|
@drop=${this.handleDrop}
|
|
492
571
|
@dragover=${(e: DragEvent) => e.preventDefault()}
|
|
493
572
|
>
|
|
494
|
-
<div class="
|
|
495
|
-
<sio-icon
|
|
496
|
-
<div class="
|
|
497
|
-
<div class="
|
|
573
|
+
<div class="dropZone">
|
|
574
|
+
<sio-icon icon="upload-cloud" size="28"></sio-icon>
|
|
575
|
+
<div class="dropText">Drop files here</div>
|
|
576
|
+
<div class="dropHint">Images and documents up to 10MB</div>
|
|
498
577
|
</div>
|
|
499
578
|
</div>
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
<div class="
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
579
|
+
|
|
580
|
+
${this.conversation.dateStamp
|
|
581
|
+
? html`<div class="dateStamp">${this.conversation.dateStamp}</div>`
|
|
582
|
+
: ''}
|
|
583
|
+
${this.conversation.messages.map(
|
|
584
|
+
(message) => html`
|
|
585
|
+
<div class="row ${message.sender === 'user' ? 'mine' : ''}">
|
|
586
|
+
${message.sender === 'support'
|
|
587
|
+
? html`<sio-avatar initials=${initials} tint=${tint} size="24"></sio-avatar>`
|
|
588
|
+
: ''}
|
|
589
|
+
<div class="bubble">
|
|
590
|
+
${message.text}
|
|
591
|
+
${message.attachments && message.attachments.length > 0
|
|
592
|
+
? html`
|
|
593
|
+
<div class="attachments">
|
|
594
|
+
${message.attachments.map((attachment) =>
|
|
595
|
+
this.isImage(attachment.type)
|
|
596
|
+
? html`
|
|
597
|
+
<div
|
|
598
|
+
class="attachmentImage"
|
|
599
|
+
@click=${() => this.openAttachment(attachment)}
|
|
600
|
+
>
|
|
601
|
+
<img src="${attachment.url}" alt="${attachment.name}" />
|
|
602
|
+
</div>
|
|
603
|
+
`
|
|
604
|
+
: html`
|
|
605
|
+
<div
|
|
606
|
+
class="attachmentFile"
|
|
607
|
+
@click=${() =>
|
|
608
|
+
this.isPdf(attachment)
|
|
609
|
+
? this.openAttachment(attachment)
|
|
610
|
+
: this.downloadFile(attachment)}
|
|
611
|
+
>
|
|
612
|
+
<sio-icon
|
|
613
|
+
icon=${this.isPdf(attachment) ? 'file-text' : 'file'}
|
|
614
|
+
size="15"
|
|
615
|
+
></sio-icon>
|
|
616
|
+
<div>
|
|
617
|
+
<div class="attachmentName">${attachment.name}</div>
|
|
618
|
+
<div class="attachmentSize">
|
|
619
|
+
${this.formatFileSize(attachment.size)}
|
|
620
|
+
</div>
|
|
621
|
+
</div>
|
|
622
|
+
</div>
|
|
623
|
+
`,
|
|
624
|
+
)}
|
|
528
625
|
</div>
|
|
529
626
|
`
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
` : ''}
|
|
533
|
-
<div class="message-time">${msg.time}</div>
|
|
534
|
-
</div>
|
|
535
|
-
</div>
|
|
536
|
-
`)}
|
|
537
|
-
|
|
538
|
-
${this.isTyping ? html`
|
|
539
|
-
<div class="message support">
|
|
540
|
-
<div class="typing-indicator">
|
|
541
|
-
<div class="typing-dot"></div>
|
|
542
|
-
<div class="typing-dot"></div>
|
|
543
|
-
<div class="typing-dot"></div>
|
|
627
|
+
: ''}
|
|
628
|
+
</div>
|
|
544
629
|
</div>
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
<div class="pending-attachment-info">
|
|
556
|
-
<div class="pending-attachment-name">${attachment.name}</div>
|
|
557
|
-
<div class="pending-attachment-size">${this.formatFileSize(attachment.size)}</div>
|
|
630
|
+
`,
|
|
631
|
+
)}
|
|
632
|
+
${showTyping
|
|
633
|
+
? html`
|
|
634
|
+
<div class="row">
|
|
635
|
+
<sio-avatar initials=${initials} tint=${tint} size="24"></sio-avatar>
|
|
636
|
+
<div class="typingBubble">
|
|
637
|
+
<span class="dot"></span>
|
|
638
|
+
<span class="dot"></span>
|
|
639
|
+
<span class="dot"></span>
|
|
558
640
|
</div>
|
|
559
|
-
|
|
560
|
-
|
|
641
|
+
</div>
|
|
642
|
+
`
|
|
643
|
+
: ''}
|
|
644
|
+
${this.away
|
|
645
|
+
? html`
|
|
646
|
+
<div class="awayNotice">
|
|
647
|
+
<sio-icon icon="envelope" size="14" .strokeWidth=${1.7}></sio-icon>
|
|
648
|
+
<div class="noticeText">
|
|
649
|
+
<b>The team is away.</b> Leave your email and your message continues there —
|
|
650
|
+
same thread, no repeats.
|
|
561
651
|
</div>
|
|
562
652
|
</div>
|
|
563
|
-
`
|
|
564
|
-
|
|
565
|
-
|
|
653
|
+
`
|
|
654
|
+
: ''}
|
|
655
|
+
</div>
|
|
656
|
+
|
|
657
|
+
<div class="composerArea">
|
|
658
|
+
${this.pendingAttachments.length > 0
|
|
659
|
+
? html`
|
|
660
|
+
<div class="pendingAttachments">
|
|
661
|
+
${this.pendingAttachments.map(
|
|
662
|
+
(attachment) => html`
|
|
663
|
+
<div class="pendingAttachment">
|
|
664
|
+
<sio-icon icon=${this.getFileIcon(attachment.type)} size="15"></sio-icon>
|
|
665
|
+
<div class="info">
|
|
666
|
+
<div class="name">${attachment.name}</div>
|
|
667
|
+
<div class="size">${this.formatFileSize(attachment.size)}</div>
|
|
668
|
+
</div>
|
|
669
|
+
<span class="remove" @click=${() => this.removeAttachment(attachment.id)}>
|
|
670
|
+
<sio-icon icon="x" size="14"></sio-icon>
|
|
671
|
+
</span>
|
|
672
|
+
</div>
|
|
673
|
+
`,
|
|
674
|
+
)}
|
|
675
|
+
</div>
|
|
676
|
+
`
|
|
677
|
+
: ''}
|
|
566
678
|
<sio-message-input
|
|
679
|
+
variant=${this.away ? 'email' : 'message'}
|
|
680
|
+
placeholder=${this.away ? 'you@email.com' : 'Write a message…'}
|
|
567
681
|
@send-message=${this.handleMessageSend}
|
|
568
682
|
@files-selected=${this.handleFilesSelected}
|
|
569
683
|
></sio-message-input>
|
|
@@ -571,62 +685,80 @@ export class SioConversationView extends DeesElement {
|
|
|
571
685
|
`;
|
|
572
686
|
}
|
|
573
687
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
688
|
+
public updated() {
|
|
689
|
+
// keep the thread pinned to the latest message
|
|
690
|
+
const container = this.shadowRoot?.querySelector('#messages');
|
|
691
|
+
if (container) {
|
|
692
|
+
container.scrollTop = container.scrollHeight;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
private deriveInitials(title: string): string {
|
|
697
|
+
return title
|
|
698
|
+
.split(/[\s·]+/)
|
|
699
|
+
.filter((part) => part.length > 0)
|
|
700
|
+
.slice(0, 2)
|
|
701
|
+
.map((part) => part[0].toUpperCase())
|
|
702
|
+
.join('');
|
|
579
703
|
}
|
|
580
704
|
|
|
581
|
-
private
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
705
|
+
private handleBack = () => {
|
|
706
|
+
this.dispatchEvent(
|
|
707
|
+
new CustomEvent('back', {
|
|
708
|
+
bubbles: true,
|
|
709
|
+
composed: true,
|
|
710
|
+
}),
|
|
711
|
+
);
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
private handleMessageSend = (event: CustomEvent<{ text: string; attachments: File[] }>) => {
|
|
715
|
+
event.stopPropagation();
|
|
716
|
+
const text = event.detail.text.trim();
|
|
717
|
+
|
|
718
|
+
if (this.away) {
|
|
719
|
+
if (!text) {
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
this.dispatchEvent(
|
|
723
|
+
new CustomEvent('email-submitted', {
|
|
724
|
+
detail: { email: text },
|
|
725
|
+
bubbles: true,
|
|
726
|
+
composed: true,
|
|
727
|
+
}),
|
|
728
|
+
);
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
if (!text && this.pendingAttachments.length === 0) {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
585
735
|
|
|
586
736
|
const message: IMessage = {
|
|
587
737
|
id: Date.now().toString(),
|
|
588
|
-
text
|
|
738
|
+
text,
|
|
589
739
|
sender: 'user',
|
|
590
740
|
time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
|
|
591
741
|
status: 'sending',
|
|
592
|
-
attachments: [...this.pendingAttachments]
|
|
742
|
+
attachments: [...this.pendingAttachments],
|
|
593
743
|
};
|
|
594
744
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
745
|
+
this.dispatchEvent(
|
|
746
|
+
new CustomEvent('send-message', {
|
|
747
|
+
detail: { message },
|
|
748
|
+
bubbles: true,
|
|
749
|
+
composed: true,
|
|
750
|
+
}),
|
|
751
|
+
);
|
|
601
752
|
|
|
602
|
-
// Clear pending attachments
|
|
603
753
|
this.pendingAttachments = [];
|
|
754
|
+
};
|
|
604
755
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
this.isTyping = false;
|
|
610
|
-
}, 2000);
|
|
611
|
-
}, 1000);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
private handleFilesSelected(event: CustomEvent) {
|
|
615
|
-
const { files } = event.detail;
|
|
616
|
-
// Handle files if needed
|
|
617
|
-
// For now, we're handling attachments separately in the parent component
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
public updated() {
|
|
622
|
-
// Scroll to bottom when new messages arrive
|
|
623
|
-
const container = this.shadowRoot?.querySelector('#messages');
|
|
624
|
-
if (container) {
|
|
625
|
-
container.scrollTop = container.scrollHeight;
|
|
626
|
-
}
|
|
627
|
-
}
|
|
756
|
+
private handleFilesSelected = (event: CustomEvent<{ files: File[] }>) => {
|
|
757
|
+
event.stopPropagation();
|
|
758
|
+
this.processFiles(event.detail.files);
|
|
759
|
+
};
|
|
628
760
|
|
|
629
|
-
// File handling
|
|
761
|
+
// File handling
|
|
630
762
|
private handleDragOver(e: DragEvent) {
|
|
631
763
|
e.preventDefault();
|
|
632
764
|
e.stopPropagation();
|
|
@@ -636,11 +768,10 @@ export class SioConversationView extends DeesElement {
|
|
|
636
768
|
private handleDragLeave(e: DragEvent) {
|
|
637
769
|
e.preventDefault();
|
|
638
770
|
e.stopPropagation();
|
|
639
|
-
|
|
640
|
-
// Check if we're actually leaving the messages container
|
|
771
|
+
|
|
641
772
|
const relatedTarget = e.relatedTarget as Node;
|
|
642
773
|
const container = e.currentTarget as HTMLElement;
|
|
643
|
-
|
|
774
|
+
|
|
644
775
|
if (!container.contains(relatedTarget)) {
|
|
645
776
|
this.isDragging = false;
|
|
646
777
|
}
|
|
@@ -657,23 +788,9 @@ export class SioConversationView extends DeesElement {
|
|
|
657
788
|
}
|
|
658
789
|
}
|
|
659
790
|
|
|
660
|
-
private openFileSelector() {
|
|
661
|
-
const fileInput = this.shadowRoot?.querySelector('#fileInput') as HTMLInputElement;
|
|
662
|
-
if (fileInput) {
|
|
663
|
-
fileInput.click();
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
private handleFileSelect(e: Event) {
|
|
668
|
-
const input = e.target as HTMLInputElement;
|
|
669
|
-
const files = Array.from(input.files || []);
|
|
670
|
-
this.processFiles(files);
|
|
671
|
-
input.value = ''; // Clear input for re-selection
|
|
672
|
-
}
|
|
673
|
-
|
|
674
791
|
private async processFiles(files: File[]) {
|
|
675
792
|
const maxSize = 10 * 1024 * 1024; // 10MB
|
|
676
|
-
const validFiles = files.filter(file => {
|
|
793
|
+
const validFiles = files.filter((file) => {
|
|
677
794
|
if (file.size > maxSize) {
|
|
678
795
|
console.warn(`File ${file.name} exceeds 10MB limit`);
|
|
679
796
|
return false;
|
|
@@ -684,14 +801,14 @@ export class SioConversationView extends DeesElement {
|
|
|
684
801
|
for (const file of validFiles) {
|
|
685
802
|
const id = `${Date.now()}-${Math.random()}`;
|
|
686
803
|
const url = await this.fileToDataUrl(file);
|
|
687
|
-
|
|
804
|
+
|
|
688
805
|
const attachment: IAttachment = {
|
|
689
806
|
id,
|
|
690
807
|
name: file.name,
|
|
691
808
|
size: file.size,
|
|
692
809
|
type: file.type,
|
|
693
810
|
url,
|
|
694
|
-
thumbnailUrl: this.isImage(file.type) ? url : undefined
|
|
811
|
+
thumbnailUrl: this.isImage(file.type) ? url : undefined,
|
|
695
812
|
};
|
|
696
813
|
|
|
697
814
|
this.pendingAttachments = [...this.pendingAttachments, attachment];
|
|
@@ -708,13 +825,19 @@ export class SioConversationView extends DeesElement {
|
|
|
708
825
|
}
|
|
709
826
|
|
|
710
827
|
private removeAttachment(id: string) {
|
|
711
|
-
this.pendingAttachments = this.pendingAttachments.filter(a => a.id !== id);
|
|
828
|
+
this.pendingAttachments = this.pendingAttachments.filter((a) => a.id !== id);
|
|
712
829
|
}
|
|
713
830
|
|
|
714
831
|
private isImage(type: string): boolean {
|
|
715
832
|
return type.startsWith('image/');
|
|
716
833
|
}
|
|
717
834
|
|
|
835
|
+
private isPdf(attachment: IAttachment): boolean {
|
|
836
|
+
return (
|
|
837
|
+
attachment.type?.includes('pdf') || attachment.name?.toLowerCase().endsWith('.pdf') || false
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
|
|
718
841
|
private formatFileSize(bytes: number): string {
|
|
719
842
|
if (bytes === 0) return '0 Bytes';
|
|
720
843
|
const k = 1024;
|
|
@@ -731,20 +854,23 @@ export class SioConversationView extends DeesElement {
|
|
|
731
854
|
return 'file';
|
|
732
855
|
}
|
|
733
856
|
|
|
734
|
-
private
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
857
|
+
private openAttachment(attachment: IAttachment) {
|
|
858
|
+
if (this.isPdf(attachment)) {
|
|
859
|
+
this.dispatchEvent(
|
|
860
|
+
new CustomEvent('open-file', {
|
|
861
|
+
detail: { attachment },
|
|
862
|
+
bubbles: true,
|
|
863
|
+
composed: true,
|
|
864
|
+
}),
|
|
865
|
+
);
|
|
742
866
|
} else {
|
|
743
|
-
this.dispatchEvent(
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
867
|
+
this.dispatchEvent(
|
|
868
|
+
new CustomEvent('open-image', {
|
|
869
|
+
detail: { attachment },
|
|
870
|
+
bubbles: true,
|
|
871
|
+
composed: true,
|
|
872
|
+
}),
|
|
873
|
+
);
|
|
748
874
|
}
|
|
749
875
|
}
|
|
750
876
|
|
|
@@ -757,44 +883,34 @@ export class SioConversationView extends DeesElement {
|
|
|
757
883
|
document.body.removeChild(a);
|
|
758
884
|
}
|
|
759
885
|
|
|
760
|
-
private handleDropdownAction(event: CustomEvent) {
|
|
761
|
-
const { item } = event.detail
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
// Handle some actions locally for demo
|
|
777
|
-
switch (item.id) {
|
|
778
|
-
case 'search':
|
|
779
|
-
// Could open a search overlay
|
|
780
|
-
console.log('Opening search...');
|
|
781
|
-
break;
|
|
782
|
-
case 'export':
|
|
783
|
-
// Export conversation as JSON/text
|
|
784
|
-
this.exportConversation();
|
|
785
|
-
break;
|
|
886
|
+
private handleDropdownAction = (event: CustomEvent<{ item: IDropdownMenuItem }>) => {
|
|
887
|
+
const { item } = event.detail;
|
|
888
|
+
|
|
889
|
+
this.dispatchEvent(
|
|
890
|
+
new CustomEvent('conversation-action', {
|
|
891
|
+
detail: {
|
|
892
|
+
action: item.id,
|
|
893
|
+
conversationId: this.conversation?.id,
|
|
894
|
+
},
|
|
895
|
+
bubbles: true,
|
|
896
|
+
composed: true,
|
|
897
|
+
}),
|
|
898
|
+
);
|
|
899
|
+
|
|
900
|
+
if (item.id === 'export') {
|
|
901
|
+
this.exportConversation();
|
|
786
902
|
}
|
|
787
|
-
}
|
|
903
|
+
};
|
|
788
904
|
|
|
789
905
|
private exportConversation() {
|
|
790
906
|
if (!this.conversation) return;
|
|
791
|
-
|
|
907
|
+
|
|
792
908
|
const exportData = {
|
|
793
909
|
conversation: this.conversation.title,
|
|
794
910
|
exportDate: new Date().toISOString(),
|
|
795
|
-
messages: this.conversation.messages
|
|
911
|
+
messages: this.conversation.messages,
|
|
796
912
|
};
|
|
797
|
-
|
|
913
|
+
|
|
798
914
|
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
|
|
799
915
|
const url = URL.createObjectURL(blob);
|
|
800
916
|
const a = document.createElement('a');
|
|
@@ -805,4 +921,4 @@ export class SioConversationView extends DeesElement {
|
|
|
805
921
|
document.body.removeChild(a);
|
|
806
922
|
URL.revokeObjectURL(url);
|
|
807
923
|
}
|
|
808
|
-
}
|
|
924
|
+
}
|