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
2 changes: 1 addition & 1 deletion src/main/api/Base.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { version } from '../../../package.json';
import { ok } from '../core/RestAPI';
import { getAllProfiles, getSettings, saveSettings } from '../core/Store';
import { processManager } from '../core/process/ProcessManager';
import { AppSettings } from '../shared/types/App.types';
import { AppSettings } from '../shared/config/Settings.config';
import { defineRoute, RouteMap } from '../shared/types/RestAPI.types';

export const BaseRoutes: RouteMap = {
Expand Down
4 changes: 2 additions & 2 deletions src/main/core/Store.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { app } from 'electron';
import Store from 'electron-store';
import { DEFAULT_SETTINGS } from '../shared/config/App.config';
import { AppSettings } from '../shared/types/App.types';
import { DEFAULT_SETTINGS } from '../shared/config/Settings.config';
import { AppSettings } from '../shared/config/Settings.config';
import { Profile } from '../shared/types/Profile.types';

interface StoreSchema {
Expand Down
2 changes: 1 addition & 1 deletion src/main/ipc/Dev.ipc.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BrowserWindow } from 'electron';
import type { RouteMap } from '../core/IPCController';
import { getAllProfiles } from '../core/Store';
import { DEFAULT_SETTINGS } from '../shared/config/App.config';
import { DEFAULT_SETTINGS } from '../shared/config/Settings.config';

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

Expand Down
2 changes: 1 addition & 1 deletion src/main/ipc/System.ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { app, dialog, shell } from 'electron';
import type { RouteMap } from '../core/IPCController';
import { restApiServer } from '../core/RestAPI';
import { getSettings, saveSettings } from '../core/Store';
import type { AppSettings } from '../shared/types/App.types';
import type { AppSettings } from '../shared/config/Settings.config';

// mainWindow is needed for dialogs — set via initSystemIPC() called from main.ts
let getWindow: () => Electron.BrowserWindow | null = () => null;
Expand Down
19 changes: 0 additions & 19 deletions src/main/shared/config/App.config.ts

This file was deleted.

188 changes: 186 additions & 2 deletions src/main/shared/config/Settings.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,193 @@
import type { SidebarTopic } from '../types/Sidebar.types';
import { REST_API_CONFIG } from './API.config';
import {
AnyFieldDef,
extractDefaults,
InferSettings,
NoteDef,
NumberDef,
RangeDef,
SettingSidebarTopic,
TextDef,
ToggleDef,
} from '../types/Settings.types';

export const SETTINGS_TOPICS: SidebarTopic[] = [
// ─── Section registry ──────────────────────────────────────────────────────
// Add a new entry here when adding a new settings section.

export type SettingSection = 'general' | 'console' | 'appearance' | 'advanced' | 'about';

export const SETTINGS_TOPICS: SettingSidebarTopic<SettingSection>[] = [
{ id: 'general', label: 'General' },
{ id: 'console', label: 'Console' },
{ id: 'appearance', label: 'Appearance' },
{ id: 'advanced', label: 'Advanced' },
{ id: 'about', label: 'About' },
];

// ─── Schema ────────────────────────────────────────────────────────────────

export const SETTINGS_SCHEMA = {
// ── General › Startup ────────────────────────────────────────────────────
launchOnStartup: {
type: 'toggle',
default: false,
section: 'general',
group: 'settings.startup',
label: 'settings.launchOnStartup',
hint: 'settings.launchOnStartupHint',
} as ToggleDef,

startMinimized: {
type: 'toggle',
default: false,
section: 'general',
group: 'settings.startup',
label: 'settings.startMinimized',
hint: 'settings.startMinimizedHint',
sub: true,
disabledWhen: (s: any) => !s.launchOnStartup,
} as ToggleDef,

minimizeToTray: {
type: 'toggle',
default: true,
section: 'general',
group: 'settings.startup',
label: 'settings.minimizeToTray',
hint: 'settings.minimizeToTrayHint',
} as ToggleDef,

// ── Console ───────────────────────────────────────────────────────────────
consoleFontSize: {
type: 'range',
default: 13,
section: 'console',
group: 'settings.console',
label: 'settings.fontSize',
hint: 'settings.fontSizeHint',
min: 10,
max: 20,
step: 1,
unit: 'px',
} as RangeDef,

consoleLineNumbers: {
type: 'toggle',
default: false,
section: 'console',
group: 'settings.console',
label: 'settings.lineNumbers',
hint: 'settings.lineNumbersHint',
} as ToggleDef,

consoleTimestamps: {
type: 'toggle',
default: false,
section: 'console',
group: 'settings.console',
label: 'settings.timestamps',
hint: 'settings.timestampsHint',
} as ToggleDef,

consoleWordWrap: {
type: 'toggle',
default: false,
section: 'console',
group: 'settings.console',
label: 'settings.wordWrap',
hint: 'settings.wordWrapHint',
} as ToggleDef,

consoleMaxLines: {
type: 'number',
default: 5000,
section: 'console',
group: 'settings.console',
label: 'settings.maxLines',
hint: 'settings.maxLinesHint',
min: 500,
max: 50000,
step: 500,
} as NumberDef,

consoleHistorySize: {
type: 'number',
default: 200,
section: 'console',
group: 'settings.console',
label: 'settings.historySize',
hint: 'settings.historySizeHint',
min: 10,
max: 2000,
step: 10,
} as NumberDef,

// ── Appearance (managed by ThemeProvider / I18nProvider) ──────────────────
themeId: {
type: 'text',
default: 'dark-default',
section: 'appearance',
group: 'settings.theme',
label: 'settings.theme',
} as TextDef,

languageId: {
type: 'text',
default: 'en',
section: 'appearance',
group: 'settings.language',
label: 'settings.language',
} as TextDef,

// ── Advanced › Dev Mode ───────────────────────────────────────────────────
devModeEnabled: {
type: 'toggle',
default: false,
section: 'advanced',
group: 'settings.devMode',
label: 'settings.devModeLabel',
hint: 'settings.devModeHint',
} as ToggleDef,

// ── Advanced › REST API ───────────────────────────────────────────────────
restApiEnabled: {
type: 'toggle',
default: false,
section: 'advanced',
group: 'settings.restApi',
label: 'settings.restApiLabel',
hint: 'settings.restApiHint',
hintParams: { port: String(REST_API_CONFIG.defaultPort) },
} as ToggleDef,

restApiPort: {
type: 'number',
default: REST_API_CONFIG.defaultPort,
section: 'advanced',
group: 'settings.restApi',
label: 'settings.restApiPort',
hint: 'settings.restApiPortHint',
sub: true,
min: 1024,
max: 65535,
step: 1,
showWhen: (s: any) => s.restApiEnabled,
} as NumberDef,

restApiNote: {
type: 'note',
section: 'advanced',
group: 'settings.restApi',
showWhen: (s: any) => s.restApiEnabled,
content: (s: any) => [
`http://${REST_API_CONFIG.host}:${s.restApiPort}/api`,
'/status · /profiles · /processes · /logs · /settings',
],
accentPattern: /https?:\/\/[^\s]+/,
} as NoteDef,
} satisfies Record<string, AnyFieldDef>;

// ─── Derived exports ───────────────────────────────────────────────────────

export type AppSettings = InferSettings<typeof SETTINGS_SCHEMA>;
export const DEFAULT_SETTINGS = extractDefaults(SETTINGS_SCHEMA);
17 changes: 0 additions & 17 deletions src/main/shared/types/App.types.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,3 @@
export interface AppSettings {
launchOnStartup: boolean;
startMinimized: boolean;
minimizeToTray: boolean;
consoleFontSize: number;
consoleMaxLines: number;
consoleWordWrap: boolean;
consoleLineNumbers: boolean;
consoleTimestamps: boolean;
consoleHistorySize: number;
themeId: string;
languageId: string;
restApiEnabled: boolean;
restApiPort: number;
devModeEnabled: boolean;
}

export interface JRCEnvironment {
isReady: boolean;
devMode: boolean;
Expand Down
73 changes: 73 additions & 0 deletions src/main/shared/types/Settings.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// ─── Field definition types ────────────────────────────────────────────────

type BaseField = {
section: string; // valid values: see SettingSection in Settings.config.ts
group: string;
label: string;
hint?: string;
hintParams?: Record<string, string>;
sub?: boolean;
showWhen?: (s: any) => boolean;
disabledWhen?: (s: any) => boolean;
};

export type ToggleDef = BaseField & { type: 'toggle'; default: boolean };
export type NumberDef = BaseField & {
type: 'number';
default: number;
min: number;
max: number;
step: number;
};
export type RangeDef = BaseField & {
type: 'range';
default: number;
min: number;
max: number;
step: number;
unit?: string;
};
export type TextDef = BaseField & { type: 'text'; default: string };
export type NoteDef = {
type: 'note';
section: string;
group: string;
showWhen?: (s: any) => boolean;
content: (s: any) => string[];
accentPattern?: RegExp;
};

export type SettingFieldDef = ToggleDef | NumberDef | RangeDef | TextDef;
export type AnyFieldDef = SettingFieldDef | NoteDef;

// ─── Sidebar topic ─────────────────────────────────────────────────────────
// Generic so Settings.config can bind S = SettingSection without a circular dep.

export type SettingSidebarTopic<S extends string = string> = {
id: S;
label: string;
};

// ─── Inference helpers ─────────────────────────────────────────────────────

export type InferSettings<Schema extends Record<string, AnyFieldDef>> = {
[K in keyof Schema as Schema[K] extends NoteDef ? never : K]: Schema[K] extends ToggleDef
? boolean
: Schema[K] extends NumberDef | RangeDef
? number
: Schema[K] extends TextDef
? string
: never;
};

export function extractDefaults<Schema extends Record<string, AnyFieldDef>>(
schema: Schema
): InferSettings<Schema> {
const result: Record<string, unknown> = {};
for (const [key, field] of Object.entries(schema)) {
if (field.type !== 'note') {
result[key] = (field as SettingFieldDef).default;
}
}
return result as InferSettings<Schema>;
}
4 changes: 0 additions & 4 deletions src/main/shared/types/Sidebar.types.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/renderer/AppProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AppSettings } from '@shared/types/App.types';
import { AppSettings } from '@shared/config/Settings.config';
import { ConsoleLine, ProcessState } from '@shared/types/Process.types';
import { Profile } from '@shared/types/Profile.types';
import {
Expand Down
6 changes: 3 additions & 3 deletions src/renderer/components/layout/navigation/SidebarLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { SidebarTopic } from '@shared/types/Sidebar.types';
import type { SettingSidebarTopic } from '@shared/types/Settings.types';
import React from 'react';

export type { SidebarTopic };
export type { SettingSidebarTopic };

interface Props {
topics: SidebarTopic[];
topics: SettingSidebarTopic[];
activeTopicId: string;
onTopicChange: (id: string) => void;
children: React.ReactNode;
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/layout/navigation/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { PanelHeader } from './PanelHeader';
export { SidebarLayout } from './SidebarLayout';
export type { SidebarTopic } from './SidebarLayout';
export type { SettingSidebarTopic } from './SidebarLayout';
export { TabBar } from './TabBar';
export type { Tab } from './TabBar';
Loading
Loading