From 8ed231092256ff6297e97f764939e1b3ad20100f Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Thu, 4 Jun 2026 11:14:09 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20AI=20=E5=AF=B9=E8=AF=9D=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=E5=88=B0=E6=89=A7=E8=A1=8C=E8=AE=B0=E5=BD=95=EF=BC=8C?= =?UTF-8?q?=E6=9C=AA=E6=89=A7=E8=A1=8C=E4=B8=BA=E4=B8=B4=E6=97=B6=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D=E4=B8=8D=E4=BF=9D=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/ai_history.rs | 91 ++++++++++---------- src-tauri/src/execution.rs | 42 +++++----- src-tauri/src/main.rs | 6 +- src-tauri/src/plugins/mod.rs | 3 + src/App.vue | 3 +- src/components/AiAssistant.vue | 123 ++++++++++------------------ src/composables/useAiHistory.ts | 55 ++++--------- src/composables/useCodeExecution.ts | 6 ++ src/types/app.ts | 1 + 9 files changed, 137 insertions(+), 193 deletions(-) diff --git a/src-tauri/src/ai_history.rs b/src-tauri/src/ai_history.rs index 61e59aa..916c209 100644 --- a/src-tauri/src/ai_history.rs +++ b/src-tauri/src/ai_history.rs @@ -1,17 +1,10 @@ use crate::execution::get_codeforge_db_path; use rusqlite::{Connection, params}; -use serde::Serialize; use std::sync::Mutex as StdMutex; use tauri::State; -#[derive(Serialize)] -pub struct AiConversationMeta { - pub id: String, - pub title: String, - pub updated_at: i64, -} - -/// AI 对话历史,存于与执行历史相同的 codeforge.sqlite 库 +/// AI 对话历史,绑定到某次执行记录(execution_id),存于同一个 codeforge.sqlite 库。 +/// 未关联执行的对话属临时会话,不落库。 pub struct AiHistory { conn: StdMutex, } @@ -20,12 +13,12 @@ impl AiHistory { pub fn new() -> Result { let db_path = get_codeforge_db_path()?; let conn = Connection::open(&db_path).map_err(|e| format!("打开数据库失败: {}", e))?; - // 并发读写更稳 let _ = conn.pragma_update(None, "journal_mode", "WAL"); + // 清理早期错误结构的旧表 + let _ = conn.execute("DROP TABLE IF EXISTS ai_conversations", []); conn.execute( - "CREATE TABLE IF NOT EXISTS ai_conversations ( - id TEXT PRIMARY KEY, - title TEXT NOT NULL, + "CREATE TABLE IF NOT EXISTS ai_execution_chats ( + execution_id INTEGER PRIMARY KEY, messages TEXT NOT NULL, updated_at INTEGER NOT NULL )", @@ -39,10 +32,10 @@ impl AiHistory { } } +/// 保存/更新某次执行对应的 AI 对话 #[tauri::command] pub async fn save_ai_conversation( - id: String, - title: String, + execution_id: i64, messages: String, updated_at: i64, history: State<'_, AiHistory>, @@ -52,67 +45,67 @@ pub async fn save_ai_conversation( .lock() .map_err(|_| "数据库锁错误".to_string())?; conn.execute( - "INSERT INTO ai_conversations (id, title, messages, updated_at) - VALUES (?1, ?2, ?3, ?4) - ON CONFLICT(id) DO UPDATE SET title=?2, messages=?3, updated_at=?4", - params![id, title, messages, updated_at], + "INSERT INTO ai_execution_chats (execution_id, messages, updated_at) + VALUES (?1, ?2, ?3) + ON CONFLICT(execution_id) DO UPDATE SET messages=?2, updated_at=?3", + params![execution_id, messages, updated_at], ) .map_err(|e| format!("保存 AI 对话失败: {}", e))?; Ok(()) } +/// 读取某次执行的 AI 对话(messages JSON);无则返回空串 #[tauri::command] -pub async fn list_ai_conversations( +pub async fn get_ai_conversation( + execution_id: i64, history: State<'_, AiHistory>, -) -> Result, String> { +) -> Result { let conn = history .conn .lock() .map_err(|_| "数据库锁错误".to_string())?; - let mut stmt = conn - .prepare("SELECT id, title, updated_at FROM ai_conversations ORDER BY updated_at DESC") - .map_err(|e| format!("读取 AI 对话失败: {}", e))?; - let rows = stmt - .query_map([], |row| { - Ok(AiConversationMeta { - id: row.get(0)?, - title: row.get(1)?, - updated_at: row.get(2)?, - }) - }) - .map_err(|e| format!("读取 AI 对话失败: {}", e))?; - rows.collect::, _>>() - .map_err(|e| format!("读取 AI 对话失败: {}", e)) + let result = conn.query_row( + "SELECT messages FROM ai_execution_chats WHERE execution_id = ?1", + params![execution_id], + |row| row.get::<_, String>(0), + ); + match result { + Ok(s) => Ok(s), + Err(rusqlite::Error::QueryReturnedNoRows) => Ok(String::new()), + Err(e) => Err(format!("读取 AI 对话失败: {}", e)), + } } -/// 返回该会话的 messages JSON 字符串 +/// 返回所有有 AI 对话的执行 id(供历史面板标记) #[tauri::command] -pub async fn get_ai_conversation( - id: String, - history: State<'_, AiHistory>, -) -> Result { +pub async fn list_ai_conversation_ids(history: State<'_, AiHistory>) -> Result, String> { let conn = history .conn .lock() .map_err(|_| "数据库锁错误".to_string())?; - conn.query_row( - "SELECT messages FROM ai_conversations WHERE id = ?1", - params![id], - |row| row.get::<_, String>(0), - ) - .map_err(|e| format!("读取 AI 对话失败: {}", e)) + let mut stmt = conn + .prepare("SELECT execution_id FROM ai_execution_chats") + .map_err(|e| format!("读取失败: {}", e))?; + let rows = stmt + .query_map([], |row| row.get::<_, i64>(0)) + .map_err(|e| format!("读取失败: {}", e))?; + rows.collect::, _>>() + .map_err(|e| format!("读取失败: {}", e)) } #[tauri::command] pub async fn delete_ai_conversation( - id: String, + execution_id: i64, history: State<'_, AiHistory>, ) -> Result<(), String> { let conn = history .conn .lock() .map_err(|_| "数据库锁错误".to_string())?; - conn.execute("DELETE FROM ai_conversations WHERE id = ?1", params![id]) - .map_err(|e| format!("删除 AI 对话失败: {}", e))?; + conn.execute( + "DELETE FROM ai_execution_chats WHERE execution_id = ?1", + params![execution_id], + ) + .map_err(|e| format!("删除 AI 对话失败: {}", e))?; Ok(()) } diff --git a/src-tauri/src/execution.rs b/src-tauri/src/execution.rs index 333ba73..54fa985 100644 --- a/src-tauri/src/execution.rs +++ b/src-tauri/src/execution.rs @@ -61,7 +61,7 @@ impl ExecutionHistory { }) } - fn insert(&self, result: &ExecutionResult) -> Result<(), String> { + fn insert(&self, result: &ExecutionResult) -> Result { let conn = self .conn .lock() @@ -83,7 +83,7 @@ impl ExecutionHistory { ) .map_err(|e| format!("保存执行历史失败: {}", e))?; - Ok(()) + Ok(conn.last_insert_rowid()) } fn list(&self) -> Result, String> { @@ -93,7 +93,7 @@ impl ExecutionHistory { .map_err(|_| "执行历史数据库锁错误".to_string())?; let mut statement = conn .prepare( - "SELECT success, code, stdout, stderr, execution_time, timestamp, language + "SELECT id, success, code, stdout, stderr, execution_time, timestamp, language FROM execution_history ORDER BY id ASC", ) @@ -102,13 +102,14 @@ impl ExecutionHistory { let rows = statement .query_map([], |row| { Ok(ExecutionResult { - success: row.get::<_, i64>(0)? != 0, - code: row.get(1)?, - stdout: row.get(2)?, - stderr: row.get(3)?, - execution_time: row.get::<_, i64>(4)? as u128, - timestamp: row.get::<_, i64>(5)? as u64, - language: row.get(6)?, + id: Some(row.get::<_, i64>(0)?), + success: row.get::<_, i64>(1)? != 0, + code: row.get(2)?, + stdout: row.get(3)?, + stderr: row.get(4)?, + execution_time: row.get::<_, i64>(5)? as u128, + timestamp: row.get::<_, i64>(6)? as u64, + language: row.get(7)?, }) }) .map_err(|e| format!("读取执行历史失败: {}", e))?; @@ -132,7 +133,7 @@ impl ExecutionHistory { let mut statement = conn .prepare( - "SELECT success, code, stdout, stderr, execution_time, timestamp, language + "SELECT id, success, code, stdout, stderr, execution_time, timestamp, language FROM execution_history ORDER BY id DESC LIMIT ?1 OFFSET ?2", @@ -142,13 +143,14 @@ impl ExecutionHistory { let rows = statement .query_map(params![limit as i64, offset as i64], |row| { Ok(ExecutionResult { - success: row.get::<_, i64>(0)? != 0, - code: row.get(1)?, - stdout: row.get(2)?, - stderr: row.get(3)?, - execution_time: row.get::<_, i64>(4)? as u128, - timestamp: row.get::<_, i64>(5)? as u64, - language: row.get(6)?, + id: Some(row.get::<_, i64>(0)?), + success: row.get::<_, i64>(1)? != 0, + code: row.get(2)?, + stdout: row.get(3)?, + stderr: row.get(4)?, + execution_time: row.get::<_, i64>(5)? as u128, + timestamp: row.get::<_, i64>(6)? as u64, + language: row.get(7)?, }) }) .map_err(|e| format!("读取执行历史失败: {}", e))?; @@ -575,6 +577,7 @@ pub async fn execute_code( } let mut result = ExecutionResult { + id: None, success: status.success(), code: request.code.clone(), stdout: stdout_lines.join("\n"), @@ -612,7 +615,8 @@ pub async fn execute_code( ); drop(manager); - history.insert(&result)?; + let row_id = history.insert(&result)?; + result.id = Some(row_id); info!("执行代码 -> 调用插件 [ {} ] 完成", request.language); return Ok(result); diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 64b8122..39bb953 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -24,7 +24,7 @@ mod utils; use crate::ai::{ai_chat, ai_chat_stream}; use crate::ai_history::{ - AiHistory, delete_ai_conversation, get_ai_conversation, list_ai_conversations, + AiHistory, delete_ai_conversation, get_ai_conversation, list_ai_conversation_ids, save_ai_conversation, }; use crate::cache::{clear_all_cache, clear_plugins_cache, get_cache_info}; @@ -181,9 +181,9 @@ fn main() { // AI 助手 ai_chat, ai_chat_stream, - // AI 对话历史 + // AI 对话历史(绑定执行记录) save_ai_conversation, - list_ai_conversations, + list_ai_conversation_ids, get_ai_conversation, delete_ai_conversation ]) diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs index 99044cc..4853b22 100644 --- a/src-tauri/src/plugins/mod.rs +++ b/src-tauri/src/plugins/mod.rs @@ -7,6 +7,9 @@ use std::path::PathBuf; // 通用结构定义 #[derive(Debug, Serialize, Deserialize, Clone)] pub struct ExecutionResult { + // 执行历史记录 id(运行后由后端填充;用于关联 AI 对话) + #[serde(default)] + pub id: Option, pub success: bool, pub code: String, pub stdout: String, diff --git a/src/App.vue b/src/App.vue index 01caea5..5f67aa5 100644 --- a/src/App.vue +++ b/src/App.vue @@ -186,7 +186,7 @@ - + {{ active.model }}
- -
- -
-
暂无历史对话
-
-
-
{{ c.title }}
-
{{ formatTime(c.updated_at) }}
-
- -
+ +
+ {{ executionId != null ? `已关联运行 #${executionId},对话随该次运行保存` : '临时会话:运行代码后对话才会保存' }}
@@ -73,10 +58,10 @@ diff --git a/src/composables/useShortcuts.ts b/src/composables/useShortcuts.ts index b236fc0..09e54b9 100644 --- a/src/composables/useShortcuts.ts +++ b/src/composables/useShortcuts.ts @@ -14,6 +14,7 @@ export const SHORTCUT_ACTIONS: ShortcutAction[] = [ {id: 'saveAs', label: '另存为', default: 'Mod+Shift+S'}, {id: 'open', label: '打开文件', default: 'Mod+O'}, {id: 'quickOpen', label: '快速打开文件', default: 'Mod+P'}, + {id: 'generate', label: 'AI 生成代码', default: 'Mod+K'}, {id: 'newTab', label: '新建标签', default: 'Mod+N'}, {id: 'closeTab', label: '关闭标签', default: 'Mod+W'}, {id: 'toggleSidebar', label: '切换侧栏', default: 'Mod+B'} From 222be4f4a5b27e482d2078980fb210f35b543473 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Thu, 4 Jun 2026 14:55:32 +0800 Subject: [PATCH 7/8] =?UTF-8?q?feat:=20=E8=87=AA=E7=84=B6=E8=AF=AD?= =?UTF-8?q?=E8=A8=80=E7=94=9F=E6=88=90=E6=94=B9=E4=B8=BA=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E5=99=A8=E5=86=85=E6=B5=AE=E5=B1=82=EF=BC=8C=E7=BB=93=E6=9E=9C?= =?UTF-8?q?=E5=8F=AF=E7=BC=96=E8=BE=91=E7=A1=AE=E8=AE=A4=E5=90=8E=E5=86=8D?= =?UTF-8?q?=E6=8F=92=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 5 +-- src/components/InlineGenerate.vue | 74 ++++++++++++++++++------------- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/src/App.vue b/src/App.vue index 0fc0ed5..be596d6 100644 --- a/src/App.vue +++ b/src/App.vue @@ -86,6 +86,7 @@ :line-count="viewerFile.lineCount" :size-bytes="viewerFile.sizeBytes" @close="closeViewer"/> +
@@ -149,6 +150,7 @@ :line-count="viewerFile.lineCount" :size-bytes="viewerFile.sizeBytes" @close="closeViewer"/> + @@ -186,9 +188,6 @@ - - - diff --git a/src/components/InlineGenerate.vue b/src/components/InlineGenerate.vue index bfbc9b3..1275cc9 100644 --- a/src/components/InlineGenerate.vue +++ b/src/components/InlineGenerate.vue @@ -1,27 +1,41 @@ @@ -41,11 +55,11 @@ const {active, reload} = useAiConfig() const prompt = ref('') const loading = ref(false) +const result = ref(null) const inputRef = ref(null) onMounted(() => inputRef.value?.focus()) -// 去掉可能的 Markdown 代码围栏 const stripFences = (text: string) => { const trimmed = text.trim() const m = trimmed.match(/^```[\w+-]*\n([\s\S]*?)\n?```$/) @@ -74,14 +88,7 @@ const generate = async () => { system: `你是代码生成助手。根据需求生成 ${props.language} 代码。只输出代码本身,不要任何解释,不要使用 Markdown 代码块标记。`, messages: [{role: 'user', content}] }) - const codeText = stripFences(reply) - if (codeText) { - emit('insert', codeText) - emit('close') - } - else { - toast.error('未生成内容') - } + result.value = stripFences(reply) } catch (error) { toast.error('生成失败: ' + error) @@ -90,4 +97,11 @@ const generate = async () => { loading.value = false } } + +const confirm = () => { + if (result.value && result.value.trim()) { + emit('insert', result.value) + emit('close') + } +} From dccaa80c6d857609eab1dc86e73a091228574fd7 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Thu, 4 Jun 2026 15:25:02 +0800 Subject: [PATCH 8/8] =?UTF-8?q?chore=20(released):=20=E5=8F=91=E5=B8=83=20?= =?UTF-8?q?26.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/content/release/26.0.0.md | 128 +++++++++++++++++++++++++++++++++ docs/pageforge.yaml | 3 +- 2 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 docs/content/release/26.0.0.md diff --git a/docs/content/release/26.0.0.md b/docs/content/release/26.0.0.md new file mode 100644 index 0000000..8986f26 --- /dev/null +++ b/docs/content/release/26.0.0.md @@ -0,0 +1,128 @@ +--- +title: 26.0.0 +--- + +CodeForge v26.0.0 是一次里程碑式的更新:CodeForge 从"按语言运行代码片段"的工具,进化为以**文件 / 项目为中心**的轻量编辑器,并首次内置 **AI 助手**。本次更新带来多标签编辑、文件树侧栏、就地运行、超大文件只读查看、可自定义快捷键,以及接入 Claude / OpenAI / DeepSeek 的 AI 对话与代码生成能力。 + +CodeForge v26.0.0 is a milestone release: CodeForge evolves from a "run code snippets by language" tool into a lightweight, **file/project-centric** editor, with a built-in **AI assistant** for the first time. This update brings multi-tab editing, a file-tree sidebar, in-place execution, a read-only viewer for huge files, customizable shortcuts, and AI chat & code generation powered by Claude / OpenAI / DeepSeek. + +--- + +## 📦 版本信息 | Release Information + +- **项目地址 | Repository**:https://github.com/devlive-community/codeforge +- **官方网站 | Official Website**:https://codeforge.devlive.org/ +- **版本号 | Version**:v26.0.0 +- **发布日期 | Release Date**:2026年6月4日 | June 4, 2026 + +--- + +## 🗂 文件与项目工作区 | Files & Project Workspace + +CodeForge 现在可以像编辑器一样打开文件夹、管理多个文件。 +CodeForge can now open folders and manage multiple files like an editor. + +- **多标签编辑** - 同时打开多个文件/片段,支持拖拽排序、右键菜单(关闭其他 / 关闭右侧 / 复制路径) +- **左侧文件树侧栏** - 打开文件夹、目录懒加载、当前文件高亮、宽度可拖拽、长文件名横向滚动 +- **打开 / 保存本地文件** - 按扩展名自动识别语言;同扩展名多引擎时智能保留当前语言 +- **文件树右键操作** - 新建文件/文件夹、重命名、删除、在系统文件管理器中显示 +- **文件监听** - 外部改动自动刷新文件树 +- **最近文件夹与会话恢复** - 记住并恢复上次打开的文件夹与文件标签 +- **快速打开(Cmd/Ctrl+P)** - 模糊查找当前文件夹内的文件 + +**Multi-tab editing** with drag-to-reorder and a tab context menu (close others / close to the right / copy path) +**File-tree sidebar** — open folder, lazy-loaded directories, active-file highlight, resizable width, horizontal scroll for long names +**Open / save local files** with automatic language detection by extension +**File operations** — create file/folder, rename, delete, reveal in system file manager +**File watching** — auto-refresh the tree on external changes +**Recent folders & session restore** — remember and restore the last folder and open tabs +**Quick open (Cmd/Ctrl+P)** — fuzzy-find files in the current folder + +### 超大文件只读查看 | Read-only Viewer for Huge Files + +- 打开超过可编辑上限的文本文件时,自动以**只读模式**在编辑器内查看 +- 基于**行偏移索引 + 虚拟滚动**,无论文件多大,滚动到任意位置都流畅 +- 可编辑打开的文件大小上限可在设置中调整 + +Open large text files in a **read-only** in-editor view, backed by a **line-offset index + virtual scrolling** for smooth scrolling at any position regardless of file size. The editable size limit is configurable in settings. + +--- + +## 🤖 AI 助手 | AI Assistant + +首次内置 AI 能力,请求经本地后端转发以规避跨域,API Key 仅保存在本机。 +Built-in AI for the first time. Requests are proxied through the local backend to avoid CORS; API keys are stored only on your machine. + +- **多服务商** - 支持 Claude (Anthropic) / OpenAI / DeepSeek,可自定义模型与接口地址 +- **流式输出** - 回复逐字呈现,支持随时**停止生成** +- **Markdown 渲染** - 回复与代码块美观呈现,代码块支持**复制 / 一键应用到编辑器** +- **与执行历史绑定** - AI 对话关联到具体的执行记录并持久化;未执行的对话为临时会话不保存 +- **一键分析报错** - 运行失败后可直接让 AI 分析报错并给出修复 +- **编辑器内自然语言生成(Cmd/Ctrl+K)** - 用自然语言描述需求,生成结果可**编辑确认**后再插入光标处 + +**Multiple providers** — Claude (Anthropic) / OpenAI / DeepSeek with customizable model and endpoint +**Streaming output** — token-by-token responses with a **stop** button +**Markdown rendering** — replies and code blocks render nicely; code blocks support **copy / apply to editor** +**Bound to execution history** — conversations are linked to a specific run and persisted; unrun chats stay temporary +**One-click error analysis** — ask AI to analyze a failed run and suggest a fix +**In-editor natural-language generation (Cmd/Ctrl+K)** — describe what you want, review/edit the result, then insert at the cursor + +--- + +## ▶️ 执行增强 | Execution Enhancements + +- **就地运行** - 关联本地文件时直接运行该文件,工作目录设为文件所在目录,多文件/相对路径正确 +- **task_id 任务路由** - 执行与事件按任务 id 路由,支持多标签并发运行、互不串扰 +- **标准输入与运行参数** - 可为程序提供 stdin,并追加运行参数 +- **运行未保存文件策略** - 自动保存 / 每次询问 / 运行临时副本,可在设置中选择 +- **执行历史** - 基于 SQLite 持久化,支持分页浏览、查看代码与输出、恢复代码 + +**In-place execution** — run the associated file directly, with the working directory set to the file's folder +**Task-based routing** — execution and events are routed by task id, enabling concurrent runs across tabs +**Stdin & run arguments** — feed stdin to the program and append run arguments +**Unsaved-run strategy** — auto-save / ask each time / run a temporary copy (configurable) +**Execution history** — persisted in SQLite with pagination, code/output inspection, and restore + +--- + +## 🎨 编辑器与布局 | Editor & Layout + +- **多种布局** - 编辑器与控制台支持左右 / 上下 / 仅编辑器三种布局 +- **字体缩放** - 编辑器内 Cmd/Ctrl 加减号实时调整字号 +- **编辑增强** - 搜索替换、代码折叠、括号匹配 +- **统一 Tooltip** - 工具栏图标按钮统一悬停提示 + +**Multiple layouts** — editor & console in side-by-side / top-bottom / editor-only modes +**Font zoom** — adjust font size with Cmd/Ctrl +/- in the editor +**Editing enhancements** — search & replace, code folding, bracket matching +**Unified tooltips** for toolbar icon buttons + +--- + +## ⌨️ 快捷键 | Keyboard Shortcuts + +- **全局快捷键** - 运行、保存、另存为、打开、新建/关闭标签、切换侧栏、快速打开、AI 生成等 +- **可自定义** - 新增"快捷键"设置页,所有快捷键可重新绑定 + +**Global shortcuts** — run, save, save as, open, new/close tab, toggle sidebar, quick open, AI generate, and more +**Customizable** — a new "Shortcuts" settings page lets you rebind any shortcut + +--- + +## 🔧 其它优化与修复 | Other Improvements & Fixes + +- **设置整理** - 新增 AI、快捷键设置页;语言列表支持按名称筛选;运行与文件相关设置归入通用 +- **切换语言不再清空已写代码** - 各语言独立保留编辑内容 +- **构建与工程** - CI 升级 Node 22 并修复依赖安装、对齐 Tauri 相关依赖版本、修复多处 UI 细节与告警 + +**Settings cleanup** — new AI and Shortcuts pages; language list filtering; run/file settings moved to General +**Language switching no longer clears your code** — each language keeps its own buffer +**Build & engineering** — CI upgraded to Node 22 with dependency fixes, aligned Tauri dependency versions, and numerous UI/lint fixes + +--- + +## 📥 立即下载 | Download Now + +在 [GitHub Releases](https://github.com/devlive-community/codeforge/releases) 下载最新版本,或访问[官方网站](https://codeforge.devlive.org/)了解更多信息。 + +Download the latest version from [GitHub Releases](https://github.com/devlive-community/codeforge/releases), or visit the [Official Website](https://codeforge.devlive.org/) for more information. diff --git a/docs/pageforge.yaml b/docs/pageforge.yaml index 495c504..41de199 100644 --- a/docs/pageforge.yaml +++ b/docs/pageforge.yaml @@ -14,7 +14,7 @@ repo: branch: dev banner: - content: 💗 CodeForge 25.0.5 已经发布, 如果喜欢我们的软件,请点击这里支持我们 ❤️ + content: 💗 CodeForge 26.0.0 已经发布, 如果喜欢我们的软件,请点击这里支持我们 ❤️ feature: lucide: @@ -43,6 +43,7 @@ footer: nav: - 发布日志: + - /release/26.0.0.md - /release/25.0.5.md - /release/25.0.4.md - /release/25.0.3.md