diff --git a/packages/cloud/src/AuthService.ts b/packages/cloud/src/AuthService.ts index 6b26ae138c..fda7df7945 100644 --- a/packages/cloud/src/AuthService.ts +++ b/packages/cloud/src/AuthService.ts @@ -9,6 +9,7 @@ import type { CloudUserInfo } from "@roo-code/types" import { getClerkBaseUrl, getRooCodeApiUrl } from "./Config" import { RefreshTimer } from "./RefreshTimer" +import { getUserAgent } from "./utils" export interface AuthServiceEvents { "inactive-session": [data: { previousState: AuthState }] @@ -435,7 +436,7 @@ export class AuthService extends EventEmitter { } private userAgent(): string { - return `Roo-Code ${this.context.extension?.packageJSON?.version}` + return getUserAgent(this.context) } private static _instance: AuthService | null = null diff --git a/packages/cloud/src/CloudService.ts b/packages/cloud/src/CloudService.ts index 2f39be2b24..08a270bfc3 100644 --- a/packages/cloud/src/CloudService.ts +++ b/packages/cloud/src/CloudService.ts @@ -7,6 +7,7 @@ import { CloudServiceCallbacks } from "./types" import { AuthService } from "./AuthService" import { SettingsService } from "./SettingsService" import { TelemetryClient } from "./TelemetryClient" +import { ShareService } from "./ShareService" export class CloudService { private static _instance: CloudService | null = null @@ -17,6 +18,7 @@ export class CloudService { private authService: AuthService | null = null private settingsService: SettingsService | null = null private telemetryClient: TelemetryClient | null = null + private shareService: ShareService | null = null private isInitialized = false private log: (...args: unknown[]) => void @@ -48,6 +50,8 @@ export class CloudService { this.telemetryClient = new TelemetryClient(this.authService, this.settingsService) + this.shareService = new ShareService(this.authService, this.settingsService, this.log) + try { TelemetryService.instance.register(this.telemetryClient) } catch (error) { @@ -112,6 +116,18 @@ export class CloudService { this.telemetryClient!.capture(event) } + // ShareService + + public async shareTask(taskId: string): Promise { + this.ensureInitialized() + return this.shareService!.shareTask(taskId) + } + + public async canShareTask(): Promise { + this.ensureInitialized() + return this.shareService!.canShareTask() + } + // Lifecycle public dispose(): void { @@ -128,7 +144,13 @@ export class CloudService { } private ensureInitialized(): void { - if (!this.isInitialized || !this.authService || !this.settingsService || !this.telemetryClient) { + if ( + !this.isInitialized || + !this.authService || + !this.settingsService || + !this.telemetryClient || + !this.shareService + ) { throw new Error("CloudService not initialized.") } } diff --git a/packages/cloud/src/ShareService.ts b/packages/cloud/src/ShareService.ts new file mode 100644 index 0000000000..96eb20963e --- /dev/null +++ b/packages/cloud/src/ShareService.ts @@ -0,0 +1,76 @@ +import axios from "axios" +import * as vscode from "vscode" + +import { shareResponseSchema } from "@roo-code/types" +import { getRooCodeApiUrl } from "./Config" +import type { AuthService } from "./AuthService" +import type { SettingsService } from "./SettingsService" +import { getUserAgent } from "./utils" + +export class ShareService { + private authService: AuthService + private settingsService: SettingsService + private log: (...args: unknown[]) => void + + constructor(authService: AuthService, settingsService: SettingsService, log?: (...args: unknown[]) => void) { + this.authService = authService + this.settingsService = settingsService + this.log = log || console.log + } + + /** + * Share a task: Create link and copy to clipboard + * Returns true if successful, false if failed + */ + async shareTask(taskId: string): Promise { + try { + const sessionToken = this.authService.getSessionToken() + if (!sessionToken) { + return false + } + + const response = await axios.post( + `${getRooCodeApiUrl()}/api/extension/share`, + { taskId }, + { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${sessionToken}`, + "User-Agent": getUserAgent(), + }, + }, + ) + + const data = shareResponseSchema.parse(response.data) + this.log("[share] Share link created successfully:", data) + + 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 + } + } catch (error) { + this.log("[share] Error sharing task:", error) + return false + } + } + + /** + * Check if sharing is available + */ + async canShareTask(): Promise { + try { + if (!this.authService.isAuthenticated()) { + return false + } + + return !!this.settingsService.getSettings()?.cloudSettings?.enableTaskSharing + } catch (error) { + this.log("[share] Error checking if task can be shared:", error) + return false + } + } +} diff --git a/packages/cloud/src/__tests__/ShareService.test.ts b/packages/cloud/src/__tests__/ShareService.test.ts new file mode 100644 index 0000000000..9a1af9d42a --- /dev/null +++ b/packages/cloud/src/__tests__/ShareService.test.ts @@ -0,0 +1,224 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { describe, it, expect, beforeEach, vi, type MockedFunction } from "vitest" +import axios from "axios" +import * as vscode from "vscode" + +import { ShareService } from "../ShareService" +import type { AuthService } from "../AuthService" +import type { SettingsService } from "../SettingsService" + +// Mock axios +vi.mock("axios") +const mockedAxios = axios as any + +// Mock vscode +vi.mock("vscode", () => ({ + window: { + showInformationMessage: vi.fn(), + showErrorMessage: vi.fn(), + }, + env: { + clipboard: { + writeText: vi.fn(), + }, + openExternal: vi.fn(), + }, + Uri: { + parse: vi.fn(), + }, + extensions: { + getExtension: vi.fn(() => ({ + packageJSON: { version: "1.0.0" }, + })), + }, +})) + +// Mock config +vi.mock("../Config", () => ({ + getRooCodeApiUrl: () => "https://app.roocode.com", +})) + +// Mock utils +vi.mock("../utils", () => ({ + getUserAgent: () => "Roo-Code 1.0.0", +})) + +describe("ShareService", () => { + let shareService: ShareService + let mockAuthService: AuthService + let mockSettingsService: SettingsService + let mockLog: MockedFunction<(...args: unknown[]) => void> + + beforeEach(() => { + vi.clearAllMocks() + + mockLog = vi.fn() + mockAuthService = { + hasActiveSession: vi.fn(), + getSessionToken: vi.fn(), + isAuthenticated: vi.fn(), + } as any + + mockSettingsService = { + getSettings: vi.fn(), + } as any + + shareService = new ShareService(mockAuthService, mockSettingsService, mockLog) + }) + + describe("shareTask", () => { + it("should share task and copy to clipboard", async () => { + const mockResponse = { + data: { + 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") + + expect(result).toBe(true) + expect(mockedAxios.post).toHaveBeenCalledWith( + "https://app.roocode.com/api/extension/share", + { taskId: "task-123" }, + { + headers: { + "Content-Type": "application/json", + Authorization: "Bearer session-token", + "User-Agent": "Roo-Code 1.0.0", + }, + }, + ) + expect(vscode.env.clipboard.writeText).toHaveBeenCalledWith("https://app.roocode.com/share/abc123") + }) + + it("should handle API error response", async () => { + const mockResponse = { + data: { + success: false, + error: "Task not found", + }, + } + + ;(mockAuthService.hasActiveSession as any).mockReturnValue(true) + ;(mockAuthService.getSessionToken as any).mockReturnValue("session-token") + mockedAxios.post.mockResolvedValue(mockResponse) + + const result = await shareService.shareTask("task-123") + + expect(result).toBe(false) + }) + + it("should handle authentication errors", async () => { + ;(mockAuthService.hasActiveSession as any).mockReturnValue(false) + + const result = await shareService.shareTask("task-123") + + expect(result).toBe(false) + expect(mockedAxios.post).not.toHaveBeenCalled() + }) + + 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", + }, + }, + } + + mockedAxios.isAxiosError.mockReturnValue(true) + mockedAxios.post.mockRejectedValue(error) + + const result = await shareService.shareTask("task-123") + + expect(result).toBe(false) + }) + + 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) + }) + }) + + describe("canShareTask", () => { + it("should return true when authenticated and sharing is enabled", async () => { + ;(mockAuthService.isAuthenticated as any).mockReturnValue(true) + ;(mockSettingsService.getSettings as any).mockReturnValue({ + cloudSettings: { + enableTaskSharing: true, + }, + }) + + const result = await shareService.canShareTask() + + expect(result).toBe(true) + }) + + it("should return false when authenticated but sharing is disabled", async () => { + ;(mockAuthService.isAuthenticated as any).mockReturnValue(true) + ;(mockSettingsService.getSettings as any).mockReturnValue({ + cloudSettings: { + enableTaskSharing: false, + }, + }) + + const result = await shareService.canShareTask() + + expect(result).toBe(false) + }) + + it("should return false when authenticated and sharing setting is undefined (default)", async () => { + ;(mockAuthService.isAuthenticated as any).mockReturnValue(true) + ;(mockSettingsService.getSettings as any).mockReturnValue({ + cloudSettings: {}, + }) + + const result = await shareService.canShareTask() + + expect(result).toBe(false) + }) + + it("should return false when authenticated and no settings available (default)", async () => { + ;(mockAuthService.isAuthenticated as any).mockReturnValue(true) + ;(mockSettingsService.getSettings as any).mockReturnValue(undefined) + + const result = await shareService.canShareTask() + + expect(result).toBe(false) + }) + + it("should return false when not authenticated", async () => { + ;(mockAuthService.isAuthenticated as any).mockReturnValue(false) + + const result = await shareService.canShareTask() + + expect(result).toBe(false) + }) + + it("should handle errors gracefully", async () => { + ;(mockAuthService.isAuthenticated as any).mockImplementation(() => { + throw new Error("Auth error") + }) + + const result = await shareService.canShareTask() + + expect(result).toBe(false) + }) + }) +}) diff --git a/packages/cloud/src/utils.ts b/packages/cloud/src/utils.ts new file mode 100644 index 0000000000..cf87aa5e28 --- /dev/null +++ b/packages/cloud/src/utils.ts @@ -0,0 +1,10 @@ +import * as vscode from "vscode" + +/** + * Get the User-Agent string for API requests + * @param context Optional extension context for more accurate version detection + * @returns User-Agent string in format "Roo-Code {version}" + */ +export function getUserAgent(context?: vscode.ExtensionContext): string { + return `Roo-Code ${context?.extension?.packageJSON?.version || "unknown"}` +} diff --git a/packages/types/src/cloud.ts b/packages/types/src/cloud.ts index f425e8c0d4..6347d596fe 100644 --- a/packages/types/src/cloud.ts +++ b/packages/types/src/cloud.ts @@ -104,3 +104,15 @@ export const ORGANIZATION_DEFAULT: OrganizationSettings = { defaultSettings: {}, allowList: ORGANIZATION_ALLOW_ALL, } as const + +/** + * Share Types + */ + +export const shareResponseSchema = z.object({ + success: z.boolean(), + shareUrl: z.string().optional(), + error: z.string().optional(), +}) + +export type ShareResponse = z.infer diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 015e3ec184..880ac3bdf4 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1306,6 +1306,7 @@ export class ClineProvider historyPreviewCollapsed, cloudUserInfo, cloudIsAuthenticated, + sharingEnabled, organizationAllowList, maxConcurrentFileReads, condensingApiConfigId, @@ -1405,6 +1406,7 @@ export class ClineProvider historyPreviewCollapsed: historyPreviewCollapsed ?? false, cloudUserInfo, cloudIsAuthenticated: cloudIsAuthenticated ?? false, + sharingEnabled: sharingEnabled ?? false, organizationAllowList, condensingApiConfigId, customCondensingPrompt, @@ -1470,6 +1472,16 @@ export class ClineProvider ) } + let sharingEnabled: boolean = false + + try { + sharingEnabled = await CloudService.instance.canShareTask() + } catch (error) { + console.error( + `[getState] failed to get sharing enabled state: ${error instanceof Error ? error.message : String(error)}`, + ) + } + // Return the same structure as before return { apiConfiguration: providerSettings, @@ -1546,6 +1558,7 @@ export class ClineProvider historyPreviewCollapsed: stateValues.historyPreviewCollapsed ?? false, cloudUserInfo, cloudIsAuthenticated, + sharingEnabled, organizationAllowList, // Explicitly add condensing settings condensingApiConfigId: stateValues.condensingApiConfigId, diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts index 77ae9c8eb7..e84a78057a 100644 --- a/src/core/webview/__tests__/ClineProvider.test.ts +++ b/src/core/webview/__tests__/ClineProvider.test.ts @@ -428,6 +428,7 @@ describe("ClineProvider", () => { autoCondenseContext: true, autoCondenseContextPercent: 100, cloudIsAuthenticated: false, + sharingEnabled: false, } const message: ExtensionMessage = { diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 659d60f31a..ff4058f8c7 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -207,6 +207,27 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We provider.exportTaskWithId(currentTaskId) } break + case "shareCurrentTask": + const shareTaskId = provider.getCurrentCline()?.taskId + if (!shareTaskId) { + vscode.window.showErrorMessage(t("common:errors.share_no_active_task")) + break + } + + try { + const success = await CloudService.instance.shareTask(shareTaskId) + if (success) { + // Show success message + vscode.window.showInformationMessage(t("common:info.share_link_copied")) + } else { + // Show generic failure message + vscode.window.showErrorMessage(t("common:errors.share_task_failed")) + } + } catch (error) { + // Show generic failure message + vscode.window.showErrorMessage(t("common:errors.share_task_failed")) + } + break case "showTaskWithId": provider.showTaskWithId(message.text!) break diff --git a/src/i18n/locales/ca/common.json b/src/i18n/locales/ca/common.json index 188c4e9942..1aebed352b 100644 --- a/src/i18n/locales/ca/common.json +++ b/src/i18n/locales/ca/common.json @@ -63,7 +63,9 @@ "condense_not_enough_messages": "No hi ha prou missatges per condensar el context", "condensed_recently": "El context s'ha condensat recentment; s'omet aquest intent", "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" + "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" }, "warnings": { "no_terminal_content": "No s'ha seleccionat contingut de terminal", @@ -79,7 +81,8 @@ "mcp_server_not_found": "Servidor \"{{serverName}}\" no trobat a la configuració", "custom_storage_path_set": "Ruta d'emmagatzematge personalitzada establerta: {{path}}", "default_storage_path": "S'ha reprès l'ús de la ruta d'emmagatzematge predeterminada", - "settings_imported": "Configuració importada correctament." + "settings_imported": "Configuració importada correctament.", + "share_link_copied": "Enllaç de compartició copiat al portapapers" }, "answers": { "yes": "Sí", diff --git a/src/i18n/locales/de/common.json b/src/i18n/locales/de/common.json index ab398a6208..c73d28138a 100644 --- a/src/i18n/locales/de/common.json +++ b/src/i18n/locales/de/common.json @@ -59,7 +59,9 @@ "condense_not_enough_messages": "Nicht genügend Nachrichten zum Verdichten des Kontexts", "condensed_recently": "Kontext wurde kürzlich verdichtet; dieser Versuch wird übersprungen", "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" + "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" }, "warnings": { "no_terminal_content": "Kein Terminal-Inhalt ausgewählt", @@ -75,7 +77,8 @@ "mcp_server_not_found": "Server \"{{serverName}}\" nicht in der Konfiguration gefunden", "custom_storage_path_set": "Benutzerdefinierter Speicherpfad festgelegt: {{path}}", "default_storage_path": "Auf Standardspeicherpfad zurückgesetzt", - "settings_imported": "Einstellungen erfolgreich importiert." + "settings_imported": "Einstellungen erfolgreich importiert.", + "share_link_copied": "Share-Link in die Zwischenablage kopiert" }, "answers": { "yes": "Ja", diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index aec4895a69..c1a1258eb5 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -59,7 +59,9 @@ "condense_not_enough_messages": "Not enough messages to condense context", "condensed_recently": "Context was condensed recently; skipping this attempt", "condense_handler_invalid": "API handler for condensing context is invalid", - "condense_context_grew": "Context size increased during condensing; skipping this attempt" + "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" }, "warnings": { "no_terminal_content": "No terminal content selected", @@ -75,7 +77,8 @@ "mcp_server_not_found": "Server \"{{serverName}}\" not found in configuration", "custom_storage_path_set": "Custom storage path set: {{path}}", "default_storage_path": "Reverted to using default storage path", - "settings_imported": "Settings imported successfully." + "settings_imported": "Settings imported successfully.", + "share_link_copied": "Share link copied to clipboard" }, "answers": { "yes": "Yes", diff --git a/src/i18n/locales/es/common.json b/src/i18n/locales/es/common.json index 85da7cca3a..43fb5c8e44 100644 --- a/src/i18n/locales/es/common.json +++ b/src/i18n/locales/es/common.json @@ -59,7 +59,9 @@ "condense_not_enough_messages": "No hay suficientes mensajes para condensar el contexto", "condensed_recently": "El contexto se condensó recientemente; se omite este intento", "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" + "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" }, "warnings": { "no_terminal_content": "No hay contenido de terminal seleccionado", @@ -75,7 +77,8 @@ "mcp_server_not_found": "Servidor \"{{serverName}}\" no encontrado en la configuración", "custom_storage_path_set": "Ruta de almacenamiento personalizada establecida: {{path}}", "default_storage_path": "Se ha vuelto a usar la ruta de almacenamiento predeterminada", - "settings_imported": "Configuración importada correctamente." + "settings_imported": "Configuración importada correctamente.", + "share_link_copied": "Enlace de compartir copiado al portapapeles" }, "answers": { "yes": "Sí", diff --git a/src/i18n/locales/fr/common.json b/src/i18n/locales/fr/common.json index c03669f8fd..d92777688e 100644 --- a/src/i18n/locales/fr/common.json +++ b/src/i18n/locales/fr/common.json @@ -59,7 +59,9 @@ "condense_not_enough_messages": "Pas assez de messages pour condenser le contexte", "condensed_recently": "Le contexte a été condensé récemment ; cette tentative est ignorée", "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" + "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" }, "warnings": { "no_terminal_content": "Aucun contenu de terminal sélectionné", @@ -75,7 +77,8 @@ "mcp_server_not_found": "Serveur \"{{serverName}}\" introuvable dans la configuration", "custom_storage_path_set": "Chemin de stockage personnalisé défini : {{path}}", "default_storage_path": "Retour au chemin de stockage par défaut", - "settings_imported": "Paramètres importés avec succès." + "settings_imported": "Paramètres importés avec succès.", + "share_link_copied": "Lien de partage 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 757503cc89..df4389f5f7 100644 --- a/src/i18n/locales/hi/common.json +++ b/src/i18n/locales/hi/common.json @@ -59,7 +59,9 @@ "condense_not_enough_messages": "संदर्भ को संक्षिप्त करने के लिए पर्याप्त संदेश नहीं हैं", "condensed_recently": "संदर्भ हाल ही में संक्षिप्त किया गया था; इस प्रयास को छोड़ा जा रहा है", "condense_handler_invalid": "संदर्भ को संक्षिप्त करने के लिए API हैंडलर अमान्य है", - "condense_context_grew": "संक्षिप्तीकरण के दौरान संदर्भ का आकार बढ़ गया; इस प्रयास को छोड़ा जा रहा है" + "condense_context_grew": "संक्षिप्तीकरण के दौरान संदर्भ का आकार बढ़ गया; इस प्रयास को छोड़ा जा रहा है", + "share_task_failed": "कार्य साझा करने में विफल। कृपया पुनः प्रयास करें।", + "share_no_active_task": "साझा करने के लिए कोई सक्रिय कार्य नहीं" }, "warnings": { "no_terminal_content": "कोई टर्मिनल सामग्री चयनित नहीं", @@ -75,7 +77,8 @@ "mcp_server_not_found": "सर्वर \"{{serverName}}\" कॉन्फ़िगरेशन में नहीं मिला", "custom_storage_path_set": "कस्टम स्टोरेज पाथ सेट किया गया: {{path}}", "default_storage_path": "डिफ़ॉल्ट स्टोरेज पाथ का उपयोग पुनः शुरू किया गया", - "settings_imported": "सेटिंग्स सफलतापूर्वक इम्पोर्ट की गईं।" + "settings_imported": "सेटिंग्स सफलतापूर्वक इम्पोर्ट की गईं।", + "share_link_copied": "साझा लिंक क्लिपबोर्ड पर कॉपी किया गया" }, "answers": { "yes": "हां", diff --git a/src/i18n/locales/it/common.json b/src/i18n/locales/it/common.json index 8a3e998976..217295afbe 100644 --- a/src/i18n/locales/it/common.json +++ b/src/i18n/locales/it/common.json @@ -59,7 +59,9 @@ "condense_not_enough_messages": "Non ci sono abbastanza messaggi per condensare il contesto", "condensed_recently": "Il contesto è stato condensato di recente; questo tentativo viene saltato", "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" + "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" }, "warnings": { "no_terminal_content": "Nessun contenuto del terminale selezionato", @@ -75,7 +77,8 @@ "mcp_server_not_found": "Server \"{{serverName}}\" non trovato nella configurazione", "custom_storage_path_set": "Percorso di archiviazione personalizzato impostato: {{path}}", "default_storage_path": "Tornato al percorso di archiviazione predefinito", - "settings_imported": "Impostazioni importate con successo." + "settings_imported": "Impostazioni importate con successo.", + "share_link_copied": "Link di condivisione copiato negli appunti" }, "answers": { "yes": "Sì", diff --git a/src/i18n/locales/ja/common.json b/src/i18n/locales/ja/common.json index e0d372691d..f1401c196c 100644 --- a/src/i18n/locales/ja/common.json +++ b/src/i18n/locales/ja/common.json @@ -59,7 +59,9 @@ "condense_not_enough_messages": "コンテキストを圧縮するのに十分なメッセージがありません", "condensed_recently": "コンテキストは最近圧縮されました;この試行をスキップします", "condense_handler_invalid": "コンテキストを圧縮するためのAPIハンドラーが無効です", - "condense_context_grew": "圧縮中にコンテキストサイズが増加しました;この試行をスキップします" + "condense_context_grew": "圧縮中にコンテキストサイズが増加しました;この試行をスキップします", + "share_task_failed": "タスクの共有に失敗しました", + "share_no_active_task": "共有するアクティブなタスクがありません" }, "warnings": { "no_terminal_content": "選択されたターミナルコンテンツがありません", @@ -75,7 +77,8 @@ "mcp_server_not_found": "サーバー\"{{serverName}}\"が設定内に見つかりません", "custom_storage_path_set": "カスタムストレージパスが設定されました:{{path}}", "default_storage_path": "デフォルトのストレージパスに戻りました", - "settings_imported": "設定が正常にインポートされました。" + "settings_imported": "設定が正常にインポートされました。", + "share_link_copied": "共有リンクがクリップボードにコピーされました" }, "answers": { "yes": "はい", diff --git a/src/i18n/locales/ko/common.json b/src/i18n/locales/ko/common.json index 56645431f7..67326a0990 100644 --- a/src/i18n/locales/ko/common.json +++ b/src/i18n/locales/ko/common.json @@ -59,7 +59,9 @@ "condense_not_enough_messages": "컨텍스트를 압축할 메시지가 충분하지 않습니다", "condensed_recently": "컨텍스트가 최근 압축되었습니다; 이 시도를 건너뜁니다", "condense_handler_invalid": "컨텍스트 압축을 위한 API 핸들러가 유효하지 않습니다", - "condense_context_grew": "압축 중 컨텍스트 크기가 증가했습니다; 이 시도를 건너뜁니다" + "condense_context_grew": "압축 중 컨텍스트 크기가 증가했습니다; 이 시도를 건너뜁니다", + "share_task_failed": "작업 공유에 실패했습니다", + "share_no_active_task": "공유할 활성 작업이 없습니다" }, "warnings": { "no_terminal_content": "선택된 터미널 내용이 없습니다", @@ -75,7 +77,8 @@ "mcp_server_not_found": "구성에서 서버 \"{{serverName}}\"을(를) 찾을 수 없습니다", "custom_storage_path_set": "사용자 지정 저장 경로 설정됨: {{path}}", "default_storage_path": "기본 저장 경로로 되돌아갔습니다", - "settings_imported": "설정이 성공적으로 가져와졌습니다." + "settings_imported": "설정이 성공적으로 가져와졌습니다.", + "share_link_copied": "공유 링크가 클립보드에 복사되었습니다" }, "answers": { "yes": "예", diff --git a/src/i18n/locales/nl/common.json b/src/i18n/locales/nl/common.json index faebeeccae..00d7fc19d0 100644 --- a/src/i18n/locales/nl/common.json +++ b/src/i18n/locales/nl/common.json @@ -59,7 +59,9 @@ "condense_not_enough_messages": "Niet genoeg berichten om context te comprimeren", "condensed_recently": "Context is recent gecomprimeerd; deze poging wordt overgeslagen", "condense_handler_invalid": "API-handler voor het comprimeren van context is ongeldig", - "condense_context_grew": "Contextgrootte nam toe tijdens comprimeren; deze poging wordt overgeslagen" + "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" }, "warnings": { "no_terminal_content": "Geen terminalinhoud geselecteerd", @@ -75,7 +77,8 @@ "mcp_server_not_found": "Server \"{{serverName}}\" niet gevonden in configuratie", "custom_storage_path_set": "Aangepast opslagpad ingesteld: {{path}}", "default_storage_path": "Terug naar standaard opslagpad", - "settings_imported": "Instellingen succesvol geïmporteerd." + "settings_imported": "Instellingen succesvol geïmporteerd.", + "share_link_copied": "Deellink gekopieerd naar klembord" }, "answers": { "yes": "Ja", diff --git a/src/i18n/locales/pl/common.json b/src/i18n/locales/pl/common.json index 6ad71a82ee..b8bd11cce6 100644 --- a/src/i18n/locales/pl/common.json +++ b/src/i18n/locales/pl/common.json @@ -59,7 +59,9 @@ "condense_not_enough_messages": "Za mało wiadomości do skondensowania kontekstu", "condensed_recently": "Kontekst został niedawno skondensowany; pomijanie tej próby", "condense_handler_invalid": "Nieprawidłowy handler API do kondensowania kontekstu", - "condense_context_grew": "Rozmiar kontekstu wzrósł podczas kondensacji; pomijanie tej próby" + "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" }, "warnings": { "no_terminal_content": "Nie wybrano zawartości terminala", @@ -75,7 +77,8 @@ "mcp_server_not_found": "Serwer \"{{serverName}}\" nie znaleziony w konfiguracji", "custom_storage_path_set": "Ustawiono niestandardową ścieżkę przechowywania: {{path}}", "default_storage_path": "Wznowiono używanie domyślnej ścieżki przechowywania", - "settings_imported": "Ustawienia zaimportowane pomyślnie." + "settings_imported": "Ustawienia zaimportowane pomyślnie.", + "share_link_copied": "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 68a5140199..64fec047a6 100644 --- a/src/i18n/locales/pt-BR/common.json +++ b/src/i18n/locales/pt-BR/common.json @@ -63,7 +63,9 @@ "condense_not_enough_messages": "Não há mensagens suficientes para condensar o contexto", "condensed_recently": "O contexto foi condensado recentemente; pulando esta tentativa", "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" + "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" }, "warnings": { "no_terminal_content": "Nenhum conteúdo do terminal selecionado", @@ -79,7 +81,8 @@ "mcp_server_not_found": "Servidor \"{{serverName}}\" não encontrado na configuração", "custom_storage_path_set": "Caminho de armazenamento personalizado definido: {{path}}", "default_storage_path": "Retornado ao caminho de armazenamento padrão", - "settings_imported": "Configurações importadas com sucesso." + "settings_imported": "Configurações importadas com sucesso.", + "share_link_copied": "Link de compartilhamento 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 1367df71f1..c5e5a41e30 100644 --- a/src/i18n/locales/ru/common.json +++ b/src/i18n/locales/ru/common.json @@ -59,7 +59,9 @@ "condense_not_enough_messages": "Недостаточно сообщений для сжатия контекста", "condensed_recently": "Контекст был недавно сжат; пропускаем эту попытку", "condense_handler_invalid": "Обработчик API для сжатия контекста недействителен", - "condense_context_grew": "Размер контекста увеличился во время сжатия; пропускаем эту попытку" + "condense_context_grew": "Размер контекста увеличился во время сжатия; пропускаем эту попытку", + "share_task_failed": "Не удалось поделиться задачей", + "share_no_active_task": "Нет активной задачи для совместного использования" }, "warnings": { "no_terminal_content": "Не выбрано содержимое терминала", @@ -75,7 +77,8 @@ "mcp_server_not_found": "Сервер \"{{serverName}}\" не найден в конфигурации", "custom_storage_path_set": "Установлен пользовательский путь хранения: {{path}}", "default_storage_path": "Возвращено использование пути хранения по умолчанию", - "settings_imported": "Настройки успешно импортированы." + "settings_imported": "Настройки успешно импортированы.", + "share_link_copied": "Ссылка для совместного использования скопирована в буфер обмена" }, "answers": { "yes": "Да", diff --git a/src/i18n/locales/tr/common.json b/src/i18n/locales/tr/common.json index 0c068af7ce..9a9dcf2557 100644 --- a/src/i18n/locales/tr/common.json +++ b/src/i18n/locales/tr/common.json @@ -59,7 +59,9 @@ "condense_not_enough_messages": "Bağlamı sıkıştırmak için yeterli mesaj yok", "condensed_recently": "Bağlam yakın zamanda sıkıştırıldı; bu deneme atlanıyor", "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" + "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" }, "warnings": { "no_terminal_content": "Seçili terminal içeriği yok", @@ -75,7 +77,8 @@ "mcp_server_not_found": "Yapılandırmada \"{{serverName}}\" sunucusu bulunamadı", "custom_storage_path_set": "Özel depolama yolu ayarlandı: {{path}}", "default_storage_path": "Varsayılan depolama yoluna geri dönüldü", - "settings_imported": "Ayarlar başarıyla içe aktarıldı." + "settings_imported": "Ayarlar başarıyla içe aktarıldı.", + "share_link_copied": "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 994981a785..445883f9ea 100644 --- a/src/i18n/locales/vi/common.json +++ b/src/i18n/locales/vi/common.json @@ -59,7 +59,9 @@ "condense_not_enough_messages": "Không đủ tin nhắn để nén ngữ cảnh", "condensed_recently": "Ngữ cảnh đã được nén gần đây; bỏ qua lần thử này", "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" + "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ẻ" }, "warnings": { "no_terminal_content": "Không có nội dung terminal được chọn", @@ -75,7 +77,8 @@ "mcp_server_not_found": "Không tìm thấy máy chủ \"{{serverName}}\" trong cấu hình", "custom_storage_path_set": "Đã thiết lập đường dẫn lưu trữ tùy chỉnh: {{path}}", "default_storage_path": "Đã quay lại sử dụng đường dẫn lưu trữ mặc định", - "settings_imported": "Cài đặt đã được nhập thành công." + "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" }, "answers": { "yes": "Có", diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json index 89b9a76546..5fd4e9957e 100644 --- a/src/i18n/locales/zh-CN/common.json +++ b/src/i18n/locales/zh-CN/common.json @@ -59,7 +59,9 @@ "condense_not_enough_messages": "没有足够的对话来压缩上下文", "condensed_recently": "上下文最近已压缩;跳过此次尝试", "condense_handler_invalid": "压缩上下文的API处理程序无效", - "condense_context_grew": "压缩过程中上下文大小增加;跳过此次尝试" + "condense_context_grew": "压缩过程中上下文大小增加;跳过此次尝试", + "share_task_failed": "分享任务失败。请重试。", + "share_no_active_task": "没有活跃任务可分享" }, "warnings": { "no_terminal_content": "没有选择终端内容", @@ -75,7 +77,8 @@ "mcp_server_not_found": "在配置中未找到服务器\"{{serverName}}\"", "custom_storage_path_set": "自定义存储路径已设置:{{path}}", "default_storage_path": "已恢复使用默认存储路径", - "settings_imported": "设置已成功导入。" + "settings_imported": "设置已成功导入。", + "share_link_copied": "分享链接已复制到剪贴板" }, "answers": { "yes": "是", diff --git a/src/i18n/locales/zh-TW/common.json b/src/i18n/locales/zh-TW/common.json index eb08597b7c..bac0efd7d3 100644 --- a/src/i18n/locales/zh-TW/common.json +++ b/src/i18n/locales/zh-TW/common.json @@ -59,7 +59,9 @@ "condense_not_enough_messages": "沒有足夠的訊息來壓縮上下文", "condensed_recently": "上下文最近已壓縮;跳過此次嘗試", "condense_handler_invalid": "壓縮上下文的 API 處理程式無效", - "condense_context_grew": "壓縮過程中上下文大小增加;跳過此次嘗試" + "condense_context_grew": "壓縮過程中上下文大小增加;跳過此次嘗試", + "share_task_failed": "分享工作失敗。請重試。", + "share_no_active_task": "沒有活躍的工作可分享" }, "warnings": { "no_terminal_content": "沒有選擇終端機內容", @@ -75,7 +77,8 @@ "mcp_server_not_found": "在設定中沒有找到伺服器\"{{serverName}}\"", "custom_storage_path_set": "自訂儲存路徑已設定:{{path}}", "default_storage_path": "已恢復使用預設儲存路徑", - "settings_imported": "設定已成功匯入。" + "settings_imported": "設定已成功匯入。", + "share_link_copied": "分享連結已複製到剪貼簿" }, "answers": { "yes": "是", diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 07d300ed01..59c024658b 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -220,6 +220,7 @@ export type ExtensionState = Pick< cloudUserInfo: CloudUserInfo | null cloudIsAuthenticated: boolean + sharingEnabled: boolean organizationAllowList: OrganizationAllowList autoCondenseContext: boolean diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 05136c18b3..ee9cc3211f 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -36,6 +36,7 @@ export interface WebviewMessage { | "didShowAnnouncement" | "selectImages" | "exportCurrentTask" + | "shareCurrentTask" | "showTaskWithId" | "deleteTaskWithId" | "exportTaskWithId" diff --git a/webview-ui/src/components/chat/TaskActions.tsx b/webview-ui/src/components/chat/TaskActions.tsx index 18325af8fb..31e942133a 100644 --- a/webview-ui/src/components/chat/TaskActions.tsx +++ b/webview-ui/src/components/chat/TaskActions.tsx @@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next" import type { HistoryItem } from "@roo-code/types" import { vscode } from "@/utils/vscode" +import { useExtensionState } from "@/context/ExtensionStateContext" import { DeleteTaskDialog } from "../history/DeleteTaskDialog" import { IconButton } from "./IconButton" @@ -17,9 +18,18 @@ interface TaskActionsProps { export const TaskActions = ({ item, buttonsDisabled }: TaskActionsProps) => { const [deleteTaskId, setDeleteTaskId] = useState(null) const { t } = useTranslation() + const { sharingEnabled } = useExtensionState() return (
+ {item?.id && sharingEnabled && ( + vscode.postMessage({ type: "shareCurrentTask" })} + /> + )} organizationAllowList: OrganizationAllowList cloudIsAuthenticated: boolean + sharingEnabled: boolean maxConcurrentFileReads?: number condensingApiConfigId?: string setCondensingApiConfigId: (value: string) => void @@ -201,6 +202,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode historyPreviewCollapsed: false, // Initialize the new state (default to expanded) cloudUserInfo: null, cloudIsAuthenticated: false, + sharingEnabled: false, organizationAllowList: ORGANIZATION_ALLOW_ALL, autoCondenseContext: true, autoCondenseContextPercent: 100, diff --git a/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx b/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx index 30f5fc7111..8662783f44 100644 --- a/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx +++ b/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx @@ -208,6 +208,7 @@ describe("mergeExtensionState", () => { autoCondenseContext: true, autoCondenseContextPercent: 100, cloudIsAuthenticated: false, + sharingEnabled: false, } const prevState: ExtensionState = { diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index d6f501b8de..a589109e4f 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -11,7 +11,8 @@ "closeAndStart": "Tancar tasca i iniciar-ne una de nova", "export": "Exportar historial de tasques", "delete": "Eliminar tasca (Shift + Clic per ometre confirmació)", - "condenseContext": "Condensar context de forma intel·ligent" + "condenseContext": "Condensar context de forma intel·ligent", + "share": "Compartir tasca" }, "unpin": "Desfixar", "pin": "Fixar", diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index 0676b3a0da..aac9231bf9 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -11,6 +11,7 @@ "closeAndStart": "Aufgabe schließen und neue starten", "export": "Aufgabenverlauf exportieren", "delete": "Aufgabe löschen (Shift + Klick zum Überspringen der Bestätigung)", + "share": "Aufgabe teilen", "condenseContext": "Kontext intelligent komprimieren" }, "unpin": "Lösen von oben", diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index f46c3af553..156ca523ee 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -11,6 +11,7 @@ "contextWindow": "Context Length:", "closeAndStart": "Close task and start a new one", "export": "Export task history", + "share": "Share task", "delete": "Delete Task (Shift + Click to skip confirmation)" }, "unpin": "Unpin", diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index 3f2cf71a4b..39cf064cf2 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -11,7 +11,8 @@ "closeAndStart": "Cerrar tarea e iniciar una nueva", "export": "Exportar historial de tareas", "delete": "Eliminar tarea (Shift + Clic para omitir confirmación)", - "condenseContext": "Condensar contexto de forma inteligente" + "condenseContext": "Condensar contexto de forma inteligente", + "share": "Compartir tarea" }, "unpin": "Desfijar", "pin": "Fijar", diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index 219657434f..62d018c5cd 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -11,7 +11,8 @@ "closeAndStart": "Fermer la tâche et en commencer une nouvelle", "export": "Exporter l'historique des tâches", "delete": "Supprimer la tâche (Shift + Clic pour ignorer la confirmation)", - "condenseContext": "Condenser intelligemment le contexte" + "condenseContext": "Condenser intelligemment le contexte", + "share": "Partager la tâche" }, "unpin": "Désépingler", "pin": "Épingler", diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index 174e7e1d60..9c10c57c0d 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -11,7 +11,8 @@ "closeAndStart": "कार्य बंद करें और नया शुरू करें", "export": "कार्य इतिहास निर्यात करें", "delete": "कार्य हटाएं (पुष्टि को छोड़ने के लिए Shift + क्लिक)", - "condenseContext": "संदर्भ को बुद्धिमानी से संघनित करें" + "condenseContext": "संदर्भ को बुद्धिमानी से संघनित करें", + "share": "कार्य साझा करें" }, "unpin": "पिन करें", "pin": "अवपिन करें", diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index 317cdf3fc0..26dce52655 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -11,7 +11,8 @@ "closeAndStart": "Chiudi attività e iniziane una nuova", "export": "Esporta cronologia attività", "delete": "Elimina attività (Shift + Clic per saltare la conferma)", - "condenseContext": "Condensa contesto in modo intelligente" + "condenseContext": "Condensa contesto in modo intelligente", + "share": "Condividi attività" }, "unpin": "Rilascia", "pin": "Fissa", diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index 6624ed17ac..e76dd9a120 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -11,7 +11,8 @@ "closeAndStart": "タスクを閉じて新しいタスクを開始", "export": "タスク履歴をエクスポート", "delete": "タスクを削除(Shift + クリックで確認をスキップ)", - "condenseContext": "コンテキストをインテリジェントに圧縮" + "condenseContext": "コンテキストをインテリジェントに圧縮", + "share": "タスクを共有" }, "unpin": "ピン留めを解除", "pin": "ピン留め", diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index f18efb53ae..563508d2a9 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -11,7 +11,8 @@ "closeAndStart": "작업 닫고 새 작업 시작", "export": "작업 기록 내보내기", "delete": "작업 삭제 (Shift + 클릭으로 확인 생략)", - "condenseContext": "컨텍스트 지능적으로 압축" + "condenseContext": "컨텍스트 지능적으로 압축", + "share": "작업 공유" }, "unpin": "고정 해제하기", "pin": "고정하기", diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index 557c338a1f..2a8ba0ac70 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -11,7 +11,8 @@ "closeAndStart": "Taak sluiten en een nieuwe starten", "export": "Taakgeschiedenis exporteren", "delete": "Taak verwijderen (Shift + Klik om bevestiging over te slaan)", - "condenseContext": "Context intelligent samenvatten" + "condenseContext": "Context intelligent samenvatten", + "share": "Taak delen" }, "unpin": "Losmaken", "pin": "Vastmaken", diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index bb7a5aa0c5..a57dc9d0cc 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -11,7 +11,8 @@ "closeAndStart": "Zamknij zadanie i rozpocznij nowe", "export": "Eksportuj historię zadań", "delete": "Usuń zadanie (Shift + Kliknięcie, aby pominąć potwierdzenie)", - "condenseContext": "Inteligentnie skondensuj kontekst" + "condenseContext": "Inteligentnie skondensuj kontekst", + "share": "Udostępnij zadanie" }, "unpin": "Odepnij", "pin": "Przypnij", diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index ccf39dfe50..05d436de71 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -11,7 +11,8 @@ "closeAndStart": "Fechar tarefa e iniciar nova", "export": "Exportar histórico de tarefas", "delete": "Excluir tarefa (Shift + Clique para pular confirmação)", - "condenseContext": "Condensar contexto de forma inteligente" + "condenseContext": "Condensar contexto de forma inteligente", + "share": "Compartilhar tarefa" }, "unpin": "Desfixar", "pin": "Fixar", diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index fe19877267..8e24a9e3ea 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -11,7 +11,8 @@ "closeAndStart": "Закрыть задачу и начать новую", "export": "Экспортировать историю задач", "delete": "Удалить задачу (Shift + клик для пропуска подтверждения)", - "condenseContext": "Интеллектуально сжать контекст" + "condenseContext": "Интеллектуально сжать контекст", + "share": "Поделиться задачей" }, "unpin": "Открепить", "pin": "Закрепить", diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index ab9b88edb3..c0d43f59b5 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -11,7 +11,8 @@ "closeAndStart": "Görevi kapat ve yeni bir görev başlat", "export": "Görev geçmişini dışa aktar", "delete": "Görevi sil (Onayı atlamak için Shift + Tıkla)", - "condenseContext": "Bağlamı akıllıca yoğunlaştır" + "condenseContext": "Bağlamı akıllıca yoğunlaştır", + "share": "Görevi paylaş" }, "unpin": "Sabitlemeyi iptal et", "pin": "Sabitle", diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index d76775d878..5ad270aa89 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -11,7 +11,8 @@ "closeAndStart": "Đóng nhiệm vụ và bắt đầu nhiệm vụ mới", "export": "Xuất lịch sử nhiệm vụ", "delete": "Xóa nhiệm vụ (Shift + Click để bỏ qua xác nhận)", - "condenseContext": "Cô đọng ngữ cảnh thông minh" + "condenseContext": "Cô đọng ngữ cảnh thông minh", + "share": "Chia sẻ nhiệm vụ" }, "unpin": "Bỏ ghim khỏi đầu", "pin": "Ghim lên đầu", diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index 694824a081..058adfd0ad 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -11,6 +11,7 @@ "closeAndStart": "关闭任务并开始新任务", "export": "导出任务历史", "delete": "删除任务(Shift + 点击跳过确认)", + "share": "分享任务", "condenseContext": "智能压缩上下文" }, "unpin": "取消置顶", diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index 0023d2dd6c..0309948a95 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -11,7 +11,8 @@ "closeAndStart": "關閉現有工作並開始一項新的工作", "export": "匯出工作紀錄", "delete": "刪除工作(按住 Shift 並點選可跳過確認)", - "condenseContext": "智慧壓縮上下文" + "condenseContext": "智慧壓縮上下文", + "share": "分享工作" }, "unpin": "取消置頂", "pin": "置頂",