@tenphi/renderer 0.25.3 → 0.26.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/components/MainContent.styles.js +3 -0
- package/dist/components/Pagination.styles.js +1 -1
- package/dist/components/Preview.astro +10 -5
- package/dist/components/Sidebar.styles.js +3 -0
- package/dist/components/appearance-bootstrap.js +21 -13
- package/dist/components/appearance-preferences.test.ts +63 -27
- package/dist/components/appearance-preferences.ts +23 -16
- package/dist/components/props.d.ts +2 -0
- package/dist/components/props.d.ts.map +1 -1
- package/dist/components/props.ts +2 -0
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/overrides/ThemeSelect.astro +16 -9
- package/dist/preview-document.d.ts +14 -0
- package/dist/preview-document.d.ts.map +1 -0
- package/dist/preview-document.js +33 -0
- package/dist/preview-document.js.map +1 -0
- package/package.json +2 -2
|
@@ -10,6 +10,9 @@ export const MainContentRoot = defineComponent("MainContent", {
|
|
|
10
10
|
styles: {
|
|
11
11
|
"@property": TASTY_SPACING_PROPERTIES,
|
|
12
12
|
padding: "0 0 5rem",
|
|
13
|
+
// A focus target for the skip link and page swaps, not a control. Clicking
|
|
14
|
+
// its text focuses it, so keyboard scrolling would otherwise show a ring.
|
|
15
|
+
outline: "none",
|
|
13
16
|
// Layout spacing is a default; generated Markdown owns prose spacing.
|
|
14
17
|
ContentSpacing: {
|
|
15
18
|
$: ":where(Panel > Container) > * + *",
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { messages, requestLanguage } from "../localization.js";
|
|
3
|
+
import { buildPreview } from "../preview-document.js";
|
|
3
4
|
import { content } from "virtual:cookbook/config";
|
|
4
5
|
import { PreviewRoot } from "./Preview.styles.js";
|
|
5
6
|
|
|
6
7
|
import type { PreviewProps as Props } from "./props.js";
|
|
7
8
|
|
|
8
|
-
const { title
|
|
9
|
-
const
|
|
9
|
+
const { title } = Astro.props;
|
|
10
|
+
const preview = await buildPreview(
|
|
11
|
+
Astro.props,
|
|
12
|
+
content.publicDir,
|
|
13
|
+
Astro.url.pathname,
|
|
14
|
+
);
|
|
10
15
|
const ui = messages(
|
|
11
16
|
requestLanguage(Astro.url.pathname, content.base, content),
|
|
12
17
|
content,
|
|
@@ -18,12 +23,12 @@ const ui = messages(
|
|
|
18
23
|
<PreviewRoot.Stage>
|
|
19
24
|
<PreviewRoot.Frame
|
|
20
25
|
title={title}
|
|
21
|
-
sandbox={
|
|
22
|
-
srcDoc={
|
|
26
|
+
sandbox={preview.sandbox}
|
|
27
|
+
srcDoc={preview.document}
|
|
23
28
|
/>
|
|
24
29
|
</PreviewRoot.Stage>
|
|
25
30
|
<PreviewRoot.Code>
|
|
26
31
|
<PreviewRoot.Summary>{ui.viewSource}</PreviewRoot.Summary>
|
|
27
|
-
<PreviewRoot.Pre><code>{source}</code></PreviewRoot.Pre>
|
|
32
|
+
<PreviewRoot.Pre><code>{preview.source}</code></PreviewRoot.Pre>
|
|
28
33
|
</PreviewRoot.Code>
|
|
29
34
|
</PreviewRoot>
|
|
@@ -254,6 +254,9 @@ export const SidebarRoot = defineComponent("Sidebar", {
|
|
|
254
254
|
'@own((:hover | :focus-visible) & ![aria-current="page"])':
|
|
255
255
|
"#surface-2-hover",
|
|
256
256
|
},
|
|
257
|
+
// Lift the focus ring above the next item's fill.
|
|
258
|
+
position: { "": null, "@own(:focus-visible)": "relative" },
|
|
259
|
+
zIndex: { "": null, "@own(:focus-visible)": "1" },
|
|
257
260
|
},
|
|
258
261
|
SummaryMarker: {
|
|
259
262
|
$: "cookbook-sidebar summary::marker, summary::-webkit-details-marker",
|
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
// Kept dependency-free for the page shell's synchronous, pre-paint script.
|
|
2
|
+
// Reads the documented storage contract in appearance-preferences.ts.
|
|
2
3
|
(() => {
|
|
3
4
|
const root = globalThis.document.documentElement;
|
|
5
|
+
let appearance = {};
|
|
4
6
|
try {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// Without storage, the document's system appearance rules remain active.
|
|
10
|
-
}
|
|
11
|
-
try {
|
|
12
|
-
const appearance = JSON.parse(
|
|
13
|
-
globalThis.localStorage.getItem("cookbook-appearance") ?? "{}",
|
|
14
|
-
);
|
|
15
|
-
if (appearance?.contrast === "normal" || appearance?.contrast === "more")
|
|
16
|
-
root.dataset.contrast = appearance.contrast;
|
|
17
|
-
else delete root.dataset.contrast;
|
|
7
|
+
appearance =
|
|
8
|
+
JSON.parse(
|
|
9
|
+
globalThis.localStorage.getItem("cookbook-appearance") ?? "{}",
|
|
10
|
+
) ?? {};
|
|
18
11
|
} catch {
|
|
19
12
|
// Malformed or unavailable storage must not prevent the first paint.
|
|
20
13
|
}
|
|
14
|
+
let theme = appearance.theme;
|
|
15
|
+
if (theme === undefined) {
|
|
16
|
+
try {
|
|
17
|
+
// A scheme saved before the appearance object held it.
|
|
18
|
+
theme = globalThis.localStorage.getItem("cookbook-theme");
|
|
19
|
+
} catch {
|
|
20
|
+
// Without storage, the document's system appearance rules remain active.
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (theme === "light" || theme === "dark") root.dataset.theme = theme;
|
|
24
|
+
else delete root.dataset.theme;
|
|
25
|
+
const contrast = appearance.contrast;
|
|
26
|
+
if (contrast === "normal" || contrast === "more")
|
|
27
|
+
root.dataset.contrast = contrast;
|
|
28
|
+
else delete root.dataset.contrast;
|
|
21
29
|
})();
|
|
@@ -7,71 +7,104 @@ function storage(values: Record<string, string> = {}) {
|
|
|
7
7
|
setItem: (key: string, value: string) => {
|
|
8
8
|
values[key] = value;
|
|
9
9
|
},
|
|
10
|
+
removeItem: (key: string) => {
|
|
11
|
+
delete values[key];
|
|
12
|
+
},
|
|
10
13
|
};
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
describe("appearance preferences", () => {
|
|
14
|
-
it("restores
|
|
17
|
+
it("restores independent theme and contrast choices from one object", () => {
|
|
15
18
|
const saved = storage({
|
|
16
|
-
"cookbook-
|
|
17
|
-
|
|
19
|
+
"cookbook-appearance": JSON.stringify({
|
|
20
|
+
theme: "dark",
|
|
21
|
+
contrast: "more",
|
|
22
|
+
}),
|
|
18
23
|
});
|
|
19
24
|
expect(loadPreferences(saved)).toEqual({
|
|
20
|
-
|
|
25
|
+
theme: "dark",
|
|
21
26
|
contrast: "more",
|
|
22
27
|
});
|
|
23
|
-
storePreferences({
|
|
28
|
+
storePreferences({ theme: "light", contrast: "more" }, saved);
|
|
24
29
|
expect(loadPreferences(saved)).toEqual({
|
|
25
|
-
|
|
30
|
+
theme: "light",
|
|
26
31
|
contrast: "more",
|
|
27
32
|
});
|
|
28
|
-
storePreferences({
|
|
29
|
-
expect(
|
|
30
|
-
|
|
33
|
+
storePreferences({ theme: "light", contrast: "normal" }, saved);
|
|
34
|
+
expect(JSON.parse(saved.getItem("cookbook-appearance")!)).toEqual({
|
|
35
|
+
theme: "light",
|
|
31
36
|
contrast: "normal",
|
|
32
37
|
});
|
|
33
38
|
});
|
|
34
39
|
|
|
35
|
-
it("persists
|
|
40
|
+
it("persists system choices without losing other fields", () => {
|
|
36
41
|
const saved = storage({
|
|
37
42
|
"cookbook-appearance": JSON.stringify({ contrast: "more", other: true }),
|
|
38
43
|
});
|
|
39
|
-
storePreferences({
|
|
40
|
-
expect(saved.getItem("cookbook-theme")).toBe("");
|
|
44
|
+
storePreferences({ theme: "system", contrast: "system" }, saved);
|
|
41
45
|
expect(JSON.parse(saved.getItem("cookbook-appearance")!)).toEqual({
|
|
46
|
+
theme: "system",
|
|
42
47
|
contrast: "system",
|
|
43
48
|
other: true,
|
|
44
49
|
});
|
|
45
50
|
expect(loadPreferences(saved)).toEqual({
|
|
46
|
-
|
|
51
|
+
theme: "system",
|
|
47
52
|
contrast: "system",
|
|
48
53
|
});
|
|
49
54
|
});
|
|
50
55
|
|
|
56
|
+
it("reads a scheme saved under the legacy key and removes it on save", () => {
|
|
57
|
+
const saved = storage({
|
|
58
|
+
"cookbook-theme": "dark",
|
|
59
|
+
"cookbook-appearance": JSON.stringify({ contrast: "more" }),
|
|
60
|
+
});
|
|
61
|
+
expect(loadPreferences(saved)).toEqual({
|
|
62
|
+
theme: "dark",
|
|
63
|
+
contrast: "more",
|
|
64
|
+
});
|
|
65
|
+
storePreferences(loadPreferences(saved), saved);
|
|
66
|
+
expect(saved.getItem("cookbook-theme")).toBeNull();
|
|
67
|
+
expect(JSON.parse(saved.getItem("cookbook-appearance")!)).toEqual({
|
|
68
|
+
theme: "dark",
|
|
69
|
+
contrast: "more",
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("prefers the object's theme over the legacy key", () => {
|
|
74
|
+
const saved = storage({
|
|
75
|
+
"cookbook-theme": "dark",
|
|
76
|
+
"cookbook-appearance": JSON.stringify({ theme: "system" }),
|
|
77
|
+
});
|
|
78
|
+
expect(loadPreferences(saved).theme).toBe("system");
|
|
79
|
+
});
|
|
80
|
+
|
|
51
81
|
it.each(["{broken", "null", "[]", '"more"', '{"contrast":"invalid"}'])(
|
|
52
|
-
"recovers from malformed
|
|
82
|
+
"recovers from malformed appearance data: %s",
|
|
53
83
|
(value) => {
|
|
54
|
-
const saved = storage({
|
|
55
|
-
"cookbook-theme": "light",
|
|
56
|
-
"cookbook-appearance": value,
|
|
57
|
-
});
|
|
84
|
+
const saved = storage({ "cookbook-appearance": value });
|
|
58
85
|
expect(loadPreferences(saved)).toEqual({
|
|
59
|
-
|
|
86
|
+
theme: "system",
|
|
60
87
|
contrast: "system",
|
|
61
88
|
});
|
|
62
|
-
storePreferences({
|
|
89
|
+
storePreferences({ theme: "dark", contrast: "more" }, saved);
|
|
63
90
|
expect(loadPreferences(saved)).toEqual({
|
|
64
|
-
|
|
91
|
+
theme: "dark",
|
|
65
92
|
contrast: "more",
|
|
66
93
|
});
|
|
67
94
|
},
|
|
68
95
|
);
|
|
69
96
|
|
|
70
97
|
it("uses system defaults for unknown values or unavailable storage", () => {
|
|
71
|
-
expect(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
98
|
+
expect(
|
|
99
|
+
loadPreferences(
|
|
100
|
+
storage({
|
|
101
|
+
"cookbook-appearance": JSON.stringify({
|
|
102
|
+
theme: "invalid",
|
|
103
|
+
contrast: "invalid",
|
|
104
|
+
}),
|
|
105
|
+
}),
|
|
106
|
+
),
|
|
107
|
+
).toEqual({ theme: "system", contrast: "system" });
|
|
75
108
|
const unavailable = {
|
|
76
109
|
getItem: () => {
|
|
77
110
|
throw new Error("Storage unavailable");
|
|
@@ -79,13 +112,16 @@ describe("appearance preferences", () => {
|
|
|
79
112
|
setItem: () => {
|
|
80
113
|
throw new Error("Storage unavailable");
|
|
81
114
|
},
|
|
115
|
+
removeItem: () => {
|
|
116
|
+
throw new Error("Storage unavailable");
|
|
117
|
+
},
|
|
82
118
|
};
|
|
83
119
|
expect(loadPreferences(unavailable)).toEqual({
|
|
84
|
-
|
|
120
|
+
theme: "system",
|
|
85
121
|
contrast: "system",
|
|
86
122
|
});
|
|
87
123
|
expect(() =>
|
|
88
|
-
storePreferences({
|
|
124
|
+
storePreferences({ theme: "dark", contrast: "more" }, unavailable),
|
|
89
125
|
).not.toThrow();
|
|
90
126
|
});
|
|
91
127
|
});
|
|
@@ -1,19 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
// `cookbook-appearance` is a documented contract that other apps on the same
|
|
2
|
+
// origin read and write (docs/theme-and-components.md, "Reader appearance").
|
|
3
|
+
// Keep its key and value format compatible; appearance-bootstrap.js reads it too.
|
|
4
|
+
export const APPEARANCE_KEY = "cookbook-appearance";
|
|
5
|
+
// The color scheme used to live in its own key. It is still read when the
|
|
6
|
+
// object has no `theme`, and removed on the next save.
|
|
7
|
+
export const LEGACY_THEME_KEY = "cookbook-theme";
|
|
8
|
+
|
|
9
|
+
type Theme = "light" | "dark" | "system";
|
|
2
10
|
type Contrast = "normal" | "more" | "system";
|
|
3
|
-
type Preferences = {
|
|
4
|
-
type PreferenceStorage = Pick<Storage, "getItem" | "setItem">;
|
|
11
|
+
type Preferences = { theme: Theme; contrast: Contrast };
|
|
12
|
+
type PreferenceStorage = Pick<Storage, "getItem" | "setItem" | "removeItem">;
|
|
5
13
|
|
|
6
|
-
export const
|
|
7
|
-
value === "light" || value === "dark" ? value : "
|
|
14
|
+
export const parseTheme = (value: unknown): Theme =>
|
|
15
|
+
value === "light" || value === "dark" ? value : "system";
|
|
8
16
|
|
|
9
17
|
export const parseContrast = (value: unknown): Contrast =>
|
|
10
18
|
value === "normal" || value === "more" ? value : "system";
|
|
11
19
|
|
|
12
20
|
function storedAppearance(storage: PreferenceStorage): Record<string, unknown> {
|
|
13
21
|
try {
|
|
14
|
-
const value: unknown = JSON.parse(
|
|
15
|
-
storage.getItem("cookbook-appearance") ?? "{}",
|
|
16
|
-
);
|
|
22
|
+
const value: unknown = JSON.parse(storage.getItem(APPEARANCE_KEY) ?? "{}");
|
|
17
23
|
return value && typeof value === "object" && !Array.isArray(value)
|
|
18
24
|
? (value as Record<string, unknown>)
|
|
19
25
|
: {};
|
|
@@ -23,11 +29,14 @@ function storedAppearance(storage: PreferenceStorage): Record<string, unknown> {
|
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
export function loadPreferences(storage?: PreferenceStorage): Preferences {
|
|
26
|
-
const preferences: Preferences = {
|
|
32
|
+
const preferences: Preferences = { theme: "system", contrast: "system" };
|
|
27
33
|
try {
|
|
28
34
|
storage ??= localStorage;
|
|
29
|
-
|
|
30
|
-
preferences.
|
|
35
|
+
const stored = storedAppearance(storage);
|
|
36
|
+
preferences.theme = parseTheme(
|
|
37
|
+
"theme" in stored ? stored.theme : storage.getItem(LEGACY_THEME_KEY),
|
|
38
|
+
);
|
|
39
|
+
preferences.contrast = parseContrast(stored.contrast);
|
|
31
40
|
} catch {
|
|
32
41
|
// System defaults work even when browser storage is unavailable.
|
|
33
42
|
}
|
|
@@ -41,16 +50,14 @@ export function storePreferences(
|
|
|
41
50
|
try {
|
|
42
51
|
storage ??= localStorage;
|
|
43
52
|
storage.setItem(
|
|
44
|
-
|
|
45
|
-
value.scheme === "auto" ? "" : value.scheme,
|
|
46
|
-
);
|
|
47
|
-
storage.setItem(
|
|
48
|
-
"cookbook-appearance",
|
|
53
|
+
APPEARANCE_KEY,
|
|
49
54
|
JSON.stringify({
|
|
50
55
|
...storedAppearance(storage),
|
|
56
|
+
theme: value.theme,
|
|
51
57
|
contrast: value.contrast,
|
|
52
58
|
}),
|
|
53
59
|
);
|
|
60
|
+
storage.removeItem(LEGACY_THEME_KEY);
|
|
54
61
|
} catch {
|
|
55
62
|
// The current page still updates when preferences cannot be persisted.
|
|
56
63
|
}
|
|
@@ -13,6 +13,8 @@ interface PreviewProps {
|
|
|
13
13
|
html?: string;
|
|
14
14
|
css?: string;
|
|
15
15
|
javascript?: string;
|
|
16
|
+
/** Public-directory paths, such as `/demo/button.js`, inlined as classic scripts before `javascript`. */
|
|
17
|
+
scripts?: string[];
|
|
16
18
|
}
|
|
17
19
|
type StepsProps = Record<string, never>;
|
|
18
20
|
interface TabsProps {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"props.d.ts","names":[],"sources":["../../src/components/props.ts"],"mappings":";UAAiB;EACf;EACA;;UAEe;EACf;EACA;EACA;;UAEe;EACf;EACA;EACA;EACA;;KAEU,aAAa;UACR;EACf;;UAEe;EACf;;UAEe;EACf;EACA;;UAEe;EACf;EACA,OAAO;IAAQ;IAAe;IAAc"}
|
|
1
|
+
{"version":3,"file":"props.d.ts","names":[],"sources":["../../src/components/props.ts"],"mappings":";UAAiB;EACf;EACA;;UAEe;EACf;EACA;EACA;;UAEe;EACf;EACA;EACA;EACA;;EAEA;;KAEU,aAAa;UACR;EACf;;UAEe;EACf;;UAEe;EACf;EACA;;UAEe;EACf;EACA,OAAO;IAAQ;IAAe;IAAc"}
|
package/dist/components/props.ts
CHANGED
|
@@ -12,6 +12,8 @@ export interface PreviewProps {
|
|
|
12
12
|
html?: string;
|
|
13
13
|
css?: string;
|
|
14
14
|
javascript?: string;
|
|
15
|
+
/** Public-directory paths, such as `/demo/button.js`, inlined as classic scripts before `javascript`. */
|
|
16
|
+
scripts?: string[];
|
|
15
17
|
}
|
|
16
18
|
export type StepsProps = Record<string, never>;
|
|
17
19
|
export interface TabsProps {
|
package/dist/index.js
CHANGED
|
@@ -1426,7 +1426,8 @@ function configuredCookbook(options) {
|
|
|
1426
1426
|
locales: loaded.config.locales,
|
|
1427
1427
|
defaultLocale: loaded.config.defaultLocale,
|
|
1428
1428
|
translations: loaded.config.translations,
|
|
1429
|
-
head: [...siteIcons?.head ?? [], ...options.config?.head ?? []]
|
|
1429
|
+
head: [...siteIcons?.head ?? [], ...options.config?.head ?? []],
|
|
1430
|
+
publicDir: fileURLToPath(context.config.publicDir)
|
|
1430
1431
|
};
|
|
1431
1432
|
}, navigation, () => [
|
|
1432
1433
|
projectRoot,
|