Compare commits

..
9 changed files with 39 additions and 107 deletions

View file

@ -6,7 +6,6 @@
import { linkify as termLinkify } from '@hive/shared/terminal.js';
import { el } from '@hive/shared/dom.js';
import '@hive/shared/side-panel.js'; // registers <hive-side-panel> — side-effect import
import '@hive/shared/hive-warn.js'; // registers <hive-warn> — side-effect import
import DOMPurify from 'dompurify';
// ─── helpers ────────────────────────────────────────────────────────────

View file

@ -50,7 +50,7 @@ function renderTombstones(s) {
// Wording is deliberately about what the list *is* rather than what it
// isn't: nothing records a destroy, so "container absent" is the only thing
// the backend can actually tell.
const warn = el('hive-warn', { level: 'warning' });
const warn = el('p', { class: 'tombstone-warn' });
warn.append(
el('strong', {}, 'shows every agent whose container is absent'),
' — not only destroyed ones. An agent part-way through being spawned ' +

View file

@ -27,6 +27,15 @@ body.cred-shell {
.cred-pane[hidden] { display: none; }
.cred-warning {
border: 1px solid var(--amber);
border-radius: 4px;
padding: 0.7rem 0.9rem;
margin: 0.6rem 0 1.1rem;
font-size: 0.9rem;
background: color-mix(in srgb, var(--amber) 10%, transparent);
}
.gh-status { margin: 0.5rem 0 1.2rem; }
.gh-status-line {
display: flex;

View file

@ -90,7 +90,7 @@
Security-warning banner + a link to generate a PAT. -->
<section class="cred-pane" id="cred-pane-github" data-tab-pane="github"
role="tabpanel" aria-labelledby="cred-tab-github" hidden>
<hive-warn level="warning">
<div class="cred-warning">
⚠ use a <strong>dedicated bot account</strong>, not a human's &mdash;
and a <strong>minimally-scoped</strong> personal access token (only
the repos/scopes the agent actually needs, e.g. <code>repo</code> +
@ -98,7 +98,7 @@
anything within the token's scopes is reachable if the agent is
ever compromised. the token is injected into the agent's state dir
and is <strong>never displayed back</strong> on this page.
</hive-warn>
</div>
<h3>◇ status</h3>
<div id="gh-status" class="gh-status"><p class="meta">select an agent to see its github credential status.</p></div>

View file

@ -444,14 +444,33 @@ hive-agent-menu {
}
.container-row.tombstone .name { color: var(--muted); }
/* K3PT ST4T3 caveat and the port-collision banner both moved to the
shared <hive-warn> component amber/no-pulse for
the standing caveat (a pulsing banner that's always present just
teaches you to stop seeing it), `severity="red" pulse` for the port
collision (an incident, not a caveat). See core.js/swarm.js and
hive-warn.css for the actual styling now.
/* K3PT ST4T3 caveat. Amber, not red like `.port-conflict`, and no pulse: this
is a standing "read the list this way" note, not an incident a pulsing
banner that is always present just teaches you to stop seeing it.
Nothing records a destroy, so this list is every agent whose container is
absent which includes one being spawned right now. */
.tombstone-warn {
background: color-mix(in srgb, var(--amber) 8%, transparent);
border: 1px solid var(--amber);
color: var(--amber);
padding: 0.5em 0.8em;
margin-bottom: 0.6em;
border-radius: 4px;
}
.tombstone-warn strong { color: var(--amber); }
Notification controls below sit between the banner and the containers. */
/* Notification controls — sit between the banner and the containers. */
.port-conflict {
background: color-mix(in srgb, var(--red) 8%, transparent);
border: 1px solid var(--red);
color: var(--red);
padding: 0.5em 0.8em;
margin-bottom: 0.6em;
border-radius: 4px;
text-shadow: 0 0 6px color-mix(in srgb, var(--red) 40%, transparent);
animation: questions-pulse 2.4s ease-in-out infinite;
}
.port-conflict strong { color: var(--red); }
/* Build-queue summary banner on the SW4RM tab: one compact line
when the rebuild queue has active work, with a link to the full queue on

View file

@ -582,7 +582,7 @@ export function renderContainers(s) {
// rebuild. The banner sits above the agent list so it's the
// first thing the operator sees when something's wedged.
if (portConflicts.length) {
const banner = el('hive-warn', { level: 'error' },
const banner = el('div', { class: 'port-conflict' },
el('strong', {}, '⚠ port collision'), ' — ');
const groups = portConflicts.map((c) =>
`:${c.port} (${c.agents.join(' + ')})`).join('; ');

View file

@ -22,7 +22,6 @@
"./modal.js": "./src/modal.js",
"./shadow-css.js": "./src/shadow-css.js",
"./hive-menu.js": "./src/hive-menu/hive-menu.js",
"./hive-warn.js": "./src/hive-warn/hive-warn.js",
"./side-panel.js": "./src/side-panel/hive-side-panel.js",
"./jobq-graph.js": "./src/jobq-graph/hive-jobq-graph.js"
},

View file

@ -1,53 +0,0 @@
/* hive-warn.css shadow-DOM stylesheet for the <hive-warn> autonomous
custom element (hive-warn.js). `level` is an attribute on the HOST
(how callers set it, `hive-warn.js`'s own header explains why)
`:host([level="…"])` sets `color`/`border-color`/`background`/
whether it pulses, same pattern as hive-btn.css's `[variant]`.
Three levels, not an open-ended severity+modifier combination (mara,
on review: "there should be distinction between info, warning, error
all warnings should be styled identically"). Colours reuse
theme.css's own documented semantics rather than inventing new
ones: --cyan is already "info accents", --amber already "warnings",
--red already "errors, fail state" (see theme.css's :root comments).
Pulse is baked into `error` specifically, not a separate opt-in
knob the one call site that wants attention-grabbing (an active
incident) is also the one call site that's semantically `error`, so
tying the two together removes a combination that shouldn't exist
independently of the tier. */
:host {
display: block;
border: 1px solid var(--amber);
border-radius: 4px;
padding: 0.5em 0.8em;
margin-bottom: 0.6em;
color: var(--amber);
background: color-mix(in srgb, var(--amber) 8%, transparent);
}
:host([level="info"]) {
border-color: var(--cyan);
color: var(--cyan);
background: color-mix(in srgb, var(--cyan) 8%, transparent);
}
:host([level="error"]) {
border-color: var(--red);
color: var(--red);
background: color-mix(in srgb, var(--red) 8%, transparent);
/* Only `error` pulses an active incident, not a standing caveat.
A banner that's always present and always pulsing just trains you
to stop seeing it, so `info`/`warning` deliberately stay still.
`currentColor` so the glow matches without a second colour switch. */
text-shadow: 0 0 6px color-mix(in srgb, currentColor 40%, transparent);
animation: hive-warn-pulse 2.4s ease-in-out infinite;
}
@keyframes hive-warn-pulse {
0%, 100% { box-shadow: 0 0 12px -4px color-mix(in srgb, currentColor 55%, transparent); }
50% { box-shadow: 0 0 22px -2px color-mix(in srgb, currentColor 95%, transparent); }
}
/* Explicit rather than relying on plain inheritance matches what
every one of the three original rules declared, kept for parity. */
::slotted(strong) {
color: inherit;
}

View file

@ -1,41 +0,0 @@
// hive-warn.js — <hive-warn>, the shared inline warning-banner
// component. Consolidates three independently-written instances of the
// same thing: `.cred-warning` (credentials.html, static
// markup), `.tombstone-warn` (core.js, JS-built), `.port-conflict`
// (swarm.js, JS-built) — the first two differed only by an
// undeliberate 10% vs 8% tint, the strongest evidence this was drift,
// not three genuinely different needs.
//
// Purely presentational — no internal state, no lifecycle beyond
// attaching its shadow root once. `level` ('info' | 'warning' | 'error',
// default 'warning') is read directly by hive-warn.css's
// `:host([level="…"])` selectors — a fixed three-tier semantic ladder,
// not an open severity+modifier combination (mara, on review: "there
// should be distinction between info, warning, error — all warnings
// should be styled identically"). Unlike `<hive-btn>`'s
// `variant`/`disabled` there's no inner native element that needs the
// attribute mirrored onto it, so no `attributeChangedCallback` is
// needed at all — the CSS reads the host attribute directly.
//
// Content is arbitrary light-DOM children through the shadow tree's
// single default `<slot>` — same reuse of the existing `<strong>`/
// `<code>` markup every call site already writes, no caller-side
// rewrite of their message content needed.
//
// Usage:
// `<hive-warn level="warning">...</hive-warn>` (static HTML)
// `el('hive-warn', { level: 'warning' }, ...)` (JS-built)
// `el('hive-warn', { level: 'error' }, ...)` (active incident, pulses)
import { attachShadowCss } from '../shadow-css.js';
import hiveWarnCss from './hive-warn.css';
class HiveWarn extends HTMLElement {
connectedCallback() {
if (this._built) return; // re-parenting re-fires connectedCallback
const root = attachShadowCss(this, hiveWarnCss);
root.append(document.createElement('slot'));
this._built = true;
}
}
customElements.define('hive-warn', HiveWarn);