generated from nhcarrigan/template
feat: add discord rich presence (#105)
### 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: #105 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit was merged in pull request #105.
This commit is contained in:
@@ -51,6 +51,7 @@
|
||||
session_cost_budget: null,
|
||||
budget_action: "warn",
|
||||
budget_warning_threshold: 0.8,
|
||||
discord_rpc_enabled: true,
|
||||
});
|
||||
|
||||
let showCustomThemeEditor = $state(false);
|
||||
@@ -967,6 +968,30 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Discord Rich Presence Section -->
|
||||
<section class="pt-6 pb-6 border-t border-[var(--border-color)]">
|
||||
<h3 class="text-lg font-semibold text-[var(--accent-primary)] mb-4 flex items-center gap-2">
|
||||
<span>🎮</span>
|
||||
<span>Discord Rich Presence</span>
|
||||
</h3>
|
||||
|
||||
<!-- Enable/Disable Discord RPC -->
|
||||
<div class="mb-4">
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={config.discord_rpc_enabled}
|
||||
class="w-4 h-4 text-[var(--accent-primary)] bg-[var(--bg-primary)] border-[var(--border-color)] rounded focus:ring-[var(--accent-primary)] focus:ring-2"
|
||||
/>
|
||||
<span class="text-sm text-[var(--text-primary)]">Show activity in Discord</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="text-xs text-[var(--text-tertiary)]">
|
||||
Display your current conversation session name and model in Discord when enabled.
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Save Button -->
|
||||
<div class="sticky bottom-0 pt-4 pb-2 bg-[var(--bg-secondary)]">
|
||||
<button
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { claudeStore, isClaudeProcessing } from "$lib/stores/claude";
|
||||
import { characterState } from "$lib/stores/character";
|
||||
import { handleNewUserMessage } from "$lib/notifications/rules";
|
||||
import { setSkipNextGreeting } from "$lib/tauri";
|
||||
import { setSkipNextGreeting, updateDiscordRpc } from "$lib/tauri";
|
||||
import { clipboardStore } from "$lib/stores/clipboard";
|
||||
import {
|
||||
setShouldRestoreHistory,
|
||||
@@ -26,6 +26,7 @@
|
||||
type SlashCommand,
|
||||
} from "$lib/commands/slashCommands";
|
||||
import { configStore, isStreamerMode } from "$lib/stores/config";
|
||||
import { conversationsStore } from "$lib/stores/conversations";
|
||||
import { stats, estimateMessageCost, formatTokenCount } from "$lib/stores/stats";
|
||||
import AttachmentPreview from "$lib/components/AttachmentPreview.svelte";
|
||||
import SnippetLibraryPanel from "$lib/components/SnippetLibraryPanel.svelte";
|
||||
@@ -350,6 +351,17 @@ User: ${formattedMessage}`;
|
||||
working_dir: workingDir,
|
||||
},
|
||||
});
|
||||
|
||||
// Update Discord RPC when reconnecting
|
||||
const config = configStore.getConfig();
|
||||
const activeConversation = get(conversationsStore.activeConversation);
|
||||
if (activeConversation) {
|
||||
await updateDiscordRpc(
|
||||
activeConversation.name,
|
||||
config.model || "claude",
|
||||
activeConversation.startedAt
|
||||
);
|
||||
}
|
||||
} catch (reconnectError) {
|
||||
console.error("Failed to auto-reconnect:", reconnectError);
|
||||
claudeStore.addLine("error", `Failed to reconnect: ${reconnectError}`);
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
import { claudeStore, hasPermissionPending } from "$lib/stores/claude";
|
||||
import { characterState } from "$lib/stores/character";
|
||||
import type { PermissionRequest } from "$lib/types/messages";
|
||||
import { updateDiscordRpc } from "$lib/tauri";
|
||||
import { conversationsStore } from "$lib/stores/conversations";
|
||||
import { configStore } from "$lib/stores/config";
|
||||
|
||||
let isVisible = $state(false);
|
||||
let permission: PermissionRequest | null = $state(null);
|
||||
@@ -64,6 +67,17 @@
|
||||
},
|
||||
});
|
||||
|
||||
// Update Discord RPC when reconnecting after permission grant
|
||||
const config = configStore.getConfig();
|
||||
const activeConversation = get(conversationsStore.activeConversation);
|
||||
if (activeConversation) {
|
||||
await updateDiscordRpc(
|
||||
activeConversation.name,
|
||||
config.model || "claude",
|
||||
activeConversation.startedAt
|
||||
);
|
||||
}
|
||||
|
||||
// Wait for connection to establish
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
createSummary,
|
||||
sanitizeForJson,
|
||||
} from "$lib/utils/conversationUtils";
|
||||
import { updateDiscordRpc } from "$lib/tauri";
|
||||
|
||||
const DISCORD_URL = "https://chat.nhcarrigan.com";
|
||||
const DONATE_URL = "https://donate.nhcarrigan.com";
|
||||
@@ -86,6 +87,7 @@
|
||||
session_cost_budget: null,
|
||||
budget_action: "warn",
|
||||
budget_warning_threshold: 0.8,
|
||||
discord_rpc_enabled: true,
|
||||
});
|
||||
|
||||
let streamerModeActive = $state(false);
|
||||
@@ -165,6 +167,16 @@
|
||||
allowed_tools: allAllowedTools,
|
||||
},
|
||||
});
|
||||
|
||||
// Update Discord RPC when a new session starts
|
||||
const activeConversation = get(conversationsStore.activeConversation);
|
||||
if (activeConversation) {
|
||||
await updateDiscordRpc(
|
||||
activeConversation.name,
|
||||
currentConfig.model || "claude",
|
||||
activeConversation.startedAt
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to start Claude:", error);
|
||||
claudeStore.addLine("error", `Connection failed: ${error}`);
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
import { claudeStore, hasQuestionPending } from "$lib/stores/claude";
|
||||
import { characterState } from "$lib/stores/character";
|
||||
import type { UserQuestionEvent } from "$lib/types/messages";
|
||||
import { updateDiscordRpc } from "$lib/tauri";
|
||||
import { conversationsStore } from "$lib/stores/conversations";
|
||||
import { configStore } from "$lib/stores/config";
|
||||
|
||||
let isVisible = $state(false);
|
||||
let question: UserQuestionEvent | null = $state(null);
|
||||
@@ -98,6 +101,17 @@
|
||||
},
|
||||
});
|
||||
|
||||
// Update Discord RPC when reconnecting after answering question
|
||||
const config = configStore.getConfig();
|
||||
const activeConversation = get(conversationsStore.activeConversation);
|
||||
if (activeConversation) {
|
||||
await updateDiscordRpc(
|
||||
activeConversation.name,
|
||||
config.model || "claude",
|
||||
activeConversation.startedAt
|
||||
);
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
|
||||
if (conversationHistory) {
|
||||
|
||||
Reference in New Issue
Block a user