@vobs/ui 0.3.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +60 -0
  3. package/package.json +52 -110
  4. package/src/activity-rail.ts +125 -0
  5. package/src/alert.ts +65 -0
  6. package/src/assets/icons/alert-circle.svg +1 -0
  7. package/src/assets/icons/alert-triangle.svg +1 -0
  8. package/src/assets/icons/alert.d27e24.svg +1 -0
  9. package/src/assets/icons/alert.svg +1 -0
  10. package/src/assets/icons/apple.svg +1 -0
  11. package/src/assets/icons/arrow-down.svg +1 -0
  12. package/src/assets/icons/arrow-expand.svg +1 -0
  13. package/src/assets/icons/arrow-left.svg +1 -0
  14. package/src/assets/icons/arrow-minimize.svg +1 -0
  15. package/src/assets/icons/arrow-right-to-line.svg +1 -0
  16. package/src/assets/icons/arrow-right.svg +1 -0
  17. package/src/assets/icons/arrow-up-right.svg +1 -0
  18. package/src/assets/icons/arrow-up.0c0c0d.svg +1 -0
  19. package/src/assets/icons/arrow-up.svg +1 -0
  20. package/src/assets/icons/at.svg +1 -0
  21. package/src/assets/icons/atom.0c0c0d.svg +1 -0
  22. package/src/assets/icons/atom.svg +1 -0
  23. package/src/assets/icons/bar.svg +1 -0
  24. package/src/assets/icons/bell.svg +1 -0
  25. package/src/assets/icons/box.svg +1 -0
  26. package/src/assets/icons/bug.svg +1 -0
  27. package/src/assets/icons/calendar.svg +1 -0
  28. package/src/assets/icons/check-circle.33c192.svg +1 -0
  29. package/src/assets/icons/check-circle.svg +1 -0
  30. package/src/assets/icons/check.d1d3db.svg +1 -0
  31. package/src/assets/icons/check.svg +1 -0
  32. package/src/assets/icons/chevron-down.svg +1 -0
  33. package/src/assets/icons/chevron-right.svg +1 -0
  34. package/src/assets/icons/chevron-up.svg +1 -0
  35. package/src/assets/icons/circle.svg +1 -0
  36. package/src/assets/icons/clock.svg +1 -0
  37. package/src/assets/icons/cmd.svg +1 -0
  38. package/src/assets/icons/code.svg +1 -0
  39. package/src/assets/icons/columns.svg +1 -0
  40. package/src/assets/icons/copy.svg +1 -0
  41. package/src/assets/icons/cpu.svg +1 -0
  42. package/src/assets/icons/dashboard.0c0c0d.svg +1 -0
  43. package/src/assets/icons/dashboard.svg +1 -0
  44. package/src/assets/icons/dollar.svg +1 -0
  45. package/src/assets/icons/download.svg +1 -0
  46. package/src/assets/icons/edit.svg +1 -0
  47. package/src/assets/icons/extensions.svg +1 -0
  48. package/src/assets/icons/external.svg +1 -0
  49. package/src/assets/icons/eye.svg +1 -0
  50. package/src/assets/icons/file-text.svg +1 -0
  51. package/src/assets/icons/file.svg +1 -0
  52. package/src/assets/icons/files.svg +1 -0
  53. package/src/assets/icons/filter.svg +1 -0
  54. package/src/assets/icons/folder.svg +1 -0
  55. package/src/assets/icons/gear.svg +1 -0
  56. package/src/assets/icons/git.svg +1 -0
  57. package/src/assets/icons/github.svg +1 -0
  58. package/src/assets/icons/globe.svg +1 -0
  59. package/src/assets/icons/grid-2x2.svg +1 -0
  60. package/src/assets/icons/hash.svg +1 -0
  61. package/src/assets/icons/heart.svg +1 -0
  62. package/src/assets/icons/help.svg +1 -0
  63. package/src/assets/icons/home.svg +1 -0
  64. package/src/assets/icons/image.svg +1 -0
  65. package/src/assets/icons/info-circle.svg +1 -0
  66. package/src/assets/icons/info-square.svg +1 -0
  67. package/src/assets/icons/info.387bff.svg +1 -0
  68. package/src/assets/icons/info.svg +1 -0
  69. package/src/assets/icons/key.svg +1 -0
  70. package/src/assets/icons/layers.svg +1 -0
  71. package/src/assets/icons/layout.svg +1 -0
  72. package/src/assets/icons/link.svg +1 -0
  73. package/src/assets/icons/list.svg +1 -0
  74. package/src/assets/icons/lock.svg +1 -0
  75. package/src/assets/icons/log-out.svg +1 -0
  76. package/src/assets/icons/logo.svg +1 -0
  77. package/src/assets/icons/mail.svg +1 -0
  78. package/src/assets/icons/menu.svg +1 -0
  79. package/src/assets/icons/message-circle.svg +1 -0
  80. package/src/assets/icons/message-plus.svg +1 -0
  81. package/src/assets/icons/message-square.svg +1 -0
  82. package/src/assets/icons/mic.svg +1 -0
  83. package/src/assets/icons/minus.svg +1 -0
  84. package/src/assets/icons/moon.svg +1 -0
  85. package/src/assets/icons/more-h.svg +1 -0
  86. package/src/assets/icons/mouse.svg +1 -0
  87. package/src/assets/icons/palette.svg +1 -0
  88. package/src/assets/icons/panel-bottom.svg +1 -0
  89. package/src/assets/icons/pause.svg +1 -0
  90. package/src/assets/icons/play.svg +1 -0
  91. package/src/assets/icons/plug.svg +1 -0
  92. package/src/assets/icons/plus.0c0c0d.svg +1 -0
  93. package/src/assets/icons/plus.svg +1 -0
  94. package/src/assets/icons/refresh.svg +1 -0
  95. package/src/assets/icons/ruler.svg +1 -0
  96. package/src/assets/icons/scroll-text.svg +1 -0
  97. package/src/assets/icons/search-menu.svg +1 -0
  98. package/src/assets/icons/search.svg +1 -0
  99. package/src/assets/icons/send.0c0c0d.svg +1 -0
  100. package/src/assets/icons/send.svg +1 -0
  101. package/src/assets/icons/settings.0c0c0d.svg +1 -0
  102. package/src/assets/icons/settings.svg +1 -0
  103. package/src/assets/icons/shield.svg +1 -0
  104. package/src/assets/icons/sidebar-left.svg +1 -0
  105. package/src/assets/icons/sidebar-right.svg +1 -0
  106. package/src/assets/icons/sliders.svg +1 -0
  107. package/src/assets/icons/sparkles.32f08c.svg +1 -0
  108. package/src/assets/icons/sparkles.svg +1 -0
  109. package/src/assets/icons/square.svg +1 -0
  110. package/src/assets/icons/star.svg +1 -0
  111. package/src/assets/icons/sun.svg +1 -0
  112. package/src/assets/icons/terminal.svg +1 -0
  113. package/src/assets/icons/trash.svg +1 -0
  114. package/src/assets/icons/trending-down.svg +1 -0
  115. package/src/assets/icons/trending-up.svg +1 -0
  116. package/src/assets/icons/type.svg +1 -0
  117. package/src/assets/icons/upload.svg +1 -0
  118. package/src/assets/icons/user-circle.svg +1 -0
  119. package/src/assets/icons/user.svg +1 -0
  120. package/src/assets/icons/users.svg +1 -0
  121. package/src/assets/icons/wrench.svg +1 -0
  122. package/src/assets/icons/x-circle.svg +1 -0
  123. package/src/assets/icons/x.svg +1 -0
  124. package/src/assets/icons/zap.0c0c0d.svg +1 -0
  125. package/src/assets/icons/zap.svg +1 -0
  126. package/src/avatar.ts +64 -0
  127. package/src/button.ts +88 -0
  128. package/src/card.ts +59 -0
  129. package/src/chat-composer.ts +156 -0
  130. package/src/dialog.ts +127 -0
  131. package/src/drawer.ts +131 -0
  132. package/src/editor-tabs.ts +184 -0
  133. package/src/file-tree.ts +214 -0
  134. package/src/forms.ts +270 -0
  135. package/src/icon.ts +117 -0
  136. package/src/icons.js +165 -0
  137. package/src/index.test.ts +213 -0
  138. package/src/index.ts +94 -0
  139. package/src/kbd.ts +72 -0
  140. package/src/menu.ts +119 -0
  141. package/src/nav-list.ts +157 -0
  142. package/src/overlay.test.ts +66 -0
  143. package/src/overlay.ts +205 -0
  144. package/src/page-header.ts +97 -0
  145. package/src/pagination.ts +163 -0
  146. package/src/second-batch.test.ts +205 -0
  147. package/src/stat-card.ts +92 -0
  148. package/src/status-bar.ts +108 -0
  149. package/src/styles/base.css +240 -0
  150. package/src/styles/components.css +1234 -0
  151. package/{dist → src/styles}/styles.css +2 -0
  152. package/src/styles/toast.css +57 -0
  153. package/src/styles/tokens.css +438 -0
  154. package/src/styles/vui.css +47 -0
  155. package/src/table.ts +191 -0
  156. package/src/tabs.ts +100 -0
  157. package/src/tag.ts +33 -0
  158. package/src/third-batch.test.ts +137 -0
  159. package/src/toast.test.ts +55 -0
  160. package/src/toast.ts +79 -0
  161. package/src/types.ts +25 -0
  162. package/src/utils.ts +221 -0
  163. package/src/workbench-titlebar.ts +137 -0
  164. package/dist/app-shell-dom.d.ts +0 -30
  165. package/dist/app-shell-dom.js +0 -913
  166. package/dist/app-shell.d.ts +0 -235
  167. package/dist/app-shell.js +0 -415
  168. package/dist/base.css +0 -51
  169. package/dist/calendar-picker.d.ts +0 -45
  170. package/dist/calendar-picker.js +0 -217
  171. package/dist/calendar.d.ts +0 -95
  172. package/dist/calendar.js +0 -194
  173. package/dist/complex-inputs.d.ts +0 -300
  174. package/dist/complex-inputs.js +0 -866
  175. package/dist/components.css +0 -4756
  176. package/dist/data-display.d.ts +0 -1094
  177. package/dist/data-display.js +0 -2341
  178. package/dist/dialog.d.ts +0 -26
  179. package/dist/dialog.js +0 -94
  180. package/dist/feedback.d.ts +0 -88
  181. package/dist/feedback.js +0 -281
  182. package/dist/forms.d.ts +0 -378
  183. package/dist/forms.js +0 -1019
  184. package/dist/icons-config.d.ts +0 -25
  185. package/dist/icons-config.js +0 -14
  186. package/dist/icons.d.ts +0 -41
  187. package/dist/icons.js +0 -185
  188. package/dist/index.d.ts +0 -31
  189. package/dist/index.js +0 -40
  190. package/dist/locale/en-US.d.ts +0 -31
  191. package/dist/locale/en-US.js +0 -20
  192. package/dist/locale/zh-CN.d.ts +0 -31
  193. package/dist/locale/zh-CN.js +0 -20
  194. package/dist/navigation.d.ts +0 -609
  195. package/dist/navigation.js +0 -1707
  196. package/dist/overlay.d.ts +0 -308
  197. package/dist/overlay.js +0 -974
  198. package/dist/page-state.d.ts +0 -23
  199. package/dist/page-state.js +0 -96
  200. package/dist/primitives.d.ts +0 -268
  201. package/dist/primitives.js +0 -764
  202. package/dist/theme-data.d.ts +0 -394
  203. package/dist/theme-data.js +0 -417
  204. package/dist/theme.d.ts +0 -97
  205. package/dist/theme.js +0 -301
  206. package/dist/tokens.css +0 -570
  207. package/dist/tree.d.ts +0 -144
  208. package/dist/tree.js +0 -475
  209. package/dist/virtual-list.d.ts +0 -190
  210. package/dist/virtual-list.js +0 -538
  211. package/dist/workbench.d.ts +0 -136
  212. package/dist/workbench.js +0 -156
@@ -0,0 +1,1234 @@
1
+ /* ═══════════════════════════════════════════════════════════════
2
+ components.css — Design System Component Definitions
3
+ Auto-extracted from preview/component-*.html
4
+ DO NOT EDIT MANUALLY — regenerate via extract-components-css.mjs
5
+ ═══════════════════════════════════════════════════════════════ */
6
+
7
+ /* ── Activity Rail ─────────────────────────────────────────── */
8
+ /* ===== Activity Rail ===== */
9
+ .vui-activityrail {
10
+ display: inline-flex;
11
+ flex-direction: column;
12
+ align-items: center;
13
+ gap: var(--spacer-4);
14
+ width: 48px;
15
+ padding: var(--spacer-8) 0;
16
+ background: var(--bg-base-secondary);
17
+ border-right: 1px solid var(--border-neutral-l1);
18
+ }
19
+ .vui-activityrail__btn {
20
+ position: relative;
21
+ display: inline-flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ width: 36px;
25
+ height: 36px;
26
+ border-radius: var(--radius-8);
27
+ background: transparent;
28
+ border: none;
29
+ cursor: pointer;
30
+ }
31
+ .vui-activityrail__btn:hover { background: var(--bg-overlay-l2); }
32
+ .vui-activityrail__btn.is-active {
33
+ background: var(--bg-overlay-l3);
34
+ }
35
+ .vui-activityrail__btn.is-active::before {
36
+ content: "";
37
+ position: absolute;
38
+ left: -8px;
39
+ top: 50%;
40
+ transform: translateY(-50%);
41
+ width: 3px;
42
+ height: 20px;
43
+ border-radius: var(--radius-full);
44
+ background: var(--bg-brand);
45
+ }
46
+ .vui-activityrail__divider {
47
+ width: 24px;
48
+ height: 1px;
49
+ margin: var(--spacer-4) 0;
50
+ background: var(--border-neutral-l2);
51
+ }
52
+ .vui-activityrail__spacer { flex: 1; }
53
+
54
+ /* ── Alert ─────────────────────────────────────────────────── */
55
+ /* ===== Alert / Banner ===== */
56
+ .vui-alert {
57
+ display: flex; gap: var(--spacer-12);
58
+ padding: var(--spacer-12) var(--spacer-16);
59
+ background: var(--bg-base-secondary);
60
+ border: 1px solid var(--border-neutral-l1);
61
+ border-radius: var(--radius-4);
62
+ color: var(--text-default);
63
+ align-items: flex-start;
64
+ }
65
+ .vui-alert__icon { flex-shrink: 0; margin-top: 1px; color: var(--icon-default); display: inline-flex; }
66
+ .vui-alert--success .vui-alert__icon { color: var(--status-success-default); }
67
+ .vui-alert--warning .vui-alert__icon { color: var(--status-warning-default); }
68
+ .vui-alert--danger .vui-alert__icon { color: var(--status-error-default); }
69
+ .vui-alert--info .vui-alert__icon { color: var(--status-primary-default); }
70
+ .vui-alert__title { font-weight: var(--font-weight-medium, 500); margin-bottom: var(--spacer-4); }
71
+ .vui-alert__desc { color: var(--text-secondary); font-size: var(--body-sm-font-size); }
72
+
73
+ /* ── Avatar ────────────────────────────────────────────────── */
74
+ /* ===== Avatar ===== */
75
+ .vui-avatar {
76
+ display: inline-flex; align-items: center; justify-content: center;
77
+ width: 32px; height: 32px; border-radius: 50%;
78
+ background: var(--bg-overlay-l3);
79
+ color: var(--text-default);
80
+ font-size: var(--body-sm-font-size); font-weight: var(--font-weight-strong);
81
+ flex-shrink: 0;
82
+ }
83
+ .vui-avatar--sm { width: 24px; height: 24px; font-size: var(--body-xs-font-size); }
84
+ .vui-avatar--lg { width: 40px; height: 40px; }
85
+ .vui-avatar--square { border-radius: var(--radius-4); }
86
+ .vui-avatar--accent { background: var(--bg-brand); color: var(--text-onbrand); }
87
+
88
+ /* ── Buttons ───────────────────────────────────────────────── */
89
+ /* ===== Buttons =====
90
+ * Hierarchy (one BRAND/green CTA per page max):
91
+ * primary → high-contrast neutral (white). Default "main action".
92
+ * secondary→ neutral surface, used for any neighbour action.
93
+ * tertiary → ghost (no fill, no border), used for dense toolbars / inline.
94
+ * danger / danger-subtle / warning / brand / link.
95
+ * Sizes follow the Figma "Sm / Default / Large": 24 / 28 / 32 / 36
96
+ * Geek hard-edged — radii are tight (4 / 6 max).
97
+ */
98
+ .vui-btn {
99
+ display: inline-flex; align-items: center; justify-content: center;
100
+ gap: var(--spacer-6);
101
+ padding: 0 var(--spacer-12); height: 28px;
102
+ border-radius: var(--radius-4);
103
+ border: 1px solid transparent;
104
+ cursor: pointer; user-select: none; white-space: nowrap;
105
+ font-family: var(--body-sm-strong-font-family);
106
+ font-size: var(--body-sm-strong-font-size);
107
+ line-height: 1;
108
+ font-weight: var(--body-sm-strong-font-weight);
109
+ transition: background .12s ease, color .12s ease, border-color .12s ease, opacity .12s ease;
110
+ }
111
+ .vui-btn .icon { color: currentColor; }
112
+ .vui-btn--sm { height: 24px; padding: 0 var(--spacer-8); border-radius: var(--radius-4); font-size: var(--body-xs-font-size); }
113
+ .vui-btn--md { height: 28px; padding: 0 var(--spacer-12); border-radius: var(--radius-4); }
114
+ .vui-btn--lg { height: 32px; padding: 0 var(--spacer-16); border-radius: var(--radius-4); font-size: var(--body-base-font-size); }
115
+
116
+ /* PRIMARY = high-contrast neutral (white). Use this when you don't need
117
+ * brand emphasis. Page rule: at most ONE brand button per page; every other
118
+ * "main" action falls back to this neutral primary. */
119
+ .vui-btn--primary { background: var(--text-default); color: var(--bg-base-default); border-color: var(--text-default); }
120
+ .vui-btn--primary:hover { background: var(--text-default-hover); }
121
+ .vui-btn--primary:active { background: var(--text-secondary); border-color: var(--text-secondary); }
122
+ .vui-btn--primary[disabled] { background: var(--bg-overlay-l3); color: var(--text-tertiary); border-color: var(--border-neutral-l1); cursor: not-allowed; }
123
+
124
+ /* SECONDARY = neutral surface */
125
+ .vui-btn--secondary { background: var(--bg-overlay-l2); color: var(--text-default); border-color: var(--border-neutral-l1); }
126
+ .vui-btn--secondary:hover { background: var(--bg-overlay-l3); border-color: var(--border-neutral-l2); }
127
+ .vui-btn--secondary:active { background: var(--bg-overlay-l1); }
128
+ .vui-btn--secondary[disabled] { color: var(--text-tertiary); cursor: not-allowed; opacity: .6; }
129
+
130
+ /* TERTIARY = ghost (no border, no fill) */
131
+ .vui-btn--tertiary,
132
+ .vui-btn--ghost { background: transparent; color: var(--text-default); border-color: transparent; }
133
+ .vui-btn--tertiary:hover,
134
+ .vui-btn--ghost:hover { background: var(--bg-overlay-l2); }
135
+ .vui-btn--tertiary:active,
136
+ .vui-btn--ghost:active { background: var(--bg-overlay-l1); }
137
+ .vui-btn--tertiary[disabled],
138
+ .vui-btn--ghost[disabled] { color: var(--text-tertiary); cursor: not-allowed; }
139
+
140
+ /* DANGER strong (filled) */
141
+ .vui-btn--danger,
142
+ .vui-btn--danger-strong {
143
+ background: var(--status-error-default); color: var(--special-white);
144
+ border-color: var(--status-error-default);
145
+ }
146
+ .vui-btn--danger:hover,
147
+ .vui-btn--danger-strong:hover { filter: brightness(0.92); }
148
+ .vui-btn--danger:active,
149
+ .vui-btn--danger-strong:active { filter: brightness(0.84); }
150
+ .vui-btn--danger[disabled],
151
+ .vui-btn--danger-strong[disabled] { opacity: .5; cursor: not-allowed; }
152
+
153
+ /* DANGER subtle (tinted surface) */
154
+ .vui-btn--danger-subtle {
155
+ background: var(--status-error-surface-l1);
156
+ color: var(--status-error-default);
157
+ border-color: var(--status-error-surface-l2);
158
+ }
159
+ .vui-btn--danger-subtle:hover { background: var(--status-error-surface-l2); }
160
+ .vui-btn--danger-subtle:active { background: var(--status-error-surface-l1); }
161
+ .vui-btn--danger-subtle[disabled] { opacity: .5; cursor: not-allowed; }
162
+
163
+ /* WARNING */
164
+ .vui-btn--warning {
165
+ background: var(--status-warning-default); color: var(--text-onbrand);
166
+ border-color: var(--status-warning-default);
167
+ }
168
+ .vui-btn--warning:hover { filter: brightness(0.94); }
169
+ .vui-btn--warning:active { filter: brightness(0.86); }
170
+ .vui-btn--warning[disabled] { opacity: .5; cursor: not-allowed; }
171
+
172
+ /* BRAND — exactly the Figma "Brand" row. Use AT MOST one per page. */
173
+ .vui-btn--brand { background: var(--bg-brand); color: var(--text-onbrand); border-color: var(--bg-brand); }
174
+ .vui-btn--brand:hover { background: var(--bg-brand-hover); border-color: var(--bg-brand-hover); }
175
+ .vui-btn--brand:active { filter: brightness(0.92); }
176
+ .vui-btn--brand[disabled] { background: var(--bg-brand-disabled, var(--bg-overlay-l3)); border-color: transparent; color: var(--text-tertiary); cursor: not-allowed; }
177
+
178
+ /* LINK */
179
+ .vui-btn--link { background: transparent; color: var(--text-default); border-color: transparent; padding: 0; height: auto; }
180
+ .vui-btn--link:hover { color: var(--text-default-hover); text-decoration: underline; }
181
+ .vui-btn--link[disabled] { color: var(--text-tertiary); text-decoration: none; cursor: not-allowed; }
182
+
183
+ /* ICON BUTTON — square */
184
+ .vui-btn--icon { padding: 0; width: 28px; justify-content: center; }
185
+ .vui-btn--icon.vui-btn--sm { width: 24px; }
186
+ .vui-btn--icon.vui-btn--lg { width: 32px; }
187
+
188
+ /* BUTTON GROUP — segmented, hard edged */
189
+ .vui-btn-group { display: inline-flex; }
190
+ .vui-btn-group > .vui-btn { border-radius: 0; margin-left: -1px; }
191
+ .vui-btn-group > .vui-btn:first-child { border-top-left-radius: var(--radius-4); border-bottom-left-radius: var(--radius-4); margin-left: 0; }
192
+ .vui-btn-group > .vui-btn:last-child { border-top-right-radius: var(--radius-4); border-bottom-right-radius: var(--radius-4); }
193
+
194
+ /* ── Cards ─────────────────────────────────────────────────── */
195
+ /* ===== Card ===== */
196
+ .vui-card {
197
+ background: var(--bg-base-secondary);
198
+ border: 1px solid var(--border-neutral-l1);
199
+ border-radius: var(--radius-4);
200
+ padding: var(--spacer-24);
201
+ }
202
+ .vui-card__title { font-size: var(--heading-xs-font-size); line-height: var(--heading-xs-line-height); font-weight: var(--heading-xs-font-weight); margin-bottom: var(--spacer-8); }
203
+ .vui-card__desc { font-size: var(--body-sm-font-size); line-height: var(--body-sm-line-height); color: var(--text-secondary); }
204
+
205
+ /* ── Chat Composer ─────────────────────────────────────────── */
206
+ /* ===== Chat Composer ===== */
207
+ .vui-composer {
208
+ display: flex;
209
+ flex-direction: column;
210
+ gap: var(--spacer-8);
211
+ width: 100%;
212
+ max-width: 720px;
213
+ padding: var(--spacer-12);
214
+ background: var(--bg-overlay-l1);
215
+ border: 1px solid var(--border-neutral-l2);
216
+ border-radius: var(--radius-10);
217
+ }
218
+ .vui-composer:focus-within {
219
+ border-color: var(--border-brand);
220
+ }
221
+ .vui-composer__input {
222
+ width: 100%;
223
+ min-height: 56px;
224
+ max-height: 200px;
225
+ resize: vertical;
226
+ border: none;
227
+ outline: none;
228
+ background: transparent;
229
+ color: var(--text-default);
230
+ font-size: var(--body-md-font-size);
231
+ line-height: var(--body-md-line-height);
232
+ font-weight: var(--body-md-font-weight);
233
+ }
234
+ .vui-composer__input::placeholder {
235
+ color: var(--text-tertiary);
236
+ }
237
+ .vui-composer__toolbar {
238
+ display: flex;
239
+ align-items: center;
240
+ justify-content: space-between;
241
+ gap: var(--spacer-8);
242
+ }
243
+ .vui-composer__tools {
244
+ display: flex;
245
+ align-items: center;
246
+ gap: var(--spacer-4);
247
+ }
248
+ .vui-composer__icon-btn {
249
+ display: inline-flex;
250
+ align-items: center;
251
+ justify-content: center;
252
+ width: 32px;
253
+ height: 32px;
254
+ padding: 0;
255
+ border: none;
256
+ border-radius: var(--radius-6);
257
+ background: transparent;
258
+ cursor: pointer;
259
+ }
260
+ .vui-composer__icon-btn:hover {
261
+ background: var(--bg-overlay-l2);
262
+ }
263
+ .vui-composer__model {
264
+ display: inline-flex;
265
+ align-items: center;
266
+ gap: var(--spacer-4);
267
+ height: 28px;
268
+ padding: 0 var(--spacer-8);
269
+ border: 1px solid var(--border-neutral-l2);
270
+ border-radius: var(--radius-full);
271
+ background: var(--bg-overlay-l2);
272
+ color: var(--text-secondary);
273
+ font-size: var(--body-sm-font-size);
274
+ line-height: var(--body-sm-line-height);
275
+ cursor: pointer;
276
+ }
277
+ .vui-composer__model:hover {
278
+ background: var(--bg-overlay-l3);
279
+ color: var(--text-default);
280
+ }
281
+ .vui-composer__actions {
282
+ display: flex;
283
+ align-items: center;
284
+ gap: var(--spacer-8);
285
+ }
286
+ .vui-composer__send {
287
+ display: inline-flex;
288
+ align-items: center;
289
+ justify-content: center;
290
+ width: 36px;
291
+ height: 36px;
292
+ padding: 0;
293
+ border: none;
294
+ border-radius: var(--radius-8);
295
+ background: var(--bg-brand);
296
+ cursor: pointer;
297
+ }
298
+ .vui-composer__send:hover {
299
+ background: var(--bg-brand-hover);
300
+ }
301
+
302
+ /* ── Dialog ────────────────────────────────────────────────── */
303
+ /* ===== Dialog / Modal ===== */
304
+ .vui-backdrop {
305
+ position: relative;
306
+ background: rgba(0,0,0,.5);
307
+ border: 1px solid var(--border-neutral-l1);
308
+ padding: var(--spacer-32) var(--spacer-24);
309
+ display: flex; align-items: center; justify-content: center;
310
+ }
311
+ .vui-dialog {
312
+ background: var(--bg-base-secondary);
313
+ border: 1px solid var(--border-neutral-l1);
314
+ border-radius: var(--radius-4);
315
+ width: 100%; max-width: 420px;
316
+ overflow: hidden;
317
+ }
318
+ .vui-dialog__head {
319
+ padding: var(--spacer-6) var(--spacer-12);;
320
+ display: flex; align-items: center; justify-content: space-between;
321
+ border-bottom: 1px solid var(--border-neutral-l1);
322
+ }
323
+ .vui-dialog__title { font-size: var(--heading-xs-font-size); font-weight: var(--heading-xs-font-weight); color: var(--text-default); }
324
+ .vui-dialog__close {
325
+ width: 24px; height: 24px;
326
+ display: inline-flex; align-items: center; justify-content: center;
327
+ background: transparent; border: none; color: var(--icon-secondary);
328
+ cursor: pointer; border-radius: var(--radius-4);
329
+ }
330
+ .vui-dialog__close:hover { background: var(--bg-overlay-l2); color: var(--icon-default); }
331
+ .vui-dialog__body { padding: var(--spacer-16) var(--spacer-24); color: var(--text-secondary); font-size: var(--body-sm-font-size); line-height: var(--body-sm-line-height); }
332
+ .vui-dialog__foot {
333
+ padding: var(--spacer-8) var(--spacer-10);
334
+ display: flex; justify-content: flex-end; gap: var(--spacer-8);
335
+ border-top: 1px solid var(--border-neutral-l1);
336
+ }
337
+
338
+ /* ===== Drawer ===== */
339
+ .vui-drawer {
340
+ background: var(--bg-base-secondary);
341
+ border: 1px solid var(--border-neutral-l1);
342
+ border-radius: var(--radius-4);
343
+ width: 100%; max-width: 360px;
344
+ display: flex; flex-direction: column;
345
+ }
346
+ .vui-drawer__head { padding: var(--spacer-6) var(--spacer-12); border-bottom: 1px solid var(--border-neutral-l1); display: flex; justify-content: space-between; align-items: center; }
347
+ .vui-drawer__body { padding: var(--spacer-16) var(--spacer-24); flex: 1; }
348
+
349
+ /* ── Editor Tabs ───────────────────────────────────────────── */
350
+ /* ===== Editor Tabs ===== */
351
+ .vui-editortabs {
352
+ display: flex;
353
+ align-items: stretch;
354
+ border-bottom: 1px solid var(--border-neutral-l1);
355
+ background-color: var(--bg-base-default);
356
+ background-image: linear-gradient(var(--special-bgtabsoverlay), var(--special-bgtabsoverlay));
357
+ }
358
+ .vui-editortab {
359
+ display: inline-flex;
360
+ align-items: center;
361
+ column-gap: 8px;
362
+ padding: 0 14px;
363
+ height: 40px;
364
+ color: var(--text-secondary);
365
+ font-size: 12px;
366
+ font-weight: 500;
367
+ border-right: 1px solid var(--border-neutral-l1);
368
+ cursor: pointer;
369
+ white-space: nowrap;
370
+ background: transparent;
371
+ min-width: 0;
372
+ }
373
+ .vui-editortab:hover {
374
+ color: var(--text-default);
375
+ background-image: linear-gradient(var(--bg-overlay-l1), var(--bg-overlay-l1));
376
+ }
377
+ .vui-editortab.is-active {
378
+ background: var(--bg-base-secondary);
379
+ color: var(--text-default);
380
+ box-shadow: inset 0 1px 0 0 var(--icon-default);
381
+ }
382
+ .vui-editortab.is-active:hover { background: var(--bg-base-secondary); }
383
+ .vui-editortab .ic {
384
+ width: 14px; height: 14px;
385
+ flex-shrink: 0;
386
+ display: inline-flex; align-items: center; justify-content: center;
387
+ }
388
+ .vui-editortab .close {
389
+ width: 16px; height: 16px;
390
+ display: inline-flex; align-items: center; justify-content: center;
391
+ color: var(--icon-tertiary);
392
+ }
393
+ .vui-editortab.is-active .close { color: var(--icon-secondary); }
394
+ .vui-editortabs__spacer { flex: 1; min-width: 0; border-right: 0; }
395
+ .vui-editortabs__actions {
396
+ display: inline-flex;
397
+ align-items: center;
398
+ column-gap: 4px;
399
+ padding: 0 8px;
400
+ color: var(--icon-secondary);
401
+ }
402
+ .vui-editortabs__actions button {
403
+ width: 24px; height: 24px;
404
+ display: inline-flex; align-items: center; justify-content: center;
405
+ background: transparent; border: 0;
406
+ color: inherit;
407
+ cursor: pointer;
408
+ border-radius: 4px;
409
+ }
410
+ .vui-editortabs__actions button:hover {
411
+ color: var(--icon-default);
412
+ background: var(--bg-overlay-l1);
413
+ }
414
+
415
+ /* ── File Tree ─────────────────────────────────────────────── */
416
+ /* ===== File Tree ===== */
417
+ .vui-filetree {
418
+ width: 260px;
419
+ padding: var(--spacer-4);
420
+ background: var(--bg-base-secondary);
421
+ border: 1px solid var(--border-neutral-l1);
422
+ border-radius: var(--radius-6);
423
+ font-size: var(--body-sm-font-size);
424
+ color: var(--text-default);
425
+ }
426
+ .vui-filetree__row {
427
+ display: flex;
428
+ align-items: center;
429
+ gap: var(--spacer-6);
430
+ height: 26px;
431
+ padding: 0 var(--spacer-6);
432
+ padding-left: var(--spacer-6);
433
+ border-radius: var(--radius-4);
434
+ cursor: pointer;
435
+ white-space: nowrap;
436
+ }
437
+ .vui-filetree__row[data-depth="1"] { padding-left: calc(var(--spacer-6) + 16px); }
438
+ .vui-filetree__row[data-depth="2"] { padding-left: calc(var(--spacer-6) + 32px); }
439
+ .vui-filetree__row[data-depth="3"] { padding-left: calc(var(--spacer-6) + 48px); }
440
+ .vui-filetree__row:hover { background: var(--bg-overlay-l2); }
441
+ .vui-filetree__row.is-active {
442
+ background: var(--bg-overlay-l3);
443
+ color: var(--text-default-hover);
444
+ }
445
+ .vui-filetree__chevron {
446
+ display: inline-flex;
447
+ width: 14px;
448
+ height: 14px;
449
+ flex: 0 0 14px;
450
+ }
451
+ .vui-filetree__chevron--leaf { visibility: hidden; }
452
+ .vui-filetree__label { overflow: hidden; text-overflow: ellipsis; }
453
+ .vui-filetree__icon--folder { filter: none; }
454
+ .vui-filetree__icon--ts { color: var(--accent-blue); }
455
+ .vui-filetree__icon--css { color: var(--code-attribute); }
456
+ .vui-filetree__icon--json { color: var(--code-constant); }
457
+
458
+ /* ── Forms ─────────────────────────────────────────────────── */
459
+ /* ===== Input / Search ===== */
460
+ .vui-input {
461
+ display: flex; align-items: center; gap: var(--spacer-8);
462
+ padding: 0 var(--spacer-12); height: 28px;
463
+ background: var(--bg-overlay-l1);
464
+ border: 1px solid var(--border-neutral-l1);
465
+ border-radius: var(--radius-4);
466
+ color: var(--text-default);
467
+ width: 100%;
468
+ font-size: var(--body-sm-font-size); line-height: 1;
469
+ }
470
+ .vui-input:focus-within { border-color: var(--border-brand); background: var(--bg-overlay-l2); }
471
+ .vui-input input {
472
+ flex: 1; background: transparent; border: none; outline: none;
473
+ color: var(--text-default); font: inherit;
474
+ }
475
+ .vui-input input::placeholder { color: var(--text-tertiary); }
476
+ .vui-input__icon { color: var(--icon-secondary); display: inline-flex; }
477
+ .vui-search { position: relative; }
478
+
479
+ /* ===== Select =====
480
+ * Dropdown indicator is a linear chevron (Lucide-style SVG, 2px stroke) — never a
481
+ * filled triangle / caret. The svg is inlined as a data-uri using currentColor
482
+ * stand-in so it inherits via the --icon-tertiary fallback.
483
+ */
484
+ .vui-select {
485
+ width: 100%; height: 28px;
486
+ padding: 0 var(--spacer-32) 0 var(--spacer-12);
487
+ background: var(--bg-overlay-l1);
488
+ background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239aa0aa' stroke-width='2' stroke-linecap='butt' stroke-linejoin='miter'><polyline points='6 9 12 15 18 9'/></svg>");
489
+ background-repeat: no-repeat;
490
+ background-position: right var(--spacer-12) center;
491
+ background-size: 12px 12px;
492
+ color: var(--text-default);
493
+ border: 1px solid var(--border-neutral-l1);
494
+ border-radius: var(--radius-4);
495
+ appearance: none; -webkit-appearance: none;
496
+ font: inherit; font-size: var(--body-sm-font-size);
497
+ }
498
+ .vui-select:focus { border-color: var(--border-brand); outline: none; }
499
+
500
+ /* ===== Textarea ===== */
501
+ .vui-textarea {
502
+ width: 100%; min-height: 96px; padding: var(--spacer-8) var(--spacer-12);
503
+ background: var(--bg-overlay-l1);
504
+ border: 1px solid var(--border-neutral-l1);
505
+ border-radius: var(--radius-4);
506
+ color: var(--text-default);
507
+ font-family: var(--body-base-font-family);
508
+ font-size: var(--body-sm-font-size);
509
+ line-height: var(--body-base-line-height);
510
+ resize: vertical;
511
+ }
512
+ .vui-textarea:focus { outline: none; border-color: var(--border-brand); }
513
+
514
+ /* ===== Slider ===== */
515
+ .vui-slider { -webkit-appearance: none; appearance: none; width: 100%; height: 4px; background: var(--bg-overlay-l3); border-radius: var(--radius-full); outline: none; }
516
+ .vui-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 14px; height: 14px; border-radius: 50%; background: var(--text-default); border: 2px solid var(--bg-base-default); box-shadow: 0 0 0 1px var(--border-neutral-l2); cursor: pointer; }
517
+ .vui-slider::-moz-range-thumb { width: 14px; height: 14px; border-radius: 50%; background: var(--text-default); border: 2px solid var(--bg-base-default); box-shadow: 0 0 0 1px var(--border-neutral-l2); cursor: pointer; }
518
+
519
+ /* ===== Checkbox / Radio / Switch ===== */
520
+ .vui-check, .vui-radio {
521
+ display: inline-flex; align-items: center; gap: var(--spacer-8);
522
+ font-size: var(--body-sm-font-size); color: var(--text-default);
523
+ cursor: pointer; user-select: none;
524
+ }
525
+ .vui-check__box {
526
+ width: 14px; height: 14px;
527
+ border: 1px solid var(--border-neutral-l2);
528
+ background: var(--bg-overlay-l1);
529
+ border-radius: var(--radius-2);
530
+ display: inline-flex; align-items: center; justify-content: center;
531
+ color: var(--text-onbrand);
532
+ transition: background .12s, border-color .12s;
533
+ }
534
+ .vui-check input { display: none; }
535
+ .vui-check input:checked + .vui-check__box {
536
+ background: var(--bg-brand);
537
+ border-color: var(--bg-brand);
538
+ }
539
+ .vui-check input:checked + .vui-check__box::after {
540
+ content: ''; width: 8px; height: 4px;
541
+ border-left: 2px solid var(--text-onbrand);
542
+ border-bottom: 2px solid var(--text-onbrand);
543
+ transform: rotate(-45deg) translate(1px, -1px);
544
+ }
545
+ .vui-check input:disabled + .vui-check__box { opacity: .5; cursor: not-allowed; }
546
+
547
+ .vui-radio__dot {
548
+ width: 16px; height: 16px;
549
+ border: 1px solid var(--border-neutral-l2);
550
+ background: var(--bg-overlay-l1);
551
+ border-radius: 50%;
552
+ display: inline-flex; align-items: center; justify-content: center;
553
+ }
554
+ .vui-radio input { display: none; }
555
+ .vui-radio input:checked + .vui-radio__dot {
556
+ border-color: var(--bg-brand);
557
+ }
558
+ .vui-radio input:checked + .vui-radio__dot::after {
559
+ content: ''; width: 8px; height: 8px; border-radius: 50%;
560
+ background: var(--bg-brand);
561
+ }
562
+
563
+ .vui-switch {
564
+ position: relative; display: inline-flex;
565
+ width: 32px; height: 18px;
566
+ background: var(--bg-overlay-l3);
567
+ border: 1px solid var(--border-neutral-l1);
568
+ border-radius: var(--radius-full);
569
+ cursor: pointer; transition: background .15s, border-color .15s;
570
+ }
571
+ .vui-switch input { display: none; }
572
+ .vui-switch__thumb {
573
+ position: absolute; top: 2px; left: 2px;
574
+ width: 12px; height: 12px;
575
+ background: var(--text-default);
576
+ border-radius: 50%;
577
+ transition: left .15s, background .15s;
578
+ }
579
+ .vui-switch input:checked ~ .vui-switch__thumb { left: 16px; background: var(--text-onbrand); }
580
+ .vui-switch:has(input:checked) {
581
+ background: var(--bg-brand);
582
+ border-color: var(--bg-brand);
583
+ }
584
+
585
+ /* Small switch */
586
+ .vui-switch--sm { width: 26px; height: 14px; }
587
+ .vui-switch--sm .vui-switch__thumb { width: 10px; height: 10px; }
588
+ .vui-switch--sm input:checked ~ .vui-switch__thumb { left: 12px; }
589
+
590
+ /* Labelled switch row: description on the left, switch on the right */
591
+ .vui-switch-row {
592
+ display: flex; align-items: center; justify-content: space-between;
593
+ gap: var(--spacer-16); padding: var(--spacer-8) 0;
594
+ max-width: 480px;
595
+ }
596
+ .vui-switch-row__text { display: flex; flex-direction: column; gap: var(--spacer-4); }
597
+ .vui-switch-row__title { font-size: var(--body-sm-font-size); color: var(--text-default); }
598
+ .vui-switch-row__hint { font-size: var(--body-xs-font-size); color: var(--text-tertiary); }
599
+
600
+ /* ── Kbd ───────────────────────────────────────────────────── */
601
+ /* ===== Kbd ===== */
602
+ .vui-kbd {
603
+ display: inline-flex;
604
+ align-items: center;
605
+ justify-content: center;
606
+ min-width: 22px;
607
+ height: 22px;
608
+ padding: 0 var(--spacer-6);
609
+ font-family: var(--code-editor-font-family);
610
+ font-size: var(--body-xs-font-size);
611
+ line-height: 1;
612
+ font-weight: var(--font-weight-medium);
613
+ color: var(--text-secondary);
614
+ background: var(--bg-overlay-l2);
615
+ border: 1px solid var(--border-neutral-l2);
616
+ border-bottom-width: 2px;
617
+ border-radius: var(--radius-4);
618
+ }
619
+ .vui-kbd__combo {
620
+ display: inline-flex;
621
+ align-items: center;
622
+ gap: var(--spacer-4);
623
+ }
624
+ .vui-kbd__plus {
625
+ color: var(--text-tertiary);
626
+ font-size: var(--body-xs-font-size);
627
+ }
628
+ .vui-kbd__row {
629
+ display: flex;
630
+ flex-wrap: wrap;
631
+ align-items: center;
632
+ gap: var(--spacer-16);
633
+ }
634
+
635
+ /* ── Menu ──────────────────────────────────────────────────── */
636
+ /* ===== Menu ===== */
637
+ .vui-menu {
638
+ background: var(--bg-menu);
639
+ border: 1px solid var(--border-neutral-l1);
640
+ border-radius: var(--radius-4);
641
+ padding: var(--spacer-4);
642
+ min-width: 180px;
643
+ }
644
+ .vui-menu__item {
645
+ display: flex; align-items: center; gap: var(--spacer-8);
646
+ padding: var(--spacer-6) var(--spacer-8);
647
+ border-radius: var(--radius-2);
648
+ color: var(--text-default);
649
+ font-size: var(--body-sm-font-size);
650
+ cursor: pointer;
651
+ }
652
+ .vui-menu__item .icon { color: var(--icon-secondary); }
653
+ .vui-menu__item:hover { background: var(--bg-overlay-l2); }
654
+ .vui-menu__item--danger { color: var(--status-error-default); }
655
+ .vui-menu__item--danger .icon { color: var(--status-error-default); }
656
+ .vui-menu__divider { height: 1px; background: var(--border-neutral-l1); margin: var(--spacer-4) 0; }
657
+ .vui-menu__shortcut { margin-left: auto; color: var(--text-tertiary); font-size: var(--body-xs-font-size); font-family: var(--code-editor-font-family); }
658
+
659
+ /* ── Nav List ──────────────────────────────────────────────── */
660
+ /* ===== Nav list ===== */
661
+ .vui-navlist {
662
+ display: flex;
663
+ flex-direction: column;
664
+ gap: var(--spacer-16);
665
+ width: 240px;
666
+ padding: var(--spacer-12);
667
+ background: var(--bg-menu);
668
+ border: 1px solid var(--border-neutral-l1);
669
+ border-radius: var(--radius-10);
670
+ }
671
+ .vui-navlist__group {
672
+ display: flex;
673
+ flex-direction: column;
674
+ gap: 2px;
675
+ }
676
+ .vui-navlist__group-title {
677
+ padding: var(--spacer-6) var(--spacer-8);
678
+ font-size: var(--body-xs-font-size);
679
+ line-height: var(--body-xs-line-height);
680
+ font-weight: var(--font-weight-medium);
681
+ letter-spacing: 0.06em;
682
+ text-transform: uppercase;
683
+ color: var(--text-tertiary);
684
+ }
685
+ .vui-navlist__item {
686
+ display: flex;
687
+ align-items: center;
688
+ gap: var(--spacer-8);
689
+ padding: var(--spacer-8);
690
+ border-radius: var(--radius-6);
691
+ font-size: var(--body-sm-font-size);
692
+ line-height: var(--body-sm-line-height);
693
+ font-weight: var(--font-weight-medium);
694
+ color: var(--text-secondary);
695
+ text-decoration: none;
696
+ cursor: pointer;
697
+ }
698
+ .vui-navlist__item:hover {
699
+ background: var(--bg-overlay-l2);
700
+ color: var(--text-default);
701
+ }
702
+ .vui-navlist__item.is-active {
703
+ background: var(--bg-brand);
704
+ color: var(--text-onbrand);
705
+ }
706
+ .vui-navlist__label { flex: 1; min-width: 0; }
707
+ .vui-navlist__badge {
708
+ font-size: var(--body-xs-font-size);
709
+ font-variant-numeric: tabular-nums;
710
+ color: var(--text-tertiary);
711
+ }
712
+ .vui-navlist__item.is-active .vui-navlist__badge { color: var(--text-onbrand); }
713
+
714
+ /* ── Page Header ───────────────────────────────────────────── */
715
+ /* ===== Page header ===== */
716
+ .vui-pagehead {
717
+ display: flex;
718
+ align-items: flex-start;
719
+ justify-content: space-between;
720
+ gap: var(--spacer-24);
721
+ padding: var(--spacer-24) 0;
722
+ border-bottom: 1px solid var(--border-neutral-l1);
723
+ }
724
+ .vui-pagehead__main {
725
+ display: flex;
726
+ flex-direction: column;
727
+ gap: var(--spacer-6);
728
+ }
729
+ .vui-pagehead__title {
730
+ font-size: var(--heading-lg-font-size);
731
+ line-height: var(--heading-lg-line-height);
732
+ font-weight: var(--font-weight-strong);
733
+ color: var(--text-default);
734
+ }
735
+ .vui-pagehead__subtitle {
736
+ font-size: var(--body-sm-font-size);
737
+ line-height: var(--body-sm-line-height);
738
+ color: var(--text-tertiary);
739
+ }
740
+ .vui-pagehead__actions {
741
+ display: flex;
742
+ align-items: center;
743
+ gap: var(--spacer-8);
744
+ flex-shrink: 0;
745
+ }
746
+ .vui-pagehead__btn {
747
+ display: inline-flex;
748
+ align-items: center;
749
+ gap: var(--spacer-6);
750
+ height: 32px;
751
+ padding: 0 var(--spacer-12);
752
+ font-size: var(--body-sm-font-size);
753
+ font-weight: var(--font-weight-medium);
754
+ color: var(--text-default);
755
+ background: var(--bg-overlay-l2);
756
+ border: 1px solid var(--border-neutral-l2);
757
+ border-radius: var(--radius-6);
758
+ cursor: pointer;
759
+ }
760
+ .vui-pagehead__btn.is-primary {
761
+ color: var(--text-onbrand);
762
+ background: var(--bg-brand);
763
+ border-color: var(--border-brand);
764
+ }
765
+
766
+ /* ── Pagination ────────────────────────────────────────────── */
767
+ /* ===== Pagination ===== */
768
+ .vui-pagination { display: inline-flex; gap: var(--spacer-4); }
769
+ .vui-pagination__item {
770
+ min-width: 24px; height: 24px; padding: 0 var(--spacer-6);
771
+ display: inline-flex; align-items: center; justify-content: center;
772
+ background: transparent; color: var(--text-secondary);
773
+ border: 1px solid var(--border-neutral-l1);
774
+ border-radius: var(--radius-2);
775
+ font: inherit; font-size: var(--body-xs-font-size);
776
+ cursor: pointer;
777
+ }
778
+ .vui-pagination__item:hover { background: var(--bg-overlay-l2); color: var(--text-default); }
779
+ .vui-pagination__item.is-active { background: var(--bg-overlay-l3); color: var(--text-default); border-color: var(--border-neutral-l2); }
780
+ .vui-pagination__item[disabled] { opacity: .4; cursor: not-allowed; }
781
+
782
+ /* ── Stat Card ─────────────────────────────────────────────── */
783
+ /* ===== Stat card ===== */
784
+ .vui-statcard {
785
+ display: flex;
786
+ flex-direction: column;
787
+ gap: var(--spacer-8);
788
+ padding: var(--spacer-16);
789
+ background: var(--bg-base-secondary);
790
+ border: 1px solid var(--border-neutral-l1);
791
+ border-radius: var(--radius-10);
792
+ min-width: 200px;
793
+ }
794
+ .vui-statcard__label {
795
+ font-size: var(--body-xs-font-size);
796
+ line-height: var(--body-xs-line-height);
797
+ font-weight: var(--font-weight-medium);
798
+ letter-spacing: 0.08em;
799
+ text-transform: uppercase;
800
+ color: var(--text-tertiary);
801
+ }
802
+ .vui-statcard__value {
803
+ font-family: var(--code-editor-font-family);
804
+ font-variant-numeric: tabular-nums;
805
+ font-size: var(--heading-lg-font-size);
806
+ line-height: var(--heading-lg-line-height);
807
+ font-weight: var(--font-weight-strong);
808
+ color: var(--text-default);
809
+ }
810
+ .vui-statcard__delta {
811
+ display: inline-flex;
812
+ align-items: center;
813
+ gap: var(--spacer-4);
814
+ font-size: var(--body-sm-font-size);
815
+ line-height: var(--body-sm-line-height);
816
+ font-weight: var(--font-weight-medium);
817
+ font-variant-numeric: tabular-nums;
818
+ }
819
+ .vui-statcard__delta.is-up { color: var(--status-success-default); }
820
+ .vui-statcard__delta.is-down { color: var(--status-error-default); }
821
+ .vui-statcard__delta .vui-statcard__caption {
822
+ color: var(--text-tertiary);
823
+ font-weight: var(--font-weight-default);
824
+ }
825
+ .vui-statcard__grid {
826
+ display: flex;
827
+ flex-wrap: wrap;
828
+ gap: var(--spacer-16);
829
+ }
830
+
831
+ /* ── Status Bar ────────────────────────────────────────────── */
832
+ /* ===== Status Bar ===== */
833
+ .vui-statusbar {
834
+ display: flex;
835
+ align-items: center;
836
+ justify-content: space-between;
837
+ height: 24px;
838
+ padding: 0 var(--spacer-8);
839
+ background: var(--bg-base-secondary);
840
+ border-top: 1px solid var(--border-neutral-l1);
841
+ font-size: var(--body-xs-font-size);
842
+ color: var(--text-secondary);
843
+ }
844
+ .vui-statusbar__group {
845
+ display: flex;
846
+ align-items: center;
847
+ gap: var(--spacer-4);
848
+ }
849
+ .vui-statusbar__item {
850
+ display: inline-flex;
851
+ align-items: center;
852
+ gap: var(--spacer-6);
853
+ height: 20px;
854
+ padding: 0 var(--spacer-6);
855
+ border-radius: var(--radius-4);
856
+ color: var(--text-secondary);
857
+ cursor: pointer;
858
+ }
859
+ .vui-statusbar__item:hover {
860
+ background: var(--bg-overlay-l2);
861
+ color: var(--text-default);
862
+ }
863
+ .vui-statusbar__dot {
864
+ width: 8px;
865
+ height: 8px;
866
+ border-radius: var(--radius-full);
867
+ }
868
+ .vui-statusbar__dot--success { background: var(--status-success-default); }
869
+ .vui-statusbar__dot--warning { background: var(--status-warning-default); }
870
+ .vui-statusbar__dot--error { background: var(--status-error-default); }
871
+
872
+ /* ── Table Panel ───────────────────────────────────────────── */
873
+ /* ===== Table Panel ===== */
874
+ .vui-tablepanel {
875
+ background: var(--bg-layout-1);
876
+ border: 1px solid var(--border-1);
877
+ border-radius: 0 0 var(--radius-4) var(--radius-4);
878
+ border-top: none;
879
+ overflow: hidden;
880
+ }
881
+ .vui-tablepanel table { width: 100%; border-collapse: collapse; }
882
+ .vui-tablepanel th, .vui-tablepanel td {
883
+ padding: var(--spacer-6) var(--spacer-12);
884
+ border-bottom: 1px solid var(--border-1);
885
+ font-size: var(--body-sm-font-size);
886
+ text-align: left;
887
+ }
888
+ .vui-tablepanel th {
889
+ color: var(--text-tertiary);
890
+ font-weight: var(--font-weight-default);
891
+ font-size: var(--body-xs-font-size);
892
+ text-transform: uppercase;
893
+ letter-spacing: 0.04em;
894
+ background: var(--bg-overlay-l1);
895
+ }
896
+ .vui-tablepanel td { color: var(--text-default); }
897
+ .vui-tablepanel tr:last-child td { border-bottom: none; }
898
+ .vui-tablepanel td.num, .vui-tablepanel th.num {
899
+ text-align: right;
900
+ font-family: var(--code-editor-font-family);
901
+ font-variant-numeric: tabular-nums;
902
+ }
903
+ .vui-tablepanel__foot {
904
+ display: flex;
905
+ align-items: center;
906
+ justify-content: space-between;
907
+ padding: var(--spacer-8) var(--spacer-16);
908
+ border-top: 1px solid var(--border-1);
909
+ color: var(--text-tertiary);
910
+ font-size: var(--body-xs-font-size);
911
+ gap: var(--spacer-12);
912
+ }
913
+
914
+ /* Local-only header bar to demonstrate the "external header above the panel" usage pattern */
915
+ .pv-tp-header {
916
+ background: var(--bg-layout-1);
917
+ border: 1px solid var(--border-1);
918
+ border-bottom: none;
919
+ border-radius: var(--radius-4) var(--radius-4) 0 0;
920
+ padding: var(--spacer-6) var(--spacer-12);
921
+ display: flex; align-items: center; justify-content: space-between;
922
+ gap: var(--spacer-12);
923
+ }
924
+ .pv-tp-header h3 { margin: 0; font-size: var(--body-md-font-size, 14px); color: var(--text-default); }
925
+ .pv-tp-header .meta { color: var(--text-tertiary); font-size: var(--body-xs-font-size); }
926
+
927
+ /* ── Table ─────────────────────────────────────────────────── */
928
+ /* ===== Table ===== */
929
+ .vui-table { width: 100%; border-collapse: collapse; }
930
+ .vui-table th, .vui-table td { text-align: left; padding: var(--spacer-12) var(--spacer-8); border-bottom: 1px solid var(--border-neutral-l1); font-size: var(--body-sm-font-size); }
931
+ .vui-table th { color: var(--text-tertiary); font-weight: var(--font-weight-default, 400); font-size: var(--body-xs-font-size); text-transform: uppercase; letter-spacing: 0.04em; }
932
+ .vui-table td { color: var(--text-default); }
933
+ .vui-table tr:last-child td { border-bottom: none; }
934
+ /* Numeric cells in tables default to mono for column alignment */
935
+ .vui-table td.num,
936
+ .vui-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
937
+
938
+ /* Table wrapper: rounded, bordered container */
939
+ .vui-table-wrap {
940
+ border: 1px solid var(--border-neutral-l1);
941
+ border-radius: var(--radius-8);
942
+ overflow: hidden;
943
+ background: var(--bg-base-secondary);
944
+ }
945
+ /* Toolbar: search on the left, actions on the right */
946
+ .vui-table__toolbar {
947
+ display: flex; align-items: center; justify-content: space-between;
948
+ gap: var(--spacer-12);
949
+ padding: var(--spacer-10);
950
+ border-bottom: 1px solid var(--border-neutral-l1);
951
+ }
952
+ .vui-table__toolbar-actions { display: flex; align-items: center; gap: var(--spacer-8); }
953
+ /* Footer: row count on the left, pagination on the right */
954
+ .vui-table__footer {
955
+ display: flex; align-items: center; justify-content: space-between;
956
+ gap: var(--spacer-12);
957
+ padding: var(--spacer-10);
958
+ border-top: 1px solid var(--border-neutral-l1);
959
+ font-size: var(--body-xs-font-size); color: var(--text-tertiary);
960
+ }
961
+ .vui-table__pagination { display: flex; align-items: center; gap: var(--spacer-8); }
962
+
963
+ /* ── Tabs ──────────────────────────────────────────────────── */
964
+ /* ===== Tabs ===== */
965
+ .vui-tabs { display: flex; gap: var(--spacer-24); border-bottom: 1px solid var(--border-neutral-l1); }
966
+ .vui-tab {
967
+ padding: var(--spacer-12) 0;
968
+ background: transparent; border: none;
969
+ color: var(--text-tertiary); cursor: pointer;
970
+ font: inherit; font-size: var(--body-sm-font-size);
971
+ position: relative;
972
+ }
973
+ .vui-tab:hover { color: var(--text-default); }
974
+ .vui-tab.is-active { color: var(--text-default); }
975
+ .vui-tab.is-active::after {
976
+ content: ''; position: absolute;
977
+ left: 0; right: 0; bottom: -1px; height: 2px;
978
+ background: var(--icon-default);
979
+ }
980
+
981
+ /* Filled tabs: selected item gets a filled background + radius, no underline */
982
+ .vui-tabs--filled { border-bottom: none; gap: var(--spacer-4); }
983
+ .vui-tabs--filled .vui-tab {
984
+ padding: var(--spacer-6) var(--spacer-12);
985
+ border-radius: var(--radius-4);
986
+ }
987
+ .vui-tabs--filled .vui-tab.is-active::after { display: none; }
988
+ .vui-tabs--filled .vui-tab.is-active {
989
+ background: var(--bg-overlay-l3);
990
+ color: var(--text-default);
991
+ }
992
+
993
+ /* Closable tabs: each tab carries an inline close button on the right */
994
+ .vui-tabs--closable .vui-tab {
995
+ display: inline-flex; align-items: center; gap: var(--spacer-8);
996
+ }
997
+ .vui-tab__close {
998
+ display: inline-flex; align-items: center; justify-content: center;
999
+ width: 16px; height: 16px;
1000
+ border-radius: var(--radius-2);
1001
+ color: var(--icon-secondary);
1002
+ }
1003
+ .vui-tab__close:hover { background: var(--bg-overlay-l3); color: var(--icon-default); }
1004
+
1005
+ /* ── Tag ───────────────────────────────────────────────────── */
1006
+ /* ===== Tag / Badge ===== */
1007
+ .vui-tag {
1008
+ display: inline-flex; align-items: center;
1009
+ padding: 0 var(--spacer-6); height: 18px;
1010
+ border-radius: var(--radius-2);
1011
+ font-size: var(--body-xs-font-size); line-height: 1;
1012
+ background: var(--bg-overlay-l2); color: var(--text-secondary);
1013
+ border: 1px solid var(--border-neutral-l1);
1014
+ }
1015
+ .vui-tag--brand { background: var(--bg-brand-popup); color: var(--text-brand); }
1016
+ .vui-tag--success { background: var(--status-success-surface-l1); color: var(--status-success-default); }
1017
+ .vui-tag--warning { background: var(--status-warning-surface-l1); color: var(--status-warning-default); }
1018
+ .vui-tag--danger { background: var(--status-error-surface-l1); color: var(--status-error-default); }
1019
+ /* Count badge: numeric pill for list counts */
1020
+ .vui-tag--count {
1021
+ justify-content: center;
1022
+ min-width: 18px; padding: 0 var(--spacer-6);
1023
+ border-radius: var(--radius-full);
1024
+ background: var(--bg-overlay-l3); color: var(--text-secondary);
1025
+ border: none;
1026
+ font-variant-numeric: tabular-nums;
1027
+ }
1028
+ /* Neutral strong: plan-style emphasis badge */
1029
+ .vui-tag--neutral-strong {
1030
+ background: var(--bg-overlay-l4); color: var(--text-default);
1031
+ border: none; font-weight: var(--font-weight-medium);
1032
+ }
1033
+
1034
+ /* ── Workbench Titlebar ────────────────────────────────────── */
1035
+ /* ===== Workbench Titlebar ===== */
1036
+ .vui-wbtitlebar {
1037
+ display: flex;
1038
+ align-items: center;
1039
+ justify-content: space-between;
1040
+ gap: var(--spacer-12);
1041
+ height: 40px;
1042
+ padding: 0 var(--spacer-12);
1043
+ background: var(--bg-base-secondary);
1044
+ border-bottom: 1px solid var(--border-neutral-l1);
1045
+ }
1046
+ .vui-wbtitlebar__left,
1047
+ .vui-wbtitlebar__right {
1048
+ display: flex;
1049
+ align-items: center;
1050
+ gap: var(--spacer-8);
1051
+ }
1052
+ .vui-wbtitlebar__lights {
1053
+ display: flex;
1054
+ align-items: center;
1055
+ gap: var(--spacer-8);
1056
+ margin-right: var(--spacer-8);
1057
+ }
1058
+ .vui-wbtitlebar__light {
1059
+ width: 12px;
1060
+ height: 12px;
1061
+ border-radius: var(--radius-full);
1062
+ }
1063
+ .vui-wbtitlebar__light--close { background: var(--status-error-default); }
1064
+ .vui-wbtitlebar__light--min { background: var(--status-alert-default); }
1065
+ .vui-wbtitlebar__light--max { background: var(--status-success-default); }
1066
+ .vui-wbtitlebar__mode-chip {
1067
+ display: inline-flex;
1068
+ align-items: center;
1069
+ gap: var(--spacer-6);
1070
+ height: 24px;
1071
+ padding: 0 var(--spacer-8);
1072
+ background: var(--bg-brand);
1073
+ color: var(--text-onbrand);
1074
+ border-radius: var(--radius-full);
1075
+ font-size: var(--body-xs-font-size);
1076
+ font-weight: var(--font-weight-medium);
1077
+ }
1078
+ .vui-wbtitlebar__project-selector {
1079
+ display: inline-flex;
1080
+ align-items: center;
1081
+ gap: var(--spacer-6);
1082
+ height: 24px;
1083
+ padding: 0 var(--spacer-8);
1084
+ background: var(--bg-overlay-l1);
1085
+ color: var(--text-default);
1086
+ border: 1px solid var(--border-neutral-l2);
1087
+ border-radius: var(--radius-6);
1088
+ font-size: var(--body-sm-font-size);
1089
+ cursor: pointer;
1090
+ }
1091
+ .vui-wbtitlebar__project-selector:hover {
1092
+ background: var(--bg-overlay-l2);
1093
+ color: var(--text-default-hover);
1094
+ }
1095
+ .vui-wbtitlebar__iconbtn {
1096
+ display: inline-flex;
1097
+ align-items: center;
1098
+ justify-content: center;
1099
+ width: 28px;
1100
+ height: 28px;
1101
+ border-radius: var(--radius-6);
1102
+ background: transparent;
1103
+ border: none;
1104
+ cursor: pointer;
1105
+ }
1106
+ .vui-wbtitlebar__iconbtn:hover { background: var(--bg-overlay-l2); }
1107
+ .vui-wbtitlebar .icon { display: block; }
1108
+
1109
+ /* ===== Setting row ===== */
1110
+ .vui-settingrow {
1111
+ display: flex;
1112
+ align-items: center;
1113
+ justify-content: space-between;
1114
+ gap: var(--spacer-24);
1115
+ padding: var(--spacer-16) var(--spacer-24);
1116
+ }
1117
+ .vui-settingrow__main {
1118
+ display: flex;
1119
+ flex-direction: column;
1120
+ gap: var(--spacer-4);
1121
+ min-width: 0;
1122
+ }
1123
+ .vui-settingrow__title {
1124
+ font-size: var(--body-md-font-size);
1125
+ line-height: var(--body-md-line-height);
1126
+ font-weight: var(--font-weight-medium);
1127
+ color: var(--text-default);
1128
+ }
1129
+ .vui-settingrow__desc {
1130
+ font-size: var(--body-sm-font-size);
1131
+ line-height: var(--body-sm-line-height);
1132
+ color: var(--text-tertiary);
1133
+ }
1134
+ .vui-settingrow__control {
1135
+ display: inline-flex;
1136
+ align-items: center;
1137
+ gap: var(--spacer-8);
1138
+ flex-shrink: 0;
1139
+ }
1140
+
1141
+ /* === Variant A: select === */
1142
+ .vui-settingrow__select {
1143
+ position: relative;
1144
+ display: inline-flex;
1145
+ align-items: center;
1146
+ justify-content: space-between;
1147
+ gap: var(--spacer-12);
1148
+ min-width: 200px;
1149
+ height: 36px;
1150
+ padding: 0 var(--spacer-12);
1151
+ font-size: var(--body-sm-font-size);
1152
+ font-weight: var(--font-weight-medium);
1153
+ color: var(--text-default);
1154
+ background: var(--bg-overlay-l2);
1155
+ border: 1px solid var(--border-neutral-l2);
1156
+ border-radius: var(--radius-6);
1157
+ cursor: pointer;
1158
+ }
1159
+ .vui-settingrow__select:hover { background: var(--bg-overlay-l3); }
1160
+ .vui-settingrow__select .icon { color: var(--icon-tertiary); flex-shrink: 0; }
1161
+
1162
+ /* === Variant B: button === */
1163
+ .vui-settingrow__btn {
1164
+ display: inline-flex;
1165
+ align-items: center;
1166
+ gap: var(--spacer-6);
1167
+ height: 36px;
1168
+ padding: 0 var(--spacer-16);
1169
+ font-size: var(--body-sm-font-size);
1170
+ font-weight: var(--font-weight-medium);
1171
+ color: var(--text-default);
1172
+ background: var(--bg-overlay-l2);
1173
+ border: 1px solid var(--border-neutral-l2);
1174
+ border-radius: var(--radius-6);
1175
+ cursor: pointer;
1176
+ }
1177
+ .vui-settingrow__btn:hover { background: var(--bg-overlay-l3); }
1178
+ .vui-settingrow__btn .icon { color: var(--icon-tertiary); }
1179
+
1180
+ /* danger text button (e.g. 清除 / Clear) */
1181
+ .vui-settingrow__btn--danger {
1182
+ color: var(--status-error-default);
1183
+ }
1184
+ .vui-settingrow__btn--danger:hover {
1185
+ background: var(--status-error-surface-l1);
1186
+ border-color: var(--status-error-surface-l2);
1187
+ }
1188
+
1189
+ /* === Variant C: select-with-icon (select + trailing icon button) === */
1190
+ .vui-settingrow__iconbtn {
1191
+ display: inline-flex;
1192
+ align-items: center;
1193
+ justify-content: center;
1194
+ width: 36px;
1195
+ height: 36px;
1196
+ color: var(--icon-secondary);
1197
+ background: transparent;
1198
+ border: 1px solid var(--border-neutral-l2);
1199
+ border-radius: var(--radius-6);
1200
+ cursor: pointer;
1201
+ }
1202
+ .vui-settingrow__iconbtn:hover {
1203
+ background: var(--bg-overlay-l2);
1204
+ color: var(--icon-default);
1205
+ }
1206
+
1207
+ /* === Panel container === */
1208
+ .vui-settingrow__panel {
1209
+ display: flex;
1210
+ flex-direction: column;
1211
+ background: var(--bg-base-secondary);
1212
+ border: 1px solid var(--border-neutral-l1);
1213
+ border-radius: var(--radius-10);
1214
+ overflow: hidden;
1215
+ }
1216
+ .vui-settingrow__panel > .vui-settingrow + .vui-settingrow {
1217
+ border-top: 1px solid var(--border-neutral-l1);
1218
+ }
1219
+
1220
+ /* === Section group label (e.g. 基础设置 / 偏好设置 / 数据管理) === */
1221
+ .vui-settingrow__group {
1222
+ display: flex;
1223
+ flex-direction: column;
1224
+ gap: var(--spacer-12);
1225
+ }
1226
+ .vui-settingrow__group + .vui-settingrow__group {
1227
+ margin-top: var(--spacer-24);
1228
+ }
1229
+ .vui-settingrow__grouplabel {
1230
+ font-size: var(--body-sm-font-size);
1231
+ font-weight: var(--font-weight-medium);
1232
+ color: var(--text-secondary);
1233
+ padding-left: var(--spacer-4);
1234
+ }