@yaakdoc/react 0.0.0-stage → 0.2.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 +232 -2
- package/dist/client.d.ts +9 -0
- package/dist/client.js +26 -0
- package/dist/color-mode-B-vJCVkP.js +77 -0
- package/dist/index-CoQ7pnuB.d.ts +471 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +551 -0
- package/dist/island-BWyuL4_6.d.ts +29 -0
- package/dist/islands.d.ts +170 -0
- package/dist/islands.js +152 -0
- package/dist/llm-Ddn5hWnS.js +205 -0
- package/dist/run-in-yaak-CMMcDS7S.js +164 -0
- package/dist/server.d.ts +26 -0
- package/dist/server.js +139 -0
- package/dist/site-DyK7ZcqQ.js +371 -0
- package/dist/theme.d.ts +45 -0
- package/dist/theme.js +7 -0
- package/dist/types-BY2lOvlK.d.ts +101 -0
- package/package.json +85 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 József Sallai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,233 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @yaakdoc/react
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Unstyled React components, hooks and the island runtime for building [yaakdoc](https://github.com/jozsefsallai/yaakdoc#readme) themes.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm i @yaakdoc/react react react-dom
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Anatomy of a theme
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
my-theme/
|
|
13
|
+
theme.ts manifest, loaded by yaakdoc in Node
|
|
14
|
+
src/entry.tsx server entry: { Layout, components?, Head? }
|
|
15
|
+
src/client.ts client entry: the islands to hydrate
|
|
16
|
+
src/styles.css styles (any Vite-supported CSS: plain CSS, Tailwind, ...)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// theme.ts – keep this file free of React imports
|
|
21
|
+
import { defineTheme } from "@yaakdoc/react/theme";
|
|
22
|
+
import { z } from "zod";
|
|
23
|
+
|
|
24
|
+
export default defineTheme({
|
|
25
|
+
name: "my-theme",
|
|
26
|
+
entry: "./src/entry.tsx",
|
|
27
|
+
|
|
28
|
+
client: "./src/client.ts",
|
|
29
|
+
styles: ["./src/styles.css"],
|
|
30
|
+
|
|
31
|
+
highlight: {
|
|
32
|
+
light: "github-light",
|
|
33
|
+
dark: "github-dark",
|
|
34
|
+
}, // Shiki themes
|
|
35
|
+
|
|
36
|
+
options: z.object({
|
|
37
|
+
accent: z.string().default("violet"),
|
|
38
|
+
}), // validates `themeOptions`
|
|
39
|
+
|
|
40
|
+
vitePlugins: async () => [], // e.g. Tailwind
|
|
41
|
+
|
|
42
|
+
eject: {
|
|
43
|
+
files: ["theme.ts", "src/**"],
|
|
44
|
+
dependencies: ["@yaakdoc/react"],
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Theme source is processed by Vite, so TSX works without a build step. A theme package can ship its source as-is.
|
|
50
|
+
|
|
51
|
+
## The layout
|
|
52
|
+
|
|
53
|
+
`entry.tsx` default-exports a `ThemeRuntime`. The `Layout` renders the page `<body>`; yaakdoc renders `<html>`/`<head>` (meta tags, assets, branding CSS variables, the color mode script).
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { Content, Footer, Logo, NavTree, type ThemeRuntime } from "@yaakdoc/react";
|
|
57
|
+
import { ColorModeToggle, NavFilter } from "@yaakdoc/react/islands";
|
|
58
|
+
|
|
59
|
+
const theme: ThemeRuntime = {
|
|
60
|
+
Layout: ({ page }) => (
|
|
61
|
+
<div className="layout">
|
|
62
|
+
<aside id="sidebar">
|
|
63
|
+
<Logo />
|
|
64
|
+
<NavFilter target="#sidebar [data-yd=nav]" />
|
|
65
|
+
<NavTree />
|
|
66
|
+
</aside>
|
|
67
|
+
|
|
68
|
+
<ColorModeToggle />
|
|
69
|
+
|
|
70
|
+
<Content />
|
|
71
|
+
|
|
72
|
+
<Footer />
|
|
73
|
+
</div>
|
|
74
|
+
),
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default theme;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`page.kind` is `index`, `folder`, `request` or `not-found`. In `pages: "single"` mode, the index page contains everything. In `per-request` mode each folder and request has its own page. `<Content />` handles both, but you can compose the pieces yourself.
|
|
81
|
+
|
|
82
|
+
## Data hooks
|
|
83
|
+
|
|
84
|
+
| Hook | Returns |
|
|
85
|
+
| --- | --- |
|
|
86
|
+
| `useDocument()` | The `PreparedDocument`: `tree`, `requests`, `folders`, `workspace`, `environment`, `variables`. |
|
|
87
|
+
| `useSite()` | Site data: title, description, logo, colors, links, `llms` settings, `base`, `pages`, ... |
|
|
88
|
+
| `usePage()` | The current page (`kind`, `id`, `path`, `markdownUrl`, ...). |
|
|
89
|
+
| `useRequest(id)` / `useFolder(id)` | A single prepared request or folder. |
|
|
90
|
+
| `useThemeOptions<T>()` | Your validated `themeOptions`. |
|
|
91
|
+
|
|
92
|
+
A prepared request already contains everything a theme needs:
|
|
93
|
+
|
|
94
|
+
- `href` and `markdownUrl`
|
|
95
|
+
- `resolvedUrl` (variables resolved, secrets as `{{placeholders}}`)
|
|
96
|
+
- `descriptionHtml` (sanitized Markdown)
|
|
97
|
+
- `bodyHtml`, `examples[].html` and `responses[]` (Shiki-highlighted example responses with `status`, `label`, `language`)
|
|
98
|
+
- headers, params and auth, each with `inheritedFrom` information
|
|
99
|
+
|
|
100
|
+
## Components
|
|
101
|
+
|
|
102
|
+
All components are unstyled. They render semantic markup with stable `data-yd="..."` attributes and accept `className`:
|
|
103
|
+
|
|
104
|
+
- **Page structure:** `Content`, `Overview`, `TreeContent`, `FolderDoc`, `ChildLinks`, `NotFound`, `Heading`
|
|
105
|
+
- **Requests:** `RequestDoc`, `RequestBody`, `MethodBadge`, `RequestUrl`, `AuthInfo`, `KeyValueTable`, `Variables`
|
|
106
|
+
- **Code and Markdown:** `CodeBlock`, `CodeExamples`, `ResponseExamples`, `Markdown`, `Html`
|
|
107
|
+
- **Environments:** `EnvironmentVariants`, `EnvironmentSelect`
|
|
108
|
+
- **Navigation:** `NavTree`, `Breadcrumbs`
|
|
109
|
+
- **Site chrome:** `Logo`, `SiteLinks`, `Footer`, `PageActions`, `RunInYaak`, `BrandingStyles`
|
|
110
|
+
|
|
111
|
+
A few attributes are useful when styling with plain CSS:
|
|
112
|
+
|
|
113
|
+
- `[data-method="get"]` on method badges.
|
|
114
|
+
- `[data-yd="url-variable"]` on the `{{variables}}` inside a `RequestUrl`.
|
|
115
|
+
- `[data-yd-section]` on documented sections, which the scroll spy uses.
|
|
116
|
+
- `aria-current` on the active navigation link.
|
|
117
|
+
|
|
118
|
+
### Overriding components
|
|
119
|
+
|
|
120
|
+
Every component above can be replaced everywhere, including where other components render it, by registering an override under its name:
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
const theme: ThemeRuntime = {
|
|
124
|
+
Layout,
|
|
125
|
+
components: {
|
|
126
|
+
// The override receives the same props, plus `Default` (the original component).
|
|
127
|
+
MethodBadge: ({ request, Default }) => <Default request={request} className="badge" />,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Use `overridable(name, Component)` to make your own components overridable.
|
|
133
|
+
|
|
134
|
+
`<Content />` decides what each page shows (everything, a folder, a request or the 404 page). A theme can keep that logic and only restyle the parts. Kiseki, for example, renders `<Content />` and overrides `Overview`, `FolderDoc`, `RequestDoc`, `ChildLinks` and `NotFound`.
|
|
135
|
+
|
|
136
|
+
### Helpers
|
|
137
|
+
|
|
138
|
+
These shared helpers keep themes consistent with the built-in components:
|
|
139
|
+
|
|
140
|
+
| Helper | Purpose |
|
|
141
|
+
| --- | --- |
|
|
142
|
+
| `usePageActions(markdownUrl)` | Data for "Copy page/section" actions (`null` when disabled). |
|
|
143
|
+
| `copyActionLabel(level)` | "Copy page" or "Copy section", depending on the heading level. |
|
|
144
|
+
| `useRunInYaak()` | Name and export URL for the "Run in Yaak" button (`null` when disabled). |
|
|
145
|
+
| `toAbsoluteUrl(site, path)` | Absolute URL of a site path, when `branding.url` is set. |
|
|
146
|
+
| `tabGroupIds`, `tabPanelProps`, `visibleTabPanelSelector` | Ids, panel props and copy targets for server-rendered panels controlled by `TabList`. |
|
|
147
|
+
| `methodLabel` (and `grpcMethodPath`, `formFieldValue` from `@yaakdoc/core`) | Display values for methods, gRPC calls and form fields. |
|
|
148
|
+
|
|
149
|
+
## Branding
|
|
150
|
+
|
|
151
|
+
Users' branding settings arrive as CSS custom properties on `:root`:
|
|
152
|
+
|
|
153
|
+
- `--yd-color-primary`, `--yd-color-accent`, `--yd-color-background` and `--yd-color-foreground`, with per-mode values under `[data-theme="light"]` and `[data-theme="dark"]`
|
|
154
|
+
- `--yd-font-sans` and `--yd-font-mono`
|
|
155
|
+
|
|
156
|
+
Use them with fallbacks:
|
|
157
|
+
|
|
158
|
+
```css
|
|
159
|
+
:root {
|
|
160
|
+
--primary: var(--yd-color-primary, #6d28d9);
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The color mode is applied before first paint as `data-theme="light|dark"` on `<html>`, and `color-scheme` is set accordingly. Without JavaScript, follow `prefers-color-scheme`. Highlighted code uses Shiki's dual themes. Pick the right variant with:
|
|
165
|
+
|
|
166
|
+
```css
|
|
167
|
+
.shiki,
|
|
168
|
+
.shiki span {
|
|
169
|
+
color: light-dark(var(--shiki-light), var(--shiki-dark));
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Islands
|
|
174
|
+
|
|
175
|
+
Pages are static HTML. Interactive parts are **islands**: components that are rendered on the server and hydrated individually on the client.
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
import { createIsland, useCopy } from "@yaakdoc/react/islands";
|
|
179
|
+
|
|
180
|
+
export const CopyLink = createIsland("my-theme:copy-link", ({ href }: { href: string }) => {
|
|
181
|
+
const { copied, copy } = useCopy();
|
|
182
|
+
return <button onClick={() => copy({ text: href })}>{copied ? "Copied" : "Copy link"}</button>;
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
// src/client.ts
|
|
188
|
+
import { builtinIslands } from "@yaakdoc/react/islands";
|
|
189
|
+
import { CopyLink } from "./islands";
|
|
190
|
+
|
|
191
|
+
export default [...builtinIslands, CopyLink];
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Islands follow two rules:
|
|
195
|
+
|
|
196
|
+
- **Props must be JSON-serializable.** They're embedded in the HTML.
|
|
197
|
+
- **Islands can't use the data hooks**, because they're hydrated in isolation. Pass data in through props.
|
|
198
|
+
|
|
199
|
+
Islands are rendered as isolated roots on the server too, so `useId()` (and libraries such as Radix UI) produce matching ids when they hydrate.
|
|
200
|
+
|
|
201
|
+
**Built-in islands:**
|
|
202
|
+
|
|
203
|
+
- `ColorModeToggle`
|
|
204
|
+
- `CopyButton`
|
|
205
|
+
- `EnvironmentSwitcher`
|
|
206
|
+
- `TabList`, which controls server-rendered tab panels and keeps the chosen language in sync across the page
|
|
207
|
+
- `NavToggle`
|
|
208
|
+
- `NavFilter`
|
|
209
|
+
- `ScrollSpy`
|
|
210
|
+
- `OpenInLink`
|
|
211
|
+
- `RunInYaakLink`
|
|
212
|
+
|
|
213
|
+
**Headless hooks** for building your own: `useColorMode`, `useCopy`, `useTabs`, `useNavToggle`, `useScrollSpy`, `useEnvironment`, `useRunInYaakHref`, plus `openInUrl()` and `runInYaakUrl()` for "Open in ChatGPT/Claude" links.
|
|
214
|
+
|
|
215
|
+
## Environments
|
|
216
|
+
|
|
217
|
+
When the config lists several `environments`, environment-dependent data is prepared per environment:
|
|
218
|
+
|
|
219
|
+
- `request.variants` holds `resolvedUrl` and `examples` for each environment.
|
|
220
|
+
- `document.variableSets` holds the variables table for each environment.
|
|
221
|
+
- `document.environments` lists the options, default first.
|
|
222
|
+
|
|
223
|
+
Render the variants with `<EnvironmentVariants variants={...}>{(variant) => ...}</EnvironmentVariants>`, and add a switcher with `<EnvironmentSelect />` (or build your own with `useEnvironment`). yaakdoc adds a small head script and CSS so that a remembered choice shows up before first paint.
|
|
224
|
+
|
|
225
|
+
## Entry points
|
|
226
|
+
|
|
227
|
+
| Import | Contents |
|
|
228
|
+
| --- | --- |
|
|
229
|
+
| `@yaakdoc/react` | Components, hooks, overrides, types. |
|
|
230
|
+
| `@yaakdoc/react/islands` | Islands and headless hooks (client-safe). |
|
|
231
|
+
| `@yaakdoc/react/client` | `hydrateIslands()` (used by yaakdoc's client entry). |
|
|
232
|
+
| `@yaakdoc/react/server` | `renderPage()` (used by yaakdoc's SSR entry). |
|
|
233
|
+
| `@yaakdoc/react/theme` | `defineTheme()` and theme types (Node-safe). |
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { t as IslandComponent } from "./island-BWyuL4_6.js";
|
|
2
|
+
//#region src/client.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Hydrates every `<yd-island>` on the page whose name matches one of the given islands.
|
|
5
|
+
* Call this once from the theme's client entry.
|
|
6
|
+
*/
|
|
7
|
+
export declare function hydrateIslands(islands: ReadonlyArray<IslandComponent<any>>, root?: ParentNode): void;
|
|
8
|
+
//#endregion
|
|
9
|
+
export type { IslandComponent };
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createElement } from "react";
|
|
2
|
+
import { hydrateRoot } from "react-dom/client";
|
|
3
|
+
//#region src/client.ts
|
|
4
|
+
/**
|
|
5
|
+
* Hydrates every `<yd-island>` on the page whose name matches one of the given islands.
|
|
6
|
+
* Call this once from the theme's client entry.
|
|
7
|
+
*/
|
|
8
|
+
function hydrateIslands(islands, root = document) {
|
|
9
|
+
const registry = new Map(islands.map((island) => [island.islandName, island.Component]));
|
|
10
|
+
for (const element of root.querySelectorAll("yd-island[data-island]")) {
|
|
11
|
+
if (element.dataset.hydrated) continue;
|
|
12
|
+
const Component = registry.get(element.dataset.island ?? "");
|
|
13
|
+
if (!Component) continue;
|
|
14
|
+
let props = {};
|
|
15
|
+
try {
|
|
16
|
+
props = JSON.parse(element.dataset.props ?? "{}");
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error(`[yaakdoc] Invalid props for island "${element.dataset.island}"`, error);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
element.dataset.hydrated = "true";
|
|
22
|
+
hydrateRoot(element, createElement(Component, props), { ...element.dataset.prefix ? { identifierPrefix: element.dataset.prefix } : {} });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { hydrateIslands };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { f as storage, m as createIsland } from "./run-in-yaak-CMMcDS7S.js";
|
|
2
|
+
import { useCallback, useSyncExternalStore } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/islands/color-mode.tsx
|
|
5
|
+
const COLOR_MODE_STORAGE_KEY = "yaakdoc-color-mode";
|
|
6
|
+
function readPreference() {
|
|
7
|
+
const value = storage.get(COLOR_MODE_STORAGE_KEY);
|
|
8
|
+
return value === "light" || value === "dark" || value === "system" ? value : null;
|
|
9
|
+
}
|
|
10
|
+
const systemMode = () => matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
11
|
+
/** Applies a preference to <html data-theme> and persists it. */
|
|
12
|
+
function applyColorMode(preference) {
|
|
13
|
+
const resolved = preference === "system" ? systemMode() : preference;
|
|
14
|
+
const root = document.documentElement;
|
|
15
|
+
root.dataset.theme = resolved;
|
|
16
|
+
root.dataset.colorMode = preference;
|
|
17
|
+
root.style.colorScheme = resolved;
|
|
18
|
+
storage.set(COLOR_MODE_STORAGE_KEY, preference);
|
|
19
|
+
}
|
|
20
|
+
function subscribe(callback) {
|
|
21
|
+
const observer = new MutationObserver(callback);
|
|
22
|
+
observer.observe(document.documentElement, {
|
|
23
|
+
attributes: true,
|
|
24
|
+
attributeFilter: ["data-theme", "data-color-mode"]
|
|
25
|
+
});
|
|
26
|
+
window.addEventListener("storage", callback);
|
|
27
|
+
return () => {
|
|
28
|
+
observer.disconnect();
|
|
29
|
+
window.removeEventListener("storage", callback);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const getSnapshot = () => `${document.documentElement.dataset.colorMode ?? readPreference() ?? "system"}|${document.documentElement.dataset.theme ?? systemMode()}`;
|
|
33
|
+
/**
|
|
34
|
+
* Headless color mode state. Hydration-safe: renders `fallback` on the server and during hydration,
|
|
35
|
+
* then switches to the real value.
|
|
36
|
+
*/
|
|
37
|
+
function useColorMode(fallback = "system") {
|
|
38
|
+
const [preference, resolved] = useSyncExternalStore(subscribe, getSnapshot, () => `${fallback}|light`).split("|");
|
|
39
|
+
return {
|
|
40
|
+
preference,
|
|
41
|
+
resolved,
|
|
42
|
+
setPreference: useCallback((next) => applyColorMode(next), []),
|
|
43
|
+
toggle: useCallback(() => applyColorMode(resolved === "dark" ? "light" : "dark"), [resolved])
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Inline script for <head> that applies the stored/system color mode before first paint, and keeps
|
|
48
|
+
* following the system setting while the preference is "system".
|
|
49
|
+
*/
|
|
50
|
+
function colorModeScript(defaultMode) {
|
|
51
|
+
return `(function(){try{var k=${JSON.stringify(COLOR_MODE_STORAGE_KEY)},d=document.documentElement,m=window.matchMedia("(prefers-color-scheme: dark)"),p;try{p=localStorage.getItem(k)}catch(e){}if(p!=="light"&&p!=="dark"&&p!=="system")p=${JSON.stringify(defaultMode)};function a(){var r=p==="system"?(m.matches?"dark":"light"):p;d.dataset.theme=r;d.dataset.colorMode=p;d.style.colorScheme=r}a();m.addEventListener("change",function(){var s;try{s=localStorage.getItem(k)}catch(e){}if(!s||s==="system"){p="system";a()}})}catch(e){}})();`;
|
|
52
|
+
}
|
|
53
|
+
function ColorModeToggleImpl({ className, label = "Toggle color mode" }) {
|
|
54
|
+
const { resolved, toggle } = useColorMode();
|
|
55
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
56
|
+
type: "button",
|
|
57
|
+
"data-yd": "color-mode-toggle",
|
|
58
|
+
"data-mode": resolved,
|
|
59
|
+
className,
|
|
60
|
+
"aria-label": label,
|
|
61
|
+
title: label,
|
|
62
|
+
onClick: toggle,
|
|
63
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
64
|
+
"data-yd-icon": "light",
|
|
65
|
+
"aria-hidden": "true",
|
|
66
|
+
children: "☀"
|
|
67
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
68
|
+
"data-yd-icon": "dark",
|
|
69
|
+
"aria-hidden": "true",
|
|
70
|
+
children: "☾"
|
|
71
|
+
})]
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/** Unstyled light/dark toggle. Both icons are rendered; show one via `[data-theme]` CSS. */
|
|
75
|
+
const ColorModeToggle = createIsland("yd:color-mode-toggle", ColorModeToggleImpl);
|
|
76
|
+
//#endregion
|
|
77
|
+
export { useColorMode as a, colorModeScript as i, ColorModeToggle as n, applyColorMode as r, COLOR_MODE_STORAGE_KEY as t };
|