Skip to content

Commit 56bc969

Browse files
ClaudiaFangclaude
andcommitted
fix(dashboard): resolve pre-existing tsc --noEmit errors
- Dispatch types (DispatchTone/Format/Response) were imported from '@/lib/api', which only re-exports its own request helpers, not the types it privately imports from '@/lib/types'. Import from the correct module in the four consuming components. - CacheBySourceCard's hit-rate calc referenced row.cacheRead/totalInput, which don't exist on CacheBySourceRow (real fields are cacheReadTokens/totalInputTokens, used correctly two lines below) — hit rate was always computing as 0. - LLMConfig.provider and SettingsPage's local LLMProvider union were each missing a different subset of providers vs. the server's canonical enum (server/src/schemas/config.ts); reconciled both to the full 8-provider set. - sse.ts: TS 5.7+'s DOM lib types TextDecoderStream.writable as WritableStream<BufferSource>, no longer structurally matching pipeThrough's expected pair type; cast through TransformStream to restore the correct shape (no runtime change). - AssistantMarkdown's `code` renderer used a custom prop type with an index signature that no longer matches react-markdown's Components type; switched to React.HTMLAttributes<HTMLElement>. - BulkAnalyzeButton.test.tsx: test fixtures/mocks had drifted from the current Session/AnalysisState/useAnalysis shapes (missing Session fields, inferred Promise<unknown> instead of Promise<void>, widened string literals in a Map literal). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent f94ba11 commit 56bc969

10 files changed

Lines changed: 25 additions & 18 deletions

File tree

dashboard/src/components/analysis/BulkAnalyzeButton.test.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ function makeSession(id: string): Session {
4040
git_branch: null,
4141
claude_version: null,
4242
source_tool: 'claude-code',
43+
home_id: null,
4344
device_id: null,
4445
device_hostname: null,
4546
device_platform: null,
@@ -49,6 +50,12 @@ function makeSession(id: string): Session {
4950
cache_creation_tokens: null,
5051
cache_read_tokens: null,
5152
estimated_cost_usd: null,
53+
models_used: null,
54+
primary_model: null,
55+
usage_source: null,
56+
compact_count: 0,
57+
auto_compact_count: 0,
58+
slash_commands: null,
5259
};
5360
}
5461

@@ -71,15 +78,12 @@ function setup(sessions: Session[], onComplete?: () => void, options?: {
7178
return analysisStates.get(key);
7279
});
7380

74-
const mockStartAnalysis = vi.fn(async (session: Session) => {
81+
const mockStartAnalysis = vi.fn(async (): Promise<void> => {
7582
if (startAnalysisBehavior === 'reject') {
7683
throw new Error('Analysis failed');
7784
}
7885
if (startAnalysisBehavior === 'deferred') {
79-
return new Promise(() => {});
80-
}
81-
if (startAnalysisBehavior === 'immediate-fail') {
82-
return Promise.resolve();
86+
return new Promise<void>(() => {});
8387
}
8488
return Promise.resolve();
8589
});
@@ -110,7 +114,7 @@ beforeEach(() => {
110114
describe('BulkAnalyzeButton', () => {
111115
describe('unconfigured state', () => {
112116
it('renders disabled button with configure message when LLM not configured', () => {
113-
mockUseLlmConfig.mockReturnValue({ data: null } as ReturnType<typeof useLlmConfig>);
117+
mockUseLlmConfig.mockReturnValue({ data: null } as unknown as ReturnType<typeof useLlmConfig>);
114118
setup([makeSession('s1')]);
115119
const btn = screen.getByRole('button', { name: /analyze selected/i });
116120
expect(btn).toBeDisabled();
@@ -201,7 +205,7 @@ describe('BulkAnalyzeButton', () => {
201205
});
202206

203207
it('shows failed count when some sessions error', async () => {
204-
const analysisStates = new Map([
208+
const analysisStates = new Map<string, AnalysisState>([
205209
['s1:session', { status: 'complete', result: { success: true } }],
206210
['s2:session', { status: 'error', result: { success: false, error: 'API timeout' } }],
207211
]);

dashboard/src/components/chat/message/markdown/AssistantMarkdown.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export function AssistantMarkdown({ content, codeStyle, searchQuery }: Assistant
5757
</td>
5858
);
5959
},
60-
code(props: { children?: React.ReactNode; className?: string; [key: string]: unknown }) {
60+
code(props: React.HTMLAttributes<HTMLElement> & { node?: unknown }) {
6161
const { children, className, ...rest } = props;
6262
const langMatch = /language-(\w+)/.exec(className || '');
6363
const isBlock = !!langMatch;

dashboard/src/components/dashboard/CacheBySourceCard.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,8 @@ export function CacheBySourceCard({ range, homeId, source }: CacheBySourceCardPr
119119
}
120120

121121
const formattedData: FormattedData[] = chartData.map((row) => {
122-
const totalWithCache = (row.cacheRead || 0) + (row.totalInput || 0);
123-
const hitRate = totalWithCache > 0 ? (row.cacheRead / totalWithCache) * 100 : 0;
122+
const totalWithCache = (row.cacheReadTokens || 0) + (row.totalInputTokens || 0);
123+
const hitRate = totalWithCache > 0 ? ((row.cacheReadTokens || 0) / totalWithCache) * 100 : 0;
124124
return {
125125
sourceTool: row.sourceTool || 'Unknown',
126126
cacheCreation: row.cacheCreationTokens || 0,

dashboard/src/components/dispatch/CoverImagePromptSection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Button } from '@/components/ui/button';
66
import { Textarea } from '@/components/ui/textarea';
77
import { Alert, AlertDescription } from '@/components/ui/alert';
88
import { generateDispatchImagePrompt } from '@/lib/api';
9-
import type { DispatchFormat } from '@/lib/api';
9+
import type { DispatchFormat } from '@/lib/types';
1010

1111
interface CoverImagePromptSectionProps {
1212
title: string;

dashboard/src/components/dispatch/DispatchDrawer.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ import { Textarea } from '@/components/ui/textarea';
3131
import { Switch } from '@/components/ui/switch';
3232
import { generateDispatch } from '@/lib/api';
3333
import { PostOverlay } from './PostOverlay';
34-
import type { Insight, DispatchPrefill } from '@/lib/types';
35-
import type { DispatchTone, DispatchFormat, DispatchResponse } from '@/lib/api';
34+
import type { Insight, DispatchPrefill, DispatchTone, DispatchFormat, DispatchResponse } from '@/lib/types';
3635

3736
const FORMAT_OPTIONS: { value: DispatchFormat; label: string; description: string }[] = [
3837
{ value: 'blog', label: 'Blog post', description: 'Full narrative, 800-1000 words, markdown ready to paste to dev.to / Hashnode' },

dashboard/src/components/dispatch/PostOverlay.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { X } from 'lucide-react';
22
import { Dialog as DialogPrimitive } from 'radix-ui';
33
import { PostPreview } from './PostPreview';
44
import { CoverImagePromptSection } from './CoverImagePromptSection';
5-
import type { DispatchResponse } from '@/lib/api';
5+
import type { DispatchResponse } from '@/lib/types';
66

77
interface PostOverlayProps {
88
open: boolean;

dashboard/src/components/dispatch/PostPreview.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { toast } from 'sonner';
55
import { Copy, Download, Check, AlertTriangle } from 'lucide-react';
66
import { Button } from '@/components/ui/button';
77
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
8-
import type { DispatchResponse } from '@/lib/api';
8+
import type { DispatchResponse } from '@/lib/types';
99

1010
interface PostPreviewProps {
1111
result: DispatchResponse;

dashboard/src/lib/sse.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
export async function* parseSSEStream(
1212
body: ReadableStream<Uint8Array>
1313
): AsyncGenerator<{ event: string; data: string }> {
14-
const reader = body.pipeThrough(new TextDecoderStream()).getReader();
14+
// TS 5.7+'s lib.dom types TextDecoderStream.writable as WritableStream<BufferSource>,
15+
// which no longer structurally matches ReadableWritablePair<string, Uint8Array<ArrayBufferLike>>.
16+
// The runtime behavior is unaffected — only the type shape needs coercing.
17+
const decoder = new TextDecoderStream() as unknown as TransformStream<Uint8Array<ArrayBufferLike>, string>;
18+
const reader = body.pipeThrough(decoder).getReader();
1519
let buffer = '';
1620
let currentEvent = '';
1721
let currentData = '';

dashboard/src/lib/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ export interface DispatchImagePromptResponse {
388388
// LLM config from /api/config/llm
389389
export interface LLMConfig {
390390
dashboardPort: number;
391-
provider?: 'openai' | 'anthropic' | 'gemini' | 'ollama' | 'llamacpp' | 'openai-compatible';
391+
provider?: 'openai' | 'anthropic' | 'gemini' | 'ollama' | 'openrouter' | 'mistral' | 'llamacpp' | 'openai-compatible';
392392
model?: string;
393393
apiKey?: string; // masked by server before returning (first4...last4)
394394
baseUrl?: string;

dashboard/src/pages/SettingsPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
Trash2,
2626
} from 'lucide-react';
2727

28-
type LLMProvider = 'openai' | 'anthropic' | 'gemini' | 'ollama' | 'openrouter' | 'mistral' | 'openai-compatible';
28+
type LLMProvider = 'openai' | 'anthropic' | 'gemini' | 'ollama' | 'openrouter' | 'mistral' | 'llamacpp' | 'openai-compatible';
2929

3030
interface ProviderInfo {
3131
id: LLMProvider;

0 commit comments

Comments
 (0)