@astryx-vue/lab 0.0.0-stage → 0.1.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.
@@ -0,0 +1,271 @@
1
+ @layer astryx.reset, astryx.tokens, astryx.base, astryx.components, astryx.overrides;
2
+
3
+ @layer astryx.components {
4
+ /*
5
+ * 终端变体故意始终是深色(终端是品牌面,和真终端一样),所以它用固定的
6
+ * 近黑灰色,而不是会跟着明暗切换的 light-dark() 主题变量。
7
+ */
8
+
9
+ .astryx-log-stream {
10
+ position: relative;
11
+ display: flex;
12
+ flex-direction: column;
13
+ border-radius: var(--astryx-radius-element, 0.625rem);
14
+ border-width: var(--astryx-border-width, 1px);
15
+ border-style: solid;
16
+ border-color: var(--astryx-color-border, light-dark(#00000014, #ffffff1a));
17
+ background-color: var(--astryx-color-background-surface, light-dark(#ffffff, #262626));
18
+ overflow: hidden;
19
+ font-family: var(--astryx-font-family-code, ui-monospace, monospace);
20
+ font-size: var(--astryx-font-size-sm, 0.75rem);
21
+ }
22
+
23
+ .astryx-log-stream[data-variant='terminal'] {
24
+ border-color: #26262a;
25
+ background-color: #0a0a0a;
26
+ }
27
+
28
+ .astryx-log-stream__scroller {
29
+ overflow-y: auto;
30
+ scrollbar-width: thin;
31
+ overscroll-behavior: contain;
32
+ }
33
+
34
+ /* 一行:时间 / 级别 / 来源 / 消息四列,列数按有没有来源、时间变。 */
35
+ .astryx-log-stream__row {
36
+ display: grid;
37
+ align-items: baseline;
38
+ column-gap: var(--astryx-spacing-3, 12px);
39
+ padding-block: var(--astryx-spacing-1, 4px);
40
+ padding-inline: var(--astryx-spacing-3, 12px);
41
+ line-height: 1.7;
42
+ color: var(--astryx-color-text-primary, light-dark(#171717, #fafafa));
43
+ border-bottom-width: var(--astryx-border-width, 1px);
44
+ border-bottom-style: solid;
45
+ border-bottom-color: var(--astryx-color-border, light-dark(#00000014, #ffffff1a));
46
+ /* 屏幕外的行跳过布局和绘制;intrinsic size 保证滚动条长度稳定。
47
+ 这里没有虚拟化,几千行以上的日志由使用方自己窗口化。 */
48
+ content-visibility: auto;
49
+ contain-intrinsic-block-size: auto 28px;
50
+ /* 追加到尾部的新行淡入;只动透明度,减少动效时立刻出现。 */
51
+ opacity: 1;
52
+ transition-property: opacity;
53
+ transition-duration: var(--astryx-duration-fast-min, 95ms);
54
+ transition-timing-function: ease-out;
55
+ }
56
+
57
+ .astryx-log-stream__row[data-cols='full'] {
58
+ grid-template-columns: 96px 52px 128px minmax(0, 1fr);
59
+ }
60
+
61
+ .astryx-log-stream__row[data-cols='no-source'] {
62
+ grid-template-columns: 96px 52px minmax(0, 1fr);
63
+ }
64
+
65
+ .astryx-log-stream__row[data-cols='no-timestamp'] {
66
+ grid-template-columns: 52px 128px minmax(0, 1fr);
67
+ }
68
+
69
+ .astryx-log-stream__row[data-cols='message-only'] {
70
+ grid-template-columns: 52px minmax(0, 1fr);
71
+ }
72
+
73
+ @starting-style {
74
+ .astryx-log-stream__row {
75
+ opacity: 0;
76
+ }
77
+ }
78
+
79
+ @media (prefers-reduced-motion: reduce) {
80
+ .astryx-log-stream__row {
81
+ transition-duration: 0.01ms;
82
+ }
83
+ }
84
+
85
+ .astryx-log-stream__row:last-child {
86
+ border-bottom-width: 0;
87
+ }
88
+
89
+ /* 终端变体:去掉行分隔线,行距压到 1px,贴合真终端的密度。 */
90
+ .astryx-log-stream[data-variant='terminal'] .astryx-log-stream__row {
91
+ color: #b9b9c0;
92
+ border-bottom-width: 0;
93
+ padding-block: 1px;
94
+ }
95
+
96
+ /* 带详情的行本身是个按钮:整行可点,键盘也能开关。 */
97
+ .astryx-log-stream__row--button {
98
+ width: 100%;
99
+ text-align: left;
100
+ border-top-width: 0;
101
+ border-inline-width: 0;
102
+ font-family: var(--astryx-font-family-code, ui-monospace, monospace);
103
+ font-size: var(--astryx-font-size-sm, 0.75rem);
104
+ cursor: pointer;
105
+ background-color: transparent;
106
+ }
107
+
108
+ @media (hover: hover) {
109
+ .astryx-log-stream__row--button:hover:where(:not(:disabled, [aria-disabled='true'])) {
110
+ background-color: var(--astryx-color-overlay-hover, light-dark(#0000000d, #ffffff0d));
111
+ }
112
+
113
+ .astryx-log-stream[data-variant='terminal'] .astryx-log-stream__row--button:hover:where(:not(:disabled, [aria-disabled='true'])) {
114
+ background-color: #141417;
115
+ }
116
+ }
117
+
118
+ .astryx-log-stream__row--button:is(:disabled, [aria-disabled='true']) {
119
+ cursor: default;
120
+ }
121
+
122
+ /* error / warn 整行淡染一层,颜色由主题变量混出来,不另开一套色板。 */
123
+ .astryx-log-stream:not([data-variant='terminal']) .astryx-log-stream__row[data-level='error'] {
124
+ background-color: color-mix(in srgb, var(--astryx-color-error, light-dark(#76000c, #ffc4be)) 6%, transparent);
125
+ }
126
+
127
+ .astryx-log-stream:not([data-variant='terminal']) .astryx-log-stream__row[data-level='warn'] {
128
+ background-color: color-mix(in srgb, var(--astryx-color-warning, light-dark(#4b3900, #f8d36a)) 5%, transparent);
129
+ }
130
+
131
+ .astryx-log-stream__time {
132
+ color: var(--astryx-color-text-secondary, light-dark(#474747, #9e9e9e));
133
+ white-space: nowrap;
134
+ font-variant-numeric: tabular-nums;
135
+ }
136
+
137
+ .astryx-log-stream[data-variant='terminal'] .astryx-log-stream__time {
138
+ color: #8b8b94;
139
+ }
140
+
141
+ .astryx-log-stream__level {
142
+ font-size: var(--astryx-font-size-xs, 0.625rem);
143
+ font-weight: var(--astryx-font-weight-semibold, 600);
144
+ letter-spacing: 0.08em;
145
+ text-transform: uppercase;
146
+ color: var(--astryx-color-text-secondary, light-dark(#474747, #9e9e9e));
147
+ }
148
+
149
+ .astryx-log-stream__row[data-level='debug'] .astryx-log-stream__level {
150
+ color: var(--astryx-color-text-disabled, light-dark(#919191, #525252));
151
+ }
152
+
153
+ /*
154
+ * 主题的黄和红是为填充块调的,当小字用达不到 4.5:1。
155
+ * light-dark() 在浅色下把黄压深、在深色下把红提亮,都从主题变量算出来。
156
+ */
157
+ .astryx-log-stream__row[data-level='warn'] .astryx-log-stream__level {
158
+ color: light-dark(color-mix(in srgb, var(--astryx-color-warning, #4b3900) 58%, black), var(--astryx-color-warning, #f8d36a));
159
+ }
160
+
161
+ .astryx-log-stream__row[data-level='error'] .astryx-log-stream__level {
162
+ color: light-dark(var(--astryx-color-error, #76000c), color-mix(in srgb, var(--astryx-color-error, #ffc4be) 82%, white));
163
+ }
164
+
165
+ .astryx-log-stream[data-variant='terminal'] .astryx-log-stream__row[data-level='info'] .astryx-log-stream__level {
166
+ color: #b9b9c0;
167
+ }
168
+
169
+ .astryx-log-stream[data-variant='terminal'] .astryx-log-stream__row[data-level='debug'] .astryx-log-stream__level {
170
+ color: #8b8b94;
171
+ }
172
+
173
+ .astryx-log-stream[data-variant='terminal'] .astryx-log-stream__row[data-level='warn'] .astryx-log-stream__level {
174
+ color: #f2c00b;
175
+ }
176
+
177
+ .astryx-log-stream[data-variant='terminal'] .astryx-log-stream__row[data-level='error'] .astryx-log-stream__level {
178
+ color: #ff6166;
179
+ }
180
+
181
+ .astryx-log-stream__source {
182
+ color: var(--astryx-color-text-secondary, light-dark(#474747, #9e9e9e));
183
+ overflow: hidden;
184
+ text-overflow: ellipsis;
185
+ white-space: nowrap;
186
+ min-width: 0;
187
+ }
188
+
189
+ .astryx-log-stream[data-variant='terminal'] .astryx-log-stream__source {
190
+ color: #8b8b94;
191
+ }
192
+
193
+ .astryx-log-stream__message {
194
+ min-width: 0;
195
+ overflow-wrap: anywhere;
196
+ white-space: pre-wrap;
197
+ }
198
+
199
+ .astryx-log-stream[data-variant='terminal'] .astryx-log-stream__message {
200
+ color: #b9b9c0;
201
+ }
202
+
203
+ .astryx-log-stream[data-variant='terminal'] .astryx-log-stream__row[data-level='error'] .astryx-log-stream__message {
204
+ color: #ff6166;
205
+ }
206
+
207
+ .astryx-log-stream[data-variant='terminal'] .astryx-log-stream__row[data-level='warn'] .astryx-log-stream__message {
208
+ color: #f2c00b;
209
+ }
210
+
211
+ /* 展开的详情面板:比行底色深一层,和上下行之间留分隔线。 */
212
+ .astryx-log-stream__detail {
213
+ padding-block: var(--astryx-spacing-3, 12px);
214
+ padding-inline: var(--astryx-spacing-3, 12px);
215
+ background-color: var(--astryx-color-background-muted, light-dark(#f1f1f1, #1b1b1b));
216
+ border-bottom-width: var(--astryx-border-width, 1px);
217
+ border-bottom-style: solid;
218
+ border-bottom-color: var(--astryx-color-border, light-dark(#00000014, #ffffff1a));
219
+ }
220
+
221
+ .astryx-log-stream[data-variant='terminal'] .astryx-log-stream__detail {
222
+ background-color: #0e0e10;
223
+ border-bottom-color: #26262a;
224
+ color: #b9b9c0;
225
+ }
226
+
227
+ /* 「跳到最新」固定在滚动区右下角。 */
228
+ .astryx-log-stream__jump {
229
+ position: absolute;
230
+ bottom: var(--astryx-spacing-3, 12px);
231
+ inset-inline-end: var(--astryx-spacing-3, 12px);
232
+ padding-block: var(--astryx-spacing-1, 4px);
233
+ padding-inline: var(--astryx-spacing-3, 12px);
234
+ border-radius: var(--astryx-radius-full, 9999px);
235
+ border-width: var(--astryx-border-width, 1px);
236
+ border-style: solid;
237
+ border-color: var(--astryx-color-border-emphasized, light-dark(#d4d4d4, #525252));
238
+ background-color: var(--astryx-color-background-surface, light-dark(#ffffff, #262626));
239
+ color: var(--astryx-color-text-primary, light-dark(#171717, #fafafa));
240
+ font-family: var(--astryx-font-family-code, ui-monospace, monospace);
241
+ font-size: var(--astryx-font-size-sm, 0.75rem);
242
+ font-weight: var(--astryx-font-weight-medium, 500);
243
+ cursor: pointer;
244
+ box-shadow: var(--astryx-shadow-med, 0 2px 4px light-dark(oklch(0 0 0 / 5%), oklch(0 0 0 / 35%)));
245
+ }
246
+
247
+ @media (hover: hover) {
248
+ .astryx-log-stream__jump:hover:where(:not(:disabled, [aria-disabled='true'])) {
249
+ background-color: var(--astryx-color-background-muted, light-dark(#f1f1f1, #1b1b1b));
250
+ }
251
+
252
+ .astryx-log-stream[data-variant='terminal'] .astryx-log-stream__jump:hover:where(:not(:disabled, [aria-disabled='true'])) {
253
+ background-color: #1d1d21;
254
+ }
255
+ }
256
+
257
+ .astryx-log-stream__jump:is(:disabled, [aria-disabled='true']) {
258
+ cursor: default;
259
+ }
260
+
261
+ .astryx-log-stream__jump:focus-visible {
262
+ outline: var(--astryx-focus-outline-width, 2px) var(--astryx-focus-outline-style, solid) var(--astryx-focus-outline-color, currentcolor);
263
+ outline-offset: var(--astryx-focus-outline-offset, 3px);
264
+ }
265
+
266
+ .astryx-log-stream[data-variant='terminal'] .astryx-log-stream__jump {
267
+ border-color: #26262a;
268
+ background-color: #141417;
269
+ color: #e8e8ea;
270
+ }
271
+ }
@@ -0,0 +1,146 @@
1
+ @layer astryx.reset, astryx.tokens, astryx.base, astryx.components, astryx.overrides;
2
+
3
+ @layer astryx.components {
4
+ /* 触发器:一个能容纳多个标签的方框,最小 44px 保证手指点得到。 */
5
+ .astryx-mobile-tokenizer__trigger {
6
+ display: flex;
7
+ flex-wrap: wrap;
8
+ align-items: center;
9
+ gap: var(--astryx-spacing-1, 4px);
10
+ width: 100%;
11
+ min-height: 44px;
12
+ padding-block: var(--astryx-spacing-1, 4px);
13
+ padding-inline: var(--astryx-spacing-2, 8px);
14
+ border: 1px solid var(--astryx-color-border, #00000014);
15
+ border-radius: var(--astryx-radius-element, 0.625rem);
16
+ background-color: transparent;
17
+ color: var(--astryx-color-text-primary, light-dark(#0a0a0a, #fafafa));
18
+ text-align: start;
19
+ cursor: pointer;
20
+ }
21
+
22
+ /* 禁用时指针样式和普通文字一致,不做「不可点」的暗示。 */
23
+ .astryx-mobile-tokenizer__trigger:is(:disabled, [aria-disabled='true']) {
24
+ cursor: default;
25
+ }
26
+
27
+ /* 键盘聚焦时的焦点环。 */
28
+ .astryx-mobile-tokenizer__trigger:focus-visible {
29
+ outline: var(--astryx-focus-outline-width, 2px) var(--astryx-focus-outline-style, solid) var(--astryx-focus-outline-color, currentcolor);
30
+ outline-offset: var(--astryx-focus-outline-offset, 2px);
31
+ }
32
+
33
+ .astryx-mobile-tokenizer__trigger-placeholder {
34
+ color: var(--astryx-color-text-secondary, light-dark(#6a6a6a, #9e9e9e));
35
+ }
36
+
37
+ /*
38
+ * 面板内容自己撑满高度并自己管滚动,所以底栏能贴着底部。
39
+ * 顶部留出抓手的位置,左右和底部留一点边。
40
+ */
41
+ .astryx-mobile-tokenizer__sheet-body {
42
+ display: flex;
43
+ flex-direction: column;
44
+ height: 100%;
45
+ min-height: 0;
46
+ padding-inline: var(--astryx-spacing-3, 12px);
47
+ padding-block-start: var(--astryx-spacing-8, 32px);
48
+ padding-block-end: var(--astryx-spacing-3, 12px);
49
+ }
50
+
51
+ .astryx-mobile-tokenizer__filter-row {
52
+ display: flex;
53
+ align-items: flex-end;
54
+ padding-block-end: var(--astryx-spacing-2, 8px);
55
+ }
56
+
57
+ /* core 的 TextInput 会让字段框收缩到内容宽,这里必须撑满整行。 */
58
+ .astryx-mobile-tokenizer__filter-input {
59
+ flex-grow: 1;
60
+ flex-shrink: 1;
61
+ flex-basis: 0;
62
+ min-width: 0;
63
+ height: var(--astryx-size-element-lg, 36px);
64
+ min-height: var(--astryx-size-element-lg, 36px);
65
+ }
66
+
67
+ .astryx-mobile-tokenizer__list {
68
+ display: flex;
69
+ flex-direction: column;
70
+ flex-grow: 1;
71
+ overflow-y: auto;
72
+ min-height: 0;
73
+ }
74
+
75
+ /* 一行既是一个复选框,也是整块可点的区域。 */
76
+ .astryx-mobile-tokenizer__row {
77
+ display: flex;
78
+ align-items: center;
79
+ justify-content: space-between;
80
+ gap: var(--astryx-spacing-2, 8px);
81
+ width: 100%;
82
+ min-height: 44px;
83
+ padding-block: var(--astryx-spacing-2, 8px);
84
+ padding-inline: var(--astryx-spacing-1, 4px);
85
+ border-width: 0;
86
+ border-radius: var(--astryx-radius-element, 0.625rem);
87
+ background-color: transparent;
88
+ color: var(--astryx-color-text-primary, light-dark(#0a0a0a, #fafafa));
89
+ text-align: start;
90
+ cursor: pointer;
91
+ }
92
+
93
+ .astryx-mobile-tokenizer__row:is(:disabled, [aria-disabled='true']) {
94
+ cursor: default;
95
+ }
96
+
97
+ @media (hover: hover) {
98
+ .astryx-mobile-tokenizer__row:hover:where(:not(:disabled, [aria-disabled='true'])) {
99
+ background-color: var(--astryx-color-overlay-hover, light-dark(#0000000a, #ffffff12));
100
+ }
101
+ }
102
+
103
+ .astryx-mobile-tokenizer__row:focus-visible {
104
+ outline: var(--astryx-focus-outline-width, 2px) var(--astryx-focus-outline-style, solid) var(--astryx-focus-outline-color, currentcolor);
105
+ outline-offset: calc(-1 * var(--astryx-focus-outline-width, 2px));
106
+ }
107
+
108
+ .astryx-mobile-tokenizer__row-label {
109
+ flex-grow: 1;
110
+ min-width: 0;
111
+ }
112
+
113
+ .astryx-mobile-tokenizer__create-row {
114
+ display: flex;
115
+ align-items: center;
116
+ gap: var(--astryx-spacing-2, 8px);
117
+ width: 100%;
118
+ min-height: 44px;
119
+ padding-block: var(--astryx-spacing-2, 8px);
120
+ padding-inline: var(--astryx-spacing-1, 4px);
121
+ }
122
+
123
+ .astryx-mobile-tokenizer__create-label {
124
+ flex-grow: 1;
125
+ min-width: 0;
126
+ }
127
+
128
+ /* 底栏两个按钮等宽,贴着面板底部。 */
129
+ .astryx-mobile-tokenizer__footer {
130
+ display: flex;
131
+ align-items: center;
132
+ gap: var(--astryx-spacing-2, 8px);
133
+ padding-block-start: var(--astryx-spacing-3, 12px);
134
+ margin-top: auto;
135
+ }
136
+
137
+ .astryx-mobile-tokenizer__footer-action {
138
+ flex-grow: 1;
139
+ flex-basis: 0;
140
+ }
141
+
142
+ /* 没有条目时空白区撑满,让提示居中。 */
143
+ .astryx-mobile-tokenizer__empty {
144
+ flex-grow: 1;
145
+ }
146
+ }
@@ -0,0 +1,101 @@
1
+ @layer astryx.reset, astryx.tokens, astryx.base, astryx.components, astryx.overrides;
2
+
3
+ @layer astryx.components {
4
+ /* 纵向堆叠:标题、数值行、说明、趋势插槽。 */
5
+ .astryx-stat {
6
+ display: flex;
7
+ flex-direction: column;
8
+ align-items: flex-start;
9
+ gap: var(--astryx-spacing-1, 4px);
10
+ min-width: 0;
11
+ }
12
+
13
+ .astryx-stat__label {
14
+ font-size: var(--astryx-text-supporting-size, 0.75rem);
15
+ line-height: var(--astryx-text-supporting-leading, 1.6667);
16
+ font-weight: var(--astryx-font-weight-medium, 500);
17
+ color: var(--astryx-color-text-secondary, light-dark(#474747, #9e9e9e));
18
+ }
19
+
20
+ /* 数值和涨跌幅按基线对齐,字号不同也不会错位。 */
21
+ .astryx-stat__value-row {
22
+ display: flex;
23
+ align-items: baseline;
24
+ gap: var(--astryx-spacing-2, 8px);
25
+ min-width: 0;
26
+ }
27
+
28
+ /* 等宽数字:数字变化时宽度不跳。 */
29
+ .astryx-stat__value {
30
+ font-family: var(--astryx-font-family-heading, inherit);
31
+ font-weight: var(--astryx-font-weight-semibold, 600);
32
+ line-height: var(--astryx-text-heading-2-leading, 1.4);
33
+ color: var(--astryx-color-text-primary, light-dark(#171717, #fafafa));
34
+ font-variant-numeric: tabular-nums;
35
+ }
36
+
37
+ .astryx-stat[data-size='sm'] .astryx-stat__value {
38
+ font-size: var(--astryx-font-size-xl, 1.25rem);
39
+ }
40
+
41
+ .astryx-stat[data-size='md'] .astryx-stat__value {
42
+ font-size: var(--astryx-font-size-2xl, 1.5rem);
43
+ }
44
+
45
+ .astryx-stat[data-size='lg'] .astryx-stat__value {
46
+ font-size: var(--astryx-font-size-3xl, 1.8125rem);
47
+ }
48
+
49
+ .astryx-stat__delta {
50
+ display: inline-flex;
51
+ align-items: center;
52
+ gap: var(--astryx-spacing-1, 4px);
53
+ font-size: var(--astryx-text-supporting-size, 0.75rem);
54
+ line-height: var(--astryx-text-supporting-leading, 1.6667);
55
+ font-weight: var(--astryx-font-weight-medium, 500);
56
+ font-variant-numeric: tabular-nums;
57
+ white-space: nowrap;
58
+ }
59
+
60
+ .astryx-stat__delta-glyph {
61
+ flex-shrink: 0;
62
+ }
63
+
64
+ .astryx-stat__delta[data-sentiment='positive'] {
65
+ color: var(--astryx-color-success, light-dark(#00490b, #a4d6a3));
66
+ }
67
+
68
+ .astryx-stat__delta[data-sentiment='negative'] {
69
+ color: var(--astryx-color-error, light-dark(#76000c, #ffc4be));
70
+ }
71
+
72
+ .astryx-stat__delta[data-sentiment='neutral'] {
73
+ color: var(--astryx-color-text-secondary, light-dark(#474747, #9e9e9e));
74
+ }
75
+
76
+ .astryx-stat__description {
77
+ font-size: var(--astryx-text-supporting-size, 0.75rem);
78
+ line-height: var(--astryx-text-supporting-leading, 1.6667);
79
+ color: var(--astryx-color-text-secondary, light-dark(#474747, #9e9e9e));
80
+ }
81
+
82
+ /* 趋势插槽铺满父容器宽度,图能跟着 Stat 的宽度走。 */
83
+ .astryx-stat__media {
84
+ margin-block-start: var(--astryx-spacing-1, 4px);
85
+ align-self: stretch;
86
+ min-width: 0;
87
+ }
88
+
89
+ /* 只给读屏软件念的方向说明。 */
90
+ .astryx-stat__sr-only {
91
+ position: absolute;
92
+ width: 1px;
93
+ height: 1px;
94
+ padding: 0;
95
+ margin: -1px;
96
+ overflow: hidden;
97
+ clip: rect(0 0 0 0);
98
+ white-space: nowrap;
99
+ border-width: 0;
100
+ }
101
+ }