From 522b54838e0fc4d7a25a08d5a42f4075c4d544c7 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 10 Apr 2026 23:38:51 -0500 Subject: [PATCH 01/25] Add metadata values to split in helpers. --- shared/helpers.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/shared/helpers.js b/shared/helpers.js index d2a9c8b73..c445e1ea1 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -93,6 +93,39 @@ const splitTextStyleAndMetadata = (brew)=>{ const metadata = yaml.load(metadataSection); Object.assign(brew, _.pick(metadata, ['title', 'description', 'renderer', 'theme', 'lang'])); brew.snippets = yamlSnippetsToText(_.pick(metadata, ['snippets']).snippets || ''); + + // Copy Required Bleed Space or Use defaults + brew.bleed = { + top : _.pick(metadata, ['bleed']).top || '.125in', + bottom : _.pick(metadata, ['bleed']).bottom || '.125in', + left : _.pick(metadata, ['bleed']).left || '.125in', + right : _.pick(metadata, ['bleed']).right || '.125in', + }; + + // Copy Required Safe Space or Use defaults + brew.safetySpace = { + top : _.pick(metadata, ['safetySpace']).top || '.125in', + bottom : _.pick(metadata, ['safetySpace']).bottom || '.125in', + left : _.pick(metadata, ['safetySpace']).left || '.125in', + right : _.pick(metadata, ['safetySpace']).right || '.125in', + }; + + // Set brew paper trim size or default to US Letter + brew.trimSize = { + width : _.pick(metadata, ['trimSize']).width || '8.5in', + height : _.pick(metadata, ['trimSize']).height || '11in', + }; + + // Set Column specs or use defaults + brew.columns = _.pick(metadata, ['columns']) || '2'; + brew.columnGutter = _.pick(metadata, ['columnGutter']) || '.125in'; + + // Set License or set to Unlicensed + brew.license = _.pick(metadata, ['license']) || 'None'; + + // Set Authors legal names or to empty. + brew.legalAuthors = _.pick(metadata, ['legalAuthors']) || ''; + brew.text = brew.text.slice(index + 6); } if(brew.text.startsWith('```css')) { From bda56d4f8d5e35e784cca1e5922c871ee0e623e0 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 11 Apr 2026 00:15:06 -0500 Subject: [PATCH 02/25] Update new meta data save, update tests --- server/homebrew.api.js | 32 +++++++++++ server/homebrew.api.spec.js | 108 ++++++++++++++++++++++++------------ 2 files changed, 106 insertions(+), 34 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 04c6507e8..d4d0f205c 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -220,6 +220,38 @@ const api = { const metadata = _.pick(brew, ['title', 'description', 'tags', 'renderer', 'theme']); const snippetsArray = brewSnippetsToJSON('brew_snippets', brew.snippets, null, false).snippets; metadata.snippets = snippetsArray.length > 0 ? snippetsArray : undefined; + + metadata.bleed = { + top : brew?.bleed?.top || '.125in', + bottom : brew?.bleed?.bottom || '.125in', + left : brew?.bleed?.left || '.125in', + right : brew?.bleed?.right || '.125in', + }; + + // Copy Required Safe Space or Use defaults + metadata.safetySpace = { + top : brew?.safetySpace?.top || '.125in', + bottom : brew?.safetySpace?.bottom || '.125in', + left : brew?.safetySpace?.left || '.125in', + right : brew?.safetySpace?.right || '.125in', + }; + + // Set brew paper trim size or default to US Letter + metadata.trimSize = { + width : brew?.trimSize?.width || '8.5in', + height : brew?.trimSize?.height || '11in', + }; + + // Set Column specs or use defaults + metadata.columns = brew?.columns || '2'; + metadata.columnGutter = brew?.columnGutter || '.125in'; + + // Set License or set to Unlicensed + metadata.license = brew?.license || 'None'; + + // Set Authors legal names or to empty. + metadata.legalAuthors = brew?.legalAuthors || ''; + text = `\`\`\`metadata\n` + `${yaml.dump(metadata)}\n` + `\`\`\`\n\n` + diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index 05ae8c51a..a18ab3b43 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -390,7 +390,13 @@ describe('Tests for api', ()=>{ tags : ['something', 'fun'], renderer : 'v3', theme : 'phb', - googleId : '12345' + googleId : '12345', + bleed : { + top : '.125in', + bottom : '.125in', + left : '.125in', + right : '.125in' + } }); expect(result).toEqual(`\`\`\`metadata @@ -401,6 +407,23 @@ tags: - fun renderer: v3 theme: phb +bleed: + top: .125in + bottom: .125in + left: .125in + right: .125in +safetySpace: + top: .125in + bottom: .125in + left: .125in + right: .125in +trimSize: + width: 8.5in + height: 11in +columns: '2' +columnGutter: .125in +license: None +legalAuthors: '' \`\`\` @@ -416,7 +439,7 @@ brew`); tags : ['something', 'fun'], renderer : 'v3', theme : 'phb', - googleId : '12345' + googleId : '12345', }); expect(result).toEqual(`\`\`\`metadata @@ -427,6 +450,23 @@ tags: - fun renderer: v3 theme: phb +bleed: + top: .125in + bottom: .125in + left: .125in + right: .125in +safetySpace: + top: .125in + bottom: .125in + left: .125in + right: .125in +trimSize: + width: 8.5in + height: 11in +columns: '2' +columnGutter: .125in +license: None +legalAuthors: '' \`\`\` @@ -537,16 +577,16 @@ brew`); expect(res.status).toHaveBeenCalledWith(200); expect(res.send).toHaveBeenCalledWith({ - _id : '1', - authors : ['test user'], - createdAt : undefined, - description : '', - editId : expect.any(String), - gDrive : false, - pageCount : 1, - published : false, - renderer : 'V3', - lang : 'en', + _id : '1', + authors : ['test user'], + createdAt : undefined, + description : '', + editId : expect.any(String), + gDrive : false, + pageCount : 1, + published : false, + renderer : 'V3', + lang : 'en', shareId : expect.any(String), style : undefined, tags : [], @@ -595,28 +635,28 @@ brew`); expect(google.newGoogleBrew).toHaveBeenCalled(); expect(res.status).toHaveBeenCalledWith(200); expect(res.send).toHaveBeenCalledWith({ - _id : '1', - authors : ['test user'], - createdAt : undefined, - description : '', - editId : expect.any(String), - gDrive : false, - pageCount : 1, - published : false, - renderer : 'V3', - lang : 'en', - shareId : expect.any(String), - googleId : expect.any(String), - style : undefined, - tags : [], - text : undefined, - textBin : undefined, - theme : '5ePHB', - thumbnail : '', - title : 'asdf', - trashed : false, - updatedAt : undefined, - views : 0 + _id : '1', + authors : ['test user'], + createdAt : undefined, + description : '', + editId : expect.any(String), + gDrive : false, + pageCount : 1, + published : false, + renderer : 'V3', + lang : 'en', + shareId : expect.any(String), + googleId : expect.any(String), + style : undefined, + tags : [], + text : undefined, + textBin : undefined, + theme : '5ePHB', + thumbnail : '', + title : 'asdf', + trashed : false, + updatedAt : undefined, + views : 0 }); }); }); From 5f266d67341ad2663378541e7654815038066072 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 11 Apr 2026 00:17:44 -0500 Subject: [PATCH 03/25] Remove unneeded test value --- server/homebrew.api.spec.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index a18ab3b43..4a469dfed 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -390,13 +390,7 @@ describe('Tests for api', ()=>{ tags : ['something', 'fun'], renderer : 'v3', theme : 'phb', - googleId : '12345', - bleed : { - top : '.125in', - bottom : '.125in', - left : '.125in', - right : '.125in' - } + googleId : '12345' }); expect(result).toEqual(`\`\`\`metadata From d3287b7cd71dcc259782585532cf236f59207584 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 11 Apr 2026 09:46:16 -0500 Subject: [PATCH 04/25] Update defaults based on RPGNow POD https://help.drivethrupartners.com/hc/en-us/articles/12780800178583-Quick-Specifications-for-Print-Books#h_01HP59NC7WJ9C84PEDSFM1WBR9 --- server/homebrew.api.js | 8 ++++---- server/homebrew.api.spec.js | 16 ++++++++-------- shared/helpers.js | 10 +++++----- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index d4d0f205c..58aa4a72e 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -230,10 +230,10 @@ const api = { // Copy Required Safe Space or Use defaults metadata.safetySpace = { - top : brew?.safetySpace?.top || '.125in', - bottom : brew?.safetySpace?.bottom || '.125in', - left : brew?.safetySpace?.left || '.125in', - right : brew?.safetySpace?.right || '.125in', + top : brew?.safetySpace?.top || '.25in', + bottom : brew?.safetySpace?.bottom || '.25in', + outer : brew?.safetySpace?.outer || '.25in', + inner : brew?.safetySpace?.inner || '.5in', }; // Set brew paper trim size or default to US Letter diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index 4a469dfed..4f151aeac 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -407,10 +407,10 @@ bleed: left: .125in right: .125in safetySpace: - top: .125in - bottom: .125in - left: .125in - right: .125in + top: .25in + bottom: .25in + outer: .25in + inner: .5in trimSize: width: 8.5in height: 11in @@ -450,10 +450,10 @@ bleed: left: .125in right: .125in safetySpace: - top: .125in - bottom: .125in - left: .125in - right: .125in + top: .25in + bottom: .25in + outer: .25in + inner: .5in trimSize: width: 8.5in height: 11in diff --git a/shared/helpers.js b/shared/helpers.js index c445e1ea1..2d75ae6b8 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -104,10 +104,10 @@ const splitTextStyleAndMetadata = (brew)=>{ // Copy Required Safe Space or Use defaults brew.safetySpace = { - top : _.pick(metadata, ['safetySpace']).top || '.125in', - bottom : _.pick(metadata, ['safetySpace']).bottom || '.125in', - left : _.pick(metadata, ['safetySpace']).left || '.125in', - right : _.pick(metadata, ['safetySpace']).right || '.125in', + top : _.pick(metadata, ['safetySpace']).top || '.25in', + bottom : _.pick(metadata, ['safetySpace']).bottom || '.25in', + outer : _.pick(metadata, ['safetySpace']).outer || '.25in', + inner : _.pick(metadata, ['safetySpace']).inner || '.5in', }; // Set brew paper trim size or default to US Letter @@ -125,7 +125,7 @@ const splitTextStyleAndMetadata = (brew)=>{ // Set Authors legal names or to empty. brew.legalAuthors = _.pick(metadata, ['legalAuthors']) || ''; - + brew.text = brew.text.slice(index + 6); } if(brew.text.startsWith('```css')) { From 2fe733cc063ad2c5777ca94b7436e31a3995bbd5 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 11 Apr 2026 10:19:37 -0500 Subject: [PATCH 05/25] Add test for custom page settings vs defaults and extended metadata Tweak SplitMetadataAndText based on faulty assumptions for setting defaults. --- server/homebrew.api.spec.js | 73 +++++++++++++++++++++++++++++++++++++ shared/helpers.js | 29 ++++++++------- 2 files changed, 88 insertions(+), 14 deletions(-) diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index 4f151aeac..60f32c940 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -1127,6 +1127,79 @@ brew`); // Text expect(testBrew.text).toEqual('text\n'); }); + + it('extended metadata', async ()=>{ + const testBrew = { + text : '```metadata\n' + + 'title: title\n' + + 'description: description\n' + + 'tags: [ \'tag a\' , \'tag b\' ]\n' + + 'renderer: legacy\n' + + 'theme: 5ePHB\n' + + 'lang: en\n' + + 'bleed:\n' + + ' top: 1.5in\n' + + ' bottom: 1.5in\n' + + ' left: 1.5in\n' + + ' right: 1.5in\n' + + 'safetySpace:\n' + + ' top: 1.25in\n' + + ' bottom: 1.25in\n' + + ' outer: 1.25in\n' + + ' inner: 1.5in\n' + + 'trimSize:\n' + + ' width: 18.5in\n' + + ' height: 111in\n' + + 'columns: 12\n' + + 'columnGutter: 1.125in\n' + + 'license: AELF\n' + + 'legalAuthors: Tom Bombadil\n' + + '\n' + + '```\n' + + '\n' + + '```css\n' + + 'style\n' + + 'style\n' + + 'style\n' + + '```\n' + + '\n' + + 'text\n' + }; + + splitTextStyleAndMetadata(testBrew); + + // Metadata + expect(testBrew.title).toEqual('title'); + expect(testBrew.description).toEqual('description'); + expect(testBrew.renderer).toEqual('legacy'); + expect(testBrew.theme).toEqual('5ePHB'); + expect(testBrew.lang).toEqual('en'); + // Paper Specfications + expect(testBrew.bleed.top).toEqual('1.5in'); + expect(testBrew.bleed.bottom).toEqual('1.5in'); + expect(testBrew.bleed.left).toEqual('1.5in'); + expect(testBrew.bleed.right).toEqual('1.5in'); + + expect(testBrew.safetySpace.top).toEqual('1.25in'); + expect(testBrew.safetySpace.bottom).toEqual('1.25in'); + expect(testBrew.safetySpace.inner).toEqual('1.5in'); + expect(testBrew.safetySpace.outer).toEqual('1.25in'); + + expect(testBrew.trimSize.width).toEqual('18.5in'); + expect(testBrew.trimSize.height).toEqual('111in'); + + expect(testBrew.columns).toEqual(12); + expect(testBrew.columnGutter).toEqual('1.125in'); + + // Extended Metadata + expect(testBrew.license).toEqual('AELF'); + expect(testBrew.legalAuthors).toEqual('Tom Bombadil'); + + // Style + expect(testBrew.style).toEqual('style\nstyle\nstyle\n'); + // Text + expect(testBrew.text).toEqual('text\n'); + }); }); describe('updateBrew', ()=>{ diff --git a/shared/helpers.js b/shared/helpers.js index 2d75ae6b8..18dd127ff 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -95,36 +95,37 @@ const splitTextStyleAndMetadata = (brew)=>{ brew.snippets = yamlSnippetsToText(_.pick(metadata, ['snippets']).snippets || ''); // Copy Required Bleed Space or Use defaults + brew.bleed = { - top : _.pick(metadata, ['bleed']).top || '.125in', - bottom : _.pick(metadata, ['bleed']).bottom || '.125in', - left : _.pick(metadata, ['bleed']).left || '.125in', - right : _.pick(metadata, ['bleed']).right || '.125in', + top : _.pick(metadata, ['bleed']).bleed?.top || '.125in', + bottom : _.pick(metadata, ['bleed']).bleed?.bottom || '.125in', + left : _.pick(metadata, ['bleed']).bleed?.left || '.125in', + right : _.pick(metadata, ['bleed']).bleed?.right || '.125in', }; // Copy Required Safe Space or Use defaults brew.safetySpace = { - top : _.pick(metadata, ['safetySpace']).top || '.25in', - bottom : _.pick(metadata, ['safetySpace']).bottom || '.25in', - outer : _.pick(metadata, ['safetySpace']).outer || '.25in', - inner : _.pick(metadata, ['safetySpace']).inner || '.5in', + top : _.pick(metadata, ['safetySpace']).safetySpace?.top || '.25in', + bottom : _.pick(metadata, ['safetySpace']).safetySpace?.bottom || '.25in', + outer : _.pick(metadata, ['safetySpace']).safetySpace?.outer || '.25in', + inner : _.pick(metadata, ['safetySpace']).safetySpace?.inner || '.5in', }; // Set brew paper trim size or default to US Letter brew.trimSize = { - width : _.pick(metadata, ['trimSize']).width || '8.5in', - height : _.pick(metadata, ['trimSize']).height || '11in', + width : _.pick(metadata, ['trimSize']).trimSize?.width || '8.5in', + height : _.pick(metadata, ['trimSize']).trimSize?.height || '11in', }; // Set Column specs or use defaults - brew.columns = _.pick(metadata, ['columns']) || '2'; - brew.columnGutter = _.pick(metadata, ['columnGutter']) || '.125in'; + brew.columns = metadata?.columns || 2; + brew.columnGutter = metadata?.columnGutter || '.125in'; // Set License or set to Unlicensed - brew.license = _.pick(metadata, ['license']) || 'None'; + brew.license = metadata?.license || 'None'; // Set Authors legal names or to empty. - brew.legalAuthors = _.pick(metadata, ['legalAuthors']) || ''; + brew.legalAuthors = metadata.legalAuthors || ''; brew.text = brew.text.slice(index + 6); } From f0dc66bf2bb38839bda2f5583fb05bfa2aa496ff Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 27 Jun 2026 16:03:10 -0500 Subject: [PATCH 06/25] Bleed should follow safetySpace dimension names ( inner/outer vs left/right ) --- shared/helpers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index b3231e176..b2cf76998 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -99,8 +99,8 @@ const splitTextStyleAndMetadata = (brew)=>{ brew.bleed = { top : _.pick(metadata, ['bleed']).bleed?.top || '.125in', bottom : _.pick(metadata, ['bleed']).bleed?.bottom || '.125in', - left : _.pick(metadata, ['bleed']).bleed?.left || '.125in', - right : _.pick(metadata, ['bleed']).bleed?.right || '.125in', + inner : _.pick(metadata, ['bleed']).bleed?.inner || '.125in', + outer : _.pick(metadata, ['bleed']).bleed?.outer || '.125in', }; // Copy Required Safe Space or Use defaults From 1e2d76885fa5ea16db5be903c49e286385a14d27 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 27 Jun 2026 16:08:07 -0500 Subject: [PATCH 07/25] Missed references fixed --- server/homebrew.api.js | 4 ++-- server/homebrew.api.spec.js | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 3bab31c8a..026ff2eda 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -224,8 +224,8 @@ const api = { metadata.bleed = { top : brew?.bleed?.top || '.125in', bottom : brew?.bleed?.bottom || '.125in', - left : brew?.bleed?.left || '.125in', - right : brew?.bleed?.right || '.125in', + inner : brew?.bleed?.inner || '.125in', + outer : brew?.bleed?.outer || '.125in', }; // Copy Required Safe Space or Use defaults diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index 60f32c940..02ebbf0c2 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -404,8 +404,8 @@ theme: phb bleed: top: .125in bottom: .125in - left: .125in - right: .125in + outer: .125in + inner: .125in safetySpace: top: .25in bottom: .25in @@ -447,8 +447,8 @@ theme: phb bleed: top: .125in bottom: .125in - left: .125in - right: .125in + outer: .125in + inner: .125in safetySpace: top: .25in bottom: .25in @@ -1140,8 +1140,8 @@ brew`); 'bleed:\n' + ' top: 1.5in\n' + ' bottom: 1.5in\n' + - ' left: 1.5in\n' + - ' right: 1.5in\n' + + ' outer: 1.5in\n' + + ' inner: 1.5in\n' + 'safetySpace:\n' + ' top: 1.25in\n' + ' bottom: 1.25in\n' + @@ -1177,8 +1177,8 @@ brew`); // Paper Specfications expect(testBrew.bleed.top).toEqual('1.5in'); expect(testBrew.bleed.bottom).toEqual('1.5in'); - expect(testBrew.bleed.left).toEqual('1.5in'); - expect(testBrew.bleed.right).toEqual('1.5in'); + expect(testBrew.bleed.inner).toEqual('1.5in'); + expect(testBrew.bleed.outer).toEqual('1.5in'); expect(testBrew.safetySpace.top).toEqual('1.25in'); expect(testBrew.safetySpace.bottom).toEqual('1.25in'); From f25c5a182d53b9e348f546b0d6afd4f13d5c7ef3 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 4 Jul 2026 15:24:01 -0500 Subject: [PATCH 08/25] Fix Tests, YAML order --- server/homebrew.api.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index 02ebbf0c2..8d0bfa9fc 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -404,8 +404,8 @@ theme: phb bleed: top: .125in bottom: .125in - outer: .125in inner: .125in + outer: .125in safetySpace: top: .25in bottom: .25in @@ -447,8 +447,8 @@ theme: phb bleed: top: .125in bottom: .125in - outer: .125in inner: .125in + outer: .125in safetySpace: top: .25in bottom: .25in From 7cc3611e4c6f90f5e409e41ae98c944160dda862 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 17 Jul 2026 20:01:29 -0500 Subject: [PATCH 09/25] Directly access metadata attributes instead of _.picking the object. --- shared/helpers.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index d074b75ac..ab78caf71 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -97,24 +97,24 @@ const splitTextStyleAndMetadata = (brew)=>{ // Copy Required Bleed Space or Use defaults brew.bleed = { - top : _.pick(metadata, ['bleed']).bleed?.top || '.125in', - bottom : _.pick(metadata, ['bleed']).bleed?.bottom || '.125in', - inner : _.pick(metadata, ['bleed']).bleed?.inner || '.125in', - outer : _.pick(metadata, ['bleed']).bleed?.outer || '.125in', + top : metadata.bleed?.top || '.125in', + bottom : metadata.bleed?.bottom || '.125in', + inner : metadata.bleed?.inner || '.125in', + outer : metadata.bleed?.outer || '.125in', }; // Copy Required Safe Space or Use defaults brew.safetySpace = { - top : _.pick(metadata, ['safetySpace']).safetySpace?.top || '.25in', - bottom : _.pick(metadata, ['safetySpace']).safetySpace?.bottom || '.25in', - outer : _.pick(metadata, ['safetySpace']).safetySpace?.outer || '.25in', - inner : _.pick(metadata, ['safetySpace']).safetySpace?.inner || '.5in', + top : metadata.safetySpace?.top || '.25in', + bottom : metadata.safetySpace?.bottom || '.25in', + outer : metadata.safetySpace?.outer || '.25in', + inner : metadata.safetySpace?.inner || '.5in', }; // Set brew paper trim size or default to US Letter brew.trimSize = { - width : _.pick(metadata, ['trimSize']).trimSize?.width || '8.5in', - height : _.pick(metadata, ['trimSize']).trimSize?.height || '11in', + width : metadata.trimSize?.width || '8.5in', + height : metadata.trimSize?.height || '11in', }; // Set Column specs or use defaults From 6d40525286543c11c6ea99c81ebba9299ebba5ca Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 17 Jul 2026 20:09:26 -0500 Subject: [PATCH 10/25] Remove relocate failover defaults to brewDefaults --- server/brewDefaults.js | 23 +++++++++++++++++++++-- shared/helpers.js | 28 ++++++++++++++-------------- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/server/brewDefaults.js b/server/brewDefaults.js index 501914735..fecdfa052 100644 --- a/server/brewDefaults.js +++ b/server/brewDefaults.js @@ -20,8 +20,27 @@ const DEFAULT_BREW = { published : false, pageCount : 1, gDrive : false, - trashed : false - + trashed : false, + bleed : { + top : '.125in', + bottom : '.125in', + inner : '.125in', + outer : '.125in' + }, + safetySpace : { + top : '.25in', + bottom : '.25in', + outer : '.25in', + inner : '.5in', + }, + trimSize : { + width : '8.5in', + height : '11in', + }, + columns : '2', + columnGutter : '.125in', + license : 'None', + legalAuthors : '' }; // Default values for older brews with missing properties // e.g., missing "renderer" is assumed to be "legacy" diff --git a/shared/helpers.js b/shared/helpers.js index ab78caf71..63d348bb5 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -97,35 +97,35 @@ const splitTextStyleAndMetadata = (brew)=>{ // Copy Required Bleed Space or Use defaults brew.bleed = { - top : metadata.bleed?.top || '.125in', - bottom : metadata.bleed?.bottom || '.125in', - inner : metadata.bleed?.inner || '.125in', - outer : metadata.bleed?.outer || '.125in', + top : metadata.bleed?.top, + bottom : metadata.bleed?.bottom, + inner : metadata.bleed?.inner, + outer : metadata.bleed?.outer, }; // Copy Required Safe Space or Use defaults brew.safetySpace = { - top : metadata.safetySpace?.top || '.25in', - bottom : metadata.safetySpace?.bottom || '.25in', - outer : metadata.safetySpace?.outer || '.25in', - inner : metadata.safetySpace?.inner || '.5in', + top : metadata.safetySpace?.top, + bottom : metadata.safetySpace?.bottom, + outer : metadata.safetySpace?.outer, + inner : metadata.safetySpace?.inner, }; // Set brew paper trim size or default to US Letter brew.trimSize = { - width : metadata.trimSize?.width || '8.5in', - height : metadata.trimSize?.height || '11in', + width : metadata.trimSize?.width, + height : metadata.trimSize?.height, }; // Set Column specs or use defaults - brew.columns = metadata?.columns || 2; - brew.columnGutter = metadata?.columnGutter || '.125in'; + brew.columns = metadata?.columns; + brew.columnGutter = metadata?.columnGutter; // Set License or set to Unlicensed - brew.license = metadata?.license || 'None'; + brew.license = metadata?.license; // Set Authors legal names or to empty. - brew.legalAuthors = metadata.legalAuthors || ''; + brew.legalAuthors = metadata.legalAuthors; brew.text = brew.text.slice(index + 6); } From 4f7c7e1c4b1be3cb21ec9d96ce4e67c1e3836788 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 17 Jul 2026 20:15:06 -0500 Subject: [PATCH 11/25] Remove extra failover values. --- server/homebrew.api.js | 38 +++++++------------------------------- 1 file changed, 7 insertions(+), 31 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 026ff2eda..1995dc07f 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -220,37 +220,13 @@ const api = { const metadata = _.pick(brew, ['title', 'description', 'tags', 'renderer', 'theme']); const snippetsArray = brewSnippetsToJSON('brew_snippets', brew.snippets, null, false).snippets; metadata.snippets = snippetsArray.length > 0 ? snippetsArray : undefined; - - metadata.bleed = { - top : brew?.bleed?.top || '.125in', - bottom : brew?.bleed?.bottom || '.125in', - inner : brew?.bleed?.inner || '.125in', - outer : brew?.bleed?.outer || '.125in', - }; - - // Copy Required Safe Space or Use defaults - metadata.safetySpace = { - top : brew?.safetySpace?.top || '.25in', - bottom : brew?.safetySpace?.bottom || '.25in', - outer : brew?.safetySpace?.outer || '.25in', - inner : brew?.safetySpace?.inner || '.5in', - }; - - // Set brew paper trim size or default to US Letter - metadata.trimSize = { - width : brew?.trimSize?.width || '8.5in', - height : brew?.trimSize?.height || '11in', - }; - - // Set Column specs or use defaults - metadata.columns = brew?.columns || '2'; - metadata.columnGutter = brew?.columnGutter || '.125in'; - - // Set License or set to Unlicensed - metadata.license = brew?.license || 'None'; - - // Set Authors legal names or to empty. - metadata.legalAuthors = brew?.legalAuthors || ''; + metadata.bleed = { top: brew?.bleed?.top, bottom: brew?.bleed?.bottom, inner: brew?.bleed?.inner, outer: brew?.bleed?.outer }; + metadata.safetySpace = { top: brew?.safetySpace?.top, bottom: brew?.safetySpace?.bottom, outer: brew?.safetySpace?.outer, inner: brew?.safetySpace?.inner }; + metadata.trimSize = { width: brew?.trimSize?.width, height: brew?.trimSize?.height }; + metadata.columns = brew?.columns; + metadata.columnGutter = brew?.columnGutter; + metadata.license = brew?.license; + metadata.legalAuthors = brew?.legalAuthors; text = `\`\`\`metadata\n` + `${yaml.dump(metadata)}\n` + From 1472e39bc8b4704b41747296bb151135b7fbbb11 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 17 Jul 2026 20:19:16 -0500 Subject: [PATCH 12/25] Reduce unnecessary linespacing and comments. --- server/brewDefaults.js | 57 ++++++++++++++++-------------------------- shared/helpers.js | 31 +++-------------------- 2 files changed, 25 insertions(+), 63 deletions(-) diff --git a/server/brewDefaults.js b/server/brewDefaults.js index fecdfa052..abf39a86c 100644 --- a/server/brewDefaults.js +++ b/server/brewDefaults.js @@ -2,41 +2,28 @@ import _ from 'lodash'; // Default properties for newly-created brews const DEFAULT_BREW = { - title : '', - text : '', - style : undefined, - description : '', - editId : undefined, - shareId : undefined, - createdAt : undefined, - updatedAt : undefined, - renderer : 'V3', - theme : '5ePHB', - authors : [], - tags : [], - lang : 'en', - thumbnail : '', - views : 0, - published : false, - pageCount : 1, - gDrive : false, - trashed : false, - bleed : { - top : '.125in', - bottom : '.125in', - inner : '.125in', - outer : '.125in' - }, - safetySpace : { - top : '.25in', - bottom : '.25in', - outer : '.25in', - inner : '.5in', - }, - trimSize : { - width : '8.5in', - height : '11in', - }, + title : '', + text : '', + style : undefined, + description : '', + editId : undefined, + shareId : undefined, + createdAt : undefined, + updatedAt : undefined, + renderer : 'V3', + theme : '5ePHB', + authors : [], + tags : [], + lang : 'en', + thumbnail : '', + views : 0, + published : false, + pageCount : 1, + gDrive : false, + trashed : false, + bleed : { top: '.125in', bottom: '.125in', inner: '.125in', outer: '.125in' }, + safetySpace : { top: '.25in', bottom: '.25in', outer: '.25in', inner: '.5in' }, + trimSize : { width: '8.5in', height: '11in' }, columns : '2', columnGutter : '.125in', license : 'None', diff --git a/shared/helpers.js b/shared/helpers.js index 63d348bb5..5800dd563 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -94,37 +94,12 @@ const splitTextStyleAndMetadata = (brew)=>{ Object.assign(brew, _.pick(metadata, ['title', 'description', 'renderer', 'theme', 'lang'])); brew.snippets = yamlSnippetsToText(_.pick(metadata, ['snippets']).snippets || ''); - // Copy Required Bleed Space or Use defaults - - brew.bleed = { - top : metadata.bleed?.top, - bottom : metadata.bleed?.bottom, - inner : metadata.bleed?.inner, - outer : metadata.bleed?.outer, - }; - - // Copy Required Safe Space or Use defaults - brew.safetySpace = { - top : metadata.safetySpace?.top, - bottom : metadata.safetySpace?.bottom, - outer : metadata.safetySpace?.outer, - inner : metadata.safetySpace?.inner, - }; - - // Set brew paper trim size or default to US Letter - brew.trimSize = { - width : metadata.trimSize?.width, - height : metadata.trimSize?.height, - }; - - // Set Column specs or use defaults + brew.bleed = { top: metadata.bleed?.top, bottom: metadata.bleed?.bottom, inner: metadata.bleed?.inner, outer: metadata.bleed?.outer }; + brew.safetySpace = { top: metadata.safetySpace?.top, bottom: metadata.safetySpace?.bottom, outer: metadata.safetySpace?.outer, inner: metadata.safetySpace?.inner }; + brew.trimSize = { width: metadata.trimSize?.width, height: metadata.trimSize?.height }; brew.columns = metadata?.columns; brew.columnGutter = metadata?.columnGutter; - - // Set License or set to Unlicensed brew.license = metadata?.license; - - // Set Authors legal names or to empty. brew.legalAuthors = metadata.legalAuthors; brew.text = brew.text.slice(index + 6); From ed2b24649439b861a04febc2415d724cbadb14e6 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 7 Aug 2026 10:52:14 -0500 Subject: [PATCH 13/25] Change bleed to bleedSize --- server/brewDefaults.js | 2 +- server/homebrew.api.js | 2 +- server/homebrew.api.spec.js | 14 +++++++------- shared/helpers.js | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/server/brewDefaults.js b/server/brewDefaults.js index abf39a86c..cb3114467 100644 --- a/server/brewDefaults.js +++ b/server/brewDefaults.js @@ -21,7 +21,7 @@ const DEFAULT_BREW = { pageCount : 1, gDrive : false, trashed : false, - bleed : { top: '.125in', bottom: '.125in', inner: '.125in', outer: '.125in' }, + bleedSize : { top: '.125in', bottom: '.125in', inner: '.125in', outer: '.125in' }, safetySpace : { top: '.25in', bottom: '.25in', outer: '.25in', inner: '.5in' }, trimSize : { width: '8.5in', height: '11in' }, columns : '2', diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 1995dc07f..b7b73b8dc 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -220,7 +220,7 @@ const api = { const metadata = _.pick(brew, ['title', 'description', 'tags', 'renderer', 'theme']); const snippetsArray = brewSnippetsToJSON('brew_snippets', brew.snippets, null, false).snippets; metadata.snippets = snippetsArray.length > 0 ? snippetsArray : undefined; - metadata.bleed = { top: brew?.bleed?.top, bottom: brew?.bleed?.bottom, inner: brew?.bleed?.inner, outer: brew?.bleed?.outer }; + metadata.bleedSize = { top: brew?.bleedSize?.top, bottom: brew?.bleedSize?.bottom, inner: brew?.bleedSize?.inner, outer: brew?.bleedSize?.outer }; metadata.safetySpace = { top: brew?.safetySpace?.top, bottom: brew?.safetySpace?.bottom, outer: brew?.safetySpace?.outer, inner: brew?.safetySpace?.inner }; metadata.trimSize = { width: brew?.trimSize?.width, height: brew?.trimSize?.height }; metadata.columns = brew?.columns; diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index 8d0bfa9fc..1b5f69687 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -401,7 +401,7 @@ tags: - fun renderer: v3 theme: phb -bleed: +bleedSize: top: .125in bottom: .125in inner: .125in @@ -444,7 +444,7 @@ tags: - fun renderer: v3 theme: phb -bleed: +bleedSize: top: .125in bottom: .125in inner: .125in @@ -1137,7 +1137,7 @@ brew`); 'renderer: legacy\n' + 'theme: 5ePHB\n' + 'lang: en\n' + - 'bleed:\n' + + 'bleedSize:\n' + ' top: 1.5in\n' + ' bottom: 1.5in\n' + ' outer: 1.5in\n' + @@ -1175,10 +1175,10 @@ brew`); expect(testBrew.theme).toEqual('5ePHB'); expect(testBrew.lang).toEqual('en'); // Paper Specfications - expect(testBrew.bleed.top).toEqual('1.5in'); - expect(testBrew.bleed.bottom).toEqual('1.5in'); - expect(testBrew.bleed.inner).toEqual('1.5in'); - expect(testBrew.bleed.outer).toEqual('1.5in'); + expect(testBrew.bleedSize.top).toEqual('1.5in'); + expect(testBrew.bleedSize.bottom).toEqual('1.5in'); + expect(testBrew.bleedSize.inner).toEqual('1.5in'); + expect(testBrew.bleedSize.outer).toEqual('1.5in'); expect(testBrew.safetySpace.top).toEqual('1.25in'); expect(testBrew.safetySpace.bottom).toEqual('1.25in'); diff --git a/shared/helpers.js b/shared/helpers.js index 5800dd563..34c0e74bf 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -94,7 +94,7 @@ const splitTextStyleAndMetadata = (brew)=>{ Object.assign(brew, _.pick(metadata, ['title', 'description', 'renderer', 'theme', 'lang'])); brew.snippets = yamlSnippetsToText(_.pick(metadata, ['snippets']).snippets || ''); - brew.bleed = { top: metadata.bleed?.top, bottom: metadata.bleed?.bottom, inner: metadata.bleed?.inner, outer: metadata.bleed?.outer }; + brew.bleedSize = { top: metadata.bleedSize?.top, bottom: metadata.bleedSize?.bottom, inner: metadata.bleedSize?.inner, outer: metadata.bleedSize?.outer }; brew.safetySpace = { top: metadata.safetySpace?.top, bottom: metadata.safetySpace?.bottom, outer: metadata.safetySpace?.outer, inner: metadata.safetySpace?.inner }; brew.trimSize = { width: metadata.trimSize?.width, height: metadata.trimSize?.height }; brew.columns = metadata?.columns; From 02c227b304693ec36797be23a2ebbce713ffe179 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 7 Aug 2026 22:10:08 -0500 Subject: [PATCH 14/25] Fix tests to reflect moving defaults --- server/homebrew.api.spec.js | 106 +++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 3 deletions(-) diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index 1b5f69687..cae613c6c 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -360,7 +360,27 @@ describe('Tests for api', ()=>{ style : undefined, trashed : false, updatedAt : undefined, - views : 0 + views : 0, + bleedSize: { + top: '.125in', + bottom: '.125in', + inner: '.125in', + outer: '.125in', + }, + columns: '2', + columnGutter: '.125in', + legalAuthors: '', + license: 'None', + safetySpace: { + top: '.25in', + bottom: '.25in', + outer: '.25in', + inner: '.5in', + }, + trimSize: { + width: '8.5in', + height: '11in', + }, }); expect(next).toHaveBeenCalled(); expect(api.getId).toHaveBeenCalledWith(req); @@ -390,7 +410,27 @@ describe('Tests for api', ()=>{ tags : ['something', 'fun'], renderer : 'v3', theme : 'phb', - googleId : '12345' + googleId : '12345', + bleedSize: { + top: '.125in', + bottom: '.125in', + inner: '.125in', + outer: '.125in', + }, + columns: '2', + columnGutter: '.125in', + legalAuthors: '', + license: 'None', + safetySpace: { + top: '.25in', + bottom: '.25in', + outer: '.25in', + inner: '.5in', + }, + trimSize: { + width: '8.5in', + height: '11in', + }, }); expect(result).toEqual(`\`\`\`metadata @@ -434,6 +474,26 @@ brew`); renderer : 'v3', theme : 'phb', googleId : '12345', + bleedSize: { + top: '.125in', + bottom: '.125in', + inner: '.125in', + outer: '.125in', + }, + columns: '2', + columnGutter: '.125in', + legalAuthors: '', + license: 'None', + safetySpace: { + top: '.25in', + bottom: '.25in', + outer: '.25in', + inner: '.5in', + }, + trimSize: { + width: '8.5in', + height: '11in', + }, }); expect(result).toEqual(`\`\`\`metadata @@ -591,7 +651,27 @@ brew`); title : 'asdf', trashed : false, updatedAt : undefined, - views : 0 + views : 0, + bleedSize: { + top: '.125in', + bottom: '.125in', + inner: '.125in', + outer: '.125in', + }, + columns: '2', + columnGutter: '.125in', + legalAuthors: '', + license: 'None', + safetySpace: { + top: '.25in', + bottom: '.25in', + outer: '.25in', + inner: '.5in', + }, + trimSize: { + width: '8.5in', + height: '11in', + }, }); }); @@ -631,6 +711,14 @@ brew`); expect(res.send).toHaveBeenCalledWith({ _id : '1', authors : ['test user'], + bleedSize: { + top: '.125in', + bottom: '.125in', + inner: '.125in', + outer: '.125in', + }, + columns: '2', + columnGutter: '.125in', createdAt : undefined, description : '', editId : expect.any(String), @@ -640,6 +728,14 @@ brew`); renderer : 'V3', lang : 'en', shareId : expect.any(String), + legalAuthors: '', + license: 'None', + safetySpace: { + top: '.25in', + bottom: '.25in', + outer: '.25in', + inner: '.5in', + }, googleId : expect.any(String), style : undefined, tags : [], @@ -649,6 +745,10 @@ brew`); thumbnail : '', title : 'asdf', trashed : false, + trimSize: { + width: '8.5in', + height: '11in', + }, updatedAt : undefined, views : 0 }); From 002e03b572faa76b01b78a1df26fe3857068588e Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 21 Aug 2026 11:59:38 -0500 Subject: [PATCH 15/25] Convert literal member copies to spread operators for simplicity and future-proofing --- package-lock.json | 16 ---------------- shared/helpers.js | 6 +++--- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 21e46a992..a4701ccac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10862,22 +10862,6 @@ "url": "https://opencollective.com/mongoose" } }, - "node_modules/mongoose/node_modules/gcp-metadata": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.1.tgz", - "integrity": "sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==", - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "gaxios": "^7.0.0", - "google-logging-utils": "^1.0.0", - "json-bigint": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/mongoose/node_modules/mongodb": { "version": "7.5.0", "license": "Apache-2.0", diff --git a/shared/helpers.js b/shared/helpers.js index 91983882c..667d7b6b1 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -94,9 +94,9 @@ const splitTextStyleAndMetadata = (brew)=>{ Object.assign(brew, _.pick(metadata, ['title', 'description', 'renderer', 'theme', 'lang'])); brew.snippets = yamlSnippetsToText(_.pick(metadata, ['snippets']).snippets || ''); - brew.bleedSize = { top: metadata.bleedSize?.top, bottom: metadata.bleedSize?.bottom, inner: metadata.bleedSize?.inner, outer: metadata.bleedSize?.outer }; - brew.safetySpace = { top: metadata.safetySpace?.top, bottom: metadata.safetySpace?.bottom, outer: metadata.safetySpace?.outer, inner: metadata.safetySpace?.inner }; - brew.trimSize = { width: metadata.trimSize?.width, height: metadata.trimSize?.height }; + brew.bleedSize = { ...metadata.bleedSize }; + brew.safetySpace = { ...metadata.safetySpace }; + brew.trimSize = { ...metadata.trimSize }; brew.columns = metadata?.columns; brew.columnGutter = metadata?.columnGutter; brew.license = metadata?.license; From 76412d11a0cf2598e9475a96ff2dae32e6905453 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:07:00 +0000 Subject: [PATCH 16/25] Bump the prod-dependencies group across 1 directory with 4 updates Bumps the prod-dependencies group with 4 updates in the / directory: [@codemirror/commands](https://github.com/codemirror/commands), [@codemirror/view](https://github.com/codemirror/view), [@oddbird/css-anchor-positioning](https://github.com/oddbird/css-anchor-positioning) and [mongoose](https://github.com/Automattic/mongoose). Updates `@codemirror/commands` from 6.10.4 to 6.11.0 - [Changelog](https://github.com/codemirror/commands/blob/main/CHANGELOG.md) - [Commits](https://github.com/codemirror/commands/commits) Updates `@codemirror/view` from 6.43.8 to 6.43.9 - [Changelog](https://github.com/codemirror/view/blob/main/CHANGELOG.md) - [Commits](https://github.com/codemirror/view/commits) Updates `@oddbird/css-anchor-positioning` from 0.10.1 to 0.10.2 - [Release notes](https://github.com/oddbird/css-anchor-positioning/releases) - [Changelog](https://github.com/oddbird/css-anchor-positioning/blob/main/CHANGELOG.md) - [Commits](https://github.com/oddbird/css-anchor-positioning/compare/v0.10.1...v0.10.2) Updates `mongoose` from 9.9.2 to 9.9.3 - [Release notes](https://github.com/Automattic/mongoose/releases) - [Changelog](https://github.com/Automattic/mongoose/blob/master/CHANGELOG.md) - [Commits](https://github.com/Automattic/mongoose/compare/9.9.2...9.9.3) --- updated-dependencies: - dependency-name: "@codemirror/commands" dependency-version: 6.11.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-dependencies - dependency-name: "@codemirror/view" dependency-version: 6.43.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: "@oddbird/css-anchor-positioning" dependency-version: 0.10.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: mongoose dependency-version: 9.9.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 50 +++++++++++++++++++++-------------------------- package.json | 8 ++++---- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index a4701ccac..a61a0c7c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@babel/preset-react": "^8.0.1", "@babel/runtime": "^8.0.0", "@codemirror/autocomplete": "^6.20.3", - "@codemirror/commands": "^6.10.3", + "@codemirror/commands": "^6.11.0", "@codemirror/highlight": "^0.19.8", "@codemirror/lang-css": "^6.3.1", "@codemirror/lang-javascript": "^6.2.5", @@ -25,11 +25,11 @@ "@codemirror/language-data": "^6.5.2", "@codemirror/search": "^6.6.0", "@codemirror/state": "^6.6.0", - "@codemirror/view": "^6.43.8", + "@codemirror/view": "^6.43.9", "@dmsnell/diff-match-patch": "^1.1.0", "@googleapis/drive": "^20.2.0", "@lezer/highlight": "^1.2.3", - "@oddbird/css-anchor-positioning": "^0.10.1", + "@oddbird/css-anchor-positioning": "^0.10.2", "@sanity/diff-match-patch": "^3.2.0", "@vitejs/plugin-react": "^6.0.5", "body-parser": "^2.3.0", @@ -64,7 +64,7 @@ "marked-variables": "^1.0.5", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^9.9.2", + "mongoose": "^9.9.3", "nanoid": "6.0.1", "nconf": "^0.13.0", "node": "^26.7.0", @@ -2632,7 +2632,9 @@ } }, "node_modules/@codemirror/commands": { - "version": "6.10.4", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.11.0.tgz", + "integrity": "sha512-/K4Rl5BN0OtTiPWmJCdqODu38XnDMsDxKY5rgrPnCkutPTJf2wVbkoixLfealF5Kwse/s8P8M5jAiURiwSwnFA==", "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", @@ -3023,7 +3025,9 @@ "license": "MIT" }, "node_modules/@codemirror/view": { - "version": "6.43.8", + "version": "6.43.9", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.9.tgz", + "integrity": "sha512-sTuUzTpPMFebRhg6dawChoKKgndIwfjmJgKVxBefPElcU2NwQ6AFroupk0SFqEerQyZOGRfDNnSN8Dw/lMAsXw==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.7.0", @@ -4272,29 +4276,15 @@ } }, "node_modules/@oddbird/css-anchor-positioning": { - "version": "0.10.1", + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@oddbird/css-anchor-positioning/-/css-anchor-positioning-0.10.2.tgz", + "integrity": "sha512-sk+1BpF5V+CfFm0CUbyTRxtwsUjcuP6Nz5tOzC6Bh500gBRdJJZJAeQ/3DYKPd9hGge5cqyVfUNl8zpWJVma/Q==", "license": "BSD-3-Clause", "dependencies": { - "@floating-ui/dom": "^1.7.6", - "@types/css-tree": "^2.3.11", + "@floating-ui/dom": "^1.8.0", + "@types/css-tree": "^3.2.0", "css-tree": "^3.2.1", - "nanoid": "^5.1.16" - } - }, - "node_modules/@oddbird/css-anchor-positioning/node_modules/nanoid": { - "version": "5.1.16", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.js" - }, - "engines": { - "node": "^18 || >=20" + "nanoid": "^6.0.1" } }, "node_modules/@oxc-project/types": { @@ -4665,7 +4655,9 @@ } }, "node_modules/@types/css-tree": { - "version": "2.3.11", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/css-tree/-/css-tree-3.2.0.tgz", + "integrity": "sha512-J5KXmk6BFIepOT7280FdFyNs4c5fh0Uee0otjKEvzchrRs38Ii9qminqc4ds0L19X8Zd/rT+brR9jkBthygjWw==", "license": "MIT" }, "node_modules/@types/gensync": { @@ -10843,7 +10835,9 @@ } }, "node_modules/mongoose": { - "version": "9.9.2", + "version": "9.9.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.9.3.tgz", + "integrity": "sha512-9dQaKct05LNgVkunDzFPZsGGBhinobl3Qc5B5KYJkLNG5lBLgqESUEItl1EUIkVaCwZJGBgTuvJiFAjjIATCiw==", "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", diff --git a/package.json b/package.json index cf1625ad2..f1c3f4894 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "@babel/preset-react": "^8.0.1", "@babel/runtime": "^8.0.0", "@codemirror/autocomplete": "^6.20.3", - "@codemirror/commands": "^6.10.3", + "@codemirror/commands": "^6.11.0", "@codemirror/highlight": "^0.19.8", "@codemirror/lang-css": "^6.3.1", "@codemirror/lang-javascript": "^6.2.5", @@ -101,11 +101,11 @@ "@codemirror/language-data": "^6.5.2", "@codemirror/search": "^6.6.0", "@codemirror/state": "^6.6.0", - "@codemirror/view": "^6.43.8", + "@codemirror/view": "^6.43.9", "@dmsnell/diff-match-patch": "^1.1.0", "@googleapis/drive": "^20.2.0", "@lezer/highlight": "^1.2.3", - "@oddbird/css-anchor-positioning": "^0.10.1", + "@oddbird/css-anchor-positioning": "^0.10.2", "@sanity/diff-match-patch": "^3.2.0", "@vitejs/plugin-react": "^6.0.5", "body-parser": "^2.3.0", @@ -140,7 +140,7 @@ "marked-variables": "^1.0.5", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^9.9.2", + "mongoose": "^9.9.3", "nanoid": "6.0.1", "nconf": "^0.13.0", "node": "^26.7.0", From a3e43ebe4eb9a330ccd1e1189e541b854c6b012f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2026 20:21:07 +0000 Subject: [PATCH 17/25] Bump @googleapis/drive from 20.2.0 to 21.0.0 Bumps [@googleapis/drive](https://github.com/googleapis/google-api-nodejs-client) from 20.2.0 to 21.0.0. - [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases) - [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/20.2.0...run-v21.0.0) --- updated-dependencies: - dependency-name: "@googleapis/drive" dependency-version: 21.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 6 ++++-- package.json | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a61a0c7c6..14b0b4eba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "@codemirror/state": "^6.6.0", "@codemirror/view": "^6.43.9", "@dmsnell/diff-match-patch": "^1.1.0", - "@googleapis/drive": "^20.2.0", + "@googleapis/drive": "^21.0.0", "@lezer/highlight": "^1.2.3", "@oddbird/css-anchor-positioning": "^0.10.2", "@sanity/diff-match-patch": "^3.2.0", @@ -3423,7 +3423,9 @@ "license": "MIT" }, "node_modules/@googleapis/drive": { - "version": "20.2.0", + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-21.0.0.tgz", + "integrity": "sha512-vebQpAqn+FZcmDWh1TEQTcYdkKftcSaHc7kO2ZxrINBb+ZMcFu1UybD3+maeqdLs60HrRDYwyzJ6TgZ7/Vn2Rg==", "license": "Apache-2.0", "dependencies": { "googleapis-common": "^8.0.0" diff --git a/package.json b/package.json index f1c3f4894..29318f4ee 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "@codemirror/state": "^6.6.0", "@codemirror/view": "^6.43.9", "@dmsnell/diff-match-patch": "^1.1.0", - "@googleapis/drive": "^20.2.0", + "@googleapis/drive": "^21.0.0", "@lezer/highlight": "^1.2.3", "@oddbird/css-anchor-positioning": "^0.10.2", "@sanity/diff-match-patch": "^3.2.0", From f53ed8c8e7ea120e25cc22971923bc3de402fb5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sun, 23 Aug 2026 19:52:11 +0200 Subject: [PATCH 18/25] this is mostly working --- client/homebrew/editor/editor.jsx | 635 +++++++++++++++++------------- 1 file changed, 354 insertions(+), 281 deletions(-) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index f500bebeb..66e6aaadf 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -1,29 +1,22 @@ -/*eslint max-lines: ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}]*/ -import './editor.less'; -import React from 'react'; -import createReactClass from 'create-react-class'; -import _ from 'lodash'; -import dedent from 'dedent'; +import "./editor.less"; +import React, { useState, useRef, useEffect, forwardRef, useImperativeHandle } from "react"; +import dedent from "dedent"; -import CodeEditor from '@components/codeEditor/codeEditor.jsx'; -import SnippetBar from './snippetbar/snippetbar.jsx'; -import MetadataEditor from './metadataEditor/metadataEditor.jsx'; +import CodeEditor from "@components/codeEditor/codeEditor.jsx"; +import SnippetBar from "./snippetbar/snippetbar.jsx"; +import MetadataEditor from "./metadataEditor/metadataEditor.jsx"; -const EDITOR_THEME_KEY = 'HB_editor_theme'; +const EDITOR_THEME_KEY = "HB_editor_theme"; -import defaultCM5Theme from '@themes/codeMirror/default.js'; -import darkbrewery from '@themes/codeMirror/darkbrewery.js'; -import cm5Themes from 'codemirror-5-themes'; +import defaultCM5Theme from "@themes/codeMirror/default.js"; +import darkbrewery from "@themes/codeMirror/darkbrewery.js"; +import cm5Themes from "codemirror-5-themes"; const themes = { default: defaultCM5Theme, ...cm5Themes, darkbrewery }; const EditorThemes = Object.entries(themes) - .filter(([name, value])=>Array.isArray(value) && - !name.endsWith('Init') && - !name.endsWith('Style') - ) - .map(([name])=>name); - + .filter(([name, value]) => Array.isArray(value) && !name.endsWith("Init") && !name.endsWith("Style")) + .map(([name]) => name); //const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m; //const SNIPPETBREAK_REGEX_V3 = /^\\snippet\ .*$/; @@ -45,306 +38,386 @@ const DEFAULT_SNIPPET_TEXT = dedent` let isJumping = false; let jumpSource = null; -const Editor = createReactClass({ - displayName : 'Editor', - getDefaultProps : function() { - return { - brew : { - text : '', - style : '' - }, +const Editor = forwardRef( + ( + { + brew = {}, - onBrewChange : ()=>{}, - reportError : ()=>{}, + onBrewChange = () => {}, + reportError = () => {}, - onCursorPageChange : ()=>{}, - onViewPageChange : ()=>{}, + onCursorPageChange = () => {}, + onViewPageChange = () => {}, - editorTheme : 'default', - renderer : 'legacy', + editorTheme = "default", + renderer = "legacy", - currentEditorCursorPageNum : 1, - currentEditorViewPageNum : 1, - currentBrewRendererPageNum : 1, + moveBrew, + moveSource, + liveScroll, + + setMoveArrows, + updateBrew, + showEditButtons, + themeBundle, + userThemes, + + currentEditorCursorPageNum = 1, + currentEditorViewPageNum = 1, + currentBrewRendererPageNum = 1, + }, + ref, + ) => { + const [currentEditorTheme, setEditorTheme] = useState(editorTheme); + const [view, setView] = useState("text"); // 'text', 'style', 'meta', 'snippet' + const [snippetBarHeight, setSnippetBarHeight] = useState(26); + + const editor = useRef(null); + const codeEditor = useRef(null); + const throttleBrewMove = useRef(null); + + const isText = () => { + return view === "text"; }; - }, - getInitialState : function() { - return { - editorTheme : this.props.editorTheme, - view : 'text', //'text', 'style', 'meta', 'snippet' - snippetBarHeight : 26, + const isStyle = () => { + return view === "style"; + }; + const isMeta = () => { + return view === "meta"; + }; + const isSnip = () => { + return view === "snippet"; }; - }, - editor : React.createRef(null), - codeEditor : React.createRef(null), + //componentDidMount equivalent + useEffect(() => { + const brewRenderer = document.getElementById("BrewRenderer"); + brewRenderer.onload = () => brewRenderer.contentDocument?.addEventListener("keydown", handleControlKeys); + document.addEventListener("keydown", handleControlKeys); - isText : function() {return this.state.view == 'text';}, - isStyle : function() {return this.state.view == 'style';}, - isMeta : function() {return this.state.view == 'meta';}, - isSnip : function() {return this.state.view == 'snippet';}, - - componentDidMount : function() { - - const brewRenderer = document.getElementById('BrewRenderer'); - brewRenderer.onload = ()=>brewRenderer.contentDocument?.addEventListener('keydown', this.handleControlKeys); - document.addEventListener('keydown', this.handleControlKeys); - - const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY); - if(editorTheme && EditorThemes.includes(editorTheme)) { - this.setState({ editorTheme }); - } else { - this.setState({ editorTheme: 'default' }); - } - const snippetBar = document.querySelector('.editor > .snippetBar'); - if(!snippetBar) return; - - this.resizeObserver = new ResizeObserver((entries)=>{ - const height = document.querySelector('.editor > .snippetBar').offsetHeight; - this.setState({ snippetBarHeight: height }); - }); - - this.resizeObserver.observe(snippetBar); - }, - - componentDidUpdate : function(prevProps, prevState, snapshot) { - - if(prevProps.moveBrew !== this.props.moveBrew) - this.brewJump(); - - if(prevProps.moveSource !== this.props.moveSource) - this.sourceJump(); - - if(this.props.liveScroll) { - if(prevProps.currentBrewRendererPageNum !== this.props.currentBrewRendererPageNum) { - this.sourceJump(this.props.currentBrewRendererPageNum, false); - } else if(prevProps.currentEditorViewPageNum !== this.props.currentEditorViewPageNum) { - this.brewJump(this.props.currentEditorViewPageNum, false); - } else if(prevProps.currentEditorCursorPageNum !== this.props.currentEditorCursorPageNum) { - this.brewJump(this.props.currentEditorCursorPageNum, false); + const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY); + if (editorTheme && EditorThemes.includes(editorTheme)) { + setEditorTheme(editorTheme); + } else { + setEditorTheme("default"); } - } - }, + const snippetBar = document.querySelector(".editor > .snippetBar"); + if (!snippetBar) return; - componentWillUnmount() { - if(this.resizeObserver) this.resizeObserver.disconnect(); - }, + const resizeObserver = new ResizeObserver((entries) => { + const height = document.querySelector(".editor > .snippetBar").offsetHeight; + setSnippetBarHeight(height); + }); - handleControlKeys : function(e){ - if(!(e.ctrlKey && e.metaKey && e.shiftKey)) return; - const LEFTARROW_KEY = 37; - const RIGHTARROW_KEY = 39; - if(e.keyCode == RIGHTARROW_KEY) this.brewJump(); - if(e.keyCode == LEFTARROW_KEY) this.sourceJump(); - if(e.keyCode == LEFTARROW_KEY || e.keyCode == RIGHTARROW_KEY) { - e.stopPropagation(); - e.preventDefault(); - } - }, + resizeObserver.observe(snippetBar); - updateCurrentCursorPage : function(pageNumber) { - this.props.onCursorPageChange(pageNumber); - }, + //ComponentWillUnmount equivalent + return () => { + if (resizeObserver) resizeObserver.disconnect(); + }; + }, []); - updateCurrentViewPage : function(pageNumber) { - this.props.onViewPageChange(pageNumber); - }, - - handleInject : function(injectText){ - this.codeEditor.current?.injectText(injectText); - }, - - handleViewChange : function(newView){ - this.props.setMoveArrows(newView === 'text'); - - this.setState({ - view : newView - }, ()=>{ - this.codeEditor.current?.focus(); + const previousProps = useRef({ + moveBrew, + moveSource, + currentBrewRendererPageNum, + currentEditorViewPageNum, + currentEditorCursorPageNum, }); - }, - brewJump : function(targetPage=this.props.currentEditorCursorPageNum, smooth=true){ - if(!window || !this.isText() || isJumping || jumpSource === 'source') - return; + //componentDidUpdate Equivalent + useEffect(() => { + const prev = previousProps.current; - // Get current brewRenderer scroll position and calculate target position - const brewRenderer = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0]; - const currentPos = brewRenderer.scrollTop; - const targetPos = window.frames['BrewRenderer'].contentDocument.getElementById(`p${targetPage}`).getBoundingClientRect().top; + if (prev.moveBrew !== moveBrew) { + brewJump(); + } - let scrollingTimeout; - const checkIfScrollComplete = ()=>{ // Prevent interrupting a scroll in progress if user clicks multiple times - clearTimeout(scrollingTimeout); // Reset the timer every time a scroll event occurs - scrollingTimeout = setTimeout(()=>{ - isJumping = false; - jumpSource = null; - brewRenderer.removeEventListener('scroll', checkIfScrollComplete); - }, 150); // If 150 ms pass without a brewRenderer scroll event, assume scrolling is done + if (prev.moveSource !== moveSource) { + sourceJump(); + } + + if (liveScroll) { + if (prev.currentBrewRendererPageNum !== currentBrewRendererPageNum) { + sourceJump(currentBrewRendererPageNum, false); + } else if (prev.currentEditorViewPageNum !== currentEditorViewPageNum) { + brewJump(currentEditorViewPageNum, false); + } else if (prev.currentEditorCursorPageNum !== currentEditorCursorPageNum) { + brewJump(currentEditorCursorPageNum, false); + } + } + + previousProps.current = { + moveBrew, + moveSource, + currentBrewRendererPageNum, + currentEditorViewPageNum, + currentEditorCursorPageNum, + }; + }, [ + moveBrew, + moveSource, + liveScroll, + currentBrewRendererPageNum, + currentEditorViewPageNum, + currentEditorCursorPageNum, + ]); + + const handleControlKeys = (e) => { + if (!(e.ctrlKey && e.metaKey && e.shiftKey)) return; + const LEFTARROW_KEY = 37; + const RIGHTARROW_KEY = 39; + if (e.keyCode == RIGHTARROW_KEY) brewJump(); + if (e.keyCode == LEFTARROW_KEY) sourceJump(); + if (e.keyCode == LEFTARROW_KEY || e.keyCode == RIGHTARROW_KEY) { + e.stopPropagation(); + e.preventDefault(); + } + }; + const updateCurrentCursorPage = (pageNumber) => { + onCursorPageChange(pageNumber); }; - isJumping = true; - jumpSource = 'brew'; - checkIfScrollComplete(); - brewRenderer.addEventListener('scroll', checkIfScrollComplete); + const updateCurrentViewPage = (pageNumber) => { + onViewPageChange(pageNumber); + }; - if(smooth) { - const bouncePos = targetPos >= 0 ? -30 : 30; //Do a little bounce before scrolling - const bounceDelay = 100; - const scrollDelay = 500; + const handleInject = (injectText) => { + codeEditor.current?.injectText(injectText); + }; - if(!this.throttleBrewMove) { - this.throttleBrewMove = _.throttle((currentPos, bouncePos, targetPos)=>{ - brewRenderer.scrollTo({ top: currentPos + bouncePos, behavior: 'smooth' }); - setTimeout(()=>{ - brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'smooth', block: 'start' }); - }, bounceDelay); - }, scrollDelay, { leading: true, trailing: false }); + const handleViewChange = (newView) => { + setMoveArrows(newView === "text"); + setView(newView); + }; + + useEffect(() => { + codeEditor.current?.focus(); + }, [view]); + + const brewJump = (targetPage = currentEditorCursorPageNum, smooth = true) => { + if (!window || !isText() || isJumping || jumpSource === "source") return; + + const brewRenderer = + window.frames["BrewRenderer"].contentDocument.getElementsByClassName("brewRenderer")[0]; + + const currentPos = brewRenderer.scrollTop; + + const targetPos = window.frames["BrewRenderer"].contentDocument + .getElementById(`p${targetPage}`) + .getBoundingClientRect().top; + + let scrollingTimeout; + + const checkIfScrollComplete = () => { + clearTimeout(scrollingTimeout); + + scrollingTimeout = setTimeout(() => { + isJumping = false; + jumpSource = null; + + brewRenderer.removeEventListener("scroll", checkIfScrollComplete); + }, 150); }; - this.throttleBrewMove(currentPos, bouncePos, targetPos); - } else { - brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'instant', block: 'start' }); - } - }, - sourceJump : function(targetPage=this.props.currentBrewRendererPageNum, smooth=true){ - if(!this.isText() || isJumping || jumpSource === 'brew') - return; + isJumping = true; + jumpSource = "brew"; - const editor = this.codeEditor.current; - if(!editor) return; - jumpSource = 'source'; + checkIfScrollComplete(); - editor.scrollToPage(targetPage); - setTimeout(()=>{ - jumpSource = null; - }, 200); - }, + brewRenderer.addEventListener("scroll", checkIfScrollComplete); - //Called when there are changes to the editor's dimensions - update : function(){}, + if (smooth) { + const bouncePos = targetPos >= 0 ? -30 : 30; + const bounceDelay = 100; + const scrollDelay = 500; - updateEditorTheme : function(newTheme){ - window.localStorage.setItem(EDITOR_THEME_KEY, newTheme); - this.setState({ - editorTheme : newTheme - }); - }, + if (!throttleBrewMove.current) { + throttleBrewMove.current = _.throttle( + (currentPos, bouncePos, targetPos) => { + brewRenderer.scrollTo({ + top: currentPos + bouncePos, + behavior: "smooth", + }); - //Called by CodeEditor after document switch, so Snippetbar can refresh UndoHistory - rerenderParent : function (){ - this.forceUpdate(); - }, + setTimeout(() => { + brewRenderer.scrollTo({ + top: currentPos + targetPos, + behavior: "smooth", + block: "start", + }); + }, bounceDelay); + }, + scrollDelay, + { + leading: true, + trailing: false, + }, + ); + } - renderEditor : function(){ - if(this.isText()){ - return <> - this.updateCurrentCursorPage(page)} - onViewChange={(page)=>this.updateCurrentViewPage(page)} - editorTheme={this.state.editorTheme} - renderer={this.props.brew.renderer} - style={{ height: `calc(100% - ${this.state.snippetBarHeight}px)` }}/> - ; - } - if(this.isStyle()){ - return <> - - ; - } - if(this.isMeta()){ - return <> - - - ; - } - if(this.isSnip()){ - if(!this.props.brew.snippets) { this.props.brew.snippets = DEFAULT_SNIPPET_TEXT; } - return <> - - ; - } - }, + throttleBrewMove.current(currentPos, bouncePos, targetPos); + } else { + brewRenderer.scrollTo({ + top: currentPos + targetPos, + behavior: "instant", + block: "start", + }); + } + }; - redo : function(){ - return this.codeEditor.current?.redo(); - }, + const sourceJump = (targetPage = currentBrewRendererPageNum, smooth = true) => { + if (!isText() || isJumping || jumpSource === "brew") return; - historySize : function(){ - return this.codeEditor.current?.historySize(); - }, + const editor = codeEditor.current; + if (!editor) return; + jumpSource = "source"; - undo : function(){ - return this.codeEditor.current?.undo(); - }, + editor.scrollToPage(targetPage); + setTimeout(() => { + jumpSource = null; + }, 200); + }; - foldCode : function() { - return this.codeEditor.current?.foldAll(); - }, + const updateEditorTheme = (newTheme) => { + window.localStorage.setItem(EDITOR_THEME_KEY, newTheme); + setEditorTheme(newTheme); + }; + + const renderEditor = () => { + if (isText()) { + return ( + <> + updateCurrentCursorPage(page)} + onViewChange={(page) => updateCurrentViewPage(page)} + editorTheme={currentEditorTheme} + renderer={brew.renderer} + style={{ height: `calc(100% - ${snippetBarHeight}px)` }} + /> + + ); + } + if (isStyle()) { + return ( + <> + + + ); + } + if (isMeta()) { + return ( + <> + + + + ); + } + if (isSnip()) { + if (!brew.snippets) { + brew.snippets = DEFAULT_SNIPPET_TEXT; + } + return ( + <> + + + ); + } + }; + const redo = () => { + return codeEditor.current?.redo(); + }; + + const historySize = () => { + return codeEditor.current?.historySize(); + }; + + const undo = () => { + return codeEditor.current?.undo(); + }; + + const foldCode = () => { + return codeEditor.current?.foldAll(); + }; + + const unfoldCode = () => { + return codeEditor.current?.unfoldAll(); + }; + + const update = () => {}; + + useImperativeHandle(ref, () => ({ + update, + undo, + redo, + foldCode, + unfoldCode, + historySize, + })); - unfoldCode : function() { - return this.codeEditor.current?.unfoldAll(); - }, - render : function(){ return ( -
+
- {this.renderEditor()} + {renderEditor()}
); - } -}); + }, +); -export default Editor; \ No newline at end of file +export default Editor; From b6554543489613187b40f1bed3f2284e715ebc55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sun, 23 Aug 2026 20:14:12 +0200 Subject: [PATCH 19/25] remove rerenderparent hook --- client/homebrew/editor/editor.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 66e6aaadf..18cc59913 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -354,7 +354,6 @@ const Editor = forwardRef( enableFolding={true} editorTheme={currentEditorTheme} renderer={brew.renderer} - rerenderParent={rerenderParent} style={{ height: `calc(100% - 25px)` }} /> @@ -407,7 +406,7 @@ const Editor = forwardRef( foldCode={foldCode} unfoldCode={unfoldCode} historySize={historySize()} - currentEditorTheme={editorTheme} + currentEditorTheme={currentEditorTheme} updateEditorTheme={updateEditorTheme} themeBundle={themeBundle} cursorPos={codeEditor.current?.getCursorPosition() || {}} From ac30edffd6b1c2ae99df4d3733b4ec0bda0acf30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sun, 23 Aug 2026 20:16:10 +0200 Subject: [PATCH 20/25] lint --- client/homebrew/editor/editor.jsx | 236 +++++++++++++++--------------- 1 file changed, 115 insertions(+), 121 deletions(-) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 18cc59913..7daf8511b 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -1,22 +1,22 @@ -import "./editor.less"; -import React, { useState, useRef, useEffect, forwardRef, useImperativeHandle } from "react"; -import dedent from "dedent"; +import './editor.less'; +import React, { useState, useRef, useEffect, forwardRef, useImperativeHandle } from 'react'; +import dedent from 'dedent'; -import CodeEditor from "@components/codeEditor/codeEditor.jsx"; -import SnippetBar from "./snippetbar/snippetbar.jsx"; -import MetadataEditor from "./metadataEditor/metadataEditor.jsx"; +import CodeEditor from '@components/codeEditor/codeEditor.jsx'; +import SnippetBar from './snippetbar/snippetbar.jsx'; +import MetadataEditor from './metadataEditor/metadataEditor.jsx'; -const EDITOR_THEME_KEY = "HB_editor_theme"; +const EDITOR_THEME_KEY = 'HB_editor_theme'; -import defaultCM5Theme from "@themes/codeMirror/default.js"; -import darkbrewery from "@themes/codeMirror/darkbrewery.js"; -import cm5Themes from "codemirror-5-themes"; +import defaultCM5Theme from '@themes/codeMirror/default.js'; +import darkbrewery from '@themes/codeMirror/darkbrewery.js'; +import cm5Themes from 'codemirror-5-themes'; const themes = { default: defaultCM5Theme, ...cm5Themes, darkbrewery }; const EditorThemes = Object.entries(themes) - .filter(([name, value]) => Array.isArray(value) && !name.endsWith("Init") && !name.endsWith("Style")) - .map(([name]) => name); + .filter(([name, value])=>Array.isArray(value) && !name.endsWith('Init') && !name.endsWith('Style')) + .map(([name])=>name); //const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m; //const SNIPPETBREAK_REGEX_V3 = /^\\snippet\ .*$/; @@ -43,14 +43,14 @@ const Editor = forwardRef( { brew = {}, - onBrewChange = () => {}, - reportError = () => {}, + onBrewChange = ()=>{}, + reportError = ()=>{}, - onCursorPageChange = () => {}, - onViewPageChange = () => {}, + onCursorPageChange = ()=>{}, + onViewPageChange = ()=>{}, - editorTheme = "default", - renderer = "legacy", + editorTheme = 'default', + renderer = 'legacy', moveBrew, moveSource, @@ -67,53 +67,47 @@ const Editor = forwardRef( currentBrewRendererPageNum = 1, }, ref, - ) => { + )=>{ const [currentEditorTheme, setEditorTheme] = useState(editorTheme); - const [view, setView] = useState("text"); // 'text', 'style', 'meta', 'snippet' + const [view, setView] = useState('text'); // 'text', 'style', 'meta', 'snippet' const [snippetBarHeight, setSnippetBarHeight] = useState(26); const editor = useRef(null); const codeEditor = useRef(null); const throttleBrewMove = useRef(null); - const isText = () => { - return view === "text"; - }; - const isStyle = () => { - return view === "style"; - }; - const isMeta = () => { - return view === "meta"; - }; - const isSnip = () => { - return view === "snippet"; - }; + const isText = ()=>isView('text'); + const isStyle = ()=>isView('style'); + const isMeta = ()=>isView('meta'); + const isSnip = ()=>isView('snippet'); + + const isView = (name)=>view === name; //componentDidMount equivalent - useEffect(() => { - const brewRenderer = document.getElementById("BrewRenderer"); - brewRenderer.onload = () => brewRenderer.contentDocument?.addEventListener("keydown", handleControlKeys); - document.addEventListener("keydown", handleControlKeys); + useEffect(()=>{ + const brewRenderer = document.getElementById('BrewRenderer'); + brewRenderer.onload = ()=>brewRenderer.contentDocument?.addEventListener('keydown', handleControlKeys); + document.addEventListener('keydown', handleControlKeys); const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY); - if (editorTheme && EditorThemes.includes(editorTheme)) { + if(editorTheme && EditorThemes.includes(editorTheme)) { setEditorTheme(editorTheme); } else { - setEditorTheme("default"); + setEditorTheme('default'); } - const snippetBar = document.querySelector(".editor > .snippetBar"); - if (!snippetBar) return; + const snippetBar = document.querySelector('.editor > .snippetBar'); + if(!snippetBar) return; - const resizeObserver = new ResizeObserver((entries) => { - const height = document.querySelector(".editor > .snippetBar").offsetHeight; + const resizeObserver = new ResizeObserver((entries)=>{ + const height = document.querySelector('.editor > .snippetBar').offsetHeight; setSnippetBarHeight(height); }); resizeObserver.observe(snippetBar); //ComponentWillUnmount equivalent - return () => { - if (resizeObserver) resizeObserver.disconnect(); + return ()=>{ + if(resizeObserver) resizeObserver.disconnect(); }; }, []); @@ -126,23 +120,23 @@ const Editor = forwardRef( }); //componentDidUpdate Equivalent - useEffect(() => { + useEffect(()=>{ const prev = previousProps.current; - if (prev.moveBrew !== moveBrew) { + if(prev.moveBrew !== moveBrew) { brewJump(); } - if (prev.moveSource !== moveSource) { + if(prev.moveSource !== moveSource) { sourceJump(); } - if (liveScroll) { - if (prev.currentBrewRendererPageNum !== currentBrewRendererPageNum) { + if(liveScroll) { + if(prev.currentBrewRendererPageNum !== currentBrewRendererPageNum) { sourceJump(currentBrewRendererPageNum, false); - } else if (prev.currentEditorViewPageNum !== currentEditorViewPageNum) { + } else if(prev.currentEditorViewPageNum !== currentEditorViewPageNum) { brewJump(currentEditorViewPageNum, false); - } else if (prev.currentEditorCursorPageNum !== currentEditorCursorPageNum) { + } else if(prev.currentEditorCursorPageNum !== currentEditorCursorPageNum) { brewJump(currentEditorCursorPageNum, false); } } @@ -163,95 +157,95 @@ const Editor = forwardRef( currentEditorCursorPageNum, ]); - const handleControlKeys = (e) => { - if (!(e.ctrlKey && e.metaKey && e.shiftKey)) return; + const handleControlKeys = (e)=>{ + if(!(e.ctrlKey && e.metaKey && e.shiftKey)) return; const LEFTARROW_KEY = 37; const RIGHTARROW_KEY = 39; - if (e.keyCode == RIGHTARROW_KEY) brewJump(); - if (e.keyCode == LEFTARROW_KEY) sourceJump(); - if (e.keyCode == LEFTARROW_KEY || e.keyCode == RIGHTARROW_KEY) { + if(e.keyCode == RIGHTARROW_KEY) brewJump(); + if(e.keyCode == LEFTARROW_KEY) sourceJump(); + if(e.keyCode == LEFTARROW_KEY || e.keyCode == RIGHTARROW_KEY) { e.stopPropagation(); e.preventDefault(); } }; - const updateCurrentCursorPage = (pageNumber) => { + const updateCurrentCursorPage = (pageNumber)=>{ onCursorPageChange(pageNumber); }; - const updateCurrentViewPage = (pageNumber) => { + const updateCurrentViewPage = (pageNumber)=>{ onViewPageChange(pageNumber); }; - const handleInject = (injectText) => { + const handleInject = (injectText)=>{ codeEditor.current?.injectText(injectText); }; - const handleViewChange = (newView) => { - setMoveArrows(newView === "text"); + const handleViewChange = (newView)=>{ + setMoveArrows(newView === 'text'); setView(newView); }; - useEffect(() => { + useEffect(()=>{ codeEditor.current?.focus(); }, [view]); - const brewJump = (targetPage = currentEditorCursorPageNum, smooth = true) => { - if (!window || !isText() || isJumping || jumpSource === "source") return; + const brewJump = (targetPage = currentEditorCursorPageNum, smooth = true)=>{ + if(!window || !isText() || isJumping || jumpSource === 'source') return; const brewRenderer = - window.frames["BrewRenderer"].contentDocument.getElementsByClassName("brewRenderer")[0]; + window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0]; const currentPos = brewRenderer.scrollTop; - const targetPos = window.frames["BrewRenderer"].contentDocument + const targetPos = window.frames['BrewRenderer'].contentDocument .getElementById(`p${targetPage}`) .getBoundingClientRect().top; let scrollingTimeout; - const checkIfScrollComplete = () => { + const checkIfScrollComplete = ()=>{ clearTimeout(scrollingTimeout); - scrollingTimeout = setTimeout(() => { + scrollingTimeout = setTimeout(()=>{ isJumping = false; jumpSource = null; - brewRenderer.removeEventListener("scroll", checkIfScrollComplete); + brewRenderer.removeEventListener('scroll', checkIfScrollComplete); }, 150); }; isJumping = true; - jumpSource = "brew"; + jumpSource = 'brew'; checkIfScrollComplete(); - brewRenderer.addEventListener("scroll", checkIfScrollComplete); + brewRenderer.addEventListener('scroll', checkIfScrollComplete); - if (smooth) { + if(smooth) { const bouncePos = targetPos >= 0 ? -30 : 30; const bounceDelay = 100; const scrollDelay = 500; - if (!throttleBrewMove.current) { + if(!throttleBrewMove.current) { throttleBrewMove.current = _.throttle( - (currentPos, bouncePos, targetPos) => { + (currentPos, bouncePos, targetPos)=>{ brewRenderer.scrollTo({ - top: currentPos + bouncePos, - behavior: "smooth", + top : currentPos + bouncePos, + behavior : 'smooth', }); - setTimeout(() => { + setTimeout(()=>{ brewRenderer.scrollTo({ - top: currentPos + targetPos, - behavior: "smooth", - block: "start", + top : currentPos + targetPos, + behavior : 'smooth', + block : 'start', }); }, bounceDelay); }, scrollDelay, { - leading: true, - trailing: false, + leading : true, + trailing : false, }, ); } @@ -259,45 +253,45 @@ const Editor = forwardRef( throttleBrewMove.current(currentPos, bouncePos, targetPos); } else { brewRenderer.scrollTo({ - top: currentPos + targetPos, - behavior: "instant", - block: "start", + top : currentPos + targetPos, + behavior : 'instant', + block : 'start', }); } }; - const sourceJump = (targetPage = currentBrewRendererPageNum, smooth = true) => { - if (!isText() || isJumping || jumpSource === "brew") return; + const sourceJump = (targetPage = currentBrewRendererPageNum, smooth = true)=>{ + if(!isText() || isJumping || jumpSource === 'brew') return; const editor = codeEditor.current; - if (!editor) return; - jumpSource = "source"; + if(!editor) return; + jumpSource = 'source'; editor.scrollToPage(targetPage); - setTimeout(() => { + setTimeout(()=>{ jumpSource = null; }, 200); }; - const updateEditorTheme = (newTheme) => { + const updateEditorTheme = (newTheme)=>{ window.localStorage.setItem(EDITOR_THEME_KEY, newTheme); setEditorTheme(newTheme); }; - const renderEditor = () => { - if (isText()) { + const renderEditor = ()=>{ + if(isText()) { return ( <> updateCurrentCursorPage(page)} - onViewChange={(page) => updateCurrentViewPage(page)} + onChange={onBrewChange('text')} + onCursorChange={(page)=>updateCurrentCursorPage(page)} + onViewChange={(page)=>updateCurrentViewPage(page)} editorTheme={currentEditorTheme} renderer={brew.renderer} style={{ height: `calc(100% - ${snippetBarHeight}px)` }} @@ -305,17 +299,17 @@ const Editor = forwardRef( ); } - if (isStyle()) { + if(isStyle()) { return ( <> ); } - if (isMeta()) { + if(isMeta()) { return ( <> - + ); } - if (isSnip()) { - if (!brew.snippets) { + if(isSnip()) { + if(!brew.snippets) { brew.snippets = DEFAULT_SNIPPET_TEXT; } return ( <> { + const redo = ()=>{ return codeEditor.current?.redo(); }; - const historySize = () => { + const historySize = ()=>{ return codeEditor.current?.historySize(); }; - const undo = () => { + const undo = ()=>{ return codeEditor.current?.undo(); }; - const foldCode = () => { + const foldCode = ()=>{ return codeEditor.current?.foldAll(); }; - const unfoldCode = () => { + const unfoldCode = ()=>{ return codeEditor.current?.unfoldAll(); }; - const update = () => {}; + const update = ()=>{}; - useImperativeHandle(ref, () => ({ + useImperativeHandle(ref, ()=>({ update, undo, redo, @@ -392,7 +386,7 @@ const Editor = forwardRef( })); return ( -
+
Date: Sun, 23 Aug 2026 20:19:03 +0200 Subject: [PATCH 21/25] minify some things --- client/homebrew/editor/editor.jsx | 36 ++++++------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 7daf8511b..cf8b46fcd 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -1,3 +1,4 @@ +/*eslint max-lines: ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}]*/ import './editor.less'; import React, { useState, useRef, useEffect, forwardRef, useImperativeHandle } from 'react'; import dedent from 'dedent'; @@ -354,37 +355,14 @@ const Editor = forwardRef( ); } }; - const redo = ()=>{ - return codeEditor.current?.redo(); - }; - - const historySize = ()=>{ - return codeEditor.current?.historySize(); - }; - - const undo = ()=>{ - return codeEditor.current?.undo(); - }; - - const foldCode = ()=>{ - return codeEditor.current?.foldAll(); - }; - - const unfoldCode = ()=>{ - return codeEditor.current?.unfoldAll(); - }; - - const update = ()=>{}; - useImperativeHandle(ref, ()=>({ - update, - undo, - redo, - foldCode, - unfoldCode, - historySize, + update : ()=>{}, + undo : ()=>codeEditor.current?.undo(), + redo : ()=>codeEditor.current?.redo(), + foldCode : ()=>codeEditor.current?.foldAll(), + unfoldCode : ()=>codeEditor.current?.unfoldAll(), + historySize : ()=>codeEditor.current?.historySize(), })); - return (
Date: Sun, 23 Aug 2026 21:43:46 +0200 Subject: [PATCH 22/25] fix small issue --- client/homebrew/editor/editor.jsx | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index cf8b46fcd..eac31f320 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -355,14 +355,23 @@ const Editor = forwardRef( ); } }; + const redo = ()=>codeEditor.current?.redo(); + const historySize = ()=>codeEditor.current?.historySize(); + const undo = ()=>codeEditor.current?.undo(); + const foldCode = ()=>codeEditor.current?.foldAll(); + const unfoldCode = ()=>codeEditor.current?.unfoldAll(); + + const update = ()=>{}; + useImperativeHandle(ref, ()=>({ - update : ()=>{}, - undo : ()=>codeEditor.current?.undo(), - redo : ()=>codeEditor.current?.redo(), - foldCode : ()=>codeEditor.current?.foldAll(), - unfoldCode : ()=>codeEditor.current?.unfoldAll(), - historySize : ()=>codeEditor.current?.historySize(), + update, + undo, + redo, + foldCode, + unfoldCode, + historySize, })); + return (
Date: Sun, 23 Aug 2026 21:56:37 +0200 Subject: [PATCH 23/25] remove lodash use --- client/homebrew/editor/editor.jsx | 57 ++--- config/editorReferenceCopy.jsx | 350 ++++++++++++++++++++++++++++++ 2 files changed, 371 insertions(+), 36 deletions(-) create mode 100644 config/editorReferenceCopy.jsx diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index eac31f320..df95d79fc 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -124,13 +124,8 @@ const Editor = forwardRef( useEffect(()=>{ const prev = previousProps.current; - if(prev.moveBrew !== moveBrew) { - brewJump(); - } - - if(prev.moveSource !== moveSource) { - sourceJump(); - } + if(prev.moveBrew !== moveBrew) brewJump(); + if(prev.moveSource !== moveSource) sourceJump(); if(liveScroll) { if(prev.currentBrewRendererPageNum !== currentBrewRendererPageNum) { @@ -194,13 +189,13 @@ const Editor = forwardRef( if(!window || !isText() || isJumping || jumpSource === 'source') return; const brewRenderer = - window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0]; + window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0]; const currentPos = brewRenderer.scrollTop; const targetPos = window.frames['BrewRenderer'].contentDocument - .getElementById(`p${targetPage}`) - .getBoundingClientRect().top; + .getElementById(`p${targetPage}`) + .getBoundingClientRect().top; let scrollingTimeout; @@ -219,39 +214,29 @@ const Editor = forwardRef( jumpSource = 'brew'; checkIfScrollComplete(); - brewRenderer.addEventListener('scroll', checkIfScrollComplete); if(smooth) { const bouncePos = targetPos >= 0 ? -30 : 30; - const bounceDelay = 100; - const scrollDelay = 500; + const now = Date.now(); - if(!throttleBrewMove.current) { - throttleBrewMove.current = _.throttle( - (currentPos, bouncePos, targetPos)=>{ - brewRenderer.scrollTo({ - top : currentPos + bouncePos, - behavior : 'smooth', - }); + // leading: true, trailing: false, 500ms throttle + if(now - throttleBrewMove.current >= 500) { + throttleBrewMove.current = now; - setTimeout(()=>{ - brewRenderer.scrollTo({ - top : currentPos + targetPos, - behavior : 'smooth', - block : 'start', - }); - }, bounceDelay); - }, - scrollDelay, - { - leading : true, - trailing : false, - }, - ); + brewRenderer.scrollTo({ + top : currentPos + bouncePos, + behavior : 'smooth', + }); + + setTimeout(()=>{ + brewRenderer.scrollTo({ + top : currentPos + targetPos, + behavior : 'smooth', + block : 'start', + }); + }, 100); } - - throttleBrewMove.current(currentPos, bouncePos, targetPos); } else { brewRenderer.scrollTo({ top : currentPos + targetPos, diff --git a/config/editorReferenceCopy.jsx b/config/editorReferenceCopy.jsx new file mode 100644 index 000000000..3f0386da4 --- /dev/null +++ b/config/editorReferenceCopy.jsx @@ -0,0 +1,350 @@ +/*eslint max-lines: ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}]*/ +import './editor.less'; +import React from 'react'; +import createReactClass from 'create-react-class'; +import _ from 'lodash'; +import dedent from 'dedent'; + +import CodeEditor from '@components/codeEditor/codeEditor.jsx'; +import SnippetBar from '../client/homebrew/editor/snippetbar/snippetbar.jsx'; +import MetadataEditor from '../client/homebrew/editor/metadataEditor/metadataEditor.jsx'; + +const EDITOR_THEME_KEY = 'HB_editor_theme'; + +import defaultCM5Theme from '@themes/codeMirror/default.js'; +import darkbrewery from '@themes/codeMirror/darkbrewery.js'; +import cm5Themes from 'codemirror-5-themes'; + +const themes = { default: defaultCM5Theme, ...cm5Themes, darkbrewery }; + +const EditorThemes = Object.entries(themes) + .filter(([name, value])=>Array.isArray(value) && + !name.endsWith('Init') && + !name.endsWith('Style') + ) + .map(([name])=>name); + + +//const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m; +//const SNIPPETBREAK_REGEX_V3 = /^\\snippet\ .*$/; +const DEFAULT_STYLE_TEXT = dedent` + /*=======--- Example CSS styling ---=======*/ + /* Any CSS here will apply to your document! */ + + .myExampleClass { + color: black; + }`; + +const DEFAULT_SNIPPET_TEXT = dedent` + \snippet example snippet + + The text between \`\snippet title\` lines will become a snippet of name \`title\` as this example provides. + + This snippet is accessible in the brew tab, and will be inherited if the brew is used as a theme. +`; +let isJumping = false; +let jumpSource = null; + +const Editor = createReactClass({ + displayName : 'Editor', + getDefaultProps : function() { + return { + brew : { + text : '', + style : '' + }, + + onBrewChange : ()=>{}, + reportError : ()=>{}, + + onCursorPageChange : ()=>{}, + onViewPageChange : ()=>{}, + + editorTheme : 'default', + renderer : 'legacy', + + currentEditorCursorPageNum : 1, + currentEditorViewPageNum : 1, + currentBrewRendererPageNum : 1, + }; + }, + getInitialState : function() { + return { + editorTheme : this.props.editorTheme, + view : 'text', //'text', 'style', 'meta', 'snippet' + snippetBarHeight : 26, + }; + }, + + editor : React.createRef(null), + codeEditor : React.createRef(null), + + isText : function() {return this.state.view == 'text';}, + isStyle : function() {return this.state.view == 'style';}, + isMeta : function() {return this.state.view == 'meta';}, + isSnip : function() {return this.state.view == 'snippet';}, + + componentDidMount : function() { + + const brewRenderer = document.getElementById('BrewRenderer'); + brewRenderer.onload = ()=>brewRenderer.contentDocument?.addEventListener('keydown', this.handleControlKeys); + document.addEventListener('keydown', this.handleControlKeys); + + const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY); + if(editorTheme && EditorThemes.includes(editorTheme)) { + this.setState({ editorTheme }); + } else { + this.setState({ editorTheme: 'default' }); + } + const snippetBar = document.querySelector('.editor > .snippetBar'); + if(!snippetBar) return; + + this.resizeObserver = new ResizeObserver((entries)=>{ + const height = document.querySelector('.editor > .snippetBar').offsetHeight; + this.setState({ snippetBarHeight: height }); + }); + + this.resizeObserver.observe(snippetBar); + }, + + componentDidUpdate : function(prevProps, prevState, snapshot) { + + if(prevProps.moveBrew !== this.props.moveBrew) + this.brewJump(); + + if(prevProps.moveSource !== this.props.moveSource) + this.sourceJump(); + + if(this.props.liveScroll) { + if(prevProps.currentBrewRendererPageNum !== this.props.currentBrewRendererPageNum) { + this.sourceJump(this.props.currentBrewRendererPageNum, false); + } else if(prevProps.currentEditorViewPageNum !== this.props.currentEditorViewPageNum) { + this.brewJump(this.props.currentEditorViewPageNum, false); + } else if(prevProps.currentEditorCursorPageNum !== this.props.currentEditorCursorPageNum) { + this.brewJump(this.props.currentEditorCursorPageNum, false); + } + } + }, + + componentWillUnmount() { + if(this.resizeObserver) this.resizeObserver.disconnect(); + }, + + handleControlKeys : function(e){ + if(!(e.ctrlKey && e.metaKey && e.shiftKey)) return; + const LEFTARROW_KEY = 37; + const RIGHTARROW_KEY = 39; + if(e.keyCode == RIGHTARROW_KEY) this.brewJump(); + if(e.keyCode == LEFTARROW_KEY) this.sourceJump(); + if(e.keyCode == LEFTARROW_KEY || e.keyCode == RIGHTARROW_KEY) { + e.stopPropagation(); + e.preventDefault(); + } + }, + + updateCurrentCursorPage : function(pageNumber) { + this.props.onCursorPageChange(pageNumber); + }, + + updateCurrentViewPage : function(pageNumber) { + this.props.onViewPageChange(pageNumber); + }, + + handleInject : function(injectText){ + this.codeEditor.current?.injectText(injectText); + }, + + handleViewChange : function(newView){ + this.props.setMoveArrows(newView === 'text'); + + this.setState({ + view : newView + }, ()=>{ + this.codeEditor.current?.focus(); + }); + }, + + brewJump : function(targetPage=this.props.currentEditorCursorPageNum, smooth=true){ + if(!window || !this.isText() || isJumping || jumpSource === 'source') + return; + + // Get current brewRenderer scroll position and calculate target position + const brewRenderer = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0]; + const currentPos = brewRenderer.scrollTop; + const targetPos = window.frames['BrewRenderer'].contentDocument.getElementById(`p${targetPage}`).getBoundingClientRect().top; + + let scrollingTimeout; + const checkIfScrollComplete = ()=>{ // Prevent interrupting a scroll in progress if user clicks multiple times + clearTimeout(scrollingTimeout); // Reset the timer every time a scroll event occurs + scrollingTimeout = setTimeout(()=>{ + isJumping = false; + jumpSource = null; + brewRenderer.removeEventListener('scroll', checkIfScrollComplete); + }, 150); // If 150 ms pass without a brewRenderer scroll event, assume scrolling is done + }; + + isJumping = true; + jumpSource = 'brew'; + checkIfScrollComplete(); + brewRenderer.addEventListener('scroll', checkIfScrollComplete); + + if(smooth) { + const bouncePos = targetPos >= 0 ? -30 : 30; //Do a little bounce before scrolling + const bounceDelay = 100; + const scrollDelay = 500; + + if(!this.throttleBrewMove) { + this.throttleBrewMove = _.throttle((currentPos, bouncePos, targetPos)=>{ + brewRenderer.scrollTo({ top: currentPos + bouncePos, behavior: 'smooth' }); + setTimeout(()=>{ + brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'smooth', block: 'start' }); + }, bounceDelay); + }, scrollDelay, { leading: true, trailing: false }); + }; + this.throttleBrewMove(currentPos, bouncePos, targetPos); + } else { + brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'instant', block: 'start' }); + } + }, + + sourceJump : function(targetPage=this.props.currentBrewRendererPageNum, smooth=true){ + if(!this.isText() || isJumping || jumpSource === 'brew') + return; + + const editor = this.codeEditor.current; + if(!editor) return; + jumpSource = 'source'; + + editor.scrollToPage(targetPage); + setTimeout(()=>{ + jumpSource = null; + }, 200); + }, + + //Called when there are changes to the editor's dimensions + update : function(){}, + + updateEditorTheme : function(newTheme){ + window.localStorage.setItem(EDITOR_THEME_KEY, newTheme); + this.setState({ + editorTheme : newTheme + }); + }, + + //Called by CodeEditor after document switch, so Snippetbar can refresh UndoHistory + rerenderParent : function (){ + this.forceUpdate(); + }, + + renderEditor : function(){ + if(this.isText()){ + return <> + this.updateCurrentCursorPage(page)} + onViewChange={(page)=>this.updateCurrentViewPage(page)} + editorTheme={this.state.editorTheme} + renderer={this.props.brew.renderer} + style={{ height: `calc(100% - ${this.state.snippetBarHeight}px)` }}/> + ; + } + if(this.isStyle()){ + return <> + + ; + } + if(this.isMeta()){ + return <> + + + ; + } + if(this.isSnip()){ + if(!this.props.brew.snippets) { this.props.brew.snippets = DEFAULT_SNIPPET_TEXT; } + return <> + + ; + } + }, + + redo : function(){ + return this.codeEditor.current?.redo(); + }, + + historySize : function(){ + return this.codeEditor.current?.historySize(); + }, + + undo : function(){ + return this.codeEditor.current?.undo(); + }, + + foldCode : function() { + return this.codeEditor.current?.foldAll(); + }, + + unfoldCode : function() { + return this.codeEditor.current?.unfoldAll(); + }, + render : function(){ + return ( +
+ + + {this.renderEditor()} +
+ ); + } +}); + +export default Editor; \ No newline at end of file From 613cea91a3c193f5a4414e87616bd2e58417a759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sun, 23 Aug 2026 22:11:30 +0200 Subject: [PATCH 24/25] skim lines --- client/homebrew/editor/editor.jsx | 124 +++++++++--------------------- 1 file changed, 36 insertions(+), 88 deletions(-) diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index df95d79fc..92ce0b78c 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -84,18 +84,13 @@ const Editor = forwardRef( const isView = (name)=>view === name; - //componentDidMount equivalent useEffect(()=>{ const brewRenderer = document.getElementById('BrewRenderer'); brewRenderer.onload = ()=>brewRenderer.contentDocument?.addEventListener('keydown', handleControlKeys); document.addEventListener('keydown', handleControlKeys); const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY); - if(editorTheme && EditorThemes.includes(editorTheme)) { - setEditorTheme(editorTheme); - } else { - setEditorTheme('default'); - } + if(editorTheme && EditorThemes.includes(editorTheme)) setEditorTheme(editorTheme); else setEditorTheme('default'); const snippetBar = document.querySelector('.editor > .snippetBar'); if(!snippetBar) return; @@ -103,55 +98,18 @@ const Editor = forwardRef( const height = document.querySelector('.editor > .snippetBar').offsetHeight; setSnippetBarHeight(height); }); - resizeObserver.observe(snippetBar); - //ComponentWillUnmount equivalent return ()=>{ if(resizeObserver) resizeObserver.disconnect(); }; }, []); - const previousProps = useRef({ - moveBrew, - moveSource, - currentBrewRendererPageNum, - currentEditorViewPageNum, - currentEditorCursorPageNum, - }); - - //componentDidUpdate Equivalent - useEffect(()=>{ - const prev = previousProps.current; - - if(prev.moveBrew !== moveBrew) brewJump(); - if(prev.moveSource !== moveSource) sourceJump(); - - if(liveScroll) { - if(prev.currentBrewRendererPageNum !== currentBrewRendererPageNum) { - sourceJump(currentBrewRendererPageNum, false); - } else if(prev.currentEditorViewPageNum !== currentEditorViewPageNum) { - brewJump(currentEditorViewPageNum, false); - } else if(prev.currentEditorCursorPageNum !== currentEditorCursorPageNum) { - brewJump(currentEditorCursorPageNum, false); - } - } - - previousProps.current = { - moveBrew, - moveSource, - currentBrewRendererPageNum, - currentEditorViewPageNum, - currentEditorCursorPageNum, - }; - }, [ - moveBrew, - moveSource, - liveScroll, - currentBrewRendererPageNum, - currentEditorViewPageNum, - currentEditorCursorPageNum, - ]); + useEffect(()=>{ if(moveBrew) brewJump(); }, [moveBrew]); + useEffect(()=>{ if(moveSource) sourceJump(); }, [moveSource]); + useEffect(()=>{ if(liveScroll) sourceJump(currentBrewRendererPageNum, false); }, [currentBrewRendererPageNum, liveScroll]); + useEffect(()=>{ if(liveScroll) brewJump(currentEditorViewPageNum, false); }, [currentEditorViewPageNum, liveScroll]); + useEffect(()=>{ if(liveScroll) brewJump(currentEditorCursorPageNum, false); }, [currentEditorCursorPageNum, liveScroll]); const handleControlKeys = (e)=>{ if(!(e.ctrlKey && e.metaKey && e.shiftKey)) return; @@ -164,6 +122,7 @@ const Editor = forwardRef( e.preventDefault(); } }; + const updateCurrentCursorPage = (pageNumber)=>{ onCursorPageChange(pageNumber); }; @@ -180,7 +139,6 @@ const Editor = forwardRef( setMoveArrows(newView === 'text'); setView(newView); }; - useEffect(()=>{ codeEditor.current?.focus(); }, [view]); @@ -189,25 +147,25 @@ const Editor = forwardRef( if(!window || !isText() || isJumping || jumpSource === 'source') return; const brewRenderer = - window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0]; + window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0]; const currentPos = brewRenderer.scrollTop; const targetPos = window.frames['BrewRenderer'].contentDocument - .getElementById(`p${targetPage}`) - .getBoundingClientRect().top; + .getElementById(`p${targetPage}`) + .getBoundingClientRect().top; let scrollingTimeout; - const checkIfScrollComplete = ()=>{ - clearTimeout(scrollingTimeout); + const checkIfScrollComplete = ()=>{// Prevent interrupting a scroll in progress if user clicks multiple times + clearTimeout(scrollingTimeout);// Reset the timer every time a scroll event occurs scrollingTimeout = setTimeout(()=>{ isJumping = false; jumpSource = null; brewRenderer.removeEventListener('scroll', checkIfScrollComplete); - }, 150); + }, 150);// If 150 ms pass without a brewRenderer scroll event, assume scrolling is done }; isJumping = true; @@ -217,31 +175,20 @@ const Editor = forwardRef( brewRenderer.addEventListener('scroll', checkIfScrollComplete); if(smooth) { - const bouncePos = targetPos >= 0 ? -30 : 30; + const bouncePos = targetPos >= 0 ? -30 : 30; //Do a little bounce before scrolling const now = Date.now(); - // leading: true, trailing: false, 500ms throttle if(now - throttleBrewMove.current >= 500) { throttleBrewMove.current = now; - brewRenderer.scrollTo({ - top : currentPos + bouncePos, - behavior : 'smooth', - }); + brewRenderer.scrollTo({ top: currentPos + bouncePos, behavior: 'smooth' }); setTimeout(()=>{ - brewRenderer.scrollTo({ - top : currentPos + targetPos, - behavior : 'smooth', - block : 'start', - }); + brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'smooth', block: 'start' }); }, 100); } } else { - brewRenderer.scrollTo({ - top : currentPos + targetPos, - behavior : 'instant', - block : 'start', + brewRenderer.scrollTo({ top : currentPos + targetPos, behavior : 'instant', block : 'start', }); } }; @@ -249,11 +196,10 @@ const Editor = forwardRef( const sourceJump = (targetPage = currentBrewRendererPageNum, smooth = true)=>{ if(!isText() || isJumping || jumpSource === 'brew') return; - const editor = codeEditor.current; - if(!editor) return; + if(!codeEditor.current) return; jumpSource = 'source'; - editor.scrollToPage(targetPage); + codeEditor.current.scrollToPage(targetPage); setTimeout(()=>{ jumpSource = null; }, 200); @@ -303,20 +249,6 @@ const Editor = forwardRef( ); } - if(isMeta()) { - return ( - <> - - - - ); - } if(isSnip()) { if(!brew.snippets) { brew.snippets = DEFAULT_SNIPPET_TEXT; @@ -339,13 +271,29 @@ const Editor = forwardRef( ); } + if(isMeta()) { + return ( + <> + + + + ); + } }; + const redo = ()=>codeEditor.current?.redo(); const historySize = ()=>codeEditor.current?.historySize(); const undo = ()=>codeEditor.current?.undo(); const foldCode = ()=>codeEditor.current?.foldAll(); const unfoldCode = ()=>codeEditor.current?.unfoldAll(); + //Called when there are changes to the editor's dimensions const update = ()=>{}; useImperativeHandle(ref, ()=>({ @@ -382,7 +330,7 @@ const Editor = forwardRef( {renderEditor()}
); - }, + } ); export default Editor; From b39502d801605cf260b45f712d81d53901539cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sun, 23 Aug 2026 22:12:11 +0200 Subject: [PATCH 25/25] remove reference file --- config/editorReferenceCopy.jsx | 350 --------------------------------- 1 file changed, 350 deletions(-) delete mode 100644 config/editorReferenceCopy.jsx diff --git a/config/editorReferenceCopy.jsx b/config/editorReferenceCopy.jsx deleted file mode 100644 index 3f0386da4..000000000 --- a/config/editorReferenceCopy.jsx +++ /dev/null @@ -1,350 +0,0 @@ -/*eslint max-lines: ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}]*/ -import './editor.less'; -import React from 'react'; -import createReactClass from 'create-react-class'; -import _ from 'lodash'; -import dedent from 'dedent'; - -import CodeEditor from '@components/codeEditor/codeEditor.jsx'; -import SnippetBar from '../client/homebrew/editor/snippetbar/snippetbar.jsx'; -import MetadataEditor from '../client/homebrew/editor/metadataEditor/metadataEditor.jsx'; - -const EDITOR_THEME_KEY = 'HB_editor_theme'; - -import defaultCM5Theme from '@themes/codeMirror/default.js'; -import darkbrewery from '@themes/codeMirror/darkbrewery.js'; -import cm5Themes from 'codemirror-5-themes'; - -const themes = { default: defaultCM5Theme, ...cm5Themes, darkbrewery }; - -const EditorThemes = Object.entries(themes) - .filter(([name, value])=>Array.isArray(value) && - !name.endsWith('Init') && - !name.endsWith('Style') - ) - .map(([name])=>name); - - -//const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m; -//const SNIPPETBREAK_REGEX_V3 = /^\\snippet\ .*$/; -const DEFAULT_STYLE_TEXT = dedent` - /*=======--- Example CSS styling ---=======*/ - /* Any CSS here will apply to your document! */ - - .myExampleClass { - color: black; - }`; - -const DEFAULT_SNIPPET_TEXT = dedent` - \snippet example snippet - - The text between \`\snippet title\` lines will become a snippet of name \`title\` as this example provides. - - This snippet is accessible in the brew tab, and will be inherited if the brew is used as a theme. -`; -let isJumping = false; -let jumpSource = null; - -const Editor = createReactClass({ - displayName : 'Editor', - getDefaultProps : function() { - return { - brew : { - text : '', - style : '' - }, - - onBrewChange : ()=>{}, - reportError : ()=>{}, - - onCursorPageChange : ()=>{}, - onViewPageChange : ()=>{}, - - editorTheme : 'default', - renderer : 'legacy', - - currentEditorCursorPageNum : 1, - currentEditorViewPageNum : 1, - currentBrewRendererPageNum : 1, - }; - }, - getInitialState : function() { - return { - editorTheme : this.props.editorTheme, - view : 'text', //'text', 'style', 'meta', 'snippet' - snippetBarHeight : 26, - }; - }, - - editor : React.createRef(null), - codeEditor : React.createRef(null), - - isText : function() {return this.state.view == 'text';}, - isStyle : function() {return this.state.view == 'style';}, - isMeta : function() {return this.state.view == 'meta';}, - isSnip : function() {return this.state.view == 'snippet';}, - - componentDidMount : function() { - - const brewRenderer = document.getElementById('BrewRenderer'); - brewRenderer.onload = ()=>brewRenderer.contentDocument?.addEventListener('keydown', this.handleControlKeys); - document.addEventListener('keydown', this.handleControlKeys); - - const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY); - if(editorTheme && EditorThemes.includes(editorTheme)) { - this.setState({ editorTheme }); - } else { - this.setState({ editorTheme: 'default' }); - } - const snippetBar = document.querySelector('.editor > .snippetBar'); - if(!snippetBar) return; - - this.resizeObserver = new ResizeObserver((entries)=>{ - const height = document.querySelector('.editor > .snippetBar').offsetHeight; - this.setState({ snippetBarHeight: height }); - }); - - this.resizeObserver.observe(snippetBar); - }, - - componentDidUpdate : function(prevProps, prevState, snapshot) { - - if(prevProps.moveBrew !== this.props.moveBrew) - this.brewJump(); - - if(prevProps.moveSource !== this.props.moveSource) - this.sourceJump(); - - if(this.props.liveScroll) { - if(prevProps.currentBrewRendererPageNum !== this.props.currentBrewRendererPageNum) { - this.sourceJump(this.props.currentBrewRendererPageNum, false); - } else if(prevProps.currentEditorViewPageNum !== this.props.currentEditorViewPageNum) { - this.brewJump(this.props.currentEditorViewPageNum, false); - } else if(prevProps.currentEditorCursorPageNum !== this.props.currentEditorCursorPageNum) { - this.brewJump(this.props.currentEditorCursorPageNum, false); - } - } - }, - - componentWillUnmount() { - if(this.resizeObserver) this.resizeObserver.disconnect(); - }, - - handleControlKeys : function(e){ - if(!(e.ctrlKey && e.metaKey && e.shiftKey)) return; - const LEFTARROW_KEY = 37; - const RIGHTARROW_KEY = 39; - if(e.keyCode == RIGHTARROW_KEY) this.brewJump(); - if(e.keyCode == LEFTARROW_KEY) this.sourceJump(); - if(e.keyCode == LEFTARROW_KEY || e.keyCode == RIGHTARROW_KEY) { - e.stopPropagation(); - e.preventDefault(); - } - }, - - updateCurrentCursorPage : function(pageNumber) { - this.props.onCursorPageChange(pageNumber); - }, - - updateCurrentViewPage : function(pageNumber) { - this.props.onViewPageChange(pageNumber); - }, - - handleInject : function(injectText){ - this.codeEditor.current?.injectText(injectText); - }, - - handleViewChange : function(newView){ - this.props.setMoveArrows(newView === 'text'); - - this.setState({ - view : newView - }, ()=>{ - this.codeEditor.current?.focus(); - }); - }, - - brewJump : function(targetPage=this.props.currentEditorCursorPageNum, smooth=true){ - if(!window || !this.isText() || isJumping || jumpSource === 'source') - return; - - // Get current brewRenderer scroll position and calculate target position - const brewRenderer = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0]; - const currentPos = brewRenderer.scrollTop; - const targetPos = window.frames['BrewRenderer'].contentDocument.getElementById(`p${targetPage}`).getBoundingClientRect().top; - - let scrollingTimeout; - const checkIfScrollComplete = ()=>{ // Prevent interrupting a scroll in progress if user clicks multiple times - clearTimeout(scrollingTimeout); // Reset the timer every time a scroll event occurs - scrollingTimeout = setTimeout(()=>{ - isJumping = false; - jumpSource = null; - brewRenderer.removeEventListener('scroll', checkIfScrollComplete); - }, 150); // If 150 ms pass without a brewRenderer scroll event, assume scrolling is done - }; - - isJumping = true; - jumpSource = 'brew'; - checkIfScrollComplete(); - brewRenderer.addEventListener('scroll', checkIfScrollComplete); - - if(smooth) { - const bouncePos = targetPos >= 0 ? -30 : 30; //Do a little bounce before scrolling - const bounceDelay = 100; - const scrollDelay = 500; - - if(!this.throttleBrewMove) { - this.throttleBrewMove = _.throttle((currentPos, bouncePos, targetPos)=>{ - brewRenderer.scrollTo({ top: currentPos + bouncePos, behavior: 'smooth' }); - setTimeout(()=>{ - brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'smooth', block: 'start' }); - }, bounceDelay); - }, scrollDelay, { leading: true, trailing: false }); - }; - this.throttleBrewMove(currentPos, bouncePos, targetPos); - } else { - brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'instant', block: 'start' }); - } - }, - - sourceJump : function(targetPage=this.props.currentBrewRendererPageNum, smooth=true){ - if(!this.isText() || isJumping || jumpSource === 'brew') - return; - - const editor = this.codeEditor.current; - if(!editor) return; - jumpSource = 'source'; - - editor.scrollToPage(targetPage); - setTimeout(()=>{ - jumpSource = null; - }, 200); - }, - - //Called when there are changes to the editor's dimensions - update : function(){}, - - updateEditorTheme : function(newTheme){ - window.localStorage.setItem(EDITOR_THEME_KEY, newTheme); - this.setState({ - editorTheme : newTheme - }); - }, - - //Called by CodeEditor after document switch, so Snippetbar can refresh UndoHistory - rerenderParent : function (){ - this.forceUpdate(); - }, - - renderEditor : function(){ - if(this.isText()){ - return <> - this.updateCurrentCursorPage(page)} - onViewChange={(page)=>this.updateCurrentViewPage(page)} - editorTheme={this.state.editorTheme} - renderer={this.props.brew.renderer} - style={{ height: `calc(100% - ${this.state.snippetBarHeight}px)` }}/> - ; - } - if(this.isStyle()){ - return <> - - ; - } - if(this.isMeta()){ - return <> - - - ; - } - if(this.isSnip()){ - if(!this.props.brew.snippets) { this.props.brew.snippets = DEFAULT_SNIPPET_TEXT; } - return <> - - ; - } - }, - - redo : function(){ - return this.codeEditor.current?.redo(); - }, - - historySize : function(){ - return this.codeEditor.current?.historySize(); - }, - - undo : function(){ - return this.codeEditor.current?.undo(); - }, - - foldCode : function() { - return this.codeEditor.current?.foldAll(); - }, - - unfoldCode : function() { - return this.codeEditor.current?.unfoldAll(); - }, - render : function(){ - return ( -
- - - {this.renderEditor()} -
- ); - } -}); - -export default Editor; \ No newline at end of file