mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-12-26 19:01:28 +08:00
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:
parent
fa3a229827
commit
50bcd71144
@ -29,6 +29,7 @@
|
||||
"dependencies": {
|
||||
"express": "^5.0.0",
|
||||
"silk-wasm": "^3.6.1",
|
||||
"vite-plugin-font": "^5.1.2",
|
||||
"ws": "^8.18.3"
|
||||
}
|
||||
}
|
||||
@ -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": {
|
||||
|
||||
Binary file not shown.
137
packages/napcat-webui-frontend/scripts/fontmin.cjs
Normal file
137
packages/napcat-webui-frontend/scripts/fontmin.cjs
Normal 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);
|
||||
BIN
packages/napcat-webui-frontend/src/assets/fonts/AaCute-full.ttf
Normal file
BIN
packages/napcat-webui-frontend/src/assets/fonts/AaCute-full.ttf
Normal file
Binary file not shown.
BIN
packages/napcat-webui-frontend/src/assets/fonts/AaCute.ttf
Normal file
BIN
packages/napcat-webui-frontend/src/assets/fonts/AaCute.ttf
Normal file
Binary file not shown.
BIN
packages/napcat-webui-frontend/src/assets/fonts/AaCute.woff
Normal file
BIN
packages/napcat-webui-frontend/src/assets/fonts/AaCute.woff
Normal file
Binary file not shown.
@ -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>
|
||||
)}
|
||||
|
||||
@ -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"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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: [
|
||||
|
||||
@ -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'],
|
||||
},
|
||||
|
||||
2020
pnpm-lock.yaml
2020
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user