cardbuddy 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.md +211 -0
  2. package/dist/cli.js +76 -0
  3. package/dist/config.js +63 -0
  4. package/dist/core.js +2 -0
  5. package/dist/preview-editor/assets/apple-touch-icon-BQcES9QK.png +0 -0
  6. package/dist/preview-editor/assets/default-image-wzZYQ86K.png +0 -0
  7. package/dist/preview-editor/assets/favicon-16-Cg6Nrosk.png +0 -0
  8. package/dist/preview-editor/assets/favicon-32-B_1744sp.png +0 -0
  9. package/dist/preview-editor/assets/index-BYB0hngG.js +72 -0
  10. package/dist/preview-editor/assets/index-CpBZEQDy.css +1 -0
  11. package/dist/preview-editor/assets/site-icon-CFMif8z4.png +0 -0
  12. package/dist/preview-editor/assets/svg-symbols-B2exBrAr.svg +118 -0
  13. package/dist/preview-editor/index.html +17 -0
  14. package/dist/server-core/_vendor/finmall-env.mjs +96 -0
  15. package/dist/server-core/assetProxy.js +154 -0
  16. package/dist/server-core/bbox.js +153 -0
  17. package/dist/server-core/browser.js +108 -0
  18. package/dist/server-core/buildDataFromSchema.js +50 -0
  19. package/dist/server-core/cardFiles.js +188 -0
  20. package/dist/server-core/cardHistory.js +156 -0
  21. package/dist/server-core/cardImage.js +85 -0
  22. package/dist/server-core/cardImportMeta.js +139 -0
  23. package/dist/server-core/cardListCache.js +68 -0
  24. package/dist/server-core/cardOpenFolder.js +135 -0
  25. package/dist/server-core/cardSave.js +127 -0
  26. package/dist/server-core/cardSidecar.js +72 -0
  27. package/dist/server-core/cardSync.js +259 -0
  28. package/dist/server-core/cardValidate.js +134 -0
  29. package/dist/server-core/index.js +82 -0
  30. package/dist/server-core/logger.js +241 -0
  31. package/dist/server-core/nodeModels.js +52 -0
  32. package/dist/server-core/renderStatus.js +207 -0
  33. package/dist/server-core/screenshot.js +123 -0
  34. package/dist/server-core/sliceStatic.js +44 -0
  35. package/dist/standalone.js +447 -0
  36. package/dist/vendored/App.vue +2330 -0
  37. package/dist/vendored/api.js +142 -0
  38. package/dist/vendored/assets/apple-touch-icon.png +0 -0
  39. package/dist/vendored/assets/default-image.png +0 -0
  40. package/dist/vendored/assets/favicon-16.png +0 -0
  41. package/dist/vendored/assets/favicon-32.png +0 -0
  42. package/dist/vendored/assets/logo.png +0 -0
  43. package/dist/vendored/assets/site-icon.png +0 -0
  44. package/dist/vendored/assets/svg-symbols.svg +118 -0
  45. package/dist/vendored/assets/svgIcons/icons.svg +54 -0
  46. package/dist/vendored/assets/svgIcons/index.js +39 -0
  47. package/dist/vendored/components/AbsoluteLayout.vue +42 -0
  48. package/dist/vendored/components/AlertCardInstance.vue +58 -0
  49. package/dist/vendored/components/AlertCardLayer.vue +59 -0
  50. package/dist/vendored/components/ApiPayloadTree.vue +168 -0
  51. package/dist/vendored/components/AppToast.vue +37 -0
  52. package/dist/vendored/components/Button.vue +37 -0
  53. package/dist/vendored/components/Canvas.vue +65 -0
  54. package/dist/vendored/components/CardGallery.vue +243 -0
  55. package/dist/vendored/components/CardPreview.vue +72 -0
  56. package/dist/vendored/components/CardSelect.vue +200 -0
  57. package/dist/vendored/components/CheckGroup.vue +131 -0
  58. package/dist/vendored/components/Column.vue +25 -0
  59. package/dist/vendored/components/ComponentPalette.vue +294 -0
  60. package/dist/vendored/components/ConfirmDialog.vue +39 -0
  61. package/dist/vendored/components/CustomPlaceholder.vue +33 -0
  62. package/dist/vendored/components/DatePickerOverlay.vue +345 -0
  63. package/dist/vendored/components/DebugPanel.vue +806 -0
  64. package/dist/vendored/components/DeviceOverlay.vue +70 -0
  65. package/dist/vendored/components/Divider.vue +22 -0
  66. package/dist/vendored/components/Dropdown.vue +191 -0
  67. package/dist/vendored/components/EventEditor.vue +247 -0
  68. package/dist/vendored/components/GridLayout.vue +67 -0
  69. package/dist/vendored/components/HoverGuides.vue +122 -0
  70. package/dist/vendored/components/Icon.vue +34 -0
  71. package/dist/vendored/components/Image.vue +34 -0
  72. package/dist/vendored/components/Input.vue +90 -0
  73. package/dist/vendored/components/JsonTree.vue +516 -0
  74. package/dist/vendored/components/ListLayout.vue +68 -0
  75. package/dist/vendored/components/LogValue.vue +51 -0
  76. package/dist/vendored/components/Map.vue +110 -0
  77. package/dist/vendored/components/MultiColumn.vue +49 -0
  78. package/dist/vendored/components/MultiText.vue +58 -0
  79. package/dist/vendored/components/NodeInspector.vue +295 -0
  80. package/dist/vendored/components/PickerOverlay.vue +131 -0
  81. package/dist/vendored/components/ProgressSlider.vue +167 -0
  82. package/dist/vendored/components/PropControl.vue +241 -0
  83. package/dist/vendored/components/PropTree.vue +128 -0
  84. package/dist/vendored/components/RadioButton.vue +74 -0
  85. package/dist/vendored/components/RemotePreview.vue +171 -0
  86. package/dist/vendored/components/RenderTree.vue +437 -0
  87. package/dist/vendored/components/Root.vue +30 -0
  88. package/dist/vendored/components/Row.vue +25 -0
  89. package/dist/vendored/components/SelectionOverlay.vue +105 -0
  90. package/dist/vendored/components/StructureTree.vue +949 -0
  91. package/dist/vendored/components/SvgCode.vue +19 -0
  92. package/dist/vendored/components/SvgSymbols.vue +30 -0
  93. package/dist/vendored/components/Switch.vue +38 -0
  94. package/dist/vendored/components/Text.vue +32 -0
  95. package/dist/vendored/components/Video.vue +70 -0
  96. package/dist/vendored/components/ViewContainer.vue +52 -0
  97. package/dist/vendored/components/base.js +12 -0
  98. package/dist/vendored/components/icons.js +47 -0
  99. package/dist/vendored/components/index.js +58 -0
  100. package/dist/vendored/components/setters/SetBgPosition.vue +64 -0
  101. package/dist/vendored/components/setters/SetBgSize.vue +90 -0
  102. package/dist/vendored/components/setters/SetCode.vue +90 -0
  103. package/dist/vendored/components/setters/SetColor.vue +244 -0
  104. package/dist/vendored/components/setters/SetCustomSelect.vue +58 -0
  105. package/dist/vendored/components/setters/SetExpr.vue +107 -0
  106. package/dist/vendored/components/setters/SetGradient.vue +125 -0
  107. package/dist/vendored/components/setters/SetGrid9.vue +68 -0
  108. package/dist/vendored/components/setters/SetIconSelect.vue +67 -0
  109. package/dist/vendored/components/setters/SetIfForm.vue +61 -0
  110. package/dist/vendored/components/setters/SetImage.vue +43 -0
  111. package/dist/vendored/components/setters/SetList.vue +139 -0
  112. package/dist/vendored/components/setters/SetNumber.vue +118 -0
  113. package/dist/vendored/components/setters/SetNumberStyle.vue +113 -0
  114. package/dist/vendored/components/setters/SetPadding.vue +77 -0
  115. package/dist/vendored/components/setters/SetSegment.vue +160 -0
  116. package/dist/vendored/components/setters/SetSelect.vue +51 -0
  117. package/dist/vendored/components/setters/SetSwitch.vue +49 -0
  118. package/dist/vendored/components/setters/SetText.vue +52 -0
  119. package/dist/vendored/components/setters/props.js +23 -0
  120. package/dist/vendored/fidelity/baseline-messageOverview.json +87 -0
  121. package/dist/vendored/fidelity/calibration.json +29 -0
  122. package/dist/vendored/fidelity/fidelity-messageOverview-after.html +114 -0
  123. package/dist/vendored/fidelity/fidelity-messageOverview-after.json +272 -0
  124. package/dist/vendored/fidelity/fidelity-messageOverview.html +185 -0
  125. package/dist/vendored/fidelity/fidelity-messageOverview.json +336 -0
  126. package/dist/vendored/fidelity/fidelity-messageOverview_v2.html +262 -0
  127. package/dist/vendored/fidelity/fidelity-messageOverview_v2.json +502 -0
  128. package/dist/vendored/fidelity/fidelity-messageOverview_v3.html +201 -0
  129. package/dist/vendored/fidelity/fidelity-messageOverview_v3.json +417 -0
  130. package/dist/vendored/fidelity/fidelity-messageOverview_visual.html +125 -0
  131. package/dist/vendored/fidelity/fidelity-messageOverview_visual.json +338 -0
  132. package/dist/vendored/index.html +16 -0
  133. package/dist/vendored/main.js +36 -0
  134. package/dist/vendored/meta/action-meta.json +141 -0
  135. package/dist/vendored/meta/component-meta.json +2714 -0
  136. package/dist/vendored/meta/directive-meta.json +40 -0
  137. package/dist/vendored/meta/style-meta.json +1086 -0
  138. package/dist/vendored/runtime/StyleParser.js +179 -0
  139. package/dist/vendored/runtime/adapter.js +166 -0
  140. package/dist/vendored/runtime/alert-card.js +125 -0
  141. package/dist/vendored/runtime/app-toast.js +33 -0
  142. package/dist/vendored/runtime/assets.js +16 -0
  143. package/dist/vendored/runtime/canvas-context.js +141 -0
  144. package/dist/vendored/runtime/card-runtime.js +289 -0
  145. package/dist/vendored/runtime/clipboard-node.js +83 -0
  146. package/dist/vendored/runtime/clipboard.js +29 -0
  147. package/dist/vendored/runtime/evaluate.js +152 -0
  148. package/dist/vendored/runtime/event-model.js +300 -0
  149. package/dist/vendored/runtime/hoverGuide.js +31 -0
  150. package/dist/vendored/runtime/jsapi-mock.js +256 -0
  151. package/dist/vendored/runtime/node-model.js +217 -0
  152. package/dist/vendored/runtime/property.js +364 -0
  153. package/dist/vendored/runtime/selectionOverlay.js +53 -0
  154. package/dist/vendored/runtime/toPreview.js +648 -0
  155. package/dist/vendored/standalone-main.js +36 -0
  156. package/dist/vendored/standalone.html +12 -0
  157. package/dist/vendored/styles/app.css +2565 -0
  158. package/dist/vendored/styles/card-gallery.css +234 -0
  159. package/dist/vendored/styles/card-select.css +281 -0
  160. package/dist/vendored/styles/components.css +383 -0
  161. package/dist/vendored/styles/dropdown.css +337 -0
  162. package/dist/vendored/styles/json-tree.css +308 -0
  163. package/dist/vendored/styles/node-inspector.css +1003 -0
  164. package/dist/vendored/styles/setters.css +962 -0
  165. package/dist/vendored/styles/standalone.css +27 -0
  166. package/dist/vendored/styles/structure-tree.css +320 -0
  167. package/package.json +42 -0
@@ -0,0 +1,2565 @@
1
+ /* 预览外壳样式 —— 配色集中在 :root(深色主题,贴近 Codex / WorkBuddy 暗色风格)。
2
+ 所有关键颜色均通过配色变量设置;[data-theme="light"] 复用同名变量切换到浅色主题。
3
+ 说明:设备模拟 UI(.dlg / .dtoast / .pk-* 等原生弹框白底)模拟真机外观,不随主题变化,保持固定。 */
4
+ /* 配色 Token 与《深浅主题配色规范手册》映射(语义单一 / 双主题同义词切换):
5
+ --accent/-2/-soft/-border/-hover = 主色 color-primary(及 hover/active 变体)
6
+ --ok/-soft/-border/-strong = 成功 success(深色提亮 #34D399 / 浅色 #10B981)
7
+ --warn/-soft/-border = 警告 warning(琥珀)
8
+ --danger/-soft/-hover = 错误 / 危险 error(红)
9
+ --info/-soft/-border = 信息 info(与主色同源,均为蓝)
10
+ --on-accent = 主色填充上的文字(深色 #0F1115 / 浅色 #fff)
11
+ --bg-* / --text-* / --border* = 中性背景 / 文本 / 边框(Slate 阶梯)
12
+ --st-id / --st-badge-bg / --st-hl-bg = 结构树节点 id / 类型徽标 / 选中高亮(蓝,非绿)
13
+ 已移除冲突的第 4 色相 --teal 与重复绿 --save(绿仅代表 success,见规范 §七.4)。 */
14
+ :root {
15
+ --bg-app: #0e0f13;
16
+ --bg-bar: #15171c;
17
+ --bg-panel: #181b21;
18
+ --bg-stage: #0b0c0f;
19
+ --bg-input: #1b1e25;
20
+ --bg-hover: #232733;
21
+ --bg-active: #2b2f3d;
22
+ --bg-pop: rgba(28, 31, 38, 0.96);
23
+ --bg-soft: #0f1219;
24
+ --border: #2a2e38;
25
+ --border-strong: #353b48;
26
+ --text: #e8eaf0;
27
+ --text-dim: #949aab;
28
+ --text-faint: #626a7d;
29
+ --accent: #6B83F7;
30
+ --accent-2: #5A6FF0;
31
+ --accent-soft: rgba(107, 131, 247, 0.14);
32
+ --accent-border: rgba(107, 131, 247, 0.45);
33
+ --accent-hover: #8195F9;
34
+ --danger: #F87171;
35
+ --danger-soft: rgba(248, 113, 113, 0.14);
36
+ --danger-hover: #EF4444;
37
+ /* 语义状态色(警告/成功/信息/保存)及其柔和底、描边变体 */
38
+ --warn: #FBBF24;
39
+ --warn-soft: rgba(251, 191, 36, 0.14);
40
+ --warn-border: rgba(251, 191, 36, 0.4);
41
+ --ok: #34D399;
42
+ --ok-soft: rgba(52, 211, 153, 0.16);
43
+ --ok-border: rgba(52, 211, 153, 0.45);
44
+ --ok-strong: #34D399;
45
+ --info: #6B83F7;
46
+ --info-soft: rgba(107, 131, 247, 0.14);
47
+ --info-border: rgba(107, 131, 247, 0.45);
48
+ /* 强调色填充上的文字(按钮实心态等) */
49
+ --on-accent: #0F1115;
50
+ /* 层级阴影与遮罩(弹层 / 抽屉 / 卡片框 / 滚动容器 / 模态遮罩) */
51
+ --radius: 8px;
52
+ --radius-sm: 6px;
53
+ --shadow-pop: rgba(0, 0, 0, 0.55);
54
+ --shadow-card: rgba(0, 0, 0, 0.5);
55
+ --shadow-drawer: rgba(0, 0, 0, 0.45);
56
+ --shadow-toast: rgba(0, 0, 0, 0.28);
57
+ --scrim: rgba(0, 0, 0, 0.55);
58
+ --scrim-soft: rgba(0, 0, 0, 0.45);
59
+ --scrim-faint: rgba(0, 0, 0, 0.4);
60
+ /* 顶栏渐变、分隔条、分隔条抓手微光、白色叠加微光 */
61
+ --bar-grad: linear-gradient(180deg, #171a20, #131519);
62
+ --splitter: #5b6cff;
63
+ --splitter-grip: rgba(255, 255, 255, 0.28);
64
+ --overlay-tint: rgba(255, 255, 255, 0.03);
65
+ --overlay-tint-strong: rgba(255, 255, 255, 0.28);
66
+ /* 调试面板细节色(接口调用条目、日志、标签、复制成功等) */
67
+ --err-item-fg: #ffb3a3;
68
+ --api-item-fg: #bcd3ff;
69
+ --api-item-border: rgb(124 140 255 / 20%);
70
+ --api-item-bg: #384c890f;
71
+ --copy-ok: #8fe39a;
72
+ --tag-ok-fg: #1a7f37;
73
+ --tag-ok-bg: rgba(26, 127, 55, 0.14);
74
+ --tag-warn-fg: #b58105;
75
+ --tag-warn-bg: rgba(181, 129, 5, 0.16);
76
+ --log-warn-fg: #e0b341;
77
+ --log-warn-bg: rgba(224, 179, 65, 0.14);
78
+ --log-warn-border: rgba(224, 179, 65, 0.35);
79
+ --scroll-thumb: rgba(148, 154, 171, 0.28);
80
+ --scroll-thumb-hover: rgba(148, 154, 171, 0.45);
81
+ /* 语法高亮配色(JSON 树 / 结构树 / 代码编辑器),随主题切换;暗色默认=原硬编码值,保证暗色外观不变 */
82
+ --jt-key: #9db4ff;
83
+ --jt-string: #8fe39a;
84
+ --jt-number: #f0b86e;
85
+ --jt-boolean: #f08e8e;
86
+ --jt-null: #626a7d;
87
+ --jt-faint: #8b93a7;
88
+ --jt-ring: rgba(157, 180, 255, 0.25);
89
+ --jt-del-fg: #f08e8e;
90
+ --jt-del-bg: rgba(240, 100, 100, 0.15);
91
+ --jt-add-bg: rgba(157, 180, 255, 0.1);
92
+ --st-label: #cccccc;
93
+ --st-id: #8b93a7;
94
+ --st-badge-bg: #6B83F7;
95
+ --st-badge-fg: #ffffff;
96
+ --st-hl-bg: rgba(107, 131, 247, 0.28);
97
+ --code-comment: #6b7280;
98
+ --code-punc: #9ca3af;
99
+ --code-prop: #f0883e;
100
+ --code-str: #7ee787;
101
+ --code-num: #79c0ff;
102
+ --code-kw: #ff7b72;
103
+ --code-fn: #d2a8ff;
104
+ --code-var: #ffa657;
105
+ --code-binding: #56d4dd;
106
+ --code-warn: #f59e0b;
107
+ /* === CardBuddy UI 尺寸与节奏规范手册 §四(单一来源,尺寸/节奏不随主题变)== */
108
+ /* 间距 8 pt 网格 */
109
+ --space-0: 0;
110
+ --space-1: 4px;
111
+ --space-2: 8px;
112
+ --space-3: 12px;
113
+ --space-4: 16px;
114
+ --space-5: 20px;
115
+ --space-6: 24px;
116
+ --space-7: 32px;
117
+ --space-8: 40px;
118
+ --space-9: 48px;
119
+ /* 字号与行高 */
120
+ --fs-xs: 11px; --lh-xs: 16px;
121
+ --fs-sm: 12px; --lh-sm: 18px;
122
+ --fs-base: 13px; --lh-base: 20px;
123
+ --fs-md: 14px; --lh-md: 22px;
124
+ --fs-lg: 15px; --lh-lg: 22px;
125
+ --fs-xl: 16px; --lh-xl: 24px;
126
+ --fs-2xl: 20px; --lh-2xl: 28px;
127
+ /* 圆角(新增阶梯:md/lg/xl/2xl/pill;--radius/--radius-sm 为历史变量,渐进迁移到 --radius-md/--radius-sm) */
128
+ --radius-xs: 4px;
129
+ --radius-md: 8px;
130
+ --radius-lg: 10px;
131
+ --radius-xl: 12px;
132
+ --radius-2xl: 16px;
133
+ --radius-pill: 999px;
134
+ /* 控件高度阶梯 */
135
+ --ctrl-xs: 24px;
136
+ --ctrl-sm: 28px;
137
+ --ctrl-md: 32px;
138
+ --ctrl-lg: 36px;
139
+ --ctrl-xl: 40px;
140
+ /* 列表 / 树行高 */
141
+ --row-h: 28px;
142
+ /* 图标尺寸 / 描边 */
143
+ --icon-xs: 12px;
144
+ --icon-sm: 14px;
145
+ --icon-md: 16px;
146
+ --icon-lg: 20px;
147
+ --icon-xl: 24px;
148
+ --stroke: 1.75;
149
+ /* 阴影形状(颜色交给配色手册) */
150
+ --shadow-1: 0 1px 2px var(--shadow-card);
151
+ --shadow-2: 0 4px 12px var(--shadow-card);
152
+ --shadow-3: 0 8px 24px var(--shadow-card);
153
+ --shadow-4: 0 20px 64px var(--shadow-pop);
154
+ --shadow-5: -20px 0 32px var(--shadow-drawer);
155
+ /* z-index 阶梯(单一来源,禁止中间塞值) */
156
+ --z-base: 1;
157
+ --z-sticky: 20;
158
+ --z-overlay-low: 50;
159
+ --z-overlay: 60;
160
+ --z-pop: 70;
161
+ --z-panel: 100;
162
+ --z-modal: 1000;
163
+ --z-deviceui: 1100;
164
+ --z-appmodal: 2000;
165
+ --z-toast-app: 2100;
166
+ }
167
+
168
+ /* 浅色主题:以白色为基色调,复用同名变量整体切换。仅覆盖配色变量,布局不变。 */
169
+ :root[data-theme='light'] {
170
+ --bg-app: #f4f5f7;
171
+ --bg-bar: #ffffff;
172
+ --bg-panel: #ffffff;
173
+ --bg-stage: #e9ecf1;
174
+ --bg-input: #eef1f5;
175
+ --bg-hover: #e3e7ee;
176
+ --bg-active: #d6dbe4;
177
+ --bg-pop: rgba(255, 255, 255, 0.97);
178
+ --bg-soft: #e7ebf2;
179
+ --border: #d8dce3;
180
+ --border-strong: #c2c8d2;
181
+ --text: #1f2329;
182
+ --text-dim: #5b6472;
183
+ --text-faint: #8a93a3;
184
+ --accent: #4F6BF2;
185
+ --accent-2: #3D57D9;
186
+ --accent-soft: rgba(79, 107, 242, 0.12);
187
+ --accent-border: rgba(79, 107, 242, 0.4);
188
+ --accent-hover: #3D57D9;
189
+ --danger: #EF4444;
190
+ --danger-soft: rgba(239, 68, 68, 0.12);
191
+ --danger-hover: #DC2626;
192
+ --warn: #F59E0B;
193
+ --warn-soft: rgba(245, 158, 11, 0.14);
194
+ --warn-border: rgba(245, 158, 11, 0.4);
195
+ --ok: #10B981;
196
+ --ok-soft: rgba(16, 185, 129, 0.14);
197
+ --ok-border: rgba(16, 185, 129, 0.4);
198
+ --ok-strong: #166534;
199
+ --info: #4F6BF2;
200
+ --info-soft: rgba(79, 107, 242, 0.12);
201
+ --info-border: rgba(79, 107, 242, 0.4);
202
+ --on-accent: #ffffff;
203
+ --radius: 8px;
204
+ --radius-sm: 6px;
205
+ --shadow-pop: rgba(15, 23, 42, 0.16);
206
+ --shadow-card: rgba(15, 23, 42, 0.16);
207
+ --shadow-drawer: rgba(15, 23, 42, 0.14);
208
+ --shadow-toast: rgba(15, 23, 42, 0.12);
209
+ --scrim: rgba(15, 23, 42, 0.35);
210
+ --scrim-soft: rgba(15, 23, 42, 0.28);
211
+ --scrim-faint: rgba(15, 23, 42, 0.22);
212
+ --bar-grad: linear-gradient(180deg, #ffffff, #f3f5f8);
213
+ --splitter: #5b6cff;
214
+ --splitter-grip: rgba(0, 0, 0, 0.16);
215
+ --overlay-tint: rgba(0, 0, 0, 0.03);
216
+ --overlay-tint-strong: rgba(0, 0, 0, 0.1);
217
+ --err-item-fg: #c0392b;
218
+ --api-item-fg: #2f4fb0;
219
+ --api-item-border: rgba(91, 108, 255, 0.25);
220
+ --api-item-bg: rgba(91, 108, 255, 0.06);
221
+ --copy-ok: #1a7f37;
222
+ --tag-ok-fg: #1a7f37;
223
+ --tag-ok-bg: rgba(26, 127, 55, 0.1);
224
+ --tag-warn-fg: #b58105;
225
+ --tag-warn-bg: rgba(181, 129, 5, 0.1);
226
+ --log-warn-fg: #9a6a00;
227
+ --log-warn-bg: rgba(181, 129, 5, 0.1);
228
+ --log-warn-border: rgba(181, 129, 5, 0.3);
229
+ --scroll-thumb: rgba(0, 0, 0, 0.2);
230
+ --scroll-thumb-hover: rgba(0, 0, 0, 0.32);
231
+ /* 语法高亮配色:浅色主题参考 GitHub Light / VSCode Light+,保证白底上的可读性 */
232
+ --jt-key: #953800;
233
+ --jt-string: #0a3069;
234
+ --jt-number: #116329;
235
+ --jt-boolean: #cf222e;
236
+ --jt-null: #57606a;
237
+ --jt-faint: #6b7280;
238
+ --jt-ring: rgba(91, 108, 255, 0.3);
239
+ --jt-del-fg: #cf222e;
240
+ --jt-del-bg: rgba(207, 34, 46, 0.12);
241
+ --jt-add-bg: rgba(91, 108, 255, 0.12);
242
+ --st-label: #24292f;
243
+ --st-id: #64748b;
244
+ --st-badge-bg: #4F6BF2;
245
+ --st-badge-fg: #ffffff;
246
+ --st-hl-bg: rgba(79, 107, 242, 0.16);
247
+ --code-comment: #6e7781;
248
+ --code-punc: #6e7781;
249
+ --code-prop: #953800;
250
+ --code-str: #0a3069;
251
+ --code-num: #0550ae;
252
+ --code-kw: #cf222e;
253
+ --code-fn: #8250df;
254
+ --code-var: #953800;
255
+ --code-binding: #0d9488;
256
+ --code-warn: #b58105;
257
+ }
258
+
259
+ * {
260
+ box-sizing: border-box;
261
+ /* 移除浏览器默认 focus 轮廓(UA :focus outline),统一由组件自定义 focus 视觉
262
+ (如 .st-input 的 border + box-shadow 光环、.app__btn 的 hover/active)。
263
+ author 规则优先级高于 UA,故此处可全局屏蔽原生 outline。 */
264
+ outline: none;
265
+ }
266
+
267
+ /* 表单控件继承外壳字体与颜色,去掉 UA 默认的数字字体 / 外边距 / 颜色等不一致样式;
268
+ 组件类(.st-input / .st-select / .app__btn 等)特异性更高,会覆盖以下继承值。 */
269
+ button,
270
+ input,
271
+ select,
272
+ textarea {
273
+ font-family: inherit;
274
+ font-size: inherit;
275
+ line-height: inherit;
276
+ color: inherit;
277
+ margin: 0;
278
+ }
279
+
280
+ html,
281
+ body {
282
+ margin: 0;
283
+ height: 100%;
284
+ font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', sans-serif;
285
+ background: var(--bg-app);
286
+ color: var(--text);
287
+ -webkit-font-smoothing: antialiased;
288
+ }
289
+ #app {
290
+ height: 100vh;
291
+ }
292
+
293
+ /* 全局滚动条(Firefox) */
294
+ * {
295
+ scrollbar-width: thin;
296
+ scrollbar-color: var(--scroll-thumb) transparent;
297
+ }
298
+ /* 全局滚动条(WebKit) */
299
+ *::-webkit-scrollbar {
300
+ width: 8px;
301
+ height: 8px;
302
+ }
303
+ *::-webkit-scrollbar-thumb {
304
+ background: var(--scroll-thumb);
305
+ border-radius: 8px;
306
+ }
307
+ *::-webkit-scrollbar-thumb:hover {
308
+ background: var(--scroll-thumb-hover);
309
+ }
310
+ *::-webkit-scrollbar-track {
311
+ background: transparent;
312
+ }
313
+
314
+ .app {
315
+ display: flex;
316
+ flex-direction: column;
317
+ height: 100vh;
318
+ background: var(--bg-app);
319
+ }
320
+
321
+ .app__bar {
322
+ display: flex;
323
+ align-items: center;
324
+ gap: 12px;
325
+ padding: 0 16px;
326
+ height: 48px;
327
+ background: var(--bar-grad);
328
+ border-bottom: 1px solid var(--border);
329
+ flex: 0 0 auto;
330
+ }
331
+ .app__logo {
332
+ display: flex;
333
+ align-items: center;
334
+ gap: 8px;
335
+ font-size: 15px;
336
+ font-weight: 600;
337
+ color: var(--text);
338
+ white-space: nowrap;
339
+ }
340
+ .app__logo-icon {
341
+ width: var(--icon-xl); /* 24px:品牌 logo 图标 */
342
+ height: var(--icon-xl);
343
+ display: block;
344
+ }
345
+ .app__logo-version {
346
+ font-size: var(--fs-xs); /* 11px:顶栏版本号徽标小字 */
347
+ font-weight: 500;
348
+ line-height: 1;
349
+ letter-spacing: 0.02em;
350
+ color: var(--text-faint);
351
+ }
352
+
353
+ .app__btn {
354
+ flex: 0 0 auto;
355
+ display: inline-flex;
356
+ align-items: center;
357
+ gap: 6px;
358
+ height: var(--ctrl-md);
359
+ padding: 0 12px;
360
+ background: var(--bg-input);
361
+ color: var(--text);
362
+ border: 1px solid var(--border-strong);
363
+ border-radius: 8px;
364
+ font-size: 13px;
365
+ cursor: pointer;
366
+ white-space: nowrap;
367
+ transition: background 0.15s, border-color 0.15s, transform 0.05s;
368
+ }
369
+ .app__btn:hover:not(:disabled) {
370
+ background: var(--bg-hover);
371
+ border-color: var(--accent-border);
372
+ color: var(--accent);
373
+ }
374
+ .app__btn:active:not(:disabled) {
375
+ transform: translateY(1px);
376
+ }
377
+ .app__btn:disabled {
378
+ opacity: 0.5;
379
+ cursor: default;
380
+ }
381
+ .app__btn--icon {
382
+ height: var(--ctrl-md);
383
+ padding: 0 8px;
384
+ }
385
+ /* 顶栏图标按钮(刷新 / 连接)与远端、op-group 统一为 28px(--ctrl-sm),不再高 32px */
386
+ .app__bar .app__btn--icon {
387
+ height: var(--ctrl-sm);
388
+ }
389
+ /* 远端分组:合并「远端保存 / 远端预览 / 全量同步 / 选中卡片同步」为一个下拉,
390
+ 组名「远端」,云图标;保存类项蓝色、同步类项绿色,与本地保存区分。 */
391
+ .app__btn--remote {
392
+ height: var(--ctrl-sm);
393
+ padding: 0 10px;
394
+ font-size: 12px;
395
+ background: var(--accent-soft);
396
+ color: var(--accent);
397
+ border-color: var(--accent-border);
398
+ }
399
+ .app__btn--remote:hover:not(:disabled) {
400
+ background: var(--accent);
401
+ color: var(--on-accent);
402
+ border-color: var(--accent);
403
+ }
404
+ /* 已保存但校验不符:触发按钮切换为琥珀警告色 */
405
+ .app__btn--remote.app__btn--warn {
406
+ background: var(--warn-soft);
407
+ color: var(--warn);
408
+ border-color: var(--warn-border);
409
+ }
410
+ .app__btn--remote.app__btn--warn:hover:not(:disabled) {
411
+ background: var(--warn);
412
+ color: var(--on-accent);
413
+ border-color: var(--warn);
414
+ }
415
+ .app__remote-menu {
416
+ position: relative;
417
+ display: inline-flex;
418
+ }
419
+ .app__remote-menu .app__btn--remote {
420
+ gap: 6px;
421
+ }
422
+ /* 触发器内的下拉箭头:菜单展开时旋转 180°(由 .app__icon--chev--open 驱动,见 dropdown.css) */
423
+ .app__icon--chev {
424
+ transition: transform 0.15s;
425
+ }
426
+ /* 下拉项:保存类 hover 蓝色、同步类 hover 绿色(基础布局见 dropdown.css .app__remote-item) */
427
+ .app__remote-item--save:hover {
428
+ background: var(--accent-soft);
429
+ color: var(--accent);
430
+ }
431
+ .app__remote-item--sync:hover {
432
+ background: var(--ok-soft);
433
+ color: var(--ok);
434
+ }
435
+ /* 编辑历史 / 复制 按钮组:配色与通用下拉框组件(.dd__trigger)一致 ——
436
+ bg-input 底色、border 边框色、text 文本色、radius-sm 圆角;连体分段控件(外圆角、内分隔线平方)。 */
437
+ .app__op-group {
438
+ display: inline-flex;
439
+ }
440
+ .app__op-group > .app__btn {
441
+ background: var(--bg-input);
442
+ color: var(--text);
443
+ border: 1px solid var(--border);
444
+ border-radius: 0;
445
+ margin-left: -1px;
446
+ height: var(--ctrl-sm);
447
+ padding: 0 10px;
448
+ font-size: 12px;
449
+ }
450
+ .app__op-group > .app__btn:first-child {
451
+ margin-left: 0;
452
+ border-top-left-radius: var(--radius-sm, 6px);
453
+ border-bottom-left-radius: var(--radius-sm, 6px);
454
+ }
455
+ .app__op-group > .app__btn:last-child {
456
+ border-top-right-radius: var(--radius-sm, 6px);
457
+ border-bottom-right-radius: var(--radius-sm, 6px);
458
+ }
459
+ .app__op-group > .app__btn:hover:not(:disabled) {
460
+ position: relative;
461
+ z-index: var(--z-base);
462
+ border-color: var(--border-strong);
463
+ background: var(--bg-hover);
464
+ }
465
+ .app__op-group > .app__btn:disabled {
466
+ opacity: 0.5;
467
+ cursor: default;
468
+ }
469
+ /* 顶栏「保存到本地」:当前显示版本与本地磁盘不一致(撤销/重做到历史版本或编辑未保存)时
470
+ 高亮(实心强调色 + 外发光环),提示可点击保存;一致时仅作普通分段按钮、置灰不可用。 */
471
+ /* 禁用态优先:即使带 is-dirty(脏标记)也保持置灰,绝不能显得可点 */
472
+ .app__btn--history-save.is-dirty:disabled {
473
+ background: var(--bg-input);
474
+ color: var(--text);
475
+ border-color: var(--border-strong);
476
+ box-shadow: none;
477
+ opacity: 0.5;
478
+ cursor: default;
479
+ }
480
+ .app__btn--history-save.is-dirty:not(:disabled) {
481
+ background: var(--accent);
482
+ color: var(--on-accent);
483
+ border-color: var(--accent);
484
+ /* box-shadow: 0 0 0 2px var(--accent-soft); */
485
+ }
486
+ .app__btn--history-save.is-dirty:not(:disabled):hover {
487
+ background: var(--accent-2);
488
+ border-color: var(--accent-2);
489
+ }
490
+ /* 发布按钮(写回 FinMall 前的发布动作),与本地保存/远端分组区分 */
491
+ .app__btn--publish {
492
+ display: inline-flex;
493
+ align-items: center;
494
+ gap: 5px;
495
+ height: var(--ctrl-md);
496
+ padding: 0 12px;
497
+ color: var(--info);
498
+ border-color: var(--info-border);
499
+ background: var(--info-soft);
500
+ }
501
+ .app__btn--publish:hover:not(:disabled) {
502
+ background: var(--info);
503
+ color: var(--on-accent);
504
+ border-color: var(--info);
505
+ }
506
+ .app__btn--publish:disabled {
507
+ opacity: 0.5;
508
+ cursor: default;
509
+ }
510
+ /* 已保存但读回校验不符:橙色警示,提示「交易成功但内容未落库」 */
511
+ .app__btn--publish.app__btn--warn {
512
+ color: var(--warn);
513
+ border-color: var(--warn-border);
514
+ background: var(--warn-soft);
515
+ }
516
+ .app__btn--publish.app__btn--warn:hover:not(:disabled) {
517
+ background: var(--warn);
518
+ color: var(--on-accent);
519
+ border-color: var(--warn);
520
+ }
521
+ .app__bar-actions {
522
+ margin-left: auto; /* 把这一组推到顶栏最右侧 */
523
+ display: flex;
524
+ align-items: center;
525
+ gap: 12px; /* 与 .app__bar 的 gap 保持一致 */
526
+ }
527
+ /* 本地开发服务设置入口(前后端分离 Phase 2) */
528
+ .app__dev-svc {
529
+ position: relative;
530
+ display: inline-flex;
531
+ }
532
+ .app__btn--warn {
533
+ background: var(--warn-soft);
534
+ color: var(--warn);
535
+ border-color: var(--warn-border);
536
+ }
537
+ /* 本地开发服务「已连接」状态:绿色常亮(绿=健康/连通,对齐语义色规范 §七.4;
538
+ 仅在 apiBaseResolved !== null 即已配置并启用本地服务时点亮,断开后回到 --warn 警示态) */
539
+ .app__btn--connected {
540
+ background: var(--ok-soft);
541
+ color: var(--ok);
542
+ border-color: var(--ok-border);
543
+ }
544
+ .app__btn--connected:hover:not(:disabled) {
545
+ background: var(--ok-soft);
546
+ color: var(--ok-strong);
547
+ border-color: var(--ok);
548
+ }
549
+ /* 本地开发服务「连接中 / 重连中」:琥珀色(正在建立或重连 SSE 连接) */
550
+ .app__btn--reconnecting {
551
+ background: var(--warn-soft);
552
+ color: var(--warn);
553
+ border-color: var(--warn-border);
554
+ }
555
+ /* 本地开发服务「已断开」:红色(连接彻底失败或服务已停止,见 onDevServiceState 的 watchdog) */
556
+ .app__btn--disconnected {
557
+ background: var(--danger-soft);
558
+ color: var(--danger);
559
+ border-color: var(--danger);
560
+ }
561
+ .app__btn--sm {
562
+ padding: 4px 10px;
563
+ font-size: 12px;
564
+ }
565
+ .app__dev-svc-panel {
566
+ position: absolute;
567
+ top: calc(100% + 8px);
568
+ right: 0;
569
+ z-index: var(--z-overlay-low);
570
+ width: 320px;
571
+ padding: 16px;
572
+ background: var(--bg-soft, #20242b);
573
+ border: 1px solid var(--border, #2b3038);
574
+ border-radius: var(--radius-xl);
575
+ box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35);
576
+ color: var(--text, #e6e9ef);
577
+ text-align: left;
578
+ }
579
+ .app__dev-svc-title {
580
+ font-size: 13px;
581
+ font-weight: 600;
582
+ margin-bottom: 10px;
583
+ }
584
+ .app__dev-svc-row {
585
+ display: flex;
586
+ align-items: center;
587
+ justify-content: space-between;
588
+ gap: 4px;
589
+ margin-bottom: 10px;
590
+ font-size: 12px;
591
+ color: var(--text-dim, #9aa3b2);
592
+ }
593
+ .app__dev-svc-row--col {
594
+ flex-direction: column;
595
+ align-items: stretch;
596
+ }
597
+ .app__dev-svc-row--col > span {
598
+ margin-bottom: 4px;
599
+ }
600
+ /* 与节点编辑面板 setter(setters.css .st-input / .st-textarea)保持完全一致:
601
+ --bg-input 底色、--border 边框、5px 圆角、focus 切 --accent-border 边框 + --accent-soft 光环。 */
602
+ .app__dev-svc-row input[type='text'],
603
+ .app__dev-svc-row input[type='password'],
604
+ .app__dev-svc-row textarea {
605
+ width: 100%;
606
+ min-width: 0;
607
+ height: var(--ctrl-md);
608
+ padding: 0 12px;
609
+ background: var(--bg-input);
610
+ border: 1px solid var(--border);
611
+ border-radius: var(--radius-sm);
612
+ color: var(--text);
613
+ font-size: 12px;
614
+ font-family: inherit;
615
+ outline: none;
616
+ transition: border-color 0.15s, box-shadow 0.15s;
617
+ }
618
+ .app__dev-svc-row textarea {
619
+ height: auto;
620
+ min-height: 84px;
621
+ padding: 6px 8px;
622
+ line-height: 1.5;
623
+ resize: none;
624
+ }
625
+ .app__dev-svc-row input[type='text']:focus,
626
+ .app__dev-svc-row input[type='password']:focus,
627
+ .app__dev-svc-row textarea:focus {
628
+ border-color: var(--accent-border);
629
+ box-shadow: 0 0 0 2px var(--accent-soft);
630
+ }
631
+ .app__dev-svc-row input::placeholder,
632
+ .app__dev-svc-row textarea::placeholder {
633
+ color: var(--text-faint);
634
+ }
635
+ .app__dev-svc-row .switch {
636
+ flex: 0 0 auto;
637
+ }
638
+ .app__dev-svc-actions {
639
+ display: flex;
640
+ gap: 8px;
641
+ margin-top: 4px;
642
+ justify-content: flex-end;
643
+ }
644
+ .app__dev-svc-hint {
645
+ margin: 8px 0 0;
646
+ font-size: 11px;
647
+ line-height: 1.5;
648
+ color: var(--warn, #e0a23a);
649
+ }
650
+ /* 部署态未配置本地服务:顶部提示条 */
651
+ .app__nosvc-banner {
652
+ padding: 8px 14px;
653
+ font-size: 13px;
654
+ color: var(--warn, #e0a23a);
655
+ background: var(--warn-soft, rgba(224, 162, 58, 0.12));
656
+ /* border-bottom: 1px solid var(--warn-border, rgba(224, 162, 58, 0.35)); */
657
+ }
658
+ /* 「已断开」提示条:红色(连接彻底失败 / 服务已停止),区别于未配置的琥珀色 */
659
+ .app__nosvc-banner--disconnected {
660
+ color: var(--danger, #ef4444);
661
+ background: var(--danger-soft, rgba(239, 68, 68, 0.12));
662
+ }
663
+ /* 外部改动自动重载提示条:信息蓝(品牌主色 #0079F2 的柔和变体),非阻塞、自动消失 */
664
+ .app__extreload-banner {
665
+ color: var(--info, #0079F2);
666
+ background: var(--info-soft, rgba(0, 121, 242, 0.12));
667
+ }
668
+ /* 主题切换开关:复用同名 CSS 变量换肤;轨道/滑块配色随主题自适应 */
669
+ .theme-toggle {
670
+ display: inline-flex;
671
+ align-items: center;
672
+ gap: 7px;
673
+ padding: 0;
674
+ border: none;
675
+ background: none;
676
+ color: var(--text-dim);
677
+ font-size: 12px;
678
+ cursor: pointer;
679
+ user-select: none;
680
+ transition: color 0.15s;
681
+ }
682
+ .theme-toggle:hover {
683
+ color: var(--text);
684
+ }
685
+ .theme-toggle__track {
686
+ position: relative;
687
+ width: 36px;
688
+ height: 20px;
689
+ border-radius: 999px;
690
+ background: var(--bg-input);
691
+ border: 1px solid var(--border-strong);
692
+ transition: background 0.18s, border-color 0.18s;
693
+ }
694
+ .theme-toggle__thumb {
695
+ position: absolute;
696
+ top: 50%;
697
+ left: 2px;
698
+ transform: translateY(-50%);
699
+ width: 16px;
700
+ height: 16px;
701
+ border-radius: 50%;
702
+ background: var(--text-faint);
703
+ display: flex;
704
+ align-items: center;
705
+ justify-content: center;
706
+ transition: transform 0.18s ease, background 0.18s;
707
+ }
708
+ .theme-toggle__icon {
709
+ width: 11px;
710
+ height: 11px;
711
+ color: var(--bg-app);
712
+ }
713
+ /* 浅色主题:滑块滑到右侧,轨道着 accent */
714
+ :root[data-theme='light'] .theme-toggle__track {
715
+ background: var(--accent-soft);
716
+ border-color: var(--accent-border);
717
+ }
718
+ :root[data-theme='light'] .theme-toggle__thumb {
719
+ transform: translateY(-50%) translateX(16px);
720
+ background: var(--accent);
721
+ }
722
+ :root[data-theme='light'] .theme-toggle__icon {
723
+ color: var(--on-accent);
724
+ }
725
+ /* 开关 Switch(通用受控组件,对齐 .theme-toggle 视觉范式,遵循《配色规范》§4.5):
726
+ 关闭=中性灰(--bg-input / --border-strong / --text-faint),开启=主色实心(--accent),
727
+ 滑块用主色上的文字(--on-accent);双主题仅切换同名 Token 取值,不写颜色分支、零硬编码。 */
728
+ .switch {
729
+ display: inline-flex;
730
+ padding: 0;
731
+ margin: 0;
732
+ border: none;
733
+ background: none;
734
+ cursor: pointer;
735
+ vertical-align: middle;
736
+ line-height: 0;
737
+ }
738
+ .switch--disabled {
739
+ cursor: not-allowed;
740
+ opacity: 0.5;
741
+ }
742
+ .switch__track {
743
+ position: relative;
744
+ display: inline-block;
745
+ width: 36px;
746
+ height: 20px;
747
+ border-radius: 999px;
748
+ background: var(--bg-input);
749
+ border: 1px solid var(--border-strong);
750
+ box-sizing: border-box;
751
+ transition: background 0.18s ease, border-color 0.18s ease;
752
+ }
753
+ .switch__thumb {
754
+ position: absolute;
755
+ top: 50%;
756
+ left: 2px;
757
+ transform: translateY(-50%);
758
+ width: 16px;
759
+ height: 16px;
760
+ border-radius: 50%;
761
+ background: var(--text-faint);
762
+ transition: transform 0.18s ease, background 0.18s ease;
763
+ }
764
+ .switch--on .switch__track {
765
+ background: var(--accent);
766
+ border-color: var(--accent);
767
+ }
768
+ .switch--on .switch__thumb {
769
+ transform: translateY(-50%) translateX(16px);
770
+ background: var(--on-accent);
771
+ }
772
+ /* 整卡校验徽标(Q3:仅 ok/error,不展开 bindingErrors 明细) */
773
+ .app__badge {
774
+ flex: 0 0 auto;
775
+ display: inline-flex;
776
+ align-items: center;
777
+ gap: 5px;
778
+ padding: 2px 9px;
779
+ border-radius: 999px;
780
+ border: 1px solid var(--border-strong);
781
+ background: var(--bg-input);
782
+ color: var(--text-dim);
783
+ font-size: 11px;
784
+ font-weight: 600;
785
+ user-select: none;
786
+ white-space: nowrap;
787
+ }
788
+ .app__badge-dot {
789
+ width: 7px;
790
+ height: 7px;
791
+ border-radius: 50%;
792
+ background: var(--text-faint);
793
+ flex: 0 0 auto;
794
+ }
795
+ .app__badge--ok {
796
+ color: var(--ok);
797
+ border-color: var(--ok-border);
798
+ background: var(--ok-soft);
799
+ }
800
+ .app__badge--ok .app__badge-dot { background: var(--ok); }
801
+ .app__badge--error {
802
+ color: var(--danger);
803
+ border-color: var(--danger-soft);
804
+ background: var(--danger-soft);
805
+ }
806
+ .app__badge--error .app__badge-dot { background: var(--danger); }
807
+ .app__badge--pending {
808
+ color: var(--warn);
809
+ border-color: var(--warn-border);
810
+ background: var(--warn-soft);
811
+ }
812
+ .app__badge--pending .app__badge-dot {
813
+ background: var(--warn);
814
+ animation: app-badge-pulse 1s ease-in-out infinite;
815
+ }
816
+ .app__badge--nopython,
817
+ .app__badge--fail {
818
+ color: var(--text-faint);
819
+ border-color: var(--border);
820
+ background: var(--bg-input);
821
+ }
822
+ .app__badge--nopython .app__badge-dot,
823
+ .app__badge--fail .app__badge-dot { background: var(--text-faint); }
824
+ @keyframes app-badge-pulse {
825
+ 0%, 100% { opacity: 1; }
826
+ 50% { opacity: 0.3; }
827
+ }
828
+ .app__icon {
829
+ width: 15px;
830
+ height: 15px;
831
+ display: block;
832
+ }
833
+ .app__icon--spin {
834
+ animation: app__spin 0.8s linear infinite;
835
+ }
836
+ @keyframes app__spin {
837
+ from { transform: rotate(0deg); }
838
+ to { transform: rotate(360deg); }
839
+ }
840
+
841
+ .app__stage {
842
+ flex: 1 1 auto;
843
+ display: flex;
844
+ min-height: 0;
845
+ position: relative; /* 作为绝对定位 splitter 的定位上下文 */
846
+ }
847
+
848
+ .phone {
849
+ flex: 0 0 auto;
850
+ display: flex;
851
+ flex-direction: column;
852
+ align-items: center; /* 水平居中缩放后的手机框 */
853
+ justify-content: center; /* 垂直居中:contain 缩放后手机始终完整,留白上下均衡 */
854
+ min-height: 0;
855
+ overflow: hidden; /* contain 缩放已保证手机不溢出,隐藏兜底避免偶发 1px 滚动条 */
856
+ background:
857
+ radial-gradient(circle at 30% 20%, rgba(124, 140, 255, 0.05), transparent 60%),
858
+ var(--bg-stage);
859
+ }
860
+ /* 缩放外框:占满整列(宽高均填满预览列),内部 flex 居中手机,保证「框架填满宽高、手机居中」 */
861
+ .phone__frame {
862
+ flex: 1 1 auto;
863
+ width: 100%;
864
+ min-height: 0;
865
+ position: relative;
866
+ display: flex;
867
+ align-items: center;
868
+ justify-content: center;
869
+ overflow: hidden;
870
+ }
871
+ /* 画布层:直接按实际列宽/列高布局(width/height 100% 填满外框),不再使用 transform 缩放;内部 device-bar 固定高、phone-viewport 弹性填充 */
872
+ .phone__scaler {
873
+ position: relative;
874
+ width: 100%;
875
+ height: 100%;
876
+ display: flex;
877
+ flex-direction: column;
878
+ }
879
+
880
+ /* 拖拽分隔条:覆盖在预览列与调试面板的接缝上,绝对定位不占布局宽度 */
881
+ .splitter {
882
+ position: absolute;
883
+ top: 0;
884
+ bottom: 0;
885
+ /* 以预览列右缘为接缝居中覆盖(宽 6px → 两侧各压 3px),不挤占实际宽度 */
886
+ left: calc(var(--preview-width) - 3px);
887
+ width: 6px;
888
+ cursor: col-resize;
889
+ z-index: var(--z-sticky);
890
+ transition: background 0.15s ease;
891
+ }
892
+ .splitter:hover,
893
+ .splitter.is-dragging {
894
+ background: var(--splitter);
895
+ }
896
+ /* 拖拽中:禁用文本选择,全局保持 col-resize 光标 */
897
+ .phone--dragging,
898
+ .phone--dragging * {
899
+ user-select: none !important;
900
+ cursor: col-resize !important;
901
+ }
902
+
903
+ /* 设备工具条(类 Chrome DevTools 设备模式顶部预留的细条):
904
+ 与手机框等宽、紧贴其上,通过下拉选择画布背景 */
905
+ .device-bar {
906
+ flex: 0 0 auto;
907
+ width: 100%;
908
+ height: 28px;
909
+ display: flex;
910
+ align-items: center;
911
+ gap: 8px;
912
+ padding: 0 12px;
913
+ background: var(--bg-bar);
914
+ border-bottom: 1px solid var(--border);
915
+ }
916
+ /* 背景下拉框由 Dropdown 组件统一渲染(.dd 体系),无边框模式见 dropdown.css 的 .dd--borderless */
917
+ .device-bar__spacer {
918
+ flex: 1 1 auto;
919
+ }
920
+ .device-bar__info {
921
+ color: var(--text-faint);
922
+ font-size: 12px;
923
+ white-space: nowrap;
924
+ }
925
+ /* 底部状态条刷新按钮:仅图标、与左侧尺寸文本上下对齐(同高 28px / 居中) */
926
+ .statusbar__refresh {
927
+ flex: 0 0 auto;
928
+ display: inline-flex;
929
+ align-items: center;
930
+ justify-content: center;
931
+ padding: 0;
932
+ background: transparent;
933
+ color: var(--text-faint);
934
+ border: none;
935
+ border-radius: 6px;
936
+ cursor: pointer;
937
+ transition: background 0.15s, color 0.15s, transform 0.05s;
938
+ }
939
+ .statusbar__refresh:hover:not(:disabled) {
940
+ color: var(--accent);
941
+ }
942
+ .statusbar__refresh:active:not(:disabled) {
943
+ transform: translateY(1px);
944
+ }
945
+ .statusbar__refresh:disabled {
946
+ opacity: 0.4;
947
+ cursor: default;
948
+ }
949
+ .device-bar__label {
950
+ color: var(--text-faint);
951
+ font-size: 12px;
952
+ white-space: nowrap;
953
+ }
954
+ /* 预览区底部状态条:与顶部 device-bar 同款风格,固定于画布底部展示画布宽高 */
955
+ .preview-statusbar {
956
+ flex: 0 0 auto;
957
+ width: 100%;
958
+ height: 28px;
959
+ display: flex;
960
+ align-items: center;
961
+ gap: 12px;
962
+ padding: 0 12px;
963
+ background: var(--bg-bar);
964
+ border-top: 1px solid var(--border);
965
+ }
966
+ /* 预览区底部状态条:本地开发服务连接状态(连接图标 + 状态文案)。
967
+ 配色复用顶栏「连接」按钮的语义色类(app__btn--connected / --reconnecting / --disconnected / --warn),
968
+ 仅取各自 color,不带入按钮的背景与边框,保证与右上角按钮状态实时一致;
969
+ 通过更高优先级选择器覆盖背景/边框,颜色仍由各自语义色类提供。 */
970
+ /* 底部状态栏「本地开发服务连接状态」指示器(事件按钮之后):
971
+ 复用顶栏「连接」按钮同款语义色类(app__btn--connected / --reconnecting / --disconnected / --warn),
972
+ 仅取各自 color,用更高优先级(0,2,0)覆盖其按钮背景/边框与默认文字色,保证与右上角按钮实时一致。 */
973
+ .statusbar__conn-icon {
974
+ width: 14px;
975
+ height: 14px;
976
+ flex: 0 0 auto;
977
+ }
978
+ .statusbar .app__btn--connected,
979
+ .statusbar .app__btn--reconnecting,
980
+ .statusbar .app__btn--disconnected,
981
+ .statusbar .app__btn--warn {
982
+ background: transparent;
983
+ border: none;
984
+ padding: 0 10px;
985
+ }
986
+ .statusbar .app__btn--connected { color: var(--ok); }
987
+ .statusbar .app__btn--reconnecting { color: var(--warn); }
988
+ .statusbar .app__btn--disconnected { color: var(--danger); }
989
+ .statusbar .app__btn--warn { color: var(--warn); }
990
+
991
+ /* 颜色选择器(@ckpack/vue-color Sketch)预设区顶部分隔线:与系统边框色保持一致,
992
+ 覆盖组件默认的 1px solid #eee(深色主题下刺眼)。组件样式为全局注入且非 scoped,
993
+ 用 !important 确保稳定覆盖其载入顺序。 */
994
+ .vc-sketch-presets {
995
+ border-top-color: var(--border) !important;
996
+ }
997
+
998
+ /* 固定视口:弹层(alertCard / alertDialog / toast)的包含块,
999
+ 不滚动,遮罩 absolute inset:0 始终精确覆盖可视区域 */
1000
+ .phone-viewport {
1001
+ position: relative;
1002
+ flex: 1 1 auto;
1003
+ min-height: 0;
1004
+ width: 100%;
1005
+ box-shadow: 0 12px 48px var(--shadow-pop);
1006
+ cursor: default;
1007
+ }
1008
+ .phone__screen {
1009
+ width: 100%;
1010
+ height: 100%;
1011
+ padding: 16px;
1012
+ /* 作为选中遮罩覆盖层 (.ce-sel-overlay) 的绝对定位包含块 */
1013
+ position: relative;
1014
+ /* 长卡片超出高度时,在手机框内纵向滚动(背景固定不随内容滚动) */
1015
+ overflow-x: hidden;
1016
+ overflow-y: auto;
1017
+ /* 隐藏滚动条但保留滚动能力(Firefox / 旧版 Edge) */
1018
+ scrollbar-width: none;
1019
+ -ms-overflow-style: none;
1020
+ }
1021
+ /* Photoshop 透明区棋盘格:白 + 浅灰 16px 方格,用于透出卡片半透明区域 */
1022
+ .phone__screen--bg-checker {
1023
+ background-color: #ffffff;
1024
+ background-image:
1025
+ linear-gradient(45deg, #cfcfcf 25%, transparent 25%, transparent 75%, #cfcfcf 75%),
1026
+ linear-gradient(45deg, #cfcfcf 25%, transparent 25%, transparent 75%, #cfcfcf 75%);
1027
+ background-size: 16px 16px;
1028
+ background-position: 0 0, 8px 8px;
1029
+ }
1030
+ .phone__screen--bg-white {
1031
+ background: #ffffff;
1032
+ }
1033
+ .phone__screen--bg-gray {
1034
+ background: #f0f0f0;
1035
+ }
1036
+ .phone__screen::-webkit-scrollbar {
1037
+ display: none; /* WebKit:隐藏滚动条(仍可滚动) */
1038
+ }
1039
+
1040
+ /* 节点选中遮罩覆盖层(DevTools 风格):作为 .phone__screen 的子节点,绝对定位在
1041
+ 屏幕 content 坐标系,随内容滚动贴合选中元素。pointer-events:none 不挡交互;
1042
+ box-sizing:border-box 让虚线边框不撑大尺寸;z-index 高于卡片内容。 */
1043
+ .ce-sel-overlay {
1044
+ position: absolute;
1045
+ background: rgba(66, 133, 244, 0.18);
1046
+ pointer-events: none;
1047
+ box-sizing: border-box;
1048
+ z-index: var(--z-overlay-low);
1049
+ }
1050
+
1051
+ /* 结构树 hover 参考线(Photoshop 风格辅助线):一对竖线(元素左/右边缘,贯穿画布全高)
1052
+ + 一对横线(元素上/下边缘,贯穿画布全宽)。荧光绿,与选中态蓝色虚线框区分;
1053
+ z-index 低于选中态,不与选中框争图层。覆盖层为 .phone__screen 子节点,随内容滚动。 */
1054
+ .ce-guides {
1055
+ position: absolute;
1056
+ top: 0;
1057
+ left: 0;
1058
+ pointer-events: none;
1059
+ z-index: var(--z-overlay-low);
1060
+ }
1061
+ .ce-guide {
1062
+ position: absolute;
1063
+ background: #68ffff;
1064
+ }
1065
+ /* 竖线:宽 1px,纵向贯穿画布全高(height 由容器 100% 撑开) */
1066
+ .ce-guide--v {
1067
+ top: 0;
1068
+ width: 1px;
1069
+ height: 100%;
1070
+ transform: translateX(-0.5px);
1071
+ }
1072
+ /* 横线:高 1px,横向贯穿画布全宽(width 由容器 100% 撑开) */
1073
+ .ce-guide--h {
1074
+ left: 0;
1075
+ height: 1px;
1076
+ width: 100%;
1077
+ transform: translateY(-0.5px);
1078
+ }
1079
+
1080
+ /* 调试面板 */
1081
+ .debug {
1082
+ flex: 1 1 auto;
1083
+ display: flex;
1084
+ flex-direction: column;
1085
+ min-width: 300px;
1086
+ background: var(--bg-panel);
1087
+ border-left: 1px solid var(--border);
1088
+ }
1089
+ .debug__tabs {
1090
+ display: flex;
1091
+ border-bottom: 1px solid var(--border);
1092
+ }
1093
+ .debug__tab {
1094
+ flex: 1;
1095
+ padding: 12px 8px;
1096
+ background: transparent;
1097
+ border: none;
1098
+ color: var(--text-dim);
1099
+ font-size: 13px;
1100
+ cursor: pointer;
1101
+ /* border-bottom: 2px solid transparent; */
1102
+ transition: color 0.15s;
1103
+ position: relative;
1104
+ }
1105
+ .debug__tab:hover {
1106
+ color: var(--text);
1107
+ }
1108
+ .debug__tab--active {
1109
+ color: var(--accent);
1110
+ /* border-bottom-color: var(--accent); */
1111
+
1112
+ &::after {
1113
+ content: '';
1114
+ position: absolute;
1115
+ bottom: -1px;
1116
+ left: 20%;
1117
+ right: 20%;
1118
+ height: 2px;
1119
+ background: var(--accent);
1120
+ }
1121
+ }
1122
+ .debug__count {
1123
+ display: inline-block;
1124
+ min-width: 16px;
1125
+ padding: 0 5px;
1126
+ margin-left: 5px;
1127
+ border-radius: 999px;
1128
+ background: var(--bg-hover);
1129
+ color: var(--text-dim);
1130
+ font-size: 12px;
1131
+ }
1132
+ .debug__body {
1133
+ position: relative;
1134
+ flex: 1 1 auto;
1135
+ overflow: auto;
1136
+ padding: 12px 16px;
1137
+ }
1138
+ .debug__placeholder {
1139
+ display: block;
1140
+ width: 100%;
1141
+ height: 38px;
1142
+ }
1143
+ .debug__empty {
1144
+ color: var(--text-faint);
1145
+ font-size: 13px;
1146
+ text-align: center;
1147
+ }
1148
+ /* 来源 tab(Phase E:导入来源可追溯) */
1149
+ .debug__meta-list {
1150
+ margin: 0;
1151
+ display: flex;
1152
+ flex-direction: column;
1153
+ gap: 8px;
1154
+ }
1155
+ .debug__meta-row {
1156
+ display: grid;
1157
+ grid-template-columns: 72px 1fr;
1158
+ gap: 8px;
1159
+ align-items: baseline;
1160
+ font-size: 13px;
1161
+ }
1162
+ .debug__meta-row dt {
1163
+ color: var(--text-faint);
1164
+ margin: 0;
1165
+ }
1166
+ .debug__meta-row dd {
1167
+ margin: 0;
1168
+ color: var(--text);
1169
+ word-break: break-all;
1170
+ }
1171
+ .debug__meta-row code {
1172
+ font-family: var(--mono, ui-monospace, monospace);
1173
+ font-size: 12px;
1174
+ background: var(--bg-hover);
1175
+ padding: 1px 5px;
1176
+ border-radius: 4px;
1177
+ }
1178
+ .debug__meta-muted {
1179
+ color: var(--text-faint);
1180
+ font-size: 12px;
1181
+ }
1182
+ .debug__meta-err {
1183
+ color: var(--danger);
1184
+ font-size: 12px;
1185
+ }
1186
+ .debug__meta-hint {
1187
+ margin-top: 12px;
1188
+ padding-top: 10px;
1189
+ border-top: 1px solid var(--border);
1190
+ color: var(--text-faint);
1191
+ font-size: 12px;
1192
+ line-height: 1.6;
1193
+ }
1194
+ /* 运行态面板(点击状态栏「运行态」指示器弹出 / 收起)。高度由面板头部拖拽调整
1195
+ (startResize,无专门拖拽节点,与预览区宽度调节同一套 mousedown 机制),初始 240px,
1196
+ 约束 [120px, 60vh],与错误/事件面板共用。 */
1197
+ .statepanel {
1198
+ flex: 0 0 auto;
1199
+ display: flex;
1200
+ flex-direction: column;
1201
+ /* 初始高度 240px;运行时由头部拖拽调整,并通过 JS 持久化回写 height */
1202
+ height: 240px;
1203
+ min-height: 120px;
1204
+ max-height: 60vh;
1205
+ /* hidden 避免面板自身出现滚动条(滚动交给 .statepanel__tree) */
1206
+ overflow: hidden;
1207
+ /* 通过 flex order 把面板固定到状态栏之上,确保状态栏始终贴底(不被面板顶上来) */
1208
+ order: 1;
1209
+ background: var(--bg-panel);
1210
+ border-top: 1px solid var(--border-strong);
1211
+ }
1212
+ .statepanel__head {
1213
+ flex: 0 0 auto;
1214
+ display: flex;
1215
+ align-items: center;
1216
+ gap: 8px;
1217
+ height: var(--ctrl-lg);
1218
+ padding: 0 12px;
1219
+ background: var(--bg-bar);
1220
+ border-bottom: 1px solid var(--border);
1221
+ font-size: 13px;
1222
+ color: var(--text);
1223
+ /* 头部即拖拽手柄(无专门节点):整条可抓取,向上拖放大面板 */
1224
+ cursor: row-resize;
1225
+ user-select: none;
1226
+ }
1227
+ .statepanel__title {
1228
+ font-weight: 600;
1229
+ letter-spacing: 0.2px;
1230
+ }
1231
+ .statepanel__title code {
1232
+ font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
1233
+ font-size: 12px;
1234
+ color: var(--accent);
1235
+ }
1236
+ .statepanel__close {
1237
+ margin-left: auto;
1238
+ display: inline-flex;
1239
+ align-items: center;
1240
+ justify-content: center;
1241
+ width: 18px;
1242
+ height: 18px;
1243
+ padding: 0;
1244
+ background: none;
1245
+ border: none;
1246
+ color: var(--text-faint);
1247
+ font-size: 15px;
1248
+ line-height: 1;
1249
+ cursor: pointer;
1250
+ transition: color 0.15s;
1251
+ }
1252
+ .statepanel__close:hover {
1253
+ color: var(--text);
1254
+ }
1255
+ .statepanel__tree {
1256
+ flex: 1 1 auto;
1257
+ overflow: auto;
1258
+ padding: 8px 12px 10px;
1259
+ }
1260
+
1261
+ /* 状态栏「运行态」指示器:复用问题/事件指示器的统一语言(无边框/无圆角/无 hover 背景块)。
1262
+ 展开时仅文字着 accent 色,与 is-log 一致,作为「面板已打开」的轻量提示。 */
1263
+ .statusbar__item--prob.is-open {
1264
+ color: var(--accent);
1265
+ }
1266
+ .debug__tag {
1267
+ display: inline-block;
1268
+ padding: 1px 8px;
1269
+ border-radius: 999px;
1270
+ font-size: 12px;
1271
+ line-height: 1.6;
1272
+ }
1273
+ .debug__tag--ok {
1274
+ color: var(--ok-strong);
1275
+ background: var(--ok-soft);
1276
+ }
1277
+ .debug__tag--warn {
1278
+ color: var(--tag-warn-fg);
1279
+ background: var(--tag-warn-bg);
1280
+ }
1281
+ .debug__tag--err {
1282
+ color: var(--danger);
1283
+ background: var(--danger-soft);
1284
+ }
1285
+ /* 接口调用标签页工具条(仿 Chrome 控制台) */
1286
+ .debug__clear {
1287
+ /* 通用带边框按钮:固定在接口调用 tab 右上角、不随滚动,方便随时清空日志 */
1288
+ position: sticky;
1289
+ top: 0;
1290
+ float: right;
1291
+ z-index: var(--z-sticky);
1292
+ display: inline-flex;
1293
+ align-items: center;
1294
+ justify-content: center;
1295
+ width: 28px;
1296
+ height: 28px;
1297
+ padding: 0;
1298
+ background: var(--bg-input);
1299
+ color: var(--text);
1300
+ border: 1px solid var(--border-strong);
1301
+ border-radius: 8px;
1302
+ cursor: pointer;
1303
+ box-shadow: -10px 0 10px var(--bg-panel);
1304
+ transition: background 0.15s, border-color 0.15s, transform 0.05s;
1305
+ }
1306
+ .debug__clear:hover:not(:disabled) {
1307
+ background: var(--bg-hover);
1308
+ border-color: var(--accent-border);
1309
+ }
1310
+ .debug__clear:active:not(:disabled) {
1311
+ transform: translateY(1px);
1312
+ }
1313
+ .debug__clear:disabled {
1314
+ opacity: 0.5;
1315
+ cursor: default;
1316
+ }
1317
+ /* 接口调用列表顶部留白,避免首行被右上角浮动的清空按钮遮挡 */
1318
+ .debug__list--api {
1319
+ padding-top: 34px;
1320
+ }
1321
+ .debug__list {
1322
+ list-style: none;
1323
+ margin: 0;
1324
+ padding: 0;
1325
+ }
1326
+ .debug__item {
1327
+ padding: 4px 8px;
1328
+ margin-bottom: 6px;
1329
+ border-radius: 8px;
1330
+ font-size: 12px;
1331
+ line-height: 1.6;
1332
+ word-break: break-all;
1333
+ }
1334
+ .debug__item--err {
1335
+ background: var(--danger-soft);
1336
+ color: var(--err-item-fg);
1337
+ }
1338
+ .debug__item--api {
1339
+ border: 1px solid var(--api-item-border);
1340
+ color: var(--api-item-fg);
1341
+ background: var(--api-item-bg);
1342
+ }
1343
+ .debug__kind {
1344
+ font-weight: 600;
1345
+ opacity: 0.85;
1346
+ }
1347
+ .debug__pre {
1348
+ margin: 4px 0 0;
1349
+ white-space: pre-wrap;
1350
+ font-size: 11px;
1351
+ color: var(--text-dim);
1352
+ }
1353
+
1354
+ /* 接口调用条目:方法名 + 最右侧复制图标 */
1355
+ .debug__api-row {
1356
+ display: flex;
1357
+ align-items: center;
1358
+ justify-content: space-between;
1359
+ gap: 8px;
1360
+ }
1361
+ .debug__copy-ic {
1362
+ flex: 0 0 auto;
1363
+ display: inline-flex;
1364
+ align-items: center;
1365
+ justify-content: center;
1366
+ width: 24px;
1367
+ height: 22px;
1368
+ padding: 0;
1369
+ background: transparent;
1370
+ border: 1px solid transparent;
1371
+ border-radius: 6px;
1372
+ color: var(--text-faint);
1373
+ cursor: pointer;
1374
+ transition: background 0.15s, color 0.15s, border-color 0.15s;
1375
+ }
1376
+ .debug__copy-ic:hover {
1377
+ background: var(--bg-hover);
1378
+ border-color: var(--border);
1379
+ color: var(--text);
1380
+ }
1381
+ .debug__copy-ok {
1382
+ font-size: 14px;
1383
+ color: var(--copy-ok);
1384
+ line-height: 1;
1385
+ }
1386
+
1387
+ /* 接口调用「返回数据」结构化折叠树(复用 json-tree.css 的 .jt__* 配色) */
1388
+ .apt {
1389
+ font-family: 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace;
1390
+ font-size: 12px;
1391
+ line-height: 1.6;
1392
+ color: var(--text-dim);
1393
+ margin-top: 4px;
1394
+ }
1395
+ .apt__list {
1396
+ list-style: none;
1397
+ margin: 0;
1398
+ padding: 0;
1399
+ }
1400
+ .apt__node {
1401
+ position: relative;
1402
+ }
1403
+ .apt__branch,
1404
+ .apt__leaf {
1405
+ display: flex;
1406
+ align-items: center;
1407
+ gap: 4px;
1408
+ padding: 1px 4px;
1409
+ border-radius: 4px;
1410
+ cursor: pointer;
1411
+ }
1412
+ .apt__branch:hover,
1413
+ .apt__leaf:hover {
1414
+ background: var(--bg-hover);
1415
+ }
1416
+ .apt__caret {
1417
+ flex: 0 0 auto;
1418
+ width: 12px;
1419
+ text-align: center;
1420
+ color: var(--text-faint);
1421
+ font-size: 9px;
1422
+ transition: transform 0.15s ease;
1423
+ transform: rotate(0deg);
1424
+ user-select: none;
1425
+ }
1426
+ .apt__caret--open {
1427
+ transform: rotate(90deg);
1428
+ color: var(--accent);
1429
+ }
1430
+ .apt__caret--leaf {
1431
+ visibility: hidden;
1432
+ }
1433
+ .apt__children {
1434
+ padding-left: 16px;
1435
+ border-left: 1px solid var(--border);
1436
+ margin-left: 4px;
1437
+ }
1438
+ .apt__close {
1439
+ padding: 1px 4px 1px 20px;
1440
+ color: var(--text-faint);
1441
+ }
1442
+ .debug__scope {
1443
+ margin: 0;
1444
+ font-size: 11px;
1445
+ line-height: 1.5;
1446
+ white-space: pre-wrap;
1447
+ color: var(--text-dim);
1448
+ }
1449
+
1450
+ /* 数据绑定:JSON 树 + 底部操作按钮
1451
+ 容器铺满调试面板主体(绝对定位贴合 body 内边距区域),内部用 flex 列布局,
1452
+ JSON 树占据剩余空间并可独立滚动,底部操作按钮始终钉在底部——
1453
+ 数据较少时不再上浮,数据较多时树内部滚动而按钮不随之移动。 */
1454
+ .debug__scope-wrap {
1455
+ position: absolute;
1456
+ /* right:0 让 JSON 树滚动条紧贴面板最右边(与其他 tab 用 body padding 作
1457
+ 16px 滚动框不同,本 tab 滚动交给内部 .debug__scope-tree,需要贴边);
1458
+ left:16px 与 body 的 16px 内边距对齐,让树内容左缘与其他 tab 一致。 */
1459
+ inset: 10px 10px 10px 16px;
1460
+ display: flex;
1461
+ flex-direction: column;
1462
+ min-height: 0;
1463
+ }
1464
+ .debug__scope-tree {
1465
+ flex: 1 1 auto;
1466
+ min-height: 0;
1467
+ overflow: auto;
1468
+ }
1469
+ .debug__scope-foot {
1470
+ flex: 0 0 auto;
1471
+ position: absolute;
1472
+ bottom: 0;
1473
+ right: 0;
1474
+ display: flex;
1475
+ justify-content: flex-end;
1476
+ align-items: center;
1477
+ gap: 8px;
1478
+ padding: 8px 12px;
1479
+ }
1480
+ .debug__copied {
1481
+ font-size: 12px;
1482
+ color: var(--accent);
1483
+ }
1484
+ .debug__copy {
1485
+ display: inline-flex;
1486
+ align-items: center;
1487
+ height: var(--ctrl-sm);
1488
+ padding: 0 14px;
1489
+ background: var(--bg-input);
1490
+ color: var(--text);
1491
+ border: 1px solid var(--accent-border);
1492
+ border-radius: var(--radius-sm);
1493
+ font-size: 13px;
1494
+ cursor: pointer;
1495
+ transition: background 0.15s, border-color 0.15s;
1496
+ }
1497
+ .debug__copy:hover {
1498
+ background: var(--bg-hover);
1499
+ border-color: var(--accent);
1500
+ }
1501
+
1502
+ /* 数据绑定 Raw 模式:直接复用 .debug__scope-wrap 的绝对定位盒(inset 已占满 body 内容区),
1503
+ 不再额外设 height:100%——否则 top/inset 的 bottom 与 height 冲突,盒子会比内容区高 10px,
1504
+ 底部「应用/取消」按钮被挤出可视区、需滚动才见。这里仅保留 flex 列布局语义:
1505
+ 编辑器占剩余空间并内部滚动,底部操作按钮始终钉在底部。 */
1506
+ .debug__scope-wrap--raw {
1507
+ display: flex;
1508
+ flex-direction: column;
1509
+ min-height: 0;
1510
+ }
1511
+ .debug__scope-raw {
1512
+ flex: 1 1 auto;
1513
+ min-height: 0;
1514
+ display: flex;
1515
+ flex-direction: column;
1516
+ }
1517
+ .debug__raw-editor {
1518
+ flex: 1 1 auto;
1519
+ min-height: 160px;
1520
+ width: 100%;
1521
+ padding: 12px 14px;
1522
+ background: var(--bg-input);
1523
+ color: var(--text);
1524
+ border: 1px solid var(--border-strong);
1525
+ border-radius: 8px;
1526
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1527
+ font-size: 12px;
1528
+ line-height: 1.6;
1529
+ tab-size: 2;
1530
+ white-space: pre;
1531
+ overflow: auto;
1532
+ outline: none;
1533
+ }
1534
+ .debug__raw-editor:focus {
1535
+ border-color: var(--accent-border);
1536
+ }
1537
+ /* CodeJar 托管的原始 JSON 编辑器:Prism(json) 语法高亮配色(与 SetCode 暗色面板一致) */
1538
+ .debug__raw-editor .token.property,
1539
+ .debug__raw-editor .token.tag,
1540
+ .debug__raw-editor .token.boolean,
1541
+ .debug__raw-editor .token.number,
1542
+ .debug__raw-editor .token.constant,
1543
+ .debug__raw-editor .token.symbol { color: var(--code-prop); }
1544
+ .debug__raw-editor .token.selector,
1545
+ .debug__raw-editor .token.attr-name,
1546
+ .debug__raw-editor .token.string,
1547
+ .debug__raw-editor .token.char,
1548
+ .debug__raw-editor .token.builtin { color: var(--code-str); }
1549
+ .debug__raw-editor .token.operator,
1550
+ .debug__raw-editor .token.entity,
1551
+ .debug__raw-editor .token.url { color: var(--code-num); }
1552
+ .debug__raw-editor .token.punctuation { color: var(--code-punc); }
1553
+ .debug__raw-editor .token.keyword,
1554
+ .debug__raw-editor .token.null { color: var(--code-kw); }
1555
+ .debug__raw-editor .token.function,
1556
+ .debug__raw-editor .token.class-name { color: var(--code-fn); }
1557
+ .debug__raw-err {
1558
+ flex: 0 0 auto;
1559
+ margin: 6px 0 0;
1560
+ font-size: 12px;
1561
+ color: var(--danger);
1562
+ }
1563
+
1564
+ /* 底部状态栏(类 VSCode 暗色状态栏,沿用主题 token,与顶栏/面板统一) */
1565
+ .statusbar {
1566
+ flex: 0 0 auto;
1567
+ display: flex;
1568
+ align-items: stretch;
1569
+ height: 28px;
1570
+ /* 永远在底部面板之上(order 最大),保证状态栏始终贴底,不被面板顶上来 */
1571
+ order: 3;
1572
+ padding: 0 2px;
1573
+ background: var(--bg-bar);
1574
+ border-top: 1px solid var(--border);
1575
+ font-size: 12px;
1576
+ color: var(--text-dim);
1577
+ user-select: none;
1578
+ }
1579
+ .statusbar__spacer {
1580
+ flex: 1 1 auto;
1581
+ }
1582
+ .statusbar__item {
1583
+ display: inline-flex;
1584
+ align-items: center;
1585
+ gap: 5px;
1586
+ padding: 0 10px;
1587
+ height: 100%;
1588
+ white-space: nowrap;
1589
+ cursor: default;
1590
+ }
1591
+ .statusbar__item--right {
1592
+ color: var(--text-faint);
1593
+ }
1594
+ /* 问题指示器:沿用统一 hover 语言(bg-hover + accent 文字),保持与整体风格一致 */
1595
+ /* 问题指示器:本质是一行状态栏文本读数,不是按钮——
1596
+ 无边框 / 无圆角 / 无 hover 背景块;仅在有错误时文字+图标着 danger 红作为提示 */
1597
+ .statusbar__item--prob {
1598
+ cursor: pointer;
1599
+ color: var(--text-dim);
1600
+ font-weight: 400;
1601
+ border: none;
1602
+ border-radius: 0;
1603
+ background: transparent;
1604
+ }
1605
+ .statusbar__item--prob:hover {
1606
+ color: var(--text);
1607
+ }
1608
+ .statusbar__item--prob.is-error {
1609
+ color: var(--danger);
1610
+ }
1611
+ .statusbar__item--prob.is-error:hover {
1612
+ color: var(--danger);
1613
+ }
1614
+ .statusbar__item--prob.is-log {
1615
+ color: var(--accent);
1616
+ }
1617
+ .statusbar__item--prob.is-log:hover {
1618
+ color: var(--accent);
1619
+ }
1620
+ .statusbar__item--prob.is-ok {
1621
+ color: var(--text-faint);
1622
+ cursor: default;
1623
+ }
1624
+ .statusbar__item--prob.is-ok:hover {
1625
+ color: var(--text-faint);
1626
+ }
1627
+ .statusbar__icon {
1628
+ font-size: 14px;
1629
+ line-height: 1;
1630
+ }
1631
+ /* 运行态偏离基线(有未落库的临时调试值)时,状态栏小点变琥珀黄提示 */
1632
+ .statusbar__icon.is-dirty {
1633
+ color: var(--warn);
1634
+ }
1635
+
1636
+ /* 底部面板(事件面板 / 绑定错误面板):点击状态栏指示器弹出 / 收起。与运行态面板互斥,
1637
+ 三者共用面板高度,由头部拖拽调整(无专门拖拽节点,与预览区宽度调节同一机制),约束 [120px, 60vh]。 */
1638
+ .errpanel {
1639
+ flex: 0 0 auto;
1640
+ display: flex;
1641
+ flex-direction: column;
1642
+ height: 240px;
1643
+ min-height: 120px;
1644
+ max-height: 60vh;
1645
+ overflow: hidden;
1646
+ /* 与运行态面板同属「状态栏之上的底部面板」,固定 order 确保状态栏始终贴底 */
1647
+ order: 2;
1648
+ background: var(--bg-panel);
1649
+ border-top: 1px solid var(--border-strong);
1650
+ }
1651
+ .errpanel__head {
1652
+ flex: 0 0 auto;
1653
+ display: flex;
1654
+ align-items: center;
1655
+ gap: 8px;
1656
+ height: var(--ctrl-lg);
1657
+ padding: 0 12px;
1658
+ background: var(--bg-bar);
1659
+ /* 头部即拖拽手柄(无专门节点):整条可抓取,向上拖放大面板 */
1660
+ cursor: row-resize;
1661
+ user-select: none;
1662
+ border-bottom: 1px solid var(--border);
1663
+ font-size: 13px;
1664
+ color: var(--text);
1665
+ }
1666
+ .errpanel__title {
1667
+ font-weight: 600;
1668
+ letter-spacing: 0.2px;
1669
+ }
1670
+ /* 头部右侧操作区:图标按钮统一靠右 */
1671
+ .errpanel__actions {
1672
+ margin-left: auto;
1673
+ display: flex;
1674
+ align-items: center;
1675
+ gap: 6px;
1676
+ }
1677
+ /* 图标按钮(关闭 / 清空):纯图标,无背景/边框/圆角,仅字形 + hover 变色,节省空间 */
1678
+ .errpanel__iconbtn {
1679
+ display: inline-flex;
1680
+ align-items: center;
1681
+ justify-content: center;
1682
+ width: 20px;
1683
+ height: 20px;
1684
+ padding: 0;
1685
+ background: none;
1686
+ border: none;
1687
+ color: var(--text-faint);
1688
+ cursor: pointer;
1689
+ transition: color 0.15s;
1690
+ }
1691
+ .errpanel__iconbtn:hover:not(:disabled) {
1692
+ color: var(--text);
1693
+ }
1694
+ .errpanel__iconbtn:disabled {
1695
+ opacity: 0.35;
1696
+ cursor: not-allowed;
1697
+ }
1698
+ /* 清空按钮(垃圾桶)hover 偏危险色提示 */
1699
+ .errpanel__clear:hover:not(:disabled) {
1700
+ color: var(--danger);
1701
+ }
1702
+ .errpanel__body {
1703
+ flex: 1 1 auto;
1704
+ overflow: auto;
1705
+ padding: 12px 16px;
1706
+ }
1707
+ /* 事件面板内「日志 / 错误」分区标题 */
1708
+ .ep__sec-title {
1709
+ margin: 8px 0 6px;
1710
+ padding-bottom: 4px;
1711
+ font-size: 12px;
1712
+ font-weight: 600;
1713
+ color: var(--text-dim);
1714
+ border-bottom: 1px dashed var(--border);
1715
+ }
1716
+ .ep__sec-title:first-child {
1717
+ margin-top: 0;
1718
+ }
1719
+ /* 事件面板日志:每行 = 等级标签 + 内容(语法高亮 / JSON 折叠) */
1720
+ .debug__list--log {
1721
+ display: flex;
1722
+ flex-direction: column;
1723
+ }
1724
+ .log__item {
1725
+ display: flex;
1726
+ align-items: flex-start;
1727
+ gap: 10px;
1728
+ padding: 6px 8px;
1729
+ border-bottom: 1px solid var(--border);
1730
+ }
1731
+ .log__item:last-child {
1732
+ border-bottom: none;
1733
+ }
1734
+ .log__item:hover {
1735
+ background: var(--bg-input);
1736
+ }
1737
+ /* 等级标签:小药丸,按级别着色 */
1738
+ .log__tag {
1739
+ flex: 0 0 auto;
1740
+ margin-top: 1px;
1741
+ padding: 1px 6px;
1742
+ border-radius: 4px;
1743
+ font-family: 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace;
1744
+ font-size: 11px;
1745
+ font-weight: 600;
1746
+ line-height: 1.5;
1747
+ text-transform: uppercase;
1748
+ letter-spacing: 0.4px;
1749
+ color: var(--text-dim);
1750
+ background: var(--bg-input);
1751
+ border: 1px solid var(--border);
1752
+ }
1753
+ .log__tag--warn {
1754
+ color: var(--log-warn-fg);
1755
+ background: var(--log-warn-bg);
1756
+ border-color: var(--log-warn-border);
1757
+ }
1758
+ .log__tag--error {
1759
+ color: var(--danger);
1760
+ background: var(--danger-soft);
1761
+ border-color: var(--danger-soft);
1762
+ }
1763
+ /* 内容区:基础类型内联、复合类型独占整行 */
1764
+ .log__content {
1765
+ flex: 1 1 auto;
1766
+ min-width: 0;
1767
+ display: flex;
1768
+ flex-wrap: wrap;
1769
+ align-items: flex-start;
1770
+ gap: 4px 10px;
1771
+ font-family: 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace;
1772
+ font-size: 12px;
1773
+ line-height: 1.5;
1774
+ color: var(--text);
1775
+ }
1776
+ .log__val {
1777
+ white-space: pre-wrap;
1778
+ word-break: break-word;
1779
+ }
1780
+ .log__json {
1781
+ flex: 1 1 100%;
1782
+ padding: 2px 0;
1783
+ }
1784
+ .log__empty {
1785
+ color: var(--text-faint);
1786
+ }
1787
+
1788
+ /* 弹窗卡片图层(alertCard):在预览区域内定位,按 gravity 定位的卡片面板 */
1789
+ .alert-layer {
1790
+ position: absolute;
1791
+ inset: 0;
1792
+ z-index: var(--z-modal);
1793
+ }
1794
+ .alert-mask {
1795
+ position: absolute;
1796
+ inset: 0;
1797
+ display: flex;
1798
+ background: rgba(0, 0, 0, 0.55);
1799
+ padding: 16px;
1800
+ }
1801
+ .alert-mask--bottom {
1802
+ align-items: flex-end;
1803
+ justify-content: center;
1804
+ }
1805
+ .alert-mask--top {
1806
+ align-items: flex-start;
1807
+ justify-content: center;
1808
+ }
1809
+ .alert-mask--center {
1810
+ align-items: center;
1811
+ justify-content: center;
1812
+ }
1813
+ .alert-mask--left {
1814
+ align-items: center;
1815
+ justify-content: flex-start;
1816
+ }
1817
+ .alert-mask--right {
1818
+ align-items: center;
1819
+ justify-content: flex-end;
1820
+ }
1821
+ .alert-panel {
1822
+ width: 100%;
1823
+ max-width: 100%;
1824
+ max-height: 92vh;
1825
+ overflow-x: hidden;
1826
+ overflow-y: auto;
1827
+ background: #ffffff;
1828
+ border-radius: 12px;
1829
+ box-shadow: 0 18px 60px rgba(0, 0, 0, 0.5);
1830
+ /* 入场动画 */
1831
+ animation: alert-pop 0.20s ease-out;
1832
+ }
1833
+ @keyframes alert-pop {
1834
+ from {
1835
+ opacity: 0;
1836
+ transform: scale(0.96) translateY(8px);
1837
+ }
1838
+ to {
1839
+ opacity: 1;
1840
+ transform: scale(1) translateY(0);
1841
+ }
1842
+ }
1843
+ .alert-mask--center .alert-panel,
1844
+ .alert-mask--left .alert-panel,
1845
+ .alert-mask--right .alert-panel {
1846
+ /* 居中 / 左右定位时取消大圆角,更像原生弹框 */
1847
+ border-radius: 12px;
1848
+ }
1849
+
1850
+ /* ============ 设备级模拟 UI(alertDialog 对话框 / showToast 弱提示) ============ */
1851
+ /* 相对固定视口 phone-viewport 定位,不随卡片内容滚动;
1852
+ z-index 高于 alertCard 图层(1000),模拟原生对话框盖住弹窗卡片 */
1853
+ .device-ui {
1854
+ position: absolute;
1855
+ inset: 0;
1856
+ z-index: var(--z-deviceui);
1857
+ pointer-events: none; /* 默认不拦截卡片交互,弹层元素各自开启 */
1858
+ }
1859
+
1860
+ /* alertDialog:原生风格居中确认对话框 */
1861
+ .dlg-mask {
1862
+ position: absolute;
1863
+ inset: 0;
1864
+ display: flex;
1865
+ align-items: center;
1866
+ justify-content: center;
1867
+ background: rgba(0, 0, 0, 0.45);
1868
+ pointer-events: auto;
1869
+ }
1870
+ .dlg {
1871
+ width: 288px;
1872
+ max-width: 82%;
1873
+ background: #ffffff;
1874
+ border-radius: 12px;
1875
+ padding: 24px 20px 0;
1876
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
1877
+ text-align: center;
1878
+ animation: dlg-pop 0.20s ease-out;
1879
+ }
1880
+ @keyframes dlg-pop {
1881
+ from {
1882
+ opacity: 0;
1883
+ transform: scale(0.94);
1884
+ }
1885
+ to {
1886
+ opacity: 1;
1887
+ transform: scale(1);
1888
+ }
1889
+ }
1890
+ .dlg__title {
1891
+ font-size: 16px;
1892
+ font-weight: 600;
1893
+ color: #333333;
1894
+ }
1895
+ .dlg__msg {
1896
+ margin-top: 10px;
1897
+ font-size: 14px;
1898
+ line-height: 1.5;
1899
+ color: #666666;
1900
+ text-align: left;
1901
+ word-break: break-all;
1902
+ white-space: pre-wrap;
1903
+ max-height: 320px;
1904
+ overflow-y: auto;
1905
+ }
1906
+ .dlg__title + .dlg__msg {
1907
+ margin-top: 12px;
1908
+ }
1909
+ .dlg__btns {
1910
+ display: flex;
1911
+ /* 负外边距抵消对话框左右内边距,分割线贯通左右边缘 */
1912
+ margin: 18px -20px 0;
1913
+ border-top: 1px solid #e8e8e8;
1914
+ }
1915
+ .dlg__btn {
1916
+ flex: 1 1 0;
1917
+ min-width: 0;
1918
+ padding: 14px 0;
1919
+ background: none;
1920
+ border: none;
1921
+ font-size: 15px;
1922
+ cursor: pointer;
1923
+ }
1924
+ .dlg__btn--ok {
1925
+ color: #1677ff;
1926
+ font-weight: 500;
1927
+ }
1928
+ .dlg__btn--cancel {
1929
+ color: #1677ff;
1930
+ border-right: 1px solid #e8e8e8;
1931
+ }
1932
+ /* cancelGrayStyle:取消按钮灰色风格 */
1933
+ .dlg__btn--cancel.dlg__btn--gray {
1934
+ color: #888888;
1935
+ }
1936
+
1937
+ /* showToast:居中弱提示(对齐 Vant Toast 视觉语言:屏幕正中、暗色半透明胶囊、
1938
+ 白描边图标、圆角 8px;设备框为独立显示区,不随编辑器主题变化) */
1939
+ .dtoast {
1940
+ position: absolute;
1941
+ top: 50%;
1942
+ left: 50%;
1943
+ transform: translate(-50%, -50%);
1944
+ display: flex;
1945
+ flex-direction: column;
1946
+ align-items: center;
1947
+ justify-content: center;
1948
+ gap: 8px;
1949
+ max-width: 76%;
1950
+ min-width: 0;
1951
+ padding: 10px 14px;
1952
+ background: rgba(0, 0, 0, 0.7);
1953
+ color: #ffffff;
1954
+ border-radius: 10px;
1955
+ font-size: 14px;
1956
+ line-height: 20px;
1957
+ text-align: center;
1958
+ box-sizing: border-box;
1959
+ pointer-events: none;
1960
+ }
1961
+ /* 带图标的 Toast:图标在上、文案在下,方形布局(对齐 Vant 图标型 Toast) */
1962
+ .dtoast--icon {
1963
+ min-width: 96px;
1964
+ padding: 16px;
1965
+ }
1966
+ .dtoast__icon {
1967
+ flex: 0 0 auto;
1968
+ width: 36px;
1969
+ height: 36px;
1970
+ display: inline-flex;
1971
+ align-items: center;
1972
+ justify-content: center;
1973
+ /* 去掉彩色圆底,改为白描边图标(Vant Toast 语义:成功/失败仅以形状区分) */
1974
+ }
1975
+ .dtoast__icon-svg {
1976
+ width: 28px;
1977
+ height: 28px;
1978
+ fill: none;
1979
+ stroke: #ffffff;
1980
+ stroke-width: 2.4;
1981
+ stroke-linecap: round;
1982
+ stroke-linejoin: round;
1983
+ }
1984
+ .dtoast__msg {
1985
+ word-break: break-all;
1986
+ }
1987
+
1988
+ /* App 级弱提示(顶栏异步操作完成反馈):固定悬浮在 App 外壳顶部居中,
1989
+ 不被手机预览框裁切;视觉语言与设备框内 .dtoast 一致。 */
1990
+ .apptoast {
1991
+ position: fixed;
1992
+ top: 56px;
1993
+ left: 50%;
1994
+ transform: translateX(-50%);
1995
+ z-index: var(--z-toast-app);
1996
+ display: inline-flex;
1997
+ align-items: center;
1998
+ gap: 8px;
1999
+ max-width: 76%;
2000
+ padding: 12px 20px;
2001
+ background: rgba(17, 17, 17, 0.82);
2002
+ color: #fff;
2003
+ border-radius: 10px;
2004
+ font-size: 14px;
2005
+ line-height: 1.4;
2006
+ box-shadow: 0 6px 24px rgba(0, 0, 0, 0.28);
2007
+ pointer-events: none;
2008
+ }
2009
+ .apptoast__icon {
2010
+ flex: 0 0 auto;
2011
+ width: 18px;
2012
+ height: 18px;
2013
+ border-radius: 50%;
2014
+ display: inline-flex;
2015
+ align-items: center;
2016
+ justify-content: center;
2017
+ /* 图标底色:仅显式声明类型才着色(ok=成功绿 / err=危险红),
2018
+ 未知/缺失类型落在中性灰(--text-faint),绝不默认成功绿——
2019
+ 防止调用方传错类型(如 'error')时把失败提示显示成成功色 */
2020
+ background: var(--text-faint);
2021
+ }
2022
+ .apptoast--ok .apptoast__icon {
2023
+ background: var(--ok);
2024
+ }
2025
+ .apptoast--err .apptoast__icon {
2026
+ background: var(--danger);
2027
+ }
2028
+ .apptoast__icon-svg {
2029
+ width: 12px;
2030
+ height: 12px;
2031
+ fill: none;
2032
+ stroke: #ffffff;
2033
+ stroke-width: 2.6;
2034
+ stroke-linecap: round;
2035
+ stroke-linejoin: round;
2036
+ }
2037
+ .apptoast__msg {
2038
+ word-break: break-all;
2039
+ }
2040
+ .apptoast-fade-enter-active,
2041
+ .apptoast-fade-leave-active {
2042
+ transition: opacity 0.2s ease, transform 0.2s ease;
2043
+ }
2044
+ .apptoast-fade-enter,
2045
+ .apptoast-fade-leave-to {
2046
+ opacity: 0;
2047
+ transform: translate(-50%, -8px);
2048
+ }
2049
+
2050
+ /* 弹层淡入淡出 */
2051
+ .dlg-fade-enter-active,
2052
+ .dlg-fade-leave-active,
2053
+ .toast-fade-enter-active,
2054
+ .toast-fade-leave-active {
2055
+ transition: opacity 0.18s ease;
2056
+ }
2057
+ .dlg-fade-enter,
2058
+ .dlg-fade-leave-to,
2059
+ .toast-fade-enter,
2060
+ .toast-fade-leave-to {
2061
+ opacity: 0;
2062
+ }
2063
+
2064
+ /* showPicker:底部弹出级联选择器(对齐原生 picker) */
2065
+ .pk-mask {
2066
+ position: absolute;
2067
+ inset: 0;
2068
+ display: flex;
2069
+ align-items: flex-end;
2070
+ background: rgba(0, 0, 0, 0.4);
2071
+ pointer-events: auto;
2072
+ }
2073
+ .pk-sheet {
2074
+ width: 100%;
2075
+ background: #ffffff;
2076
+ border-radius: 12px 12px 0 0;
2077
+ animation: pk-up 0.20s ease-out;
2078
+ }
2079
+ @keyframes pk-up {
2080
+ from {
2081
+ transform: translateY(100%);
2082
+ }
2083
+ to {
2084
+ transform: translateY(0);
2085
+ }
2086
+ }
2087
+ .pk-bar {
2088
+ display: flex;
2089
+ align-items: center;
2090
+ justify-content: space-between;
2091
+ height: 44px;
2092
+ padding: 0 16px;
2093
+ box-sizing: border-box;
2094
+ border-bottom: 1px solid #eef0f2;
2095
+ }
2096
+ .pk-bar__title {
2097
+ font-size: 15px;
2098
+ font-weight: 600;
2099
+ color: #333333;
2100
+ }
2101
+ .pk-bar__btn {
2102
+ background: none;
2103
+ border: none;
2104
+ font-size: 15px;
2105
+ cursor: pointer;
2106
+ padding: 4px 2px;
2107
+ }
2108
+ .pk-bar__btn--cancel {
2109
+ color: #888888;
2110
+ }
2111
+ .pk-bar__btn--ok {
2112
+ color: #1677ff;
2113
+ font-weight: 600;
2114
+ }
2115
+ .pk-cols {
2116
+ display: flex;
2117
+ position: relative;
2118
+ height: 230px;
2119
+ overflow: hidden;
2120
+ }
2121
+ .pk-col {
2122
+ flex: 1 1 0;
2123
+ position: relative;
2124
+ height: 100%;
2125
+ min-width: 0;
2126
+ }
2127
+ /* 中间选中高亮带:列中线一条浅色带 */
2128
+ .pk-col__band {
2129
+ position: absolute;
2130
+ top: 50%;
2131
+ left: 0;
2132
+ right: 0;
2133
+ height: 40px;
2134
+ transform: translateY(-50%);
2135
+ background: rgba(22, 119, 255, 0.08);
2136
+ border-top: 1px solid #d6e4ff;
2137
+ border-bottom: 1px solid #d6e4ff;
2138
+ pointer-events: none;
2139
+ }
2140
+ .pk-col__list {
2141
+ height: 100%;
2142
+ overflow-y: auto;
2143
+ /* 上下留白使中间项对齐高亮带(230/2=115,项高40,padding 95 -> 首项中线落在115) */
2144
+ padding: 95px 0;
2145
+ -webkit-overflow-scrolling: touch;
2146
+ }
2147
+ .pk-item {
2148
+ height: 40px;
2149
+ line-height: 40px;
2150
+ text-align: center;
2151
+ font-size: 16px;
2152
+ color: #333333;
2153
+ cursor: pointer;
2154
+ white-space: nowrap;
2155
+ overflow: hidden;
2156
+ text-overflow: ellipsis;
2157
+ padding: 0 6px;
2158
+ }
2159
+ .pk-item--on {
2160
+ color: #1677ff;
2161
+ font-weight: 600;
2162
+ }
2163
+ /* showDatePicker(滚轮模式):保留 .pk-col__list 原生 overflow-y:auto + padding 结构,
2164
+ 通过 scrollTop 定位,避免大幅 translateY 在部分浏览器中的渲染裁剪问题。
2165
+ 仅作用于 .pk-cols--wheel,不影响 showPicker 的 .pk-cols 原生滚动交互。 */
2166
+ .pk-cols--wheel .pk-col {
2167
+ cursor: grab;
2168
+ touch-action: none;
2169
+ }
2170
+ .pk-cols--wheel.is-dragging {
2171
+ user-select: none;
2172
+ cursor: grabbing;
2173
+ }
2174
+ .pk-cols--wheel .pk-col__list {
2175
+ scrollbar-width: none;
2176
+ -ms-overflow-style: none;
2177
+ }
2178
+ .pk-cols--wheel .pk-col__list::-webkit-scrollbar {
2179
+ display: none;
2180
+ }
2181
+ /* 选择器淡入淡出(与 dialog/toast 一致) */
2182
+ .picker-fade-enter-active,
2183
+ .picker-fade-leave-active {
2184
+ transition: opacity 0.18s ease;
2185
+ }
2186
+ .picker-fade-enter,
2187
+ .picker-fade-leave-to {
2188
+ opacity: 0;
2189
+ }
2190
+
2191
+ /* ===== 远端预览二维码弹层 ===== */
2192
+ .rp-mask {
2193
+ position: fixed;
2194
+ inset: 0;
2195
+ z-index: var(--z-appmodal);
2196
+ display: flex;
2197
+ align-items: center;
2198
+ justify-content: center;
2199
+ background: rgba(8, 10, 16, 0.55);
2200
+ backdrop-filter: blur(2px);
2201
+ }
2202
+ .rp-card {
2203
+ width: 300px;
2204
+ max-width: calc(100vw - 32px);
2205
+ background: var(--bg-panel);
2206
+ border: 1px solid var(--border);
2207
+ border-radius: 14px;
2208
+ box-shadow: 0 18px 50px var(--shadow-card);
2209
+ overflow: hidden;
2210
+ color: var(--text);
2211
+ }
2212
+ .rp-head {
2213
+ display: flex;
2214
+ align-items: center;
2215
+ justify-content: space-between;
2216
+ padding: 12px 14px;
2217
+ border-bottom: 1px solid var(--border);
2218
+ }
2219
+ .rp-title {
2220
+ font-size: 14px;
2221
+ font-weight: 600;
2222
+ }
2223
+ .rp-x {
2224
+ width: 26px;
2225
+ height: 26px;
2226
+ border: none;
2227
+ border-radius: 7px;
2228
+ background: transparent;
2229
+ color: var(--text-dim);
2230
+ font-size: 20px;
2231
+ line-height: 1;
2232
+ cursor: pointer;
2233
+ transition: background 0.15s, color 0.15s;
2234
+ }
2235
+ .rp-x:hover {
2236
+ background: var(--bg-hover);
2237
+ color: var(--text);
2238
+ }
2239
+ .rp-body {
2240
+ padding: 18px 16px 16px;
2241
+ display: flex;
2242
+ flex-direction: column;
2243
+ align-items: center;
2244
+ }
2245
+ .rp-qr-wrap {
2246
+ position: relative;
2247
+ width: 220px;
2248
+ height: 220px;
2249
+ display: flex;
2250
+ align-items: center;
2251
+ justify-content: center;
2252
+ background: #fff;
2253
+ border-radius: 10px;
2254
+ padding: 8px;
2255
+ box-sizing: border-box;
2256
+ }
2257
+ .rp-qr {
2258
+ width: 204px;
2259
+ height: 204px;
2260
+ display: block;
2261
+ }
2262
+ .rp-qr--loading {
2263
+ display: flex;
2264
+ align-items: center;
2265
+ justify-content: center;
2266
+ width: 100%;
2267
+ height: 100%;
2268
+ color: var(--text-placeholder);
2269
+ font-size: 14px;
2270
+ }
2271
+ /* 发布链路(保存/取code/发布/通知)进行中:二维码模糊 + 加载遮罩 */
2272
+ .rp-qr--blurred {
2273
+ filter: blur(7px);
2274
+ opacity: 0.55;
2275
+ transition: filter 0.25s ease, opacity 0.25s ease;
2276
+ }
2277
+ .rp-qr-loading {
2278
+ position: absolute;
2279
+ inset: 0;
2280
+ display: flex;
2281
+ flex-direction: column;
2282
+ align-items: center;
2283
+ justify-content: center;
2284
+ gap: 10px;
2285
+ background: rgba(255, 255, 255, 0.5);
2286
+ border-radius: 12px;
2287
+ }
2288
+ .rp-spinner {
2289
+ width: 30px;
2290
+ height: 30px;
2291
+ border: 3px solid rgba(16, 19, 26, 0.18);
2292
+ border-top-color: var(--accent);
2293
+ border-radius: 50%;
2294
+ animation: rp-spin 0.8s linear infinite;
2295
+ }
2296
+ .rp-loading-text {
2297
+ font-size: 13px;
2298
+ color: var(--text-dim);
2299
+ }
2300
+ @keyframes rp-spin {
2301
+ to { transform: rotate(360deg); }
2302
+ }
2303
+ .rp-tip {
2304
+ margin: 12px 0 10px;
2305
+ font-size: 12.5px;
2306
+ color: var(--text-dim);
2307
+ text-align: center;
2308
+ }
2309
+ .rp-url {
2310
+ display: flex;
2311
+ align-items: center;
2312
+ gap: 8px;
2313
+ width: 100%;
2314
+ }
2315
+ .rp-url-text {
2316
+ flex: 1 1 auto;
2317
+ min-width: 0;
2318
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
2319
+ font-size: 11px;
2320
+ color: var(--text-dim);
2321
+ background: var(--bg-input);
2322
+ border: 1px solid var(--border);
2323
+ border-radius: 7px;
2324
+ padding: 6px 8px;
2325
+ overflow: hidden;
2326
+ text-overflow: ellipsis;
2327
+ white-space: nowrap;
2328
+ }
2329
+ .rp-copy {
2330
+ flex: 0 0 auto;
2331
+ padding: 6px 10px;
2332
+ font-size: 12px;
2333
+ border-radius: 7px;
2334
+ border: 1px solid var(--accent-border);
2335
+ background: var(--accent);
2336
+ color: var(--on-accent);
2337
+ cursor: pointer;
2338
+ transition: background 0.15s, opacity 0.15s;
2339
+ }
2340
+ .rp-copy:hover:not(:disabled) {
2341
+ background: var(--accent-hover);
2342
+ }
2343
+ .rp-copy:disabled {
2344
+ opacity: 0.5;
2345
+ cursor: default;
2346
+ }
2347
+
2348
+ /* ============ 纯净截图模式(?chrome=off,配合 /__screenshot 无头截图) ============ */
2349
+ /* 此时顶栏 / 设备条 / 调试面板已由 v-show 隐藏,这里仅做布局收尾:
2350
+ 把手机框在舞台内居中、去掉设备框阴影,让截出的卡片更干净。 */
2351
+ .app--chrome-off .app__stage {
2352
+ justify-content: center;
2353
+ }
2354
+ .app--chrome-off .phone-viewport {
2355
+ box-shadow: none;
2356
+ }
2357
+ /* 截图/度量基准:chrome=off 下 .phone__screen 固定为卡片设计宽度(view.width,标准 375px)、
2358
+ 去掉 16px padding——设计稿元素坐标以 0..375 为基准,渲染度量必须同基准,否则对拍
2359
+ (/__bbox + fidelity_report.mjs)的宽度基准错位(此前 width:100% 撑满视口,渲染卡宽随视口漂移)。 */
2360
+ .app--chrome-off .phone__screen {
2361
+ width: 375px;
2362
+ padding: 0;
2363
+ margin: 0 auto;
2364
+ }
2365
+ .rp-warn {
2366
+ margin: 10px 0 0;
2367
+ font-size: 11.5px;
2368
+ line-height: 1.5;
2369
+ color: var(--warn);
2370
+ text-align: center;
2371
+ }
2372
+
2373
+ /* ===== 登录凭据弹窗(远程同步缺凭据时弹出) ===== */
2374
+ .login-mask {
2375
+ position: fixed;
2376
+ inset: 0;
2377
+ z-index: var(--z-appmodal);
2378
+ display: flex;
2379
+ align-items: center;
2380
+ justify-content: center;
2381
+ background: rgba(8, 10, 16, 0.55);
2382
+ backdrop-filter: blur(2px);
2383
+ }
2384
+ .login-modal {
2385
+ width: 320px;
2386
+ max-width: calc(100vw - 32px);
2387
+ padding: 18px 18px 16px;
2388
+ background: var(--bg-panel);
2389
+ border: 1px solid var(--border);
2390
+ border-radius: 14px;
2391
+ box-shadow: 0 18px 50px var(--shadow-card);
2392
+ color: var(--text);
2393
+ box-sizing: border-box;
2394
+ }
2395
+ .login-modal__title {
2396
+ font-size: 15px;
2397
+ font-weight: 600;
2398
+ margin-bottom: 6px;
2399
+ }
2400
+ .login-modal__desc {
2401
+ font-size: 12px;
2402
+ line-height: 1.5;
2403
+ color: var(--text-dim);
2404
+ margin-bottom: 14px;
2405
+ }
2406
+ .login-modal__row {
2407
+ display: flex;
2408
+ align-items: center;
2409
+ gap: 10px;
2410
+ margin-bottom: 12px;
2411
+ }
2412
+ .login-modal__label {
2413
+ flex: 0 0 36px;
2414
+ font-size: 13px;
2415
+ color: var(--text-dim);
2416
+ }
2417
+ .login-modal__input {
2418
+ flex: 1 1 auto;
2419
+ min-width: 0;
2420
+ height: 34px;
2421
+ padding: 0 10px;
2422
+ font-size: 13px;
2423
+ color: var(--text);
2424
+ background: var(--bg-input);
2425
+ border: 1px solid var(--border);
2426
+ border-radius: 8px;
2427
+ outline: none;
2428
+ box-sizing: border-box;
2429
+ }
2430
+ .login-modal__input:focus {
2431
+ border-color: var(--accent);
2432
+ }
2433
+ .login-modal__error {
2434
+ font-size: 12px;
2435
+ color: var(--danger);
2436
+ margin-bottom: 10px;
2437
+ }
2438
+ .login-modal__actions {
2439
+ display: flex;
2440
+ justify-content: flex-end;
2441
+ gap: 10px;
2442
+ margin-top: 4px;
2443
+ }
2444
+ .login-modal__btn {
2445
+ padding: 7px 14px;
2446
+ font-size: 13px;
2447
+ border-radius: 8px;
2448
+ border: 1px solid var(--border);
2449
+ cursor: pointer;
2450
+ transition: background 0.15s, opacity 0.15s, border-color 0.15s;
2451
+ }
2452
+ .login-modal__btn--ghost {
2453
+ background: transparent;
2454
+ color: var(--text-dim);
2455
+ }
2456
+ .login-modal__btn--ghost:hover:not(:disabled) {
2457
+ background: var(--bg-hover);
2458
+ color: var(--text);
2459
+ }
2460
+ .login-modal__btn--primary {
2461
+ border-color: var(--accent-border);
2462
+ background: var(--accent);
2463
+ color: var(--on-accent);
2464
+ }
2465
+ .login-modal__btn--primary:hover:not(:disabled) {
2466
+ background: var(--accent-hover);
2467
+ }
2468
+ .login-modal__btn:disabled {
2469
+ opacity: 0.5;
2470
+ cursor: default;
2471
+ }
2472
+
2473
+ /* ===== 二次确认弹窗(版本推送 / 当前同步 / 全量同步 操作前弹出) ===== */
2474
+ .confirm-mask {
2475
+ position: fixed;
2476
+ inset: 0;
2477
+ z-index: var(--z-appmodal);
2478
+ display: flex;
2479
+ align-items: center;
2480
+ justify-content: center;
2481
+ background: rgba(8, 10, 16, 0.55);
2482
+ backdrop-filter: blur(2px);
2483
+ }
2484
+ .confirm-modal {
2485
+ width: 360px;
2486
+ max-width: calc(100vw - 32px);
2487
+ padding: 18px 18px 16px;
2488
+ background: var(--bg-panel);
2489
+ border: 1px solid var(--border);
2490
+ border-radius: 14px;
2491
+ box-shadow: 0 18px 50px var(--shadow-card);
2492
+ color: var(--text);
2493
+ box-sizing: border-box;
2494
+ }
2495
+ .confirm-modal__title {
2496
+ display: flex;
2497
+ align-items: center;
2498
+ gap: 8px;
2499
+ font-size: 15px;
2500
+ font-weight: 600;
2501
+ margin-bottom: 8px;
2502
+ }
2503
+ .confirm-modal__title.is-danger {
2504
+ color: var(--danger);
2505
+ }
2506
+ .confirm-modal__icon {
2507
+ width: 18px;
2508
+ height: 18px;
2509
+ flex: 0 0 auto;
2510
+ }
2511
+ .confirm-modal__icon :is(path, line, polyline, circle, rect) {
2512
+ vector-effect: non-scaling-stroke;
2513
+ }
2514
+ .confirm-modal__desc {
2515
+ font-size: 12.5px;
2516
+ line-height: 1.6;
2517
+ color: var(--text-dim);
2518
+ margin-bottom: 16px;
2519
+ white-space: pre-line;
2520
+ }
2521
+ .confirm-modal__actions {
2522
+ display: flex;
2523
+ justify-content: flex-end;
2524
+ gap: 10px;
2525
+ }
2526
+ .confirm-modal__btn {
2527
+ padding: 7px 16px;
2528
+ font-size: 13px;
2529
+ border-radius: 8px;
2530
+ border: 1px solid var(--border);
2531
+ cursor: pointer;
2532
+ transition: background 0.15s, opacity 0.15s, border-color 0.15s;
2533
+ }
2534
+ .confirm-modal__btn--ghost {
2535
+ background: transparent;
2536
+ color: var(--text-dim);
2537
+ }
2538
+ .confirm-modal__btn--ghost:hover {
2539
+ background: var(--bg-hover);
2540
+ color: var(--text);
2541
+ }
2542
+ .confirm-modal__btn--primary {
2543
+ border-color: var(--accent-border);
2544
+ background: var(--accent);
2545
+ color: var(--on-accent);
2546
+ }
2547
+ .confirm-modal__btn--primary:hover {
2548
+ background: var(--accent-hover);
2549
+ }
2550
+ .confirm-modal__btn--danger {
2551
+ border-color: var(--danger);
2552
+ background: var(--danger);
2553
+ color: var(--on-accent);
2554
+ }
2555
+ .confirm-modal__btn--danger:hover {
2556
+ background: var(--danger-hover);
2557
+ }
2558
+ .confirm-fade-enter-active,
2559
+ .confirm-fade-leave-active {
2560
+ transition: opacity 0.15s ease;
2561
+ }
2562
+ .confirm-fade-enter-from,
2563
+ .confirm-fade-leave-to {
2564
+ opacity: 0;
2565
+ }