hd-garden 0.3.11 → 0.3.13
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/app/admin/page.tsx +11 -15
- package/app/admin/ui/page.tsx +1 -1
- package/app/components/AdminDashboard.tsx +24 -34
- package/app/components/AdminUiPanels.tsx +5 -5
- package/app/components/AdminUiShell.tsx +7 -7
- package/app/components/GardenAdminGuide.tsx +38 -45
- package/app/components/GardenContentGuide.tsx +22 -106
- package/app/components/GardenMediaGuide.tsx +24 -45
- package/app/components/PageIntro.tsx +2 -2
- package/app/components/UiStudio.tsx +2 -2
- package/app/garden/page.tsx +85 -223
- package/app/globals.css +930 -76
- package/app/lib/fallback-site-config.ts +1 -1
- package/app/ui-parity/page.tsx +9 -6
- package/package.json +9 -9
- package/public/images/garden/admin-studio.png +0 -0
- package/site.config.yaml +1 -1
- package/templates/site/site.config.yaml +1 -1
- package/ui/README.md +70 -0
- package/ui/core/catalog.mjs +14 -0
- package/ui/core/static.mjs +2 -0
- package/ui/core/validate.mjs +11 -0
- package/ui/host.mjs +155 -0
- package/ui/index.mjs +80 -0
- package/ui/parity-fixtures.mjs +76 -0
- package/ui/preview/dist/garden-ui.css +54 -31
- package/ui/preview/dist/garden-ui.esm.js +172 -131
- package/ui/preview/dist/garden-ui.iife.js +172 -131
- package/ui/prompt/rules.md +1 -0
- package/ui/react/CatalogPage.tsx +37 -13
- package/ui/react/components/chart.tsx +1 -1
- package/ui/react/components/sandbox.tsx +68 -0
- package/ui/react/index.tsx +5 -3
- package/ui/react/parity.tsx +25 -0
- package/ui/react/runtime.tsx +22 -6
- package/ui/react/ui.css +54 -31
package/ui/prompt/rules.md
CHANGED
|
@@ -46,6 +46,7 @@ batch 为 {batch}、序列 {thousands(seq)} 时,KV cache 要 **{bytes(mem)}**
|
|
|
46
46
|
- 组件标签用大写开头,属性用名字传:字符串用双引号,数字、布尔、表达式用 `{}`。
|
|
47
47
|
- 正文里可以写普通 Markdown 段落、加粗、列表;用 `{表达式}` 把会变的数字插进句子。字面的 `{` 写成 `\{`。
|
|
48
48
|
- 不能写:标题、HTML 标签、图片语法、代码围栏、脚注、`import` / `export`。
|
|
49
|
+
- 目录表达不了的画布(可转的示意、逐步出现的图钉、一段自包含动画)用 `<Sandbox>`。`html` / `css` / `js` 必须是字面量,不能加载远程脚本、图片或样式。脚本里可以用 `GardenUI.openUrl`(只打开 `https`)、`GardenUI.copy`,以及 `GardenUI.issuePrompt`。`issuePrompt` 只有宿主实现了继续对话时才可用;博客阅读页没有这个能力。
|
|
49
50
|
|
|
50
51
|
## 二、状态和表达式
|
|
51
52
|
|
package/ui/react/CatalogPage.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { marked } from "marked";
|
|
4
|
-
import {
|
|
4
|
+
import { useMemo, useState, useSyncExternalStore } from "react";
|
|
5
5
|
import { COMPONENTS, GROUPS } from "../core/catalog.mjs";
|
|
6
6
|
import { compileUiBlock } from "../core/compile.mjs";
|
|
7
7
|
import { DEMOS, demoFence } from "../core/demos.mjs";
|
|
@@ -68,11 +68,45 @@ function PropTable({ spec }: { spec: Spec }) {
|
|
|
68
68
|
|
|
69
69
|
const CATEGORIES = ["配方", ...GROUPS];
|
|
70
70
|
|
|
71
|
+
const hashListeners = new Set<() => void>();
|
|
72
|
+
|
|
73
|
+
function subscribeCatalogHash(onStoreChange: () => void) {
|
|
74
|
+
hashListeners.add(onStoreChange);
|
|
75
|
+
window.addEventListener("hashchange", onStoreChange);
|
|
76
|
+
return () => {
|
|
77
|
+
hashListeners.delete(onStoreChange);
|
|
78
|
+
window.removeEventListener("hashchange", onStoreChange);
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function categoryFromHash(hash: string): string {
|
|
83
|
+
const id = hash.replace(/^#/, "");
|
|
84
|
+
if (id.startsWith("component-")) {
|
|
85
|
+
const spec = COMPONENTS[id.slice("component-".length) as keyof typeof COMPONENTS] as Spec | undefined;
|
|
86
|
+
if (spec?.group) return spec.group;
|
|
87
|
+
} else if (id === "配方" || id.startsWith("recipe-")) return "配方";
|
|
88
|
+
else {
|
|
89
|
+
try {
|
|
90
|
+
const decoded = decodeURIComponent(id);
|
|
91
|
+
if ((CATEGORIES as string[]).includes(decoded)) return decoded;
|
|
92
|
+
} catch {
|
|
93
|
+
// A malformed hash stays on the default category.
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return "展示";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function replaceCatalogHash(name: string) {
|
|
100
|
+
window.history.replaceState(null, "", `#${encodeURIComponent(name)}`);
|
|
101
|
+
for (const listener of hashListeners) listener();
|
|
102
|
+
}
|
|
103
|
+
|
|
71
104
|
/** Category rail on the left, a grid of name + preview cards on the right. */
|
|
72
105
|
export function CatalogPage({ recipes = [], onStart }: { recipes?: RecipeSource[]; onStart?: (fence: string) => void }) {
|
|
73
106
|
const parsedRecipes = useMemo(() => recipes.map(parseRecipe).filter((recipe) => recipe.fence), [recipes]);
|
|
74
107
|
const [query, setQuery] = useState("");
|
|
75
|
-
const
|
|
108
|
+
const hash = useSyncExternalStore(subscribeCatalogHash, () => window.location.hash, () => "");
|
|
109
|
+
const category = categoryFromHash(hash);
|
|
76
110
|
const [openId, setOpenId] = useState<string | null>(null);
|
|
77
111
|
const normalized = query.trim().toLowerCase();
|
|
78
112
|
|
|
@@ -108,19 +142,9 @@ export function CatalogPage({ recipes = [], onStart }: { recipes?: RecipeSource[
|
|
|
108
142
|
const activeCategory = categories.includes(category) ? category : categories[0];
|
|
109
143
|
const cards = activeCategory ? cardsFor(activeCategory) : [];
|
|
110
144
|
|
|
111
|
-
useEffect(() => {
|
|
112
|
-
const id = window.location.hash.replace(/^#/, "");
|
|
113
|
-
if (id.startsWith("component-")) {
|
|
114
|
-
const spec = COMPONENTS[id.slice("component-".length) as keyof typeof COMPONENTS] as Spec | undefined;
|
|
115
|
-
if (spec?.group) setCategory(spec.group);
|
|
116
|
-
} else if (id === "配方" || id.startsWith("recipe-")) setCategory("配方");
|
|
117
|
-
else if ((CATEGORIES as string[]).includes(decodeURIComponent(id))) setCategory(decodeURIComponent(id));
|
|
118
|
-
}, []);
|
|
119
|
-
|
|
120
145
|
const chooseCategory = (name: string) => {
|
|
121
|
-
setCategory(name);
|
|
122
146
|
setOpenId(null);
|
|
123
|
-
|
|
147
|
+
replaceCatalogHash(name);
|
|
124
148
|
};
|
|
125
149
|
|
|
126
150
|
return (
|
|
@@ -8,7 +8,7 @@ import { asNumber, asString, evalExpr, expandChildren, propValue, registerCompon
|
|
|
8
8
|
import type { Env, Lookup, UiNode } from "../types";
|
|
9
9
|
|
|
10
10
|
const SERIES = new Set(["Line", "Area", "Bar", "Point"]);
|
|
11
|
-
const DEFAULT_TONES = ["
|
|
11
|
+
const DEFAULT_TONES = ["accent", "muted", "new", "same"];
|
|
12
12
|
const MARGIN = { top: 14, right: 16, bottom: 30, left: 60 };
|
|
13
13
|
|
|
14
14
|
type Scale = { map: (value: number) => number; ticks: number[] };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { buildSrcdoc, isHttpsUrl, readSandboxMessage, sandboxRemote } from "../../host.mjs";
|
|
5
|
+
import { asString, propValue, registerComponents, useHost, useRuntime } from "../runtime";
|
|
6
|
+
import type { Env, UiNode } from "../types";
|
|
7
|
+
|
|
8
|
+
function Sandbox({ node, env }: { node: UiNode; env: Env }) {
|
|
9
|
+
const runtime = useRuntime();
|
|
10
|
+
const host = useHost();
|
|
11
|
+
const frameRef = useRef<HTMLIFrameElement>(null);
|
|
12
|
+
const [height, setHeight] = useState(180);
|
|
13
|
+
const [srcDoc, setSrcDoc] = useState("");
|
|
14
|
+
const html = asString(propValue(runtime, node, "html", env));
|
|
15
|
+
const css = asString(propValue(runtime, node, "css", env));
|
|
16
|
+
const js = asString(propValue(runtime, node, "js", env));
|
|
17
|
+
const title = asString(propValue(runtime, node, "title", env), "交互");
|
|
18
|
+
const blocked = sandboxRemote(html) || sandboxRemote(css) || sandboxRemote(js);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (blocked) return;
|
|
22
|
+
const paint = () => {
|
|
23
|
+
const theme = document.documentElement.getAttribute("data-theme") === "dark" ? "dark" : "light";
|
|
24
|
+
setSrcDoc(buildSrcdoc({ html, css, js, canPrompt: typeof host.issuePrompt === "function", theme }));
|
|
25
|
+
};
|
|
26
|
+
paint();
|
|
27
|
+
const observer = new MutationObserver(paint);
|
|
28
|
+
observer.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
|
|
29
|
+
return () => observer.disconnect();
|
|
30
|
+
}, [blocked, html, css, js, host]);
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const onMessage = (event: MessageEvent) => {
|
|
34
|
+
const frame = frameRef.current;
|
|
35
|
+
if (!frame || event.source !== frame.contentWindow) return;
|
|
36
|
+
const message = readSandboxMessage(event.data);
|
|
37
|
+
if (!message) return;
|
|
38
|
+
if (message.type === "height") setHeight(message.height);
|
|
39
|
+
if (message.type === "openUrl" && isHttpsUrl(message.url)) {
|
|
40
|
+
if (host.openUrl) host.openUrl(message.url);
|
|
41
|
+
else window.open(message.url, "_blank", "noopener,noreferrer");
|
|
42
|
+
}
|
|
43
|
+
if (message.type === "issuePrompt") host.issuePrompt?.(message.text);
|
|
44
|
+
if (message.type === "copy") {
|
|
45
|
+
if (host.copy) host.copy(message.text);
|
|
46
|
+
else void navigator.clipboard?.writeText(message.text);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
window.addEventListener("message", onMessage);
|
|
50
|
+
return () => window.removeEventListener("message", onMessage);
|
|
51
|
+
}, [host]);
|
|
52
|
+
|
|
53
|
+
if (blocked) return <p className="garden-ui-error-inline">{blocked}</p>;
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<iframe
|
|
57
|
+
ref={frameRef}
|
|
58
|
+
className="garden-ui-sandbox"
|
|
59
|
+
title={title}
|
|
60
|
+
sandbox="allow-scripts"
|
|
61
|
+
referrerPolicy="no-referrer"
|
|
62
|
+
srcDoc={srcDoc}
|
|
63
|
+
style={{ height }}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
registerComponents({ Sandbox });
|
package/ui/react/index.tsx
CHANGED
|
@@ -5,16 +5,18 @@ import "./components/inputs";
|
|
|
5
5
|
import "./components/display";
|
|
6
6
|
import "./components/chart";
|
|
7
7
|
import "./components/media";
|
|
8
|
-
import
|
|
8
|
+
import "./components/sandbox";
|
|
9
|
+
import { UiRuntime, type UiHost } from "./runtime";
|
|
9
10
|
import type { Diagnostic, UiPayload } from "./types";
|
|
10
11
|
|
|
11
12
|
export { UiRuntime } from "./runtime";
|
|
13
|
+
export type { UiHost } from "./runtime";
|
|
12
14
|
export type { Diagnostic, UiNode, UiPayload } from "./types";
|
|
13
15
|
|
|
14
|
-
export function UiFigure({ payload }: { payload: UiPayload }) {
|
|
16
|
+
export function UiFigure({ payload, host }: { payload: UiPayload; host?: UiHost }) {
|
|
15
17
|
return (
|
|
16
18
|
<figure className="garden-ui" lang="en-US" id={`ui-${payload.id}`}>
|
|
17
|
-
<UiRuntime payload={payload} />
|
|
19
|
+
<UiRuntime payload={payload} host={host} />
|
|
18
20
|
<figcaption>{payload.caption}</figcaption>
|
|
19
21
|
</figure>
|
|
20
22
|
);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { compileUiBlock } from "../core/compile.mjs";
|
|
4
|
+
import { PARITY_FIXTURES } from "../parity-fixtures.mjs";
|
|
5
|
+
import { UiFigure } from "./index";
|
|
6
|
+
import type { UiPayload } from "./types";
|
|
7
|
+
|
|
8
|
+
function payloadFor(body: string): UiPayload | null {
|
|
9
|
+
return compileUiBlock(body).payload as UiPayload | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function ParityInteractions() {
|
|
13
|
+
return (
|
|
14
|
+
<>
|
|
15
|
+
{PARITY_FIXTURES.map((fixture) => {
|
|
16
|
+
const payload = payloadFor(fixture.body);
|
|
17
|
+
return (
|
|
18
|
+
<section id={fixture.id} key={fixture.id} style={{ width: fixture.width, marginBottom: 32 }}>
|
|
19
|
+
{payload ? <UiFigure payload={payload} /> : <p>对照块没有通过检查。</p>}
|
|
20
|
+
</section>
|
|
21
|
+
);
|
|
22
|
+
})}
|
|
23
|
+
</>
|
|
24
|
+
);
|
|
25
|
+
}
|
package/ui/react/runtime.tsx
CHANGED
|
@@ -24,6 +24,18 @@ export type Runtime = {
|
|
|
24
24
|
|
|
25
25
|
const RuntimeContext = createContext<Runtime | null>(null);
|
|
26
26
|
|
|
27
|
+
export type UiHost = {
|
|
28
|
+
openUrl?: (url: string) => void;
|
|
29
|
+
issuePrompt?: (text: string) => void;
|
|
30
|
+
copy?: (text: string) => void;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const HostContext = createContext<UiHost>({});
|
|
34
|
+
|
|
35
|
+
export function useHost() {
|
|
36
|
+
return useContext(HostContext);
|
|
37
|
+
}
|
|
38
|
+
|
|
27
39
|
export function useRuntime() {
|
|
28
40
|
const runtime = useContext(RuntimeContext);
|
|
29
41
|
if (!runtime) throw new Error("Garden UI component outside UiRuntime");
|
|
@@ -204,7 +216,9 @@ function carryOver(defaults: Record<string, unknown>, previous: Record<string, u
|
|
|
204
216
|
return next;
|
|
205
217
|
}
|
|
206
218
|
|
|
207
|
-
|
|
219
|
+
const EMPTY_HOST: UiHost = {};
|
|
220
|
+
|
|
221
|
+
export function UiRuntime({ payload, host }: { payload: UiPayload; host?: UiHost }) {
|
|
208
222
|
const decls = useMemo(() => collectDecls(payload.root), [payload.root]);
|
|
209
223
|
const defaults = useMemo(() => defaultStateValues(decls) as Record<string, unknown>, [decls]);
|
|
210
224
|
const [state, setStateValue] = useState<Record<string, unknown>>(defaults);
|
|
@@ -230,10 +244,12 @@ export function UiRuntime({ payload }: { payload: UiPayload }) {
|
|
|
230
244
|
[payload.id, payload.root, decls, data, defaults, state, scope, sliders, setState, reset, scopeFor],
|
|
231
245
|
);
|
|
232
246
|
return (
|
|
233
|
-
<
|
|
234
|
-
<
|
|
235
|
-
<
|
|
236
|
-
|
|
237
|
-
|
|
247
|
+
<HostContext.Provider value={host ?? EMPTY_HOST}>
|
|
248
|
+
<RuntimeContext.Provider value={runtime}>
|
|
249
|
+
<div className="garden-ui-body">
|
|
250
|
+
<RenderNodes nodes={payload.root.c} env={{ locals: [] }} />
|
|
251
|
+
</div>
|
|
252
|
+
</RuntimeContext.Provider>
|
|
253
|
+
</HostContext.Provider>
|
|
238
254
|
);
|
|
239
255
|
}
|
package/ui/react/ui.css
CHANGED
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
.garden-catalog,
|
|
8
8
|
.garden-ui-preview {
|
|
9
9
|
--gui-bg: #fff;
|
|
10
|
-
--gui-chart:
|
|
10
|
+
--gui-chart: #f4f4f4;
|
|
11
11
|
--gui-track: #e4e4e4;
|
|
12
12
|
--gui-thumb: #fff;
|
|
13
|
+
--gui-slider-track: #0d0d0d1f;
|
|
14
|
+
--gui-slider-thumb: #333;
|
|
13
15
|
--gui-text: #0d0d0d;
|
|
14
16
|
--gui-muted: #5d5d5d;
|
|
15
17
|
--gui-subtle: #8f8f8f;
|
|
@@ -36,6 +38,12 @@
|
|
|
36
38
|
--gui-popover-border: #0000000d;
|
|
37
39
|
--gui-shadow: 0 2px 6px #00000014;
|
|
38
40
|
--gui-code-bg: #f9f9f9;
|
|
41
|
+
--gui-accent: #0285ff;
|
|
42
|
+
--gui-series-plain: #171717;
|
|
43
|
+
--gui-series-accent: #0285ff;
|
|
44
|
+
--gui-series-new: #00a240;
|
|
45
|
+
--gui-series-waste: #e02e2a;
|
|
46
|
+
--gui-series-same: #924ff7;
|
|
39
47
|
--gui-info: #539af8;
|
|
40
48
|
--gui-info-text: #0169cc;
|
|
41
49
|
--gui-info-soft: #0285ff1a;
|
|
@@ -82,6 +90,8 @@
|
|
|
82
90
|
--gui-segment-shadow: none;
|
|
83
91
|
--gui-track: #3a3a3a;
|
|
84
92
|
--gui-thumb: #fff;
|
|
93
|
+
--gui-slider-track: #ffffff26;
|
|
94
|
+
--gui-slider-thumb: #ededed;
|
|
85
95
|
--gui-progress: #0285ff;
|
|
86
96
|
--gui-progress-track: #303030;
|
|
87
97
|
--gui-focus: #3a83f7;
|
|
@@ -89,6 +99,10 @@
|
|
|
89
99
|
--gui-popover-border: #ffffff26;
|
|
90
100
|
--gui-shadow: 0 2px 8px #00000066;
|
|
91
101
|
--gui-code-bg: #171717;
|
|
102
|
+
--gui-accent: #0285ff;
|
|
103
|
+
--gui-series-plain: #ededed;
|
|
104
|
+
--gui-series-new: #04b84c;
|
|
105
|
+
--gui-series-waste: #fa423e;
|
|
92
106
|
--gui-info: #0285ff;
|
|
93
107
|
--gui-info-text: #66b5ff;
|
|
94
108
|
--gui-info-soft: #0285ff26;
|
|
@@ -276,7 +290,7 @@
|
|
|
276
290
|
.garden-ui-steps-head strong { color: var(--gui-text); font-size: 16px; font-weight: 600; line-height: 1.4; }
|
|
277
291
|
.garden-ui-steps-head span { flex-shrink: 0; color: var(--gui-muted); font-size: 12px; font-variant-numeric: tabular-nums; }
|
|
278
292
|
|
|
279
|
-
.garden-ui-steps .garden-ui-progress { height:
|
|
293
|
+
.garden-ui-steps .garden-ui-progress { height: 3px; margin: 2px 0 0; }
|
|
280
294
|
|
|
281
295
|
.garden-ui-steps-stage { display: grid; }
|
|
282
296
|
|
|
@@ -386,7 +400,7 @@
|
|
|
386
400
|
|
|
387
401
|
.garden-ui-button.garden-ui-tone-accent:hover { border-color: var(--gui-solid-hover); background: var(--gui-solid-hover); }
|
|
388
402
|
|
|
389
|
-
/* slider:
|
|
403
|
+
/* slider: 24px track, 18px thumb inset 3px, no filled portion */
|
|
390
404
|
.garden-ui-slider { display: flex; flex-direction: column; gap: 8px; }
|
|
391
405
|
|
|
392
406
|
.garden-ui-slider label {
|
|
@@ -403,12 +417,12 @@
|
|
|
403
417
|
|
|
404
418
|
.garden-ui-slider-track { padding: 8px 0; }
|
|
405
419
|
|
|
406
|
-
.garden-ui-slider-track:has(input:focus-visible) { outline: 2px solid var(--gui-focus); outline-offset:
|
|
420
|
+
.garden-ui-slider-track:has(input:focus-visible) { outline: 2px solid var(--gui-focus); outline-offset: 3px; border-radius: 999px; }
|
|
407
421
|
|
|
408
422
|
.garden-ui-slider input[type="range"] {
|
|
409
423
|
display: block;
|
|
410
424
|
width: 100%;
|
|
411
|
-
height:
|
|
425
|
+
height: 38px;
|
|
412
426
|
margin: 0;
|
|
413
427
|
background: transparent;
|
|
414
428
|
cursor: ew-resize;
|
|
@@ -419,39 +433,39 @@
|
|
|
419
433
|
.garden-ui-slider input[type="range"]:focus-visible { outline: none; }
|
|
420
434
|
|
|
421
435
|
.garden-ui-slider input[type="range"]::-webkit-slider-runnable-track {
|
|
422
|
-
height:
|
|
436
|
+
height: 24px;
|
|
423
437
|
border: 0;
|
|
424
438
|
border-radius: 999px;
|
|
425
|
-
background: var(--gui-track);
|
|
439
|
+
background: var(--gui-slider-track);
|
|
426
440
|
}
|
|
427
441
|
|
|
428
442
|
.garden-ui-slider input[type="range"]::-moz-range-track {
|
|
429
|
-
height:
|
|
443
|
+
height: 24px;
|
|
430
444
|
border: 0;
|
|
431
445
|
border-radius: 999px;
|
|
432
|
-
background: var(--gui-track);
|
|
446
|
+
background: var(--gui-slider-track);
|
|
433
447
|
}
|
|
434
448
|
|
|
435
449
|
.garden-ui-slider input[type="range"]::-webkit-slider-thumb {
|
|
436
|
-
width:
|
|
437
|
-
height:
|
|
438
|
-
margin-top:
|
|
439
|
-
border:
|
|
450
|
+
width: 18px;
|
|
451
|
+
height: 18px;
|
|
452
|
+
margin-top: 3px;
|
|
453
|
+
border: 0;
|
|
440
454
|
border-radius: 50%;
|
|
441
|
-
background: var(--gui-thumb);
|
|
442
|
-
box-shadow:
|
|
455
|
+
background: var(--gui-slider-thumb);
|
|
456
|
+
box-shadow: none;
|
|
443
457
|
-webkit-appearance: none;
|
|
444
458
|
appearance: none;
|
|
445
459
|
transition: transform 120ms ease;
|
|
446
460
|
}
|
|
447
461
|
|
|
448
462
|
.garden-ui-slider input[type="range"]::-moz-range-thumb {
|
|
449
|
-
width:
|
|
450
|
-
height:
|
|
451
|
-
border:
|
|
463
|
+
width: 18px;
|
|
464
|
+
height: 18px;
|
|
465
|
+
border: 0;
|
|
452
466
|
border-radius: 50%;
|
|
453
|
-
background: var(--gui-thumb);
|
|
454
|
-
box-shadow:
|
|
467
|
+
background: var(--gui-slider-thumb);
|
|
468
|
+
box-shadow: none;
|
|
455
469
|
}
|
|
456
470
|
|
|
457
471
|
.garden-ui-slider input[type="range"]:active::-webkit-slider-thumb { transform: scale(1.08); }
|
|
@@ -802,16 +816,16 @@
|
|
|
802
816
|
}
|
|
803
817
|
.garden-ui-chart-canvas:focus-visible { outline: 2px solid var(--gui-focus); outline-offset: 2px; }
|
|
804
818
|
|
|
805
|
-
.garden-ui-series-plain { --gui-series:
|
|
806
|
-
.garden-ui-series-accent { --gui-series:
|
|
807
|
-
.garden-ui-series-new { --gui-series:
|
|
808
|
-
.garden-ui-series-waste { --gui-series:
|
|
809
|
-
.garden-ui-series-same { --gui-series:
|
|
819
|
+
.garden-ui-series-plain { --gui-series: var(--gui-series-plain); }
|
|
820
|
+
.garden-ui-series-accent { --gui-series: var(--gui-series-accent); }
|
|
821
|
+
.garden-ui-series-new { --gui-series: var(--gui-series-new); }
|
|
822
|
+
.garden-ui-series-waste { --gui-series: var(--gui-series-waste); }
|
|
823
|
+
.garden-ui-series-same { --gui-series: var(--gui-series-same); }
|
|
810
824
|
.garden-ui-series-muted,
|
|
811
825
|
.garden-ui-series-cached { --gui-series: var(--gui-subtle); }
|
|
812
826
|
|
|
813
827
|
.garden-ui-chart-grid { stroke: var(--gui-border); stroke-width: 1; stroke-dasharray: 2 4; }
|
|
814
|
-
.garden-ui-chart-axis { stroke:
|
|
828
|
+
.garden-ui-chart-axis { stroke: var(--gui-border); stroke-width: 1; }
|
|
815
829
|
.garden-ui-chart .garden-ui-legend { justify-content: center; }
|
|
816
830
|
.garden-ui-chart-tick,
|
|
817
831
|
.garden-ui-chart-label { fill: var(--gui-subtle); font-family: var(--gui-font); font-size: 12px; }
|
|
@@ -889,8 +903,8 @@
|
|
|
889
903
|
}
|
|
890
904
|
|
|
891
905
|
.garden-ui-code code { padding: 0; border-radius: 0; background: none; color: var(--gui-text); font-size: 13px; }
|
|
892
|
-
.garden-ui-code-line { display: block; padding: 0 14px 0
|
|
893
|
-
.garden-ui-code-line[data-highlight="true"] {
|
|
906
|
+
.garden-ui-code-line { display: block; padding: 0 14px 0 12px; box-shadow: inset 2px 0 0 transparent; }
|
|
907
|
+
.garden-ui-code-line[data-highlight="true"] { background: var(--gui-info-soft); box-shadow: inset 2px 0 0 var(--gui-accent); }
|
|
894
908
|
.garden-ui-code-number { display: inline-block; width: 3em; padding-right: 1em; color: var(--gui-subtle); text-align: right; user-select: none; }
|
|
895
909
|
|
|
896
910
|
.garden-ui-diagram-canvas { overflow-x: auto; padding: 12px; border-radius: 12px; background: var(--gui-code-bg); }
|
|
@@ -908,10 +922,19 @@
|
|
|
908
922
|
.garden-ui-diagram-canvas .garden-ui-diagram-on rect,
|
|
909
923
|
.garden-ui-diagram-canvas .garden-ui-diagram-on polygon,
|
|
910
924
|
.garden-ui-diagram-canvas .garden-ui-diagram-on circle,
|
|
911
|
-
.garden-ui-diagram-canvas rect.garden-ui-diagram-on { fill: var(--gui-info-soft) !important; stroke: var(--gui-
|
|
925
|
+
.garden-ui-diagram-canvas rect.garden-ui-diagram-on { fill: var(--gui-info-soft) !important; stroke: var(--gui-accent) !important; }
|
|
912
926
|
.garden-ui-diagram-canvas .garden-ui-diagram-on .nodeLabel,
|
|
913
|
-
.garden-ui-diagram-canvas .garden-ui-diagram-on text { color: var(--gui-
|
|
914
|
-
.garden-ui-diagram-canvas path.garden-ui-diagram-on { stroke: var(--gui-
|
|
927
|
+
.garden-ui-diagram-canvas .garden-ui-diagram-on text { color: var(--gui-accent) !important; fill: var(--gui-accent) !important; }
|
|
928
|
+
.garden-ui-diagram-canvas path.garden-ui-diagram-on { stroke: var(--gui-accent) !important; stroke-width: 2px !important; }
|
|
929
|
+
|
|
930
|
+
.garden-ui-sandbox {
|
|
931
|
+
display: block;
|
|
932
|
+
width: 100%;
|
|
933
|
+
min-height: 48px;
|
|
934
|
+
border: 0;
|
|
935
|
+
border-radius: 12px;
|
|
936
|
+
background: transparent;
|
|
937
|
+
}
|
|
915
938
|
|
|
916
939
|
/* intent: the author's words, shown like a message bubble */
|
|
917
940
|
.garden-ui-intent { border: 0; background: transparent; }
|