@particle-academy/fancy-code 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -108,10 +108,11 @@ function CodeEditorPanel({ className }) {
108
108
  "textarea",
109
109
  {
110
110
  ref: textareaRef,
111
- className: "relative m-0 block w-full resize-none overflow-hidden border-none bg-transparent p-2.5 text-[13px] leading-[1.5] text-transparent outline-none",
111
+ className: "relative m-0 block w-full resize-none border-none bg-transparent p-2.5 text-[13px] leading-[1.5] text-transparent outline-none",
112
112
  style: {
113
113
  caretColor: themeColors.cursorColor,
114
114
  minHeight: _minHeight ? _minHeight - 40 : 80,
115
+ overflow: "hidden",
115
116
  whiteSpace: wordWrap ? "pre-wrap" : "pre",
116
117
  overflowWrap: wordWrap ? "break-word" : "normal"
117
118
  },
@@ -660,6 +661,377 @@ var tokenizePhp = (source) => {
660
661
  return tokens;
661
662
  };
662
663
 
664
+ // src/engine/tokenizers/python.ts
665
+ var KEYWORDS2 = /* @__PURE__ */ new Set([
666
+ "and",
667
+ "as",
668
+ "assert",
669
+ "async",
670
+ "await",
671
+ "break",
672
+ "class",
673
+ "continue",
674
+ "def",
675
+ "del",
676
+ "elif",
677
+ "else",
678
+ "except",
679
+ "finally",
680
+ "for",
681
+ "from",
682
+ "global",
683
+ "if",
684
+ "import",
685
+ "in",
686
+ "is",
687
+ "lambda",
688
+ "nonlocal",
689
+ "not",
690
+ "or",
691
+ "pass",
692
+ "raise",
693
+ "return",
694
+ "try",
695
+ "while",
696
+ "with",
697
+ "yield",
698
+ "True",
699
+ "False",
700
+ "None"
701
+ ]);
702
+ var TYPES = /* @__PURE__ */ new Set([
703
+ "int",
704
+ "str",
705
+ "float",
706
+ "bool",
707
+ "list",
708
+ "dict",
709
+ "tuple",
710
+ "set",
711
+ "bytes",
712
+ "bytearray",
713
+ "memoryview",
714
+ "range",
715
+ "frozenset",
716
+ "complex",
717
+ "type",
718
+ "object",
719
+ "property",
720
+ "classmethod",
721
+ "staticmethod",
722
+ "Any",
723
+ "Optional",
724
+ "Union",
725
+ "Callable",
726
+ "List",
727
+ "Dict",
728
+ "Tuple",
729
+ "Set",
730
+ "Sequence",
731
+ "Mapping",
732
+ "Iterator",
733
+ "Generator",
734
+ "Coroutine"
735
+ ]);
736
+ var tokenizePython = (source) => {
737
+ const tokens = [];
738
+ const len = source.length;
739
+ let i = 0;
740
+ while (i < len) {
741
+ const ch = source[i];
742
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
743
+ i++;
744
+ continue;
745
+ }
746
+ if (ch === "#") {
747
+ const pos = i;
748
+ i++;
749
+ while (i < len && source[i] !== "\n") i++;
750
+ tokens.push({ type: "comment", start: pos, end: i });
751
+ continue;
752
+ }
753
+ if (ch === "@" && i + 1 < len && /[a-zA-Z_]/.test(source[i + 1])) {
754
+ const pos = i;
755
+ i++;
756
+ while (i < len && /[a-zA-Z0-9_.]/.test(source[i])) i++;
757
+ tokens.push({ type: "keyword", start: pos, end: i });
758
+ continue;
759
+ }
760
+ if (ch === '"' || ch === "'" || (ch === "f" || ch === "r" || ch === "b" || ch === "F" || ch === "R" || ch === "B") && (source[i + 1] === '"' || source[i + 1] === "'")) {
761
+ const pos = i;
762
+ if (ch !== '"' && ch !== "'") i++;
763
+ const quote = source[i];
764
+ if (source[i + 1] === quote && source[i + 2] === quote) {
765
+ const triple = quote + quote + quote;
766
+ i += 3;
767
+ while (i < len && source.slice(i, i + 3) !== triple) {
768
+ if (source[i] === "\\") i++;
769
+ i++;
770
+ }
771
+ i += 3;
772
+ tokens.push({ type: "string", start: pos, end: i });
773
+ continue;
774
+ }
775
+ i++;
776
+ while (i < len && source[i] !== quote && source[i] !== "\n") {
777
+ if (source[i] === "\\") i++;
778
+ i++;
779
+ }
780
+ if (i < len && source[i] === quote) i++;
781
+ tokens.push({ type: "string", start: pos, end: i });
782
+ continue;
783
+ }
784
+ if (ch >= "0" && ch <= "9" || ch === "." && i + 1 < len && source[i + 1] >= "0" && source[i + 1] <= "9") {
785
+ const pos = i;
786
+ if (ch === "0" && (source[i + 1] === "x" || source[i + 1] === "X")) {
787
+ i += 2;
788
+ while (i < len && /[0-9a-fA-F_]/.test(source[i])) i++;
789
+ } else if (ch === "0" && (source[i + 1] === "b" || source[i + 1] === "B")) {
790
+ i += 2;
791
+ while (i < len && /[01_]/.test(source[i])) i++;
792
+ } else if (ch === "0" && (source[i + 1] === "o" || source[i + 1] === "O")) {
793
+ i += 2;
794
+ while (i < len && /[0-7_]/.test(source[i])) i++;
795
+ } else {
796
+ while (i < len && /[0-9_.]/.test(source[i])) i++;
797
+ if (i < len && (source[i] === "e" || source[i] === "E")) {
798
+ i++;
799
+ if (i < len && (source[i] === "+" || source[i] === "-")) i++;
800
+ while (i < len && source[i] >= "0" && source[i] <= "9") i++;
801
+ }
802
+ }
803
+ if (i < len && source[i] === "j") i++;
804
+ tokens.push({ type: "number", start: pos, end: i });
805
+ continue;
806
+ }
807
+ if (/[a-zA-Z_]/.test(ch)) {
808
+ const pos = i;
809
+ i++;
810
+ while (i < len && /[a-zA-Z0-9_]/.test(source[i])) i++;
811
+ const word = source.slice(pos, i);
812
+ let j = i;
813
+ while (j < len && (source[j] === " " || source[j] === " ")) j++;
814
+ if (KEYWORDS2.has(word)) {
815
+ tokens.push({ type: "keyword", start: pos, end: i });
816
+ } else if (TYPES.has(word)) {
817
+ tokens.push({ type: "type", start: pos, end: i });
818
+ } else if (source[j] === "(") {
819
+ tokens.push({ type: "function", start: pos, end: i });
820
+ } else if (word[0] >= "A" && word[0] <= "Z") {
821
+ tokens.push({ type: "type", start: pos, end: i });
822
+ } else {
823
+ tokens.push({ type: "variable", start: pos, end: i });
824
+ }
825
+ continue;
826
+ }
827
+ if ("+-*/%=<>!&|^~:@".includes(ch)) {
828
+ const pos = i;
829
+ i++;
830
+ while (i < len && "+-*/%=<>!&|^~:".includes(source[i])) i++;
831
+ tokens.push({ type: "operator", start: pos, end: i });
832
+ continue;
833
+ }
834
+ if ("()[]{},.;\\".includes(ch)) {
835
+ tokens.push({ type: "punctuation", start: i, end: i + 1 });
836
+ i++;
837
+ continue;
838
+ }
839
+ tokens.push({ type: "plain", start: i, end: i + 1 });
840
+ i++;
841
+ }
842
+ return tokens;
843
+ };
844
+
845
+ // src/engine/tokenizers/go.ts
846
+ var KEYWORDS3 = /* @__PURE__ */ new Set([
847
+ "break",
848
+ "case",
849
+ "chan",
850
+ "const",
851
+ "continue",
852
+ "default",
853
+ "defer",
854
+ "else",
855
+ "fallthrough",
856
+ "for",
857
+ "func",
858
+ "go",
859
+ "goto",
860
+ "if",
861
+ "import",
862
+ "interface",
863
+ "map",
864
+ "package",
865
+ "range",
866
+ "return",
867
+ "select",
868
+ "struct",
869
+ "switch",
870
+ "type",
871
+ "var",
872
+ "nil",
873
+ "true",
874
+ "false",
875
+ "iota"
876
+ ]);
877
+ var TYPES2 = /* @__PURE__ */ new Set([
878
+ "bool",
879
+ "byte",
880
+ "complex64",
881
+ "complex128",
882
+ "error",
883
+ "float32",
884
+ "float64",
885
+ "int",
886
+ "int8",
887
+ "int16",
888
+ "int32",
889
+ "int64",
890
+ "rune",
891
+ "string",
892
+ "uint",
893
+ "uint8",
894
+ "uint16",
895
+ "uint32",
896
+ "uint64",
897
+ "uintptr",
898
+ "any"
899
+ ]);
900
+ var BUILTINS = /* @__PURE__ */ new Set([
901
+ "make",
902
+ "len",
903
+ "cap",
904
+ "new",
905
+ "append",
906
+ "copy",
907
+ "close",
908
+ "delete",
909
+ "complex",
910
+ "real",
911
+ "imag",
912
+ "panic",
913
+ "recover",
914
+ "print",
915
+ "println"
916
+ ]);
917
+ var tokenizeGo = (source) => {
918
+ const tokens = [];
919
+ const len = source.length;
920
+ let i = 0;
921
+ while (i < len) {
922
+ const ch = source[i];
923
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
924
+ i++;
925
+ continue;
926
+ }
927
+ if (ch === "/" && source[i + 1] === "/") {
928
+ const pos = i;
929
+ i += 2;
930
+ while (i < len && source[i] !== "\n") i++;
931
+ tokens.push({ type: "comment", start: pos, end: i });
932
+ continue;
933
+ }
934
+ if (ch === "/" && source[i + 1] === "*") {
935
+ const pos = i;
936
+ i += 2;
937
+ while (i < len && !(source[i] === "*" && source[i + 1] === "/")) i++;
938
+ i += 2;
939
+ tokens.push({ type: "comment", start: pos, end: i });
940
+ continue;
941
+ }
942
+ if (ch === "`") {
943
+ const pos = i;
944
+ i++;
945
+ while (i < len && source[i] !== "`") i++;
946
+ i++;
947
+ tokens.push({ type: "string", start: pos, end: i });
948
+ continue;
949
+ }
950
+ if (ch === '"') {
951
+ const pos = i;
952
+ i++;
953
+ while (i < len && source[i] !== '"' && source[i] !== "\n") {
954
+ if (source[i] === "\\") i++;
955
+ i++;
956
+ }
957
+ if (i < len && source[i] === '"') i++;
958
+ tokens.push({ type: "string", start: pos, end: i });
959
+ continue;
960
+ }
961
+ if (ch === "'") {
962
+ const pos = i;
963
+ i++;
964
+ while (i < len && source[i] !== "'" && source[i] !== "\n") {
965
+ if (source[i] === "\\") i++;
966
+ i++;
967
+ }
968
+ if (i < len && source[i] === "'") i++;
969
+ tokens.push({ type: "string", start: pos, end: i });
970
+ continue;
971
+ }
972
+ if (ch >= "0" && ch <= "9" || ch === "." && i + 1 < len && source[i + 1] >= "0" && source[i + 1] <= "9") {
973
+ const pos = i;
974
+ if (ch === "0" && (source[i + 1] === "x" || source[i + 1] === "X")) {
975
+ i += 2;
976
+ while (i < len && /[0-9a-fA-F_]/.test(source[i])) i++;
977
+ } else if (ch === "0" && (source[i + 1] === "b" || source[i + 1] === "B")) {
978
+ i += 2;
979
+ while (i < len && /[01_]/.test(source[i])) i++;
980
+ } else if (ch === "0" && (source[i + 1] === "o" || source[i + 1] === "O")) {
981
+ i += 2;
982
+ while (i < len && /[0-7_]/.test(source[i])) i++;
983
+ } else {
984
+ while (i < len && /[0-9_.]/.test(source[i])) i++;
985
+ if (i < len && (source[i] === "e" || source[i] === "E")) {
986
+ i++;
987
+ if (i < len && (source[i] === "+" || source[i] === "-")) i++;
988
+ while (i < len && source[i] >= "0" && source[i] <= "9") i++;
989
+ }
990
+ }
991
+ if (i < len && source[i] === "i") i++;
992
+ tokens.push({ type: "number", start: pos, end: i });
993
+ continue;
994
+ }
995
+ if (/[a-zA-Z_]/.test(ch)) {
996
+ const pos = i;
997
+ i++;
998
+ while (i < len && /[a-zA-Z0-9_]/.test(source[i])) i++;
999
+ const word = source.slice(pos, i);
1000
+ let j = i;
1001
+ while (j < len && (source[j] === " " || source[j] === " ")) j++;
1002
+ if (KEYWORDS3.has(word)) {
1003
+ tokens.push({ type: "keyword", start: pos, end: i });
1004
+ } else if (TYPES2.has(word)) {
1005
+ tokens.push({ type: "type", start: pos, end: i });
1006
+ } else if (BUILTINS.has(word) && source[j] === "(") {
1007
+ tokens.push({ type: "function", start: pos, end: i });
1008
+ } else if (source[j] === "(") {
1009
+ tokens.push({ type: "function", start: pos, end: i });
1010
+ } else if (word[0] >= "A" && word[0] <= "Z") {
1011
+ tokens.push({ type: "type", start: pos, end: i });
1012
+ } else {
1013
+ tokens.push({ type: "variable", start: pos, end: i });
1014
+ }
1015
+ continue;
1016
+ }
1017
+ if ("+-*/%=<>!&|^~:".includes(ch)) {
1018
+ const pos = i;
1019
+ i++;
1020
+ while (i < len && "+-*/%=<>!&|^~:".includes(source[i])) i++;
1021
+ tokens.push({ type: "operator", start: pos, end: i });
1022
+ continue;
1023
+ }
1024
+ if ("(){}[];,.".includes(ch)) {
1025
+ tokens.push({ type: "punctuation", start: i, end: i + 1 });
1026
+ i++;
1027
+ continue;
1028
+ }
1029
+ tokens.push({ type: "plain", start: i, end: i + 1 });
1030
+ i++;
1031
+ }
1032
+ return tokens;
1033
+ };
1034
+
663
1035
  // src/languages/builtin.ts
664
1036
  registerLanguage({
665
1037
  name: "JavaScript",
@@ -682,6 +1054,16 @@ registerLanguage({
682
1054
  aliases: ["php"],
683
1055
  tokenize: tokenizePhp
684
1056
  });
1057
+ registerLanguage({
1058
+ name: "Python",
1059
+ aliases: ["py", "python"],
1060
+ tokenize: tokenizePython
1061
+ });
1062
+ registerLanguage({
1063
+ name: "Go",
1064
+ aliases: ["go", "golang"],
1065
+ tokenize: tokenizeGo
1066
+ });
685
1067
  var iconBtnClass = "inline-flex items-center justify-center rounded-md p-1 text-zinc-500 transition-colors hover:bg-zinc-100 hover:text-zinc-700 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-200";
686
1068
  function LanguageSelector() {
687
1069
  const { language, setLanguage } = useCodeEditor();
@@ -944,14 +1326,23 @@ function useEditorEngine({
944
1326
  }
945
1327
  return count;
946
1328
  }, [value]);
1329
+ const autoResize = useCallback(() => {
1330
+ const ta = textareaRef.current;
1331
+ if (!ta) return;
1332
+ ta.style.height = "auto";
1333
+ ta.style.height = ta.scrollHeight + "px";
1334
+ }, []);
947
1335
  useEffect(() => {
948
1336
  const ta = textareaRef.current;
949
- if (!ta || ta.value === value) return;
950
- const { selectionStart, selectionEnd } = ta;
951
- ta.value = value;
952
- ta.selectionStart = selectionStart;
953
- ta.selectionEnd = selectionEnd;
954
- }, [value]);
1337
+ if (!ta) return;
1338
+ if (ta.value !== value) {
1339
+ const { selectionStart, selectionEnd } = ta;
1340
+ ta.value = value;
1341
+ ta.selectionStart = selectionStart;
1342
+ ta.selectionEnd = selectionEnd;
1343
+ }
1344
+ autoResize();
1345
+ }, [value, autoResize]);
955
1346
  const updateCursorInfo = useCallback(() => {
956
1347
  const ta = textareaRef.current;
957
1348
  if (!ta) return;
@@ -968,8 +1359,9 @@ function useEditorEngine({
968
1359
  const ta = textareaRef.current;
969
1360
  if (!ta) return;
970
1361
  onChangeRef.current?.(ta.value);
1362
+ autoResize();
971
1363
  updateCursorInfo();
972
- }, [updateCursorInfo]);
1364
+ }, [autoResize, updateCursorInfo]);
973
1365
  const handleSelect = useCallback(() => {
974
1366
  updateCursorInfo();
975
1367
  }, [updateCursorInfo]);
@@ -1029,6 +1421,7 @@ function useEditorEngine({
1029
1421
  ta.selectionStart = ta.selectionEnd = start + tabSize;
1030
1422
  onChangeRef.current?.(ta.value);
1031
1423
  }
1424
+ autoResize();
1032
1425
  updateCursorInfo();
1033
1426
  return;
1034
1427
  }
@@ -1047,11 +1440,12 @@ function useEditorEngine({
1047
1440
  ta.value = before + insertion + after;
1048
1441
  ta.selectionStart = ta.selectionEnd = start + insertion.length;
1049
1442
  onChangeRef.current?.(ta.value);
1443
+ autoResize();
1050
1444
  updateCursorInfo();
1051
1445
  return;
1052
1446
  }
1053
1447
  },
1054
- [readOnly, tabSize, updateCursorInfo]
1448
+ [readOnly, tabSize, autoResize, updateCursorInfo]
1055
1449
  );
1056
1450
  return {
1057
1451
  textareaRef,