diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 8e74473b3..56d3f25b2 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -106,6 +106,7 @@ const BrewRenderer = (props)=>{ currentBrewRendererPageNum : 1, themeBundle : {}, onPageChange : ()=>{}, + showToolbar : true, ...props }; @@ -271,7 +272,6 @@ const BrewRenderer = (props)=>{ const frameDidMount = ()=>{ //This triggers when iFrame finishes internal "componentDidMount" scrollToHash(window.location.hash); - 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)=>({ @@ -301,6 +301,53 @@ 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}/>; + + const brewRenderFrameContents = ( + <> +
+ + {/* Apply CSS from Style tab and render pages from Markdown tab */} + {state.isMounted + && + <> + {renderedStyle} +
+ {renderedPages} +
+ + } +
+ {headerState ? : <>} + + ); + + const brewRenderFrameWrapper = ( + <> + {emitClick();}} + > + {brewRenderFrameContents} + + + ); + + const brewRenderDivWrapper = ( + <> +
+ {brewRenderFrameContents} +
+ + ); + + if (!props.showToolbar && state.visibility != 'visible') { frameDidMount(); } return ( <> {/*render dummy page while iFrame is mounting.*/} @@ -318,32 +365,13 @@ 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.*/} - {emitClick();}} - > -
- - {/* Apply CSS from Style tab and render pages from Markdown tab */} - {state.isMounted - && - <> - {renderedStyle} -
- {renderedPages} -
- - } -
- {headerState ? : <>} - + {props.showToolbar ? brewRenderFrameWrapper:brewRenderDivWrapper} + {state.isMounted && +
+ } ); }; diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx index 252b5ac11..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'; @@ -70,7 +71,8 @@ const Homebrew = (props)=>{
} /> - } /> + } /> + } /> } /> } /> } /> 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 ea262cf03..a60522986 100644 --- a/client/homebrew/navbar/print.navitem.jsx +++ b/client/homebrew/navbar/print.navitem.jsx @@ -1,9 +1,20 @@ 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(){ - return - get PDF - ; +export default function(props){ + return + + export + + + get PDF + + + get HTML + + + get HTML (Zip) + + ; }; diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 176158e2c..55079acb5 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -362,7 +362,7 @@ const EditPage = (props)=>{ {renderAutoSaveButton()} } - + diff --git a/client/homebrew/pages/embedPage/embedPage.jsx b/client/homebrew/pages/embedPage/embedPage.jsx new file mode 100644 index 000000000..f23fb204c --- /dev/null +++ b/client/homebrew/pages/embedPage/embedPage.jsx @@ -0,0 +1,162 @@ +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' } : {}) + // 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..34c1bad68 --- /dev/null +++ b/client/homebrew/pages/embedPage/embedPage.less @@ -0,0 +1,8 @@ +.page { + page-break-before : auto; + page-break-after : auto; +} + +.homebrew:not(:has(>.sitePage)) { + background: unset !important; +} \ No newline at end of file diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index 580d69e76..019d1778b 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -182,7 +182,7 @@ const HomePage =(props)=>{ ? : renderSaveButton()} - + diff --git a/client/homebrew/pages/sharePage/sharePage.jsx b/client/homebrew/pages/sharePage/sharePage.jsx index 093fc8965..c0838f42b 100644 --- a/client/homebrew/pages/sharePage/sharePage.jsx +++ b/client/homebrew/pages/sharePage/sharePage.jsx @@ -16,7 +16,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); @@ -66,40 +66,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/package-lock.json b/package-lock.json index f5ddb544a..e323d8588 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3805,9 +3805,9 @@ } }, "node_modules/@sinclair/typebox": { - "version": "0.34.48", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", - "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", + "version": "0.34.49", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", + "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, "license": "MIT" }, @@ -4110,9 +4110,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "license": "MIT", "dependencies": { @@ -5050,9 +5050,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -6908,9 +6908,9 @@ } }, "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.4.tgz", + "integrity": "sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==", "dev": true, "license": "ISC" }, @@ -7293,9 +7293,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -9911,6 +9911,22 @@ "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==", + "license": "Apache-2.0", + "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.1.1", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.1.1.tgz", @@ -10627,9 +10643,9 @@ "license": "ISC" }, "node_modules/path-to-regexp": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.0.tgz", - "integrity": "sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", "funding": { "type": "opencollective", @@ -10643,9 +10659,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { @@ -12560,9 +12576,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "engines": { "node": ">=12" @@ -12838,9 +12854,9 @@ } }, "node_modules/undici": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.6.tgz", - "integrity": "sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==", + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", + "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", "dev": true, "license": "MIT", "engines": { @@ -13135,9 +13151,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "engines": { "node": ">=12" diff --git a/server/app.js b/server/app.js index 18b1d68bc..e3eadba73 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'; @@ -80,10 +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 - if(!origin || 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')); } }, @@ -438,8 +437,9 @@ export default async function createApp(vite) { return next(); })); - //Share Page - app.get('/share/:id', dbCheck, asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{ + + const shareEmbedCommon = async(req, res)=>{ + const { brew } = req; req.ogMeta = { ...defaultMetaTags, title : `${req.brew.title || 'Untitled Brew'} - ${req.brew.authors[0] || 'No author.'}`, @@ -462,6 +462,17 @@ export default async function createApp(vite) { 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)=>{ + 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)=>{ + await shareEmbedCommon(req,res); return next(); })); diff --git a/shared/helpers.js b/shared/helpers.js index d2a9c8b73..c53fdb5ec 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)=>{ @@ -130,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)=>{ @@ -168,10 +176,45 @@ const debugTextMismatch = (clientTextRaw, serverTextRaw, label)=>{ } }; +const scrapeBrew = ()=>{ + const htmlBody = `\n${window.frames['BrewRenderer'].contentDocument.documentElement.innerHTML}\n`; + return htmlBody; +}; + + +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! +}; + +const scrapeBrewHTML = ()=>{ + const htmlBody = scrapeBrew(); + // Manipulate the body to change all relative path references to full URLs + downloadBlob(htmlBody, 'testDownload.html'); +}; + export { splitTextStyleAndMetadata, printCurrentBrew, fetchThemeBundle, brewSnippetsToJSON, - debugTextMismatch + debugTextMismatch, + scrapeBrewHTML, + scrapeBrewZip, }; 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'] }, });