diff --git a/packages/cloud/src/CloudService.ts b/packages/cloud/src/CloudService.ts index e6f64223e7..60115e31bf 100644 --- a/packages/cloud/src/CloudService.ts +++ b/packages/cloud/src/CloudService.ts @@ -138,9 +138,9 @@ export class CloudService { // ShareService - public async shareTask(taskId: string): Promise { + public async shareTask(taskId: string, visibility: "organization" | "public" = "organization") { this.ensureInitialized() - return this.shareService!.shareTask(taskId) + return this.shareService!.shareTask(taskId, visibility) } public async canShareTask(): Promise { diff --git a/packages/cloud/src/ShareService.ts b/packages/cloud/src/ShareService.ts index 96eb20963e..42ea9c9fcf 100644 --- a/packages/cloud/src/ShareService.ts +++ b/packages/cloud/src/ShareService.ts @@ -7,6 +7,8 @@ import type { AuthService } from "./AuthService" import type { SettingsService } from "./SettingsService" import { getUserAgent } from "./utils" +export type ShareVisibility = "organization" | "public" + export class ShareService { private authService: AuthService private settingsService: SettingsService @@ -19,19 +21,19 @@ export class ShareService { } /** - * Share a task: Create link and copy to clipboard - * Returns true if successful, false if failed + * Share a task with specified visibility + * Returns the share response data */ - async shareTask(taskId: string): Promise { + async shareTask(taskId: string, visibility: ShareVisibility = "organization") { try { const sessionToken = this.authService.getSessionToken() if (!sessionToken) { - return false + throw new Error("Authentication required") } const response = await axios.post( `${getRooCodeApiUrl()}/api/extension/share`, - { taskId }, + { taskId, visibility }, { headers: { "Content-Type": "application/json", @@ -47,14 +49,12 @@ export class ShareService { if (data.success && data.shareUrl) { // Copy to clipboard await vscode.env.clipboard.writeText(data.shareUrl) - return true - } else { - this.log("[share] Share failed:", data.error) - return false } + + return data } catch (error) { this.log("[share] Error sharing task:", error) - return false + throw error } } diff --git a/packages/cloud/src/__tests__/ShareService.test.ts b/packages/cloud/src/__tests__/ShareService.test.ts index b46cefa6a0..55fc4e7c38 100644 --- a/packages/cloud/src/__tests__/ShareService.test.ts +++ b/packages/cloud/src/__tests__/ShareService.test.ts @@ -17,6 +17,7 @@ vi.mock("vscode", () => ({ window: { showInformationMessage: vi.fn(), showErrorMessage: vi.fn(), + showQuickPick: vi.fn(), }, env: { clipboard: { @@ -68,7 +69,7 @@ describe("ShareService", () => { }) describe("shareTask", () => { - it("should share task and copy to clipboard", async () => { + it("should share task with organization visibility and copy to clipboard", async () => { const mockResponse = { data: { success: true, @@ -76,16 +77,16 @@ describe("ShareService", () => { }, } - ;(mockAuthService.hasActiveSession as any).mockReturnValue(true) ;(mockAuthService.getSessionToken as any).mockReturnValue("session-token") mockedAxios.post.mockResolvedValue(mockResponse) - const result = await shareService.shareTask("task-123") + const result = await shareService.shareTask("task-123", "organization") - expect(result).toBe(true) + expect(result.success).toBe(true) + expect(result.shareUrl).toBe("https://app.roocode.com/share/abc123") expect(mockedAxios.post).toHaveBeenCalledWith( "https://app.roocode.com/api/extension/share", - { taskId: "task-123" }, + { taskId: "task-123", visibility: "organization" }, { headers: { "Content-Type": "application/json", @@ -97,63 +98,76 @@ describe("ShareService", () => { expect(vscode.env.clipboard.writeText).toHaveBeenCalledWith("https://app.roocode.com/share/abc123") }) - it("should handle API error response", async () => { + it("should share task with public visibility", async () => { const mockResponse = { data: { - success: false, - error: "Task not found", + success: true, + shareUrl: "https://app.roocode.com/share/abc123", }, } - ;(mockAuthService.hasActiveSession as any).mockReturnValue(true) ;(mockAuthService.getSessionToken as any).mockReturnValue("session-token") mockedAxios.post.mockResolvedValue(mockResponse) - const result = await shareService.shareTask("task-123") + const result = await shareService.shareTask("task-123", "public") - expect(result).toBe(false) + expect(result.success).toBe(true) + expect(mockedAxios.post).toHaveBeenCalledWith( + "https://app.roocode.com/api/extension/share", + { taskId: "task-123", visibility: "public" }, + expect.any(Object), + ) }) - it("should handle authentication errors", async () => { - ;(mockAuthService.hasActiveSession as any).mockReturnValue(false) + it("should default to organization visibility when not specified", async () => { + const mockResponse = { + data: { + success: true, + shareUrl: "https://app.roocode.com/share/abc123", + }, + } + + ;(mockAuthService.getSessionToken as any).mockReturnValue("session-token") + mockedAxios.post.mockResolvedValue(mockResponse) const result = await shareService.shareTask("task-123") - expect(result).toBe(false) - expect(mockedAxios.post).not.toHaveBeenCalled() + expect(result.success).toBe(true) + expect(mockedAxios.post).toHaveBeenCalledWith( + "https://app.roocode.com/api/extension/share", + { taskId: "task-123", visibility: "organization" }, + expect.any(Object), + ) }) - it("should handle 403 error for disabled sharing", async () => { - ;(mockAuthService.hasActiveSession as any).mockReturnValue(true) - ;(mockAuthService.getSessionToken as any).mockReturnValue("session-token") - - const error = { - isAxiosError: true, - response: { - status: 403, - data: { - error: "Task sharing is not enabled for this organization", - }, + it("should handle API error response", async () => { + const mockResponse = { + data: { + success: false, + error: "Task not found", }, } - mockedAxios.isAxiosError.mockReturnValue(true) - mockedAxios.post.mockRejectedValue(error) + ;(mockAuthService.getSessionToken as any).mockReturnValue("session-token") + mockedAxios.post.mockResolvedValue(mockResponse) - const result = await shareService.shareTask("task-123") + const result = await shareService.shareTask("task-123", "organization") - expect(result).toBe(false) + expect(result.success).toBe(false) + expect(result.error).toBe("Task not found") + }) + + it("should handle authentication errors", async () => { + ;(mockAuthService.getSessionToken as any).mockReturnValue(null) + + await expect(shareService.shareTask("task-123", "organization")).rejects.toThrow("Authentication required") }) it("should handle unexpected errors", async () => { - ;(mockAuthService.hasActiveSession as any).mockReturnValue(true) ;(mockAuthService.getSessionToken as any).mockReturnValue("session-token") - mockedAxios.post.mockRejectedValue(new Error("Network error")) - const result = await shareService.shareTask("task-123") - - expect(result).toBe(false) + await expect(shareService.shareTask("task-123", "organization")).rejects.toThrow("Network error") }) }) diff --git a/packages/types/src/cloud.ts b/packages/types/src/cloud.ts index 6f5547b3d5..fbe7a3b658 100644 --- a/packages/types/src/cloud.ts +++ b/packages/types/src/cloud.ts @@ -139,6 +139,8 @@ export const shareResponseSchema = z.object({ success: z.boolean(), shareUrl: z.string().optional(), error: z.string().optional(), + isNewShare: z.boolean().optional(), + manageUrl: z.string().optional(), }) export type ShareResponse = z.infer diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index c5433497dc..41fdb1926a 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -226,16 +226,31 @@ export const webviewMessageHandler = async ( } try { - const success = await CloudService.instance.shareTask(shareTaskId) - if (success) { - // Show success message - vscode.window.showInformationMessage(t("common:info.share_link_copied")) + const visibility = message.visibility || "organization" + const result = await CloudService.instance.shareTask(shareTaskId, visibility) + + if (result.success && result.shareUrl) { + // Show success notification + const messageKey = + visibility === "public" + ? "common:info.public_share_link_copied" + : "common:info.organization_share_link_copied" + vscode.window.showInformationMessage(t(messageKey)) } else { - // Show generic failure message - vscode.window.showErrorMessage(t("common:errors.share_task_failed")) + // Handle error + const errorMessage = result.error || "Failed to create share link" + if (errorMessage.includes("Authentication")) { + vscode.window.showErrorMessage(t("common:errors.share_auth_required")) + } else if (errorMessage.includes("sharing is not enabled")) { + vscode.window.showErrorMessage(t("common:errors.share_not_enabled")) + } else if (errorMessage.includes("not found")) { + vscode.window.showErrorMessage(t("common:errors.share_task_not_found")) + } else { + vscode.window.showErrorMessage(errorMessage) + } } } catch (error) { - // Show generic failure message + provider.log(`[shareCurrentTask] Unexpected error: ${error}`) vscode.window.showErrorMessage(t("common:errors.share_task_failed")) } break diff --git a/src/i18n/locales/ca/common.json b/src/i18n/locales/ca/common.json index c94e1042fe..8fdc1276cf 100644 --- a/src/i18n/locales/ca/common.json +++ b/src/i18n/locales/ca/common.json @@ -63,7 +63,10 @@ "condense_handler_invalid": "El gestor de l'API per condensar el context no és vàlid", "condense_context_grew": "La mida del context ha augmentat durant la condensació; s'omet aquest intent", "share_task_failed": "Ha fallat compartir la tasca. Si us plau, torna-ho a provar.", - "share_no_active_task": "No hi ha cap tasca activa per compartir" + "share_no_active_task": "No hi ha cap tasca activa per compartir", + "share_auth_required": "Es requereix autenticació. Si us plau, inicia sessió per compartir tasques.", + "share_not_enabled": "La compartició de tasques no està habilitada per a aquesta organització.", + "share_task_not_found": "Tasca no trobada o accés denegat." }, "warnings": { "no_terminal_content": "No s'ha seleccionat contingut de terminal", @@ -78,7 +81,9 @@ "settings_imported": "Configuració importada correctament.", "share_link_copied": "Enllaç de compartició copiat al portapapers", "image_copied_to_clipboard": "URI de dades de la imatge copiada al portapapers", - "image_saved": "Imatge desada a {{path}}" + "image_saved": "Imatge desada a {{path}}", + "organization_share_link_copied": "Enllaç de compartició d'organització copiat al porta-retalls!", + "public_share_link_copied": "Enllaç de compartició pública copiat al porta-retalls!" }, "answers": { "yes": "Sí", diff --git a/src/i18n/locales/de/common.json b/src/i18n/locales/de/common.json index 4a37795bd6..e52de53868 100644 --- a/src/i18n/locales/de/common.json +++ b/src/i18n/locales/de/common.json @@ -59,7 +59,10 @@ "condense_handler_invalid": "API-Handler zum Verdichten des Kontexts ist ungültig", "condense_context_grew": "Kontextgröße ist während der Verdichtung gewachsen; dieser Versuch wird übersprungen", "share_task_failed": "Teilen der Aufgabe fehlgeschlagen. Bitte versuche es erneut.", - "share_no_active_task": "Keine aktive Aufgabe zum Teilen" + "share_no_active_task": "Keine aktive Aufgabe zum Teilen", + "share_auth_required": "Authentifizierung erforderlich. Bitte melde dich an, um Aufgaben zu teilen.", + "share_not_enabled": "Aufgabenfreigabe ist für diese Organisation nicht aktiviert.", + "share_task_not_found": "Aufgabe nicht gefunden oder Zugriff verweigert." }, "warnings": { "no_terminal_content": "Kein Terminal-Inhalt ausgewählt", @@ -74,7 +77,9 @@ "settings_imported": "Einstellungen erfolgreich importiert.", "share_link_copied": "Share-Link in die Zwischenablage kopiert", "image_copied_to_clipboard": "Bild-Daten-URI in die Zwischenablage kopiert", - "image_saved": "Bild gespeichert unter {{path}}" + "image_saved": "Bild gespeichert unter {{path}}", + "organization_share_link_copied": "Organisations-Freigabelink in die Zwischenablage kopiert!", + "public_share_link_copied": "Öffentlicher Freigabelink in die Zwischenablage kopiert!" }, "answers": { "yes": "Ja", diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index a505933b69..7d534d8bc3 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -59,7 +59,10 @@ "condense_handler_invalid": "API handler for condensing context is invalid", "condense_context_grew": "Context size increased during condensing; skipping this attempt", "share_task_failed": "Failed to share task. Please try again.", - "share_no_active_task": "No active task to share" + "share_no_active_task": "No active task to share", + "share_auth_required": "Authentication required. Please sign in to share tasks.", + "share_not_enabled": "Task sharing is not enabled for this organization.", + "share_task_not_found": "Task not found or access denied." }, "warnings": { "no_terminal_content": "No terminal content selected", @@ -73,6 +76,8 @@ "default_storage_path": "Reverted to using default storage path", "settings_imported": "Settings imported successfully.", "share_link_copied": "Share link copied to clipboard", + "organization_share_link_copied": "Organization share link copied to clipboard!", + "public_share_link_copied": "Public share link copied to clipboard!", "image_copied_to_clipboard": "Image data URI copied to clipboard", "image_saved": "Image saved to {{path}}" }, diff --git a/src/i18n/locales/es/common.json b/src/i18n/locales/es/common.json index d85e725509..62634f065b 100644 --- a/src/i18n/locales/es/common.json +++ b/src/i18n/locales/es/common.json @@ -59,7 +59,10 @@ "condense_handler_invalid": "El manejador de API para condensar el contexto no es válido", "condense_context_grew": "El tamaño del contexto aumentó durante la condensación; se omite este intento", "share_task_failed": "Error al compartir la tarea. Por favor, inténtalo de nuevo.", - "share_no_active_task": "No hay tarea activa para compartir" + "share_no_active_task": "No hay tarea activa para compartir", + "share_auth_required": "Se requiere autenticación. Por favor, inicia sesión para compartir tareas.", + "share_not_enabled": "La compartición de tareas no está habilitada para esta organización.", + "share_task_not_found": "Tarea no encontrada o acceso denegado." }, "warnings": { "no_terminal_content": "No hay contenido de terminal seleccionado", @@ -74,7 +77,9 @@ "settings_imported": "Configuración importada correctamente.", "share_link_copied": "Enlace de compartir copiado al portapapeles", "image_copied_to_clipboard": "URI de datos de imagen copiada al portapapeles", - "image_saved": "Imagen guardada en {{path}}" + "image_saved": "Imagen guardada en {{path}}", + "organization_share_link_copied": "¡Enlace de compartición de organización copiado al portapapeles!", + "public_share_link_copied": "¡Enlace de compartición pública copiado al portapapeles!" }, "answers": { "yes": "Sí", diff --git a/src/i18n/locales/fr/common.json b/src/i18n/locales/fr/common.json index bceaceb8e9..811be35894 100644 --- a/src/i18n/locales/fr/common.json +++ b/src/i18n/locales/fr/common.json @@ -59,7 +59,10 @@ "condense_handler_invalid": "Le gestionnaire d'API pour condenser le contexte est invalide", "condense_context_grew": "La taille du contexte a augmenté pendant la condensation ; cette tentative est ignorée", "share_task_failed": "Échec du partage de la tâche. Veuillez réessayer.", - "share_no_active_task": "Aucune tâche active à partager" + "share_no_active_task": "Aucune tâche active à partager", + "share_auth_required": "Authentification requise. Veuillez vous connecter pour partager des tâches.", + "share_not_enabled": "Le partage de tâches n'est pas activé pour cette organisation.", + "share_task_not_found": "Tâche non trouvée ou accès refusé." }, "warnings": { "no_terminal_content": "Aucun contenu de terminal sélectionné", @@ -74,7 +77,9 @@ "settings_imported": "Paramètres importés avec succès.", "share_link_copied": "Lien de partage copié dans le presse-papiers", "image_copied_to_clipboard": "URI de données d'image copiée dans le presse-papiers", - "image_saved": "Image enregistrée dans {{path}}" + "image_saved": "Image enregistrée dans {{path}}", + "organization_share_link_copied": "Lien de partage d'organisation copié dans le presse-papiers !", + "public_share_link_copied": "Lien de partage public copié dans le presse-papiers !" }, "answers": { "yes": "Oui", diff --git a/src/i18n/locales/hi/common.json b/src/i18n/locales/hi/common.json index 9dfcdd6f46..bbc5558afe 100644 --- a/src/i18n/locales/hi/common.json +++ b/src/i18n/locales/hi/common.json @@ -59,7 +59,10 @@ "condense_handler_invalid": "संदर्भ को संक्षिप्त करने के लिए API हैंडलर अमान्य है", "condense_context_grew": "संक्षिप्तीकरण के दौरान संदर्भ का आकार बढ़ गया; इस प्रयास को छोड़ा जा रहा है", "share_task_failed": "कार्य साझा करने में विफल। कृपया पुनः प्रयास करें।", - "share_no_active_task": "साझा करने के लिए कोई सक्रिय कार्य नहीं" + "share_no_active_task": "साझा करने के लिए कोई सक्रिय कार्य नहीं", + "share_auth_required": "प्रमाणीकरण आवश्यक है। कार्य साझा करने के लिए कृपया साइन इन करें।", + "share_not_enabled": "इस संगठन के लिए कार्य साझाकरण सक्षम नहीं है।", + "share_task_not_found": "कार्य नहीं मिला या पहुंच अस्वीकृत।" }, "warnings": { "no_terminal_content": "कोई टर्मिनल सामग्री चयनित नहीं", @@ -74,7 +77,9 @@ "settings_imported": "सेटिंग्स सफलतापूर्वक इम्पोर्ट की गईं।", "share_link_copied": "साझा लिंक क्लिपबोर्ड पर कॉपी किया गया", "image_copied_to_clipboard": "छवि डेटा URI क्लिपबोर्ड में कॉपी की गई", - "image_saved": "छवि {{path}} में सहेजी गई" + "image_saved": "छवि {{path}} में सहेजी गई", + "organization_share_link_copied": "संगठन साझाकरण लिंक क्लिपबोर्ड में कॉपी किया गया!", + "public_share_link_copied": "सार्वजनिक साझाकरण लिंक क्लिपबोर्ड में कॉपी किया गया!" }, "answers": { "yes": "हां", diff --git a/src/i18n/locales/id/common.json b/src/i18n/locales/id/common.json index c53c2e0a7c..4c459324b1 100644 --- a/src/i18n/locales/id/common.json +++ b/src/i18n/locales/id/common.json @@ -59,7 +59,10 @@ "condense_handler_invalid": "Handler API untuk mengompres konteks tidak valid", "condense_context_grew": "Ukuran konteks bertambah saat mengompres; melewati percobaan ini", "share_task_failed": "Gagal membagikan tugas. Silakan coba lagi.", - "share_no_active_task": "Tidak ada tugas aktif untuk dibagikan" + "share_no_active_task": "Tidak ada tugas aktif untuk dibagikan", + "share_auth_required": "Autentikasi diperlukan. Silakan masuk untuk berbagi tugas.", + "share_not_enabled": "Berbagi tugas tidak diaktifkan untuk organisasi ini.", + "share_task_not_found": "Tugas tidak ditemukan atau akses ditolak." }, "warnings": { "no_terminal_content": "Tidak ada konten terminal yang dipilih", @@ -74,7 +77,9 @@ "settings_imported": "Pengaturan berhasil diimpor.", "share_link_copied": "Link bagikan disalin ke clipboard", "image_copied_to_clipboard": "Data URI gambar disalin ke clipboard", - "image_saved": "Gambar disimpan ke {{path}}" + "image_saved": "Gambar disimpan ke {{path}}", + "organization_share_link_copied": "Tautan berbagi organisasi disalin ke clipboard!", + "public_share_link_copied": "Tautan berbagi publik disalin ke clipboard!" }, "answers": { "yes": "Ya", diff --git a/src/i18n/locales/it/common.json b/src/i18n/locales/it/common.json index bf89af34f4..55eb562dfe 100644 --- a/src/i18n/locales/it/common.json +++ b/src/i18n/locales/it/common.json @@ -59,7 +59,10 @@ "condense_handler_invalid": "Il gestore API per condensare il contesto non è valido", "condense_context_grew": "La dimensione del contesto è aumentata durante la condensazione; questo tentativo viene saltato", "share_task_failed": "Condivisione dell'attività fallita. Riprova.", - "share_no_active_task": "Nessuna attività attiva da condividere" + "share_no_active_task": "Nessuna attività attiva da condividere", + "share_auth_required": "Autenticazione richiesta. Accedi per condividere le attività.", + "share_not_enabled": "La condivisione delle attività non è abilitata per questa organizzazione.", + "share_task_not_found": "Attività non trovata o accesso negato." }, "warnings": { "no_terminal_content": "Nessun contenuto del terminale selezionato", @@ -74,7 +77,9 @@ "settings_imported": "Impostazioni importate con successo.", "share_link_copied": "Link di condivisione copiato negli appunti", "image_copied_to_clipboard": "URI dati dell'immagine copiato negli appunti", - "image_saved": "Immagine salvata in {{path}}" + "image_saved": "Immagine salvata in {{path}}", + "organization_share_link_copied": "Link di condivisione organizzazione copiato negli appunti!", + "public_share_link_copied": "Link di condivisione pubblica copiato negli appunti!" }, "answers": { "yes": "Sì", diff --git a/src/i18n/locales/ja/common.json b/src/i18n/locales/ja/common.json index 0f06670456..965ba73bca 100644 --- a/src/i18n/locales/ja/common.json +++ b/src/i18n/locales/ja/common.json @@ -59,7 +59,10 @@ "condense_handler_invalid": "コンテキストを圧縮するためのAPIハンドラーが無効です", "condense_context_grew": "圧縮中にコンテキストサイズが増加しました;この試行をスキップします", "share_task_failed": "タスクの共有に失敗しました", - "share_no_active_task": "共有するアクティブなタスクがありません" + "share_no_active_task": "共有するアクティブなタスクがありません", + "share_auth_required": "認証が必要です。タスクを共有するにはサインインしてください。", + "share_not_enabled": "この組織ではタスク共有が有効になっていません。", + "share_task_not_found": "タスクが見つからないか、アクセスが拒否されました。" }, "warnings": { "no_terminal_content": "選択されたターミナルコンテンツがありません", @@ -74,7 +77,9 @@ "settings_imported": "設定が正常にインポートされました。", "share_link_copied": "共有リンクがクリップボードにコピーされました", "image_copied_to_clipboard": "画像データURIがクリップボードにコピーされました", - "image_saved": "画像を{{path}}に保存しました" + "image_saved": "画像を{{path}}に保存しました", + "organization_share_link_copied": "組織共有リンクがクリップボードにコピーされました!", + "public_share_link_copied": "公開共有リンクがクリップボードにコピーされました!" }, "answers": { "yes": "はい", diff --git a/src/i18n/locales/ko/common.json b/src/i18n/locales/ko/common.json index 0c9ff07213..670326b851 100644 --- a/src/i18n/locales/ko/common.json +++ b/src/i18n/locales/ko/common.json @@ -59,7 +59,10 @@ "condense_handler_invalid": "컨텍스트 압축을 위한 API 핸들러가 유효하지 않습니다", "condense_context_grew": "압축 중 컨텍스트 크기가 증가했습니다; 이 시도를 건너뜁니다", "share_task_failed": "작업 공유에 실패했습니다", - "share_no_active_task": "공유할 활성 작업이 없습니다" + "share_no_active_task": "공유할 활성 작업이 없습니다", + "share_auth_required": "인증이 필요합니다. 작업을 공유하려면 로그인하세요.", + "share_not_enabled": "이 조직에서는 작업 공유가 활성화되지 않았습니다.", + "share_task_not_found": "작업을 찾을 수 없거나 액세스가 거부되었습니다." }, "warnings": { "no_terminal_content": "선택된 터미널 내용이 없습니다", @@ -74,7 +77,9 @@ "settings_imported": "설정이 성공적으로 가져와졌습니다.", "share_link_copied": "공유 링크가 클립보드에 복사되었습니다", "image_copied_to_clipboard": "이미지 데이터 URI가 클립보드에 복사되었습니다", - "image_saved": "이미지가 {{path}}에 저장되었습니다" + "image_saved": "이미지가 {{path}}에 저장되었습니다", + "organization_share_link_copied": "조직 공유 링크가 클립보드에 복사되었습니다!", + "public_share_link_copied": "공개 공유 링크가 클립보드에 복사되었습니다!" }, "answers": { "yes": "예", diff --git a/src/i18n/locales/nl/common.json b/src/i18n/locales/nl/common.json index 5d24fd7206..7a97a6b1bc 100644 --- a/src/i18n/locales/nl/common.json +++ b/src/i18n/locales/nl/common.json @@ -59,7 +59,10 @@ "condense_handler_invalid": "API-handler voor het comprimeren van context is ongeldig", "condense_context_grew": "Contextgrootte nam toe tijdens comprimeren; deze poging wordt overgeslagen", "share_task_failed": "Delen van taak mislukt", - "share_no_active_task": "Geen actieve taak om te delen" + "share_no_active_task": "Geen actieve taak om te delen", + "share_auth_required": "Authenticatie vereist. Log in om taken te delen.", + "share_not_enabled": "Taken delen is niet ingeschakeld voor deze organisatie.", + "share_task_not_found": "Taak niet gevonden of toegang geweigerd." }, "warnings": { "no_terminal_content": "Geen terminalinhoud geselecteerd", @@ -74,7 +77,9 @@ "settings_imported": "Instellingen succesvol geïmporteerd.", "share_link_copied": "Deellink gekopieerd naar klembord", "image_copied_to_clipboard": "Afbeelding data-URI gekopieerd naar klembord", - "image_saved": "Afbeelding opgeslagen naar {{path}}" + "image_saved": "Afbeelding opgeslagen naar {{path}}", + "organization_share_link_copied": "Organisatie deel-link gekopieerd naar klembord!", + "public_share_link_copied": "Openbare deel-link gekopieerd naar klembord!" }, "answers": { "yes": "Ja", diff --git a/src/i18n/locales/pl/common.json b/src/i18n/locales/pl/common.json index 842bb7ecec..87293db303 100644 --- a/src/i18n/locales/pl/common.json +++ b/src/i18n/locales/pl/common.json @@ -59,7 +59,10 @@ "condense_handler_invalid": "Nieprawidłowy handler API do kondensowania kontekstu", "condense_context_grew": "Rozmiar kontekstu wzrósł podczas kondensacji; pomijanie tej próby", "share_task_failed": "Nie udało się udostępnić zadania", - "share_no_active_task": "Brak aktywnego zadania do udostępnienia" + "share_no_active_task": "Brak aktywnego zadania do udostępnienia", + "share_auth_required": "Wymagana autoryzacja. Zaloguj się, aby udostępniać zadania.", + "share_not_enabled": "Udostępnianie zadań nie jest włączone dla tej organizacji.", + "share_task_not_found": "Zadanie nie znalezione lub dostęp odmówiony." }, "warnings": { "no_terminal_content": "Nie wybrano zawartości terminala", @@ -74,7 +77,9 @@ "settings_imported": "Ustawienia zaimportowane pomyślnie.", "share_link_copied": "Link udostępniania skopiowany do schowka", "image_copied_to_clipboard": "URI danych obrazu skopiowane do schowka", - "image_saved": "Obraz zapisany w {{path}}" + "image_saved": "Obraz zapisany w {{path}}", + "organization_share_link_copied": "Link udostępniania organizacji skopiowany do schowka!", + "public_share_link_copied": "Publiczny link udostępniania skopiowany do schowka!" }, "answers": { "yes": "Tak", diff --git a/src/i18n/locales/pt-BR/common.json b/src/i18n/locales/pt-BR/common.json index b9a1dd6d6f..e2847d590d 100644 --- a/src/i18n/locales/pt-BR/common.json +++ b/src/i18n/locales/pt-BR/common.json @@ -63,7 +63,10 @@ "condense_handler_invalid": "O manipulador de API para condensar o contexto é inválido", "condense_context_grew": "O tamanho do contexto aumentou durante a condensação; pulando esta tentativa", "share_task_failed": "Falha ao compartilhar tarefa", - "share_no_active_task": "Nenhuma tarefa ativa para compartilhar" + "share_no_active_task": "Nenhuma tarefa ativa para compartilhar", + "share_auth_required": "Autenticação necessária. Faça login para compartilhar tarefas.", + "share_not_enabled": "O compartilhamento de tarefas não está habilitado para esta organização.", + "share_task_not_found": "Tarefa não encontrada ou acesso negado." }, "warnings": { "no_terminal_content": "Nenhum conteúdo do terminal selecionado", @@ -78,7 +81,9 @@ "settings_imported": "Configurações importadas com sucesso.", "share_link_copied": "Link de compartilhamento copiado para a área de transferência", "image_copied_to_clipboard": "URI de dados da imagem copiada para a área de transferência", - "image_saved": "Imagem salva em {{path}}" + "image_saved": "Imagem salva em {{path}}", + "organization_share_link_copied": "Link de compartilhamento da organização copiado para a área de transferência!", + "public_share_link_copied": "Link de compartilhamento público copiado para a área de transferência!" }, "answers": { "yes": "Sim", diff --git a/src/i18n/locales/ru/common.json b/src/i18n/locales/ru/common.json index e71766472d..9d5f29a44a 100644 --- a/src/i18n/locales/ru/common.json +++ b/src/i18n/locales/ru/common.json @@ -59,7 +59,10 @@ "condense_handler_invalid": "Обработчик API для сжатия контекста недействителен", "condense_context_grew": "Размер контекста увеличился во время сжатия; пропускаем эту попытку", "share_task_failed": "Не удалось поделиться задачей", - "share_no_active_task": "Нет активной задачи для совместного использования" + "share_no_active_task": "Нет активной задачи для совместного использования", + "share_auth_required": "Требуется аутентификация. Войдите в систему для совместного доступа к задачам.", + "share_not_enabled": "Совместный доступ к задачам не включен для этой организации.", + "share_task_not_found": "Задача не найдена или доступ запрещен." }, "warnings": { "no_terminal_content": "Не выбрано содержимое терминала", @@ -74,7 +77,9 @@ "settings_imported": "Настройки успешно импортированы.", "share_link_copied": "Ссылка для совместного использования скопирована в буфер обмена", "image_copied_to_clipboard": "URI данных изображения скопирован в буфер обмена", - "image_saved": "Изображение сохранено в {{path}}" + "image_saved": "Изображение сохранено в {{path}}", + "organization_share_link_copied": "Ссылка для совместного доступа организации скопирована в буфер обмена!", + "public_share_link_copied": "Публичная ссылка для совместного доступа скопирована в буфер обмена!" }, "answers": { "yes": "Да", diff --git a/src/i18n/locales/tr/common.json b/src/i18n/locales/tr/common.json index 8c729c5c7a..893117222a 100644 --- a/src/i18n/locales/tr/common.json +++ b/src/i18n/locales/tr/common.json @@ -59,7 +59,10 @@ "condense_handler_invalid": "Bağlamı sıkıştırmak için API işleyicisi geçersiz", "condense_context_grew": "Sıkıştırma sırasında bağlam boyutu arttı; bu deneme atlanıyor", "share_task_failed": "Görev paylaşılamadı", - "share_no_active_task": "Paylaşılacak aktif görev yok" + "share_no_active_task": "Paylaşılacak aktif görev yok", + "share_auth_required": "Kimlik doğrulama gerekli. Görevleri paylaşmak için lütfen giriş yapın.", + "share_not_enabled": "Bu kuruluş için görev paylaşımı etkinleştirilmemiş.", + "share_task_not_found": "Görev bulunamadı veya erişim reddedildi." }, "warnings": { "no_terminal_content": "Seçili terminal içeriği yok", @@ -74,7 +77,9 @@ "settings_imported": "Ayarlar başarıyla içe aktarıldı.", "share_link_copied": "Paylaşım bağlantısı panoya kopyalandı", "image_copied_to_clipboard": "Resim veri URI'si panoya kopyalandı", - "image_saved": "Resim {{path}} konumuna kaydedildi" + "image_saved": "Resim {{path}} konumuna kaydedildi", + "organization_share_link_copied": "Kuruluş paylaşım bağlantısı panoya kopyalandı!", + "public_share_link_copied": "Herkese açık paylaşım bağlantısı panoya kopyalandı!" }, "answers": { "yes": "Evet", diff --git a/src/i18n/locales/vi/common.json b/src/i18n/locales/vi/common.json index ae5457ef14..cc8a22f8a2 100644 --- a/src/i18n/locales/vi/common.json +++ b/src/i18n/locales/vi/common.json @@ -59,7 +59,10 @@ "condense_handler_invalid": "Trình xử lý API để nén ngữ cảnh không hợp lệ", "condense_context_grew": "Kích thước ngữ cảnh tăng lên trong quá trình nén; bỏ qua lần thử này", "share_task_failed": "Không thể chia sẻ nhiệm vụ", - "share_no_active_task": "Không có nhiệm vụ hoạt động để chia sẻ" + "share_no_active_task": "Không có nhiệm vụ hoạt động để chia sẻ", + "share_auth_required": "Cần xác thực. Vui lòng đăng nhập để chia sẻ nhiệm vụ.", + "share_not_enabled": "Chia sẻ nhiệm vụ không được bật cho tổ chức này.", + "share_task_not_found": "Không tìm thấy nhiệm vụ hoặc truy cập bị từ chối." }, "warnings": { "no_terminal_content": "Không có nội dung terminal được chọn", @@ -74,7 +77,9 @@ "settings_imported": "Cài đặt đã được nhập thành công.", "share_link_copied": "Liên kết chia sẻ đã được sao chép vào clipboard", "image_copied_to_clipboard": "URI dữ liệu hình ảnh đã được sao chép vào clipboard", - "image_saved": "Hình ảnh đã được lưu vào {{path}}" + "image_saved": "Hình ảnh đã được lưu vào {{path}}", + "organization_share_link_copied": "Liên kết chia sẻ tổ chức đã được sao chép vào clipboard!", + "public_share_link_copied": "Liên kết chia sẻ công khai đã được sao chép vào clipboard!" }, "answers": { "yes": "Có", diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json index dd2e456890..b8e2307b85 100644 --- a/src/i18n/locales/zh-CN/common.json +++ b/src/i18n/locales/zh-CN/common.json @@ -64,7 +64,10 @@ "condense_handler_invalid": "压缩上下文的API处理程序无效", "condense_context_grew": "压缩过程中上下文大小增加;跳过此次尝试", "share_task_failed": "分享任务失败。请重试。", - "share_no_active_task": "没有活跃任务可分享" + "share_no_active_task": "没有活跃任务可分享", + "share_auth_required": "需要身份验证。请登录以分享任务。", + "share_not_enabled": "此组织未启用任务分享功能。", + "share_task_not_found": "未找到任务或访问被拒绝。" }, "warnings": { "no_terminal_content": "没有选择终端内容", @@ -79,7 +82,9 @@ "settings_imported": "设置已成功导入。", "share_link_copied": "分享链接已复制到剪贴板", "image_copied_to_clipboard": "图片数据 URI 已复制到剪贴板", - "image_saved": "图片已保存到 {{path}}" + "image_saved": "图片已保存到 {{path}}", + "organization_share_link_copied": "组织分享链接已复制到剪贴板!", + "public_share_link_copied": "公开分享链接已复制到剪贴板!" }, "answers": { "yes": "是", diff --git a/src/i18n/locales/zh-TW/common.json b/src/i18n/locales/zh-TW/common.json index 298c7aa539..57e065a7f7 100644 --- a/src/i18n/locales/zh-TW/common.json +++ b/src/i18n/locales/zh-TW/common.json @@ -59,7 +59,10 @@ "condense_handler_invalid": "壓縮上下文的 API 處理程式無效", "condense_context_grew": "壓縮過程中上下文大小增加;跳過此次嘗試", "share_task_failed": "分享工作失敗。請重試。", - "share_no_active_task": "沒有活躍的工作可分享" + "share_no_active_task": "沒有活躍的工作可分享", + "share_auth_required": "需要身份驗證。請登入以分享工作。", + "share_not_enabled": "此組織未啟用工作分享功能。", + "share_task_not_found": "未找到工作或存取被拒絕。" }, "warnings": { "no_terminal_content": "沒有選擇終端機內容", @@ -74,7 +77,9 @@ "settings_imported": "設定已成功匯入。", "share_link_copied": "分享連結已複製到剪貼簿", "image_copied_to_clipboard": "圖片資料 URI 已複製到剪貼簿", - "image_saved": "圖片已儲存至 {{path}}" + "image_saved": "圖片已儲存至 {{path}}", + "organization_share_link_copied": "組織分享連結已複製到剪貼簿!", + "public_share_link_copied": "公開分享連結已複製到剪貼簿!" }, "answers": { "yes": "是", diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index cbcf0c10e3..97dc9ccdf0 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -204,6 +204,7 @@ export interface WebviewMessage { mpItem?: MarketplaceItem mpInstallOptions?: InstallMarketplaceItemOptions config?: Record // Add config to the payload + visibility?: "organization" | "public" // For share visibility } export const checkoutDiffPayloadSchema = z.object({ diff --git a/webview-ui/src/components/chat/TaskActions.tsx b/webview-ui/src/components/chat/TaskActions.tsx index 31e942133a..cef27408eb 100644 --- a/webview-ui/src/components/chat/TaskActions.tsx +++ b/webview-ui/src/components/chat/TaskActions.tsx @@ -6,6 +6,16 @@ import type { HistoryItem } from "@roo-code/types" import { vscode } from "@/utils/vscode" import { useExtensionState } from "@/context/ExtensionStateContext" +import { + Button, + Popover, + PopoverContent, + PopoverTrigger, + Command, + CommandList, + CommandItem, + CommandGroup, +} from "@/components/ui" import { DeleteTaskDialog } from "../history/DeleteTaskDialog" import { IconButton } from "./IconButton" @@ -17,18 +27,65 @@ interface TaskActionsProps { export const TaskActions = ({ item, buttonsDisabled }: TaskActionsProps) => { const [deleteTaskId, setDeleteTaskId] = useState(null) + const [shareDropdownOpen, setShareDropdownOpen] = useState(false) const { t } = useTranslation() const { sharingEnabled } = useExtensionState() + const handleShare = (visibility: "organization" | "public") => { + vscode.postMessage({ + type: "shareCurrentTask", + visibility, + }) + setShareDropdownOpen(false) + } + return (
{item?.id && sharingEnabled && ( - vscode.postMessage({ type: "shareCurrentTask" })} - /> + + + + + + + + + handleShare("organization")} + className="cursor-pointer"> +
+ +
+ {t("chat:task.shareWithOrganization")} + + {t("chat:task.shareWithOrganizationDescription")} + +
+
+
+ handleShare("public")} className="cursor-pointer"> +
+ +
+ {t("chat:task.sharePublicly")} + + {t("chat:task.sharePubliclyDescription")} + +
+
+
+
+
+
+
+
)}