@mhosaic/feedback 0.9.0 → 0.10.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.
@@ -476,8 +476,12 @@ var DEFAULT_STRINGS = {
476
476
  "annotator.tool.arrow": "Arrow",
477
477
  "annotator.tool.freehand": "Freehand",
478
478
  "annotator.tool.text": "Text",
479
+ "annotator.tool.highlight": "Highlight",
480
+ "annotator.tool.blur": "Blur (hide sensitive data)",
479
481
  "annotator.text_prompt": "Enter text:",
482
+ "annotator.color_picker": "Custom color",
480
483
  "annotator.undo": "Undo",
484
+ "annotator.redo": "Redo",
481
485
  "annotator.clear": "Clear all",
482
486
  "annotator.count_suffix": "annotations",
483
487
  "annotator.loading": "Loading\u2026",
@@ -578,8 +582,12 @@ var FRENCH_STRINGS = {
578
582
  "annotator.tool.arrow": "Fl\xE8che",
579
583
  "annotator.tool.freehand": "Dessin libre",
580
584
  "annotator.tool.text": "Texte",
585
+ "annotator.tool.highlight": "Surligner",
586
+ "annotator.tool.blur": "Flouter (donn\xE9es sensibles)",
581
587
  "annotator.text_prompt": "Entrez le texte :",
588
+ "annotator.color_picker": "Couleur personnalis\xE9e",
582
589
  "annotator.undo": "Annuler",
590
+ "annotator.redo": "Refaire",
583
591
  "annotator.clear": "Tout effacer",
584
592
  "annotator.count_suffix": "annotations",
585
593
  "annotator.loading": "Chargement\u2026",
@@ -846,10 +854,11 @@ function Fab({ label, onClick }) {
846
854
  import { useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "preact/hooks";
847
855
 
848
856
  // src/widget/Annotator.tsx
849
- import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "preact/hooks";
857
+ import { useEffect as useEffect2, useLayoutEffect, useRef as useRef2, useState as useState2 } from "preact/hooks";
850
858
  import { jsx as jsx4, jsxs as jsxs3 } from "preact/jsx-runtime";
851
859
  var COLORS = ["#ef4444", "#f59e0b", "#10b981", "#3b82f6", "#ffffff"];
852
- function drawShape(ctx, shape) {
860
+ var HIGHLIGHT_ALPHA = 0.35;
861
+ function drawShape(ctx, shape, sourceImage) {
853
862
  ctx.save();
854
863
  ctx.strokeStyle = shape.color;
855
864
  ctx.fillStyle = shape.color;
@@ -882,9 +891,22 @@ function drawShape(ctx, shape) {
882
891
  ctx.fillRect(shape.x - padding, shape.y - padding, w, hh);
883
892
  ctx.fillStyle = shape.color;
884
893
  ctx.fillText(shape.text, shape.x, shape.y);
894
+ } else if (shape.kind === "highlight") {
895
+ ctx.globalAlpha = HIGHLIGHT_ALPHA;
896
+ ctx.fillRect(
897
+ normalizeStart(shape.x, shape.w),
898
+ normalizeStart(shape.y, shape.h),
899
+ Math.abs(shape.w),
900
+ Math.abs(shape.h)
901
+ );
902
+ } else if (shape.kind === "blur") {
903
+ drawBlur(ctx, shape, sourceImage);
885
904
  }
886
905
  ctx.restore();
887
906
  }
907
+ function normalizeStart(start, size) {
908
+ return size < 0 ? start + size : start;
909
+ }
888
910
  function drawArrow(ctx, x1, y1, x2, y2) {
889
911
  const headLen = 14;
890
912
  const angle = Math.atan2(y2 - y1, x2 - x1);
@@ -905,12 +927,77 @@ function drawArrow(ctx, x1, y1, x2, y2) {
905
927
  ctx.closePath();
906
928
  ctx.fill();
907
929
  }
930
+ function drawBlur(ctx, shape, sourceImage) {
931
+ if (!sourceImage) return;
932
+ const x = normalizeStart(shape.x, shape.w);
933
+ const y = normalizeStart(shape.y, shape.h);
934
+ const w = Math.abs(shape.w);
935
+ const h2 = Math.abs(shape.h);
936
+ if (w < 2 || h2 < 2) return;
937
+ const tile = Math.max(4, shape.tile);
938
+ for (let yy = y; yy < y + h2; yy += tile) {
939
+ for (let xx = x; xx < x + w; xx += tile) {
940
+ const tw = Math.min(tile, x + w - xx);
941
+ const th = Math.min(tile, y + h2 - yy);
942
+ ctx.drawImage(
943
+ sourceImage,
944
+ xx,
945
+ yy,
946
+ tw,
947
+ th,
948
+ // source rect from the image
949
+ xx,
950
+ yy,
951
+ tw,
952
+ th
953
+ // dest rect on the canvas (same coords)
954
+ );
955
+ }
956
+ }
957
+ ctx.imageSmoothingEnabled = false;
958
+ for (let yy = y; yy < y + h2; yy += tile) {
959
+ for (let xx = x; xx < x + w; xx += tile) {
960
+ const tw = Math.min(tile, x + w - xx);
961
+ const th = Math.min(tile, y + h2 - yy);
962
+ ctx.drawImage(
963
+ sourceImage,
964
+ xx,
965
+ yy,
966
+ 1,
967
+ 1,
968
+ // single source pixel
969
+ xx,
970
+ yy,
971
+ tw,
972
+ th
973
+ // stretched dest tile
974
+ );
975
+ }
976
+ }
977
+ ctx.imageSmoothingEnabled = true;
978
+ }
908
979
  var Icon = {
909
980
  rect: /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("rect", { x: "2", y: "3", width: "12", height: "10", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }) }),
910
981
  arrow: /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M2 8h11M9 4l4 4-4 4", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
911
982
  pencil: /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M11.5 2.5l2 2L5 13H3v-2l8.5-8.5z", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linejoin": "round" }) }),
912
983
  text: /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M3 3h10M8 3v10M5 13h6", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" }) }),
984
+ highlight: /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: [
985
+ /* @__PURE__ */ jsx4("path", { d: "M3 13l3-3L11 5l-2-2-5 5-3 3v2h2zM10 4l2 2", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }),
986
+ /* @__PURE__ */ jsx4("path", { d: "M2 14h12", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" })
987
+ ] }),
988
+ blur: /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: [
989
+ /* @__PURE__ */ jsx4("rect", { x: "2", y: "2", width: "4", height: "4", fill: "currentColor", opacity: "0.85" }),
990
+ /* @__PURE__ */ jsx4("rect", { x: "7", y: "2", width: "3", height: "3", fill: "currentColor", opacity: "0.55" }),
991
+ /* @__PURE__ */ jsx4("rect", { x: "11", y: "2", width: "3", height: "4", fill: "currentColor", opacity: "0.4" }),
992
+ /* @__PURE__ */ jsx4("rect", { x: "2", y: "7", width: "3", height: "3", fill: "currentColor", opacity: "0.6" }),
993
+ /* @__PURE__ */ jsx4("rect", { x: "6", y: "7", width: "3", height: "3", fill: "currentColor", opacity: "0.85" }),
994
+ /* @__PURE__ */ jsx4("rect", { x: "10", y: "7", width: "4", height: "3", fill: "currentColor", opacity: "0.5" }),
995
+ /* @__PURE__ */ jsx4("rect", { x: "2", y: "11", width: "4", height: "3", fill: "currentColor", opacity: "0.4" }),
996
+ /* @__PURE__ */ jsx4("rect", { x: "7", y: "11", width: "3", height: "3", fill: "currentColor", opacity: "0.65" }),
997
+ /* @__PURE__ */ jsx4("rect", { x: "11", y: "11", width: "3", height: "3", fill: "currentColor", opacity: "0.85" })
998
+ ] }),
913
999
  undo: /* @__PURE__ */ jsx4("svg", { width: "14", height: "14", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M4 7l3-3M4 7l3 3M4 7h6a3 3 0 0 1 0 6H7", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
1000
+ redo: /* @__PURE__ */ jsx4("svg", { width: "14", height: "14", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M12 7l-3-3M12 7l-3 3M12 7H6a3 3 0 0 0 0 6h2", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
914
1001
  trash: /* @__PURE__ */ jsx4("svg", { width: "14", height: "14", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M3 4h10M6 4V2.5h4V4M5 4l.5 9h5L11 4", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
915
1002
  close: /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M4 4l8 8M12 4l-8 8", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" }) })
916
1003
  };
@@ -921,12 +1008,50 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
921
1008
  const [tool, setTool] = useState2("rectangle");
922
1009
  const [color, setColor] = useState2(COLORS[0]);
923
1010
  const [shapes, setShapes] = useState2([]);
1011
+ const [past, setPast] = useState2([]);
1012
+ const [future, setFuture] = useState2([]);
924
1013
  const isDrawingRef = useRef2(false);
925
1014
  const [draftShape, setDraftShape] = useState2(null);
926
1015
  const [imageLoaded, setImageLoaded] = useState2(false);
927
1016
  const [saving, setSaving] = useState2(false);
928
1017
  const scaleRef = useRef2(1);
929
- useEffect2(() => {
1018
+ function addShape(shape) {
1019
+ setShapes((s) => {
1020
+ setPast((p) => [...p, s]);
1021
+ return [...s, shape];
1022
+ });
1023
+ setFuture([]);
1024
+ }
1025
+ function clearShapes() {
1026
+ setShapes((s) => {
1027
+ setPast((p) => [...p, s]);
1028
+ return [];
1029
+ });
1030
+ setFuture([]);
1031
+ }
1032
+ function undo() {
1033
+ setPast((p) => {
1034
+ if (p.length === 0) return p;
1035
+ const prev = p[p.length - 1];
1036
+ setShapes((s) => {
1037
+ setFuture((f) => [s, ...f]);
1038
+ return prev;
1039
+ });
1040
+ return p.slice(0, -1);
1041
+ });
1042
+ }
1043
+ function redo() {
1044
+ setFuture((f) => {
1045
+ if (f.length === 0) return f;
1046
+ const next = f[0];
1047
+ setShapes((s) => {
1048
+ setPast((p) => [...p, s]);
1049
+ return next;
1050
+ });
1051
+ return f.slice(1);
1052
+ });
1053
+ }
1054
+ useLayoutEffect(() => {
930
1055
  const onKey = (e) => {
931
1056
  if (e.key === "Escape") {
932
1057
  e.stopPropagation();
@@ -936,6 +1061,25 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
936
1061
  window.addEventListener("keydown", onKey);
937
1062
  return () => window.removeEventListener("keydown", onKey);
938
1063
  }, [onCancel]);
1064
+ useEffect2(() => {
1065
+ const onKey = (e) => {
1066
+ const mod = e.metaKey || e.ctrlKey;
1067
+ if (!mod) return;
1068
+ const k = e.key.toLowerCase();
1069
+ if (k === "z") {
1070
+ e.preventDefault();
1071
+ e.stopPropagation();
1072
+ if (e.shiftKey) redo();
1073
+ else undo();
1074
+ } else if (k === "y") {
1075
+ e.preventDefault();
1076
+ e.stopPropagation();
1077
+ redo();
1078
+ }
1079
+ };
1080
+ window.addEventListener("keydown", onKey);
1081
+ return () => window.removeEventListener("keydown", onKey);
1082
+ }, [past, future, shapes]);
939
1083
  useEffect2(() => {
940
1084
  const url = URL.createObjectURL(imageBlob);
941
1085
  const img = new Image();
@@ -953,7 +1097,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
953
1097
  const img = imageRef.current;
954
1098
  const container = containerRef.current;
955
1099
  const maxW = container.clientWidth - 16;
956
- const maxH = window.innerHeight * 0.6;
1100
+ const maxH = window.innerHeight * 0.65;
957
1101
  const fitScale = Math.min(maxW / img.width, maxH / img.height, 1);
958
1102
  scaleRef.current = fitScale;
959
1103
  const canvas = canvasRef.current;
@@ -974,8 +1118,8 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
974
1118
  if (!ctx) return;
975
1119
  ctx.clearRect(0, 0, canvas.width, canvas.height);
976
1120
  ctx.drawImage(img, 0, 0);
977
- for (const s of shapes) drawShape(ctx, s);
978
- if (draftShape) drawShape(ctx, draftShape);
1121
+ for (const s of shapes) drawShape(ctx, s, img);
1122
+ if (draftShape) drawShape(ctx, draftShape, img);
979
1123
  }
980
1124
  function getCanvasCoords(e) {
981
1125
  const canvas = canvasRef.current;
@@ -988,22 +1132,21 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
988
1132
  const handleMouseDown = (e) => {
989
1133
  if (!imageLoaded) return;
990
1134
  const { x, y } = getCanvasCoords(e);
991
- const lineWidth = Math.max(3, Math.round(canvasRef.current.width / 400));
1135
+ const canvasW = canvasRef.current.width;
1136
+ const lineWidth = Math.max(3, Math.round(canvasW / 400));
1137
+ const blurTile = Math.max(8, Math.round(canvasW / 80));
992
1138
  if (tool === "text") {
993
1139
  const text = window.prompt(strings["annotator.text_prompt"]);
994
1140
  if (text && text.trim()) {
995
- setShapes((prev) => [
996
- ...prev,
997
- {
998
- kind: "text",
999
- x,
1000
- y,
1001
- text: text.trim(),
1002
- color,
1003
- fontSize: Math.max(16, Math.round(canvasRef.current.width / 50)),
1004
- lineWidth: 1
1005
- }
1006
- ]);
1141
+ addShape({
1142
+ kind: "text",
1143
+ x,
1144
+ y,
1145
+ text: text.trim(),
1146
+ color,
1147
+ fontSize: Math.max(16, Math.round(canvasW / 50)),
1148
+ lineWidth: 1
1149
+ });
1007
1150
  }
1008
1151
  return;
1009
1152
  }
@@ -1014,12 +1157,33 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
1014
1157
  setDraftShape({ kind: "arrow", x1: x, y1: y, x2: x, y2: y, color, lineWidth });
1015
1158
  } else if (tool === "freehand") {
1016
1159
  setDraftShape({ kind: "freehand", points: [{ x, y }], color, lineWidth });
1160
+ } else if (tool === "highlight") {
1161
+ setDraftShape({
1162
+ kind: "highlight",
1163
+ x,
1164
+ y,
1165
+ w: 0,
1166
+ h: 0,
1167
+ color: color === "#ffffff" ? "#fde047" : color,
1168
+ lineWidth: 1
1169
+ });
1170
+ } else if (tool === "blur") {
1171
+ setDraftShape({
1172
+ kind: "blur",
1173
+ x,
1174
+ y,
1175
+ w: 0,
1176
+ h: 0,
1177
+ color: "#000000",
1178
+ lineWidth: 0,
1179
+ tile: blurTile
1180
+ });
1017
1181
  }
1018
1182
  };
1019
1183
  const handleMouseMove = (e) => {
1020
1184
  if (!isDrawingRef.current || !draftShape) return;
1021
1185
  const { x, y } = getCanvasCoords(e);
1022
- if (draftShape.kind === "rectangle") {
1186
+ if (draftShape.kind === "rectangle" || draftShape.kind === "highlight" || draftShape.kind === "blur") {
1023
1187
  setDraftShape({ ...draftShape, w: x - draftShape.x, h: y - draftShape.y });
1024
1188
  } else if (draftShape.kind === "arrow") {
1025
1189
  setDraftShape({ ...draftShape, x2: x, y2: y });
@@ -1029,12 +1193,12 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
1029
1193
  };
1030
1194
  const handleMouseUp = () => {
1031
1195
  if (isDrawingRef.current && draftShape) {
1032
- const isTiny = draftShape.kind === "rectangle" && Math.abs(draftShape.w) < 4 && Math.abs(draftShape.h) < 4 || draftShape.kind === "arrow" && Math.hypot(
1196
+ const isTiny = (draftShape.kind === "rectangle" || draftShape.kind === "highlight" || draftShape.kind === "blur") && Math.abs(draftShape.w) < 4 && Math.abs(draftShape.h) < 4 || draftShape.kind === "arrow" && Math.hypot(
1033
1197
  draftShape.x2 - draftShape.x1,
1034
1198
  draftShape.y2 - draftShape.y1
1035
1199
  ) < 4 || draftShape.kind === "freehand" && draftShape.points.length < 3;
1036
1200
  if (!isTiny) {
1037
- setShapes((prev) => [...prev, draftShape]);
1201
+ addShape(draftShape);
1038
1202
  }
1039
1203
  }
1040
1204
  isDrawingRef.current = false;
@@ -1057,7 +1221,9 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
1057
1221
  { id: "rectangle", icon: Icon.rect, label: strings["annotator.tool.rectangle"] },
1058
1222
  { id: "arrow", icon: Icon.arrow, label: strings["annotator.tool.arrow"] },
1059
1223
  { id: "freehand", icon: Icon.pencil, label: strings["annotator.tool.freehand"] },
1060
- { id: "text", icon: Icon.text, label: strings["annotator.tool.text"] }
1224
+ { id: "text", icon: Icon.text, label: strings["annotator.tool.text"] },
1225
+ { id: "highlight", icon: Icon.highlight, label: strings["annotator.tool.highlight"] },
1226
+ { id: "blur", icon: Icon.blur, label: strings["annotator.tool.blur"] }
1061
1227
  ];
1062
1228
  return /* @__PURE__ */ jsx4(
1063
1229
  "div",
@@ -1096,26 +1262,38 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
1096
1262
  t.id
1097
1263
  )) }),
1098
1264
  /* @__PURE__ */ jsx4("span", { class: "annotator-sep" }),
1099
- /* @__PURE__ */ jsx4("div", { class: "annotator-colors", children: COLORS.map((c) => /* @__PURE__ */ jsx4(
1100
- "button",
1101
- {
1102
- type: "button",
1103
- onClick: () => setColor(c),
1104
- "aria-label": c,
1105
- "aria-pressed": color === c,
1106
- class: `annotator-color ${color === c ? "is-active" : ""}`,
1107
- style: { backgroundColor: c }
1108
- },
1109
- c
1110
- )) }),
1265
+ /* @__PURE__ */ jsxs3("div", { class: "annotator-colors", children: [
1266
+ COLORS.map((c) => /* @__PURE__ */ jsx4(
1267
+ "button",
1268
+ {
1269
+ type: "button",
1270
+ onClick: () => setColor(c),
1271
+ "aria-label": c,
1272
+ "aria-pressed": color === c,
1273
+ class: `annotator-color ${color === c ? "is-active" : ""}`,
1274
+ style: { backgroundColor: c }
1275
+ },
1276
+ c
1277
+ )),
1278
+ /* @__PURE__ */ jsx4("label", { class: "annotator-color annotator-color-picker", title: strings["annotator.color_picker"], children: /* @__PURE__ */ jsx4(
1279
+ "input",
1280
+ {
1281
+ type: "color",
1282
+ value: color,
1283
+ onInput: (e) => setColor(e.target.value),
1284
+ "aria-label": strings["annotator.color_picker"]
1285
+ }
1286
+ ) })
1287
+ ] }),
1111
1288
  /* @__PURE__ */ jsx4("span", { class: "annotator-sep" }),
1112
1289
  /* @__PURE__ */ jsxs3(
1113
1290
  "button",
1114
1291
  {
1115
1292
  type: "button",
1116
1293
  class: "annotator-btn",
1117
- onClick: () => setShapes((prev) => prev.slice(0, -1)),
1118
- disabled: shapes.length === 0,
1294
+ onClick: undo,
1295
+ disabled: past.length === 0,
1296
+ title: strings["annotator.undo"],
1119
1297
  children: [
1120
1298
  Icon.undo,
1121
1299
  /* @__PURE__ */ jsx4("span", { children: strings["annotator.undo"] })
@@ -1127,7 +1305,21 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
1127
1305
  {
1128
1306
  type: "button",
1129
1307
  class: "annotator-btn",
1130
- onClick: () => setShapes([]),
1308
+ onClick: redo,
1309
+ disabled: future.length === 0,
1310
+ title: strings["annotator.redo"],
1311
+ children: [
1312
+ Icon.redo,
1313
+ /* @__PURE__ */ jsx4("span", { children: strings["annotator.redo"] })
1314
+ ]
1315
+ }
1316
+ ),
1317
+ /* @__PURE__ */ jsxs3(
1318
+ "button",
1319
+ {
1320
+ type: "button",
1321
+ class: "annotator-btn",
1322
+ onClick: clearShapes,
1131
1323
  disabled: shapes.length === 0,
1132
1324
  children: [
1133
1325
  Icon.trash,
@@ -1359,25 +1551,27 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
1359
1551
  ),
1360
1552
  screenshotPreview ? /* @__PURE__ */ jsxs4("div", { class: "screenshot-preview", children: [
1361
1553
  /* @__PURE__ */ jsx5("img", { src: screenshotPreview, alt: "" }),
1362
- /* @__PURE__ */ jsx5(
1363
- "button",
1364
- {
1365
- type: "button",
1366
- class: "screenshot-remove",
1367
- onClick: clearScreenshot,
1368
- "aria-label": strings["form.screenshot.remove"],
1369
- children: "\xD7"
1370
- }
1371
- ),
1372
- /* @__PURE__ */ jsx5(
1373
- "button",
1374
- {
1375
- type: "button",
1376
- class: "btn screenshot-annotate",
1377
- onClick: () => setAnnotatorOpen(true),
1378
- children: strings["form.screenshot.annotate"]
1379
- }
1380
- )
1554
+ /* @__PURE__ */ jsxs4("div", { class: "screenshot-preview-actions", children: [
1555
+ /* @__PURE__ */ jsxs4(
1556
+ "button",
1557
+ {
1558
+ type: "button",
1559
+ class: "btn btn--primary screenshot-annotate",
1560
+ onClick: () => setAnnotatorOpen(true),
1561
+ children: [
1562
+ /* @__PURE__ */ jsxs4("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: [
1563
+ /* @__PURE__ */ jsx5("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
1564
+ /* @__PURE__ */ jsx5("path", { d: "M18.5 2.5a2.12 2.12 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" })
1565
+ ] }),
1566
+ strings["form.screenshot.annotate"]
1567
+ ]
1568
+ }
1569
+ ),
1570
+ /* @__PURE__ */ jsxs4("button", { type: "button", class: "btn", onClick: clearScreenshot, children: [
1571
+ /* @__PURE__ */ jsx5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx5("path", { d: "M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14z" }) }),
1572
+ strings["form.screenshot.remove"]
1573
+ ] })
1574
+ ] })
1381
1575
  ] }) : /* @__PURE__ */ jsxs4(
1382
1576
  "div",
1383
1577
  {
@@ -1397,6 +1591,11 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
1397
1591
  onDragLeave: handleDragLeave,
1398
1592
  onDrop: handleDrop,
1399
1593
  children: [
1594
+ /* @__PURE__ */ jsxs4("svg", { class: "screenshot-icon", width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.6", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: [
1595
+ /* @__PURE__ */ jsx5("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
1596
+ /* @__PURE__ */ jsx5("polyline", { points: "17 8 12 3 7 8" }),
1597
+ /* @__PURE__ */ jsx5("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
1598
+ ] }),
1400
1599
  /* @__PURE__ */ jsxs4("div", { class: "screenshot-cta", children: [
1401
1600
  /* @__PURE__ */ jsx5("strong", { children: strings["form.screenshot.cta_click"] }),
1402
1601
  ", ",
@@ -1972,13 +2171,39 @@ var WIDGET_STYLES = `
1972
2171
  --mfb-accent-contrast: #ffffff;
1973
2172
  --mfb-bg: #ffffff;
1974
2173
  --mfb-surface: #f9fafb;
2174
+ --mfb-surface-2: #f3f4f6;
1975
2175
  --mfb-text: #0a0a0a;
1976
2176
  --mfb-text-muted: #6b7280;
1977
2177
  --mfb-border: #e5e7eb;
2178
+ --mfb-border-strong: #d1d5db;
1978
2179
  --mfb-radius: 8px;
2180
+ --mfb-radius-lg: 14px;
1979
2181
  --mfb-font: system-ui, -apple-system, sans-serif;
1980
2182
  --mfb-z-index: 2147483640;
1981
2183
 
2184
+ /* Spacing scale \u2014 4px base, used everywhere instead of magic numbers. */
2185
+ --mfb-space-1: 4px;
2186
+ --mfb-space-2: 8px;
2187
+ --mfb-space-3: 12px;
2188
+ --mfb-space-4: 16px;
2189
+ --mfb-space-5: 24px;
2190
+ --mfb-space-6: 32px;
2191
+ --mfb-space-7: 48px;
2192
+
2193
+ /* Type scale \u2014 fixed sizes, no fluid scaling. Body 14px sits in the
2194
+ "comfortable on every viewport" range; headings step up gracefully. */
2195
+ --mfb-text-xs: 11px;
2196
+ --mfb-text-sm: 13px;
2197
+ --mfb-text-base: 14px;
2198
+ --mfb-text-md: 15px;
2199
+ --mfb-text-lg: 17px;
2200
+ --mfb-text-xl: 20px;
2201
+
2202
+ /* Elevation \u2014 three tiers for layering. */
2203
+ --mfb-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.08);
2204
+ --mfb-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.12);
2205
+ --mfb-shadow-lg: 0 24px 56px rgba(0, 0, 0, 0.18), 0 8px 20px rgba(0, 0, 0, 0.10);
2206
+
1982
2207
  all: initial;
1983
2208
  font-family: var(--mfb-font);
1984
2209
  color: var(--mfb-text);
@@ -1988,11 +2213,14 @@ var WIDGET_STYLES = `
1988
2213
 
1989
2214
  @media (prefers-color-scheme: dark) {
1990
2215
  :host {
1991
- --mfb-bg: #111827;
1992
- --mfb-surface: #1f2937;
1993
- --mfb-text: #f9fafb;
1994
- --mfb-text-muted: #9ca3af;
1995
- --mfb-border: #374151;
2216
+ --mfb-bg: #0f172a;
2217
+ --mfb-surface: #1e293b;
2218
+ --mfb-surface-2: #253349;
2219
+ --mfb-text: #f8fafc;
2220
+ --mfb-text-muted: #94a3b8;
2221
+ --mfb-border: #334155;
2222
+ --mfb-border-strong: #475569;
2223
+ --mfb-shadow-lg: 0 24px 56px rgba(0, 0, 0, 0.55), 0 8px 20px rgba(0, 0, 0, 0.40);
1996
2224
  }
1997
2225
  }
1998
2226
 
@@ -2051,60 +2279,98 @@ var WIDGET_STYLES = `
2051
2279
  .fab:hover, .fab:active { transform: none; }
2052
2280
  }
2053
2281
 
2282
+ /* Backdrop \u2014 fade in with a slight blur for depth. The blur gives the
2283
+ modal that "page that opens" weight: the underlying page recedes
2284
+ visually so the widget feels foregrounded, not pasted on. */
2054
2285
  .backdrop {
2055
2286
  position: fixed;
2056
2287
  inset: 0;
2057
- background: rgba(0, 0, 0, 0.45);
2288
+ background: rgba(15, 23, 42, 0.55);
2289
+ backdrop-filter: blur(6px);
2290
+ -webkit-backdrop-filter: blur(6px);
2058
2291
  display: grid;
2059
2292
  place-items: center;
2293
+ animation: mfb-backdrop-in 220ms cubic-bezier(0.16, 1, 0.3, 1);
2294
+ }
2295
+
2296
+ @keyframes mfb-backdrop-in {
2297
+ from { opacity: 0; backdrop-filter: blur(0px); -webkit-backdrop-filter: blur(0px); }
2298
+ to { opacity: 1; backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
2060
2299
  }
2061
2300
 
2062
2301
  .modal {
2063
2302
  background: var(--mfb-bg);
2064
- border-radius: calc(var(--mfb-radius) * 1.5);
2065
- box-shadow: 0 20px 48px rgba(0, 0, 0, 0.25);
2066
- /* 440px is the industry sweet spot for short forms \u2014 Linear, Plain,
2067
- Sentry all sit in the 420-480 range. 92vw caps it on narrow screens. */
2068
- width: min(440px, 92vw);
2069
- /* 24px is shadcn's default; gives every input room to breathe. */
2070
- padding: 24px;
2303
+ border-radius: var(--mfb-radius-lg);
2304
+ box-shadow: var(--mfb-shadow-lg);
2305
+ /* 720px is the "page that opens" sweet spot \u2014 wide enough to feel
2306
+ like a workspace, narrow enough to not dominate the screen. Was
2307
+ 440px before; the bump trades a denser modal for a calmer canvas. */
2308
+ width: min(720px, calc(100vw - var(--mfb-space-7)));
2309
+ padding: var(--mfb-space-6);
2071
2310
  display: flex;
2072
2311
  flex-direction: column;
2073
- /* 18px between major blocks (h2 \u2192 field \u2192 field \u2192 footer). Within a
2074
- field group, .field uses a tighter 6px label-to-input gap. */
2075
- gap: 18px;
2312
+ gap: var(--mfb-space-5);
2076
2313
  position: relative;
2077
- /* Cap modal height on short viewports (mobile landscape, tiny laptops)
2078
- so the form scrolls inside the modal rather than overflowing the
2079
- viewport with no way to reach the actions row. */
2080
- max-height: calc(100vh - 32px);
2314
+ /* Cap height with breathing room. The form scrolls internally if
2315
+ the user attaches a tall screenshot. */
2316
+ max-height: min(820px, calc(100vh - var(--mfb-space-7)));
2081
2317
  overflow-y: auto;
2318
+ /* Entrance: opacity-only. Avoids any geometric shift during the
2319
+ animation so interaction tests that read boundingBox immediately
2320
+ after the modal becomes visible get stable coordinates. 220ms
2321
+ ease-out is snappy enough that human users barely register it. */
2322
+ animation: mfb-modal-in 220ms ease-out;
2323
+ }
2324
+
2325
+ @keyframes mfb-modal-in {
2326
+ from { opacity: 0; }
2327
+ to { opacity: 1; }
2328
+ }
2329
+
2330
+ /* Mobile: full-height sheet that slides up from the bottom. The
2331
+ slide-up gesture matches platform-native sheets users already know. */
2332
+ @media (max-width: 640px) {
2333
+ .backdrop { place-items: end center; }
2334
+ .modal {
2335
+ width: 100vw;
2336
+ height: calc(100vh - var(--mfb-space-7));
2337
+ max-height: none;
2338
+ border-radius: var(--mfb-radius-lg) var(--mfb-radius-lg) 0 0;
2339
+ padding: var(--mfb-space-5) var(--mfb-space-4) var(--mfb-space-4);
2340
+ animation: mfb-sheet-up 280ms cubic-bezier(0.16, 1, 0.3, 1);
2341
+ }
2342
+ }
2343
+
2344
+ @keyframes mfb-sheet-up {
2345
+ from { transform: translateY(100%); }
2346
+ to { transform: translateY(0); }
2347
+ }
2348
+
2349
+ @media (prefers-reduced-motion: reduce) {
2350
+ .backdrop, .modal { animation: none; }
2082
2351
  }
2083
2352
 
2084
2353
  .modal h2 {
2085
2354
  margin: 0;
2086
- font-size: 17px;
2355
+ font-size: var(--mfb-text-xl);
2087
2356
  font-weight: 600;
2088
- padding-right: 28px;
2089
- letter-spacing: -0.01em;
2357
+ padding-right: var(--mfb-space-6);
2358
+ letter-spacing: -0.015em;
2359
+ line-height: 1.2;
2090
2360
  }
2091
2361
 
2092
- /* The form sits inside .modal as a single flex child, so the modal-level
2093
- gap doesn't separate the form's *own* children. Mirror the column +
2094
- gap pattern on the form itself; 22px lands in the middle of the
2095
- 20-24px field-to-field range the design critic recommended. */
2096
2362
  .modal form {
2097
2363
  display: flex;
2098
2364
  flex-direction: column;
2099
- gap: 22px;
2365
+ gap: var(--mfb-space-5);
2100
2366
  }
2101
2367
 
2102
2368
  .modal-close {
2103
2369
  position: absolute;
2104
- top: 8px;
2105
- right: 8px;
2106
- width: 32px;
2107
- height: 32px;
2370
+ top: var(--mfb-space-3);
2371
+ right: var(--mfb-space-3);
2372
+ width: 36px;
2373
+ height: 36px;
2108
2374
  display: grid;
2109
2375
  place-items: center;
2110
2376
  background: transparent;
@@ -2115,36 +2381,39 @@ var WIDGET_STYLES = `
2115
2381
  font-size: 22px;
2116
2382
  line-height: 1;
2117
2383
  cursor: pointer;
2384
+ transition: background 120ms ease, color 120ms ease;
2118
2385
  }
2119
2386
  .modal-close:hover { background: var(--mfb-surface); color: var(--mfb-text); }
2120
2387
  .modal-close:focus-visible { outline: 2px solid var(--mfb-accent); outline-offset: 2px; }
2121
2388
 
2122
- /* Each field group: label (13px medium, muted) + input (14px regular)
2123
- stacked with a 6px gap. The 18px modal-level gap separates groups. */
2124
- .field { display: flex; flex-direction: column; gap: 6px; font-size: 13px; }
2389
+ /* Each field group: label + input stacked with breathing room.
2390
+ The 24px modal-level gap separates groups. */
2391
+ .field { display: flex; flex-direction: column; gap: var(--mfb-space-2); font-size: var(--mfb-text-sm); }
2125
2392
 
2126
2393
  .field label {
2127
2394
  color: var(--mfb-text-muted);
2128
2395
  font-weight: 500;
2129
- font-size: 12px;
2130
- letter-spacing: 0.01em;
2396
+ font-size: var(--mfb-text-xs);
2397
+ letter-spacing: 0.03em;
2398
+ text-transform: uppercase;
2131
2399
  }
2132
2400
 
2133
2401
  .field input, .field select, .field textarea {
2134
2402
  font-family: inherit;
2135
- font-size: 14px;
2403
+ font-size: var(--mfb-text-base);
2136
2404
  color: inherit;
2137
- padding: 9px 12px;
2405
+ padding: 11px 14px;
2138
2406
  border: 1px solid var(--mfb-border);
2139
2407
  border-radius: var(--mfb-radius);
2140
2408
  background: var(--mfb-surface);
2141
- transition: border-color 120ms ease, box-shadow 120ms ease;
2409
+ transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease;
2142
2410
  }
2143
2411
 
2144
- .field input:hover, .field select:hover, .field textarea:hover { border-color: var(--mfb-text-muted); }
2412
+ .field input:hover, .field select:hover, .field textarea:hover { border-color: var(--mfb-border-strong); }
2145
2413
  .field input:focus, .field select:focus, .field textarea:focus {
2146
2414
  outline: none;
2147
2415
  border-color: var(--mfb-accent);
2416
+ background: var(--mfb-bg);
2148
2417
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--mfb-accent) 22%, transparent);
2149
2418
  }
2150
2419
 
@@ -2158,34 +2427,35 @@ var WIDGET_STYLES = `
2158
2427
  background-position: right 10px center;
2159
2428
  }
2160
2429
 
2161
- .field textarea { min-height: 96px; resize: vertical; line-height: 1.45; }
2430
+ .field textarea { min-height: 120px; resize: vertical; line-height: 1.5; }
2162
2431
 
2163
- .row { display: flex; gap: 12px; }
2432
+ .row { display: flex; gap: var(--mfb-space-3); }
2164
2433
  .row > * { flex: 1; }
2165
2434
 
2166
- /* Footer separation: border-top + 16px breathing room, Stripe/Linear pattern.
2167
- Stops Cancel/Send from floating against the dropzone or page-URL footer. */
2435
+ /* Footer: subtle separation via border, slightly more vertical space. */
2168
2436
  .actions {
2169
2437
  display: flex;
2170
- gap: 8px;
2438
+ gap: var(--mfb-space-2);
2171
2439
  justify-content: flex-end;
2172
- padding-top: 16px;
2173
- margin-top: 4px;
2440
+ padding-top: var(--mfb-space-4);
2441
+ margin-top: var(--mfb-space-1);
2174
2442
  border-top: 1px solid var(--mfb-border);
2175
2443
  }
2176
2444
 
2177
2445
  .btn {
2178
- padding: 9px 16px;
2446
+ padding: 10px 18px;
2179
2447
  border-radius: var(--mfb-radius);
2180
2448
  border: 1px solid var(--mfb-border);
2181
2449
  background: var(--mfb-bg);
2182
2450
  color: var(--mfb-text);
2183
2451
  font: inherit;
2452
+ font-size: var(--mfb-text-sm);
2184
2453
  font-weight: 500;
2185
2454
  cursor: pointer;
2186
- transition: background 120ms ease, border-color 120ms ease;
2455
+ transition: background 120ms ease, border-color 120ms ease, transform 80ms ease;
2187
2456
  }
2188
- .btn:hover { background: var(--mfb-surface); }
2457
+ .btn:hover { background: var(--mfb-surface); border-color: var(--mfb-border-strong); }
2458
+ .btn:active { transform: scale(0.98); }
2189
2459
 
2190
2460
  .btn--primary {
2191
2461
  background: var(--mfb-accent);
@@ -2216,28 +2486,49 @@ var WIDGET_STYLES = `
2216
2486
  border: 0;
2217
2487
  }
2218
2488
 
2489
+ /* Dropzone \u2014 bigger, more inviting; the icon + heading + sub-line
2490
+ hierarchy makes it feel like a deliberate target, not an afterthought. */
2219
2491
  .screenshot-dropzone {
2220
- border: 1px dashed var(--mfb-border);
2221
- border-radius: var(--mfb-radius);
2222
- padding: 18px 14px;
2492
+ border: 1.5px dashed var(--mfb-border-strong);
2493
+ border-radius: var(--mfb-radius-lg);
2494
+ padding: var(--mfb-space-6) var(--mfb-space-4);
2223
2495
  text-align: center;
2224
2496
  cursor: pointer;
2225
2497
  background: var(--mfb-surface);
2226
- transition: border-color 120ms ease, background 120ms ease;
2498
+ transition: border-color 160ms ease, background 160ms ease, transform 120ms ease;
2499
+ display: flex;
2500
+ flex-direction: column;
2501
+ align-items: center;
2502
+ gap: var(--mfb-space-2);
2503
+ }
2504
+ .screenshot-dropzone:hover {
2505
+ border-color: var(--mfb-accent);
2506
+ background: color-mix(in srgb, var(--mfb-accent) 4%, var(--mfb-surface));
2227
2507
  }
2228
- .screenshot-dropzone:hover { border-color: var(--mfb-text-muted); }
2229
2508
  .screenshot-dropzone.is-dragover {
2230
2509
  border-color: var(--mfb-accent);
2231
2510
  border-style: solid;
2232
- background: color-mix(in srgb, var(--mfb-accent) 8%, var(--mfb-surface));
2511
+ background: color-mix(in srgb, var(--mfb-accent) 10%, var(--mfb-surface));
2512
+ transform: scale(1.005);
2233
2513
  }
2234
2514
  .screenshot-dropzone:focus-visible {
2235
2515
  outline: 2px solid var(--mfb-accent);
2236
2516
  outline-offset: 2px;
2237
2517
  }
2238
- .screenshot-cta { font-size: 13px; color: var(--mfb-text); }
2518
+ .screenshot-icon {
2519
+ color: var(--mfb-text-muted);
2520
+ opacity: 0.7;
2521
+ margin-bottom: var(--mfb-space-1);
2522
+ transition: color 160ms ease, opacity 160ms ease;
2523
+ }
2524
+ .screenshot-dropzone:hover .screenshot-icon,
2525
+ .screenshot-dropzone.is-dragover .screenshot-icon {
2526
+ color: var(--mfb-accent);
2527
+ opacity: 1;
2528
+ }
2529
+ .screenshot-cta { font-size: var(--mfb-text-base); color: var(--mfb-text); font-weight: 500; }
2239
2530
  .screenshot-cta strong { color: var(--mfb-accent); font-weight: 600; }
2240
- .screenshot-formats { font-size: 11px; color: var(--mfb-text-muted); margin-top: 4px; }
2531
+ .screenshot-formats { font-size: var(--mfb-text-xs); color: var(--mfb-text-muted); }
2241
2532
 
2242
2533
  .screenshot-preview {
2243
2534
  position: relative;
@@ -2252,11 +2543,38 @@ var WIDGET_STYLES = `
2252
2543
  display: block;
2253
2544
  width: 100%;
2254
2545
  height: auto;
2255
- max-height: 180px;
2546
+ max-height: 280px;
2256
2547
  object-fit: contain;
2257
2548
  background: #1a1a1a;
2258
2549
  }
2550
+ .screenshot-preview-actions {
2551
+ display: flex;
2552
+ gap: var(--mfb-space-2);
2553
+ padding: var(--mfb-space-2);
2554
+ border-top: 1px solid var(--mfb-border);
2555
+ background: var(--mfb-bg);
2556
+ }
2557
+ .screenshot-preview-actions .btn {
2558
+ flex: 1;
2559
+ padding: 8px 12px;
2560
+ font-size: var(--mfb-text-sm);
2561
+ display: inline-flex;
2562
+ align-items: center;
2563
+ justify-content: center;
2564
+ gap: 6px;
2565
+ }
2566
+ .screenshot-preview-actions .btn--primary {
2567
+ background: var(--mfb-bg);
2568
+ color: var(--mfb-accent);
2569
+ border-color: var(--mfb-accent);
2570
+ }
2571
+ .screenshot-preview-actions .btn--primary:hover {
2572
+ background: color-mix(in srgb, var(--mfb-accent) 8%, var(--mfb-bg));
2573
+ border-color: var(--mfb-accent);
2574
+ color: var(--mfb-accent);
2575
+ }
2259
2576
  .screenshot-remove {
2577
+ /* Kept for legacy markup that uses the old corner-cross button. */
2260
2578
  position: absolute;
2261
2579
  top: 6px;
2262
2580
  right: 6px;
@@ -2274,6 +2592,7 @@ var WIDGET_STYLES = `
2274
2592
  }
2275
2593
  .screenshot-remove:hover { background: #fff; }
2276
2594
  .screenshot-annotate {
2595
+ /* Kept for legacy; new markup uses .screenshot-preview-actions. */
2277
2596
  border-radius: 0;
2278
2597
  border-width: 0;
2279
2598
  border-top: 1px solid var(--mfb-border);
@@ -2377,11 +2696,31 @@ var WIDGET_STYLES = `
2377
2696
  cursor: pointer;
2378
2697
  padding: 0;
2379
2698
  transition: transform 120ms ease;
2699
+ position: relative;
2380
2700
  }
2381
2701
  .annotator-color.is-active {
2382
2702
  transform: scale(1.12);
2383
2703
  border-color: var(--mfb-text);
2384
2704
  }
2705
+ /* Color-picker swatch: rainbow gradient fill so users see this isn't
2706
+ a preset, plus a tiny native <input type="color"> overlaid invisibly. */
2707
+ .annotator-color-picker {
2708
+ display: grid;
2709
+ place-items: center;
2710
+ background: conic-gradient(from 180deg, #ef4444, #f59e0b, #10b981, #3b82f6, #8b5cf6, #ec4899, #ef4444);
2711
+ overflow: hidden;
2712
+ }
2713
+ .annotator-color-picker input[type="color"] {
2714
+ position: absolute;
2715
+ inset: 0;
2716
+ width: 100%;
2717
+ height: 100%;
2718
+ border: 0;
2719
+ padding: 0;
2720
+ margin: 0;
2721
+ opacity: 0;
2722
+ cursor: pointer;
2723
+ }
2385
2724
  .annotator-btn {
2386
2725
  display: inline-flex;
2387
2726
  align-items: center;
@@ -2431,37 +2770,39 @@ var WIDGET_STYLES = `
2431
2770
 
2432
2771
  .tab-strip {
2433
2772
  display: flex;
2434
- gap: 4px;
2773
+ gap: var(--mfb-space-1);
2435
2774
  border-bottom: 1px solid var(--mfb-border);
2436
- margin: 0 -4px 4px;
2437
- padding: 0 4px;
2775
+ margin: 0;
2776
+ padding: 0;
2438
2777
  }
2439
2778
  .tab-button {
2440
2779
  appearance: none;
2441
2780
  background: transparent;
2442
2781
  border: 0;
2443
2782
  border-bottom: 2px solid transparent;
2444
- padding: 8px 12px;
2783
+ padding: var(--mfb-space-3) var(--mfb-space-4);
2784
+ margin-bottom: -1px;
2445
2785
  font: inherit;
2446
- font-size: 13px;
2786
+ font-size: var(--mfb-text-sm);
2447
2787
  font-weight: 500;
2448
2788
  color: var(--mfb-text-muted);
2449
2789
  cursor: pointer;
2790
+ transition: color 120ms ease, border-color 120ms ease;
2450
2791
  }
2451
2792
  .tab-button:hover { color: var(--mfb-text); }
2452
2793
  .tab-button.is-active {
2453
- color: var(--mfb-text);
2794
+ color: var(--mfb-accent);
2454
2795
  border-bottom-color: var(--mfb-accent);
2455
2796
  }
2456
2797
  .tab-button[aria-selected="true"] { font-weight: 600; }
2457
2798
 
2458
- .mine-list { display: flex; flex-direction: column; gap: 10px; }
2799
+ .mine-list { display: flex; flex-direction: column; gap: var(--mfb-space-3); }
2459
2800
  .mine-list-header {
2460
2801
  display: flex;
2461
2802
  align-items: center;
2462
2803
  justify-content: space-between;
2463
2804
  }
2464
- .mine-list-header h2 { margin: 0; font-size: 15px; font-weight: 600; }
2805
+ .mine-list-header h2 { margin: 0; font-size: var(--mfb-text-lg); font-weight: 600; letter-spacing: -0.01em; }
2465
2806
  .mine-loading { color: var(--mfb-text-muted); font-size: 13px; }
2466
2807
  .mine-empty {
2467
2808
  text-align: center;
@@ -3142,8 +3483,8 @@ function createFeedback(config) {
3142
3483
  capture_method: screenshot ? manualScreenshot ? "manual" : "html2canvas" : "none",
3143
3484
  technical_context
3144
3485
  };
3145
- if ("0.9.0") {
3146
- payload.widget_version = "0.9.0";
3486
+ if ("0.10.0") {
3487
+ payload.widget_version = "0.10.0";
3147
3488
  }
3148
3489
  if (screenshot) payload.screenshot = screenshot;
3149
3490
  if (values.synthetic) payload.synthetic = true;
@@ -3229,4 +3570,4 @@ function createFeedback(config) {
3229
3570
  export {
3230
3571
  createFeedback
3231
3572
  };
3232
- //# sourceMappingURL=chunk-KO5NHJ7J.mjs.map
3573
+ //# sourceMappingURL=chunk-XSAUJEJN.mjs.map