Skip to content

Commit 293ee03

Browse files
authored
Merge pull request #38 from timonmdy/v2.2/hotfix-3
Settings rework for improved DevEx
2 parents 539af90 + 9ebfc05 commit 293ee03

17 files changed

Lines changed: 426 additions & 181 deletions

src/main/api/Base.routes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { version } from '../../../package.json';
22
import { ok } from '../core/RestAPI';
33
import { getAllProfiles, getSettings, saveSettings } from '../core/Store';
44
import { processManager } from '../core/process/ProcessManager';
5-
import { AppSettings } from '../shared/types/App.types';
5+
import { AppSettings } from '../shared/config/Settings.config';
66
import { defineRoute, RouteMap } from '../shared/types/RestAPI.types';
77

88
export const BaseRoutes: RouteMap = {

src/main/core/Store.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { app } from 'electron';
22
import Store from 'electron-store';
3-
import { DEFAULT_SETTINGS } from '../shared/config/App.config';
4-
import { AppSettings } from '../shared/types/App.types';
3+
import { DEFAULT_SETTINGS } from '../shared/config/Settings.config';
4+
import { AppSettings } from '../shared/config/Settings.config';
55
import { Profile } from '../shared/types/Profile.types';
66

77
interface StoreSchema {

src/main/ipc/Dev.ipc.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { BrowserWindow } from 'electron';
22
import type { RouteMap } from '../core/IPCController';
33
import { getAllProfiles } from '../core/Store';
4-
import { DEFAULT_SETTINGS } from '../shared/config/App.config';
4+
import { DEFAULT_SETTINGS } from '../shared/config/Settings.config';
55

66
let getWindow: () => BrowserWindow | null = () => null;
77

src/main/ipc/System.ipc.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { app, dialog, shell } from 'electron';
22
import type { RouteMap } from '../core/IPCController';
33
import { restApiServer } from '../core/RestAPI';
44
import { getSettings, saveSettings } from '../core/Store';
5-
import type { AppSettings } from '../shared/types/App.types';
5+
import type { AppSettings } from '../shared/config/Settings.config';
66

77
// mainWindow is needed for dialogs — set via initSystemIPC() called from main.ts
88
let getWindow: () => Electron.BrowserWindow | null = () => null;

src/main/shared/config/App.config.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.
Lines changed: 186 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,193 @@
1-
import type { SidebarTopic } from '../types/Sidebar.types';
1+
import { REST_API_CONFIG } from './API.config';
2+
import {
3+
AnyFieldDef,
4+
extractDefaults,
5+
InferSettings,
6+
NoteDef,
7+
NumberDef,
8+
RangeDef,
9+
SettingSidebarTopic,
10+
TextDef,
11+
ToggleDef,
12+
} from '../types/Settings.types';
213

3-
export const SETTINGS_TOPICS: SidebarTopic[] = [
14+
// ─── Section registry ──────────────────────────────────────────────────────
15+
// Add a new entry here when adding a new settings section.
16+
17+
export type SettingSection = 'general' | 'console' | 'appearance' | 'advanced' | 'about';
18+
19+
export const SETTINGS_TOPICS: SettingSidebarTopic<SettingSection>[] = [
420
{ id: 'general', label: 'General' },
521
{ id: 'console', label: 'Console' },
622
{ id: 'appearance', label: 'Appearance' },
723
{ id: 'advanced', label: 'Advanced' },
824
{ id: 'about', label: 'About' },
925
];
26+
27+
// ─── Schema ────────────────────────────────────────────────────────────────
28+
29+
export const SETTINGS_SCHEMA = {
30+
// ── General › Startup ────────────────────────────────────────────────────
31+
launchOnStartup: {
32+
type: 'toggle',
33+
default: false,
34+
section: 'general',
35+
group: 'settings.startup',
36+
label: 'settings.launchOnStartup',
37+
hint: 'settings.launchOnStartupHint',
38+
} as ToggleDef,
39+
40+
startMinimized: {
41+
type: 'toggle',
42+
default: false,
43+
section: 'general',
44+
group: 'settings.startup',
45+
label: 'settings.startMinimized',
46+
hint: 'settings.startMinimizedHint',
47+
sub: true,
48+
disabledWhen: (s: any) => !s.launchOnStartup,
49+
} as ToggleDef,
50+
51+
minimizeToTray: {
52+
type: 'toggle',
53+
default: true,
54+
section: 'general',
55+
group: 'settings.startup',
56+
label: 'settings.minimizeToTray',
57+
hint: 'settings.minimizeToTrayHint',
58+
} as ToggleDef,
59+
60+
// ── Console ───────────────────────────────────────────────────────────────
61+
consoleFontSize: {
62+
type: 'range',
63+
default: 13,
64+
section: 'console',
65+
group: 'settings.console',
66+
label: 'settings.fontSize',
67+
hint: 'settings.fontSizeHint',
68+
min: 10,
69+
max: 20,
70+
step: 1,
71+
unit: 'px',
72+
} as RangeDef,
73+
74+
consoleLineNumbers: {
75+
type: 'toggle',
76+
default: false,
77+
section: 'console',
78+
group: 'settings.console',
79+
label: 'settings.lineNumbers',
80+
hint: 'settings.lineNumbersHint',
81+
} as ToggleDef,
82+
83+
consoleTimestamps: {
84+
type: 'toggle',
85+
default: false,
86+
section: 'console',
87+
group: 'settings.console',
88+
label: 'settings.timestamps',
89+
hint: 'settings.timestampsHint',
90+
} as ToggleDef,
91+
92+
consoleWordWrap: {
93+
type: 'toggle',
94+
default: false,
95+
section: 'console',
96+
group: 'settings.console',
97+
label: 'settings.wordWrap',
98+
hint: 'settings.wordWrapHint',
99+
} as ToggleDef,
100+
101+
consoleMaxLines: {
102+
type: 'number',
103+
default: 5000,
104+
section: 'console',
105+
group: 'settings.console',
106+
label: 'settings.maxLines',
107+
hint: 'settings.maxLinesHint',
108+
min: 500,
109+
max: 50000,
110+
step: 500,
111+
} as NumberDef,
112+
113+
consoleHistorySize: {
114+
type: 'number',
115+
default: 200,
116+
section: 'console',
117+
group: 'settings.console',
118+
label: 'settings.historySize',
119+
hint: 'settings.historySizeHint',
120+
min: 10,
121+
max: 2000,
122+
step: 10,
123+
} as NumberDef,
124+
125+
// ── Appearance (managed by ThemeProvider / I18nProvider) ──────────────────
126+
themeId: {
127+
type: 'text',
128+
default: 'dark-default',
129+
section: 'appearance',
130+
group: 'settings.theme',
131+
label: 'settings.theme',
132+
} as TextDef,
133+
134+
languageId: {
135+
type: 'text',
136+
default: 'en',
137+
section: 'appearance',
138+
group: 'settings.language',
139+
label: 'settings.language',
140+
} as TextDef,
141+
142+
// ── Advanced › Dev Mode ───────────────────────────────────────────────────
143+
devModeEnabled: {
144+
type: 'toggle',
145+
default: false,
146+
section: 'advanced',
147+
group: 'settings.devMode',
148+
label: 'settings.devModeLabel',
149+
hint: 'settings.devModeHint',
150+
} as ToggleDef,
151+
152+
// ── Advanced › REST API ───────────────────────────────────────────────────
153+
restApiEnabled: {
154+
type: 'toggle',
155+
default: false,
156+
section: 'advanced',
157+
group: 'settings.restApi',
158+
label: 'settings.restApiLabel',
159+
hint: 'settings.restApiHint',
160+
hintParams: { port: String(REST_API_CONFIG.defaultPort) },
161+
} as ToggleDef,
162+
163+
restApiPort: {
164+
type: 'number',
165+
default: REST_API_CONFIG.defaultPort,
166+
section: 'advanced',
167+
group: 'settings.restApi',
168+
label: 'settings.restApiPort',
169+
hint: 'settings.restApiPortHint',
170+
sub: true,
171+
min: 1024,
172+
max: 65535,
173+
step: 1,
174+
showWhen: (s: any) => s.restApiEnabled,
175+
} as NumberDef,
176+
177+
restApiNote: {
178+
type: 'note',
179+
section: 'advanced',
180+
group: 'settings.restApi',
181+
showWhen: (s: any) => s.restApiEnabled,
182+
content: (s: any) => [
183+
`http://${REST_API_CONFIG.host}:${s.restApiPort}/api`,
184+
'/status · /profiles · /processes · /logs · /settings',
185+
],
186+
accentPattern: /https?:\/\/[^\s]+/,
187+
} as NoteDef,
188+
} satisfies Record<string, AnyFieldDef>;
189+
190+
// ─── Derived exports ───────────────────────────────────────────────────────
191+
192+
export type AppSettings = InferSettings<typeof SETTINGS_SCHEMA>;
193+
export const DEFAULT_SETTINGS = extractDefaults(SETTINGS_SCHEMA);

src/main/shared/types/App.types.ts

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,3 @@
1-
export interface AppSettings {
2-
launchOnStartup: boolean;
3-
startMinimized: boolean;
4-
minimizeToTray: boolean;
5-
consoleFontSize: number;
6-
consoleMaxLines: number;
7-
consoleWordWrap: boolean;
8-
consoleLineNumbers: boolean;
9-
consoleTimestamps: boolean;
10-
consoleHistorySize: number;
11-
themeId: string;
12-
languageId: string;
13-
restApiEnabled: boolean;
14-
restApiPort: number;
15-
devModeEnabled: boolean;
16-
}
17-
181
export interface JRCEnvironment {
192
isReady: boolean;
203
devMode: boolean;
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// ─── Field definition types ────────────────────────────────────────────────
2+
3+
type BaseField = {
4+
section: string; // valid values: see SettingSection in Settings.config.ts
5+
group: string;
6+
label: string;
7+
hint?: string;
8+
hintParams?: Record<string, string>;
9+
sub?: boolean;
10+
showWhen?: (s: any) => boolean;
11+
disabledWhen?: (s: any) => boolean;
12+
};
13+
14+
export type ToggleDef = BaseField & { type: 'toggle'; default: boolean };
15+
export type NumberDef = BaseField & {
16+
type: 'number';
17+
default: number;
18+
min: number;
19+
max: number;
20+
step: number;
21+
};
22+
export type RangeDef = BaseField & {
23+
type: 'range';
24+
default: number;
25+
min: number;
26+
max: number;
27+
step: number;
28+
unit?: string;
29+
};
30+
export type TextDef = BaseField & { type: 'text'; default: string };
31+
export type NoteDef = {
32+
type: 'note';
33+
section: string;
34+
group: string;
35+
showWhen?: (s: any) => boolean;
36+
content: (s: any) => string[];
37+
accentPattern?: RegExp;
38+
};
39+
40+
export type SettingFieldDef = ToggleDef | NumberDef | RangeDef | TextDef;
41+
export type AnyFieldDef = SettingFieldDef | NoteDef;
42+
43+
// ─── Sidebar topic ─────────────────────────────────────────────────────────
44+
// Generic so Settings.config can bind S = SettingSection without a circular dep.
45+
46+
export type SettingSidebarTopic<S extends string = string> = {
47+
id: S;
48+
label: string;
49+
};
50+
51+
// ─── Inference helpers ─────────────────────────────────────────────────────
52+
53+
export type InferSettings<Schema extends Record<string, AnyFieldDef>> = {
54+
[K in keyof Schema as Schema[K] extends NoteDef ? never : K]: Schema[K] extends ToggleDef
55+
? boolean
56+
: Schema[K] extends NumberDef | RangeDef
57+
? number
58+
: Schema[K] extends TextDef
59+
? string
60+
: never;
61+
};
62+
63+
export function extractDefaults<Schema extends Record<string, AnyFieldDef>>(
64+
schema: Schema
65+
): InferSettings<Schema> {
66+
const result: Record<string, unknown> = {};
67+
for (const [key, field] of Object.entries(schema)) {
68+
if (field.type !== 'note') {
69+
result[key] = (field as SettingFieldDef).default;
70+
}
71+
}
72+
return result as InferSettings<Schema>;
73+
}

src/main/shared/types/Sidebar.types.ts

Lines changed: 0 additions & 4 deletions
This file was deleted.

src/renderer/AppProvider.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AppSettings } from '@shared/types/App.types';
1+
import { AppSettings } from '@shared/config/Settings.config';
22
import { ConsoleLine, ProcessState } from '@shared/types/Process.types';
33
import { Profile } from '@shared/types/Profile.types';
44
import {

0 commit comments

Comments
 (0)