elowen-plugin-ui-kit 0.2.0 → 0.6.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.js +3 -3
- package/index.d.ts +110 -3
- package/index.js +1 -1
- package/package.json +1 -1
- package/theme.css +75 -33
package/build.js
CHANGED
|
@@ -47,8 +47,8 @@ export async function buildPluginUiBundle({ entry, outfile, minify = false, node
|
|
|
47
47
|
* utility rendered unstyled there, with nothing the user could do about it. So the plugin brings its own.
|
|
48
48
|
*
|
|
49
49
|
* Three scoping rules make that safe to drop into a running app, and each is load-bearing:
|
|
50
|
-
* - everything lands in `@layer utilities`,
|
|
51
|
-
*
|
|
50
|
+
* - everything lands in `@layer utilities`, and the host loader inserts the plugin sheet BEFORE host styles,
|
|
51
|
+
* so plugin-only classes exist while matching host utilities win by source order;
|
|
52
52
|
* - NO preflight — a plugin must never reset the host's elements;
|
|
53
53
|
* - NO prefix — the plugin shares class names with the shared components it renders from
|
|
54
54
|
* `window.ElowenUiRuntime`, which are styled by the host's sheet.
|
|
@@ -65,7 +65,7 @@ export async function buildPluginUiCss({ bundle, outfile }) {
|
|
|
65
65
|
|
|
66
66
|
const source = resolve(bundle);
|
|
67
67
|
const css = [
|
|
68
|
-
// Declare the
|
|
68
|
+
// Declare the standard Tailwind order; the host loader owns source order between plugin and host sheets.
|
|
69
69
|
'@layer theme, base, components, utilities;',
|
|
70
70
|
// Reference-only: gives the compiler the design system (Tailwind's defaults + the host tokens)
|
|
71
71
|
// WITHOUT emitting a single variable or preflight rule of its own.
|
package/index.d.ts
CHANGED
|
@@ -10,7 +10,74 @@ 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: 11;
|
|
14
|
+
|
|
15
|
+
/** Public props of `ElowenUiRuntime.components.Slider`. */
|
|
16
|
+
export interface SliderProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'min' | 'max' | 'step' | 'type'> {
|
|
17
|
+
value: number;
|
|
18
|
+
onChange: (value: number) => void;
|
|
19
|
+
min?: number;
|
|
20
|
+
max?: number;
|
|
21
|
+
step?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Public props of `ElowenUiRuntime.components.DirectoryPicker`. Selection reports the currently open
|
|
25
|
+
* server directory; closing has no side effect. */
|
|
26
|
+
export interface DirectoryPickerProps {
|
|
27
|
+
initialPath?: string;
|
|
28
|
+
onSelect: (path: string) => void;
|
|
29
|
+
onClose: () => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type ConfirmDialogButtonVariant = 'default' | 'accent' | 'ghost' | 'danger' | 'ghost-danger' | 'outline' | 'outline-danger';
|
|
33
|
+
|
|
34
|
+
/** Public props of the API 11 async-safe `ElowenUiRuntime.components.ConfirmDialog`. */
|
|
35
|
+
export interface ConfirmDialogProps {
|
|
36
|
+
open: boolean;
|
|
37
|
+
title: string;
|
|
38
|
+
description?: string;
|
|
39
|
+
confirmLabel?: string;
|
|
40
|
+
confirmVariant?: ConfirmDialogButtonVariant;
|
|
41
|
+
pendingLabel?: string;
|
|
42
|
+
pending?: boolean;
|
|
43
|
+
disabled?: boolean;
|
|
44
|
+
/** Legacy confirm-only disable; unlike `disabled`, Cancel remains available. */
|
|
45
|
+
confirmDisabled?: boolean;
|
|
46
|
+
error?: React.ReactNode;
|
|
47
|
+
/** A promise enables the built-in pending lock; synchronous callbacks remain source-compatible. */
|
|
48
|
+
onConfirm: () => unknown;
|
|
49
|
+
onConfirmError?: (error: unknown) => void;
|
|
50
|
+
onClose: () => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Project metadata exposed to a contextual plugin panel. The Project remains core-owned; a panel uses
|
|
54
|
+
* this identity to address only its own project-scoped API data. */
|
|
55
|
+
export interface PluginUiProject {
|
|
56
|
+
id: number;
|
|
57
|
+
slug: string;
|
|
58
|
+
path: string;
|
|
59
|
+
notes: string;
|
|
60
|
+
icon?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The selected core User DTO exposed only to administrator user-detail panels. Compatibility fields stay
|
|
64
|
+
* present because plugins receive the same object as the host Users screen, not a second partial identity. */
|
|
65
|
+
export interface PluginUiUser {
|
|
66
|
+
id: number;
|
|
67
|
+
username: string;
|
|
68
|
+
created_at: string;
|
|
69
|
+
is_admin: boolean;
|
|
70
|
+
allowed_execs: string[];
|
|
71
|
+
disabled_tools: string[];
|
|
72
|
+
allowed_tools: string[];
|
|
73
|
+
granted_plugins: string[];
|
|
74
|
+
name: string;
|
|
75
|
+
email: string;
|
|
76
|
+
avatar: string;
|
|
77
|
+
default_exec: string;
|
|
78
|
+
advisor_exec: string;
|
|
79
|
+
advisor_autostart: boolean;
|
|
80
|
+
}
|
|
14
81
|
|
|
15
82
|
/** Props every plugin page/settings component receives. */
|
|
16
83
|
export interface PluginPageProps {
|
|
@@ -33,13 +100,53 @@ export interface PluginPageProps {
|
|
|
33
100
|
onSaveState?: (status: 'idle' | 'saving' | 'saved' | 'error', retry?: () => void) => void;
|
|
34
101
|
}
|
|
35
102
|
|
|
103
|
+
/** Props for a contextual Project panel. It is never a standalone route: the selected Project and panel
|
|
104
|
+
* id come from the host's Project detail rail. */
|
|
105
|
+
export interface PluginProjectPanelProps {
|
|
106
|
+
plugin: string;
|
|
107
|
+
panelId: string;
|
|
108
|
+
project: PluginUiProject;
|
|
109
|
+
surface: 'project';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Props for an administrator panel mounted for one selected core User. */
|
|
113
|
+
export interface PluginUserPanelProps {
|
|
114
|
+
plugin: string;
|
|
115
|
+
panelId: string;
|
|
116
|
+
user: PluginUiUser;
|
|
117
|
+
surface: 'user';
|
|
118
|
+
}
|
|
119
|
+
|
|
36
120
|
/** What a bundle hands to window.__elowenRegisterPluginUi. Routes are `/`-joined segment patterns
|
|
37
|
-
* (`''` = the root page, `detail/:id` captures params).
|
|
38
|
-
* manifest
|
|
121
|
+
* (`''` = the root page, `detail/:id` captures params). Contextual component maps are keyed by their
|
|
122
|
+
* matching manifest panel ids and mount only in the corresponding host surface. */
|
|
39
123
|
export interface PluginUiRegistration {
|
|
40
124
|
requiresApiVersion: number;
|
|
41
125
|
pages?: Record<string, ComponentType<PluginPageProps>>;
|
|
126
|
+
account?: Record<string, ComponentType<PluginPageProps>>;
|
|
127
|
+
/** For an `account` entry placed as `linkedAccount`: its one-line claim in the CLOSED Linked accounts
|
|
128
|
+
* summary, keyed by the same panel id. Whether a connector is currently linked is a fact only the
|
|
129
|
+
* bundle holds, so the host cannot draw this chip on its behalf — but it also must not mount the
|
|
130
|
+
* PANEL to ask, which is why this is its own entry. Omit it and the summary simply says nothing
|
|
131
|
+
* about this connector. Draw it with `components.SummaryChip` so it matches the chips beside it. */
|
|
132
|
+
accountChip?: Record<string, ComponentType<PluginPageProps>>;
|
|
133
|
+
user?: Record<string, ComponentType<PluginUserPanelProps>>;
|
|
134
|
+
project?: Record<string, ComponentType<PluginProjectPanelProps>>;
|
|
42
135
|
settings?: Record<string, ComponentType<PluginPageProps>>;
|
|
136
|
+
/** Ids of `settings` sections that draw their OWN page frame — the shell, the masthead and the save
|
|
137
|
+
* indicator — and therefore want none from the host.
|
|
138
|
+
*
|
|
139
|
+
* The host serves a plugin's SOLE settings section as its page (`/p/<plugin>`) and wraps it in a
|
|
140
|
+
* page column plus a masthead, because a section written for the Settings deck brings neither. A
|
|
141
|
+
* section that renders `components.WorkspaceShell` itself then gets both twice: two nested page
|
|
142
|
+
* frames, so the gutter and the bottom padding apply twice and the page is measurably narrower than
|
|
143
|
+
* its sibling registers, above a masthead that is a zero-height row of margin holding an idle save
|
|
144
|
+
* indicator. Naming the section here is how a bundle says "the frame is mine", and it is a fact only
|
|
145
|
+
* the bundle holds — the host cannot see what a component renders without mounting it.
|
|
146
|
+
*
|
|
147
|
+
* A section listed here owns the whole surface, so it also owns showing its own save state and must
|
|
148
|
+
* not rely on `onSaveState` being displayed anywhere. Ids not present in `settings` are ignored. */
|
|
149
|
+
ownsPageFrame?: string[];
|
|
43
150
|
}
|
|
44
151
|
|
|
45
152
|
/** The host API surface a bundle finds on `window.ElowenUiRuntime`: the HOST's React instance (a
|
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 = 11;
|
package/package.json
CHANGED
package/theme.css
CHANGED
|
@@ -3,34 +3,86 @@
|
|
|
3
3
|
* emit `var(--token, fallback)`. tests/contract/pluginUiKitTheme.test.ts fails the moment the two drift.
|
|
4
4
|
*/
|
|
5
5
|
@theme {
|
|
6
|
-
/* OLED Ember: true black canvas, warm near-black surfaces, one unmistakable Elowen
|
|
7
|
-
--color-
|
|
6
|
+
/* OLED Ember: true black canvas, warm near-black surfaces, one unmistakable Elowen ember. */
|
|
7
|
+
--color-background: #000000;
|
|
8
|
+
--color-foreground: #f7f3f0;
|
|
9
|
+
|
|
10
|
+
/* NOT a shadcn token. One ground between the canvas and a card, for the ambient chrome that has to
|
|
11
|
+
read as raised off `background` without being a card — the orbit pods, the section nodes, the
|
|
12
|
+
settings groups. shadcn's ramp goes straight from `background` to `card`. */
|
|
8
13
|
--color-document: #030303;
|
|
9
|
-
|
|
10
|
-
--color-
|
|
11
|
-
--color-
|
|
14
|
+
|
|
15
|
+
--color-card: #070707;
|
|
16
|
+
--color-card-foreground: var(--color-foreground);
|
|
17
|
+
--color-popover: #151515;
|
|
18
|
+
--color-popover-foreground: var(--color-foreground);
|
|
19
|
+
|
|
20
|
+
--color-muted: #0d0d0d;
|
|
21
|
+
--color-muted-foreground: #9d948e;
|
|
22
|
+
/* shadcn separates the secondary-button fill from the muted surface so a design can make one louder
|
|
23
|
+
than the other; ours does not, and an alias says that on purpose rather than by omission. */
|
|
24
|
+
--color-secondary: var(--color-muted);
|
|
25
|
+
--color-secondary-foreground: var(--color-foreground);
|
|
26
|
+
|
|
27
|
+
--color-accent: color-mix(in srgb, var(--color-foreground) 10%, transparent);
|
|
28
|
+
--color-accent-foreground: var(--color-foreground);
|
|
29
|
+
|
|
30
|
+
/* NOT a shadcn token: shadcn's text ramp stops at `muted-foreground`, and the app has a third step
|
|
31
|
+
carrying timestamps, units and table metadata. It still has to be READABLE, not decorative, so it is
|
|
32
|
+
held at >= 4.5:1 (WCAG AA, normal text) against every surface token — 4.57:1 on the lightest one,
|
|
33
|
+
--color-muted — while staying far enough under --color-muted-foreground (6.53:1 there) that the
|
|
34
|
+
three steps remain distinguishable. */
|
|
35
|
+
--color-subtle-foreground: #827974;
|
|
36
|
+
|
|
37
|
+
/* NOT a shadcn token. Opaque backing for anything that scrolls content UNDER itself — sticky table
|
|
38
|
+
headers, sticky toolbars. It must not be translucent or rows show through the header as it passes,
|
|
39
|
+
which rules out `accent`. Derived from the document ground so every skin, including forks this repo
|
|
40
|
+
cannot see, inherits a correct sticky colour without declaring one. */
|
|
41
|
+
--color-sticky: color-mix(in srgb, var(--color-document) 94%, var(--color-foreground) 6%);
|
|
42
|
+
|
|
12
43
|
--color-border: #242424;
|
|
44
|
+
/* NOT a shadcn token: the heavier rule, for a scrollbar thumb and a focused edge. */
|
|
13
45
|
--color-border-strong: #363636;
|
|
14
|
-
|
|
15
|
-
|
|
46
|
+
/* Form-control edges are the app's ordinary border. shadcn separates `input` from `border` so a design
|
|
47
|
+
can make fields louder than cards; ours does not, and an alias says that on purpose rather than by
|
|
48
|
+
omission — a skin that wants the distinction overrides this one token. */
|
|
49
|
+
--color-input: var(--color-border);
|
|
50
|
+
--color-ring: var(--color-primary);
|
|
51
|
+
|
|
52
|
+
--color-primary: #ff5236;
|
|
53
|
+
/* The ink on a saturated fill. shadcn gives every fill its own `-foreground`, and the app deliberately
|
|
54
|
+
paints all of them with ONE ink, so the value is stated here once and the others alias it — a skin
|
|
55
|
+
that wants to split them overrides the individual names. */
|
|
56
|
+
--color-primary-foreground: #070707;
|
|
57
|
+
/* NOT shadcn tokens: the brand ember is a RAMP, not a single fill. `-hot` is its hover step and
|
|
58
|
+
`-bright` its pale far stop, so a heat gradient or a chart moves with a repaint instead of staying
|
|
59
|
+
orange under a blue design. */
|
|
60
|
+
--color-primary-hot: #ff735c;
|
|
16
61
|
--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
62
|
--color-ember-bright: #ffd09a;
|
|
20
63
|
|
|
21
|
-
--color-
|
|
22
|
-
--color-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
--color-danger: #e94d42;
|
|
64
|
+
--color-destructive: #e94d42;
|
|
65
|
+
--color-destructive-foreground: var(--color-primary-foreground);
|
|
66
|
+
/* NOT shadcn tokens. shadcn ships only `destructive`; an app that reports outcomes needs the other
|
|
67
|
+
three states, and borrowing `primary` or `chart-*` for them would say something they do not mean. */
|
|
26
68
|
--color-success: #32c986;
|
|
69
|
+
--color-success-foreground: var(--color-primary-foreground);
|
|
27
70
|
--color-warning: #edae49;
|
|
28
|
-
--color-error: #e94d42;
|
|
29
71
|
--color-info: #62a8ea;
|
|
30
|
-
--color-approve: #32c986;
|
|
31
|
-
--color-cancelled: #756d68;
|
|
32
72
|
|
|
33
|
-
/*
|
|
73
|
+
/* The categorical series. Charts and legends read these and never a literal, so a repaint moves the
|
|
74
|
+
whole set. 1–3 are the brand ramp; 4 and 5 are deliberately UNLIKE it, so a swatch stays
|
|
75
|
+
distinguishable from its neighbours without borrowing `destructive` or `success` and implying a
|
|
76
|
+
state it does not mean. */
|
|
77
|
+
--color-chart-1: var(--color-primary);
|
|
78
|
+
--color-chart-2: var(--color-ember);
|
|
79
|
+
--color-chart-3: var(--color-ember-bright);
|
|
80
|
+
--color-chart-4: #a78bfa;
|
|
81
|
+
--color-chart-5: #f472b6;
|
|
82
|
+
|
|
83
|
+
/* Organic but disciplined: compact controls, roomier panels, never pill-shaped by default.
|
|
84
|
+
This scale ALREADY carries shadcn's names, so components use `rounded-sm|md|lg|xl|2xl` directly and
|
|
85
|
+
must never reach for `rounded-[var(--radius)]` — that would bypass the scale and pin one size. */
|
|
34
86
|
--radius-sm: 0.375rem;
|
|
35
87
|
--radius: 0.5rem;
|
|
36
88
|
--radius-md: 0.625rem;
|
|
@@ -38,16 +90,6 @@
|
|
|
38
90
|
--radius-xl: 1rem;
|
|
39
91
|
--radius-2xl: 1.25rem;
|
|
40
92
|
|
|
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
93
|
--font-mono: var(--font-geist-mono), ui-monospace, "SF Mono", monospace;
|
|
52
94
|
--font-sans: var(--font-geist-sans), -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
53
95
|
--font-display: var(--font-geist-sans), var(--font-sans);
|
|
@@ -56,14 +98,14 @@
|
|
|
56
98
|
--text-caption: 0.6875rem;
|
|
57
99
|
--text-tiny: 0.5625rem;
|
|
58
100
|
|
|
59
|
-
/* The
|
|
101
|
+
/* The primary as a bare R G B triple, so shadow/glow tokens can compose alpha variants — and so a
|
|
60
102
|
white-label theme can retint them by overriding this ONE variable instead of every baked shadow. */
|
|
61
|
-
--
|
|
103
|
+
--primary-rgb: 255 82 54;
|
|
62
104
|
--shadow-card: 0 1px 0 rgb(255 255 255 / 0.025), 0 14px 40px rgb(0 0 0 / 0.2);
|
|
63
105
|
--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(--
|
|
65
|
-
--glow-active: 0 0 0 1px rgb(var(--
|
|
66
|
-
--glow-soft: 0 0 44px rgb(var(--
|
|
106
|
+
--shadow-ember: 0 16px 48px rgb(var(--primary-rgb) / 0.14);
|
|
107
|
+
--glow-active: 0 0 0 1px rgb(var(--primary-rgb) / 0.3), 0 0 24px rgb(var(--primary-rgb) / 0.22), 0 0 64px rgb(var(--primary-rgb) / 0.12);
|
|
108
|
+
--glow-soft: 0 0 44px rgb(var(--primary-rgb) / 0.1);
|
|
67
109
|
--hairline: 1px solid rgb(255 255 255 / 0.075);
|
|
68
110
|
|
|
69
111
|
--motion-instant: 80ms;
|