Skip to content

Commit 2d297e3

Browse files
committed
feat: add resource deletion functionality and update localization strings
1 parent 54a9da3 commit 2d297e3

5 files changed

Lines changed: 160 additions & 13 deletions

File tree

apps/desktop/src/locales/en/common.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,14 @@
5555
"type": "Type",
5656
"domain": "Domain",
5757
"version": "Version",
58-
"description": "Description"
58+
"description": "Description",
59+
"config": "Config",
60+
"dangerZone": "Danger Zone",
61+
"deleteResource": "Delete Resource",
62+
"deleteResourceDesc": "Once deleted, this resource will be removed from the local registry and cannot be recovered",
63+
"deleteConfirmMessage": "This action cannot be undone. Please enter the full resource locator to confirm deletion:",
64+
"deleteConfirmLabel": "Enter resource locator to confirm",
65+
"confirmDelete": "Confirm Delete"
5966
},
6067
"settings": {
6168
"title": "Settings",

apps/desktop/src/locales/zh-CN/common.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,14 @@
6060
"type": "类型",
6161
"domain": "域名",
6262
"version": "版本",
63-
"description": "描述"
63+
"description": "描述",
64+
"config": "配置",
65+
"dangerZone": "危险操作",
66+
"deleteResource": "删除资源",
67+
"deleteResourceDesc": "删除后无法恢复,资源将从本地注册表中移除",
68+
"deleteConfirmMessage": "此操作不可恢复。请输入完整的资源定位符以确认删除:",
69+
"deleteConfirmLabel": "请输入资源定位符确认",
70+
"confirmDelete": "确认删除"
6471
},
6572
"settings": {
6673
"title": "设置",

apps/desktop/src/pages/Resources/ResourceDetail.tsx

Lines changed: 136 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { useState, useEffect } from "react";
22
import { useTranslation } from "react-i18next";
3-
import { ArrowLeft, Play, Copy, Check, Loader2, AlertCircle, FileText, Wrench, Package } from "lucide-react";
3+
import { ArrowLeft, Play, Copy, Check, Loader2, AlertCircle, FileText, Wrench, Package, Trash2 } from "lucide-react";
44
import { cn } from "@/lib/utils";
5-
import { useResourceDetail, useResourceResolve } from "@/hooks/useResources";
5+
import { useResourceDetail, useResourceResolve, useResourceDelete } from "@/hooks/useResources";
66
import type { ResolveResponse } from "agentvm/client";
77

88
interface ResourceDetailProps {
99
locator: string;
1010
onBack: () => void;
1111
}
1212

13-
type TabType = "resolve" | "content" | "versions";
13+
type TabType = "resolve" | "content" | "versions" | "config";
1414

1515
/** JSON Schema property definition */
1616
interface JSONSchemaProperty {
@@ -48,10 +48,13 @@ export function ResourceDetail({ locator, onBack }: ResourceDetailProps) {
4848
const [result, setResult] = useState<ResolveResponse | null>(null);
4949
const [copied, setCopied] = useState(false);
5050
const [executeError, setExecuteError] = useState<string | null>(null);
51+
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
52+
const [deleteConfirmInput, setDeleteConfirmInput] = useState("");
5153

5254
// Load resource details
5355
const { data: resource, isLoading: loading, isError, error } = useResourceDetail(locator);
5456
const resolveMutation = useResourceResolve();
57+
const deleteMutation = useResourceDelete();
5558

5659
const config = typeConfig[resource?.manifest.type || "default"] || typeConfig.default;
5760
const Icon = config.icon;
@@ -121,8 +124,21 @@ export function ResourceDetail({ locator, onBack }: ResourceDetailProps) {
121124
{ id: "resolve", labelKey: "resources.resolve" },
122125
{ id: "content", labelKey: "resources.content" },
123126
{ id: "versions", labelKey: "resources.versions" },
127+
{ id: "config", labelKey: "resources.config" },
124128
];
125129

130+
const handleDelete = async () => {
131+
if (deleteConfirmInput !== locator) return;
132+
133+
try {
134+
await deleteMutation.mutateAsync(locator);
135+
setShowDeleteConfirm(false);
136+
onBack();
137+
} catch (err) {
138+
// Error handled by mutation
139+
}
140+
};
141+
126142
const schema = resource?.schema as JSONSchema | undefined;
127143
const hasSchema = schema?.properties && Object.keys(schema.properties).length > 0;
128144

@@ -346,6 +362,38 @@ export function ResourceDetail({ locator, onBack }: ResourceDetailProps) {
346362
</p>
347363
</div>
348364
)}
365+
366+
{activeTab === "config" && (
367+
<div className="space-y-6">
368+
{/* Danger Zone */}
369+
<div className="rounded-lg border border-[var(--accent-error)]/30 overflow-hidden">
370+
<div className="px-4 py-3 bg-[var(--accent-error)]/5 border-b border-[var(--accent-error)]/30">
371+
<h3 className="text-sm font-medium text-[var(--accent-error)]">
372+
{t("resources.dangerZone")}
373+
</h3>
374+
</div>
375+
<div className="p-4">
376+
<div className="flex items-center justify-between">
377+
<div>
378+
<p className="text-sm font-medium text-[var(--text-primary)]">
379+
{t("resources.deleteResource")}
380+
</p>
381+
<p className="text-xs text-[var(--text-muted)] mt-1">
382+
{t("resources.deleteResourceDesc")}
383+
</p>
384+
</div>
385+
<button
386+
onClick={() => setShowDeleteConfirm(true)}
387+
className="h-8 px-3 rounded-lg flex items-center gap-2 text-sm font-medium border border-[var(--accent-error)] text-[var(--accent-error)] hover:bg-[var(--accent-error)] hover:text-white transition-colors"
388+
>
389+
<Trash2 className="w-4 h-4" />
390+
{t("common.delete")}
391+
</button>
392+
</div>
393+
</div>
394+
</div>
395+
</div>
396+
)}
349397
</div>
350398

351399
{/* About Panel - 紧贴内容 */}
@@ -397,6 +445,91 @@ export function ResourceDetail({ locator, onBack }: ResourceDetailProps) {
397445
</div>
398446
</div>
399447
</div>
448+
449+
{/* Delete Confirmation Modal */}
450+
{showDeleteConfirm && (
451+
<div className="fixed inset-0 bg-black/30 flex items-center justify-center z-50">
452+
<div className="bg-[var(--bg-card)] rounded-xl w-[480px] shadow-xl border border-[var(--border-light)]">
453+
{/* Header */}
454+
<div className="px-4 py-3 border-b border-[var(--border-light)]">
455+
<h2 className="text-base font-medium text-[var(--accent-error)]">
456+
{t("resources.deleteResource")}
457+
</h2>
458+
</div>
459+
460+
{/* Content */}
461+
<div className="p-4 space-y-4">
462+
<p className="text-sm text-[var(--text-secondary)]">
463+
{t("resources.deleteConfirmMessage")}
464+
</p>
465+
466+
<div className="p-3 bg-[var(--bg-secondary)] rounded-lg">
467+
<code className="text-sm font-mono text-[var(--text-primary)] break-all">
468+
{locator}
469+
</code>
470+
</div>
471+
472+
<div>
473+
<label className="block text-sm text-[var(--text-secondary)] mb-2">
474+
{t("resources.deleteConfirmLabel")}
475+
</label>
476+
<input
477+
type="text"
478+
value={deleteConfirmInput}
479+
onChange={(e) => setDeleteConfirmInput(e.target.value)}
480+
placeholder={locator}
481+
className={cn(
482+
"w-full h-10 px-3 rounded-lg",
483+
"bg-[var(--bg-card)] border",
484+
deleteConfirmInput === locator
485+
? "border-[var(--accent-error)]"
486+
: "border-[var(--border-light)]",
487+
"text-sm text-[var(--text-primary)] placeholder:text-[var(--text-muted)]",
488+
"outline-none focus:border-[var(--border-medium)] transition-colors"
489+
)}
490+
/>
491+
</div>
492+
493+
{deleteMutation.isError && (
494+
<div className="p-3 bg-red-50 rounded-lg flex items-start gap-2">
495+
<AlertCircle className="w-4 h-4 text-red-600 shrink-0 mt-0.5" />
496+
<p className="text-sm text-red-600">
497+
{deleteMutation.error instanceof Error
498+
? deleteMutation.error.message
499+
: t("common.error")}
500+
</p>
501+
</div>
502+
)}
503+
</div>
504+
505+
{/* Footer */}
506+
<div className="border-t border-[var(--border-light)] p-3 flex justify-end gap-2">
507+
<button
508+
onClick={() => {
509+
setShowDeleteConfirm(false);
510+
setDeleteConfirmInput("");
511+
}}
512+
className="h-8 px-4 rounded text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] transition-colors"
513+
>
514+
{t("common.cancel")}
515+
</button>
516+
<button
517+
onClick={handleDelete}
518+
disabled={deleteConfirmInput !== locator || deleteMutation.isPending}
519+
className={cn(
520+
"h-8 px-4 rounded text-sm font-medium transition-colors flex items-center gap-2",
521+
deleteConfirmInput === locator && !deleteMutation.isPending
522+
? "bg-[var(--accent-error)] text-white hover:bg-[var(--accent-error)]/90"
523+
: "bg-[var(--bg-tertiary)] text-[var(--text-muted)] cursor-not-allowed"
524+
)}
525+
>
526+
{deleteMutation.isPending && <Loader2 className="w-4 h-4 animate-spin" />}
527+
{t("resources.confirmDelete")}
528+
</button>
529+
</div>
530+
</div>
531+
</div>
532+
)}
400533
</div>
401534
);
402535
}

bun.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,6 @@
5151
"bun": ">=1.3.0"
5252
},
5353
"dependencies": {
54-
"resourcexjs": "^1.5.0"
54+
"resourcexjs": "^1.6.0"
5555
}
5656
}

0 commit comments

Comments
 (0)