Dev - #1134
Dev#1134
Conversation
- Add version-bump workflow for semantic versioning across all files - Add beta-release workflow for automated pre-release testing - Add production-release workflow with manual approval gates - Add hotfix-release workflow for emergency patches - Create comprehensive CONTRIBUTING.md with workflow guide - Create detailed RELEASE_PROCESS.md for maintainers - Add PR template with release checklists - Update CODEOWNERS to protect workflow files - Update README with contribution links - Remove /docs from .gitignore to allow documentation This implements a dev beta main branching strategy with: - Automated version management across 6 files - Changelog generation from conventional commits - GitHub Releases with build artifacts - Environment-based approvals for production - Back-merge support for hotfixes
* fix(workflows): prevent beta release for non-beta versions * fix(workflows): address copilot PR review feedback - Support iterative beta versions (7.6.0-beta.1 -> 7.6.0-beta.2) - Remove tag trigger from beta workflow to prevent premature releases - Fix tag format in docs/summaries to include 'v' prefix - Clarify deployment approval wording
… Checkbox, Dropdown, Radio, Slider, and Text
…location reset functionality
…z-index adjustments
Signed-off-by: Alex Sparkes <alexsparkes@gmail.com>
Deploying mue with
|
| Latest commit: |
ce6b05f
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://95219183.mue.pages.dev |
| Branch Preview URL: | https://dev.mue.pages.dev |
There was a problem hiding this comment.
Pull request overview
This pull request removes Material-UI (MUI) dependencies and replaces them with custom-built form components, fixes a clock padding issue with zero digits in localized numerals, migrates from the deprecated 'api' quote type to 'quote_pack', and bumps the version to 7.6.0. The PR also adds comprehensive release process documentation and GitHub Actions workflows for version management.
Changes:
- Removed all MUI dependencies (@mui/material, @emotion/react, @emotion/styled, embla-carousel packages) and replaced with custom-built components (Checkbox, Radio, Switch, Dropdown, Slider, Textarea, SearchInput, ChipSelect)
- Fixed clock display issue where padded zeros weren't being formatted correctly for locales with custom numeral systems
- Deprecated 'api' quote type in favor of 'quote_pack' with automatic migration logic and default pack installation
- Added release process documentation (CONTRIBUTING.md, RELEASE_PROCESS.md) and GitHub Actions workflows for version bumping, beta releases, production releases, and hotfixes
Reviewed changes
Copilot reviewed 49 out of 50 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| package.json, bun.lock | Removed MUI and related dependencies, bumped version to 7.6.0 |
| src/components/Form/Settings/* | New custom form components replacing MUI components with full styling |
| src/features/time/Clock.jsx | Fixed padding issue with formatPaddedDigits helper for locale-specific numerals |
| src/features/quote/* | Migrated from 'api' to 'quote_pack' type with backward compatibility |
| src/features/misc/modals/Modals.jsx | Added default pack auto-installation logic |
| src/utils/marketplace/uninstall.js | Added tracking of uninstalled packs to prevent auto-reinstall |
| src/features/marketplace/* | Updated marketplace UI with uninstall buttons, sideload badges, and improved sorting |
| src/scss/_toast.scss | Complete redesign with glassmorphism effects |
| docs/, .github/workflows/ | Added comprehensive release documentation and automation workflows |
| manifest/, safari/ | Version bumped to 7.6.0 across all platforms |
| // Helper function to format padded time values while preserving padding | ||
| const formatPaddedDigits = (value) => { | ||
| const str = String(value); | ||
| // Format each digit individually to preserve padding with locale numerals | ||
| return str.split('').map(digit => formatDigits(digit)).join(''); | ||
| }; |
There was a problem hiding this comment.
The PR description mentions "fixed issue with 0 padding on clock" but the change from formatDigits to formatPaddedDigits affects all padded values (hours, minutes, seconds) when they contain '0' digits. The formatPaddedDigits function formats each digit individually, which is correct for preserving locale-specific numerals for '0'. However, ensure this works correctly with all locales and doesn't break RTL languages or locales with different numeral systems.
| const [quoteType, setQuoteType] = useState(() => { | ||
| let type = localStorage.getItem('quoteType') || 'quote_pack'; | ||
| // Migrate deprecated 'api' type to 'quote_pack' | ||
| if (type === 'api') { | ||
| type = 'quote_pack'; | ||
| localStorage.setItem('quoteType', 'quote_pack'); | ||
| } | ||
| return type; | ||
| }); |
There was a problem hiding this comment.
The default value fallback logic has changed from 'api' to 'quote_pack', but the migration only happens in the useState initialization and in getQuote. If quoteType is accessed or set elsewhere in the codebase without going through these code paths, the old 'api' value could persist. Consider adding a migration check in localStorage directly on application initialization to ensure all users are migrated.
| <div | ||
| className={'todo-checkbox' + (todoItem.done ? ' checked' : '')} | ||
| onClick={() => updateTodo('done', index)} | ||
| /> | ||
| <TextareaAutosize | ||
| > | ||
| {todoItem.done && <MdCheck />} | ||
| </div> |
There was a problem hiding this comment.
The todo-checkbox class is used in the JSX but there are no corresponding styles defined in the codebase for this class. This will result in an unstyled checkbox element. The styles for .todo-checkbox and .todo-checkbox.checked need to be added to properly display the checkbox.
| .items { | ||
| display: grid; | ||
| grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); | ||
| grid-template-columns: repeat(auto-fill, minmax(250px, 280px)); |
There was a problem hiding this comment.
The grid layout has changed from 'repeat(auto-fit, minmax(250px, 1fr))' to 'repeat(auto-fill, minmax(250px, 280px))'. This changes the behavior significantly: auto-fill creates as many columns as can fit (leaving empty space), while auto-fit stretches items to fill available space. Also, the max width is now fixed at 280px instead of 1fr (flexible). This could result in awkward gaps on larger screens. Consider whether auto-fit with 1fr was the intended behavior.
| grid-template-columns: repeat(auto-fill, minmax(250px, 280px)); | |
| grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); |
| return ( | ||
| <FormControlLabel | ||
| control={ | ||
| <SwitchUI | ||
| name={props.name} | ||
| color="primary" | ||
| checked={checked} | ||
| onChange={handleChange} | ||
| /> | ||
| } | ||
| label={props.header ? '' : props.text} | ||
| labelPlacement="start" | ||
| /> | ||
| <div className="switch-wrapper"> | ||
| {!props.header && <span className="switch-label">{props.text}</span>} | ||
| <div className={`switch-track ${checked ? 'checked' : ''}`} onClick={handleChange}> | ||
| <div className="switch-thumb" /> | ||
| </div> | ||
| <input | ||
| type="checkbox" | ||
| name={props.name} | ||
| checked={checked} | ||
| onChange={handleChange} | ||
| className="switch-input" | ||
| aria-hidden="true" | ||
| /> | ||
| </div> |
There was a problem hiding this comment.
The Switch component has aria-hidden="true" on the input element, which hides it from screen readers. However, the switch-track div that handles the click has no ARIA attributes and is not keyboard accessible. This makes the switch completely inaccessible to keyboard and screen reader users. Consider: 1) Removing aria-hidden from the input, 2) Adding proper ARIA role and labels to the wrapper, 3) Making the switch keyboard accessible with proper focus management and keyboard event handlers.
| const handleChange = useCallback( | ||
| async (newValue) => { | ||
| if (newValue === 'loading') { | ||
| return; | ||
| } | ||
|
|
||
| if (newValue === 'loading') { | ||
| return; | ||
| } | ||
| if (props.name === 'language') { | ||
| changeLanguage(newValue); | ||
| setValue(newValue); | ||
|
|
||
| if (props.name === 'language') { | ||
| // Use context to change language directly - no EventBus needed | ||
| changeLanguage(newValue); | ||
| setValue(newValue); | ||
| variables.stats.postEvent('setting', `${props.name} from ${value} to ${newValue}`); | ||
|
|
||
| variables.stats.postEvent('setting', `${props.name} from ${value} to ${newValue}`); | ||
| if (props.onChange) { | ||
| props.onChange(newValue); | ||
| } | ||
|
|
||
| if (props.onChange) { | ||
| props.onChange(newValue); | ||
| EventBus.emit('refresh', props.category); | ||
| return; | ||
| } | ||
|
|
||
| EventBus.emit('refresh', props.category); | ||
| return; | ||
| } | ||
|
|
||
| localStorage.setItem(props.name, newValue); | ||
| setValue(newValue); | ||
| localStorage.setItem(props.name, newValue); | ||
| setValue(newValue); | ||
|
|
||
| if (props.onChange) { | ||
| props.onChange(newValue); | ||
| } | ||
| if (props.onChange) { | ||
| props.onChange(newValue); | ||
| } | ||
|
|
||
| variables.stats.postEvent('setting', `${props.name} from ${value} to ${newValue}`); | ||
| variables.stats.postEvent('setting', `${props.name} from ${value} to ${newValue}`); | ||
|
|
||
| if (props.element) { | ||
| if (!document.querySelector(props.element)) { | ||
| document.querySelector('.reminder-info').style.display = 'flex'; | ||
| return localStorage.setItem('showReminder', true); | ||
| if (props.element) { | ||
| if (!document.querySelector(props.element)) { | ||
| document.querySelector('.reminder-info').style.display = 'flex'; | ||
| return localStorage.setItem('showReminder', true); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| EventBus.emit('refresh', props.category); | ||
| }, [value, props, changeLanguage]); | ||
| EventBus.emit('refresh', props.category); | ||
| }, | ||
| [value, props, changeLanguage], | ||
| ); | ||
|
|
||
| return ( | ||
| <FormControl component="fieldset"> | ||
| <FormLabel | ||
| className={props.smallTitle ? 'radio-title-small' : 'radio-title'} | ||
| component="legend" | ||
| > | ||
| {props.title} | ||
| </FormLabel> | ||
| <RadioGroup | ||
| aria-label={props.name} | ||
| name={props.name} | ||
| onChange={handleChange} | ||
| value={value} | ||
| > | ||
| <div className="radio-group"> | ||
| {props.title && ( | ||
| <legend className={props.smallTitle ? 'radio-title-small' : 'radio-title'}> | ||
| {props.title} | ||
| </legend> | ||
| )} | ||
| <div className="radio-options" role="radiogroup" aria-label={props.name}> | ||
| {props.options.map((option) => ( | ||
| <FormControlLabel | ||
| value={option.value} | ||
| control={<RadioUI />} | ||
| label={option.name} | ||
| <label | ||
| key={option.value} | ||
| /> | ||
| className={`radio-option ${value === option.value ? 'selected' : ''} ${option.disabled || props.disabled ? 'disabled' : ''}`} | ||
| > | ||
| <span className="radio-label">{option.name}</span> | ||
| <input | ||
| type="radio" | ||
| name={props.name} | ||
| value={option.value} | ||
| checked={value === option.value} | ||
| onChange={() => handleChange(option.value)} |
There was a problem hiding this comment.
The handleChange function is called with newValue directly instead of event.target.value. This is inconsistent with the previous implementation and could cause issues if there are any onChange handlers expecting an event object. Consider maintaining backward compatibility by checking if the parameter is an event object before extracting the value.
| <Textarea | ||
| placeholder={t('widgets.navbar.notes.placeholder')} | ||
| value={notes} | ||
| onChange={handleSetNotes} | ||
| minRows={5} | ||
| maxLength={10000} | ||
| /> |
There was a problem hiding this comment.
The maxLength prop has been removed from the Textarea component. If this was intentional to allow unlimited input, this is fine. However, if there was a specific reason for the 10000 character limit (e.g., storage constraints, performance), this should be re-added to the custom Textarea component or handled differently.
| <textarea | ||
| ref={textareaRef} | ||
| className={`textarea-autosize${className ? ` ${className}` : ''}`} | ||
| value={value} | ||
| onChange={onChange} | ||
| placeholder={placeholder} | ||
| style={style} | ||
| readOnly={readOnly} | ||
| rows={minRows} | ||
| /> |
There was a problem hiding this comment.
The Textarea component is missing support for the maxLength property that was previously available in MUI's TextareaAutosize. This property is used in Notes.jsx (removed on line 119). If character limits are needed for any textarea usage, the component should accept and pass through the maxLength prop to the native textarea element.
| const animate = (currentTime) => { | ||
| const elapsed = currentTime - startTime; | ||
| const progress = Math.min(elapsed / duration, 1); | ||
|
|
||
| // Easing function for smooth animation | ||
| const easeOutCubic = 1 - Math.pow(1 - progress, 3); | ||
|
|
||
| const currentValue = startValue + (endValue - startValue) * easeOutCubic; | ||
| const roundedValue = Math.round(currentValue / (Number(props.step) || 1)) * (Number(props.step) || 1); | ||
|
|
||
| localStorage.setItem(props.name, roundedValue); | ||
| setValue(roundedValue); | ||
|
|
||
| if (progress < 1) { | ||
| animationRef.current = requestAnimationFrame(animate); | ||
| } else { | ||
| // Ensure we end exactly at the target value | ||
| localStorage.setItem(props.name, endValue); | ||
| setValue(endValue); | ||
| EventBus.emit('refresh', props.category); | ||
| } |
There was a problem hiding this comment.
The reset animation emits the 'refresh' event only at the end of the animation (line 71), but also updates localStorage during each animation frame (line 62). This could cause a performance issue if the refresh event triggers expensive operations. The previous implementation called refresh immediately. If components are listening to localStorage changes separately, they might update during the animation causing janky UI. Consider debouncing the refresh event or only updating localStorage at the end of the animation.
| const resetItem = useCallback(() => { | ||
| handleChange({ | ||
| target: { | ||
| value: props.default || '', | ||
| }, | ||
| }); | ||
| if (animationRef.current) { | ||
| cancelAnimationFrame(animationRef.current); | ||
| } | ||
|
|
||
| const startValue = Number(value); | ||
| const endValue = Number(props.default || 0); | ||
| const duration = 300; // milliseconds | ||
| const startTime = performance.now(); | ||
|
|
||
| const animate = (currentTime) => { | ||
| const elapsed = currentTime - startTime; | ||
| const progress = Math.min(elapsed / duration, 1); | ||
|
|
||
| // Easing function for smooth animation | ||
| const easeOutCubic = 1 - Math.pow(1 - progress, 3); | ||
|
|
||
| const currentValue = startValue + (endValue - startValue) * easeOutCubic; | ||
| const roundedValue = Math.round(currentValue / (Number(props.step) || 1)) * (Number(props.step) || 1); | ||
|
|
||
| localStorage.setItem(props.name, roundedValue); | ||
| setValue(roundedValue); | ||
|
|
||
| if (progress < 1) { | ||
| animationRef.current = requestAnimationFrame(animate); | ||
| } else { | ||
| // Ensure we end exactly at the target value | ||
| localStorage.setItem(props.name, endValue); | ||
| setValue(endValue); | ||
| EventBus.emit('refresh', props.category); | ||
| } | ||
| }; | ||
|
|
||
| animationRef.current = requestAnimationFrame(animate); | ||
| toast(variables.getMessage('toasts.reset')); | ||
| }, [handleChange, props.default]); | ||
| }, [value, props]); |
There was a problem hiding this comment.
The component is missing a cleanup for the animation when the component unmounts. If the user navigates away while the reset animation is running, the animationRef.current will still be scheduled and could cause a memory leak or errors when trying to update state on an unmounted component. Add a useEffect cleanup that cancels the animation frame on unmount.
* feat: add professional three-branch release workflow automation (#1129) (#1130) - Add version-bump workflow for semantic versioning across all files - Add beta-release workflow for automated pre-release testing - Add production-release workflow with manual approval gates - Add hotfix-release workflow for emergency patches - Create comprehensive CONTRIBUTING.md with workflow guide - Create detailed RELEASE_PROCESS.md for maintainers - Add PR template with release checklists - Update CODEOWNERS to protect workflow files - Update README with contribution links - Remove /docs from .gitignore to allow documentation This implements a dev beta main branching strategy with: - Automated version management across 6 files - Changelog generation from conventional commits - GitHub Releases with build artifacts - Environment-based approvals for production - Back-merge support for hotfixes * feat: new default quotes experience, improve added page * Sync/workflow fixes to beta (#1132) * feat: add professional three-branch release workflow automation (#1129) - Add version-bump workflow for semantic versioning across all files - Add beta-release workflow for automated pre-release testing - Add production-release workflow with manual approval gates - Add hotfix-release workflow for emergency patches - Create comprehensive CONTRIBUTING.md with workflow guide - Create detailed RELEASE_PROCESS.md for maintainers - Add PR template with release checklists - Update CODEOWNERS to protect workflow files - Update README with contribution links - Remove /docs from .gitignore to allow documentation This implements a dev beta main branching strategy with: - Automated version management across 6 files - Changelog generation from conventional commits - GitHub Releases with build artifacts - Environment-based approvals for production - Back-merge support for hotfixes * fix(workflows): prevent beta release for non-beta versions * Fix/beta workflow version check (#1131) * fix(workflows): prevent beta release for non-beta versions * fix(workflows): address copilot PR review feedback - Support iterative beta versions (7.6.0-beta.1 -> 7.6.0-beta.2) - Remove tag trigger from beta workflow to prevent premature releases - Fix tag format in docs/summaries to include 'v' prefix - Clarify deployment approval wording --------- Signed-off-by: Alex Sparkes <alexsparkes@gmail.com> * feat: replace mui with new style * feat: improve time formatting in Clock component with padded digits * fix: change Checkbox component from label to div for better semantics * fix: change Switch component from label to div for better semantics * feat: add smooth animation to reset functionality in Slider component * feat: enhance accessibility and styling for form components including Checkbox, Dropdown, Radio, Slider, and Text * feat: enhance WeatherOptions component with improved layout and auto location reset functionality * feat: update Slider and Dropdown components with improved layout and z-index adjustments * feat: add reset functionality to Dropdown component with toast notification * feat: update Dropdown component styles for improved layout and structure * feat: update languageSettings component with increased padding for better spacing * feat: bump version to 7.6.0 across all manifests and documentation * Dev (#1134) * feat: add professional three-branch release workflow automation (#1129) - Add version-bump workflow for semantic versioning across all files - Add beta-release workflow for automated pre-release testing - Add production-release workflow with manual approval gates - Add hotfix-release workflow for emergency patches - Create comprehensive CONTRIBUTING.md with workflow guide - Create detailed RELEASE_PROCESS.md for maintainers - Add PR template with release checklists - Update CODEOWNERS to protect workflow files - Update README with contribution links - Remove /docs from .gitignore to allow documentation This implements a dev beta main branching strategy with: - Automated version management across 6 files - Changelog generation from conventional commits - GitHub Releases with build artifacts - Environment-based approvals for production - Back-merge support for hotfixes * feat: new default quotes experience, improve added page * Fix/beta workflow version check (#1131) * fix(workflows): prevent beta release for non-beta versions * fix(workflows): address copilot PR review feedback - Support iterative beta versions (7.6.0-beta.1 -> 7.6.0-beta.2) - Remove tag trigger from beta workflow to prevent premature releases - Fix tag format in docs/summaries to include 'v' prefix - Clarify deployment approval wording * feat: replace mui with new style * feat: improve time formatting in Clock component with padded digits * fix: change Checkbox component from label to div for better semantics * fix: change Switch component from label to div for better semantics * feat: add smooth animation to reset functionality in Slider component * feat: enhance accessibility and styling for form components including Checkbox, Dropdown, Radio, Slider, and Text * feat: enhance WeatherOptions component with improved layout and auto location reset functionality * feat: update Slider and Dropdown components with improved layout and z-index adjustments * feat: add reset functionality to Dropdown component with toast notification * feat: update Dropdown component styles for improved layout and structure * feat: update languageSettings component with increased padding for better spacing * feat: bump version to 7.6.0 across all manifests and documentation --------- Signed-off-by: Alex Sparkes <alexsparkes@gmail.com> Co-authored-by: David Ralph <me@davidcralph.co.uk> * font: replace montserrat with inter * cleanup: remove unused code from addons and marketplace * fix(greeting/events): event text box styling * fix(quote/buttons): improve state management and event handling * feat(background): implement custom background loading and improve state management * feat: enhance image management features - Added new localization strings for image management, including upload and storage information. - Refactored custom background database functions to support metadata and backward compatibility. - Introduced a new FolderTaggingModal component for organizing images into folders. - Created utility functions for image metadata extraction, including dimensions, blur hash generation, and file size calculation. - Implemented functions to delete multiple backgrounds and update background metadata. * Add new localization strings and improve image metadata utility functions - Updated localization files for multiple languages (Hungarian, Indonesian, Japanese, Lithuanian, Latvian, Dutch, Norwegian, Persian, Portuguese, Brazilian Portuguese, Russian, Slovenian, Swedish, Tamil, Turkish, Ukrainian, Vietnamese, Simplified Chinese, Traditional Chinese) to include new strings for image management features such as "Delete Selected", "Uploading", "Tag Images", and storage information. - Enhanced the `getDataUrlSize` and `formatBytes` functions in `imageMetadata.js` for better readability and maintainability by adding braces for conditional statements. * fix(background/custom): prevent flashing during uploads * feat(storage): implement dynamic storage quota estimation and request persistence * feat(modal): enhance close button styling and theming support * fix(Custom): remove unnecessary characters from loading state * feat(Dropdown): implement dropdown closing animation and portal rendering * fix(QuoteOptions): ensure authorDetails is set to true for all users during migration * refactor(Items): remove unused imports and hex color conversion logic * fix: add blurhash dependency for image metadata encoding --------- Signed-off-by: Alex Sparkes <alexsparkes@gmail.com> Co-authored-by: David Ralph <me@davidcralph.co.uk>
removes MUI UI
fixed issue with 0 padding on clock