| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { create } from 'zustand'; |
| import { persist, createJSONStorage } from 'zustand/middleware'; |
|
|
| |
|
|
| export type HighlightMode = 'minimal' | 'normal' | 'advanced'; |
|
|
| export type WsStatus = 'connected' | 'reconnecting' | 'offline'; |
|
|
| export interface AnalysisResult { |
| claimHash: string; |
| claimText: string; |
| color: 'green' | 'yellow' | 'red' | 'purple'; |
| confidence: number; |
| verdict: string; |
| explanation: string; |
| sources: string[]; |
| trustScore: number; |
| cached: boolean; |
| processingMs: number; |
| } |
|
|
| export interface ExtensionState { |
| |
| enabled: boolean; |
| mode: HighlightMode; |
|
|
| |
| wsStatus: WsStatus; |
| backendUrl: string; |
| clientId: string; |
|
|
| |
| results: Record<string, AnalysisResult>; |
|
|
| |
| totalAnalyzed: number; |
| totalFlagged: number; |
|
|
| |
| setEnabled: (v: boolean) => void; |
| setMode: (m: HighlightMode) => void; |
| setWsStatus: (s: WsStatus) => void; |
| setBackendUrl: (url: string) => void; |
| addResults: (results: AnalysisResult[]) => void; |
| clearResults: () => void; |
| } |
|
|
| |
| const chromeStorageAdapter = createJSONStorage(() => ({ |
| getItem: (key: string): Promise<string | null> => { |
| return new Promise((resolve) => { |
| if (typeof chrome === 'undefined' || !chrome.storage) { |
| resolve(localStorage.getItem(key)); |
| return; |
| } |
| chrome.storage.sync.get([key], (result) => { |
| resolve(result[key] ?? null); |
| }); |
| }); |
| }, |
| setItem: (key: string, value: string): Promise<void> => { |
| return new Promise((resolve) => { |
| if (typeof chrome === 'undefined' || !chrome.storage) { |
| localStorage.setItem(key, value); |
| resolve(); |
| return; |
| } |
| chrome.storage.sync.set({ [key]: value }, resolve); |
| }); |
| }, |
| removeItem: (key: string): Promise<void> => { |
| return new Promise((resolve) => { |
| if (typeof chrome === 'undefined' || !chrome.storage) { |
| localStorage.removeItem(key); |
| resolve(); |
| return; |
| } |
| chrome.storage.sync.remove([key], resolve); |
| }); |
| }, |
| })); |
|
|
| |
| function generateClientId(): string { |
| const arr = new Uint8Array(8); |
| crypto.getRandomValues(arr); |
| return 'ext-' + Array.from(arr).map(b => b.toString(16).padStart(2, '0')).join(''); |
| } |
|
|
| |
| export const useExtensionStore = create<ExtensionState>()( |
| persist( |
| (set, get) => ({ |
| enabled: true, |
| mode: 'normal', |
| wsStatus: 'offline', |
| backendUrl: import.meta.env.VITE_WS_URL || 'ws://localhost:7860', |
| clientId: generateClientId(), |
| results: {}, |
| totalAnalyzed: 0, |
| totalFlagged: 0, |
|
|
| setEnabled: (v) => set({ enabled: v }), |
| setMode: (m) => set({ mode: m }), |
| setWsStatus: (s) => set({ wsStatus: s }), |
| setBackendUrl: (url) => set({ backendUrl: url }), |
|
|
| addResults: (newResults) => { |
| const { results, totalAnalyzed, totalFlagged } = get(); |
| const updated = { ...results }; |
| let flaggedDelta = 0; |
| for (const r of newResults) { |
| updated[r.claimHash] = r; |
| if (r.color === 'red' || r.color === 'purple') flaggedDelta++; |
| } |
| set({ |
| results: updated, |
| totalAnalyzed: totalAnalyzed + newResults.length, |
| totalFlagged: totalFlagged + flaggedDelta, |
| }); |
| }, |
|
|
| clearResults: () => set({ results: {}, totalAnalyzed: 0, totalFlagged: 0 }), |
| }), |
| { |
| name: 'fact-engine-store', |
| storage: chromeStorageAdapter, |
| |
| partialize: (state) => ({ |
| enabled: state.enabled, |
| mode: state.mode, |
| backendUrl: state.backendUrl, |
| clientId: state.clientId, |
| totalAnalyzed: state.totalAnalyzed, |
| totalFlagged: state.totalFlagged, |
| }), |
| } |
| ) |
| ); |
|
|