mirror of
https://github.com/ByteAtATime/raycast-linux.git
synced 2025-08-31 11:17:27 +00:00
feat(settings): Implement remaining preference types
This commit introduces support for all remaining preference types as specified in the API, including password, checkbox, appPicker, file, and directory. Eventually, we will probably have to somehow make the password preferences encrypted.
This commit is contained in:
parent
f2191cbdb4
commit
1bdb0f534e
4 changed files with 88 additions and 21 deletions
|
@ -121,7 +121,7 @@ export const PreferenceSchema = z.object({
|
|||
name: z.string(),
|
||||
title: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
type: z.enum(['textfield', 'dropdown', 'checkbox', 'directory']),
|
||||
type: z.enum(['textfield', 'password', 'checkbox', 'dropdown', 'appPicker', 'file', 'directory']),
|
||||
required: z.boolean().optional(),
|
||||
default: z.union([z.string(), z.boolean()]).optional(),
|
||||
data: z
|
||||
|
|
|
@ -27,6 +27,7 @@ import { showHUD } from './hud';
|
|||
import { BrowserExtensionAPI } from './browserExtension';
|
||||
import { Clipboard } from './clipboard';
|
||||
import * as OAuth from './oauth';
|
||||
import type { Preference } from '@raycast-linux/protocol';
|
||||
|
||||
const Image = {
|
||||
Mask: {
|
||||
|
@ -36,20 +37,9 @@ const Image = {
|
|||
};
|
||||
|
||||
let currentPluginName: string | null = null;
|
||||
let currentPluginPreferences: Array<{
|
||||
name: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
type: 'textfield' | 'dropdown' | 'checkbox' | 'directory';
|
||||
required?: boolean;
|
||||
default?: string | boolean;
|
||||
data?: Array<{ title: string; value: string }>;
|
||||
}> = [];
|
||||
let currentPluginPreferences: Preference[] = [];
|
||||
|
||||
export const setCurrentPlugin = (
|
||||
pluginName: string,
|
||||
preferences?: typeof currentPluginPreferences
|
||||
) => {
|
||||
export const setCurrentPlugin = (pluginName: string, preferences?: Preference[]) => {
|
||||
currentPluginName = pluginName;
|
||||
currentPluginPreferences = preferences || [];
|
||||
};
|
||||
|
|
32
src/lib/components/PasswordInput.svelte
Normal file
32
src/lib/components/PasswordInput.svelte
Normal file
|
@ -0,0 +1,32 @@
|
|||
<script lang="ts">
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Eye, EyeOff } from '@lucide/svelte';
|
||||
import type { HTMLInputAttributes } from 'svelte/elements';
|
||||
|
||||
type Props = {
|
||||
value: string | undefined | null;
|
||||
} & HTMLInputAttributes;
|
||||
|
||||
let { value, ...restProps }: Props = $props();
|
||||
let showPassword = $state(false);
|
||||
const inputType = $derived(showPassword ? 'text' : 'password');
|
||||
</script>
|
||||
|
||||
<div class="relative">
|
||||
<Input {...restProps} {value} type={inputType} class="pr-10" />
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
class="absolute top-1/2 right-0 h-full w-10 -translate-y-1/2 rounded-lg"
|
||||
onclick={() => (showPassword = !showPassword)}
|
||||
aria-label={showPassword ? 'Hide password' : 'Show password'}
|
||||
>
|
||||
{#if showPassword}
|
||||
<EyeOff class="size-4" />
|
||||
{:else}
|
||||
<Eye class="size-4" />
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
|
@ -7,6 +7,9 @@
|
|||
import BaseList from './BaseList.svelte';
|
||||
import { Button } from './ui/button';
|
||||
import path from 'path';
|
||||
import { open as openDialog } from '@tauri-apps/plugin-dialog';
|
||||
import { appsStore } from '$lib/apps.svelte';
|
||||
import PasswordInput from './PasswordInput.svelte';
|
||||
|
||||
type Props = {
|
||||
plugins: PluginInfo[];
|
||||
|
@ -30,6 +33,7 @@
|
|||
let selectedIndex = $state(0);
|
||||
let preferenceValues = $state<Record<string, unknown>>({});
|
||||
let searchText = $state('');
|
||||
const { apps } = $derived(appsStore);
|
||||
|
||||
$effect(() => {
|
||||
// This effect syncs the local preference values with the prop.
|
||||
|
@ -135,6 +139,16 @@
|
|||
function getPreferenceValue(pref: Preference): unknown {
|
||||
return (preferenceValues as Record<string, unknown>)[pref.name] ?? pref.default ?? '';
|
||||
}
|
||||
|
||||
async function browse(type: 'file' | 'directory', prefName: string) {
|
||||
const result = await openDialog({
|
||||
directory: type === 'directory',
|
||||
multiple: false
|
||||
});
|
||||
if (typeof result === 'string') {
|
||||
handlePreferenceChange(prefName, result);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window onkeydown={handleKeydown} />
|
||||
|
@ -222,6 +236,13 @@
|
|||
handlePreferenceChange(pref.name, (e.target as HTMLInputElement)?.value)}
|
||||
placeholder={pref.default as string}
|
||||
/>
|
||||
{:else if pref.type === 'password'}
|
||||
<PasswordInput
|
||||
value={getPreferenceValue(pref) as string}
|
||||
onchange={(e) =>
|
||||
handlePreferenceChange(pref.name, (e.target as HTMLInputElement)?.value)}
|
||||
placeholder="••••••••••••"
|
||||
/>
|
||||
{:else if pref.type === 'checkbox'}
|
||||
<label class="flex items-center gap-2">
|
||||
<Checkbox
|
||||
|
@ -250,13 +271,37 @@
|
|||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
{:else if pref.type === 'directory'}
|
||||
<Input
|
||||
value={getPreferenceValue(pref) as string}
|
||||
onchange={(e) =>
|
||||
handlePreferenceChange(pref.name, (e.target as HTMLInputElement)?.value)}
|
||||
placeholder={pref.default as string}
|
||||
/>
|
||||
{:else if pref.type === 'appPicker'}
|
||||
<Select.Root
|
||||
value={(getPreferenceValue(pref) as string) || undefined}
|
||||
onValueChange={(value) => handlePreferenceChange(pref.name, value)}
|
||||
>
|
||||
<Select.Trigger class="w-full">
|
||||
{@const selectedApp = apps.find((a) => a.exec === getPreferenceValue(pref))}
|
||||
{selectedApp?.name ?? 'Select Application'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each apps as app (app.exec)}
|
||||
<Select.Item value={app.exec}>{app.name}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
{:else if pref.type === 'file' || pref.type === 'directory'}
|
||||
<div class="flex items-center gap-2">
|
||||
<Input
|
||||
value={getPreferenceValue(pref) as string}
|
||||
onchange={(e) =>
|
||||
handlePreferenceChange(pref.name, (e.target as HTMLInputElement)?.value)}
|
||||
placeholder={pref.default as string}
|
||||
class="flex-grow"
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
onclick={() => browse(pref.type as 'file' | 'directory', pref.name)}
|
||||
>
|
||||
Browse...
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue