generated from nhcarrigan/template
feat: add automatic greeting upon connection (#42)
### Explanation _No response_ ### Issue Closes #23 ### 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: #42 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit was merged in pull request #42.
This commit is contained in:
@@ -9,6 +9,8 @@
|
||||
mcp_servers_json: null,
|
||||
auto_granted_tools: [],
|
||||
theme: "dark",
|
||||
greeting_enabled: true,
|
||||
greeting_custom_prompt: null,
|
||||
});
|
||||
|
||||
let isOpen = $state(false);
|
||||
@@ -220,6 +222,44 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Greeting Section -->
|
||||
<section class="mb-6">
|
||||
<h3 class="text-sm font-medium text-[var(--accent-primary)] uppercase tracking-wider mb-3">
|
||||
Greeting
|
||||
</h3>
|
||||
|
||||
<!-- Enable/Disable Toggle -->
|
||||
<div class="mb-4">
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={config.greeting_enabled}
|
||||
class="w-4 h-4 rounded border-[var(--border-color)] bg-[var(--bg-primary)] text-[var(--accent-primary)] focus:ring-[var(--accent-primary)]"
|
||||
/>
|
||||
<span class="text-sm text-gray-300">Send greeting on connect</span>
|
||||
</label>
|
||||
<p class="text-xs text-gray-500 mt-1 ml-7">
|
||||
Automatically greet you when a session starts with time-based messages
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Custom Greeting Prompt -->
|
||||
{#if config.greeting_enabled}
|
||||
<div class="mb-4">
|
||||
<label for="greeting-prompt" class="block text-sm text-gray-400 mb-1">
|
||||
Custom Greeting Prompt <span class="text-gray-600">(optional)</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="greeting-prompt"
|
||||
bind:value={config.greeting_custom_prompt}
|
||||
rows="3"
|
||||
placeholder="Leave empty for time-based greetings, or customize how you'd like to be greeted..."
|
||||
class="w-full px-3 py-2 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded-lg text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent-primary)] resize-none"
|
||||
></textarea>
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<!-- MCP Servers Section -->
|
||||
<section class="mb-6">
|
||||
<h3 class="text-sm font-medium text-[var(--accent-primary)] uppercase tracking-wider mb-3">
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
mcp_servers_json: null,
|
||||
auto_granted_tools: [],
|
||||
theme: "dark",
|
||||
greeting_enabled: true,
|
||||
greeting_custom_prompt: null,
|
||||
});
|
||||
|
||||
onMount(async () => {
|
||||
|
||||
@@ -10,6 +10,8 @@ export interface HikariConfig {
|
||||
mcp_servers_json: string | null;
|
||||
auto_granted_tools: string[];
|
||||
theme: Theme;
|
||||
greeting_enabled: boolean;
|
||||
greeting_custom_prompt: string | null;
|
||||
}
|
||||
|
||||
const defaultConfig: HikariConfig = {
|
||||
@@ -19,6 +21,8 @@ const defaultConfig: HikariConfig = {
|
||||
mcp_servers_json: null,
|
||||
auto_granted_tools: [],
|
||||
theme: "dark",
|
||||
greeting_enabled: true,
|
||||
greeting_custom_prompt: null,
|
||||
};
|
||||
|
||||
function createConfigStore() {
|
||||
|
||||
+45
-1
@@ -1,6 +1,8 @@
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { claudeStore } from "$lib/stores/claude";
|
||||
import { characterState } from "$lib/stores/character";
|
||||
import { configStore } from "$lib/stores/config";
|
||||
import type { ConnectionStatus, PermissionPromptEvent } from "$lib/types/messages";
|
||||
import type { CharacterState } from "$lib/types/states";
|
||||
|
||||
@@ -9,6 +11,46 @@ interface StateChangePayload {
|
||||
tool_name: string | null;
|
||||
}
|
||||
|
||||
function getTimeOfDay(): string {
|
||||
const hour = new Date().getHours();
|
||||
|
||||
if (hour >= 5 && hour < 12) {
|
||||
return "morning";
|
||||
} else if (hour >= 12 && hour < 17) {
|
||||
return "afternoon";
|
||||
} else if (hour >= 17 && hour < 21) {
|
||||
return "evening";
|
||||
} else {
|
||||
return "late night";
|
||||
}
|
||||
}
|
||||
|
||||
function generateGreetingPrompt(): string {
|
||||
const timeOfDay = getTimeOfDay();
|
||||
return `[System: A new session has started. It's currently ${timeOfDay}. Please greet the user warmly and briefly. Keep it short - just 1-2 sentences.]`;
|
||||
}
|
||||
|
||||
async function sendGreeting() {
|
||||
const config = configStore.getConfig();
|
||||
|
||||
if (!config.greeting_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const greetingPrompt = config.greeting_custom_prompt?.trim() || generateGreetingPrompt();
|
||||
|
||||
// Don't show the system prompt in the UI - just trigger Claude to respond
|
||||
characterState.setState("thinking");
|
||||
|
||||
try {
|
||||
await invoke("send_prompt", { message: greetingPrompt });
|
||||
} catch (error) {
|
||||
console.error("Failed to send greeting:", error);
|
||||
claudeStore.addLine("error", `Failed to send greeting: ${error}`);
|
||||
characterState.setTemporaryState("error", 3000);
|
||||
}
|
||||
}
|
||||
|
||||
interface OutputPayload {
|
||||
line_type: string;
|
||||
content: string;
|
||||
@@ -16,13 +58,15 @@ interface OutputPayload {
|
||||
}
|
||||
|
||||
export async function initializeTauriListeners() {
|
||||
await listen<string>("claude:connection", (event) => {
|
||||
await listen<string>("claude:connection", async (event) => {
|
||||
const status = event.payload as ConnectionStatus;
|
||||
claudeStore.setConnectionStatus(status);
|
||||
|
||||
if (status === "connected") {
|
||||
claudeStore.addLine("system", "Connected to Claude Code");
|
||||
characterState.setState("idle");
|
||||
// Send greeting when connection is established
|
||||
await sendGreeting();
|
||||
} else if (status === "disconnected") {
|
||||
claudeStore.addLine("system", "Disconnected from Claude Code");
|
||||
characterState.setState("idle");
|
||||
|
||||
Reference in New Issue
Block a user