@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simple-photo-gallery/theme-modern",
3
- "version": "2.0.2",
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": {
@@ -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
- <p class="gallery-section__header-description">{section.description}</p>
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={image.alt ? `<p class="image-caption">${image.alt}</p>` : ''}
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
- {description && <p class="hero__description">{description}</p>}
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>
@@ -81,7 +81,7 @@
81
81
  }
82
82
 
83
83
  .pswp__caption .image-caption {
84
- text-align: center;
84
+ text-align: left;
85
85
  background: rgba(128, 128, 128, 0.3);
86
86
  backdrop-filter: blur(8px);
87
87
  -webkit-backdrop-filter: blur(8px);
@@ -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
+ }