@tenphi/renderer 0.19.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/LICENSE +21 -0
- package/README.md +23 -0
- package/dist/client/appearance.d.ts +12 -0
- package/dist/client/appearance.d.ts.map +1 -0
- package/dist/client/appearance.js +66 -0
- package/dist/client/appearance.js.map +1 -0
- package/dist/client/code-copy.d.ts +1 -0
- package/dist/client/code-copy.js +63 -0
- package/dist/client/code-copy.js.map +1 -0
- package/dist/client/heading-links.d.ts +6 -0
- package/dist/client/heading-links.d.ts.map +1 -0
- package/dist/client/heading-links.js +87 -0
- package/dist/client/heading-links.js.map +1 -0
- package/dist/client/mobile-toc.d.ts +1 -0
- package/dist/client/mobile-toc.js +28 -0
- package/dist/client/mobile-toc.js.map +1 -0
- package/dist/client/navigation.d.ts +1 -0
- package/dist/client/navigation.js +112 -0
- package/dist/client/navigation.js.map +1 -0
- package/dist/client/page-copy.d.ts +1 -0
- package/dist/client/page-copy.js +34 -0
- package/dist/client/page-copy.js.map +1 -0
- package/dist/client/search.d.ts +13 -0
- package/dist/client/search.d.ts.map +1 -0
- package/dist/client/search.js +87 -0
- package/dist/client/search.js.map +1 -0
- package/dist/client/sidebar.d.ts +1 -0
- package/dist/client/sidebar.js +93 -0
- package/dist/client/sidebar.js.map +1 -0
- package/dist/client/tabs.d.ts +6 -0
- package/dist/client/tabs.d.ts.map +1 -0
- package/dist/client/tabs.js +64 -0
- package/dist/client/tabs.js.map +1 -0
- package/dist/components/Callout.astro +15 -0
- package/dist/components/Card.astro +13 -0
- package/dist/components/CodeGroup.astro +47 -0
- package/dist/components/ContrastIcon.astro +29 -0
- package/dist/components/EditLink.astro +11 -0
- package/dist/components/GlobalStyles.js +1675 -0
- package/dist/components/HeaderLinks.astro +84 -0
- package/dist/components/Icon.astro +39 -0
- package/dist/components/LastUpdated.astro +17 -0
- package/dist/components/LayoutComponents.js +866 -0
- package/dist/components/Logo.astro +37 -0
- package/dist/components/MobileNavigationTabs.astro +79 -0
- package/dist/components/MobileTocList.astro +22 -0
- package/dist/components/PackageVersion.astro +25 -0
- package/dist/components/PageActions.astro +29 -0
- package/dist/components/Pagination.astro +29 -0
- package/dist/components/Preview.astro +34 -0
- package/dist/components/SidebarList.astro +122 -0
- package/dist/components/SiteLogo.astro +40 -0
- package/dist/components/SiteTitle.astro +7 -0
- package/dist/components/Steps.astro +6 -0
- package/dist/components/Tab.astro +10 -0
- package/dist/components/TableOfContents.astro +23 -0
- package/dist/components/Tabs.astro +16 -0
- package/dist/components/TastyComponents.js +255 -0
- package/dist/components/VersionSwitcher.astro +44 -0
- package/dist/components/appearance-preferences.test.ts +91 -0
- package/dist/components/appearance-preferences.ts +57 -0
- package/dist/components/component-styles.test.ts +129 -0
- package/dist/components/component-styles.ts +65 -0
- package/dist/components/customize-component.js +8 -0
- package/dist/components/props.d.ts +38 -0
- package/dist/components/props.d.ts.map +1 -0
- package/dist/components/props.js +1 -0
- package/dist/components/props.ts +30 -0
- package/dist/components/svg-icon.test.ts +11 -0
- package/dist/components/svg-icon.ts +11 -0
- package/dist/components/tasty-states.d.ts +2 -0
- package/dist/components/tasty-states.js +42 -0
- package/dist/components-public.d.ts +21 -0
- package/dist/components.d.ts +10 -0
- package/dist/components.js +9 -0
- package/dist/content.d.ts +11 -0
- package/dist/content.d.ts.map +1 -0
- package/dist/content.js +16 -0
- package/dist/content.js.map +1 -0
- package/dist/eslint-plugin.d.ts +32 -0
- package/dist/eslint-plugin.d.ts.map +1 -0
- package/dist/eslint-plugin.js +155 -0
- package/dist/eslint-plugin.js.map +1 -0
- package/dist/icons/arrow-left.svg +1 -0
- package/dist/icons/arrow-right.svg +1 -0
- package/dist/icons/check.svg +1 -0
- package/dist/icons/chevron-down.svg +1 -0
- package/dist/icons/chevron-right.svg +1 -0
- package/dist/icons/close.svg +1 -0
- package/dist/icons/copy.svg +1 -0
- package/dist/icons/device-desktop.svg +1 -0
- package/dist/icons/favicon.svg +11 -0
- package/dist/icons/menu.svg +1 -0
- package/dist/icons/moon.svg +1 -0
- package/dist/icons/palette.svg +1 -0
- package/dist/icons/search.svg +1 -0
- package/dist/icons/sun.svg +1 -0
- package/dist/index-BuX_Sb5c.d.ts +10 -0
- package/dist/index-BuX_Sb5c.d.ts.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1648 -0
- package/dist/index.js.map +1 -0
- package/dist/localization.d.ts +69 -0
- package/dist/localization.d.ts.map +1 -0
- package/dist/localization.js +195 -0
- package/dist/localization.js.map +1 -0
- package/dist/markdown/rendered-content.d.ts +6 -0
- package/dist/markdown/rendered-content.d.ts.map +1 -0
- package/dist/markdown/rendered-content.js +34 -0
- package/dist/markdown/rendered-content.js.map +1 -0
- package/dist/messages-B_3xUzUb.js +14 -0
- package/dist/messages-B_3xUzUb.js.map +1 -0
- package/dist/navigation.d.ts +31 -0
- package/dist/navigation.d.ts.map +1 -0
- package/dist/navigation.js +165 -0
- package/dist/navigation.js.map +1 -0
- package/dist/overrides/DraftContentNotice.astro +8 -0
- package/dist/overrides/EmptyFooter.astro +3 -0
- package/dist/overrides/Footer.astro +23 -0
- package/dist/overrides/Head.astro +59 -0
- package/dist/overrides/Header.astro +139 -0
- package/dist/overrides/Hero.astro +73 -0
- package/dist/overrides/LanguageSelect.astro +63 -0
- package/dist/overrides/MarkdownContent.astro +14 -0
- package/dist/overrides/MobileMenuFooter.astro +15 -0
- package/dist/overrides/MobileMenuToggle.astro +36 -0
- package/dist/overrides/MobileTableOfContents.astro +37 -0
- package/dist/overrides/PageFrame.astro +35 -0
- package/dist/overrides/Search.astro +64 -0
- package/dist/overrides/Sidebar.astro +89 -0
- package/dist/overrides/SocialIcons.astro +27 -0
- package/dist/overrides/ThemeSelect.astro +148 -0
- package/dist/page-metadata.d.ts +13 -0
- package/dist/page-metadata.d.ts.map +1 -0
- package/dist/page-metadata.js +109 -0
- package/dist/page-metadata.js.map +1 -0
- package/dist/route-context.d.ts +85 -0
- package/dist/route-context.d.ts.map +1 -0
- package/dist/route-context.js +116 -0
- package/dist/route-context.js.map +1 -0
- package/dist/routes/DocsPage.astro +91 -0
- package/dist/routes/NotFound.astro +12 -0
- package/dist/routes/Page.astro +120 -0
- package/dist/styling.d.ts +17 -0
- package/dist/styling.d.ts.map +1 -0
- package/dist/styling.js +17 -0
- package/dist/styling.js.map +1 -0
- package/dist/tasty-states-BDKIbfw2.js +65 -0
- package/dist/tasty-states-BDKIbfw2.js.map +1 -0
- package/dist/theme/defaults.d.ts +21 -0
- package/dist/theme/defaults.d.ts.map +1 -0
- package/dist/theme/defaults.js +93 -0
- package/dist/theme/defaults.js.map +1 -0
- package/dist/theme/fonts.ts +64 -0
- package/dist/theme/shiki-theme.d.ts +13 -0
- package/dist/theme/shiki-theme.d.ts.map +1 -0
- package/dist/theme/shiki-theme.js +328 -0
- package/dist/theme/shiki-theme.js.map +1 -0
- package/dist/theme/tasty-config.d.ts +13 -0
- package/dist/theme/tasty-config.d.ts.map +1 -0
- package/dist/theme/tasty-config.js +16 -0
- package/dist/theme/tasty-config.js.map +1 -0
- package/dist/ui.d.ts +6 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +53 -0
- package/dist/ui.js.map +1 -0
- package/dist/versioning.d.ts +11 -0
- package/dist/versioning.d.ts.map +1 -0
- package/dist/versioning.js +38 -0
- package/dist/versioning.js.map +1 -0
- package/package.json +92 -0
- package/tasty.config.mjs +1 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { tasty } from "@tenphi/tasty";
|
|
2
|
+
import { customizeComponent } from "./customize-component.js";
|
|
3
|
+
import { configureCookbookStates } from "./tasty-states.js";
|
|
4
|
+
|
|
5
|
+
configureCookbookStates();
|
|
6
|
+
|
|
7
|
+
export const CardRoot = customizeComponent(
|
|
8
|
+
"Card",
|
|
9
|
+
tasty({
|
|
10
|
+
as: "article",
|
|
11
|
+
styles: {
|
|
12
|
+
display: "grid",
|
|
13
|
+
gap: "1.5x",
|
|
14
|
+
padding: "2.5x",
|
|
15
|
+
color: "#text",
|
|
16
|
+
textDecoration: "none",
|
|
17
|
+
border: true,
|
|
18
|
+
radius: "1cr",
|
|
19
|
+
fill: "#surface-2",
|
|
20
|
+
shadow: "0 1px 2px #shadow",
|
|
21
|
+
transition: "fill $transition, shadow $transition, translate $transition",
|
|
22
|
+
|
|
23
|
+
Heading2: {
|
|
24
|
+
$: "h2",
|
|
25
|
+
margin: "0",
|
|
26
|
+
},
|
|
27
|
+
Heading3: {
|
|
28
|
+
$: "h3",
|
|
29
|
+
margin: "0",
|
|
30
|
+
},
|
|
31
|
+
Paragraph: {
|
|
32
|
+
$: "p",
|
|
33
|
+
margin: "0",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
}),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
export const CardLink = tasty(CardRoot, {
|
|
40
|
+
as: "a",
|
|
41
|
+
styles: {
|
|
42
|
+
fill: {
|
|
43
|
+
"": "#surface-2",
|
|
44
|
+
":hover": "#surface-2-hover",
|
|
45
|
+
":active": "#surface-2-pressed",
|
|
46
|
+
},
|
|
47
|
+
shadow: {
|
|
48
|
+
"": "0 1px 2px #shadow",
|
|
49
|
+
":hover": "0 .75x 2x #shadow",
|
|
50
|
+
},
|
|
51
|
+
translate: {
|
|
52
|
+
"": "0",
|
|
53
|
+
":hover": "0 -1px",
|
|
54
|
+
":active": "0",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const previewElements = {
|
|
60
|
+
Caption: "figcaption",
|
|
61
|
+
Stage: "div",
|
|
62
|
+
Frame: "iframe",
|
|
63
|
+
Code: "details",
|
|
64
|
+
Summary: "summary",
|
|
65
|
+
Pre: "pre",
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const PreviewRoot = customizeComponent(
|
|
69
|
+
"Preview",
|
|
70
|
+
tasty({
|
|
71
|
+
as: "figure",
|
|
72
|
+
styles: {
|
|
73
|
+
margin: "0",
|
|
74
|
+
overflow: "clip",
|
|
75
|
+
color: "#text",
|
|
76
|
+
border: true,
|
|
77
|
+
radius: "1cr",
|
|
78
|
+
fill: "#surface",
|
|
79
|
+
shadow: "0 1px 2px #shadow",
|
|
80
|
+
|
|
81
|
+
Caption: {
|
|
82
|
+
padding: "1.5x 2x",
|
|
83
|
+
preset: "small",
|
|
84
|
+
fill: "#surface-2",
|
|
85
|
+
},
|
|
86
|
+
Stage: {
|
|
87
|
+
padding: "3x",
|
|
88
|
+
fill: "#surface",
|
|
89
|
+
},
|
|
90
|
+
Frame: {
|
|
91
|
+
display: "block",
|
|
92
|
+
width: "100%",
|
|
93
|
+
height: "min 18rem",
|
|
94
|
+
border: "0",
|
|
95
|
+
fill: "#surface",
|
|
96
|
+
},
|
|
97
|
+
Code: {
|
|
98
|
+
border: "1bw top #border",
|
|
99
|
+
},
|
|
100
|
+
Summary: {
|
|
101
|
+
padding: "1.5x 2x",
|
|
102
|
+
preset: "small",
|
|
103
|
+
fill: "#surface-2",
|
|
104
|
+
cursor: "pointer",
|
|
105
|
+
},
|
|
106
|
+
Pre: {
|
|
107
|
+
margin: "0",
|
|
108
|
+
radius: "0",
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
elements: previewElements,
|
|
112
|
+
}),
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
export const StepsRoot = customizeComponent(
|
|
116
|
+
"Steps",
|
|
117
|
+
tasty({
|
|
118
|
+
as: "ol",
|
|
119
|
+
styles: {
|
|
120
|
+
display: "grid",
|
|
121
|
+
gap: "2x",
|
|
122
|
+
padding: "4x left",
|
|
123
|
+
border: "1bw left #border",
|
|
124
|
+
|
|
125
|
+
Item: {
|
|
126
|
+
$: "> li",
|
|
127
|
+
padding: "1x left",
|
|
128
|
+
},
|
|
129
|
+
Marker: {
|
|
130
|
+
$: "> li::marker",
|
|
131
|
+
color: "#accent-text",
|
|
132
|
+
preset: "strong",
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
}),
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
export const TabsRoot = customizeComponent(
|
|
139
|
+
"Tabs",
|
|
140
|
+
tasty({
|
|
141
|
+
as: "div",
|
|
142
|
+
styles: {
|
|
143
|
+
margin: "2x 0",
|
|
144
|
+
border: true,
|
|
145
|
+
radius: "1cr",
|
|
146
|
+
overflow: "clip",
|
|
147
|
+
fill: "#surface",
|
|
148
|
+
List: {
|
|
149
|
+
padding: "1x",
|
|
150
|
+
display: "flex",
|
|
151
|
+
gap: "1x",
|
|
152
|
+
overflow: "auto",
|
|
153
|
+
fill: "#surface-2",
|
|
154
|
+
},
|
|
155
|
+
Button: {
|
|
156
|
+
$: "> [data-tabs-list] > button",
|
|
157
|
+
margin: "0",
|
|
158
|
+
preset: "navigation",
|
|
159
|
+
padding: "1x 2x",
|
|
160
|
+
border: "0",
|
|
161
|
+
radius: "1r",
|
|
162
|
+
color: "#text",
|
|
163
|
+
fill: "#clear",
|
|
164
|
+
cursor: "pointer",
|
|
165
|
+
whiteSpace: "nowrap",
|
|
166
|
+
},
|
|
167
|
+
SelectedButton: {
|
|
168
|
+
$: '> [data-tabs-list] > button[aria-selected="true"]',
|
|
169
|
+
color: "#accent-surface-text",
|
|
170
|
+
fill: "#accent-surface",
|
|
171
|
+
},
|
|
172
|
+
FocusedButton: {
|
|
173
|
+
$: "> [data-tabs-list] > button:focus-visible",
|
|
174
|
+
outline: "2px #focus / -2px",
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
elements: { List: "div" },
|
|
178
|
+
}),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
export const TabRoot = customizeComponent(
|
|
182
|
+
"Tab",
|
|
183
|
+
tasty({
|
|
184
|
+
as: "section",
|
|
185
|
+
styles: {
|
|
186
|
+
margin: "0",
|
|
187
|
+
padding: "2x",
|
|
188
|
+
Heading: { margin: "0 0 1x", preset: "h3" },
|
|
189
|
+
Hidden: { $: "&[hidden]", hide: true },
|
|
190
|
+
HiddenHeading: { $: "> [data-tab-heading][hidden]", hide: true },
|
|
191
|
+
},
|
|
192
|
+
elements: { Heading: "h3" },
|
|
193
|
+
}),
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
export const CalloutRoot = customizeComponent(
|
|
197
|
+
"Callout",
|
|
198
|
+
tasty({
|
|
199
|
+
as: "aside",
|
|
200
|
+
styles: {
|
|
201
|
+
"#callout-border": "#info",
|
|
202
|
+
"#callout-text": "#info-text",
|
|
203
|
+
"#callout-surface": "#info-surface",
|
|
204
|
+
margin: "2x 0",
|
|
205
|
+
padding: "2x 3x",
|
|
206
|
+
border: "1bw #callout-border",
|
|
207
|
+
radius: "1cr",
|
|
208
|
+
fill: "#callout-surface",
|
|
209
|
+
color: "#text",
|
|
210
|
+
Title: {
|
|
211
|
+
preset: "body / strong",
|
|
212
|
+
color: "#callout-text",
|
|
213
|
+
margin: "0 0 1x",
|
|
214
|
+
},
|
|
215
|
+
Body: { color: "#text", margin: "0" },
|
|
216
|
+
Tip: {
|
|
217
|
+
$: '&[data-kind="tip"]',
|
|
218
|
+
"#callout-border": "#success",
|
|
219
|
+
"#callout-text": "#success-text",
|
|
220
|
+
"#callout-surface": "#success-surface",
|
|
221
|
+
},
|
|
222
|
+
Caution: {
|
|
223
|
+
$: '&[data-kind="caution"]',
|
|
224
|
+
"#callout-border": "#warning",
|
|
225
|
+
"#callout-text": "#warning-text",
|
|
226
|
+
"#callout-surface": "#warning-surface",
|
|
227
|
+
},
|
|
228
|
+
Danger: {
|
|
229
|
+
$: '&[data-kind="danger"]',
|
|
230
|
+
"#callout-border": "#danger",
|
|
231
|
+
"#callout-text": "#danger-text",
|
|
232
|
+
"#callout-surface": "#danger-surface",
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
elements: { Title: "p", Body: "div" },
|
|
236
|
+
}),
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
export const CodeGroupRoot = customizeComponent(
|
|
240
|
+
"CodeGroup",
|
|
241
|
+
tasty({
|
|
242
|
+
as: "div",
|
|
243
|
+
styles: {
|
|
244
|
+
Caption: { preset: "small", color: "#text-soft", margin: "0 0 1x" },
|
|
245
|
+
Pre: { $: "pre" },
|
|
246
|
+
Code: {
|
|
247
|
+
$: "pre code",
|
|
248
|
+
preset: "code",
|
|
249
|
+
color: "#syntax-text",
|
|
250
|
+
whiteSpace: "pre",
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
elements: { Caption: "p", Pre: "pre", Code: "code" },
|
|
254
|
+
}),
|
|
255
|
+
);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { messages } from "../localization.js";
|
|
3
|
+
import { content } from "virtual:cookbook/config";
|
|
4
|
+
import type { DocsRoute, SiteVersion } from "@tenphi/docs";
|
|
5
|
+
import { versionLinks } from "../versioning.js";
|
|
6
|
+
import { VersionSwitcherRoot } from "./LayoutComponents.js";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
versions: SiteVersion[];
|
|
10
|
+
routes: DocsRoute[];
|
|
11
|
+
currentPath: string;
|
|
12
|
+
base: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { current, links } = versionLinks(
|
|
16
|
+
Astro.props.versions,
|
|
17
|
+
Astro.props.routes,
|
|
18
|
+
Astro.props.currentPath,
|
|
19
|
+
Astro.props.base,
|
|
20
|
+
content,
|
|
21
|
+
);
|
|
22
|
+
const ui = messages(Astro.locals.cookbookRoute?.lang ?? "en", content);
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<VersionSwitcherRoot data-tasty-anatomy="VersionSwitcher">
|
|
26
|
+
<summary
|
|
27
|
+
aria-label={`${ui.documentationVersion}: ${current?.label ?? ui.versions}`}
|
|
28
|
+
>{current?.label ?? ui.versions}</summary
|
|
29
|
+
>
|
|
30
|
+
<nav aria-label={ui.documentationVersions}>
|
|
31
|
+
{
|
|
32
|
+
links.map((version) => (
|
|
33
|
+
<a
|
|
34
|
+
href={version.href}
|
|
35
|
+
aria-current={
|
|
36
|
+
version.routeBase === current?.routeBase ? "page" : undefined
|
|
37
|
+
}
|
|
38
|
+
>
|
|
39
|
+
{version.label}
|
|
40
|
+
</a>
|
|
41
|
+
))
|
|
42
|
+
}
|
|
43
|
+
</nav>
|
|
44
|
+
</VersionSwitcherRoot>
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { loadPreferences, storePreferences } from "./appearance-preferences.js";
|
|
3
|
+
|
|
4
|
+
function storage(values: Record<string, string> = {}) {
|
|
5
|
+
return {
|
|
6
|
+
getItem: (key: string) => values[key] ?? null,
|
|
7
|
+
setItem: (key: string, value: string) => {
|
|
8
|
+
values[key] = value;
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe("appearance preferences", () => {
|
|
14
|
+
it("restores existing independent scheme and contrast choices", () => {
|
|
15
|
+
const saved = storage({
|
|
16
|
+
"cookbook-theme": "dark",
|
|
17
|
+
"cookbook-appearance": JSON.stringify({ contrast: "more" }),
|
|
18
|
+
});
|
|
19
|
+
expect(loadPreferences(saved)).toEqual({
|
|
20
|
+
scheme: "dark",
|
|
21
|
+
contrast: "more",
|
|
22
|
+
});
|
|
23
|
+
storePreferences({ scheme: "light", contrast: "more" }, saved);
|
|
24
|
+
expect(loadPreferences(saved)).toEqual({
|
|
25
|
+
scheme: "light",
|
|
26
|
+
contrast: "more",
|
|
27
|
+
});
|
|
28
|
+
storePreferences({ scheme: "light", contrast: "normal" }, saved);
|
|
29
|
+
expect(loadPreferences(saved)).toEqual({
|
|
30
|
+
scheme: "light",
|
|
31
|
+
contrast: "normal",
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("persists Auto using Cookbook storage without losing other preferences", () => {
|
|
36
|
+
const saved = storage({
|
|
37
|
+
"cookbook-appearance": JSON.stringify({ contrast: "more", other: true }),
|
|
38
|
+
});
|
|
39
|
+
storePreferences({ scheme: "auto", contrast: "system" }, saved);
|
|
40
|
+
expect(saved.getItem("cookbook-theme")).toBe("");
|
|
41
|
+
expect(JSON.parse(saved.getItem("cookbook-appearance")!)).toEqual({
|
|
42
|
+
contrast: "system",
|
|
43
|
+
other: true,
|
|
44
|
+
});
|
|
45
|
+
expect(loadPreferences(saved)).toEqual({
|
|
46
|
+
scheme: "auto",
|
|
47
|
+
contrast: "system",
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it.each(["{broken", "null", "[]", '"more"', '{"contrast":"invalid"}'])(
|
|
52
|
+
"recovers from malformed contrast data: %s",
|
|
53
|
+
(value) => {
|
|
54
|
+
const saved = storage({
|
|
55
|
+
"cookbook-theme": "light",
|
|
56
|
+
"cookbook-appearance": value,
|
|
57
|
+
});
|
|
58
|
+
expect(loadPreferences(saved)).toEqual({
|
|
59
|
+
scheme: "light",
|
|
60
|
+
contrast: "system",
|
|
61
|
+
});
|
|
62
|
+
storePreferences({ scheme: "dark", contrast: "more" }, saved);
|
|
63
|
+
expect(loadPreferences(saved)).toEqual({
|
|
64
|
+
scheme: "dark",
|
|
65
|
+
contrast: "more",
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
it("uses system defaults for unknown values or unavailable storage", () => {
|
|
71
|
+
expect(loadPreferences(storage({ "cookbook-theme": "invalid" }))).toEqual({
|
|
72
|
+
scheme: "auto",
|
|
73
|
+
contrast: "system",
|
|
74
|
+
});
|
|
75
|
+
const unavailable = {
|
|
76
|
+
getItem: () => {
|
|
77
|
+
throw new Error("Storage unavailable");
|
|
78
|
+
},
|
|
79
|
+
setItem: () => {
|
|
80
|
+
throw new Error("Storage unavailable");
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
expect(loadPreferences(unavailable)).toEqual({
|
|
84
|
+
scheme: "auto",
|
|
85
|
+
contrast: "system",
|
|
86
|
+
});
|
|
87
|
+
expect(() =>
|
|
88
|
+
storePreferences({ scheme: "dark", contrast: "more" }, unavailable),
|
|
89
|
+
).not.toThrow();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
type Scheme = "light" | "dark" | "auto";
|
|
2
|
+
type Contrast = "normal" | "more" | "system";
|
|
3
|
+
type Preferences = { scheme: Scheme; contrast: Contrast };
|
|
4
|
+
type PreferenceStorage = Pick<Storage, "getItem" | "setItem">;
|
|
5
|
+
|
|
6
|
+
export const parseScheme = (value: unknown): Scheme =>
|
|
7
|
+
value === "light" || value === "dark" ? value : "auto";
|
|
8
|
+
|
|
9
|
+
export const parseContrast = (value: unknown): Contrast =>
|
|
10
|
+
value === "normal" || value === "more" ? value : "system";
|
|
11
|
+
|
|
12
|
+
function storedAppearance(storage: PreferenceStorage): Record<string, unknown> {
|
|
13
|
+
try {
|
|
14
|
+
const value: unknown = JSON.parse(
|
|
15
|
+
storage.getItem("cookbook-appearance") ?? "{}",
|
|
16
|
+
);
|
|
17
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
18
|
+
? (value as Record<string, unknown>)
|
|
19
|
+
: {};
|
|
20
|
+
} catch {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function loadPreferences(storage?: PreferenceStorage): Preferences {
|
|
26
|
+
const preferences: Preferences = { scheme: "auto", contrast: "system" };
|
|
27
|
+
try {
|
|
28
|
+
storage ??= localStorage;
|
|
29
|
+
preferences.scheme = parseScheme(storage.getItem("cookbook-theme"));
|
|
30
|
+
preferences.contrast = parseContrast(storedAppearance(storage).contrast);
|
|
31
|
+
} catch {
|
|
32
|
+
// System defaults work even when browser storage is unavailable.
|
|
33
|
+
}
|
|
34
|
+
return preferences;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function storePreferences(
|
|
38
|
+
value: Preferences,
|
|
39
|
+
storage?: PreferenceStorage,
|
|
40
|
+
): void {
|
|
41
|
+
try {
|
|
42
|
+
storage ??= localStorage;
|
|
43
|
+
storage.setItem(
|
|
44
|
+
"cookbook-theme",
|
|
45
|
+
value.scheme === "auto" ? "" : value.scheme,
|
|
46
|
+
);
|
|
47
|
+
storage.setItem(
|
|
48
|
+
"cookbook-appearance",
|
|
49
|
+
JSON.stringify({
|
|
50
|
+
...storedAppearance(storage),
|
|
51
|
+
contrast: value.contrast,
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
} catch {
|
|
55
|
+
// The current page still updates when preferences cannot be persisted.
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
configureComponentStyles,
|
|
4
|
+
resolveLegacyAnatomyStyles,
|
|
5
|
+
resolveComponentStyleOverride,
|
|
6
|
+
resolveComponentStyles,
|
|
7
|
+
unusedCustomStyleNames,
|
|
8
|
+
} from "./component-styles.js";
|
|
9
|
+
|
|
10
|
+
const defaults = {
|
|
11
|
+
color: "#text",
|
|
12
|
+
padding: "2x",
|
|
13
|
+
Label: { color: "#text-soft", fontWeight: 500 },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
afterEach(() => configureComponentStyles(undefined));
|
|
17
|
+
|
|
18
|
+
describe("component style configuration", () => {
|
|
19
|
+
it("extends defaults with a plain Tasty style object", () => {
|
|
20
|
+
configureComponentStyles({
|
|
21
|
+
Card: { padding: "3x", Label: { color: "#accent-text" } },
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
expect(resolveComponentStyles("Card", defaults)).toEqual({
|
|
25
|
+
color: "#text",
|
|
26
|
+
padding: "3x",
|
|
27
|
+
Label: { color: "#accent-text", fontWeight: 500 },
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("returns the user override for Tasty component composition", () => {
|
|
32
|
+
expect(resolveComponentStyleOverride("Card")).toBeUndefined();
|
|
33
|
+
|
|
34
|
+
configureComponentStyles({ Card: { padding: "3x" } });
|
|
35
|
+
expect(resolveComponentStyleOverride("Card")).toEqual({
|
|
36
|
+
padding: "3x",
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("merges configuration into bridged component style trees", () => {
|
|
41
|
+
configureComponentStyles({
|
|
42
|
+
MarkdownTable: { Table: { radius: "0" } },
|
|
43
|
+
Mermaid: { Diagram: { maxInlineSize: "90%" } },
|
|
44
|
+
MobileTableOfContents: { LinkLabel: { whiteSpace: "normal" } },
|
|
45
|
+
Sidebar: { Link: { padding: "1x" } },
|
|
46
|
+
TableOfContents: { Link: { color: "#text" } },
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(
|
|
50
|
+
resolveComponentStyles("MarkdownTable", {
|
|
51
|
+
Table: { border: true, radius: "1cr" },
|
|
52
|
+
Cell: { padding: "1.5x 2x" },
|
|
53
|
+
}),
|
|
54
|
+
).toEqual({
|
|
55
|
+
Table: { border: true, radius: "0" },
|
|
56
|
+
Cell: { padding: "1.5x 2x" },
|
|
57
|
+
});
|
|
58
|
+
expect(
|
|
59
|
+
resolveComponentStyles("Mermaid", {
|
|
60
|
+
Diagram: { display: "block", maxInlineSize: "100%" },
|
|
61
|
+
}),
|
|
62
|
+
).toEqual({
|
|
63
|
+
Diagram: { display: "block", maxInlineSize: "90%" },
|
|
64
|
+
});
|
|
65
|
+
expect(
|
|
66
|
+
resolveComponentStyles("Sidebar", {
|
|
67
|
+
display: "block",
|
|
68
|
+
Content: { gap: "3x" },
|
|
69
|
+
Link: { color: "#text-soft", padding: "2x" },
|
|
70
|
+
}),
|
|
71
|
+
).toEqual({
|
|
72
|
+
display: "block",
|
|
73
|
+
Content: { gap: "3x" },
|
|
74
|
+
Link: { color: "#text-soft", padding: "1x" },
|
|
75
|
+
});
|
|
76
|
+
expect(
|
|
77
|
+
resolveComponentStyles("TableOfContents", {
|
|
78
|
+
paddingBlockStart: "5x",
|
|
79
|
+
Heading: { color: "#text" },
|
|
80
|
+
Link: { color: "#text-muted", preset: "small" },
|
|
81
|
+
}),
|
|
82
|
+
).toEqual({
|
|
83
|
+
paddingBlockStart: "5x",
|
|
84
|
+
Heading: { color: "#text" },
|
|
85
|
+
Link: { color: "#text", preset: "small" },
|
|
86
|
+
});
|
|
87
|
+
expect(
|
|
88
|
+
resolveComponentStyles("MobileTableOfContents", {
|
|
89
|
+
display: "grid",
|
|
90
|
+
Item: { minInlineSize: "0" },
|
|
91
|
+
LinkLabel: { overflow: "hidden", whiteSpace: "nowrap" },
|
|
92
|
+
}),
|
|
93
|
+
).toEqual({
|
|
94
|
+
display: "grid",
|
|
95
|
+
Item: { minInlineSize: "0" },
|
|
96
|
+
LinkLabel: { overflow: "hidden", whiteSpace: "normal" },
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("keeps custom anatomy styles on the compatibility bridge", () => {
|
|
101
|
+
expect(
|
|
102
|
+
resolveLegacyAnatomyStyles({
|
|
103
|
+
Footer: { color: "#text-muted" },
|
|
104
|
+
ThemeSelect: { padding: "1x" },
|
|
105
|
+
ProductBadge: { color: "#accent-text" },
|
|
106
|
+
}),
|
|
107
|
+
).toEqual({
|
|
108
|
+
'[data-tasty-anatomy="ProductBadge"]': { color: "#accent-text" },
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("reports only custom style names without a component or anatomy match", () => {
|
|
113
|
+
const styles = {
|
|
114
|
+
ProjectBadge: { color: "#accent-text" },
|
|
115
|
+
ProjectNote: { color: "#text" },
|
|
116
|
+
ProjectBadg: { color: "#text-muted" },
|
|
117
|
+
};
|
|
118
|
+
configureComponentStyles(styles);
|
|
119
|
+
resolveComponentStyles("ProjectBadge", { color: "#text" });
|
|
120
|
+
|
|
121
|
+
expect(unusedCustomStyleNames(styles, new Set(["ProjectNote"]))).toEqual([
|
|
122
|
+
"ProjectBadg",
|
|
123
|
+
]);
|
|
124
|
+
configureComponentStyles(styles);
|
|
125
|
+
expect(unusedCustomStyleNames(styles, new Set())).toEqual(
|
|
126
|
+
Object.keys(styles),
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { mergeStyles, type Styles } from "@tenphi/tasty/core";
|
|
2
|
+
import { COOKBOOK_COMPONENT_NAMES } from "@tenphi/docs";
|
|
3
|
+
|
|
4
|
+
// Astro can load the integration and renderer through separate module graphs.
|
|
5
|
+
// Keep their component configuration on the shared process global.
|
|
6
|
+
const sharedConfiguration = globalThis as typeof globalThis & {
|
|
7
|
+
__tenphiCookbookComponentStyles?: Record<string, Styles | undefined>;
|
|
8
|
+
__tenphiCookbookUsedComponentStyles?: Set<string>;
|
|
9
|
+
};
|
|
10
|
+
const cookbookComponentNames = new Set<string>(COOKBOOK_COMPONENT_NAMES);
|
|
11
|
+
|
|
12
|
+
export function configureComponentStyles(
|
|
13
|
+
styles: Record<string, Styles | undefined> | undefined,
|
|
14
|
+
): void {
|
|
15
|
+
sharedConfiguration.__tenphiCookbookComponentStyles = styles ?? {};
|
|
16
|
+
sharedConfiguration.__tenphiCookbookUsedComponentStyles = new Set();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Merge a partial theme override into a built-in or consumer style tree. */
|
|
20
|
+
export function resolveComponentStyles(
|
|
21
|
+
name: string,
|
|
22
|
+
baseStyles: Styles,
|
|
23
|
+
): Styles {
|
|
24
|
+
sharedConfiguration.__tenphiCookbookUsedComponentStyles?.add(name);
|
|
25
|
+
const configuredStyles = sharedConfiguration
|
|
26
|
+
.__tenphiCookbookComponentStyles?.[name] as Styles | undefined;
|
|
27
|
+
return configuredStyles
|
|
28
|
+
? mergeStyles(baseStyles, configuredStyles as Styles)
|
|
29
|
+
: baseStyles;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Find custom names without a component style resolver or rendered anatomy. */
|
|
33
|
+
export function unusedCustomStyleNames(
|
|
34
|
+
styles: Record<string, Styles> | undefined,
|
|
35
|
+
anatomyNames: ReadonlySet<string>,
|
|
36
|
+
): string[] {
|
|
37
|
+
return Object.keys(styles ?? {}).filter(
|
|
38
|
+
(name) =>
|
|
39
|
+
!sharedConfiguration.__tenphiCookbookUsedComponentStyles?.has(name) &&
|
|
40
|
+
!anatomyNames.has(name),
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function resolveComponentStyleOverride(
|
|
45
|
+
name: string,
|
|
46
|
+
): Styles | undefined {
|
|
47
|
+
return sharedConfiguration.__tenphiCookbookComponentStyles?.[name] as
|
|
48
|
+
Styles | undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Preserve custom anatomy names from the pre-component style API. */
|
|
52
|
+
export function resolveLegacyAnatomyStyles(
|
|
53
|
+
styles: Record<string, Styles | undefined> | undefined,
|
|
54
|
+
): Record<string, Styles> | undefined {
|
|
55
|
+
if (!styles) return undefined;
|
|
56
|
+
const entries = Object.entries(styles)
|
|
57
|
+
.filter(
|
|
58
|
+
(entry): entry is [string, Styles] =>
|
|
59
|
+
!cookbookComponentNames.has(entry[0]) && entry[1] !== undefined,
|
|
60
|
+
)
|
|
61
|
+
.map(([name, value]) => [`[data-tasty-anatomy="${name}"]`, value]);
|
|
62
|
+
return entries.length
|
|
63
|
+
? (Object.fromEntries(entries) as Record<string, Styles>)
|
|
64
|
+
: undefined;
|
|
65
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { tasty } from "@tenphi/tasty";
|
|
2
|
+
import { resolveComponentStyleOverride } from "./component-styles.js";
|
|
3
|
+
|
|
4
|
+
export function customizeComponent(name, base) {
|
|
5
|
+
const override = resolveComponentStyleOverride(name);
|
|
6
|
+
if (!override) return base;
|
|
7
|
+
return tasty(base, { styles: override });
|
|
8
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/components/props.d.ts
|
|
2
|
+
interface CardProps {
|
|
3
|
+
title?: string;
|
|
4
|
+
href?: string;
|
|
5
|
+
}
|
|
6
|
+
interface LogoProps {
|
|
7
|
+
label?: string;
|
|
8
|
+
decorative?: boolean;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
interface PreviewProps {
|
|
12
|
+
title: string;
|
|
13
|
+
html?: string;
|
|
14
|
+
css?: string;
|
|
15
|
+
javascript?: string;
|
|
16
|
+
}
|
|
17
|
+
type StepsProps = Record<string, never>;
|
|
18
|
+
interface TabsProps {
|
|
19
|
+
label?: string;
|
|
20
|
+
}
|
|
21
|
+
interface TabProps {
|
|
22
|
+
label: string;
|
|
23
|
+
}
|
|
24
|
+
interface CalloutProps {
|
|
25
|
+
title?: string;
|
|
26
|
+
kind?: "note" | "tip" | "caution" | "danger";
|
|
27
|
+
}
|
|
28
|
+
interface CodeGroupProps {
|
|
29
|
+
label?: string;
|
|
30
|
+
items: Array<{
|
|
31
|
+
label: string;
|
|
32
|
+
code: string;
|
|
33
|
+
language?: string;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { CalloutProps, CardProps, CodeGroupProps, LogoProps, PreviewProps, StepsProps, TabProps, TabsProps };
|
|
38
|
+
//# sourceMappingURL=props.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|