Skip to content

Commit

Permalink
Light UI polish
Browse files Browse the repository at this point in the history
  • Loading branch information
ColinMcNeil committed Mar 4, 2025
1 parent d687a16 commit cf30bb0
Showing 3 changed files with 37 additions and 15 deletions.
3 changes: 2 additions & 1 deletion src/extension/ui/src/Secrets.ts
Original file line number Diff line number Diff line change
@@ -28,7 +28,8 @@ namespace Secrets {

export async function addSecret(client: v1.DockerDesktopClient, secret: Secret): Promise<void> {
try {
await client.extension.host?.cli.exec('host-binary', ['--name', secret.name, '--value', secret.value]);
const response = await client.extension.host?.cli.exec('host-binary', ['--name', secret.name, '--value', secret.value]);
console.log('Response', response)
client.desktopUI.toast.success('Secret set successfully')
} catch (error) {
client.desktopUI.toast.error('Failed to set secret: ' + error)
34 changes: 23 additions & 11 deletions src/extension/ui/src/components/CatalogGrid.tsx
Original file line number Diff line number Diff line change
@@ -26,14 +26,26 @@ const filterCatalog = (catalogItems: CatalogItemWithName[], registryItems: { [ke

const NEVER_SHOW_AGAIN_KEY = 'registry-sync-never-show-again';

const debounce = (func: (...args: any[]) => void, delay: number) => {
let timeout: NodeJS.Timeout;
return (...args: any[]) => {
clearTimeout(timeout);
timeout = setTimeout(() => func(...args), delay);
const debounce = (inner: (...args: any[]) => Promise<void>, ms = 0) => {
let timer: NodeJS.Timeout | null = null;
let resolves: ((value: void | PromiseLike<void>) => void)[] = [];

return function (...args: any[]) {
// Run the function after a certain amount of time
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
// Get the result of the inner function, then apply it to the resolve function of
// each promise that has been created since the last time the inner function was run
let result = inner(...args);
resolves.forEach(r => r(result));
resolves = [];
}, ms);

return new Promise(r => resolves.push(r));
};
}

export const CatalogGrid: React.FC<CatalogGridProps> = ({
registryItems,
canRegister,
@@ -81,8 +93,8 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
setSecrets(response || []);
}

const debouncedAddSecret = debounce((client: v1.DockerDesktopClient, name: string, value: string) => {
Secrets.addSecret(client, { name, value, policies: [MCP_POLICY_NAME] })
const debouncedAddSecret = debounce(async (client: v1.DockerDesktopClient, name: string, value: string) => {
await Secrets.addSecret(client, { name, value, policies: [MCP_POLICY_NAME] })
loadSecrets();
}, 1000);

@@ -205,8 +217,8 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
registered={Object.keys(registryItems).some((i) => i === item.name)}
register={registerCatalogItem}
unregister={unregisterCatalogItem}
onSecretChange={(secret) => {
debouncedAddSecret(client, secret.name, secret.value);
onSecretChange={async (secret) => {
await debouncedAddSecret(client, secret.name, secret.value);
}}
secrets={secrets}
/>
@@ -229,7 +241,7 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
name.toLowerCase().includes(search.toLowerCase()) && <Grid2 size={{ xs: 12, sm: 6, md: 4 }} key={name}>
<CatalogItemCard item={catalogItems.find((i) => i.name === name)!} openUrl={() => {
client.host.openExternal(Ref.fromRef(item.ref).toURL(true));
}} canRegister={canRegister} registered={true} register={registerCatalogItem} unregister={unregisterCatalogItem} onSecretChange={(secret) => {
}} canRegister={canRegister} registered={true} register={registerCatalogItem} unregister={unregisterCatalogItem} onSecretChange={async (secret) => {
debouncedAddSecret(client, secret.name, secret.value);
}} secrets={secrets} />
</Grid2>
15 changes: 12 additions & 3 deletions src/extension/ui/src/components/PromptCard.tsx
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ export interface CatalogItemWithName extends CatalogItem {
name: string;
}

export function CatalogItemCard({ openUrl, item, canRegister, registered, register, unregister, onSecretChange, secrets }: { openUrl: () => void, item: CatalogItemWithName, canRegister: boolean, registered: boolean, register: (item: CatalogItemWithName) => Promise<void>, unregister: (item: CatalogItemWithName) => Promise<void>, onSecretChange: (secret: { name: string, value: string }) => void, secrets: Secrets.Secret[] }) {
export function CatalogItemCard({ openUrl, item, canRegister, registered, register, unregister, onSecretChange, secrets }: { openUrl: () => void, item: CatalogItemWithName, canRegister: boolean, registered: boolean, register: (item: CatalogItemWithName) => Promise<void>, unregister: (item: CatalogItemWithName) => Promise<void>, onSecretChange: (secret: { name: string, value: string }) => Promise<void>, secrets: Secrets.Secret[] }) {
const loadAssignedSecrets = () => {
const assignedSecrets = Secrets.getAssignedSecrets(item, secrets);
setAssignedSecrets(assignedSecrets)
@@ -32,6 +32,7 @@ export function CatalogItemCard({ openUrl, item, canRegister, registered, regist
const [showSecretDialog, setShowSecretDialog] = useState(false)
const [assignedSecrets, setAssignedSecrets] = useState<{ name: string, assigned: boolean }[]>([])
const [changedSecrets, setChangedSecrets] = useState<{ [key: string]: string | undefined }>({})
const [secretLoading, setSecretLoading] = useState(false)

useEffect(() => {
loadAssignedSecrets()
@@ -53,8 +54,16 @@ export function CatalogItemCard({ openUrl, item, canRegister, registered, regist
{assignedSecrets.find(s => s.name === secret.name)?.assigned && changedSecrets[secret.name] && <IconButton onClick={() => setChangedSecrets({ ...changedSecrets, [secret.name]: undefined })}>
<LockReset />
</IconButton>}
{changedSecrets[secret.name] && <IconButton onClick={() => onSecretChange({ name: secret.name, value: changedSecrets[secret.name] || '' })}>
<Save />
{changedSecrets[secret.name] && <IconButton onClick={() => {
setSecretLoading(true)
onSecretChange({ name: secret.name, value: changedSecrets[secret.name] || '' }).then(() => {
setSecretLoading(false)
const newChangedSecrets = { ...changedSecrets }
delete newChangedSecrets[secret.name]
setChangedSecrets(newChangedSecrets)
})
}}>
{secretLoading ? <CircularProgress size={20} /> : <Save />}
</IconButton>}
</Stack>
))}

0 comments on commit cf30bb0

Please sign in to comment.