@leadertechie/personal-site-kit 0.1.0-alpha.2 → 0.1.0-alpha.20

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 (169) hide show
  1. package/README.md +94 -17
  2. package/dist/api/content-utils.d.ts +27 -0
  3. package/dist/api/content-utils.d.ts.map +1 -0
  4. package/dist/api/handlers/about-me.d.ts.map +1 -1
  5. package/dist/api/handlers/auth-handler.d.ts +2 -0
  6. package/dist/api/handlers/auth-handler.d.ts.map +1 -0
  7. package/dist/api/handlers/auth.d.ts +23 -0
  8. package/dist/api/handlers/auth.d.ts.map +1 -0
  9. package/dist/api/handlers/content-api.d.ts +0 -1
  10. package/dist/api/handlers/content-api.d.ts.map +1 -1
  11. package/dist/api/handlers/content.d.ts.map +1 -1
  12. package/dist/api/handlers/home.d.ts.map +1 -1
  13. package/dist/api/handlers/static-details.d.ts +1 -1
  14. package/dist/api/handlers/static-details.d.ts.map +1 -1
  15. package/dist/api/index.d.ts +2 -0
  16. package/dist/api/index.d.ts.map +1 -1
  17. package/dist/api/website-api.d.ts +1 -1
  18. package/dist/api/website-api.d.ts.map +1 -1
  19. package/dist/api.js +17 -2
  20. package/dist/assets/logo-placeholder.svg +21 -0
  21. package/dist/chunks/index-C1krnvU3.js +211 -0
  22. package/dist/chunks/index-DrnbjP2Q.js +2715 -0
  23. package/dist/chunks/site-store-CGV9c2DI.js +89 -0
  24. package/dist/chunks/{template-gGTkeOcA.js → template-DVy2k_na.js} +128 -90
  25. package/dist/chunks/website-api-CFRUPu0X.js +958 -0
  26. package/dist/index.js +42 -14
  27. package/dist/prerender/data-fetcher.d.ts +19 -0
  28. package/dist/prerender/data-fetcher.d.ts.map +1 -0
  29. package/dist/prerender/page-content.d.ts.map +1 -1
  30. package/dist/prerender/page-generators/about.d.ts +16 -0
  31. package/dist/prerender/page-generators/about.d.ts.map +1 -0
  32. package/dist/prerender/page-generators/base.d.ts +25 -0
  33. package/dist/prerender/page-generators/base.d.ts.map +1 -0
  34. package/dist/prerender/page-generators/blog-detail.d.ts +15 -0
  35. package/dist/prerender/page-generators/blog-detail.d.ts.map +1 -0
  36. package/dist/prerender/page-generators/blogs-list.d.ts +17 -0
  37. package/dist/prerender/page-generators/blogs-list.d.ts.map +1 -0
  38. package/dist/prerender/page-generators/home.d.ts +19 -0
  39. package/dist/prerender/page-generators/home.d.ts.map +1 -0
  40. package/dist/prerender/page-generators/index.d.ts +9 -0
  41. package/dist/prerender/page-generators/index.d.ts.map +1 -0
  42. package/dist/prerender/page-generators/not-found.d.ts +14 -0
  43. package/dist/prerender/page-generators/not-found.d.ts.map +1 -0
  44. package/dist/prerender/page-generators/stories-list.d.ts +17 -0
  45. package/dist/prerender/page-generators/stories-list.d.ts.map +1 -0
  46. package/dist/prerender/page-generators/story-detail.d.ts +15 -0
  47. package/dist/prerender/page-generators/story-detail.d.ts.map +1 -0
  48. package/dist/prerender/template.d.ts +3 -1
  49. package/dist/prerender/template.d.ts.map +1 -1
  50. package/dist/prerender/website-prerender.d.ts +6 -0
  51. package/dist/prerender/website-prerender.d.ts.map +1 -1
  52. package/dist/prerender.js +291 -145
  53. package/dist/shared/config/index.d.ts +1 -0
  54. package/dist/shared/config/index.d.ts.map +1 -1
  55. package/dist/shared/core/site-store.d.ts +1 -0
  56. package/dist/shared/core/site-store.d.ts.map +1 -1
  57. package/dist/shared/core/theme-toggle.d.ts.map +1 -1
  58. package/dist/shared/page-content.d.ts.map +1 -1
  59. package/dist/shared/router.d.ts +9 -3
  60. package/dist/shared/router.d.ts.map +1 -1
  61. package/dist/shared/website-ui.d.ts +23 -0
  62. package/dist/shared/website-ui.d.ts.map +1 -1
  63. package/dist/shared.js +6 -4
  64. package/dist/ui/about-me/index.d.ts +2 -10
  65. package/dist/ui/about-me/index.d.ts.map +1 -1
  66. package/dist/ui/about-me/styles.d.ts.map +1 -1
  67. package/dist/ui/admin/api.d.ts +16 -0
  68. package/dist/ui/admin/api.d.ts.map +1 -0
  69. package/dist/ui/admin/components/AboutMeSection.d.ts +7 -0
  70. package/dist/ui/admin/components/AboutMeSection.d.ts.map +1 -0
  71. package/dist/ui/admin/components/AdminSection.d.ts +13 -0
  72. package/dist/ui/admin/components/AdminSection.d.ts.map +1 -0
  73. package/dist/ui/admin/components/BlogsSection.d.ts +7 -0
  74. package/dist/ui/admin/components/BlogsSection.d.ts.map +1 -0
  75. package/dist/ui/admin/components/HomeSection.d.ts +7 -0
  76. package/dist/ui/admin/components/HomeSection.d.ts.map +1 -0
  77. package/dist/ui/admin/components/ImagesSection.d.ts +7 -0
  78. package/dist/ui/admin/components/ImagesSection.d.ts.map +1 -0
  79. package/dist/ui/admin/components/LoginForm.d.ts +9 -0
  80. package/dist/ui/admin/components/LoginForm.d.ts.map +1 -0
  81. package/dist/ui/admin/components/LogoSection.d.ts +7 -0
  82. package/dist/ui/admin/components/LogoSection.d.ts.map +1 -0
  83. package/dist/ui/admin/components/ProfileSection.d.ts +7 -0
  84. package/dist/ui/admin/components/ProfileSection.d.ts.map +1 -0
  85. package/dist/ui/admin/components/StaticSection.d.ts +9 -0
  86. package/dist/ui/admin/components/StaticSection.d.ts.map +1 -0
  87. package/dist/ui/admin/components/StoriesSection.d.ts +7 -0
  88. package/dist/ui/admin/components/StoriesSection.d.ts.map +1 -0
  89. package/dist/ui/admin/components/index.d.ts +11 -0
  90. package/dist/ui/admin/components/index.d.ts.map +1 -0
  91. package/dist/ui/admin/index.d.ts +27 -26
  92. package/dist/ui/admin/index.d.ts.map +1 -1
  93. package/dist/ui/admin/styles.d.ts.map +1 -1
  94. package/dist/ui/admin/types.d.ts +24 -0
  95. package/dist/ui/admin/types.d.ts.map +1 -0
  96. package/dist/ui/banner/index.d.ts.map +1 -1
  97. package/dist/ui/banner/styles.d.ts.map +1 -1
  98. package/dist/ui/blog-viewer/__tests__/blogviewer.test.d.ts +2 -0
  99. package/dist/ui/blog-viewer/__tests__/blogviewer.test.d.ts.map +1 -0
  100. package/dist/ui/blog-viewer/index.d.ts +25 -0
  101. package/dist/ui/blog-viewer/index.d.ts.map +1 -0
  102. package/dist/ui/blog-viewer/styles.d.ts +2 -0
  103. package/dist/ui/blog-viewer/styles.d.ts.map +1 -0
  104. package/dist/ui/footer/index.d.ts.map +1 -1
  105. package/dist/ui/footer/styles.d.ts.map +1 -1
  106. package/dist/ui/index.d.ts +2 -0
  107. package/dist/ui/index.d.ts.map +1 -1
  108. package/dist/ui/story-viewer/__tests__/storyviewer.test.d.ts +2 -0
  109. package/dist/ui/story-viewer/__tests__/storyviewer.test.d.ts.map +1 -0
  110. package/dist/ui/story-viewer/index.d.ts +25 -0
  111. package/dist/ui/story-viewer/index.d.ts.map +1 -0
  112. package/dist/ui/story-viewer/styles.d.ts +2 -0
  113. package/dist/ui/story-viewer/styles.d.ts.map +1 -0
  114. package/dist/ui.js +15 -3
  115. package/package.json +37 -13
  116. package/public/assets/logo-placeholder.svg +21 -0
  117. package/dist/chunks/index-BqixlS-2.js +0 -1157
  118. package/dist/chunks/website-api-CVsi-OLc.js +0 -596
  119. package/dist/ui/about-me/renderer.d.ts +0 -5
  120. package/dist/ui/about-me/renderer.d.ts.map +0 -1
  121. package/src/api/__tests__/info.test.ts +0 -44
  122. package/src/api/__tests__/utils.test.ts +0 -78
  123. package/src/api/handlers/about-me.ts +0 -99
  124. package/src/api/handlers/content-api.ts +0 -268
  125. package/src/api/handlers/content.ts +0 -72
  126. package/src/api/handlers/home.ts +0 -79
  127. package/src/api/handlers/info.ts +0 -12
  128. package/src/api/handlers/logo.ts +0 -55
  129. package/src/api/handlers/static-details.ts +0 -48
  130. package/src/api/index.ts +0 -7
  131. package/src/api/utils.ts +0 -16
  132. package/src/api/website-api.ts +0 -124
  133. package/src/index.ts +0 -4
  134. package/src/prerender/__tests__/page-content.test.ts +0 -54
  135. package/src/prerender/__tests__/template.test.ts +0 -54
  136. package/src/prerender/index.ts +0 -7
  137. package/src/prerender/page-content.ts +0 -263
  138. package/src/prerender/prerender.ts +0 -25
  139. package/src/prerender/template.ts +0 -65
  140. package/src/prerender/website-prerender.ts +0 -152
  141. package/src/shared/config/api.ts +0 -16
  142. package/src/shared/config/index.ts +0 -41
  143. package/src/shared/config/types.ts +0 -16
  144. package/src/shared/core/__tests__/theme-toggle.test.ts +0 -204
  145. package/src/shared/core/site-store.ts +0 -38
  146. package/src/shared/core/theme-toggle.ts +0 -118
  147. package/src/shared/index.ts +0 -17
  148. package/src/shared/interfaces/ifooter-link.ts +0 -4
  149. package/src/shared/interfaces/iroute.ts +0 -4
  150. package/src/shared/models/theme-variables.css +0 -25
  151. package/src/shared/page-content.ts +0 -210
  152. package/src/shared/router.ts +0 -241
  153. package/src/shared/runtime.ts +0 -11
  154. package/src/shared/template.ts +0 -35
  155. package/src/shared/website-ui.ts +0 -92
  156. package/src/styles/markdown.css +0 -129
  157. package/src/ui/about-me/api.ts +0 -12
  158. package/src/ui/about-me/index.ts +0 -155
  159. package/src/ui/about-me/renderer.ts +0 -7
  160. package/src/ui/about-me/styles.ts +0 -10
  161. package/src/ui/admin/index.ts +0 -492
  162. package/src/ui/admin/styles.ts +0 -317
  163. package/src/ui/banner/index.ts +0 -38
  164. package/src/ui/banner/styles.ts +0 -10
  165. package/src/ui/footer/index.ts +0 -37
  166. package/src/ui/footer/styles.ts +0 -9
  167. package/src/ui/index.ts +0 -4
  168. /package/{src/shared → dist}/styles/markdown.css +0 -0
  169. /package/{src → dist}/styles/theme.css +0 -0
package/dist/prerender.js CHANGED
@@ -1,24 +1,42 @@
1
1
  import { R2ContentLoader } from "@leadertechie/r2tohtml";
2
+ let cachedAssets = null;
3
+ let isDiscovering = false;
2
4
  async function getAssetPaths(baseSiteUrl) {
3
- const assetsUrl = `${baseSiteUrl}/cdn-assets.json`;
5
+ if (cachedAssets) return cachedAssets;
6
+ if (isDiscovering) {
7
+ return { js: "/assets/index.js", css: "/assets/index.css" };
8
+ }
9
+ isDiscovering = true;
4
10
  try {
11
+ const assetsUrl = `${baseSiteUrl}/cdn-assets.json`;
5
12
  const res = await fetch(assetsUrl);
6
13
  if (res.ok) {
7
14
  const data = await res.json();
8
- return { js: data.js, css: data.css };
15
+ cachedAssets = { js: data.js, css: data.css };
16
+ isDiscovering = false;
17
+ return cachedAssets;
9
18
  }
10
19
  } catch (e) {
11
20
  }
12
21
  try {
13
- const res = await fetch(`${baseSiteUrl}/?t=${Date.now()}`);
14
- const html = await res.text();
15
- const jsMatch = html.match(/src="(\/assets\/index-[^"]+\.js)"/);
16
- const cssMatch = html.match(/href="(\/assets\/index-[^"]+\.css)"/);
17
- return {
18
- js: jsMatch ? jsMatch[1] : "/assets/index.js",
19
- css: cssMatch ? cssMatch[1] : "/assets/index.css"
20
- };
22
+ const res = await fetch(`${baseSiteUrl}/index.html`);
23
+ if (res.ok) {
24
+ const html = await res.text();
25
+ const jsMatch = html.match(/src="([^"]*assets\/index-[^"]+\.js)"/);
26
+ const cssMatch = html.match(/href="([^"]*assets\/index-[^"]+\.css)"/);
27
+ if (jsMatch || cssMatch) {
28
+ cachedAssets = {
29
+ js: jsMatch ? jsMatch[1].startsWith("/") ? jsMatch[1] : "/" + jsMatch[1] : "/assets/index.js",
30
+ css: cssMatch ? cssMatch[1].startsWith("/") ? cssMatch[1] : "/" + cssMatch[1] : "/assets/index.css"
31
+ };
32
+ isDiscovering = false;
33
+ return cachedAssets;
34
+ }
35
+ }
21
36
  } catch (e) {
37
+ console.warn("Asset discovery failed:", e);
38
+ } finally {
39
+ isDiscovering = false;
22
40
  }
23
41
  return { js: "/assets/index.js", css: "/assets/index.css" };
24
42
  }
@@ -28,13 +46,23 @@ const createHtmlTemplate = async ({
28
46
  canonicalUrl,
29
47
  content,
30
48
  hydrationData = "",
31
- baseSiteUrl = ""
49
+ baseSiteUrl = "",
50
+ jsAsset,
51
+ cssAsset
32
52
  }) => {
33
- const { js: jsAsset, css: cssAsset } = await getAssetPaths(baseSiteUrl);
53
+ let js = jsAsset;
54
+ let css = cssAsset;
55
+ if (!js || !css) {
56
+ const effectiveBaseUrl = baseSiteUrl || new URL(canonicalUrl).origin;
57
+ const discovered = await getAssetPaths(effectiveBaseUrl);
58
+ js = js || discovered.js;
59
+ css = css || discovered.css;
60
+ }
34
61
  return `<!doctype html>
35
62
  <html lang="en" data-theme="light">
36
63
  <head>
37
64
  <meta charset="UTF-8" />
65
+ <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; connect-src 'self' https: http://localhost:* http://127.0.0.1:*; font-src 'self' data: https:;" />
38
66
  <link rel="icon" type="image/svg+xml" href="/api/logo" />
39
67
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
40
68
  <title>${title}</title>
@@ -43,8 +71,28 @@ const createHtmlTemplate = async ({
43
71
  <meta property="og:description" content="${description}" />
44
72
  <meta property="og:url" content="${canonicalUrl}" />
45
73
  <link rel="canonical" href="${canonicalUrl}" />
46
- <link rel="stylesheet" crossorigin href="${cssAsset}" />
47
- <script type="module" crossorigin src="${jsAsset}"><\/script>
74
+ <link rel="stylesheet" crossorigin href="${css}" />
75
+ <script type="module" crossorigin src="${js}"><\/script>
76
+ <!-- md2interact: client-side DOM interactions, CSS hydration, and event bus -->
77
+ <script type="module">
78
+ import { init } from '@leadertechie/md2interact';
79
+ document.addEventListener('DOMContentLoaded', () => {
80
+ init({
81
+ interactions: {
82
+ 'poll': { selector: '[data-interact="poll"]' },
83
+ 'live-update': { selector: '[data-interact="live-update"]' },
84
+ 'click-toggle': { selector: '[data-interact="click-toggle"]' },
85
+ 'infinite-scroll': { selector: '[data-interact="infinite-scroll"]' },
86
+ 'form-live': { selector: '[data-interact="form-live"]' }
87
+ },
88
+ cssHydration: {
89
+ inlineCritical: true,
90
+ layerInjection: true,
91
+ themeToggle: true
92
+ }
93
+ });
94
+ });
95
+ <\/script>
48
96
  </head>
49
97
  <body>
50
98
  ${hydrationData}
@@ -59,8 +107,29 @@ function getLoader(env) {
59
107
  if (!loader) {
60
108
  if (!env?.CONTENT_BUCKET) return null;
61
109
  loader = new R2ContentLoader(
62
- { bucket: env.CONTENT_BUCKET, cacheTTL: 5 * 60 * 1e3 },
63
- { md2html: { imagePathPrefix: "images/", styleOptions: { classPrefix: "md-", addHeadingIds: true } } }
110
+ {
111
+ bucket: env.CONTENT_BUCKET,
112
+ cacheTTL: 5 * 60 * 1e3,
113
+ cfCache: true,
114
+ // Enable Cloudflare edge cache tier
115
+ cfCacheTTL: 300,
116
+ // CF cache for 5 minutes
117
+ swrTTL: 30 * 60 * 1e3
118
+ // SWR window: 30 minutes
119
+ },
120
+ {
121
+ md2html: {
122
+ imagePathPrefix: "images/",
123
+ preserveRawHTML: true,
124
+ errorRecovery: "warn",
125
+ maxRecursionDepth: 50,
126
+ styleOptions: {
127
+ classPrefix: "md-",
128
+ addHeadingIds: true,
129
+ emitScopeAnchors: true
130
+ }
131
+ }
132
+ }
64
133
  );
65
134
  }
66
135
  return loader;
@@ -76,21 +145,11 @@ async function fetchProfile(env) {
76
145
  return null;
77
146
  }
78
147
  }
79
- async function fetchAboutMe(env) {
80
- try {
81
- const r2 = getLoader(env);
82
- if (!r2) return "";
83
- const result = await r2.getRendered("about-me.md");
84
- return result?.content || "";
85
- } catch {
86
- return "";
87
- }
88
- }
89
148
  async function fetchHome(env) {
90
149
  try {
91
150
  const r2 = getLoader(env);
92
151
  if (!r2) return "";
93
- const result = await r2.getRendered("home.md");
152
+ const result = await r2.getRendered("pages/home.md");
94
153
  return result?.content || "";
95
154
  } catch {
96
155
  return "";
@@ -137,8 +196,8 @@ async function fetchLatestStorySummaries(env, count = 3) {
137
196
  }
138
197
  }
139
198
  const generatePageContent = async (pathname, routes, footerLinks, env) => {
140
- const apiUrl = env?.apiUrl;
141
- const baseUrl = env?.baseUrl || "https://www.techieleader.com";
199
+ const apiUrl = env?.API_URL || env?.apiUrl || "https://api.example.com";
200
+ const baseUrl = env?.BASE_URL || env?.baseUrl || "https://www.example.com";
142
201
  let staticDetails = {
143
202
  siteTitle: "My Personal Website",
144
203
  copyright: "2026 My Personal Website",
@@ -151,133 +210,137 @@ const generatePageContent = async (pathname, routes, footerLinks, env) => {
151
210
  if (res.ok) staticDetails = await res.json();
152
211
  } catch (e) {
153
212
  }
154
- const logo = "/api/logo";
155
- const navLinks = routes.map((r) => `<a href="${r.link}" class="nav-link" data-route="${r.link === "/" ? "home" : r.text.toLowerCase()}">${r.text}</a>`).join("");
156
- const bannerTemplate = `
157
- <my-banner header="${staticDetails.siteTitle}" logo="${logo}">
158
- <theme-toggle slot="theme-switcher"></theme-toggle>
159
- <nav slot="nav-links">
160
- ${navLinks}
161
- </nav>
162
- </my-banner>`;
163
- const footerTemplate = `
164
- <my-footer
165
- copyright="${staticDetails.copyright}"
166
- footerlinks='${JSON.stringify(footerLinks)}'>
167
- </my-footer>`;
168
213
  let profile = null;
169
- let aboutMeContent = "";
170
214
  let homeContent = "";
171
215
  let latestBlogs = [];
172
216
  let latestStories = [];
173
217
  if (env?.CONTENT_BUCKET) {
174
- [profile, aboutMeContent, homeContent, latestBlogs, latestStories] = await Promise.all([
218
+ [profile, homeContent, latestBlogs, latestStories] = await Promise.all([
175
219
  fetchProfile(env),
176
- fetchAboutMe(env),
177
220
  fetchHome(env),
178
221
  fetchLatestBlogSummaries(env, 3),
179
222
  fetchLatestStorySummaries(env, 3)
180
223
  ]);
181
224
  }
182
225
  const name = profile?.name || "User";
183
- const title = profile?.title || "Professional";
184
- profile?.experience || "some";
185
- const canonicalUrl = new URL(pathname, baseUrl).toString();
226
+ const strategies = {
227
+ home: async () => {
228
+ const { HomePageGenerator } = await import("./chunks/index-C1krnvU3.js");
229
+ const generator = new HomePageGenerator();
230
+ return generator.generate({
231
+ routes,
232
+ footerLinks,
233
+ staticDetails,
234
+ apiUrl,
235
+ baseUrl,
236
+ pathname,
237
+ profile,
238
+ homeContent,
239
+ latestBlogs,
240
+ latestStories
241
+ });
242
+ },
243
+ about: async () => {
244
+ const { AboutPageGenerator } = await import("./chunks/index-C1krnvU3.js");
245
+ const generator = new AboutPageGenerator();
246
+ return generator.generate({
247
+ routes,
248
+ footerLinks,
249
+ staticDetails,
250
+ apiUrl,
251
+ baseUrl,
252
+ pathname,
253
+ profile
254
+ });
255
+ },
256
+ blogsList: async () => {
257
+ const { BlogsListPageGenerator } = await import("./chunks/index-C1krnvU3.js");
258
+ const generator = new BlogsListPageGenerator();
259
+ return generator.generate({
260
+ routes,
261
+ footerLinks,
262
+ staticDetails,
263
+ apiUrl,
264
+ baseUrl,
265
+ pathname,
266
+ latestBlogs,
267
+ name
268
+ });
269
+ },
270
+ storiesList: async () => {
271
+ const { StoriesListPageGenerator } = await import("./chunks/index-C1krnvU3.js");
272
+ const generator = new StoriesListPageGenerator();
273
+ return generator.generate({
274
+ routes,
275
+ footerLinks,
276
+ staticDetails,
277
+ apiUrl,
278
+ baseUrl,
279
+ pathname,
280
+ latestStories,
281
+ name
282
+ });
283
+ },
284
+ blogDetail: async (slug) => {
285
+ const { BlogDetailPageGenerator } = await import("./chunks/index-C1krnvU3.js");
286
+ const generator = new BlogDetailPageGenerator();
287
+ return generator.generate({
288
+ routes,
289
+ footerLinks,
290
+ staticDetails,
291
+ apiUrl,
292
+ baseUrl,
293
+ pathname,
294
+ slug
295
+ });
296
+ },
297
+ storyDetail: async (slug) => {
298
+ const { StoryDetailPageGenerator } = await import("./chunks/index-C1krnvU3.js");
299
+ const generator = new StoryDetailPageGenerator();
300
+ return generator.generate({
301
+ routes,
302
+ footerLinks,
303
+ staticDetails,
304
+ apiUrl,
305
+ baseUrl,
306
+ pathname,
307
+ slug
308
+ });
309
+ },
310
+ notFound: async () => {
311
+ const { NotFoundPageGenerator } = await import("./chunks/index-C1krnvU3.js");
312
+ const generator = new NotFoundPageGenerator();
313
+ return generator.generate({
314
+ routes,
315
+ footerLinks,
316
+ staticDetails,
317
+ apiUrl,
318
+ baseUrl,
319
+ pathname
320
+ });
321
+ }
322
+ };
186
323
  if (pathname === "/" || pathname === "") {
187
- const homeHtml = homeContent || `<h1>Welcome to ${name}</h1><p>Upload home.md to customize this page.</p>`;
188
- const blogGists = latestBlogs.map((b) => `<div class="gist-card"><a href="/blogs/${b.slug}"><h4>${b.title}</h4></a><p>${b.summary}</p><small>${b.date}</small></div>`).join("");
189
- const storyGists = latestStories.map((s) => `<div class="gist-card"><a href="/stories/${s.slug}"><h4>${s.title}</h4></a><p>${s.summary}</p><small>${s.date}</small></div>`).join("");
190
- const mainContent = `
191
- ${bannerTemplate}
192
- <main class="container container-wide column-layout">
193
- <div class="main-column">
194
- ${homeHtml}
195
- </div>
196
- <div class="sidebar-column">
197
- <h3>Recent Blogs</h3>
198
- ${blogGists || "<p>No blogs yet.</p>"}
199
- <h3 class="mt-2">Recent Stories</h3>
200
- ${storyGists || "<p>No stories yet.</p>"}
201
- </div>
202
- </main>
203
- ${footerTemplate}`;
204
- return {
205
- title: `${name} – ${title}`,
206
- description: `Welcome to ${name}'s personal website. Professional portfolio and content.`,
207
- canonicalUrl,
208
- content: mainContent
209
- };
324
+ return strategies.home();
210
325
  } else if (pathname === "/about-me") {
211
- const mainContent = `
212
- ${bannerTemplate}
213
- <main class="container container-narrow">
214
- <my-aboutme base-url="${apiUrl}"></my-aboutme>
215
- </main>
216
- ${footerTemplate}`;
217
- return {
218
- title: `About - ${name}`,
219
- description: `Learn more about ${name}'s experience and skills.`,
220
- canonicalUrl,
221
- content: mainContent
222
- };
326
+ return strategies.about();
223
327
  } else if (pathname === "/blogs" || pathname === "/blogs/") {
224
- const blogGists = latestBlogs.map((b) => `<div class="gist-card"><a href="/blogs/${b.slug}"><h4>${b.title}</h4></a><p>${b.summary}</p><small>${b.date}</small></div>`).join("");
225
- const mainContent = `
226
- ${bannerTemplate}
227
- <main class="container container-wide">
228
- <h1>Blogs</h1>
229
- <input type="text" placeholder="Search blogs..." class="search-input" />
230
- <div class="blog-list">
231
- ${blogGists || "<p>No blogs yet.</p>"}
232
- </div>
233
- </main>
234
- ${footerTemplate}`;
235
- return { title: `Blogs – ${name}`, description: "Read the latest blog posts.", canonicalUrl, content: mainContent };
328
+ return strategies.blogsList();
236
329
  } else if (pathname === "/stories" || pathname === "/stories/") {
237
- const storyGists = latestStories.map((s) => `<div class="gist-card"><a href="/stories/${s.slug}"><h4>${s.title}</h4></a><p>${s.summary}</p><small>${s.date}</small></div>`).join("");
238
- const mainContent = `
239
- ${bannerTemplate}
240
- <main class="container container-wide">
241
- <h1>Stories</h1>
242
- <input type="text" placeholder="Search stories..." class="search-input" />
243
- <div class="story-list">
244
- ${storyGists || "<p>No stories yet.</p>"}
245
- </div>
246
- </main>
247
- ${footerTemplate}`;
248
- return { title: `Stories – ${name}`, description: "Read the latest stories.", canonicalUrl, content: mainContent };
330
+ return strategies.storiesList();
249
331
  } else if (pathname.startsWith("/blogs/")) {
250
332
  const slug = pathname.replace("/blogs/", "").replace("/", "");
251
- const mainContent = `
252
- ${bannerTemplate}
253
- <main class="container container-narrow">
254
- <my-blog-viewer slug="${slug}"></my-blog-viewer>
255
- </main>
256
- ${footerTemplate}`;
257
- return { title: `Blog: ${slug}`, description: "Blog post", canonicalUrl, content: mainContent };
333
+ return strategies.blogDetail(slug);
258
334
  } else if (pathname.startsWith("/stories/")) {
259
335
  const slug = pathname.replace("/stories/", "").replace("/", "");
260
- const mainContent = `
261
- ${bannerTemplate}
262
- <main class="container container-narrow">
263
- <my-story-viewer slug="${slug}"></my-story-viewer>
264
- </main>
265
- ${footerTemplate}`;
266
- return { title: `Story: ${slug}`, description: "Story post", canonicalUrl, content: mainContent };
336
+ return strategies.storyDetail(slug);
267
337
  } else {
268
- const mainContent = `
269
- ${bannerTemplate}
270
- <main class="container container-narrow text-center">
271
- <h1>Page Not Found</h1>
272
- <p>The page you're looking for doesn't exist.</p>
273
- <p><a href="/">Return to home</a></p>
274
- </main>
275
- ${footerTemplate}`;
276
- return { title: "404 Not Found", description: "The page you requested could not be found.", canonicalUrl, content: mainContent };
338
+ return strategies.notFound();
277
339
  }
278
340
  };
279
341
  class WebsitePrerender {
280
342
  constructor(options = {}) {
343
+ this.cachedAssets = null;
281
344
  this.routes = options.routes || [
282
345
  { link: "/", text: "Home" },
283
346
  { link: "/blogs", text: "Blogs" },
@@ -293,12 +356,49 @@ class WebsitePrerender {
293
356
  this.siteTitle = options.siteTitle || "My Personal Website";
294
357
  this.copyright = options.copyright || "2026 My Personal Website";
295
358
  this.templateRenderer = options.templateRenderer || createHtmlTemplate;
359
+ this.apiHandler = options.apiHandler;
360
+ }
361
+ async discoverAssets(env, baseSiteUrl) {
362
+ if (this.cachedAssets) return this.cachedAssets;
363
+ try {
364
+ let html = "";
365
+ if (env.ASSETS) {
366
+ const res = await env.ASSETS.fetch(new Request("http://localhost/index.html"));
367
+ if (res.ok) html = await res.text();
368
+ }
369
+ if (!html) {
370
+ const res = await fetch(`${baseSiteUrl}/index.html`, {
371
+ headers: { "X-Asset-Discovery": "true" }
372
+ });
373
+ if (res.ok) html = await res.text();
374
+ }
375
+ if (html) {
376
+ const jsMatch = html.match(/src="([^"]*assets\/index-[^"]+\.js)"/);
377
+ const cssMatch = html.match(/href="([^"]*assets\/index-[^"]+\.css)"/);
378
+ if (jsMatch || cssMatch) {
379
+ this.cachedAssets = {
380
+ js: jsMatch ? jsMatch[1].startsWith("/") ? jsMatch[1] : "/" + jsMatch[1] : "/assets/index.js",
381
+ css: cssMatch ? cssMatch[1].startsWith("/") ? cssMatch[1] : "/" + cssMatch[1] : "/assets/index.css"
382
+ };
383
+ return this.cachedAssets;
384
+ }
385
+ }
386
+ } catch (e) {
387
+ console.warn("Asset discovery failed:", e);
388
+ }
389
+ return { js: "/assets/index.js", css: "/assets/index.css" };
296
390
  }
297
- async fetchStaticDetails(apiUrl) {
391
+ async fetchStaticDetails(apiUrl, env) {
298
392
  try {
299
- const res = await fetch(`${apiUrl}/api/static`);
300
- if (res.ok) {
301
- const data = await res.json();
393
+ let data;
394
+ if (this.apiHandler) {
395
+ const res = await this.apiHandler.fetch(new Request("http://localhost/api/static"), env, {});
396
+ if (res.ok) data = await res.json();
397
+ } else {
398
+ const res = await fetch(`${apiUrl}/api/static`);
399
+ if (res.ok) data = await res.json();
400
+ }
401
+ if (data) {
302
402
  this.siteTitle = data.siteTitle || this.siteTitle;
303
403
  this.copyright = data.copyright || this.copyright;
304
404
  const normalizeUrl = (url) => {
@@ -316,21 +416,53 @@ class WebsitePrerender {
316
416
  } catch (e) {
317
417
  }
318
418
  }
319
- async fetchAboutMeData(apiUrl) {
419
+ async fetchAboutMeData(apiUrl, env) {
320
420
  try {
321
- const res = await fetch(`${apiUrl}/api/about-me`);
322
- if (res.ok) return await res.json();
421
+ if (this.apiHandler) {
422
+ const res = await this.apiHandler.fetch(new Request("http://localhost/api/aboutme"), env, {});
423
+ if (res.ok) return await res.json();
424
+ } else {
425
+ const res = await fetch(`${apiUrl}/api/aboutme`);
426
+ if (res.ok) return await res.json();
427
+ }
323
428
  } catch (e) {
324
429
  }
325
430
  return null;
326
431
  }
327
432
  async fetch(request, env, ctx) {
328
- const apiUrl = env?.API_URL || "https://api.example.com";
329
- const baseSiteUrl = env?.BASE_SITE_URL || "https://site.example.com";
330
- await this.fetchStaticDetails(apiUrl);
331
433
  const url = new URL(request.url);
434
+ const apiUrl = env?.API_URL || `${url.origin}/api`;
435
+ const baseSiteUrl = env?.BASE_SITE_URL || url.origin;
436
+ if (url.pathname === "/admin" || url.pathname.startsWith("/admin/")) {
437
+ if (env.ASSETS) {
438
+ const assetRes = await env.ASSETS.fetch(new Request("http://localhost/index.html"));
439
+ if (assetRes.ok) return assetRes;
440
+ }
441
+ const templateResponse = await fetch(`${baseSiteUrl}/index.html`);
442
+ if (templateResponse.ok) {
443
+ return new Response(templateResponse.body, {
444
+ headers: { "content-type": "text/html" }
445
+ });
446
+ }
447
+ }
448
+ if (request.headers.get("X-Asset-Discovery") === "true") {
449
+ if (env.ASSETS) {
450
+ return env.ASSETS.fetch(new Request("http://localhost/index.html"));
451
+ }
452
+ }
453
+ await this.fetchStaticDetails(apiUrl, env);
332
454
  if (url.pathname.startsWith("/api/")) {
333
- return fetch(`${apiUrl}${url.pathname}${url.search}`);
455
+ if (this.apiHandler) {
456
+ return this.apiHandler.fetch(request, env, ctx);
457
+ }
458
+ return fetch(`${apiUrl}${url.pathname.replace(/^\/api/, "")}${url.search}`);
459
+ }
460
+ if (env.ASSETS && (url.pathname.startsWith("/assets/") || url.pathname === "/favicon.ico" || url.pathname === "/logo.png")) {
461
+ try {
462
+ const assetRes = await env.ASSETS.fetch(request);
463
+ if (assetRes.ok) return assetRes;
464
+ } catch (e) {
465
+ }
334
466
  }
335
467
  if (url.pathname.startsWith("/images/")) {
336
468
  const imageKey = url.pathname.slice(1);
@@ -340,12 +472,16 @@ class WebsitePrerender {
340
472
  return new Response(image.body, {
341
473
  headers: {
342
474
  "content-type": image.httpMetadata?.contentType || "image/jpeg",
343
- "cache-control": "public, max-age=86400"
475
+ "cache-control": "public, max-age=86400",
476
+ "access-control-allow-origin": "*"
344
477
  }
345
478
  });
346
479
  }
347
480
  } catch (e) {
348
481
  }
482
+ if (this.apiHandler) {
483
+ return this.apiHandler.fetch(request, env, ctx);
484
+ }
349
485
  return new Response("Not found", { status: 404 });
350
486
  }
351
487
  if (url.pathname.startsWith("/assets/") || url.pathname === "/logo.png" || url.pathname === "/favicon.ico") {
@@ -363,6 +499,9 @@ class WebsitePrerender {
363
499
  ico: "image/x-icon"
364
500
  };
365
501
  const contentType = contentTypes[ext || ""] || "application/octet-stream";
502
+ if (baseSiteUrl === url.origin && !env.ASSETS) {
503
+ return new Response("Asset not found", { status: 404 });
504
+ }
366
505
  const response = await fetch(`${baseSiteUrl}${path}`);
367
506
  if (response.ok) {
368
507
  return new Response(response.body, {
@@ -382,13 +521,20 @@ class WebsitePrerender {
382
521
  }
383
522
  let hydrationScript = "";
384
523
  if (url.pathname === "/about-me" || url.pathname === "/about-me/") {
385
- const aboutMeData = await this.fetchAboutMeData(apiUrl);
524
+ const aboutMeData = await this.fetchAboutMeData(apiUrl, env);
386
525
  if (aboutMeData) {
387
526
  hydrationScript = `<script>window.__HYDRATION_DATA__ = ${JSON.stringify(aboutMeData)};<\/script>`;
388
527
  }
389
528
  }
529
+ const assets = await this.discoverAssets(env, baseSiteUrl);
390
530
  const pageContent = await generatePageContent(url.pathname, this.routes, this.footerLinks, { ...env, apiUrl, siteTitle: this.siteTitle, copyright: this.copyright });
391
- const html = await this.templateRenderer({ ...pageContent, hydrationData: hydrationScript });
531
+ const html = await this.templateRenderer({
532
+ ...pageContent,
533
+ hydrationData: hydrationScript,
534
+ baseSiteUrl,
535
+ jsAsset: assets.js,
536
+ cssAsset: assets.css
537
+ });
392
538
  return new Response(html, {
393
539
  headers: {
394
540
  "content-type": "text/html",
@@ -1,5 +1,6 @@
1
1
  import { InfrastructureConfig, WebsiteConfig } from './types';
2
2
  export * from './types';
3
3
  export declare function initializeConfig(infra?: Partial<InfrastructureConfig>): Promise<WebsiteConfig>;
4
+ export declare function refreshConfig(): Promise<WebsiteConfig>;
4
5
  export declare function getConfig(): WebsiteConfig;
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAiB,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7E,cAAc,SAAS,CAAC;AAkBxB,wBAAsB,gBAAgB,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAgBpG;AAED,wBAAgB,SAAS,IAAI,aAAa,CAEzC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAiB,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7E,cAAc,SAAS,CAAC;AAkBxB,wBAAsB,gBAAgB,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAsBpG;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,aAAa,CAAC,CAW5D;AAED,wBAAgB,SAAS,IAAI,aAAa,CAEzC"}
@@ -11,6 +11,7 @@ export declare class SiteStore {
11
11
  }): Promise<WebsiteConfig>;
12
12
  subscribe(listener: (config: WebsiteConfig) => void): () => boolean;
13
13
  private notify;
14
+ refresh(): Promise<void>;
14
15
  getConfig(): WebsiteConfig;
15
16
  }
16
17
  //# sourceMappingURL=site-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"site-store.d.ts","sourceRoot":"","sources":["../../../src/shared/core/site-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAA+B,MAAM,WAAW,CAAC;AAEtF,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAY;IACnC,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,SAAS,CAAmD;IAEpE,OAAO;IAEP,MAAM,CAAC,WAAW,IAAI,SAAS;IAOzB,IAAI,CAAC,KAAK,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAMtD,SAAS,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI;IAMnD,OAAO,CAAC,MAAM;IAMd,SAAS,IAAI,aAAa;CAG3B"}
1
+ {"version":3,"file":"site-store.d.ts","sourceRoot":"","sources":["../../../src/shared/core/site-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA8C,MAAM,WAAW,CAAC;AAEtF,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAY;IACnC,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,SAAS,CAAmD;IAEpE,OAAO;IAEP,MAAM,CAAC,WAAW,IAAI,SAAS;IAOzB,IAAI,CAAC,KAAK,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAMtD,SAAS,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI;IAMnD,OAAO,CAAC,MAAM;IAMR,OAAO;IAQb,SAAS,IAAI,aAAa;CAG3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"theme-toggle.d.ts","sourceRoot":"","sources":["../../../src/shared/core/theme-toggle.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAY,SAAQ,WAAW;;IAQ1C,iBAAiB;IAIjB,oBAAoB;IAIpB,MAAM;IAkCN,0BAA0B;IAc1B,WAAW,aAOT;IAEF,kBAAkB,CAAC,KAAK,EAAE,MAAM;IAUhC,UAAU,IAAI,MAAM;IAgBpB,WAAW,IAAI,MAAM;IAQrB,wBAAwB,CAAC,KAAK,EAAE,MAAM;CAQvC"}
1
+ {"version":3,"file":"theme-toggle.d.ts","sourceRoot":"","sources":["../../../src/shared/core/theme-toggle.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAY,SAAQ,WAAW;;IAQ1C,iBAAiB;IAIjB,oBAAoB;IAIpB,MAAM;IAiCN,0BAA0B;IAc1B,WAAW,aAOT;IAEF,kBAAkB,CAAC,KAAK,EAAE,MAAM;IAUhC,UAAU,IAAI,MAAM;IAgBpB,WAAW,IAAI,MAAM;IAQrB,wBAAwB,CAAC,KAAK,EAAE,MAAM;CAQvC"}
@@ -1 +1 @@
1
- {"version":3,"file":"page-content.d.ts","sourceRoot":"","sources":["../../src/shared/page-content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;AAGpC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAUD,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,KAAG,MAGhD,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,UAAU,MAAM,EAChB,QAAQ,MAAM,EAAE,EAChB,aAAa,WAAW,EAAE,EAC1B,OAAO;IACL,KAAK,CAAC,EAAE,eAAe,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,KACA,WA0JF,CAAC"}
1
+ {"version":3,"file":"page-content.d.ts","sourceRoot":"","sources":["../../src/shared/page-content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;AAGpC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAwBD,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,KAAG,MAGhD,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,UAAU,MAAM,EAChB,QAAQ,MAAM,EAAE,EAChB,aAAa,WAAW,EAAE,EAC1B,OAAO;IACL,KAAK,CAAC,EAAE,eAAe,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,KACA,WA0JF,CAAC"}
@@ -2,17 +2,23 @@ import { WebsiteUI } from './website-ui';
2
2
  export declare class Router {
3
3
  private ui;
4
4
  private routes;
5
- private siteTitle;
6
- private copyright;
7
- private footerLinks;
8
5
  private apiUrl;
9
6
  private logo;
10
7
  private appElement;
11
8
  constructor(ui: WebsiteUI);
9
+ private get config();
10
+ private get siteTitle();
11
+ private get copyright();
12
+ private get footerLinks();
12
13
  init(appElementId?: string): void;
13
14
  private setupEventListeners;
14
15
  navigate(path: string): Promise<void>;
15
16
  private setPageMeta;
17
+ /**
18
+ * After rendering new content, reinitialize md2interact so it
19
+ * re-scans the DOM for interaction elements (poll, live-update, etc.)
20
+ */
21
+ private afterRender;
16
22
  private renderHomePage;
17
23
  private renderAboutMePage;
18
24
  private renderContentListPage;
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/shared/router.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,qBAAa,MAAM;IASL,OAAO,CAAC,EAAE;IARtB,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAuB;IACnC,OAAO,CAAC,UAAU,CAA4B;gBAE1B,EAAE,EAAE,SAAS;IA8B1B,IAAI,CAAC,YAAY,GAAE,MAAc;IAWxC,OAAO,CAAC,mBAAmB;IAmCd,QAAQ,CAAC,IAAI,EAAE,MAAM;IA6BlC,OAAO,CAAC,WAAW;YAgCL,cAAc;IAyB5B,OAAO,CAAC,iBAAiB;YAQX,qBAAqB;YAiBrB,uBAAuB;YAmBvB,eAAe;CAqB9B"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/shared/router.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,qBAAa,MAAM;IAML,OAAO,CAAC,EAAE;IALtB,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAuB;IACnC,OAAO,CAAC,UAAU,CAA4B;gBAE1B,EAAE,EAAE,SAAS;IAejC,OAAO,KAAK,MAAM,GAEjB;IAED,OAAO,KAAK,SAAS,GAEpB;IAED,OAAO,KAAK,SAAS,GAEpB;IAED,OAAO,KAAK,WAAW,GAatB;IAEM,IAAI,CAAC,YAAY,GAAE,MAAc;IAWxC,OAAO,CAAC,mBAAmB;IAmCd,QAAQ,CAAC,IAAI,EAAE,MAAM;IA6BlC,OAAO,CAAC,WAAW;IAgCnB;;;OAGG;IACH,OAAO,CAAC,WAAW;YAIL,cAAc;IAmC5B,OAAO,CAAC,iBAAiB;YASX,qBAAqB;YAkBrB,uBAAuB;YAoBvB,eAAe;CAiB9B"}