mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 06:05:24 +00:00
feat: redesign user host defaults system
This commit is contained in:
1 parent
3028ab1176
commit
a4a4d4f27b
144 files changed
+24609
-2555
No files matched your search
@@ -108,7 +108,8 @@
|
||||
"descriptionKey": "host.startupDescription",
|
||||
"hidden": true,
|
||||
"min": 1,
|
||||
"shareRead": "view"
|
||||
"shareRead": "view",
|
||||
"defaultLevels": ["user", "folder"]
|
||||
},
|
||||
{
|
||||
"key": "quickActions",
|
||||
@@ -117,7 +118,8 @@
|
||||
"descriptionKey": "host.quickActionsDescription",
|
||||
"hidden": true,
|
||||
"default": [],
|
||||
"shareRead": "view"
|
||||
"shareRead": "view",
|
||||
"defaultLevels": ["user", "folder"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,7 +3,14 @@ import {
|
||||
useTranslation,
|
||||
type HostEditorSectionProps,
|
||||
} from "@termix/plugin-sdk/frontend";
|
||||
import { Button, Input, SectionCard, Select2 } from "@termix/plugin-sdk/ui";
|
||||
import {
|
||||
Button,
|
||||
HostDefaultBadge,
|
||||
HostDefaultField,
|
||||
Input,
|
||||
SectionCard,
|
||||
Select2,
|
||||
} from "@termix/plugin-sdk/ui";
|
||||
import { PLUGIN_ID, readStartupSnippetId } from "../shared/host-settings.js";
|
||||
import { useSnippetOptions } from "./use-snippet-options";
|
||||
|
||||
@@ -62,110 +69,119 @@ export function HostSnippetsSection({
|
||||
|
||||
return (
|
||||
<>
|
||||
<SectionCard
|
||||
title={t("host.startupTitle")}
|
||||
icon={<Play className="size-3.5" />}
|
||||
>
|
||||
<div className="flex flex-col gap-1.5 py-3">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("host.startupDescription")}
|
||||
</p>
|
||||
<Select2
|
||||
aria-label={t("host.startupTitle")}
|
||||
value={startupSnippetId ?? ""}
|
||||
onChange={(e) =>
|
||||
write({
|
||||
startupSnippetId: e.target.value
|
||||
? Number(e.target.value)
|
||||
: null,
|
||||
})
|
||||
}
|
||||
className="flex h-9 w-full border border-border bg-background px-3 py-1 text-xs outline-none focus:ring-1 focus:ring-ring"
|
||||
>
|
||||
<option value="">{t("host.none")}</option>
|
||||
{options.map((snippet) => (
|
||||
<option key={snippet.id} value={snippet.id}>
|
||||
{snippet.name}
|
||||
</option>
|
||||
))}
|
||||
</Select2>
|
||||
</div>
|
||||
</SectionCard>
|
||||
<SectionCard
|
||||
title={t("host.quickActionsTitle")}
|
||||
icon={<Zap className="size-3.5" />}
|
||||
action={
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-6 text-[10px] px-2 border-accent-brand/40 text-accent-brand"
|
||||
onClick={() =>
|
||||
setQuickActions([...quickActions, { name: "", snippetId: null }])
|
||||
}
|
||||
>
|
||||
<Plus className="size-3 mr-1" /> {t("host.addQuickAction")}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<div className="flex flex-col gap-3 py-3">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("host.quickActionsDescription")}
|
||||
</p>
|
||||
{quickActions.length === 0 && (
|
||||
<div className="flex flex-col items-center justify-center py-4 text-muted-foreground/40 gap-1.5">
|
||||
<Zap className="size-6" />
|
||||
<span className="text-xs">{t("host.noQuickActions")}</span>
|
||||
</div>
|
||||
)}
|
||||
{quickActions.map((row, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center gap-2 p-2 bg-muted/20 border border-border group"
|
||||
<HostDefaultField settingKey="startupSnippetId">
|
||||
<SectionCard
|
||||
title={t("host.startupTitle")}
|
||||
icon={<Play className="size-3.5" />}
|
||||
>
|
||||
<div className="flex flex-col gap-1.5 py-3">
|
||||
<p className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
{t("host.startupDescription")}
|
||||
<HostDefaultBadge settingKey="startupSnippetId" />
|
||||
</p>
|
||||
<Select2
|
||||
aria-label={t("host.startupTitle")}
|
||||
value={startupSnippetId ?? ""}
|
||||
onChange={(e) =>
|
||||
write({
|
||||
startupSnippetId: e.target.value
|
||||
? Number(e.target.value)
|
||||
: null,
|
||||
})
|
||||
}
|
||||
className="flex h-9 w-full border border-border bg-background px-3 py-1 text-xs outline-none focus:ring-1 focus:ring-ring"
|
||||
>
|
||||
<Input
|
||||
className="h-7 text-xs flex-1"
|
||||
placeholder={t("host.buttonLabel")}
|
||||
value={row.name}
|
||||
onChange={(e) => {
|
||||
const next = [...quickActions];
|
||||
next[index] = { ...row, name: e.target.value };
|
||||
setQuickActions(next);
|
||||
}}
|
||||
/>
|
||||
<Select2
|
||||
aria-label={t("host.selectSnippet")}
|
||||
className="h-7 text-xs flex-1 border border-border bg-background px-2 outline-none focus:ring-1 focus:ring-ring"
|
||||
value={row.snippetId ?? ""}
|
||||
onChange={(e) => {
|
||||
const next = [...quickActions];
|
||||
next[index] = {
|
||||
...row,
|
||||
snippetId: e.target.value ? Number(e.target.value) : null,
|
||||
};
|
||||
setQuickActions(next);
|
||||
}}
|
||||
<option value="">{t("host.none")}</option>
|
||||
{options.map((snippet) => (
|
||||
<option key={snippet.id} value={snippet.id}>
|
||||
{snippet.name}
|
||||
</option>
|
||||
))}
|
||||
</Select2>
|
||||
</div>
|
||||
</SectionCard>
|
||||
</HostDefaultField>
|
||||
<HostDefaultField settingKey="quickActions">
|
||||
<SectionCard
|
||||
title={t("host.quickActionsTitle")}
|
||||
icon={<Zap className="size-3.5" />}
|
||||
action={
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-6 text-[10px] px-2 border-accent-brand/40 text-accent-brand"
|
||||
onClick={() =>
|
||||
setQuickActions([
|
||||
...quickActions,
|
||||
{ name: "", snippetId: null },
|
||||
])
|
||||
}
|
||||
>
|
||||
<Plus className="size-3 mr-1" /> {t("host.addQuickAction")}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<div className="flex flex-col gap-3 py-3">
|
||||
<p className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
{t("host.quickActionsDescription")}
|
||||
<HostDefaultBadge settingKey="quickActions" />
|
||||
</p>
|
||||
{quickActions.length === 0 && (
|
||||
<div className="flex flex-col items-center justify-center py-4 text-muted-foreground/40 gap-1.5">
|
||||
<Zap className="size-6" />
|
||||
<span className="text-xs">{t("host.noQuickActions")}</span>
|
||||
</div>
|
||||
)}
|
||||
{quickActions.map((row, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center gap-2 p-2 bg-muted/20 border border-border group"
|
||||
>
|
||||
<option value="">{t("host.selectSnippet")}</option>
|
||||
{options.map((snippet) => (
|
||||
<option key={snippet.id} value={snippet.id}>
|
||||
{snippet.name}
|
||||
</option>
|
||||
))}
|
||||
</Select2>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t("host.removeQuickAction")}
|
||||
className="text-muted-foreground hover:text-destructive opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
onClick={() =>
|
||||
setQuickActions(quickActions.filter((_, i) => i !== index))
|
||||
}
|
||||
>
|
||||
<Trash2 className="size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</SectionCard>
|
||||
<Input
|
||||
className="h-7 text-xs flex-1"
|
||||
placeholder={t("host.buttonLabel")}
|
||||
value={row.name}
|
||||
onChange={(e) => {
|
||||
const next = [...quickActions];
|
||||
next[index] = { ...row, name: e.target.value };
|
||||
setQuickActions(next);
|
||||
}}
|
||||
/>
|
||||
<Select2
|
||||
aria-label={t("host.selectSnippet")}
|
||||
className="h-7 text-xs flex-1 border border-border bg-background px-2 outline-none focus:ring-1 focus:ring-ring"
|
||||
value={row.snippetId ?? ""}
|
||||
onChange={(e) => {
|
||||
const next = [...quickActions];
|
||||
next[index] = {
|
||||
...row,
|
||||
snippetId: e.target.value ? Number(e.target.value) : null,
|
||||
};
|
||||
setQuickActions(next);
|
||||
}}
|
||||
>
|
||||
<option value="">{t("host.selectSnippet")}</option>
|
||||
{options.map((snippet) => (
|
||||
<option key={snippet.id} value={snippet.id}>
|
||||
{snippet.name}
|
||||
</option>
|
||||
))}
|
||||
</Select2>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t("host.removeQuickAction")}
|
||||
className="text-muted-foreground hover:text-destructive opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
onClick={() =>
|
||||
setQuickActions(quickActions.filter((_, i) => i !== index))
|
||||
}
|
||||
>
|
||||
<Trash2 className="size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</SectionCard>
|
||||
</HostDefaultField>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -127,6 +127,7 @@ export function activate(app: TermixApp): void {
|
||||
titleKey: "host.tabTitle",
|
||||
icon: Play,
|
||||
order: 15,
|
||||
defaults: true,
|
||||
component: HostSnippetsSection,
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user