@x1a0f3n9/dsh-client-ui-dockkit 0.1.5-rc.3
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/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +107 -0
- package/README.zh.md +107 -0
- package/lib/components/dockkit.module.css +731 -0
- package/lib/index.js +3133 -0
- package/lib/types/components/DockSurface.d.ts +60 -0
- package/lib/types/components/FloatLayer.d.ts +17 -0
- package/lib/types/components/PaneTree.d.ts +18 -0
- package/lib/types/components/TabMenu.d.ts +17 -0
- package/lib/types/components/TabPanel.d.ts +12 -0
- package/lib/types/components/TabTitle.d.ts +6 -0
- package/lib/types/components/measure.d.ts +40 -0
- package/lib/types/components/pointer.d.ts +47 -0
- package/lib/types/components/render.d.ts +47 -0
- package/lib/types/contract/adapter.d.ts +94 -0
- package/lib/types/contract/types.d.ts +255 -0
- package/lib/types/engine/constraints.d.ts +61 -0
- package/lib/types/engine/controller.d.ts +191 -0
- package/lib/types/engine/geometry.d.ts +162 -0
- package/lib/types/engine/initial.d.ts +36 -0
- package/lib/types/engine/operations.d.ts +26 -0
- package/lib/types/engine/planner.d.ts +187 -0
- package/lib/types/engine/sequence.d.ts +131 -0
- package/lib/types/engine/tree.d.ts +180 -0
- package/lib/types/index.d.ts +38 -0
- package/package.json +44 -0
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Docking-kit styles. One sheet on purpose: a consumer bundle de-duplicates
|
|
3
|
+
* injected stylesheets by `<pluginId>/<basename>`, so a second sheet whose file
|
|
4
|
+
* name matches one in the embedding package would be dropped silently. Keeping
|
|
5
|
+
* a single `dockkit.module.css` makes that collision impossible.
|
|
6
|
+
*
|
|
7
|
+
* Colours come from the embedder's token layer; the kit names no literal. Type
|
|
8
|
+
* follows the embedder's content axis (`--dsh-content-font-size` and its
|
|
9
|
+
* secondary step) so a surface reads at the same size as the page around it.
|
|
10
|
+
* Emphasis — the drop caret, the drop-zone hint — takes the platform's accent
|
|
11
|
+
* (`--dsw-alias-brand-primary-new-colorprimary-new-color`), not
|
|
12
|
+
* `--dsw-alias-brand-primary`, which this platform binds to its near-black
|
|
13
|
+
* (light) or near-white (dark) foreground. A hovered divider takes the
|
|
14
|
+
* caption label ink instead, reading as a handle rather than a highlight.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
.split {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex: 1 1 auto;
|
|
20
|
+
min-width: 0;
|
|
21
|
+
min-height: 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.splitRow {
|
|
25
|
+
flex-direction: row;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.splitColumn {
|
|
29
|
+
flex-direction: column;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.splitCell {
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-basis: 0;
|
|
35
|
+
min-width: 0;
|
|
36
|
+
min-height: 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* The divider owns no layout room (`SPLIT_MINIMUMS.divider` mirrors the 0):
|
|
40
|
+
the halves abut, so a rule a pane draws across its own edge — a header's
|
|
41
|
+
hairline — runs unbroken past the seam. The visible rule is a 0.5px hairline
|
|
42
|
+
centred on the seam, matching the embedder's other borders, and `::after`
|
|
43
|
+
widens the pointer target to 8px by reaching 4px over each neighbour.
|
|
44
|
+
`z-index` keeps that overhang above the later sibling, which would otherwise
|
|
45
|
+
take the hit. */
|
|
46
|
+
.divider {
|
|
47
|
+
position: relative;
|
|
48
|
+
z-index: 1;
|
|
49
|
+
flex: none;
|
|
50
|
+
touch-action: none;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.divider::before,
|
|
54
|
+
.divider::after {
|
|
55
|
+
content: '';
|
|
56
|
+
position: absolute;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.divider::before {
|
|
60
|
+
background: var(--dsw-alias-border-l4);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.splitRow > .divider {
|
|
64
|
+
width: 0;
|
|
65
|
+
cursor: col-resize;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.splitRow > .divider::before {
|
|
69
|
+
top: 0;
|
|
70
|
+
bottom: 0;
|
|
71
|
+
left: -0.25px;
|
|
72
|
+
width: 0.5px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.splitRow > .divider::after {
|
|
76
|
+
top: 0;
|
|
77
|
+
bottom: 0;
|
|
78
|
+
left: -4px;
|
|
79
|
+
right: -4px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.splitColumn > .divider {
|
|
83
|
+
height: 0;
|
|
84
|
+
cursor: row-resize;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.splitColumn > .divider::before {
|
|
88
|
+
left: 0;
|
|
89
|
+
right: 0;
|
|
90
|
+
top: -0.25px;
|
|
91
|
+
height: 0.5px;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.splitColumn > .divider::after {
|
|
95
|
+
left: 0;
|
|
96
|
+
right: 0;
|
|
97
|
+
top: -4px;
|
|
98
|
+
bottom: -4px;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Hover: a 1px grip fades in over the hairline, its caption ink deepest at
|
|
102
|
+
its middle and fading toward both ends. The grip is painted on the `::after`
|
|
103
|
+
hit target, centred on the seam, and crossfaded through opacity, because a
|
|
104
|
+
gradient cannot transition from the hairline's solid colour. */
|
|
105
|
+
.divider::after {
|
|
106
|
+
opacity: 0;
|
|
107
|
+
transition: opacity 120ms ease-out;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.divider:hover::after {
|
|
111
|
+
opacity: 1;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.splitRow > .divider::after {
|
|
115
|
+
background: linear-gradient(to bottom, transparent, var(--dsw-alias-label-caption) 50%, transparent) center / 1px 100% no-repeat;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.splitColumn > .divider::after {
|
|
119
|
+
background: linear-gradient(to right, transparent, var(--dsw-alias-label-caption) 50%, transparent) center / 100% 1px no-repeat;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Both floors: a flex item's minimum is its content's, and a body's longest
|
|
123
|
+
unwrapped line would widen the surface past the embedder's box, carrying the
|
|
124
|
+
strip's controls and the body's scrollbar out of view. */
|
|
125
|
+
.surface {
|
|
126
|
+
display: flex;
|
|
127
|
+
flex: 1 1 auto;
|
|
128
|
+
min-width: 0;
|
|
129
|
+
min-height: 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* A body that overflows scrolls inside its pane; nothing escapes the pane. */
|
|
133
|
+
.pane {
|
|
134
|
+
display: flex;
|
|
135
|
+
flex: 1 1 auto;
|
|
136
|
+
flex-direction: column;
|
|
137
|
+
min-width: 0;
|
|
138
|
+
min-height: 0;
|
|
139
|
+
overflow: hidden;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* A 38px row whose 28px content band sits at its bottom: chips fill the band,
|
|
143
|
+
so they end flush with the strip's bottom edge, and the 28px add control, the
|
|
144
|
+
28px split control, and the embedder's 28px chrome centre on the band's one
|
|
145
|
+
centre line, so chip text and control glyphs never sit at different heights.
|
|
146
|
+
The 10px above is the strip's own top margin, inside its box so the hit
|
|
147
|
+
area stays one element; the 10px at the start is the first chip's inset
|
|
148
|
+
from the edge, and the 6px at the end puts the last control's glyph 12px
|
|
149
|
+
from the edge. The strip draws no border: a body
|
|
150
|
+
that wants a rule under it draws its own. A floating panel's header is
|
|
151
|
+
this same row.
|
|
152
|
+
|
|
153
|
+
The strip never clips: the chip box below is its one shrinking part, and
|
|
154
|
+
every control after it is `flex: none`, so a narrow pane costs chips, never
|
|
155
|
+
controls. */
|
|
156
|
+
.tabStrip {
|
|
157
|
+
display: flex;
|
|
158
|
+
flex: none;
|
|
159
|
+
gap: 4px;
|
|
160
|
+
align-items: center;
|
|
161
|
+
height: 28px;
|
|
162
|
+
padding: 10px 6px 0 10px;
|
|
163
|
+
touch-action: none;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* The chips. Shrinks to nothing before any control after it moves; chips keep
|
|
167
|
+
their 80px floor, so what no longer fits scrolls here on the wheel, with no
|
|
168
|
+
scrollbar drawn. `touch-action: none` keeps a touch press-and-move a tab drag
|
|
169
|
+
rather than a pan the scroller would claim and cancel the pointer for.
|
|
170
|
+
`data-dockkit-strip-scroll` names the hidden sides (`start`, `end`, or
|
|
171
|
+
both); each hidden side fades the chips out over 24px into whatever ground
|
|
172
|
+
the pane draws, so the row reads as continuing under the controls. */
|
|
173
|
+
.stripTabs {
|
|
174
|
+
display: flex;
|
|
175
|
+
flex: 0 1 auto;
|
|
176
|
+
align-items: center;
|
|
177
|
+
min-width: 0;
|
|
178
|
+
overflow-x: auto;
|
|
179
|
+
overflow-y: hidden;
|
|
180
|
+
/* The scroll to a newly active chip glides rather than jumps. */
|
|
181
|
+
scroll-behavior: smooth;
|
|
182
|
+
scrollbar-width: none;
|
|
183
|
+
touch-action: none;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.stripTabs::-webkit-scrollbar {
|
|
187
|
+
display: none;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.stripTabs[data-dockkit-strip-scroll='end'] {
|
|
191
|
+
mask-image: linear-gradient(to right, black calc(100% - 24px), transparent);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.stripTabs[data-dockkit-strip-scroll='start'] {
|
|
195
|
+
mask-image: linear-gradient(to right, transparent, black 24px);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.stripTabs[data-dockkit-strip-scroll='start end'] {
|
|
199
|
+
mask-image: linear-gradient(to right, transparent, black 24px, black calc(100% - 24px), transparent);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* Takes the free space and gives it all back first: a zero basis shrinks
|
|
203
|
+
nothing, so shortage lands on the chip box alone. */
|
|
204
|
+
.stripFill {
|
|
205
|
+
flex: 1 1 0;
|
|
206
|
+
min-width: 0;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/* Embedder controls at the strip's end, spaced from the kit's own split
|
|
210
|
+
control as they are from each other: the strip's 4px gap plus this 4px
|
|
211
|
+
margin equals the 8px between the controls. */
|
|
212
|
+
.stripChrome {
|
|
213
|
+
display: flex;
|
|
214
|
+
flex: none;
|
|
215
|
+
gap: 8px;
|
|
216
|
+
align-items: center;
|
|
217
|
+
height: 28px;
|
|
218
|
+
margin-left: 4px;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* The slot between two chips: a 10px box with a hairline down its centre.
|
|
222
|
+
Targeted by a drag, the same box draws the caret instead, so the chips
|
|
223
|
+
around it never move. */
|
|
224
|
+
.slot {
|
|
225
|
+
display: flex;
|
|
226
|
+
flex: none;
|
|
227
|
+
align-items: center;
|
|
228
|
+
justify-content: center;
|
|
229
|
+
width: 10px;
|
|
230
|
+
height: 28px;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.slot::before {
|
|
234
|
+
content: '';
|
|
235
|
+
width: 0.5px;
|
|
236
|
+
height: 14px;
|
|
237
|
+
background: var(--dsw-alias-border-l4);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* The active chip is a filled capsule and needs no rule against it: the two
|
|
241
|
+
slots beside it go blank, so the row reads as the capsule between bare
|
|
242
|
+
chips. A caret in either slot still draws. */
|
|
243
|
+
.tabActive + .slot:not(.slotCaret)::before,
|
|
244
|
+
.slot:not(.slotCaret):has(+ .tabActive)::before {
|
|
245
|
+
background: transparent;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.slotCaret::before {
|
|
249
|
+
width: 2px;
|
|
250
|
+
height: 20px;
|
|
251
|
+
background: var(--dsw-alias-brand-primary-new-colorprimary-new-color);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/* The chip: a 28px capsule whose title runs to the right inset. The close
|
|
255
|
+
control is not in the flow — it sits over the title's last 14px and shows
|
|
256
|
+
while the chip is active, hovered, or holds focus — so a chip is the same
|
|
257
|
+
width with and without it and nothing shifts on hover. */
|
|
258
|
+
.tab {
|
|
259
|
+
position: relative;
|
|
260
|
+
display: flex;
|
|
261
|
+
flex: 0 1 auto;
|
|
262
|
+
align-items: center;
|
|
263
|
+
min-width: 80px;
|
|
264
|
+
max-width: 170px;
|
|
265
|
+
height: 28px;
|
|
266
|
+
padding: 0 10px;
|
|
267
|
+
color: var(--dsw-alias-label-secondary);
|
|
268
|
+
font-size: var(--dsh-content-font-size-secondary, 13px);
|
|
269
|
+
line-height: 1;
|
|
270
|
+
white-space: nowrap;
|
|
271
|
+
border-radius: 12px;
|
|
272
|
+
cursor: pointer;
|
|
273
|
+
touch-action: none;
|
|
274
|
+
user-select: none;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* A flex row with a 5px gap, so an embedder's leading glyph (`renderTabTitle`)
|
|
278
|
+
centres on the text's line rather than sitting on its baseline and keeps
|
|
279
|
+
one distance from it. The clip is horizontal in intent; the line box must
|
|
280
|
+
hold the descenders it would otherwise cut, hence a line-height above the
|
|
281
|
+
chip's own 1. No ellipsis: a title wider than its box (`TabTitle` sets
|
|
282
|
+
`data-dockkit-tab-clipped`) fades out over its last 16px instead. A mask on
|
|
283
|
+
the title rather than a painted gradient: the chip's fill is bare,
|
|
284
|
+
translucent hover, or the active tag colour, and a mask matches every one
|
|
285
|
+
without naming it. */
|
|
286
|
+
.tabTitle {
|
|
287
|
+
display: flex;
|
|
288
|
+
flex: 1 1 auto;
|
|
289
|
+
gap: 5px;
|
|
290
|
+
align-items: center;
|
|
291
|
+
min-width: 0;
|
|
292
|
+
overflow: hidden;
|
|
293
|
+
line-height: 1.4;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.tabTitle[data-dockkit-tab-clipped] {
|
|
297
|
+
mask-image: linear-gradient(to right, black calc(100% - 16px), transparent);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* While the close shows, the title is gone under the close's circle and fades
|
|
301
|
+
out over the 16px before it: the circle spans the chip's last 24px and the
|
|
302
|
+
title ends 10px inside the chip, so the title's last 14px are under it.
|
|
303
|
+
Later than the clipped mask, so a clipped chip showing its close fades
|
|
304
|
+
under the close. A floating panel's header chip has no close, so it keeps
|
|
305
|
+
the clipped mask alone. */
|
|
306
|
+
.tab:not(.floatTitle):hover .tabTitle,
|
|
307
|
+
.tab:not(.floatTitle):focus-within .tabTitle,
|
|
308
|
+
.tabActive .tabTitle {
|
|
309
|
+
mask-image: linear-gradient(to right, black calc(100% - 30px), transparent calc(100% - 14px));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/* A 20px icon button inset 4px from the chip's end, holding a 14px glyph in
|
|
313
|
+
the tertiary ink, so it sits back from the title beside it. */
|
|
314
|
+
.tabClose {
|
|
315
|
+
position: absolute;
|
|
316
|
+
top: 4px;
|
|
317
|
+
right: 4px;
|
|
318
|
+
display: flex;
|
|
319
|
+
align-items: center;
|
|
320
|
+
justify-content: center;
|
|
321
|
+
width: 20px;
|
|
322
|
+
height: 20px;
|
|
323
|
+
padding: 0;
|
|
324
|
+
color: var(--dsw-alias-label-tertiary);
|
|
325
|
+
line-height: 1;
|
|
326
|
+
background: transparent;
|
|
327
|
+
border: none;
|
|
328
|
+
border-radius: 20px;
|
|
329
|
+
cursor: pointer;
|
|
330
|
+
opacity: 0;
|
|
331
|
+
/* Hidden means untouchable too: without this a touch press on the chip's
|
|
332
|
+
trailing 20px would close the tab instead of activating it. */
|
|
333
|
+
pointer-events: none;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/* Shown while the chip is hovered or holds focus, and always on the active
|
|
337
|
+
chip: the tab in view is the one a reader closes next. */
|
|
338
|
+
.tab:hover .tabClose,
|
|
339
|
+
.tab:focus-within .tabClose,
|
|
340
|
+
.tabActive .tabClose {
|
|
341
|
+
opacity: 1;
|
|
342
|
+
pointer-events: auto;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/* The chip's height and corner, so it reads as one more capsule in the row. */
|
|
346
|
+
.addTab {
|
|
347
|
+
display: flex;
|
|
348
|
+
flex: none;
|
|
349
|
+
align-items: center;
|
|
350
|
+
justify-content: center;
|
|
351
|
+
width: 28px;
|
|
352
|
+
height: 28px;
|
|
353
|
+
padding: 0;
|
|
354
|
+
color: var(--dsw-alias-label-secondary);
|
|
355
|
+
line-height: 1;
|
|
356
|
+
background: transparent;
|
|
357
|
+
border: none;
|
|
358
|
+
border-radius: 12px;
|
|
359
|
+
cursor: pointer;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.addTab:hover {
|
|
363
|
+
color: var(--dsw-alias-label-primary);
|
|
364
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.tabClose:hover {
|
|
368
|
+
color: var(--dsw-alias-label-primary);
|
|
369
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.tab:hover {
|
|
373
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/* The active chip is the filled capsule, in the tag fill; the rest are bare text. */
|
|
377
|
+
.tabActive {
|
|
378
|
+
color: var(--dsw-alias-label-primary);
|
|
379
|
+
background: var(--dsw-alias-markdown-tag);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/* A pane's lone unclosable chip is a label, not a choice: no capsule, no
|
|
383
|
+
hover fill — the primary ink on the pane's own ground. Its title never
|
|
384
|
+
yields to a close control, so it keeps the clipped fade alone. */
|
|
385
|
+
.tab.tabQuiet,
|
|
386
|
+
.tab.tabQuiet:hover {
|
|
387
|
+
color: var(--dsw-alias-label-primary);
|
|
388
|
+
background: transparent;
|
|
389
|
+
cursor: default;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.tab.tabQuiet .tabTitle,
|
|
393
|
+
.tab.tabQuiet:hover .tabTitle,
|
|
394
|
+
.tab.tabQuiet:focus-within .tabTitle {
|
|
395
|
+
mask-image: none;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.tab.tabQuiet .tabTitle[data-dockkit-tab-clipped] {
|
|
399
|
+
mask-image: linear-gradient(to right, black calc(100% - 16px), transparent);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.tabDragging {
|
|
403
|
+
opacity: 0.5;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/* The geometry of a message's action row button (ui-chat MessageIconActions):
|
|
407
|
+
a 28px circle around a 15px glyph. */
|
|
408
|
+
.iconButton {
|
|
409
|
+
display: inline-flex;
|
|
410
|
+
flex: none;
|
|
411
|
+
align-items: center;
|
|
412
|
+
justify-content: center;
|
|
413
|
+
width: 28px;
|
|
414
|
+
height: 28px;
|
|
415
|
+
padding: 6px;
|
|
416
|
+
color: var(--dsw-alias-label-secondary);
|
|
417
|
+
line-height: 1;
|
|
418
|
+
background: transparent;
|
|
419
|
+
border: none;
|
|
420
|
+
border-radius: 28px;
|
|
421
|
+
cursor: pointer;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.iconButton svg {
|
|
425
|
+
width: 15px;
|
|
426
|
+
height: 15px;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.iconButton:hover:not(:disabled) {
|
|
430
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.iconButton:disabled {
|
|
434
|
+
opacity: 0.4;
|
|
435
|
+
cursor: default;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/*
|
|
439
|
+
* Portalled beside the app root, so its stacking level is stated here: above the
|
|
440
|
+
* floating panels (which the embedder draws at 60), because a menu opened from
|
|
441
|
+
* a tab must never sit under a panel. No z-index token layer exists to draw from
|
|
442
|
+
* yet.
|
|
443
|
+
*/
|
|
444
|
+
.menu {
|
|
445
|
+
position: fixed;
|
|
446
|
+
z-index: 70;
|
|
447
|
+
display: flex;
|
|
448
|
+
flex-direction: column;
|
|
449
|
+
min-width: 96px;
|
|
450
|
+
padding: 4px;
|
|
451
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
452
|
+
border: 0.5px solid var(--dsw-alias-border-l2);
|
|
453
|
+
border-radius: 6px;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.menu:not(:has([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio'])) {
|
|
457
|
+
display: none;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.menuItem {
|
|
461
|
+
padding: 5px 8px;
|
|
462
|
+
color: var(--dsw-alias-label-primary);
|
|
463
|
+
font-size: var(--dsh-content-font-size-secondary, 13px);
|
|
464
|
+
text-align: left;
|
|
465
|
+
background: transparent;
|
|
466
|
+
border: none;
|
|
467
|
+
border-radius: 4px;
|
|
468
|
+
cursor: pointer;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.menuItem:hover {
|
|
472
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/* No padding of its own: a tab's body reaches the pane's edges and the strip's
|
|
476
|
+
bottom edge, and keeps its own insets, so a header row it draws sits flush
|
|
477
|
+
under the strip. */
|
|
478
|
+
.paneBody {
|
|
479
|
+
position: relative;
|
|
480
|
+
flex: 1 1 auto;
|
|
481
|
+
min-width: 0;
|
|
482
|
+
min-height: 0;
|
|
483
|
+
overflow: auto; /* This sheet draws elevated surfaces (the strip and the menu), so a scroller
|
|
484
|
+
inside it rebinds the thumb indirection in a complete pair — a base-surface
|
|
485
|
+
thumb on an elevated ground reads as a smudge. */
|
|
486
|
+
--dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
|
|
487
|
+
--dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.empty {
|
|
491
|
+
margin: 0;
|
|
492
|
+
padding: 12px;
|
|
493
|
+
color: var(--dsw-alias-label-tertiary);
|
|
494
|
+
font-size: var(--dsh-content-font-size-secondary, 13px);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/* The drop hint is a scrim and, over it, one box pair per landing region.
|
|
498
|
+
The scrim (`.dockScrim`) covers the whole body with the translucent, blurred
|
|
499
|
+
ground, so the insets between and around the cards dim the body's content
|
|
500
|
+
instead of letting it show through raw. The outer box (`.dockHint`) is the
|
|
501
|
+
region a release fills — the whole body, or 40% (edge bands) or 50%
|
|
502
|
+
(horizontal halves) of it — and draws nothing; it is a padded frame that
|
|
503
|
+
keeps the card inside it 8px clear of the body's edges and, between two
|
|
504
|
+
horizontal halves, 4px clear of the seam, so neighbouring cards sit 8px
|
|
505
|
+
apart like the body's own insets. The card (`.dockHintCard`) is what the eye
|
|
506
|
+
lands on: a dashed frame with the zone's glyph and caption stacked at its
|
|
507
|
+
centre. A card that is not under the pointer stays as a quiet outline so the
|
|
508
|
+
reader sees where the other release would land. */
|
|
509
|
+
.dockScrim {
|
|
510
|
+
position: absolute;
|
|
511
|
+
inset: 0;
|
|
512
|
+
z-index: 10;
|
|
513
|
+
pointer-events: none;
|
|
514
|
+
/* The ground the panes sit on, not a raised layer: in dark mode `bg-layer-2`
|
|
515
|
+
is a lighter bluish step than the column's `bg-base`, which tinted the
|
|
516
|
+
whole scrim away from the content beneath it. */
|
|
517
|
+
background: color-mix(in srgb, var(--dsw-alias-bg-base) 72%, transparent);
|
|
518
|
+
backdrop-filter: blur(6px);
|
|
519
|
+
animation: dockScrimIn 140ms ease-out;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.dockHint {
|
|
523
|
+
position: absolute;
|
|
524
|
+
z-index: 10;
|
|
525
|
+
display: flex;
|
|
526
|
+
box-sizing: border-box;
|
|
527
|
+
padding: 8px;
|
|
528
|
+
pointer-events: none;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.dockHint[data-dockkit-dock-zone='center'] {
|
|
532
|
+
inset: 0;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.dockHint[data-dockkit-dock-zone='left'] {
|
|
536
|
+
top: 0;
|
|
537
|
+
bottom: 0;
|
|
538
|
+
left: 0;
|
|
539
|
+
width: 40%;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.dockHint[data-dockkit-dock-zone='right'] {
|
|
543
|
+
top: 0;
|
|
544
|
+
right: 0;
|
|
545
|
+
bottom: 0;
|
|
546
|
+
width: 40%;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
[data-dockkit-drop-zones='horizontal'] .dockHint[data-dockkit-dock-zone='left'] {
|
|
550
|
+
width: 50%;
|
|
551
|
+
padding-right: 4px;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
[data-dockkit-drop-zones='horizontal'] .dockHint[data-dockkit-dock-zone='right'] {
|
|
555
|
+
width: 50%;
|
|
556
|
+
padding-left: 4px;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.dockHintCard {
|
|
560
|
+
display: flex;
|
|
561
|
+
flex: 1 1 auto;
|
|
562
|
+
flex-direction: column;
|
|
563
|
+
gap: 8px;
|
|
564
|
+
align-items: center;
|
|
565
|
+
justify-content: center;
|
|
566
|
+
min-width: 0;
|
|
567
|
+
min-height: 0;
|
|
568
|
+
padding: 12px;
|
|
569
|
+
overflow: hidden;
|
|
570
|
+
color: var(--dsw-alias-label-tertiary);
|
|
571
|
+
border: 1.5px dashed var(--dsw-alias-border-l2);
|
|
572
|
+
border-radius: 12px;
|
|
573
|
+
transition: color 120ms ease-out, background-color 120ms ease-out, border-color 120ms ease-out;
|
|
574
|
+
animation: dockHintIn 140ms ease-out;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/* The card under the pointer takes the accent: a tinted ground over the scrim,
|
|
578
|
+
an accent dashed frame, and secondary ink for the glyph and caption. */
|
|
579
|
+
.dockHint[data-dockkit-drop-active] .dockHintCard {
|
|
580
|
+
color: var(--dsw-alias-label-secondary);
|
|
581
|
+
background: color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 8%, transparent);
|
|
582
|
+
border-color: var(--dsw-alias-brand-primary-new-colorprimary-new-color);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.dockHintCard svg {
|
|
586
|
+
flex: none;
|
|
587
|
+
width: 20px;
|
|
588
|
+
height: 20px;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/* One line, clipped rather than wrapped: the card in a narrow half is still
|
|
592
|
+
wide enough for its glyph, and the caption fades under the frame. */
|
|
593
|
+
.dockHintLabel {
|
|
594
|
+
max-width: 100%;
|
|
595
|
+
overflow: hidden;
|
|
596
|
+
font-size: var(--dsh-content-font-size-secondary, 13px);
|
|
597
|
+
font-weight: 500;
|
|
598
|
+
line-height: 1.4;
|
|
599
|
+
white-space: nowrap;
|
|
600
|
+
text-overflow: ellipsis;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
@keyframes dockHintIn {
|
|
604
|
+
from {
|
|
605
|
+
opacity: 0;
|
|
606
|
+
transform: scale(0.98);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
to {
|
|
610
|
+
opacity: 1;
|
|
611
|
+
transform: none;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/* The scrim fades in without the cards' scale: a full-body cover that shrinks
|
|
616
|
+
would expose an uncovered rim of raw content on entry. */
|
|
617
|
+
@keyframes dockScrimIn {
|
|
618
|
+
from {
|
|
619
|
+
opacity: 0;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
to {
|
|
623
|
+
opacity: 1;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
.dockHint[data-dockkit-dock-zone='top'] {
|
|
628
|
+
top: 0;
|
|
629
|
+
right: 0;
|
|
630
|
+
left: 0;
|
|
631
|
+
height: 40%;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
.dockHint[data-dockkit-dock-zone='bottom'] {
|
|
635
|
+
right: 0;
|
|
636
|
+
bottom: 0;
|
|
637
|
+
left: 0;
|
|
638
|
+
height: 40%;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.float {
|
|
642
|
+
position: fixed;
|
|
643
|
+
display: flex;
|
|
644
|
+
flex-direction: column;
|
|
645
|
+
/* The body inside is unpadded and reaches the edges, so the frame clips it
|
|
646
|
+
to its own corners. */
|
|
647
|
+
overflow: hidden;
|
|
648
|
+
background: var(--dsw-alias-bg-layer-2);
|
|
649
|
+
border-radius: 20px;
|
|
650
|
+
/* The menu's shadow with the stroke rebound one step lighter than the
|
|
651
|
+
default (l2): the panel reads as the same kind of raised surface, and the
|
|
652
|
+
shadow's hairline outlines it, so the frame draws no border of its own. */
|
|
653
|
+
--dsw-elevation-stroke-color: var(--dsw-alias-border-l2);
|
|
654
|
+
box-shadow: var(--dsw-elevation-prominent);
|
|
655
|
+
pointer-events: auto;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/* The active panel keeps the same frame: it is already on top of the z order
|
|
659
|
+
and casts the same shadow, and a heavier or darker frame read as a defect.
|
|
660
|
+
`data-dockkit-float-active` stays on the element for tests. */
|
|
661
|
+
|
|
662
|
+
/* The panel's header is the pane strip's row (`.tabStrip` supplies the
|
|
663
|
+
metrics) and the whole of it is the move grip. */
|
|
664
|
+
.floatHeader {
|
|
665
|
+
cursor: move;
|
|
666
|
+
user-select: none;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/* The one tab, drawn as its chip would be in a strip but never selected,
|
|
670
|
+
hovered, or closable from here: the frame's controls do that. */
|
|
671
|
+
.floatTitle {
|
|
672
|
+
flex: 0 1 auto;
|
|
673
|
+
cursor: inherit;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
.tab.floatTitle:hover {
|
|
677
|
+
background: transparent;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/* Mirrored so the filled pane sits at the right, where the docked surface is. */
|
|
681
|
+
.dockGlyph {
|
|
682
|
+
transform: scaleX(-1);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/* Unpadded like `.paneBody`: the same body draws the same insets in a float. */
|
|
686
|
+
.floatBody {
|
|
687
|
+
flex: 1 1 auto;
|
|
688
|
+
min-height: 0;
|
|
689
|
+
overflow: auto; /* This sheet draws elevated surfaces (the strip and the menu), so a scroller
|
|
690
|
+
inside it rebinds the thumb indirection in a complete pair — a base-surface
|
|
691
|
+
thumb on an elevated ground reads as a smudge. */
|
|
692
|
+
--dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
|
|
693
|
+
--dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
.floatResize {
|
|
697
|
+
position: absolute;
|
|
698
|
+
right: 0;
|
|
699
|
+
bottom: 0;
|
|
700
|
+
width: 20px;
|
|
701
|
+
height: 20px;
|
|
702
|
+
cursor: nwse-resize;
|
|
703
|
+
touch-action: none;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/* The grip is a quarter arc concentric with the frame's 20px corner (16px
|
|
707
|
+
radius at a 4px inset), so it reads as part of the frame. It shows only
|
|
708
|
+
while the pointer is over the panel — an idle panel keeps a clean corner —
|
|
709
|
+
and `:active` keeps it lit while a drag holds the pointer capture. */
|
|
710
|
+
.floatResize::after {
|
|
711
|
+
content: '';
|
|
712
|
+
position: absolute;
|
|
713
|
+
right: 4px;
|
|
714
|
+
bottom: 4px;
|
|
715
|
+
width: 16px;
|
|
716
|
+
height: 16px;
|
|
717
|
+
border-right: 1.5px solid var(--dsw-alias-label-caption);
|
|
718
|
+
border-bottom: 1.5px solid var(--dsw-alias-label-caption);
|
|
719
|
+
border-bottom-right-radius: 16px;
|
|
720
|
+
opacity: 0;
|
|
721
|
+
transition: opacity 120ms ease-out;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
.float:hover .floatResize::after,
|
|
725
|
+
.floatResize:active::after {
|
|
726
|
+
opacity: 1;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
.floatResize:hover::after {
|
|
730
|
+
border-color: var(--dsw-alias-label-tertiary);
|
|
731
|
+
}
|