make keybinds configurable
This commit is contained in:
parent
5685948608
commit
8d7b01629d
@ -6,7 +6,6 @@ use is_terminal::IsTerminal;
|
|||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
use tauri::{
|
use tauri::{
|
||||||
AppHandle,
|
|
||||||
Manager,
|
Manager,
|
||||||
GlobalShortcutManager,
|
GlobalShortcutManager,
|
||||||
async_runtime as rt,
|
async_runtime as rt,
|
||||||
@ -27,10 +26,17 @@ pub struct TermConfig {
|
|||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
pub struct HotkeyConfig {
|
pub struct HotkeysConfig {
|
||||||
// tauri uses strings to represent keybinds, so we will as well
|
// tauri uses strings to represent keybinds, so we will as well
|
||||||
pub show_window: String,
|
pub show_window: Hotkey,
|
||||||
pub launch_terminal: String,
|
pub launch_terminal: Hotkey,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
|
pub struct Hotkey {
|
||||||
|
pub keys: String,
|
||||||
|
pub enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -49,7 +55,7 @@ pub struct AppConfig {
|
|||||||
#[serde(default = "default_term_config")]
|
#[serde(default = "default_term_config")]
|
||||||
pub terminal: TermConfig,
|
pub terminal: TermConfig,
|
||||||
#[serde(default = "default_hotkey_config")]
|
#[serde(default = "default_hotkey_config")]
|
||||||
pub hotkeys: HotkeyConfig,
|
pub hotkeys: HotkeysConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -187,42 +193,46 @@ fn default_term_config() -> TermConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn default_hotkey_config() -> HotkeyConfig {
|
fn default_hotkey_config() -> HotkeysConfig {
|
||||||
HotkeyConfig {
|
HotkeysConfig {
|
||||||
show_window: "alt+shift+C".into(),
|
show_window: Hotkey {keys: "alt+shift+C".into(), enabled: true},
|
||||||
launch_terminal: "alt+shift+T".into(),
|
launch_terminal: Hotkey {keys: "alt+shift+T".into(), enabled: true},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: will panic if called before APP is set
|
// note: will panic if called before APP is set
|
||||||
pub fn register_hotkeys(hotkeys: &HotkeyConfig) -> tauri::Result<()> {
|
pub fn register_hotkeys(hotkeys: &HotkeysConfig) -> tauri::Result<()> {
|
||||||
let app = crate::app::APP.get().unwrap();
|
let app = crate::app::APP.get().unwrap();
|
||||||
|
|
||||||
let mut manager = app.global_shortcut_manager();
|
let mut manager = app.global_shortcut_manager();
|
||||||
manager.unregister_all()?;
|
manager.unregister_all()?;
|
||||||
|
|
||||||
let h = app.app_handle();
|
if hotkeys.show_window.enabled {
|
||||||
manager.register(
|
let handle = app.app_handle();
|
||||||
&hotkeys.show_window,
|
manager.register(
|
||||||
move || {
|
&hotkeys.show_window.keys,
|
||||||
h.get_window("main")
|
move || {
|
||||||
.map(|w| w.show().error_popup("Failed to show"))
|
handle.get_window("main")
|
||||||
.ok_or(HandlerError::NoMainWindow)
|
.map(|w| w.show().error_popup("Failed to show"))
|
||||||
.error_popup("No main window");
|
.ok_or(HandlerError::NoMainWindow)
|
||||||
},
|
.error_popup("No main window");
|
||||||
)?;
|
},
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
// register() doesn't take an async fn, so we have to use spawn
|
if hotkeys.launch_terminal.enabled {
|
||||||
manager.register(
|
// register() doesn't take an async fn, so we have to use spawn
|
||||||
&hotkeys.launch_terminal,
|
manager.register(
|
||||||
|| {
|
&hotkeys.launch_terminal.keys,
|
||||||
rt::spawn(async {
|
|| {
|
||||||
crate::terminal::launch(false)
|
rt::spawn(async {
|
||||||
.await
|
crate::terminal::launch(false)
|
||||||
.error_popup("Failed to launch");
|
.await
|
||||||
});
|
.error_popup("Failed to launch");
|
||||||
}
|
});
|
||||||
)?;
|
}
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,9 @@ impl AppState {
|
|||||||
sv.rebind(new_config.listen_addr, new_config.listen_port).await?;
|
sv.rebind(new_config.listen_addr, new_config.listen_port).await?;
|
||||||
}
|
}
|
||||||
// re-register hotkeys if necessary
|
// re-register hotkeys if necessary
|
||||||
if new_config.hotkeys != live_config.hotkeys {
|
if new_config.hotkeys.show_window != live_config.hotkeys.show_window
|
||||||
|
|| new_config.hotkeys.launch_terminal != live_config.hotkeys.launch_terminal
|
||||||
|
{
|
||||||
config::register_hotkeys(&new_config.hotkeys)?;
|
config::register_hotkeys(&new_config.hotkeys)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
11
src/ui/KeyCombo.svelte
Normal file
11
src/ui/KeyCombo.svelte
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script>
|
||||||
|
export let keys;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex gap-x-[0.2em] items-center">
|
||||||
|
{#each keys as key, i}
|
||||||
|
{#if i > 0} + {/if}
|
||||||
|
<kbd class="normal-case px-1 py-0.5 rounded border border-neutral">{key}</kbd>
|
||||||
|
{/each}
|
||||||
|
</div>
|
@ -4,14 +4,13 @@
|
|||||||
import Setting from './Setting.svelte';
|
import Setting from './Setting.svelte';
|
||||||
|
|
||||||
export let title;
|
export let title;
|
||||||
export let divider = true;
|
|
||||||
export let value;
|
export let value;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<Setting {title} {divider}>
|
<Setting {title}>
|
||||||
<div slot="input">
|
<div slot="input">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
61
src/ui/settings/Keybind.svelte
Normal file
61
src/ui/settings/Keybind.svelte
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<script>
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
import KeyCombo from '../KeyCombo.svelte';
|
||||||
|
|
||||||
|
export let description;
|
||||||
|
export let value;
|
||||||
|
|
||||||
|
const id = Math.random().toString().slice(2);
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
let listening = false;
|
||||||
|
|
||||||
|
function listen() {
|
||||||
|
// don't re-listen if we already are
|
||||||
|
if (listening) return;
|
||||||
|
|
||||||
|
listening = true;
|
||||||
|
window.addEventListener('keyup', setKeybind, {once: true});
|
||||||
|
// setTimeout avoids reacting to the click event that we are currently processing
|
||||||
|
setTimeout(() => window.addEventListener('click', cancel, {once: true}), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setKeybind(event) {
|
||||||
|
console.log(event);
|
||||||
|
let keys = [];
|
||||||
|
if (event.ctrlKey) keys.push('ctrl');
|
||||||
|
if (event.altKey) keys.push('alt');
|
||||||
|
if (event.metaKey) keys.push('meta');
|
||||||
|
if (event.shiftKey) keys.push('shift');
|
||||||
|
keys.push(event.key);
|
||||||
|
|
||||||
|
value.keys = keys.join('+');
|
||||||
|
dispatch('update', {value});
|
||||||
|
listening = false;
|
||||||
|
window.removeEventListener('click', cancel, {once: true});
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel() {
|
||||||
|
listening = false;
|
||||||
|
window.removeEventListener('keyup', setKeybind, {once: true});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<input
|
||||||
|
{id}
|
||||||
|
type="checkbox"
|
||||||
|
class="checkbox checkbox-primary"
|
||||||
|
bind:checked={value.enabled}
|
||||||
|
on:change={() => dispatch('update', {value})}
|
||||||
|
>
|
||||||
|
<label for={id} class="cursor-pointer ml-4 text-lg">{description}</label>
|
||||||
|
|
||||||
|
<button class="h-12 p-2 rounded border border-neutral cursor-pointer text-center" on:click={listen}>
|
||||||
|
{#if listening}
|
||||||
|
Click to cancel
|
||||||
|
{:else}
|
||||||
|
<KeyCombo keys={value.keys.split('+')} />
|
||||||
|
{/if}
|
||||||
|
</button>
|
@ -4,8 +4,8 @@
|
|||||||
import Setting from './Setting.svelte';
|
import Setting from './Setting.svelte';
|
||||||
|
|
||||||
export let title;
|
export let title;
|
||||||
export let divider = true;
|
|
||||||
export let value;
|
export let value;
|
||||||
|
|
||||||
export let unit = '';
|
export let unit = '';
|
||||||
export let min = null;
|
export let min = null;
|
||||||
export let max = null;
|
export let max = null;
|
||||||
@ -60,7 +60,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<Setting {title} {divider}>
|
<Setting {title}>
|
||||||
<div slot="input">
|
<div slot="input">
|
||||||
{#if unit}
|
{#if unit}
|
||||||
<span class="mr-2">{unit}:</span>
|
<span class="mr-2">{unit}:</span>
|
||||||
|
@ -3,20 +3,20 @@
|
|||||||
import ErrorAlert from '../ErrorAlert.svelte';
|
import ErrorAlert from '../ErrorAlert.svelte';
|
||||||
|
|
||||||
export let title;
|
export let title;
|
||||||
export let divider = true;
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
{#if divider}
|
<div>
|
||||||
<div class="divider"></div>
|
<div class="flex flex-wrap justify-between gap-y-4">
|
||||||
{/if}
|
<h3 class="text-lg font-bold shrink-0">{title}</h3>
|
||||||
<div class="flex flex-wrap justify-between gap-y-4">
|
{#if $$slots.input}
|
||||||
<h3 class="text-lg font-bold shrink-0">{title}</h3>
|
<slot name="input"></slot>
|
||||||
<slot name="input"></slot>
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $$slots.description}
|
{#if $$slots.description}
|
||||||
<p class="mt-3">
|
<p class="mt-3">
|
||||||
<slot name="description"></slot>
|
<slot name="description"></slot>
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
</div>
|
||||||
|
14
src/ui/settings/SettingsGroup.svelte
Normal file
14
src/ui/settings/SettingsGroup.svelte
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<script>
|
||||||
|
export let name;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="divider mt-0 mb-8">
|
||||||
|
<h2 class="text-xl font-bold">{name}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-12">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -3,14 +3,13 @@
|
|||||||
import Setting from './Setting.svelte';
|
import Setting from './Setting.svelte';
|
||||||
|
|
||||||
export let title;
|
export let title;
|
||||||
export let divider = true;
|
|
||||||
export let value;
|
export let value;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<Setting {title} {divider}>
|
<Setting {title}>
|
||||||
<div slot="input">
|
<div slot="input">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -4,14 +4,13 @@
|
|||||||
import Setting from './Setting.svelte';
|
import Setting from './Setting.svelte';
|
||||||
|
|
||||||
export let title;
|
export let title;
|
||||||
export let divider = true; // passed through to Setting
|
|
||||||
export let value;
|
export let value;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<Setting {title} {divider}>
|
<Setting {title}>
|
||||||
<input
|
<input
|
||||||
slot="input"
|
slot="input"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
import { appState, completeRequest } from '../lib/state.js';
|
import { appState, completeRequest } from '../lib/state.js';
|
||||||
import ErrorAlert from '../ui/ErrorAlert.svelte';
|
import ErrorAlert from '../ui/ErrorAlert.svelte';
|
||||||
import Link from '../ui/Link.svelte';
|
import Link from '../ui/Link.svelte';
|
||||||
|
import KeyCombo from '../ui/KeyCombo.svelte';
|
||||||
|
|
||||||
|
|
||||||
// Send response to backend, display error if applicable
|
// Send response to backend, display error if applicable
|
||||||
@ -108,17 +109,15 @@
|
|||||||
<div class="w-full flex justify-between">
|
<div class="w-full flex justify-between">
|
||||||
<Link target={deny} hotkey="Escape">
|
<Link target={deny} hotkey="Escape">
|
||||||
<button class="btn btn-error justify-self-start">
|
<button class="btn btn-error justify-self-start">
|
||||||
Deny
|
<span class="mr-2">Deny</span>
|
||||||
<kbd class="ml-2 normal-case px-1 py-0.5 rounded border border-neutral">Esc</kbd>
|
<KeyCombo keys={['Esc']} />
|
||||||
</button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link target={approve} hotkey="Enter" shift="{true}">
|
<Link target={approve} hotkey="Enter" shift="{true}">
|
||||||
<button class="btn btn-success justify-self-end">
|
<button class="btn btn-success justify-self-end">
|
||||||
Approve
|
<span class="mr-2">Approve</span>
|
||||||
<kbd class="ml-2 normal-case px-1 py-0.5 rounded border border-neutral">Shift</kbd>
|
<KeyCombo keys={['Shift', 'Enter']} />
|
||||||
<span class="mx-0.5">+</span>
|
|
||||||
<kbd class="normal-case px-1 py-0.5 rounded border border-neutral">Enter</kbd>
|
|
||||||
</button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
|
<script context="module">
|
||||||
|
import { type } from '@tauri-apps/api/os';
|
||||||
|
const osType = await type();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { invoke } from '@tauri-apps/api/tauri';
|
import { invoke } from '@tauri-apps/api/tauri';
|
||||||
import { type } from '@tauri-apps/api/os';
|
|
||||||
|
|
||||||
import { appState } from '../lib/state.js';
|
import { appState } from '../lib/state.js';
|
||||||
import Nav from '../ui/Nav.svelte';
|
import Nav from '../ui/Nav.svelte';
|
||||||
import Link from '../ui/Link.svelte';
|
import Link from '../ui/Link.svelte';
|
||||||
import ErrorAlert from '../ui/ErrorAlert.svelte';
|
import ErrorAlert from '../ui/ErrorAlert.svelte';
|
||||||
|
import SettingsGroup from '../ui/settings/SettingsGroup.svelte';
|
||||||
|
import Keybind from '../ui/settings/Keybind.svelte';
|
||||||
import { Setting, ToggleSetting, NumericSetting, FileSetting, TextSetting } from '../ui/settings';
|
import { Setting, ToggleSetting, NumericSetting, FileSetting, TextSetting } from '../ui/settings';
|
||||||
|
|
||||||
import { fly } from 'svelte/transition';
|
import { fly } from 'svelte/transition';
|
||||||
@ -14,6 +21,7 @@
|
|||||||
|
|
||||||
let error = null;
|
let error = null;
|
||||||
async function save() {
|
async function save() {
|
||||||
|
console.log('updating config');
|
||||||
try {
|
try {
|
||||||
await invoke('save_config', {config: $appState.config});
|
await invoke('save_config', {config: $appState.config});
|
||||||
}
|
}
|
||||||
@ -22,12 +30,6 @@
|
|||||||
$appState.config = await invoke('get_config');
|
$appState.config = await invoke('get_config');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let osType = '';
|
|
||||||
type().then(t => osType = t);
|
|
||||||
|
|
||||||
console.log($appState.config.terminal);
|
|
||||||
window.term = $appState.config.terminal;
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
@ -36,68 +38,71 @@
|
|||||||
</Nav>
|
</Nav>
|
||||||
|
|
||||||
{#await invoke('get_config') then config}
|
{#await invoke('get_config') then config}
|
||||||
<div class="max-w-lg mx-auto mt-1.5 p-4">
|
<div class="max-w-lg mx-auto mt-1.5 p-4 space-y-16">
|
||||||
<!-- <h2 class="text-2xl font-bold text-center">Settings</h2> -->
|
<SettingsGroup name="General">
|
||||||
|
<ToggleSetting title="Start on login" bind:value={$appState.config.start_on_login} on:update={save}>
|
||||||
|
<svelte:fragment slot="description">
|
||||||
|
Start Creddy when you log in to your computer.
|
||||||
|
</svelte:fragment>
|
||||||
|
</ToggleSetting>
|
||||||
|
|
||||||
<div class="divider mt-0 mb-8">
|
<ToggleSetting title="Start minimized" bind:value={$appState.config.start_minimized} on:update={save}>
|
||||||
<h2 class="text-xl font-bold">General</h2>
|
<svelte:fragment slot="description">
|
||||||
</div>
|
Minimize to the system tray at startup.
|
||||||
|
</svelte:fragment>
|
||||||
<ToggleSetting title="Start on login" divider={false} bind:value={$appState.config.start_on_login} on:update={save}>
|
</ToggleSetting>
|
||||||
<svelte:fragment slot="description">
|
|
||||||
Start Creddy when you log in to your computer.
|
|
||||||
</svelte:fragment>
|
|
||||||
</ToggleSetting>
|
|
||||||
|
|
||||||
<ToggleSetting title="Start minimized" bind:value={$appState.config.start_minimized} on:update={save}>
|
<NumericSetting title="Re-hide delay" bind:value={$appState.config.rehide_ms} min={0} unit="Milliseconds" on:update={save}>
|
||||||
<svelte:fragment slot="description">
|
<svelte:fragment slot="description">
|
||||||
Minimize to the system tray at startup.
|
How long to wait after a request is approved/denied before minimizing
|
||||||
</svelte:fragment>
|
the window to tray. Only applicable if the window was minimized
|
||||||
</ToggleSetting>
|
to tray before the request was received.
|
||||||
|
</svelte:fragment>
|
||||||
|
</NumericSetting>
|
||||||
|
|
||||||
<NumericSetting title="Re-hide delay" bind:value={$appState.config.rehide_ms} min={0} unit="Milliseconds" on:update={save}>
|
<NumericSetting
|
||||||
<svelte:fragment slot="description">
|
title="Listen port"
|
||||||
How long to wait after a request is approved/denied before minimizing
|
bind:value={$appState.config.listen_port}
|
||||||
the window to tray. Only applicable if the window was minimized
|
min={osType === 'Windows_NT' ? 1 : 0}
|
||||||
to tray before the request was received.
|
on:update={save}
|
||||||
</svelte:fragment>
|
>
|
||||||
</NumericSetting>
|
<svelte:fragment slot="description">
|
||||||
|
Listen for credentials requests on this port.
|
||||||
|
(Should be used with <code>$AWS_CONTAINER_CREDENTIALS_FULL_URI</code>)
|
||||||
|
</svelte:fragment>
|
||||||
|
</NumericSetting>
|
||||||
|
|
||||||
<NumericSetting
|
<Setting title="Update credentials">
|
||||||
title="Listen port"
|
<Link slot="input" target="EnterCredentials">
|
||||||
bind:value={$appState.config.listen_port}
|
<button class="btn btn-sm btn-primary">Update</button>
|
||||||
min={osType === 'Windows_NT' ? 1 : 0}
|
</Link>
|
||||||
on:update={save}
|
<svelte:fragment slot="description">
|
||||||
>
|
Update or re-enter your encrypted credentials.
|
||||||
<svelte:fragment slot="description">
|
</svelte:fragment>
|
||||||
Listen for credentials requests on this port.
|
</Setting>
|
||||||
(Should be used with <code>$AWS_CONTAINER_CREDENTIALS_FULL_URI</code>)
|
|
||||||
</svelte:fragment>
|
|
||||||
</NumericSetting>
|
|
||||||
|
|
||||||
<Setting title="Update credentials">
|
<FileSetting
|
||||||
<Link slot="input" target="EnterCredentials">
|
title="Terminal emulator"
|
||||||
<button class="btn btn-sm btn-primary">Update</button>
|
bind:value={$appState.config.terminal.exec}
|
||||||
</Link>
|
on:update={save}
|
||||||
<svelte:fragment slot="description">
|
>
|
||||||
Update or re-enter your encrypted credentials.
|
<svelte:fragment slot="description">
|
||||||
</svelte:fragment>
|
Choose your preferred terminal emulator (e.g. <code>gnome-terminal</code> or <code>wt.exe</code>.) May be an absolute path or an executable discoverable on <code>$PATH</code>.
|
||||||
</Setting>
|
</svelte:fragment>
|
||||||
|
</FileSetting>
|
||||||
|
</SettingsGroup>
|
||||||
|
|
||||||
<div class="divider mt-10 mb-8">
|
<SettingsGroup name="Hotkeys">
|
||||||
<h2 class="text-xl font-bold">Terminal</h2>
|
<div class="space-y-4">
|
||||||
</div>
|
<p>Click on a keybinding to modify it. Use the checkbox to enable or disable a keybinding entirely.</p>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-[auto_1fr_auto] gap-y-3 items-center">
|
||||||
|
<Keybind description="Show Creddy" value={$appState.config.hotkeys.show_window} on:update={save} />
|
||||||
|
<Keybind description="Launch terminal" value={$appState.config.hotkeys.launch_terminal} on:update={save} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SettingsGroup>
|
||||||
|
|
||||||
<FileSetting
|
|
||||||
title="Emulator"
|
|
||||||
divider={false}
|
|
||||||
bind:value={$appState.config.terminal.exec}
|
|
||||||
on:update={save}
|
|
||||||
>
|
|
||||||
<svelte:fragment slot="description">
|
|
||||||
Choose your preferred terminal emulator (e.g. <code>gnome-terminal</code>, <code>wt.exe</code>.) May be an absolute path or an executable discoverable on <code>$PATH</code>.
|
|
||||||
</svelte:fragment>
|
|
||||||
</FileSetting>
|
|
||||||
</div>
|
</div>
|
||||||
{/await}
|
{/await}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user