diff --git a/.circleci/config.yml b/.circleci/config.yml index d1bc1ed80..13d339892 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,17 +2,23 @@ # # Check https://circleci.com/docs/2.0/language-javascript/ for more details # -version: 2 +version: 2.1 + +orbs: + node: circleci/node@3.0.0 + jobs: build: docker: - - image: circleci/node:12.16.3 - - image: circleci/mongo:3.4-jessie + - image: cimg/node:16.11.0 + - image: mongo:4.4 - working_directory: ~/repo + working_directory: ~/homebrewery + executor: node/default steps: - - checkout + - checkout: + path: ~/homebrewery # Download and cache dependencies - restore_cache: @@ -21,12 +27,48 @@ jobs: # fallback to using the latest cache if no exact match is found - v1-dependencies- - - run: npm install + - node/install-npm + - node/install-packages: + app-dir: ~/homebrewery + cache-path: node_modules + override-ci-command: npm i - save_cache: paths: - node_modules key: v1-dependencies-{{ checksum "package.json" }} + - persist_to_workspace: + root: . + paths: + - . + + test: + docker: + - image: cimg/node:16.11.0 + + working_directory: ~/homebrewery + parallelism: 4 + + steps: + - attach_workspace: + at: . + # run tests! - - run: npm run circleci + - run: + name: Test - Basic + command: npm run test:basic + - run: + name: Test - Mustache Spans + command: npm run test:mustache-span + - run: + name: Test - Routes + command: npm run test:route + +workflows: + build_and_test: + jobs: + - build + - test: + requires: + - build \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 4483c7766..33adea2b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,5 @@ -FROM node:14.15 +FROM node:16.11-alpine +RUN apk --no-cache add git ENV NODE_ENV=docker diff --git a/README.md b/README.md index 9a5faeaf4..35f0150d1 100644 --- a/README.md +++ b/README.md @@ -9,37 +9,37 @@ using [Markdown][markdown-url]. It is distributed under the terms of the [MIT Li [markdown-url]: https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet ## Quick Start -The easiest way to get started using the Homebrewery is to use it +The easiest way to get started using The Homebrewery is to use it [on our website][homebrewery-url]. The code is open source, so feel free to -clone it, tinker with it. If you want to make changes to the code, you can run +clone it and tinker with it. If you want to make changes to the code, you can run your own local version for testing by following the installation instructions below. [homebrewery-url]: https://homebrewery.naturalcrit.com ### Installation -First, install three programs that the Homebrewery requires to run and retrieve +First, install three programs that The Homebrewery requires to run and retrieve updates: 1. install [node](https://nodejs.org/en/) 1. install [mongodb](https://www.mongodb.com/try/download/community) (Community version) - For easiest installation, follow these steps: - 1. In the installer, uncheck the option to run as a service - 1. You can install MongoDB Compass if you want a GUI to view your database documents - 1. Go to the C drive and create a folder called "data" - 1. Inside the "data" folder, create a new folder called "db" - 1. Open a command prompt or other terminal and navigate to your mongodb install folder (c:program files\mongo\server\4.4\bin) - 1. In the command prompt, run "mongod", which will start up your local database server - 1. While MongoD is running, open a second command prompt and navigate to the mongodb install folder - 1. In the second command prompt, run "mongo", which allows you to edit the database - 1. Type `use homebrewery` to create the homebrewery database. You should see `switched to db homebrewery` - 1. Type `db.brews.insert({"title":"test"})` to create a blank document. You should see `WriteResult({ "nInserted" : 1 })` - 1. Search in Windows for "Advanced system settings" and open it - 1. Click "Environment variables", find the "path" variable, and double-click to open it - 1. Click "New" and paste in the path to the mongodb "bin" folder - 1. Click "OK", "OK", "OK" to close all the windows -1. install [git](https://git-scm.com/downloads) (select the option that allows Git to run from the command prompt) + For the easiest installation, follow these steps: + 1. In the installer, uncheck the option to run as a service. + 1. You can install MongoDB Compass if you want a GUI to view your database documents. + 1. Go to the C:\ drive and create a folder called "data". + 1. Inside the "data" folder, create a new folder called "db". + 1. Open a command prompt or other terminal and navigate to your MongoDB install folder (C:\Program Files\Mongo\Server\4.4\bin). + 1. In the command prompt, run "mongod", which will start up your local database server. + 1. While MongoD is running, open a second command prompt and navigate to the MongoDB install folder. + 1. In the second command prompt, run "mongo", which allows you to edit the database. + 1. Type `use homebrewery` to create The Homebrewery database. You should see `switched to db homebrewery`. + 1. Type `db.brews.insert({"title":"test"})` to create a blank document. You should see `WriteResult({ "nInserted" : 1 })`. + 1. Search in Windows for "Advanced system settings" and open it. + 1. Click "Environment variables", find the "path" variable, and double-click to open it. + 1. Click "New" and paste in the path to the MongoDB "bin" folder. + 1. Click "OK" three times to close all the windows. +1. install [git](https://git-scm.com/downloads) (select the option that allows Git to run from the command prompt). Checkout the repo ([documentation][github-clone-repo-docs-url]): ``` @@ -54,7 +54,7 @@ the project to run locally. You can set this temporarily in your shell of choice: * Windows Powershell: `$env:NODE_ENV="local"` * Windows CMD: `set NODE_ENV=local` -* Linux / OSX: `export NODE_ENV=local` +* Linux / macOS: `export NODE_ENV=local` Third, you will need to install the Node dependencies, compile the app, and run it using the two commands: @@ -63,7 +63,7 @@ it using the two commands: 1. `npm start` You should now be able to go to [http://localhost:8000](http://localhost:8000) -in your browser and use the Homebrewery offline. +in your browser and use The Homebrewery offline. ### Running the application via Docker @@ -95,11 +95,11 @@ You can check out the [changelog](./changelog.md). ## License -This project is licensed under the [MIT license](./license). Which means you +This project is licensed under the [MIT license](./license), which means you are free to use The Homebrewery in any way that you want, except for claiming that you made it yourself. -If you wish to sell or in some way gain profit for what's created on this site, +If you wish to sell, or in some way gain profit for, what's created on this site, it's your responsibility to ensure you have the proper licenses/rights for any images or resources used. @@ -108,13 +108,12 @@ images or resources used. You are welcome to contribute to the development and maintenance of the project! There are several ways of doing that: - At the moment, we have a huge backlog of [issues][repo-issues-url] and some - of them are outdated, duplicates or doesn't contain any useful info. In order - to help you can [mark duplicates][github-mark-duplicate-url], try to - reproduce some complex or weird issues, try with finding a workaround for a - reported bug or just mention issue managers team to let them know about - outdated issue via `@naturalcrit/issue-managers`. + of them are outdated, duplicates, or don't contain any useful info. To help, you can [mark duplicates][github-mark-duplicate-url], try to + reproduce some complex or weird issues, try finding a workaround for a + reported bug, or just mention our issue managers team to let them know about + outdated issues via `@naturalcrit/issue-managers`. - Our [subreddit][subreddit-url] is constantly growing and there are number of - bug reports: any help with sorting them out is very welcome. + bug reports. Any help with sorting them out is very welcome. - And of course you can contribute by fixing a bug or implementing a new feature by yourself, we are waiting for your [pull requests][github-pr-docs-url]! diff --git a/changelog.md b/changelog.md index e36cbb82a..c4f5e4420 100644 --- a/changelog.md +++ b/changelog.md @@ -3,11 +3,12 @@ h5 { font-size: .35cm !important; } -.taskList li { - list-style-type : none; +.page ul ul { + margin-left: 0px; } .taskList li input { + list-style-type : none; margin-left : -0.52cm; transform: translateY(.05cm); filter: brightness(1.1) drop-shadow(1px 2px 1px #222); @@ -28,9 +29,268 @@ pre { .page p + pre { margin-top : 0.1cm; } + +.page .openSans { + font-family: 'Open Sans'; + font-size: 0.9em; +} ``` -# changelog +## changelog +For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery). + +### Thursday 09/06/2022 - v3.1.1 +{{taskList + +##### Calculuschild: + +* [x] Fixed class table decorations appearing on top of the table in PDF output. + + Fixes issues: [#1784](https://github.com/naturalcrit/homebrewery/issues/1784) + +* [x] Fix bottom decoration on half class tables disappearing when the table is too short. + + Fixes issues: [#2202](https://github.com/naturalcrit/homebrewery/issues/2202) +}} + +### Monday 06/06/2022 - v3.1.0 +{{taskList + +##### G-Ambatte: + +* [x] "Jump to Preview/Editor" buttons added to the divider bar. Easily sync between the editor and preview panels! + + Fixes issues: [#1756](https://github.com/naturalcrit/homebrewery/issues/1756) + +* [x] Speedups to the user page for users with large and/or many brews. + + Fixes issues: [#2147](https://github.com/naturalcrit/homebrewery/issues/2147) + +* [x] Search text on the user page is saved to the URL for easy bookmarking in your browser + + Fixes issues: [#1858](https://github.com/naturalcrit/homebrewery/issues/1858) + +* [x] Added easy login system for offline installs. + + Fixes issues: [#269](https://github.com/naturalcrit/homebrewery/issues/269) + +* [x] New **THUMBNAIL** option in the {{fa,fa-info-circle}} **Properties** menu. This image will show up in social media links. + + Fixes issues: [#820](https://github.com/naturalcrit/homebrewery/issues/820) +}} + +### Wednesday 27/03/2022 - v3.0.8 +{{taskList +* [x] Style updates to user page. + +* [x] Added a logout button (finally)! You can find it under {{openSans **USERNAME {{fa,fa-user}} → LOGOUT {{fas,fa-power-off}}**}} + + Fixes issues: [#303](https://github.com/naturalcrit/homebrewery/issues/303) + +* [x] Clarified the default text when submitting an issue via Reddit post. + +* [x] Fixed broken Table of Contents links in PDFs. (Thanks lucastucious!) + + Fixes issues: [#1749](https://github.com/naturalcrit/homebrewery/issues/1749) + +* [x] Fixed window resizing causing the edit page divider to get lost off of the edge of the page. + + Fixes issues: [#2053](https://github.com/naturalcrit/homebrewery/issues/2053) + +* [x] Fixed Class Table decorations overlapping main text. + + Fixes issues: [#1985](https://github.com/naturalcrit/homebrewery/issues/1985) + +* [x] Updated {{openSans **STYLE EDITOR {{fa,fa-pencil-alt}} → REMOVE DROP CAP {{fas,fa-remove-format}}**}} snippet to also remove small-caps first line font. + +* [x] Background work in preparation for brew themes. +}} + +### Wednesday 02/02/2022 - v3.0.7 +{{taskList +* [x] Revert active line highlighting. + + Fixes issues: [#1913](https://github.com/naturalcrit/homebrewery/issues/1913) + +* [x] Added install steps for Ubuntu. [HERE](https://github.com/naturalcrit/homebrewery/blob/master/install/README.UBUNTU.md) + + Fixes issues: [#1900](https://github.com/naturalcrit/homebrewery/issues/1900) + +* [x] Added social media links to home page. + +* [x] Increase brews visible on the user page to 1,000. + + Fixes issues: [#1943](https://github.com/naturalcrit/homebrewery/issues/1943) + +* [x] Added a Legacy to V3 migration guide under {{openSans **NEED HELP? {{fa,fa-question-circle}} → MIGRATE {{fas,fa-file-import}}**}} + +* [x] Background refactoring and unit tests. +}} + +### Saturday 18/12/2021 - v3.0.6 +{{taskList +* [x] Fixed text wrapping for long strings in code blocks. + + Fixes issues: [#1736](https://github.com/naturalcrit/homebrewery/issues/1736) + +* [x] Code search/replace PC: `CTRL F / CTRL SHIFT F` / Mac: `CMD F / OPTION CMD F` + + Fixes issues: [#1201](https://github.com/naturalcrit/homebrewery/issues/1201) + +* [x] Auto-closing HTML tags and curly braces `{{ }}` +* [x] Highlight current active line + + Fixes issues: [#1202](https://github.com/naturalcrit/homebrewery/issues/1202) + +* [x] Display tabs and trailing spaces + + Fixes issues: [#1622](https://github.com/naturalcrit/homebrewery/issues/1622) + +* [x] Make columns even in V3 Table of Contents. + + Fixes issues: [#1671](https://github.com/naturalcrit/homebrewery/issues/1671) + +* [x] Fix `CTRL P` failing to print from `/new` pages. + + Fixes issues: [#1815](https://github.com/naturalcrit/homebrewery/issues/1815) +}} + +\page + +### Tuesday 07/12/2021 - v3.0.5 +{{taskList +* [x] Fixed paragraph spacing for **note** and **descriptive** boxes in V3. + + Fixes issues: [#1836](https://github.com/naturalcrit/homebrewery/issues/1836) + +* [x] Added a whole bunch of hotkeys: + + * Page Break `CTRL + ENTER` + * Column Break `CTRL + SHIFT + ENTER` + * Bulleted Lists `CTRL + L` + * Numbered Lists `CTRL + SHIFT + L` + * Headers `CTRL + SHIFT + (1-6)` + * Underline `CTRL + U` + * Link `CTRL + K` + * Non-breaking space (\ ) `CTRL + .` + * Add Horizontal Space `CTRL + SHIFT + .` + * Remove Horizontal Space `CTRL + SHIFT + ,` + * Curly Span `CTRL + M` + * Curly Div `CTRL + SHIFT + M` + +* [x] Fixed page numbers in the editor panel getting scrambled when scrolling up and down. + +* [x] Faster swapping between tabs on long brews. + +* [x] Better error messages for common issue with Google Drive credentials expiring. +}} + +### Wednesday 17/11/2021 - v3.0.4 +{{taskList +* [x] Fixed incorrect sorting of Google brews by page count and views on the user page. + + Fixes issues: [#1793](https://github.com/naturalcrit/homebrewery/issues/1793) + +* [x] Added code folding! Only on a page-level for now. Hotkeys `CTRL + [` and `CTRL + ]` to fold/unfold all pages. (Thanks jeddai, new contributor!) + + Fixes issues: [#629](https://github.com/naturalcrit/homebrewery/issues/629) + +* [x] Fixed rendering issues due to the latest Chrome update to version 96. (Also thanks to jeddai!) + + Fixes issues: [#1828](https://github.com/naturalcrit/homebrewery/issues/1828) +}} + +### Wednesday 27/10/2021 - v3.0.3 + +{{taskList +* [x] Moved **Post To Reddit** button from {{fa,fa-info-circle}} **Properties** menu to the **SHARE** {{fa,fa-share-alt}} button as a dropdown. + +* [x] Added a **Copy URL** button to the **SHARE** {{fa,fa-share-alt}} button as a dropdown. + +* [x] Fixed pages being printed directly from `/new` not recognizing the V3 renderer. + + Fixes issues: [#1702](https://github.com/naturalcrit/homebrewery/issues/1702) + +* [x] Updated links to [r/UnearthedArcana](https://www.reddit.com/r/UnearthedArcana/) on home page. + + Fixes issues: [#1744](https://github.com/naturalcrit/homebrewery/issues/1744) + +* [x] Added a [FAQ page](https://homebrewery.naturalcrit.com/faq). + + Fixes issues: [#810](https://github.com/naturalcrit/homebrewery/issues/810) + +* [x] Added {{fa,fa-undo}} **Undo** and {{fa,fa-redo}} **Redo** buttons to the snippet bar. + +}} + +\column + +{{taskList + +* [x] Switching between the {{fa,fa-beer}} **Brew** and {{fa,fa-paint-brush}} **Style** tabs no longer loses your scroll position or undo history. + + Fixes issues: [#1735](https://github.com/naturalcrit/homebrewery/issues/1735) + +* [x] Divider bar between editor and preview panels can no longer be dragged off the edge of the screen. + + Fixes issues: [#1674](https://github.com/naturalcrit/homebrewery/issues/1674) +}} + + +### Wednesday 06/10/2021 - v3.0.2 + +{{taskList +* [x] Fixed V3 **EDITOR → QR Code** snippet not working on `/new` (unsaved) pages. + + Fixes issues: [#1710](https://github.com/naturalcrit/homebrewery/issues/1710) + +* [x] Reorganized several snippets from the **Brew Editor** panel into the **Style Editor** panel. + + Fixes issues: [Reported on Reddit](https://www.reddit.com/r/homebrewery/comments/pm6ki7/two_version_of_class_features_making_it_look_more/) + +* [x] Added a page counter to the right of each `\page` line in V3 to help navigate your brews. Starts counting from page 2. + + Fixes issues: [#846](https://github.com/naturalcrit/homebrewery/issues/846) + +* [x] Moved the changelog to be accessible by clicking on the Homebrewery version number. + + Fixes issues: [#1166](https://github.com/naturalcrit/homebrewery/issues/1166) +}} + +### Friday, 17/09/2021 - v3.0.1 + +{{taskList +* [x] Updated V3 **PHB → Class Feature** snippet to use V3 syntax. + + Fixes issues: [Reported on Reddit](https://www.reddit.com/r/homebrewery/comments/pm6ki7/two_version_of_class_features_making_it_look_more/) + +* [x] Improved V3 **PHB → Monster Stat Block** snippet and styling to allow for easier control of paragraph indentation in the Abilities text. + + Fixes issues: [#181](https://github.com/naturalcrit/homebrewery/issues/181) + +* [x] Improved Legacy **TABLES → Split Table** snippet by removing unneeded column-break backticks. + + Fixes issues: [#844](https://github.com/naturalcrit/homebrewery/issues/844) + +* [x] Changed block elements to use CSS `width` instead of `min-width`. This should make custom styles behave more predictably when trying to resize items. + + Fixes issues: [Reported on Reddit](https://www.reddit.com/r/homebrewery/comments/pohoy3/looking_for_help_with_basic_stuff_in_v3/) + +* [x] Fixed Partial Page Rendering in V3 for large brews + + Fixes issues: [Reported on Reddit](https://www.reddit.com/r/homebrewery/comments/pori3a/weird_behaviour_of_the_brew_after_page_50/) + +* [x] Fixed HTML validation to handle tags starting with 'a', as in `<​aside>`. + + Fixes issues: [#230](https://github.com/naturalcrit/homebrewery/issues/230) + +* [x] Fixed page footers switching side when printing. + + Fixes issues: [#1612](https://github.com/naturalcrit/homebrewery/issues/1612) +}} + + +\page ### Saturday, 11/09/2021 - v3.0.0 diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 260dbdf13..787423528 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -17,6 +17,7 @@ const PAGE_HEIGHT = 1056; const PPR_THRESHOLD = 50; const BrewRenderer = createClass({ + displayName : 'BrewRenderer', getDefaultProps : function() { return { text : '', @@ -187,8 +188,7 @@ const BrewRenderer = createClass({ : null} - +
- -
- {/* Apply CSS from Style tab and render pages from Markdown tab */} - {this.state.isMounted - && - <> - {this.renderStyle()} + + {/* Apply CSS from Style tab and render pages from Markdown tab */} + {this.state.isMounted + && + <> + {this.renderStyle()} +
{this.renderPages()} - - } -
+
+ + } {this.renderPageInfo()} diff --git a/client/homebrew/brewRenderer/errorBar/errorBar.jsx b/client/homebrew/brewRenderer/errorBar/errorBar.jsx index 971903211..dd5ec5bc2 100644 --- a/client/homebrew/brewRenderer/errorBar/errorBar.jsx +++ b/client/homebrew/brewRenderer/errorBar/errorBar.jsx @@ -5,6 +5,7 @@ const _ = require('lodash'); const cx = require('classnames'); const ErrorBar = createClass({ + displayName : 'ErrorBar', getDefaultProps : function() { return { errors : [] diff --git a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx index 819bf53d3..92193d22c 100644 --- a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx +++ b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx @@ -7,6 +7,7 @@ const cx = require('classnames'); //Unused variable const DISMISS_KEY = 'dismiss_notification09-9-21'; const NotificationPopup = createClass({ + displayName : 'NotificationPopup', getInitialState : function() { return { notifications : {} diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 20f084c28..fd9f850d3 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -1,3 +1,4 @@ +/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/ require('./editor.less'); const React = require('react'); const createClass = require('create-react-class'); @@ -25,6 +26,7 @@ const splice = function(str, index, inject){ const Editor = createClass({ + displayName : 'Editor', getDefaultProps : function() { return { brew : { @@ -59,6 +61,16 @@ const Editor = createClass({ window.removeEventListener('resize', this.updateEditorSize); }, + componentDidUpdate : function(prevProps, prevState, snapshot) { + this.highlightCustomMarkdown(); + if(prevProps.moveBrew !== this.props.moveBrew) { + this.brewJump(); + }; + if(prevProps.moveSource !== this.props.moveSource) { + this.sourceJump(); + }; + }, + updateEditorSize : function() { if(this.refs.codeEditor) { let paneHeight = this.refs.main.parentNode.clientHeight; @@ -84,15 +96,20 @@ const Editor = createClass({ }, handleViewChange : function(newView){ + this.props.setMoveArrows(newView === 'text'); this.setState({ view : newView }, this.updateEditorSize); //TODO: not sure if updateeditorsize needed }, getCurrentPage : function(){ - const lines = this.props.brew.text.split('\n').slice(0, this.cursorPosition.line + 1); + const lines = this.props.brew.text.split('\n').slice(0, this.refs.codeEditor.getCursorPosition().line + 1); return _.reduce(lines, (r, line)=>{ - if(line.indexOf('\\page') !== -1) r++; + if( + (this.props.renderer == 'legacy' && line.indexOf('\\page') !== -1) + || + (this.props.renderer == 'V3' && line.match(/^\\page$/)) + ) r++; return r; }, 1); }, @@ -102,73 +119,143 @@ const Editor = createClass({ if(this.state.view === 'text') { const codeMirror = this.refs.codeEditor.codeMirror; - //reset custom text styles - const customHighlights = codeMirror.getAllMarks(); - for (let i=0;i{ // Batch CodeMirror styling + //reset custom text styles + const customHighlights = codeMirror.getAllMarks().filter((mark)=>!mark.__isFold); //Don't undo code folding + for (let i=customHighlights.length - 1;i>=0;i--) customHighlights[i].clear(); - const lineNumbers = _.reduce(this.props.brew.text.split('\n'), (r, line, lineNumber)=>{ + let editorPageCount = 2; // start page count from page 2 - //reset custom line styles - codeMirror.removeLineClass(lineNumber, 'background'); - codeMirror.removeLineClass(lineNumber, 'text'); + _.forEach(this.props.brew.text.split('\n'), (line, lineNumber)=>{ - // Legacy Codemirror styling - if(this.props.renderer == 'legacy') { - if(line.includes('\\page')){ + //reset custom line styles + codeMirror.removeLineClass(lineNumber, 'background', 'pageLine'); + codeMirror.removeLineClass(lineNumber, 'text'); + codeMirror.removeLineClass(lineNumber, 'wrap', 'sourceMoveFlash'); + + // Styling for \page breaks + if((this.props.renderer == 'legacy' && line.includes('\\page')) || + (this.props.renderer == 'V3' && line.match(/^\\page$/))) { + + // add back the original class 'background' but also add the new class '.pageline' codeMirror.addLineClass(lineNumber, 'background', 'pageLine'); - r.push(lineNumber); - } - } + const pageCountElement = Object.assign(document.createElement('span'), { + className : 'editor-page-count', + textContent : editorPageCount + }); + codeMirror.setBookmark({ line: lineNumber, ch: line.length }, pageCountElement); - // New Codemirror styling for V3 renderer - if(this.props.renderer == 'V3') { - if(line.match(/^\\page$/)){ - codeMirror.addLineClass(lineNumber, 'background', 'pageLine'); - r.push(lineNumber); - } + editorPageCount += 1; + }; - if(line.match(/^\\column$/)){ - codeMirror.addLineClass(lineNumber, 'text', 'columnSplit'); - r.push(lineNumber); - } - - // Highlight inline spans {{content}} - if(line.includes('{{') && line.includes('}}')){ - const regex = /{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*\s*|}}/g; - let match; - let blockCount = 0; - while ((match = regex.exec(line)) != null) { - if(match[0].startsWith('{')) { - blockCount += 1; - } else { - blockCount -= 1; - } - if(blockCount < 0) { - blockCount = 0; - continue; - } - codeMirror.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: 'inline-block' }); + // New Codemirror styling for V3 renderer + if(this.props.renderer == 'V3') { + if(line.match(/^\\column$/)){ + codeMirror.addLineClass(lineNumber, 'text', 'columnSplit'); } - } else if(line.trimLeft().startsWith('{{') || line.trimLeft().startsWith('}}')){ - // Highlight block divs {{\n Content \n}} - let endCh = line.length+1; - const match = line.match(/^ *{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])* *$|^ *}}$/); - if(match) - endCh = match.index+match[0].length; - codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' }); + // Highlight inline spans {{content}} + if(line.includes('{{') && line.includes('}}')){ + const regex = /{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*\s*|}}/g; + let match; + let blockCount = 0; + while ((match = regex.exec(line)) != null) { + if(match[0].startsWith('{')) { + blockCount += 1; + } else { + blockCount -= 1; + } + if(blockCount < 0) { + blockCount = 0; + continue; + } + codeMirror.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: 'inline-block' }); + } + } else if(line.trimLeft().startsWith('{{') || line.trimLeft().startsWith('}}')){ + // Highlight block divs {{\n Content \n}} + let endCh = line.length+1; + + const match = line.match(/^ *{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])* *$|^ *}}$/); + if(match) + endCh = match.index+match[0].length; + codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' }); + } } - } - - return r; - }, []); - return lineNumbers; + }); + }); } }, - brewJump : function(){ - const currentPage = this.getCurrentPage(); - window.location.hash = `p${currentPage}`; + brewJump : function(targetPage=this.getCurrentPage()){ + if(!window) return; + // console.log(`Scroll to: p${targetPage}`); + const brewRenderer = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0]; + const currentPos = brewRenderer.scrollTop; + const targetPos = window.frames['BrewRenderer'].contentDocument.getElementById(`p${targetPage}`).getBoundingClientRect().top; + const interimPos = targetPos >= 0 ? -30 : 30; + + const bounceDelay = 100; + const scrollDelay = 500; + + if(!this.throttleBrewMove) { + this.throttleBrewMove = _.throttle((currentPos, interimPos, targetPos)=>{ + brewRenderer.scrollTo({ top: currentPos + interimPos, behavior: 'smooth' }); + setTimeout(()=>{ + brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'smooth', block: 'start' }); + }, bounceDelay); + }, scrollDelay, { leading: true, trailing: false }); + }; + this.throttleBrewMove(currentPos, interimPos, targetPos); + + // const hashPage = (page != 1) ? `p${page}` : ''; + // window.location.hash = hashPage; + }, + + sourceJump : function(targetLine=null){ + if(this.isText()) { + if(targetLine == null) { + targetLine = 0; + + const pageCollection = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('page'); + const brewRendererHeight = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer').item(0).getBoundingClientRect().height; + + let currentPage = 1; + for (const page of pageCollection) { + if(page.getBoundingClientRect().bottom > (brewRendererHeight / 2)) { + currentPage = parseInt(page.id.slice(1)) || 1; + break; + } + } + + const textSplit = this.props.renderer == 'V3' ? /^\\page$/gm : /\\page/; + const textString = this.props.brew.text.split(textSplit).slice(0, currentPage-1).join(textSplit); + const textPosition = textString.length; + const lineCount = textString.match('\n') ? textString.slice(0, textPosition).split('\n').length : 0; + + targetLine = lineCount - 1; //Scroll to `\page`, which is one line back. + + let currentY = this.refs.codeEditor.codeMirror.getScrollInfo().top; + let targetY = this.refs.codeEditor.codeMirror.heightAtLine(targetLine, 'local', true); + + //Scroll 1/10 of the way every 10ms until 1px off. + const incrementalScroll = setInterval(()=>{ + currentY += (targetY - currentY) / 10; + this.refs.codeEditor.codeMirror.scrollTo(null, currentY); + + // Update target: target height is not accurate until within +-10 lines of the visible window + if(Math.abs(targetY - currentY > 100)) + targetY = this.refs.codeEditor.codeMirror.heightAtLine(targetLine, 'local', true); + + // End when close enough + if(Math.abs(targetY - currentY) < 1) { + this.refs.codeEditor.codeMirror.scrollTo(null, targetY); // Scroll any remaining difference + this.refs.codeEditor.setCursorPosition({ line: targetLine + 1, ch: 0 }); + this.refs.codeEditor.codeMirror.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash'); + clearInterval(incrementalScroll); + } + }, 10); + } + } }, //Called when there are changes to the editor's dimensions @@ -176,30 +263,61 @@ const Editor = createClass({ this.refs.codeEditor?.updateSize(); }, + //Called by CodeEditor after document switch, so Snippetbar can refresh UndoHistory + rerenderParent : function (){ + this.forceUpdate(); + }, + renderEditor : function(){ if(this.isText()){ - return ; + return <> + + ; } if(this.isStyle()){ - return ; + return <> + + ; } if(this.isMeta()){ - return ; + return <> + + + ; } }, + redo : function(){ + return this.refs.codeEditor?.redo(); + }, + + historySize : function(){ + return this.refs.codeEditor?.historySize(); + }, + + undo : function(){ + return this.refs.codeEditor?.undo(); + }, + render : function(){ - this.highlightCustomMarkdown(); return (
+ renderer={this.props.renderer} + undo={this.undo} + redo={this.redo} + historySize={this.historySize()} /> {this.renderEditor()}
diff --git a/client/homebrew/editor/editor.less b/client/homebrew/editor/editor.less index cd190ea88..810ee1710 100644 --- a/client/homebrew/editor/editor.less +++ b/client/homebrew/editor/editor.less @@ -4,42 +4,58 @@ width : 100%; .codeEditor{ - height : 100%; + height : 100%; .pageLine{ - background-color : fade(#333, 15%); - border-bottom : #333 solid 1px; + background : #33333328; + border-top : #339 solid 1px; + } + .editor-page-count{ + color : grey; + float : right; } .columnSplit{ - font-style : italic; - color : grey; - background-color : fade(#299, 15%); - border-bottom : #299 solid 1px; + font-style : italic; + color : grey; + background-color : fade(#299, 15%); + border-bottom : #299 solid 1px; } .block{ - color : purple; + color : purple; font-weight : bold; //font-style: italic; } .inline-block{ - color : red; + color : red; font-weight : bold; //font-style: italic; } } .brewJump{ - position: absolute; - background-color: @teal; - cursor: pointer; - width : 30px; - height : 30px; - display : flex; - align-items : center; - bottom : 20px; - right : 20px; - z-index: 1000000; - justify-content:center; + position : absolute; + background-color : @teal; + cursor : pointer; + width : 30px; + height : 30px; + display : flex; + align-items : center; + bottom : 20px; + right : 20px; + z-index : 1000000; + justify-content : center; .tooltipLeft("Jump to brew page"); } + .editorToolbar{ + position: absolute; + top: 5px; + left: 50%; + color: black; + font-size: 13px; + z-index: 9; + span { + padding: 2px 5px; + } + } + } diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.jsx b/client/homebrew/editor/metadataEditor/metadataEditor.jsx index 07158ad45..d692fe262 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.jsx +++ b/client/homebrew/editor/metadataEditor/metadataEditor.jsx @@ -7,7 +7,10 @@ const request = require('superagent'); const SYSTEMS = ['5e', '4e', '3.5e', 'Pathfinder']; +const homebreweryThumbnail = require('../../thumbnail.png'); + const MetadataEditor = createClass({ + displayName : 'MetadataEditor', getDefaultProps : function() { return { metadata : { @@ -24,6 +27,23 @@ const MetadataEditor = createClass({ }; }, + getInitialState : function(){ + return { + showThumbnail : true + }; + }, + + toggleThumbnailDisplay : function(){ + this.setState({ + showThumbnail : !this.state.showThumbnail + }); + }, + + renderThumbnail : function(){ + if(!this.state.showThumbnail) return; + return ; + }, + handleFieldChange : function(name, e){ this.props.onChange(_.merge({}, this.props.metadata, { [name] : e.target.value @@ -58,25 +78,13 @@ const MetadataEditor = createClass({ if(!confirm('Are you REALLY sure? You will lose editor access to this document.')) return; } - request.delete(`/api/${this.props.metadata.editId}`) + request.delete(`/api/${this.props.metadata.googleId ?? ''}${this.props.metadata.editId}`) .send() .end(function(err, res){ window.location.href = '/'; }); }, - getRedditLink : function(){ - const meta = this.props.metadata; - - const shareLink = (meta.googleId || '') + meta.shareId; - const title = `${meta.title} [${meta.systems.join(' ')}]`; - const text = `Hey guys! I've been working on this homebrew. I'd love your feedback. Check it out. - -**[Homebrewery Link](https://homebrewery.naturalcrit.com/share/${shareLink})**`; - - return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(title)}&text=${encodeURIComponent(text)}`; - }, - renderSystems : function(){ return _.map(SYSTEMS, (val)=>{ return