From c428acc5a5cdb3b64ee3dd9c70ad2967f998b5be Mon Sep 17 00:00:00 2001 From: xierfloat <2053619887@qq.com> Date: Fri, 27 Feb 2026 11:32:56 +0800 Subject: [PATCH 1/3] feat(desktop): add export button for roles and tools with v2 path support Add export buttons to RoleDetailPanel and ToolDetailPanel for non-system resources. Fix resources:download handler to resolve v2 role paths from ~/.rolex/roles// and wrap v2 ZIP contents in a roleId-prefixed directory so import correctly restores the original ID. Co-Authored-By: Claude Opus 4.6 --- apps/desktop/src/i18n/locales/en.json | 5 ++++- apps/desktop/src/i18n/locales/zh-CN.json | 5 ++++- .../src/main/windows/ResourceListWindow.ts | 12 ++++++++--- .../components/RoleDetailPanel.tsx | 20 ++++++++++++++++++- .../components/ToolDetailPanel.tsx | 17 +++++++++++++++- 5 files changed, 52 insertions(+), 7 deletions(-) diff --git a/apps/desktop/src/i18n/locales/en.json b/apps/desktop/src/i18n/locales/en.json index 2c4f3112..cadab941 100644 --- a/apps/desktop/src/i18n/locales/en.json +++ b/apps/desktop/src/i18n/locales/en.json @@ -280,7 +280,10 @@ "edit": "Edit", "download": "Download", "delete": "Delete", - "use": "Use" + "use": "Use", + "export": "Export", + "exportSuccess": "Exported successfully", + "exportFailed": "Export failed" }, "messages": { "loadFailed": "Failed to load resources", diff --git a/apps/desktop/src/i18n/locales/zh-CN.json b/apps/desktop/src/i18n/locales/zh-CN.json index 37ebf86b..fee51d7a 100644 --- a/apps/desktop/src/i18n/locales/zh-CN.json +++ b/apps/desktop/src/i18n/locales/zh-CN.json @@ -279,7 +279,10 @@ "edit": "编辑", "download": "下载", "delete": "删除", - "use": "使用" + "use": "使用", + "export": "导出", + "exportSuccess": "导出成功", + "exportFailed": "导出失败" }, "messages": { "loadFailed": "加载资源失败", diff --git a/apps/desktop/src/main/windows/ResourceListWindow.ts b/apps/desktop/src/main/windows/ResourceListWindow.ts index bec6c4ef..71006644 100644 --- a/apps/desktop/src/main/windows/ResourceListWindow.ts +++ b/apps/desktop/src/main/windows/ResourceListWindow.ts @@ -306,11 +306,12 @@ export class ResourceListWindow { }) // 新增:下载资源(分享即下载,导出为 ZIP 压缩包) - ipcMain.handle('resources:download', async (_evt, payload: { id: string; type: 'role' | 'tool'; source?: string }) => { + ipcMain.handle('resources:download', async (_evt, payload: { id: string; type: 'role' | 'tool'; source?: string; version?: string }) => { try { const id = payload?.id const type = payload?.type const source = payload?.source ?? 'user' + const version = payload?.version ?? 'v1' if (!id || !type) { return { success: false, message: t('resources.missingParams') } } @@ -324,7 +325,10 @@ export class ResourceListWindow { // 定位资源目录 let sourceDir: string | null = null if (source === 'user') { - sourceDir = path.join(os.homedir(), '.promptx', 'resource', type, id) + // V2 角色存储在 ~/.rolex/roles//,V1 及工具存储在 ~/.promptx/resource/// + sourceDir = (type === 'role' && version === 'v2') + ? path.join(os.homedir(), '.rolex', 'roles', id) + : path.join(os.homedir(), '.promptx', 'resource', type, id) } else if (source === 'project') { try { const { ProjectPathResolver } = require('@promptx/core') @@ -386,7 +390,9 @@ export class ResourceListWindow { } } - await addDirectoryToZip(sourceDir) + // V2 角色导出时用 roleId 作为 ZIP 内顶层目录,确保导入时能正确还原 ID + const zipRootPrefix = (type === 'role' && version === 'v2') ? id : '' + await addDirectoryToZip(sourceDir, zipRootPrefix) // 写入 ZIP 文件 zip.writeZip(zipFilePath) diff --git a/apps/desktop/src/view/pages/roles-window/components/RoleDetailPanel.tsx b/apps/desktop/src/view/pages/roles-window/components/RoleDetailPanel.tsx index 7183c3ea..5d714c89 100644 --- a/apps/desktop/src/view/pages/roles-window/components/RoleDetailPanel.tsx +++ b/apps/desktop/src/view/pages/roles-window/components/RoleDetailPanel.tsx @@ -9,7 +9,8 @@ import { DialogDescription, DialogFooter, } from "@/components/ui/dialog" -import { Pencil, BookOpen, Layers, Brain, FileText, ChevronRight, ChevronDown, Save, Loader2, Target, Building2, Upload, Trash2 } from "lucide-react" +import { Pencil, BookOpen, Layers, Brain, FileText, ChevronRight, ChevronDown, Save, Loader2, Target, Building2, Upload, Trash2, Download } from "lucide-react" +import { toast } from "sonner" import { useEffect, useState, useCallback } from "react" import type { RoleItem } from "./RoleListPanel" import MemoryTab from "./MemoryTab" @@ -982,6 +983,23 @@ export default function RoleDetailPanel({ selectedRole, onActivate, onDelete, on
+ {(selectedRole.source ?? "user") !== "system" && ( + + )} {(selectedRole.source ?? "user") !== "system" && (
{(selectedTool.source ?? "user") === "user" && (
+