Merge pull request #381 from leookun/feature/conversation-architecture

Feature/conversation architecture
This commit is contained in:
leokun
2026-08-30 12:26:43 +08:00
committed by GitHub
598 changed files with 45667 additions and 61888 deletions
+2 -2
View File
@@ -206,7 +206,7 @@ jobs:
env: env:
VERSION: ${{ needs.prepare.outputs.version }} VERSION: ${{ needs.prepare.outputs.version }}
run: | run: |
node scripts/release/generate-legacy-update.mjs \ node .github/scripts/generate-legacy-update.mjs \
--version "${VERSION}" \ --version "${VERSION}" \
--repository "${GITHUB_REPOSITORY}" \ --repository "${GITHUB_REPOSITORY}" \
--assets-dir legacy-update \ --assets-dir legacy-update \
@@ -231,7 +231,7 @@ jobs:
-H "Accept: application/octet-stream" \ -H "Accept: application/octet-stream" \
"repos/${GITHUB_REPOSITORY}/releases/assets/${asset_id}" \ "repos/${GITHUB_REPOSITORY}/releases/assets/${asset_id}" \
> tauri-update/latest.json > tauri-update/latest.json
node scripts/release/normalize-tauri-update.mjs \ node .github/scripts/normalize-tauri-update.mjs \
--manifest tauri-update/latest.json \ --manifest tauri-update/latest.json \
--release tauri-update/release.json \ --release tauri-update/release.json \
--repository "${GITHUB_REPOSITORY}" \ --repository "${GITHUB_REPOSITORY}" \
+16
View File
@@ -7,3 +7,19 @@
- Prefer established, well-maintained libraries when they reduce overall complexity or improve reliability. Do not reimplement common functionality without a clear reason. - Prefer established, well-maintained libraries when they reduce overall complexity or improve reliability. Do not reimplement common functionality without a clear reason.
- Lean on the dependencies already in the project before writing your own implementation or adding packages. Do not assume a library lacks a capability without checking its documentation and types. - Lean on the dependencies already in the project before writing your own implementation or adding packages. Do not assume a library lacks a capability without checking its documentation and types.
- Make architectural decisions for the long term. Do not accept a stopgap that only works for now and is meant to be replaced later. - Make architectural decisions for the long term. Do not accept a stopgap that only works for now and is meant to be replaced later.
## Communication
- Lead with the conclusion. Keep responses direct and omit filler, repeated context, generic explanations, and narration of obvious steps.
- Match the level requested by the user: discuss architecture as architecture, behavior as behavior, and code details only when they materially support the answer.
- For architecture, refactoring, and module-boundary discussions, communicate primarily with annotated directory trees and ASCII architecture/data-flow diagrams instead of long prose.
- In directory trees, annotate every relevant directory and file with its single responsibility. When discussing code size, include line counts in the comments:
- use measured line counts for existing code;
- use clearly marked approximate targets such as `≈300 lines` for proposed code;
- state whether tests, generated code, and blank lines are excluded.
- Show the complete main execution path, including inputs, ownership boundaries, runtime loops, persistence, outputs, and extension points. Make the direction of data and control flow explicit.
- Clearly separate the current structure from the proposed structure. Explicitly list modules that move, merge, split, or are deleted.
- Use the user's vocabulary consistently. Do not introduce new domain terms when existing plain-language terms are sufficient; when an implementation name must be mentioned, distinguish it from the architecture concept.
- For stateful or concurrent behavior, show timing, ownership, state boundaries, and before/during/after behavior explicitly. Distinguish continuation within the same lifecycle from creation of a new lifecycle.
- Prefer compact trees, flow diagrams, state matrices, and mappings when they communicate the structure more clearly than paragraphs.
- Tie recommendations to the actual repository structure and code. Do not present a speculative target architecture as if it already exists.
Generated
+114
View File
@@ -97,6 +97,28 @@ dependencies = [
"x11rb", "x11rb",
] ]
[[package]]
name = "ashpd"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f3f79755c74fd155000314eb349864caa787c6592eace6c6882dad873d9c39"
dependencies = [
"async-fs",
"async-net",
"enumflags2",
"futures-channel",
"futures-util",
"rand 0.9.5",
"raw-window-handle",
"serde",
"serde_repr",
"url",
"wayland-backend",
"wayland-client",
"wayland-protocols",
"zbus",
]
[[package]] [[package]]
name = "asn1-rs" name = "asn1-rs"
version = "0.7.2" version = "0.7.2"
@@ -186,6 +208,17 @@ dependencies = [
"slab", "slab",
] ]
[[package]]
name = "async-fs"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5"
dependencies = [
"async-lock",
"blocking",
"futures-lite",
]
[[package]] [[package]]
name = "async-io" name = "async-io"
version = "2.6.0" version = "2.6.0"
@@ -215,6 +248,17 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "async-net"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7"
dependencies = [
"async-io",
"blocking",
"futures-lite",
]
[[package]] [[package]]
name = "async-process" name = "async-process"
version = "2.5.0" version = "2.5.0"
@@ -1132,6 +1176,7 @@ version = "0.1.5-beta.1"
dependencies = [ dependencies = [
"axum", "axum",
"cursor-server", "cursor-server",
"rfd",
"serde", "serde",
"serde_json", "serde_json",
"tauri", "tauri",
@@ -1145,6 +1190,7 @@ dependencies = [
"tokio", "tokio",
"tokio-util", "tokio-util",
"tracing", "tracing",
"tracing-appender",
"tracing-subscriber", "tracing-subscriber",
"url", "url",
] ]
@@ -1520,6 +1566,15 @@ dependencies = [
"syn 3.0.3", "syn 3.0.3",
] ]
[[package]]
name = "dlib"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a"
dependencies = [
"libloading",
]
[[package]] [[package]]
name = "dlopen2" name = "dlopen2"
version = "0.8.2" version = "0.8.2"
@@ -4555,6 +4610,12 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "pollster"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
[[package]] [[package]]
name = "portable-atomic" name = "portable-atomic"
version = "1.15.0" version = "1.15.0"
@@ -5196,6 +5257,30 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "rfd"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed"
dependencies = [
"ashpd",
"block2",
"dispatch2",
"js-sys",
"log",
"objc2",
"objc2-app-kit",
"objc2-core-foundation",
"objc2-foundation",
"pollster",
"raw-window-handle",
"urlencoding",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.17.14" version = "0.17.14"
@@ -6289,6 +6374,12 @@ dependencies = [
"serde_json", "serde_json",
] ]
[[package]]
name = "symlink"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@@ -7262,6 +7353,19 @@ dependencies = [
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-appender"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c"
dependencies = [
"crossbeam-channel",
"symlink",
"thiserror 2.0.20",
"time",
"tracing-subscriber",
]
[[package]] [[package]]
name = "tracing-attributes" name = "tracing-attributes"
version = "0.1.31" version = "0.1.31"
@@ -7636,6 +7740,12 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "urlencoding"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]] [[package]]
name = "urlpattern" name = "urlpattern"
version = "0.3.0" version = "0.3.0"
@@ -7852,6 +7962,7 @@ dependencies = [
"cc", "cc",
"downcast-rs", "downcast-rs",
"rustix", "rustix",
"scoped-tls",
"smallvec", "smallvec",
"wayland-sys", "wayland-sys",
] ]
@@ -7910,6 +8021,8 @@ version = "0.31.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"
dependencies = [ dependencies = [
"dlib",
"log",
"pkg-config", "pkg-config",
] ]
@@ -9028,6 +9141,7 @@ dependencies = [
"endi", "endi",
"enumflags2", "enumflags2",
"serde", "serde",
"url",
"winnow 1.0.4", "winnow 1.0.4",
"zcheapstr", "zcheapstr",
"zvariant_derive", "zvariant_derive",
+1 -1
View File
@@ -3,7 +3,7 @@ members = [
"server", "server",
"apps/desktop/src-tauri", "apps/desktop/src-tauri",
"crates/semble-core", "crates/semble-core",
"benchmarks/semble", "support/benchmarks/semble",
] ]
resolver = "2" resolver = "2"
+1 -1
View File
@@ -20,7 +20,7 @@ COPY apps/desktop/src-tauri/src/ apps/desktop/src-tauri/src/
COPY apps/desktop/src-tauri/capabilities/ apps/desktop/src-tauri/capabilities/ COPY apps/desktop/src-tauri/capabilities/ apps/desktop/src-tauri/capabilities/
COPY apps/desktop/src-tauri/icons/ apps/desktop/src-tauri/icons/ COPY apps/desktop/src-tauri/icons/ apps/desktop/src-tauri/icons/
COPY apps/desktop/src-tauri/tauri.conf.json apps/desktop/src-tauri/tauri.conf.json COPY apps/desktop/src-tauri/tauri.conf.json apps/desktop/src-tauri/tauri.conf.json
COPY scripts/cursor-proto/proto/ scripts/cursor-proto/proto/ COPY protocols/cursor/ protocols/cursor/
RUN --mount=type=cache,target=/usr/local/cargo/registry \ RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \ --mount=type=cache,target=/usr/local/cargo/git \
--mount=type=cache,target=/src/target \ --mount=type=cache,target=/src/target \
+1 -1
View File
@@ -35,7 +35,7 @@ $(LOCAL_TAURI_SIGNING_KEY):
@powershell -NoProfile -Command "New-Item -ItemType Directory -Force -Path '$(dir $@)' | Out-Null; & '$(CURDIR)/apps/desktop/node_modules/.bin/tauri.cmd' signer generate --ci --write-keys '$@'" @powershell -NoProfile -Command "New-Item -ItemType Directory -Force -Path '$(dir $@)' | Out-Null; & '$(CURDIR)/apps/desktop/node_modules/.bin/tauri.cmd' signer generate --ci --write-keys '$@'"
build-desktop: $(LOCAL_TAURI_SIGNING_KEY) build-desktop: $(LOCAL_TAURI_SIGNING_KEY)
@set "TAURI_SIGNING_PRIVATE_KEY=$(LOCAL_TAURI_SIGNING_KEY)" && set "TAURI_SIGNING_PRIVATE_KEY_PASSWORD=" && npm --prefix apps/desktop run tauri:build -- --bundles nsis @node -e "const { spawnSync } = require('node:child_process'); const result = spawnSync(process.execPath, ['node_modules/@tauri-apps/cli/tauri.js', 'build', '--bundles', 'nsis'], { cwd: 'apps/desktop', stdio: 'inherit', env: { ...process.env, TAURI_SIGNING_PRIVATE_KEY: process.argv[1], TAURI_SIGNING_PRIVATE_KEY_PASSWORD: '' } }); process.exit(result.status ?? 1)" "$(LOCAL_TAURI_SIGNING_KEY)"
else else
$(LOCAL_TAURI_SIGNING_KEY): $(LOCAL_TAURI_SIGNING_KEY):
@install -d -m 700 "$(dir $@)" @install -d -m 700 "$(dir $@)"
+26 -4
View File
@@ -120,13 +120,35 @@ API Key、模型配置和应用设置保存在本机。模型请求仍会发送
```text ```text
cursor-byok/ cursor-byok/
├── apps/ ├── apps/
│ ├── desktop/ # React、Vite、Tauri 桌面应用 │ ├── desktop/
│ │ ├── src/
│ │ │ ├── features/ # 首页、模型、调用记录与设置
│ │ │ ├── shell/ # 窗口框架、页面布局与广告外壳
│ │ │ ├── shared/ # UI、虚拟列表、状态、API 与平台能力
│ │ │ ├── i18n/ # 本地化运行时与语言目录
│ │ │ └── styles/ # 全局主题与排版令牌
│ │ └── src-tauri/ # Tauri 桌面生命周期
│ └── docs/ # Next.js 与 Fumadocs 中文/英文文档站 │ └── docs/ # Next.js 与 Fumadocs 中文/英文文档站
├── server/ # 本地服务、Cursor 请求处理和模型转发 ├── server/
│ ├── src/
│ │ ├── cursor/ # Cursor 协议适配
│ │ ├── run/ # 通用 Agent Runtime 与内部 Port
│ │ ├── provider/ # 模型供应商适配
│ │ ├── model/ # 聚合领域模型
│ │ ├── store/ # SQLite Repository
│ │ ├── control/ # 管理面 API
│ │ ├── harness/ # Cursor 本地集成
│ │ └── search/ # Web 与 Semble 搜索接入
│ ├── prompt/
│ └── migrations/
├── crates/ ├── crates/
│ └── semble-core/ # 本地代码索引与搜索核心库 │ └── semble-core/ # 本地代码索引与搜索核心库
├── cursor-proto/ # Cursor 协议定义 ├── protocols/
├── benchmarks/ # 代码搜索和索引基准测试 │ └── cursor/ # Cursor 协议定义的唯一来源
├── support/ # 仓库辅助设施
│ ├── cursor-protocol-extractor/ # Cursor 协议提取与 Go 代码生成工具
│ ├── cursor-capture/ # Cursor 协议抓取和调试工具
│ └── benchmarks/ # 代码搜索和索引基准测试
├── images/ # README 展示图片 ├── images/ # README 展示图片
├── Cargo.toml # Rust 工作区配置 ├── Cargo.toml # Rust 工作区配置
└── Makefile # 常用开发、检查和构建命令 └── Makefile # 常用开发、检查和构建命令
+2
View File
@@ -14,6 +14,7 @@ tauri-build = { version = "2", features = [] }
[dependencies] [dependencies]
axum = "0.8" axum = "0.8"
cursor-server = { path = "../../../server" } cursor-server = { path = "../../../server" }
rfd = "0.15"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
tauri = { version = "2", features = ["tray-icon"] } tauri = { version = "2", features = ["tray-icon"] }
@@ -26,5 +27,6 @@ tauri-plugin-updater = "2"
tokio = { version = "1", features = ["time"] } tokio = { version = "1", features = ["time"] }
tokio-util = "0.7" tokio-util = "0.7"
tracing = "0.1" tracing = "0.1"
tracing-appender = "0.2"
tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] }
url = "2" url = "2"
+28 -13
View File
@@ -1,5 +1,5 @@
use std::{ use std::{
process::Command, process::{Command, ExitCode},
sync::{ sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
Mutex, Mutex,
@@ -19,7 +19,6 @@ use tauri::{
}; };
use tauri_plugin_opener::OpenerExt; use tauri_plugin_opener::OpenerExt;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[cfg(dev)] #[cfg(dev)]
use cursor_server::config::ConsoleSource; use cursor_server::config::ConsoleSource;
@@ -27,6 +26,7 @@ use cursor_server::{App, Config, Result};
#[cfg(not(dev))] #[cfg(not(dev))]
use crate::frontend; use crate::frontend;
use crate::startup::{self, StartupDiagnostics};
use crate::tray; use crate::tray;
pub(crate) const MAIN_WINDOW_LABEL: &str = "main"; pub(crate) const MAIN_WINDOW_LABEL: &str = "main";
@@ -141,16 +141,23 @@ fn create_main_window(
builder.build() builder.build()
} }
pub fn run() { pub fn run() -> ExitCode {
let started_by_autostart = std::env::args_os().any(|arg| arg == AUTOSTART_ARG); let diagnostics = match StartupDiagnostics::initialize() {
Ok(diagnostics) => diagnostics,
Err(error) => {
startup::report_logging_failure(error.as_ref());
return ExitCode::FAILURE;
}
};
tracing::info!(
version = env!("CARGO_PKG_VERSION"),
os = std::env::consts::OS,
architecture = std::env::consts::ARCH,
log_directory = %diagnostics.log_directory().display(),
"desktop starting"
);
tracing_subscriber::registry() let started_by_autostart = std::env::args_os().any(|arg| arg == AUTOSTART_ARG);
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "cursor_server=info".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
let app = tauri::Builder::default() let app = tauri::Builder::default()
.invoke_handler(tauri::generate_handler![open_terminal_with_command]) .invoke_handler(tauri::generate_handler![open_terminal_with_command])
@@ -218,8 +225,14 @@ pub fn run() {
tray::create(app)?; tray::create(app)?;
Ok(()) Ok(())
}) })
.build(tauri::generate_context!()) .build(tauri::generate_context!());
.expect("failed to build Cursor BYOK desktop app"); let app = match app {
Ok(app) => app,
Err(error) => {
diagnostics.report_fatal(&error);
return ExitCode::FAILURE;
}
};
app.run(|app, event| match event { app.run(|app, event| match event {
RunEvent::WindowEvent { RunEvent::WindowEvent {
@@ -264,4 +277,6 @@ pub fn run() {
} => tray::show_main_window(app), } => tray::show_main_window(app),
_ => {} _ => {}
}); });
ExitCode::SUCCESS
} }
+1
View File
@@ -1,6 +1,7 @@
mod desktop; mod desktop;
#[cfg(not(dev))] #[cfg(not(dev))]
mod frontend; mod frontend;
mod startup;
mod tray; mod tray;
pub use desktop::run; pub use desktop::run;
+2 -2
View File
@@ -1,5 +1,5 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() { fn main() -> std::process::ExitCode {
cursor_byok_desktop::run(); cursor_byok_desktop::run()
} }
+139
View File
@@ -0,0 +1,139 @@
//! Captures startup diagnostics before the database and desktop runtime initialize.
use std::{error::Error, path::PathBuf};
use rfd::{MessageButtons, MessageDialog, MessageLevel};
use tracing_appender::{
non_blocking::WorkerGuard,
rolling::{RollingFileAppender, Rotation},
};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
const LOG_DIRECTORY_NAME: &str = "logs";
const LOG_FILE_PREFIX: &str = "cursor-byok";
const LOG_FILE_SUFFIX: &str = "log";
const RETAINED_LOG_FILES: usize = 15;
type BoxError = Box<dyn Error + Send + Sync>;
pub(crate) struct StartupDiagnostics {
log_directory: PathBuf,
_writer_guard: WorkerGuard,
}
impl StartupDiagnostics {
pub(crate) fn initialize() -> Result<Self, BoxError> {
let log_directory = cursor_server::config::managed_data_dir()?.join(LOG_DIRECTORY_NAME);
std::fs::create_dir_all(&log_directory)?;
let file_appender = RollingFileAppender::builder()
.rotation(Rotation::DAILY)
.filename_prefix(LOG_FILE_PREFIX)
.filename_suffix(LOG_FILE_SUFFIX)
.max_log_files(RETAINED_LOG_FILES)
.build(&log_directory)?;
let (file_writer, writer_guard) = tracing_appender::non_blocking(file_appender);
let filter = tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "cursor_byok_desktop=info,cursor_server=info".into());
tracing_subscriber::registry()
.with(filter)
.with(tracing_subscriber::fmt::layer())
.with(
tracing_subscriber::fmt::layer()
.with_ansi(false)
.with_writer(file_writer),
)
.try_init()?;
Ok(Self {
log_directory,
_writer_guard: writer_guard,
})
}
pub(crate) fn log_directory(&self) -> &std::path::Path {
&self.log_directory
}
pub(crate) fn report_fatal(&self, error: &(dyn Error + 'static)) {
let details = error_chain(error);
tracing::error!(
error = %details,
log_directory = %self.log_directory.display(),
"desktop failed to start"
);
show_fatal_dialog(&details, Some(&self.log_directory));
}
}
pub(crate) fn report_logging_failure(error: &(dyn Error + 'static)) {
let details = error_chain(error);
eprintln!("Cursor BYOK failed to initialize logging: {details}");
show_fatal_dialog(&details, None);
}
fn show_fatal_dialog(details: &str, log_directory: Option<&std::path::Path>) {
let log_guidance = match log_directory {
Some(directory) => format!(
"日志目录 / Log directory:\n{}\n\n请将最新的日志文件发送给开发者。\nPlease send the latest log file to the developer.",
directory.display()
),
None => "日志系统也未能启动,因此没有生成日志文件。\nLogging also failed to initialize, so no log file was created."
.to_owned(),
};
let description = format!(
"Cursor BYOK 无法启动 / failed to start.\n\n错误 / Error:\n{details}\n\n{log_guidance}"
);
let _ = MessageDialog::new()
.set_level(MessageLevel::Error)
.set_title("Cursor BYOK 启动失败 / Startup Error")
.set_description(description)
.set_buttons(MessageButtons::Ok)
.show();
}
fn error_chain(error: &(dyn Error + 'static)) -> String {
let mut details = error.to_string();
let mut source = error.source();
while let Some(cause) = source {
details.push_str("\nCaused by: ");
details.push_str(&cause.to_string());
source = cause.source();
}
details
}
#[cfg(test)]
mod tests {
use super::*;
use std::fmt;
#[derive(Debug)]
struct OuterError(std::io::Error);
impl fmt::Display for OuterError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("database initialization failed")
}
}
impl Error for OuterError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.0)
}
}
#[test]
fn fatal_report_includes_the_complete_error_chain() {
let error = OuterError(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"database file is read-only",
));
assert_eq!(
error_chain(&error),
"database initialization failed\nCaused by: database file is read-only"
);
}
}
+11 -11
View File
@@ -1,16 +1,16 @@
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import { HashRouter, Navigate, Route, Routes } from "react-router-dom"; import { HashRouter, Navigate, Route, Routes } from "react-router-dom";
import { MessageProvider } from "./components/ui/MessageProvider"; import { MessageProvider } from "./shared/ui/MessageProvider";
import { useMessage } from "./components/ui/message"; import { useMessage } from "./shared/ui/message";
import { AppFrame } from "./layouts/AppFrame"; import { AppFrame } from "./shell/AppFrame";
import { AppLayout } from "./layouts/AppLayout"; import { AppLayout } from "./shell/AppLayout";
import { CallsPage } from "./pages/CallsPage"; import { CallsPage } from "./features/calls/CallsPage";
import { CallDetailsPage } from "./pages/CallDetailsPage"; import { CallDetailsPage } from "./features/calls/CallDetailsPage";
import { CursorSettingsPage } from "./pages/CursorSettingsPage"; import { CursorSettingsPage } from "./features/models/CursorSettingsPage";
import { HomePage } from "./pages/HomePage"; import { HomePage } from "./features/home/HomePage";
import { SettingsPage } from "./pages/SettingsPage"; import { SettingsPage } from "./features/settings/SettingsPage";
import { useAppStore } from "./store/appStore"; import { useAppStore } from "./shared/store/appStore";
import { updateStore } from "./store/updateStore"; import { updateStore } from "./shared/store/updateStore";
export function App() { export function App() {
return ( return (
+1 -1
View File
@@ -8,7 +8,7 @@ import type {
ProxySettings, ProxySettings,
StatisticsStorage, StatisticsStorage,
TabSettings, TabSettings,
} from "../api"; } from "../shared/api";
const API_ROOT = "/__byok-api__/api"; const API_ROOT = "/__byok-api__/api";
const FIXED_NOW = Date.UTC(2026, 7, 27, 8, 0, 0); const FIXED_NOW = Date.UTC(2026, 7, 27, 8, 0, 0);
@@ -1,4 +1,4 @@
@use "../styles/typography" as type; @use "../../styles/typography" as type;
.root { .root {
section { min-width: 0; } section { min-width: 0; }
@@ -1,6 +1,6 @@
import type { CallDetail } from "../api"; import type { CallDetail } from "../../shared/api";
import { JsonEditor } from "./ui/JsonEditor"; import { JsonEditor } from "../../shared/ui/JsonEditor";
import { Tabs, type TabItem } from "./ui/Tabs"; import { Tabs, type TabItem } from "../../shared/ui/Tabs";
import styles from "./CallDetails.module.scss"; import styles from "./CallDetails.module.scss";
const show = (value: string | number | null) => value ?? "-"; const show = (value: string | number | null) => value ?? "-";
@@ -1,4 +1,4 @@
@use "../styles/typography" as type; @use "../../styles/typography" as type;
.root { .root {
width: 100%; width: 100%;
@@ -1,10 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { api, type CallDetail } from "../api"; import { api, type CallDetail } from "../../shared/api";
import { CallDetails } from "../components/CallDetails"; import { CallDetails } from "./CallDetails";
import { TitledCard } from "../components/ui/TitledCard"; import { TitledCard } from "../../shared/ui/TitledCard";
import styles from "./CallDetailsPage.module.scss"; import styles from "./CallDetailsPage.module.scss";
import { ScrollableContent } from "../components/virtual/ScrollableContent"; import { ScrollableContent } from "../../shared/virtual/ScrollableContent";
export function CallDetailsPage() { export function CallDetailsPage() {
const { callId = "" } = useParams(); const { callId = "" } = useParams();
@@ -1,4 +1,4 @@
@use "../styles/typography" as type; @use "../../styles/typography" as type;
.status { .status {
padding: 3px 7px; padding: 3px 7px;
@@ -1,9 +1,9 @@
import type { LlmCall } from "../api"; import type { LlmCall } from "../../shared/api";
import controls from "./ui/Controls.module.scss"; import controls from "../../shared/ui/Controls.module.scss";
import { DataTable, type DataTableColumn } from "./ui/DataTable"; import { DataTable, type DataTableColumn } from "../../shared/ui/DataTable";
import { Icon } from "./ui/Icon"; import { Icon } from "../../shared/ui/Icon";
import { TooltipTrigger } from "./ui/TooltipTrigger"; import { TooltipTrigger } from "../../shared/ui/TooltipTrigger";
import { eyeIcon } from "./ui/icons"; import { eyeIcon } from "../../shared/ui/icons";
import styles from "./CallTable.module.scss"; import styles from "./CallTable.module.scss";
const value = (input: string | number | null) => input ?? "-"; const value = (input: string | number | null) => input ?? "-";
@@ -1,7 +1,7 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { CallTable } from "../components/CallTable"; import { CallTable } from "./CallTable";
import { PageContent } from "../components/layout/PageContent"; import { PageContent } from "../../shell/layout/PageContent";
import { appStore, useAppStore } from "../store/appStore"; import { appStore, useAppStore } from "../../shared/store/appStore";
import styles from "./CallsPage.module.scss"; import styles from "./CallsPage.module.scss";
const CALL_REFRESH_INTERVAL_MS = 2_000; const CALL_REFRESH_INTERVAL_MS = 2_000;
@@ -1,15 +1,15 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { api, type Overview } from "../api"; import { api, type Overview } from "../../shared/api";
import { ContributionCalendarChart } from "../components/charts/ContributionCalendarChart"; import { ContributionCalendarChart } from "./charts/ContributionCalendarChart";
import { DailyTokenUsageChart } from "../components/charts/DailyTokenUsageChart"; import { DailyTokenUsageChart } from "./charts/DailyTokenUsageChart";
import { HomeMetrics } from "../components/metrics/HomeMetrics"; import { HomeMetrics } from "./metrics/HomeMetrics";
import { PageContent } from "../components/layout/PageContent"; import { PageContent } from "../../shell/layout/PageContent";
import type { VirtualPageSection } from "../components/layout/VirtualPage"; import type { VirtualPageSection } from "../../shell/layout/VirtualPage";
import { OverviewTimeRangeFilter, type OverviewRangePreset } from "../components/overview/OverviewTimeRangeFilter"; import { OverviewTimeRangeFilter, type OverviewRangePreset } from "./overview/OverviewTimeRangeFilter";
import { PageActions } from "../layouts/PageActions"; import { PageActions } from "../../shell/PageActions";
import { appStore, useAppStore } from "../store/appStore"; import { appStore, useAppStore } from "../../shared/store/appStore";
import { formatTimeInput, parseTimeInput } from "../utils/parseTimeInput"; import { formatTimeInput, parseTimeInput } from "../../shared/utils/parseTimeInput";
import { claudeIcon, openAiIcon } from "../components/ui/icons"; import { claudeIcon, openAiIcon } from "../../shared/ui/icons";
type TimeRange = { startMs: number; endMs: number }; type TimeRange = { startMs: number; endMs: number };
@@ -1,4 +1,4 @@
@use "../../styles/typography" as type; @use "../../../styles/typography" as type;
.root { .root {
width: 100%; width: 100%;
@@ -1,8 +1,8 @@
import { useLayoutEffect, useMemo, useRef, useState } from "react"; import { useLayoutEffect, useMemo, useRef, useState } from "react";
import { init, Rect, type ElementEvent } from "zrender"; import { init, Rect, type ElementEvent } from "zrender";
import type { Locale } from "../../i18n/runtime"; import type { Locale } from "../../../i18n/runtime";
import { useI18n } from "../../i18n/store"; import { useI18n } from "../../../i18n/store";
import { Tooltip, type TooltipAnchor } from "../ui/Tooltip"; import { Tooltip, type TooltipAnchor } from "../../../shared/ui/Tooltip";
import styles from "./ContributionCalendarChart.module.scss"; import styles from "./ContributionCalendarChart.module.scss";
export type ContributionDay = { export type ContributionDay = {
@@ -1,4 +1,4 @@
@use "../../styles/typography" as type; @use "../../../styles/typography" as type;
.root { .root {
--daily-token-tooltip-font-size: #{type.$font-size-2xs}; --daily-token-tooltip-font-size: #{type.$font-size-2xs};
@@ -1,7 +1,7 @@
import type { EChartsCoreOption } from "echarts/core"; import type { EChartsCoreOption } from "echarts/core";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import type { TokenUsageGranularity } from "../../api"; import type { TokenUsageGranularity } from "../../../shared/api";
import { formatCompactInteger } from "../../utils/numberFormat"; import { formatCompactInteger } from "../../../shared/utils/numberFormat";
import { EChart } from "./EChart"; import { EChart } from "./EChart";
import styles from "./DailyTokenUsageChart.module.scss"; import styles from "./DailyTokenUsageChart.module.scss";
@@ -1,5 +1,5 @@
import type { EChartsCoreOption } from "echarts/core"; import type { EChartsCoreOption } from "echarts/core";
import type { LlmCall } from "../../api"; import type { LlmCall } from "../../../shared/api";
import { EChart } from "./EChart"; import { EChart } from "./EChart";
export function LatencyChart({ calls }: { calls: LlmCall[] }) { export function LatencyChart({ calls }: { calls: LlmCall[] }) {
@@ -1,5 +1,5 @@
import type { EChartsCoreOption } from "echarts/core"; import type { EChartsCoreOption } from "echarts/core";
import type { LlmCall } from "../../api"; import type { LlmCall } from "../../../shared/api";
import { EChart } from "./EChart"; import { EChart } from "./EChart";
export function TokenTrendChart({ calls }: { calls: LlmCall[] }) { export function TokenTrendChart({ calls }: { calls: LlmCall[] }) {
@@ -1,4 +1,4 @@
@use "../../styles/typography" as type; @use "../../../styles/typography" as type;
.root { .root {
position: relative; position: relative;
@@ -1,4 +1,4 @@
@use "../../styles/typography" as type; @use "../../../styles/typography" as type;
.scroller { .scroller {
width: 100%; width: 100%;
@@ -1,8 +1,8 @@
import { useState } from "react"; import { useState } from "react";
import { formatCompactInteger, formatInteger } from "../../utils/numberFormat"; import { formatCompactInteger, formatInteger } from "../../../shared/utils/numberFormat";
import { Icon } from "../ui/Icon"; import { Icon } from "../../../shared/ui/Icon";
import { Tooltip, type TooltipAnchor } from "../ui/Tooltip"; import { Tooltip, type TooltipAnchor } from "../../../shared/ui/Tooltip";
import { informationOutlineIcon } from "../ui/icons"; import { informationOutlineIcon } from "../../../shared/ui/icons";
import { CacheHitRateChart } from "./CacheHitRateChart"; import { CacheHitRateChart } from "./CacheHitRateChart";
import styles from "./HomeMetrics.module.scss"; import styles from "./HomeMetrics.module.scss";
@@ -1,4 +1,4 @@
@use "../../styles/typography" as type; @use "../../../styles/typography" as type;
.root, .root,
.presets { .presets {
@@ -1,12 +1,12 @@
import { autoUpdate, computePosition, flip, offset, shift, size } from "@floating-ui/dom"; import { autoUpdate, computePosition, flip, offset, shift, size } from "@floating-ui/dom";
import { useEffect, useId, useLayoutEffect, useRef, useState } from "react"; import { useEffect, useId, useLayoutEffect, useRef, useState } from "react";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { parseTimeInput } from "../../utils/parseTimeInput"; import { parseTimeInput } from "../../../shared/utils/parseTimeInput";
import controls from "../ui/Controls.module.scss"; import controls from "../../../shared/ui/Controls.module.scss";
import { Icon } from "../ui/Icon"; import { Icon } from "../../../shared/ui/Icon";
import { MultiSelect, type MultiSelectOption } from "../ui/MultiSelect"; import { MultiSelect, type MultiSelectOption } from "../../../shared/ui/MultiSelect";
import { TooltipTrigger } from "../ui/TooltipTrigger"; import { TooltipTrigger } from "../../../shared/ui/TooltipTrigger";
import { refreshIcon } from "../ui/icons"; import { refreshIcon } from "../../../shared/ui/icons";
import styles from "./OverviewTimeRangeFilter.module.scss"; import styles from "./OverviewTimeRangeFilter.module.scss";
export type OverviewRangePreset = "ten-minutes" | "hour" | "today" | "week" | "month" | "custom"; export type OverviewRangePreset = "ten-minutes" | "hour" | "today" | "week" | "month" | "custom";
@@ -1,6 +1,6 @@
import { createContext, useContext, type ReactNode } from "react"; import { createContext, useContext, type ReactNode } from "react";
import { useAppStore } from "../../store/appStore"; import { useAppStore } from "../../shared/store/appStore";
import controls from "../ui/Controls.module.scss"; import controls from "../../shared/ui/Controls.module.scss";
import styles from "./CursorSettings.module.scss"; import styles from "./CursorSettings.module.scss";
const CaReady = createContext(false); const CaReady = createContext(false);
@@ -1,11 +1,11 @@
import type { IconifyIcon } from "@iconify/react/offline"; import type { IconifyIcon } from "@iconify/react/offline";
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import Sortable from "sortablejs"; import Sortable from "sortablejs";
import type { Model } from "../../api"; import type { Model } from "../../shared/api";
import { Button } from "../ui/Button"; import { Button } from "../../shared/ui/Button";
import { Card } from "../ui/Card"; import { Card } from "../../shared/ui/Card";
import { Icon } from "../ui/Icon"; import { Icon } from "../../shared/ui/Icon";
import { claudeIcon, dragIcon, flatColorOrganizationIcon, openAiIcon } from "../ui/icons"; import { claudeIcon, dragIcon, flatColorOrganizationIcon, openAiIcon } from "../../shared/ui/icons";
import { CursorModelTestResult, type CursorModelTestState } from "./CursorModelTestResult"; import { CursorModelTestResult, type CursorModelTestState } from "./CursorModelTestResult";
import styles from "./CursorSettings.module.scss"; import styles from "./CursorSettings.module.scss";
@@ -1,13 +1,13 @@
import type { ModelInput, ModelType } from "../../api"; import type { ModelInput, ModelType } from "../../shared/api";
import { defaultCustomHeadersText } from "../../utils/modelDefaults"; import { defaultCustomHeadersText } from "../../shared/utils/modelDefaults";
import { modelPresets, presetEndpoint, trimTrailingSlash, type ModelPreset } from "../../utils/modelPresets"; import { modelPresets, presetEndpoint, trimTrailingSlash, type ModelPreset } from "../../shared/utils/modelPresets";
import { Button } from "../ui/Button"; import { Button } from "../../shared/ui/Button";
import { Checkbox } from "../ui/Checkbox"; import { Checkbox } from "../../shared/ui/Checkbox";
import { FormField, SecretTextInput, TextInput } from "../ui/FormControls"; import { FormField, SecretTextInput, TextInput } from "../../shared/ui/FormControls";
import { JsonEditor } from "../ui/JsonEditor"; import { JsonEditor } from "../../shared/ui/JsonEditor";
import { Combobox, Select } from "../ui/Select"; import { Combobox, Select } from "../../shared/ui/Select";
import { Switch } from "../ui/Switch"; import { Switch } from "../../shared/ui/Switch";
import { claudeIcon, openAiIcon } from "../ui/icons"; import { claudeIcon, openAiIcon } from "../../shared/ui/icons";
import { CursorPresetChips } from "./CursorPresetChips"; import { CursorPresetChips } from "./CursorPresetChips";
import styles from "./CursorSettings.module.scss"; import styles from "./CursorSettings.module.scss";
@@ -1,7 +1,7 @@
import type { ModelConnectivityResult } from "../../api"; import type { ModelConnectivityResult } from "../../shared/api";
import { Icon } from "../ui/Icon"; import { Icon } from "../../shared/ui/Icon";
import { TooltipTrigger } from "../ui/TooltipTrigger"; import { TooltipTrigger } from "../../shared/ui/TooltipTrigger";
import { informationOutlineIcon } from "../ui/icons"; import { informationOutlineIcon } from "../../shared/ui/icons";
import styles from "./CursorModelTestResult.module.scss"; import styles from "./CursorModelTestResult.module.scss";
export type CursorModelTestState = export type CursorModelTestState =
@@ -1,5 +1,5 @@
import type { ModelType } from "../../api"; import type { ModelType } from "../../shared/api";
import { modelPresets, presetEndpoint, trimTrailingSlash, type ModelPreset } from "../../utils/modelPresets"; import { modelPresets, presetEndpoint, trimTrailingSlash, type ModelPreset } from "../../shared/utils/modelPresets";
import styles from "./CursorPresetChips.module.scss"; import styles from "./CursorPresetChips.module.scss";
/** 常用服务商预设:点击按当前协议类型自动填充对应端点与默认模型 */ /** 常用服务商预设:点击按当前协议类型自动填充对应端点与默认模型 */
@@ -1,21 +1,21 @@
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { api, type Model, type ModelInput } from "../api"; import { api, type Model, type ModelInput } from "../../shared/api";
import { CursorCaGate, CursorCaProvider, CursorModelGate, CursorModelProvider } from "../components/cursor/CursorGates"; import { CursorCaGate, CursorCaProvider, CursorModelGate, CursorModelProvider } from "./CursorGates";
import { CursorModelCards, cursorModelGroups, type CursorModelGrouping } from "../components/cursor/CursorModelCards"; import { CursorModelCards, cursorModelGroups, type CursorModelGrouping } from "./CursorModelCards";
import { CursorModelEditor, emptyCursorModelDraft, type CursorModelDraft } from "../components/cursor/CursorModelEditor"; import { CursorModelEditor, emptyCursorModelDraft, type CursorModelDraft } from "./CursorModelEditor";
import { CursorModelTestResult, type CursorModelTestState } from "../components/cursor/CursorModelTestResult"; import { CursorModelTestResult, type CursorModelTestState } from "./CursorModelTestResult";
import styles from "../components/cursor/CursorSettings.module.scss"; import styles from "./CursorSettings.module.scss";
import { PageContent } from "../components/layout/PageContent"; import { PageContent } from "../../shell/layout/PageContent";
import { LegacyModelImport } from "../components/models/LegacyModelImport"; import { LegacyModelImport } from "./LegacyModelImport";
import { ConfirmDialog } from "../components/ui/ConfirmDialog"; import { ConfirmDialog } from "../../shared/ui/ConfirmDialog";
import controls from "../components/ui/Controls.module.scss"; import controls from "../../shared/ui/Controls.module.scss";
import { Icon } from "../components/ui/Icon"; import { Icon } from "../../shared/ui/Icon";
import { Modal } from "../components/ui/Modal"; import { Modal } from "../../shared/ui/Modal";
import { TooltipTrigger } from "../components/ui/TooltipTrigger"; import { TooltipTrigger } from "../../shared/ui/TooltipTrigger";
import { addIcon } from "../components/ui/icons"; import { addIcon } from "../../shared/ui/icons";
import { useMessage } from "../components/ui/message"; import { useMessage } from "../../shared/ui/message";
import { PageActions } from "../layouts/PageActions"; import { PageActions } from "../../shell/PageActions";
import { appStore, useAppStore } from "../store/appStore"; import { appStore, useAppStore } from "../../shared/store/appStore";
export function CursorSettingsPage() { export function CursorSettingsPage() {
const { models, cursorHarness, cursorBusy } = useAppStore(); const { models, cursorHarness, cursorBusy } = useAppStore();
@@ -1,8 +1,8 @@
import { useState, type ReactNode } from "react"; import { useState, type ReactNode } from "react";
import { api, type LegacyModelImportPreview } from "../../api"; import { api, type LegacyModelImportPreview } from "../../shared/api";
import { appStore } from "../../store/appStore"; import { appStore } from "../../shared/store/appStore";
import { ConfirmDialog } from "../ui/ConfirmDialog"; import { ConfirmDialog } from "../../shared/ui/ConfirmDialog";
import { useMessage } from "../ui/message"; import { useMessage } from "../../shared/ui/message";
import styles from "./LegacyModelImport.module.scss"; import styles from "./LegacyModelImport.module.scss";
type LegacyModelImportControl = { type LegacyModelImportControl = {
@@ -8,12 +8,12 @@ import {
writeAutostart, writeAutostart,
writeDockIconVisibility, writeDockIconVisibility,
writeSilentStart, writeSilentStart,
} from "../../native/appLifecycle"; } from "../../shared/native/appLifecycle";
import { updateStore, useUpdateStore } from "../../store/updateStore"; import { updateStore, useUpdateStore } from "../../shared/store/updateStore";
import { Button } from "../ui/Button"; import { Button } from "../../shared/ui/Button";
import { Switch } from "../ui/Switch"; import { Switch } from "../../shared/ui/Switch";
import { TitledCard } from "../ui/TitledCard"; import { TitledCard } from "../../shared/ui/TitledCard";
import { useMessage } from "../ui/message"; import { useMessage } from "../../shared/ui/message";
import styles from "./AppLifecycleSettingsCard.module.scss"; import styles from "./AppLifecycleSettingsCard.module.scss";
export function AppLifecycleSettingsCard() { export function AppLifecycleSettingsCard() {
@@ -1,9 +1,9 @@
import type { ProxySettings, ProxySettingsInput } from "../../api"; import type { ProxySettings, ProxySettingsInput } from "../../shared/api";
import { Button } from "../ui/Button"; import { Button } from "../../shared/ui/Button";
import { Checkbox } from "../ui/Checkbox"; import { Checkbox } from "../../shared/ui/Checkbox";
import { TextInput } from "../ui/FormControls"; import { TextInput } from "../../shared/ui/FormControls";
import { Select } from "../ui/Select"; import { Select } from "../../shared/ui/Select";
import { TitledCard } from "../ui/TitledCard"; import { TitledCard } from "../../shared/ui/TitledCard";
import styles from "./ProxySettingsCard.module.scss"; import styles from "./ProxySettingsCard.module.scss";
export function ProxySettingsCard({ export function ProxySettingsCard({
@@ -1,4 +1,4 @@
@use "../styles/typography" as type; @use "../../styles/typography" as type;
.page { .page {
display: grid; display: grid;
@@ -41,6 +41,15 @@
} }
} }
.textButton {
padding: 4px;
color: var(--vscode-textLink-foreground);
background: transparent;
border: 0;
&:hover { color: var(--vscode-textLink-activeForeground); }
}
.importRow { .importRow {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -59,15 +68,6 @@
} }
} }
.textButton {
padding: 4px;
color: var(--vscode-textLink-foreground);
background: transparent;
border: 0;
&:hover { color: var(--vscode-textLink-activeForeground); }
}
.confirmContent { .confirmContent {
display: grid; display: grid;
gap: 8px; gap: 8px;
@@ -155,6 +155,7 @@
align-items: stretch; align-items: stretch;
flex-direction: column; flex-direction: column;
} }
} }
.themeActions .selected { .themeActions .selected {
@@ -1,20 +1,20 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { api, type ProxySettings, type ProxySettingsInput, type StatisticsStorage, type StatisticsStorageScope, type TabSettings } from "../api"; import { api, type ProxySettings, type ProxySettingsInput, type StatisticsStorage, type StatisticsStorageScope, type TabSettings } from "../../shared/api";
import { PageContent } from "../components/layout/PageContent"; import { PageContent } from "../../shell/layout/PageContent";
import { LegacyModelImport } from "../components/models/LegacyModelImport"; import { LegacyModelImport } from "../models/LegacyModelImport";
import { AppLifecycleSettingsCard } from "../components/settings/AppLifecycleSettingsCard"; import { AppLifecycleSettingsCard } from "./AppLifecycleSettingsCard";
import { ProxySettingsCard } from "../components/settings/ProxySettingsCard"; import { ProxySettingsCard } from "./ProxySettingsCard";
import { TabSettingsCard } from "../components/settings/TabSettingsCard"; import { TabSettingsCard } from "./TabSettingsCard";
import { Button } from "../components/ui/Button"; import { Button } from "../../shared/ui/Button";
import { Checkbox } from "../components/ui/Checkbox"; import { Checkbox } from "../../shared/ui/Checkbox";
import { ConfirmDialog } from "../components/ui/ConfirmDialog"; import { ConfirmDialog } from "../../shared/ui/ConfirmDialog";
import { FormField, TextInput } from "../components/ui/FormControls"; import { FormField, TextInput } from "../../shared/ui/FormControls";
import { Select } from "../components/ui/Select"; import { Select } from "../../shared/ui/Select";
import { TitledCard } from "../components/ui/TitledCard"; import { TitledCard } from "../../shared/ui/TitledCard";
import { setLocalePreference, useI18n, type LocalePreference } from "../i18n/store"; import { setLocalePreference, useI18n, type LocalePreference } from "../../i18n/store";
import { useMessage } from "../components/ui/message"; import { useMessage } from "../../shared/ui/message";
import { appStore, useAppStore } from "../store/appStore"; import { appStore, useAppStore } from "../../shared/store/appStore";
import { themeOptions } from "../theme/theme"; import { themeOptions } from "../../shared/theme/theme";
import styles from "./SettingsPage.module.scss"; import styles from "./SettingsPage.module.scss";
export function SettingsPage() { export function SettingsPage() {
@@ -1,10 +1,10 @@
import cursorIconUrl from "../../assets/icons/cursor.svg"; import cursorIconUrl from "../../shared/assets/icons/cursor.svg";
import type { TabMode, TabSettings } from "../../api"; import type { TabMode, TabSettings } from "../../shared/api";
import { Button } from "../ui/Button"; import { Button } from "../../shared/ui/Button";
import { TextInput } from "../ui/FormControls"; import { TextInput } from "../../shared/ui/FormControls";
import { Icon } from "../ui/Icon"; import { Icon } from "../../shared/ui/Icon";
import { Select } from "../ui/Select"; import { Select } from "../../shared/ui/Select";
import { TitledCard } from "../ui/TitledCard"; import { TitledCard } from "../../shared/ui/TitledCard";
import styles from "./TabSettingsCard.module.scss"; import styles from "./TabSettingsCard.module.scss";
export function TabSettingsCard({ export function TabSettingsCard({
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -2,8 +2,8 @@ import ReactDOM from "react-dom/client";
import "../node_modules/monaco-editor/min/vs/editor/editor.main.css"; import "../node_modules/monaco-editor/min/vs/editor/editor.main.css";
import { I18nRoot } from "./i18n/I18nRoot"; import { I18nRoot } from "./i18n/I18nRoot";
import { initializeI18n } from "./i18n/store"; import { initializeI18n } from "./i18n/store";
import { appStore } from "./store/appStore"; import { appStore } from "./shared/store/appStore";
import { applyTheme } from "./theme/theme"; import { applyTheme } from "./shared/theme/theme";
import "./styles/globals.scss"; import "./styles/globals.scss";
initializeI18n(); initializeI18n();
@@ -1,5 +1,5 @@
import type { AdRuntime } from "./components/ads/types"; import type { AdRuntime } from "../shell/ads/types";
import type { Locale } from "./i18n/runtime"; import type { Locale } from "../i18n/runtime";
export type ModelType = "openai" | "anthropic"; export type ModelType = "openai" | "anthropic";

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before

Width:  |  Height:  |  Size: 711 B

After

Width:  |  Height:  |  Size: 711 B

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Some files were not shown because too many files have changed in this diff Show More