@ultimat3/admin 1.1.0 → 1.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/package.json +14 -14
- package/src/dev/server.ts +41 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/admin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Two dashboards: the /_x framework dev panels and the generated, AI-first app admin",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -31,18 +31,18 @@
|
|
|
31
31
|
"test": "bun test"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ultimat3/action": "1.
|
|
35
|
-
"@ultimat3/ai": "1.
|
|
36
|
-
"@ultimat3/cache": "1.
|
|
37
|
-
"@ultimat3/core": "1.
|
|
38
|
-
"@ultimat3/entity": "1.
|
|
39
|
-
"@ultimat3/i18n": "1.
|
|
40
|
-
"@ultimat3/jobs": "1.
|
|
41
|
-
"@ultimat3/mcp": "1.
|
|
42
|
-
"@ultimat3/money": "1.
|
|
43
|
-
"@ultimat3/policy": "1.
|
|
44
|
-
"@ultimat3/query": "1.
|
|
45
|
-
"@ultimat3/render": "1.
|
|
46
|
-
"@ultimat3/ui": "1.
|
|
34
|
+
"@ultimat3/action": "1.2.0",
|
|
35
|
+
"@ultimat3/ai": "1.2.0",
|
|
36
|
+
"@ultimat3/cache": "1.2.0",
|
|
37
|
+
"@ultimat3/core": "1.2.0",
|
|
38
|
+
"@ultimat3/entity": "1.2.0",
|
|
39
|
+
"@ultimat3/i18n": "1.2.0",
|
|
40
|
+
"@ultimat3/jobs": "1.2.0",
|
|
41
|
+
"@ultimat3/mcp": "1.2.0",
|
|
42
|
+
"@ultimat3/money": "1.2.0",
|
|
43
|
+
"@ultimat3/policy": "1.2.0",
|
|
44
|
+
"@ultimat3/query": "1.2.0",
|
|
45
|
+
"@ultimat3/render": "1.2.0",
|
|
46
|
+
"@ultimat3/ui": "1.2.0"
|
|
47
47
|
}
|
|
48
48
|
}
|
package/src/dev/server.ts
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
// SQL, policy traces, and caught mail. The refusal is a throw at construction, not a 404 at
|
|
3
3
|
// request time: an app that boots with /_x mounted in prod has already lost.
|
|
4
4
|
|
|
5
|
+
// Type-only, so it is erased and the 46-component barrel stays out of the mount graph — the
|
|
6
|
+
// values arrive through the dynamic `import()` in `shellStyle()`, same reason as `data.ts`.
|
|
7
|
+
import type { ColorRole } from '@ultimat3/ui';
|
|
5
8
|
import { DevDashboardInProdError } from '../errors';
|
|
6
9
|
import { defaultDevSources } from './data';
|
|
7
10
|
import type { DevSources } from './facts';
|
|
@@ -78,26 +81,17 @@ const jsonResponse = (body: unknown, status = 200): Response =>
|
|
|
78
81
|
const escapeHtml = (value: string): string =>
|
|
79
82
|
value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
80
83
|
|
|
81
|
-
/**
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
html[data-theme="light"] {
|
|
94
|
-
--x-color-bg: 253 246 240; --x-color-surface: 255 255 255; --x-color-fg: 38 34 31;
|
|
95
|
-
--x-color-fg-muted: 110 102 94; --x-color-line: 224 216 208; --x-color-accent: 34 122 197;
|
|
96
|
-
}
|
|
97
|
-
html[data-theme="dark"] {
|
|
98
|
-
--x-color-bg: 18 18 20; --x-color-surface: 34 34 39; --x-color-fg: 228 226 222;
|
|
99
|
-
--x-color-fg-muted: 150 146 140; --x-color-line: 54 54 60; --x-color-accent: 96 170 240;
|
|
100
|
-
}
|
|
84
|
+
/** The six roles /_x paints with. `--x-*` is the admin's namespace; the values are ui's. */
|
|
85
|
+
const SHELL_ROLES = [
|
|
86
|
+
'bg',
|
|
87
|
+
'surface-raised',
|
|
88
|
+
'fg',
|
|
89
|
+
'fg-muted',
|
|
90
|
+
'line',
|
|
91
|
+
'accent',
|
|
92
|
+
] as const satisfies readonly ColorRole[];
|
|
93
|
+
|
|
94
|
+
const SHELL_LAYOUT = `
|
|
101
95
|
body { margin: 0; background: rgb(var(--x-color-bg)); color: rgb(var(--x-color-fg));
|
|
102
96
|
font: 14px/1.5 ui-monospace, monospace; }
|
|
103
97
|
header { display: flex; gap: 1rem; padding: .75rem 1rem;
|
|
@@ -106,12 +100,36 @@ a { color: rgb(var(--x-color-accent)); }
|
|
|
106
100
|
main { padding: 1rem; }
|
|
107
101
|
h1 { font-size: 1rem; margin: 0 1rem 0 0; }
|
|
108
102
|
p.question { color: rgb(var(--x-color-fg-muted)); margin: 0 0 1rem; }
|
|
109
|
-
pre { background: rgb(var(--x-color-surface)); border: 1px solid rgb(var(--x-color-line));
|
|
103
|
+
pre { background: rgb(var(--x-color-surface-raised)); border: 1px solid rgb(var(--x-color-line));
|
|
110
104
|
padding: 1rem; overflow: auto; }
|
|
111
105
|
:focus-visible { outline: 2px solid rgb(var(--x-color-accent)); outline-offset: 2px; }
|
|
112
106
|
`;
|
|
113
107
|
|
|
108
|
+
let stylePromise: Promise<string> | undefined;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Tokens are inlined because /_x is a standalone page with no stylesheet pipeline — but the
|
|
112
|
+
* VALUES are read from `@ultimat3/ui` rather than copied, because the copy that used to live
|
|
113
|
+
* here went stale through a WCAG retune and shipped `line` on `surface-raised` at 1.16:1.
|
|
114
|
+
* Reached by dynamic `import()` for the same reason `data.ts` is: /_x stays out of the
|
|
115
|
+
* production graph, and the 46-component barrel loads only once a panel is actually drawn.
|
|
116
|
+
*/
|
|
117
|
+
async function shellStyle(): Promise<string> {
|
|
118
|
+
stylePromise ??= import('@ultimat3/ui').then(({ colorTokens }) => {
|
|
119
|
+
const block = (theme: 'light' | 'dark'): string =>
|
|
120
|
+
SHELL_ROLES.map((role) => `--x-color-${role}: ${colorTokens[theme][role]};`).join(' ');
|
|
121
|
+
return `
|
|
122
|
+
:root { ${block('light')} }
|
|
123
|
+
@media (prefers-color-scheme: dark) { :root { ${block('dark')} } }
|
|
124
|
+
html[data-theme="light"] { ${block('light')} }
|
|
125
|
+
html[data-theme="dark"] { ${block('dark')} }
|
|
126
|
+
${SHELL_LAYOUT}`;
|
|
127
|
+
});
|
|
128
|
+
return stylePromise;
|
|
129
|
+
}
|
|
130
|
+
|
|
114
131
|
function shell(
|
|
132
|
+
style: string,
|
|
115
133
|
basePath: string,
|
|
116
134
|
panels: readonly DevPanel[],
|
|
117
135
|
active: DevPanel,
|
|
@@ -128,7 +146,7 @@ function shell(
|
|
|
128
146
|
<html lang="en"><head><meta charset="utf-8">
|
|
129
147
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
130
148
|
<meta name="robots" content="noindex, nofollow">
|
|
131
|
-
<title>_x · ${escapeHtml(active.key)}</title><style>${
|
|
149
|
+
<title>_x · ${escapeHtml(active.key)}</title><style>${style}</style></head>
|
|
132
150
|
<body><header><h1>_x</h1><nav>${tabs}</nav>
|
|
133
151
|
<a href="${basePath}/${active.key}?json=1">--json</a></header>
|
|
134
152
|
<main><p class="question">${escapeHtml(active.question)}</p>
|
|
@@ -180,7 +198,7 @@ export function devDashboard(opts: DevDashboardOptions = {}): DevDashboard {
|
|
|
180
198
|
|
|
181
199
|
return wantsJson
|
|
182
200
|
? jsonResponse(payload, payload.ok ? 200 : 500)
|
|
183
|
-
: new Response(shell(basePath, panels, panel, payload), {
|
|
201
|
+
: new Response(shell(await shellStyle(), basePath, panels, panel, payload), {
|
|
184
202
|
status: 200,
|
|
185
203
|
headers: { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-store' },
|
|
186
204
|
});
|