@diegotsi/flint-react 2.4.7 → 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",
@@ -1281,6 +1451,7 @@ function WidgetContent({
1281
1451
  statusPageUrl,
1282
1452
  enableScreenshot = true,
1283
1453
  enableTextIssues = true,
1454
+ enableFeatureRequests = false,
1284
1455
  onBeforeSubmit,
1285
1456
  onSuccess,
1286
1457
  onError,
@@ -1451,6 +1622,7 @@ function WidgetContent({
1451
1622
  initialSelection: pendingSelection.current,
1452
1623
  enableScreenshot,
1453
1624
  enableTextIssues,
1625
+ enableFeatureRequests,
1454
1626
  statusPageUrl,
1455
1627
  onBeforeSubmit,
1456
1628
  onSuccess,