307 lines
8.5 KiB
TypeScript
307 lines
8.5 KiB
TypeScript
export type ExhibitType = 'folder' | 'document' | 'note' | 'event' | 'party' | 'claim'
|
|
export type PartyKind = 'person' | 'organization'
|
|
export type OrganizationKind = 'business' | 'public_body' | 'association' | 'informal_group' | 'other'
|
|
export type SourceFileType = 'image' | 'pdf' | 'web_capture' | 'email' | 'article' | 'filing' | 'price_list' | 'text' | 'file'
|
|
export type DocumentCaptureKind = 'unclassified' | 'photo' | 'scene' | 'clipping' | 'full_page'
|
|
|
|
export interface CanvasPlacement {
|
|
x: number
|
|
y: number
|
|
width: number
|
|
height: number
|
|
rotation: number
|
|
zIndex: number
|
|
hidden: boolean
|
|
}
|
|
|
|
export interface ExhibitBase extends CanvasPlacement {
|
|
id: string
|
|
type: ExhibitType
|
|
title: string
|
|
}
|
|
|
|
export interface DocumentRegion {
|
|
id: string
|
|
label: string
|
|
excerpt: string
|
|
date?: string
|
|
}
|
|
|
|
export interface FolderExhibit extends ExhibitBase {
|
|
type: 'folder'
|
|
content: string
|
|
isOpen: boolean
|
|
}
|
|
|
|
export interface DocumentExhibit extends ExhibitBase {
|
|
type: 'document'
|
|
/** Stable, board-local number used when citing this exhibit. */
|
|
displayNumber?: number
|
|
/** Author-mode reveal requirements. Omitted from play-mode payloads. */
|
|
requiredFlags?: string[]
|
|
publishedAt?: string
|
|
capturedAt?: string
|
|
sourceUri?: string
|
|
sourceCitation?: string
|
|
body: string[]
|
|
regions: DocumentRegion[]
|
|
assetId?: string
|
|
fileName?: string
|
|
mimeType?: string
|
|
fileSize?: number
|
|
fileType: SourceFileType
|
|
/** Evidentiary form on the board; independent of MIME/file type. */
|
|
captureKind: DocumentCaptureKind
|
|
metadata: Record<string, string>
|
|
}
|
|
|
|
export interface DocumentUploadAnalysis {
|
|
extractionStatus: 'succeeded' | 'unsupported' | 'failed'
|
|
matchedFlags: string[]
|
|
awardedFlags: string[]
|
|
goals: LevelGoal[]
|
|
}
|
|
|
|
export interface UploadedCaseDocument extends DocumentExhibit {
|
|
/** Transient upload response data; it is not part of persisted exhibit state. */
|
|
analysis: DocumentUploadAnalysis
|
|
}
|
|
|
|
export type NotePresentation = 'luggage' | 'lined_sheet'
|
|
|
|
export interface NoteExhibit extends ExhibitBase {
|
|
type: 'note'
|
|
content: string
|
|
presentation: NotePresentation
|
|
}
|
|
|
|
export interface EventExhibit extends ExhibitBase {
|
|
type: 'event'
|
|
content: string
|
|
eventDate?: string
|
|
}
|
|
|
|
export interface PartyExhibit extends ExhibitBase {
|
|
type: 'party'
|
|
content: string
|
|
partyKind: PartyKind
|
|
organizationKind?: OrganizationKind
|
|
aliases: string[]
|
|
}
|
|
|
|
export interface ClaimExhibit extends ExhibitBase {
|
|
type: 'claim'
|
|
statement: string
|
|
}
|
|
|
|
export type Exhibit = FolderExhibit | DocumentExhibit | NoteExhibit | EventExhibit | PartyExhibit | ClaimExhibit
|
|
export type Evidence = Exclude<Exhibit, DocumentExhibit>
|
|
export type CaseDocument = DocumentExhibit
|
|
export type EvidenceType = Evidence['type']
|
|
|
|
interface ExhibitRelationBase {
|
|
id: string
|
|
fromExhibitId: string
|
|
toExhibitId: string
|
|
sortOrder: number
|
|
note?: string
|
|
}
|
|
|
|
export interface FolderMembership extends ExhibitRelationBase { type: 'contains' }
|
|
export interface EventSupportRelation extends ExhibitRelationBase { type: 'supports' }
|
|
export interface PartyAssociationRelation extends ExhibitRelationBase { type: 'concerns' }
|
|
export interface ProvenanceRelation extends ExhibitRelationBase { type: 'source'; sourceRegionId?: string }
|
|
export type ExhibitRelation = FolderMembership | EventSupportRelation | PartyAssociationRelation | ProvenanceRelation
|
|
|
|
export interface Connection {
|
|
id: string
|
|
fromExhibitId: string
|
|
toExhibitId: string
|
|
label?: string
|
|
/** Percentage from slack (0) to taut (100). */
|
|
tightness?: number
|
|
tagStyle?: 'luggage' | 'compact'
|
|
/** Percentage along the thread from its source exhibit. */
|
|
tagPosition?: number
|
|
/** Signed perpendicular distance from the thread, constrained by tightness. */
|
|
tagOffset?: number
|
|
}
|
|
|
|
export interface Viewport { x: number; y: number; zoom: number }
|
|
export interface TimelineRange { start: string; end: string }
|
|
|
|
export type BoardViewPlacement =
|
|
| { mode: 'docked'; dockEdge: 'top' | 'right' | 'bottom' | 'left'; size: number }
|
|
| { mode: 'canvas' | 'window'; x: number; y: number; width: number; height: number }
|
|
|
|
export interface TimelineView {
|
|
id: string
|
|
type: 'timeline'
|
|
placement: BoardViewPlacement
|
|
visible: boolean
|
|
zIndex: number
|
|
rangeMode: 'auto' | 'fixed'
|
|
range?: TimelineRange
|
|
}
|
|
|
|
export type BoardView = TimelineView
|
|
|
|
export interface TemporalFact {
|
|
id: string
|
|
exhibitId: string
|
|
kind: 'published' | 'captured' | 'occurred' | 'region_date'
|
|
start: string
|
|
end?: string
|
|
label: string
|
|
}
|
|
|
|
export interface BriefConcept {
|
|
id: string
|
|
label: string
|
|
context: string
|
|
expectedPartyKind?: PartyKind
|
|
resolvedPartyExhibitId?: string
|
|
}
|
|
|
|
export interface LevelBrief { body: string; concepts: BriefConcept[] }
|
|
|
|
export interface LevelGoal {
|
|
/** Present in author mode so the goal can be edited; omitted in play mode. */
|
|
id?: string
|
|
key: string
|
|
title: string
|
|
instructions: string
|
|
completionMessage: string
|
|
enabled?: boolean
|
|
/** Author-only success condition. Expected answer flags stay out of play payloads. */
|
|
requiredFlags?: string[]
|
|
status: 'pending' | 'complete'
|
|
completedAt?: string
|
|
/** True only in the response to the mutation that completed this goal. */
|
|
newlyCompleted: boolean
|
|
}
|
|
|
|
export type CaseReportSubmissionStatus = 'evidence_insufficient' | 'evidence_accepted_report_incomplete' | 'accepted'
|
|
export type EvidenceVerificationStatus = 'accepted' | 'not_evaluated' | 'ocr_unavailable' | 'text_not_matched' | 'semantic_pending' | 'semantic_failed' | 'semantic_rejected'
|
|
|
|
export interface EvidenceVerification {
|
|
status: EvidenceVerificationStatus
|
|
detail: string
|
|
score?: number
|
|
matchedMarkers?: number
|
|
requiredMarkers?: number
|
|
}
|
|
|
|
export interface CaseReportEvidence {
|
|
connectionId: string
|
|
documentExhibitId: string
|
|
displayNumber: number
|
|
documentTitle: string
|
|
fileType: SourceFileType
|
|
relationText: string
|
|
publishedAt?: string
|
|
sourceCitation?: string
|
|
sourceUri?: string
|
|
evidenceAccepted: boolean
|
|
verification: EvidenceVerification
|
|
}
|
|
|
|
export interface CaseReportClaim {
|
|
claimExhibitId: string
|
|
statement: string
|
|
evidence: CaseReportEvidence[]
|
|
}
|
|
|
|
export interface CaseReport {
|
|
title: string
|
|
investigatorName: string
|
|
requiredForCompletion: boolean
|
|
status: 'draft' | CaseReportSubmissionStatus
|
|
feedback?: string
|
|
issues: string[]
|
|
claims: CaseReportClaim[]
|
|
}
|
|
|
|
export interface CaseReportSubmissionInput {
|
|
investigatorName: string
|
|
}
|
|
|
|
export interface CaseState {
|
|
id: string
|
|
title: string
|
|
subtitle: string
|
|
exhibits: Exhibit[]
|
|
relations: ExhibitRelation[]
|
|
connections: Connection[]
|
|
views: BoardView[]
|
|
viewport: Viewport
|
|
brief: LevelBrief
|
|
goals: LevelGoal[]
|
|
report?: CaseReport
|
|
revision: number
|
|
updatedAt?: string
|
|
levelStatus?: string
|
|
sourceTemplateVersionId?: string
|
|
editingAllowed?: boolean
|
|
/** Visible documents that have not previously played their arrival flourish. */
|
|
newlyVisibleDocumentIds?: string[]
|
|
}
|
|
|
|
export interface LevelFlag {
|
|
key: string
|
|
earnedAt?: string
|
|
gatedDocumentCount: number
|
|
}
|
|
|
|
export interface EvidenceMatchAnchorDefinition {
|
|
id: string
|
|
phrase: string
|
|
minimumSimilarity: number
|
|
sortOrder: number
|
|
}
|
|
|
|
export interface EvidenceMatchRuleDefinition {
|
|
id: string
|
|
name: string
|
|
sourceLabel?: string
|
|
sourceUri?: string
|
|
flagKey: string
|
|
matcherVersion: 'char_trigram_v1'
|
|
minimumAnchorMatches: number
|
|
enabled: boolean
|
|
anchors: EvidenceMatchAnchorDefinition[]
|
|
}
|
|
|
|
export interface EvidenceSemanticRuleDefinition {
|
|
id: string
|
|
goalId: string
|
|
goalKey: string
|
|
name: string
|
|
targetSubject: string
|
|
relatedSubject?: string
|
|
assertion: string
|
|
successFlagKey: string
|
|
relatedFlagKey?: string
|
|
minimumConfidence: number
|
|
evaluatorVersion: string
|
|
enabled: boolean
|
|
}
|
|
|
|
export interface DocumentSemanticAnalysis {
|
|
status: 'not_needed' | 'unavailable' | 'pending' | 'succeeded' | 'failed'
|
|
subject?: 'target' | 'related' | 'ambiguous' | 'neither'
|
|
supportsClaim?: boolean
|
|
evidenceExcerpt?: string
|
|
confidence?: number
|
|
retryable: boolean
|
|
awardedFlags: string[]
|
|
goals: LevelGoal[]
|
|
}
|
|
|
|
export function isDocumentExhibit(exhibit: Exhibit): exhibit is DocumentExhibit { return exhibit.type === 'document' }
|
|
export function isEvidenceExhibit(exhibit: Exhibit): exhibit is Evidence { return exhibit.type !== 'document' }
|
|
export function isFolderExhibit(exhibit: Exhibit): exhibit is FolderExhibit { return exhibit.type === 'folder' }
|
|
export function isEventExhibit(exhibit: Exhibit): exhibit is EventExhibit { return exhibit.type === 'event' }
|
|
export function isPartyExhibit(exhibit: Exhibit): exhibit is PartyExhibit { return exhibit.type === 'party' }
|
|
export function isClaimExhibit(exhibit: Exhibit): exhibit is ClaimExhibit { return exhibit.type === 'claim' }
|