@silicaclaw/cli 2026.3.18-3 → 2026.3.18-4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -141,23 +141,23 @@
141
141
  flex-direction: column;
142
142
  min-height: 0;
143
143
  border-right: 1px solid color-mix(in srgb, var(--border) 74%, transparent);
144
- background: var(--bg);
145
- padding: 14px 10px 12px;
144
+ background: color-mix(in srgb, var(--bg) 96%, var(--bg-elevated) 4%);
146
145
  position: relative;
147
146
  overflow: hidden;
148
- transition: padding .2s ease;
149
147
  }
150
148
  :root[data-theme-mode="light"] .sidebar {
151
- background: var(--panel);
152
- }
153
- .app.nav-collapsed .sidebar {
154
- padding: 12px 8px 10px;
149
+ background: color-mix(in srgb, var(--panel) 98%, white 2%);
155
150
  }
156
151
  .sidebar-shell {
157
152
  display: flex;
158
153
  flex: 1 1 auto;
159
154
  min-height: 0;
160
155
  flex-direction: column;
156
+ padding: 14px 10px 12px;
157
+ background: transparent;
158
+ }
159
+ .app.nav-collapsed .sidebar-shell {
160
+ padding: 12px 8px 10px;
161
161
  }
162
162
  .sidebar-shell__header {
163
163
  flex: 0 0 auto;
@@ -174,12 +174,13 @@
174
174
  display: flex;
175
175
  align-items: center;
176
176
  justify-content: center;
177
- background: var(--bg-elevated);
177
+ background: color-mix(in srgb, var(--bg-elevated) 88%, transparent);
178
178
  border: 1px solid color-mix(in srgb, var(--border-strong) 68%, transparent);
179
179
  border-radius: 999px;
180
180
  cursor: pointer;
181
181
  color: var(--muted);
182
182
  flex: 0 0 auto;
183
+ box-shadow: inset 0 1px 0 color-mix(in srgb, white 8%, transparent);
183
184
  transition:
184
185
  background .16s ease,
185
186
  border-color .16s ease,
@@ -223,7 +224,7 @@
223
224
  display: flex;
224
225
  align-items: center;
225
226
  gap: 10px;
226
- margin-bottom: 0;
227
+ min-width: 0;
227
228
  }
228
229
  .brand-logo {
229
230
  width: 32px;
@@ -245,14 +246,24 @@
245
246
  place-items: center;
246
247
  }
247
248
  .brand-badge.hidden { display: none; }
249
+ .brand-copy {
250
+ display: flex;
251
+ flex-direction: column;
252
+ gap: 2px;
253
+ min-width: 0;
254
+ }
248
255
  .brand h1 {
249
256
  margin: 0;
250
257
  font-size: 15px;
251
258
  line-height: 1.1;
252
259
  letter-spacing: -0.03em;
260
+ color: var(--text-strong);
261
+ white-space: nowrap;
262
+ overflow: hidden;
263
+ text-overflow: ellipsis;
253
264
  }
254
265
  .brand p {
255
- margin: 2px 0 0;
266
+ margin: 0;
256
267
  color: var(--muted);
257
268
  font-size: 10px;
258
269
  line-height: 1.15;
@@ -270,7 +281,7 @@
270
281
  .app.nav-collapsed .brand h1,
271
282
  .app.nav-collapsed .brand p,
272
283
  .app.nav-collapsed .sidebar-foot,
273
- .app.nav-collapsed .sidebar-nav__label {
284
+ .app.nav-collapsed .nav-section__label {
274
285
  display: none;
275
286
  }
276
287
  .app.focus-mode .sidebar {
@@ -308,18 +319,36 @@
308
319
  .app.nav-collapsed .nav button .tab-icon {
309
320
  width: 18px;
310
321
  height: 18px;
322
+ transform: translateX(.5px);
311
323
  }
312
324
  .app.nav-collapsed .nav button .tab-icon svg {
313
325
  width: 18px;
314
326
  height: 18px;
315
327
  }
316
328
  .app.nav-collapsed .nav button.active::before {
329
+ content: "";
330
+ position: absolute;
317
331
  left: 8px;
318
332
  top: 10px;
319
333
  bottom: 10px;
334
+ width: 3px;
335
+ border-radius: 999px;
336
+ background: color-mix(in srgb, var(--accent) 86%, transparent);
337
+ box-shadow: 0 0 14px color-mix(in srgb, var(--accent) 34%, transparent);
320
338
  }
321
339
  .app.nav-collapsed .sidebar-shell__footer {
322
340
  padding: 8px 0 2px;
341
+ display: grid;
342
+ justify-items: center;
343
+ }
344
+ .app.nav-collapsed .nav-collapse-toggle {
345
+ width: 42px;
346
+ height: 42px;
347
+ border-color: color-mix(in srgb, var(--border) 82%, transparent);
348
+ background: color-mix(in srgb, var(--bg-elevated) 92%, transparent);
349
+ box-shadow:
350
+ inset 0 1px 0 color-mix(in srgb, white 8%, transparent),
351
+ 0 8px 18px color-mix(in srgb, black 16%, transparent);
323
352
  }
324
353
  .app.nav-collapsed .sidebar-utility {
325
354
  display: none;
@@ -331,24 +360,56 @@
331
360
  justify-content: center;
332
361
  border-radius: 16px;
333
362
  }
363
+ .app.nav-collapsed .sidebar-version__status {
364
+ margin-left: 0;
365
+ }
334
366
 
335
367
  .nav {
336
368
  display: grid;
337
- gap: 4px;
369
+ align-content: start;
338
370
  min-height: 0;
339
371
  overflow: auto;
340
372
  padding: 0;
341
373
  scrollbar-width: none;
342
374
  }
343
375
  .nav::-webkit-scrollbar { display: none; }
344
- .sidebar-nav__label {
345
- padding: 0 10px 8px;
376
+ .sidebar-nav {
377
+ flex: 1;
378
+ overflow-y: auto;
379
+ overflow-x: hidden;
380
+ padding: 0;
381
+ }
382
+ .sidebar-nav::-webkit-scrollbar {
383
+ display: none;
384
+ }
385
+ .nav-section {
386
+ display: grid;
387
+ align-content: start;
388
+ gap: 6px;
389
+ margin-bottom: 10px;
390
+ }
391
+ .nav-section:last-child {
392
+ margin-bottom: 0;
393
+ }
394
+ .nav-section__label {
395
+ display: flex;
396
+ align-items: center;
397
+ justify-content: space-between;
398
+ gap: 8px;
399
+ width: 100%;
400
+ min-height: 28px;
401
+ padding: 0 10px;
346
402
  color: var(--muted);
347
403
  font-size: 12px;
348
404
  font-weight: 700;
349
405
  letter-spacing: 0.06em;
350
406
  text-transform: uppercase;
351
407
  }
408
+ .nav-section__items {
409
+ display: grid;
410
+ align-content: start;
411
+ gap: 4px;
412
+ }
352
413
  .nav button {
353
414
  position: relative;
354
415
  display: flex;
@@ -360,10 +421,11 @@
360
421
  border: 1px solid transparent;
361
422
  background: transparent;
362
423
  color: var(--muted);
363
- padding: 0 9px;
424
+ padding: 0 10px;
364
425
  border-radius: 12px;
365
426
  cursor: pointer;
366
427
  font: inherit;
428
+ line-height: 1;
367
429
  transition:
368
430
  border-color .16s ease,
369
431
  background .16s ease,
@@ -378,6 +440,7 @@
378
440
  justify-content: center;
379
441
  flex-shrink: 0;
380
442
  opacity: .72;
443
+ transform: translateY(-.5px);
381
444
  }
382
445
  .nav button .tab-icon svg {
383
446
  width: 16px;
@@ -396,6 +459,7 @@
396
459
  font-size: 14px;
397
460
  font-weight: 600;
398
461
  color: inherit;
462
+ line-height: 1.1;
399
463
  white-space: nowrap;
400
464
  }
401
465
  .nav button .tab-copy {
@@ -403,9 +467,11 @@
403
467
  }
404
468
  .nav button.active {
405
469
  color: var(--text-strong);
406
- border-color: color-mix(in srgb, var(--accent) 18%, transparent);
407
- background: color-mix(in srgb, var(--accent-subtle) 88%, var(--bg-elevated) 12%);
408
- box-shadow: inset 0 1px 0 color-mix(in srgb, white 10%, transparent);
470
+ border-color: color-mix(in srgb, var(--accent) 14%, transparent);
471
+ background: color-mix(in srgb, var(--accent-subtle) 76%, var(--bg-elevated) 24%);
472
+ box-shadow:
473
+ inset 0 1px 0 color-mix(in srgb, white 10%, transparent),
474
+ 0 10px 18px color-mix(in srgb, black 8%, transparent);
409
475
  }
410
476
  .nav button.active .tab-icon {
411
477
  opacity: 1;
@@ -456,16 +522,31 @@
456
522
  font-weight: 600;
457
523
  }
458
524
  .sidebar-version {
459
- margin-top: 10px;
525
+ margin-top: 0;
460
526
  display: flex;
461
527
  align-items: center;
462
- justify-content: center;
463
- min-height: 40px;
528
+ justify-content: space-between;
529
+ gap: 10px;
530
+ min-height: 42px;
464
531
  padding: 0 12px;
465
532
  border-radius: 14px;
466
- background: var(--bg-elevated);
533
+ background: color-mix(in srgb, var(--bg-elevated) 72%, transparent);
467
534
  border: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
468
- box-shadow: none;
535
+ box-shadow: inset 0 1px 0 color-mix(in srgb, white 6%, transparent);
536
+ }
537
+ .sidebar-version__copy {
538
+ display: flex;
539
+ flex-direction: column;
540
+ gap: 2px;
541
+ min-width: 0;
542
+ }
543
+ .sidebar-version__label {
544
+ font-size: 10px;
545
+ line-height: 1;
546
+ color: var(--muted);
547
+ font-weight: 700;
548
+ letter-spacing: 0.08em;
549
+ text-transform: uppercase;
469
550
  }
470
551
  .sidebar-version__text {
471
552
  font-size: 12px;
@@ -475,12 +556,30 @@
475
556
  white-space: nowrap;
476
557
  overflow: hidden;
477
558
  text-overflow: ellipsis;
478
- text-align: center;
559
+ }
560
+ .sidebar-version__status {
561
+ width: 8px;
562
+ height: 8px;
563
+ border-radius: 999px;
564
+ background: color-mix(in srgb, var(--muted) 55%, transparent);
565
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--muted) 12%, transparent);
566
+ flex: 0 0 auto;
567
+ }
568
+ .sidebar-version__status.ok {
569
+ background: var(--ok);
570
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--ok) 14%, transparent);
571
+ }
572
+ .sidebar-version__status.warn {
573
+ background: var(--warn);
574
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--warn) 14%, transparent);
479
575
  }
480
576
  .app.nav-collapsed .sidebar-version {
481
577
  justify-content: center;
482
578
  padding: 0;
483
579
  }
580
+ .app.nav-collapsed .sidebar-version__copy {
581
+ display: none;
582
+ }
484
583
 
485
584
  .main {
486
585
  display: flex;
@@ -494,16 +593,21 @@
494
593
  .topbar {
495
594
  flex: 0 0 auto;
496
595
  display: flex;
497
- justify-content: space-between;
498
- gap: 12px;
499
596
  align-items: center;
500
- min-height: 52px;
597
+ min-height: 58px;
501
598
  padding: 0 24px;
502
599
  border-bottom: 1px solid color-mix(in srgb, var(--border) 74%, transparent);
503
600
  background: var(--chrome);
504
601
  backdrop-filter: blur(12px) saturate(1.6);
505
602
  -webkit-backdrop-filter: blur(12px) saturate(1.6);
506
603
  }
604
+ .topnav-shell {
605
+ display: flex;
606
+ align-items: center;
607
+ gap: 16px;
608
+ width: 100%;
609
+ min-height: 52px;
610
+ }
507
611
  .topbar h2 {
508
612
  display: none;
509
613
  margin: 0;
@@ -523,9 +627,7 @@
523
627
  gap: 8px;
524
628
  min-width: 0;
525
629
  overflow: hidden;
526
- font-size: 12px;
527
- margin-bottom: 2px;
528
- letter-spacing: 0.01em;
630
+ font-size: 13px;
529
631
  }
530
632
  .dashboard-header__breadcrumb-link,
531
633
  .dashboard-header__breadcrumb-sep {
@@ -544,11 +646,17 @@
544
646
  flex: 1;
545
647
  overflow: hidden;
546
648
  }
649
+ .topnav-shell__actions {
650
+ display: flex;
651
+ align-items: center;
652
+ gap: 10px;
653
+ flex-shrink: 0;
654
+ }
547
655
  .main-scroll {
548
656
  flex: 1 1 auto;
549
657
  min-height: 0;
550
658
  overflow: auto;
551
- padding: 12px 18px 18px;
659
+ padding: 10px 18px 18px;
552
660
  }
553
661
  .status-row {
554
662
  display: flex;
@@ -558,14 +666,19 @@
558
666
  flex-shrink: 0;
559
667
  min-width: max-content;
560
668
  }
669
+ .topbar-status {
670
+ display: flex;
671
+ align-items: center;
672
+ gap: 8px;
673
+ }
561
674
  .topbar-actions {
562
675
  display: inline-flex;
563
676
  align-items: center;
564
- gap: 8px;
677
+ gap: 10px;
565
678
  }
566
679
  .icon-btn {
567
- width: 30px;
568
- height: 30px;
680
+ width: 32px;
681
+ height: 32px;
569
682
  display: inline-flex;
570
683
  align-items: center;
571
684
  justify-content: center;
@@ -586,8 +699,8 @@
586
699
  background: var(--accent-subtle);
587
700
  }
588
701
  .icon-btn svg {
589
- width: 15px;
590
- height: 15px;
702
+ width: 16px;
703
+ height: 16px;
591
704
  stroke: currentColor;
592
705
  }
593
706
  .topbar-theme-mode {
@@ -665,6 +778,7 @@
665
778
  min-height: 32px;
666
779
  display: inline-flex;
667
780
  align-items: center;
781
+ gap: 6px;
668
782
  box-shadow: inset 0 1px 0 color-mix(in srgb, white 4%, transparent);
669
783
  }
670
784
  .pill.ok { color: var(--ok); border-color: rgba(34, 197, 94, 0.45); background: rgba(34, 197, 94, 0.08); }
@@ -672,7 +786,7 @@
672
786
 
673
787
  .notice {
674
788
  display: none;
675
- margin-bottom: 12px;
789
+ margin-bottom: 8px;
676
790
  border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
677
791
  background: var(--accent-subtle);
678
792
  border-radius: 10px;
@@ -684,12 +798,12 @@
684
798
  position: sticky;
685
799
  top: 0;
686
800
  z-index: 9;
687
- margin-bottom: 8px;
801
+ margin-bottom: 4px;
688
802
  border: 1px solid color-mix(in srgb, var(--border) 82%, transparent);
689
803
  border-radius: 14px;
690
804
  background:
691
805
  linear-gradient(180deg, color-mix(in srgb, var(--card-soft) 98%, transparent), color-mix(in srgb, var(--bg-elevated) 94%, transparent));
692
- padding: 8px 12px;
806
+ padding: 6px 11px;
693
807
  font-size: 12px;
694
808
  line-height: 1.45;
695
809
  color: var(--text);
@@ -703,56 +817,90 @@
703
817
  }
704
818
  .page-hero {
705
819
  display: grid;
706
- grid-template-columns: 1.2fr .8fr;
707
- gap: 10px;
708
- margin-bottom: 8px;
820
+ grid-template-columns: minmax(0, 1fr) 440px;
821
+ align-items: stretch;
822
+ gap: 6px;
823
+ margin-bottom: 4px;
709
824
  }
710
825
  .hero-copy {
826
+ display: flex;
827
+ align-items: center;
828
+ gap: 12px;
711
829
  border: 1px solid color-mix(in srgb, var(--border) 84%, transparent);
712
830
  border-radius: 16px;
713
831
  background:
714
832
  linear-gradient(180deg, color-mix(in srgb, var(--card-strong) 98%, transparent), color-mix(in srgb, var(--card-soft) 96%, transparent));
715
- padding: 11px 14px;
833
+ padding: 10px 12px;
716
834
  box-shadow:
717
835
  inset 0 1px 0 color-mix(in srgb, white 4%, transparent),
718
836
  0 10px 24px color-mix(in srgb, black 9%, transparent);
719
837
  }
720
838
  .hero-copy h3 {
721
839
  margin: 0;
722
- font-size: 17px;
840
+ flex: 0 0 auto;
841
+ font-size: 15px;
723
842
  color: var(--text-strong);
724
843
  letter-spacing: -0.02em;
725
844
  }
726
845
  .hero-copy p {
727
- margin: 5px 0 0;
846
+ margin: 0;
728
847
  color: var(--muted);
729
848
  font-size: 12px;
730
849
  line-height: 1.42;
850
+ min-width: 0;
731
851
  }
732
852
  .hero-meta {
733
853
  border: 1px solid color-mix(in srgb, var(--border) 84%, transparent);
734
854
  border-radius: 16px;
735
855
  background:
736
856
  linear-gradient(180deg, color-mix(in srgb, var(--bg-elevated) 97%, transparent), color-mix(in srgb, var(--panel) 98%, transparent));
737
- padding: 11px;
857
+ padding: 8px;
738
858
  box-shadow: inset 0 1px 0 color-mix(in srgb, white 4%, transparent);
739
859
  }
740
860
  .hero-meta-grid {
741
861
  display: grid;
742
862
  grid-template-columns: repeat(2, minmax(0, 1fr));
743
- gap: 6px;
863
+ gap: 4px;
744
864
  }
745
865
  .hero-meta-item {
746
866
  border: 1px solid color-mix(in srgb, var(--border) 76%, transparent);
747
867
  border-radius: 12px;
748
868
  background: color-mix(in srgb, var(--panel) 90%, transparent);
749
- padding: 7px 9px;
869
+ padding: 5px 7px;
750
870
  }
751
871
 
752
872
  .view { display: none; }
753
873
  .view.active {
754
874
  display: grid;
755
- gap: 8px;
875
+ gap: 6px;
876
+ }
877
+
878
+ .overview-panel-header {
879
+ display: flex;
880
+ align-items: center;
881
+ justify-content: space-between;
882
+ gap: 12px;
883
+ margin-bottom: 6px;
884
+ }
885
+ .overview-panel-title {
886
+ min-width: 0;
887
+ }
888
+ .overview-panel-title .title-sm {
889
+ margin: 0;
890
+ }
891
+ .overview-panel-controls {
892
+ display: inline-flex;
893
+ align-items: center;
894
+ gap: 10px;
895
+ flex-shrink: 0;
896
+ }
897
+ .overview-inline-toggle {
898
+ display: inline-flex;
899
+ align-items: center;
900
+ gap: 6px;
901
+ color: var(--muted);
902
+ font-size: 12px;
903
+ white-space: nowrap;
756
904
  }
757
905
 
758
906
  .section-header {
@@ -761,12 +909,13 @@
761
909
  justify-content: space-between;
762
910
  gap: 12px;
763
911
  min-width: 0;
912
+ margin-bottom: 2px;
764
913
  }
765
914
  .section-header__copy {
766
915
  min-width: 0;
767
916
  }
768
917
  .section-header__eyebrow {
769
- margin: 0 0 3px;
918
+ margin: 0 0 2px;
770
919
  color: var(--muted);
771
920
  font-size: 11px;
772
921
  font-weight: 700;
@@ -780,7 +929,7 @@
780
929
  letter-spacing: -0.02em;
781
930
  }
782
931
  .section-header__body {
783
- margin: 4px 0 0;
932
+ margin: 3px 0 0;
784
933
  color: var(--muted);
785
934
  font-size: 12px;
786
935
  line-height: 1.45;
@@ -790,13 +939,16 @@
790
939
  border-radius: 16px;
791
940
  background:
792
941
  linear-gradient(180deg, color-mix(in srgb, var(--card-strong) 98%, transparent), color-mix(in srgb, var(--card-soft) 96%, transparent));
793
- padding: 12px;
942
+ padding: 10px;
794
943
  box-shadow:
795
944
  inset 0 1px 0 color-mix(in srgb, white 4%, transparent),
796
945
  0 14px 30px color-mix(in srgb, black 10%, transparent);
797
946
  }
798
947
  .section-surface.compact {
799
- padding: 10px 12px;
948
+ padding: 8px 10px;
949
+ }
950
+ #view-overview .section-surface.compact {
951
+ margin-bottom: 2px;
800
952
  }
801
953
 
802
954
  .grid {
@@ -815,10 +967,10 @@
815
967
  border-radius: 16px;
816
968
  background:
817
969
  linear-gradient(180deg, color-mix(in srgb, var(--card-strong) 98%, transparent), color-mix(in srgb, var(--card-soft) 96%, transparent));
818
- padding: 12px;
970
+ padding: 10px;
819
971
  box-shadow:
820
972
  inset 0 1px 0 color-mix(in srgb, white 4%, transparent),
821
- 0 14px 30px color-mix(in srgb, black 10%, transparent);
973
+ 0 12px 24px color-mix(in srgb, black 9%, transparent);
822
974
  transition: border-color .16s ease, transform .16s ease, box-shadow .16s ease;
823
975
  }
824
976
  .card:hover {
@@ -831,12 +983,12 @@
831
983
  .value { margin-top: 4px; font-size: 22px; font-weight: 800; letter-spacing: -0.03em; }
832
984
 
833
985
  .split {
834
- margin-top: 8px;
986
+ margin-top: 4px;
835
987
  display: grid;
836
- gap: 8px;
988
+ gap: 6px;
837
989
  grid-template-columns: 1.2fr 1fr;
838
990
  }
839
- .title-sm { margin: 0 0 10px; font-size: 16px; letter-spacing: -0.02em; }
991
+ .title-sm { margin: 0 0 6px; font-size: 15px; letter-spacing: -0.02em; }
840
992
  .mono { font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
841
993
 
842
994
  .table {
@@ -853,15 +1005,171 @@
853
1005
  .table th, .table td {
854
1006
  border-bottom: 1px solid var(--border);
855
1007
  text-align: left;
856
- padding: 8px 6px;
1008
+ padding: 7px 6px;
857
1009
  font-size: 13px;
858
1010
  }
1011
+ .agent-list {
1012
+ display: grid;
1013
+ gap: 8px;
1014
+ }
1015
+ .agent-card {
1016
+ display: grid;
1017
+ grid-template-columns: auto minmax(0, 1fr) auto auto;
1018
+ gap: 10px;
1019
+ align-items: center;
1020
+ padding: 9px 10px;
1021
+ border: 1px solid color-mix(in srgb, var(--border) 80%, transparent);
1022
+ border-radius: 14px;
1023
+ background: linear-gradient(180deg, color-mix(in srgb, var(--panel) 94%, transparent), color-mix(in srgb, var(--bg-elevated) 90%, transparent));
1024
+ }
1025
+ .agent-card__avatar {
1026
+ width: 34px;
1027
+ height: 34px;
1028
+ border-radius: 10px;
1029
+ object-fit: cover;
1030
+ display: block;
1031
+ border: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
1032
+ background: color-mix(in srgb, var(--bg-elevated) 92%, transparent);
1033
+ }
1034
+ .agent-card__avatar-fallback {
1035
+ width: 34px;
1036
+ height: 34px;
1037
+ border-radius: 10px;
1038
+ display: grid;
1039
+ place-items: center;
1040
+ font-size: 12px;
1041
+ font-weight: 800;
1042
+ color: var(--text-strong);
1043
+ border: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
1044
+ background: color-mix(in srgb, var(--accent-subtle) 70%, var(--bg-elevated) 30%);
1045
+ }
1046
+ .agent-card__main {
1047
+ min-width: 0;
1048
+ display: grid;
1049
+ gap: 4px;
1050
+ }
1051
+ .agent-card__row {
1052
+ display: flex;
1053
+ align-items: center;
1054
+ gap: 8px;
1055
+ min-width: 0;
1056
+ }
1057
+ .agent-card__name {
1058
+ min-width: 0;
1059
+ font-size: 14px;
1060
+ font-weight: 700;
1061
+ color: var(--text-strong);
1062
+ white-space: nowrap;
1063
+ overflow: hidden;
1064
+ text-overflow: ellipsis;
1065
+ }
1066
+ .agent-card__id {
1067
+ color: var(--muted);
1068
+ font-size: 11px;
1069
+ white-space: nowrap;
1070
+ }
1071
+ .agent-card__bio {
1072
+ color: var(--muted);
1073
+ font-size: 12px;
1074
+ line-height: 1.4;
1075
+ white-space: nowrap;
1076
+ overflow: hidden;
1077
+ text-overflow: ellipsis;
1078
+ }
1079
+ .agent-card__tags {
1080
+ display: flex;
1081
+ align-items: center;
1082
+ gap: 6px;
1083
+ flex-wrap: wrap;
1084
+ }
1085
+ .agent-card__meta {
1086
+ display: grid;
1087
+ gap: 4px;
1088
+ justify-items: end;
1089
+ text-align: right;
1090
+ }
1091
+ .agent-card__updated {
1092
+ color: var(--muted);
1093
+ font-size: 12px;
1094
+ white-space: nowrap;
1095
+ }
1096
+ .agent-list__footer {
1097
+ display: flex;
1098
+ align-items: center;
1099
+ justify-content: space-between;
1100
+ gap: 10px;
1101
+ margin-top: 8px;
1102
+ }
1103
+ .agent-list__page {
1104
+ color: var(--muted);
1105
+ font-size: 12px;
1106
+ }
1107
+ .agent-list__pager {
1108
+ display: inline-flex;
1109
+ align-items: center;
1110
+ gap: 6px;
1111
+ }
1112
+ .agent-list__pager button {
1113
+ min-height: 32px;
1114
+ padding: 0 10px;
1115
+ }
1116
+ .snapshot-card {
1117
+ display: grid;
1118
+ gap: 10px;
1119
+ }
1120
+ .snapshot-card__identity {
1121
+ display: grid;
1122
+ gap: 6px;
1123
+ }
1124
+ .snapshot-card__label {
1125
+ font-size: 11px;
1126
+ font-weight: 700;
1127
+ color: var(--muted);
1128
+ letter-spacing: 0.06em;
1129
+ text-transform: uppercase;
1130
+ }
1131
+ .snapshot-card__title {
1132
+ font-size: 15px;
1133
+ font-weight: 700;
1134
+ color: var(--text-strong);
1135
+ line-height: 1.2;
1136
+ word-break: break-word;
1137
+ }
1138
+ .snapshot-card__subtle {
1139
+ color: var(--muted);
1140
+ font-size: 12px;
1141
+ line-height: 1.45;
1142
+ word-break: break-word;
1143
+ }
1144
+ .snapshot-card__grid {
1145
+ display: grid;
1146
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1147
+ gap: 6px;
1148
+ }
1149
+ .snapshot-card__item {
1150
+ border: 1px solid color-mix(in srgb, var(--border) 76%, transparent);
1151
+ border-radius: 12px;
1152
+ background: color-mix(in srgb, var(--panel) 90%, transparent);
1153
+ padding: 7px 8px;
1154
+ }
1155
+ .snapshot-card__item .label {
1156
+ font-size: 11px;
1157
+ }
1158
+ .snapshot-card__item .mono,
1159
+ .snapshot-card__item .value-inline {
1160
+ margin-top: 4px;
1161
+ display: block;
1162
+ color: var(--text);
1163
+ font-size: 12px;
1164
+ line-height: 1.35;
1165
+ word-break: break-word;
1166
+ }
859
1167
  .online { color: var(--ok); font-weight: 700; }
860
1168
  .offline { color: #f48c8f; font-weight: 700; }
861
1169
  .stale { color: var(--warn); font-weight: 700; }
862
1170
 
863
- .stack { display: grid; gap: 10px; }
864
- .row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
1171
+ .stack { display: grid; gap: 8px; }
1172
+ .row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
865
1173
  label { color: var(--muted); font-size: 13px; }
866
1174
  input, textarea {
867
1175
  margin-top: 5px;
@@ -871,7 +1179,7 @@
871
1179
  background: linear-gradient(180deg, color-mix(in srgb, var(--bg-elevated) 98%, transparent), color-mix(in srgb, var(--panel) 96%, transparent));
872
1180
  color: var(--text);
873
1181
  font: inherit;
874
- padding: 10px;
1182
+ padding: 9px 10px;
875
1183
  box-shadow: inset 0 1px 0 color-mix(in srgb, white 3%, transparent);
876
1184
  }
877
1185
  select {
@@ -894,7 +1202,7 @@
894
1202
  inset 0 1px 0 color-mix(in srgb, white 3%, transparent),
895
1203
  0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent);
896
1204
  }
897
- textarea { min-height: 90px; resize: vertical; }
1205
+ textarea { min-height: 84px; resize: vertical; }
898
1206
 
899
1207
  .actions {
900
1208
  display: flex;
@@ -910,7 +1218,7 @@
910
1218
  display: flex;
911
1219
  gap: 8px;
912
1220
  flex-wrap: wrap;
913
- margin-bottom: 8px;
1221
+ margin-bottom: 4px;
914
1222
  padding: 2px 0 0;
915
1223
  }
916
1224
  .summary-list {
@@ -920,7 +1228,7 @@
920
1228
  .summary-item {
921
1229
  display: grid;
922
1230
  gap: 3px;
923
- padding: 10px;
1231
+ padding: 9px;
924
1232
  border-radius: 12px;
925
1233
  border: 1px solid color-mix(in srgb, var(--border) 82%, transparent);
926
1234
  background: linear-gradient(180deg, color-mix(in srgb, var(--bg-elevated) 98%, transparent), color-mix(in srgb, var(--panel) 94%, transparent));
@@ -929,10 +1237,11 @@
929
1237
  .empty-state {
930
1238
  border: 1px dashed color-mix(in srgb, var(--border-strong) 88%, transparent);
931
1239
  border-radius: 14px;
932
- padding: 18px;
1240
+ padding: 15px 16px;
933
1241
  color: var(--muted);
934
1242
  background: linear-gradient(180deg, color-mix(in srgb, var(--panel) 94%, transparent), color-mix(in srgb, var(--bg-elevated) 88%, transparent));
935
- line-height: 1.55;
1243
+ line-height: 1.5;
1244
+ font-size: 12.5px;
936
1245
  }
937
1246
  button {
938
1247
  border: 1px solid transparent;
@@ -969,29 +1278,34 @@
969
1278
  }
970
1279
  button.secondary {
971
1280
  border-color: var(--border-strong);
972
- background: color-mix(in srgb, var(--bg-elevated) 96%, transparent);
1281
+ background: linear-gradient(180deg, color-mix(in srgb, var(--bg-elevated) 98%, transparent), color-mix(in srgb, var(--panel) 94%, transparent));
973
1282
  color: var(--text);
974
1283
  box-shadow: none;
975
1284
  }
1285
+ button.secondary:hover {
1286
+ border-color: color-mix(in srgb, var(--accent) 18%, var(--border-strong));
1287
+ background: color-mix(in srgb, var(--bg-hover) 86%, transparent);
1288
+ }
976
1289
 
977
1290
  .feedback {
978
1291
  border: 1px solid color-mix(in srgb, var(--border) 84%, transparent);
979
1292
  border-radius: 12px;
980
1293
  background: linear-gradient(180deg, color-mix(in srgb, var(--bg-elevated) 98%, transparent), color-mix(in srgb, var(--panel) 96%, transparent));
981
- padding: 10px 11px;
982
- font-size: 13px;
983
- color: var(--muted);
1294
+ padding: 9px 10px;
1295
+ font-size: 12.5px;
1296
+ color: var(--text);
984
1297
  box-shadow: inset 0 1px 0 color-mix(in srgb, white 4%, transparent);
1298
+ line-height: 1.45;
985
1299
  }
986
1300
  .profile-layout {
987
1301
  display: grid;
988
- gap: 10px;
1302
+ gap: 8px;
989
1303
  grid-template-columns: 1.1fr .9fr;
990
1304
  }
991
1305
  .profile-meta {
992
1306
  border: 1px solid color-mix(in srgb, var(--border) 82%, transparent);
993
1307
  border-radius: 12px;
994
- padding: 10px;
1308
+ padding: 9px;
995
1309
  background: linear-gradient(180deg, color-mix(in srgb, var(--card-soft) 98%, transparent), color-mix(in srgb, var(--bg-elevated) 90%, transparent));
996
1310
  box-shadow: inset 0 1px 0 color-mix(in srgb, white 4%, transparent);
997
1311
  }
@@ -1012,7 +1326,7 @@
1012
1326
  line-height: 1.45;
1013
1327
  }
1014
1328
  .tag-chips {
1015
- margin-top: 10px;
1329
+ margin-top: 8px;
1016
1330
  display: flex;
1017
1331
  gap: 6px;
1018
1332
  flex-wrap: wrap;
@@ -1033,7 +1347,7 @@
1033
1347
  margin-top: 4px;
1034
1348
  color: var(--muted);
1035
1349
  font-size: 12px;
1036
- line-height: 1.45;
1350
+ line-height: 1.4;
1037
1351
  }
1038
1352
  .field-error {
1039
1353
  margin-top: 4px;
@@ -1053,8 +1367,8 @@
1053
1367
  overflow: auto;
1054
1368
  border: 1px solid color-mix(in srgb, var(--border) 82%, transparent);
1055
1369
  border-radius: 14px;
1056
- background: linear-gradient(180deg, color-mix(in srgb, var(--panel) 96%, transparent), color-mix(in srgb, var(--bg-elevated) 90%, transparent));
1057
- padding: 10px;
1370
+ background: linear-gradient(180deg, color-mix(in srgb, var(--panel) 97%, transparent), color-mix(in srgb, var(--bg-elevated) 92%, transparent));
1371
+ padding: 9px;
1058
1372
  box-shadow: inset 0 1px 0 color-mix(in srgb, white 3%, transparent);
1059
1373
  }
1060
1374
  .log-item {
@@ -1070,7 +1384,7 @@
1070
1384
  gap: 10px;
1071
1385
  flex-wrap: wrap;
1072
1386
  align-items: end;
1073
- margin-bottom: 10px;
1387
+ margin-bottom: 8px;
1074
1388
  padding-bottom: 2px;
1075
1389
  }
1076
1390
  .toolbar .field {
@@ -1079,8 +1393,8 @@
1079
1393
  .mono-block {
1080
1394
  border: 1px solid color-mix(in srgb, var(--border) 82%, transparent);
1081
1395
  border-radius: 12px;
1082
- background: linear-gradient(180deg, color-mix(in srgb, var(--bg-elevated) 96%, transparent), color-mix(in srgb, var(--panel) 92%, transparent));
1083
- padding: 10px;
1396
+ background: linear-gradient(180deg, color-mix(in srgb, var(--bg-elevated) 97%, transparent), color-mix(in srgb, var(--panel) 93%, transparent));
1397
+ padding: 9px;
1084
1398
  max-height: 240px;
1085
1399
  overflow: auto;
1086
1400
  white-space: pre-wrap;
@@ -1088,7 +1402,11 @@
1088
1402
  box-shadow: inset 0 1px 0 color-mix(in srgb, white 3%, transparent);
1089
1403
  }
1090
1404
  .advanced-panel {
1091
- margin-top: 8px;
1405
+ margin-top: 6px;
1406
+ }
1407
+ details.card,
1408
+ .advanced-panel.card {
1409
+ background: linear-gradient(180deg, color-mix(in srgb, var(--card-soft) 97%, transparent), color-mix(in srgb, var(--panel) 94%, transparent));
1092
1410
  }
1093
1411
  .advanced-panel.card > summary,
1094
1412
  details.card > summary {
@@ -1166,6 +1484,10 @@
1166
1484
  .nav { grid-template-columns: repeat(2, minmax(0,1fr)); }
1167
1485
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
1168
1486
  .split, .row, .profile-layout, .page-hero { grid-template-columns: 1fr; }
1487
+ .hero-copy {
1488
+ display: grid;
1489
+ gap: 4px;
1490
+ }
1169
1491
  .main {
1170
1492
  height: auto;
1171
1493
  overflow: visible;
@@ -1186,7 +1508,7 @@
1186
1508
  <div class="brand">
1187
1509
  <img id="brandLogo" class="brand-logo" src="/assets/silicaclaw-logo.png" alt="SilicaClaw logo" />
1188
1510
  <div id="brandFallback" class="brand-badge hidden">SC</div>
1189
- <div>
1511
+ <div class="brand-copy">
1190
1512
  <h1>SilicaClaw</h1>
1191
1513
  <p>Control UI</p>
1192
1514
  </div>
@@ -1202,9 +1524,11 @@
1202
1524
  </button>
1203
1525
  </div>
1204
1526
  <div class="sidebar-shell__body">
1205
- <div class="sidebar-nav__label">Control</div>
1206
- <nav class="nav">
1207
- <button class="tab active" data-tab="overview">
1527
+ <nav class="sidebar-nav nav">
1528
+ <section class="nav-section">
1529
+ <div class="nav-section__label">Control</div>
1530
+ <div class="nav-section__items">
1531
+ <button class="tab nav-item active" data-tab="overview">
1208
1532
  <span class="tab-icon" aria-hidden="true">
1209
1533
  <svg viewBox="0 0 24 24">
1210
1534
  <line x1="12" x2="12" y1="20" y2="10"></line>
@@ -1214,7 +1538,7 @@
1214
1538
  </span>
1215
1539
  <span class="tab-labels"><span class="tab-title">Overview</span><span class="tab-copy">Agent summary and discovered peers</span></span>
1216
1540
  </button>
1217
- <button class="tab" data-tab="profile">
1541
+ <button class="tab nav-item" data-tab="profile">
1218
1542
  <span class="tab-icon" aria-hidden="true">
1219
1543
  <svg viewBox="0 0 24 24">
1220
1544
  <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
@@ -1226,7 +1550,7 @@
1226
1550
  </span>
1227
1551
  <span class="tab-labels"><span class="tab-title">Profile</span><span class="tab-copy">Public identity and saved profile</span></span>
1228
1552
  </button>
1229
- <button class="tab" data-tab="network">
1553
+ <button class="tab nav-item" data-tab="network">
1230
1554
  <span class="tab-icon" aria-hidden="true">
1231
1555
  <svg viewBox="0 0 24 24">
1232
1556
  <circle cx="12" cy="12" r="2"></circle>
@@ -1235,7 +1559,7 @@
1235
1559
  </span>
1236
1560
  <span class="tab-labels"><span class="tab-title">Network</span><span class="tab-copy">Relay health, broadcast, diagnostics</span></span>
1237
1561
  </button>
1238
- <button class="tab" data-tab="social">
1562
+ <button class="tab nav-item" data-tab="social">
1239
1563
  <span class="tab-icon" aria-hidden="true">
1240
1564
  <svg viewBox="0 0 24 24">
1241
1565
  <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
@@ -1243,11 +1567,17 @@
1243
1567
  </span>
1244
1568
  <span class="tab-labels"><span class="tab-title">Social</span><span class="tab-copy">social.md config and runtime state</span></span>
1245
1569
  </button>
1570
+ </div>
1571
+ </section>
1246
1572
  </nav>
1247
1573
  </div>
1248
1574
  <div class="sidebar-shell__footer">
1249
1575
  <div class="sidebar-version" title="Current version">
1250
- <span class="sidebar-version__text" id="brandVersion">-</span>
1576
+ <div class="sidebar-version__copy">
1577
+ <span class="sidebar-version__label">Version</span>
1578
+ <span class="sidebar-version__text" id="brandVersion">-</span>
1579
+ </div>
1580
+ <span class="sidebar-version__status" id="brandStatusDot" aria-hidden="true"></span>
1251
1581
  </div>
1252
1582
  </div>
1253
1583
  </div>
@@ -1255,60 +1585,64 @@
1255
1585
 
1256
1586
  <main class="main">
1257
1587
  <div class="topbar">
1258
- <div class="topnav-shell__content">
1259
- <div class="dashboard-header__breadcrumb">
1260
- <span class="dashboard-header__breadcrumb-link">SilicaClaw</span>
1261
- <span class="dashboard-header__breadcrumb-sep">/</span>
1262
- <span class="dashboard-header__breadcrumb-current" id="pageBreadcrumb">Overview</span>
1588
+ <div class="topnav-shell">
1589
+ <div class="topnav-shell__content">
1590
+ <div class="dashboard-header__breadcrumb">
1591
+ <span class="dashboard-header__breadcrumb-link">SilicaClaw</span>
1592
+ <span class="dashboard-header__breadcrumb-sep">/</span>
1593
+ <span class="dashboard-header__breadcrumb-current" id="pageBreadcrumb">Overview</span>
1594
+ </div>
1263
1595
  </div>
1264
- <h2 id="topbarTitle">Local Console</h2>
1265
- <p id="topbarSubtitle">OpenClaw-inspired local-first agent control surface</p>
1266
- </div>
1267
- <div class="status-row">
1268
- <div class="pill" id="pillAdapter">adapter: -</div>
1269
- <div class="pill" id="pillBroadcast">broadcast: -</div>
1270
- <div class="topbar-actions">
1271
- <button class="icon-btn" id="focusModeBtn" type="button" title="Toggle focus mode" aria-label="Toggle focus mode">
1272
- <svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1273
- <path d="M4 7V4h3"></path>
1274
- <path d="M20 7V4h-3"></path>
1275
- <path d="M4 17v3h3"></path>
1276
- <path d="M20 17v3h-3"></path>
1277
- <circle cx="12" cy="12" r="3"></circle>
1278
- </svg>
1279
- </button>
1280
- <div class="topbar-theme-mode" id="themeModeGroup" role="group" aria-label="Color mode">
1281
- <button class="topbar-theme-mode__btn" data-theme-choice="dark" type="button" title="Dark" aria-label="Color mode: Dark">
1282
- <span class="theme-icon" aria-hidden="true">
1283
- <svg viewBox="0 0 24 24">
1284
- <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9z"></path>
1285
- </svg>
1286
- </span>
1287
- </button>
1288
- <button class="topbar-theme-mode__btn" data-theme-choice="light" type="button" title="Light" aria-label="Color mode: Light">
1289
- <span class="theme-icon" aria-hidden="true">
1290
- <svg viewBox="0 0 24 24">
1291
- <circle cx="12" cy="12" r="4"></circle>
1292
- <path d="M12 2v2"></path>
1293
- <path d="M12 20v2"></path>
1294
- <path d="M4.93 4.93l1.41 1.41"></path>
1295
- <path d="M17.66 17.66l1.41 1.41"></path>
1296
- <path d="M2 12h2"></path>
1297
- <path d="M20 12h2"></path>
1298
- <path d="M4.93 19.07l1.41-1.41"></path>
1299
- <path d="M17.66 6.34l1.41-1.41"></path>
1300
- </svg>
1301
- </span>
1302
- </button>
1303
- <button class="topbar-theme-mode__btn" data-theme-choice="system" type="button" title="System" aria-label="Color mode: System">
1304
- <span class="theme-icon" aria-hidden="true">
1305
- <svg viewBox="0 0 24 24">
1306
- <rect x="3" y="4" width="18" height="12" rx="2"></rect>
1307
- <path d="M8 20h8"></path>
1308
- <path d="M12 16v4"></path>
1596
+ <div class="topnav-shell__actions">
1597
+ <div class="topbar-status">
1598
+ <div class="status-row">
1599
+ <div class="pill" id="pillAdapter">adapter: -</div>
1600
+ <div class="pill" id="pillBroadcast">broadcast: -</div>
1601
+ </div>
1602
+ <div class="topbar-actions">
1603
+ <button class="icon-btn" id="focusModeBtn" type="button" title="Toggle focus mode" aria-label="Toggle focus mode">
1604
+ <svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1605
+ <path d="M4 7V4h3"></path>
1606
+ <path d="M20 7V4h-3"></path>
1607
+ <path d="M4 17v3h3"></path>
1608
+ <path d="M20 17v3h-3"></path>
1609
+ <circle cx="12" cy="12" r="3"></circle>
1309
1610
  </svg>
1310
- </span>
1311
- </button>
1611
+ </button>
1612
+ <div class="topbar-theme-mode" id="themeModeGroup" role="group" aria-label="Color mode">
1613
+ <button class="topbar-theme-mode__btn" data-theme-choice="dark" type="button" title="Dark" aria-label="Color mode: Dark">
1614
+ <span class="theme-icon" aria-hidden="true">
1615
+ <svg viewBox="0 0 24 24">
1616
+ <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9z"></path>
1617
+ </svg>
1618
+ </span>
1619
+ </button>
1620
+ <button class="topbar-theme-mode__btn" data-theme-choice="light" type="button" title="Light" aria-label="Color mode: Light">
1621
+ <span class="theme-icon" aria-hidden="true">
1622
+ <svg viewBox="0 0 24 24">
1623
+ <circle cx="12" cy="12" r="4"></circle>
1624
+ <path d="M12 2v2"></path>
1625
+ <path d="M12 20v2"></path>
1626
+ <path d="M4.93 4.93l1.41 1.41"></path>
1627
+ <path d="M17.66 17.66l1.41 1.41"></path>
1628
+ <path d="M2 12h2"></path>
1629
+ <path d="M20 12h2"></path>
1630
+ <path d="M4.93 19.07l1.41-1.41"></path>
1631
+ <path d="M17.66 6.34l1.41-1.41"></path>
1632
+ </svg>
1633
+ </span>
1634
+ </button>
1635
+ <button class="topbar-theme-mode__btn" data-theme-choice="system" type="button" title="System" aria-label="Color mode: System">
1636
+ <span class="theme-icon" aria-hidden="true">
1637
+ <svg viewBox="0 0 24 24">
1638
+ <rect x="3" y="4" width="18" height="12" rx="2"></rect>
1639
+ <path d="M8 20h8"></path>
1640
+ <path d="M12 16v4"></path>
1641
+ </svg>
1642
+ </span>
1643
+ </button>
1644
+ </div>
1645
+ </div>
1312
1646
  </div>
1313
1647
  </div>
1314
1648
  </div>
@@ -1348,12 +1682,18 @@
1348
1682
  <div class="grid" id="overviewCards"></div>
1349
1683
  <div class="split">
1350
1684
  <div class="card">
1351
- <h3 class="title-sm">Discovered Agents</h3>
1352
- <div class="field-hint" id="agentsCountHint">0 agents</div>
1353
- <label class="field-hint" style="display:inline-flex;gap:6px;align-items:center;margin:6px 0 8px;">
1354
- <input type="checkbox" id="onlyOnlineToggle" />
1355
- Only show online
1356
- </label>
1685
+ <div class="overview-panel-header">
1686
+ <div class="overview-panel-title">
1687
+ <h3 class="title-sm">Discovered Agents</h3>
1688
+ <div class="field-hint" id="agentsCountHint">0 agents</div>
1689
+ </div>
1690
+ <div class="overview-panel-controls">
1691
+ <label class="overview-inline-toggle">
1692
+ <input type="checkbox" id="onlyOnlineToggle" />
1693
+ <span>Only show online</span>
1694
+ </label>
1695
+ </div>
1696
+ </div>
1357
1697
  <div id="agentsWrap"></div>
1358
1698
  </div>
1359
1699
  <div class="card">
@@ -1760,6 +2100,9 @@
1760
2100
  onboardingNotice: 'Connected · mode={mode} · discoverable={discoverable} · next: update your profile, turn on Public Enabled, and export social.md',
1761
2101
  pillRunning: 'broadcast: running',
1762
2102
  pillPaused: 'broadcast: paused',
2103
+ pageStatus: 'Page {page} / {total}',
2104
+ prevPage: 'Prev',
2105
+ nextPage: 'Next',
1763
2106
  },
1764
2107
  preview: {
1765
2108
  unnamedAgent: '(unnamed agent)',
@@ -1916,7 +2259,7 @@
1916
2259
  body: '查看当前节点是否在线,以及还能看到哪些节点。',
1917
2260
  },
1918
2261
  profile: {
1919
- title: 'Profile',
2262
+ title: '资料',
1920
2263
  body: '编辑其他节点可见的公开名片。',
1921
2264
  },
1922
2265
  network: {
@@ -1930,7 +2273,7 @@
1930
2273
  },
1931
2274
  actions: {
1932
2275
  broadcastNow: '立即广播',
1933
- editProfile: '编辑 Profile',
2276
+ editProfile: '编辑资料',
1934
2277
  openNetwork: '打开网络页',
1935
2278
  startBroadcast: '启动广播',
1936
2279
  stopBroadcast: '停止广播',
@@ -1940,12 +2283,12 @@
1940
2283
  copyTemplate: '复制模板',
1941
2284
  downloadTemplate: '下载模板',
1942
2285
  applyRuntimeMode: '应用运行时模式',
1943
- saveProfile: '保存 Profile',
1944
- copyPublicProfilePreview: '复制公开 Profile 预览摘要',
2286
+ saveProfile: '保存资料',
2287
+ copyPublicProfilePreview: '复制公开资料预览摘要',
1945
2288
  },
1946
2289
  labels: {
1947
2290
  overviewTabCopy: '代理摘要与已发现节点',
1948
- profileTabCopy: '公开身份与已保存的 Profile',
2291
+ profileTabCopy: '公开身份与已保存资料',
1949
2292
  networkTabCopy: 'Relay 健康度、广播与诊断',
1950
2293
  socialTabCopy: 'social.md 配置与运行时状态',
1951
2294
  collapseSidebar: '折叠侧边栏',
@@ -1967,9 +2310,9 @@
1967
2310
  discoveredAgents: '已发现代理',
1968
2311
  onlyShowOnline: '只显示在线',
1969
2312
  nodeSnapshot: '节点快照',
1970
- profileEyebrow: 'Profile',
1971
- publicProfile: '公开 Profile',
1972
- publicProfileEditor: '公开 Profile 编辑器',
2313
+ profileEyebrow: '资料',
2314
+ publicProfile: '公开资料',
2315
+ publicProfileEditor: '公开资料编辑器',
1973
2316
  displayName: '显示名称',
1974
2317
  avatarUrl: '头像 URL',
1975
2318
  bio: '简介',
@@ -1978,7 +2321,7 @@
1978
2321
  livePreview: '实时预览',
1979
2322
  publicCard: '公开卡片',
1980
2323
  publishStatus: '发布状态',
1981
- publicProfilePreview: '公开 Profile 预览',
2324
+ publicProfilePreview: '公开资料预览',
1982
2325
  networkEyebrow: '网络',
1983
2326
  connectionSummary: '连接摘要',
1984
2327
  quickActions: '快捷操作',
@@ -2006,14 +2349,14 @@
2006
2349
  networkModeRuntime: '网络模式(运行时)',
2007
2350
  },
2008
2351
  hints: {
2009
- publicDiscoverySwitch: '使用 Profile -> Public Enabled 作为唯一的公开可见性开关。',
2352
+ publicDiscoverySwitch: '使用资料 -> Public Enabled 作为唯一的公开可见性开关。',
2010
2353
  discoverabilityRecommendation: '建议 2-32 个字符,以提升可发现性。',
2011
2354
  avatarOptional: '可选。如提供,必须是 http(s) URL。',
2012
2355
  tagsLimit: '最多 8 个标签,每个不超过 20 个字符。',
2013
2356
  publicEnabledHint: '这是发现与 relay 广播使用的主公开可见性开关。',
2014
- signedPublicProfileHint: '这是其他节点或 explorer 可以看到的已签名公开 Profile 视图。',
2357
+ signedPublicProfileHint: '这是其他节点或 explorer 可以看到的已签名公开资料视图。',
2015
2358
  useTheseFirst: '优先使用这些操作。',
2016
- profileVisibilityManaged: 'Profile 可见性在 Profile 页面管理。',
2359
+ profileVisibilityManaged: '资料可见性在资料页面管理。',
2017
2360
  checkingIntegration: '正在检查集成状态...',
2018
2361
  allIntegrationChecksPassed: '所有集成检查均已通过。',
2019
2362
  },
@@ -2036,9 +2379,12 @@
2036
2379
  tableStatus: '状态',
2037
2380
  tableUpdated: '更新时间',
2038
2381
  unnamed: '(未命名)',
2039
- onboardingNotice: '已连接 · mode={mode} · discoverable={discoverable} · 下一步:更新 Profile、开启 Public Enabled,并导出 social.md',
2382
+ onboardingNotice: '已连接 · mode={mode} · discoverable={discoverable} · 下一步:更新资料、开启 Public Enabled,并导出 social.md',
2040
2383
  pillRunning: 'broadcast: 运行中',
2041
2384
  pillPaused: 'broadcast: 已暂停',
2385
+ pageStatus: '第 {page} / {total} 页',
2386
+ prevPage: '上一页',
2387
+ nextPage: '下一页',
2042
2388
  },
2043
2389
  preview: {
2044
2390
  unnamedAgent: '(未命名代理)',
@@ -2131,9 +2477,9 @@
2131
2477
  unsavedChanges: '你有尚未保存的更改。',
2132
2478
  allChangesSaved: '所有更改都已保存。',
2133
2479
  fixValidation: '请先修复校验错误再保存。',
2134
- savingProfile: '正在保存 Profile...',
2135
- profileSaved: 'Profile 已保存',
2136
- profileReloaded: 'Profile 表单已重新加载',
2480
+ savingProfile: '正在保存资料...',
2481
+ profileSaved: '资料已保存',
2482
+ profileReloaded: '资料表单已重新加载',
2137
2483
  exportingTemplate: '正在导出模板...',
2138
2484
  templateExported: '已按当前运行时导出模板。',
2139
2485
  templateCopied: '模板已复制到剪贴板。',
@@ -2161,7 +2507,7 @@
2161
2507
  avatarProtocol: '头像 URL 必须以 http:// 或 https:// 开头',
2162
2508
  tooManyTags: '标签过多(最多 8 个)。',
2163
2509
  tagTooLong: '每个标签必须不超过 20 个字符。',
2164
- leaveConfirm: '你有未保存的 Profile 更改,仍要离开吗?',
2510
+ leaveConfirm: '你有未保存的资料更改,仍要离开吗?',
2165
2511
  },
2166
2512
  },
2167
2513
  };
@@ -2217,9 +2563,11 @@
2217
2563
  document.getElementById('sidebarToggleBtn').title = t('labels.collapseSidebar');
2218
2564
  document.getElementById('sidebarToggleBtn').setAttribute('aria-label', t('labels.collapseSidebar'));
2219
2565
  document.querySelector('.sidebar-version').title = t('common.version');
2566
+ setText('.sidebar-version__label', t('common.version'));
2220
2567
  setText('.dashboard-header__breadcrumb-current', t('pageMeta.overview.title'));
2221
- document.getElementById('topbarTitle').textContent = t('common.localConsole');
2222
- document.getElementById('topbarSubtitle').textContent = t('common.subtitle');
2568
+ document.getElementById('pageBreadcrumb').textContent = t('pageMeta.overview.title');
2569
+ document.getElementById('pageHeroTitle').textContent = t('pageMeta.overview.title');
2570
+ document.getElementById('pageHeroBody').textContent = t('pageMeta.overview.body');
2223
2571
  document.getElementById('focusModeBtn').title = t('labels.toggleFocusMode');
2224
2572
  document.getElementById('focusModeBtn').setAttribute('aria-label', t('labels.toggleFocusMode'));
2225
2573
  document.getElementById('themeModeGroup').setAttribute('aria-label', t('labels.colorMode'));
@@ -2317,6 +2665,8 @@
2317
2665
  let logLevelFilter = 'all';
2318
2666
  let socialTemplate = '';
2319
2667
  let onlyShowOnline = false;
2668
+ let agentsPage = 1;
2669
+ const AGENTS_PAGE_SIZE = 10;
2320
2670
  const pageMeta = TRANSLATIONS[currentLocale].pageMeta || TRANSLATIONS[DEFAULT_LOCALE].pageMeta;
2321
2671
 
2322
2672
  function ago(ts) {
@@ -2330,6 +2680,14 @@
2330
2680
  if (!id) return '-';
2331
2681
  return `${id.slice(0, 10)}...${id.slice(-6)}`;
2332
2682
  }
2683
+ function escapeHtml(value) {
2684
+ return String(value ?? '')
2685
+ .replace(/&/g, '&amp;')
2686
+ .replace(/</g, '&lt;')
2687
+ .replace(/>/g, '&gt;')
2688
+ .replace(/"/g, '&quot;')
2689
+ .replace(/'/g, '&#39;');
2690
+ }
2333
2691
  function toPrettyJson(obj) {
2334
2692
  try {
2335
2693
  return JSON.stringify(obj, null, 2);
@@ -2337,6 +2695,48 @@
2337
2695
  return String(obj);
2338
2696
  }
2339
2697
  }
2698
+ function readUiCache(key) {
2699
+ try {
2700
+ const raw = localStorage.getItem(key);
2701
+ return raw ? JSON.parse(raw) : null;
2702
+ } catch {
2703
+ return null;
2704
+ }
2705
+ }
2706
+ function writeUiCache(key, value) {
2707
+ try {
2708
+ localStorage.setItem(key, JSON.stringify(value));
2709
+ } catch {
2710
+ // ignore cache write failures
2711
+ }
2712
+ }
2713
+ function hydrateCachedShell() {
2714
+ const overview = readUiCache('silicaclaw_ui_overview');
2715
+ if (overview) {
2716
+ if (overview.overviewCardsHtml) document.getElementById('overviewCards').innerHTML = overview.overviewCardsHtml;
2717
+ if (overview.brandVersionText) document.getElementById('brandVersion').textContent = overview.brandVersionText;
2718
+ if (overview.snapshotText) document.getElementById('snapshot').innerHTML = overview.snapshotText;
2719
+ if (overview.heroModeText) document.getElementById('heroMode').textContent = overview.heroModeText;
2720
+ if (overview.pillBroadcastText) document.getElementById('pillBroadcast').textContent = overview.pillBroadcastText;
2721
+ if (overview.pillBroadcastClassName) document.getElementById('pillBroadcast').className = overview.pillBroadcastClassName;
2722
+ if (overview.agentsCountHintText) document.getElementById('agentsCountHint').textContent = overview.agentsCountHintText;
2723
+ if (overview.agentsWrapHtml) document.getElementById('agentsWrap').innerHTML = overview.agentsWrapHtml;
2724
+ }
2725
+ const network = readUiCache('silicaclaw_ui_network');
2726
+ if (network) {
2727
+ if (network.heroAdapterText) document.getElementById('heroAdapter').textContent = network.heroAdapterText;
2728
+ if (network.heroRelayText) document.getElementById('heroRelay').textContent = network.heroRelayText;
2729
+ if (network.heroRoomText) document.getElementById('heroRoom').textContent = network.heroRoomText;
2730
+ if (network.pillAdapterText) document.getElementById('pillAdapter').textContent = network.pillAdapterText;
2731
+ }
2732
+ const social = readUiCache('silicaclaw_ui_social');
2733
+ if (social) {
2734
+ if (social.integrationStatusText) document.getElementById('integrationStatusBar').textContent = social.integrationStatusText;
2735
+ if (social.integrationStatusClassName) document.getElementById('integrationStatusBar').className = social.integrationStatusClassName;
2736
+ if (social.socialStatusLineText) document.getElementById('socialStatusLine').textContent = social.socialStatusLineText;
2737
+ if (social.socialStatusSublineText) document.getElementById('socialStatusSubline').textContent = social.socialStatusSublineText;
2738
+ }
2739
+ }
2340
2740
  function toast(msg) {
2341
2741
  const t = document.getElementById('toast');
2342
2742
  t.textContent = msg;
@@ -2519,31 +2919,98 @@
2519
2919
  }
2520
2920
 
2521
2921
  async function refreshOverview() {
2522
- const [overview, discovered] = await Promise.all([api('/api/overview'), api('/api/search?q=')]);
2523
- const o = overview.data;
2524
- const all = discovered.data || [];
2525
- const d = onlyShowOnline ? all.filter((agent) => agent.online) : all;
2922
+ const [overviewRes, discoveredRes, peerRes] = await Promise.allSettled([
2923
+ api('/api/overview'),
2924
+ api('/api/search?q='),
2925
+ api('/api/peers'),
2926
+ ]);
2927
+ if (overviewRes.status !== 'fulfilled') {
2928
+ throw overviewRes.reason;
2929
+ }
2930
+ const o = overviewRes.value.data;
2931
+ const allProfiles = discoveredRes.status === 'fulfilled' ? (discoveredRes.value.data || []) : [];
2932
+ const peers = peerRes.status === 'fulfilled' ? (peerRes.value.data || {}) : {};
2933
+ const peerItems = peers.items || [];
2934
+ const mergedById = new Map();
2935
+
2936
+ for (const agent of allProfiles) {
2937
+ mergedById.set(agent.agent_id, agent);
2938
+ }
2939
+
2940
+ for (const peer of peerItems) {
2941
+ const existing = mergedById.get(peer.peer_id);
2942
+ if (existing) {
2943
+ if (peer.status && !existing.online) {
2944
+ existing.online = peer.status === 'online';
2945
+ }
2946
+ if (peer.last_seen_at && (!existing.updated_at || peer.last_seen_at > existing.updated_at)) {
2947
+ existing.updated_at = peer.last_seen_at;
2948
+ }
2949
+ continue;
2950
+ }
2951
+ mergedById.set(peer.peer_id, {
2952
+ agent_id: peer.peer_id,
2953
+ display_name: shortId(peer.peer_id),
2954
+ online: peer.status === 'online',
2955
+ updated_at: peer.last_seen_at || peer.first_seen_at || 0,
2956
+ });
2957
+ }
2526
2958
 
2527
- document.getElementById('overviewCards').innerHTML = [
2528
- [t('overview.discovered'), o.discovered_count],
2529
- [t('overview.online'), o.online_count],
2530
- [t('overview.offline'), o.offline_count],
2959
+ const all = Array.from(mergedById.values());
2960
+ const d = onlyShowOnline ? all.filter((agent) => agent.online) : all;
2961
+ const totalAgentPages = Math.max(1, Math.ceil(d.length / AGENTS_PAGE_SIZE));
2962
+ agentsPage = Math.min(Math.max(1, agentsPage), totalAgentPages);
2963
+ const pagedAgents = d.slice((agentsPage - 1) * AGENTS_PAGE_SIZE, agentsPage * AGENTS_PAGE_SIZE);
2964
+ const discoveredCount = Math.max(Number(o.discovered_count || 0), Number(peers.total || 0), all.length);
2965
+ const onlineCount = Math.max(Number(o.online_count || 0), Number(peers.online || 0), all.filter((agent) => agent.online).length);
2966
+ const offlineCount = Math.max(0, discoveredCount - onlineCount);
2967
+
2968
+ const overviewCardsHtml = [
2969
+ [t('overview.discovered'), discoveredCount],
2970
+ [t('overview.online'), onlineCount],
2971
+ [t('overview.offline'), offlineCount],
2531
2972
  [t('overview.presenceTtl'), `${Math.floor(o.presence_ttl_ms / 1000)}s`],
2532
2973
  ].map(([k,v]) => `<div class="card"><div class="label">${k}</div><div class="value">${v}</div></div>`).join('');
2974
+ document.getElementById('overviewCards').innerHTML = overviewCardsHtml;
2533
2975
 
2534
- document.getElementById('brandVersion').textContent = o.app_version ? `v${o.app_version}` : '-';
2976
+ const brandVersionText = o.app_version ? `v${o.app_version}` : '-';
2977
+ document.getElementById('brandVersion').textContent = brandVersionText;
2535
2978
 
2536
- document.getElementById('snapshot').textContent = [
2537
- `app_version: ${o.app_version || '-'}`,
2538
- `agent_id: ${o.agent_id || '-'}`,
2539
- `public_enabled: ${o.public_enabled}`,
2540
- `broadcast_enabled: ${o.broadcast_enabled}`,
2541
- `last_broadcast: ${ago(o.last_broadcast_at)}`,
2542
- ].join('\n');
2543
- document.getElementById('heroMode').textContent = o.social?.network_mode || '-';
2979
+ const snapshotHtml = `
2980
+ <div class="snapshot-card">
2981
+ <div class="snapshot-card__identity">
2982
+ <div class="snapshot-card__label">Current Node</div>
2983
+ <div class="snapshot-card__title">${escapeHtml(o.display_name || t('overview.unnamed'))}</div>
2984
+ <div class="snapshot-card__subtle mono">${escapeHtml(o.agent_id || '-')}</div>
2985
+ </div>
2986
+ <div class="snapshot-card__grid">
2987
+ <div class="snapshot-card__item">
2988
+ <div class="label">Version</div>
2989
+ <span class="value-inline">${escapeHtml(o.app_version || '-')}</span>
2990
+ </div>
2991
+ <div class="snapshot-card__item">
2992
+ <div class="label">Public</div>
2993
+ <span class="value-inline">${o.public_enabled ? t('common.on') : t('common.off')}</span>
2994
+ </div>
2995
+ <div class="snapshot-card__item">
2996
+ <div class="label">Broadcast</div>
2997
+ <span class="value-inline">${o.broadcast_enabled ? t('common.on') : t('common.off')}</span>
2998
+ </div>
2999
+ <div class="snapshot-card__item">
3000
+ <div class="label">Last Broadcast</div>
3001
+ <span class="value-inline">${escapeHtml(ago(o.last_broadcast_at))}</span>
3002
+ </div>
3003
+ </div>
3004
+ </div>
3005
+ `;
3006
+ document.getElementById('snapshot').innerHTML = snapshotHtml;
3007
+ const heroModeText = o.social?.network_mode || '-';
3008
+ document.getElementById('heroMode').textContent = heroModeText;
2544
3009
 
2545
- document.getElementById('pillBroadcast').textContent = o.broadcast_enabled ? t('overview.pillRunning') : t('overview.pillPaused');
2546
- document.getElementById('pillBroadcast').className = `pill ${o.broadcast_enabled ? 'ok' : 'warn'}`;
3010
+ const pillBroadcastText = o.broadcast_enabled ? t('overview.pillRunning') : t('overview.pillPaused');
3011
+ const pillBroadcastClassName = `pill ${o.broadcast_enabled ? 'ok' : 'warn'}`;
3012
+ document.getElementById('pillBroadcast').textContent = pillBroadcastText;
3013
+ document.getElementById('pillBroadcast').className = pillBroadcastClassName;
2547
3014
 
2548
3015
  const init = o.init_state || {};
2549
3016
  const onboarding = o.onboarding || {};
@@ -2566,27 +3033,88 @@
2566
3033
  }
2567
3034
 
2568
3035
  if (!d.length) {
2569
- document.getElementById('agentsCountHint').textContent = t('overview.agentsZero');
2570
- document.getElementById('agentsWrap').innerHTML = `<div class="label">${t('overview.noDiscoveredAgents')}</div>`;
3036
+ const agentsCountHintText = t('overview.agentsZero');
3037
+ const agentsWrapHtml = `<div class="label">${t('overview.noDiscoveredAgents')}</div>`;
3038
+ document.getElementById('agentsCountHint').textContent = agentsCountHintText;
3039
+ document.getElementById('agentsWrap').innerHTML = agentsWrapHtml;
3040
+ writeUiCache('silicaclaw_ui_overview', {
3041
+ overviewCardsHtml,
3042
+ brandVersionText,
3043
+ snapshotText: snapshotHtml,
3044
+ heroModeText,
3045
+ pillBroadcastText,
3046
+ pillBroadcastClassName,
3047
+ agentsCountHintText,
3048
+ agentsWrapHtml,
3049
+ });
2571
3050
  } else {
2572
- document.getElementById('agentsCountHint').textContent = onlyShowOnline
3051
+ const agentsCountHintText = onlyShowOnline
2573
3052
  ? t('overview.agentsOnlineFilter', { shown: String(d.length), total: String(all.length) })
2574
3053
  : t('overview.agentsDiscovered', { count: String(d.length) });
2575
- document.getElementById('agentsWrap').innerHTML = `
2576
- <table class="table">
2577
- <thead><tr><th>${t('overview.tableName')}</th><th>${t('overview.tableAgentId')}</th><th>${t('overview.tableStatus')}</th><th>${t('overview.tableUpdated')}</th></tr></thead>
2578
- <tbody>
2579
- ${d.map((a) => `
2580
- <tr>
2581
- <td>${a.display_name || t('overview.unnamed')}</td>
2582
- <td class="mono">${shortId(a.agent_id)}</td>
2583
- <td class="${a.online ? 'online' : 'offline'}">${a.online ? t('overview.online') : t('overview.offline')}</td>
2584
- <td>${ago(a.updated_at)}</td>
2585
- </tr>
2586
- `).join('')}
2587
- </tbody>
2588
- </table>
3054
+ const renderAvatar = (agent) => {
3055
+ const avatar = String(agent.avatar_url || '').trim();
3056
+ if (avatar) {
3057
+ return `<img class="agent-card__avatar" src="${avatar}" alt="${escapeHtml(agent.display_name || 'agent')}" loading="lazy" />`;
3058
+ }
3059
+ const label = String(agent.display_name || agent.agent_id || '?').trim();
3060
+ const initial = escapeHtml((label[0] || '?').toUpperCase());
3061
+ return `<div class="agent-card__avatar-fallback">${initial}</div>`;
3062
+ };
3063
+ const renderTags = (agent) => {
3064
+ const tags = Array.isArray(agent.tags) ? agent.tags.slice(0, 4) : [];
3065
+ if (!tags.length) return '';
3066
+ return `<div class="agent-card__tags">${tags.map((tag) => `<span class="tag-chip">${escapeHtml(String(tag))}</span>`).join('')}</div>`;
3067
+ };
3068
+ const agentsWrapHtml = `
3069
+ <div class="agent-list">
3070
+ ${pagedAgents.map((a) => `
3071
+ <div class="agent-card">
3072
+ ${renderAvatar(a)}
3073
+ <div class="agent-card__main">
3074
+ <div class="agent-card__row">
3075
+ <div class="agent-card__name">${escapeHtml(a.display_name || t('overview.unnamed'))}</div>
3076
+ <div class="agent-card__id mono">${shortId(a.agent_id)}</div>
3077
+ </div>
3078
+ <div class="agent-card__bio">${escapeHtml(a.bio || t('preview.noBioYet'))}</div>
3079
+ ${renderTags(a)}
3080
+ </div>
3081
+ <div class="${a.online ? 'online' : 'offline'}">${a.online ? t('overview.online') : t('overview.offline')}</div>
3082
+ <div class="agent-card__meta">
3083
+ <div class="agent-card__updated">${ago(a.updated_at)}</div>
3084
+ </div>
3085
+ </div>
3086
+ `).join('')}
3087
+ <div class="agent-list__footer">
3088
+ <div class="agent-list__page">${t('overview.pageStatus', { page: String(agentsPage), total: String(totalAgentPages) })}</div>
3089
+ <div class="agent-list__pager">
3090
+ <button class="secondary" type="button" id="agentsPrevPageBtn" ${agentsPage <= 1 ? 'disabled' : ''}>${t('overview.prevPage')}</button>
3091
+ <button class="secondary" type="button" id="agentsNextPageBtn" ${agentsPage >= totalAgentPages ? 'disabled' : ''}>${t('overview.nextPage')}</button>
3092
+ </div>
3093
+ </div>
3094
+ </div>
2589
3095
  `;
3096
+ document.getElementById('agentsCountHint').textContent = agentsCountHintText;
3097
+ document.getElementById('agentsWrap').innerHTML = agentsWrapHtml;
3098
+ document.getElementById('agentsPrevPageBtn')?.addEventListener('click', async () => {
3099
+ if (agentsPage <= 1) return;
3100
+ agentsPage -= 1;
3101
+ await refreshOverview();
3102
+ });
3103
+ document.getElementById('agentsNextPageBtn')?.addEventListener('click', async () => {
3104
+ if (agentsPage >= totalAgentPages) return;
3105
+ agentsPage += 1;
3106
+ await refreshOverview();
3107
+ });
3108
+ writeUiCache('silicaclaw_ui_overview', {
3109
+ overviewCardsHtml,
3110
+ brandVersionText,
3111
+ snapshotText: snapshotHtml,
3112
+ heroModeText,
3113
+ pillBroadcastText,
3114
+ pillBroadcastClassName,
3115
+ agentsCountHintText,
3116
+ agentsWrapHtml,
3117
+ });
2590
3118
  }
2591
3119
  }
2592
3120
 
@@ -2627,7 +3155,7 @@
2627
3155
  const msg = s.message_counters || {};
2628
3156
  const p = s.peer_counters || {};
2629
3157
  const a = s.adapter_stats || {};
2630
- const t = s.adapter_transport_stats || {};
3158
+ const transportStats = s.adapter_transport_stats || {};
2631
3159
  const d = s.adapter_discovery_stats || {};
2632
3160
  const dx = s.adapter_diagnostics_summary || {};
2633
3161
  const ac = s.adapter_config || c.adapter_config || {};
@@ -2636,6 +3164,12 @@
2636
3164
  document.getElementById('heroRoom').textContent = dx.room || '-';
2637
3165
 
2638
3166
  document.getElementById('pillAdapter').textContent = `adapter: ${c.adapter}`;
3167
+ writeUiCache('silicaclaw_ui_network', {
3168
+ heroAdapterText: c.adapter || '-',
3169
+ heroRelayText: dx.signaling_url || '-',
3170
+ heroRoomText: dx.room || '-',
3171
+ pillAdapterText: `adapter: ${c.adapter}`,
3172
+ });
2639
3173
  document.getElementById('networkCards').innerHTML = [
2640
3174
  [t('labels.adapter'), c.adapter],
2641
3175
  [t('labels.namespace'), c.namespace || '-'],
@@ -2689,7 +3223,7 @@
2689
3223
  `dropped_namespace_mismatch: ${a.dropped_namespace_mismatch ?? '-'}`,
2690
3224
  `received_validated: ${a.received_validated ?? '-'}`,
2691
3225
  `send_errors: ${a.send_errors ?? '-'}`,
2692
- `transport_send_errors: ${t.send_errors ?? '-'}`,
3226
+ `transport_send_errors: ${transportStats.send_errors ?? '-'}`,
2693
3227
  `discovery_heartbeat_send_errors: ${d.heartbeat_send_errors ?? '-'}`,
2694
3228
  `signaling_messages_sent_total: ${dx.signaling_messages_sent_total ?? '-'}`,
2695
3229
  `signaling_messages_received_total: ${dx.signaling_messages_received_total ?? '-'}`,
@@ -2811,20 +3345,35 @@
2811
3345
  }
2812
3346
 
2813
3347
  async function refreshSocial() {
2814
- const [socialRes, summaryRes, statusRes] = await Promise.all([
3348
+ const [socialRes, summaryRes, statusRes, networkCfgRes] = await Promise.all([
2815
3349
  api('/api/social/config'),
2816
3350
  api('/api/social/integration-summary'),
2817
3351
  api('/api/integration/status'),
3352
+ api('/api/network/config'),
2818
3353
  ]);
2819
3354
  const social = socialRes.data || {};
2820
3355
  const summary = summaryRes.data || {};
2821
3356
  const status = statusRes.data || {};
3357
+ const networkCfg = networkCfgRes.data || {};
2822
3358
  const runtime = social.runtime || {};
2823
3359
  const config = social.social_config || {};
2824
3360
  const network = config.network || {};
2825
3361
  const runtimeNetwork = runtime.resolved_network || {};
3362
+ const effectiveAdapterExtra = networkCfg.adapter_extra || {};
3363
+ const effectiveMode = networkCfg.mode || status.network_mode || summary.current_network_mode || runtimeNetwork.mode || network.mode || '-';
3364
+ const effectiveAdapter = networkCfg.adapter || runtimeNetwork.adapter || summary.current_adapter || '-';
3365
+ const effectiveNamespace = networkCfg.namespace || runtimeNetwork.namespace || summary.current_namespace || '-';
3366
+ const effectiveRoom = effectiveAdapterExtra.room || runtimeNetwork.room || network.room || '-';
3367
+ const effectiveRelay = effectiveAdapterExtra.signaling_url || runtimeNetwork.signaling_url || network.signaling_url || '-';
3368
+ const effectiveSignalingEndpoints = effectiveAdapterExtra.signaling_endpoints || runtimeNetwork.signaling_urls || network.signaling_urls || [];
3369
+ const effectiveBootstrapSources = effectiveAdapterExtra.bootstrap_sources || runtimeNetwork.bootstrap_sources || [];
3370
+ const effectiveSeedPeersCount = typeof effectiveAdapterExtra.seed_peers_count === 'number'
3371
+ ? effectiveAdapterExtra.seed_peers_count
3372
+ : Array.isArray(runtimeNetwork.seed_peers)
3373
+ ? runtimeNetwork.seed_peers.length
3374
+ : 0;
2826
3375
  const discoverable = status.discoverable === true;
2827
- const mode = status.network_mode || summary.current_network_mode || network.mode || runtimeNetwork.mode || '-';
3376
+ const mode = effectiveMode;
2828
3377
  const summaryLine = status.status_line || summary.summary_line || `${summary.connected ? t('social.connectedToSilicaClaw') : t('social.notConnectedToSilicaClaw')} · ${discoverable ? t('social.discoverableInCurrentMode') : t('social.notDiscoverableInCurrentMode')} · ${t('social.usingMode', { mode })}`;
2829
3378
  const publicDiscoveryText = status.public_enabled ? t('social.publicDiscoveryEnabled') : t('social.publicDiscoveryDisabled');
2830
3379
 
@@ -2838,6 +3387,14 @@
2838
3387
  mode,
2839
3388
  public: status.public_enabled ? t('common.on') : t('common.off'),
2840
3389
  });
3390
+ const brandStatusDot = document.getElementById('brandStatusDot');
3391
+ brandStatusDot.className = `sidebar-version__status ${status.connected_to_silicaclaw ? 'ok' : 'warn'}`;
3392
+ writeUiCache('silicaclaw_ui_social', {
3393
+ integrationStatusText: bar.textContent,
3394
+ integrationStatusClassName: bar.className,
3395
+ socialStatusLineText: summaryLine,
3396
+ socialStatusSublineText: namespaceText,
3397
+ });
2841
3398
  const reasons = [];
2842
3399
  if (!status.configured && status.configured_reason) reasons.push(t('social.configuredReason', { reason: status.configured_reason }));
2843
3400
  if (!status.running && status.running_reason) reasons.push(t('social.runningReason', { reason: status.running_reason }));
@@ -2870,17 +3427,27 @@
2870
3427
  ].map(([k,v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join('');
2871
3428
 
2872
3429
  document.getElementById('socialAdvancedCards').innerHTML = [
2873
- [t('labels.adapter'), runtimeNetwork.adapter || summary.current_adapter || '-'],
2874
- [t('social.namespace'), runtimeNetwork.namespace || summary.current_namespace || '-'],
2875
- [t('labels.room'), runtimeNetwork.room || network.room || '-'],
2876
- [t('network.signalingEndpoints'), (runtimeNetwork.signaling_urls || []).length || 0],
2877
- [t('network.bootstrapSources'), (runtimeNetwork.bootstrap_sources || []).length || 0],
2878
- [t('network.seedPeers'), (runtimeNetwork.seed_peers || []).length || 0],
3430
+ [t('labels.adapter'), effectiveAdapter],
3431
+ [t('social.namespace'), effectiveNamespace],
3432
+ [t('labels.room'), effectiveRoom],
3433
+ [t('network.signalingEndpoints'), effectiveSignalingEndpoints.length || 0],
3434
+ [t('network.bootstrapSources'), effectiveBootstrapSources.length || 0],
3435
+ [t('network.seedPeers'), effectiveSeedPeersCount],
2879
3436
  [t('social.bootstrapHints'), (runtimeNetwork.bootstrap_hints || []).length || 0],
2880
3437
  [t('social.restartRequired'), social.network_requires_restart ? t('common.yes') : t('common.no')],
2881
3438
  ].map(([k,v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join('');
2882
3439
  document.getElementById('socialAdvancedWrap').textContent = toPrettyJson({
2883
3440
  runtime_network: runtimeNetwork,
3441
+ effective_network: {
3442
+ mode: effectiveMode,
3443
+ adapter: effectiveAdapter,
3444
+ namespace: effectiveNamespace,
3445
+ relay: effectiveRelay,
3446
+ room: effectiveRoom,
3447
+ signaling_endpoints: effectiveSignalingEndpoints,
3448
+ bootstrap_sources: effectiveBootstrapSources,
3449
+ seed_peers_count: effectiveSeedPeersCount,
3450
+ },
2884
3451
  });
2885
3452
 
2886
3453
  document.getElementById('socialSourceWrap').textContent = toPrettyJson({
@@ -2928,18 +3495,18 @@
2928
3495
  }
2929
3496
 
2930
3497
  async function refreshAll() {
2931
- try {
2932
- const tasks = [refreshOverview(), refreshNetwork(), refreshSocial(), refreshPublicProfilePreview()];
2933
- if (activeTab === 'network') {
2934
- tasks.push(refreshPeers(), refreshDiscovery(), refreshLogs());
2935
- }
2936
- const shouldRefreshProfile = !(activeTab === 'profile' && profileDirty);
2937
- if (shouldRefreshProfile) {
2938
- tasks.push(refreshProfile());
2939
- }
2940
- await Promise.all(tasks);
2941
- } catch (e) {
2942
- setFeedback('networkFeedback', e instanceof Error ? e.message : t('common.unknownError'), 'error');
3498
+ const tasks = [refreshOverview(), refreshNetwork(), refreshSocial(), refreshPublicProfilePreview()];
3499
+ if (activeTab === 'network') {
3500
+ tasks.push(refreshPeers(), refreshDiscovery(), refreshLogs());
3501
+ }
3502
+ const shouldRefreshProfile = !(activeTab === 'profile' && profileDirty);
3503
+ if (shouldRefreshProfile) {
3504
+ tasks.push(refreshProfile());
3505
+ }
3506
+ const results = await Promise.allSettled(tasks);
3507
+ const firstError = results.find((result) => result.status === 'rejected');
3508
+ if (firstError && firstError.status === 'rejected') {
3509
+ setFeedback('networkFeedback', firstError.reason instanceof Error ? firstError.reason.message : t('common.unknownError'), 'error');
2943
3510
  }
2944
3511
  }
2945
3512
 
@@ -3093,6 +3660,7 @@
3093
3660
  });
3094
3661
  document.getElementById('onlyOnlineToggle').addEventListener('change', async (event) => {
3095
3662
  onlyShowOnline = Boolean(event.target?.checked);
3663
+ agentsPage = 1;
3096
3664
  await refreshOverview();
3097
3665
  });
3098
3666
  document.getElementById('refreshLogsBtn').addEventListener('click', async () => {
@@ -3204,6 +3772,7 @@
3204
3772
  }
3205
3773
 
3206
3774
  applyTheme(localStorage.getItem('silicaclaw_theme_mode') || 'dark');
3775
+ hydrateCachedShell();
3207
3776
  refreshAll();
3208
3777
  exportSocialTemplate().catch(() => {});
3209
3778
  setInterval(refreshAll, 4000);