From 7ae0d9916a1ac143fe39a73baa5ed9ee0765eed3 Mon Sep 17 00:00:00 2001 From: Graham Barber Date: Mon, 15 Sep 2025 09:00:08 -0700 Subject: [PATCH] redesign chat interface --- src-tauri/Cargo.toml | 9 +- src-tauri/src/letta/mod.rs | 27 ++- src/app.css | 7 + src/lib/forms/dropdown.svelte | 20 +- src/lib/forms/input.svelte | 32 ++- src/routes/+layout.svelte | 4 +- src/routes/+page.svelte | 262 ++++++++++++++++------ src/routes/settings/letta-settings.svelte | 8 +- 8 files changed, 257 insertions(+), 112 deletions(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 2ebacd3..c5553a5 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -28,8 +28,12 @@ tungstenite = "0.27.0" futures-util = "0.3.31" tokio = "1.47.1" dasp = { version = "0.11.0", features = ["all"] } -keyring = { version = "3.6.3", features = ["apple-native", "windows-native", "sync-secret-service"] } -strum = {version = "0.27.2", features = ["derive"] } +keyring = { version = "3.6.3", features = [ + "apple-native", + "windows-native", + "sync-secret-service", +] } +strum = { version = "0.27.2", features = ["derive"] } tauri-plugin-store = "2" tauri-plugin-http = "2" reqwest = { version = "0.12.23", features = ["json"] } @@ -39,4 +43,3 @@ reqwest-eventsource = "0.6.0" tauri-plugin-positioner = "2" tauri-plugin-single-instance = "2" tauri-plugin-window-state = "2" - diff --git a/src-tauri/src/letta/mod.rs b/src-tauri/src/letta/mod.rs index 46255cd..491d1ee 100644 --- a/src-tauri/src/letta/mod.rs +++ b/src-tauri/src/letta/mod.rs @@ -101,21 +101,28 @@ impl LettaManager { { Ok(api_key) => { let base_url = self.base_url.lock().await.to_owned(); - let agent_id = self.base_url.lock().await.to_owned(); + let agent_id = self.agent_id.lock().await.to_owned(); + let body = &json!({ + "messages": [ + { + "role": "user", + "content": [{ + "type": "text", + "text": msg, + }] + } + ], + "stream_tokens": true + }); + + println!("body: {:?}", body); + let req = self .http_client .post(format!("{base_url}/v1/agents/{agent_id}/messages/stream")) .header("Authorization", format!("Bearer {api_key}")) .header("Content-Type", "application/json") - .json(&json!({ - "messages": [ - { - "role": "user", - "content": [ msg ] - } - ], - "stream_tokens": true - })); + .json(body); let mut source = EventSource::new(req).expect("could not convert request to event source"); diff --git a/src/app.css b/src/app.css index 6ff4439..79f6116 100644 --- a/src/app.css +++ b/src/app.css @@ -20,6 +20,13 @@ --color-rose-pine-highlight-high: hsl(248deg, 13%, 36%); --font-sans: "Recursive Variable"; + + --font-cursive: "Recursive Variable"; + --font-cursive--font-variation-settings: "CRSV" 1, "CASL" 1; + + --font-mono: "Recursive Variable"; + --font-mono--font-variation-settings: "MONO" 1; + --font-skeleton: "Flow Block"; --animate-wiggle: wiggle 1s ease-in-out infinite; diff --git a/src/lib/forms/dropdown.svelte b/src/lib/forms/dropdown.svelte index 8c89986..02436e5 100644 --- a/src/lib/forms/dropdown.svelte +++ b/src/lib/forms/dropdown.svelte @@ -23,24 +23,24 @@ }: DropdownProps = $props(); let value = $state("loading"); - let err = $state("") + let err = $state(""); let isLoading = $derived(!Array.isArray(options)); $effect(() => { Promise.allSettled([ Promise.resolve(initialOption), - Promise.resolve(options) + Promise.resolve(options), ]).then(([init, opts]) => { if (init.status === "fulfilled") { - value = init.value + value = init.value; } if (opts.status === "rejected") { - err = opts.reason + err = opts.reason; } - isLoading = false - }) + isLoading = false; + }); }); @@ -54,8 +54,8 @@ bind:value class={[ "bg-rose-pine-surface px-3 py-2 w-full flex-1 border-1 border-rose-pine-muted/20 rounded-sm focus:ring-2 ring-rose-pine-iris text-rose-pine-text placeholder:text-rose-pine-subtle", - isLoading && "font-skeleton animate-pulse", props.class, - + isLoading && "font-skeleton animate-pulse", + props.class, ]} onchange={(e) => onSelect(e.currentTarget.value)} > @@ -75,8 +75,8 @@ isLoading ? "icon-[svg-spinners--90-ring-with-bg] text-rose-pine-text" : value - ? "icon-[tabler--circle-check-filled] text-rose-pine-foam" - : "icon-[tabler--exclamation-circle] text-rose-pine-love" + ? "icon-[tabler--circle-check-filled] text-rose-pine-foam" + : "icon-[tabler--exclamation-circle] text-rose-pine-love", ]} > diff --git a/src/lib/forms/input.svelte b/src/lib/forms/input.svelte index e108271..2c0498c 100644 --- a/src/lib/forms/input.svelte +++ b/src/lib/forms/input.svelte @@ -18,17 +18,23 @@ let value = $state("loading"); let initial = $state("loading"); let isDirty = $derived(value !== initial); - let isLoading = $state(typeof initialValue === "object" && "then" in initialValue) + let isLoading = $state( + typeof initialValue === "object" && "then" in initialValue, + ); $effect(() => { Promise.resolve(initialValue) - .then(str => {value = str; initial = str; isLoading = false}) - }) + .then((str) => { + value = str; + initial = str; + isLoading = false; + }); + }); async function onsubmit() { - isLoading = true + isLoading = true; await Promise.resolve(onSave(value)); - isLoading = false + isLoading = false; } @@ -61,14 +67,16 @@ {:else if required || isLoading}
- + : "icon-[tabler--exclamation-circle-filled] text-rose-pine-love", + ]} + >
{/if} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 0b21ad6..5d2738a 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,11 +1,11 @@ -
+
{@render children()}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 8f36498..ae17e80 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,6 +1,7 @@ -
- +
+ {#if isDrafting} +
+ + +
+ {/if} +
- -
- {#await keyInvocation} -
+
- {:then hasKey} - {#if hasKey} - - {:else} - - Set your Cartesia API key in Settings to get started - - {/if} - {:catch err} -

{err}

- {/await} + > + + + + + +
-{#if draft} -
- {draft} -
-{/if} + -{#each history as text} -
- {text} -
-{/each} +
+ {#each history as message, i (message.id)} +
+
+ {message.content} +
+
+ {/each} + + {#if isCompleting} +
+
+ +
+
+ {/if} +
diff --git a/src/routes/settings/letta-settings.svelte b/src/routes/settings/letta-settings.svelte index 666f912..14171ce 100644 --- a/src/routes/settings/letta-settings.svelte +++ b/src/routes/settings/letta-settings.svelte @@ -36,7 +36,9 @@ let baseUrl = $state(fetchBaseUrl()); let agentId = $state(fetchAgentId()); - let lettaAgents: Promise = $state(Promise.resolve([])); + let lettaAgents: Promise = $state( + Promise.resolve([]), + ); let allowAgentEdit = $derived.by(async () => { const [hasKey, url] = await Promise.all([ @@ -46,9 +48,9 @@ const canEdit = Boolean(hasKey && url); - if (canEdit) lettaAgents = getLettaAgents() + if (canEdit) lettaAgents = getLettaAgents(); - return canEdit + return canEdit; }); -- 2.51.2