diff --git a/Dockerfile b/Dockerfile index d464b756..6db601b4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -53,7 +53,7 @@ COPY --from=build /encryptid-sdk /encryptid-sdk RUN bun install --production # Create data directories -RUN mkdir -p /data/communities /data/books /data/swag-artifacts /data/files /data/splats +RUN mkdir -p /data/communities /data/books /data/swag-artifacts /data/files /data/splats /data/rpubs-publications # Copy entrypoint for Infisical secret injection COPY entrypoint.sh /app/entrypoint.sh @@ -67,6 +67,7 @@ ENV BOOKS_DIR=/data/books ENV SWAG_ARTIFACTS_DIR=/data/swag-artifacts ENV FILES_DIR=/data/files ENV SPLATS_DIR=/data/splats +ENV PUBS_DIR=/data/rpubs-publications ENV PORT=3000 # Data volumes for persistence @@ -75,6 +76,7 @@ VOLUME /data/books VOLUME /data/swag-artifacts VOLUME /data/files VOLUME /data/splats +VOLUME /data/rpubs-publications EXPOSE 3000 diff --git a/modules/rpubs/components/folk-pubs-editor.ts b/modules/rpubs/components/folk-pubs-editor.ts index 360a9784..3c58bec6 100644 --- a/modules/rpubs/components/folk-pubs-editor.ts +++ b/modules/rpubs/components/folk-pubs-editor.ts @@ -72,6 +72,7 @@ const SVG_ARROW_RIGHT = ``; const SVG_EXPAND = ``; const SVG_DOWNLOAD = ``; +const SVG_BOOK = ``; const SVG_SPINNER = ``; export class FolkPubsEditor extends HTMLElement { @@ -83,6 +84,7 @@ export class FolkPubsEditor extends HTMLElement { private _pdfUrl: string | null = null; private _pdfInfo: string | null = null; private _pdfPageCount = 0; + private _previewEpubLoading: "reflowable" | "fixed" | null = null; // ── Cached content (for publish panel access when textarea isn't in DOM) ── private _cachedContent = ""; @@ -534,7 +536,9 @@ export class FolkPubsEditor extends HTMLElement {
- ${SVG_DOWNLOAD} Download + ${SVG_DOWNLOAD} PDF + + ${currentFormat ? `${this.escapeHtml(currentFormat.name)} \u00B7 ${dims}` : ''}
@@ -604,6 +608,14 @@ export class FolkPubsEditor extends HTMLElement { this.render(); }); + // EPUB quick downloads from preview step + this.shadowRoot.querySelectorAll('[data-action="preview-download-epub"]').forEach((btn) => { + btn.addEventListener("click", () => { + const mode = (btn as HTMLElement).dataset.epubMode as "reflowable" | "fixed"; + this.previewDownloadEpub(mode); + }); + }); + // Fullscreen toggle this.shadowRoot.querySelector('[data-action="fullscreen-toggle"]')?.addEventListener("click", () => { const flipbook = this.shadowRoot!.querySelector("folk-pubs-flipbook"); @@ -803,6 +815,42 @@ export class FolkPubsEditor extends HTMLElement { }); } + private async previewDownloadEpub(mode: "reflowable" | "fixed") { + if (!this._cachedContent) return; + this._previewEpubLoading = mode; + this.render(); + try { + const res = await fetch(`${getModuleApiBase("rpubs")}/api/generate-epub`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + content: this._cachedContent, + title: this._cachedTitle || undefined, + author: this._cachedAuthor || undefined, + format: this._selectedFormat, + mode, + }), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + throw new Error((err as any).error || "EPUB generation failed"); + } + const blob = await res.blob(); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + const titleSlug = (this._cachedTitle || "publication").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "publication"; + a.download = mode === "fixed" ? `${titleSlug}-${this._selectedFormat}-fixed.epub` : `${titleSlug}.epub`; + a.click(); + URL.revokeObjectURL(url); + } catch (e: any) { + this._error = e.message; + } finally { + this._previewEpubLoading = null; + this.render(); + } + } + private updateWordCount() { if (!this.shadowRoot) return; const el = this.shadowRoot.querySelector('.word-count'); diff --git a/modules/rpubs/components/folk-pubs-publish-panel.ts b/modules/rpubs/components/folk-pubs-publish-panel.ts index 8ce035d1..b184a1e5 100644 --- a/modules/rpubs/components/folk-pubs-publish-panel.ts +++ b/modules/rpubs/components/folk-pubs-publish-panel.ts @@ -29,6 +29,13 @@ export class FolkPubsPublishPanel extends HTMLElement { private _emailSent = false; private _emailError: string | null = null; private _impositionLoading = false; + private _epubReflowLoading = false; + private _epubFixedLoading = false; + private _epubError: string | null = null; + private _publishing = false; + private _publishResult: { hosted_url: string; pdf_url: string; epub_url: string; epub_fixed_url: string | null } | null = null; + private _publishError: string | null = null; + private _publishIncludeFixed = false; private _orderStatus: string | null = null; private _batchStatus: any = null; @@ -113,21 +120,29 @@ export class FolkPubsPublishPanel extends HTMLElement {
- + ${this.renderPublishSection()} + +
downloads
+ +
${FolkPubsPublishPanel.ICONS.download} Download PDF - - -
- - or send by email - +
+ +
+

Reflowable reads on any e-reader. Fixed layout preserves page design.

+ ${this._epubError ? `
${this.esc(this._epubError)}
` : ''} + +
or send by email