v2.0.6 — oumta-dashboard: hardwire config, drop settings tab

This plugin only targets one specific deployment, so the settings
panel was noise that tempted accidental misconfiguration. baseUrl,
authToken and pollIntervalSec now live in a top-of-file CONFIG
constant; the hyprcrd-settings tab, the localStorage persistence,
and the panel CSS are gone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
thandClaude Opus 4.7 committed 2026-04-18 16:20:09 +02:00
1 parent 1e51bdec7c
commit 0553960a57
5 files changed
+27 -106

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
<?php
define('APP_NAME', 'hyprcrd');
define('APP_VERSION', '2.0.5');
define('APP_VERSION', '2.0.6');
// APP_DEBUG: gates verbose error responses (curl traces, upstream bodies).
// Off by default; enable via env for local troubleshooting only.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "hyprcrd",
"version": "2.0.5",
"version": "2.0.6",
"description": "hyprcrd — self-hosted card manager",
"main": "main.js",
"scripts": {
+7 -5
View File
@@ -19,13 +19,15 @@ Close with **ESC** or the × button.
Tab row at the top. Currently two mocks (`oumta-dev`, `oumta-beta`). Real list comes from the backend once `/api/namespaces` exists.
## Settings
## Configuration
Open the hyprcrd settings overlay and choose the **oumta** tab:
Hardwired at the top of `oumta-dashboard.js` in the `CONFIG` block:
- **API Base URL** — e.g. `http://localhost:8080` for local dev, or the cluster-internal URL
- **Auth token** — optional, sent as `Authorization: Bearer …`. Will eventually be filled automatically once Keycloak is wired up.
- **Poll interval** — polling fallback cadence when SSE is unavailable. Default 15 s.
- **baseUrl** — where the Oumta Go backend is reachable (default `http://localhost:8085`).
- **authToken** — optional, sent as `Authorization: Bearer …` when non-empty.
- **pollIntervalSec** — polling fallback cadence when SSE is unavailable. Default 15 s.
No UI settings panel: the dashboard only runs against one specific deployment, so exposing these as editable fields would be noise.
## State today
@@ -234,24 +234,3 @@
#oumta-live-indicator[data-state="live"] .oumta-dot { background: #4ade80; box-shadow: 0 0 6px #4ade80; }
#oumta-live-indicator[data-state="polling"] .oumta-dot { background: #fb923c; }
#oumta-live-indicator[data-state="error"] .oumta-dot { background: var(--t-danger); }
/* ── Settings tab ── */
.oumta-settings-field {
display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px;
}
.oumta-settings-field label {
font-family: var(--font-pixel, 'Silkscreen', cursive);
font-size: 9px; color: var(--t-dim); text-transform: uppercase;
}
.oumta-settings-field input {
background: var(--t-bg);
border: 1px solid var(--t-border);
color: var(--t-bright);
padding: 8px 10px;
border-radius: var(--ui-radius-input);
font-family: var(--font-content, monospace);
font-size: 12px;
outline: none;
}
.oumta-settings-field input:focus { border-color: var(--t-accent); }
.oumta-settings-help { font-size: 10px; color: var(--t-dim); line-height: 1.5; }
@@ -1,24 +1,21 @@
// ── Oumta Dashboard plugin ──
//
// Fullscreen view that consumes the Oumta Go backend (`oumta-dashboard`).
// All reads go through `api()` below; configure the backend URL in the
// hyprcrd settings overlay (oumta tab) or accept the localhost default.
// Backend location + poll interval are hardwired below — change them here
// if the deployment topology shifts. No UI settings panel on purpose:
// this plugin only makes sense in one specific deployment, so a settings
// tab would just be noise and tempt accidental misconfiguration.
const h = window.hyprcrd;
const $ = id => document.getElementById(id);
// ── Settings / persisted state ──
//
// We use localStorage for now. Later we might persist via a hyprcrd setting
// or a plugin-level table — see TODO.md.
const LS_KEY = 'oumta-dashboard-settings';
const state = {
settings: {
baseUrl: 'http://localhost:8080', // e.g. https://dashboard.oumta.internal; default fits local dev
const CONFIG = {
baseUrl: 'http://localhost:8085',
authToken: '', // Keycloak bearer; empty = no auth yet
pollIntervalSec: 15,
},
};
const state = {
namespaces: [], // [{ id, label, namespace }]
activeNamespace: null,
activeView: 'overview',
@@ -28,31 +25,21 @@ const state = {
data: { overview: null, workloads: [], timeline: [], scopeWorkload: null },
};
function loadSettings() {
try {
const raw = localStorage.getItem(LS_KEY);
if (raw) Object.assign(state.settings, JSON.parse(raw));
} catch {}
}
function saveSettings() {
localStorage.setItem(LS_KEY, JSON.stringify(state.settings));
}
// ── Central API surface ─────────────────────────────────────────
//
// Every backend call routes through `api()`. Requests go against
// `state.settings.baseUrl`; the active namespace (if any) is attached
// as a `namespace` query parameter, and the configured auth token is
// sent as a bearer header. Response shapes are defined by the Go
// backend in `internal/httpapi/contracts.go`.
// Every backend call routes through `api()`. Requests go against the
// hardwired baseUrl; the active namespace (if any) is attached as a
// `namespace` query parameter, and the bearer token (if any) is sent as
// Authorization. Response shapes are defined by the Go backend in
// `internal/httpapi/contracts.go`.
async function api(path, opts = {}) {
const url = new URL(path, state.settings.baseUrl);
const url = new URL(path, CONFIG.baseUrl);
if (!url.searchParams.has('namespace') && state.activeNamespace) {
url.searchParams.set('namespace', state.activeNamespace.namespace);
}
const headers = state.settings.authToken
? { Authorization: 'Bearer ' + state.settings.authToken }
const headers = CONFIG.authToken
? { Authorization: 'Bearer ' + CONFIG.authToken }
: {};
const res = await fetch(url, { ...opts, headers });
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
@@ -337,7 +324,7 @@ function stopLive() {
setLive('off');
}
function startPolling() {
const ms = (state.settings.pollIntervalSec || 15) * 1000;
const ms = (CONFIG.pollIntervalSec || 15) * 1000;
state.pollHandle = setInterval(refresh, ms);
setLive('polling');
}
@@ -394,46 +381,6 @@ if (document.readyState === 'loading') {
injectHeaderButton();
}
// ── Settings tab in hyprcrd settings ─────────────────────────
function renderSettingsPanel(panelEl) {
panelEl.innerHTML = `
<div class="oumta-settings-field">
<label for="oumta-set-baseurl">API Base URL</label>
<input type="text" id="oumta-set-baseurl" placeholder="http://localhost:8080">
<div class="oumta-settings-help">Where the Oumta Go backend is reachable from this browser. No trailing slash.</div>
</div>
<div class="oumta-settings-field">
<label for="oumta-set-token">Auth token (optional)</label>
<input type="text" id="oumta-set-token" placeholder="Bearer token from Keycloak">
<div class="oumta-settings-help">Will be sent as <code>Authorization: Bearer &lt;token&gt;</code>. Leave empty if the API is unauthenticated.</div>
</div>
<div class="oumta-settings-field">
<label for="oumta-set-poll">Poll interval (seconds)</label>
<input type="number" id="oumta-set-poll" min="5" max="300">
<div class="oumta-settings-help">Used as a fallback when SSE is unavailable.</div>
</div>
<button id="oumta-set-save" class="flex items-center gap-1.5 px-3 py-1.5 text-xs border border-t-accent text-t-accent hover:bg-t-accent hover:text-t-bg transition-colors">
<i class="ph ph-floppy-disk text-sm"></i><span class="font-pixel text-[10px]">Save</span>
</button>
<span id="oumta-set-status" class="font-pixel text-[10px] text-t-dim ml-3"></span>
`;
$('oumta-set-baseurl').value = state.settings.baseUrl || '';
$('oumta-set-token').value = state.settings.authToken || '';
$('oumta-set-poll').value = state.settings.pollIntervalSec || 15;
$('oumta-set-save').addEventListener('click', () => {
state.settings.baseUrl = $('oumta-set-baseurl').value.trim();
state.settings.authToken = $('oumta-set-token').value.trim();
state.settings.pollIntervalSec = Math.max(5, parseInt($('oumta-set-poll').value) || 15);
saveSettings();
const s = $('oumta-set-status');
s.textContent = 'saved';
setTimeout(() => { s.textContent = ''; }, 1500);
// If the overlay is open, restart polling with new interval.
if (isOpen()) { stopLive(); startLive(); }
});
}
// ── Helpers ─────────────────────────────────────────────────
function escapeHtml(s) {
@@ -449,11 +396,4 @@ function formatTime(iso) {
// ── Boot ────────────────────────────────────────────────────
loadSettings();
h?.settings?.registerTab?.({
id: 'oumta-dashboard',
label: 'oumta',
icon: 'ph-gauge',
onShow: renderSettingsPanel,
});
h?.registerPlugin?.({ name: 'oumta-dashboard', version: '0.1.0' });