astro-html 0.19.0 → 0.20.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.
Files changed (3) hide show
  1. package/index.astro +91 -1
  2. package/index.js +60 -37
  3. package/package.json +1 -1
package/index.astro CHANGED
@@ -114,6 +114,8 @@ if (failed) {
114
114
  <html>
115
115
  <head>
116
116
  <title>Astro Email</title>
117
+
118
+ <meta name="viewport" content="width=device-width, initial-scale=1">
117
119
 
118
120
  <Fragment
119
121
  set:html={`
@@ -129,8 +131,14 @@ if (failed) {
129
131
  </head>
130
132
 
131
133
  <body>
134
+ <button id="mobile-menu-toggle" class="mobile-menu-toggle" aria-label="Toggle menu">
135
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
136
+ <path d="M3 12h18M3 6h18M3 18h18"/>
137
+ </svg>
138
+ </button>
139
+
132
140
  <div class="layout">
133
- <div class="sidebar">
141
+ <div class="sidebar" id="sidebar">
134
142
  <div class="sidebar-header">
135
143
  <h1>Templates</h1>
136
144
  <button id="download-assets" class="download-button" data-has-assets="true">
@@ -289,6 +297,25 @@ if (failed) {
289
297
  window.addEventListener("hashchange", () => {
290
298
  showTemplate(location.hash.substring(1));
291
299
  });
300
+
301
+ // Mobile menu toggle
302
+ const menuToggle = document.querySelector("#mobile-menu-toggle");
303
+ const sidebar = document.querySelector("#sidebar");
304
+
305
+ if (menuToggle && sidebar) {
306
+ menuToggle.addEventListener("click", () => {
307
+ sidebar.classList.toggle("open");
308
+ });
309
+
310
+ // Close sidebar when clicking a template link on mobile
311
+ document.querySelectorAll('.button a[href^="#"]').forEach(link => {
312
+ link.addEventListener("click", () => {
313
+ if (window.innerWidth <= 768) {
314
+ sidebar.classList.remove("open");
315
+ }
316
+ });
317
+ });
318
+ }
292
319
  </script>
293
320
  </div>
294
321
  </div>
@@ -364,6 +391,8 @@ if (failed) {
364
391
  box-sizing: border-box;
365
392
  border-right: 1px solid #e0e0e0;
366
393
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
394
+ max-height: 100vh;
395
+ overflow-y: auto;
367
396
  }
368
397
 
369
398
  .sidebar-header {
@@ -495,6 +524,62 @@ if (failed) {
495
524
  box-shadow: none;
496
525
  }
497
526
 
527
+ .mobile-menu-toggle {
528
+ display: none;
529
+ position: fixed;
530
+ bottom: 20px;
531
+ right: 20px;
532
+ z-index: 1000;
533
+ width: 56px;
534
+ height: 56px;
535
+ border-radius: 50%;
536
+ background: #007bff;
537
+ color: white;
538
+ border: none;
539
+ box-shadow: 0 4px 12px rgba(0, 123, 255, 0.4);
540
+ cursor: pointer;
541
+ align-items: center;
542
+ justify-content: center;
543
+ transition: transform 0.2s;
544
+ }
545
+
546
+ .mobile-menu-toggle:active {
547
+ transform: scale(0.95);
548
+ }
549
+
550
+ @media (max-width: 768px) {
551
+ .layout {
552
+ grid-template-columns: 1fr;
553
+ }
554
+
555
+ .sidebar {
556
+ position: fixed;
557
+ left: 0;
558
+ top: 0;
559
+ bottom: 0;
560
+ z-index: 999;
561
+ transform: translateX(-100%);
562
+ transition: transform 0.3s ease;
563
+ margin: 0;
564
+ border-radius: 0;
565
+ width: 280px;
566
+ max-width: 85vw;
567
+ }
568
+
569
+ .sidebar.open {
570
+ transform: translateX(0);
571
+ box-shadow: 4px 0 12px rgba(0, 0, 0, 0.15);
572
+ }
573
+
574
+ .mobile-menu-toggle {
575
+ display: flex;
576
+ }
577
+
578
+ .preview {
579
+ margin: 0;
580
+ }
581
+ }
582
+
498
583
  @media (prefers-color-scheme: dark) {
499
584
  button {
500
585
  border: 1px solid #4b4b4b;
@@ -516,6 +601,11 @@ if (failed) {
516
601
  .download-button:disabled {
517
602
  background: #6c757d;
518
603
  }
604
+
605
+ .mobile-menu-toggle {
606
+ background: #0066cc;
607
+ box-shadow: 0 4px 12px rgba(0, 102, 204, 0.4);
608
+ }
519
609
  }
520
610
  </style>
521
611
  </body>
package/index.js CHANGED
@@ -12,33 +12,6 @@ import child_process from "node:child_process";
12
12
  export default function email(options) {
13
13
  return {
14
14
  name: "email",
15
- configureServer(server) {
16
- server.middlewares.use((req, res, next) => {
17
- const url = req.url;
18
- if (!url) return next();
19
-
20
- // Extract the filename from any sub-path
21
- const segments = url.split("/").filter(Boolean);
22
- const filename = segments[segments.length - 1];
23
-
24
- // Only handle requests for files with extensions
25
- if (filename?.includes(".")) {
26
- const publicPath = path.join(process.cwd(), "public", filename);
27
-
28
- if (fs.existsSync(publicPath)) {
29
- const stat = fs.statSync(publicPath);
30
- if (stat.isFile()) {
31
- res.setHeader("Content-Length", stat.size);
32
- const stream = fs.createReadStream(publicPath);
33
- stream.pipe(res);
34
- return;
35
- }
36
- }
37
- }
38
-
39
- next();
40
- });
41
- },
42
15
  hooks: {
43
16
  "astro:config:setup": ({
44
17
  command,
@@ -57,6 +30,7 @@ export default function email(options) {
57
30
  plugins: [
58
31
  react(),
59
32
  optionsPlugin(false), // required for @astrojs/react/server.js to work.
33
+ publicAssetsPlugin(),
60
34
  ],
61
35
  build: {
62
36
  assetsInlineLimit: 1024 * 20, // inline all assets
@@ -78,12 +52,24 @@ export default function email(options) {
78
52
  "astro:build:done": async ({ dir, pages }) => {
79
53
  if (options.filename) {
80
54
  const manifest = [];
55
+ const publicDir = path.resolve("public");
56
+ const publicFiles = [];
57
+
58
+ if (fs.existsSync(publicDir)) {
59
+ const files = fs.readdirSync(publicDir);
60
+ for (const file of files) {
61
+ const publicFilePath = path.resolve(publicDir, file);
62
+ if (fs.statSync(publicFilePath).isFile()) {
63
+ publicFiles.push(file);
64
+ }
65
+ }
66
+ }
81
67
 
82
68
  for (const page of pages) {
83
69
  const pathname = page.pathname;
84
70
  const basename = pathname.split(".")[0];
85
71
 
86
- if (!pathname) continue; // index has none
72
+ if (!pathname) continue;
87
73
 
88
74
  const name =
89
75
  typeof options.filename === "string"
@@ -97,19 +83,24 @@ export default function email(options) {
97
83
 
98
84
  const files = [name];
99
85
 
100
- // Add all public files
101
- const publicDir = path.resolve("public");
102
- if (fs.existsSync(publicDir)) {
103
- const publicFiles = fs.readdirSync(publicDir);
86
+ // Copy public files to subdirectories if needed
87
+ const pathSegments = pathname.split("/").filter(Boolean);
88
+ if (pathSegments.length > 1) {
89
+ const subdirPath = path.resolve(
90
+ dir.pathname,
91
+ ...pathSegments.slice(0, -1),
92
+ );
93
+ fs.mkdirSync(subdirPath, { recursive: true });
94
+
104
95
  for (const publicFile of publicFiles) {
105
- const publicFilePath = path.resolve(publicDir, publicFile);
106
- if (fs.statSync(publicFilePath).isFile()) {
107
- files.push(publicFile);
108
- }
96
+ const targetPath = path.resolve(subdirPath, publicFile);
97
+ const sourcePath = path.resolve(publicDir, publicFile);
98
+ fs.copyFileSync(sourcePath, targetPath);
109
99
  }
110
100
  }
111
101
 
112
- // check if an .jpg file with the same name exists and copy it to dist too.
102
+ files.push(...publicFiles);
103
+
113
104
  const jpgPath = path.resolve("src/pages", `${pathname}.jpg`);
114
105
  if (fs.existsSync(jpgPath)) {
115
106
  const newJpgName =
@@ -167,3 +158,35 @@ function optionsPlugin(experimentalReactChildren) {
167
158
  },
168
159
  };
169
160
  }
161
+
162
+ /** @type {() => vite.Plugin} */
163
+ function publicAssetsPlugin() {
164
+ return {
165
+ name: "astro-html/public-assets",
166
+ configureServer(server) {
167
+ server.middlewares.use((req, res, next) => {
168
+ const url = req.url?.split("?")[0];
169
+ if (!url) return next();
170
+
171
+ const segments = url.split("/").filter(Boolean);
172
+ const filename = segments[segments.length - 1];
173
+
174
+ if (filename?.includes(".")) {
175
+ const publicPath = path.join(process.cwd(), "public", filename);
176
+
177
+ if (fs.existsSync(publicPath)) {
178
+ const stat = fs.statSync(publicPath);
179
+ if (stat.isFile()) {
180
+ res.setHeader("Content-Length", stat.size);
181
+ const stream = fs.createReadStream(publicPath);
182
+ stream.pipe(res);
183
+ return;
184
+ }
185
+ }
186
+ }
187
+
188
+ next();
189
+ });
190
+ },
191
+ };
192
+ }
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "type": "git",
11
11
  "url": "https://github.com/luckydye/astro-html.git"
12
12
  },
13
- "version": "0.19.0",
13
+ "version": "0.20.1",
14
14
  "main": "index.js",
15
15
  "scripts": {
16
16
  "dev": "astro dev",