Filter & Search Workspace Task History (#2526)

* Enhancement: Add 'Show all workspaces' feature to task history and update translations

* Enhancement: Add Checkbox component and integrate it into HistoryPreview and HistoryView

* Simplify the UX

---------

Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com>
This commit is contained in:
Sam Hoang Van 2025-04-13 10:20:14 +07:00 committed by GitHub
parent 628d232f9f
commit 08110ae35b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 365 additions and 47 deletions

View file

@ -9,6 +9,7 @@
"version": "0.1.0",
"dependencies": {
"@radix-ui/react-alert-dialog": "^1.1.6",
"@radix-ui/react-checkbox": "^1.1.5",
"@radix-ui/react-collapsible": "^1.1.3",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-dropdown-menu": "^2.1.5",
@ -3851,6 +3852,218 @@
}
}
},
"node_modules/@radix-ui/react-checkbox": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.5.tgz",
"integrity": "sha512-B0gYIVxl77KYDR25AY9EGe/G//ef85RVBIxQvK+m5pxAC7XihAc/8leMHhDvjvhDu02SBSb6BuytlWr/G7F3+g==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.2",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-presence": "1.1.3",
"@radix-ui/react-primitive": "2.0.3",
"@radix-ui/react-use-controllable-state": "1.1.1",
"@radix-ui/react-use-previous": "1.1.1",
"@radix-ui/react-use-size": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/primitive": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz",
"integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==",
"license": "MIT"
},
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-context": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-presence": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.3.tgz",
"integrity": "sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-primitive": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.3.tgz",
"integrity": "sha512-Pf/t/GkndH7CQ8wE2hbkXA+WyZ83fhQQn5DDmwDiDo6AwN/fhaH8oqZ0jRjMrO2iaMhDi6P1HRx6AZwyMinY1g==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-slot": "1.2.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-slot": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-use-controllable-state": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.1.tgz",
"integrity": "sha512-YnEXIy8/ga01Y1PN0VfaNH//MhA91JlEGVBDxDzROqwrAtG5Yr2QGEPz8A/rJA3C7ZAHryOYGaUv8fLSW2H/mg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-callback-ref": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-use-layout-effect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-use-previous": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
"integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-use-size": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
"integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collapsible": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.3.tgz",

View file

@ -19,6 +19,7 @@
},
"dependencies": {
"@radix-ui/react-alert-dialog": "^1.1.6",
"@radix-ui/react-checkbox": "^1.1.5",
"@radix-ui/react-collapsible": "^1.1.3",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-dropdown-menu": "^2.1.5",

View file

@ -4,15 +4,15 @@ import { vscode } from "@/utils/vscode"
import { formatLargeNumber, formatDate } from "@/utils/format"
import { Button } from "@/components/ui"
import { useExtensionState } from "../../context/ExtensionStateContext"
import { useAppTranslation } from "../../i18n/TranslationContext"
import { CopyButton } from "./CopyButton"
import { useTaskSearch } from "./useTaskSearch"
type HistoryPreviewProps = {
showHistoryView: () => void
}
const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
const { taskHistory } = useExtensionState()
const { tasks } = useTaskSearch()
const { t } = useAppTranslation()
return (
@ -26,7 +26,7 @@ const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
{t("history:viewAll")}
</Button>
</div>
{taskHistory.slice(0, 3).map((item) => (
{tasks.slice(0, 3).map((item) => (
<div
key={item.id}
className="bg-vscode-toolbar-hoverBackground/50 hover:bg-vscode-toolbar-hoverBackground/75 rounded-xs relative overflow-hidden opacity-90 hover:opacity-100 cursor-pointer"

View file

@ -4,12 +4,12 @@ import { BatchDeleteTaskDialog } from "./BatchDeleteTaskDialog"
import prettyBytes from "pretty-bytes"
import { Virtuoso } from "react-virtuoso"
import { VSCodeTextField, VSCodeRadioGroup, VSCodeRadio, VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
import { VSCodeTextField, VSCodeRadioGroup, VSCodeRadio } from "@vscode/webview-ui-toolkit/react"
import { vscode } from "@/utils/vscode"
import { formatLargeNumber, formatDate } from "@/utils/format"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui"
import { Button, Checkbox } from "@/components/ui"
import { useAppTranslation } from "@/i18n/TranslationContext"
import { Tab, TabContent, TabHeader } from "../common/Tab"
@ -24,7 +24,16 @@ type HistoryViewProps = {
type SortOption = "newest" | "oldest" | "mostExpensive" | "mostTokens" | "mostRelevant"
const HistoryView = ({ onDone }: HistoryViewProps) => {
const { tasks, searchQuery, setSearchQuery, sortOption, setSortOption, setLastNonRelevantSort } = useTaskSearch()
const {
tasks,
searchQuery,
setSearchQuery,
sortOption,
setSortOption,
setLastNonRelevantSort,
showAllWorkspaces,
setShowAllWorkspaces,
} = useTaskSearch()
const { t } = useAppTranslation()
const [deleteTaskId, setDeleteTaskId] = useState<string | null>(null)
@ -147,21 +156,36 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
</VSCodeRadio>
</VSCodeRadioGroup>
<div className="flex items-center gap-2" onClick={() => setShowAllWorkspaces(!showAllWorkspaces)}>
<Checkbox
checked={showAllWorkspaces}
onCheckedChange={(checked) => setShowAllWorkspaces(checked === true)}
variant="description"
/>
<span className="text-vscode-foreground">{t("history:showAllWorkspaces")}</span>
</div>
{/* Select all control in selection mode */}
{isSelectionMode && tasks.length > 0 && (
<div className="flex items-center py-1 px-2 bg-vscode-editor-background rounded">
<VSCodeCheckbox
checked={tasks.length > 0 && selectedTaskIds.length === tasks.length}
onChange={(e) => toggleSelectAll((e.target as HTMLInputElement).checked)}
/>
<span className="ml-2 text-vscode-foreground">
{selectedTaskIds.length === tasks.length
? t("history:deselectAll")
: t("history:selectAll")}
</span>
<span className="ml-auto text-vscode-descriptionForeground text-xs">
{t("history:selectedItems", { selected: selectedTaskIds.length, total: tasks.length })}
</span>
<div className="flex items-center gap-2">
<Checkbox
checked={tasks.length > 0 && selectedTaskIds.length === tasks.length}
onCheckedChange={(checked) => toggleSelectAll(checked === true)}
variant="description"
/>
<span className="text-vscode-foreground">
{selectedTaskIds.length === tasks.length
? t("history:deselectAll")
: t("history:selectAll")}
</span>
<span className="ml-auto text-vscode-descriptionForeground text-xs">
{t("history:selectedItems", {
selected: selectedTaskIds.length,
total: tasks.length,
})}
</span>
</div>
</div>
)}
</div>
@ -203,11 +227,12 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
onClick={(e) => {
e.stopPropagation()
}}>
<VSCodeCheckbox
<Checkbox
checked={selectedTaskIds.includes(item.id)}
onChange={(e) =>
toggleTaskSelection(item.id, (e.target as HTMLInputElement).checked)
onCheckedChange={(checked) =>
toggleTaskSelection(item.id, checked === true)
}
variant="description"
/>
</div>
)}
@ -407,6 +432,13 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
)}
</div>
)}
{showAllWorkspaces && item.workspace && (
<div className="flex flex-row gap-1 text-vscode-descriptionForeground text-xs">
<span className="codicon codicon-folder scale-80" />
<span>{item.workspace}</span>
</div>
)}
</div>
</div>
</div>

View file

@ -7,10 +7,11 @@ import { useExtensionState } from "@/context/ExtensionStateContext"
type SortOption = "newest" | "oldest" | "mostExpensive" | "mostTokens" | "mostRelevant"
export const useTaskSearch = () => {
const { taskHistory } = useExtensionState()
const { taskHistory, cwd } = useExtensionState()
const [searchQuery, setSearchQuery] = useState("")
const [sortOption, setSortOption] = useState<SortOption>("newest")
const [lastNonRelevantSort, setLastNonRelevantSort] = useState<SortOption | null>("newest")
const [showAllWorkspaces, setShowAllWorkspaces] = useState(false)
useEffect(() => {
if (searchQuery && sortOption !== "mostRelevant" && !lastNonRelevantSort) {
@ -23,8 +24,12 @@ export const useTaskSearch = () => {
}, [searchQuery, sortOption, lastNonRelevantSort])
const presentableTasks = useMemo(() => {
return taskHistory.filter((item) => item.ts && item.task)
}, [taskHistory])
let tasks = taskHistory.filter((item) => item.ts && item.task)
if (!showAllWorkspaces) {
tasks = tasks.filter((item) => item.workspace === cwd)
}
return tasks
}, [taskHistory, showAllWorkspaces, cwd])
const fzf = useMemo(() => {
return new Fzf(presentableTasks, {
@ -34,19 +39,26 @@ export const useTaskSearch = () => {
const tasks = useMemo(() => {
let results = presentableTasks
if (searchQuery) {
const searchResults = fzf.find(searchQuery)
results = searchResults.map((result) => ({
...result.item,
task: highlightFzfMatch(result.item.task, Array.from(result.positions)),
}))
results = searchResults.map((result) => {
const positions = Array.from(result.positions)
const taskEndIndex = result.item.task.length
return {
...result.item,
task: highlightFzfMatch(
result.item.task,
positions.filter((p) => p < taskEndIndex),
),
workspace: result.item.workspace,
}
})
}
// First apply search if needed
const searchResults = searchQuery ? results : presentableTasks
// Then sort the results
return [...searchResults].sort((a, b) => {
return [...results].sort((a, b) => {
switch (sortOption) {
case "oldest":
return (a.ts || 0) - (b.ts || 0)
@ -74,5 +86,7 @@ export const useTaskSearch = () => {
setSortOption,
lastNonRelevantSort,
setLastNonRelevantSort,
showAllWorkspaces,
setShowAllWorkspaces,
}
}

View file

@ -0,0 +1,42 @@
"use client"
import * as React from "react"
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
import { Check } from "lucide-react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const checkboxVariants = cva(
"peer h-4 w-4 shrink-0 rounded-sm border ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
{
variants: {
variant: {
default:
"border-vscode-foreground data-[state=checked]:bg-vscode-foreground data-[state=checked]:text-primary-foreground",
description:
"border-vscode-descriptionForeground data-[state=checked]:bg-vscode-descriptionForeground data-[state=checked]:text-white",
},
},
defaultVariants: {
variant: "default",
},
},
)
export interface CheckboxProps
extends React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>,
VariantProps<typeof checkboxVariants> {}
const Checkbox = React.forwardRef<React.ElementRef<typeof CheckboxPrimitive.Root>, CheckboxProps>(
({ className, variant, ...props }, ref) => (
<CheckboxPrimitive.Root ref={ref} className={cn(checkboxVariants({ variant, className }))} {...props}>
<CheckboxPrimitive.Indicator className={cn("flex items-center justify-center text-current")}>
<Check className="h-4 w-4 text-vscode-background" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
),
)
Checkbox.displayName = CheckboxPrimitive.Root.displayName
export { Checkbox, checkboxVariants }

View file

@ -2,6 +2,7 @@ export * from "./alert-dialog"
export * from "./autosize-textarea"
export * from "./badge"
export * from "./button"
export * from "./checkbox"
export * from "./collapsible"
export * from "./command"
export * from "./dialog"

View file

@ -34,5 +34,6 @@
"deleteTasks": "Eliminar tasques",
"confirmDeleteTasks": "Estàs segur que vols eliminar {{count}} tasques?",
"deleteTasksWarning": "Les tasques eliminades no es poden recuperar. Si us plau, assegura't que vols continuar.",
"deleteItems": "Eliminar {{count}} elements"
"deleteItems": "Eliminar {{count}} elements",
"showAllWorkspaces": "Mostrar tasques de tots els espais de treball"
}

View file

@ -34,5 +34,6 @@
"deleteTasks": "Aufgaben löschen",
"confirmDeleteTasks": "Bist du sicher, dass du {{count}} Aufgaben löschen möchtest?",
"deleteTasksWarning": "Gelöschte Aufgaben können nicht wiederhergestellt werden. Bitte vergewissere dich, dass du fortfahren möchtest.",
"deleteItems": "{{count}} Elemente löschen"
"deleteItems": "{{count}} Elemente löschen",
"showAllWorkspaces": "Aufgaben aus allen Arbeitsbereichen anzeigen"
}

View file

@ -34,5 +34,6 @@
"deleteTasks": "Delete Tasks",
"confirmDeleteTasks": "Are you sure you want to delete {{count}} tasks?",
"deleteTasksWarning": "Deleted tasks cannot be recovered. Please make sure you want to proceed.",
"deleteItems": "Delete {{count}} Items"
"deleteItems": "Delete {{count}} Items",
"showAllWorkspaces": "Show tasks from all workspaces"
}

View file

@ -34,5 +34,6 @@
"deleteTasks": "Eliminar tareas",
"confirmDeleteTasks": "¿Estás seguro de que quieres eliminar {{count}} tareas?",
"deleteTasksWarning": "Las tareas eliminadas no se pueden recuperar. Por favor, asegúrate de que quieres continuar.",
"deleteItems": "Eliminar {{count}} elementos"
"deleteItems": "Eliminar {{count}} elementos",
"showAllWorkspaces": "Mostrar tareas de todos los espacios de trabajo"
}

View file

@ -34,5 +34,6 @@
"deleteTasks": "Supprimer les tâches",
"confirmDeleteTasks": "Êtes-vous sûr de vouloir supprimer {{count}} tâches ?",
"deleteTasksWarning": "Les tâches supprimées ne peuvent pas être récupérées. Veuillez confirmer que vous souhaitez continuer.",
"deleteItems": "Supprimer {{count}} éléments"
"deleteItems": "Supprimer {{count}} éléments",
"showAllWorkspaces": "Afficher les tâches de tous les espaces de travail"
}

View file

@ -34,5 +34,6 @@
"deleteTasks": "कार्य हटाएं",
"confirmDeleteTasks": "क्या आप वाकई {{count}} कार्य हटाना चाहते हैं?",
"deleteTasksWarning": "हटाए गए कार्य पुनर्प्राप्त नहीं किए जा सकते। कृपया सुनिश्चित करें कि आप आगे बढ़ना चाहते हैं।",
"deleteItems": "{{count}} आइटम हटाएं"
"deleteItems": "{{count}} आइटम हटाएं",
"showAllWorkspaces": "सभी वर्कस्पेस से कार्य दिखाएं"
}

View file

@ -34,5 +34,6 @@
"deleteTasks": "Elimina attività",
"confirmDeleteTasks": "Sei sicuro di voler eliminare {{count}} attività?",
"deleteTasksWarning": "Le attività eliminate non possono essere recuperate. Assicurati di voler continuare.",
"deleteItems": "Elimina {{count}} elementi"
"deleteItems": "Elimina {{count}} elementi",
"showAllWorkspaces": "Mostra attività da tutti gli spazi di lavoro"
}

View file

@ -34,5 +34,6 @@
"deleteTasks": "タスクを削除",
"confirmDeleteTasks": "{{count}} 件のタスクを削除してもよろしいですか?",
"deleteTasksWarning": "削除されたタスクは復元できません。続行してもよろしいですか?",
"deleteItems": "{{count}} 項目を削除"
"deleteItems": "{{count}} 項目を削除",
"showAllWorkspaces": "すべてのワークスペースのタスクを表示"
}

View file

@ -34,5 +34,6 @@
"deleteTasks": "작업 삭제",
"confirmDeleteTasks": "{{count}}개의 작업을 삭제하시겠습니까?",
"deleteTasksWarning": "삭제된 작업은 복구할 수 없습니다. 계속 진행하시겠습니까?",
"deleteItems": "{{count}}개 항목 삭제"
"deleteItems": "{{count}}개 항목 삭제",
"showAllWorkspaces": "모든 워크스페이스의 작업 표시"
}

View file

@ -34,5 +34,6 @@
"deleteTasks": "Usuń zadania",
"confirmDeleteTasks": "Czy na pewno chcesz usunąć {{count}} zadań?",
"deleteTasksWarning": "Usuniętych zadań nie można przywrócić. Upewnij się, że chcesz kontynuować.",
"deleteItems": "Usuń {{count}} elementów"
"deleteItems": "Usuń {{count}} elementów",
"showAllWorkspaces": "Pokaż zadania ze wszystkich przestrzeni roboczych"
}

View file

@ -34,5 +34,6 @@
"deleteTasks": "Excluir tarefas",
"confirmDeleteTasks": "Tem certeza que deseja excluir {{count}} tarefas?",
"deleteTasksWarning": "As tarefas excluídas não podem ser recuperadas. Por favor, certifique-se de que deseja prosseguir.",
"deleteItems": "Excluir {{count}} itens"
"deleteItems": "Excluir {{count}} itens",
"showAllWorkspaces": "Mostrar tarefas de todos os espaços de trabalho"
}

View file

@ -34,5 +34,6 @@
"deleteTasks": "Görevleri Sil",
"confirmDeleteTasks": "{{count}} görevi silmek istediğinizden emin misiniz?",
"deleteTasksWarning": "Silinen görevler geri alınamaz. Lütfen devam etmek istediğinizden emin olun.",
"deleteItems": "{{count}} Öğeyi Sil"
"deleteItems": "{{count}} Öğeyi Sil",
"showAllWorkspaces": "Tüm çalışma alanlarından görevleri göster"
}

View file

@ -34,5 +34,6 @@
"deleteTasks": "Xóa nhiệm vụ",
"confirmDeleteTasks": "Bạn có chắc chắn muốn xóa {{count}} nhiệm vụ không?",
"deleteTasksWarning": "Các nhiệm vụ đã xóa không thể khôi phục. Vui lòng chắc chắn bạn muốn tiếp tục.",
"deleteItems": "Xóa {{count}} mục"
"deleteItems": "Xóa {{count}} mục",
"showAllWorkspaces": "Hiển thị nhiệm vụ từ tất cả không gian làm việc"
}

View file

@ -34,5 +34,6 @@
"deleteTasks": "删除任务",
"confirmDeleteTasks": "确认删除 {{count}} 项任务?",
"deleteTasksWarning": "删除后将无法恢复,请谨慎操作。",
"deleteItems": "删除 {{count}} 项"
"deleteItems": "删除 {{count}} 项",
"showAllWorkspaces": "显示所有工作区的任务"
}

View file

@ -34,5 +34,6 @@
"deleteTasks": "刪除工作",
"confirmDeleteTasks": "確定要刪除 {{count}} 個工作嗎?",
"deleteTasksWarning": "已刪除的工作無法還原。請確認是否要繼續。",
"deleteItems": "刪除 {{count}} 個項目"
"deleteItems": "刪除 {{count}} 個項目",
"showAllWorkspaces": "顯示所有工作區的工作"
}