@qilin-agent/client-ui-primitives 3.1.7 → 3.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.i18n.yaml +8 -8
  2. package/README.md +11 -4
  3. package/README.zh.md +12 -4
  4. package/lib/AnimatedRows.module.css +7 -0
  5. package/lib/Button.module.css +6 -3
  6. package/lib/DetailDocument.module.css +67 -0
  7. package/lib/HoverCard.module.css +3 -3
  8. package/lib/ImageLightbox.module.css +1 -1
  9. package/lib/InlineEditor.module.css +1 -1
  10. package/lib/Input.module.css +2 -2
  11. package/lib/Menu.module.css +127 -78
  12. package/lib/MenuSurface.module.css +23 -7
  13. package/lib/QuotePreview.module.css +89 -0
  14. package/lib/RowDragPreview.module.css +12 -0
  15. package/lib/SegmentedTabs.module.css +4 -1
  16. package/lib/Tag.module.css +1 -1
  17. package/lib/TerminalBlock.module.css +1 -1
  18. package/lib/Toast.module.css +28 -0
  19. package/lib/UserMessageBubble.module.css +11 -0
  20. package/lib/index.js +2094 -1115
  21. package/lib/markdown/CodeBlock.module.css +23 -0
  22. package/lib/markdown/JsonBlock.module.css +1 -1
  23. package/lib/markdown/MarkdownText.module.css +11 -7
  24. package/lib/settings-form/fields.module.css +2 -2
  25. package/lib/types/AnimatedRows.d.ts +46 -0
  26. package/lib/types/Button.d.ts +13 -4
  27. package/lib/types/DetailDocument.d.ts +79 -0
  28. package/lib/types/ImageLightbox.d.ts +3 -1
  29. package/lib/types/Menu.d.ts +25 -9
  30. package/lib/types/MenuSurface.d.ts +9 -0
  31. package/lib/types/QuotePreview.d.ts +26 -0
  32. package/lib/types/SegmentedTabs.d.ts +3 -1
  33. package/lib/types/Toast.d.ts +19 -1
  34. package/lib/types/Tooltip.d.ts +13 -8
  35. package/lib/types/UserMessageBubble.d.ts +11 -0
  36. package/lib/types/code-highlighting.d.ts +12 -0
  37. package/lib/types/focus.d.ts +8 -0
  38. package/lib/types/icons/index.d.ts +8 -0
  39. package/lib/types/icons/shared-artwork.d.ts +1 -0
  40. package/lib/types/index.d.ts +20 -4
  41. package/lib/types/markdown/MarkdownDelegate.d.ts +19 -2
  42. package/lib/types/markdown/MarkdownText.d.ts +3 -1
  43. package/lib/types/markdown/file-link.d.ts +13 -3
  44. package/lib/types/markdown/mathCompatibility.d.ts +12 -5
  45. package/lib/types/markdown/render.d.ts +2 -1
  46. package/lib/types/markdown/windows-path-destinations.d.ts +12 -0
  47. package/lib/types/motion.d.ts +13 -0
  48. package/lib/types/overlay-top-margin.d.ts +10 -2
  49. package/lib/types/quote-text.d.ts +69 -0
  50. package/lib/types/use-clipboard-copy.d.ts +14 -0
  51. package/lib/types/useAnchoredPosition.d.ts +31 -18
  52. package/lib/types/useRowDragPreview.d.ts +20 -0
  53. package/lib/types/user-text.d.ts +24 -2
  54. package/package.json +8 -6
  55. package/lib/types/use-copy-feedback.d.ts +0 -14
@@ -3,8 +3,7 @@
3
3
  display: inline-flex;
4
4
  }
5
5
 
6
- /* Dropdown card (figma MenuDropdown 122:9481 / 419:16920): menu surface,
7
- * r12, elevation-prominent (hairline stroke in shadow), 4px inset padding. */
6
+ /* Compact translucent dropdown card shared by feature menus. */
8
7
  .list,
9
8
  .submenu {
10
9
  /* min-widths below are the design's outer card widths — include the pad. */
@@ -14,27 +13,30 @@
14
13
  flex-direction: column;
15
14
  gap: 0;
16
15
  border: 0;
17
- border-radius: 20px;
18
- background: var(--qilin-specific-menu);
19
- --qilin-elevation-stroke-color: var(--qilin-alias-border-l1);
20
- box-shadow: var(--qilin-elevation-prominent);
16
+ --dsw-elevation-stroke-color: var(--dsw-alias-border-l1);
17
+ box-shadow: var(--dsw-elevation-prominent);
21
18
  /* Elevated surface: the scrollbar thumb takes the l2 elevation tokens. The
22
19
  declaration sits on the card rather than on `.scrollable .viewport`
23
20
  because the elevation is a property of this surface, and the custom
24
21
  properties inherit down to whichever descendant actually scrolls (see
25
22
  ui-theme styles/scrollbar.css for the rebinding contract). */
26
- --qilin-scrollbar-thumb: var(--qilin-alias-scrollbar-bg-l2);
27
- --qilin-scrollbar-thumb-hover: var(--qilin-alias-scrollbar-hover-l2);
23
+ --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
24
+ --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);
28
25
  }
29
26
 
30
- /* Primary card is 218 wide in the design across both hosts. */
27
+ /* Primary cards grow with their content between compact and readable bounds. */
31
28
  .list {
32
29
  position: absolute;
33
30
  top: calc(100% + 4px);
34
31
  left: 0;
35
32
  z-index: 100;
36
- min-width: 218px;
37
- max-width: 360px;
33
+ min-width: min(144px, var(--dsh-anchored-max-width, 100vw));
34
+ max-width: min(360px, var(--dsh-anchored-max-width, 100vw));
35
+ }
36
+
37
+ /* Legacy slots can render no rows; keep empty context popups out of layout. */
38
+ .list[data-context-menu]:not(:has([role='menuitem'])) {
39
+ display: none;
38
40
  }
39
41
 
40
42
  /* Portal mode: fixed in the viewport, coordinates supplied inline from the
@@ -60,12 +62,17 @@
60
62
  }
61
63
 
62
64
  /* Viewport fit: the card stops 12px short of the viewport's top/bottom edges
63
- * (24 = 2 × the portal MARGIN in Menu.tsx) and taller content scrolls inside
64
- * .viewport, so a pinned .footer stays visible. Menus with submenu rows skip
65
- * this class — the overflow clip would crop the side card, so they rely on
66
- * staying short. */
65
+ * (the portal MARGIN in Menu.tsx; the top side widens to the frame's published
66
+ * overlay inset) and taller content scrolls inside .viewport, so a pinned
67
+ * .footer stays visible. The dynamic unit, not `vh`: mobile browser chrome
68
+ * shrinks the visible height while `vh` keeps the taller bars-hidden height, so
69
+ * a `vh` cap exceeds the viewport the placement clamps into and the card ends
70
+ * up taller than the screen showing it. Menus with submenu rows skip this
71
+ * class — the overflow clip would crop the side card, so they rely on staying
72
+ * short. */
67
73
  .scrollable {
68
- max-height: calc(100vh - 24px);
74
+ max-height: min(calc(100vh - 12px - max(12px, var(--dsh-frame-overlay-top, 12px))), var(--dsh-anchored-max-height, 100vh));
75
+ max-height: min(calc(100dvh - 12px - max(12px, var(--dsh-frame-overlay-top, 12px))), var(--dsh-anchored-max-height, 100vh));
69
76
  }
70
77
 
71
78
  .viewport {
@@ -84,87 +91,94 @@
84
91
  flex: none;
85
92
  display: flex;
86
93
  flex-direction: column;
87
- margin-top: 4px;
88
- padding-top: 4px;
89
- border-top: 0.5px solid var(--qilin-alias-border-l2);
94
+ margin-top: 3px;
95
+ padding-top: 3px;
96
+ border-top: 0.5px solid var(--dsw-alias-border-l2);
97
+ }
98
+
99
+ .list[data-context-menu] .footer:has(> .separator:first-child) {
100
+ margin-top: 0;
101
+ padding-top: 0;
102
+ border-top: none;
90
103
  }
91
104
 
92
105
  .itemWrap {
93
106
  position: relative;
94
107
  }
95
108
 
96
- /* Menu cell (figma .Menu_cell): min-h 40, r10, pad 10/8, 14/22 primary,
97
- * gap 8 between leading icon / label / trailing check. */
109
+ /* Menu cell: compact text, icon, and selection spacing. */
98
110
  .item {
99
111
  display: flex;
100
112
  align-items: center;
101
- gap: 8px;
113
+ gap: 6px;
102
114
  width: 100%;
103
- min-height: 40px;
104
- padding: 8px 10px;
115
+ min-height: 34px;
116
+ padding: 6px 8px;
105
117
  border: none;
106
- border-radius: 10px;
118
+ border-radius: var(--dsw-radius-md);
107
119
  background: transparent;
108
120
  cursor: pointer;
109
- font-size: 14px;
110
- line-height: 22px;
111
- color: var(--qilin-alias-label-primary);
121
+ font-size: 13px;
122
+ line-height: 20px;
123
+ color: var(--dsw-alias-label-primary);
112
124
  text-align: left;
113
125
  }
114
126
 
115
- .item:hover:not(:disabled) {
116
- background: var(--qilin-alias-interactive-bg-hover);
127
+ @media (hover: hover) {
128
+ .item:hover:not(:disabled):not([aria-disabled='true']) {
129
+ background: var(--dsw-alias-interactive-bg-hover);
130
+ }
117
131
  }
118
132
 
119
133
  /* Arrow navigation moves real focus, so the row the keyboard is on carries the
120
134
  same fill the pointer gets: the fill is the row's focus indication, and the
121
135
  browser's default ring would double it. */
122
136
  .item:focus-visible:not(:disabled) {
123
- background: var(--qilin-alias-interactive-bg-hover);
137
+ background: var(--dsw-alias-interactive-bg-hover);
124
138
  outline: none;
125
139
  }
126
140
 
127
141
  .denseList .item {
128
- min-height: 34px;
129
- padding-block: 5px;
142
+ min-height: 30px;
143
+ padding-block: 4px;
130
144
  }
131
145
 
132
146
  .denseList .label {
133
- padding-block: 4px;
147
+ padding-block: 3px;
134
148
  }
135
149
 
136
150
  .list.compactList,
137
151
  .submenu.compactList {
138
- min-width: 164px;
139
- padding: 2px;
140
- border-radius: 7px;
152
+ min-width: min(156px, var(--dsh-anchored-max-width, 100vw));
153
+ padding: 4px;
141
154
  }
142
155
 
143
156
  .compactList .item {
144
- min-height: 26px;
145
- gap: 6px;
146
- padding: 3px 7px;
147
- border-radius: 5px;
148
- font-size: 12px;
149
- line-height: 18px;
157
+ min-height: 24px;
158
+ gap: 5px;
159
+ padding: 2px 6px;
160
+ border-radius: var(--dsw-radius-sm);
161
+ font-size: 11px;
162
+ line-height: 17px;
150
163
  }
151
164
 
152
165
  .compactList .itemIcon {
153
- width: 14px;
154
- height: 14px;
166
+ width: 12px;
167
+ height: 12px;
155
168
  }
156
169
 
157
170
  .compactList .separator {
158
- margin: 2px;
171
+ margin: 2px 6px;
159
172
  }
160
173
 
161
174
  .compactList .label {
162
- padding: 4px 7px;
163
- font-size: 11px;
164
- line-height: 16px;
175
+ padding: 3px 6px;
176
+ font-size: 10px;
177
+ line-height: 15px;
165
178
  }
166
179
 
167
- .item:disabled {
180
+ .item:disabled,
181
+ .item[aria-disabled='true'] {
168
182
  opacity: 0.4;
169
183
  cursor: not-allowed;
170
184
  }
@@ -172,11 +186,23 @@
172
186
  .itemIcon {
173
187
  display: inline-flex;
174
188
  flex: none;
175
- width: 16px;
176
- height: 16px;
189
+ width: 14px;
190
+ height: 14px;
177
191
  align-items: center;
178
192
  justify-content: center;
179
- color: var(--qilin-alias-label-tertiary);
193
+ color: var(--dsw-alias-menu-icon);
194
+ }
195
+
196
+ .itemIcon svg,
197
+ .check {
198
+ width: 14px;
199
+ height: 14px;
200
+ }
201
+
202
+ .compactList .itemIcon svg,
203
+ .compactList .check {
204
+ width: 12px;
205
+ height: 12px;
180
206
  }
181
207
 
182
208
  .itemLabel {
@@ -193,16 +219,20 @@
193
219
  }
194
220
 
195
221
  .shortcut .shortcutKeys {
196
- height: auto;
197
- padding: 0;
198
- border-radius: 0;
199
- background: transparent;
200
- color: var(--qilin-alias-label-tertiary);
222
+ color: var(--dsw-alias-label-caption);
223
+ font-size: 11px;
224
+ line-height: 16px;
225
+ }
226
+
227
+ @media (pointer: coarse) {
228
+ :global(html:not([data-input-modality='keyboard'])) .shortcut {
229
+ display: none;
230
+ }
201
231
  }
202
232
 
203
233
  .check {
204
234
  flex: none;
205
- color: var(--qilin-alias-label-primary);
235
+ color: var(--dsw-alias-label-primary);
206
236
  }
207
237
 
208
238
  /* Selected cell keeps the plain fill (marker is the trailing check); the
@@ -213,48 +243,60 @@
213
243
 
214
244
  /* Fill-mode selection: the row holds the hover fill instead of a check. */
215
245
  .selectedFill {
216
- background: var(--qilin-alias-interactive-bg-hover);
246
+ background: var(--dsw-alias-interactive-bg-hover);
217
247
  }
218
248
 
219
249
  /* Destructive row: error text/icon, danger hover fill. */
220
250
  .danger {
221
- color: var(--qilin-alias-state-error-primary);
251
+ color: var(--dsw-alias-state-error-primary);
222
252
  }
223
253
 
224
254
  .danger .itemIcon {
225
- color: var(--qilin-alias-state-error-primary);
255
+ color: var(--dsw-alias-state-error-primary);
226
256
  }
227
257
 
228
- .danger:hover:not(:disabled) {
229
- background: var(--qilin-alias-interactive-bg-hover-danger);
258
+ @media (hover: hover) {
259
+ .danger:hover:not(:disabled):not([aria-disabled='true']) {
260
+ background: var(--dsw-alias-interactive-bg-hover-danger);
261
+ }
230
262
  }
231
263
 
232
264
  .danger:focus-visible:not(:disabled) {
233
- background: var(--qilin-alias-interactive-bg-hover-danger);
265
+ background: var(--dsw-alias-interactive-bg-hover-danger);
234
266
  outline: none;
235
267
  }
236
268
 
237
269
  /* Heading row: non-interactive small grey text, padding aligned with items. */
238
270
  .label {
239
- padding: 8px 10px;
240
- font-size: 12px;
241
- line-height: 16px;
242
- color: var(--qilin-alias-label-tertiary);
271
+ padding: 6px 8px;
272
+ font-size: 11px;
273
+ line-height: 15px;
274
+ color: var(--dsw-alias-label-tertiary);
243
275
  }
244
276
 
245
- /* Separator cell (figma 122:9481): py 4 / px 2 around the hairline. */
277
+ /* Separator endpoints align with the row content. The l2 stroke stays visible
278
+ * on the menu surface. */
246
279
  .separator {
247
280
  height: 0.5px;
248
- margin: 4px 2px;
249
- background: var(--qilin-alias-border-l1);
281
+ margin: 3px 8px;
282
+ background: var(--dsw-alias-border-l2);
283
+ }
284
+
285
+ /* A data separator directly followed by a row that starts a group draws one
286
+ * line, not two; a group start on the list's first row draws none. */
287
+ .separator + .separator,
288
+ .separator + .itemWrap > .separator:first-child,
289
+ .itemWrap:first-child > .separator:first-child {
290
+ display: none;
250
291
  }
251
292
 
252
- /* Nested card to the right of the parent row (figma 419:16920).
253
- * Bottom-aligned with the parent menu card (grows upward): itemWrap sits in
254
- * .list's 4px pad, so bottom: -4px matches the list's outer bottom edge.
255
- * Horizontal: list pad (4px) + 6px card gap = 10px past itemWrap — plain
256
- * `100% + 6px` collapses to ~2px between outer card edges.
257
- * ::before bridges the full gap so the pointer can cross without mouseLeave. */
293
+ /* A legacy outlet following structured rows is not the beginning of the menu. */
294
+ .list[data-context-menu] .viewport > .itemWrap ~ [data-slot] .itemWrap:first-child > .separator:first-child {
295
+ display: block;
296
+ }
297
+
298
+ /* The submenu aligns with the parent's bottom edge. Its hit bridge covers
299
+ * the menu inset and the 6px gap so pointer movement keeps it open. */
258
300
  .submenu {
259
301
  position: absolute;
260
302
  top: auto;
@@ -272,3 +314,10 @@
272
314
  left: -10px;
273
315
  width: 10px;
274
316
  }
317
+
318
+ .list[data-menu-phase='dismiss'],
319
+ .list[data-menu-phase='replace'] { pointer-events: none; }
320
+ .list[data-context-menu].scrollable {
321
+ max-height: min(calc(100vh - 24px), var(--dsh-anchored-max-height, 100vh));
322
+ max-height: min(calc(100dvh - 24px), var(--dsh-anchored-max-height, 100vh));
323
+ }
@@ -1,10 +1,10 @@
1
1
  .surface,
2
2
  .backing {
3
- border-radius: var(--qilin-radius-lg);
3
+ border-radius: var(--dsw-radius-lg);
4
4
  }
5
5
 
6
6
  .compact {
7
- border-radius: var(--qilin-radius-md);
7
+ border-radius: var(--dsw-radius-md);
8
8
  }
9
9
 
10
10
  :where(.surface) {
@@ -12,7 +12,7 @@
12
12
  }
13
13
 
14
14
  .surface {
15
- anchor-name: var(--qilin-menu-anchor);
15
+ anchor-name: var(--dsh-menu-anchor);
16
16
  isolation: isolate;
17
17
  }
18
18
 
@@ -23,8 +23,8 @@
23
23
  inset: 0;
24
24
  z-index: -1;
25
25
  border-radius: inherit;
26
- background: var(--qilin-menu-surface-fill, var(--qilin-specific-menu, var(--qilin-alias-bg-layer-2)));
27
- backdrop-filter: var(--qilin-menu-backdrop-filter);
26
+ background: var(--dsw-menu-surface-fill);
27
+ backdrop-filter: var(--dsw-menu-backdrop-filter);
28
28
  pointer-events: none;
29
29
  }
30
30
 
@@ -38,12 +38,28 @@
38
38
  :global(html[data-platform='darwin']) .backing {
39
39
  display: block;
40
40
  position: fixed;
41
- position-anchor: var(--qilin-menu-anchor);
41
+ position-anchor: var(--dsh-menu-anchor);
42
42
  top: anchor(top);
43
43
  left: anchor(left);
44
44
  width: anchor-size(width, 0px);
45
45
  height: anchor-size(height, 0px);
46
46
  z-index: -1;
47
- background: var(--qilin-alias-bg-base);
47
+ background: var(--dsw-alias-bg-base);
48
48
  pointer-events: none;
49
49
  }
50
+
51
+ /* Opacity on the container would prevent its material from blurring the page.
52
+ * Enter animations release their compositing layers so nested menus can blur too. */
53
+ .surface[data-menu-phase='enter'] > *,
54
+ .backing[data-menu-phase='enter'] { animation: dsh-context-menu-in var(--dsh-menu-enter) ease-out; }
55
+ .surface[data-menu-phase='dismiss'] > *,
56
+ .backing[data-menu-phase='dismiss'],
57
+ .surface[data-menu-phase='replace'] > *,
58
+ .backing[data-menu-phase='replace'] { animation: dsh-context-menu-out var(--dsh-menu-dismiss) ease-in both; }
59
+ .surface[data-menu-phase='replace'] > *,
60
+ .backing[data-menu-phase='replace'] { animation-duration: var(--dsh-menu-replace); }
61
+ @keyframes dsh-context-menu-in { from { opacity: 0; } to { opacity: 1; } }
62
+ @keyframes dsh-context-menu-out { from { opacity: 1; } to { opacity: 0; } }
63
+ @media (prefers-reduced-motion: reduce) {
64
+ .surface[data-menu-phase] > *, .backing[data-menu-phase] { animation-duration: 0ms; }
65
+ }
@@ -0,0 +1,89 @@
1
+ /* One quoted excerpt inside sent text. Inline so it flows in the consumer's
2
+ own line box; the excerpt keeps one line and the full text rides the hover
3
+ card. Colour is the business accent, matching reference chips. */
4
+
5
+ .quote {
6
+ display: inline-flex;
7
+ align-items: center;
8
+ gap: 4px;
9
+ box-sizing: border-box;
10
+ max-width: 100%;
11
+ margin: 0;
12
+ padding: 0 4px 0 0;
13
+ border-left: 2px solid var(--qilin-alias-state-business-primary);
14
+ border-radius: 2px;
15
+ vertical-align: baseline;
16
+ color: var(--qilin-alias-state-business-primary);
17
+ font-size: inherit;
18
+ line-height: inherit;
19
+ }
20
+
21
+ .popoverText {
22
+ display: block;
23
+ padding: 8px 10px;
24
+ color: var(--qilin-alias-label-primary);
25
+ font-size: 14px;
26
+ line-height: 20px;
27
+ overflow-wrap: anywhere;
28
+ white-space: pre-wrap;
29
+ }
30
+
31
+ .source {
32
+ min-width: 0;
33
+ padding-left: 4px;
34
+ color: inherit;
35
+ font: inherit;
36
+ line-height: inherit;
37
+ text-align: left;
38
+ overflow: hidden;
39
+ text-overflow: ellipsis;
40
+ white-space: nowrap;
41
+ }
42
+
43
+ .marker {
44
+ display: grid;
45
+ place-items: center;
46
+ flex: none;
47
+ width: 16px;
48
+ height: 16px;
49
+ opacity: 0.7;
50
+ pointer-events: none;
51
+ user-select: none;
52
+ }
53
+
54
+ .quoteMark {
55
+ width: 16px;
56
+ height: 16px;
57
+ }
58
+
59
+ .action {
60
+ display: grid;
61
+ place-items: center;
62
+ flex: none;
63
+ width: 18px;
64
+ height: 18px;
65
+ padding: 0;
66
+ border: none;
67
+ border-radius: 4px;
68
+ background: transparent;
69
+ color: inherit;
70
+ opacity: 0;
71
+ cursor: pointer;
72
+ transition: opacity 120ms ease;
73
+ }
74
+
75
+ .action:focus-visible {
76
+ opacity: 1;
77
+ }
78
+
79
+ @media (hover: hover) {
80
+ .quote:hover .action {
81
+ opacity: 1;
82
+ }
83
+ }
84
+
85
+ @media (hover: none) {
86
+ .action {
87
+ opacity: 1;
88
+ }
89
+ }
@@ -0,0 +1,12 @@
1
+ .preview {
2
+ position: fixed;
3
+ top: 0;
4
+ pointer-events: none;
5
+ }
6
+
7
+ .preview > :first-child {
8
+ box-sizing: border-box;
9
+ margin: 0;
10
+ width: 100%;
11
+ height: 100%;
12
+ }
@@ -1,4 +1,7 @@
1
1
  .tabs {
2
+ /* The indicator's inline width subtracts this inset twice; it matches the
3
+ track's own padding so the sliding pill spans the padded content box. */
4
+ --segmented-tabs-inset: 4px;
2
5
  position: relative;
3
6
  display: grid;
4
7
  padding: 4px;
@@ -10,7 +13,7 @@
10
13
  position: absolute;
11
14
  inset: 4px auto 4px 4px;
12
15
  box-sizing: border-box;
13
- border: 0.5px solid var(--qilin-alias-border-l3);
16
+ border: 1px solid var(--qilin-alias-border-l3);
14
17
  border-radius: var(--qilin-radius-md);
15
18
  background: var(--qilin-alias-bg-layer-3);
16
19
  transition: transform 180ms ease;
@@ -14,7 +14,7 @@
14
14
  }
15
15
 
16
16
  .tag[data-tone='outline'] {
17
- border: 0.5px solid var(--qilin-alias-border-l4);
17
+ border: 1px solid var(--qilin-alias-border-l4);
18
18
  color: var(--qilin-alias-label-tertiary);
19
19
  }
20
20
 
@@ -62,7 +62,7 @@
62
62
  divider the IN/OUT card draws between its sections. A banner-only card (a
63
63
  running command that has printed nothing yet) draws none. */
64
64
  .block[data-body] .header {
65
- border-bottom: 0.5px solid var(--qilin-alias-border-l2);
65
+ border-bottom: 1px solid var(--qilin-alias-border-l2);
66
66
  }
67
67
 
68
68
  /* One row per command line. The prompt column is the only element allowed to
@@ -49,6 +49,31 @@
49
49
  min-width: 0;
50
50
  }
51
51
 
52
+ /* The action text is the one interactive part: the banner surface stays
53
+ click-through, so only this text takes the pointer. deepseek-400 mixed
54
+ toward the toast's own label color keeps the blue legible on the contrast
55
+ surface, which flips with the theme. */
56
+ .action {
57
+ display: inline;
58
+ padding: 0;
59
+ border: 0;
60
+ background: none;
61
+ color: color-mix(in srgb, var(--qilin-static-deepseek-400) 78%, var(--qilin-alias-label-primary-inverted));
62
+ font: inherit;
63
+ cursor: pointer;
64
+ /* Breathing room where the action text meets the surrounding copy. */
65
+ margin-inline: 3px;
66
+ /* The banner surface stays click-through (pointer-events: none above); only
67
+ its action text takes the pointer. */
68
+ pointer-events: auto;
69
+ }
70
+
71
+ @media (hover: hover) {
72
+ .action:hover {
73
+ opacity: 0.8;
74
+ }
75
+ }
76
+
52
77
  @keyframes qilin-toast-in {
53
78
  from {
54
79
  opacity: 0;
@@ -61,9 +86,12 @@
61
86
  }
62
87
  }
63
88
 
89
+ /* The fade also hides the banner itself, so a fully faded action takes no
90
+ input. */
64
91
  @keyframes qilin-toast-fade {
65
92
  to {
66
93
  opacity: 0;
94
+ visibility: hidden;
67
95
  }
68
96
  }
69
97
 
@@ -0,0 +1,11 @@
1
+ .bubble {
2
+ max-width: 100%;
3
+ background: var(--qilin-specific-bubble);
4
+ border-radius: 22px;
5
+ padding: 10px 16px;
6
+ font-size: var(--qilin-content-font-size, 14px);
7
+ line-height: calc(22px + var(--qilin-content-font-delta, 0px));
8
+ color: var(--qilin-alias-label-primary);
9
+ white-space: pre-wrap;
10
+ word-break: break-word;
11
+ }