tetrons 2.3.90 → 2.3.91

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.
@@ -2,7 +2,7 @@
2
2
  import { MathInline } from "./extensions/MathExtension";
3
3
  import React, { useEffect, useRef, useState } from "react";
4
4
  import { useEditor, EditorContent as TiptapEditorContent } from "@tiptap/react";
5
- import type { AddOn } from "./toolbar/TetronsToolbar";
5
+ import type { AddOn } from "../../types/types";
6
6
  import { getActivePlan, Plan } from "../../utils/licenseTracker";
7
7
 
8
8
  import Document from "@tiptap/extension-document";
package/dist/index.cjs CHANGED
@@ -16729,26 +16729,7 @@ async function checkGrammar(text) {
16729
16729
  }
16730
16730
 
16731
16731
  // src/components/tetrons/toolbar/MiscGroup.tsx
16732
- function MiscGroup({ editor }) {
16733
- const handlePreview = () => {
16734
- const html = editor.getHTML();
16735
- const previewWindow = window.open("", "_blank");
16736
- if (previewWindow) {
16737
- previewWindow.document.open();
16738
- previewWindow.document.write(`
16739
- <html>
16740
- <head>
16741
- <title>Preview</title>
16742
- <style>
16743
- body { font-family: sans-serif; padding: 2rem; line-height: 1.6; }
16744
- </style>
16745
- </head>
16746
- <body>${html}</body>
16747
- </html>
16748
- `);
16749
- previewWindow.document.close();
16750
- }
16751
- };
16732
+ function MiscGroup({ editor, enabledFeatures }) {
16752
16733
  const handleGrammarCheck = async () => {
16753
16734
  const text = editor.getText();
16754
16735
  try {
@@ -16772,7 +16753,26 @@ Reason: ${issue.message}
16772
16753
  alert("\u274C Failed to check grammar. Please try again later.");
16773
16754
  }
16774
16755
  };
16775
- return /* @__PURE__ */ import_react16.default.createElement("div", { className: "misc-group" }, /* @__PURE__ */ import_react16.default.createElement(
16756
+ const handlePreview = () => {
16757
+ const html = editor.getHTML();
16758
+ const previewWindow = window.open("", "_blank");
16759
+ if (previewWindow) {
16760
+ previewWindow.document.open();
16761
+ previewWindow.document.write(`
16762
+ <html>
16763
+ <head>
16764
+ <title>Preview</title>
16765
+ <style>
16766
+ body { font-family: sans-serif; padding: 2rem; line-height: 1.6; }
16767
+ </style>
16768
+ </head>
16769
+ <body>${html}</body>
16770
+ </html>
16771
+ `);
16772
+ previewWindow.document.close();
16773
+ }
16774
+ };
16775
+ return /* @__PURE__ */ import_react16.default.createElement("div", { className: "misc-group" }, enabledFeatures.includes("undo") && /* @__PURE__ */ import_react16.default.createElement(
16776
16776
  ToolbarButton_default,
16777
16777
  {
16778
16778
  icon: import_md6.MdUndo,
@@ -16780,7 +16780,7 @@ Reason: ${issue.message}
16780
16780
  onClick: () => editor.chain().focus().undo().run(),
16781
16781
  disabled: !editor.can().undo()
16782
16782
  }
16783
- ), /* @__PURE__ */ import_react16.default.createElement(
16783
+ ), enabledFeatures.includes("redo") && /* @__PURE__ */ import_react16.default.createElement(
16784
16784
  ToolbarButton_default,
16785
16785
  {
16786
16786
  icon: import_md6.MdRedo,
@@ -16788,14 +16788,14 @@ Reason: ${issue.message}
16788
16788
  onClick: () => editor.chain().focus().redo().run(),
16789
16789
  disabled: !editor.can().redo()
16790
16790
  }
16791
- ), /* @__PURE__ */ import_react16.default.createElement(
16791
+ ), enabledFeatures.includes("resetFormatting") && /* @__PURE__ */ import_react16.default.createElement(
16792
16792
  ToolbarButton_default,
16793
16793
  {
16794
16794
  icon: import_md6.MdRefresh,
16795
16795
  label: "Reset Formatting",
16796
16796
  onClick: () => editor.chain().focus().unsetAllMarks().clearNodes().run()
16797
16797
  }
16798
- ), /* @__PURE__ */ import_react16.default.createElement(
16798
+ ), enabledFeatures.includes("codeBlock") && /* @__PURE__ */ import_react16.default.createElement(
16799
16799
  ToolbarButton_default,
16800
16800
  {
16801
16801
  icon: import_md6.MdCode,
@@ -16803,14 +16803,14 @@ Reason: ${issue.message}
16803
16803
  onClick: () => editor.chain().focus().toggleCodeBlock().run(),
16804
16804
  isActive: editor.isActive("codeBlock")
16805
16805
  }
16806
- ), /* @__PURE__ */ import_react16.default.createElement(
16806
+ ), enabledFeatures.includes("preview") && /* @__PURE__ */ import_react16.default.createElement(
16807
16807
  ToolbarButton_default,
16808
16808
  {
16809
16809
  icon: import_md6.MdVisibility,
16810
16810
  label: "Preview",
16811
16811
  onClick: handlePreview
16812
16812
  }
16813
- ), /* @__PURE__ */ import_react16.default.createElement(
16813
+ ), enabledFeatures.includes("spell check") && /* @__PURE__ */ import_react16.default.createElement(
16814
16814
  ToolbarButton_default,
16815
16815
  {
16816
16816
  icon: import_md6.MdSpellcheck,
@@ -16876,16 +16876,11 @@ function FileGroup({ editor }) {
16876
16876
  var import_react18 = __toESM(require("react"));
16877
16877
  var import_fa2 = require("react-icons/fa");
16878
16878
  var import_loaders = require("@uiball/loaders");
16879
- var import_framer_motion = require("framer-motion");
16880
- function AiGroup({ editor }) {
16879
+ function AiGroup({ editor, enabledFeatures }) {
16881
16880
  const [isRecording, setIsRecording] = (0, import_react18.useState)(false);
16882
16881
  const [audioBlob, setAudioBlob] = (0, import_react18.useState)(null);
16883
16882
  const [isTranscribing, setIsTranscribing] = (0, import_react18.useState)(false);
16884
16883
  const [transcriptionError, setTranscriptionError] = (0, import_react18.useState)("");
16885
- const [showPromptInput, setShowPromptInput] = (0, import_react18.useState)(false);
16886
- const [prompt2, setPrompt] = (0, import_react18.useState)("");
16887
- const [isLoadingAI, setIsLoadingAI] = (0, import_react18.useState)(false);
16888
- const [aiError, setAiError] = (0, import_react18.useState)("");
16889
16884
  const mediaRecorderRef = (0, import_react18.useRef)(null);
16890
16885
  const chunksRef = (0, import_react18.useRef)([]);
16891
16886
  const startRecording = async () => {
@@ -16932,35 +16927,8 @@ function AiGroup({ editor }) {
16932
16927
  setIsTranscribing(false);
16933
16928
  }
16934
16929
  };
16935
- const handleAiClick = () => {
16936
- setShowPromptInput(true);
16937
- setPrompt("");
16938
- setAiError("");
16939
- };
16940
- const handlePromptSubmit = async () => {
16941
- if (!prompt2.trim()) return;
16942
- setIsLoadingAI(true);
16943
- setAiError("");
16944
- try {
16945
- const res = await fetch("https://staging.tetrons.com/api/ai-action", {
16946
- method: "POST",
16947
- headers: { "Content-Type": "application/json" },
16948
- body: JSON.stringify({ content: prompt2 })
16949
- });
16950
- const data = await res.json();
16951
- if (!res.ok || !data.response) {
16952
- throw new Error(data.error || "AI failed to generate content");
16953
- }
16954
- editor.commands.insertContent(data.response);
16955
- setShowPromptInput(false);
16956
- } catch (e) {
16957
- console.error(e);
16958
- setAiError("Failed to generate content. Try again.");
16959
- } finally {
16960
- setIsLoadingAI(false);
16961
- }
16962
- };
16963
- return /* @__PURE__ */ import_react18.default.createElement("div", { className: "group relative space-y-3" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "flex gap-2 items-center" }, !isRecording ? /* @__PURE__ */ import_react18.default.createElement(
16930
+ if (!enabledFeatures.includes("voice to text")) return null;
16931
+ return /* @__PURE__ */ import_react18.default.createElement("div", { className: "group flex flex-col gap-2 items-start" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "flex gap-2 items-center" }, !isRecording ? /* @__PURE__ */ import_react18.default.createElement(
16964
16932
  "button",
16965
16933
  {
16966
16934
  type: "button",
@@ -16978,60 +16946,7 @@ function AiGroup({ editor }) {
16978
16946
  title: "Stop Recording"
16979
16947
  },
16980
16948
  /* @__PURE__ */ import_react18.default.createElement(import_fa2.FaStop, { size: 18 })
16981
- ), /* @__PURE__ */ import_react18.default.createElement(
16982
- "button",
16983
- {
16984
- type: "button",
16985
- onClick: handleAiClick,
16986
- className: "ai-button",
16987
- title: "AI Assist"
16988
- },
16989
- "AI"
16990
- )), isRecording && /* @__PURE__ */ import_react18.default.createElement("div", { className: "flex flex-col items-center" }, /* @__PURE__ */ import_react18.default.createElement(import_loaders.Waveform, { size: 30, lineWeight: 3.5, speed: 1, color: "#4F46E5" }), /* @__PURE__ */ import_react18.default.createElement("p", { className: "text-sm mt-1 text-gray-600" }, "Recording...")), isTranscribing && /* @__PURE__ */ import_react18.default.createElement("p", { className: "text-sm text-gray-500" }, "Transcribing..."), transcriptionError && /* @__PURE__ */ import_react18.default.createElement("p", { className: "text-sm text-red-600" }, transcriptionError), audioBlob && /* @__PURE__ */ import_react18.default.createElement("div", { className: "mt-2" }, /* @__PURE__ */ import_react18.default.createElement("audio", { controls: true, src: URL.createObjectURL(audioBlob) })), /* @__PURE__ */ import_react18.default.createElement(import_framer_motion.AnimatePresence, null, showPromptInput && /* @__PURE__ */ import_react18.default.createElement(
16991
- import_framer_motion.motion.div,
16992
- {
16993
- className: "ai-modal-backdrop",
16994
- initial: { opacity: 0 },
16995
- animate: { opacity: 1 },
16996
- exit: { opacity: 0 }
16997
- },
16998
- /* @__PURE__ */ import_react18.default.createElement(
16999
- import_framer_motion.motion.div,
17000
- {
17001
- className: "ai-modal-content",
17002
- initial: { scale: 0.9, opacity: 0 },
17003
- animate: { scale: 1, opacity: 1 },
17004
- exit: { scale: 0.9, opacity: 0 }
17005
- },
17006
- /* @__PURE__ */ import_react18.default.createElement("h2", { className: "ai-modal-title" }, "AI Prompt"),
17007
- /* @__PURE__ */ import_react18.default.createElement(
17008
- "textarea",
17009
- {
17010
- className: "ai-modal-textarea",
17011
- value: prompt2,
17012
- onChange: (e) => setPrompt(e.target.value),
17013
- placeholder: "Enter your prompt here..."
17014
- }
17015
- ),
17016
- aiError && /* @__PURE__ */ import_react18.default.createElement("p", { className: "ai-modal-error" }, aiError),
17017
- /* @__PURE__ */ import_react18.default.createElement("div", { className: "ai-modal-actions" }, /* @__PURE__ */ import_react18.default.createElement(
17018
- "button",
17019
- {
17020
- onClick: () => setShowPromptInput(false),
17021
- className: "ai-cancel-btn"
17022
- },
17023
- "Cancel"
17024
- ), /* @__PURE__ */ import_react18.default.createElement(
17025
- "button",
17026
- {
17027
- onClick: handlePromptSubmit,
17028
- disabled: isLoadingAI,
17029
- className: "ai-submit-btn"
17030
- },
17031
- isLoadingAI ? "Generating..." : "Submit"
17032
- ))
17033
- )
17034
- )));
16949
+ )), isRecording && /* @__PURE__ */ import_react18.default.createElement("div", { className: "flex flex-col items-center" }, /* @__PURE__ */ import_react18.default.createElement(import_loaders.Waveform, { size: 30, lineWeight: 3.5, speed: 1, color: "#4F46E5" }), /* @__PURE__ */ import_react18.default.createElement("p", { className: "text-sm mt-1 text-gray-600" }, "Recording...")), isTranscribing && /* @__PURE__ */ import_react18.default.createElement("p", { className: "text-sm text-gray-500" }, "Transcribing..."), transcriptionError && /* @__PURE__ */ import_react18.default.createElement("p", { className: "text-sm text-red-600" }, transcriptionError), audioBlob && /* @__PURE__ */ import_react18.default.createElement("div", { className: "mt-2" }, /* @__PURE__ */ import_react18.default.createElement("audio", { controls: true, src: URL.createObjectURL(audioBlob) })));
17035
16950
  }
17036
16951
 
17037
16952
  // src/components/tetrons/toolbar/AddOnGroup.tsx
@@ -17043,20 +16958,15 @@ var MathModal2 = (0, import_dynamic.default)(() => Promise.resolve().then(() =>
17043
16958
  var CodeEditorModal2 = (0, import_dynamic.default)(() => Promise.resolve().then(() => (init_CodeEditorModal(), CodeEditorModal_exports)), {
17044
16959
  ssr: false
17045
16960
  });
17046
- var AddOnGroup = ({ editor }) => {
17047
- const [isModalOpen, setModalOpen] = (0, import_react22.useState)(false);
16961
+ var AddOnGroup = ({ editor, addOns }) => {
16962
+ const [isMathModalOpen, setMathModalOpen] = (0, import_react22.useState)(false);
17048
16963
  const [latexValue, setLatexValue] = (0, import_react22.useState)("");
17049
16964
  const [isCodeModalOpen, setCodeModalOpen] = (0, import_react22.useState)(false);
17050
16965
  if (!editor) return null;
17051
- const insertCodeBlock = () => {
17052
- setCodeModalOpen(true);
17053
- };
16966
+ const insertCodeBlock = () => setCodeModalOpen(true);
17054
16967
  const handleMathInsert = (latex) => {
17055
- editor.chain().focus().insertContent({
17056
- type: "mathInline",
17057
- attrs: { formula: latex }
17058
- }).run();
17059
- setModalOpen(false);
16968
+ editor.chain().focus().insertContent({ type: "mathInline", attrs: { formula: latex } }).run();
16969
+ setMathModalOpen(false);
17060
16970
  setLatexValue("");
17061
16971
  };
17062
16972
  const handleSubmitCode = (code) => {
@@ -17082,7 +16992,7 @@ var AddOnGroup = ({ editor }) => {
17082
16992
  }).run();
17083
16993
  setCodeModalOpen(false);
17084
16994
  };
17085
- return /* @__PURE__ */ import_react22.default.createElement(import_react22.default.Fragment, null, /* @__PURE__ */ import_react22.default.createElement("div", { className: "group flex gap-2 items-center" }, /* @__PURE__ */ import_react22.default.createElement(
16995
+ return /* @__PURE__ */ import_react22.default.createElement(import_react22.default.Fragment, null, /* @__PURE__ */ import_react22.default.createElement("div", { className: "group flex gap-2 items-center" }, addOns.includes("code") && /* @__PURE__ */ import_react22.default.createElement(
17086
16996
  "button",
17087
16997
  {
17088
16998
  type: "button",
@@ -17091,25 +17001,25 @@ var AddOnGroup = ({ editor }) => {
17091
17001
  title: "Open Code Editor"
17092
17002
  },
17093
17003
  /* @__PURE__ */ import_react22.default.createElement(import_lucide_react.MessageSquareCode, { size: 18 })
17094
- ), /* @__PURE__ */ import_react22.default.createElement(
17004
+ ), addOns.includes("math") && /* @__PURE__ */ import_react22.default.createElement(
17095
17005
  "button",
17096
17006
  {
17097
17007
  type: "button",
17098
- onClick: () => setModalOpen(true),
17008
+ onClick: () => setMathModalOpen(true),
17099
17009
  className: "addon-btn",
17100
17010
  title: "Insert Math Equation"
17101
17011
  },
17102
17012
  /* @__PURE__ */ import_react22.default.createElement(import_lucide_react.SquareRadical, { size: 18 })
17103
- )), /* @__PURE__ */ import_react22.default.createElement(
17013
+ )), addOns.includes("math") && /* @__PURE__ */ import_react22.default.createElement(
17104
17014
  MathModal2,
17105
17015
  {
17106
- isOpen: isModalOpen,
17107
- onClose: () => setModalOpen(false),
17016
+ isOpen: isMathModalOpen,
17017
+ onClose: () => setMathModalOpen(false),
17108
17018
  onSubmit: handleMathInsert,
17109
17019
  value: latexValue,
17110
17020
  setValue: setLatexValue
17111
17021
  }
17112
- ), /* @__PURE__ */ import_react22.default.createElement(
17022
+ ), addOns.includes("code") && /* @__PURE__ */ import_react22.default.createElement(
17113
17023
  CodeEditorModal2,
17114
17024
  {
17115
17025
  isOpen: isCodeModalOpen,
@@ -17163,7 +17073,27 @@ function TetronsToolbar({
17163
17073
  checked: autoSave,
17164
17074
  onChange: (e) => setAutoSave(e.target.checked)
17165
17075
  }
17166
- ), /* @__PURE__ */ import_react23.default.createElement("label", { htmlFor: "autoSave" }, "Auto Save")), ["pro", "premium", "platinum"].includes(version) && /* @__PURE__ */ import_react23.default.createElement(FileGroup, { editor }), /* @__PURE__ */ import_react23.default.createElement(ClipboardGroup, { editor }), /* @__PURE__ */ import_react23.default.createElement(FontStyleGroup, { editor }), /* @__PURE__ */ import_react23.default.createElement(ListAlignGroup, { editor }), ["premium", "platinum"].includes(version) && /* @__PURE__ */ import_react23.default.createElement(import_react23.default.Fragment, null, /* @__PURE__ */ import_react23.default.createElement(InsertGroup, { editor }), /* @__PURE__ */ import_react23.default.createElement(ActionGroup, { editor })), (["pro", "premium"].includes(version) && effectiveAddOns.includes("spell check") || version === "platinum") && /* @__PURE__ */ import_react23.default.createElement(MiscGroup, { editor }), (version === "premium" && effectiveAddOns.includes("ai") || version === "platinum") && /* @__PURE__ */ import_react23.default.createElement(AiGroup, { editor }), effectiveAddOns.length > 0 && /* @__PURE__ */ import_react23.default.createElement(AddOnGroup_default, { editor }));
17076
+ ), /* @__PURE__ */ import_react23.default.createElement("label", { htmlFor: "autoSave" }, "Auto Save")), ["pro", "premium", "platinum"].includes(version) && /* @__PURE__ */ import_react23.default.createElement(FileGroup, { editor }), /* @__PURE__ */ import_react23.default.createElement(ClipboardGroup, { editor }), /* @__PURE__ */ import_react23.default.createElement(FontStyleGroup, { editor }), /* @__PURE__ */ import_react23.default.createElement(ListAlignGroup, { editor }), ["premium", "platinum"].includes(version) && /* @__PURE__ */ import_react23.default.createElement(import_react23.default.Fragment, null, /* @__PURE__ */ import_react23.default.createElement(InsertGroup, { editor }), /* @__PURE__ */ import_react23.default.createElement(ActionGroup, { editor })), (["pro", "premium"].includes(version) || version === "platinum") && /* @__PURE__ */ import_react23.default.createElement(
17077
+ MiscGroup,
17078
+ {
17079
+ editor,
17080
+ enabledFeatures: effectiveAddOns
17081
+ }
17082
+ ), (version === "premium" && effectiveAddOns.includes("ai") || version === "platinum" || effectiveAddOns.includes("voice to text")) && /* @__PURE__ */ import_react23.default.createElement(
17083
+ AiGroup,
17084
+ {
17085
+ editor,
17086
+ enabledFeatures: effectiveAddOns.filter(
17087
+ (a) => ["ai", "voice to text"].includes(a)
17088
+ )
17089
+ }
17090
+ ), effectiveAddOns.some((a) => a === "math" || a === "code") && /* @__PURE__ */ import_react23.default.createElement(
17091
+ AddOnGroup_default,
17092
+ {
17093
+ editor,
17094
+ addOns: effectiveAddOns.filter((a) => a === "math" || a === "code")
17095
+ }
17096
+ ));
17167
17097
  }
17168
17098
 
17169
17099
  // src/components/tetrons/EditorContent.tsx
package/dist/index.mjs CHANGED
@@ -16738,26 +16738,7 @@ async function checkGrammar(text) {
16738
16738
  }
16739
16739
 
16740
16740
  // src/components/tetrons/toolbar/MiscGroup.tsx
16741
- function MiscGroup({ editor }) {
16742
- const handlePreview = () => {
16743
- const html = editor.getHTML();
16744
- const previewWindow = window.open("", "_blank");
16745
- if (previewWindow) {
16746
- previewWindow.document.open();
16747
- previewWindow.document.write(`
16748
- <html>
16749
- <head>
16750
- <title>Preview</title>
16751
- <style>
16752
- body { font-family: sans-serif; padding: 2rem; line-height: 1.6; }
16753
- </style>
16754
- </head>
16755
- <body>${html}</body>
16756
- </html>
16757
- `);
16758
- previewWindow.document.close();
16759
- }
16760
- };
16741
+ function MiscGroup({ editor, enabledFeatures }) {
16761
16742
  const handleGrammarCheck = async () => {
16762
16743
  const text = editor.getText();
16763
16744
  try {
@@ -16781,7 +16762,26 @@ Reason: ${issue.message}
16781
16762
  alert("\u274C Failed to check grammar. Please try again later.");
16782
16763
  }
16783
16764
  };
16784
- return /* @__PURE__ */ React10.createElement("div", { className: "misc-group" }, /* @__PURE__ */ React10.createElement(
16765
+ const handlePreview = () => {
16766
+ const html = editor.getHTML();
16767
+ const previewWindow = window.open("", "_blank");
16768
+ if (previewWindow) {
16769
+ previewWindow.document.open();
16770
+ previewWindow.document.write(`
16771
+ <html>
16772
+ <head>
16773
+ <title>Preview</title>
16774
+ <style>
16775
+ body { font-family: sans-serif; padding: 2rem; line-height: 1.6; }
16776
+ </style>
16777
+ </head>
16778
+ <body>${html}</body>
16779
+ </html>
16780
+ `);
16781
+ previewWindow.document.close();
16782
+ }
16783
+ };
16784
+ return /* @__PURE__ */ React10.createElement("div", { className: "misc-group" }, enabledFeatures.includes("undo") && /* @__PURE__ */ React10.createElement(
16785
16785
  ToolbarButton_default,
16786
16786
  {
16787
16787
  icon: MdUndo,
@@ -16789,7 +16789,7 @@ Reason: ${issue.message}
16789
16789
  onClick: () => editor.chain().focus().undo().run(),
16790
16790
  disabled: !editor.can().undo()
16791
16791
  }
16792
- ), /* @__PURE__ */ React10.createElement(
16792
+ ), enabledFeatures.includes("redo") && /* @__PURE__ */ React10.createElement(
16793
16793
  ToolbarButton_default,
16794
16794
  {
16795
16795
  icon: MdRedo,
@@ -16797,14 +16797,14 @@ Reason: ${issue.message}
16797
16797
  onClick: () => editor.chain().focus().redo().run(),
16798
16798
  disabled: !editor.can().redo()
16799
16799
  }
16800
- ), /* @__PURE__ */ React10.createElement(
16800
+ ), enabledFeatures.includes("resetFormatting") && /* @__PURE__ */ React10.createElement(
16801
16801
  ToolbarButton_default,
16802
16802
  {
16803
16803
  icon: MdRefresh,
16804
16804
  label: "Reset Formatting",
16805
16805
  onClick: () => editor.chain().focus().unsetAllMarks().clearNodes().run()
16806
16806
  }
16807
- ), /* @__PURE__ */ React10.createElement(
16807
+ ), enabledFeatures.includes("codeBlock") && /* @__PURE__ */ React10.createElement(
16808
16808
  ToolbarButton_default,
16809
16809
  {
16810
16810
  icon: MdCode,
@@ -16812,14 +16812,14 @@ Reason: ${issue.message}
16812
16812
  onClick: () => editor.chain().focus().toggleCodeBlock().run(),
16813
16813
  isActive: editor.isActive("codeBlock")
16814
16814
  }
16815
- ), /* @__PURE__ */ React10.createElement(
16815
+ ), enabledFeatures.includes("preview") && /* @__PURE__ */ React10.createElement(
16816
16816
  ToolbarButton_default,
16817
16817
  {
16818
16818
  icon: MdVisibility,
16819
16819
  label: "Preview",
16820
16820
  onClick: handlePreview
16821
16821
  }
16822
- ), /* @__PURE__ */ React10.createElement(
16822
+ ), enabledFeatures.includes("spell check") && /* @__PURE__ */ React10.createElement(
16823
16823
  ToolbarButton_default,
16824
16824
  {
16825
16825
  icon: MdSpellcheck,
@@ -16885,16 +16885,11 @@ function FileGroup({ editor }) {
16885
16885
  import React12, { useState as useState7, useRef as useRef6 } from "react";
16886
16886
  import { FaMicrophone, FaStop } from "react-icons/fa";
16887
16887
  import { Waveform } from "@uiball/loaders";
16888
- import { motion, AnimatePresence } from "framer-motion";
16889
- function AiGroup({ editor }) {
16888
+ function AiGroup({ editor, enabledFeatures }) {
16890
16889
  const [isRecording, setIsRecording] = useState7(false);
16891
16890
  const [audioBlob, setAudioBlob] = useState7(null);
16892
16891
  const [isTranscribing, setIsTranscribing] = useState7(false);
16893
16892
  const [transcriptionError, setTranscriptionError] = useState7("");
16894
- const [showPromptInput, setShowPromptInput] = useState7(false);
16895
- const [prompt2, setPrompt] = useState7("");
16896
- const [isLoadingAI, setIsLoadingAI] = useState7(false);
16897
- const [aiError, setAiError] = useState7("");
16898
16893
  const mediaRecorderRef = useRef6(null);
16899
16894
  const chunksRef = useRef6([]);
16900
16895
  const startRecording = async () => {
@@ -16941,35 +16936,8 @@ function AiGroup({ editor }) {
16941
16936
  setIsTranscribing(false);
16942
16937
  }
16943
16938
  };
16944
- const handleAiClick = () => {
16945
- setShowPromptInput(true);
16946
- setPrompt("");
16947
- setAiError("");
16948
- };
16949
- const handlePromptSubmit = async () => {
16950
- if (!prompt2.trim()) return;
16951
- setIsLoadingAI(true);
16952
- setAiError("");
16953
- try {
16954
- const res = await fetch("https://staging.tetrons.com/api/ai-action", {
16955
- method: "POST",
16956
- headers: { "Content-Type": "application/json" },
16957
- body: JSON.stringify({ content: prompt2 })
16958
- });
16959
- const data = await res.json();
16960
- if (!res.ok || !data.response) {
16961
- throw new Error(data.error || "AI failed to generate content");
16962
- }
16963
- editor.commands.insertContent(data.response);
16964
- setShowPromptInput(false);
16965
- } catch (e) {
16966
- console.error(e);
16967
- setAiError("Failed to generate content. Try again.");
16968
- } finally {
16969
- setIsLoadingAI(false);
16970
- }
16971
- };
16972
- return /* @__PURE__ */ React12.createElement("div", { className: "group relative space-y-3" }, /* @__PURE__ */ React12.createElement("div", { className: "flex gap-2 items-center" }, !isRecording ? /* @__PURE__ */ React12.createElement(
16939
+ if (!enabledFeatures.includes("voice to text")) return null;
16940
+ return /* @__PURE__ */ React12.createElement("div", { className: "group flex flex-col gap-2 items-start" }, /* @__PURE__ */ React12.createElement("div", { className: "flex gap-2 items-center" }, !isRecording ? /* @__PURE__ */ React12.createElement(
16973
16941
  "button",
16974
16942
  {
16975
16943
  type: "button",
@@ -16987,60 +16955,7 @@ function AiGroup({ editor }) {
16987
16955
  title: "Stop Recording"
16988
16956
  },
16989
16957
  /* @__PURE__ */ React12.createElement(FaStop, { size: 18 })
16990
- ), /* @__PURE__ */ React12.createElement(
16991
- "button",
16992
- {
16993
- type: "button",
16994
- onClick: handleAiClick,
16995
- className: "ai-button",
16996
- title: "AI Assist"
16997
- },
16998
- "AI"
16999
- )), isRecording && /* @__PURE__ */ React12.createElement("div", { className: "flex flex-col items-center" }, /* @__PURE__ */ React12.createElement(Waveform, { size: 30, lineWeight: 3.5, speed: 1, color: "#4F46E5" }), /* @__PURE__ */ React12.createElement("p", { className: "text-sm mt-1 text-gray-600" }, "Recording...")), isTranscribing && /* @__PURE__ */ React12.createElement("p", { className: "text-sm text-gray-500" }, "Transcribing..."), transcriptionError && /* @__PURE__ */ React12.createElement("p", { className: "text-sm text-red-600" }, transcriptionError), audioBlob && /* @__PURE__ */ React12.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React12.createElement("audio", { controls: true, src: URL.createObjectURL(audioBlob) })), /* @__PURE__ */ React12.createElement(AnimatePresence, null, showPromptInput && /* @__PURE__ */ React12.createElement(
17000
- motion.div,
17001
- {
17002
- className: "ai-modal-backdrop",
17003
- initial: { opacity: 0 },
17004
- animate: { opacity: 1 },
17005
- exit: { opacity: 0 }
17006
- },
17007
- /* @__PURE__ */ React12.createElement(
17008
- motion.div,
17009
- {
17010
- className: "ai-modal-content",
17011
- initial: { scale: 0.9, opacity: 0 },
17012
- animate: { scale: 1, opacity: 1 },
17013
- exit: { scale: 0.9, opacity: 0 }
17014
- },
17015
- /* @__PURE__ */ React12.createElement("h2", { className: "ai-modal-title" }, "AI Prompt"),
17016
- /* @__PURE__ */ React12.createElement(
17017
- "textarea",
17018
- {
17019
- className: "ai-modal-textarea",
17020
- value: prompt2,
17021
- onChange: (e) => setPrompt(e.target.value),
17022
- placeholder: "Enter your prompt here..."
17023
- }
17024
- ),
17025
- aiError && /* @__PURE__ */ React12.createElement("p", { className: "ai-modal-error" }, aiError),
17026
- /* @__PURE__ */ React12.createElement("div", { className: "ai-modal-actions" }, /* @__PURE__ */ React12.createElement(
17027
- "button",
17028
- {
17029
- onClick: () => setShowPromptInput(false),
17030
- className: "ai-cancel-btn"
17031
- },
17032
- "Cancel"
17033
- ), /* @__PURE__ */ React12.createElement(
17034
- "button",
17035
- {
17036
- onClick: handlePromptSubmit,
17037
- disabled: isLoadingAI,
17038
- className: "ai-submit-btn"
17039
- },
17040
- isLoadingAI ? "Generating..." : "Submit"
17041
- ))
17042
- )
17043
- )));
16958
+ )), isRecording && /* @__PURE__ */ React12.createElement("div", { className: "flex flex-col items-center" }, /* @__PURE__ */ React12.createElement(Waveform, { size: 30, lineWeight: 3.5, speed: 1, color: "#4F46E5" }), /* @__PURE__ */ React12.createElement("p", { className: "text-sm mt-1 text-gray-600" }, "Recording...")), isTranscribing && /* @__PURE__ */ React12.createElement("p", { className: "text-sm text-gray-500" }, "Transcribing..."), transcriptionError && /* @__PURE__ */ React12.createElement("p", { className: "text-sm text-red-600" }, transcriptionError), audioBlob && /* @__PURE__ */ React12.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React12.createElement("audio", { controls: true, src: URL.createObjectURL(audioBlob) })));
17044
16959
  }
17045
16960
 
17046
16961
  // src/components/tetrons/toolbar/AddOnGroup.tsx
@@ -17052,20 +16967,15 @@ var MathModal2 = dynamic(() => Promise.resolve().then(() => (init_MathModal(), M
17052
16967
  var CodeEditorModal2 = dynamic(() => Promise.resolve().then(() => (init_CodeEditorModal(), CodeEditorModal_exports)), {
17053
16968
  ssr: false
17054
16969
  });
17055
- var AddOnGroup = ({ editor }) => {
17056
- const [isModalOpen, setModalOpen] = useState9(false);
16970
+ var AddOnGroup = ({ editor, addOns }) => {
16971
+ const [isMathModalOpen, setMathModalOpen] = useState9(false);
17057
16972
  const [latexValue, setLatexValue] = useState9("");
17058
16973
  const [isCodeModalOpen, setCodeModalOpen] = useState9(false);
17059
16974
  if (!editor) return null;
17060
- const insertCodeBlock = () => {
17061
- setCodeModalOpen(true);
17062
- };
16975
+ const insertCodeBlock = () => setCodeModalOpen(true);
17063
16976
  const handleMathInsert = (latex) => {
17064
- editor.chain().focus().insertContent({
17065
- type: "mathInline",
17066
- attrs: { formula: latex }
17067
- }).run();
17068
- setModalOpen(false);
16977
+ editor.chain().focus().insertContent({ type: "mathInline", attrs: { formula: latex } }).run();
16978
+ setMathModalOpen(false);
17069
16979
  setLatexValue("");
17070
16980
  };
17071
16981
  const handleSubmitCode = (code) => {
@@ -17091,7 +17001,7 @@ var AddOnGroup = ({ editor }) => {
17091
17001
  }).run();
17092
17002
  setCodeModalOpen(false);
17093
17003
  };
17094
- return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "group flex gap-2 items-center" }, /* @__PURE__ */ React15.createElement(
17004
+ return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "group flex gap-2 items-center" }, addOns.includes("code") && /* @__PURE__ */ React15.createElement(
17095
17005
  "button",
17096
17006
  {
17097
17007
  type: "button",
@@ -17100,25 +17010,25 @@ var AddOnGroup = ({ editor }) => {
17100
17010
  title: "Open Code Editor"
17101
17011
  },
17102
17012
  /* @__PURE__ */ React15.createElement(MessageSquareCode, { size: 18 })
17103
- ), /* @__PURE__ */ React15.createElement(
17013
+ ), addOns.includes("math") && /* @__PURE__ */ React15.createElement(
17104
17014
  "button",
17105
17015
  {
17106
17016
  type: "button",
17107
- onClick: () => setModalOpen(true),
17017
+ onClick: () => setMathModalOpen(true),
17108
17018
  className: "addon-btn",
17109
17019
  title: "Insert Math Equation"
17110
17020
  },
17111
17021
  /* @__PURE__ */ React15.createElement(SquareRadical, { size: 18 })
17112
- )), /* @__PURE__ */ React15.createElement(
17022
+ )), addOns.includes("math") && /* @__PURE__ */ React15.createElement(
17113
17023
  MathModal2,
17114
17024
  {
17115
- isOpen: isModalOpen,
17116
- onClose: () => setModalOpen(false),
17025
+ isOpen: isMathModalOpen,
17026
+ onClose: () => setMathModalOpen(false),
17117
17027
  onSubmit: handleMathInsert,
17118
17028
  value: latexValue,
17119
17029
  setValue: setLatexValue
17120
17030
  }
17121
- ), /* @__PURE__ */ React15.createElement(
17031
+ ), addOns.includes("code") && /* @__PURE__ */ React15.createElement(
17122
17032
  CodeEditorModal2,
17123
17033
  {
17124
17034
  isOpen: isCodeModalOpen,
@@ -17172,7 +17082,27 @@ function TetronsToolbar({
17172
17082
  checked: autoSave,
17173
17083
  onChange: (e) => setAutoSave(e.target.checked)
17174
17084
  }
17175
- ), /* @__PURE__ */ React16.createElement("label", { htmlFor: "autoSave" }, "Auto Save")), ["pro", "premium", "platinum"].includes(version) && /* @__PURE__ */ React16.createElement(FileGroup, { editor }), /* @__PURE__ */ React16.createElement(ClipboardGroup, { editor }), /* @__PURE__ */ React16.createElement(FontStyleGroup, { editor }), /* @__PURE__ */ React16.createElement(ListAlignGroup, { editor }), ["premium", "platinum"].includes(version) && /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(InsertGroup, { editor }), /* @__PURE__ */ React16.createElement(ActionGroup, { editor })), (["pro", "premium"].includes(version) && effectiveAddOns.includes("spell check") || version === "platinum") && /* @__PURE__ */ React16.createElement(MiscGroup, { editor }), (version === "premium" && effectiveAddOns.includes("ai") || version === "platinum") && /* @__PURE__ */ React16.createElement(AiGroup, { editor }), effectiveAddOns.length > 0 && /* @__PURE__ */ React16.createElement(AddOnGroup_default, { editor }));
17085
+ ), /* @__PURE__ */ React16.createElement("label", { htmlFor: "autoSave" }, "Auto Save")), ["pro", "premium", "platinum"].includes(version) && /* @__PURE__ */ React16.createElement(FileGroup, { editor }), /* @__PURE__ */ React16.createElement(ClipboardGroup, { editor }), /* @__PURE__ */ React16.createElement(FontStyleGroup, { editor }), /* @__PURE__ */ React16.createElement(ListAlignGroup, { editor }), ["premium", "platinum"].includes(version) && /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(InsertGroup, { editor }), /* @__PURE__ */ React16.createElement(ActionGroup, { editor })), (["pro", "premium"].includes(version) || version === "platinum") && /* @__PURE__ */ React16.createElement(
17086
+ MiscGroup,
17087
+ {
17088
+ editor,
17089
+ enabledFeatures: effectiveAddOns
17090
+ }
17091
+ ), (version === "premium" && effectiveAddOns.includes("ai") || version === "platinum" || effectiveAddOns.includes("voice to text")) && /* @__PURE__ */ React16.createElement(
17092
+ AiGroup,
17093
+ {
17094
+ editor,
17095
+ enabledFeatures: effectiveAddOns.filter(
17096
+ (a) => ["ai", "voice to text"].includes(a)
17097
+ )
17098
+ }
17099
+ ), effectiveAddOns.some((a) => a === "math" || a === "code") && /* @__PURE__ */ React16.createElement(
17100
+ AddOnGroup_default,
17101
+ {
17102
+ editor,
17103
+ addOns: effectiveAddOns.filter((a) => a === "math" || a === "code")
17104
+ }
17105
+ ));
17176
17106
  }
17177
17107
 
17178
17108
  // src/components/tetrons/EditorContent.tsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tetrons",
3
- "version": "2.3.90",
3
+ "version": "2.3.91",
4
4
  "description": "A Next.js project written in TypeScript",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",