fix(panel): resolve undefined font vars + remove dead client code

- layout.tsx/globals.css: font-sans/font-mono pointed at undefined --font-geist-*
  vars (Geist-starter leftover); now resolve to the actually-loaded Inter via
  --font-inter. No visible change.
- remove orphaned company-goals-card.tsx (unmounted; live charter UI is goals-tab.tsx)
- remove dead streamApi transcription REST client (zero consumers) + its barrel
  export; AgentStreamViewer (a different, live websocket-based component) untouched.
This commit is contained in:
Renn F
2026-07-04 08:17:45 +02:00
parent d6fffae6dd
commit b5fbc72372
5 changed files with 4 additions and 355 deletions
+2 -2
View File
@@ -7,8 +7,8 @@
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
--font-sans: var(--font-inter);
--font-mono: var(--font-inter);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
+2 -2
View File
@@ -3,7 +3,7 @@ import { Inter } from "next/font/google";
import "./globals.css";
import { Providers } from "@/components/providers";
const inter = Inter({ subsets: ["latin"] });
const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
export const metadata: Metadata = {
title: "RoboCo Control Panel",
@@ -17,7 +17,7 @@ export default function RootLayout({
}>) {
return (
<html lang="en" suppressHydrationWarning>
<body className={inter.className}>
<body className={`${inter.className} ${inter.variable}`}>
<Providers>{children}</Providers>
</body>
</html>
@@ -1,183 +0,0 @@
"use client";
import { useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
companyGoalsApi,
type CompanyGoalsUpdate,
} from "@/lib/api/company-goals";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Button } from "@/components/ui/button";
import { Target, Save } from "lucide-react";
import { toast } from "sonner";
function parseError(e: unknown): string {
return e instanceof Error ? e.message : "parse error";
}
export function CompanyGoalsCard() {
const queryClient = useQueryClient();
// null = "show the server value"; deriving the displayed value avoids syncing
// query state into local state with an effect (react-hooks/set-state-in-effect).
const [northStar, setNorthStar] = useState<string | null>(null);
const [constraints, setConstraints] = useState<string | null>(null);
const [objectives, setObjectives] = useState<string | null>(null);
const [policy, setPolicy] = useState<string | null>(null);
const [brandVoice, setBrandVoice] = useState<string | null>(null);
const { data: goals, isLoading } = useQuery({
queryKey: ["company-goals"],
queryFn: companyGoalsApi.get,
});
const northStarVal = northStar ?? goals?.north_star ?? "";
const constraintsVal = constraints ?? (goals?.constraints ?? []).join("\n");
const objectivesVal =
objectives ?? JSON.stringify(goals?.objectives ?? [], null, 2);
const policyVal =
policy ?? JSON.stringify(goals?.operating_policy ?? {}, null, 2);
const brandVoiceVal = brandVoice ?? goals?.brand_voice ?? "";
const saveMutation = useMutation({
mutationFn: (update: CompanyGoalsUpdate) => companyGoalsApi.update(update),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["company-goals"] });
setNorthStar(null);
setConstraints(null);
setObjectives(null);
setPolicy(null);
setBrandVoice(null);
toast.success("Company charter updated");
},
onError: (error) => {
toast.error(`Failed to save: ${parseError(error)}`);
},
});
const handleSave = () => {
let parsedObjectives: Record<string, unknown>[];
let parsedPolicy: Record<string, unknown>;
try {
parsedObjectives = JSON.parse(objectivesVal);
if (!Array.isArray(parsedObjectives)) {
throw new Error("must be a JSON array");
}
} catch (e) {
toast.error(`Objectives: invalid JSON — ${parseError(e)}`);
return;
}
try {
parsedPolicy = JSON.parse(policyVal);
if (
typeof parsedPolicy !== "object" ||
parsedPolicy === null ||
Array.isArray(parsedPolicy)
) {
throw new Error("must be a JSON object");
}
} catch (e) {
toast.error(`Operating policy: invalid JSON — ${parseError(e)}`);
return;
}
saveMutation.mutate({
north_star: northStarVal,
objectives: parsedObjectives,
constraints: constraintsVal
.split("\n")
.map((c) => c.trim())
.filter(Boolean),
operating_policy: parsedPolicy,
brand_voice: brandVoiceVal,
});
};
return (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Target className="h-5 w-5" />
Company Charter
</CardTitle>
<CardDescription>
The CEO-owned north star, objectives, constraints, operating policy,
and brand voice. Injected into every agent&apos;s briefing so all
work stays goal-aware.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="north-star">North star</Label>
<Textarea
id="north-star"
rows={3}
value={northStarVal}
disabled={isLoading}
onChange={(e) => setNorthStar(e.target.value)}
placeholder="The long-term vision in one or two sentences..."
/>
</div>
<div className="space-y-2">
<Label htmlFor="brand-voice">Brand voice</Label>
<Textarea
id="brand-voice"
rows={3}
value={brandVoiceVal}
disabled={isLoading}
onChange={(e) => setBrandVoice(e.target.value)}
placeholder="Sample posts or a style description for the Head of Marketing's drafts..."
/>
</div>
<div className="space-y-2">
<Label htmlFor="constraints">Constraints (one per line)</Label>
<Textarea
id="constraints"
rows={3}
value={constraintsVal}
disabled={isLoading}
onChange={(e) => setConstraints(e.target.value)}
placeholder={"AGPL only\nNo external data egress"}
/>
</div>
<div className="space-y-2">
<Label htmlFor="objectives">Objectives (JSON array)</Label>
<Textarea
id="objectives"
rows={6}
value={objectivesVal}
disabled={isLoading}
onChange={(e) => setObjectives(e.target.value)}
className="font-mono text-xs"
placeholder='[{"metric": "NPS", "target": 50, "status": "active"}]'
/>
</div>
<div className="space-y-2">
<Label htmlFor="policy">Operating policy (JSON object)</Label>
<Textarea
id="policy"
rows={6}
value={policyVal}
disabled={isLoading}
onChange={(e) => setPolicy(e.target.value)}
className="font-mono text-xs"
placeholder='{"autonomy_level": "assisted", "monthly_budget_cap": 500}'
/>
</div>
<Button
onClick={handleSave}
disabled={saveMutation.isPending || isLoading}
>
<Save className="h-4 w-4 mr-2" />
{saveMutation.isPending ? "Saving..." : "Save charter"}
</Button>
</CardContent>
</Card>
);
}
-1
View File
@@ -10,7 +10,6 @@ export { productsApi } from "./products";
export { workSessionsApi } from "./work-sessions";
export { gitApi } from "./git";
export { a2aApi } from "./a2a";
export { streamApi } from "./stream";
export { settingsApi } from "./settings";
export { companyGoalsApi } from "./company-goals";
export { releaseApi } from "./release";
-167
View File
@@ -1,167 +0,0 @@
/**
* Stream API Client
*
* API functions for transcription and extraction operations.
*/
import api from "./client";
import { isMockMode } from "@/lib/mock-data";
// =============================================================================
// Types
// =============================================================================
export interface StreamChunkRequest {
channel_name: string;
audio_data: string; // Base64 encoded
chunk_index: number;
is_final?: boolean;
}
export interface StreamChunkResponse {
status: string;
chunk_index: number;
queued: boolean;
}
export interface StreamCompleteRequest {
channel_name: string;
session_id?: string;
}
export interface StreamCompleteResponse {
status: string;
channel_name: string;
total_chunks: number;
transcription_id?: string;
}
export interface ExtractionRequest {
content: string;
extraction_type: "tasks" | "decisions" | "action_items" | "summary";
context?: string;
}
export interface ExtractionResponse {
extraction_type: string;
results: Record<string, unknown>[];
confidence: number;
}
export interface TranscriptionStats {
total_transcriptions: number;
active_channels: number;
chunks_processed: number;
avg_processing_time_ms: number;
}
export interface ChannelPermissions {
channel_name: string;
can_transcribe: boolean;
can_extract: boolean;
rate_limit?: number;
}
// =============================================================================
// API Client
// =============================================================================
export const streamApi = {
// ===========================================================================
// TRANSCRIPTION ENDPOINTS
// ===========================================================================
/**
* Submit an audio chunk for transcription
*/
submitChunk: async (
request: StreamChunkRequest,
): Promise<StreamChunkResponse> => {
if (isMockMode()) {
return {
status: "queued",
chunk_index: request.chunk_index,
queued: true,
};
}
const { data } = await api.post<StreamChunkResponse>(
"/stream/chunk",
request,
);
return data;
},
/**
* Complete a transcription session
*/
complete: async (
request: StreamCompleteRequest,
): Promise<StreamCompleteResponse> => {
if (isMockMode()) {
return {
status: "completed",
channel_name: request.channel_name,
total_chunks: 10,
transcription_id: `trans-${Date.now()}`,
};
}
const { data } = await api.post<StreamCompleteResponse>(
"/stream/complete",
request,
);
return data;
},
// ===========================================================================
// EXTRACTION ENDPOINTS
// ===========================================================================
/**
* Extract structured information from content
*/
extract: async (request: ExtractionRequest): Promise<ExtractionResponse> => {
if (isMockMode()) {
return {
extraction_type: request.extraction_type,
results: [],
confidence: 0.85,
};
}
const { data } = await api.post<ExtractionResponse>(
"/stream/extract",
request,
);
return data;
},
// ===========================================================================
// STATS & PERMISSIONS
// ===========================================================================
/**
* Get transcription statistics
*/
getStats: async (): Promise<TranscriptionStats> => {
if (isMockMode()) {
return {
total_transcriptions: 100,
active_channels: 5,
chunks_processed: 1500,
avg_processing_time_ms: 250,
};
}
const { data } = await api.get<TranscriptionStats>("/stream/stats");
return data;
},
/**
* Get all channel permissions
*/
getPermissions: async (): Promise<ChannelPermissions[]> => {
if (isMockMode()) {
return [];
}
const { data } = await api.get<ChannelPermissions[]>("/stream/permissions");
return data;
},
};