elowen-plugin-ui-kit 0.1.0 → 0.5.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/build.d.ts +12 -0
- package/build.js +51 -0
- package/index.d.ts +52 -3
- package/index.js +1 -1
- package/package.json +6 -2
- package/theme.css +78 -0
package/build.d.ts
CHANGED
|
@@ -12,3 +12,15 @@ export interface BuildPluginUiBundleOptions {
|
|
|
12
12
|
|
|
13
13
|
/** Bundle `entry` into the ESM file at `outfile`. Throws on any build error. */
|
|
14
14
|
export declare function buildPluginUiBundle(options: BuildPluginUiBundleOptions): Promise<void>;
|
|
15
|
+
|
|
16
|
+
export interface BuildPluginUiCssOptions {
|
|
17
|
+
/** The FINISHED bundle to scan for utility classes (the output of `buildPluginUiBundle`). */
|
|
18
|
+
bundle: string;
|
|
19
|
+
/** Where to write the compiled stylesheet (the plugin manifest's `web.css`). */
|
|
20
|
+
outfile: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Compile the plugin's own stylesheet from its built bundle. Utilities only, no preflight, no prefix,
|
|
24
|
+
* and the host's design tokens are referenced (`var(--token, fallback)`) rather than inlined, so the
|
|
25
|
+
* plugin follows the host's skin. Resolves to the emitted CSS. */
|
|
26
|
+
export declare function buildPluginUiCss(options: BuildPluginUiCssOptions): Promise<string>;
|
package/build.js
CHANGED
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
* emits a plain `index.js`). React imports are aliased to shims that read the HOST's instance from
|
|
4
4
|
* `window.ElowenUiRuntime`, so a bundle can never ship a second React (two copies break hooks). */
|
|
5
5
|
import { build } from 'esbuild';
|
|
6
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
7
|
+
import { dirname, resolve } from 'node:path';
|
|
6
8
|
import { fileURLToPath } from 'node:url';
|
|
7
9
|
|
|
8
10
|
const shim = (name) => fileURLToPath(new URL(`./shims/${name}.cjs`, import.meta.url));
|
|
9
11
|
|
|
12
|
+
/** This package's own directory — the resolution base for the Tailwind compile below, so
|
|
13
|
+
* `tailwindcss/theme.css` resolves against the kit's dependencies rather than the plugin's. */
|
|
14
|
+
const kitDir = fileURLToPath(new URL('.', import.meta.url));
|
|
15
|
+
|
|
10
16
|
/** Bundle `entry` into the ESM file at `outfile`. Throws on any build error. */
|
|
11
17
|
export async function buildPluginUiBundle({ entry, outfile, minify = false, nodePaths }) {
|
|
12
18
|
await build({
|
|
@@ -32,3 +38,48 @@ export async function buildPluginUiBundle({ entry, outfile, minify = false, node
|
|
|
32
38
|
},
|
|
33
39
|
});
|
|
34
40
|
}
|
|
41
|
+
|
|
42
|
+
/** Compile the plugin's OWN stylesheet from the FINISHED bundle at `bundle` and write it to `outfile`.
|
|
43
|
+
*
|
|
44
|
+
* Why this exists: Elowen ships a PREBUILT web app (`web-dist/` is in `package.json → files`), so on a
|
|
45
|
+
* user's machine there is no Tailwind and no Next build — the host's CSS is frozen at publish time and
|
|
46
|
+
* carries only the utilities the HOST itself uses. A plugin from the registry that reaches for any other
|
|
47
|
+
* utility rendered unstyled there, with nothing the user could do about it. So the plugin brings its own.
|
|
48
|
+
*
|
|
49
|
+
* Three scoping rules make that safe to drop into a running app, and each is load-bearing:
|
|
50
|
+
* - everything lands in `@layer utilities`, so the host's own utilities (declared in the same layer, but
|
|
51
|
+
* earlier in the cascade order the host establishes) are not globally outranked by a plugin's sheet;
|
|
52
|
+
* - NO preflight — a plugin must never reset the host's elements;
|
|
53
|
+
* - NO prefix — the plugin shares class names with the shared components it renders from
|
|
54
|
+
* `window.ElowenUiRuntime`, which are styled by the host's sheet.
|
|
55
|
+
*
|
|
56
|
+
* The theme is `@reference`d, never imported: referencing emits `var(--token, fallback)` instead of
|
|
57
|
+
* inlining the value, so the plugin reads the HOST's live variables and follows a skin
|
|
58
|
+
* (`web/skins/*` override the tokens on `:root[data-skin]`). Inlining them would freeze every plugin
|
|
59
|
+
* on the default palette. Returns the emitted CSS. */
|
|
60
|
+
export async function buildPluginUiCss({ bundle, outfile }) {
|
|
61
|
+
// Loaded lazily so `buildPluginUiBundle` — the far more common call — never pays for Tailwind's
|
|
62
|
+
// native scanner just to bundle JS.
|
|
63
|
+
const { compile } = await import('@tailwindcss/node');
|
|
64
|
+
const { Scanner } = await import('@tailwindcss/oxide');
|
|
65
|
+
|
|
66
|
+
const source = resolve(bundle);
|
|
67
|
+
const css = [
|
|
68
|
+
// Declare the layer order the host uses, so `utilities` sorts where the host puts it.
|
|
69
|
+
'@layer theme, base, components, utilities;',
|
|
70
|
+
// Reference-only: gives the compiler the design system (Tailwind's defaults + the host tokens)
|
|
71
|
+
// WITHOUT emitting a single variable or preflight rule of its own.
|
|
72
|
+
'@reference "tailwindcss/theme.css";',
|
|
73
|
+
`@reference ${JSON.stringify(resolve(kitDir, 'theme.css'))};`,
|
|
74
|
+
// The finished bundle is the only scan target: class names survive minification as plain string
|
|
75
|
+
// literals, so the built file is a complete and honest record of what the plugin actually uses.
|
|
76
|
+
`@source ${JSON.stringify(source)};`,
|
|
77
|
+
'@layer utilities { @tailwind utilities; }',
|
|
78
|
+
].join('\n');
|
|
79
|
+
|
|
80
|
+
const compiler = await compile(css, { base: kitDir, onDependency: () => {} });
|
|
81
|
+
const out = compiler.build(new Scanner({ sources: compiler.sources }).scan());
|
|
82
|
+
await mkdir(dirname(resolve(outfile)), { recursive: true });
|
|
83
|
+
await writeFile(resolve(outfile), out, 'utf8');
|
|
84
|
+
return out;
|
|
85
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -10,7 +10,36 @@ import type { ComponentType } from 'react';
|
|
|
10
10
|
/** See index.js — bump on incompatible changes to `ElowenUiRuntime`. Deliberately a LITERAL type:
|
|
11
11
|
* the web app re-declares the value and annotates it with `typeof PLUGIN_UI_API_VERSION`, so a kit
|
|
12
12
|
* bump that forgets the host fails the web typecheck instead of drifting silently. */
|
|
13
|
-
export declare const PLUGIN_UI_API_VERSION:
|
|
13
|
+
export declare const PLUGIN_UI_API_VERSION: 6;
|
|
14
|
+
|
|
15
|
+
/** Project metadata exposed to a contextual plugin panel. The Project remains core-owned; a panel uses
|
|
16
|
+
* this identity to address only its own project-scoped API data. */
|
|
17
|
+
export interface PluginUiProject {
|
|
18
|
+
id: number;
|
|
19
|
+
slug: string;
|
|
20
|
+
path: string;
|
|
21
|
+
notes: string;
|
|
22
|
+
icon?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The selected core User DTO exposed only to administrator user-detail panels. Compatibility fields stay
|
|
26
|
+
* present because plugins receive the same object as the host Users screen, not a second partial identity. */
|
|
27
|
+
export interface PluginUiUser {
|
|
28
|
+
id: number;
|
|
29
|
+
username: string;
|
|
30
|
+
created_at: string;
|
|
31
|
+
is_admin: boolean;
|
|
32
|
+
allowed_execs: string[];
|
|
33
|
+
disabled_tools: string[];
|
|
34
|
+
allowed_tools: string[];
|
|
35
|
+
granted_plugins: string[];
|
|
36
|
+
name: string;
|
|
37
|
+
email: string;
|
|
38
|
+
avatar: string;
|
|
39
|
+
default_exec: string;
|
|
40
|
+
advisor_exec: string;
|
|
41
|
+
advisor_autostart: boolean;
|
|
42
|
+
}
|
|
14
43
|
|
|
15
44
|
/** Props every plugin page/settings component receives. */
|
|
16
45
|
export interface PluginPageProps {
|
|
@@ -33,12 +62,32 @@ export interface PluginPageProps {
|
|
|
33
62
|
onSaveState?: (status: 'idle' | 'saving' | 'saved' | 'error', retry?: () => void) => void;
|
|
34
63
|
}
|
|
35
64
|
|
|
65
|
+
/** Props for a contextual Project panel. It is never a standalone route: the selected Project and panel
|
|
66
|
+
* id come from the host's Project detail rail. */
|
|
67
|
+
export interface PluginProjectPanelProps {
|
|
68
|
+
plugin: string;
|
|
69
|
+
panelId: string;
|
|
70
|
+
project: PluginUiProject;
|
|
71
|
+
surface: 'project';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Props for an administrator panel mounted for one selected core User. */
|
|
75
|
+
export interface PluginUserPanelProps {
|
|
76
|
+
plugin: string;
|
|
77
|
+
panelId: string;
|
|
78
|
+
user: PluginUiUser;
|
|
79
|
+
surface: 'user';
|
|
80
|
+
}
|
|
81
|
+
|
|
36
82
|
/** What a bundle hands to window.__elowenRegisterPluginUi. Routes are `/`-joined segment patterns
|
|
37
|
-
* (`''` = the root page, `detail/:id` captures params).
|
|
38
|
-
* manifest
|
|
83
|
+
* (`''` = the root page, `detail/:id` captures params). Contextual component maps are keyed by their
|
|
84
|
+
* matching manifest panel ids and mount only in the corresponding host surface. */
|
|
39
85
|
export interface PluginUiRegistration {
|
|
40
86
|
requiresApiVersion: number;
|
|
41
87
|
pages?: Record<string, ComponentType<PluginPageProps>>;
|
|
88
|
+
account?: Record<string, ComponentType<PluginPageProps>>;
|
|
89
|
+
user?: Record<string, ComponentType<PluginUserPanelProps>>;
|
|
90
|
+
project?: Record<string, ComponentType<PluginProjectPanelProps>>;
|
|
42
91
|
settings?: Record<string, ComponentType<PluginPageProps>>;
|
|
43
92
|
}
|
|
44
93
|
|
package/index.js
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* `window.ElowenUiRuntime` stamped with this number; a bundle whose `requiresApiVersion` is NEWER
|
|
3
3
|
* renders a placeholder instead of executing against a contract it was not built for. Bump on
|
|
4
4
|
* incompatible changes to the `ElowenUiRuntime` surface (see index.d.ts). */
|
|
5
|
-
export const PLUGIN_UI_API_VERSION =
|
|
5
|
+
export const PLUGIN_UI_API_VERSION = 5;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "elowen-plugin-ui-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Contract types and esbuild toolchain for building Elowen plugin browser-UI bundles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -25,11 +25,15 @@
|
|
|
25
25
|
"build.d.ts",
|
|
26
26
|
"bin.js",
|
|
27
27
|
"shims/",
|
|
28
|
+
"theme.css",
|
|
28
29
|
"README.md"
|
|
29
30
|
],
|
|
30
31
|
"dependencies": {
|
|
32
|
+
"@tailwindcss/node": "^4.3.3",
|
|
33
|
+
"@tailwindcss/oxide": "^4.3.3",
|
|
31
34
|
"@types/react": "^19.2.0",
|
|
32
35
|
"@types/react-dom": "^19.2.0",
|
|
33
|
-
"esbuild": "^0.28.2"
|
|
36
|
+
"esbuild": "^0.28.2",
|
|
37
|
+
"tailwindcss": "^4.3.3"
|
|
34
38
|
}
|
|
35
39
|
}
|
package/theme.css
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/* GENERATED MIRROR of web/app/styles/tokens.css @theme blocks — do not edit by hand.
|
|
2
|
+
* The host owns these tokens; this copy exists only so a plugin CSS build can `@reference` them and
|
|
3
|
+
* emit `var(--token, fallback)`. tests/contract/pluginUiKitTheme.test.ts fails the moment the two drift.
|
|
4
|
+
*/
|
|
5
|
+
@theme {
|
|
6
|
+
/* OLED Ember: true black canvas, warm near-black surfaces, one unmistakable Elowen accent. */
|
|
7
|
+
--color-bg: #000000;
|
|
8
|
+
--color-document: #030303;
|
|
9
|
+
--color-surface: #070707;
|
|
10
|
+
--color-elevated: #0d0d0d;
|
|
11
|
+
--color-overlay: #151515;
|
|
12
|
+
--color-border: #242424;
|
|
13
|
+
--color-border-strong: #363636;
|
|
14
|
+
--color-accent: #ff5236;
|
|
15
|
+
--color-accent-hot: #ff735c;
|
|
16
|
+
--color-ember: #ff9a62;
|
|
17
|
+
/* The pale end of the ember ramp — the far stop of a heat gradient, where the accent is the near one.
|
|
18
|
+
It is a token so a repaint moves the whole ramp; a literal in a chart would stay orange. */
|
|
19
|
+
--color-ember-bright: #ffd09a;
|
|
20
|
+
|
|
21
|
+
--color-text: #f7f3f0;
|
|
22
|
+
--color-text-muted: #9d948e;
|
|
23
|
+
--color-text-subtle: #6e6661;
|
|
24
|
+
|
|
25
|
+
--color-danger: #e94d42;
|
|
26
|
+
--color-success: #32c986;
|
|
27
|
+
--color-warning: #edae49;
|
|
28
|
+
--color-error: #e94d42;
|
|
29
|
+
--color-info: #62a8ea;
|
|
30
|
+
--color-approve: #32c986;
|
|
31
|
+
--color-cancelled: #756d68;
|
|
32
|
+
|
|
33
|
+
/* Organic but disciplined: compact controls, roomier panels, never pill-shaped by default. */
|
|
34
|
+
--radius-sm: 0.375rem;
|
|
35
|
+
--radius: 0.5rem;
|
|
36
|
+
--radius-md: 0.625rem;
|
|
37
|
+
--radius-lg: 0.75rem;
|
|
38
|
+
--radius-xl: 1rem;
|
|
39
|
+
--radius-2xl: 1.25rem;
|
|
40
|
+
|
|
41
|
+
--space-1: 0.25rem;
|
|
42
|
+
--space-2: 0.5rem;
|
|
43
|
+
--space-3: 0.75rem;
|
|
44
|
+
--space-4: 1rem;
|
|
45
|
+
--space-5: 1.25rem;
|
|
46
|
+
--space-6: 1.5rem;
|
|
47
|
+
--space-8: 2rem;
|
|
48
|
+
--space-10: 2.5rem;
|
|
49
|
+
--space-12: 3rem;
|
|
50
|
+
|
|
51
|
+
--font-mono: var(--font-geist-mono), ui-monospace, "SF Mono", monospace;
|
|
52
|
+
--font-sans: var(--font-geist-sans), -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
53
|
+
--font-display: var(--font-geist-sans), var(--font-sans);
|
|
54
|
+
|
|
55
|
+
--text-display: clamp(2rem, 1.55rem + 1.5vw, 3.25rem);
|
|
56
|
+
--text-caption: 0.6875rem;
|
|
57
|
+
--text-tiny: 0.5625rem;
|
|
58
|
+
|
|
59
|
+
/* The accent as a bare R G B triple, so shadow/glow tokens can compose alpha variants — and so a
|
|
60
|
+
white-label theme can retint them by overriding this ONE variable instead of every baked shadow. */
|
|
61
|
+
--accent-rgb: 255 82 54;
|
|
62
|
+
--shadow-card: 0 1px 0 rgb(255 255 255 / 0.025), 0 14px 40px rgb(0 0 0 / 0.2);
|
|
63
|
+
--shadow-raised: 0 1px 0 rgb(255 255 255 / 0.04), 0 22px 64px rgb(0 0 0 / 0.48);
|
|
64
|
+
--shadow-ember: 0 16px 48px rgb(var(--accent-rgb) / 0.14);
|
|
65
|
+
--glow-active: 0 0 0 1px rgb(var(--accent-rgb) / 0.3), 0 0 24px rgb(var(--accent-rgb) / 0.22), 0 0 64px rgb(var(--accent-rgb) / 0.12);
|
|
66
|
+
--glow-soft: 0 0 44px rgb(var(--accent-rgb) / 0.1);
|
|
67
|
+
--hairline: 1px solid rgb(255 255 255 / 0.075);
|
|
68
|
+
|
|
69
|
+
--motion-instant: 80ms;
|
|
70
|
+
--motion-fast: 140ms;
|
|
71
|
+
--motion-base: 240ms;
|
|
72
|
+
--motion-slow: 420ms;
|
|
73
|
+
--motion-ambient: 8s;
|
|
74
|
+
--motion-distance-md: 10px;
|
|
75
|
+
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
|
76
|
+
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
|
77
|
+
--ease-spring: cubic-bezier(0.34, 1.35, 0.64, 1);
|
|
78
|
+
}
|