persistent config between sessions"

This commit is contained in:
Abdenasser 2024-11-09 20:22:05 +01:00
parent c0c989a2d9
commit 5634a8d355
6 changed files with 125 additions and 9 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "macos-task-manager",
"version": "1.0.5",
"version": "1.0.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "macos-task-manager",
"version": "1.0.5",
"version": "1.0.8",
"license": "MIT",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.6.0",

View File

@ -8,6 +8,8 @@
faChevronDown,
faChevronRight,
} from "@fortawesome/free-solid-svg-icons";
import { configStore } from "$lib/stores/config";
import type { AppConfig } from "$lib/types/config";
export let searchTerm: string;
export let statusFilter: string = "all";
export let itemsPerPage: number;
@ -46,6 +48,26 @@
currentPage = page;
}
}
function handleColumnVisibilityChange(columnId: string, visible: boolean) {
configStore.updateConfig({
appearance: {
columnVisibility: {
...$configStore.appearance.columnVisibility,
[columnId]: visible,
},
},
});
}
function updateBehaviorConfig(key: keyof AppConfig["behavior"], value: any) {
configStore.updateConfig({
behavior: {
...$configStore.behavior,
[key]: value,
},
});
}
</script>
<div class="toolbar">
@ -66,7 +88,12 @@
</div>
</div>
<div class="toolbar-group">
<select bind:value={statusFilter} class="select-input">
<select
bind:value={statusFilter}
on:change={() =>
updateBehaviorConfig("defaultStatusFilter", statusFilter)}
class="select-input"
>
{#each statusOptions as option}
<option value={option.value}>{option.label}</option>
{/each}
@ -79,6 +106,7 @@
<select
class="select-input"
bind:value={itemsPerPage}
on:change={() => updateBehaviorConfig("itemsPerPage", itemsPerPage)}
aria-label="Items per page"
>
{#each itemsPerPageOptions as option}
@ -146,8 +174,13 @@
<label class="column-option">
<input
type="checkbox"
bind:checked={column.visible}
checked={column.visible}
disabled={column.required}
on:change={(e) =>
handleColumnVisibilityChange(
column.id,
e.currentTarget.checked,
)}
/>
<span>{column.label}</span>
</label>
@ -161,6 +194,7 @@
<select
class="select-input"
bind:value={refreshRate}
on:change={() => updateBehaviorConfig("refreshRate", refreshRate)}
disabled={isFrozen}
>
{#each refreshRateOptions as option}

36
src/lib/stores/config.ts Normal file
View File

@ -0,0 +1,36 @@
import { writable } from 'svelte/store';
import type { AppConfig } from '$lib/types/config';
import { DEFAULT_CONFIG } from '$lib/types/config';
function createConfigStore() {
const { subscribe, set, update } = writable<AppConfig>(DEFAULT_CONFIG);
return {
subscribe,
init: () => {
if (typeof window !== 'undefined') {
const stored = localStorage.getItem('neohtop_config');
if (stored) {
try {
const config = JSON.parse(stored);
set({ ...DEFAULT_CONFIG, ...config });
} catch (e) {
console.error('Failed to parse stored config:', e);
set(DEFAULT_CONFIG);
}
}
}
},
updateConfig: (newConfig: Partial<AppConfig>) => {
update(config => {
const updated = { ...config, ...newConfig };
if (typeof window !== 'undefined') {
localStorage.setItem('neohtop_config', JSON.stringify(updated));
}
return updated;
});
}
};
}
export const configStore = createConfigStore();

37
src/lib/types/config.ts Normal file
View File

@ -0,0 +1,37 @@
export interface AppConfig {
appearance: {
columnVisibility: Record<string, boolean>;
};
behavior: {
itemsPerPage: number;
refreshRate: number;
defaultStatusFilter: string;
};
}
export const DEFAULT_CONFIG: AppConfig = {
appearance: {
columnVisibility: {
name: true,
pid: true,
status: true,
user: true,
cpu_usage: true,
memory_usage: true,
virtual_memory: true,
disk_usage: true,
ppid: false,
root: false,
command: false,
environ: false,
session_id: false,
start_time: false,
run_time: true
}
},
behavior: {
itemsPerPage: 15,
refreshRate: 1000,
defaultStatusFilter: 'all'
}
};

View File

@ -10,6 +10,7 @@
import { themeStore } from "$lib/stores";
import type { Process, SystemStats, Column } from "$lib/types";
import TitleBar from "$lib/components/TitleBar.svelte";
import { configStore } from "$lib/stores/config";
let processes: Process[] = [];
let systemStats: SystemStats | null = null;
@ -18,19 +19,16 @@
let searchTerm = "";
let isLoading = true;
let currentPage = 1;
let itemsPerPage = 15;
let pinnedProcesses: Set<string> = new Set();
let selectedProcess: Process | null = null;
let showInfoModal = false;
let showConfirmModal = false;
let processToKill: Process | null = null;
let isKilling = false;
let statusFilter = "all";
let refreshRate = 1000;
let isFrozen = false;
let selectedProcessPid: number | null = null;
let columns: Column[] = [
let columnDefinitions: Column[] = [
{ id: "name", label: "Process Name", visible: true, required: true },
{ id: "pid", label: "PID", visible: true, required: false },
{
@ -90,6 +88,17 @@
},
];
// Merge column definitions with stored visibility
$: columns = columnDefinitions.map((col) => ({
...col,
visible:
col.required ||
($configStore.appearance.columnVisibility[col.id] ?? col.visible),
}));
$: itemsPerPage = $configStore.behavior.itemsPerPage;
$: refreshRate = $configStore.behavior.refreshRate;
$: statusFilter = $configStore.behavior.defaultStatusFilter;
let sortConfig = {
field: "cpu_usage" as keyof Process,
direction: "desc" as "asc" | "desc",
@ -262,6 +271,7 @@
isLoading = false;
}
configStore.init();
themeStore.init();
});

View File

@ -1,7 +1,6 @@
import { defineConfig } from "vite";
import { sveltekit } from "@sveltejs/kit/vite";
// @ts-expect-error process is a nodejs global
const host = process.env.TAURI_DEV_HOST;
// https://vitejs.dev/config/