import { OpConflict } from '../command/op'; import { CommandFailure } from '../editor/controller'; import { MessageKey } from '../i18n'; export interface OpConflictNotice { /** 배너 제목 키 — 상태 자체(무엇이 일어났는지). 뷰가 t()로 렌더. */ titleKey: MessageKey; /** 배너 본문 키 — 원인 + 복구 방향(다시 불러오기). 뷰가 t()로 렌더. */ detailKey: MessageKey; } /** * 충돌 종류 → 사용자 문구 **키**(문구는 i18n 카탈로그 소유 — 검증 P3 code+params→뷰 지역화 패턴 동형). * `missing`은 동시편집이 아니라 전송 실패(혼자 편집 중에도 발생)라 원인을 구분한다 * (호스트 배선 경로와 동일한 kind 오진 방지). null → null(배너 미노출). */ export declare function opConflictNotice(conflict: OpConflict | null | undefined): OpConflictNotice | null; /** * 배너가 실제로 그리는 것 — 원인(둘) × 복구 진행(하나)의 합성. 뷰 computed 가 아니라 여기가 소유한다 * (분기를 컴포넌트에 두면 jsdom 에 레이아웃이 없어 단위로 못 재고, 프로브만 남아 vacuous 해진다). * * ## 왜 «진행» 축이 필요한가 (T2-2 host 하드닝 #3) * 위 세 detail 문구는 전부 *"최신 내용을 다시 불러오세요"* 로 끝나는 **행동 요구**다. 그런데 배선 호스트의 * 자동 복구 경로(`rev`·`layout`·`commandFailure`)에서는 호스트가 **이미 재적재 중**이라 사용자가 할 일이 * 없고, 배너에 누를 것도 없다 ⇒ **수단 없는 지시**가 된다. `reloadInFlight` 가 그 창을 덮는다. * * ★**미배선 임베더에서 문구가 갈리지 않는다** — 그쪽은 자동 reload 자체가 없어 신호가 영원히 false 이고, * 그러면 *"다시 불러오세요"* 가 **맞는 안내**다. 그래서 이 축은 «신호가 있을 때만» 문구를 바꾸고 * 기본값을 false 로 둔다(호스트가 인자를 빠뜨려도 종전 동작). * * ⚠️**진행 축은 detail 만 덮고 title 은 덮지 않는다** — 편집이 잠긴 것은 재적재 중에도 참이고, 제목까지 * 바뀌면 자동 경로에서 배너가 뜨자마자 두 제목이 교차해 깜빡인다(호스트가 같은 watch 안에서 reload 를 * 시작하므로 두 상태의 간격이 거의 0이다). */ export interface FreezeBannerNotice extends OpConflictNotice { /** 재적재 진행 중 — 뷰가 경고 아이콘 대신 진행 표시를 그리는 축(문구는 detailKey 가 이미 반영). */ reloading: boolean; } export interface FreezeBannerInput { /** 서버 축 — 로컬은 맞는데 서버가 못 따라왔다. */ conflict: OpConflict | null | undefined; /** 로컬 축 — 명령 apply 가 중간에 throw 해 화면이 어긋났다(op 미방출). */ commandFailure: CommandFailure | null | undefined; /** 호스트가 「재적재 중」을 알린 신호. 미배선이면 undefined ⇒ false. */ reloadInFlight?: boolean; } export declare function freezeBannerNotice(input: FreezeBannerInput): FreezeBannerNotice | null;