import { AffectedRef, AffectedRefRemapping, CommentsCreateInput, CommentsDeleteInput, CommentsPatchInput, HistoryActionResult, Receipt, ReviewDecideInput, StoryLocator } from '../../../document-api/src/index.js'; import { ReviewTargetDiagnostic } from './review-target.js'; /** * Review-shell interaction rejection codes. These describe UI-layer focus and * action gating outcomes; they are distinct from the frozen * `ReceiptFailureCode` union and never widen it. */ export type SDReviewInteractionRejectionCode = 'review-surface-read-only' | 'review-target-invalidated' | 'review-command-unavailable' | 'comment-anchor-create-deferred' | 'comment-anchor-move-deferred'; export interface SDReviewInteractionRejection { code: SDReviewInteractionRejectionCode; detail?: string; } export type ActiveReviewTargetEntityType = 'comment' | 'trackedChange'; export type ActiveReviewTargetOrigin = 'document' | 'panel' | 'bubble' | 'history' | 'receipt' | 'shell'; /** * Authoritative review-focus object owned by the review shell. Held outside * any selection-driven UI compatibility mirror; mirrors may follow this * object one-way for legacy compatibility but never lead it. */ export interface ActiveReviewTarget { entityType: ActiveReviewTargetEntityType; entityId: string; origin: ActiveReviewTargetOrigin; /** Layout epoch captured when this target became active. */ layoutEpoch: number; /** Story locator (when known). Comment targets default to body. */ story?: StoryLocator; /** Diagnostics carried alongside the focus (e.g. from hit-test). */ diagnostics?: readonly ReviewTargetDiagnostic[]; } /** * Review commands supported by Phase 0. The set is closed: 003 routes only * these verbs through the executor; 005 owns the command matrix gate that * decides which subset is enabled at runtime. */ export type ReviewCommandKind = 'comment.reply' | 'comment.edit' | 'comment.resolve' | 'comment.reopen' | 'comment.delete' | 'trackedChange.accept' | 'trackedChange.reject' | 'trackedChange.acceptAll' | 'trackedChange.rejectAll' | 'history.undo' | 'history.redo'; /** * Minimal async executor contract consumed by 003. It mirrors the frozen * C1 operation shapes; the real review host (004) wires this to its async * worker transport, while tests bind it to deterministic fakes. */ export interface ReviewShellCommandExecutor { comments: { create(input: CommentsCreateInput): Promise; patch(input: CommentsPatchInput): Promise; delete(input: CommentsDeleteInput): Promise; }; trackChanges: { decide(input: ReviewDecideInput): Promise; }; history: { undo(): Promise; redo(): Promise; }; } export interface ReviewActionOk { status: 'ok'; receipt: Receipt; } export interface ReviewHistoryOk { status: 'ok'; result: HistoryActionResult; } export interface ReviewActionRejected { status: 'rejected'; reason: SDReviewInteractionRejection; } export type ReviewActionResult = ReviewActionOk | ReviewActionRejected; export type ReviewHistoryResult = ReviewHistoryOk | ReviewActionRejected; export interface ApplyReceiptLikeInput { invalidatedRefs?: readonly AffectedRef[]; remappedRefs?: readonly AffectedRefRemapping[]; affectedStories?: readonly StoryLocator[]; txId?: string; /** Local review provenance (only set when the receipt originated from a 003 command). */ provenance?: { commandKind: ReviewCommandKind; beforeTarget: ActiveReviewTarget | null; requestedFollowFocus?: ActiveReviewTarget | null; }; } export type DocumentInputEventKind = 'beforeinput' | 'compositionstart' | 'compositionupdate' | 'compositionend' | 'paste' | 'drop' | 'dragover' | 'keydown'; export interface ReviewStateAdapterOptions { executor: ReviewShellCommandExecutor; /** Returns the currently painted layout epoch. Used to gate held-target actions. */ getCurrentLayoutEpoch?: () => number | null; /** Returns whether a held target is still painted. Used by `onPaintEpochChange`. */ isTargetPainted?: (target: ActiveReviewTarget) => boolean; /** Capability gate for review commands. 005 wires the real matrix. */ isCommandAvailable?: (command: ReviewCommandKind) => boolean; /** Provider/layout invalidation forward hook. Called for shell-owned local receipts. */ forwardStoryInvalidation?: (input: { stories: readonly StoryLocator[]; txId?: string; }) => void; /** Notified after capability/history may have changed. Lets the shell refresh chrome. */ notifyCapabilityRefresh?: () => void; } export interface ReviewStateSnapshot { activeReviewTarget: ActiveReviewTarget | null; lastInteractionRejection: SDReviewInteractionRejection | null; pendingTargets: ReadonlyMap; undoDepth: number; redoDepth: number; nextUndoTxId: string | null; nextRedoTxId: string | null; } export type ReviewStateListener = (snapshot: ReviewStateSnapshot) => void; export interface ReviewHistoryProvenance { /** Direction the history call is about to take. */ direction: 'undo' | 'redo'; /** Optional originating txId (set when 005 can identify the entry). */ originTxId?: string; } export interface ReviewStateAdapter { /** Current authoritative target, or null when no review focus is held. */ getActiveReviewTarget(): ActiveReviewTarget | null; /** * Set the authoritative target. The shell calls this in response to a * document-surface hit, panel selection, or bubble click. Returns null on * success or a rejection describing why the focus was refused. */ setActiveReviewTarget(target: ActiveReviewTarget | null): SDReviewInteractionRejection | null; /** Clear the active target with the given rejection code as the reason. */ clearActiveReviewTarget(reason?: SDReviewInteractionRejectionCode, detail?: string): void; /** Subscribe to snapshot updates. */ subscribe(listener: ReviewStateListener): () => void; /** Synchronous snapshot for chrome rendering. */ getSnapshot(): ReviewStateSnapshot; replyToComment(input: { parentCommentId: string; text: string; followFocus?: ActiveReviewTarget; }): Promise; editComment(input: { commentId: string; text: string; }): Promise; resolveComment(input: { commentId: string; }): Promise; reopenComment(input: { commentId: string; }): Promise; deleteComment(input: { commentId: string; }): Promise; decideTrackedChange(input: ReviewDecideInput): Promise; undo(provenance?: ReviewHistoryProvenance): Promise; redo(provenance?: ReviewHistoryProvenance): Promise; /** * Push receipt-like effects from 004/006 into the held target lifecycle. * Does not surface a UI failure; the originating receipt carries the * authoritative `ReceiptFailureCode` already. */ applyReceiptLike(input: ApplyReceiptLikeInput): void; /** * Re-check the held target after a view change or repaint. Pass the * current painted epoch. Re-applies highlight if still painted, or * clears with `review-target-invalidated` if not. */ onPaintEpochChange(epoch: number): void; /** * Returns true when the document-surface should block this input event. * Plain-text caret/selection movement is intentionally NOT in this list: * it can fall out as a compatibility side effect but it is not the * authoritative review state and is not a precondition for any action. */ shouldBlockInputEvent(eventType: DocumentInputEventKind, event?: KeyboardEvent): boolean; /** Dispose the adapter. Clears the held target and disables further actions. */ dispose(): void; } export declare function createReviewStateAdapter(options: ReviewStateAdapterOptions): ReviewStateAdapter; /** * One-way compatibility mirror: forwards `activeReviewTarget` changes to the * legacy selection-driven `ui.comments.activeIds` / `ui.trackChanges.activeId` * surfaces. The returned subscription disposer does not unsubscribe the mirror * from listener-driven legacy state — the mirror is intentionally write-only. * If a legacy mirror tries to drive the authoritative target, that drive path * must be removed instead of being adapted to feed back into 003. */ export interface LegacyReviewMirrorSinks { setActiveComments?(ids: readonly string[]): void; setActiveTrackedChange?(id: string | null): void; } export declare function attachLegacyReviewMirror(adapter: ReviewStateAdapter, sinks: LegacyReviewMirrorSinks): () => void; //# sourceMappingURL=review-state.d.ts.map