@leadertechie/personal-site-kit 0.1.0-alpha.15 → 0.1.0-alpha.17

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @leadertechie/personal-site-kit
2
2
 
3
- A high-performance, modular engine for building personal websites and professional portfolios. Powered by Cloudflare Workers, R2 Storage, and Lit Web Components.
3
+ A high-performance, modular engine for building personal websites and professional portfolios. Powered by Cloudflare Workers, R2 Storage, Lit Web Components, and the **@leadertechie** ecosystem.
4
4
 
5
5
  ## Features
6
6
 
@@ -8,18 +8,21 @@ A high-performance, modular engine for building personal websites and profession
8
8
  - **🛠️ 4-Section Engine**: Fully integrated logic for **API**, **UI**, **Prerender**, and **Admin**.
9
9
  - **🎨 Modern Layout**: Semantic grid system with responsive `main-column` and `sidebar-column` components.
10
10
  - **🌓 Theme Engine**: Built-in light/dark mode support with persistent user preferences.
11
- - **📝 Content Driven**: Dynamic Markdown rendering with frontmatter support via `@leadertechie/md2html`.
11
+ - **📝 Content Driven**: Dynamic Markdown rendering with frontmatter support via `@leadertechie/md2html` (v2).
12
12
  - **⚙️ Remote Config**: Manage site settings (title, copyright, social links) directly from R2 without re-deploying.
13
13
  - **🔍 SEO Optimized**: Server-side prerendering ensures search engines see your content perfectly.
14
+ - **🔄 SWR Caching**: Stale-while-revalidate caching via `@leadertechie/r2tohtml` for optimal performance.
15
+ - **⚡ Client-Side Interactivity**: DOM interaction patterns (poll, live-update, click-toggle, infinite-scroll, form-live) via `@leadertechie/md2interact`.
16
+ - **🎯 CSS Hydration**: Inline critical CSS, layer injection, and theme toggle support.
14
17
 
15
18
  ## Architecture
16
19
 
17
20
  The kit is divided into four logical subpaths:
18
21
 
19
- 1. **/shared**: Common interfaces, types, and the reactive `SiteStore`.
20
- 2. **/ui**: Web components for the Banner, Footer, About Me, and Admin Portal.
21
- 3. **/api**: Standard handlers for Cloudflare Workers to manage R2 content.
22
- 4. **/prerender**: SEO engine to generate static HTML for crawlers.
22
+ 1. **/shared**: Common interfaces, types, the reactive `SiteStore`, and the `WebsiteUI` bootstrap.
23
+ 2. **/ui**: Web components for the Banner, Footer, About Me, Blog Viewer, Story Viewer, and Admin Portal.
24
+ 3. **/api**: Standard handlers for Cloudflare Workers to manage R2 content with v2 caching.
25
+ 4. **/prerender**: SEO engine to generate static HTML for crawlers with SWR caching.
23
26
 
24
27
  ## Installation
25
28
 
@@ -29,26 +32,100 @@ npm install @leadertechie/personal-site-kit
29
32
 
30
33
  ## Quick Start (Usage)
31
34
 
32
- ### 1. API Worker (`api.ts`)
35
+ ### 1. API Worker (`api/index.ts`)
36
+
33
37
  ```typescript
34
- import { handleAPIRoute } from '@leadertechie/personal-site-kit/api';
38
+ import { WebsiteAPI } from '@leadertechie/personal-site-kit/api';
39
+
40
+ export default new WebsiteAPI();
41
+ ```
42
+
43
+ ### 2. UI Entry (`ui/index.ts`)
35
44
 
36
- export default {
37
- async fetch(request, env) {
38
- return handleAPIRoute(request, env);
45
+ ```typescript
46
+ import { WebsiteUI } from '@leadertechie/personal-site-kit/shared';
47
+ import '@leadertechie/personal-site-kit/styles/theme.css';
48
+ import '@leadertechie/personal-site-kit/ui';
49
+
50
+ // Bootstrap the UI — md2interact is automatically initialized
51
+ WebsiteUI.getInstance({
52
+ apiUrl: window.location.origin,
53
+ // Optional: configure md2interact interactions
54
+ interactConfig: {
55
+ interactions: {
56
+ 'poll': { selector: '[data-interact="poll"]' },
57
+ 'live-update': { selector: '[data-interact="live-update"]' },
58
+ 'click-toggle': { selector: '[data-interact="click-toggle"]' },
59
+ 'infinite-scroll': { selector: '[data-interact="infinite-scroll"]' },
60
+ 'form-live': { selector: '[data-interact="form-live"]' }
61
+ },
62
+ cssHydration: {
63
+ inlineCritical: true,
64
+ layerInjection: true,
65
+ themeToggle: true
66
+ }
39
67
  }
40
- };
68
+ }).bootstrap();
41
69
  ```
42
70
 
43
- ### 2. UI Entry (`main.ts`)
71
+ ### 3. Prerender (`prerender/index.ts`)
72
+
44
73
  ```typescript
45
- import { SiteStore } from '@leadertechie/personal-site-kit/shared';
46
- import '@leadertechie/personal-site-kit/ui/banner';
74
+ import { WebsitePrerender } from '@leadertechie/personal-site-kit/prerender';
47
75
 
48
- const store = SiteStore.getInstance();
49
- await store.init({ apiUrl: 'https://api.yourdomain.com' });
76
+ export default new WebsitePrerender();
50
77
  ```
51
78
 
79
+ ## Client-Side Interactivity with md2interact
80
+
81
+ The kit automatically initializes `@leadertechie/md2interact` during `WebsiteUI.bootstrap()`. This enables:
82
+
83
+ ### DOM Interaction Patterns
84
+
85
+ Markdown content with frontmatter can declare interaction patterns:
86
+
87
+ ```markdown
88
+ ---
89
+ interaction: poll
90
+ interactConfig:
91
+ url: /api/data
92
+ interval: 5000
93
+ ---
94
+
95
+ # Live Poll Data
96
+ ```
97
+
98
+ Supported patterns: `poll`, `live-update`, `click-toggle`, `infinite-scroll`, `form-live`, `mfe`, `custom`
99
+
100
+ ### CSS Hydration
101
+
102
+ - **inlineCritical**: Injects critical CSS directly into the DOM
103
+ - **layerInjection**: Uses CSS `@layer` for organized style injection
104
+ - **themeToggle**: Enables light/dark theme toggling
105
+
106
+ ### SPA Navigation
107
+
108
+ After dynamic content loads (e.g., blog detail pages), call `reinitInteract()` to re-scan the DOM:
109
+
110
+ ```typescript
111
+ const ui = WebsiteUI.getInstance();
112
+ ui.reinitInteract();
113
+ ```
114
+
115
+ ## Caching Strategy (v2)
116
+
117
+ The kit uses `@leadertechie/r2tohtml` with a multi-tier caching strategy:
118
+
119
+ | Tier | Cache | TTL |
120
+ |------|-------|-----|
121
+ | In-Memory | `ContentCacheV2` with SWR | 5 min fresh, 30 min stale |
122
+ | Cloudflare Edge | `cfCache` | 5 min |
123
+
124
+ This means:
125
+ - **Fresh**: Served instantly from memory
126
+ - **Stale**: Served from memory while revalidating in background (SWR)
127
+ - **Miss**: Fetched from R2, cached at edge and in-memory
128
+
52
129
  ## Deployment
53
130
 
54
131
  This kit is designed to be deployed to **Cloudflare**. Ensure your `wrangler.toml` is configured with an R2 bucket named `CONTENT_BUCKET`.
@@ -1 +1 @@
1
- {"version":3,"file":"content-utils.d.ts","sourceRoot":"","sources":["../../src/api/content-utils.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,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;AAED,MAAM,WAAW,QAAS,SAAQ,eAAe;IAC/C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAU,SAAQ,eAAe;IAChD,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAStF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAUvD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CA6BhG;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,GAAG,GAAG,QAAQ,GAAG,IAAI,CAK5D;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAqB1H;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAiB1H;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,QAAQ,GAAG,SAAS,CAAC,EAAE,CAAC,CA2BjG"}
1
+ {"version":3,"file":"content-utils.d.ts","sourceRoot":"","sources":["../../src/api/content-utils.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,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;AAED,MAAM,WAAW,QAAS,SAAQ,eAAe;IAC/C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAU,SAAQ,eAAe;IAChD,OAAO,EAAE,MAAM,CAAC;CACjB;AASD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAoBtF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAMvD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CA6BhG;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,GAAG,GAAG,QAAQ,GAAG,IAAI,CAK5D;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAqB1H;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAiB1H;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,QAAQ,GAAG,SAAS,CAAC,EAAE,CAAC,CA2BjG"}
@@ -1 +1 @@
1
- {"version":3,"file":"about-me.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/about-me.ts"],"names":[],"mappings":"AA4CA,wBAAgB,iBAAiB,SAEhC;AAED,wBAAsB,aAAa,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CA4DhE"}
1
+ {"version":3,"file":"about-me.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/about-me.ts"],"names":[],"mappings":"AA0DA,wBAAgB,iBAAiB,SAEhC;AAED,wBAAsB,aAAa,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CA4DhE"}
@@ -1 +1 @@
1
- {"version":3,"file":"home.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/home.ts"],"names":[],"mappings":"AAiCA,wBAAgB,iBAAiB,SAEhC;AAED,wBAAsB,UAAU,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAwC7D"}
1
+ {"version":3,"file":"home.d.ts","sourceRoot":"","sources":["../../../src/api/handlers/home.ts"],"names":[],"mappings":"AA+CA,wBAAgB,iBAAiB,SAEhC;AAED,wBAAsB,UAAU,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAwC7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"website-api.d.ts","sourceRoot":"","sources":["../../src/api/website-api.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAE3E,qBAAa,UAAU;IACrB,OAAO,CAAC,cAAc,CAAiC;IAEhD,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU;IAIzD,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,UAAU;IAqBL,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC;CA+FlE"}
1
+ {"version":3,"file":"website-api.d.ts","sourceRoot":"","sources":["../../src/api/website-api.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAE3E,qBAAa,UAAU;IACrB,OAAO,CAAC,cAAc,CAAiC;IAEhD,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU;IAIzD,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,mBAAmB;IAmB3B,OAAO,CAAC,UAAU;IAqBL,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC;CAgGlE"}
package/dist/api.js CHANGED
@@ -1,5 +1,5 @@
1
- import { W as WebsiteAPI } from "./chunks/website-api-CE2q2nt7.js";
2
- import { A, B, M, R, c, a, g, b, d, h, e, r, s, v } from "./chunks/website-api-CE2q2nt7.js";
1
+ import { W as WebsiteAPI } from "./chunks/website-api-Cy2kZ0dC.js";
2
+ import { A, B, M, R, c, a, g, b, d, h, e, r, s, v } from "./chunks/website-api-Cy2kZ0dC.js";
3
3
  const defaultAPI = new WebsiteAPI();
4
4
  export {
5
5
  A as AUTH_KV,
@@ -2385,9 +2385,20 @@ var __privateMethod$1 = (obj, member, method) => (__accessCheck$1(obj, member, "
2385
2385
  var _error_dec$1, _loading_dec$1, _blogPost_dec, _slug_dec$1, _a$1, _BlogViewer_decorators, _init$1, _slug$1, _blogPost, _loading$1, _error$1;
2386
2386
  const pipeline$1 = new MarkdownPipeline({
2387
2387
  imagePathPrefix: "images/",
2388
+ preserveRawHTML: true,
2389
+ errorRecovery: "warn",
2390
+ maxRecursionDepth: 50,
2388
2391
  styleOptions: {
2389
2392
  classPrefix: "md-",
2390
- addHeadingIds: true
2393
+ addHeadingIds: true,
2394
+ emitScopeAnchors: true
2395
+ },
2396
+ slotPattern: /\[\[(.*?)\]\]/g,
2397
+ onSlot: (name) => {
2398
+ const slotMap = {
2399
+ "CURRENT_YEAR": (/* @__PURE__ */ new Date()).getFullYear().toString()
2400
+ };
2401
+ return slotMap[name] || `[[${name}]]`;
2391
2402
  }
2392
2403
  });
2393
2404
  _BlogViewer_decorators = [customElement("my-blog-viewer")];
@@ -2583,9 +2594,20 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
2583
2594
  var _error_dec, _loading_dec, _storyPost_dec, _slug_dec, _a, _StoryViewer_decorators, _init, _slug, _storyPost, _loading, _error;
2584
2595
  const pipeline = new MarkdownPipeline({
2585
2596
  imagePathPrefix: "images/",
2597
+ preserveRawHTML: true,
2598
+ errorRecovery: "warn",
2599
+ maxRecursionDepth: 50,
2586
2600
  styleOptions: {
2587
2601
  classPrefix: "md-",
2588
- addHeadingIds: true
2602
+ addHeadingIds: true,
2603
+ emitScopeAnchors: true
2604
+ },
2605
+ slotPattern: /\[\[(.*?)\]\]/g,
2606
+ onSlot: (name) => {
2607
+ const slotMap = {
2608
+ "CURRENT_YEAR": (/* @__PURE__ */ new Date()).getFullYear().toString()
2609
+ };
2610
+ return slotMap[name] || `[[${name}]]`;
2589
2611
  }
2590
2612
  });
2591
2613
  _StoryViewer_decorators = [customElement("my-story-viewer")];
@@ -1,10 +1,23 @@
1
1
  import { S as SiteStore } from "./site-store-CGV9c2DI.js";
2
2
  import { MarkdownPipeline } from "@leadertechie/r2tohtml";
3
+ import { init, reinit } from "@leadertechie/md2interact";
3
4
  const pipeline = new MarkdownPipeline({
4
5
  imagePathPrefix: "images/",
6
+ preserveRawHTML: true,
7
+ errorRecovery: "warn",
8
+ maxRecursionDepth: 50,
5
9
  styleOptions: {
6
10
  classPrefix: "md-",
7
- addHeadingIds: true
11
+ addHeadingIds: true,
12
+ emitScopeAnchors: true
13
+ },
14
+ slotPattern: /\[\[(.*?)\]\]/g,
15
+ onSlot: (name) => {
16
+ const slotMap = {
17
+ "SITE_TITLE": document?.querySelector("title")?.textContent || "My Site",
18
+ "CURRENT_YEAR": (/* @__PURE__ */ new Date()).getFullYear().toString()
19
+ };
20
+ return slotMap[name] || `[[${name}]]`;
8
21
  }
9
22
  });
10
23
  const renderMarkdown = (content) => {
@@ -277,6 +290,13 @@ class Router {
277
290
  }
278
291
  canonicalLink.setAttribute("href", url);
279
292
  }
293
+ /**
294
+ * After rendering new content, reinitialize md2interact so it
295
+ * re-scans the DOM for interaction elements (poll, live-update, etc.)
296
+ */
297
+ afterRender() {
298
+ this.ui.reinitInteract();
299
+ }
280
300
  async renderHomePage() {
281
301
  let blogs = [];
282
302
  let stories = [];
@@ -311,6 +331,7 @@ class Router {
311
331
  });
312
332
  if (this.appElement) this.appElement.innerHTML = pageContent.content;
313
333
  this.setPageMeta(pageContent.title, pageContent.description, pageContent.canonicalUrl);
334
+ this.afterRender();
314
335
  }
315
336
  renderAboutMePage() {
316
337
  const pageContent = generatePageContent("/about-me", this.routes, this.footerLinks, {
@@ -320,6 +341,7 @@ class Router {
320
341
  });
321
342
  if (this.appElement) this.appElement.innerHTML = pageContent.content;
322
343
  this.setPageMeta(pageContent.title, pageContent.description, pageContent.canonicalUrl);
344
+ this.afterRender();
323
345
  }
324
346
  async renderContentListPage(pathname) {
325
347
  const type = pathname === "/blogs" ? "blogs" : "stories";
@@ -338,6 +360,7 @@ class Router {
338
360
  });
339
361
  if (this.appElement) this.appElement.innerHTML = pageContent.content;
340
362
  this.setPageMeta(pageContent.title, pageContent.description, pageContent.canonicalUrl);
363
+ this.afterRender();
341
364
  }
342
365
  async renderContentDetailPage(pathname) {
343
366
  const isBlog = pathname.startsWith("/blogs/");
@@ -357,6 +380,7 @@ class Router {
357
380
  });
358
381
  if (this.appElement) this.appElement.innerHTML = pageContent.content;
359
382
  this.setPageMeta(`${slug.replace(/-/g, " ")} - ${this.siteTitle}`, "Read more content", window.location.href);
383
+ this.afterRender();
360
384
  }
361
385
  async renderAdminPage() {
362
386
  generatePageContent("/admin", this.routes, this.footerLinks, {
@@ -377,11 +401,13 @@ class Router {
377
401
  <my-footer copyright="${this.copyright}" footerLinks='${JSON.stringify(this.footerLinks)}'></my-footer>
378
402
  `;
379
403
  }
404
+ this.afterRender();
380
405
  }
381
406
  }
382
407
  class WebsiteUI {
383
408
  constructor(config = {}) {
384
409
  this.router = null;
410
+ this.interactInitialized = false;
385
411
  this.store = SiteStore.getInstance();
386
412
  this.config = config;
387
413
  }
@@ -411,11 +437,44 @@ class WebsiteUI {
411
437
  this.updateBanner(this.store.getConfig());
412
438
  this.router = new Router(this);
413
439
  this.router.init(this.config.appElementId || "app");
440
+ this.initInteract();
414
441
  if (this.config.onBootstrap) {
415
442
  await this.config.onBootstrap(this);
416
443
  }
417
444
  console.log("WebsiteUI bootstrapped");
418
445
  }
446
+ /**
447
+ * Initialize md2interact for client-side DOM interactions,
448
+ * CSS hydration, and event bus communication.
449
+ */
450
+ initInteract() {
451
+ if (this.interactInitialized) return;
452
+ const interactCfg = this.config.interactConfig || {};
453
+ init({
454
+ interactions: interactCfg.interactions || {
455
+ "poll": { selector: '[data-interact="poll"]' },
456
+ "live-update": { selector: '[data-interact="live-update"]' },
457
+ "click-toggle": { selector: '[data-interact="click-toggle"]' },
458
+ "infinite-scroll": { selector: '[data-interact="infinite-scroll"]' },
459
+ "form-live": { selector: '[data-interact="form-live"]' }
460
+ },
461
+ cssHydration: interactCfg.cssHydration || {
462
+ inlineCritical: true,
463
+ layerInjection: true,
464
+ themeToggle: true
465
+ }
466
+ });
467
+ this.interactInitialized = true;
468
+ }
469
+ /**
470
+ * Reinitialize md2interact after dynamic content changes (e.g., SPA navigation).
471
+ * This re-scans the DOM for new interaction elements.
472
+ */
473
+ reinitInteract() {
474
+ if (this.interactInitialized) {
475
+ reinit();
476
+ }
477
+ }
419
478
  updateBanner(config) {
420
479
  const banner = document.querySelector("my-banner");
421
480
  if (banner) {
@@ -493,13 +552,12 @@ class ThemeToggle extends HTMLElement {
493
552
  cursor: pointer;
494
553
  font-size: 1.5rem;
495
554
  padding: 0.5rem;
496
- color: var(--text-color, #213547); /* Default for light mode */
555
+ color: var(--text-color, #213547);
497
556
  transition: color 0.3s ease;
498
557
  }
499
558
  button:hover {
500
- color: var(--primary-color, #747bff); /* Hover color */
559
+ color: var(--primary-color, #747bff);
501
560
  }
502
- /* Dark mode specific styles for the button */
503
561
  html[data-theme='dark'] button {
504
562
  color: var(--dark-mode-text-color, rgba(255, 255, 255, 0.87));
505
563
  }
@@ -508,7 +566,7 @@ class ThemeToggle extends HTMLElement {
508
566
  }
509
567
  </style>
510
568
  <button id="theme-toggle" aria-label="Toggle theme">
511
- <span class="icon">${this.getSunIcon()}</span> <!-- Default to sun for light mode -->
569
+ <span class="icon">${this.getSunIcon()}</span>
512
570
  </button>
513
571
  `;
514
572
  }
@@ -1,4 +1,4 @@
1
- import { R2ContentLoader } from "@leadertechie/r2tohtml";
1
+ import { ContentCacheV2, R2ContentLoader } from "@leadertechie/r2tohtml";
2
2
  function createJSONResponse(data, status = 200) {
3
3
  return new Response(JSON.stringify(data), {
4
4
  status,
@@ -9,6 +9,14 @@ function createErrorResponse(message, status = 500) {
9
9
  return createJSONResponse({ error: message }, status);
10
10
  }
11
11
  let loader$1 = null;
12
+ new ContentCacheV2(
13
+ 5 * 60 * 1e3,
14
+ // TTL: 5 minutes fresh
15
+ true,
16
+ // enabled
17
+ 30 * 60 * 1e3
18
+ // SWR TTL: 30 minutes stale window
19
+ );
12
20
  function getLoader$1(env) {
13
21
  if (!env?.CONTENT_BUCKET) {
14
22
  return null;
@@ -17,14 +25,24 @@ function getLoader$1(env) {
17
25
  loader$1 = new R2ContentLoader(
18
26
  {
19
27
  bucket: env.CONTENT_BUCKET,
20
- cacheTTL: 5 * 60 * 1e3
28
+ cacheTTL: 5 * 60 * 1e3,
29
+ cfCache: true,
30
+ // Enable Cloudflare edge cache tier
31
+ cfCacheTTL: 300,
32
+ // CF cache for 5 minutes
33
+ swrTTL: 30 * 60 * 1e3
34
+ // SWR window: 30 minutes
21
35
  },
22
36
  {
23
37
  md2html: {
24
38
  imagePathPrefix: "images/",
39
+ preserveRawHTML: true,
40
+ errorRecovery: "warn",
41
+ maxRecursionDepth: 50,
25
42
  styleOptions: {
26
43
  classPrefix: "md-",
27
- addHeadingIds: true
44
+ addHeadingIds: true,
45
+ emitScopeAnchors: true
28
46
  }
29
47
  }
30
48
  }
@@ -89,19 +107,37 @@ async function handleAboutMe(env) {
89
107
  }
90
108
  }
91
109
  let loader = null;
110
+ new ContentCacheV2(
111
+ 5 * 60 * 1e3,
112
+ // TTL: 5 minutes fresh
113
+ true,
114
+ // enabled
115
+ 30 * 60 * 1e3
116
+ // SWR TTL: 30 minutes stale window
117
+ );
92
118
  function getLoader(env) {
93
119
  if (!loader && env?.CONTENT_BUCKET) {
94
120
  loader = new R2ContentLoader(
95
121
  {
96
122
  bucket: env.CONTENT_BUCKET,
97
- cacheTTL: 5 * 60 * 1e3
123
+ cacheTTL: 5 * 60 * 1e3,
124
+ cfCache: true,
125
+ // Enable Cloudflare edge cache tier
126
+ cfCacheTTL: 300,
127
+ // CF cache for 5 minutes
128
+ swrTTL: 30 * 60 * 1e3
129
+ // SWR window: 30 minutes
98
130
  },
99
131
  {
100
132
  md2html: {
101
133
  imagePathPrefix: "images/",
134
+ preserveRawHTML: true,
135
+ errorRecovery: "warn",
136
+ maxRecursionDepth: 50,
102
137
  styleOptions: {
103
138
  classPrefix: "md-",
104
- addHeadingIds: true
139
+ addHeadingIds: true,
140
+ emitScopeAnchors: true
105
141
  }
106
142
  }
107
143
  }
@@ -543,15 +579,29 @@ async function handleLogout(request, env) {
543
579
  }
544
580
  });
545
581
  }
546
- const contentCache = /* @__PURE__ */ new Map();
547
- const CACHE_TTL = 5 * 60 * 1e3;
582
+ const contentCache = new ContentCacheV2(
583
+ 5 * 60 * 1e3,
584
+ // TTL: 5 minutes fresh
585
+ true,
586
+ // enabled
587
+ 30 * 60 * 1e3
588
+ // SWR TTL: 30 minutes stale window
589
+ );
548
590
  function getCachedOrFetch(key, fetchFn) {
549
591
  const cached = contentCache.get(key);
550
- if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
592
+ if (cached) {
593
+ if (!cached.stale) {
594
+ return Promise.resolve(cached.data);
595
+ }
596
+ fetchFn().then((data) => {
597
+ contentCache.set(key, data);
598
+ contentCache.refresh(key);
599
+ }).catch(() => {
600
+ });
551
601
  return Promise.resolve(cached.data);
552
602
  }
553
603
  return fetchFn().then((data) => {
554
- contentCache.set(key, { data, timestamp: Date.now() });
604
+ contentCache.set(key, data);
555
605
  return data;
556
606
  });
557
607
  }
@@ -798,16 +848,20 @@ class WebsiteAPI {
798
848
  this.customHandlers.set(route, handler);
799
849
  }
800
850
  addCORSHeaders(response) {
801
- response.headers.set("Access-Control-Allow-Origin", "*");
851
+ if (!response.headers.has("Access-Control-Allow-Origin")) {
852
+ response.headers.set("Access-Control-Allow-Origin", "*");
853
+ }
802
854
  response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
803
855
  response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Session-Token");
804
856
  return response;
805
857
  }
806
858
  addAdminCORSHeaders(response, origin) {
807
859
  const isLocal = origin && (origin.includes("localhost") || origin.includes("127.0.0.1"));
808
- if (isLocal) {
809
- response.headers.set("Access-Control-Allow-Origin", origin);
810
- response.headers.set("Access-Control-Allow-Credentials", "true");
860
+ if (!response.headers.has("Access-Control-Allow-Origin")) {
861
+ if (isLocal) {
862
+ response.headers.set("Access-Control-Allow-Origin", origin);
863
+ response.headers.set("Access-Control-Allow-Credentials", "true");
864
+ }
811
865
  }
812
866
  response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
813
867
  response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Session-Token");
@@ -871,7 +925,7 @@ class WebsiteAPI {
871
925
  return this.addAdminCORSHeaders(createErrorResponse("Unauthorized", 401), origin);
872
926
  }
873
927
  clearContentCache$1();
874
- return this.addAdminCORSHeaders(new Response(JSON.stringify({ success: true, message: "Cache cleared" }), { status: 200 }), origin);
928
+ return this.addAdminCORSHeaders(new Response(JSON.stringify({ success: true, message: "Cache cleared" }), { status: 200, headers: { "Content-Type": "application/json" } }), origin);
875
929
  case "aboutme":
876
930
  return this.addAdminCORSHeaders(await handleAboutMe(env), origin);
877
931
  case "logo":
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import { A, B, M, R, W, c, a, g, b, d, h, e, r, s, v } from "./chunks/website-api-CE2q2nt7.js";
1
+ import { A, B, M, R, W, c, a, g, b, d, h, e, r, s, v } from "./chunks/website-api-Cy2kZ0dC.js";
2
2
  import { WebsitePrerender } from "./prerender.js";
3
- import { A as A2, a as a2, b as b2, c as c2, d as d2, e as e2, f, g as g2, h as h2, i, B as B2, F, M as M2, j, S, k } from "./chunks/index-B2jGIzSi.js";
3
+ import { A as A2, a as a2, b as b2, c as c2, d as d2, e as e2, f, g as g2, h as h2, i, B as B2, F, M as M2, j, S, k } from "./chunks/index-DAog9TQE.js";
4
4
  import { S as S2, g as g3, i as i2, r as r2 } from "./chunks/site-store-CGV9c2DI.js";
5
- import { R as R2, T, W as W2, b as b3, c as c3, g as g4, r as r3 } from "./chunks/template-DXUSp0BG.js";
5
+ import { R as R2, T, W as W2, b as b3, c as c3, g as g4, r as r3 } from "./chunks/template-DUdadCrH.js";
6
6
  export {
7
7
  A as AUTH_KV,
8
8
  A2 as AdminAboutMeSection,
@@ -1 +1 @@
1
- {"version":3,"file":"data-fetcher.d.ts","sourceRoot":"","sources":["../../src/prerender/data-fetcher.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAeD,wBAAsB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAQpE;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAO5D;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAOzD;AAED,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,GAAE,MAAU,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAgB/F;AAED,wBAAsB,yBAAyB,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,GAAE,MAAU,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAgBhG"}
1
+ {"version":3,"file":"data-fetcher.d.ts","sourceRoot":"","sources":["../../src/prerender/data-fetcher.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAwCD,wBAAsB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAQpE;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAO5D;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAOzD;AAED,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,GAAE,MAAU,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAgB/F;AAED,wBAAsB,yBAAyB,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,GAAE,MAAU,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAgBhG"}
@@ -1 +1 @@
1
- {"version":3,"file":"page-content.d.ts","sourceRoot":"","sources":["../../src/prerender/page-content.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAuFD,eAAO,MAAM,mBAAmB,GAC9B,UAAU,MAAM,EAChB,QAAQ,MAAM,EAAE,EAChB,aAAa,WAAW,EAAE,EAC1B,MAAM,GAAG,KACR,OAAO,CAAC,WAAW,CAqJrB,CAAC"}
1
+ {"version":3,"file":"page-content.d.ts","sourceRoot":"","sources":["../../src/prerender/page-content.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAgHD,eAAO,MAAM,mBAAmB,GAC9B,UAAU,MAAM,EAChB,QAAQ,MAAM,EAAE,EAChB,aAAa,WAAW,EAAE,EAC1B,MAAM,GAAG,KACR,OAAO,CAAC,WAAW,CAqJrB,CAAC"}
@@ -5,6 +5,8 @@ export interface TemplateProps {
5
5
  content: string;
6
6
  hydrationData?: string;
7
7
  baseSiteUrl?: string;
8
+ jsAsset?: string;
9
+ cssAsset?: string;
8
10
  }
9
- export declare const createHtmlTemplate: ({ title, description, canonicalUrl, content, hydrationData, baseSiteUrl }: TemplateProps) => Promise<string>;
11
+ export declare const createHtmlTemplate: ({ title, description, canonicalUrl, content, hydrationData, baseSiteUrl, jsAsset, cssAsset }: TemplateProps) => Promise<string>;
10
12
  //# sourceMappingURL=template.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/prerender/template.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAuCD,eAAO,MAAM,kBAAkB,GAAU,2EAOtC,aAAa,KAAG,OAAO,CAAC,MAAM,CA4BhC,CAAC"}
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/prerender/template.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAsDD,eAAO,MAAM,kBAAkB,GAAU,8FAStC,aAAa,KAAG,OAAO,CAAC,MAAM,CAuDhC,CAAC"}
@@ -18,7 +18,9 @@ export declare class WebsitePrerender {
18
18
  private copyright;
19
19
  private templateRenderer;
20
20
  private apiHandler?;
21
+ private cachedAssets;
21
22
  constructor(options?: PrerenderOptions);
23
+ private discoverAssets;
22
24
  private fetchStaticDetails;
23
25
  private fetchAboutMeData;
24
26
  fetch(request: Request, env: any, ctx: any): Promise<Response>;
@@ -1 +1 @@
1
- {"version":3,"file":"website-prerender.d.ts","sourceRoot":"","sources":["../../src/prerender/website-prerender.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,aAAa,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,EAAuB,MAAM,gBAAgB,CAAC;AAE1E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,kBAAkB,CAAC,EAAE,WAAW,EAAE,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IACtE,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAA;KAAE,CAAC;CAChG;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,kBAAkB,CAAgB;IAC1C,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,gBAAgB,CAAqD;IAC7E,OAAO,CAAC,UAAU,CAAC,CAAoF;gBAE3F,OAAO,GAAE,gBAAqB;YAmB5B,kBAAkB;YAiClB,gBAAgB;IAajB,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC;CAoH5E"}
1
+ {"version":3,"file":"website-prerender.d.ts","sourceRoot":"","sources":["../../src/prerender/website-prerender.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,aAAa,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,EAAuB,MAAM,gBAAgB,CAAC;AAE1E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,kBAAkB,CAAC,EAAE,WAAW,EAAE,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IACtE,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAA;KAAE,CAAC;CAChG;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,kBAAkB,CAAgB;IAC1C,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,gBAAgB,CAAqD;IAC7E,OAAO,CAAC,UAAU,CAAC,CAAoF;IACvG,OAAO,CAAC,YAAY,CAA4C;gBAEpD,OAAO,GAAE,gBAAqB;YAmB5B,cAAc;YAuCd,kBAAkB;YAgClB,gBAAgB;IAajB,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC;CAiI5E"}
package/dist/prerender.js CHANGED
@@ -1,27 +1,42 @@
1
- import { R2ContentLoader } from "@leadertechie/r2tohtml";
1
+ import { ContentCacheV2, R2ContentLoader } from "@leadertechie/r2tohtml";
2
+ let cachedAssets = null;
3
+ let isDiscovering = false;
2
4
  async function getAssetPaths(baseSiteUrl) {
3
- if (baseSiteUrl.includes("localhost") || baseSiteUrl.includes("127.0.0.1")) ;
4
- 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;
5
10
  try {
11
+ const assetsUrl = `${baseSiteUrl}/cdn-assets.json`;
6
12
  const res = await fetch(assetsUrl);
7
13
  if (res.ok) {
8
14
  const data = await res.json();
9
- return { js: data.js, css: data.css };
15
+ cachedAssets = { js: data.js, css: data.css };
16
+ isDiscovering = false;
17
+ return cachedAssets;
10
18
  }
11
19
  } catch (e) {
12
20
  }
13
21
  try {
14
- const res = await fetch(`${baseSiteUrl}/?t=${Date.now()}`);
15
- const html = await res.text();
16
- const jsMatch = html.match(/src="(\/assets\/index-[^"]+\.js)"/);
17
- const cssMatch = html.match(/href="(\/assets\/index-[^"]+\.css)"/);
18
- if (jsMatch || cssMatch) {
19
- return {
20
- js: jsMatch ? jsMatch[1] : "/assets/index.js",
21
- css: cssMatch ? cssMatch[1] : "/assets/index.css"
22
- };
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
+ }
23
35
  }
24
36
  } catch (e) {
37
+ console.warn("Asset discovery failed:", e);
38
+ } finally {
39
+ isDiscovering = false;
25
40
  }
26
41
  return { js: "/assets/index.js", css: "/assets/index.css" };
27
42
  }
@@ -31,10 +46,18 @@ const createHtmlTemplate = async ({
31
46
  canonicalUrl,
32
47
  content,
33
48
  hydrationData = "",
34
- baseSiteUrl = ""
49
+ baseSiteUrl = "",
50
+ jsAsset,
51
+ cssAsset
35
52
  }) => {
36
- const effectiveBaseUrl = baseSiteUrl || new URL(canonicalUrl).origin;
37
- const { js: jsAsset, css: cssAsset } = await getAssetPaths(effectiveBaseUrl);
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
+ }
38
61
  return `<!doctype html>
39
62
  <html lang="en" data-theme="light">
40
63
  <head>
@@ -48,8 +71,28 @@ const createHtmlTemplate = async ({
48
71
  <meta property="og:description" content="${description}" />
49
72
  <meta property="og:url" content="${canonicalUrl}" />
50
73
  <link rel="canonical" href="${canonicalUrl}" />
51
- <link rel="stylesheet" crossorigin href="${cssAsset}" />
52
- <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>
53
96
  </head>
54
97
  <body>
55
98
  ${hydrationData}
@@ -60,12 +103,41 @@ const createHtmlTemplate = async ({
60
103
  </html>`;
61
104
  };
62
105
  let loader = null;
106
+ new ContentCacheV2(
107
+ 5 * 60 * 1e3,
108
+ // TTL: 5 minutes fresh
109
+ true,
110
+ // enabled
111
+ 30 * 60 * 1e3
112
+ // SWR TTL: 30 minutes stale window
113
+ );
63
114
  function getLoader(env) {
64
115
  if (!loader) {
65
116
  if (!env?.CONTENT_BUCKET) return null;
66
117
  loader = new R2ContentLoader(
67
- { bucket: env.CONTENT_BUCKET, cacheTTL: 5 * 60 * 1e3 },
68
- { md2html: { imagePathPrefix: "images/", styleOptions: { classPrefix: "md-", addHeadingIds: true } } }
118
+ {
119
+ bucket: env.CONTENT_BUCKET,
120
+ cacheTTL: 5 * 60 * 1e3,
121
+ cfCache: true,
122
+ // Enable Cloudflare edge cache tier
123
+ cfCacheTTL: 300,
124
+ // CF cache for 5 minutes
125
+ swrTTL: 30 * 60 * 1e3
126
+ // SWR window: 30 minutes
127
+ },
128
+ {
129
+ md2html: {
130
+ imagePathPrefix: "images/",
131
+ preserveRawHTML: true,
132
+ errorRecovery: "warn",
133
+ maxRecursionDepth: 50,
134
+ styleOptions: {
135
+ classPrefix: "md-",
136
+ addHeadingIds: true,
137
+ emitScopeAnchors: true
138
+ }
139
+ }
140
+ }
69
141
  );
70
142
  }
71
143
  return loader;
@@ -290,6 +362,7 @@ const generatePageContent = async (pathname, routes, footerLinks, env) => {
290
362
  };
291
363
  class WebsitePrerender {
292
364
  constructor(options = {}) {
365
+ this.cachedAssets = null;
293
366
  this.routes = options.routes || [
294
367
  { link: "/", text: "Home" },
295
368
  { link: "/blogs", text: "Blogs" },
@@ -307,11 +380,41 @@ class WebsitePrerender {
307
380
  this.templateRenderer = options.templateRenderer || createHtmlTemplate;
308
381
  this.apiHandler = options.apiHandler;
309
382
  }
383
+ async discoverAssets(env, baseSiteUrl) {
384
+ if (this.cachedAssets) return this.cachedAssets;
385
+ try {
386
+ let html = "";
387
+ if (env.ASSETS) {
388
+ const res = await env.ASSETS.fetch(new Request("http://localhost/index.html"));
389
+ if (res.ok) html = await res.text();
390
+ }
391
+ if (!html) {
392
+ const res = await fetch(`${baseSiteUrl}/index.html`, {
393
+ headers: { "X-Asset-Discovery": "true" }
394
+ });
395
+ if (res.ok) html = await res.text();
396
+ }
397
+ if (html) {
398
+ const jsMatch = html.match(/src="([^"]*assets\/index-[^"]+\.js)"/);
399
+ const cssMatch = html.match(/href="([^"]*assets\/index-[^"]+\.css)"/);
400
+ if (jsMatch || cssMatch) {
401
+ this.cachedAssets = {
402
+ js: jsMatch ? jsMatch[1].startsWith("/") ? jsMatch[1] : "/" + jsMatch[1] : "/assets/index.js",
403
+ css: cssMatch ? cssMatch[1].startsWith("/") ? cssMatch[1] : "/" + cssMatch[1] : "/assets/index.css"
404
+ };
405
+ return this.cachedAssets;
406
+ }
407
+ }
408
+ } catch (e) {
409
+ console.warn("Asset discovery failed:", e);
410
+ }
411
+ return { js: "/assets/index.js", css: "/assets/index.css" };
412
+ }
310
413
  async fetchStaticDetails(apiUrl, env) {
311
414
  try {
312
415
  let data;
313
416
  if (this.apiHandler) {
314
- const res = await this.apiHandler.fetch(new Request(`${apiUrl}/api/static`), env, {});
417
+ const res = await this.apiHandler.fetch(new Request("http://localhost/api/static"), env, {});
315
418
  if (res.ok) data = await res.json();
316
419
  } else {
317
420
  const res = await fetch(`${apiUrl}/api/static`);
@@ -338,7 +441,7 @@ class WebsitePrerender {
338
441
  async fetchAboutMeData(apiUrl, env) {
339
442
  try {
340
443
  if (this.apiHandler) {
341
- const res = await this.apiHandler.fetch(new Request(`${apiUrl}/api/aboutme`), env, {});
444
+ const res = await this.apiHandler.fetch(new Request("http://localhost/api/aboutme"), env, {});
342
445
  if (res.ok) return await res.json();
343
446
  } else {
344
447
  const res = await fetch(`${apiUrl}/api/aboutme`);
@@ -353,6 +456,10 @@ class WebsitePrerender {
353
456
  const apiUrl = env?.API_URL || `${url.origin}/api`;
354
457
  const baseSiteUrl = env?.BASE_SITE_URL || url.origin;
355
458
  if (url.pathname === "/admin" || url.pathname.startsWith("/admin/")) {
459
+ if (env.ASSETS) {
460
+ const assetRes = await env.ASSETS.fetch(new Request("http://localhost/index.html"));
461
+ if (assetRes.ok) return assetRes;
462
+ }
356
463
  const templateResponse = await fetch(`${baseSiteUrl}/index.html`);
357
464
  if (templateResponse.ok) {
358
465
  return new Response(templateResponse.body, {
@@ -360,6 +467,11 @@ class WebsitePrerender {
360
467
  });
361
468
  }
362
469
  }
470
+ if (request.headers.get("X-Asset-Discovery") === "true") {
471
+ if (env.ASSETS) {
472
+ return env.ASSETS.fetch(new Request("http://localhost/index.html"));
473
+ }
474
+ }
363
475
  await this.fetchStaticDetails(apiUrl, env);
364
476
  if (url.pathname.startsWith("/api/")) {
365
477
  if (this.apiHandler) {
@@ -409,7 +521,7 @@ class WebsitePrerender {
409
521
  ico: "image/x-icon"
410
522
  };
411
523
  const contentType = contentTypes[ext || ""] || "application/octet-stream";
412
- if (baseSiteUrl === url.origin) {
524
+ if (baseSiteUrl === url.origin && !env.ASSETS) {
413
525
  return new Response("Asset not found", { status: 404 });
414
526
  }
415
527
  const response = await fetch(`${baseSiteUrl}${path}`);
@@ -436,8 +548,15 @@ class WebsitePrerender {
436
548
  hydrationScript = `<script>window.__HYDRATION_DATA__ = ${JSON.stringify(aboutMeData)};<\/script>`;
437
549
  }
438
550
  }
551
+ const assets = await this.discoverAssets(env, baseSiteUrl);
439
552
  const pageContent = await generatePageContent(url.pathname, this.routes, this.footerLinks, { ...env, apiUrl, siteTitle: this.siteTitle, copyright: this.copyright });
440
- const html = await this.templateRenderer({ ...pageContent, hydrationData: hydrationScript });
553
+ const html = await this.templateRenderer({
554
+ ...pageContent,
555
+ hydrationData: hydrationScript,
556
+ baseSiteUrl,
557
+ jsAsset: assets.js,
558
+ cssAsset: assets.css
559
+ });
441
560
  return new Response(html, {
442
561
  headers: {
443
562
  "content-type": "text/html",
@@ -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":"AAEA,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"}
@@ -14,6 +14,11 @@ export declare class Router {
14
14
  private setupEventListeners;
15
15
  navigate(path: string): Promise<void>;
16
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;
17
22
  private renderHomePage;
18
23
  private renderAboutMePage;
19
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;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;YAgCL,cAAc;IAkC5B,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;CAsB9B"}
@@ -13,15 +13,36 @@ export interface UIConfig {
13
13
  customCss?: string;
14
14
  };
15
15
  onBootstrap?: (ui: WebsiteUI) => void | Promise<void>;
16
+ interactConfig?: {
17
+ interactions?: Record<string, {
18
+ selector: string;
19
+ }>;
20
+ cssHydration?: {
21
+ inlineCritical?: boolean;
22
+ layerInjection?: boolean;
23
+ themeToggle?: boolean;
24
+ };
25
+ };
16
26
  }
17
27
  export declare class WebsiteUI {
18
28
  private static instance;
19
29
  private store;
20
30
  private config;
21
31
  private router;
32
+ private interactInitialized;
22
33
  private constructor();
23
34
  static getInstance(config?: UIConfig): WebsiteUI;
24
35
  bootstrap(): Promise<void>;
36
+ /**
37
+ * Initialize md2interact for client-side DOM interactions,
38
+ * CSS hydration, and event bus communication.
39
+ */
40
+ private initInteract;
41
+ /**
42
+ * Reinitialize md2interact after dynamic content changes (e.g., SPA navigation).
43
+ * This re-scans the DOM for new interaction elements.
44
+ */
45
+ reinitInteract(): void;
25
46
  private updateBanner;
26
47
  private applyTheme;
27
48
  private updateFavicon;
@@ -1 +1 @@
1
- {"version":3,"file":"website-ui.d.ts","sourceRoot":"","sources":["../../src/shared/website-ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE;QACN,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAY;IACnC,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,MAAM,CAAuB;IAErC,OAAO;WAKO,WAAW,CAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS;IAU1C,SAAS;IAsCtB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,UAAU;IAiBlB,OAAO,CAAC,aAAa;IAad,QAAQ;IAIR,SAAS;IAIT,SAAS;CAGjB;AAED,eAAO,MAAM,SAAS,GAAI,SAAS,QAAQ,kBAA8C,CAAC"}
1
+ {"version":3,"file":"website-ui.d.ts","sourceRoot":"","sources":["../../src/shared/website-ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGlC,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE;QACN,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,cAAc,CAAC,EAAE;QACf,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACpD,YAAY,CAAC,EAAE;YACb,cAAc,CAAC,EAAE,OAAO,CAAC;YACzB,cAAc,CAAC,EAAE,OAAO,CAAC;YACzB,WAAW,CAAC,EAAE,OAAO,CAAC;SACvB,CAAC;KACH,CAAC;CACH;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAY;IACnC,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,mBAAmB,CAAS;IAEpC,OAAO;WAKO,WAAW,CAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS;IAU1C,SAAS;IAyCtB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAuBpB;;;OAGG;IACI,cAAc;IAMrB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,UAAU;IAiBlB,OAAO,CAAC,aAAa;IAad,QAAQ;IAIR,SAAS;IAIT,SAAS;CAGjB;AAED,eAAO,MAAM,SAAS,GAAI,SAAS,QAAQ,kBAA8C,CAAC"}
package/dist/shared.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { S, g, i, r } from "./chunks/site-store-CGV9c2DI.js";
2
- import { R, T, W, b, c, g as g2, r as r2 } from "./chunks/template-DXUSp0BG.js";
2
+ import { R, T, W, b, c, g as g2, r as r2 } from "./chunks/template-DUdadCrH.js";
3
3
  export {
4
4
  R as Router,
5
5
  S as SiteStore,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/blog-viewer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAc5C,UAAU,QAAQ;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBACa,UAAW,SAAQ,UAAU;IACxC,MAAM,CAAC,MAAM,0BAAoB;IAGjC,QAAQ,CAAC,IAAI,SAAM;IAGnB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAQ;IAG1C,QAAQ,CAAC,OAAO,UAAQ;IAGxB,QAAQ,CAAC,KAAK,SAAM;IAEpB,OAAO,KAAK,UAAU,GAMrB;IAED,iBAAiB;IAOjB,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAM/C,QAAQ;IA4BR,MAAM;IA+BN,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;CAKxC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/blog-viewer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AA0B5C,UAAU,QAAQ;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBACa,UAAW,SAAQ,UAAU;IACxC,MAAM,CAAC,MAAM,0BAAoB;IAGjC,QAAQ,CAAC,IAAI,SAAM;IAGnB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAQ;IAG1C,QAAQ,CAAC,OAAO,UAAQ;IAGxB,QAAQ,CAAC,KAAK,SAAM;IAEpB,OAAO,KAAK,UAAU,GAMrB;IAED,iBAAiB;IAOjB,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAM/C,QAAQ;IA4BR,MAAM;IA+BN,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;CAKxC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/story-viewer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAc5C,UAAU,SAAS;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBACa,WAAY,SAAQ,UAAU;IACzC,MAAM,CAAC,MAAM,0BAAqB;IAGlC,QAAQ,CAAC,IAAI,SAAM;IAGnB,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAQ;IAG5C,QAAQ,CAAC,OAAO,UAAQ;IAGxB,QAAQ,CAAC,KAAK,SAAM;IAEpB,OAAO,KAAK,UAAU,GAKrB;IAEK,iBAAiB;IAOvB,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC;IAMrC,SAAS;IAuBf,MAAM;IA+BN,OAAO,CAAC,cAAc;CAKvB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/story-viewer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AA0B5C,UAAU,SAAS;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBACa,WAAY,SAAQ,UAAU;IACzC,MAAM,CAAC,MAAM,0BAAqB;IAGlC,QAAQ,CAAC,IAAI,SAAM;IAGnB,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAQ;IAG5C,QAAQ,CAAC,OAAO,UAAQ;IAGxB,QAAQ,CAAC,KAAK,SAAM;IAEpB,OAAO,KAAK,UAAU,GAKrB;IAEK,iBAAiB;IAOvB,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC;IAMrC,SAAS;IAuBf,MAAM;IA+BN,OAAO,CAAC,cAAc;CAKvB"}
package/dist/ui.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A, a, b, c, d, e, f, g, h, i, B, F, M, j, S, k } from "./chunks/index-B2jGIzSi.js";
1
+ import { A, a, b, c, d, e, f, g, h, i, B, F, M, j, S, k } from "./chunks/index-DAog9TQE.js";
2
2
  export {
3
3
  A as AdminAboutMeSection,
4
4
  a as AdminBlogsSection,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leadertechie/personal-site-kit",
3
- "version": "0.1.0-alpha.15",
3
+ "version": "0.1.0-alpha.17",
4
4
  "type": "module",
5
5
  "description": "A high-performance personal website engine for Cloudflare Workers and R2",
6
6
  "repository": {
@@ -43,8 +43,9 @@
43
43
  "test": "vitest run"
44
44
  },
45
45
  "dependencies": {
46
- "@leadertechie/md2html": "^0.1.0-alpha.13",
47
- "@leadertechie/r2tohtml": "^0.1.0-alpha.12",
46
+ "@leadertechie/md2html": "^0.1.0-alpha.15",
47
+ "@leadertechie/md2interact": "^0.1.0-alpha.1",
48
+ "@leadertechie/r2tohtml": "^0.1.0-alpha.14",
48
49
  "lit": "^3.2.1"
49
50
  },
50
51
  "devDependencies": {