@tldraw/collaboration 0.0.0-bootstrap → 5.3.0-canary.31036673d3f8
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/collaboration.css +1139 -329
- package/dist-cjs/index.js +1 -1
- package/dist-esm/index.mjs +1 -1
- package/package.json +3 -3
package/collaboration.css
CHANGED
|
@@ -2,87 +2,227 @@
|
|
|
2
2
|
/* This file is created by the copy-css-files.mjs script in packages/collaboration. */
|
|
3
3
|
/* It combines the stylesheets of the collaboration umbrella's sub-packages. */
|
|
4
4
|
|
|
5
|
+
/* avatar — a person's image, or a single-initial coloured circle. Shared by the mention picker and,
|
|
6
|
+
via @tldraw/commenting, the comment UI. */
|
|
7
|
+
.tlui-cmt-avatar {
|
|
8
|
+
width: 28px;
|
|
9
|
+
height: 28px;
|
|
10
|
+
border-radius: 50%;
|
|
11
|
+
background: var(--tl-color-selected);
|
|
12
|
+
color: #fff;
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
font-size: 11px;
|
|
17
|
+
font-weight: 600;
|
|
18
|
+
flex-shrink: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.tlui-cmt-avatar--image {
|
|
22
|
+
object-fit: cover;
|
|
23
|
+
background: none;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* mention pill */
|
|
27
|
+
.tlui-cmt-mention {
|
|
28
|
+
color: var(--tl-color-selected);
|
|
29
|
+
font-weight: 600;
|
|
30
|
+
background: color-mix(in srgb, var(--tl-color-selected) 12%, transparent);
|
|
31
|
+
padding: 0 4px;
|
|
32
|
+
border-radius: 4px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* mention picker */
|
|
36
|
+
.tlui-cmt-mention-list {
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
gap: 4px;
|
|
40
|
+
padding: 4px;
|
|
41
|
+
width: 100%;
|
|
42
|
+
box-sizing: border-box;
|
|
43
|
+
max-height: 260px;
|
|
44
|
+
overflow-y: auto;
|
|
45
|
+
color: var(--tl-color-text-1, #1d1d1d);
|
|
46
|
+
background: var(--tl-color-panel, #fff);
|
|
47
|
+
border-radius: 10px;
|
|
48
|
+
/* shadow-3 (like the context menu) already carries a 1px inset edge — a separate `border`
|
|
49
|
+
on top of it double-outlines the popup. */
|
|
50
|
+
box-shadow: var(--tl-shadow-3, 0 2px 12px rgba(0, 0, 0, 0.14));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.tlui-cmt-mention-list__item {
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
gap: 8px;
|
|
57
|
+
padding: 6px 8px;
|
|
58
|
+
border: none;
|
|
59
|
+
background: transparent;
|
|
60
|
+
border-radius: 6px;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
text-align: left;
|
|
63
|
+
color: var(--tl-color-text-1);
|
|
64
|
+
font: inherit;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.tlui-cmt-mention-list__item--active,
|
|
68
|
+
.tlui-cmt-mention-list__item:hover {
|
|
69
|
+
background: var(--tl-color-muted-2, rgba(0, 0, 0, 0.06));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.tlui-cmt-mention-list__text {
|
|
73
|
+
display: flex;
|
|
74
|
+
flex-direction: column;
|
|
75
|
+
min-width: 0;
|
|
76
|
+
line-height: 1.3;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.tlui-cmt-mention-list__name {
|
|
80
|
+
font-size: 12px;
|
|
81
|
+
font-weight: 500;
|
|
82
|
+
white-space: nowrap;
|
|
83
|
+
overflow: hidden;
|
|
84
|
+
text-overflow: ellipsis;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.tlui-cmt-mention-list__you {
|
|
88
|
+
margin-left: 4px;
|
|
89
|
+
font-weight: 400;
|
|
90
|
+
color: var(--tl-color-text-3);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.tlui-cmt-mention-list__secondary {
|
|
94
|
+
font-size: 12px;
|
|
95
|
+
color: var(--tl-color-text-3);
|
|
96
|
+
white-space: nowrap;
|
|
97
|
+
overflow: hidden;
|
|
98
|
+
text-overflow: ellipsis;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.tlui-cmt-mention-list--empty {
|
|
102
|
+
padding: 8px 10px;
|
|
103
|
+
width: 100%;
|
|
104
|
+
box-sizing: border-box;
|
|
105
|
+
color: var(--tl-color-text-3);
|
|
106
|
+
font-size: 13px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* the @-picker popup — anchored under the editor field, positioned by the suggestion plugin */
|
|
110
|
+
.tlui-cmt-mention-popup {
|
|
111
|
+
position: fixed;
|
|
112
|
+
z-index: var(--tl-layer-menus);
|
|
113
|
+
}
|
|
114
|
+
|
|
5
115
|
/* Real comment UI components. Co-located so styles travel to any view (studio,
|
|
6
116
|
canvas), and themed via tldraw's --tl-color tokens so they follow light/dark. */
|
|
7
|
-
.cmt-card {
|
|
117
|
+
.tlui-cmt-card {
|
|
8
118
|
position: relative;
|
|
9
119
|
display: flex;
|
|
10
|
-
|
|
11
|
-
|
|
120
|
+
flex-direction: column;
|
|
121
|
+
gap: 8px;
|
|
122
|
+
padding: 8px;
|
|
12
123
|
width: 260px;
|
|
13
124
|
background: var(--tl-color-panel);
|
|
14
125
|
border: 1px solid var(--tl-color-divider);
|
|
15
|
-
border-radius:
|
|
126
|
+
border-radius: var(--tl-radius-3);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* The avatar lives in the byline row (see Byline), so the body text below indents to line up with
|
|
130
|
+
the name — the avatar's width plus the head's gap. */
|
|
131
|
+
.tlui-cmt-body {
|
|
132
|
+
padding-left: calc(28px + 6px);
|
|
16
133
|
}
|
|
17
134
|
|
|
18
|
-
/* hover-revealed controls (edit…) at the card's top-right */
|
|
19
|
-
.cmt-card__actions {
|
|
135
|
+
/* hover-revealed controls (edit, react…) stacked at the card's top-right */
|
|
136
|
+
.tlui-cmt-card__actions {
|
|
20
137
|
position: absolute;
|
|
21
138
|
top: 6px;
|
|
22
139
|
right: 6px;
|
|
23
140
|
display: flex;
|
|
24
|
-
gap:
|
|
141
|
+
gap: 0;
|
|
25
142
|
opacity: 0;
|
|
26
143
|
}
|
|
27
|
-
.cmt-card:hover .cmt-card__actions,
|
|
28
|
-
.cmt-card:focus-within .cmt-card__actions
|
|
144
|
+
.tlui-cmt-card:hover .tlui-cmt-card__actions,
|
|
145
|
+
.tlui-cmt-card:focus-within .tlui-cmt-card__actions,
|
|
146
|
+
/* an open menu is portaled out of the card, so neither :hover nor :focus-within survives moving
|
|
147
|
+
the pointer into it — keep the controls up while their menu is open */
|
|
148
|
+
.tlui-cmt-card__actions:has([data-state='open']) {
|
|
29
149
|
opacity: 1;
|
|
30
150
|
}
|
|
31
151
|
|
|
32
|
-
.cmt-body {
|
|
152
|
+
.tlui-cmt-body {
|
|
33
153
|
flex: 1;
|
|
34
154
|
min-width: 0;
|
|
35
155
|
}
|
|
36
156
|
|
|
37
|
-
.cmt-head {
|
|
157
|
+
.tlui-cmt-head {
|
|
38
158
|
display: flex;
|
|
39
|
-
align-items:
|
|
159
|
+
align-items: center;
|
|
40
160
|
gap: 6px;
|
|
41
161
|
}
|
|
42
162
|
|
|
43
|
-
.cmt-author {
|
|
44
|
-
font-weight:
|
|
45
|
-
font-size:
|
|
163
|
+
.tlui-cmt-author {
|
|
164
|
+
font-weight: 500;
|
|
165
|
+
font-size: 12px;
|
|
46
166
|
color: var(--tl-color-text-1);
|
|
167
|
+
/* a long name ellipsizes rather than pushing the time out or running under the actions */
|
|
168
|
+
min-width: 0;
|
|
169
|
+
overflow: hidden;
|
|
170
|
+
text-overflow: ellipsis;
|
|
171
|
+
white-space: nowrap;
|
|
47
172
|
}
|
|
48
173
|
|
|
49
|
-
.cmt-time {
|
|
50
|
-
font-size:
|
|
174
|
+
.tlui-cmt-time {
|
|
175
|
+
font-size: 12px;
|
|
51
176
|
color: var(--tl-color-text-3);
|
|
177
|
+
flex-shrink: 0;
|
|
178
|
+
white-space: nowrap;
|
|
52
179
|
}
|
|
53
180
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
181
|
+
/* In the hover preview a long comment truncates with an ellipsis rather than growing the card —
|
|
182
|
+
~7 lines (≈120px at this line height). Preview only. */
|
|
183
|
+
.tlui-cmt-canvas-preview--thread .tlui-cmt-text {
|
|
184
|
+
display: -webkit-box;
|
|
185
|
+
-webkit-box-orient: vertical;
|
|
186
|
+
-webkit-line-clamp: 7;
|
|
187
|
+
line-clamp: 7;
|
|
188
|
+
overflow: hidden;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.tlui-cmt-text {
|
|
192
|
+
margin: 0 0 6px;
|
|
193
|
+
font-size: 12px;
|
|
57
194
|
line-height: 1.4;
|
|
58
195
|
color: var(--tl-color-text-1);
|
|
196
|
+
/* Break long unbroken strings (URLs, keyboard-mashing) so they wrap inside the card instead of
|
|
197
|
+
overflowing it. `anywhere` also lets the flex body shrink to the card width. */
|
|
198
|
+
overflow-wrap: anywhere;
|
|
59
199
|
}
|
|
60
200
|
|
|
61
|
-
.cmt-edited {
|
|
201
|
+
.tlui-cmt-edited {
|
|
62
202
|
font-style: italic;
|
|
63
203
|
}
|
|
64
204
|
|
|
65
205
|
/* rendered rich text inside a comment body — bare tags emitted by the comment renderer
|
|
66
206
|
(paragraphs, lists, code, highlight, links), matching the limited comment extension set. */
|
|
67
|
-
.cmt-text p {
|
|
207
|
+
.tlui-cmt-text p {
|
|
68
208
|
margin: 0;
|
|
69
209
|
}
|
|
70
210
|
|
|
71
|
-
.cmt-text p + p,
|
|
72
|
-
.cmt-text p + ul,
|
|
73
|
-
.cmt-text p + ol,
|
|
74
|
-
.cmt-text ul + p,
|
|
75
|
-
.cmt-text ol + p {
|
|
211
|
+
.tlui-cmt-text p + p,
|
|
212
|
+
.tlui-cmt-text p + ul,
|
|
213
|
+
.tlui-cmt-text p + ol,
|
|
214
|
+
.tlui-cmt-text ul + p,
|
|
215
|
+
.tlui-cmt-text ol + p {
|
|
76
216
|
margin-top: 6px;
|
|
77
217
|
}
|
|
78
218
|
|
|
79
|
-
.cmt-text ul,
|
|
80
|
-
.cmt-text ol {
|
|
219
|
+
.tlui-cmt-text ul,
|
|
220
|
+
.tlui-cmt-text ol {
|
|
81
221
|
margin: 0;
|
|
82
222
|
padding-left: 18px;
|
|
83
223
|
}
|
|
84
224
|
|
|
85
|
-
.cmt-text code {
|
|
225
|
+
.tlui-cmt-text code {
|
|
86
226
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
87
227
|
font-size: 0.92em;
|
|
88
228
|
background: var(--tl-color-muted-2);
|
|
@@ -90,212 +230,263 @@
|
|
|
90
230
|
padding: 0 3px;
|
|
91
231
|
}
|
|
92
232
|
|
|
93
|
-
.cmt-text mark {
|
|
233
|
+
.tlui-cmt-text mark {
|
|
94
234
|
background: color-mix(in srgb, #ffeb3b 60%, transparent);
|
|
95
235
|
color: inherit;
|
|
96
236
|
border-radius: 2px;
|
|
97
237
|
}
|
|
98
238
|
|
|
99
|
-
.cmt-text a {
|
|
239
|
+
.tlui-cmt-text a {
|
|
100
240
|
color: var(--tl-color-selected);
|
|
101
241
|
text-decoration: underline;
|
|
102
242
|
}
|
|
103
243
|
|
|
104
|
-
.
|
|
105
|
-
|
|
106
|
-
height: 28px;
|
|
107
|
-
border-radius: 50%;
|
|
108
|
-
background: var(--tl-color-selected);
|
|
109
|
-
color: #fff;
|
|
110
|
-
display: flex;
|
|
111
|
-
align-items: center;
|
|
112
|
-
justify-content: center;
|
|
113
|
-
font-size: 11px;
|
|
114
|
-
font-weight: 600;
|
|
115
|
-
flex-shrink: 0;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
.cmt-avatar--image {
|
|
119
|
-
object-fit: cover;
|
|
120
|
-
background: none;
|
|
121
|
-
}
|
|
244
|
+
/* Avatar and mention styles live in @tldraw/mentions (src/mention.css) and are bundled into
|
|
245
|
+
commenting.css by copy-css-files.mjs. */
|
|
122
246
|
|
|
123
247
|
/* composer — an avatar beside a rounded-rect field holding the rich-text input and the send button.
|
|
124
248
|
Formatting is applied via markdown and keyboard shortcuts (no toolbar). The avatar aligns to the
|
|
125
249
|
top so it stays put as the field grows to multiple lines. */
|
|
126
|
-
.cmt-composer {
|
|
250
|
+
.tlui-cmt-composer {
|
|
127
251
|
display: flex;
|
|
128
252
|
align-items: flex-start;
|
|
129
253
|
gap: 10px;
|
|
130
254
|
padding: 4px 0 8px 4px;
|
|
131
255
|
}
|
|
132
256
|
|
|
133
|
-
.cmt-composer__field {
|
|
257
|
+
.tlui-cmt-composer__field {
|
|
134
258
|
flex: 1;
|
|
135
259
|
min-width: 0;
|
|
136
260
|
display: flex;
|
|
137
261
|
align-items: center;
|
|
138
262
|
gap: 6px;
|
|
139
|
-
padding: 3px 3px 3px
|
|
140
|
-
background: var(--tl-color-muted-
|
|
263
|
+
padding: 3px 3px 3px 8px;
|
|
264
|
+
background: var(--tl-color-muted-0);
|
|
141
265
|
border: 1px solid var(--tl-color-divider);
|
|
142
|
-
border-radius: var(--tl-radius-
|
|
266
|
+
border-radius: var(--tl-radius-2);
|
|
267
|
+
/* The whole field reads as the text input — clicking its empty area focuses the editor
|
|
268
|
+
(see CommentComposer). The send button restores its own pointer cursor. */
|
|
269
|
+
cursor: text;
|
|
143
270
|
}
|
|
144
271
|
|
|
145
|
-
.cmt-composer__field:focus-within {
|
|
272
|
+
.tlui-cmt-composer__field:focus-within {
|
|
146
273
|
border-color: var(--tl-color-selected);
|
|
274
|
+
background: var(--tl-color-panel);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* Expanded: the content no longer fits on one line beside the send button, so the input takes
|
|
278
|
+
the full width and the send button drops to its own row, right-aligned. */
|
|
279
|
+
.tlui-cmt-composer__field--expanded {
|
|
280
|
+
flex-wrap: wrap;
|
|
281
|
+
/* No vertical gap between the input and the send-button row it wraps onto. */
|
|
282
|
+
row-gap: 0;
|
|
283
|
+
}
|
|
284
|
+
.tlui-cmt-composer__field--expanded .tlui-cmt-composer__input-wrap {
|
|
285
|
+
flex-basis: 100%;
|
|
286
|
+
/* The field pads 8px left but only 3px right (the send button's inset). With the input
|
|
287
|
+
spanning the full width, give the text the same 8px on the right. */
|
|
288
|
+
padding-right: 5px;
|
|
289
|
+
}
|
|
290
|
+
.tlui-cmt-composer__field--expanded .tlui-cmt-send {
|
|
291
|
+
margin-left: auto;
|
|
147
292
|
}
|
|
148
293
|
|
|
149
294
|
/* the editable area, with the placeholder overlaid while it's empty */
|
|
150
|
-
.cmt-composer__input-wrap {
|
|
295
|
+
.tlui-cmt-composer__input-wrap {
|
|
151
296
|
position: relative;
|
|
152
297
|
flex: 1;
|
|
153
298
|
min-width: 0;
|
|
154
|
-
|
|
299
|
+
/* Frames the input's line height symmetrically so the text sits centred in the field. Paired
|
|
300
|
+
with the input's min-height, which equals its line height (no gap for the text to top-align
|
|
301
|
+
into). Keeps the field's overall height. */
|
|
302
|
+
padding: 4.5px 0;
|
|
155
303
|
}
|
|
156
304
|
|
|
157
|
-
|
|
305
|
+
/* The hidden single-line measuring clone of the editor's content (see CommentComposer). */
|
|
306
|
+
.tlui-cmt-composer__mirror {
|
|
307
|
+
position: absolute;
|
|
308
|
+
top: 0;
|
|
309
|
+
left: 0;
|
|
310
|
+
visibility: hidden;
|
|
311
|
+
pointer-events: none;
|
|
312
|
+
white-space: nowrap;
|
|
313
|
+
width: max-content;
|
|
314
|
+
max-height: none;
|
|
315
|
+
overflow: visible;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.tlui-cmt-input {
|
|
158
319
|
font: inherit;
|
|
159
|
-
font-size:
|
|
320
|
+
font-size: 12px;
|
|
160
321
|
line-height: 1.4;
|
|
161
322
|
color: var(--tl-color-text-1);
|
|
162
323
|
outline: none;
|
|
163
|
-
min-height:
|
|
164
|
-
max-height: 40vh;
|
|
165
|
-
overflow-y: auto;
|
|
324
|
+
min-height: 17px;
|
|
166
325
|
white-space: pre-wrap;
|
|
167
326
|
word-break: break-word;
|
|
168
327
|
}
|
|
169
328
|
|
|
170
|
-
.cmt-input p {
|
|
329
|
+
.tlui-cmt-input p {
|
|
171
330
|
margin: 0;
|
|
172
331
|
}
|
|
173
332
|
|
|
174
|
-
.cmt-input p + p,
|
|
175
|
-
.cmt-input p + ul,
|
|
176
|
-
.cmt-input p + ol {
|
|
333
|
+
.tlui-cmt-input p + p,
|
|
334
|
+
.tlui-cmt-input p + ul,
|
|
335
|
+
.tlui-cmt-input p + ol {
|
|
177
336
|
margin-top: 4px;
|
|
178
337
|
}
|
|
179
338
|
|
|
180
|
-
.cmt-input ul,
|
|
181
|
-
.cmt-input ol {
|
|
339
|
+
.tlui-cmt-input ul,
|
|
340
|
+
.tlui-cmt-input ol {
|
|
182
341
|
margin: 0;
|
|
183
342
|
padding-left: 18px;
|
|
184
343
|
}
|
|
185
344
|
|
|
186
|
-
.cmt-input__placeholder {
|
|
345
|
+
.tlui-cmt-input__placeholder {
|
|
187
346
|
position: absolute;
|
|
188
|
-
top:
|
|
347
|
+
top: 4.5px;
|
|
189
348
|
left: 0;
|
|
190
|
-
font-size:
|
|
349
|
+
font-size: 12px;
|
|
191
350
|
line-height: 1.4;
|
|
192
351
|
color: var(--tl-color-text-3);
|
|
193
352
|
pointer-events: none;
|
|
194
353
|
}
|
|
195
354
|
|
|
196
|
-
|
|
355
|
+
/* A bare up-arrow icon button (matching the placement composer): no fill by default, a grey square
|
|
356
|
+
on hover, grey while there's nothing to send and dark once there is. */
|
|
357
|
+
.tlui-cmt-send {
|
|
358
|
+
flex-shrink: 0;
|
|
359
|
+
display: inline-flex;
|
|
360
|
+
align-items: center;
|
|
361
|
+
justify-content: center;
|
|
362
|
+
width: 28px;
|
|
363
|
+
height: 28px;
|
|
197
364
|
border: 0;
|
|
198
365
|
border-radius: var(--tl-radius-2);
|
|
199
|
-
background:
|
|
200
|
-
color:
|
|
201
|
-
font: inherit;
|
|
202
|
-
font-size: 12px;
|
|
203
|
-
font-weight: 600;
|
|
204
|
-
padding: 6px 12px;
|
|
366
|
+
background: transparent;
|
|
367
|
+
color: var(--tl-color-text-3);
|
|
205
368
|
cursor: pointer;
|
|
206
|
-
align-self:
|
|
369
|
+
align-self: center;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.tlui-cmt-send:not(:disabled) {
|
|
373
|
+
color: var(--tl-color-text-1);
|
|
207
374
|
}
|
|
208
375
|
|
|
209
|
-
.cmt-send:hover
|
|
210
|
-
|
|
211
|
-
background: color-mix(in srgb, var(--tl-color-selected) 88%, black);
|
|
376
|
+
.tlui-cmt-send:hover:not(:disabled) {
|
|
377
|
+
background: var(--tl-color-muted-2);
|
|
212
378
|
}
|
|
213
379
|
|
|
214
|
-
.cmt-send:active,
|
|
215
|
-
.pseudo-active .cmt-send {
|
|
216
|
-
background:
|
|
380
|
+
.tlui-cmt-send:active:not(:disabled),
|
|
381
|
+
.pseudo-active .tlui-cmt-send:not(:disabled) {
|
|
382
|
+
background: var(--tl-color-muted-1);
|
|
217
383
|
}
|
|
218
384
|
|
|
219
|
-
.cmt-send:focus-visible,
|
|
220
|
-
.pseudo-focus-visible .cmt-send {
|
|
385
|
+
.tlui-cmt-send:focus-visible,
|
|
386
|
+
.pseudo-focus-visible .tlui-cmt-send {
|
|
221
387
|
outline: 2px solid var(--tl-color-selected);
|
|
222
388
|
outline-offset: 2px;
|
|
223
389
|
}
|
|
224
390
|
|
|
225
|
-
.cmt-send:disabled {
|
|
226
|
-
|
|
391
|
+
.tlui-cmt-send:disabled {
|
|
392
|
+
background: transparent;
|
|
393
|
+
color: var(--tl-color-text-3);
|
|
227
394
|
cursor: not-allowed;
|
|
228
395
|
}
|
|
229
396
|
|
|
230
|
-
/* pin
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
397
|
+
/* The shared marker treatment. To the pointer a comment pin and a count badge are the same
|
|
398
|
+
object — a token sitting on the canvas — so they rest and lift identically; everything shared
|
|
399
|
+
lives here so the two can't drift apart. */
|
|
400
|
+
.tlui-cmt-marker {
|
|
401
|
+
box-shadow: var(--tlui-cmt-marker-shadow);
|
|
402
|
+
cursor: pointer;
|
|
403
|
+
transition:
|
|
404
|
+
transform var(--tlui-cmt-marker-transition),
|
|
405
|
+
box-shadow var(--tlui-cmt-marker-transition);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.tlui-cmt-marker:hover {
|
|
409
|
+
transform: scale(var(--tlui-cmt-marker-hover-scale));
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/* The hover shadow is for closed markers only — an open one keeps its selection ring, which this
|
|
413
|
+
box-shadow would otherwise override (the hover selector outranks the --open rule). */
|
|
414
|
+
.tlui-cmt-marker:not(.tlui-cmt-marker--open):hover {
|
|
415
|
+
box-shadow: var(--tlui-cmt-marker-shadow-hover);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/* Count badges don't react to hover — no scale, no shadow lift. Only pins do. */
|
|
419
|
+
.tlui-cmt-count-badge:hover {
|
|
420
|
+
transform: none;
|
|
421
|
+
box-shadow: var(--tlui-cmt-marker-shadow);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/* Pins don't scale on hover — only the shadow lifts. */
|
|
425
|
+
.tlui-cmt-pin:hover {
|
|
426
|
+
transform: none;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/* open: the thread (or a stack's list) is showing — the active marker just keeps its lift shadow,
|
|
430
|
+
no selection ring. */
|
|
431
|
+
.tlui-cmt-marker--open {
|
|
432
|
+
box-shadow: var(--tlui-cmt-marker-shadow);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/* pin — a canvas marker: a white teardrop holding the author's avatar circle, so a thread is
|
|
436
|
+
identifiable at a glance. */
|
|
437
|
+
.tlui-cmt-pin {
|
|
438
|
+
--tlui-cmt-marker-size: var(--tlui-cmt-pin-size);
|
|
439
|
+
width: var(--tlui-cmt-marker-size);
|
|
440
|
+
height: var(--tlui-cmt-marker-size);
|
|
441
|
+
border-radius: 50% 50% 50% 0;
|
|
442
|
+
background: var(--tl-color-panel);
|
|
236
443
|
color: #fff;
|
|
237
444
|
display: flex;
|
|
238
445
|
align-items: center;
|
|
239
446
|
justify-content: center;
|
|
240
447
|
font-size: 14px;
|
|
241
448
|
font-weight: 600;
|
|
242
|
-
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
|
|
243
|
-
cursor: pointer;
|
|
244
|
-
transition:
|
|
245
|
-
transform 0.08s ease,
|
|
246
|
-
box-shadow 0.08s ease;
|
|
247
449
|
}
|
|
248
|
-
|
|
249
|
-
.cmt-pin
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.32);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/* open: the thread is showing — a selection ring marks the active pin */
|
|
256
|
-
.cmt-pin--open {
|
|
257
|
-
box-shadow:
|
|
258
|
-
0 0 0 3px var(--tl-color-panel),
|
|
259
|
-
0 0 0 5px var(--tl-color-selected),
|
|
260
|
-
0 2px 6px rgba(0, 0, 0, 0.25);
|
|
450
|
+
/* The pin holds the author's 22px avatar circle, centred in its 28px face. */
|
|
451
|
+
.tlui-cmt-pin .tlui-cmt-avatar {
|
|
452
|
+
width: 22px;
|
|
453
|
+
height: 22px;
|
|
261
454
|
}
|
|
262
455
|
|
|
263
456
|
/* resolved: a solid, muted grey (the default --tl-color-muted-1 is 10% black, so it goes
|
|
264
457
|
translucent over the canvas). Dark mode gets its own grey — the token's dark value is near-black. */
|
|
265
|
-
.cmt-pin--resolved {
|
|
458
|
+
.tlui-cmt-pin--resolved {
|
|
266
459
|
background: #dfe0e1;
|
|
267
460
|
color: hsl(214, 8%, 42%);
|
|
268
461
|
}
|
|
269
|
-
.tl-theme__dark .cmt-pin--resolved {
|
|
462
|
+
.tl-theme__dark .tlui-cmt-pin--resolved {
|
|
270
463
|
background: hsl(240, 5%, 30%);
|
|
271
464
|
color: hsl(0, 0%, 72%);
|
|
272
465
|
}
|
|
273
466
|
|
|
274
|
-
/* count badge */
|
|
275
|
-
.cmt-count-badge {
|
|
276
|
-
width:
|
|
277
|
-
height:
|
|
467
|
+
/* count badge — the same marker as a pin. */
|
|
468
|
+
.tlui-cmt-count-badge {
|
|
469
|
+
width: var(--tlui-cmt-marker-size);
|
|
470
|
+
height: var(--tlui-cmt-marker-size);
|
|
278
471
|
border-radius: 50%;
|
|
279
472
|
display: flex;
|
|
280
473
|
align-items: center;
|
|
281
474
|
justify-content: center;
|
|
282
475
|
background: var(--tl-color-text-1);
|
|
283
476
|
color: var(--tl-color-panel);
|
|
284
|
-
font-size:
|
|
477
|
+
font-size: 12px;
|
|
285
478
|
font-weight: 700;
|
|
286
|
-
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
|
|
287
479
|
user-select: none;
|
|
288
480
|
}
|
|
289
481
|
|
|
290
482
|
/* thread */
|
|
291
|
-
.cmt-thread {
|
|
483
|
+
.tlui-cmt-thread {
|
|
292
484
|
display: flex;
|
|
293
485
|
flex-direction: column;
|
|
294
|
-
gap: 8px;
|
|
295
486
|
width: 300px;
|
|
296
487
|
}
|
|
297
488
|
|
|
298
|
-
.cmt-thread__head {
|
|
489
|
+
.tlui-cmt-thread__head {
|
|
299
490
|
display: flex;
|
|
300
491
|
align-items: center;
|
|
301
492
|
gap: 8px;
|
|
@@ -306,7 +497,7 @@
|
|
|
306
497
|
letter-spacing: 0.04em;
|
|
307
498
|
}
|
|
308
499
|
|
|
309
|
-
.cmt-badge {
|
|
500
|
+
.tlui-cmt-badge {
|
|
310
501
|
font-size: 10px;
|
|
311
502
|
font-weight: 600;
|
|
312
503
|
text-transform: none;
|
|
@@ -318,134 +509,105 @@
|
|
|
318
509
|
}
|
|
319
510
|
|
|
320
511
|
/* reactions */
|
|
321
|
-
.cmt-reactions {
|
|
512
|
+
.tlui-cmt-reactions {
|
|
322
513
|
display: flex;
|
|
323
|
-
|
|
514
|
+
flex-wrap: wrap;
|
|
515
|
+
gap: 4px;
|
|
516
|
+
margin-top: 6px;
|
|
324
517
|
}
|
|
325
518
|
|
|
326
|
-
.cmt-reaction {
|
|
519
|
+
.tlui-cmt-reaction {
|
|
327
520
|
display: inline-flex;
|
|
328
521
|
align-items: center;
|
|
329
|
-
gap:
|
|
330
|
-
padding:
|
|
331
|
-
border
|
|
332
|
-
border:
|
|
333
|
-
background: var(--tl-color-
|
|
522
|
+
gap: 4px;
|
|
523
|
+
padding: 2px 6px;
|
|
524
|
+
border: none;
|
|
525
|
+
border-radius: var(--tl-radius-1);
|
|
526
|
+
background: var(--tl-color-muted-2);
|
|
334
527
|
font: inherit;
|
|
335
|
-
font-size:
|
|
528
|
+
font-size: 10px;
|
|
529
|
+
font-weight: 400;
|
|
336
530
|
color: var(--tl-color-text-1);
|
|
337
531
|
cursor: pointer;
|
|
338
532
|
}
|
|
339
533
|
|
|
340
|
-
.
|
|
341
|
-
|
|
342
|
-
|
|
534
|
+
/* Hover deepens the grey — no border to tint now. Mixing toward the text colour is theme-aware:
|
|
535
|
+
it darkens the fill in light mode and lightens it in dark, tracking the pill's own contrast. */
|
|
536
|
+
.tlui-cmt-reaction:hover {
|
|
537
|
+
background: var(--tl-color-muted-1);
|
|
343
538
|
}
|
|
344
539
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
540
|
+
/* a reaction you're part of fills with the selection colour, so "I reacted with this" stands out
|
|
541
|
+
from the grey pills of reactions you haven't joined. */
|
|
542
|
+
.tlui-cmt-reaction--active {
|
|
543
|
+
background: color-mix(in srgb, var(--tl-color-selected) 28%, var(--tl-color-panel));
|
|
348
544
|
}
|
|
349
545
|
|
|
350
|
-
.cmt-
|
|
351
|
-
|
|
546
|
+
.tlui-cmt-reaction--active:hover {
|
|
547
|
+
background: color-mix(in srgb, var(--tl-color-selected) 38%, var(--tl-color-panel));
|
|
352
548
|
}
|
|
353
549
|
|
|
354
|
-
|
|
550
|
+
/* the emoji sits at the same 10px as the count beside it */
|
|
551
|
+
.tlui-cmt-reaction__emoji {
|
|
552
|
+
font-size: 10px;
|
|
553
|
+
line-height: 1;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.tlui-cmt-reaction__count {
|
|
355
557
|
color: var(--tl-color-text-3);
|
|
356
|
-
font-
|
|
558
|
+
font-variant-numeric: tabular-nums;
|
|
357
559
|
}
|
|
358
560
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
color: var(--tl-color-selected);
|
|
362
|
-
font-weight: 600;
|
|
363
|
-
background: color-mix(in srgb, var(--tl-color-selected) 12%, transparent);
|
|
364
|
-
padding: 0 4px;
|
|
365
|
-
border-radius: 4px;
|
|
561
|
+
.tlui-cmt-reaction--active .tlui-cmt-reaction__count {
|
|
562
|
+
color: var(--tl-color-text-1);
|
|
366
563
|
}
|
|
367
564
|
|
|
368
|
-
/*
|
|
369
|
-
.cmt-
|
|
370
|
-
display:
|
|
371
|
-
|
|
372
|
-
gap:
|
|
565
|
+
/* emoji picker */
|
|
566
|
+
.tlui-cmt-emoji-picker {
|
|
567
|
+
display: grid;
|
|
568
|
+
grid-template-columns: repeat(5, 40px);
|
|
569
|
+
gap: 0;
|
|
373
570
|
padding: 4px;
|
|
374
|
-
width: 100%;
|
|
375
|
-
box-sizing: border-box;
|
|
376
|
-
max-height: 260px;
|
|
377
|
-
overflow-y: auto;
|
|
378
|
-
color: var(--tl-color-text-1, #1d1d1d);
|
|
379
|
-
background: var(--tl-color-panel, #fff);
|
|
380
|
-
border: 1px solid var(--tl-color-divider, rgba(0, 0, 0, 0.08));
|
|
381
|
-
border-radius: 10px;
|
|
382
|
-
box-shadow: var(--tl-shadow-2, 0 2px 12px rgba(0, 0, 0, 0.14));
|
|
383
571
|
}
|
|
384
572
|
|
|
385
|
-
.cmt-
|
|
573
|
+
.tlui-cmt-emoji-picker__item {
|
|
574
|
+
position: relative;
|
|
386
575
|
display: flex;
|
|
387
576
|
align-items: center;
|
|
388
|
-
|
|
389
|
-
|
|
577
|
+
justify-content: center;
|
|
578
|
+
width: 40px;
|
|
579
|
+
height: 40px;
|
|
580
|
+
padding: 0;
|
|
390
581
|
border: none;
|
|
391
|
-
background:
|
|
392
|
-
border-radius: 6px;
|
|
393
|
-
cursor: pointer;
|
|
394
|
-
text-align: left;
|
|
395
|
-
color: var(--tl-color-text-1);
|
|
582
|
+
background: none;
|
|
396
583
|
font: inherit;
|
|
584
|
+
font-size: 21px;
|
|
585
|
+
line-height: 1;
|
|
586
|
+
cursor: pointer;
|
|
397
587
|
}
|
|
398
588
|
|
|
399
|
-
|
|
400
|
-
.
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
min-width: 0;
|
|
408
|
-
line-height: 1.3;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
.cmt-mention-list__name {
|
|
412
|
-
font-size: 13px;
|
|
413
|
-
font-weight: 500;
|
|
414
|
-
white-space: nowrap;
|
|
415
|
-
overflow: hidden;
|
|
416
|
-
text-overflow: ellipsis;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
.cmt-mention-list__you {
|
|
420
|
-
margin-left: 4px;
|
|
421
|
-
font-weight: 400;
|
|
422
|
-
color: var(--tl-color-text-3);
|
|
589
|
+
/* Inset wash behind the glyph — the 40px hit cells tile edge-to-edge, the squares sit 4px apart,
|
|
590
|
+
like the thread action buttons. Sits under the emoji so an opaque selected fill can't hide it. */
|
|
591
|
+
.tlui-cmt-emoji-picker__item::after {
|
|
592
|
+
content: '';
|
|
593
|
+
position: absolute;
|
|
594
|
+
inset: 2px;
|
|
595
|
+
z-index: -1;
|
|
596
|
+
border-radius: var(--tl-radius-2);
|
|
423
597
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
font-size: 12px;
|
|
427
|
-
color: var(--tl-color-text-3);
|
|
428
|
-
white-space: nowrap;
|
|
429
|
-
overflow: hidden;
|
|
430
|
-
text-overflow: ellipsis;
|
|
598
|
+
.tlui-cmt-emoji-picker__item:hover {
|
|
599
|
+
z-index: 1;
|
|
431
600
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
padding: 8px 10px;
|
|
435
|
-
width: 100%;
|
|
436
|
-
box-sizing: border-box;
|
|
437
|
-
color: var(--tl-color-text-3);
|
|
438
|
-
font-size: 13px;
|
|
601
|
+
.tlui-cmt-emoji-picker__item:hover::after {
|
|
602
|
+
background: var(--tl-color-muted-2);
|
|
439
603
|
}
|
|
440
604
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
position: fixed;
|
|
444
|
-
z-index: var(--tl-layer-menus);
|
|
605
|
+
.tlui-cmt-emoji-picker__item--active::after {
|
|
606
|
+
background: color-mix(in srgb, var(--tl-color-selected) 28%, var(--tl-color-panel));
|
|
445
607
|
}
|
|
446
608
|
|
|
447
609
|
/* empty state */
|
|
448
|
-
.cmt-empty {
|
|
610
|
+
.tlui-cmt-empty {
|
|
449
611
|
display: flex;
|
|
450
612
|
flex-direction: column;
|
|
451
613
|
align-items: center;
|
|
@@ -455,50 +617,116 @@
|
|
|
455
617
|
width: 240px;
|
|
456
618
|
}
|
|
457
619
|
|
|
458
|
-
.cmt-empty__icon {
|
|
620
|
+
.tlui-cmt-empty__icon {
|
|
459
621
|
font-size: 28px;
|
|
460
622
|
}
|
|
461
623
|
|
|
462
|
-
.cmt-empty__message {
|
|
624
|
+
.tlui-cmt-empty__message {
|
|
463
625
|
margin: 0;
|
|
464
626
|
color: var(--tl-color-text-3);
|
|
465
|
-
font-size:
|
|
627
|
+
font-size: 12px;
|
|
466
628
|
line-height: 1.4;
|
|
467
629
|
}
|
|
468
630
|
|
|
469
|
-
/* a comment thread: an elevated panel
|
|
470
|
-
|
|
471
|
-
|
|
631
|
+
/* a comment thread: an elevated panel holding a header, an optional resolved banner, the
|
|
632
|
+
comments, and a reply composer */
|
|
633
|
+
/* The thread panel's layout metrics live on the container because two separate portals need them:
|
|
634
|
+
the panel itself, and the canvas hover preview, which has to land its first comment on exactly
|
|
635
|
+
the pixel the panel would. Deriving the preview's offset from these means the two can't drift —
|
|
636
|
+
retune the padding or the header here and the preview follows. */
|
|
637
|
+
.tl-container {
|
|
638
|
+
/* Everything that has to line up against a pin — its preview's bridge, the draft composer's
|
|
639
|
+
offsets, `PIN_SIZE` in thread-view.tsx — derives from this, so the pin's size stays one
|
|
640
|
+
number. */
|
|
641
|
+
--tlui-cmt-pin-size: 28px;
|
|
642
|
+
/* A cluster badge and a stack badge are this square — the same size as a pin, so the marker
|
|
643
|
+
kinds read as one family. The hover preview measures its bridge against it, so the bridge
|
|
644
|
+
stops at the marker's edge instead of covering it. Mirrored by `MARKER_SIZE` in
|
|
645
|
+
thread-view.tsx. */
|
|
646
|
+
--tlui-cmt-marker-size: var(--tlui-cmt-pin-size);
|
|
647
|
+
/* How a marker sits, lifts, and rings. Shared so a pin and a badge respond identically.
|
|
648
|
+
shadow-3's depth without its `inset 1px panel-contrast` edge — that inset reads as a white
|
|
649
|
+
border on a coloured pin (it's invisible on the panel-white menus shadow-3 is meant for). */
|
|
650
|
+
--tlui-cmt-marker-shadow: 0px 1px 2px hsl(0, 0%, 0%, 28%), 0px 2px 6px hsl(0, 0%, 0%, 14%);
|
|
651
|
+
--tlui-cmt-marker-shadow-hover: var(--tlui-cmt-marker-shadow);
|
|
652
|
+
--tlui-cmt-marker-hover-scale: 1.08;
|
|
653
|
+
--tlui-cmt-marker-transition: 0.08s ease;
|
|
654
|
+
--tlui-cmt-thread-padding: 12px;
|
|
655
|
+
--tlui-cmt-thread-gap: 0px;
|
|
656
|
+
--tlui-cmt-thread-action-size: 26px;
|
|
657
|
+
/* A floating pill's edge — its hairline ring is the only thing separating it from the panel it
|
|
658
|
+
overlaps, so the ring is a theme token rather than a literal black: a near-black hairline
|
|
659
|
+
vanishes on a dark panel, where the divider token lightens instead. The drop below it stays
|
|
660
|
+
literal; a soft dark blur reads on both themes. */
|
|
661
|
+
--tlui-cmt-pill-shadow: 0 0 0 1px var(--tl-color-divider), 0 1px 3px hsl(0, 0%, 0%, 0.05);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.tlui-cmt-thread {
|
|
665
|
+
position: relative;
|
|
472
666
|
display: flex;
|
|
473
667
|
flex-direction: column;
|
|
474
|
-
gap: 6px;
|
|
475
668
|
width: 300px;
|
|
476
|
-
padding: 8px;
|
|
477
669
|
background: var(--tl-color-panel);
|
|
478
670
|
border-radius: var(--tl-radius-4);
|
|
479
|
-
box-shadow: var(--tl-shadow-
|
|
671
|
+
box-shadow: var(--tl-shadow-3);
|
|
672
|
+
}
|
|
673
|
+
/* A pill of its own, floating above the panel's top-right corner and overlapping its top edge. */
|
|
674
|
+
.tlui-cmt-thread__header {
|
|
675
|
+
position: absolute;
|
|
676
|
+
right: 10px;
|
|
677
|
+
bottom: calc(100% - 15px);
|
|
678
|
+
display: flex;
|
|
679
|
+
gap: 2px;
|
|
680
|
+
padding: 2px;
|
|
681
|
+
background: var(--tl-color-panel);
|
|
682
|
+
border-radius: var(--tl-radius-3);
|
|
683
|
+
box-shadow: var(--tlui-cmt-pill-shadow);
|
|
684
|
+
transition: opacity 0.1s ease;
|
|
685
|
+
}
|
|
686
|
+
/* The first comment's hover actions surface right where the header pill sits, so hide the pill
|
|
687
|
+
while that comment is hovered — one pill in the corner, never two stacked. */
|
|
688
|
+
.tlui-cmt-thread:has(.tlui-cmt-thread__list > :first-child:hover)
|
|
689
|
+
.tlui-cmt-thread__header:not(:has(.tlui-cmt-thread__title)) {
|
|
690
|
+
opacity: 0;
|
|
691
|
+
pointer-events: none;
|
|
480
692
|
}
|
|
481
|
-
.
|
|
482
|
-
|
|
693
|
+
/* A title puts the header back in the panel, in flow. The pill above is tuned for the actions-only
|
|
694
|
+
case tldraw itself uses: right-anchored and only as wide as its buttons, so a title inside it
|
|
695
|
+
would grow leftwards off the panel's edge and float over whatever sits above. `header` is public
|
|
696
|
+
API (see `CommentThreadProps`), so it has to keep laying out sensibly for anyone passing one. */
|
|
697
|
+
.tlui-cmt-thread__header:has(.tlui-cmt-thread__title) {
|
|
698
|
+
position: static;
|
|
483
699
|
align-items: center;
|
|
484
|
-
|
|
485
|
-
|
|
700
|
+
padding: 0;
|
|
701
|
+
background: none;
|
|
702
|
+
border-radius: 0;
|
|
703
|
+
box-shadow: none;
|
|
486
704
|
}
|
|
487
|
-
.cmt-thread__title {
|
|
705
|
+
.tlui-cmt-thread__title {
|
|
706
|
+
padding: 10px 12px;
|
|
488
707
|
font-size: 12px;
|
|
489
|
-
|
|
708
|
+
line-height: 20px;
|
|
709
|
+
font-weight: 500;
|
|
490
710
|
color: var(--tl-color-text-1);
|
|
491
711
|
}
|
|
492
|
-
.cmt-thread__actions {
|
|
712
|
+
.tlui-cmt-thread__actions {
|
|
493
713
|
display: flex;
|
|
494
|
-
gap:
|
|
495
|
-
|
|
496
|
-
|
|
714
|
+
gap: 0;
|
|
715
|
+
/* Right-align the actions whether or not the header has a title (it currently has none, so
|
|
716
|
+
space-between alone would leave them at the left). */
|
|
717
|
+
margin-left: auto;
|
|
718
|
+
}
|
|
719
|
+
/* Follows tldraw's toolbar buttons (.tlui-button), used by both the header and card actions: the
|
|
720
|
+
button is the 40px hit target that tiles with its neighbours; its 32px ::after is the visual
|
|
721
|
+
square carrying the hover wash, so the hit area (not the ::after) is what's clickable and it
|
|
722
|
+
never reaches past its row. */
|
|
723
|
+
.tlui-cmt-thread__action {
|
|
724
|
+
position: relative;
|
|
497
725
|
display: flex;
|
|
498
726
|
align-items: center;
|
|
499
727
|
justify-content: center;
|
|
500
|
-
width:
|
|
501
|
-
height:
|
|
728
|
+
width: 40px;
|
|
729
|
+
height: 40px;
|
|
502
730
|
padding: 0;
|
|
503
731
|
border: none;
|
|
504
732
|
border-radius: 6px;
|
|
@@ -508,39 +736,156 @@
|
|
|
508
736
|
line-height: 1;
|
|
509
737
|
cursor: pointer;
|
|
510
738
|
}
|
|
511
|
-
.
|
|
512
|
-
|
|
739
|
+
/* Overlap each button 4px into the previous so the 32px visual squares sit 4px apart, not 8px.
|
|
740
|
+
Left-only, so the last button's hit area still stops at its row's right edge. */
|
|
741
|
+
.tlui-cmt-thread__action + .tlui-cmt-thread__action {
|
|
742
|
+
margin-left: -4px;
|
|
743
|
+
}
|
|
744
|
+
.tlui-cmt-thread__action::after {
|
|
745
|
+
content: '';
|
|
746
|
+
position: absolute;
|
|
747
|
+
inset: 4px;
|
|
748
|
+
border-radius: var(--tl-radius-2);
|
|
749
|
+
}
|
|
750
|
+
.tlui-cmt-thread__action:hover {
|
|
513
751
|
color: var(--tl-color-text-1);
|
|
752
|
+
/* Raise the hovered button above its overlapping neighbours so its 40px hit area wins the
|
|
753
|
+
overlap zone — the same z-index flip tldraw's own toolbar buttons use. */
|
|
754
|
+
z-index: 1;
|
|
755
|
+
}
|
|
756
|
+
.tlui-cmt-thread__action:hover::after {
|
|
757
|
+
background: var(--tl-color-muted-2);
|
|
758
|
+
}
|
|
759
|
+
.tlui-cmt-thread__action--danger:hover {
|
|
760
|
+
color: var(--tl-color-danger);
|
|
514
761
|
}
|
|
515
|
-
.cmt-thread__resolved {
|
|
762
|
+
.tlui-cmt-thread__resolved {
|
|
763
|
+
margin: 4px 12px;
|
|
516
764
|
padding: 6px 10px;
|
|
517
765
|
border-radius: 8px;
|
|
518
766
|
background: var(--tl-color-muted-2);
|
|
519
767
|
font-size: 12px;
|
|
520
768
|
color: var(--tl-color-text-3);
|
|
521
769
|
}
|
|
522
|
-
.cmt-thread__list {
|
|
770
|
+
.tlui-cmt-thread__list {
|
|
523
771
|
display: flex;
|
|
524
772
|
flex-direction: column;
|
|
525
|
-
gap:
|
|
773
|
+
gap: var(--tlui-cmt-thread-gap);
|
|
774
|
+
}
|
|
775
|
+
/* When nothing follows the list (a resolved thread has no composer or footer) the last comment
|
|
776
|
+
would sit against the panel's rounded bottom — restore the trailing inset the composer supplies. */
|
|
777
|
+
.tlui-cmt-thread__list:last-child {
|
|
778
|
+
padding-bottom: 12px;
|
|
779
|
+
}
|
|
780
|
+
/* The footer slot sits where the composer would — its content (e.g. a sign-in prompt) sits at
|
|
781
|
+
the thread's trailing edge, like the composer's send button. */
|
|
782
|
+
.tlui-cmt-thread__footer {
|
|
783
|
+
display: flex;
|
|
784
|
+
justify-content: flex-end;
|
|
785
|
+
padding: 4px 2px 2px;
|
|
786
|
+
}
|
|
787
|
+
/* Inside a thread, author identity is a small colour dot, no initial — the letter doesn't fit
|
|
788
|
+
at this size. */
|
|
789
|
+
.tlui-cmt-thread .tlui-cmt-avatar,
|
|
790
|
+
.tlui-cmt-canvas-preview--thread .tlui-cmt-avatar,
|
|
791
|
+
.tlui-cmt-canvas-preview--list .tlui-cmt-avatar,
|
|
792
|
+
.tlui-cmt-stack-list .tlui-cmt-avatar {
|
|
793
|
+
width: 12px;
|
|
794
|
+
height: 12px;
|
|
795
|
+
font-size: 0;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
.tlui-cmt-thread .tlui-cmt-composer {
|
|
799
|
+
padding: 12px;
|
|
800
|
+
}
|
|
801
|
+
.tlui-cmt-thread .tlui-cmt-composer .tlui-cmt-avatar {
|
|
802
|
+
display: none;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/* The sidebar's rows use the same small colour dot as the thread view. */
|
|
806
|
+
.tlui-cmt-list .tlui-cmt-avatar {
|
|
807
|
+
width: 14px;
|
|
808
|
+
height: 14px;
|
|
809
|
+
font-size: 0;
|
|
810
|
+
margin-top: 2px;
|
|
811
|
+
}
|
|
812
|
+
/* The sidebar row already leads with its own avatar (the flex row's first column); the byline's
|
|
813
|
+
avatar — added for the canvas thread views — would double it, so hide the byline's inside a row. */
|
|
814
|
+
.tlui-cmt-list .tlui-cmt-head .tlui-cmt-avatar {
|
|
815
|
+
display: none;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/* The dot treatment is for colour-only identity — an image avatar stays full size. */
|
|
819
|
+
.tlui-cmt-thread .tlui-cmt-avatar--image,
|
|
820
|
+
.tlui-cmt-canvas-preview--thread .tlui-cmt-avatar--image,
|
|
821
|
+
.tlui-cmt-canvas-preview--list .tlui-cmt-avatar--image,
|
|
822
|
+
.tlui-cmt-stack-list .tlui-cmt-avatar--image {
|
|
823
|
+
/* Match the 12px colour dot's width so the body's calc(12px + 6px) indent still lines up under
|
|
824
|
+
the byline name when the author has an image rather than a dot. */
|
|
825
|
+
width: 12px;
|
|
826
|
+
height: 12px;
|
|
827
|
+
margin-top: 0;
|
|
828
|
+
}
|
|
829
|
+
.tlui-cmt-list .tlui-cmt-avatar--image {
|
|
830
|
+
width: 24px;
|
|
831
|
+
height: 24px;
|
|
832
|
+
margin-top: 0;
|
|
526
833
|
}
|
|
834
|
+
|
|
527
835
|
/* inside the panel, comments are plain content — no surrounding box; the composer's field carries
|
|
528
|
-
its own surface.
|
|
529
|
-
|
|
836
|
+
its own surface. The hover preview is the same panel without a header, so its comment is laid
|
|
837
|
+
out by the same rule rather than a copy of it. */
|
|
838
|
+
.tlui-cmt-thread .tlui-cmt-card,
|
|
839
|
+
.tlui-cmt-canvas-preview--thread .tlui-cmt-card {
|
|
530
840
|
width: 100%;
|
|
531
|
-
|
|
841
|
+
gap: 0;
|
|
842
|
+
padding: 0 12px;
|
|
532
843
|
background: transparent;
|
|
533
844
|
border: none;
|
|
534
845
|
}
|
|
846
|
+
/* The avatar is a 12px dot, so the body lines up 12px + the head's 6px gap in from the row. */
|
|
847
|
+
.tlui-cmt-thread .tlui-cmt-body,
|
|
848
|
+
.tlui-cmt-canvas-preview--thread .tlui-cmt-body,
|
|
849
|
+
.tlui-cmt-canvas-preview--list .tlui-cmt-body,
|
|
850
|
+
.tlui-cmt-stack-list .tlui-cmt-body {
|
|
851
|
+
padding-left: calc(12px + 6px);
|
|
852
|
+
}
|
|
853
|
+
/* Inside a panel the actions float as their own pill — the same pill the thread header is, in the
|
|
854
|
+
same corner (which is why the header hides while the first comment is hovered). It sits just
|
|
855
|
+
above the card's top edge rather than on the byline. */
|
|
856
|
+
/* Line the card actions up with the header actions: flush to the full-width card's right edge (the
|
|
857
|
+
panel edge, matching the header ✕) and flush on the 40px byline row, so the 40px buttons fill it. */
|
|
858
|
+
.tlui-cmt-thread .tlui-cmt-card .tlui-cmt-card__actions {
|
|
859
|
+
top: 0;
|
|
860
|
+
right: 0;
|
|
861
|
+
}
|
|
862
|
+
/* The byline row is 40px: on the opened thread it fits the action pills (react/edit/delete) that
|
|
863
|
+
float in its corner without the text running underneath; previews and collapsed cards carry the
|
|
864
|
+
same height so every comment row reads identically, solo or in a stack. */
|
|
865
|
+
.tlui-cmt-thread .tlui-cmt-card .tlui-cmt-head,
|
|
866
|
+
.tlui-cmt-canvas-preview--thread .tlui-cmt-card .tlui-cmt-head,
|
|
867
|
+
.tlui-cmt-canvas-preview--list .tlui-cmt-card .tlui-cmt-head,
|
|
868
|
+
.tlui-cmt-stack-list .tlui-cmt-card .tlui-cmt-head {
|
|
869
|
+
min-height: 40px;
|
|
870
|
+
}
|
|
871
|
+
/* Only the opened thread's comments actually carry the pills, so only they reserve room for them.
|
|
872
|
+
Derived from the action size so it can't drift: n buttons + (n−1) 2px gaps + the pill's 2px
|
|
873
|
+
padding either side + its 2px right inset. One button (react) on others' comments. */
|
|
874
|
+
.tlui-cmt-thread .tlui-cmt-card .tlui-cmt-head {
|
|
875
|
+
padding-right: calc(var(--tlui-cmt-thread-action-size) + 6px);
|
|
876
|
+
}
|
|
877
|
+
.tlui-cmt-thread .tlui-cmt-card--you .tlui-cmt-head {
|
|
878
|
+
padding-right: calc(3 * var(--tlui-cmt-thread-action-size) + 10px);
|
|
879
|
+
}
|
|
535
880
|
|
|
536
881
|
/* comments list — thread summaries, one per row (used by the canvas sidebar) */
|
|
537
|
-
.cmt-list {
|
|
882
|
+
.tlui-cmt-list {
|
|
538
883
|
display: flex;
|
|
539
884
|
flex-direction: column;
|
|
540
885
|
width: 100%;
|
|
541
886
|
min-height: 0;
|
|
542
887
|
}
|
|
543
|
-
.cmt-list__header {
|
|
888
|
+
.tlui-cmt-list__header {
|
|
544
889
|
flex-shrink: 0;
|
|
545
890
|
display: flex;
|
|
546
891
|
align-items: center;
|
|
@@ -549,16 +894,15 @@
|
|
|
549
894
|
padding: 6px 6px 6px 14px;
|
|
550
895
|
border-bottom: 1px solid var(--tl-color-divider);
|
|
551
896
|
}
|
|
552
|
-
.cmt-list__header-title {
|
|
553
|
-
font-size:
|
|
554
|
-
font-weight:
|
|
555
|
-
|
|
556
|
-
text-transform: uppercase;
|
|
557
|
-
color: var(--tl-color-text-3);
|
|
897
|
+
.tlui-cmt-list__header-title {
|
|
898
|
+
font-size: 12px;
|
|
899
|
+
font-weight: 500;
|
|
900
|
+
color: var(--tl-color-text-1);
|
|
558
901
|
}
|
|
559
902
|
|
|
560
903
|
/* a small square icon button in the sidebar header (filter funnel, overflow …) */
|
|
561
|
-
.cmt-header-btn {
|
|
904
|
+
.tlui-cmt-header-btn {
|
|
905
|
+
position: relative;
|
|
562
906
|
display: flex;
|
|
563
907
|
align-items: center;
|
|
564
908
|
justify-content: center;
|
|
@@ -571,51 +915,83 @@
|
|
|
571
915
|
color: var(--tl-color-text-2);
|
|
572
916
|
cursor: pointer;
|
|
573
917
|
}
|
|
574
|
-
|
|
575
|
-
|
|
918
|
+
/* Same hit-target expansion as the thread actions, tiled the same way — this row's gap is 2px too
|
|
919
|
+
(see `.tlui-cmt-list__header-actions`). */
|
|
920
|
+
.tlui-cmt-header-btn::after {
|
|
921
|
+
content: '';
|
|
922
|
+
position: absolute;
|
|
923
|
+
inset: -6px -1px;
|
|
924
|
+
}
|
|
925
|
+
.tlui-cmt-header-btn:first-child::after {
|
|
926
|
+
left: -6px;
|
|
927
|
+
}
|
|
928
|
+
.tlui-cmt-header-btn:last-child::after {
|
|
929
|
+
right: -6px;
|
|
930
|
+
}
|
|
931
|
+
.tlui-cmt-header-btn:hover,
|
|
932
|
+
.tlui-cmt-header-btn[data-state='open'] {
|
|
576
933
|
background: var(--tl-color-muted-2);
|
|
577
934
|
color: var(--tl-color-text-1);
|
|
578
935
|
}
|
|
579
|
-
.cmt-list__header-actions {
|
|
936
|
+
.tlui-cmt-list__header-actions {
|
|
580
937
|
display: flex;
|
|
581
938
|
align-items: center;
|
|
582
939
|
gap: 2px;
|
|
583
940
|
}
|
|
584
941
|
|
|
942
|
+
/* The commenting dropdowns' surface — a little air between the items and the menu edge. */
|
|
943
|
+
.tlui-cmt-menu {
|
|
944
|
+
min-width: 100px;
|
|
945
|
+
padding: 4px;
|
|
946
|
+
}
|
|
947
|
+
|
|
585
948
|
/* a menu row with a label and a right-aligned shortcut hint (overflow menu) */
|
|
586
|
-
.cmt-menu-item {
|
|
949
|
+
.tlui-cmt-menu-item {
|
|
950
|
+
position: relative;
|
|
587
951
|
display: flex;
|
|
588
952
|
align-items: center;
|
|
589
953
|
justify-content: space-between;
|
|
590
954
|
gap: 24px;
|
|
591
955
|
width: 100%;
|
|
592
|
-
|
|
956
|
+
min-height: 40px;
|
|
957
|
+
padding: 0 10px;
|
|
593
958
|
border: none;
|
|
594
|
-
border-radius: 6px;
|
|
595
959
|
background: transparent;
|
|
596
960
|
font: inherit;
|
|
597
|
-
font-size:
|
|
961
|
+
font-size: 12px;
|
|
598
962
|
color: var(--tl-color-text-1);
|
|
599
963
|
cursor: pointer;
|
|
600
964
|
}
|
|
601
|
-
|
|
965
|
+
/* 36px visual inset within the 40px row — the hover wash the action and emoji buttons use. */
|
|
966
|
+
.tlui-cmt-menu-item::after {
|
|
967
|
+
content: '';
|
|
968
|
+
position: absolute;
|
|
969
|
+
inset: 2px;
|
|
970
|
+
border-radius: var(--tl-radius-2);
|
|
971
|
+
}
|
|
972
|
+
.tlui-cmt-menu-item:hover::after {
|
|
602
973
|
background: var(--tl-color-muted-2);
|
|
603
974
|
}
|
|
604
|
-
|
|
975
|
+
/* Destructive menu entries (delete) — the same danger colour as tldraw's danger buttons. */
|
|
976
|
+
.tlui-cmt-menu-item--danger {
|
|
977
|
+
color: var(--tl-color-danger);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
.tlui-cmt-menu-item__shortcut {
|
|
605
981
|
color: var(--tl-color-text-3);
|
|
606
982
|
font-size: 12px;
|
|
607
983
|
}
|
|
608
|
-
.cmt-list__items {
|
|
984
|
+
.tlui-cmt-list__items {
|
|
609
985
|
flex: 1;
|
|
610
986
|
min-height: 0;
|
|
611
987
|
overflow-y: auto;
|
|
612
988
|
}
|
|
613
|
-
.cmt-list__empty {
|
|
989
|
+
.tlui-cmt-list__empty {
|
|
614
990
|
padding: 20px 14px;
|
|
615
991
|
color: var(--tl-color-text-3);
|
|
616
|
-
font-size:
|
|
992
|
+
font-size: 12px;
|
|
617
993
|
}
|
|
618
|
-
.cmt-list__item {
|
|
994
|
+
.tlui-cmt-list__item {
|
|
619
995
|
display: flex;
|
|
620
996
|
gap: 10px;
|
|
621
997
|
width: 100%;
|
|
@@ -626,34 +1002,48 @@
|
|
|
626
1002
|
font: inherit;
|
|
627
1003
|
text-align: left;
|
|
628
1004
|
cursor: pointer;
|
|
1005
|
+
/* rows can render as anchors (for open-in-new-tab); keep them looking like list rows */
|
|
1006
|
+
color: inherit;
|
|
1007
|
+
text-decoration: none;
|
|
629
1008
|
}
|
|
630
|
-
.cmt-list__item:hover {
|
|
1009
|
+
.tlui-cmt-list__item:hover {
|
|
631
1010
|
background: var(--tl-color-muted-2);
|
|
632
1011
|
}
|
|
633
|
-
|
|
634
|
-
|
|
1012
|
+
/* Selected sits a step above hover so the open thread's row stays distinguishable while
|
|
1013
|
+
pointing at other rows — but only a step: the muted-1 token (10%) reads too heavy here.
|
|
1014
|
+
These rows sit on the opaque sidebar panel, so a translucent tint composes safely. */
|
|
1015
|
+
.tlui-cmt-list__item--selected,
|
|
1016
|
+
.tlui-cmt-list__item--selected:hover {
|
|
1017
|
+
background: hsl(0, 0%, 0%, 5%);
|
|
1018
|
+
}
|
|
1019
|
+
.tl-theme__dark .tlui-cmt-list__item--selected,
|
|
1020
|
+
.tl-theme__dark .tlui-cmt-list__item--selected:hover {
|
|
1021
|
+
background: hsl(0, 0%, 100%, 5%);
|
|
635
1022
|
}
|
|
636
1023
|
/* resolved rows mute their content but keep the "Resolved" marker legible */
|
|
637
|
-
.cmt-list__item[data-resolved] .cmt-head,
|
|
638
|
-
.cmt-list__item[data-resolved] .cmt-list__item-preview {
|
|
1024
|
+
.tlui-cmt-list__item[data-resolved] .tlui-cmt-head,
|
|
1025
|
+
.tlui-cmt-list__item[data-resolved] .tlui-cmt-list__item-preview {
|
|
639
1026
|
opacity: 0.55;
|
|
640
1027
|
}
|
|
641
|
-
.cmt-list__item-body {
|
|
1028
|
+
.tlui-cmt-list__item-body {
|
|
642
1029
|
flex: 1;
|
|
643
1030
|
min-width: 0;
|
|
644
1031
|
}
|
|
645
|
-
.cmt-list__item-preview {
|
|
1032
|
+
.tlui-cmt-list__item-preview {
|
|
646
1033
|
margin-top: 2px;
|
|
647
|
-
font-size:
|
|
1034
|
+
font-size: 12px;
|
|
648
1035
|
color: var(--tl-color-text-1);
|
|
649
1036
|
overflow: hidden;
|
|
650
1037
|
text-overflow: ellipsis;
|
|
651
1038
|
display: -webkit-box;
|
|
652
1039
|
-webkit-line-clamp: 2;
|
|
653
1040
|
-webkit-box-orient: vertical;
|
|
1041
|
+
/* Long unbroken strings (URLs, mashed keys) wrap onto the clamped lines instead of clipping
|
|
1042
|
+
mid-string — same treatment as the card body. */
|
|
1043
|
+
overflow-wrap: anywhere;
|
|
654
1044
|
}
|
|
655
1045
|
/* meta line under the preview: page label and/or a resolved marker */
|
|
656
|
-
.cmt-list__item-meta {
|
|
1046
|
+
.tlui-cmt-list__item-meta {
|
|
657
1047
|
display: flex;
|
|
658
1048
|
align-items: center;
|
|
659
1049
|
gap: 8px;
|
|
@@ -661,92 +1051,371 @@
|
|
|
661
1051
|
font-size: 11px;
|
|
662
1052
|
color: var(--tl-color-text-3);
|
|
663
1053
|
}
|
|
664
|
-
.cmt-list__item-resolved {
|
|
1054
|
+
.tlui-cmt-list__item-resolved {
|
|
665
1055
|
display: inline-flex;
|
|
666
1056
|
align-items: center;
|
|
667
1057
|
gap: 3px;
|
|
668
1058
|
font-weight: 600;
|
|
669
1059
|
}
|
|
670
|
-
.cmt-list__item-page {
|
|
1060
|
+
.tlui-cmt-list__item-page {
|
|
671
1061
|
display: inline-flex;
|
|
672
1062
|
align-items: center;
|
|
673
1063
|
padding: 1px 6px;
|
|
674
1064
|
border-radius: 4px;
|
|
675
1065
|
background: var(--tl-color-muted-2);
|
|
676
1066
|
}
|
|
677
|
-
.cmt-list__item-
|
|
678
|
-
|
|
679
|
-
align-
|
|
1067
|
+
.tlui-cmt-list__item-replies {
|
|
1068
|
+
display: inline-flex;
|
|
1069
|
+
align-items: center;
|
|
1070
|
+
}
|
|
1071
|
+
/* reply count under a preview card's opening comment — the thread continues past what's shown */
|
|
1072
|
+
.tlui-cmt-card__replies {
|
|
1073
|
+
display: block;
|
|
1074
|
+
margin-top: 6px;
|
|
680
1075
|
font-size: 12px;
|
|
681
1076
|
color: var(--tl-color-text-3);
|
|
682
1077
|
}
|
|
683
1078
|
|
|
684
1079
|
/* The comments layer is portaled into the editor container. Pins live in the canvas-in-front layer
|
|
685
1080
|
(beneath the UI panels at z-index 300), so the toolbar and style panel draw over them; the open
|
|
686
|
-
thread's popover portals up to the menus layer separately (`.cmt-canvas-popover`) so it isn't
|
|
1081
|
+
thread's popover portals up to the menus layer separately (`.tlui-cmt-canvas-popover`) so it isn't
|
|
687
1082
|
clipped. Click-through except on its own children. */
|
|
688
|
-
.cmt-canvas-layer {
|
|
1083
|
+
.tlui-cmt-canvas-layer {
|
|
689
1084
|
position: absolute;
|
|
690
1085
|
inset: 0;
|
|
691
|
-
|
|
1086
|
+
/* Just below the collaborator cursor layer, so cursors ride over the pins — and below the
|
|
1087
|
+
in-front content (250), so active tool UI covers both. Derived, not a literal, so the
|
|
1088
|
+
ordering can't silently drift if the cursor slot moves. */
|
|
1089
|
+
z-index: calc(var(--tl-layer-collaborators) - 5);
|
|
692
1090
|
pointer-events: none;
|
|
693
1091
|
}
|
|
694
1092
|
|
|
695
|
-
.cmt-canvas-pin,
|
|
696
|
-
.cmt-canvas-cluster,
|
|
697
|
-
.cmt-canvas-composer {
|
|
1093
|
+
.tlui-cmt-canvas-pin,
|
|
1094
|
+
.tlui-cmt-canvas-cluster,
|
|
1095
|
+
.tlui-cmt-canvas-composer {
|
|
698
1096
|
position: absolute;
|
|
699
1097
|
}
|
|
700
1098
|
|
|
701
|
-
.cmt-canvas-
|
|
702
|
-
.cmt-canvas-composer {
|
|
1099
|
+
.tlui-cmt-canvas-composer {
|
|
703
1100
|
pointer-events: auto;
|
|
704
1101
|
}
|
|
705
1102
|
|
|
706
|
-
/*
|
|
707
|
-
.
|
|
708
|
-
|
|
1103
|
+
/* The pin's only interactive surface is the square hit layer below — the wrapper itself is
|
|
1104
|
+
inert. Its box sits at the anchor point, not over the (translated) visual, so letting it take
|
|
1105
|
+
events would put a phantom hit zone beside the pin. */
|
|
1106
|
+
.tlui-cmt-canvas-pin {
|
|
1107
|
+
pointer-events: none;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
.tlui-cmt-button {
|
|
1111
|
+
appearance: none;
|
|
1112
|
+
margin: 0;
|
|
1113
|
+
padding: 0;
|
|
1114
|
+
border: none;
|
|
1115
|
+
background: none;
|
|
1116
|
+
color: inherit;
|
|
1117
|
+
font: inherit;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/* Anchored by its bottom-left corner like a pin, so the badge sits up-and-right of the cluster's
|
|
1121
|
+
centroid — the same visual-to-anchor relationship a single comment pin has. */
|
|
1122
|
+
.tlui-cmt-canvas-cluster {
|
|
1123
|
+
transform: translate(0, -100%);
|
|
709
1124
|
pointer-events: auto;
|
|
710
1125
|
cursor: pointer;
|
|
711
1126
|
}
|
|
712
1127
|
|
|
713
|
-
|
|
1128
|
+
/* Keyboard focus has to be visible on the canvas, where there's no surrounding chrome to imply
|
|
1129
|
+
it. The ring goes on the marker visual (the rounded pin/badge), not the button box, so it
|
|
1130
|
+
traces the shape rather than a rectangle around it; the button's own outline is suppressed
|
|
1131
|
+
because its box is offset from what you see. */
|
|
1132
|
+
.tlui-cmt-canvas-pin__marker:focus-visible,
|
|
1133
|
+
.tlui-cmt-canvas-cluster:focus-visible {
|
|
1134
|
+
outline: none;
|
|
1135
|
+
}
|
|
1136
|
+
.tlui-cmt-canvas-pin__marker:focus-visible .tlui-cmt-marker,
|
|
1137
|
+
.tlui-cmt-canvas-cluster:focus-visible .tlui-cmt-marker {
|
|
1138
|
+
outline: 2px solid var(--tl-color-selected);
|
|
1139
|
+
outline-offset: 2px;
|
|
1140
|
+
}
|
|
1141
|
+
/* Focus should read like hover, so the pin lifts the same way when tabbed to. */
|
|
1142
|
+
.tlui-cmt-canvas-pin__marker:focus-visible .tlui-cmt-marker {
|
|
1143
|
+
transform: scale(var(--tlui-cmt-marker-hover-scale));
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
.tlui-cmt-cluster-fade {
|
|
714
1147
|
opacity: 1;
|
|
715
1148
|
transition: opacity 150ms ease;
|
|
716
1149
|
}
|
|
717
1150
|
|
|
718
|
-
.cmt-cluster-fade--entering {
|
|
1151
|
+
.tlui-cmt-cluster-fade--entering {
|
|
719
1152
|
opacity: 0;
|
|
720
1153
|
}
|
|
721
1154
|
|
|
722
|
-
.cmt-cluster-fade--present {
|
|
1155
|
+
.tlui-cmt-cluster-fade--present {
|
|
723
1156
|
opacity: 1;
|
|
724
1157
|
}
|
|
725
1158
|
|
|
726
|
-
.cmt-cluster-fade--exiting {
|
|
1159
|
+
.tlui-cmt-cluster-fade--exiting {
|
|
727
1160
|
opacity: 0;
|
|
728
1161
|
}
|
|
729
1162
|
|
|
730
|
-
.cmt-cluster-fade--entering *,
|
|
731
|
-
.cmt-cluster-fade--exiting * {
|
|
1163
|
+
.tlui-cmt-cluster-fade--entering *,
|
|
1164
|
+
.tlui-cmt-cluster-fade--exiting * {
|
|
732
1165
|
pointer-events: none !important;
|
|
733
1166
|
}
|
|
734
1167
|
|
|
735
1168
|
@media (prefers-reduced-motion: reduce) {
|
|
736
|
-
.cmt-cluster-fade {
|
|
1169
|
+
.tlui-cmt-cluster-fade {
|
|
737
1170
|
transition: none;
|
|
738
1171
|
}
|
|
739
1172
|
}
|
|
740
1173
|
|
|
741
1174
|
/* The open thread (its pin + popover) sits above the other pins, so nearby markers don't
|
|
742
1175
|
overlap its popover — they tuck behind it. */
|
|
743
|
-
.cmt-canvas-pin--open {
|
|
1176
|
+
.tlui-cmt-canvas-pin--open {
|
|
744
1177
|
z-index: 1;
|
|
745
1178
|
}
|
|
746
1179
|
|
|
1180
|
+
/* The count badge standing in for a stack of coincident pins — same face as a cluster badge,
|
|
1181
|
+
but clicking opens the stack's thread list rather than zooming (no zoom can separate them).
|
|
1182
|
+
Re-enables pointer events: the badge lives inside `.tlui-cmt-canvas-pin`, which is inert
|
|
1183
|
+
(`pointer-events: none`) so its box doesn't shadow the canvas — without this the badge renders
|
|
1184
|
+
but can't be hovered or clicked, like the cluster badge's own `pointer-events: auto`. */
|
|
1185
|
+
.tlui-cmt-canvas-stack-badge {
|
|
1186
|
+
/* Bottom-left anchored like the pins it stands in for, and like the cluster badge. */
|
|
1187
|
+
transform: translate(0, -100%);
|
|
1188
|
+
width: max-content;
|
|
1189
|
+
pointer-events: auto;
|
|
1190
|
+
cursor: pointer;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
.tlui-cmt-canvas-stack-badge:focus-visible {
|
|
1194
|
+
outline: none;
|
|
1195
|
+
}
|
|
1196
|
+
.tlui-cmt-canvas-stack-badge:focus-visible .tlui-cmt-marker {
|
|
1197
|
+
outline: 2px solid var(--tl-color-selected);
|
|
1198
|
+
outline-offset: 2px;
|
|
1199
|
+
transform: scale(var(--tlui-cmt-marker-hover-scale));
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
/* The stack's popover content: one card per thread, the expanded thread inline among them. */
|
|
1203
|
+
.tlui-cmt-stack-list {
|
|
1204
|
+
display: flex;
|
|
1205
|
+
flex-direction: column;
|
|
1206
|
+
gap: 10px;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
/* The expanded thread is the only entry with pills floating above its panel — its header actions,
|
|
1210
|
+
and its first comment's hover actions. Those reach ~15px up, past the list's 10px gap, so give
|
|
1211
|
+
this one entry extra room: without it the pills land on the card above, and against the scroll
|
|
1212
|
+
box's clip edge. Collapsed cards keep their own actions inside, so they need none of this. */
|
|
1213
|
+
.tlui-cmt-stack-list__thread {
|
|
1214
|
+
margin-top: 8px;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
/* Collapsed entries match the expanded thread's surface (same width, panel, elevation) so the
|
|
1218
|
+
list reads as one column of cards with one of them opened up. */
|
|
1219
|
+
.tlui-cmt-stack-list__card {
|
|
1220
|
+
box-sizing: border-box;
|
|
1221
|
+
position: relative;
|
|
1222
|
+
width: 300px;
|
|
1223
|
+
background: var(--tl-color-panel);
|
|
1224
|
+
border-radius: 12px;
|
|
1225
|
+
box-shadow: var(--tl-shadow-3);
|
|
1226
|
+
padding: 2px 12px 10px;
|
|
1227
|
+
cursor: pointer;
|
|
1228
|
+
text-align: left;
|
|
1229
|
+
display: block;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/* The card's keyboard affordance, covering the whole surface so it's one target rather than a
|
|
1233
|
+
control tucked in a corner. It's laid under the card's content instead of wrapping it: a comment
|
|
1234
|
+
body renders its links as anchors, which can't be nested inside a button (see the note in
|
|
1235
|
+
`StackThreadCard`). Pointer clicks anywhere still reach the card's own handler by bubbling. */
|
|
1236
|
+
.tlui-cmt-stack-list__card-action {
|
|
1237
|
+
position: absolute;
|
|
1238
|
+
inset: 0;
|
|
1239
|
+
border-radius: inherit;
|
|
1240
|
+
cursor: pointer;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
.tlui-cmt-stack-list__card-action:focus-visible {
|
|
1244
|
+
outline: 2px solid var(--tl-color-selected);
|
|
1245
|
+
outline-offset: 2px;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
/* The wrapper is the card's surface — inside it the comment is plain content, mirroring how the
|
|
1249
|
+
thread panel flattens its own cards. Stacked above the action overlay so the body's links stay
|
|
1250
|
+
clickable. */
|
|
1251
|
+
.tlui-cmt-stack-list__card .tlui-cmt-card {
|
|
1252
|
+
position: relative;
|
|
1253
|
+
width: 100%;
|
|
1254
|
+
gap: 0;
|
|
1255
|
+
padding: 0;
|
|
1256
|
+
background: transparent;
|
|
1257
|
+
border: none;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
/* The hover preview, shown for any marker — a pin, a coincident stack, or a cluster badge. Live,
|
|
1261
|
+
not a tooltip: the pointer travels into it to hover and click the cards.
|
|
1262
|
+
|
|
1263
|
+
Two elements, deliberately: this root is the hover *region* (it reaches back over the marker via
|
|
1264
|
+
the bridge below), and `__panel` inside it is the visible surface. A pseudo-element's hover
|
|
1265
|
+
counts as its host's, so a surface on this root would light up whenever the pointer was on the
|
|
1266
|
+
marker — the pin would appear pressed just for being hovered. */
|
|
1267
|
+
.tlui-cmt-canvas-preview {
|
|
1268
|
+
position: absolute;
|
|
1269
|
+
z-index: var(--tl-layer-menus);
|
|
1270
|
+
pointer-events: auto;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
/* The bridge over the gap between the marker and the panel. Moving right off the pin would
|
|
1274
|
+
otherwise cross bare canvas, and that leave would retract the panel before the pointer arrived.
|
|
1275
|
+
Invisible, part of the panel's hover region, and only present while the panel is.
|
|
1276
|
+
|
|
1277
|
+
It spans the gap and stops there. Reaching any further would put it over the marker itself, and
|
|
1278
|
+
since it sits in the menus layer — above the pins — it would take the marker's hover and clicks:
|
|
1279
|
+
the pin would go dead exactly where it looks most alive. The marker needs no covering anyway, as
|
|
1280
|
+
it carries the same hover handlers.
|
|
1281
|
+
|
|
1282
|
+
The gap is the panel's own offset (`--tlui-cmt-preview-offset`, set from `POPOVER_OFFSET` so
|
|
1283
|
+
there's one source) less however far the marker reaches toward it, per variant below. */
|
|
1284
|
+
.tlui-cmt-canvas-preview::before {
|
|
1285
|
+
content: '';
|
|
1286
|
+
position: absolute;
|
|
1287
|
+
top: 0;
|
|
1288
|
+
bottom: 0;
|
|
1289
|
+
right: 100%;
|
|
1290
|
+
width: max(0px, var(--tlui-cmt-preview-bridge, 0px));
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
/* A pin is anchored at its bottom-left corner, so its full width lies between anchor and panel.
|
|
1294
|
+
The popover and preview share a first-comment position now that the actions float outside the
|
|
1295
|
+
panel rather than reserving a header row, so no vertical shift is needed. */
|
|
1296
|
+
.tlui-cmt-canvas-preview--thread {
|
|
1297
|
+
--tlui-cmt-preview-bridge: calc(var(--tlui-cmt-preview-offset) - var(--tlui-cmt-pin-size));
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
/* A badge is anchored at its bottom-left corner like a pin, so its full width does too. */
|
|
1301
|
+
.tlui-cmt-canvas-preview--list {
|
|
1302
|
+
--tlui-cmt-preview-bridge: calc(var(--tlui-cmt-preview-offset) - var(--tlui-cmt-marker-size));
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
.tlui-cmt-canvas-preview__panel {
|
|
1306
|
+
display: flex;
|
|
1307
|
+
flex-direction: column;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
/* Previewing a single pin: the thread panel itself, minus the header — the same surface, width,
|
|
1311
|
+
and flattened comment (see `.tlui-cmt-thread .tlui-cmt-card`), so the hover shows exactly what
|
|
1312
|
+
the click opens. */
|
|
1313
|
+
.tlui-cmt-canvas-preview__panel--thread {
|
|
1314
|
+
box-sizing: border-box;
|
|
1315
|
+
width: 300px;
|
|
1316
|
+
gap: var(--tlui-cmt-thread-gap);
|
|
1317
|
+
/* Top/bottom only — the card pads its own sides now (12px), matching the expanded thread. A
|
|
1318
|
+
little extra below so the (often truncated) last line doesn't sit against the rounded edge. */
|
|
1319
|
+
padding: 4px 0 8px;
|
|
1320
|
+
background: var(--tl-color-panel);
|
|
1321
|
+
border-radius: var(--tl-radius-4);
|
|
1322
|
+
box-shadow: var(--tl-shadow-3);
|
|
1323
|
+
}
|
|
1324
|
+
/* The reply count is a short line that sits closer to the edge than a wrapped comment does, so give
|
|
1325
|
+
it more room below when it's the panel's last line. */
|
|
1326
|
+
.tlui-cmt-canvas-preview__panel--thread:has(.tlui-cmt-card__replies) {
|
|
1327
|
+
padding-bottom: 12px;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
/* Previewing a stack or cluster: one card per thread, matching the card list a click opens. */
|
|
1331
|
+
.tlui-cmt-canvas-preview__panel--list {
|
|
1332
|
+
gap: 8px;
|
|
1333
|
+
padding: 4px 0 8px;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
/* Matches the stack list's card surface, so a preview and the list it previews read as the same
|
|
1337
|
+
object seen twice — once on hover, once opened. */
|
|
1338
|
+
.tlui-cmt-preview-card {
|
|
1339
|
+
box-sizing: border-box;
|
|
1340
|
+
width: 300px;
|
|
1341
|
+
background: var(--tl-color-panel);
|
|
1342
|
+
border-radius: 12px;
|
|
1343
|
+
box-shadow: var(--tl-shadow-3);
|
|
1344
|
+
/* A 2px nudge above the byline's centring — a boxed card reads a touch better with it than the
|
|
1345
|
+
single preview's flat card, which pads 0 vertically and leans on the panel + byline. */
|
|
1346
|
+
padding: 2px 12px 10px;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
.tlui-cmt-preview-card--selectable {
|
|
1350
|
+
cursor: pointer;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
/* Every card surface that tints on hover — a collapsed thread in a stack list, a preview card, and
|
|
1354
|
+
the single-comment preview panel. One rule, because they're the same object to the eye.
|
|
1355
|
+
|
|
1356
|
+
The opaque low-surface token, not a translucent muted one: these float directly over the canvas,
|
|
1357
|
+
where a translucent fill would let shapes show through.
|
|
1358
|
+
|
|
1359
|
+
The second line is why this isn't a one-liner. `--tl-shadow-3` ends with a 1px *inset* ring in
|
|
1360
|
+
`--tl-color-panel-contrast` — pure white in light mode — and an inset shadow paints over the
|
|
1361
|
+
background. At rest that's invisible (the panel is 99% white under a 100% white ring), but tint
|
|
1362
|
+
the background to grey and the ring stays white, hugging the edge as a pale outline. Re-tinting
|
|
1363
|
+
`--tl-color-panel-contrast` here wouldn't reach it: `--tl-shadow-3` is declared on the container,
|
|
1364
|
+
so its var() was already substituted there and the white is baked into what we inherit. Instead
|
|
1365
|
+
lay a ring of the new surface over the old one — first shadow in the list paints on top. */
|
|
1366
|
+
.tlui-cmt-stack-list__card:hover,
|
|
1367
|
+
.tlui-cmt-preview-card--selectable:hover,
|
|
1368
|
+
.tlui-cmt-canvas-preview__panel--thread.tlui-cmt-canvas-preview__panel--selectable:hover {
|
|
1369
|
+
--tlui-cmt-preview-hover-surface: color-mix(
|
|
1370
|
+
in srgb,
|
|
1371
|
+
var(--tl-color-low) 50%,
|
|
1372
|
+
var(--tl-color-panel)
|
|
1373
|
+
);
|
|
1374
|
+
background: var(--tlui-cmt-preview-hover-surface);
|
|
1375
|
+
box-shadow:
|
|
1376
|
+
inset 0 0 0 1px var(--tlui-cmt-preview-hover-surface),
|
|
1377
|
+
var(--tl-shadow-3);
|
|
1378
|
+
cursor: pointer;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
.tlui-cmt-preview-card .tlui-cmt-card {
|
|
1382
|
+
width: 100%;
|
|
1383
|
+
gap: 0;
|
|
1384
|
+
padding: 0;
|
|
1385
|
+
background: transparent;
|
|
1386
|
+
border: none;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
/* A preview is a glance, not a list — past a few cards it says how many were left out rather than
|
|
1390
|
+
growing to the height of the cluster. */
|
|
1391
|
+
.tlui-cmt-preview-more {
|
|
1392
|
+
box-sizing: border-box;
|
|
1393
|
+
width: 300px;
|
|
1394
|
+
padding: 6px 14px;
|
|
1395
|
+
border-radius: 12px;
|
|
1396
|
+
background: var(--tl-color-panel);
|
|
1397
|
+
box-shadow: var(--tl-shadow-3);
|
|
1398
|
+
color: var(--tl-color-text-3);
|
|
1399
|
+
font-size: 12px;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
1403
|
+
.tlui-cmt-canvas-preview {
|
|
1404
|
+
animation: tlui-cmt-preview-in 120ms ease;
|
|
1405
|
+
}
|
|
1406
|
+
@keyframes tlui-cmt-preview-in {
|
|
1407
|
+
from {
|
|
1408
|
+
opacity: 0;
|
|
1409
|
+
}
|
|
1410
|
+
to {
|
|
1411
|
+
opacity: 1;
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
|
|
747
1416
|
/* A region anchor's area: a dashed box under the pins, non-interactive so canvas events pass
|
|
748
1417
|
through. Drawn for the live drag draft and for a region thread while hovered or open. */
|
|
749
|
-
.cmt-canvas-region {
|
|
1418
|
+
.tlui-cmt-canvas-region {
|
|
750
1419
|
position: absolute;
|
|
751
1420
|
pointer-events: none;
|
|
752
1421
|
border: 2px dashed var(--tl-color-selected);
|
|
@@ -756,13 +1425,13 @@
|
|
|
756
1425
|
|
|
757
1426
|
/* A movable region body (the 'body'/'both' move modes): draggable to translate the whole region.
|
|
758
1427
|
Interactive, so it captures the interior while shown — the tradeoff of dragging the body. */
|
|
759
|
-
.cmt-canvas-region--movable {
|
|
1428
|
+
.tlui-cmt-canvas-region--movable {
|
|
760
1429
|
pointer-events: auto;
|
|
761
1430
|
cursor: move;
|
|
762
1431
|
}
|
|
763
1432
|
|
|
764
1433
|
/* Corner resize handles on a region box (three corners; the pin corner is the move handle). */
|
|
765
|
-
.cmt-canvas-region-handle {
|
|
1434
|
+
.tlui-cmt-canvas-region-handle {
|
|
766
1435
|
position: absolute;
|
|
767
1436
|
width: 9px;
|
|
768
1437
|
height: 9px;
|
|
@@ -774,59 +1443,200 @@
|
|
|
774
1443
|
touch-action: none;
|
|
775
1444
|
}
|
|
776
1445
|
|
|
777
|
-
/*
|
|
778
|
-
|
|
1446
|
+
/* Anchor the pin by its bottom-left corner, so the marker's one sharp corner (the pin shape's
|
|
1447
|
+
3px bottom-left) points at the exact anchor point. */
|
|
1448
|
+
.tlui-cmt-canvas-pin__marker {
|
|
1449
|
+
position: relative;
|
|
779
1450
|
width: max-content;
|
|
780
|
-
transform: translate(
|
|
781
|
-
cursor
|
|
1451
|
+
transform: translate(0, -100%);
|
|
1452
|
+
/* Default at rest, like hovering a shape; the editor's move cursor while dragging (below). */
|
|
1453
|
+
cursor: var(--tl-cursor-default);
|
|
782
1454
|
touch-action: none;
|
|
1455
|
+
pointer-events: none;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
/* Dragging a pin shows the same move cursor as dragging a shape. */
|
|
1459
|
+
.tlui-cmt-canvas-pin--dragging .tlui-cmt-canvas-pin__marker {
|
|
1460
|
+
cursor: var(--tl-cursor-move);
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
/* The predictable hit area: a fixed square over the marker's box, and the only part of the pin
|
|
1464
|
+
that takes pointer events. Events over it target the marker (a pseudo-element's events report
|
|
1465
|
+
its originating element), so the marker's handlers and :hover keep working — but the hit area
|
|
1466
|
+
no longer stretches with pin content or scales with the hover transform. */
|
|
1467
|
+
.tlui-cmt-canvas-pin__marker::after {
|
|
1468
|
+
content: '';
|
|
1469
|
+
position: absolute;
|
|
1470
|
+
inset: 0;
|
|
1471
|
+
pointer-events: auto;
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
/* With the hit layer targeting the marker, the pin visual is never the hovered element itself
|
|
1475
|
+
(`:hover` matches the target and its ancestors, and the pin is the marker's child) — so the
|
|
1476
|
+
canvas pin's hover has to key off the hit layer instead of the marker's own `:hover`. Only the
|
|
1477
|
+
selector differs: the values come from the same tokens the shared rule uses, so a pin on the
|
|
1478
|
+
canvas and a badge (which is hovered directly) lift by exactly the same amount. */
|
|
1479
|
+
.tlui-cmt-canvas-pin__marker:hover .tlui-cmt-marker {
|
|
1480
|
+
transform: scale(var(--tlui-cmt-marker-hover-scale));
|
|
783
1481
|
}
|
|
784
|
-
.cmt-canvas-pin__marker:
|
|
785
|
-
|
|
1482
|
+
.tlui-cmt-canvas-pin__marker:hover .tlui-cmt-marker:not(.tlui-cmt-marker--open) {
|
|
1483
|
+
box-shadow: var(--tlui-cmt-marker-shadow-hover);
|
|
1484
|
+
}
|
|
1485
|
+
/* The comment pin doesn't scale on hover — only the shadow lifts (badges still scale). */
|
|
1486
|
+
.tlui-cmt-canvas-pin__marker:hover .tlui-cmt-pin {
|
|
1487
|
+
transform: none;
|
|
786
1488
|
}
|
|
787
1489
|
|
|
788
1490
|
/* The open thread's popover — portaled to the menus layer (above the UI), positioned at the pin. */
|
|
789
|
-
.cmt-canvas-popover {
|
|
1491
|
+
.tlui-cmt-canvas-popover {
|
|
790
1492
|
position: absolute;
|
|
791
1493
|
z-index: var(--tl-layer-menus);
|
|
792
1494
|
pointer-events: auto;
|
|
793
1495
|
}
|
|
794
1496
|
|
|
795
1497
|
/* The placement composer — a distinct floating view (portaled to the menus layer, below the click
|
|
796
|
-
point): a
|
|
797
|
-
|
|
798
|
-
.cmt-canvas-composer {
|
|
1498
|
+
point): a draft avatar pin beside a pill field that is itself the surface (no wrapping panel).
|
|
1499
|
+
Scoped here so the in-thread reply composer keeps its own squarer look. */
|
|
1500
|
+
.tlui-cmt-canvas-composer {
|
|
799
1501
|
position: absolute;
|
|
800
1502
|
z-index: var(--tl-layer-menus);
|
|
801
1503
|
pointer-events: auto;
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
}
|
|
807
|
-
|
|
1504
|
+
/* Anchor the draft avatar bottom-left like the pins it becomes: it is a pin wide and sits 4px
|
|
1505
|
+
into the row, so its bottom-left corner is 4px right of and 4px + a pin below the composer's
|
|
1506
|
+
top-left corner. */
|
|
1507
|
+
transform: translate(-4px, calc(-4px - var(--tlui-cmt-pin-size)));
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
/* A region placement centres the draft avatar on the region's pin corner — the pin it becomes
|
|
1511
|
+
straddles that corner. The avatar's centre sits 4px + half a pin into the composer. */
|
|
1512
|
+
.tlui-cmt-canvas-composer--region {
|
|
1513
|
+
--tlui-cmt-draft-avatar-centre: calc(4px + var(--tlui-cmt-pin-size) / 2);
|
|
1514
|
+
transform: translate(
|
|
1515
|
+
calc(-1 * var(--tlui-cmt-draft-avatar-centre)),
|
|
1516
|
+
calc(-1 * var(--tlui-cmt-draft-avatar-centre))
|
|
1517
|
+
);
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
/* Fallback placement (no composer to show): no draft avatar to anchor by, so no offset — a small
|
|
1521
|
+
panel hanging off the click point, sized to its content (e.g. the host's sign-in prompt). */
|
|
1522
|
+
.tlui-cmt-canvas-composer--fallback {
|
|
1523
|
+
transform: none;
|
|
1524
|
+
width: auto;
|
|
1525
|
+
display: flex;
|
|
1526
|
+
flex-direction: column;
|
|
1527
|
+
padding: 8px;
|
|
808
1528
|
background: var(--tl-color-panel);
|
|
809
|
-
border-color: transparent;
|
|
810
1529
|
border-radius: var(--tl-radius-4);
|
|
1530
|
+
box-shadow: var(--tl-shadow-3);
|
|
1531
|
+
}
|
|
1532
|
+
.tlui-cmt-canvas-composer .tlui-cmt-composer {
|
|
1533
|
+
gap: 6px;
|
|
1534
|
+
}
|
|
1535
|
+
/* The card: a fixed 300×42 surface whose uniform 3px padding derives the 294×36 focusable input. */
|
|
1536
|
+
.tlui-cmt-canvas-composer .tlui-cmt-composer__field {
|
|
1537
|
+
position: relative;
|
|
1538
|
+
flex: none;
|
|
1539
|
+
width: 300px;
|
|
1540
|
+
min-height: 42px;
|
|
1541
|
+
padding: 3px;
|
|
1542
|
+
border: none;
|
|
1543
|
+
background: var(--tl-color-panel);
|
|
1544
|
+
border-radius: var(--tl-radius-3);
|
|
1545
|
+
box-shadow: var(--tl-shadow-2);
|
|
1546
|
+
/* The pin is anchored by its bottom corner at the click point and can't move; raise the taller
|
|
1547
|
+
card onto the shorter pin's centre instead. */
|
|
1548
|
+
margin-top: -6px;
|
|
1549
|
+
}
|
|
1550
|
+
/* Focused (the placement composer autofocuses on mount): a selection-blue ring inset 3px from the
|
|
1551
|
+
card edge, so the card frames a bordered input rather than the border sitting on the card edge. */
|
|
1552
|
+
.tlui-cmt-canvas-composer .tlui-cmt-composer__field:focus-within::before {
|
|
1553
|
+
content: '';
|
|
1554
|
+
position: absolute;
|
|
1555
|
+
inset: 3px;
|
|
1556
|
+
border: 1px solid var(--tl-color-selected);
|
|
1557
|
+
border-radius: var(--tl-radius-2);
|
|
1558
|
+
pointer-events: none;
|
|
1559
|
+
}
|
|
1560
|
+
/* The text sits 12px in from the focus ring rather than against it. The placeholder is absolutely
|
|
1561
|
+
positioned (it ignores the wrap's padding), so it carries the same offset itself. */
|
|
1562
|
+
.tlui-cmt-canvas-composer .tlui-cmt-composer__input-wrap {
|
|
1563
|
+
padding: 4.5px 12px;
|
|
1564
|
+
}
|
|
1565
|
+
.tlui-cmt-canvas-composer .tlui-cmt-composer__field--expanded .tlui-cmt-composer__input-wrap {
|
|
1566
|
+
/* The base expanded rule re-pads the right edge for the squarer composer's geometry; here the
|
|
1567
|
+
12px is already symmetric. */
|
|
1568
|
+
padding-right: 12px;
|
|
1569
|
+
}
|
|
1570
|
+
.tlui-cmt-canvas-composer .tlui-cmt-input__placeholder {
|
|
1571
|
+
left: 12px;
|
|
1572
|
+
}
|
|
1573
|
+
/* The draft avatar previews the pin it becomes — a white 28px pin holding the author's 22px avatar
|
|
1574
|
+
circle — sharing the composer's lift. Not interactive: it drops the pin's pointer cursor and
|
|
1575
|
+
hover affordances (scale + shadow bump), but keeps a 40×40 hit area (the ::after below). */
|
|
1576
|
+
.tlui-cmt-canvas-composer .tlui-cmt-pin {
|
|
1577
|
+
position: relative;
|
|
1578
|
+
box-shadow: var(--tl-shadow-2);
|
|
1579
|
+
cursor: default;
|
|
1580
|
+
}
|
|
1581
|
+
.tlui-cmt-canvas-composer .tlui-cmt-pin::after {
|
|
1582
|
+
content: '';
|
|
1583
|
+
position: absolute;
|
|
1584
|
+
inset: -6px;
|
|
1585
|
+
}
|
|
1586
|
+
.tlui-cmt-canvas-composer .tlui-cmt-pin:hover {
|
|
1587
|
+
transform: none;
|
|
811
1588
|
box-shadow: var(--tl-shadow-2);
|
|
812
1589
|
}
|
|
1590
|
+
/* The placement composer's send button — a bare arrow, no pill: grey while empty, black once
|
|
1591
|
+
there's something to send. A 28px square centred in the 36px input height, so its hover wash
|
|
1592
|
+
clears the focus ring rather than butting against it; the ::after keeps a 40×40 hit area. */
|
|
1593
|
+
.tlui-cmt-canvas-composer .tlui-cmt-send {
|
|
1594
|
+
position: relative;
|
|
1595
|
+
width: 28px;
|
|
1596
|
+
height: 28px;
|
|
1597
|
+
margin-right: 4px;
|
|
1598
|
+
background: transparent;
|
|
1599
|
+
border-radius: var(--tl-radius-2);
|
|
1600
|
+
color: var(--tl-color-text-3);
|
|
1601
|
+
}
|
|
1602
|
+
.tlui-cmt-canvas-composer .tlui-cmt-send::after {
|
|
1603
|
+
content: '';
|
|
1604
|
+
position: absolute;
|
|
1605
|
+
inset: -6px;
|
|
1606
|
+
}
|
|
1607
|
+
.tlui-cmt-canvas-composer .tlui-cmt-send:not(:disabled) {
|
|
1608
|
+
color: var(--tl-color-text-1);
|
|
1609
|
+
}
|
|
1610
|
+
/* Hover and press wash grey behind the bare arrow (the same treatment as the thread actions),
|
|
1611
|
+
overriding the base button's blue fill. */
|
|
1612
|
+
.tlui-cmt-canvas-composer .tlui-cmt-send:hover:not(:disabled) {
|
|
1613
|
+
background: var(--tl-color-muted-2);
|
|
1614
|
+
}
|
|
1615
|
+
.tlui-cmt-canvas-composer .tlui-cmt-send:active:not(:disabled) {
|
|
1616
|
+
background: var(--tl-color-muted-1);
|
|
1617
|
+
}
|
|
1618
|
+
/* In the expanded (multi-line) layout the send drops to the bottom-right corner; give it the same
|
|
1619
|
+
4px gap from the focus ring on the bottom that it already keeps on the right. */
|
|
1620
|
+
.tlui-cmt-canvas-composer .tlui-cmt-composer__field--expanded .tlui-cmt-send {
|
|
1621
|
+
margin-bottom: 4px;
|
|
1622
|
+
}
|
|
813
1623
|
|
|
814
|
-
/* The comments list panel — a right-side surface shown while the
|
|
815
|
-
the popover layer, so an open thread still draws over it.
|
|
816
|
-
(top
|
|
817
|
-
than stretching the full canvas height. */
|
|
818
|
-
.cmt-canvas-sidebar {
|
|
1624
|
+
/* The comments list panel — a right-side surface shown while the comments sidebar is open. Below
|
|
1625
|
+
the popover layer, so an open thread still draws over it. Its top aligns flush with the style
|
|
1626
|
+
panel (48px from the top, clearing the share panel) and it sizes to its content, scrolling past
|
|
1627
|
+
max-height rather than stretching the full canvas height. */
|
|
1628
|
+
.tlui-cmt-canvas-sidebar {
|
|
819
1629
|
position: absolute;
|
|
820
|
-
top:
|
|
1630
|
+
top: 48px;
|
|
821
1631
|
right: 8px;
|
|
822
1632
|
width: 280px;
|
|
823
|
-
max-height: calc(100% -
|
|
1633
|
+
max-height: calc(100% - 56px);
|
|
824
1634
|
z-index: var(--tl-layer-panels);
|
|
825
1635
|
pointer-events: auto;
|
|
826
1636
|
display: flex;
|
|
827
1637
|
background: var(--tl-color-panel);
|
|
828
1638
|
border-radius: var(--tl-radius-4);
|
|
829
|
-
box-shadow: var(--tl-shadow-
|
|
1639
|
+
box-shadow: var(--tl-shadow-3);
|
|
830
1640
|
overflow: hidden;
|
|
831
1641
|
}
|
|
832
1642
|
|