ankka-console 0.10.0 → 0.11.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/dist/client/control-plane.d.ts +25 -1
- package/dist/client/control-plane.js +60 -1
- package/dist/client/schemas.d.ts +525 -0
- package/dist/client/schemas.js +174 -0
- package/dist/context.d.ts +98 -0
- package/dist/context.js +73 -0
- package/dist/extensions/render.d.ts +2 -0
- package/dist/extensions/render.js +5 -0
- package/dist/extensions/types.d.ts +1 -1
- package/dist/extensions/types.js +7 -0
- package/dist/fonts/inter-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/inter-latin-wght-normal.woff2 +0 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.js +7 -1
- package/dist/routes/auth.sign-out.js +8 -3
- package/dist/routes/front.js +12 -6
- package/dist/routes/logs.d.ts +35 -0
- package/dist/routes/logs.js +15 -14
- package/dist/routes/members.js +11 -8
- package/dist/routes/organization-new.js +7 -4
- package/dist/routes/organization.js +13 -6
- package/dist/routes/project-new.js +8 -4
- package/dist/routes/project.d.ts +73 -2
- package/dist/routes/project.js +81 -9
- package/dist/routes/service-apply.d.ts +5 -2
- package/dist/routes/service-apply.js +17 -14
- package/dist/routes/service-history.d.ts +30 -0
- package/dist/routes/service-history.js +79 -0
- package/dist/routes/service-topology.d.ts +153 -0
- package/dist/routes/service-topology.js +54 -0
- package/dist/routes/service.d.ts +34 -10
- package/dist/routes/service.js +27 -22
- package/dist/routes/stream.service.js +18 -1
- package/dist/routes/tokens.js +8 -6
- package/dist/routes.js +2 -0
- package/dist/styles.css +2 -481
- package/dist/testing/fake-control-plane.d.ts +155 -0
- package/dist/testing/fake-control-plane.js +379 -12
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/scenarios.js +6 -0
- package/dist/theme.css +66 -0
- package/dist/ui/console.d.ts +19 -6
- package/dist/ui/console.js +9 -2
- package/dist/ui/errors.js +7 -5
- package/dist/ui/hosting.d.ts +11 -0
- package/dist/ui/hosting.js +20 -0
- package/dist/ui/primitives/button.d.ts +11 -0
- package/dist/ui/primitives/button.js +21 -0
- package/dist/ui/primitives/select.d.ts +10 -0
- package/dist/ui/primitives/select.js +4 -0
- package/dist/ui/primitives/switch.d.ts +9 -0
- package/dist/ui/primitives/switch.js +4 -0
- package/dist/ui/shape.d.ts +4 -0
- package/dist/ui/shape.js +36 -0
- package/dist/ui/shell.d.ts +76 -0
- package/dist/ui/shell.js +107 -0
- package/dist/ui/tokens.d.ts +82 -0
- package/dist/ui/tokens.js +172 -0
- package/dist/ui/topology/Graph.d.ts +13 -0
- package/dist/ui/topology/Graph.js +56 -0
- package/dist/ui/topology/Table.d.ts +10 -0
- package/dist/ui/topology/Table.js +14 -0
- package/dist/ui/topology/layout.d.ts +113 -0
- package/dist/ui/topology/layout.js +177 -0
- package/dist/ui/use-stream.d.ts +137 -1
- package/dist/ui/use-stream.js +11 -1
- package/package.json +11 -3
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { fakeControlPlane } from "./fake-control-plane.ts";
|
|
2
|
-
export type { FakeClaims, FakeControlPlane, FakeControlPlaneOptions, FakeSeed } from "./fake-control-plane.ts";
|
|
2
|
+
export type { FakeClaims, FakeControlPlane, FakeControlPlaneOptions, FakeSeed, FakeTopology } from "./fake-control-plane.ts";
|
|
3
3
|
export { fakeIssuer, fakeUsers } from "./fake-issuer.ts";
|
|
4
4
|
export type { FakeIssuer, FakeIssuerOptions, FakeUser } from "./fake-issuer.ts";
|
|
5
5
|
export { scenarios } from "./scenarios.ts";
|
|
@@ -27,8 +27,14 @@ export const scenarios = {
|
|
|
27
27
|
"US3-4a": "a service page and a listing follow the control plane's changes live",
|
|
28
28
|
"US3-5": "an exposed service's hostname is a link",
|
|
29
29
|
"US3-6": "logs with the CLI's choices, followed live",
|
|
30
|
+
"TOPO-1": "a member reads a deployed service's topology, merged across its instances, followed live",
|
|
31
|
+
"TOPO-2": "an instance that did not answer leaves the topology partial and is named",
|
|
30
32
|
"US3-7": "a deleted service is gone, and applying the name again continues its generation",
|
|
31
33
|
"US3-8": "a suspended service reads Suspended and its operations are refused, not hidden",
|
|
34
|
+
"RB-1": "a member rolls a service back from the console",
|
|
35
|
+
"RB-2": "the console offers a roll back only to a generation that can be rolled back to",
|
|
36
|
+
"RB-3": "the console shows a refused roll back as the control plane refused it",
|
|
37
|
+
"RB-4": "the console shows the image of each generation that was applied",
|
|
32
38
|
"US4-1": "an owner sees members and pending invitations",
|
|
33
39
|
"US4-2": "an invitation is claimed when the invited person signs in",
|
|
34
40
|
"US4-3": "roles change and members are removed; the last owner cannot be",
|
package/dist/theme.css
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* Generated from src/ui/tokens.ts by scripts/build.ts. Do not edit. */
|
|
2
|
+
@theme static {
|
|
3
|
+
--*: initial;
|
|
4
|
+
--spacing: 0.25rem;
|
|
5
|
+
--color-mesh-base: #111f26;
|
|
6
|
+
--color-mesh-slate: #1e2e3a;
|
|
7
|
+
--color-glow: rgba(212, 128, 88, 0.34);
|
|
8
|
+
--color-peach: rgba(222, 170, 132, 0.16);
|
|
9
|
+
--color-dot: rgba(255, 255, 255, 0.08);
|
|
10
|
+
--color-glass: rgba(255, 255, 255, 0.05);
|
|
11
|
+
--color-glass-strong: rgba(255, 255, 255, 0.09);
|
|
12
|
+
--color-glass-line: rgba(255, 255, 255, 0.2);
|
|
13
|
+
--color-tile: rgba(255, 255, 255, 0.06);
|
|
14
|
+
--color-tile-line: rgba(255, 255, 255, 0.14);
|
|
15
|
+
--color-node: rgba(255, 255, 255, 0.1);
|
|
16
|
+
--color-node-line: rgba(255, 255, 255, 0.38);
|
|
17
|
+
--color-inset: rgba(0, 0, 0, 0.2);
|
|
18
|
+
--color-edge: rgba(255, 255, 255, 0.7);
|
|
19
|
+
--color-ink: #f5f8f7;
|
|
20
|
+
--color-ink-2: #dfe8e6;
|
|
21
|
+
--color-ink-3: #ccd9d6;
|
|
22
|
+
--color-amber: #f0b83c;
|
|
23
|
+
--color-amber-ink: #13343b;
|
|
24
|
+
--color-green: #8fe0ad;
|
|
25
|
+
--color-red: #ffc4bc;
|
|
26
|
+
--color-red-wash: rgba(255, 138, 128, 0.16);
|
|
27
|
+
--shadow-part: 0 18px 50px rgba(5, 15, 18, 0.28);
|
|
28
|
+
--radius-panel: 20px;
|
|
29
|
+
--radius-card: 14px;
|
|
30
|
+
--radius-control: 10px;
|
|
31
|
+
--radius-node: 18px;
|
|
32
|
+
--font-sans: "Inter Variable", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
33
|
+
--font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
|
34
|
+
--size-rail: 56px;
|
|
35
|
+
--size-panel: 232px;
|
|
36
|
+
--size-inspector: 300px;
|
|
37
|
+
--size-bar: 64px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@layer theme {
|
|
41
|
+
:root:has(.ac-light) {
|
|
42
|
+
--ac-color-mesh-base: #e6eeee;
|
|
43
|
+
--ac-color-mesh-slate: #cfe0dc;
|
|
44
|
+
--ac-color-glow: rgba(224, 165, 38, 0.30);
|
|
45
|
+
--ac-color-peach: rgba(241, 213, 194, 0.80);
|
|
46
|
+
--ac-color-dot: rgba(19, 52, 59, 0.10);
|
|
47
|
+
--ac-color-glass: rgba(255, 255, 255, 0.50);
|
|
48
|
+
--ac-color-glass-strong: rgba(255, 255, 255, 0.85);
|
|
49
|
+
--ac-color-glass-line: rgba(19, 52, 59, 0.14);
|
|
50
|
+
--ac-color-tile: rgba(255, 255, 255, 0.50);
|
|
51
|
+
--ac-color-tile-line: rgba(19, 52, 59, 0.10);
|
|
52
|
+
--ac-color-node: rgba(255, 255, 255, 0.62);
|
|
53
|
+
--ac-color-node-line: rgba(19, 52, 59, 0.22);
|
|
54
|
+
--ac-color-inset: rgba(19, 52, 59, 0.06);
|
|
55
|
+
--ac-color-edge: rgba(19, 52, 59, 0.55);
|
|
56
|
+
--ac-color-ink: #13343b;
|
|
57
|
+
--ac-color-ink-2: #33504f;
|
|
58
|
+
--ac-color-ink-3: #4a6465;
|
|
59
|
+
--ac-color-amber: #e0a526;
|
|
60
|
+
--ac-color-amber-ink: #13343b;
|
|
61
|
+
--ac-color-green: #1f6b3a;
|
|
62
|
+
--ac-color-red: #a8231b;
|
|
63
|
+
--ac-color-red-wash: #fbe9e7;
|
|
64
|
+
--ac-shadow-part: 0 18px 50px rgba(19, 52, 59, 0.10);
|
|
65
|
+
}
|
|
66
|
+
}
|
package/dist/ui/console.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { type ReactNode } from "react";
|
|
7
7
|
import { type FormProps, type LinkProps } from "react-router";
|
|
8
8
|
import type { ConsoleExtensions, Operation } from "../extensions/types.ts";
|
|
9
|
-
import type { ConsolePageData } from "../context.ts";
|
|
9
|
+
import type { ConsolePageData, Crumb, ShellData } from "../context.ts";
|
|
10
10
|
/**
|
|
11
11
|
* Hands the host's extensions to the pages. Rendered in the host's layout, around its `<Outlet />`,
|
|
12
12
|
* with the same `extensions` object given to `consoleMiddleware`, so panels render in the browser as
|
|
@@ -18,6 +18,8 @@ export declare function ConsoleProvider({ extensions, children }: {
|
|
|
18
18
|
}): import("react").JSX.Element;
|
|
19
19
|
export interface UseConsole extends ConsolePageData {
|
|
20
20
|
extensions: ConsoleExtensions;
|
|
21
|
+
/** The shell around the page; empty on a host's own page that supplies none. */
|
|
22
|
+
shell: ShellData;
|
|
21
23
|
href(path?: string): string;
|
|
22
24
|
shows(operation: Operation): boolean;
|
|
23
25
|
}
|
|
@@ -37,10 +39,24 @@ export declare function ConsoleForm({ to, intent, children, ...rest }: Omit<Form
|
|
|
37
39
|
intent?: string;
|
|
38
40
|
children: ReactNode;
|
|
39
41
|
}): import("react").JSX.Element;
|
|
40
|
-
export declare function Submit({ intent, children, danger }: {
|
|
42
|
+
export declare function Submit({ intent, children, danger, primary, className, label, }: {
|
|
41
43
|
intent?: string;
|
|
42
44
|
children: ReactNode;
|
|
43
45
|
danger?: boolean;
|
|
46
|
+
primary?: boolean;
|
|
47
|
+
/** Replaces the button's own classes, for a control drawn differently (the shape's tab). */
|
|
48
|
+
className?: string;
|
|
49
|
+
/** The control's accessible name, when its content is an icon. */
|
|
50
|
+
label?: string;
|
|
51
|
+
}): import("react").JSX.Element;
|
|
52
|
+
/** One operation as a form with one button, posting its intent to the page it is on. */
|
|
53
|
+
export declare function OperationForm({ intent, children, danger, primary, className, label, }: {
|
|
54
|
+
intent: string;
|
|
55
|
+
children: ReactNode;
|
|
56
|
+
danger?: boolean;
|
|
57
|
+
primary?: boolean;
|
|
58
|
+
className?: string;
|
|
59
|
+
label?: string;
|
|
44
60
|
}): import("react").JSX.Element;
|
|
45
61
|
/** A labelled field; a refusal about it is announced with it. */
|
|
46
62
|
export declare function Field({ label, name, hint, error, ...input }: {
|
|
@@ -49,10 +65,7 @@ export declare function Field({ label, name, hint, error, ...input }: {
|
|
|
49
65
|
hint?: string;
|
|
50
66
|
error?: string;
|
|
51
67
|
} & React.InputHTMLAttributes<HTMLInputElement>): import("react").JSX.Element;
|
|
52
|
-
export
|
|
53
|
-
label: string;
|
|
54
|
-
to?: string;
|
|
55
|
-
}
|
|
68
|
+
export type { Crumb };
|
|
56
69
|
/** Where the page sits in the installation: organization, project, service. */
|
|
57
70
|
export declare function Breadcrumbs({ trail }: {
|
|
58
71
|
trail: Crumb[];
|
package/dist/ui/console.js
CHANGED
|
@@ -6,6 +6,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
6
6
|
*/
|
|
7
7
|
import { createContext, useContext } from "react";
|
|
8
8
|
import { Form, Link, useMatches, useNavigation } from "react-router";
|
|
9
|
+
import { button } from "./primitives/button.js";
|
|
9
10
|
const ExtensionsContext = createContext({});
|
|
10
11
|
/**
|
|
11
12
|
* Hands the host's extensions to the pages. Rendered in the host's layout, around its `<Outlet />`,
|
|
@@ -31,6 +32,7 @@ export function useConsole() {
|
|
|
31
32
|
mount,
|
|
32
33
|
principal: page?.principal ?? null,
|
|
33
34
|
hidden: [...hidden],
|
|
35
|
+
shell: page?.shell ?? { crumbs: [] },
|
|
34
36
|
extensions,
|
|
35
37
|
href: (path = "") => mount + path.replace(/^\/+/, ""),
|
|
36
38
|
shows: (operation) => !hidden.has(operation),
|
|
@@ -50,10 +52,15 @@ export function ConsoleForm({ to, intent, children, ...rest }) {
|
|
|
50
52
|
const { href } = useConsole();
|
|
51
53
|
return (_jsxs(Form, { method: "post", action: to === undefined ? undefined : href(to), ...rest, children: [intent ? _jsx("input", { type: "hidden", name: "intent", value: intent }) : null, children] }));
|
|
52
54
|
}
|
|
53
|
-
export function Submit({ intent, children, danger }) {
|
|
55
|
+
export function Submit({ intent, children, danger, primary, className, label, }) {
|
|
54
56
|
const navigation = useNavigation();
|
|
55
57
|
const busy = navigation.state === "submitting" && (intent === undefined || navigation.formData?.get("intent") === intent);
|
|
56
|
-
|
|
58
|
+
const classes = className ?? button({ variant: danger ? "danger" : primary ? "primary" : "default" });
|
|
59
|
+
return (_jsx("button", { type: "submit", className: classes, disabled: busy, "aria-busy": busy || undefined, "aria-label": label, title: label, children: children }));
|
|
60
|
+
}
|
|
61
|
+
/** One operation as a form with one button, posting its intent to the page it is on. */
|
|
62
|
+
export function OperationForm({ intent, children, danger, primary, className, label, }) {
|
|
63
|
+
return (_jsx(ConsoleForm, { intent: intent, children: _jsx(Submit, { intent: intent, danger: danger, primary: primary, className: className, label: label, children: children }) }));
|
|
57
64
|
}
|
|
58
65
|
/** A labelled field; a refusal about it is announced with it. */
|
|
59
66
|
export function Field({ label, name, hint, error, ...input }) {
|
package/dist/ui/errors.js
CHANGED
|
@@ -5,6 +5,8 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
5
5
|
*/
|
|
6
6
|
import { isRouteErrorResponse, useRouteError } from "react-router";
|
|
7
7
|
import { ConsoleLink } from "./console.js";
|
|
8
|
+
import { Page } from "./shell.js";
|
|
9
|
+
import { button } from "./primitives/button.js";
|
|
8
10
|
/** A refusal from the control plane, shown beside the thing refused, in the control plane's words. */
|
|
9
11
|
export function Refusal({ reason, problems }) {
|
|
10
12
|
if (!reason && (!problems || problems.length === 0))
|
|
@@ -16,15 +18,15 @@ export function ConsoleErrorBoundary() {
|
|
|
16
18
|
if (isRouteErrorResponse(error) && error.data && typeof error.data === "object" && "kind" in error.data) {
|
|
17
19
|
const failure = error.data;
|
|
18
20
|
if (failure.kind === "lost") {
|
|
19
|
-
return (_jsxs("section", { className: "ac-
|
|
21
|
+
return (_jsx(Page, { children: _jsxs("section", { className: "ac-card ac-failure", children: [_jsx("h1", { children: "You no longer have access to this" }), _jsx("p", { children: "It was deleted, or you are no longer a member of the organization it belongs to." }), _jsx("p", { children: _jsx(ConsoleLink, { to: "", children: "Go to your organizations" }) })] }) }));
|
|
20
22
|
}
|
|
21
23
|
if (failure.kind === "unavailable") {
|
|
22
|
-
return (_jsxs("section", { className: "ac-
|
|
24
|
+
return (_jsx(Page, { children: _jsxs("section", { className: "ac-card ac-failure", children: [_jsxs("h1", { children: ["The ", failure.what, " is not answering"] }), _jsx("p", { children: "Your session is kept. Try again in a few seconds." }), _jsx("p", { className: "ac-hint", children: failure.reason }), _jsx("p", { children: _jsx("a", { className: button(), href: "", children: "Try again" }) })] }) }));
|
|
23
25
|
}
|
|
24
|
-
return (_jsxs("section", { className: "ac-
|
|
26
|
+
return (_jsx(Page, { children: _jsxs("section", { className: "ac-card ac-failure", children: [_jsx("h1", { children: "This was refused" }), _jsx(Refusal, { reason: failure.reason }), _jsx("p", { children: _jsx(ConsoleLink, { to: "", children: "Go to your organizations" }) })] }) }));
|
|
25
27
|
}
|
|
26
28
|
if (isRouteErrorResponse(error) && error.status === 404) {
|
|
27
|
-
return (_jsxs("section", { className: "ac-
|
|
29
|
+
return (_jsx(Page, { children: _jsxs("section", { className: "ac-card ac-failure", children: [_jsx("h1", { children: "There is no page here" }), _jsx("p", { children: _jsx(ConsoleLink, { to: "", children: "Go to your organizations" }) })] }) }));
|
|
28
30
|
}
|
|
29
|
-
return (_jsxs("section", { className: "ac-
|
|
31
|
+
return (_jsx(Page, { children: _jsxs("section", { className: "ac-card ac-failure", children: [_jsx("h1", { children: "The console could not show this page" }), _jsx("p", { children: "Reload to try again. If it keeps happening, the console's own log says why." })] }) }));
|
|
30
32
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** How a deployed service's own code is hosted, in words: the facts list and the shape both say it. */
|
|
2
|
+
export declare function runsAs(s: {
|
|
3
|
+
hosting: string;
|
|
4
|
+
protocol?: string;
|
|
5
|
+
processPort?: number;
|
|
6
|
+
}): string;
|
|
7
|
+
/** The same, short enough for a part of the shape. */
|
|
8
|
+
export declare function hostedAs(s: {
|
|
9
|
+
hosting: string;
|
|
10
|
+
processPort?: number;
|
|
11
|
+
}): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** How a deployed service's own code is hosted, in words: the facts list and the shape both say it. */
|
|
2
|
+
export function runsAs(s) {
|
|
3
|
+
if (s.hosting === "web")
|
|
4
|
+
return `Your program beside the platform's proxy${s.processPort ? `, on port ${s.processPort}` : ""}`;
|
|
5
|
+
if (s.hosting === "process")
|
|
6
|
+
return `A process beside the platform's sidecar${s.protocol ? `, protocol ${s.protocol}` : ""}`;
|
|
7
|
+
if (s.hosting === "wasm")
|
|
8
|
+
return `A module loaded into the platform's runtime${s.protocol ? `, protocol ${s.protocol}` : ""}`;
|
|
9
|
+
return "Embedded in the platform's runtime";
|
|
10
|
+
}
|
|
11
|
+
/** The same, short enough for a part of the shape. */
|
|
12
|
+
export function hostedAs(s) {
|
|
13
|
+
if (s.hosting === "web")
|
|
14
|
+
return s.processPort ? `Your program, port ${s.processPort}` : "Your program";
|
|
15
|
+
if (s.hosting === "process")
|
|
16
|
+
return "A process beside the sidecar";
|
|
17
|
+
if (s.hosting === "wasm")
|
|
18
|
+
return "A module in the runtime";
|
|
19
|
+
return "In the platform's runtime";
|
|
20
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The button recipe: the classes for a button, or a link drawn as one, by variant and size. In the
|
|
3
|
+
* shape of shadcn's recipes, drawn on the console's own `ac-` component classes so a host keeps the
|
|
4
|
+
* class names the documentation promises.
|
|
5
|
+
*/
|
|
6
|
+
import { type VariantProps } from "class-variance-authority";
|
|
7
|
+
export declare const button: (props?: ({
|
|
8
|
+
variant?: "danger" | "default" | "primary" | "quiet" | null | undefined;
|
|
9
|
+
size?: "default" | "small" | null | undefined;
|
|
10
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
11
|
+
export type ButtonVariants = VariantProps<typeof button>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The button recipe: the classes for a button, or a link drawn as one, by variant and size. In the
|
|
3
|
+
* shape of shadcn's recipes, drawn on the console's own `ac-` component classes so a host keeps the
|
|
4
|
+
* class names the documentation promises.
|
|
5
|
+
*/
|
|
6
|
+
import { cva } from "class-variance-authority";
|
|
7
|
+
export const button = cva("ac-button", {
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "",
|
|
11
|
+
primary: "ac-button-primary",
|
|
12
|
+
danger: "ac-button-danger",
|
|
13
|
+
quiet: "ac-button-quiet",
|
|
14
|
+
},
|
|
15
|
+
size: {
|
|
16
|
+
default: "",
|
|
17
|
+
small: "ac-button-small",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultVariants: { variant: "default", size: "default" },
|
|
21
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A choice: the browser's own select, labelled. A choice drawn by script cannot be made without it;
|
|
3
|
+
* this one can, and it submits with its form.
|
|
4
|
+
*/
|
|
5
|
+
import type { ReactNode, SelectHTMLAttributes } from "react";
|
|
6
|
+
export declare function Select({ label, id, children, ...select }: {
|
|
7
|
+
label: string;
|
|
8
|
+
id: string;
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
} & SelectHTMLAttributes<HTMLSelectElement>): import("react").JSX.Element;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function Select({ label, id, children, ...select }) {
|
|
3
|
+
return (_jsxs("div", { className: "ac-field", children: [_jsx("label", { htmlFor: id, children: label }), _jsx("select", { id: id, ...select, children: children })] }));
|
|
4
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A switch: the browser's own checkbox, drawn as a track. It is a form control, so it submits with
|
|
3
|
+
* its form when no script runs, which a switch drawn as a button and toggled by script cannot.
|
|
4
|
+
*/
|
|
5
|
+
import type { InputHTMLAttributes } from "react";
|
|
6
|
+
export declare function Switch({ label, id, ...input }: {
|
|
7
|
+
label: string;
|
|
8
|
+
id: string;
|
|
9
|
+
} & Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "id">): import("react").JSX.Element;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function Switch({ label, id, ...input }) {
|
|
3
|
+
return (_jsxs("label", { className: "ac-switch", htmlFor: id, children: [_jsx("span", { children: label }), _jsx("input", { type: "checkbox", id: id, ...input }), _jsx("span", { className: "ac-switch-track", "aria-hidden": "true" })] }));
|
|
4
|
+
}
|
package/dist/ui/shape.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Database, FileText, Globe, Layers, Pause, Play, RotateCw, Route } from "lucide-react";
|
|
3
|
+
import { ConsoleLink, OperationForm, useConsole } from "./console.js";
|
|
4
|
+
import { Lifecycle } from "./status.js";
|
|
5
|
+
import { hostedAs } from "./hosting.js";
|
|
6
|
+
const icon = { strokeWidth: 1.25, "aria-hidden": true };
|
|
7
|
+
/** The joins from one part to `to` parts side by side below it, each arriving at its part's centre. */
|
|
8
|
+
function Join({ to }) {
|
|
9
|
+
if (to <= 1)
|
|
10
|
+
return (_jsx("svg", { className: "ac-edge", viewBox: "0 0 100 48", preserveAspectRatio: "none", "aria-hidden": "true", children: _jsx("path", { d: "M50 0 V48" }) }));
|
|
11
|
+
const xs = Array.from({ length: to }, (_x, i) => ((2 * i + 1) / (2 * to)) * 100);
|
|
12
|
+
return (_jsx("svg", { className: "ac-edge ac-edge-split", viewBox: "0 0 100 48", preserveAspectRatio: "none", "aria-hidden": "true", children: xs.map((x) => (_jsx("path", { d: `M50 0 C50 28 ${x} 20 ${x} 48` }, x))) }));
|
|
13
|
+
}
|
|
14
|
+
const mountTrouble = {
|
|
15
|
+
"no service": "No service of that name",
|
|
16
|
+
"serves no HTTP": "Serves no HTTP",
|
|
17
|
+
paused: "Paused",
|
|
18
|
+
};
|
|
19
|
+
export function Shape({ service: s }) {
|
|
20
|
+
const { shows } = useConsole();
|
|
21
|
+
const path = `projects/${encodeURIComponent(s.projectId)}/services/${encodeURIComponent(s.name)}`;
|
|
22
|
+
const address = s.hostname?.replace(/^https?:\/\//, "");
|
|
23
|
+
const web = s.hosting === "web";
|
|
24
|
+
const project = `projects/${encodeURIComponent(s.projectId)}/services/`;
|
|
25
|
+
const children = [
|
|
26
|
+
_jsxs("div", { className: "ac-part ac-part-up", "data-part": "instances", children: [_jsx("span", { className: "ac-part-icon", children: _jsx(Layers, { ...icon }) }), _jsx("span", { className: "ac-part-title", children: "Instances" }), _jsxs("span", { className: "ac-part-sub", children: [s.readyInstances, " of ", s.desiredInstances, " ready"] })] }, "instances"),
|
|
27
|
+
...(web
|
|
28
|
+
? s.mounts.map((m) => (_jsxs("div", { className: "ac-part ac-part-up", "data-part": "mount", "data-mount": m.path, children: [_jsx("span", { className: "ac-part-icon", children: _jsx(Route, { ...icon }) }), _jsx("span", { className: "ac-part-title", title: m.service, children: m.state === "no service" ? m.service : _jsx(ConsoleLink, { to: project + encodeURIComponent(m.service), children: m.service }) }), _jsx("span", { className: "ac-part-sub", title: m.path, children: m.path }), m.state && m.state !== "ok" ? (_jsx("span", { className: "ac-part-inset", children: _jsxs("span", { className: "ac-status ac-status-trouble", children: [_jsx("span", { className: "ac-status-mark", "aria-hidden": "true", children: "\u25B2" }), mountTrouble[m.state] ?? m.state] }) })) : null] }, m.path)))
|
|
29
|
+
: [
|
|
30
|
+
_jsxs("div", { className: "ac-part ac-part-up", "data-part": "database", children: [_jsx("span", { className: "ac-part-icon", children: _jsx(Database, { ...icon }) }), _jsx("span", { className: "ac-part-title", title: s.database, children: s.database ?? "Not reported yet" }), _jsx("span", { className: "ac-part-sub", children: s.database ? "Kept if the service is deleted" : "The cluster has not reported it" })] }, "database"),
|
|
31
|
+
]),
|
|
32
|
+
];
|
|
33
|
+
return (_jsx("figure", { className: "ac-shape", "aria-label": `What the platform runs for ${s.name}`, children: _jsxs("div", { className: "ac-tree", children: [s.exposed ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "ac-row", children: _jsxs("div", { className: "ac-part ac-part-down", "data-part": "hostname", children: [_jsx("span", { className: "ac-part-icon", children: _jsx(Globe, { ...icon }) }), _jsx("span", { className: "ac-part-title", title: address, children: address ?? "No address yet" }), _jsx("span", { className: "ac-part-sub", children: address ? "On the installation's gateway" : "Exposed; no address reported yet" })] }) }), _jsx(Join, { to: 1 })] })) : null, _jsx("div", { className: "ac-row", children: _jsxs("div", { className: `ac-part ac-part-self ac-part-tabbed ac-part-down${s.exposed ? " ac-part-up" : ""}`, "data-part": "service", children: [_jsxs("span", { className: "ac-part-tab", "aria-label": `Controls of ${s.name}`, role: "group", children: [shows("service.logs") ? (_jsx(ConsoleLink, { to: `${path}/logs`, className: "ac-tab-control", "aria-label": "Logs", title: "Logs", children: _jsx(FileText, { "aria-hidden": "true" }) })) : null, s.paused
|
|
34
|
+
? shows("service.resume") && (_jsx(OperationForm, { intent: "resume", className: "ac-tab-control", label: "Resume", children: _jsx(Play, { "aria-hidden": "true" }) }))
|
|
35
|
+
: shows("service.pause") && (_jsx(OperationForm, { intent: "pause", className: "ac-tab-control", label: "Pause", children: _jsx(Pause, { "aria-hidden": "true" }) })), shows("service.restart") ? (_jsx(OperationForm, { intent: "restart", className: "ac-tab-control", label: "Restart", children: _jsx(RotateCw, { "aria-hidden": "true" }) })) : null] }), _jsx("span", { className: "ac-part-icon", children: _jsx(Box, { ...icon }) }), _jsx("span", { className: "ac-part-title", children: s.name }), _jsxs("span", { className: "ac-part-sub", title: s.image, children: ["Generation ", s.generation, ", ", s.image] }), _jsxs("span", { className: "ac-part-inset", children: [_jsx("span", { children: hostedAs(s) }), _jsx(Lifecycle, { lifecycle: s.lifecycle, confirmed: s.confirmed })] })] }) }), children.length > 0 ? (_jsxs(_Fragment, { children: [_jsx(Join, { to: children.length }), _jsx("div", { className: "ac-row ac-row-many", style: { gridTemplateColumns: `repeat(${children.length}, minmax(0, 1fr))` }, children: children })] })) : null] }) }));
|
|
36
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shell every console page is shown inside: the backdrop, a rail of areas, a bar saying where
|
|
3
|
+
* the member is, a listing of what sits beside the page, the page, and an inspector holding what the
|
|
4
|
+
* page offers to be done. A host composes the parts it has content for; the backdrop and the bar are
|
|
5
|
+
* the least it mounts. The installation's console mounts them all:
|
|
6
|
+
*
|
|
7
|
+
* ```tsx
|
|
8
|
+
* <Shell><Backdrop /><Rail /><Bar /><Listing /><Outlet /></Shell>
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* The rail, the bar and the listing read the shell's data from whichever package page is matched;
|
|
12
|
+
* each page renders its own body and inspector through `Page`, so its forms, refusals and busy
|
|
13
|
+
* states stay where they are and work without scripts.
|
|
14
|
+
*/
|
|
15
|
+
import type { ReactNode } from "react";
|
|
16
|
+
/**
|
|
17
|
+
* The grid that lays out whichever parts it is given. It is the console's root. The console is
|
|
18
|
+
* dark; a host that wants the light theme says so here.
|
|
19
|
+
*/
|
|
20
|
+
export declare function Shell({ children, className, theme }: {
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
className?: string;
|
|
23
|
+
theme?: "dark" | "light";
|
|
24
|
+
}): import("react").JSX.Element;
|
|
25
|
+
/** The mesh behind every surface: slate, the glow at the centre of the screen, the dot grid. */
|
|
26
|
+
export declare function Backdrop(): import("react").JSX.Element;
|
|
27
|
+
/**
|
|
28
|
+
* The bar: the wordmark and anything a host adds beside it (its own navigation), where the member
|
|
29
|
+
* is, the page's primary operation, and who is signed in. `end` replaces the last of these, for a
|
|
30
|
+
* host whose visitors may not be signed in.
|
|
31
|
+
*/
|
|
32
|
+
export declare function Bar({ wordmark, children, end }: {
|
|
33
|
+
wordmark?: ReactNode;
|
|
34
|
+
children?: ReactNode;
|
|
35
|
+
end?: ReactNode;
|
|
36
|
+
}): import("react").JSX.Element;
|
|
37
|
+
/**
|
|
38
|
+
* The rail: the console's areas, the one the page belongs to marked. Members and deploy tokens open
|
|
39
|
+
* those of the organization the page belongs to; with no organization in view they are shown and
|
|
40
|
+
* unavailable. Signing out is at its foot.
|
|
41
|
+
*/
|
|
42
|
+
export declare function Rail(): import("react").JSX.Element;
|
|
43
|
+
/** The listing beside the page: a project's services or an organization's projects. */
|
|
44
|
+
export declare function Listing(): import("react").JSX.Element | null;
|
|
45
|
+
/** The column of what a page offers to be done. */
|
|
46
|
+
export declare function Inspector({ children, label }: {
|
|
47
|
+
children: ReactNode;
|
|
48
|
+
label?: string;
|
|
49
|
+
}): import("react").JSX.Element;
|
|
50
|
+
/** A page: its body, and the inspector beside it when it offers anything. */
|
|
51
|
+
export declare function Page({ children, inspector, className }: {
|
|
52
|
+
children: ReactNode;
|
|
53
|
+
inspector?: ReactNode;
|
|
54
|
+
className?: string;
|
|
55
|
+
}): import("react").JSX.Element;
|
|
56
|
+
/** A heading for a group in a side column. */
|
|
57
|
+
export declare function SectionTitle({ children }: {
|
|
58
|
+
children: ReactNode;
|
|
59
|
+
}): import("react").JSX.Element;
|
|
60
|
+
export interface SegmentLink {
|
|
61
|
+
label: string;
|
|
62
|
+
to: string;
|
|
63
|
+
current?: boolean;
|
|
64
|
+
}
|
|
65
|
+
/** A page's sections, as links: each is a page of its own. */
|
|
66
|
+
export declare function SegmentedLinks({ label, links }: {
|
|
67
|
+
label: string;
|
|
68
|
+
links: SegmentLink[];
|
|
69
|
+
}): import("react").JSX.Element;
|
|
70
|
+
export type ServiceSection = "overview" | "topology" | "logs" | "history";
|
|
71
|
+
/** A service's four sections. */
|
|
72
|
+
export declare function ServiceSections({ projectId, name, current }: {
|
|
73
|
+
projectId: string;
|
|
74
|
+
name: string;
|
|
75
|
+
current: ServiceSection;
|
|
76
|
+
}): import("react").JSX.Element;
|
package/dist/ui/shell.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { clsx } from "clsx";
|
|
3
|
+
import { Box, Building2, Folder, KeyRound, LogOut, Plus, Users } from "lucide-react";
|
|
4
|
+
import { Breadcrumbs, ConsoleForm, ConsoleLink, useConsole } from "./console.js";
|
|
5
|
+
import { Lifecycle } from "./status.js";
|
|
6
|
+
import { button } from "./primitives/button.js";
|
|
7
|
+
/**
|
|
8
|
+
* The grid that lays out whichever parts it is given. It is the console's root. The console is
|
|
9
|
+
* dark; a host that wants the light theme says so here.
|
|
10
|
+
*/
|
|
11
|
+
export function Shell({ children, className, theme = "dark" }) {
|
|
12
|
+
return _jsx("div", { className: clsx("ac-root ac-shell", theme === "light" && "ac-light", className), children: children });
|
|
13
|
+
}
|
|
14
|
+
/** The mesh behind every surface: slate, the glow at the centre of the screen, the dot grid. */
|
|
15
|
+
export function Backdrop() {
|
|
16
|
+
return _jsx("div", { className: "ac-backdrop", "aria-hidden": "true" });
|
|
17
|
+
}
|
|
18
|
+
function Mark() {
|
|
19
|
+
return (_jsxs("svg", { viewBox: "0 0 36 36", width: "32", height: "32", fill: "none", "aria-hidden": "true", children: [_jsx("path", { d: "M8 24c0-7 5-12 10-12s10 5 10 12", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round" }), _jsx("path", { d: "M14 24h8", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round" }), _jsx("circle", { cx: "18", cy: "9", r: "2.2", fill: "var(--ac-color-amber)" })] }));
|
|
20
|
+
}
|
|
21
|
+
const initials = (name) => name
|
|
22
|
+
.split(/[\s@.]+/)
|
|
23
|
+
.filter(Boolean)
|
|
24
|
+
.slice(0, 2)
|
|
25
|
+
.map((w) => w[0].toUpperCase())
|
|
26
|
+
.join("");
|
|
27
|
+
/**
|
|
28
|
+
* The bar: the wordmark and anything a host adds beside it (its own navigation), where the member
|
|
29
|
+
* is, the page's primary operation, and who is signed in. `end` replaces the last of these, for a
|
|
30
|
+
* host whose visitors may not be signed in.
|
|
31
|
+
*/
|
|
32
|
+
export function Bar({ wordmark, children, end }) {
|
|
33
|
+
const { shell, principal, shows } = useConsole();
|
|
34
|
+
const name = principal ? (principal.name ?? principal.email ?? principal.subject) : null;
|
|
35
|
+
const primary = shell.primary && (!shell.primary.operation || shows(shell.primary.operation)) ? shell.primary : null;
|
|
36
|
+
return (_jsxs("header", { className: "ac-bar", children: [_jsxs("div", { className: "ac-bar-start", children: [wordmark ?? (_jsxs(ConsoleLink, { to: "", className: "ac-wordmark", children: ["ankka ", _jsx("span", { children: "console" })] })), children] }), shell.crumbs.length > 0 ? _jsx(Breadcrumbs, { trail: shell.crumbs }) : _jsx("span", {}), _jsxs("div", { className: "ac-bar-end", children: [primary ? (_jsxs(ConsoleLink, { to: primary.to, className: button({ variant: "primary" }), "data-primary": true, children: [_jsx(Plus, { "aria-hidden": "true" }), primary.label] })) : null, end ??
|
|
37
|
+
(name ? (_jsxs("span", { className: "ac-person", title: name, children: [_jsx("span", { "aria-hidden": "true", children: initials(name) }), _jsxs("span", { className: "ac-visually-hidden", children: ["You are ", name] })] })) : null)] })] }));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The rail: the console's areas, the one the page belongs to marked. Members and deploy tokens open
|
|
41
|
+
* those of the organization the page belongs to; with no organization in view they are shown and
|
|
42
|
+
* unavailable. Signing out is at its foot.
|
|
43
|
+
*/
|
|
44
|
+
export function Rail() {
|
|
45
|
+
const { shell } = useConsole();
|
|
46
|
+
const org = shell.organization;
|
|
47
|
+
const project = shell.project;
|
|
48
|
+
const orgPath = org ? `organizations/${encodeURIComponent(org.id)}` : undefined;
|
|
49
|
+
const entries = [
|
|
50
|
+
{ area: "organizations", label: "Organizations", icon: Building2, to: "", unavailable: "" },
|
|
51
|
+
{ area: "projects", label: org ? `Projects of ${org.name}` : "Projects", icon: Folder, to: orgPath, unavailable: "Projects: open an organization first" },
|
|
52
|
+
{
|
|
53
|
+
area: "services",
|
|
54
|
+
label: project ? `Services of ${project.name}` : "Services",
|
|
55
|
+
icon: Box,
|
|
56
|
+
to: project ? `projects/${encodeURIComponent(project.id)}` : undefined,
|
|
57
|
+
unavailable: "Services: open a project first",
|
|
58
|
+
},
|
|
59
|
+
{ area: "members", label: org ? `Members of ${org.name}` : "Members", icon: Users, to: orgPath && `${orgPath}/members`, unavailable: "Members: open an organization first" },
|
|
60
|
+
{
|
|
61
|
+
area: "tokens",
|
|
62
|
+
label: org ? `Deploy tokens of ${org.name}` : "Deploy tokens",
|
|
63
|
+
icon: KeyRound,
|
|
64
|
+
to: orgPath && org?.manages ? `${orgPath}/tokens` : undefined,
|
|
65
|
+
unavailable: org ? "Deploy tokens: only an owner manages them" : "Deploy tokens: open an organization first",
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
return (_jsxs("nav", { className: "ac-rail", "aria-label": "Console", children: [_jsx(ConsoleLink, { to: "", className: "ac-mark", "aria-label": "ankka console", title: "ankka console", children: _jsx(Mark, {}) }), _jsx("ul", { className: "ac-rail-areas", children: entries.map((e) => {
|
|
69
|
+
const Icon = e.icon;
|
|
70
|
+
return (_jsx("li", { "data-area": e.area, children: e.to !== undefined ? (_jsx(ConsoleLink, { to: e.to, className: "ac-rail-item", "aria-label": e.label, title: e.label, "aria-current": shell.area === e.area ? "true" : undefined, children: _jsx(Icon, { "aria-hidden": "true" }) })) : (_jsx("span", { className: "ac-rail-item", role: "link", "aria-disabled": "true", "aria-label": e.unavailable, title: e.unavailable, children: _jsx(Icon, { "aria-hidden": "true" }) })) }, e.area));
|
|
71
|
+
}) }), _jsx("div", { className: "ac-rail-foot", children: _jsx(ConsoleForm, { to: "auth/sign-out", children: _jsx("button", { type: "submit", className: "ac-rail-item", "aria-label": "Sign out", title: "Sign out", children: _jsx(LogOut, { "aria-hidden": "true" }) }) }) })] }));
|
|
72
|
+
}
|
|
73
|
+
/** The listing beside the page: a project's services or an organization's projects. */
|
|
74
|
+
export function Listing() {
|
|
75
|
+
const { shell } = useConsole();
|
|
76
|
+
const l = shell.listing;
|
|
77
|
+
if (!l)
|
|
78
|
+
return null;
|
|
79
|
+
return (_jsxs("nav", { className: "ac-listing", "aria-label": l.label, children: [_jsxs("p", { className: "ac-section-title", children: [_jsx("span", { children: l.title }), _jsx("span", { children: l.items.length })] }), l.items.length === 0 ? _jsx("p", { className: "ac-hint", children: "Nothing here yet." }) : null, _jsx("ul", { className: "ac-listing-items", children: l.items.map((i) => (_jsx("li", { "data-item": i.key, children: _jsxs(ConsoleLink, { to: i.to, className: "ac-listing-item", "aria-current": i.key === l.current ? "page" : undefined, children: [_jsx("span", { className: "ac-listing-name", children: i.name }), i.kind === "service" ? (_jsxs(_Fragment, { children: [_jsxs("span", { className: "ac-listing-count", children: [i.ready, " of ", i.desired] }), _jsx(Lifecycle, { lifecycle: i.lifecycle, confirmed: i.confirmed })] })) : (_jsxs("span", { className: "ac-listing-count", children: [i.services, " service", i.services === 1 ? "" : "s"] }))] }) }, i.key))) })] }));
|
|
80
|
+
}
|
|
81
|
+
/** The column of what a page offers to be done. */
|
|
82
|
+
export function Inspector({ children, label = "Operations" }) {
|
|
83
|
+
return (_jsx("aside", { className: "ac-inspector", "aria-label": label, children: children }));
|
|
84
|
+
}
|
|
85
|
+
/** A page: its body, and the inspector beside it when it offers anything. */
|
|
86
|
+
export function Page({ children, inspector, className }) {
|
|
87
|
+
return (_jsxs("div", { className: "ac-page", children: [_jsx("main", { className: clsx("ac-body", className), id: "main", children: children }), inspector ? _jsx(Inspector, { children: inspector }) : null] }));
|
|
88
|
+
}
|
|
89
|
+
/** A heading for a group in a side column. */
|
|
90
|
+
export function SectionTitle({ children }) {
|
|
91
|
+
return _jsx("h2", { className: "ac-section-title", children: children });
|
|
92
|
+
}
|
|
93
|
+
/** A page's sections, as links: each is a page of its own. */
|
|
94
|
+
export function SegmentedLinks({ label, links }) {
|
|
95
|
+
return (_jsx("nav", { className: "ac-seg", "aria-label": label, children: links.map((l) => (_jsx(ConsoleLink, { to: l.to, "aria-current": l.current ? "page" : undefined, children: l.label }, l.to))) }));
|
|
96
|
+
}
|
|
97
|
+
/** A service's four sections. */
|
|
98
|
+
export function ServiceSections({ projectId, name, current }) {
|
|
99
|
+
const path = `projects/${encodeURIComponent(projectId)}/services/${encodeURIComponent(name)}`;
|
|
100
|
+
const links = [
|
|
101
|
+
["overview", "Overview", path],
|
|
102
|
+
["topology", "Topology", `${path}/topology`],
|
|
103
|
+
["logs", "Logs", `${path}/logs`],
|
|
104
|
+
["history", "History", `${path}/history`],
|
|
105
|
+
];
|
|
106
|
+
return _jsx(SegmentedLinks, { label: "Sections of the service", links: links.map(([s, label, to]) => ({ label, to, current: s === current })) });
|
|
107
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The console's look as values: the one source the stylesheet is generated from, the contrast test
|
|
3
|
+
* reads, and the documentation's table of properties is written from. Each becomes a `--ac-*`
|
|
4
|
+
* custom property a host may set; the names are a contract, the values are not.
|
|
5
|
+
*
|
|
6
|
+
* Text sits on translucent surfaces, so its contrast depends on what is behind it. The backdrop is
|
|
7
|
+
* brightest where the glow peaks, and every ink must keep 4.5:1 on every surface there, in both
|
|
8
|
+
* themes. The glow's alpha is therefore a limit: `test/contrast.test.ts` fails if it is raised past
|
|
9
|
+
* what the inks can stand.
|
|
10
|
+
*/
|
|
11
|
+
export type Rgba = string;
|
|
12
|
+
export interface Theme {
|
|
13
|
+
mesh: {
|
|
14
|
+
base: Rgba;
|
|
15
|
+
slate: Rgba;
|
|
16
|
+
glow: Rgba;
|
|
17
|
+
peach: Rgba;
|
|
18
|
+
dot: Rgba;
|
|
19
|
+
};
|
|
20
|
+
surface: {
|
|
21
|
+
glass: Rgba;
|
|
22
|
+
glassStrong: Rgba;
|
|
23
|
+
glassLine: Rgba;
|
|
24
|
+
tile: Rgba;
|
|
25
|
+
tileLine: Rgba;
|
|
26
|
+
node: Rgba;
|
|
27
|
+
nodeLine: Rgba;
|
|
28
|
+
inset: Rgba;
|
|
29
|
+
edge: Rgba;
|
|
30
|
+
};
|
|
31
|
+
ink: {
|
|
32
|
+
ink: Rgba;
|
|
33
|
+
ink2: Rgba;
|
|
34
|
+
ink3: Rgba;
|
|
35
|
+
};
|
|
36
|
+
accent: {
|
|
37
|
+
amber: Rgba;
|
|
38
|
+
amberInk: Rgba;
|
|
39
|
+
green: Rgba;
|
|
40
|
+
red: Rgba;
|
|
41
|
+
redWash: Rgba;
|
|
42
|
+
};
|
|
43
|
+
shadow: string;
|
|
44
|
+
}
|
|
45
|
+
export interface Tokens {
|
|
46
|
+
dark: Theme;
|
|
47
|
+
light: Theme;
|
|
48
|
+
radius: {
|
|
49
|
+
panel: string;
|
|
50
|
+
card: string;
|
|
51
|
+
control: string;
|
|
52
|
+
node: string;
|
|
53
|
+
};
|
|
54
|
+
font: {
|
|
55
|
+
sans: string;
|
|
56
|
+
mono: string;
|
|
57
|
+
};
|
|
58
|
+
size: {
|
|
59
|
+
rail: string;
|
|
60
|
+
panel: string;
|
|
61
|
+
inspector: string;
|
|
62
|
+
bar: string;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export declare const tokens: Tokens;
|
|
66
|
+
/** Every colour property of a theme, by its CSS name without the `--ac-` prefix. */
|
|
67
|
+
export declare function colours(theme: Theme): [string, Rgba][];
|
|
68
|
+
/** The properties that are the same in both themes. */
|
|
69
|
+
export declare function constants(t: Tokens): [string, string][];
|
|
70
|
+
/**
|
|
71
|
+
* The stylesheet's theme. The console is dark; its values are the theme itself. Light redefines the
|
|
72
|
+
* same properties when a host chooses it, by putting `ac-light` on the shell. Browsers report a
|
|
73
|
+
* preference for light when nobody has stated one, so the browser's preference is not followed.
|
|
74
|
+
* Both sit on `:root` and inside the `theme` layer, so a host's own, unlayered declaration of any of
|
|
75
|
+
* them wins in either theme, on any element.
|
|
76
|
+
*
|
|
77
|
+
* Tailwind prefixes the `@theme` names (`--color-ink` becomes `--ac-color-ink`); the light block is
|
|
78
|
+
* plain CSS and names the prefixed property itself.
|
|
79
|
+
*/
|
|
80
|
+
export declare function toCss(t: Tokens): string;
|
|
81
|
+
/** What each property is for, by its name without the `--ac-` prefix: the documentation's table. */
|
|
82
|
+
export declare const roles: Record<string, string>;
|