From b9ca8ef3373830797d07a07fd675e1de41d0164d Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 20 Oct 2026 23:37:41 +1100 Subject: [PATCH 01/13] escalate 5px padding remove from .brewItem . text .description add to .brewItem .text --- .../pages/basePages/listPage/brewItem/brewItem.less | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.less b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.less index 4b3751ca3..d091f636f 100644 --- a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.less +++ b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.less @@ -145,8 +145,8 @@ box-shadow : 0 4px 5px 0 #333333; .text { min-height : 54px; + padding-left : 5px; h2 { margin-bottom : 5px; } - .description { padding-left : 5px; } } .info { padding-left : 5px; } .links { @@ -398,7 +398,7 @@ } .storage { display : none; } } - .brewItem { + .brewItem { h2 { width : 200px; font-size : 1em; @@ -426,7 +426,7 @@ } .info { display : grid; - grid-template-areas : + grid-template-areas : 'tags pages views views' 'authors update update storage'; grid-template-columns : 2fr 1fr 1fr 20px; @@ -440,7 +440,7 @@ a, button { margin : 0; } } - } + } } } @container (max-width : 650px) { From 503bc5b542998d7ac2950d331ef7a260e9840d92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 03:23:39 +0000 Subject: [PATCH 02/13] Bump @googleapis/drive from 26.0.2 to 27.0.0 Bumps [@googleapis/drive](https://github.com/googleapis/google-api-nodejs-client) from 26.0.2 to 27.0.0. - [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases) - [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/drive-v26.0.2...v27.0.0) --- updated-dependencies: - dependency-name: "@googleapis/drive" dependency-version: 27.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 30446043f..d0333bd8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@codemirror/search": "^6.6.0", "@codemirror/state": "^6.7.6", "@codemirror/view": "^6.43.15", - "@googleapis/drive": "^26.0.2", + "@googleapis/drive": "^27.0.0", "@lezer/highlight": "^1.2.4", "@oddbird/css-anchor-positioning": "^0.10.2", "@sanity/diff-match-patch": "^3.2.1", @@ -2559,9 +2559,9 @@ "license": "MIT" }, "node_modules/@googleapis/drive": { - "version": "26.0.2", - "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-26.0.2.tgz", - "integrity": "sha512-VeKVZvzgH4ioczJZrZ5sy73RJbHrYLI1XZIZ3ZqGKQhC4U03XsOOOiBHKqvH4F9vbXrxR2LawmktoIJuTJhzYg==", + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-27.0.0.tgz", + "integrity": "sha512-q+K1+T4Kbz3VUOww15lhV+jq+/IzuxzkfiASIXzxVG6X894QUPdhBgU9hKFNSKh+e58o0g6mX/06XoLsS1JE6Q==", "license": "Apache-2.0", "dependencies": { "googleapis-common": "^9.0.0" diff --git a/package.json b/package.json index 2eee5c8d5..d4949081b 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "@codemirror/search": "^6.6.0", "@codemirror/state": "^6.7.6", "@codemirror/view": "^6.43.15", - "@googleapis/drive": "^26.0.2", + "@googleapis/drive": "^27.0.0", "@lezer/highlight": "^1.2.4", "@oddbird/css-anchor-positioning": "^0.10.2", "@sanity/diff-match-patch": "^3.2.1", From 9971d2ae5905de406daf4e562bdcea9c720d1846 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 27 Oct 2026 15:12:02 +1100 Subject: [PATCH 03/13] walk directories only MacOS will sometimes drop a `.DS_Store` file into these folders, and then the build fails with /themes/Legacy/.DS_Store/settings.json is not a valid path. --- vitePlugins/generateAssetsPlugin.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/vitePlugins/generateAssetsPlugin.js b/vitePlugins/generateAssetsPlugin.js index 7eef8bda5..0cd31d162 100644 --- a/vitePlugins/generateAssetsPlugin.js +++ b/vitePlugins/generateAssetsPlugin.js @@ -28,8 +28,10 @@ export function generateAssetsPlugin(isDev = false) { // Compile Legacy themes const themes = { Legacy: {}, V3: {} }; - const legacyDirs = fs.readdirSync('./themes/Legacy'); - for (const dir of legacyDirs) { + const legacyDirs = fs.readdirSync('./themes/Legacy', { withFileTypes: true }) + .filter(entry => entry.isDirectory()) + .map(entry => entry.name); + for (const dir of legacyDirs) { const themeData = JSON.parse(fs.readFileSync(`./themes/Legacy/${dir}/settings.json`, 'utf-8')); themeData.path = dir; themes.Legacy[dir] = themeData; @@ -41,7 +43,9 @@ export function generateAssetsPlugin(isDev = false) { } // Compile V3 themes - const v3Dirs = fs.readdirSync('./themes/V3'); + const v3Dirs = fs.readdirSync('./themes/V3', { withFileTypes: true }) + .filter(entry => entry.isDirectory()) + .map(entry => entry.name); for (const dir of v3Dirs) { const themeData = JSON.parse(fs.readFileSync(`./themes/V3/${dir}/settings.json`, 'utf-8')); themeData.path = dir; From b33b3f7c7f500f9dd7dd122d645128199ddc9af5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 21:06:01 +0000 Subject: [PATCH 04/13] Bump fast-uri from 3.1.8 to 3.1.9 Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.8 to 3.1.9. - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](https://github.com/fastify/fast-uri/compare/v3.1.8...v3.1.9) --- updated-dependencies: - dependency-name: fast-uri dependency-version: 3.1.9 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d0333bd8f..4c093179f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7660,9 +7660,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", - "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.9.tgz", + "integrity": "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==", "dev": true, "funding": [ { From 302373e19c6981401e901f094a268e7c6f3c8bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Thu, 29 Oct 2026 14:55:44 +0100 Subject: [PATCH 05/13] basic refactor --- client/components/combobox.jsx | 200 ++++++++++++++++----------------- 1 file changed, 97 insertions(+), 103 deletions(-) diff --git a/client/components/combobox.jsx b/client/components/combobox.jsx index 8f6f4ead9..036494224 100644 --- a/client/components/combobox.jsx +++ b/client/components/combobox.jsx @@ -1,117 +1,113 @@ -import React from 'react'; -import createReactClass from 'create-react-class'; +import React, { useState, useEffect, useRef } from 'react'; import _ from 'lodash'; import './combobox.less'; -const Combobox = createReactClass({ - displayName : 'Combobox', - getDefaultProps : function() { - return { - id : '', - className : '', - trigger : 'hover', - default : '', - placeholder : '', - tooltip : '', - autoSuggest : { - clearAutoSuggestOnClick : true, - suggestMethod : 'includes', - filterOn : [] // should allow as array to filter on multiple attributes, or even custom filter - }, - valuePatterns : /.+/ - }; +const Combobox = ({ + id = '', + className = '', + trigger = 'hover', + default: defaultValue = '', + placeholder = '', + tooltip = '', + autoSuggest = { + clearAutoSuggestOnClick : true, + suggestMethod : 'includes', + filterOn : [] // should allow as array to filter on multiple attributes, or even custom filter }, - getInitialState : function() { - this.dropdownRef = React.createRef(); - return { - showDropdown : false, - value : '', - options : [...this.props.options], - inputFocused : false - }; - }, - componentDidMount : function() { - if(this.props.trigger == 'click') - document.addEventListener('click', this.handleClickOutside); - this.setState({ - value : this.props.default - }); - }, - componentWillUnmount : function() { - if(this.props.trigger == 'click') - document.removeEventListener('click', this.handleClickOutside); - }, - handleClickOutside : function(e){ - // Close dropdown when clicked outside - if(this.dropdownRef.current && !this.dropdownRef.current.contains(e.target)) { - this.handleDropdown(false); + valuePatterns = /.+/, + ...props +})=>{ + + const dropdownRef = useRef(null); + const [showDropdown, setShowDropdown] = useState(false); + const [value, setValue] = useState(''); + const [options, setOptions] = useState([...props.options]); + const [inputFocused, setInputFocused] = useState(false); + + + useEffect(()=>{ + if(trigger == 'click') { + document.addEventListener('click', handleClickOutside); } - }, - handleDropdown : function(show){ - this.setState({ - value : show ? '' : this.props.default, - showDropdown : show, - inputFocused : this.props.autoSuggest.clearAutoSuggestOnClick ? show : false - }); - }, - handleInput : function(e){ - e.persist(); - this.setState({ - value : e.target.value, - inputFocused : false - }, ()=>{ - this.props.onEntry(e); - }); - }, - handleSelect : function(value, data=value){ - this.setState({ - value : value - }, ()=>{this.props.onSelect(data);}); - ; - }, - renderTextInput : function(){ + setValue(defaultValue); + + return ()=>{ + if(trigger == 'click') { + document.removeEventListener('click', handleClickOutside); + } + }; + }, []); + + const handleClickOutside = (e)=>{ + // Close dropdown when clicked outside + if(dropdownRef.current && !dropdownRef.current.contains(e.target)) { + handleDropdown(false); + } + }; + + const handleDropdown = (show)=>{ + setValue(show ? '' : defaultValue); + setShowDropdown(show); + setInputFocused(autoSuggest.clearAutoSuggestOnClick ? show : false); + }; + + const handleInput = (e)=>{ + setValue(e.target.value); + setInputFocused(false); + props.onEntry(e); + }; + + const handleSelect = (value, data=value)=>{ + setValue(value); + props.onSelect(data); + }; + + const renderTextInput = ()=>{ return (
{this.handleDropdown(true);} : undefined} - onClick= {this.props.trigger == 'click' ? ()=>{this.handleDropdown(true);} : undefined} - {...(this.props.tooltip ? { 'data-tooltip-right': this.props.tooltip } : {})}> + onMouseEnter={trigger == 'hover' ? ()=>{handleDropdown(true);} : undefined} + onClick= {trigger == 'click' ? ()=>{handleDropdown(true);} : undefined} + {...(tooltip ? { 'data-tooltip-right': tooltip } : {})}> this.handleInput(e)} - value={this.state.value || ''} + onChange={(e)=>handleInput(e)} + value={value || ''} title='' - pattern={this.props.valuePatterns} - placeholder={this.props.placeholder} + pattern={valuePatterns} + placeholder={placeholder} onBlur={(e)=>{ if(!e.target.checkValidity()){ - this.setState({ - value : this.props.default - }); + setValue(defaultValue); } }} onKeyDown={(e)=>{ if(e.key === 'Enter') { e.preventDefault(); - this.props.onEntry(e); + props.onEntry(e); } }} />
); - }, - renderDropdown : function(dropdownChildren){ - if(!this.state.showDropdown) return null; - if(this.props.autoSuggest && !this.state.inputFocused){ - const suggestMethod = this.props.autoSuggest.suggestMethod; - const filterOn = _.isString(this.props.autoSuggest.filterOn) ? [this.props.autoSuggest.filterOn] : this.props.autoSuggest.filterOn; + }; + + const renderDropdown = ()=>{ + if(!showDropdown) return null; + const dropdownChildren = options.map((child, i)=>{ + const clone = React.cloneElement(child, { onClick: ()=>handleSelect(child.props.value, child.props.data) }); + return clone; + }); + if(autoSuggest && !inputFocused){ + const suggestMethod = autoSuggest.suggestMethod; + const filterOn = _.isString(autoSuggest.filterOn) ? [autoSuggest.filterOn] : autoSuggest.filterOn; const filteredArrays = filterOn.map((attr)=>{ const children = dropdownChildren.filter((item)=>{ if(suggestMethod === 'includes') - return item.props[attr]?.toLowerCase().includes(this.state.value.toLowerCase()); + return item.props[attr]?.toLowerCase().includes(value.toLowerCase()); if(suggestMethod === 'startsWith') - return item.props[attr]?.toLowerCase().startsWith(this.state.value.toLowerCase()); + return item.props[attr]?.toLowerCase().startsWith(value.toLowerCase()); }); return children; }); @@ -123,21 +119,19 @@ const Combobox = createReactClass({ {dropdownChildren} ); - }, - render : function () { - const dropdownChildren = this.state.options.map((child, i)=>{ - const clone = React.cloneElement(child, { onClick: ()=>this.handleSelect(child.props.value, child.props.data) }); - return clone; - }); - return ( -
{this.handleDropdown(false);} : undefined}> - {this.renderTextInput()} - {this.renderDropdown(dropdownChildren)} -
- ); - } -}); + }; + + + return ( +
{handleDropdown(false);} : undefined}> + {renderTextInput()} + {renderDropdown()} +
+ ); + + +}; export default Combobox; From 8ee317e8c4e6fb7aab77e4d4dfb5a2df3c5c89b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Thu, 29 Oct 2026 14:56:35 +0100 Subject: [PATCH 06/13] linting --- client/components/combobox.jsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/components/combobox.jsx b/client/components/combobox.jsx index 036494224..1bb36d4a3 100644 --- a/client/components/combobox.jsx +++ b/client/components/combobox.jsx @@ -24,7 +24,6 @@ const Combobox = ({ const [options, setOptions] = useState([...props.options]); const [inputFocused, setInputFocused] = useState(false); - useEffect(()=>{ if(trigger == 'click') { document.addEventListener('click', handleClickOutside); @@ -121,7 +120,6 @@ const Combobox = ({ ); }; - return (
); - }; export default Combobox; From f38d20540fa9c76b121e805fdb5c1eb4e31e7974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Thu, 29 Oct 2026 15:05:44 +0100 Subject: [PATCH 07/13] simplify as possible --- client/components/combobox.jsx | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/client/components/combobox.jsx b/client/components/combobox.jsx index 1bb36d4a3..497634c19 100644 --- a/client/components/combobox.jsx +++ b/client/components/combobox.jsx @@ -15,33 +15,24 @@ const Combobox = ({ filterOn : [] // should allow as array to filter on multiple attributes, or even custom filter }, valuePatterns = /.+/, + options, ...props })=>{ - const dropdownRef = useRef(null); const [showDropdown, setShowDropdown] = useState(false); - const [value, setValue] = useState(''); - const [options, setOptions] = useState([...props.options]); const [inputFocused, setInputFocused] = useState(false); + const [value, setValue] = useState(defaultValue); useEffect(()=>{ - if(trigger == 'click') { + if(trigger === 'click') { document.addEventListener('click', handleClickOutside); + return ()=>document.removeEventListener('click', handleClickOutside); } - setValue(defaultValue); - - return ()=>{ - if(trigger == 'click') { - document.removeEventListener('click', handleClickOutside); - } - }; }, []); const handleClickOutside = (e)=>{ // Close dropdown when clicked outside - if(dropdownRef.current && !dropdownRef.current.contains(e.target)) { - handleDropdown(false); - } + if(!dropdownRef.current?.contains(e.target)) handleDropdown(false); }; const handleDropdown = (show)=>{ @@ -112,7 +103,6 @@ const Combobox = ({ }); dropdownChildren = _.uniq(filteredArrays.flat(1)); } - return (
{dropdownChildren} From 4ec73938b425f8bcd8b96dbb81d7fe243e834150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Thu, 29 Oct 2026 15:08:11 +0100 Subject: [PATCH 08/13] further simplification --- client/components/combobox.jsx | 4 ++-- client/homebrew/editor/metadataEditor/metadataEditor.jsx | 4 ++-- client/homebrew/editor/tagInput/tagInput.jsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/components/combobox.jsx b/client/components/combobox.jsx index 497634c19..5f160d112 100644 --- a/client/components/combobox.jsx +++ b/client/components/combobox.jsx @@ -6,7 +6,7 @@ const Combobox = ({ id = '', className = '', trigger = 'hover', - default: defaultValue = '', + defaultValue = '', placeholder = '', tooltip = '', autoSuggest = { @@ -15,7 +15,7 @@ const Combobox = ({ filterOn : [] // should allow as array to filter on multiple attributes, or even custom filter }, valuePatterns = /.+/, - options, + options = [], ...props })=>{ const dropdownRef = useRef(null); diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.jsx b/client/homebrew/editor/metadataEditor/metadataEditor.jsx index d5c4387e9..518cc0ac8 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.jsx +++ b/client/homebrew/editor/metadataEditor/metadataEditor.jsx @@ -219,7 +219,7 @@ const MetadataEditor = ({ handleTheme(value)} onEntry={(e)=>{ @@ -261,7 +261,7 @@ const MetadataEditor = ({ handleLanguage(value)} onEntry={(e)=>{ diff --git a/client/homebrew/editor/tagInput/tagInput.jsx b/client/homebrew/editor/tagInput/tagInput.jsx index a153fef8b..ede194834 100644 --- a/client/homebrew/editor/tagInput/tagInput.jsx +++ b/client/homebrew/editor/tagInput/tagInput.jsx @@ -131,7 +131,7 @@ const TagInput = ({ id, tooltip, label, valuePatterns, values = [], unique = tru id={id} trigger='click' className='tagInput-dropdown' - default='' + defaultValue='' placeholder={placeholder} options={label === 'tags' ? suggestionOptions : []} tooltip={tooltip} From 9b02d74fb1e58f619d6012406e0f05f56e93f506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Thu, 29 Oct 2026 15:13:05 +0100 Subject: [PATCH 09/13] fix build --- client/components/combobox.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/components/combobox.jsx b/client/components/combobox.jsx index 5f160d112..f78cbe9a2 100644 --- a/client/components/combobox.jsx +++ b/client/components/combobox.jsx @@ -85,7 +85,7 @@ const Combobox = ({ const renderDropdown = ()=>{ if(!showDropdown) return null; - const dropdownChildren = options.map((child, i)=>{ + let dropdownChildren = options.map((child, i)=>{ const clone = React.cloneElement(child, { onClick: ()=>handleSelect(child.props.value, child.props.data) }); return clone; }); From 844fb2f055f5cf5c697a03f7cb4fe85105bd07ee Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 29 Oct 2026 20:40:21 +0000 Subject: [PATCH 10/13] Lint listpage --- .../pages/basePages/listPage/listPage.jsx | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/client/homebrew/pages/basePages/listPage/listPage.jsx b/client/homebrew/pages/basePages/listPage/listPage.jsx index 802c3a14a..bbc35c216 100644 --- a/client/homebrew/pages/basePages/listPage/listPage.jsx +++ b/client/homebrew/pages/basePages/listPage/listPage.jsx @@ -187,7 +187,7 @@ const ListPage = ({ brewCollection = [{ title: '', class: '', brews: [] }], navI if(filterTags?.length == 0) return; return (
- Tags: + Tags: {_.map(filterTags, (tag, idx)=>{ const matches = tag.match(/^(?:([^:]+):)?([^:]+)$/); return ( @@ -198,7 +198,7 @@ const ListPage = ({ brewCollection = [{ title: '', class: '', brews: [] }], navI updateUrl(filterString, sortType, sortDir, tag); }}> {matches[2]} - + ); })}
@@ -298,8 +298,8 @@ const ListPage = ({ brewCollection = [{ title: '', class: '', brews: [] }], navI
View: