Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/desktop/src-tauri/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod keyring;
pub mod theme;
11 changes: 11 additions & 0 deletions apps/desktop/src-tauri/src/commands/theme.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use std::fs;

#[tauri::command]
pub fn get_gtk_css() -> Result<String, String> {
let home = std::env::var("HOME").map_err(|_| "HOME environment variable not set")?;
let css_path = format!("{}/.config/gtk-4.0/gtk.css", home);
match fs::read_to_string(&css_path) {
Ok(content) => Ok(content),
Err(_) => Ok(String::new()), // Return empty if file doesn't exist or can't read
}
}
1 change: 1 addition & 0 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ pub fn run() {
.plugin(tauri_plugin_system_info::init())
.invoke_handler(tauri::generate_handler![
commands::keyring::get_stronghold_key,
commands::theme::get_gtk_css,
]);

let app = builder
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src-tauri/src/setup/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub fn setup_window<R: Runtime>(app: &AppHandle<R>) {
main_win.set_transparent_titlebar(true, true);
let splashscreen_win = app.get_webview_window("splashscreen").unwrap();
splashscreen_win.set_transparent_titlebar(true, true);
main_win.center().unwrap();
}
#[cfg(not(target_os = "macos"))]
{
Expand All @@ -16,5 +17,6 @@ pub fn setup_window<R: Runtime>(app: &AppHandle<R>) {
main_win
.set_decorations(false)
.expect("Failed to set decorations");
main_win.center().unwrap();
}
}
31 changes: 31 additions & 0 deletions apps/desktop/src/lib/components/standalone/general-settings.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
const loadingAnimations = ["spinning-circle", "kunkun-dancing"] as const
let launchAtLogin = $state(false)
let language = $state(languageTag())
const fontSizes = [12, 14, 16, 18, 20] as const
let fontSize = $state($appConfig.fontSize)
onMount(() => {
autoStart.isEnabled().then((enabled) => {
launchAtLogin = enabled
Expand Down Expand Up @@ -81,6 +83,12 @@
<span>{m.settings_general_developer_mode()}</span>
<Switch bind:checked={$appConfig.developerMode} />
</li>
{#if $appConfig.platform === 'linux'}
<li>
<span>Use GTK Theme</span>
<Switch bind:checked={$appConfig.useGtkTheme} />
</li>
{/if}
<li>
<span>{m.settings_general_language()}</span>

Expand Down Expand Up @@ -130,6 +138,29 @@
</Select.Content>
</Select.Root>
</li>
<li>
<span>Font Size</span>

<Select.Root type="single" name="fontSize" bind:value={fontSize}>
<Select.Trigger class="w-fit">
{fontSize}px
</Select.Trigger>
<Select.Content>
<Select.Group>
<Select.GroupHeading>Font Size</Select.GroupHeading>
{#each fontSizes as size}
<Select.Item
onclick={() => {
appConfig.setFontSize(size)
}}
value={size}
label={`${size}px`}>{size}px</Select.Item
>
{/each}
</Select.Group>
</Select.Content>
</Select.Root>
</li>
</ul>

<style scoped>
Expand Down
27 changes: 22 additions & 5 deletions apps/desktop/src/lib/stores/appConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Store } from "@tauri-store/svelte"
import { toast } from "svelte-sonner"
import { get, writable } from "svelte/store"
import * as v from "valibot"
import { browser } from '$app/environment'

export const defaultAppConfig: AppConfigState = {
isInitialized: false,
Expand All @@ -30,7 +31,9 @@ export const defaultAppConfig: AppConfigState = {
onBoarded: false,
developerMode: false,
appSearchPaths: [],
loadingAnimation: "kunkun-dancing"
loadingAnimation: "kunkun-dancing",
useGtkTheme: false,
fontSize: 14
}

export const appConfigLoaded = writable(false)
Expand All @@ -45,17 +48,25 @@ interface AppConfigAPI {
setLanguage: (language: string) => void
addAppSearchPath: (appSearchPath: SearchPath) => void
removeAppSearchPath: (appSearchPath: SearchPath) => void
setLoadingAnimation: (loadingAnimation: LoadingAnimation) => void
setUseGtkTheme: (useGtkTheme: boolean) => void
setFontSize: (fontSize: number) => void
}

class AppConfigStore extends Store<AppConfigState> implements AppConfigAPI {
constructor() {
super("app-config", defaultAppConfig, {
saveOnChange: true
})
this.start().catch((err) => {
error("Failed to start app config store", err)
toast.error("Failed to start app config store", { description: err.message })
})
if (browser) {
this.start().catch((err) => {
error("Failed to start app config store", err)
toast.error("Failed to start app config store", { description: err.message })
})
} else {
// On server, set as loaded with defaults
appConfigLoaded.set(true)
}
}
async init() {
debug("Initializing app config")
Expand Down Expand Up @@ -103,6 +114,12 @@ class AppConfigStore extends Store<AppConfigState> implements AppConfigAPI {
setLoadingAnimation(loadingAnimation: LoadingAnimation) {
this.update((config) => ({ ...config, loadingAnimation }))
}
setUseGtkTheme(useGtkTheme: boolean) {
this.update((config) => ({ ...config, useGtkTheme }))
}
setFontSize(fontSize: number) {
this.update((config) => ({ ...config, fontSize }))
}
}

// export const appConfig = createAppConfig()
Expand Down
80 changes: 80 additions & 0 deletions apps/desktop/src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,90 @@
import "../app.css"
import FullScreenLoading from "@/components/common/FullScreenLoading.svelte"
import { appState } from "@/stores/appState"
import { appConfig } from "@/stores"
import { ModeWatcher, ThemeWrapper } from "@kksh/svelte5"
import { Toaster } from "svelte-sonner"
import { invoke } from "@tauri-apps/api/core"
import { onMount } from "svelte"
import { get } from "svelte/store"

let { children } = $props()

let colors: Record<string, string> | null = null;

onMount(async () => {
try {
const css = await invoke<string>('get_gtk_css');
if (css) {
colors = parseGtkColors(css);
}
} catch (e) {
console.error('Failed to load GTK CSS:', e);
}

// Subscribe to config changes to apply/remove GTK theme immediately
appConfig.subscribe((config) => {
if (config.useGtkTheme && colors) {
applyGtkColors(colors);
} else {
resetGtkColors();
}
// Apply font size
document.documentElement.style.fontSize = `${config.fontSize}px`;
});
})

function resetGtkColors() {
const varNames = ['--background', '--foreground', '--card', '--card-foreground', '--muted', '--muted-foreground'];
varNames.forEach(varName => {
document.documentElement.style.removeProperty(varName);
});
// Remove the switch thumb style
const style = document.getElementById('gtk-switch-thumb-style');
if (style) style.remove();
}

function parseGtkColors(css: string): Record<string, string> {
const colorMap: Record<string, string> = {};
const defineColorRegex = /@define-color\s+(\w+)\s+([^;]+);/g;
let match;
while ((match = defineColorRegex.exec(css)) !== null) {
colorMap[match[1]] = match[2];
}
console.log('Extracted GTK colors:', colorMap);
return colorMap;
}

function applyGtkColors(colors: Record<string, string>) {
const mappings: Record<string, string> = {
'window_bg_color': '--background',
'window_fg_color': '--foreground',
'view_bg_color': '--card',
'view_fg_color': '--card-foreground',
'headerbar_bg_color': '--muted',
'headerbar_fg_color': '--muted-foreground',
};
for (const [gtkColor, cssVar] of Object.entries(mappings)) {
if (colors[gtkColor]) {
console.log(`Setting ${cssVar} to ${colors[gtkColor]}`);
// Temporarily set background to red for debugging
document.documentElement.style.setProperty(cssVar, cssVar === '--background' ? 'red' : colors[gtkColor]);
}
}
// Make switch thumb black for visibility on dark GTK backgrounds
const style = document.createElement('style');
style.id = 'gtk-switch-thumb-style';
style.textContent = `
[data-switch-thumb] { background: black !important; }
[cmdk-root] { background: var(--background) !important; }
[cmdk-input] { background: var(--background) !important; color: var(--foreground) !important; }
[cmdk-list] { background: var(--background) !important; }
[cmdk-item] { color: var(--foreground) !important; }
[cmdk-empty] { color: var(--foreground) !important; }
[data-command-input] { user-select: text !important; }
`;
document.head.appendChild(style);
}
</script>

<ParaglideJS {i18n}>
Expand Down
53 changes: 45 additions & 8 deletions apps/desktop/src/routes/app/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,47 @@
const win = getCurrentWindow()
let inputEle: HTMLInputElement | null = $state(null)
function onKeyDown(event: KeyboardEvent) {
const input = event.target as HTMLInputElement;
const originalStart = input.selectionStart;
const originalEnd = input.selectionEnd;
if (event.ctrlKey && event.key === 'a') {
input.selectionStart = 0;
input.selectionEnd = input.value.length;
event.preventDefault();
event.stopPropagation();
return;
}
if (event.key === 'Home') {
if (event.shiftKey) {
input.selectionStart = 0;
input.selectionEnd = originalEnd;
} else {
input.selectionStart = 0;
input.selectionEnd = 0;
}
// Remove prevent to allow browser handling
// event.preventDefault();
// event.stopPropagation();
return;
}
if (event.key === 'End') {
if (event.shiftKey) {
input.selectionStart = originalStart;
input.selectionEnd = input.value.length;
} else {
input.selectionStart = input.value.length;
input.selectionEnd = input.value.length;
}
// Remove prevent to allow browser handling
// event.preventDefault();
// event.stopPropagation();
return;
}
if (event.key === "Escape") {
if ((event.target as HTMLInputElement).value === "") {
if (input.value === "") {
win.hide()
} else {
;(event.target as HTMLInputElement).value = ""
input.value = ""
$appState.searchTerm = ""
}
}
Expand Down Expand Up @@ -117,12 +153,13 @@
<Inspect name="$appState.searchTerm" value={$appState.searchTerm} />
-->

<Command.Root
class={cn("h-screen rounded-lg shadow-md")}
bind:value={$appState.highlightedCmd}
shouldFilter={false}
loop
>
<Command.Root
class={cn("h-screen rounded-lg shadow-md")}
bind:value={$appState.highlightedCmd}
shouldFilter={false}
loop
on:keydown={onKeyDown}
>
<CustomCommandInput
autofocus
bind:ref={inputEle}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

## Permission Table

<table>
Expand All @@ -6,6 +7,7 @@
<th>Description</th>
</tr>


<tr>
<td>

Expand Down
Loading