@solhun/feedback-kit-web 0.6.1 → 0.7.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
@@ -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;
@@ -167,6 +179,12 @@ declare class ElementPickingController {
167
179
  private readonly rankHintsFor;
168
180
  private readonly listeners;
169
181
  private active;
182
+ /**
183
+ * 일시중지. **저장하지 않는다** — 멈추는 목적이 대개 "링크를 눌러 다른 화면으로 가는 것"
184
+ * 이라, 그 이동이 끝나면 지목이 돌아와 있는 게 목적에 맞다. 같은 이유로 경로가 바뀌면
185
+ * 스스로 풀린다(`syncPath`) — 그래야 SPA 이동과 새로고침이 같게 동작한다.
186
+ */
187
+ private paused;
170
188
  private attached;
171
189
  private hovered;
172
190
  private popup;
@@ -179,7 +197,7 @@ declare class ElementPickingController {
179
197
  /** 지금 도는 자동 캡처. Enter 가 캡처보다 빨랐을 때 기다릴 대상. */
180
198
  private capturing;
181
199
  /**
182
- * 사용자가 [제안 ▾] 를 직접 건드렸는가. 한 번이라도 건드리면 이후 타이핑에 의한 자동
200
+ * 사용자가 [이런 건가요? ▾] 를 직접 건드렸는가. 한 번이라도 건드리면 이후 타이핑에 의한 자동
183
201
  * 접힘/펼침이 멈춘다 — 모달의 `userToggledHints` 와 같은 규칙. 팝업을 새로 열 때마다 리셋된다.
184
202
  */
185
203
  private userToggledHints;
@@ -188,10 +206,23 @@ declare class ElementPickingController {
188
206
  constructor(opts: ElementPickingOpts);
189
207
  getState(): PickingState;
190
208
  get isActive(): boolean;
209
+ /** 켜져 있지만 클릭을 안 먹는 상태인가. */
210
+ get isPaused(): boolean;
191
211
  subscribe(listener: PickingListener): () => void;
192
212
  start(): void;
193
213
  /** [지목 종료]. 저장된 플래그까지 지워서 새로고침해도 다시 켜지지 않게 한다. */
194
214
  stop(): void;
215
+ /**
216
+ * 잠시 멈춘다 — 페이지 클릭이 원래대로 동작하고, 모드는 켜진 채로 남는다.
217
+ *
218
+ * 팝업이 열려 있으면 **그대로 둔다.** 쓰던 한 줄을 여기서 지우면 「지목 켜면 모달 초안이
219
+ * 날아간다」와 같은 사고를 다른 자리에 만드는 셈이다. 팝업은 위젯 자신의 UI 라
220
+ * 리스너를 떼도 계속 눌린다.
221
+ */
222
+ pause(): void;
223
+ /** 다시 지목을 받는다. */
224
+ resume(): void;
225
+ togglePause(): void;
195
226
  /**
196
227
  * 저장돼 있던 모드를 되살린다. 새로고침·페이지 이동 직후에 한 번 부른다.
197
228
  * @returns 되살아났으면 true.
@@ -220,7 +251,7 @@ declare class ElementPickingController {
220
251
  * 아니면 줄바꿈 뒤 이어붙인다 — 모달의 `applyHint` 와 동일한 규칙이다.
221
252
  */
222
253
  applyHint(hintId: string): void;
223
- /** [제안 ▾] 토글. 방향과 무관하게 이후 자동 접힘/펼침을 멈춘다. */
254
+ /** [이런 건가요? ▾] 토글. 방향과 무관하게 이후 자동 접힘/펼침을 멈춘다. */
224
255
  toggleHints(): void;
225
256
  /**
226
257
  * 사용자가 붙여넣기(Cmd+V)나 드래그로 넣은 그림. 자동 캡처 결과를 덮는다.
package/dist/index.d.ts CHANGED
@@ -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;
@@ -167,6 +179,12 @@ declare class ElementPickingController {
167
179
  private readonly rankHintsFor;
168
180
  private readonly listeners;
169
181
  private active;
182
+ /**
183
+ * 일시중지. **저장하지 않는다** — 멈추는 목적이 대개 "링크를 눌러 다른 화면으로 가는 것"
184
+ * 이라, 그 이동이 끝나면 지목이 돌아와 있는 게 목적에 맞다. 같은 이유로 경로가 바뀌면
185
+ * 스스로 풀린다(`syncPath`) — 그래야 SPA 이동과 새로고침이 같게 동작한다.
186
+ */
187
+ private paused;
170
188
  private attached;
171
189
  private hovered;
172
190
  private popup;
@@ -179,7 +197,7 @@ declare class ElementPickingController {
179
197
  /** 지금 도는 자동 캡처. Enter 가 캡처보다 빨랐을 때 기다릴 대상. */
180
198
  private capturing;
181
199
  /**
182
- * 사용자가 [제안 ▾] 를 직접 건드렸는가. 한 번이라도 건드리면 이후 타이핑에 의한 자동
200
+ * 사용자가 [이런 건가요? ▾] 를 직접 건드렸는가. 한 번이라도 건드리면 이후 타이핑에 의한 자동
183
201
  * 접힘/펼침이 멈춘다 — 모달의 `userToggledHints` 와 같은 규칙. 팝업을 새로 열 때마다 리셋된다.
184
202
  */
185
203
  private userToggledHints;
@@ -188,10 +206,23 @@ declare class ElementPickingController {
188
206
  constructor(opts: ElementPickingOpts);
189
207
  getState(): PickingState;
190
208
  get isActive(): boolean;
209
+ /** 켜져 있지만 클릭을 안 먹는 상태인가. */
210
+ get isPaused(): boolean;
191
211
  subscribe(listener: PickingListener): () => void;
192
212
  start(): void;
193
213
  /** [지목 종료]. 저장된 플래그까지 지워서 새로고침해도 다시 켜지지 않게 한다. */
194
214
  stop(): void;
215
+ /**
216
+ * 잠시 멈춘다 — 페이지 클릭이 원래대로 동작하고, 모드는 켜진 채로 남는다.
217
+ *
218
+ * 팝업이 열려 있으면 **그대로 둔다.** 쓰던 한 줄을 여기서 지우면 「지목 켜면 모달 초안이
219
+ * 날아간다」와 같은 사고를 다른 자리에 만드는 셈이다. 팝업은 위젯 자신의 UI 라
220
+ * 리스너를 떼도 계속 눌린다.
221
+ */
222
+ pause(): void;
223
+ /** 다시 지목을 받는다. */
224
+ resume(): void;
225
+ togglePause(): void;
195
226
  /**
196
227
  * 저장돼 있던 모드를 되살린다. 새로고침·페이지 이동 직후에 한 번 부른다.
197
228
  * @returns 되살아났으면 true.
@@ -220,7 +251,7 @@ declare class ElementPickingController {
220
251
  * 아니면 줄바꿈 뒤 이어붙인다 — 모달의 `applyHint` 와 동일한 규칙이다.
221
252
  */
222
253
  applyHint(hintId: string): void;
223
- /** [제안 ▾] 토글. 방향과 무관하게 이후 자동 접힘/펼침을 멈춘다. */
254
+ /** [이런 건가요? ▾] 토글. 방향과 무관하게 이후 자동 접힘/펼침을 멈춘다. */
224
255
  toggleHints(): void;
225
256
  /**
226
257
  * 사용자가 붙여넣기(Cmd+V)나 드래그로 넣은 그림. 자동 캡처 결과를 덮는다.
package/dist/index.js CHANGED
@@ -320,6 +320,12 @@ var ElementPickingController = class {
320
320
  constructor(opts) {
321
321
  this.listeners = /* @__PURE__ */ new Set();
322
322
  this.active = false;
323
+ /**
324
+ * 일시중지. **저장하지 않는다** — 멈추는 목적이 대개 "링크를 눌러 다른 화면으로 가는 것"
325
+ * 이라, 그 이동이 끝나면 지목이 돌아와 있는 게 목적에 맞다. 같은 이유로 경로가 바뀌면
326
+ * 스스로 풀린다(`syncPath`) — 그래야 SPA 이동과 새로고침이 같게 동작한다.
327
+ */
328
+ this.paused = false;
323
329
  this.attached = false;
324
330
  this.hovered = null;
325
331
  this.popup = null;
@@ -331,7 +337,7 @@ var ElementPickingController = class {
331
337
  /** 지금 도는 자동 캡처. Enter 가 캡처보다 빨랐을 때 기다릴 대상. */
332
338
  this.capturing = null;
333
339
  /**
334
- * 사용자가 [제안 ▾] 를 직접 건드렸는가. 한 번이라도 건드리면 이후 타이핑에 의한 자동
340
+ * 사용자가 [이런 건가요? ▾] 를 직접 건드렸는가. 한 번이라도 건드리면 이후 타이핑에 의한 자동
335
341
  * 접힘/펼침이 멈춘다 — 모달의 `userToggledHints` 와 같은 규칙. 팝업을 새로 열 때마다 리셋된다.
336
342
  */
337
343
  this.userToggledHints = false;
@@ -358,6 +364,7 @@ var ElementPickingController = class {
358
364
  getState() {
359
365
  return {
360
366
  active: this.active,
367
+ paused: this.paused,
361
368
  hovered: this.hovered,
362
369
  popup: this.popup,
363
370
  markers: this.markers
@@ -366,6 +373,10 @@ var ElementPickingController = class {
366
373
  get isActive() {
367
374
  return this.active;
368
375
  }
376
+ /** 켜져 있지만 클릭을 안 먹는 상태인가. */
377
+ get isPaused() {
378
+ return this.paused;
379
+ }
369
380
  subscribe(listener) {
370
381
  this.listeners.add(listener);
371
382
  listener(this.getState());
@@ -377,6 +388,7 @@ var ElementPickingController = class {
377
388
  start() {
378
389
  if (this.active) return;
379
390
  this.active = true;
391
+ this.paused = false;
380
392
  this.store.setPickingActive(true);
381
393
  this.attach();
382
394
  this.startPathWatch();
@@ -386,6 +398,7 @@ var ElementPickingController = class {
386
398
  /** [지목 종료]. 저장된 플래그까지 지워서 새로고침해도 다시 켜지지 않게 한다. */
387
399
  stop() {
388
400
  this.active = false;
401
+ this.paused = false;
389
402
  this.store.setPickingActive(false);
390
403
  this.detach();
391
404
  this.stopPathWatch();
@@ -393,6 +406,31 @@ var ElementPickingController = class {
393
406
  this.popup = null;
394
407
  this.emit();
395
408
  }
409
+ /**
410
+ * 잠시 멈춘다 — 페이지 클릭이 원래대로 동작하고, 모드는 켜진 채로 남는다.
411
+ *
412
+ * 팝업이 열려 있으면 **그대로 둔다.** 쓰던 한 줄을 여기서 지우면 「지목 켜면 모달 초안이
413
+ * 날아간다」와 같은 사고를 다른 자리에 만드는 셈이다. 팝업은 위젯 자신의 UI 라
414
+ * 리스너를 떼도 계속 눌린다.
415
+ */
416
+ pause() {
417
+ if (!this.active || this.paused) return;
418
+ this.paused = true;
419
+ this.detach();
420
+ this.hovered = null;
421
+ this.emit();
422
+ }
423
+ /** 다시 지목을 받는다. */
424
+ resume() {
425
+ if (!this.active || !this.paused) return;
426
+ this.paused = false;
427
+ this.attach();
428
+ this.emit();
429
+ }
430
+ togglePause() {
431
+ if (this.paused) this.resume();
432
+ else this.pause();
433
+ }
396
434
  /**
397
435
  * 저장돼 있던 모드를 되살린다. 새로고침·페이지 이동 직후에 한 번 부른다.
398
436
  * @returns 되살아났으면 true.
@@ -408,6 +446,7 @@ var ElementPickingController = class {
408
446
  }
409
447
  /** 경로가 바뀌었을 때 그 경로의 마커로 갈아 끼운다. */
410
448
  syncPath() {
449
+ if (this.paused) this.resume();
411
450
  this.lastPathname = this.getPathname();
412
451
  this.markers = this.store.list(this.lastPathname);
413
452
  this.reconcileMarkerOutcomes();
@@ -436,12 +475,12 @@ var ElementPickingController = class {
436
475
  this.attached = false;
437
476
  }
438
477
  handleMouseOver(event) {
439
- if (!this.active || isOwnUi(event.target)) return;
478
+ if (!this.active || this.paused || isOwnUi(event.target)) return;
440
479
  this.hovered = describeElement(event.target);
441
480
  this.emit();
442
481
  }
443
482
  handleClick(event) {
444
- if (!this.active) return;
483
+ if (!this.active || this.paused) return;
445
484
  if (isOwnUi(event.target)) return;
446
485
  event.preventDefault();
447
486
  event.stopPropagation();
@@ -522,7 +561,7 @@ ${hint.draft}`;
522
561
  };
523
562
  this.emit();
524
563
  }
525
- /** [제안 ▾] 토글. 방향과 무관하게 이후 자동 접힘/펼침을 멈춘다. */
564
+ /** [이런 건가요? ▾] 토글. 방향과 무관하게 이후 자동 접힘/펼침을 멈춘다. */
526
565
  toggleHints() {
527
566
  if (!this.popup) return;
528
567
  this.userToggledHints = true;
@@ -973,7 +1012,7 @@ import {
973
1012
  } from "react";
974
1013
 
975
1014
  // src/version.ts
976
- var VERSION = true ? "0.6.1" : "dev";
1015
+ var VERSION = true ? "0.7.0" : "dev";
977
1016
 
978
1017
  // src/feedback-kit.tsx
979
1018
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -995,6 +1034,7 @@ var OWN_UI_PROPS = { [OWN_UI_ATTR]: "" };
995
1034
  var FONT_STACK = "ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
996
1035
  var INITIAL_PICKING_STATE = {
997
1036
  active: false,
1037
+ paused: false,
998
1038
  hovered: null,
999
1039
  popup: null,
1000
1040
  markers: []
@@ -1148,6 +1188,7 @@ var inputStyle = {
1148
1188
  };
1149
1189
  function PickToggle({
1150
1190
  active,
1191
+ paused,
1151
1192
  disabled,
1152
1193
  variant,
1153
1194
  focusId,
@@ -1175,14 +1216,9 @@ function PickToggle({
1175
1216
  // 항상 값을 준다. 조건부로 빼면 리렌더 때 shorthand(border)와 충돌한다고 React 가 경고한다.
1176
1217
  borderColor: active ? TOKENS.accent : TOKENS.line,
1177
1218
  ...floating ? {
1178
- position: "fixed",
1179
- top: 16,
1180
- left: "50%",
1181
- transform: "translateX(-50%)",
1182
- // 오버레이 자체는 클릭을 통과시킨다(pointerEvents:none) — 이 토글만 되살린다.
1183
- pointerEvents: "auto",
1184
- background: TOKENS.surface,
1185
- boxShadow: TOKENS.shadow
1219
+ // 위치는 감싸는 `PickBar` 가 잡는다 — 옆에 [잠시 멈춤] 이 함께 서야 하는데
1220
+ // 토글이 스스로 fixed 면 둘을 나란히 둘 수 없다.
1221
+ background: TOKENS.surface
1186
1222
  } : {
1187
1223
  width: "100%",
1188
1224
  justifyContent: "space-between",
@@ -1199,8 +1235,12 @@ function PickToggle({
1199
1235
  /* @__PURE__ */ jsx(
1200
1236
  "span",
1201
1237
  {
1202
- style: { fontSize: 12, fontWeight: 600, color: active ? TOKENS.accent : TOKENS.muted },
1203
- children: active ? "\uCF1C\uC9D0" : "\uAEBC\uC9D0"
1238
+ style: {
1239
+ fontSize: 12,
1240
+ fontWeight: 600,
1241
+ color: active && !paused ? TOKENS.accent : TOKENS.muted
1242
+ },
1243
+ children: active ? paused ? "\uC77C\uC2DC\uC911\uC9C0" : "\uCF1C\uC9D0" : "\uAEBC\uC9D0"
1204
1244
  }
1205
1245
  ),
1206
1246
  /* @__PURE__ */ jsx(
@@ -1212,7 +1252,7 @@ function PickToggle({
1212
1252
  width: 38,
1213
1253
  height: 22,
1214
1254
  borderRadius: 11,
1215
- background: active ? TOKENS.accent : TOKENS.line
1255
+ background: active ? paused ? TOKENS.muted : TOKENS.accent : TOKENS.line
1216
1256
  },
1217
1257
  children: /* @__PURE__ */ jsx(
1218
1258
  "span",
@@ -1236,6 +1276,58 @@ function PickToggle({
1236
1276
  }
1237
1277
  );
1238
1278
  }
1279
+ function PickBar({
1280
+ paused,
1281
+ onToggle,
1282
+ onTogglePause
1283
+ }) {
1284
+ return /* @__PURE__ */ jsxs(
1285
+ "div",
1286
+ {
1287
+ style: {
1288
+ position: "fixed",
1289
+ top: 16,
1290
+ left: "50%",
1291
+ transform: "translateX(-50%)",
1292
+ display: "flex",
1293
+ alignItems: "stretch",
1294
+ gap: 8,
1295
+ // 오버레이 자체는 클릭을 통과시킨다(pointerEvents:none) — 이 막대만 되살린다.
1296
+ pointerEvents: "auto",
1297
+ padding: 6,
1298
+ borderRadius: 12,
1299
+ background: TOKENS.surface,
1300
+ boxShadow: TOKENS.shadow
1301
+ },
1302
+ children: [
1303
+ /* @__PURE__ */ jsx(PickToggle, { variant: "floating", active: true, paused, onToggle }),
1304
+ /* @__PURE__ */ jsxs(
1305
+ "button",
1306
+ {
1307
+ type: "button",
1308
+ "aria-pressed": paused,
1309
+ "data-fk-pick-pause": paused ? "paused" : "running",
1310
+ onClick: onTogglePause,
1311
+ style: {
1312
+ ...baseButton,
1313
+ display: "inline-flex",
1314
+ alignItems: "center",
1315
+ gap: 6,
1316
+ whiteSpace: "nowrap",
1317
+ borderColor: paused ? TOKENS.accent : TOKENS.line,
1318
+ color: paused ? TOKENS.accent : TOKENS.ink,
1319
+ fontWeight: 600
1320
+ },
1321
+ children: [
1322
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: paused ? "\u25B6" : "\u23F8" }),
1323
+ paused ? "\uC9C0\uBAA9 \uC7AC\uAC1C" : "\uC7A0\uC2DC \uBA48\uCDA4"
1324
+ ]
1325
+ }
1326
+ )
1327
+ ]
1328
+ }
1329
+ );
1330
+ }
1239
1331
  function MarkerStatus({ status }) {
1240
1332
  const presentation = status === "sending" ? { icon: "\u21BB", text: "\uC804\uC1A1 \uC911", color: TOKENS.accent } : status === "done" ? { icon: "\u2713", text: "\uC644\uB8CC", color: TOKENS.success } : { icon: "\u25F7", text: "\uB300\uAE30", color: TOKENS.warning };
1241
1333
  return /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", gap: 4, alignItems: "center", color: presentation.color }, children: [
@@ -1259,47 +1351,7 @@ function HintChips({
1259
1351
  onToggle
1260
1352
  }) {
1261
1353
  return /* @__PURE__ */ jsxs("div", { style: { marginBottom: 16 }, children: [
1262
- hintsExpanded ? /* @__PURE__ */ jsxs(Fragment, { children: [
1263
- /* @__PURE__ */ jsx("p", { style: { margin: "0 0 7px", color: TOKENS.muted, fontSize: 12 }, children: "\uC774\uB7F0 \uAC74\uAC00\uC694?" }),
1264
- /* @__PURE__ */ jsx(
1265
- "div",
1266
- {
1267
- role: "group",
1268
- "aria-label": "\uC790\uC8FC \uB098\uC624\uB294 \uC81C\uBCF4 \uC81C\uC548",
1269
- style: { display: "flex", flexWrap: "wrap", gap: 6, marginBottom: 7 },
1270
- children: hints.map((hint) => {
1271
- const used = usedHintIds.includes(hint.id);
1272
- return /* @__PURE__ */ jsx(
1273
- "button",
1274
- {
1275
- type: "button",
1276
- "data-fk-focus-id": `hint:${hint.id}`,
1277
- disabled: used || disabled,
1278
- "aria-pressed": used,
1279
- onClick: () => onApply(hint.id),
1280
- style: {
1281
- ...baseButton,
1282
- minHeight: 30,
1283
- padding: "5px 10px",
1284
- fontSize: 12,
1285
- fontWeight: 600,
1286
- borderRadius: 999,
1287
- ...used ? {
1288
- background: TOKENS.subtle,
1289
- color: TOKENS.muted,
1290
- borderColor: TOKENS.line,
1291
- cursor: "default"
1292
- } : {}
1293
- },
1294
- children: hint.label
1295
- },
1296
- hint.id
1297
- );
1298
- })
1299
- }
1300
- )
1301
- ] }) : null,
1302
- /* @__PURE__ */ jsx(
1354
+ /* @__PURE__ */ jsxs(
1303
1355
  "button",
1304
1356
  {
1305
1357
  type: "button",
@@ -1307,10 +1359,61 @@ function HintChips({
1307
1359
  "aria-expanded": hintsExpanded,
1308
1360
  disabled,
1309
1361
  onClick: onToggle,
1310
- style: { ...baseButton, minHeight: 28, padding: "4px 10px", fontSize: 12 },
1311
- children: hintsExpanded ? "\uC81C\uC548 \u25B4" : "\uC81C\uC548 \u25BE"
1362
+ style: {
1363
+ display: "block",
1364
+ margin: "0 0 7px",
1365
+ padding: 0,
1366
+ border: "none",
1367
+ background: "none",
1368
+ font: "inherit",
1369
+ fontSize: 12,
1370
+ color: TOKENS.muted,
1371
+ cursor: disabled ? "default" : "pointer",
1372
+ textAlign: "left"
1373
+ },
1374
+ children: [
1375
+ "\uC774\uB7F0 \uAC74\uAC00\uC694? ",
1376
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: hintsExpanded ? "\u25B4" : "\u25BE" })
1377
+ ]
1312
1378
  }
1313
- )
1379
+ ),
1380
+ hintsExpanded ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
1381
+ "div",
1382
+ {
1383
+ role: "group",
1384
+ "aria-label": "\uC790\uC8FC \uB098\uC624\uB294 \uC81C\uBCF4 \uC81C\uC548",
1385
+ style: { display: "flex", flexWrap: "wrap", gap: 6, marginBottom: 7 },
1386
+ children: hints.map((hint) => {
1387
+ const used = usedHintIds.includes(hint.id);
1388
+ return /* @__PURE__ */ jsx(
1389
+ "button",
1390
+ {
1391
+ type: "button",
1392
+ "data-fk-focus-id": `hint:${hint.id}`,
1393
+ disabled: used || disabled,
1394
+ "aria-pressed": used,
1395
+ onClick: () => onApply(hint.id),
1396
+ style: {
1397
+ ...baseButton,
1398
+ minHeight: 30,
1399
+ padding: "5px 10px",
1400
+ fontSize: 12,
1401
+ fontWeight: 600,
1402
+ borderRadius: 999,
1403
+ ...used ? {
1404
+ background: TOKENS.subtle,
1405
+ color: TOKENS.muted,
1406
+ borderColor: TOKENS.line,
1407
+ cursor: "default"
1408
+ } : {}
1409
+ },
1410
+ children: hint.label
1411
+ },
1412
+ hint.id
1413
+ );
1414
+ })
1415
+ }
1416
+ ) }) : null
1314
1417
  ] });
1315
1418
  }
1316
1419
  function FeedbackKit(props) {
@@ -1629,6 +1732,7 @@ function FeedbackKit(props) {
1629
1732
  {
1630
1733
  variant: "inline",
1631
1734
  active: pickingActive,
1735
+ paused: pickingState.paused,
1632
1736
  disabled: draftLocked,
1633
1737
  focusId: MODAL_ACTION_PICK,
1634
1738
  onToggle: togglePicking
@@ -1880,7 +1984,14 @@ function FeedbackKit(props) {
1880
1984
  }
1881
1985
  }
1882
1986
  ) : null,
1883
- /* @__PURE__ */ jsx(PickToggle, { variant: "floating", active: true, onToggle: togglePicking }),
1987
+ /* @__PURE__ */ jsx(
1988
+ PickBar,
1989
+ {
1990
+ paused: pickingState.paused,
1991
+ onToggle: togglePicking,
1992
+ onTogglePause: () => activeKit.picking.togglePause()
1993
+ }
1994
+ ),
1884
1995
  pickingState.markers.map((marker, index) => /* @__PURE__ */ jsx(
1885
1996
  "div",
1886
1997
  {