@particle-academy/fancy-code 0.15.1 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createContext, useMemo, useState, useCallback, useRef, useEffect, useSyncExternalStore, useContext } from 'react';
2
2
  import { cn, useControllableState, ContentRenderer, resolveMediaType, FauxClient, MediaViewer, Callout, textareaAdapter, InputTag } from '@particle-academy/react-fancy';
3
- import { annotateLines, highlightCode, tok, tokenizeHtml, LIGHT_COLORS, DARK_COLORS, languageFromFilename } from '@particle-academy/fancy-file-commons';
4
- export { languageFromFilename } from '@particle-academy/fancy-file-commons';
3
+ import { annotateLines, highlightCode, tokenizeMarkdown, tokenizeJavaScript, tokenizeHtml, tokenizePhp, tokenizePython, tokenizeGo, tokenizeShell, tokenizeDotenv, LIGHT_COLORS, DARK_COLORS, languageFromFilename } from '@particle-academy/fancy-file-commons';
4
+ export { languageFromFilename, tokenizeMarkdown } from '@particle-academy/fancy-file-commons';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
 
7
7
  // src/components/CodeEditor/CodeEditor.tsx
@@ -41,7 +41,8 @@ function CodeEditorPanel({ className }) {
41
41
  _diffAnnotations: diffAnnotations,
42
42
  _maxHeight,
43
43
  _minHeight,
44
- _highlightLines: highlightLines
44
+ _highlightLines: highlightLines,
45
+ _aria: aria
45
46
  } = useCodeEditor();
46
47
  const lineNumberElements = useMemo(() => {
47
48
  if (!engine || !lineNumbers) return null;
@@ -131,6 +132,7 @@ function CodeEditorPanel({ className }) {
131
132
  themeColors,
132
133
  handleKeyDown,
133
134
  handleInput,
135
+ handleBlur,
134
136
  handleScroll,
135
137
  handleSelect,
136
138
  scrollTop,
@@ -203,8 +205,12 @@ function CodeEditorPanel({ className }) {
203
205
  "textarea",
204
206
  {
205
207
  ref: textareaRef,
208
+ "aria-label": aria?.["aria-label"],
209
+ "aria-labelledby": aria?.["aria-labelledby"],
210
+ "aria-describedby": aria?.["aria-describedby"],
206
211
  className: cn(
207
- "relative m-0 block resize-none border-none bg-transparent p-2.5 text-[13px] leading-[1.5] text-transparent outline-none",
212
+ "relative m-0 block resize-none border-none bg-transparent p-2.5 text-[13px] leading-[1.5] text-transparent",
213
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[color:var(--fancy-focus,var(--color-blue-500))] dark:focus-visible:ring-[color:var(--fancy-focus-dark,var(--color-blue-400))]",
208
214
  wordWrap ? "w-full" : "min-w-full"
209
215
  ),
210
216
  style: {
@@ -221,6 +227,7 @@ function CodeEditorPanel({ className }) {
221
227
  readOnly,
222
228
  onKeyDown: handleKeyDown,
223
229
  onInput: handleInput,
230
+ onBlur: handleBlur,
224
231
  onScroll: handleScroll,
225
232
  onSelect: handleSelect,
226
233
  onClick: handleSelect
@@ -267,850 +274,6 @@ function getRegisteredLanguages() {
267
274
  }
268
275
  return names;
269
276
  }
270
-
271
- // src/engine/tokenizers/javascript.ts
272
- var KEYWORDS = /* @__PURE__ */ new Set([
273
- "abstract",
274
- "arguments",
275
- "as",
276
- "async",
277
- "await",
278
- "boolean",
279
- "break",
280
- "byte",
281
- "case",
282
- "catch",
283
- "char",
284
- "class",
285
- "const",
286
- "continue",
287
- "debugger",
288
- "default",
289
- "delete",
290
- "do",
291
- "double",
292
- "else",
293
- "enum",
294
- "export",
295
- "extends",
296
- "false",
297
- "final",
298
- "finally",
299
- "float",
300
- "for",
301
- "from",
302
- "function",
303
- "get",
304
- "goto",
305
- "if",
306
- "implements",
307
- "import",
308
- "in",
309
- "instanceof",
310
- "int",
311
- "interface",
312
- "keyof",
313
- "let",
314
- "long",
315
- "native",
316
- "new",
317
- "null",
318
- "of",
319
- "package",
320
- "private",
321
- "protected",
322
- "public",
323
- "readonly",
324
- "return",
325
- "set",
326
- "short",
327
- "static",
328
- "super",
329
- "switch",
330
- "synchronized",
331
- "this",
332
- "throw",
333
- "throws",
334
- "transient",
335
- "true",
336
- "try",
337
- "type",
338
- "typeof",
339
- "undefined",
340
- "var",
341
- "void",
342
- "volatile",
343
- "while",
344
- "with",
345
- "yield"
346
- ]);
347
- var TYPE_KEYWORDS = /* @__PURE__ */ new Set([
348
- "string",
349
- "number",
350
- "boolean",
351
- "any",
352
- "void",
353
- "never",
354
- "unknown",
355
- "object",
356
- "symbol",
357
- "bigint",
358
- "undefined",
359
- "null"
360
- ]);
361
- var tokenizeJavaScript = (source) => {
362
- const tokens = [];
363
- const len = source.length;
364
- let i = 0;
365
- while (i < len) {
366
- const ch = source[i];
367
- if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
368
- i++;
369
- continue;
370
- }
371
- if (ch === "/" && source[i + 1] === "/") {
372
- const pos = i;
373
- i += 2;
374
- while (i < len && source[i] !== "\n") i++;
375
- tokens.push({ type: "comment", start: pos, end: i });
376
- continue;
377
- }
378
- if (ch === "/" && source[i + 1] === "*") {
379
- const pos = i;
380
- i += 2;
381
- while (i < len && !(source[i] === "*" && source[i + 1] === "/")) i++;
382
- i += 2;
383
- tokens.push({ type: "comment", start: pos, end: i });
384
- continue;
385
- }
386
- if (ch === "`") {
387
- const pos = i;
388
- i++;
389
- while (i < len && source[i] !== "`") {
390
- if (source[i] === "\\") i++;
391
- i++;
392
- }
393
- i++;
394
- tokens.push({ type: "string", start: pos, end: i });
395
- continue;
396
- }
397
- if (ch === '"' || ch === "'") {
398
- const pos = i;
399
- const quote = ch;
400
- i++;
401
- while (i < len && source[i] !== quote) {
402
- if (source[i] === "\\") i++;
403
- i++;
404
- }
405
- i++;
406
- tokens.push({ type: "string", start: pos, end: i });
407
- continue;
408
- }
409
- if (ch >= "0" && ch <= "9" || ch === "." && source[i + 1] >= "0" && source[i + 1] <= "9") {
410
- const pos = i;
411
- if (ch === "0" && (source[i + 1] === "x" || source[i + 1] === "X")) {
412
- i += 2;
413
- while (i < len && /[0-9a-fA-F_]/.test(source[i])) i++;
414
- } else if (ch === "0" && (source[i + 1] === "b" || source[i + 1] === "B")) {
415
- i += 2;
416
- while (i < len && /[01_]/.test(source[i])) i++;
417
- } else {
418
- while (i < len && /[0-9_.]/.test(source[i])) i++;
419
- if (i < len && (source[i] === "e" || source[i] === "E")) {
420
- i++;
421
- if (i < len && (source[i] === "+" || source[i] === "-")) i++;
422
- while (i < len && source[i] >= "0" && source[i] <= "9") i++;
423
- }
424
- }
425
- if (i < len && source[i] === "n") i++;
426
- tokens.push({ type: "number", start: pos, end: i });
427
- continue;
428
- }
429
- if (/[a-zA-Z_$]/.test(ch)) {
430
- const pos = i;
431
- i++;
432
- while (i < len && /[a-zA-Z0-9_$]/.test(source[i])) i++;
433
- const word = source.slice(pos, i);
434
- let j = i;
435
- while (j < len && (source[j] === " " || source[j] === " ")) j++;
436
- if (TYPE_KEYWORDS.has(word)) {
437
- tokens.push({ type: "type", start: pos, end: i });
438
- } else if (KEYWORDS.has(word)) {
439
- tokens.push({ type: "keyword", start: pos, end: i });
440
- } else if (source[j] === "(") {
441
- tokens.push({ type: "function", start: pos, end: i });
442
- } else if (word[0] >= "A" && word[0] <= "Z") {
443
- tokens.push({ type: "type", start: pos, end: i });
444
- } else {
445
- tokens.push({ type: "variable", start: pos, end: i });
446
- }
447
- continue;
448
- }
449
- if ("+-*/%=<>!&|^~?:".includes(ch)) {
450
- const pos = i;
451
- i++;
452
- while (i < len && "+-*/%=<>!&|^~?:".includes(source[i])) i++;
453
- tokens.push({ type: "operator", start: pos, end: i });
454
- continue;
455
- }
456
- if ("(){}[];,.@#".includes(ch)) {
457
- tokens.push({ type: "punctuation", start: i, end: i + 1 });
458
- i++;
459
- continue;
460
- }
461
- tokens.push({ type: "plain", start: i, end: i + 1 });
462
- i++;
463
- }
464
- return tokens;
465
- };
466
-
467
- // src/engine/tokenizers/php.ts
468
- var PHP_KEYWORDS = /* @__PURE__ */ new Set([
469
- "abstract",
470
- "and",
471
- "array",
472
- "as",
473
- "break",
474
- "callable",
475
- "case",
476
- "catch",
477
- "class",
478
- "clone",
479
- "const",
480
- "continue",
481
- "declare",
482
- "default",
483
- "do",
484
- "echo",
485
- "else",
486
- "elseif",
487
- "empty",
488
- "enddeclare",
489
- "endfor",
490
- "endforeach",
491
- "endif",
492
- "endswitch",
493
- "endwhile",
494
- "enum",
495
- "eval",
496
- "exit",
497
- "extends",
498
- "false",
499
- "final",
500
- "finally",
501
- "fn",
502
- "for",
503
- "foreach",
504
- "function",
505
- "global",
506
- "goto",
507
- "if",
508
- "implements",
509
- "include",
510
- "include_once",
511
- "instanceof",
512
- "insteadof",
513
- "interface",
514
- "isset",
515
- "list",
516
- "match",
517
- "namespace",
518
- "new",
519
- "null",
520
- "or",
521
- "print",
522
- "private",
523
- "protected",
524
- "public",
525
- "readonly",
526
- "require",
527
- "require_once",
528
- "return",
529
- "self",
530
- "static",
531
- "switch",
532
- "throw",
533
- "trait",
534
- "true",
535
- "try",
536
- "unset",
537
- "use",
538
- "var",
539
- "while",
540
- "xor",
541
- "yield"
542
- ]);
543
- function tokenizePhpBlock(source, offset) {
544
- const tokens = [];
545
- const len = source.length;
546
- let i = offset;
547
- while (i < len) {
548
- const ch = source[i];
549
- if (ch === "?" && source[i + 1] === ">") {
550
- return { tokens, end: i };
551
- }
552
- if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
553
- i++;
554
- continue;
555
- }
556
- if (ch === "/" && source[i + 1] === "/" || ch === "#") {
557
- const pos = i;
558
- while (i < len && source[i] !== "\n") i++;
559
- tokens.push({ type: "comment", start: pos, end: i });
560
- continue;
561
- }
562
- if (ch === "/" && source[i + 1] === "*") {
563
- const pos = i;
564
- i += 2;
565
- while (i < len && !(source[i] === "*" && source[i + 1] === "/")) i++;
566
- i += 2;
567
- tokens.push({ type: "comment", start: pos, end: i });
568
- continue;
569
- }
570
- if (ch === "$") {
571
- const pos = i;
572
- i++;
573
- while (i < len && /[a-zA-Z0-9_]/.test(source[i])) i++;
574
- tokens.push({ type: "variable", start: pos, end: i });
575
- continue;
576
- }
577
- if (ch === '"' || ch === "'") {
578
- const pos = i;
579
- const quote = ch;
580
- i++;
581
- while (i < len && source[i] !== quote) {
582
- if (source[i] === "\\") i++;
583
- i++;
584
- }
585
- i++;
586
- tokens.push({ type: "string", start: pos, end: i });
587
- continue;
588
- }
589
- if (ch >= "0" && ch <= "9") {
590
- const pos = i;
591
- while (i < len && /[0-9_.]/.test(source[i])) i++;
592
- if (i < len && (source[i] === "e" || source[i] === "E")) {
593
- i++;
594
- if (i < len && (source[i] === "+" || source[i] === "-")) i++;
595
- while (i < len && source[i] >= "0" && source[i] <= "9") i++;
596
- }
597
- tokens.push({ type: "number", start: pos, end: i });
598
- continue;
599
- }
600
- if (/[a-zA-Z_]/.test(ch)) {
601
- const pos = i;
602
- i++;
603
- while (i < len && /[a-zA-Z0-9_]/.test(source[i])) i++;
604
- const word = source.slice(pos, i);
605
- let j = i;
606
- while (j < len && (source[j] === " " || source[j] === " ")) j++;
607
- if (PHP_KEYWORDS.has(word.toLowerCase())) {
608
- tokens.push({ type: "keyword", start: pos, end: i });
609
- } else if (source[j] === "(") {
610
- tokens.push({ type: "function", start: pos, end: i });
611
- } else if (word[0] >= "A" && word[0] <= "Z") {
612
- tokens.push({ type: "type", start: pos, end: i });
613
- } else {
614
- tokens.push({ type: "variable", start: pos, end: i });
615
- }
616
- continue;
617
- }
618
- if ("+-*/%=<>!&|^~?:.".includes(ch)) {
619
- const pos = i;
620
- i++;
621
- while (i < len && "+-*/%=<>!&|^~?:.".includes(source[i])) i++;
622
- tokens.push({ type: "operator", start: pos, end: i });
623
- continue;
624
- }
625
- if ("(){}[];,@\\".includes(ch)) {
626
- tokens.push({ type: "punctuation", start: i, end: i + 1 });
627
- i++;
628
- continue;
629
- }
630
- tokens.push({ type: "plain", start: i, end: i + 1 });
631
- i++;
632
- }
633
- return { tokens, end: i };
634
- }
635
- var tokenizePhp = (source) => {
636
- const tokens = [];
637
- const len = source.length;
638
- let i = 0;
639
- let htmlChunkStart = 0;
640
- while (i < len) {
641
- if (source[i] === "<" && source[i + 1] === "?") {
642
- if (i > htmlChunkStart) {
643
- const htmlChunk = source.slice(htmlChunkStart, i);
644
- const htmlTokens = tokenizeHtml(htmlChunk);
645
- for (const t of htmlTokens) {
646
- tokens.push({ type: t.type, start: t.start + htmlChunkStart, end: t.end + htmlChunkStart });
647
- }
648
- }
649
- const tagStart = i;
650
- i += 2;
651
- if (i < len && source.slice(i, i + 3).toLowerCase() === "php") {
652
- i += 3;
653
- } else if (i < len && source[i] === "=") {
654
- i++;
655
- }
656
- tokens.push({ type: "keyword", start: tagStart, end: i });
657
- const result = tokenizePhpBlock(source, i);
658
- tokens.push(...result.tokens);
659
- i = result.end;
660
- if (i < len && source[i] === "?" && source[i + 1] === ">") {
661
- tokens.push({ type: "keyword", start: i, end: i + 2 });
662
- i += 2;
663
- }
664
- htmlChunkStart = i;
665
- continue;
666
- }
667
- i++;
668
- }
669
- if (htmlChunkStart < len) {
670
- const htmlChunk = source.slice(htmlChunkStart);
671
- const htmlTokens = tokenizeHtml(htmlChunk);
672
- for (const t of htmlTokens) {
673
- tokens.push({ type: t.type, start: t.start + htmlChunkStart, end: t.end + htmlChunkStart });
674
- }
675
- }
676
- return tokens;
677
- };
678
-
679
- // src/engine/tokenizers/python.ts
680
- var KEYWORDS2 = /* @__PURE__ */ new Set([
681
- "and",
682
- "as",
683
- "assert",
684
- "async",
685
- "await",
686
- "break",
687
- "class",
688
- "continue",
689
- "def",
690
- "del",
691
- "elif",
692
- "else",
693
- "except",
694
- "finally",
695
- "for",
696
- "from",
697
- "global",
698
- "if",
699
- "import",
700
- "in",
701
- "is",
702
- "lambda",
703
- "nonlocal",
704
- "not",
705
- "or",
706
- "pass",
707
- "raise",
708
- "return",
709
- "try",
710
- "while",
711
- "with",
712
- "yield",
713
- "True",
714
- "False",
715
- "None"
716
- ]);
717
- var TYPES = /* @__PURE__ */ new Set([
718
- "int",
719
- "str",
720
- "float",
721
- "bool",
722
- "list",
723
- "dict",
724
- "tuple",
725
- "set",
726
- "bytes",
727
- "bytearray",
728
- "memoryview",
729
- "range",
730
- "frozenset",
731
- "complex",
732
- "type",
733
- "object",
734
- "property",
735
- "classmethod",
736
- "staticmethod",
737
- "Any",
738
- "Optional",
739
- "Union",
740
- "Callable",
741
- "List",
742
- "Dict",
743
- "Tuple",
744
- "Set",
745
- "Sequence",
746
- "Mapping",
747
- "Iterator",
748
- "Generator",
749
- "Coroutine"
750
- ]);
751
- var tokenizePython = (source) => {
752
- const tokens = [];
753
- const len = source.length;
754
- let i = 0;
755
- while (i < len) {
756
- const ch = source[i];
757
- if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
758
- i++;
759
- continue;
760
- }
761
- if (ch === "#") {
762
- const pos = i;
763
- i++;
764
- while (i < len && source[i] !== "\n") i++;
765
- tokens.push({ type: "comment", start: pos, end: i });
766
- continue;
767
- }
768
- if (ch === "@" && i + 1 < len && /[a-zA-Z_]/.test(source[i + 1])) {
769
- const pos = i;
770
- i++;
771
- while (i < len && /[a-zA-Z0-9_.]/.test(source[i])) i++;
772
- tokens.push({ type: "keyword", start: pos, end: i });
773
- continue;
774
- }
775
- if (ch === '"' || ch === "'" || (ch === "f" || ch === "r" || ch === "b" || ch === "F" || ch === "R" || ch === "B") && (source[i + 1] === '"' || source[i + 1] === "'")) {
776
- const pos = i;
777
- if (ch !== '"' && ch !== "'") i++;
778
- const quote = source[i];
779
- if (source[i + 1] === quote && source[i + 2] === quote) {
780
- const triple = quote + quote + quote;
781
- i += 3;
782
- while (i < len && source.slice(i, i + 3) !== triple) {
783
- if (source[i] === "\\") i++;
784
- i++;
785
- }
786
- i += 3;
787
- tokens.push({ type: "string", start: pos, end: i });
788
- continue;
789
- }
790
- i++;
791
- while (i < len && source[i] !== quote && source[i] !== "\n") {
792
- if (source[i] === "\\") i++;
793
- i++;
794
- }
795
- if (i < len && source[i] === quote) i++;
796
- tokens.push({ type: "string", start: pos, end: i });
797
- continue;
798
- }
799
- if (ch >= "0" && ch <= "9" || ch === "." && i + 1 < len && source[i + 1] >= "0" && source[i + 1] <= "9") {
800
- const pos = i;
801
- if (ch === "0" && (source[i + 1] === "x" || source[i + 1] === "X")) {
802
- i += 2;
803
- while (i < len && /[0-9a-fA-F_]/.test(source[i])) i++;
804
- } else if (ch === "0" && (source[i + 1] === "b" || source[i + 1] === "B")) {
805
- i += 2;
806
- while (i < len && /[01_]/.test(source[i])) i++;
807
- } else if (ch === "0" && (source[i + 1] === "o" || source[i + 1] === "O")) {
808
- i += 2;
809
- while (i < len && /[0-7_]/.test(source[i])) i++;
810
- } else {
811
- while (i < len && /[0-9_.]/.test(source[i])) i++;
812
- if (i < len && (source[i] === "e" || source[i] === "E")) {
813
- i++;
814
- if (i < len && (source[i] === "+" || source[i] === "-")) i++;
815
- while (i < len && source[i] >= "0" && source[i] <= "9") i++;
816
- }
817
- }
818
- if (i < len && source[i] === "j") i++;
819
- tokens.push({ type: "number", start: pos, end: i });
820
- continue;
821
- }
822
- if (/[a-zA-Z_]/.test(ch)) {
823
- const pos = i;
824
- i++;
825
- while (i < len && /[a-zA-Z0-9_]/.test(source[i])) i++;
826
- const word = source.slice(pos, i);
827
- let j = i;
828
- while (j < len && (source[j] === " " || source[j] === " ")) j++;
829
- if (KEYWORDS2.has(word)) {
830
- tokens.push({ type: "keyword", start: pos, end: i });
831
- } else if (TYPES.has(word)) {
832
- tokens.push({ type: "type", start: pos, end: i });
833
- } else if (source[j] === "(") {
834
- tokens.push({ type: "function", start: pos, end: i });
835
- } else if (word[0] >= "A" && word[0] <= "Z") {
836
- tokens.push({ type: "type", start: pos, end: i });
837
- } else {
838
- tokens.push({ type: "variable", start: pos, end: i });
839
- }
840
- continue;
841
- }
842
- if ("+-*/%=<>!&|^~:@".includes(ch)) {
843
- const pos = i;
844
- i++;
845
- while (i < len && "+-*/%=<>!&|^~:".includes(source[i])) i++;
846
- tokens.push({ type: "operator", start: pos, end: i });
847
- continue;
848
- }
849
- if ("()[]{},.;\\".includes(ch)) {
850
- tokens.push({ type: "punctuation", start: i, end: i + 1 });
851
- i++;
852
- continue;
853
- }
854
- tokens.push({ type: "plain", start: i, end: i + 1 });
855
- i++;
856
- }
857
- return tokens;
858
- };
859
-
860
- // src/engine/tokenizers/go.ts
861
- var KEYWORDS3 = /* @__PURE__ */ new Set([
862
- "break",
863
- "case",
864
- "chan",
865
- "const",
866
- "continue",
867
- "default",
868
- "defer",
869
- "else",
870
- "fallthrough",
871
- "for",
872
- "func",
873
- "go",
874
- "goto",
875
- "if",
876
- "import",
877
- "interface",
878
- "map",
879
- "package",
880
- "range",
881
- "return",
882
- "select",
883
- "struct",
884
- "switch",
885
- "type",
886
- "var",
887
- "nil",
888
- "true",
889
- "false",
890
- "iota"
891
- ]);
892
- var TYPES2 = /* @__PURE__ */ new Set([
893
- "bool",
894
- "byte",
895
- "complex64",
896
- "complex128",
897
- "error",
898
- "float32",
899
- "float64",
900
- "int",
901
- "int8",
902
- "int16",
903
- "int32",
904
- "int64",
905
- "rune",
906
- "string",
907
- "uint",
908
- "uint8",
909
- "uint16",
910
- "uint32",
911
- "uint64",
912
- "uintptr",
913
- "any"
914
- ]);
915
- var BUILTINS = /* @__PURE__ */ new Set([
916
- "make",
917
- "len",
918
- "cap",
919
- "new",
920
- "append",
921
- "copy",
922
- "close",
923
- "delete",
924
- "complex",
925
- "real",
926
- "imag",
927
- "panic",
928
- "recover",
929
- "print",
930
- "println"
931
- ]);
932
- var tokenizeGo = (source) => {
933
- const tokens = [];
934
- const len = source.length;
935
- let i = 0;
936
- while (i < len) {
937
- const ch = source[i];
938
- if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
939
- i++;
940
- continue;
941
- }
942
- if (ch === "/" && source[i + 1] === "/") {
943
- const pos = i;
944
- i += 2;
945
- while (i < len && source[i] !== "\n") i++;
946
- tokens.push({ type: "comment", start: pos, end: i });
947
- continue;
948
- }
949
- if (ch === "/" && source[i + 1] === "*") {
950
- const pos = i;
951
- i += 2;
952
- while (i < len && !(source[i] === "*" && source[i + 1] === "/")) i++;
953
- i += 2;
954
- tokens.push({ type: "comment", start: pos, end: i });
955
- continue;
956
- }
957
- if (ch === "`") {
958
- const pos = i;
959
- i++;
960
- while (i < len && source[i] !== "`") i++;
961
- i++;
962
- tokens.push({ type: "string", start: pos, end: i });
963
- continue;
964
- }
965
- if (ch === '"') {
966
- const pos = i;
967
- i++;
968
- while (i < len && source[i] !== '"' && source[i] !== "\n") {
969
- if (source[i] === "\\") i++;
970
- i++;
971
- }
972
- if (i < len && source[i] === '"') i++;
973
- tokens.push({ type: "string", start: pos, end: i });
974
- continue;
975
- }
976
- if (ch === "'") {
977
- const pos = i;
978
- i++;
979
- while (i < len && source[i] !== "'" && source[i] !== "\n") {
980
- if (source[i] === "\\") i++;
981
- i++;
982
- }
983
- if (i < len && source[i] === "'") i++;
984
- tokens.push({ type: "string", start: pos, end: i });
985
- continue;
986
- }
987
- if (ch >= "0" && ch <= "9" || ch === "." && i + 1 < len && source[i + 1] >= "0" && source[i + 1] <= "9") {
988
- const pos = i;
989
- if (ch === "0" && (source[i + 1] === "x" || source[i + 1] === "X")) {
990
- i += 2;
991
- while (i < len && /[0-9a-fA-F_]/.test(source[i])) i++;
992
- } else if (ch === "0" && (source[i + 1] === "b" || source[i + 1] === "B")) {
993
- i += 2;
994
- while (i < len && /[01_]/.test(source[i])) i++;
995
- } else if (ch === "0" && (source[i + 1] === "o" || source[i + 1] === "O")) {
996
- i += 2;
997
- while (i < len && /[0-7_]/.test(source[i])) i++;
998
- } else {
999
- while (i < len && /[0-9_.]/.test(source[i])) i++;
1000
- if (i < len && (source[i] === "e" || source[i] === "E")) {
1001
- i++;
1002
- if (i < len && (source[i] === "+" || source[i] === "-")) i++;
1003
- while (i < len && source[i] >= "0" && source[i] <= "9") i++;
1004
- }
1005
- }
1006
- if (i < len && source[i] === "i") i++;
1007
- tokens.push({ type: "number", start: pos, end: i });
1008
- continue;
1009
- }
1010
- if (/[a-zA-Z_]/.test(ch)) {
1011
- const pos = i;
1012
- i++;
1013
- while (i < len && /[a-zA-Z0-9_]/.test(source[i])) i++;
1014
- const word = source.slice(pos, i);
1015
- let j = i;
1016
- while (j < len && (source[j] === " " || source[j] === " ")) j++;
1017
- if (KEYWORDS3.has(word)) {
1018
- tokens.push({ type: "keyword", start: pos, end: i });
1019
- } else if (TYPES2.has(word)) {
1020
- tokens.push({ type: "type", start: pos, end: i });
1021
- } else if (BUILTINS.has(word) && source[j] === "(") {
1022
- tokens.push({ type: "function", start: pos, end: i });
1023
- } else if (source[j] === "(") {
1024
- tokens.push({ type: "function", start: pos, end: i });
1025
- } else if (word[0] >= "A" && word[0] <= "Z") {
1026
- tokens.push({ type: "type", start: pos, end: i });
1027
- } else {
1028
- tokens.push({ type: "variable", start: pos, end: i });
1029
- }
1030
- continue;
1031
- }
1032
- if ("+-*/%=<>!&|^~:".includes(ch)) {
1033
- const pos = i;
1034
- i++;
1035
- while (i < len && "+-*/%=<>!&|^~:".includes(source[i])) i++;
1036
- tokens.push({ type: "operator", start: pos, end: i });
1037
- continue;
1038
- }
1039
- if ("(){}[];,.".includes(ch)) {
1040
- tokens.push({ type: "punctuation", start: i, end: i + 1 });
1041
- i++;
1042
- continue;
1043
- }
1044
- tokens.push({ type: "plain", start: i, end: i + 1 });
1045
- i++;
1046
- }
1047
- return tokens;
1048
- };
1049
-
1050
- // src/engine/tokenizers/markdown.ts
1051
- function tokenizeMarkdown(source) {
1052
- const tokens = [];
1053
- const lines = source.split("\n");
1054
- let offset = 0;
1055
- let inFence = false;
1056
- for (const line of lines) {
1057
- const base = offset;
1058
- const len = line.length;
1059
- offset += len + 1;
1060
- if (/^\s*(```|~~~)/.test(line)) {
1061
- tokens.push(tok("string", base, base + len));
1062
- inFence = !inFence;
1063
- continue;
1064
- }
1065
- if (inFence) {
1066
- tokens.push(tok("string", base, base + len));
1067
- continue;
1068
- }
1069
- if (/^#{1,6}\s/.test(line)) {
1070
- tokens.push(tok("keyword", base, base + len));
1071
- continue;
1072
- }
1073
- if (/^\s*>/.test(line)) {
1074
- tokens.push(tok("comment", base, base + len));
1075
- continue;
1076
- }
1077
- const list = line.match(/^(\s*)([-*+]|\d+\.)\s/);
1078
- if (list) {
1079
- const start = base + list[1].length;
1080
- tokens.push(tok("operator", start, start + list[2].length));
1081
- }
1082
- pushInline(line, base, tokens);
1083
- }
1084
- return tokens;
1085
- }
1086
- function pushInline(line, base, out) {
1087
- if (line.length > 5e3) return;
1088
- const spans = [];
1089
- const add = (type, start, end) => {
1090
- spans.push(tok(type, base + start, base + end));
1091
- };
1092
- let m;
1093
- const code = /`[^`]+`/g;
1094
- while (m = code.exec(line)) add("string", m.index, m.index + m[0].length);
1095
- const emph = /(\*\*|__)(?=\S)([\s\S]*?\S)\1|(\*|_)(?=\S)([\s\S]*?\S)\3/g;
1096
- while (m = emph.exec(line)) add("type", m.index, m.index + m[0].length);
1097
- const link = /!?\[[^\]]*\]\(([^)]+)\)/g;
1098
- while (m = link.exec(line)) {
1099
- add("tag", m.index, m.index + m[0].length - m[1].length - 1);
1100
- const urlStart = m.index + m[0].length - m[1].length - 1;
1101
- add("attributeValue", urlStart, urlStart + m[1].length);
1102
- }
1103
- spans.sort((a, b) => a.start - b.start || b.end - a.end);
1104
- let lastEnd = -1;
1105
- for (const s of spans) {
1106
- if (s.start >= lastEnd) {
1107
- out.push(s);
1108
- lastEnd = s.end;
1109
- }
1110
- }
1111
- }
1112
-
1113
- // src/languages/builtin.ts
1114
277
  registerLanguage({
1115
278
  name: "Markdown",
1116
279
  aliases: ["md", "markdown", "mkd"],
@@ -1147,6 +310,16 @@ registerLanguage({
1147
310
  aliases: ["go", "golang"],
1148
311
  tokenize: tokenizeGo
1149
312
  });
313
+ registerLanguage({
314
+ name: "Shell",
315
+ aliases: ["sh", "bash", "zsh", "shell"],
316
+ tokenize: tokenizeShell
317
+ });
318
+ registerLanguage({
319
+ name: "Dotenv",
320
+ aliases: ["env", "dotenv"],
321
+ tokenize: tokenizeDotenv
322
+ });
1150
323
  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";
1151
324
  function LanguageSelector() {
1152
325
  const { language, setLanguage } = useCodeEditor();
@@ -1316,6 +489,7 @@ function useEditorEngine({
1316
489
  const [scrollTop, setScrollTop] = useState(0);
1317
490
  const [scrollLeft, setScrollLeft] = useState(0);
1318
491
  const [activeLine, setActiveLine] = useState(1);
492
+ const tabArmedRef = useRef(false);
1319
493
  const themeColors = useMemo(() => {
1320
494
  const def = getTheme(theme);
1321
495
  return def?.colors ?? DEFAULT_COLORS;
@@ -1376,6 +550,7 @@ function useEditorEngine({
1376
550
  const handleInput = useCallback(() => {
1377
551
  const ta = textareaRef.current;
1378
552
  if (!ta) return;
553
+ tabArmedRef.current = true;
1379
554
  onChangeRef.current?.(ta.value);
1380
555
  autoResize();
1381
556
  updateCursorInfo();
@@ -1411,14 +586,19 @@ function useEditorEngine({
1411
586
  },
1412
587
  [updateCursorInfo]
1413
588
  );
589
+ const handleBlur = useCallback(() => {
590
+ tabArmedRef.current = false;
591
+ }, []);
1414
592
  const handleKeyDown = useCallback(
1415
593
  (e) => {
1416
594
  if (readOnly) {
1417
- if (e.key === "c" && (e.metaKey || e.ctrlKey)) return;
1418
- if (e.key === "a" && (e.metaKey || e.ctrlKey)) return;
1419
- e.preventDefault();
1420
595
  return;
1421
596
  }
597
+ if (e.key === "Escape") {
598
+ tabArmedRef.current = false;
599
+ return;
600
+ }
601
+ if (e.key === "Tab" && !tabArmedRef.current) return;
1422
602
  if (e.key === "Tab") {
1423
603
  e.preventDefault();
1424
604
  const ta = e.currentTarget;
@@ -1467,6 +647,7 @@ function useEditorEngine({
1467
647
  }
1468
648
  if (e.key === "Enter") {
1469
649
  e.preventDefault();
650
+ tabArmedRef.current = true;
1470
651
  const ta = e.currentTarget;
1471
652
  const start = ta.selectionStart;
1472
653
  const end = ta.selectionEnd;
@@ -1495,6 +676,7 @@ function useEditorEngine({
1495
676
  themeColors,
1496
677
  handleKeyDown,
1497
678
  handleInput,
679
+ handleBlur,
1498
680
  handleScroll,
1499
681
  handleSelect,
1500
682
  revealLine,
@@ -1535,8 +717,19 @@ function CodeEditorRoot({
1535
717
  cursorLine,
1536
718
  cursorColumn,
1537
719
  diffBase,
1538
- highlightLines
720
+ highlightLines,
721
+ "aria-label": ariaLabel,
722
+ "aria-labelledby": ariaLabelledBy,
723
+ "aria-describedby": ariaDescribedBy
1539
724
  }) {
725
+ const aria = useMemo(
726
+ () => ({
727
+ "aria-label": ariaLabelledBy ? void 0 : ariaLabel ?? "Code editor",
728
+ "aria-labelledby": ariaLabelledBy,
729
+ "aria-describedby": ariaDescribedBy
730
+ }),
731
+ [ariaLabel, ariaLabelledBy, ariaDescribedBy]
732
+ );
1540
733
  const [currentValue, setCurrentValue] = useControllableState(valueProp, defaultValue, onChange);
1541
734
  const diffAnnotations = useMemo(
1542
735
  () => diffBase == null ? null : annotateLines(diffBase, currentValue),
@@ -1629,9 +822,10 @@ function CodeEditorRoot({
1629
822
  _engineReturn: engineReturn,
1630
823
  _minHeight: minHeight,
1631
824
  _maxHeight: maxHeight,
1632
- _highlightLines: highlightLines
825
+ _highlightLines: highlightLines,
826
+ _aria: aria
1633
827
  }),
1634
- [engineReturn, currentValue, currentLanguage, setLanguage, resolvedTheme, readOnly, showLineNumbers, isWordWrap, tabSizeProp, cursorPosition, selectionLength, setCurrentValue, placeholder, diffAnnotations, minHeight, maxHeight, highlightLines]
828
+ [engineReturn, currentValue, currentLanguage, setLanguage, resolvedTheme, readOnly, showLineNumbers, isWordWrap, tabSizeProp, cursorPosition, selectionLength, setCurrentValue, placeholder, diffAnnotations, minHeight, maxHeight, highlightLines, aria]
1635
829
  );
1636
830
  return /* @__PURE__ */ jsx(CodeEditorContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
1637
831
  "div",
@@ -1667,7 +861,10 @@ function MarkdownEditor({
1667
861
  minHeight,
1668
862
  maxHeight,
1669
863
  className,
1670
- renderPreview
864
+ renderPreview,
865
+ "aria-label": ariaLabel,
866
+ "aria-labelledby": ariaLabelledBy,
867
+ "aria-describedby": ariaDescribedBy
1671
868
  }) {
1672
869
  const [content, setContent] = useControllableState(value, defaultValue, onValueChange);
1673
870
  const showEditor = mode !== "preview";
@@ -1692,6 +889,9 @@ function MarkdownEditor({
1692
889
  placeholder,
1693
890
  minHeight,
1694
891
  maxHeight,
892
+ "aria-label": ariaLabel ?? "Markdown editor",
893
+ "aria-labelledby": ariaLabelledBy,
894
+ "aria-describedby": ariaDescribedBy,
1695
895
  children: /* @__PURE__ */ jsx(CodeEditor.Panel, {})
1696
896
  }
1697
897
  ) }),
@@ -1793,7 +993,10 @@ function FileViewer({
1793
993
  frameProps,
1794
994
  document,
1795
995
  className,
1796
- style
996
+ style,
997
+ "aria-label": ariaLabel,
998
+ "aria-labelledby": ariaLabelledBy,
999
+ "aria-describedby": ariaDescribedBy
1797
1000
  }) {
1798
1001
  const resolved = fileKind ?? resolveFileKind({ filename, mime });
1799
1002
  if (resolved.kind === "document" && view === "rendered" && readOnly && line == null) {
@@ -1887,6 +1090,9 @@ function FileViewer({
1887
1090
  cursorLine: line,
1888
1091
  cursorColumn: column,
1889
1092
  highlightLines: highlightLines ?? (line != null ? [line] : void 0),
1093
+ "aria-label": ariaLabel ?? filename ?? "File contents",
1094
+ "aria-labelledby": ariaLabelledBy,
1095
+ "aria-describedby": ariaDescribedBy,
1890
1096
  children: /* @__PURE__ */ jsx(CodeEditor.Panel, {})
1891
1097
  }
1892
1098
  )
@@ -2076,6 +1282,6 @@ function CodeEditorInputTag(props) {
2076
1282
  return /* @__PURE__ */ jsx(InputTag, { adapter, ...props });
2077
1283
  }
2078
1284
 
2079
- export { CodeEditor, CodeEditorInputTag, FileViewer, MarkdownEditor, clearOfficeViewers, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, officeViewerFor, registerLanguage, registerOfficeViewer, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
1285
+ export { CodeEditor, CodeEditorInputTag, FileViewer, MarkdownEditor, clearOfficeViewers, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, officeViewerFor, registerLanguage, registerOfficeViewer, registerTheme, renderMarkdown, resolveFileKind, useCodeEditor, useCodeEditorInputTagAdapter };
2080
1286
  //# sourceMappingURL=index.js.map
2081
1287
  //# sourceMappingURL=index.js.map