@yurikilian/lex4 0.2.2 → 0.3.2

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.
@@ -454,6 +454,258 @@ function jumpToHistoryEntry(history, entryIndex) {
454
454
  caretSelection: ((_b = nextHistory.entries[nextHistory.cursor - 1]) == null ? void 0 : _b.caretSelection) ?? null
455
455
  };
456
456
  }
457
+ const DEFAULT_TRANSLATIONS = {
458
+ toolbar: {
459
+ undo: "Undo",
460
+ redo: "Redo",
461
+ bold: "Bold (Ctrl+B)",
462
+ italic: "Italic (Ctrl+I)",
463
+ underline: "Underline (Ctrl+U)",
464
+ strikethrough: "Strikethrough",
465
+ alignLeft: "Align Left",
466
+ alignCenter: "Align Center",
467
+ alignRight: "Align Right",
468
+ justify: "Justify",
469
+ numberedList: "Numbered List",
470
+ bulletList: "Bullet List",
471
+ indent: "Indent",
472
+ outdent: "Outdent",
473
+ openHistory: "Open History",
474
+ closeHistory: "Close History"
475
+ },
476
+ history: {
477
+ title: "History",
478
+ subtitle: "Word-style session history (last 100 actions)",
479
+ empty: "No history yet.",
480
+ clearHistory: "Clear History",
481
+ actions: {
482
+ enabledHeadersFooters: "Enabled headers and footers",
483
+ disabledHeadersFooters: "Disabled headers and footers",
484
+ copiedHeaderToAll: "Copied header to all pages",
485
+ copiedFooterToAll: "Copied footer to all pages",
486
+ clearedHeader: "Cleared header",
487
+ clearedFooter: "Cleared footer",
488
+ clearedAllHeaders: "Cleared all headers",
489
+ clearedAllFooters: "Cleared all footers",
490
+ pageCounterSet: "Page counter set to {{value}}",
491
+ boldApplied: "Bold applied",
492
+ italicApplied: "Italic applied",
493
+ underlineApplied: "Underline applied",
494
+ strikethroughApplied: "Strikethrough applied",
495
+ alignedLeft: "Aligned left",
496
+ alignedCenter: "Aligned center",
497
+ alignedRight: "Aligned right",
498
+ justifiedText: "Justified text",
499
+ insertedNumberedList: "Inserted numbered list",
500
+ insertedBulletList: "Inserted bullet list",
501
+ indentedContent: "Indented content",
502
+ outdentedContent: "Outdented content",
503
+ fontChanged: "Font changed to {{value}}",
504
+ fontSizeChanged: "Font size changed to {{value}}pt"
505
+ }
506
+ },
507
+ variables: {
508
+ title: "Variables",
509
+ available: "{{count}} available",
510
+ refreshVariables: "Refresh variables",
511
+ searchPlaceholder: "Search variables...",
512
+ noVariablesFound: "No variables found",
513
+ insertVariable: "Insert variable {{key}}",
514
+ openPanel: "Open Variables",
515
+ closePanel: "Close Variables"
516
+ },
517
+ header: {
518
+ placeholder: "Header"
519
+ },
520
+ footer: {
521
+ placeholder: "Footer"
522
+ },
523
+ body: {
524
+ placeholder: "Start typing..."
525
+ },
526
+ pageCounter: {
527
+ format: "Page {{current}} of {{total}}"
528
+ },
529
+ regions: {
530
+ body: "body",
531
+ header: "header",
532
+ footer: "footer",
533
+ document: "document",
534
+ toolbar: "toolbar",
535
+ overflow: "overflow",
536
+ history: "history",
537
+ page: "Page {{page}}"
538
+ },
539
+ headerFooter: {
540
+ label: "Headers & Footers"
541
+ },
542
+ sidebar: {
543
+ close: "Close sidebar"
544
+ },
545
+ historyLabels: {
546
+ typedText: "Typed text",
547
+ pastedContent: "Pasted content",
548
+ insertedLineBreak: "Inserted line break",
549
+ deletedBackward: "Deleted backward",
550
+ deletedForward: "Deleted forward",
551
+ formattedText: "Formatted text",
552
+ formattedParagraph: "Formatted paragraph",
553
+ editedBody: "Edited body",
554
+ editedHeader: "Edited header",
555
+ editedFooter: "Edited footer",
556
+ clearedDocumentBody: "Cleared document body",
557
+ resizedHeader: "Resized header",
558
+ resizedFooter: "Resized footer",
559
+ addedPage: "Added page",
560
+ removedPage: "Removed page",
561
+ documentReflow: "Document reflow",
562
+ updatedDocument: "Updated document"
563
+ }
564
+ };
565
+ const PT_BR_TRANSLATIONS = {
566
+ toolbar: {
567
+ undo: "Desfazer",
568
+ redo: "Refazer",
569
+ bold: "Negrito (Ctrl+B)",
570
+ italic: "Itálico (Ctrl+I)",
571
+ underline: "Sublinhado (Ctrl+U)",
572
+ strikethrough: "Tachado",
573
+ alignLeft: "Alinhar à Esquerda",
574
+ alignCenter: "Centralizar",
575
+ alignRight: "Alinhar à Direita",
576
+ justify: "Justificar",
577
+ numberedList: "Lista Numerada",
578
+ bulletList: "Lista com Marcadores",
579
+ indent: "Aumentar Recuo",
580
+ outdent: "Diminuir Recuo",
581
+ openHistory: "Abrir Histórico",
582
+ closeHistory: "Fechar Histórico"
583
+ },
584
+ history: {
585
+ title: "Histórico",
586
+ subtitle: "Histórico de sessão (últimas 100 ações)",
587
+ empty: "Nenhum histórico ainda.",
588
+ clearHistory: "Limpar Histórico",
589
+ actions: {
590
+ enabledHeadersFooters: "Cabeçalhos e rodapés ativados",
591
+ disabledHeadersFooters: "Cabeçalhos e rodapés desativados",
592
+ copiedHeaderToAll: "Cabeçalho copiado para todas as páginas",
593
+ copiedFooterToAll: "Rodapé copiado para todas as páginas",
594
+ clearedHeader: "Cabeçalho limpo",
595
+ clearedFooter: "Rodapé limpo",
596
+ clearedAllHeaders: "Todos os cabeçalhos limpos",
597
+ clearedAllFooters: "Todos os rodapés limpos",
598
+ pageCounterSet: "Contador de páginas definido como {{value}}",
599
+ boldApplied: "Negrito aplicado",
600
+ italicApplied: "Itálico aplicado",
601
+ underlineApplied: "Sublinhado aplicado",
602
+ strikethroughApplied: "Tachado aplicado",
603
+ alignedLeft: "Alinhado à esquerda",
604
+ alignedCenter: "Centralizado",
605
+ alignedRight: "Alinhado à direita",
606
+ justifiedText: "Texto justificado",
607
+ insertedNumberedList: "Lista numerada inserida",
608
+ insertedBulletList: "Lista com marcadores inserida",
609
+ indentedContent: "Conteúdo recuado",
610
+ outdentedContent: "Recuo reduzido",
611
+ fontChanged: "Fonte alterada para {{value}}",
612
+ fontSizeChanged: "Tamanho da fonte alterado para {{value}}pt"
613
+ }
614
+ },
615
+ variables: {
616
+ title: "Variáveis",
617
+ available: "{{count}} disponíveis",
618
+ refreshVariables: "Atualizar variáveis",
619
+ searchPlaceholder: "Buscar variáveis...",
620
+ noVariablesFound: "Nenhuma variável encontrada",
621
+ insertVariable: "Inserir variável {{key}}",
622
+ openPanel: "Abrir Variáveis",
623
+ closePanel: "Fechar Variáveis"
624
+ },
625
+ header: {
626
+ placeholder: "Cabeçalho"
627
+ },
628
+ footer: {
629
+ placeholder: "Rodapé"
630
+ },
631
+ body: {
632
+ placeholder: "Comece a digitar..."
633
+ },
634
+ pageCounter: {
635
+ format: "Página {{current}} de {{total}}"
636
+ },
637
+ regions: {
638
+ body: "corpo",
639
+ header: "cabeçalho",
640
+ footer: "rodapé",
641
+ document: "documento",
642
+ toolbar: "barra de ferramentas",
643
+ overflow: "estouro",
644
+ history: "histórico",
645
+ page: "Página {{page}}"
646
+ },
647
+ headerFooter: {
648
+ label: "Cabeçalhos e Rodapés"
649
+ },
650
+ sidebar: {
651
+ close: "Fechar barra lateral"
652
+ },
653
+ historyLabels: {
654
+ typedText: "Texto digitado",
655
+ pastedContent: "Conteúdo colado",
656
+ insertedLineBreak: "Quebra de linha inserida",
657
+ deletedBackward: "Exclusão para trás",
658
+ deletedForward: "Exclusão para frente",
659
+ formattedText: "Texto formatado",
660
+ formattedParagraph: "Parágrafo formatado",
661
+ editedBody: "Corpo editado",
662
+ editedHeader: "Cabeçalho editado",
663
+ editedFooter: "Rodapé editado",
664
+ clearedDocumentBody: "Corpo do documento limpo",
665
+ resizedHeader: "Cabeçalho redimensionado",
666
+ resizedFooter: "Rodapé redimensionado",
667
+ addedPage: "Página adicionada",
668
+ removedPage: "Página removida",
669
+ documentReflow: "Redistribuição do documento",
670
+ updatedDocument: "Documento atualizado"
671
+ }
672
+ };
673
+ function deepMerge(target, source) {
674
+ if (typeof target !== "object" || target === null) {
675
+ return source ?? target;
676
+ }
677
+ const result = { ...target };
678
+ for (const key of Object.keys(source)) {
679
+ const sourceVal = source[key];
680
+ const targetVal = target[key];
681
+ if (sourceVal !== void 0 && typeof sourceVal === "object" && sourceVal !== null && !Array.isArray(sourceVal) && typeof targetVal === "object" && targetVal !== null) {
682
+ result[key] = deepMerge(targetVal, sourceVal);
683
+ } else if (sourceVal !== void 0) {
684
+ result[key] = sourceVal;
685
+ }
686
+ }
687
+ return result;
688
+ }
689
+ const TranslationsContext = React.createContext(DEFAULT_TRANSLATIONS);
690
+ function useTranslations() {
691
+ return React.useContext(TranslationsContext);
692
+ }
693
+ function interpolate(template, params) {
694
+ return template.replace(
695
+ /\{\{(\w+)\}\}/g,
696
+ (_, key) => String(params[key] ?? `{{${key}}}`)
697
+ );
698
+ }
699
+ const TranslationsProvider = ({
700
+ translations,
701
+ children
702
+ }) => {
703
+ const merged = React.useMemo(
704
+ () => translations ? deepMerge(DEFAULT_TRANSLATIONS, translations) : DEFAULT_TRANSLATIONS,
705
+ [translations]
706
+ );
707
+ return /* @__PURE__ */ jsxRuntime.jsx(TranslationsContext.Provider, { value: merged, children });
708
+ };
457
709
  const HISTORY_RESTORE_SUPPRESSION_MS = 100;
458
710
  const HISTORY_BATCH_FLUSH_MS = 16;
459
711
  function cloneDocumentSnapshot(document2) {
@@ -526,12 +778,15 @@ function getPageNumber(document2, pageId) {
526
778
  const index = document2.pages.findIndex((page) => page.id === pageId);
527
779
  return index >= 0 ? index + 1 : null;
528
780
  }
529
- function describeAction(action, document2) {
781
+ function describeAction(action, document2, t) {
782
+ const pageSuffix = (pageId) => {
783
+ const num = getPageNumber(document2, pageId);
784
+ return num ? ` - ${interpolate(t.regions.page, { page: num })}` : "";
785
+ };
530
786
  switch (action.type) {
531
787
  case "UPDATE_PAGE_BODY": {
532
- const pageNumber = getPageNumber(document2, action.pageId);
533
788
  return {
534
- label: pageNumber ? `Edited body - Page ${pageNumber}` : "Edited body",
789
+ label: t.historyLabels.editedBody + pageSuffix(action.pageId),
535
790
  source: "body",
536
791
  pageId: action.pageId,
537
792
  region: "body"
@@ -539,9 +794,8 @@ function describeAction(action, document2) {
539
794
  }
540
795
  case "UPDATE_PAGE_HEADER":
541
796
  case "UPDATE_PAGE_HEADER_CONTENT": {
542
- const pageNumber = getPageNumber(document2, action.pageId);
543
797
  return {
544
- label: pageNumber ? `Edited header - Page ${pageNumber}` : "Edited header",
798
+ label: t.historyLabels.editedHeader + pageSuffix(action.pageId),
545
799
  source: "header",
546
800
  pageId: action.pageId,
547
801
  region: "header"
@@ -549,9 +803,8 @@ function describeAction(action, document2) {
549
803
  }
550
804
  case "UPDATE_PAGE_FOOTER":
551
805
  case "UPDATE_PAGE_FOOTER_CONTENT": {
552
- const pageNumber = getPageNumber(document2, action.pageId);
553
806
  return {
554
- label: pageNumber ? `Edited footer - Page ${pageNumber}` : "Edited footer",
807
+ label: t.historyLabels.editedFooter + pageSuffix(action.pageId),
555
808
  source: "footer",
556
809
  pageId: action.pageId,
557
810
  region: "footer"
@@ -559,43 +812,41 @@ function describeAction(action, document2) {
559
812
  }
560
813
  case "SET_HEADER_FOOTER_ENABLED":
561
814
  return {
562
- label: action.enabled ? "Enabled headers and footers" : "Disabled headers and footers",
815
+ label: action.enabled ? t.history.actions.enabledHeadersFooters : t.history.actions.disabledHeadersFooters,
563
816
  source: "document",
564
817
  region: "document"
565
818
  };
566
819
  case "SET_PAGE_COUNTER_MODE":
567
820
  return {
568
- label: `Page counter set to ${action.mode}`,
821
+ label: interpolate(t.history.actions.pageCounterSet, { value: action.mode }),
569
822
  source: "document",
570
823
  region: "document"
571
824
  };
572
825
  case "COPY_HEADER_TO_ALL":
573
826
  return {
574
- label: "Copied header to all pages",
827
+ label: t.history.actions.copiedHeaderToAll,
575
828
  source: "toolbar",
576
829
  pageId: action.sourcePageId,
577
830
  region: "header"
578
831
  };
579
832
  case "COPY_FOOTER_TO_ALL":
580
833
  return {
581
- label: "Copied footer to all pages",
834
+ label: t.history.actions.copiedFooterToAll,
582
835
  source: "toolbar",
583
836
  pageId: action.sourcePageId,
584
837
  region: "footer"
585
838
  };
586
839
  case "CLEAR_HEADER": {
587
- const pageNumber = getPageNumber(document2, action.pageId);
588
840
  return {
589
- label: pageNumber ? `Cleared header - Page ${pageNumber}` : "Cleared header",
841
+ label: t.history.actions.clearedHeader + pageSuffix(action.pageId),
590
842
  source: "toolbar",
591
843
  pageId: action.pageId,
592
844
  region: "header"
593
845
  };
594
846
  }
595
847
  case "CLEAR_FOOTER": {
596
- const pageNumber = getPageNumber(document2, action.pageId);
597
848
  return {
598
- label: pageNumber ? `Cleared footer - Page ${pageNumber}` : "Cleared footer",
849
+ label: t.history.actions.clearedFooter + pageSuffix(action.pageId),
599
850
  source: "toolbar",
600
851
  pageId: action.pageId,
601
852
  region: "footer"
@@ -603,35 +854,33 @@ function describeAction(action, document2) {
603
854
  }
604
855
  case "CLEAR_ALL_HEADERS":
605
856
  return {
606
- label: "Cleared all headers",
857
+ label: t.history.actions.clearedAllHeaders,
607
858
  source: "toolbar",
608
859
  region: "header"
609
860
  };
610
861
  case "CLEAR_ALL_FOOTERS":
611
862
  return {
612
- label: "Cleared all footers",
863
+ label: t.history.actions.clearedAllFooters,
613
864
  source: "toolbar",
614
865
  region: "footer"
615
866
  };
616
867
  case "CLEAR_DOCUMENT_CONTENT":
617
868
  return {
618
- label: "Cleared document body",
869
+ label: t.historyLabels.clearedDocumentBody,
619
870
  source: "document",
620
871
  region: "document"
621
872
  };
622
873
  case "SET_HEADER_HEIGHT": {
623
- const pageNumber = getPageNumber(document2, action.pageId);
624
874
  return {
625
- label: pageNumber ? `Resized header - Page ${pageNumber}` : "Resized header",
875
+ label: t.historyLabels.resizedHeader + pageSuffix(action.pageId),
626
876
  source: "header",
627
877
  pageId: action.pageId,
628
878
  region: "header"
629
879
  };
630
880
  }
631
881
  case "SET_FOOTER_HEIGHT": {
632
- const pageNumber = getPageNumber(document2, action.pageId);
633
882
  return {
634
- label: pageNumber ? `Resized footer - Page ${pageNumber}` : "Resized footer",
883
+ label: t.historyLabels.resizedFooter + pageSuffix(action.pageId),
635
884
  source: "footer",
636
885
  pageId: action.pageId,
637
886
  region: "footer"
@@ -639,25 +888,25 @@ function describeAction(action, document2) {
639
888
  }
640
889
  case "ADD_PAGE":
641
890
  return {
642
- label: "Added page",
891
+ label: t.historyLabels.addedPage,
643
892
  source: "overflow",
644
893
  region: "document"
645
894
  };
646
895
  case "REMOVE_PAGE":
647
896
  return {
648
- label: "Removed page",
897
+ label: t.historyLabels.removedPage,
649
898
  source: "overflow",
650
899
  region: "document"
651
900
  };
652
901
  case "SET_DOCUMENT":
653
902
  return {
654
- label: "Document reflow",
903
+ label: t.historyLabels.documentReflow,
655
904
  source: "overflow",
656
905
  region: "document"
657
906
  };
658
907
  default:
659
908
  return {
660
- label: "Updated document",
909
+ label: t.historyLabels.updatedDocument,
661
910
  source: "document",
662
911
  region: "document"
663
912
  };
@@ -684,6 +933,7 @@ const DocumentProvider = ({
684
933
  const [globalSelectionActive, setGlobalSelectionActive] = React.useState(false);
685
934
  const [historySidebarOpen, setHistorySidebarOpen] = React.useState(true);
686
935
  const [focusAtEndVersion, setFocusAtEndVersion] = React.useState(0);
936
+ const t = useTranslations();
687
937
  const activeEditorRef = React.useRef(null);
688
938
  const activeCaretPositionRef = React.useRef(null);
689
939
  const pendingCaretPositionRef = React.useRef(null);
@@ -877,7 +1127,7 @@ const DocumentProvider = ({
877
1127
  };
878
1128
  scheduleHistoryBatchFlush();
879
1129
  } else {
880
- const descriptor = describeAction(action, currentDocument);
1130
+ const descriptor = describeAction(action, currentDocument, t);
881
1131
  setHistoryState((previousHistory) => {
882
1132
  const nextHistory = recordHistoryEntry(
883
1133
  previousHistory,
@@ -1476,194 +1726,6 @@ const __iconNode = [
1476
1726
  ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
1477
1727
  ];
1478
1728
  const X = createLucideIcon("x", __iconNode);
1479
- const DEFAULT_TRANSLATIONS = {
1480
- toolbar: {
1481
- undo: "Undo",
1482
- redo: "Redo",
1483
- bold: "Bold (Ctrl+B)",
1484
- italic: "Italic (Ctrl+I)",
1485
- underline: "Underline (Ctrl+U)",
1486
- strikethrough: "Strikethrough",
1487
- alignLeft: "Align Left",
1488
- alignCenter: "Align Center",
1489
- alignRight: "Align Right",
1490
- justify: "Justify",
1491
- numberedList: "Numbered List",
1492
- bulletList: "Bullet List",
1493
- indent: "Indent",
1494
- outdent: "Outdent",
1495
- openHistory: "Open History",
1496
- closeHistory: "Close History"
1497
- },
1498
- history: {
1499
- title: "History",
1500
- subtitle: "Word-style session history (last 100 actions)",
1501
- empty: "No history yet.",
1502
- clearHistory: "Clear History",
1503
- actions: {
1504
- enabledHeadersFooters: "Enabled headers and footers",
1505
- disabledHeadersFooters: "Disabled headers and footers",
1506
- copiedHeaderToAll: "Copied header to all pages",
1507
- copiedFooterToAll: "Copied footer to all pages",
1508
- clearedHeader: "Cleared header",
1509
- clearedFooter: "Cleared footer",
1510
- clearedAllHeaders: "Cleared all headers",
1511
- clearedAllFooters: "Cleared all footers",
1512
- pageCounterSet: "Page counter set to {{value}}",
1513
- boldApplied: "Bold applied",
1514
- italicApplied: "Italic applied",
1515
- underlineApplied: "Underline applied",
1516
- strikethroughApplied: "Strikethrough applied",
1517
- alignedLeft: "Aligned left",
1518
- alignedCenter: "Aligned center",
1519
- alignedRight: "Aligned right",
1520
- justifiedText: "Justified text",
1521
- insertedNumberedList: "Inserted numbered list",
1522
- insertedBulletList: "Inserted bullet list",
1523
- indentedContent: "Indented content",
1524
- outdentedContent: "Outdented content",
1525
- fontChanged: "Font changed to {{value}}",
1526
- fontSizeChanged: "Font size changed to {{value}}pt"
1527
- }
1528
- },
1529
- variables: {
1530
- title: "Variables",
1531
- available: "{{count}} available",
1532
- refreshVariables: "Refresh variables",
1533
- searchPlaceholder: "Search variables...",
1534
- noVariablesFound: "No variables found",
1535
- insertVariable: "Insert variable {{key}}",
1536
- openPanel: "Open Variables",
1537
- closePanel: "Close Variables"
1538
- },
1539
- header: {
1540
- placeholder: "Header"
1541
- },
1542
- footer: {
1543
- placeholder: "Footer"
1544
- },
1545
- body: {
1546
- placeholder: "Start typing..."
1547
- },
1548
- headerFooter: {
1549
- label: "Headers & Footers"
1550
- },
1551
- sidebar: {
1552
- close: "Close sidebar"
1553
- }
1554
- };
1555
- const PT_BR_TRANSLATIONS = {
1556
- toolbar: {
1557
- undo: "Desfazer",
1558
- redo: "Refazer",
1559
- bold: "Negrito (Ctrl+B)",
1560
- italic: "Itálico (Ctrl+I)",
1561
- underline: "Sublinhado (Ctrl+U)",
1562
- strikethrough: "Tachado",
1563
- alignLeft: "Alinhar à Esquerda",
1564
- alignCenter: "Centralizar",
1565
- alignRight: "Alinhar à Direita",
1566
- justify: "Justificar",
1567
- numberedList: "Lista Numerada",
1568
- bulletList: "Lista com Marcadores",
1569
- indent: "Aumentar Recuo",
1570
- outdent: "Diminuir Recuo",
1571
- openHistory: "Abrir Histórico",
1572
- closeHistory: "Fechar Histórico"
1573
- },
1574
- history: {
1575
- title: "Histórico",
1576
- subtitle: "Histórico de sessão (últimas 100 ações)",
1577
- empty: "Nenhum histórico ainda.",
1578
- clearHistory: "Limpar Histórico",
1579
- actions: {
1580
- enabledHeadersFooters: "Cabeçalhos e rodapés ativados",
1581
- disabledHeadersFooters: "Cabeçalhos e rodapés desativados",
1582
- copiedHeaderToAll: "Cabeçalho copiado para todas as páginas",
1583
- copiedFooterToAll: "Rodapé copiado para todas as páginas",
1584
- clearedHeader: "Cabeçalho limpo",
1585
- clearedFooter: "Rodapé limpo",
1586
- clearedAllHeaders: "Todos os cabeçalhos limpos",
1587
- clearedAllFooters: "Todos os rodapés limpos",
1588
- pageCounterSet: "Contador de páginas definido como {{value}}",
1589
- boldApplied: "Negrito aplicado",
1590
- italicApplied: "Itálico aplicado",
1591
- underlineApplied: "Sublinhado aplicado",
1592
- strikethroughApplied: "Tachado aplicado",
1593
- alignedLeft: "Alinhado à esquerda",
1594
- alignedCenter: "Centralizado",
1595
- alignedRight: "Alinhado à direita",
1596
- justifiedText: "Texto justificado",
1597
- insertedNumberedList: "Lista numerada inserida",
1598
- insertedBulletList: "Lista com marcadores inserida",
1599
- indentedContent: "Conteúdo recuado",
1600
- outdentedContent: "Recuo reduzido",
1601
- fontChanged: "Fonte alterada para {{value}}",
1602
- fontSizeChanged: "Tamanho da fonte alterado para {{value}}pt"
1603
- }
1604
- },
1605
- variables: {
1606
- title: "Variáveis",
1607
- available: "{{count}} disponíveis",
1608
- refreshVariables: "Atualizar variáveis",
1609
- searchPlaceholder: "Buscar variáveis...",
1610
- noVariablesFound: "Nenhuma variável encontrada",
1611
- insertVariable: "Inserir variável {{key}}",
1612
- openPanel: "Abrir Variáveis",
1613
- closePanel: "Fechar Variáveis"
1614
- },
1615
- header: {
1616
- placeholder: "Cabeçalho"
1617
- },
1618
- footer: {
1619
- placeholder: "Rodapé"
1620
- },
1621
- body: {
1622
- placeholder: "Comece a digitar..."
1623
- },
1624
- headerFooter: {
1625
- label: "Cabeçalhos e Rodapés"
1626
- },
1627
- sidebar: {
1628
- close: "Fechar barra lateral"
1629
- }
1630
- };
1631
- function deepMerge(target, source) {
1632
- if (typeof target !== "object" || target === null) {
1633
- return source ?? target;
1634
- }
1635
- const result = { ...target };
1636
- for (const key of Object.keys(source)) {
1637
- const sourceVal = source[key];
1638
- const targetVal = target[key];
1639
- if (sourceVal !== void 0 && typeof sourceVal === "object" && sourceVal !== null && !Array.isArray(sourceVal) && typeof targetVal === "object" && targetVal !== null) {
1640
- result[key] = deepMerge(targetVal, sourceVal);
1641
- } else if (sourceVal !== void 0) {
1642
- result[key] = sourceVal;
1643
- }
1644
- }
1645
- return result;
1646
- }
1647
- const TranslationsContext = React.createContext(DEFAULT_TRANSLATIONS);
1648
- function useTranslations() {
1649
- return React.useContext(TranslationsContext);
1650
- }
1651
- function interpolate(template, params) {
1652
- return template.replace(
1653
- /\{\{(\w+)\}\}/g,
1654
- (_, key) => String(params[key] ?? `{{${key}}}`)
1655
- );
1656
- }
1657
- const TranslationsProvider = ({
1658
- translations,
1659
- children
1660
- }) => {
1661
- const merged = React.useMemo(
1662
- () => translations ? deepMerge(DEFAULT_TRANSLATIONS, translations) : DEFAULT_TRANSLATIONS,
1663
- [translations]
1664
- );
1665
- return /* @__PURE__ */ jsxRuntime.jsx(TranslationsContext.Provider, { value: merged, children });
1666
- };
1667
1729
  const EditorSidebar = ({
1668
1730
  title,
1669
1731
  subtitle,
@@ -1770,7 +1832,7 @@ const HistorySidebar = () => {
1770
1832
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
1771
1833
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
1772
1834
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `text-xs ${isCurrent ? "font-semibold text-blue-700" : "text-gray-900"}`, children: entry.label }),
1773
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 text-xs text-gray-400", children: entry.source })
1835
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 text-xs text-gray-400", children: t.regions[entry.source] ?? entry.source })
1774
1836
  ] }),
1775
1837
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 text-xs text-gray-400", children: formatTimestamp(entry.timestamp) })
1776
1838
  ] })
@@ -2515,6 +2577,19 @@ function mergeEditorStates(stateA, stateB) {
2515
2577
  if (all.length === 0) return null;
2516
2578
  return createEditorStateFromNodes(all);
2517
2579
  }
2580
+ function splitBlockNode(block, childOffset) {
2581
+ const children = block.children ?? [];
2582
+ if (childOffset <= 0) return [null, structuredClone(block)];
2583
+ if (childOffset >= children.length) return [structuredClone(block), null];
2584
+ const before = children.slice(0, childOffset);
2585
+ const after = children.slice(childOffset);
2586
+ const makeCopy = (kids) => {
2587
+ const copy = structuredClone(block);
2588
+ copy.children = kids;
2589
+ return copy;
2590
+ };
2591
+ return [makeCopy(before), makeCopy(after)];
2592
+ }
2518
2593
  function usePagination(document2, dispatch) {
2519
2594
  const reflowingRef = React.useRef(false);
2520
2595
  const pendingReflowRef = React.useRef(false);
@@ -2549,7 +2624,61 @@ function usePagination(document2, dispatch) {
2549
2624
  const footerH = document2.headerFooterEnabled ? page.footerHeight : 0;
2550
2625
  const bodyHeight = computeBodyHeight(headerH, footerH);
2551
2626
  const nodes = getTopLevelNodes(page.bodyState);
2552
- if (nodes.length <= 1) return;
2627
+ if (nodes.length <= 1) {
2628
+ if (nodes.length === 1) {
2629
+ const singleNode = nodes[0];
2630
+ const children = singleNode.children;
2631
+ if (children && children.length > 1) {
2632
+ const estimatedLineHeight = 24;
2633
+ const maxChildren = Math.max(1, Math.floor(bodyHeight / estimatedLineHeight));
2634
+ if (maxChildren < children.length) {
2635
+ const [keepBlock, overflowBlock] = splitBlockNode(singleNode, maxChildren);
2636
+ if (keepBlock && overflowBlock) {
2637
+ const keepState2 = createEditorStateFromNodes([keepBlock]);
2638
+ const overflowState2 = createEditorStateFromNodes([overflowBlock]);
2639
+ const nextPage2 = pages[pageIndex + 1];
2640
+ if (nextPage2) {
2641
+ const mergedBody = mergeEditorStates(overflowState2, nextPage2.bodyState);
2642
+ dispatch({
2643
+ type: "SET_DOCUMENT",
2644
+ document: {
2645
+ ...document2,
2646
+ pages: pages.map((p, i) => {
2647
+ if (i === pageIndex) return { ...p, bodyState: keepState2 };
2648
+ if (i === pageIndex + 1) return { ...p, bodyState: mergedBody };
2649
+ return p;
2650
+ })
2651
+ }
2652
+ });
2653
+ } else {
2654
+ const newPage = {
2655
+ ...createPageFromTemplate({
2656
+ headerState: document2.defaultHeaderState,
2657
+ footerState: document2.defaultFooterState,
2658
+ headerHeight: document2.defaultHeaderHeight,
2659
+ footerHeight: document2.defaultFooterHeight
2660
+ }),
2661
+ bodyState: overflowState2
2662
+ };
2663
+ dispatch({
2664
+ type: "SET_DOCUMENT",
2665
+ document: {
2666
+ ...document2,
2667
+ pages: [
2668
+ ...pages.map(
2669
+ (p, i) => i === pageIndex ? { ...p, bodyState: keepState2 } : p
2670
+ ),
2671
+ newPage
2672
+ ]
2673
+ }
2674
+ });
2675
+ }
2676
+ }
2677
+ }
2678
+ }
2679
+ }
2680
+ return;
2681
+ }
2553
2682
  const fitCount = estimateNodesFitting(page.bodyState, bodyHeight);
2554
2683
  if (fitCount >= nodes.length) return;
2555
2684
  const [keepState, overflowState] = splitEditorState(page.bodyState, fitCount);
@@ -2919,6 +3048,213 @@ const ActiveEditorPlugin = ({
2919
3048
  }, [editor, onFocus, pageId, region, setActiveEditor, setActivePageId]);
2920
3049
  return null;
2921
3050
  };
3051
+ function findMidBlockSplitPoint(blockElement, availableHeight) {
3052
+ const blockRect = blockElement.getBoundingClientRect();
3053
+ const maxBottom = blockRect.top + availableHeight;
3054
+ const textNodes = [];
3055
+ const walker = document.createTreeWalker(blockElement, NodeFilter.SHOW_TEXT);
3056
+ let walkNode;
3057
+ while (walkNode = walker.nextNode()) {
3058
+ if (walkNode.length > 0) {
3059
+ textNodes.push(walkNode);
3060
+ }
3061
+ }
3062
+ if (textNodes.length === 0) return null;
3063
+ const range = document.createRange();
3064
+ for (const textNode of textNodes) {
3065
+ range.selectNodeContents(textNode);
3066
+ const nodeRect = range.getBoundingClientRect();
3067
+ if (nodeRect.bottom <= maxBottom) {
3068
+ continue;
3069
+ }
3070
+ if (nodeRect.top >= maxBottom) {
3071
+ return backupToWordBoundary(textNode, 0);
3072
+ }
3073
+ let low = 0;
3074
+ let high = textNode.length;
3075
+ while (low < high) {
3076
+ const mid = Math.floor((low + high) / 2);
3077
+ range.setStart(textNode, 0);
3078
+ range.setEnd(textNode, mid + 1);
3079
+ const testRect = range.getBoundingClientRect();
3080
+ if (testRect.bottom > maxBottom) {
3081
+ high = mid;
3082
+ } else {
3083
+ low = mid + 1;
3084
+ }
3085
+ }
3086
+ return backupToWordBoundary(textNode, low);
3087
+ }
3088
+ return null;
3089
+ }
3090
+ function backupToWordBoundary(textNode, offset) {
3091
+ const text = textNode.textContent || "";
3092
+ if (offset <= 0 || offset >= text.length) {
3093
+ if (offset > 0 && offset < text.length) {
3094
+ return { textNode, offset };
3095
+ }
3096
+ if (offset > 0) return { textNode, offset };
3097
+ return null;
3098
+ }
3099
+ let adjusted = offset;
3100
+ while (adjusted > 0 && !/\s/.test(text[adjusted - 1])) {
3101
+ adjusted--;
3102
+ }
3103
+ if (adjusted <= 0) {
3104
+ adjusted = offset;
3105
+ }
3106
+ return { textNode, offset: adjusted };
3107
+ }
3108
+ function serializeNodeTree$1(node) {
3109
+ const json = node.exportJSON();
3110
+ if (lexical.$isElementNode(node)) {
3111
+ json.children = node.getChildren().map(serializeNodeTree$1);
3112
+ }
3113
+ return json;
3114
+ }
3115
+ function findListSplitIndex(listElement, availableHeight) {
3116
+ const listRect = listElement.getBoundingClientRect();
3117
+ const maxBottom = listRect.top + availableHeight;
3118
+ const children = Array.from(listElement.children);
3119
+ for (let i = 0; i < children.length; i++) {
3120
+ const child = children[i];
3121
+ const childRect = child.getBoundingClientRect();
3122
+ if (childRect.bottom > maxBottom && i > 0) {
3123
+ return i;
3124
+ }
3125
+ }
3126
+ return -1;
3127
+ }
3128
+ function performMidBlockSplit(rootElement, availableHeight, overflowBlockIndex) {
3129
+ const root = lexical.$getRoot();
3130
+ const allChildren = root.getChildren();
3131
+ const blockNode = allChildren[overflowBlockIndex];
3132
+ if (!blockNode || !lexical.$isElementNode(blockNode)) {
3133
+ debugWarn("overflow", "target node is not an ElementNode");
3134
+ return null;
3135
+ }
3136
+ const blockElements = Array.from(rootElement.children);
3137
+ const blockElement = blockElements[overflowBlockIndex];
3138
+ if (!blockElement) {
3139
+ debugWarn("overflow", "no DOM element for block index");
3140
+ return null;
3141
+ }
3142
+ const blockTop = blockElement.offsetTop;
3143
+ const heightForBlock = availableHeight - blockTop;
3144
+ if (heightForBlock <= 0) {
3145
+ debugWarn("overflow", "no space for block");
3146
+ return null;
3147
+ }
3148
+ if (list.$isListNode(blockNode)) {
3149
+ return splitListNode(blockNode, blockElement, heightForBlock);
3150
+ }
3151
+ return splitParagraphNode(blockNode, blockElement, heightForBlock);
3152
+ }
3153
+ function splitListNode(listNode, listElement, availableHeight) {
3154
+ const splitIndex = findListSplitIndex(listElement, availableHeight);
3155
+ if (splitIndex <= 0) {
3156
+ debug("overflow", "cannot split list — first item exceeds height");
3157
+ return null;
3158
+ }
3159
+ const [, overflowList] = lexical.$splitNode(listNode, splitIndex);
3160
+ const overflowNodes = [];
3161
+ const toRemove = [];
3162
+ overflowNodes.push(serializeNodeTree$1(overflowList));
3163
+ toRemove.push(overflowList);
3164
+ let nextSibling = overflowList.getNextSibling();
3165
+ while (nextSibling) {
3166
+ overflowNodes.push(serializeNodeTree$1(nextSibling));
3167
+ toRemove.push(nextSibling);
3168
+ nextSibling = nextSibling.getNextSibling();
3169
+ }
3170
+ for (const node of toRemove) {
3171
+ node.remove();
3172
+ }
3173
+ debug("overflow", `split list at item ${splitIndex}, extracted ${overflowNodes.length} overflow nodes`);
3174
+ return overflowNodes;
3175
+ }
3176
+ function splitParagraphNode(blockNode, blockElement, availableHeight) {
3177
+ const splitPoint = findMidBlockSplitPoint(blockElement, availableHeight);
3178
+ if (!splitPoint) {
3179
+ debug("overflow", "no valid split point found in paragraph");
3180
+ return null;
3181
+ }
3182
+ const lexicalNode = lexical.$getNearestNodeFromDOMNode(splitPoint.textNode);
3183
+ if (!lexicalNode || !lexical.$isTextNode(lexicalNode)) {
3184
+ debugWarn("overflow", "could not map DOM text node to Lexical TextNode");
3185
+ return null;
3186
+ }
3187
+ const [, afterText] = lexicalNode.splitText(splitPoint.offset);
3188
+ if (!afterText) {
3189
+ debugWarn("overflow", "splitText returned no second half");
3190
+ return null;
3191
+ }
3192
+ const parent = afterText.getParent();
3193
+ if (!parent || !lexical.$isElementNode(parent)) {
3194
+ debugWarn("overflow", "split text has no element parent");
3195
+ return null;
3196
+ }
3197
+ let directChild = afterText;
3198
+ let directChildParent = parent;
3199
+ while (directChildParent && directChildParent.getKey() !== blockNode.getKey()) {
3200
+ directChild = directChildParent;
3201
+ const next = directChildParent.getParent();
3202
+ if (!next || !lexical.$isElementNode(next)) {
3203
+ debugWarn("overflow", "could not find block node in parent chain");
3204
+ return null;
3205
+ }
3206
+ directChildParent = next;
3207
+ }
3208
+ const splitChildIndex = blockNode.getChildren().indexOf(directChild);
3209
+ if (splitChildIndex <= 0) {
3210
+ debug("overflow", "split index is 0 or not found — nothing to keep on this page");
3211
+ return null;
3212
+ }
3213
+ if (directChild !== afterText) {
3214
+ let current = parent;
3215
+ let childToSplitAt = afterText;
3216
+ while (current.getKey() !== blockNode.getKey()) {
3217
+ const childIdx = current.getChildren().indexOf(childToSplitAt);
3218
+ if (childIdx > 0) {
3219
+ lexical.$splitNode(current, childIdx);
3220
+ }
3221
+ childToSplitAt = current;
3222
+ const next = current.getParent();
3223
+ if (!next || !lexical.$isElementNode(next)) break;
3224
+ current = next;
3225
+ }
3226
+ }
3227
+ let finalSplitChild = afterText;
3228
+ let p = afterText.getParent();
3229
+ while (p && p.getKey() !== blockNode.getKey()) {
3230
+ finalSplitChild = p;
3231
+ p = p.getParent();
3232
+ }
3233
+ const finalSplitIndex = blockNode.getChildren().indexOf(finalSplitChild);
3234
+ if (finalSplitIndex <= 0) {
3235
+ debug("overflow", "final split index is 0 or not found — nothing to keep on this page");
3236
+ return null;
3237
+ }
3238
+ const [, overflowBlock] = lexical.$splitNode(blockNode, finalSplitIndex);
3239
+ const overflowNodes = [];
3240
+ const toRemove = [];
3241
+ overflowNodes.push(serializeNodeTree$1(overflowBlock));
3242
+ toRemove.push(overflowBlock);
3243
+ let nextSibling = overflowBlock.getNextSibling();
3244
+ while (nextSibling) {
3245
+ overflowNodes.push(serializeNodeTree$1(nextSibling));
3246
+ toRemove.push(nextSibling);
3247
+ nextSibling = nextSibling.getNextSibling();
3248
+ }
3249
+ for (const node of toRemove) {
3250
+ node.remove();
3251
+ }
3252
+ debug(
3253
+ "overflow",
3254
+ `split paragraph at offset ${splitPoint.offset}, extracted ${overflowNodes.length} overflow nodes`
3255
+ );
3256
+ return overflowNodes;
3257
+ }
2922
3258
  function serializeNodeTree(node) {
2923
3259
  const json = node.exportJSON();
2924
3260
  if (lexical.$isElementNode(node)) {
@@ -2947,18 +3283,51 @@ const OverflowPlugin = ({
2947
3283
  const children = Array.from(rootElement.children);
2948
3284
  if (children.length === 0) return;
2949
3285
  if (children.length <= 1) {
2950
- debug("overflow", `single block overflows (content=${contentHeight}px > available=${availableHeight}px) — cannot split`);
3286
+ debug("overflow", `single block overflows (content=${contentHeight}px > available=${availableHeight}px) — attempting mid-block split`);
3287
+ processingRef.current = true;
3288
+ editor.update(
3289
+ () => {
3290
+ const overflowNodes = performMidBlockSplit(rootElement, availableHeight, 0);
3291
+ if (!overflowNodes || overflowNodes.length === 0) {
3292
+ debug("overflow", "mid-block split not possible for single block");
3293
+ processingRef.current = false;
3294
+ return;
3295
+ }
3296
+ const overflowState = {
3297
+ root: {
3298
+ children: overflowNodes,
3299
+ direction: null,
3300
+ format: "",
3301
+ indent: 0,
3302
+ type: "root",
3303
+ version: 1
3304
+ }
3305
+ };
3306
+ debug("overflow", `mid-block split extracted ${overflowNodes.length} overflow nodes from single block`);
3307
+ setTimeout(() => {
3308
+ onOverflowRef.current(overflowState, cause);
3309
+ processingRef.current = false;
3310
+ }, 0);
3311
+ },
3312
+ { tag: "overflow-split" }
3313
+ );
2951
3314
  return;
2952
3315
  }
2953
3316
  processingRef.current = true;
2954
3317
  debug("overflow", `OVERFLOW detected: content=${contentHeight}px available=${availableHeight}px children=${children.length}`);
2955
3318
  let splitIndex = children.length;
3319
+ let firstBlockOverflows = false;
2956
3320
  for (let i = 0; i < children.length; i++) {
2957
3321
  const child = children[i];
2958
3322
  const childBottom = child.offsetTop + child.offsetHeight;
2959
- if (childBottom > availableHeight && i > 0) {
2960
- splitIndex = i;
2961
- debug("overflow", `split at index ${i} (childBottom=${childBottom}px > ${availableHeight}px)`);
3323
+ if (childBottom > availableHeight) {
3324
+ if (i === 0) {
3325
+ firstBlockOverflows = true;
3326
+ splitIndex = 1;
3327
+ } else {
3328
+ splitIndex = i;
3329
+ }
3330
+ debug("overflow", `split at index ${i === 0 ? "0 (mid-block)" : i} (childBottom=${childBottom}px > ${availableHeight}px)`);
2962
3331
  break;
2963
3332
  }
2964
3333
  }
@@ -2976,15 +3345,37 @@ const OverflowPlugin = ({
2976
3345
  processingRef.current = false;
2977
3346
  return;
2978
3347
  }
2979
- const overflowNodes = [];
2980
- const toRemove = allChildren.slice(splitIndex);
2981
- for (const node of toRemove) {
2982
- overflowNodes.push(serializeNodeTree(node));
3348
+ let overflowNodes = [];
3349
+ if (firstBlockOverflows) {
3350
+ const midBlockOverflow = performMidBlockSplit(rootElement, availableHeight, 0);
3351
+ if (midBlockOverflow && midBlockOverflow.length > 0) {
3352
+ overflowNodes.push(...midBlockOverflow);
3353
+ debug("overflow", `mid-block split on first block extracted ${midBlockOverflow.length} nodes`);
3354
+ } else {
3355
+ debug("overflow", "mid-block split failed on first block — keeping it whole");
3356
+ }
3357
+ const freshChildren = root.getChildren();
3358
+ const toRemove = freshChildren.slice(1);
3359
+ for (const node of toRemove) {
3360
+ overflowNodes.push(serializeNodeTree(node));
3361
+ }
3362
+ for (const node of toRemove) {
3363
+ node.remove();
3364
+ }
3365
+ } else {
3366
+ const toRemove = allChildren.slice(splitIndex);
3367
+ for (const node of toRemove) {
3368
+ overflowNodes.push(serializeNodeTree(node));
3369
+ }
3370
+ for (const node of toRemove) {
3371
+ node.remove();
3372
+ }
2983
3373
  }
2984
- for (const node of toRemove) {
2985
- node.remove();
3374
+ if (overflowNodes.length === 0) {
3375
+ processingRef.current = false;
3376
+ return;
2986
3377
  }
2987
- debug("overflow", `extracted ${overflowNodes.length} overflow nodes, kept ${splitIndex} nodes on current page`);
3378
+ debug("overflow", `extracted ${overflowNodes.length} overflow nodes total`);
2988
3379
  const overflowState = {
2989
3380
  root: {
2990
3381
  children: overflowNodes,
@@ -3065,16 +3456,10 @@ const OverflowPlugin = ({
3065
3456
  }, [editor]);
3066
3457
  return null;
3067
3458
  };
3068
- function createPageLabel(region, pageNumber) {
3069
- if (region === "body") {
3070
- return pageNumber ? `Page ${pageNumber}` : "Body";
3071
- }
3072
- const regionLabel = region.charAt(0).toUpperCase() + region.slice(1);
3073
- return pageNumber ? `${regionLabel} Page ${pageNumber}` : regionLabel;
3074
- }
3075
3459
  const HistoryCapturePlugin = ({ pageId, region }) => {
3076
3460
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
3077
3461
  const { document: document2, queueHistoryAction } = useDocument();
3462
+ const t = useTranslations();
3078
3463
  const context = React.useMemo(() => {
3079
3464
  const pageNumber = document2.pages.findIndex((page) => page.id === pageId);
3080
3465
  return {
@@ -3082,9 +3467,18 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
3082
3467
  source: region
3083
3468
  };
3084
3469
  }, [document2.pages, pageId, region]);
3470
+ const createPageLabel = React.useMemo(() => {
3471
+ return (r, pageNumber) => {
3472
+ if (r === "body") {
3473
+ return pageNumber ? interpolate(t.regions.page, { page: pageNumber }) : t.regions.body;
3474
+ }
3475
+ const regionLabel = r === "header" ? t.regions.header : t.regions.footer;
3476
+ return pageNumber ? `${regionLabel} ${interpolate(t.regions.page, { page: pageNumber })}` : regionLabel;
3477
+ };
3478
+ }, [t]);
3085
3479
  React.useEffect(() => {
3086
- const buildDescriptor = (prefix) => ({
3087
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3480
+ const buildDescriptor = (label) => ({
3481
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
3088
3482
  source: context.source,
3089
3483
  pageId,
3090
3484
  region
@@ -3092,15 +3486,15 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
3092
3486
  return editor.registerCommand(
3093
3487
  lexical.CONTROLLED_TEXT_INSERTION_COMMAND,
3094
3488
  () => {
3095
- queueHistoryAction(buildDescriptor("Typed text"));
3489
+ queueHistoryAction(buildDescriptor(t.historyLabels.typedText));
3096
3490
  return false;
3097
3491
  },
3098
3492
  lexical.COMMAND_PRIORITY_LOW
3099
3493
  );
3100
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3494
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
3101
3495
  React.useEffect(() => {
3102
- const buildDescriptor = (prefix) => ({
3103
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3496
+ const buildDescriptor = (label) => ({
3497
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
3104
3498
  source: context.source,
3105
3499
  pageId,
3106
3500
  region
@@ -3114,15 +3508,15 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
3114
3508
  if (event.key.length !== 1) {
3115
3509
  return false;
3116
3510
  }
3117
- queueHistoryAction(buildDescriptor("Typed text"));
3511
+ queueHistoryAction(buildDescriptor(t.historyLabels.typedText));
3118
3512
  return false;
3119
3513
  },
3120
3514
  lexical.COMMAND_PRIORITY_LOW
3121
3515
  );
3122
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3516
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
3123
3517
  React.useEffect(() => {
3124
- const buildDescriptor = (prefix) => ({
3125
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3518
+ const buildDescriptor = (label) => ({
3519
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
3126
3520
  source: context.source,
3127
3521
  pageId,
3128
3522
  region
@@ -3135,15 +3529,15 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
3135
3529
  if (text.trim().length === 0) {
3136
3530
  return false;
3137
3531
  }
3138
- queueHistoryAction(buildDescriptor("Pasted content"));
3532
+ queueHistoryAction(buildDescriptor(t.historyLabels.pastedContent));
3139
3533
  return false;
3140
3534
  },
3141
3535
  lexical.COMMAND_PRIORITY_LOW
3142
3536
  );
3143
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3537
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
3144
3538
  React.useEffect(() => {
3145
- const buildDescriptor = (prefix) => ({
3146
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3539
+ const buildDescriptor = (label) => ({
3540
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
3147
3541
  source: context.source,
3148
3542
  pageId,
3149
3543
  region
@@ -3151,15 +3545,15 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
3151
3545
  return editor.registerCommand(
3152
3546
  lexical.KEY_ENTER_COMMAND,
3153
3547
  () => {
3154
- queueHistoryAction(buildDescriptor("Inserted line break"));
3548
+ queueHistoryAction(buildDescriptor(t.historyLabels.insertedLineBreak));
3155
3549
  return false;
3156
3550
  },
3157
3551
  lexical.COMMAND_PRIORITY_LOW
3158
3552
  );
3159
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3553
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
3160
3554
  React.useEffect(() => {
3161
- const buildDescriptor = (prefix) => ({
3162
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3555
+ const buildDescriptor = (label) => ({
3556
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
3163
3557
  source: context.source,
3164
3558
  pageId,
3165
3559
  region
@@ -3167,15 +3561,15 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
3167
3561
  return editor.registerCommand(
3168
3562
  lexical.KEY_BACKSPACE_COMMAND,
3169
3563
  () => {
3170
- queueHistoryAction(buildDescriptor("Deleted backward"));
3564
+ queueHistoryAction(buildDescriptor(t.historyLabels.deletedBackward));
3171
3565
  return false;
3172
3566
  },
3173
3567
  lexical.COMMAND_PRIORITY_LOW
3174
3568
  );
3175
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3569
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
3176
3570
  React.useEffect(() => {
3177
- const buildDescriptor = (prefix) => ({
3178
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3571
+ const buildDescriptor = (label) => ({
3572
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
3179
3573
  source: context.source,
3180
3574
  pageId,
3181
3575
  region
@@ -3183,15 +3577,15 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
3183
3577
  return editor.registerCommand(
3184
3578
  lexical.KEY_DELETE_COMMAND,
3185
3579
  () => {
3186
- queueHistoryAction(buildDescriptor("Deleted forward"));
3580
+ queueHistoryAction(buildDescriptor(t.historyLabels.deletedForward));
3187
3581
  return false;
3188
3582
  },
3189
3583
  lexical.COMMAND_PRIORITY_LOW
3190
3584
  );
3191
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3585
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
3192
3586
  React.useEffect(() => {
3193
- const buildDescriptor = (prefix) => ({
3194
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3587
+ const buildDescriptor = (label) => ({
3588
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
3195
3589
  source: context.source,
3196
3590
  pageId,
3197
3591
  region
@@ -3199,15 +3593,15 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
3199
3593
  return editor.registerCommand(
3200
3594
  lexical.FORMAT_TEXT_COMMAND,
3201
3595
  () => {
3202
- queueHistoryAction(buildDescriptor("Formatted text"));
3596
+ queueHistoryAction(buildDescriptor(t.historyLabels.formattedText));
3203
3597
  return false;
3204
3598
  },
3205
3599
  lexical.COMMAND_PRIORITY_LOW
3206
3600
  );
3207
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3601
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
3208
3602
  React.useEffect(() => {
3209
- const buildDescriptor = (prefix) => ({
3210
- label: `${prefix} - ${createPageLabel(region, context.pageNumber)}`,
3603
+ const buildDescriptor = (label) => ({
3604
+ label: `${label} - ${createPageLabel(region, context.pageNumber)}`,
3211
3605
  source: context.source,
3212
3606
  pageId,
3213
3607
  region
@@ -3215,12 +3609,12 @@ const HistoryCapturePlugin = ({ pageId, region }) => {
3215
3609
  return editor.registerCommand(
3216
3610
  lexical.FORMAT_ELEMENT_COMMAND,
3217
3611
  () => {
3218
- queueHistoryAction(buildDescriptor("Formatted paragraph"));
3612
+ queueHistoryAction(buildDescriptor(t.historyLabels.formattedParagraph));
3219
3613
  return false;
3220
3614
  },
3221
3615
  lexical.COMMAND_PRIORITY_LOW
3222
3616
  );
3223
- }, [context.pageNumber, context.source, editor, pageId, queueHistoryAction, region]);
3617
+ }, [context.pageNumber, context.source, createPageLabel, editor, pageId, queueHistoryAction, region, t]);
3224
3618
  return null;
3225
3619
  };
3226
3620
  function getCollapsedTextPosition(rootElement) {
@@ -3633,10 +4027,14 @@ const PageView = React.memo(({
3633
4027
  onMoveToNextPage
3634
4028
  }) => {
3635
4029
  const { document: document2, dispatch, setActivePageId } = useDocument();
4030
+ const t = useTranslations();
3636
4031
  const page = document2.pages.find((p) => p.id === pageId);
3637
4032
  const showHeaderFooter = document2.headerFooterEnabled;
3638
4033
  const pageCounterMode = document2.pageCounterMode;
3639
- const pageCounterLabel = `Page ${pageIndex + 1} of ${document2.pages.length}`;
4034
+ const pageCounterLabel = interpolate(t.pageCounter.format, {
4035
+ current: pageIndex + 1,
4036
+ total: document2.pages.length
4037
+ });
3640
4038
  if (!page) return null;
3641
4039
  const handleBodyChange = React.useCallback(
3642
4040
  (bodyState) => {
@@ -3727,6 +4125,7 @@ const DocumentView = () => {
3727
4125
  setActiveEditor,
3728
4126
  setActivePageId
3729
4127
  } = useDocument();
4128
+ const t = useTranslations();
3730
4129
  const { handlePageUnderflow, reflowAll } = usePagination(document2, dispatch);
3731
4130
  const previousBodyHeightsRef = React.useRef(null);
3732
4131
  const pasteOverflowSequenceRef = React.useRef(false);
@@ -3891,7 +4290,7 @@ const DocumentView = () => {
3891
4290
  }
3892
4291
  runHistoryAction(
3893
4292
  {
3894
- label: `Deleted backward - Page ${pageIndex + 1}`,
4293
+ label: `${t.historyLabels.deletedBackward} - ${interpolate(t.regions.page, { page: pageIndex + 1 })}`,
3895
4294
  source: "body",
3896
4295
  pageId,
3897
4296
  region: "body"
@@ -3913,7 +4312,7 @@ const DocumentView = () => {
3913
4312
  }
3914
4313
  runHistoryAction(
3915
4314
  {
3916
- label: `Deleted forward - Page ${pageIndex + 1}`,
4315
+ label: `${t.historyLabels.deletedForward} - ${interpolate(t.regions.page, { page: pageIndex + 1 })}`,
3917
4316
  source: "body",
3918
4317
  pageId,
3919
4318
  region: "body"
@@ -4146,7 +4545,7 @@ const EditorChrome = ({
4146
4545
  }
4147
4546
  ),
4148
4547
  /* @__PURE__ */ jsxRuntime.jsx(Toolbar, {}),
4149
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden bg-gray-700", children: [
4548
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden bg-gray-200", children: [
4150
4549
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1 overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsx(DocumentView, {}) }),
4151
4550
  sidePanels.map((Panel, idx) => /* @__PURE__ */ jsxRuntime.jsx(Panel, {}, idx)),
4152
4551
  /* @__PURE__ */ jsxRuntime.jsx(HistorySidebar, {})