@diegotsi/flint-react 2.4.6 → 2.5.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.js CHANGED
@@ -213,6 +213,7 @@ function FlintModal({
213
213
  initialSelection = "",
214
214
  enableScreenshot = true,
215
215
  enableTextIssues = true,
216
+ enableFeatureRequests = false,
216
217
  statusPageUrl,
217
218
  onBeforeSubmit,
218
219
  onSuccess,
@@ -248,9 +249,14 @@ function FlintModal({
248
249
  const [status, setStatus] = useState2("idle");
249
250
  const [result, setResult] = useState2(null);
250
251
  const [errorMsg, setErrorMsg] = useState2("");
251
- const [mode, setMode] = useState2(enableTextIssues && initialSelection ? "text" : "bug");
252
+ const hasInitialSelection = enableTextIssues && !!initialSelection;
253
+ const hasMultipleModes = enableTextIssues || enableFeatureRequests;
254
+ const [step, setStep] = useState2(hasInitialSelection || !hasMultipleModes ? "form" : "selector");
255
+ const [mode, setMode] = useState2(hasInitialSelection ? "text" : "bug");
252
256
  const [textOriginal, setTextOriginal] = useState2(initialSelection);
253
257
  const [textSuggested, setTextSuggested] = useState2("");
258
+ const [featureDescription, setFeatureDescription] = useState2("");
259
+ const [featureUseCase, setFeatureUseCase] = useState2("");
254
260
  const [textLang, setTextLang] = useState2(
255
261
  typeof document !== "undefined" ? document.documentElement.lang?.split("-")[0] || "en" : "en"
256
262
  );
@@ -275,8 +281,11 @@ function FlintModal({
275
281
  const handleSubmit = async (e) => {
276
282
  e.preventDefault();
277
283
  const isText = mode === "text";
284
+ const isFeature = mode === "feature";
278
285
  if (isText) {
279
286
  if (!textOriginal.trim() || !textSuggested.trim()) return;
287
+ } else if (isFeature) {
288
+ if (!featureDescription.trim()) return;
280
289
  } else {
281
290
  if (!description.trim()) return;
282
291
  }
@@ -296,6 +305,11 @@ function FlintModal({
296
305
  lang: textLang
297
306
  };
298
307
  }
308
+ if (isFeature) {
309
+ collectedMeta.featureRequest = {
310
+ useCase: featureUseCase.trim()
311
+ };
312
+ }
299
313
  if (needsIdentity) {
300
314
  try {
301
315
  if (reporterName.trim()) localStorage.setItem("flint_reporter_name", reporterName.trim());
@@ -307,14 +321,15 @@ function FlintModal({
307
321
  reporterId: user?.id ?? (reporterEmail.trim() || "anonymous"),
308
322
  reporterName: user?.name ?? (reporterName.trim() || "Anonymous"),
309
323
  reporterEmail: user?.email ?? (reporterEmail.trim() || void 0),
310
- description: isText ? `[Text issue] "${textOriginal.trim()}" \u2192 "${textSuggested.trim()}"` : description.trim(),
311
- expectedBehavior: !isText ? expectedBehavior.trim() || void 0 : void 0,
324
+ description: isFeature ? featureDescription.trim() : isText ? `[Text issue] "${textOriginal.trim()}" \u2192 "${textSuggested.trim()}"` : description.trim(),
325
+ expectedBehavior: !isText && !isFeature ? expectedBehavior.trim() || void 0 : void 0,
312
326
  externalReplayUrl: getExternalReplayUrl() || void 0,
313
- severity: isText ? "P3" : severity,
327
+ severity: isText || isFeature ? "P3" : severity,
314
328
  url: window.location.href,
315
329
  meta: collectedMeta,
316
- label: isText ? "TEXT" : void 0,
317
- source: isText ? "text_issue" : "widget"
330
+ label: isText ? "TEXT" : isFeature ? "FEATURE" : void 0,
331
+ source: isFeature ? "feature_request" : isText ? "text_issue" : "widget",
332
+ type: isFeature ? "FEATURE_REQUEST" : "BUG"
318
333
  };
319
334
  if (onBeforeSubmit) {
320
335
  const proceed = await onBeforeSubmit(payload);
@@ -324,7 +339,12 @@ function FlintModal({
324
339
  }
325
340
  }
326
341
  try {
327
- const res = await submitReport(serverUrl, projectKey, payload, !isText ? screenshot ?? void 0 : void 0);
342
+ const res = await submitReport(
343
+ serverUrl,
344
+ projectKey,
345
+ payload,
346
+ !isText && !isFeature ? screenshot ?? void 0 : void 0
347
+ );
328
348
  setResult(res);
329
349
  setStatus("success");
330
350
  onSuccess?.(res);
@@ -653,365 +673,470 @@ function FlintModal({
653
673
  }
654
674
  ),
655
675
  /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, style: { padding: "20px 24px 24px" }, children: [
656
- enableTextIssues && /* @__PURE__ */ jsx2(
657
- "div",
658
- {
659
- style: {
660
- display: "flex",
661
- gap: 4,
662
- marginBottom: 20,
663
- background: colors.backgroundSecondary,
664
- borderRadius: 12,
665
- padding: 4,
666
- border: `1px solid ${inputBorder}`
667
- },
668
- children: ["bug", "text"].map((m) => /* @__PURE__ */ jsx2(
669
- "button",
670
- {
671
- type: "button",
672
- onClick: () => setMode(m),
673
- style: {
674
- flex: 1,
675
- padding: "8px 10px",
676
- borderRadius: 9,
677
- border: "none",
678
- cursor: "pointer",
679
- fontSize: 13,
680
- fontWeight: mode === m ? 700 : 500,
681
- fontFamily: "inherit",
682
- transition: "background 0.15s, color 0.15s",
683
- background: mode === m ? `linear-gradient(135deg, ${colors.accent}, ${colors.accentHover})` : "transparent",
684
- color: mode === m ? colors.buttonText : colors.textMuted,
685
- boxShadow: mode === m ? `0 2px 8px ${colors.accent}30` : "none"
686
- },
687
- children: m === "bug" ? "\u{1F41B} Bug" : "\u{1F524} Text / Translation"
688
- },
689
- m
690
- ))
691
- }
692
- ),
693
- mode === "text" && /* @__PURE__ */ jsxs2(Fragment, { children: [
694
- /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 14 }, children: [
695
- /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-text-original", children: "Original text" }),
696
- /* @__PURE__ */ jsx2(
697
- "textarea",
698
- {
699
- id: "flint-text-original",
700
- style: { ...inputStyle, resize: "vertical", minHeight: 60 },
701
- value: textOriginal,
702
- onChange: (e) => setTextOriginal(e.target.value),
703
- placeholder: "Text that is wrong on screen\u2026",
704
- required: true
705
- }
706
- )
676
+ step === "selector" && /* @__PURE__ */ jsxs2(Fragment, { children: [
677
+ needsIdentity && /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 10, marginBottom: 18 }, children: [
678
+ /* @__PURE__ */ jsxs2("div", { style: { flex: 1 }, children: [
679
+ /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-reporter-name", children: t("nameLabel", "Your name") }),
680
+ /* @__PURE__ */ jsx2(
681
+ "input",
682
+ {
683
+ id: "flint-reporter-name",
684
+ type: "text",
685
+ style: inputStyle,
686
+ value: reporterName,
687
+ onChange: (e) => setReporterName(e.target.value),
688
+ placeholder: "Jane Doe"
689
+ }
690
+ )
691
+ ] }),
692
+ /* @__PURE__ */ jsxs2("div", { style: { flex: 1 }, children: [
693
+ /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-reporter-email", children: t("emailLabel", "Email") }),
694
+ /* @__PURE__ */ jsx2(
695
+ "input",
696
+ {
697
+ id: "flint-reporter-email",
698
+ type: "email",
699
+ style: inputStyle,
700
+ value: reporterEmail,
701
+ onChange: (e) => setReporterEmail(e.target.value),
702
+ placeholder: "jane@company.com"
703
+ }
704
+ )
705
+ ] })
707
706
  ] }),
708
- /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 14 }, children: [
709
- /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-text-suggested", children: "Suggested correction" }),
707
+ /* @__PURE__ */ jsx2(FieldLabel, { colors, children: "What would you like to report?" }),
708
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", gap: 8, marginBottom: 16 }, children: [
710
709
  /* @__PURE__ */ jsx2(
711
- "textarea",
710
+ TypeSelectorButton,
712
711
  {
713
- id: "flint-text-suggested",
714
- style: { ...inputStyle, resize: "vertical", minHeight: 60 },
715
- value: textSuggested,
716
- onChange: (e) => setTextSuggested(e.target.value),
717
- placeholder: "How it should read\u2026",
718
- required: true
712
+ emoji: "\u{1F41B}",
713
+ title: "Bug Report",
714
+ subtitle: "Something isn't working",
715
+ colors,
716
+ inputBorder,
717
+ onClick: () => {
718
+ setMode("bug");
719
+ setStep("form");
720
+ }
719
721
  }
720
- )
721
- ] }),
722
- /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 20 }, children: [
723
- /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-text-lang", children: "Language" }),
724
- /* @__PURE__ */ jsxs2(
725
- "select",
722
+ ),
723
+ enableFeatureRequests && /* @__PURE__ */ jsx2(
724
+ TypeSelectorButton,
726
725
  {
727
- id: "flint-text-lang",
728
- value: textLang,
729
- onChange: (e) => setTextLang(e.target.value),
730
- style: {
731
- ...inputStyle,
732
- appearance: "none",
733
- cursor: "pointer"
734
- },
735
- children: [
736
- /* @__PURE__ */ jsx2("option", { value: "en", children: "English (en)" }),
737
- /* @__PURE__ */ jsx2("option", { value: "he", children: "\u05E2\u05D1\u05E8\u05D9\u05EA (he)" })
738
- ]
726
+ emoji: "\u{1F4A1}",
727
+ title: "Feature Request / Improvement",
728
+ subtitle: "I have an idea or suggestion",
729
+ colors,
730
+ inputBorder,
731
+ onClick: () => {
732
+ setMode("feature");
733
+ setStep("form");
734
+ }
739
735
  }
740
- )
741
- ] })
742
- ] }),
743
- needsIdentity && /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 10, marginBottom: 14 }, children: [
744
- /* @__PURE__ */ jsxs2("div", { style: { flex: 1 }, children: [
745
- /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-reporter-name", children: t("nameLabel", "Your name") }),
746
- /* @__PURE__ */ jsx2(
747
- "input",
736
+ ),
737
+ enableTextIssues && /* @__PURE__ */ jsx2(
738
+ TypeSelectorButton,
748
739
  {
749
- id: "flint-reporter-name",
750
- type: "text",
751
- style: inputStyle,
752
- value: reporterName,
753
- onChange: (e) => setReporterName(e.target.value),
754
- placeholder: "Jane Doe"
740
+ emoji: "\u270F\uFE0F",
741
+ title: "Text Issue",
742
+ subtitle: "There's a text or translation problem",
743
+ colors,
744
+ inputBorder,
745
+ onClick: () => {
746
+ setMode("text");
747
+ setStep("form");
748
+ }
755
749
  }
756
750
  )
757
751
  ] }),
758
- /* @__PURE__ */ jsxs2("div", { style: { flex: 1 }, children: [
759
- /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-reporter-email", children: t("emailLabel", "Email") }),
760
- /* @__PURE__ */ jsx2(
761
- "input",
762
- {
763
- id: "flint-reporter-email",
764
- type: "email",
765
- style: inputStyle,
766
- value: reporterEmail,
767
- onChange: (e) => setReporterEmail(e.target.value),
768
- placeholder: "jane@company.com"
769
- }
770
- )
771
- ] })
772
- ] }),
773
- /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 18, display: mode === "text" ? "none" : void 0 }, children: [
774
- /* @__PURE__ */ jsx2(FieldLabel, { colors, children: t("severityLabel") }),
775
- /* @__PURE__ */ jsx2("div", { style: { display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 8 }, children: SEVERITIES.map((sev) => /* @__PURE__ */ jsx2(
776
- SeverityButton,
777
- {
778
- sev,
779
- label: t(`severity_${sev}_label`),
780
- selected: severity === sev,
781
- hint: t(`severity_${sev}_hint`),
782
- color: SEV_COLOR[sev],
783
- accent: colors.accent,
784
- border: inputBorder,
785
- bg: colors.backgroundSecondary,
786
- text: colors.text,
787
- onClick: () => setSeverity(sev)
788
- },
789
- sev
790
- )) })
791
- ] }),
792
- mode === "bug" && /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 14 }, children: [
793
- /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-description", children: t("whatIsBrokenLabel") }),
794
752
  /* @__PURE__ */ jsx2(
795
- "textarea",
753
+ "button",
796
754
  {
797
- id: "flint-description",
798
- style: { ...inputStyle, resize: "vertical", minHeight: 80 },
799
- value: description,
800
- onChange: (e) => setDescription(e.target.value),
801
- placeholder: t("whatIsBrokenPlaceholder"),
802
- required: true
755
+ type: "button",
756
+ onClick: onClose,
757
+ style: {
758
+ width: "100%",
759
+ padding: "10px",
760
+ background: "none",
761
+ border: "none",
762
+ cursor: "pointer",
763
+ fontSize: 15,
764
+ color: colors.textMuted,
765
+ fontFamily: "inherit",
766
+ borderRadius: 8
767
+ },
768
+ children: t("cancel")
803
769
  }
804
770
  )
805
771
  ] }),
806
- mode === "bug" && /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 14 }, children: [
807
- /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-expected", children: t("expectedBehaviorLabel") }),
808
- /* @__PURE__ */ jsx2(
809
- "textarea",
772
+ step === "form" && /* @__PURE__ */ jsxs2(Fragment, { children: [
773
+ !hasInitialSelection && /* @__PURE__ */ jsxs2(
774
+ "button",
810
775
  {
811
- id: "flint-expected",
812
- style: { ...inputStyle, resize: "vertical", minHeight: 72 },
813
- value: expectedBehavior,
814
- onChange: (e) => setExpectedBehavior(e.target.value),
815
- placeholder: t("expectedBehaviorPlaceholder")
776
+ type: "button",
777
+ onClick: () => setStep("selector"),
778
+ style: {
779
+ display: "flex",
780
+ alignItems: "center",
781
+ gap: 4,
782
+ background: "none",
783
+ border: "none",
784
+ cursor: "pointer",
785
+ fontSize: 13,
786
+ fontWeight: 600,
787
+ color: colors.textMuted,
788
+ fontFamily: "inherit",
789
+ padding: "0 0 12px",
790
+ marginBottom: 4
791
+ },
792
+ children: [
793
+ "\u2190 ",
794
+ mode === "bug" ? "\u{1F41B} Bug Report" : mode === "feature" ? "\u{1F4A1} Feature Request" : "\u270F\uFE0F Text Issue"
795
+ ]
816
796
  }
817
- )
818
- ] }),
819
- /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 20, display: mode === "text" || !enableScreenshot ? "none" : void 0 }, children: [
820
- /* @__PURE__ */ jsx2(FieldLabel, { colors, children: t("screenshotLabel") }),
821
- screenshot ? /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 10 }, children: [
822
- /* @__PURE__ */ jsx2(
823
- "img",
824
- {
825
- src: URL.createObjectURL(screenshot),
826
- alt: "Screenshot preview",
827
- style: { height: 60, borderRadius: 8, objectFit: "cover", border: `1px solid ${inputBorder}` }
828
- }
829
- ),
830
- /* @__PURE__ */ jsx2(
831
- "span",
832
- {
833
- style: {
834
- fontSize: 13,
835
- color: colors.textMuted,
836
- flex: 1,
837
- overflow: "hidden",
838
- textOverflow: "ellipsis",
839
- whiteSpace: "nowrap"
840
- },
841
- children: screenshot.name
842
- }
843
- ),
797
+ ),
798
+ mode === "text" && /* @__PURE__ */ jsxs2(Fragment, { children: [
799
+ /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 14 }, children: [
800
+ /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-text-original", children: "Original text" }),
801
+ /* @__PURE__ */ jsx2(
802
+ "textarea",
803
+ {
804
+ id: "flint-text-original",
805
+ style: { ...inputStyle, resize: "vertical", minHeight: 60 },
806
+ value: textOriginal,
807
+ onChange: (e) => setTextOriginal(e.target.value),
808
+ placeholder: "Text that is wrong on screen\u2026",
809
+ required: true
810
+ }
811
+ )
812
+ ] }),
813
+ /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 14 }, children: [
814
+ /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-text-suggested", children: "Suggested correction" }),
815
+ /* @__PURE__ */ jsx2(
816
+ "textarea",
817
+ {
818
+ id: "flint-text-suggested",
819
+ style: { ...inputStyle, resize: "vertical", minHeight: 60 },
820
+ value: textSuggested,
821
+ onChange: (e) => setTextSuggested(e.target.value),
822
+ placeholder: "How it should read\u2026",
823
+ required: true
824
+ }
825
+ )
826
+ ] }),
827
+ /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 20 }, children: [
828
+ /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-text-lang", children: "Language" }),
829
+ /* @__PURE__ */ jsxs2(
830
+ "select",
831
+ {
832
+ id: "flint-text-lang",
833
+ value: textLang,
834
+ onChange: (e) => setTextLang(e.target.value),
835
+ style: {
836
+ ...inputStyle,
837
+ appearance: "none",
838
+ cursor: "pointer"
839
+ },
840
+ children: [
841
+ /* @__PURE__ */ jsx2("option", { value: "en", children: "English (en)" }),
842
+ /* @__PURE__ */ jsx2("option", { value: "he", children: "\u05E2\u05D1\u05E8\u05D9\u05EA (he)" })
843
+ ]
844
+ }
845
+ )
846
+ ] })
847
+ ] }),
848
+ mode === "feature" && /* @__PURE__ */ jsxs2(Fragment, { children: [
849
+ /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 14 }, children: [
850
+ /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-feature-description", children: "Description" }),
851
+ /* @__PURE__ */ jsx2(
852
+ "textarea",
853
+ {
854
+ id: "flint-feature-description",
855
+ style: { ...inputStyle, resize: "vertical", minHeight: 80 },
856
+ value: featureDescription,
857
+ onChange: (e) => setFeatureDescription(e.target.value),
858
+ placeholder: "Describe the feature you'd like to see...",
859
+ required: true
860
+ }
861
+ )
862
+ ] }),
863
+ /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 20 }, children: [
864
+ /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-feature-usecase", children: "Use case" }),
865
+ /* @__PURE__ */ jsx2(
866
+ "textarea",
867
+ {
868
+ id: "flint-feature-usecase",
869
+ style: { ...inputStyle, resize: "vertical", minHeight: 72 },
870
+ value: featureUseCase,
871
+ onChange: (e) => setFeatureUseCase(e.target.value),
872
+ placeholder: "What problem would this solve for you?"
873
+ }
874
+ )
875
+ ] })
876
+ ] }),
877
+ /* @__PURE__ */ jsxs2(
878
+ "div",
879
+ {
880
+ style: { marginBottom: 18, display: mode === "text" || mode === "feature" ? "none" : void 0 },
881
+ children: [
882
+ /* @__PURE__ */ jsx2(FieldLabel, { colors, children: t("severityLabel") }),
883
+ /* @__PURE__ */ jsx2("div", { style: { display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 8 }, children: SEVERITIES.map((sev) => /* @__PURE__ */ jsx2(
884
+ SeverityButton,
885
+ {
886
+ sev,
887
+ label: t(`severity_${sev}_label`),
888
+ selected: severity === sev,
889
+ hint: t(`severity_${sev}_hint`),
890
+ color: SEV_COLOR[sev],
891
+ accent: colors.accent,
892
+ border: inputBorder,
893
+ bg: colors.backgroundSecondary,
894
+ text: colors.text,
895
+ onClick: () => setSeverity(sev)
896
+ },
897
+ sev
898
+ )) })
899
+ ]
900
+ }
901
+ ),
902
+ mode === "bug" && /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 14 }, children: [
903
+ /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-description", children: t("whatIsBrokenLabel") }),
844
904
  /* @__PURE__ */ jsx2(
845
- "button",
905
+ "textarea",
846
906
  {
847
- type: "button",
848
- onClick: () => setScreenshot(null),
849
- "aria-label": "Remove screenshot",
850
- style: {
851
- background: "none",
852
- border: "none",
853
- cursor: "pointer",
854
- fontSize: 18,
855
- color: colors.textMuted,
856
- lineHeight: 1,
857
- padding: "2px 6px",
858
- borderRadius: 6,
859
- fontFamily: "inherit"
860
- },
861
- children: "\xD7"
907
+ id: "flint-description",
908
+ style: { ...inputStyle, resize: "vertical", minHeight: 80 },
909
+ value: description,
910
+ onChange: (e) => setDescription(e.target.value),
911
+ placeholder: t("whatIsBrokenPlaceholder"),
912
+ required: true
862
913
  }
863
914
  )
864
- ] }) : /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 8 }, children: [
865
- /* @__PURE__ */ jsxs2(
866
- "button",
867
- {
868
- type: "button",
869
- onClick: () => fileRef.current?.click(),
870
- style: {
871
- flex: 1,
872
- display: "flex",
873
- alignItems: "center",
874
- justifyContent: "center",
875
- gap: 6,
876
- padding: "10px 13px",
877
- borderRadius: 10,
878
- border: `1px dashed ${inputBorder}`,
879
- cursor: "pointer",
880
- fontSize: 14,
881
- color: colors.textMuted,
882
- background: colors.backgroundSecondary,
883
- fontFamily: "inherit"
884
- },
885
- children: [
886
- "\u{1F4CE} ",
887
- t("screenshotAttachFile")
888
- ]
889
- }
890
- ),
891
- /* @__PURE__ */ jsxs2(
892
- "button",
915
+ ] }),
916
+ mode === "bug" && /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 14 }, children: [
917
+ /* @__PURE__ */ jsx2(FieldLabel, { colors, htmlFor: "flint-expected", children: t("expectedBehaviorLabel") }),
918
+ /* @__PURE__ */ jsx2(
919
+ "textarea",
893
920
  {
894
- type: "button",
895
- onClick: () => setAnnotating(true),
896
- style: {
897
- flex: 1,
898
- display: "flex",
899
- alignItems: "center",
900
- justifyContent: "center",
901
- gap: 6,
902
- padding: "10px 13px",
903
- borderRadius: 10,
904
- border: `1px dashed ${inputBorder}`,
905
- cursor: "pointer",
906
- fontSize: 14,
907
- color: colors.textMuted,
908
- background: colors.backgroundSecondary,
909
- fontFamily: "inherit"
910
- },
911
- children: [
912
- "\u{1F532} ",
913
- t("screenshotMarkOnScreen")
914
- ]
921
+ id: "flint-expected",
922
+ style: { ...inputStyle, resize: "vertical", minHeight: 72 },
923
+ value: expectedBehavior,
924
+ onChange: (e) => setExpectedBehavior(e.target.value),
925
+ placeholder: t("expectedBehaviorPlaceholder")
915
926
  }
916
927
  )
917
928
  ] }),
929
+ /* @__PURE__ */ jsxs2(
930
+ "div",
931
+ {
932
+ style: {
933
+ marginBottom: 20,
934
+ display: mode === "text" || mode === "feature" || !enableScreenshot ? "none" : void 0
935
+ },
936
+ children: [
937
+ /* @__PURE__ */ jsx2(FieldLabel, { colors, children: t("screenshotLabel") }),
938
+ screenshot ? /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 10 }, children: [
939
+ /* @__PURE__ */ jsx2(
940
+ "img",
941
+ {
942
+ src: URL.createObjectURL(screenshot),
943
+ alt: "Screenshot preview",
944
+ style: {
945
+ height: 60,
946
+ borderRadius: 8,
947
+ objectFit: "cover",
948
+ border: `1px solid ${inputBorder}`
949
+ }
950
+ }
951
+ ),
952
+ /* @__PURE__ */ jsx2(
953
+ "span",
954
+ {
955
+ style: {
956
+ fontSize: 13,
957
+ color: colors.textMuted,
958
+ flex: 1,
959
+ overflow: "hidden",
960
+ textOverflow: "ellipsis",
961
+ whiteSpace: "nowrap"
962
+ },
963
+ children: screenshot.name
964
+ }
965
+ ),
966
+ /* @__PURE__ */ jsx2(
967
+ "button",
968
+ {
969
+ type: "button",
970
+ onClick: () => setScreenshot(null),
971
+ "aria-label": "Remove screenshot",
972
+ style: {
973
+ background: "none",
974
+ border: "none",
975
+ cursor: "pointer",
976
+ fontSize: 18,
977
+ color: colors.textMuted,
978
+ lineHeight: 1,
979
+ padding: "2px 6px",
980
+ borderRadius: 6,
981
+ fontFamily: "inherit"
982
+ },
983
+ children: "\xD7"
984
+ }
985
+ )
986
+ ] }) : /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 8 }, children: [
987
+ /* @__PURE__ */ jsxs2(
988
+ "button",
989
+ {
990
+ type: "button",
991
+ onClick: () => fileRef.current?.click(),
992
+ style: {
993
+ flex: 1,
994
+ display: "flex",
995
+ alignItems: "center",
996
+ justifyContent: "center",
997
+ gap: 6,
998
+ padding: "10px 13px",
999
+ borderRadius: 10,
1000
+ border: `1px dashed ${inputBorder}`,
1001
+ cursor: "pointer",
1002
+ fontSize: 14,
1003
+ color: colors.textMuted,
1004
+ background: colors.backgroundSecondary,
1005
+ fontFamily: "inherit"
1006
+ },
1007
+ children: [
1008
+ "\u{1F4CE} ",
1009
+ t("screenshotAttachFile")
1010
+ ]
1011
+ }
1012
+ ),
1013
+ /* @__PURE__ */ jsxs2(
1014
+ "button",
1015
+ {
1016
+ type: "button",
1017
+ onClick: () => setAnnotating(true),
1018
+ style: {
1019
+ flex: 1,
1020
+ display: "flex",
1021
+ alignItems: "center",
1022
+ justifyContent: "center",
1023
+ gap: 6,
1024
+ padding: "10px 13px",
1025
+ borderRadius: 10,
1026
+ border: `1px dashed ${inputBorder}`,
1027
+ cursor: "pointer",
1028
+ fontSize: 14,
1029
+ color: colors.textMuted,
1030
+ background: colors.backgroundSecondary,
1031
+ fontFamily: "inherit"
1032
+ },
1033
+ children: [
1034
+ "\u{1F532} ",
1035
+ t("screenshotMarkOnScreen")
1036
+ ]
1037
+ }
1038
+ )
1039
+ ] }),
1040
+ /* @__PURE__ */ jsx2(
1041
+ "input",
1042
+ {
1043
+ id: "flint-screenshot",
1044
+ ref: fileRef,
1045
+ type: "file",
1046
+ accept: "image/*",
1047
+ style: { display: "none" },
1048
+ onChange: (e) => setScreenshot(e.target.files?.[0] ?? null)
1049
+ }
1050
+ )
1051
+ ]
1052
+ }
1053
+ ),
1054
+ /* @__PURE__ */ jsxs2(
1055
+ "div",
1056
+ {
1057
+ style: {
1058
+ display: "flex",
1059
+ alignItems: "center",
1060
+ gap: 8,
1061
+ padding: "9px 12px",
1062
+ borderRadius: 10,
1063
+ background: isDark ? "rgba(255,255,255,0.04)" : "rgba(0,77,240,0.04)",
1064
+ border: `1px solid ${isDark ? "rgba(255,255,255,0.08)" : "rgba(0,77,240,0.1)"}`,
1065
+ marginBottom: 16
1066
+ },
1067
+ children: [
1068
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 16 }, children: "\u{1F3A5}" }),
1069
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 14, color: colors.textMuted, lineHeight: 1.4 }, children: t("replayInfo") })
1070
+ ]
1071
+ }
1072
+ ),
1073
+ status === "error" && /* @__PURE__ */ jsxs2(
1074
+ "div",
1075
+ {
1076
+ style: {
1077
+ padding: "10px 13px",
1078
+ borderRadius: 10,
1079
+ background: "rgba(239,68,68,0.08)",
1080
+ border: "1px solid rgba(239,68,68,0.2)",
1081
+ color: "#f87171",
1082
+ fontSize: 14,
1083
+ marginBottom: 16
1084
+ },
1085
+ children: [
1086
+ "\u26A0\uFE0F ",
1087
+ errorMsg || t("errorLabel")
1088
+ ]
1089
+ }
1090
+ ),
1091
+ /* @__PURE__ */ jsxs2(
1092
+ "button",
1093
+ {
1094
+ type: "submit",
1095
+ style: {
1096
+ width: "100%",
1097
+ padding: "13px 20px",
1098
+ borderRadius: 12,
1099
+ border: "none",
1100
+ background: `linear-gradient(135deg, ${colors.accent}, ${colors.accentHover})`,
1101
+ color: colors.buttonText,
1102
+ fontSize: 17,
1103
+ fontWeight: 700,
1104
+ cursor: "pointer",
1105
+ letterSpacing: "-0.01em",
1106
+ boxShadow: accentGlow,
1107
+ fontFamily: "inherit",
1108
+ display: "flex",
1109
+ alignItems: "center",
1110
+ justifyContent: "center",
1111
+ gap: 8
1112
+ },
1113
+ children: [
1114
+ /* @__PURE__ */ jsx2(SparkIcon, { color: colors.buttonText, size: 15 }),
1115
+ t("submitLabel")
1116
+ ]
1117
+ }
1118
+ ),
918
1119
  /* @__PURE__ */ jsx2(
919
- "input",
1120
+ "button",
920
1121
  {
921
- id: "flint-screenshot",
922
- ref: fileRef,
923
- type: "file",
924
- accept: "image/*",
925
- style: { display: "none" },
926
- onChange: (e) => setScreenshot(e.target.files?.[0] ?? null)
1122
+ type: "button",
1123
+ onClick: onClose,
1124
+ style: {
1125
+ width: "100%",
1126
+ padding: "10px",
1127
+ marginTop: 8,
1128
+ background: "none",
1129
+ border: "none",
1130
+ cursor: "pointer",
1131
+ fontSize: 15,
1132
+ color: colors.textMuted,
1133
+ fontFamily: "inherit",
1134
+ borderRadius: 8
1135
+ },
1136
+ children: t("cancel")
927
1137
  }
928
1138
  )
929
- ] }),
930
- /* @__PURE__ */ jsxs2(
931
- "div",
932
- {
933
- style: {
934
- display: "flex",
935
- alignItems: "center",
936
- gap: 8,
937
- padding: "9px 12px",
938
- borderRadius: 10,
939
- background: isDark ? "rgba(255,255,255,0.04)" : "rgba(0,77,240,0.04)",
940
- border: `1px solid ${isDark ? "rgba(255,255,255,0.08)" : "rgba(0,77,240,0.1)"}`,
941
- marginBottom: 16
942
- },
943
- children: [
944
- /* @__PURE__ */ jsx2("span", { style: { fontSize: 16 }, children: "\u{1F3A5}" }),
945
- /* @__PURE__ */ jsx2("span", { style: { fontSize: 14, color: colors.textMuted, lineHeight: 1.4 }, children: t("replayInfo") })
946
- ]
947
- }
948
- ),
949
- status === "error" && /* @__PURE__ */ jsxs2(
950
- "div",
951
- {
952
- style: {
953
- padding: "10px 13px",
954
- borderRadius: 10,
955
- background: "rgba(239,68,68,0.08)",
956
- border: "1px solid rgba(239,68,68,0.2)",
957
- color: "#f87171",
958
- fontSize: 14,
959
- marginBottom: 16
960
- },
961
- children: [
962
- "\u26A0\uFE0F ",
963
- errorMsg || t("errorLabel")
964
- ]
965
- }
966
- ),
967
- /* @__PURE__ */ jsxs2(
968
- "button",
969
- {
970
- type: "submit",
971
- style: {
972
- width: "100%",
973
- padding: "13px 20px",
974
- borderRadius: 12,
975
- border: "none",
976
- background: `linear-gradient(135deg, ${colors.accent}, ${colors.accentHover})`,
977
- color: colors.buttonText,
978
- fontSize: 17,
979
- fontWeight: 700,
980
- cursor: "pointer",
981
- letterSpacing: "-0.01em",
982
- boxShadow: accentGlow,
983
- fontFamily: "inherit",
984
- display: "flex",
985
- alignItems: "center",
986
- justifyContent: "center",
987
- gap: 8
988
- },
989
- children: [
990
- /* @__PURE__ */ jsx2(SparkIcon, { color: colors.buttonText, size: 15 }),
991
- t("submitLabel")
992
- ]
993
- }
994
- ),
995
- /* @__PURE__ */ jsx2(
996
- "button",
997
- {
998
- type: "button",
999
- onClick: onClose,
1000
- style: {
1001
- width: "100%",
1002
- padding: "10px",
1003
- marginTop: 8,
1004
- background: "none",
1005
- border: "none",
1006
- cursor: "pointer",
1007
- fontSize: 15,
1008
- color: colors.textMuted,
1009
- fontFamily: "inherit",
1010
- borderRadius: 8
1011
- },
1012
- children: t("cancel")
1013
- }
1014
- )
1139
+ ] })
1015
1140
  ] })
1016
1141
  ] }) })
1017
1142
  ] });
@@ -1125,6 +1250,51 @@ function SendingDots({ color }) {
1125
1250
  i
1126
1251
  )) });
1127
1252
  }
1253
+ function TypeSelectorButton({
1254
+ emoji,
1255
+ title,
1256
+ subtitle,
1257
+ colors,
1258
+ inputBorder,
1259
+ onClick
1260
+ }) {
1261
+ return /* @__PURE__ */ jsxs2(
1262
+ "button",
1263
+ {
1264
+ type: "button",
1265
+ onClick,
1266
+ style: {
1267
+ display: "flex",
1268
+ alignItems: "center",
1269
+ gap: 14,
1270
+ padding: "14px 16px",
1271
+ borderRadius: 12,
1272
+ border: `1px solid ${inputBorder}`,
1273
+ background: colors.backgroundSecondary,
1274
+ cursor: "pointer",
1275
+ fontFamily: "inherit",
1276
+ textAlign: "left",
1277
+ transition: "border-color 0.15s, box-shadow 0.15s"
1278
+ },
1279
+ onMouseEnter: (e) => {
1280
+ e.currentTarget.style.borderColor = colors.accent;
1281
+ e.currentTarget.style.boxShadow = `0 0 0 1px ${colors.accent}40`;
1282
+ },
1283
+ onMouseLeave: (e) => {
1284
+ e.currentTarget.style.borderColor = inputBorder;
1285
+ e.currentTarget.style.boxShadow = "none";
1286
+ },
1287
+ children: [
1288
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 24, lineHeight: 1, flexShrink: 0 }, children: emoji }),
1289
+ /* @__PURE__ */ jsxs2("div", { children: [
1290
+ /* @__PURE__ */ jsx2("div", { style: { fontSize: 14, fontWeight: 600, color: colors.text, marginBottom: 2 }, children: title }),
1291
+ /* @__PURE__ */ jsx2("div", { style: { fontSize: 12, color: colors.textMuted }, children: subtitle })
1292
+ ] }),
1293
+ /* @__PURE__ */ jsx2("span", { style: { marginLeft: "auto", fontSize: 16, color: colors.textMuted, flexShrink: 0 }, children: "\u2192" })
1294
+ ]
1295
+ }
1296
+ );
1297
+ }
1128
1298
  function SeverityButton({ sev, label, selected, hint, color, accent, border, bg, text, onClick }) {
1129
1299
  return /* @__PURE__ */ jsxs2(
1130
1300
  "button",
@@ -1252,7 +1422,8 @@ widgetI18n.use(initReactI18next).init({
1252
1422
  fallbackLng: "en-US",
1253
1423
  resources: { "en-US": { translation: en_default } },
1254
1424
  interpolation: { escapeValue: false },
1255
- initImmediate: false
1425
+ initImmediate: false,
1426
+ showSupportNotice: false
1256
1427
  });
1257
1428
  var i18n_default = widgetI18n;
1258
1429
 
@@ -1280,6 +1451,7 @@ function WidgetContent({
1280
1451
  statusPageUrl,
1281
1452
  enableScreenshot = true,
1282
1453
  enableTextIssues = true,
1454
+ enableFeatureRequests = false,
1283
1455
  onBeforeSubmit,
1284
1456
  onSuccess,
1285
1457
  onError,
@@ -1450,6 +1622,7 @@ function WidgetContent({
1450
1622
  initialSelection: pendingSelection.current,
1451
1623
  enableScreenshot,
1452
1624
  enableTextIssues,
1625
+ enableFeatureRequests,
1453
1626
  statusPageUrl,
1454
1627
  onBeforeSubmit,
1455
1628
  onSuccess,