@solhun/feedback-kit-web 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { ElementInfo, FeedbackPin, FeedbackScreenshot, FeedbackHint, FeedbackReport, SubmitOutcome, QueueStatus, ReportParts, ScreenshotCapture, ScreenshotReencode, WidgetControllerOpts, HintProvider, WidgetController, FeedbackStorage, ContextProviders } from '@solhun/feedback-kit-core';
1
+ import { ElementInfo, FeedbackPin, FeedbackScreenshot, FeedbackHint, FeedbackReport, SubmitOutcome, QueueStatus, ReportParts, ScreenshotCapture, ScreenshotReencode, RouteWatcher, WidgetControllerOpts, HintProvider, WidgetController, FeedbackStorage, ContextProviders, DiagnosticsInstallOpts, RouteTrailStore } from '@solhun/feedback-kit-core';
2
2
  export { COMMENT_MAX_CHARS, COMMENT_REQUIRED_MESSAGE, COMMENT_TOO_LONG_MESSAGE, ElementInfo, ElementLike, FLOATING_BUTTON_ID, FeedbackAdapter, FeedbackConfig, FeedbackContext, FeedbackHint, FeedbackPin, FeedbackReport, FeedbackStorage, FeedbackUser, HintCatalog, HintElementRule, HintPlatform, HintProvider, HintRankInput, HintScope, MODAL_ACTION_HINT_TOGGLE, MODAL_ACTION_PICK, ModalSubmitStatus, ReportModalController, ReportModalState, ResolvedConfig, SCREENSHOT_FAILED_MESSAGE, SOURCE_ATTR, SUBMIT_DONE_MESSAGE, SUBMIT_PENDING_MESSAGE, ScreenshotStatus, SubmitResult, Visibility, VisibilityEnv, VisibilityFn, WidgetController, WidgetPosition, WidgetScreen, WidgetState, denormalizePin, normalizePin, parseSourceAttr, rankHints, resolveConfig, shouldShowWidget, sourceFromElement } from '@solhun/feedback-kit-core';
3
3
  import * as react from 'react';
4
4
 
@@ -109,6 +109,18 @@ interface AnnotationPopupState {
109
109
  }
110
110
  interface PickingState {
111
111
  active: boolean;
112
+ /**
113
+ * 켜져 있지만 **클릭 가로채기만 잠시 끈 상태**.
114
+ *
115
+ * 왜 필요한가: 지목 모드는 페이지의 모든 클릭을 먹는다(그래야 지목하려고 누른 링크가
116
+ * 실제로 이동해 버리지 않는다). 그 대가로 **제보할 화면을 만들 수가 없다** — 드롭다운을
117
+ * 열고 그 안의 항목을 지목하고 싶어도 여는 클릭부터 막힌다. 그때 모드를 끄면 다시
118
+ * 켜야 하고, 사용자에겐 그게 "모드가 유지되지 않는다"로 읽힌다.
119
+ *
120
+ * 그래서 끄는 것과 멈추는 것을 나눈다. 멈춘 동안 페이지는 원래대로 동작하고, 모드는
121
+ * 켜진 채로 남는다.
122
+ */
123
+ paused: boolean;
112
124
  /** 지금 마우스가 올라가 있는 요소. 하이라이트를 그리는 근거. */
113
125
  hovered: ElementInfo | null;
114
126
  popup: AnnotationPopupState | null;
@@ -151,6 +163,12 @@ interface ElementPickingOpts {
151
163
  * `rankHints` 한 결과를 돌려준다. 없으면(힌트 기능이 꺼져 있으면) 팝업에 칩이 안 뜬다.
152
164
  */
153
165
  rankHintsFor?: ((element: ElementInfo | null) => readonly FeedbackHint[]) | null;
166
+ /**
167
+ * 화면 이동 감시자. 기본은 웹 공용 감시자(`webRouteWatcher`) — **진단의 화면 이동 궤적과
168
+ * 같은 것**이다. 여기만 따로 구현하면 한쪽이 이동을 놓쳤을 때 원인을 가릴 수 없다.
169
+ * `null` 을 주면 감시하지 않는다(테스트에서 `syncPath()` 를 직접 부를 때).
170
+ */
171
+ watchRoutes?: RouteWatcher | null;
154
172
  }
155
173
  declare class ElementPickingController {
156
174
  private readonly queue;
@@ -167,11 +185,21 @@ declare class ElementPickingController {
167
185
  private readonly rankHintsFor;
168
186
  private readonly listeners;
169
187
  private active;
188
+ /**
189
+ * 일시중지. **저장하지 않는다** — 멈추는 목적이 대개 "링크를 눌러 다른 화면으로 가는 것"
190
+ * 이라, 그 이동이 끝나면 지목이 돌아와 있는 게 목적에 맞다. 같은 이유로 경로가 바뀌면
191
+ * 스스로 풀린다(`syncPath`) — 그래야 SPA 이동과 새로고침이 같게 동작한다.
192
+ */
193
+ private paused;
170
194
  private attached;
195
+ /** 같은 요소 위 mousemove 마다 React 경로·선택자를 다시 만들지 않기 위한 캐시. */
196
+ private hoveredTarget;
171
197
  private hovered;
172
198
  private popup;
173
199
  private markers;
174
- private pathWatch;
200
+ private readonly watchRoutes;
201
+ /** 경로 감시 해제. 감시 중이 아니면 null. */
202
+ private unwatchRoutes;
175
203
  private lastPathname;
176
204
  private saving;
177
205
  /** 늦게 끝난 캡처가 다음 주석의 그림을 덮지 못하게 하는 세대 번호. */
@@ -179,24 +207,49 @@ declare class ElementPickingController {
179
207
  /** 지금 도는 자동 캡처. Enter 가 캡처보다 빨랐을 때 기다릴 대상. */
180
208
  private capturing;
181
209
  /**
182
- * 사용자가 [제안 ▾] 를 직접 건드렸는가. 한 번이라도 건드리면 이후 타이핑에 의한 자동
210
+ * 사용자가 [이런 건가요? ▾] 를 직접 건드렸는가. 한 번이라도 건드리면 이후 타이핑에 의한 자동
183
211
  * 접힘/펼침이 멈춘다 — 모달의 `userToggledHints` 와 같은 규칙. 팝업을 새로 열 때마다 리셋된다.
184
212
  */
185
213
  private userToggledHints;
186
214
  private readonly onClick;
187
215
  private readonly onMouseOver;
216
+ private readonly onMouseMove;
188
217
  constructor(opts: ElementPickingOpts);
189
218
  getState(): PickingState;
190
219
  get isActive(): boolean;
220
+ /** 켜져 있지만 클릭을 안 먹는 상태인가. */
221
+ get isPaused(): boolean;
191
222
  subscribe(listener: PickingListener): () => void;
192
223
  start(): void;
193
224
  /** [지목 종료]. 저장된 플래그까지 지워서 새로고침해도 다시 켜지지 않게 한다. */
194
225
  stop(): void;
226
+ /**
227
+ * 잠시 멈춘다 — 페이지 클릭이 원래대로 동작하고, 모드는 켜진 채로 남는다.
228
+ *
229
+ * 팝업이 열려 있으면 **그대로 둔다.** 쓰던 한 줄을 여기서 지우면 「지목 켜면 모달 초안이
230
+ * 날아간다」와 같은 사고를 다른 자리에 만드는 셈이다. 팝업은 위젯 자신의 UI 라
231
+ * 리스너를 떼도 계속 눌린다.
232
+ */
233
+ pause(): void;
234
+ /** 다시 지목을 받는다. */
235
+ resume(): void;
236
+ togglePause(): void;
195
237
  /**
196
238
  * 저장돼 있던 모드를 되살린다. 새로고침·페이지 이동 직후에 한 번 부른다.
197
239
  * @returns 되살아났으면 true.
198
240
  */
199
241
  restore(): boolean;
242
+ /**
243
+ * 이 화면에 쌓인 마커 표시를 지운다.
244
+ *
245
+ * 지워지는 건 **화면 표시뿐**이다. 제보는 이미 큐를 거쳐 수집처로 갔으므로 없어지지 않는다.
246
+ * 마커는 `localStorage` 에 남아 새로고침에도 살아남는데(그게 원래 목적이다) 정작 치울
247
+ * 수단이 없어서, 한 번 보낸 [완료] 배지가 그 화면을 볼 때마다 계속 따라다녔다.
248
+ *
249
+ * 화면에 보이는 목록의 기준은 `lastPathname` 이다(`reconcileMarkerOutcomes` 와 같다).
250
+ * 이동 직후 아직 `syncPath` 가 안 돈 순간에도 "지금 눈에 보이는 것"이 지워져야 한다.
251
+ */
252
+ clearMarkers(): void;
200
253
  /** 경로가 바뀌었을 때 그 경로의 마커로 갈아 끼운다. */
201
254
  syncPath(): readonly StoredMarker[];
202
255
  dispose(): void;
@@ -220,7 +273,7 @@ declare class ElementPickingController {
220
273
  * 아니면 줄바꿈 뒤 이어붙인다 — 모달의 `applyHint` 와 동일한 규칙이다.
221
274
  */
222
275
  applyHint(hintId: string): void;
223
- /** [제안 ▾] 토글. 방향과 무관하게 이후 자동 접힘/펼침을 멈춘다. */
276
+ /** [이런 건가요? ▾] 토글. 방향과 무관하게 이후 자동 접힘/펼침을 멈춘다. */
224
277
  toggleHints(): void;
225
278
  /**
226
279
  * 사용자가 붙여넣기(Cmd+V)나 드래그로 넣은 그림. 자동 캡처 결과를 덮는다.
@@ -344,5 +397,58 @@ declare function createWebStorage(backing?: SyncStorage): FeedbackStorage;
344
397
  * 서버 렌더 단계에서 실행될 수 있기 때문이다.
345
398
  */
346
399
  declare function webContextProviders(): ContextProviders;
400
+ /**
401
+ * 웹 진단 수집의 기본 배선.
402
+ *
403
+ * `sharedDiagnostics.install(webDiagnosticsOptions({ ... }))` 로 쓴다. 넘긴 값이 기본값을
404
+ * 덮으므로 감시자·저장소를 직접 갈아 끼울 수도 있다.
405
+ *
406
+ * 왜 패키지가 주나: 감시자를 호스트가 깜빡하면 **궤적만 조용히 빈다.** 네트워크·로그는
407
+ * 그대로 모이니 화면상 아무 문제가 없어 보이고, "이 제보엔 이동이 없었나 보다"로 읽힌다 —
408
+ * 빠진 것과 없는 것이 구분되지 않는 실패다.
409
+ *
410
+ * 진단을 끄면(`captureDiagnostics: false`) install 자체가 안 불리므로 궤적도 같이 꺼진다.
411
+ * 궤적만 따로 켜고 끄는 스위치는 만들지 않는다.
412
+ */
413
+ declare function webDiagnosticsOptions(opts?: DiagnosticsInstallOpts): DiagnosticsInstallOpts;
414
+
415
+ /** 감시자가 들여다보는 전역. 테스트에서는 가짜를 넣는다. */
416
+ interface RouteWatchTarget {
417
+ location?: {
418
+ pathname?: string;
419
+ } | null;
420
+ history?: {
421
+ pushState?: (...args: unknown[]) => unknown;
422
+ replaceState?: (...args: unknown[]) => unknown;
423
+ } | null;
424
+ addEventListener?: (type: string, listener: () => void) => void;
425
+ removeEventListener?: (type: string, listener: () => void) => void;
426
+ }
427
+ /**
428
+ * 경로 감시자 하나를 만든다.
429
+ *
430
+ * 돌려주는 것은 **구독 함수**다. 구독하면 현재 경로를 한 번 흘리고(마운트 시점의 화면이
431
+ * 궤적의 첫 줄이 된다), 이후 이동마다 새 경로를 흘린다. 해제 함수를 부르면 구독이 끊긴다.
432
+ *
433
+ * `history` 패치는 **첫 구독자가 생길 때 걸고 마지막이 나가면 푼다.** 구독자가 없는데 패치가
434
+ * 남아 있으면, 위젯을 떼어낸 뒤에도 호스트의 모든 화면 이동이 우리 코드를 거친다.
435
+ */
436
+ declare function createRouteWatcher(target?: RouteWatchTarget | null): RouteWatcher;
437
+ /**
438
+ * 웹 기본 감시자. **모듈 스코프에 하나**라서 궤적과 지목이 같은 인스턴스를 물려 쓴다.
439
+ * 각자 `createRouteWatcher()` 를 부르면 `history` 가 두 겹으로 패치돼, 해제 순서가 엇갈릴 때
440
+ * 원본이 복구되지 않는다.
441
+ */
442
+ declare const webRouteWatcher: RouteWatcher;
443
+ /**
444
+ * 궤적을 남길 탭 단위 저장소(`sessionStorage`).
445
+ *
446
+ * `localStorage` 가 아닌 이유: 궤적은 **이 탭에서 지금 지나온 길**이다. 탭을 나눠 쓰면
447
+ * 다른 탭의 이동이 섞여 재현 절차가 거짓이 된다.
448
+ *
449
+ * 프라이빗 모드·차단 설정에서는 접근 자체가 던진다. 그때는 `null` 을 돌려주고 궤적은
450
+ * 메모리로 떨어진다 — 저장을 못 한다고 제보를 막지 않는다.
451
+ */
452
+ declare function webRouteStorage(): RouteTrailStore | null;
347
453
 
348
- export { type AnnotationPopupState, ELEMENT_TEXT_MAX_CHARS, ElementPickingController, type ElementPickingOpts, FeedbackKit, type FeedbackKitProps, MARKER_KEY_PREFIX, MAX_MARKERS_PER_PATH, type MarkerStatus, MarkerStore, OWN_UI_ATTR, PICKING_MODE_KEY, type PickingListener, type PickingQueueLike, type PickingState, SELECTOR_MAX_DEPTH, type StorageLike, type StoredMarker, type WebWidget, type WebWidgetOpts, captureWebScreenshot, createWebStorage, createWebWidget, cssSelectorPath, describeElement, isOwnUi, reactComponentPath, reactComponentSummary, reencodeWebScreenshot, visibleText, webContextProviders };
454
+ export { type AnnotationPopupState, ELEMENT_TEXT_MAX_CHARS, ElementPickingController, type ElementPickingOpts, FeedbackKit, type FeedbackKitProps, MARKER_KEY_PREFIX, MAX_MARKERS_PER_PATH, type MarkerStatus, MarkerStore, OWN_UI_ATTR, PICKING_MODE_KEY, type PickingListener, type PickingQueueLike, type PickingState, type RouteWatchTarget, SELECTOR_MAX_DEPTH, type StorageLike, type StoredMarker, type WebWidget, type WebWidgetOpts, captureWebScreenshot, createRouteWatcher, createWebStorage, createWebWidget, cssSelectorPath, describeElement, isOwnUi, reactComponentPath, reactComponentSummary, reencodeWebScreenshot, visibleText, webContextProviders, webDiagnosticsOptions, webRouteStorage, webRouteWatcher };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ElementInfo, FeedbackPin, FeedbackScreenshot, FeedbackHint, FeedbackReport, SubmitOutcome, QueueStatus, ReportParts, ScreenshotCapture, ScreenshotReencode, WidgetControllerOpts, HintProvider, WidgetController, FeedbackStorage, ContextProviders } from '@solhun/feedback-kit-core';
1
+ import { ElementInfo, FeedbackPin, FeedbackScreenshot, FeedbackHint, FeedbackReport, SubmitOutcome, QueueStatus, ReportParts, ScreenshotCapture, ScreenshotReencode, RouteWatcher, WidgetControllerOpts, HintProvider, WidgetController, FeedbackStorage, ContextProviders, DiagnosticsInstallOpts, RouteTrailStore } from '@solhun/feedback-kit-core';
2
2
  export { COMMENT_MAX_CHARS, COMMENT_REQUIRED_MESSAGE, COMMENT_TOO_LONG_MESSAGE, ElementInfo, ElementLike, FLOATING_BUTTON_ID, FeedbackAdapter, FeedbackConfig, FeedbackContext, FeedbackHint, FeedbackPin, FeedbackReport, FeedbackStorage, FeedbackUser, HintCatalog, HintElementRule, HintPlatform, HintProvider, HintRankInput, HintScope, MODAL_ACTION_HINT_TOGGLE, MODAL_ACTION_PICK, ModalSubmitStatus, ReportModalController, ReportModalState, ResolvedConfig, SCREENSHOT_FAILED_MESSAGE, SOURCE_ATTR, SUBMIT_DONE_MESSAGE, SUBMIT_PENDING_MESSAGE, ScreenshotStatus, SubmitResult, Visibility, VisibilityEnv, VisibilityFn, WidgetController, WidgetPosition, WidgetScreen, WidgetState, denormalizePin, normalizePin, parseSourceAttr, rankHints, resolveConfig, shouldShowWidget, sourceFromElement } from '@solhun/feedback-kit-core';
3
3
  import * as react from 'react';
4
4
 
@@ -109,6 +109,18 @@ interface AnnotationPopupState {
109
109
  }
110
110
  interface PickingState {
111
111
  active: boolean;
112
+ /**
113
+ * 켜져 있지만 **클릭 가로채기만 잠시 끈 상태**.
114
+ *
115
+ * 왜 필요한가: 지목 모드는 페이지의 모든 클릭을 먹는다(그래야 지목하려고 누른 링크가
116
+ * 실제로 이동해 버리지 않는다). 그 대가로 **제보할 화면을 만들 수가 없다** — 드롭다운을
117
+ * 열고 그 안의 항목을 지목하고 싶어도 여는 클릭부터 막힌다. 그때 모드를 끄면 다시
118
+ * 켜야 하고, 사용자에겐 그게 "모드가 유지되지 않는다"로 읽힌다.
119
+ *
120
+ * 그래서 끄는 것과 멈추는 것을 나눈다. 멈춘 동안 페이지는 원래대로 동작하고, 모드는
121
+ * 켜진 채로 남는다.
122
+ */
123
+ paused: boolean;
112
124
  /** 지금 마우스가 올라가 있는 요소. 하이라이트를 그리는 근거. */
113
125
  hovered: ElementInfo | null;
114
126
  popup: AnnotationPopupState | null;
@@ -151,6 +163,12 @@ interface ElementPickingOpts {
151
163
  * `rankHints` 한 결과를 돌려준다. 없으면(힌트 기능이 꺼져 있으면) 팝업에 칩이 안 뜬다.
152
164
  */
153
165
  rankHintsFor?: ((element: ElementInfo | null) => readonly FeedbackHint[]) | null;
166
+ /**
167
+ * 화면 이동 감시자. 기본은 웹 공용 감시자(`webRouteWatcher`) — **진단의 화면 이동 궤적과
168
+ * 같은 것**이다. 여기만 따로 구현하면 한쪽이 이동을 놓쳤을 때 원인을 가릴 수 없다.
169
+ * `null` 을 주면 감시하지 않는다(테스트에서 `syncPath()` 를 직접 부를 때).
170
+ */
171
+ watchRoutes?: RouteWatcher | null;
154
172
  }
155
173
  declare class ElementPickingController {
156
174
  private readonly queue;
@@ -167,11 +185,21 @@ declare class ElementPickingController {
167
185
  private readonly rankHintsFor;
168
186
  private readonly listeners;
169
187
  private active;
188
+ /**
189
+ * 일시중지. **저장하지 않는다** — 멈추는 목적이 대개 "링크를 눌러 다른 화면으로 가는 것"
190
+ * 이라, 그 이동이 끝나면 지목이 돌아와 있는 게 목적에 맞다. 같은 이유로 경로가 바뀌면
191
+ * 스스로 풀린다(`syncPath`) — 그래야 SPA 이동과 새로고침이 같게 동작한다.
192
+ */
193
+ private paused;
170
194
  private attached;
195
+ /** 같은 요소 위 mousemove 마다 React 경로·선택자를 다시 만들지 않기 위한 캐시. */
196
+ private hoveredTarget;
171
197
  private hovered;
172
198
  private popup;
173
199
  private markers;
174
- private pathWatch;
200
+ private readonly watchRoutes;
201
+ /** 경로 감시 해제. 감시 중이 아니면 null. */
202
+ private unwatchRoutes;
175
203
  private lastPathname;
176
204
  private saving;
177
205
  /** 늦게 끝난 캡처가 다음 주석의 그림을 덮지 못하게 하는 세대 번호. */
@@ -179,24 +207,49 @@ declare class ElementPickingController {
179
207
  /** 지금 도는 자동 캡처. Enter 가 캡처보다 빨랐을 때 기다릴 대상. */
180
208
  private capturing;
181
209
  /**
182
- * 사용자가 [제안 ▾] 를 직접 건드렸는가. 한 번이라도 건드리면 이후 타이핑에 의한 자동
210
+ * 사용자가 [이런 건가요? ▾] 를 직접 건드렸는가. 한 번이라도 건드리면 이후 타이핑에 의한 자동
183
211
  * 접힘/펼침이 멈춘다 — 모달의 `userToggledHints` 와 같은 규칙. 팝업을 새로 열 때마다 리셋된다.
184
212
  */
185
213
  private userToggledHints;
186
214
  private readonly onClick;
187
215
  private readonly onMouseOver;
216
+ private readonly onMouseMove;
188
217
  constructor(opts: ElementPickingOpts);
189
218
  getState(): PickingState;
190
219
  get isActive(): boolean;
220
+ /** 켜져 있지만 클릭을 안 먹는 상태인가. */
221
+ get isPaused(): boolean;
191
222
  subscribe(listener: PickingListener): () => void;
192
223
  start(): void;
193
224
  /** [지목 종료]. 저장된 플래그까지 지워서 새로고침해도 다시 켜지지 않게 한다. */
194
225
  stop(): void;
226
+ /**
227
+ * 잠시 멈춘다 — 페이지 클릭이 원래대로 동작하고, 모드는 켜진 채로 남는다.
228
+ *
229
+ * 팝업이 열려 있으면 **그대로 둔다.** 쓰던 한 줄을 여기서 지우면 「지목 켜면 모달 초안이
230
+ * 날아간다」와 같은 사고를 다른 자리에 만드는 셈이다. 팝업은 위젯 자신의 UI 라
231
+ * 리스너를 떼도 계속 눌린다.
232
+ */
233
+ pause(): void;
234
+ /** 다시 지목을 받는다. */
235
+ resume(): void;
236
+ togglePause(): void;
195
237
  /**
196
238
  * 저장돼 있던 모드를 되살린다. 새로고침·페이지 이동 직후에 한 번 부른다.
197
239
  * @returns 되살아났으면 true.
198
240
  */
199
241
  restore(): boolean;
242
+ /**
243
+ * 이 화면에 쌓인 마커 표시를 지운다.
244
+ *
245
+ * 지워지는 건 **화면 표시뿐**이다. 제보는 이미 큐를 거쳐 수집처로 갔으므로 없어지지 않는다.
246
+ * 마커는 `localStorage` 에 남아 새로고침에도 살아남는데(그게 원래 목적이다) 정작 치울
247
+ * 수단이 없어서, 한 번 보낸 [완료] 배지가 그 화면을 볼 때마다 계속 따라다녔다.
248
+ *
249
+ * 화면에 보이는 목록의 기준은 `lastPathname` 이다(`reconcileMarkerOutcomes` 와 같다).
250
+ * 이동 직후 아직 `syncPath` 가 안 돈 순간에도 "지금 눈에 보이는 것"이 지워져야 한다.
251
+ */
252
+ clearMarkers(): void;
200
253
  /** 경로가 바뀌었을 때 그 경로의 마커로 갈아 끼운다. */
201
254
  syncPath(): readonly StoredMarker[];
202
255
  dispose(): void;
@@ -220,7 +273,7 @@ declare class ElementPickingController {
220
273
  * 아니면 줄바꿈 뒤 이어붙인다 — 모달의 `applyHint` 와 동일한 규칙이다.
221
274
  */
222
275
  applyHint(hintId: string): void;
223
- /** [제안 ▾] 토글. 방향과 무관하게 이후 자동 접힘/펼침을 멈춘다. */
276
+ /** [이런 건가요? ▾] 토글. 방향과 무관하게 이후 자동 접힘/펼침을 멈춘다. */
224
277
  toggleHints(): void;
225
278
  /**
226
279
  * 사용자가 붙여넣기(Cmd+V)나 드래그로 넣은 그림. 자동 캡처 결과를 덮는다.
@@ -344,5 +397,58 @@ declare function createWebStorage(backing?: SyncStorage): FeedbackStorage;
344
397
  * 서버 렌더 단계에서 실행될 수 있기 때문이다.
345
398
  */
346
399
  declare function webContextProviders(): ContextProviders;
400
+ /**
401
+ * 웹 진단 수집의 기본 배선.
402
+ *
403
+ * `sharedDiagnostics.install(webDiagnosticsOptions({ ... }))` 로 쓴다. 넘긴 값이 기본값을
404
+ * 덮으므로 감시자·저장소를 직접 갈아 끼울 수도 있다.
405
+ *
406
+ * 왜 패키지가 주나: 감시자를 호스트가 깜빡하면 **궤적만 조용히 빈다.** 네트워크·로그는
407
+ * 그대로 모이니 화면상 아무 문제가 없어 보이고, "이 제보엔 이동이 없었나 보다"로 읽힌다 —
408
+ * 빠진 것과 없는 것이 구분되지 않는 실패다.
409
+ *
410
+ * 진단을 끄면(`captureDiagnostics: false`) install 자체가 안 불리므로 궤적도 같이 꺼진다.
411
+ * 궤적만 따로 켜고 끄는 스위치는 만들지 않는다.
412
+ */
413
+ declare function webDiagnosticsOptions(opts?: DiagnosticsInstallOpts): DiagnosticsInstallOpts;
414
+
415
+ /** 감시자가 들여다보는 전역. 테스트에서는 가짜를 넣는다. */
416
+ interface RouteWatchTarget {
417
+ location?: {
418
+ pathname?: string;
419
+ } | null;
420
+ history?: {
421
+ pushState?: (...args: unknown[]) => unknown;
422
+ replaceState?: (...args: unknown[]) => unknown;
423
+ } | null;
424
+ addEventListener?: (type: string, listener: () => void) => void;
425
+ removeEventListener?: (type: string, listener: () => void) => void;
426
+ }
427
+ /**
428
+ * 경로 감시자 하나를 만든다.
429
+ *
430
+ * 돌려주는 것은 **구독 함수**다. 구독하면 현재 경로를 한 번 흘리고(마운트 시점의 화면이
431
+ * 궤적의 첫 줄이 된다), 이후 이동마다 새 경로를 흘린다. 해제 함수를 부르면 구독이 끊긴다.
432
+ *
433
+ * `history` 패치는 **첫 구독자가 생길 때 걸고 마지막이 나가면 푼다.** 구독자가 없는데 패치가
434
+ * 남아 있으면, 위젯을 떼어낸 뒤에도 호스트의 모든 화면 이동이 우리 코드를 거친다.
435
+ */
436
+ declare function createRouteWatcher(target?: RouteWatchTarget | null): RouteWatcher;
437
+ /**
438
+ * 웹 기본 감시자. **모듈 스코프에 하나**라서 궤적과 지목이 같은 인스턴스를 물려 쓴다.
439
+ * 각자 `createRouteWatcher()` 를 부르면 `history` 가 두 겹으로 패치돼, 해제 순서가 엇갈릴 때
440
+ * 원본이 복구되지 않는다.
441
+ */
442
+ declare const webRouteWatcher: RouteWatcher;
443
+ /**
444
+ * 궤적을 남길 탭 단위 저장소(`sessionStorage`).
445
+ *
446
+ * `localStorage` 가 아닌 이유: 궤적은 **이 탭에서 지금 지나온 길**이다. 탭을 나눠 쓰면
447
+ * 다른 탭의 이동이 섞여 재현 절차가 거짓이 된다.
448
+ *
449
+ * 프라이빗 모드·차단 설정에서는 접근 자체가 던진다. 그때는 `null` 을 돌려주고 궤적은
450
+ * 메모리로 떨어진다 — 저장을 못 한다고 제보를 막지 않는다.
451
+ */
452
+ declare function webRouteStorage(): RouteTrailStore | null;
347
453
 
348
- export { type AnnotationPopupState, ELEMENT_TEXT_MAX_CHARS, ElementPickingController, type ElementPickingOpts, FeedbackKit, type FeedbackKitProps, MARKER_KEY_PREFIX, MAX_MARKERS_PER_PATH, type MarkerStatus, MarkerStore, OWN_UI_ATTR, PICKING_MODE_KEY, type PickingListener, type PickingQueueLike, type PickingState, SELECTOR_MAX_DEPTH, type StorageLike, type StoredMarker, type WebWidget, type WebWidgetOpts, captureWebScreenshot, createWebStorage, createWebWidget, cssSelectorPath, describeElement, isOwnUi, reactComponentPath, reactComponentSummary, reencodeWebScreenshot, visibleText, webContextProviders };
454
+ export { type AnnotationPopupState, ELEMENT_TEXT_MAX_CHARS, ElementPickingController, type ElementPickingOpts, FeedbackKit, type FeedbackKitProps, MARKER_KEY_PREFIX, MAX_MARKERS_PER_PATH, type MarkerStatus, MarkerStore, OWN_UI_ATTR, PICKING_MODE_KEY, type PickingListener, type PickingQueueLike, type PickingState, type RouteWatchTarget, SELECTOR_MAX_DEPTH, type StorageLike, type StoredMarker, type WebWidget, type WebWidgetOpts, captureWebScreenshot, createRouteWatcher, createWebStorage, createWebWidget, cssSelectorPath, describeElement, isOwnUi, reactComponentPath, reactComponentSummary, reencodeWebScreenshot, visibleText, webContextProviders, webDiagnosticsOptions, webRouteStorage, webRouteWatcher };