treefmt: apply prettier

Pure `nix fmt` output from the commit before this one — no hand edits.
203 files: 52 md, 42 tsx, 32 js, 32 css, 21 ts, 13 html, 8 json, 3 mjs.

Reproduce with `nix develop -c nix fmt` on the parent commit; the result
should be byte-identical to this tree.

None of the 13 `.prettierignore` entries appears here — verified by
intersecting the changed-file list against the ignore file, with a
control proving the intersection finds a match when one exists.
This commit is contained in:
atlas 2026-09-02 14:29:33 +02:00
commit 39b95c2ede
203 changed files with 10090 additions and 6085 deletions

View file

@ -28,21 +28,28 @@
// loader is `text` (for unrelated shadow-DOM components' CSS-as-string
// needs), and that loader is global per call, not per-module.
import { useState, useEffect, useCallback, useRef } from 'preact/hooks';
import { useState, useEffect, useCallback, useRef } from "preact/hooks";
// Mirrors `hive_jobq_wire::StateSchema` verbatim (variant names, no
// `rename_all`) — see that enum's own doc comment for why it's kept in
// an exhaustive match on the Rust side; this union is this file's
// equivalent contract.
type NodeState = 'Pending' | 'Running' | 'Finishing' | 'Done' | 'Failed' | 'Cancelled' | 'Skipped';
type NodeState =
| "Pending"
| "Running"
| "Finishing"
| "Done"
| "Failed"
| "Cancelled"
| "Skipped";
type TerminalState = 'Done' | 'Failed' | 'Cancelled' | 'Skipped';
type TerminalState = "Done" | "Failed" | "Cancelled" | "Skipped";
// Mirrors `hive_jobq_wire::GraphDep` — externally tagged on `kind`,
// values are the Rust variant names verbatim.
type GraphDep =
| { kind: 'Node'; id: number; accepts: TerminalState[] }
| { kind: 'Resource'; name: string; count: number };
| { kind: "Node"; id: number; accepts: TerminalState[] }
| { kind: "Resource"; name: string; count: number };
interface NodePayload {
label: string;
@ -69,13 +76,13 @@ interface TreeNode extends GraphNode {
}
const STATE_GLYPH: Record<NodeState, string> = {
Pending: '⏸',
Running: '▶',
Finishing: '◐',
Done: '✔',
Failed: '✖',
Cancelled: '⊘',
Skipped: '·',
Pending: "⏸",
Running: "▶",
Finishing: "◐",
Done: "✔",
Failed: "✖",
Cancelled: "⊘",
Skipped: "·",
};
// Declaration order doubles as render order for the filter checkboxes —
@ -84,12 +91,16 @@ const STATE_GLYPH: Record<NodeState, string> = {
const ALL_STATES = Object.keys(STATE_GLYPH) as NodeState[];
// Product call: "default selection filters out skipped and done."
const DEFAULT_HIDDEN_STATES = new Set<NodeState>(['Done', 'Skipped']);
const DEFAULT_HIDDEN_STATES = new Set<NodeState>(["Done", "Skipped"]);
// Non-terminal states a cancel button makes sense on. Finishing is
// included — "own logic done, children still running" is still a subtree
// worth stopping early.
const CANCELLABLE_STATES = new Set<NodeState>(['Pending', 'Running', 'Finishing']);
const CANCELLABLE_STATES = new Set<NodeState>([
"Pending",
"Running",
"Finishing",
]);
// Build a parent/child tree from the flat wire array. `parent` (structural
// grouping) defines tree shape. Sibling order follows array order, which
@ -117,7 +128,9 @@ function buildTree(nodes: GraphNode[]): TreeNode[] {
}
for (const n of byId.values()) {
n._waitsOn = (n.deps || [])
.filter((d): d is Extract<GraphDep, { kind: 'Node' }> => d.kind === 'Node')
.filter(
(d): d is Extract<GraphDep, { kind: "Node" }> => d.kind === "Node",
)
.map((d) => byId.get(d.id))
.filter((dep): dep is TreeNode => dep != null)
.map((dep) => dep.payload.label);
@ -131,17 +144,19 @@ function buildTree(nodes: GraphNode[]): TreeNode[] {
// single stringified row rather than silently dropping it).
function DataList({ data }: { data: unknown }) {
if (data == null) return null;
const isPlainObject = typeof data === 'object' && !Array.isArray(data);
const isPlainObject = typeof data === "object" && !Array.isArray(data);
const entries: [string, unknown][] = isPlainObject
? Object.entries(data as Record<string, unknown>)
: [['data', data]];
: [["data", data]];
if (!entries.length) return null;
return (
<dl class="jg-data">
{entries.map(([k, v]) => (
<>
<dt key={k + '-dt'}>{k}</dt>
<dd key={k + '-dd'}>{typeof v === 'string' ? v : JSON.stringify(v)}</dd>
<dt key={k + "-dt"}>{k}</dt>
<dd key={k + "-dd"}>
{typeof v === "string" ? v : JSON.stringify(v)}
</dd>
</>
))}
</dl>
@ -157,7 +172,7 @@ function NodeView({
cancellable: boolean;
onCancel?: (id: number) => void;
}) {
const glyph = STATE_GLYPH[n.state] || '?';
const glyph = STATE_GLYPH[n.state] || "?";
const showCancel = cancellable && CANCELLABLE_STATES.has(n.state);
// Flash the state glyph on a genuine state change (Pending → Running,
// etc.), not on mount — `prevState` starts at the node's own initial
@ -176,27 +191,41 @@ function NodeView({
return (
<div class="jg-node">
<div class="jg-row">
<span class={'jg-state jg-state-' + n.state.toLowerCase() + (flashing ? ' jg-state-flash' : '')}
title={n.state + (n.error ? ' — ' + n.error : '')}
onAnimationEnd={() => setFlashing(false)}>
<span
class={
"jg-state jg-state-" +
n.state.toLowerCase() +
(flashing ? " jg-state-flash" : "")
}
title={n.state + (n.error ? " — " + n.error : "")}
onAnimationEnd={() => setFlashing(false)}
>
{glyph}
</span>
{' '}
</span>{" "}
<span class="jg-label">{n.payload.label}</span>
{showCancel && (
<button type="button" class="jg-cancel-btn" title={'cancel ' + n.payload.label}
onClick={() => onCancel && onCancel(n.id)}>
<button
type="button"
class="jg-cancel-btn"
title={"cancel " + n.payload.label}
onClick={() => onCancel && onCancel(n.id)}
>
</button>
)}
</div>
{n._waitsOn && n._waitsOn.length > 0 && (
<div class="jg-waits-on">waits on: {n._waitsOn.join(', ')}</div>
<div class="jg-waits-on">waits on: {n._waitsOn.join(", ")}</div>
)}
<DataList data={n.payload.data} />
{n.error && <pre class="jg-error">{n.error}</pre>}
{n._children.map((child) => (
<NodeView key={child.id} n={child} cancellable={cancellable} onCancel={onCancel} />
<NodeView
key={child.id}
n={child}
cancellable={cancellable}
onCancel={onCancel}
/>
))}
</div>
);
@ -212,12 +241,20 @@ function FilterBar({
return (
<div class="jg-filter">
{ALL_STATES.map((state) => {
const id = 'jg-filter-' + state.toLowerCase();
const id = "jg-filter-" + state.toLowerCase();
return (
<label key={state} for={id} class={'jg-filter-label jg-state-' + state.toLowerCase()}>
<input type="checkbox" id={id} checked={selectedStates.has(state)}
onChange={() => onToggle(state)} />
{' '}{STATE_GLYPH[state] + ' ' + state}
<label
key={state}
for={id}
class={"jg-filter-label jg-state-" + state.toLowerCase()}
>
<input
type="checkbox"
id={id}
checked={selectedStates.has(state)}
onChange={() => onToggle(state)}
/>{" "}
{STATE_GLYPH[state] + " " + state}
</label>
);
})}
@ -229,11 +266,14 @@ function FilterBar({
// param — omitted entirely when every state is checked, so the
// unfiltered default case sends the exact same request as before this
// filter existed.
function fetchUrl(endpoint: string | undefined, selectedStates: Set<NodeState>): string | null {
function fetchUrl(
endpoint: string | undefined,
selectedStates: Set<NodeState>,
): string | null {
if (!endpoint) return null;
if (selectedStates.size >= ALL_STATES.length) return endpoint;
const url = new URL(endpoint, window.location.origin);
url.searchParams.set('states', Array.from(selectedStates).join(','));
url.searchParams.set("states", Array.from(selectedStates).join(","));
return url.pathname + url.search;
}
@ -249,7 +289,13 @@ export interface JobqGraphProps {
// change identity so the effect below re-runs, giving a host an
// explicit "refetch now" lever (bump it and re-render) without an
// imperative ref into this component.
export function JobqGraph({ endpoint, cancellable = false, onUpdate, onCancel, refreshToken = 0 }: JobqGraphProps) {
export function JobqGraph({
endpoint,
cancellable = false,
onUpdate,
onCancel,
refreshToken = 0,
}: JobqGraphProps) {
const [selectedStates, setSelectedStates] = useState<Set<NodeState>>(
() => new Set(ALL_STATES.filter((s) => !DEFAULT_HIDDEN_STATES.has(s))),
);
@ -259,7 +305,8 @@ export function JobqGraph({ endpoint, cancellable = false, onUpdate, onCancel, r
const toggleState = useCallback((state: NodeState) => {
setSelectedStates((prev) => {
const next = new Set(prev);
if (next.has(state)) next.delete(state); else next.add(state);
if (next.has(state)) next.delete(state);
else next.add(state);
return next;
});
}, []);
@ -271,7 +318,7 @@ export function JobqGraph({ endpoint, cancellable = false, onUpdate, onCancel, r
(async () => {
try {
const r = await fetch(url);
if (!r.ok) throw new Error('http ' + r.status);
if (!r.ok) throw new Error("http " + r.status);
const data = (await r.json()) as GraphNode[];
if (cancelled) return;
setNodes(data);
@ -282,11 +329,13 @@ export function JobqGraph({ endpoint, cancellable = false, onUpdate, onCancel, r
setError(String(err));
}
})();
return () => { cancelled = true; };
return () => {
cancelled = true;
};
// eslint-disable-next-line react-hooks/exhaustive-deps -- selectedStates is a Set;
// its *contents* are what should retrigger the fetch, not its identity, and the
// string form below already changes identity exactly when contents do.
}, [endpoint, Array.from(selectedStates).sort().join(','), refreshToken]);
}, [endpoint, Array.from(selectedStates).sort().join(","), refreshToken]);
return (
<div class="jg-root">
@ -300,7 +349,12 @@ export function JobqGraph({ endpoint, cancellable = false, onUpdate, onCancel, r
<p class="jg-empty">empty</p>
) : (
buildTree(nodes).map((root) => (
<NodeView key={root.id} n={root} cancellable={cancellable} onCancel={onCancel} />
<NodeView
key={root.id}
n={root}
cancellable={cancellable}
onCancel={onCancel}
/>
))
)}
</div>

View file

@ -65,10 +65,10 @@
animation: none;
}
}
:root[data-motion='reduce'] .jg-node {
:root[data-motion="reduce"] .jg-node {
animation: none;
}
:root[data-motion='allow'] .jg-node {
:root[data-motion="allow"] .jg-node {
animation: jg-node-enter 160ms ease;
}
@ -93,13 +93,30 @@
min-width: 1.2em;
text-align: center;
}
.jg-state-pending { color: var(--muted); }
.jg-state-running { color: var(--cyan); }
.jg-state-finishing { color: var(--cyan); opacity: 0.75; }
.jg-state-done { color: var(--green); }
.jg-state-failed { color: var(--red); }
.jg-state-cancelled { color: var(--muted); text-decoration: line-through; }
.jg-state-skipped { color: var(--muted); opacity: 0.5; }
.jg-state-pending {
color: var(--muted);
}
.jg-state-running {
color: var(--cyan);
}
.jg-state-finishing {
color: var(--cyan);
opacity: 0.75;
}
.jg-state-done {
color: var(--green);
}
.jg-state-failed {
color: var(--red);
}
.jg-state-cancelled {
color: var(--muted);
text-decoration: line-through;
}
.jg-state-skipped {
color: var(--muted);
opacity: 0.5;
}
/* Short-lived pulse applied by `NodeView` (JobqGraph.tsx) exactly when
a node's own `state` value changes on an existing DOM node a mount
@ -123,10 +140,10 @@
animation: none;
}
}
:root[data-motion='reduce'] .jg-state-flash {
:root[data-motion="reduce"] .jg-state-flash {
animation: none;
}
:root[data-motion='allow'] .jg-state-flash {
:root[data-motion="allow"] .jg-state-flash {
animation: jg-state-flash 350ms ease;
}
@ -149,7 +166,9 @@
display: inline-flex;
align-items: center;
justify-content: center;
transition: color 0.15s ease, border-color 0.15s ease;
transition:
color 0.15s ease,
border-color 0.15s ease;
}
.jg-cancel-btn:hover,
.jg-cancel-btn:focus-visible {
@ -166,8 +185,13 @@
grid-template-columns: auto 1fr;
gap: 0 0.5em;
}
.jg-data dt { font-weight: 600; }
.jg-data dd { margin: 0; word-break: break-word; }
.jg-data dt {
font-weight: 600;
}
.jg-data dd {
margin: 0;
word-break: break-word;
}
.jg-waits-on {
margin: 0.1em 0 0 1.6em;