@solhun/feedback-kit-core 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.cjs +76 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +92 -3
- package/dist/index.d.ts +92 -3
- package/dist/index.js +75 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -891,6 +891,18 @@ declare const SUBMIT_FAILED_MESSAGE = "\uBCF4\uB0B4\uC9C0 \uBABB\uD588\uC2B5\uB2
|
|
|
891
891
|
type WidgetPlatform = "web" | "native";
|
|
892
892
|
type ScreenshotStatus = "none" | "capturing" | "ready" | "failed";
|
|
893
893
|
type ModalSubmitStatus = "idle" | "sending" | "sent" | "pending" | "failed";
|
|
894
|
+
/**
|
|
895
|
+
* 핀이 **무엇을 기준으로** 찍힌 좌표인가. 이미지가 바뀔 때 핀을 버릴지 말지가 여기서 갈린다.
|
|
896
|
+
*
|
|
897
|
+
* - `picture`: 스크린샷 **그림 위**를 탭해서 찍은 좌표(앱 핀 화면). 그림이 바뀌면 가리키는
|
|
898
|
+
* 대상이 달라지므로 **버려야 한다.**
|
|
899
|
+
* - `viewport`: **페이지 화면**을 클릭해서 나온 좌표(웹 요소 지목). 같은 뷰포트를 찍은
|
|
900
|
+
* 스크린샷이면 어느 장이든 같은 지점을 가리키므로 **살려야 한다.**
|
|
901
|
+
*
|
|
902
|
+
* 이 구분이 없던 동안 지목 좌표가 전부 유실됐다: 클릭 직후 `setPin` 으로 들어온 좌표를
|
|
903
|
+
* 수백 ms 뒤 도착한 자동 캡처가 `pin: null` 로 덮었다(실측 — 서버 `x`/`y` 가 전부 null).
|
|
904
|
+
*/
|
|
905
|
+
type PinAnchor = "picture" | "viewport";
|
|
894
906
|
/** 모달 안에서 포커스를 받는 요소들의 고정 id. 렌더러가 그대로 매단다. */
|
|
895
907
|
declare const MODAL_FIELD_COMMENT = "comment";
|
|
896
908
|
declare const MODAL_FIELD_PRIORITY = "priority";
|
|
@@ -970,6 +982,8 @@ declare class ReportModalController {
|
|
|
970
982
|
private lastReport;
|
|
971
983
|
/** 이전 open/recapture가 늦게 끝나 최신 상태를 덮지 못하게 하는 세대 번호. */
|
|
972
984
|
private captureGeneration;
|
|
985
|
+
/** 지금 들고 있는 핀의 기준. 핀이 없으면 의미 없다(기본 `picture`). */
|
|
986
|
+
private pinAnchor;
|
|
973
987
|
private state;
|
|
974
988
|
constructor(opts: ReportModalOpts);
|
|
975
989
|
getState(): ReportModalState;
|
|
@@ -979,8 +993,14 @@ declare class ReportModalController {
|
|
|
979
993
|
dispose(): void;
|
|
980
994
|
/** WidgetController가 소유한 큐 lifecycle을 함께 정리한다. */
|
|
981
995
|
stopQueue(): void;
|
|
996
|
+
/**
|
|
997
|
+
* @param opts.pin 요소를 지목해서 열 때의 **클릭 지점**(뷰포트 기준 0~1).
|
|
998
|
+
* 여기로 받아야 뒤늦게 끝나는 자동 캡처가 좌표를 지우지 않는다 — `setPin` 으로
|
|
999
|
+
* 따로 넣으면 캡처와 경쟁해서 진다(그 경쟁에 계속 져서 좌표가 전부 유실됐다).
|
|
1000
|
+
*/
|
|
982
1001
|
open(opts?: {
|
|
983
1002
|
element?: ElementInfo | null;
|
|
1003
|
+
pin?: FeedbackPin | null;
|
|
984
1004
|
}): Promise<void>;
|
|
985
1005
|
/**
|
|
986
1006
|
* 닫기 요청. 쓰던 내용이 있으면 곧장 닫지 않고 확인을 받는다.
|
|
@@ -997,8 +1017,12 @@ declare class ReportModalController {
|
|
|
997
1017
|
/** 상한을 넘겨도 값을 자르지 않는다. 거부는 하되 사용자가 쓴 글은 보존한다. */
|
|
998
1018
|
setComment(value: string): void;
|
|
999
1019
|
setPriority(priority: FeedbackPriority): void;
|
|
1000
|
-
|
|
1001
|
-
|
|
1020
|
+
/**
|
|
1021
|
+
* @param anchor 이 좌표의 기준. 기본은 `picture`(그림 위 탭) — 앱 핀 화면이 쓰는 값이다.
|
|
1022
|
+
* 웹에서 페이지를 클릭해 얻은 좌표는 `viewport` 로 넣어야 캡처에 지워지지 않는다.
|
|
1023
|
+
*/
|
|
1024
|
+
setPin(pin: FeedbackPin | null, anchor?: PinAnchor): void;
|
|
1025
|
+
/** 스크린샷 제거. 그림 위에 찍은 핀은 가리킬 대상이 사라지므로 함께 버린다. */
|
|
1002
1026
|
removeScreenshot(): void;
|
|
1003
1027
|
recapture(): Promise<void>;
|
|
1004
1028
|
/** 캡처가 실패했을 때의 대체 경로 — 사용자가 직접 고른 파일. */
|
|
@@ -1062,9 +1086,12 @@ declare class WidgetController {
|
|
|
1062
1086
|
* @param opts.element 요소를 지목해서 열 때 그 요소. 지목 모드에서도 화면을
|
|
1063
1087
|
* `modal` 로 바꾼다 — `modal.open()` 을 직접 부르면 상태만 열리고 화면은
|
|
1064
1088
|
* `picking` 에 머물러 **모달이 렌더되지 않는다**.
|
|
1089
|
+
* @param opts.pin 지목한 클릭 지점. 열기 인자로 넘겨야 자동 캡처가 좌표를 지우지 않는다
|
|
1090
|
+
* (열고 난 뒤 `modal.setPin` 으로 넣으면 캡처와 경쟁한다).
|
|
1065
1091
|
*/
|
|
1066
1092
|
openReport(opts?: {
|
|
1067
1093
|
element?: ElementInfo | null;
|
|
1094
|
+
pin?: FeedbackPin | null;
|
|
1068
1095
|
}): Promise<void>;
|
|
1069
1096
|
/** 모달 닫기 요청. 쓰던 내용이 있으면 확인부터 받는다(화면은 그대로 모달). */
|
|
1070
1097
|
closeReport(): "closed" | "confirm";
|
|
@@ -1194,6 +1221,55 @@ interface LassoAdapterOpts {
|
|
|
1194
1221
|
* 400 은 재시도 불가라 모든 제보가 조용히 데드레터로 사라졌다.
|
|
1195
1222
|
* 계약을 바꿀 때는 반드시 실호출로 다시 확인한다(adapters.live.test.ts).
|
|
1196
1223
|
*/
|
|
1224
|
+
/**
|
|
1225
|
+
* 구조화 칸이 따로 없는 값들을 담는 자리 → 서버가 `meta` 로 옮긴다.
|
|
1226
|
+
*
|
|
1227
|
+
* 왜 생겼나: 예전에는 이 값들을 **사람이 읽는 마크다운으로 접어 `comment` 에 넣었다.**
|
|
1228
|
+
* 그런데 라쏘런의 `comment` 는 목록·상세의 **제목 자리**다. 5자짜리 코멘트 하나가
|
|
1229
|
+
* 1,399자 덩어리가 되어 화면에 쏟아졌다(실측). 라쏘런은 구조화 칸이 있는 대상이니
|
|
1230
|
+
* 접어 넣을 이유가 없다 — 대상별로 담는 방식이 다른 게 어댑터를 두는 이유다.
|
|
1231
|
+
*
|
|
1232
|
+
* 이름은 **뷰어가 이미 읽는 키를 그대로** 쓴다(`user`·`device`·`appInfo`·`navPath` …).
|
|
1233
|
+
* 새 이름을 지으면 같은 값이 화면에서 두 군데로 갈린다.
|
|
1234
|
+
*/
|
|
1235
|
+
interface LassoReportContext {
|
|
1236
|
+
/** 제보 종류(`report`=화면 전체, `annotation`=요소 지목). 서버는 요소 유무로도 가른다. */
|
|
1237
|
+
kind: string;
|
|
1238
|
+
app: string;
|
|
1239
|
+
platform: string;
|
|
1240
|
+
sessionId: string;
|
|
1241
|
+
clientTimestamp: string;
|
|
1242
|
+
timezone: string | null;
|
|
1243
|
+
user: {
|
|
1244
|
+
id: string | null;
|
|
1245
|
+
name: string | null;
|
|
1246
|
+
email: string | null;
|
|
1247
|
+
role: string | null;
|
|
1248
|
+
isGuest: boolean;
|
|
1249
|
+
} | null;
|
|
1250
|
+
/** 빌드 플러그인이 심은 화면·소스 매핑. 없으면 null. */
|
|
1251
|
+
source: {
|
|
1252
|
+
screenId: string | null;
|
|
1253
|
+
sourceFile: string | null;
|
|
1254
|
+
sourceLine: number | null;
|
|
1255
|
+
} | null;
|
|
1256
|
+
/** 지목한 요소의 나머지 정보(id·속성). 태그·선택자·텍스트는 report.element 에 있다. */
|
|
1257
|
+
element: {
|
|
1258
|
+
id: string | null;
|
|
1259
|
+
attributes: Record<string, string>;
|
|
1260
|
+
} | null;
|
|
1261
|
+
/** 앱 전용. 뷰어의 구현 정보 칸(기기·앱 버전·OTA·탐색 경로)이 이걸 읽는다. */
|
|
1262
|
+
native: {
|
|
1263
|
+
screenPath: string | null;
|
|
1264
|
+
navPath: string[];
|
|
1265
|
+
routeParams: Record<string, unknown> | null;
|
|
1266
|
+
appInfo: Record<string, unknown>;
|
|
1267
|
+
device: Record<string, unknown>;
|
|
1268
|
+
display: Record<string, unknown>;
|
|
1269
|
+
} | null;
|
|
1270
|
+
/** 호스트가 넣은 자유 메타. 우리가 해석하지 않는다. */
|
|
1271
|
+
extra: Record<string, unknown>;
|
|
1272
|
+
}
|
|
1197
1273
|
interface LassoEnvelope {
|
|
1198
1274
|
/** UUID. **Idempotency-Key 헤더와 반드시 같은 값**이어야 서버가 받는다. */
|
|
1199
1275
|
clientSubmissionId: string;
|
|
@@ -1226,7 +1302,16 @@ interface LassoEnvelope {
|
|
|
1226
1302
|
timestamp: number;
|
|
1227
1303
|
};
|
|
1228
1304
|
report?: {
|
|
1305
|
+
/**
|
|
1306
|
+
* **사용자가 쓴 문장 그대로.** 진단을 여기에 접어 넣지 않는다 — 이 값이 곧 제목이다.
|
|
1307
|
+
* 나머지는 구조화 칸(`web`·`network`·`logs`·`element`)과 `context` 로 간다.
|
|
1308
|
+
*/
|
|
1229
1309
|
comment: string;
|
|
1310
|
+
/**
|
|
1311
|
+
* 좌표. **0~100 백분율**이다(코어의 0~1 정규화를 여기서 옮긴다).
|
|
1312
|
+
* 라쏘런의 `x`/`y` 컬럼과 뷰어 오버레이가 백분율을 전제한다 — 0~1 로 보내면
|
|
1313
|
+
* 뷰어의 뷰포트 역산이 "0.12%" 로 읽어 조용히 버리거나 터무니없는 값을 낸다.
|
|
1314
|
+
*/
|
|
1230
1315
|
pin: {
|
|
1231
1316
|
x: number;
|
|
1232
1317
|
y: number;
|
|
@@ -1269,8 +1354,12 @@ interface LassoEnvelope {
|
|
|
1269
1354
|
screenshotContentType: "image/jpeg" | "image/png" | null;
|
|
1270
1355
|
network: unknown[];
|
|
1271
1356
|
logs: unknown[];
|
|
1357
|
+
/** 위 칸에 자리가 없는 값들. 서버가 `meta` 로 펼친다. */
|
|
1358
|
+
context: LassoReportContext;
|
|
1272
1359
|
};
|
|
1273
1360
|
}
|
|
1361
|
+
/** 구조화 칸에 자리가 없는 값들을 모은다. 비어 있어도 키는 남긴다(미수집과 없음을 구분). */
|
|
1362
|
+
declare function buildLassoContext(report: FeedbackReport): LassoReportContext;
|
|
1274
1363
|
/** 제보를 라쏘런 봉투로 바꾼다. 순수 함수라 테스트가 전송 없이 모양만 볼 수 있다. */
|
|
1275
1364
|
declare function buildLassoEnvelope(report: FeedbackReport): LassoEnvelope;
|
|
1276
1365
|
/**
|
|
@@ -1305,4 +1394,4 @@ declare function guestUser(id: string): FeedbackUser;
|
|
|
1305
1394
|
*/
|
|
1306
1395
|
declare function uuidv4(): string;
|
|
1307
1396
|
|
|
1308
|
-
export { type AdapterName, type AppInfo, BACKOFF_BASE_MS, BACKOFF_MAX_MS, type BuildContextOpts, type BuildReportOpts, COMMENT_MAX_CHARS, COMMENT_REQUIRED_MESSAGE, COMMENT_TOO_LONG_MESSAGE, type CaptureWithinLimitOpts, type ConfigWarning, type ConfigWarningCode, type ContextProviders, type ContextUser, DEFAULT_ADAPTER, DEFAULT_INTERNAL_ROLES, DEFAULT_POSITION, type DeviceInfo, type DiagLogEntry, type DiagNetworkEntry, type DiagRequestRef, DiagnosticsCollector, type DiagnosticsExcludeMatcher, type DiagnosticsInstallOpts, type DiagnosticsPayload, type DiagnosticsSource, type DisplayInfo, ENDPOINT_FROM_ADAPTER, type ElementInfo, FLOATING_BUTTON_ID, type FeedbackAdapter, type FeedbackConfig, type FeedbackContext, type FeedbackKind, type FeedbackPin, type FeedbackPriority, FeedbackQueue, type FeedbackQueueOpts, type FeedbackReport, type FeedbackScreenshot, type FeedbackStorage, type FeedbackUser, FocusRing, type GetCurrentScreenFn, type GetUserFn, KEEPALIVE_BODY_LIMIT_BYTES, LASSO_DEFAULT_ENDPOINT, LOG_BUFFER_LIMIT, type LassoAdapterOpts, type LassoEnvelope, type LassoFetch, type LassoRequestInit, type LassoResponseLike, type LinearIssueInput, MAX_LOG_MESSAGE_CHARS, MAX_QUEUE_AGE_MS, MAX_QUEUE_ITEMS, MODAL_ACTION_ATTACH, MODAL_ACTION_CANCEL, MODAL_ACTION_PICK, MODAL_ACTION_PIN, MODAL_ACTION_REMOVE_SCREENSHOT, MODAL_ACTION_RETRY, MODAL_ACTION_SEND, MODAL_FIELD_COMMENT, MODAL_FIELD_PRIORITY, type ModalQueueLike, type ModalSubmitStatus, NETWORK_BUFFER_LIMIT, type NativeContext, type NotionPageInput, PinController, type PixelPoint, type PixelSize, type QueueStatus, type QueueStatusListener, RATE_LIMIT_MIN_DELAY_MS, REDACTED, ReportModalController, type ReportModalListener, type ReportModalOpts, type ReportModalState, type ReportParts, type ResolvedConfig, RingBuffer, SCREENSHOT_FAILED_MESSAGE, SCREENSHOT_MAX_BASE64_BYTES, SCREENSHOT_QUALITY_STEPS, SOURCE_ATTR, SUBMIT_DONE_MESSAGE, SUBMIT_FAILED_MESSAGE, SUBMIT_PENDING_MESSAGE, type ScreenshotCapture, type ScreenshotOutcome, type ScreenshotReencode, type ScreenshotStatus, type SourceLocation, type SourceMapping, type SubmitOutcome, type SubmitResult, TITLE_MAX_CHARS, type Visibility, type VisibilityEnv, type VisibilityFn, type WebContext, type WebContextInput, WidgetController, type WidgetControllerOpts, type WidgetCorner, type WidgetListener, type WidgetPlatform, type WidgetPosition, type WidgetScreen, type WidgetState, buildContext, buildLassoEnvelope, buildLinearIssue, buildNotionPage, buildReport, buildTitle, byteLengthOf, canUseKeepalive, captureWithinLimit, createLassoAdapter, defaultBackoff, denormalizePin, detectDevBuild, diagnosticsProviderFor, getOrCreateGuestId, guestUser, isInternalUser, normalizePin, normalizeUser, parseSourceAttr, renderReportBody, resolveConfig, resolveContextUser, sanitizeUrl, screenshotBytes, sharedDiagnostics, shouldShowWidget, sourceFromElement, uuidv4 };
|
|
1397
|
+
export { type AdapterName, type AppInfo, BACKOFF_BASE_MS, BACKOFF_MAX_MS, type BuildContextOpts, type BuildReportOpts, COMMENT_MAX_CHARS, COMMENT_REQUIRED_MESSAGE, COMMENT_TOO_LONG_MESSAGE, type CaptureWithinLimitOpts, type ConfigWarning, type ConfigWarningCode, type ContextProviders, type ContextUser, DEFAULT_ADAPTER, DEFAULT_INTERNAL_ROLES, DEFAULT_POSITION, type DeviceInfo, type DiagLogEntry, type DiagNetworkEntry, type DiagRequestRef, DiagnosticsCollector, type DiagnosticsExcludeMatcher, type DiagnosticsInstallOpts, type DiagnosticsPayload, type DiagnosticsSource, type DisplayInfo, ENDPOINT_FROM_ADAPTER, type ElementInfo, FLOATING_BUTTON_ID, type FeedbackAdapter, type FeedbackConfig, type FeedbackContext, type FeedbackKind, type FeedbackPin, type FeedbackPriority, FeedbackQueue, type FeedbackQueueOpts, type FeedbackReport, type FeedbackScreenshot, type FeedbackStorage, type FeedbackUser, FocusRing, type GetCurrentScreenFn, type GetUserFn, KEEPALIVE_BODY_LIMIT_BYTES, LASSO_DEFAULT_ENDPOINT, LOG_BUFFER_LIMIT, type LassoAdapterOpts, type LassoEnvelope, type LassoFetch, type LassoReportContext, type LassoRequestInit, type LassoResponseLike, type LinearIssueInput, MAX_LOG_MESSAGE_CHARS, MAX_QUEUE_AGE_MS, MAX_QUEUE_ITEMS, MODAL_ACTION_ATTACH, MODAL_ACTION_CANCEL, MODAL_ACTION_PICK, MODAL_ACTION_PIN, MODAL_ACTION_REMOVE_SCREENSHOT, MODAL_ACTION_RETRY, MODAL_ACTION_SEND, MODAL_FIELD_COMMENT, MODAL_FIELD_PRIORITY, type ModalQueueLike, type ModalSubmitStatus, NETWORK_BUFFER_LIMIT, type NativeContext, type NotionPageInput, PinController, type PixelPoint, type PixelSize, type QueueStatus, type QueueStatusListener, RATE_LIMIT_MIN_DELAY_MS, REDACTED, ReportModalController, type ReportModalListener, type ReportModalOpts, type ReportModalState, type ReportParts, type ResolvedConfig, RingBuffer, SCREENSHOT_FAILED_MESSAGE, SCREENSHOT_MAX_BASE64_BYTES, SCREENSHOT_QUALITY_STEPS, SOURCE_ATTR, SUBMIT_DONE_MESSAGE, SUBMIT_FAILED_MESSAGE, SUBMIT_PENDING_MESSAGE, type ScreenshotCapture, type ScreenshotOutcome, type ScreenshotReencode, type ScreenshotStatus, type SourceLocation, type SourceMapping, type SubmitOutcome, type SubmitResult, TITLE_MAX_CHARS, type Visibility, type VisibilityEnv, type VisibilityFn, type WebContext, type WebContextInput, WidgetController, type WidgetControllerOpts, type WidgetCorner, type WidgetListener, type WidgetPlatform, type WidgetPosition, type WidgetScreen, type WidgetState, buildContext, buildLassoContext, buildLassoEnvelope, buildLinearIssue, buildNotionPage, buildReport, buildTitle, byteLengthOf, canUseKeepalive, captureWithinLimit, createLassoAdapter, defaultBackoff, denormalizePin, detectDevBuild, diagnosticsProviderFor, getOrCreateGuestId, guestUser, isInternalUser, normalizePin, normalizeUser, parseSourceAttr, renderReportBody, resolveConfig, resolveContextUser, sanitizeUrl, screenshotBytes, sharedDiagnostics, shouldShowWidget, sourceFromElement, uuidv4 };
|
package/dist/index.d.ts
CHANGED
|
@@ -891,6 +891,18 @@ declare const SUBMIT_FAILED_MESSAGE = "\uBCF4\uB0B4\uC9C0 \uBABB\uD588\uC2B5\uB2
|
|
|
891
891
|
type WidgetPlatform = "web" | "native";
|
|
892
892
|
type ScreenshotStatus = "none" | "capturing" | "ready" | "failed";
|
|
893
893
|
type ModalSubmitStatus = "idle" | "sending" | "sent" | "pending" | "failed";
|
|
894
|
+
/**
|
|
895
|
+
* 핀이 **무엇을 기준으로** 찍힌 좌표인가. 이미지가 바뀔 때 핀을 버릴지 말지가 여기서 갈린다.
|
|
896
|
+
*
|
|
897
|
+
* - `picture`: 스크린샷 **그림 위**를 탭해서 찍은 좌표(앱 핀 화면). 그림이 바뀌면 가리키는
|
|
898
|
+
* 대상이 달라지므로 **버려야 한다.**
|
|
899
|
+
* - `viewport`: **페이지 화면**을 클릭해서 나온 좌표(웹 요소 지목). 같은 뷰포트를 찍은
|
|
900
|
+
* 스크린샷이면 어느 장이든 같은 지점을 가리키므로 **살려야 한다.**
|
|
901
|
+
*
|
|
902
|
+
* 이 구분이 없던 동안 지목 좌표가 전부 유실됐다: 클릭 직후 `setPin` 으로 들어온 좌표를
|
|
903
|
+
* 수백 ms 뒤 도착한 자동 캡처가 `pin: null` 로 덮었다(실측 — 서버 `x`/`y` 가 전부 null).
|
|
904
|
+
*/
|
|
905
|
+
type PinAnchor = "picture" | "viewport";
|
|
894
906
|
/** 모달 안에서 포커스를 받는 요소들의 고정 id. 렌더러가 그대로 매단다. */
|
|
895
907
|
declare const MODAL_FIELD_COMMENT = "comment";
|
|
896
908
|
declare const MODAL_FIELD_PRIORITY = "priority";
|
|
@@ -970,6 +982,8 @@ declare class ReportModalController {
|
|
|
970
982
|
private lastReport;
|
|
971
983
|
/** 이전 open/recapture가 늦게 끝나 최신 상태를 덮지 못하게 하는 세대 번호. */
|
|
972
984
|
private captureGeneration;
|
|
985
|
+
/** 지금 들고 있는 핀의 기준. 핀이 없으면 의미 없다(기본 `picture`). */
|
|
986
|
+
private pinAnchor;
|
|
973
987
|
private state;
|
|
974
988
|
constructor(opts: ReportModalOpts);
|
|
975
989
|
getState(): ReportModalState;
|
|
@@ -979,8 +993,14 @@ declare class ReportModalController {
|
|
|
979
993
|
dispose(): void;
|
|
980
994
|
/** WidgetController가 소유한 큐 lifecycle을 함께 정리한다. */
|
|
981
995
|
stopQueue(): void;
|
|
996
|
+
/**
|
|
997
|
+
* @param opts.pin 요소를 지목해서 열 때의 **클릭 지점**(뷰포트 기준 0~1).
|
|
998
|
+
* 여기로 받아야 뒤늦게 끝나는 자동 캡처가 좌표를 지우지 않는다 — `setPin` 으로
|
|
999
|
+
* 따로 넣으면 캡처와 경쟁해서 진다(그 경쟁에 계속 져서 좌표가 전부 유실됐다).
|
|
1000
|
+
*/
|
|
982
1001
|
open(opts?: {
|
|
983
1002
|
element?: ElementInfo | null;
|
|
1003
|
+
pin?: FeedbackPin | null;
|
|
984
1004
|
}): Promise<void>;
|
|
985
1005
|
/**
|
|
986
1006
|
* 닫기 요청. 쓰던 내용이 있으면 곧장 닫지 않고 확인을 받는다.
|
|
@@ -997,8 +1017,12 @@ declare class ReportModalController {
|
|
|
997
1017
|
/** 상한을 넘겨도 값을 자르지 않는다. 거부는 하되 사용자가 쓴 글은 보존한다. */
|
|
998
1018
|
setComment(value: string): void;
|
|
999
1019
|
setPriority(priority: FeedbackPriority): void;
|
|
1000
|
-
|
|
1001
|
-
|
|
1020
|
+
/**
|
|
1021
|
+
* @param anchor 이 좌표의 기준. 기본은 `picture`(그림 위 탭) — 앱 핀 화면이 쓰는 값이다.
|
|
1022
|
+
* 웹에서 페이지를 클릭해 얻은 좌표는 `viewport` 로 넣어야 캡처에 지워지지 않는다.
|
|
1023
|
+
*/
|
|
1024
|
+
setPin(pin: FeedbackPin | null, anchor?: PinAnchor): void;
|
|
1025
|
+
/** 스크린샷 제거. 그림 위에 찍은 핀은 가리킬 대상이 사라지므로 함께 버린다. */
|
|
1002
1026
|
removeScreenshot(): void;
|
|
1003
1027
|
recapture(): Promise<void>;
|
|
1004
1028
|
/** 캡처가 실패했을 때의 대체 경로 — 사용자가 직접 고른 파일. */
|
|
@@ -1062,9 +1086,12 @@ declare class WidgetController {
|
|
|
1062
1086
|
* @param opts.element 요소를 지목해서 열 때 그 요소. 지목 모드에서도 화면을
|
|
1063
1087
|
* `modal` 로 바꾼다 — `modal.open()` 을 직접 부르면 상태만 열리고 화면은
|
|
1064
1088
|
* `picking` 에 머물러 **모달이 렌더되지 않는다**.
|
|
1089
|
+
* @param opts.pin 지목한 클릭 지점. 열기 인자로 넘겨야 자동 캡처가 좌표를 지우지 않는다
|
|
1090
|
+
* (열고 난 뒤 `modal.setPin` 으로 넣으면 캡처와 경쟁한다).
|
|
1065
1091
|
*/
|
|
1066
1092
|
openReport(opts?: {
|
|
1067
1093
|
element?: ElementInfo | null;
|
|
1094
|
+
pin?: FeedbackPin | null;
|
|
1068
1095
|
}): Promise<void>;
|
|
1069
1096
|
/** 모달 닫기 요청. 쓰던 내용이 있으면 확인부터 받는다(화면은 그대로 모달). */
|
|
1070
1097
|
closeReport(): "closed" | "confirm";
|
|
@@ -1194,6 +1221,55 @@ interface LassoAdapterOpts {
|
|
|
1194
1221
|
* 400 은 재시도 불가라 모든 제보가 조용히 데드레터로 사라졌다.
|
|
1195
1222
|
* 계약을 바꿀 때는 반드시 실호출로 다시 확인한다(adapters.live.test.ts).
|
|
1196
1223
|
*/
|
|
1224
|
+
/**
|
|
1225
|
+
* 구조화 칸이 따로 없는 값들을 담는 자리 → 서버가 `meta` 로 옮긴다.
|
|
1226
|
+
*
|
|
1227
|
+
* 왜 생겼나: 예전에는 이 값들을 **사람이 읽는 마크다운으로 접어 `comment` 에 넣었다.**
|
|
1228
|
+
* 그런데 라쏘런의 `comment` 는 목록·상세의 **제목 자리**다. 5자짜리 코멘트 하나가
|
|
1229
|
+
* 1,399자 덩어리가 되어 화면에 쏟아졌다(실측). 라쏘런은 구조화 칸이 있는 대상이니
|
|
1230
|
+
* 접어 넣을 이유가 없다 — 대상별로 담는 방식이 다른 게 어댑터를 두는 이유다.
|
|
1231
|
+
*
|
|
1232
|
+
* 이름은 **뷰어가 이미 읽는 키를 그대로** 쓴다(`user`·`device`·`appInfo`·`navPath` …).
|
|
1233
|
+
* 새 이름을 지으면 같은 값이 화면에서 두 군데로 갈린다.
|
|
1234
|
+
*/
|
|
1235
|
+
interface LassoReportContext {
|
|
1236
|
+
/** 제보 종류(`report`=화면 전체, `annotation`=요소 지목). 서버는 요소 유무로도 가른다. */
|
|
1237
|
+
kind: string;
|
|
1238
|
+
app: string;
|
|
1239
|
+
platform: string;
|
|
1240
|
+
sessionId: string;
|
|
1241
|
+
clientTimestamp: string;
|
|
1242
|
+
timezone: string | null;
|
|
1243
|
+
user: {
|
|
1244
|
+
id: string | null;
|
|
1245
|
+
name: string | null;
|
|
1246
|
+
email: string | null;
|
|
1247
|
+
role: string | null;
|
|
1248
|
+
isGuest: boolean;
|
|
1249
|
+
} | null;
|
|
1250
|
+
/** 빌드 플러그인이 심은 화면·소스 매핑. 없으면 null. */
|
|
1251
|
+
source: {
|
|
1252
|
+
screenId: string | null;
|
|
1253
|
+
sourceFile: string | null;
|
|
1254
|
+
sourceLine: number | null;
|
|
1255
|
+
} | null;
|
|
1256
|
+
/** 지목한 요소의 나머지 정보(id·속성). 태그·선택자·텍스트는 report.element 에 있다. */
|
|
1257
|
+
element: {
|
|
1258
|
+
id: string | null;
|
|
1259
|
+
attributes: Record<string, string>;
|
|
1260
|
+
} | null;
|
|
1261
|
+
/** 앱 전용. 뷰어의 구현 정보 칸(기기·앱 버전·OTA·탐색 경로)이 이걸 읽는다. */
|
|
1262
|
+
native: {
|
|
1263
|
+
screenPath: string | null;
|
|
1264
|
+
navPath: string[];
|
|
1265
|
+
routeParams: Record<string, unknown> | null;
|
|
1266
|
+
appInfo: Record<string, unknown>;
|
|
1267
|
+
device: Record<string, unknown>;
|
|
1268
|
+
display: Record<string, unknown>;
|
|
1269
|
+
} | null;
|
|
1270
|
+
/** 호스트가 넣은 자유 메타. 우리가 해석하지 않는다. */
|
|
1271
|
+
extra: Record<string, unknown>;
|
|
1272
|
+
}
|
|
1197
1273
|
interface LassoEnvelope {
|
|
1198
1274
|
/** UUID. **Idempotency-Key 헤더와 반드시 같은 값**이어야 서버가 받는다. */
|
|
1199
1275
|
clientSubmissionId: string;
|
|
@@ -1226,7 +1302,16 @@ interface LassoEnvelope {
|
|
|
1226
1302
|
timestamp: number;
|
|
1227
1303
|
};
|
|
1228
1304
|
report?: {
|
|
1305
|
+
/**
|
|
1306
|
+
* **사용자가 쓴 문장 그대로.** 진단을 여기에 접어 넣지 않는다 — 이 값이 곧 제목이다.
|
|
1307
|
+
* 나머지는 구조화 칸(`web`·`network`·`logs`·`element`)과 `context` 로 간다.
|
|
1308
|
+
*/
|
|
1229
1309
|
comment: string;
|
|
1310
|
+
/**
|
|
1311
|
+
* 좌표. **0~100 백분율**이다(코어의 0~1 정규화를 여기서 옮긴다).
|
|
1312
|
+
* 라쏘런의 `x`/`y` 컬럼과 뷰어 오버레이가 백분율을 전제한다 — 0~1 로 보내면
|
|
1313
|
+
* 뷰어의 뷰포트 역산이 "0.12%" 로 읽어 조용히 버리거나 터무니없는 값을 낸다.
|
|
1314
|
+
*/
|
|
1230
1315
|
pin: {
|
|
1231
1316
|
x: number;
|
|
1232
1317
|
y: number;
|
|
@@ -1269,8 +1354,12 @@ interface LassoEnvelope {
|
|
|
1269
1354
|
screenshotContentType: "image/jpeg" | "image/png" | null;
|
|
1270
1355
|
network: unknown[];
|
|
1271
1356
|
logs: unknown[];
|
|
1357
|
+
/** 위 칸에 자리가 없는 값들. 서버가 `meta` 로 펼친다. */
|
|
1358
|
+
context: LassoReportContext;
|
|
1272
1359
|
};
|
|
1273
1360
|
}
|
|
1361
|
+
/** 구조화 칸에 자리가 없는 값들을 모은다. 비어 있어도 키는 남긴다(미수집과 없음을 구분). */
|
|
1362
|
+
declare function buildLassoContext(report: FeedbackReport): LassoReportContext;
|
|
1274
1363
|
/** 제보를 라쏘런 봉투로 바꾼다. 순수 함수라 테스트가 전송 없이 모양만 볼 수 있다. */
|
|
1275
1364
|
declare function buildLassoEnvelope(report: FeedbackReport): LassoEnvelope;
|
|
1276
1365
|
/**
|
|
@@ -1305,4 +1394,4 @@ declare function guestUser(id: string): FeedbackUser;
|
|
|
1305
1394
|
*/
|
|
1306
1395
|
declare function uuidv4(): string;
|
|
1307
1396
|
|
|
1308
|
-
export { type AdapterName, type AppInfo, BACKOFF_BASE_MS, BACKOFF_MAX_MS, type BuildContextOpts, type BuildReportOpts, COMMENT_MAX_CHARS, COMMENT_REQUIRED_MESSAGE, COMMENT_TOO_LONG_MESSAGE, type CaptureWithinLimitOpts, type ConfigWarning, type ConfigWarningCode, type ContextProviders, type ContextUser, DEFAULT_ADAPTER, DEFAULT_INTERNAL_ROLES, DEFAULT_POSITION, type DeviceInfo, type DiagLogEntry, type DiagNetworkEntry, type DiagRequestRef, DiagnosticsCollector, type DiagnosticsExcludeMatcher, type DiagnosticsInstallOpts, type DiagnosticsPayload, type DiagnosticsSource, type DisplayInfo, ENDPOINT_FROM_ADAPTER, type ElementInfo, FLOATING_BUTTON_ID, type FeedbackAdapter, type FeedbackConfig, type FeedbackContext, type FeedbackKind, type FeedbackPin, type FeedbackPriority, FeedbackQueue, type FeedbackQueueOpts, type FeedbackReport, type FeedbackScreenshot, type FeedbackStorage, type FeedbackUser, FocusRing, type GetCurrentScreenFn, type GetUserFn, KEEPALIVE_BODY_LIMIT_BYTES, LASSO_DEFAULT_ENDPOINT, LOG_BUFFER_LIMIT, type LassoAdapterOpts, type LassoEnvelope, type LassoFetch, type LassoRequestInit, type LassoResponseLike, type LinearIssueInput, MAX_LOG_MESSAGE_CHARS, MAX_QUEUE_AGE_MS, MAX_QUEUE_ITEMS, MODAL_ACTION_ATTACH, MODAL_ACTION_CANCEL, MODAL_ACTION_PICK, MODAL_ACTION_PIN, MODAL_ACTION_REMOVE_SCREENSHOT, MODAL_ACTION_RETRY, MODAL_ACTION_SEND, MODAL_FIELD_COMMENT, MODAL_FIELD_PRIORITY, type ModalQueueLike, type ModalSubmitStatus, NETWORK_BUFFER_LIMIT, type NativeContext, type NotionPageInput, PinController, type PixelPoint, type PixelSize, type QueueStatus, type QueueStatusListener, RATE_LIMIT_MIN_DELAY_MS, REDACTED, ReportModalController, type ReportModalListener, type ReportModalOpts, type ReportModalState, type ReportParts, type ResolvedConfig, RingBuffer, SCREENSHOT_FAILED_MESSAGE, SCREENSHOT_MAX_BASE64_BYTES, SCREENSHOT_QUALITY_STEPS, SOURCE_ATTR, SUBMIT_DONE_MESSAGE, SUBMIT_FAILED_MESSAGE, SUBMIT_PENDING_MESSAGE, type ScreenshotCapture, type ScreenshotOutcome, type ScreenshotReencode, type ScreenshotStatus, type SourceLocation, type SourceMapping, type SubmitOutcome, type SubmitResult, TITLE_MAX_CHARS, type Visibility, type VisibilityEnv, type VisibilityFn, type WebContext, type WebContextInput, WidgetController, type WidgetControllerOpts, type WidgetCorner, type WidgetListener, type WidgetPlatform, type WidgetPosition, type WidgetScreen, type WidgetState, buildContext, buildLassoEnvelope, buildLinearIssue, buildNotionPage, buildReport, buildTitle, byteLengthOf, canUseKeepalive, captureWithinLimit, createLassoAdapter, defaultBackoff, denormalizePin, detectDevBuild, diagnosticsProviderFor, getOrCreateGuestId, guestUser, isInternalUser, normalizePin, normalizeUser, parseSourceAttr, renderReportBody, resolveConfig, resolveContextUser, sanitizeUrl, screenshotBytes, sharedDiagnostics, shouldShowWidget, sourceFromElement, uuidv4 };
|
|
1397
|
+
export { type AdapterName, type AppInfo, BACKOFF_BASE_MS, BACKOFF_MAX_MS, type BuildContextOpts, type BuildReportOpts, COMMENT_MAX_CHARS, COMMENT_REQUIRED_MESSAGE, COMMENT_TOO_LONG_MESSAGE, type CaptureWithinLimitOpts, type ConfigWarning, type ConfigWarningCode, type ContextProviders, type ContextUser, DEFAULT_ADAPTER, DEFAULT_INTERNAL_ROLES, DEFAULT_POSITION, type DeviceInfo, type DiagLogEntry, type DiagNetworkEntry, type DiagRequestRef, DiagnosticsCollector, type DiagnosticsExcludeMatcher, type DiagnosticsInstallOpts, type DiagnosticsPayload, type DiagnosticsSource, type DisplayInfo, ENDPOINT_FROM_ADAPTER, type ElementInfo, FLOATING_BUTTON_ID, type FeedbackAdapter, type FeedbackConfig, type FeedbackContext, type FeedbackKind, type FeedbackPin, type FeedbackPriority, FeedbackQueue, type FeedbackQueueOpts, type FeedbackReport, type FeedbackScreenshot, type FeedbackStorage, type FeedbackUser, FocusRing, type GetCurrentScreenFn, type GetUserFn, KEEPALIVE_BODY_LIMIT_BYTES, LASSO_DEFAULT_ENDPOINT, LOG_BUFFER_LIMIT, type LassoAdapterOpts, type LassoEnvelope, type LassoFetch, type LassoReportContext, type LassoRequestInit, type LassoResponseLike, type LinearIssueInput, MAX_LOG_MESSAGE_CHARS, MAX_QUEUE_AGE_MS, MAX_QUEUE_ITEMS, MODAL_ACTION_ATTACH, MODAL_ACTION_CANCEL, MODAL_ACTION_PICK, MODAL_ACTION_PIN, MODAL_ACTION_REMOVE_SCREENSHOT, MODAL_ACTION_RETRY, MODAL_ACTION_SEND, MODAL_FIELD_COMMENT, MODAL_FIELD_PRIORITY, type ModalQueueLike, type ModalSubmitStatus, NETWORK_BUFFER_LIMIT, type NativeContext, type NotionPageInput, PinController, type PixelPoint, type PixelSize, type QueueStatus, type QueueStatusListener, RATE_LIMIT_MIN_DELAY_MS, REDACTED, ReportModalController, type ReportModalListener, type ReportModalOpts, type ReportModalState, type ReportParts, type ResolvedConfig, RingBuffer, SCREENSHOT_FAILED_MESSAGE, SCREENSHOT_MAX_BASE64_BYTES, SCREENSHOT_QUALITY_STEPS, SOURCE_ATTR, SUBMIT_DONE_MESSAGE, SUBMIT_FAILED_MESSAGE, SUBMIT_PENDING_MESSAGE, type ScreenshotCapture, type ScreenshotOutcome, type ScreenshotReencode, type ScreenshotStatus, type SourceLocation, type SourceMapping, type SubmitOutcome, type SubmitResult, TITLE_MAX_CHARS, type Visibility, type VisibilityEnv, type VisibilityFn, type WebContext, type WebContextInput, WidgetController, type WidgetControllerOpts, type WidgetCorner, type WidgetListener, type WidgetPlatform, type WidgetPosition, type WidgetScreen, type WidgetState, buildContext, buildLassoContext, buildLassoEnvelope, buildLinearIssue, buildNotionPage, buildReport, buildTitle, byteLengthOf, canUseKeepalive, captureWithinLimit, createLassoAdapter, defaultBackoff, denormalizePin, detectDevBuild, diagnosticsProviderFor, getOrCreateGuestId, guestUser, isInternalUser, normalizePin, normalizeUser, parseSourceAttr, renderReportBody, resolveConfig, resolveContextUser, sanitizeUrl, screenshotBytes, sharedDiagnostics, shouldShowWidget, sourceFromElement, uuidv4 };
|
package/dist/index.js
CHANGED
|
@@ -1358,6 +1358,8 @@ var ReportModalController = class {
|
|
|
1358
1358
|
this.lastReport = null;
|
|
1359
1359
|
/** 이전 open/recapture가 늦게 끝나 최신 상태를 덮지 못하게 하는 세대 번호. */
|
|
1360
1360
|
this.captureGeneration = 0;
|
|
1361
|
+
/** 지금 들고 있는 핀의 기준. 핀이 없으면 의미 없다(기본 `picture`). */
|
|
1362
|
+
this.pinAnchor = "picture";
|
|
1361
1363
|
this.state = {
|
|
1362
1364
|
open: false,
|
|
1363
1365
|
element: null,
|
|
@@ -1426,12 +1428,18 @@ var ReportModalController = class {
|
|
|
1426
1428
|
this.queue.stop?.();
|
|
1427
1429
|
}
|
|
1428
1430
|
// ── 열기/닫기 ─────────────────────────────────────────────────────────────
|
|
1431
|
+
/**
|
|
1432
|
+
* @param opts.pin 요소를 지목해서 열 때의 **클릭 지점**(뷰포트 기준 0~1).
|
|
1433
|
+
* 여기로 받아야 뒤늦게 끝나는 자동 캡처가 좌표를 지우지 않는다 — `setPin` 으로
|
|
1434
|
+
* 따로 넣으면 캡처와 경쟁해서 진다(그 경쟁에 계속 져서 좌표가 전부 유실됐다).
|
|
1435
|
+
*/
|
|
1429
1436
|
async open(opts = {}) {
|
|
1430
1437
|
if (this.state.open) return;
|
|
1431
1438
|
const wasPending = this.state.submitStatus === "pending" && this.lastReport !== null;
|
|
1432
1439
|
const pendingReport = wasPending ? this.lastReport : null;
|
|
1433
1440
|
this.touched = false;
|
|
1434
1441
|
this.submitAttempted = false;
|
|
1442
|
+
this.pinAnchor = opts.pin && !pendingReport ? "viewport" : "picture";
|
|
1435
1443
|
this.patch({
|
|
1436
1444
|
open: true,
|
|
1437
1445
|
comment: pendingReport?.comment ?? "",
|
|
@@ -1441,7 +1449,7 @@ var ReportModalController = class {
|
|
|
1441
1449
|
screenshot: pendingReport?.screenshot ?? null,
|
|
1442
1450
|
screenshotStatus: "none",
|
|
1443
1451
|
screenshotMessage: null,
|
|
1444
|
-
pin: pendingReport?.pin ?? null,
|
|
1452
|
+
pin: pendingReport?.pin ?? opts.pin ?? null,
|
|
1445
1453
|
submitStatus: wasPending ? "pending" : "idle",
|
|
1446
1454
|
submitMessage: wasPending ? SUBMIT_PENDING_MESSAGE : null,
|
|
1447
1455
|
closeConfirmVisible: false,
|
|
@@ -1490,6 +1498,7 @@ var ReportModalController = class {
|
|
|
1490
1498
|
};
|
|
1491
1499
|
this.touched = false;
|
|
1492
1500
|
this.submitAttempted = false;
|
|
1501
|
+
if (!keepDraft) this.pinAnchor = "picture";
|
|
1493
1502
|
this.patch({
|
|
1494
1503
|
open: false,
|
|
1495
1504
|
closeConfirmVisible: false,
|
|
@@ -1509,11 +1518,16 @@ var ReportModalController = class {
|
|
|
1509
1518
|
if (this.isDraftLocked()) return;
|
|
1510
1519
|
this.patch({ priority });
|
|
1511
1520
|
}
|
|
1512
|
-
|
|
1521
|
+
/**
|
|
1522
|
+
* @param anchor 이 좌표의 기준. 기본은 `picture`(그림 위 탭) — 앱 핀 화면이 쓰는 값이다.
|
|
1523
|
+
* 웹에서 페이지를 클릭해 얻은 좌표는 `viewport` 로 넣어야 캡처에 지워지지 않는다.
|
|
1524
|
+
*/
|
|
1525
|
+
setPin(pin, anchor = "picture") {
|
|
1513
1526
|
if (this.isDraftLocked()) return;
|
|
1527
|
+
this.pinAnchor = anchor;
|
|
1514
1528
|
this.patch({ pin });
|
|
1515
1529
|
}
|
|
1516
|
-
/** 스크린샷
|
|
1530
|
+
/** 스크린샷 제거. 그림 위에 찍은 핀은 가리킬 대상이 사라지므로 함께 버린다. */
|
|
1517
1531
|
removeScreenshot() {
|
|
1518
1532
|
if (this.isDraftLocked()) return;
|
|
1519
1533
|
this.captureGeneration += 1;
|
|
@@ -1521,7 +1535,7 @@ var ReportModalController = class {
|
|
|
1521
1535
|
screenshot: null,
|
|
1522
1536
|
screenshotStatus: "none",
|
|
1523
1537
|
screenshotMessage: null,
|
|
1524
|
-
pin: null
|
|
1538
|
+
...this.pinAnchor === "picture" ? { pin: null } : {}
|
|
1525
1539
|
});
|
|
1526
1540
|
}
|
|
1527
1541
|
async recapture() {
|
|
@@ -1554,15 +1568,21 @@ var ReportModalController = class {
|
|
|
1554
1568
|
}
|
|
1555
1569
|
}
|
|
1556
1570
|
applyScreenshot(shot, failed) {
|
|
1571
|
+
const dropPin = this.pinAnchor === "picture" ? { pin: null } : {};
|
|
1557
1572
|
if (shot && !failed) {
|
|
1558
|
-
this.patch({
|
|
1573
|
+
this.patch({
|
|
1574
|
+
screenshot: shot,
|
|
1575
|
+
screenshotStatus: "ready",
|
|
1576
|
+
screenshotMessage: null,
|
|
1577
|
+
...dropPin
|
|
1578
|
+
});
|
|
1559
1579
|
return;
|
|
1560
1580
|
}
|
|
1561
1581
|
this.patch({
|
|
1562
1582
|
screenshot: null,
|
|
1563
1583
|
screenshotStatus: "failed",
|
|
1564
1584
|
screenshotMessage: SCREENSHOT_FAILED_MESSAGE,
|
|
1565
|
-
|
|
1585
|
+
...dropPin
|
|
1566
1586
|
});
|
|
1567
1587
|
}
|
|
1568
1588
|
// ── 포커스 ────────────────────────────────────────────────────────────────
|
|
@@ -1758,6 +1778,8 @@ var WidgetController = class {
|
|
|
1758
1778
|
* @param opts.element 요소를 지목해서 열 때 그 요소. 지목 모드에서도 화면을
|
|
1759
1779
|
* `modal` 로 바꾼다 — `modal.open()` 을 직접 부르면 상태만 열리고 화면은
|
|
1760
1780
|
* `picking` 에 머물러 **모달이 렌더되지 않는다**.
|
|
1781
|
+
* @param opts.pin 지목한 클릭 지점. 열기 인자로 넘겨야 자동 캡처가 좌표를 지우지 않는다
|
|
1782
|
+
* (열고 난 뒤 `modal.setPin` 으로 넣으면 캡처와 경쟁한다).
|
|
1761
1783
|
*/
|
|
1762
1784
|
async openReport(opts = {}) {
|
|
1763
1785
|
this.screen = "modal";
|
|
@@ -2113,6 +2135,47 @@ function idFrom(parsed) {
|
|
|
2113
2135
|
}
|
|
2114
2136
|
return null;
|
|
2115
2137
|
}
|
|
2138
|
+
function toPercent(value) {
|
|
2139
|
+
return Math.round(value * 1e5) / 1e3;
|
|
2140
|
+
}
|
|
2141
|
+
function buildLassoContext(report) {
|
|
2142
|
+
const c = report.context;
|
|
2143
|
+
const el = report.element;
|
|
2144
|
+
const user = c.user;
|
|
2145
|
+
const source = el ? sourceFromElement(el) : c.source;
|
|
2146
|
+
const hasSource = Boolean(source.screenId || source.sourceFile);
|
|
2147
|
+
return {
|
|
2148
|
+
kind: report.kind,
|
|
2149
|
+
app: c.app,
|
|
2150
|
+
platform: c.platform,
|
|
2151
|
+
sessionId: c.sessionId,
|
|
2152
|
+
clientTimestamp: c.clientTimestamp,
|
|
2153
|
+
timezone: c.timezone,
|
|
2154
|
+
user: user ? {
|
|
2155
|
+
id: user.id,
|
|
2156
|
+
name: user.name ?? null,
|
|
2157
|
+
email: user.email,
|
|
2158
|
+
role: user.role ?? null,
|
|
2159
|
+
isGuest: user.isGuest
|
|
2160
|
+
} : null,
|
|
2161
|
+
source: hasSource ? {
|
|
2162
|
+
screenId: source.screenId,
|
|
2163
|
+
sourceFile: source.sourceFile,
|
|
2164
|
+
sourceLine: source.sourceLine ?? null
|
|
2165
|
+
} : null,
|
|
2166
|
+
// 속성이 하나도 없으면 빈 객체를 보내지 않는다 — 화면에 빈 항목이 늘어난다.
|
|
2167
|
+
element: el && (el.id !== null || Object.keys(el.attributes).length > 0) ? { id: el.id, attributes: el.attributes } : null,
|
|
2168
|
+
native: c.native ? {
|
|
2169
|
+
screenPath: c.native.screenPath,
|
|
2170
|
+
navPath: c.native.navPath,
|
|
2171
|
+
routeParams: c.native.routeParams,
|
|
2172
|
+
appInfo: { ...c.native.appInfo },
|
|
2173
|
+
device: { ...c.native.device },
|
|
2174
|
+
display: { ...c.native.display }
|
|
2175
|
+
} : null,
|
|
2176
|
+
extra: c.extra
|
|
2177
|
+
};
|
|
2178
|
+
}
|
|
2116
2179
|
function buildLassoEnvelope(report) {
|
|
2117
2180
|
const c = report.context;
|
|
2118
2181
|
const clientType = c.platform === "native" ? "app" : "web";
|
|
@@ -2128,8 +2191,9 @@ function buildLassoEnvelope(report) {
|
|
|
2128
2191
|
url: c.url ?? (clientType === "app" ? `app://${c.screen ?? "unknown"}` : ""),
|
|
2129
2192
|
priority: report.priority,
|
|
2130
2193
|
report: {
|
|
2131
|
-
|
|
2132
|
-
|
|
2194
|
+
// 서버가 1자 이상을 요구한다. 코멘트 없는 제보를 400 으로 잃지 않게 한 줄을 채운다.
|
|
2195
|
+
comment: report.comment.trim().length > 0 ? report.comment.trim() : "(\uCF54\uBA58\uD2B8 \uC5C6\uC74C)",
|
|
2196
|
+
pin: report.pin ? { x: toPercent(report.pin.x), y: toPercent(report.pin.y) } : null,
|
|
2133
2197
|
sourceFile: el?.attributes["data-feedback-source"] ?? null,
|
|
2134
2198
|
element: el ? {
|
|
2135
2199
|
tag: el.tag,
|
|
@@ -2151,7 +2215,8 @@ function buildLassoEnvelope(report) {
|
|
|
2151
2215
|
screenshotContentType: report.screenshot?.contentType ?? null,
|
|
2152
2216
|
// 서버가 없으면 빈 배열로 취급하지만, 미연동과 "없었음"을 구분해 보낸다.
|
|
2153
2217
|
network: report.context.diagnostics?.network ?? [],
|
|
2154
|
-
logs: report.context.diagnostics?.logs ?? []
|
|
2218
|
+
logs: report.context.diagnostics?.logs ?? [],
|
|
2219
|
+
context: buildLassoContext(report)
|
|
2155
2220
|
}
|
|
2156
2221
|
};
|
|
2157
2222
|
}
|
|
@@ -2285,6 +2350,7 @@ export {
|
|
|
2285
2350
|
TITLE_MAX_CHARS,
|
|
2286
2351
|
WidgetController,
|
|
2287
2352
|
buildContext,
|
|
2353
|
+
buildLassoContext,
|
|
2288
2354
|
buildLassoEnvelope,
|
|
2289
2355
|
buildLinearIssue,
|
|
2290
2356
|
buildNotionPage,
|