Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions languages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@
"appearance.syncHint": "Designs und Sprachen aus /themes und /languages im Projektverzeichnis laden",
"appearance.sync": "Synchronisieren",
"appearance.synced": "Synchronisiert",
"appearance.localOnly": "Nur lokal",
"appearance.localOverride": "Überschreibt Remote",

"settings.advanced": "Erweitert",
"settings.devMode": "Entwickleroptionen",
Expand Down Expand Up @@ -234,6 +236,7 @@
"settings.version": "Version",
"settings.stack": "Technologie",
"settings.configPath": "Konfiguration",
"settings.openConfigFolder": "Konfigurationsordner öffnen",

"release.title": "Release-Details",
"release.preRelease": "Vorabversion",
Expand Down
96 changes: 94 additions & 2 deletions src/main/AssetManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import https from 'https';
import { GITHUB_CONFIG } from './shared/config/GitHub.config';
import { BUILTIN_THEME, THEME_GITHUB_PATH } from './shared/config/Theme.config';
import { ENGLISH } from './shared/config/DefaultLanguage.config';
import type { ThemeDefinition, LocalThemeState } from './shared/types/Theme.types';
import type { LanguageDefinition, LocalLanguageState } from './shared/types/Language.types';
import type { ThemeDefinition, LocalThemeState, ThemePreview } from './shared/types/Theme.types';
import type {
LanguageDefinition,
LocalLanguageState,
LanguagePreview,
} from './shared/types/Language.types';

function dataDir(): string {
return app.getPath('userData');
Expand Down Expand Up @@ -93,6 +97,53 @@ export async function fetchRemoteThemes(): Promise<{
}
}

export async function fetchRemoteThemePreviews(): Promise<{
ok: boolean;
themes?: ThemePreview[];
error?: string;
}> {
try {
const listing = await httpsGetJson(contentsUrl(THEME_GITHUB_PATH));
if (!Array.isArray(listing)) return { ok: false, error: 'Themes folder not found' };
const themes: ThemePreview[] = [];
for (const f of (listing as Array<{ name: string }>).filter((f) => f.name.endsWith('.json'))) {
try {
const theme = (await httpsGetJson(rawUrl(THEME_GITHUB_PATH, f.name))) as ThemeDefinition;
if (theme.id && theme.name && theme.colors) {
themes.push({
id: theme.id,
name: theme.name,
filename: f.name,
previewColors: {
accent: theme.colors.accent,
'base-900': theme.colors['base-900'],
'surface-raised': theme.colors['surface-raised'],
'text-primary': theme.colors['text-primary'],
},
});
}
} catch {
/* skip */
}
}
return { ok: true, themes };
} catch (e) {
return { ok: false, error: String(e) };
}
}

export async function fetchRemoteThemeByFile(
filename: string
): Promise<{ ok: boolean; theme?: ThemeDefinition; error?: string }> {
try {
const theme = (await httpsGetJson(rawUrl(THEME_GITHUB_PATH, filename))) as ThemeDefinition;
if (theme.id && theme.name && theme.colors) return { ok: true, theme };
return { ok: false, error: 'Invalid theme' };
} catch (e) {
return { ok: false, error: String(e) };
}
}

// ─── Languages ────────────────────────────────────────────────────────────────

const LANG_FILE = 'language-state.json';
Expand Down Expand Up @@ -142,6 +193,47 @@ export async function fetchRemoteLanguages(): Promise<{
}
}

export async function fetchRemoteLanguagePreviews(): Promise<{
ok: boolean;
languages?: LanguagePreview[];
error?: string;
}> {
try {
const listing = await httpsGetJson(contentsUrl(GITHUB_CONFIG.languagesPath));
if (!Array.isArray(listing)) return { ok: false, error: 'Languages folder not found' };
const languages: LanguagePreview[] = [];
for (const f of (listing as Array<{ name: string }>).filter((f) => f.name.endsWith('.json'))) {
try {
const lang = (await httpsGetJson(
rawUrl(GITHUB_CONFIG.languagesPath, f.name)
)) as LanguageDefinition;
if (lang.id && lang.name) {
languages.push({ id: lang.id, name: lang.name, filename: f.name });
}
} catch {
/* skip */
}
}
return { ok: true, languages };
} catch (e) {
return { ok: false, error: String(e) };
}
}

export async function fetchRemoteLanguageByFile(
filename: string
): Promise<{ ok: boolean; language?: LanguageDefinition; error?: string }> {
try {
const lang = (await httpsGetJson(
rawUrl(GITHUB_CONFIG.languagesPath, filename)
)) as LanguageDefinition;
if (lang.id && lang.name && lang.strings) return { ok: true, language: lang };
return { ok: false, error: 'Invalid language' };
} catch (e) {
return { ok: false, error: String(e) };
}
}

// ─── Dev mode: load from local project directories ────────────────────────────

function projectRoot(): string {
Expand Down
24 changes: 24 additions & 0 deletions src/main/ipc/Asset.ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ import {
getActiveTheme,
setActiveTheme,
fetchRemoteThemes,
fetchRemoteThemePreviews,
fetchRemoteThemeByFile,
getActiveLanguage,
setActiveLanguage,
fetchRemoteLanguages,
fetchRemoteLanguagePreviews,
fetchRemoteLanguageByFile,
loadDevAssets,
} from '../AssetManager';
import type { ThemeDefinition } from '../shared/types/Theme.types';
Expand All @@ -28,6 +32,16 @@ export const AssetIPC = {
channel: 'asset:fetchThemes',
handler: () => fetchRemoteThemes(),
},
fetchThemePreviews: {
type: 'invoke',
channel: 'asset:themePreviews',
handler: () => fetchRemoteThemePreviews(),
},
fetchThemeByFile: {
type: 'invoke',
channel: 'asset:themeByFile',
handler: (_e: any, filename: string) => fetchRemoteThemeByFile(filename),
},

// Languages
getActiveLanguage: {
Expand All @@ -45,6 +59,16 @@ export const AssetIPC = {
channel: 'asset:fetchLangs',
handler: () => fetchRemoteLanguages(),
},
fetchLanguagePreviews: {
type: 'invoke',
channel: 'asset:langPreviews',
handler: () => fetchRemoteLanguagePreviews(),
},
fetchLanguageByFile: {
type: 'invoke',
channel: 'asset:langByFile',
handler: (_e: any, filename: string) => fetchRemoteLanguageByFile(filename),
},

// Dev
loadDevAssets: {
Expand Down
8 changes: 7 additions & 1 deletion src/main/ipc/System.ipc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { dialog, shell } from 'electron';
import { app, dialog, shell } from 'electron';
import { restApiServer } from '../RestAPI';
import type { RouteMap } from '../IPCController';
import type { AppSettings, JRCEnvironment } from '../shared/types/App.types';
Expand Down Expand Up @@ -65,4 +65,10 @@ export const SystemIPC = {
channel: 'shell:openExternal',
handler: (_e: any, url: string) => shell.openExternal(url),
},

openConfigFolder: {
type: 'invoke',
channel: 'shell:openConfigFolder',
handler: () => shell.openPath(app.getPath('userData')),
},
} satisfies RouteMap;
3 changes: 3 additions & 0 deletions src/main/shared/config/DefaultLanguage.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ const ENGLISH_STRINGS = {
'Load themes and languages from /themes and /languages in the project root',
'appearance.sync': 'Sync',
'appearance.synced': 'Synced',
'appearance.localOnly': 'Local only',
'appearance.localOverride': 'Overrides remote',

// Settings: Advanced
'settings.advanced': 'Advanced',
Expand Down Expand Up @@ -259,6 +261,7 @@ const ENGLISH_STRINGS = {
'settings.version': 'Version',
'settings.stack': 'Stack',
'settings.configPath': 'Config',
'settings.openConfigFolder': 'Open config folder',

// Release modal
'release.title': 'Release Details',
Expand Down
6 changes: 6 additions & 0 deletions src/main/shared/types/Language.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ export interface LocalLanguageState {
activeLanguageId: string;
activeLanguage: LanguageDefinition;
}

export interface LanguagePreview {
id: string;
name: string;
filename: string;
}
12 changes: 12 additions & 0 deletions src/main/shared/types/Theme.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,15 @@ export interface LocalThemeState {
activeThemeId: string;
activeTheme: ThemeDefinition;
}

export type ThemePreviewColors = Pick<
ThemeColors,
'accent' | 'base-900' | 'surface-raised' | 'text-primary'
>;

export interface ThemePreview {
id: string;
name: string;
filename: string;
previewColors: ThemePreviewColors;
}
2 changes: 2 additions & 0 deletions src/renderer/components/common/ContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ export function ContextMenu({ x, y, items, onClose }: Props) {
};
document.addEventListener('mousedown', handleOutside);
document.addEventListener('keydown', handleKey);
document.addEventListener('scroll', onClose, true);
return () => {
document.removeEventListener('mousedown', handleOutside);
document.removeEventListener('keydown', handleKey);
document.removeEventListener('scroll', onClose, true);
};
}, [onClose]);

Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/settings/VersionChecker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Button } from '../common/Button';
import { Tooltip } from '../common/Tooltip';
import { ReleaseModal } from './ReleaseModal';
import { VscCheck, VscWarning, VscSync, VscCircleSlash } from 'react-icons/vsc';
import { GitHubRelease } from '../../../../main/shared/types/GitHub.types';
import { GitHubRelease } from '../../../main/shared/types/GitHub.types';

interface Props {
currentVersion: string;
Expand Down
14 changes: 13 additions & 1 deletion src/renderer/components/settings/sections/AboutSection.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React from 'react';
import { useTranslation } from '../../../i18n/I18nProvider';
import { Section, Row } from '../SettingsRow';
import { Tooltip } from '../../common/Tooltip';
import { VersionChecker } from '../VersionChecker';
import { VscFolderOpened } from 'react-icons/vsc';
import { version } from '../../../../../package.json';

export function AboutSection() {
Expand All @@ -13,7 +15,17 @@ export function AboutSection() {
<span className="font-mono text-xs text-text-secondary">Electron · React · TypeScript</span>
</Row>
<Row label={t('settings.configPath')}>
<span className="font-mono text-xs text-text-muted">%APPDATA%\java-runner-client</span>
<div className="flex items-center gap-2">
<span className="font-mono text-xs text-text-muted">%APPDATA%\java-runner-client</span>
<Tooltip content={t('settings.openConfigFolder')} side="left" delay={300}>
<button
onClick={() => window.api.openConfigFolder()}
className="p-1 rounded text-text-muted hover:text-text-primary hover:bg-surface-raised transition-colors"
>
<VscFolderOpened size={14} />
</button>
</Tooltip>
</div>
</Row>
</Section>
);
Expand Down
Loading
Loading