@yurikilian/lex4 0.2.1 → 0.3.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.
Files changed (35) hide show
  1. package/README.md +2 -3
  2. package/dist/components/DocumentView.d.ts.map +1 -1
  3. package/dist/components/HeaderFooterActions.d.ts.map +1 -1
  4. package/dist/components/HeaderFooterToggle.d.ts.map +1 -1
  5. package/dist/components/PageBody.d.ts.map +1 -1
  6. package/dist/components/PageFooter.d.ts.map +1 -1
  7. package/dist/components/PageHeader.d.ts.map +1 -1
  8. package/dist/components/PageView.d.ts.map +1 -1
  9. package/dist/components/Toolbar.d.ts.map +1 -1
  10. package/dist/context/document-provider.d.ts.map +1 -1
  11. package/dist/engine/overflow.d.ts +2 -0
  12. package/dist/engine/overflow.d.ts.map +1 -1
  13. package/dist/hooks/use-pagination.d.ts.map +1 -1
  14. package/dist/i18n/defaults.d.ts.map +1 -1
  15. package/dist/i18n/index.d.ts +1 -0
  16. package/dist/i18n/index.d.ts.map +1 -1
  17. package/dist/i18n/pt-BR.d.ts +3 -0
  18. package/dist/i18n/pt-BR.d.ts.map +1 -0
  19. package/dist/i18n/types.d.ts +42 -0
  20. package/dist/i18n/types.d.ts.map +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/lex4-editor.cjs +1025 -444
  24. package/dist/lex4-editor.cjs.map +1 -1
  25. package/dist/lex4-editor.js +1028 -447
  26. package/dist/lex4-editor.js.map +1 -1
  27. package/dist/lexical/plugins/font-plugin.d.ts +1 -1
  28. package/dist/lexical/plugins/history-capture-plugin.d.ts.map +1 -1
  29. package/dist/lexical/plugins/overflow-plugin.d.ts.map +1 -1
  30. package/dist/lexical/utils/mid-block-split.d.ts +34 -0
  31. package/dist/lexical/utils/mid-block-split.d.ts.map +1 -0
  32. package/dist/style.css +49 -28
  33. package/dist/utils/editor-state-utils.d.ts +10 -0
  34. package/dist/utils/editor-state-utils.d.ts.map +1 -1
  35. package/package.json +1 -1
@@ -2,10 +2,10 @@ var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
5
- import React, { createContext, useContext, useReducer, useState, useRef, useMemo, useCallback, useEffect, forwardRef, createElement, useImperativeHandle } from "react";
6
- import { $getRoot, $createRangeSelectionFromDom, $getSelection, $isRangeSelection, $isTextNode, FORMAT_ELEMENT_COMMAND, FORMAT_TEXT_COMMAND, OUTDENT_CONTENT_COMMAND, INDENT_CONTENT_COMMAND, $selectAll, KEY_TAB_COMMAND, COMMAND_PRIORITY_LOW, $isElementNode, $isParagraphNode, $setSelection, FOCUS_COMMAND, CONTROLLED_TEXT_INSERTION_COMMAND, KEY_DOWN_COMMAND, PASTE_COMMAND, KEY_ENTER_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_CRITICAL, $applyNodeReplacement, DecoratorNode, createCommand, $insertNodes, COMMAND_PRIORITY_EDITOR } from "lexical";
5
+ import React, { createContext, useContext, useMemo, useReducer, useState, useRef, useCallback, useEffect, forwardRef, createElement, useImperativeHandle } from "react";
6
+ import { $getRoot, $createRangeSelectionFromDom, $getSelection, $isRangeSelection, $isTextNode, FORMAT_ELEMENT_COMMAND, FORMAT_TEXT_COMMAND, OUTDENT_CONTENT_COMMAND, INDENT_CONTENT_COMMAND, $selectAll, KEY_TAB_COMMAND, COMMAND_PRIORITY_LOW, $isElementNode, $isParagraphNode, $setSelection, FOCUS_COMMAND, $splitNode, $getNearestNodeFromDOMNode, CONTROLLED_TEXT_INSERTION_COMMAND, KEY_DOWN_COMMAND, PASTE_COMMAND, KEY_ENTER_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_CRITICAL, $applyNodeReplacement, DecoratorNode, createCommand, $insertNodes, COMMAND_PRIORITY_EDITOR } from "lexical";
7
7
  import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
8
- import { INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, ListNode, ListItemNode } from "@lexical/list";
8
+ import { INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, ListNode, ListItemNode, $isListNode } from "@lexical/list";
9
9
  import { LexicalComposer } from "@lexical/react/LexicalComposer";
10
10
  import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
11
11
  import { ContentEditable } from "@lexical/react/LexicalContentEditable";
@@ -452,6 +452,258 @@ function jumpToHistoryEntry(history, entryIndex) {
452
452
  caretSelection: ((_b = nextHistory.entries[nextHistory.cursor - 1]) == null ? void 0 : _b.caretSelection) ?? null
453
453
  };
454
454
  }
455
+ const DEFAULT_TRANSLATIONS = {
456
+ toolbar: {
457
+ undo: "Undo",
458
+ redo: "Redo",
459
+ bold: "Bold (Ctrl+B)",
460
+ italic: "Italic (Ctrl+I)",
461
+ underline: "Underline (Ctrl+U)",
462
+ strikethrough: "Strikethrough",
463
+ alignLeft: "Align Left",
464
+ alignCenter: "Align Center",
465
+ alignRight: "Align Right",
466
+ justify: "Justify",
467
+ numberedList: "Numbered List",
468
+ bulletList: "Bullet List",
469
+ indent: "Indent",
470
+ outdent: "Outdent",
471
+ openHistory: "Open History",
472
+ closeHistory: "Close History"
473
+ },
474
+ history: {
475
+ title: "History",
476
+ subtitle: "Word-style session history (last 100 actions)",
477
+ empty: "No history yet.",
478
+ clearHistory: "Clear History",
479
+ actions: {
480
+ enabledHeadersFooters: "Enabled headers and footers",
481
+ disabledHeadersFooters: "Disabled headers and footers",
482
+ copiedHeaderToAll: "Copied header to all pages",
483
+ copiedFooterToAll: "Copied footer to all pages",
484
+ clearedHeader: "Cleared header",
485
+ clearedFooter: "Cleared footer",
486
+ clearedAllHeaders: "Cleared all headers",
487
+ clearedAllFooters: "Cleared all footers",
488
+ pageCounterSet: "Page counter set to {{value}}",
489
+ boldApplied: "Bold applied",
490
+ italicApplied: "Italic applied",
491
+ underlineApplied: "Underline applied",
492
+ strikethroughApplied: "Strikethrough applied",
493
+ alignedLeft: "Aligned left",
494
+ alignedCenter: "Aligned center",
495
+ alignedRight: "Aligned right",
496
+ justifiedText: "Justified text",
497
+ insertedNumberedList: "Inserted numbered list",
498
+ insertedBulletList: "Inserted bullet list",
499
+ indentedContent: "Indented content",
500
+ outdentedContent: "Outdented content",
501
+ fontChanged: "Font changed to {{value}}",
502
+ fontSizeChanged: "Font size changed to {{value}}pt"
503
+ }
504
+ },
505
+ variables: {
506
+ title: "Variables",
507
+ available: "{{count}} available",
508
+ refreshVariables: "Refresh variables",
509
+ searchPlaceholder: "Search variables...",
510
+ noVariablesFound: "No variables found",
511
+ insertVariable: "Insert variable {{key}}",
512
+ openPanel: "Open Variables",
513
+ closePanel: "Close Variables"
514
+ },
515
+ header: {
516
+ placeholder: "Header"
517
+ },
518
+ footer: {
519
+ placeholder: "Footer"
520
+ },
521
+ body: {
522
+ placeholder: "Start typing..."
523
+ },
524
+ pageCounter: {
525
+ format: "Page {{current}} of {{total}}"
526
+ },
527
+ regions: {
528
+ body: "body",
529
+ header: "header",
530
+ footer: "footer",
531
+ document: "document",
532
+ toolbar: "toolbar",
533
+ overflow: "overflow",
534
+ history: "history",
535
+ page: "Page {{page}}"
536
+ },
537
+ headerFooter: {
538
+ label: "Headers & Footers"
539
+ },
540
+ sidebar: {
541
+ close: "Close sidebar"
542
+ },
543
+ historyLabels: {
544
+ typedText: "Typed text",
545
+ pastedContent: "Pasted content",
546
+ insertedLineBreak: "Inserted line break",
547
+ deletedBackward: "Deleted backward",
548
+ deletedForward: "Deleted forward",
549
+ formattedText: "Formatted text",
550
+ formattedParagraph: "Formatted paragraph",
551
+ editedBody: "Edited body",
552
+ editedHeader: "Edited header",
553
+ editedFooter: "Edited footer",
554
+ clearedDocumentBody: "Cleared document body",
555
+ resizedHeader: "Resized header",
556
+ resizedFooter: "Resized footer",
557
+ addedPage: "Added page",
558
+ removedPage: "Removed page",
559
+ documentReflow: "Document reflow",
560
+ updatedDocument: "Updated document"
561
+ }
562
+ };
563
+ const PT_BR_TRANSLATIONS = {
564
+ toolbar: {
565
+ undo: "Desfazer",
566
+ redo: "Refazer",
567
+ bold: "Negrito (Ctrl+B)",
568
+ italic: "Itálico (Ctrl+I)",
569
+ underline: "Sublinhado (Ctrl+U)",
570
+ strikethrough: "Tachado",
571
+ alignLeft: "Alinhar à Esquerda",
572
+ alignCenter: "Centralizar",
573
+ alignRight: "Alinhar à Direita",
574
+ justify: "Justificar",
575
+ numberedList: "Lista Numerada",
576
+ bulletList: "Lista com Marcadores",
577
+ indent: "Aumentar Recuo",
578
+ outdent: "Diminuir Recuo",
579
+ openHistory: "Abrir Histórico",
580
+ closeHistory: "Fechar Histórico"
581
+ },
582
+ history: {
583
+ title: "Histórico",
584
+ subtitle: "Histórico de sessão (últimas 100 ações)",
585
+ empty: "Nenhum histórico ainda.",
586
+ clearHistory: "Limpar Histórico",
587
+ actions: {
588
+ enabledHeadersFooters: "Cabeçalhos e rodapés ativados",
589
+ disabledHeadersFooters: "Cabeçalhos e rodapés desativados",
590
+ copiedHeaderToAll: "Cabeçalho copiado para todas as páginas",
591
+ copiedFooterToAll: "Rodapé copiado para todas as páginas",
592
+ clearedHeader: "Cabeçalho limpo",
593
+ clearedFooter: "Rodapé limpo",
594
+ clearedAllHeaders: "Todos os cabeçalhos limpos",
595
+ clearedAllFooters: "Todos os rodapés limpos",
596
+ pageCounterSet: "Contador de páginas definido como {{value}}",
597
+ boldApplied: "Negrito aplicado",
598
+ italicApplied: "Itálico aplicado",
599
+ underlineApplied: "Sublinhado aplicado",
600
+ strikethroughApplied: "Tachado aplicado",
601
+ alignedLeft: "Alinhado à esquerda",
602
+ alignedCenter: "Centralizado",
603
+ alignedRight: "Alinhado à direita",
604
+ justifiedText: "Texto justificado",
605
+ insertedNumberedList: "Lista numerada inserida",
606
+ insertedBulletList: "Lista com marcadores inserida",
607
+ indentedContent: "Conteúdo recuado",
608
+ outdentedContent: "Recuo reduzido",
609
+ fontChanged: "Fonte alterada para {{value}}",
610
+ fontSizeChanged: "Tamanho da fonte alterado para {{value}}pt"
611
+ }
612
+ },
613
+ variables: {
614
+ title: "Variáveis",
615
+ available: "{{count}} disponíveis",
616
+ refreshVariables: "Atualizar variáveis",
617
+ searchPlaceholder: "Buscar variáveis...",
618
+ noVariablesFound: "Nenhuma variável encontrada",
619
+ insertVariable: "Inserir variável {{key}}",
620
+ openPanel: "Abrir Variáveis",
621
+ closePanel: "Fechar Variáveis"
622
+ },
623
+ header: {
624
+ placeholder: "Cabeçalho"
625
+ },
626
+ footer: {
627
+ placeholder: "Rodapé"
628
+ },
629
+ body: {
630
+ placeholder: "Comece a digitar..."
631
+ },
632
+ pageCounter: {
633
+ format: "Página {{current}} de {{total}}"
634
+ },
635
+ regions: {
636
+ body: "corpo",
637
+ header: "cabeçalho",
638
+ footer: "rodapé",
639
+ document: "documento",
640
+ toolbar: "barra de ferramentas",
641
+ overflow: "estouro",
642
+ history: "histórico",
643
+ page: "Página {{page}}"
644
+ },
645
+ headerFooter: {
646
+ label: "Cabeçalhos e Rodapés"
647
+ },
648
+ sidebar: {
649
+ close: "Fechar barra lateral"
650
+ },
651
+ historyLabels: {
652
+ typedText: "Texto digitado",
653
+ pastedContent: "Conteúdo colado",
654
+ insertedLineBreak: "Quebra de linha inserida",
655
+ deletedBackward: "Exclusão para trás",
656
+ deletedForward: "Exclusão para frente",
657
+ formattedText: "Texto formatado",
658
+ formattedParagraph: "Parágrafo formatado",
659
+ editedBody: "Corpo editado",
660
+ editedHeader: "Cabeçalho editado",
661
+ editedFooter: "Rodapé editado",
662
+ clearedDocumentBody: "Corpo do documento limpo",
663
+ resizedHeader: "Cabeçalho redimensionado",
664
+ resizedFooter: "Rodapé redimensionado",
665
+ addedPage: "Página adicionada",
666
+ removedPage: "Página removida",
667
+ documentReflow: "Redistribuição do documento",
668
+ updatedDocument: "Documento atualizado"
669
+ }
670
+ };
671
+ function deepMerge(target, source) {
672
+ if (typeof target !== "object" || target === null) {
673
+ return source ?? target;
674
+ }
675
+ const result = { ...target };
676
+ for (const key of Object.keys(source)) {
677
+ const sourceVal = source[key];
678
+ const targetVal = target[key];
679
+ if (sourceVal !== void 0 && typeof sourceVal === "object" && sourceVal !== null && !Array.isArray(sourceVal) && typeof targetVal === "object" && targetVal !== null) {
680
+ result[key] = deepMerge(targetVal, sourceVal);
681
+ } else if (sourceVal !== void 0) {
682
+ result[key] = sourceVal;
683
+ }
684
+ }
685
+ return result;
686
+ }
687
+ const TranslationsContext = createContext(DEFAULT_TRANSLATIONS);
688
+ function useTranslations() {
689
+ return useContext(TranslationsContext);
690
+ }
691
+ function interpolate(template, params) {
692
+ return template.replace(
693
+ /\{\{(\w+)\}\}/g,
694
+ (_, key) => String(params[key] ?? `{{${key}}}`)
695
+ );
696
+ }
697
+ const TranslationsProvider = ({
698
+ translations,
699
+ children
700
+ }) => {
701
+ const merged = useMemo(
702
+ () => translations ? deepMerge(DEFAULT_TRANSLATIONS, translations) : DEFAULT_TRANSLATIONS,
703
+ [translations]
704
+ );
705
+ return /* @__PURE__ */ jsx(TranslationsContext.Provider, { value: merged, children });
706
+ };
455
707
  const HISTORY_RESTORE_SUPPRESSION_MS = 100;
456
708
  const HISTORY_BATCH_FLUSH_MS = 16;
457
709
  function cloneDocumentSnapshot(document2) {
@@ -524,12 +776,15 @@ function getPageNumber(document2, pageId) {
524
776
  const index = document2.pages.findIndex((page) => page.id === pageId);
525
777
  return index >= 0 ? index + 1 : null;
526
778
  }
527
- function describeAction(action, document2) {
779
+ function describeAction(action, document2, t) {
780
+ const pageSuffix = (pageId) => {
781
+ const num = getPageNumber(document2, pageId);
782
+ return num ? ` - ${interpolate(t.regions.page, { page: num })}` : "";
783
+ };
528
784
  switch (action.type) {
529
785
  case "UPDATE_PAGE_BODY": {
530
- const pageNumber = getPageNumber(document2, action.pageId);
531
786
  return {
532
- label: pageNumber ? `Edited body - Page ${pageNumber}` : "Edited body",
787
+ label: t.historyLabels.editedBody + pageSuffix(action.pageId),
533
788
  source: "body",
534
789
  pageId: action.pageId,
535
790
  region: "body"
@@ -537,9 +792,8 @@ function describeAction(action, document2) {
537
792
  }
538
793
  case "UPDATE_PAGE_HEADER":
539
794
  case "UPDATE_PAGE_HEADER_CONTENT": {
540
- const pageNumber = getPageNumber(document2, action.pageId);
541
795
  return {
542
- label: pageNumber ? `Edited header - Page ${pageNumber}` : "Edited header",
796
+ label: t.historyLabels.editedHeader + pageSuffix(action.pageId),
543
797
  source: "header",
544
798
  pageId: action.pageId,
545
799
  region: "header"
@@ -547,9 +801,8 @@ function describeAction(action, document2) {
547
801
  }
548
802
  case "UPDATE_PAGE_FOOTER":
549
803
  case "UPDATE_PAGE_FOOTER_CONTENT": {
550
- const pageNumber = getPageNumber(document2, action.pageId);
551
804
  return {
552
- label: pageNumber ? `Edited footer - Page ${pageNumber}` : "Edited footer",
805
+ label: t.historyLabels.editedFooter + pageSuffix(action.pageId),
553
806
  source: "footer",
554
807
  pageId: action.pageId,
555
808
  region: "footer"
@@ -557,43 +810,41 @@ function describeAction(action, document2) {
557
810
  }
558
811
  case "SET_HEADER_FOOTER_ENABLED":
559
812
  return {
560
- label: action.enabled ? "Enabled headers and footers" : "Disabled headers and footers",
813
+ label: action.enabled ? t.history.actions.enabledHeadersFooters : t.history.actions.disabledHeadersFooters,
561
814
  source: "document",
562
815
  region: "document"
563
816
  };
564
817
  case "SET_PAGE_COUNTER_MODE":
565
818
  return {
566
- label: `Page counter set to ${action.mode}`,
819
+ label: interpolate(t.history.actions.pageCounterSet, { value: action.mode }),
567
820
  source: "document",
568
821
  region: "document"
569
822
  };
570
823
  case "COPY_HEADER_TO_ALL":
571
824
  return {
572
- label: "Copied header to all pages",
825
+ label: t.history.actions.copiedHeaderToAll,
573
826
  source: "toolbar",
574
827
  pageId: action.sourcePageId,
575
828
  region: "header"
576
829
  };
577
830
  case "COPY_FOOTER_TO_ALL":
578
831
  return {
579
- label: "Copied footer to all pages",
832
+ label: t.history.actions.copiedFooterToAll,
580
833
  source: "toolbar",
581
834
  pageId: action.sourcePageId,
582
835
  region: "footer"
583
836
  };
584
837
  case "CLEAR_HEADER": {
585
- const pageNumber = getPageNumber(document2, action.pageId);
586
838
  return {
587
- label: pageNumber ? `Cleared header - Page ${pageNumber}` : "Cleared header",
839
+ label: t.history.actions.clearedHeader + pageSuffix(action.pageId),
588
840
  source: "toolbar",
589
841
  pageId: action.pageId,
590
842
  region: "header"
591
843
  };
592
844
  }
593
845
  case "CLEAR_FOOTER": {
594
- const pageNumber = getPageNumber(document2, action.pageId);
595
846
  return {
596
- label: pageNumber ? `Cleared footer - Page ${pageNumber}` : "Cleared footer",
847
+ label: t.history.actions.clearedFooter + pageSuffix(action.pageId),
597
848
  source: "toolbar",
598
849
  pageId: action.pageId,
599
850
  region: "footer"
@@ -601,35 +852,33 @@ function describeAction(action, document2) {
601
852
  }
602
853
  case "CLEAR_ALL_HEADERS":
603
854
  return {
604
- label: "Cleared all headers",
855
+ label: t.history.actions.clearedAllHeaders,
605
856
  source: "toolbar",
606
857
  region: "header"
607
858
  };
608
859
  case "CLEAR_ALL_FOOTERS":
609
860
  return {
610
- label: "Cleared all footers",
861
+ label: t.history.actions.clearedAllFooters,
611
862
  source: "toolbar",
612
863
  region: "footer"
613
864
  };
614
865
  case "CLEAR_DOCUMENT_CONTENT":
615
866
  return {
616
- label: "Cleared document body",
867
+ label: t.historyLabels.clearedDocumentBody,
617
868
  source: "document",
618
869
  region: "document"
619
870
  };
620
871
  case "SET_HEADER_HEIGHT": {
621
- const pageNumber = getPageNumber(document2, action.pageId);
622
872
  return {
623
- label: pageNumber ? `Resized header - Page ${pageNumber}` : "Resized header",
873
+ label: t.historyLabels.resizedHeader + pageSuffix(action.pageId),
624
874
  source: "header",
625
875
  pageId: action.pageId,
626
876
  region: "header"
627
877
  };
628
878
  }
629
879
  case "SET_FOOTER_HEIGHT": {
630
- const pageNumber = getPageNumber(document2, action.pageId);
631
880
  return {
632
- label: pageNumber ? `Resized footer - Page ${pageNumber}` : "Resized footer",
881
+ label: t.historyLabels.resizedFooter + pageSuffix(action.pageId),
633
882
  source: "footer",
634
883
  pageId: action.pageId,
635
884
  region: "footer"
@@ -637,25 +886,25 @@ function describeAction(action, document2) {
637
886
  }
638
887
  case "ADD_PAGE":
639
888
  return {
640
- label: "Added page",
889
+ label: t.historyLabels.addedPage,
641
890
  source: "overflow",
642
891
  region: "document"
643
892
  };
644
893
  case "REMOVE_PAGE":
645
894
  return {
646
- label: "Removed page",
895
+ label: t.historyLabels.removedPage,
647
896
  source: "overflow",
648
897
  region: "document"
649
898
  };
650
899
  case "SET_DOCUMENT":
651
900
  return {
652
- label: "Document reflow",
901
+ label: t.historyLabels.documentReflow,
653
902
  source: "overflow",
654
903
  region: "document"
655
904
  };
656
905
  default:
657
906
  return {
658
- label: "Updated document",
907
+ label: t.historyLabels.updatedDocument,
659
908
  source: "document",
660
909
  region: "document"
661
910
  };
@@ -682,6 +931,7 @@ const DocumentProvider = ({
682
931
  const [globalSelectionActive, setGlobalSelectionActive] = useState(false);
683
932
  const [historySidebarOpen, setHistorySidebarOpen] = useState(true);
684
933
  const [focusAtEndVersion, setFocusAtEndVersion] = useState(0);
934
+ const t = useTranslations();
685
935
  const activeEditorRef = useRef(null);
686
936
  const activeCaretPositionRef = useRef(null);
687
937
  const pendingCaretPositionRef = useRef(null);
@@ -875,7 +1125,7 @@ const DocumentProvider = ({
875
1125
  };
876
1126
  scheduleHistoryBatchFlush();
877
1127
  } else {
878
- const descriptor = describeAction(action, currentDocument);
1128
+ const descriptor = describeAction(action, currentDocument, t);
879
1129
  setHistoryState((previousHistory) => {
880
1130
  const nextHistory = recordHistoryEntry(
881
1131
  previousHistory,
@@ -1134,20 +1384,33 @@ const createLucideIcon = (iconName, iconNode) => {
1134
1384
  * This source code is licensed under the ISC license.
1135
1385
  * See the LICENSE file in the root directory of this source tree.
1136
1386
  */
1137
- const __iconNode$m = [
1387
+ const __iconNode$q = [
1388
+ ["path", { d: "m15 16 2.536-7.328a1.02 1.02 1 0 1 1.928 0L22 16", key: "xik6mr" }],
1389
+ ["path", { d: "M15.697 14h5.606", key: "1stdlc" }],
1390
+ ["path", { d: "m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16", key: "d5nyq2" }],
1391
+ ["path", { d: "M3.304 13h6.392", key: "1q3zxz" }]
1392
+ ];
1393
+ const ALargeSmall = createLucideIcon("a-large-small", __iconNode$q);
1394
+ /**
1395
+ * @license lucide-react v1.8.0 - ISC
1396
+ *
1397
+ * This source code is licensed under the ISC license.
1398
+ * See the LICENSE file in the root directory of this source tree.
1399
+ */
1400
+ const __iconNode$p = [
1138
1401
  [
1139
1402
  "path",
1140
1403
  { d: "M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8", key: "mg9rjx" }
1141
1404
  ]
1142
1405
  ];
1143
- const Bold = createLucideIcon("bold", __iconNode$m);
1406
+ const Bold = createLucideIcon("bold", __iconNode$p);
1144
1407
  /**
1145
1408
  * @license lucide-react v1.8.0 - ISC
1146
1409
  *
1147
1410
  * This source code is licensed under the ISC license.
1148
1411
  * See the LICENSE file in the root directory of this source tree.
1149
1412
  */
1150
- const __iconNode$l = [
1413
+ const __iconNode$o = [
1151
1414
  [
1152
1415
  "path",
1153
1416
  { d: "M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1", key: "ezmyqa" }
@@ -1160,27 +1423,35 @@ const __iconNode$l = [
1160
1423
  }
1161
1424
  ]
1162
1425
  ];
1163
- const Braces = createLucideIcon("braces", __iconNode$l);
1426
+ const Braces = createLucideIcon("braces", __iconNode$o);
1164
1427
  /**
1165
1428
  * @license lucide-react v1.8.0 - ISC
1166
1429
  *
1167
1430
  * This source code is licensed under the ISC license.
1168
1431
  * See the LICENSE file in the root directory of this source tree.
1169
1432
  */
1170
- const __iconNode$k = [
1433
+ const __iconNode$n = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
1434
+ const ChevronDown = createLucideIcon("chevron-down", __iconNode$n);
1435
+ /**
1436
+ * @license lucide-react v1.8.0 - ISC
1437
+ *
1438
+ * This source code is licensed under the ISC license.
1439
+ * See the LICENSE file in the root directory of this source tree.
1440
+ */
1441
+ const __iconNode$m = [
1171
1442
  ["line", { x1: "15", x2: "15", y1: "12", y2: "18", key: "1p7wdc" }],
1172
1443
  ["line", { x1: "12", x2: "18", y1: "15", y2: "15", key: "1nscbv" }],
1173
1444
  ["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2", key: "17jyea" }],
1174
1445
  ["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
1175
1446
  ];
1176
- const CopyPlus = createLucideIcon("copy-plus", __iconNode$k);
1447
+ const CopyPlus = createLucideIcon("copy-plus", __iconNode$m);
1177
1448
  /**
1178
1449
  * @license lucide-react v1.8.0 - ISC
1179
1450
  *
1180
1451
  * This source code is licensed under the ISC license.
1181
1452
  * See the LICENSE file in the root directory of this source tree.
1182
1453
  */
1183
- const __iconNode$j = [
1454
+ const __iconNode$l = [
1184
1455
  [
1185
1456
  "path",
1186
1457
  {
@@ -1190,65 +1461,52 @@ const __iconNode$j = [
1190
1461
  ],
1191
1462
  ["path", { d: "m5.082 11.09 8.828 8.828", key: "1wx5vj" }]
1192
1463
  ];
1193
- const Eraser = createLucideIcon("eraser", __iconNode$j);
1464
+ const Eraser = createLucideIcon("eraser", __iconNode$l);
1194
1465
  /**
1195
1466
  * @license lucide-react v1.8.0 - ISC
1196
1467
  *
1197
1468
  * This source code is licensed under the ISC license.
1198
1469
  * See the LICENSE file in the root directory of this source tree.
1199
1470
  */
1200
- const __iconNode$i = [
1201
- ["line", { x1: "4", x2: "20", y1: "9", y2: "9", key: "4lhtct" }],
1202
- ["line", { x1: "4", x2: "20", y1: "15", y2: "15", key: "vyu0kd" }],
1203
- ["line", { x1: "10", x2: "8", y1: "3", y2: "21", key: "1ggp8o" }],
1204
- ["line", { x1: "16", x2: "14", y1: "3", y2: "21", key: "weycgp" }]
1205
- ];
1206
- const Hash = createLucideIcon("hash", __iconNode$i);
1207
- /**
1208
- * @license lucide-react v1.8.0 - ISC
1209
- *
1210
- * This source code is licensed under the ISC license.
1211
- * See the LICENSE file in the root directory of this source tree.
1212
- */
1213
- const __iconNode$h = [
1471
+ const __iconNode$k = [
1214
1472
  ["line", { x1: "19", x2: "10", y1: "4", y2: "4", key: "15jd3p" }],
1215
1473
  ["line", { x1: "14", x2: "5", y1: "20", y2: "20", key: "bu0au3" }],
1216
1474
  ["line", { x1: "15", x2: "9", y1: "4", y2: "20", key: "uljnxc" }]
1217
1475
  ];
1218
- const Italic = createLucideIcon("italic", __iconNode$h);
1476
+ const Italic = createLucideIcon("italic", __iconNode$k);
1219
1477
  /**
1220
1478
  * @license lucide-react v1.8.0 - ISC
1221
1479
  *
1222
1480
  * This source code is licensed under the ISC license.
1223
1481
  * See the LICENSE file in the root directory of this source tree.
1224
1482
  */
1225
- const __iconNode$g = [
1483
+ const __iconNode$j = [
1226
1484
  ["path", { d: "M21 5H11", key: "us1j55" }],
1227
1485
  ["path", { d: "M21 12H11", key: "wd7e0v" }],
1228
1486
  ["path", { d: "M21 19H11", key: "saa85w" }],
1229
1487
  ["path", { d: "m7 8-4 4 4 4", key: "o5hrat" }]
1230
1488
  ];
1231
- const ListIndentDecrease = createLucideIcon("list-indent-decrease", __iconNode$g);
1489
+ const ListIndentDecrease = createLucideIcon("list-indent-decrease", __iconNode$j);
1232
1490
  /**
1233
1491
  * @license lucide-react v1.8.0 - ISC
1234
1492
  *
1235
1493
  * This source code is licensed under the ISC license.
1236
1494
  * See the LICENSE file in the root directory of this source tree.
1237
1495
  */
1238
- const __iconNode$f = [
1496
+ const __iconNode$i = [
1239
1497
  ["path", { d: "M21 5H11", key: "us1j55" }],
1240
1498
  ["path", { d: "M21 12H11", key: "wd7e0v" }],
1241
1499
  ["path", { d: "M21 19H11", key: "saa85w" }],
1242
1500
  ["path", { d: "m3 8 4 4-4 4", key: "1a3j6y" }]
1243
1501
  ];
1244
- const ListIndentIncrease = createLucideIcon("list-indent-increase", __iconNode$f);
1502
+ const ListIndentIncrease = createLucideIcon("list-indent-increase", __iconNode$i);
1245
1503
  /**
1246
1504
  * @license lucide-react v1.8.0 - ISC
1247
1505
  *
1248
1506
  * This source code is licensed under the ISC license.
1249
1507
  * See the LICENSE file in the root directory of this source tree.
1250
1508
  */
1251
- const __iconNode$e = [
1509
+ const __iconNode$h = [
1252
1510
  ["path", { d: "M11 5h10", key: "1cz7ny" }],
1253
1511
  ["path", { d: "M11 12h10", key: "1438ji" }],
1254
1512
  ["path", { d: "M11 19h10", key: "11t30w" }],
@@ -1256,14 +1514,14 @@ const __iconNode$e = [
1256
1514
  ["path", { d: "M4 9h2", key: "r1h2o0" }],
1257
1515
  ["path", { d: "M6.5 20H3.4c0-1 2.6-1.925 2.6-3.5a1.5 1.5 0 0 0-2.6-1.02", key: "xtkcd5" }]
1258
1516
  ];
1259
- const ListOrdered = createLucideIcon("list-ordered", __iconNode$e);
1517
+ const ListOrdered = createLucideIcon("list-ordered", __iconNode$h);
1260
1518
  /**
1261
1519
  * @license lucide-react v1.8.0 - ISC
1262
1520
  *
1263
1521
  * This source code is licensed under the ISC license.
1264
1522
  * See the LICENSE file in the root directory of this source tree.
1265
1523
  */
1266
- const __iconNode$d = [
1524
+ const __iconNode$g = [
1267
1525
  ["path", { d: "M3 5h.01", key: "18ugdj" }],
1268
1526
  ["path", { d: "M3 12h.01", key: "nlz23k" }],
1269
1527
  ["path", { d: "M3 19h.01", key: "noohij" }],
@@ -1271,127 +1529,168 @@ const __iconNode$d = [
1271
1529
  ["path", { d: "M8 12h13", key: "1za7za" }],
1272
1530
  ["path", { d: "M8 19h13", key: "m83p4d" }]
1273
1531
  ];
1274
- const List = createLucideIcon("list", __iconNode$d);
1532
+ const List = createLucideIcon("list", __iconNode$g);
1275
1533
  /**
1276
1534
  * @license lucide-react v1.8.0 - ISC
1277
1535
  *
1278
1536
  * This source code is licensed under the ISC license.
1279
1537
  * See the LICENSE file in the root directory of this source tree.
1280
1538
  */
1281
- const __iconNode$c = [
1539
+ const __iconNode$f = [
1282
1540
  ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }],
1283
1541
  ["path", { d: "M15 3v18", key: "14nvp0" }]
1284
1542
  ];
1285
- const PanelRight = createLucideIcon("panel-right", __iconNode$c);
1543
+ const PanelRight = createLucideIcon("panel-right", __iconNode$f);
1286
1544
  /**
1287
1545
  * @license lucide-react v1.8.0 - ISC
1288
1546
  *
1289
1547
  * This source code is licensed under the ISC license.
1290
1548
  * See the LICENSE file in the root directory of this source tree.
1291
1549
  */
1292
- const __iconNode$b = [
1550
+ const __iconNode$e = [
1293
1551
  ["path", { d: "m15 14 5-5-5-5", key: "12vg1m" }],
1294
1552
  ["path", { d: "M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13", key: "6uklza" }]
1295
1553
  ];
1296
- const Redo2 = createLucideIcon("redo-2", __iconNode$b);
1554
+ const Redo2 = createLucideIcon("redo-2", __iconNode$e);
1297
1555
  /**
1298
1556
  * @license lucide-react v1.8.0 - ISC
1299
1557
  *
1300
1558
  * This source code is licensed under the ISC license.
1301
1559
  * See the LICENSE file in the root directory of this source tree.
1302
1560
  */
1303
- const __iconNode$a = [
1561
+ const __iconNode$d = [
1304
1562
  ["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
1305
1563
  ["path", { d: "M21 3v5h-5", key: "1q7to0" }],
1306
1564
  ["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
1307
1565
  ["path", { d: "M8 16H3v5", key: "1cv678" }]
1308
1566
  ];
1309
- const RefreshCw = createLucideIcon("refresh-cw", __iconNode$a);
1567
+ const RefreshCw = createLucideIcon("refresh-cw", __iconNode$d);
1310
1568
  /**
1311
1569
  * @license lucide-react v1.8.0 - ISC
1312
1570
  *
1313
1571
  * This source code is licensed under the ISC license.
1314
1572
  * See the LICENSE file in the root directory of this source tree.
1315
1573
  */
1316
- const __iconNode$9 = [
1574
+ const __iconNode$c = [
1575
+ ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }],
1576
+ ["path", { d: "M21 9H3", key: "1338ky" }],
1577
+ ["path", { d: "M21 15H3", key: "9uk58r" }]
1578
+ ];
1579
+ const Rows3 = createLucideIcon("rows-3", __iconNode$c);
1580
+ /**
1581
+ * @license lucide-react v1.8.0 - ISC
1582
+ *
1583
+ * This source code is licensed under the ISC license.
1584
+ * See the LICENSE file in the root directory of this source tree.
1585
+ */
1586
+ const __iconNode$b = [
1317
1587
  ["path", { d: "m21 21-4.34-4.34", key: "14j7rj" }],
1318
1588
  ["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }]
1319
1589
  ];
1320
- const Search = createLucideIcon("search", __iconNode$9);
1590
+ const Search = createLucideIcon("search", __iconNode$b);
1321
1591
  /**
1322
1592
  * @license lucide-react v1.8.0 - ISC
1323
1593
  *
1324
1594
  * This source code is licensed under the ISC license.
1325
1595
  * See the LICENSE file in the root directory of this source tree.
1326
1596
  */
1327
- const __iconNode$8 = [
1597
+ const __iconNode$a = [
1598
+ [
1599
+ "path",
1600
+ {
1601
+ d: "M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915",
1602
+ key: "1i5ecw"
1603
+ }
1604
+ ],
1605
+ ["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
1606
+ ];
1607
+ const Settings = createLucideIcon("settings", __iconNode$a);
1608
+ /**
1609
+ * @license lucide-react v1.8.0 - ISC
1610
+ *
1611
+ * This source code is licensed under the ISC license.
1612
+ * See the LICENSE file in the root directory of this source tree.
1613
+ */
1614
+ const __iconNode$9 = [
1328
1615
  ["path", { d: "M16 4H9a3 3 0 0 0-2.83 4", key: "43sutm" }],
1329
1616
  ["path", { d: "M14 12a4 4 0 0 1 0 8H6", key: "nlfj13" }],
1330
1617
  ["line", { x1: "4", x2: "20", y1: "12", y2: "12", key: "1e0a9i" }]
1331
1618
  ];
1332
- const Strikethrough = createLucideIcon("strikethrough", __iconNode$8);
1619
+ const Strikethrough = createLucideIcon("strikethrough", __iconNode$9);
1333
1620
  /**
1334
1621
  * @license lucide-react v1.8.0 - ISC
1335
1622
  *
1336
1623
  * This source code is licensed under the ISC license.
1337
1624
  * See the LICENSE file in the root directory of this source tree.
1338
1625
  */
1339
- const __iconNode$7 = [
1626
+ const __iconNode$8 = [
1340
1627
  ["path", { d: "M21 5H3", key: "1fi0y6" }],
1341
1628
  ["path", { d: "M17 12H7", key: "16if0g" }],
1342
1629
  ["path", { d: "M19 19H5", key: "vjpgq2" }]
1343
1630
  ];
1344
- const TextAlignCenter = createLucideIcon("text-align-center", __iconNode$7);
1631
+ const TextAlignCenter = createLucideIcon("text-align-center", __iconNode$8);
1345
1632
  /**
1346
1633
  * @license lucide-react v1.8.0 - ISC
1347
1634
  *
1348
1635
  * This source code is licensed under the ISC license.
1349
1636
  * See the LICENSE file in the root directory of this source tree.
1350
1637
  */
1351
- const __iconNode$6 = [
1638
+ const __iconNode$7 = [
1352
1639
  ["path", { d: "M21 5H3", key: "1fi0y6" }],
1353
1640
  ["path", { d: "M21 12H9", key: "dn1m92" }],
1354
1641
  ["path", { d: "M21 19H7", key: "4cu937" }]
1355
1642
  ];
1356
- const TextAlignEnd = createLucideIcon("text-align-end", __iconNode$6);
1643
+ const TextAlignEnd = createLucideIcon("text-align-end", __iconNode$7);
1357
1644
  /**
1358
1645
  * @license lucide-react v1.8.0 - ISC
1359
1646
  *
1360
1647
  * This source code is licensed under the ISC license.
1361
1648
  * See the LICENSE file in the root directory of this source tree.
1362
1649
  */
1363
- const __iconNode$5 = [
1650
+ const __iconNode$6 = [
1364
1651
  ["path", { d: "M3 5h18", key: "1u36vt" }],
1365
1652
  ["path", { d: "M3 12h18", key: "1i2n21" }],
1366
1653
  ["path", { d: "M3 19h18", key: "awlh7x" }]
1367
1654
  ];
1368
- const TextAlignJustify = createLucideIcon("text-align-justify", __iconNode$5);
1655
+ const TextAlignJustify = createLucideIcon("text-align-justify", __iconNode$6);
1369
1656
  /**
1370
1657
  * @license lucide-react v1.8.0 - ISC
1371
1658
  *
1372
1659
  * This source code is licensed under the ISC license.
1373
1660
  * See the LICENSE file in the root directory of this source tree.
1374
1661
  */
1375
- const __iconNode$4 = [
1662
+ const __iconNode$5 = [
1376
1663
  ["path", { d: "M21 5H3", key: "1fi0y6" }],
1377
1664
  ["path", { d: "M15 12H3", key: "6jk70r" }],
1378
1665
  ["path", { d: "M17 19H3", key: "z6ezky" }]
1379
1666
  ];
1380
- const TextAlignStart = createLucideIcon("text-align-start", __iconNode$4);
1667
+ const TextAlignStart = createLucideIcon("text-align-start", __iconNode$5);
1381
1668
  /**
1382
1669
  * @license lucide-react v1.8.0 - ISC
1383
1670
  *
1384
1671
  * This source code is licensed under the ISC license.
1385
1672
  * See the LICENSE file in the root directory of this source tree.
1386
1673
  */
1387
- const __iconNode$3 = [
1674
+ const __iconNode$4 = [
1388
1675
  ["path", { d: "M10 11v6", key: "nco0om" }],
1389
1676
  ["path", { d: "M14 11v6", key: "outv1u" }],
1390
1677
  ["path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6", key: "miytrc" }],
1391
1678
  ["path", { d: "M3 6h18", key: "d0wm0j" }],
1392
1679
  ["path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2", key: "e791ji" }]
1393
1680
  ];
1394
- const Trash2 = createLucideIcon("trash-2", __iconNode$3);
1681
+ const Trash2 = createLucideIcon("trash-2", __iconNode$4);
1682
+ /**
1683
+ * @license lucide-react v1.8.0 - ISC
1684
+ *
1685
+ * This source code is licensed under the ISC license.
1686
+ * See the LICENSE file in the root directory of this source tree.
1687
+ */
1688
+ const __iconNode$3 = [
1689
+ ["path", { d: "M12 4v16", key: "1654pz" }],
1690
+ ["path", { d: "M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2", key: "e0r10z" }],
1691
+ ["path", { d: "M9 20h6", key: "s66wpe" }]
1692
+ ];
1693
+ const Type = createLucideIcon("type", __iconNode$3);
1395
1694
  /**
1396
1695
  * @license lucide-react v1.8.0 - ISC
1397
1696
  *
@@ -1414,123 +1713,17 @@ const __iconNode$1 = [
1414
1713
  ["path", { d: "M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11", key: "f3b9sd" }]
1415
1714
  ];
1416
1715
  const Undo2 = createLucideIcon("undo-2", __iconNode$1);
1417
- /**
1418
- * @license lucide-react v1.8.0 - ISC
1419
- *
1420
- * This source code is licensed under the ISC license.
1421
- * See the LICENSE file in the root directory of this source tree.
1422
- */
1423
- const __iconNode = [
1424
- ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
1425
- ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
1426
- ];
1427
- const X = createLucideIcon("x", __iconNode);
1428
- const DEFAULT_TRANSLATIONS = {
1429
- toolbar: {
1430
- undo: "Undo",
1431
- redo: "Redo",
1432
- bold: "Bold (Ctrl+B)",
1433
- italic: "Italic (Ctrl+I)",
1434
- underline: "Underline (Ctrl+U)",
1435
- strikethrough: "Strikethrough",
1436
- alignLeft: "Align Left",
1437
- alignCenter: "Align Center",
1438
- alignRight: "Align Right",
1439
- justify: "Justify",
1440
- numberedList: "Numbered List",
1441
- bulletList: "Bullet List",
1442
- indent: "Indent",
1443
- outdent: "Outdent",
1444
- openHistory: "Open History",
1445
- closeHistory: "Close History"
1446
- },
1447
- history: {
1448
- title: "History",
1449
- subtitle: "Word-style session history (last 100 actions)",
1450
- empty: "No history yet.",
1451
- clearHistory: "Clear History",
1452
- actions: {
1453
- enabledHeadersFooters: "Enabled headers and footers",
1454
- disabledHeadersFooters: "Disabled headers and footers",
1455
- copiedHeaderToAll: "Copied header to all pages",
1456
- copiedFooterToAll: "Copied footer to all pages",
1457
- clearedHeader: "Cleared header",
1458
- clearedFooter: "Cleared footer",
1459
- clearedAllHeaders: "Cleared all headers",
1460
- clearedAllFooters: "Cleared all footers",
1461
- pageCounterSet: "Page counter set to {{value}}",
1462
- boldApplied: "Bold applied",
1463
- italicApplied: "Italic applied",
1464
- underlineApplied: "Underline applied",
1465
- strikethroughApplied: "Strikethrough applied",
1466
- alignedLeft: "Aligned left",
1467
- alignedCenter: "Aligned center",
1468
- alignedRight: "Aligned right",
1469
- justifiedText: "Justified text",
1470
- insertedNumberedList: "Inserted numbered list",
1471
- insertedBulletList: "Inserted bullet list",
1472
- indentedContent: "Indented content",
1473
- outdentedContent: "Outdented content",
1474
- fontChanged: "Font changed to {{value}}",
1475
- fontSizeChanged: "Font size changed to {{value}}pt"
1476
- }
1477
- },
1478
- variables: {
1479
- title: "Variables",
1480
- available: "{{count}} available",
1481
- refreshVariables: "Refresh variables",
1482
- searchPlaceholder: "Search variables...",
1483
- noVariablesFound: "No variables found",
1484
- insertVariable: "Insert variable {{key}}",
1485
- openPanel: "Open Variables",
1486
- closePanel: "Close Variables"
1487
- },
1488
- header: {
1489
- placeholder: "Header"
1490
- },
1491
- footer: {
1492
- placeholder: "Footer"
1493
- },
1494
- sidebar: {
1495
- close: "Close sidebar"
1496
- }
1497
- };
1498
- function deepMerge(target, source) {
1499
- if (typeof target !== "object" || target === null) {
1500
- return source ?? target;
1501
- }
1502
- const result = { ...target };
1503
- for (const key of Object.keys(source)) {
1504
- const sourceVal = source[key];
1505
- const targetVal = target[key];
1506
- if (sourceVal !== void 0 && typeof sourceVal === "object" && sourceVal !== null && !Array.isArray(sourceVal) && typeof targetVal === "object" && targetVal !== null) {
1507
- result[key] = deepMerge(targetVal, sourceVal);
1508
- } else if (sourceVal !== void 0) {
1509
- result[key] = sourceVal;
1510
- }
1511
- }
1512
- return result;
1513
- }
1514
- const TranslationsContext = createContext(DEFAULT_TRANSLATIONS);
1515
- function useTranslations() {
1516
- return useContext(TranslationsContext);
1517
- }
1518
- function interpolate(template, params) {
1519
- return template.replace(
1520
- /\{\{(\w+)\}\}/g,
1521
- (_, key) => String(params[key] ?? `{{${key}}}`)
1522
- );
1523
- }
1524
- const TranslationsProvider = ({
1525
- translations,
1526
- children
1527
- }) => {
1528
- const merged = useMemo(
1529
- () => translations ? deepMerge(DEFAULT_TRANSLATIONS, translations) : DEFAULT_TRANSLATIONS,
1530
- [translations]
1531
- );
1532
- return /* @__PURE__ */ jsx(TranslationsContext.Provider, { value: merged, children });
1533
- };
1716
+ /**
1717
+ * @license lucide-react v1.8.0 - ISC
1718
+ *
1719
+ * This source code is licensed under the ISC license.
1720
+ * See the LICENSE file in the root directory of this source tree.
1721
+ */
1722
+ const __iconNode = [
1723
+ ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
1724
+ ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
1725
+ ];
1726
+ const X = createLucideIcon("x", __iconNode);
1534
1727
  const EditorSidebar = ({
1535
1728
  title,
1536
1729
  subtitle,
@@ -1637,7 +1830,7 @@ const HistorySidebar = () => {
1637
1830
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
1638
1831
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
1639
1832
  /* @__PURE__ */ jsx("div", { className: `text-xs ${isCurrent ? "font-semibold text-blue-700" : "text-gray-900"}`, children: entry.label }),
1640
- /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-gray-400", children: entry.source })
1833
+ /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-gray-400", children: t.regions[entry.source] ?? entry.source })
1641
1834
  ] }),
1642
1835
  /* @__PURE__ */ jsx("div", { className: "shrink-0 text-xs text-gray-400", children: formatTimestamp(entry.timestamp) })
1643
1836
  ] })
@@ -1651,13 +1844,15 @@ const HeaderFooterToggle = ({
1651
1844
  enabled,
1652
1845
  onToggle
1653
1846
  }) => {
1847
+ const t = useTranslations();
1654
1848
  return /* @__PURE__ */ jsxs(
1655
1849
  "label",
1656
1850
  {
1657
1851
  className: "flex items-center gap-1.5 cursor-pointer select-none",
1658
1852
  "data-testid": "header-footer-toggle",
1659
1853
  children: [
1660
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-600", children: "Headers & Footers" }),
1854
+ /* @__PURE__ */ jsx(Rows3, { size: 14, className: "text-gray-500" }),
1855
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-600", children: t.headerFooter.label }),
1661
1856
  /* @__PURE__ */ jsx(
1662
1857
  "button",
1663
1858
  {
@@ -1688,6 +1883,12 @@ const HeaderFooterToggle = ({
1688
1883
  }
1689
1884
  );
1690
1885
  };
1886
+ const PAGE_COUNTER_OPTIONS = [
1887
+ { value: "none", label: "None" },
1888
+ { value: "header", label: "Header" },
1889
+ { value: "footer", label: "Footer" },
1890
+ { value: "both", label: "Both" }
1891
+ ];
1691
1892
  const HeaderFooterActions = ({
1692
1893
  activePageId,
1693
1894
  pageCounterMode,
@@ -1699,119 +1900,154 @@ const HeaderFooterActions = ({
1699
1900
  onClearAllHeaders,
1700
1901
  onClearAllFooters
1701
1902
  }) => {
1903
+ const [open, setOpen] = useState(false);
1904
+ const containerRef = useRef(null);
1702
1905
  const hasActivePage = activePageId !== null;
1703
- const handlePageCounterModeChange = (event) => {
1704
- const nextMode = event.target.value;
1705
- if (nextMode === "none" || nextMode === "header" || nextMode === "footer" || nextMode === "both") {
1706
- onPageCounterModeChange(nextMode);
1707
- }
1906
+ const close = useCallback(() => setOpen(false), []);
1907
+ useEffect(() => {
1908
+ if (!open) return;
1909
+ const handleClickOutside = (e) => {
1910
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
1911
+ close();
1912
+ }
1913
+ };
1914
+ document.addEventListener("mousedown", handleClickOutside);
1915
+ return () => document.removeEventListener("mousedown", handleClickOutside);
1916
+ }, [open, close]);
1917
+ const handleAction = (action) => {
1918
+ action();
1919
+ close();
1708
1920
  };
1709
- return /* @__PURE__ */ jsxs(
1710
- "div",
1711
- {
1712
- className: "flex flex-wrap items-center gap-2 text-xs",
1713
- "data-testid": "header-footer-actions",
1714
- children: [
1715
- /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1 text-gray-500", htmlFor: "page-counter-mode", children: [
1716
- /* @__PURE__ */ jsx(Hash, { size: 13, className: "text-gray-400" }),
1717
- /* @__PURE__ */ jsxs(
1718
- "select",
1921
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative", "data-testid": "header-footer-actions", children: [
1922
+ /* @__PURE__ */ jsxs(
1923
+ "button",
1924
+ {
1925
+ type: "button",
1926
+ onMouseDown: (e) => e.preventDefault(),
1927
+ onClick: () => setOpen(!open),
1928
+ className: `
1929
+ flex h-7 items-center gap-1 rounded px-1.5 text-xs transition-colors
1930
+ ${open ? "bg-blue-50 text-blue-600" : "text-gray-600 hover:bg-gray-100 hover:text-gray-900"}
1931
+ `,
1932
+ "data-testid": "header-footer-menu-trigger",
1933
+ "aria-expanded": open,
1934
+ "aria-haspopup": "true",
1935
+ children: [
1936
+ /* @__PURE__ */ jsx(Settings, { size: 14 }),
1937
+ /* @__PURE__ */ jsx(ChevronDown, { size: 12 })
1938
+ ]
1939
+ }
1940
+ ),
1941
+ open && /* @__PURE__ */ jsxs(
1942
+ "div",
1943
+ {
1944
+ className: "absolute left-0 top-full mt-1 z-50 w-56 rounded-lg border border-gray-200 bg-white py-1 shadow-lg",
1945
+ role: "menu",
1946
+ "data-testid": "header-footer-menu",
1947
+ children: [
1948
+ /* @__PURE__ */ jsx(SectionLabel, { children: "Page counter" }),
1949
+ /* @__PURE__ */ jsx("div", { className: "px-3 pb-2 grid grid-cols-2 gap-1", "data-testid": "page-counter-mode", children: PAGE_COUNTER_OPTIONS.map(({ value, label }) => /* @__PURE__ */ jsx(
1950
+ "button",
1719
1951
  {
1720
- id: "page-counter-mode",
1721
- className: "h-6 rounded border border-gray-200 bg-white px-1.5 text-xs text-gray-600\n focus:border-blue-400 focus:outline-none focus:ring-1 focus:ring-blue-400",
1722
- "data-testid": "page-counter-mode",
1723
- value: pageCounterMode,
1724
- onChange: handlePageCounterModeChange,
1725
- children: [
1726
- /* @__PURE__ */ jsx("option", { value: "none", children: "None" }),
1727
- /* @__PURE__ */ jsx("option", { value: "header", children: "Header" }),
1728
- /* @__PURE__ */ jsx("option", { value: "footer", children: "Footer" }),
1729
- /* @__PURE__ */ jsx("option", { value: "both", children: "Both" })
1730
- ]
1952
+ type: "button",
1953
+ role: "menuitemradio",
1954
+ "aria-checked": pageCounterMode === value,
1955
+ onMouseDown: (e) => e.preventDefault(),
1956
+ onClick: () => onPageCounterModeChange(value),
1957
+ className: `
1958
+ rounded px-2 py-1 text-xs text-left transition-colors
1959
+ ${pageCounterMode === value ? "bg-blue-50 text-blue-700 font-medium" : "text-gray-600 hover:bg-gray-100"}
1960
+ `,
1961
+ "data-testid": `page-counter-${value}`,
1962
+ children: label
1963
+ },
1964
+ value
1965
+ )) }),
1966
+ /* @__PURE__ */ jsx(MenuDivider, {}),
1967
+ /* @__PURE__ */ jsx(SectionLabel, { children: "Header" }),
1968
+ /* @__PURE__ */ jsx(
1969
+ MenuItem,
1970
+ {
1971
+ icon: /* @__PURE__ */ jsx(CopyPlus, { size: 14 }),
1972
+ label: "Copy to all pages",
1973
+ onClick: () => handleAction(onCopyHeaderToAll),
1974
+ disabled: !hasActivePage,
1975
+ testId: "copy-header-all"
1976
+ }
1977
+ ),
1978
+ /* @__PURE__ */ jsx(
1979
+ MenuItem,
1980
+ {
1981
+ icon: /* @__PURE__ */ jsx(Eraser, { size: 14 }),
1982
+ label: "Clear this page",
1983
+ onClick: () => handleAction(onClearHeader),
1984
+ disabled: !hasActivePage,
1985
+ testId: "clear-header"
1986
+ }
1987
+ ),
1988
+ /* @__PURE__ */ jsx(
1989
+ MenuItem,
1990
+ {
1991
+ icon: /* @__PURE__ */ jsx(Trash2, { size: 14 }),
1992
+ label: "Clear all",
1993
+ onClick: () => handleAction(onClearAllHeaders),
1994
+ testId: "clear-all-headers"
1995
+ }
1996
+ ),
1997
+ /* @__PURE__ */ jsx(MenuDivider, {}),
1998
+ /* @__PURE__ */ jsx(SectionLabel, { children: "Footer" }),
1999
+ /* @__PURE__ */ jsx(
2000
+ MenuItem,
2001
+ {
2002
+ icon: /* @__PURE__ */ jsx(CopyPlus, { size: 14 }),
2003
+ label: "Copy to all pages",
2004
+ onClick: () => handleAction(onCopyFooterToAll),
2005
+ disabled: !hasActivePage,
2006
+ testId: "copy-footer-all"
2007
+ }
2008
+ ),
2009
+ /* @__PURE__ */ jsx(
2010
+ MenuItem,
2011
+ {
2012
+ icon: /* @__PURE__ */ jsx(Eraser, { size: 14 }),
2013
+ label: "Clear this page",
2014
+ onClick: () => handleAction(onClearFooter),
2015
+ disabled: !hasActivePage,
2016
+ testId: "clear-footer"
2017
+ }
2018
+ ),
2019
+ /* @__PURE__ */ jsx(
2020
+ MenuItem,
2021
+ {
2022
+ icon: /* @__PURE__ */ jsx(Trash2, { size: 14 }),
2023
+ label: "Clear all",
2024
+ onClick: () => handleAction(onClearAllFooters),
2025
+ testId: "clear-all-footers"
1731
2026
  }
1732
2027
  )
1733
- ] }),
1734
- /* @__PURE__ */ jsx(ActionDivider, {}),
1735
- /* @__PURE__ */ jsx("span", { className: "text-gray-400 font-medium", children: "Header" }),
1736
- /* @__PURE__ */ jsx(
1737
- ActionIconButton,
1738
- {
1739
- title: "Copy header to all pages",
1740
- icon: /* @__PURE__ */ jsx(CopyPlus, { size: 13 }),
1741
- onClick: onCopyHeaderToAll,
1742
- disabled: !hasActivePage,
1743
- testId: "copy-header-all"
1744
- }
1745
- ),
1746
- /* @__PURE__ */ jsx(
1747
- ActionIconButton,
1748
- {
1749
- title: "Clear header on this page",
1750
- icon: /* @__PURE__ */ jsx(Eraser, { size: 13 }),
1751
- onClick: onClearHeader,
1752
- disabled: !hasActivePage,
1753
- testId: "clear-header"
1754
- }
1755
- ),
1756
- /* @__PURE__ */ jsx(
1757
- ActionIconButton,
1758
- {
1759
- title: "Clear all headers",
1760
- icon: /* @__PURE__ */ jsx(Trash2, { size: 13 }),
1761
- onClick: onClearAllHeaders,
1762
- testId: "clear-all-headers"
1763
- }
1764
- ),
1765
- /* @__PURE__ */ jsx(ActionDivider, {}),
1766
- /* @__PURE__ */ jsx("span", { className: "text-gray-400 font-medium", children: "Footer" }),
1767
- /* @__PURE__ */ jsx(
1768
- ActionIconButton,
1769
- {
1770
- title: "Copy footer to all pages",
1771
- icon: /* @__PURE__ */ jsx(CopyPlus, { size: 13 }),
1772
- onClick: onCopyFooterToAll,
1773
- disabled: !hasActivePage,
1774
- testId: "copy-footer-all"
1775
- }
1776
- ),
1777
- /* @__PURE__ */ jsx(
1778
- ActionIconButton,
1779
- {
1780
- title: "Clear footer on this page",
1781
- icon: /* @__PURE__ */ jsx(Eraser, { size: 13 }),
1782
- onClick: onClearFooter,
1783
- disabled: !hasActivePage,
1784
- testId: "clear-footer"
1785
- }
1786
- ),
1787
- /* @__PURE__ */ jsx(
1788
- ActionIconButton,
1789
- {
1790
- title: "Clear all footers",
1791
- icon: /* @__PURE__ */ jsx(Trash2, { size: 13 }),
1792
- onClick: onClearAllFooters,
1793
- testId: "clear-all-footers"
1794
- }
1795
- )
1796
- ]
1797
- }
1798
- );
2028
+ ]
2029
+ }
2030
+ )
2031
+ ] });
1799
2032
  };
1800
- const ActionIconButton = ({ title, icon, onClick, disabled, testId }) => /* @__PURE__ */ jsx(
2033
+ const SectionLabel = ({ children }) => /* @__PURE__ */ jsx("div", { className: "px-3 pt-2 pb-1 text-[10px] font-semibold uppercase tracking-wider text-gray-400", children });
2034
+ const MenuItem = ({ icon, label, onClick, disabled, testId }) => /* @__PURE__ */ jsxs(
1801
2035
  "button",
1802
2036
  {
1803
2037
  type: "button",
1804
- title,
1805
- "aria-label": title,
2038
+ role: "menuitem",
1806
2039
  onMouseDown: (e) => e.preventDefault(),
1807
2040
  onClick,
1808
2041
  disabled,
1809
- className: "flex h-6 w-6 items-center justify-center rounded text-gray-500 transition-colors\n hover:bg-gray-200/60 hover:text-gray-700\n disabled:cursor-not-allowed disabled:text-gray-300",
2042
+ className: "flex w-full items-center gap-2 px-3 py-1.5 text-xs text-gray-700 transition-colors\n hover:bg-gray-50 disabled:cursor-not-allowed disabled:text-gray-300",
1810
2043
  "data-testid": testId,
1811
- children: icon
2044
+ children: [
2045
+ icon,
2046
+ label
2047
+ ]
1812
2048
  }
1813
2049
  );
1814
- const ActionDivider = () => /* @__PURE__ */ jsx("div", { className: "mx-0.5 h-4 w-px bg-gray-300/60" });
2050
+ const MenuDivider = () => /* @__PURE__ */ jsx("div", { className: "my-1 h-px bg-gray-100" });
1815
2051
  function resolveExtensions(extensions) {
1816
2052
  const resolved = {
1817
2053
  nodes: [],
@@ -1886,10 +2122,10 @@ function useExtensionContext(getDocument, getActiveEditor) {
1886
2122
  }), [getDocument, getActiveEditor, stateRef]);
1887
2123
  }
1888
2124
  const SUPPORTED_FONTS = [
2125
+ "Calibri",
1889
2126
  "Inter",
1890
2127
  "Times New Roman",
1891
2128
  "Arial",
1892
- "Calibri",
1893
2129
  "Georgia",
1894
2130
  "Courier New"
1895
2131
  ];
@@ -2153,87 +2389,107 @@ const Toolbar = () => {
2153
2389
  },
2154
2390
  [applyToBodyEditors, runToolbarAction, t.history.actions.fontSizeChanged]
2155
2391
  );
2156
- return /* @__PURE__ */ jsx(
2392
+ return /* @__PURE__ */ jsxs(
2157
2393
  "div",
2158
2394
  {
2159
2395
  className: "lex4-toolbar sticky top-0 z-10 bg-white border-b border-gray-200",
2160
2396
  "data-testid": "toolbar",
2161
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 px-2 py-1.5", children: [
2162
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": "history-controls", children: [
2163
- /* @__PURE__ */ jsx(
2397
+ children: [
2398
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 px-2 py-1.5", children: [
2399
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": "history-controls", children: [
2400
+ /* @__PURE__ */ jsx(
2401
+ ToolbarIconButton,
2402
+ {
2403
+ title: t.toolbar.undo,
2404
+ testId: "btn-undo",
2405
+ disabled: !canUndo,
2406
+ onClick: undo,
2407
+ children: /* @__PURE__ */ jsx(Undo2, { size: 15 })
2408
+ }
2409
+ ),
2410
+ /* @__PURE__ */ jsx(
2411
+ ToolbarIconButton,
2412
+ {
2413
+ title: t.toolbar.redo,
2414
+ testId: "btn-redo",
2415
+ disabled: !canRedo,
2416
+ onClick: redo,
2417
+ children: /* @__PURE__ */ jsx(Redo2, { size: 15 })
2418
+ }
2419
+ )
2420
+ ] }),
2421
+ /* @__PURE__ */ jsx(Divider, {}),
2422
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2423
+ /* @__PURE__ */ jsx(Type, { size: 14, className: "text-gray-500" }),
2424
+ /* @__PURE__ */ jsx(
2425
+ "select",
2426
+ {
2427
+ className: "h-7 rounded border border-gray-200 bg-white px-2 text-xs text-gray-700\n focus:border-blue-400 focus:outline-none focus:ring-1 focus:ring-blue-400",
2428
+ "data-testid": "font-selector",
2429
+ defaultValue: "Calibri",
2430
+ onChange: handleFontChange,
2431
+ children: SUPPORTED_FONTS.map((font) => /* @__PURE__ */ jsx("option", { value: font, style: { fontFamily: font }, children: font }, font))
2432
+ }
2433
+ )
2434
+ ] }),
2435
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2436
+ /* @__PURE__ */ jsx(ALargeSmall, { size: 14, className: "text-gray-500" }),
2437
+ /* @__PURE__ */ jsx(
2438
+ "select",
2439
+ {
2440
+ className: "h-7 w-16 rounded border border-gray-200 bg-white px-1 text-xs text-gray-700\n focus:border-blue-400 focus:outline-none focus:ring-1 focus:ring-blue-400",
2441
+ "data-testid": "font-size-selector",
2442
+ defaultValue: "12",
2443
+ onChange: handleFontSizeChange,
2444
+ children: SUPPORTED_FONT_SIZES.map((size) => /* @__PURE__ */ jsx("option", { value: size, children: size }, size))
2445
+ }
2446
+ )
2447
+ ] }),
2448
+ /* @__PURE__ */ jsx(Divider, {}),
2449
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": "format-group", children: [
2450
+ /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.bold, testId: "btn-bold", onClick: handleBold, children: /* @__PURE__ */ jsx(Bold, { size: 15 }) }),
2451
+ /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.italic, testId: "btn-italic", onClick: handleItalic, children: /* @__PURE__ */ jsx(Italic, { size: 15 }) }),
2452
+ /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.underline, testId: "btn-underline", onClick: handleUnderline, children: /* @__PURE__ */ jsx(Underline, { size: 15 }) }),
2453
+ /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.strikethrough, testId: "btn-strike", onClick: handleStrikethrough, children: /* @__PURE__ */ jsx(Strikethrough, { size: 15 }) })
2454
+ ] }),
2455
+ /* @__PURE__ */ jsx(Divider, {}),
2456
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": "align-group", children: [
2457
+ /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.alignLeft, testId: "btn-align-left", onClick: handleAlignLeft, children: /* @__PURE__ */ jsx(TextAlignStart, { size: 15 }) }),
2458
+ /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.alignCenter, testId: "btn-align-center", onClick: handleAlignCenter, children: /* @__PURE__ */ jsx(TextAlignCenter, { size: 15 }) }),
2459
+ /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.alignRight, testId: "btn-align-right", onClick: handleAlignRight, children: /* @__PURE__ */ jsx(TextAlignEnd, { size: 15 }) }),
2460
+ /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.justify, testId: "btn-align-justify", onClick: handleAlignJustify, children: /* @__PURE__ */ jsx(TextAlignJustify, { size: 15 }) })
2461
+ ] }),
2462
+ /* @__PURE__ */ jsx(Divider, {}),
2463
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": "list-group", children: [
2464
+ /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.numberedList, testId: "btn-list-number", onClick: handleListNumber, children: /* @__PURE__ */ jsx(ListOrdered, { size: 15 }) }),
2465
+ /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.bulletList, testId: "btn-list-bullet", onClick: handleListBullet, children: /* @__PURE__ */ jsx(List, { size: 15 }) }),
2466
+ /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.indent, testId: "btn-indent", onClick: handleIndent, children: /* @__PURE__ */ jsx(ListIndentIncrease, { size: 15 }) }),
2467
+ /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.outdent, testId: "btn-outdent", onClick: handleOutdent, children: /* @__PURE__ */ jsx(ListIndentDecrease, { size: 15 }) })
2468
+ ] }),
2469
+ toolbarItems.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
2470
+ /* @__PURE__ */ jsx(Divider, {}),
2471
+ toolbarItems.map((ToolbarItem, idx) => /* @__PURE__ */ jsx(ToolbarItem, {}, idx))
2472
+ ] }),
2473
+ /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center", children: /* @__PURE__ */ jsx(
2164
2474
  ToolbarIconButton,
2165
2475
  {
2166
- title: t.toolbar.undo,
2167
- testId: "btn-undo",
2168
- disabled: !canUndo,
2169
- onClick: undo,
2170
- children: /* @__PURE__ */ jsx(Undo2, { size: 15 })
2476
+ title: historySidebarOpen ? t.toolbar.closeHistory : t.toolbar.openHistory,
2477
+ testId: "toggle-history-sidebar",
2478
+ active: historySidebarOpen,
2479
+ onClick: () => setHistorySidebarOpen(!historySidebarOpen),
2480
+ children: /* @__PURE__ */ jsx(PanelRight, { size: 15 })
2171
2481
  }
2172
- ),
2482
+ ) })
2483
+ ] }),
2484
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 px-2 py-1 border-t border-gray-100", children: [
2173
2485
  /* @__PURE__ */ jsx(
2174
- ToolbarIconButton,
2486
+ HeaderFooterToggle,
2175
2487
  {
2176
- title: t.toolbar.redo,
2177
- testId: "btn-redo",
2178
- disabled: !canRedo,
2179
- onClick: redo,
2180
- children: /* @__PURE__ */ jsx(Redo2, { size: 15 })
2488
+ enabled: document2.headerFooterEnabled,
2489
+ onToggle: handleToggle
2181
2490
  }
2182
- )
2183
- ] }),
2184
- /* @__PURE__ */ jsx(Divider, {}),
2185
- /* @__PURE__ */ jsx(
2186
- "select",
2187
- {
2188
- className: "h-7 rounded border border-gray-200 bg-white px-2 text-xs text-gray-700\n focus:border-blue-400 focus:outline-none focus:ring-1 focus:ring-blue-400",
2189
- "data-testid": "font-selector",
2190
- defaultValue: "Inter",
2191
- onChange: handleFontChange,
2192
- children: SUPPORTED_FONTS.map((font) => /* @__PURE__ */ jsx("option", { value: font, style: { fontFamily: font }, children: font }, font))
2193
- }
2194
- ),
2195
- /* @__PURE__ */ jsx(
2196
- "select",
2197
- {
2198
- className: "h-7 w-16 rounded border border-gray-200 bg-white px-1 text-xs text-gray-700\n focus:border-blue-400 focus:outline-none focus:ring-1 focus:ring-blue-400",
2199
- "data-testid": "font-size-selector",
2200
- defaultValue: "12",
2201
- onChange: handleFontSizeChange,
2202
- children: SUPPORTED_FONT_SIZES.map((size) => /* @__PURE__ */ jsx("option", { value: size, children: size }, size))
2203
- }
2204
- ),
2205
- /* @__PURE__ */ jsx(Divider, {}),
2206
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": "format-group", children: [
2207
- /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.bold, testId: "btn-bold", onClick: handleBold, children: /* @__PURE__ */ jsx(Bold, { size: 15 }) }),
2208
- /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.italic, testId: "btn-italic", onClick: handleItalic, children: /* @__PURE__ */ jsx(Italic, { size: 15 }) }),
2209
- /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.underline, testId: "btn-underline", onClick: handleUnderline, children: /* @__PURE__ */ jsx(Underline, { size: 15 }) }),
2210
- /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.strikethrough, testId: "btn-strike", onClick: handleStrikethrough, children: /* @__PURE__ */ jsx(Strikethrough, { size: 15 }) })
2211
- ] }),
2212
- /* @__PURE__ */ jsx(Divider, {}),
2213
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": "align-group", children: [
2214
- /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.alignLeft, testId: "btn-align-left", onClick: handleAlignLeft, children: /* @__PURE__ */ jsx(TextAlignStart, { size: 15 }) }),
2215
- /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.alignCenter, testId: "btn-align-center", onClick: handleAlignCenter, children: /* @__PURE__ */ jsx(TextAlignCenter, { size: 15 }) }),
2216
- /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.alignRight, testId: "btn-align-right", onClick: handleAlignRight, children: /* @__PURE__ */ jsx(TextAlignEnd, { size: 15 }) }),
2217
- /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.justify, testId: "btn-align-justify", onClick: handleAlignJustify, children: /* @__PURE__ */ jsx(TextAlignJustify, { size: 15 }) })
2218
- ] }),
2219
- /* @__PURE__ */ jsx(Divider, {}),
2220
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": "list-group", children: [
2221
- /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.numberedList, testId: "btn-list-number", onClick: handleListNumber, children: /* @__PURE__ */ jsx(ListOrdered, { size: 15 }) }),
2222
- /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.bulletList, testId: "btn-list-bullet", onClick: handleListBullet, children: /* @__PURE__ */ jsx(List, { size: 15 }) }),
2223
- /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.indent, testId: "btn-indent", onClick: handleIndent, children: /* @__PURE__ */ jsx(ListIndentIncrease, { size: 15 }) }),
2224
- /* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.outdent, testId: "btn-outdent", onClick: handleOutdent, children: /* @__PURE__ */ jsx(ListIndentDecrease, { size: 15 }) })
2225
- ] }),
2226
- /* @__PURE__ */ jsx(Divider, {}),
2227
- /* @__PURE__ */ jsx(
2228
- HeaderFooterToggle,
2229
- {
2230
- enabled: document2.headerFooterEnabled,
2231
- onToggle: handleToggle
2232
- }
2233
- ),
2234
- document2.headerFooterEnabled && /* @__PURE__ */ jsxs(Fragment, { children: [
2235
- /* @__PURE__ */ jsx(Divider, {}),
2236
- /* @__PURE__ */ jsx(
2491
+ ),
2492
+ document2.headerFooterEnabled && /* @__PURE__ */ jsx(
2237
2493
  HeaderFooterActions,
2238
2494
  {
2239
2495
  activePageId,
@@ -2247,22 +2503,8 @@ const Toolbar = () => {
2247
2503
  onClearAllFooters: handleClearAllFooters
2248
2504
  }
2249
2505
  )
2250
- ] }),
2251
- toolbarItems.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
2252
- /* @__PURE__ */ jsx(Divider, {}),
2253
- toolbarItems.map((ToolbarItem, idx) => /* @__PURE__ */ jsx(ToolbarItem, {}, idx))
2254
- ] }),
2255
- /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center", children: /* @__PURE__ */ jsx(
2256
- ToolbarIconButton,
2257
- {
2258
- title: historySidebarOpen ? t.toolbar.closeHistory : t.toolbar.openHistory,
2259
- testId: "toggle-history-sidebar",
2260
- active: historySidebarOpen,
2261
- onClick: () => setHistorySidebarOpen(!historySidebarOpen),
2262
- children: /* @__PURE__ */ jsx(PanelRight, { size: 15 })
2263
- }
2264
- ) })
2265
- ] })
2506
+ ] })
2507
+ ]
2266
2508
  }
2267
2509
  );
2268
2510
  };
@@ -2333,6 +2575,19 @@ function mergeEditorStates(stateA, stateB) {
2333
2575
  if (all.length === 0) return null;
2334
2576
  return createEditorStateFromNodes(all);
2335
2577
  }
2578
+ function splitBlockNode(block, childOffset) {
2579
+ const children = block.children ?? [];
2580
+ if (childOffset <= 0) return [null, structuredClone(block)];
2581
+ if (childOffset >= children.length) return [structuredClone(block), null];
2582
+ const before = children.slice(0, childOffset);
2583
+ const after = children.slice(childOffset);
2584
+ const makeCopy = (kids) => {
2585
+ const copy = structuredClone(block);
2586
+ copy.children = kids;
2587
+ return copy;
2588
+ };
2589
+ return [makeCopy(before), makeCopy(after)];
2590
+ }
2336
2591
  function usePagination(document2, dispatch) {
2337
2592
  const reflowingRef = useRef(false);
2338
2593
  const pendingReflowRef = useRef(false);
@@ -2367,7 +2622,61 @@ function usePagination(document2, dispatch) {
2367
2622
  const footerH = document2.headerFooterEnabled ? page.footerHeight : 0;
2368
2623
  const bodyHeight = computeBodyHeight(headerH, footerH);
2369
2624
  const nodes = getTopLevelNodes(page.bodyState);
2370
- if (nodes.length <= 1) return;
2625
+ if (nodes.length <= 1) {
2626
+ if (nodes.length === 1) {
2627
+ const singleNode = nodes[0];
2628
+ const children = singleNode.children;
2629
+ if (children && children.length > 1) {
2630
+ const estimatedLineHeight = 24;
2631
+ const maxChildren = Math.max(1, Math.floor(bodyHeight / estimatedLineHeight));
2632
+ if (maxChildren < children.length) {
2633
+ const [keepBlock, overflowBlock] = splitBlockNode(singleNode, maxChildren);
2634
+ if (keepBlock && overflowBlock) {
2635
+ const keepState2 = createEditorStateFromNodes([keepBlock]);
2636
+ const overflowState2 = createEditorStateFromNodes([overflowBlock]);
2637
+ const nextPage2 = pages[pageIndex + 1];
2638
+ if (nextPage2) {
2639
+ const mergedBody = mergeEditorStates(overflowState2, nextPage2.bodyState);
2640
+ dispatch({
2641
+ type: "SET_DOCUMENT",
2642
+ document: {
2643
+ ...document2,
2644
+ pages: pages.map((p, i) => {
2645
+ if (i === pageIndex) return { ...p, bodyState: keepState2 };
2646
+ if (i === pageIndex + 1) return { ...p, bodyState: mergedBody };
2647
+ return p;
2648
+ })
2649
+ }
2650
+ });
2651
+ } else {
2652
+ const newPage = {
2653
+ ...createPageFromTemplate({
2654
+ headerState: document2.defaultHeaderState,
2655
+ footerState: document2.defaultFooterState,
2656
+ headerHeight: document2.defaultHeaderHeight,
2657
+ footerHeight: document2.defaultFooterHeight
2658
+ }),
2659
+ bodyState: overflowState2
2660
+ };
2661
+ dispatch({
2662
+ type: "SET_DOCUMENT",
2663
+ document: {
2664
+ ...document2,
2665
+ pages: [
2666
+ ...pages.map(
2667
+ (p, i) => i === pageIndex ? { ...p, bodyState: keepState2 } : p
2668
+ ),
2669
+ newPage
2670
+ ]
2671
+ }
2672
+ });
2673
+ }
2674
+ }
2675
+ }
2676
+ }
2677
+ }
2678
+ return;
2679
+ }
2371
2680
  const fitCount = estimateNodesFitting(page.bodyState, bodyHeight);
2372
2681
  if (fitCount >= nodes.length) return;
2373
2682
  const [keepState, overflowState] = splitEditorState(page.bodyState, fitCount);
@@ -2531,7 +2840,7 @@ function usePagination(document2, dispatch) {
2531
2840
  }
2532
2841
  const lexicalTheme = {
2533
2842
  root: "lex4-root outline-none",
2534
- paragraph: "lex4-paragraph mb-1",
2843
+ paragraph: "lex4-paragraph text-justify",
2535
2844
  heading: {
2536
2845
  h1: "text-3xl font-bold mb-2",
2537
2846
  h2: "text-2xl font-bold mb-2",
@@ -2737,6 +3046,213 @@ const ActiveEditorPlugin = ({
2737
3046
  }, [editor, onFocus, pageId, region, setActiveEditor, setActivePageId]);
2738
3047
  return null;
2739
3048
  };
3049
+ function findMidBlockSplitPoint(blockElement, availableHeight) {
3050
+ const blockRect = blockElement.getBoundingClientRect();
3051
+ const maxBottom = blockRect.top + availableHeight;
3052
+ const textNodes = [];
3053
+ const walker = document.createTreeWalker(blockElement, NodeFilter.SHOW_TEXT);
3054
+ let walkNode;
3055
+ while (walkNode = walker.nextNode()) {
3056
+ if (walkNode.length > 0) {
3057
+ textNodes.push(walkNode);
3058
+ }
3059
+ }
3060
+ if (textNodes.length === 0) return null;
3061
+ const range = document.createRange();
3062
+ for (const textNode of textNodes) {
3063
+ range.selectNodeContents(textNode);
3064
+ const nodeRect = range.getBoundingClientRect();
3065
+ if (nodeRect.bottom <= maxBottom) {
3066
+ continue;
3067
+ }
3068
+ if (nodeRect.top >= maxBottom) {
3069
+ return backupToWordBoundary(textNode, 0);
3070
+ }
3071
+ let low = 0;
3072
+ let high = textNode.length;
3073
+ while (low < high) {
3074
+ const mid = Math.floor((low + high) / 2);
3075
+ range.setStart(textNode, 0);
3076
+ range.setEnd(textNode, mid + 1);
3077
+ const testRect = range.getBoundingClientRect();
3078
+ if (testRect.bottom > maxBottom) {
3079
+ high = mid;
3080
+ } else {
3081
+ low = mid + 1;
3082
+ }
3083
+ }
3084
+ return backupToWordBoundary(textNode, low);
3085
+ }
3086
+ return null;
3087
+ }
3088
+ function backupToWordBoundary(textNode, offset) {
3089
+ const text = textNode.textContent || "";
3090
+ if (offset <= 0 || offset >= text.length) {
3091
+ if (offset > 0 && offset < text.length) {
3092
+ return { textNode, offset };
3093
+ }
3094
+ if (offset > 0) return { textNode, offset };
3095
+ return null;
3096
+ }
3097
+ let adjusted = offset;
3098
+ while (adjusted > 0 && !/\s/.test(text[adjusted - 1])) {
3099
+ adjusted--;
3100
+ }
3101
+ if (adjusted <= 0) {
3102
+ adjusted = offset;
3103
+ }
3104
+ return { textNode, offset: adjusted };
3105
+ }
3106
+ function serializeNodeTree$1(node) {
3107
+ const json = node.exportJSON();
3108
+ if ($isElementNode(node)) {
3109
+ json.children = node.getChildren().map(serializeNodeTree$1);
3110
+ }
3111
+ return json;
3112
+ }
3113
+ function findListSplitIndex(listElement, availableHeight) {
3114
+ const listRect = listElement.getBoundingClientRect();
3115
+ const maxBottom = listRect.top + availableHeight;
3116
+ const children = Array.from(listElement.children);
3117
+ for (let i = 0; i < children.length; i++) {
3118
+ const child = children[i];
3119
+ const childRect = child.getBoundingClientRect();
3120
+ if (childRect.bottom > maxBottom && i > 0) {
3121
+ return i;
3122
+ }
3123
+ }
3124
+ return -1;
3125
+ }
3126
+ function performMidBlockSplit(rootElement, availableHeight, overflowBlockIndex) {
3127
+ const root = $getRoot();
3128
+ const allChildren = root.getChildren();
3129
+ const blockNode = allChildren[overflowBlockIndex];
3130
+ if (!blockNode || !$isElementNode(blockNode)) {
3131
+ debugWarn("overflow", "target node is not an ElementNode");
3132
+ return null;
3133
+ }
3134
+ const blockElements = Array.from(rootElement.children);
3135
+ const blockElement = blockElements[overflowBlockIndex];
3136
+ if (!blockElement) {
3137
+ debugWarn("overflow", "no DOM element for block index");
3138
+ return null;
3139
+ }
3140
+ const blockTop = blockElement.offsetTop;
3141
+ const heightForBlock = availableHeight - blockTop;
3142
+ if (heightForBlock <= 0) {
3143
+ debugWarn("overflow", "no space for block");
3144
+ return null;
3145
+ }
3146
+ if ($isListNode(blockNode)) {
3147
+ return splitListNode(blockNode, blockElement, heightForBlock);
3148
+ }
3149
+ return splitParagraphNode(blockNode, blockElement, heightForBlock);
3150
+ }
3151
+ function splitListNode(listNode, listElement, availableHeight) {
3152
+ const splitIndex = findListSplitIndex(listElement, availableHeight);
3153
+ if (splitIndex <= 0) {
3154
+ debug("overflow", "cannot split list — first item exceeds height");
3155
+ return null;
3156
+ }
3157
+ const [, overflowList] = $splitNode(listNode, splitIndex);
3158
+ const overflowNodes = [];
3159
+ const toRemove = [];
3160
+ overflowNodes.push(serializeNodeTree$1(overflowList));
3161
+ toRemove.push(overflowList);
3162
+ let nextSibling = overflowList.getNextSibling();
3163
+ while (nextSibling) {
3164
+ overflowNodes.push(serializeNodeTree$1(nextSibling));
3165
+ toRemove.push(nextSibling);
3166
+ nextSibling = nextSibling.getNextSibling();
3167
+ }
3168
+ for (const node of toRemove) {
3169
+ node.remove();
3170
+ }
3171
+ debug("overflow", `split list at item ${splitIndex}, extracted ${overflowNodes.length} overflow nodes`);
3172
+ return overflowNodes;
3173
+ }
3174
+ function splitParagraphNode(blockNode, blockElement, availableHeight) {
3175
+ const splitPoint = findMidBlockSplitPoint(blockElement, availableHeight);
3176
+ if (!splitPoint) {
3177
+ debug("overflow", "no valid split point found in paragraph");
3178
+ return null;
3179
+ }
3180
+ const lexicalNode = $getNearestNodeFromDOMNode(splitPoint.textNode);
3181
+ if (!lexicalNode || !$isTextNode(lexicalNode)) {
3182
+ debugWarn("overflow", "could not map DOM text node to Lexical TextNode");
3183
+ return null;
3184
+ }
3185
+ const [, afterText] = lexicalNode.splitText(splitPoint.offset);
3186
+ if (!afterText) {
3187
+ debugWarn("overflow", "splitText returned no second half");
3188
+ return null;
3189
+ }
3190
+ const parent = afterText.getParent();
3191
+ if (!parent || !$isElementNode(parent)) {
3192
+ debugWarn("overflow", "split text has no element parent");
3193
+ return null;
3194
+ }
3195
+ let directChild = afterText;
3196
+ let directChildParent = parent;
3197
+ while (directChildParent && directChildParent.getKey() !== blockNode.getKey()) {
3198
+ directChild = directChildParent;
3199
+ const next = directChildParent.getParent();
3200
+ if (!next || !$isElementNode(next)) {
3201
+ debugWarn("overflow", "could not find block node in parent chain");
3202
+ return null;
3203
+ }
3204
+ directChildParent = next;
3205
+ }
3206
+ const splitChildIndex = blockNode.getChildren().indexOf(directChild);
3207
+ if (splitChildIndex <= 0) {
3208
+ debug("overflow", "split index is 0 or not found — nothing to keep on this page");
3209
+ return null;
3210
+ }
3211
+ if (directChild !== afterText) {
3212
+ let current = parent;
3213
+ let childToSplitAt = afterText;
3214
+ while (current.getKey() !== blockNode.getKey()) {
3215
+ const childIdx = current.getChildren().indexOf(childToSplitAt);
3216
+ if (childIdx > 0) {
3217
+ $splitNode(current, childIdx);
3218
+ }
3219
+ childToSplitAt = current;
3220
+ const next = current.getParent();
3221
+ if (!next || !$isElementNode(next)) break;
3222
+ current = next;
3223
+ }
3224
+ }
3225
+ let finalSplitChild = afterText;
3226
+ let p = afterText.getParent();
3227
+ while (p && p.getKey() !== blockNode.getKey()) {
3228
+ finalSplitChild = p;
3229
+ p = p.getParent();
3230
+ }
3231
+ const finalSplitIndex = blockNode.getChildren().indexOf(finalSplitChild);
3232
+ if (finalSplitIndex <= 0) {
3233
+ debug("overflow", "final split index is 0 or not found — nothing to keep on this page");
3234
+ return null;
3235
+ }
3236
+ const [, overflowBlock] = $splitNode(blockNode, finalSplitIndex);
3237
+ const overflowNodes = [];
3238
+ const toRemove = [];
3239
+ overflowNodes.push(serializeNodeTree$1(overflowBlock));
3240
+ toRemove.push(overflowBlock);
3241
+ let nextSibling = overflowBlock.getNextSibling();
3242
+ while (nextSibling) {
3243
+ overflowNodes.push(serializeNodeTree$1(nextSibling));
3244
+ toRemove.push(nextSibling);
3245
+ nextSibling = nextSibling.getNextSibling();
3246
+ }
3247
+ for (const node of toRemove) {
3248
+ node.remove();
3249
+ }
3250
+ debug(
3251
+ "overflow",
3252
+ `split paragraph at offset ${splitPoint.offset}, extracted ${overflowNodes.length} overflow nodes`
3253
+ );
3254
+ return overflowNodes;
3255
+ }
2740
3256
  function serializeNodeTree(node) {
2741
3257
  const json = node.exportJSON();
2742
3258
  if ($isElementNode(node)) {
@@ -2765,18 +3281,51 @@ const OverflowPlugin = ({
2765
3281
  const children = Array.from(rootElement.children);
2766
3282
  if (children.length === 0) return;
2767
3283
  if (children.length <= 1) {
2768
- debug("overflow", `single block overflows (content=${contentHeight}px > available=${availableHeight}px) — cannot split`);
3284
+ debug("overflow", `single block overflows (content=${contentHeight}px > available=${availableHeight}px) — attempting mid-block split`);
3285
+ processingRef.current = true;
3286
+ editor.update(
3287
+ () => {
3288
+ const overflowNodes = performMidBlockSplit(rootElement, availableHeight, 0);
3289
+ if (!overflowNodes || overflowNodes.length === 0) {
3290
+ debug("overflow", "mid-block split not possible for single block");
3291
+ processingRef.current = false;
3292
+ return;
3293
+ }
3294
+ const overflowState = {
3295
+ root: {
3296
+ children: overflowNodes,
3297
+ direction: null,
3298
+ format: "",
3299
+ indent: 0,
3300
+ type: "root",
3301
+ version: 1
3302
+ }
3303
+ };
3304
+ debug("overflow", `mid-block split extracted ${overflowNodes.length} overflow nodes from single block`);
3305
+ setTimeout(() => {
3306
+ onOverflowRef.current(overflowState, cause);
3307
+ processingRef.current = false;
3308
+ }, 0);
3309
+ },
3310
+ { tag: "overflow-split" }
3311
+ );
2769
3312
  return;
2770
3313
  }
2771
3314
  processingRef.current = true;
2772
3315
  debug("overflow", `OVERFLOW detected: content=${contentHeight}px available=${availableHeight}px children=${children.length}`);
2773
3316
  let splitIndex = children.length;
3317
+ let firstBlockOverflows = false;
2774
3318
  for (let i = 0; i < children.length; i++) {
2775
3319
  const child = children[i];
2776
3320
  const childBottom = child.offsetTop + child.offsetHeight;
2777
- if (childBottom > availableHeight && i > 0) {
2778
- splitIndex = i;
2779
- debug("overflow", `split at index ${i} (childBottom=${childBottom}px > ${availableHeight}px)`);
3321
+ if (childBottom > availableHeight) {
3322
+ if (i === 0) {
3323
+ firstBlockOverflows = true;
3324
+ splitIndex = 1;
3325
+ } else {
3326
+ splitIndex = i;
3327
+ }
3328
+ debug("overflow", `split at index ${i === 0 ? "0 (mid-block)" : i} (childBottom=${childBottom}px > ${availableHeight}px)`);
2780
3329
  break;
2781
3330
  }
2782
3331
  }
@@ -2794,15 +3343,37 @@ const OverflowPlugin = ({
2794
3343
  processingRef.current = false;
2795
3344
  return;
2796
3345
  }
2797
- const overflowNodes = [];
2798
- const toRemove = allChildren.slice(splitIndex);
2799
- for (const node of toRemove) {
2800
- overflowNodes.push(serializeNodeTree(node));
3346
+ let overflowNodes = [];
3347
+ if (firstBlockOverflows) {
3348
+ const midBlockOverflow = performMidBlockSplit(rootElement, availableHeight, 0);
3349
+ if (midBlockOverflow && midBlockOverflow.length > 0) {
3350
+ overflowNodes.push(...midBlockOverflow);
3351
+ debug("overflow", `mid-block split on first block extracted ${midBlockOverflow.length} nodes`);
3352
+ } else {
3353
+ debug("overflow", "mid-block split failed on first block — keeping it whole");
3354
+ }
3355
+ const freshChildren = root.getChildren();
3356
+ const toRemove = freshChildren.slice(1);
3357
+ for (const node of toRemove) {
3358
+ overflowNodes.push(serializeNodeTree(node));
3359
+ }
3360
+ for (const node of toRemove) {
3361
+ node.remove();
3362
+ }
3363
+ } else {
3364
+ const toRemove = allChildren.slice(splitIndex);
3365
+ for (const node of toRemove) {
3366
+ overflowNodes.push(serializeNodeTree(node));
3367
+ }
3368
+ for (const node of toRemove) {
3369
+ node.remove();
3370
+ }
2801
3371
  }
2802
- for (const node of toRemove) {
2803
- node.remove();
3372
+ if (overflowNodes.length === 0) {
3373
+ processingRef.current = false;
3374
+ return;
2804
3375
  }
2805
- debug("overflow", `extracted ${overflowNodes.length} overflow nodes, kept ${splitIndex} nodes on current page`);
3376
+ debug("overflow", `extracted ${overflowNodes.length} overflow nodes total`);
2806
3377
  const overflowState = {
2807
3378
  root: {
2808
3379
  children: overflowNodes,
@@ -2883,16 +3454,10 @@ const OverflowPlugin = ({
2883
3454
  }, [editor]);
2884
3455
  return null;
2885
3456
  };
2886
- function createPageLabel(region, pageNumber) {
2887
- if (region === "body") {
2888
- return pageNumber ? `Page ${pageNumber}` : "Body";
2889
- }
2890
- const regionLabel = region.charAt(0).toUpperCase() + region.slice(1);
2891
- return pageNumber ? `${regionLabel} Page ${pageNumber}` : regionLabel;
2892
- }
2893
3457
  const HistoryCapturePlugin = ({ pageId, region }) => {
2894
3458
  const [editor] = useLexicalComposerContext();
2895
3459
  const { document: document2, queueHistoryAction } = useDocument();
3460
+ const t = useTranslations();
2896
3461
  const context = useMemo(() => {
2897
3462
  const pageNumber = document2.pages.findIndex((page) => page.id === pageId);
2898
3463
  return {
@@ -2900,9 +3465,18 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
2900
3465
  source: region
2901
3466
  };
2902
3467
  }, [document2.pages, pageId, region]);
3468
+ const createPageLabel = useMemo(() => {
3469
+ return (r, pageNumber) => {
3470
+ if (r === "body") {
3471
+ return pageNumber ? interpolate(t.regions.page, { page: pageNumber }) : t.regions.body;
3472
+ }
3473
+ const regionLabel = r === "header" ? t.regions.header : t.regions.footer;
3474
+ return pageNumber ? `${regionLabel} ${interpolate(t.regions.page, { page: pageNumber })}` : regionLabel;
3475
+ };
3476
+ }, [t]);
2903
3477
  useEffect(() => {
2904
- const buildDescriptor = (prefix) => ({
2905
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3478
+ const buildDescriptor = (label) => ({
3479
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
2906
3480
  source: context.source,
2907
3481
  pageId,
2908
3482
  region
@@ -2910,15 +3484,15 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
2910
3484
  return editor.registerCommand(
2911
3485
  CONTROLLED_TEXT_INSERTION_COMMAND,
2912
3486
  () => {
2913
- queueHistoryAction(buildDescriptor("Typed text"));
3487
+ queueHistoryAction(buildDescriptor(t.historyLabels.typedText));
2914
3488
  return false;
2915
3489
  },
2916
3490
  COMMAND_PRIORITY_LOW
2917
3491
  );
2918
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3492
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
2919
3493
  useEffect(() => {
2920
- const buildDescriptor = (prefix) => ({
2921
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3494
+ const buildDescriptor = (label) => ({
3495
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
2922
3496
  source: context.source,
2923
3497
  pageId,
2924
3498
  region
@@ -2932,15 +3506,15 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
2932
3506
  if (event.key.length !== 1) {
2933
3507
  return false;
2934
3508
  }
2935
- queueHistoryAction(buildDescriptor("Typed text"));
3509
+ queueHistoryAction(buildDescriptor(t.historyLabels.typedText));
2936
3510
  return false;
2937
3511
  },
2938
3512
  COMMAND_PRIORITY_LOW
2939
3513
  );
2940
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3514
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
2941
3515
  useEffect(() => {
2942
- const buildDescriptor = (prefix) => ({
2943
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3516
+ const buildDescriptor = (label) => ({
3517
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
2944
3518
  source: context.source,
2945
3519
  pageId,
2946
3520
  region
@@ -2953,15 +3527,15 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
2953
3527
  if (text.trim().length === 0) {
2954
3528
  return false;
2955
3529
  }
2956
- queueHistoryAction(buildDescriptor("Pasted content"));
3530
+ queueHistoryAction(buildDescriptor(t.historyLabels.pastedContent));
2957
3531
  return false;
2958
3532
  },
2959
3533
  COMMAND_PRIORITY_LOW
2960
3534
  );
2961
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3535
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
2962
3536
  useEffect(() => {
2963
- const buildDescriptor = (prefix) => ({
2964
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3537
+ const buildDescriptor = (label) => ({
3538
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
2965
3539
  source: context.source,
2966
3540
  pageId,
2967
3541
  region
@@ -2969,15 +3543,15 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
2969
3543
  return editor.registerCommand(
2970
3544
  KEY_ENTER_COMMAND,
2971
3545
  () => {
2972
- queueHistoryAction(buildDescriptor("Inserted line break"));
3546
+ queueHistoryAction(buildDescriptor(t.historyLabels.insertedLineBreak));
2973
3547
  return false;
2974
3548
  },
2975
3549
  COMMAND_PRIORITY_LOW
2976
3550
  );
2977
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3551
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
2978
3552
  useEffect(() => {
2979
- const buildDescriptor = (prefix) => ({
2980
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3553
+ const buildDescriptor = (label) => ({
3554
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
2981
3555
  source: context.source,
2982
3556
  pageId,
2983
3557
  region
@@ -2985,15 +3559,15 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
2985
3559
  return editor.registerCommand(
2986
3560
  KEY_BACKSPACE_COMMAND,
2987
3561
  () => {
2988
- queueHistoryAction(buildDescriptor("Deleted backward"));
3562
+ queueHistoryAction(buildDescriptor(t.historyLabels.deletedBackward));
2989
3563
  return false;
2990
3564
  },
2991
3565
  COMMAND_PRIORITY_LOW
2992
3566
  );
2993
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3567
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
2994
3568
  useEffect(() => {
2995
- const buildDescriptor = (prefix) => ({
2996
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3569
+ const buildDescriptor = (label) => ({
3570
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
2997
3571
  source: context.source,
2998
3572
  pageId,
2999
3573
  region
@@ -3001,15 +3575,15 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
3001
3575
  return editor.registerCommand(
3002
3576
  KEY_DELETE_COMMAND,
3003
3577
  () => {
3004
- queueHistoryAction(buildDescriptor("Deleted forward"));
3578
+ queueHistoryAction(buildDescriptor(t.historyLabels.deletedForward));
3005
3579
  return false;
3006
3580
  },
3007
3581
  COMMAND_PRIORITY_LOW
3008
3582
  );
3009
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3583
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
3010
3584
  useEffect(() => {
3011
- const buildDescriptor = (prefix) => ({
3012
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3585
+ const buildDescriptor = (label) => ({
3586
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
3013
3587
  source: context.source,
3014
3588
  pageId,
3015
3589
  region
@@ -3017,15 +3591,15 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
3017
3591
  return editor.registerCommand(
3018
3592
  FORMAT_TEXT_COMMAND,
3019
3593
  () => {
3020
- queueHistoryAction(buildDescriptor("Formatted text"));
3594
+ queueHistoryAction(buildDescriptor(t.historyLabels.formattedText));
3021
3595
  return false;
3022
3596
  },
3023
3597
  COMMAND_PRIORITY_LOW
3024
3598
  );
3025
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3599
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
3026
3600
  useEffect(() => {
3027
- const buildDescriptor = (prefix) => ({
3028
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3601
+ const buildDescriptor = (label) => ({
3602
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
3029
3603
  source: context.source,
3030
3604
  pageId,
3031
3605
  region
@@ -3033,12 +3607,12 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
3033
3607
  return editor.registerCommand(
3034
3608
  FORMAT_ELEMENT_COMMAND,
3035
3609
  () => {
3036
- queueHistoryAction(buildDescriptor("Formatted paragraph"));
3610
+ queueHistoryAction(buildDescriptor(t.historyLabels.formattedParagraph));
3037
3611
  return false;
3038
3612
  },
3039
3613
  COMMAND_PRIORITY_LOW
3040
3614
  );
3041
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3615
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
3042
3616
  return null;
3043
3617
  };
3044
3618
  function getCollapsedTextPosition(rootElement) {
@@ -3143,6 +3717,7 @@ const PageBody = ({
3143
3717
  readOnly = false
3144
3718
  }) => {
3145
3719
  const { nodes, bodyPlugins, themeOverrides } = useExtensions();
3720
+ const t = useTranslations();
3146
3721
  const config = useMemo(
3147
3722
  () => {
3148
3723
  var _a, _b;
@@ -3196,7 +3771,7 @@ const PageBody = ({
3196
3771
  style: { overflow: "visible" }
3197
3772
  }
3198
3773
  ),
3199
- placeholder: /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 text-gray-400 pointer-events-none select-none", children: "Start typing..." }),
3774
+ placeholder: /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 text-gray-400 pointer-events-none select-none", children: t.body.placeholder }),
3200
3775
  ErrorBoundary: LexicalErrorBoundary
3201
3776
  }
3202
3777
  ),
@@ -3336,10 +3911,10 @@ const PageHeader = ({
3336
3911
  ContentEditable,
3337
3912
  {
3338
3913
  ref: contentRef,
3339
- className: `outline-none p-2 text-sm text-gray-600 min-h-[24px] ${hasPageCounter ? "pr-24" : ""}`
3914
+ className: `outline-none p-2 text-gray-600 min-h-[24px] ${hasPageCounter ? "pr-24" : ""}`
3340
3915
  }
3341
3916
  ),
3342
- placeholder: /* @__PURE__ */ jsx("div", { className: `absolute top-0 left-0 text-gray-400 pointer-events-none select-none p-2 text-sm ${hasPageCounter ? "pr-24" : ""}`, children: t.header.placeholder }),
3917
+ placeholder: /* @__PURE__ */ jsx("div", { className: `absolute top-0 left-0 text-gray-400 pointer-events-none select-none p-2 ${hasPageCounter ? "pr-24" : ""}`, children: t.header.placeholder }),
3343
3918
  ErrorBoundary: LexicalErrorBoundary
3344
3919
  }
3345
3920
  ),
@@ -3416,10 +3991,10 @@ const PageFooter = ({
3416
3991
  ContentEditable,
3417
3992
  {
3418
3993
  ref: contentRef,
3419
- className: `outline-none p-2 text-sm text-gray-600 min-h-[24px] ${hasPageCounter ? "pr-24" : ""}`
3994
+ className: `outline-none p-2 text-gray-600 min-h-[24px] ${hasPageCounter ? "pr-24" : ""}`
3420
3995
  }
3421
3996
  ),
3422
- placeholder: /* @__PURE__ */ jsx("div", { className: `absolute top-0 left-0 text-gray-400 pointer-events-none select-none p-2 text-sm ${hasPageCounter ? "pr-24" : ""}`, children: t.footer.placeholder }),
3997
+ placeholder: /* @__PURE__ */ jsx("div", { className: `absolute top-0 left-0 text-gray-400 pointer-events-none select-none p-2 ${hasPageCounter ? "pr-24" : ""}`, children: t.footer.placeholder }),
3423
3998
  ErrorBoundary: LexicalErrorBoundary
3424
3999
  }
3425
4000
  ),
@@ -3450,10 +4025,14 @@ const PageView = React.memo(({
3450
4025
  onMoveToNextPage
3451
4026
  }) => {
3452
4027
  const { document: document2, dispatch, setActivePageId } = useDocument();
4028
+ const t = useTranslations();
3453
4029
  const page = document2.pages.find((p) => p.id === pageId);
3454
4030
  const showHeaderFooter = document2.headerFooterEnabled;
3455
4031
  const pageCounterMode = document2.pageCounterMode;
3456
- const pageCounterLabel = `Page ${pageIndex + 1} of ${document2.pages.length}`;
4032
+ const pageCounterLabel = interpolate(t.pageCounter.format, {
4033
+ current: pageIndex + 1,
4034
+ total: document2.pages.length
4035
+ });
3457
4036
  if (!page) return null;
3458
4037
  const handleBodyChange = useCallback(
3459
4038
  (bodyState) => {
@@ -3544,6 +4123,7 @@ const DocumentView = () => {
3544
4123
  setActiveEditor,
3545
4124
  setActivePageId
3546
4125
  } = useDocument();
4126
+ const t = useTranslations();
3547
4127
  const { handlePageUnderflow, reflowAll } = usePagination(document2, dispatch);
3548
4128
  const previousBodyHeightsRef = useRef(null);
3549
4129
  const pasteOverflowSequenceRef = useRef(false);
@@ -3708,7 +4288,7 @@ const DocumentView = () => {
3708
4288
  }
3709
4289
  runHistoryAction(
3710
4290
  {
3711
- label: `Deleted backward - Page ${pageIndex + 1}`,
4291
+ label: `${t.historyLabels.deletedBackward} - ${interpolate(t.regions.page, { page: pageIndex + 1 })}`,
3712
4292
  source: "body",
3713
4293
  pageId,
3714
4294
  region: "body"
@@ -3730,7 +4310,7 @@ const DocumentView = () => {
3730
4310
  }
3731
4311
  runHistoryAction(
3732
4312
  {
3733
- label: `Deleted forward - Page ${pageIndex + 1}`,
4313
+ label: `${t.historyLabels.deletedForward} - ${interpolate(t.regions.page, { page: pageIndex + 1 })}`,
3734
4314
  source: "body",
3735
4315
  pageId,
3736
4316
  region: "body"
@@ -3963,7 +4543,7 @@ const EditorChrome = ({
3963
4543
  }
3964
4544
  ),
3965
4545
  /* @__PURE__ */ jsx(Toolbar, {}),
3966
- /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden bg-gray-700", children: [
4546
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden bg-gray-200", children: [
3967
4547
  /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 overflow-auto", children: /* @__PURE__ */ jsx(DocumentView, {}) }),
3968
4548
  sidePanels.map((Panel, idx) => /* @__PURE__ */ jsx(Panel, {}, idx)),
3969
4549
  /* @__PURE__ */ jsx(HistorySidebar, {})
@@ -4777,6 +5357,7 @@ export {
4777
5357
  Lex4Editor,
4778
5358
  MAX_FOOTER_HEIGHT_PX,
4779
5359
  MAX_HEADER_HEIGHT_PX,
5360
+ PT_BR_TRANSLATIONS,
4780
5361
  VariableNode,
4781
5362
  astExtension,
4782
5363
  buildSavePayload,