tetrons 2.3.91 → 2.3.92

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.
Files changed (3) hide show
  1. package/dist/index.cjs +197 -90
  2. package/dist/index.mjs +197 -90
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -16016,14 +16016,10 @@ function ActionGroup({ editor }) {
16016
16016
  setDropdownOpen(false);
16017
16017
  }
16018
16018
  };
16019
- if (dropdownOpen) {
16019
+ if (dropdownOpen)
16020
16020
  document.addEventListener("mousedown", handleClickOutside);
16021
- } else {
16022
- document.removeEventListener("mousedown", handleClickOutside);
16023
- }
16024
- return () => {
16025
- document.removeEventListener("mousedown", handleClickOutside);
16026
- };
16021
+ else document.removeEventListener("mousedown", handleClickOutside);
16022
+ return () => document.removeEventListener("mousedown", handleClickOutside);
16027
16023
  }, [dropdownOpen]);
16028
16024
  const zoomIn = () => {
16029
16025
  const element = document.querySelector(
@@ -16032,8 +16028,7 @@ function ActionGroup({ editor }) {
16032
16028
  if (element) {
16033
16029
  const style = element.style;
16034
16030
  const currentZoom = parseFloat(style.zoom || "1");
16035
- const next = Math.min(currentZoom + 0.1, 2);
16036
- style.zoom = next.toString();
16031
+ style.zoom = Math.min(currentZoom + 0.1, 2).toString();
16037
16032
  }
16038
16033
  };
16039
16034
  const zoomOut = () => {
@@ -16043,28 +16038,24 @@ function ActionGroup({ editor }) {
16043
16038
  if (element) {
16044
16039
  const style = element.style;
16045
16040
  const currentZoom = parseFloat(style.zoom || "1");
16046
- const next = Math.max(currentZoom - 0.1, 0.5);
16047
- style.zoom = next.toString();
16041
+ style.zoom = Math.max(currentZoom - 0.1, 0.5).toString();
16048
16042
  }
16049
16043
  };
16050
16044
  const handlePrint = () => {
16045
+ if (!editor) return;
16051
16046
  const html = editor.getHTML();
16052
16047
  const printWindow = window.open("", "_blank");
16053
16048
  if (printWindow) {
16054
- printWindow.document.write(`
16055
- <html>
16056
- <head>
16057
- <title>Print</title>
16058
- </head>
16059
- <body>${html}</body>
16060
- </html>
16061
- `);
16049
+ printWindow.document.write(
16050
+ `<html><head><title>Print</title></head><body>${html}</body></html>`
16051
+ );
16062
16052
  printWindow.document.close();
16063
16053
  printWindow.focus();
16064
16054
  printWindow.print();
16065
16055
  }
16066
16056
  };
16067
16057
  const handleSave = async () => {
16058
+ if (!editor) return;
16068
16059
  const content = editor.getJSON();
16069
16060
  try {
16070
16061
  const response = await fetch("/api/save", {
@@ -16074,14 +16065,14 @@ function ActionGroup({ editor }) {
16074
16065
  });
16075
16066
  if (!response.ok) throw new Error("Failed to save file");
16076
16067
  const result = await response.json();
16077
- console.log(result.message);
16078
- alert("Content saved successfully!");
16068
+ alert(result.message || "Content saved successfully!");
16079
16069
  } catch (error) {
16080
- console.error("Error saving content:", error);
16070
+ console.error(error);
16081
16071
  alert("Failed to save content.");
16082
16072
  }
16083
16073
  };
16084
16074
  const handleDownloadPDF = async () => {
16075
+ if (!editor) return;
16085
16076
  const html = editor.getHTML();
16086
16077
  const container = document.createElement("div");
16087
16078
  container.innerHTML = html;
@@ -16089,22 +16080,22 @@ function ActionGroup({ editor }) {
16089
16080
  document.body.appendChild(container);
16090
16081
  try {
16091
16082
  const domToPdf = (await import("dom-to-pdf")).default;
16092
- const options = {
16093
- filename: "document.pdf",
16094
- overrideWidth: 800,
16095
- overrideHeight: 1120
16096
- };
16097
- domToPdf(container, options, () => {
16098
- console.log("PDF downloaded!");
16099
- });
16083
+ domToPdf(
16084
+ container,
16085
+ { filename: "document.pdf", overrideWidth: 800, overrideHeight: 1120 },
16086
+ () => {
16087
+ console.log("PDF downloaded!");
16088
+ }
16089
+ );
16100
16090
  } catch (error) {
16101
- console.error("PDF download failed", error);
16091
+ console.error(error);
16102
16092
  alert("Failed to download PDF.");
16103
16093
  } finally {
16104
16094
  document.body.removeChild(container);
16105
16095
  }
16106
16096
  };
16107
16097
  const handleDownloadHTML = () => {
16098
+ if (!editor) return;
16108
16099
  const html = editor.getHTML();
16109
16100
  const blob = new Blob([html], { type: "text/html" });
16110
16101
  const link = document.createElement("a");
@@ -16115,15 +16106,11 @@ function ActionGroup({ editor }) {
16115
16106
  document.body.removeChild(link);
16116
16107
  };
16117
16108
  const handleDownloadDOCX = async () => {
16109
+ if (!editor) return;
16118
16110
  const { Document: Document2, Packer, Paragraph: Paragraph2 } = await import("docx");
16119
16111
  const text = editor.getText();
16120
16112
  const doc3 = new Document2({
16121
- sections: [
16122
- {
16123
- properties: {},
16124
- children: [new Paragraph2(text)]
16125
- }
16126
- ]
16113
+ sections: [{ properties: {}, children: [new Paragraph2(text)] }]
16127
16114
  });
16128
16115
  const blob = await Packer.toBlob(doc3);
16129
16116
  const link = document.createElement("a");
@@ -16133,49 +16120,59 @@ function ActionGroup({ editor }) {
16133
16120
  link.click();
16134
16121
  document.body.removeChild(link);
16135
16122
  };
16136
- return /* @__PURE__ */ import_react10.default.createElement("div", { className: "action-group", role: "group", "aria-label": "Editor actions" }, /* @__PURE__ */ import_react10.default.createElement(ToolbarButton_default, { icon: import_md.MdZoomIn, onClick: zoomIn, title: "Zoom In" }), /* @__PURE__ */ import_react10.default.createElement(ToolbarButton_default, { icon: import_md.MdZoomOut, onClick: zoomOut, title: "Zoom Out" }), /* @__PURE__ */ import_react10.default.createElement(ToolbarButton_default, { icon: import_md.MdPrint, onClick: handlePrint, title: "Print" }), /* @__PURE__ */ import_react10.default.createElement(ToolbarButton_default, { icon: import_md.MdSave, onClick: handleSave, title: "Save" }), /* @__PURE__ */ import_react10.default.createElement("div", { className: "relative", ref: dropdownRef }, /* @__PURE__ */ import_react10.default.createElement(
16137
- "button",
16138
- {
16139
- type: "button",
16140
- onClick: () => setDropdownOpen((open) => !open),
16141
- "aria-haspopup": "menu",
16142
- "aria-expanded": dropdownOpen ? "true" : "false",
16143
- className: "export-button",
16144
- title: "Export"
16145
- },
16146
- /* @__PURE__ */ import_react10.default.createElement(import_md.MdDownload, null),
16147
- /* @__PURE__ */ import_react10.default.createElement("span", { className: "text-sm" })
16148
- ), dropdownOpen && /* @__PURE__ */ import_react10.default.createElement("div", { className: "export-dropdown" }, /* @__PURE__ */ import_react10.default.createElement(
16149
- "button",
16150
- {
16151
- type: "button",
16152
- onClick: () => {
16153
- setDropdownOpen(false);
16154
- handleDownloadPDF();
16155
- }
16156
- },
16157
- "Export as PDF"
16158
- ), /* @__PURE__ */ import_react10.default.createElement(
16159
- "button",
16160
- {
16161
- type: "button",
16162
- onClick: () => {
16163
- setDropdownOpen(false);
16164
- handleDownloadHTML();
16165
- }
16166
- },
16167
- "Export as HTML"
16168
- ), /* @__PURE__ */ import_react10.default.createElement(
16169
- "button",
16123
+ if (!editor) return null;
16124
+ return /* @__PURE__ */ import_react10.default.createElement(
16125
+ "div",
16170
16126
  {
16171
- type: "button",
16172
- onClick: () => {
16173
- setDropdownOpen(false);
16174
- handleDownloadDOCX();
16175
- }
16127
+ className: "action-group bg-white text-black dark:bg-gray-800 dark:text-white",
16128
+ role: "group",
16129
+ "aria-label": "Editor actions"
16176
16130
  },
16177
- "Export as DOCX"
16178
- ))));
16131
+ /* @__PURE__ */ import_react10.default.createElement(ToolbarButton_default, { icon: import_md.MdZoomIn, onClick: zoomIn, title: "Zoom In" }),
16132
+ /* @__PURE__ */ import_react10.default.createElement(ToolbarButton_default, { icon: import_md.MdZoomOut, onClick: zoomOut, title: "Zoom Out" }),
16133
+ /* @__PURE__ */ import_react10.default.createElement(ToolbarButton_default, { icon: import_md.MdPrint, onClick: handlePrint, title: "Print" }),
16134
+ /* @__PURE__ */ import_react10.default.createElement(ToolbarButton_default, { icon: import_md.MdSave, onClick: handleSave, title: "Save" }),
16135
+ /* @__PURE__ */ import_react10.default.createElement("div", { className: "relative", ref: dropdownRef }, /* @__PURE__ */ import_react10.default.createElement(
16136
+ "button",
16137
+ {
16138
+ type: "button",
16139
+ onClick: () => setDropdownOpen((o) => !o),
16140
+ className: "export-button",
16141
+ title: "Export"
16142
+ },
16143
+ /* @__PURE__ */ import_react10.default.createElement(import_md.MdDownload, null)
16144
+ ), dropdownOpen && /* @__PURE__ */ import_react10.default.createElement("div", { className: "export-dropdown" }, /* @__PURE__ */ import_react10.default.createElement(
16145
+ "button",
16146
+ {
16147
+ type: "button",
16148
+ onClick: () => {
16149
+ setDropdownOpen(false);
16150
+ handleDownloadPDF();
16151
+ }
16152
+ },
16153
+ "Export as PDF"
16154
+ ), /* @__PURE__ */ import_react10.default.createElement(
16155
+ "button",
16156
+ {
16157
+ type: "button",
16158
+ onClick: () => {
16159
+ setDropdownOpen(false);
16160
+ handleDownloadHTML();
16161
+ }
16162
+ },
16163
+ "Export as HTML"
16164
+ ), /* @__PURE__ */ import_react10.default.createElement(
16165
+ "button",
16166
+ {
16167
+ type: "button",
16168
+ onClick: () => {
16169
+ setDropdownOpen(false);
16170
+ handleDownloadDOCX();
16171
+ }
16172
+ },
16173
+ "Export as DOCX"
16174
+ )))
16175
+ );
16179
16176
  }
16180
16177
 
16181
16178
  // src/components/tetrons/toolbar/ClipboardGroup.tsx
@@ -16638,13 +16635,18 @@ function InsertGroup({ editor }) {
16638
16635
  var import_react15 = __toESM(require("react"));
16639
16636
  var import_md5 = require("react-icons/md");
16640
16637
  function ListAlignGroup({ editor }) {
16641
- return /* @__PURE__ */ import_react15.default.createElement("div", { className: "list-align-group" }, /* @__PURE__ */ import_react15.default.createElement(
16638
+ if (!editor) return null;
16639
+ const canToggleBullet = editor.can().toggleBulletList() || editor.isEmpty;
16640
+ const canToggleOrdered = editor.can().toggleOrderedList() || editor.isEmpty;
16641
+ const canSink = editor.can().sinkListItem("listItem");
16642
+ const canLift = editor.can().liftListItem("listItem");
16643
+ return /* @__PURE__ */ import_react15.default.createElement("div", { className: "list-align-group bg-white text-black dark:bg-gray-800 dark:text-white" }, /* @__PURE__ */ import_react15.default.createElement(
16642
16644
  ToolbarButton_default,
16643
16645
  {
16644
16646
  icon: import_md5.MdFormatListBulleted,
16645
16647
  title: "Bulleted List",
16646
16648
  onClick: () => editor.chain().focus().toggleBulletList().run(),
16647
- disabled: !editor.can().toggleBulletList()
16649
+ disabled: !canToggleBullet
16648
16650
  }
16649
16651
  ), /* @__PURE__ */ import_react15.default.createElement(
16650
16652
  ToolbarButton_default,
@@ -16652,7 +16654,7 @@ function ListAlignGroup({ editor }) {
16652
16654
  icon: import_md5.MdFormatListNumbered,
16653
16655
  title: "Numbered List",
16654
16656
  onClick: () => editor.chain().focus().toggleOrderedList().run(),
16655
- disabled: !editor.can().toggleOrderedList()
16657
+ disabled: !canToggleOrdered
16656
16658
  }
16657
16659
  ), /* @__PURE__ */ import_react15.default.createElement(
16658
16660
  ToolbarButton_default,
@@ -16660,7 +16662,7 @@ function ListAlignGroup({ editor }) {
16660
16662
  icon: import_md5.MdFormatIndentIncrease,
16661
16663
  title: "Increase Indent",
16662
16664
  onClick: () => editor.chain().focus().sinkListItem("listItem").run(),
16663
- disabled: !editor.can().sinkListItem("listItem")
16665
+ disabled: !canSink
16664
16666
  }
16665
16667
  ), /* @__PURE__ */ import_react15.default.createElement(
16666
16668
  ToolbarButton_default,
@@ -16668,7 +16670,7 @@ function ListAlignGroup({ editor }) {
16668
16670
  icon: import_md5.MdFormatIndentDecrease,
16669
16671
  title: "Decrease Indent",
16670
16672
  onClick: () => editor.chain().focus().liftListItem("listItem").run(),
16671
- disabled: !editor.can().liftListItem("listItem")
16673
+ disabled: !canLift
16672
16674
  }
16673
16675
  ), /* @__PURE__ */ import_react15.default.createElement(
16674
16676
  ToolbarButton_default,
@@ -16876,11 +16878,16 @@ function FileGroup({ editor }) {
16876
16878
  var import_react18 = __toESM(require("react"));
16877
16879
  var import_fa2 = require("react-icons/fa");
16878
16880
  var import_loaders = require("@uiball/loaders");
16881
+ var import_framer_motion = require("framer-motion");
16879
16882
  function AiGroup({ editor, enabledFeatures }) {
16880
16883
  const [isRecording, setIsRecording] = (0, import_react18.useState)(false);
16881
16884
  const [audioBlob, setAudioBlob] = (0, import_react18.useState)(null);
16882
16885
  const [isTranscribing, setIsTranscribing] = (0, import_react18.useState)(false);
16883
16886
  const [transcriptionError, setTranscriptionError] = (0, import_react18.useState)("");
16887
+ const [showPromptInput, setShowPromptInput] = (0, import_react18.useState)(false);
16888
+ const [prompt2, setPrompt] = (0, import_react18.useState)("");
16889
+ const [isLoadingAI, setIsLoadingAI] = (0, import_react18.useState)(false);
16890
+ const [aiError, setAiError] = (0, import_react18.useState)("");
16884
16891
  const mediaRecorderRef = (0, import_react18.useRef)(null);
16885
16892
  const chunksRef = (0, import_react18.useRef)([]);
16886
16893
  const startRecording = async () => {
@@ -16927,6 +16934,34 @@ function AiGroup({ editor, enabledFeatures }) {
16927
16934
  setIsTranscribing(false);
16928
16935
  }
16929
16936
  };
16937
+ const handleAiClick = () => {
16938
+ setShowPromptInput(true);
16939
+ setPrompt("");
16940
+ setAiError("");
16941
+ };
16942
+ const handlePromptSubmit = async () => {
16943
+ if (!prompt2.trim()) return;
16944
+ setIsLoadingAI(true);
16945
+ setAiError("");
16946
+ try {
16947
+ const res = await fetch("https://staging.tetrons.com/api/ai-action", {
16948
+ method: "POST",
16949
+ headers: { "Content-Type": "application/json" },
16950
+ body: JSON.stringify({ content: prompt2 })
16951
+ });
16952
+ const data = await res.json();
16953
+ if (!res.ok || !data.response) {
16954
+ throw new Error(data.error || "AI failed to generate content");
16955
+ }
16956
+ editor.commands.insertContent(data.response);
16957
+ setShowPromptInput(false);
16958
+ } catch (e) {
16959
+ console.error(e);
16960
+ setAiError("Failed to generate content. Try again.");
16961
+ } finally {
16962
+ setIsLoadingAI(false);
16963
+ }
16964
+ };
16930
16965
  if (!enabledFeatures.includes("voice to text")) return null;
16931
16966
  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(
16932
16967
  "button",
@@ -16946,7 +16981,60 @@ function AiGroup({ editor, enabledFeatures }) {
16946
16981
  title: "Stop Recording"
16947
16982
  },
16948
16983
  /* @__PURE__ */ import_react18.default.createElement(import_fa2.FaStop, { size: 18 })
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) })));
16984
+ ), /* @__PURE__ */ import_react18.default.createElement(
16985
+ "button",
16986
+ {
16987
+ type: "button",
16988
+ onClick: handleAiClick,
16989
+ className: "ai-button",
16990
+ title: "AI Assist"
16991
+ },
16992
+ "AI"
16993
+ )), 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(
16994
+ import_framer_motion.motion.div,
16995
+ {
16996
+ className: "ai-modal-backdrop",
16997
+ initial: { opacity: 0 },
16998
+ animate: { opacity: 1 },
16999
+ exit: { opacity: 0 }
17000
+ },
17001
+ /* @__PURE__ */ import_react18.default.createElement(
17002
+ import_framer_motion.motion.div,
17003
+ {
17004
+ className: "ai-modal-content",
17005
+ initial: { scale: 0.9, opacity: 0 },
17006
+ animate: { scale: 1, opacity: 1 },
17007
+ exit: { scale: 0.9, opacity: 0 }
17008
+ },
17009
+ /* @__PURE__ */ import_react18.default.createElement("h2", { className: "ai-modal-title" }, "AI Prompt"),
17010
+ /* @__PURE__ */ import_react18.default.createElement(
17011
+ "textarea",
17012
+ {
17013
+ className: "ai-modal-textarea",
17014
+ value: prompt2,
17015
+ onChange: (e) => setPrompt(e.target.value),
17016
+ placeholder: "Enter your prompt here..."
17017
+ }
17018
+ ),
17019
+ aiError && /* @__PURE__ */ import_react18.default.createElement("p", { className: "ai-modal-error" }, aiError),
17020
+ /* @__PURE__ */ import_react18.default.createElement("div", { className: "ai-modal-actions" }, /* @__PURE__ */ import_react18.default.createElement(
17021
+ "button",
17022
+ {
17023
+ onClick: () => setShowPromptInput(false),
17024
+ className: "ai-cancel-btn"
17025
+ },
17026
+ "Cancel"
17027
+ ), /* @__PURE__ */ import_react18.default.createElement(
17028
+ "button",
17029
+ {
17030
+ onClick: handlePromptSubmit,
17031
+ disabled: isLoadingAI,
17032
+ className: "ai-submit-btn"
17033
+ },
17034
+ isLoadingAI ? "Generating..." : "Submit"
17035
+ ))
17036
+ )
17037
+ )));
16950
17038
  }
16951
17039
 
16952
17040
  // src/components/tetrons/toolbar/AddOnGroup.tsx
@@ -17039,9 +17127,8 @@ function TetronsToolbar({
17039
17127
  }) {
17040
17128
  const [autoSave, setAutoSave] = (0, import_react23.useState)(false);
17041
17129
  (0, import_react23.useEffect)(() => {
17042
- if (!editor) return;
17130
+ if (!editor || !autoSave) return;
17043
17131
  const handleUpdate = () => {
17044
- if (!autoSave) return;
17045
17132
  const content = editor.getJSON();
17046
17133
  fetch("/api/save", {
17047
17134
  method: "POST",
@@ -17060,7 +17147,18 @@ function TetronsToolbar({
17060
17147
  case "premium":
17061
17148
  return addOns;
17062
17149
  case "platinum":
17063
- return ["math", "code"];
17150
+ return [
17151
+ "math",
17152
+ "code",
17153
+ "ai",
17154
+ "voice to text",
17155
+ "undo",
17156
+ "redo",
17157
+ "resetFormatting",
17158
+ "preview",
17159
+ "codeBlock",
17160
+ "spell check"
17161
+ ];
17064
17162
  default:
17065
17163
  return [];
17066
17164
  }
@@ -17073,13 +17171,22 @@ function TetronsToolbar({
17073
17171
  checked: autoSave,
17074
17172
  onChange: (e) => setAutoSave(e.target.checked)
17075
17173
  }
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(
17174
+ ), /* @__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", "platinum"].includes(version) && /* @__PURE__ */ import_react23.default.createElement(
17077
17175
  MiscGroup,
17078
17176
  {
17079
17177
  editor,
17080
- enabledFeatures: effectiveAddOns
17178
+ enabledFeatures: effectiveAddOns.filter(
17179
+ (a) => [
17180
+ "undo",
17181
+ "redo",
17182
+ "resetFormatting",
17183
+ "preview",
17184
+ "codeBlock",
17185
+ "spell check"
17186
+ ].includes(a)
17187
+ )
17081
17188
  }
17082
- ), (version === "premium" && effectiveAddOns.includes("ai") || version === "platinum" || effectiveAddOns.includes("voice to text")) && /* @__PURE__ */ import_react23.default.createElement(
17189
+ ), effectiveAddOns.some((a) => ["ai", "voice to text"].includes(a)) && /* @__PURE__ */ import_react23.default.createElement(
17083
17190
  AiGroup,
17084
17191
  {
17085
17192
  editor,
package/dist/index.mjs CHANGED
@@ -15986,14 +15986,10 @@ function ActionGroup({ editor }) {
15986
15986
  setDropdownOpen(false);
15987
15987
  }
15988
15988
  };
15989
- if (dropdownOpen) {
15989
+ if (dropdownOpen)
15990
15990
  document.addEventListener("mousedown", handleClickOutside);
15991
- } else {
15992
- document.removeEventListener("mousedown", handleClickOutside);
15993
- }
15994
- return () => {
15995
- document.removeEventListener("mousedown", handleClickOutside);
15996
- };
15991
+ else document.removeEventListener("mousedown", handleClickOutside);
15992
+ return () => document.removeEventListener("mousedown", handleClickOutside);
15997
15993
  }, [dropdownOpen]);
15998
15994
  const zoomIn = () => {
15999
15995
  const element = document.querySelector(
@@ -16002,8 +15998,7 @@ function ActionGroup({ editor }) {
16002
15998
  if (element) {
16003
15999
  const style = element.style;
16004
16000
  const currentZoom = parseFloat(style.zoom || "1");
16005
- const next = Math.min(currentZoom + 0.1, 2);
16006
- style.zoom = next.toString();
16001
+ style.zoom = Math.min(currentZoom + 0.1, 2).toString();
16007
16002
  }
16008
16003
  };
16009
16004
  const zoomOut = () => {
@@ -16013,28 +16008,24 @@ function ActionGroup({ editor }) {
16013
16008
  if (element) {
16014
16009
  const style = element.style;
16015
16010
  const currentZoom = parseFloat(style.zoom || "1");
16016
- const next = Math.max(currentZoom - 0.1, 0.5);
16017
- style.zoom = next.toString();
16011
+ style.zoom = Math.max(currentZoom - 0.1, 0.5).toString();
16018
16012
  }
16019
16013
  };
16020
16014
  const handlePrint = () => {
16015
+ if (!editor) return;
16021
16016
  const html = editor.getHTML();
16022
16017
  const printWindow = window.open("", "_blank");
16023
16018
  if (printWindow) {
16024
- printWindow.document.write(`
16025
- <html>
16026
- <head>
16027
- <title>Print</title>
16028
- </head>
16029
- <body>${html}</body>
16030
- </html>
16031
- `);
16019
+ printWindow.document.write(
16020
+ `<html><head><title>Print</title></head><body>${html}</body></html>`
16021
+ );
16032
16022
  printWindow.document.close();
16033
16023
  printWindow.focus();
16034
16024
  printWindow.print();
16035
16025
  }
16036
16026
  };
16037
16027
  const handleSave = async () => {
16028
+ if (!editor) return;
16038
16029
  const content = editor.getJSON();
16039
16030
  try {
16040
16031
  const response = await fetch("/api/save", {
@@ -16044,14 +16035,14 @@ function ActionGroup({ editor }) {
16044
16035
  });
16045
16036
  if (!response.ok) throw new Error("Failed to save file");
16046
16037
  const result = await response.json();
16047
- console.log(result.message);
16048
- alert("Content saved successfully!");
16038
+ alert(result.message || "Content saved successfully!");
16049
16039
  } catch (error) {
16050
- console.error("Error saving content:", error);
16040
+ console.error(error);
16051
16041
  alert("Failed to save content.");
16052
16042
  }
16053
16043
  };
16054
16044
  const handleDownloadPDF = async () => {
16045
+ if (!editor) return;
16055
16046
  const html = editor.getHTML();
16056
16047
  const container = document.createElement("div");
16057
16048
  container.innerHTML = html;
@@ -16059,22 +16050,22 @@ function ActionGroup({ editor }) {
16059
16050
  document.body.appendChild(container);
16060
16051
  try {
16061
16052
  const domToPdf = (await import("dom-to-pdf")).default;
16062
- const options = {
16063
- filename: "document.pdf",
16064
- overrideWidth: 800,
16065
- overrideHeight: 1120
16066
- };
16067
- domToPdf(container, options, () => {
16068
- console.log("PDF downloaded!");
16069
- });
16053
+ domToPdf(
16054
+ container,
16055
+ { filename: "document.pdf", overrideWidth: 800, overrideHeight: 1120 },
16056
+ () => {
16057
+ console.log("PDF downloaded!");
16058
+ }
16059
+ );
16070
16060
  } catch (error) {
16071
- console.error("PDF download failed", error);
16061
+ console.error(error);
16072
16062
  alert("Failed to download PDF.");
16073
16063
  } finally {
16074
16064
  document.body.removeChild(container);
16075
16065
  }
16076
16066
  };
16077
16067
  const handleDownloadHTML = () => {
16068
+ if (!editor) return;
16078
16069
  const html = editor.getHTML();
16079
16070
  const blob = new Blob([html], { type: "text/html" });
16080
16071
  const link = document.createElement("a");
@@ -16085,15 +16076,11 @@ function ActionGroup({ editor }) {
16085
16076
  document.body.removeChild(link);
16086
16077
  };
16087
16078
  const handleDownloadDOCX = async () => {
16079
+ if (!editor) return;
16088
16080
  const { Document: Document2, Packer, Paragraph: Paragraph2 } = await import("docx");
16089
16081
  const text = editor.getText();
16090
16082
  const doc3 = new Document2({
16091
- sections: [
16092
- {
16093
- properties: {},
16094
- children: [new Paragraph2(text)]
16095
- }
16096
- ]
16083
+ sections: [{ properties: {}, children: [new Paragraph2(text)] }]
16097
16084
  });
16098
16085
  const blob = await Packer.toBlob(doc3);
16099
16086
  const link = document.createElement("a");
@@ -16103,49 +16090,59 @@ function ActionGroup({ editor }) {
16103
16090
  link.click();
16104
16091
  document.body.removeChild(link);
16105
16092
  };
16106
- return /* @__PURE__ */ React5.createElement("div", { className: "action-group", role: "group", "aria-label": "Editor actions" }, /* @__PURE__ */ React5.createElement(ToolbarButton_default, { icon: MdZoomIn, onClick: zoomIn, title: "Zoom In" }), /* @__PURE__ */ React5.createElement(ToolbarButton_default, { icon: MdZoomOut, onClick: zoomOut, title: "Zoom Out" }), /* @__PURE__ */ React5.createElement(ToolbarButton_default, { icon: MdPrint, onClick: handlePrint, title: "Print" }), /* @__PURE__ */ React5.createElement(ToolbarButton_default, { icon: MdSave, onClick: handleSave, title: "Save" }), /* @__PURE__ */ React5.createElement("div", { className: "relative", ref: dropdownRef }, /* @__PURE__ */ React5.createElement(
16107
- "button",
16108
- {
16109
- type: "button",
16110
- onClick: () => setDropdownOpen((open) => !open),
16111
- "aria-haspopup": "menu",
16112
- "aria-expanded": dropdownOpen ? "true" : "false",
16113
- className: "export-button",
16114
- title: "Export"
16115
- },
16116
- /* @__PURE__ */ React5.createElement(MdDownload, null),
16117
- /* @__PURE__ */ React5.createElement("span", { className: "text-sm" })
16118
- ), dropdownOpen && /* @__PURE__ */ React5.createElement("div", { className: "export-dropdown" }, /* @__PURE__ */ React5.createElement(
16119
- "button",
16120
- {
16121
- type: "button",
16122
- onClick: () => {
16123
- setDropdownOpen(false);
16124
- handleDownloadPDF();
16125
- }
16126
- },
16127
- "Export as PDF"
16128
- ), /* @__PURE__ */ React5.createElement(
16129
- "button",
16130
- {
16131
- type: "button",
16132
- onClick: () => {
16133
- setDropdownOpen(false);
16134
- handleDownloadHTML();
16135
- }
16136
- },
16137
- "Export as HTML"
16138
- ), /* @__PURE__ */ React5.createElement(
16139
- "button",
16093
+ if (!editor) return null;
16094
+ return /* @__PURE__ */ React5.createElement(
16095
+ "div",
16140
16096
  {
16141
- type: "button",
16142
- onClick: () => {
16143
- setDropdownOpen(false);
16144
- handleDownloadDOCX();
16145
- }
16097
+ className: "action-group bg-white text-black dark:bg-gray-800 dark:text-white",
16098
+ role: "group",
16099
+ "aria-label": "Editor actions"
16146
16100
  },
16147
- "Export as DOCX"
16148
- ))));
16101
+ /* @__PURE__ */ React5.createElement(ToolbarButton_default, { icon: MdZoomIn, onClick: zoomIn, title: "Zoom In" }),
16102
+ /* @__PURE__ */ React5.createElement(ToolbarButton_default, { icon: MdZoomOut, onClick: zoomOut, title: "Zoom Out" }),
16103
+ /* @__PURE__ */ React5.createElement(ToolbarButton_default, { icon: MdPrint, onClick: handlePrint, title: "Print" }),
16104
+ /* @__PURE__ */ React5.createElement(ToolbarButton_default, { icon: MdSave, onClick: handleSave, title: "Save" }),
16105
+ /* @__PURE__ */ React5.createElement("div", { className: "relative", ref: dropdownRef }, /* @__PURE__ */ React5.createElement(
16106
+ "button",
16107
+ {
16108
+ type: "button",
16109
+ onClick: () => setDropdownOpen((o) => !o),
16110
+ className: "export-button",
16111
+ title: "Export"
16112
+ },
16113
+ /* @__PURE__ */ React5.createElement(MdDownload, null)
16114
+ ), dropdownOpen && /* @__PURE__ */ React5.createElement("div", { className: "export-dropdown" }, /* @__PURE__ */ React5.createElement(
16115
+ "button",
16116
+ {
16117
+ type: "button",
16118
+ onClick: () => {
16119
+ setDropdownOpen(false);
16120
+ handleDownloadPDF();
16121
+ }
16122
+ },
16123
+ "Export as PDF"
16124
+ ), /* @__PURE__ */ React5.createElement(
16125
+ "button",
16126
+ {
16127
+ type: "button",
16128
+ onClick: () => {
16129
+ setDropdownOpen(false);
16130
+ handleDownloadHTML();
16131
+ }
16132
+ },
16133
+ "Export as HTML"
16134
+ ), /* @__PURE__ */ React5.createElement(
16135
+ "button",
16136
+ {
16137
+ type: "button",
16138
+ onClick: () => {
16139
+ setDropdownOpen(false);
16140
+ handleDownloadDOCX();
16141
+ }
16142
+ },
16143
+ "Export as DOCX"
16144
+ )))
16145
+ );
16149
16146
  }
16150
16147
 
16151
16148
  // src/components/tetrons/toolbar/ClipboardGroup.tsx
@@ -16640,13 +16637,18 @@ import {
16640
16637
  MdFormatAlignJustify
16641
16638
  } from "react-icons/md";
16642
16639
  function ListAlignGroup({ editor }) {
16643
- return /* @__PURE__ */ React9.createElement("div", { className: "list-align-group" }, /* @__PURE__ */ React9.createElement(
16640
+ if (!editor) return null;
16641
+ const canToggleBullet = editor.can().toggleBulletList() || editor.isEmpty;
16642
+ const canToggleOrdered = editor.can().toggleOrderedList() || editor.isEmpty;
16643
+ const canSink = editor.can().sinkListItem("listItem");
16644
+ const canLift = editor.can().liftListItem("listItem");
16645
+ return /* @__PURE__ */ React9.createElement("div", { className: "list-align-group bg-white text-black dark:bg-gray-800 dark:text-white" }, /* @__PURE__ */ React9.createElement(
16644
16646
  ToolbarButton_default,
16645
16647
  {
16646
16648
  icon: MdFormatListBulleted,
16647
16649
  title: "Bulleted List",
16648
16650
  onClick: () => editor.chain().focus().toggleBulletList().run(),
16649
- disabled: !editor.can().toggleBulletList()
16651
+ disabled: !canToggleBullet
16650
16652
  }
16651
16653
  ), /* @__PURE__ */ React9.createElement(
16652
16654
  ToolbarButton_default,
@@ -16654,7 +16656,7 @@ function ListAlignGroup({ editor }) {
16654
16656
  icon: MdFormatListNumbered,
16655
16657
  title: "Numbered List",
16656
16658
  onClick: () => editor.chain().focus().toggleOrderedList().run(),
16657
- disabled: !editor.can().toggleOrderedList()
16659
+ disabled: !canToggleOrdered
16658
16660
  }
16659
16661
  ), /* @__PURE__ */ React9.createElement(
16660
16662
  ToolbarButton_default,
@@ -16662,7 +16664,7 @@ function ListAlignGroup({ editor }) {
16662
16664
  icon: MdFormatIndentIncrease,
16663
16665
  title: "Increase Indent",
16664
16666
  onClick: () => editor.chain().focus().sinkListItem("listItem").run(),
16665
- disabled: !editor.can().sinkListItem("listItem")
16667
+ disabled: !canSink
16666
16668
  }
16667
16669
  ), /* @__PURE__ */ React9.createElement(
16668
16670
  ToolbarButton_default,
@@ -16670,7 +16672,7 @@ function ListAlignGroup({ editor }) {
16670
16672
  icon: MdFormatIndentDecrease,
16671
16673
  title: "Decrease Indent",
16672
16674
  onClick: () => editor.chain().focus().liftListItem("listItem").run(),
16673
- disabled: !editor.can().liftListItem("listItem")
16675
+ disabled: !canLift
16674
16676
  }
16675
16677
  ), /* @__PURE__ */ React9.createElement(
16676
16678
  ToolbarButton_default,
@@ -16885,11 +16887,16 @@ function FileGroup({ editor }) {
16885
16887
  import React12, { useState as useState7, useRef as useRef6 } from "react";
16886
16888
  import { FaMicrophone, FaStop } from "react-icons/fa";
16887
16889
  import { Waveform } from "@uiball/loaders";
16890
+ import { motion, AnimatePresence } from "framer-motion";
16888
16891
  function AiGroup({ editor, enabledFeatures }) {
16889
16892
  const [isRecording, setIsRecording] = useState7(false);
16890
16893
  const [audioBlob, setAudioBlob] = useState7(null);
16891
16894
  const [isTranscribing, setIsTranscribing] = useState7(false);
16892
16895
  const [transcriptionError, setTranscriptionError] = useState7("");
16896
+ const [showPromptInput, setShowPromptInput] = useState7(false);
16897
+ const [prompt2, setPrompt] = useState7("");
16898
+ const [isLoadingAI, setIsLoadingAI] = useState7(false);
16899
+ const [aiError, setAiError] = useState7("");
16893
16900
  const mediaRecorderRef = useRef6(null);
16894
16901
  const chunksRef = useRef6([]);
16895
16902
  const startRecording = async () => {
@@ -16936,6 +16943,34 @@ function AiGroup({ editor, enabledFeatures }) {
16936
16943
  setIsTranscribing(false);
16937
16944
  }
16938
16945
  };
16946
+ const handleAiClick = () => {
16947
+ setShowPromptInput(true);
16948
+ setPrompt("");
16949
+ setAiError("");
16950
+ };
16951
+ const handlePromptSubmit = async () => {
16952
+ if (!prompt2.trim()) return;
16953
+ setIsLoadingAI(true);
16954
+ setAiError("");
16955
+ try {
16956
+ const res = await fetch("https://staging.tetrons.com/api/ai-action", {
16957
+ method: "POST",
16958
+ headers: { "Content-Type": "application/json" },
16959
+ body: JSON.stringify({ content: prompt2 })
16960
+ });
16961
+ const data = await res.json();
16962
+ if (!res.ok || !data.response) {
16963
+ throw new Error(data.error || "AI failed to generate content");
16964
+ }
16965
+ editor.commands.insertContent(data.response);
16966
+ setShowPromptInput(false);
16967
+ } catch (e) {
16968
+ console.error(e);
16969
+ setAiError("Failed to generate content. Try again.");
16970
+ } finally {
16971
+ setIsLoadingAI(false);
16972
+ }
16973
+ };
16939
16974
  if (!enabledFeatures.includes("voice to text")) return null;
16940
16975
  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(
16941
16976
  "button",
@@ -16955,7 +16990,60 @@ function AiGroup({ editor, enabledFeatures }) {
16955
16990
  title: "Stop Recording"
16956
16991
  },
16957
16992
  /* @__PURE__ */ React12.createElement(FaStop, { size: 18 })
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) })));
16993
+ ), /* @__PURE__ */ React12.createElement(
16994
+ "button",
16995
+ {
16996
+ type: "button",
16997
+ onClick: handleAiClick,
16998
+ className: "ai-button",
16999
+ title: "AI Assist"
17000
+ },
17001
+ "AI"
17002
+ )), 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(
17003
+ motion.div,
17004
+ {
17005
+ className: "ai-modal-backdrop",
17006
+ initial: { opacity: 0 },
17007
+ animate: { opacity: 1 },
17008
+ exit: { opacity: 0 }
17009
+ },
17010
+ /* @__PURE__ */ React12.createElement(
17011
+ motion.div,
17012
+ {
17013
+ className: "ai-modal-content",
17014
+ initial: { scale: 0.9, opacity: 0 },
17015
+ animate: { scale: 1, opacity: 1 },
17016
+ exit: { scale: 0.9, opacity: 0 }
17017
+ },
17018
+ /* @__PURE__ */ React12.createElement("h2", { className: "ai-modal-title" }, "AI Prompt"),
17019
+ /* @__PURE__ */ React12.createElement(
17020
+ "textarea",
17021
+ {
17022
+ className: "ai-modal-textarea",
17023
+ value: prompt2,
17024
+ onChange: (e) => setPrompt(e.target.value),
17025
+ placeholder: "Enter your prompt here..."
17026
+ }
17027
+ ),
17028
+ aiError && /* @__PURE__ */ React12.createElement("p", { className: "ai-modal-error" }, aiError),
17029
+ /* @__PURE__ */ React12.createElement("div", { className: "ai-modal-actions" }, /* @__PURE__ */ React12.createElement(
17030
+ "button",
17031
+ {
17032
+ onClick: () => setShowPromptInput(false),
17033
+ className: "ai-cancel-btn"
17034
+ },
17035
+ "Cancel"
17036
+ ), /* @__PURE__ */ React12.createElement(
17037
+ "button",
17038
+ {
17039
+ onClick: handlePromptSubmit,
17040
+ disabled: isLoadingAI,
17041
+ className: "ai-submit-btn"
17042
+ },
17043
+ isLoadingAI ? "Generating..." : "Submit"
17044
+ ))
17045
+ )
17046
+ )));
16959
17047
  }
16960
17048
 
16961
17049
  // src/components/tetrons/toolbar/AddOnGroup.tsx
@@ -17048,9 +17136,8 @@ function TetronsToolbar({
17048
17136
  }) {
17049
17137
  const [autoSave, setAutoSave] = useState10(false);
17050
17138
  useEffect8(() => {
17051
- if (!editor) return;
17139
+ if (!editor || !autoSave) return;
17052
17140
  const handleUpdate = () => {
17053
- if (!autoSave) return;
17054
17141
  const content = editor.getJSON();
17055
17142
  fetch("/api/save", {
17056
17143
  method: "POST",
@@ -17069,7 +17156,18 @@ function TetronsToolbar({
17069
17156
  case "premium":
17070
17157
  return addOns;
17071
17158
  case "platinum":
17072
- return ["math", "code"];
17159
+ return [
17160
+ "math",
17161
+ "code",
17162
+ "ai",
17163
+ "voice to text",
17164
+ "undo",
17165
+ "redo",
17166
+ "resetFormatting",
17167
+ "preview",
17168
+ "codeBlock",
17169
+ "spell check"
17170
+ ];
17073
17171
  default:
17074
17172
  return [];
17075
17173
  }
@@ -17082,13 +17180,22 @@ function TetronsToolbar({
17082
17180
  checked: autoSave,
17083
17181
  onChange: (e) => setAutoSave(e.target.checked)
17084
17182
  }
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(
17183
+ ), /* @__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", "platinum"].includes(version) && /* @__PURE__ */ React16.createElement(
17086
17184
  MiscGroup,
17087
17185
  {
17088
17186
  editor,
17089
- enabledFeatures: effectiveAddOns
17187
+ enabledFeatures: effectiveAddOns.filter(
17188
+ (a) => [
17189
+ "undo",
17190
+ "redo",
17191
+ "resetFormatting",
17192
+ "preview",
17193
+ "codeBlock",
17194
+ "spell check"
17195
+ ].includes(a)
17196
+ )
17090
17197
  }
17091
- ), (version === "premium" && effectiveAddOns.includes("ai") || version === "platinum" || effectiveAddOns.includes("voice to text")) && /* @__PURE__ */ React16.createElement(
17198
+ ), effectiveAddOns.some((a) => ["ai", "voice to text"].includes(a)) && /* @__PURE__ */ React16.createElement(
17092
17199
  AiGroup,
17093
17200
  {
17094
17201
  editor,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tetrons",
3
- "version": "2.3.91",
3
+ "version": "2.3.92",
4
4
  "description": "A Next.js project written in TypeScript",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",