Add choice to share with organization or publicly (#4899)

This commit is contained in:
Matt Rubens 2025-06-19 22:29:42 -04:00 committed by GitHub
parent 9e5ecb18f7
commit b6bbb1e95b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 369 additions and 118 deletions

View file

@ -138,9 +138,9 @@ export class CloudService {
// ShareService
public async shareTask(taskId: string): Promise<boolean> {
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<boolean> {

View file

@ -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<boolean> {
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
}
}

View file

@ -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,6 +98,48 @@ describe("ShareService", () => {
expect(vscode.env.clipboard.writeText).toHaveBeenCalledWith("https://app.roocode.com/share/abc123")
})
it("should share task with public visibility", 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", "public")
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 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.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 API error response", async () => {
const mockResponse = {
data: {
@ -105,55 +148,26 @@ 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(false)
expect(result.success).toBe(false)
expect(result.error).toBe("Task not found")
})
it("should handle authentication errors", async () => {
;(mockAuthService.hasActiveSession as any).mockReturnValue(false)
;(mockAuthService.getSessionToken as any).mockReturnValue(null)
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)
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")
})
})

View file

@ -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<typeof shareResponseSchema>

View file

@ -233,16 +233,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

View file

@ -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í",

View file

@ -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",

View file

@ -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}}"
},

View file

@ -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í",

View file

@ -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",

View file

@ -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": "हां",

View file

@ -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",

View file

@ -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ì",

View file

@ -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": "はい",

View file

@ -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": "예",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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": "Да",

View file

@ -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",

View file

@ -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ó",

View file

@ -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": "是",

View file

@ -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": "是",

View file

@ -204,6 +204,7 @@ export interface WebviewMessage {
mpItem?: MarketplaceItem
mpInstallOptions?: InstallMarketplaceItemOptions
config?: Record<string, any> // Add config to the payload
visibility?: "organization" | "public" // For share visibility
}
export const checkoutDiffPayloadSchema = z.object({

View file

@ -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<string | null>(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 (
<div className="flex flex-row gap-1">
{item?.id && sharingEnabled && (
<IconButton
iconClass="codicon-link"
title={t("chat:task.share")}
disabled={buttonsDisabled}
onClick={() => vscode.postMessage({ type: "shareCurrentTask" })}
/>
<Popover open={shareDropdownOpen} onOpenChange={setShareDropdownOpen}>
<PopoverTrigger asChild>
<Button
variant="ghost"
size="icon"
disabled={buttonsDisabled}
className="h-6 w-6 p-0 hover:bg-vscode-toolbar-hoverBackground"
title={t("chat:task.share")}>
<span className="codicon codicon-link text-xs"></span>
</Button>
</PopoverTrigger>
<PopoverContent className="w-56 p-0" align="start">
<Command>
<CommandList>
<CommandGroup>
<CommandItem
onSelect={() => handleShare("organization")}
className="cursor-pointer">
<div className="flex items-center gap-2">
<span className="codicon codicon-organization text-sm"></span>
<div className="flex flex-col">
<span className="text-sm">{t("chat:task.shareWithOrganization")}</span>
<span className="text-xs text-vscode-descriptionForeground">
{t("chat:task.shareWithOrganizationDescription")}
</span>
</div>
</div>
</CommandItem>
<CommandItem onSelect={() => handleShare("public")} className="cursor-pointer">
<div className="flex items-center gap-2">
<span className="codicon codicon-globe text-sm"></span>
<div className="flex flex-col">
<span className="text-sm">{t("chat:task.sharePublicly")}</span>
<span className="text-xs text-vscode-descriptionForeground">
{t("chat:task.sharePubliclyDescription")}
</span>
</div>
</div>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
)}
<IconButton
iconClass="codicon-desktop-download"

View file

@ -12,7 +12,11 @@
"export": "Exportar historial de tasques",
"delete": "Eliminar tasca (Shift + Clic per ometre confirmació)",
"condenseContext": "Condensar context de forma intel·ligent",
"share": "Compartir tasca"
"share": "Compartir tasca",
"shareWithOrganization": "Compartir amb l'organització",
"shareWithOrganizationDescription": "Només els membres de la teva organització poden accedir",
"sharePublicly": "Compartir públicament",
"sharePubliclyDescription": "Qualsevol amb l'enllaç pot accedir"
},
"unpin": "Desfixar",
"pin": "Fixar",

View file

@ -12,7 +12,11 @@
"export": "Aufgabenverlauf exportieren",
"delete": "Aufgabe löschen (Shift + Klick zum Überspringen der Bestätigung)",
"share": "Aufgabe teilen",
"condenseContext": "Kontext intelligent komprimieren"
"condenseContext": "Kontext intelligent komprimieren",
"shareWithOrganization": "Mit Organisation teilen",
"shareWithOrganizationDescription": "Nur Mitglieder deiner Organisation können zugreifen",
"sharePublicly": "Öffentlich teilen",
"sharePubliclyDescription": "Jeder mit dem Link kann zugreifen"
},
"unpin": "Lösen von oben",
"pin": "Anheften",

View file

@ -12,7 +12,11 @@
"closeAndStart": "Close task and start a new one",
"export": "Export task history",
"share": "Share task",
"delete": "Delete Task (Shift + Click to skip confirmation)"
"delete": "Delete Task (Shift + Click to skip confirmation)",
"shareWithOrganization": "Share with Organization",
"shareWithOrganizationDescription": "Only members of your organization can access",
"sharePublicly": "Share Publicly",
"sharePubliclyDescription": "Anyone with the link can access"
},
"unpin": "Unpin",
"pin": "Pin",

View file

@ -12,7 +12,11 @@
"export": "Exportar historial de tareas",
"delete": "Eliminar tarea (Shift + Clic para omitir confirmación)",
"condenseContext": "Condensar contexto de forma inteligente",
"share": "Compartir tarea"
"share": "Compartir tarea",
"shareWithOrganization": "Compartir con organización",
"shareWithOrganizationDescription": "Solo los miembros de tu organización pueden acceder",
"sharePublicly": "Compartir públicamente",
"sharePubliclyDescription": "Cualquiera con el enlace puede acceder"
},
"unpin": "Desfijar",
"pin": "Fijar",

View file

@ -12,7 +12,11 @@
"export": "Exporter l'historique des tâches",
"delete": "Supprimer la tâche (Shift + Clic pour ignorer la confirmation)",
"condenseContext": "Condenser intelligemment le contexte",
"share": "Partager la tâche"
"share": "Partager la tâche",
"shareWithOrganization": "Partager avec l'organisation",
"shareWithOrganizationDescription": "Seuls les membres de ton organisation peuvent accéder",
"sharePublicly": "Partager publiquement",
"sharePubliclyDescription": "Toute personne avec le lien peut accéder"
},
"unpin": "Désépingler",
"pin": "Épingler",

View file

@ -12,7 +12,11 @@
"export": "कार्य इतिहास निर्यात करें",
"delete": "कार्य हटाएं (पुष्टि को छोड़ने के लिए Shift + क्लिक)",
"condenseContext": "संदर्भ को बुद्धिमानी से संघनित करें",
"share": "कार्य साझा करें"
"share": "कार्य साझा करें",
"shareWithOrganization": "संगठन के साथ साझा करें",
"shareWithOrganizationDescription": "केवल आपके संगठन के सदस्य पहुंच सकते हैं",
"sharePublicly": "सार्वजनिक रूप से साझा करें",
"sharePubliclyDescription": "लिंक वाला कोई भी व्यक्ति पहुंच सकता है"
},
"unpin": "पिन करें",
"pin": "अवपिन करें",

View file

@ -12,7 +12,11 @@
"closeAndStart": "Tutup tugas dan mulai yang baru",
"export": "Ekspor riwayat tugas",
"share": "Bagikan tugas",
"delete": "Hapus Tugas (Shift + Klik untuk lewati konfirmasi)"
"delete": "Hapus Tugas (Shift + Klik untuk lewati konfirmasi)",
"shareWithOrganization": "Bagikan dengan organisasi",
"shareWithOrganizationDescription": "Hanya anggota organisasi Anda yang dapat mengakses",
"sharePublicly": "Bagikan secara publik",
"sharePubliclyDescription": "Siapa pun dengan tautan dapat mengakses"
},
"history": {
"title": "Riwayat"

View file

@ -12,7 +12,11 @@
"export": "Esporta cronologia attività",
"delete": "Elimina attività (Shift + Clic per saltare la conferma)",
"condenseContext": "Condensa contesto in modo intelligente",
"share": "Condividi attività"
"share": "Condividi attività",
"shareWithOrganization": "Condividi con l'organizzazione",
"shareWithOrganizationDescription": "Solo i membri della tua organizzazione possono accedere",
"sharePublicly": "Condividi pubblicamente",
"sharePubliclyDescription": "Chiunque con il link può accedere"
},
"unpin": "Rilascia",
"pin": "Fissa",

View file

@ -12,7 +12,11 @@
"export": "タスク履歴をエクスポート",
"delete": "タスクを削除Shift + クリックで確認をスキップ)",
"condenseContext": "コンテキストをインテリジェントに圧縮",
"share": "タスクを共有"
"share": "タスクを共有",
"shareWithOrganization": "組織と共有",
"shareWithOrganizationDescription": "組織のメンバーのみがアクセスできます",
"sharePublicly": "公開で共有",
"sharePubliclyDescription": "リンクを持つ誰でもアクセスできます"
},
"unpin": "ピン留めを解除",
"pin": "ピン留め",

View file

@ -12,7 +12,11 @@
"export": "작업 기록 내보내기",
"delete": "작업 삭제 (Shift + 클릭으로 확인 생략)",
"condenseContext": "컨텍스트 지능적으로 압축",
"share": "작업 공유"
"share": "작업 공유",
"shareWithOrganization": "조직과 공유",
"shareWithOrganizationDescription": "조직 구성원만 액세스할 수 있습니다",
"sharePublicly": "공개적으로 공유",
"sharePubliclyDescription": "링크가 있는 누구나 액세스할 수 있습니다"
},
"unpin": "고정 해제하기",
"pin": "고정하기",

View file

@ -12,7 +12,11 @@
"export": "Taakgeschiedenis exporteren",
"delete": "Taak verwijderen (Shift + Klik om bevestiging over te slaan)",
"condenseContext": "Context intelligent samenvatten",
"share": "Taak delen"
"share": "Taak delen",
"shareWithOrganization": "Delen met organisatie",
"shareWithOrganizationDescription": "Alleen leden van je organisatie kunnen toegang krijgen",
"sharePublicly": "Openbaar delen",
"sharePubliclyDescription": "Iedereen met de link kan toegang krijgen"
},
"unpin": "Losmaken",
"pin": "Vastmaken",

View file

@ -12,7 +12,11 @@
"export": "Eksportuj historię zadań",
"delete": "Usuń zadanie (Shift + Kliknięcie, aby pominąć potwierdzenie)",
"condenseContext": "Inteligentnie skondensuj kontekst",
"share": "Udostępnij zadanie"
"share": "Udostępnij zadanie",
"shareWithOrganization": "Udostępnij organizacji",
"shareWithOrganizationDescription": "Tylko członkowie twojej organizacji mogą uzyskać dostęp",
"sharePublicly": "Udostępnij publicznie",
"sharePubliclyDescription": "Każdy z linkiem może uzyskać dostęp"
},
"unpin": "Odepnij",
"pin": "Przypnij",

View file

@ -12,7 +12,11 @@
"export": "Exportar histórico de tarefas",
"delete": "Excluir tarefa (Shift + Clique para pular confirmação)",
"condenseContext": "Condensar contexto de forma inteligente",
"share": "Compartilhar tarefa"
"share": "Compartilhar tarefa",
"shareWithOrganization": "Compartilhar com organização",
"shareWithOrganizationDescription": "Apenas membros da sua organização podem acessar",
"sharePublicly": "Compartilhar publicamente",
"sharePubliclyDescription": "Qualquer pessoa com o link pode acessar"
},
"unpin": "Desfixar",
"pin": "Fixar",

View file

@ -12,7 +12,11 @@
"export": "Экспортировать историю задач",
"delete": "Удалить задачу (Shift + клик для пропуска подтверждения)",
"condenseContext": "Интеллектуально сжать контекст",
"share": "Поделиться задачей"
"share": "Поделиться задачей",
"shareWithOrganization": "Поделиться с организацией",
"shareWithOrganizationDescription": "Только члены вашей организации могут получить доступ",
"sharePublicly": "Поделиться публично",
"sharePubliclyDescription": "Любой, у кого есть ссылка, может получить доступ"
},
"unpin": "Открепить",
"pin": "Закрепить",

View file

@ -12,7 +12,11 @@
"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",
"share": "Görevi paylaş"
"share": "Görevi paylaş",
"shareWithOrganization": "Kuruluşla paylaş",
"shareWithOrganizationDescription": "Sadece kuruluşunuzun üyeleri erişebilir",
"sharePublicly": "Herkese açık paylaş",
"sharePubliclyDescription": "Bağlantıya sahip herkes erişebilir"
},
"unpin": "Sabitlemeyi iptal et",
"pin": "Sabitle",

View file

@ -12,7 +12,11 @@
"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",
"share": "Chia sẻ nhiệm vụ"
"share": "Chia sẻ nhiệm vụ",
"shareWithOrganization": "Chia sẻ với tổ chức",
"shareWithOrganizationDescription": "Chỉ thành viên tổ chức của bạn mới có thể truy cập",
"sharePublicly": "Chia sẻ công khai",
"sharePubliclyDescription": "Bất kỳ ai có liên kết đều có thể truy cập"
},
"unpin": "Bỏ ghim khỏi đầu",
"pin": "Ghim lên đầu",

View file

@ -12,7 +12,11 @@
"export": "导出任务历史",
"delete": "删除任务Shift + 点击跳过确认)",
"share": "分享任务",
"condenseContext": "智能压缩上下文"
"condenseContext": "智能压缩上下文",
"shareWithOrganization": "与组织分享",
"shareWithOrganizationDescription": "仅组织成员可访问",
"sharePublicly": "公开分享",
"sharePubliclyDescription": "任何拥有链接的人都可访问"
},
"unpin": "取消置顶",
"pin": "置顶",

View file

@ -12,7 +12,11 @@
"export": "匯出工作紀錄",
"delete": "刪除工作(按住 Shift 並點選可跳過確認)",
"condenseContext": "智慧壓縮上下文",
"share": "分享工作"
"share": "分享工作",
"shareWithOrganization": "與組織分享",
"shareWithOrganizationDescription": "僅組織成員可存取",
"sharePublicly": "公開分享",
"sharePubliclyDescription": "任何擁有連結的人都可存取"
},
"unpin": "取消置頂",
"pin": "置頂",