@hollis-labs/kit-admin 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ - Align configuration typography with compact dashboard roles; keep scalable
4
+ profiles and root-relative control/layout sizing. Remove directory type
5
+ overrides; restore configuration avatar dimensions through named theme roles.
6
+
7
+ ## Unreleased
8
+
9
+ - Add controlled record directories, theme-colored identity avatars, activity
10
+ rows, inspectors, facts, timelines and sparklines. Hosts own observations,
11
+ calculations, selection and commands.
12
+ - Add configuration sections, definition pairs, badges, action buttons, tabs,
13
+ fields and native form controls for Builder-style workspaces. Reuse base
14
+ dialog/button/input/tab behavior and dashboard typography/control tokens.
15
+ - Directory layouts support balanced, wide and compact navigation panes with
16
+ bounded mobile lists. Existing defaults remain unchanged.
17
+
18
+ ## 0.1.0 - 2026-10-03 - first release
19
+
20
+ - Render callback-free Settings as read-only text even for writable declarations,
21
+ using a presentation copy without changing host data or backend capabilities.
22
+
23
+ - Controlled admin navigation/content/standalone composition per the
24
+ [admin shell spec](../../docs/admin-shell-spec.md), over existing dashboard
25
+ layouts, settings provenance/setup and observation components. Host-owned
26
+ data, routes and commands remain controlled inputs.
27
+ - Require design-components, design-tokens and kit-dashboard `^0.4.0`,
28
+ kit-settings `^0.2.0` and kit-observe `^0.1.1`.
29
+ - Keep Dashboard a declaration directory and enforce canonical resource sections,
30
+ unavailable discovery/group states and host-owned context.
31
+ - Include opt-in `/charts` delegation, fixture demos and behavioral/browser evidence.
32
+ - Use optional settings text presentation when the host supplies no settings
33
+ callbacks, so read-only preferences render labelled values without disabled
34
+ editor affordances. Keep source, locks and apply state in canonical Settings.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Chrispian Burks / Hollis Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,170 @@
1
- # Temporary Holding Version
1
+ # @hollis-labs/kit-admin
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **0.1.0** is the first release of controlled admin composition, implemented
4
+ from the approved [admin shell spec](../../docs/admin-shell-spec.md). The host
5
+ owns data, routes and commands.
6
+
7
+ `AdminNavigation` projects declared destinations through dashboard `NavRail`.
8
+ `AdminContent` supplies the inner page region for an existing host shell.
9
+ `AdminShell` combines those with the existing base `AppShell`, dashboard
10
+ `PageHeader` and `ListPageLayout`. Use one viewport shell and one page scroll
11
+ owner; embedded apps use `AdminContent` rather than nesting `AdminShell`.
12
+
13
+ | Page | Composition |
14
+ | --- | --- |
15
+ | Dashboard | App identity, declaration revision and links to canonical pages/groups; no config values or runtime KPIs. |
16
+ | Settings | One `SettingsProvenanceRenderer` for the host-selected group; source, locks, scope and pending application stay here. |
17
+ | Settings, `mode: 'setup'` | Existing `SettingsWizard` over the same groups/drafts, replacing the normal editor. Host controls step/checks/results; optional `setupContext` supplies provenance summaries beside it. Group links return to normal Settings. |
18
+ | Status | Existing `HealthSummary` and `StatCollection`; no configuration mutation controls. |
19
+ | Diagnostics | Existing `DiagnosticPanel`; an explicitly supplied series renderer receives existing `SampleSeriesViewProps`. |
20
+
21
+ ## Controlled host inputs
22
+
23
+ ```tsx
24
+ import { AdminShell } from '@hollis-labs/kit-admin'
25
+ import type { AdminContentProps } from '@hollis-labs/kit-admin'
26
+
27
+ // The application supplies validated projections and its own adapters.
28
+ export function AppAdmin({ state }: { state: AdminContentProps }) {
29
+ return <AdminShell {...state} />
30
+ }
31
+ ```
32
+
33
+ The host supplies `contextKey`, discovery phase/manifest, page/group/mode
34
+ selection, `destination(target)` returning either an `href` or `onSelect`,
35
+ per-group settings read states, optional settings callbacks, observation maps
36
+ and `nowMs`. `AdminManifest` is a minimal **presentation projection**, not a wire
37
+ registry or complete manifest validator. Transport fields/endpoints and wire
38
+ identity remain with the host and go-envelopes. `adminManifestProblem` fences
39
+ presentation identity, required arrays, contract version, resource placement
40
+ and observation age; supported settings schemas/provenance are checked by
41
+ kit-settings. The host validates all wire schemas, units, bounds, capabilities,
42
+ auth and diagnostic payloads before supplying these projections.
43
+
44
+ Keep all supplied data scoped to the current authorized `contextKey`; a discovery
45
+ context mismatch hides old content globally. Clear old snapshots/drafts and
46
+ cancel requests on auth/context changes. Host-owned routes/history, fetching,
47
+ retry/poll schedules, clocks, effective permissions, per-group revisions/ETags,
48
+ 409/412 reconciliation and uncertain write outcomes never move into this kit.
49
+ Accept and clear only successful group drafts. Reset means host resolution,
50
+ not a browser-computed fallback. Secret replacements remain transient in host
51
+ memory; never persist/log them or infer their values from declarations.
52
+
53
+ No writes occur on mount. Callbacks receive existing controlled settings intents.
54
+ `onApply` is omitted by default; an authorized host may explicitly supply its own
55
+ workflow. Save/setup completion never implies restart, readiness or health. The
56
+ host preserves per-field pending state independently of this command's
57
+ `restart_required` metadata, including no-op outcomes.
58
+
59
+ Failed initial discovery is globally unavailable. Retained declarations during
60
+ refresh/error are labelled and disable editing and command callbacks. A failed
61
+ individual group read disables only that group; sibling destinations remain
62
+ available. Contradictory sections are rejected, never relocated. Missing arrays
63
+ are malformed; empty arrays omit optional nav items and explain unsupported deep
64
+ links. All-empty Dashboard says “No admin resources declared”. Missing observation
65
+ snapshots stay unavailable, never fabricated healthy or zero values.
66
+
67
+ ## Direct directory and editor compositions
68
+
69
+ For application-owned records, use `AdminDirectory`, `AdminRecordButton`,
70
+ `AdminFacts` and `IdentityAvatar` without an admin manifest. Directory `layout`
71
+ can be `balanced` (default), `wide` or `compact`; `size="comfortable"` on a row
72
+ uses the larger named type steps. The host owns filtering, selection and data.
73
+
74
+ `AdminSection`, `AdminHeading`, `AdminFact`, `AdminBadge`, `AdminActionButton`,
75
+ `AdminTabs`, `AdminField`, `AdminInput`, `AdminSelect` and `AdminTextarea` compose
76
+ configuration workspaces. Tabs are controlled and use the base keyboard
77
+ behavior; disabled tabs remain discoverable by keyboard but cannot activate.
78
+ The host renders the selected content and retains draft lifetime. Buttons
79
+ default to `type="button"`; form submission must be explicit. Fields accept
80
+ host-computed validation messages, never calculate policy or send requests.
81
+
82
+ Activity surfaces use `ActivityGroup`, `ActivityRecord`, `ActivityMetric`,
83
+ `ActivityStatus`, `ActivityDialog`, `ActivityFacts`, `ActivitySection` and
84
+ `ActivityActionButton`. `ActivityTimeline` and `ActivityBars` accept normalized
85
+ observations and nullable samples; they do not invent history or run a clock.
86
+ The inspector delegates focus, dismissal and scrolling to the base dialog.
87
+
88
+ These compositions use the shared token contract and dashboard typography.
89
+ Import `@hollis-labs/kit-dashboard/theme.css` and `@hollis-labs/kit-admin/source.css`
90
+ in the consumer's stylesheet. Parallax exercises captured activity; Tachyon's
91
+ isolated Builder frontend exercises live reads and preserves host-owned commands.
92
+
93
+ ## Optional charts and CSS
94
+
95
+ Root imports no chart library and re-exports no sibling components.
96
+ Opt in explicitly, or supply another renderer with the same existing props:
97
+
98
+ ```tsx
99
+ import { renderAdminSeries } from '@hollis-labs/kit-admin/charts'
100
+ // <AdminShell {...state} renderSeries={renderAdminSeries} />
101
+ ```
102
+
103
+ The adapter delegates to kit-observe `/charts`, which delegates to dashboard
104
+ `TimestampSampleChart`; it does not copy chart code or synthesize history.
105
+ Dashboard remains a dependency for navigation/layout even without series.
106
+ A host using Tailwind v4 imports the theme and each composition's class sources:
107
+
108
+ ```css
109
+ @import '@hollis-labs/kit-dashboard/theme.css';
110
+ @import '@hollis-labs/kit-settings/source.css';
111
+ @import '@hollis-labs/kit-observe/source.css';
112
+ @import '@hollis-labs/kit-admin/source.css';
113
+ ```
114
+
115
+ Set the host's theme and document shell reset as usual. Style evidence uses
116
+ `sysop-p4-white` and includes dashboard CSS. Internal ranges are components and
117
+ dashboard `^0.4.0`, settings `^0.2.0`, observe `^0.1.1`, tokens `^0.4.0`; React 19,
118
+ Tailwind 4, Base UI and lucide use the workspace's existing peers.
119
+
120
+ ## Local fixture review
121
+
122
+ ```bash
123
+ npm run build --workspace=@hollis-labs/kit-admin
124
+ npm run demo:build --workspace=@hollis-labs/kit-admin
125
+ npm run demo --workspace=@hollis-labs/kit-admin -- --host 127.0.0.1
126
+ ```
127
+
128
+ Package-local `prebuild` builds dashboard/settings/observe prerequisites because
129
+ npm's root workspace order visits kit-admin first. No root build script changes.
130
+ The demo imports the built kit-admin entrypoints. Both approved manifest examples
131
+ render all four pages; `?app=nanite|tachyon|tether&page=...` chooses a fixture.
132
+ `mode=setup`, `failure=first|refresh|group` and `embedded=yes` exercise alternate
133
+ composition/failure modes. The separate `settings-only.html` entry imports root
134
+ only. Build emits a manual module inventory for reviewing chart isolation.
135
+ Fixture settings snapshots are explicit existing kit-settings fixtures, not
136
+ manifest-derived defaults. Command callbacks record intent only, without saved
137
+ outcomes, persistence, actual validation, secret logging or restart.
138
+
139
+ [Browser evidence and reproduction](docs/evidence.md) covers the approved
140
+ examples at 1280/390 and a Tether-style desired-config/runtime split. Unit tests
141
+ exercise fail-closed behavior, controlled callbacks, alternate setup, field
142
+ focus and canonical placement. Fixtures establish page/state mapping,
143
+ independently of the real-backend proof below. App-owned wire adapters, Folio
144
+ wiring and application adoption remain separate work.
145
+
146
+ ## Proof and limits
147
+
148
+ Packed consumers have been exercised in Chromium against fixtures and against
149
+ one real Nanite backend in a **read-only configuration**: the page issues only
150
+ GET requests. The real-backend proof used an isolated scratch instance built
151
+ from production code, not a deployed production instance.
152
+
153
+ Not proven: writes through kit-admin against a real backend; production Tachyon rollout;
154
+ secret, wizard or conflict flows on a real backend; other browsers; or a shared
155
+ typed read client. Backend atomicity and two-app acceptance are not established
156
+ by the read-only Nanite proof.
157
+
158
+ ## Configuration typography
159
+
160
+ Editor headings, facts, badges, tabs and field messages consume the compact
161
+ roles from kit-dashboard. AdminInput and AdminTextarea use the same dashboard
162
+ control type as native selects, including mobile breakpoints. Directory rows
163
+ use shared record title/metadata typography; their legacy `size` prop remains
164
+ accepted but no longer bypasses the typography profile. Font family and domain
165
+ layout remain separate choices. Use dashboard roles for host-supplied copy too:
166
+ core `text-*` utilities choose a different, root-relative scale.
167
+
168
+ IdentityAvatar configuration sizes (`small`, `definition`, `large`) follow shared
169
+ compact dimensions of 28/36/64px, with spacing-relative values in the scalable
170
+ profile. Activity `default` and `detail` sizes retain their existing scaling.
@@ -0,0 +1,42 @@
1
+ import { jsx as e, jsxs as a } from "react/jsx-runtime";
2
+ import { cn as g, Dialog as b, DialogContent as p, DialogHeader as u, DialogTitle as h, DialogDescription as x } from "@hollis-labs/design-components";
3
+ import { ActivityMetric as v } from "./activity-directory.js";
4
+ function y({ open: t, onClose: r, title: i, avatar: o, description: l, metadata: n, status: c, accessory: s, score: d, children: m }) {
5
+ return /* @__PURE__ */ e(b, { open: t, onOpenChange: (f) => {
6
+ f || r();
7
+ }, children: /* @__PURE__ */ a(p, { widthClassName: "max-w-2xl", children: [
8
+ /* @__PURE__ */ e(u, { children: /* @__PURE__ */ a("div", { className: "flex flex-wrap items-center gap-4", children: [
9
+ /* @__PURE__ */ e("span", { className: "shrink-0 border-2 border-fg-faint/50 bg-bg p-1.5", children: o }),
10
+ /* @__PURE__ */ a("div", { className: "min-w-0 flex-1 basis-32", children: [
11
+ /* @__PURE__ */ e(h, { className: "break-words font-mono text-base font-medium uppercase leading-none tracking-wide", children: i }),
12
+ /* @__PURE__ */ e(x, { className: "mt-1 break-words font-mono text-label tracking-wider", children: l }),
13
+ n && /* @__PURE__ */ e("div", { className: "break-words font-mono text-label tracking-wider text-fg-muted", children: n }),
14
+ /* @__PURE__ */ e("div", { className: "mt-1", children: c })
15
+ ] }),
16
+ /* @__PURE__ */ a("div", { className: "ml-auto flex shrink-0 items-start gap-4 pt-4 text-right", children: [
17
+ s,
18
+ d
19
+ ] })
20
+ ] }) }),
21
+ m
22
+ ] }) });
23
+ }
24
+ function A({ label: t = "Activity facts", items: r }) {
25
+ return /* @__PURE__ */ e("div", { role: "group", "aria-label": t, className: "grid grid-cols-2 gap-3 border-y border-border py-3 sm:grid-cols-4", children: r.map((i) => /* @__PURE__ */ e(v, { ...i }, i.label)) });
26
+ }
27
+ function D({ title: t, children: r, spacing: i = "default" }) {
28
+ return /* @__PURE__ */ a("section", { "aria-label": t, className: i === "tight" ? "mt-1" : "mt-3", children: [
29
+ /* @__PURE__ */ e("h3", { className: "mb-1 font-mono text-caption font-normal uppercase tracking-widest text-fg-faint", children: t }),
30
+ r
31
+ ] });
32
+ }
33
+ function C({ className: t, type: r = "button", ...i }) {
34
+ return /* @__PURE__ */ e("button", { ...i, type: r, className: g("border border-border px-2.5 py-1 font-mono text-caption uppercase tracking-widest text-fg-muted transition-colors hover:border-fg-faint hover:text-fg focus-visible:bg-panel-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-40", t) });
35
+ }
36
+ export {
37
+ C as ActivityActionButton,
38
+ y as ActivityDialog,
39
+ A as ActivityFacts,
40
+ D as ActivitySection
41
+ };
42
+ //# sourceMappingURL=activity-dialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity-dialog.js","sources":["../src/activity-dialog.tsx"],"sourcesContent":["import type { ComponentProps, ReactNode } from 'react'\nimport { cn, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@hollis-labs/design-components'\nimport { ActivityMetric } from './activity-directory'\n\n/** Content-sized activity inspection. The host owns selection and evidence;\n * the dialog primitive owns focus, Escape and the viewport's scrolling. */\nexport function ActivityDialog({ open, onClose, title, avatar, description, metadata, status, accessory, score, children }: {\n open: boolean; onClose: () => void; title: string; avatar: ReactNode;\n description: ReactNode; metadata?: ReactNode; status: ReactNode;\n accessory?: ReactNode; score?: ReactNode; children: ReactNode\n}) {\n return <Dialog open={open} onOpenChange={next => { if (!next) onClose() }}>\n <DialogContent widthClassName=\"max-w-2xl\">\n <DialogHeader>\n <div className=\"flex flex-wrap items-center gap-4\">\n <span className=\"shrink-0 border-2 border-fg-faint/50 bg-bg p-1.5\">{avatar}</span>\n <div className=\"min-w-0 flex-1 basis-32\">\n <DialogTitle className=\"break-words font-mono text-base font-medium uppercase leading-none tracking-wide\">{title}</DialogTitle>\n <DialogDescription className=\"mt-1 break-words font-mono text-label tracking-wider\">{description}</DialogDescription>\n {metadata && <div className=\"break-words font-mono text-label tracking-wider text-fg-muted\">{metadata}</div>}\n <div className=\"mt-1\">{status}</div>\n </div>\n <div className=\"ml-auto flex shrink-0 items-start gap-4 pt-4 text-right\">\n {accessory}\n {score}\n </div>\n </div>\n </DialogHeader>\n {children}\n </DialogContent>\n </Dialog>\n}\n\n/** Dense value-first facts, with full values available as text/title. */\nexport function ActivityFacts({ label = 'Activity facts', items }: {\n label?: string; items: { label: string; value: ReactNode; title?: string }[]\n}) {\n return <div role=\"group\" aria-label={label} className=\"grid grid-cols-2 gap-3 border-y border-border py-3 sm:grid-cols-4\">\n {items.map(item => <ActivityMetric key={item.label} {...item} />)}\n </div>\n}\n\n/** Compact observation/action section, without an additional card surface. */\nexport function ActivitySection({ title, children, spacing = 'default' }: {\n title: string; children: ReactNode; spacing?: 'default' | 'tight'\n}) {\n return <section aria-label={title} className={spacing === 'tight' ? 'mt-1' : 'mt-3'}>\n <h3 className=\"mb-1 font-mono text-caption font-normal uppercase tracking-widest text-fg-faint\">{title}</h3>\n {children}\n </section>\n}\n\n/** Small outlined action for activity inspectors; availability is host-owned. */\nexport function ActivityActionButton({ className, type = 'button', ...props }: ComponentProps<'button'>) {\n return <button {...props} type={type} className={cn('border border-border px-2.5 py-1 font-mono text-caption uppercase tracking-widest text-fg-muted transition-colors hover:border-fg-faint hover:text-fg focus-visible:bg-panel-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-40', className)} />\n}\n"],"names":["ActivityDialog","open","onClose","title","avatar","description","metadata","status","accessory","score","children","jsx","Dialog","next","jsxs","DialogContent","DialogHeader","DialogTitle","DialogDescription","ActivityFacts","label","items","item","ActivityMetric","ActivitySection","spacing","ActivityActionButton","className","type","props","cn"],"mappings":";;;AAMO,SAASA,EAAe,EAAE,MAAAC,GAAM,SAAAC,GAAS,OAAAC,GAAO,QAAAC,GAAQ,aAAAC,GAAa,UAAAC,GAAU,QAAAC,GAAQ,WAAAC,GAAW,OAAAC,GAAO,UAAAC,EAAA,GAI7G;AACD,SAAO,gBAAAC,EAACC,GAAA,EAAO,MAAAX,GAAY,cAAc,CAAAY,MAAQ;AAAE,IAAKA,KAAMX,EAAA;AAAA,EAAU,GACtE,UAAA,gBAAAY,EAACC,GAAA,EAAc,gBAAe,aAC5B,UAAA;AAAA,IAAA,gBAAAJ,EAACK,GAAA,EACC,UAAA,gBAAAF,EAAC,OAAA,EAAI,WAAU,qCACb,UAAA;AAAA,MAAA,gBAAAH,EAAC,QAAA,EAAK,WAAU,oDAAoD,UAAAP,GAAO;AAAA,MAC3E,gBAAAU,EAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,QAAA,gBAAAH,EAACM,GAAA,EAAY,WAAU,oFAAoF,UAAAd,GAAM;AAAA,QACjH,gBAAAQ,EAACO,GAAA,EAAkB,WAAU,wDAAwD,UAAAb,GAAY;AAAA,QAChGC,KAAY,gBAAAK,EAAC,OAAA,EAAI,WAAU,iEAAiE,UAAAL,GAAS;AAAA,QACtG,gBAAAK,EAAC,OAAA,EAAI,WAAU,QAAQ,UAAAJ,EAAA,CAAO;AAAA,MAAA,GAChC;AAAA,MACA,gBAAAO,EAAC,OAAA,EAAI,WAAU,2DACZ,UAAA;AAAA,QAAAN;AAAA,QACAC;AAAA,MAAA,EAAA,CACH;AAAA,IAAA,EAAA,CACF,EAAA,CACF;AAAA,IACCC;AAAA,EAAA,EAAA,CACH,EAAA,CACF;AACF;AAGO,SAASS,EAAc,EAAE,OAAAC,IAAQ,kBAAkB,OAAAC,KAEvD;AACD,2BAAQ,OAAA,EAAI,MAAK,SAAQ,cAAYD,GAAO,WAAU,qEACnD,UAAAC,EAAM,IAAI,CAAAC,wBAASC,GAAA,EAAiC,GAAGD,KAAhBA,EAAK,KAAiB,CAAE,GAClE;AACF;AAGO,SAASE,EAAgB,EAAE,OAAArB,GAAO,UAAAO,GAAU,SAAAe,IAAU,aAE1D;AACD,SAAO,gBAAAX,EAAC,aAAQ,cAAYX,GAAO,WAAWsB,MAAY,UAAU,SAAS,QAC3E,UAAA;AAAA,IAAA,gBAAAd,EAAC,MAAA,EAAG,WAAU,mFAAmF,UAAAR,GAAM;AAAA,IACtGO;AAAA,EAAA,GACH;AACF;AAGO,SAASgB,EAAqB,EAAE,WAAAC,GAAW,MAAAC,IAAO,UAAU,GAAGC,KAAmC;AACvG,SAAO,gBAAAlB,EAAC,YAAQ,GAAGkB,GAAO,MAAAD,GAAY,WAAWE,EAAG,6PAA6PH,CAAS,GAAG;AAC/T;"}
@@ -0,0 +1,63 @@
1
+ import { jsxs as s, jsx as e } from "react/jsx-runtime";
2
+ import { cn as l } from "@hollis-labs/design-components";
3
+ function N({ title: n, metadata: a, summary: t, children: r }) {
4
+ return /* @__PURE__ */ s("section", { "aria-label": n, className: "min-w-0", children: [
5
+ /* @__PURE__ */ s("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1 border-y border-border bg-panel px-4 py-1.5 font-mono", children: [
6
+ /* @__PURE__ */ e("h2", { className: "text-label font-normal uppercase tracking-widest", children: n }),
7
+ a && /* @__PURE__ */ e("span", { className: "min-w-0 break-all dash-record-metadata text-fg-faint", children: a }),
8
+ t && /* @__PURE__ */ e("span", { className: "ml-auto dash-support tabular-nums text-warning", children: t })
9
+ ] }),
10
+ r
11
+ ] });
12
+ }
13
+ function w({ label: n, value: a, title: t, highlight: r = !1, size: i = "default" }) {
14
+ return /* @__PURE__ */ s("span", { className: "block min-w-0", title: t, children: [
15
+ /* @__PURE__ */ e("span", { className: l("block truncate font-mono tabular-nums", i === "prominent" ? "text-xl" : "text-sm", r && "text-warning"), children: a }),
16
+ /* @__PURE__ */ e("span", { className: "block dash-shortcut font-mono uppercase tracking-widest text-fg-faint", children: n })
17
+ ] });
18
+ }
19
+ function k({ label: n, detail: a, tone: t = "muted" }) {
20
+ const r = { positive: "text-success", attention: "text-warning", neutral: "text-info", muted: "text-fg-faint" }[t];
21
+ return /* @__PURE__ */ s("span", { className: l("inline-flex flex-wrap items-center gap-1.5 font-mono dash-caption leading-normal uppercase tracking-widest", r), children: [
22
+ /* @__PURE__ */ e("span", { className: "size-1.5 shrink-0 bg-current", "aria-hidden": !0 }),
23
+ n,
24
+ a && /* @__PURE__ */ s("span", { className: "normal-case tracking-normal text-fg-faint", children: [
25
+ "· ",
26
+ a
27
+ ] })
28
+ ] });
29
+ }
30
+ function v({ label: n, onSelect: a, rank: t, avatar: r, name: i, metadata: c, description: o, status: f, metrics: d, history: m, accessory: p, score: h, muted: u = !1, attention: x = !1 }) {
31
+ return /* @__PURE__ */ e("li", { className: "border-b border-border", children: /* @__PURE__ */ s(
32
+ "button",
33
+ {
34
+ type: "button",
35
+ "aria-label": n,
36
+ onClick: a,
37
+ className: l("group flex w-full flex-wrap items-center gap-x-4 gap-y-2 px-4 py-3 text-left hover:bg-panel-2 focus-visible:bg-panel-2 focus-visible:outline-none", x && "bg-warning-muted", u && "opacity-70"),
38
+ children: [
39
+ /* @__PURE__ */ e("span", { className: "w-8 shrink-0 text-center dash-record-metadata text-fg-faint", children: t }),
40
+ /* @__PURE__ */ e("span", { className: "shrink-0 border-2 border-fg-faint/50 bg-bg p-1 group-hover:border-fg-faint", children: r }),
41
+ /* @__PURE__ */ s("span", { className: "flex min-h-16 min-w-0 flex-1 basis-48 flex-col justify-center", children: [
42
+ /* @__PURE__ */ s("span", { className: "flex flex-wrap items-baseline gap-x-2", children: [
43
+ /* @__PURE__ */ e("span", { className: "min-w-0 truncate font-mono text-sm uppercase tracking-wide", children: /* @__PURE__ */ e("strong", { className: "font-semibold", children: i }) }),
44
+ c && /* @__PURE__ */ e("span", { className: "min-w-0 truncate dash-record-metadata uppercase text-fg-faint", children: c })
45
+ ] }),
46
+ o && /* @__PURE__ */ e("span", { className: "mt-0.5 block truncate text-xs leading-4.5 text-fg-muted", children: o }),
47
+ f
48
+ ] }),
49
+ d && /* @__PURE__ */ e("span", { className: "hidden shrink-0 gap-4 sm:flex", children: d }),
50
+ m && /* @__PURE__ */ e("span", { className: "hidden w-30 shrink-0 md:block", children: m }),
51
+ p && /* @__PURE__ */ e("span", { className: "flex w-4 shrink-0 justify-center", children: p }),
52
+ /* @__PURE__ */ e("span", { className: "w-20 shrink-0 text-right", children: h })
53
+ ]
54
+ }
55
+ ) });
56
+ }
57
+ export {
58
+ N as ActivityGroup,
59
+ w as ActivityMetric,
60
+ v as ActivityRecord,
61
+ k as ActivityStatus
62
+ };
63
+ //# sourceMappingURL=activity-directory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity-directory.js","sources":["../src/activity-directory.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { cn } from '@hollis-labs/design-components'\n\n/** Compact grouped activity list. Ranking and aggregation remain host-owned. */\nexport function ActivityGroup({ title, metadata, summary, children }: {\n title: string; metadata?: ReactNode; summary?: ReactNode; children: ReactNode\n}) {\n return <section aria-label={title} className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1 border-y border-border bg-panel px-4 py-1.5 font-mono\">\n <h2 className=\"text-label font-normal uppercase tracking-widest\">{title}</h2>\n {metadata && <span className=\"min-w-0 break-all dash-record-metadata text-fg-faint\">{metadata}</span>}\n {summary && <span className=\"ml-auto dash-support tabular-nums text-warning\">{summary}</span>}\n </div>\n {children}\n </section>\n}\n\n/** Value-first metric, shared by activity rows and detail summaries. */\nexport function ActivityMetric({ label, value, title, highlight = false, size = 'default' }: {\n label: string; value: ReactNode; title?: string; highlight?: boolean; size?: 'default' | 'prominent'\n}) {\n return <span className=\"block min-w-0\" title={title}>\n <span className={cn('block truncate font-mono tabular-nums', size === 'prominent' ? 'text-xl' : 'text-sm', highlight && 'text-warning')}>{value}</span>\n <span className=\"block dash-shortcut font-mono uppercase tracking-widest text-fg-faint\">{label}</span>\n </span>\n}\n\n/** Unboxed status for dense activity lists. A label always accompanies the dot. */\nexport function ActivityStatus({ label, detail, tone = 'muted' }: {\n label: string; detail?: string; tone?: 'positive' | 'attention' | 'neutral' | 'muted'\n}) {\n const color = { positive: 'text-success', attention: 'text-warning', neutral: 'text-info', muted: 'text-fg-faint' }[tone]\n return <span className={cn('inline-flex flex-wrap items-center gap-1.5 font-mono dash-caption leading-normal uppercase tracking-widest', color)}>\n <span className=\"size-1.5 shrink-0 bg-current\" aria-hidden />\n {label}\n {detail && <span className=\"normal-case tracking-normal text-fg-faint\">· {detail}</span>}\n </span>\n}\n\n/** One inspectable identity with aligned ranks, metrics and optional history.\n * Slots are presentation only; the host supplies evidence and handles selection. */\nexport function ActivityRecord({ label, onSelect, rank, avatar, name, metadata, description, status, metrics, history, accessory, score, muted = false, attention = false }: {\n label: string; onSelect: () => void; rank: ReactNode; avatar: ReactNode;\n name: string; metadata?: ReactNode; description?: ReactNode; status: ReactNode;\n metrics?: ReactNode; history?: ReactNode; accessory?: ReactNode; score: ReactNode;\n muted?: boolean; attention?: boolean\n}) {\n return <li className=\"border-b border-border\">\n <button type=\"button\" aria-label={label} onClick={onSelect}\n className={cn('group flex w-full flex-wrap items-center gap-x-4 gap-y-2 px-4 py-3 text-left hover:bg-panel-2 focus-visible:bg-panel-2 focus-visible:outline-none', attention && 'bg-warning-muted', muted && 'opacity-70')}>\n <span className=\"w-8 shrink-0 text-center dash-record-metadata text-fg-faint\">{rank}</span>\n <span className=\"shrink-0 border-2 border-fg-faint/50 bg-bg p-1 group-hover:border-fg-faint\">{avatar}</span>\n <span className=\"flex min-h-16 min-w-0 flex-1 basis-48 flex-col justify-center\">\n <span className=\"flex flex-wrap items-baseline gap-x-2\">\n <span className=\"min-w-0 truncate font-mono text-sm uppercase tracking-wide\"><strong className=\"font-semibold\">{name}</strong></span>\n {metadata && <span className=\"min-w-0 truncate dash-record-metadata uppercase text-fg-faint\">{metadata}</span>}\n </span>\n {description && <span className=\"mt-0.5 block truncate text-xs leading-4.5 text-fg-muted\">{description}</span>}\n {status}\n </span>\n {metrics && <span className=\"hidden shrink-0 gap-4 sm:flex\">{metrics}</span>}\n {history && <span className=\"hidden w-30 shrink-0 md:block\">{history}</span>}\n {accessory && <span className=\"flex w-4 shrink-0 justify-center\">{accessory}</span>}\n <span className=\"w-20 shrink-0 text-right\">{score}</span>\n </button>\n </li>\n}\n"],"names":["ActivityGroup","title","metadata","summary","children","jsxs","jsx","ActivityMetric","label","value","highlight","size","cn","ActivityStatus","detail","tone","color","ActivityRecord","onSelect","rank","avatar","name","description","status","metrics","history","accessory","score","muted","attention"],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { jsx as a, jsxs as s } from "react/jsx-runtime";
2
+ import { cn as c } from "@hollis-labs/design-components";
3
+ const h = { positive: "fill-success", attention: "fill-warning", neutral: "fill-info", muted: "fill-fg-faint" };
4
+ function f({ label: n, segments: e, size: r = "default" }) {
5
+ return /* @__PURE__ */ a(
6
+ "svg",
7
+ {
8
+ role: "img",
9
+ "aria-label": n,
10
+ viewBox: "0 0 100 1",
11
+ preserveAspectRatio: "none",
12
+ shapeRendering: "crispEdges",
13
+ className: c("w-full bg-panel-2", r === "compact" ? "h-1.5" : "h-7"),
14
+ children: e.map((t, i) => /* @__PURE__ */ a(
15
+ "rect",
16
+ {
17
+ x: Math.max(0, t.from) * 100,
18
+ y: 0,
19
+ width: Math.max(0, Math.min(1, t.to) - Math.max(0, t.from)) * 100,
20
+ height: 1,
21
+ className: h[t.tone],
22
+ children: /* @__PURE__ */ a("title", { children: t.label })
23
+ },
24
+ i
25
+ ))
26
+ }
27
+ );
28
+ }
29
+ function d({ label: n, values: e, tone: r = "positive" }) {
30
+ const t = e.length ? 120 / e.length : 120;
31
+ return /* @__PURE__ */ a("svg", { role: "img", "aria-label": n, viewBox: "0 0 120 22", preserveAspectRatio: "none", shapeRendering: "crispEdges", className: "h-5.5 w-full", children: e.map((i, l) => {
32
+ if (i === null) return null;
33
+ const o = Math.max(1, Math.round(Math.min(100, Math.max(0, i)) / 100 * 22));
34
+ return /* @__PURE__ */ a("rect", { x: l * t, y: 22 - o, width: Math.max(0, t - 1), height: o, className: c(h[r], i === 0 && "opacity-25"), children: /* @__PURE__ */ s("title", { children: [
35
+ Math.round(i),
36
+ "% working"
37
+ ] }) }, l);
38
+ }) });
39
+ }
40
+ export {
41
+ d as ActivityBars,
42
+ f as ActivityTimeline
43
+ };
44
+ //# sourceMappingURL=activity-history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity-history.js","sources":["../src/activity-history.tsx"],"sourcesContent":["import { cn } from '@hollis-labs/design-components'\n\nexport type ActivityTone = 'positive' | 'attention' | 'neutral' | 'muted'\nconst fills = { positive: 'fill-success', attention: 'fill-warning', neutral: 'fill-info', muted: 'fill-fg-faint' }\n\n/** Normalized observed intervals. Empty areas remain unpainted, not idle. */\nexport function ActivityTimeline({ label, segments, size = 'default' }: {\n label: string; segments: { from: number; to: number; label: string; tone: ActivityTone }[];\n size?: 'default' | 'compact'\n}) {\n return <svg role=\"img\" aria-label={label} viewBox=\"0 0 100 1\" preserveAspectRatio=\"none\" shapeRendering=\"crispEdges\"\n className={cn('w-full bg-panel-2', size === 'compact' ? 'h-1.5' : 'h-7')}>\n {segments.map((segment, i) => <rect key={i} x={Math.max(0, segment.from) * 100} y={0}\n width={Math.max(0, Math.min(1, segment.to) - Math.max(0, segment.from)) * 100} height={1} className={fills[segment.tone]}>\n <title>{segment.label}</title>\n </rect>)}\n </svg>\n}\n\n/** A frozen activity sparkline. null buckets are unobserved; zero is observed. */\nexport function ActivityBars({ label, values, tone = 'positive' }: { label: string; values: (number | null)[]; tone?: ActivityTone }) {\n const step = values.length ? 120 / values.length : 120\n return <svg role=\"img\" aria-label={label} viewBox=\"0 0 120 22\" preserveAspectRatio=\"none\" shapeRendering=\"crispEdges\" className=\"h-5.5 w-full\">\n {values.map((value, i) => {\n if (value === null) return null\n const height = Math.max(1, Math.round(Math.min(100, Math.max(0, value)) / 100 * 22))\n return <rect key={i} x={i * step} y={22 - height} width={Math.max(0, step - 1)} height={height} className={cn(fills[tone], value === 0 && 'opacity-25')}>\n <title>{Math.round(value)}% working</title>\n </rect>\n })}\n </svg>\n}\n"],"names":["fills","ActivityTimeline","label","segments","size","jsx","cn","segment","ActivityBars","values","tone","step","value","i","height","jsxs"],"mappings":";;AAGA,MAAMA,IAAQ,EAAE,UAAU,gBAAgB,WAAW,gBAAgB,SAAS,aAAa,OAAO,gBAAA;AAG3F,SAASC,EAAiB,EAAE,OAAAC,GAAO,UAAAC,GAAU,MAAAC,IAAO,aAGxD;AACD,SAAO,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MAAI,MAAK;AAAA,MAAM,cAAYH;AAAA,MAAO,SAAQ;AAAA,MAAY,qBAAoB;AAAA,MAAO,gBAAe;AAAA,MACtG,WAAWI,EAAG,qBAAqBF,MAAS,YAAY,UAAU,KAAK;AAAA,MACtE,UAAAD,EAAS,IAAI,CAACI,GAAS,MAAM,gBAAAF;AAAA,QAAC;AAAA,QAAA;AAAA,UAAa,GAAG,KAAK,IAAI,GAAGE,EAAQ,IAAI,IAAI;AAAA,UAAK,GAAG;AAAA,UACjF,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAGA,EAAQ,EAAE,IAAI,KAAK,IAAI,GAAGA,EAAQ,IAAI,CAAC,IAAI;AAAA,UAAK,QAAQ;AAAA,UAAG,WAAWP,EAAMO,EAAQ,IAAI;AAAA,UACvH,UAAA,gBAAAF,EAAC,SAAA,EAAO,UAAAE,EAAQ,MAAA,CAAM;AAAA,QAAA;AAAA,QAFiB;AAAA,MAAA,CAGlC;AAAA,IAAA;AAAA,EAAA;AAEX;AAGO,SAASC,EAAa,EAAE,OAAAN,GAAO,QAAAO,GAAQ,MAAAC,IAAO,cAAiF;AACpI,QAAMC,IAAOF,EAAO,SAAS,MAAMA,EAAO,SAAS;AACnD,2BAAQ,OAAA,EAAI,MAAK,OAAM,cAAYP,GAAO,SAAQ,cAAa,qBAAoB,QAAO,gBAAe,cAAa,WAAU,gBAC7H,YAAO,IAAI,CAACU,GAAOC,MAAM;AACxB,QAAID,MAAU,KAAM,QAAO;AAC3B,UAAME,IAAS,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,IAAI,KAAK,KAAK,IAAI,GAAGF,CAAK,CAAC,IAAI,MAAM,EAAE,CAAC;AACnF,WAAO,gBAAAP,EAAC,QAAA,EAAa,GAAGQ,IAAIF,GAAM,GAAG,KAAKG,GAAQ,OAAO,KAAK,IAAI,GAAGH,IAAO,CAAC,GAAG,QAAAG,GAAgB,WAAWR,EAAGN,EAAMU,CAAI,GAAGE,MAAU,KAAK,YAAY,GACpJ,UAAA,gBAAAG,EAAC,SAAA,EAAO,UAAA;AAAA,MAAA,KAAK,MAAMH,CAAK;AAAA,MAAE;AAAA,IAAA,EAAA,CAAS,KADnBC,CAElB;AAAA,EACF,CAAC,EAAA,CACH;AACF;"}
package/dist/admin.js ADDED
@@ -0,0 +1,160 @@
1
+ import { jsx as e, jsxs as m, Fragment as $ } from "react/jsx-runtime";
2
+ import { SquareTerminal as j, Activity as O, Settings as V, LayoutDashboard as z } from "lucide-react";
3
+ import { Callout as v, AppShell as I, Button as E } from "@hollis-labs/design-components";
4
+ import { NavRail as M, PageHeader as W } from "@hollis-labs/kit-dashboard/ui";
5
+ import { ListPageLayout as G } from "@hollis-labs/kit-dashboard/layout";
6
+ import { settingsMetadataProblem as D, SettingsProvenanceRenderer as H, SettingsWizard as q } from "@hollis-labs/kit-settings";
7
+ import { HealthSummary as B, ObservationStatus as S, StatCollection as F, DiagnosticPanel as U } from "@hollis-labs/kit-observe";
8
+ import { adminManifestProblem as P, adminPages as R } from "./model.js";
9
+ const y = { dashboard: "Dashboard", settings: "Settings", status: "Status", diagnostics: "Diagnostics" }, J = { dashboard: z, settings: V, status: O, diagnostics: j };
10
+ function K(i) {
11
+ const { discovery: n, contextKey: c } = i;
12
+ if (!(n.contextKey !== c || P(n.manifest)))
13
+ return n.manifest;
14
+ }
15
+ function L({ destination: i, children: n }) {
16
+ return i.href !== void 0 ? /* @__PURE__ */ e("a", { className: "break-words text-control text-primary underline underline-offset-2 focus-visible:outline-ring", href: i.href, children: n }) : /* @__PURE__ */ e(E, { type: "button", size: "sm", variant: "outline", onClick: i.onSelect, children: n });
17
+ }
18
+ function f({ children: i }) {
19
+ return /* @__PURE__ */ e("p", { role: "status", className: "text-control text-fg-secondary", children: i });
20
+ }
21
+ function T({ manifest: i, props: n }) {
22
+ return /* @__PURE__ */ e("nav", { "aria-label": "Settings groups", className: "flex flex-wrap gap-3", children: i.settings.map((c) => /* @__PURE__ */ e(L, { destination: n.destination({ page: "settings", groupId: c.id }), children: c.label }, c.id)) });
23
+ }
24
+ function Q(i) {
25
+ const n = K(i), c = n ? R(n) : ["dashboard"];
26
+ return /* @__PURE__ */ e(M, { logoLabel: (n == null ? void 0 : n.app.label) ?? "Admin", items: c.map((l) => {
27
+ const a = J[l];
28
+ return { key: l, label: y[l], icon: /* @__PURE__ */ e(a, { className: "size-4", "aria-hidden": !0 }), active: i.selection.page === l, ...i.destination({ page: l }) };
29
+ }) });
30
+ }
31
+ function X({ props: i, manifest: n }) {
32
+ var N, A, _, C, k;
33
+ const { selection: c, settings: l = {}, settingsActions: a, setup: p } = i, o = i.discovery.phase === "ready", u = (d) => o && (d == null ? void 0 : d.phase) === "ready" && !!d.state, s = n.settings.find((d) => d.id === c.groupId), g = Object.fromEntries(n.settings.map((d) => {
34
+ var b;
35
+ return [d.id, (b = l[d.id]) == null ? void 0 : b.state];
36
+ })), t = (d, b) => {
37
+ u(l[d]) && (a == null || a.onDraftChange(d, b));
38
+ }, r = c.mode !== "setup";
39
+ let h = s, w = g;
40
+ const x = s && g[s.id];
41
+ return r && !a && s && x && !D(s, x) && (h = { ...s, capabilities: {
42
+ ...s.capabilities,
43
+ can_validate: !1,
44
+ can_update: !1,
45
+ can_reset: !1
46
+ } }, w = { ...g, [s.id]: { ...x, draft: {}, values: Object.fromEntries(
47
+ Object.entries(x.values).map(([d, b]) => [d, b.editable ? { ...b, editable: !1, read_only_reason: "This view has no editing actions." } : b])
48
+ ) } }), n.settings.length ? /* @__PURE__ */ m("section", { "aria-label": "Admin Settings", className: "flex min-w-0 flex-col gap-4", children: [
49
+ /* @__PURE__ */ e(T, { manifest: n, props: i }),
50
+ r && (s != null && s.scope) ? /* @__PURE__ */ m(f, { children: [
51
+ "Scope: ",
52
+ s.scope.kind,
53
+ s.scope.id ? ` / ${s.scope.id}` : ""
54
+ ] }) : null,
55
+ r ? c.groupId ? s ? /* @__PURE__ */ m($, { children: [
56
+ ((N = l[s.id]) == null ? void 0 : N.phase) === "error" ? /* @__PURE__ */ m(v, { tone: "danger", title: "Settings read failed", children: [
57
+ ((A = l[s.id]) == null ? void 0 : A.error) ?? "The host could not read this group.",
58
+ " ",
59
+ (_ = l[s.id]) != null && _.state ? "Previous snapshot retained; writes suspended." : "No successful snapshot is available."
60
+ ] }) : null,
61
+ ((C = l[s.id]) == null ? void 0 : C.phase) === "loading" ? /* @__PURE__ */ e(f, { children: (k = l[s.id]) != null && k.state ? "Refreshing settings snapshot; writes suspended." : "Waiting for a settings snapshot." }) : null,
62
+ /* @__PURE__ */ e("fieldset", { disabled: !u(l[s.id]) || !a, className: "min-w-0 border-0 p-0", children: /* @__PURE__ */ e(
63
+ H,
64
+ {
65
+ contractVersion: n.contract_version,
66
+ groups: [h],
67
+ states: w,
68
+ onDraftChange: t,
69
+ readOnlyContext: !a,
70
+ onSave: u(l[s.id]) && (a != null && a.onSave) ? a.onSave : void 0,
71
+ onReset: u(l[s.id]) && (a != null && a.onReset) ? a.onReset : void 0,
72
+ onValidate: u(l[s.id]) && (a != null && a.onValidate) ? a.onValidate : void 0,
73
+ onApply: u(l[s.id]) && (a != null && a.onApply) ? a.onApply : void 0
74
+ },
75
+ `${i.contextKey}:${n.app.id}:${n.revision}:${s.id}`
76
+ ) })
77
+ ] }) : /* @__PURE__ */ e(v, { tone: "warning", title: "Settings group unavailable", children: "This declaration does not contain the selected group." }) : /* @__PURE__ */ e(f, { children: "Choose a settings group." }) : p ? /* @__PURE__ */ m($, { children: [
78
+ /* @__PURE__ */ e(f, { children: "Setup edits the same Settings groups. Source, locks and pending application remain in canonical Settings." }),
79
+ i.setupContext,
80
+ n.settings.some((d) => !u(l[d.id]) || g[d.id] && D(d, g[d.id])) ? /* @__PURE__ */ e(v, { tone: "warning", title: "Setup unavailable", children: "Every setup group needs a current, supported settings snapshot and provenance. Normal Settings remains available per group." }) : /* @__PURE__ */ e("fieldset", { disabled: !o || !a, className: "min-w-0 border-0 p-0", children: /* @__PURE__ */ e(
81
+ q,
82
+ {
83
+ ...p,
84
+ contractVersion: n.contract_version,
85
+ groups: n.settings,
86
+ states: g,
87
+ onDraftChange: t,
88
+ busy: !o || !a || p.busy,
89
+ onComplete: (d) => {
90
+ o && a && p.onComplete(d);
91
+ },
92
+ onCheck: p.onCheck ? (d) => {
93
+ var b;
94
+ o && a && ((b = p.onCheck) == null || b.call(p, d));
95
+ } : void 0
96
+ },
97
+ `${i.contextKey}:${n.app.id}:${n.revision}:setup`
98
+ ) })
99
+ ] }) : /* @__PURE__ */ e(f, { children: "Setup unavailable: the host has not supplied setup controls." })
100
+ ] }) : /* @__PURE__ */ e(f, { children: "Settings unsupported: no settings groups declared." });
101
+ }
102
+ function Y(i) {
103
+ const { discovery: n, selection: c, observations: l = {}, nowMs: a } = i;
104
+ if (n.contextKey !== i.contextKey) return /* @__PURE__ */ e(v, { tone: "warning", title: "Admin context unavailable", children: "The host must supply discovery and snapshots for the current authorized context." });
105
+ if (!n.manifest) return n.phase === "loading" ? /* @__PURE__ */ e(f, { children: "Loading admin declarations." }) : /* @__PURE__ */ e(v, { tone: "danger", title: "Admin unavailable", children: n.error ?? "No admin declaration is available." });
106
+ const p = P(n.manifest);
107
+ if (p) return /* @__PURE__ */ e(v, { tone: "danger", title: "Admin declaration unavailable", children: p });
108
+ const o = n.manifest, u = (t) => ({ phase: "idle", nowMs: a, staleAfterMs: t }), s = R(o);
109
+ let g;
110
+ return c.page === "dashboard" ? g = /* @__PURE__ */ m("section", { "aria-label": "Admin directory", className: "flex min-w-0 flex-col gap-4", children: [
111
+ /* @__PURE__ */ e("h2", { className: "text-heading text-fg", children: o.app.label }),
112
+ /* @__PURE__ */ m(f, { children: [
113
+ "Declaration revision: ",
114
+ o.revision
115
+ ] }),
116
+ s.length === 1 ? /* @__PURE__ */ e(f, { children: "No admin resources declared" }) : /* @__PURE__ */ e("nav", { "aria-label": "Admin directory links", className: "flex flex-wrap gap-3", children: s.filter((t) => t !== "dashboard").map((t) => /* @__PURE__ */ e(L, { destination: i.destination({ page: t }), children: y[t] }, t)) }),
117
+ o.settings.length ? /* @__PURE__ */ e(T, { manifest: o, props: i }) : null
118
+ ] }) : c.page === "settings" ? g = /* @__PURE__ */ e(X, { props: i, manifest: o }) : c.page === "status" ? g = !o.health.length && !o.stats.length ? /* @__PURE__ */ e(f, { children: "Status unsupported: no health or stats resources declared." }) : /* @__PURE__ */ m("section", { "aria-label": "Admin Status", className: "flex min-w-0 flex-col gap-4", children: [
119
+ o.health.map((t) => {
120
+ var h;
121
+ const r = (h = l.health) == null ? void 0 : h[t.id];
122
+ return r ? /* @__PURE__ */ e(B, { ...r, label: t.label }, t.id) : /* @__PURE__ */ e(S, { label: t.label, observation: u(t.stale_after_ms) }, t.id);
123
+ }),
124
+ o.stats.length ? /* @__PURE__ */ e(F, { label: "Stats", rows: o.stats.map((t) => {
125
+ var h;
126
+ const r = (h = l.stats) == null ? void 0 : h[t.id];
127
+ return { id: t.id, label: t.label, unit: t.unit, kind: t.kind, value: (r == null ? void 0 : r.value) ?? null, observation: (r == null ? void 0 : r.observation) ?? u(t.stale_after_ms) };
128
+ }) }) : null
129
+ ] }) : c.page === "diagnostics" ? g = !o.series.length && !o.diagnostics.length ? /* @__PURE__ */ e(f, { children: "Diagnostics unsupported: no series or diagnostic resources declared." }) : /* @__PURE__ */ m("section", { "aria-label": "Admin Diagnostics", className: "flex min-w-0 flex-col gap-4", children: [
130
+ o.series.map((t) => {
131
+ var h;
132
+ const r = (h = l.series) == null ? void 0 : h[t.id];
133
+ return /* @__PURE__ */ e("section", { className: "min-w-0", children: i.renderSeries ? r ? i.renderSeries({ ...r, label: t.label, unit: t.unit, kind: t.kind, bounds: { maxPoints: t.max_points, maxWindowSeconds: t.max_window_seconds } }) : /* @__PURE__ */ e(S, { label: t.label, observation: u(t.stale_after_ms) }) : /* @__PURE__ */ m(v, { tone: "warning", title: "Series renderer unavailable", children: [
134
+ t.label,
135
+ ": the host has not supplied a series renderer."
136
+ ] }) }, t.id);
137
+ }),
138
+ o.diagnostics.map((t) => {
139
+ var h;
140
+ const r = (h = l.diagnostics) == null ? void 0 : h[t.id];
141
+ return r ? /* @__PURE__ */ e(U, { ...r, label: t.label, schema: t.schema }, t.id) : /* @__PURE__ */ e(S, { label: t.label, observation: u(t.stale_after_ms) }, t.id);
142
+ })
143
+ ] }) : g = /* @__PURE__ */ e(v, { tone: "warning", title: "Admin page unavailable", children: "The host selected an unsupported page." }), /* @__PURE__ */ m("div", { className: "mx-auto flex w-full min-w-0 max-w-5xl flex-col gap-4 p-4 text-control text-fg", children: [
144
+ n.phase !== "ready" ? /* @__PURE__ */ m(v, { tone: "warning", title: "Previous declarations retained", children: [
145
+ n.error ?? "Refreshing admin declarations.",
146
+ " Content uses the last declaration; writes are suspended until discovery succeeds."
147
+ ] }) : null,
148
+ g
149
+ ] }, `${i.contextKey}:${o.app.id}:${o.revision}`);
150
+ }
151
+ function oe(i) {
152
+ const n = K(i);
153
+ return /* @__PURE__ */ e(I, { nav: /* @__PURE__ */ e(Q, { ...i }), children: /* @__PURE__ */ e(G, { header: /* @__PURE__ */ e(W, { title: `${(n == null ? void 0 : n.app.label) ?? "Admin"} / ${y[i.selection.page] ?? "Unavailable"}` }), children: /* @__PURE__ */ e(Y, { ...i }) }) });
154
+ }
155
+ export {
156
+ Y as AdminContent,
157
+ Q as AdminNavigation,
158
+ oe as AdminShell
159
+ };
160
+ //# sourceMappingURL=admin.js.map