Remove unused dependencies and optimize Monaco workers

Removed @simplewebauthn/browser, framer-motion, and react-responsive from dependencies as they are no longer used. Updated Monaco editor configuration to only load the JSON worker for improved performance, falling back to the basic editor worker for other languages. Refactored the new version tip UI to use Chip and Spinner instead of Button and removed unused react-icons import. Also updated Vite config to stop sharing react-icons.
This commit is contained in:
手瓜一十雪 2025-12-24 15:32:21 +08:00
parent fa3a229827
commit 50bcd71144
17 changed files with 2185 additions and 122 deletions

View File

@ -29,6 +29,7 @@
"dependencies": {
"express": "^5.0.0",
"silk-wasm": "^3.6.1",
"vite-plugin-font": "^5.1.2",
"ws": "^8.18.3"
}
}

View File

@ -6,6 +6,7 @@
"scripts": {
"dev": "vite --host=0.0.0.0",
"build": "tsc && vite build",
"fontmin": "node scripts/fontmin.cjs",
"typecheck": "tsc --noEmit",
"lint": "eslint -c eslint.config.mjs ./src/**/**/*.{ts,tsx} --fix",
"preview": "vite preview"
@ -49,7 +50,6 @@
"@monaco-editor/react": "4.7.0-rc.0",
"@react-aria/visually-hidden": "^3.8.19",
"@reduxjs/toolkit": "^2.5.1",
"@simplewebauthn/browser": "^13.2.2",
"@uidotdev/usehooks": "^2.4.1",
"@xterm/addon-canvas": "^0.7.0",
"@xterm/addon-fit": "^0.10.0",
@ -60,7 +60,6 @@
"clsx": "^2.1.1",
"crypto-js": "^4.2.0",
"event-source-polyfill": "^1.0.31",
"framer-motion": "^12.0.6",
"monaco-editor": "^0.52.2",
"motion": "^12.0.6",
"path-browserify": "^1.0.1",
@ -78,7 +77,6 @@
"react-markdown": "^9.0.3",
"react-photo-view": "^1.2.7",
"react-redux": "^9.2.0",
"react-responsive": "^10.0.0",
"react-router-dom": "^7.1.4",
"react-use-websocket": "^4.11.1",
"react-window": "^1.8.11",
@ -110,8 +108,11 @@
"prettier": "^3.4.2",
"typescript": "^5.7.3",
"vite": "^6.0.5",
"vite-plugin-font": "^5.1.2",
"vite-plugin-static-copy": "^2.2.0",
"vite-tsconfig-paths": "^5.1.4"
"vite-tsconfig-paths": "^5.1.4",
"fontmin": "^0.9.9",
"glob": "^10.3.10"
},
"overrides": {
"ahooks": {

View File

@ -0,0 +1,137 @@
/**
* Fontmin Script - 动态裁剪字体
* 扫描 src 目录中所有中文字符生成字体子集
*/
const Fontmin = require('fontmin');
const fs = require('fs');
const path = require('path');
const glob = require('glob');
// 配置
const SOURCE_FONT = path.resolve(__dirname, '../src/assets/fonts/AaCute-full.ttf');
const SOURCE_TTF_ORIGINAL = path.resolve(__dirname, '../src/assets/fonts/AaCute.ttf');
const OUTPUT_DIR = path.resolve(__dirname, '../public/fonts');
const OUTPUT_NAME = 'AaCute.woff';
const SRC_DIR = path.resolve(__dirname, '../src');
// 基础字符集(常用汉字 + 标点 + 数字 + 字母)
const BASE_CHARS = `
0123456789
abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
""''·
,.:;!?'"()[]<>-_+=*/\\|@#$%^&~\`
基础信息系统版本网络配置服务器客户端终端日志调试关于设置主题
登录退出确定取消保存删除编辑新建刷新加载更新下载上传
成功失败错误警告提示信息状态在线离线连接断开
用户名密码账号验证码记住自动
文件管理打开关闭复制粘贴剪切重命名移动
发送消息输入内容搜索查找筛选排序
帮助文档教程反馈问题建议
开启关闭启用禁用显示隐藏展开收起
返回前进上一步下一步完成跳过
今天昨天明天时间日期年月日时分秒
总量使用占用剩余内存内核主频型号
有新版本可用当前最新立即稍后
`;
/**
* 从源码文件中提取所有中文字符
*/
function extractCharsFromSource () {
const chars = new Set(BASE_CHARS.replace(/\s/g, ''));
// 匹配所有 .tsx, .ts, .jsx, .js, .css 文件
const files = glob.sync(`${SRC_DIR}/**/*.{tsx,ts,jsx,js,css}`, {
ignore: ['**/node_modules/**']
});
// 中文字符正则
const chineseRegex = /[\u4e00-\u9fa5]/g;
files.forEach(file => {
try {
const content = fs.readFileSync(file, 'utf-8');
const matches = content.match(chineseRegex);
if (matches) {
matches.forEach(char => chars.add(char));
}
} catch (e) {
console.warn(`Warning: Could not read file ${file}`);
}
});
return Array.from(chars).join('');
}
/**
* 运行 fontmin
*/
async function run () {
console.log('🔍 Scanning source files for Chinese characters...');
const text = extractCharsFromSource();
console.log(`📝 Found ${text.length} unique characters`);
// 检查源字体是否存在
let sourceFont = SOURCE_FONT;
if (!fs.existsSync(SOURCE_FONT)) {
// 尝试查找原始 TTF 并复制(不重命名,保留原始)
if (fs.existsSync(SOURCE_TTF_ORIGINAL)) {
console.log('📦 Copying original font to AaCute-full.ttf...');
fs.copyFileSync(SOURCE_TTF_ORIGINAL, SOURCE_FONT);
} else {
console.error(`❌ Source font not found: ${SOURCE_FONT}`);
console.log('💡 Please ensure AaCute.ttf exists in src/assets/fonts/');
process.exit(1);
}
}
console.log('✂️ Subsetting font...');
// 确保输出目录存在
if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
}
const fontmin = new Fontmin()
.src(sourceFont)
.use(Fontmin.glyph({ text }))
.use(Fontmin.ttf2woff())
.dest(OUTPUT_DIR);
return new Promise((resolve, reject) => {
fontmin.run((err, files) => {
if (err) {
console.error('❌ Fontmin error:', err);
reject(err);
} else {
// 重命名输出文件
const generatedWoff = path.join(OUTPUT_DIR, 'AaCute-full.woff');
const targetFile = path.join(OUTPUT_DIR, OUTPUT_NAME);
if (fs.existsSync(generatedWoff)) {
// 如果目标文件存在,先删除
if (fs.existsSync(targetFile)) {
fs.unlinkSync(targetFile);
}
fs.renameSync(generatedWoff, targetFile);
}
// 清理生成的 TTF 文件
const generatedTtf = path.join(OUTPUT_DIR, 'AaCute-full.ttf');
if (fs.existsSync(generatedTtf)) {
fs.unlinkSync(generatedTtf);
}
if (fs.existsSync(targetFile)) {
const stats = fs.statSync(targetFile);
const sizeKB = (stats.size / 1024).toFixed(2);
console.log(`✅ Font subset created: ${targetFile} (${sizeKB} KB)`);
}
resolve();
}
});
});
}
run().catch(console.error);

View File

@ -40,7 +40,7 @@ const QQInfoCard: React.FC<QQInfoCardProps> = ({ data, error, loading }) => {
: (
<CardBody className='flex-row items-center gap-4 overflow-hidden relative p-4'>
{!hasBackground && (
<div className='absolute right-[-10px] bottom-[-10px] text-7xl text-default-400/10 rotate-12 pointer-events-none'>
<div className='absolute right-[-10px] bottom-[-10px] text-7xl text-default-400/10 rotate-12 pointer-events-none dark:hidden'>
<BsTencentQq />
</div>
)}

View File

@ -97,11 +97,12 @@ const renderItems = (items: MenuItem[], children = false) => {
: (
<div
className={clsx(
'w-3 h-1.5 rounded-full ml-auto shadow-lg',
'w-3 h-1.5 rounded-full ml-auto',
isActive
? 'bg-primary-500 animate-spinner-ease-spin'
: 'bg-primary-200 dark:bg-white'
? 'bg-primary-500 animate-nav-spin'
: 'bg-primary-200 dark:bg-white shadow-lg'
)}
aria-hidden="true"
/>
)
}

View File

@ -1,12 +1,12 @@
import { Card, CardBody, CardHeader } from '@heroui/card';
import { Button } from '@heroui/button';
import { Chip } from '@heroui/chip';
import { Spinner } from '@heroui/spinner';
import { Tooltip } from '@heroui/tooltip';
import { useLocalStorage } from '@uidotdev/usehooks';
import { useRequest } from 'ahooks';
import clsx from 'clsx';
import { FaCircleInfo, FaInfo, FaQq } from 'react-icons/fa6';
import { FaCircleInfo, FaQq } from 'react-icons/fa6';
import { IoLogoChrome, IoLogoOctocat } from 'react-icons/io';
import { RiMacFill } from 'react-icons/ri';
import { useState } from 'react';
@ -363,15 +363,19 @@ const NewVersionTip = (props: NewVersionTipProps) => {
return (
<Tooltip content='有新版本可用'>
<Button
isIconOnly
radius='full'
className='!w-5 !h-5 !min-w-0 text-[10px] shadow-lg shadow-pink-500/40 bg-gradient-to-tr from-[#D33FF0] to-[#FF709F] text-white'
isLoading={updateStatus === 'updating'}
onPress={showUpdateDialog}
>
<FaInfo />
</Button>
<div className="cursor-pointer" onClick={updateStatus === 'updating' ? undefined : showUpdateDialog}>
<Chip
size="sm"
color="danger"
variant="flat"
classNames={{
content: "font-bold text-[10px] px-1",
base: "h-5 min-h-5"
}}
>
{updateStatus === 'updating' ? <Spinner size="sm" color="danger" /> : 'New'}
</Chip>
</div>
</Tooltip>
);
};

View File

@ -42,7 +42,7 @@ const XTerm = forwardRef<XTermRef, XTermProps>((props, ref) => {
const terminal = new Terminal({
allowTransparency: true,
fontFamily:
'"JetBrains Mono", "Aa偷吃可爱长大的", "Noto Serif SC", monospace',
'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", "JetBrains Mono", monospace',
cursorInactiveStyle: 'outline',
drawBoldTextInBrightColors: false,
fontSize: fontSize,

View File

@ -1,33 +1,20 @@
import * as monaco from 'monaco-editor';
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
// Monaco Environment - Only load JSON worker for performance
// Other languages will use basic editor worker (no IntelliSense, but syntax highlighting works)
self.MonacoEnvironment = {
getWorker (_: unknown, label: string) {
if (label === 'json') {
// eslint-disable-next-line new-cap
return new jsonWorker();
}
if (label === 'css' || label === 'scss' || label === 'less') {
// eslint-disable-next-line new-cap
return new cssWorker();
}
if (label === 'html' || label === 'handlebars' || label === 'razor') {
// eslint-disable-next-line new-cap
return new htmlWorker();
}
if (label === 'typescript' || label === 'javascript') {
// eslint-disable-next-line new-cap
return new tsWorker();
}
// For all other languages, use the basic editor worker
// This provides syntax highlighting but no language-specific IntelliSense
// eslint-disable-next-line new-cap
return new editorWorker();
},
};
monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true);
export default monaco;

View File

@ -1,13 +1,16 @@
@font-face {
font-family: 'Aa偷吃可爱长大的';
src: url('/fonts/AaCute.woff') format('woff');
font-display: swap;
}
@font-face {
font-family: 'JetBrains Mono';
src: url('/fonts/JetBrainsMono.ttf') format('truetype');
}
@font-face {
font-family: 'JetBrains Mono';
src: url('/fonts/JetBrainsMono-Italic.ttf') format('truetype');
font-style: italic;
}
}

View File

@ -6,6 +6,7 @@
body {
font-family:
'Aa偷吃可爱长大的',
'Quicksand',
'Nunito',
'Inter',
@ -18,27 +19,38 @@ body {
'PingFang SC',
'Microsoft YaHei',
sans-serif !important;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
font-smooth: always;
letter-spacing: 0.02em;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
font-smooth: always;
letter-spacing: 0.02em;
}
:root {
--heroui-primary: 217.2 91.2% 59.8%; /* 自然的现代蓝 */
--heroui-primary: 217.2 91.2% 59.8%;
/* 自然的现代蓝 */
--heroui-primary-foreground: 210 40% 98%;
--heroui-radius: 0.75rem;
--text-primary: 222.2 47.4% 11.2%;
--text-secondary: 215.4 16.3% 46.9%;
}
h1, h2, h3, h4, h5, h6 {
h1,
h2,
h3,
h4,
h5,
h6 {
color: hsl(var(--text-primary));
letter-spacing: -0.02em;
}
.dark h1, .dark h2, .dark h3, .dark h4, .dark h5, .dark h6 {
.dark h1,
.dark h2,
.dark h3,
.dark h4,
.dark h5,
.dark h6 {
color: hsl(210 40% 98%);
}
@ -52,11 +64,13 @@ h1, h2, h3, h4, h5, h6 {
width: 0 !important;
height: 0 !important;
}
.hide-scrollbar::-webkit-scrollbar-thumb {
width: 0 !important;
height: 0 !important;
background-color: transparent !important;
}
.hide-scrollbar::-webkit-scrollbar-track {
width: 0 !important;
height: 0 !important;
@ -80,7 +94,8 @@ h1, h2, h3, h4, h5, h6 {
}
::-webkit-scrollbar-thumb {
background-color: rgba(255, 182, 193, 0.4); /* 浅粉色滚动条 */
background-color: rgba(255, 182, 193, 0.4);
/* 浅粉色滚动条 */
border-radius: 3px;
transition: all 0.3s;
}
@ -123,16 +138,36 @@ h1, h2, h3, h4, h5, h6 {
.context-view.monaco-menu-container * {
font-family:
PingFang SC,
'Aa偷吃可爱长大的',
Helvetica Neue,
Microsoft YaHei,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
'PingFang SC',
'Microsoft YaHei',
sans-serif !important;
}
.ql-hidden {
@apply hidden;
}
.ql-editor img {
@apply inline-block;
}
/* GPU-accelerated navigation indicator animation */
@keyframes nav-indicator-spin {
0% {
transform: rotate(0deg) translateZ(0);
}
100% {
transform: rotate(360deg) translateZ(0);
}
}
.animate-nav-spin {
animation: nav-indicator-spin 2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
will-change: transform;
backface-visibility: hidden;
perspective: 1000px;
}

View File

@ -16,7 +16,20 @@ export default {
},
],
theme: {
extend: {},
extend: {
fontFamily: {
mono: [
'ui-monospace',
'SFMono-Regular',
'SF Mono',
'Menlo',
'Consolas',
'Liberation Mono',
'JetBrains Mono',
'monospace',
],
},
},
},
darkMode: 'class',
plugins: [

View File

@ -52,7 +52,6 @@ export default defineConfig(({ mode }) => {
'react-dom': ['react-dom'],
'react-router-dom': ['react-router-dom'],
'react-hook-form': ['react-hook-form'],
'react-icons': ['react-icons'],
'react-hot-toast': ['react-hot-toast'],
qface: ['qface'],
},

File diff suppressed because it is too large Load Diff