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.
Files changed (124) hide show
  1. package/.browserslistrc +1 -0
  2. package/README.md +384 -0
  3. package/package.json +82 -0
  4. package/src/assets/fonts/MartianGrotesk-VF.woff2 +0 -0
  5. package/src/assets/fonts/MartianMono-Regular.woff2 +0 -0
  6. package/src/attachments/attachResizing.ts +177 -0
  7. package/src/attachments/attachRovingFocus.ts +126 -0
  8. package/src/attachments/styles.module.css +53 -0
  9. package/src/components/Button/Button.astro +13 -0
  10. package/src/components/Button/index.ts +1 -0
  11. package/src/components/Button/styles.module.css +30 -0
  12. package/src/components/CodeBlockWrapper/CodeBlockWrapper.astro +45 -0
  13. package/src/components/CodeBlockWrapper/index.ts +1 -0
  14. package/src/components/CodeBlockWrapper/styles.module.css +49 -0
  15. package/src/components/CodeEditor/CodeEditor.astro +81 -0
  16. package/src/components/CodeEditor/index.ts +1 -0
  17. package/src/components/CodeEditor/styles.module.css +22 -0
  18. package/src/components/CodeExample/CodeExample.astro +153 -0
  19. package/src/components/CodeExample/index.ts +1 -0
  20. package/src/components/CodeExample/styles.module.css +162 -0
  21. package/src/components/CodePanels/CodePanels.astro +30 -0
  22. package/src/components/CodePanels/index.ts +2 -0
  23. package/src/components/CodePanels/styles.module.css +48 -0
  24. package/src/components/CollapsiblePane/CollapsiblePane.astro +145 -0
  25. package/src/components/CollapsiblePane/index.ts +1 -0
  26. package/src/components/CollapsiblePane/styles.module.css +35 -0
  27. package/src/components/Footer/Footer.astro +32 -0
  28. package/src/components/Footer/index.ts +1 -0
  29. package/src/components/Footer/styles.module.css +36 -0
  30. package/src/components/HueSlider/HueScript.astro +11 -0
  31. package/src/components/HueSlider/HueSlider.astro +214 -0
  32. package/src/components/HueSlider/index.ts +2 -0
  33. package/src/components/HueSlider/styles.module.css +161 -0
  34. package/src/components/Icon/Icon.astro +62 -0
  35. package/src/components/Icon/index.ts +1 -0
  36. package/src/components/InstallPackage/InstallPackage.astro +27 -0
  37. package/src/components/InstallPackage/index.ts +1 -0
  38. package/src/components/Kbd/Kbd.astro +12 -0
  39. package/src/components/Kbd/index.ts +1 -0
  40. package/src/components/Kbd/styles.module.css +17 -0
  41. package/src/components/Layout/Layout.astro +127 -0
  42. package/src/components/Layout/index.ts +1 -0
  43. package/src/components/Layout/scripts.ts +8 -0
  44. package/src/components/Layout/styles.module.css +222 -0
  45. package/src/components/LivePreview/LivePreview.astro +67 -0
  46. package/src/components/LivePreview/base.css +76 -0
  47. package/src/components/LivePreview/index.ts +2 -0
  48. package/src/components/LivePreview/styles.module.css +14 -0
  49. package/src/components/LivePreview/types.ts +28 -0
  50. package/src/components/LivePreview/utils.ts +102 -0
  51. package/src/components/PageHeading/PageHeading.astro +24 -0
  52. package/src/components/PageHeading/index.ts +1 -0
  53. package/src/components/PageHeading/styles.module.css +16 -0
  54. package/src/components/PrevNextNav/PrevNextNav.astro +28 -0
  55. package/src/components/PrevNextNav/index.ts +2 -0
  56. package/src/components/PrevNextNav/styles.module.css +57 -0
  57. package/src/components/PrevNextNav/types.ts +4 -0
  58. package/src/components/ResizablePanes/ResizablePane.astro +12 -0
  59. package/src/components/ResizablePanes/ResizablePanes.astro +66 -0
  60. package/src/components/ResizablePanes/index.ts +2 -0
  61. package/src/components/ResizablePanes/styles.module.css +29 -0
  62. package/src/components/Search/Search.astro +215 -0
  63. package/src/components/Search/index.ts +1 -0
  64. package/src/components/Search/search-worker.ts +78 -0
  65. package/src/components/Search/styles.module.css +236 -0
  66. package/src/components/Search/types.ts +30 -0
  67. package/src/components/TableOfContents/List.astro +29 -0
  68. package/src/components/TableOfContents/MobileTableOfContents.astro +48 -0
  69. package/src/components/TableOfContents/TableOfContents.astro +122 -0
  70. package/src/components/TableOfContents/index.ts +1 -0
  71. package/src/components/TableOfContents/styles.module.css +129 -0
  72. package/src/components/TableOfContents/utils.ts +5 -0
  73. package/src/components/Tabs/Tab.astro +14 -0
  74. package/src/components/Tabs/Tabs.astro +113 -0
  75. package/src/components/Tabs/index.ts +2 -0
  76. package/src/components/Tabs/styles.module.css +70 -0
  77. package/src/components/ThemeToggle/ThemeScript.astro +10 -0
  78. package/src/components/ThemeToggle/ThemeToggle.astro +42 -0
  79. package/src/components/ThemeToggle/index.ts +2 -0
  80. package/src/components/ThemeToggle/styles.module.css +47 -0
  81. package/src/components/index.ts +11 -0
  82. package/src/components/playground.ts +7 -0
  83. package/src/content.ts +16 -0
  84. package/src/env.d.ts +7 -0
  85. package/src/index.ts +12 -0
  86. package/src/integration.ts +201 -0
  87. package/src/loaders/examples-loader.ts +120 -0
  88. package/src/pages/[...slug].astro +28 -0
  89. package/src/pages/[...slug].md.ts +16 -0
  90. package/src/pages/apple-touch-icon.png.ts +5 -0
  91. package/src/pages/favicon-96x96.png.ts +5 -0
  92. package/src/pages/favicon.ico.ts +5 -0
  93. package/src/pages/favicon.svg.ts +5 -0
  94. package/src/pages/llms-full.txt.ts +23 -0
  95. package/src/pages/llms.txt.ts +36 -0
  96. package/src/pages/search-index.json.ts +41 -0
  97. package/src/pages/site.webmanifest.ts +31 -0
  98. package/src/pages/web-app-manifest-192x192.png.ts +5 -0
  99. package/src/pages/web-app-manifest-512x512.png.ts +5 -0
  100. package/src/stores/media.ts +28 -0
  101. package/src/stores/pkgManager.ts +5 -0
  102. package/src/stores/theme.ts +50 -0
  103. package/src/styles/globals.css +51 -0
  104. package/src/styles/index.css +6 -0
  105. package/src/styles/media.css +6 -0
  106. package/src/styles/reset.css +114 -0
  107. package/src/styles/theme.css +77 -0
  108. package/src/styles/utilities.css +192 -0
  109. package/src/themes/adaptive-code-theme.ts +848 -0
  110. package/src/themes/codemirror-adaptive-theme.ts +154 -0
  111. package/src/types.ts +80 -0
  112. package/src/utils/content.ts +38 -0
  113. package/src/utils/defineCodePanels.ts +24 -0
  114. package/src/utils/dom.ts +4 -0
  115. package/src/utils/fonts.ts +30 -0
  116. package/src/utils/github.ts +20 -0
  117. package/src/utils/icon.ts +67 -0
  118. package/src/utils/markdown.ts +98 -0
  119. package/src/utils/nanostores.ts +11 -0
  120. package/src/utils/response.ts +22 -0
  121. package/src/utils/schemas.ts +15 -0
  122. package/src/utils/urls.ts +20 -0
  123. package/src/virtual.d.ts +42 -0
  124. package/stylelint.config.js +21 -0
@@ -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
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](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
+ }
@@ -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
+ }