Skip to content

Commit c82b121

Browse files
author
MasterSelects
committed
feat: show live credit balance in toolbar
1 parent 5274914 commit c82b121

4 files changed

Lines changed: 59 additions & 2 deletions

File tree

src/App.css

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,42 @@ input, textarea {
736736
margin-left: 0;
737737
}
738738

739+
.toolbar-credit-pill {
740+
display: inline-flex;
741+
align-items: center;
742+
gap: 8px;
743+
height: 28px;
744+
padding: 0 10px;
745+
border: 1px solid rgba(120, 138, 255, 0.35);
746+
border-radius: 999px;
747+
background: linear-gradient(180deg, rgba(73, 86, 198, 0.3), rgba(39, 47, 108, 0.24));
748+
color: var(--text-primary);
749+
cursor: pointer;
750+
transition:
751+
border-color var(--transition-fast),
752+
background var(--transition-fast),
753+
transform var(--transition-fast);
754+
}
755+
756+
.toolbar-credit-pill:hover {
757+
border-color: rgba(132, 149, 255, 0.58);
758+
background: linear-gradient(180deg, rgba(84, 98, 225, 0.36), rgba(49, 58, 134, 0.3));
759+
transform: translateY(-1px);
760+
}
761+
762+
.toolbar-credit-pill-label {
763+
color: var(--text-secondary);
764+
font-size: var(--font-xs);
765+
text-transform: uppercase;
766+
letter-spacing: 0.06em;
767+
}
768+
769+
.toolbar-credit-pill-value {
770+
color: #f4f7ff;
771+
font-size: var(--font-sm);
772+
font-variant-numeric: tabular-nums;
773+
}
774+
739775
.toolbar-right .version {
740776
color: var(--text-secondary);
741777
font-size: var(--font-sm);

src/components/common/Toolbar.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export function Toolbar({ onOpenChangelog }: ToolbarProps) {
5454
saveLayoutAsDefault: s.saveLayoutAsDefault,
5555
})));
5656
const { isSupported: midiSupported, isEnabled: midiEnabled, enableMIDI, disableMIDI, devices } = useMIDI();
57+
const accountCredits = useAccountStore((s) => s.creditBalance);
5758
const accountSession = useAccountStore((s) => s.session);
5859
const accountUser = useAccountStore((s) => s.user);
5960
const openAccountDialog = useAccountStore((s) => s.openAccountDialog);
@@ -800,6 +801,17 @@ export function Toolbar({ onOpenChangelog }: ToolbarProps) {
800801

801802
{/* Status */}
802803
<div className="toolbar-section toolbar-right">
804+
{accountSession?.authenticated && (
805+
<button
806+
className="toolbar-credit-pill"
807+
onClick={openAccountDialog}
808+
title={`${accountCredits} credits available`}
809+
type="button"
810+
>
811+
<span className="toolbar-credit-pill-label">Credits</span>
812+
<strong className="toolbar-credit-pill-value">{accountCredits}</strong>
813+
</button>
814+
)}
803815
<button
804816
className="menu-trigger"
805817
onClick={() => (accountSession?.authenticated ? openAccountDialog() : openAuthDialog())}

src/components/panels/AIChatPanel.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ export function AIChatPanel() {
160160
const { apiKeys, openSettings, aiApprovalMode } = useSettingsStore();
161161
const hostedAIEnabled = useAccountStore((s) => s.hostedAIEnabled);
162162
const accountSession = useAccountStore((s) => s.session);
163+
const loadAccountState = useAccountStore((s) => s.loadAccountState);
163164
const openAuthDialog = useAccountStore((s) => s.openAuthDialog);
164165
const openPricingDialog = useAccountStore((s) => s.openPricingDialog);
165166
const openAccountDialog = useAccountStore((s) => s.openAccountDialog);
@@ -405,10 +406,13 @@ export function AIChatPanel() {
405406
} catch (err) {
406407
setError(err instanceof Error ? err.message : 'Failed to send message');
407408
} finally {
409+
if (accessMode === 'hosted') {
410+
void loadAccountState();
411+
}
408412
setIsLoading(false);
409413
setCurrentToolAction(null);
410414
}
411-
}, [input, hasAccess, isLoading, buildAPIMessages, callOpenAI, aiApprovalMode]);
415+
}, [input, hasAccess, isLoading, buildAPIMessages, callOpenAI, aiApprovalMode, accessMode, loadAccountState]);
412416

413417
// Handle key press
414418
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {

src/components/panels/AIVideoPanel.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ export function AIVideoPanel() {
162162
const { apiKeys, openSettings } = useSettingsStore();
163163
const accountSession = useAccountStore((s) => s.session);
164164
const entitlements = useAccountStore((s) => s.entitlements);
165+
const loadAccountState = useAccountStore((s) => s.loadAccountState);
165166
const openAuthDialog = useAccountStore((s) => s.openAuthDialog);
166167
const openPricingDialog = useAccountStore((s) => s.openPricingDialog);
167168
const openAccountDialog = useAccountStore((s) => s.openAccountDialog);
@@ -528,6 +529,10 @@ export function AIVideoPanel() {
528529
taskId = await service.createImageToVideo(params);
529530
}
530531

532+
if (selectedService === 'kieai' && !apiKeys.kieai && hasHostedKlingAccess) {
533+
void loadAccountState();
534+
}
535+
531536
// Add job to list
532537
const job: GenerationJob = {
533538
id: taskId,
@@ -587,7 +592,7 @@ export function AIVideoPanel() {
587592
}, [
588593
prompt, negativePrompt, selectedProvider, selectedVersion, duration, aspectRatio, mode, cfgScale, generateAudio,
589594
generationType, startImagePreview, startCropData, endImagePreview, endCropData, isGenerating,
590-
importVideoToProject, getCroppedImageUrl, getActiveService,
595+
importVideoToProject, getCroppedImageUrl, getActiveService, selectedService, apiKeys.kieai, hasHostedKlingAccess, loadAccountState,
591596
]);
592597

593598
// Remove job from list

0 commit comments

Comments
 (0)