Widgets tab links the external demo when config.json names one (demo_url)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
65a9888784
commit
62605d57eb
@@ -15,3 +15,6 @@ Anyone who can load the page can read the token, so serve it only where that's a
|
||||
(it can read sensors and nothing else). Deployed on `core` as the `hamlab-demo` stack —
|
||||
see Hamlet `docs/docker.md`. Update it with
|
||||
`cp examples/external-demo/index.html /home/core/apps/hamlab-demo/site/`.
|
||||
|
||||
To link it from the Widgets tab, set `"demo_url": "http://…"` in the hub's `config.json`
|
||||
and restart Ham Lab.
|
||||
|
||||
@@ -736,10 +736,12 @@ func (s *Server) me(w http.ResponseWriter, _ *http.Request) {
|
||||
}
|
||||
}
|
||||
_, rebootErr := os.Stat("/run/reboot-required")
|
||||
var demoURL string
|
||||
s.cfg.View(func(c *config.Config) { demoURL = c.DemoURL })
|
||||
jsonOK(w, 200, map[string]any{
|
||||
"host": s.host, "user": s.user, "version": s.version,
|
||||
"load": load, "uptime": uptime, "service_started": s.started,
|
||||
"reboot_needed": rebootErr == nil, "fingerprint": s.fingerprint, "arch": runtime.GOARCH,
|
||||
"reboot_needed": rebootErr == nil, "fingerprint": s.fingerprint, "arch": runtime.GOARCH, "demo_url": demoURL,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@ type Config struct {
|
||||
TLS string `json:"tls,omitempty"` // "auto" → self-signed cert in the data dir; "" → plain HTTP
|
||||
Tokens []Token `json:"tokens,omitempty"`
|
||||
Nodes []Node `json:"nodes,omitempty"`
|
||||
// DemoURL, when set, is linked from the Widgets tab: a page outside Ham Lab that
|
||||
// shows the sensor API being used (examples/external-demo).
|
||||
DemoURL string `json:"demo_url,omitempty"`
|
||||
|
||||
path string
|
||||
mu sync.Mutex
|
||||
|
||||
+6
-2
@@ -31,7 +31,7 @@ function Tile({ spec, data, note }: { spec: WidgetSpec; data: WidgetData; note:
|
||||
)
|
||||
}
|
||||
|
||||
export default function Gallery() {
|
||||
export default function Gallery({ demoUrl }: { demoUrl?: string }) {
|
||||
const [cat, setCat] = useState<Sensor[] | null>(null)
|
||||
const [err, setErr] = useState('')
|
||||
const rate = useRefreshMs()
|
||||
@@ -54,7 +54,11 @@ export default function Gallery() {
|
||||
|
||||
return (
|
||||
<div className="dash gallery">
|
||||
<div className="dash-bar"><span className="hint">{sensors.length} sensors on this machine · live · tap a tile to copy its ID</span></div>
|
||||
<div className="dash-bar">
|
||||
<span className="hint">{sensors.length} sensors on this machine · live · tap a tile to copy its ID</span>
|
||||
<span className="spacer" />
|
||||
{demoUrl && <a className="btn" href={demoUrl} target="_blank" rel="noreferrer" title="a page outside Ham Lab, built on the same sensor API">External demo ↗</a>}
|
||||
</div>
|
||||
|
||||
<h2 className="section">Widget types</h2>
|
||||
<div className="kpis">
|
||||
|
||||
+1
-1
@@ -179,7 +179,7 @@ export default function Shell({ me: hubMe, onLoggedOut }: { me: Me; onLoggedOut:
|
||||
{view === 'settings' && <Settings me={hubMe} />}
|
||||
{view === 'machines' && <Machines hub={hubMe} onChanged={loadMachines} onOpen={(n) => { switchMachine(n); setView('dashboard') }} />}
|
||||
{view === 'dashboard' && <Dashboard />}
|
||||
{view === 'widgets' && <Gallery />}
|
||||
{view === 'widgets' && <Gallery demoUrl={hubMe.demo_url} />}
|
||||
{view === 'procs' && <Procs />}
|
||||
{view === 'services' && <Services />}
|
||||
{view === 'cron' && <Cron />}
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ async function req<T>(method: string, path: string, body?: unknown, local = fals
|
||||
return r.json()
|
||||
}
|
||||
|
||||
export interface Me { host: string; user: string; version: string; load: string; uptime: number; reboot_needed?: boolean; fingerprint?: string; arch?: string }
|
||||
export interface Me { demo_url?: string; host: string; user: string; version: string; load: string; uptime: number; reboot_needed?: boolean; fingerprint?: string; arch?: string }
|
||||
export interface PushResult { name: string; ok: boolean; version?: string; error?: string }
|
||||
export interface MachineStatus { name: string; url: string; fingerprint?: string; added: string; online: boolean; host?: string; version?: string; load?: string; uptime?: number; reboot_needed?: boolean; last_seen?: string; error?: string }
|
||||
export interface Term { id: string; name: string; running: boolean; exit_code: number; created: string; clients: number }
|
||||
|
||||
@@ -427,3 +427,4 @@ h2.section { margin: 0; font-size: 13px; font-weight: 600; color: var(--muted);
|
||||
.gallery-tile .value { font-size: 18px; overflow-wrap: anywhere; }
|
||||
.gallery-id { margin-top: auto; padding-top: 8px; font-size: 10.5px; color: var(--muted); overflow-wrap: anywhere; }
|
||||
@media (max-width: 800px) { .gallery .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
||||
a.btn { text-decoration: none; display: inline-flex; align-items: center; }
|
||||
|
||||
Reference in New Issue
Block a user