diff --git a/.circleci/config.yml b/.circleci/config.yml
index 8a756b3ded..274ec25ac0 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -67,6 +67,9 @@ jobs:
- run:
name: Test - Definition Lists
command: npm run test:definition-lists
+ - run:
+ name: Test - Hard Breaks
+ command: npm run test:hard-breaks
- run:
name: Test - Variables
command: npm run test:variables
diff --git a/.eslintrc.js b/.eslintrc.js
deleted file mode 100644
index 74e7bb6607..0000000000
--- a/.eslintrc.js
+++ /dev/null
@@ -1,79 +0,0 @@
-module.exports = {
- root : true,
- parserOptions : {
- ecmaVersion : 2021,
- sourceType : 'module',
- ecmaFeatures : {
- jsx : true
- }
- },
- env : {
- browser : true,
- node : true
- },
- plugins : ['react', 'jest'],
- rules : {
- /** Errors **/
- 'camelcase' : ['error', { properties: 'never' }],
- //'func-style' : ['error', 'expression', { allowArrowFunctions: true }],
- 'no-array-constructor' : 'error',
- 'no-iterator' : 'error',
- 'no-nested-ternary' : 'error',
- 'no-new-object' : 'error',
- 'no-proto' : 'error',
- 'react/jsx-no-bind' : ['error', { allowArrowFunctions: true }],
- 'react/jsx-uses-react' : 'error',
- 'react/prefer-es6-class' : ['error', 'never'],
- 'jest/valid-expect' : ['error', { maxArgs: 3 }],
-
- /** Warnings **/
- 'max-lines' : ['warn', {
- max : 200,
- skipComments : true,
- skipBlankLines : true,
- }],
- 'max-depth' : ['warn', { max: 4 }],
- 'max-params' : ['warn', { max: 5 }],
- 'no-restricted-syntax' : ['warn', 'ClassDeclaration', 'SwitchStatement'],
- 'no-unused-vars' : ['warn', {
- vars : 'all',
- args : 'none',
- varsIgnorePattern : 'config|_|cx|createClass'
- }],
- 'react/jsx-uses-vars' : 'warn',
-
- /** Fixable **/
- 'arrow-parens' : ['warn', 'always'],
- 'brace-style' : ['warn', '1tbs', { allowSingleLine: true }],
- 'jsx-quotes' : ['warn', 'prefer-single'],
- 'no-var' : 'warn',
- 'prefer-const' : 'warn',
- 'prefer-template' : 'warn',
- 'quotes' : ['warn', 'single', { 'allowTemplateLiterals': true }],
- 'semi' : ['warn', 'always'],
-
- /** Whitespace **/
- 'array-bracket-spacing' : ['warn', 'never'],
- 'arrow-spacing' : ['warn', { before: false, after: false }],
- 'comma-spacing' : ['warn', { before: false, after: true }],
- 'indent' : ['warn', 'tab', { 'MemberExpression': 'off' }],
- 'keyword-spacing' : ['warn', {
- before : true,
- after : true,
- overrides : {
- if : { 'before': false, 'after': false }
- }
- }],
- 'key-spacing' : ['warn', {
- multiLine : { beforeColon: true, afterColon: true, align: 'colon' },
- singleLine : { beforeColon: false, afterColon: true }
- }],
- 'linebreak-style' : 'off',
- 'no-trailing-spaces' : 'warn',
- 'no-whitespace-before-property' : 'warn',
- 'object-curly-spacing' : ['warn', 'always'],
- 'react/jsx-indent-props' : ['warn', 'tab'],
- 'space-in-parens' : ['warn', 'never'],
- 'template-curly-spacing' : ['warn', 'never'],
- }
-};
diff --git a/.gitattributes b/.gitattributes
index 20eac6017b..2f90f4172b 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,3 @@
-package-lock.json binary
\ No newline at end of file
+package-lock.json binary
+
+*.json text eol=lf
\ No newline at end of file
diff --git a/changelog.md b/changelog.md
index 0cdb89c29d..9d1ddf32d4 100644
--- a/changelog.md
+++ b/changelog.md
@@ -84,6 +84,158 @@ pre {
## changelog
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
+### Wednesday 9/04/2024 - v3.15.0
+
+{{taskList
+##### 5e-Cleric, abquintic, calculuschild, Gazook89, G-Ambatte, Ericsheid, Kaiburr
+
+* [x] New {{openSans **VAULT** {{fas,fa-dungeon}}}} page 🎉🎉🎉
+:
+All **PUBLISHED** brews ({{openSans :fas_circle_info: **Properties**}} menu) will be searchable, by title or author, and filtered by renderer. More features and adjustments will be coming.
+:
+Note: If any of your own brews are not showing up in search (particularly if stored on Google Drive), please edit and re-save to ensure our database has the data needed from document to be searchable.
+
+Fixes issue [#697](https://github.com/naturalcrit/homebrewery/issues/697)
+
+##### Gazook89
+
+* [x] Auto-focus on text editor when switching editor tabs
+}}
+
+### Wednesday 8/28/2024 - v3.14.3
+
+{{taskList
+##### calculuschild, G-Ambatte
+
+* [x] New {{openSans **IMAGES → {{fac,image-wrap-left}} IMAGE WRAP LEFT/RIGHT**}} snippets
+
+Fixes issue [#380](https://github.com/naturalcrit/homebrewery/issues/380)
+
+* [x] Fix v3.14.2 bug with `꞉꞉꞉꞉` failing after tables
+
+##### 5e-Cleric
+
+* [x] Fix Account page crash when not logged in
+
+Fixes issue [#3605](https://github.com/naturalcrit/homebrewery/issues/3605)
+
+##### abquintic
+
+* [x] Fix jump hotkeys conflicting with `CTRL + SHIFT`. Preview and Source movement shortcuts now use `CTRL + SHIFT + META + LEFT\RIGHTARROW`
+
+##### G-Ambatte
+
+* [x] Fix display issue with image wrap icons
+}}
+
+
+### Tuesday 8/27/2024 - v3.14.2
+
+{{taskList
+##### calculuschild
+
+* [x] Reroute invalid urls to homepage
+
+Fixes issues [#3269](https://github.com/naturalcrit/homebrewery/issues/3629)
+
+* [x] Background dependency updates
+
+##### G-Ambatte
+
+* [x] Add route to get brew styling via `/css/shareId`
+
+Fixes issues [#1097](https://github.com/naturalcrit/homebrewery/issues/1097)
+
+* [x] Fix `:emojis:` preventing code folding
+
+Fixes issues [#3604](https://github.com/naturalcrit/homebrewery/issues/3604)
+
+* [x] Fix mask image warping when rotated and stretched
+
+Fixes issues [#3636](https://github.com/naturalcrit/homebrewery/issues/3636)
+
+* [x] Fix Table of Contents uppercasing
+
+Fixes issues [#3572](https://github.com/naturalcrit/homebrewery/issues/3572)
+
+##### abquintic
+
+* [x] Create globally unique Header IDs across pages
+
+Fixes issues [#1430](https://github.com/naturalcrit/homebrewery/issues/1430)
+
+* [x] Fix colon `꞉꞉꞉꞉` being parsed in codeblocks
+
+* [x] Prevent crashes when loading undefined renderer or theme bundle
+
+* [x] Add Jump-To hotkeys
+
+ * Use `CTRL/META + SHIFT + LEFTARROW` to brewJump
+ * Use `CTRL/META + SHIFT + RIGHTARROW` to sourceJump
+
+* [x] Prevent reload from clobbering modified fresh clones
+
+##### 5e-Cleric, Gazook89
+
+* [x] Viewer tools for zoom/page navigation
+}}
+
+### Tuesday 8/13/2024 - v3.14.1
+
+{{taskList
+##### abquintic
+
+* [x] Allow Table of Contents to flow across columns
+
+Fixes issues [#2563](https://github.com/naturalcrit/homebrewery/issues/2563)
+
+* [x] Fix unusual margin spacing for adjacent `.descriptive` and `.wide` blocks
+
+Fixes issues [#2688](https://github.com/naturalcrit/homebrewery/issues/2688)
+
+* [x] Add code folding to :fas_paintbrush: {{openSans **STYLE**}} tab
+
+##### G-Ambatte
+
+* [x] Fix edge case where Table of Contents generator changed capitalization of headings
+
+Fixes issues [#3572](https://github.com/naturalcrit/homebrewery/issues/3572)
+
+* [x] Fix **Ink Friendly** snippet causing unselectable PDF text
+
+Fixes issues [#3563](https://github.com/naturalcrit/homebrewery/issues/3563)
+
+* [x] Prevent brews selecting themselves as a theme
+
+Fixes issues [#3614](https://github.com/naturalcrit/homebrewery/issues/3614)
+
+* [x] Fix info pages (`/faq`, `/migrate`, etc.) showing blank authorship info
+
+Fixes issues [#3568](https://github.com/naturalcrit/homebrewery/issues/3568)
+
+* [x] Add `abs()`, `sign()` and `signed()` functions to variable syntax math handler
+
+Fixes issues [#3537](https://github.com/naturalcrit/homebrewery/issues/3537)
+
+* [x] Fix variable math handler not processing commas (i.e., in `$[max(varA,varB)]`
+
+Fixes issues [#3613](https://github.com/naturalcrit/homebrewery/issues/3613)
+
+* [x] Fix variable math handler scrambling variables with names that are subsets of other variables
+
+Fixes issues [#3622](https://github.com/naturalcrit/homebrewery/issues/3622)
+
+##### calculuschild
+
+* [x] Fix `/migrate` page using an editor context instead of share context
+
+##### 5e-Cleric
+
+* [x] Fix Monster Stat Blocks losing color in Safari
+}}
+
+\page
+
### Monday 7/29/2024 - v3.14.0
{{taskList
@@ -450,7 +602,7 @@ Fixes issue [#2729](https://github.com/naturalcrit/homebrewery/issues/2729),
### Thursday 17/08/2023 - v3.9.2
{{taskList
-##### Calculuschild
+##### calculuschild
* [x] Fix links to certain old Google Drive files
@@ -508,7 +660,7 @@ Fixes issue [#1924](https://github.com/naturalcrit/homebrewery/issues/1924)
### Friday 02/06/2023 - v3.9.0
{{taskList
-##### Calculuschild
+##### calculuschild
* [x] Fix some files not showing up on userpage when user has a large number of brews in Google Drive
@@ -605,7 +757,7 @@ Fixes issues [#2731](https://github.com/naturalcrit/homebrewery/issues/2731)
### Monday 13/03/2023 - v3.7.2
{{taskList
-##### Calculuschild
+##### calculuschild
* [x] Fix wide Monster Stat Blocks not spanning columns on Legacy
}}
@@ -628,7 +780,7 @@ Fixes issues [#1569](https://github.com/naturalcrit/homebrewery/issues/1569)
* [x] Updated the Google Drive icon
* [x] Backend fix to unit tests failing intermittently
-##### Calculuschild
+##### calculuschild
* [x] Fix PDF pixelation on CoverPage text outlines
}}
@@ -640,7 +792,7 @@ Fixes issues [#1569](https://github.com/naturalcrit/homebrewery/issues/1569)
**NOTE:** Some new snippets will now show a {{beta BETA}} tag. Feel free to use them, but be aware we may change how they work depending on your feedback.
}}
-##### Calculuschild
+##### calculuschild
* [x] New {{openSans **IMAGES → WATERCOLOR EDGE** {{fac,mask-edge}} }} and {{openSans **WATERCOLOR CORNER** {{fac,mask-corner}} }} snippets for V3, which adds a stylish watercolor texture to the edge of your images! (Thanks to /u/flamableconcrete on Reddit for providing these image masks!)
@@ -784,7 +936,7 @@ Fixes issues [#1670](https://github.com/naturalcrit/homebrewery/issues/1670)
### Thursday 28/10/2022 - v3.3.1
{{taskList
-##### Calculuschild
+##### calculuschild
* [x] Fixes to several broken CSS styles from v3.3.0
@@ -799,7 +951,7 @@ Fixes issues [#2468](https://github.com/naturalcrit/homebrewery/issues/2468)
### Friday 19/10/2022 - v3.3.0
{{taskList
-##### Calculuschild
+##### calculuschild
* [x] Fix for tables broken by Chrome v106
@@ -882,7 +1034,7 @@ Fixes issues [#2317](https://github.com/naturalcrit/homebrewery/issues/2317), [
### Wednesday 31/08/2022 - v3.2.1
{{taskList
-##### Calculuschild
+##### calculuschild
* [x] Reference Links should now work inside tables
@@ -908,7 +1060,7 @@ Fixes issues [#2317](https://github.com/naturalcrit/homebrewery/issues/2317), [
### Saturday 27/08/2022 - v3.2.0
{{taskList
-##### Calculuschild
+##### calculuschild
* [x] The V3 renderer is now the default for new brews.
@@ -935,7 +1087,7 @@ Fixes issues [#2317](https://github.com/naturalcrit/homebrewery/issues/2317), [
### Thursday 09/06/2022 - v3.1.1
{{taskList
-##### Calculuschild:
+##### calculuschild:
* [x] Fixed class table decorations appearing on top of the table in PDF output.
@@ -1779,4 +1931,4 @@ Massive changelog incoming:
* Added `phb.standalone.css` plus a build system for creating it
* Added page numbers and footer text
-* Page accent now flips each page
\ No newline at end of file
+* Page accent now flips each page
diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx
index 4b82c6bc0a..f3b284a93c 100644
--- a/client/homebrew/brewRenderer/brewRenderer.jsx
+++ b/client/homebrew/brewRenderer/brewRenderer.jsx
@@ -7,6 +7,7 @@ const _ = require('lodash');
const MarkdownLegacy = require('naturalcrit/markdownLegacy.js');
const Markdown = require('naturalcrit/markdown.js');
const ErrorBar = require('./errorBar/errorBar.jsx');
+const ToolBar = require('./toolBar/toolBar.jsx');
//TODO: move to the brew renderer
const RenderWarnings = require('homebrewery/renderWarnings/renderWarnings.jsx');
@@ -60,10 +61,11 @@ const BrewRenderer = (props)=>{
};
const [state, setState] = useState({
- viewablePageNumber : 0,
- height : PAGE_HEIGHT,
- isMounted : false,
- visibility : 'hidden',
+ height : PAGE_HEIGHT,
+ isMounted : false,
+ visibility : 'hidden',
+ zoom : 100,
+ currentPageNumber : 1,
});
const mainRef = useRef(null);
@@ -85,11 +87,14 @@ const BrewRenderer = (props)=>{
}));
};
- const handleScroll = (e)=>{
- const target = e.target;
+ const getCurrentPage = (e)=>{
+ const { scrollTop, clientHeight, scrollHeight } = e.target;
+ const totalScrollableHeight = scrollHeight - clientHeight;
+ const currentPageNumber = Math.ceil((scrollTop / totalScrollableHeight) * rawPages.length);
+
setState((prevState)=>({
...prevState,
- viewablePageNumber : Math.floor(target.scrollTop / target.scrollHeight * rawPages.length)
+ currentPageNumber : currentPageNumber || 1
}));
};
@@ -100,23 +105,12 @@ const BrewRenderer = (props)=>{
if(index == props.currentEditorPage) //Already rendered before this step
return false;
- if(Math.abs(index - state.viewablePageNumber) <= 3)
+ if(Math.abs(index - state.currentPageNumber) <= 3)
return true;
return false;
};
- const renderPageInfo = ()=>{
- return
-
- {props.renderer}
-
-
- {state.viewablePageNumber + 1} / {rawPages.length}
-
-
;
- };
-
const renderDummyPage = (index)=>{
return
@@ -186,11 +180,19 @@ const BrewRenderer = (props)=>{
document.dispatchEvent(new MouseEvent('click'));
};
+ //Toolbar settings:
+ const handleZoom = (newZoom)=>{
+ setState((prevState)=>({
+ ...prevState,
+ zoom : newZoom
+ }));
+ };
+
return (
<>
{/*render dummy page while iFrame is mounting.*/}
{!state.isMounted
- ?
+ ?
{renderDummyPage(1)}
@@ -198,11 +200,13 @@ const BrewRenderer = (props)=>{
: null}
-
+
+
+
{/*render in iFrame so broken code doesn't crash the site.*/}
{
onClick={()=>{emitClick();}}
>
+
{/* Apply CSS from Style tab and render pages from Markdown tab */}
{state.isMounted
&&
<>
{renderStyle()}
-
- {renderPageInfo()}
>
);
};
diff --git a/client/homebrew/brewRenderer/brewRenderer.less b/client/homebrew/brewRenderer/brewRenderer.less
index 28ea8005e5..dca64c455a 100644
--- a/client/homebrew/brewRenderer/brewRenderer.less
+++ b/client/homebrew/brewRenderer/brewRenderer.less
@@ -1,8 +1,9 @@
@import (multiple, less) 'shared/naturalcrit/styles/reset.less';
.brewRenderer {
- will-change : transform;
- overflow-y : scroll;
+ overflow-y : scroll;
+ will-change : transform;
+ padding-top : 30px;
:where(.pages) {
margin : 30px 0px;
& > :where(.page) {
@@ -14,66 +15,31 @@
box-shadow : 1px 4px 14px #000000;
}
}
-
&::-webkit-scrollbar {
- width: 20px;
- &:horizontal{
- height: 20px;
- width:auto;
+ width : 20px;
+ &:horizontal {
+ width : auto;
+ height : 20px;
}
&-thumb {
- background: linear-gradient(90deg, #d3c1af 15px, #00000000 15px);
- &:horizontal{
- background: linear-gradient(0deg, #d3c1af 15px, #00000000 15px);
- }
- }
- &-corner {
- visibility: hidden;
+ background : linear-gradient(90deg, #D3C1AF 15px, #00000000 15px);
+ &:horizontal { background : linear-gradient(0deg, #D3C1AF 15px, #00000000 15px); }
}
+ &-corner { visibility : hidden; }
}
-
-
-
-
-
}
+
.pane { position : relative; }
-.pageInfo {
- position : absolute;
- right : 17px;
- bottom : 0;
- z-index : 1000;
- font-size : 10px;
- font-weight : 800;
- color : white;
- background-color : #333333;
- div {
- display : inline-block;
- padding : 8px 10px;
- &:not(:last-child) { border-right : 1px solid #666666; }
- }
-}
-.ppr_msg {
- position : absolute;
- bottom : 0;
- left : 0px;
- z-index : 1000;
- padding : 8px 10px;
- font-size : 10px;
- font-weight : 800;
- color : white;
- background-color : #333333;
-}
@media print {
+ .toolBar { display : none; }
.brewRenderer {
- height: 100%;
- overflow-y: unset;
+ height : 100%;
+ padding-top : unset;
+ overflow-y : unset;
.pages {
- margin: 0px;
- &>.page {
- box-shadow: unset;
- }
+ margin : 0px;
+ & > .page { box-shadow : unset; }
}
}
}
\ No newline at end of file
diff --git a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx
index cca60bbecf..aa45bbb8e1 100644
--- a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx
+++ b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx
@@ -4,7 +4,7 @@ const _ = require('lodash');
import Dialog from '../../../components/dialog.jsx';
-const DISMISS_KEY = 'dismiss_notification12-04-23';
+const DISMISS_KEY = 'dismiss_notification04-09-24';
const DISMISS_BUTTON =
;
const NotificationPopup = ()=>{
@@ -15,11 +15,12 @@ const NotificationPopup = ()=>{
This website is always improving and we are still adding new features and squashing bugs. Keep the following in mind:
-
- Don't store IMAGES in Google Drive
- Google Drive is not an image service, and will block images from being used
- in brews if they get more views than expected. Google has confirmed they won't fix
- this, so we recommend you look for another image hosting service such as imgur, ImgBB or Google Photos.
+
+ Search brews with our new page!
+ We have been working very hard in making this possible, now you can share your work and look at it in the new Vault page!
+ All PUBLISHED brews will be available to anyone searching there, by title or author, and filtering by renderer.
+
+ More features will be coming.
diff --git a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less
index 26d764aff5..2982055c87 100644
--- a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less
+++ b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less
@@ -1,9 +1,10 @@
.popups {
position : fixed;
- top : @navbarHeight;
+ top : calc(@navbarHeight + @viewerToolsHeight);
right : 24px;
z-index : 10001;
width : 450px;
+ margin-top : 5px;
}
.notificationPopup {
diff --git a/client/homebrew/brewRenderer/toolBar/toolBar.jsx b/client/homebrew/brewRenderer/toolBar/toolBar.jsx
new file mode 100644
index 0000000000..fb3b620676
--- /dev/null
+++ b/client/homebrew/brewRenderer/toolBar/toolBar.jsx
@@ -0,0 +1,162 @@
+require('./toolBar.less');
+const React = require('react');
+const { useState, useEffect } = React;
+const _ = require('lodash');
+
+
+const MAX_ZOOM = 300;
+const MIN_ZOOM = 10;
+
+const ToolBar = ({ onZoomChange, currentPage, onPageChange, totalPages })=>{
+
+ const [zoomLevel, setZoomLevel] = useState(100);
+ const [pageNum, setPageNum] = useState(currentPage);
+
+ useEffect(()=>{
+ onZoomChange(zoomLevel);
+ }, [zoomLevel]);
+
+ useEffect(()=>{
+ setPageNum(currentPage);
+ }, [currentPage]);
+
+ const handleZoomButton = (zoom)=>{
+ setZoomLevel(_.round(_.clamp(zoom, MIN_ZOOM, MAX_ZOOM)));
+ };
+
+ const handlePageInput = (pageInput)=>{
+ if(/[0-9]/.test(pageInput))
+ setPageNum(parseInt(pageInput)); // input type is 'text', so `page` comes in as a string, not number.
+ };
+
+ const scrollToPage = (pageNumber)=>{
+ pageNumber = _.clamp(pageNumber, 1, totalPages);
+ const iframe = document.getElementById('BrewRenderer');
+ const brewRenderer = iframe?.contentWindow?.document.querySelector('.brewRenderer');
+ const page = brewRenderer?.querySelector(`#p${pageNumber}`);
+ page?.scrollIntoView({ block: 'start' });
+ setPageNum(pageNumber);
+ };
+
+
+ const calculateChange = (mode)=>{
+ const iframe = document.getElementById('BrewRenderer');
+ const iframeWidth = iframe.getBoundingClientRect().width;
+ const iframeHeight = iframe.getBoundingClientRect().height;
+ const pages = iframe.contentWindow.document.getElementsByClassName('page');
+
+ let desiredZoom = 0;
+
+ if(mode == 'fill'){
+ // find widest page, in case pages are different widths, so that the zoom is adapted to not cut the widest page off screen.
+ const widestPage = _.maxBy([...pages], 'offsetWidth').offsetWidth;
+
+ desiredZoom = (iframeWidth / widestPage) * 100;
+
+ } else if(mode == 'fit'){
+ // find the page with the largest single dim (height or width) so that zoom can be adapted to fit it.
+ const minDimRatio = [...pages].reduce((minRatio, page) => Math.min(minRatio, iframeWidth / page.offsetWidth, iframeHeight / page.offsetHeight), Infinity);
+
+ desiredZoom = minDimRatio * 100;
+ }
+
+ const margin = 5; // extra space so page isn't edge to edge (not truly "to fill")
+
+ const deltaZoom = (desiredZoom - zoomLevel) - margin;
+ return deltaZoom;
+ };
+
+ return (
+
+ {/*v=====----------------------< Zoom Controls >---------------------=====v*/}
+
+ handleZoomButton(zoomLevel + calculateChange('fill'))}
+ >
+
+
+ handleZoomButton(zoomLevel + calculateChange('fit'))}
+ >
+
+
+ handleZoomButton(zoomLevel - 20)}
+ disabled={zoomLevel <= MIN_ZOOM}
+ >
+
+
+ handleZoomButton(parseInt(e.target.value))}
+ />
+
+
+
+
+ handleZoomButton(zoomLevel + 20)}
+ disabled={zoomLevel >= MAX_ZOOM}
+ >
+
+
+
+
+ {/*v=====----------------------< Page Controls >---------------------=====v*/}
+
+
scrollToPage(pageNum - 1)}
+ disabled={pageNum <= 1}
+ >
+
+
+
+
+ e.target.select()}
+ onChange={(e)=>handlePageInput(e.target.value)}
+ onBlur={()=>scrollToPage(pageNum)}
+ onKeyDown={(e)=>e.key == 'Enter' && scrollToPage(pageNum)}
+ />
+ / {totalPages}
+
+
+
scrollToPage(pageNum + 1)}
+ disabled={pageNum >= totalPages}
+ >
+
+
+
+
+ );
+};
+
+module.exports = ToolBar;
diff --git a/client/homebrew/brewRenderer/toolBar/toolBar.less b/client/homebrew/brewRenderer/toolBar/toolBar.less
new file mode 100644
index 0000000000..d565ca7d48
--- /dev/null
+++ b/client/homebrew/brewRenderer/toolBar/toolBar.less
@@ -0,0 +1,103 @@
+@import (less) './client/icons/customIcons.less';
+
+.toolBar {
+ position : absolute;
+ z-index : 1;
+ box-sizing : border-box;
+ display : flex;
+ flex-wrap : wrap;
+ gap : 8px 30px;
+ align-items : center;
+ justify-content : center;
+ width : 100%;
+ height : auto;
+ padding : 2px 0;
+ font-family : 'Open Sans', sans-serif;
+ color : #CCCCCC;
+ background-color : #555555;
+
+ .group {
+ box-sizing : border-box;
+ display : flex;
+ gap : 0 3px;
+ align-items : center;
+ justify-content : center;
+ height : 28px;
+ }
+
+ .tool {
+ display : flex;
+ align-items : center;
+ }
+
+ input {
+ position : relative;
+ height : 1.5em;
+ padding : 2px 5px;
+ font-family : 'Open Sans', sans-serif;
+ color : #000000;
+ background : #EEEEEE;
+ border : 1px solid gray;
+ &:focus { outline : 1px solid #D3D3D3; }
+
+ // `.range-input` if generic to all range inputs, or `#zoom-slider` if only for zoom slider
+ &.range-input {
+ padding : 2px 0;
+ color : #D3D3D3;
+ accent-color : #D3D3D3;
+
+ &::-webkit-slider-thumb, &::-moz-slider-thumb {
+ width : 5px;
+ height : 5px;
+ cursor : pointer;
+ outline : none;
+ }
+
+ &:hover::after {
+ position : absolute;
+ bottom : -30px;
+ left : 50%;
+ z-index : 1;
+ display : grid;
+ place-items : center;
+ width : 4ch;
+ height : 1.2lh;
+ pointer-events : none;
+ content : attr(value);
+ background-color : #555555;
+ border : 1px solid #A1A1A1;
+ transform : translate(-50%, 50%);
+ }
+ }
+
+ // `.text-input` if generic to all range inputs, or `#page-input` if only for current page input
+ page-input {
+ width : 4ch;
+ margin-right : 1ch;
+ text-align : center;
+ }
+ }
+
+ button {
+ box-sizing : content-box;
+ display : flex;
+ align-items : center;
+ justify-content : center;
+ width : auto;
+ min-width : 46px;
+ height : 100%;
+ padding : 0 0px;
+ font-weight : unset;
+ color : inherit;
+ background-color : unset;
+ &:hover { background-color : #444444; }
+ &:focus { outline : 1px solid #D3D3D3; }
+ &:disabled {
+ color : #777777;
+ background-color : unset !important;
+ }
+ i {
+ font-size:1.2em;
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx
index 1ecdcb22bc..24e975ebc8 100644
--- a/client/homebrew/editor/editor.jsx
+++ b/client/homebrew/editor/editor.jsx
@@ -59,6 +59,8 @@ const Editor = createClass({
this.updateEditorSize();
this.highlightCustomMarkdown();
window.addEventListener('resize', this.updateEditorSize);
+ document.getElementById('BrewRenderer').addEventListener('keydown', this.handleControlKeys);
+ document.addEventListener('keydown', this.handleControlKeys);
const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY);
if(editorTheme) {
@@ -82,6 +84,19 @@ const Editor = createClass({
};
},
+ handleControlKeys : function(e){
+ if(!(e.ctrlKey && e.metaKey)) return;
+ const LEFTARROW_KEY = 37;
+ const RIGHTARROW_KEY = 39;
+ if (e.shiftKey && (e.keyCode == RIGHTARROW_KEY)) this.brewJump();
+ if (e.shiftKey && (e.keyCode == LEFTARROW_KEY)) this.sourceJump();
+ if ((e.keyCode == LEFTARROW_KEY) || (e.keyCode == RIGHTARROW_KEY)) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ },
+
+
updateEditorSize : function() {
if(this.codeEditor.current) {
let paneHeight = this.editor.current.parentNode.clientHeight;
@@ -98,7 +113,10 @@ const Editor = createClass({
this.props.setMoveArrows(newView === 'text');
this.setState({
view : newView
- }, this.updateEditorSize); //TODO: not sure if updateeditorsize needed
+ }, ()=>{
+ this.codeEditor.current?.codeMirror.focus();
+ this.updateEditorSize();
+ }); //TODO: not sure if updateeditorsize needed
},
getCurrentPage : function(){
@@ -119,8 +137,19 @@ const Editor = createClass({
const codeMirror = this.codeEditor.current.codeMirror;
codeMirror.operation(()=>{ // Batch CodeMirror styling
+
+ const foldLines = [];
+
//reset custom text styles
- const customHighlights = codeMirror.getAllMarks().filter((mark)=>!mark.__isFold); //Don't undo code folding
+ const customHighlights = codeMirror.getAllMarks().filter((mark)=>{
+ // Record details of folded sections
+ if(mark.__isFold) {
+ const fold = mark.find();
+ foldLines.push({from: fold.from?.line, to: fold.to?.line});
+ }
+ return !mark.__isFold;
+ }); //Don't undo code folding
+
for (let i=customHighlights.length - 1;i>=0;i--) customHighlights[i].clear();
let editorPageCount = 2; // start page count from page 2
@@ -132,6 +161,11 @@ const Editor = createClass({
codeMirror.removeLineClass(lineNumber, 'text');
codeMirror.removeLineClass(lineNumber, 'wrap', 'sourceMoveFlash');
+ // Don't process lines inside folded text
+ // If the current lineNumber is inside any folded marks, skip line styling
+ if (foldLines.some(fold => lineNumber >= fold.from && lineNumber <= fold.to))
+ return;
+
// Styling for \page breaks
if((this.props.renderer == 'legacy' && line.includes('\\page')) ||
(this.props.renderer == 'V3' && line.match(/^\\page$/))) {
@@ -244,7 +278,7 @@ const Editor = createClass({
// Iterate over conflicting marks and clear them
var marks = codeMirror.findMarks(startPos, endPos);
marks.forEach(function(marker) {
- marker.clear();
+ if(!marker.__isFold) marker.clear();
});
codeMirror.markText(startPos, endPos, { className: 'emoji' });
}
diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx
index 2226c4f3f9..63cf295fe8 100644
--- a/client/homebrew/homebrew.jsx
+++ b/client/homebrew/homebrew.jsx
@@ -10,6 +10,7 @@ const UserPage = require('./pages/userPage/userPage.jsx');
const SharePage = require('./pages/sharePage/sharePage.jsx');
const NewPage = require('./pages/newPage/newPage.jsx');
const ErrorPage = require('./pages/errorPage/errorPage.jsx');
+const VaultPage = require('./pages/vaultPage/vaultPage.jsx');
const AccountPage = require('./pages/accountPage/accountPage.jsx');
const WithRoute = (props)=>{
@@ -71,6 +72,7 @@ const Homebrew = createClass({
} />
} />
} />
+ }/>
} />
} />
} />
diff --git a/client/homebrew/navbar/navbar.less b/client/homebrew/navbar/navbar.less
index d0f2f77e80..4525a193ea 100644
--- a/client/homebrew/navbar/navbar.less
+++ b/client/homebrew/navbar/navbar.less
@@ -1,6 +1,7 @@
@import 'naturalcrit/styles/colors.less';
@navbarHeight : 28px;
+@viewerToolsHeight : 32px;
@keyframes pinkColoring {
0% { color : pink; }
diff --git a/client/homebrew/navbar/vault.navitem.jsx b/client/homebrew/navbar/vault.navitem.jsx
new file mode 100644
index 0000000000..087297011d
--- /dev/null
+++ b/client/homebrew/navbar/vault.navitem.jsx
@@ -0,0 +1,17 @@
+const React = require('react');
+
+const Nav = require('naturalcrit/nav/nav.jsx');
+
+module.exports = function (props) {
+ return (
+
+ Vault
+
+ );
+};
diff --git a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx
index bf0624f1c2..039bc98f5c 100644
--- a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx
+++ b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx
@@ -19,7 +19,8 @@ const BrewItem = createClass({
stubbed : true
},
updateListFilter : ()=>{},
- reportError : ()=>{}
+ reportError : ()=>{},
+ renderStorage : true
};
},
@@ -95,6 +96,7 @@ const BrewItem = createClass({
},
renderStorageIcon : function(){
+ if(!this.props.renderStorage) return;
if(this.props.brew.googleId) {
return
@@ -142,10 +144,14 @@ const BrewItem = createClass({
}
{brew.authors?.map((author, index)=>(
- <>
- {author}
- {index < brew.authors.length - 1 && ', '}
- >))}
+
+ {author === 'hidden'
+ ? {author}
+ : {author}
+ }
+ {index < brew.authors.length - 1 && ', '}
+
+ ))}
diff --git a/client/homebrew/pages/errorPage/errors/errorIndex.js b/client/homebrew/pages/errorPage/errors/errorIndex.js
index 7bf2caae1f..c5c455bbe0 100644
--- a/client/homebrew/pages/errorPage/errors/errorIndex.js
+++ b/client/homebrew/pages/errorPage/errors/errorIndex.js
@@ -2,6 +2,9 @@ const dedent = require('dedent-tabs').default;
const loginUrl = 'https://www.naturalcrit.com/login';
+//001-050 : Brew errors
+//050-100 : Other pages errors
+
const errorIndex = (props)=>{
return {
// Default catch all
@@ -149,8 +152,16 @@ const errorIndex = (props)=>{
**Brew ID:** ${props.brew.brewId}`,
+ //account page when account is not defined
+ '50' : dedent`
+ ## You are not signed in
+
+ You are trying to access the account page, but are not signed in to an account.
+
+ Please login or signup at our [login page](https://www.naturalcrit.com/login?redirect=https://homebrewery.naturalcrit.com/account).`,
+
// Brew locked by Administrators error
- '100' : dedent`
+ '51' : dedent`
## This brew has been locked.
Only an author may request that this lock is removed.
@@ -160,7 +171,12 @@ const errorIndex = (props)=>{
**Brew ID:** ${props.brew.brewId}
**Brew Title:** ${props.brew.brewTitle}`,
+
+ '90' : dedent` An unexpected error occurred while looking for these brews.
+ Try again in a few minutes.`,
+
+ '91' : dedent` An unexpected error occurred while trying to get the total of brews.`,
};
};
-module.exports = errorIndex;
\ No newline at end of file
+module.exports = errorIndex;
diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx
index 490b225961..d7efcaf14b 100644
--- a/client/homebrew/pages/homePage/homePage.jsx
+++ b/client/homebrew/pages/homePage/homePage.jsx
@@ -10,12 +10,12 @@ const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const NewBrewItem = require('../../navbar/newbrew.navitem.jsx');
const HelpNavItem = require('../../navbar/help.navitem.jsx');
+const VaultNavItem = require('../../navbar/vault.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const AccountNavItem = require('../../navbar/account.navitem.jsx');
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
const { fetchThemeBundle } = require('../../../../shared/helpers.js');
-
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
const Editor = require('../../editor/editor.jsx');
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
@@ -76,6 +76,7 @@ const HomePage = createClass({
}
+
diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx
index 89d1df874f..5b0f59c005 100644
--- a/client/homebrew/pages/newPage/newPage.jsx
+++ b/client/homebrew/pages/newPage/newPage.jsx
@@ -84,6 +84,9 @@ const NewPage = createClass({
if(brew.style)
localStorage.setItem(STYLEKEY, brew.style);
localStorage.setItem(METAKEY, JSON.stringify({ 'renderer': brew.renderer, 'theme': brew.theme, 'lang': brew.lang }));
+ if(window.location.pathname != '/new') {
+ window.history.replaceState({}, window.location.title, '/new/');
+ }
},
componentWillUnmount : function() {
document.removeEventListener('keydown', this.handleControlKeys);
diff --git a/client/homebrew/pages/userPage/userPage.jsx b/client/homebrew/pages/userPage/userPage.jsx
index 01778be445..d6fe25b30b 100644
--- a/client/homebrew/pages/userPage/userPage.jsx
+++ b/client/homebrew/pages/userPage/userPage.jsx
@@ -12,6 +12,7 @@ const Account = require('../../navbar/account.navitem.jsx');
const NewBrew = require('../../navbar/newbrew.navitem.jsx');
const HelpNavItem = require('../../navbar/help.navitem.jsx');
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
+const VaultNavitem = require('../../navbar/vault.navitem.jsx');
const UserPage = createClass({
displayName : 'UserPage',
@@ -66,6 +67,7 @@ const UserPage = createClass({
}
+
diff --git a/client/homebrew/pages/vaultPage/vaultPage.jsx b/client/homebrew/pages/vaultPage/vaultPage.jsx
new file mode 100644
index 0000000000..a550ec5787
--- /dev/null
+++ b/client/homebrew/pages/vaultPage/vaultPage.jsx
@@ -0,0 +1,396 @@
+require('./vaultPage.less');
+
+const React = require('react');
+const { useState, useEffect, useRef } = React;
+
+const Nav = require('naturalcrit/nav/nav.jsx');
+const Navbar = require('../../navbar/navbar.jsx');
+const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
+const Account = require('../../navbar/account.navitem.jsx');
+const NewBrew = require('../../navbar/newbrew.navitem.jsx');
+const HelpNavItem = require('../../navbar/help.navitem.jsx');
+const BrewItem = require('../basePages/listPage/brewItem/brewItem.jsx');
+const SplitPane = require('../../../../shared/naturalcrit/splitPane/splitPane.jsx');
+const ErrorIndex = require('../errorPage/errors/errorIndex.js');
+
+const request = require('../../utils/request-middleware.js');
+
+const VaultPage = (props)=>{
+ const [pageState, setPageState] = useState(parseInt(props.query.page) || 1);
+
+ //Response state
+ const [brewCollection, setBrewCollection] = useState(null);
+ const [totalBrews, setTotalBrews] = useState(null);
+ const [searching, setSearching] = useState(false);
+ const [error, setError] = useState(null);
+
+
+ const titleRef = useRef(null);
+ const authorRef = useRef(null);
+ const countRef = useRef(null);
+ const v3Ref = useRef(null);
+ const legacyRef = useRef(null);
+ const submitButtonRef = useRef(null);
+
+ useEffect(()=>{
+ disableSubmitIfFormInvalid();
+ loadPage(pageState, true);
+ }, []);
+
+ const updateStateWithBrews = (brews, page)=>{
+ setBrewCollection(brews || null);
+ setPageState(parseInt(page) || 1);
+ setSearching(false);
+ };
+
+ const updateUrl = (titleValue, authorValue, countValue, v3Value, legacyValue, page)=>{
+ const url = new URL(window.location.href);
+ const urlParams = new URLSearchParams(url.search);
+
+ urlParams.set('title', titleValue);
+ urlParams.set('author', authorValue);
+ urlParams.set('count', countValue);
+ urlParams.set('v3', v3Value);
+ urlParams.set('legacy', legacyValue);
+ urlParams.set('page', page);
+
+ url.search = urlParams.toString();
+ window.history.replaceState(null, '', url.toString());
+ };
+
+ const performSearch = async (title, author, count, v3, legacy, page)=>{
+ updateUrl(title, author, count, v3, legacy, page);
+
+ const response = await request.get(
+ `/api/vault?title=${title}&author=${author}&v3=${v3}&legacy=${legacy}&count=${count}&page=${page}`
+ ).catch((error)=>{
+ console.log('error at loadPage: ', error);
+ setError(error);
+ updateStateWithBrews([], 1);
+ });
+
+ if(response.ok)
+ updateStateWithBrews(response.body.brews, page);
+ };
+
+ const loadTotal = async (title, author, v3, legacy)=>{
+ setTotalBrews(null);
+
+ const response = await request.get(
+ `/api/vault/total?title=${title}&author=${author}&v3=${v3}&legacy=${legacy}`
+ ).catch((error)=>{
+ console.log('error at loadTotal: ', error);
+ setError(error);
+ updateStateWithBrews([], 1);
+ });
+
+ if(response.ok)
+ setTotalBrews(response.body.totalBrews);
+ };
+
+ const loadPage = async (page, updateTotal)=>{
+ if(!validateForm())
+ return;
+
+ setSearching(true);
+ setError(null);
+
+ const title = titleRef.current.value || '';
+ const author = authorRef.current.value || '';
+ const count = countRef.current.value || 10;
+ const v3 = v3Ref.current.checked != false;
+ const legacy = legacyRef.current.checked != false;
+
+ performSearch(title, author, count, v3, legacy, page);
+
+ if(updateTotal)
+ loadTotal(title, author, v3, legacy);
+ };
+
+ const renderNavItems = ()=>(
+
+
+
+ Vault: Search for brews
+
+
+
+
+
+
+
+
+
+ );
+
+ const validateForm = ()=>{
+ //form validity: title or author must be written, and at least one renderer set
+ const isTitleValid = titleRef.current.validity.valid && titleRef.current.value;
+ const isAuthorValid = authorRef.current.validity.valid && authorRef.current.value;
+ const isCheckboxChecked = legacyRef.current.checked || v3Ref.current.checked;
+
+ const isFormValid = (isTitleValid || isAuthorValid) && isCheckboxChecked;
+
+ return isFormValid;
+ };
+
+ const disableSubmitIfFormInvalid = ()=>{
+ submitButtonRef.current.disabled = !validateForm();
+ };
+
+ const renderForm = ()=>(
+
+
Brew Lookup
+
+
+ Title of the brew
+ {
+ if(e.key === 'Enter' && !submitButtonRef.current.disabled)
+ loadPage(1, true);
+ }}
+ placeholder='v3 Reference Document'
+ />
+
+
+
+ Author of the brew
+ {
+ if(e.key === 'Enter' && !submitButtonRef.current.disabled)
+ loadPage(1, true);
+ }}
+ placeholder='Username'
+ />
+
+
+
+ Results per page
+
+ 10
+ 20
+ 40
+ 60
+
+
+
+
+
+ Search for v3 brews
+
+
+
+
+ Search for legacy brews
+
+
+ {
+ loadPage(1, true);
+ }}
+ >
+ Search
+
+
+
+
+ Tips and tricks
+
+
+ Only published brews are searchable via this tool
+
+
+ Usernames are case-sensitive
+
+
+ Use "word"
to match an exact string,
+ and -
to exclude words (at least one word must not be negated)
+
+
+ Some common words like "a", "after", "through", "itself", "here", etc.,
+ are ignored in searches. The full list can be found
+
+ here
+
+
+
+ New features will be coming, such as filters and search by tags.
+
+
+ );
+
+ const renderPaginationControls = ()=>{
+ if(!totalBrews) return null;
+
+ const countInt = parseInt(props.query.count || 20);
+ const totalPages = Math.ceil(totalBrews / countInt);
+
+ let startPage, endPage;
+ if(pageState <= 6) {
+ startPage = 1;
+ endPage = Math.min(totalPages, 10);
+ } else if(pageState + 4 >= totalPages) {
+ startPage = Math.max(1, totalPages - 9);
+ endPage = totalPages;
+ } else {
+ startPage = pageState - 5;
+ endPage = pageState + 4;
+ }
+
+ const pagesAroundCurrent = new Array(endPage - startPage + 1)
+ .fill()
+ .map((_, index)=>(
+ loadPage(startPage + index, false)}
+ >
+ {startPage + index}
+
+ ));
+
+ return (
+
+ );
+ };
+
+ const renderFoundBrews = ()=>{
+ if(searching) {
+ return (
+
+
Searching
+
+ );
+ }
+
+ if(error) {
+ const errorText = ErrorIndex()[error.HBErrorCode.toString()] || '';
+
+ return (
+
+
Error: {errorText}
+
+ );
+ }
+
+ if(!brewCollection) {
+ return (
+
+
No search yet
+
+ );
+ }
+
+ if(brewCollection.length === 0) {
+ return (
+
+
No brews found
+
+ );
+ }
+
+ return (
+
+
+ {`Brews found: `}
+ {totalBrews}
+
+ {brewCollection.map((brew, index)=>{
+ return (
+
+ );
+ })}
+ {renderPaginationControls()}
+
+ );
+ };
+
+ return (
+
+
+
+ {renderNavItems()}
+
+
+ {renderForm()}
+
+
+ {renderFoundBrews()}
+
+
+
+
+ );
+};
+
+module.exports = VaultPage;
diff --git a/client/homebrew/pages/vaultPage/vaultPage.less b/client/homebrew/pages/vaultPage/vaultPage.less
new file mode 100644
index 0000000000..95e6b4c69f
--- /dev/null
+++ b/client/homebrew/pages/vaultPage/vaultPage.less
@@ -0,0 +1,362 @@
+.vaultPage {
+ height : 100%;
+ overflow-y : hidden;
+ background-color : #2C3E50;
+
+ *:not(input) { user-select : none; }
+
+ .content {
+ background : #2C3E50;
+ height: 100%;
+
+ .dataGroup {
+ width : 100%;
+ height : 100%;
+ background : white;
+
+ &.form .brewLookup {
+ position : relative;
+ padding : 50px clamp(20px, 4vw, 50px);
+
+ small {
+ font-size : 10pt;
+ color : #555555;
+
+ a { color : #333333; }
+ }
+
+ code {
+ padding-inline : 5px;
+ background : lightgrey;
+ border-radius : 5px;
+ font-family : monospace;
+ }
+
+ h1, h2, h3, h4 {
+ font-family : 'CodeBold';
+ letter-spacing : 2px;
+ }
+
+ legend {
+ h3 {
+ margin-block : 30px 20px;
+ font-size : 20px;
+ text-align : center;
+ border-bottom : 2px solid;
+ }
+ ul {
+ padding-inline : 30px 10px;
+ li {
+ margin-block : 5px;
+ line-height : calc(1em + 5px);
+ list-style : disc;
+ }
+ }
+ }
+
+ &::after {
+ position : absolute;
+ top : 0;
+ right : 0;
+ left : 0;
+ display : block;
+ padding : 10px;
+ font-weight : 900;
+ color : white;
+ white-space : pre-wrap;
+ content : 'Error:\A At least one renderer should be enabled to make a search';
+ background : rgb(255, 60, 60);
+ opacity : 0;
+ transition : opacity 0.5s;
+ }
+ &:not(:has(input[type='checkbox']:checked))::after { opacity : 1; }
+
+ .formTitle {
+ margin : 20px 0;
+ font-size : 30px;
+ color : black;
+ text-align : center;
+ border-bottom : 2px solid;
+ }
+
+ .formContents {
+ position : relative;
+ display : flex;
+ flex-direction : column;
+
+ label {
+ display : flex;
+ align-items : center;
+ margin : 10px 0;
+ }
+ select { margin : 0 10px; }
+
+ input {
+ margin : 0 10px;
+
+ &:invalid { background : rgb(255, 188, 181); }
+
+ &[type='checkbox'] {
+ position : relative;
+ display : inline-block;
+ width : 50px;
+ height : 30px;
+ font-family : 'WalterTurncoat';
+ font-size : 20px;
+ font-weight : 800;
+ color : white;
+ letter-spacing : 2px;
+ appearance : none;
+ background : red;
+ isolation : isolate;
+ border-radius : 5px;
+
+ &::before,&::after {
+ position : absolute;
+ inset : 0;
+ z-index : 5;
+ padding-top : 2px;
+ text-align : center;
+ }
+
+ &::before {
+ display : block;
+ content : 'No';
+ }
+
+ &::after {
+ display : none;
+ content : 'Yes';
+ }
+
+ &:checked {
+ background : green;
+
+ &::before { display : none; }
+ &::after { display : block; }
+ }
+ }
+ }
+
+ #searchButton {
+ position : absolute;
+ right : 20px;
+ bottom : 0;
+
+ i {
+ margin-left : 10px;
+ animation-duration : 1000s;
+ }
+ }
+ }
+ }
+
+ &.resultsContainer {
+ display : flex;
+ flex-direction : column;
+ height : 100%;
+ overflow-y : auto;
+ font-family : 'BookInsanityRemake';
+ font-size : 0.34cm;
+
+ h3 {
+ font-family : 'Open Sans';
+ font-weight : 900;
+ color : white;
+ }
+
+ .foundBrews {
+ position : relative;
+ width : 100%;
+ height : 100%;
+ max-height : 100%;
+ padding : 50px 50px 70px 50px;
+ overflow-y : scroll;
+ background-color : #2C3E50;
+
+ h3 { font-size : 25px; }
+
+ &.noBrews {
+ display : grid;
+ place-items : center;
+ color : white;
+ }
+
+ &.searching {
+ display : grid;
+ place-items : center;
+ color : white;
+
+ h3 { position : relative; }
+
+ h3.searchAnim::after {
+ position : absolute;
+ top : 50%;
+ right : 0;
+ width : max-content;
+ height : 1em;
+ content : '';
+ translate : calc(100% + 5px) -50%;
+ animation : trailingDots 2s ease infinite;
+ }
+ }
+
+ .totalBrews {
+ position : fixed;
+ right : 0;
+ bottom : 0;
+ z-index : 1000;
+ padding : 8px 10px;
+ font-family : 'Open Sans';
+ font-size : 11px;
+ font-weight : 800;
+ color : white;
+ background-color : #333333;
+
+ .searchAnim {
+ position : relative;
+ display : inline-block;
+ width : 3ch;
+ height : 1em;
+ }
+
+ .searchAnim::after {
+ position : absolute;
+ top : 50%;
+ right : 0;
+ width : max-content;
+ height : 1em;
+ content : '';
+ translate : -50% -50%;
+ animation : trailingDots 2s ease infinite;
+ }
+ }
+
+ .brewItem {
+ width : 47%;
+ margin-right : 40px;
+ color : black;
+ isolation:isolate;
+
+ &:after {
+ position:absolute;
+ inset:0;
+ display:block;
+ content:'';
+ background-image : url('/assets/parchmentBackground.jpg');
+ z-index:-1;
+ }
+
+ &:nth-child(even of .brewItem) { margin-right : 0; }
+
+ h2 {
+ font-family : 'MrEavesRemake';
+ font-size : 0.75cm;
+ font-weight : 800;
+ line-height : 0.988em;
+ color : var(--HB_Color_HeaderText);
+ }
+ .info {
+ font-family : 'ScalySansRemake';
+ font-size : 1.2em;
+ position:relative;
+ z-index:2;
+
+ >span {
+ margin-right : 12px;
+ line-height : 1.5em;
+ }
+ }
+ .links {
+ z-index:2;
+ }
+
+ hr {
+ margin: 0px;
+ visibility: hidden;
+ }
+
+ .thumbnail {
+ z-index:1;
+ }
+ }
+
+ .paginationControls {
+ position : absolute;
+ left : 50%;
+ display : grid;
+ grid-template-areas : 'previousPage currentPage nextPage';
+ grid-template-columns : 50px 1fr 50px;
+ place-items : center;
+ width : auto;
+ translate : -50%;
+
+ .pages {
+ display : flex;
+ grid-area : currentPage;
+ justify-content : space-evenly;
+ width : 100%;
+ height : 100%;
+ padding : 5px 8px;
+ text-align : center;
+
+ .pageNumber {
+ margin-inline : 1vw;
+ font-family : 'Open Sans';
+ font-weight : 900;
+ color : white;
+ text-underline-position : under;
+ text-wrap : nowrap;
+ cursor : pointer;
+
+ &.currentPage {
+ color : gold;
+ text-decoration : underline;
+ pointer-events : none;
+ }
+
+ &.firstPage { margin-right : -5px; }
+
+ &.lastPage { margin-left : -5px; }
+ }
+ }
+
+ button {
+ width : max-content;
+
+ &.previousPage { grid-area : previousPage; }
+
+ &.nextPage { grid-area : nextPage; }
+ }
+
+ }
+ }
+ }
+ }
+ }
+}
+
+@keyframes trailingDots {
+
+ 0%,
+ 32% { content : ' .'; }
+
+ 33%,
+ 65% { content : ' ..'; }
+
+ 66%,
+ 100% { content : ' ...'; }
+}
+
+// media query for when the page is smaller than 1079 px in width
+@media screen and (max-width : 1079px) {
+ .vaultPage .content {
+
+ .dataGroup.form .brewLookup { padding : 1px 20px 20px 10px; }
+
+ .dataGroup.resultsContainer .foundBrews .brewItem {
+ width : 100%;
+ margin-inline : auto;
+ }
+ }
+}
diff --git a/client/icons/customIcons.less b/client/icons/customIcons.less
index dd66053268..0d462833da 100644
--- a/client/icons/customIcons.less
+++ b/client/icons/customIcons.less
@@ -1,57 +1,75 @@
.fac {
- display : inline-block;
+ display : inline-block;
+ background-color : currentColor;
+ mask-size : contain;
+ mask-repeat : no-repeat;
+ mask-position : center;
+ width : 1em;
+ aspect-ratio : 1;
}
.position-top-left {
- content: url('../icons/position-top-left.svg');
+ mask-image: url('../icons/position-top-left.svg');
}
.position-top-right {
- content: url('../icons/position-top-right.svg');
+ mask-image: url('../icons/position-top-right.svg');
}
.position-bottom-left {
- content: url('../icons/position-bottom-left.svg');
+ mask-image: url('../icons/position-bottom-left.svg');
}
.position-bottom-right {
- content: url('../icons/position-bottom-right.svg');
+ mask-image: url('../icons/position-bottom-right.svg');
}
.position-top {
- content: url('../icons/position-top.svg');
+ mask-image: url('../icons/position-top.svg');
}
.position-right {
- content: url('../icons/position-right.svg');
+ mask-image: url('../icons/position-right.svg');
}
.position-bottom {
- content: url('../icons/position-bottom.svg');
+ mask-image: url('../icons/position-bottom.svg');
}
.position-left {
- content: url('../icons/position-left.svg');
+ mask-image: url('../icons/position-left.svg');
}
.mask-edge {
- content: url('../icons/mask-edge.svg');
+ mask-image: url('../icons/mask-edge.svg');
}
.mask-corner {
- content: url('../icons/mask-corner.svg');
+ mask-image: url('../icons/mask-corner.svg');
}
.mask-center {
- content: url('../icons/mask-center.svg');
+ mask-image: url('../icons/mask-center.svg');
}
.book-front-cover {
- content: url('../icons/book-front-cover.svg');
+ mask-image: url('../icons/book-front-cover.svg');
}
.book-back-cover {
- content: url('../icons/book-back-cover.svg');
+ mask-image: url('../icons/book-back-cover.svg');
}
.book-inside-cover {
- content: url('../icons/book-inside-cover.svg');
+ mask-image: url('../icons/book-inside-cover.svg');
}
.book-part-cover {
- content: url('../icons/book-part-cover.svg');
+ mask-image: url('../icons/book-part-cover.svg');
+}
+.image-wrap-left {
+ mask-image: url('../icons/image-wrap-left.svg');
+}
+.image-wrap-right {
+ mask-image: url('../icons/image-wrap-right.svg');
}
.davek {
- content: url('../icons/Davek.svg');
+ mask-image: url('../icons/Davek.svg');
}
.rellanic {
- content: url('../icons/Rellanic.svg');
+ mask-image: url('../icons/Rellanic.svg');
}
.iokharic {
- content: url('../icons/Iokharic.svg');
+ mask-image: url('../icons/Iokharic.svg');
+}
+.zoom-to-fit {
+ mask-image: url('../icons/zoom-to-fit.svg');
+}
+.fit-width {
+ mask-image: url('../icons/fit-width.svg');
}
diff --git a/client/icons/fit-width.svg b/client/icons/fit-width.svg
new file mode 100644
index 0000000000..dd3e52f757
--- /dev/null
+++ b/client/icons/fit-width.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/icons/image-wrap-left.svg b/client/icons/image-wrap-left.svg
new file mode 100644
index 0000000000..fe1024e431
--- /dev/null
+++ b/client/icons/image-wrap-left.svg
@@ -0,0 +1,58 @@
+
+
diff --git a/client/icons/image-wrap-right.svg b/client/icons/image-wrap-right.svg
new file mode 100644
index 0000000000..336a20b641
--- /dev/null
+++ b/client/icons/image-wrap-right.svg
@@ -0,0 +1,58 @@
+
+
diff --git a/client/icons/zoom-to-fit.svg b/client/icons/zoom-to-fit.svg
new file mode 100644
index 0000000000..5179ec45e2
--- /dev/null
+++ b/client/icons/zoom-to-fit.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 0000000000..25d0395c77
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,71 @@
+import react from "eslint-plugin-react";
+import jest from "eslint-plugin-jest";
+import globals from "globals";
+
+export default [{
+ ignores: ["build/"]
+ },
+ {
+ files : ['**/*.js', '**/*.jsx'],
+ plugins : { react, jest },
+ languageOptions : {
+ ecmaVersion : "latest",
+ sourceType : "module",
+ parserOptions : { ecmaFeatures: { jsx: true } },
+ globals : { ...globals.browser, ...globals.node }
+ },
+ rules: {
+ /** Errors **/
+ "camelcase" : ["error", { properties: "never" }],
+ "no-array-constructor" : "error",
+ "no-iterator" : "error",
+ "no-nested-ternary" : "error",
+ "no-new-object" : "error",
+ "no-proto" : "error",
+ "react/jsx-no-bind" : ["error", { allowArrowFunctions: true }],
+ "react/jsx-uses-react" : "error",
+ "react/prefer-es6-class" : ["error", "never"],
+ "jest/valid-expect" : ["error", { maxArgs: 3 }],
+
+ /** Warnings **/
+ "max-lines" : ["warn", { max: 200, skipComments: true, skipBlankLines: true }],
+ "max-depth" : ["warn", { max: 4 }],
+ "max-params" : ["warn", { max: 5 }],
+ "no-restricted-syntax" : ["warn", "ClassDeclaration", "SwitchStatement"],
+ "no-unused-vars" : ["warn", { vars: "all", args: "none", varsIgnorePattern: "config|_|cx|createClass" }],
+ "react/jsx-uses-vars" : "warn",
+
+ /** Fixable **/
+ "arrow-parens" : ["warn", "always"],
+ "brace-style" : ["warn", "1tbs", { allowSingleLine: true }],
+ "jsx-quotes" : ["warn", "prefer-single"],
+ "no-var" : "warn",
+ "prefer-const" : "warn",
+ "prefer-template" : "warn",
+ "quotes" : ["warn", "single", { allowTemplateLiterals: true }],
+ "semi" : ["warn", "always"],
+
+ /** Whitespace **/
+ "array-bracket-spacing" : ["warn", "never"],
+ "arrow-spacing" : ["warn", { before: false, after: false }],
+ "comma-spacing" : ["warn", { before: false, after: true }],
+ "indent" : ["warn", "tab", { MemberExpression: "off" }],
+ "linebreak-style" : "off",
+ "no-trailing-spaces" : "warn",
+ "no-whitespace-before-property" : "warn",
+ "object-curly-spacing" : ["warn", "always"],
+ "react/jsx-indent-props" : ["warn", "tab"],
+ "space-in-parens" : ["warn", "never"],
+ "template-curly-spacing" : ["warn", "never"],
+ "keyword-spacing" : ["warn", {
+ before : true,
+ after : true,
+ overrides : { if: { before: false, after: false } }
+ }],
+ "key-spacing" : ["warn", {
+ multiLine : { beforeColon: true, afterColon: true, align: "colon" },
+ singleLine : { beforeColon: false, afterColon: true }
+ }]
+ }
+ }
+];
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index e025814aef..ed0cbf1f1f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,20 +1,20 @@
{
"name": "homebrewery",
- "version": "3.14.0",
+ "version": "3.15.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "homebrewery",
- "version": "3.14.0",
+ "version": "3.15.0",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@babel/core": "^7.25.2",
- "@babel/plugin-transform-runtime": "^7.24.7",
- "@babel/preset-env": "^7.25.3",
+ "@babel/plugin-transform-runtime": "^7.25.4",
+ "@babel/preset-env": "^7.25.4",
"@babel/preset-react": "^7.24.7",
- "@googleapis/drive": "^8.11.0",
+ "@googleapis/drive": "^8.14.0",
"body-parser": "^1.20.2",
"classnames": "^2.5.1",
"codemirror": "^5.65.6",
@@ -33,32 +33,33 @@
"lodash": "^4.17.21",
"marked": "11.2.0",
"marked-emoji": "^1.4.2",
- "marked-extended-tables": "^1.0.8",
+ "marked-extended-tables": "^1.0.10",
"marked-gfm-heading-id": "^3.2.0",
"marked-smartypants-lite": "^1.0.2",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1",
- "mongoose": "^8.5.2",
+ "mongoose": "^8.6.1",
"nanoid": "3.3.4",
"nconf": "^0.12.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-frame-component": "^4.1.3",
- "react-router-dom": "6.26.0",
+ "react-router-dom": "6.26.1",
"sanitize-filename": "1.6.3",
- "superagent": "^9.0.2",
+ "superagent": "^10.1.0",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
},
"devDependencies": {
- "@stylistic/stylelint-plugin": "^3.0.0",
- "eslint": "^8.57.0",
- "eslint-plugin-jest": "^28.8.0",
- "eslint-plugin-react": "^7.35.0",
+ "@stylistic/stylelint-plugin": "^3.0.1",
+ "eslint": "^9.9.1",
+ "eslint-plugin-jest": "^28.8.3",
+ "eslint-plugin-react": "^7.35.2",
+ "globals": "^15.9.0",
"jest": "^29.7.0",
"jest-expect-message": "^1.1.3",
"postcss-less": "^6.0.0",
- "stylelint": "^16.8.0",
- "stylelint-config-recess-order": "^5.0.1",
+ "stylelint": "^16.9.0",
+ "stylelint-config-recess-order": "^5.1.0",
"stylelint-config-recommended": "^14.0.1",
"supertest": "^7.0.0"
},
@@ -94,10 +95,9 @@
}
},
"node_modules/@babel/compat-data": {
- "version": "7.25.2",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz",
- "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz",
+ "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==",
"engines": {
"node": ">=6.9.0"
}
@@ -133,12 +133,11 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.25.0",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz",
- "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.4.tgz",
+ "integrity": "sha512-NFtZmZsyzDPJnk9Zg3BbTfKKc9UlHYzD0E//p2Z3B9nCwwtJW9T0gVbCz8+fBngnn4zf1Dr3IK8PHQQHq0lDQw==",
"dependencies": {
- "@babel/types": "^7.25.0",
+ "@babel/types": "^7.25.4",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
"jsesc": "^2.5.1"
@@ -189,17 +188,16 @@
}
},
"node_modules/@babel/helper-create-class-features-plugin": {
- "version": "7.25.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.0.tgz",
- "integrity": "sha512-GYM6BxeQsETc9mnct+nIIpf63SAyzvyYN7UB/IlTyd+MBg06afFGp0mIeUqGyWgS2mxad6vqbMrHVlaL3m70sQ==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz",
+ "integrity": "sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.24.7",
"@babel/helper-member-expression-to-functions": "^7.24.8",
"@babel/helper-optimise-call-expression": "^7.24.7",
"@babel/helper-replace-supers": "^7.25.0",
"@babel/helper-skip-transparent-expression-wrappers": "^7.24.7",
- "@babel/traverse": "^7.25.0",
+ "@babel/traverse": "^7.25.4",
"semver": "^6.3.1"
},
"engines": {
@@ -437,12 +435,11 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.25.3",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz",
- "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz",
+ "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==",
"dependencies": {
- "@babel/types": "^7.25.2"
+ "@babel/types": "^7.25.4"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -837,15 +834,14 @@
}
},
"node_modules/@babel/plugin-transform-async-generator-functions": {
- "version": "7.25.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz",
- "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz",
+ "integrity": "sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==",
"dependencies": {
"@babel/helper-plugin-utils": "^7.24.8",
"@babel/helper-remap-async-to-generator": "^7.25.0",
"@babel/plugin-syntax-async-generators": "^7.8.4",
- "@babel/traverse": "^7.25.0"
+ "@babel/traverse": "^7.25.4"
},
"engines": {
"node": ">=6.9.0"
@@ -902,13 +898,12 @@
}
},
"node_modules/@babel/plugin-transform-class-properties": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz",
- "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz",
+ "integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==",
"dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.24.7",
- "@babel/helper-plugin-utils": "^7.24.7"
+ "@babel/helper-create-class-features-plugin": "^7.25.4",
+ "@babel/helper-plugin-utils": "^7.24.8"
},
"engines": {
"node": ">=6.9.0"
@@ -935,16 +930,15 @@
}
},
"node_modules/@babel/plugin-transform-classes": {
- "version": "7.25.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.0.tgz",
- "integrity": "sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz",
+ "integrity": "sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.24.7",
- "@babel/helper-compilation-targets": "^7.24.8",
+ "@babel/helper-compilation-targets": "^7.25.2",
"@babel/helper-plugin-utils": "^7.24.8",
"@babel/helper-replace-supers": "^7.25.0",
- "@babel/traverse": "^7.25.0",
+ "@babel/traverse": "^7.25.4",
"globals": "^11.1.0"
},
"engines": {
@@ -954,6 +948,14 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-classes/node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/@babel/plugin-transform-computed-properties": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz",
@@ -1388,13 +1390,12 @@
}
},
"node_modules/@babel/plugin-transform-private-methods": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz",
- "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz",
+ "integrity": "sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==",
"dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.24.7",
- "@babel/helper-plugin-utils": "^7.24.7"
+ "@babel/helper-create-class-features-plugin": "^7.25.4",
+ "@babel/helper-plugin-utils": "^7.24.8"
},
"engines": {
"node": ">=6.9.0"
@@ -1533,15 +1534,14 @@
}
},
"node_modules/@babel/plugin-transform-runtime": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz",
- "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.4.tgz",
+ "integrity": "sha512-8hsyG+KUYGY0coX6KUCDancA0Vw225KJ2HJO0yCNr1vq5r+lJTleDaJf0K7iOhjw4SWhu03TMBzYTJ9krmzULQ==",
"dependencies": {
"@babel/helper-module-imports": "^7.24.7",
- "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.8",
"babel-plugin-polyfill-corejs2": "^0.4.10",
- "babel-plugin-polyfill-corejs3": "^0.10.1",
+ "babel-plugin-polyfill-corejs3": "^0.10.6",
"babel-plugin-polyfill-regenerator": "^0.6.1",
"semver": "^6.3.1"
},
@@ -1676,13 +1676,12 @@
}
},
"node_modules/@babel/plugin-transform-unicode-sets-regex": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz",
- "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz",
+ "integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==",
"dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.24.7",
- "@babel/helper-plugin-utils": "^7.24.7"
+ "@babel/helper-create-regexp-features-plugin": "^7.25.2",
+ "@babel/helper-plugin-utils": "^7.24.8"
},
"engines": {
"node": ">=6.9.0"
@@ -1692,12 +1691,11 @@
}
},
"node_modules/@babel/preset-env": {
- "version": "7.25.3",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz",
- "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.4.tgz",
+ "integrity": "sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==",
"dependencies": {
- "@babel/compat-data": "^7.25.2",
+ "@babel/compat-data": "^7.25.4",
"@babel/helper-compilation-targets": "^7.25.2",
"@babel/helper-plugin-utils": "^7.24.8",
"@babel/helper-validator-option": "^7.24.8",
@@ -1726,13 +1724,13 @@
"@babel/plugin-syntax-top-level-await": "^7.14.5",
"@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
"@babel/plugin-transform-arrow-functions": "^7.24.7",
- "@babel/plugin-transform-async-generator-functions": "^7.25.0",
+ "@babel/plugin-transform-async-generator-functions": "^7.25.4",
"@babel/plugin-transform-async-to-generator": "^7.24.7",
"@babel/plugin-transform-block-scoped-functions": "^7.24.7",
"@babel/plugin-transform-block-scoping": "^7.25.0",
- "@babel/plugin-transform-class-properties": "^7.24.7",
+ "@babel/plugin-transform-class-properties": "^7.25.4",
"@babel/plugin-transform-class-static-block": "^7.24.7",
- "@babel/plugin-transform-classes": "^7.25.0",
+ "@babel/plugin-transform-classes": "^7.25.4",
"@babel/plugin-transform-computed-properties": "^7.24.7",
"@babel/plugin-transform-destructuring": "^7.24.8",
"@babel/plugin-transform-dotall-regex": "^7.24.7",
@@ -1760,7 +1758,7 @@
"@babel/plugin-transform-optional-catch-binding": "^7.24.7",
"@babel/plugin-transform-optional-chaining": "^7.24.8",
"@babel/plugin-transform-parameters": "^7.24.7",
- "@babel/plugin-transform-private-methods": "^7.24.7",
+ "@babel/plugin-transform-private-methods": "^7.25.4",
"@babel/plugin-transform-private-property-in-object": "^7.24.7",
"@babel/plugin-transform-property-literals": "^7.24.7",
"@babel/plugin-transform-regenerator": "^7.24.7",
@@ -1773,10 +1771,10 @@
"@babel/plugin-transform-unicode-escapes": "^7.24.7",
"@babel/plugin-transform-unicode-property-regex": "^7.24.7",
"@babel/plugin-transform-unicode-regex": "^7.24.7",
- "@babel/plugin-transform-unicode-sets-regex": "^7.24.7",
+ "@babel/plugin-transform-unicode-sets-regex": "^7.25.4",
"@babel/preset-modules": "0.1.6-no-external-plugins",
"babel-plugin-polyfill-corejs2": "^0.4.10",
- "babel-plugin-polyfill-corejs3": "^0.10.4",
+ "babel-plugin-polyfill-corejs3": "^0.10.6",
"babel-plugin-polyfill-regenerator": "^0.6.1",
"core-js-compat": "^3.37.1",
"semver": "^6.3.1"
@@ -1855,16 +1853,15 @@
}
},
"node_modules/@babel/traverse": {
- "version": "7.25.3",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz",
- "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz",
+ "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==",
"dependencies": {
"@babel/code-frame": "^7.24.7",
- "@babel/generator": "^7.25.0",
- "@babel/parser": "^7.25.3",
+ "@babel/generator": "^7.25.4",
+ "@babel/parser": "^7.25.4",
"@babel/template": "^7.25.0",
- "@babel/types": "^7.25.2",
+ "@babel/types": "^7.25.4",
"debug": "^4.3.1",
"globals": "^11.1.0"
},
@@ -1872,11 +1869,19 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/types": {
- "version": "7.25.2",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz",
- "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==",
+ "node_modules/@babel/traverse/node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz",
+ "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==",
"dependencies": {
"@babel/helper-string-parser": "^7.24.8",
"@babel/helper-validator-identifier": "^7.24.7",
@@ -1894,9 +1899,9 @@
"license": "MIT"
},
"node_modules/@csstools/css-parser-algorithms": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.7.1.tgz",
- "integrity": "sha512-2SJS42gxmACHgikc1WGesXLIT8d/q2l0UFM7TaEeIzdFCE/FPMtTiizcPGGJtlPo2xuQzY09OhrLTzRxqJqwGw==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.1.tgz",
+ "integrity": "sha512-lSquqZCHxDfuTg/Sk2hiS0mcSFCEBuj49JfzPHJogDBT0mGCyY5A1AQzBWngitrp7i1/HAZpIgzF/VjhOEIJIg==",
"dev": true,
"funding": [
{
@@ -1908,18 +1913,17 @@
"url": "https://opencollective.com/csstools"
}
],
- "license": "MIT",
"engines": {
- "node": "^14 || ^16 || >=18"
+ "node": ">=18"
},
"peerDependencies": {
- "@csstools/css-tokenizer": "^2.4.1"
+ "@csstools/css-tokenizer": "^3.0.1"
}
},
"node_modules/@csstools/css-tokenizer": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.4.1.tgz",
- "integrity": "sha512-eQ9DIktFJBhGjioABJRtUucoWR2mwllurfnM8LuNGAqX3ViZXaUchqk+1s7jjtkFiT9ySdACsFEA3etErkALUg==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.1.tgz",
+ "integrity": "sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==",
"dev": true,
"funding": [
{
@@ -1931,15 +1935,14 @@
"url": "https://opencollective.com/csstools"
}
],
- "license": "MIT",
"engines": {
- "node": "^14 || ^16 || >=18"
+ "node": ">=18"
}
},
"node_modules/@csstools/media-query-list-parser": {
- "version": "2.1.13",
- "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.13.tgz",
- "integrity": "sha512-XaHr+16KRU9Gf8XLi3q8kDlI18d5vzKSKCY510Vrtc9iNR0NJzbY9hhTmwhzYZj/ZwGL4VmB3TA9hJW0Um2qFA==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz",
+ "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==",
"dev": true,
"funding": [
{
@@ -1951,19 +1954,18 @@
"url": "https://opencollective.com/csstools"
}
],
- "license": "MIT",
"engines": {
- "node": "^14 || ^16 || >=18"
+ "node": ">=18"
},
"peerDependencies": {
- "@csstools/css-parser-algorithms": "^2.7.1",
- "@csstools/css-tokenizer": "^2.4.1"
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1"
}
},
"node_modules/@csstools/selector-specificity": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.1.1.tgz",
- "integrity": "sha512-a7cxGcJ2wIlMFLlh8z2ONm+715QkPHiyJcxwQlKOz/03GPw1COpfhcmC9wm4xlZfp//jWHNNMwzjtqHXVWU9KA==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-4.0.0.tgz",
+ "integrity": "sha512-189nelqtPd8++phaHNwYovKZI0FOzH1vQEE3QhHHkNIGrg5fSs9CbYP3RvfEH5geztnIA9Jwq91wyOIwAW5JIQ==",
"dev": true,
"funding": [
{
@@ -1975,12 +1977,11 @@
"url": "https://opencollective.com/csstools"
}
],
- "license": "MIT-0",
"engines": {
- "node": "^14 || ^16 || >=18"
+ "node": ">=18"
},
"peerDependencies": {
- "postcss-selector-parser": "^6.0.13"
+ "postcss-selector-parser": "^6.1.0"
}
},
"node_modules/@dual-bundle/import-meta-resolve": {
@@ -2020,17 +2021,31 @@
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
+ "node_modules/@eslint/config-array": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz",
+ "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==",
+ "dev": true,
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.4",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
"node_modules/@eslint/eslintrc": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
- "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
+ "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
- "espree": "^9.6.0",
- "globals": "^13.19.0",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.0",
@@ -2038,56 +2053,47 @@
"strip-json-comments": "^3.1.1"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@eslint/eslintrc/node_modules/globals": {
- "version": "13.24.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
- "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "type-fest": "^0.20.2"
- },
"engines": {
- "node": ">=8"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@eslint/eslintrc/node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "node_modules/@eslint/js": {
+ "version": "9.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz",
+ "integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==",
"dev": true,
- "license": "(MIT OR CC0-1.0)",
"engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "node_modules/@eslint/js": {
- "version": "8.57.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
- "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz",
+ "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==",
"dev": true,
- "license": "MIT",
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@googleapis/drive": {
- "version": "8.11.0",
- "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.11.0.tgz",
- "integrity": "sha512-HW6/2oThc4X086mGkZxpdP4P+aHpYbjHa6wr9l1F/R+snpk6G8/EuRXEcTkgQUl2t/NdNz3lj8re0AQBG5faSA==",
- "license": "Apache-2.0",
+ "version": "8.14.0",
+ "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.14.0.tgz",
+ "integrity": "sha512-AOokfpP6pCdcJXWA8khaCEgbGpWYavWTdAAhL4idbbf2VCQcJ2f7vPalAYNu6a4Sfj0Ly4Ehnd1xw9J9TixB1A==",
"dependencies": {
"googleapis-common": "^7.0.0"
},
@@ -2095,22 +2101,6 @@
"node": ">=12.0.0"
}
},
- "node_modules/@humanwhocodes/config-array": {
- "version": "0.11.14",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
- "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
- "deprecated": "Use @eslint/config-array instead",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@humanwhocodes/object-schema": "^2.0.2",
- "debug": "^4.3.1",
- "minimatch": "^3.0.5"
- },
- "engines": {
- "node": ">=10.10.0"
- }
- },
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -2125,13 +2115,19 @@
"url": "https://github.com/sponsors/nzakas"
}
},
- "node_modules/@humanwhocodes/object-schema": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
- "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
- "deprecated": "Use @eslint/object-schema instead",
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz",
+ "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==",
"dev": true,
- "license": "BSD-3-Clause"
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
},
"node_modules/@istanbuljs/load-nyc-config": {
"version": "1.1.0",
@@ -2974,7 +2970,6 @@
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz",
"integrity": "sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==",
- "license": "MIT",
"dependencies": {
"sparse-bitfield": "^3.0.3"
}
@@ -3018,9 +3013,9 @@
}
},
"node_modules/@remix-run/router": {
- "version": "1.19.0",
- "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.0.tgz",
- "integrity": "sha512-zDICCLKEwbVYTS6TjYaWtHXxkdoUvD/QXvyVZjGCsWz5vyH7aFeONlPffPdW+Y/t6KT0MgXb2Mfjun9YpWN1dA==",
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz",
+ "integrity": "sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==",
"engines": {
"node": ">=14.0.0"
}
@@ -3053,20 +3048,19 @@
}
},
"node_modules/@stylistic/stylelint-plugin": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.0.0.tgz",
- "integrity": "sha512-GymY+9CSqkPaZ1A3m3w/tvCdpP3qQcaL1FSaoVv9aKL3Tn6GVJWHc2VWVkbNEsYr4QImHjWnlmVZROwgUEjMmQ==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.0.1.tgz",
+ "integrity": "sha512-j3mH8HSw2Rob/KJFWZ627w3CQ8gQqVHtzCdPeEffUg5vOgpz4rgrR+Xw2kU0OQCDcdW8Y1nKfdXKKjM5Rn8X0g==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@csstools/css-parser-algorithms": "^2.7.1",
- "@csstools/css-tokenizer": "^2.4.1",
- "@csstools/media-query-list-parser": "^2.1.13",
+ "@csstools/css-parser-algorithms": "^3.0.0",
+ "@csstools/css-tokenizer": "^3.0.0",
+ "@csstools/media-query-list-parser": "^3.0.0",
"is-plain-object": "^5.0.0",
- "postcss-selector-parser": "^6.1.1",
+ "postcss-selector-parser": "^6.1.2",
"postcss-value-parser": "^4.2.0",
"style-search": "^0.1.0",
- "stylelint": "^16.8.0"
+ "stylelint": "^16.8.2"
},
"engines": {
"node": "^18.12 || >=20.9"
@@ -3177,14 +3171,12 @@
"node_modules/@types/webidl-conversions": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
- "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==",
- "license": "MIT"
+ "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="
},
"node_modules/@types/whatwg-url": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz",
"integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==",
- "license": "MIT",
"dependencies": {
"@types/webidl-conversions": "*"
}
@@ -3207,17 +3199,17 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz",
- "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz",
+ "integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "7.18.0",
- "@typescript-eslint/visitor-keys": "7.18.0"
+ "@typescript-eslint/types": "8.2.0",
+ "@typescript-eslint/visitor-keys": "8.2.0"
},
"engines": {
- "node": "^18.18.0 || >=20.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
@@ -3225,13 +3217,13 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
- "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz",
+ "integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==",
"dev": true,
"license": "MIT",
"engines": {
- "node": "^18.18.0 || >=20.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
@@ -3239,14 +3231,14 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz",
- "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz",
+ "integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
- "@typescript-eslint/types": "7.18.0",
- "@typescript-eslint/visitor-keys": "7.18.0",
+ "@typescript-eslint/types": "8.2.0",
+ "@typescript-eslint/visitor-keys": "8.2.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -3255,7 +3247,7 @@
"ts-api-utils": "^1.3.0"
},
"engines": {
- "node": "^18.18.0 || >=20.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
@@ -3307,53 +3299,46 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz",
- "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz",
+ "integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "7.18.0",
- "@typescript-eslint/types": "7.18.0",
- "@typescript-eslint/typescript-estree": "7.18.0"
+ "@typescript-eslint/scope-manager": "8.2.0",
+ "@typescript-eslint/types": "8.2.0",
+ "@typescript-eslint/typescript-estree": "8.2.0"
},
"engines": {
- "node": "^18.18.0 || >=20.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.56.0"
+ "eslint": "^8.57.0 || ^9.0.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz",
- "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz",
+ "integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "7.18.0",
+ "@typescript-eslint/types": "8.2.0",
"eslint-visitor-keys": "^3.4.3"
},
"engines": {
- "node": "^18.18.0 || >=20.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
- "node_modules/@ungap/structured-clone": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
- "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@@ -3972,13 +3957,12 @@
}
},
"node_modules/babel-plugin-polyfill-corejs3": {
- "version": "0.10.4",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz",
- "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==",
- "license": "MIT",
+ "version": "0.10.6",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz",
+ "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==",
"dependencies": {
- "@babel/helper-define-polyfill-provider": "^0.6.1",
- "core-js-compat": "^3.36.1"
+ "@babel/helper-define-polyfill-provider": "^0.6.2",
+ "core-js-compat": "^3.38.0"
},
"peerDependencies": {
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
@@ -4447,7 +4431,6 @@
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz",
"integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==",
- "license": "Apache-2.0",
"engines": {
"node": ">=16.20.1"
}
@@ -4931,12 +4914,11 @@
}
},
"node_modules/core-js-compat": {
- "version": "3.37.1",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz",
- "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==",
- "license": "MIT",
+ "version": "3.38.1",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz",
+ "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==",
"dependencies": {
- "browserslist": "^4.23.0"
+ "browserslist": "^4.23.3"
},
"funding": {
"type": "opencollective",
@@ -5521,19 +5503,6 @@
"node": ">=8"
}
},
- "node_modules/doctrine": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
- "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "esutils": "^2.0.2"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/domain-browser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
@@ -5581,10 +5550,9 @@
"license": "ISC"
},
"node_modules/elliptic": {
- "version": "6.5.6",
- "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.6.tgz",
- "integrity": "sha512-mpzdtpeCLuS3BmE3pO3Cpp5bbjlOPY2Q0PgoF+Od1XZrHLYI28Xe3ossCmYCQt11FQKEYd9+PF8jymTvtWJSHQ==",
- "license": "MIT",
+ "version": "6.5.7",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz",
+ "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==",
"dependencies": {
"bn.js": "^4.11.9",
"brorand": "^1.1.0",
@@ -5851,42 +5819,37 @@
}
},
"node_modules/eslint": {
- "version": "8.57.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
- "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+ "version": "9.9.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.1.tgz",
+ "integrity": "sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.6.1",
- "@eslint/eslintrc": "^2.1.4",
- "@eslint/js": "8.57.0",
- "@humanwhocodes/config-array": "^0.11.14",
+ "@eslint-community/regexpp": "^4.11.0",
+ "@eslint/config-array": "^0.18.0",
+ "@eslint/eslintrc": "^3.1.0",
+ "@eslint/js": "9.9.1",
"@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.3.0",
"@nodelib/fs.walk": "^1.2.8",
- "@ungap/structured-clone": "^1.2.0",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
- "doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.2.2",
- "eslint-visitor-keys": "^3.4.3",
- "espree": "^9.6.1",
- "esquery": "^1.4.2",
+ "eslint-scope": "^8.0.2",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.1.0",
+ "esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^6.0.1",
+ "file-entry-cache": "^8.0.0",
"find-up": "^5.0.0",
"glob-parent": "^6.0.2",
- "globals": "^13.19.0",
- "graphemer": "^1.4.0",
"ignore": "^5.2.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"is-path-inside": "^3.0.3",
- "js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash.merge": "^4.6.2",
@@ -5900,16 +5863,24 @@
"eslint": "bin/eslint.js"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
- "url": "https://opencollective.com/eslint"
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
}
},
"node_modules/eslint-plugin-jest": {
- "version": "28.8.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.8.0.tgz",
- "integrity": "sha512-Tubj1hooFxCl52G4qQu0edzV/+EZzPUeN8p2NnW5uu4fbDs+Yo7+qDVDc4/oG3FbCqEBmu/OC3LSsyiU22oghw==",
+ "version": "28.8.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.8.3.tgz",
+ "integrity": "sha512-HIQ3t9hASLKm2IhIOqnu+ifw7uLZkIlR7RYNv7fMcEi/p0CIiJmfriStQS2LDkgtY4nyLbIZAD+JL347Yc2ETQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0"
@@ -5932,11 +5903,10 @@
}
},
"node_modules/eslint-plugin-react": {
- "version": "7.35.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz",
- "integrity": "sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==",
+ "version": "7.35.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.2.tgz",
+ "integrity": "sha512-Rbj2R9zwP2GYNcIak4xoAMV57hrBh3hTaR0k7hVjwCQgryE/pw5px4b13EYjduOI0hfXyZhwBxaGpOTbWSGzKQ==",
"dev": true,
- "license": "MIT",
"dependencies": {
"array-includes": "^3.1.8",
"array.prototype.findlast": "^1.2.5",
@@ -5996,9 +5966,9 @@
}
},
"node_modules/eslint-scope": {
- "version": "7.2.2",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
- "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz",
+ "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -6006,7 +5976,7 @@
"estraverse": "^5.2.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -6091,20 +6061,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/eslint/node_modules/globals": {
- "version": "13.24.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
- "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "node_modules/eslint/node_modules/eslint-visitor-keys": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+ "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "type-fest": "^0.20.2"
- },
+ "license": "Apache-2.0",
"engines": {
- "node": ">=8"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/has-flag": {
@@ -6130,32 +6097,32 @@
"node": ">=8"
}
},
- "node_modules/eslint/node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "node_modules/espree": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz",
+ "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==",
"dev": true,
- "license": "(MIT OR CC0-1.0)",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.12.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.0.0"
+ },
"engines": {
- "node": ">=10"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/espree": {
- "version": "9.6.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
- "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+ "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
"dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "acorn": "^8.9.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.4.1"
- },
+ "license": "Apache-2.0",
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -6575,16 +6542,16 @@
}
},
"node_modules/file-entry-cache": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
- "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "flat-cache": "^3.0.4"
+ "flat-cache": "^4.0.0"
},
"engines": {
- "node": "^10.12.0 || >=12.0.0"
+ "node": ">=16.0.0"
}
},
"node_modules/file-uri-to-path": {
@@ -6657,18 +6624,17 @@
}
},
"node_modules/flat-cache": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
- "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
"dev": true,
"license": "MIT",
"dependencies": {
"flatted": "^3.2.9",
- "keyv": "^4.5.3",
- "rimraf": "^3.0.2"
+ "keyv": "^4.5.4"
},
"engines": {
- "node": "^10.12.0 || >=12.0.0"
+ "node": ">=16"
}
},
"node_modules/flatted": {
@@ -7051,12 +7017,16 @@
}
},
"node_modules/globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "version": "15.9.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz",
+ "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==",
+ "dev": true,
"license": "MIT",
"engines": {
- "node": ">=4"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/globalthis": {
@@ -7156,13 +7126,6 @@
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"license": "ISC"
},
- "node_modules/graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/gtoken": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
@@ -7481,11 +7444,10 @@
"license": "BSD-3-Clause"
},
"node_modules/ignore": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
- "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">= 4"
}
@@ -10463,12 +10425,11 @@
}
},
"node_modules/marked-extended-tables": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/marked-extended-tables/-/marked-extended-tables-1.0.8.tgz",
- "integrity": "sha512-GcVQP7EnfQ98o09ooqM4t4M0qfpKdKuk7/z4qZfgkLyXTXsIyFS1eeBmfC36o1NbR6aSq8ynL/LeTz3w4RS27Q==",
- "license": "MIT",
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/marked-extended-tables/-/marked-extended-tables-1.0.10.tgz",
+ "integrity": "sha512-zvRS0GPTkxq8UWawSDecd1Rxd2KD8crrmq2QALGDdrgkcgRNQzHlbnlujBGuXxdgDJg7f6UTv+JpcfejBpKdSg==",
"peerDependencies": {
- "marked": ">=3 <12"
+ "marked": ">=3 <15"
}
},
"node_modules/marked-gfm-heading-id": {
@@ -10546,8 +10507,7 @@
"node_modules/memory-pager": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
- "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
- "license": "MIT"
+ "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="
},
"node_modules/meow": {
"version": "13.2.0",
@@ -10595,11 +10555,10 @@
}
},
"node_modules/micromatch": {
- "version": "4.0.7",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
- "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
- "license": "MIT",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
@@ -10798,7 +10757,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz",
"integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==",
- "license": "Apache-2.0",
"dependencies": {
"@types/whatwg-url": "^11.0.2",
"whatwg-url": "^13.0.0"
@@ -10808,7 +10766,6 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
"integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
- "license": "MIT",
"dependencies": {
"punycode": "^2.3.0"
},
@@ -10820,7 +10777,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
- "license": "BSD-2-Clause",
"engines": {
"node": ">=12"
}
@@ -10829,7 +10785,6 @@
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz",
"integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==",
- "license": "MIT",
"dependencies": {
"tr46": "^4.1.1",
"webidl-conversions": "^7.0.0"
@@ -10839,14 +10794,13 @@
}
},
"node_modules/mongoose": {
- "version": "8.5.2",
- "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.5.2.tgz",
- "integrity": "sha512-GZB4rHMdYfGatV+23IpCrqFbyCOjCNOHXgWbirr92KRwTEncBrtW3kgU9vmpKjsGf7nMmnAy06SwWUv1vhDkSg==",
- "license": "MIT",
+ "version": "8.6.1",
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.6.1.tgz",
+ "integrity": "sha512-dppGcYqvsdg+VcnqXR5b467V4a+iNhmvkfYNpEPi6AjaUxnz6ioEDmrMLOi+sOWjvoHapuwPOigV4f2l7HC6ag==",
"dependencies": {
"bson": "^6.7.0",
"kareem": "2.6.3",
- "mongodb": "6.7.0",
+ "mongodb": "6.8.0",
"mpath": "0.9.0",
"mquery": "5.0.0",
"ms": "2.1.3",
@@ -10864,7 +10818,6 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
- "license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
@@ -10878,7 +10831,6 @@
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz",
"integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==",
- "license": "Apache-2.0",
"optional": true,
"peer": true,
"dependencies": {
@@ -10895,7 +10847,6 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz",
"integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==",
- "license": "Apache-2.0",
"optional": true,
"peer": true,
"dependencies": {
@@ -10910,7 +10861,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
- "license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
@@ -10922,10 +10872,9 @@
}
},
"node_modules/mongoose/node_modules/mongodb": {
- "version": "6.7.0",
- "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.7.0.tgz",
- "integrity": "sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==",
- "license": "Apache-2.0",
+ "version": "6.8.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.8.0.tgz",
+ "integrity": "sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==",
"dependencies": {
"@mongodb-js/saslprep": "^1.1.5",
"bson": "^6.7.0",
@@ -11846,9 +11795,9 @@
}
},
"node_modules/postcss": {
- "version": "8.4.40",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz",
- "integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==",
+ "version": "8.4.41",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz",
+ "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==",
"dev": true,
"funding": [
{
@@ -11864,7 +11813,6 @@
"url": "https://github.com/sponsors/ai"
}
],
- "license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.1",
@@ -11888,11 +11836,10 @@
}
},
"node_modules/postcss-resolve-nested-selector": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.4.tgz",
- "integrity": "sha512-R6vHqZWgVnTAPq0C+xjyHfEZqfIYboCBVSy24MjxEDm+tIh1BU4O6o7DP7AA7kHzf136d+Qc5duI4tlpHjixDw==",
- "dev": true,
- "license": "MIT"
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz",
+ "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==",
+ "dev": true
},
"node_modules/postcss-safe-parser": {
"version": "7.0.0",
@@ -11922,11 +11869,10 @@
}
},
"node_modules/postcss-selector-parser": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz",
- "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==",
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"dev": true,
- "license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -12258,11 +12204,11 @@
"license": "MIT"
},
"node_modules/react-router": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.0.tgz",
- "integrity": "sha512-wVQq0/iFYd3iZ9H2l3N3k4PL8EEHcb0XlU2Na8nEwmiXgIUElEH6gaJDtUQxJ+JFzmIXaQjfdpcGWaM6IoQGxg==",
+ "version": "6.26.1",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz",
+ "integrity": "sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==",
"dependencies": {
- "@remix-run/router": "1.19.0"
+ "@remix-run/router": "1.19.1"
},
"engines": {
"node": ">=14.0.0"
@@ -12272,12 +12218,12 @@
}
},
"node_modules/react-router-dom": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.0.tgz",
- "integrity": "sha512-RRGUIiDtLrkX3uYcFiCIxKFWMcWQGMojpYZfcstc63A1+sSnVgILGIm9gNUA6na3Fm1QuPGSBQH2EMbAZOnMsQ==",
+ "version": "6.26.1",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.1.tgz",
+ "integrity": "sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==",
"dependencies": {
- "@remix-run/router": "1.19.0",
- "react-router": "6.26.0"
+ "@remix-run/router": "1.19.1",
+ "react-router": "6.26.1"
},
"engines": {
"node": ">=14.0.0"
@@ -12635,23 +12581,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "deprecated": "Rimraf versions prior to v4 are no longer supported",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "glob": "^7.1.3"
- },
- "bin": {
- "rimraf": "bin.js"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/ripemd160": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
@@ -13285,7 +13214,6 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
"integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
- "license": "MIT",
"dependencies": {
"memory-pager": "^1.0.2"
}
@@ -13627,9 +13555,9 @@
"license": "ISC"
},
"node_modules/stylelint": {
- "version": "16.8.1",
- "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.8.1.tgz",
- "integrity": "sha512-O8aDyfdODSDNz/B3gW2HQ+8kv8pfhSu7ZR7xskQ93+vI6FhKKGUJMQ03Ydu+w3OvXXE0/u4hWU4hCPNOyld+OA==",
+ "version": "16.9.0",
+ "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.9.0.tgz",
+ "integrity": "sha512-31Nm3WjxGOBGpQqF43o3wO9L5AC36TPIe6030Lnm13H3vDMTcS21DrLh69bMX+DBilKqMMVLian4iG6ybBoNRQ==",
"dev": true,
"funding": [
{
@@ -13641,12 +13569,11 @@
"url": "https://github.com/sponsors/stylelint"
}
],
- "license": "MIT",
"dependencies": {
- "@csstools/css-parser-algorithms": "^2.7.1",
- "@csstools/css-tokenizer": "^2.4.1",
- "@csstools/media-query-list-parser": "^2.1.13",
- "@csstools/selector-specificity": "^3.1.1",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
+ "@csstools/media-query-list-parser": "^3.0.1",
+ "@csstools/selector-specificity": "^4.0.0",
"@dual-bundle/import-meta-resolve": "^4.1.0",
"balanced-match": "^2.0.0",
"colord": "^2.9.3",
@@ -13661,24 +13588,24 @@
"globby": "^11.1.0",
"globjoin": "^0.1.4",
"html-tags": "^3.3.1",
- "ignore": "^5.3.1",
+ "ignore": "^5.3.2",
"imurmurhash": "^0.1.4",
"is-plain-object": "^5.0.0",
"known-css-properties": "^0.34.0",
"mathml-tag-names": "^2.1.3",
"meow": "^13.2.0",
- "micromatch": "^4.0.7",
+ "micromatch": "^4.0.8",
"normalize-path": "^3.0.0",
"picocolors": "^1.0.1",
- "postcss": "^8.4.40",
- "postcss-resolve-nested-selector": "^0.1.4",
+ "postcss": "^8.4.41",
+ "postcss-resolve-nested-selector": "^0.1.6",
"postcss-safe-parser": "^7.0.0",
- "postcss-selector-parser": "^6.1.1",
+ "postcss-selector-parser": "^6.1.2",
"postcss-value-parser": "^4.2.0",
"resolve-from": "^5.0.0",
"string-width": "^4.2.3",
"strip-ansi": "^7.1.0",
- "supports-hyperlinks": "^3.0.0",
+ "supports-hyperlinks": "^3.1.0",
"svg-tags": "^1.0.0",
"table": "^6.8.2",
"write-file-atomic": "^5.0.1"
@@ -13691,11 +13618,10 @@
}
},
"node_modules/stylelint-config-recess-order": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/stylelint-config-recess-order/-/stylelint-config-recess-order-5.0.1.tgz",
- "integrity": "sha512-rKbGkoa3h0rINrGln9TFVowvSCLgPJC5O0EuPiqlqWcJMb1lImEtXktcjFCVz+hwtSUiHD3ijJc3vP9muFOgJg==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/stylelint-config-recess-order/-/stylelint-config-recess-order-5.1.0.tgz",
+ "integrity": "sha512-ddapCF6B/kEtQYIFhQFReQ0dvK1ZdgJDM/SGFtIyeooYDbqaJqcOlGkRRGaVErCQYJY/bPSPsLRS2LdQtLJUVQ==",
"dev": true,
- "license": "ISC",
"dependencies": {
"stylelint-order": "^6.0.4"
},
@@ -13850,10 +13776,9 @@
}
},
"node_modules/superagent": {
- "version": "9.0.2",
- "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz",
- "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==",
- "license": "MIT",
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.1.0.tgz",
+ "integrity": "sha512-JMmik7PbnXGlq7g528Gi6apHbVbTz2vrE3du6fuG4kIPSb2PnLoSOPvfjKn8aQYuJcBWAKW6ZG90qPPsE5jZxQ==",
"dependencies": {
"component-emitter": "^1.3.0",
"cookiejar": "^2.1.4",
@@ -13895,6 +13820,38 @@
"node": ">=14.18.0"
}
},
+ "node_modules/supertest/node_modules/mime": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+ "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+ "dev": true,
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/supertest/node_modules/superagent": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz",
+ "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==",
+ "dev": true,
+ "dependencies": {
+ "component-emitter": "^1.3.0",
+ "cookiejar": "^2.1.4",
+ "debug": "^4.3.4",
+ "fast-safe-stringify": "^2.1.1",
+ "form-data": "^4.0.0",
+ "formidable": "^3.5.1",
+ "methods": "^1.1.2",
+ "mime": "2.6.0",
+ "qs": "^6.11.0"
+ },
+ "engines": {
+ "node": ">=14.18.0"
+ }
+ },
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -13908,17 +13865,19 @@
}
},
"node_modules/supports-hyperlinks": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz",
- "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz",
+ "integrity": "sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==",
"dev": true,
- "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0",
"supports-color": "^7.0.0"
},
"engines": {
"node": ">=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/supports-hyperlinks/node_modules/has-flag": {
@@ -13926,7 +13885,6 @@
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">=8"
}
@@ -13936,7 +13894,6 @@
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
- "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
diff --git a/package.json b/package.json
index 2dbff24ed8..8248f4304d 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "homebrewery",
"description": "Create authentic looking D&D homebrews using only markdown",
- "version": "3.14.0",
+ "version": "3.15.0",
"engines": {
"npm": "^10.2.x",
"node": "^20.8.x"
@@ -15,8 +15,8 @@
"quick": "node scripts/quick.js",
"build": "node scripts/buildHomebrew.js && node scripts/buildAdmin.js",
"builddev": "node scripts/buildHomebrew.js --dev",
- "lint": "eslint --fix **/*.{js,jsx}",
- "lint:dry": "eslint **/*.{js,jsx}",
+ "lint": "eslint --fix",
+ "lint:dry": "eslint",
"stylelint": "stylelint --fix **/*.{less}",
"stylelint:dry": "stylelint **/*.less",
"circleci": "npm test && eslint **/*.{js,jsx} --max-warnings=0",
@@ -24,6 +24,7 @@
"test": "jest --runInBand",
"test:api-unit": "jest \"server/.*.spec.js\" --verbose",
"test:api-unit:themes": "jest \"server/.*.spec.js\" -t \"theme bundle\" --verbose",
+ "test:api-unit:css": "jest \"server/.*.spec.js\" -t \"Get CSS\" --verbose",
"test:coverage": "jest --coverage --silent --runInBand",
"test:dev": "jest --verbose --watch",
"test:basic": "jest tests/markdown/basic.test.js --verbose",
@@ -33,6 +34,7 @@
"test:mustache-syntax:block": "jest \".*(mustache-syntax).*\" -t '^Block:.*' --verbose --noStackTrace",
"test:mustache-syntax:injection": "jest \".*(mustache-syntax).*\" -t '^Injection:.*' --verbose --noStackTrace",
"test:definition-lists": "jest tests/markdown/definition-lists.test.js --verbose --noStackTrace",
+ "test:hard-breaks": "jest tests/markdown/hard-breaks.test.js --verbose --noStackTrace",
"test:emojis": "jest tests/markdown/emojis.test.js --verbose --noStackTrace",
"test:route": "jest tests/routes/static-pages.test.js --verbose",
"phb": "node scripts/phb.js",
@@ -84,10 +86,10 @@
},
"dependencies": {
"@babel/core": "^7.25.2",
- "@babel/plugin-transform-runtime": "^7.24.7",
- "@babel/preset-env": "^7.25.3",
+ "@babel/plugin-transform-runtime": "^7.25.4",
+ "@babel/preset-env": "^7.25.4",
"@babel/preset-react": "^7.24.7",
- "@googleapis/drive": "^8.11.0",
+ "@googleapis/drive": "^8.14.0",
"body-parser": "^1.20.2",
"classnames": "^2.5.1",
"codemirror": "^5.65.6",
@@ -106,32 +108,33 @@
"lodash": "^4.17.21",
"marked": "11.2.0",
"marked-emoji": "^1.4.2",
- "marked-extended-tables": "^1.0.8",
+ "marked-extended-tables": "^1.0.10",
"marked-gfm-heading-id": "^3.2.0",
"marked-smartypants-lite": "^1.0.2",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1",
- "mongoose": "^8.5.2",
+ "mongoose": "^8.6.1",
"nanoid": "3.3.4",
"nconf": "^0.12.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-frame-component": "^4.1.3",
- "react-router-dom": "6.26.0",
+ "react-router-dom": "6.26.1",
"sanitize-filename": "1.6.3",
- "superagent": "^9.0.2",
+ "superagent": "^10.1.0",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
},
"devDependencies": {
- "@stylistic/stylelint-plugin": "^3.0.0",
- "eslint": "^8.57.0",
- "eslint-plugin-jest": "^28.8.0",
- "eslint-plugin-react": "^7.35.0",
+ "@stylistic/stylelint-plugin": "^3.0.1",
+ "eslint": "^9.9.1",
+ "eslint-plugin-jest": "^28.8.3",
+ "eslint-plugin-react": "^7.35.2",
+ "globals": "^15.9.0",
"jest": "^29.7.0",
"jest-expect-message": "^1.1.3",
"postcss-less": "^6.0.0",
- "stylelint": "^16.8.0",
- "stylelint-config-recess-order": "^5.0.1",
+ "stylelint": "^16.9.0",
+ "stylelint-config-recess-order": "^5.1.0",
"stylelint-config-recommended": "^14.0.1",
"supertest": "^7.0.0"
}
diff --git a/server/app.js b/server/app.js
index b419c5cead..02075d31d0 100644
--- a/server/app.js
+++ b/server/app.js
@@ -9,11 +9,12 @@ const yaml = require('js-yaml');
const app = express();
const config = require('./config.js');
-const { homebrewApi, getBrew, getUsersBrewThemes } = require('./homebrew.api.js');
+const { homebrewApi, getBrew, getUsersBrewThemes, getCSS } = require('./homebrew.api.js');
const GoogleActions = require('./googleActions.js');
const serveCompressedStaticAssets = require('./static-assets.mv.js');
const sanitizeFilename = require('sanitize-filename');
const asyncHandler = require('express-async-handler');
+const templateFn = require('./../client/template.js');
const { DEFAULT_BREW } = require('./brewDefaults.js');
@@ -54,6 +55,7 @@ app.use((req, res, next)=>{
app.use(homebrewApi);
app.use(require('./admin.api.js'));
+app.use(require('./vault.api.js'));
const HomebrewModel = require('./homebrew.model.js').model;
const welcomeText = require('fs').readFileSync('client/homebrew/pages/homePage/welcome_msg.md', 'utf8');
@@ -200,6 +202,9 @@ app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{
res.status(200).send(brew.text);
});
+//Serve brew styling
+app.get('/css/:id', asyncHandler(getBrew('share')), (req, res)=>{getCSS(req, res);});
+
//User Page
app.get('/user/:username', async (req, res, next)=>{
const ownAccount = req.account && (req.account.username == req.params.username);
@@ -356,6 +361,15 @@ app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, r
app.get('/account', asyncHandler(async (req, res, next)=>{
const data = {};
data.title = 'Account Information Page';
+
+ if(!req.account) {
+ res.set('WWW-Authenticate', 'Bearer realm="Authorization Required"');
+ const error = new Error('No valid account');
+ error.status = 401;
+ error.HBErrorCode = '50';
+ error.page = data.title;
+ return next(error);
+ };
let auth;
let googleCount = [];
@@ -420,8 +434,21 @@ if(isLocalEnvironment){
});
}
+//Vault Page
+app.get('/vault', asyncHandler(async(req, res, next)=>{
+ return next();
+}));
+
+//Send rendered page
+app.use(asyncHandler(async (req, res, next)=>{
+ if (!req.route) return res.redirect('/'); // Catch-all for invalid routes
+
+ const page = await renderPage(req, res);
+ if(!page) return;
+ res.send(page);
+}));
+
//Render the page
-const templateFn = require('./../client/template.js');
const renderPage = async (req, res)=>{
// Create configuration object
const configuration = {
@@ -450,13 +477,6 @@ const renderPage = async (req, res)=>{
return page;
};
-//Send rendered page
-app.use(asyncHandler(async (req, res, next)=>{
- const page = await renderPage(req, res);
- if(!page) return;
- res.send(page);
-}));
-
//v=====----- Error-Handling Middleware -----=====v//
//Format Errors as plain objects so all fields will appear in the string sent
const formatErrors = (key, value)=>{
diff --git a/server/homebrew.api.js b/server/homebrew.api.js
index 521bf096e3..b824596e89 100644
--- a/server/homebrew.api.js
+++ b/server/homebrew.api.js
@@ -99,7 +99,7 @@ const api = {
stub = stub?.toObject();
if(stub?.lock && accessType != 'edit') {
- throw { HBErrorCode: '100', code: stub.lock.code, message: stub.lock.shareMessage, brewId: stub.shareId, brewTitle: stub.title };
+ throw { HBErrorCode: '51', code: stub.lock.code, message: stub.lock.shareMessage, brewId: stub.shareId, brewTitle: stub.title };
}
// If there is a google id, try to find the google brew
@@ -148,6 +148,20 @@ const api = {
next();
};
},
+
+ getCSS : async (req, res)=>{
+ const { brew } = req;
+ if(!brew) return res.status(404).send('');
+ splitTextStyleAndMetadata(brew);
+ if(!brew.style) return res.status(404).send('');
+
+ res.set({
+ 'Cache-Control' : 'no-cache',
+ 'Content-Type' : 'text/css'
+ });
+ return res.status(200).send(brew.style);
+ },
+
mergeBrewText : (brew)=>{
let text = brew.text;
if(brew.style !== undefined) {
diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js
index 5f1739b972..6e7c36641f 100644
--- a/server/homebrew.api.spec.js
+++ b/server/homebrew.api.spec.js
@@ -50,6 +50,7 @@ describe('Tests for api', ()=>{
res = {
status : jest.fn(()=>res),
send : jest.fn(()=>{}),
+ set : jest.fn(()=>{}),
setHeader : jest.fn(()=>{})
};
@@ -308,7 +309,7 @@ describe('Tests for api', ()=>{
const req = { brew: {} };
const next = jest.fn();
- await expect(fn(req, null, next)).rejects.toEqual({ 'HBErrorCode': '100', 'brewId': '1', 'brewTitle': 'test brew', 'code': 404, 'message': 'brew locked' });
+ await expect(fn(req, null, next)).rejects.toEqual({ 'HBErrorCode': '51', 'brewId': '1', 'brewTitle': 'test brew', 'code': 404, 'message': 'brew locked' });
});
});
@@ -916,4 +917,66 @@ brew`);
expect(saved.googleId).toEqual(brew.googleId);
});
});
+ describe('Get CSS', ()=>{
+ it('should return brew style content as CSS text', async ()=>{
+ const testBrew = { title: 'test brew', text: '```css\n\nI Have a style!\n````\n\n' };
+
+ const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew }));
+ api.getId = jest.fn(()=>({ id: '1', googleId: undefined }));
+ model.get = jest.fn(()=>toBrewPromise(testBrew));
+
+ const fn = api.getBrew('share', true);
+ const req = { brew: {} };
+ const next = jest.fn();
+ await fn(req, null, next);
+ await api.getCSS(req, res);
+
+ expect(req.brew).toEqual(testBrew);
+ expect(req.brew).toHaveProperty('style', '\nI Have a style!\n');
+ expect(res.status).toHaveBeenCalledWith(200);
+ expect(res.send).toHaveBeenCalledWith("\nI Have a style!\n");
+ expect(res.set).toHaveBeenCalledWith({
+ 'Cache-Control' : 'no-cache',
+ 'Content-Type' : 'text/css'
+ });
+ });
+
+ it('should return 404 when brew has no style content', async ()=>{
+ const testBrew = { title: 'test brew', text: 'I don\'t have a style!' };
+
+ const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew }));
+ api.getId = jest.fn(()=>({ id: '1', googleId: undefined }));
+ model.get = jest.fn(()=>toBrewPromise(testBrew));
+
+ const fn = api.getBrew('share', true);
+ const req = { brew: {} };
+ const next = jest.fn();
+ await fn(req, null, next);
+ await api.getCSS(req, res);
+
+ expect(req.brew).toEqual(testBrew);
+ expect(req.brew).toHaveProperty('style');
+ expect(res.status).toHaveBeenCalledWith(404);
+ expect(res.send).toHaveBeenCalledWith('');
+ });
+
+ it('should return 404 when brew does not exist', async ()=>{
+ const testBrew = { };
+
+ const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew }));
+ api.getId = jest.fn(()=>({ id: '1', googleId: undefined }));
+ model.get = jest.fn(()=>toBrewPromise(testBrew));
+
+ const fn = api.getBrew('share', true);
+ const req = { brew: {} };
+ const next = jest.fn();
+ await fn(req, null, next);
+ await api.getCSS(req, res);
+
+ expect(req.brew).toEqual(testBrew);
+ expect(req.brew).toHaveProperty('style');
+ expect(res.status).toHaveBeenCalledWith(404);
+ expect(res.send).toHaveBeenCalledWith('');
+ });
+ });
});
diff --git a/server/vault.api.js b/server/vault.api.js
new file mode 100644
index 0000000000..41ceeab8e3
--- /dev/null
+++ b/server/vault.api.js
@@ -0,0 +1,102 @@
+const express = require('express');
+const asyncHandler = require('express-async-handler');
+const HomebrewModel = require('./homebrew.model.js').model;
+
+const router = express.Router();
+
+const titleConditions = (title)=>{
+ if(!title) return {};
+ return {
+ $text : {
+ $search : title,
+ $caseSensitive : false,
+ },
+ };
+};
+
+const authorConditions = (author)=>{
+ if(!author) return {};
+ return { authors: author };
+};
+
+const rendererConditions = (legacy, v3)=>{
+ if(legacy === 'true' && v3 !== 'true')
+ return { renderer: 'legacy' };
+
+ if(v3 === 'true' && legacy !== 'true')
+ return { renderer: 'V3' };
+
+ return {}; // If all renderers selected, renderer field not needed in query for speed
+};
+
+const findBrews = async (req, res)=>{
+ const title = req.query.title || '';
+ const author = req.query.author || '';
+ const page = Math.max(parseInt(req.query.page) || 1, 1);
+ const count = Math.max(parseInt(req.query.count) || 20, 10);
+ const skip = (page - 1) * count;
+
+ const combinedQuery = {
+ $and : [
+ { published: true },
+ rendererConditions(req.query.legacy, req.query.v3),
+ titleConditions(title),
+ authorConditions(author)
+ ],
+ };
+
+ const projection = {
+ editId : 0,
+ googleId : 0,
+ text : 0,
+ textBin : 0,
+ version : 0
+ };
+
+ await HomebrewModel.find(combinedQuery, projection)
+ .skip(skip)
+ .limit(count)
+ .maxTimeMS(5000)
+ .exec()
+ .then((brews)=>{
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+
+ const processedBrews = brews.map((brew)=>{
+ brew.authors = brew.authors.map((author)=>emailRegex.test(author) ? 'hidden' : author
+ );
+ return brew;
+ });
+ res.json({ brews: processedBrews, page });
+ })
+ .catch((error)=>{
+ throw { ...error, message: 'Error finding brews in Vault search', HBErrorCode: 90 };
+ });
+};
+
+const findTotal = async (req, res)=>{
+ const title = req.query.title || '';
+ const author = req.query.author || '';
+
+ const combinedQuery = {
+ $and : [
+ { published: true },
+ rendererConditions(req.query.legacy, req.query.v3),
+ titleConditions(title),
+ authorConditions(author)
+ ],
+ };
+
+ await HomebrewModel.countDocuments(combinedQuery)
+ .then((totalBrews)=>{
+ console.log(`when returning, the total of brews is ${totalBrews} for the query ${JSON.stringify(combinedQuery)}`);
+ res.json({ totalBrews });
+ })
+ .catch((error)=>{
+ throw { ...error, message: 'Error finding brews in Vault search findTotal function', HBErrorCode: 91 };
+ });
+};
+
+router.get('/api/vault/total', asyncHandler(findTotal));
+router.get('/api/vault', asyncHandler(findBrews));
+
+module.exports = router;
diff --git a/shared/helpers.js b/shared/helpers.js
index e5c1b7769b..ac684b06f6 100644
--- a/shared/helpers.js
+++ b/shared/helpers.js
@@ -35,6 +35,7 @@ const printCurrentBrew = ()=>{
};
const fetchThemeBundle = async (obj, renderer, theme)=>{
+ if(!renderer || !theme) return;
const res = await request
.get(`/api/theme/${renderer}/${theme}`)
.catch((err)=>{
diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js
index 9388e912ac..205063641d 100644
--- a/shared/naturalcrit/markdown.js
+++ b/shared/naturalcrit/markdown.js
@@ -3,7 +3,7 @@ const _ = require('lodash');
const Marked = require('marked');
const MarkedExtendedTables = require('marked-extended-tables');
const { markedSmartypantsLite: MarkedSmartypantsLite } = require('marked-smartypants-lite');
-const { gfmHeadingId: MarkedGFMHeadingId } = require('marked-gfm-heading-id');
+const { gfmHeadingId: MarkedGFMHeadingId, resetHeadings: MarkedGFMResetHeadingIDs } = require('marked-gfm-heading-id');
const { markedEmoji: MarkedEmojis } = require('marked-emoji');
//Icon fonts included so they can appear in emoji autosuggest dropdown
@@ -86,7 +86,7 @@ renderer.link = function (href, title, text) {
if(href[0] == '#') {
self = true;
}
- href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
+ href = cleanUrl(href);
if(href === null) {
return text;
@@ -102,6 +102,20 @@ renderer.link = function (href, title, text) {
return out;
};
+// Expose `src` attribute as `--HB_src` to make the URL accessible via CSS
+renderer.image = function (href, title, text) {
+ href = cleanUrl(href);
+ if (href === null)
+ return text;
+
+ let out = ` ';
+ return out;
+}
+
// Disable default reflink behavior, as it steps on our variables extension
tokenizer.def = function () {
return undefined;
@@ -356,6 +370,27 @@ const superSubScripts = {
}
};
+const forcedParagraphBreaks = {
+ name : 'hardBreaks',
+ level : 'block',
+ start(src) { return src.match(/\n:+$/m)?.index; }, // Hint to Marked.js to stop and check for a match
+ tokenizer(src, tokens) {
+ const regex = /^(:+)(?:\n|$)/ym;
+ const match = regex.exec(src);
+ if(match?.length) {
+ return {
+ type : 'hardBreaks', // Should match "name" above
+ raw : match[0], // Text to consume from the source
+ length : match[1].length,
+ text : ''
+ };
+ }
+ },
+ renderer(token) {
+ return `
`.repeat(token.length).concat('\n');
+ }
+};
+
const definitionListsSingleLine = {
name : 'definitionListsSingleLine',
level : 'block',
@@ -400,9 +435,9 @@ const definitionListsSingleLine = {
const definitionListsMultiLine = {
name : 'definitionListsMultiLine',
level : 'block',
- start(src) { return src.match(/\n[^\n]*\n::/m)?.index; }, // Hint to Marked.js to stop and check for a match
+ start(src) { return src.match(/\n[^\n]*\n::[^:\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) {
- const regex = /(\n?\n?(?!::)[^\n]+?(?=\n::))|\n::(.(?:.|\n)*?(?=(?:\n::)|(?:\n\n)|$))/y;
+ const regex = /(\n?\n?(?!::)[^\n]+?(?=\n::[^:\n]))|\n::([^:\n](?:.|\n)*?(?=(?:\n::)|(?:\n\n)|$))/y;
let match;
let endIndex = 0;
const definitions = [];
@@ -706,34 +741,27 @@ const MarkedEmojiOptions = {
renderer : (token)=>` `
};
+const tableTerminators = [
+ `:+\\n`, // hardBreak
+ ` *{[^\n]+}`, // blockInjector
+ ` *{{[^{\n]*\n.*?\n}}` // mustacheDiv
+]
+
Marked.use(MarkedVariables());
-Marked.use({ extensions: [definitionListsMultiLine, definitionListsSingleLine, superSubScripts, mustacheSpans, mustacheDivs, mustacheInjectInline] });
+Marked.use({ extensions : [definitionListsMultiLine, definitionListsSingleLine, forcedParagraphBreaks, superSubScripts,
+ mustacheSpans, mustacheDivs, mustacheInjectInline] });
Marked.use(mustacheInjectBlock);
Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
-Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions));
+Marked.use(MarkedExtendedTables(tableTerminators), MarkedGFMHeadingId({ globalSlugs: true }), MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions));
-const nonWordAndColonTest = /[^\w:]/g;
-const cleanUrl = function (sanitize, base, href) {
- if(sanitize) {
- let prot;
- try {
- prot = decodeURIComponent(unescape(href))
- .replace(nonWordAndColonTest, '')
- .toLowerCase();
- } catch (e) {
- return null;
- }
- if(prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
- return null;
- }
- }
- try {
- href = encodeURI(href).replace(/%25/g, '%');
- } catch (e) {
- return null;
- }
- return href;
-};
+function cleanUrl(href) {
+ try {
+ href = encodeURI(href).replace(/%25/g, '%');
+ } catch {
+ return null;
+ }
+ return href;
+}
const escapeTest = /[&<>"']/;
const escapeReplace = /[&<>"']/g;
@@ -828,13 +856,15 @@ let globalPageNumber = 0;
module.exports = {
marked : Marked,
- render : (rawBrewText, pageNumber=1)=>{
- globalVarsList[pageNumber] = {}; //Reset global links for current page, to ensure values are parsed in order
- varsQueue = []; //Could move into MarkedVariables()
- globalPageNumber = pageNumber;
+ render : (rawBrewText, pageNumber=0)=>{
+ globalVarsList[pageNumber] = {}; //Reset global links for current page, to ensure values are parsed in order
+ varsQueue = []; //Could move into MarkedVariables()
+ globalPageNumber = pageNumber;
+ if(pageNumber==0) {
+ MarkedGFMResetHeadingIDs();
+ }
- rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n
\n`)
- .replace(/^(:+)$/gm, (match)=>`${`
`.repeat(match.length)}\n`);
+ rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n
\n`);
const opts = Marked.defaults;
rawBrewText = opts.hooks.preprocess(rawBrewText);
diff --git a/shared/naturalcrit/splitPane/splitPane.jsx b/shared/naturalcrit/splitPane/splitPane.jsx
index 55af5e3862..9a7b6fe2b2 100644
--- a/shared/naturalcrit/splitPane/splitPane.jsx
+++ b/shared/naturalcrit/splitPane/splitPane.jsx
@@ -7,8 +7,9 @@ const SplitPane = createClass({
displayName : 'SplitPane',
getDefaultProps : function() {
return {
- storageKey : 'naturalcrit-pane-split',
- onDragFinish : function(){} //fires when dragging
+ storageKey : 'naturalcrit-pane-split',
+ onDragFinish : function(){}, //fires when dragging
+ showDividerButtons : true
};
},
@@ -142,9 +143,11 @@ const SplitPane = createClass({
width={this.state.currentDividerPos}
>
{React.cloneElement(this.props.children[0], {
- moveBrew : this.state.moveBrew,
- moveSource : this.state.moveSource,
- setMoveArrows : this.setMoveArrows
+ ...(this.props.showDividerButtons && {
+ moveBrew: this.state.moveBrew,
+ moveSource: this.state.moveSource,
+ setMoveArrows: this.setMoveArrows,
+ }),
})}
{this.renderDivider()}
diff --git a/tests/markdown/definition-lists.test.js b/tests/markdown/definition-lists.test.js
index 9f5025d737..9c0bdf6b01 100644
--- a/tests/markdown/definition-lists.test.js
+++ b/tests/markdown/definition-lists.test.js
@@ -88,4 +88,16 @@ describe('Multiline Definition Lists', ()=>{
const rendered = Markdown.render(source).trim();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('Term 1 Inline definition 1 \nInline definition 2 (no DT) \n ');
});
+
+ test('Multiline Definition Term must have at least one non-empty Definition', function() {
+ const source = 'Term 1\n::';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`Term 1
\n
`);
+ });
+
+ test('Multiline Definition List must have at least one non-newline character after ::', function() {
+ const source = 'Term 1\n::\nDefinition 1\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`Term 1
\n
\nDefinition 1
`);
+ });
});
diff --git a/tests/markdown/hard-breaks.test.js b/tests/markdown/hard-breaks.test.js
new file mode 100644
index 0000000000..3d0f59a411
--- /dev/null
+++ b/tests/markdown/hard-breaks.test.js
@@ -0,0 +1,47 @@
+/* eslint-disable max-lines */
+
+const Markdown = require('naturalcrit/markdown.js');
+
+describe('Hard Breaks', ()=>{
+ test('Single Break', function() {
+ const source = ':\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ });
+
+ test('Double Break', function() {
+ const source = '::\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ });
+
+ test('Triple Break', function() {
+ const source = ':::\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ });
+
+ test('Many Break', function() {
+ const source = '::::::::::\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ });
+
+ test('Multiple sets of Breaks', function() {
+ const source = ':::\n:::\n:::';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
\n
\n
`);
+ });
+
+ test('Break directly between two paragraphs', function() {
+ const source = 'Line 1\n::\nLine 2';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`Line 1
\n
\nLine 2
`);
+ });
+
+ test('Ignored inside a code block', function() {
+ const source = '```\n\n:\n\n```\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`\n:\n
`);
+ });
+});
diff --git a/tests/markdown/mustache-syntax.test.js b/tests/markdown/mustache-syntax.test.js
index 3f7f2529b1..51284ef2b1 100644
--- a/tests/markdown/mustache-syntax.test.js
+++ b/tests/markdown/mustache-syntax.test.js
@@ -322,9 +322,9 @@ describe('Injection: When an injection tag follows an element', ()=>{
});
it('Renders an image element with injected style', function() {
- const source = '{position:absolute}';
+ const source = '{position:absolute}';
const rendered = Markdown.render(source).trimReturns();
- expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('
');
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('
');
});
it('Renders an element modified by only the first of two consecutive injections', function() {
@@ -343,19 +343,19 @@ describe('Injection: When an injection tag follows an element', ()=>{
it('Renders an image with added attributes', function() {
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`;
const rendered = Markdown.render(source).trimReturns();
- expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
});
it('Renders an image with "=" in the url, and added attributes', function() {
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`;
const rendered = Markdown.render(source).trimReturns();
- expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
});
it('Renders an image and added attributes with "=" in the value, ', function() {
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e,otherUrl="url?auth=12345"}`;
const rendered = Markdown.render(source).trimReturns();
- expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
});
});
diff --git a/tests/markdown/variables.test.js b/tests/markdown/variables.test.js
index 2c8db375ed..bf778b14de 100644
--- a/tests/markdown/variables.test.js
+++ b/tests/markdown/variables.test.js
@@ -315,21 +315,21 @@ describe('Normal Links and Images', ()=>{
const source = ``;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
-
`.trimReturns());
+
`.trimReturns());
});
it('Renders normal images with a title', function() {
const source = 'An image !';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
- An image !
`.trimReturns());
+ An image !
`.trimReturns());
});
it('Applies curly injectors to images', function() {
const source = `{width:100px}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
-
`.trimReturns());
+
`.trimReturns());
});
it('Renders normal links', function() {
diff --git a/themes/V3/Blank/snippets.js b/themes/V3/Blank/snippets.js
index 8d45560c58..8437dab2e7 100644
--- a/themes/V3/Blank/snippets.js
+++ b/themes/V3/Blank/snippets.js
@@ -153,6 +153,18 @@ module.exports = [
gen : dedent`
 {width:325px,mix-blend-mode:multiply}`
},
+ {
+ name : 'Image Wrap Left',
+ icon : 'fac image-wrap-left',
+ gen : dedent`
+  {width:280px,margin-right:-3cm,wrapLeft}`
+ },
+ {
+ name : 'Image Wrap Right',
+ icon : 'fac image-wrap-right',
+ gen : dedent`
+  {width:280px,margin-left:-3cm,wrapRight}`
+ },
{
name : 'Background Image',
icon : 'fas fa-tree',
diff --git a/themes/V3/Blank/style.less b/themes/V3/Blank/style.less
index 0f779c38b2..0f3766342f 100644
--- a/themes/V3/Blank/style.less
+++ b/themes/V3/Blank/style.less
@@ -156,6 +156,19 @@ body { counter-reset : page-numbers; }
break-inside : avoid;
}
+ /* Wrap Text */
+ .wrapLeft {
+ shape-outside : var(--HB_src);
+ float : right;
+ shape-margin : 0.2cm;
+ }
+
+ .wrapRight {
+ shape-outside : var(--HB_src);
+ float : left;
+ shape-margin : 0.2cm;
+ }
+
/* Watermark */
.watermark {
position : absolute;
@@ -236,7 +249,7 @@ body { counter-reset : page-numbers; }
left : 50%;
width : 50%;
height : 50%;
- transform : translateX(-50%) translateY(50%) rotate(calc(-1deg * var(--rotation))) scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY)));
+ transform : translateX(-50%) translateY(50%) scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY))) rotate(calc(-1deg * var(--rotation)));
}
& img {
position : absolute;