fix somethings

This commit is contained in:
2026-06-08 11:16:28 +08:00
parent 9fea9c6a53
commit e7963b267e
34 changed files with 5195 additions and 246 deletions

View File

@@ -0,0 +1,211 @@
/**
* PageStateContext — preserves page-level session state across route changes.
*
* When React Router unmounts a page component, all its useState values are lost.
* This context lives above the router and holds the state that must survive
* navigation so users can switch modules and return without losing their work.
*
* Covered pages:
* - RagChat: message history, citation rail, sessionId, input draft
* - Compliance: analysis result (sources, findings, conclusion, meta)
* - Perception: selected signal, filter state, AI analysis output
*/
import React, { createContext, useContext, useState, useCallback, useRef } from 'react';
// ── RagChat types ─────────────────────────────────────────────────────────────
export interface RagMessage {
id: string;
role: 'user' | 'assistant';
text: string;
citationRefs?: number[];
}
export interface RagCitation {
index: number;
score: number;
name: string;
clause: string;
snippet: string;
docId?: string;
}
export interface RagChatState {
messages: RagMessage[];
citations: RagCitation[];
sessionId: string | null;
inputDraft: string;
}
const RAG_INIT: RagChatState = {
messages: [
{
id: 'init',
role: 'assistant',
text: 'Hello! I can answer questions about your indexed regulations and compliance documents. Try asking about EU AI Act requirements, MIIT rules, or ISO/SAE 21434 scope.',
},
],
citations: [],
sessionId: null,
inputDraft: '',
};
// ── Compliance types ──────────────────────────────────────────────────────────
export interface ComplianceSourceEvent {
standard: string;
clause: string;
score: number;
status: string;
full_content: string;
}
export interface ComplianceFindingEvent {
title: string;
desc: string;
status: 'ok' | 'warn' | 'risk';
clause_ref?: string;
}
export interface ComplianceActionItem {
label: string;
value: string;
risk?: boolean;
}
export interface ComplianceDonePayload {
conclusion: string;
actions: ComplianceActionItem[];
risk_score: number;
highlight_terms: string[];
para_text: string;
}
export interface ComplianceMeta {
title: string;
sourceType: 'text' | 'doc' | 'upload';
startedAt: string;
}
export type ComplianceStatus = 'idle' | 'streaming' | 'done' | 'error';
export interface ComplianceState {
status: ComplianceStatus;
stageLabel: string;
stageKey: string;
meta: ComplianceMeta | null;
sources: ComplianceSourceEvent[];
findings: ComplianceFindingEvent[];
done: ComplianceDonePayload | null;
errorText: string;
}
const COMPLIANCE_INIT: ComplianceState = {
status: 'idle',
stageLabel: '',
stageKey: '',
meta: null,
sources: [],
findings: [],
done: null,
errorText: '',
};
// ── Perception types ──────────────────────────────────────────────────────────
export interface PerceptionSignal {
id: string;
source: string;
standard: string;
status: 'ok' | 'warn' | 'risk' | 'info';
title: string;
summary: string;
date: string;
tags: string[];
impact: 'High' | 'Medium' | 'Low';
}
export interface PerceptionPageState {
signals: PerceptionSignal[];
searchQuery: string;
sourceFilter: string;
impactFilter: string;
selectedId: string | null;
aiOutput: string;
detailTab: 'overview' | 'obligations' | 'assessment' | 'diff';
crawlStatus: string;
}
const PERCEPTION_INIT: PerceptionPageState = {
signals: [],
searchQuery: '',
sourceFilter: 'All',
impactFilter: 'All',
selectedId: null,
aiOutput: '',
detailTab: 'overview',
crawlStatus: '',
};
// ── Context value ─────────────────────────────────────────────────────────────
interface PageStateContextValue {
// RagChat
ragState: RagChatState;
setRagState: React.Dispatch<React.SetStateAction<RagChatState>>;
ragStreamingRef: React.MutableRefObject<boolean>;
ragAbortRef: React.MutableRefObject<AbortController | null>;
// Compliance
complianceState: ComplianceState;
setComplianceState: React.Dispatch<React.SetStateAction<ComplianceState>>;
complianceAbortRef: React.MutableRefObject<AbortController | null>;
resetCompliance: () => void;
// Perception
perceptionState: PerceptionPageState;
setPerceptionState: React.Dispatch<React.SetStateAction<PerceptionPageState>>;
perceptionAbortRef: React.MutableRefObject<AbortController | null>;
perceptionCrawlAbortRef: React.MutableRefObject<AbortController | null>;
}
const PageStateContext = createContext<PageStateContextValue | null>(null);
// ── Provider ──────────────────────────────────────────────────────────────────
export function PageStateProvider({ children }: { children: React.ReactNode }) {
const [ragState, setRagState] = useState<RagChatState>(RAG_INIT);
const ragStreamingRef = useRef(false);
const ragAbortRef = useRef<AbortController | null>(null);
const [complianceState, setComplianceState] = useState<ComplianceState>(COMPLIANCE_INIT);
const complianceAbortRef = useRef<AbortController | null>(null);
const resetCompliance = useCallback(() => {
complianceAbortRef.current?.abort();
setComplianceState(COMPLIANCE_INIT);
}, []);
const [perceptionState, setPerceptionState] = useState<PerceptionPageState>(PERCEPTION_INIT);
const perceptionAbortRef = useRef<AbortController | null>(null);
const perceptionCrawlAbortRef = useRef<AbortController | null>(null);
return (
<PageStateContext.Provider value={{
ragState, setRagState, ragStreamingRef, ragAbortRef,
complianceState, setComplianceState, complianceAbortRef, resetCompliance,
perceptionState, setPerceptionState, perceptionAbortRef, perceptionCrawlAbortRef,
}}>
{children}
</PageStateContext.Provider>
);
}
// ── Hook ──────────────────────────────────────────────────────────────────────
export function usePageState() {
const ctx = useContext(PageStateContext);
if (!ctx) throw new Error('usePageState must be used inside PageStateProvider');
return ctx;
}

View File

@@ -1,3 +1,18 @@
export { ThemeProvider, useTheme } from './ThemeContext';
export { AuthProvider, useAuth } from './AuthContext';
export type { AuthUser } from './AuthContext';
export { PageStateProvider, usePageState } from './PageStateContext';
export type {
RagChatState,
RagMessage,
RagCitation,
ComplianceState,
ComplianceStatus,
ComplianceSourceEvent,
ComplianceFindingEvent,
ComplianceDonePayload,
ComplianceMeta,
ComplianceActionItem,
PerceptionPageState,
PerceptionSignal,
} from './PageStateContext';