@qilin-agent/client-ui-primitives 0.0.0-stage → 3.1.4

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 (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.i18n.yaml +61 -0
  3. package/README.md +187 -2
  4. package/README.zh.md +188 -0
  5. package/lib/Button.module.css +79 -0
  6. package/lib/Checkbox.module.css +28 -0
  7. package/lib/CommandText.module.css +12 -0
  8. package/lib/ConnectionIndicator.module.css +123 -0
  9. package/lib/DiffBlock.module.css +109 -0
  10. package/lib/DisclosureRow.module.css +84 -0
  11. package/lib/FileTypeIcon.module.css +50 -0
  12. package/lib/HoverCard.module.css +70 -0
  13. package/lib/ImageLightbox.module.css +48 -0
  14. package/lib/ImagePreview.module.css +24 -0
  15. package/lib/Input.module.css +38 -0
  16. package/lib/JsonTree.module.css +345 -0
  17. package/lib/Menu.module.css +274 -0
  18. package/lib/MenuGroup.module.css +37 -0
  19. package/lib/MenuSurface.module.css +49 -0
  20. package/lib/Modal.module.css +103 -0
  21. package/lib/OnboardingSurface.module.css +29 -0
  22. package/lib/PathLabel.module.css +28 -0
  23. package/lib/Pill.module.css +27 -0
  24. package/lib/ReadBlock.module.css +111 -0
  25. package/lib/RiskConfirmation.module.css +73 -0
  26. package/lib/SearchBlock.module.css +112 -0
  27. package/lib/SegmentedControl.module.css +76 -0
  28. package/lib/ShortcutKeys.module.css +33 -0
  29. package/lib/StateDot.module.css +73 -0
  30. package/lib/Switch.module.css +50 -0
  31. package/lib/Tag.module.css +57 -0
  32. package/lib/TerminalBlock.module.css +205 -0
  33. package/lib/TextShimmer.module.css +88 -0
  34. package/lib/Toast.module.css +76 -0
  35. package/lib/Tooltip.module.css +52 -0
  36. package/lib/WebBlock.module.css +141 -0
  37. package/lib/index.js +10647 -0
  38. package/lib/markdown/CodeBlock.module.css +130 -0
  39. package/lib/markdown/JsonBlock.module.css +32 -0
  40. package/lib/markdown/MarkdownText.module.css +485 -0
  41. package/lib/markdown/mermaid.module.css +49 -0
  42. package/lib/settings-form/SettingsForm.module.css +53 -0
  43. package/lib/settings-form/fields.module.css +145 -0
  44. package/lib/types/Button.d.ts +18 -0
  45. package/lib/types/Checkbox.d.ts +20 -0
  46. package/lib/types/CodeFileIcon.d.ts +12 -0
  47. package/lib/types/CommandText.d.ts +16 -0
  48. package/lib/types/ConnectionIndicator.d.ts +26 -0
  49. package/lib/types/DiffBlock.d.ts +52 -0
  50. package/lib/types/DisclosureRow.d.ts +36 -0
  51. package/lib/types/FileTypeIcon.d.ts +43 -0
  52. package/lib/types/FoldToggle.d.ts +20 -0
  53. package/lib/types/HoverCard.d.ts +32 -0
  54. package/lib/types/ImageLightbox.d.ts +27 -0
  55. package/lib/types/ImagePreview.d.ts +12 -0
  56. package/lib/types/Input.d.ts +12 -0
  57. package/lib/types/JsonTree.d.ts +59 -0
  58. package/lib/types/LinkIcon.d.ts +49 -0
  59. package/lib/types/Menu.d.ts +133 -0
  60. package/lib/types/MenuGroup.d.ts +22 -0
  61. package/lib/types/MenuSurface.d.ts +16 -0
  62. package/lib/types/Modal.d.ts +44 -0
  63. package/lib/types/OnboardingSurface.d.ts +11 -0
  64. package/lib/types/PathLabel.d.ts +13 -0
  65. package/lib/types/Pill.d.ts +13 -0
  66. package/lib/types/QilinSeal.d.ts +18 -0
  67. package/lib/types/ReadBlock.d.ts +47 -0
  68. package/lib/types/ReferenceIcon.d.ts +16 -0
  69. package/lib/types/RiskConfirmation.d.ts +20 -0
  70. package/lib/types/SearchBlock.d.ts +72 -0
  71. package/lib/types/SegmentedControl.d.ts +35 -0
  72. package/lib/types/ShortcutKeys.d.ts +11 -0
  73. package/lib/types/SiteGlyph.d.ts +25 -0
  74. package/lib/types/StateDot.d.ts +18 -0
  75. package/lib/types/Switch.d.ts +20 -0
  76. package/lib/types/Tag.d.ts +32 -0
  77. package/lib/types/TerminalBlock.d.ts +68 -0
  78. package/lib/types/TextShimmer.d.ts +22 -0
  79. package/lib/types/Toast.d.ts +36 -0
  80. package/lib/types/Tooltip.d.ts +45 -0
  81. package/lib/types/WebBlock.d.ts +61 -0
  82. package/lib/types/ansi.d.ts +17 -0
  83. package/lib/types/clipboard.d.ts +9 -0
  84. package/lib/types/code-file-icon-artwork.d.ts +54 -0
  85. package/lib/types/code-file-types.d.ts +30 -0
  86. package/lib/types/darwin-desktop.d.ts +18 -0
  87. package/lib/types/file-size.d.ts +17 -0
  88. package/lib/types/focus.d.ts +9 -0
  89. package/lib/types/head-tail-cap.d.ts +23 -0
  90. package/lib/types/icons/index.d.ts +211 -0
  91. package/lib/types/icons/props.d.ts +9 -0
  92. package/lib/types/index.d.ts +92 -0
  93. package/lib/types/input-modality.d.ts +19 -0
  94. package/lib/types/keyboard-composition.d.ts +11 -0
  95. package/lib/types/markdown/CodeBlock.d.ts +30 -0
  96. package/lib/types/markdown/JsonBlock.d.ts +8 -0
  97. package/lib/types/markdown/MarkdownDelegate.d.ts +46 -0
  98. package/lib/types/markdown/MarkdownText.d.ts +54 -0
  99. package/lib/types/markdown/cjkFriendlyStrong.d.ts +9 -0
  100. package/lib/types/markdown/file-link.d.ts +13 -0
  101. package/lib/types/markdown/highlight.d.ts +142 -0
  102. package/lib/types/markdown/incremental.d.ts +80 -0
  103. package/lib/types/markdown/katex.d.ts +27 -0
  104. package/lib/types/markdown/local-image-syntax.d.ts +10 -0
  105. package/lib/types/markdown/mathCompatibility.d.ts +11 -0
  106. package/lib/types/markdown/mermaid.d.ts +39 -0
  107. package/lib/types/markdown/parse.d.ts +25 -0
  108. package/lib/types/markdown/plain-text.d.ts +22 -0
  109. package/lib/types/markdown/render.d.ts +158 -0
  110. package/lib/types/markdown/useViewportHighlighting.d.ts +11 -0
  111. package/lib/types/overlay-top-margin.d.ts +8 -0
  112. package/lib/types/pointer-grace.d.ts +22 -0
  113. package/lib/types/rank-by-name.d.ts +22 -0
  114. package/lib/types/relative-time.d.ts +23 -0
  115. package/lib/types/seal-geometry.d.ts +27 -0
  116. package/lib/types/seal-glyphs.d.ts +9 -0
  117. package/lib/types/settings-form/SettingsForm.d.ts +46 -0
  118. package/lib/types/settings-form/fields.d.ts +70 -0
  119. package/lib/types/settings-form/form-model.d.ts +214 -0
  120. package/lib/types/use-copy-feedback.d.ts +14 -0
  121. package/lib/types/useAnchoredMaxHeight.d.ts +11 -0
  122. package/lib/types/useAnchoredPosition.d.ts +34 -0
  123. package/lib/types/useDismissOnOutsidePointer.d.ts +11 -0
  124. package/lib/types/useModalLayer.d.ts +27 -0
  125. package/lib/types/user-text.d.ts +37 -0
  126. package/lib/user-text.module.css +43 -0
  127. package/package.json +63 -3
@@ -0,0 +1,103 @@
1
+ /* Full-viewport layer (figma Mask + Dialog 451:18655): mask + centered card. */
2
+ .root {
3
+ position: fixed;
4
+ inset: 0;
5
+ z-index: 1000;
6
+ display: flex;
7
+ align-items: center;
8
+ justify-content: center;
9
+ padding: 24px;
10
+ }
11
+
12
+ /* User/spec mask: rgba(0,0,0,0.24) + blur(2px) via --qilin-alias-bg-mask-1 /
13
+ --qilin-mask-blur (light); dark theme raises mask opacity. */
14
+ .mask {
15
+ position: absolute;
16
+ inset: 0;
17
+ background: var(--qilin-alias-bg-mask-1);
18
+ backdrop-filter: var(--qilin-mask-blur);
19
+ }
20
+
21
+ /* Dialog card: r24, elevation-prominent, layer-2 fill, pb 24. */
22
+ .dialog {
23
+ position: relative;
24
+ z-index: 1;
25
+ display: flex;
26
+ flex-direction: column;
27
+ gap: 20px;
28
+ width: min(380px, 100%);
29
+ padding: 0 0 24px;
30
+ overflow: hidden;
31
+ border: 0;
32
+ border-radius: 24px;
33
+ background: var(--qilin-alias-bg-layer-2);
34
+ box-shadow: var(--qilin-elevation-prominent);
35
+ }
36
+
37
+ .content {
38
+ display: flex;
39
+ flex-direction: column;
40
+ width: 100%;
41
+ }
42
+
43
+ /* Header row (figma Title row): pad l24/t22/r14/b12, SPACE_BETWEEN —
44
+ * title left, close button right. */
45
+ .header {
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: space-between;
49
+ gap: 8px;
50
+ padding: 22px 14px 12px 24px;
51
+ }
52
+
53
+ .title {
54
+ margin: 0;
55
+ font-size: 16px;
56
+ line-height: 24px;
57
+ font-weight: 500; /* figma wt510, rendered 500 */
58
+ color: var(--qilin-alias-label-primary);
59
+ }
60
+
61
+ .close {
62
+ flex: none;
63
+ display: inline-flex;
64
+ align-items: center;
65
+ justify-content: center;
66
+ width: 28px;
67
+ height: 28px;
68
+ border: none;
69
+ border-radius: 8px;
70
+ background: transparent;
71
+ cursor: pointer;
72
+ color: var(--qilin-alias-label-secondary);
73
+ }
74
+
75
+ .close:hover {
76
+ background: var(--qilin-alias-interactive-bg-hover);
77
+ }
78
+
79
+ /* Description and body share the 332px content column (24px side pads). */
80
+ .description {
81
+ margin: 0;
82
+ padding: 0 24px;
83
+ font-size: 14px;
84
+ line-height: 22px;
85
+ font-weight: 400;
86
+ color: var(--qilin-alias-label-primary);
87
+ }
88
+
89
+ .body {
90
+ display: flex;
91
+ flex-direction: column;
92
+ min-width: 0;
93
+ margin-top: 20px;
94
+ padding: 0 24px;
95
+ }
96
+
97
+ .footer {
98
+ display: flex;
99
+ align-items: center;
100
+ justify-content: flex-end;
101
+ gap: 8px;
102
+ padding: 0 24px;
103
+ }
@@ -0,0 +1,29 @@
1
+ /* First-run stage: keep the product top bar visible, then let onboarding own
2
+ the complete workspace instead of presenting another settings modal. */
3
+ .onboardingOverlay {
4
+ position: fixed;
5
+ inset: 0;
6
+ z-index: 1100;
7
+ }
8
+
9
+ /* Mask */
10
+ .onboardingMask {
11
+ position: absolute;
12
+ left: 0px;
13
+ right: 0px;
14
+ top: 80px;
15
+ bottom: 0px;
16
+ background: rgba(0, 0, 0, 0.24);
17
+ /* Mask-blur */
18
+ backdrop-filter: blur(2px);
19
+ }
20
+
21
+ .onboardingStage {
22
+ position: absolute;
23
+ z-index: 1;
24
+ inset: 0;
25
+ display: flex;
26
+ justify-content: center;
27
+ overflow: hidden;
28
+ background: var(--qilin-alias-bg-layer-1);
29
+ }
@@ -0,0 +1,28 @@
1
+ /* Auto margin keeps fitting text at the left. On overflow it becomes zero,
2
+ so flex-end preserves the filename suffix at the right edge. */
3
+ .path {
4
+ display: flex;
5
+ flex: 1 1 auto;
6
+ justify-content: flex-end;
7
+ min-width: 0;
8
+ overflow: hidden;
9
+ font-size: 12px;
10
+ white-space: nowrap;
11
+ }
12
+
13
+ .path[data-path-clipped] {
14
+ mask-image: linear-gradient(to right, transparent, black 28px);
15
+ }
16
+
17
+ .text {
18
+ flex: none;
19
+ margin-right: auto;
20
+ }
21
+
22
+ .directory {
23
+ color: var(--qilin-alias-label-tertiary);
24
+ }
25
+
26
+ .name {
27
+ color: var(--qilin-alias-label-primary);
28
+ }
@@ -0,0 +1,27 @@
1
+ .pill {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ gap: 4px;
5
+ height: 24px;
6
+ padding: 0 8px;
7
+ border: none;
8
+ border-radius: 12px;
9
+ font-size: 12px;
10
+ line-height: 18px;
11
+ color: var(--qilin-alias-label-secondary);
12
+ background: var(--qilin-alias-bg-layer-2);
13
+ }
14
+
15
+ .interactive {
16
+ cursor: pointer;
17
+ }
18
+
19
+ .interactive:hover {
20
+ background: var(--qilin-alias-interactive-bg-hover);
21
+ }
22
+
23
+ .active {
24
+ color: var(--qilin-alias-label-primary);
25
+ background: var(--qilin-alias-button-ghost-active-fill);
26
+ box-shadow: inset 0 0 0 1px var(--qilin-alias-button-ghost-active-border);
27
+ }
@@ -0,0 +1,111 @@
1
+ .block {
2
+ --dsl-read-radius: 12px;
3
+ --dsl-read-line-height: 22px;
4
+ /* Fixed-width gutter column for the line numbers, so the content edge stays
5
+ put down the whole window regardless of how wide the numbers grow. */
6
+ --dsl-read-gutter: 48px;
7
+
8
+ position: relative;
9
+ margin: 16px 0;
10
+ color: var(--qilin-alias-label-primary);
11
+ background: var(--qilin-alias-markdown-code-block);
12
+ border-radius: var(--dsl-read-radius);
13
+ }
14
+
15
+ .banner {
16
+ display: flex;
17
+ justify-content: space-between;
18
+ align-items: center;
19
+ gap: 12px;
20
+ padding: 9px 14px;
21
+ background: var(--qilin-alias-markdown-code-block-banner);
22
+ border-top-left-radius: var(--dsl-read-radius);
23
+ border-top-right-radius: var(--dsl-read-radius);
24
+ }
25
+
26
+ .label {
27
+ min-width: 0;
28
+ overflow: hidden;
29
+ text-overflow: ellipsis;
30
+ white-space: nowrap;
31
+ color: var(--qilin-alias-label-primary);
32
+ font-family: var(--ds-font-family-code);
33
+ font-size: 12px;
34
+ line-height: 18px;
35
+ }
36
+
37
+ .action {
38
+ display: flex;
39
+ align-items: center;
40
+ flex-shrink: 0;
41
+ gap: 12px;
42
+ }
43
+
44
+ .count {
45
+ color: var(--qilin-alias-label-tertiary);
46
+ font: var(--qilin-font-xs-13);
47
+ }
48
+
49
+ .lang {
50
+ color: var(--qilin-alias-label-tertiary);
51
+ font-family: var(--ds-font-family-code);
52
+ font-size: 12px;
53
+ line-height: 18px;
54
+ }
55
+
56
+ .copyButton {
57
+ background-color: transparent;
58
+ border: none;
59
+ padding: 0;
60
+ margin: 0;
61
+ color: var(--qilin-alias-label-secondary);
62
+ cursor: pointer;
63
+ font: var(--qilin-font-xs-13);
64
+ }
65
+
66
+ .body {
67
+ padding: 12px 0;
68
+ font: var(--qilin-font-markdown-code-block);
69
+ overflow-x: auto;
70
+ overflow-y: hidden;
71
+ }
72
+
73
+ /* One row per file line: a fixed gutter column, then the content. No wrapping —
74
+ a source line's leading whitespace is meaningful and scrolls sideways. */
75
+ .line {
76
+ display: flex;
77
+ min-height: var(--dsl-read-line-height);
78
+ line-height: var(--dsl-read-line-height);
79
+ white-space: pre;
80
+ }
81
+
82
+ .gutter {
83
+ flex: none;
84
+ width: var(--dsl-read-gutter);
85
+ padding-right: 14px;
86
+ text-align: right;
87
+ color: var(--qilin-alias-label-tertiary);
88
+ /* The gutter is chrome, not content: keep it out of a text selection so a
89
+ copy of the visible rows carries the source, not the line numbers. */
90
+ user-select: none;
91
+ }
92
+
93
+ .content {
94
+ color: var(--qilin-alias-label-primary);
95
+ }
96
+
97
+ .expand {
98
+ display: block;
99
+ width: 100%;
100
+ padding: 0 0 0 var(--dsl-read-gutter);
101
+ border: none;
102
+ background-color: transparent;
103
+ color: var(--qilin-alias-label-tertiary);
104
+ cursor: pointer;
105
+ font: inherit;
106
+ text-align: left;
107
+ }
108
+
109
+ .expand:hover {
110
+ color: var(--qilin-alias-label-secondary);
111
+ }
@@ -0,0 +1,73 @@
1
+ .confirmation {
2
+ width: min(440px, 100%);
3
+ max-height: calc(100vh - 48px);
4
+ overflow: hidden;
5
+ }
6
+
7
+ .confirmationContent {
8
+ min-height: 0;
9
+ overflow-y: auto;
10
+ overscroll-behavior: contain;
11
+ }
12
+
13
+ @supports (height: 100dvh) {
14
+ .confirmation {
15
+ max-height: calc(100dvh - 48px);
16
+ }
17
+ }
18
+
19
+ .warning {
20
+ display: flex;
21
+ align-items: flex-start;
22
+ gap: 10px;
23
+ color: var(--qilin-alias-label-secondary);
24
+ font-size: 14px;
25
+ line-height: 22px;
26
+ }
27
+
28
+ .warning p {
29
+ margin: 0;
30
+ }
31
+
32
+ .warningIcon {
33
+ flex: none;
34
+ margin-top: 2px;
35
+ color: var(--qilin-alias-state-error-primary);
36
+ }
37
+
38
+ .acknowledgement {
39
+ display: flex;
40
+ align-items: flex-start;
41
+ gap: 10px;
42
+ margin-top: 20px;
43
+ color: var(--qilin-alias-label-primary);
44
+ font-size: 14px;
45
+ line-height: 22px;
46
+ cursor: pointer;
47
+ }
48
+
49
+ .acknowledgement input {
50
+ flex: none;
51
+ width: 16px;
52
+ height: 16px;
53
+ margin: 3px 0 0;
54
+ accent-color: var(--qilin-alias-button-primary-fill);
55
+ cursor: pointer;
56
+ }
57
+
58
+ .acknowledgement input:focus-visible {
59
+ outline: var(--qilin-focus-ring-width) solid var(--qilin-focus-ring-color, var(--qilin-alias-state-business-primary));
60
+ outline-offset: 2px;
61
+ }
62
+
63
+ .acknowledgement input:disabled {
64
+ cursor: default;
65
+ }
66
+
67
+ .modalAction {
68
+ min-width: 72px;
69
+ }
70
+
71
+ .confirmAction {
72
+ min-width: 136px;
73
+ }
@@ -0,0 +1,112 @@
1
+ .block {
2
+ --dsl-search-radius: 12px;
3
+ --dsl-search-line-height: 22px;
4
+
5
+ position: relative;
6
+ margin: 16px 0;
7
+ color: var(--qilin-alias-label-primary);
8
+ background: var(--qilin-alias-markdown-code-block);
9
+ border-radius: var(--dsl-search-radius);
10
+ }
11
+
12
+ .header {
13
+ display: flex;
14
+ align-items: center;
15
+ gap: 12px;
16
+ padding: 9px 14px;
17
+ background: var(--qilin-alias-markdown-code-block-banner);
18
+ border-top-left-radius: var(--dsl-search-radius);
19
+ border-top-right-radius: var(--dsl-search-radius);
20
+ }
21
+
22
+ .summary {
23
+ flex: 1;
24
+ min-width: 0;
25
+ overflow: hidden;
26
+ text-overflow: ellipsis;
27
+ white-space: nowrap;
28
+ font: var(--qilin-font-xs-13);
29
+ color: var(--qilin-alias-label-secondary);
30
+ }
31
+
32
+ .copyButton {
33
+ flex: none;
34
+ background-color: transparent;
35
+ border: none;
36
+ padding: 0;
37
+ margin: 0;
38
+ color: var(--qilin-alias-label-secondary);
39
+ cursor: pointer;
40
+ font: var(--qilin-font-xs-13);
41
+ }
42
+
43
+ .body {
44
+ padding: 8px 14px 12px 0;
45
+ font: var(--qilin-font-markdown-code-block);
46
+ overflow-x: auto;
47
+ overflow-y: hidden;
48
+ }
49
+
50
+ /* No wrapping: a match line or a path keeps its content on one row and scrolls
51
+ sideways instead of folding. */
52
+ .line {
53
+ min-height: var(--dsl-search-line-height);
54
+ padding-left: 14px;
55
+ white-space: pre;
56
+ }
57
+
58
+ /* The 1-based line number ahead of a grep match line, dimmed so the match text
59
+ stays the salient content. */
60
+ .lineNumber {
61
+ color: var(--qilin-alias-label-tertiary);
62
+ }
63
+
64
+ /* A file group's header: a bold path label plus its match count, the whole row
65
+ the collapse control. */
66
+ .fileHeader {
67
+ display: flex;
68
+ align-items: baseline;
69
+ gap: 8px;
70
+ width: 100%;
71
+ min-height: var(--dsl-search-line-height);
72
+ padding: 0 14px;
73
+ border: none;
74
+ background-color: transparent;
75
+ cursor: pointer;
76
+ font: inherit;
77
+ text-align: left;
78
+ }
79
+
80
+ .filePath {
81
+ min-width: 0;
82
+ font-weight: 600;
83
+ color: var(--qilin-alias-label-primary);
84
+ white-space: pre;
85
+ }
86
+
87
+ .fileCount {
88
+ flex: none;
89
+ color: var(--qilin-alias-label-tertiary);
90
+ }
91
+
92
+ .expand {
93
+ display: block;
94
+ width: 100%;
95
+ padding: 0 14px;
96
+ border: none;
97
+ background-color: transparent;
98
+ color: var(--qilin-alias-label-tertiary);
99
+ cursor: pointer;
100
+ font: inherit;
101
+ text-align: left;
102
+ }
103
+
104
+ .expand:hover {
105
+ color: var(--qilin-alias-label-secondary);
106
+ }
107
+
108
+ .empty {
109
+ padding: 12px 14px;
110
+ font: var(--qilin-font-markdown-code-block);
111
+ color: var(--qilin-alias-label-tertiary);
112
+ }
@@ -0,0 +1,76 @@
1
+ /* The track is the same translucent fill the hover state uses, so it reads as
2
+ * a place rather than a second button; the one raised part is the indicator,
3
+ * a white pill that slides under the selected segment. Its stroke rides inside
4
+ * the soft elevation shadow (border: 0), the way every elevated surface draws
5
+ * its edge, so the elevation gate keeps it off the alias-border set.
6
+ *
7
+ * Segments are equal-width grid tracks sized to the widest label, which is
8
+ * what lets one indicator be placed arithmetically: the tablist carries the
9
+ * segment count and the selected index as custom properties, and the indicator
10
+ * derives its width and offset from them instead of measuring the DOM. */
11
+ .control {
12
+ position: relative;
13
+ display: inline-grid;
14
+ grid-auto-flow: column;
15
+ grid-auto-columns: 1fr;
16
+ gap: 2px;
17
+ padding: 4px;
18
+ border-radius: var(--qilin-radius-md);
19
+ background: var(--qilin-alias-interactive-bg-hover);
20
+ }
21
+
22
+ .indicator {
23
+ position: absolute;
24
+ top: 4px;
25
+ left: 4px;
26
+ width: calc((100% - 8px - 2px * (var(--qilin-segment-count) - 1)) / var(--qilin-segment-count));
27
+ height: calc(100% - 8px);
28
+ border: 0;
29
+ border-radius: var(--qilin-radius-sm);
30
+ background: var(--qilin-alias-bg-layer-1);
31
+ box-shadow: var(--qilin-elevation-soft);
32
+ transform: translateX(calc(var(--qilin-segment-index) * (100% + 2px)));
33
+ transition: transform 160ms ease;
34
+ pointer-events: none;
35
+ }
36
+
37
+ .tab {
38
+ box-sizing: border-box;
39
+ position: relative;
40
+ z-index: 1;
41
+ height: 28px;
42
+ padding: 0 16px;
43
+ border: 0;
44
+ border-radius: var(--qilin-radius-sm);
45
+ background: transparent;
46
+ color: var(--qilin-alias-label-secondary);
47
+ font: inherit;
48
+ font-size: 13px;
49
+ line-height: 20px;
50
+ font-weight: 500;
51
+ white-space: nowrap;
52
+ cursor: pointer;
53
+ transition: color 120ms ease;
54
+ }
55
+
56
+ .tab:hover:not(:disabled),
57
+ .tab[aria-selected='true'] {
58
+ color: var(--qilin-alias-label-primary);
59
+ }
60
+
61
+ .tab:disabled {
62
+ cursor: default;
63
+ opacity: 0.4;
64
+ }
65
+
66
+ .tab:focus-visible {
67
+ outline: var(--qilin-focus-ring-width) solid var(--qilin-focus-ring-color, var(--qilin-alias-state-business-primary));
68
+ outline-offset: -2px;
69
+ }
70
+
71
+ @media (prefers-reduced-motion: reduce) {
72
+ .indicator,
73
+ .tab {
74
+ transition: none;
75
+ }
76
+ }
@@ -0,0 +1,33 @@
1
+ .keys {
2
+ display: inline-flex;
3
+ flex: none;
4
+ align-items: center;
5
+ gap: 3px;
6
+ color: var(--qilin-alias-label-tertiary);
7
+ font-size: 12px;
8
+ line-height: 16px;
9
+ white-space: nowrap;
10
+ }
11
+
12
+ .key {
13
+ box-sizing: border-box;
14
+ display: inline-flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ font: inherit;
18
+ }
19
+
20
+ .tooltip { color: inherit; font-size: 11px; line-height: 14px; gap: 2px; }
21
+ .tooltip .key {
22
+ min-width: 16px;
23
+ height: 16px;
24
+ padding: 0 2px;
25
+ border-radius: 4px;
26
+ border: 0;
27
+ background: var(--qilin-alias-tooltip-key-bg);
28
+ }
29
+
30
+ .separator { font: inherit; }
31
+
32
+ .joined { box-sizing: border-box; height: 16px; padding: 0 4px; border-radius: 4px; background: var(--qilin-alias-tooltip-key-bg); }
33
+ .joined .key { min-width: 0; height: auto; padding: 0; border: 0; background: transparent; }
@@ -0,0 +1,73 @@
1
+ /* Ongoing blue has no alias token (state-business-primary is the 500 step,
2
+ * not this 450) — component-level var pinned to the static scale instead. */
3
+ .dot,
4
+ .matrix {
5
+ --qilin-state-ongoing: var(--qilin-static-deepseek-450);
6
+ }
7
+
8
+ /* Solid states: same-color halo via a 0.10-opacity outer layer (::before)
9
+ * with a 6/10-scale solid core. Layer color rides currentColor set per state. */
10
+ .dot {
11
+ position: relative;
12
+ display: inline-block;
13
+ flex: none;
14
+ }
15
+
16
+ .dot::before {
17
+ content: '';
18
+ position: absolute;
19
+ inset: 0;
20
+ border-radius: 50%;
21
+ corner-shape: round;
22
+ background: currentColor;
23
+ opacity: 0.1;
24
+ }
25
+
26
+ .dot::after {
27
+ content: '';
28
+ position: absolute;
29
+ inset: 20%;
30
+ border-radius: 50%;
31
+ corner-shape: round;
32
+ background: currentColor;
33
+ }
34
+
35
+ .dot[data-state='done'] {
36
+ color: var(--qilin-alias-state-success-primary);
37
+ }
38
+
39
+ .dot[data-state='warning'] {
40
+ color: var(--qilin-alias-state-warn-primary);
41
+ }
42
+
43
+ .dot[data-state='error'] {
44
+ color: var(--qilin-alias-state-error-primary);
45
+ }
46
+
47
+ /* Idle is the absence of activity, not a fourth outcome: it stays on the
48
+ * tertiary label color so it recedes beside the three outcome colors. */
49
+ .dot[data-state='idle'] {
50
+ color: var(--qilin-alias-label-tertiary);
51
+ }
52
+
53
+ /* Pixel chase: each outer cell holds a discrete brightness step (flat keyframe
54
+ * holds, no tweening — the retro feel), peaking when the chase hits it and
55
+ * decaying over the next three cells. Phase offsets come from per-rect
56
+ * animation-delay (index * -125ms) set inline by the component. */
57
+ .matrix {
58
+ flex: none;
59
+ color: var(--qilin-state-ongoing);
60
+ }
61
+
62
+ .cell {
63
+ fill: currentColor;
64
+ opacity: 0.15;
65
+ animation: qilin-state-dot-chase 1s infinite;
66
+ }
67
+
68
+ @keyframes qilin-state-dot-chase {
69
+ 0%, 12.4% { opacity: 1; }
70
+ 12.5%, 24.9% { opacity: 0.6; }
71
+ 25%, 37.4% { opacity: 0.35; }
72
+ 37.5%, 100% { opacity: 0.15; }
73
+ }