├── .changeset ├── README.md └── config.json ├── .github └── workflows │ ├── autofix.yml │ ├── integration.yml │ └── release.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.cjs ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── docs ├── README.md ├── astro.config.ts ├── astro.no-caption.config.ts ├── package.json ├── public │ └── favicon.svg ├── src │ ├── assets │ │ ├── demo │ │ │ ├── algorithm.jpg │ │ │ ├── blackboard.jpg │ │ │ ├── bridge.jpg │ │ │ ├── mug.jpg │ │ │ └── paper.jpg │ │ ├── showcase │ │ │ ├── aoi.js.org.png │ │ │ ├── docs.astronvim.com.png │ │ │ ├── docs.testomat.io.png │ │ │ ├── events-3bg.pages.dev.png │ │ │ ├── userdocs.github.io-qbittorrent-nox-static.png │ │ │ └── wiki.atlauncher.com.png │ │ └── tests │ │ │ ├── astro-light.svg │ │ │ ├── large.jpg │ │ │ ├── starlight-light.png │ │ │ └── text.jpg │ ├── components │ │ └── ImageAttribution.astro │ ├── content.config.ts │ ├── content │ │ └── docs │ │ │ ├── configuration.md │ │ │ ├── customization.mdx │ │ │ ├── demo.mdx │ │ │ ├── getting-started.mdx │ │ │ ├── ignoring-images.mdx │ │ │ ├── index.mdx │ │ │ ├── resources │ │ │ ├── showcase.mdx │ │ │ └── starlight.mdx │ │ │ └── tests │ │ │ ├── data-zoom-id.md │ │ │ ├── hero.mdx │ │ │ ├── large.md │ │ │ ├── not-content.md │ │ │ ├── shared-syntax.md │ │ │ ├── shared-syntax.mdx │ │ │ ├── specific-syntax.md │ │ │ ├── specific-syntax.mdx │ │ │ ├── text.md │ │ │ ├── zoom-off.mdx │ │ │ └── zoom.mdx │ ├── env.d.ts │ └── styles │ │ └── custom.css └── tsconfig.json ├── eslint.config.mjs ├── package.json ├── packages └── starlight-image-zoom │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── components │ └── ImageZoom.astro │ ├── eslint.config.mjs │ ├── index.ts │ ├── libs │ ├── constants.ts │ ├── integration.ts │ ├── rehype.ts │ └── vite.ts │ ├── overrides │ └── MarkdownContent.astro │ ├── package.json │ ├── playwright.config.ts │ ├── tests │ ├── fixtures │ │ └── TestPage.ts │ ├── interaction.test.ts │ ├── no-caption.test.ts │ ├── shared-syntax.test.ts │ ├── specific-syntax.test.ts │ ├── test.ts │ └── zoom.test.ts │ ├── tsconfig.json │ └── virtual.d.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── tsconfig.json /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.4/schema.json", 3 | "changelog": [ 4 | "@changesets/changelog-github", 5 | { "repo": "HiDeoo/starlight-image-zoom" } 6 | ], 7 | "commit": false, 8 | "access": "public", 9 | "baseBranch": "main", 10 | "updateInternalDependencies": "patch", 11 | "ignore": ["starlight-image-zoom-docs"] 12 | } 13 | -------------------------------------------------------------------------------- /.github/workflows/autofix.yml: -------------------------------------------------------------------------------- 1 | name: autofix.ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | workflow_call: 11 | 12 | permissions: 13 | contents: read 14 | 15 | concurrency: 16 | cancel-in-progress: true 17 | group: ${{ github.workflow }}-${{ github.event_name == 'pull_request_target' && github.head_ref || github.ref }} 18 | 19 | jobs: 20 | autofix: 21 | name: Format code 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v4 26 | 27 | - name: Install pnpm 28 | uses: pnpm/action-setup@v4 29 | 30 | - name: Install Node.js 31 | uses: actions/setup-node@v4 32 | with: 33 | cache: pnpm 34 | node-version: 18 35 | 36 | - name: Install dependencies 37 | run: pnpm install 38 | 39 | - name: Format code 40 | run: pnpm format 41 | 42 | - name: Run autofix 43 | uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c 44 | with: 45 | fail-fast: false 46 | -------------------------------------------------------------------------------- /.github/workflows/integration.yml: -------------------------------------------------------------------------------- 1 | name: integration 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | workflow_call: 11 | 12 | concurrency: 13 | cancel-in-progress: true 14 | group: ${{ github.workflow }}-${{ github.event_name == 'pull_request_target' && github.head_ref || github.ref }} 15 | 16 | jobs: 17 | lint_test: 18 | name: Lint & Test 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v4 23 | 24 | - name: Install pnpm 25 | uses: pnpm/action-setup@v4 26 | 27 | - name: Install Node.js 28 | uses: actions/setup-node@v4 29 | with: 30 | cache: pnpm 31 | node-version: 18 32 | 33 | - name: Install dependencies 34 | run: pnpm install 35 | 36 | - name: Generates docs TypeScript types 37 | run: pnpm astro sync 38 | working-directory: docs 39 | 40 | - name: Lint 41 | run: pnpm lint 42 | 43 | - name: Test 44 | run: pnpm test 45 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | changeset: 10 | name: Changeset 11 | if: ${{ github.repository_owner == 'hideoo' }} 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: write 15 | id-token: write 16 | pull-requests: write 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 0 22 | 23 | - name: Install pnpm 24 | uses: pnpm/action-setup@v4 25 | 26 | - name: Install Node.js 27 | uses: actions/setup-node@v4 28 | with: 29 | cache: pnpm 30 | node-version: 18 31 | 32 | - name: Install dependencies 33 | run: pnpm install 34 | 35 | - name: Create Release Pull Request or Publish 36 | uses: changesets/action@v1 37 | with: 38 | version: pnpm run version 39 | publish: pnpm changeset publish 40 | commit: 'ci: release' 41 | title: 'ci: release' 42 | env: 43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 44 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .astro 2 | .DS_Store 3 | .eslintcache 4 | .idea 5 | .next 6 | .turbo 7 | .vercel 8 | .vscode/* 9 | !.vscode/extensions.json 10 | !.vscode/launch.json 11 | !.vscode/settings.json 12 | !.vscode/tasks.json 13 | .vscode-test 14 | .vscode-test-web 15 | *.local 16 | *.log 17 | *.pem 18 | *.tsbuildinfo 19 | build 20 | coverage 21 | dist 22 | dist-no-caption 23 | dist-ssr 24 | lerna-debug.log* 25 | logs 26 | next-env.d.ts 27 | node_modules 28 | npm-debug.log* 29 | out 30 | pnpm-debug.log* 31 | releases 32 | test-results 33 | yarn-debug.log* 34 | yarn-error.log* 35 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .astro 2 | .changeset 3 | .github/blocks 4 | .next 5 | .vercel 6 | .vscode-test 7 | .vscode-test-web 8 | build 9 | coverage 10 | dist 11 | dist-ssr 12 | out 13 | pnpm-lock.yaml 14 | 15 | # The content of this file should match the one from the `shared-syntax-md.md` file. 16 | docs/src/content/docs/tests/shared-syntax.mdx 17 | -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | const baseConfig = require('@hideoo/prettier-config') 2 | 3 | /** 4 | * @type {import('prettier').Config} 5 | */ 6 | const prettierConfig = { 7 | ...baseConfig, 8 | overrides: [ 9 | { 10 | files: '*.astro', 11 | options: { 12 | parser: 'astro', 13 | }, 14 | }, 15 | ], 16 | plugins: [require.resolve('prettier-plugin-astro')], 17 | } 18 | 19 | module.exports = prettierConfig 20 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.useFlatConfig": true, 3 | "eslint.validate": [ 4 | "javascript", 5 | "javascriptreact", 6 | "typescript", 7 | "typescriptreact", 8 | "html", 9 | "vue", 10 | "markdown", 11 | "astro" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024-present, HiDeoo 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | packages/starlight-image-zoom/README.md -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 |
2 |

starlight-image-zoom 🕵️

3 |

Starlight plugin adding zoom capabilities to your documentation images.

4 |
5 | 6 |
7 | 8 | Integration Status 9 | 10 | 11 | License 12 | 13 |
14 |
15 | 16 | ## Documentation 17 | 18 | Run the documentation locally using [pnpm](https://pnpm.io): 19 | 20 | ```shell 21 | pnpm run dev 22 | ``` 23 | 24 | ## License 25 | 26 | Licensed under the MIT License, Copyright © HiDeoo. 27 | 28 | See [LICENSE](https://github.com/HiDeoo/starlight-image-zoom/blob/main/LICENSE) for more information. 29 | -------------------------------------------------------------------------------- /docs/astro.config.ts: -------------------------------------------------------------------------------- 1 | import starlight from '@astrojs/starlight' 2 | import { defineConfig } from 'astro/config' 3 | import starlightImageZoom from 'starlight-image-zoom' 4 | 5 | export default defineConfig({ 6 | integrations: [ 7 | starlight({ 8 | customCss: ['./src/styles/custom.css'], 9 | editLink: { 10 | baseUrl: 'https://github.com/HiDeoo/starlight-image-zoom/edit/main/docs/', 11 | }, 12 | plugins: [starlightImageZoom()], 13 | sidebar: [ 14 | { 15 | label: 'Start Here', 16 | items: [ 17 | { label: 'Getting Started', link: '/getting-started/' }, 18 | { label: 'Configuration', link: '/configuration/' }, 19 | ], 20 | }, 21 | { 22 | label: 'Guides', 23 | items: [ 24 | { label: 'Ignoring Images', link: '/ignoring-images/' }, 25 | { label: 'Customization', link: '/customization/' }, 26 | ], 27 | }, 28 | { 29 | label: 'Resources', 30 | items: [ 31 | { label: 'Showcase', link: '/resources/showcase/' }, 32 | { label: 'Plugins and Tools', link: '/resources/starlight/' }, 33 | ], 34 | }, 35 | { label: 'Demo', link: '/demo/' }, 36 | ], 37 | social: { 38 | blueSky: 'https://bsky.app/profile/hideoo.dev', 39 | github: 'https://github.com/HiDeoo/starlight-image-zoom', 40 | }, 41 | title: 'Starlight Image Zoom', 42 | }), 43 | ], 44 | }) 45 | -------------------------------------------------------------------------------- /docs/astro.no-caption.config.ts: -------------------------------------------------------------------------------- 1 | import starlight from '@astrojs/starlight' 2 | import { defineConfig } from 'astro/config' 3 | import starlightImageZoom from 'starlight-image-zoom' 4 | 5 | export default defineConfig({ 6 | base: '/no-caption/', 7 | integrations: [ 8 | starlight({ 9 | plugins: [starlightImageZoom({ showCaptions: false })], 10 | title: 'Starlight Image Zoom No Caption Example', 11 | }), 12 | ], 13 | outDir: './dist-no-caption', 14 | server: { 15 | port: 4322, 16 | }, 17 | }) 18 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starlight-image-zoom-docs", 3 | "version": "0.9.0", 4 | "license": "MIT", 5 | "description": "Starlight plugin adding zoom capabilities to your documentation images.", 6 | "author": "HiDeoo (https://hideoo.dev)", 7 | "type": "module", 8 | "scripts": { 9 | "dev": "astro dev", 10 | "dev:no-caption": "astro dev --config astro.no-caption.config.ts", 11 | "build": "astro build", 12 | "build:no-caption": "astro build --config astro.no-caption.config.ts", 13 | "preview": "astro preview", 14 | "preview:no-caption": "astro preview --config astro.no-caption.config.ts", 15 | "lint": "eslint . --cache --max-warnings=0" 16 | }, 17 | "dependencies": { 18 | "@astrojs/starlight": "^0.32.0", 19 | "@hideoo/starlight-plugins-docs-components": "^0.4.0", 20 | "astro": "^5.3.0", 21 | "sharp": "^0.33.5", 22 | "starlight-image-zoom": "workspace:*" 23 | }, 24 | "engines": { 25 | "node": ">=18" 26 | }, 27 | "packageManager": "pnpm@8.15.4", 28 | "private": true, 29 | "sideEffects": false, 30 | "keywords": [ 31 | "starlight", 32 | "image", 33 | "zoom", 34 | "documentation", 35 | "image-zoom" 36 | ], 37 | "homepage": "https://github.com/HiDeoo/starlight-image-zoom", 38 | "repository": { 39 | "type": "git", 40 | "url": "https://github.com/HiDeoo/starlight-image-zoom.git", 41 | "directory": "docs" 42 | }, 43 | "bugs": "https://github.com/HiDeoo/starlight-image-zoom/issues" 44 | } 45 | -------------------------------------------------------------------------------- /docs/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 🕵️ 2 | -------------------------------------------------------------------------------- /docs/src/assets/demo/algorithm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiDeoo/starlight-image-zoom/b71545b645e1ccc82f1bda33f49c34db14a73aa5/docs/src/assets/demo/algorithm.jpg -------------------------------------------------------------------------------- /docs/src/assets/demo/blackboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiDeoo/starlight-image-zoom/b71545b645e1ccc82f1bda33f49c34db14a73aa5/docs/src/assets/demo/blackboard.jpg -------------------------------------------------------------------------------- /docs/src/assets/demo/bridge.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiDeoo/starlight-image-zoom/b71545b645e1ccc82f1bda33f49c34db14a73aa5/docs/src/assets/demo/bridge.jpg -------------------------------------------------------------------------------- /docs/src/assets/demo/mug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiDeoo/starlight-image-zoom/b71545b645e1ccc82f1bda33f49c34db14a73aa5/docs/src/assets/demo/mug.jpg -------------------------------------------------------------------------------- /docs/src/assets/demo/paper.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiDeoo/starlight-image-zoom/b71545b645e1ccc82f1bda33f49c34db14a73aa5/docs/src/assets/demo/paper.jpg -------------------------------------------------------------------------------- /docs/src/assets/showcase/aoi.js.org.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiDeoo/starlight-image-zoom/b71545b645e1ccc82f1bda33f49c34db14a73aa5/docs/src/assets/showcase/aoi.js.org.png -------------------------------------------------------------------------------- /docs/src/assets/showcase/docs.astronvim.com.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiDeoo/starlight-image-zoom/b71545b645e1ccc82f1bda33f49c34db14a73aa5/docs/src/assets/showcase/docs.astronvim.com.png -------------------------------------------------------------------------------- /docs/src/assets/showcase/docs.testomat.io.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiDeoo/starlight-image-zoom/b71545b645e1ccc82f1bda33f49c34db14a73aa5/docs/src/assets/showcase/docs.testomat.io.png -------------------------------------------------------------------------------- /docs/src/assets/showcase/events-3bg.pages.dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiDeoo/starlight-image-zoom/b71545b645e1ccc82f1bda33f49c34db14a73aa5/docs/src/assets/showcase/events-3bg.pages.dev.png -------------------------------------------------------------------------------- /docs/src/assets/showcase/userdocs.github.io-qbittorrent-nox-static.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiDeoo/starlight-image-zoom/b71545b645e1ccc82f1bda33f49c34db14a73aa5/docs/src/assets/showcase/userdocs.github.io-qbittorrent-nox-static.png -------------------------------------------------------------------------------- /docs/src/assets/showcase/wiki.atlauncher.com.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiDeoo/starlight-image-zoom/b71545b645e1ccc82f1bda33f49c34db14a73aa5/docs/src/assets/showcase/wiki.atlauncher.com.png -------------------------------------------------------------------------------- /docs/src/assets/tests/astro-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/src/assets/tests/large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiDeoo/starlight-image-zoom/b71545b645e1ccc82f1bda33f49c34db14a73aa5/docs/src/assets/tests/large.jpg -------------------------------------------------------------------------------- /docs/src/assets/tests/starlight-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiDeoo/starlight-image-zoom/b71545b645e1ccc82f1bda33f49c34db14a73aa5/docs/src/assets/tests/starlight-light.png -------------------------------------------------------------------------------- /docs/src/assets/tests/text.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HiDeoo/starlight-image-zoom/b71545b645e1ccc82f1bda33f49c34db14a73aa5/docs/src/assets/tests/text.jpg -------------------------------------------------------------------------------- /docs/src/components/ImageAttribution.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props { 3 | id: string 4 | name: string 5 | username: string 6 | } 7 | 8 | const { id, name, username } = Astro.props 9 | --- 10 | 11 |

12 | Photo by 15 | {name} 16 | on 19 | Unsplash 20 | 21 |

22 | 23 | 31 | -------------------------------------------------------------------------------- /docs/src/content.config.ts: -------------------------------------------------------------------------------- 1 | import { docsLoader } from '@astrojs/starlight/loaders' 2 | import { docsSchema } from '@astrojs/starlight/schema' 3 | import { defineCollection } from 'astro:content' 4 | 5 | export const collections = { 6 | docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), 7 | } 8 | -------------------------------------------------------------------------------- /docs/src/content/docs/configuration.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Configuration 3 | description: An overview of all the configuration options supported by the Starlight Image Zoom plugin. 4 | --- 5 | 6 | The Starlight Image Zoom plugin can be configured inside the `astro.config.mjs` configuration file of your project: 7 | 8 | ```js {11} 9 | // astro.config.mjs 10 | import starlight from '@astrojs/starlight' 11 | import { defineConfig } from 'astro/config' 12 | import starlightImageZoom from 'starlight-image-zoom' 13 | 14 | export default defineConfig({ 15 | integrations: [ 16 | starlight({ 17 | plugins: [ 18 | starlightImageZoom({ 19 | // Configuration options go here. 20 | }), 21 | ], 22 | title: 'My Docs', 23 | }), 24 | ], 25 | }) 26 | ``` 27 | 28 | ## Configuration options 29 | 30 | The Starlight Image Zoom plugin accepts the following configuration options: 31 | 32 | ### `showCaptions` 33 | 34 | **Type:** `boolean` 35 | **Default:** `true` 36 | 37 | Whether an image alternate text should be displayed as a caption when the image is zoomed. 38 | Disabling this options is useful if you are already using another approach to display captions. 39 | -------------------------------------------------------------------------------- /docs/src/content/docs/customization.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Customization 3 | description: Learn how to customize the appearance of zoomed images. 4 | --- 5 | 6 | Zoomed images are displayed in a modal [`` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) which includes a backdrop displayed behind the image. 7 | 8 | ## Customizing the backdrop 9 | 10 | By default, the backdrop background color is an non-transparent color based on Starlight's color palette. 11 | You can customize this color using the `--starlight-image-zoom-backdrop-bg` CSS custom property. 12 | 13 | To learn how to add custom CSS styles to Starlight, refer to the [“Custom CSS styles”](https://starlight.astro.build/guides/css-and-tailwind/#custom-css-styles) guide in the Starlight documentation. 14 | 15 | ```css 16 | :root { 17 | /* Custom backdrop background color in dark mode. */ 18 | --starlight-image-zoom-backdrop-bg: oklch(0 0 0); 19 | } 20 | 21 | :root[data-theme='light'] { 22 | /* Custom backdrop background color in light mode. */ 23 | --starlight-image-zoom-backdrop-bg: oklch(100% 0 0); 24 | } 25 | ``` 26 | 27 | ## Customizing zoomed images using IDs 28 | 29 | When zooming images, the `id` attribute of the image is not copied to the zoomed image in the modal. 30 | If you are using the `id` attribute to identify images and customize their appearance using CSS, you can use the `data-zoom-id` attribute which will contain the `id` of the original image. 31 | 32 | For example, if your page includes Mermaid diagrams with IDs like `mermaid-1`, `mermaid-2`, etc., the following CSS snippet inverts the color samples of all the diagrams in dark mode: 33 | 34 | ```css 35 | :root[data-theme='dark'] img[id^='mermaid-'] { 36 | filter: invert(1); 37 | } 38 | ``` 39 | 40 | To apply the same effect to the zoomed images, you can use the `data-zoom-id` attribute in your CSS selector: 41 | 42 | ```css 43 | :root[data-theme='dark'] img[id^='mermaid-'], 44 | :root[data-theme='dark'] img[data-zoom-id^='mermaid-'] { 45 | filter: invert(1); 46 | } 47 | ``` 48 | -------------------------------------------------------------------------------- /docs/src/content/docs/demo.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Demo 3 | description: A list of examples showcasing the the Starlight Image Zoom plugin. 4 | --- 5 | 6 | The following examples showcase the Starlight Image Zoom plugin in action. 7 | 8 | ## Markdown 9 | 10 | import { Preview } from '@hideoo/starlight-plugins-docs-components' 11 | import ImageAttribution from '../../components/ImageAttribution.astro' 12 | 13 | 14 | 15 | ```md 16 | --- 17 | // src/content/docs/example.md 18 | title: My page title 19 | --- 20 | 21 | ![Ceramic mug on table](../../assets/mug.jpg) 22 | ``` 23 | 24 | 25 | 26 | ![Ceramic mug on table](../../assets/demo/mug.jpg) 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | ## `` 35 | 36 | import { Image } from 'astro:assets' 37 | import paper from '../../assets/demo/paper.jpg' 38 | 39 | 40 | 41 | ```mdx 42 | --- 43 | // src/content/docs/example.mdx 44 | title: My page title 45 | --- 46 | 47 | import { Image } from 'astro:assets' 48 | import paper from '../../assets/paper.jpg' 49 | 50 | Brown pencil on white printing paper 51 | ``` 52 | 53 | 54 | 55 | Brown pencil on white printing paper 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | ## `` 64 | 65 | import { Picture } from 'astro:assets' 66 | import blackboard from '../../assets/demo/blackboard.jpg' 67 | 68 | 69 | 70 | ```mdx 71 | --- 72 | // src/content/docs/example.mdx 73 | title: My page title 74 | --- 75 | 76 | import { Picture } from 'astro:assets' 77 | import blackboard from '../../assets/blackboard.jpg' 78 | 79 | 80 | ``` 81 | 82 | 83 | 84 | 85 | 86 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /docs/src/content/docs/getting-started.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | description: Learn how to add zoom capabilities to your documentation images with the Starlight Image Zoom plugin. 4 | --- 5 | 6 | A [Starlight](https://starlight.astro.build) plugin adding zoom capabilities to your documentation images. 7 | 8 | - Lightweight UI based on the [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) element 9 | - No client-side third-party dependencies 10 | - Markdown and MDX images support: Markdown syntax, HTML syntax, and the `` or `` components 11 | - Alternate text displayed as a caption 12 | - Accessible buttons to trigger zoom 13 | 14 | Check out the [demo](/demo/) for a preview of the plugin in action. 15 | 16 | ## Prerequisites 17 | 18 | You will need to have a Starlight website set up. 19 | If you don't have one yet, you can follow the ["Getting Started"](https://starlight.astro.build/getting-started) guide in the Starlight docs to create one. 20 | 21 | ## Installation 22 | 23 | import { Steps } from '@astrojs/starlight/components' 24 | import { PackageManagers } from '@hideoo/starlight-plugins-docs-components' 25 | 26 | 27 | 28 | 1. Starlight Image Zoom is a Starlight [plugin](https://starlight.astro.build/reference/plugins/) that you can install using your favorite package manager: 29 | 30 | 31 | 32 | 2. Configure the plugin in your Starlight [configuration](https://starlight.astro.build/reference/configuration/#plugins) in the `astro.config.mjs` file. 33 | 34 | ```diff lang="js" 35 | // astro.config.mjs 36 | import starlight from '@astrojs/starlight' 37 | import { defineConfig } from 'astro/config' 38 | +import starlightImageZoom from 'starlight-image-zoom' 39 | 40 | export default defineConfig({ 41 | integrations: [ 42 | starlight({ 43 | + plugins: [starlightImageZoom()], 44 | title: 'My Docs', 45 | }), 46 | ], 47 | }) 48 | ``` 49 | 50 | 3. [Start the development server](https://starlight.astro.build/getting-started/#start-the-development-server) to see the plugin in action. 51 | 52 | 53 | 54 | The Starlight Image Zoom plugin behavior can be tweaked using various [configuration options](/configuration). 55 | 56 | ## Component overrides 57 | 58 | The Starlight Image Zoom plugin uses a Starlight [component override](https://starlight.astro.build/guides/overriding-components/) for the [`MarkdownContent`](https://starlight.astro.build/reference/overrides/#markdowncontent) component to add zoom capabilities to images. 59 | 60 | If you have a custom `MarkdownContent` component override in your Starlight project, you will need to manually render the `ImageZoom` component from the Starlight Image Zoom plugin in your custom component: 61 | 62 | ```diff lang="astro" 63 | --- 64 | // src/components/overrides/MarkdownContent.astro 65 | import Default from '@astrojs/starlight/components/MarkdownContent.astro' 66 | +import ImageZoom from 'starlight-image-zoom/components/ImageZoom.astro' 67 | --- 68 | 69 | + 70 |

Custom content in the MarkdownContent override

71 | 72 | ``` 73 | -------------------------------------------------------------------------------- /docs/src/content/docs/ignoring-images.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Ignoring Images 3 | description: Learn how to ignore images from being zoomable when using the Starlight Image Zoom plugin. 4 | --- 5 | 6 | By default, all images in your documentation content are zoomable when using the Starlight Image Zoom plugin with a few exceptions. 7 | 8 | ## Exceptions 9 | 10 | The following images are ignored by the Starlight Image Zoom plugin: 11 | 12 | - Icons rendered using the Starlight [``](https://starlight.astro.build/guides/components/#icon) component 13 | - Images embedded in interactive elements such as buttons, links, etc. 14 | - Images embedded in elements with the [`not-content`](https://starlight.astro.build/guides/components/#compatibility-with-starlights-styles) CSS class 15 | 16 | ## Ignoring specific images 17 | 18 | The Starlight Image Zoom plugin will ignore images with the `data-zoom-off` attribute. You can add this attribute to any image to prevent it from being zoomable. 19 | 20 | import { Preview } from '@hideoo/starlight-plugins-docs-components' 21 | import { Image } from 'astro:assets' 22 | import algorithm from '../../assets/demo/algorithm.jpg' 23 | import ImageAttribution from '../../components/ImageAttribution.astro' 24 | 25 | 26 | 27 | ```mdx "data-zoom-off" 28 | --- 29 | // src/content/docs/example.mdx 30 | title: My page title 31 | --- 32 | 33 | import { Image } from 'astro:assets' 34 | import algorithm from '../../assets/demo/algorithm.jpg' 35 | 36 | A book with a diagram on it 37 | ``` 38 | 39 | 40 | 41 | A book with a diagram on it 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /docs/src/content/docs/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Starlight Image Zoom 3 | description: Starlight plugin adding zoom capabilities to your documentation images. 4 | head: 5 | - tag: title 6 | content: Starlight Image Zoom 7 | template: splash 8 | editUrl: false 9 | lastUpdated: false 10 | hero: 11 | tagline: Starlight plugin adding zoom capabilities to your documentation images. 12 | image: 13 | html: '🕵️' 14 | actions: 15 | - text: Getting Started 16 | link: /getting-started/ 17 | icon: rocket 18 | - text: Demo 19 | link: /demo 20 | icon: right-arrow 21 | variant: minimal 22 | --- 23 | 24 | import { Card, CardGrid } from '@astrojs/starlight/components' 25 | 26 | ## Next steps 27 | 28 | 29 | 30 | Check the [getting started guide](/getting-started/) for installation instructions. 31 | 32 | 33 | Test the plugin with the image below or check the [demo page](/demo) for more examples. 34 | 35 | 36 | 37 | import { Image } from 'astro:assets' 38 | import ImageAttribution from '../../components/ImageAttribution.astro' 39 | import bridge from '../../assets/demo/bridge.jpg' 40 | 41 | Man standing alone on hanging bridge 42 | 43 | 44 | -------------------------------------------------------------------------------- /docs/src/content/docs/resources/showcase.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Showcase 3 | description: Discover Starlight project using the Starlight Image Zoom plugin. 4 | --- 5 | 6 | import { ShowcaseIntro, Showcase } from '@hideoo/starlight-plugins-docs-components' 7 | 8 | 12 | 13 | ## Sites 14 | 15 | The Starlight Image Zoom plugin is already being used in production. These are some of the sites around the web: 16 | 17 | 51 | 52 | See all the [public project repos using Starlight Image Zoom on GitHub](https://github.com/hideoo/starlight-image-zoom/network/dependents). 53 | -------------------------------------------------------------------------------- /docs/src/content/docs/resources/starlight.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Starlight Plugins and Tools 3 | description: Discover other Starlight plugins, components and tools developed by HiDeoo. 4 | --- 5 | 6 | import { ResourcesIntro, Resources } from '@hideoo/starlight-plugins-docs-components' 7 | 8 | 9 | 10 | ## Plugins 11 | 12 | 13 | 14 | ## Components 15 | 16 | 17 | 18 | ## Tools 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/src/content/docs/tests/data-zoom-id.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: data-zoom-id 3 | pagefind: false 4 | --- 5 | 6 |

Heading

7 | 8 | Astro logo 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/src/content/docs/tests/hero.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hero 3 | template: splash 4 | hero: 5 | title: 'Test' 6 | tagline: This is a test to ensure that the hero image is not zoomable 7 | image: 8 | alt: Starlight logo 9 | file: ../../../assets/tests/starlight-light.png 10 | pagefind: false 11 | --- 12 | 13 | Test 14 | -------------------------------------------------------------------------------- /docs/src/content/docs/tests/large.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Large image 3 | pagefind: false 4 | --- 5 | 6 | Photo by Eilis Garvey on Unsplash 7 | 8 | ![a coffee shop with people sitting in the window](../../../assets/tests/large.jpg) 9 | -------------------------------------------------------------------------------- /docs/src/content/docs/tests/not-content.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CSS class not-content 3 | pagefind: false 4 | --- 5 | 6 |

Heading

7 | 8 | Astro logo 9 | 10 |
11 |

Heading

12 | 13 | Astro logo 14 |
15 | -------------------------------------------------------------------------------- /docs/src/content/docs/tests/shared-syntax.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Shared syntax Markdown 3 | slug: tests/shared-syntax-md 4 | pagefind: false 5 | --- 6 | 7 | An image using Markdown syntax: 8 | 9 | ![Starlight logo](../../../assets/tests/starlight-light.png) 10 | 11 | A remote image using Markdown syntax: 12 | 13 | ![Astro logo](https://astro.build/assets/press/astro-logo-light-gradient.png) 14 | 15 | An image using Markdown syntax with no alt text: 16 | 17 | ![](../../../assets/tests/starlight-light.png) 18 | 19 | A remote SVG image with no explicit width using Markdown syntax: 20 | 21 | ![D2 diagram](https://d2.atlas.lucas.tools/?script=KkktLuECBAAA__8%3D&) 22 | 23 | An image using HTML syntax with the `img` tag: 24 | 25 | Astro logo 26 | 27 | An image using HTML syntax with the `picture` tag: 28 | 29 | 30 | 34 | Astro logomark 38 | 39 | -------------------------------------------------------------------------------- /docs/src/content/docs/tests/shared-syntax.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Shared syntax MDX 3 | slug: tests/shared-syntax-mdx 4 | pagefind: false 5 | --- 6 | 7 | An image using Markdown syntax: 8 | 9 | ![Starlight logo](../../../assets/tests/starlight-light.png) 10 | 11 | A remote image using Markdown syntax: 12 | 13 | ![Astro logo](https://astro.build/assets/press/astro-logo-light-gradient.png) 14 | 15 | An image using Markdown syntax with no alt text: 16 | 17 | ![](../../../assets/tests/starlight-light.png) 18 | 19 | A remote SVG image with no explicit width using Markdown syntax: 20 | 21 | ![D2 diagram](https://d2.atlas.lucas.tools/?script=KkktLuECBAAA__8%3D&) 22 | 23 | An image using HTML syntax with the `img` tag: 24 | 25 | Astro logo 26 | 27 | An image using HTML syntax with the `picture` tag: 28 | 29 | 30 | 34 | Astro logomark 38 | 39 | -------------------------------------------------------------------------------- /docs/src/content/docs/tests/specific-syntax.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Specific syntax Markdown 3 | slug: tests/specific-syntax-md 4 | pagefind: false 5 | --- 6 | 7 | A code block with no frame: 8 | 9 | ```js frame="none" 10 | // src/example.js 11 | console.log('Hello, world!') 12 | ``` 13 | 14 | A remote image using Markdown syntax: 15 | 16 | ![Astro logo](https://astro.build/assets/press/astro-logo-light-gradient.png) 17 | 18 | A remote image using HTML syntax with the `img` tag: 19 | 20 | Astro logo 21 | -------------------------------------------------------------------------------- /docs/src/content/docs/tests/specific-syntax.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Specific syntax MDX 3 | slug: tests/specific-syntax-mdx 4 | pagefind: false 5 | --- 6 | 7 | import { Image, Picture } from 'astro:assets' 8 | import starlightLight from '../../../assets/tests/starlight-light.png' 9 | 10 | An image using the `` component: 11 | 12 | Starlight logo 13 | 14 | An image using the `` component: 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/src/content/docs/tests/text.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Image with text 3 | pagefind: false 4 | --- 5 | 6 | Photo by Luke Chesser on Unsplash 7 | 8 | ![Screenshot of GitHub on a desktop computer](../../../assets/tests/text.jpg) 9 | -------------------------------------------------------------------------------- /docs/src/content/docs/tests/zoom-off.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Data attribute zoom-off 3 | pagefind: false 4 | --- 5 | 6 | Astro logo 7 | 8 | Astro logo 9 | 10 | import { Image, Picture } from 'astro:assets' 11 | import starlightLight from '../../../assets/tests/starlight-light.png' 12 | 13 | Starlight logo 14 | -------------------------------------------------------------------------------- /docs/src/content/docs/tests/zoom.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Zoom 3 | pagefind: false 4 | --- 5 | 6 | An SVG image: 7 | 8 | ![Astro logo](../../../assets/tests/astro-light.svg) 9 | 10 | An icon using the `` component: 11 | 12 | import { Icon } from '@astrojs/starlight/components' 13 | 14 | 15 | 16 | An image in a link: 17 | 18 | [![Astro logo](../../../assets/tests/astro-light.svg)](https://astro.build) 19 | -------------------------------------------------------------------------------- /docs/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /docs/src/styles/custom.css: -------------------------------------------------------------------------------- 1 | .hero-html { 2 | --size: 12rem; 3 | 4 | font-size: var(--size); 5 | justify-content: center; 6 | line-height: var(--size); 7 | } 8 | 9 | h2 + div.card-grid.stagger { 10 | margin-bottom: 2rem; 11 | } 12 | -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import hideoo from '@hideoo/eslint-config' 2 | 3 | export default hideoo() 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starlight-image-zoom-monorepo", 3 | "version": "0.9.0", 4 | "license": "MIT", 5 | "description": "Starlight plugin adding zoom capabilities to your documentation images.", 6 | "author": "HiDeoo (https://hideoo.dev)", 7 | "type": "module", 8 | "scripts": { 9 | "test": "pnpm --stream -r test", 10 | "lint": "astro check --noSync && pnpm -r lint", 11 | "format": "prettier -w --cache --ignore-unknown .", 12 | "version": "pnpm changeset version && pnpm i --no-frozen-lockfile" 13 | }, 14 | "devDependencies": { 15 | "@astrojs/check": "^0.9.4", 16 | "@changesets/changelog-github": "^0.5.0", 17 | "@changesets/cli": "^2.27.10", 18 | "@hideoo/eslint-config": "^4.0.0", 19 | "@hideoo/prettier-config": "^2.0.0", 20 | "@hideoo/tsconfig": "^2.0.1", 21 | "astro": "^5.3.0", 22 | "eslint": "^9.17.0", 23 | "prettier": "^3.4.2", 24 | "prettier-plugin-astro": "^0.14.1", 25 | "typescript": "^5.7.2" 26 | }, 27 | "engines": { 28 | "node": ">=18" 29 | }, 30 | "packageManager": "pnpm@8.15.4", 31 | "private": true, 32 | "sideEffects": false, 33 | "keywords": [ 34 | "starlight", 35 | "image", 36 | "zoom", 37 | "documentation", 38 | "image-zoom" 39 | ], 40 | "homepage": "https://github.com/HiDeoo/starlight-image-zoom", 41 | "repository": { 42 | "type": "git", 43 | "url": "https://github.com/HiDeoo/starlight-image-zoom.git" 44 | }, 45 | "bugs": "https://github.com/HiDeoo/starlight-image-zoom/issues" 46 | } 47 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/.npmignore: -------------------------------------------------------------------------------- 1 | .eslintcache 2 | playwright.config.ts 3 | tests 4 | tsconfig.json 5 | tsconfig.tsbuildinfo 6 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # starlight-image-zoom 2 | 3 | ## 0.12.0 4 | 5 | ### Minor Changes 6 | 7 | - [#39](https://github.com/HiDeoo/starlight-image-zoom/pull/39) [`837e543`](https://github.com/HiDeoo/starlight-image-zoom/commit/837e5430e971345b1d6c737657d607dbef73948b) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Adds a new `data-zoom-id` attribute to zoomed images matching the `id` of the original image if it exists. 8 | 9 | This can be useful if you are relying on the `id` attribute to customize the appearance of images using CSS and also want to apply the same styles to zoomed images. 10 | 11 | ## 0.11.1 12 | 13 | ### Patch Changes 14 | 15 | - [#34](https://github.com/HiDeoo/starlight-image-zoom/pull/34) [`58dcce6`](https://github.com/HiDeoo/starlight-image-zoom/commit/58dcce681f52f0001c18adba15b4cb086cd14eb5) Thanks [@DaniFoldi](https://github.com/DaniFoldi)! - Moves `mdast-util-mdx-jsx` package to non-dev dependencies to prevent issues in monorepos with hoisting disabled. 16 | 17 | ## 0.11.0 18 | 19 | ### Minor Changes 20 | 21 | - [#32](https://github.com/HiDeoo/starlight-image-zoom/pull/32) [`02a52df`](https://github.com/HiDeoo/starlight-image-zoom/commit/02a52df958e0bf814f9e51e92d4ed0ce46dff53a) Thanks [@HiDeoo](https://github.com/HiDeoo)! - ⚠️ **BREAKING CHANGE:** The minimum supported version of Starlight is now version `0.32.0`. 22 | 23 | Please use the `@astrojs/upgrade` command to upgrade your project: 24 | 25 | ```sh 26 | npx @astrojs/upgrade 27 | ``` 28 | 29 | ## 0.10.1 30 | 31 | ### Patch Changes 32 | 33 | - [#30](https://github.com/HiDeoo/starlight-image-zoom/pull/30) [`98ae50e`](https://github.com/HiDeoo/starlight-image-zoom/commit/98ae50e5c4d9102991f146570a76844e816602ed) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Fixes an issue where SVG images were not being properly identified as SVGs when running in development mode. 34 | 35 | ## 0.10.0 36 | 37 | ### Minor Changes 38 | 39 | - [#27](https://github.com/HiDeoo/starlight-image-zoom/pull/27) [`e716c66`](https://github.com/HiDeoo/starlight-image-zoom/commit/e716c66dd585fe2fd78432add8d9895c8110131f) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Adds support for Astro v5, drops support for Astro v4. 40 | 41 | ⚠️ **BREAKING CHANGE:** The minimum supported version of Starlight is now `0.30.0`. 42 | 43 | Please follow the [upgrade guide](https://github.com/withastro/starlight/releases/tag/%40astrojs/starlight%400.30.0) to update your project. 44 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/README.md: -------------------------------------------------------------------------------- 1 |
2 |

starlight-image-zoom 🕵️

3 |

Starlight plugin adding zoom capabilities to your documentation images.

4 |
5 | 6 |
7 | 8 | Integration Status 9 | 10 | 11 | License 12 | 13 |
14 |
15 | 16 | ## Getting Started 17 | 18 | Want to get started immediately? Check out the [getting started guide](https://starlight-image-zoom.vercel.app/getting-started/) or check out the [demo](https://starlight-image-zoom.vercel.app/demo/) to see the plugin in action. 19 | 20 | ## Features 21 | 22 | A [Starlight](https://starlight.astro.build) plugin adding zoom capabilities to your documentation images. 23 | 24 | - Lightweight UI based on the [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) element 25 | - No client-side third-party dependencies 26 | - Markdown and MDX images support: Markdown syntax, HTML syntax, and the `` or `` components 27 | - Alternate text displayed as a caption 28 | - Accessible buttons to trigger zoom 29 | 30 | ## License 31 | 32 | Licensed under the MIT License, Copyright © HiDeoo. 33 | 34 | See [LICENSE](https://github.com/HiDeoo/starlight-image-zoom/blob/main/LICENSE) for more information. 35 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/components/ImageZoom.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import config from 'virtual:starlight-image-zoom-config' 3 | 4 | const dataAttributes: DOMStringMap = {} 5 | if (!config.showCaptions) dataAttributes['data-hide-caption'] = '' 6 | --- 7 | 8 | 9 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 | 97 | 98 | 168 | 169 | 458 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import hideoo from '@hideoo/eslint-config' 2 | 3 | export default hideoo({ 4 | files: ['**/*.astro/*'], 5 | rules: { 6 | 'no-unused-private-class-members': 'off', 7 | }, 8 | }) 9 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/index.ts: -------------------------------------------------------------------------------- 1 | import type { StarlightPlugin, StarlightUserConfig } from '@astrojs/starlight/types' 2 | import { AstroError } from 'astro/errors' 3 | import { z } from 'astro/zod' 4 | 5 | import { starlightImageZoomIntegration } from './libs/integration' 6 | 7 | const starlightImageZoomConfigSchema = z 8 | .object({ 9 | /** 10 | * Whether an image alternate text should be displayed as a caption when the image is zoomed. 11 | * Disabling this options is useful if you are already using another approach to display captions. 12 | * 13 | * @default true 14 | */ 15 | showCaptions: z.boolean().default(true), 16 | }) 17 | .default({}) 18 | 19 | export default function starlightImageZoomPlugin(userConfig?: StarlightImageZoomUserConfig): StarlightPlugin { 20 | const parsedConfig = starlightImageZoomConfigSchema.safeParse(userConfig) 21 | 22 | if (!parsedConfig.success) { 23 | throw new AstroError( 24 | `The provided plugin configuration is invalid.\n${parsedConfig.error.issues.map((issue) => issue.message).join('\n')}`, 25 | `See the error report above for more informations.\n\nIf you believe this is a bug, please file an issue at https://github.com/HiDeoo/starlight-image-zoom/issues/new/choose`, 26 | ) 27 | } 28 | 29 | return { 30 | name: 'starlight-image-zoom-plugin', 31 | hooks: { 32 | 'config:setup'({ addIntegration, config, updateConfig }) { 33 | const updatedConfig: Partial = { components: { ...config.components } } 34 | 35 | if (!updatedConfig.components) { 36 | updatedConfig.components = {} 37 | } 38 | 39 | if (!config.components?.MarkdownContent) { 40 | updatedConfig.components.MarkdownContent = 'starlight-image-zoom/overrides/MarkdownContent.astro' 41 | } 42 | 43 | addIntegration(starlightImageZoomIntegration(parsedConfig.data)) 44 | updateConfig(updatedConfig) 45 | }, 46 | }, 47 | } 48 | } 49 | 50 | export type StarlightImageZoomUserConfig = z.input 51 | export type StarlightImageZoomConfig = z.output 52 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/libs/constants.ts: -------------------------------------------------------------------------------- 1 | export const STARLIGHT_IMAGE_ZOOM_ZOOMABLE_TAG = 'starlight-image-zoom-zoomable' 2 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/libs/integration.ts: -------------------------------------------------------------------------------- 1 | import type { AstroIntegration } from 'astro' 2 | import rehypeRaw from 'rehype-raw' 3 | 4 | import type { StarlightImageZoomConfig } from '..' 5 | 6 | import { rehypeMetaString, rehypeStarlightImageZoom } from './rehype' 7 | import { vitePluginStarlightImageZoomConfig } from './vite' 8 | 9 | export function starlightImageZoomIntegration(config: StarlightImageZoomConfig): AstroIntegration { 10 | return { 11 | name: 'starlight-image-zoom-integration', 12 | hooks: { 13 | 'astro:config:setup': ({ updateConfig }) => { 14 | updateConfig({ 15 | markdown: { 16 | rehypePlugins: [rehypeMetaString, rehypeRaw, rehypeStarlightImageZoom], 17 | }, 18 | vite: { 19 | plugins: [vitePluginStarlightImageZoomConfig(config)], 20 | }, 21 | }) 22 | }, 23 | }, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/libs/rehype.ts: -------------------------------------------------------------------------------- 1 | import 'mdast-util-mdx-jsx' 2 | import type { Root } from 'hast' 3 | import { CONTINUE, EXIT, SKIP, visit } from 'unist-util-visit' 4 | import { visitParents } from 'unist-util-visit-parents' 5 | 6 | import { STARLIGHT_IMAGE_ZOOM_ZOOMABLE_TAG } from './constants' 7 | 8 | const elementTagNames = new Set(['img', 'picture']) 9 | const mdxJsxFlowElementNames = new Set(['img', 'picture', 'astro-image', 'Image', 'Picture']) 10 | 11 | export function rehypeStarlightImageZoom() { 12 | return function transformer(tree: Root) { 13 | visitParents(tree, ['element', 'mdxJsxFlowElement'], (node, parents) => { 14 | if (node.type !== 'element' && node.type !== 'mdxJsxFlowElement') return CONTINUE 15 | if (node.type === 'element' && !elementTagNames.has(node.tagName)) return CONTINUE 16 | if (node.type === 'mdxJsxFlowElement' && node.name && !mdxJsxFlowElementNames.has(node.name)) return CONTINUE 17 | 18 | // Skip images with the `data-zoom-off` attribute. 19 | if ( 20 | (node.type === 'element' && 'dataZoomOff' in node.properties) || 21 | (node.type === 'mdxJsxFlowElement' && 22 | node.attributes.some( 23 | (attribute) => attribute.type === 'mdxJsxAttribute' && attribute.name === 'data-zoom-off', 24 | )) 25 | ) { 26 | return SKIP 27 | } 28 | 29 | const isInvalidImage = parents.some((parent) => { 30 | return ( 31 | parent.type === 'element' && 32 | // Exclude images wrapped in an element with the CSS class `not-content`. 33 | (String(parent.properties['className']).includes('not-content') || 34 | // Exclude images wrapped in an interactive element. 35 | parent.tagName === 'button' || 36 | (parent.tagName === 'a' && 'href' in parent.properties)) 37 | ) 38 | }) 39 | 40 | if (isInvalidImage) return SKIP 41 | 42 | let alt = '' 43 | 44 | if (node.type === 'element' && node.tagName === 'img') { 45 | alt = String(node.properties['alt']).trim() 46 | } else if (node.type === 'element' && node.tagName === 'picture') { 47 | visit(node, 'element', (child) => { 48 | if (child.tagName !== 'img') { 49 | return CONTINUE 50 | } 51 | 52 | alt = String(child.properties['alt']).trim() 53 | return EXIT 54 | }) 55 | } else if (node.type === 'mdxJsxFlowElement') { 56 | const altAttribute = node.attributes.find( 57 | (attribute) => attribute.type === 'mdxJsxAttribute' && attribute.name === 'alt', 58 | ) 59 | alt = String(altAttribute?.value).trim() 60 | } 61 | 62 | const parent = parents.at(-1) 63 | const index = parent?.children.indexOf(node) 64 | 65 | if (!parent || index === undefined) return CONTINUE 66 | 67 | parent.children[index] = { 68 | type: 'element', 69 | tagName: STARLIGHT_IMAGE_ZOOM_ZOOMABLE_TAG, 70 | properties: {}, 71 | children: [ 72 | node, 73 | { 74 | type: 'element', 75 | tagName: 'button', 76 | properties: { 77 | 'aria-label': `Zoom image${alt.length > 0 ? `: ${alt}` : ''}`, 78 | class: 'starlight-image-zoom-control', 79 | }, 80 | children: [ 81 | { 82 | type: 'element', 83 | tagName: 'svg', 84 | properties: { 85 | 'aria-hidden': 'true', 86 | fill: 'currentColor', 87 | viewBox: '0 0 24 24', 88 | }, 89 | children: [ 90 | { 91 | type: 'element', 92 | tagName: 'use', 93 | properties: { 94 | href: '#starlight-image-zoom-icon-zoom', 95 | }, 96 | children: [], 97 | }, 98 | ], 99 | }, 100 | ], 101 | }, 102 | ], 103 | } 104 | 105 | return SKIP 106 | }) 107 | } 108 | } 109 | 110 | /** 111 | * rehype-raw strips the `meta` property from code blocks so we manually moved it to a `metastring` property which is 112 | * supported by expressive-code. 113 | * 114 | * @see https://github.com/syntax-tree/hast-util-raw/issues/13 115 | * @see https://github.com/expressive-code/expressive-code/blob/21fdaa441c89d6a6ac38f5d522b6b60741df2f5d/packages/rehype-expressive-code/src/utils.ts#L15 116 | */ 117 | export function rehypeMetaString() { 118 | return function (tree: Root) { 119 | visit(tree, ['element'], (node) => { 120 | if (node.type === 'element' && node.tagName === 'code' && node.data?.meta) { 121 | node.properties['metastring'] = node.data.meta 122 | } 123 | }) 124 | } 125 | } 126 | 127 | declare module 'hast' { 128 | interface Data { 129 | meta?: string 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/libs/vite.ts: -------------------------------------------------------------------------------- 1 | import type { ViteUserConfig } from 'astro' 2 | 3 | import type { StarlightImageZoomConfig } from '..' 4 | 5 | export function vitePluginStarlightImageZoomConfig(config: StarlightImageZoomConfig): VitePlugin { 6 | const moduleId = 'virtual:starlight-image-zoom-config' 7 | const resolvedModuleId = `\0${moduleId}` 8 | const moduleContent = `export default ${JSON.stringify(config)}` 9 | 10 | return { 11 | name: 'vite-plugin-starlight-image-zoom-config', 12 | load(id) { 13 | return id === resolvedModuleId ? moduleContent : undefined 14 | }, 15 | resolveId(id) { 16 | return id === moduleId ? resolvedModuleId : undefined 17 | }, 18 | } 19 | } 20 | 21 | type VitePlugin = NonNullable[number] 22 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/overrides/MarkdownContent.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import StarlightMarkdownContent from '@astrojs/starlight/components/MarkdownContent.astro' 3 | 4 | import ImageZoom from '../components/ImageZoom.astro' 5 | --- 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starlight-image-zoom", 3 | "version": "0.12.0", 4 | "license": "MIT", 5 | "description": "Starlight plugin adding zoom capabilities to your documentation images.", 6 | "author": "HiDeoo (https://hideoo.dev)", 7 | "type": "module", 8 | "exports": { 9 | ".": "./index.ts", 10 | "./components/ImageZoom.astro": "./components/ImageZoom.astro", 11 | "./overrides/MarkdownContent.astro": "./overrides/MarkdownContent.astro", 12 | "./package.json": "./package.json" 13 | }, 14 | "scripts": { 15 | "test": "playwright install --with-deps chromium && playwright test", 16 | "lint": "eslint . --cache --max-warnings=0" 17 | }, 18 | "dependencies": { 19 | "mdast-util-mdx-jsx": "^3.1.3", 20 | "rehype-raw": "^7.0.0", 21 | "unist-util-visit": "^5.0.0", 22 | "unist-util-visit-parents": "^6.0.1" 23 | }, 24 | "devDependencies": { 25 | "@playwright/test": "^1.49.1", 26 | "@types/hast": "^3.0.4" 27 | }, 28 | "peerDependencies": { 29 | "@astrojs/starlight": ">=0.32.0" 30 | }, 31 | "engines": { 32 | "node": ">=18" 33 | }, 34 | "packageManager": "pnpm@8.15.4", 35 | "publishConfig": { 36 | "access": "public", 37 | "provenance": true 38 | }, 39 | "sideEffects": false, 40 | "keywords": [ 41 | "starlight", 42 | "image", 43 | "zoom", 44 | "documentation", 45 | "image-zoom" 46 | ], 47 | "homepage": "https://github.com/HiDeoo/starlight-image-zoom", 48 | "repository": { 49 | "type": "git", 50 | "url": "https://github.com/HiDeoo/starlight-image-zoom.git", 51 | "directory": "packages/starlight-image-zoom" 52 | }, 53 | "bugs": "https://github.com/HiDeoo/starlight-image-zoom/issues" 54 | } 55 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from '@playwright/test' 2 | 3 | export default defineConfig({ 4 | forbidOnly: !!process.env['CI'], 5 | projects: [ 6 | { 7 | name: 'chromium', 8 | use: { ...devices['Desktop Chrome'], headless: true }, 9 | }, 10 | ], 11 | webServer: [ 12 | { 13 | command: 'pnpm build && pnpm preview', 14 | cwd: '../../docs', 15 | reuseExistingServer: !process.env['CI'], 16 | url: 'http://localhost:4321', 17 | }, 18 | { 19 | command: 'pnpm build:no-caption && pnpm preview:no-caption', 20 | cwd: '../../docs', 21 | reuseExistingServer: !process.env['CI'], 22 | url: 'http://localhost:4322/no-caption/', 23 | }, 24 | ], 25 | }) 26 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/tests/fixtures/TestPage.ts: -------------------------------------------------------------------------------- 1 | import type { Locator, Page } from '@playwright/test' 2 | 3 | export class TestPage { 4 | static #zoomedImageSelector = '.starlight-image-zoom-image' 5 | 6 | static async zoomImage(image: Locator) { 7 | await image.click() 8 | 9 | // Wait for the zoomed image to be stable. 10 | const zoomedImageLocator = image.page().locator(TestPage.#zoomedImageSelector) 11 | const zoomedImageHandle = await zoomedImageLocator.elementHandle() 12 | await zoomedImageHandle?.waitForElementState('stable') 13 | await zoomedImageHandle?.waitForElementState('stable') 14 | } 15 | 16 | constructor(public readonly page: Page) {} 17 | 18 | async goto(slug: string, noCaption = false) { 19 | await this.page.goto(`http://localhost:${noCaption ? '4322/no-caption' : '4321'}/tests/${slug.replace(/^\//, '')}`) 20 | await this.page.waitForLoadState('networkidle') 21 | } 22 | 23 | getNthImage(n: number) { 24 | // Note that `getByRole('img')` cannot be used as images with no alt attribute would not be found. 25 | return this.page.locator('.sl-markdown-content img').locator(`nth=${n}`) 26 | } 27 | 28 | getZoomedImage() { 29 | return this.page.locator(TestPage.#zoomedImageSelector) 30 | } 31 | 32 | zoomImage(image: Locator) { 33 | return TestPage.zoomImage(image) 34 | } 35 | 36 | closeZoomedImage() { 37 | return this.page.locator(TestPage.#zoomedImageSelector).click() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/tests/interaction.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from './test' 2 | 3 | test('adds a button to trigger the zoom of an image', async ({ testPage }) => { 4 | await testPage.goto('zoom') 5 | 6 | // Focus the first link in the table of contents. 7 | await testPage.page.locator('starlight-toc a').focus() 8 | // Tab to the first image associated button. 9 | await testPage.page.keyboard.down('Tab') 10 | 11 | expect(await testPage.page.evaluate(() => document.activeElement?.ariaLabel)).toBe('Zoom image: Astro logo') 12 | 13 | // Click the button to zoom the image. 14 | await testPage.page.locator('*:focus').click() 15 | 16 | await expect(testPage.getZoomedImage()).toBeAttached() 17 | }) 18 | 19 | test('adds a button to trigger the unzoom of an image', async ({ testPage }) => { 20 | await testPage.goto('zoom') 21 | 22 | await expect(testPage.getNthImage(0)).toBeZoomedAfterClick() 23 | 24 | expect(await testPage.page.evaluate(() => document.activeElement?.ariaLabel)).toBe('Unzoom image') 25 | 26 | // Click the button to unzoom the image. 27 | await testPage.page.locator('*:focus').click() 28 | 29 | await expect(testPage.getZoomedImage()).not.toBeAttached() 30 | }) 31 | 32 | test('removes the dialog after unzooming an image', async ({ testPage }) => { 33 | await testPage.goto('zoom') 34 | 35 | await expect(testPage.getNthImage(0)).toBeZoomedAfterClick() 36 | 37 | expect(await testPage.page.evaluate(() => document.activeElement?.ariaLabel)).toBe('Unzoom image') 38 | 39 | // Click the button to unzoom the image. 40 | await testPage.page.locator('*:focus').click() 41 | 42 | await expect(testPage.page.locator('dialog.starlight-image-zoom-dialog')).not.toBeAttached() 43 | }) 44 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/tests/no-caption.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from './test' 2 | 3 | test('does not add a caption if the `showCaptions` option is disabled', async ({ testPage }) => { 4 | await testPage.goto('zoom', true) 5 | 6 | await expect(testPage.getNthImage(0)).toBeZoomedAfterClick() 7 | }) 8 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/tests/shared-syntax.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from './test' 2 | 3 | const formats = ['md', 'mdx'] 4 | 5 | for (const format of formats) { 6 | const slug = `shared-syntax-${format}` 7 | 8 | test(`${format}: zooms an image using Markdown syntax`, async ({ testPage }) => { 9 | await testPage.goto(slug) 10 | 11 | await expect(testPage.getNthImage(0)).toBeZoomedAfterClick() 12 | }) 13 | 14 | test(`${format}: zooms a remote image using Markdown syntax`, async ({ testPage }) => { 15 | await testPage.goto(slug) 16 | 17 | await expect(testPage.getNthImage(1)).toBeZoomedAfterClick() 18 | }) 19 | 20 | test(`${format}: zooms an image using Markdown syntax with no caption`, async ({ testPage }) => { 21 | await testPage.goto(slug) 22 | 23 | await expect(testPage.getNthImage(2)).toBeZoomedAfterClick() 24 | }) 25 | 26 | test(`${format}: zooms an image using HTML syntax with the \`\` tag`, async ({ testPage }) => { 27 | await testPage.goto(slug) 28 | 29 | await expect(testPage.getNthImage(3)).toBeZoomedAfterClick() 30 | }) 31 | 32 | test(`${format}: zooms an image using HTML syntax with the \`\` tag`, async ({ testPage }) => { 33 | await testPage.goto(slug) 34 | 35 | await expect(testPage.getNthImage(4)).toBeZoomedAfterClick() 36 | }) 37 | 38 | test(`${format}: zooms the expected image when using HTML syntax with the \`\` tag`, async ({ 39 | testPage, 40 | }) => { 41 | await testPage.page.emulateMedia({ colorScheme: 'light' }) 42 | await testPage.goto(slug) 43 | 44 | const image = testPage.getNthImage(5) 45 | await testPage.zoomImage(image) 46 | 47 | expect(await testPage.getZoomedImage().getAttribute('src')).toMatch(/-dark.png$/) 48 | 49 | await testPage.closeZoomedImage() 50 | 51 | await testPage.page.emulateMedia({ colorScheme: 'dark' }) 52 | await testPage.page.reload() 53 | 54 | await testPage.zoomImage(image) 55 | 56 | expect(await testPage.getZoomedImage().getAttribute('src')).toMatch(/-light.png$/) 57 | }) 58 | 59 | test(`${format}: displays remote SVGs with no explicit width`, async ({ testPage }) => { 60 | await testPage.goto(slug) 61 | 62 | const image = testPage.getNthImage(3) 63 | const boundingBox = await image.boundingBox() 64 | 65 | expect(boundingBox?.width).not.toBe(0) 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/tests/specific-syntax.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from './test' 2 | 3 | test('mdx: zooms an image using the `` component', async ({ testPage }) => { 4 | await testPage.goto('specific-syntax-mdx') 5 | 6 | await expect(testPage.getNthImage(0)).toBeZoomedAfterClick() 7 | }) 8 | 9 | test('mdx: zooms an image using the `` component', async ({ testPage }) => { 10 | await testPage.goto('specific-syntax-mdx') 11 | 12 | await expect(testPage.getNthImage(1)).toBeZoomedAfterClick() 13 | }) 14 | 15 | test('md: preserves code block metadata', async ({ testPage }) => { 16 | await testPage.goto('specific-syntax-md') 17 | 18 | await expect(testPage.page.getByText('// src/example.js')).toBeVisible() 19 | }) 20 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/tests/test.ts: -------------------------------------------------------------------------------- 1 | import { expect as baseExpect, test as base, type Locator } from '@playwright/test' 2 | 3 | import { TestPage } from './fixtures/TestPage' 4 | 5 | export const test = base.extend({ 6 | testPage: async ({ page }, use) => { 7 | const testPage = new TestPage(page) 8 | 9 | await use(testPage) 10 | }, 11 | }) 12 | 13 | export const expect = baseExpect.extend({ 14 | async toBeZoomedAfterClick(image: Locator) { 15 | const page = image.page() 16 | 17 | const assertionName = 'toHaveZoomedImage' 18 | let matcherResult: MatcherResult | undefined 19 | let expected: unknown 20 | let pass = false 21 | 22 | await image.scrollIntoViewIfNeeded() 23 | 24 | let imageAlt = await image.getAttribute('alt') 25 | imageAlt = imageAlt?.trim() ?? '' 26 | 27 | await TestPage.zoomImage(image) 28 | 29 | try { 30 | const dialog = page.locator('dialog.starlight-image-zoom-dialog') 31 | 32 | // The dialog should be visible. 33 | expected = 'visible' 34 | await baseExpect(dialog).toBeVisible() 35 | 36 | // The zoomed image should be visible. 37 | expected = 'visible' 38 | await baseExpect(dialog.locator('.starlight-image-zoom-image')).toBeVisible() 39 | 40 | const captionLocator = dialog.locator('figcaption') 41 | const noCaption = await page.locator('starlight-image-zoom').getAttribute('data-hide-caption') 42 | 43 | if (imageAlt.length === 0 || noCaption !== null) { 44 | // If the image has no alt attribute, the caption should not be visible. 45 | expected = 'hidden' 46 | await baseExpect(captionLocator).not.toBeAttached() 47 | } else { 48 | // The caption should be the image's alt attribute. 49 | expected = imageAlt 50 | baseExpect(await captionLocator.textContent()).toBe(expected) 51 | } 52 | 53 | pass = true 54 | } catch (error) { 55 | if (isExpectError(error)) { 56 | matcherResult = error.matcherResult 57 | } 58 | } 59 | 60 | const message = () => 61 | `${this.utils.matcherHint(assertionName, undefined, undefined, { isNot: this.isNot })}\n\n` + 62 | // eslint-disable-next-line @typescript-eslint/no-base-to-string 63 | `Locator: ${String(image)}\n` + 64 | `Expected: ${pass ? (this.isNot ? 'not' : '') : ''}${this.utils.printExpected(expected)}\n${ 65 | matcherResult ? `Received: ${this.utils.printReceived(matcherResult.actual)}` : '' 66 | }` 67 | 68 | return { 69 | actual: matcherResult?.actual, 70 | expected, 71 | message, 72 | name: assertionName, 73 | pass, 74 | } 75 | }, 76 | }) 77 | 78 | function isExpectError(error: unknown): error is ExpectError { 79 | return 'matcherResult' in (error as ExpectError) 80 | } 81 | 82 | interface Fixtures { 83 | testPage: TestPage 84 | } 85 | 86 | interface ExpectError extends Error { 87 | matcherResult: MatcherResult 88 | } 89 | 90 | interface MatcherResult { 91 | actual?: unknown 92 | } 93 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/tests/zoom.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from './test' 2 | 3 | test('zooms an SVG image', async ({ testPage }) => { 4 | await testPage.goto('zoom') 5 | 6 | await expect(testPage.getNthImage(0)).toBeZoomedAfterClick() 7 | }) 8 | 9 | test('does not zoom an SVG image from the `` component', async ({ testPage }) => { 10 | await testPage.goto('zoom') 11 | 12 | const svg = testPage.page.locator('.sl-markdown-content p + svg:nth-of-type(1)') 13 | await svg.click() 14 | 15 | await expect(testPage.getZoomedImage()).not.toBeAttached() 16 | }) 17 | 18 | test('does not zoom an hero image', async ({ testPage }) => { 19 | await testPage.goto('hero') 20 | 21 | const heroImage = testPage.page.locator('.hero img') 22 | await heroImage.click() 23 | 24 | await expect(testPage.getZoomedImage()).not.toBeAttached() 25 | }) 26 | 27 | test('does not zoom an image inside a parent with the `not-content` CSS class', async ({ testPage }) => { 28 | await testPage.goto('not-content') 29 | 30 | const notContentImage = testPage.getNthImage(1) 31 | await notContentImage.click() 32 | 33 | await expect(testPage.getZoomedImage()).not.toBeAttached() 34 | }) 35 | 36 | test('does not zoom an image with the `zoom-off` data attribute', async ({ testPage }) => { 37 | await testPage.goto('zoom-off') 38 | 39 | const zoomOffImage = testPage.getNthImage(1) 40 | await zoomOffImage.click() 41 | 42 | await expect(testPage.getZoomedImage()).not.toBeAttached() 43 | }) 44 | 45 | test('does not zoom an image using the `` component with the `zoom-off` data attribute', async ({ 46 | testPage, 47 | }) => { 48 | await testPage.goto('zoom-off') 49 | 50 | const zoomOffImage = testPage.getNthImage(2) 51 | await zoomOffImage.click() 52 | 53 | await expect(testPage.getZoomedImage()).not.toBeAttached() 54 | }) 55 | 56 | test('does not zoom an SVG image inside an interactive element', async ({ testPage }) => { 57 | await testPage.goto('zoom') 58 | 59 | const linkImage = testPage.getNthImage(1) 60 | await linkImage.click() 61 | 62 | await expect(testPage.getZoomedImage()).not.toBeAttached() 63 | expect(testPage.page.url()).toBe('https://astro.build/') 64 | }) 65 | 66 | test('does not set an ID on the zoomed image', async ({ testPage }) => { 67 | await testPage.goto('data-zoom-id') 68 | 69 | const imageWithNoId = testPage.getNthImage(0) 70 | await imageWithNoId.click() 71 | 72 | const zoomedImage = testPage.getZoomedImage() 73 | 74 | expect(await zoomedImage.getAttribute('id')).toBe(null) 75 | expect(await zoomedImage.getAttribute('data-zoom-id')).toBe(null) 76 | }) 77 | 78 | test('preserves image ID in the `data-zoom-id` attribute', async ({ testPage }) => { 79 | await testPage.goto('data-zoom-id') 80 | 81 | const imageWithId = testPage.getNthImage(1) 82 | await imageWithId.click() 83 | 84 | const zoomedImage = testPage.getZoomedImage() 85 | 86 | expect(await zoomedImage.getAttribute('id')).toBe(null) 87 | expect(await zoomedImage.getAttribute('data-zoom-id')).toBe('astro-logo') 88 | }) 89 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/starlight-image-zoom/virtual.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'virtual:starlight-image-zoom-config' { 2 | const StarlightImageZoomConfig: import('./index').StarlightImageZoomConfig 3 | 4 | export default StarlightImageZoomConfig 5 | } 6 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'docs' 3 | - 'packages/*' 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@hideoo/tsconfig", 3 | "include": ["docs/.astro/types.d.ts", "**/*"] 4 | } 5 | --------------------------------------------------------------------------------