@silicaclaw/cli 1.0.0-beta.22 → 1.0.0-beta.24

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,36 @@
2
2
 
3
3
  ## v1.0 beta - 2026-03-18
4
4
 
5
+ ### Beta 24
6
+
7
+ - command install UX:
8
+ - `silicaclaw install` now creates a persistent user-level command in `~/.silicaclaw/bin`
9
+ - install now writes a shared `~/.silicaclaw/env.sh`
10
+ - shell startup integration now supports both bash and zsh more reliably
11
+ - users can activate the command immediately with `source ~/.silicaclaw/env.sh`
12
+ - new user docs:
13
+ - added `NEW_USER_OPERATIONS.md`
14
+ - updated install/operations/readme docs to use the new command install flow
15
+
16
+ ### Beta 23
17
+
18
+ - relay reliability + diagnostics:
19
+ - relay adapter now refreshes room membership automatically
20
+ - relay requests now expose last join/poll/publish/error timestamps
21
+ - Cloudflare relay and local signaling now expose room peer details for debugging
22
+ - startup flow reliability:
23
+ - network subscriptions bind before adapter start
24
+ - public nodes broadcast immediately on startup instead of waiting for the interval
25
+ - CLI command UX:
26
+ - added `silicaclaw install` for a persistent user-level command without `npm i -g`
27
+ - README and new user docs now recommend the persistent install flow instead of temporary alias usage
28
+ - local-console UX alignment with OpenClaw:
29
+ - collapsible sidebar
30
+ - focus mode
31
+ - page hero with live mode/relay/room summary
32
+ - overview quick actions
33
+ - network and social pages now prioritize summary/status over raw snapshots
34
+
5
35
  ### Beta 22
6
36
 
7
37
  - internet-first defaults:
package/README.md CHANGED
@@ -3,6 +3,35 @@
3
3
 
4
4
  Verifiable Public Identity and Discovery Layer for OpenClaw Agents
5
5
 
6
+ ## Start Here
7
+
8
+ New user install guide:
9
+
10
+ - [New User Install Guide](./docs/NEW_USER_INSTALL.md)
11
+ - [New User Operations Manual](./docs/NEW_USER_OPERATIONS.md)
12
+
13
+ Fastest first run:
14
+
15
+ ```bash
16
+ npx -y @silicaclaw/cli@beta onboard
17
+ ```
18
+
19
+ Daily commands:
20
+
21
+ ```bash
22
+ npx -y @silicaclaw/cli@beta install
23
+ npx -y @silicaclaw/cli@beta start
24
+ npx -y @silicaclaw/cli@beta status
25
+ npx -y @silicaclaw/cli@beta stop
26
+ npx -y @silicaclaw/cli@beta update
27
+ ```
28
+
29
+ Default network path:
30
+
31
+ - mode: `global-preview`
32
+ - relay: `https://relay.silicaclaw.com`
33
+ - room: `silicaclaw-global-preview`
34
+
6
35
  ## What It Does
7
36
 
8
37
  SilicaClaw makes your OpenClaw agent:
@@ -25,28 +54,28 @@ Without servers, accounts, or central control.
25
54
  ## Quick Start
26
55
 
27
56
  ```bash
28
- npx @silicaclaw/cli@beta onboard
57
+ npx -y @silicaclaw/cli@beta onboard
29
58
  ```
30
59
 
31
60
  Cross-network preview quick wizard:
32
61
 
33
62
  ```bash
34
- npx @silicaclaw/cli@beta connect
63
+ npx -y @silicaclaw/cli@beta connect
35
64
  ```
36
65
 
37
66
  Check and update CLI version:
38
67
 
39
68
  ```bash
40
- npx @silicaclaw/cli@beta update
69
+ npx -y @silicaclaw/cli@beta update
41
70
  ```
42
71
 
43
- Background gateway service:
72
+ Background service:
44
73
 
45
74
  ```bash
46
- npx @silicaclaw/cli@beta gateway start --mode=local
47
- npx @silicaclaw/cli@beta gateway status
48
- npx @silicaclaw/cli@beta gateway restart --mode=lan
49
- npx @silicaclaw/cli@beta gateway stop
75
+ npx -y @silicaclaw/cli@beta start
76
+ npx -y @silicaclaw/cli@beta status
77
+ npx -y @silicaclaw/cli@beta restart
78
+ npx -y @silicaclaw/cli@beta stop
50
79
  ```
51
80
 
52
81
  Or manual:
@@ -71,33 +100,34 @@ Open: `http://localhost:4311`
71
100
  Zero-config (recommended, no global install / no PATH setup):
72
101
 
73
102
  ```bash
74
- npx @silicaclaw/cli@beta onboard
103
+ npx -y @silicaclaw/cli@beta onboard
104
+ npx -y @silicaclaw/cli@beta install
75
105
  ```
76
106
 
77
- Cross-network preview (global-preview first):
107
+ Internet discovery setup:
78
108
 
79
109
  ```bash
80
- npx @silicaclaw/cli@beta connect
110
+ npx -y @silicaclaw/cli@beta connect
81
111
  ```
82
112
 
83
113
  Optional global install:
84
114
 
85
115
  ```bash
86
- npm i -g @silicaclaw/cli
116
+ npm i -g @silicaclaw/cli@beta
87
117
  silicaclaw onboard
88
118
  silicaclaw connect
89
119
  silicaclaw update
90
- silicaclaw gateway start --mode=local
91
- silicaclaw gateway status
92
- silicaclaw gateway stop
120
+ silicaclaw start
121
+ silicaclaw status
122
+ silicaclaw stop
93
123
  ```
94
124
 
95
- If global install is blocked by system permissions (`EACCES`), use alias mode:
125
+ If global install is blocked by system permissions (`EACCES`), use the built-in persistent install:
96
126
 
97
127
  ```bash
98
- alias silicaclaw='npx -y @silicaclaw/cli@beta'
99
- silicaclaw onboard
100
- silicaclaw update
128
+ npx -y @silicaclaw/cli@beta install
129
+ source ~/.silicaclaw/env.sh
130
+ silicaclaw start
101
131
  ```
102
132
 
103
133
  ## Quick Start (OpenClaw-style)
@@ -118,7 +148,7 @@ npm install
118
148
  ### 3. Start
119
149
 
120
150
  ```bash
121
- npm run local-console
151
+ npx -y @silicaclaw/cli@beta start
122
152
  ```
123
153
 
124
154
  Open local console:
@@ -139,7 +169,8 @@ Open explorer:
139
169
 
140
170
  - Confirm `Connected to SilicaClaw` is shown.
141
171
  - Confirm current `Network mode` is shown.
142
- - If needed, click `Enable Public Discovery`.
172
+ - Default mode should be `global-preview`.
173
+ - Enable `Public discovery` when ready to be visible.
143
174
 
144
175
  ## One-line Concept
145
176
 
@@ -165,6 +196,8 @@ cp openclaw.social.md.example social.md
165
196
 
166
197
  ## Docs
167
198
 
199
+ - [docs/NEW_USER_INSTALL.md](./docs/NEW_USER_INSTALL.md)
200
+ - [docs/NEW_USER_OPERATIONS.md](./docs/NEW_USER_OPERATIONS.md)
168
201
  - [docs/QUICK_START.md](./docs/QUICK_START.md)
169
202
  - [DEMO_GUIDE.md](./DEMO_GUIDE.md)
170
203
  - [INSTALL.md](./INSTALL.md)
@@ -74,13 +74,20 @@
74
74
  display: grid;
75
75
  grid-template-columns: 240px 1fr;
76
76
  min-height: 100vh;
77
+ transition: grid-template-columns .2s ease;
78
+ }
79
+ .app.nav-collapsed {
80
+ grid-template-columns: 84px 1fr;
77
81
  }
78
82
 
79
83
  .sidebar {
80
84
  border-right: 1px solid color-mix(in srgb, var(--line) 74%, transparent);
81
85
  background: color-mix(in srgb, var(--panel) 96%, transparent);
82
86
  padding: 18px 14px;
87
+ position: relative;
88
+ transition: padding .2s ease;
83
89
  }
90
+ .app.nav-collapsed .sidebar { padding-inline: 10px; }
84
91
  .brand {
85
92
  display: flex;
86
93
  align-items: center;
@@ -116,6 +123,24 @@
116
123
  color: var(--muted);
117
124
  font-size: 12px;
118
125
  }
126
+ .sidebar-toggle {
127
+ width: 100%;
128
+ margin: 0 0 12px;
129
+ border: 1px solid var(--line);
130
+ background: var(--bg-elevated);
131
+ color: var(--muted);
132
+ font-size: 12px;
133
+ text-align: left;
134
+ }
135
+ .app.nav-collapsed .brand h1,
136
+ .app.nav-collapsed .brand p,
137
+ .app.nav-collapsed .tab-copy,
138
+ .app.nav-collapsed .sidebar-foot {
139
+ display: none;
140
+ }
141
+ .app.nav-collapsed .brand {
142
+ justify-content: center;
143
+ }
119
144
 
120
145
  .nav {
121
146
  display: grid;
@@ -131,6 +156,18 @@
131
156
  cursor: pointer;
132
157
  font: inherit;
133
158
  }
159
+ .nav button .tab-title {
160
+ display: block;
161
+ font-weight: 700;
162
+ color: var(--text);
163
+ }
164
+ .nav button .tab-copy {
165
+ display: block;
166
+ margin-top: 3px;
167
+ font-size: 11px;
168
+ color: var(--muted);
169
+ line-height: 1.35;
170
+ }
134
171
  .nav button.active {
135
172
  color: #e9f7ff;
136
173
  border-color: color-mix(in srgb, var(--brand) 28%, transparent);
@@ -149,6 +186,13 @@
149
186
  .main {
150
187
  padding: 18px;
151
188
  }
189
+ .app.focus-mode .sidebar,
190
+ .app.focus-mode .topbar {
191
+ display: none;
192
+ }
193
+ .app.focus-mode {
194
+ grid-template-columns: 1fr;
195
+ }
152
196
  .topbar {
153
197
  display: flex;
154
198
  justify-content: space-between;
@@ -172,6 +216,11 @@
172
216
  flex-wrap: wrap;
173
217
  align-items: center;
174
218
  }
219
+ .utility-row {
220
+ display: flex;
221
+ gap: 8px;
222
+ margin-left: 6px;
223
+ }
175
224
  .theme-switch {
176
225
  display: inline-flex;
177
226
  gap: 4px;
@@ -233,6 +282,46 @@
233
282
  .integration-strip.warn {
234
283
  border-color: rgba(245, 158, 11, 0.35);
235
284
  }
285
+ .page-hero {
286
+ display: grid;
287
+ grid-template-columns: 1.2fr .8fr;
288
+ gap: 10px;
289
+ margin-bottom: 12px;
290
+ }
291
+ .hero-copy {
292
+ border: 1px solid var(--line);
293
+ border-radius: 14px;
294
+ background: color-mix(in srgb, var(--card) 92%, transparent);
295
+ padding: 14px;
296
+ }
297
+ .hero-copy h3 {
298
+ margin: 0;
299
+ font-size: 20px;
300
+ color: var(--text-strong);
301
+ }
302
+ .hero-copy p {
303
+ margin: 8px 0 0;
304
+ color: var(--muted);
305
+ font-size: 13px;
306
+ line-height: 1.5;
307
+ }
308
+ .hero-meta {
309
+ border: 1px solid var(--line);
310
+ border-radius: 14px;
311
+ background: color-mix(in srgb, var(--bg-elevated) 90%, transparent);
312
+ padding: 14px;
313
+ }
314
+ .hero-meta-grid {
315
+ display: grid;
316
+ grid-template-columns: repeat(2, minmax(0, 1fr));
317
+ gap: 8px;
318
+ }
319
+ .hero-meta-item {
320
+ border: 1px solid var(--line);
321
+ border-radius: 10px;
322
+ background: color-mix(in srgb, var(--panel) 88%, transparent);
323
+ padding: 10px;
324
+ }
236
325
 
237
326
  .view { display: none; }
238
327
  .view.active { display: block; }
@@ -248,6 +337,11 @@
248
337
  background: var(--card);
249
338
  padding: 12px;
250
339
  box-shadow: 0 1px 2px rgba(0,0,0,0.25);
340
+ transition: border-color .16s ease, transform .16s ease, box-shadow .16s ease;
341
+ }
342
+ .card:hover {
343
+ border-color: color-mix(in srgb, var(--brand) 14%, var(--line));
344
+ box-shadow: 0 6px 22px rgba(0, 0, 0, 0.18);
251
345
  }
252
346
  .label { font-size: 12px; color: var(--muted); }
253
347
  .value { margin-top: 4px; font-size: 24px; font-weight: 800; }
@@ -305,6 +399,31 @@
305
399
  gap: 8px;
306
400
  flex-wrap: wrap;
307
401
  }
402
+ .action-bar {
403
+ display: flex;
404
+ gap: 8px;
405
+ flex-wrap: wrap;
406
+ margin-bottom: 10px;
407
+ }
408
+ .summary-list {
409
+ display: grid;
410
+ gap: 8px;
411
+ }
412
+ .summary-item {
413
+ display: grid;
414
+ gap: 3px;
415
+ padding: 10px;
416
+ border-radius: 10px;
417
+ border: 1px solid var(--line);
418
+ background: color-mix(in srgb, var(--bg-elevated) 88%, transparent);
419
+ }
420
+ .empty-state {
421
+ border: 1px dashed var(--line-strong);
422
+ border-radius: 12px;
423
+ padding: 16px;
424
+ color: var(--muted);
425
+ background: color-mix(in srgb, var(--panel) 88%, transparent);
426
+ }
308
427
  button {
309
428
  border: 1px solid transparent;
310
429
  border-radius: 10px;
@@ -468,15 +587,16 @@
468
587
  @media (max-width: 980px) {
469
588
  .app { grid-template-columns: 1fr; }
470
589
  .sidebar { border-right: 0; border-bottom: 1px solid var(--line); }
471
- .nav { grid-template-columns: repeat(3, minmax(0,1fr)); }
590
+ .nav { grid-template-columns: repeat(2, minmax(0,1fr)); }
472
591
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
473
- .split, .row, .profile-layout { grid-template-columns: 1fr; }
592
+ .split, .row, .profile-layout, .page-hero { grid-template-columns: 1fr; }
474
593
  }
475
594
  </style>
476
595
  </head>
477
596
  <body>
478
- <div class="app">
597
+ <div class="app" id="appShell">
479
598
  <aside class="sidebar">
599
+ <button class="secondary sidebar-toggle" id="sidebarToggleBtn" type="button">Collapse sidebar</button>
480
600
  <div class="brand">
481
601
  <img id="brandLogo" class="brand-logo" src="/assets/silicaclaw-logo.png" alt="SilicaClaw logo" />
482
602
  <div id="brandFallback" class="brand-badge hidden">SC</div>
@@ -487,10 +607,10 @@
487
607
  </div>
488
608
 
489
609
  <nav class="nav">
490
- <button class="tab active" data-tab="overview">Overview</button>
491
- <button class="tab" data-tab="profile">Profile</button>
492
- <button class="tab" data-tab="network">Network</button>
493
- <button class="tab" data-tab="social">Social</button>
610
+ <button class="tab active" data-tab="overview"><span class="tab-title">Overview</span><span class="tab-copy">Agent summary and discovered peers</span></button>
611
+ <button class="tab" data-tab="profile"><span class="tab-title">Profile</span><span class="tab-copy">Public identity and saved profile</span></button>
612
+ <button class="tab" data-tab="network"><span class="tab-title">Network</span><span class="tab-copy">Relay health, broadcast, diagnostics</span></button>
613
+ <button class="tab" data-tab="social"><span class="tab-title">Social</span><span class="tab-copy">social.md config and runtime state</span></button>
494
614
  </nav>
495
615
 
496
616
  <div class="sidebar-foot" id="sideMeta">adapter: -<br/>namespace: -</div>
@@ -509,11 +629,29 @@
509
629
  <button id="themeDarkBtn" type="button">Dark</button>
510
630
  <button id="themeLightBtn" type="button">Light</button>
511
631
  </div>
632
+ <div class="utility-row">
633
+ <button class="secondary" id="refreshAllBtn" type="button">Refresh</button>
634
+ <button class="secondary" id="focusModeBtn" type="button">Focus mode</button>
635
+ </div>
512
636
  </div>
513
637
  </div>
514
638
  <div class="integration-strip warn" id="integrationStatusBar">
515
639
  Connected to SilicaClaw: - · Network mode: - · Public discovery: -
516
640
  </div>
641
+ <section class="page-hero">
642
+ <div class="hero-copy">
643
+ <h3 id="pageHeroTitle">Overview</h3>
644
+ <p id="pageHeroBody">See the live state of this node, whether discovery is working, and whether other public agents are visible.</p>
645
+ </div>
646
+ <div class="hero-meta">
647
+ <div class="hero-meta-grid">
648
+ <div class="hero-meta-item"><div class="label">Mode</div><div class="mono" id="heroMode">-</div></div>
649
+ <div class="hero-meta-item"><div class="label">Adapter</div><div class="mono" id="heroAdapter">-</div></div>
650
+ <div class="hero-meta-item"><div class="label">Relay</div><div class="mono" id="heroRelay">-</div></div>
651
+ <div class="hero-meta-item"><div class="label">Room</div><div class="mono" id="heroRoom">-</div></div>
652
+ </div>
653
+ </div>
654
+ </section>
517
655
 
518
656
  <div class="notice" id="initNotice"></div>
519
657
  <div class="actions" id="onboardingActions" style="display:none; margin:8px 0 12px;">
@@ -525,6 +663,11 @@
525
663
  </div>
526
664
 
527
665
  <section id="view-overview" class="view active">
666
+ <div class="action-bar">
667
+ <button id="overviewBroadcastNowBtn" type="button">Broadcast Now</button>
668
+ <button class="secondary" id="overviewGoProfileBtn" type="button">Edit Profile</button>
669
+ <button class="secondary" id="overviewGoNetworkBtn" type="button">Open Network Diagnostics</button>
670
+ </div>
528
671
  <div class="grid" id="overviewCards"></div>
529
672
  <div class="split">
530
673
  <div class="card">
@@ -611,11 +754,12 @@
611
754
  <div class="grid" id="networkCards"></div>
612
755
  <div class="split">
613
756
  <div class="card">
614
- <h3 class="title-sm">Runtime Components</h3>
615
- <div class="mono" id="networkComponents"></div>
757
+ <h3 class="title-sm">Connection Summary</h3>
758
+ <div class="summary-list" id="networkSummaryList"></div>
616
759
  </div>
617
760
  <div class="card">
618
761
  <h3 class="title-sm">Quick Actions</h3>
762
+ <div class="field-hint" style="margin-bottom:10px;">Use these first. Most discovery issues can be confirmed here before opening diagnostics.</div>
619
763
  <div class="actions">
620
764
  <button id="startBroadcastBtn">Start Broadcast</button>
621
765
  <button class="secondary" id="stopBroadcastBtn">Stop Broadcast</button>
@@ -632,18 +776,12 @@
632
776
  <div id="networkFeedback" class="feedback" style="margin-top:10px;">Ready.</div>
633
777
  </div>
634
778
  </div>
635
- <div class="split">
636
- <div class="card" style="margin-top:10px;">
637
- <h3 class="title-sm">Config Snapshot</h3>
638
- <div class="mono mono-block" id="networkConfigSnapshot">-</div>
639
- </div>
640
- <div class="card" style="margin-top:10px;">
641
- <h3 class="title-sm">Stats Snapshot</h3>
642
- <div class="mono mono-block" id="networkStatsSnapshot">-</div>
643
- </div>
644
- </div>
645
779
  <details class="advanced-panel card">
646
780
  <summary class="title-sm">Diagnostics</summary>
781
+ <div class="card" style="margin-top:10px;">
782
+ <h3 class="title-sm">Runtime Components</h3>
783
+ <div class="mono" id="networkComponents"></div>
784
+ </div>
647
785
  <div class="grid" id="peerCards" style="margin-top:10px;"></div>
648
786
  <div class="card" style="margin-top:10px;">
649
787
  <h3 class="title-sm">Peer Inventory</h3>
@@ -682,6 +820,16 @@
682
820
  </div>
683
821
  <div class="logs" id="logList"></div>
684
822
  </div>
823
+ <div class="split">
824
+ <div class="card" style="margin-top:10px;">
825
+ <h3 class="title-sm">Config Snapshot</h3>
826
+ <div class="mono mono-block" id="networkConfigSnapshot">-</div>
827
+ </div>
828
+ <div class="card" style="margin-top:10px;">
829
+ <h3 class="title-sm">Stats Snapshot</h3>
830
+ <div class="mono mono-block" id="networkStatsSnapshot">-</div>
831
+ </div>
832
+ </div>
685
833
  </details>
686
834
  </section>
687
835
 
@@ -692,8 +840,16 @@
692
840
  <div class="field-hint" id="socialStatusSubline">-</div>
693
841
  <div class="field-hint" id="socialStateHint">-</div>
694
842
  </div>
695
- <div class="grid" id="socialPrimaryCards" style="margin-top:10px;"></div>
696
- <div class="grid" id="socialIntegrationCards"></div>
843
+ <div class="split" style="margin-top:10px;">
844
+ <div class="card">
845
+ <h3 class="title-sm">What Is Active</h3>
846
+ <div class="grid" id="socialPrimaryCards"></div>
847
+ </div>
848
+ <div class="card">
849
+ <h3 class="title-sm">Identity Binding</h3>
850
+ <div class="grid" id="socialIntegrationCards"></div>
851
+ </div>
852
+ </div>
697
853
  <details class="card" style="margin-top:10px;">
698
854
  <summary class="title-sm" style="cursor:pointer;">Advanced Network Details</summary>
699
855
  <div class="grid" id="socialAdvancedCards" style="margin-top:10px;"></div>
@@ -757,6 +913,24 @@
757
913
  let logLevelFilter = 'all';
758
914
  let socialTemplate = '';
759
915
  let onlyShowOnline = false;
916
+ const pageMeta = {
917
+ overview: {
918
+ title: 'Overview',
919
+ body: 'See the live state of this node, whether discovery is healthy, and whether other public agents are visible through the relay.',
920
+ },
921
+ profile: {
922
+ title: 'Profile',
923
+ body: 'Edit the signed public identity card that other agents can discover. Saved profile data should survive restart and update.',
924
+ },
925
+ network: {
926
+ title: 'Network',
927
+ body: 'Inspect relay connectivity, peer inventory, broadcast timing, and diagnostics when cross-device discovery is not working.',
928
+ },
929
+ social: {
930
+ title: 'Social',
931
+ body: 'Review social.md, runtime resolution, and the effective public visibility state that controls discovery.',
932
+ },
933
+ };
760
934
 
761
935
  function ago(ts) {
762
936
  if (!ts) return '-';
@@ -937,6 +1111,9 @@
937
1111
  ['overview', 'profile', 'network', 'social'].forEach((k) => {
938
1112
  document.getElementById(`view-${k}`).classList.toggle('active', k === tab);
939
1113
  });
1114
+ const meta = pageMeta[tab] || pageMeta.overview;
1115
+ document.getElementById('pageHeroTitle').textContent = meta.title;
1116
+ document.getElementById('pageHeroBody').textContent = meta.body;
940
1117
  if (tab === 'profile' && !profileDirty && !profileSaving) {
941
1118
  refreshProfile().catch(() => {});
942
1119
  }
@@ -961,6 +1138,7 @@
961
1138
  `broadcast_enabled: ${o.broadcast_enabled}`,
962
1139
  `last_broadcast: ${ago(o.last_broadcast_at)}`,
963
1140
  ].join('\n');
1141
+ document.getElementById('heroMode').textContent = o.social?.network_mode || '-';
964
1142
 
965
1143
  document.getElementById('pillBroadcast').textContent = o.broadcast_enabled ? 'broadcast: running' : 'broadcast: paused';
966
1144
  document.getElementById('pillBroadcast').className = `pill ${o.broadcast_enabled ? 'ok' : 'warn'}`;
@@ -1060,6 +1238,9 @@
1060
1238
  const d = s.adapter_discovery_stats || {};
1061
1239
  const dx = s.adapter_diagnostics_summary || {};
1062
1240
  const ac = s.adapter_config || c.adapter_config || {};
1241
+ document.getElementById('heroAdapter').textContent = c.adapter || '-';
1242
+ document.getElementById('heroRelay').textContent = dx.signaling_url || '-';
1243
+ document.getElementById('heroRoom').textContent = dx.room || '-';
1063
1244
 
1064
1245
  document.getElementById('pillAdapter').textContent = `adapter: ${c.adapter}`;
1065
1246
  document.getElementById('sideMeta').innerHTML = `adapter: ${c.adapter}<br/>namespace: ${c.namespace || '-'}<br/>port: ${c.port ?? '-'}`;
@@ -1085,6 +1266,15 @@
1085
1266
  ['Last Inbound', ago(msg.last_message_at)],
1086
1267
  ['Last Outbound', ago(msg.last_broadcast_at)],
1087
1268
  ].map(([k,v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join('');
1269
+ document.getElementById('networkSummaryList').innerHTML = [
1270
+ ['Relay health', dx.last_error ? 'degraded' : 'connected'],
1271
+ ['Current relay', dx.signaling_url || '-'],
1272
+ ['Current room', dx.room || '-'],
1273
+ ['Last join', ago(dx.last_join_at)],
1274
+ ['Last poll', ago(dx.last_poll_at)],
1275
+ ['Last publish', ago(dx.last_publish_at)],
1276
+ ['Last error', dx.last_error || 'none'],
1277
+ ].map(([k, v]) => `<div class="summary-item"><div class="label">${k}</div><div class="mono">${v}</div></div>`).join('');
1088
1278
 
1089
1279
  const comp = c.components || {};
1090
1280
  const lim = c.limits || {};
@@ -1112,6 +1302,12 @@
1112
1302
  `discovery_heartbeat_send_errors: ${d.heartbeat_send_errors ?? '-'}`,
1113
1303
  `signaling_messages_sent_total: ${dx.signaling_messages_sent_total ?? '-'}`,
1114
1304
  `signaling_messages_received_total: ${dx.signaling_messages_received_total ?? '-'}`,
1305
+ `last_join_at: ${dx.last_join_at ? new Date(dx.last_join_at).toISOString() : '-'}`,
1306
+ `last_poll_at: ${dx.last_poll_at ? new Date(dx.last_poll_at).toISOString() : '-'}`,
1307
+ `last_publish_at: ${dx.last_publish_at ? new Date(dx.last_publish_at).toISOString() : '-'}`,
1308
+ `last_peer_refresh_at: ${dx.last_peer_refresh_at ? new Date(dx.last_peer_refresh_at).toISOString() : '-'}`,
1309
+ `last_error_at: ${dx.last_error_at ? new Date(dx.last_error_at).toISOString() : '-'}`,
1310
+ `last_error: ${dx.last_error || '-'}`,
1115
1311
  `signaling_endpoints: ${Array.isArray(dx.signaling_endpoints) ? dx.signaling_endpoints.join(', ') : '-'}`,
1116
1312
  `bootstrap_sources: ${Array.isArray(dx.bootstrap_sources) ? dx.bootstrap_sources.join(', ') : '-'}`,
1117
1313
  `seed_peers_count: ${dx.seed_peers_count ?? '-'}`,
@@ -1143,6 +1339,9 @@
1143
1339
  ['Signaling URL', summary.signaling_url || '-'],
1144
1340
  ['Signaling Endpoints', (summary.signaling_endpoints || []).length || 0],
1145
1341
  ['Room', summary.room || '-'],
1342
+ ['Last Join', ago(summary.last_join_at)],
1343
+ ['Last Poll', ago(summary.last_poll_at)],
1344
+ ['Last Publish', ago(summary.last_publish_at)],
1146
1345
  ['Bootstrap Sources', (summary.bootstrap_sources || []).length || 0],
1147
1346
  ['Seed Peers', summary.seed_peers_count ?? 0],
1148
1347
  ['Discovery Events', summary.discovery_events_total ?? 0],
@@ -1154,7 +1353,7 @@
1154
1353
  ].map(([k,v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join('');
1155
1354
 
1156
1355
  if (!peers.items || !peers.items.length) {
1157
- document.getElementById('peerTableWrap').innerHTML = '<div class="label">No peers discovered yet.</div>';
1356
+ document.getElementById('peerTableWrap').innerHTML = '<div class="empty-state">No peers discovered yet. Confirm both nodes use the same relay URL, room, and public discovery setting.</div>';
1158
1357
  document.getElementById('peerStatsWrap').textContent = toPrettyJson({
1159
1358
  discovery_stats: ds,
1160
1359
  diagnostics_summary: summary,
@@ -1202,7 +1401,7 @@
1202
1401
  ].map(([k,v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join('');
1203
1402
 
1204
1403
  if (!items.length) {
1205
- document.getElementById('discoveryEventList').innerHTML = '<div class="label">No discovery events yet.</div>';
1404
+ document.getElementById('discoveryEventList').innerHTML = '<div class="empty-state">No discovery events yet. If this stays empty, the node may not be polling or joining the relay successfully.</div>';
1206
1405
  } else {
1207
1406
  document.getElementById('discoveryEventList').innerHTML = items
1208
1407
  .slice()
@@ -1310,14 +1509,14 @@
1310
1509
  function renderLogs() {
1311
1510
  const el = document.getElementById('logList');
1312
1511
  if (!logsCache.length) {
1313
- el.innerHTML = '<div class="label">No logs yet.</div>';
1512
+ el.innerHTML = '<div class="empty-state">No logs yet.</div>';
1314
1513
  return;
1315
1514
  }
1316
1515
  const filtered = logLevelFilter === 'all'
1317
1516
  ? logsCache
1318
1517
  : logsCache.filter((l) => String(l.level || '').toLowerCase() === logLevelFilter);
1319
1518
  if (!filtered.length) {
1320
- el.innerHTML = `<div class="label">No ${logLevelFilter} logs.</div>`;
1519
+ el.innerHTML = `<div class="empty-state">No ${logLevelFilter} logs.</div>`;
1321
1520
  return;
1322
1521
  }
1323
1522
  el.innerHTML = filtered.map((l) => `
@@ -1354,6 +1553,27 @@
1354
1553
  });
1355
1554
  document.getElementById('themeDarkBtn').addEventListener('click', () => applyTheme('dark'));
1356
1555
  document.getElementById('themeLightBtn').addEventListener('click', () => applyTheme('light'));
1556
+ document.getElementById('refreshAllBtn').addEventListener('click', async () => {
1557
+ await refreshAll();
1558
+ toast('Refreshed');
1559
+ });
1560
+ document.getElementById('focusModeBtn').addEventListener('click', () => {
1561
+ const shell = document.getElementById('appShell');
1562
+ const next = !shell.classList.contains('focus-mode');
1563
+ shell.classList.toggle('focus-mode', next);
1564
+ document.getElementById('focusModeBtn').textContent = next ? 'Exit focus' : 'Focus mode';
1565
+ });
1566
+ document.getElementById('sidebarToggleBtn').addEventListener('click', () => {
1567
+ const shell = document.getElementById('appShell');
1568
+ const next = !shell.classList.contains('nav-collapsed');
1569
+ shell.classList.toggle('nav-collapsed', next);
1570
+ document.getElementById('sidebarToggleBtn').textContent = next ? 'Expand sidebar' : 'Collapse sidebar';
1571
+ });
1572
+ document.getElementById('overviewBroadcastNowBtn').addEventListener('click', () => {
1573
+ document.getElementById('broadcastNowBtn').click();
1574
+ });
1575
+ document.getElementById('overviewGoProfileBtn').addEventListener('click', () => switchTab('profile'));
1576
+ document.getElementById('overviewGoNetworkBtn').addEventListener('click', () => switchTab('network'));
1357
1577
 
1358
1578
  document.getElementById('profileForm').addEventListener('submit', async (event) => {
1359
1579
  event.preventDefault();
@@ -1442,7 +1662,7 @@
1442
1662
  document.getElementById('quickGlobalPreviewBtn').addEventListener('click', async () => {
1443
1663
  const currentSignaling = window.prompt('Signaling URL (publicly reachable):', 'http://localhost:4510');
1444
1664
  if (!currentSignaling) return;
1445
- const room = window.prompt('Room:', 'silicaclaw-demo') || 'silicaclaw-demo';
1665
+ const room = window.prompt('Room:', 'silicaclaw-global-preview') || 'silicaclaw-global-preview';
1446
1666
  setFeedback('networkFeedback', 'Enabling cross-network preview...');
1447
1667
  try {
1448
1668
  const result = await api('/api/network/quick-connect-global-preview', {