feat: 新增主进程功能

This commit is contained in:
DEV_DSW
2025-12-18 16:52:25 +08:00
parent 69a8e9472f
commit 6778c57a0e
25 changed files with 1503 additions and 161 deletions

View File

@@ -5,6 +5,7 @@ export enum IPC_EVENTS {
WINDOW_MINIMIZE = 'window-minimize',
WINDOW_MAXIMIZE = 'window-maximize',
WINDOW_CLOSE = 'window-close',
IS_WINDOW_MAXIMIZED = 'is-window-maximized',
APP_SET_FRAMELESS = 'app:set-frameless',
TAB_CREATE = 'tab:create',
TAB_LIST = 'tab:list',
@@ -27,12 +28,29 @@ export enum IPC_EVENTS {
CUSTOM_EVENT ='custom:event',
TIME_UPDATE = 'time:update',
RENDERER_IS_READY = 'renderer-ready',
SHOW_CONTEXT_MENU = 'show-context-menu',
START_A_DIALOGUE = 'start-a-dialogue',
// 打开窗口
OPEN_WINDOW = 'open-window',
// 发送日志
LOG_DEBUG = 'log-debug',
LOG_INFO = 'log-info',
LOG_WARN = 'log-warn',
LOG_ERROR = 'log-error',
// 设置
CONFIG_UPDATED = 'config-updated',
SET_CONFIG = 'set-config',
GET_CONFIG = 'get-config',
UPDATE_CONFIG = 'update-config',
// 主题
SET_THEME_MODE = 'set-theme-mode',
GET_THEME_MODE = 'get-theme-mode',
IS_DARK_THEME = 'is-dark-theme',
THEME_MODE_UPDATED = 'theme-mode-updated',
}
export const MAIN_WIN_SIZE = {
@@ -42,7 +60,48 @@ export const MAIN_WIN_SIZE = {
minHeight: 900,
} as const
export enum WINDOW_NAMES {
MAIN = 'main',
SETTING = 'setting',
DIALOG = 'dialog',
}
export enum CONFIG_KEYS {
THEME_MODE = 'themeMode',
PRIMARY_COLOR = 'primaryColor',
LANGUAGE = 'language',
FONT_SIZE = 'fontSize',
MINIMIZE_TO_TRAY = 'minimizeToTray',
PROVIDER = 'provider',
DEFAULT_MODEL = 'defaultModel',
}
export enum MENU_IDS {
CONVERSATION_ITEM = 'conversation-item',
CONVERSATION_LIST = 'conversation-list',
MESSAGE_ITEM = 'message-item',
}
export enum CONVERSATION_ITEM_MENU_IDS {
PIN = 'pin',
RENAME = 'rename',
DEL = 'del',
}
export enum CONVERSATION_LIST_MENU_IDS {
NEW_CONVERSATION = 'newConversation',
SORT_BY = 'sortBy',
SORT_BY_CREATE_TIME = 'sortByCreateTime',
SORT_BY_UPDATE_TIME = 'sortByUpdateTime',
SORT_BY_NAME = 'sortByName',
SORT_BY_MODEL = 'sortByModel',
SORT_ASCENDING = 'sortAscending',
SORT_DESCENDING = 'sortDescending',
BATCH_OPERATIONS = 'batchOperations',
}
export enum MESSAGE_ITEM_MENU_IDS {
COPY = 'copy',
DELETE = 'delete',
SELECT = 'select',
}

38
src/common/types.ts Normal file
View File

@@ -0,0 +1,38 @@
import { WINDOW_NAMES, CONFIG_KEYS } from './constants';
export type WindowNames = `${WINDOW_NAMES}`;
export type ConfigKeys = `${CONFIG_KEYS}`;
export interface IConfig {
// 主题模式配置
[CONFIG_KEYS.THEME_MODE]: ThemeMode;
// 高亮色
[CONFIG_KEYS.PRIMARY_COLOR]: string;
// 语言
[CONFIG_KEYS.LANGUAGE]: 'zh' | 'en';
// 字体大小
[CONFIG_KEYS.FONT_SIZE]: number;
// 关闭时最小化到托盘
[CONFIG_KEYS.MINIMIZE_TO_TRAY]: boolean;
// provider 配置 JSON
[CONFIG_KEYS.PROVIDER]?: string;
// 默认模型
[CONFIG_KEYS.DEFAULT_MODEL]?: string | null;
}
export interface Provider {
id: number;
name: string;
visible?: boolean;
title?: string;
type?: 'OpenAI';
openAISetting?: string;
createdAt: number;
updatedAt: number;
models: string[];
}
export interface OpenAISetting {
baseURL?: string;
apiKey?: string;
}

94
src/common/utils.ts Normal file
View File

@@ -0,0 +1,94 @@
import type { OpenAISetting } from './types'
import { encode, decode } from 'js-base64'
/**
* 防抖函数
* @param fn 需要执行的函数
* @param delay 延迟时间(毫秒)
* @returns 防抖处理后的函数
*/
export function debounce<T extends (...args: any[]) => any>(fn: T, delay: number): (...args: Parameters<T>) => void {
let timer: NodeJS.Timeout | null = null;
return function (this: any, ...args: Parameters<T>) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
/**
* 节流函数
* @param fn 需要执行的函数
* @param interval 间隔时间(毫秒)
* @returns 节流处理后的函数
*/
export function throttle<T extends (...args: any[]) => any>(fn: T, interval: number): (...args: Parameters<T>) => void {
let lastTime = 0;
return function (this: any, ...args: Parameters<T>) {
const now = Date.now();
if (now - lastTime >= interval) {
fn.apply(this, args);
lastTime = now;
}
};
}
export function cloneDeep<T>(obj: T): T {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(item => cloneDeep(item)) as T;
}
const clone = Object.assign({}, obj);
for (const key in clone) {
if (Object.prototype.hasOwnProperty.call(clone, key)) {
clone[key] = cloneDeep(clone[key]);
}
}
return clone;
}
export function simpleCloneDeep<T>(obj: T): T {
try {
return JSON.parse(JSON.stringify(obj));
} catch (error) {
console.error('simpleCloneDeep failed:', error);
return obj;
}
}
export function stringifyOpenAISetting(setting: OpenAISetting) {
try {
return encode(JSON.stringify(setting));
} catch (error) {
console.error('stringifyOpenAISetting failed:', error);
return '';
}
}
export function parseOpenAISetting(setting: string): OpenAISetting {
try {
return JSON.parse(decode(setting));
} catch (error) {
console.error('parseOpenAISetting failed:', error);
return {} as OpenAISetting;
}
}
export function uniqueByKey<T extends Record<string, any>>(arr: T[], key: keyof T): T[] {
const seen = new Map<any, boolean>();
return arr.filter(item => {
const keyValue = item[key];
if (seen.has(keyValue)) {
return false;
}
seen.set(keyValue, true);
return true;
});
}