@calliopelabs/ui 0.12.0 → 0.14.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/README.md +68 -3
- package/dist/agent/AgentChat.d.ts +56 -0
- package/dist/agent/AgentChat.js +76 -0
- package/dist/agent/AgentLauncher.d.ts +25 -0
- package/dist/agent/AgentLauncher.js +25 -0
- package/dist/agent/AgentProvider.d.ts +82 -0
- package/dist/agent/AgentProvider.js +176 -0
- package/dist/agent/CalliopeAgent.d.ts +43 -0
- package/dist/agent/CalliopeAgent.js +111 -0
- package/dist/agent/controller.d.ts +148 -0
- package/dist/agent/controller.js +463 -0
- package/dist/agent/copilotkit/CopilotKitAgentProvider.d.ts +41 -0
- package/dist/agent/copilotkit/CopilotKitAgentProvider.js +145 -0
- package/dist/agent/copilotkit/index.d.ts +6 -0
- package/dist/agent/copilotkit/index.js +6 -0
- package/dist/agent/engine.d.ts +41 -0
- package/dist/agent/engine.js +93 -0
- package/dist/agent/index.d.ts +15 -0
- package/dist/agent/index.js +15 -0
- package/dist/agent/mock.d.ts +34 -0
- package/dist/agent/mock.js +112 -0
- package/dist/agent/surface.d.ts +30 -0
- package/dist/agent/surface.js +90 -0
- package/dist/agent/tool.d.ts +98 -0
- package/dist/agent/tool.js +81 -0
- package/dist/appearance/brands.d.ts +14 -12
- package/dist/appearance/brands.js +18 -51
- package/dist/chat/AgentProgress.d.ts +19 -0
- package/dist/chat/AgentProgress.js +34 -0
- package/dist/chat/Composer.d.ts +31 -0
- package/dist/chat/Composer.js +44 -0
- package/dist/chat/MessageBubble.d.ts +23 -0
- package/dist/chat/MessageBubble.js +26 -0
- package/dist/chat/MessageList.d.ts +36 -0
- package/dist/chat/MessageList.js +68 -0
- package/dist/chat/ModelPicker.d.ts +17 -0
- package/dist/chat/ModelPicker.js +13 -0
- package/dist/chat/ToolCallCard.d.ts +27 -0
- package/dist/chat/ToolCallCard.js +52 -0
- package/dist/chat/model.d.ts +123 -0
- package/dist/chat/model.js +337 -0
- package/dist/chat/patch.d.ts +12 -0
- package/dist/chat/patch.js +149 -0
- package/dist/chat/useAgUiRun.d.ts +52 -0
- package/dist/chat/useAgUiRun.js +137 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +11 -1
- package/dist/primitives/Button.d.ts +1 -1
- package/dist/provider/messages.d.ts +83 -1
- package/dist/provider/messages.js +91 -0
- package/dist/theme/mappings/palette.d.ts +52 -0
- package/dist/theme/mappings/palette.js +208 -0
- package/dist/theme/mappings/render.d.ts +9 -0
- package/dist/theme/mappings/render.js +520 -0
- package/dist/theme/mappings/targets.d.ts +43 -0
- package/dist/theme/mappings/targets.js +222 -0
- package/dist/theme/tokens.d.ts +10 -9
- package/dist/theme/tokens.generated.d.ts +353 -0
- package/dist/theme/tokens.generated.js +357 -0
- package/dist/theme/tokens.js +20 -264
- package/dist/tokens/README.jupyterlab.md +125 -0
- package/dist/tokens/README.md +15 -0
- package/dist/tokens/README.native.md +29 -0
- package/dist/tokens/README.plain.md +84 -0
- package/dist/tokens/README.python.md +33 -0
- package/dist/tokens/README.vscode.md +114 -0
- package/dist/tokens/jupyterlab.css +1710 -0
- package/dist/tokens/plain.css +930 -0
- package/dist/tokens/tokens.native.d.ts +85 -0
- package/dist/tokens/tokens.native.js +773 -0
- package/dist/tokens/tokens.py +1043 -0
- package/dist/tokens/vscode.css +1138 -0
- package/dist/workflow/canvas/WorkflowCanvas.js +41 -1
- package/dist/workflow/definition.d.ts +20 -0
- package/dist/workflow/definition.js +44 -0
- package/dist/workflow/editor/WorkflowInspector.d.ts +21 -3
- package/dist/workflow/editor/WorkflowInspector.js +13 -5
- package/dist/workflow/editor/WorkflowWorkspace.d.ts +28 -3
- package/dist/workflow/editor/WorkflowWorkspace.js +75 -16
- package/package.json +35 -2
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
import { block } from "../css.js";
|
|
2
|
+
import { ACCENTS, GROUNDS } from "../tokens.js";
|
|
3
|
+
import { ACCENT_KEYS, COLOR_TOKENS, DEFAULT_ACCENT, DEFAULT_GROUND, FONTS, GROUND_KEYS, allPalettes, flatten, fontFamily, parseColor, splitByAccent, staticNumbers, } from "./palette.js";
|
|
4
|
+
import { JUPYTERLAB, JUPYTERLAB_STATIC, PLAIN, PLAIN_STATIC, VSCODE, VSCODE_STATIC, } from "./targets.js";
|
|
5
|
+
const GENERATED = "Generated by `npm run theme` from the design tokens (tokens/*.json). Do not edit by hand.";
|
|
6
|
+
/**
|
|
7
|
+
* A CSS target: `:root` paints the default ground and accent, each
|
|
8
|
+
* `[data-ground]` block paints its ground with the default accent, and each
|
|
9
|
+
* `[data-ground][data-accent]` block repaints what moves with the accent.
|
|
10
|
+
* The ground carries the mode, so no `data-mode` is needed; appearance.js
|
|
11
|
+
* from the CSS layer stamps the same attributes.
|
|
12
|
+
*/
|
|
13
|
+
function renderCss(options) {
|
|
14
|
+
const palettes = allPalettes();
|
|
15
|
+
const values = (ground, accent) => Object.fromEntries(options.rows.map((r) => [r.variable, palettes[ground][accent][r.token]]));
|
|
16
|
+
const split = splitByAccent(values);
|
|
17
|
+
const scheme = (ground) => options.colorScheme ? [["color-scheme", GROUNDS[ground].mode]] : [];
|
|
18
|
+
const groundDecls = (ground) => [
|
|
19
|
+
...scheme(ground),
|
|
20
|
+
...Object.entries({
|
|
21
|
+
...split.ground[ground],
|
|
22
|
+
...split.accent[ground][DEFAULT_ACCENT],
|
|
23
|
+
}).sort((a, b) => order(a[0]) - order(b[0])),
|
|
24
|
+
];
|
|
25
|
+
const order = (variable) => options.rows.findIndex((r) => r.variable === variable);
|
|
26
|
+
const selector = (s) => [s, ...(options.also?.(s) ?? [])].join(",\n");
|
|
27
|
+
const blocks = [
|
|
28
|
+
`/*\n * ${options.title}\n * ${GENERATED}\n *\n${options.intro
|
|
29
|
+
.split("\n")
|
|
30
|
+
.map((line) => (line ? ` * ${line}` : " *"))
|
|
31
|
+
.join("\n")}\n */`,
|
|
32
|
+
block(":root", [
|
|
33
|
+
...options.statics.map((s) => [s.variable, s.value]),
|
|
34
|
+
...groundDecls(DEFAULT_GROUND),
|
|
35
|
+
], `Default: ${GROUNDS[DEFAULT_GROUND].label} ground, ${ACCENTS[DEFAULT_ACCENT].label} accent`),
|
|
36
|
+
...(options.extra?.(groundDecls) ?? []),
|
|
37
|
+
];
|
|
38
|
+
for (const ground of GROUND_KEYS) {
|
|
39
|
+
const { label, mode } = GROUNDS[ground];
|
|
40
|
+
const base = `[data-ground="${ground}"]`;
|
|
41
|
+
blocks.push(block(selector(base), groundDecls(ground), `Ground: ${label} (${mode})`));
|
|
42
|
+
for (const accent of ACCENT_KEYS) {
|
|
43
|
+
const decls = Object.entries(split.accent[ground][accent]);
|
|
44
|
+
if (decls.length === 0)
|
|
45
|
+
continue;
|
|
46
|
+
blocks.push(block(selector(`${base}[data-accent="${accent}"]`), decls.sort((a, b) => order(a[0]) - order(b[0])), `Accent: ${ACCENTS[accent].label} on ${label}`));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return `${blocks.join("\n\n")}\n`;
|
|
50
|
+
}
|
|
51
|
+
export function renderJupyterLabCss() {
|
|
52
|
+
return renderCss({
|
|
53
|
+
title: "@calliopelabs/ui tokens for JupyterLab (--jp-*).",
|
|
54
|
+
intro: `Load after JupyterLab's own theme variables (a theme extension's
|
|
55
|
+
variables.css, or a stylesheet the server adds to the lab page). With no
|
|
56
|
+
data-ground, the ground follows JupyterLab's own theme: Black under a dark
|
|
57
|
+
theme, Paper under a light one (data-jp-theme-light on <body>). Set
|
|
58
|
+
data-ground (and data-accent) on <html> to choose one. See README.jupyterlab.md.`,
|
|
59
|
+
rows: JUPYTERLAB,
|
|
60
|
+
statics: JUPYTERLAB_STATIC,
|
|
61
|
+
also: (s) => [`${s} [data-jp-theme-light]`],
|
|
62
|
+
extra: (decls) => [
|
|
63
|
+
block('[data-jp-theme-light="true"]', decls("paper"), "No data-ground under a light JupyterLab theme: Paper"),
|
|
64
|
+
],
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
export function renderVsCodeCss() {
|
|
68
|
+
return renderCss({
|
|
69
|
+
title: "@calliopelabs/ui tokens as VS Code webview fallbacks (--vscode-*).",
|
|
70
|
+
intro: `Inside a VS Code webview the host sets every --vscode-* variable
|
|
71
|
+
inline on <html>, so the editor's theme wins and these never show. Outside
|
|
72
|
+
VS Code (a browser preview, Storybook, a test) they stand in, so a webview
|
|
73
|
+
written against --vscode-* looks like Calliope AI. data-ground and
|
|
74
|
+
data-accent on <html> choose the ground and accent. See README.vscode.md.`,
|
|
75
|
+
rows: VSCODE,
|
|
76
|
+
statics: VSCODE_STATIC,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
export function renderPlainCss() {
|
|
80
|
+
return renderCss({
|
|
81
|
+
title: "@calliopelabs/ui tokens as plain CSS variables.",
|
|
82
|
+
intro: `Every colour token under its own name as a concrete value (no var()
|
|
83
|
+
chains, no color-mix, no product machinery), plus corners, motion and type.
|
|
84
|
+
For static sites and apps that only read variables. data-ground and
|
|
85
|
+
data-accent on <html> (or any element) choose the ground and accent.
|
|
86
|
+
See README.plain.md.`,
|
|
87
|
+
rows: PLAIN,
|
|
88
|
+
statics: PLAIN_STATIC,
|
|
89
|
+
colorScheme: true,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/* Python ------------------------------------------------------------------ */
|
|
93
|
+
const py = (value) => {
|
|
94
|
+
if (typeof value === "string")
|
|
95
|
+
return JSON.stringify(value);
|
|
96
|
+
if (typeof value === "number")
|
|
97
|
+
return String(value);
|
|
98
|
+
if (Array.isArray(value))
|
|
99
|
+
return `(${value.map(py).join(", ")}${value.length === 1 ? "," : ""})`;
|
|
100
|
+
throw new Error(`no Python literal for ${String(value)}`);
|
|
101
|
+
};
|
|
102
|
+
function pyDict(entries, indent) {
|
|
103
|
+
const pad = " ".repeat(indent);
|
|
104
|
+
if (entries.length === 0)
|
|
105
|
+
return "{}";
|
|
106
|
+
return `{\n${entries.map(([k, v]) => `${pad} ${JSON.stringify(k)}: ${v},`).join("\n")}\n${pad}}`;
|
|
107
|
+
}
|
|
108
|
+
export function renderPython() {
|
|
109
|
+
const palettes = allPalettes();
|
|
110
|
+
const rgb = (g, a) => Object.fromEntries(COLOR_TOKENS.map((t) => {
|
|
111
|
+
const c = flatten(palettes[g][a], t);
|
|
112
|
+
return [t, py([c.r, c.g, c.b])];
|
|
113
|
+
}));
|
|
114
|
+
const alpha = (g, a) => Object.fromEntries(COLOR_TOKENS.flatMap((t) => {
|
|
115
|
+
const c = parseColor(palettes[g][a][t]);
|
|
116
|
+
return c.a === 1 ? [] : [[t, py([c.r, c.g, c.b, c.a])]];
|
|
117
|
+
}));
|
|
118
|
+
const nested = (split) => [
|
|
119
|
+
pyDict(GROUND_KEYS.map((g) => [g, pyDict(Object.entries(split.ground[g]), 4)]), 0),
|
|
120
|
+
pyDict(GROUND_KEYS.map((g) => [
|
|
121
|
+
g,
|
|
122
|
+
pyDict(ACCENT_KEYS.map((a) => [
|
|
123
|
+
a,
|
|
124
|
+
pyDict(Object.entries(split.accent[g][a]), 8),
|
|
125
|
+
]), 4),
|
|
126
|
+
]), 0),
|
|
127
|
+
];
|
|
128
|
+
const [groundRgb, accentRgb] = nested(splitByAccent(rgb));
|
|
129
|
+
const [groundRgba, accentRgba] = nested(splitByAccent(alpha));
|
|
130
|
+
const { radius, motion } = staticNumbers();
|
|
131
|
+
return `"""@calliopelabs/ui tokens for Python, as RGB triplets.
|
|
132
|
+
|
|
133
|
+
${GENERATED}
|
|
134
|
+
|
|
135
|
+
Every colour token of every ground and family accent, as the preset paints it
|
|
136
|
+
with no product brand. Translucent tokens (hairlines, tints) are given two
|
|
137
|
+
ways: rgb() lays them on their surface (sidebar-* on the sidebar, the rest on
|
|
138
|
+
the background) for targets with no alpha, and rgba() keeps the alpha.
|
|
139
|
+
|
|
140
|
+
from calliope_tokens import rgb, triplet, palette
|
|
141
|
+
|
|
142
|
+
rgb("background", "charcoal") # (14, 15, 16)
|
|
143
|
+
triplet("primary", "paper", "copper") # "156 87 48", for rgb(var(--x) / 0.2)
|
|
144
|
+
palette("emerald") # {"background": (5, 11, 8), ...}
|
|
145
|
+
|
|
146
|
+
See README.python.md.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
from __future__ import annotations
|
|
150
|
+
|
|
151
|
+
GROUNDS: dict[str, str] = ${pyDict(GROUND_KEYS.map((g) => [g, py(GROUNDS[g].mode)]), 0)}
|
|
152
|
+
"""Every ground and its mode ("dark" or "light")."""
|
|
153
|
+
|
|
154
|
+
ACCENTS: tuple[str, ...] = ${py([...ACCENT_KEYS])}
|
|
155
|
+
"""The family accents."""
|
|
156
|
+
|
|
157
|
+
TOKENS: tuple[str, ...] = (
|
|
158
|
+
${COLOR_TOKENS.map((t) => ` ${JSON.stringify(t)},`).join("\n")}
|
|
159
|
+
)
|
|
160
|
+
"""Every colour token, by its CSS name without the leading dashes."""
|
|
161
|
+
|
|
162
|
+
DEFAULT_GROUND = ${py(DEFAULT_GROUND)}
|
|
163
|
+
DEFAULT_ACCENT = ${py(DEFAULT_ACCENT)}
|
|
164
|
+
|
|
165
|
+
RADIUS_PX = ${radius}
|
|
166
|
+
MOTION_MS: dict[str, int] = ${pyDict(Object.entries(motion).map(([k, v]) => [k, String(v)]), 0)}
|
|
167
|
+
FONTS: dict[str, str] = ${pyDict(Object.entries(FONTS).map(([k, v]) => [k, py(v)]), 0)}
|
|
168
|
+
"""CSS font stacks, as the preset sets --font-sans, --font-mono and --font-head."""
|
|
169
|
+
|
|
170
|
+
Triplet = tuple[int, int, int]
|
|
171
|
+
Rgba = tuple[int, int, int, float]
|
|
172
|
+
|
|
173
|
+
# Per ground: what every accent agrees on. Per ground and accent: the rest.
|
|
174
|
+
_GROUND_RGB: dict[str, dict[str, Triplet]] = ${groundRgb}
|
|
175
|
+
|
|
176
|
+
_ACCENT_RGB: dict[str, dict[str, dict[str, Triplet]]] = ${accentRgb}
|
|
177
|
+
|
|
178
|
+
# Translucent tokens only, with their alpha.
|
|
179
|
+
_GROUND_RGBA: dict[str, dict[str, Rgba]] = ${groundRgba}
|
|
180
|
+
|
|
181
|
+
_ACCENT_RGBA: dict[str, dict[str, dict[str, Rgba]]] = ${accentRgba}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _check(ground: str, accent: str) -> None:
|
|
185
|
+
if ground not in GROUNDS:
|
|
186
|
+
raise KeyError(f"unknown ground {ground!r}; one of {', '.join(GROUNDS)}")
|
|
187
|
+
if accent not in ACCENTS:
|
|
188
|
+
raise KeyError(f"unknown accent {accent!r}; one of {', '.join(ACCENTS)}")
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def palette(ground: str = DEFAULT_GROUND, accent: str = DEFAULT_ACCENT) -> dict[str, Triplet]:
|
|
192
|
+
"""Every token of a ground and accent, as opaque RGB triplets."""
|
|
193
|
+
_check(ground, accent)
|
|
194
|
+
return {**_GROUND_RGB[ground], **_ACCENT_RGB[ground][accent]}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def rgb(token: str, ground: str = DEFAULT_GROUND, accent: str = DEFAULT_ACCENT) -> Triplet:
|
|
198
|
+
"""One token as an opaque (r, g, b), translucent ones laid on their surface."""
|
|
199
|
+
values = palette(ground, accent)
|
|
200
|
+
if token not in values:
|
|
201
|
+
raise KeyError(f"unknown token {token!r}")
|
|
202
|
+
return values[token]
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def rgba(token: str, ground: str = DEFAULT_GROUND, accent: str = DEFAULT_ACCENT) -> Rgba:
|
|
206
|
+
"""One token as (r, g, b, alpha), keeping a translucent token's alpha."""
|
|
207
|
+
_check(ground, accent)
|
|
208
|
+
translucent = {**_GROUND_RGBA[ground], **_ACCENT_RGBA[ground][accent]}
|
|
209
|
+
if token in translucent:
|
|
210
|
+
return translucent[token]
|
|
211
|
+
r, g, b = rgb(token, ground, accent)
|
|
212
|
+
return (r, g, b, 1.0)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def triplet(token: str, ground: str = DEFAULT_GROUND, accent: str = DEFAULT_ACCENT) -> str:
|
|
216
|
+
"""One token as "R G B", the channel form CSS reads in rgb(var(--x) / alpha)."""
|
|
217
|
+
return " ".join(str(c) for c in rgb(token, ground, accent))
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def hex(token: str, ground: str = DEFAULT_GROUND, accent: str = DEFAULT_ACCENT) -> str: # noqa: A001
|
|
221
|
+
"""One token as opaque "#rrggbb"."""
|
|
222
|
+
return "#" + "".join(f"{c:02x}" for c in rgb(token, ground, accent))
|
|
223
|
+
`;
|
|
224
|
+
}
|
|
225
|
+
/* React Native ------------------------------------------------------------ */
|
|
226
|
+
const camel = (token) => token.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
227
|
+
function jsObject(entries, indent) {
|
|
228
|
+
const pad = " ".repeat(indent);
|
|
229
|
+
if (entries.length === 0)
|
|
230
|
+
return "{}";
|
|
231
|
+
return `{\n${entries.map(([k, v]) => `${pad} ${/^[a-zA-Z_$][\w$]*$/.test(k) ? k : JSON.stringify(k)}: ${v},`).join("\n")}\n${pad}}`;
|
|
232
|
+
}
|
|
233
|
+
export function renderNativeJs() {
|
|
234
|
+
const palettes = allPalettes();
|
|
235
|
+
const split = splitByAccent((g, a) => Object.fromEntries(COLOR_TOKENS.map((t) => [camel(t), palettes[g][a][t]])));
|
|
236
|
+
const { radius, motion } = staticNumbers();
|
|
237
|
+
const entries = (o) => Object.entries(o).map(([k, v]) => [k, JSON.stringify(v)]);
|
|
238
|
+
return `/*
|
|
239
|
+
* @calliopelabs/ui tokens for React Native.
|
|
240
|
+
* ${GENERATED}
|
|
241
|
+
*
|
|
242
|
+
* Colours are strings React Native's StyleSheet takes as they are (#rrggbb
|
|
243
|
+
* or rgba()), keyed in camelCase. palette(ground, accent) gives one ground
|
|
244
|
+
* and accent; each ground is also exported by name with the default accent.
|
|
245
|
+
* See README.native.md.
|
|
246
|
+
*/
|
|
247
|
+
|
|
248
|
+
export const GROUNDS = Object.freeze(${jsObject(GROUND_KEYS.map((g) => [
|
|
249
|
+
g,
|
|
250
|
+
`Object.freeze({ label: ${JSON.stringify(GROUNDS[g].label)}, mode: ${JSON.stringify(GROUNDS[g].mode)} })`,
|
|
251
|
+
]), 0)});
|
|
252
|
+
|
|
253
|
+
export const ACCENTS = Object.freeze(${JSON.stringify([...ACCENT_KEYS])});
|
|
254
|
+
|
|
255
|
+
export const DEFAULT_GROUND = ${JSON.stringify(DEFAULT_GROUND)};
|
|
256
|
+
export const DEFAULT_ACCENT = ${JSON.stringify(DEFAULT_ACCENT)};
|
|
257
|
+
|
|
258
|
+
export const radius = ${radius};
|
|
259
|
+
export const motion = Object.freeze(${jsObject(Object.entries(motion).map(([k, v]) => [k, String(v)]), 0)});
|
|
260
|
+
export const fonts = Object.freeze(${jsObject(Object.entries(FONTS).map(([k, v]) => [
|
|
261
|
+
k,
|
|
262
|
+
JSON.stringify(fontFamily(v)),
|
|
263
|
+
]), 0)});
|
|
264
|
+
|
|
265
|
+
const byGround = ${jsObject(GROUND_KEYS.map((g) => [g, jsObject(entries(split.ground[g]), 2)]), 0)};
|
|
266
|
+
|
|
267
|
+
const byAccent = ${jsObject(GROUND_KEYS.map((g) => [
|
|
268
|
+
g,
|
|
269
|
+
jsObject(ACCENT_KEYS.map((a) => [a, jsObject(entries(split.accent[g][a]), 4)]), 2),
|
|
270
|
+
]), 0)};
|
|
271
|
+
|
|
272
|
+
export function palette(ground = DEFAULT_GROUND, accent = DEFAULT_ACCENT) {
|
|
273
|
+
if (!Object.prototype.hasOwnProperty.call(byGround, ground)) {
|
|
274
|
+
throw new Error("unknown ground " + ground);
|
|
275
|
+
}
|
|
276
|
+
if (ACCENTS.indexOf(accent) < 0) throw new Error("unknown accent " + accent);
|
|
277
|
+
return Object.freeze(Object.assign({}, byGround[ground], byAccent[ground][accent]));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
${GROUND_KEYS.map((g) => `export const ${g} = palette(${JSON.stringify(g)});`).join("\n")}
|
|
281
|
+
`;
|
|
282
|
+
}
|
|
283
|
+
export function renderNativeDts() {
|
|
284
|
+
return `/*
|
|
285
|
+
* @calliopelabs/ui tokens for React Native: types.
|
|
286
|
+
* ${GENERATED}
|
|
287
|
+
*/
|
|
288
|
+
|
|
289
|
+
export type Ground = ${GROUND_KEYS.map((g) => JSON.stringify(g)).join(" | ")};
|
|
290
|
+
export type Accent = ${ACCENT_KEYS.map((a) => JSON.stringify(a)).join(" | ")};
|
|
291
|
+
export type Mode = "dark" | "light";
|
|
292
|
+
|
|
293
|
+
/** Every colour token of one ground and accent, as #rrggbb or rgba(). */
|
|
294
|
+
export interface Palette {
|
|
295
|
+
${COLOR_TOKENS.map((t) => ` readonly ${camel(t)}: string;`).join("\n")}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export declare const GROUNDS: Readonly<Record<Ground, { readonly label: string; readonly mode: Mode }>>;
|
|
299
|
+
export declare const ACCENTS: readonly Accent[];
|
|
300
|
+
export declare const DEFAULT_GROUND: ${JSON.stringify(DEFAULT_GROUND)};
|
|
301
|
+
export declare const DEFAULT_ACCENT: ${JSON.stringify(DEFAULT_ACCENT)};
|
|
302
|
+
|
|
303
|
+
/** The corners setting, in points. */
|
|
304
|
+
export declare const radius: number;
|
|
305
|
+
/** Motion durations, in milliseconds. */
|
|
306
|
+
export declare const motion: Readonly<{ fast: number; base: number; slow: number }>;
|
|
307
|
+
/** Font family names; the app loads the fonts. */
|
|
308
|
+
export declare const fonts: Readonly<{ sans: string; mono: string; head: string }>;
|
|
309
|
+
|
|
310
|
+
export declare function palette(ground?: Ground, accent?: Accent): Palette;
|
|
311
|
+
|
|
312
|
+
${GROUND_KEYS.map((g) => `export declare const ${g}: Palette;`).join("\n")}
|
|
313
|
+
`;
|
|
314
|
+
}
|
|
315
|
+
/* READMEs ----------------------------------------------------------------- */
|
|
316
|
+
function table(rows, statics) {
|
|
317
|
+
const sample = allPalettes()[DEFAULT_GROUND][DEFAULT_ACCENT];
|
|
318
|
+
const light = allPalettes().paper[DEFAULT_ACCENT];
|
|
319
|
+
const lines = [
|
|
320
|
+
"| Variable | Library token | Black, green | Paper, green | Note |",
|
|
321
|
+
"| --- | --- | --- | --- | --- |",
|
|
322
|
+
...rows.map((r) => `| \`${r.variable}\` | \`--${r.token}\` | \`${sample[r.token]}\` | \`${light[r.token]}\` | ${r.note ?? ""} |`),
|
|
323
|
+
...statics.map((s) => `| \`${s.variable}\` | \`${s.from}\` | \`${s.value}\` | same | Every ground |`),
|
|
324
|
+
];
|
|
325
|
+
return lines.join("\n");
|
|
326
|
+
}
|
|
327
|
+
const GROUND_LIST = GROUND_KEYS.map((g) => `\`${g}\` (${GROUNDS[g].mode})`).join(", ");
|
|
328
|
+
const ACCENT_LIST = ACCENT_KEYS.map((a) => `\`${a}\``).join(", ");
|
|
329
|
+
const SELECTING = `Grounds: ${GROUND_LIST}. Accents: ${ACCENT_LIST}. The ground carries the mode, so there is no \`data-mode\`; \`:root\` paints \`${DEFAULT_GROUND}\` with \`${DEFAULT_ACCENT}\`. The CSS layer's \`appearance.js\` stamps the same \`data-ground\` and \`data-accent\` attributes from the person's stored choice.`;
|
|
330
|
+
const FOOTER = `Generated by \`npm run theme\` from the design tokens (\`tokens/*.json\`), like the preset; a test holds every value to the preset's on every ground and accent.`;
|
|
331
|
+
export function renderReadmes() {
|
|
332
|
+
return {
|
|
333
|
+
"README.md": `# @calliopelabs/ui tokens
|
|
334
|
+
|
|
335
|
+
The family's tokens for stacks that cannot use the Tailwind preset or the CSS layer, generated from the same token table. Each value is what the preset paints on that ground with that accent and no product brand.
|
|
336
|
+
|
|
337
|
+
| File | For | Guide |
|
|
338
|
+
| --- | --- | --- |
|
|
339
|
+
| \`jupyterlab.css\` | JupyterLab and Jupyter Notebook 7 (\`--jp-*\`) | [README.jupyterlab.md](README.jupyterlab.md) |
|
|
340
|
+
| \`vscode.css\` | VS Code webviews outside VS Code (\`--vscode-*\` fallbacks) | [README.vscode.md](README.vscode.md) |
|
|
341
|
+
| \`tokens.py\` | Python (the installer): RGB triplets and accessors | [README.python.md](README.python.md) |
|
|
342
|
+
| \`tokens.native.js\`, \`tokens.native.d.ts\` | React Native | [README.native.md](README.native.md) |
|
|
343
|
+
| \`plain.css\` | Sites and apps that read plain variables (Cykick) | [README.plain.md](README.plain.md) |
|
|
344
|
+
|
|
345
|
+
Server-rendered apps that want the components as well take the CSS layer (\`@calliopelabs/ui/css/*\`) instead.
|
|
346
|
+
|
|
347
|
+
${FOOTER}
|
|
348
|
+
`,
|
|
349
|
+
"README.jupyterlab.md": `# JupyterLab: \`jupyterlab.css\`
|
|
350
|
+
|
|
351
|
+
Maps the library tokens onto JupyterLab's theme variables, for JupyterLab 4 and Notebook 7 (JunoHub's lab).
|
|
352
|
+
|
|
353
|
+
## Use
|
|
354
|
+
|
|
355
|
+
In a theme extension, import it after the stock variables in the theme's \`style/variables.css\`:
|
|
356
|
+
|
|
357
|
+
\`\`\`css
|
|
358
|
+
@import "@jupyterlab/theme-dark-extension/style/variables.css";
|
|
359
|
+
@import "@calliopelabs/ui/tokens/jupyterlab.css";
|
|
360
|
+
\`\`\`
|
|
361
|
+
|
|
362
|
+
Or, without an extension, serve the file and add it to the lab page after JupyterLab's styles (a \`custom.css\`, or the page template).
|
|
363
|
+
|
|
364
|
+
With no \`data-ground\`, the ground follows JupyterLab's own theme switch: Black under a dark theme, Paper under a light one (JupyterLab sets \`data-jp-theme-light\` on \`<body>\`). To choose, set \`data-ground\` and \`data-accent\` on \`<html>\`; pick a ground whose mode matches the JupyterLab theme you load:
|
|
365
|
+
|
|
366
|
+
\`\`\`js
|
|
367
|
+
document.documentElement.dataset.ground = "charcoal";
|
|
368
|
+
document.documentElement.dataset.accent = "copper";
|
|
369
|
+
\`\`\`
|
|
370
|
+
|
|
371
|
+
${SELECTING}
|
|
372
|
+
|
|
373
|
+
Left as JupyterLab ships them: syntax colours (\`--jp-mirror-editor-*\`), the search highlight, collaborator and completer colours, scrollbars, font sizes, and the red "warn" dialog button set, whose white label the family's dark danger red cannot carry.
|
|
374
|
+
|
|
375
|
+
## Mapping
|
|
376
|
+
|
|
377
|
+
Layout colours run from the page (0) outwards; font colours from strongest (0) to faintest (3); state colours from text (0) and marks (1) to the tinted hairline (2) and the fill (3).
|
|
378
|
+
|
|
379
|
+
${table(JUPYTERLAB, JUPYTERLAB_STATIC)}
|
|
380
|
+
|
|
381
|
+
${FOOTER}
|
|
382
|
+
`,
|
|
383
|
+
"README.vscode.md": `# VS Code webviews: \`vscode.css\`
|
|
384
|
+
|
|
385
|
+
Calliope AI values for the \`--vscode-*\` variables a webview reads ([theme colour ids](https://code.visualstudio.com/api/references/theme-color), dots as dashes).
|
|
386
|
+
|
|
387
|
+
## Use
|
|
388
|
+
|
|
389
|
+
Link it in the webview's HTML before the webview's own styles:
|
|
390
|
+
|
|
391
|
+
\`\`\`html
|
|
392
|
+
<link rel="stylesheet" href="\${webview.asWebviewUri(vscodeCss)}" />
|
|
393
|
+
\`\`\`
|
|
394
|
+
|
|
395
|
+
Inside VS Code the host sets every \`--vscode-*\` variable inline on \`<html>\`, so the editor's theme always wins and these never show. Outside VS Code (a browser preview of the webview, Storybook, a test in jsdom) they stand in, so a webview written against \`--vscode-*\` renders in the family's colours.
|
|
396
|
+
|
|
397
|
+
To go the other way, and paint the library's own tokens from the editor's theme with these as the fallback, read them through:
|
|
398
|
+
|
|
399
|
+
\`\`\`css
|
|
400
|
+
:root {
|
|
401
|
+
--background: var(--vscode-editor-background);
|
|
402
|
+
--foreground: var(--vscode-foreground);
|
|
403
|
+
--primary: var(--vscode-button-background);
|
|
404
|
+
}
|
|
405
|
+
\`\`\`
|
|
406
|
+
|
|
407
|
+
${SELECTING}
|
|
408
|
+
|
|
409
|
+
VS Code's \`--vscode-font-size\` and \`--vscode-editor-font-size\` are left to the host. Left alone: colours the family has no hue for (\`charts.blue\`, \`charts.purple\`, \`charts.orange\`; info is green) and scrollbars.
|
|
410
|
+
|
|
411
|
+
## Mapping
|
|
412
|
+
|
|
413
|
+
${table(VSCODE, VSCODE_STATIC)}
|
|
414
|
+
|
|
415
|
+
${FOOTER}
|
|
416
|
+
`,
|
|
417
|
+
"README.python.md": `# Python: \`tokens.py\`
|
|
418
|
+
|
|
419
|
+
Every colour token of every ground and accent as RGB triplets, with a few accessors. Standard library only, Python 3.9 and up.
|
|
420
|
+
|
|
421
|
+
## Use
|
|
422
|
+
|
|
423
|
+
Copy (vendor) the file into the package under a module name of your choice, for example \`calliope_tokens.py\`:
|
|
424
|
+
|
|
425
|
+
\`\`\`sh
|
|
426
|
+
cp node_modules/@calliopelabs/ui/dist/tokens/tokens.py installer/wizard/calliope_tokens.py
|
|
427
|
+
\`\`\`
|
|
428
|
+
|
|
429
|
+
\`\`\`python
|
|
430
|
+
from calliope_tokens import GROUNDS, palette, rgb, rgba, triplet, hex
|
|
431
|
+
|
|
432
|
+
rgb("background", "charcoal") # (14, 15, 16)
|
|
433
|
+
triplet("primary", "paper", "copper") # "156 87 48"
|
|
434
|
+
rgba("border", "black") # (255, 255, 255, 0.1)
|
|
435
|
+
hex("border", "black") # "#1a1a1a", laid on the background
|
|
436
|
+
palette("emerald", "ice") # {"background": (5, 11, 8), ...}
|
|
437
|
+
\`\`\`
|
|
438
|
+
|
|
439
|
+
\`triplet\` is the \`R G B\` channel form CSS reads in \`rgb(var(--x-rgb) / 0.18)\`, so a stylesheet built in Python can take any alpha of any token:
|
|
440
|
+
|
|
441
|
+
\`\`\`python
|
|
442
|
+
css = "\\n".join(f" --{name}-rgb: {' '.join(map(str, value))};" for name, value in palette(ground).items())
|
|
443
|
+
\`\`\`
|
|
444
|
+
|
|
445
|
+
Tokens are named as in CSS, without the dashes: \`background\`, \`card\`, \`primary\`, \`primary-foreground\`, \`muted-foreground\`, \`border\`, \`ring\`, \`success\`, \`success-fg\`, \`danger-bg\` and so on (\`TOKENS\` lists them all). Translucent tokens (hairlines, tints) come two ways: \`rgb\`, \`triplet\`, \`hex\` and \`palette\` lay them on their surface (\`sidebar-*\` on the sidebar, the rest on the background), for targets with no alpha; \`rgba\` keeps the alpha. \`RADIUS_PX\`, \`MOTION_MS\` and \`FONTS\` carry the rest.
|
|
446
|
+
|
|
447
|
+
${SELECTING.replace(/ The CSS layer.*$/, "")}
|
|
448
|
+
|
|
449
|
+
${FOOTER}
|
|
450
|
+
`,
|
|
451
|
+
"README.native.md": `# React Native: \`tokens.native.js\`
|
|
452
|
+
|
|
453
|
+
Every colour token of every ground and accent as objects React Native's \`StyleSheet\` takes as they are (\`#rrggbb\` or \`rgba()\`), keyed in camelCase, with types.
|
|
454
|
+
|
|
455
|
+
## Use
|
|
456
|
+
|
|
457
|
+
\`\`\`ts
|
|
458
|
+
import { palette, charcoal, radius, fonts, GROUNDS } from "@calliopelabs/ui/tokens/native";
|
|
459
|
+
|
|
460
|
+
const c = palette("paper", "copper");
|
|
461
|
+
const styles = StyleSheet.create({
|
|
462
|
+
screen: { flex: 1, backgroundColor: c.background },
|
|
463
|
+
title: { color: c.foreground, fontFamily: fonts.head },
|
|
464
|
+
button: { backgroundColor: c.primary, borderRadius: radius },
|
|
465
|
+
buttonLabel: { color: c.primaryForeground },
|
|
466
|
+
hint: { color: c.mutedForeground },
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// charcoal is palette("charcoal") with the default accent.
|
|
470
|
+
const statusBar = GROUNDS.charcoal.mode === "dark" ? "light-content" : "dark-content";
|
|
471
|
+
\`\`\`
|
|
472
|
+
|
|
473
|
+
Each ground is exported by name (\`${GROUND_KEYS.join("`, `")}\`) with the default accent; \`palette(ground, accent)\` gives any combination and throws on an unknown one. Keys follow the CSS names in camelCase: \`background\`, \`card\`, \`primary\`, \`primaryForeground\`, \`mutedForeground\`, \`border\`, \`ring\`, \`chart1\`, \`sidebarAccent\`, \`success\`, \`successFg\`, \`dangerBg\` and so on. \`radius\` is in points, \`motion\` in milliseconds, and \`fonts\` holds family names; the app loads the fonts (IBM Plex Sans, IBM Plex Mono, DM Sans).
|
|
474
|
+
|
|
475
|
+
The package is ESM; Metro resolves the \`@calliopelabs/ui/tokens/native\` subpath through \`exports\` (on by default since React Native 0.79). Or copy the two files into the app.
|
|
476
|
+
|
|
477
|
+
${SELECTING.replace(/ The ground carries.*$/, "")}
|
|
478
|
+
|
|
479
|
+
${FOOTER}
|
|
480
|
+
`,
|
|
481
|
+
"README.plain.md": `# Plain CSS: \`plain.css\`
|
|
482
|
+
|
|
483
|
+
Every colour token under its own name (\`--background\`, \`--primary\`, \`--success-fg\`, ...) as a concrete value, plus \`--radius\`, \`--motion-*\` and \`--font-sans\`, \`--font-mono\`, \`--font-head\`. No \`var()\` chains, no \`color-mix\`, no product or brand machinery, and \`color-scheme\` set per ground: for static sites and apps that only read variables (Cykick, the marketing sites).
|
|
484
|
+
|
|
485
|
+
## Use
|
|
486
|
+
|
|
487
|
+
\`\`\`html
|
|
488
|
+
<html data-ground="charcoal" data-accent="ice">
|
|
489
|
+
<link rel="stylesheet" href="/static/calliope/plain.css" />
|
|
490
|
+
\`\`\`
|
|
491
|
+
|
|
492
|
+
\`\`\`css
|
|
493
|
+
body { background: var(--background); color: var(--foreground); font-family: var(--font-sans); }
|
|
494
|
+
.button { background: var(--primary); color: var(--primary-foreground); border-radius: var(--radius); }
|
|
495
|
+
\`\`\`
|
|
496
|
+
|
|
497
|
+
Any element can carry the attributes, so a section can sit on another ground. ${SELECTING}
|
|
498
|
+
|
|
499
|
+
When to take the CSS layer (\`@calliopelabs/ui/css/tokens.css\`) instead: when the page needs product brands (the branded sidebar, a product's own accent), a custom accent colour, density or corners settings, or the class-based components. \`plain.css\` carries the family accents only.
|
|
500
|
+
|
|
501
|
+
## Variables
|
|
502
|
+
|
|
503
|
+
${table(PLAIN, PLAIN_STATIC)}
|
|
504
|
+
|
|
505
|
+
${FOOTER}
|
|
506
|
+
`,
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
/** Every generated file under src/tokens/, by file name. */
|
|
510
|
+
export function renderTokenFiles() {
|
|
511
|
+
return {
|
|
512
|
+
"jupyterlab.css": renderJupyterLabCss(),
|
|
513
|
+
"vscode.css": renderVsCodeCss(),
|
|
514
|
+
"plain.css": renderPlainCss(),
|
|
515
|
+
"tokens.py": renderPython(),
|
|
516
|
+
"tokens.native.js": renderNativeJs(),
|
|
517
|
+
"tokens.native.d.ts": renderNativeDts(),
|
|
518
|
+
...renderReadmes(),
|
|
519
|
+
};
|
|
520
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type ColorToken } from "./palette.js";
|
|
2
|
+
/**
|
|
3
|
+
* The mapping tables: which library token each target variable takes. One
|
|
4
|
+
* row per variable; the note says why when the match is not one to one.
|
|
5
|
+
* The READMEs print these tables, and the tests check every row's value
|
|
6
|
+
* against the preset on every ground and accent.
|
|
7
|
+
*/
|
|
8
|
+
export interface Row {
|
|
9
|
+
variable: string;
|
|
10
|
+
token: ColorToken;
|
|
11
|
+
note?: string;
|
|
12
|
+
}
|
|
13
|
+
/** A target variable set once, the same on every ground (type, corners). */
|
|
14
|
+
export interface StaticRow {
|
|
15
|
+
variable: string;
|
|
16
|
+
value: string;
|
|
17
|
+
from: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* JupyterLab theme variables (`@jupyterlab/theme-light-extension` and
|
|
21
|
+
* `theme-dark-extension`, `style/variables.css`). Layout colours run from
|
|
22
|
+
* the page (0) outwards; font colours from strongest (0) to faintest (3);
|
|
23
|
+
* brand and state colours from the strong end (0, 1) to the pale tint (3).
|
|
24
|
+
* Left alone: syntax colours (`--jp-mirror-editor-*`), the search
|
|
25
|
+
* highlight, collaborator and completer colours, and the red "warn" button
|
|
26
|
+
* set, whose white label the family's dark danger red cannot carry.
|
|
27
|
+
*/
|
|
28
|
+
export declare const JUPYTERLAB: readonly Row[];
|
|
29
|
+
export declare const JUPYTERLAB_STATIC: readonly StaticRow[];
|
|
30
|
+
/**
|
|
31
|
+
* VS Code theme colours as a webview sees them: `--vscode-` and the colour
|
|
32
|
+
* id with dots as dashes (https://code.visualstudio.com/api/references/theme-color).
|
|
33
|
+
* Inside VS Code the host sets these inline on <html>, so they win and the
|
|
34
|
+
* webview follows the editor's theme; outside it (a browser preview,
|
|
35
|
+
* Storybook, a test) these values stand in. Left alone: colours the family
|
|
36
|
+
* has no hue for (`charts.blue`, `charts.purple`, `charts.orange`) and
|
|
37
|
+
* scrollbars.
|
|
38
|
+
*/
|
|
39
|
+
export declare const VSCODE: readonly Row[];
|
|
40
|
+
export declare const VSCODE_STATIC: readonly StaticRow[];
|
|
41
|
+
/** Plain: every colour token under its own name, as concrete values. */
|
|
42
|
+
export declare const PLAIN: readonly Row[];
|
|
43
|
+
export declare const PLAIN_STATIC: readonly StaticRow[];
|