feat: integrate HeroUI and Tailwind CSS for enhanced styling (#9973)

This commit is contained in:
MyPrototypeWhat
2025-09-07 16:49:30 +08:00
committed by GitHub
parent 0187f1780e
commit 4b65dfa6ea
50 changed files with 5043 additions and 1622 deletions

View File

@@ -1,7 +1,7 @@
name: Auto I18N
env:
API_KEY: ${{ secrets.TRANSLATE_API_KEY}}
API_KEY: ${{ secrets.TRANSLATE_API_KEY }}
MODEL: ${{ vars.MODEL || 'deepseek/deepseek-v3.1'}}
BASE_URL: ${{ vars.BASE_URL || 'https://api.ppinfra.com/openai'}}
@@ -35,7 +35,7 @@ jobs:
# 在临时目录安装依赖
mkdir -p /tmp/translation-deps
cd /tmp/translation-deps
echo '{"dependencies": {"openai": "^5.12.2", "cli-progress": "^3.12.0", "tsx": "^4.20.3", "prettier": "^3.5.3", "prettier-plugin-sort-json": "^4.1.1"}}' > package.json
echo '{"dependencies": {"openai": "^5.12.2", "cli-progress": "^3.12.0", "tsx": "^4.20.3", "prettier": "^3.5.3", "prettier-plugin-sort-json": "^4.1.1", "prettier-plugin-tailwindcss": "^0.6.14"}}' > package.json
npm install --no-package-lock
# 设置 NODE_PATH 让项目能找到这些依赖

View File

@@ -3,9 +3,11 @@
"endOfLine": "lf",
"jsonRecursiveSort": true,
"jsonSortOrder": "{\"*\": \"lexical\"}",
"plugins": ["prettier-plugin-sort-json"],
"plugins": ["prettier-plugin-sort-json", "prettier-plugin-tailwindcss"],
"printWidth": 120,
"semi": false,
"singleQuote": true,
"tailwindFunctions": ["clsx"],
"tailwindStylesheet": "./src/renderer/src/assets/styles/tailwind.css",
"trailingComma": "none"
}

21
components.json Normal file
View File

@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"aliases": {
"components": "@renderer/ui/third-party",
"hooks": "@renderer/hooks",
"lib": "@renderer/lib",
"ui": "@renderer/ui",
"utils": "@renderer/utils"
},
"iconLibrary": "lucide",
"rsc": false,
"style": "new-york",
"tailwind": {
"baseColor": "zinc",
"config": "",
"css": "src/renderer/src/assets/styles/tailwind.css",
"cssVariables": true,
"prefix": ""
},
"tsx": true
}

View File

@@ -60,6 +60,7 @@ export default defineConfig({
},
renderer: {
plugins: [
(async () => (await import('@tailwindcss/vite')).default())(),
react({
tsDecorators: true,
plugins: [

View File

@@ -123,7 +123,9 @@ export default defineConfig([
'.gitignore',
'scripts/cloudflare-worker.js',
'src/main/integration/nutstore/sso/lib/**',
'src/main/integration/cherryin/index.js'
'src/main/integration/cherryin/index.js',
'src/main/integration/nutstore/sso/lib/**',
'src/renderer/src/ui/**'
]
}
])

View File

@@ -128,6 +128,7 @@
"@eslint/js": "^9.22.0",
"@google/genai": "patch:@google/genai@npm%3A1.0.1#~/.yarn/patches/@google-genai-npm-1.0.1-e26f0f9af7.patch",
"@hello-pangea/dnd": "^18.0.1",
"@heroui/react": "^2.8.3",
"@kangfenmao/keyv-storage": "^0.1.0",
"@langchain/community": "^0.3.50",
"@langchain/core": "^0.3.68",
@@ -148,6 +149,7 @@
"@reduxjs/toolkit": "^2.2.5",
"@shikijs/markdown-it": "^3.12.0",
"@swc/plugin-styled-components": "^8.0.4",
"@tailwindcss/vite": "^4.1.13",
"@tanstack/react-query": "^5.85.5",
"@tanstack/react-virtual": "^3.13.12",
"@testing-library/dom": "^10.4.0",
@@ -210,6 +212,7 @@
"cheerio": "^1.1.2",
"chokidar": "^4.0.3",
"cli-progress": "^3.12.0",
"clsx": "^2.1.1",
"code-inspector-plugin": "^0.20.14",
"color": "^5.0.0",
"concurrently": "^9.2.1",
@@ -238,6 +241,7 @@
"fast-diff": "^1.3.0",
"fast-xml-parser": "^5.2.0",
"fetch-socks": "1.3.2",
"framer-motion": "^12.23.12",
"franc-min": "^6.2.0",
"fs-extra": "^11.2.0",
"google-auth-library": "^9.15.1",
@@ -272,6 +276,7 @@
"playwright": "^1.52.0",
"prettier": "^3.5.3",
"prettier-plugin-sort-json": "^4.1.1",
"prettier-plugin-tailwindcss": "^0.6.14",
"proxy-agent": "^6.5.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
@@ -301,17 +306,18 @@
"remark-math": "^6.0.0",
"remove-markdown": "^0.6.2",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.88.0",
"shiki": "^3.12.0",
"strict-url-sanitise": "^0.0.1",
"string-width": "^7.2.0",
"striptags": "^3.2.0",
"styled-components": "^6.1.11",
"tailwindcss": "^4.1.13",
"tar": "^7.4.3",
"tiny-pinyin": "^1.3.2",
"tokenx": "^1.1.0",
"tsx": "^4.20.3",
"turndown-plugin-gfm": "^1.0.2",
"tw-animate-css": "^1.3.8",
"typescript": "^5.6.2",
"undici": "6.21.2",
"unified": "^11.0.5",
@@ -356,7 +362,7 @@
"prettier --write",
"eslint --fix"
],
"*.{json,yml,yaml,css,scss,html}": [
"*.{json,yml,yaml,css,html}": [
"prettier --write"
]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@cherrystudio/ai-core",
"version": "1.0.0-alpha.11",
"version": "1.0.0-alpha.12",
"description": "Cherry Studio AI Core - Unified AI Provider Interface Based on Vercel AI SDK",
"main": "dist/index.js",
"module": "dist/index.mjs",

View File

@@ -8,18 +8,18 @@
</head>
<body class="bg-gray-50">
<div class="max-w-4xl mx-auto px-4 py-8">
<div class="mx-auto max-w-4xl px-4 py-8">
<!-- 中文版本 -->
<div class="mb-12">
<h1 class="text-3xl font-bold mb-8 text-gray-900">许可协议</h1>
<h1 class="mb-8 text-3xl font-bold text-gray-900">许可协议</h1>
<p class="mb-6 text-gray-700">
本项目采用<strong>区分用户的双重许可 (User-Segmented Dual Licensing)</strong> 模式。
</p>
<section class="mb-8">
<h2 class="text-xl font-semibold mb-4 text-gray-900">核心原则</h2>
<ul class="list-disc pl-6 space-y-2 text-gray-700">
<h2 class="mb-4 text-xl font-semibold text-gray-900">核心原则</h2>
<ul class="list-disc space-y-2 pl-6 text-gray-700">
<li>
<strong>个人用户 和 10人及以下企业/组织:</strong> 默认适用
<strong>GNU Affero 通用公共许可证 v3.0 (AGPLv3)</strong>
@@ -32,7 +32,7 @@
</section>
<section class="mb-8">
<h2 class="text-xl font-semibold mb-4 text-gray-900">定义:"10人及以下"</h2>
<h2 class="mb-4 text-xl font-semibold text-gray-900">定义:"10人及以下"</h2>
<p class="text-gray-700">
指在您的组织包括公司、非营利组织、政府机构、教育机构等任何实体能够访问、使用或以任何方式直接或间接受益于本软件Cherry
Studio功能的个人总数不超过10人。这包括但不限于开发者、测试人员、运营人员、最终用户、通过集成系统间接使用者等。
@@ -40,10 +40,10 @@
</section>
<section class="mb-8">
<h2 class="text-xl font-semibold mb-4 text-gray-900">
<h2 class="mb-4 text-xl font-semibold text-gray-900">
1. 开源许可证 (Open Source License): AGPLv3 - 适用于个人及10人及以下组织
</h2>
<ul class="list-disc pl-6 space-y-2 text-gray-700">
<ul class="list-disc space-y-2 pl-6 text-gray-700">
<li>
如果您是个人用户,或者您的组织满足上述"10人及以下"的定义,您可以在
<strong>AGPLv3</strong> 的条款下自由使用、修改和分发 Cherry Studio。AGPLv3 的完整文本可以访问
@@ -62,10 +62,10 @@
</section>
<section class="mb-8">
<h2 class="text-xl font-semibold mb-4 text-gray-900">
<h2 class="mb-4 text-xl font-semibold text-gray-900">
2. 商业许可证 (Commercial License) - 适用于超过10人的组织或希望规避 AGPLv3 义务的用户
</h2>
<ul class="list-disc pl-6 space-y-2 text-gray-700">
<ul class="list-disc space-y-2 pl-6 text-gray-700">
<li>
<strong>强制要求:</strong>
如果您的组织<strong></strong>满足上述"10人及以下"的定义即有11人或更多人可以访问、使用或受益于本软件<strong>必须</strong>联系我们获取并签署一份商业许可证才能使用
@@ -80,7 +80,7 @@
</li>
<li>
<strong>需要商业许可证的常见情况包括(但不限于):</strong>
<ul class="list-disc pl-6 mt-2 space-y-1">
<ul class="mt-2 list-disc space-y-1 pl-6">
<li>您的组织规模超过10人。</li>
<li>
(无论组织规模)您希望分发修改过的 Cherry Studio 版本,但<strong>不希望</strong>根据 AGPLv3
@@ -104,8 +104,8 @@
</section>
<section class="mb-8">
<h2 class="text-xl font-semibold mb-4 text-gray-900">3. 贡献 (Contributions)</h2>
<ul class="list-disc pl-6 space-y-2 text-gray-700">
<h2 class="mb-4 text-xl font-semibold text-gray-900">3. 贡献 (Contributions)</h2>
<ul class="list-disc space-y-2 pl-6 text-gray-700">
<li>
我们欢迎社区对 Cherry Studio 的贡献。所有向本项目提交的贡献都将被视为在
<strong>AGPLv3</strong> 许可证下提供。
@@ -119,8 +119,8 @@
</section>
<section class="mb-8">
<h2 class="text-xl font-semibold mb-4 text-gray-900">4. 其他条款 (Other Terms)</h2>
<ul class="list-disc pl-6 space-y-2 text-gray-700">
<h2 class="mb-4 text-xl font-semibold text-gray-900">4. 其他条款 (Other Terms)</h2>
<ul class="list-disc space-y-2 pl-6 text-gray-700">
<li>关于商业许可证的具体条款和条件,以双方签署的正式商业许可协议为准。</li>
<li>
项目维护者保留根据需要更新本许可政策(包括用户规模定义和阈值)的权利。相关更新将通过项目官方渠道(如代码仓库、官方网站)进行通知。
@@ -133,13 +133,13 @@
<!-- English Version -->
<div>
<h1 class="text-3xl font-bold mb-8 text-gray-900">Licensing</h1>
<h1 class="mb-8 text-3xl font-bold text-gray-900">Licensing</h1>
<p class="mb-6 text-gray-700">This project employs a <strong>User-Segmented Dual Licensing</strong> model.</p>
<section class="mb-8">
<h2 class="text-xl font-semibold mb-4 text-gray-900">Core Principle</h2>
<ul class="list-disc pl-6 space-y-2 text-gray-700">
<h2 class="mb-4 text-xl font-semibold text-gray-900">Core Principle</h2>
<ul class="list-disc space-y-2 pl-6 text-gray-700">
<li>
<strong>Individual Users and Organizations with 10 or Fewer Individuals:</strong> Governed by default
under the <strong>GNU Affero General Public License v3.0 (AGPLv3)</strong>.
@@ -152,7 +152,7 @@
</section>
<section class="mb-8">
<h2 class="text-xl font-semibold mb-4 text-gray-900">Definition: "10 or Fewer Individuals"</h2>
<h2 class="mb-4 text-xl font-semibold text-gray-900">Definition: "10 or Fewer Individuals"</h2>
<p class="text-gray-700">
Refers to any organization (including companies, non-profits, government agencies, educational institutions,
etc.) where the total number of individuals who can access, use, or in any way directly or indirectly
@@ -162,10 +162,10 @@
</section>
<section class="mb-8">
<h2 class="text-xl font-semibold mb-4 text-gray-900">
<h2 class="mb-4 text-xl font-semibold text-gray-900">
1. Open Source License: AGPLv3 - For Individuals and Organizations of 10 or Fewer
</h2>
<ul class="list-disc pl-6 space-y-2 text-gray-700">
<ul class="list-disc space-y-2 pl-6 text-gray-700">
<li>
If you are an individual user, or if your organization meets the "10 or Fewer Individuals" definition
above, you are free to use, modify, and distribute Cherry Studio under the terms of the
@@ -186,11 +186,11 @@
</section>
<section class="mb-8">
<h2 class="text-xl font-semibold mb-4 text-gray-900">
<h2 class="mb-4 text-xl font-semibold text-gray-900">
2. Commercial License - For Organizations with More Than 10 Individuals, or Users Needing to Avoid AGPLv3
Obligations
</h2>
<ul class="list-disc pl-6 space-y-2 text-gray-700">
<ul class="list-disc space-y-2 pl-6 text-gray-700">
<li>
<strong>Mandatory Requirement:</strong> If your organization does <strong>not</strong> meet the "10 or
Fewer Individuals" definition above (i.e., 11 or more individuals can access, use, or benefit from the
@@ -207,7 +207,7 @@
</li>
<li>
<strong>Common scenarios requiring a Commercial License include (but are not limited to):</strong>
<ul class="list-disc pl-6 mt-2 space-y-1">
<ul class="mt-2 list-disc space-y-1 pl-6">
<li>
Your organization has more than 10 individuals who can access, use, or benefit from the software.
</li>
@@ -236,8 +236,8 @@
</section>
<section class="mb-8">
<h2 class="text-xl font-semibold mb-4 text-gray-900">3. Contributions</h2>
<ul class="list-disc pl-6 space-y-2 text-gray-700">
<h2 class="mb-4 text-xl font-semibold text-gray-900">3. Contributions</h2>
<ul class="list-disc space-y-2 pl-6 text-gray-700">
<li>
We welcome community contributions to Cherry Studio. All contributions submitted to this project are
considered to be offered under the <strong>AGPLv3</strong> license.
@@ -255,8 +255,8 @@
</section>
<section class="mb-8">
<h2 class="text-xl font-semibold mb-4 text-gray-900">4. Other Terms</h2>
<ul class="list-disc pl-6 space-y-2 text-gray-700">
<h2 class="mb-4 text-xl font-semibold text-gray-900">4. Other Terms</h2>
<ul class="list-disc space-y-2 pl-6 text-gray-700">
<li>
The specific terms and conditions of the Commercial License are governed by the formal commercial license
agreement signed by both parties.

View File

@@ -12,18 +12,18 @@
<body id="app">
<div :class="isDark ? 'dark-bg' : 'bg'" class="min-h-screen">
<div class="max-w-3xl mx-auto py-12 px-4">
<h1 class="text-3xl font-bold mb-8" :class="isDark ? 'text-white' : 'text-gray-900'">Release Timeline</h1>
<div class="mx-auto max-w-3xl px-4 py-12">
<h1 class="mb-8 text-3xl font-bold" :class="isDark ? 'text-white' : 'text-gray-900'">Release Timeline</h1>
<!-- Loading状态 -->
<div v-if="loading" class="text-center py-8">
<div v-if="loading" class="py-8 text-center">
<div
class="inline-block animate-spin rounded-full h-8 w-8 border-4"
class="inline-block h-8 w-8 animate-spin rounded-full border-4"
:class="isDark ? 'border-gray-700 border-t-blue-500' : 'border-gray-300 border-t-blue-500'"></div>
</div>
<!-- Error 状态 -->
<div v-else-if="error" class="text-red-500 text-center py-8">{{ error }}</div>
<div v-else-if="error" class="py-8 text-center text-red-500">{{ error }}</div>
<!-- Release 列表 -->
<div v-else class="space-y-8">
@@ -32,21 +32,21 @@
:key="release.id"
class="relative pl-8"
:class="isDark ? 'border-l-2 border-gray-700' : 'border-l-2 border-gray-200'">
<div class="absolute -left-2 top-0 w-4 h-4 rounded-full bg-green-500"></div>
<div class="absolute top-0 -left-2 h-4 w-4 rounded-full bg-green-500"></div>
<div
class="rounded-lg shadow-sm p-6 transition-shadow"
class="rounded-lg p-6 shadow-sm transition-shadow"
:class="isDark ? 'bg-black hover:shadow-md hover:shadow-black' : 'bg-white hover:shadow-md'">
<div class="flex items-start justify-between mb-4">
<div class="mb-4 flex items-start justify-between">
<div>
<h2 class="text-xl font-semibold" :class="isDark ? 'text-white' : 'text-gray-900'">
{{ release.name || release.tag_name }}
</h2>
<p class="text-sm mt-1" :class="isDark ? 'text-gray-400' : 'text-gray-500'">
<p class="mt-1 text-sm" :class="isDark ? 'text-gray-400' : 'text-gray-500'">
{{ formatDate(release.published_at) }}
</p>
</div>
<span
class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium"
class="inline-flex items-center rounded-full px-3 py-1 text-sm font-medium"
:class="isDark ? 'bg-green-900 text-green-200' : 'bg-green-100 text-green-800'">
{{ release.tag_name }}
</span>

View File

@@ -1,6 +1,6 @@
import { exec } from 'child_process'
import * as fs from 'fs/promises'
import linguistLanguages from 'linguist-languages'
import * as linguistLanguages from 'linguist-languages'
import * as path from 'path'
import { promisify } from 'util'

View File

@@ -1,11 +1,13 @@
import '@renderer/databases'
import { HeroUIProvider } from '@heroui/react'
import { loggerService } from '@logger'
import store, { persistor } from '@renderer/store'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist/integration/react'
import { ToastPortal } from './components/ToastPortal'
import TopViewContainer from './components/TopView'
import AntdProvider from './context/AntdProvider'
import { CodeStyleProvider } from './context/CodeStyleProvider'
@@ -32,21 +34,24 @@ function App(): React.ReactElement {
return (
<Provider store={store}>
<QueryClientProvider client={queryClient}>
<StyleSheetManager>
<ThemeProvider>
<AntdProvider>
<NotificationProvider>
<CodeStyleProvider>
<PersistGate loading={null} persistor={persistor}>
<TopViewContainer>
<Router />
</TopViewContainer>
</PersistGate>
</CodeStyleProvider>
</NotificationProvider>
</AntdProvider>
</ThemeProvider>
</StyleSheetManager>
<HeroUIProvider className="flex flex-1">
<StyleSheetManager>
<ThemeProvider>
<AntdProvider>
<NotificationProvider>
<CodeStyleProvider>
<PersistGate loading={null} persistor={persistor}>
<TopViewContainer>
<Router />
</TopViewContainer>
</PersistGate>
</CodeStyleProvider>
</NotificationProvider>
</AntdProvider>
</ThemeProvider>
</StyleSheetManager>
<ToastPortal />
</HeroUIProvider>
</QueryClientProvider>
</Provider>
)

View File

@@ -0,0 +1,60 @@
.command-list-popover {
/* Base styles are handled inline for theme support */
/* Arrow styles based on placement */
}
.command-list-popover[data-placement^='bottom'] {
transform-origin: top center;
animation: slideDownAndFadeIn 0.15s ease-out;
}
.command-list-popover[data-placement^='top'] {
transform-origin: bottom center;
animation: slideUpAndFadeIn 0.15s ease-out;
}
.command-list-popover[data-placement*='start'] {
transform-origin: left center;
}
.command-list-popover[data-placement*='end'] {
transform-origin: right center;
}
@keyframes slideDownAndFadeIn {
0% {
opacity: 0;
transform: translateY(-8px) scale(0.95);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
@keyframes slideUpAndFadeIn {
0% {
opacity: 0;
transform: translateY(8px) scale(0.95);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
/* Ensure smooth scrolling in virtual list */
.command-list-popover .dynamic-virtual-list {
scroll-behavior: smooth;
}
/* Better focus indicators */
.command-list-popover [data-index] {
position: relative;
}
.command-list-popover [data-index]:focus-visible {
outline: 2px solid var(--color-primary, #1677ff);
outline-offset: -2px;
}

View File

@@ -1,59 +0,0 @@
.command-list-popover {
// Base styles are handled inline for theme support
// Arrow styles based on placement
&[data-placement^='bottom'] {
transform-origin: top center;
animation: slideDownAndFadeIn 0.15s ease-out;
}
&[data-placement^='top'] {
transform-origin: bottom center;
animation: slideUpAndFadeIn 0.15s ease-out;
}
&[data-placement*='start'] {
transform-origin: left center;
}
&[data-placement*='end'] {
transform-origin: right center;
}
}
@keyframes slideDownAndFadeIn {
0% {
opacity: 0;
transform: translateY(-8px) scale(0.95);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
@keyframes slideUpAndFadeIn {
0% {
opacity: 0;
transform: translateY(8px) scale(0.95);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
// Ensure smooth scrolling in virtual list
.command-list-popover .dynamic-virtual-list {
scroll-behavior: smooth;
}
// Better focus indicators
.command-list-popover [data-index] {
position: relative;
&:focus-visible {
outline: 2px solid var(--color-primary, #1677ff);
outline-offset: -2px;
}
}

View File

@@ -10,14 +10,14 @@
}
}
// 电磁波扩散效果
/* 电磁波扩散效果 */
.animation-pulse {
--pulse-color: 59, 130, 246;
--pulse-size: 8px;
animation: animation-pulse 1.5s infinite;
}
// Modal动画
/* Modal动画 */
@keyframes animation-move-down-in {
0% {
transform: translate3d(0, 100%, 0);
@@ -54,7 +54,7 @@
animation-duration: 0.25s;
}
// 旋转动画
/* 旋转动画 */
@keyframes animation-rotate {
from {
transform: rotate(0deg);
@@ -69,7 +69,7 @@
animation: animation-rotate 0.75s linear infinite;
}
// 定位高亮动画
/* 定位高亮动画 */
@keyframes animation-locate-highlight {
0% {
background-color: transparent;

View File

@@ -0,0 +1,238 @@
@import './container.css';
/* Modal 关闭按钮不应该可拖拽,以确保点击正常 */
.ant-modal-close {
-webkit-app-region: no-drag;
}
/* 普通 Drawer 内容不应该可拖拽 */
.ant-drawer-content {
-webkit-app-region: no-drag;
}
/* minapp-drawer 有自己的拖拽规则 */
/* 下拉菜单和弹出框内容不应该可拖拽 */
.ant-dropdown,
.ant-dropdown-menu,
.ant-popover-content,
.ant-tooltip-content,
.ant-popconfirm {
-webkit-app-region: no-drag;
}
#inputbar {
resize: none;
}
.ant-image-preview-switch-left {
-webkit-app-region: no-drag;
}
.ant-btn:not(:disabled):focus-visible {
outline: none;
}
/* Align lucide icon in Button */
.ant-btn .ant-btn-icon {
display: inline-flex;
align-items: center;
justify-content: center;
}
.ant-tabs-tabpane:focus-visible {
outline: none;
}
.ant-tabs-tab-btn {
outline: none !important;
}
.ant-segmented-group {
gap: 4px;
}
.minapp-drawer .ant-drawer-content-wrapper {
box-shadow: none;
}
.minapp-drawer .ant-drawer-header {
position: absolute;
-webkit-app-region: drag;
min-height: calc(var(--navbar-height) + 0.5px);
margin-top: -0.5px;
border-bottom: none;
}
.minapp-drawer .ant-drawer-body {
padding: 0;
margin-top: var(--navbar-height);
overflow: hidden;
/* 手动展开 @extend #content-container 的内容 */
background-color: var(--color-background);
}
.minapp-drawer .minapp-mask {
background-color: transparent !important;
}
[navbar-position='left'] .minapp-drawer {
max-width: calc(100vw - var(--sidebar-width));
}
[navbar-position='left'] .minapp-drawer .ant-drawer-header {
width: calc(100vw - var(--sidebar-width));
}
[navbar-position='top'] .minapp-drawer {
max-width: 100vw;
}
[navbar-position='top'] .minapp-drawer .ant-drawer-header {
width: 100vw;
}
.ant-drawer-header {
/* 普通 drawer header 不应该可拖拽,除非被 minapp-drawer 覆盖 */
-webkit-app-region: no-drag;
}
.message-attachments .ant-upload-list-item:hover {
background-color: initial !important;
}
.ant-dropdown-menu .ant-dropdown-menu-sub {
max-height: 80vh;
width: max-content;
overflow-y: auto;
overflow-x: hidden;
border: 0.5px solid var(--color-border);
}
.ant-dropdown {
background-color: var(--ant-color-bg-elevated);
overflow: hidden;
border-radius: var(--ant-border-radius-lg);
user-select: none;
}
.ant-dropdown .ant-dropdown-menu {
max-height: 80vh;
overflow-y: auto;
border: 0.5px solid var(--color-border);
}
/* Align lucide icon in dropdown menu item extra */
.ant-dropdown .ant-dropdown-menu .ant-dropdown-menu-submenu-expand-icon,
.ant-dropdown .ant-dropdown-menu .ant-dropdown-menu-item-extra {
display: inline-flex;
align-items: center;
justify-content: center;
}
.ant-dropdown .ant-dropdown-arrow + .ant-dropdown-menu {
border: none;
}
.ant-select-dropdown {
border: 0.5px solid var(--color-border);
}
.ant-dropdown-menu-submenu {
background-color: var(--ant-color-bg-elevated);
overflow: hidden;
border-radius: var(--ant-border-radius-lg);
}
.ant-dropdown-menu-submenu .ant-dropdown-menu-submenu-title {
align-items: center;
}
.ant-popover .ant-popover-inner {
border: 0.5px solid var(--color-border);
}
.ant-popover .ant-popover-inner .ant-popover-inner-content {
max-height: 70vh;
overflow-y: auto;
}
.ant-popover .ant-popover-arrow + .ant-popover-content .ant-popover-inner {
border: none;
}
.ant-modal:not(.ant-modal-confirm) .ant-modal-confirm-body-has-title {
padding: 16px 0 0 0;
}
.ant-modal:not(.ant-modal-confirm) .ant-modal-content {
border-radius: 10px;
border: 0.5px solid var(--color-border);
padding: 0 0 8px 0;
}
.ant-modal:not(.ant-modal-confirm) .ant-modal-content .ant-modal-close {
margin-right: 2px;
}
.ant-modal:not(.ant-modal-confirm) .ant-modal-content .ant-modal-header {
padding: 16px 16px 0 16px;
border-radius: 10px;
}
.ant-modal:not(.ant-modal-confirm) .ant-modal-content .ant-modal-body {
/* 保持 body 在视口内,使用标准的最大高度 */
max-height: 80vh;
overflow-y: auto;
padding: 0 16px 0 16px;
}
.ant-modal:not(.ant-modal-confirm) .ant-modal-content .ant-modal-footer {
padding: 0 16px 8px 16px;
}
.ant-modal:not(.ant-modal-confirm) .ant-modal-content .ant-modal-confirm-btns {
margin-bottom: 8px;
}
.ant-modal.ant-modal-confirm.ant-modal-confirm-confirm .ant-modal-content {
padding: 16px;
}
.ant-collapse:not(.ant-collapse-ghost) {
border: 1px solid var(--color-border);
}
.ant-color-picker .ant-collapse:not(.ant-collapse-ghost) {
border: none;
}
.ant-collapse:not(.ant-collapse-ghost) .ant-collapse-content {
border-top: 0.5px solid var(--color-border) !important;
}
.ant-color-picker .ant-collapse:not(.ant-collapse-ghost) .ant-collapse-content {
border-top: none !important;
}
.ant-slider .ant-slider-handle::after {
box-shadow: 0 1px 4px 0px rgb(128 128 128 / 50%) !important;
}
.ant-splitter-bar .ant-splitter-bar-dragger::before {
background-color: var(--color-border) !important;
transition:
background-color 0.15s ease,
width 0.15s ease;
}
.ant-splitter-bar .ant-splitter-bar-dragger:hover::before {
width: 4px !important;
background-color: var(--color-primary) !important;
transition-delay: 0.15s;
}
.ant-splitter-bar .ant-splitter-bar-dragger-active::before {
width: 4px !important;
background-color: var(--color-primary) !important;
}

View File

@@ -1,234 +0,0 @@
@use './container.scss';
/* Modal 关闭按钮不应该可拖拽,以确保点击正常 */
.ant-modal-close {
-webkit-app-region: no-drag;
}
/* 普通 Drawer 内容不应该可拖拽 */
.ant-drawer-content {
-webkit-app-region: no-drag;
}
/* minapp-drawer 有自己的拖拽规则 */
/* 下拉菜单和弹出框内容不应该可拖拽 */
.ant-dropdown,
.ant-dropdown-menu,
.ant-popover-content,
.ant-tooltip-content,
.ant-popconfirm {
-webkit-app-region: no-drag;
}
#inputbar {
resize: none;
}
.ant-image-preview-switch-left {
-webkit-app-region: no-drag;
}
.ant-btn:not(:disabled):focus-visible {
outline: none;
}
// Align lucide icon in Button
.ant-btn .ant-btn-icon {
display: inline-flex;
align-items: center;
justify-content: center;
}
.ant-tabs-tabpane:focus-visible {
outline: none;
}
.ant-tabs-tab-btn {
outline: none !important;
}
.ant-segmented-group {
gap: 4px;
}
.minapp-drawer {
[navbar-position='left'] & {
max-width: calc(100vw - var(--sidebar-width));
.ant-drawer-header {
width: calc(100vw - var(--sidebar-width));
}
}
[navbar-position='top'] & {
max-width: 100vw;
.ant-drawer-header {
width: 100vw;
}
}
.ant-drawer-content-wrapper {
box-shadow: none;
}
.ant-drawer-header {
position: absolute;
-webkit-app-region: drag;
min-height: calc(var(--navbar-height) + 0.5px);
margin-top: -0.5px;
border-bottom: none;
}
.ant-drawer-body {
padding: 0;
margin-top: var(--navbar-height);
overflow: hidden;
@extend #content-container;
}
.minapp-mask {
background-color: transparent !important;
}
}
.ant-drawer-header {
/* 普通 drawer header 不应该可拖拽,除非被 minapp-drawer 覆盖 */
-webkit-app-region: no-drag;
}
.message-attachments {
.ant-upload-list-item:hover {
background-color: initial !important;
}
}
.ant-dropdown-menu .ant-dropdown-menu-sub {
max-height: 80vh;
width: max-content;
overflow-y: auto;
overflow-x: hidden;
border: 0.5px solid var(--color-border);
}
.ant-dropdown {
background-color: var(--ant-color-bg-elevated);
overflow: hidden;
border-radius: var(--ant-border-radius-lg);
user-select: none;
.ant-dropdown-menu {
max-height: 80vh;
overflow-y: auto;
border: 0.5px solid var(--color-border);
// Align lucide icon in dropdown menu item extra
.ant-dropdown-menu-submenu-expand-icon,
.ant-dropdown-menu-item-extra {
display: inline-flex;
align-items: center;
justify-content: center;
}
}
.ant-dropdown-arrow + .ant-dropdown-menu {
border: none;
}
}
.ant-select-dropdown {
border: 0.5px solid var(--color-border);
}
.ant-dropdown-menu-submenu {
background-color: var(--ant-color-bg-elevated);
overflow: hidden;
border-radius: var(--ant-border-radius-lg);
.ant-dropdown-menu-submenu-title {
align-items: center;
}
}
.ant-popover {
.ant-popover-inner {
border: 0.5px solid var(--color-border);
.ant-popover-inner-content {
max-height: 70vh;
overflow-y: auto;
}
}
.ant-popover-arrow + .ant-popover-content {
.ant-popover-inner {
border: none;
}
}
}
.ant-modal:not(.ant-modal-confirm) {
.ant-modal-confirm-body-has-title {
padding: 16px 0 0 0;
}
.ant-modal-content {
border-radius: 10px;
border: 0.5px solid var(--color-border);
padding: 0 0 8px 0;
.ant-modal-close {
margin-right: 2px;
}
.ant-modal-header {
padding: 16px 16px 0 16px;
border-radius: 10px;
}
.ant-modal-body {
/* 保持 body 在视口内,使用标准的最大高度 */
max-height: 80vh;
overflow-y: auto;
padding: 0 16px 0 16px;
}
.ant-modal-footer {
padding: 0 16px 8px 16px;
}
.ant-modal-confirm-btns {
margin-bottom: 8px;
}
}
}
.ant-modal.ant-modal-confirm.ant-modal-confirm-confirm {
.ant-modal-content {
padding: 16px;
}
}
.ant-collapse:not(.ant-collapse-ghost) {
border: 1px solid var(--color-border);
.ant-color-picker & {
border: none;
}
.ant-collapse-content {
border-top: 0.5px solid var(--color-border) !important;
.ant-color-picker & {
border-top: none !important;
}
}
}
.ant-slider {
.ant-slider-handle::after {
box-shadow: 0 1px 4px 0px rgb(128 128 128 / 50%) !important;
}
}
.ant-splitter-bar {
.ant-splitter-bar-dragger {
&::before {
background-color: var(--color-border) !important;
transition:
background-color 0.15s ease,
width 0.15s ease;
}
&:hover {
&::before {
width: 4px !important;
background-color: var(--color-primary) !important;
transition-delay: 0.15s;
}
}
}
.ant-splitter-bar-dragger-active {
&::before {
width: 4px !important;
background-color: var(--color-primary) !important;
}
}
}

View File

@@ -19,7 +19,7 @@
--color-background-soft: var(--color-black-soft);
--color-background-mute: var(--color-black-mute);
--color-background-opacity: rgba(34, 34, 34, 0.7);
--inner-glow-opacity: 0.3; // For the glassmorphism effect in the dropdown menu
--inner-glow-opacity: 0.3; /* For the glassmorphism effect in the dropdown menu */
--color-primary: #00b96b;
--color-primary-soft: #00b96b99;

View File

@@ -0,0 +1,9 @@
#content-container {
background-color: var(--color-background);
}
[navbar-position='left'] #content-container {
border-top: 0.5px solid var(--color-border);
border-top-left-radius: 10px;
border-left: 0.5px solid var(--color-border);
}

View File

@@ -1,11 +0,0 @@
#content-container {
background-color: var(--color-background);
}
[navbar-position='left'] {
#content-container {
border-top: 0.5px solid var(--color-border);
border-top-left-radius: 10px;
border-left: 0.5px solid var(--color-border);
}
}

View File

@@ -11,7 +11,7 @@
--code-font-family: 'Cascadia Code', 'Fira Code', 'Consolas', Menlo, Courier, monospace;
}
// Windows系统专用字体配置
/* Windows系统专用字体配置 */
body[os='windows'] {
--font-family:
'Twemoji Country Flags', Ubuntu, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, Roboto, Oxygen,

View File

@@ -1,12 +1,12 @@
@use './color.scss';
@use './font.scss';
@use './markdown.scss';
@use './ant.scss';
@use './scrollbar.scss';
@use './container.scss';
@use './animation.scss';
@use './richtext.scss';
@use './responsive.scss';
@import './color.css';
@import './font.css';
@import './markdown.css';
@import './ant.css';
@import './scrollbar.css';
@import './container.css';
@import './animation.css';
@import './richtext.css';
@import './responsive.css';
@import '../fonts/icon-fonts/iconfont.css';
@import '../fonts/ubuntu/ubuntu.css';
@import '../fonts/country-flag-fonts/flag.css';
@@ -15,7 +15,7 @@
*::before,
*::after {
box-sizing: border-box;
margin: 0;
/* margin: 0; */
font-weight: normal;
}
@@ -35,11 +35,11 @@ body,
margin: 0;
}
#root {
/* #root {
display: flex;
flex-direction: row;
flex: 1;
}
} */
body {
display: flex;
@@ -114,62 +114,62 @@ ul {
word-wrap: break-word;
}
.bubble:not(.multi-select-mode) {
.block-wrapper {
display: flow-root;
}
.bubble:not(.multi-select-mode) .block-wrapper {
display: flow-root;
}
.block-wrapper:last-child > *:last-child {
margin-bottom: 0;
}
.bubble:not(.multi-select-mode) .block-wrapper:last-child > *:last-child {
margin-bottom: 0;
}
.message-content-container > *:last-child {
margin-bottom: 0;
}
.bubble:not(.multi-select-mode) .message-content-container > *:last-child {
margin-bottom: 0;
}
.message-thought-container {
margin-top: 8px;
}
.bubble:not(.multi-select-mode) .message-thought-container {
margin-top: 8px;
}
.message-user {
.message-header {
flex-direction: row-reverse;
text-align: right;
.message-header-info-wrap {
flex-direction: row-reverse;
text-align: right;
}
}
.message-content-container {
border-radius: 10px;
padding: 10px 16px 10px 16px;
background-color: var(--chat-background-user);
align-self: self-end;
}
.MessageFooter {
margin-top: 2px;
align-self: self-end;
}
}
.bubble:not(.multi-select-mode) .message-user .message-header {
flex-direction: row-reverse;
text-align: right;
}
.message-assistant {
.message-content-container {
padding-left: 0;
}
.MessageFooter {
margin-left: 0;
}
}
.bubble:not(.multi-select-mode) .message-user .message-header .message-header-info-wrap {
flex-direction: row-reverse;
text-align: right;
}
code {
color: var(--color-text);
}
.markdown {
display: flow-root;
*:last-child {
margin-bottom: 0;
}
}
.bubble:not(.multi-select-mode) .message-user .message-content-container {
border-radius: 10px;
padding: 10px 16px 10px 16px;
background-color: var(--chat-background-user);
align-self: self-end;
}
.bubble:not(.multi-select-mode) .message-user .MessageFooter {
margin-top: 2px;
align-self: self-end;
}
.bubble:not(.multi-select-mode) .message-assistant .message-content-container {
padding-left: 0;
}
.bubble:not(.multi-select-mode) .message-assistant .MessageFooter {
margin-left: 0;
}
.bubble:not(.multi-select-mode) code {
color: var(--color-text);
}
.bubble:not(.multi-select-mode) .markdown {
display: flow-root;
}
.bubble:not(.multi-select-mode) .markdown *:last-child {
margin-bottom: 0;
}
.lucide:not(.lucide-custom) {
@@ -185,8 +185,6 @@ ul {
background-color: var(--color-background-highlight-accent);
}
textarea {
&::-webkit-resizer {
display: none;
}
textarea::-webkit-resizer {
display: none;
}

View File

@@ -0,0 +1,388 @@
.markdown {
color: var(--color-text);
line-height: 1.6;
user-select: text;
word-break: break-word;
}
.markdown h1:first-child,
.markdown h2:first-child,
.markdown h3:first-child,
.markdown h4:first-child,
.markdown h5:first-child,
.markdown h6:first-child {
margin-top: 0;
}
.markdown h1,
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
margin: 1.5em 0 1em 0;
line-height: 1.3;
font-weight: bold;
}
.markdown h1 {
margin-top: 0;
font-size: 2em;
border-bottom: 0.5px solid var(--color-border);
padding-bottom: 0.3em;
}
.markdown h2 {
font-size: 1.5em;
border-bottom: 0.5px solid var(--color-border);
padding-bottom: 0.3em;
}
.markdown h3 {
font-size: 1.2em;
}
.markdown h4 {
font-size: 1em;
}
.markdown h5 {
font-size: 0.9em;
}
.markdown h6 {
font-size: 0.8em;
}
.markdown p {
margin: 1.3em 0;
white-space: pre-wrap;
line-height: 1.6;
}
.markdown p:last-child {
margin-bottom: 5px;
}
.markdown p:first-child {
margin-top: 0;
}
.markdown p:has(+ ul) {
margin-bottom: 0;
}
.markdown ul {
list-style: initial;
}
.markdown ul,
.markdown ol {
padding-left: 1.5em;
margin: 1em 0;
}
.markdown li {
margin-bottom: 0.5em;
}
.markdown li pre {
margin: 1.5em 0 !important;
}
.markdown li::marker {
color: var(--color-text-3);
}
.markdown li > ul,
.markdown li > ol {
margin: 0.5em 0;
}
.markdown hr {
border: none;
border-top: 0.5px solid var(--color-border);
margin: 20px 0;
}
.markdown span {
white-space: pre-wrap;
}
.markdown .katex span {
white-space: pre;
}
.markdown p code,
.markdown li code {
background: var(--color-background-mute);
padding: 3px 5px;
margin: 0 2px;
border-radius: 5px;
word-break: keep-all;
white-space: pre;
}
.markdown code {
font-family: var(--code-font-family);
}
.markdown pre {
border-radius: 8px;
overflow-x: auto;
font-family: var(--code-font-family);
background-color: var(--color-background-mute);
}
.markdown pre:has(.special-preview) {
background-color: transparent;
}
.markdown pre:not(pre pre) > code:not(pre pre > code) {
padding: 15px;
display: block;
}
.markdown pre pre {
margin: 0 !important;
}
.markdown pre pre code {
background: none;
padding: 0;
border-radius: 0;
}
.markdown pre + pre {
margin-top: 10px;
}
.markdown .markdown-alert,
.markdown blockquote {
margin: 1.5em 0;
padding: 1em 1.5em;
background-color: var(--color-background-soft);
border-left: 4px solid var(--color-primary);
border-radius: 0 8px 8px 0;
font-style: italic;
position: relative;
}
.markdown table {
--table-border-radius: 8px;
margin: 2em 0;
font-size: 0.9em;
width: 100%;
border-radius: var(--table-border-radius);
overflow: hidden;
border-collapse: separate;
border: 0.5px solid var(--color-border);
border-spacing: 0;
}
.markdown th,
.markdown td {
border-right: 0.5px solid var(--color-border);
border-bottom: 0.5px solid var(--color-border);
padding: 0.5em;
}
.markdown th:last-child,
.markdown td:last-child {
border-right: none;
}
.markdown tr:last-child td {
border-bottom: none;
}
.markdown th {
background-color: var(--color-background-mute);
font-weight: 600;
text-align: left;
}
.markdown tr:hover {
background-color: var(--color-background-soft);
}
.markdown img {
max-width: 100%;
height: auto;
margin: 1em 0;
}
.markdown a,
.markdown .link {
color: var(--color-link);
text-decoration: none;
cursor: pointer;
}
.markdown a:hover,
.markdown .link:hover {
text-decoration: underline;
}
.markdown strong {
font-weight: bold;
}
.markdown em {
font-style: italic;
}
.markdown del {
text-decoration: line-through;
}
.markdown sup,
.markdown sub {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
.markdown sup {
top: -0.5em;
border-radius: 50%;
background-color: var(--color-reference);
color: var(--color-reference-text);
padding: 2px 5px;
zoom: 0.8;
}
.markdown sup > span.link {
color: var(--color-reference-text);
}
.markdown sub {
bottom: -0.25em;
}
.markdown .footnote-ref {
font-size: 0.8em;
vertical-align: super;
line-height: 0;
margin: 0 2px;
color: var(--color-primary);
text-decoration: none;
}
.markdown .footnote-ref:hover {
text-decoration: underline;
}
.footnotes {
margin-top: 1em;
margin-bottom: 1em;
padding-top: 1em;
background-color: var(--color-reference-background);
border-radius: 8px;
padding: 8px 12px;
}
.footnotes h4 {
margin-bottom: 5px;
font-size: 12px;
}
.footnotes a {
color: var(--color-link);
}
.footnotes ol {
padding-left: 1em;
margin: 0;
}
.footnotes ol li:last-child {
margin-bottom: 0;
}
.footnotes li {
font-size: 0.9em;
margin-bottom: 0.5em;
color: var(--color-text-light);
}
.footnotes li p {
display: inline;
margin: 0;
}
.footnotes .footnote-backref {
font-size: 0.8em;
vertical-align: super;
line-height: 0;
margin-left: 5px;
color: var(--color-primary);
text-decoration: none;
}
.footnotes .footnote-backref:hover {
text-decoration: underline;
}
emoji-picker {
--border-size: 0;
}
.block-wrapper + .block-wrapper {
margin-top: 1em;
}
.katex,
mjx-container {
display: inline-block;
overflow-x: auto;
overflow-y: hidden;
overflow-wrap: break-word;
vertical-align: middle;
max-width: 100%;
padding: 1px 2px;
margin-top: -2px;
}
/* Shiki 相关样式 */
.shiki {
font-family: var(--code-font-family);
/* 保持行高为初始值,在 shiki 代码块中处理 */
line-height: initial;
}
/* CodeMirror 相关样式 */
.cm-editor {
border-radius: inherit;
}
.cm-editor.cm-focused {
outline: none;
}
.cm-editor .cm-scroller {
font-family: var(--code-font-family);
border-radius: inherit;
}
.cm-editor .cm-scroller .cm-gutters {
line-height: 1.6;
border-right: none;
}
.cm-editor .cm-scroller .cm-content {
line-height: 1.6;
padding-left: 0.25em;
}
.cm-editor .cm-scroller .cm-lineWrapping * {
word-wrap: break-word;
white-space: pre-wrap;
}
.cm-editor .cm-announced {
position: absolute;
display: none;
}

View File

@@ -1,379 +0,0 @@
.markdown {
color: var(--color-text);
line-height: 1.6;
user-select: text;
word-break: break-word;
h1:first-child,
h2:first-child,
h3:first-child,
h4:first-child,
h5:first-child,
h6:first-child {
margin-top: 0;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 1.5em 0 1em 0;
line-height: 1.3;
font-weight: bold;
}
h1 {
margin-top: 0;
font-size: 2em;
border-bottom: 0.5px solid var(--color-border);
padding-bottom: 0.3em;
}
h2 {
font-size: 1.5em;
border-bottom: 0.5px solid var(--color-border);
padding-bottom: 0.3em;
}
h3 {
font-size: 1.2em;
}
h4 {
font-size: 1em;
}
h5 {
font-size: 0.9em;
}
h6 {
font-size: 0.8em;
}
p {
margin: 1.3em 0;
white-space: pre-wrap;
line-height: 1.6;
&:last-child {
margin-bottom: 5px;
}
&:first-child {
margin-top: 0;
}
&:has(+ ul) {
margin-bottom: 0;
}
}
ul {
list-style: initial;
}
ul,
ol {
padding-left: 1.5em;
margin: 1em 0;
}
li {
margin-bottom: 0.5em;
pre {
margin: 1.5em 0 !important;
}
&::marker {
color: var(--color-text-3);
}
}
li > ul,
li > ol {
margin: 0.5em 0;
}
hr {
border: none;
border-top: 0.5px solid var(--color-border);
margin: 20px 0;
}
span {
white-space: pre-wrap;
}
.katex span {
white-space: pre;
}
p code,
li code {
background: var(--color-background-mute);
padding: 3px 5px;
margin: 0 2px;
border-radius: 5px;
word-break: keep-all;
white-space: pre;
}
code {
font-family: var(--code-font-family);
}
pre {
border-radius: 8px;
overflow-x: auto;
font-family: var(--code-font-family);
background-color: var(--color-background-mute);
&:has(.special-preview) {
background-color: transparent;
}
&:not(pre pre) {
> code:not(pre pre > code) {
padding: 15px;
display: block;
}
}
pre {
margin: 0 !important;
code {
background: none;
padding: 0;
border-radius: 0;
}
}
}
pre + pre {
margin-top: 10px;
}
.markdown-alert,
blockquote {
margin: 1.5em 0;
padding: 1em 1.5em;
background-color: var(--color-background-soft);
border-left: 4px solid var(--color-primary);
border-radius: 0 8px 8px 0;
font-style: italic;
position: relative;
}
table {
--table-border-radius: 8px;
margin: 2em 0;
font-size: 0.9em;
width: 100%;
border-radius: var(--table-border-radius);
overflow: hidden;
border-collapse: separate;
border: 0.5px solid var(--color-border);
border-spacing: 0;
}
th,
td {
border-right: 0.5px solid var(--color-border);
border-bottom: 0.5px solid var(--color-border);
padding: 0.5em;
&:last-child {
border-right: none;
}
}
tr:last-child td {
border-bottom: none;
}
th {
background-color: var(--color-background-mute);
font-weight: 600;
text-align: left;
}
tr:hover {
background-color: var(--color-background-soft);
}
img {
max-width: 100%;
height: auto;
margin: 1em 0;
}
a,
.link {
color: var(--color-link);
text-decoration: none;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
strong {
font-weight: bold;
}
em {
font-style: italic;
}
del {
text-decoration: line-through;
}
sup,
sub {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
border-radius: 50%;
background-color: var(--color-reference);
color: var(--color-reference-text);
padding: 2px 5px;
zoom: 0.8;
& > span.link {
color: var(--color-reference-text);
}
}
sub {
bottom: -0.25em;
}
.footnote-ref {
font-size: 0.8em;
vertical-align: super;
line-height: 0;
margin: 0 2px;
color: var(--color-primary);
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
.footnotes {
margin-top: 1em;
margin-bottom: 1em;
padding-top: 1em;
background-color: var(--color-reference-background);
border-radius: 8px;
padding: 8px 12px;
h4 {
margin-bottom: 5px;
font-size: 12px;
}
a {
color: var(--color-link);
}
ol {
padding-left: 1em;
margin: 0;
li:last-child {
margin-bottom: 0;
}
}
li {
font-size: 0.9em;
margin-bottom: 0.5em;
color: var(--color-text-light);
p {
display: inline;
margin: 0;
}
}
.footnote-backref {
font-size: 0.8em;
vertical-align: super;
line-height: 0;
margin-left: 5px;
color: var(--color-primary);
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
emoji-picker {
--border-size: 0;
}
.block-wrapper + .block-wrapper {
margin-top: 1em;
}
.katex,
mjx-container {
display: inline-block;
overflow-x: auto;
overflow-y: hidden;
overflow-wrap: break-word;
vertical-align: middle;
max-width: 100%;
padding: 1px 2px;
margin-top: -2px;
}
/* Shiki 相关样式 */
.shiki {
font-family: var(--code-font-family);
// 保持行高为初始值,在 shiki 代码块中处理
line-height: initial;
}
/* CodeMirror 相关样式 */
.cm-editor {
border-radius: inherit;
&.cm-focused {
outline: none;
}
.cm-scroller {
font-family: var(--code-font-family);
border-radius: inherit;
.cm-gutters {
line-height: 1.6;
border-right: none;
}
.cm-content {
line-height: 1.6;
padding-left: 0.25em;
}
.cm-lineWrapping * {
word-wrap: break-word;
white-space: pre-wrap;
}
}
.cm-announced {
position: absolute;
display: none;
}
}

View File

@@ -1,4 +1,4 @@
// xl, xxl, default style
/* xl, xxl, default style */
:root {
--navbar-height: 44px;
--sidebar-width: 50px;
@@ -17,7 +17,7 @@
--list-item-border-radius: 20px;
}
// lg
/* lg */
@media (max-width: 1080px) {
:root {
--assistants-width: 210px;

View File

@@ -0,0 +1,522 @@
.tiptap {
/* 预留5px给scrollbar */
padding: 12px 55px 12px 60px;
outline: none;
min-height: 120px;
overflow-wrap: break-word;
word-break: break-word;
}
.tiptap:focus {
outline: none;
}
.tiptap :first-child {
margin-top: 0;
}
.tiptap h1:first-child,
.tiptap h2:first-child,
.tiptap h3:first-child,
.tiptap h4:first-child,
.tiptap h5:first-child,
.tiptap h6:first-child {
margin-top: 0;
}
.tiptap h1,
.tiptap h2,
.tiptap h3,
.tiptap h4,
.tiptap h5,
.tiptap h6 {
margin: 1.5rem 0 1rem 0;
line-height: 1.1;
text-wrap: pretty;
font-weight: 600;
}
.tiptap h1 code,
.tiptap h2 code,
.tiptap h3 code,
.tiptap h4 code,
.tiptap h5 code,
.tiptap h6 code {
font-size: inherit;
font-weight: inherit;
}
.tiptap h1 {
margin-top: 0;
font-size: 2rem;
}
.tiptap h2 {
font-size: 1.5rem;
}
.tiptap h3 {
font-size: 1.2rem;
}
.tiptap h4,
.tiptap h5,
.tiptap h6 {
font-size: 1rem;
}
.tiptap p {
margin: 1.1rem 0 0.5rem 0;
white-space: normal;
overflow-wrap: break-word;
word-break: break-word;
width: 100%;
line-height: 1.6;
hyphens: auto;
}
.tiptap p:has(+ ul) {
margin-bottom: 0;
}
.tiptap a {
color: var(--color-link);
text-decoration: none;
cursor: pointer;
}
.tiptap a:hover {
text-decoration: underline;
}
.tiptap blockquote {
border-left: 4px solid var(--color-primary);
margin: 1.5rem 0;
padding-left: 1rem;
}
.tiptap code {
background-color: var(--color-inline-code-background);
border-radius: 0.4rem;
color: var(--color-inline-code-text);
font-size: 0.85rem;
padding: 0.25em 0.3em;
font-family: var(--code-font-family);
}
.tiptap pre {
background: var(--color-code-background);
border-radius: 0.5rem;
color: var(--color-text);
font-family: var(--code-font-family);
margin: 1.5rem 0;
padding: 0.75rem 1rem;
border: 1px solid var(--color-border-soft);
}
.tiptap pre code {
background: none;
color: inherit;
font-size: 0.8rem;
padding: 0;
border: none;
}
.tiptap hr {
border: none;
border-top: 1px solid var(--color-gray-2);
margin: 2rem 0;
}
.tiptap em {
font-style: italic;
}
.tiptap u {
text-decoration: underline;
}
.tiptap strong,
.tiptap strong * {
font-weight: 600;
}
.tiptap .placeholder {
position: relative;
}
.tiptap .placeholder:before {
content: attr(data-placeholder);
position: absolute;
color: var(--color-text-secondary);
opacity: 0.6;
pointer-events: none;
font-style: italic;
left: 0;
right: 0;
}
/* Ensure drag handles and plus buttons remain interactive */
.tiptap .placeholder .drag-handle,
.tiptap .placeholder .plus-button {
pointer-events: auto;
}
/* Show placeholder only when focused or when it's the only empty node */
.tiptap .placeholder.has-focus:before {
opacity: 0.8;
}
.tiptap img {
max-width: 800px;
width: 100%;
height: auto;
}
.tiptap table {
border-collapse: collapse;
margin: 0;
/* Allow action endpoints (rendered as decorations) to slightly overflow table edges */
overflow: visible;
table-layout: fixed;
width: 100%;
}
.tiptap td,
.tiptap th {
border: 1px solid var(--color-border-soft);
box-sizing: border-box;
display: table-cell;
min-width: 120px;
padding: 6px 8px;
position: relative;
vertical-align: top;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tiptap td > *,
.tiptap th > * {
margin-bottom: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tiptap th,
.tiptap th * {
background-color: var(--color-gray-3);
font-weight: bold;
text-align: left;
}
.tiptap .selectedCell {
position: relative; /* 确保伪元素定位 */
}
.tiptap .selectedCell::after {
content: '';
position: absolute;
inset: 0;
pointer-events: none;
border: 0 solid var(--color-primary);
border-radius: 0;
}
.tiptap .selectedCell.selection-top::after {
border-top-width: 2px;
}
.tiptap .selectedCell.selection-bottom::after {
border-bottom-width: 2px;
}
.tiptap .selectedCell.selection-left::after {
border-left-width: 2px;
}
.tiptap .selectedCell.selection-right::after {
border-right-width: 2px;
}
.tiptap .column-resize-handle {
background-color: var(--color-primary);
bottom: -2px;
pointer-events: none;
position: absolute;
right: -2px;
top: 0;
width: 4px;
}
.tiptap table:has(.selectedCell) {
caret-color: transparent !important;
user-select: none !important;
}
.tiptap table:has(.selectedCell) *::selection {
background: transparent !important;
}
.tiptap table:has(.selectedCell) .column-resize-handle {
display: none;
}
/* Position row action buttons relative to first column cells */
.tiptap table tbody tr td:first-child,
.tiptap table tbody tr th:first-child {
position: relative;
}
/* Position column action buttons relative to first row cells */
.tiptap table tbody tr:first-child td,
.tiptap table tbody tr:first-child th {
position: relative;
}
.tiptap .tableWrapper {
position: relative;
margin: 1rem 0;
display: grid;
grid-template-columns: 1fr 25px;
grid-template-rows: 1fr 25px;
grid-template-areas:
'table column-btn'
'row-btn corner';
gap: 5px;
}
.tiptap .tableWrapper .table-container {
grid-area: table;
overflow-x: auto;
overflow-y: visible;
}
.tiptap .tableWrapper .table-container::-webkit-scrollbar {
cursor: default;
}
.tiptap .tableWrapper .table-container::-webkit-scrollbar:horizontal {
cursor: default;
}
.tiptap .tableWrapper .table-container::-webkit-scrollbar-thumb {
cursor: default;
}
.tiptap .tableWrapper .table-container::-webkit-scrollbar-track {
cursor: default;
}
.tiptap .tableWrapper .table-container table {
width: max-content;
min-width: 100%;
}
.tiptap .tableWrapper .add-row-button,
.tiptap .tableWrapper .add-column-button {
border: 1px solid var(--color-border);
background: var(--color-bg-base);
border-radius: 4px;
font-size: 12px;
line-height: 1;
cursor: pointer;
display: none;
align-items: center;
justify-content: center;
color: var(--color-text);
z-index: 20;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
pointer-events: auto;
}
.tiptap .tableWrapper .add-row-button:hover,
.tiptap .tableWrapper .add-column-button:hover {
background: var(--color-primary);
color: white;
border-color: var(--color-primary);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.tiptap .tableWrapper .add-row-button:active,
.tiptap .tableWrapper .add-column-button:active {
transform: scale(0.98);
}
.tiptap .tableWrapper .add-row-button::before,
.tiptap .tableWrapper .add-column-button::before {
content: '+';
font-weight: bold;
}
.tiptap .tableWrapper .add-row-button {
grid-area: row-btn;
}
.tiptap .tableWrapper .add-column-button {
grid-area: column-btn;
}
.tiptap .tableWrapper:hover .add-row-button,
.tiptap .tableWrapper:has(.add-row-button:hover) .add-row-button,
.tiptap .tableWrapper:has(.add-column-button:hover) .add-row-button,
.tiptap .tableWrapper:hover .add-column-button,
.tiptap .tableWrapper:has(.add-row-button:hover) .add-column-button,
.tiptap .tableWrapper:has(.add-column-button:hover) .add-column-button {
display: flex;
}
/* Do not show in readonly even on hover */
.tiptap .tableWrapper.is-readonly .add-row-button,
.tiptap .tableWrapper.is-readonly:hover .add-row-button,
.tiptap .tableWrapper.is-readonly .add-column-button,
.tiptap .tableWrapper.is-readonly:hover .add-column-button {
display: none !important;
}
.tiptap .tableWrapper .add-row-button:hover,
.tiptap .tableWrapper .add-column-button:hover {
display: flex !important;
}
/* Row/Column action triggers (visible on cell selection) */
.tiptap .tableWrapper .row-action-trigger,
.tiptap .tableWrapper .column-action-trigger {
position: absolute;
height: 20px;
border-radius: 8px;
background: var(--color-primary);
color: #fff;
border: 1px solid var(--color-primary);
display: none;
align-items: center;
justify-content: center;
font-size: 12px;
line-height: 1;
z-index: 30;
pointer-events: auto;
}
.tiptap .tableWrapper .row-action-trigger::before,
.tiptap .tableWrapper .column-action-trigger::before {
content: '•••';
}
.tiptap.resize-cursor {
cursor: ew-resize;
cursor: col-resize;
}
.tiptap ul,
.tiptap ol {
padding: 0 1rem;
margin: 1.25rem 1rem 1.25rem 0.4rem;
}
.tiptap ul li p,
.tiptap ol li p {
margin-top: 0.25em;
margin-bottom: 0.25em;
}
/* Reduce spacing for nested lists */
.tiptap ul ul,
.tiptap ul ol,
.tiptap ol ul,
.tiptap ol ol {
margin: 0.5rem 0.5rem 0.5rem 0.2rem;
}
.tiptap ul {
list-style: disc;
}
.tiptap ol {
list-style: decimal;
}
.tiptap ul[data-type='taskList'] {
list-style: none;
margin-left: 0;
padding: 0;
}
.tiptap ul[data-type='taskList'] li {
align-items: center;
display: flex;
}
.tiptap ul[data-type='taskList'] li > label {
flex: 0 0 auto;
margin-right: 0.5rem;
user-select: none;
display: flex;
align-items: center;
}
.tiptap ul[data-type='taskList'] li > div {
flex: 1 1 auto;
}
.tiptap ul[data-type='taskList'] li > div p {
margin: 0;
}
/* Checked task item appearance */
.tiptap ul[data-type='taskList'] li[data-checked='true'] > div {
color: var(--color-text-2);
text-decoration: line-through;
}
.tiptap ul[data-type='taskList'] input[type='checkbox'] {
cursor: pointer;
}
/* Use primary color for checked checkbox */
.tiptap ul[data-type='taskList'] input[type='checkbox']:checked {
accent-color: var(--color-primary);
background-color: var(--color-primary);
border-color: var(--color-primary);
}
.tiptap ul[data-type='taskList'] ul[data-type='taskList'] {
margin: 0;
}
/* Math block */
.tiptap .block-math-inner {
display: flex;
justify-content: center;
align-items: center;
font-size: 1.2rem;
}
/* Bottom spacer to create viewport padding */
.tiptap::after {
content: '';
display: block;
height: 50px;
pointer-events: none;
}
/* Code block wrapper and header styles */
.code-block-wrapper {
position: relative;
}
.code-block-wrapper .code-block-header {
display: flex;
align-items: center;
gap: 6px;
position: absolute;
top: 4px;
right: 6px;
opacity: 0;
transition: opacity 0.2s;
}
.code-block-wrapper:hover .code-block-header {
opacity: 1;
}

View File

@@ -1,508 +0,0 @@
.tiptap {
// 预留5px给scrollbar
padding: 12px 55px 12px 60px;
outline: none;
min-height: 120px;
overflow-wrap: break-word;
word-break: break-word;
&:focus {
outline: none;
}
:first-child {
margin-top: 0;
}
h1:first-child,
h2:first-child,
h3:first-child,
h4:first-child,
h5:first-child,
h6:first-child {
margin-top: 0;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 1.5rem 0 1rem 0;
line-height: 1.1;
text-wrap: pretty;
font-weight: 600;
code {
font-size: inherit;
font-weight: inherit;
}
}
h1 {
margin-top: 0;
font-size: 2rem;
}
h2 {
font-size: 1.5rem;
}
h3 {
font-size: 1.2rem;
}
h4,
h5,
h6 {
font-size: 1rem;
}
p {
margin: 1.1rem 0 0.5rem 0;
white-space: normal;
overflow-wrap: break-word;
word-break: break-word;
width: 100%;
line-height: 1.6;
hyphens: auto;
&:has(+ ul) {
margin-bottom: 0;
}
}
a {
color: var(--color-link);
text-decoration: none;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
blockquote {
border-left: 4px solid var(--color-primary);
margin: 1.5rem 0;
padding-left: 1rem;
}
code {
background-color: var(--color-inline-code-background);
border-radius: 0.4rem;
color: var(--color-inline-code-text);
font-size: 0.85rem;
padding: 0.25em 0.3em;
font-family: var(--code-font-family);
}
pre {
background: var(--color-code-background);
border-radius: 0.5rem;
color: var(--color-text);
font-family: var(--code-font-family);
margin: 1.5rem 0;
padding: 0.75rem 1rem;
border: 1px solid var(--color-border-soft);
code {
background: none;
color: inherit;
font-size: 0.8rem;
padding: 0;
border: none;
}
}
hr {
border: none;
border-top: 1px solid var(--color-gray-2);
margin: 2rem 0;
}
em {
font-style: italic;
}
u {
text-decoration: underline;
}
strong,
strong * {
font-weight: 600;
}
.placeholder {
position: relative;
&:before {
content: attr(data-placeholder);
position: absolute;
color: var(--color-text-secondary);
opacity: 0.6;
pointer-events: none;
font-style: italic;
left: 0;
right: 0;
}
/* Ensure drag handles and plus buttons remain interactive */
.drag-handle,
.plus-button {
pointer-events: auto;
}
}
/* Show placeholder only when focused or when it's the only empty node */
.placeholder.has-focus:before {
opacity: 0.8;
}
img {
max-width: 800px;
width: 100%;
height: auto;
}
table {
border-collapse: collapse;
margin: 0;
/* Allow action endpoints (rendered as decorations) to slightly overflow table edges */
overflow: visible;
table-layout: fixed;
width: 100%;
td,
th {
border: 1px solid var(--color-border-soft);
box-sizing: border-box;
display: table-cell;
min-width: 120px;
padding: 6px 8px;
position: relative;
vertical-align: top;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
> * {
margin-bottom: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
th,
th * {
background-color: var(--color-gray-3);
font-weight: bold;
text-align: left;
}
.selectedCell {
position: relative; // 确保伪元素定位
}
.selectedCell::after {
content: '';
position: absolute;
inset: 0;
pointer-events: none;
border: 0 solid var(--color-primary);
border-radius: 0;
}
.selectedCell.selection-top::after {
border-top-width: 2px;
}
.selectedCell.selection-bottom::after {
border-bottom-width: 2px;
}
.selectedCell.selection-left::after {
border-left-width: 2px;
}
.selectedCell.selection-right::after {
border-right-width: 2px;
}
.column-resize-handle {
background-color: var(--color-primary);
bottom: -2px;
pointer-events: none;
position: absolute;
right: -2px;
top: 0;
width: 4px;
}
&:has(.selectedCell) {
caret-color: transparent !important;
user-select: none !important;
*::selection {
background: transparent !important;
}
.column-resize-handle {
display: none;
}
}
// Position row action buttons relative to first column cells
tbody tr td:first-child,
tbody tr th:first-child {
position: relative;
}
// Position column action buttons relative to first row cells
tbody tr:first-child td,
tbody tr:first-child th {
position: relative;
}
}
.tableWrapper {
position: relative;
margin: 1rem 0;
display: grid;
grid-template-columns: 1fr 25px;
grid-template-rows: 1fr 25px;
grid-template-areas:
'table column-btn'
'row-btn corner';
gap: 5px;
.table-container {
grid-area: table;
overflow-x: auto;
overflow-y: visible;
&::-webkit-scrollbar {
cursor: default;
}
&::-webkit-scrollbar:horizontal {
cursor: default;
}
&::-webkit-scrollbar-thumb {
cursor: default;
}
&::-webkit-scrollbar-track {
cursor: default;
}
table {
width: max-content;
min-width: 100%;
}
}
.add-row-button,
.add-column-button {
border: 1px solid var(--color-border);
background: var(--color-bg-base);
border-radius: 4px;
font-size: 12px;
line-height: 1;
cursor: pointer;
display: none;
align-items: center;
justify-content: center;
color: var(--color-text);
z-index: 20;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
pointer-events: auto;
&:hover {
background: var(--color-primary);
color: white;
border-color: var(--color-primary);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
&:active {
transform: scale(0.98);
}
&::before {
content: '+';
font-weight: bold;
}
}
.add-row-button {
grid-area: row-btn;
}
.add-column-button {
grid-area: column-btn;
}
&:hover,
&:has(.add-row-button:hover),
&:has(.add-column-button:hover) {
.add-row-button,
.add-column-button {
display: flex;
}
}
/* Do not show in readonly even on hover */
&.is-readonly,
&.is-readonly:hover {
.add-row-button,
.add-column-button {
display: none !important;
}
}
.add-row-button:hover,
.add-column-button:hover {
display: flex !important;
}
/* Row/Column action triggers (visible on cell selection) */
.row-action-trigger,
.column-action-trigger {
position: absolute;
height: 20px;
border-radius: 8px;
background: var(--color-primary);
color: #fff;
border: 1px solid var(--color-primary);
display: none;
align-items: center;
justify-content: center;
font-size: 12px;
line-height: 1;
z-index: 30;
pointer-events: auto;
}
.row-action-trigger::before,
.column-action-trigger::before {
content: '•••';
}
}
&.resize-cursor {
cursor: ew-resize;
cursor: col-resize;
}
ul,
ol {
padding: 0 1rem;
margin: 1.25rem 1rem 1.25rem 0.4rem;
li p {
margin-top: 0.25em;
margin-bottom: 0.25em;
}
// Reduce spacing for nested lists
ul,
ol {
margin: 0.5rem 0.5rem 0.5rem 0.2rem;
}
}
ul {
list-style: disc;
}
ol {
list-style: decimal;
}
ul[data-type='taskList'] {
list-style: none;
margin-left: 0;
padding: 0;
li {
align-items: center;
display: flex;
> label {
flex: 0 0 auto;
margin-right: 0.5rem;
user-select: none;
display: flex;
align-items: center;
}
> div {
flex: 1 1 auto;
p {
margin: 0;
}
}
}
/* Checked task item appearance */
li[data-checked='true'] {
> div {
color: var(--color-text-2);
text-decoration: line-through;
}
}
input[type='checkbox'] {
cursor: pointer;
}
/* Use primary color for checked checkbox */
input[type='checkbox']:checked {
accent-color: var(--color-primary);
background-color: var(--color-primary);
border-color: var(--color-primary);
}
ul[data-type='taskList'] {
margin: 0;
}
}
/* Math block */
.block-math-inner {
display: flex;
justify-content: center;
align-items: center;
font-size: 1.2rem;
}
/* Bottom spacer to create viewport padding */
&::after {
content: '';
display: block;
height: 50px;
pointer-events: none;
}
}
// Code block wrapper and header styles
.code-block-wrapper {
position: relative;
.code-block-header {
display: flex;
align-items: center;
gap: 6px;
position: absolute;
top: 4px;
right: 6px;
opacity: 0;
transition: opacity 0.2s;
}
&:hover .code-block-header {
opacity: 1;
}
}

View File

@@ -31,17 +31,19 @@ body[theme-mode='light'] {
::-webkit-scrollbar-thumb {
border-radius: var(--scrollbar-thumb-radius);
background: var(--color-scrollbar-thumb);
&:hover {
background: var(--color-scrollbar-thumb-hover);
}
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-scrollbar-thumb-hover);
}
pre:not(.shiki)::-webkit-scrollbar-thumb {
border-radius: 0;
background: rgba(0, 0, 0, 0.08);
&:hover {
background: rgba(0, 0, 0, 0.15);
}
}
pre:not(.shiki)::-webkit-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 0.15);
}
.shiki-dark {
@@ -71,7 +73,8 @@ pre:not(.shiki)::-webkit-scrollbar-thumb {
.rc-virtual-list-scrollbar-thumb {
border-radius: var(--scrollbar-thumb-radius) !important;
background: var(--color-scrollbar-thumb) !important;
&:hover {
background: var(--color-scrollbar-thumb-hover) !important;
}
}
.rc-virtual-list-scrollbar-thumb:hover {
background: var(--color-scrollbar-thumb-hover) !important;
}

View File

@@ -0,0 +1,72 @@
@import './font.css';
html {
font-family: var(--font-family);
}
:root {
/* Basic Colors */
--color-error: #f44336;
--selection-toolbar-color-error: var(--color-error);
/* Toolbar */
--selection-toolbar-height: 36px; /* default: 36px max: 42px */
--selection-toolbar-font-size: 14px; /* default: 14px */
--selection-toolbar-logo-display: flex; /* values: flex | none */
--selection-toolbar-logo-size: 22px; /* default: 22px */
--selection-toolbar-logo-border-width: 0.5px 0 0.5px 0.5px; /* default: none */
--selection-toolbar-logo-border-style: solid; /* default: none */
--selection-toolbar-logo-border-color: rgba(255, 255, 255, 0.2);
--selection-toolbar-logo-margin: 0; /* default: 0 */
--selection-toolbar-logo-padding: 0 6px 0 8px; /* default: 0 4px 0 8px */
--selection-toolbar-logo-background: transparent; /* default: transparent */
/* DO NOT MODIFY THESE VALUES, IF YOU DON'T KNOW WHAT YOU ARE DOING */
--selection-toolbar-padding: 0; /* default: 0 */
--selection-toolbar-margin: 2px 3px 5px 3px; /* default: 2px 3px 5px 3px */
/* ------------------------------------------------------------ */
--selection-toolbar-border-radius: 10px;
--selection-toolbar-border: none;
--selection-toolbar-box-shadow: 0px 2px 3px rgba(50, 50, 50, 0.3);
--selection-toolbar-background: rgba(20, 20, 20, 0.95);
/* Buttons */
--selection-toolbar-buttons-border-width: 0.5px 0.5px 0.5px 0;
--selection-toolbar-buttons-border-style: solid;
--selection-toolbar-buttons-border-color: rgba(255, 255, 255, 0.2);
--selection-toolbar-buttons-border-radius: 0 var(--selection-toolbar-border-radius)
var(--selection-toolbar-border-radius) 0;
--selection-toolbar-button-icon-size: 16px; /* default: 16px */
--selection-toolbar-button-direction: row; /* default: row | column */
--selection-toolbar-button-text-margin: 0 0 0 0; /* default: 0 0 0 0 */
--selection-toolbar-button-margin: 0; /* default: 0 */
--selection-toolbar-button-padding: 0 8px; /* default: 0 8px */
--selection-toolbar-button-last-padding: 0 12px 0 8px;
--selection-toolbar-button-border-radius: 0; /* default: 0 */
--selection-toolbar-button-border: none; /* default: none */
--selection-toolbar-button-box-shadow: none; /* default: none */
--selection-toolbar-button-text-color: rgba(255, 255, 245, 0.9);
--selection-toolbar-button-icon-color: var(--selection-toolbar-button-text-color);
--selection-toolbar-button-bgcolor: transparent; /* default: transparent */
--selection-toolbar-button-bgcolor-hover: #333333;
}
[theme-mode='light'] {
--selection-toolbar-border: none;
--selection-toolbar-box-shadow: 0px 2px 3px rgba(50, 50, 50, 0.1);
--selection-toolbar-background: rgba(245, 245, 245, 0.95);
/* Buttons */
--selection-toolbar-buttons-border-color: rgba(0, 0, 0, 0.08);
--selection-toolbar-logo-border-color: rgba(0, 0, 0, 0.08);
--selection-toolbar-button-text-color: rgba(0, 0, 0, 1);
--selection-toolbar-button-icon-color: var(--selection-toolbar-button-text-color);
--selection-toolbar-button-bgcolor-hover: rgba(0, 0, 0, 0.04);
}

View File

@@ -1,72 +0,0 @@
@use './font.scss';
html {
font-family: var(--font-family);
}
:root {
// Basic Colors
--color-error: #f44336;
--selection-toolbar-color-error: var(--color-error);
// Toolbar
--selection-toolbar-height: 36px; // default: 36px max: 42px
--selection-toolbar-font-size: 14px; // default: 14px
--selection-toolbar-logo-display: flex; // values: flex | none
--selection-toolbar-logo-size: 22px; // default: 22px
--selection-toolbar-logo-border-width: 0.5px 0 0.5px 0.5px; // default: none
--selection-toolbar-logo-border-style: solid; // default: none
--selection-toolbar-logo-border-color: rgba(255, 255, 255, 0.2);
--selection-toolbar-logo-margin: 0; // default: 0
--selection-toolbar-logo-padding: 0 6px 0 8px; // default: 0 4px 0 8px
--selection-toolbar-logo-background: transparent; // default: transparent
// DO NOT MODIFY THESE VALUES, IF YOU DON'T KNOW WHAT YOU ARE DOING
--selection-toolbar-padding: 0; // default: 0
--selection-toolbar-margin: 2px 3px 5px 3px; // default: 2px 3px 5px 3px
// ------------------------------------------------------------
--selection-toolbar-border-radius: 10px;
--selection-toolbar-border: none;
--selection-toolbar-box-shadow: 0px 2px 3px rgba(50, 50, 50, 0.3);
--selection-toolbar-background: rgba(20, 20, 20, 0.95);
// Buttons
--selection-toolbar-buttons-border-width: 0.5px 0.5px 0.5px 0;
--selection-toolbar-buttons-border-style: solid;
--selection-toolbar-buttons-border-color: rgba(255, 255, 255, 0.2);
--selection-toolbar-buttons-border-radius: 0 var(--selection-toolbar-border-radius)
var(--selection-toolbar-border-radius) 0;
--selection-toolbar-button-icon-size: 16px; // default: 16px
--selection-toolbar-button-direction: row; // default: row | column
--selection-toolbar-button-text-margin: 0 0 0 0; // default: 0 0 0 0
--selection-toolbar-button-margin: 0; // default: 0
--selection-toolbar-button-padding: 0 8px; // default: 0 8px
--selection-toolbar-button-last-padding: 0 12px 0 8px;
--selection-toolbar-button-border-radius: 0; // default: 0
--selection-toolbar-button-border: none; // default: none
--selection-toolbar-button-box-shadow: none; // default: none
--selection-toolbar-button-text-color: rgba(255, 255, 245, 0.9);
--selection-toolbar-button-icon-color: var(--selection-toolbar-button-text-color);
--selection-toolbar-button-bgcolor: transparent; // default: transparent
--selection-toolbar-button-bgcolor-hover: #333333;
}
[theme-mode='light'] {
--selection-toolbar-border: none;
--selection-toolbar-box-shadow: 0px 2px 3px rgba(50, 50, 50, 0.1);
--selection-toolbar-background: rgba(245, 245, 245, 0.95);
// Buttons
--selection-toolbar-buttons-border-color: rgba(0, 0, 0, 0.08);
--selection-toolbar-logo-border-color: rgba(0, 0, 0, 0.08);
--selection-toolbar-button-text-color: rgba(0, 0, 0, 1);
--selection-toolbar-button-icon-color: var(--selection-toolbar-button-text-color);
--selection-toolbar-button-bgcolor-hover: rgba(0, 0, 0, 0.04);
}

View File

@@ -0,0 +1,156 @@
@import 'tailwindcss' source('../../../../renderer');
@import 'tw-animate-css';
/* heroui */
@plugin '../../hero.ts';
@source '../../../../../node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}';
@custom-variant dark (&:is(.dark *));
/* 如需自定义:
1. 清晰地组织自定义 CSS 到相应的层中。
2. 基础样式(如全局重置、链接样式)放入 base 层;
3. 可复用的组件样式(如果仍使用 @apply 或原生 CSS 嵌套创建)放入 components 层;
4. 新的自定义工具类放入 utilities 层。
*/
/*To customize:
1. Clearly organize custom CSS into the corresponding layers.
2. Place basic styles (such as global reset and link styles) in the base layer;
3. Put reusable component styles (if still created using @ apply or native CSS nesting) into the components layer;
4. Put the new custom utility class into the utilities layer.
*/
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.141 0.005 285.823);
--card: oklch(1 0 0);
--card-foreground: oklch(0.141 0.005 285.823);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.141 0.005 285.823);
--primary: oklch(0.21 0.006 285.885);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.967 0.001 286.375);
--secondary-foreground: oklch(0.21 0.006 285.885);
--muted: oklch(0.967 0.001 286.375);
--muted-foreground: oklch(0.552 0.016 285.938);
--accent: oklch(0.967 0.001 286.375);
--accent-foreground: oklch(0.21 0.006 285.885);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.92 0.004 286.32);
--input: oklch(0.92 0.004 286.32);
--ring: oklch(0.705 0.015 286.067);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.141 0.005 285.823);
--sidebar-primary: oklch(0.21 0.006 285.885);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.967 0.001 286.375);
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
--sidebar-border: oklch(0.92 0.004 286.32);
--sidebar-ring: oklch(0.705 0.015 286.067);
}
.dark {
--background: oklch(0.141 0.005 285.823);
--foreground: oklch(0.985 0 0);
--card: oklch(0.21 0.006 285.885);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.21 0.006 285.885);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.92 0.004 286.32);
--primary-foreground: oklch(0.21 0.006 285.885);
--secondary: oklch(0.274 0.006 286.033);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.274 0.006 286.033);
--muted-foreground: oklch(0.705 0.015 286.067);
--accent: oklch(0.274 0.006 286.033);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.552 0.016 285.938);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.21 0.006 285.885);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.274 0.006 286.033);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.552 0.016 285.938);
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--animate-marquee: marquee var(--duration) infinite linear;
--animate-marquee-vertical: marquee-vertical var(--duration) linear infinite;
@keyframes marquee {
from {
transform: translateX(0);
}
to {
transform: translateX(calc(-100% - var(--gap)));
}
}
@keyframes marquee-vertical {
from {
transform: translateY(0);
}
to {
transform: translateY(calc(-100% - var(--gap)));
}
}
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}

View File

@@ -1,10 +1,10 @@
import { Button } from '@heroui/button'
import { formatErrorMessage } from '@renderer/utils/error'
import { Alert, Button, Space } from 'antd'
import { Alert, Space } from 'antd'
import { ComponentType, ReactNode } from 'react'
import { ErrorBoundary, FallbackProps } from 'react-error-boundary'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
const DefaultFallback: ComponentType<FallbackProps> = (props: FallbackProps): ReactNode => {
const { t } = useTranslation()
const { error } = props
@@ -23,10 +23,10 @@ const DefaultFallback: ComponentType<FallbackProps> = (props: FallbackProps): Re
type="error"
action={
<Space>
<Button size="small" danger onClick={debug}>
<Button size="sm" onPress={debug}>
{t('error.boundary.default.devtools')}
</Button>
<Button size="small" danger onClick={reload}>
<Button size="sm" onPress={reload}>
{t('error.boundary.default.reload')}
</Button>
</Space>

View File

@@ -1,5 +1,5 @@
import { lightbulbVariants } from '@renderer/utils/motionVariants'
import { motion } from 'framer-motion'
import { motion } from 'motion/react'
import { SVGProps } from 'react'
export const StreamlineGoodHealthAndWellBeing = (

View File

@@ -1,4 +1,4 @@
import '@renderer/assets/styles/CommandListPopover.scss'
import '@renderer/assets/styles/CommandListPopover.css'
import { DynamicVirtualList, type DynamicVirtualListRef } from '@renderer/components/VirtualList'
import { useTheme } from '@renderer/context/ThemeProvider'

View File

@@ -0,0 +1,27 @@
import { ToastProvider } from '@heroui/toast'
import { useEffect, useState } from 'react'
import { createPortal } from 'react-dom'
export const ToastPortal = () => {
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
return () => setMounted(false)
}, [])
if (!mounted) return null
return createPortal(
<ToastProvider
placement="bottom-center"
regionProps={{
className: 'z-[1001]'
}}
toastProps={{
timeout: 3000
}}
/>,
document.body
)
}

View File

@@ -1,4 +1,5 @@
// import { loggerService } from '@logger'
import { addToast, closeAll, closeToast, getToastQueue, isToastClosing } from '@heroui/toast'
import TopViewMinappContainer from '@renderer/components/MinApp/TopViewMinappContainer'
import { useAppInit } from '@renderer/hooks/useAppInit'
import { useShortcuts } from '@renderer/hooks/useShortcuts'
@@ -43,6 +44,13 @@ const TopViewContainer: React.FC<Props> = ({ children }) => {
useEffect(() => {
window.message = messageApi
window.modal = modal
window.toast = {
getToastQueue: getToastQueue,
addToast: addToast,
closeToast: closeToast,
closeAll: closeAll,
isToastClosing: isToastClosing
}
}, [messageApi, modal])
onPop = () => {

View File

@@ -23,6 +23,12 @@ interface ThemeProviderProps extends PropsWithChildren {
defaultTheme?: ThemeMode
}
const tailwindThemeChange = (theme: ThemeMode) => {
const root = window.document.documentElement
root.classList.remove('light', 'dark')
root.classList.add(theme)
}
export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
// 用户设置的主题
const { theme: settedTheme, setTheme: setSettedTheme } = useSettings()
@@ -63,6 +69,7 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
}, [actualTheme, initUserTheme, navbarPosition, setSettedTheme, settedTheme])
useEffect(() => {
tailwindThemeChange(settedTheme)
window.api.setTheme(settedTheme)
}, [settedTheme])

View File

@@ -1,4 +1,5 @@
import './assets/styles/index.scss'
import './assets/styles/index.css'
import './assets/styles/tailwind.css'
import '@ant-design/v5-patch-for-react-19'
import { createRoot } from 'react-dom/client'

View File

@@ -1,5 +1,6 @@
/// <reference types="vite/client" />
import { addToast, closeAll, closeToast, getToastQueue, isToastClosing } from '@heroui/toast'
import type KeyvStorage from '@kangfenmao/keyv-storage'
import { MessageInstance } from 'antd/es/message/interface'
import { HookAPI } from 'antd/es/modal/useModal'
@@ -16,10 +17,20 @@ interface ImportMeta {
declare global {
interface Window {
root: HTMLElement
/**
* @deprecated
*/
message: MessageInstance
modal: HookAPI
keyv: KeyvStorage
store: any
navigate: NavigateFunction
toast: {
getToastQueue: typeof getToastQueue
addToast: typeof addToast
closeToast: typeof closeToast
closeAll: typeof closeAll
isToastClosing: typeof isToastClosing
}
}
}

2
src/renderer/src/hero.ts Normal file
View File

@@ -0,0 +1,2 @@
import { heroui } from '@heroui/react'
export default heroui()

View File

@@ -37,7 +37,7 @@ export const useMinappPopup = () => {
const createLRUCache = useCallback(() => {
return new LRUCache<string, MinAppType>({
max: maxKeepAliveMinapps,
max: maxKeepAliveMinapps ?? 10,
disposeAfter: (_value, key) => {
// Clean up WebView state when app is disposed from cache
clearWebviewState(key)

View File

@@ -1,3 +1,4 @@
import { Button } from '@heroui/button'
import CodeViewer from '@renderer/components/CodeViewer'
import { useTimer } from '@renderer/hooks/useTimer'
import { getHttpMessageLabel, getProviderLabel } from '@renderer/i18n/label'
@@ -33,7 +34,7 @@ import {
} from '@renderer/types/error'
import type { ErrorMessageBlock, Message } from '@renderer/types/newMessage'
import { formatAiSdkError, formatError, safeToString } from '@renderer/utils/error'
import { Alert as AntdAlert, Button, Modal } from 'antd'
import { Alert as AntdAlert, Modal } from 'antd'
import React, { useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { Link } from 'react-router-dom'
@@ -143,13 +144,7 @@ const MessageErrorInfo: React.FC<{ block: ErrorMessageBlock; message: Message }>
onClick={showErrorDetail}
style={{ cursor: 'pointer' }}
action={
<Button
size="small"
type="text"
onClick={(e) => {
e.stopPropagation()
showErrorDetail()
}}>
<Button size="sm" className="p-0" variant="light" onPress={showErrorDetail}>
{t('common.detail')}
</Button>
}
@@ -181,7 +176,7 @@ const ErrorDetailModal: React.FC<ErrorDetailModalProps> = ({ open, onClose, erro
}
navigator.clipboard.writeText(errorText)
window.message.success(t('message.copied'))
window.toast.addToast({ title: t('message.copied') })
}
const renderErrorDetails = (error?: SerializedError) => {
@@ -203,10 +198,10 @@ const ErrorDetailModal: React.FC<ErrorDetailModalProps> = ({ open, onClose, erro
open={open}
onCancel={onClose}
footer={[
<Button key="copy" onClick={copyErrorDetails}>
<Button key="copy" size="sm" variant="light" onPress={copyErrorDetails}>
{t('common.copy')}
</Button>,
<Button key="close" onClick={onClose}>
<Button key="close" size="sm" variant="light" onPress={onClose}>
{t('common.close')}
</Button>
]}

View File

@@ -1,4 +1,5 @@
export const lightbulbVariants = {
import type { Variants } from 'motion/react'
export const lightbulbVariants: Variants = {
active: {
opacity: [1, 0.2, 1],
transition: {
@@ -17,7 +18,7 @@ export const lightbulbVariants = {
}
}
export const lightbulbSoftVariants = {
export const lightbulbSoftVariants: Variants = {
active: {
opacity: [1, 0.5, 1],
transition: {

View File

@@ -1,4 +1,4 @@
import '@renderer/assets/styles/index.scss'
import '@renderer/assets/styles/index.css'
import '@ant-design/v5-patch-for-react-19'
import KeyvStorage from '@kangfenmao/keyv-storage'

View File

@@ -169,7 +169,7 @@ const WindowFooter: FC<FooterProps> = ({
<Pause size={14} className="btn-icon loading-icon" style={{ position: 'absolute', left: 1, top: 1 }} />
<LoadingOutlined
style={{ fontSize: 16, position: 'absolute', left: 0, top: 0 }}
className="btn-icon loading-icon"
className="btn-icon loading-icon"
spin
/>
</LoadingIconWrapper>

View File

@@ -1,4 +1,4 @@
import '@renderer/assets/styles/index.scss'
import '@renderer/assets/styles/index.css'
import '@ant-design/v5-patch-for-react-19'
import KeyvStorage from '@kangfenmao/keyv-storage'

View File

@@ -1,4 +1,4 @@
import '@renderer/assets/styles/selection-toolbar.scss'
import '@renderer/assets/styles/selection-toolbar.css'
import { loggerService } from '@logger'
import { AppLogo } from '@renderer/config/env'

View File

@@ -12,6 +12,10 @@
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"useDefineForClassFields": true
"useDefineForClassFields": true,
"baseUrl": ".",
"paths": {
"@renderer/*": ["src/renderer/src/*"]
}
}
}

View File

@@ -13,6 +13,7 @@
"compilerOptions": {
"composite": true,
"incremental": true,
"moduleResolution": "bundler",
"tsBuildInfoFile": ".tsbuildinfo/tsconfig.node.tsbuildinfo",
"types": [
"electron-vite/node",
@@ -24,7 +25,8 @@
"@main/*": ["src/main/*"],
"@types": ["src/renderer/src/types/index.ts"],
"@shared/*": ["packages/shared/*"],
"@mcp-trace/*": ["packages/mcp-trace/*"]
"@mcp-trace/*": ["packages/mcp-trace/*"],
"@modelcontextprotocol/sdk/*": ["node_modules/@modelcontextprotocol/sdk/dist/esm/*"]
},
"experimentalDecorators": true,
"emitDecoratorMetadata": true,

View File

@@ -25,7 +25,7 @@ export default defineConfig({
// 渲染进程单元测试配置
{
extends: true,
plugins: rendererConfig.plugins,
plugins: rendererConfig.plugins.filter((plugin: any) => plugin.name !== 'tailwindcss'),
resolve: {
alias: rendererConfig.resolve.alias
},

3519
yarn.lock

File diff suppressed because it is too large Load Diff