@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.cjs
CHANGED
|
@@ -64,6 +64,7 @@ __export(index_exports, {
|
|
|
64
64
|
TITLE_MAX_CHARS: () => TITLE_MAX_CHARS,
|
|
65
65
|
WidgetController: () => WidgetController,
|
|
66
66
|
buildContext: () => buildContext,
|
|
67
|
+
buildLassoContext: () => buildLassoContext,
|
|
67
68
|
buildLassoEnvelope: () => buildLassoEnvelope,
|
|
68
69
|
buildLinearIssue: () => buildLinearIssue,
|
|
69
70
|
buildNotionPage: () => buildNotionPage,
|
|
@@ -1455,6 +1456,8 @@ var ReportModalController = class {
|
|
|
1455
1456
|
this.lastReport = null;
|
|
1456
1457
|
/** 이전 open/recapture가 늦게 끝나 최신 상태를 덮지 못하게 하는 세대 번호. */
|
|
1457
1458
|
this.captureGeneration = 0;
|
|
1459
|
+
/** 지금 들고 있는 핀의 기준. 핀이 없으면 의미 없다(기본 `picture`). */
|
|
1460
|
+
this.pinAnchor = "picture";
|
|
1458
1461
|
this.state = {
|
|
1459
1462
|
open: false,
|
|
1460
1463
|
element: null,
|
|
@@ -1523,12 +1526,18 @@ var ReportModalController = class {
|
|
|
1523
1526
|
this.queue.stop?.();
|
|
1524
1527
|
}
|
|
1525
1528
|
// ── 열기/닫기 ─────────────────────────────────────────────────────────────
|
|
1529
|
+
/**
|
|
1530
|
+
* @param opts.pin 요소를 지목해서 열 때의 **클릭 지점**(뷰포트 기준 0~1).
|
|
1531
|
+
* 여기로 받아야 뒤늦게 끝나는 자동 캡처가 좌표를 지우지 않는다 — `setPin` 으로
|
|
1532
|
+
* 따로 넣으면 캡처와 경쟁해서 진다(그 경쟁에 계속 져서 좌표가 전부 유실됐다).
|
|
1533
|
+
*/
|
|
1526
1534
|
async open(opts = {}) {
|
|
1527
1535
|
if (this.state.open) return;
|
|
1528
1536
|
const wasPending = this.state.submitStatus === "pending" && this.lastReport !== null;
|
|
1529
1537
|
const pendingReport = wasPending ? this.lastReport : null;
|
|
1530
1538
|
this.touched = false;
|
|
1531
1539
|
this.submitAttempted = false;
|
|
1540
|
+
this.pinAnchor = opts.pin && !pendingReport ? "viewport" : "picture";
|
|
1532
1541
|
this.patch({
|
|
1533
1542
|
open: true,
|
|
1534
1543
|
comment: pendingReport?.comment ?? "",
|
|
@@ -1538,7 +1547,7 @@ var ReportModalController = class {
|
|
|
1538
1547
|
screenshot: pendingReport?.screenshot ?? null,
|
|
1539
1548
|
screenshotStatus: "none",
|
|
1540
1549
|
screenshotMessage: null,
|
|
1541
|
-
pin: pendingReport?.pin ?? null,
|
|
1550
|
+
pin: pendingReport?.pin ?? opts.pin ?? null,
|
|
1542
1551
|
submitStatus: wasPending ? "pending" : "idle",
|
|
1543
1552
|
submitMessage: wasPending ? SUBMIT_PENDING_MESSAGE : null,
|
|
1544
1553
|
closeConfirmVisible: false,
|
|
@@ -1587,6 +1596,7 @@ var ReportModalController = class {
|
|
|
1587
1596
|
};
|
|
1588
1597
|
this.touched = false;
|
|
1589
1598
|
this.submitAttempted = false;
|
|
1599
|
+
if (!keepDraft) this.pinAnchor = "picture";
|
|
1590
1600
|
this.patch({
|
|
1591
1601
|
open: false,
|
|
1592
1602
|
closeConfirmVisible: false,
|
|
@@ -1606,11 +1616,16 @@ var ReportModalController = class {
|
|
|
1606
1616
|
if (this.isDraftLocked()) return;
|
|
1607
1617
|
this.patch({ priority });
|
|
1608
1618
|
}
|
|
1609
|
-
|
|
1619
|
+
/**
|
|
1620
|
+
* @param anchor 이 좌표의 기준. 기본은 `picture`(그림 위 탭) — 앱 핀 화면이 쓰는 값이다.
|
|
1621
|
+
* 웹에서 페이지를 클릭해 얻은 좌표는 `viewport` 로 넣어야 캡처에 지워지지 않는다.
|
|
1622
|
+
*/
|
|
1623
|
+
setPin(pin, anchor = "picture") {
|
|
1610
1624
|
if (this.isDraftLocked()) return;
|
|
1625
|
+
this.pinAnchor = anchor;
|
|
1611
1626
|
this.patch({ pin });
|
|
1612
1627
|
}
|
|
1613
|
-
/** 스크린샷
|
|
1628
|
+
/** 스크린샷 제거. 그림 위에 찍은 핀은 가리킬 대상이 사라지므로 함께 버린다. */
|
|
1614
1629
|
removeScreenshot() {
|
|
1615
1630
|
if (this.isDraftLocked()) return;
|
|
1616
1631
|
this.captureGeneration += 1;
|
|
@@ -1618,7 +1633,7 @@ var ReportModalController = class {
|
|
|
1618
1633
|
screenshot: null,
|
|
1619
1634
|
screenshotStatus: "none",
|
|
1620
1635
|
screenshotMessage: null,
|
|
1621
|
-
pin: null
|
|
1636
|
+
...this.pinAnchor === "picture" ? { pin: null } : {}
|
|
1622
1637
|
});
|
|
1623
1638
|
}
|
|
1624
1639
|
async recapture() {
|
|
@@ -1651,15 +1666,21 @@ var ReportModalController = class {
|
|
|
1651
1666
|
}
|
|
1652
1667
|
}
|
|
1653
1668
|
applyScreenshot(shot, failed) {
|
|
1669
|
+
const dropPin = this.pinAnchor === "picture" ? { pin: null } : {};
|
|
1654
1670
|
if (shot && !failed) {
|
|
1655
|
-
this.patch({
|
|
1671
|
+
this.patch({
|
|
1672
|
+
screenshot: shot,
|
|
1673
|
+
screenshotStatus: "ready",
|
|
1674
|
+
screenshotMessage: null,
|
|
1675
|
+
...dropPin
|
|
1676
|
+
});
|
|
1656
1677
|
return;
|
|
1657
1678
|
}
|
|
1658
1679
|
this.patch({
|
|
1659
1680
|
screenshot: null,
|
|
1660
1681
|
screenshotStatus: "failed",
|
|
1661
1682
|
screenshotMessage: SCREENSHOT_FAILED_MESSAGE,
|
|
1662
|
-
|
|
1683
|
+
...dropPin
|
|
1663
1684
|
});
|
|
1664
1685
|
}
|
|
1665
1686
|
// ── 포커스 ────────────────────────────────────────────────────────────────
|
|
@@ -1855,6 +1876,8 @@ var WidgetController = class {
|
|
|
1855
1876
|
* @param opts.element 요소를 지목해서 열 때 그 요소. 지목 모드에서도 화면을
|
|
1856
1877
|
* `modal` 로 바꾼다 — `modal.open()` 을 직접 부르면 상태만 열리고 화면은
|
|
1857
1878
|
* `picking` 에 머물러 **모달이 렌더되지 않는다**.
|
|
1879
|
+
* @param opts.pin 지목한 클릭 지점. 열기 인자로 넘겨야 자동 캡처가 좌표를 지우지 않는다
|
|
1880
|
+
* (열고 난 뒤 `modal.setPin` 으로 넣으면 캡처와 경쟁한다).
|
|
1858
1881
|
*/
|
|
1859
1882
|
async openReport(opts = {}) {
|
|
1860
1883
|
this.screen = "modal";
|
|
@@ -2210,6 +2233,47 @@ function idFrom(parsed) {
|
|
|
2210
2233
|
}
|
|
2211
2234
|
return null;
|
|
2212
2235
|
}
|
|
2236
|
+
function toPercent(value) {
|
|
2237
|
+
return Math.round(value * 1e5) / 1e3;
|
|
2238
|
+
}
|
|
2239
|
+
function buildLassoContext(report) {
|
|
2240
|
+
const c = report.context;
|
|
2241
|
+
const el = report.element;
|
|
2242
|
+
const user = c.user;
|
|
2243
|
+
const source = el ? sourceFromElement(el) : c.source;
|
|
2244
|
+
const hasSource = Boolean(source.screenId || source.sourceFile);
|
|
2245
|
+
return {
|
|
2246
|
+
kind: report.kind,
|
|
2247
|
+
app: c.app,
|
|
2248
|
+
platform: c.platform,
|
|
2249
|
+
sessionId: c.sessionId,
|
|
2250
|
+
clientTimestamp: c.clientTimestamp,
|
|
2251
|
+
timezone: c.timezone,
|
|
2252
|
+
user: user ? {
|
|
2253
|
+
id: user.id,
|
|
2254
|
+
name: user.name ?? null,
|
|
2255
|
+
email: user.email,
|
|
2256
|
+
role: user.role ?? null,
|
|
2257
|
+
isGuest: user.isGuest
|
|
2258
|
+
} : null,
|
|
2259
|
+
source: hasSource ? {
|
|
2260
|
+
screenId: source.screenId,
|
|
2261
|
+
sourceFile: source.sourceFile,
|
|
2262
|
+
sourceLine: source.sourceLine ?? null
|
|
2263
|
+
} : null,
|
|
2264
|
+
// 속성이 하나도 없으면 빈 객체를 보내지 않는다 — 화면에 빈 항목이 늘어난다.
|
|
2265
|
+
element: el && (el.id !== null || Object.keys(el.attributes).length > 0) ? { id: el.id, attributes: el.attributes } : null,
|
|
2266
|
+
native: c.native ? {
|
|
2267
|
+
screenPath: c.native.screenPath,
|
|
2268
|
+
navPath: c.native.navPath,
|
|
2269
|
+
routeParams: c.native.routeParams,
|
|
2270
|
+
appInfo: { ...c.native.appInfo },
|
|
2271
|
+
device: { ...c.native.device },
|
|
2272
|
+
display: { ...c.native.display }
|
|
2273
|
+
} : null,
|
|
2274
|
+
extra: c.extra
|
|
2275
|
+
};
|
|
2276
|
+
}
|
|
2213
2277
|
function buildLassoEnvelope(report) {
|
|
2214
2278
|
const c = report.context;
|
|
2215
2279
|
const clientType = c.platform === "native" ? "app" : "web";
|
|
@@ -2225,8 +2289,9 @@ function buildLassoEnvelope(report) {
|
|
|
2225
2289
|
url: c.url ?? (clientType === "app" ? `app://${c.screen ?? "unknown"}` : ""),
|
|
2226
2290
|
priority: report.priority,
|
|
2227
2291
|
report: {
|
|
2228
|
-
|
|
2229
|
-
|
|
2292
|
+
// 서버가 1자 이상을 요구한다. 코멘트 없는 제보를 400 으로 잃지 않게 한 줄을 채운다.
|
|
2293
|
+
comment: report.comment.trim().length > 0 ? report.comment.trim() : "(\uCF54\uBA58\uD2B8 \uC5C6\uC74C)",
|
|
2294
|
+
pin: report.pin ? { x: toPercent(report.pin.x), y: toPercent(report.pin.y) } : null,
|
|
2230
2295
|
sourceFile: el?.attributes["data-feedback-source"] ?? null,
|
|
2231
2296
|
element: el ? {
|
|
2232
2297
|
tag: el.tag,
|
|
@@ -2248,7 +2313,8 @@ function buildLassoEnvelope(report) {
|
|
|
2248
2313
|
screenshotContentType: report.screenshot?.contentType ?? null,
|
|
2249
2314
|
// 서버가 없으면 빈 배열로 취급하지만, 미연동과 "없었음"을 구분해 보낸다.
|
|
2250
2315
|
network: report.context.diagnostics?.network ?? [],
|
|
2251
|
-
logs: report.context.diagnostics?.logs ?? []
|
|
2316
|
+
logs: report.context.diagnostics?.logs ?? [],
|
|
2317
|
+
context: buildLassoContext(report)
|
|
2252
2318
|
}
|
|
2253
2319
|
};
|
|
2254
2320
|
}
|
|
@@ -2383,6 +2449,7 @@ function createLassoAdapter(opts = {}) {
|
|
|
2383
2449
|
TITLE_MAX_CHARS,
|
|
2384
2450
|
WidgetController,
|
|
2385
2451
|
buildContext,
|
|
2452
|
+
buildLassoContext,
|
|
2386
2453
|
buildLassoEnvelope,
|
|
2387
2454
|
buildLinearIssue,
|
|
2388
2455
|
buildNotionPage,
|