mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
webui: keystore — expandable row detail view for key data
Expandable detail row per key. Clicking the ▶ arrow left of the name unfolds a full-width row showing labeled key fields (Value / Public Key / Private Key) as word-wrapped monospace blocks (max-width: 72ch), making base64 data easy to read and copy. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
@@ -61,6 +61,8 @@
|
||||
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
--font-mono: "SF Mono", "Fira Code", "Fira Mono", Menlo, Consolas, monospace;
|
||||
|
||||
--surface-alt: var(--slate-100);
|
||||
|
||||
/* Legacy variable mapping */
|
||||
--card-bg: var(--surface);
|
||||
}
|
||||
@@ -74,6 +76,7 @@
|
||||
--fg-muted: var(--slate-400);
|
||||
--border: var(--slate-700);
|
||||
--border-subtle: var(--slate-800);
|
||||
--surface-alt: var(--slate-900);
|
||||
--sidebar-bg: var(--slate-800);
|
||||
--sidebar-fg: var(--slate-200);
|
||||
--sidebar-hover: rgba(255, 255, 255, 0.1);
|
||||
@@ -92,6 +95,7 @@
|
||||
--fg-muted: var(--slate-400);
|
||||
--border: var(--slate-700);
|
||||
--border-subtle: var(--slate-800);
|
||||
--surface-alt: var(--slate-900);
|
||||
--zebra-stripe: rgba(255,255,255,0.03);
|
||||
--sidebar-bg: var(--slate-800);
|
||||
--sidebar-fg: var(--slate-200);
|
||||
@@ -110,6 +114,7 @@
|
||||
--fg-muted: var(--slate-500);
|
||||
--border: var(--slate-200);
|
||||
--border-subtle: var(--slate-100);
|
||||
--surface-alt: var(--slate-100);
|
||||
--sidebar-bg: var(--slate-100);
|
||||
--sidebar-fg: var(--slate-700);
|
||||
--sidebar-hover: rgba(0, 0, 0, 0.07);
|
||||
@@ -132,7 +137,6 @@
|
||||
.light .badge-reject { background: #fef2f2; color: #991b1b; }
|
||||
.light .badge-drop { background: var(--slate-100); color: var(--slate-600); }
|
||||
.light .badge-continue { background: #dbeafe; color: #1e40af; }
|
||||
.light .key-full { background: var(--slate-100); }
|
||||
|
||||
/* ==========================================================================
|
||||
Reset & Base
|
||||
@@ -1264,9 +1268,51 @@ details.fw-hint[open] summary::before { transform: rotate(90deg); }
|
||||
|
||||
/* Keystore */
|
||||
.empty-message { color: var(--fg-muted); font-style: italic; padding: 1rem; text-align: center; }
|
||||
.key-full { font-family: var(--font-mono); font-size: 0.8rem; word-break: break-all; color: var(--fg); background: var(--slate-100); padding: 0.25rem; border-radius: 4px; }
|
||||
@media (prefers-color-scheme: dark) { .key-full { background: var(--slate-800); } }
|
||||
.dark .key-full { background: var(--slate-800); }
|
||||
|
||||
.key-row-toggle {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
color: var(--fg);
|
||||
font: inherit;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
text-align: left;
|
||||
}
|
||||
.key-row-arrow {
|
||||
font-size: 0.6rem;
|
||||
color: var(--fg-muted);
|
||||
display: inline-block;
|
||||
transition: transform 0.15s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.key-row-toggle[aria-expanded="true"] .key-row-arrow { transform: rotate(90deg); }
|
||||
|
||||
.key-detail-row { display: none; }
|
||||
.key-detail-row.is-open { display: table-row; }
|
||||
.key-detail-cell {
|
||||
background: var(--surface-alt);
|
||||
padding: 0.75rem 1rem 0.75rem 1.5rem !important;
|
||||
border-top: none !important;
|
||||
}
|
||||
.key-detail-body { display: flex; flex-direction: column; gap: 0.6rem; }
|
||||
.key-field { display: flex; flex-direction: column; gap: 0.2rem; }
|
||||
.key-field-label { font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--fg-muted); }
|
||||
.key-field-value {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.6;
|
||||
word-break: break-all;
|
||||
white-space: pre-wrap;
|
||||
color: var(--fg);
|
||||
background: var(--surface);
|
||||
padding: 0.4rem 0.6rem;
|
||||
border-radius: 4px;
|
||||
max-width: 72ch;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Survey */
|
||||
.survey-chart { max-width: 100%; height: auto; display: block; }
|
||||
|
||||
@@ -432,6 +432,19 @@
|
||||
});
|
||||
})();
|
||||
|
||||
// Keystore key detail row toggle
|
||||
(function() {
|
||||
document.addEventListener('click', function(e) {
|
||||
var btn = e.target.closest('.key-row-toggle');
|
||||
if (!btn) return;
|
||||
var target = btn.getAttribute('data-target');
|
||||
var row = document.getElementById('key-detail-' + target);
|
||||
if (!row) return;
|
||||
var open = row.classList.toggle('is-open');
|
||||
btn.setAttribute('aria-expanded', open ? 'true' : 'false');
|
||||
});
|
||||
})();
|
||||
|
||||
// Sidebar toggle (mobile)
|
||||
(function() {
|
||||
var MOBILE_BP = 1024;
|
||||
|
||||
@@ -23,15 +23,23 @@
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Format</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .SymmetricKeys}}
|
||||
<tr>
|
||||
<td>{{.Name}}</td>
|
||||
{{range $i, $k := .SymmetricKeys}}
|
||||
<tr class="key-summary-row">
|
||||
<td><button class="key-row-toggle" type="button" aria-expanded="false" data-target="sym-{{$i}}"><span class="key-row-arrow" aria-hidden="true">▶</span>{{.Name}}</button></td>
|
||||
<td>{{.Format}}</td>
|
||||
<td><code class="key-full">{{.Value}}</code></td>
|
||||
</tr>
|
||||
<tr class="key-detail-row" id="key-detail-sym-{{$i}}">
|
||||
<td colspan="2" class="key-detail-cell">
|
||||
<div class="key-detail-body">
|
||||
<div class="key-field">
|
||||
<span class="key-field-label">Value</span>
|
||||
<code class="key-field-value">{{.Value}}</code>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
@@ -49,21 +57,33 @@
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Algorithm</th>
|
||||
<th>Public Key</th>
|
||||
{{if (index .AsymmetricKeys 0).PrivateKeyFull}}<th>Private Key</th>{{end}}
|
||||
<th>Certificates</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .AsymmetricKeys}}
|
||||
<tr>
|
||||
<td>{{.Name}}</td>
|
||||
{{range $i, $k := .AsymmetricKeys}}
|
||||
<tr class="key-summary-row">
|
||||
<td><button class="key-row-toggle" type="button" aria-expanded="false" data-target="asym-{{$i}}"><span class="key-row-arrow" aria-hidden="true">▶</span>{{.Name}}</button></td>
|
||||
<td>{{.Algorithm}}</td>
|
||||
<td>{{if .PublicKeyFull}}<code class="key-full">{{.PublicKeyFull}}</code>{{else}}-{{end}}</td>
|
||||
{{if .PrivateKeyFull}}
|
||||
<td><code class="key-full">{{.PrivateKeyFull}}</code></td>
|
||||
{{end}}
|
||||
<td>{{range $i, $c := .Certificates}}{{if $i}}, {{end}}{{$c}}{{end}}</td>
|
||||
<td>{{range $j, $c := .Certificates}}{{if $j}}, {{end}}{{$c}}{{end}}</td>
|
||||
</tr>
|
||||
<tr class="key-detail-row" id="key-detail-asym-{{$i}}">
|
||||
<td colspan="4" class="key-detail-cell">
|
||||
<div class="key-detail-body">
|
||||
{{if .PublicKeyFull}}
|
||||
<div class="key-field">
|
||||
<span class="key-field-label">Public Key</span>
|
||||
<code class="key-field-value">{{.PublicKeyFull}}</code>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .PrivateKeyFull}}
|
||||
<div class="key-field">
|
||||
<span class="key-field-label">Private Key</span>
|
||||
<code class="key-field-value">{{.PrivateKeyFull}}</code>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user