@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.
Files changed (130) hide show
  1. package/.smartconfig.json +51 -0
  2. package/dist_bundle/bundle.js +5455 -3248
  3. package/dist_ts_web/00_commitinfo_data.js +3 -3
  4. package/dist_ts_web/elements/index.d.ts +21 -3
  5. package/dist_ts_web/elements/index.js +28 -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 +38 -0
  15. package/dist_ts_web/elements/sio-channel-rail.js +288 -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-logo-render.d.ts +42 -0
  45. package/dist_ts_web/elements/sio-logo-render.js +306 -0
  46. package/dist_ts_web/elements/sio-logo.d.ts +64 -0
  47. package/dist_ts_web/elements/sio-logo.js +393 -0
  48. package/dist_ts_web/elements/sio-message-input.d.ts +25 -4
  49. package/dist_ts_web/elements/sio-message-input.js +231 -226
  50. package/dist_ts_web/elements/sio-paper-thumb.d.ts +24 -0
  51. package/dist_ts_web/elements/sio-paper-thumb.js +293 -0
  52. package/dist_ts_web/elements/sio-pdf-viewer.d.ts +2 -0
  53. package/dist_ts_web/elements/sio-pdf-viewer.js +344 -244
  54. package/dist_ts_web/elements/sio-segmented.d.ts +32 -0
  55. package/dist_ts_web/elements/sio-segmented.js +217 -0
  56. package/dist_ts_web/elements/sio-step-timeline.d.ts +28 -0
  57. package/dist_ts_web/elements/sio-step-timeline.js +222 -0
  58. package/dist_ts_web/elements/sio-tab-bar.d.ts +36 -0
  59. package/dist_ts_web/elements/sio-tab-bar.js +232 -0
  60. package/dist_ts_web/elements/sio-unread-badge.d.ts +25 -0
  61. package/dist_ts_web/elements/sio-unread-badge.js +203 -0
  62. package/dist_ts_web/elements/sio-waveform.d.ts +34 -0
  63. package/dist_ts_web/elements/sio-waveform.js +304 -0
  64. package/dist_ts_web/elements/tokens.d.ts +70 -0
  65. package/dist_ts_web/elements/tokens.js +277 -0
  66. package/dist_ts_web/pages/firstpage.d.ts +5 -0
  67. package/dist_ts_web/pages/firstpage.js +21 -2
  68. package/dist_ts_web/pages/mainpage.d.ts +6 -0
  69. package/dist_ts_web/pages/mainpage.js +111 -39
  70. package/{license → license.md} +3 -1
  71. package/package.json +23 -29
  72. package/readme.hints.md +82 -0
  73. package/readme.md +113 -146
  74. package/ts_web/00_commitinfo_data.ts +2 -2
  75. package/ts_web/elements/index.ts +31 -7
  76. package/ts_web/elements/sio-ai-card.ts +175 -0
  77. package/ts_web/elements/sio-avatar.ts +295 -0
  78. package/ts_web/elements/sio-bucket-header.ts +157 -0
  79. package/ts_web/elements/sio-button.ts +149 -127
  80. package/ts_web/elements/sio-channel-rail.ts +256 -0
  81. package/ts_web/elements/sio-chip.ts +134 -0
  82. package/ts_web/elements/sio-cmdk.ts +461 -0
  83. package/ts_web/elements/sio-combox.ts +327 -204
  84. package/ts_web/elements/sio-composer.ts +306 -0
  85. package/ts_web/elements/sio-conversation-selector.ts +350 -228
  86. package/ts_web/elements/sio-conversation-view.ts +573 -457
  87. package/ts_web/elements/sio-dropdown-menu.ts +112 -130
  88. package/ts_web/elements/sio-fab.ts +192 -227
  89. package/ts_web/elements/sio-glass-toolbar.ts +140 -0
  90. package/ts_web/elements/sio-icon.ts +147 -25
  91. package/ts_web/elements/sio-image-lightbox.ts +62 -64
  92. package/ts_web/elements/sio-inset-list.ts +140 -0
  93. package/ts_web/elements/sio-kbd.ts +80 -0
  94. package/ts_web/elements/sio-list-row.ts +393 -0
  95. package/ts_web/elements/sio-logo-render.ts +367 -0
  96. package/ts_web/elements/sio-logo.ts +311 -0
  97. package/ts_web/elements/sio-message-input.ts +142 -221
  98. package/ts_web/elements/sio-paper-thumb.ts +244 -0
  99. package/ts_web/elements/sio-pdf-viewer.ts +58 -60
  100. package/ts_web/elements/sio-segmented.ts +183 -0
  101. package/ts_web/elements/sio-step-timeline.ts +179 -0
  102. package/ts_web/elements/sio-tab-bar.ts +195 -0
  103. package/ts_web/elements/sio-unread-badge.ts +147 -0
  104. package/ts_web/elements/sio-waveform.ts +239 -0
  105. package/ts_web/elements/tokens.ts +297 -0
  106. package/ts_web/pages/firstpage.ts +20 -1
  107. package/ts_web/pages/mainpage.ts +111 -39
  108. package/dist_bundle/bundle.js.map +0 -7
  109. package/dist_ts_web/elements/00colors.d.ts +0 -108
  110. package/dist_ts_web/elements/00colors.js +0 -137
  111. package/dist_ts_web/elements/00fonts.d.ts +0 -60
  112. package/dist_ts_web/elements/00fonts.js +0 -129
  113. package/dist_ts_web/elements/00tokens.d.ts +0 -123
  114. package/dist_ts_web/elements/00tokens.js +0 -176
  115. package/dist_ts_web/elements/sio-conversation-list.d.ts +0 -20
  116. package/dist_ts_web/elements/sio-conversation-list.js +0 -495
  117. package/dist_ts_web/elements/sio-recorder.d.ts +0 -60
  118. package/dist_ts_web/elements/sio-recorder.js +0 -173
  119. package/dist_ts_web/elements/sio-subwidget-conversations.d.ts +0 -16
  120. package/dist_ts_web/elements/sio-subwidget-conversations.js +0 -350
  121. package/dist_ts_web/elements/sio-subwidget-onboardme.d.ts +0 -7
  122. package/dist_ts_web/elements/sio-subwidget-onboardme.js +0 -243
  123. package/dist_watch/bundle.js +0 -81694
  124. package/dist_watch/bundle.js.map +0 -7
  125. package/dist_watch/index.html +0 -25
  126. package/npmextra.json +0 -19
  127. package/ts_web/elements/00colors.ts +0 -164
  128. package/ts_web/elements/00fonts.ts +0 -145
  129. package/ts_web/elements/00tokens.ts +0 -197
  130. package/ts_web/elements/sio-recorder.ts +0 -213
@@ -1,24 +1,19 @@
1
1
  import {
2
2
  DeesElement,
3
- property,
4
3
  html,
4
+ property,
5
5
  customElement,
6
- type TemplateResult,
7
- cssManager,
8
6
  css,
7
+ cssManager,
8
+ type TemplateResult,
9
9
  } from '@design.estate/dees-element';
10
10
  import * as domtools from '@design.estate/dees-domtools';
11
11
 
12
+ import { sioElementStyles } from './tokens.js';
12
13
  import { SioCombox } from './sio-combox.js';
13
- import { SioIcon } from './sio-icon.js';
14
- import { state } from '@design.estate/dees-element';
14
+ import './sio-icon.js';
15
+ import './sio-unread-badge.js';
15
16
  SioCombox;
16
- SioIcon;
17
-
18
- // Import design tokens
19
- import { colors, bdTheme } from './00colors.js';
20
- import { spacing, radius, shadows, transitions, sizes } from './00tokens.js';
21
- import { fontFamilies, typography } from './00fonts.js';
22
17
 
23
18
  declare global {
24
19
  interface HTMLElementTagNameMap {
@@ -26,219 +21,173 @@ declare global {
26
21
  }
27
22
  }
28
23
 
24
+ /**
25
+ * sio-fab — the widget launcher bubble (v7 SocialIOWidget.jsx SIOWidgetLauncher).
26
+ *
27
+ * 56px circle on solid system tint with the white social.io chat glyph,
28
+ * tinted ambient shadow (40% tint) plus a soft contact shadow. Unread
29
+ * count sits top-right as a red pill with a 2px page-surface ring.
30
+ * While the combox is open the glyph swaps to a chevron-down.
31
+ *
32
+ * Owns the SioCombox singleton: creates it on <body>, toggles it on
33
+ * click and on Ctrl+S, and mirrors its open state.
34
+ */
29
35
  @customElement('sio-fab')
30
36
  export class SioFab extends DeesElement {
37
+ public static demo = () => html` <sio-fab .unread=${1}></sio-fab> `;
38
+
39
+ /** mirrors the combox open state (chevron glyph while open) */
31
40
  @property({ type: Boolean })
32
41
  public accessor showCombox = false;
33
42
 
34
- @state()
35
- private accessor hasShownOnce = false;
36
-
37
- @state()
38
- private accessor shouldPulse = false;
39
-
40
- public static demo = () => html` <sio-fab .showCombox=${true}></sio-fab> `;
43
+ /** unread conversation count — red pill on the launcher while closed */
44
+ @property({ type: Number })
45
+ public accessor unread = 0;
41
46
 
42
47
  constructor() {
43
48
  super();
44
49
  domtools.DomTools.setupDomTools();
45
50
  }
46
51
 
47
- public render(): TemplateResult {
48
- return html`
49
- ${domtools.elementBasic.styles}
50
- <style>
51
- :host {
52
- will-change: transform;
53
- position: absolute;
54
- display: block;
55
- bottom: 20px;
56
- right: 20px;
57
- z-index: 10000;
58
- color: #fff;
59
- --fab-gradient-start: #6366f1;
60
- --fab-gradient-mid: #8b5cf6;
61
- --fab-gradient-end: #a855f7;
62
- --fab-gradient-hover-end: #c026d3;
63
- --fab-shadow-color: rgba(139, 92, 246, 0.25);
64
- }
52
+ public static styles = [
53
+ ...sioElementStyles,
54
+ css`
55
+ :host {
56
+ position: absolute;
57
+ bottom: 24px;
58
+ right: 24px;
59
+ z-index: 10000;
60
+ display: block;
61
+ width: 56px;
62
+ height: 56px;
63
+ }
65
64
 
66
- #mainbox {
67
- transition: ${transitions.all};
68
- position: absolute;
69
- bottom: 0px;
70
- right: 0px;
71
- height: 60px;
72
- width: 60px;
73
- box-shadow: 0 4px 16px -2px rgba(0, 0, 0, 0.1), 0 2px 8px -2px rgba(0, 0, 0, 0.06);
74
- line-height: 60px;
75
- text-align: center;
76
- cursor: pointer;
77
- background: linear-gradient(135deg, var(--fab-gradient-start) 0%, var(--fab-gradient-mid) 50%, var(--fab-gradient-end) 100%);
78
- color: white;
79
- border-radius: ${radius.full};
80
- user-select: none;
81
- border: none;
82
- animation: fabEntrance 300ms cubic-bezier(0.4, 0, 0.2, 1);
83
- overflow: hidden;
84
- position: relative;
85
- backdrop-filter: blur(10px);
86
- -webkit-backdrop-filter: blur(10px);
87
- }
88
-
89
- #mainbox::before {
90
- content: '';
91
- position: absolute;
92
- top: 0;
93
- left: 0;
94
- right: 0;
95
- bottom: 0;
96
- background: linear-gradient(135deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 50%);
97
- opacity: 0;
98
- transition: opacity 200ms ease;
99
- }
100
-
101
- #mainbox::after {
102
- content: '';
103
- position: absolute;
104
- top: -4px;
105
- left: -4px;
106
- right: -4px;
107
- bottom: -4px;
108
- background: linear-gradient(135deg, var(--fab-gradient-start), var(--fab-gradient-end));
109
- border-radius: inherit;
110
- z-index: -1;
111
- opacity: 0;
112
- filter: blur(12px);
113
- transition: opacity 300ms ease;
114
- }
115
-
116
- #mainbox:hover::before {
117
- opacity: 1;
118
- }
119
-
120
- #mainbox:hover::after {
121
- opacity: 0.3;
122
- }
123
-
124
- @keyframes fabEntrance {
125
- from {
126
- transform: scale(0.8);
127
- opacity: 0;
128
- }
129
- to {
130
- transform: scale(1);
131
- opacity: 1;
132
- }
133
- }
65
+ .fab {
66
+ position: relative;
67
+ width: 56px;
68
+ height: 56px;
69
+ border-radius: 28px;
70
+ border: none;
71
+ padding: 0;
72
+ background: var(--sio-tint);
73
+ color: #fff; /* on-tint constant across themes (v7 launcher glyph) */
74
+ cursor: pointer;
75
+ user-select: none;
76
+ box-shadow:
77
+ 0 8px 24px color-mix(in oklab, var(--sio-tint) 40%, transparent),
78
+ 0 2px 6px rgba(0, 0, 0, 0.12);
79
+ transition:
80
+ background 120ms ease,
81
+ transform 120ms ease,
82
+ box-shadow 120ms ease;
83
+ animation: fabEntrance 240ms cubic-bezier(0.4, 0, 0.2, 1);
84
+ }
134
85
 
135
- #mainbox:hover {
136
- transform: scale(1.02);
137
- background: linear-gradient(135deg, var(--fab-gradient-start) 0%, var(--fab-gradient-mid) 50%, var(--fab-gradient-hover-end) 100%);
138
- }
139
-
140
- #mainbox:hover {
141
- box-shadow: 0 8px 20px -4px var(--fab-shadow-color);
142
- }
86
+ .fab:hover {
87
+ background: var(--sio-tint-hover);
88
+ transform: scale(1.03);
89
+ }
143
90
 
144
- #mainbox:active {
145
- transform: scale(0.98);
146
- box-shadow: 0 4px 12px -2px var(--fab-shadow-color);
147
- }
148
-
149
- #mainbox.pulse::after {
150
- animation: fabPulse 0.6s ease-out forwards;
151
- }
152
-
153
- @keyframes fabPulse {
154
- 0% {
155
- box-shadow: 0 0 0 0 rgba(139, 92, 246, 0.4);
156
- opacity: 1;
157
- }
158
- 100% {
159
- box-shadow: 0 0 0 12px rgba(139, 92, 246, 0);
160
- opacity: 0;
161
- }
162
- }
91
+ .fab:active {
92
+ transform: scale(0.96);
93
+ }
163
94
 
164
- #mainbox .icon {
165
- position: absolute;
166
- top: 0px;
167
- left: 0px;
168
- will-change: transform, opacity;
169
- transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1);
170
- height: 100%;
171
- width: 100%;
172
- display: flex;
173
- align-items: center;
174
- justify-content: center;
95
+ .fab:focus-visible {
96
+ outline: 2px solid color-mix(in oklab, var(--sio-tint) 55%, transparent);
97
+ outline-offset: 2px;
98
+ }
99
+
100
+ @keyframes fabEntrance {
101
+ from {
102
+ transform: scale(0.8);
103
+ opacity: 0;
175
104
  }
105
+ to {
106
+ transform: scale(1);
107
+ opacity: 1;
108
+ }
109
+ }
176
110
 
111
+ .glyph {
112
+ position: absolute;
113
+ inset: 0;
114
+ display: flex;
115
+ align-items: center;
116
+ justify-content: center;
117
+ transition:
118
+ opacity 160ms ease,
119
+ transform 160ms cubic-bezier(0.4, 0, 0.2, 1);
120
+ }
177
121
 
178
- #mainbox .icon.open {
179
- opacity: ${this.showCombox ? '0' : '1'};
180
- transform: ${this.showCombox ? 'rotate(45deg) scale(0.9)' : 'rotate(0deg) scale(1)'};
181
- }
122
+ .glyph svg {
123
+ display: block;
124
+ }
182
125
 
183
- #mainbox .icon.close {
184
- opacity: ${this.showCombox ? '1' : '0'};
185
- transform: ${this.showCombox ? 'rotate(0deg) scale(1)' : 'rotate(-45deg) scale(0.9)'};
186
- }
126
+ .glyph.chat {
127
+ opacity: 1;
128
+ transform: rotate(0deg) scale(1);
129
+ }
187
130
 
188
- #mainbox .icon sio-icon {
189
- color: white;
190
- filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));
191
- }
131
+ .glyph.chevron {
132
+ opacity: 0;
133
+ transform: rotate(-45deg) scale(0.8);
134
+ }
192
135
 
193
- #mainbox .icon.close sio-icon {
194
- transform: scale(1);
195
- }
136
+ :host(.combox-open) .glyph.chat {
137
+ opacity: 0;
138
+ transform: rotate(45deg) scale(0.8);
139
+ }
196
140
 
197
- #comboxContainer {
198
- position: absolute;
199
- bottom: 0;
200
- right: 0;
201
- pointer-events: none;
202
- }
141
+ :host(.combox-open) .glyph.chevron {
142
+ opacity: 1;
143
+ transform: rotate(0deg) scale(1);
144
+ }
203
145
 
204
- #comboxContainer sio-combox {
205
- position: absolute;
206
- bottom: calc(60px + ${spacing["4"]});
207
- right: 0;
208
- transition: ${transitions.all};
209
- will-change: transform;
210
- transform: translateY(${spacing["5"]});
211
- opacity: 0;
212
- pointer-events: none;
213
- }
146
+ .unread {
147
+ position: absolute;
148
+ top: -3px;
149
+ right: -3px;
150
+ pointer-events: none;
151
+ transition: opacity 120ms ease;
152
+ }
214
153
 
215
- #comboxContainer.show {
216
- pointer-events: all;
217
- }
154
+ :host(.combox-open) .unread {
155
+ opacity: 0;
156
+ }
157
+ `,
158
+ cssManager.cssForPhablet(css`
159
+ :host {
160
+ bottom: 16px;
161
+ right: 16px;
162
+ }
163
+ `),
164
+ ];
218
165
 
219
- #comboxContainer.show sio-combox {
220
- transform: translateY(0px);
221
- opacity: 1;
222
- pointer-events: all;
223
- }
224
- </style>
225
- <div id="mainbox"
226
- class="${this.shouldPulse ? 'pulse' : ''}"
227
- @click=${this.toggleCombox}
228
- @animationend=${() => { this.shouldPulse = false; }}
229
- >
230
- <div class="icon open">
231
- <sio-icon icon="message-square" size="28"></sio-icon>
232
- </div>
233
- <div class="icon close">
234
- <sio-icon icon="x" size="22"></sio-icon>
235
- </div>
236
- </div>
237
- <div id="comboxContainer" class="${this.showCombox ? 'show' : ''}">
238
- ${this.showCombox || this.hasShownOnce ? html`
239
- <sio-combox @close=${() => this.showCombox = false}></sio-combox>
240
- ` : ''}
241
- </div>
166
+ public render(): TemplateResult {
167
+ return html`
168
+ <button class="fab" aria-label="Open messenger" @click=${this.toggleCombox}>
169
+ <span class="glyph chat">
170
+ <!-- social.io chat glyph (v7 brand mark) -->
171
+ <svg width="26" height="26" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
172
+ <path
173
+ 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"
174
+ />
175
+ </svg>
176
+ </span>
177
+ <span class="glyph chevron">
178
+ <sio-icon icon="chevron-down" size="22" .strokeWidth=${2.4}></sio-icon>
179
+ </span>
180
+ ${this.unread > 0
181
+ ? html`
182
+ <sio-unread-badge
183
+ class="unread"
184
+ count=${this.unread}
185
+ channel="alert"
186
+ ring
187
+ ></sio-unread-badge>
188
+ `
189
+ : ''}
190
+ </button>
242
191
  `;
243
192
  }
244
193
 
@@ -246,40 +195,56 @@ export class SioFab extends DeesElement {
246
195
  * toggles the combox
247
196
  */
248
197
  public async toggleCombox() {
249
- console.log('toggle combox');
250
- const wasOpen = this.showCombox;
251
- this.showCombox = !this.showCombox;
252
- if (this.showCombox) {
253
- this.hasShownOnce = true;
254
- if (!wasOpen) {
255
- this.shouldPulse = true;
256
- }
198
+ const combox = SioCombox.getInstance();
199
+ if (combox) {
200
+ combox.toggle();
201
+ this.showCombox = combox.getIsOpen();
257
202
  }
258
203
  }
259
204
 
260
- public async firstUpdated(args: any) {
205
+ private comboxCloseHandler = () => {
206
+ this.showCombox = false;
207
+ };
208
+ private comboxRef: SioCombox | null = null;
209
+
210
+ public async firstUpdated(args: Map<PropertyKey, unknown>) {
261
211
  super.firstUpdated(args);
262
- const domtools = await this.domtoolsPromise;
263
-
264
- // Set up keyboard shortcut
265
- domtools.keyboard
266
- .on([domtools.keyboard.keyEnum.Ctrl, domtools.keyboard.keyEnum.S])
267
- .subscribe(() => {
268
- this.toggleCombox();
269
- });
212
+ const domtoolsInstance = await this.domtoolsPromise;
213
+
214
+ // Create the singleton combox on body
215
+ const combox = SioCombox.createOnBody();
216
+
217
+ // Listen for close events (removed on disconnect — the singleton outlives us)
218
+ this.comboxRef = combox;
219
+ combox.addEventListener('close', this.comboxCloseHandler);
220
+
221
+ // Set up keyboard shortcut; the subscription is auto-unsubscribed by
222
+ // DeesElement on disconnect, otherwise the global keyboard singleton
223
+ // would retain this element (and fire once per ever-mounted fab)
224
+ const keyboard = domtoolsInstance.keyboard;
225
+ if (keyboard) {
226
+ this.rxSubscriptions.push(
227
+ keyboard.on([keyboard.keyEnum.Ctrl, keyboard.keyEnum.S]).subscribe(() => {
228
+ this.toggleCombox();
229
+ }),
230
+ );
231
+ }
232
+ }
233
+
234
+ public async disconnectedCallback() {
235
+ await super.disconnectedCallback();
236
+ if (this.comboxRef) {
237
+ this.comboxRef.removeEventListener('close', this.comboxCloseHandler);
238
+ this.comboxRef = null;
239
+ }
270
240
  }
271
-
272
- public async updated(changedProperties: Map<string | number | symbol, unknown>) {
241
+
242
+ public async updated(changedProperties: Map<PropertyKey, unknown>) {
273
243
  super.updated(changedProperties);
274
-
275
- // Set reference object when combox is rendered
276
- if ((changedProperties.has('showCombox') || changedProperties.has('hasShownOnce')) &&
277
- (this.showCombox || this.hasShownOnce)) {
278
- const sioCombox: SioCombox = this.shadowRoot.querySelector('sio-combox');
279
- const mainBox: HTMLElement = this.shadowRoot.querySelector('#mainbox');
280
- if (sioCombox && mainBox && !sioCombox.referenceObject) {
281
- sioCombox.referenceObject = mainBox;
282
- }
244
+
245
+ // Mirror combox state onto the host for the glyph crossfade
246
+ if (changedProperties.has('showCombox')) {
247
+ this.classList.toggle('combox-open', this.showCombox);
283
248
  }
284
249
  }
285
250
  }
@@ -0,0 +1,140 @@
1
+ import {
2
+ DeesElement,
3
+ html,
4
+ customElement,
5
+ css,
6
+ type TemplateResult,
7
+ } from '@design.estate/dees-element';
8
+ import { sioElementStyles, sioToolbarGlass } from './tokens.js';
9
+ import './sio-icon.js';
10
+
11
+ declare global {
12
+ interface HTMLElementTagNameMap {
13
+ 'sio-glass-toolbar': SioGlassToolbar;
14
+ }
15
+ }
16
+
17
+ /**
18
+ * sio-glass-toolbar — the 44px liquid-glass command bar that tops every
19
+ * social.io pane (v7 prototype: SocialIODesktop.jsx thread/list toolbars,
20
+ * styles/socialio.css `.sio-toolbar-glass`).
21
+ *
22
+ * The bar is deliberately chromeless: it only provides the toolbar glass
23
+ * recipe (blur 28px, saturate 180%) with the 0.5px bottom hairline and a
24
+ * three-region layout. Content flows into the `start` (leading actions),
25
+ * `center` (title / context) and `end` (trailing actions, search) slots.
26
+ */
27
+ @customElement('sio-glass-toolbar')
28
+ export class SioGlassToolbar extends DeesElement {
29
+ public static demo = () => html`
30
+ <div
31
+ style="display: flex; flex-direction: column; gap: 24px; max-width: 720px; font-family: var(--sio-font-sf); color: var(--sio-label);"
32
+ >
33
+ <!-- desktop thread pane: leading actions · trailing search -->
34
+ <div
35
+ style="position: relative; height: 176px; border-radius: 14px; overflow: hidden; border: var(--sio-hairline) solid var(--sio-separator); background: var(--sio-surface);"
36
+ >
37
+ <div style="padding: 60px 28px 0;">
38
+ <div style="font-size: 24px; font-weight: 700; letter-spacing: -0.5px; line-height: 1.18;">
39
+ Contract draft — comments by Friday?
40
+ </div>
41
+ <div style="margin-top: 8px; font-size: 12px; color: var(--sio-label-3);">
42
+ Dr. Sabine Vogel · 3 participants · 4 messages
43
+ </div>
44
+ </div>
45
+ <sio-glass-toolbar style="position: absolute; top: 0; left: 0; right: 0;">
46
+ <sio-icon slot="start" icon="archive" size="15" style="padding: 5px 8px; color: var(--sio-label-3);"></sio-icon>
47
+ <sio-icon slot="start" icon="clock" size="15" style="padding: 5px 8px; color: var(--sio-label-3);"></sio-icon>
48
+ <sio-icon slot="start" icon="trash" size="15" style="padding: 5px 8px; color: var(--sio-label-3);"></sio-icon>
49
+ <sio-icon slot="start" icon="reply" size="15" style="padding: 5px 8px; color: var(--sio-label-3);"></sio-icon>
50
+ <sio-icon slot="start" icon="forward" size="15" style="padding: 5px 8px; color: var(--sio-label-3);"></sio-icon>
51
+ <span
52
+ slot="end"
53
+ style="display: inline-flex; align-items: center; gap: 6px; width: 170px; padding: 4px 10px; border-radius: 8px; background: var(--sio-surface-2); color: var(--sio-label-3); font-size: 12px;"
54
+ >
55
+ <sio-icon icon="search" size="12"></sio-icon>
56
+ <span style="flex: 1;">Search</span>
57
+ <span
58
+ style="padding: 1px 5px; border-radius: 4px; background: var(--sio-surface-2); border: var(--sio-hairline) solid var(--sio-separator); font-family: var(--sio-font-mono); font-size: 11px;"
59
+ >⌘F</span
60
+ >
61
+ </span>
62
+ </sio-glass-toolbar>
63
+ </div>
64
+
65
+ <!-- centered-title variant (mobile detail header) -->
66
+ <div
67
+ style="position: relative; height: 132px; border-radius: 14px; overflow: hidden; border: var(--sio-hairline) solid var(--sio-separator); background: var(--sio-bg);"
68
+ >
69
+ <div style="padding: 58px 20px 0; font-size: 15px; line-height: 1.45; color: var(--sio-label-2);">
70
+ Deploy is queued behind the cert renewal — ETA 15:40. I'll ping this thread the second staging is green.
71
+ </div>
72
+ <sio-glass-toolbar style="position: absolute; top: 0; left: 0; right: 0;">
73
+ <span
74
+ slot="start"
75
+ style="display: inline-flex; align-items: center; gap: 2px; color: var(--sio-tint); font-size: 15px;"
76
+ >
77
+ <sio-icon icon="chevron-left" size="18"></sio-icon>
78
+ Chats
79
+ </span>
80
+ <span slot="center" style="font-size: 15px; font-weight: 600; letter-spacing: -0.2px; white-space: nowrap;">
81
+ Orbit · launch week
82
+ </span>
83
+ <sio-icon slot="end" icon="phone" size="18" style="color: var(--sio-ch-chats);"></sio-icon>
84
+ <sio-icon slot="end" icon="video" size="18" style="color: var(--sio-ch-chats);"></sio-icon>
85
+ </sio-glass-toolbar>
86
+ </div>
87
+ </div>
88
+ `;
89
+
90
+ public static styles = [
91
+ ...sioElementStyles,
92
+ css`
93
+ :host {
94
+ display: block;
95
+ }
96
+
97
+ .toolbar {
98
+ ${sioToolbarGlass}
99
+ height: 44px;
100
+ box-sizing: border-box;
101
+ display: grid;
102
+ grid-template-columns: 1fr auto 1fr;
103
+ align-items: center;
104
+ column-gap: 8px;
105
+ padding: 0 14px;
106
+ }
107
+
108
+ .region {
109
+ display: flex;
110
+ align-items: center;
111
+ min-width: 0;
112
+ }
113
+
114
+ .start {
115
+ justify-content: flex-start;
116
+ gap: 2px;
117
+ }
118
+
119
+ .center {
120
+ justify-content: center;
121
+ overflow: hidden;
122
+ }
123
+
124
+ .end {
125
+ justify-content: flex-end;
126
+ gap: 8px;
127
+ }
128
+ `,
129
+ ];
130
+
131
+ public render(): TemplateResult {
132
+ return html`
133
+ <div class="toolbar" part="toolbar">
134
+ <div class="region start"><slot name="start"></slot></div>
135
+ <div class="region center"><slot name="center"></slot></div>
136
+ <div class="region end"><slot name="end"></slot></div>
137
+ </div>
138
+ `;
139
+ }
140
+ }