cardbuddy 0.7.0 → 0.9.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 (36) hide show
  1. package/README.md +21 -0
  2. package/dist/cli.js +5 -0
  3. package/dist/preview-editor/assets/index-CrcrVs4F.css +1 -0
  4. package/dist/preview-editor/assets/index-DnpHHKWl.js +80 -0
  5. package/dist/preview-editor/index.html +2 -2
  6. package/dist/server-core/codeBridge.js +359 -0
  7. package/dist/server-core/index.js +27 -2
  8. package/dist/server-core/logStream.js +275 -0
  9. package/dist/standalone.js +33 -6
  10. package/dist/vendored/App.vue +28 -19
  11. package/dist/vendored/ansi.js +185 -0
  12. package/dist/vendored/api.js +164 -28
  13. package/dist/vendored/components/ApiCallPanel.vue +13 -1
  14. package/dist/vendored/components/ApiPayloadTree.vue +34 -1
  15. package/dist/vendored/components/CardGallery.vue +2 -2
  16. package/dist/vendored/components/ComponentPalette.vue +46 -39
  17. package/dist/vendored/components/ConfirmDialog.vue +2 -2
  18. package/dist/vendored/components/DebugPanel.vue +420 -69
  19. package/dist/vendored/components/EventEditor.vue +29 -3
  20. package/dist/vendored/components/LogValue.vue +2 -1
  21. package/dist/vendored/components/NodeInspector.vue +1 -1
  22. package/dist/vendored/components/PropControl.vue +6 -2
  23. package/dist/vendored/components/RemotePreview.vue +3 -1
  24. package/dist/vendored/components/StructureTree.vue +2 -0
  25. package/dist/vendored/components/setters/SetCode.vue +111 -3
  26. package/dist/vendored/styles/app.css +529 -96
  27. package/dist/vendored/styles/card-gallery.css +4 -11
  28. package/dist/vendored/styles/card-select.css +0 -15
  29. package/dist/vendored/styles/dropdown.css +3 -15
  30. package/dist/vendored/styles/json-tree.css +1 -1
  31. package/dist/vendored/styles/node-inspector.css +204 -131
  32. package/dist/vendored/styles/setters.css +91 -2
  33. package/dist/vendored/styles/structure-tree.css +27 -12
  34. package/package.json +1 -1
  35. package/dist/preview-editor/assets/index-BlscexDE.js +0 -79
  36. package/dist/preview-editor/assets/index-dlbSPb5W.css +0 -1
@@ -64,6 +64,9 @@
64
64
  --scrim: rgba(0, 0, 0, 0.55);
65
65
  --scrim-soft: rgba(0, 0, 0, 0.45);
66
66
  --scrim-faint: rgba(0, 0, 0, 0.4);
67
+ /* 模态模糊遮罩(深浅主题统一,不随主题翻转):确认框 / 网格预览 / 添加组件 / 远端预览 / 登录框共用 */
68
+ --scrim-modal: rgba(8, 10, 16, 0.55);
69
+ --scrim-modal-blur: 2px;
67
70
  /* 顶栏渐变、分隔条、分隔条抓手微光、白色叠加微光 */
68
71
  --bar-grad: linear-gradient(180deg, #171a20, #131519);
69
72
  --splitter: #5b6cff;
@@ -85,6 +88,8 @@
85
88
  --log-warn-border: rgba(224, 179, 65, 0.35);
86
89
  --scroll-thumb: rgba(148, 154, 171, 0.28);
87
90
  --scroll-thumb-hover: rgba(148, 154, 171, 0.45);
91
+ /* 静止档:鼠标不在滚动区域时弱化显示 */
92
+ --scroll-thumb-idle: rgba(148, 154, 171, 0.14);
88
93
  /* 语法高亮配色(JSON 树 / 结构树 / 代码编辑器),随主题切换;暗色默认=原硬编码值,保证暗色外观不变 */
89
94
  --jt-key: #9db4ff;
90
95
  --jt-string: #8fe39a;
@@ -239,6 +244,7 @@
239
244
  --log-warn-border: rgba(181, 129, 5, 0.3);
240
245
  --scroll-thumb: rgba(0, 0, 0, 0.2);
241
246
  --scroll-thumb-hover: rgba(0, 0, 0, 0.32);
247
+ --scroll-thumb-idle: rgba(0, 0, 0, 0.1);
242
248
  /* 语法高亮配色:浅色主题参考 GitHub Light / VSCode Light+,保证白底上的可读性 */
243
249
  --jt-key: #953800;
244
250
  --jt-string: #0a3069;
@@ -301,23 +307,47 @@ body {
301
307
  height: 100vh;
302
308
  }
303
309
 
304
- /* 全局滚动条(Firefox) */
305
- * {
306
- scrollbar-width: thin;
307
- scrollbar-color: var(--scroll-thumb) transparent;
310
+ /* 全局滚动条(仅 Firefox):FF 不识别 ::-webkit-scrollbar,也不支持悬停分档/过渡,取中间档常显。
311
+ 必须隔离在 FF:Chrome 121+ 一旦命中 scrollbar-width 会改走标准滚动条,
312
+ 忽略 ::-webkit-scrollbar 定制(含下方三档透明度与过渡)。 */
313
+ @supports (-moz-appearance: none) {
314
+ * {
315
+ scrollbar-width: thin;
316
+ scrollbar-color: var(--scroll-thumb) transparent;
317
+ }
308
318
  }
309
- /* 全局滚动条(WebKit) */
319
+ /* 全局滚动条(WebKit)三档透明度:
320
+ 静止(鼠标不在滚动区域)弱化 → 鼠标进入区域回常 → 按住 thumb 再提亮;
321
+ background-color 过渡保证移入/移出平滑淡入淡出。页面所有滚动区域继承本规则。
322
+ 注:区域档必须用 :has(:hover)(容器内有被悬停后代)而非容器自身 :hover ——
323
+ Chromium 不会因滚动容器自身 hover 状态变化重算其 ::-webkit-scrollbar-* 样式(实测无效),
324
+ :has(:hover) 与祖先 hover 均可正常触发重绘。 */
310
325
  *::-webkit-scrollbar {
311
326
  width: 8px;
312
327
  height: 8px;
313
328
  }
314
329
  *::-webkit-scrollbar-thumb {
315
- background: var(--scroll-thumb);
330
+ background: var(--scroll-thumb-idle);
316
331
  border-radius: 8px;
332
+ transition: background-color 0.2s ease;
333
+ }
334
+ *:has(:hover)::-webkit-scrollbar-thumb {
335
+ background: var(--scroll-thumb);
336
+ }
337
+ /* 纯文本滚动区兜底:CodeJar 编辑器与原生对话框正文以文本节点为主,容器内几乎没有可命中的
338
+ 悬停后代,仅靠 :has(:hover) 会随鼠标落在「文字 / 缩进空白」上而闪烁,故改用紧贴外层容器的 hover。 */
339
+ .st-expr:hover .st-expr__editor::-webkit-scrollbar-thumb,
340
+ .st-code:hover .st-code__editor::-webkit-scrollbar-thumb,
341
+ .debug__scope-raw:hover .debug__raw-editor::-webkit-scrollbar-thumb,
342
+ .dlg:hover .dlg__msg::-webkit-scrollbar-thumb {
343
+ background: var(--scroll-thumb);
317
344
  }
318
345
  *::-webkit-scrollbar-thumb:hover {
319
346
  background: var(--scroll-thumb-hover);
320
347
  }
348
+ *::-webkit-scrollbar-thumb:active {
349
+ background: var(--scroll-thumb-hover);
350
+ }
321
351
  *::-webkit-scrollbar-track {
322
352
  background: transparent;
323
353
  }
@@ -878,14 +908,24 @@ body {
878
908
  min-height: 0;
879
909
  }
880
910
  .workside__bar {
911
+ flex: 0 0 auto;
912
+ order: 2;
913
+ }
914
+ /* 底部弹出面板容器(运行态 / 事件 / 变量绑定):Teleport 目标,宽度贯穿中右两栏;
915
+ 空容器(面板均 v-show 隐藏时)高度为 0,不占位 */
916
+ .workside__panels {
881
917
  flex: 0 0 auto;
882
918
  order: 1;
919
+ display: flex;
920
+ flex-direction: column;
883
921
  }
884
922
  .workside__cols {
885
923
  flex: 1 1 auto;
886
924
  display: flex;
887
925
  min-width: 0;
888
926
  min-height: 0;
927
+ /* 绝对定位 splitter 的定位上下文:分隔条只贯穿两栏区高度,不穿过底部面板 / 状态栏 */
928
+ position: relative;
889
929
  }
890
930
 
891
931
  .phone {
@@ -920,12 +960,13 @@ body {
920
960
  flex-direction: column;
921
961
  }
922
962
 
923
- /* 拖拽分隔条:覆盖在预览列与调试面板的接缝上,绝对定位不占布局宽度 */
963
+ /* 拖拽分隔条:覆盖在接缝上,绝对定位不占布局宽度(命中宽 6px → 两侧各压 3px)。
964
+ 左条定位上下文为 .app__stage:预览列 | 工作区 的缝贯穿底面板 / 状态栏左缘,通到底部全程可拖;
965
+ 右条(.splitter--api)定位上下文为 .workside__cols:该缝在底面板区域不存在,只贯穿列区高度。 */
924
966
  .splitter {
925
967
  position: absolute;
926
968
  top: 0;
927
969
  bottom: 0;
928
- /* 以预览列右缘为接缝居中覆盖(宽 6px → 两侧各压 3px),不挤占实际宽度 */
929
970
  left: calc(var(--preview-width) - 3px);
930
971
  width: 6px;
931
972
  cursor: col-resize;
@@ -937,9 +978,22 @@ body {
937
978
  left: auto;
938
979
  right: calc(var(--api-panel-width, 360px) - 3px);
939
980
  }
940
- .splitter:hover,
941
- .splitter.is-dragging {
981
+ /* 拖拽高亮统一规格:左 / 右竖向分隔条与底部面板头部横向拖拽条,hover / 拖拽中一律
982
+ 显示 2px 的 --splitter 高亮线(同色同粗细);竖条命中区仍为 6px,高亮只画居中 2px */
983
+ .splitter::after {
984
+ content: '';
985
+ position: absolute;
986
+ top: 0;
987
+ bottom: 0;
988
+ left: 1.5px;
989
+ width: 3px;
942
990
  background: var(--splitter);
991
+ opacity: 0;
992
+ transition: opacity 0.15s ease;
993
+ }
994
+ .splitter:hover::after,
995
+ .splitter.is-dragging::after {
996
+ opacity: 1;
943
997
  }
944
998
  /* 拖拽中:禁用文本选择,全局保持 col-resize 光标 */
945
999
  .phone--dragging,
@@ -999,7 +1053,9 @@ body {
999
1053
  font-size: 12px;
1000
1054
  white-space: nowrap;
1001
1055
  }
1002
- /* 预览区底部状态条:与顶部 device-bar 同款风格,固定于画布底部展示画布宽高 */
1056
+ /* 预览区底部状态条:画布尺寸读数 + 刷新按钮。
1057
+ 底色透明(融入手机框 --bg-stage),不再作为独立暗面板——消除其与全局 .statusbar
1058
+ (bg-bar)因所处父容器底色不同而产生的“颜色细微差异”观感;仅保留 1px 分隔线作页脚边界。 */
1003
1059
  .preview-statusbar {
1004
1060
  flex: 0 0 auto;
1005
1061
  width: 100%;
@@ -1008,7 +1064,7 @@ body {
1008
1064
  align-items: center;
1009
1065
  gap: 12px;
1010
1066
  padding: 0 12px;
1011
- background: var(--bg-bar);
1067
+ background: transparent;
1012
1068
  border-top: 1px solid var(--border);
1013
1069
  }
1014
1070
  /* 预览区底部状态条:本地开发服务连接状态(连接图标 + 状态文案)。
@@ -1035,6 +1091,20 @@ body {
1035
1091
  .statusbar .app__btn--reconnecting { color: var(--warn); }
1036
1092
  .statusbar .app__btn--disconnected { color: var(--danger); }
1037
1093
  .statusbar .app__btn--warn { color: var(--warn); }
1094
+ /* 连接指示器现为可点按钮(开合底部「服务日志」面板):保持状态栏读数观感
1095
+ (无边框 / 无圆角 / 语义色由 app__btn--* 提供),hover 仅加极淡底色提示可点,
1096
+ 展开态用 accent 底线标识(与 .statusbar__item--prob.is-open 同一语言,不抢语义色)。 */
1097
+ button.statusbar__conn {
1098
+ cursor: pointer;
1099
+ font: inherit;
1100
+ }
1101
+ .statusbar__conn:hover {
1102
+ background: var(--bg-hover);
1103
+ }
1104
+ .statusbar__conn.is-open {
1105
+ background: var(--bg-hover);
1106
+ box-shadow: inset 0 -2px 0 0 var(--accent);
1107
+ }
1038
1108
 
1039
1109
  /* 颜色选择器(@ckpack/vue-color Sketch)预设区顶部分隔线:与系统边框色保持一致,
1040
1110
  覆盖组件默认的 1px solid #eee(深色主题下刺眼)。组件样式为全局注入且非 scoped,
@@ -1200,6 +1270,40 @@ body {
1200
1270
  overflow: auto;
1201
1271
  padding: 8px 12px;
1202
1272
  }
1273
+ /* 通用空状态占位(API 调用面板 / 卡片来源 tab 复用):徽章 + 图标 + 文案 + 弱提示,垂直居中 */
1274
+ .empty-state {
1275
+ display: flex;
1276
+ flex-direction: column;
1277
+ align-items: center;
1278
+ justify-content: center;
1279
+ gap: 12px;
1280
+ padding: 56px 16px;
1281
+ color: var(--text-faint);
1282
+ }
1283
+ /* 虚线圆形徽章:复用全局弱态配色(bg-hover 底 + 虚线描边),内嵌 20px 线性图标(插头/云等,随场景) */
1284
+ .empty-state__badge {
1285
+ display: inline-flex;
1286
+ align-items: center;
1287
+ justify-content: center;
1288
+ width: 48px;
1289
+ height: 48px;
1290
+ border-radius: 50%;
1291
+ background: var(--bg-hover);
1292
+ border: 1px dashed var(--border-strong);
1293
+ color: var(--text-faint);
1294
+ }
1295
+ .empty-state__badge svg {
1296
+ width: 20px;
1297
+ height: 20px;
1298
+ }
1299
+ .empty-state__hint {
1300
+ margin: 0;
1301
+ max-width: 240px;
1302
+ color: var(--text-faint);
1303
+ font-size: 11px;
1304
+ line-height: 1.6;
1305
+ text-align: center;
1306
+ }
1203
1307
  .debug__tabs {
1204
1308
  display: flex;
1205
1309
  border-bottom: 1px solid var(--border);
@@ -1252,7 +1356,10 @@ body {
1252
1356
  }
1253
1357
  .debug__meta {
1254
1358
  margin: 0;
1255
- padding: 8px 12px;
1359
+ padding: 8px 12px 0; /* 底部留白交给 hint 页脚,保证贴底无缝 */
1360
+ display: flex;
1361
+ flex-direction: column;
1362
+ min-height: 100%; /* 内容不足一屏时也能把 hint 推到面板底部 */
1256
1363
  }
1257
1364
  .debug__empty {
1258
1365
  color: var(--text-faint);
@@ -1298,8 +1405,12 @@ body {
1298
1405
  font-size: 12px;
1299
1406
  }
1300
1407
  .debug__meta-hint {
1301
- margin-top: 12px;
1302
- padding-top: 10px;
1408
+ /* 固定在面板底部:内容短时由 margin-top:auto 推底;内容超长滚动时 sticky 吸底不随列表滚走 */
1409
+ position: sticky;
1410
+ bottom: 0;
1411
+ margin: auto -12px 0;
1412
+ padding: 10px 12px;
1413
+ background: var(--bg-panel);
1303
1414
  border-top: 1px solid var(--border);
1304
1415
  color: var(--text-faint);
1305
1416
  font-size: 12px;
@@ -1312,14 +1423,14 @@ body {
1312
1423
  flex: 0 0 auto;
1313
1424
  display: flex;
1314
1425
  flex-direction: column;
1426
+ /* 顶缘拖拽条 .panelhead__edge 的定位上下文 */
1427
+ position: relative;
1315
1428
  /* 初始高度 240px;运行时由头部拖拽调整,并通过 JS 持久化回写 height */
1316
1429
  height: 240px;
1317
1430
  min-height: 120px;
1318
1431
  max-height: 60vh;
1319
1432
  /* hidden 避免面板自身出现滚动条(滚动交给 .statepanel__tree) */
1320
1433
  overflow: hidden;
1321
- /* 通过 flex order 把面板固定到状态栏之上,确保状态栏始终贴底(不被面板顶上来) */
1322
- order: 1;
1323
1434
  background: var(--bg-panel);
1324
1435
  border-top: 1px solid var(--border-strong);
1325
1436
  }
@@ -1330,13 +1441,37 @@ body {
1330
1441
  gap: 8px;
1331
1442
  height: var(--ctrl-lg);
1332
1443
  padding: 0 12px;
1333
- background: var(--bg-bar);
1334
1444
  border-bottom: 1px solid var(--border);
1335
1445
  font-size: 13px;
1336
1446
  color: var(--text);
1337
- /* 头部即拖拽手柄(无专门节点):整条可抓取,向上拖放大面板 */
1447
+ }
1448
+ /* 底面板顶缘拖拽条:与竖向分隔条同一规格——命中 6px、高亮 3px --splitter;
1449
+ 仅鼠标进入分割边缘才触发高亮 / 拖拽,头部本体不再响应(面板 overflow:hidden,
1450
+ 高亮线贴缝内侧 0~3px 绘制)。拖拽中经 is-dragging 保持高亮。 */
1451
+ .panelhead__edge {
1452
+ position: absolute;
1453
+ top: 0;
1454
+ left: 0;
1455
+ right: 0;
1456
+ height: 6px;
1338
1457
  cursor: row-resize;
1339
1458
  user-select: none;
1459
+ z-index: var(--z-sticky);
1460
+ }
1461
+ .panelhead__edge::after {
1462
+ content: '';
1463
+ position: absolute;
1464
+ left: 0;
1465
+ right: 0;
1466
+ top: 0;
1467
+ height: 3px;
1468
+ background: var(--splitter);
1469
+ opacity: 0;
1470
+ transition: opacity 0.15s ease;
1471
+ }
1472
+ .panelhead__edge:hover::after,
1473
+ .panelhead__edge.is-dragging::after {
1474
+ opacity: 1;
1340
1475
  }
1341
1476
  .statepanel__title {
1342
1477
  font-weight: 600;
@@ -1345,27 +1480,34 @@ body {
1345
1480
  .statepanel__title code {
1346
1481
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
1347
1482
  font-size: 12px;
1348
- color: var(--accent);
1483
+ /* 与 .debug__meta-row code 同一芯片语言:浅底 + 弱文字色,不用 accent 抢焦 */
1484
+ color: var(--text-dim);
1485
+ background: var(--bg-hover);
1486
+ padding: 1px 5px;
1487
+ border-radius: 4px;
1349
1488
  }
1350
1489
  .statepanel__close {
1351
1490
  margin-left: auto;
1352
1491
  display: inline-flex;
1353
1492
  align-items: center;
1354
1493
  justify-content: center;
1355
- width: 18px;
1356
- height: 18px;
1494
+ width: 24px;
1495
+ height: 24px;
1357
1496
  padding: 0;
1358
1497
  background: none;
1359
1498
  border: none;
1360
- color: var(--text-faint);
1361
- font-size: 15px;
1362
- line-height: 1;
1499
+ color: var(--text-dim);
1363
1500
  cursor: pointer;
1364
- transition: color 0.15s;
1501
+ border-radius: var(--radius-xs);
1502
+ transition: background 0.15s, color 0.15s;
1365
1503
  }
1366
1504
  .statepanel__close:hover {
1505
+ background: var(--bg-hover);
1367
1506
  color: var(--text);
1368
1507
  }
1508
+ .statepanel__close svg {
1509
+ display: block;
1510
+ }
1369
1511
  .statepanel__tree {
1370
1512
  flex: 1 1 auto;
1371
1513
  overflow: auto;
@@ -1402,21 +1544,66 @@ body {
1402
1544
  padding: 0;
1403
1545
  }
1404
1546
  .debug__item {
1405
- padding: 4px 8px;
1547
+ padding: 9px 11px;
1406
1548
  margin-bottom: 6px;
1407
1549
  border-radius: 8px;
1408
1550
  font-size: 12px;
1409
1551
  line-height: 1.6;
1410
- word-break: break-all;
1552
+ /* break-all 会把键名/标识符拦腰拆行(msg → ms/g);anywhere 仅在单词真放不下时才断 */
1553
+ word-break: normal;
1554
+ overflow-wrap: anywhere;
1411
1555
  }
1412
1556
  .debug__item--err {
1413
- background: var(--danger-soft);
1557
+ display: grid;
1558
+ grid-template-columns: 52px 84px minmax(0, 1fr);
1559
+ align-items: baseline;
1560
+ gap: 8px;
1561
+ margin-bottom: 0;
1562
+ padding: 3px 12px 3px 10px;
1563
+ border-radius: 0;
1564
+ /* 左侧红条标示错误归属;行底透明避免整片红块,hover 才整行染 danger 色 */
1565
+ border-left: 2px solid var(--danger);
1566
+ border-bottom: 1px solid var(--border);
1567
+ background: transparent;
1414
1568
  color: var(--err-item-fg);
1569
+ font-size: 11px;
1570
+ line-height: 1.45;
1571
+ word-break: normal;
1572
+ }
1573
+ .debug__item--err:last-child {
1574
+ border-bottom: none;
1575
+ }
1576
+ .debug__item--err:hover {
1577
+ background: var(--danger-soft);
1578
+ }
1579
+ /* 错误类型列:等宽小标签,超长省略 */
1580
+ .debug__item--err .debug__kind {
1581
+ overflow: hidden;
1582
+ font-family: 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace;
1583
+ font-size: 10px;
1584
+ color: var(--danger);
1585
+ text-overflow: ellipsis;
1586
+ white-space: nowrap;
1587
+ opacity: 1;
1588
+ }
1589
+ .err__msg {
1590
+ min-width: 0;
1591
+ word-break: break-word;
1415
1592
  }
1416
1593
  .debug__item--api {
1417
1594
  border: 1px solid var(--api-item-border);
1418
1595
  color: var(--api-item-fg);
1419
1596
  background: var(--api-item-bg);
1597
+ margin-bottom: 8px;
1598
+ }
1599
+ .debug__item--api:last-child {
1600
+ margin-bottom: 0;
1601
+ }
1602
+ /* 方法名与下方代码树同语言:等宽 + 显式行高(规范手册 6.2) */
1603
+ .debug__item--api .debug__kind {
1604
+ font-family: 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace;
1605
+ font-size: 12px;
1606
+ line-height: 1.6;
1420
1607
  }
1421
1608
  .debug__kind {
1422
1609
  font-weight: 600;
@@ -1481,12 +1668,27 @@ body {
1481
1668
  .apt__branch,
1482
1669
  .apt__leaf {
1483
1670
  display: flex;
1484
- align-items: center;
1671
+ /* 值折行时键名/冒号保持首行对齐,center 会让它们悬在多行值的垂直中部 */
1672
+ align-items: baseline;
1485
1673
  gap: 4px;
1486
1674
  padding: 1px 4px;
1487
1675
  border-radius: 4px;
1488
1676
  cursor: pointer;
1489
1677
  }
1678
+ /* 冒号贴键(DevTools 风格):抵消与键之间的 flex gap,值仍保留 4px 间隔 */
1679
+ .apt .jt__colon {
1680
+ margin-left: -4px;
1681
+ }
1682
+ /* 键名不参与 flex 收缩:否则长值会把小 basis 的键列按比例挤到拦腰换行(msg → ms/g);
1683
+ 折行只应发生在值上(值已 min-width:0 + overflow-wrap:anywhere) */
1684
+ .apt .jt__key {
1685
+ flex-shrink: 0;
1686
+ }
1687
+ /* 值/摘要允许在剩余宽度内折行:解除 flex 项 min-content 下限 */
1688
+ .apt .jt__val,
1689
+ .apt .jt__preview {
1690
+ min-width: 0;
1691
+ }
1490
1692
  .apt__branch:hover,
1491
1693
  .apt__leaf:hover {
1492
1694
  background: var(--bg-hover);
@@ -1545,36 +1747,68 @@ body {
1545
1747
  overflow: auto;
1546
1748
  }
1547
1749
  .debug__scope-foot {
1548
- flex: 0 0 auto;
1549
1750
  position: absolute;
1550
- bottom: 0;
1551
- right: 0;
1751
+ bottom: 10px;
1752
+ right: 10px;
1552
1753
  display: flex;
1553
1754
  justify-content: flex-end;
1554
1755
  align-items: center;
1555
1756
  gap: 8px;
1556
- padding: 8px 12px;
1757
+ /* 悬浮操作条:卡片容器 + 投影,与滚动内容分层,不再“贴”在树内容上 */
1758
+ padding: 6px;
1759
+ background: var(--bg-bar);
1760
+ border: 1px solid var(--border);
1761
+ border-radius: 10px;
1762
+ box-shadow: 0 6px 18px rgba(0, 0, 0, 0.28);
1557
1763
  }
1558
1764
  .debug__copied {
1765
+ margin: 0 4px 0 8px;
1559
1766
  font-size: 12px;
1560
1767
  color: var(--accent);
1561
1768
  }
1769
+ /* 次级按钮:中性描边 + 弱文本色,hover 提亮 */
1562
1770
  .debug__copy {
1563
1771
  display: inline-flex;
1564
1772
  align-items: center;
1565
- height: var(--ctrl-sm);
1566
- padding: 0 14px;
1773
+ gap: 5px;
1774
+ height: 26px;
1775
+ padding: 0 10px;
1567
1776
  background: var(--bg-input);
1568
- color: var(--text);
1569
- border: 1px solid var(--accent-border);
1570
- border-radius: var(--radius-sm);
1571
- font-size: 13px;
1777
+ color: var(--text-dim);
1778
+ border: 1px solid var(--border-strong);
1779
+ border-radius: 7px;
1780
+ font-size: 12px;
1572
1781
  cursor: pointer;
1573
- transition: background 0.15s, border-color 0.15s;
1782
+ transition: background 0.15s, border-color 0.15s, color 0.15s;
1574
1783
  }
1575
1784
  .debug__copy:hover {
1576
1785
  background: var(--bg-hover);
1577
- border-color: var(--accent);
1786
+ border-color: var(--accent-border);
1787
+ color: var(--text);
1788
+ }
1789
+ .debug__copy-icn {
1790
+ flex: 0 0 auto;
1791
+ width: 13px;
1792
+ height: 13px;
1793
+ }
1794
+ /* 主按钮(应用):常态与其他边线按钮一致,hover 转绿色系(ok)提示“提交/生效”语义 */
1795
+ .debug__copy--primary:hover {
1796
+ background: var(--ok-soft);
1797
+ border-color: var(--ok-border);
1798
+ color: var(--ok);
1799
+ }
1800
+ /* 丢弃类次级按钮:hover 转 warn 色提示破坏性(重置丢弃内存编辑) */
1801
+ .debug__copy--warn:hover {
1802
+ background: var(--warn-soft);
1803
+ border-color: var(--warn-border);
1804
+ color: var(--warn);
1805
+ }
1806
+ /* 按钮分组间的竖向分隔线 */
1807
+ .debug__sep {
1808
+ width: 1px;
1809
+ height: 16px;
1810
+ margin: 0 2px;
1811
+ background: var(--border-strong);
1578
1812
  }
1579
1813
 
1580
1814
  /* 数据绑定 Raw 模式:直接复用 .debug__scope-wrap 的绝对定位盒(inset 已占满 body 内容区),
@@ -1711,18 +1945,18 @@ body {
1711
1945
  color: var(--warn);
1712
1946
  }
1713
1947
 
1714
- /* 底部面板(事件面板 / 绑定错误面板):点击状态栏指示器弹出 / 收起。与运行态面板互斥,
1948
+ /* 底部面板(事件 / 变量绑定):点击状态栏指示器弹出 / 收起。与运行态面板互斥,
1715
1949
  三者共用面板高度,由头部拖拽调整(无专门拖拽节点,与预览区宽度调节同一机制),约束 [120px, 60vh]。 */
1716
1950
  .errpanel {
1717
1951
  flex: 0 0 auto;
1718
1952
  display: flex;
1719
1953
  flex-direction: column;
1954
+ /* 顶缘拖拽条 .panelhead__edge 的定位上下文 */
1955
+ position: relative;
1720
1956
  height: 240px;
1721
1957
  min-height: 120px;
1722
1958
  max-height: 60vh;
1723
1959
  overflow: hidden;
1724
- /* 与运行态面板同属「状态栏之上的底部面板」,固定 order 确保状态栏始终贴底 */
1725
- order: 2;
1726
1960
  background: var(--bg-panel);
1727
1961
  border-top: 1px solid var(--border-strong);
1728
1962
  }
@@ -1733,10 +1967,6 @@ body {
1733
1967
  gap: 8px;
1734
1968
  height: var(--ctrl-lg);
1735
1969
  padding: 0 12px;
1736
- background: var(--bg-bar);
1737
- /* 头部即拖拽手柄(无专门节点):整条可抓取,向上拖放大面板 */
1738
- cursor: row-resize;
1739
- user-select: none;
1740
1970
  border-bottom: 1px solid var(--border);
1741
1971
  font-size: 13px;
1742
1972
  color: var(--text);
@@ -1757,18 +1987,23 @@ body {
1757
1987
  display: inline-flex;
1758
1988
  align-items: center;
1759
1989
  justify-content: center;
1760
- width: 20px;
1761
- height: 20px;
1990
+ width: 24px;
1991
+ height: 24px;
1762
1992
  padding: 0;
1763
1993
  background: none;
1764
1994
  border: none;
1765
- color: var(--text-faint);
1995
+ color: var(--text-dim);
1766
1996
  cursor: pointer;
1767
- transition: color 0.15s;
1997
+ border-radius: var(--radius-xs);
1998
+ transition: background 0.15s, color 0.15s;
1768
1999
  }
1769
2000
  .errpanel__iconbtn:hover:not(:disabled) {
2001
+ background: var(--bg-hover);
1770
2002
  color: var(--text);
1771
2003
  }
2004
+ .errpanel__iconbtn svg {
2005
+ display: block;
2006
+ }
1772
2007
  .errpanel__iconbtn:disabled {
1773
2008
  opacity: 0.35;
1774
2009
  cursor: not-allowed;
@@ -1777,33 +2012,172 @@ body {
1777
2012
  .errpanel__clear:hover:not(:disabled) {
1778
2013
  color: var(--danger);
1779
2014
  }
2015
+ /* 事件面板 tab 条:日志 / 错误 二选一,并入头部单行(标题之后、操作按钮之前)。
2016
+ 沿用 .debug__tabs 视觉语言(选中项 accent 短下划线,落到头部底发丝线上),
2017
+ 与头部同高,不再单独占一行面板高度。 */
2018
+ .errpanel__tabs {
2019
+ flex: 0 0 auto;
2020
+ align-self: stretch;
2021
+ display: flex;
2022
+ align-items: stretch;
2023
+ gap: 2px;
2024
+ margin-left: 4px;
2025
+ }
2026
+ .errpanel__tab {
2027
+ position: relative;
2028
+ display: inline-flex;
2029
+ align-items: center;
2030
+ gap: 5px;
2031
+ height: 100%;
2032
+ padding: 0 10px;
2033
+ background: none;
2034
+ border: none;
2035
+ color: var(--text-dim);
2036
+ font-size: 12px;
2037
+ cursor: pointer;
2038
+ transition: color 0.15s;
2039
+ }
2040
+ .errpanel__tab:hover {
2041
+ color: var(--text);
2042
+ }
2043
+ .errpanel__tab--active {
2044
+ color: var(--accent);
2045
+ }
2046
+ .errpanel__tab--active::after {
2047
+ content: '';
2048
+ position: absolute;
2049
+ left: 20%;
2050
+ right: 20%;
2051
+ bottom: -1px;
2052
+ height: 2px;
2053
+ background: var(--accent);
2054
+ }
2055
+ /* tab 计数小药丸:错误侧计数非 0 时转红,不切 tab 也能看出那边有货 */
2056
+ .errpanel__tab-count {
2057
+ min-width: 16px;
2058
+ padding: 0 5px;
2059
+ border-radius: 999px;
2060
+ background: var(--bg-hover);
2061
+ color: var(--text-faint);
2062
+ font-size: 11px;
2063
+ line-height: 15px;
2064
+ text-align: center;
2065
+ }
2066
+ .errpanel__tab--active .errpanel__tab-count {
2067
+ background: var(--accent-soft);
2068
+ color: var(--accent);
2069
+ }
2070
+ .errpanel__tab-count.is-hit,
2071
+ .errpanel__tab--active .errpanel__tab-count.is-hit {
2072
+ background: var(--danger-soft);
2073
+ color: var(--danger);
2074
+ }
1780
2075
  .errpanel__body {
1781
2076
  flex: 1 1 auto;
1782
2077
  overflow: auto;
1783
- padding: 12px 16px;
2078
+ /* 行贴边排布(行自带左右内边距),把竖向空间全部留给内容 */
2079
+ padding: 4px 0 6px;
1784
2080
  }
1785
- /* 事件面板内「日志 / 错误」分区标题 */
1786
- .ep__sec-title {
1787
- margin: 8px 0 6px;
1788
- padding-bottom: 4px;
1789
- font-size: 12px;
1790
- font-weight: 600;
1791
- color: var(--text-dim);
1792
- border-bottom: 1px dashed var(--border);
2081
+ /* 空态占位(复用 API 调用面板 .empty-state 语言):底部面板高度有限,
2082
+ 压缩留白与徽章尺寸,避免空态自身撑出滚动条 */
2083
+ .errpanel__body .empty-state {
2084
+ padding: 20px 16px;
2085
+ gap: 8px;
1793
2086
  }
1794
- .ep__sec-title:first-child {
1795
- margin-top: 0;
2087
+ .errpanel__body .empty-state__badge {
2088
+ width: 40px;
2089
+ height: 40px;
2090
+ }
2091
+ .errpanel__body .empty-state__badge svg {
2092
+ width: 18px;
2093
+ height: 18px;
2094
+ }
2095
+ .errpanel__body .empty-state__hint {
2096
+ max-width: 460px;
2097
+ }
2098
+
2099
+ /* ——— 服务日志面板(底部第四面板):本地服务 cardbuddy 终端输出的实时镜像 ———
2100
+ 服务端按行镜像 stdout/stderr 并保留原始 ANSI,前端 preview/ansi.js 转成内联 style,
2101
+ 因此这里固定深色终端底 + 等宽字体:chalk 的等级色取自深色设计令牌,浅色主题下同样可读。
2102
+ 仅在有日志时切深底(见 .errpanel__body--term 绑定),空态仍走主题底色。 */
2103
+ .errpanel__body--term {
2104
+ padding: 0;
2105
+ background: #14161c;
2106
+ color: #e8eaf0;
2107
+ }
2108
+ .svclog {
2109
+ padding: 6px 0 10px;
2110
+ font-family: 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace;
2111
+ font-size: 11.5px;
2112
+ line-height: 1.55;
2113
+ }
2114
+ .svclog__line {
2115
+ display: flex;
2116
+ min-height: 1.55em;
2117
+ padding: 0 12px;
2118
+ /* 长行折行而非横向滚动:底部面板高度有限,横向滚动会同时吃掉两向空间 */
2119
+ white-space: pre-wrap;
2120
+ word-break: break-word;
2121
+ border-left: 2px solid transparent;
2122
+ }
2123
+ .svclog__line:hover {
2124
+ background: rgba(255, 255, 255, 0.045);
2125
+ }
2126
+ /* 级别只作左侧色条 + 极淡底:ANSI 本身已带终端配色,不叠加二次着色以免打架 */
2127
+ .svclog__line--ok {
2128
+ border-left-color: rgba(52, 211, 153, 0.55);
2129
+ }
2130
+ .svclog__line--warn {
2131
+ border-left-color: var(--warn);
2132
+ background: rgba(251, 191, 36, 0.07);
2133
+ }
2134
+ .svclog__line--error {
2135
+ border-left-color: var(--danger);
2136
+ background: rgba(248, 113, 113, 0.08);
1796
2137
  }
1797
- /* 事件面板日志:每行 = 等级标签 + 内容(语法高亮 / JSON 折叠) */
2138
+ .svclog__text {
2139
+ min-width: 0;
2140
+ }
2141
+ /* 头部读数:行数 + 实时态小圆点,落在标题之后(与事件面板 tab 同一位置语言) */
2142
+ .svclog__meta {
2143
+ display: inline-flex;
2144
+ align-items: center;
2145
+ gap: 5px;
2146
+ margin-left: 8px;
2147
+ color: var(--text-faint);
2148
+ font-size: 11px;
2149
+ white-space: nowrap;
2150
+ }
2151
+ .svclog__dot {
2152
+ width: 6px;
2153
+ height: 6px;
2154
+ margin-left: 4px;
2155
+ border-radius: 50%;
2156
+ background: var(--text-faint);
2157
+ }
2158
+ .svclog__dot.is-live {
2159
+ background: var(--ok);
2160
+ box-shadow: 0 0 0 2px var(--ok-soft);
2161
+ }
2162
+ /* 跟随最新输出按钮:开启态 accent,暂停态弱化(与其它头部图标按钮同一规格) */
2163
+ .svclog__follow.is-on {
2164
+ color: var(--accent);
2165
+ }
2166
+ /* 复制完成瞬时反馈:与「已复制」语义一致的成功绿 */
2167
+ .errpanel__iconbtn.is-done {
2168
+ color: var(--ok);
2169
+ }
2170
+ /* 事件面板日志:控制台风格高密度行 = 时间 | 等级 | 内容,发丝线分隔 */
1798
2171
  .debug__list--log {
1799
2172
  display: flex;
1800
2173
  flex-direction: column;
1801
2174
  }
1802
2175
  .log__item {
1803
- display: flex;
1804
- align-items: flex-start;
1805
- gap: 10px;
1806
- padding: 6px 8px;
2176
+ display: grid;
2177
+ grid-template-columns: 52px 44px minmax(0, 1fr);
2178
+ align-items: baseline;
2179
+ gap: 8px;
2180
+ padding: 2px 12px;
1807
2181
  border-bottom: 1px solid var(--border);
1808
2182
  }
1809
2183
  .log__item:last-child {
@@ -1812,31 +2186,40 @@ body {
1812
2186
  .log__item:hover {
1813
2187
  background: var(--bg-input);
1814
2188
  }
1815
- /* 等级标签:小药丸,按级别着色 */
2189
+ /* 行首时间戳:tabular-nums 保证整列对齐 */
2190
+ .log__time,
2191
+ .err__time {
2192
+ font-family: 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace;
2193
+ font-size: 10px;
2194
+ font-variant-numeric: tabular-nums;
2195
+ color: var(--text-faint);
2196
+ }
2197
+ /* 等级标签:无边框小药丸,定宽成列对齐 */
1816
2198
  .log__tag {
1817
- flex: 0 0 auto;
1818
- margin-top: 1px;
1819
- padding: 1px 6px;
1820
- border-radius: 4px;
2199
+ justify-self: start;
2200
+ padding: 0 5px;
2201
+ border: none;
2202
+ border-radius: 3px;
1821
2203
  font-family: 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace;
1822
- font-size: 11px;
2204
+ font-size: 10px;
1823
2205
  font-weight: 600;
1824
- line-height: 1.5;
2206
+ line-height: 14px;
1825
2207
  text-transform: uppercase;
1826
- letter-spacing: 0.4px;
2208
+ letter-spacing: 0.3px;
1827
2209
  color: var(--text-dim);
1828
- background: var(--bg-input);
1829
- border: 1px solid var(--border);
2210
+ background: var(--bg-hover);
2211
+ }
2212
+ .log__tag--info {
2213
+ color: var(--accent);
2214
+ background: var(--accent-soft);
1830
2215
  }
1831
2216
  .log__tag--warn {
1832
2217
  color: var(--log-warn-fg);
1833
2218
  background: var(--log-warn-bg);
1834
- border-color: var(--log-warn-border);
1835
2219
  }
1836
2220
  .log__tag--error {
1837
2221
  color: var(--danger);
1838
2222
  background: var(--danger-soft);
1839
- border-color: var(--danger-soft);
1840
2223
  }
1841
2224
  /* 内容区:基础类型内联、复合类型独占整行 */
1842
2225
  .log__content {
@@ -1845,10 +2228,10 @@ body {
1845
2228
  display: flex;
1846
2229
  flex-wrap: wrap;
1847
2230
  align-items: flex-start;
1848
- gap: 4px 10px;
2231
+ gap: 3px 8px;
1849
2232
  font-family: 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace;
1850
- font-size: 12px;
1851
- line-height: 1.5;
2233
+ font-size: 11px;
2234
+ line-height: 1.45;
1852
2235
  color: var(--text);
1853
2236
  }
1854
2237
  .log__val {
@@ -1857,7 +2240,23 @@ body {
1857
2240
  }
1858
2241
  .log__json {
1859
2242
  flex: 1 1 100%;
1860
- padding: 2px 0;
2243
+ padding: 0;
2244
+ }
2245
+ /* 日志内嵌 JSON 树比接口面板再紧一档,去掉多余上下留白 */
2246
+ .log__json .apt {
2247
+ margin-top: 0;
2248
+ font-size: 11px;
2249
+ line-height: 1.4;
2250
+ }
2251
+ /* 根摘要行可点:hover 给底色提示;展开后明细列表缩进一级 */
2252
+ .log__json .apt__branch--root {
2253
+ cursor: pointer;
2254
+ border-radius: 4px;
2255
+ }
2256
+ .log__json .apt__list--nested {
2257
+ margin-left: 4px;
2258
+ padding-left: 14px;
2259
+ border-left: 1px solid var(--border);
1861
2260
  }
1862
2261
  .log__empty {
1863
2262
  color: var(--text-faint);
@@ -2274,8 +2673,8 @@ body {
2274
2673
  display: flex;
2275
2674
  align-items: center;
2276
2675
  justify-content: center;
2277
- background: rgba(8, 10, 16, 0.55);
2278
- backdrop-filter: blur(2px);
2676
+ background: var(--scrim-modal);
2677
+ backdrop-filter: blur(var(--scrim-modal-blur));
2279
2678
  }
2280
2679
  .rp-card {
2281
2680
  width: 300px;
@@ -2456,8 +2855,8 @@ body {
2456
2855
  display: flex;
2457
2856
  align-items: center;
2458
2857
  justify-content: center;
2459
- background: rgba(8, 10, 16, 0.55);
2460
- backdrop-filter: blur(2px);
2858
+ background: var(--scrim-modal);
2859
+ backdrop-filter: blur(var(--scrim-modal-blur));
2461
2860
  }
2462
2861
  .login-modal {
2463
2862
  width: 320px;
@@ -2556,8 +2955,8 @@ body {
2556
2955
  display: flex;
2557
2956
  align-items: center;
2558
2957
  justify-content: center;
2559
- background: rgba(8, 10, 16, 0.55);
2560
- backdrop-filter: blur(2px);
2958
+ background: var(--scrim-modal);
2959
+ backdrop-filter: blur(var(--scrim-modal-blur));
2561
2960
  }
2562
2961
  .confirm-modal {
2563
2962
  width: 360px;
@@ -2646,11 +3045,45 @@ body {
2646
3045
  .confirm-modal__btn--warn:hover {
2647
3046
  background: var(--warn-hover);
2648
3047
  }
2649
- .confirm-fade-enter-active,
2650
- .confirm-fade-leave-active {
2651
- transition: opacity 0.15s ease;
3048
+ /* ===== 轻量浮层(帮助 / 快捷键弹层)统一进出场 =====
3049
+ 淡入 + 轻弹性缩放(过冲回稳),缩放原点由各浮层按锚定方位自定(transform-origin);
3050
+ 离场快速收回、不回弹。 */
3051
+ .pop-enter-active {
3052
+ transition: opacity 0.15s ease, transform 0.26s cubic-bezier(0.34, 1.56, 0.64, 1);
2652
3053
  }
2653
- .confirm-fade-enter-from,
2654
- .confirm-fade-leave-to {
3054
+ .pop-leave-active {
3055
+ transition: opacity 0.12s ease, transform 0.12s ease;
3056
+ }
3057
+ .pop-enter-from,
3058
+ .pop-leave-to {
3059
+ opacity: 0;
3060
+ transform: translateY(6px) scale(0.95);
3061
+ }
3062
+ /* ===== 带遮罩模态的统一进出场动画 =====
3063
+ 适用:确认框 / 卡片网格预览 / 远端预览二维码 / 登录凭据 / 组件选择 / 节点属性等居中模态。
3064
+ 遮罩仅淡入/淡出;弹卡片(.modal-card)微抬 + 缩放入场、原路离场。
3065
+ 右侧抽屉(ee-drawer)保留自己的右滑语言,不复用本组。 */
3066
+ .modal-enter-active,
3067
+ .modal-leave-active {
3068
+ transition: opacity 0.2s ease;
3069
+ }
3070
+ .modal-leave-active {
3071
+ transition: opacity 0.16s ease;
3072
+ }
3073
+ .modal-enter-from,
3074
+ .modal-leave-to {
2655
3075
  opacity: 0;
2656
3076
  }
3077
+ /* 入场带一点弹性:back-out 过冲曲线让弹卡片微 overshoot 后回稳;离场干脆不回弹 */
3078
+ .modal-enter-active .modal-card {
3079
+ transition: transform 0.34s cubic-bezier(0.34, 1.56, 0.64, 1);
3080
+ }
3081
+ .modal-leave-active .modal-card {
3082
+ transition: transform 0.16s ease;
3083
+ }
3084
+ .modal-enter-from .modal-card {
3085
+ transform: translateY(14px) scale(0.94);
3086
+ }
3087
+ .modal-leave-to .modal-card {
3088
+ transform: translateY(6px) scale(0.98);
3089
+ }