@simple-photo-gallery/theme-modern 2.0.2 → 2.0.3
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/package.json +2 -1
- package/src/features/themes/base-theme/components/gallery-section/GallerySectionHeader.astro +7 -2
- package/src/features/themes/base-theme/components/gallery-section/GallerySectionItem.astro +6 -1
- package/src/features/themes/base-theme/components/hero/Hero.astro +6 -1
- package/src/features/themes/base-theme/components/lightbox/PhotoSwipe.astro +1 -1
- package/src/features/themes/base-theme/layouts/MainLayout.astro +82 -0
- package/src/lib/markdown.ts +37 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simple-photo-gallery/theme-modern",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "Modern theme for Simple Photo Gallery",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Vladimir Haltakov, Tomasz Rusin",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"astro": "^5.11.0",
|
|
31
31
|
"astro-relative-links": "^0.4.2",
|
|
32
32
|
"blurhash": "^2.0.5",
|
|
33
|
+
"marked": "^16.4.0",
|
|
33
34
|
"photoswipe": "^5.4.4"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
package/src/features/themes/base-theme/components/gallery-section/GallerySectionHeader.astro
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { GallerySection as GallerySectionType } from '@simple-photo-gallery/common';
|
|
3
|
+
import { renderMarkdown } from '@/lib/markdown';
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
6
|
section: GallerySectionType;
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
const { section } = Astro.props;
|
|
10
|
+
|
|
11
|
+
// Parse description as Markdown if it exists
|
|
12
|
+
const parsedDescription = section.description ? await renderMarkdown(section.description) : '';
|
|
9
13
|
---
|
|
10
14
|
|
|
11
15
|
<div class="gallery-section__header">
|
|
12
16
|
<h2 class="gallery-section__header-title">{section.title}</h2>
|
|
13
|
-
<
|
|
17
|
+
{parsedDescription && <div class="gallery-section__header-description markdown-content" set:html={parsedDescription} />}
|
|
14
18
|
</div>
|
|
15
19
|
|
|
16
20
|
<style>
|
|
17
21
|
.gallery-section__header {
|
|
18
|
-
text-align: center;
|
|
19
22
|
margin-bottom: 4rem;
|
|
20
23
|
}
|
|
21
24
|
|
|
@@ -24,6 +27,7 @@ const { section } = Astro.props;
|
|
|
24
27
|
font-weight: 700;
|
|
25
28
|
margin-bottom: 1.5rem;
|
|
26
29
|
color: #111827;
|
|
30
|
+
text-align: center;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
.gallery-section__header-description {
|
|
@@ -32,5 +36,6 @@ const { section } = Astro.props;
|
|
|
32
36
|
max-width: 42rem;
|
|
33
37
|
margin: 0 auto;
|
|
34
38
|
line-height: 1.6;
|
|
39
|
+
text-align: left;
|
|
35
40
|
}
|
|
36
41
|
</style>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { getPhotoPath, getRelativePath } from '@/features/themes/base-theme/utils';
|
|
3
|
+
import { renderMarkdown } from '@/lib/markdown';
|
|
3
4
|
|
|
4
5
|
import type { MediaFile } from '@simple-photo-gallery/common/src/gallery';
|
|
5
6
|
|
|
@@ -10,6 +11,10 @@ interface Props {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
const { image, mediaBaseUrl, blurHash } = Astro.props;
|
|
14
|
+
|
|
15
|
+
// Parse caption as Markdown if it exists
|
|
16
|
+
const parsedCaption = image.alt ? await renderMarkdown(image.alt) : '';
|
|
17
|
+
const captionHtml = parsedCaption ? `<div class="image-caption markdown-content">${parsedCaption}</div>` : '';
|
|
13
18
|
---
|
|
14
19
|
|
|
15
20
|
<a
|
|
@@ -19,7 +24,7 @@ const { image, mediaBaseUrl, blurHash } = Astro.props;
|
|
|
19
24
|
data-pswp-width={image.width}
|
|
20
25
|
data-pswp-height={image.height}
|
|
21
26
|
data-pswp-type={image.type}
|
|
22
|
-
data-pswp-caption={
|
|
27
|
+
data-pswp-caption={captionHtml}
|
|
23
28
|
style={`--w: ${image.thumbnail?.width}; --h: ${image.thumbnail?.height}`}>
|
|
24
29
|
<canvas data-blur-hash={blurHash} width={32} height={32}></canvas>
|
|
25
30
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import HeroScrollToGalleryBtn from '@/features/themes/base-theme/components/hero/HeroScrollToGalleryBtn.astro';
|
|
3
|
+
import { renderMarkdown } from '@/lib/markdown';
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
6
|
title: string;
|
|
@@ -7,6 +8,9 @@ interface Props {
|
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
const { title, description } = Astro.props;
|
|
11
|
+
|
|
12
|
+
// Parse description as Markdown if it exists
|
|
13
|
+
const parsedDescription: string = description ? await renderMarkdown(description) : '';
|
|
10
14
|
---
|
|
11
15
|
|
|
12
16
|
<section class="hero">
|
|
@@ -63,7 +67,7 @@ const { title, description } = Astro.props;
|
|
|
63
67
|
<div class="hero__overlay"></div>
|
|
64
68
|
<div class="hero__content">
|
|
65
69
|
<h1 class="hero__title">{title}</h1>
|
|
66
|
-
{
|
|
70
|
+
{parsedDescription && <div class="hero__description markdown-content" set:html={parsedDescription} />}
|
|
67
71
|
<HeroScrollToGalleryBtn />
|
|
68
72
|
</div>
|
|
69
73
|
</section>
|
|
@@ -128,5 +132,6 @@ const { title, description } = Astro.props;
|
|
|
128
132
|
font-weight: 300;
|
|
129
133
|
opacity: 0.9;
|
|
130
134
|
line-height: 1.5;
|
|
135
|
+
color: white;
|
|
131
136
|
}
|
|
132
137
|
</style>
|
|
@@ -40,4 +40,86 @@ const { title, description, metadata, url } = Astro.props;
|
|
|
40
40
|
color: #333;
|
|
41
41
|
font-weight: 400;
|
|
42
42
|
}
|
|
43
|
+
|
|
44
|
+
/* Markdown content styles */
|
|
45
|
+
.markdown-content p {
|
|
46
|
+
margin-bottom: 1rem;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.markdown-content p:last-child {
|
|
50
|
+
margin-bottom: 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.markdown-content strong {
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.markdown-content em {
|
|
58
|
+
font-style: italic;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.markdown-content a {
|
|
62
|
+
color: #374151;
|
|
63
|
+
text-decoration: none;
|
|
64
|
+
font-weight: 500;
|
|
65
|
+
transition: color 0.2s ease;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.markdown-content a:hover {
|
|
69
|
+
color: #111827;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Light links on dark backgrounds (hero, captions) */
|
|
73
|
+
.hero__description.markdown-content a,
|
|
74
|
+
.image-caption.markdown-content a {
|
|
75
|
+
color: #d1d5db;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.hero__description.markdown-content a:hover,
|
|
79
|
+
.image-caption.markdown-content a:hover {
|
|
80
|
+
color: #f3f4f6;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.markdown-content ul,
|
|
84
|
+
.markdown-content ol {
|
|
85
|
+
margin: 1rem 0;
|
|
86
|
+
padding-left: 2rem;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.markdown-content li {
|
|
90
|
+
margin-bottom: 0.5rem;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.markdown-content li:last-child {
|
|
94
|
+
margin-bottom: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.markdown-content blockquote {
|
|
98
|
+
border-left: 3px solid currentColor;
|
|
99
|
+
padding-left: 1rem;
|
|
100
|
+
margin: 1rem 0;
|
|
101
|
+
font-style: italic;
|
|
102
|
+
opacity: 0.9;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.markdown-content code {
|
|
106
|
+
background-color: rgba(0, 0, 0, 0.1);
|
|
107
|
+
padding: 0.2em 0.4em;
|
|
108
|
+
border-radius: 3px;
|
|
109
|
+
font-family: 'Courier New', Courier, monospace;
|
|
110
|
+
font-size: 0.9em;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.markdown-content pre {
|
|
114
|
+
background-color: rgba(0, 0, 0, 0.05);
|
|
115
|
+
padding: 1rem;
|
|
116
|
+
border-radius: 5px;
|
|
117
|
+
overflow-x: auto;
|
|
118
|
+
margin: 1rem 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.markdown-content pre code {
|
|
122
|
+
background-color: transparent;
|
|
123
|
+
padding: 0;
|
|
124
|
+
}
|
|
43
125
|
</style>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { marked } from 'marked';
|
|
2
|
+
|
|
3
|
+
// Configure marked to only allow specific formatting options
|
|
4
|
+
const renderer = new marked.Renderer();
|
|
5
|
+
|
|
6
|
+
// Disable headings by rendering them as paragraphs
|
|
7
|
+
renderer.heading = ({ text }: { text: string }) => {
|
|
8
|
+
return '<p>' + text + '</p>\n';
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// Disable images
|
|
12
|
+
renderer.image = () => '';
|
|
13
|
+
|
|
14
|
+
// Disable HTML
|
|
15
|
+
renderer.html = () => '';
|
|
16
|
+
|
|
17
|
+
// Disable tables
|
|
18
|
+
renderer.table = () => '';
|
|
19
|
+
renderer.tablerow = () => '';
|
|
20
|
+
renderer.tablecell = () => '';
|
|
21
|
+
|
|
22
|
+
// Configure marked options
|
|
23
|
+
marked.use({
|
|
24
|
+
renderer: renderer,
|
|
25
|
+
breaks: true,
|
|
26
|
+
gfm: true,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Renders markdown with limited formatting options.
|
|
31
|
+
* Supported: paragraphs, bold, italic, lists, code blocks, blockquotes, links
|
|
32
|
+
* Disabled: headings (rendered as paragraphs), images, HTML, tables
|
|
33
|
+
*/
|
|
34
|
+
export async function renderMarkdown(markdown: string): Promise<string> {
|
|
35
|
+
if (!markdown) return '';
|
|
36
|
+
return await marked.parse(markdown);
|
|
37
|
+
}
|