@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.
- package/docs/advanced-configuration.mdx +31 -0
- package/docs/changelog.mdx +38 -0
- package/package.json +4 -2
- package/setup/vite-plugins.ts +153 -0
- package/utils/layoutHelper.ts +40 -6
|
@@ -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:
|
package/docs/changelog.mdx
CHANGED
|
@@ -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 
|
|
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 
|
|
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.
|
|
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
|
+
})
|
package/utils/layoutHelper.ts
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import type { CSSProperties } from "vue";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
|
14
|
-
|
|
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
|
-
//
|
|
20
|
-
|
|
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
|
/**
|