@solhun/feedback-kit-web 0.3.0 → 0.4.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, FeedbackReport, SubmitOutcome, QueueStatus, ReportParts, ScreenshotCapture, ScreenshotReencode, WidgetControllerOpts, WidgetController, FeedbackStorage, ContextProviders } from '@solhun/feedback-kit-core';
1
+ import { ElementInfo, FeedbackPin, FeedbackScreenshot, FeedbackReport, SubmitOutcome, QueueStatus, ReportParts, ScreenshotCapture, ScreenshotReencode, WidgetControllerOpts, WidgetController, FeedbackStorage, ContextProviders } from '@solhun/feedback-kit-core';
2
2
  export { COMMENT_MAX_CHARS, COMMENT_REQUIRED_MESSAGE, COMMENT_TOO_LONG_MESSAGE, ElementInfo, FLOATING_BUTTON_ID, FeedbackAdapter, FeedbackConfig, FeedbackContext, FeedbackPin, FeedbackReport, FeedbackStorage, FeedbackUser, 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, resolveConfig, shouldShowWidget, sourceFromElement } from '@solhun/feedback-kit-core';
3
3
  import * as react from 'react';
4
4
 
@@ -76,12 +76,26 @@ interface PickingQueueLike {
76
76
  subscribe?(listener: (status: QueueStatus) => void): () => void;
77
77
  getOutcome?(clientSubmissionId: string): Pick<SubmitOutcome, "delivered" | "id"> | null;
78
78
  }
79
+ /** 퀵 주석의 스크린샷 상태. 모달과 같은 어휘를 쓴다(렌더러가 둘을 같은 방식으로 그린다). */
80
+ type AnnotationShotStatus = "none" | "capturing" | "ready" | "failed";
79
81
  interface AnnotationPopupState {
80
82
  element: ElementInfo | null;
81
83
  /** 클릭한 지점의 뷰포트 상대 좌표(0~1). */
82
84
  point: FeedbackPin;
83
85
  comment: string;
84
86
  canSave: boolean;
87
+ /**
88
+ * 이 주석에 실려 갈 그림. 클릭 직후 자동 캡처가 채우고, 사용자가 붙여넣기(Cmd+V)나
89
+ * 드래그로 덮을 수 있다.
90
+ *
91
+ * 왜 퀵 주석에도 그림을 싣나: 이게 없으면 **들어온 문에 따라 정보량이 달라진다.**
92
+ * 지목으로 남긴 제보만 화면 증거가 비는 일이 실제로 있었고(0.2.0), 그때 얻은 규칙이
93
+ * "같은 제보는 어느 문으로 들어와도 같은 것을 담는다" 다.
94
+ */
95
+ screenshot: FeedbackScreenshot | null;
96
+ screenshotStatus: AnnotationShotStatus;
97
+ /** 저장 중인가. 두 번 눌러 같은 주석이 두 번 가는 것을 막는다. */
98
+ saving: boolean;
85
99
  }
86
100
  interface PickingState {
87
101
  active: boolean;
@@ -106,8 +120,17 @@ interface ElementPickingOpts {
106
120
  height: number;
107
121
  };
108
122
  /**
109
- * 요소를 고르면 호출된다. 주면 **인라인 팝업 대신 이걸 부른다**
110
- * 호스트가 리포트 모달을 열어 스크린샷·우선순위까지 받게 하기 위해서다.
123
+ * 주석에 붙일 자동 캡처. 없으면 그림 없이 코멘트만 받는다.
124
+ * 기본 캡처는 위젯 자신의 UI 결과에서 빼므로(`ignoreElements`) 팝업이 찍히지 않는다.
125
+ */
126
+ capture?: ScreenshotCapture | null;
127
+ reencode?: ScreenshotReencode | null;
128
+ screenshotLimitBytes?: number;
129
+ /**
130
+ * 요소를 고르면 호출된다. 주면 **퀵 주석 팝업 대신 이걸 부른다** —
131
+ * 호스트가 전체 리포트 모달을 열어 우선순위까지 받고 싶을 때의 탈출구다.
132
+ * 기본값은 팝업이다: 지목은 "클릭 → 한 줄 → Enter" 로 끝나는 게 목적이라,
133
+ * 매번 중앙 모달이 뜨면 흐름이 끊긴다.
111
134
  *
112
135
  * 모드는 켜진 채로 둔다. 한 번 켜고 여러 요소를 연달아 지목하는 게 이 모드의 목적이라,
113
136
  * 요소 하나 고를 때마다 모드가 꺼지면 매번 다시 켜야 한다.
@@ -122,6 +145,9 @@ declare class ElementPickingController {
122
145
  private readonly getPathname;
123
146
  private readonly doc;
124
147
  private readonly getViewport;
148
+ private readonly capture;
149
+ private readonly reencode;
150
+ private readonly screenshotLimitBytes;
125
151
  private readonly unsubscribeQueue;
126
152
  private readonly listeners;
127
153
  private active;
@@ -132,6 +158,10 @@ declare class ElementPickingController {
132
158
  private pathWatch;
133
159
  private lastPathname;
134
160
  private saving;
161
+ /** 늦게 끝난 캡처가 다음 주석의 그림을 덮지 못하게 하는 세대 번호. */
162
+ private shotGeneration;
163
+ /** 지금 도는 자동 캡처. Enter 가 캡처보다 빨랐을 때 기다릴 대상. */
164
+ private capturing;
135
165
  private readonly onClick;
136
166
  private readonly onMouseOver;
137
167
  constructor(opts: ElementPickingOpts);
@@ -159,8 +189,21 @@ declare class ElementPickingController {
159
189
  y: number;
160
190
  }): void;
161
191
  setAnnotationComment(value: string): void;
192
+ /**
193
+ * 사용자가 붙여넣기(Cmd+V)나 드래그로 넣은 그림. 자동 캡처 결과를 덮는다.
194
+ *
195
+ * 자동 캡처보다 이게 우선인 이유: 사용자가 굳이 그림을 붙였다면 그건 "지금 화면"이
196
+ * 아니라 **보여주고 싶은 다른 것**이다(잘라낸 부분, 다른 탭, 기대하는 디자인).
197
+ */
198
+ attachAnnotationImage(shot: FeedbackScreenshot): Promise<void>;
199
+ /** 그림만 뗀다. 코멘트는 그대로 — 그림이 없어도 주석은 성립한다. */
200
+ removeAnnotationImage(): void;
162
201
  /** 취소 — 마커도 제보도 남기지 않는다. 모드는 켜진 채로 둔다. */
163
202
  cancelAnnotation(): void;
203
+ private isSavable;
204
+ /** 도는 캡처가 있으면 끝나기를 기다린다. 캡처가 던져도 저장을 막지 않는다. */
205
+ private settleCapture;
206
+ private runCapture;
164
207
  /**
165
208
  * 제출된 제보를 마커로 남긴다. 모달 경로에서 쓴다 — 마커 생성이 팝업 저장 안에만
166
209
  * 있으면, 리포트 모달로 보낸 지목 제보는 화면에 흔적이 남지 않는다("보냈는지 알 수
@@ -202,6 +245,16 @@ declare function reactComponentPath(node: Element, limit?: number): string[];
202
245
  /** 제보 본문에 실을 한 줄. 컴포넌트를 못 찾으면 null(빈 줄을 만들지 않는다). */
203
246
  declare function reactComponentSummary(node: Element): string | null;
204
247
 
248
+ /**
249
+ * 요소를 고른 뒤 무엇을 띄우는가.
250
+ *
251
+ * - `popup`(기본): 클릭한 자리에 **한 줄 입력창**. Enter 로 바로 보낸다.
252
+ * - `modal`: 화면 중앙의 전체 리포트 모달(우선순위까지 고른다).
253
+ *
254
+ * 기본이 팝업인 이유: 지목은 "여기 이거요" 를 여러 곳에 연달아 남기는 작업이다.
255
+ * 클릭마다 중앙 모달이 뜨면 시선이 클릭 지점을 떠나고, 어디를 가리켰는지 잊는다.
256
+ */
257
+ type PickTarget = "popup" | "modal";
205
258
  interface WebWidgetOpts extends Omit<WidgetControllerOpts, "platform" | "initialPicking" | "onPickingChange"> {
206
259
  /** 지목 모드 플래그와 마커를 담아 둘 저장소. 기본은 `localStorage`. */
207
260
  store?: MarkerStore;
@@ -213,6 +266,8 @@ interface WebWidgetOpts extends Omit<WidgetControllerOpts, "platform" | "initial
213
266
  width: number;
214
267
  height: number;
215
268
  };
269
+ /** 요소를 고른 뒤 띄울 UI. 기본은 `popup`. */
270
+ pickTarget?: PickTarget;
216
271
  }
217
272
  interface WebWidget {
218
273
  widget: WidgetController;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ElementInfo, FeedbackPin, FeedbackReport, SubmitOutcome, QueueStatus, ReportParts, ScreenshotCapture, ScreenshotReencode, WidgetControllerOpts, WidgetController, FeedbackStorage, ContextProviders } from '@solhun/feedback-kit-core';
1
+ import { ElementInfo, FeedbackPin, FeedbackScreenshot, FeedbackReport, SubmitOutcome, QueueStatus, ReportParts, ScreenshotCapture, ScreenshotReencode, WidgetControllerOpts, WidgetController, FeedbackStorage, ContextProviders } from '@solhun/feedback-kit-core';
2
2
  export { COMMENT_MAX_CHARS, COMMENT_REQUIRED_MESSAGE, COMMENT_TOO_LONG_MESSAGE, ElementInfo, FLOATING_BUTTON_ID, FeedbackAdapter, FeedbackConfig, FeedbackContext, FeedbackPin, FeedbackReport, FeedbackStorage, FeedbackUser, 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, resolveConfig, shouldShowWidget, sourceFromElement } from '@solhun/feedback-kit-core';
3
3
  import * as react from 'react';
4
4
 
@@ -76,12 +76,26 @@ interface PickingQueueLike {
76
76
  subscribe?(listener: (status: QueueStatus) => void): () => void;
77
77
  getOutcome?(clientSubmissionId: string): Pick<SubmitOutcome, "delivered" | "id"> | null;
78
78
  }
79
+ /** 퀵 주석의 스크린샷 상태. 모달과 같은 어휘를 쓴다(렌더러가 둘을 같은 방식으로 그린다). */
80
+ type AnnotationShotStatus = "none" | "capturing" | "ready" | "failed";
79
81
  interface AnnotationPopupState {
80
82
  element: ElementInfo | null;
81
83
  /** 클릭한 지점의 뷰포트 상대 좌표(0~1). */
82
84
  point: FeedbackPin;
83
85
  comment: string;
84
86
  canSave: boolean;
87
+ /**
88
+ * 이 주석에 실려 갈 그림. 클릭 직후 자동 캡처가 채우고, 사용자가 붙여넣기(Cmd+V)나
89
+ * 드래그로 덮을 수 있다.
90
+ *
91
+ * 왜 퀵 주석에도 그림을 싣나: 이게 없으면 **들어온 문에 따라 정보량이 달라진다.**
92
+ * 지목으로 남긴 제보만 화면 증거가 비는 일이 실제로 있었고(0.2.0), 그때 얻은 규칙이
93
+ * "같은 제보는 어느 문으로 들어와도 같은 것을 담는다" 다.
94
+ */
95
+ screenshot: FeedbackScreenshot | null;
96
+ screenshotStatus: AnnotationShotStatus;
97
+ /** 저장 중인가. 두 번 눌러 같은 주석이 두 번 가는 것을 막는다. */
98
+ saving: boolean;
85
99
  }
86
100
  interface PickingState {
87
101
  active: boolean;
@@ -106,8 +120,17 @@ interface ElementPickingOpts {
106
120
  height: number;
107
121
  };
108
122
  /**
109
- * 요소를 고르면 호출된다. 주면 **인라인 팝업 대신 이걸 부른다**
110
- * 호스트가 리포트 모달을 열어 스크린샷·우선순위까지 받게 하기 위해서다.
123
+ * 주석에 붙일 자동 캡처. 없으면 그림 없이 코멘트만 받는다.
124
+ * 기본 캡처는 위젯 자신의 UI 결과에서 빼므로(`ignoreElements`) 팝업이 찍히지 않는다.
125
+ */
126
+ capture?: ScreenshotCapture | null;
127
+ reencode?: ScreenshotReencode | null;
128
+ screenshotLimitBytes?: number;
129
+ /**
130
+ * 요소를 고르면 호출된다. 주면 **퀵 주석 팝업 대신 이걸 부른다** —
131
+ * 호스트가 전체 리포트 모달을 열어 우선순위까지 받고 싶을 때의 탈출구다.
132
+ * 기본값은 팝업이다: 지목은 "클릭 → 한 줄 → Enter" 로 끝나는 게 목적이라,
133
+ * 매번 중앙 모달이 뜨면 흐름이 끊긴다.
111
134
  *
112
135
  * 모드는 켜진 채로 둔다. 한 번 켜고 여러 요소를 연달아 지목하는 게 이 모드의 목적이라,
113
136
  * 요소 하나 고를 때마다 모드가 꺼지면 매번 다시 켜야 한다.
@@ -122,6 +145,9 @@ declare class ElementPickingController {
122
145
  private readonly getPathname;
123
146
  private readonly doc;
124
147
  private readonly getViewport;
148
+ private readonly capture;
149
+ private readonly reencode;
150
+ private readonly screenshotLimitBytes;
125
151
  private readonly unsubscribeQueue;
126
152
  private readonly listeners;
127
153
  private active;
@@ -132,6 +158,10 @@ declare class ElementPickingController {
132
158
  private pathWatch;
133
159
  private lastPathname;
134
160
  private saving;
161
+ /** 늦게 끝난 캡처가 다음 주석의 그림을 덮지 못하게 하는 세대 번호. */
162
+ private shotGeneration;
163
+ /** 지금 도는 자동 캡처. Enter 가 캡처보다 빨랐을 때 기다릴 대상. */
164
+ private capturing;
135
165
  private readonly onClick;
136
166
  private readonly onMouseOver;
137
167
  constructor(opts: ElementPickingOpts);
@@ -159,8 +189,21 @@ declare class ElementPickingController {
159
189
  y: number;
160
190
  }): void;
161
191
  setAnnotationComment(value: string): void;
192
+ /**
193
+ * 사용자가 붙여넣기(Cmd+V)나 드래그로 넣은 그림. 자동 캡처 결과를 덮는다.
194
+ *
195
+ * 자동 캡처보다 이게 우선인 이유: 사용자가 굳이 그림을 붙였다면 그건 "지금 화면"이
196
+ * 아니라 **보여주고 싶은 다른 것**이다(잘라낸 부분, 다른 탭, 기대하는 디자인).
197
+ */
198
+ attachAnnotationImage(shot: FeedbackScreenshot): Promise<void>;
199
+ /** 그림만 뗀다. 코멘트는 그대로 — 그림이 없어도 주석은 성립한다. */
200
+ removeAnnotationImage(): void;
162
201
  /** 취소 — 마커도 제보도 남기지 않는다. 모드는 켜진 채로 둔다. */
163
202
  cancelAnnotation(): void;
203
+ private isSavable;
204
+ /** 도는 캡처가 있으면 끝나기를 기다린다. 캡처가 던져도 저장을 막지 않는다. */
205
+ private settleCapture;
206
+ private runCapture;
164
207
  /**
165
208
  * 제출된 제보를 마커로 남긴다. 모달 경로에서 쓴다 — 마커 생성이 팝업 저장 안에만
166
209
  * 있으면, 리포트 모달로 보낸 지목 제보는 화면에 흔적이 남지 않는다("보냈는지 알 수
@@ -202,6 +245,16 @@ declare function reactComponentPath(node: Element, limit?: number): string[];
202
245
  /** 제보 본문에 실을 한 줄. 컴포넌트를 못 찾으면 null(빈 줄을 만들지 않는다). */
203
246
  declare function reactComponentSummary(node: Element): string | null;
204
247
 
248
+ /**
249
+ * 요소를 고른 뒤 무엇을 띄우는가.
250
+ *
251
+ * - `popup`(기본): 클릭한 자리에 **한 줄 입력창**. Enter 로 바로 보낸다.
252
+ * - `modal`: 화면 중앙의 전체 리포트 모달(우선순위까지 고른다).
253
+ *
254
+ * 기본이 팝업인 이유: 지목은 "여기 이거요" 를 여러 곳에 연달아 남기는 작업이다.
255
+ * 클릭마다 중앙 모달이 뜨면 시선이 클릭 지점을 떠나고, 어디를 가리켰는지 잊는다.
256
+ */
257
+ type PickTarget = "popup" | "modal";
205
258
  interface WebWidgetOpts extends Omit<WidgetControllerOpts, "platform" | "initialPicking" | "onPickingChange"> {
206
259
  /** 지목 모드 플래그와 마커를 담아 둘 저장소. 기본은 `localStorage`. */
207
260
  store?: MarkerStore;
@@ -213,6 +266,8 @@ interface WebWidgetOpts extends Omit<WidgetControllerOpts, "platform" | "initial
213
266
  width: number;
214
267
  height: number;
215
268
  };
269
+ /** 요소를 고른 뒤 띄울 UI. 기본은 `popup`. */
270
+ pickTarget?: PickTarget;
216
271
  }
217
272
  interface WebWidget {
218
273
  widget: WidgetController;