feat: Rename to Toju and add translation
Some checks failed
Deploy Web Apps / deploy (push) Successful in 5m52s
Build Android APK / build-android-apk (push) Failing after 23m15s
Queue Release Build / prepare (push) Successful in 1m42s
Queue Release Build / build-linux (push) Failing after 9m33s
Queue Release Build / build-windows (push) Successful in 26m5s
Queue Release Build / finalize (push) Has been skipped

This commit is contained in:
2026-06-05 17:13:03 +02:00
parent 8ecfc9a1fe
commit ee293d7daf
301 changed files with 8247 additions and 2218 deletions

View File

@@ -9,6 +9,7 @@ import {
createDefaultThemeDocument
} from '../../domain/logic/theme-defaults.logic';
import { validateThemeDocument } from '../../domain/logic/theme-validation.logic';
import { initializeAppI18nForTests, provideAppI18nForTests } from '../../../../core/i18n/app-i18n.testing';
import { ThemeService } from './theme.service';
describe('ThemeService theme application', () => {
@@ -20,12 +21,14 @@ describe('ThemeService theme application', () => {
installLocalStorageMock();
styleElements = [];
injector = createEnvironmentInjector([
...provideAppI18nForTests(),
ThemeService,
{
provide: DOCUMENT,
useValue: createDocumentStub(styleElements)
}
]);
initializeAppI18nForTests(injector);
service = injector.get(ThemeService);
service.initialize();
@@ -200,7 +203,7 @@ describe('ThemeService theme application', () => {
expect(loaded).toBe(true);
expect(service.activeThemeName()).toBe('Complete Theme Fixture');
expect(service.getTextOverride('titleBar')).toBe('MetoYou Lab');
expect(service.getTextOverride('titleBar')).toBe('Toju Lab');
expect(service.getIcon('titleBar')).toBe('MT');
expect(service.getLink('titleBar')).toBe('https://example.com/theme');
expect(service.getAnimationClass('titleBar')).toBe('themePulse');
@@ -532,7 +535,7 @@ function createCompleteThemeDocument() {
boxShadow: '0 18px 50px rgba(0, 0, 0, 0.35)',
backdropFilter: 'var(--theme-effect-glass-blur)',
icon: 'MT',
textOverride: 'MetoYou Lab',
textOverride: 'Toju Lab',
link: 'https://example.com/theme',
animationClass: 'themePulse'
};

View File

@@ -24,6 +24,7 @@ import {
import { createAnimationStarterDefinition } from '../../domain/logic/theme-schema.logic';
import { findThemeLayoutContainer } from '../../domain/logic/theme-registry.logic';
import { validateThemeDocument } from '../../domain/logic/theme-validation.logic';
import { AppI18nService } from '../../../../core/i18n';
import {
loadThemeStorageSnapshot,
saveActiveThemeText,
@@ -141,6 +142,7 @@ export class ThemeService {
readonly builtInPresets = BUILT_IN_THEME_PRESETS;
private readonly documentRef = inject(DOCUMENT);
private readonly appI18n = inject(AppI18nService);
private readonly activeThemeInternal = signal<ThemeDocument>(createDefaultThemeDocument());
private readonly activeThemeTextInternal = signal(DEFAULT_THEME_JSON);
@@ -239,7 +241,7 @@ export class ThemeService {
formatDraft(): void {
if (!this.draftIsValidInternal()) {
this.setStatusMessage('Fix JSON errors before formatting the theme draft.');
this.setStatusMessage(this.appI18n.instant('theme.status.fixJsonBeforeFormat'));
return;
}
@@ -247,18 +249,18 @@ export class ThemeService {
this.draftTextInternal.set(formatted);
saveDraftThemeText(formatted);
this.setStatusMessage('Theme draft formatted.');
this.setStatusMessage(this.appI18n.instant('theme.status.draftFormatted'));
}
applyDraft(): boolean {
if (!this.draftIsValidInternal()) {
this.setStatusMessage('The current draft has validation errors. The previous working theme is still active.');
this.setStatusMessage(this.appI18n.instant('theme.status.draftValidationErrors'));
return false;
}
const formatted = stringifyTheme(this.draftThemeInternal());
this.commitTheme(this.draftThemeInternal(), formatted, 'Theme applied.');
this.commitTheme(this.draftThemeInternal(), formatted, this.appI18n.instant('theme.status.themeApplied'));
return true;
}
@@ -277,7 +279,7 @@ export class ThemeService {
const formatted = stringifyTheme(theme);
this.commitTheme(theme, formatted, 'CSS applied over the JSON theme.');
this.commitTheme(theme, formatted, this.appI18n.instant('theme.status.cssApplied'));
return true;
}
@@ -285,7 +287,7 @@ export class ThemeService {
const result = this.parseAndValidateTheme(text, sourceLabel);
if (!result.valid || !result.value) {
this.setStatusMessage(result.errors[0] ?? `The ${sourceLabel} could not be loaded.`);
this.setStatusMessage(result.errors[0] ?? this.appI18n.instant('theme.status.couldNotLoad', { source: sourceLabel }));
return false;
}
@@ -324,14 +326,16 @@ export class ThemeService {
saveDraftThemeText(defaultText);
this.syncAnimationStylesheet();
this.syncCssStylesheet();
this.setStatusMessage(reason === 'shortcut' ? 'Theme reset to the default preset by shortcut.' : 'Theme reset to the default preset.');
this.setStatusMessage(reason === 'shortcut'
? this.appI18n.instant('theme.status.resetByShortcut')
: this.appI18n.instant('theme.status.resetByButton'));
}
applyBuiltInPreset(name: string): boolean {
const preset = this.builtInPresets.find((builtInPreset) => builtInPreset.theme.meta.name === name || builtInPreset.key === name);
if (!preset) {
this.setStatusMessage('Built-in theme preset not found.');
this.setStatusMessage(this.appI18n.instant('theme.status.presetNotFound'));
return false;
}
@@ -339,11 +343,11 @@ export class ThemeService {
const result = validateThemeDocument(theme);
if (!result.valid || !result.value) {
this.setStatusMessage(result.errors[0] ?? 'Built-in theme preset could not be applied.');
this.setStatusMessage(result.errors[0] ?? this.appI18n.instant('theme.status.presetCouldNotApply'));
return false;
}
this.commitTheme(result.value, stringifyTheme(result.value), `${result.value.meta.name} preset applied.`);
this.commitTheme(result.value, stringifyTheme(result.value), this.appI18n.instant('theme.status.presetApplied', { name: result.value.meta.name }));
return true;
}
@@ -361,7 +365,7 @@ export class ThemeService {
ensureElementEntry(key: string): void {
if (!this.draftIsValidInternal()) {
this.setStatusMessage('Fix JSON errors before using the structured theme tools.');
this.setStatusMessage(this.appI18n.instant('theme.status.fixJsonBeforeTools'));
return;
}
@@ -378,7 +382,7 @@ export class ThemeService {
const result = validateThemeDocument(draftJson);
if (!result.valid || !result.value) {
this.setStatusMessage('The structured change could not be validated.');
this.setStatusMessage(this.appI18n.instant('theme.status.structuredChangeInvalid'));
return;
}
@@ -389,7 +393,7 @@ export class ThemeService {
this.draftIsValidInternal.set(true);
this.draftErrorsInternal.set([]);
saveDraftThemeText(formatted);
this.setStatusMessage(`Prepared ${key} in the theme draft.`);
this.setStatusMessage(this.appI18n.instant('theme.status.preparedElement', { key }));
}
ensureLayoutEntry(key: string): void {
@@ -400,7 +404,7 @@ export class ThemeService {
draft.layout[key] = draft.layout[key] ?? defaults[key];
},
false,
`Prepared ${key} layout in the theme draft.`
this.appI18n.instant('theme.status.preparedLayout', { key })
);
}
@@ -413,7 +417,7 @@ export class ThemeService {
};
},
applyImmediately,
`${key} updated.`
this.appI18n.instant('theme.status.elementUpdated', { key })
);
}
@@ -423,7 +427,7 @@ export class ThemeService {
draft.animations[key] = definition;
},
applyImmediately,
`Animation ${key} updated.`
this.appI18n.instant('theme.status.animationUpdated', { key })
);
}
@@ -512,7 +516,7 @@ export class ThemeService {
updateStructuredDraft(mutator: (draft: ThemeDocument) => void, applyImmediately: boolean, successMessage: string): void {
if (!this.draftIsValidInternal()) {
this.setStatusMessage('Fix JSON errors before using the structured theme tools.');
this.setStatusMessage(this.appI18n.instant('theme.status.fixJsonBeforeTools'));
return;
}
@@ -523,7 +527,7 @@ export class ThemeService {
const result = validateThemeDocument(nextDraft);
if (!result.valid || !result.value) {
this.setStatusMessage('The structured change could not be validated.');
this.setStatusMessage(this.appI18n.instant('theme.status.structuredChangeInvalid'));
return;
}
@@ -559,7 +563,7 @@ export class ThemeService {
private composeDraftThemeWithCss(css: string): ThemeDocument | null {
if (!this.draftIsValidInternal()) {
this.setStatusMessage('Fix JSON errors before applying CSS over the theme draft.');
this.setStatusMessage(this.appI18n.instant('theme.status.fixJsonBeforeCss'));
return null;
}
@@ -570,7 +574,7 @@ export class ThemeService {
const result = validateThemeDocument(theme);
if (!result.valid || !result.value) {
this.setStatusMessage(result.errors[0] ?? 'The CSS-only theme could not be applied.');
this.setStatusMessage(result.errors[0] ?? this.appI18n.instant('theme.status.cssOnlyCouldNotApply'));
return null;
}