generated from nhcarrigan/template
feat: massive overhaul to manage costs (#103)
### Explanation _No response_ ### Issue Closes #102 ### 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: #103 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit was merged in pull request #103.
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
type SlashCommand,
|
||||
} from "$lib/commands/slashCommands";
|
||||
import { configStore, isStreamerMode } from "$lib/stores/config";
|
||||
import { stats, estimateMessageCost, formatTokenCount } from "$lib/stores/stats";
|
||||
import AttachmentPreview from "$lib/components/AttachmentPreview.svelte";
|
||||
import SnippetLibraryPanel from "$lib/components/SnippetLibraryPanel.svelte";
|
||||
import QuickActionsPanel from "$lib/components/QuickActionsPanel.svelte";
|
||||
@@ -50,6 +51,13 @@
|
||||
let showClipboardHistory = $state(false);
|
||||
let streamerModeActive = $state(false);
|
||||
|
||||
// Cost estimation for pre-submission display
|
||||
let costEstimate = $derived(
|
||||
inputValue.trim()
|
||||
? estimateMessageCost(inputValue, $stats.context_tokens_used, $stats.model)
|
||||
: null
|
||||
);
|
||||
|
||||
// Context menu state
|
||||
let textareaElement: HTMLTextAreaElement | undefined = $state();
|
||||
let contextMenuShow = $state(false);
|
||||
@@ -913,6 +921,13 @@ User: ${formattedMessage}`;
|
||||
</div>
|
||||
|
||||
<div class="button-wrapper">
|
||||
{#if costEstimate && isConnected && !isProcessing}
|
||||
<div class="cost-estimate" title="Estimated input cost for this message">
|
||||
<span class="cost-tokens">+{formatTokenCount(costEstimate.messageTokens)}</span>
|
||||
<span class="cost-value">${costEstimate.estimatedCost.toFixed(4)}</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<button type="button" onclick={handleFilePicker} class="attach-button" title="Attach files">
|
||||
<svg
|
||||
width="20"
|
||||
@@ -1138,6 +1153,28 @@ User: ${formattedMessage}`;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
height: 100%;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.cost-estimate {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
padding: 0 8px;
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-secondary);
|
||||
min-width: 60px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.cost-tokens {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.cost-value {
|
||||
font-family: var(--font-mono, monospace);
|
||||
color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.attach-button {
|
||||
|
||||
Reference in New Issue
Block a user