This commit is contained in:
DEV_DSW
2025-12-09 17:03:30 +08:00
35 changed files with 1429 additions and 1218 deletions

View File

@@ -27,12 +27,12 @@ const config: ForgeConfig = {
build: [
{
// `entry` is just an alias for `build.lib.entry` in the corresponding file of `config`.
entry: 'src/electron/main/main.ts',
entry: 'src/main/main.ts',
config: 'vite.main.config.ts',
target: 'main',
},
{
entry: 'src/electron/preload/preload.ts',
entry: 'src/preload.ts',
config: 'vite.preload.config.ts',
target: 'preload',
},

7
global.d.ts vendored Normal file
View File

@@ -0,0 +1,7 @@
declare module "@store/*";
declare module "@utils/*";
declare module "@assets/images/*";
declare module "@constant/*";
declare module "@remixicon/vue";
declare module "vue-router";

1240
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
"description": "My Electron application description",
"main": ".vite/build/main.js",
"scripts": {
"start": "electron-forge start",
"start": "dotenv -e .env -- electron-forge start",
"package": "electron-forge package",
"make": "electron-forge make",
"publish": "electron-forge publish",
@@ -30,11 +30,10 @@
"@electron-forge/plugin-fuses": "^7.10.2",
"@electron-forge/plugin-vite": "^7.10.2",
"@electron/fuses": "^1.8.0",
"@tailwindcss/postcss": "^4.1.14",
"@tailwindcss/vite": "^4.0.0",
"@tailwindcss/typography": "^0.5.19",
"@tailwindcss/vite": "^4.1.17",
"@types/electron-squirrel-startup": "^1.0.2",
"@types/lodash-es": "^4.17.12",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@vitejs/plugin-vue": "^5.2.4",
"autoprefixer": "^10.4.21",
@@ -42,15 +41,17 @@
"eslint": "^8.57.1",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-vue": "^10.6.0",
"postcss": "^8.5.6",
"sass-embedded": "^1.89.1",
"tailwindcss": "^3.4.18",
"typescript": "~4.5.4",
"vite": "^7.1.9",
"vue-eslint-parser": "^10.2.0"
"tailwindcss": "^4.1.11",
"typescript": "^5.8.3",
"unplugin-auto-import": "^20.3.0",
"vite": "^7.1.9"
},
"dependencies": {
"@remixicon/vue": "^4.7.0",
"@vueuse/core": "^14.1.0",
"dotenv-cli": "^11.0.0",
"electron-log": "^5.4.3",
"axios": "^1.12.2",
"bytenode": "^1.5.7",
"crypto": "^1.0.1",
@@ -60,6 +61,8 @@
"log4js": "^6.9.1",
"pinia": "^2.3.1",
"vue": "^3.5.22",
"vue-i18n": "^11.1.9",
"vue-markdown-render": "^2.3.0",
"vue-router": "^4.5.1"
}
}

6
shims-vue.d.ts vendored
View File

@@ -1,6 +0,0 @@
declare module '*.vue' {
import type { DefineComponent } from 'vue'
// 泛型参数Props类型、Emits类型、Slots类型默认任意
const component: DefineComponent<{}, {}, any>
export default component
}

108
src/common/constants.ts Normal file
View File

@@ -0,0 +1,108 @@
// 渲染进程|主进程常量定义
export enum IPC_EVENTS {
EXTERNAL_OPEN = 'external-open',
WINDOW_MINIMIZE = 'window-minimize',
WINDOW_MAXIMIZE = 'window-maximize',
WINDOW_CLOSE = 'window-close',
APP_SET_FRAMELESS = 'app:set-frameless',
TAB_CREATE = 'tab:create',
TAB_LIST = 'tab:list',
TAB_NAVIGATE = 'tab:navigate',
TAB_RELOAD = 'tab:reload',
TAB_BACK = 'tab:back',
TAB_FORWARD = 'tab:forward',
TAB_SWITCH = 'tab:switch',
TAB_CLOSE = 'tab:close',
LOG_TO_MAIN = 'log-to-main',
READ_FILE = 'read-file',
INVOKE = 'ipc:invoke',
INVOKE_ASYNC = 'ipc:invokeAsync',
APP_MINIMIZE ='app:minimize',
APP_MAXIMIZE ='app:maximize',
APP_QUIT ='app:quit',
FILE_READ = 'file:read',
FILE_WRITE = 'file:write',
GET_WINDOW_ID='get-window-id',
CUSTOM_EVENT ='custom:event',
TIME_UPDATE = 'time:update'
}
export const MAIN_WIN_SIZE = {
width: 1440,
height: 900,
minWidth: 1440,
minHeight: 900,
} as const
// 定义每个通道的参数和返回值类型
interface IPCTypings {
// 同步通信
[IPC_EVENTS.APP_MINIMIZE]: {
params: [window:number]
return: {success: boolean, error?: string}
}
[IPC_EVENTS.APP_MAXIMIZE]: {
params: [window:number]
return: {success: boolean, error?: string}
}
[IPC_EVENTS.GET_WINDOW_ID]: {
params: []
return: number
}
// 异步通信
[IPC_EVENTS.FILE_READ]: {
params: [filePath: string]
return: Promise<{success: boolean, data?: string, error?: string}>
}
[IPC_EVENTS.FILE_WRITE]: {
params: [filePath: string, content: string]
return: Promise<{success: boolean, error?: string}>
}
// 事件通信
[IPC_EVENTS.TIME_UPDATE]: {
params: [time: string]
return: void
}
[IPC_EVENTS.CUSTOM_EVENT]: {
params: [message: string]
return: void
}
}
// 定义IPC API 接口
export interface WindowApi {
invoke<T extends keyof IPCTypings>(channel: T, ...args: IPCTypings[T]['params']): IPCTypings[T]['return'],
invokeAsync<T extends keyof IPCTypings>(channel: T, ...args: IPCTypings[T]['params']): IPCTypings[T]['return'],
on<T extends keyof IPCTypings>(channel: T, callback: (...args: IPCTypings[T]['params']) => void): () => void
send<T extends keyof IPCTypings>(channel: T, ...args: IPCTypings[T]['params']): void,
getCurrentWindowId(): number,
versions: NodeJS.ProcessVersions,
external: {
open: (url: string) => void
},
window: {
minimize: () => void,
maximize: () => void,
close: () => void
},
app: {
setFrameless: (route?: string) => void
},
tabs: {
create: (url?: string) => void,
list: () => void,
navigate: (tabId: string, url: string) => void,
reload: (tabId: string) => void,
back: (tabId: string) => void,
forward: (tabId: string) => void,
switch: (tabId: string) => void,
close: (tabId: string) => void,
on: (event: 'tab-updated' | 'tab-created' | 'tab-closed' | 'tab-switched', handler: (payload: any) => void) => void
},
readFile: (filePath: string) => Promise<{success: boolean, data?: string, error?: string}>,
logToMain: (logLevel: string, message: string) => void,
}

View File

@@ -1,28 +0,0 @@
const { chromium } = require("playwright");
(async () => {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
try {
// 打开百度
await page.goto("https://www.baidu.com");
// 输入搜索关键词并回车
await page.fill("textarea#chat-textarea", "Playwright 自动化");
await page.keyboard.press("Enter");
// 等待搜索结果加载
await page.waitForSelector("#content_left");
console.log("百度搜索执行成功");
} catch (e) {
console.error("自动化执行失败:", e);
process.exitCode = 1;
} finally {
// 保持几秒以方便观察,再关闭
await page.waitForTimeout(2000);
await browser.close();
}
})();

View File

@@ -1,247 +0,0 @@
const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');
// 录制配置
const recordingConfig = {
outputFile: 'recorded-steps.json',
headless: false, // 显示浏览器窗口
slowMo: 100, // 减慢操作速度,便于观察
viewport: { width: 1280, height: 720 }
};
// 存储录制的步骤
let recordedSteps = [];
let recordingStartTime = Date.now();
let isRecording = true;
// 生成时间戳
function getTimestamp() {
return Date.now() - recordingStartTime;
}
// 记录步骤
function recordStep(step) {
const stepWithTimestamp = {
...step,
timestamp: getTimestamp()
};
recordedSteps.push(stepWithTimestamp);
console.log(`📹 录制步骤: ${step.type} - ${step.description}`);
}
// 保存录制的步骤到文件
function saveRecordedSteps() {
const outputPath = path.join(__dirname, recordingConfig.outputFile);
const data = {
steps: recordedSteps,
metadata: {
totalSteps: recordedSteps.length,
duration: getTimestamp(),
recordedAt: new Date().toISOString()
}
};
fs.writeFileSync(outputPath, JSON.stringify(data, null, 2));
console.log(`\n✅ 录制完成!共录制 ${recordedSteps.length} 个步骤`);
console.log(`📁 录制文件已保存到: ${outputPath}`);
}
// 监听页面事件
async function setupPageListeners(page) {
// 监听点击事件
await page.exposeFunction('__recordClick', (selector, coordinates) => {
if (!isRecording) return;
recordStep({
type: 'click',
description: `点击元素: ${selector || '未知元素'}`,
selector: selector || '',
coordinates: coordinates || {}
});
});
// 监听输入事件
await page.exposeFunction('__recordInput', (selector, text) => {
if (!isRecording) return;
recordStep({
type: 'type',
description: `输入文本: "${text || ''}"`,
selector: selector || '',
text: text || ''
});
});
// 注入脚本监听用户操作
await page.addInitScript(() => {
// 监听点击事件
document.addEventListener('click', (event) => {
const target = event.target;
const selector = generateSelector(target);
const coordinates = { x: event.clientX, y: event.clientY };
if (window.__recordClick) {
window.__recordClick(selector, coordinates);
}
}, true);
// 监听输入事件
document.addEventListener('input', (event) => {
const target = event.target;
const selector = generateSelector(target);
const text = target.value || target.textContent || '';
if (window.__recordInput) {
window.__recordInput(selector, text);
}
}, true);
// 生成元素选择器
function generateSelector(element) {
if (!element) return '';
// 优先使用 ID
if (element.id) {
return `#${element.id}`;
}
// 使用 class 和标签名
const tagName = element.tagName.toLowerCase();
if (element.className) {
const classes = element.className.split(' ').filter(c => c.trim());
if (classes.length > 0) {
return `${tagName}.${classes.join('.')}`;
}
}
// 使用属性
const attributes = ['name', 'placeholder', 'type'];
for (const attr of attributes) {
if (element.getAttribute(attr)) {
return `${tagName}[${attr}="${element.getAttribute(attr)}"]`;
}
}
// 使用路径
return getElementPath(element);
}
function getElementPath(element) {
const path = [];
while (element && element.nodeType === Node.ELEMENT_NODE) {
let selector = element.nodeName.toLowerCase();
if (element.id) {
selector += `#${element.id}`;
path.unshift(selector);
break;
} else {
let sibling = element;
let nth = 1;
while (sibling = sibling.previousElementSibling) {
if (sibling.nodeName.toLowerCase() === selector) nth++;
}
if (nth !== 1) selector += `:nth-of-type(${nth})`;
}
path.unshift(selector);
element = element.parentNode;
}
return path.join(' > ');
}
});
}
// 主录制函数
async function startRecording() {
console.log('🎬 开始自动化录制...');
console.log('请在浏览器中进行操作,系统会自动记录您的操作步骤');
console.log('按 Ctrl+C 停止录制并保存结果\n');
let browser;
let page;
try {
// 启动浏览器
browser = await chromium.launch({
headless: recordingConfig.headless,
slowMo: recordingConfig.slowMo
});
// 创建新页面
page = await browser.newPage({
viewport: recordingConfig.viewport
});
// 设置页面监听器
await setupPageListeners(page);
// 监听页面导航
page.on('framenavigated', async (frame) => {
if (frame === page.mainFrame() && isRecording) {
recordStep({
type: 'navigate',
description: `导航到: ${frame.url()}`,
url: frame.url()
});
// 重新设置监听器
await setupPageListeners(page);
}
});
// 导航到百度首页作为起始页面
console.log('🌐 正在打开百度首页...');
await page.goto('https://www.baidu.com');
recordStep({
type: 'navigate',
description: '打开百度首页',
url: 'https://www.baidu.com'
});
console.log('✅ 录制已启动!请在浏览器中进行操作...');
console.log('💡 您可以:');
console.log(' - 点击页面元素');
console.log(' - 在输入框中输入文本');
console.log(' - 导航到其他页面');
console.log(' - 按回车键或其他键盘操作');
console.log('');
console.log('⚠️ 按 Ctrl+C 停止录制并保存结果');
// 保持浏览器打开,直到用户手动停止
await new Promise((resolve) => {
process.on('SIGINT', () => {
console.log('\n🛑 用户停止录制');
isRecording = false;
resolve();
});
});
} catch (error) {
console.error('❌ 录制过程中发生错误:', error);
} finally {
if (browser) {
// 保存录制的步骤
saveRecordedSteps();
await browser.close();
}
}
}
// 处理程序终止
process.on('SIGINT', () => {
console.log('\n🛑 用户停止录制');
saveRecordedSteps();
process.exit(0);
});
process.on('SIGTERM', () => {
console.log('\n🛑 程序终止');
saveRecordedSteps();
process.exit(0);
});
// 启动录制
(async () => {
await startRecording();
})();

View File

@@ -1,72 +0,0 @@
import { contextBridge, ipcRenderer } from 'electron'
import { IPCChannel, IPCAPI } from '@/shared/types/ipc.types'
const api: IPCAPI = {
versions: process.versions,
external: {
open: (url: string) => ipcRenderer.invoke('external-open', url)
},
window: {
minimize: () => ipcRenderer.send('window-min'),
maximize: () => ipcRenderer.send('window-max'),
close: () => ipcRenderer.send('window-close')
},
app: {
setFrameless: (route?: string) => ipcRenderer.invoke('app:set-frameless', route)
},
tabs: {
create: (url?: string) => ipcRenderer.invoke('tab:create', url),
list: () => ipcRenderer.invoke('tab:list'),
navigate: (tabId: string, url: string) => ipcRenderer.invoke('tab:navigate', { tabId, url }),
reload: (tabId: string) => ipcRenderer.invoke('tab:reload', tabId),
back: (tabId: string) => ipcRenderer.invoke('tab:back', tabId),
forward: (tabId: string) => ipcRenderer.invoke('tab:forward', tabId),
switch: (tabId: string) => ipcRenderer.invoke('tab:switch', tabId),
close: (tabId: string) => ipcRenderer.invoke('tab:close', tabId),
on: (event: 'tab-updated' | 'tab-created' | 'tab-closed' | 'tab-switched', handler: (payload: any) => void) => {
const listener = (_e: any, payload: any) => handler(payload)
ipcRenderer.on(event, listener)
return () => ipcRenderer.removeListener(event, listener)
}
},
// 通过 IPC 调用主进程
readFile: (filePath: string) => ipcRenderer.invoke('read-file', filePath),
// 同步调用
invoke: (channel: IPCChannel, ...args: any[]) => ipcRenderer.sendSync('ipc:invoke', channel, ...args),
// 异步调用
invokeAsync: (channel: IPCChannel, ...args: any[]) => {
try {
ipcRenderer.invoke('ipc:invokeAsync', channel, ...args)
} catch (error) {
throw error
}
},
// 监听主进程消息
on: (event: IPCChannel, callback: (...args: any[]) => void) => {
const subscription = (_event: any, ...args: any[]) => callback(...args)
ipcRenderer.on(event, subscription)
return () => ipcRenderer.removeListener(event, subscription)
},
// 发送消息到主进程
send: (channel: IPCChannel, ...args: any[]) => ipcRenderer.send(channel, ...args),
// 获取窗口ID
getCurrentWindowId: () => ipcRenderer.sendSync(IPCChannel.GET_WINDOW_ID),
// 发送日志
logToMain: (logLevel: string, message: string) => ipcRenderer.send('log-to-main', logLevel, message),
// 打开新窗口
openNewTab: (url: string) => ipcRenderer.invoke('open-new-tab', url)
}
contextBridge.exposeInMainWorld('ipcAPI', api)

View File

@@ -21,6 +21,7 @@ export default function electronBytecode(options?: ElectronBytecodeOptions): Plu
const outputPath = entryPath.replace(/\.js$/, '.jsc')
const backupPath = `${entryPath}.bak`
fs.copyFileSync(entryPath, backupPath)
await bytenode.compileFile({ filename: entryPath, output: outputPath })
const stub = [

69
src/preload.ts Normal file
View File

@@ -0,0 +1,69 @@
import { contextBridge, ipcRenderer } from 'electron'
import { IPC_EVENTS, WindowApi } from '@common/constants';
const api: WindowApi = {
versions: process.versions,
external: {
open: (url: string) => ipcRenderer.invoke('external-open', url)
},
window: {
minimize: () => ipcRenderer.send(IPC_EVENTS.WINDOW_MINIMIZE),
maximize: () => ipcRenderer.send(IPC_EVENTS.WINDOW_MAXIMIZE),
close: () => ipcRenderer.send(IPC_EVENTS.WINDOW_CLOSE)
},
app: {
setFrameless: (route?: string) => ipcRenderer.invoke(IPC_EVENTS.APP_SET_FRAMELESS, route)
},
tabs: {
create: (url?: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_CREATE, url),
list: () => ipcRenderer.invoke(IPC_EVENTS.TAB_LIST),
navigate: (tabId: string, url: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_NAVIGATE, { tabId, url }),
reload: (tabId: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_RELOAD, tabId),
back: (tabId: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_BACK, tabId),
forward: (tabId: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_FORWARD, tabId),
switch: (tabId: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_SWITCH, tabId),
close: (tabId: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_CLOSE, tabId),
on: (event: 'tab-updated' | 'tab-created' | 'tab-closed' | 'tab-switched', handler: (payload: any) => void) => {
const listener = (_e: any, payload: any) => handler(payload)
ipcRenderer.on(event, listener)
return () => ipcRenderer.removeListener(event, listener)
}
},
// 通过 IPC 调用主进程
readFile: (filePath: string) => ipcRenderer.invoke(IPC_EVENTS.READ_FILE, filePath),
// 同步调用
invoke: (channel: IPC_EVENTS, ...args: any[]) => ipcRenderer.sendSync(IPC_EVENTS.INVOKE, channel, ...args),
// 异步调用
invokeAsync: (channel: IPC_EVENTS, ...args: any[]) => {
try {
ipcRenderer.invoke(IPC_EVENTS.INVOKE_ASYNC, channel, ...args)
} catch (error) {
throw error
}
},
// 监听主进程消息
on: (event: IPC_EVENTS, callback: (...args: any[]) => void) => {
const subscription = (_event: any, ...args: any[]) => callback(...args)
ipcRenderer.on(event, subscription)
return () => ipcRenderer.removeListener(event, subscription)
},
// 发送消息到主进程
send: (channel: IPC_EVENTS, ...args: any[]) => ipcRenderer.send(channel, ...args),
// 获取窗口ID
getCurrentWindowId: () => ipcRenderer.sendSync(IPC_EVENTS.GET_WINDOW_ID),
// 发送日志
logToMain: (logLevel: string, message: string) => ipcRenderer.send(IPC_EVENTS.LOG_TO_MAIN, logLevel, message),
}
contextBridge.exposeInMainWorld('api', api)

317
src/renderer/auto-imports.d.ts vendored Normal file
View File

@@ -0,0 +1,317 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const EffectScope: typeof import('vue').EffectScope
const acceptHMRUpdate: typeof import('pinia').acceptHMRUpdate
const asyncComputed: typeof import('@vueuse/core').asyncComputed
const autoResetRef: typeof import('@vueuse/core').autoResetRef
const computed: typeof import('vue').computed
const computedAsync: typeof import('@vueuse/core').computedAsync
const computedEager: typeof import('@vueuse/core').computedEager
const computedInject: typeof import('@vueuse/core').computedInject
const computedWithControl: typeof import('@vueuse/core').computedWithControl
const controlledComputed: typeof import('@vueuse/core').controlledComputed
const controlledRef: typeof import('@vueuse/core').controlledRef
const createApp: typeof import('vue').createApp
const createEventHook: typeof import('@vueuse/core').createEventHook
const createGlobalState: typeof import('@vueuse/core').createGlobalState
const createInjectionState: typeof import('@vueuse/core').createInjectionState
const createPinia: typeof import('pinia').createPinia
const createReactiveFn: typeof import('@vueuse/core').createReactiveFn
const createRef: typeof import('@vueuse/core').createRef
const createReusableTemplate: typeof import('@vueuse/core').createReusableTemplate
const createSharedComposable: typeof import('@vueuse/core').createSharedComposable
const createTemplatePromise: typeof import('@vueuse/core').createTemplatePromise
const createUnrefFn: typeof import('@vueuse/core').createUnrefFn
const customRef: typeof import('vue').customRef
const debouncedRef: typeof import('@vueuse/core').debouncedRef
const debouncedWatch: typeof import('@vueuse/core').debouncedWatch
const defineAsyncComponent: typeof import('vue').defineAsyncComponent
const defineComponent: typeof import('vue').defineComponent
const defineStore: typeof import('pinia').defineStore
const eagerComputed: typeof import('@vueuse/core').eagerComputed
const effectScope: typeof import('vue').effectScope
const extendRef: typeof import('@vueuse/core').extendRef
const getActivePinia: typeof import('pinia').getActivePinia
const getCurrentInstance: typeof import('vue').getCurrentInstance
const getCurrentScope: typeof import('vue').getCurrentScope
const getCurrentWatcher: typeof import('vue').getCurrentWatcher
const h: typeof import('vue').h
const ignorableWatch: typeof import('@vueuse/core').ignorableWatch
const inject: typeof import('vue').inject
const injectLocal: typeof import('@vueuse/core').injectLocal
const isDefined: typeof import('@vueuse/core').isDefined
const isProxy: typeof import('vue').isProxy
const isReactive: typeof import('vue').isReactive
const isReadonly: typeof import('vue').isReadonly
const isRef: typeof import('vue').isRef
const isShallow: typeof import('vue').isShallow
const makeDestructurable: typeof import('@vueuse/core').makeDestructurable
const mapActions: typeof import('pinia').mapActions
const mapGetters: typeof import('pinia').mapGetters
const mapState: typeof import('pinia').mapState
const mapStores: typeof import('pinia').mapStores
const mapWritableState: typeof import('pinia').mapWritableState
const markRaw: typeof import('vue').markRaw
const nextTick: typeof import('vue').nextTick
const onActivated: typeof import('vue').onActivated
const onBeforeMount: typeof import('vue').onBeforeMount
const onBeforeRouteLeave: typeof import('vue-router').onBeforeRouteLeave
const onBeforeRouteUpdate: typeof import('vue-router').onBeforeRouteUpdate
const onBeforeUnmount: typeof import('vue').onBeforeUnmount
const onBeforeUpdate: typeof import('vue').onBeforeUpdate
const onClickOutside: typeof import('@vueuse/core').onClickOutside
const onDeactivated: typeof import('vue').onDeactivated
const onElementRemoval: typeof import('@vueuse/core').onElementRemoval
const onErrorCaptured: typeof import('vue').onErrorCaptured
const onKeyStroke: typeof import('@vueuse/core').onKeyStroke
const onLongPress: typeof import('@vueuse/core').onLongPress
const onMounted: typeof import('vue').onMounted
const onRenderTracked: typeof import('vue').onRenderTracked
const onRenderTriggered: typeof import('vue').onRenderTriggered
const onScopeDispose: typeof import('vue').onScopeDispose
const onServerPrefetch: typeof import('vue').onServerPrefetch
const onStartTyping: typeof import('@vueuse/core').onStartTyping
const onUnmounted: typeof import('vue').onUnmounted
const onUpdated: typeof import('vue').onUpdated
const onWatcherCleanup: typeof import('vue').onWatcherCleanup
const pausableWatch: typeof import('@vueuse/core').pausableWatch
const provide: typeof import('vue').provide
const provideLocal: typeof import('@vueuse/core').provideLocal
const reactify: typeof import('@vueuse/core').reactify
const reactifyObject: typeof import('@vueuse/core').reactifyObject
const reactive: typeof import('vue').reactive
const reactiveComputed: typeof import('@vueuse/core').reactiveComputed
const reactiveOmit: typeof import('@vueuse/core').reactiveOmit
const reactivePick: typeof import('@vueuse/core').reactivePick
const readonly: typeof import('vue').readonly
const ref: typeof import('vue').ref
const refAutoReset: typeof import('@vueuse/core').refAutoReset
const refDebounced: typeof import('@vueuse/core').refDebounced
const refDefault: typeof import('@vueuse/core').refDefault
const refManualReset: typeof import('@vueuse/core').refManualReset
const refThrottled: typeof import('@vueuse/core').refThrottled
const refWithControl: typeof import('@vueuse/core').refWithControl
const resolveComponent: typeof import('vue').resolveComponent
const resolveRef: typeof import('@vueuse/core').resolveRef
const setActivePinia: typeof import('pinia').setActivePinia
const setMapStoreSuffix: typeof import('pinia').setMapStoreSuffix
const shallowReactive: typeof import('vue').shallowReactive
const shallowReadonly: typeof import('vue').shallowReadonly
const shallowRef: typeof import('vue').shallowRef
const storeToRefs: typeof import('pinia').storeToRefs
const syncRef: typeof import('@vueuse/core').syncRef
const syncRefs: typeof import('@vueuse/core').syncRefs
const templateRef: typeof import('@vueuse/core').templateRef
const throttledRef: typeof import('@vueuse/core').throttledRef
const throttledWatch: typeof import('@vueuse/core').throttledWatch
const toRaw: typeof import('vue').toRaw
const toReactive: typeof import('@vueuse/core').toReactive
const toRef: typeof import('vue').toRef
const toRefs: typeof import('vue').toRefs
const toValue: typeof import('vue').toValue
const triggerRef: typeof import('vue').triggerRef
const tryOnBeforeMount: typeof import('@vueuse/core').tryOnBeforeMount
const tryOnBeforeUnmount: typeof import('@vueuse/core').tryOnBeforeUnmount
const tryOnMounted: typeof import('@vueuse/core').tryOnMounted
const tryOnScopeDispose: typeof import('@vueuse/core').tryOnScopeDispose
const tryOnUnmounted: typeof import('@vueuse/core').tryOnUnmounted
const unref: typeof import('vue').unref
const unrefElement: typeof import('@vueuse/core').unrefElement
const until: typeof import('@vueuse/core').until
const useActiveElement: typeof import('@vueuse/core').useActiveElement
const useAnimate: typeof import('@vueuse/core').useAnimate
const useArrayDifference: typeof import('@vueuse/core').useArrayDifference
const useArrayEvery: typeof import('@vueuse/core').useArrayEvery
const useArrayFilter: typeof import('@vueuse/core').useArrayFilter
const useArrayFind: typeof import('@vueuse/core').useArrayFind
const useArrayFindIndex: typeof import('@vueuse/core').useArrayFindIndex
const useArrayFindLast: typeof import('@vueuse/core').useArrayFindLast
const useArrayIncludes: typeof import('@vueuse/core').useArrayIncludes
const useArrayJoin: typeof import('@vueuse/core').useArrayJoin
const useArrayMap: typeof import('@vueuse/core').useArrayMap
const useArrayReduce: typeof import('@vueuse/core').useArrayReduce
const useArraySome: typeof import('@vueuse/core').useArraySome
const useArrayUnique: typeof import('@vueuse/core').useArrayUnique
const useAsyncQueue: typeof import('@vueuse/core').useAsyncQueue
const useAsyncState: typeof import('@vueuse/core').useAsyncState
const useAttrs: typeof import('vue').useAttrs
const useBase64: typeof import('@vueuse/core').useBase64
const useBattery: typeof import('@vueuse/core').useBattery
const useBluetooth: typeof import('@vueuse/core').useBluetooth
const useBreakpoints: typeof import('@vueuse/core').useBreakpoints
const useBroadcastChannel: typeof import('@vueuse/core').useBroadcastChannel
const useBrowserLocation: typeof import('@vueuse/core').useBrowserLocation
const useCached: typeof import('@vueuse/core').useCached
const useClipboard: typeof import('@vueuse/core').useClipboard
const useClipboardItems: typeof import('@vueuse/core').useClipboardItems
const useCloned: typeof import('@vueuse/core').useCloned
const useColorMode: typeof import('@vueuse/core').useColorMode
const useConfirmDialog: typeof import('@vueuse/core').useConfirmDialog
const useCountdown: typeof import('@vueuse/core').useCountdown
const useCounter: typeof import('@vueuse/core').useCounter
const useCssModule: typeof import('vue').useCssModule
const useCssVar: typeof import('@vueuse/core').useCssVar
const useCssVars: typeof import('vue').useCssVars
const useCurrentElement: typeof import('@vueuse/core').useCurrentElement
const useCycleList: typeof import('@vueuse/core').useCycleList
const useDark: typeof import('@vueuse/core').useDark
const useDateFormat: typeof import('@vueuse/core').useDateFormat
const useDebounce: typeof import('@vueuse/core').useDebounce
const useDebounceFn: typeof import('@vueuse/core').useDebounceFn
const useDebouncedRefHistory: typeof import('@vueuse/core').useDebouncedRefHistory
const useDeviceMotion: typeof import('@vueuse/core').useDeviceMotion
const useDeviceOrientation: typeof import('@vueuse/core').useDeviceOrientation
const useDevicePixelRatio: typeof import('@vueuse/core').useDevicePixelRatio
const useDevicesList: typeof import('@vueuse/core').useDevicesList
const useDisplayMedia: typeof import('@vueuse/core').useDisplayMedia
const useDocumentVisibility: typeof import('@vueuse/core').useDocumentVisibility
const useDraggable: typeof import('@vueuse/core').useDraggable
const useDropZone: typeof import('@vueuse/core').useDropZone
const useElementBounding: typeof import('@vueuse/core').useElementBounding
const useElementByPoint: typeof import('@vueuse/core').useElementByPoint
const useElementHover: typeof import('@vueuse/core').useElementHover
const useElementSize: typeof import('@vueuse/core').useElementSize
const useElementVisibility: typeof import('@vueuse/core').useElementVisibility
const useEventBus: typeof import('@vueuse/core').useEventBus
const useEventListener: typeof import('@vueuse/core').useEventListener
const useEventSource: typeof import('@vueuse/core').useEventSource
const useEyeDropper: typeof import('@vueuse/core').useEyeDropper
const useFavicon: typeof import('@vueuse/core').useFavicon
const useFetch: typeof import('@vueuse/core').useFetch
const useFileDialog: typeof import('@vueuse/core').useFileDialog
const useFileSystemAccess: typeof import('@vueuse/core').useFileSystemAccess
const useFocus: typeof import('@vueuse/core').useFocus
const useFocusWithin: typeof import('@vueuse/core').useFocusWithin
const useFps: typeof import('@vueuse/core').useFps
const useFullscreen: typeof import('@vueuse/core').useFullscreen
const useGamepad: typeof import('@vueuse/core').useGamepad
const useGeolocation: typeof import('@vueuse/core').useGeolocation
const useId: typeof import('vue').useId
const useIdle: typeof import('@vueuse/core').useIdle
const useImage: typeof import('@vueuse/core').useImage
const useInfiniteScroll: typeof import('@vueuse/core').useInfiniteScroll
const useIntersectionObserver: typeof import('@vueuse/core').useIntersectionObserver
const useInterval: typeof import('@vueuse/core').useInterval
const useIntervalFn: typeof import('@vueuse/core').useIntervalFn
const useKeyModifier: typeof import('@vueuse/core').useKeyModifier
const useLastChanged: typeof import('@vueuse/core').useLastChanged
const useLink: typeof import('vue-router').useLink
const useLocalStorage: typeof import('@vueuse/core').useLocalStorage
const useMagicKeys: typeof import('@vueuse/core').useMagicKeys
const useManualRefHistory: typeof import('@vueuse/core').useManualRefHistory
const useMediaControls: typeof import('@vueuse/core').useMediaControls
const useMediaQuery: typeof import('@vueuse/core').useMediaQuery
const useMemoize: typeof import('@vueuse/core').useMemoize
const useMemory: typeof import('@vueuse/core').useMemory
const useModel: typeof import('vue').useModel
const useMounted: typeof import('@vueuse/core').useMounted
const useMouse: typeof import('@vueuse/core').useMouse
const useMouseInElement: typeof import('@vueuse/core').useMouseInElement
const useMousePressed: typeof import('@vueuse/core').useMousePressed
const useMutationObserver: typeof import('@vueuse/core').useMutationObserver
const useNavigatorLanguage: typeof import('@vueuse/core').useNavigatorLanguage
const useNetwork: typeof import('@vueuse/core').useNetwork
const useNow: typeof import('@vueuse/core').useNow
const useObjectUrl: typeof import('@vueuse/core').useObjectUrl
const useOffsetPagination: typeof import('@vueuse/core').useOffsetPagination
const useOnline: typeof import('@vueuse/core').useOnline
const usePageLeave: typeof import('@vueuse/core').usePageLeave
const useParallax: typeof import('@vueuse/core').useParallax
const useParentElement: typeof import('@vueuse/core').useParentElement
const usePerformanceObserver: typeof import('@vueuse/core').usePerformanceObserver
const usePermission: typeof import('@vueuse/core').usePermission
const usePointer: typeof import('@vueuse/core').usePointer
const usePointerLock: typeof import('@vueuse/core').usePointerLock
const usePointerSwipe: typeof import('@vueuse/core').usePointerSwipe
const usePreferredColorScheme: typeof import('@vueuse/core').usePreferredColorScheme
const usePreferredContrast: typeof import('@vueuse/core').usePreferredContrast
const usePreferredDark: typeof import('@vueuse/core').usePreferredDark
const usePreferredLanguages: typeof import('@vueuse/core').usePreferredLanguages
const usePreferredReducedMotion: typeof import('@vueuse/core').usePreferredReducedMotion
const usePreferredReducedTransparency: typeof import('@vueuse/core').usePreferredReducedTransparency
const usePrevious: typeof import('@vueuse/core').usePrevious
const useRafFn: typeof import('@vueuse/core').useRafFn
const useRefHistory: typeof import('@vueuse/core').useRefHistory
const useResizeObserver: typeof import('@vueuse/core').useResizeObserver
const useRoute: typeof import('vue-router').useRoute
const useRouter: typeof import('vue-router').useRouter
const useSSRWidth: typeof import('@vueuse/core').useSSRWidth
const useScreenOrientation: typeof import('@vueuse/core').useScreenOrientation
const useScreenSafeArea: typeof import('@vueuse/core').useScreenSafeArea
const useScriptTag: typeof import('@vueuse/core').useScriptTag
const useScroll: typeof import('@vueuse/core').useScroll
const useScrollLock: typeof import('@vueuse/core').useScrollLock
const useSessionStorage: typeof import('@vueuse/core').useSessionStorage
const useShare: typeof import('@vueuse/core').useShare
const useSlots: typeof import('vue').useSlots
const useSorted: typeof import('@vueuse/core').useSorted
const useSpeechRecognition: typeof import('@vueuse/core').useSpeechRecognition
const useSpeechSynthesis: typeof import('@vueuse/core').useSpeechSynthesis
const useStepper: typeof import('@vueuse/core').useStepper
const useStorage: typeof import('@vueuse/core').useStorage
const useStorageAsync: typeof import('@vueuse/core').useStorageAsync
const useStyleTag: typeof import('@vueuse/core').useStyleTag
const useSupported: typeof import('@vueuse/core').useSupported
const useSwipe: typeof import('@vueuse/core').useSwipe
const useTemplateRef: typeof import('vue').useTemplateRef
const useTemplateRefsList: typeof import('@vueuse/core').useTemplateRefsList
const useTextDirection: typeof import('@vueuse/core').useTextDirection
const useTextSelection: typeof import('@vueuse/core').useTextSelection
const useTextareaAutosize: typeof import('@vueuse/core').useTextareaAutosize
const useThrottle: typeof import('@vueuse/core').useThrottle
const useThrottleFn: typeof import('@vueuse/core').useThrottleFn
const useThrottledRefHistory: typeof import('@vueuse/core').useThrottledRefHistory
const useTimeAgo: typeof import('@vueuse/core').useTimeAgo
const useTimeAgoIntl: typeof import('@vueuse/core').useTimeAgoIntl
const useTimeout: typeof import('@vueuse/core').useTimeout
const useTimeoutFn: typeof import('@vueuse/core').useTimeoutFn
const useTimeoutPoll: typeof import('@vueuse/core').useTimeoutPoll
const useTimestamp: typeof import('@vueuse/core').useTimestamp
const useTitle: typeof import('@vueuse/core').useTitle
const useToNumber: typeof import('@vueuse/core').useToNumber
const useToString: typeof import('@vueuse/core').useToString
const useToggle: typeof import('@vueuse/core').useToggle
const useTransition: typeof import('@vueuse/core').useTransition
const useUrlSearchParams: typeof import('@vueuse/core').useUrlSearchParams
const useUserMedia: typeof import('@vueuse/core').useUserMedia
const useVModel: typeof import('@vueuse/core').useVModel
const useVModels: typeof import('@vueuse/core').useVModels
const useVibrate: typeof import('@vueuse/core').useVibrate
const useVirtualList: typeof import('@vueuse/core').useVirtualList
const useWakeLock: typeof import('@vueuse/core').useWakeLock
const useWebNotification: typeof import('@vueuse/core').useWebNotification
const useWebSocket: typeof import('@vueuse/core').useWebSocket
const useWebWorker: typeof import('@vueuse/core').useWebWorker
const useWebWorkerFn: typeof import('@vueuse/core').useWebWorkerFn
const useWindowFocus: typeof import('@vueuse/core').useWindowFocus
const useWindowScroll: typeof import('@vueuse/core').useWindowScroll
const useWindowSize: typeof import('@vueuse/core').useWindowSize
const watch: typeof import('vue').watch
const watchArray: typeof import('@vueuse/core').watchArray
const watchAtMost: typeof import('@vueuse/core').watchAtMost
const watchDebounced: typeof import('@vueuse/core').watchDebounced
const watchDeep: typeof import('@vueuse/core').watchDeep
const watchEffect: typeof import('vue').watchEffect
const watchIgnorable: typeof import('@vueuse/core').watchIgnorable
const watchImmediate: typeof import('@vueuse/core').watchImmediate
const watchOnce: typeof import('@vueuse/core').watchOnce
const watchPausable: typeof import('@vueuse/core').watchPausable
const watchPostEffect: typeof import('vue').watchPostEffect
const watchSyncEffect: typeof import('vue').watchSyncEffect
const watchThrottled: typeof import('@vueuse/core').watchThrottled
const watchTriggerable: typeof import('@vueuse/core').watchTriggerable
const watchWithFilter: typeof import('@vueuse/core').watchWithFilter
const whenever: typeof import('@vueuse/core').whenever
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}

View File

@@ -57,26 +57,26 @@ const refreshActiveAddress = () => {
}
const syncList = async () => {
const list: TabInfo[] = await (window as any).ipcAPI.tabs.list()
const list: TabInfo[] = await (window as any).api.tabs.list()
tabs.splice(0, tabs.length, ...list)
if (!activeId.value && list.length > 0) activeId.value = list[0].id
refreshActiveAddress()
}
const onNewTab = async () => {
const info: TabInfo = await (window as any).ipcAPI.tabs.create('about:blank')
const info: TabInfo = await (window as any).api.tabs.create('about:blank')
activeId.value = info.id
}
const onSwitch = async (id: string) => {
await (window as any).ipcAPI.tabs.switch(id)
await (window as any).api.tabs.switch(id)
activeId.value = id
refreshActiveAddress()
}
const onCloseTab = async () => {
if (!activeId.value) return
await (window as any).ipcAPI.tabs.close(activeId.value)
await (window as any).api.tabs.close(activeId.value)
}
const normalizeUrl = (u: string) => {
@@ -90,46 +90,46 @@ const onNavigate = async () => {
if (!activeId.value || !address.value) return
const url = normalizeUrl(address.value)
if (!url) return
await (window as any).ipcAPI.tabs.navigate(activeId.value, url)
await (window as any).api.tabs.navigate(activeId.value, url)
}
const onReload = async () => {
if (!activeId.value) return
await (window as any).ipcAPI.tabs.reload(activeId.value)
await (window as any).api.tabs.reload(activeId.value)
}
const onBack = async () => {
if (!activeId.value) return
await (window as any).ipcAPI.tabs.back(activeId.value)
await (window as any).api.tabs.back(activeId.value)
}
const onForward = async () => {
if (!activeId.value) return
await (window as any).ipcAPI.tabs.forward(activeId.value)
await (window as any).api.tabs.forward(activeId.value)
}
const onCloseTabId = async (id: string) => {
await (window as any).ipcAPI.tabs.close(id)
await (window as any).api.tabs.close(id)
}
const onCloseWindow = () => (window as any).ipcAPI.window.close()
const onMinimizeWindow = () => (window as any).ipcAPI.window.minimize()
const onMaximizeWindow = () => (window as any).ipcAPI.window.maximize()
const onCloseWindow = () => (window as any).api.window.close()
const onMinimizeWindow = () => (window as any).api.window.minimize()
const onMaximizeWindow = () => (window as any).api.window.maximize()
onMounted(async () => {
await syncList()
; (window as any).ipcAPI.tabs.on('tab-created', (info: TabInfo) => {
; (window as any).api.tabs.on('tab-created', (info: TabInfo) => {
const i = tabs.findIndex(t => t.id === info.id)
if (i === -1) tabs.push(info)
activeId.value = info.id
refreshActiveAddress()
})
; (window as any).ipcAPI.tabs.on('tab-updated', (info: TabInfo) => {
; (window as any).api.tabs.on('tab-updated', (info: TabInfo) => {
const i = tabs.findIndex(t => t.id === info.id)
if (i >= 0) tabs[i] = info
if (activeId.value === info.id) refreshActiveAddress()
})
; (window as any).ipcAPI.tabs.on('tab-closed', ({ tabId }: { tabId: string }) => {
; (window as any).api.tabs.on('tab-closed', ({ tabId }: { tabId: string }) => {
const i = tabs.findIndex(t => t.id === tabId)
if (i >= 0) tabs.splice(i, 1)
if (activeId.value === tabId) {
@@ -138,7 +138,7 @@ onMounted(async () => {
refreshActiveAddress()
}
})
; (window as any).ipcAPI.tabs.on('tab-switched', ({ tabId }: { tabId: string }) => {
; (window as any).api.tabs.on('tab-switched', ({ tabId }: { tabId: string }) => {
activeId.value = tabId
refreshActiveAddress()
})

View File

@@ -103,7 +103,7 @@ tab-switched: { tabId: TabId }
1. 核心能力(主进程 + IPC + 预加载)
- 实现 `TabManager` 与全部导航方法
- 注册 IPCtabs/bookmarks/plugins与广播
- 预加载扩展 `window.ipcAPI.tabs/*`、事件订阅封装
- 预加载扩展 `window.api.tabs/*`、事件订阅封装
2. 渲染层界面
- 新建 `BrowserLayout` 与基础组件TabBar、AddressBar、Controls
- 打通地址栏与导航;同步标题与加载状态

View File

@@ -1,7 +0,0 @@
declare module "@store/counter";
declare module "@utils/request";
declare module "@assets/images/*";
declare module "@constant/rate";
declare module "@constant/menus";
declare module "@remixicon/vue";
declare module "vue-router";

View File

@@ -1,12 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
margin: 0;
padding: 0;
}
.bg {
background: linear-gradient( 180deg, #EFF6FF 0%, #F5F7FA 40%);
}

View File

@@ -1,4 +1,4 @@
import "./index.css";
import "./styles/index.css";
import { createApp } from "vue";
import { createPinia } from "pinia";
import router from "./router";
@@ -16,9 +16,7 @@ app.use(createPinia());
// 使用 Vue Router
app.use(router);
app.use(ElementPlus, {
locale,
})
app.use(ElementPlus, { locale })
// 挂载应用到 DOM
app.mount("#app");

View File

@@ -0,0 +1,12 @@
@import "tailwindcss";
@import "./theme/index.css";
@plugin "@tailwindcss/typography";
body {
margin: 0;
padding: 0;
}
.bg {
background: linear-gradient(180deg, #EFF6FF 0%, #F5F7FA 40%);
}

View File

@@ -0,0 +1,126 @@
@media (prefers-color-scheme: dark) {
:root {
--primary-color: #07C160;
--bg-color: #1E1E1E;
--bg-secondary: #2C2C2C;
--text-primary: #E0E0E0;
--text-secondary: #A0A0A0;
--bubble-self: var(--primary-color);
--bubble-others: #3A3A3A;
--input-bg: #333333;
--ripple-color: var(--text-secondary);
--ripple-opacity: 0.2;
}
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em
}
code.hljs {
padding: 3px 5px
}
/*!
Theme: GitHub Dark
Description: Dark theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Outdated base version: https://github.com/primer/github-syntax-dark
Current colors taken from GitHub's CSS
*/
.hljs {
color: var(--text-primary);
background: var(--input-bg);
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
/* prettylights-syntax-keyword */
color: #ff7b72
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
/* prettylights-syntax-entity */
color: #d2a8ff
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-variable,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id {
/* prettylights-syntax-constant */
color: #79c0ff
}
.hljs-regexp,
.hljs-string,
.hljs-meta .hljs-string {
/* prettylights-syntax-string */
color: #a5d6ff
}
.hljs-built_in,
.hljs-symbol {
/* prettylights-syntax-variable */
color: #ffa657
}
.hljs-comment,
.hljs-code,
.hljs-formula {
/* prettylights-syntax-comment */
color: #8b949e
}
.hljs-name,
.hljs-quote,
.hljs-selector-tag,
.hljs-selector-pseudo {
/* prettylights-syntax-entity-tag */
color: #7ee787
}
.hljs-subst {
/* prettylights-syntax-storage-modifier-import */
color: #c9d1d9
}
.hljs-section {
/* prettylights-syntax-markup-heading */
color: #1f6feb;
font-weight: bold
}
.hljs-bullet {
/* prettylights-syntax-markup-list */
color: #f2cc60
}
.hljs-emphasis {
/* prettylights-syntax-markup-italic */
color: #c9d1d9;
font-style: italic
}
.hljs-strong {
/* prettylights-syntax-markup-bold */
color: #c9d1d9;
font-weight: bold
}
.hljs-addition {
/* prettylights-syntax-markup-inserted */
color: #aff5b4;
background-color: #033a16
}
.hljs-deletion {
/* prettylights-syntax-markup-deleted */
color: #ffdcd7;
background-color: #67060c
}
}

View File

@@ -0,0 +1,18 @@
@import './dark.css';
@import './light.css';
@theme {
--color-primary: var(--primary-color);
--color-primary-light: var(--primary-color-light);
--color-primary-dark: var(--primary-color-dark);
--color-primary-hover: var(--primary-color-hover);
--color-primary-active: var(--primary-color-active);
--color-primary-subtle: var(--primary-color-subtle);
--color-main: var(--bg-color);
--color-secondary: var(--bg-secondary);
--color-input: var(--input-bg);
--color-bubble-self: var(--bubble-self);
--color-bubble-others: var(--bubble-others);
--color-tx-primary: var(--text-primary);
--color-tx-secondary: var(--text-secondary);
}

View File

@@ -0,0 +1,129 @@
@media (prefers-color-scheme: light) {
:root {
--primary-color: #07C160;
--bg-color: #FFFFFF;
--bg-secondary: #F5F5F5;
--text-primary: #000000;
--text-secondary: #7F7F7F;
--header-bg: var(--primary-color);
--bubble-self: var(--primary-color);
--bubble-others: #FFFFFF;
--input-bg: #F0F0F0;
--ripple-color: var(--text-secondary);
--ripple-opacity: 0.2;
}
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em
}
code.hljs {
padding: 3px 5px
}
/*!
Theme: GitHub
Description: Light theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Outdated base version: https://github.com/primer/github-syntax-light
Current colors taken from GitHub's CSS
*/
.hljs {
/* color: #24292e;
background: #ffffff */
color: var(--text-primary);
background: var(--input-bg);
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
/* prettylights-syntax-keyword */
color: #d73a49
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
/* prettylights-syntax-entity */
color: #6f42c1
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-variable,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id {
/* prettylights-syntax-constant */
color: #005cc5
}
.hljs-regexp,
.hljs-string,
.hljs-meta .hljs-string {
/* prettylights-syntax-string */
color: #032f62
}
.hljs-built_in,
.hljs-symbol {
/* prettylights-syntax-variable */
color: #e36209
}
.hljs-comment,
.hljs-code,
.hljs-formula {
/* prettylights-syntax-comment */
color: #6a737d
}
.hljs-name,
.hljs-quote,
.hljs-selector-tag,
.hljs-selector-pseudo {
/* prettylights-syntax-entity-tag */
color: #22863a
}
.hljs-subst {
/* prettylights-syntax-storage-modifier-import */
color: #24292e
}
.hljs-section {
/* prettylights-syntax-markup-heading */
color: #005cc5;
font-weight: bold
}
.hljs-bullet {
/* prettylights-syntax-markup-list */
color: #735c0f
}
.hljs-emphasis {
/* prettylights-syntax-markup-italic */
color: #24292e;
font-style: italic
}
.hljs-strong {
/* prettylights-syntax-markup-bold */
color: #24292e;
font-weight: bold
}
.hljs-addition {
/* prettylights-syntax-markup-inserted */
color: #22863a;
background-color: #f0fff4
}
.hljs-deletion {
/* prettylights-syntax-markup-deleted */
color: #b31d28;
background-color: #ffeef0
}
}

View File

@@ -105,7 +105,7 @@ const onSubmit = async () => {
// const token = res && (res.token || res.data?.token || res.access_token);
// if (!token) throw new Error("登录失败");
// localStorage.setItem("token", token);
// await (window as any).ipcAPI.app.setFrameless('/home')
// await (window as any).api.app.setFrameless('/home')
router.push('/home');
} finally {
// loading.value = false;

View File

@@ -1,83 +0,0 @@
export enum IPCChannel {
APP_MINIMIZE ='app:minimize',
APP_MAXIMIZE ='app:maximize',
APP_QUIT ='app:quit',
FILE_READ = 'file:read',
FILE_WRITE = 'file:write',
GET_WINDOW_ID='get-window-id',
CUSTOM_EVENT ='custom:event',
TIME_UPDATE = 'time:update'
}
// 定义每个通道的参数和返回值类型
export interface IPCTypings {
// 同步通信
[IPCChannel.APP_MINIMIZE]: {
params: [window:number]
return: {success: boolean, error?: string}
}
[IPCChannel.APP_MAXIMIZE]: {
params: [window:number]
return: {success: boolean, error?: string}
}
[IPCChannel.GET_WINDOW_ID]: {
params: []
return: number
}
// 异步通信
[IPCChannel.FILE_READ]: {
params: [filePath: string]
return: Promise<{success: boolean, data?: string, error?: string}>
}
[IPCChannel.FILE_WRITE]: {
params: [filePath: string, content: string]
return: Promise<{success: boolean, error?: string}>
}
// 事件通信
[IPCChannel.TIME_UPDATE]: {
params: [time: string]
return: void
}
[IPCChannel.CUSTOM_EVENT]: {
params: [message: string]
return: void
}
}
// 定义IPC API 接口
export interface IPCAPI {
invoke<T extends keyof IPCTypings>(channel: T, ...args: IPCTypings[T]['params']): IPCTypings[T]['return'],
invokeAsync<T extends keyof IPCTypings>(channel: T, ...args: IPCTypings[T]['params']): IPCTypings[T]['return'],
on<T extends keyof IPCTypings>(channel: T, callback: (...args: IPCTypings[T]['params']) => void): () => void
send<T extends keyof IPCTypings>(channel: T, ...args: IPCTypings[T]['params']): void,
getCurrentWindowId(): number,
versions: NodeJS.ProcessVersions,
external: {
open: (url: string) => void
},
window: {
minimize: () => void,
maximize: () => void,
close: () => void
},
app: {
setFrameless: (route?: string) => void
},
tabs: {
create: (url?: string) => void,
list: () => void,
navigate: (tabId: string, url: string) => void,
reload: (tabId: string) => void,
back: (tabId: string) => void,
forward: (tabId: string) => void,
switch: (tabId: string) => void,
close: (tabId: string) => void,
on: (event: 'tab-updated' | 'tab-created' | 'tab-closed' | 'tab-switched', handler: (payload: any) => void) => void
},
readFile: (filePath: string) => Promise<{success: boolean, data?: string, error?: string}>,
logToMain: (logLevel: string, message: string) => void,
// 打开新窗口
openNewTab: (url: string) => Promise<void>,
}

View File

@@ -1,11 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}

View File

@@ -17,16 +17,17 @@
"paths": {
"@/*": ["src/renderer/*"],
"@assets/*": ["src/assets/*"],
"@common/*": ["src/common/*"],
"@store/*": ["src/renderer/store/*"],
"@constant/*": ["src/renderer/constant/*"],
"@utils/*": ["src/renderer/utils/*"],
"@api/*": ["src/renderer/api/*"],
"@/types": ["src/renderer/types/index.ts"],
"@modules/*": ["src/electron/main/modules/*"],
"@modules/*": ["src/main/modules/*"],
"@/shared/*": ["src/shared/*"],
},
"outDir": "dist",
"moduleResolution": "bundler",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"types": ["element-plus/global"]
},
@@ -34,7 +35,11 @@
"src/**/*",
"./package.json",
"./forge.config.ts",
"*.ts",
"vite.renderer.config.ts"
, "packages/electron-chrome-context-menu", "packages/chrome-ui" ]
"forge.env.d.ts",
"**/*.ts",
"**/*.d.ts",
"vite.renderer.config.ts",
"packages/electron-chrome-context-menu",
"packages/chrome-ui"
]
}

View File

@@ -1,14 +1,18 @@
import { defineConfig } from "vite";
import { resolve } from "path";
import electronBytecode from "./src/plugins/bytenode/vite-plugin-electron-encrypt";
// https://vitejs.dev/config
export default defineConfig({
plugins: [electronBytecode({ entry: ".vite/build/main.js", keepSource: false })],
resolve: {
alias: {
"@": resolve(__dirname, "./src/electron"),
"@modules": resolve(__dirname, "./src/electron/main/modules"),
export default defineConfig( async () => {
const electronBytecode = (await import("./src/plugins/bytenode/vite-plugin-electron-encrypt")).default
return {
plugins: [electronBytecode({ entry: ".vite/build/main.js", keepSource: false })],
resolve: {
alias: {
"@": resolve(__dirname, "./src/main"),
'@common': resolve(__dirname, './src/common'),
"@modules": resolve(__dirname, "./src/main/modules"),
},
},
},
}
});

View File

@@ -3,9 +3,10 @@ import { resolve } from "path";
// https://vitejs.dev/config
export default defineConfig({
resolve: {
resolve: {
alias: {
"@": resolve(__dirname, "./src")
"@": resolve(__dirname, "./src"),
'@common': resolve(__dirname, './src/common'),
},
},
});

View File

@@ -1,25 +1,34 @@
import { resolve } from "path";
import vue from "@vitejs/plugin-vue";
import { defineConfig } from "vite";
import { defineConfig, type CSSOptions } from "vite";
// https://vitejs.dev/config
export default defineConfig({
plugins: [vue()],
css: {
postcss: {
plugins: [require("tailwindcss"), require("autoprefixer")],
export default defineConfig(async () => {
const vue = (await import("@vitejs/plugin-vue")).default
const tailwindcss = (await import('@tailwindcss/vite')).default;
const autoImport = (await import('unplugin-auto-import/vite')).default;
return {
plugins: [vue(), tailwindcss(), autoImport({
imports: ['vue', 'vue-router', 'pinia', '@vueuse/core'],
dts: 'src/renderer/auto-imports.d.ts'
})],
css: {
transformer: 'lightningcss' as CSSOptions['transformer'],
},
},
resolve: {
preserveSymlinks: true,
alias: {
"@": resolve(__dirname, "./src/renderer"),
"@assets": resolve(__dirname, "./src/assets"),
"@store": resolve(__dirname, "./src/renderer/store"),
"@constant": resolve(__dirname, "./src/renderer/constant"),
"@utils": resolve(__dirname, "./src/renderer/utils"),
"@api": resolve(__dirname, "./src/renderer/api"),
"@/types": resolve(__dirname, "./src/renderer/types"),
resolve: {
preserveSymlinks: true,
alias: {
"@": resolve(__dirname, "./src/renderer"),
"@api": resolve(__dirname, "./src/renderer/api"),
"@assets": resolve(__dirname, "./src/assets"),
'@common': resolve(__dirname, './src/common'),
"@constant": resolve(__dirname, "./src/renderer/constant"),
"@store": resolve(__dirname, "./src/renderer/store"),
"@utils": resolve(__dirname, "./src/renderer/utils"),
"@/types": resolve(__dirname, "./src/renderer/types"),
},
},
},
}
});