@diegotsi/flint-react 0.5.0 → 0.5.1
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 +210 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +211 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -321,7 +321,8 @@ function FlintModal({
|
|
|
321
321
|
getConsoleLogs,
|
|
322
322
|
getNetworkErrors,
|
|
323
323
|
getReplayEvents,
|
|
324
|
-
getExternalReplayUrl
|
|
324
|
+
getExternalReplayUrl,
|
|
325
|
+
initialSelection = ""
|
|
325
326
|
}) {
|
|
326
327
|
const { t } = (0, import_react_i18next.useTranslation)();
|
|
327
328
|
const colors = resolveTheme(theme);
|
|
@@ -334,6 +335,12 @@ function FlintModal({
|
|
|
334
335
|
const [status, setStatus] = (0, import_react2.useState)("idle");
|
|
335
336
|
const [result, setResult] = (0, import_react2.useState)(null);
|
|
336
337
|
const [errorMsg, setErrorMsg] = (0, import_react2.useState)("");
|
|
338
|
+
const [mode, setMode] = (0, import_react2.useState)(initialSelection ? "text" : "bug");
|
|
339
|
+
const [textOriginal, setTextOriginal] = (0, import_react2.useState)(initialSelection);
|
|
340
|
+
const [textSuggested, setTextSuggested] = (0, import_react2.useState)("");
|
|
341
|
+
const [textLang, setTextLang] = (0, import_react2.useState)(
|
|
342
|
+
typeof document !== "undefined" ? document.documentElement.lang?.split("-")[0] || "en" : "en"
|
|
343
|
+
);
|
|
337
344
|
const fileRef = (0, import_react2.useRef)(null);
|
|
338
345
|
const overlayRef = (0, import_react2.useRef)(null);
|
|
339
346
|
(0, import_react2.useEffect)(() => {
|
|
@@ -354,7 +361,12 @@ function FlintModal({
|
|
|
354
361
|
);
|
|
355
362
|
const handleSubmit = async (e) => {
|
|
356
363
|
e.preventDefault();
|
|
357
|
-
|
|
364
|
+
const isText = mode === "text";
|
|
365
|
+
if (isText) {
|
|
366
|
+
if (!textOriginal.trim() || !textSuggested.trim()) return;
|
|
367
|
+
} else {
|
|
368
|
+
if (!description.trim()) return;
|
|
369
|
+
}
|
|
358
370
|
setStatus("submitting");
|
|
359
371
|
setErrorMsg("");
|
|
360
372
|
const collectedMeta = {
|
|
@@ -363,6 +375,13 @@ function FlintModal({
|
|
|
363
375
|
consoleLogs: getConsoleLogs(),
|
|
364
376
|
networkErrors: getNetworkErrors()
|
|
365
377
|
};
|
|
378
|
+
if (isText) {
|
|
379
|
+
collectedMeta.textIssue = {
|
|
380
|
+
original: textOriginal.trim(),
|
|
381
|
+
suggested: textSuggested.trim(),
|
|
382
|
+
lang: textLang
|
|
383
|
+
};
|
|
384
|
+
}
|
|
366
385
|
try {
|
|
367
386
|
const res = await submitReport(
|
|
368
387
|
serverUrl,
|
|
@@ -370,14 +389,15 @@ function FlintModal({
|
|
|
370
389
|
{
|
|
371
390
|
reporterId: user?.id ?? "anonymous",
|
|
372
391
|
reporterName: user?.name ?? "Anonymous",
|
|
373
|
-
description: description.trim(),
|
|
374
|
-
expectedBehavior: expectedBehavior.trim() || void 0,
|
|
392
|
+
description: isText ? `[Text issue] "${textOriginal.trim()}" \u2192 "${textSuggested.trim()}"` : description.trim(),
|
|
393
|
+
expectedBehavior: !isText ? expectedBehavior.trim() || void 0 : void 0,
|
|
375
394
|
externalReplayUrl: getExternalReplayUrl() || void 0,
|
|
376
|
-
severity,
|
|
395
|
+
severity: isText ? "P3" : severity,
|
|
377
396
|
url: window.location.href,
|
|
378
|
-
meta: collectedMeta
|
|
397
|
+
meta: collectedMeta,
|
|
398
|
+
label: isText ? "TEXT" : void 0
|
|
379
399
|
},
|
|
380
|
-
screenshot ?? void 0
|
|
400
|
+
!isText ? screenshot ?? void 0 : void 0
|
|
381
401
|
);
|
|
382
402
|
setResult(res);
|
|
383
403
|
setStatus("success");
|
|
@@ -612,7 +632,88 @@ function FlintModal({
|
|
|
612
632
|
}
|
|
613
633
|
),
|
|
614
634
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("form", { onSubmit: handleSubmit, style: { padding: "20px 24px 24px" }, children: [
|
|
615
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.
|
|
635
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
|
|
636
|
+
display: "flex",
|
|
637
|
+
gap: 4,
|
|
638
|
+
marginBottom: 20,
|
|
639
|
+
background: colors.backgroundSecondary,
|
|
640
|
+
borderRadius: 12,
|
|
641
|
+
padding: 4,
|
|
642
|
+
border: `1px solid ${inputBorder}`
|
|
643
|
+
}, children: ["bug", "text"].map((m) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
644
|
+
"button",
|
|
645
|
+
{
|
|
646
|
+
type: "button",
|
|
647
|
+
onClick: () => setMode(m),
|
|
648
|
+
style: {
|
|
649
|
+
flex: 1,
|
|
650
|
+
padding: "8px 10px",
|
|
651
|
+
borderRadius: 9,
|
|
652
|
+
border: "none",
|
|
653
|
+
cursor: "pointer",
|
|
654
|
+
fontSize: 13,
|
|
655
|
+
fontWeight: mode === m ? 700 : 500,
|
|
656
|
+
fontFamily: "inherit",
|
|
657
|
+
transition: "background 0.15s, color 0.15s",
|
|
658
|
+
background: mode === m ? `linear-gradient(135deg, ${colors.accent}, ${colors.accentHover})` : "transparent",
|
|
659
|
+
color: mode === m ? colors.buttonText : colors.textMuted,
|
|
660
|
+
boxShadow: mode === m ? `0 2px 8px ${colors.accent}30` : "none"
|
|
661
|
+
},
|
|
662
|
+
children: m === "bug" ? "\u{1F41B} Bug" : "\u{1F524} Text / Translation"
|
|
663
|
+
},
|
|
664
|
+
m
|
|
665
|
+
)) }),
|
|
666
|
+
mode === "text" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
667
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: 14 }, children: [
|
|
668
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FieldLabel, { colors, htmlFor: "flint-text-original", children: "Original text" }),
|
|
669
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
670
|
+
"textarea",
|
|
671
|
+
{
|
|
672
|
+
id: "flint-text-original",
|
|
673
|
+
style: { ...inputStyle, resize: "vertical", minHeight: 60 },
|
|
674
|
+
value: textOriginal,
|
|
675
|
+
onChange: (e) => setTextOriginal(e.target.value),
|
|
676
|
+
placeholder: "Text that is wrong on screen\u2026",
|
|
677
|
+
required: true
|
|
678
|
+
}
|
|
679
|
+
)
|
|
680
|
+
] }),
|
|
681
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: 14 }, children: [
|
|
682
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FieldLabel, { colors, htmlFor: "flint-text-suggested", children: "Suggested correction" }),
|
|
683
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
684
|
+
"textarea",
|
|
685
|
+
{
|
|
686
|
+
id: "flint-text-suggested",
|
|
687
|
+
style: { ...inputStyle, resize: "vertical", minHeight: 60 },
|
|
688
|
+
value: textSuggested,
|
|
689
|
+
onChange: (e) => setTextSuggested(e.target.value),
|
|
690
|
+
placeholder: "How it should read\u2026",
|
|
691
|
+
required: true
|
|
692
|
+
}
|
|
693
|
+
)
|
|
694
|
+
] }),
|
|
695
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: 20 }, children: [
|
|
696
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FieldLabel, { colors, htmlFor: "flint-text-lang", children: "Language" }),
|
|
697
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
698
|
+
"select",
|
|
699
|
+
{
|
|
700
|
+
id: "flint-text-lang",
|
|
701
|
+
value: textLang,
|
|
702
|
+
onChange: (e) => setTextLang(e.target.value),
|
|
703
|
+
style: {
|
|
704
|
+
...inputStyle,
|
|
705
|
+
appearance: "none",
|
|
706
|
+
cursor: "pointer"
|
|
707
|
+
},
|
|
708
|
+
children: [
|
|
709
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "en", children: "English (en)" }),
|
|
710
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "he", children: "\u05E2\u05D1\u05E8\u05D9\u05EA (he)" })
|
|
711
|
+
]
|
|
712
|
+
}
|
|
713
|
+
)
|
|
714
|
+
] })
|
|
715
|
+
] }),
|
|
716
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: 18, display: mode === "text" ? "none" : void 0 }, children: [
|
|
616
717
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FieldLabel, { colors, children: t("severityLabel") }),
|
|
617
718
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 8 }, children: SEVERITIES.map((sev) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
618
719
|
SeverityButton,
|
|
@@ -631,7 +732,7 @@ function FlintModal({
|
|
|
631
732
|
sev
|
|
632
733
|
)) })
|
|
633
734
|
] }),
|
|
634
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: 14 }, children: [
|
|
735
|
+
mode === "bug" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: 14 }, children: [
|
|
635
736
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FieldLabel, { colors, htmlFor: "flint-description", children: t("whatIsBrokenLabel") }),
|
|
636
737
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
637
738
|
"textarea",
|
|
@@ -645,7 +746,7 @@ function FlintModal({
|
|
|
645
746
|
}
|
|
646
747
|
)
|
|
647
748
|
] }),
|
|
648
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: 14 }, children: [
|
|
749
|
+
mode === "bug" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: 14 }, children: [
|
|
649
750
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FieldLabel, { colors, htmlFor: "flint-expected", children: t("expectedBehaviorLabel") }),
|
|
650
751
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
651
752
|
"textarea",
|
|
@@ -658,7 +759,7 @@ function FlintModal({
|
|
|
658
759
|
}
|
|
659
760
|
)
|
|
660
761
|
] }),
|
|
661
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: 20 }, children: [
|
|
762
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: 20, display: mode === "text" ? "none" : void 0 }, children: [
|
|
662
763
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FieldLabel, { colors, children: t("screenshotLabel") }),
|
|
663
764
|
screenshot ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10 }, children: [
|
|
664
765
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
@@ -1281,7 +1382,49 @@ function WidgetContent({
|
|
|
1281
1382
|
const { t } = (0, import_react_i18next3.useTranslation)();
|
|
1282
1383
|
const [open, setOpen] = (0, import_react4.useState)(false);
|
|
1283
1384
|
const [hovered, setHovered] = (0, import_react4.useState)(false);
|
|
1385
|
+
const pendingSelection = (0, import_react4.useRef)("");
|
|
1284
1386
|
const colors = resolveTheme(theme);
|
|
1387
|
+
const [selectionTooltip, setSelectionTooltip] = (0, import_react4.useState)(null);
|
|
1388
|
+
const tooltipRef = (0, import_react4.useRef)(null);
|
|
1389
|
+
const triggerRef = (0, import_react4.useRef)(null);
|
|
1390
|
+
const handleMouseUp = (0, import_react4.useCallback)((e) => {
|
|
1391
|
+
const target = e.target;
|
|
1392
|
+
if (tooltipRef.current?.contains(target)) return;
|
|
1393
|
+
if (triggerRef.current?.contains(target)) return;
|
|
1394
|
+
requestAnimationFrame(() => {
|
|
1395
|
+
const sel = window.getSelection();
|
|
1396
|
+
const text = sel?.toString().trim() ?? "";
|
|
1397
|
+
if (text.length < 2) {
|
|
1398
|
+
setSelectionTooltip(null);
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
const range = sel?.getRangeAt(0);
|
|
1402
|
+
if (!range) return;
|
|
1403
|
+
const rect = range.getBoundingClientRect();
|
|
1404
|
+
setSelectionTooltip({
|
|
1405
|
+
text,
|
|
1406
|
+
x: rect.left + rect.width / 2,
|
|
1407
|
+
y: rect.top - 8
|
|
1408
|
+
});
|
|
1409
|
+
});
|
|
1410
|
+
}, []);
|
|
1411
|
+
const handleSelectionChange = (0, import_react4.useCallback)(() => {
|
|
1412
|
+
const text = window.getSelection()?.toString().trim() ?? "";
|
|
1413
|
+
if (text.length < 2) setSelectionTooltip(null);
|
|
1414
|
+
}, []);
|
|
1415
|
+
(0, import_react4.useEffect)(() => {
|
|
1416
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
1417
|
+
document.addEventListener("selectionchange", handleSelectionChange);
|
|
1418
|
+
return () => {
|
|
1419
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
1420
|
+
document.removeEventListener("selectionchange", handleSelectionChange);
|
|
1421
|
+
};
|
|
1422
|
+
}, [handleMouseUp, handleSelectionChange]);
|
|
1423
|
+
const openWithSelection = (text) => {
|
|
1424
|
+
pendingSelection.current = text;
|
|
1425
|
+
setSelectionTooltip(null);
|
|
1426
|
+
setOpen(true);
|
|
1427
|
+
};
|
|
1285
1428
|
const consoleCollector = (0, import_react4.useRef)(null);
|
|
1286
1429
|
const networkCollector = (0, import_react4.useRef)(null);
|
|
1287
1430
|
const replayEvents = (0, import_react4.useRef)([]);
|
|
@@ -1323,6 +1466,10 @@ function WidgetContent({
|
|
|
1323
1466
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1324
1467
|
"button",
|
|
1325
1468
|
{
|
|
1469
|
+
ref: triggerRef,
|
|
1470
|
+
onMouseDown: () => {
|
|
1471
|
+
pendingSelection.current = window.getSelection()?.toString().trim() ?? "";
|
|
1472
|
+
},
|
|
1326
1473
|
onClick: () => setOpen(true),
|
|
1327
1474
|
onMouseEnter: () => setHovered(true),
|
|
1328
1475
|
onMouseLeave: () => setHovered(false),
|
|
@@ -1355,6 +1502,44 @@ function WidgetContent({
|
|
|
1355
1502
|
]
|
|
1356
1503
|
}
|
|
1357
1504
|
),
|
|
1505
|
+
selectionTooltip && !open && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1506
|
+
"button",
|
|
1507
|
+
{
|
|
1508
|
+
ref: tooltipRef,
|
|
1509
|
+
onClick: () => openWithSelection(selectionTooltip.text),
|
|
1510
|
+
style: {
|
|
1511
|
+
position: "fixed",
|
|
1512
|
+
left: Math.max(8, Math.min(selectionTooltip.x - 70, window.innerWidth - 148)),
|
|
1513
|
+
top: Math.max(8, selectionTooltip.y - 36),
|
|
1514
|
+
zIndex: zIndex + 1,
|
|
1515
|
+
display: "flex",
|
|
1516
|
+
alignItems: "center",
|
|
1517
|
+
gap: 6,
|
|
1518
|
+
padding: "6px 12px",
|
|
1519
|
+
borderRadius: 10,
|
|
1520
|
+
border: "none",
|
|
1521
|
+
background: `linear-gradient(135deg, ${colors.accent}, ${colors.accentHover})`,
|
|
1522
|
+
color: colors.buttonText,
|
|
1523
|
+
fontSize: 12,
|
|
1524
|
+
fontWeight: 600,
|
|
1525
|
+
cursor: "pointer",
|
|
1526
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
1527
|
+
boxShadow: `0 4px 20px rgba(0,0,0,0.25), 0 0 12px ${colors.accent}40`,
|
|
1528
|
+
animation: "flint-tooltip-in 0.15s ease-out",
|
|
1529
|
+
whiteSpace: "nowrap"
|
|
1530
|
+
},
|
|
1531
|
+
children: [
|
|
1532
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TextIcon, {}),
|
|
1533
|
+
"Report text issue"
|
|
1534
|
+
]
|
|
1535
|
+
}
|
|
1536
|
+
),
|
|
1537
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: `
|
|
1538
|
+
@keyframes flint-tooltip-in {
|
|
1539
|
+
from { opacity: 0; transform: translateY(4px); }
|
|
1540
|
+
to { opacity: 1; transform: translateY(0); }
|
|
1541
|
+
}
|
|
1542
|
+
` }),
|
|
1358
1543
|
open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1359
1544
|
FlintModal,
|
|
1360
1545
|
{
|
|
@@ -1364,16 +1549,28 @@ function WidgetContent({
|
|
|
1364
1549
|
meta,
|
|
1365
1550
|
theme,
|
|
1366
1551
|
zIndex,
|
|
1367
|
-
onClose: () =>
|
|
1552
|
+
onClose: () => {
|
|
1553
|
+
setOpen(false);
|
|
1554
|
+
pendingSelection.current = "";
|
|
1555
|
+
},
|
|
1368
1556
|
getEnvironment: collectEnvironment,
|
|
1369
1557
|
getConsoleLogs: () => consoleCollector.current?.getEntries() ?? [],
|
|
1370
1558
|
getNetworkErrors: () => networkCollector.current?.getEntries() ?? [],
|
|
1371
1559
|
getReplayEvents: () => [...replayEvents.current],
|
|
1372
|
-
getExternalReplayUrl
|
|
1560
|
+
getExternalReplayUrl,
|
|
1561
|
+
initialSelection: pendingSelection.current
|
|
1373
1562
|
}
|
|
1374
1563
|
)
|
|
1375
1564
|
] });
|
|
1376
1565
|
}
|
|
1566
|
+
function TextIcon() {
|
|
1567
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
1568
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M17 10H3" }),
|
|
1569
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M21 6H3" }),
|
|
1570
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M21 14H3" }),
|
|
1571
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M17 18H3" })
|
|
1572
|
+
] });
|
|
1573
|
+
}
|
|
1377
1574
|
function SparkIcon2() {
|
|
1378
1575
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1379
1576
|
"svg",
|