astro-pigment 0.9.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/.browserslistrc +1 -0
- package/README.md +384 -0
- package/package.json +82 -0
- package/src/assets/fonts/MartianGrotesk-VF.woff2 +0 -0
- package/src/assets/fonts/MartianMono-Regular.woff2 +0 -0
- package/src/attachments/attachResizing.ts +177 -0
- package/src/attachments/attachRovingFocus.ts +126 -0
- package/src/attachments/styles.module.css +53 -0
- package/src/components/Button/Button.astro +13 -0
- package/src/components/Button/index.ts +1 -0
- package/src/components/Button/styles.module.css +30 -0
- package/src/components/CodeBlockWrapper/CodeBlockWrapper.astro +45 -0
- package/src/components/CodeBlockWrapper/index.ts +1 -0
- package/src/components/CodeBlockWrapper/styles.module.css +49 -0
- package/src/components/CodeEditor/CodeEditor.astro +81 -0
- package/src/components/CodeEditor/index.ts +1 -0
- package/src/components/CodeEditor/styles.module.css +22 -0
- package/src/components/CodeExample/CodeExample.astro +153 -0
- package/src/components/CodeExample/index.ts +1 -0
- package/src/components/CodeExample/styles.module.css +162 -0
- package/src/components/CodePanels/CodePanels.astro +30 -0
- package/src/components/CodePanels/index.ts +2 -0
- package/src/components/CodePanels/styles.module.css +48 -0
- package/src/components/CollapsiblePane/CollapsiblePane.astro +145 -0
- package/src/components/CollapsiblePane/index.ts +1 -0
- package/src/components/CollapsiblePane/styles.module.css +35 -0
- package/src/components/Footer/Footer.astro +32 -0
- package/src/components/Footer/index.ts +1 -0
- package/src/components/Footer/styles.module.css +36 -0
- package/src/components/HueSlider/HueScript.astro +11 -0
- package/src/components/HueSlider/HueSlider.astro +214 -0
- package/src/components/HueSlider/index.ts +2 -0
- package/src/components/HueSlider/styles.module.css +161 -0
- package/src/components/Icon/Icon.astro +62 -0
- package/src/components/Icon/index.ts +1 -0
- package/src/components/InstallPackage/InstallPackage.astro +27 -0
- package/src/components/InstallPackage/index.ts +1 -0
- package/src/components/Kbd/Kbd.astro +12 -0
- package/src/components/Kbd/index.ts +1 -0
- package/src/components/Kbd/styles.module.css +17 -0
- package/src/components/Layout/Layout.astro +127 -0
- package/src/components/Layout/index.ts +1 -0
- package/src/components/Layout/scripts.ts +8 -0
- package/src/components/Layout/styles.module.css +222 -0
- package/src/components/LivePreview/LivePreview.astro +67 -0
- package/src/components/LivePreview/base.css +76 -0
- package/src/components/LivePreview/index.ts +2 -0
- package/src/components/LivePreview/styles.module.css +14 -0
- package/src/components/LivePreview/types.ts +28 -0
- package/src/components/LivePreview/utils.ts +102 -0
- package/src/components/PageHeading/PageHeading.astro +24 -0
- package/src/components/PageHeading/index.ts +1 -0
- package/src/components/PageHeading/styles.module.css +16 -0
- package/src/components/PrevNextNav/PrevNextNav.astro +28 -0
- package/src/components/PrevNextNav/index.ts +2 -0
- package/src/components/PrevNextNav/styles.module.css +57 -0
- package/src/components/PrevNextNav/types.ts +4 -0
- package/src/components/ResizablePanes/ResizablePane.astro +12 -0
- package/src/components/ResizablePanes/ResizablePanes.astro +66 -0
- package/src/components/ResizablePanes/index.ts +2 -0
- package/src/components/ResizablePanes/styles.module.css +29 -0
- package/src/components/Search/Search.astro +215 -0
- package/src/components/Search/index.ts +1 -0
- package/src/components/Search/search-worker.ts +78 -0
- package/src/components/Search/styles.module.css +236 -0
- package/src/components/Search/types.ts +30 -0
- package/src/components/TableOfContents/List.astro +29 -0
- package/src/components/TableOfContents/MobileTableOfContents.astro +48 -0
- package/src/components/TableOfContents/TableOfContents.astro +122 -0
- package/src/components/TableOfContents/index.ts +1 -0
- package/src/components/TableOfContents/styles.module.css +129 -0
- package/src/components/TableOfContents/utils.ts +5 -0
- package/src/components/Tabs/Tab.astro +14 -0
- package/src/components/Tabs/Tabs.astro +113 -0
- package/src/components/Tabs/index.ts +2 -0
- package/src/components/Tabs/styles.module.css +70 -0
- package/src/components/ThemeToggle/ThemeScript.astro +10 -0
- package/src/components/ThemeToggle/ThemeToggle.astro +42 -0
- package/src/components/ThemeToggle/index.ts +2 -0
- package/src/components/ThemeToggle/styles.module.css +47 -0
- package/src/components/index.ts +11 -0
- package/src/components/playground.ts +7 -0
- package/src/content.ts +16 -0
- package/src/env.d.ts +7 -0
- package/src/index.ts +12 -0
- package/src/integration.ts +201 -0
- package/src/loaders/examples-loader.ts +120 -0
- package/src/pages/[...slug].astro +28 -0
- package/src/pages/[...slug].md.ts +16 -0
- package/src/pages/apple-touch-icon.png.ts +5 -0
- package/src/pages/favicon-96x96.png.ts +5 -0
- package/src/pages/favicon.ico.ts +5 -0
- package/src/pages/favicon.svg.ts +5 -0
- package/src/pages/llms-full.txt.ts +23 -0
- package/src/pages/llms.txt.ts +36 -0
- package/src/pages/search-index.json.ts +41 -0
- package/src/pages/site.webmanifest.ts +31 -0
- package/src/pages/web-app-manifest-192x192.png.ts +5 -0
- package/src/pages/web-app-manifest-512x512.png.ts +5 -0
- package/src/stores/media.ts +28 -0
- package/src/stores/pkgManager.ts +5 -0
- package/src/stores/theme.ts +50 -0
- package/src/styles/globals.css +51 -0
- package/src/styles/index.css +6 -0
- package/src/styles/media.css +6 -0
- package/src/styles/reset.css +114 -0
- package/src/styles/theme.css +77 -0
- package/src/styles/utilities.css +192 -0
- package/src/themes/adaptive-code-theme.ts +848 -0
- package/src/themes/codemirror-adaptive-theme.ts +154 -0
- package/src/types.ts +80 -0
- package/src/utils/content.ts +38 -0
- package/src/utils/defineCodePanels.ts +24 -0
- package/src/utils/dom.ts +4 -0
- package/src/utils/fonts.ts +30 -0
- package/src/utils/github.ts +20 -0
- package/src/utils/icon.ts +67 -0
- package/src/utils/markdown.ts +98 -0
- package/src/utils/nanostores.ts +11 -0
- package/src/utils/response.ts +22 -0
- package/src/utils/schemas.ts +15 -0
- package/src/utils/urls.ts +20 -0
- package/src/virtual.d.ts +42 -0
- package/stylelint.config.js +21 -0
package/.browserslistrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
last 2 versions and not dead
|
package/README.md
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# astro-pigment
|
|
2
|
+
|
|
3
|
+
An Astro 6 documentation theme with dark mode, interactive playgrounds, and SEO endpoints. One integration call gives you a complete docs site: layout, navigation, table of contents, code highlighting, LLM-friendly endpoints, and a library of interactive components.
|
|
4
|
+
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
- **Single integration**: rehype plugins, PostCSS, Shiki themes, sitemap, and SEO routes configured automatically
|
|
8
|
+
- **Dark mode**: three-state toggle (auto/light/dark) with View Transitions, no FOUC
|
|
9
|
+
- **CSS variable theming**: override `--theme-hue-override` or `--layout-width-override` in plain CSS, no config options needed
|
|
10
|
+
- **Interactive playgrounds**: CodeMirror editor + sandboxed live preview with console capture
|
|
11
|
+
- **LLM endpoints**: `/llms.txt` and `/llms-full.txt` auto-generated from your markdown content
|
|
12
|
+
- **Auto-generated favicons**: provide one or two source icons (simplified favicon + detailed manifest), get favicon.ico, SVG, PNG, apple-touch-icon, and webmanifest
|
|
13
|
+
- **Bundled fonts**: Martian Grotesk + Martian Mono auto-injected (opt out with `fonts: false`)
|
|
14
|
+
- **Accessible**: roving focus, ARIA attributes, keyboard navigation throughout
|
|
15
|
+
- **Zero build step**: Astro resolves `.astro`/`.ts` source directly from the package
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
Add the theme as a GitHub dependency along with its peer dependencies:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
// package.json
|
|
23
|
+
{
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"astro-pigment": "github:psd-coder/astro-pigment",
|
|
26
|
+
"astro": "^6.0.0",
|
|
27
|
+
"nanotags": "^0.14.0",
|
|
28
|
+
"nanostores": "^1.0.0"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Then install:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pnpm install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
// astro.config.mjs
|
|
43
|
+
import { defineConfig } from "astro/config";
|
|
44
|
+
import docsTheme from "astro-pigment";
|
|
45
|
+
|
|
46
|
+
export default defineConfig({
|
|
47
|
+
integrations: [
|
|
48
|
+
docsTheme({
|
|
49
|
+
project: {
|
|
50
|
+
name: "my-project",
|
|
51
|
+
description: "A short description of your project",
|
|
52
|
+
license: { name: "MIT", url: "https://github.com/your-name/your-repo/blob/main/LICENSE" },
|
|
53
|
+
github: { user: "your-name", repository: "your-repo" },
|
|
54
|
+
},
|
|
55
|
+
author: { name: "Your Name", url: "https://x.com/your_handle" },
|
|
56
|
+
icon: "src/assets/icon.svg",
|
|
57
|
+
navLinks: [
|
|
58
|
+
{ href: "/", label: "Overview" },
|
|
59
|
+
{ href: "/api", label: "API" },
|
|
60
|
+
],
|
|
61
|
+
}),
|
|
62
|
+
],
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
// src/content.config.ts
|
|
68
|
+
import { defineDocsCollections } from "astro-pigment/content";
|
|
69
|
+
|
|
70
|
+
export const collections = defineDocsCollections();
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Drop your `.md`/`.mdx` files in `src/content/docs/`. The integration injects `/[...slug]` automatically; pages render with the full layout, TOC, prev/next navigation, and edit-on-github link out of the box. Dark mode, sticky header, sidebar + mobile TOC, code copy buttons, favicons, webmanifest, sitemap, and LLM endpoints are all wired up automatically.
|
|
74
|
+
|
|
75
|
+
To render pages yourself, set `docs.renderDefaultPage: false` and create your own `src/pages/[...slug].astro`. Reuse the boilerplate via `getDocsStaticPaths` from `astro-pigment/utils/content`.
|
|
76
|
+
|
|
77
|
+
### Pick a theme hue (optional)
|
|
78
|
+
|
|
79
|
+
Temporarily enable the hue slider to find the right color for your site:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
docsTheme({
|
|
83
|
+
// ...your config
|
|
84
|
+
hueSlider: true, // shows a color slider in the header
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Drag the slider, pick a hue you like, then hardcode it in CSS and remove `hueSlider`:
|
|
89
|
+
|
|
90
|
+
```css
|
|
91
|
+
:root {
|
|
92
|
+
--theme-hue-override: 135; /* the value you picked */
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
All UI and code syntax highlighting colors derive from this hue via OKLch.
|
|
97
|
+
|
|
98
|
+
## Integration Config
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
type DocsThemeConfig = {
|
|
102
|
+
// Required
|
|
103
|
+
github: {
|
|
104
|
+
user?: string; // one of user/organization required
|
|
105
|
+
organization?: string;
|
|
106
|
+
repository: string;
|
|
107
|
+
};
|
|
108
|
+
project: {
|
|
109
|
+
name: string;
|
|
110
|
+
description: string;
|
|
111
|
+
license: { name: string; url: string };
|
|
112
|
+
};
|
|
113
|
+
author: {
|
|
114
|
+
name: string;
|
|
115
|
+
url: string;
|
|
116
|
+
icon?: IconName; // auto-detected: "x" for x.com URLs
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Optional
|
|
120
|
+
links?: Array<{ label: string; url: string; icon?: IconName }>;
|
|
121
|
+
site?: string; // default: auto GitHub Pages URL
|
|
122
|
+
icon?: string; // path to 512x512 PNG or SVG, generates favicons + webmanifest
|
|
123
|
+
hueSlider?: boolean; // show hue slider in header for initial theme setup
|
|
124
|
+
shikiThemes?: { // overrides adaptive hue-based theme
|
|
125
|
+
light: string;
|
|
126
|
+
dark: string;
|
|
127
|
+
};
|
|
128
|
+
navLinks?: NavItem[]; // header nav links; href accepts "/api" or "api"
|
|
129
|
+
docs?: {
|
|
130
|
+
directory?: string; // default: "src/content/docs"
|
|
131
|
+
pattern?: string; // default: "**/*.{md,mdx}"
|
|
132
|
+
deepSections?: string[];// slugs where llms.txt shows ### headings
|
|
133
|
+
renderDefaultPage?: boolean; // default: true. set false to ship your own [...slug].astro
|
|
134
|
+
tocItemsSelector?: string; // default: ".prose :is(h2, h3)[id]"
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### What the integration does
|
|
140
|
+
|
|
141
|
+
- Stores config in a virtual module (`virtual:theme-integration-config`) so components read it automatically
|
|
142
|
+
- Auto-sets `site` and `base` from GitHub config (GitHub Pages URL in CI, `/` in dev)
|
|
143
|
+
- Injects rehype-slug + rehype-autolink-headings
|
|
144
|
+
- Injects an adaptive Shiki theme that derives syntax colors from `--theme-hue` (based on Catppuccin, hue-rotated via OKLch). Override with `shikiThemes` to use fixed themes instead.
|
|
145
|
+
- Injects PostCSS preset-env (nesting, custom-media, media-query-ranges)
|
|
146
|
+
- When `icon` is configured: generates favicons (svg, ico, 96x96 png), apple-touch-icon, webmanifest + manifest icons
|
|
147
|
+
- Injects sitemap + llms.txt, llms-full.txt, [slug].md routes
|
|
148
|
+
- Injects `/[...slug]` page rendering docs from the content collection (opt out with `docs.renderDefaultPage: false`)
|
|
149
|
+
|
|
150
|
+
## Components
|
|
151
|
+
|
|
152
|
+
### Core
|
|
153
|
+
|
|
154
|
+
Import from `astro-pigment/components`:
|
|
155
|
+
|
|
156
|
+
**Layout** -- full page shell: sticky header, sidebar, footer, code copy buttons. Config read from virtual module. Includes ThemeToggle, ThemeScript, CodeBlockWrapper automatically.
|
|
157
|
+
|
|
158
|
+
```astro
|
|
159
|
+
<Layout title="Page Title" navItems={[{ href: "", label: "Home" }, { href: "api", label: "API" }]}>
|
|
160
|
+
<MyLogo slot="logo" />
|
|
161
|
+
<TableOfContents slot="sidebar" headings={headings} itemsSelector=".prose :is(h2, h3)[id]" />
|
|
162
|
+
<article class="prose"><slot /></article>
|
|
163
|
+
<span slot="footer-extra">& My Company</span>
|
|
164
|
+
</Layout>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Props: `title`, `navItems?`. Slots: `default`, `sidebar`, `logo`, `head-extra`, `footer-extra`, `author-icon`.
|
|
168
|
+
|
|
169
|
+
**TableOfContents** -- desktop sidebar with scroll-spy highlighting + mobile popover trigger. Both rendered from a single component.
|
|
170
|
+
|
|
171
|
+
```astro
|
|
172
|
+
<TableOfContents slot="sidebar" headings={headings} itemsSelector=".prose :is(h2, h3)[id]" />
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**PageHeading** -- heading row with a "view as markdown" icon link.
|
|
176
|
+
|
|
177
|
+
```astro
|
|
178
|
+
<PageHeading title="API Reference" href="/api.md" />
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Button** -- styled button with optional `square` prop for icon-only use.
|
|
182
|
+
|
|
183
|
+
```astro
|
|
184
|
+
<Button>Click me</Button>
|
|
185
|
+
<Button square aria-label="Menu"><Icon name="list" /></Button>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Icon** -- built-in SVGs: `check`, `chevron-left`, `copy`, `github`, `list`, `markdown`, `x`. Use `name="custom"` + slot for your own.
|
|
189
|
+
|
|
190
|
+
```astro
|
|
191
|
+
<Icon name="github" size={32} />
|
|
192
|
+
<Icon name="custom" label="Mastodon"><svg>...</svg></Icon>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Footer** -- license, GitHub, and author links from virtual config. Slot: `extra`. Included in Layout by default.
|
|
196
|
+
|
|
197
|
+
**ThemeToggle** -- three-state switcher (auto/light/dark). Included in Layout automatically.
|
|
198
|
+
|
|
199
|
+
**ThemeScript** -- inline script preventing FOUC. Included in Layout automatically.
|
|
200
|
+
|
|
201
|
+
**CodeBlockWrapper** -- adds copy buttons to all `.prose pre` blocks. Included in Layout automatically.
|
|
202
|
+
|
|
203
|
+
**InstallPackage** -- tabbed package manager switcher. Selection persists to localStorage.
|
|
204
|
+
|
|
205
|
+
```astro
|
|
206
|
+
<InstallPackage pkg="nanotags nanostores" />
|
|
207
|
+
<InstallPackage pkg="typescript" dev />
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**PrevNextNav** -- previous/next page navigation.
|
|
211
|
+
|
|
212
|
+
```astro
|
|
213
|
+
<PrevNextNav prev={{ title: "Getting Started", href: "/" }} next={{ title: "API", href: "/api" }} />
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Playground
|
|
217
|
+
|
|
218
|
+
Import from `astro-pigment/components/playground`:
|
|
219
|
+
|
|
220
|
+
**CodeEditor** -- CodeMirror 6 with adaptive hue-based theme synced to dark mode.
|
|
221
|
+
|
|
222
|
+
```astro
|
|
223
|
+
<CodeEditor lang="javascript" />
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**LivePreview** -- sandboxed iframe execution with console capture.
|
|
227
|
+
|
|
228
|
+
**CodeExample** -- full playground: tabbed editor + live preview + collapsible logs.
|
|
229
|
+
|
|
230
|
+
```astro
|
|
231
|
+
<CodeExample files={[
|
|
232
|
+
{ name: "index.html", type: "html", lang: "html", content: "<h1>Hello</h1>" },
|
|
233
|
+
{ name: "app.js", type: "javascript", lang: "javascript", content: "console.log('hi')" },
|
|
234
|
+
]} />
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**CodePanels** -- multi-file code display with Shiki highlighting and tabs.
|
|
238
|
+
|
|
239
|
+
**ResizablePanes** / **ResizablePane** -- draggable split-pane layout.
|
|
240
|
+
|
|
241
|
+
**CollapsiblePane** -- expandable/collapsible section with resize handle.
|
|
242
|
+
|
|
243
|
+
**Tabs** / **Tab** -- accessible tabs with roving focus and scroll arrows.
|
|
244
|
+
|
|
245
|
+
## CSS Customization
|
|
246
|
+
|
|
247
|
+
The theme uses CSS variables with fallback defaults. Override them in your own CSS:
|
|
248
|
+
|
|
249
|
+
```css
|
|
250
|
+
:root {
|
|
251
|
+
--theme-hue-override: 135; /* green instead of default cyan (180) */
|
|
252
|
+
--layout-width-override: 1280px; /* wider layout */
|
|
253
|
+
--layout-sidebar-width-override: 280px;
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
All color tokens are derived from `--theme-hue` using OKLch, so changing the hue recolors the entire site.
|
|
258
|
+
|
|
259
|
+
### Available tokens
|
|
260
|
+
|
|
261
|
+
| Token | Light | Dark |
|
|
262
|
+
|-------|-------|------|
|
|
263
|
+
| `--color-surface-1` | 99% lightness | 12% lightness |
|
|
264
|
+
| `--color-surface-2` | 98% | 18% |
|
|
265
|
+
| `--color-surface-3` | 96% | 21% |
|
|
266
|
+
| `--color-accent` | 55% lightness | 65% lightness |
|
|
267
|
+
| `--color-text-primary` | 15% | 90% |
|
|
268
|
+
| `--color-text-secondary` | 40% | 75% |
|
|
269
|
+
| `--color-border` | 90% | 25% |
|
|
270
|
+
|
|
271
|
+
Typography: `--text-xxs` (0.625rem) through `--text-2xl` (2rem). Spacing base: `--spacing` (4px). Radii: `--radius-sm`, `--radius-md`.
|
|
272
|
+
|
|
273
|
+
## Fonts
|
|
274
|
+
|
|
275
|
+
The integration auto-injects bundled Martian Grotesk (variable weight) and Martian Mono (400) as local fonts, setting `--font-sans` and `--font-mono` CSS variables. Pass `fonts: false` to opt out and set those variables to your own fonts.
|
|
276
|
+
|
|
277
|
+
## Stores
|
|
278
|
+
|
|
279
|
+
Available from `astro-pigment/stores/theme` and `astro-pigment/stores/media`:
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
import { $themeSetting, $resolvedTheme, cycleTheme } from "astro-pigment/stores/theme";
|
|
283
|
+
import { $prefersDarkScheme, $prefersReducedMotion } from "astro-pigment/stores/media";
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
- `$themeSetting`: persistent atom (`"auto"` | `"light"` | `"dark"`)
|
|
287
|
+
- `$resolvedTheme`: computed (`"light"` | `"dark"`)
|
|
288
|
+
- `cycleTheme()`: cycles auto -> light -> dark
|
|
289
|
+
|
|
290
|
+
Package manager store (from `astro-pigment/stores/pkgManager`):
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
import { $pkgManager } from "astro-pigment/stores/pkgManager";
|
|
294
|
+
|
|
295
|
+
$pkgManager.get() // "pnpm" | "npm" | "yarn" | "bun"
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Used by `InstallPackage` internally. Also available for custom `CodePanels`-based tab switchers via `defineCodePanels`:
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
import { defineCodePanels } from "astro-pigment/utils/defineCodePanels";
|
|
302
|
+
import { $pkgManager } from "astro-pigment/stores/pkgManager";
|
|
303
|
+
|
|
304
|
+
defineCodePanels("x-my-switcher", $pkgManager);
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## SEO & LLM Endpoints
|
|
308
|
+
|
|
309
|
+
When `docs` is configured, the integration auto-generates:
|
|
310
|
+
|
|
311
|
+
- **`/llms.txt`**: structured index with project name, description, and per-doc sections
|
|
312
|
+
- **`/llms-full.txt`**: all docs concatenated into a single markdown file
|
|
313
|
+
- **`/[slug].md`**: individual markdown endpoints for each doc file
|
|
314
|
+
- **Sitemap**: via `@astrojs/sitemap`
|
|
315
|
+
|
|
316
|
+
## Favicon & Webmanifest
|
|
317
|
+
|
|
318
|
+
`icon` accepts either a single source path or an object with two sources:
|
|
319
|
+
|
|
320
|
+
```js
|
|
321
|
+
// single source (same icon for all sizes)
|
|
322
|
+
icon: "src/assets/icon.svg",
|
|
323
|
+
|
|
324
|
+
// two sources — simplified design for tiny favicons, detailed for manifest
|
|
325
|
+
icon: {
|
|
326
|
+
favicon: "src/assets/favicon.svg", // used for /favicon.svg and /favicon.ico (16-32px)
|
|
327
|
+
manifest: "src/assets/icon-detailed.svg", // used for 96px and up
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Use the object form when a 512x512 design has fine details that become illegible at 16-32px. Both fields are required in the object form.
|
|
332
|
+
|
|
333
|
+
Generated routes:
|
|
334
|
+
|
|
335
|
+
- `/favicon.svg` — from `favicon` source (passthrough for SVG)
|
|
336
|
+
- `/favicon.ico` — from `favicon` source (32x32)
|
|
337
|
+
- `/favicon-96x96.png` — from `manifest` source
|
|
338
|
+
- `/apple-touch-icon.png` — from `manifest` source (180x180)
|
|
339
|
+
- `/web-app-manifest-192x192.png` — from `manifest` source
|
|
340
|
+
- `/web-app-manifest-512x512.png` — from `manifest` source
|
|
341
|
+
- `/site.webmanifest`
|
|
342
|
+
|
|
343
|
+
Layout renders the corresponding `<link>` tags only when `icon` is set.
|
|
344
|
+
|
|
345
|
+
## Examples Loader
|
|
346
|
+
|
|
347
|
+
For sites with interactive code examples, import the content collection loader:
|
|
348
|
+
|
|
349
|
+
```ts
|
|
350
|
+
// content.config.ts
|
|
351
|
+
import { examplesLoader } from "astro-pigment/loaders/examples";
|
|
352
|
+
|
|
353
|
+
const examples = defineCollection({
|
|
354
|
+
loader: examplesLoader("src/content/examples/"),
|
|
355
|
+
schema: z.object({
|
|
356
|
+
title: z.string(),
|
|
357
|
+
description: z.string(),
|
|
358
|
+
files: z.array(z.object({
|
|
359
|
+
name: z.string(),
|
|
360
|
+
type: z.enum(["html", "javascript", "css", "importmap"]),
|
|
361
|
+
lang: z.enum(["html", "javascript", "css"]),
|
|
362
|
+
content: z.string(),
|
|
363
|
+
})),
|
|
364
|
+
}),
|
|
365
|
+
});
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
The loader parses `.html` files with `data-type` attributes into `FileEntry` arrays compatible with the `CodeExample` playground component.
|
|
369
|
+
|
|
370
|
+
## Exportable Configs
|
|
371
|
+
|
|
372
|
+
```js
|
|
373
|
+
// stylelint.config.js
|
|
374
|
+
export default { extends: ["astro-pigment/stylelint.config"] };
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
```
|
|
378
|
+
// .browserslistrc
|
|
379
|
+
extends astro-pigment/browserslist
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## License
|
|
383
|
+
|
|
384
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "astro-pigment",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"files": [
|
|
6
|
+
"src",
|
|
7
|
+
"stylelint.config.js",
|
|
8
|
+
".browserslistrc",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./src/index.ts",
|
|
13
|
+
"./content": "./src/content.ts",
|
|
14
|
+
"./components": "./src/components/index.ts",
|
|
15
|
+
"./components/playground": "./src/components/playground.ts",
|
|
16
|
+
"./stores/theme": "./src/stores/theme.ts",
|
|
17
|
+
"./stores/media": "./src/stores/media.ts",
|
|
18
|
+
"./stores/pkgManager": "./src/stores/pkgManager.ts",
|
|
19
|
+
"./loaders/examples": "./src/loaders/examples-loader.ts",
|
|
20
|
+
"./utils/content": "./src/utils/content.ts",
|
|
21
|
+
"./utils/urls": "./src/utils/urls.ts",
|
|
22
|
+
"./utils/schemas": "./src/utils/schemas.ts",
|
|
23
|
+
"./utils/defineCodePanels": "./src/utils/defineCodePanels.ts",
|
|
24
|
+
"./styles/*": "./src/styles/*",
|
|
25
|
+
"./types": "./src/types.ts",
|
|
26
|
+
"./stylelint.config": "./stylelint.config.js",
|
|
27
|
+
"./browserslist": "./.browserslistrc"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"astro": "^6.0.0",
|
|
31
|
+
"nanostores": "^1.0.0",
|
|
32
|
+
"nanotags": "^0.14.0"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@astrojs/mdx": "^5.0.3",
|
|
36
|
+
"@astrojs/sitemap": "^3.7.1",
|
|
37
|
+
"@codemirror/lang-css": "^6.3.1",
|
|
38
|
+
"@codemirror/lang-html": "^6.4.9",
|
|
39
|
+
"@codemirror/lang-javascript": "^6.2.3",
|
|
40
|
+
"@codemirror/lang-json": "^6.0.1",
|
|
41
|
+
"@codemirror/language": "^6.12.3",
|
|
42
|
+
"@codemirror/state": "^6.5.0",
|
|
43
|
+
"@codemirror/view": "^6.36.0",
|
|
44
|
+
"@lezer/highlight": "^1.2.3",
|
|
45
|
+
"@nanostores/persistent": "^1.3.0",
|
|
46
|
+
"codemirror": "^6.0.1",
|
|
47
|
+
"es-toolkit": "^1.45.1",
|
|
48
|
+
"fuse.js": "7.4.0-beta.1",
|
|
49
|
+
"github-slugger": "^2.0.0",
|
|
50
|
+
"keyux": "^0.11.3",
|
|
51
|
+
"linkedom": "^0.18.12",
|
|
52
|
+
"mdast-util-to-string": "^4.0.0",
|
|
53
|
+
"nanoid": "^5.1.7",
|
|
54
|
+
"postcss-preset-env": "^11.0.0",
|
|
55
|
+
"rehype-autolink-headings": "^7.0.0",
|
|
56
|
+
"rehype-slug": "^6.0.0",
|
|
57
|
+
"remark-frontmatter": "^5.0.0",
|
|
58
|
+
"remark-mdx": "^3.1.1",
|
|
59
|
+
"remark-parse": "^11.0.0",
|
|
60
|
+
"remark-stringify": "^11.0.0",
|
|
61
|
+
"sharp": "^0.34.0",
|
|
62
|
+
"typed-channel": "^0.10.1",
|
|
63
|
+
"unified": "^11.0.5",
|
|
64
|
+
"unist-util-visit": "^5.1.0",
|
|
65
|
+
"valibot": "^1.3.1"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@types/mdast": "^4.0.4",
|
|
69
|
+
"astro": "^6.1.5",
|
|
70
|
+
"nanostores": "^1.0.0",
|
|
71
|
+
"nanotags": "^0.15.0",
|
|
72
|
+
"oxfmt": "^0.44.0",
|
|
73
|
+
"oxlint": "^1.59.0",
|
|
74
|
+
"typescript": "^5.9.3"
|
|
75
|
+
},
|
|
76
|
+
"scripts": {
|
|
77
|
+
"bump": "bash scripts/bump.sh",
|
|
78
|
+
"typecheck": "tsc --noEmit",
|
|
79
|
+
"lint": "oxlint --fix --fix-suggestions",
|
|
80
|
+
"format": "oxfmt --write src/"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { SetupContext } from "nanotags";
|
|
2
|
+
import { atom } from "nanostores";
|
|
3
|
+
import styles from "./styles.module.css";
|
|
4
|
+
|
|
5
|
+
type AttachResizingOptions = {
|
|
6
|
+
position: "before" | "after";
|
|
7
|
+
element: HTMLElement;
|
|
8
|
+
direction: "horizontal" | "vertical";
|
|
9
|
+
label?: string;
|
|
10
|
+
minValue?: number;
|
|
11
|
+
maxValue?: number;
|
|
12
|
+
step?: number;
|
|
13
|
+
initialValue?: number;
|
|
14
|
+
onDragStart?: (info: { startPos: number; elementSize: number; containerSize: number }) => void;
|
|
15
|
+
onDrag?: (delta: number) => void;
|
|
16
|
+
onDragEnd?: () => void;
|
|
17
|
+
onKeyResize?: (value: number) => void;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function createHandle(direction: "horizontal" | "vertical"): HTMLElement {
|
|
21
|
+
const handle = document.createElement("div");
|
|
22
|
+
handle.className = styles.handle ?? "";
|
|
23
|
+
handle.dataset["direction"] = direction;
|
|
24
|
+
handle.setAttribute("role", "separator");
|
|
25
|
+
handle.setAttribute("tabindex", "0");
|
|
26
|
+
return handle;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function attachResizing(
|
|
30
|
+
ctx: SetupContext<{}, {}>,
|
|
31
|
+
options: AttachResizingOptions,
|
|
32
|
+
): { handle: HTMLElement; setAriaValue: (value: number) => void } {
|
|
33
|
+
const {
|
|
34
|
+
position,
|
|
35
|
+
element,
|
|
36
|
+
direction,
|
|
37
|
+
label,
|
|
38
|
+
minValue = 0,
|
|
39
|
+
maxValue = 100,
|
|
40
|
+
step = 1,
|
|
41
|
+
initialValue = 50,
|
|
42
|
+
onDragStart,
|
|
43
|
+
onDrag,
|
|
44
|
+
onDragEnd,
|
|
45
|
+
onKeyResize,
|
|
46
|
+
} = options;
|
|
47
|
+
|
|
48
|
+
const handle = createHandle(direction);
|
|
49
|
+
handle.setAttribute("aria-valuemin", String(minValue));
|
|
50
|
+
handle.setAttribute("aria-valuemax", String(maxValue));
|
|
51
|
+
handle.setAttribute("aria-valuenow", String(initialValue));
|
|
52
|
+
if (label) {
|
|
53
|
+
handle.setAttribute("aria-label", label);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
element.insertAdjacentElement(position === "before" ? "beforebegin" : "afterend", handle);
|
|
57
|
+
|
|
58
|
+
const $drag = atom({ isDragging: false, startPos: 0 });
|
|
59
|
+
let valueBeforeCollapse: number | null = null;
|
|
60
|
+
|
|
61
|
+
function setAriaValue(value: number) {
|
|
62
|
+
handle.setAttribute("aria-valuenow", String(Math.round(value)));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function onMouseMove(ev: MouseEvent) {
|
|
66
|
+
const { startPos } = $drag.get();
|
|
67
|
+
const currentPos = direction === "vertical" ? ev.clientY : ev.clientX;
|
|
68
|
+
const rawDelta = currentPos - startPos;
|
|
69
|
+
const delta = position === "before" ? -rawDelta : rawDelta;
|
|
70
|
+
onDrag?.(delta);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function onMouseUp() {
|
|
74
|
+
window.removeEventListener("mousemove", onMouseMove);
|
|
75
|
+
window.removeEventListener("mouseup", onMouseUp);
|
|
76
|
+
$drag.set({ ...$drag.get(), isDragging: false });
|
|
77
|
+
onDragEnd?.();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
ctx.on(handle, "mousedown", (ev) => {
|
|
81
|
+
ev.preventDefault();
|
|
82
|
+
const isVertical = direction === "vertical";
|
|
83
|
+
const startPos = isVertical ? ev.clientY : ev.clientX;
|
|
84
|
+
|
|
85
|
+
$drag.set({ isDragging: true, startPos });
|
|
86
|
+
|
|
87
|
+
window.addEventListener("mousemove", onMouseMove);
|
|
88
|
+
window.addEventListener("mouseup", onMouseUp);
|
|
89
|
+
|
|
90
|
+
const elementRect = element.getBoundingClientRect();
|
|
91
|
+
const containerRect = element.parentElement?.getBoundingClientRect();
|
|
92
|
+
onDragStart?.({
|
|
93
|
+
startPos,
|
|
94
|
+
elementSize: isVertical ? elementRect.height : elementRect.width,
|
|
95
|
+
containerSize: containerRect
|
|
96
|
+
? isVertical
|
|
97
|
+
? containerRect.height
|
|
98
|
+
: containerRect.width
|
|
99
|
+
: Infinity,
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
ctx.on(handle, "keydown", (ev) => {
|
|
104
|
+
const current = Number(handle.getAttribute("aria-valuenow"));
|
|
105
|
+
let next: number | null = null;
|
|
106
|
+
|
|
107
|
+
const isHorizontal = direction === "horizontal";
|
|
108
|
+
const invert = position === "before";
|
|
109
|
+
const decreaseKey = isHorizontal
|
|
110
|
+
? invert
|
|
111
|
+
? "ArrowRight"
|
|
112
|
+
: "ArrowLeft"
|
|
113
|
+
: invert
|
|
114
|
+
? "ArrowDown"
|
|
115
|
+
: "ArrowUp";
|
|
116
|
+
const increaseKey = isHorizontal
|
|
117
|
+
? invert
|
|
118
|
+
? "ArrowLeft"
|
|
119
|
+
: "ArrowRight"
|
|
120
|
+
: invert
|
|
121
|
+
? "ArrowUp"
|
|
122
|
+
: "ArrowDown";
|
|
123
|
+
|
|
124
|
+
switch (ev.key) {
|
|
125
|
+
case decreaseKey:
|
|
126
|
+
next = Math.max(minValue, current - step);
|
|
127
|
+
break;
|
|
128
|
+
case increaseKey:
|
|
129
|
+
next = Math.min(maxValue, current + step);
|
|
130
|
+
break;
|
|
131
|
+
case "Home":
|
|
132
|
+
next = invert ? maxValue : minValue;
|
|
133
|
+
break;
|
|
134
|
+
case "End":
|
|
135
|
+
next = invert ? minValue : maxValue;
|
|
136
|
+
break;
|
|
137
|
+
case "Enter": {
|
|
138
|
+
if (current > minValue) {
|
|
139
|
+
valueBeforeCollapse = current;
|
|
140
|
+
next = minValue;
|
|
141
|
+
} else if (valueBeforeCollapse !== null) {
|
|
142
|
+
next = valueBeforeCollapse;
|
|
143
|
+
valueBeforeCollapse = null;
|
|
144
|
+
}
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
default:
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
ev.preventDefault();
|
|
152
|
+
if (next !== null && next !== current) {
|
|
153
|
+
setAriaValue(next);
|
|
154
|
+
onKeyResize?.(next);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
ctx.effect($drag, ({ isDragging }) => {
|
|
159
|
+
handle.dataset["dragging"] = String(isDragging);
|
|
160
|
+
|
|
161
|
+
const parent = element.parentElement;
|
|
162
|
+
if (!parent) return;
|
|
163
|
+
for (const child of parent.children) {
|
|
164
|
+
if (child !== handle && child instanceof HTMLElement) {
|
|
165
|
+
child.style.pointerEvents = isDragging ? "none" : "";
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
ctx.onCleanup(() => {
|
|
171
|
+
window.removeEventListener("mousemove", onMouseMove);
|
|
172
|
+
window.removeEventListener("mouseup", onMouseUp);
|
|
173
|
+
handle.remove();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return { handle, setAriaValue };
|
|
177
|
+
}
|