@sp-days-framework/slidev-theme-sykehuspartner 1.1.0 → 1.1.1

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.
@@ -32,6 +32,37 @@ Layouts with images support:
32
32
  | `imagePosition` | string | varies | Position: `'left'` or `'right'` |
33
33
  | `imageBackgroundMode` | boolean | varies | Background vs foreground mode |
34
34
 
35
+ ### Image Path Resolution
36
+
37
+ The theme automatically resolves image paths in frontmatter, so you can use different path formats:
38
+
39
+ | Path Format | Example | Description |
40
+ |-------------|---------|-------------|
41
+ | **Relative** | `./image.png` | Relative to the markdown file |
42
+ | **Parent relative** | `../assets/img.png` | Navigate to parent directories |
43
+ | **Same directory** | `image.png` | File in same folder as markdown |
44
+ | **Public directory** | `/image.png` | Absolute path from `/public` folder |
45
+ | **External URL** | `https://...` | Remote images |
46
+
47
+ :::tip Relative Paths Recommended
48
+ Using relative paths (e.g., `./assets/diagram.png`) is recommended when working with the `docusaurus-plugin-slidev` integration. The theme's Vite plugin automatically resolves these paths and ensures images are correctly bundled during build.
49
+ :::
50
+
51
+ ```markdown title="Example: Using relative image paths"
52
+ ---
53
+ layout: image-left
54
+ imageSrc: ./assets/architecture.png
55
+ ---
56
+
57
+ # Architecture Overview
58
+
59
+ This diagram shows our system design.
60
+ ```
61
+
62
+ :::note Public Directory
63
+ For images in the `/public` directory, use absolute paths starting with `/`. These are served as-is without processing.
64
+ :::
65
+
35
66
  ## Global Theme Configuration
36
67
 
37
68
  Configure theme-wide settings in your presentation frontmatter:
@@ -19,6 +19,44 @@ All packages within `@sp-days-framework` use the same version number. In some ca
19
19
 
20
20
  ---
21
21
 
22
+ ## Version 1.1.1 ![Release](https://img.shields.io/badge/release-production-blue)
23
+
24
+ Relative image path support and security updates
25
+
26
+ <details>
27
+ <summary><strong>Details</strong> (2026 February 09)</summary>
28
+
29
+ ### New Features
30
+
31
+ - **Relative Image Paths**: Image-based layouts (`image`, `image-left`, `image-right`, `intro`, `about-me`) now support relative paths in frontmatter (e.g., `imageSrc: ./assets/diagram.png`)
32
+ - **Vite Plugin**: Added `setup/vite-plugins.ts` that automatically transforms relative image paths into proper imports during build
33
+ - **Build Compatibility**: Images referenced with relative paths are now correctly bundled and hashed in production builds
34
+
35
+ ### Improvements
36
+
37
+ - **Security**: Added `lodash-es` and `chevrotain` overrides to fix prototype pollution vulnerability (CVE in lodash-es < 4.17.23)
38
+ - **Dependencies**: Upgraded all dependencies to latest versions — @iconify-json/vscode-icons 1.2.40 and more
39
+
40
+ ### How It Works
41
+
42
+ The theme includes a Vite plugin that intercepts frontmatter and transforms relative image paths:
43
+
44
+ | Path Type | Example | Processed? |
45
+ |-----------|---------|------------|
46
+ | Relative | `./image.png`, `../assets/img.png` | ✅ Yes |
47
+ | Same directory | `image.png` | ✅ Yes |
48
+ | Absolute (public) | `/image.png` | ❌ No (served from public) |
49
+ | External URL | `https://...` | ❌ No |
50
+ | Data URI | `data:image/...` | ❌ No |
51
+
52
+ ### Migration
53
+
54
+ No changes required. Existing presentations using `/public` directory paths continue to work. You can now also use relative paths for better portability.
55
+
56
+ </details>
57
+
58
+ ---
59
+
22
60
  ## Version 1.1.0 ![Release](https://img.shields.io/badge/release-production-blue)
23
61
 
24
62
  Bug fixes for VSCode icons and layout improvements
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sp-days-framework/slidev-theme-sykehuspartner",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "A Slidev theme for Sykehuspartner presentations.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -42,7 +42,9 @@
42
42
  "@slidev/types": ">=52.0.0"
43
43
  },
44
44
  "overrides": {
45
- "dompurify": "^3.2.4"
45
+ "dompurify": "^3.2.4",
46
+ "lodash-es": ">=4.17.23",
47
+ "chevrotain": ">=11.1.1"
46
48
  },
47
49
  "files": [
48
50
  "layouts/",
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Vite Plugin Setup for Sykehuspartner Slidev Theme
3
+ *
4
+ * This plugin automatically resolves relative image paths in frontmatter
5
+ * so they work correctly after build. Without this, images referenced in
6
+ * frontmatter like `imageSrc: ./image.png` would break after build because
7
+ * Vite can't statically analyze frontmatter values.
8
+ *
9
+ * The plugin intercepts the frontmatter virtual modules generated by Slidev
10
+ * and transforms relative image paths into proper imports.
11
+ */
12
+ import { defineVitePluginsSetup } from '@slidev/types'
13
+ import type { Plugin } from 'vite'
14
+ import { resolve, dirname, isAbsolute } from 'node:path'
15
+ import { existsSync } from 'node:fs'
16
+
17
+ // Image properties that should be resolved in frontmatter
18
+ const IMAGE_PROPS = [
19
+ 'imageSrc',
20
+ 'background',
21
+ 'image',
22
+ ]
23
+
24
+ // Supported image extensions
25
+ const IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'avif', 'ico']
26
+ const IMAGE_EXT_PATTERN = IMAGE_EXTENSIONS.join('|')
27
+
28
+ export default defineVitePluginsSetup((options) => {
29
+ const { userRoot, data } = options
30
+
31
+ /**
32
+ * Plugin that transforms frontmatter image paths into imports
33
+ */
34
+ const frontmatterImagePlugin: Plugin = {
35
+ name: 'slidev-theme-sykehuspartner:frontmatter-images',
36
+ enforce: 'post', // Run after Slidev's loader generates the frontmatter module
37
+
38
+ transform(code, id) {
39
+ // Only process frontmatter virtual modules from Slidev
40
+ if (!id.includes('__slidev_') || !id.endsWith('.frontmatter')) {
41
+ return null
42
+ }
43
+
44
+ // Find the slide's markdown file path to resolve relative images
45
+ const slideMatch = id.match(/(.+?)__slidev_(\d+)\.frontmatter$/)
46
+ if (!slideMatch) return null
47
+
48
+ const mdFilePath = slideMatch[1]
49
+ const mdDir = dirname(mdFilePath)
50
+
51
+ // Regex to find relative image paths in frontmatter properties
52
+ // Matches: "imageSrc": "./image.png" or 'imageSrc': './image.png'
53
+ // Also matches paths without ./ prefix like "image.png"
54
+ const relativePathRegex = new RegExp(
55
+ `(["'])((?:\\.\\/|\\.\\.\\/|(?![/]|https?:|data:))[^"']*\\.(${IMAGE_EXT_PATTERN}))\\1`,
56
+ 'gi'
57
+ )
58
+
59
+ const matches = [...code.matchAll(relativePathRegex)]
60
+
61
+ if (matches.length === 0) {
62
+ return null
63
+ }
64
+
65
+ // Track unique imports to avoid duplicates
66
+ const imports: Map<string, string> = new Map()
67
+ let importIndex = 0
68
+ let newCode = code
69
+
70
+ for (const match of matches) {
71
+ const fullMatch = match[0]
72
+ const quote = match[1]
73
+ const relativePath = match[2]
74
+
75
+ // Resolve the absolute path to check if file exists
76
+ const absolutePath = resolve(mdDir, relativePath)
77
+
78
+ // Only transform if the file actually exists
79
+ if (!existsSync(absolutePath)) {
80
+ console.warn(`[slidev-theme-sykehuspartner] Image not found: ${relativePath} (resolved to ${absolutePath})`)
81
+ continue
82
+ }
83
+
84
+ // Get or create import variable name for this path
85
+ let varName = imports.get(relativePath)
86
+ if (!varName) {
87
+ varName = `__spImg${importIndex++}`
88
+ imports.set(relativePath, varName)
89
+ }
90
+
91
+ // Replace the string path with the import variable
92
+ // "imageSrc": "./image.png" becomes "imageSrc": __spImg0
93
+ newCode = newCode.replace(fullMatch, varName)
94
+ }
95
+
96
+ if (imports.size === 0) {
97
+ return null
98
+ }
99
+
100
+ // Generate import statements
101
+ const importStatements = Array.from(imports.entries())
102
+ .map(([path, varName]) => `import ${varName} from "${path}"`)
103
+ .join('\n')
104
+
105
+ // Prepend imports to the module code
106
+ const transformedCode = importStatements + '\n' + newCode
107
+
108
+ return {
109
+ code: transformedCode,
110
+ map: null, // Source map not needed for this transformation
111
+ }
112
+ },
113
+ }
114
+
115
+ /**
116
+ * Plugin that ensures images referenced in layouts are resolved correctly
117
+ * This handles the case where images are passed as props to layout components
118
+ */
119
+ const layoutImagePlugin: Plugin = {
120
+ name: 'slidev-theme-sykehuspartner:layout-images',
121
+ enforce: 'pre',
122
+
123
+ async resolveId(source, importer) {
124
+ // Only process if importer is a markdown slide file
125
+ if (!importer || !importer.includes('__slidev_')) {
126
+ return null
127
+ }
128
+
129
+ // Check if this looks like a relative image path
130
+ const isRelativePath = source.startsWith('./') || source.startsWith('../')
131
+ const isImageFile = new RegExp(`\\.(${IMAGE_EXT_PATTERN})$`, 'i').test(source)
132
+
133
+ if (isRelativePath && isImageFile) {
134
+ // Find the original markdown file path
135
+ const mdMatch = importer.match(/(.+?)__slidev_\d+/)
136
+ if (mdMatch) {
137
+ const mdDir = dirname(mdMatch[1])
138
+ const resolved = resolve(mdDir, source)
139
+ if (existsSync(resolved)) {
140
+ return resolved
141
+ }
142
+ }
143
+ }
144
+
145
+ return null
146
+ },
147
+ }
148
+
149
+ return [
150
+ frontmatterImagePlugin,
151
+ layoutImagePlugin,
152
+ ]
153
+ })
@@ -1,8 +1,20 @@
1
1
  import type { CSSProperties } from "vue";
2
2
 
3
3
  /**
4
- * Simple function to resolve image paths
5
- * Works for URLs, absolute paths, and relative paths
4
+ * Resolve image paths for use in layouts.
5
+ *
6
+ * This function handles multiple types of image paths:
7
+ * - Already resolved paths (from Vite plugin transformation) - returned as-is
8
+ * - Full URLs (http/https) - returned as-is
9
+ * - Data URLs (data:) - returned as-is
10
+ * - Blob URLs (blob:) - returned as-is
11
+ * - Root-relative paths (/path) - returned as-is
12
+ * - Colors (#hex, rgb) - returned as-is
13
+ * - Relative paths (./path, path) - converted to root-relative
14
+ *
15
+ * When the vite-plugins.ts is active, relative paths in frontmatter are
16
+ * automatically transformed into resolved import URLs before reaching
17
+ * this function, so they'll be handled by the "already resolved" case.
6
18
  */
7
19
  export function getImageUrl(path?: string): string {
8
20
  if (!path) return "";
@@ -10,14 +22,36 @@ export function getImageUrl(path?: string): string {
10
22
  // If it's a color, don't process it
11
23
  if (path.startsWith("#") || path.startsWith("rgb")) return path;
12
24
 
13
- // If it's a full URL, use it as is
14
- if (path.match(/^https?:\/\//)) return path;
25
+ // If it's already a resolved URL (from Vite import), use as-is
26
+ // This includes:
27
+ // - Data URLs: data:image/...
28
+ // - Blob URLs: blob:...
29
+ // - Full URLs: http:// or https://
30
+ // - Vite asset URLs: /assets/image.hash.png or /@fs/...
31
+ if (
32
+ path.startsWith("data:") ||
33
+ path.startsWith("blob:") ||
34
+ path.match(/^https?:\/\//) ||
35
+ path.startsWith("/@fs/") ||
36
+ path.match(/^\/assets\//)
37
+ ) {
38
+ return path;
39
+ }
15
40
 
16
41
  // If it's a root-relative path (starts with /), use it as is
17
42
  if (path.startsWith("/")) return path;
18
43
 
19
- // Otherwise, treat as relative path and add leading slash
20
- return `/${path}`;
44
+ // For relative paths, try to use the base URL if available
45
+ // This handles the case where images are in the public folder
46
+ const baseUrl = typeof import.meta !== 'undefined'
47
+ ? (import.meta.env?.BASE_URL || '/')
48
+ : '/';
49
+
50
+ // Remove ./ prefix if present
51
+ const cleanPath = path.startsWith("./") ? path.slice(2) : path;
52
+
53
+ // Return with base URL
54
+ return `${baseUrl}${cleanPath}`;
21
55
  }
22
56
 
23
57
  /**