@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 +34 -0
- package/LICENSE +21 -0
- package/README.md +169 -2
- package/dist/activity-dialog.js +42 -0
- package/dist/activity-dialog.js.map +1 -0
- package/dist/activity-directory.js +63 -0
- package/dist/activity-directory.js.map +1 -0
- package/dist/activity-history.js +44 -0
- package/dist/activity-history.js.map +1 -0
- package/dist/admin.js +160 -0
- package/dist/admin.js.map +1 -0
- package/dist/charts.js +9 -0
- package/dist/charts.js.map +1 -0
- package/dist/directory.js +42 -0
- package/dist/directory.js.map +1 -0
- package/dist/editor.js +71 -0
- package/dist/editor.js.map +1 -0
- package/dist/identity-avatar.js +23 -0
- package/dist/identity-avatar.js.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/model.js +24 -0
- package/dist/model.js.map +1 -0
- package/dist/src/activity-dialog.d.ts +32 -0
- package/dist/src/activity-directory.d.ts +40 -0
- package/dist/src/activity-history.d.ts +18 -0
- package/dist/src/admin.d.ts +7 -0
- package/dist/src/charts.d.ts +3 -0
- package/dist/src/directory.d.ts +27 -0
- package/dist/src/editor.d.ts +48 -0
- package/dist/src/identity-avatar.d.ts +4 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/model.d.ts +4 -0
- package/dist/src/types.d.ts +93 -0
- package/package.json +94 -4
- package/src/styles/source.css +1 -0
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
|
-
#
|
|
1
|
+
# @hollis-labs/kit-admin
|
|
2
2
|
|
|
3
|
-
|
|
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
|