tetrons 2.3.90 → 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.
@@ -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
@@ -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,
@@ -16729,26 +16731,7 @@ async function checkGrammar(text) {
16729
16731
  }
16730
16732
 
16731
16733
  // 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
- };
16734
+ function MiscGroup({ editor, enabledFeatures }) {
16752
16735
  const handleGrammarCheck = async () => {
16753
16736
  const text = editor.getText();
16754
16737
  try {
@@ -16772,7 +16755,26 @@ Reason: ${issue.message}
16772
16755
  alert("\u274C Failed to check grammar. Please try again later.");
16773
16756
  }
16774
16757
  };
16775
- return /* @__PURE__ */ import_react16.default.createElement("div", { className: "misc-group" }, /* @__PURE__ */ import_react16.default.createElement(
16758
+ const handlePreview = () => {
16759
+ const html = editor.getHTML();
16760
+ const previewWindow = window.open("", "_blank");
16761
+ if (previewWindow) {
16762
+ previewWindow.document.open();
16763
+ previewWindow.document.write(`
16764
+ <html>
16765
+ <head>
16766
+ <title>Preview</title>
16767
+ <style>
16768
+ body { font-family: sans-serif; padding: 2rem; line-height: 1.6; }
16769
+ </style>
16770
+ </head>
16771
+ <body>${html}</body>
16772
+ </html>
16773
+ `);
16774
+ previewWindow.document.close();
16775
+ }
16776
+ };
16777
+ return /* @__PURE__ */ import_react16.default.createElement("div", { className: "misc-group" }, enabledFeatures.includes("undo") && /* @__PURE__ */ import_react16.default.createElement(
16776
16778
  ToolbarButton_default,
16777
16779
  {
16778
16780
  icon: import_md6.MdUndo,
@@ -16780,7 +16782,7 @@ Reason: ${issue.message}
16780
16782
  onClick: () => editor.chain().focus().undo().run(),
16781
16783
  disabled: !editor.can().undo()
16782
16784
  }
16783
- ), /* @__PURE__ */ import_react16.default.createElement(
16785
+ ), enabledFeatures.includes("redo") && /* @__PURE__ */ import_react16.default.createElement(
16784
16786
  ToolbarButton_default,
16785
16787
  {
16786
16788
  icon: import_md6.MdRedo,
@@ -16788,14 +16790,14 @@ Reason: ${issue.message}
16788
16790
  onClick: () => editor.chain().focus().redo().run(),
16789
16791
  disabled: !editor.can().redo()
16790
16792
  }
16791
- ), /* @__PURE__ */ import_react16.default.createElement(
16793
+ ), enabledFeatures.includes("resetFormatting") && /* @__PURE__ */ import_react16.default.createElement(
16792
16794
  ToolbarButton_default,
16793
16795
  {
16794
16796
  icon: import_md6.MdRefresh,
16795
16797
  label: "Reset Formatting",
16796
16798
  onClick: () => editor.chain().focus().unsetAllMarks().clearNodes().run()
16797
16799
  }
16798
- ), /* @__PURE__ */ import_react16.default.createElement(
16800
+ ), enabledFeatures.includes("codeBlock") && /* @__PURE__ */ import_react16.default.createElement(
16799
16801
  ToolbarButton_default,
16800
16802
  {
16801
16803
  icon: import_md6.MdCode,
@@ -16803,14 +16805,14 @@ Reason: ${issue.message}
16803
16805
  onClick: () => editor.chain().focus().toggleCodeBlock().run(),
16804
16806
  isActive: editor.isActive("codeBlock")
16805
16807
  }
16806
- ), /* @__PURE__ */ import_react16.default.createElement(
16808
+ ), enabledFeatures.includes("preview") && /* @__PURE__ */ import_react16.default.createElement(
16807
16809
  ToolbarButton_default,
16808
16810
  {
16809
16811
  icon: import_md6.MdVisibility,
16810
16812
  label: "Preview",
16811
16813
  onClick: handlePreview
16812
16814
  }
16813
- ), /* @__PURE__ */ import_react16.default.createElement(
16815
+ ), enabledFeatures.includes("spell check") && /* @__PURE__ */ import_react16.default.createElement(
16814
16816
  ToolbarButton_default,
16815
16817
  {
16816
16818
  icon: import_md6.MdSpellcheck,
@@ -16877,7 +16879,7 @@ var import_react18 = __toESM(require("react"));
16877
16879
  var import_fa2 = require("react-icons/fa");
16878
16880
  var import_loaders = require("@uiball/loaders");
16879
16881
  var import_framer_motion = require("framer-motion");
16880
- function AiGroup({ editor }) {
16882
+ function AiGroup({ editor, enabledFeatures }) {
16881
16883
  const [isRecording, setIsRecording] = (0, import_react18.useState)(false);
16882
16884
  const [audioBlob, setAudioBlob] = (0, import_react18.useState)(null);
16883
16885
  const [isTranscribing, setIsTranscribing] = (0, import_react18.useState)(false);
@@ -16960,7 +16962,8 @@ function AiGroup({ editor }) {
16960
16962
  setIsLoadingAI(false);
16961
16963
  }
16962
16964
  };
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(
16965
+ if (!enabledFeatures.includes("voice to text")) return null;
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(
16964
16967
  "button",
16965
16968
  {
16966
16969
  type: "button",
@@ -17043,20 +17046,15 @@ var MathModal2 = (0, import_dynamic.default)(() => Promise.resolve().then(() =>
17043
17046
  var CodeEditorModal2 = (0, import_dynamic.default)(() => Promise.resolve().then(() => (init_CodeEditorModal(), CodeEditorModal_exports)), {
17044
17047
  ssr: false
17045
17048
  });
17046
- var AddOnGroup = ({ editor }) => {
17047
- const [isModalOpen, setModalOpen] = (0, import_react22.useState)(false);
17049
+ var AddOnGroup = ({ editor, addOns }) => {
17050
+ const [isMathModalOpen, setMathModalOpen] = (0, import_react22.useState)(false);
17048
17051
  const [latexValue, setLatexValue] = (0, import_react22.useState)("");
17049
17052
  const [isCodeModalOpen, setCodeModalOpen] = (0, import_react22.useState)(false);
17050
17053
  if (!editor) return null;
17051
- const insertCodeBlock = () => {
17052
- setCodeModalOpen(true);
17053
- };
17054
+ const insertCodeBlock = () => setCodeModalOpen(true);
17054
17055
  const handleMathInsert = (latex) => {
17055
- editor.chain().focus().insertContent({
17056
- type: "mathInline",
17057
- attrs: { formula: latex }
17058
- }).run();
17059
- setModalOpen(false);
17056
+ editor.chain().focus().insertContent({ type: "mathInline", attrs: { formula: latex } }).run();
17057
+ setMathModalOpen(false);
17060
17058
  setLatexValue("");
17061
17059
  };
17062
17060
  const handleSubmitCode = (code) => {
@@ -17082,7 +17080,7 @@ var AddOnGroup = ({ editor }) => {
17082
17080
  }).run();
17083
17081
  setCodeModalOpen(false);
17084
17082
  };
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(
17083
+ 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
17084
  "button",
17087
17085
  {
17088
17086
  type: "button",
@@ -17091,25 +17089,25 @@ var AddOnGroup = ({ editor }) => {
17091
17089
  title: "Open Code Editor"
17092
17090
  },
17093
17091
  /* @__PURE__ */ import_react22.default.createElement(import_lucide_react.MessageSquareCode, { size: 18 })
17094
- ), /* @__PURE__ */ import_react22.default.createElement(
17092
+ ), addOns.includes("math") && /* @__PURE__ */ import_react22.default.createElement(
17095
17093
  "button",
17096
17094
  {
17097
17095
  type: "button",
17098
- onClick: () => setModalOpen(true),
17096
+ onClick: () => setMathModalOpen(true),
17099
17097
  className: "addon-btn",
17100
17098
  title: "Insert Math Equation"
17101
17099
  },
17102
17100
  /* @__PURE__ */ import_react22.default.createElement(import_lucide_react.SquareRadical, { size: 18 })
17103
- )), /* @__PURE__ */ import_react22.default.createElement(
17101
+ )), addOns.includes("math") && /* @__PURE__ */ import_react22.default.createElement(
17104
17102
  MathModal2,
17105
17103
  {
17106
- isOpen: isModalOpen,
17107
- onClose: () => setModalOpen(false),
17104
+ isOpen: isMathModalOpen,
17105
+ onClose: () => setMathModalOpen(false),
17108
17106
  onSubmit: handleMathInsert,
17109
17107
  value: latexValue,
17110
17108
  setValue: setLatexValue
17111
17109
  }
17112
- ), /* @__PURE__ */ import_react22.default.createElement(
17110
+ ), addOns.includes("code") && /* @__PURE__ */ import_react22.default.createElement(
17113
17111
  CodeEditorModal2,
17114
17112
  {
17115
17113
  isOpen: isCodeModalOpen,
@@ -17129,9 +17127,8 @@ function TetronsToolbar({
17129
17127
  }) {
17130
17128
  const [autoSave, setAutoSave] = (0, import_react23.useState)(false);
17131
17129
  (0, import_react23.useEffect)(() => {
17132
- if (!editor) return;
17130
+ if (!editor || !autoSave) return;
17133
17131
  const handleUpdate = () => {
17134
- if (!autoSave) return;
17135
17132
  const content = editor.getJSON();
17136
17133
  fetch("/api/save", {
17137
17134
  method: "POST",
@@ -17150,7 +17147,18 @@ function TetronsToolbar({
17150
17147
  case "premium":
17151
17148
  return addOns;
17152
17149
  case "platinum":
17153
- 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
+ ];
17154
17162
  default:
17155
17163
  return [];
17156
17164
  }
@@ -17163,7 +17171,36 @@ function TetronsToolbar({
17163
17171
  checked: autoSave,
17164
17172
  onChange: (e) => setAutoSave(e.target.checked)
17165
17173
  }
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 }));
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(
17175
+ MiscGroup,
17176
+ {
17177
+ editor,
17178
+ enabledFeatures: effectiveAddOns.filter(
17179
+ (a) => [
17180
+ "undo",
17181
+ "redo",
17182
+ "resetFormatting",
17183
+ "preview",
17184
+ "codeBlock",
17185
+ "spell check"
17186
+ ].includes(a)
17187
+ )
17188
+ }
17189
+ ), effectiveAddOns.some((a) => ["ai", "voice to text"].includes(a)) && /* @__PURE__ */ import_react23.default.createElement(
17190
+ AiGroup,
17191
+ {
17192
+ editor,
17193
+ enabledFeatures: effectiveAddOns.filter(
17194
+ (a) => ["ai", "voice to text"].includes(a)
17195
+ )
17196
+ }
17197
+ ), effectiveAddOns.some((a) => a === "math" || a === "code") && /* @__PURE__ */ import_react23.default.createElement(
17198
+ AddOnGroup_default,
17199
+ {
17200
+ editor,
17201
+ addOns: effectiveAddOns.filter((a) => a === "math" || a === "code")
17202
+ }
17203
+ ));
17167
17204
  }
17168
17205
 
17169
17206
  // src/components/tetrons/EditorContent.tsx
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,
@@ -16738,26 +16740,7 @@ async function checkGrammar(text) {
16738
16740
  }
16739
16741
 
16740
16742
  // 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
- };
16743
+ function MiscGroup({ editor, enabledFeatures }) {
16761
16744
  const handleGrammarCheck = async () => {
16762
16745
  const text = editor.getText();
16763
16746
  try {
@@ -16781,7 +16764,26 @@ Reason: ${issue.message}
16781
16764
  alert("\u274C Failed to check grammar. Please try again later.");
16782
16765
  }
16783
16766
  };
16784
- return /* @__PURE__ */ React10.createElement("div", { className: "misc-group" }, /* @__PURE__ */ React10.createElement(
16767
+ const handlePreview = () => {
16768
+ const html = editor.getHTML();
16769
+ const previewWindow = window.open("", "_blank");
16770
+ if (previewWindow) {
16771
+ previewWindow.document.open();
16772
+ previewWindow.document.write(`
16773
+ <html>
16774
+ <head>
16775
+ <title>Preview</title>
16776
+ <style>
16777
+ body { font-family: sans-serif; padding: 2rem; line-height: 1.6; }
16778
+ </style>
16779
+ </head>
16780
+ <body>${html}</body>
16781
+ </html>
16782
+ `);
16783
+ previewWindow.document.close();
16784
+ }
16785
+ };
16786
+ return /* @__PURE__ */ React10.createElement("div", { className: "misc-group" }, enabledFeatures.includes("undo") && /* @__PURE__ */ React10.createElement(
16785
16787
  ToolbarButton_default,
16786
16788
  {
16787
16789
  icon: MdUndo,
@@ -16789,7 +16791,7 @@ Reason: ${issue.message}
16789
16791
  onClick: () => editor.chain().focus().undo().run(),
16790
16792
  disabled: !editor.can().undo()
16791
16793
  }
16792
- ), /* @__PURE__ */ React10.createElement(
16794
+ ), enabledFeatures.includes("redo") && /* @__PURE__ */ React10.createElement(
16793
16795
  ToolbarButton_default,
16794
16796
  {
16795
16797
  icon: MdRedo,
@@ -16797,14 +16799,14 @@ Reason: ${issue.message}
16797
16799
  onClick: () => editor.chain().focus().redo().run(),
16798
16800
  disabled: !editor.can().redo()
16799
16801
  }
16800
- ), /* @__PURE__ */ React10.createElement(
16802
+ ), enabledFeatures.includes("resetFormatting") && /* @__PURE__ */ React10.createElement(
16801
16803
  ToolbarButton_default,
16802
16804
  {
16803
16805
  icon: MdRefresh,
16804
16806
  label: "Reset Formatting",
16805
16807
  onClick: () => editor.chain().focus().unsetAllMarks().clearNodes().run()
16806
16808
  }
16807
- ), /* @__PURE__ */ React10.createElement(
16809
+ ), enabledFeatures.includes("codeBlock") && /* @__PURE__ */ React10.createElement(
16808
16810
  ToolbarButton_default,
16809
16811
  {
16810
16812
  icon: MdCode,
@@ -16812,14 +16814,14 @@ Reason: ${issue.message}
16812
16814
  onClick: () => editor.chain().focus().toggleCodeBlock().run(),
16813
16815
  isActive: editor.isActive("codeBlock")
16814
16816
  }
16815
- ), /* @__PURE__ */ React10.createElement(
16817
+ ), enabledFeatures.includes("preview") && /* @__PURE__ */ React10.createElement(
16816
16818
  ToolbarButton_default,
16817
16819
  {
16818
16820
  icon: MdVisibility,
16819
16821
  label: "Preview",
16820
16822
  onClick: handlePreview
16821
16823
  }
16822
- ), /* @__PURE__ */ React10.createElement(
16824
+ ), enabledFeatures.includes("spell check") && /* @__PURE__ */ React10.createElement(
16823
16825
  ToolbarButton_default,
16824
16826
  {
16825
16827
  icon: MdSpellcheck,
@@ -16886,7 +16888,7 @@ 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";
16888
16890
  import { motion, AnimatePresence } from "framer-motion";
16889
- function AiGroup({ editor }) {
16891
+ function AiGroup({ editor, enabledFeatures }) {
16890
16892
  const [isRecording, setIsRecording] = useState7(false);
16891
16893
  const [audioBlob, setAudioBlob] = useState7(null);
16892
16894
  const [isTranscribing, setIsTranscribing] = useState7(false);
@@ -16969,7 +16971,8 @@ function AiGroup({ editor }) {
16969
16971
  setIsLoadingAI(false);
16970
16972
  }
16971
16973
  };
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(
16974
+ if (!enabledFeatures.includes("voice to text")) return null;
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(
16973
16976
  "button",
16974
16977
  {
16975
16978
  type: "button",
@@ -17052,20 +17055,15 @@ var MathModal2 = dynamic(() => Promise.resolve().then(() => (init_MathModal(), M
17052
17055
  var CodeEditorModal2 = dynamic(() => Promise.resolve().then(() => (init_CodeEditorModal(), CodeEditorModal_exports)), {
17053
17056
  ssr: false
17054
17057
  });
17055
- var AddOnGroup = ({ editor }) => {
17056
- const [isModalOpen, setModalOpen] = useState9(false);
17058
+ var AddOnGroup = ({ editor, addOns }) => {
17059
+ const [isMathModalOpen, setMathModalOpen] = useState9(false);
17057
17060
  const [latexValue, setLatexValue] = useState9("");
17058
17061
  const [isCodeModalOpen, setCodeModalOpen] = useState9(false);
17059
17062
  if (!editor) return null;
17060
- const insertCodeBlock = () => {
17061
- setCodeModalOpen(true);
17062
- };
17063
+ const insertCodeBlock = () => setCodeModalOpen(true);
17063
17064
  const handleMathInsert = (latex) => {
17064
- editor.chain().focus().insertContent({
17065
- type: "mathInline",
17066
- attrs: { formula: latex }
17067
- }).run();
17068
- setModalOpen(false);
17065
+ editor.chain().focus().insertContent({ type: "mathInline", attrs: { formula: latex } }).run();
17066
+ setMathModalOpen(false);
17069
17067
  setLatexValue("");
17070
17068
  };
17071
17069
  const handleSubmitCode = (code) => {
@@ -17091,7 +17089,7 @@ var AddOnGroup = ({ editor }) => {
17091
17089
  }).run();
17092
17090
  setCodeModalOpen(false);
17093
17091
  };
17094
- return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "group flex gap-2 items-center" }, /* @__PURE__ */ React15.createElement(
17092
+ 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
17093
  "button",
17096
17094
  {
17097
17095
  type: "button",
@@ -17100,25 +17098,25 @@ var AddOnGroup = ({ editor }) => {
17100
17098
  title: "Open Code Editor"
17101
17099
  },
17102
17100
  /* @__PURE__ */ React15.createElement(MessageSquareCode, { size: 18 })
17103
- ), /* @__PURE__ */ React15.createElement(
17101
+ ), addOns.includes("math") && /* @__PURE__ */ React15.createElement(
17104
17102
  "button",
17105
17103
  {
17106
17104
  type: "button",
17107
- onClick: () => setModalOpen(true),
17105
+ onClick: () => setMathModalOpen(true),
17108
17106
  className: "addon-btn",
17109
17107
  title: "Insert Math Equation"
17110
17108
  },
17111
17109
  /* @__PURE__ */ React15.createElement(SquareRadical, { size: 18 })
17112
- )), /* @__PURE__ */ React15.createElement(
17110
+ )), addOns.includes("math") && /* @__PURE__ */ React15.createElement(
17113
17111
  MathModal2,
17114
17112
  {
17115
- isOpen: isModalOpen,
17116
- onClose: () => setModalOpen(false),
17113
+ isOpen: isMathModalOpen,
17114
+ onClose: () => setMathModalOpen(false),
17117
17115
  onSubmit: handleMathInsert,
17118
17116
  value: latexValue,
17119
17117
  setValue: setLatexValue
17120
17118
  }
17121
- ), /* @__PURE__ */ React15.createElement(
17119
+ ), addOns.includes("code") && /* @__PURE__ */ React15.createElement(
17122
17120
  CodeEditorModal2,
17123
17121
  {
17124
17122
  isOpen: isCodeModalOpen,
@@ -17138,9 +17136,8 @@ function TetronsToolbar({
17138
17136
  }) {
17139
17137
  const [autoSave, setAutoSave] = useState10(false);
17140
17138
  useEffect8(() => {
17141
- if (!editor) return;
17139
+ if (!editor || !autoSave) return;
17142
17140
  const handleUpdate = () => {
17143
- if (!autoSave) return;
17144
17141
  const content = editor.getJSON();
17145
17142
  fetch("/api/save", {
17146
17143
  method: "POST",
@@ -17159,7 +17156,18 @@ function TetronsToolbar({
17159
17156
  case "premium":
17160
17157
  return addOns;
17161
17158
  case "platinum":
17162
- 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
+ ];
17163
17171
  default:
17164
17172
  return [];
17165
17173
  }
@@ -17172,7 +17180,36 @@ function TetronsToolbar({
17172
17180
  checked: autoSave,
17173
17181
  onChange: (e) => setAutoSave(e.target.checked)
17174
17182
  }
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 }));
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(
17184
+ MiscGroup,
17185
+ {
17186
+ editor,
17187
+ enabledFeatures: effectiveAddOns.filter(
17188
+ (a) => [
17189
+ "undo",
17190
+ "redo",
17191
+ "resetFormatting",
17192
+ "preview",
17193
+ "codeBlock",
17194
+ "spell check"
17195
+ ].includes(a)
17196
+ )
17197
+ }
17198
+ ), effectiveAddOns.some((a) => ["ai", "voice to text"].includes(a)) && /* @__PURE__ */ React16.createElement(
17199
+ AiGroup,
17200
+ {
17201
+ editor,
17202
+ enabledFeatures: effectiveAddOns.filter(
17203
+ (a) => ["ai", "voice to text"].includes(a)
17204
+ )
17205
+ }
17206
+ ), effectiveAddOns.some((a) => a === "math" || a === "code") && /* @__PURE__ */ React16.createElement(
17207
+ AddOnGroup_default,
17208
+ {
17209
+ editor,
17210
+ addOns: effectiveAddOns.filter((a) => a === "math" || a === "code")
17211
+ }
17212
+ ));
17176
17213
  }
17177
17214
 
17178
17215
  // 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.92",
4
4
  "description": "A Next.js project written in TypeScript",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",