0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2025-12-31 21:42:44 +00:00

Merge branch 'master' into Issue_241

This commit is contained in:
David Bolack
2024-08-13 12:17:45 -05:00
16 changed files with 2635 additions and 2409 deletions

View File

@@ -3,7 +3,7 @@
"stylelint-config-recess-order", "stylelint-config-recess-order",
"stylelint-config-recommended"], "stylelint-config-recommended"],
"plugins": [ "plugins": [
"stylelint-stylistic", "@stylistic/stylelint-plugin",
"./stylelint_plugins/declaration-colon-align.js", "./stylelint_plugins/declaration-colon-align.js",
"./stylelint_plugins/declaration-colon-min-space-before", "./stylelint_plugins/declaration-colon-min-space-before",
"./stylelint_plugins/declaration-block-multi-line-min-declarations" "./stylelint_plugins/declaration-block-multi-line-min-declarations"
@@ -16,32 +16,32 @@
"font-family-no-missing-generic-family-keyword" : null, "font-family-no-missing-generic-family-keyword" : null,
"font-weight-notation" : "named-where-possible", "font-weight-notation" : "named-where-possible",
"font-family-name-quotes" : "always-unless-keyword", "font-family-name-quotes" : "always-unless-keyword",
"stylistic/indentation" : "tab", "@stylistic/indentation" : "tab",
"no-duplicate-selectors" : true, "no-duplicate-selectors" : true,
"stylistic/color-hex-case" : "upper", "@stylistic/color-hex-case" : "upper",
"color-hex-length" : "long", "color-hex-length" : "long",
"stylistic/selector-combinator-space-after" : "always", "@stylistic/selector-combinator-space-after" : "always",
"stylistic/selector-combinator-space-before" : "always", "@stylistic/selector-combinator-space-before" : "always",
"stylistic/selector-attribute-operator-space-before" : "never", "@stylistic/selector-attribute-operator-space-before" : "never",
"stylistic/selector-attribute-operator-space-after" : "never", "@stylistic/selector-attribute-operator-space-after" : "never",
"stylistic/selector-attribute-brackets-space-inside" : "never", "@stylistic/selector-attribute-brackets-space-inside" : "never",
"selector-attribute-quotes" : "always", "selector-attribute-quotes" : "always",
"selector-pseudo-element-colon-notation" : "double", "selector-pseudo-element-colon-notation" : "double",
"stylistic/selector-pseudo-class-parentheses-space-inside" : "never", "@stylistic/selector-pseudo-class-parentheses-space-inside" : "never",
"stylistic/block-opening-brace-space-before" : "always", "@stylistic/block-opening-brace-space-before" : "always",
"naturalcrit/declaration-colon-min-space-before" : 1, "naturalcrit/declaration-colon-min-space-before" : 1,
"stylistic/declaration-block-trailing-semicolon" : "always", "@stylistic/declaration-block-trailing-semicolon" : "always",
"stylistic/declaration-colon-space-after" : "always", "@stylistic/declaration-colon-space-after" : "always",
"stylistic/number-leading-zero" : "always", "@stylistic/number-leading-zero" : "always",
"function-url-quotes" : ["always", { "except": ["empty"] }], "function-url-quotes" : ["always", { "except": ["empty"] }],
"function-url-scheme-disallowed-list" : ["data","http"], "function-url-scheme-disallowed-list" : ["data","http"],
"comment-whitespace-inside" : "always", "comment-whitespace-inside" : "always",
"stylistic/string-quotes" : "single", "@stylistic/string-quotes" : "single",
"stylistic/media-feature-range-operator-space-before" : "always", "@stylistic/media-feature-range-operator-space-before" : "always",
"stylistic/media-feature-range-operator-space-after" : "always", "@stylistic/media-feature-range-operator-space-after" : "always",
"stylistic/media-feature-parentheses-space-inside" : "never", "@stylistic/media-feature-parentheses-space-inside" : "never",
"stylistic/media-feature-colon-space-before" : "always", "@stylistic/media-feature-colon-space-before" : "always",
"stylistic/media-feature-colon-space-after" : "always", "@stylistic/media-feature-colon-space-after" : "always",
"naturalcrit/declaration-colon-align" : true, "naturalcrit/declaration-colon-align" : true,
"naturalcrit/declaration-block-multi-line-min-declarations": 1 "naturalcrit/declaration-block-multi-line-min-declarations": 1
} }

View File

@@ -388,7 +388,7 @@ const Editor = createClass({
view={this.state.view} view={this.state.view}
value={this.props.brew.style ?? DEFAULT_STYLE_TEXT} value={this.props.brew.style ?? DEFAULT_STYLE_TEXT}
onChange={this.props.onStyleChange} onChange={this.props.onStyleChange}
enableFolding={false} enableFolding={true}
editorTheme={this.state.editorTheme} editorTheme={this.state.editorTheme}
rerenderParent={this.rerenderParent} /> rerenderParent={this.rerenderParent} />
</>; </>;

View File

@@ -28,6 +28,7 @@ const MetadataEditor = createClass({
return { return {
metadata : { metadata : {
editId : null, editId : null,
shareId : null,
title : '', title : '',
description : '', description : '',
thumbnail : '', thumbnail : '',
@@ -196,6 +197,7 @@ const MetadataEditor = createClass({
const listThemes = (renderer)=>{ const listThemes = (renderer)=>{
return _.map(_.values(mergedThemes[renderer]), (theme)=>{ return _.map(_.values(mergedThemes[renderer]), (theme)=>{
if(theme.path == this.props.metadata.shareId) return;
const preview = theme.thumbnail || `/themes/${theme.renderer}/${theme.path}/dropdownPreview.png`; const preview = theme.thumbnail || `/themes/${theme.renderer}/${theme.path}/dropdownPreview.png`;
const texture = theme.thumbnail || `/themes/${theme.renderer}/${theme.path}/dropdownTexture.png`; const texture = theme.thumbnail || `/themes/${theme.renderer}/${theme.path}/dropdownTexture.png`;
return <div className='item' key={`${renderer}_${theme.name}`} onClick={()=>this.handleTheme(theme)} title={''}> return <div className='item' key={`${renderer}_${theme.name}`} onClick={()=>this.handleTheme(theme)} title={''}>

View File

@@ -1,258 +1,239 @@
@import 'naturalcrit/styles/colors.less'; @import 'naturalcrit/styles/colors.less';
.metadataEditor{ .metadataEditor {
position : absolute; position : absolute;
z-index : 5; z-index : 5;
box-sizing : border-box; box-sizing : border-box;
width : 100%; width : 100%;
padding : 25px;
background-color : #999;
height : calc(100vh - 54px); // 54px is the height of the navbar + snippet bar. probably a better way to dynamic get this. height : calc(100vh - 54px); // 54px is the height of the navbar + snippet bar. probably a better way to dynamic get this.
padding : 25px;
overflow-y : auto; overflow-y : auto;
background-color : #999999;
.sectionHead { .sectionHead {
font-weight: 1000; margin : 20px 0;
margin: 20px 0; font-weight : 1000;
&:first-of-type { &:first-of-type { margin-top : 0; }
margin-top: 0;
}
} }
& > div { & > div { margin-bottom : 10px; }
margin-bottom: 10px;
}
.field-group { .field-group {
display: flex; display : flex;
width: 100%; flex-wrap : wrap;
flex-wrap: wrap; gap : 10px;
gap: 10px; width : 100%;
} }
.field-column { .field-column {
display: flex; display : flex;
flex-direction: column; flex : 5 0 200px;
flex: 5 0 200px; flex-direction : column;
gap: 10px; gap : 10px;
} }
.field{ .field {
position : relative;
display : flex; display : flex;
flex-wrap : wrap; flex-wrap : wrap;
width : 100%; width : 100%;
min-width : 200px; min-width : 200px;
position : relative; & > label {
&>label{
width : 80px; width : 80px;
font-size : 11px; font-size : 11px;
font-weight : 800; font-weight : 800;
line-height : 1.8em; line-height : 1.8em;
text-transform : uppercase; text-transform : uppercase;
} }
&>.value{ & > .value {
flex : 1 1 auto; flex : 1 1 auto;
width : 50px; width : 50px;
&:invalid { &:invalid { background : #FFB9B9; }
background : #ffb9b9;
}
} }
input[type='text'], textarea { input[type='text'], textarea {
border : 1px solid gray; border : 1px solid gray;
&:focus { &:focus { outline : 1px solid #444444; }
outline: 1px solid #444;
}
} }
&.thumbnail{ &.thumbnail {
height : 1.4em; height : 1.4em;
label{ label { line-height : 2.0em; }
line-height: 2.0em; .value {
overflow : hidden;
text-overflow : ellipsis;
} }
.value{ button {
overflow: hidden; padding : 0px 5px;
text-overflow: ellipsis; color : white;
} background-color : black;
button{ border : 1px solid #999999;
border: 1px solid #999; &:hover { background-color : #777777; }
color: white;
padding: 0px 5px;
background-color: black;
&:hover{
background-color: #777;
}
} }
} }
&.description { &.description {
flex: 1; flex : 1;
textarea.value { textarea.value {
resize : none;
height : auto; height : auto;
font-family : 'Open Sans', sans-serif; font-family : 'Open Sans', sans-serif;
font-size : 0.8em; font-size : 0.8em;
resize : none;
} }
} }
&.language .language-dropdown { &.language .language-dropdown {
max-width : 150px;
z-index : 200; z-index : 200;
max-width : 150px;
} }
small { small {
display : inline-block;
font-size : 0.6em; font-size : 0.6em;
font-style : italic; font-style : italic;
line-height : 1.4em; line-height : 1.4em;
display : inline-block;
} }
} }
.thumbnail-preview { .thumbnail-preview {
position: relative; position : relative;
justify-self: center; flex : 1 1;
width: 80px; justify-self : center;
height: min-content; width : 80px;
flex: 1 1; height : min-content;
max-height: 115px; max-height : 115px;
aspect-ratio: 1 / 1; aspect-ratio : 1 / 1;
object-fit: contain; object-fit : contain;
background-color: #AAA; background-color : #AAAAAA;
} }
.systems.field .value{ .systems.field .value {
label{ label {
vertical-align : middle;
margin-right : 15px;
cursor : pointer;
font-size : 0.7em;
font-weight : 800;
user-select : none;
white-space : nowrap;
display : inline-flex; display : inline-flex;
align-items : center; align-items : center;
} margin-right : 15px;
a { font-size : 0.7em;
font-size : 0.7em; font-weight : 800;
font-weight : 800; white-space : nowrap;
display : inline-flex;
}
input{
vertical-align : middle; vertical-align : middle;
cursor : pointer; cursor : pointer;
user-select : none;
}
a {
display : inline-flex;
font-size : 0.7em;
font-weight : 800;
}
input {
margin : 3px; margin : 3px;
vertical-align : middle;
cursor : pointer;
} }
} }
.publish.field .value{ .publish.field .value {
position : relative; position : relative;
margin-bottom: 15px; margin-bottom : 15px;
button{ button { width : 100%; }
width:100%; button.publish {
}
button.publish{
.button(@blueLight); .button(@blueLight);
} }
button.unpublish{ button.unpublish {
.button(@silver); .button(@silver);
} }
} }
.delete.field .value{ .delete.field .value {
button{ button {
.button(@red); .button(@red);
} }
} }
.authors.field .value{ .authors.field .value {
font-size: 0.8em; font-size : 0.8em;
line-height : 1.5em; line-height : 1.5em;
} }
.themes.field{ .themes.field {
font-size : 13.33px; font-size : 13.33px;
.navDropdownContainer { .navDropdownContainer {
background-color : white; position : relative;
position : relative; z-index : 100;
z-index : 100; background-color : white;
&.disabled { &.disabled {
font-style :italic; font-style : italic;
font-style : italic; color : dimgray;
background-color : darkgray; background-color : darkgray;
color : dimgray;
} }
&>div:first-child { & > div:first-child {
border : 2px solid rgb(118,118,118); padding : 6px 3px;
padding : 6px 3px; background-color : inherit;
background-color : inherit; border : 2px solid rgb(118,118,118);
i { i { float : right; }
float : right;
}
&:hover { &:hover {
background-color : @blue; color : white;
color : white; background-color : @blue;
} }
} }
.navDropdown .item > p { .navDropdown .item > p {
width: 45%; width : 45%;
white-space: nowrap; height : 1.1em;
text-overflow: ellipsis; overflow : hidden;
overflow: hidden; text-overflow : ellipsis;
height: 1.1em; white-space : nowrap;
} }
.navDropdown { .navDropdown {
box-shadow : 0px 5px 10px rgba(0, 0, 0, 0.3);
position : absolute; position : absolute;
width : 100%; width : 100%;
box-shadow : 0px 5px 10px rgba(0, 0, 0, 0.3);
.item { .item {
padding : 3px 3px;
border-top : 1px solid rgb(118, 118, 118);
position : relative; position : relative;
padding : 3px 3px;
overflow : visible; overflow : visible;
background-color : white; background-color : white;
border-top : 1px solid rgb(118, 118, 118);
.preview { .preview {
display : flex; position : absolute;
flex-direction: column; top : 0;
background : #ccc; right : 0;
border-radius : 5px; z-index : 1;
box-shadow : 0 0 5px black; display : flex;
width : 200px; flex-direction : column;
color :black; width : 200px;
position : absolute; overflow : hidden;
top : 0; color : black;
right : 0; background : #CCCCCC;
opacity : 0; border-radius : 5px;
transition : opacity 250ms ease; box-shadow : 0 0 5px black;
z-index : 1; opacity : 0;
overflow :hidden; transition : opacity 250ms ease;
h6 { h6 {
font-weight : 900; padding-block : 0.5em;
padding-inline:1em; padding-inline : 1em;
padding-block :.5em; font-weight : 900;
border-bottom :2px solid hsl(0,0%,40%); border-bottom : 2px solid hsl(0,0%,40%);
} }
} }
&:hover { &:hover {
background-color : @blue; color : white;
color : white; background-color : @blue;
}
&:hover > .preview {
opacity: 1;
} }
&:hover > .preview { opacity : 1; }
.texture-container { .texture-container {
position: absolute; position : absolute;
width: 100%; top : 0;
height: 100%; left : 0;
min-height: 100%; width : 100%;
top: 0; height : 100%;
left: 0; min-height : 100%;
overflow: hidden; overflow : hidden;
> img { > img {
mask-image : linear-gradient(90deg, transparent, black 20%); position : absolute;
-webkit-mask-image : linear-gradient(90deg, transparent, black 20%); top : 0px;
position : absolute; right : 0;
right : 0; width : 50%;
top : 0px;
width : 50%;
min-height : 100%; min-height : 100%;
-webkit-mask-image : linear-gradient(90deg, transparent, black 20%);
mask-image : linear-gradient(90deg, transparent, black 20%);
} }
} }
} }
@@ -260,84 +241,69 @@
} }
} }
.field .list { .field .list {
display: flex; display : flex;
flex: 1 0; flex : 1 0;
flex-wrap: wrap; flex-wrap : wrap;
> * { > * { flex : 0 0 auto; }
flex: 0 0 auto;
}
#groupedIcon { #groupedIcon {
#backgroundColors; #backgroundColors;
display: inline-block; position : relative;
height: ~"calc(100% + 0.6em)"; top : -0.3em;
position: relative; right : -0.3em;
top: -0.3em; display : inline-block;
right: -0.3em; min-width : 20px;
cursor: pointer; height : ~'calc(100% + 0.6em)';
min-width: 20px; color : white;
text-align: center; text-align : center;
color: white; cursor : pointer;
i { i {
position: relative; position : relative;
top: 50%; top : 50%;
transform: translateY(-50%); transform : translateY(-50%);
} }
&:not(:last-child) { &:not(:last-child) { border-right : 1px solid black; }
border-right: 1px solid black;
}
&:last-child { &:last-child { border-radius : 0 0.5em 0.5em 0; }
border-radius: 0 0.5em 0.5em 0;
}
} }
.badge { .badge {
background-color: #dddddd; padding : 0.3em;
border-radius: .5em; margin : 2px;
font-size: .9em; font-size : 0.9em;
margin: 2px; background-color : #DDDDDD;
padding: .3em; border-radius : 0.5em;
.icon { .icon {
#groupedIcon #groupedIcon; }
}
} }
.input-group { .input-group {
height: ~"calc(.9em + 4px + .6em)"; height : ~'calc(.9em + 4px + .6em)';
input { input { border-radius : 0.5em 0 0 0.5em; }
border-radius: .5em 0 0 .5em;
}
input:last-child { input:last-child { border-radius : 0.5em; }
border-radius: .5em;
}
.value { .value {
width: 7.5vw; width : 7.5vw;
min-width: 75px; min-width : 75px;
height: 100%; height : 100%;
} }
.invalid:focus { .invalid:focus { background-color : pink; }
background-color: pink;
}
.icon { .icon {
#groupedIcon; #groupedIcon;
height: 97%; top : -0.54em;
font-size: .8em; right : 1px;
right: 1px; height : 97%;
top: -.54em; font-size : 0.8em;
i { i { font-size : 1.125em; }
font-size: 1.125em;
}
} }
} }
} }

View File

@@ -71,9 +71,9 @@ const Homebrew = createClass({
<Route path='/new/:id' element={<WithRoute el={NewPage} brew={this.props.brew} userThemes={this.props.userThemes}/>} /> <Route path='/new/:id' element={<WithRoute el={NewPage} brew={this.props.brew} userThemes={this.props.userThemes}/>} />
<Route path='/new' element={<WithRoute el={NewPage} userThemes={this.props.userThemes}/> } /> <Route path='/new' element={<WithRoute el={NewPage} userThemes={this.props.userThemes}/> } />
<Route path='/user/:username' element={<WithRoute el={UserPage} brews={this.props.brews} />} /> <Route path='/user/:username' element={<WithRoute el={UserPage} brews={this.props.brews} />} />
<Route path='/changelog' element={<WithRoute el={SharePage} brew={this.props.brew} />} /> <Route path='/changelog' element={<WithRoute el={SharePage} brew={this.props.brew} disableMeta={true} />} />
<Route path='/faq' element={<WithRoute el={SharePage} brew={this.props.brew} />} /> <Route path='/faq' element={<WithRoute el={SharePage} brew={this.props.brew} disableMeta={true} />} />
<Route path='/migrate' element={<WithRoute el={SharePage} brew={this.props.brew} />} /> <Route path='/migrate' element={<WithRoute el={SharePage} brew={this.props.brew} disableMeta={true} />} />
<Route path='/account' element={<WithRoute el={AccountPage} brew={this.props.brew} accountDetails={this.props.brew.accountDetails} />} /> <Route path='/account' element={<WithRoute el={AccountPage} brew={this.props.brew} accountDetails={this.props.brew.accountDetails} />} />
<Route path='/legacy' element={<WithRoute el={HomePage} brew={this.props.brew} />} /> <Route path='/legacy' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
<Route path='/error' element={<WithRoute el={ErrorPage} brew={this.props.brew} />} /> <Route path='/error' element={<WithRoute el={ErrorPage} brew={this.props.brew} />} />

View File

@@ -18,7 +18,8 @@ const SharePage = createClass({
displayName : 'SharePage', displayName : 'SharePage',
getDefaultProps : function() { getDefaultProps : function() {
return { return {
brew : DEFAULT_BREW_LOAD, brew : DEFAULT_BREW_LOAD,
disableMeta : false
}; };
}, },
@@ -68,13 +69,21 @@ const SharePage = createClass({
}, },
render : function(){ render : function(){
const titleStyle = this.props.disableMeta ? { cursor: 'default' } : {};
const titleEl = <Nav.item className='brewTitle' style={titleStyle}>{this.props.brew.title}</Nav.item>;
return <div className='sharePage sitePage'> return <div className='sharePage sitePage'>
<Meta name='robots' content='noindex, nofollow' /> <Meta name='robots' content='noindex, nofollow' />
<Navbar> <Navbar>
<Nav.section className='titleSection'> <Nav.section className='titleSection'>
<MetadataNav brew={this.props.brew}> {
<Nav.item className='brewTitle'>{this.props.brew.title}</Nav.item> this.props.disableMeta ?
</MetadataNav> titleEl
:
<MetadataNav brew={this.props.brew}>
{titleEl}
</MetadataNav>
}
</Nav.section> </Nav.section>
<Nav.section> <Nav.section>

4481
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -105,7 +105,7 @@
"less": "^3.13.1", "less": "^3.13.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"marked": "11.2.0", "marked": "11.2.0",
"marked-emoji": "^1.4.1", "marked-emoji": "^1.4.2",
"marked-extended-tables": "^1.0.8", "marked-extended-tables": "^1.0.8",
"marked-gfm-heading-id": "^3.2.0", "marked-gfm-heading-id": "^3.2.0",
"marked-smartypants-lite": "^1.0.2", "marked-smartypants-lite": "^1.0.2",
@@ -117,22 +117,22 @@
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-frame-component": "^4.1.3", "react-frame-component": "^4.1.3",
"react-router-dom": "6.25.1", "react-router-dom": "6.26.0",
"sanitize-filename": "1.6.3", "sanitize-filename": "1.6.3",
"superagent": "^9.0.2", "superagent": "^9.0.2",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git" "vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
}, },
"devDependencies": { "devDependencies": {
"@stylistic/stylelint-plugin": "^3.0.0",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-plugin-jest": "^28.6.0", "eslint-plugin-jest": "^28.8.0",
"eslint-plugin-react": "^7.35.0", "eslint-plugin-react": "^7.35.0",
"jest": "^29.7.0", "jest": "^29.7.0",
"jest-expect-message": "^1.1.3", "jest-expect-message": "^1.1.3",
"postcss-less": "^6.0.0", "postcss-less": "^6.0.0",
"stylelint": "^15.11.0", "stylelint": "^16.8.0",
"stylelint-config-recess-order": "^4.6.0", "stylelint-config-recess-order": "^5.0.1",
"stylelint-config-recommended": "^13.0.0", "stylelint-config-recommended": "^14.0.1",
"stylelint-stylistic": "^0.4.3",
"supertest": "^7.0.0" "supertest": "^7.0.0"
} }
} }

View File

@@ -39,8 +39,10 @@ if(typeof window !== 'undefined'){
//Autocompletion //Autocompletion
require('codemirror/addon/hint/show-hint.js'); require('codemirror/addon/hint/show-hint.js');
const foldCode = require('./fold-code'); const foldPagesCode = require('./fold-pages');
foldCode.registerHomebreweryHelper(CodeMirror); foldPagesCode.registerHomebreweryHelper(CodeMirror);
const foldCSSCode = require('./fold-css');
foldCSSCode.registerHomebreweryHelper(CodeMirror);
} }
const CodeEditor = createClass({ const CodeEditor = createClass({
@@ -411,11 +413,11 @@ const CodeEditor = createClass({
foldOptions : function(cm){ foldOptions : function(cm){
return { return {
scanUp : true, scanUp : true,
rangeFinder : CodeMirror.fold.homebrewery, rangeFinder : this.props.language === 'css' ? CodeMirror.fold.homebrewerycss : CodeMirror.fold.homebrewery,
widget : (from, to)=>{ widget : (from, to)=>{
let text = ''; let text = '';
let currentLine = from.line; let currentLine = from.line;
const maxLength = 50; let maxLength = 50;
let foldPreviewText = ''; let foldPreviewText = '';
while (currentLine <= to.line && text.length <= maxLength) { while (currentLine <= to.line && text.length <= maxLength) {
@@ -430,10 +432,15 @@ const CodeEditor = createClass({
} }
} }
text = foldPreviewText || `Lines ${from.line+1}-${to.line+1}`; text = foldPreviewText || `Lines ${from.line+1}-${to.line+1}`;
text = text.replace('{', '').trim();
// Truncate data URLs at `data:`
const startOfData = text.indexOf('data:');
if(startOfData > 0)
maxLength = Math.min(startOfData + 5, maxLength);
text = text.trim();
if(text.length > maxLength) if(text.length > maxLength)
text = `${text.substr(0, maxLength)}...`; text = `${text.slice(0, maxLength)}...`;
return `\u21A4 ${text} \u21A6`; return `\u21A4 ${text} \u21A6`;
} }
@@ -450,3 +457,4 @@ const CodeEditor = createClass({
}); });
module.exports = CodeEditor; module.exports = CodeEditor;

View File

@@ -0,0 +1,44 @@
module.exports = {
registerHomebreweryHelper : function(CodeMirror) {
CodeMirror.registerHelper('fold', 'homebrewerycss', function(cm, start) {
// BRACE FOLDING
const startMatcher = /\{[ \t]*$/;
const endMatcher = /\}[ \t]*$/;
const activeLine = cm.getLine(start.line);
if(activeLine.match(startMatcher)) {
const lastLineNo = cm.lastLine();
let end = start.line + 1;
let braceCount = 1;
while (end < lastLineNo) {
const curLine = cm.getLine(end);
if(curLine.match(startMatcher)) braceCount++;
if(curLine.match(endMatcher)) braceCount--;
if(braceCount == 0) break;
++end;
}
return {
from : CodeMirror.Pos(start.line, 0),
to : CodeMirror.Pos(end, cm.getLine(end).length)
};
}
// @import and data-url folding
const importMatcher = /^@import.*?;/;
const dataURLMatcher = /url\(.*?data\:.*\)/;
if(activeLine.match(importMatcher) || activeLine.match(dataURLMatcher)) {
return {
from : CodeMirror.Pos(start.line, 0),
to : CodeMirror.Pos(start.line, activeLine.length)
};
}
return null;
});
}
};

View File

@@ -28,17 +28,18 @@ const mathParser = new MathParser({
round : true, round : true,
floor : true, floor : true,
ceil : true, ceil : true,
abs : true,
sin : false, cos : false, tan : false, asin : false, acos : false, sin : false, cos : false, tan : false, asin : false, acos : false,
atan : false, sinh : false, cosh : false, tanh : false, asinh : false, atan : false, sinh : false, cosh : false, tanh : false, asinh : false,
acosh : false, atanh : false, sqrt : false, cbrt : false, log : false, acosh : false, atanh : false, sqrt : false, cbrt : false, log : false,
log2 : false, ln : false, lg : false, log10 : false, expm1 : false, log2 : false, ln : false, lg : false, log10 : false, expm1 : false,
log1p : false, abs : false, trunc : false, join : false, sum : false, log1p : false, trunc : false, join : false, sum : false, indexOf : false,
'-' : false, '+' : false, exp : false, not : false, length : false, '-' : false, '+' : false, exp : false, not : false, length : false,
'!' : false, sign : false, random : false, fac : false, min : false, '!' : false, sign : false, random : false, fac : false, min : false,
max : false, hypot : false, pyt : false, pow : false, atan2 : false, max : false, hypot : false, pyt : false, pow : false, atan2 : false,
'if' : false, gamma : false, roundTo : false, map : false, fold : false, 'if' : false, gamma : false, roundTo : false, map : false, fold : false,
filter : false, indexOf : false, filter : false,
remainder : false, factorial : false, remainder : false, factorial : false,
comparison : false, concatenate : false, comparison : false, concatenate : false,
@@ -46,6 +47,16 @@ const mathParser = new MathParser({
array : false, fndef : false array : false, fndef : false
} }
}); });
// Add sign function
mathParser.functions.sign = function (a) {
if(a >= 0) return '+';
return '-';
};
// Add signed function
mathParser.functions.signed = function (a) {
if(a >= 0) return `+${a}`;
return `${a}`;
};
//Processes the markdown within an HTML block if it's just a class-wrapper //Processes the markdown within an HTML block if it's just a class-wrapper
renderer.html = function (html) { renderer.html = function (html) {
@@ -441,7 +452,7 @@ const replaceVar = function(input, hoist=false, allowUnresolved=false) {
const label = match[2]; const label = match[2];
//v=====--------------------< HANDLE MATH >-------------------=====v// //v=====--------------------< HANDLE MATH >-------------------=====v//
const mathRegex = /[a-z]+\(|[+\-*/^()]/g; const mathRegex = /[a-z]+\(|[+\-*/^(),]/g;
const matches = label.split(mathRegex); const matches = label.split(mathRegex);
const mathVars = matches.filter((match)=>isNaN(match))?.map((s)=>s.trim()); // Capture any variable names const mathVars = matches.filter((match)=>isNaN(match))?.map((s)=>s.trim()); // Capture any variable names
@@ -451,7 +462,7 @@ const replaceVar = function(input, hoist=false, allowUnresolved=false) {
mathVars?.forEach((variable)=>{ mathVars?.forEach((variable)=>{
const foundVar = lookupVar(variable, globalPageNumber, hoist); const foundVar = lookupVar(variable, globalPageNumber, hoist);
if(foundVar && foundVar.resolved && foundVar.content && !isNaN(foundVar.content)) // Only subsitute math values if fully resolved, not empty strings, and numbers if(foundVar && foundVar.resolved && foundVar.content && !isNaN(foundVar.content)) // Only subsitute math values if fully resolved, not empty strings, and numbers
replacedLabel = replacedLabel.replaceAll(variable, foundVar.content); replacedLabel = replacedLabel.replaceAll(new RegExp(`(?<!\\w)(${variable})(?!\\w)`, 'g'), foundVar.content);
}); });
try { try {

View File

@@ -1,5 +1,5 @@
const stylelint = require('stylelint'); const stylelint = require('stylelint');
const { isNumber } = require('stylelint/lib/utils/validateTypes'); const { isNumber } = require('stylelint/lib/utils/validateTypes.cjs');
const { report, ruleMessages, validateOptions } = stylelint.utils; const { report, ruleMessages, validateOptions } = stylelint.utils;
const ruleName = 'naturalcrit/declaration-block-multi-line-min-declarations'; const ruleName = 'naturalcrit/declaration-block-multi-line-min-declarations';

View File

@@ -1,5 +1,5 @@
const stylelint = require('stylelint'); const stylelint = require('stylelint');
const { isNumber } = require('stylelint/lib/utils/validateTypes'); const { isNumber } = require('stylelint/lib/utils/validateTypes.cjs');
const { report, ruleMessages, validateOptions } = stylelint.utils; const { report, ruleMessages, validateOptions } = stylelint.utils;
const ruleName = 'naturalcrit/declaration-colon-min-space-before'; const ruleName = 'naturalcrit/declaration-colon-min-space-before';

View File

@@ -371,3 +371,35 @@ describe('Cross-page variables', ()=>{
expect(rendered, `Input:\n${[source0, source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('<p>two</p><p>one</p>\\page<p>two</p>'); expect(rendered, `Input:\n${[source0, source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('<p>two</p><p>one</p>\\page<p>two</p>');
}); });
}); });
describe('Math function parameter handling', ()=>{
it('allows variables in single-parameter functions', function() {
const source = '[var]:4.1\n\n$[floor(var)]';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>4</p>`);
});
it('allows one variable and a number in two-parameter functions', function() {
const source = '[var]:4\n\n$[min(1,var)]';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>1</p>`);
});
it('allows two variables in two-parameter functions', function() {
const source = '[var1]:4\n\n[var2]:8\n\n$[min(var1,var2)]';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>4</p>`);
});
});
describe('Variable names that are subsets of other names', ()=>{
it('do not conflict with function names', function() {
const source = `[a]: -1\n\n$[abs(a)]`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered).toBe('<p>1</p>');
});
it('do not conflict with other variable names', function() {
const source = `[ab]: 2\n\n[aba]: 8\n\n[ba]: 4\n\n$[ab + aba + ba]`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered).toBe('<p>14</p>');
});
});

View File

@@ -892,6 +892,9 @@ h6,
.useColumns(0.96, @fillMode: balance); .useColumns(0.96, @fillMode: balance);
} }
} }
.toc.wide li {
break-inside: auto;
}
} }
// ***************************** // *****************************