From 5faa12a79e1455e956eba4e84f9e9a9b630d1d15 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 9 Jan 2026 16:22:07 -0600 Subject: [PATCH 01/17] Initial Commit Adds menu items for "regular", zipped, and inline output. Currently only displays inline output with *no* URL massaging ( all relative path references are still relative ) Displays, not downloads --- client/homebrew/navbar/pdf.navitem.jsx | 9 ++ client/homebrew/navbar/print.navitem.jsx | 22 +++- client/homebrew/pages/editPage/editPage.jsx | 2 +- client/homebrew/pages/homePage/homePage.jsx | 2 +- client/homebrew/pages/newPage/newPage.jsx | 4 +- client/homebrew/pages/sharePage/sharePage.jsx | 2 +- server/app.js | 42 +++++-- shared/helpers.js | 106 +++++++++++++++++- 8 files changed, 169 insertions(+), 20 deletions(-) create mode 100644 client/homebrew/navbar/pdf.navitem.jsx diff --git a/client/homebrew/navbar/pdf.navitem.jsx b/client/homebrew/navbar/pdf.navitem.jsx new file mode 100644 index 000000000..4ab0e0191 --- /dev/null +++ b/client/homebrew/navbar/pdf.navitem.jsx @@ -0,0 +1,9 @@ +const React = require('react'); +const Nav = require('client/homebrew/navbar/nav.jsx'); +const { printCurrentBrew } = require('../../../shared/helpers.js'); + +module.exports = function(props){ + return + get PDF + ; +}; diff --git a/client/homebrew/navbar/print.navitem.jsx b/client/homebrew/navbar/print.navitem.jsx index ccad820fa..73687592b 100644 --- a/client/homebrew/navbar/print.navitem.jsx +++ b/client/homebrew/navbar/print.navitem.jsx @@ -2,8 +2,22 @@ const React = require('react'); const Nav = require('client/homebrew/navbar/nav.jsx'); const { printCurrentBrew } = require('../../../shared/helpers.js'); -module.exports = function(){ - return - get PDF - ; +module.exports = function(props){ + return + + export + + + get PDF + + + get HTML (Slim) + + + get HTML (Zip) + + + get HTML (Inline) + + ; }; diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 595436c5b..584daac3a 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -360,7 +360,7 @@ const EditPage = (props)=>{ {renderAutoSaveButton()} } - + diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index 463df333b..943a8f76a 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -179,7 +179,7 @@ const HomePage =(props)=>{ ? : renderSaveButton()} - + diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index 83eaeda45..b3c7115d1 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -21,7 +21,7 @@ import AccountNavItem from 'client/homebrew/navbar/account.navitem.js import ErrorNavItem from 'client/homebrew/navbar/error-navitem.jsx'; import HelpNavItem from 'client/homebrew/navbar/help.navitem.jsx'; import VaultNavItem from 'client/homebrew/navbar/vault.navitem.jsx'; -import PrintNavItem from 'client/homebrew/navbar/print.navitem.jsx'; +import PDFNavItem from 'client/homebrew/navbar/pdf.navitem.jsx'; import { both as RecentNavItem } from 'client/homebrew/navbar/recent.navitem.jsx'; // Page specific imports @@ -229,7 +229,7 @@ const NewPage = (props)=>{ ? : renderSaveButton()} - + diff --git a/client/homebrew/pages/sharePage/sharePage.jsx b/client/homebrew/pages/sharePage/sharePage.jsx index 32e88c9d8..9c654943c 100644 --- a/client/homebrew/pages/sharePage/sharePage.jsx +++ b/client/homebrew/pages/sharePage/sharePage.jsx @@ -76,7 +76,7 @@ const SharePage = (props)=>{ {brew.shareId && ( <> - + source diff --git a/server/app.js b/server/app.js index 1bdb5aac3..1be4cbe77 100644 --- a/server/app.js +++ b/server/app.js @@ -25,10 +25,11 @@ import serveCompressedStaticAssets from './static-assets.mv.js'; import sanitizeFilename from 'sanitize-filename'; import asyncHandler from 'express-async-handler'; import templateFn from '../client/template.js'; -import { model as HomebrewModel } from './homebrew.model.js'; +import { model as HomebrewModel } from './homebrew.model.js'; -import { DEFAULT_BREW } from './brewDefaults.js'; -import { splitTextStyleAndMetadata } from '../shared/helpers.js'; +import { DEFAULT_BREW } from './brewDefaults.js'; +import { splitTextStyleAndMetadata, + simulateRender } from '../shared/helpers.js'; //==== Middleware Imports ====// import contentNegotiation from './middleware/content-negotiation.js'; @@ -47,6 +48,14 @@ const sanitizeBrew = (brew, accessType)=>{ return brew; }; +const encodeRFC3986ValueChars = (str)=>{ + return ( + encodeURIComponent(str) + .replace(/[!'()*]/g, (char)=>{`%${char.charCodeAt(0).toString(16).toUpperCase()}`;}) + ); +}; + + app.set('trust proxy', 1 /* number of proxies between user and server */); app.use('/', serveCompressedStaticAssets(`build`)); @@ -231,19 +240,32 @@ app.get('/source/:id', asyncHandler(getBrew('share')), (req, res)=>{ res.status(200).send(text); }); +//Export the Brew as HTML +app.get('/export/:mode/:id', asyncHandler(getBrew('admin')), asyncHandler(simulateRender), (req, res)=>{ + + const id = req.params.id; + const mode = req.params.mode; + const { brew } = req; + sanitizeBrew(brew, 'share'); + const prefix = 'HB - '; + + let fileName = sanitizeFilename(`${prefix}${brew.title}`).replaceAll(' ', ''); + if(!fileName || !fileName.length) { fileName = `${prefix}-Untitled-Brew`; }; + // res.set({ + // 'Cache-Control' : 'no-cache', + // 'Content-Type' : 'text/plain', + // 'Content-Disposition' : `attachment; filename*=UTF-8''${encodeRFC3986ValueChars(fileName)}.html` + // }); + res.status(200).send(brew.html); +}); + + //Download brew source page app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{ const { brew } = req; sanitizeBrew(brew, 'share'); const prefix = 'HB - '; - const encodeRFC3986ValueChars = (str)=>{ - return ( - encodeURIComponent(str) - .replace(/[!'()*]/g, (char)=>{`%${char.charCodeAt(0).toString(16).toUpperCase()}`;}) - ); - }; - let fileName = sanitizeFilename(`${prefix}${brew.title}`).replaceAll(' ', ''); if(!fileName || !fileName.length) { fileName = `${prefix}-Untitled-Brew`; }; res.set({ diff --git a/shared/helpers.js b/shared/helpers.js index adf5b889a..8936235d1 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -1,6 +1,14 @@ +/* eslint-disable max-lines */ import _ from 'lodash'; import yaml from 'js-yaml'; import request from '../client/homebrew/utils/request-middleware.js'; +import Markdown from '../shared/markdown.js'; +import packageJSON from '../package.json' with { type: 'json' }; + +const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m; +const PAGEBREAK_REGEX_LEGACY = /\\page(?:break)?/m; +const COLUMNBREAK_REGEX_LEGACY = /\\column(:?break)?/m; + // Convert the templates from a brew to a Snippets Structure. const brewSnippetsToJSON = (menuTitle, userBrewSnippets, themeBundleSnippets=null, full=true)=>{ @@ -168,10 +176,106 @@ const debugTextMismatch = (clientTextRaw, serverTextRaw, label)=>{ } }; +const simulateRenderPage = (pageText, index, renderer)=>{ + + let styles = {}; + + let classes = 'page'; + let attributes = {}; + + if(renderer == 'legacy') { + pageText.replace(COLUMNBREAK_REGEX_LEGACY, '```\n````\n'); // Allow Legacy brews to use `\column(break)` + // const html = MarkdownLegacy.render(pageText); + const html = "Markdown Legacy currently unsupported" + + return `
\n${html}\n
\n`; + } else { + if(pageText.startsWith('\\page')) { + const firstLineTokens = Markdown.marked.lexer(pageText.split('\n', 1)[0])[0].tokens; + const injectedTags = firstLineTokens?.find((obj)=>obj.injectedTags !== undefined)?.injectedTags; + if(injectedTags) { + styles = { ...styles, ...injectedTags.styles }; + styles = _.mapKeys(styles, (v, k)=>k.startsWith('--') ? k : _.camelCase(k)).join(''); // Convert CSS to camelCase for React + classes = [classes, injectedTags.classes].join(' ').trim(); + attributes = injectedTags.attributes; + } + pageText = pageText.includes('\n') ? pageText.substring(pageText.indexOf('\n') + 1) : ''; // Remove the \page line + } + + // DO NOT REMOVE!!! REQUIRED FOR BACKWARDS COMPATIBILITY WITH NON-UPGRADABLE VERSIONS OF CHROME. + pageText += `\n\n \n\\column\n `; //Artificial column break at page end to emulate column-fill:auto (until `wide` is used, when column-fill:balance will reappear) + + const html = Markdown.render(pageText, index); + + return `
\n${html}\n
`; + } +}; + + +const simulateRender = async (req, res, next)=>{ + let htmlHead = ''; + let htmlStyles = ''; + let htmlBody = ''; + let errorMsg = {}; + // Build HTML similar to the BrewRender ? + + const setError = (error)=>{ + errorMsg = error; + }; + + + splitTextStyleAndMetadata(req.brew); + + const PORT = req.header('host').indexOf[':'] > -1 ? req.header('host').split(':')[1] : '8000'; + + const themeRes = await request + .get(`http://localhost:${PORT}/api/theme/${req.brew.renderer}/${req.brew.theme}`) + .set('Homebrewery-Version', packageJSON.version) + .catch((err)=>{ + setError(err); + }); + + const htmlThemeBundle = themeRes.body.styles.map((style)=>``).join('\n\n'); + + // Create Head + htmlHead += ` + + + + ${req.brew.title} + `; + + htmlStyles = `\t
\n` + + `\t\t${htmlThemeBundle}\n` + + `\t\t\n` + + `\t
`; + + let rawPages = []; + let renderedPages = []; + + if(req.brew.renderer == 'legacy') { + rawPages = req.brew.text.split(PAGEBREAK_REGEX_LEGACY); + } else { + rawPages = req.brew.text.split(PAGEBREAK_REGEX_V3); + } + + _.forEach(rawPages, (page, index)=>{ + renderedPages[index] = simulateRenderPage(page, index, req.brew.renderer); + }); + + htmlBody = `
${renderedPages.join('\n')}\n\n${htmlHead}\n\n\t
\n\t\t
\n\t\t\t
\n` + + `\t\t\t\t${htmlStyles}\n${htmlBody}\n\t\t\t
\n\t\t
\n\t
\n\n`; + req.brew.html = result; + next(); +}; + export { splitTextStyleAndMetadata, printCurrentBrew, fetchThemeBundle, brewSnippetsToJSON, - debugTextMismatch + debugTextMismatch, + simulateRender, }; From fabd280efa85444b64689144e30c16848d422869 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 6 Feb 2026 17:04:32 -0600 Subject: [PATCH 02/17] Remove Inline HTML option --- client/homebrew/navbar/print.navitem.jsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/homebrew/navbar/print.navitem.jsx b/client/homebrew/navbar/print.navitem.jsx index 4d1feaf51..a3f2887cd 100644 --- a/client/homebrew/navbar/print.navitem.jsx +++ b/client/homebrew/navbar/print.navitem.jsx @@ -11,13 +11,10 @@ export default function(props){ get PDF - get HTML (Slim) + get HTML get HTML (Zip) - - get HTML (Inline) - ; }; From f16c8b7663780fefa2ec30b52386a4894210dd75 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 6 Feb 2026 18:01:53 -0600 Subject: [PATCH 03/17] Add /embed endpoint This duplicates the share endpoint. It uses the Share Page template with a boolean for share vs embed to toggle displaying the navbar and toolbar. Added a showToolbar property to brewRender to toggle... showing the toolbar. --- client/homebrew/brewRenderer/brewRenderer.jsx | 5 +- client/homebrew/homebrew.jsx | 3 +- client/homebrew/pages/sharePage/sharePage.jsx | 68 ++++++++++--------- server/app.js | 15 +++- 4 files changed, 55 insertions(+), 36 deletions(-) diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 771a6aa31..22bb734fb 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -102,6 +102,7 @@ const BrewRenderer = (props)=>{ currentBrewRendererPageNum : 1, themeBundle : {}, onPageChange : ()=>{}, + showToolbar : true, ...props }; @@ -297,6 +298,8 @@ const BrewRenderer = (props)=>{ const renderedStyle = useMemo(()=>renderStyle(), [props.style, props.themeBundle]); renderedPages = useMemo(()=>renderPages(), [props.text, displayOptions]); + const toolbarEl = 0 ? state.visiblePages : [state.centerPage]} totalPages={rawPages.length} headerState={headerState} setHeaderState={setHeaderState}/>; + return ( <> {/*render dummy page while iFrame is mounting.*/} @@ -314,7 +317,7 @@ const BrewRenderer = (props)=>{
- 0 ? state.visiblePages : [state.centerPage]} totalPages={rawPages.length} headerState={headerState} setHeaderState={setHeaderState}/> + {props.showToolbar ? toolbarEl : ''} {/*render in iFrame so broken code doesn't crash the site.*/} {
} /> - } /> + } /> + } /> } /> } /> } /> diff --git a/client/homebrew/pages/sharePage/sharePage.jsx b/client/homebrew/pages/sharePage/sharePage.jsx index 62778778f..25b9302ef 100644 --- a/client/homebrew/pages/sharePage/sharePage.jsx +++ b/client/homebrew/pages/sharePage/sharePage.jsx @@ -15,7 +15,7 @@ import { DEFAULT_BREW_LOAD } from '../../../../server/brewDefaults.js'; import { printCurrentBrew, fetchThemeBundle } from '../../../../shared/helpers.js'; const SharePage = (props)=>{ - const { brew = DEFAULT_BREW_LOAD, disableMeta = false } = props; + const { brew = DEFAULT_BREW_LOAD, disableMeta = false, share = true } = props; const [themeBundle, setThemeBundle] = useState({}); const [currentBrewRendererPageNum, setCurrentBrewRendererPageNum] = useState(1); @@ -65,40 +65,43 @@ const SharePage = (props)=>{ ); + const showNav = ( + + + {disableMeta ? titleEl : {titleEl}} + + + + {brew.shareId && ( + <> + + + + source + + + view + + {renderEditLink()} + + download + + + clone to new + + + + )} + + + + + ); + return (
- - - {disableMeta ? titleEl : {titleEl}} - - - - {brew.shareId && ( - <> - - - - source - - - view - - {renderEditLink()} - - download - - - clone to new - - - - )} - - - - - + {share ? showNav : ''}
{ onPageChange={handleBrewRendererPageChange} currentBrewRendererPageNum={currentBrewRendererPageNum} allowPrint={true} + showToolbar={share} />
diff --git a/server/app.js b/server/app.js index 1be4cbe77..2cdf6ca0b 100644 --- a/server/app.js +++ b/server/app.js @@ -454,8 +454,8 @@ app.get('/new', asyncHandler(async(req, res, next)=>{ return next(); })); -//Share Page -app.get('/share/:id', dbCheck, asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{ + +const shareEmbedCommon = async (req)=>{ const { brew } = req; req.ogMeta = { ...defaultMetaTags, title : `${req.brew.title || 'Untitled Brew'} - ${req.brew.authors[0] || 'No author.'}`, @@ -478,6 +478,17 @@ app.get('/share/:id', dbCheck, asyncHandler(getBrew('share')), asyncHandler(asyn brew.authors.includes(req.account?.username) ? sanitizeBrew(req.brew, 'shareAuthor') : sanitizeBrew(req.brew, 'share'); splitTextStyleAndMetadata(req.brew); +}; + +//Share Page +app.get('/share/:id', dbCheck, asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{ + shareEmbedCommon(req); + return next(); +})); + +//Embed Page +app.get('/embed/:id', dbCheck, asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{ + shareEmbedCommon(req); return next(); })); From 2791c2259b2414a20081da8bac6f02c6a97d4228 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sun, 8 Feb 2026 19:48:57 -0600 Subject: [PATCH 04/17] Rework HTML Download options to use a DOM snatch and grab Still requires path manipulation. Stubs the same for Zipfiles. --- client/homebrew/navbar/print.navitem.jsx | 6 +- server/app.js | 24 +---- shared/helpers.js | 115 ++++++----------------- 3 files changed, 32 insertions(+), 113 deletions(-) diff --git a/client/homebrew/navbar/print.navitem.jsx b/client/homebrew/navbar/print.navitem.jsx index a3f2887cd..271e17027 100644 --- a/client/homebrew/navbar/print.navitem.jsx +++ b/client/homebrew/navbar/print.navitem.jsx @@ -1,6 +1,6 @@ import React from 'react'; import Nav from './nav.jsx'; -import { printCurrentBrew } from '../../../shared/helpers.js'; +import { printCurrentBrew, scrapeBrewHTML, scrapeBrewZip } from '../../../shared/helpers.js'; export default function(props){ return @@ -10,10 +10,10 @@ export default function(props){ get PDF - + get HTML - + get HTML (Zip) ; diff --git a/server/app.js b/server/app.js index 2cdf6ca0b..3ecadcf0e 100644 --- a/server/app.js +++ b/server/app.js @@ -27,9 +27,8 @@ import asyncHandler from 'express-async-handler'; import templateFn from '../client/template.js'; import { model as HomebrewModel } from './homebrew.model.js'; -import { DEFAULT_BREW } from './brewDefaults.js'; -import { splitTextStyleAndMetadata, - simulateRender } from '../shared/helpers.js'; +import { DEFAULT_BREW } from './brewDefaults.js'; +import { splitTextStyleAndMetadata } from '../shared/helpers.js'; //==== Middleware Imports ====// import contentNegotiation from './middleware/content-negotiation.js'; @@ -240,25 +239,6 @@ app.get('/source/:id', asyncHandler(getBrew('share')), (req, res)=>{ res.status(200).send(text); }); -//Export the Brew as HTML -app.get('/export/:mode/:id', asyncHandler(getBrew('admin')), asyncHandler(simulateRender), (req, res)=>{ - - const id = req.params.id; - const mode = req.params.mode; - const { brew } = req; - sanitizeBrew(brew, 'share'); - const prefix = 'HB - '; - - let fileName = sanitizeFilename(`${prefix}${brew.title}`).replaceAll(' ', ''); - if(!fileName || !fileName.length) { fileName = `${prefix}-Untitled-Brew`; }; - // res.set({ - // 'Cache-Control' : 'no-cache', - // 'Content-Type' : 'text/plain', - // 'Content-Disposition' : `attachment; filename*=UTF-8''${encodeRFC3986ValueChars(fileName)}.html` - // }); - res.status(200).send(brew.html); -}); - //Download brew source page app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{ diff --git a/shared/helpers.js b/shared/helpers.js index 8936235d1..91ad3bfb9 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -176,99 +176,37 @@ const debugTextMismatch = (clientTextRaw, serverTextRaw, label)=>{ } }; -const simulateRenderPage = (pageText, index, renderer)=>{ - - let styles = {}; - - let classes = 'page'; - let attributes = {}; - - if(renderer == 'legacy') { - pageText.replace(COLUMNBREAK_REGEX_LEGACY, '```\n````\n'); // Allow Legacy brews to use `\column(break)` - // const html = MarkdownLegacy.render(pageText); - const html = "Markdown Legacy currently unsupported" - - return `
\n${html}\n
\n`; - } else { - if(pageText.startsWith('\\page')) { - const firstLineTokens = Markdown.marked.lexer(pageText.split('\n', 1)[0])[0].tokens; - const injectedTags = firstLineTokens?.find((obj)=>obj.injectedTags !== undefined)?.injectedTags; - if(injectedTags) { - styles = { ...styles, ...injectedTags.styles }; - styles = _.mapKeys(styles, (v, k)=>k.startsWith('--') ? k : _.camelCase(k)).join(''); // Convert CSS to camelCase for React - classes = [classes, injectedTags.classes].join(' ').trim(); - attributes = injectedTags.attributes; - } - pageText = pageText.includes('\n') ? pageText.substring(pageText.indexOf('\n') + 1) : ''; // Remove the \page line - } - - // DO NOT REMOVE!!! REQUIRED FOR BACKWARDS COMPATIBILITY WITH NON-UPGRADABLE VERSIONS OF CHROME. - pageText += `\n\n \n\\column\n `; //Artificial column break at page end to emulate column-fill:auto (until `wide` is used, when column-fill:balance will reappear) - - const html = Markdown.render(pageText, index); - - return `
\n${html}\n
`; - } +const scrapeBrew = ()=>{ + const htmlBody = `\n${window.frames['BrewRenderer'].contentDocument.documentElement.innerHTML}\n`; + return htmlBody; }; -const simulateRender = async (req, res, next)=>{ - let htmlHead = ''; - let htmlStyles = ''; - let htmlBody = ''; - let errorMsg = {}; - // Build HTML similar to the BrewRender ? - - const setError = (error)=>{ - errorMsg = error; +const downloadBlob = (brewHtml, fileName)=>{ + const blob = new Blob([brewHtml], { type: 'text/plain' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = fileName || 'download'; + const clickHandler = ()=>{ + setTimeout(()=>{ + URL.revokeObjectURL(url); + removeEventListener('click', clickHandler); + }, 150); }; + a.addEventListener('click', clickHandler, false); + a.click(); +}; +const scrapeBrewZip = ()=>{ + const htmlBody = scrapeBrew(); + // DO STUFF! +}; - splitTextStyleAndMetadata(req.brew); - - const PORT = req.header('host').indexOf[':'] > -1 ? req.header('host').split(':')[1] : '8000'; - - const themeRes = await request - .get(`http://localhost:${PORT}/api/theme/${req.brew.renderer}/${req.brew.theme}`) - .set('Homebrewery-Version', packageJSON.version) - .catch((err)=>{ - setError(err); - }); - - const htmlThemeBundle = themeRes.body.styles.map((style)=>``).join('\n\n'); - - // Create Head - htmlHead += ` - - - - ${req.brew.title} - `; - - htmlStyles = `\t
\n` + - `\t\t${htmlThemeBundle}\n` + - `\t\t\n` + - `\t
`; - - let rawPages = []; - let renderedPages = []; - - if(req.brew.renderer == 'legacy') { - rawPages = req.brew.text.split(PAGEBREAK_REGEX_LEGACY); - } else { - rawPages = req.brew.text.split(PAGEBREAK_REGEX_V3); - } - - _.forEach(rawPages, (page, index)=>{ - renderedPages[index] = simulateRenderPage(page, index, req.brew.renderer); - }); - - htmlBody = `
${renderedPages.join('\n')}\n\n${htmlHead}\n\n\t
\n\t\t
\n\t\t\t
\n` + - `\t\t\t\t${htmlStyles}\n${htmlBody}\n\t\t\t
\n\t\t
\n\t
\n\n`; - req.brew.html = result; - next(); +const scrapeBrewHTML = ()=>{ + const htmlBody = scrapeBrew(); + // Manipulate the body to change all relative path references to full URLs + downloadBlob(htmlBody, 'testDownload.html'); }; export { @@ -277,5 +215,6 @@ export { fetchThemeBundle, brewSnippetsToJSON, debugTextMismatch, - simulateRender, + scrapeBrewHTML, + scrapeBrewZip, }; From a1d56fe5d375cf5ee44f1e641b28acc60119e295 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Thu, 19 Mar 2026 19:35:05 -0500 Subject: [PATCH 05/17] WIP --- client/homebrew/brewRenderer/brewRenderer.jsx | 40 +++++++++++-------- server/app.js | 10 +++-- vite.config.js | 1 + 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index b7c23d981..9c2ca1312 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -304,6 +304,28 @@ const BrewRenderer = (props)=>{ const toolbarEl = 0 ? state.visiblePages : [state.centerPage]} totalPages={rawPages.length} headerState={headerState} setHeaderState={setHeaderState}/>; + const brewRenderFrameContents = ( + <> +
+ + {/* Apply CSS from Style tab and render pages from Markdown tab */} + {state.isMounted + && + <> + {renderedStyle} +
+ {renderedPages} +
+ + } +
+ {headerState ? : <>} + + ); + return ( <> {/*render dummy page while iFrame is mounting.*/} @@ -329,23 +351,7 @@ const BrewRenderer = (props)=>{ contentDidMount={frameDidMount} onClick={()=>{emitClick();}} > -
- - {/* Apply CSS from Style tab and render pages from Markdown tab */} - {state.isMounted - && - <> - {renderedStyle} -
- {renderedPages} -
- - } -
- {headerState ? : <>} + {brewRenderFrameContents} {state.isMounted &&
diff --git a/server/app.js b/server/app.js index bee173b7e..9a2d8a934 100644 --- a/server/app.js +++ b/server/app.js @@ -80,15 +80,19 @@ export default async function createApp(vite) { const herokuRegex = /^https:\/\/(?:homebrewery-pr-\d+\.herokuapp\.com|naturalcrit-pr-\d+\.herokuapp\.com)$/; // Matches any Heroku app + console.log( 'IsLocalEnvironment'); console.log(isLocalEnvironment); + console.log('localNetworkRegex'); console.log(localNetworkRegex.test(origin)); - console.log(origin == 'null'); + console.log('origin'); + console.log(origin === 'null'); + console.log(origin); - if(!origin | origin == 'null' || allowedOrigins.includes(origin) || herokuRegex.test(origin) || (isLocalEnvironment && localNetworkRegex.test(origin))) { + if(!origin || origin === 'null' || allowedOrigins.includes(origin) || herokuRegex.test(origin) || (isLocalEnvironment && localNetworkRegex.test(origin))) { callback(null, true); } else { console.log(origin, 'not allowed'); - callback(new Error('Not allowed by CORS, if you think this is an error, please contact us')); + callback(new Error('Not allowed by CORS, if you think this is an error, please contact us - Skidoosh')); } }, methods : ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], diff --git a/vite.config.js b/vite.config.js index d9eacd502..1e246346b 100644 --- a/vite.config.js +++ b/vite.config.js @@ -34,5 +34,6 @@ export default defineConfig({ fs : { allow : ['.'], }, + allowedHosts : ['fedora.copy.to'] }, }); From ae424d5da8fc8dc047155603bc7d966b1fecccc3 Mon Sep 17 00:00:00 2001 From: db Date: Sat, 21 Mar 2026 22:53:27 -0500 Subject: [PATCH 06/17] i --- package-lock.json | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3236f5d4..74718fb50 100644 --- a/package-lock.json +++ b/package-lock.json @@ -178,7 +178,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -2011,7 +2010,6 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -2125,7 +2123,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" }, @@ -2166,7 +2163,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" } @@ -3385,7 +3381,6 @@ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", "license": "MIT", - "peer": true, "engines": { "node": "^14.21.3 || >=16" }, @@ -4520,7 +4515,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5092,7 +5086,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -6259,7 +6252,6 @@ "integrity": "sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", @@ -8508,7 +8500,6 @@ "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.2.0", "@jest/types": "30.2.0", @@ -9140,7 +9131,6 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -9520,7 +9510,6 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", "license": "MIT", - "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -9884,6 +9873,21 @@ "url": "https://opencollective.com/mongoose" } }, + "node_modules/mongoose/node_modules/gcp-metadata": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.1.tgz", + "integrity": "sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==", + "optional": true, + "peer": true, + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/mongoose/node_modules/mongodb": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.0.0.tgz", @@ -10690,7 +10694,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -10746,7 +10749,6 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -10761,6 +10763,7 @@ "integrity": "sha512-Mn8KJ45HNNG6JBpBizXcyf6LqY/qyqetGcou/nprDnFwBFBLGj0j/sNKV2lj2KMOVOwdXu14aEzqJv8CIV6e8g==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "postcss": "^8.4.20" } @@ -10833,7 +10836,6 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -10970,7 +10972,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -10980,7 +10981,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -12054,7 +12054,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", @@ -12543,7 +12542,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -13027,7 +13025,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -13119,7 +13116,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, From d2ebdf39582b15ac9f54b01ca813e8cd2ce31f34 Mon Sep 17 00:00:00 2001 From: db Date: Fri, 27 Mar 2026 17:49:59 -0500 Subject: [PATCH 07/17] WIP --- server/app.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/app.js b/server/app.js index 9a2d8a934..95ec132b0 100644 --- a/server/app.js +++ b/server/app.js @@ -12,7 +12,7 @@ import _ from 'lodash'; import jwt from 'jwt-simple'; import express from 'express'; import config from './config.js'; -import path from 'path'; +import path from 'path'; import fs from 'fs-extra'; import api from './homebrew.api.js'; @@ -447,7 +447,7 @@ export default async function createApp(vite) { })); - const shareEmbedCommon = async(req)=>{ + const shareEmbedCommon = async(req, res)=>{ const { brew } = req; req.ogMeta = { ...defaultMetaTags, @@ -475,13 +475,13 @@ export default async function createApp(vite) { //Share Page app.get('/share/:id', dbCheck, asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{ - shareEmbedCommon(req); + shareEmbedCommon(req,res); return next(); })); //Embed Page - More work will be done on this later... app.get('/embed/:id', dbCheck, asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{ - shareEmbedCommon(req); + shareEmbedCommon(req,res); return next(); })); From ca160d7d83ba8b278fcea921d245d72dcb5b5ec1 Mon Sep 17 00:00:00 2001 From: db Date: Sat, 28 Mar 2026 13:44:43 -0500 Subject: [PATCH 08/17] Start adding new class --- client/homebrew/brewRenderer/brewRenderer.jsx | 33 +++-- client/homebrew/homebrew.jsx | 3 +- client/homebrew/pages/embedPage/embedPage.jsx | 124 ++++++++++++++++++ .../homebrew/pages/embedPage/embedPage.less | 7 + client/homebrew/pages/embedPage/sharePage.jsx | 124 ++++++++++++++++++ .../homebrew/pages/embedPage/sharePage.less | 7 + server/app.js | 4 +- 7 files changed, 291 insertions(+), 11 deletions(-) create mode 100644 client/homebrew/pages/embedPage/embedPage.jsx create mode 100644 client/homebrew/pages/embedPage/embedPage.less create mode 100644 client/homebrew/pages/embedPage/sharePage.jsx create mode 100644 client/homebrew/pages/embedPage/sharePage.less diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 9c2ca1312..da16f0a08 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -272,7 +272,7 @@ const BrewRenderer = (props)=>{ const frameDidMount = ()=>{ //This triggers when iFrame finishes internal "componentDidMount" scrollToHash(window.location.hash); - + console.log('Frame'); setTimeout(()=>{ //We still see a flicker where the style isn't applied yet, so wait 100ms before showing iFrame renderPages(); //Make sure page is renderable before showing setState((prevState)=>({ @@ -326,6 +326,29 @@ const BrewRenderer = (props)=>{ ); + const brewRenderFrameWrapper = ( + <> + {emitClick();}} + > + {brewRenderFrameContents} + + + ); + + const brewRenderDivWrapper = ( + <> +
+ {brewRenderFrameContents} +
+ + ); + return ( <> {/*render dummy page while iFrame is mounting.*/} @@ -346,13 +369,7 @@ const BrewRenderer = (props)=>{ {props.showToolbar ? toolbarEl : ''} {/*render in iFrame so broken code doesn't crash the site.*/} - {emitClick();}} - > - {brewRenderFrameContents} - + {brewRenderDivWrapper} {state.isMounted &&
} diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx index 99b3e8fb4..afe0f2b16 100644 --- a/client/homebrew/homebrew.jsx +++ b/client/homebrew/homebrew.jsx @@ -9,6 +9,7 @@ import HomePage from './pages/homePage/homePage.jsx'; import EditPage from './pages/editPage/editPage.jsx'; import UserPage from './pages/userPage/userPage.jsx'; import SharePage from './pages/sharePage/sharePage.jsx'; +import EmbedPage from './pages/embedPage/embedPage.jsx'; import NewPage from './pages/newPage/newPage.jsx'; import ErrorPage from './pages/errorPage/errorPage.jsx'; import VaultPage from './pages/vaultPage/vaultPage.jsx'; @@ -71,7 +72,7 @@ const Homebrew = (props)=>{ } /> } /> - } /> + } /> } /> } /> } /> diff --git a/client/homebrew/pages/embedPage/embedPage.jsx b/client/homebrew/pages/embedPage/embedPage.jsx new file mode 100644 index 000000000..9aed04df4 --- /dev/null +++ b/client/homebrew/pages/embedPage/embedPage.jsx @@ -0,0 +1,124 @@ +import './embedPage.less'; +import React, { useState, useEffect, useCallback } from 'react'; +import Headtags from '../../../../vitreum/headtags.js'; +const Meta = Headtags.Meta; + +import Nav from '@navbar/nav.jsx'; +import Navbar from '@navbar/navbar.jsx'; +import MetadataNav from '@navbar/metadata.navitem.jsx'; +import PrintNavItem from '@navbar/print.navitem.jsx'; +import RecentNavItems from '@navbar/recent.navitem.jsx'; +const { both: RecentNavItem } = RecentNavItems; +import Account from '@navbar/account.navitem.jsx'; +import BrewRenderer from '../../brewRenderer/brewRenderer.jsx'; + +import { DEFAULT_BREW_LOAD } from '../../../../server/brewDefaults.js'; +import { printCurrentBrew, fetchThemeBundle } from '@shared/helpers.js'; + +const SharePage = (props)=>{ + const { brew = DEFAULT_BREW_LOAD, disableMeta = false, share = true } = props; + + const [themeBundle, setThemeBundle] = useState({}); + const [currentBrewRendererPageNum, setCurrentBrewRendererPageNum] = useState(1); + + const handleBrewRendererPageChange = useCallback((pageNumber)=>{ + setCurrentBrewRendererPageNum(pageNumber); + }, []); + + const handleControlKeys = (e)=>{ + if(!(e.ctrlKey || e.metaKey)) return; + const P_KEY = 80; + if(e.keyCode === P_KEY) { + printCurrentBrew(); + e.stopPropagation(); + e.preventDefault(); + } + }; + + useEffect(()=>{ + document.addEventListener('keydown', handleControlKeys); + fetchThemeBundle(undefined, setThemeBundle, brew.renderer, brew.theme); + + return ()=>{ + document.removeEventListener('keydown', handleControlKeys); + }; + }, []); + + const processShareId = ()=>{ + return brew.googleId && !brew.stubbed ? brew.googleId + brew.shareId : brew.shareId; + }; + + const renderEditLink = ()=>{ + if(!brew.editId) return null; + + const editLink = brew.googleId && ! brew.stubbed ? brew.googleId + brew.editId : brew.editId; + + return ( + + edit + + ); + }; + + const titleEl = ( + + {brew.title} + + ); + + const showNav = ( + + + {disableMeta ? titleEl : {titleEl}} + + + + {brew.shareId && ( + <> + + + + source + + + view + + {renderEditLink()} + + download + + + clone to new + + + + )} + + + + + ); + + return ( +
+ + {share ? showNav : ''} +
+ +
+
+ ); +}; + +export default SharePage; diff --git a/client/homebrew/pages/embedPage/embedPage.less b/client/homebrew/pages/embedPage/embedPage.less new file mode 100644 index 000000000..b76dc50f9 --- /dev/null +++ b/client/homebrew/pages/embedPage/embedPage.less @@ -0,0 +1,7 @@ +.sharePage { + nav .navSection.titleSection { + flex-grow : 1; + justify-content : center; + } + .content { overflow-y : hidden; } +} diff --git a/client/homebrew/pages/embedPage/sharePage.jsx b/client/homebrew/pages/embedPage/sharePage.jsx new file mode 100644 index 000000000..c0838f42b --- /dev/null +++ b/client/homebrew/pages/embedPage/sharePage.jsx @@ -0,0 +1,124 @@ +import './sharePage.less'; +import React, { useState, useEffect, useCallback } from 'react'; +import Headtags from '../../../../vitreum/headtags.js'; +const Meta = Headtags.Meta; + +import Nav from '@navbar/nav.jsx'; +import Navbar from '@navbar/navbar.jsx'; +import MetadataNav from '@navbar/metadata.navitem.jsx'; +import PrintNavItem from '@navbar/print.navitem.jsx'; +import RecentNavItems from '@navbar/recent.navitem.jsx'; +const { both: RecentNavItem } = RecentNavItems; +import Account from '@navbar/account.navitem.jsx'; +import BrewRenderer from '../../brewRenderer/brewRenderer.jsx'; + +import { DEFAULT_BREW_LOAD } from '../../../../server/brewDefaults.js'; +import { printCurrentBrew, fetchThemeBundle } from '@shared/helpers.js'; + +const SharePage = (props)=>{ + const { brew = DEFAULT_BREW_LOAD, disableMeta = false, share = true } = props; + + const [themeBundle, setThemeBundle] = useState({}); + const [currentBrewRendererPageNum, setCurrentBrewRendererPageNum] = useState(1); + + const handleBrewRendererPageChange = useCallback((pageNumber)=>{ + setCurrentBrewRendererPageNum(pageNumber); + }, []); + + const handleControlKeys = (e)=>{ + if(!(e.ctrlKey || e.metaKey)) return; + const P_KEY = 80; + if(e.keyCode === P_KEY) { + printCurrentBrew(); + e.stopPropagation(); + e.preventDefault(); + } + }; + + useEffect(()=>{ + document.addEventListener('keydown', handleControlKeys); + fetchThemeBundle(undefined, setThemeBundle, brew.renderer, brew.theme); + + return ()=>{ + document.removeEventListener('keydown', handleControlKeys); + }; + }, []); + + const processShareId = ()=>{ + return brew.googleId && !brew.stubbed ? brew.googleId + brew.shareId : brew.shareId; + }; + + const renderEditLink = ()=>{ + if(!brew.editId) return null; + + const editLink = brew.googleId && ! brew.stubbed ? brew.googleId + brew.editId : brew.editId; + + return ( + + edit + + ); + }; + + const titleEl = ( + + {brew.title} + + ); + + const showNav = ( + + + {disableMeta ? titleEl : {titleEl}} + + + + {brew.shareId && ( + <> + + + + source + + + view + + {renderEditLink()} + + download + + + clone to new + + + + )} + + + + + ); + + return ( +
+ + {share ? showNav : ''} +
+ +
+
+ ); +}; + +export default SharePage; diff --git a/client/homebrew/pages/embedPage/sharePage.less b/client/homebrew/pages/embedPage/sharePage.less new file mode 100644 index 000000000..b76dc50f9 --- /dev/null +++ b/client/homebrew/pages/embedPage/sharePage.less @@ -0,0 +1,7 @@ +.sharePage { + nav .navSection.titleSection { + flex-grow : 1; + justify-content : center; + } + .content { overflow-y : hidden; } +} diff --git a/server/app.js b/server/app.js index 95ec132b0..6910b386f 100644 --- a/server/app.js +++ b/server/app.js @@ -475,13 +475,13 @@ export default async function createApp(vite) { //Share Page app.get('/share/:id', dbCheck, asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{ - shareEmbedCommon(req,res); + await shareEmbedCommon(req,res); return next(); })); //Embed Page - More work will be done on this later... app.get('/embed/:id', dbCheck, asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{ - shareEmbedCommon(req,res); + await shareEmbedCommon(req,res); return next(); })); From 8ad208600da25ddb6b9f7eacf394c6e6f6d59ee6 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 28 Mar 2026 15:01:28 -0500 Subject: [PATCH 09/17] Clear error in helpers? --- shared/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/helpers.js b/shared/helpers.js index 17e2a2e93..c53fdb5ec 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -138,7 +138,7 @@ const fetchThemeBundle = async (setError, setThemeBundle, renderer, theme)=>{ const themeBundle = res.body; themeBundle.joinedStyles = themeBundle.styles.map((style)=>``).join('\n\n'); setThemeBundle(themeBundle); - setError(null); + if(setError) { setError(null); } }; const debugTextMismatch = (clientTextRaw, serverTextRaw, label)=>{ From e61556e43b76bbf0971d27a6333c380f4936f7d6 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 28 Mar 2026 15:24:49 -0500 Subject: [PATCH 10/17] I feel like there is a cleaner way to handle this, but this embeds as desired and it doesn't blink --- client/homebrew/brewRenderer/brewRenderer.jsx | 6 +- client/homebrew/homebrew.jsx | 3 +- client/homebrew/pages/embedPage/embedPage.jsx | 124 ------------------ .../homebrew/pages/embedPage/embedPage.less | 7 - client/homebrew/pages/embedPage/sharePage.jsx | 124 ------------------ .../homebrew/pages/embedPage/sharePage.less | 7 - 6 files changed, 4 insertions(+), 267 deletions(-) delete mode 100644 client/homebrew/pages/embedPage/embedPage.jsx delete mode 100644 client/homebrew/pages/embedPage/embedPage.less delete mode 100644 client/homebrew/pages/embedPage/sharePage.jsx delete mode 100644 client/homebrew/pages/embedPage/sharePage.less diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index da16f0a08..bf684cc0e 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -340,15 +340,15 @@ const BrewRenderer = (props)=>{ const brewRenderDivWrapper = ( <> -
{brewRenderFrameContents}
); + if (!props.showToolbar && state.visibility != 'visible') { frameDidMount(); } return ( <> {/*render dummy page while iFrame is mounting.*/} @@ -369,7 +369,7 @@ const BrewRenderer = (props)=>{ {props.showToolbar ? toolbarEl : ''} {/*render in iFrame so broken code doesn't crash the site.*/} - {brewRenderDivWrapper} + {props.showToolbar ? brewRenderFrameWrapper:brewRenderDivWrapper} {state.isMounted &&
} diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx index afe0f2b16..99b3e8fb4 100644 --- a/client/homebrew/homebrew.jsx +++ b/client/homebrew/homebrew.jsx @@ -9,7 +9,6 @@ import HomePage from './pages/homePage/homePage.jsx'; import EditPage from './pages/editPage/editPage.jsx'; import UserPage from './pages/userPage/userPage.jsx'; import SharePage from './pages/sharePage/sharePage.jsx'; -import EmbedPage from './pages/embedPage/embedPage.jsx'; import NewPage from './pages/newPage/newPage.jsx'; import ErrorPage from './pages/errorPage/errorPage.jsx'; import VaultPage from './pages/vaultPage/vaultPage.jsx'; @@ -72,7 +71,7 @@ const Homebrew = (props)=>{ } /> } /> - } /> + } /> } /> } /> } /> diff --git a/client/homebrew/pages/embedPage/embedPage.jsx b/client/homebrew/pages/embedPage/embedPage.jsx deleted file mode 100644 index 9aed04df4..000000000 --- a/client/homebrew/pages/embedPage/embedPage.jsx +++ /dev/null @@ -1,124 +0,0 @@ -import './embedPage.less'; -import React, { useState, useEffect, useCallback } from 'react'; -import Headtags from '../../../../vitreum/headtags.js'; -const Meta = Headtags.Meta; - -import Nav from '@navbar/nav.jsx'; -import Navbar from '@navbar/navbar.jsx'; -import MetadataNav from '@navbar/metadata.navitem.jsx'; -import PrintNavItem from '@navbar/print.navitem.jsx'; -import RecentNavItems from '@navbar/recent.navitem.jsx'; -const { both: RecentNavItem } = RecentNavItems; -import Account from '@navbar/account.navitem.jsx'; -import BrewRenderer from '../../brewRenderer/brewRenderer.jsx'; - -import { DEFAULT_BREW_LOAD } from '../../../../server/brewDefaults.js'; -import { printCurrentBrew, fetchThemeBundle } from '@shared/helpers.js'; - -const SharePage = (props)=>{ - const { brew = DEFAULT_BREW_LOAD, disableMeta = false, share = true } = props; - - const [themeBundle, setThemeBundle] = useState({}); - const [currentBrewRendererPageNum, setCurrentBrewRendererPageNum] = useState(1); - - const handleBrewRendererPageChange = useCallback((pageNumber)=>{ - setCurrentBrewRendererPageNum(pageNumber); - }, []); - - const handleControlKeys = (e)=>{ - if(!(e.ctrlKey || e.metaKey)) return; - const P_KEY = 80; - if(e.keyCode === P_KEY) { - printCurrentBrew(); - e.stopPropagation(); - e.preventDefault(); - } - }; - - useEffect(()=>{ - document.addEventListener('keydown', handleControlKeys); - fetchThemeBundle(undefined, setThemeBundle, brew.renderer, brew.theme); - - return ()=>{ - document.removeEventListener('keydown', handleControlKeys); - }; - }, []); - - const processShareId = ()=>{ - return brew.googleId && !brew.stubbed ? brew.googleId + brew.shareId : brew.shareId; - }; - - const renderEditLink = ()=>{ - if(!brew.editId) return null; - - const editLink = brew.googleId && ! brew.stubbed ? brew.googleId + brew.editId : brew.editId; - - return ( - - edit - - ); - }; - - const titleEl = ( - - {brew.title} - - ); - - const showNav = ( - - - {disableMeta ? titleEl : {titleEl}} - - - - {brew.shareId && ( - <> - - - - source - - - view - - {renderEditLink()} - - download - - - clone to new - - - - )} - - - - - ); - - return ( -
- - {share ? showNav : ''} -
- -
-
- ); -}; - -export default SharePage; diff --git a/client/homebrew/pages/embedPage/embedPage.less b/client/homebrew/pages/embedPage/embedPage.less deleted file mode 100644 index b76dc50f9..000000000 --- a/client/homebrew/pages/embedPage/embedPage.less +++ /dev/null @@ -1,7 +0,0 @@ -.sharePage { - nav .navSection.titleSection { - flex-grow : 1; - justify-content : center; - } - .content { overflow-y : hidden; } -} diff --git a/client/homebrew/pages/embedPage/sharePage.jsx b/client/homebrew/pages/embedPage/sharePage.jsx deleted file mode 100644 index c0838f42b..000000000 --- a/client/homebrew/pages/embedPage/sharePage.jsx +++ /dev/null @@ -1,124 +0,0 @@ -import './sharePage.less'; -import React, { useState, useEffect, useCallback } from 'react'; -import Headtags from '../../../../vitreum/headtags.js'; -const Meta = Headtags.Meta; - -import Nav from '@navbar/nav.jsx'; -import Navbar from '@navbar/navbar.jsx'; -import MetadataNav from '@navbar/metadata.navitem.jsx'; -import PrintNavItem from '@navbar/print.navitem.jsx'; -import RecentNavItems from '@navbar/recent.navitem.jsx'; -const { both: RecentNavItem } = RecentNavItems; -import Account from '@navbar/account.navitem.jsx'; -import BrewRenderer from '../../brewRenderer/brewRenderer.jsx'; - -import { DEFAULT_BREW_LOAD } from '../../../../server/brewDefaults.js'; -import { printCurrentBrew, fetchThemeBundle } from '@shared/helpers.js'; - -const SharePage = (props)=>{ - const { brew = DEFAULT_BREW_LOAD, disableMeta = false, share = true } = props; - - const [themeBundle, setThemeBundle] = useState({}); - const [currentBrewRendererPageNum, setCurrentBrewRendererPageNum] = useState(1); - - const handleBrewRendererPageChange = useCallback((pageNumber)=>{ - setCurrentBrewRendererPageNum(pageNumber); - }, []); - - const handleControlKeys = (e)=>{ - if(!(e.ctrlKey || e.metaKey)) return; - const P_KEY = 80; - if(e.keyCode === P_KEY) { - printCurrentBrew(); - e.stopPropagation(); - e.preventDefault(); - } - }; - - useEffect(()=>{ - document.addEventListener('keydown', handleControlKeys); - fetchThemeBundle(undefined, setThemeBundle, brew.renderer, brew.theme); - - return ()=>{ - document.removeEventListener('keydown', handleControlKeys); - }; - }, []); - - const processShareId = ()=>{ - return brew.googleId && !brew.stubbed ? brew.googleId + brew.shareId : brew.shareId; - }; - - const renderEditLink = ()=>{ - if(!brew.editId) return null; - - const editLink = brew.googleId && ! brew.stubbed ? brew.googleId + brew.editId : brew.editId; - - return ( - - edit - - ); - }; - - const titleEl = ( - - {brew.title} - - ); - - const showNav = ( - - - {disableMeta ? titleEl : {titleEl}} - - - - {brew.shareId && ( - <> - - - - source - - - view - - {renderEditLink()} - - download - - - clone to new - - - - )} - - - - - ); - - return ( -
- - {share ? showNav : ''} -
- -
-
- ); -}; - -export default SharePage; diff --git a/client/homebrew/pages/embedPage/sharePage.less b/client/homebrew/pages/embedPage/sharePage.less deleted file mode 100644 index b76dc50f9..000000000 --- a/client/homebrew/pages/embedPage/sharePage.less +++ /dev/null @@ -1,7 +0,0 @@ -.sharePage { - nav .navSection.titleSection { - flex-grow : 1; - justify-content : center; - } - .content { overflow-y : hidden; } -} From 16bfffe78f82d3f88255858e669e6abb65b4d787 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 28 Mar 2026 18:29:15 -0500 Subject: [PATCH 11/17] I need extra eyes. --- client/homebrew/brewRenderer/brewRenderer.jsx | 1 - client/homebrew/homebrew.jsx | 3 +- client/homebrew/pages/embedPage/embedPage.jsx | 163 ++++++++++++++++++ .../homebrew/pages/embedPage/embedPage.less | 4 + server/app.js | 9 - 5 files changed, 169 insertions(+), 11 deletions(-) create mode 100644 client/homebrew/pages/embedPage/embedPage.jsx create mode 100644 client/homebrew/pages/embedPage/embedPage.less diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index bf684cc0e..56d3f25b2 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -272,7 +272,6 @@ const BrewRenderer = (props)=>{ const frameDidMount = ()=>{ //This triggers when iFrame finishes internal "componentDidMount" scrollToHash(window.location.hash); - console.log('Frame'); setTimeout(()=>{ //We still see a flicker where the style isn't applied yet, so wait 100ms before showing iFrame renderPages(); //Make sure page is renderable before showing setState((prevState)=>({ diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx index 99b3e8fb4..afe0f2b16 100644 --- a/client/homebrew/homebrew.jsx +++ b/client/homebrew/homebrew.jsx @@ -9,6 +9,7 @@ import HomePage from './pages/homePage/homePage.jsx'; import EditPage from './pages/editPage/editPage.jsx'; import UserPage from './pages/userPage/userPage.jsx'; import SharePage from './pages/sharePage/sharePage.jsx'; +import EmbedPage from './pages/embedPage/embedPage.jsx'; import NewPage from './pages/newPage/newPage.jsx'; import ErrorPage from './pages/errorPage/errorPage.jsx'; import VaultPage from './pages/vaultPage/vaultPage.jsx'; @@ -71,7 +72,7 @@ const Homebrew = (props)=>{ } /> } /> - } /> + } /> } /> } /> } /> diff --git a/client/homebrew/pages/embedPage/embedPage.jsx b/client/homebrew/pages/embedPage/embedPage.jsx new file mode 100644 index 000000000..ac68d721f --- /dev/null +++ b/client/homebrew/pages/embedPage/embedPage.jsx @@ -0,0 +1,163 @@ +import './embedPage.less'; +import React, { useState, useEffect, useCallback, useRef } from 'react'; +import Headtags from '../../../../vitreum/headtags.js'; +import MarkdownLegacy from '@shared/markdownLegacy.js'; +import Markdown from '@shared/markdown.js'; + +const Meta = Headtags.Meta; + +import Nav from '@navbar/nav.jsx'; +import Navbar from '@navbar/navbar.jsx'; +import MetadataNav from '@navbar/metadata.navitem.jsx'; +import PrintNavItem from '@navbar/print.navitem.jsx'; +import RecentNavItems from '@navbar/recent.navitem.jsx'; +const { both: RecentNavItem } = RecentNavItems; +import Account from '@navbar/account.navitem.jsx'; +import safeHTML from '../../brewRenderer/safeHTML.js'; + +import { DEFAULT_BREW_LOAD } from '../../../../server/brewDefaults.js'; +import { printCurrentBrew, fetchThemeBundle } from '@shared/helpers.js'; +import _ from 'lodash'; + +const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m; +const PAGEBREAK_REGEX_LEGACY = /\\page(?:break)?/m; +const COLUMNBREAK_REGEX_LEGACY = /\\column(:?break)?/m; + +let renderedPages = []; +let rawPages = []; + +const BrewPage = (props)=>{ + props = { + contents : '', + index : 0, + ...props + }; + const pageRef = useRef(null); + const cleanText = safeHTML(props.contents); + + return
+
+
; +}; + + +const EmbedPage = (props)=>{ + const [displayOptions, setDisplayOptions] = useState({ + zoomLevel : 100, + spread : 'single', + startOnRight : true, + pageShadows : true, + rowGap : 5, + columnGap : 10, + }); + + if(props.renderer == 'legacy') { + rawPages = props.brew.text.split(PAGEBREAK_REGEX_LEGACY); + } else { + rawPages = props.brew.text.split(PAGEBREAK_REGEX_V3); + } + + const pagesStyle = { + zoom : `${displayOptions.zoomLevel}%`, + columnGap : `${displayOptions.columnGap}px`, + rowGap : `${displayOptions.rowGap}px`, + overflowY : 'auto' + }; + + const { brew = DEFAULT_BREW_LOAD, disableMeta = false, share = true } = props; + + const [themeBundle, setThemeBundle] = useState({}); + const [currentBrewRendererPageNum, setCurrentBrewRendererPageNum] = useState(1); + + const handleBrewRendererPageChange = useCallback((pageNumber)=>{ + setCurrentBrewRendererPageNum(pageNumber); + }, []); + + const handleControlKeys = (e)=>{ + if(!(e.ctrlKey || e.metaKey)) return; + const P_KEY = 80; + if(e.keyCode === P_KEY) { + printCurrentBrew(); + e.stopPropagation(); + e.preventDefault(); + } + }; + + useEffect(()=>{ + document.addEventListener('keydown', handleControlKeys); + fetchThemeBundle(undefined, setThemeBundle, brew.renderer, brew.theme); + + return ()=>{ + document.removeEventListener('keydown', handleControlKeys); + }; + }, []); + + const renderStyle = ()=>{ + const themeStyles = themeBundle?.joinedStyles ?? ''; + const cleanStyle = safeHTML(`${themeStyles} \n\n `); + return
; + }; + + const renderPage = (pageText, index)=>{ + + let styles = { + ...(!displayOptions.pageShadows ? { boxShadow: 'none' } : {}), + marginTop : '10px' + // Add more conditions as needed + }; + let classes = 'page'; + let attributes = {}; + + if(props.renderer == 'legacy') { + pageText.replace(COLUMNBREAK_REGEX_LEGACY, '```\n````\n'); // Allow Legacy brews to use `\column(break)` + const html = MarkdownLegacy.render(pageText); + + return ; + } else { + if(pageText.startsWith('\\page')) { + const firstLineTokens = Markdown.marked.lexer(pageText.split('\n', 1)[0])[0].tokens; + const injectedTags = firstLineTokens?.find((obj)=>obj.injectedTags !== undefined)?.injectedTags; + if(injectedTags) { + styles = { ...styles, ...injectedTags.styles }; + styles = _.mapKeys(styles, (v, k)=>k.startsWith('--') ? k : _.camelCase(k)); // Convert CSS to camelCase for React + classes = [classes, injectedTags.classes].join(' ').trim(); + attributes = injectedTags.attributes; + } + pageText = pageText.includes('\n') ? pageText.substring(pageText.indexOf('\n') + 1) : ''; // Remove the \page line + } + + // DO NOT REMOVE!!! REQUIRED FOR BACKWARDS COMPATIBILITY WITH NON-UPGRADABLE VERSIONS OF CHROME. + pageText += `\n\n \n\\column\n `; //Artificial column break at page end to emulate column-fill:auto (until `wide` is used, when column-fill:balance will reappear) + + const html = Markdown.render(pageText, index); + + return ; + } + }; + + const renderPages = ()=>{ + if(props.errors && props.errors.length) + return renderedPages; + + renderedPages.length = 0; + + _.forEach(rawPages, (page, index)=>{ + { + renderedPages[index] = renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range + } + }); + return renderedPages; + }; + + return ( +
+ + {renderStyle()} +
+ {renderPages()} +
+
+ ); +}; + +export default EmbedPage; diff --git a/client/homebrew/pages/embedPage/embedPage.less b/client/homebrew/pages/embedPage/embedPage.less new file mode 100644 index 000000000..c6cfd0438 --- /dev/null +++ b/client/homebrew/pages/embedPage/embedPage.less @@ -0,0 +1,4 @@ +@page { + margin : 0; + size : 8.5in 11in; +} diff --git a/server/app.js b/server/app.js index 6910b386f..f94fc5f1b 100644 --- a/server/app.js +++ b/server/app.js @@ -80,18 +80,9 @@ export default async function createApp(vite) { const herokuRegex = /^https:\/\/(?:homebrewery-pr-\d+\.herokuapp\.com|naturalcrit-pr-\d+\.herokuapp\.com)$/; // Matches any Heroku app - console.log( 'IsLocalEnvironment'); - console.log(isLocalEnvironment); - console.log('localNetworkRegex'); - console.log(localNetworkRegex.test(origin)); - console.log('origin'); - console.log(origin === 'null'); - console.log(origin); - if(!origin || origin === 'null' || allowedOrigins.includes(origin) || herokuRegex.test(origin) || (isLocalEnvironment && localNetworkRegex.test(origin))) { callback(null, true); } else { - console.log(origin, 'not allowed'); callback(new Error('Not allowed by CORS, if you think this is an error, please contact us - Skidoosh')); } }, From 0be91191aa207b3ca2c035dbf9e0c931fd3127bd Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 28 Mar 2026 19:07:46 -0500 Subject: [PATCH 12/17] a little better but WTF --- client/homebrew/pages/embedPage/embedPage.jsx | 2 +- client/homebrew/pages/embedPage/embedPage.less | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/client/homebrew/pages/embedPage/embedPage.jsx b/client/homebrew/pages/embedPage/embedPage.jsx index ac68d721f..57105dba0 100644 --- a/client/homebrew/pages/embedPage/embedPage.jsx +++ b/client/homebrew/pages/embedPage/embedPage.jsx @@ -150,7 +150,7 @@ const EmbedPage = (props)=>{ }; return ( -
+
{renderStyle()}
diff --git a/client/homebrew/pages/embedPage/embedPage.less b/client/homebrew/pages/embedPage/embedPage.less index c6cfd0438..486a51f5e 100644 --- a/client/homebrew/pages/embedPage/embedPage.less +++ b/client/homebrew/pages/embedPage/embedPage.less @@ -2,3 +2,12 @@ margin : 0; size : 8.5in 11in; } + +.page { + page-break-before : always; + page-break-after : always; + display : flex; + flex-direction : column; + height : 100%; + overflow-y : auto; +} From edaff80bd3ae5dcc88b50d346355ed5c734ee35a Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 28 Mar 2026 22:36:04 -0500 Subject: [PATCH 13/17] Fixed! --- client/homebrew/pages/embedPage/embedPage.jsx | 3 +-- client/homebrew/pages/embedPage/embedPage.less | 13 ++----------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/client/homebrew/pages/embedPage/embedPage.jsx b/client/homebrew/pages/embedPage/embedPage.jsx index 57105dba0..f23fb204c 100644 --- a/client/homebrew/pages/embedPage/embedPage.jsx +++ b/client/homebrew/pages/embedPage/embedPage.jsx @@ -101,8 +101,7 @@ const EmbedPage = (props)=>{ const renderPage = (pageText, index)=>{ let styles = { - ...(!displayOptions.pageShadows ? { boxShadow: 'none' } : {}), - marginTop : '10px' + ...(!displayOptions.pageShadows ? { boxShadow: 'none' } : {}) // Add more conditions as needed }; let classes = 'page'; diff --git a/client/homebrew/pages/embedPage/embedPage.less b/client/homebrew/pages/embedPage/embedPage.less index 486a51f5e..29a5f359f 100644 --- a/client/homebrew/pages/embedPage/embedPage.less +++ b/client/homebrew/pages/embedPage/embedPage.less @@ -1,13 +1,4 @@ -@page { - margin : 0; - size : 8.5in 11in; -} - .page { - page-break-before : always; - page-break-after : always; - display : flex; - flex-direction : column; - height : 100%; - overflow-y : auto; + page-break-before : auto; + page-break-after : auto; } From 3f7cc4682bf07fc731da40ab18f39c767ae00d87 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sun, 29 Mar 2026 11:57:21 -0500 Subject: [PATCH 14/17] Override the .homebrew class background on the embed page per recommendation. --- client/homebrew/pages/embedPage/embedPage.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/homebrew/pages/embedPage/embedPage.less b/client/homebrew/pages/embedPage/embedPage.less index 29a5f359f..1b5c82a3d 100644 --- a/client/homebrew/pages/embedPage/embedPage.less +++ b/client/homebrew/pages/embedPage/embedPage.less @@ -2,3 +2,7 @@ page-break-before : auto; page-break-after : auto; } + +.homebrew { + background: unset !important; +} \ No newline at end of file From f9632955b0796663b3298612d44671e76240fd88 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Tue, 31 Mar 2026 12:03:05 -0500 Subject: [PATCH 15/17] Small fixes --- themes/fonts/Journal/fonts.less | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/themes/fonts/Journal/fonts.less b/themes/fonts/Journal/fonts.less index 20190c651..370a07d13 100644 --- a/themes/fonts/Journal/fonts.less +++ b/themes/fonts/Journal/fonts.less @@ -33,26 +33,26 @@ font-family : "NodestoCapsCondensed"; font-style : normal; font-weight : normal; - src : url('../fonts/5e/Nodesto Caps Condensed.woff2'); + src : url('../../../fonts/5e/Nodesto Caps Condensed.woff2'); } @font-face { font-family : "NodestoCapsCondensed"; font-style : normal; font-weight : bold; - src : url('../fonts/5e/Nodesto Caps Condensed Bold.woff2'); + src : url('../../../fonts/5e/Nodesto Caps Condensed Bold.woff2'); } @font-face { font-family : "NodestoCapsCondensed"; font-style : italic; font-weight : normal; - src : url('../fonts/5e/Nodesto Caps Condensed Italic.woff2'); + src : url('../../../fonts/5e/Nodesto Caps Condensed Italic.woff2'); } @font-face { font-family : "NodestoCapsCondensed"; font-style : italic; font-weight : bold; - src : url('../fonts/5e/Nodesto Caps Condensed Bold Italic.woff2'); + src : url('../../../fonts/5e/Nodesto Caps Condensed Bold Italic.woff2'); } From 5bbfc4e5cb17d87188e2b0b87a8ca7e0166ed3e0 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 3 Apr 2026 15:17:43 -0500 Subject: [PATCH 16/17] Remove comment rubish --- server/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/app.js b/server/app.js index f94fc5f1b..e3eadba73 100644 --- a/server/app.js +++ b/server/app.js @@ -83,7 +83,7 @@ export default async function createApp(vite) { if(!origin || origin === 'null' || allowedOrigins.includes(origin) || herokuRegex.test(origin) || (isLocalEnvironment && localNetworkRegex.test(origin))) { callback(null, true); } else { - callback(new Error('Not allowed by CORS, if you think this is an error, please contact us - Skidoosh')); + callback(new Error('Not allowed by CORS, if you think this is an error, please contact us')); } }, methods : ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], From 893deac57ee394f258a905377ca881891128865d Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 3 Apr 2026 15:25:16 -0500 Subject: [PATCH 17/17] Fix CSS --- client/homebrew/pages/embedPage/embedPage.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/homebrew/pages/embedPage/embedPage.less b/client/homebrew/pages/embedPage/embedPage.less index 1b5c82a3d..34c1bad68 100644 --- a/client/homebrew/pages/embedPage/embedPage.less +++ b/client/homebrew/pages/embedPage/embedPage.less @@ -3,6 +3,6 @@ page-break-after : auto; } -.homebrew { +.homebrew:not(:has(>.sitePage)) { background: unset !important; } \ No newline at end of file