8 lines
6.0 KiB
JSON
8 lines
6.0 KiB
JSON
{
|
|
"html": "<div id=\"header\">\n <span class=\"title\">⬡ Data Lake</span>\n <input id=\"search\" type=\"text\" placeholder=\"filter variables\u2026\" />\n <span id=\"count\">0 vars</span>\n <span id=\"status\">connecting\u2026</span>\n</div>\n<div id=\"table-wrap\">\n <table>\n <thead>\n <tr>\n <th>Variable</th>\n <th>Type</th>\n <th>Value</th>\n </tr>\n </thead>\n <tbody id=\"tbody\"></tbody>\n </table>\n <div id=\"empty\" style=\"display:none\">No variables match the filter.</div>\n</div>",
|
|
|
|
"css": "*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n:root {\n --bg0: #09111a;\n --bg1: #0e1d2e;\n --bg2: #162336;\n --border: #1e3050;\n --text0: #d8eeff;\n --text1: #6a9bbf;\n --accent: #00c8f0;\n --green: #00e09a;\n --mono: 'Courier New', monospace;\n}\nbody {\n background: var(--bg0);\n color: var(--text0);\n font-family: var(--mono);\n font-size: 12px;\n height: 100vh;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n#header {\n background: var(--bg2);\n border-bottom: 1px solid var(--border);\n padding: 5px 8px;\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n}\n#header .title { font-size: 11px; font-weight: bold; color: var(--accent); white-space: nowrap; }\n#search {\n flex: 1;\n background: var(--bg0);\n border: 1px solid var(--border);\n border-radius: 3px;\n color: var(--text0);\n font-family: var(--mono);\n font-size: 11px;\n padding: 2px 6px;\n outline: none;\n}\n#search:focus { border-color: var(--accent); }\n#count { font-size: 10px; color: var(--text1); white-space: nowrap; }\n#status { font-size: 10px; color: #ffb830; white-space: nowrap; }\n#status.ok { color: var(--green); }\n#status.err { color: #ff3a5a; }\n#table-wrap { flex: 1; overflow-y: auto; overflow-x: hidden; }\n#table-wrap::-webkit-scrollbar { width: 5px; }\n#table-wrap::-webkit-scrollbar-track { background: var(--bg0); }\n#table-wrap::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }\ntable { width: 100%; border-collapse: collapse; }\nthead th {\n position: sticky; top: 0;\n background: var(--bg2);\n border-bottom: 1px solid var(--border);\n color: var(--text1);\n font-size: 10px; font-weight: normal;\n padding: 3px 6px; text-align: left;\n text-transform: uppercase; letter-spacing: 0.05em;\n z-index: 1;\n}\nthead th:nth-child(1) { width: 55%; }\nthead th:nth-child(2) { width: 12%; }\nthead th:nth-child(3) { width: 33%; }\ntbody tr { border-bottom: 1px solid var(--border); transition: background 0.1s; }\ntbody tr:hover { background: var(--bg2); }\ntbody tr.flash td { color: var(--green); }\ntd { padding: 3px 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\ntd:nth-child(1) { color: var(--accent); font-size: 11px; }\ntd:nth-child(2) { color: var(--text1); font-size: 10px; }\ntd:nth-child(3) { color: var(--text0); font-size: 11px; }\n#empty { padding: 20px; text-align: center; color: var(--text1); font-size: 11px; }",
|
|
|
|
"js": "// State\nlet vars = {};\nlet filter = '';\n\n// DOM\nconst tbody = document.getElementById('tbody');\nconst countEl = document.getElementById('count');\nconst statusEl= document.getElementById('status');\nconst searchEl= document.getElementById('search');\nconst emptyEl = document.getElementById('empty');\n\n// Format a value for display\nfunction fmt(v) {\n if (v === null || v === undefined) return '\u2014';\n if (typeof v === 'number') return Number.isInteger(v) ? String(v) : v.toFixed(4);\n if (typeof v === 'boolean') return v ? 'true' : 'false';\n if (typeof v === 'object') return JSON.stringify(v).slice(0, 60);\n return String(v).slice(0, 80);\n}\n\n// Render table\nfunction render() {\n const keys = Object.keys(vars).sort();\n const filtered = keys.filter(k => !filter || k.toLowerCase().includes(filter));\n emptyEl.style.display = filtered.length === 0 ? '' : 'none';\n countEl.textContent = filtered.length + ' / ' + keys.length + ' vars';\n const existingRows = {};\n tbody.querySelectorAll('tr[data-key]').forEach(row => { existingRows[row.dataset.key] = row; });\n Object.keys(existingRows).forEach(k => { if (!filtered.includes(k)) { existingRows[k].remove(); delete existingRows[k]; } });\n filtered.forEach(function(key) {\n const d = vars[key];\n let row = existingRows[key];\n if (!row) {\n row = document.createElement('tr');\n row.dataset.key = key;\n row.innerHTML = '<td title=\"' + key + '\">' + key + '</td><td></td><td></td>';\n tbody.appendChild(row);\n }\n const cells = row.cells;\n const newType = d.type || '\u2014';\n const newValue = fmt(d.value);\n if (cells[2].textContent !== newValue) {\n row.classList.add('flash');\n setTimeout(function() { row.classList.remove('flash'); }, 400);\n }\n cells[1].textContent = newType;\n cells[2].textContent = newValue;\n });\n}\n\n// Poll data lake\nfunction poll() {\n try {\n if (typeof window.cockpit === 'undefined' || typeof window.cockpit.getAllDataLakeVariablesInfo !== 'function') {\n statusEl.className = 'err';\n statusEl.textContent = 'API not ready';\n return;\n }\n const info = window.cockpit.getAllDataLakeVariablesInfo();\n if (!info || Object.keys(info).length === 0) {\n statusEl.className = '';\n statusEl.textContent = 'no data';\n return;\n }\n Object.entries(info).forEach(function(entry) {\n const key = entry[0]; const meta = entry[1];\n vars[key] = { type: meta && meta.type ? meta.type : (typeof (meta && meta.value)), value: meta && meta.value !== undefined ? meta.value : meta };\n });\n statusEl.className = 'ok';\n statusEl.textContent = 'live';\n render();\n } catch(err) {\n statusEl.className = 'err';\n statusEl.textContent = 'error';\n console.error('[DataLake]', err);\n }\n}\n\n// Filter handler\nsearchEl.addEventListener('input', function() { filter = searchEl.value.trim().toLowerCase(); render(); });\n\n// Start\nsetTimeout(poll, 300);\nsetInterval(poll, 500);"
|
|
}
|