generated from nhcarrigan/template
feat: add ability to configure the agent (also theme switcher) (#3)
### Explanation _No response_ ### Issue _No response_ ### Attestations - [ ] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/) - [ ] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/). - [ ] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/). ### Dependencies - [ ] I have pinned the dependencies to a specific patch version. ### Style - [ ] I have run the linter and resolved any errors. - [ ] My pull request uses an appropriate title, matching the conventional commit standards. - [ ] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request. ### Tests - [ ] My contribution adds new code, and I have added tests to cover it. - [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes. - [ ] All new and existing tests pass locally with my changes. - [ ] Code coverage remains at or above the configured threshold. ### Documentation _No response_ ### Versioning _No response_ Reviewed-on: #3 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit was merged in pull request #3.
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import { writable, derived } from "svelte/store";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
export type Theme = "dark" | "light";
|
||||
|
||||
export interface HikariConfig {
|
||||
model: string | null;
|
||||
api_key: string | null;
|
||||
custom_instructions: string | null;
|
||||
mcp_servers_json: string | null;
|
||||
auto_granted_tools: string[];
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
const defaultConfig: HikariConfig = {
|
||||
model: null,
|
||||
api_key: null,
|
||||
custom_instructions: null,
|
||||
mcp_servers_json: null,
|
||||
auto_granted_tools: [],
|
||||
theme: "dark",
|
||||
};
|
||||
|
||||
function createConfigStore() {
|
||||
const config = writable<HikariConfig>(defaultConfig);
|
||||
const isLoading = writable<boolean>(true);
|
||||
const isSidebarOpen = writable<boolean>(false);
|
||||
const saveError = writable<string | null>(null);
|
||||
|
||||
async function loadConfig() {
|
||||
isLoading.set(true);
|
||||
try {
|
||||
const savedConfig = await invoke<HikariConfig>("get_config");
|
||||
config.set(savedConfig);
|
||||
} catch (error) {
|
||||
console.error("Failed to load config:", error);
|
||||
config.set(defaultConfig);
|
||||
} finally {
|
||||
isLoading.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function saveConfig(newConfig: HikariConfig) {
|
||||
saveError.set(null);
|
||||
try {
|
||||
await invoke("save_config", { config: newConfig });
|
||||
config.set(newConfig);
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
saveError.set(errorMessage);
|
||||
console.error("Failed to save config:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function updateConfig(updates: Partial<HikariConfig>) {
|
||||
let currentConfig: HikariConfig = defaultConfig;
|
||||
config.subscribe((c) => (currentConfig = c))();
|
||||
const newConfig = { ...currentConfig, ...updates };
|
||||
await saveConfig(newConfig);
|
||||
}
|
||||
|
||||
return {
|
||||
config: { subscribe: config.subscribe },
|
||||
isLoading: { subscribe: isLoading.subscribe },
|
||||
isSidebarOpen: { subscribe: isSidebarOpen.subscribe },
|
||||
saveError: { subscribe: saveError.subscribe },
|
||||
|
||||
loadConfig,
|
||||
saveConfig,
|
||||
updateConfig,
|
||||
|
||||
openSidebar: () => isSidebarOpen.set(true),
|
||||
closeSidebar: () => isSidebarOpen.set(false),
|
||||
toggleSidebar: () => isSidebarOpen.update((open) => !open),
|
||||
|
||||
setTheme: async (theme: Theme) => {
|
||||
await updateConfig({ theme });
|
||||
applyTheme(theme);
|
||||
},
|
||||
|
||||
addAutoGrantedTool: async (tool: string) => {
|
||||
let currentConfig: HikariConfig = defaultConfig;
|
||||
config.subscribe((c) => (currentConfig = c))();
|
||||
if (!currentConfig.auto_granted_tools.includes(tool)) {
|
||||
const newTools = [...currentConfig.auto_granted_tools, tool];
|
||||
await updateConfig({ auto_granted_tools: newTools });
|
||||
}
|
||||
},
|
||||
|
||||
removeAutoGrantedTool: async (tool: string) => {
|
||||
let currentConfig: HikariConfig = defaultConfig;
|
||||
config.subscribe((c) => (currentConfig = c))();
|
||||
const newTools = currentConfig.auto_granted_tools.filter((t) => t !== tool);
|
||||
await updateConfig({ auto_granted_tools: newTools });
|
||||
},
|
||||
|
||||
getConfig: (): HikariConfig => {
|
||||
let currentConfig: HikariConfig = defaultConfig;
|
||||
config.subscribe((c) => (currentConfig = c))();
|
||||
return currentConfig;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function applyTheme(theme: Theme) {
|
||||
if (typeof document !== "undefined") {
|
||||
document.documentElement.setAttribute("data-theme", theme);
|
||||
}
|
||||
}
|
||||
|
||||
export const configStore = createConfigStore();
|
||||
|
||||
export const isDarkTheme = derived(configStore.config, ($config) => $config.theme === "dark");
|
||||
Reference in New Issue
Block a user