mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-09 03:32:54 -05:00
Replace theme screenshots with color palette preview (#6722)
* Replace theme screenshots with color palette preview - Replace screenshot images in theme installer with 3x2 color palette grid - Add colors array to catalog themes in customThemeCatalog.json - Update CatalogTheme type to include optional colors property - Remove getThemeScreenshotUrl function - Extract ColorPalette component to separate file - Update documentation to reflect color palette instead of screenshots - Update tests to verify color palettes instead of images - Extract actual theme colors from GitHub repositories * Update ColorPalette component to use data-swatch attribute for color swatches in ThemeInstaller tests * Update packages/desktop-client/src/data/customThemeCatalog.json Co-authored-by: Michael Clark <5285928+MikesGlitch@users.noreply.github.com> * Update color palettes for Simple Dark and Okabe Ito themes Co-authored-by: matiss <matiss@mja.lv> --------- Co-authored-by: Michael Clark <5285928+MikesGlitch@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
committed by
GitHub
parent
6a9df6562c
commit
c9a0ffa91c
@@ -0,0 +1,30 @@
|
||||
import { View } from '@actual-app/components/view';
|
||||
|
||||
const DEFAULT_COLORS = ['#ccc', '#999', '#666', '#333', '#111', '#000'];
|
||||
|
||||
type ColorPaletteProps = {
|
||||
colors?: string[];
|
||||
};
|
||||
|
||||
export function ColorPalette({ colors }: ColorPaletteProps) {
|
||||
// Default fallback colors if not provided
|
||||
const paletteColors = colors ?? DEFAULT_COLORS;
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(3, 1fr)',
|
||||
gridTemplateRows: 'repeat(2, 1fr)',
|
||||
width: '100%',
|
||||
height: 60,
|
||||
borderRadius: 4,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{paletteColors.slice(0, 6).map((color, i) => (
|
||||
<div key={i} data-swatch style={{ backgroundColor: color }} />
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -27,10 +27,6 @@ vi.mock('@desktop-client/style/customThemes', async () => {
|
||||
normalizeGitHubRepo: vi.fn((repo: string) =>
|
||||
repo.startsWith('http') ? repo : `https://github.com/${repo}`,
|
||||
),
|
||||
getThemeScreenshotUrl: vi.fn(
|
||||
(repo: string) =>
|
||||
`https://raw.githubusercontent.com/${repo}/refs/heads/main/screenshot.png`,
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -51,14 +47,38 @@ describe('ThemeInstaller', () => {
|
||||
{
|
||||
name: 'Demo Theme',
|
||||
repo: 'actualbudget/demo-theme',
|
||||
colors: [
|
||||
'#1a1a2e',
|
||||
'#16213e',
|
||||
'#0f3460',
|
||||
'#e94560',
|
||||
'#533483',
|
||||
'#f1f1f1',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Ocean Blue',
|
||||
repo: 'actualbudget/ocean-theme',
|
||||
colors: [
|
||||
'#0d47a1',
|
||||
'#1565c0',
|
||||
'#1976d2',
|
||||
'#1e88e5',
|
||||
'#42a5f5',
|
||||
'#90caf9',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Forest Green',
|
||||
repo: 'actualbudget/forest-theme',
|
||||
colors: [
|
||||
'#1b5e20',
|
||||
'#2e7d32',
|
||||
'#388e3c',
|
||||
'#43a047',
|
||||
'#66bb6a',
|
||||
'#a5d6a7',
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -562,21 +582,21 @@ describe('ThemeInstaller', () => {
|
||||
});
|
||||
|
||||
describe('catalog theme display', () => {
|
||||
it('displays theme screenshot URL correctly', () => {
|
||||
it('displays theme color palette correctly', () => {
|
||||
render(
|
||||
<ThemeInstaller onInstall={mockOnInstall} onClose={mockOnClose} />,
|
||||
);
|
||||
|
||||
const images = screen.getAllByRole('img');
|
||||
expect(images.length).toBeGreaterThan(0);
|
||||
// Check that color palettes are rendered instead of images
|
||||
const images = screen.queryAllByRole('img');
|
||||
expect(images.length).toBe(0);
|
||||
|
||||
// Check that images have the correct src pattern
|
||||
images.forEach(img => {
|
||||
expect(img).toHaveAttribute(
|
||||
'src',
|
||||
expect.stringContaining('raw.githubusercontent.com'),
|
||||
);
|
||||
// Check that color swatches are rendered (6 divs per theme)
|
||||
const demoThemeButton = screen.getByRole('button', {
|
||||
name: 'Demo Theme',
|
||||
});
|
||||
const colorSwatches = demoThemeButton.querySelectorAll('[data-swatch]');
|
||||
expect(colorSwatches.length).toBe(6);
|
||||
});
|
||||
|
||||
it('displays theme author correctly', () => {
|
||||
|
||||
@@ -11,6 +11,8 @@ import { Text } from '@actual-app/components/text';
|
||||
import { theme as themeStyle } from '@actual-app/components/theme';
|
||||
import { View } from '@actual-app/components/view';
|
||||
|
||||
import { ColorPalette } from './ColorPalette';
|
||||
|
||||
import { Link } from '@desktop-client/components/common/Link';
|
||||
import { FixedSizeList } from '@desktop-client/components/FixedSizeList';
|
||||
import { useThemeCatalog } from '@desktop-client/hooks/useThemeCatalog';
|
||||
@@ -18,7 +20,6 @@ import {
|
||||
extractRepoOwner,
|
||||
fetchThemeCss,
|
||||
generateThemeId,
|
||||
getThemeScreenshotUrl,
|
||||
normalizeGitHubRepo,
|
||||
validateThemeCss,
|
||||
type CatalogTheme,
|
||||
@@ -337,16 +338,7 @@ export function ThemeInstaller({
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
<img
|
||||
src={getThemeScreenshotUrl(theme.repo)}
|
||||
alt={theme.name}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: 60,
|
||||
objectFit: 'cover',
|
||||
borderRadius: 4,
|
||||
}}
|
||||
/>
|
||||
<ColorPalette colors={theme.colors} />
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
|
||||
@@ -1,34 +1,37 @@
|
||||
[
|
||||
{
|
||||
"name": "Demo Theme",
|
||||
"repo": "actualbudget/demo-theme"
|
||||
"repo": "actualbudget/demo-theme",
|
||||
"colors": ["#f5f1e8", "#5a4a38", "#b8956a", "#4a3f2e", "#fffef9", "#d4b896"]
|
||||
},
|
||||
{
|
||||
"name": "Shades of Coffee",
|
||||
"repo": "Juulz/shades-of-coffee"
|
||||
"repo": "Juulz/shades-of-coffee",
|
||||
"colors": ["#e2d8cf", "#1a0c00", "#cfb3ff", "#f5f2ef", "#604b39", "#c29670"]
|
||||
},
|
||||
{
|
||||
"name": "Miami Beach",
|
||||
"repo": "Juulz/miami-beach"
|
||||
"repo": "Juulz/miami-beach",
|
||||
"colors": ["#d095ca", "#5e2a91", "#c5ece4", "#24b2a0", "#f9deb8", "#1e9484"]
|
||||
},
|
||||
{
|
||||
"name": "Simple Dark",
|
||||
"repo": "Juulz/simple-dark"
|
||||
"repo": "Juulz/simple-dark",
|
||||
"colors": ["#222222", "#063446", "#14aeeb", "#434343", "#edbe5e", "#d9e2ec"]
|
||||
},
|
||||
{
|
||||
"name": "Matrix",
|
||||
"repo": "MatissJanis/actualbudget-matrix-theme"
|
||||
"repo": "MatissJanis/actualbudget-matrix-theme",
|
||||
"colors": ["#000000", "#001a00", "#00ff41", "#004400", "#39ff14", "#00ff00"]
|
||||
},
|
||||
{
|
||||
"name": "Black Gold",
|
||||
"repo": "MikesGlitch/actual-black-gold-theme"
|
||||
},
|
||||
{
|
||||
"name": "Simple Dark",
|
||||
"repo": "Juulz/miami-beach"
|
||||
"repo": "MikesGlitch/actual-black-gold-theme",
|
||||
"colors": ["#141520", "#242733", "#373B4A", "#E8ECF0", "#FFD700", "#8F7A20"]
|
||||
},
|
||||
{
|
||||
"name": "Okabe Ito",
|
||||
"repo": "Juulz/okabe-ito"
|
||||
"repo": "Juulz/okabe-ito",
|
||||
"colors": ["#222222", "#141520", "#e69f00", "#56b4e9", "#b88115", "#00304d"]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -2,8 +2,7 @@ import { useEffect, useState } from 'react';
|
||||
|
||||
import { type CatalogTheme } from '@desktop-client/style/customThemes';
|
||||
|
||||
const CATALOG_URL =
|
||||
'https://raw.githubusercontent.com/actualbudget/actual/master/packages/desktop-client/src/data/customThemeCatalog.json';
|
||||
const CATALOG_URL = `https://raw.githubusercontent.com/actualbudget/actual/${process.env.REACT_APP_BRANCH || 'master'}/packages/desktop-client/src/data/customThemeCatalog.json`;
|
||||
|
||||
/**
|
||||
* Custom hook to fetch and manage the theme catalog from GitHub.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
export type CatalogTheme = {
|
||||
name: string;
|
||||
repo: string;
|
||||
colors?: string[];
|
||||
};
|
||||
|
||||
export type InstalledTheme = {
|
||||
@@ -50,24 +51,6 @@ export function normalizeGitHubRepo(repo: string): string {
|
||||
return `https://github.com/${owner}/${repoName}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the screenshot URL for a theme repo.
|
||||
* Returns a safe fallback URL for malformed repos.
|
||||
*/
|
||||
export function getThemeScreenshotUrl(repo: string): string {
|
||||
if (
|
||||
!repo ||
|
||||
typeof repo !== 'string' ||
|
||||
!repo.trim() ||
|
||||
!repo.includes('/')
|
||||
) {
|
||||
// Return a placeholder or empty data URL for malformed repos
|
||||
return 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQwIiBoZWlnaHQ9IjYwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxNDAiIGhlaWdodD0iNjAiIGZpbGw9IiNmNWY1ZjUiLz48L3N2Zz4=';
|
||||
}
|
||||
const trimmed = repo.trim();
|
||||
return `https://raw.githubusercontent.com/${trimmed}/refs/heads/main/screenshot.png`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try fetching actual.css from main branch.
|
||||
*/
|
||||
|
||||
@@ -77,6 +77,7 @@ export default defineConfig(async ({ mode }) => {
|
||||
// Forward Netlify env variables
|
||||
if (process.env.REVIEW_ID) {
|
||||
process.env.REACT_APP_REVIEW_ID = process.env.REVIEW_ID;
|
||||
process.env.REACT_APP_BRANCH = process.env.BRANCH;
|
||||
}
|
||||
|
||||
let resolveExtensions = [
|
||||
|
||||
@@ -28,7 +28,7 @@ The easiest way to install a custom theme is to choose one from the catalog:
|
||||
3. The theme installer will open showing available themes from the catalog
|
||||
4. Click on any theme to install it immediately
|
||||
|
||||
Themes in the catalog are hosted on GitHub and are automatically fetched when you select them. Each theme shows a preview screenshot and includes a link to its source repository.
|
||||
Themes in the catalog are hosted on GitHub and are automatically fetched when you select them. Each theme shows a color palette preview (6 colors in a 3x2 grid) and includes a link to its source repository.
|
||||
|
||||
### Installing a Theme by Pasting CSS
|
||||
|
||||
@@ -124,14 +124,12 @@ To share your theme with others or add it to the catalog, you can host it on Git
|
||||
1. Create a new GitHub repository
|
||||
2. Create a file named `actual.css` in the root directory (on the `main` branch)
|
||||
3. Add your theme CSS to this file
|
||||
4. Add a `screenshot.png` file for catalog display (recommended size: 140x60px or similar)
|
||||
|
||||
**Example repository structure:**
|
||||
|
||||
```text
|
||||
your-theme-repo/
|
||||
├── actual.css # Your theme CSS
|
||||
└── screenshot.png # Preview image
|
||||
└── actual.css # Your theme CSS
|
||||
```
|
||||
|
||||
**Example `actual.css`:**
|
||||
@@ -149,6 +147,8 @@ your-theme-repo/
|
||||
|
||||
The theme can then be referenced in the catalog using the format `owner/repo` (e.g., `actualbudget/demo-theme`).
|
||||
|
||||
When your theme is added to the catalog, it will display a color palette preview. The palette is defined in the catalog JSON file and should include 6 representative colors from your theme (typically background colors, accent colors, and text colors).
|
||||
|
||||
### Example Theme
|
||||
|
||||
For a complete example of a custom theme, check out the [demo theme repository](https://github.com/actualbudget/demo-theme). This repository contains multiple theme variations and demonstrates the proper structure and format.
|
||||
@@ -157,7 +157,6 @@ The demo theme includes examples of:
|
||||
|
||||
- Proper CSS variable naming
|
||||
- Complete theme definitions
|
||||
- Screenshot assets for catalog display
|
||||
|
||||
You can use this as a template for creating your own themes.
|
||||
|
||||
@@ -176,5 +175,10 @@ To have your theme added to the official catalog, you'll need to:
|
||||
1. Host your theme on GitHub following the structure above
|
||||
2. Open an issue or pull request on the Actual repository requesting your theme be added to the catalog
|
||||
3. Provide the repository name in `owner/repo` format
|
||||
4. Include 6 representative colors for the color palette preview (as an array of hex color values)
|
||||
|
||||
The catalog is maintained in `packages/desktop-client/src/data/customThemeCatalog.json`.
|
||||
The catalog is maintained in `packages/desktop-client/src/data/customThemeCatalog.json`. Each theme entry includes:
|
||||
|
||||
- `name`: The theme name
|
||||
- `repo`: The GitHub repository in `owner/repo` format
|
||||
- `colors`: An array of 6 hex color values for the palette preview (e.g., `["#1a1a2e", "#16213e", "#0f3460", "#e94560", "#533483", "#f1f1f1"]`)
|
||||
|
||||
6
upcoming-release-notes/6722.md
Normal file
6
upcoming-release-notes/6722.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Enhancements
|
||||
authors: [MatissJanis]
|
||||
---
|
||||
|
||||
Themes: use color palette for preview instead of screenshots
|
||||
Reference in New Issue
Block a user