feat: redesign user host defaults system

This commit is contained in:
LukeGus committed 2026-09-30 06:19:56 -05:00
1 parent 3028ab1176
commit a4a4d4f27b
144 files changed
+24609 -2555

No files matched your search

+4 -2
View File
@@ -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>
</>
);
}
+1
View File
@@ -127,6 +127,7 @@ export function activate(app: TermixApp): void {
titleKey: "host.tabTitle",
icon: Play,
order: 15,
defaults: true,
component: HostSnippetsSection,
});