@@ -160,7 +160,10 @@ export class FolkPubsFlipbook extends HTMLElement {
`;
- this.initFlipbook(pageW, pageH);
+ this.initFlipbook(Math.round(pageW), Math.round(pageH)).catch((e) => {
+ console.warn('[folk-pubs-flipbook] Flipbook init failed, using fallback:', e);
+ this.renderFallback();
+ });
this.bindEvents();
}
@@ -183,36 +186,69 @@ export class FolkPubsFlipbook extends HTMLElement {
return;
}
- this._flipBook = new PageFlip(container, {
- width: Math.round(pageW),
- height: Math.round(pageH),
- showCover: true,
- maxShadowOpacity: 0.5,
- mobileScrollSupport: false,
- useMouseEvents: true,
- swipeDistance: 30,
- clickEventForward: false,
- flippingTime: 600,
- startPage: this._currentPage,
- });
+ try {
+ this._flipBook = new PageFlip(container, {
+ width: pageW,
+ height: pageH,
+ showCover: true,
+ maxShadowOpacity: 0.5,
+ mobileScrollSupport: false,
+ useMouseEvents: true,
+ swipeDistance: 30,
+ clickEventForward: false,
+ flippingTime: 600,
+ startPage: this._currentPage,
+ });
- const pages: HTMLElement[] = [];
- for (let i = 0; i < this._pageImages.length; i++) {
- const page = document.createElement("div");
- page.style.cssText = `
- width: 100%; height: 100%;
- background-image: url(${this._pageImages[i]});
- background-size: cover;
- background-position: center;
- `;
- pages.push(page);
+ const pages: HTMLElement[] = [];
+ for (let i = 0; i < this._pageImages.length; i++) {
+ const page = document.createElement("div");
+ page.className = "flipbook-page";
+ page.style.cssText = `
+ width: 100%; height: 100%;
+ background-image: url("${this._pageImages[i]}");
+ background-size: cover;
+ background-position: center;
+ background-color: #fff;
+ `;
+ pages.push(page);
+ }
+
+ this._flipBook.loadFromHTML(pages);
+ this._flipBook.on("flip", (e: any) => {
+ this._currentPage = e.data;
+ this.updatePageInfo();
+ });
+ } catch (e) {
+ console.warn('[folk-pubs-flipbook] StPageFlip render failed, using fallback:', e);
+ this._flipBook = null;
+ this.renderFallback();
+ return;
}
- this._flipBook.loadFromHTML(pages);
- this._flipBook.on("flip", (e: any) => {
- this._currentPage = e.data;
- this.updatePageInfo();
- });
+ // Verify pages actually rendered — if not, fall back after a short delay
+ setTimeout(() => {
+ if (!this.shadowRoot) return;
+ const items = this.shadowRoot.querySelectorAll('.stf__item');
+ // If StPageFlip created items but none are visible, fall back
+ if (items.length > 0) {
+ const anyVisible = Array.from(items).some(
+ (el) => (el as HTMLElement).style.display !== 'none'
+ );
+ if (!anyVisible) {
+ console.warn('[folk-pubs-flipbook] No visible pages after init, using fallback');
+ this._flipBook?.destroy();
+ this._flipBook = null;
+ this.renderFallback();
+ }
+ } else if (!this.shadowRoot.querySelector('.stf__parent')) {
+ // StPageFlip didn't create its structure at all
+ console.warn('[folk-pubs-flipbook] StPageFlip structure missing, using fallback');
+ this._flipBook = null;
+ this.renderFallback();
+ }
+ }, 500);
+
// Initial page info update for spread display
this.updatePageInfo();
}
@@ -272,9 +308,14 @@ export class FolkPubsFlipbook extends HTMLElement {
${this.getStyles()}
- ${this._pageImages.map((src, i) => `

`).join('')}
+ ${this._pageImages.map((src, i) => `
+
+
${i + 1}
+

+
+ `).join('')}
-
${this._numPages} pages
+
${this._numPages} pages (scroll view)
`;
}
@@ -288,7 +329,7 @@ export class FolkPubsFlipbook extends HTMLElement {
}
/* StPageFlip injects these into document.head, but they don't
- penetrate shadow DOM — so we replicate them here. */
+ penetrate shadow DOM — replicate complete CSS here. */
.stf__parent {
position: relative;
display: block;
@@ -296,10 +337,15 @@ export class FolkPubsFlipbook extends HTMLElement {
transform: translateZ(0);
-ms-touch-action: pan-y;
touch-action: pan-y;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
}
.stf__wrapper {
position: relative;
width: 100%;
+ height: 100%;
box-sizing: border-box;
}
.stf__parent canvas {
@@ -308,6 +354,7 @@ export class FolkPubsFlipbook extends HTMLElement {
height: 100%;
left: 0;
top: 0;
+ z-index: 2;
}
.stf__block {
position: absolute;
@@ -315,11 +362,17 @@ export class FolkPubsFlipbook extends HTMLElement {
height: 100%;
box-sizing: border-box;
perspective: 2000px;
+ z-index: 1;
}
.stf__item {
display: none;
position: absolute;
+ box-sizing: border-box;
transform-style: preserve-3d;
+ top: 0;
+ left: 0;
+ overflow: hidden;
+ backface-visibility: hidden;
}
.stf__outerShadow,
.stf__innerShadow,
@@ -328,6 +381,11 @@ export class FolkPubsFlipbook extends HTMLElement {
position: absolute;
left: 0;
top: 0;
+ pointer-events: none;
+ }
+ /* Ensure page content fills the flipbook page */
+ .flipbook-page {
+ box-sizing: border-box;
}
.loading {
@@ -361,6 +419,20 @@ export class FolkPubsFlipbook extends HTMLElement {
display: flex; flex-direction: column; align-items: center; gap: 1rem;
padding: 1rem; max-height: 500px; overflow-y: auto;
}
+ .fallback-page-wrap {
+ position: relative;
+ max-width: 100%;
+ }
+ .fallback-page-num {
+ position: absolute;
+ top: 4px;
+ right: 8px;
+ font-size: 0.6rem;
+ padding: 0.15rem 0.4rem;
+ border-radius: 0.25rem;
+ background: rgba(0,0,0,0.5);
+ color: #fff;
+ }
.fallback-pages img {
max-width: 100%; border-radius: 2px;
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
diff --git a/modules/rpubs/components/folk-pubs-publish-panel.ts b/modules/rpubs/components/folk-pubs-publish-panel.ts
index 849d345..ecd19dc 100644
--- a/modules/rpubs/components/folk-pubs-publish-panel.ts
+++ b/modules/rpubs/components/folk-pubs-publish-panel.ts
@@ -593,11 +593,23 @@ export class FolkPubsPublishPanel extends HTMLElement {
}
}
- /** Get content from the parent editor by reading its textarea */
+ /** Get content from the parent editor — uses cached values since textarea may not be in DOM during publish step */
private getEditorContent(): { content: string; title?: string; author?: string } {
const editor = this.closest("folk-pubs-editor") || document.querySelector("folk-pubs-editor");
- if (!editor?.shadowRoot) return { content: "" };
+ if (!editor) return { content: "" };
+ // Try cached content first (always available after PDF generation)
+ const cached = (editor as any).cachedContent;
+ if (cached?.content) {
+ return {
+ content: cached.content,
+ title: cached.title || undefined,
+ author: cached.author || undefined,
+ };
+ }
+
+ // Fallback: try reading from shadow DOM (only works during write step)
+ if (!editor.shadowRoot) return { content: "" };
const textarea = editor.shadowRoot.querySelector(".content-area") as HTMLTextAreaElement;
const titleInput = editor.shadowRoot.querySelector(".title-input") as HTMLInputElement;
const authorInput = editor.shadowRoot.querySelector(".author-input") as HTMLInputElement;
diff --git a/modules/rpubs/mod.ts b/modules/rpubs/mod.ts
index 63a3dd7..117287d 100644
--- a/modules/rpubs/mod.ts
+++ b/modules/rpubs/mod.ts
@@ -657,7 +657,24 @@ routes.get("/zine", (c) => {
return c.redirect(`/${spaceSlug}?tool=folk-zine-gen`);
});
-// ── Page: Editor ──
+// ── Page: Editor (also served at /press) ──
+routes.get("/press", (c) => {
+ const spaceSlug = c.req.param("space") || "personal";
+ const dataSpace = c.get("effectiveSpace") || spaceSlug;
+ return c.html(renderShell({
+ title: `${spaceSlug} — rPubs Press | rSpace`,
+ moduleId: "rpubs",
+ spaceSlug,
+ modules: getModuleInfoList(),
+ theme: "dark",
+ body: `
`,
+ scripts: `
+
+ `,
+ styles: `
`,
+ }));
+});
+
routes.get("/", (c) => {
const spaceSlug = c.req.param("space") || "personal";
const dataSpace = c.get("effectiveSpace") || spaceSlug;
@@ -668,9 +685,9 @@ routes.get("/", (c) => {
modules: getModuleInfoList(),
theme: "dark",
body: `
`,
- scripts: `
-
- `,
+ scripts: `
+
+ `,
styles: `
`,
}));
});