@social.io/catalog 1.3.0 → 2.0.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.
Files changed (124) hide show
  1. package/.smartconfig.json +51 -0
  2. package/dist_bundle/bundle.js +5450 -3267
  3. package/dist_ts_web/00_commitinfo_data.js +3 -3
  4. package/dist_ts_web/elements/index.d.ts +20 -3
  5. package/dist_ts_web/elements/index.js +27 -7
  6. package/dist_ts_web/elements/sio-ai-card.d.ts +39 -0
  7. package/dist_ts_web/elements/sio-ai-card.js +229 -0
  8. package/dist_ts_web/elements/sio-avatar.d.ts +46 -0
  9. package/dist_ts_web/elements/sio-avatar.js +379 -0
  10. package/dist_ts_web/elements/sio-bucket-header.d.ts +36 -0
  11. package/dist_ts_web/elements/sio-bucket-header.js +197 -0
  12. package/dist_ts_web/elements/sio-button.d.ts +7 -3
  13. package/dist_ts_web/elements/sio-button.js +253 -152
  14. package/dist_ts_web/elements/sio-channel-rail.d.ts +37 -0
  15. package/dist_ts_web/elements/sio-channel-rail.js +275 -0
  16. package/dist_ts_web/elements/sio-chip.d.ts +25 -0
  17. package/dist_ts_web/elements/sio-chip.js +188 -0
  18. package/dist_ts_web/elements/sio-cmdk.d.ts +59 -0
  19. package/dist_ts_web/elements/sio-cmdk.js +471 -0
  20. package/dist_ts_web/elements/sio-combox.d.ts +55 -1
  21. package/dist_ts_web/elements/sio-combox.js +458 -261
  22. package/dist_ts_web/elements/sio-composer.d.ts +48 -0
  23. package/dist_ts_web/elements/sio-composer.js +357 -0
  24. package/dist_ts_web/elements/sio-conversation-selector.d.ts +41 -1
  25. package/dist_ts_web/elements/sio-conversation-selector.js +426 -238
  26. package/dist_ts_web/elements/sio-conversation-view.d.ts +34 -6
  27. package/dist_ts_web/elements/sio-conversation-view.js +738 -564
  28. package/dist_ts_web/elements/sio-dropdown-menu.d.ts +6 -6
  29. package/dist_ts_web/elements/sio-dropdown-menu.js +223 -169
  30. package/dist_ts_web/elements/sio-fab.d.ts +23 -5
  31. package/dist_ts_web/elements/sio-fab.js +259 -235
  32. package/dist_ts_web/elements/sio-glass-toolbar.d.ts +22 -0
  33. package/dist_ts_web/elements/sio-glass-toolbar.js +178 -0
  34. package/dist_ts_web/elements/sio-icon.d.ts +1 -0
  35. package/dist_ts_web/elements/sio-icon.js +254 -94
  36. package/dist_ts_web/elements/sio-image-lightbox.d.ts +3 -4
  37. package/dist_ts_web/elements/sio-image-lightbox.js +282 -191
  38. package/dist_ts_web/elements/sio-inset-list.d.ts +28 -0
  39. package/dist_ts_web/elements/sio-inset-list.js +191 -0
  40. package/dist_ts_web/elements/sio-kbd.d.ts +16 -0
  41. package/dist_ts_web/elements/sio-kbd.js +117 -0
  42. package/dist_ts_web/elements/sio-list-row.d.ts +55 -0
  43. package/dist_ts_web/elements/sio-list-row.js +462 -0
  44. package/dist_ts_web/elements/sio-message-input.d.ts +25 -4
  45. package/dist_ts_web/elements/sio-message-input.js +231 -226
  46. package/dist_ts_web/elements/sio-paper-thumb.d.ts +24 -0
  47. package/dist_ts_web/elements/sio-paper-thumb.js +293 -0
  48. package/dist_ts_web/elements/sio-pdf-viewer.d.ts +2 -0
  49. package/dist_ts_web/elements/sio-pdf-viewer.js +344 -244
  50. package/dist_ts_web/elements/sio-segmented.d.ts +32 -0
  51. package/dist_ts_web/elements/sio-segmented.js +217 -0
  52. package/dist_ts_web/elements/sio-step-timeline.d.ts +28 -0
  53. package/dist_ts_web/elements/sio-step-timeline.js +222 -0
  54. package/dist_ts_web/elements/sio-tab-bar.d.ts +36 -0
  55. package/dist_ts_web/elements/sio-tab-bar.js +232 -0
  56. package/dist_ts_web/elements/sio-unread-badge.d.ts +25 -0
  57. package/dist_ts_web/elements/sio-unread-badge.js +203 -0
  58. package/dist_ts_web/elements/sio-waveform.d.ts +34 -0
  59. package/dist_ts_web/elements/sio-waveform.js +304 -0
  60. package/dist_ts_web/elements/tokens.d.ts +70 -0
  61. package/dist_ts_web/elements/tokens.js +277 -0
  62. package/dist_ts_web/pages/firstpage.d.ts +5 -0
  63. package/dist_ts_web/pages/firstpage.js +21 -2
  64. package/dist_ts_web/pages/mainpage.d.ts +6 -0
  65. package/dist_ts_web/pages/mainpage.js +111 -39
  66. package/{license → license.md} +3 -1
  67. package/package.json +23 -29
  68. package/readme.hints.md +81 -0
  69. package/readme.md +85 -151
  70. package/ts_web/00_commitinfo_data.ts +2 -2
  71. package/ts_web/elements/index.ts +30 -7
  72. package/ts_web/elements/sio-ai-card.ts +175 -0
  73. package/ts_web/elements/sio-avatar.ts +295 -0
  74. package/ts_web/elements/sio-bucket-header.ts +157 -0
  75. package/ts_web/elements/sio-button.ts +149 -127
  76. package/ts_web/elements/sio-channel-rail.ts +243 -0
  77. package/ts_web/elements/sio-chip.ts +134 -0
  78. package/ts_web/elements/sio-cmdk.ts +461 -0
  79. package/ts_web/elements/sio-combox.ts +327 -204
  80. package/ts_web/elements/sio-composer.ts +306 -0
  81. package/ts_web/elements/sio-conversation-selector.ts +350 -228
  82. package/ts_web/elements/sio-conversation-view.ts +573 -457
  83. package/ts_web/elements/sio-dropdown-menu.ts +112 -130
  84. package/ts_web/elements/sio-fab.ts +192 -227
  85. package/ts_web/elements/sio-glass-toolbar.ts +140 -0
  86. package/ts_web/elements/sio-icon.ts +147 -25
  87. package/ts_web/elements/sio-image-lightbox.ts +62 -64
  88. package/ts_web/elements/sio-inset-list.ts +140 -0
  89. package/ts_web/elements/sio-kbd.ts +80 -0
  90. package/ts_web/elements/sio-list-row.ts +393 -0
  91. package/ts_web/elements/sio-message-input.ts +142 -221
  92. package/ts_web/elements/sio-paper-thumb.ts +244 -0
  93. package/ts_web/elements/sio-pdf-viewer.ts +58 -60
  94. package/ts_web/elements/sio-segmented.ts +183 -0
  95. package/ts_web/elements/sio-step-timeline.ts +179 -0
  96. package/ts_web/elements/sio-tab-bar.ts +195 -0
  97. package/ts_web/elements/sio-unread-badge.ts +147 -0
  98. package/ts_web/elements/sio-waveform.ts +239 -0
  99. package/ts_web/elements/tokens.ts +297 -0
  100. package/ts_web/pages/firstpage.ts +20 -1
  101. package/ts_web/pages/mainpage.ts +111 -39
  102. package/dist_bundle/bundle.js.map +0 -7
  103. package/dist_ts_web/elements/00colors.d.ts +0 -108
  104. package/dist_ts_web/elements/00colors.js +0 -137
  105. package/dist_ts_web/elements/00fonts.d.ts +0 -60
  106. package/dist_ts_web/elements/00fonts.js +0 -129
  107. package/dist_ts_web/elements/00tokens.d.ts +0 -123
  108. package/dist_ts_web/elements/00tokens.js +0 -176
  109. package/dist_ts_web/elements/sio-conversation-list.d.ts +0 -20
  110. package/dist_ts_web/elements/sio-conversation-list.js +0 -495
  111. package/dist_ts_web/elements/sio-recorder.d.ts +0 -60
  112. package/dist_ts_web/elements/sio-recorder.js +0 -173
  113. package/dist_ts_web/elements/sio-subwidget-conversations.d.ts +0 -16
  114. package/dist_ts_web/elements/sio-subwidget-conversations.js +0 -350
  115. package/dist_ts_web/elements/sio-subwidget-onboardme.d.ts +0 -7
  116. package/dist_ts_web/elements/sio-subwidget-onboardme.js +0 -243
  117. package/dist_watch/bundle.js +0 -81694
  118. package/dist_watch/bundle.js.map +0 -7
  119. package/dist_watch/index.html +0 -25
  120. package/npmextra.json +0 -19
  121. package/ts_web/elements/00colors.ts +0 -164
  122. package/ts_web/elements/00fonts.ts +0 -145
  123. package/ts_web/elements/00tokens.ts +0 -197
  124. package/ts_web/elements/sio-recorder.ts +0 -213
@@ -1,27 +1,24 @@
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
- unsafeCSS,
10
- state,
8
+ cssManager,
9
+ type TemplateResult,
11
10
  } from '@design.estate/dees-element';
12
11
  import * as domtools from '@design.estate/dees-domtools';
13
12
 
14
- // Import design tokens
15
- import { colors, bdTheme } from './00colors.js';
16
- import { spacing, radius, shadows, transitions } from './00tokens.js';
17
- import { fontFamilies, typography } from './00fonts.js';
18
-
19
- // Import components
13
+ import { sioElementStyles } from './tokens.js';
20
14
  import { SioConversationSelector, type IConversation } from './sio-conversation-selector.js';
21
- import { SioConversationView, type IMessage, type IConversationData, type IAttachment } from './sio-conversation-view.js';
22
- import { SioImageLightbox, type ILightboxImage } from './sio-image-lightbox.js';
23
-
24
- // Make sure components are loaded
15
+ import {
16
+ SioConversationView,
17
+ type IMessage,
18
+ type IConversationData,
19
+ type IAttachment,
20
+ } from './sio-conversation-view.js';
21
+ import { SioImageLightbox, type ILightboxFile } from './sio-image-lightbox.js';
25
22
  SioConversationSelector;
26
23
  SioConversationView;
27
24
  SioImageLightbox;
@@ -32,94 +29,100 @@ declare global {
32
29
  }
33
30
  }
34
31
 
32
+ /**
33
+ * sio-combox — the widget panel (v7 SocialIOWidget.jsx SIOWidgetShell).
34
+ *
35
+ * 380×620 panel, radius 20 on --sio-surface, floating shadow with a
36
+ * 0.5px ring, anchored above the sio-fab launcher. Slides between the
37
+ * home/welcome state (sio-conversation-selector) and the conversation
38
+ * state (sio-conversation-view); the away/email-fallback state is the
39
+ * conversation state with `away` set. Every state carries the centered
40
+ * "Powered by social.io" footer with the mini brand glyph.
41
+ *
42
+ * Singleton API: SioCombox.createOnBody() / getInstance(), plus
43
+ * open() / close() / toggle() / getIsOpen(). Full-screen on phablet.
44
+ */
35
45
  @customElement('sio-combox')
36
46
  export class SioCombox extends DeesElement {
37
- public static demo = () => html` <sio-combox></sio-combox> `;
47
+ public static demo = () => html`
48
+ <div style="min-height: 660px; padding: 20px; background: var(--sio-bg);">
49
+ <sio-combox class="open" style="position: relative; bottom: auto; right: auto;"></sio-combox>
50
+ </div>
51
+ `;
38
52
 
39
- @property({ type: Object })
40
- public accessor referenceObject: HTMLElement;
53
+ // Singleton instance
54
+ private static instance: SioCombox | null = null;
55
+
56
+ /**
57
+ * Creates and appends a singleton combox to document.body
58
+ */
59
+ public static createOnBody(): SioCombox {
60
+ if (!SioCombox.instance) {
61
+ SioCombox.instance = new SioCombox();
62
+ document.body.appendChild(SioCombox.instance);
63
+ }
64
+ return SioCombox.instance;
65
+ }
66
+
67
+ /**
68
+ * Gets the singleton instance if it exists
69
+ */
70
+ public static getInstance(): SioCombox | null {
71
+ return SioCombox.instance;
72
+ }
73
+
74
+ /** team is away — the conversation state falls back to email capture */
75
+ @property({ type: Boolean })
76
+ public accessor away = false;
41
77
 
42
78
  @state()
43
79
  private accessor selectedConversationId: string | null = null;
44
80
 
81
+ @state()
82
+ private accessor isKeyboardVisible: boolean = false;
83
+
84
+ @state()
85
+ private accessor isOpen: boolean = false;
86
+
87
+ private keyboardBlurTimeout?: number;
88
+
89
+ // demo data — the prototype's Lena/Orbit launch-banner thread
45
90
  @state()
46
91
  private accessor conversations: IConversation[] = [
47
92
  {
48
- id: '1',
49
- title: 'Technical Support',
50
- lastMessage: 'Thanks for your help with the login issue!',
51
- time: '2 min ago',
52
- unread: true,
93
+ id: 'orbit-launch',
94
+ title: 'Lena · Orbit',
95
+ lastMessage: 'Glad that sorted it — anything else, just write!',
96
+ time: '2d',
97
+ initials: 'LP',
98
+ tint: '#FF2D55',
99
+ status: 'resolved',
53
100
  },
54
- {
55
- id: '2',
56
- title: 'Billing Question',
57
- lastMessage: 'I need help understanding my invoice',
58
- time: '1 hour ago',
59
- },
60
- {
61
- id: '3',
62
- title: 'Feature Request',
63
- lastMessage: 'That would be great! Looking forward to it',
64
- time: 'Yesterday',
65
- },
66
- {
67
- id: '4',
68
- title: 'General Inquiry',
69
- lastMessage: 'Thank you for the information',
70
- time: '2 days ago',
71
- }
72
101
  ];
73
102
 
74
103
  @state()
75
104
  private accessor messages: { [conversationId: string]: IMessage[] } = {
76
- '1': [
77
- { id: '1', text: 'Hi, I\'m having trouble logging in', sender: 'user', time: '10:00 AM' },
78
- { id: '2', text: 'I can help you with that. Can you tell me what error you\'re seeing?', sender: 'support', time: '10:02 AM' },
79
- { id: '3', text: 'It says "Invalid credentials" but I\'m sure my password is correct', sender: 'user', time: '10:03 AM' },
80
- { id: '4', text: 'Let me check your account. Please try resetting your password using the forgot password link.', sender: 'support', time: '10:05 AM' },
81
- {
82
- id: '5',
83
- text: 'Here\'s a screenshot of the error',
84
- sender: 'user',
85
- time: '10:08 AM',
86
- attachments: [{
87
- id: 'att1',
88
- name: 'error-screenshot.png',
89
- size: 245780,
90
- type: 'image/png',
91
- url: 'https://picsum.photos/400/300?random=1'
92
- }]
105
+ 'orbit-launch': [
106
+ {
107
+ id: '1',
108
+ text: 'Hey! 👋 You wrote about the launch banner — what would you like to change?',
109
+ sender: 'support',
110
+ time: '14:02',
93
111
  },
94
- { id: '6', text: 'Thanks for your help with the login issue!', sender: 'user', time: '10:10 AM' },
95
- {
96
- id: '7',
97
- text: 'Here is the documentation you requested',
98
- sender: 'support',
99
- time: '10:15 AM',
100
- attachments: [{
101
- id: 'att2',
102
- name: 'user-guide.pdf',
103
- size: 2457600,
104
- type: 'application/pdf',
105
- url: 'data:application/pdf;base64,JVBERi0xLjMKJeLjz9MKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovT3V0bGluZXMgMiAwIFIKL1BhZ2VzIDMgMCBSCj4+CmVuZG9iagoyIDAgb2JqCjw8Ci9UeXBlIC9PdXRsaW5lcwovQ291bnQgMAo+PgplbmRvYmoKMyAwIG9iago8PAovVHlwZSAvUGFnZXMKL0NvdW50IDEKL0tpZHMgWzQgMCBSXQo+PgplbmRvYmoKNCAwIG9iago8PAovVHlwZSAvUGFnZQovUGFyZW50IDMgMCBSCi9NZWRpYUJveCBbMCAwIDYxMiA3OTJdCi9Db250ZW50cyA1IDAgUgovUmVzb3VyY2VzIDw8Ci9Gb250IDw8Ci9GMSA2IDAgUgo+Pgo+Pgo+PgplbmRvYmoKNSAwIG9iago8PAovTGVuZ3RoIDQ0Cj4+CnN0cmVhbQpCVApxCjcwIDUwIFRECi9GMSAxMiBUZgooSGVsbG8gV29ybGQpIFRqCkVUClEKZW5kc3RyZWFtCmVuZG9iago2IDAgb2JqCjw8Ci9UeXBlIC9Gb250Ci9TdWJ0eXBlIC9UeXBlMQovQmFzZUZvbnQgL1RpbWVzLVJvbWFuCj4+CmVuZG9iagp4cmVmCjAgNwowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMDkgMDAwMDAgbiAKMDAwMDAwMDA3NCAwMDAwMCBuIAowMDAwMDAwMTIwIDAwMDAwIG4gCjAwMDAwMDAxNzkgMDAwMDAgbiAKMDAwMDAwMDM2NCAwMDAwMCBuIAowMDAwMDAwNDY2IDAwMDAwIG4gCnRyYWlsZXIKPDwKL1NpemUgNwovUm9vdCAxIDAgUgo+PgpzdGFydHhyZWYKNTY1CiUlRU9G'
106
- }]
112
+ {
113
+ id: '2',
114
+ text: 'The headline is cut off on tablet. Can we shorten it before Thursday?',
115
+ sender: 'user',
116
+ time: '14:03',
107
117
  },
118
+ {
119
+ id: '3',
120
+ text: "Good catch. We can ship the short variant today — I'll send a preview here in ~10 min.",
121
+ sender: 'support',
122
+ time: '14:04',
123
+ },
124
+ { id: '4', text: 'Perfect, thanks!', sender: 'user', time: '14:04' },
108
125
  ],
109
- '2': [
110
- { id: '1', text: 'I need help understanding my invoice', sender: 'user', time: '9:00 AM' },
111
- { id: '2', text: 'I\'d be happy to help explain your invoice. Which part is unclear?', sender: 'support', time: '9:05 AM' },
112
- ],
113
- '3': [
114
- { id: '1', text: 'I\'d love to see dark mode support in your app!', sender: 'user', time: 'Yesterday' },
115
- { id: '2', text: 'Thanks for the suggestion! We\'re actually working on dark mode and it should be available next month.', sender: 'support', time: 'Yesterday' },
116
- { id: '3', text: 'That would be great! Looking forward to it', sender: 'user', time: 'Yesterday' },
117
- ],
118
- '4': [
119
- { id: '1', text: 'Can you tell me more about your enterprise plans?', sender: 'user', time: '2 days ago' },
120
- { id: '2', text: 'Of course! Our enterprise plans include advanced features like SSO, dedicated support, and custom integrations.', sender: 'support', time: '2 days ago' },
121
- { id: '3', text: 'Thank you for the information', sender: 'user', time: '2 days ago' },
122
- ]
123
126
  };
124
127
 
125
128
  constructor() {
@@ -127,131 +130,227 @@ export class SioCombox extends DeesElement {
127
130
  domtools.DomTools.setupDomTools();
128
131
  }
129
132
 
133
+ async connectedCallback() {
134
+ await super.connectedCallback();
135
+ this.addEventListener('input-focus', this.handleInputFocus as EventListener);
136
+ this.addEventListener('input-blur', this.handleInputBlur as EventListener);
137
+ }
138
+
139
+ async disconnectedCallback() {
140
+ await super.disconnectedCallback();
141
+ this.removeEventListener('input-focus', this.handleInputFocus as EventListener);
142
+ this.removeEventListener('input-blur', this.handleInputBlur as EventListener);
143
+ if (this.keyboardBlurTimeout) {
144
+ clearTimeout(this.keyboardBlurTimeout);
145
+ }
146
+ }
147
+
148
+ private handleInputFocus = () => {
149
+ if (this.keyboardBlurTimeout) {
150
+ clearTimeout(this.keyboardBlurTimeout);
151
+ this.keyboardBlurTimeout = undefined;
152
+ }
153
+ this.isKeyboardVisible = true;
154
+ };
155
+
156
+ private handleInputBlur = () => {
157
+ if (this.keyboardBlurTimeout) {
158
+ clearTimeout(this.keyboardBlurTimeout);
159
+ }
160
+ this.keyboardBlurTimeout = window.setTimeout(() => {
161
+ this.isKeyboardVisible = false;
162
+ this.keyboardBlurTimeout = undefined;
163
+ }, 150);
164
+ };
165
+
166
+ updated(changedProperties: Map<string, any>) {
167
+ super.updated(changedProperties);
168
+ if (changedProperties.has('isKeyboardVisible')) {
169
+ if (this.isKeyboardVisible) {
170
+ this.setAttribute('keyboard-visible', '');
171
+ } else {
172
+ this.removeAttribute('keyboard-visible');
173
+ }
174
+ }
175
+ if (changedProperties.has('isOpen')) {
176
+ if (this.isOpen) {
177
+ this.classList.add('open');
178
+ this.dispatchEvent(new CustomEvent('opened', { bubbles: true, composed: true }));
179
+ } else {
180
+ this.classList.remove('open');
181
+ this.dispatchEvent(new CustomEvent('closed', { bubbles: true, composed: true }));
182
+ }
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Opens the combox
188
+ */
189
+ public open() {
190
+ this.isOpen = true;
191
+ }
192
+
193
+ /**
194
+ * Closes the combox
195
+ */
196
+ public close() {
197
+ this.isOpen = false;
198
+ this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true }));
199
+ }
200
+
201
+ /**
202
+ * Toggles the combox open/closed state
203
+ */
204
+ public toggle() {
205
+ if (this.isOpen) {
206
+ this.close();
207
+ } else {
208
+ this.open();
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Returns whether the combox is currently open
214
+ */
215
+ public getIsOpen(): boolean {
216
+ return this.isOpen;
217
+ }
218
+
130
219
  public static styles = [
131
- cssManager.defaultStyles,
220
+ ...sioElementStyles,
132
221
  css`
133
222
  :host {
134
- display: block;
135
- height: 600px;
136
- width: 800px;
137
- background: ${bdTheme('background')};
138
- border-radius: ${unsafeCSS(radius['2xl'])};
139
- border: 1px solid ${bdTheme('border')};
140
- box-shadow: ${unsafeCSS(shadows.xl)};
223
+ position: fixed;
224
+ bottom: 96px;
225
+ right: 24px;
226
+ width: 380px;
227
+ height: 620px;
228
+ display: flex;
229
+ flex-direction: column;
230
+ background: var(--sio-surface);
231
+ border-radius: 20px;
141
232
  overflow: hidden;
142
- font-family: ${unsafeCSS(fontFamilies.sans)};
143
- position: relative;
233
+ box-shadow:
234
+ 0 24px 64px rgba(0, 0, 0, 0.22),
235
+ 0 0 0 0.5px rgba(0, 0, 0, 0.08);
236
+ font-family: var(--sio-font-sf);
144
237
  transform-origin: bottom right;
145
- }
146
-
147
- :host(.animate-in) {
148
- animation: scaleIn 300ms cubic-bezier(0.34, 1.56, 0.64, 1);
149
- }
150
-
151
- @keyframes scaleIn {
152
- from {
153
- opacity: 0;
154
- transform: scale(0.9) translateY(10px);
155
- }
156
- to {
157
- opacity: 1;
158
- transform: scale(1) translateY(0);
159
- }
160
- }
161
-
162
- :host::before {
163
- content: '';
164
- position: absolute;
165
- inset: 0;
166
- border-radius: ${unsafeCSS(radius['2xl'])};
167
- padding: 1px;
168
- background: linear-gradient(145deg, ${bdTheme('border')}, transparent 50%);
169
- -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
170
- -webkit-mask-composite: exclude;
171
- mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
172
- mask-composite: exclude;
173
- opacity: 0.5;
238
+ z-index: 10001;
239
+
240
+ /* hidden by default */
241
+ opacity: 0;
174
242
  pointer-events: none;
243
+ transform: scale(0.95) translateY(10px);
244
+ transition:
245
+ opacity 200ms ease,
246
+ transform 200ms ease;
247
+ }
248
+
249
+ :host(.open) {
250
+ opacity: 1;
251
+ pointer-events: all;
252
+ transform: scale(1) translateY(0);
175
253
  }
176
254
 
177
255
  .container {
178
- display: flex;
179
- height: 100%;
180
- overflow: visible;
181
- border-radius: ${unsafeCSS(radius['2xl'])};
256
+ position: relative;
257
+ flex: 1;
258
+ min-height: 0;
259
+ overflow: hidden;
182
260
  }
183
261
 
184
- /* Responsive layout */
185
- @media (max-width: 600px) {
186
- :host {
187
- width: 100%;
188
- height: 100%;
189
- border-radius: 0;
190
- }
262
+ sio-conversation-selector,
263
+ sio-conversation-view {
264
+ position: absolute;
265
+ top: 0;
266
+ width: 100%;
267
+ height: 100%;
268
+ transition:
269
+ left 280ms cubic-bezier(0.4, 0, 0.2, 1),
270
+ opacity 200ms ease;
271
+ }
191
272
 
192
- .container {
193
- position: relative;
194
- }
273
+ .container.show-list sio-conversation-selector {
274
+ left: 0;
275
+ opacity: 1;
276
+ }
195
277
 
196
- sio-conversation-selector {
197
- position: absolute;
198
- width: 100%;
199
- height: 100%;
200
- transition: left 300ms ease, opacity 200ms ease;
201
- }
278
+ .container.show-list sio-conversation-view {
279
+ left: 100%;
280
+ opacity: 0;
281
+ pointer-events: none;
282
+ }
202
283
 
203
- sio-conversation-view {
204
- position: absolute;
205
- width: 100%;
206
- height: 100%;
207
- transition: left 300ms ease, opacity 200ms ease;
208
- }
284
+ .container.show-conversation sio-conversation-selector {
285
+ left: -100%;
286
+ opacity: 0;
287
+ pointer-events: none;
288
+ }
209
289
 
210
- /* Mobile navigation states */
211
- .container.show-list sio-conversation-selector {
212
- left: 0;
213
- opacity: 1;
214
- }
290
+ .container.show-conversation sio-conversation-view {
291
+ left: 0;
292
+ opacity: 1;
293
+ }
215
294
 
216
- .container.show-list sio-conversation-view {
217
- left: 100%;
218
- opacity: 0;
219
- }
295
+ /* footer — every state (v7 SIOWidgetShell) */
296
+ .footer {
297
+ flex-shrink: 0;
298
+ padding: 8px 0 10px;
299
+ display: flex;
300
+ align-items: center;
301
+ justify-content: center;
302
+ gap: 5px;
303
+ font-size: 10.5px;
304
+ color: color-mix(in oklab, var(--sio-label-3) 75%, transparent);
305
+ user-select: none;
306
+ }
220
307
 
221
- .container.show-conversation sio-conversation-selector {
222
- left: -100%;
223
- opacity: 0;
224
- }
308
+ .footer svg {
309
+ display: block;
310
+ }
225
311
 
226
- .container.show-conversation sio-conversation-view {
227
- left: 0;
228
- opacity: 1;
229
- }
312
+ .footer span {
313
+ font-weight: 500;
314
+ }
315
+ `,
316
+ // phablet: full-screen sheet
317
+ cssManager.cssForPhablet(css`
318
+ :host {
319
+ top: 0;
320
+ left: 0;
321
+ right: 0;
322
+ bottom: 0;
323
+ width: 100vw;
324
+ height: 100vh;
325
+ height: 100dvh;
326
+ border-radius: 0;
327
+ transform-origin: center center;
230
328
  }
231
329
 
232
- @media (min-width: 601px) {
233
- sio-conversation-selector {
234
- width: 320px;
235
- flex-shrink: 0;
236
- }
330
+ :host(.open) {
331
+ transform: scale(1) translateY(0);
332
+ }
237
333
 
238
- sio-conversation-view {
239
- flex: 1;
240
- }
334
+ :host([keyboard-visible]) {
335
+ height: 100vh;
336
+ height: 100dvh;
241
337
  }
242
- `,
338
+ `),
243
339
  ];
244
340
 
245
341
  public render(): TemplateResult {
246
342
  const selectedConversation = this.selectedConversationId
247
- ? this.conversations.find(c => c.id === this.selectedConversationId)
343
+ ? this.conversations.find((c) => c.id === this.selectedConversationId)
248
344
  : null;
249
345
 
250
346
  const conversationData: IConversationData | null = selectedConversation
251
347
  ? {
252
348
  id: selectedConversation.id,
253
349
  title: selectedConversation.title,
254
- messages: this.messages[selectedConversation.id] || []
350
+ agentInitials: selectedConversation.initials,
351
+ agentTint: selectedConversation.tint,
352
+ dateStamp: 'Today, 14:02',
353
+ messages: this.messages[selectedConversation.id] || [],
255
354
  }
256
355
  : null;
257
356
 
@@ -263,17 +362,30 @@ export class SioCombox extends DeesElement {
263
362
  .conversations=${this.conversations}
264
363
  .selectedConversationId=${this.selectedConversationId}
265
364
  @conversation-selected=${this.handleConversationSelected}
365
+ @new-conversation=${this.handleNewConversation}
366
+ @close=${() => this.close()}
266
367
  ></sio-conversation-selector>
267
-
368
+
268
369
  <sio-conversation-view
269
370
  .conversation=${conversationData}
371
+ ?away=${this.away}
270
372
  @back=${this.handleBack}
271
373
  @send-message=${this.handleSendMessage}
272
374
  @open-image=${this.handleOpenImage}
273
375
  @open-file=${this.handleOpenImage}
274
376
  ></sio-conversation-view>
275
377
  </div>
276
-
378
+
379
+ <div class="footer">
380
+ <!-- mini social.io brand glyph -->
381
+ <svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
382
+ <path
383
+ d="M12 3C7 3 3 6.6 3 11c0 2.5 1.3 4.7 3.3 6.2-.1 1-.5 2.1-1.3 2.9 1.6-.1 3-.7 4-1.4.9.2 1.9.3 3 .3 5 0 9-3.6 9-8S17 3 12 3z"
384
+ />
385
+ </svg>
386
+ <span>Powered by social.io</span>
387
+ </div>
388
+
277
389
  <sio-image-lightbox></sio-image-lightbox>
278
390
  `;
279
391
  }
@@ -281,24 +393,39 @@ export class SioCombox extends DeesElement {
281
393
  private handleConversationSelected(event: CustomEvent) {
282
394
  const conversation = event.detail.conversation as IConversation;
283
395
  this.selectedConversationId = conversation.id;
284
-
396
+
285
397
  // Mark conversation as read
286
- const convIndex = this.conversations.findIndex(c => c.id === conversation.id);
398
+ const convIndex = this.conversations.findIndex((c) => c.id === conversation.id);
287
399
  if (convIndex !== -1) {
288
400
  this.conversations[convIndex] = { ...this.conversations[convIndex], unread: false };
289
401
  this.conversations = [...this.conversations];
290
402
  }
291
403
  }
292
404
 
405
+ private handleNewConversation = () => {
406
+ const newConversation: IConversation = {
407
+ id: `new-${Date.now()}`,
408
+ title: 'Lena · Orbit',
409
+ lastMessage: '',
410
+ time: 'now',
411
+ initials: 'LP',
412
+ tint: '#FF2D55',
413
+ status: 'new',
414
+ };
415
+ this.conversations = [newConversation, ...this.conversations];
416
+ this.messages = { ...this.messages, [newConversation.id]: [] };
417
+ this.selectedConversationId = newConversation.id;
418
+ };
419
+
293
420
  private handleBack() {
294
- // For mobile view, go back to conversation list
421
+ // back to the home / welcome state
295
422
  this.selectedConversationId = null;
296
423
  }
297
424
 
298
425
  private handleSendMessage(event: CustomEvent) {
299
426
  const message = event.detail.message as IMessage;
300
427
  const conversationId = this.selectedConversationId;
301
-
428
+
302
429
  if (conversationId) {
303
430
  // Add message to the conversation
304
431
  if (!this.messages[conversationId]) {
@@ -306,47 +433,43 @@ export class SioCombox extends DeesElement {
306
433
  }
307
434
  this.messages[conversationId] = [...this.messages[conversationId], message];
308
435
  this.messages = { ...this.messages };
309
-
436
+
310
437
  // Update conversation's last message
311
- const convIndex = this.conversations.findIndex(c => c.id === conversationId);
438
+ const convIndex = this.conversations.findIndex((c) => c.id === conversationId);
312
439
  if (convIndex !== -1) {
313
440
  this.conversations[convIndex] = {
314
441
  ...this.conversations[convIndex],
315
442
  lastMessage: message.text,
316
- time: 'Just now'
443
+ time: 'now',
317
444
  };
318
445
  // Move conversation to top
319
446
  const [conv] = this.conversations.splice(convIndex, 1);
320
447
  this.conversations = [conv, ...this.conversations];
321
448
  }
322
-
323
- // Simulate a response after a delay (remove in production)
324
- setTimeout(() => {
325
- const responseMessage: IMessage = {
326
- id: Date.now().toString(),
327
- text: 'Thanks for your message! We\'ll get back to you shortly.',
328
- sender: 'support',
329
- time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
330
- };
331
-
332
- this.messages[conversationId] = [...this.messages[conversationId], responseMessage];
333
- this.messages = { ...this.messages };
334
- }, 3000);
335
449
  }
336
450
  }
337
451
 
452
+ /**
453
+ * Append an inbound (support-side) message to a conversation.
454
+ * The host application calls this when a reply arrives.
455
+ */
456
+ public receiveMessage(conversationId: string, message: IMessage): void {
457
+ this.messages[conversationId] = [...(this.messages[conversationId] ?? []), message];
458
+ this.messages = { ...this.messages };
459
+ }
460
+
338
461
  private handleOpenImage(event: CustomEvent) {
339
462
  const attachment = event.detail.attachment as IAttachment;
340
463
  const lightbox = this.shadowRoot?.querySelector('sio-image-lightbox') as SioImageLightbox;
341
-
464
+
342
465
  if (lightbox && attachment) {
343
- const lightboxFile: ILightboxImage = {
466
+ const lightboxFile: ILightboxFile = {
344
467
  url: attachment.url,
345
468
  name: attachment.name,
346
469
  size: attachment.size,
347
- type: attachment.type
470
+ type: attachment.type,
348
471
  };
349
472
  lightbox.open(lightboxFile);
350
473
  }
351
474
  }
352
- }
475
+ }