@sunshaobei/dsh-session-manager 0.0.0-stage → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +115 -2
- package/README.zh.md +115 -0
- package/client.css +1613 -0
- package/client.js +2189 -0
- package/cordis.patch.yml +16 -0
- package/index.js +78 -0
- package/package.json +61 -4
- package/packages/session-trash-host/lib/index.js +1904 -0
- package/packages/session-trash-host/lib/types/index.d.ts +32 -0
- package/packages/session-trash-host/package.json +26 -0
package/client.css
ADDED
|
@@ -0,0 +1,1613 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* dsh-session-recycle-bin — plugin stylesheet (client.css)
|
|
3
|
+
*
|
|
4
|
+
* Served by the host at GET /api/session-trash/client.css and injected by
|
|
5
|
+
* client.js as a <link rel="stylesheet">. All colors are CSS custom
|
|
6
|
+
* properties (design tokens) declared on :root; the dark palette applies
|
|
7
|
+
* under `body[data-ds-dark-theme]` (the resolved DSH appearance: light /
|
|
8
|
+
* dark / system, set by the host right after <body> opens) OR under
|
|
9
|
+
* `@media (prefers-color-scheme: dark)` (OS-level fallback), while an
|
|
10
|
+
* explicit light appearance restores the light palette via
|
|
11
|
+
* `body:not([data-ds-dark-theme])` — so inside DSH the app preference is
|
|
12
|
+
* authoritative, and the OS media query remains the fallback without it.
|
|
13
|
+
*
|
|
14
|
+
* Naming: components use the `dsh-trash-` prefix; markdown blocks are styled
|
|
15
|
+
* with element selectors scoped under `.dsh-trash-md`.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/* ------------------------------------------------------------------ */
|
|
19
|
+
/* Design tokens (light default; dark = app theme || OS fallback) */
|
|
20
|
+
/* ------------------------------------------------------------------ */
|
|
21
|
+
|
|
22
|
+
:root {
|
|
23
|
+
/* FONT STACKS ARE RESOLVED AT THE POINT OF USE, NOT HERE.
|
|
24
|
+
*
|
|
25
|
+
* These two tokens are the last-resort fallbacks for hosts that do not
|
|
26
|
+
* publish the shell's own font tokens (older DSH builds, the standalone
|
|
27
|
+
* test harness). They are deliberately CONCRETE stacks: a custom property
|
|
28
|
+
* whose value contains var() is substituted at computed-value time on the
|
|
29
|
+
* element that DECLARES it, and that computed value is what inherits. So
|
|
30
|
+
* :root { --dstrb-font: var(--dsw-font-family); }
|
|
31
|
+
* would freeze the :root-scoped stock stack and would NOT follow the
|
|
32
|
+
* body-level `--dsw-font-family` override that dsh-ui-font installs for the
|
|
33
|
+
* whole app — the plugin page would keep rendering in the default face.
|
|
34
|
+
* The effective font is therefore routed at the use sites (see the
|
|
35
|
+
* "Font routing" block below), where the inherited DSH tokens are visible.
|
|
36
|
+
*/
|
|
37
|
+
--dstrb-font-fallback: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif;
|
|
38
|
+
--dstrb-mono-fallback: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
|
|
39
|
+
|
|
40
|
+
/* Text */
|
|
41
|
+
--dstrb-text-strong: #1a202c;
|
|
42
|
+
--dstrb-text: #2d3748;
|
|
43
|
+
--dstrb-text-secondary: #4a5568;
|
|
44
|
+
/* #718096 measured 4.02:1 on the card surface and 3.83:1 on the subtle
|
|
45
|
+
surface (badges) — under the 4.5:1 text bar for the small labels that use
|
|
46
|
+
it. #5f6b7a clears it everywhere it lands: 5.43 on surface, 5.18 on
|
|
47
|
+
surface-subtle, 4.82 on surface-hover (the hovered badge), while staying
|
|
48
|
+
visibly weaker than --dstrb-text (11.99) and --dstrb-text-secondary (7.53). */
|
|
49
|
+
--dstrb-text-muted: #5f6b7a;
|
|
50
|
+
--dstrb-text-faint: #a0aec0;
|
|
51
|
+
|
|
52
|
+
/* Accents */
|
|
53
|
+
--dstrb-accent: #3182ce;
|
|
54
|
+
--dstrb-accent-veil: rgba(49, 130, 206, 0.15);
|
|
55
|
+
--dstrb-danger: #e53e3e;
|
|
56
|
+
--dstrb-danger-strong: #c53030;
|
|
57
|
+
--dstrb-danger-border: #feb2b2;
|
|
58
|
+
--dstrb-danger-veil: rgba(229, 62, 62, 0.08);
|
|
59
|
+
--dstrb-on-danger: #ffffff;
|
|
60
|
+
|
|
61
|
+
/* Surfaces */
|
|
62
|
+
--dstrb-surface: #ffffff; /* cards, modals, menus */
|
|
63
|
+
--dstrb-surface-subtle: #f7fafc; /* inputs, badges, checked rows */
|
|
64
|
+
--dstrb-surface-hover: #edf2f7; /* cancel button, user bubble base */
|
|
65
|
+
|
|
66
|
+
/* Borders */
|
|
67
|
+
--dstrb-border: #e2e8f0;
|
|
68
|
+
--dstrb-border-light: #edf2f7;
|
|
69
|
+
--dstrb-border-hairline: #f0f4f8;
|
|
70
|
+
|
|
71
|
+
/* Structural lines that must stay IDENTIFIABLE, not decorative: WCAG 1.4.11
|
|
72
|
+
wants >= 3:1 from the boundary that carries the hierarchy — the default
|
|
73
|
+
borders sit at ~1.1:1, so the nesting cue uses this token instead.
|
|
74
|
+
#64748b on the light card surface (#ffffff) = 4.76:1. */
|
|
75
|
+
--dstrb-structure: #64748b;
|
|
76
|
+
|
|
77
|
+
/* Veils & overlays */
|
|
78
|
+
--dstrb-veil: rgba(0, 0, 0, 0.06);
|
|
79
|
+
--dstrb-overlay: rgba(0, 0, 0, 0.45);
|
|
80
|
+
|
|
81
|
+
/* Shadows */
|
|
82
|
+
--dstrb-shadow-card: 0 2px 8px rgba(0, 0, 0, 0.03);
|
|
83
|
+
--dstrb-shadow-menu: 0 4px 16px rgba(0, 0, 0, 0.12);
|
|
84
|
+
--dstrb-shadow-modal: 0 20px 40px rgba(0, 0, 0, 0.2);
|
|
85
|
+
--dstrb-shadow-modal-sm: 0 10px 25px rgba(0, 0, 0, 0.2);
|
|
86
|
+
--dstrb-shadow-tooltip: 0 8px 24px rgba(0, 0, 0, 0.2);
|
|
87
|
+
--dstrb-shadow-toast: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
88
|
+
|
|
89
|
+
/* Inverted tooltip */
|
|
90
|
+
--dstrb-tooltip-bg: #1a202c;
|
|
91
|
+
--dstrb-tooltip-text: #ffffff;
|
|
92
|
+
--dstrb-tooltip-muted: #a0aec0;
|
|
93
|
+
|
|
94
|
+
/* Toast (also inverted) */
|
|
95
|
+
--dstrb-toast-bg: #2d3748;
|
|
96
|
+
--dstrb-toast-text: #ffffff;
|
|
97
|
+
--dstrb-toast-btn-bg: #4a5568;
|
|
98
|
+
--dstrb-toast-btn-text: #63b3ed;
|
|
99
|
+
|
|
100
|
+
/* Markdown */
|
|
101
|
+
--dstrb-code-inline-bg: rgba(0, 0, 0, 0.07);
|
|
102
|
+
--dstrb-code-inline-text: #c53030;
|
|
103
|
+
--dstrb-quote-bg: rgba(0, 0, 0, 0.02);
|
|
104
|
+
--dstrb-quote-border: #cbd5e0;
|
|
105
|
+
--dstrb-bubble-user: #edf2f7;
|
|
106
|
+
/* Code blocks stay dark in both modes (editor-style) */
|
|
107
|
+
--dstrb-codeblock-bg: #1e293b;
|
|
108
|
+
--dstrb-codeblock-text: #e2e8f0;
|
|
109
|
+
--dstrb-codeblock-lang: #94a3b8;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@media (prefers-color-scheme: dark) {
|
|
113
|
+
:root {
|
|
114
|
+
--dstrb-text-strong: #edf2f7;
|
|
115
|
+
--dstrb-text: #e2e8f0;
|
|
116
|
+
--dstrb-text-secondary: #cbd5e0;
|
|
117
|
+
--dstrb-text-muted: #a0aec0;
|
|
118
|
+
--dstrb-text-faint: #718096;
|
|
119
|
+
|
|
120
|
+
--dstrb-accent: #63b3ed;
|
|
121
|
+
--dstrb-accent-veil: rgba(99, 179, 237, 0.2);
|
|
122
|
+
--dstrb-danger: #fc8181;
|
|
123
|
+
--dstrb-danger-strong: #feb2b2;
|
|
124
|
+
--dstrb-danger-border: #9b2c2c;
|
|
125
|
+
--dstrb-danger-veil: rgba(252, 129, 129, 0.12);
|
|
126
|
+
--dstrb-on-danger: #1a202c;
|
|
127
|
+
|
|
128
|
+
--dstrb-surface: #1f2530;
|
|
129
|
+
--dstrb-surface-subtle: #262e3d;
|
|
130
|
+
--dstrb-surface-hover: #2c3444;
|
|
131
|
+
|
|
132
|
+
--dstrb-border: #3a4356;
|
|
133
|
+
--dstrb-border-light: #333c4e;
|
|
134
|
+
--dstrb-border-hairline: #2c3444;
|
|
135
|
+
|
|
136
|
+
/* Same >=3:1 rule on the dark card surface (#1f2530): #8a97a8 = 5.18:1. */
|
|
137
|
+
--dstrb-structure: #8a97a8;
|
|
138
|
+
|
|
139
|
+
--dstrb-veil: rgba(255, 255, 255, 0.08);
|
|
140
|
+
--dstrb-overlay: rgba(0, 0, 0, 0.65);
|
|
141
|
+
|
|
142
|
+
--dstrb-shadow-card: 0 2px 8px rgba(0, 0, 0, 0.35);
|
|
143
|
+
--dstrb-shadow-menu: 0 4px 16px rgba(0, 0, 0, 0.5);
|
|
144
|
+
--dstrb-shadow-modal: 0 20px 40px rgba(0, 0, 0, 0.55);
|
|
145
|
+
--dstrb-shadow-modal-sm: 0 10px 25px rgba(0, 0, 0, 0.5);
|
|
146
|
+
--dstrb-shadow-tooltip: 0 8px 24px rgba(0, 0, 0, 0.55);
|
|
147
|
+
--dstrb-shadow-toast: 0 4px 12px rgba(0, 0, 0, 0.4);
|
|
148
|
+
|
|
149
|
+
--dstrb-tooltip-bg: #e2e8f0;
|
|
150
|
+
--dstrb-tooltip-text: #1a202c;
|
|
151
|
+
--dstrb-tooltip-muted: #4a5568;
|
|
152
|
+
|
|
153
|
+
--dstrb-toast-bg: #e2e8f0;
|
|
154
|
+
--dstrb-toast-text: #1a202c;
|
|
155
|
+
--dstrb-toast-btn-bg: #cbd5e0;
|
|
156
|
+
--dstrb-toast-btn-text: #1a202c;
|
|
157
|
+
|
|
158
|
+
--dstrb-code-inline-bg: rgba(255, 255, 255, 0.12);
|
|
159
|
+
--dstrb-code-inline-text: #fc8181;
|
|
160
|
+
--dstrb-quote-bg: rgba(255, 255, 255, 0.04);
|
|
161
|
+
--dstrb-quote-border: #4a5568;
|
|
162
|
+
--dstrb-bubble-user: #2c3444;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* ------------------------------------------------------------------ */
|
|
167
|
+
/* DSH app theme — body[data-ds-dark-theme] */
|
|
168
|
+
/* ------------------------------------------------------------------ */
|
|
169
|
+
/* The host app resolves its appearance preference (light / dark / */
|
|
170
|
+
/* system) onto body[data-ds-dark-theme] as an inline script right */
|
|
171
|
+
/* after <body> opens. This block ORs with the OS media query above: */
|
|
172
|
+
/* either match resolves the dark palette. */
|
|
173
|
+
/* ------------------------------------------------------------------ */
|
|
174
|
+
|
|
175
|
+
body[data-ds-dark-theme] {
|
|
176
|
+
--dstrb-text-strong: #edf2f7;
|
|
177
|
+
--dstrb-text: #e2e8f0;
|
|
178
|
+
--dstrb-text-secondary: #cbd5e0;
|
|
179
|
+
--dstrb-text-muted: #a0aec0;
|
|
180
|
+
--dstrb-text-faint: #718096;
|
|
181
|
+
|
|
182
|
+
--dstrb-accent: #63b3ed;
|
|
183
|
+
--dstrb-accent-veil: rgba(99, 179, 237, 0.2);
|
|
184
|
+
--dstrb-danger: #fc8181;
|
|
185
|
+
--dstrb-danger-strong: #feb2b2;
|
|
186
|
+
--dstrb-danger-border: #9b2c2c;
|
|
187
|
+
--dstrb-danger-veil: rgba(252, 129, 129, 0.12);
|
|
188
|
+
--dstrb-on-danger: #1a202c;
|
|
189
|
+
|
|
190
|
+
--dstrb-surface: #1f2530;
|
|
191
|
+
--dstrb-surface-subtle: #262e3d;
|
|
192
|
+
--dstrb-surface-hover: #2c3444;
|
|
193
|
+
|
|
194
|
+
--dstrb-border: #3a4356;
|
|
195
|
+
--dstrb-border-light: #333c4e;
|
|
196
|
+
--dstrb-border-hairline: #2c3444;
|
|
197
|
+
|
|
198
|
+
/* Same >=3:1 rule on the dark card surface (#1f2530): #8a97a8 = 5.18:1. */
|
|
199
|
+
--dstrb-structure: #8a97a8;
|
|
200
|
+
|
|
201
|
+
--dstrb-veil: rgba(255, 255, 255, 0.08);
|
|
202
|
+
--dstrb-overlay: rgba(0, 0, 0, 0.65);
|
|
203
|
+
|
|
204
|
+
--dstrb-shadow-card: 0 2px 8px rgba(0, 0, 0, 0.35);
|
|
205
|
+
--dstrb-shadow-menu: 0 4px 16px rgba(0, 0, 0, 0.5);
|
|
206
|
+
--dstrb-shadow-modal: 0 20px 40px rgba(0, 0, 0, 0.55);
|
|
207
|
+
--dstrb-shadow-modal-sm: 0 10px 25px rgba(0, 0, 0, 0.5);
|
|
208
|
+
--dstrb-shadow-tooltip: 0 8px 24px rgba(0, 0, 0, 0.55);
|
|
209
|
+
--dstrb-shadow-toast: 0 4px 12px rgba(0, 0, 0, 0.4);
|
|
210
|
+
|
|
211
|
+
--dstrb-tooltip-bg: #e2e8f0;
|
|
212
|
+
--dstrb-tooltip-text: #1a202c;
|
|
213
|
+
--dstrb-tooltip-muted: #4a5568;
|
|
214
|
+
|
|
215
|
+
--dstrb-toast-bg: #e2e8f0;
|
|
216
|
+
--dstrb-toast-text: #1a202c;
|
|
217
|
+
--dstrb-toast-btn-bg: #cbd5e0;
|
|
218
|
+
--dstrb-toast-btn-text: #1a202c;
|
|
219
|
+
|
|
220
|
+
--dstrb-code-inline-bg: rgba(255, 255, 255, 0.12);
|
|
221
|
+
--dstrb-code-inline-text: #fc8181;
|
|
222
|
+
--dstrb-quote-bg: rgba(255, 255, 255, 0.04);
|
|
223
|
+
--dstrb-quote-border: #4a5568;
|
|
224
|
+
--dstrb-bubble-user: #2c3444;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/* An explicit light preference restores the light palette even under */
|
|
228
|
+
/* a dark OS, so the app preference stays authoritative inside DSH */
|
|
229
|
+
/* (fixes near-white titles on a light page). */
|
|
230
|
+
|
|
231
|
+
body:not([data-ds-dark-theme]) {
|
|
232
|
+
/* Fonts are NOT palette state: they are routed once, at the point of use,
|
|
233
|
+
in the "Font routing" block above — do not re-declare them here, or the
|
|
234
|
+
explicit-light branch would pin the plugin back to the stock stack. */
|
|
235
|
+
|
|
236
|
+
/* Text */
|
|
237
|
+
--dstrb-text-strong: #1a202c;
|
|
238
|
+
--dstrb-text: #2d3748;
|
|
239
|
+
--dstrb-text-secondary: #4a5568;
|
|
240
|
+
/* #718096 measured 4.02:1 on the card surface and 3.83:1 on the subtle
|
|
241
|
+
surface (badges) — under the 4.5:1 text bar for the small labels that use
|
|
242
|
+
it. #5f6b7a clears it everywhere it lands: 5.43 on surface, 5.18 on
|
|
243
|
+
surface-subtle, 4.82 on surface-hover (the hovered badge), while staying
|
|
244
|
+
visibly weaker than --dstrb-text (11.99) and --dstrb-text-secondary (7.53). */
|
|
245
|
+
--dstrb-text-muted: #5f6b7a;
|
|
246
|
+
--dstrb-text-faint: #a0aec0;
|
|
247
|
+
|
|
248
|
+
/* Accents */
|
|
249
|
+
--dstrb-accent: #3182ce;
|
|
250
|
+
--dstrb-accent-veil: rgba(49, 130, 206, 0.15);
|
|
251
|
+
--dstrb-danger: #e53e3e;
|
|
252
|
+
--dstrb-danger-strong: #c53030;
|
|
253
|
+
--dstrb-danger-border: #feb2b2;
|
|
254
|
+
--dstrb-danger-veil: rgba(229, 62, 62, 0.08);
|
|
255
|
+
--dstrb-on-danger: #ffffff;
|
|
256
|
+
|
|
257
|
+
/* Surfaces */
|
|
258
|
+
--dstrb-surface: #ffffff; /* cards, modals, menus */
|
|
259
|
+
--dstrb-surface-subtle: #f7fafc; /* inputs, badges, checked rows */
|
|
260
|
+
--dstrb-surface-hover: #edf2f7; /* cancel button, user bubble base */
|
|
261
|
+
|
|
262
|
+
/* Borders */
|
|
263
|
+
--dstrb-border: #e2e8f0;
|
|
264
|
+
--dstrb-border-light: #edf2f7;
|
|
265
|
+
--dstrb-border-hairline: #f0f4f8;
|
|
266
|
+
|
|
267
|
+
/* Structural lines that must stay IDENTIFIABLE, not decorative: WCAG 1.4.11
|
|
268
|
+
wants >= 3:1 from the boundary that carries the hierarchy — the default
|
|
269
|
+
borders sit at ~1.1:1, so the nesting cue uses this token instead.
|
|
270
|
+
#64748b on the light card surface (#ffffff) = 4.76:1. */
|
|
271
|
+
--dstrb-structure: #64748b;
|
|
272
|
+
|
|
273
|
+
/* Veils & overlays */
|
|
274
|
+
--dstrb-veil: rgba(0, 0, 0, 0.06);
|
|
275
|
+
--dstrb-overlay: rgba(0, 0, 0, 0.45);
|
|
276
|
+
|
|
277
|
+
/* Shadows */
|
|
278
|
+
--dstrb-shadow-card: 0 2px 8px rgba(0, 0, 0, 0.03);
|
|
279
|
+
--dstrb-shadow-menu: 0 4px 16px rgba(0, 0, 0, 0.12);
|
|
280
|
+
--dstrb-shadow-modal: 0 20px 40px rgba(0, 0, 0, 0.2);
|
|
281
|
+
--dstrb-shadow-modal-sm: 0 10px 25px rgba(0, 0, 0, 0.2);
|
|
282
|
+
--dstrb-shadow-tooltip: 0 8px 24px rgba(0, 0, 0, 0.2);
|
|
283
|
+
--dstrb-shadow-toast: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
284
|
+
|
|
285
|
+
/* Inverted tooltip */
|
|
286
|
+
--dstrb-tooltip-bg: #1a202c;
|
|
287
|
+
--dstrb-tooltip-text: #ffffff;
|
|
288
|
+
--dstrb-tooltip-muted: #a0aec0;
|
|
289
|
+
|
|
290
|
+
/* Toast (also inverted) */
|
|
291
|
+
--dstrb-toast-bg: #2d3748;
|
|
292
|
+
--dstrb-toast-text: #ffffff;
|
|
293
|
+
--dstrb-toast-btn-bg: #4a5568;
|
|
294
|
+
--dstrb-toast-btn-text: #63b3ed;
|
|
295
|
+
|
|
296
|
+
/* Markdown */
|
|
297
|
+
--dstrb-code-inline-bg: rgba(0, 0, 0, 0.07);
|
|
298
|
+
--dstrb-code-inline-text: #c53030;
|
|
299
|
+
--dstrb-quote-bg: rgba(0, 0, 0, 0.02);
|
|
300
|
+
--dstrb-quote-border: #cbd5e0;
|
|
301
|
+
--dstrb-bubble-user: #edf2f7;
|
|
302
|
+
/* Code blocks stay dark in both modes (editor-style) */
|
|
303
|
+
--dstrb-codeblock-bg: #1e293b;
|
|
304
|
+
--dstrb-codeblock-text: #e2e8f0;
|
|
305
|
+
--dstrb-codeblock-lang: #94a3b8;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/* ------------------------------------------------------------------ */
|
|
309
|
+
/* Font routing — follow the DSH shell, stay overridable */
|
|
310
|
+
/* ------------------------------------------------------------------ */
|
|
311
|
+
/*
|
|
312
|
+
* The shell paints `<body>` with `font-family: var(--dsw-font-family, …)`
|
|
313
|
+
* and dsh-ui-font swaps that token at the body level to apply the user's
|
|
314
|
+
* Settings → 字体 choice (and `--ds-font-family-code` for code faces)
|
|
315
|
+
* across the whole app. Referencing those host tokens HERE — on the plugin
|
|
316
|
+
* roots, where the inherited values are already in scope — is what makes
|
|
317
|
+
* the settings page and the portaled toast track the app font, live.
|
|
318
|
+
*
|
|
319
|
+
* --dstrb-font / --dstrb-mono remain available as optional override hooks:
|
|
320
|
+
* a theme may still declare either one higher up the tree and it wins,
|
|
321
|
+
* because the fallback chain is evaluated at the use site.
|
|
322
|
+
*/
|
|
323
|
+
.dsh-trash-container,
|
|
324
|
+
.dsh-trash-toast {
|
|
325
|
+
font-family: var(--dstrb-font, var(--dsw-font-family, var(--dstrb-font-fallback)));
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* ------------------------------------------------------------------ */
|
|
329
|
+
/* Page container — width follows the window ratio (no fixed px cap) */
|
|
330
|
+
/* ------------------------------------------------------------------ */
|
|
331
|
+
|
|
332
|
+
.dsh-trash-container {
|
|
333
|
+
width: 90%;
|
|
334
|
+
padding: 24px;
|
|
335
|
+
margin: 0 auto;
|
|
336
|
+
color: var(--dstrb-text);
|
|
337
|
+
color-scheme: light dark; /* native select/checkbox follow the resolved mode */
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/* App-theme-scoped overrides: native controls (select, checkbox,
|
|
341
|
+
scrollbars) follow the resolved plugin mode, matching the painted
|
|
342
|
+
surfaces even when the OS media query disagrees with the DSH
|
|
343
|
+
appearance preference. */
|
|
344
|
+
body[data-ds-dark-theme] .dsh-trash-container { color-scheme: dark; }
|
|
345
|
+
body:not([data-ds-dark-theme]) .dsh-trash-container { color-scheme: light; }
|
|
346
|
+
|
|
347
|
+
.dsh-trash-container,
|
|
348
|
+
.dsh-trash-container * {
|
|
349
|
+
box-sizing: border-box;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/* ------------------------------------------------------------------ */
|
|
353
|
+
/* Header */
|
|
354
|
+
/* ------------------------------------------------------------------ */
|
|
355
|
+
|
|
356
|
+
.dsh-trash-header {
|
|
357
|
+
display: flex;
|
|
358
|
+
justify-content: space-between;
|
|
359
|
+
align-items: center;
|
|
360
|
+
margin-bottom: 20px;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.dsh-trash-header-left {
|
|
364
|
+
display: flex;
|
|
365
|
+
flex-direction: column;
|
|
366
|
+
gap: 4px;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.dsh-trash-title {
|
|
370
|
+
margin: 0;
|
|
371
|
+
font-size: 20px;
|
|
372
|
+
font-weight: 600;
|
|
373
|
+
color: var(--dstrb-text-strong);
|
|
374
|
+
display: flex;
|
|
375
|
+
align-items: center;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.dsh-trash-subtitle {
|
|
379
|
+
margin: 0;
|
|
380
|
+
font-size: 13px;
|
|
381
|
+
color: var(--dstrb-text-muted);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/* Icons (size/margin/color moved out of JS inline styles) */
|
|
385
|
+
.dsh-trash-icon-title { width: 20px; height: 20px; margin-right: 8px; color: var(--dstrb-text-secondary); flex: none; }
|
|
386
|
+
.dsh-trash-icon-btn { width: 14px; height: 14px; margin-right: 6px; }
|
|
387
|
+
.dsh-trash-icon-menu { width: 13px; height: 13px; margin-right: 6px; }
|
|
388
|
+
.dsh-trash-icon-folder { width: 18px; height: 18px; color: var(--dstrb-text-muted); flex: none; }
|
|
389
|
+
.dsh-trash-icon-badge { width: 13px; height: 13px; margin-right: 4px; color: var(--dstrb-text-muted); }
|
|
390
|
+
.dsh-trash-icon-sm { width: 12px; height: 12px; margin-right: 3px; }
|
|
391
|
+
|
|
392
|
+
/* ------------------------------------------------------------------ */
|
|
393
|
+
/* Search bar */
|
|
394
|
+
/* ------------------------------------------------------------------ */
|
|
395
|
+
|
|
396
|
+
.dsh-trash-search {
|
|
397
|
+
position: relative;
|
|
398
|
+
margin-bottom: 12px;
|
|
399
|
+
width: 100%;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.dsh-trash-search-icon {
|
|
403
|
+
position: absolute;
|
|
404
|
+
top: 50%;
|
|
405
|
+
transform: translateY(-50%);
|
|
406
|
+
left: 14px;
|
|
407
|
+
color: var(--dstrb-text-faint);
|
|
408
|
+
pointer-events: none;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.dsh-trash-search-input {
|
|
412
|
+
width: 100%;
|
|
413
|
+
padding: 10px 14px 10px 40px;
|
|
414
|
+
border-radius: 24px;
|
|
415
|
+
border: 1px solid var(--dstrb-border);
|
|
416
|
+
background-color: var(--dstrb-surface-subtle);
|
|
417
|
+
font-size: 14px;
|
|
418
|
+
font-family: inherit;
|
|
419
|
+
color: var(--dstrb-text);
|
|
420
|
+
outline: none;
|
|
421
|
+
transition: all 0.15s ease;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.dsh-trash-search-input::placeholder {
|
|
425
|
+
color: var(--dstrb-text-muted);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.dsh-trash-search-input:focus {
|
|
429
|
+
border-color: var(--dstrb-accent);
|
|
430
|
+
box-shadow: 0 0 0 3px var(--dstrb-accent-veil);
|
|
431
|
+
background-color: var(--dstrb-surface);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/* ------------------------------------------------------------------ */
|
|
435
|
+
/* Filter toolbar */
|
|
436
|
+
/* ------------------------------------------------------------------ */
|
|
437
|
+
|
|
438
|
+
/* Flex-wrap rather than `1fr 1fr auto`: a `1fr` grid track refuses to shrink
|
|
439
|
+
below its longest option, so at a 320px settings column the toolbar pushed
|
|
440
|
+
itself 6px past the panel. The controls now wrap instead of spilling. */
|
|
441
|
+
.dsh-trash-toolbar {
|
|
442
|
+
display: flex;
|
|
443
|
+
flex-wrap: wrap;
|
|
444
|
+
gap: 12px;
|
|
445
|
+
align-items: center;
|
|
446
|
+
margin-bottom: 20px;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.dsh-trash-toolbar > .dsh-trash-select {
|
|
450
|
+
flex: 1 1 120px;
|
|
451
|
+
min-width: 0;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.dsh-trash-toolbar > .dsh-trash-btn-danger-outline {
|
|
455
|
+
flex: 0 0 auto;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.dsh-trash-select {
|
|
459
|
+
width: 100%;
|
|
460
|
+
height: 36px;
|
|
461
|
+
padding: 0 32px 0 14px;
|
|
462
|
+
border-radius: 18px;
|
|
463
|
+
border: 1px solid var(--dstrb-border);
|
|
464
|
+
background-color: var(--dstrb-surface);
|
|
465
|
+
font-size: 13px;
|
|
466
|
+
font-family: inherit;
|
|
467
|
+
color: var(--dstrb-text-secondary);
|
|
468
|
+
cursor: pointer;
|
|
469
|
+
outline: none;
|
|
470
|
+
appearance: none;
|
|
471
|
+
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a0aec0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
|
|
472
|
+
background-repeat: no-repeat;
|
|
473
|
+
background-position: right 10px center;
|
|
474
|
+
background-size: 14px;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.dsh-trash-btn-danger-outline {
|
|
478
|
+
height: 36px;
|
|
479
|
+
background-color: var(--dstrb-surface);
|
|
480
|
+
color: var(--dstrb-danger);
|
|
481
|
+
border: 1px solid var(--dstrb-danger-border);
|
|
482
|
+
padding: 0 16px;
|
|
483
|
+
border-radius: 18px;
|
|
484
|
+
font-size: 13px;
|
|
485
|
+
font-weight: 500;
|
|
486
|
+
font-family: inherit;
|
|
487
|
+
display: inline-flex;
|
|
488
|
+
align-items: center;
|
|
489
|
+
justify-content: center;
|
|
490
|
+
cursor: pointer;
|
|
491
|
+
transition: all 0.15s ease;
|
|
492
|
+
white-space: nowrap;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.dsh-trash-btn-danger-outline:hover {
|
|
496
|
+
background-color: var(--dstrb-danger-veil);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/* ------------------------------------------------------------------ */
|
|
500
|
+
/* States (loading / error / empty) */
|
|
501
|
+
/* ------------------------------------------------------------------ */
|
|
502
|
+
|
|
503
|
+
.dsh-trash-state {
|
|
504
|
+
padding: 40px 0;
|
|
505
|
+
text-align: center;
|
|
506
|
+
color: var(--dstrb-text-muted);
|
|
507
|
+
font-size: 14px;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.dsh-trash-state--error {
|
|
511
|
+
color: var(--dstrb-danger);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/* ------------------------------------------------------------------ */
|
|
515
|
+
/* Workspace groups */
|
|
516
|
+
/* ------------------------------------------------------------------ */
|
|
517
|
+
|
|
518
|
+
.dsh-trash-groups {
|
|
519
|
+
display: flex;
|
|
520
|
+
flex-direction: column;
|
|
521
|
+
gap: 24px;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
.dsh-trash-group {
|
|
525
|
+
display: flex;
|
|
526
|
+
flex-direction: column;
|
|
527
|
+
gap: 10px;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.dsh-trash-group-header {
|
|
531
|
+
display: flex;
|
|
532
|
+
justify-content: space-between;
|
|
533
|
+
align-items: center;
|
|
534
|
+
padding: 4px 2px;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.dsh-trash-group-title-group {
|
|
538
|
+
display: flex;
|
|
539
|
+
align-items: center;
|
|
540
|
+
gap: 8px;
|
|
541
|
+
min-width: 0;
|
|
542
|
+
flex: 1;
|
|
543
|
+
margin-right: 12px;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.dsh-trash-group-title {
|
|
547
|
+
font-size: 15px;
|
|
548
|
+
font-weight: 600;
|
|
549
|
+
color: var(--dstrb-text-strong);
|
|
550
|
+
white-space: nowrap;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.dsh-trash-group-path {
|
|
554
|
+
font-size: 11px;
|
|
555
|
+
color: var(--dstrb-text-muted);
|
|
556
|
+
font-family: var(--dstrb-mono, var(--ds-font-family-code, var(--dstrb-mono-fallback)));
|
|
557
|
+
margin-left: 4px;
|
|
558
|
+
overflow: hidden;
|
|
559
|
+
text-overflow: ellipsis;
|
|
560
|
+
white-space: nowrap;
|
|
561
|
+
max-width: 260px;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.dsh-trash-group-right {
|
|
565
|
+
display: flex;
|
|
566
|
+
align-items: center;
|
|
567
|
+
gap: 12px;
|
|
568
|
+
flex-shrink: 0;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.dsh-trash-group-count {
|
|
572
|
+
font-size: 12px;
|
|
573
|
+
color: var(--dstrb-text-muted);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
.dsh-trash-more-wrap {
|
|
577
|
+
position: relative;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.dsh-trash-more-btn {
|
|
581
|
+
background: none;
|
|
582
|
+
border: none;
|
|
583
|
+
color: var(--dstrb-text-muted);
|
|
584
|
+
font-size: 14px;
|
|
585
|
+
cursor: pointer;
|
|
586
|
+
padding: 2px 6px;
|
|
587
|
+
border-radius: 4px;
|
|
588
|
+
transition: background 0.12s ease, color 0.12s ease;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.dsh-trash-more-btn:hover {
|
|
592
|
+
background: var(--dstrb-veil);
|
|
593
|
+
color: var(--dstrb-text-muted);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
.dsh-trash-menu {
|
|
597
|
+
position: absolute;
|
|
598
|
+
right: 0;
|
|
599
|
+
top: 24px;
|
|
600
|
+
background-color: var(--dstrb-surface);
|
|
601
|
+
box-shadow: var(--dstrb-shadow-menu);
|
|
602
|
+
border-radius: 8px;
|
|
603
|
+
padding: 6px 0;
|
|
604
|
+
z-index: 100;
|
|
605
|
+
min-width: 180px;
|
|
606
|
+
border: 1px solid var(--dstrb-border-light);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.dsh-trash-menu-item--danger {
|
|
610
|
+
width: 100%;
|
|
611
|
+
text-align: left;
|
|
612
|
+
padding: 8px 14px;
|
|
613
|
+
background-color: transparent;
|
|
614
|
+
border: none;
|
|
615
|
+
color: var(--dstrb-danger);
|
|
616
|
+
font-size: 13px;
|
|
617
|
+
font-family: inherit;
|
|
618
|
+
display: flex;
|
|
619
|
+
align-items: center;
|
|
620
|
+
cursor: pointer;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
.dsh-trash-menu-item--danger:hover {
|
|
624
|
+
background-color: var(--dstrb-danger-veil);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/* ------------------------------------------------------------------ */
|
|
628
|
+
/* Session cards */
|
|
629
|
+
/* ------------------------------------------------------------------ */
|
|
630
|
+
|
|
631
|
+
.dsh-trash-cards {
|
|
632
|
+
display: flex;
|
|
633
|
+
flex-direction: column;
|
|
634
|
+
background-color: var(--dstrb-surface);
|
|
635
|
+
border-radius: 16px;
|
|
636
|
+
border: 1px solid var(--dstrb-border-light);
|
|
637
|
+
overflow: hidden;
|
|
638
|
+
box-shadow: var(--dstrb-shadow-card);
|
|
639
|
+
/* Query container for the narrow-panel rules below: the panel sits inside
|
|
640
|
+
the settings column, so its own width — not the viewport — decides what
|
|
641
|
+
the meta row can still show. */
|
|
642
|
+
container-type: inline-size;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/* ONE geometry at every width. A per-width padding/gap (plus a label that
|
|
646
|
+
reappears once the container grows) makes the title NARROWER on a wider
|
|
647
|
+
panel — a 400px list showed a 97px title while a 310px one showed 178px.
|
|
648
|
+
With the row's fixed costs constant, the title grows 1:1 with the panel. */
|
|
649
|
+
.dsh-trash-card {
|
|
650
|
+
display: flex;
|
|
651
|
+
justify-content: space-between;
|
|
652
|
+
align-items: center;
|
|
653
|
+
padding: 12px 10px;
|
|
654
|
+
border-bottom: 1px solid var(--dstrb-border-hairline);
|
|
655
|
+
transition: background-color 0.15s ease;
|
|
656
|
+
gap: 4px;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.dsh-trash-card--checked {
|
|
660
|
+
background-color: var(--dstrb-surface-subtle);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
.dsh-trash-checkbox {
|
|
664
|
+
width: 16px;
|
|
665
|
+
height: 16px;
|
|
666
|
+
border-radius: 4px;
|
|
667
|
+
border: 1px solid var(--dstrb-border);
|
|
668
|
+
cursor: pointer;
|
|
669
|
+
accent-color: var(--dstrb-accent);
|
|
670
|
+
margin-right: 8px;
|
|
671
|
+
flex-shrink: 0;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/* The row gap already separates the card checkbox; an extra margin would come
|
|
675
|
+
straight out of the title's width. Declared AFTER the shared rule: same
|
|
676
|
+
specificity, so source order decides. */
|
|
677
|
+
.dsh-trash-checkbox--card {
|
|
678
|
+
margin-right: 0;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
.dsh-trash-card-main {
|
|
682
|
+
display: flex;
|
|
683
|
+
flex-direction: column;
|
|
684
|
+
gap: 6px;
|
|
685
|
+
min-width: 0;
|
|
686
|
+
flex: 1 1 0%;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.dsh-trash-card-title {
|
|
690
|
+
font-size: 14px;
|
|
691
|
+
font-weight: 600;
|
|
692
|
+
color: var(--dstrb-text);
|
|
693
|
+
overflow: hidden;
|
|
694
|
+
text-overflow: ellipsis;
|
|
695
|
+
white-space: nowrap;
|
|
696
|
+
line-height: 1.3;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
.dsh-trash-card-meta {
|
|
700
|
+
display: flex;
|
|
701
|
+
align-items: center;
|
|
702
|
+
flex-wrap: wrap;
|
|
703
|
+
gap: 14px;
|
|
704
|
+
font-size: 12px;
|
|
705
|
+
color: var(--dstrb-text-muted);
|
|
706
|
+
line-height: 1;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
.dsh-trash-card-meta-item {
|
|
710
|
+
white-space: nowrap;
|
|
711
|
+
/* An item wider than the wrapping line must yield, not spill: without this a
|
|
712
|
+
squeezed card clips the timestamp by a couple of pixels inside its own
|
|
713
|
+
column (scrollWidth > clientWidth). */
|
|
714
|
+
min-width: 0;
|
|
715
|
+
overflow: hidden;
|
|
716
|
+
text-overflow: ellipsis;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.dsh-trash-badge {
|
|
720
|
+
background-color: var(--dstrb-surface-subtle);
|
|
721
|
+
padding: 2px 8px;
|
|
722
|
+
border-radius: 6px;
|
|
723
|
+
font-size: 11px;
|
|
724
|
+
color: var(--dstrb-text-muted);
|
|
725
|
+
border: 1px solid var(--dstrb-border-light);
|
|
726
|
+
white-space: nowrap;
|
|
727
|
+
display: inline-flex;
|
|
728
|
+
align-items: center;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/* Sub-session badge: same box as the size badge, but it discloses the inline
|
|
732
|
+
list, so it is a real button rather than a label. No font-size of its own —
|
|
733
|
+
it inherits the badge's, which keeps both badges the same size. */
|
|
734
|
+
.dsh-trash-badge--children {
|
|
735
|
+
font-family: inherit;
|
|
736
|
+
cursor: pointer;
|
|
737
|
+
transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
.dsh-trash-badge--children:hover {
|
|
741
|
+
background-color: var(--dstrb-surface-hover);
|
|
742
|
+
border-color: var(--dstrb-text-faint);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/* The open state is carried by the accent border and icon; the COUNT is 11px
|
|
746
|
+
text, and --dstrb-accent on the accent veil measures 3.22:1 — under the
|
|
747
|
+
4.5:1 text bar this count already had to meet. */
|
|
748
|
+
.dsh-trash-badge--children[aria-expanded="true"] {
|
|
749
|
+
background-color: var(--dstrb-accent-veil);
|
|
750
|
+
border-color: var(--dstrb-accent);
|
|
751
|
+
color: var(--dstrb-text-strong);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
.dsh-trash-badge--children[aria-expanded="true"] .dsh-trash-icon-badge {
|
|
755
|
+
color: var(--dstrb-accent);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
.dsh-trash-badge--children:focus-visible {
|
|
759
|
+
outline: 2px solid var(--dstrb-accent);
|
|
760
|
+
outline-offset: 1px;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/* A reserved slot with tabular figures: the count is backfilled asynchronously,
|
|
764
|
+
so its digits must not resize the badge and shift the row under it. */
|
|
765
|
+
.dsh-trash-badge-num {
|
|
766
|
+
min-width: 1.6em;
|
|
767
|
+
text-align: center;
|
|
768
|
+
font-variant-numeric: tabular-nums;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
.dsh-trash-card-actions {
|
|
772
|
+
display: flex;
|
|
773
|
+
align-items: center;
|
|
774
|
+
/* Icon-only buttons need less air than labelled pills; the width they give up
|
|
775
|
+
belongs to the title. */
|
|
776
|
+
gap: 4px;
|
|
777
|
+
flex-shrink: 0;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/* The action LABELS are hidden at every width, not just on a narrow panel.
|
|
781
|
+
Re-showing them once the container grows is what made a wider panel show a
|
|
782
|
+
narrower title: a label costs ~123px, so the title would drop by that much
|
|
783
|
+
the moment they come back. The icons keep a full accessible name
|
|
784
|
+
(aria-label + title), which is the state P0-4 asks for on a narrow panel.
|
|
785
|
+
To trade this back, show .dsh-trash-btn-label under an @container rule and
|
|
786
|
+
accept that the title then steps DOWN at that width. */
|
|
787
|
+
.dsh-trash-btn-label {
|
|
788
|
+
display: none;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
.dsh-trash-btn-ghost .dsh-trash-icon-sm,
|
|
792
|
+
.dsh-trash-btn-danger-text .dsh-trash-icon-sm {
|
|
793
|
+
margin-right: 0;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
.dsh-trash-btn-ghost {
|
|
797
|
+
background-color: var(--dstrb-surface-subtle);
|
|
798
|
+
color: var(--dstrb-text-secondary);
|
|
799
|
+
border: 1px solid var(--dstrb-border);
|
|
800
|
+
padding: 4px 5px;
|
|
801
|
+
border-radius: 12px;
|
|
802
|
+
font-size: 11px;
|
|
803
|
+
font-weight: 500;
|
|
804
|
+
font-family: inherit;
|
|
805
|
+
display: inline-flex;
|
|
806
|
+
align-items: center;
|
|
807
|
+
/* WCAG 2.5.8: an icon-only action keeps a >=24x24 target. */
|
|
808
|
+
min-width: 24px;
|
|
809
|
+
min-height: 24px;
|
|
810
|
+
justify-content: center;
|
|
811
|
+
cursor: pointer;
|
|
812
|
+
transition: all 0.12s ease;
|
|
813
|
+
white-space: nowrap;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
.dsh-trash-btn-ghost:hover {
|
|
817
|
+
background-color: var(--dstrb-surface-hover);
|
|
818
|
+
border-color: var(--dstrb-text-faint);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
.dsh-trash-btn-danger-text {
|
|
822
|
+
background-color: transparent;
|
|
823
|
+
color: var(--dstrb-danger);
|
|
824
|
+
border: none;
|
|
825
|
+
padding: 4px 5px;
|
|
826
|
+
border-radius: 12px;
|
|
827
|
+
font-size: 11px;
|
|
828
|
+
font-weight: 500;
|
|
829
|
+
font-family: inherit;
|
|
830
|
+
display: inline-flex;
|
|
831
|
+
align-items: center;
|
|
832
|
+
/* WCAG 2.5.8: an icon-only action keeps a >=24x24 target. */
|
|
833
|
+
min-width: 24px;
|
|
834
|
+
min-height: 24px;
|
|
835
|
+
justify-content: center;
|
|
836
|
+
cursor: pointer;
|
|
837
|
+
transition: all 0.12s ease;
|
|
838
|
+
white-space: nowrap;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
.dsh-trash-btn-danger-text:hover {
|
|
842
|
+
background-color: var(--dstrb-danger-veil);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/* ------------------------------------------------------------------ */
|
|
846
|
+
/* Sub-session rows — full-width inline list under a card */
|
|
847
|
+
/* ------------------------------------------------------------------ */
|
|
848
|
+
|
|
849
|
+
/* Sits between two cards of the list, so it owns the hairline separator the
|
|
850
|
+
card would otherwise paint. The nesting cue is the RAIL, not a background
|
|
851
|
+
tint: a tint cannot carry a boundary (a subtle fill is ~1.05:1 against the
|
|
852
|
+
card by construction), so the hierarchy hangs on a line that meets the
|
|
853
|
+
>=3:1 non-text contrast bar in both themes — see --dstrb-structure.
|
|
854
|
+
24px = the card's 10px padding + the 2px rail + the 12px indent. */
|
|
855
|
+
.dsh-trash-children {
|
|
856
|
+
position: relative;
|
|
857
|
+
padding: 6px 10px 10px 24px;
|
|
858
|
+
background-color: transparent;
|
|
859
|
+
border-bottom: 1px solid var(--dstrb-border-hairline);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
.dsh-trash-children::before {
|
|
863
|
+
content: '';
|
|
864
|
+
position: absolute;
|
|
865
|
+
left: 10px;
|
|
866
|
+
top: 6px;
|
|
867
|
+
bottom: 10px;
|
|
868
|
+
width: 2px;
|
|
869
|
+
border-radius: 1px;
|
|
870
|
+
background-color: var(--dstrb-structure);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
.dsh-trash-child {
|
|
874
|
+
display: flex;
|
|
875
|
+
align-items: center;
|
|
876
|
+
gap: 6px;
|
|
877
|
+
height: 28px;
|
|
878
|
+
font-size: 0.92em;
|
|
879
|
+
color: var(--dstrb-text-secondary);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
.dsh-trash-child--live {
|
|
883
|
+
/* No row-level opacity: fading the whole row drags the TITLE down with it
|
|
884
|
+
(2.81:1, below the 4.5:1 text bar). The running state is carried by the
|
|
885
|
+
pill plus a danger tint, and only the periphery is dimmed — below. */
|
|
886
|
+
background-color: var(--dstrb-danger-veil);
|
|
887
|
+
border-radius: 6px;
|
|
888
|
+
cursor: not-allowed;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
.dsh-trash-child--live .dsh-trash-child-check,
|
|
892
|
+
.dsh-trash-child--live .dsh-trash-child-meta,
|
|
893
|
+
.dsh-trash-child--live .dsh-trash-child-delete {
|
|
894
|
+
opacity: 0.55;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
.dsh-trash-child-check {
|
|
898
|
+
/* Smaller and muted next to the card's own checkbox: this one scopes a
|
|
899
|
+
cascade delete, that one picks sessions for a batch action. */
|
|
900
|
+
width: 13px;
|
|
901
|
+
height: 13px;
|
|
902
|
+
margin-right: 0;
|
|
903
|
+
accent-color: var(--dstrb-text-faint);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
/* The title is the preview control (a real button), so it needs button
|
|
907
|
+
resets; it also has to outrank the meta line for the row's width. */
|
|
908
|
+
.dsh-trash-child-title {
|
|
909
|
+
flex: 1 1 auto;
|
|
910
|
+
min-width: 0;
|
|
911
|
+
overflow: hidden;
|
|
912
|
+
text-overflow: ellipsis;
|
|
913
|
+
white-space: nowrap;
|
|
914
|
+
padding: 0;
|
|
915
|
+
border: none;
|
|
916
|
+
background: none;
|
|
917
|
+
color: inherit;
|
|
918
|
+
font: inherit;
|
|
919
|
+
text-align: left;
|
|
920
|
+
cursor: pointer;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
.dsh-trash-child-title:hover {
|
|
924
|
+
color: var(--dstrb-text);
|
|
925
|
+
text-decoration: underline;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
.dsh-trash-child-title:focus-visible {
|
|
929
|
+
outline: 2px solid var(--dstrb-accent);
|
|
930
|
+
outline-offset: 1px;
|
|
931
|
+
border-radius: 3px;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
.dsh-trash-child-meta {
|
|
935
|
+
/* Shrinkable, so the title keeps its room: this is the first thing to give
|
|
936
|
+
way when the panel narrows. */
|
|
937
|
+
flex: 0 1 auto;
|
|
938
|
+
min-width: 0;
|
|
939
|
+
overflow: hidden;
|
|
940
|
+
text-overflow: ellipsis;
|
|
941
|
+
white-space: nowrap;
|
|
942
|
+
color: var(--dstrb-text-muted);
|
|
943
|
+
font-variant-numeric: tabular-nums;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
.dsh-trash-child-live {
|
|
947
|
+
flex: none;
|
|
948
|
+
padding: 0 5px;
|
|
949
|
+
border-radius: 4px;
|
|
950
|
+
border: 1px solid var(--dstrb-danger-border);
|
|
951
|
+
background-color: var(--dstrb-surface);
|
|
952
|
+
color: var(--dstrb-danger);
|
|
953
|
+
line-height: 1.6;
|
|
954
|
+
white-space: nowrap;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/* A 24x24 icon button, always visible: a hover-only affordance does not exist
|
|
958
|
+
on touch, and WCAG 2.5.8 asks for a >=24px target. */
|
|
959
|
+
.dsh-trash-child-delete {
|
|
960
|
+
flex: none;
|
|
961
|
+
display: inline-flex;
|
|
962
|
+
align-items: center;
|
|
963
|
+
justify-content: center;
|
|
964
|
+
width: 24px;
|
|
965
|
+
height: 24px;
|
|
966
|
+
padding: 0;
|
|
967
|
+
border: none;
|
|
968
|
+
border-radius: 6px;
|
|
969
|
+
background: none;
|
|
970
|
+
color: var(--dstrb-danger);
|
|
971
|
+
cursor: pointer;
|
|
972
|
+
transition: background-color 0.12s ease;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
.dsh-trash-child-delete:hover {
|
|
976
|
+
background-color: var(--dstrb-danger-veil);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
.dsh-trash-child-delete:focus-visible {
|
|
980
|
+
outline: 2px solid var(--dstrb-accent);
|
|
981
|
+
outline-offset: 1px;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
.dsh-trash-child-delete:disabled {
|
|
985
|
+
cursor: not-allowed;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
.dsh-trash-child-delete-icon {
|
|
989
|
+
width: 14px;
|
|
990
|
+
height: 14px;
|
|
991
|
+
margin-right: 0;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/* Two placeholder rows: the grouping is fetched after the list paints, so an
|
|
995
|
+
expanded card holds its height instead of jumping when the rows arrive. */
|
|
996
|
+
.dsh-trash-child--skeleton {
|
|
997
|
+
border-radius: 6px;
|
|
998
|
+
background-color: var(--dstrb-veil);
|
|
999
|
+
margin: 3px 0;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
.dsh-trash-children-more {
|
|
1003
|
+
display: block;
|
|
1004
|
+
width: 100%;
|
|
1005
|
+
padding: 4px 0 0;
|
|
1006
|
+
border: none;
|
|
1007
|
+
background: none;
|
|
1008
|
+
color: var(--dstrb-accent);
|
|
1009
|
+
font-family: inherit;
|
|
1010
|
+
font-size: inherit;
|
|
1011
|
+
text-align: left;
|
|
1012
|
+
cursor: pointer;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
.dsh-trash-children-empty {
|
|
1016
|
+
padding: 4px 0;
|
|
1017
|
+
color: var(--dstrb-text-muted);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
/* ── Narrow panel ───────────────────────────────────────────────────
|
|
1021
|
+
The container is the card list, so these fire on the panel's own width.
|
|
1022
|
+
Priority is fixed: the turn count goes first, the timestamp second, then the
|
|
1023
|
+
action LABELS (the icons stay — they keep their aria-label/title), and the
|
|
1024
|
+
sub-session age last. The size badge and the sub-session badge are never
|
|
1025
|
+
hidden: they are counts the row exists to show. */
|
|
1026
|
+
@container (max-width: 360px) {
|
|
1027
|
+
.dsh-trash-card-meta-item--turns {
|
|
1028
|
+
display: none;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/* Title first: the age gives way instead of squeezing the name. The row's
|
|
1032
|
+
size and age stay in the TITLE BUTTON's accessible name, which is never
|
|
1033
|
+
hidden — `display: none` here would drop them out of the accessibility
|
|
1034
|
+
tree entirely, and a tooltip is not readable by keyboard or screen
|
|
1035
|
+
reader. */
|
|
1036
|
+
.dsh-trash-child-meta {
|
|
1037
|
+
display: none;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
@container (max-width: 320px) {
|
|
1042
|
+
.dsh-trash-card-meta-item--time {
|
|
1043
|
+
display: none;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/* Kept sub-sessions of a main session that is already gone: no restore is
|
|
1048
|
+
offered for them, so the row reads as a bin shell, not as a card. */
|
|
1049
|
+
.dsh-trash-card--orphan {
|
|
1050
|
+
background-color: var(--dstrb-surface-subtle);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
.dsh-trash-tag-kept {
|
|
1054
|
+
padding: 0 5px;
|
|
1055
|
+
border-radius: 4px;
|
|
1056
|
+
border: 1px solid var(--dstrb-border);
|
|
1057
|
+
background-color: var(--dstrb-surface);
|
|
1058
|
+
color: var(--dstrb-text-muted);
|
|
1059
|
+
white-space: nowrap;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/* ------------------------------------------------------------------ */
|
|
1063
|
+
/* Hover info card (left/top set inline by JS, from the hovered TITLE) */
|
|
1064
|
+
/* ------------------------------------------------------------------ */
|
|
1065
|
+
/*
|
|
1066
|
+
* Deliberately NOT the host tooltip language. `.dsh-trash-tooltip-host` is a
|
|
1067
|
+
* short hint ON a button and follows the app's own tooltip tokens
|
|
1068
|
+
* (--dsw-alias-tooltip-*), which are a DARK bubble in both themes — the host's
|
|
1069
|
+
* choice for one-line hints. This card is a small PANEL of the recycle bin that
|
|
1070
|
+
* the user reads and selects text from, so it is painted with the plugin's own
|
|
1071
|
+
* theme surface: white in the light theme, #1f2530 in the dark one, like every
|
|
1072
|
+
* other surface of this panel. The boundary uses the structure token
|
|
1073
|
+
* (4.76:1 light / 5.18:1 dark), not a hairline.
|
|
1074
|
+
*/
|
|
1075
|
+
.dsh-trash-tooltip {
|
|
1076
|
+
position: fixed;
|
|
1077
|
+
z-index: 20000;
|
|
1078
|
+
display: flex;
|
|
1079
|
+
flex-direction: column;
|
|
1080
|
+
gap: 6px;
|
|
1081
|
+
max-width: min(380px, 92vw);
|
|
1082
|
+
padding: 10px 12px;
|
|
1083
|
+
border-radius: 8px;
|
|
1084
|
+
background: var(--dstrb-surface);
|
|
1085
|
+
color: var(--dstrb-text-strong);
|
|
1086
|
+
/* The boundary is a real boundary, so it uses the structure token (>=3:1)
|
|
1087
|
+
rather than a hairline that only reads as a smudge; the elevation shadow
|
|
1088
|
+
underneath carries the same message. */
|
|
1089
|
+
border: 1px solid var(--dstrb-structure);
|
|
1090
|
+
box-shadow: var(--dstrb-shadow-tooltip);
|
|
1091
|
+
/* Readable and selectable: the card is content, not a decoration. */
|
|
1092
|
+
pointer-events: auto;
|
|
1093
|
+
user-select: text;
|
|
1094
|
+
-webkit-user-select: text;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
/* Placement is decided in JS from the measured box; the transform only flips
|
|
1098
|
+
the card to the row's other side. */
|
|
1099
|
+
.dsh-trash-tooltip--above {
|
|
1100
|
+
transform: translateY(-100%);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
.dsh-trash-tooltip--below {
|
|
1104
|
+
transform: none;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
.dsh-trash-tooltip-title {
|
|
1108
|
+
font-size: 1em;
|
|
1109
|
+
font-weight: 600;
|
|
1110
|
+
color: inherit;
|
|
1111
|
+
word-break: break-all;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
.dsh-trash-tooltip-sub {
|
|
1115
|
+
font-size: 0.86em;
|
|
1116
|
+
color: var(--dstrb-text-muted);
|
|
1117
|
+
font-family: var(--dstrb-mono, var(--ds-font-family-code, var(--dstrb-mono-fallback)));
|
|
1118
|
+
word-break: break-all;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/* ------------------------------------------------------------------ */
|
|
1122
|
+
/* Footer */
|
|
1123
|
+
/* ------------------------------------------------------------------ */
|
|
1124
|
+
|
|
1125
|
+
.dsh-trash-footer {
|
|
1126
|
+
margin-top: 24px;
|
|
1127
|
+
font-size: 12px;
|
|
1128
|
+
color: var(--dstrb-text-muted);
|
|
1129
|
+
text-align: right;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/* ------------------------------------------------------------------ */
|
|
1133
|
+
/* Modals */
|
|
1134
|
+
/* ------------------------------------------------------------------ */
|
|
1135
|
+
|
|
1136
|
+
.dsh-trash-overlay {
|
|
1137
|
+
position: fixed;
|
|
1138
|
+
top: 0;
|
|
1139
|
+
left: 0;
|
|
1140
|
+
right: 0;
|
|
1141
|
+
bottom: 0;
|
|
1142
|
+
background-color: var(--dstrb-overlay);
|
|
1143
|
+
display: flex;
|
|
1144
|
+
justify-content: center;
|
|
1145
|
+
align-items: center;
|
|
1146
|
+
z-index: 10000;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
.dsh-trash-preview {
|
|
1150
|
+
background-color: var(--dstrb-surface);
|
|
1151
|
+
border-radius: 12px;
|
|
1152
|
+
width: 680px;
|
|
1153
|
+
max-width: 92vw;
|
|
1154
|
+
max-height: 85vh;
|
|
1155
|
+
display: flex;
|
|
1156
|
+
flex-direction: column;
|
|
1157
|
+
box-shadow: var(--dstrb-shadow-modal);
|
|
1158
|
+
overflow: hidden;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
.dsh-trash-preview-header {
|
|
1162
|
+
padding: 16px 24px;
|
|
1163
|
+
border-bottom: 1px solid var(--dstrb-border-light);
|
|
1164
|
+
display: flex;
|
|
1165
|
+
justify-content: space-between;
|
|
1166
|
+
align-items: center;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
.dsh-trash-preview-title {
|
|
1170
|
+
font-size: 16px;
|
|
1171
|
+
font-weight: 600;
|
|
1172
|
+
color: var(--dstrb-text-strong);
|
|
1173
|
+
overflow: hidden;
|
|
1174
|
+
text-overflow: ellipsis;
|
|
1175
|
+
white-space: nowrap;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
.dsh-trash-close {
|
|
1179
|
+
background: none;
|
|
1180
|
+
border: none;
|
|
1181
|
+
font-size: 18px;
|
|
1182
|
+
color: var(--dstrb-text-muted);
|
|
1183
|
+
cursor: pointer;
|
|
1184
|
+
padding: 4px 8px;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
.dsh-trash-close:hover {
|
|
1188
|
+
color: var(--dstrb-text-muted);
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
.dsh-trash-preview-body {
|
|
1192
|
+
padding: 20px 24px;
|
|
1193
|
+
overflow-y: auto;
|
|
1194
|
+
display: flex;
|
|
1195
|
+
flex-direction: column;
|
|
1196
|
+
gap: 16px;
|
|
1197
|
+
max-height: 70vh;
|
|
1198
|
+
background-color: var(--dstrb-surface-subtle);
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
.dsh-trash-preview-state {
|
|
1202
|
+
text-align: center;
|
|
1203
|
+
padding: 30px;
|
|
1204
|
+
color: var(--dstrb-text-muted);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
.dsh-trash-modal {
|
|
1208
|
+
background-color: var(--dstrb-surface);
|
|
1209
|
+
border-radius: 12px;
|
|
1210
|
+
padding: 24px;
|
|
1211
|
+
width: 420px;
|
|
1212
|
+
max-width: 92vw;
|
|
1213
|
+
box-shadow: var(--dstrb-shadow-modal-sm);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
.dsh-trash-modal-title {
|
|
1217
|
+
margin: 0 0 12px 0;
|
|
1218
|
+
font-size: 16px;
|
|
1219
|
+
font-weight: 600;
|
|
1220
|
+
color: var(--dstrb-text-strong);
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
.dsh-trash-modal-body {
|
|
1224
|
+
margin: 0 0 20px 0;
|
|
1225
|
+
font-size: 14px;
|
|
1226
|
+
color: var(--dstrb-text-secondary);
|
|
1227
|
+
line-height: 1.5;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
/* The cascade dialog names every sub-session it will erase; past a handful the
|
|
1231
|
+
list scrolls inside the dialog rather than growing it. */
|
|
1232
|
+
.dsh-trash-modal-list {
|
|
1233
|
+
margin: -8px 0 20px 0;
|
|
1234
|
+
padding: 6px 10px;
|
|
1235
|
+
max-height: 40vh;
|
|
1236
|
+
overflow-y: auto;
|
|
1237
|
+
border: 1px solid var(--dstrb-border-light);
|
|
1238
|
+
border-radius: 8px;
|
|
1239
|
+
background-color: var(--dstrb-surface-subtle);
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
.dsh-trash-modal-list-item {
|
|
1243
|
+
color: var(--dstrb-text-secondary);
|
|
1244
|
+
line-height: 1.8;
|
|
1245
|
+
overflow: hidden;
|
|
1246
|
+
text-overflow: ellipsis;
|
|
1247
|
+
white-space: nowrap;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
.dsh-trash-modal-actions {
|
|
1251
|
+
display: flex;
|
|
1252
|
+
justify-content: flex-end;
|
|
1253
|
+
gap: 12px;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
.dsh-trash-btn-cancel {
|
|
1257
|
+
background-color: var(--dstrb-surface-hover);
|
|
1258
|
+
color: var(--dstrb-text-secondary);
|
|
1259
|
+
border: none;
|
|
1260
|
+
padding: 8px 16px;
|
|
1261
|
+
border-radius: 6px;
|
|
1262
|
+
font-size: 14px;
|
|
1263
|
+
font-family: inherit;
|
|
1264
|
+
cursor: pointer;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
.dsh-trash-btn-cancel:hover {
|
|
1268
|
+
background-color: var(--dstrb-border);
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
.dsh-trash-btn-confirm {
|
|
1272
|
+
background-color: var(--dstrb-danger);
|
|
1273
|
+
color: var(--dstrb-on-danger);
|
|
1274
|
+
border: none;
|
|
1275
|
+
padding: 8px 16px;
|
|
1276
|
+
border-radius: 6px;
|
|
1277
|
+
font-size: 14px;
|
|
1278
|
+
font-family: inherit;
|
|
1279
|
+
cursor: pointer;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
.dsh-trash-btn-confirm:hover {
|
|
1283
|
+
background-color: var(--dstrb-danger-strong);
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
/* ------------------------------------------------------------------ */
|
|
1287
|
+
/* Message bubbles (preview modal) */
|
|
1288
|
+
/* ------------------------------------------------------------------ */
|
|
1289
|
+
|
|
1290
|
+
.dsh-trash-message {
|
|
1291
|
+
display: flex;
|
|
1292
|
+
flex-direction: column;
|
|
1293
|
+
gap: 6px;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
.dsh-trash-message-header {
|
|
1297
|
+
display: flex;
|
|
1298
|
+
align-items: center;
|
|
1299
|
+
gap: 10px;
|
|
1300
|
+
font-size: 12px;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
.dsh-trash-message-role {
|
|
1304
|
+
font-weight: 600;
|
|
1305
|
+
color: var(--dstrb-text-secondary);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
.dsh-trash-message-time {
|
|
1309
|
+
color: var(--dstrb-text-muted);
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
.dsh-trash-bubble-user {
|
|
1313
|
+
background-color: var(--dstrb-bubble-user);
|
|
1314
|
+
color: var(--dstrb-text-strong);
|
|
1315
|
+
padding: 12px 16px;
|
|
1316
|
+
border-radius: 12px;
|
|
1317
|
+
border-top-left-radius: 2px;
|
|
1318
|
+
font-size: 14px;
|
|
1319
|
+
line-height: 1.6;
|
|
1320
|
+
white-space: pre-wrap;
|
|
1321
|
+
word-break: break-word;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
.dsh-trash-bubble-assistant {
|
|
1325
|
+
background-color: var(--dstrb-surface);
|
|
1326
|
+
color: var(--dstrb-text);
|
|
1327
|
+
padding: 14px 18px;
|
|
1328
|
+
border-radius: 12px;
|
|
1329
|
+
border-top-left-radius: 2px;
|
|
1330
|
+
border: 1px solid var(--dstrb-border);
|
|
1331
|
+
font-size: 14px;
|
|
1332
|
+
line-height: 1.6;
|
|
1333
|
+
white-space: pre-wrap;
|
|
1334
|
+
word-break: break-word;
|
|
1335
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02);
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
/* ------------------------------------------------------------------ */
|
|
1339
|
+
/* Lightweight markdown renderer */
|
|
1340
|
+
/* ------------------------------------------------------------------ */
|
|
1341
|
+
|
|
1342
|
+
.dsh-trash-md {
|
|
1343
|
+
white-space: normal;
|
|
1344
|
+
word-break: break-word;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
.dsh-trash-md p {
|
|
1348
|
+
margin: 0 0 10px 0;
|
|
1349
|
+
line-height: 1.65;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
.dsh-trash-md h1 { font-size: 20px; font-weight: 600; margin: 16px 0 8px; line-height: 1.4; color: var(--dstrb-text-strong); }
|
|
1353
|
+
.dsh-trash-md h2 { font-size: 18px; font-weight: 600; margin: 14px 0 8px; line-height: 1.4; color: var(--dstrb-text-strong); }
|
|
1354
|
+
.dsh-trash-md h3 { font-size: 16px; font-weight: 600; margin: 12px 0 6px; line-height: 1.4; color: var(--dstrb-text-strong); }
|
|
1355
|
+
.dsh-trash-md h4 { font-size: 15px; font-weight: 600; margin: 12px 0 6px; line-height: 1.4; color: var(--dstrb-text-strong); }
|
|
1356
|
+
.dsh-trash-md h5 { font-size: 14px; font-weight: 600; margin: 10px 0 4px; line-height: 1.4; color: var(--dstrb-text-strong); }
|
|
1357
|
+
.dsh-trash-md h6 { font-size: 13px; font-weight: 600; margin: 10px 0 4px; line-height: 1.4; color: var(--dstrb-text-strong); }
|
|
1358
|
+
|
|
1359
|
+
.dsh-trash-md ul,
|
|
1360
|
+
.dsh-trash-md ol {
|
|
1361
|
+
margin: 6px 0 10px;
|
|
1362
|
+
padding-left: 22px;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
.dsh-trash-md li {
|
|
1366
|
+
margin: 3px 0;
|
|
1367
|
+
line-height: 1.6;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
.dsh-trash-md blockquote {
|
|
1371
|
+
border-left: 3px solid var(--dstrb-quote-border);
|
|
1372
|
+
margin: 8px 0;
|
|
1373
|
+
padding: 4px 12px;
|
|
1374
|
+
color: var(--dstrb-text-secondary);
|
|
1375
|
+
background-color: var(--dstrb-quote-bg);
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
.dsh-trash-md hr {
|
|
1379
|
+
border: none;
|
|
1380
|
+
border-top: 1px solid var(--dstrb-border);
|
|
1381
|
+
margin: 14px 0;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
.dsh-trash-md-code-inline {
|
|
1385
|
+
background-color: var(--dstrb-code-inline-bg);
|
|
1386
|
+
padding: 2px 5px;
|
|
1387
|
+
border-radius: 4px;
|
|
1388
|
+
font-family: var(--dstrb-mono, var(--ds-font-family-code, var(--dstrb-mono-fallback)));
|
|
1389
|
+
font-size: 0.9em;
|
|
1390
|
+
color: var(--dstrb-code-inline-text);
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
.dsh-trash-md-codeblock {
|
|
1394
|
+
background-color: var(--dstrb-codeblock-bg);
|
|
1395
|
+
color: var(--dstrb-codeblock-text);
|
|
1396
|
+
padding: 12px 14px;
|
|
1397
|
+
border-radius: 8px;
|
|
1398
|
+
overflow-x: auto;
|
|
1399
|
+
margin: 10px 0;
|
|
1400
|
+
font-family: var(--dstrb-mono, var(--ds-font-family-code, var(--dstrb-mono-fallback)));
|
|
1401
|
+
font-size: 13px;
|
|
1402
|
+
line-height: 1.5;
|
|
1403
|
+
white-space: pre-wrap;
|
|
1404
|
+
word-break: break-word;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
.dsh-trash-md-codeblock-content {
|
|
1408
|
+
font-family: inherit;
|
|
1409
|
+
font-size: inherit;
|
|
1410
|
+
white-space: pre-wrap;
|
|
1411
|
+
word-break: break-word;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
.dsh-trash-md-lang {
|
|
1415
|
+
font-size: 11px;
|
|
1416
|
+
color: var(--dstrb-codeblock-lang);
|
|
1417
|
+
text-transform: uppercase;
|
|
1418
|
+
letter-spacing: 0.5px;
|
|
1419
|
+
margin-bottom: 6px;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
.dsh-trash-md-link {
|
|
1423
|
+
color: var(--dstrb-accent);
|
|
1424
|
+
text-decoration: underline;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
.dsh-trash-md-table-wrap {
|
|
1428
|
+
overflow-x: auto;
|
|
1429
|
+
margin: 10px 0;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
.dsh-trash-md-table {
|
|
1433
|
+
border-collapse: collapse;
|
|
1434
|
+
width: 100%;
|
|
1435
|
+
font-size: 13px;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
.dsh-trash-md-th {
|
|
1439
|
+
border: 1px solid var(--dstrb-border);
|
|
1440
|
+
padding: 6px 10px;
|
|
1441
|
+
background-color: var(--dstrb-surface-subtle);
|
|
1442
|
+
font-weight: 600;
|
|
1443
|
+
text-align: left;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
.dsh-trash-md-td {
|
|
1447
|
+
border: 1px solid var(--dstrb-border);
|
|
1448
|
+
padding: 6px 10px;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
/* ------------------------------------------------------------------ */
|
|
1452
|
+
/* Toast layer — host-matched surface */
|
|
1453
|
+
/* ------------------------------------------------------------------ */
|
|
1454
|
+
/*
|
|
1455
|
+
* Ported from @deepseek-ai/dsh-client-ui-primitives Toast.module.css. This
|
|
1456
|
+
* hand-written bundle cannot import that package: primitives declares no
|
|
1457
|
+
* `dsh.client`, so Vite inlines it into each consumer and it is neither a
|
|
1458
|
+
* platform seed word nor a registered module-row factory — `require()` would
|
|
1459
|
+
* miss the module table. Porting the spec is the only way to match it.
|
|
1460
|
+
*
|
|
1461
|
+
* Two things changed beyond styling:
|
|
1462
|
+
* - Placement is TOP-CENTRE, not a bottom-right card.
|
|
1463
|
+
* - The surface is dark in BOTH themes. The old --dstrb-toast-* pair flipped
|
|
1464
|
+
* to a LIGHT panel in dark mode, i.e. the exact opposite of the host.
|
|
1465
|
+
* The host tokens lead with the plugin's own as fallbacks so older cohorts,
|
|
1466
|
+
* which publish no --dsw-* palette, still render a readable toast.
|
|
1467
|
+
*/
|
|
1468
|
+
|
|
1469
|
+
.dsh-trash-toast-container {
|
|
1470
|
+
position: fixed;
|
|
1471
|
+
top: 40px;
|
|
1472
|
+
left: 50%;
|
|
1473
|
+
transform: translateX(-50%);
|
|
1474
|
+
/* Above the 1000 the image lightbox backdrop uses, as the host does. */
|
|
1475
|
+
z-index: 1100;
|
|
1476
|
+
display: flex;
|
|
1477
|
+
flex-direction: column;
|
|
1478
|
+
align-items: center;
|
|
1479
|
+
gap: 8px;
|
|
1480
|
+
/* Announcements never intercept clicks; only the action text does. */
|
|
1481
|
+
pointer-events: none;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
.dsh-trash-toast {
|
|
1485
|
+
display: flex;
|
|
1486
|
+
align-items: center;
|
|
1487
|
+
gap: 10px;
|
|
1488
|
+
width: max-content;
|
|
1489
|
+
max-width: min(640px, calc(100vw - 48px));
|
|
1490
|
+
padding: 12px 16px;
|
|
1491
|
+
border-radius: var(--dsw-radius-lg, 12px);
|
|
1492
|
+
background: var(--dsw-alias-toast-bg, var(--dstrb-toast-bg));
|
|
1493
|
+
color: var(--dsw-alias-toast-label, var(--dstrb-toast-text));
|
|
1494
|
+
font-size: 14px;
|
|
1495
|
+
line-height: 22px;
|
|
1496
|
+
box-shadow: var(--dsw-shadow-lv3, var(--dstrb-shadow-toast));
|
|
1497
|
+
pointer-events: none;
|
|
1498
|
+
/* One value drives both the fade delay and the unmount timer below. */
|
|
1499
|
+
animation:
|
|
1500
|
+
dsh-trash-toast-in 160ms ease-out,
|
|
1501
|
+
dsh-trash-toast-fade 1000ms ease var(--dsh-toast-hold, 3000ms) forwards;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
@keyframes dsh-trash-toast-in {
|
|
1505
|
+
from {
|
|
1506
|
+
opacity: 0;
|
|
1507
|
+
transform: translateY(-6px);
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
to {
|
|
1511
|
+
opacity: 1;
|
|
1512
|
+
transform: translateY(0);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
@keyframes dsh-trash-toast-fade {
|
|
1517
|
+
to {
|
|
1518
|
+
opacity: 0;
|
|
1519
|
+
visibility: hidden;
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
/* Reduced motion drops the slide-in; the delayed fade still ends the banner
|
|
1524
|
+
before the timed unmount. */
|
|
1525
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1526
|
+
.dsh-trash-toast {
|
|
1527
|
+
animation: dsh-trash-toast-fade 1000ms ease var(--dsh-toast-hold, 3000ms) forwards;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
.dsh-trash-toast-undo {
|
|
1532
|
+
display: inline;
|
|
1533
|
+
padding: 0;
|
|
1534
|
+
border: 0;
|
|
1535
|
+
background: none;
|
|
1536
|
+
/* deepseek-400 reads as the action against the always-dark surface. */
|
|
1537
|
+
color: var(--dsw-static-deepseek-400, var(--dstrb-toast-btn-text));
|
|
1538
|
+
font: inherit;
|
|
1539
|
+
cursor: pointer;
|
|
1540
|
+
margin-inline: 3px;
|
|
1541
|
+
/* The banner stays click-through; only this takes the pointer. */
|
|
1542
|
+
pointer-events: auto;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
.dsh-trash-toast-undo:hover {
|
|
1546
|
+
opacity: 0.8;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
/* ------------------------------------------------------------------ */
|
|
1550
|
+
/* Conversation-header delete action */
|
|
1551
|
+
/* ------------------------------------------------------------------ */
|
|
1552
|
+
|
|
1553
|
+
.dsh-trash-header-action {
|
|
1554
|
+
display: inline-flex;
|
|
1555
|
+
align-items: center;
|
|
1556
|
+
justify-content: center;
|
|
1557
|
+
background: none;
|
|
1558
|
+
border: none;
|
|
1559
|
+
padding: 5px;
|
|
1560
|
+
width: 26px;
|
|
1561
|
+
height: 26px;
|
|
1562
|
+
border-radius: 7px;
|
|
1563
|
+
line-height: 1;
|
|
1564
|
+
cursor: pointer;
|
|
1565
|
+
color: var(--dsw-alias-label-tertiary, var(--dstrb-text-muted));
|
|
1566
|
+
opacity: 0.85;
|
|
1567
|
+
transition: color 0.12s ease, background 0.12s ease, opacity 0.12s ease;
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
.dsh-trash-header-action:hover {
|
|
1571
|
+
color: var(--dstrb-danger);
|
|
1572
|
+
background: var(--dstrb-veil);
|
|
1573
|
+
opacity: 1;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
/* One-line label tooltip for the header action, replacing the native `title`
|
|
1577
|
+
attribute: an OS-drawn bubble next to the host's themed tooltips read as
|
|
1578
|
+
"unstyled plugin", and it cannot follow the palette or the motion setting.
|
|
1579
|
+
Ported from @deepseek-ai/dsh-client-ui-primitives Tooltip.module.css for the
|
|
1580
|
+
same reason the toast is (the package is inlined by Vite, so it cannot be
|
|
1581
|
+
required from a hand-written bundle).
|
|
1582
|
+
`translateX(-100%)` is the host's `side: bottom` + `align: end` transform,
|
|
1583
|
+
so `left` carries the anchor's RIGHT edge. */
|
|
1584
|
+
.dsh-trash-tooltip-host {
|
|
1585
|
+
position: fixed;
|
|
1586
|
+
z-index: 100;
|
|
1587
|
+
width: max-content;
|
|
1588
|
+
max-width: 50vw;
|
|
1589
|
+
padding: 3px 7px;
|
|
1590
|
+
border-radius: var(--dsw-radius-sm, 4px);
|
|
1591
|
+
background: var(--dsw-alias-tooltip-bg, var(--dstrb-tooltip-bg));
|
|
1592
|
+
color: var(--dsw-static-neutral-bluish-00, var(--dstrb-tooltip-text));
|
|
1593
|
+
font-size: 13px;
|
|
1594
|
+
line-height: 20px;
|
|
1595
|
+
white-space: pre-line;
|
|
1596
|
+
/* Unbreakable tokens (paths, URLs) must not push past max-width. */
|
|
1597
|
+
overflow-wrap: break-word;
|
|
1598
|
+
pointer-events: none;
|
|
1599
|
+
transform: translateX(-100%);
|
|
1600
|
+
animation: dsh-trash-tooltip-in 150ms var(--ds-ease-in-out, ease-in-out);
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
@keyframes dsh-trash-tooltip-in {
|
|
1604
|
+
from {
|
|
1605
|
+
opacity: 0;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1610
|
+
.dsh-trash-tooltip-host {
|
|
1611
|
+
animation: none;
|
|
1612
|
+
}
|
|
1613
|
+
}
|