@a3s-lab/office 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -5
- package/dist/0~4595.js +117 -11
- package/dist/0~5050.js +124 -113
- package/dist/0~9512.js +1 -0
- package/dist/0~document-editor.js +2836 -1429
- package/dist/0~work-document-extensions.js +2 -2
- package/dist/0~work-docx-import.js +88 -20
- package/dist/internal/design-system/primitives/navigation/tabs.d.ts +3 -1
- package/dist/internal/features/work/editors/document-command-catalog.d.ts +527 -0
- package/dist/internal/features/work/editors/document-editor-support.d.ts +7 -3
- package/dist/internal/features/work/editors/document-editor.d.ts +1 -1
- package/dist/internal/features/work/editors/document-format-clipboard.d.ts +6 -0
- package/dist/internal/features/work/editors/document-format-tools.d.ts +4 -0
- package/dist/internal/features/work/editors/document-layout-panel.d.ts +4 -1
- package/dist/internal/features/work/editors/document-page-chrome-wps-shortcuts.d.ts +2 -0
- package/dist/internal/features/work/editors/document-page-layout-ribbon.d.ts +14 -0
- package/dist/internal/features/work/editors/document-review-navigation.d.ts +9 -0
- package/dist/internal/features/work/editors/document-statistics-dialog.d.ts +7 -0
- package/dist/internal/features/work/editors/document-status-bar.d.ts +2 -1
- package/dist/internal/features/work/editors/document-toolbar.d.ts +12 -42
- package/dist/internal/features/work/editors/document-wps-shortcuts.d.ts +12 -0
- package/dist/internal/features/work/editors/document-zoom.d.ts +17 -0
- package/dist/internal/features/work/editors/work-office-chrome.d.ts +22 -3
- package/dist/internal/features/work/editors/work-office-ribbon-overflow.d.ts +2 -0
- package/dist/office-kernel.wasm +0 -0
- package/dist/styles.css +213 -1
- package/docs/latest/en/browser-editor-architecture.md +33 -0
- package/package.json +11 -1
package/README.md
CHANGED
|
@@ -255,7 +255,19 @@ interaction model.
|
|
|
255
255
|
long-document windows for page buttons, heading rows, full-text results, and
|
|
256
256
|
tracked-revision and anchored-comment review that preserve native scroll
|
|
257
257
|
distance, sparse current/selection/draft pins, and Home/End reachability;
|
|
258
|
-
|
|
258
|
+
a WPS-familiar command catalog, quick-access undo and redo, a priority-aware
|
|
259
|
+
adaptive and persistently collapsible ribbon whose tabs can temporarily
|
|
260
|
+
expose commands; WPS-oriented Insert groups and direct Page Layout presets
|
|
261
|
+
for margins, orientation, paper size, and columns, with matching advanced
|
|
262
|
+
Page Setup destinations; WPS-oriented References, Review, and View groups,
|
|
263
|
+
direct tracked-change navigation and decisions, contextual object tabs, and
|
|
264
|
+
viewport-calculated 100%, one-page, and page-width zoom; an actionable status
|
|
265
|
+
bar with live word-count details, WPS `Ctrl+Shift+G`, keyboard-traversable
|
|
266
|
+
view and zoom controls, and compact Web prioritization; editor-scoped WPS
|
|
267
|
+
formatting, paragraph, style, and review shortcuts, plus a permission-free
|
|
268
|
+
formatting clipboard and one-shot format painter shared with compatible
|
|
269
|
+
header and footer formatting, paragraph styles and phone-sized
|
|
270
|
+
paragraph-spacing and pagination controls
|
|
259
271
|
with touch targets and exact invoker-focus restoration; stable compact-ribbon
|
|
260
272
|
edge paging and viewport-bounded list galleries with touch-sized numbering
|
|
261
273
|
actions, selection-preserving bullet and numbering commands, and explicit
|
|
@@ -307,10 +319,13 @@ interaction model.
|
|
|
307
319
|
gaps, centered fixed table, row heights, borders, fills, and cell margins
|
|
308
320
|
stay within the checked one-pixel layout-landmark budget while preserving
|
|
309
321
|
the original OOXML line multiples for DOCX export.
|
|
310
|
-
Separate 30-row common-font, 36-row CJK-font,
|
|
311
|
-
matrices now gate text-band positions against
|
|
312
|
-
|
|
313
|
-
|
|
322
|
+
Separate 30-row common-font, 36-row CJK-font, 18-row document-grid, and
|
|
323
|
+
30-row multilingual-script matrices now gate text-band positions against
|
|
324
|
+
WPS. DOCX import selects the Word `ascii`, `hAnsi`, `eastAsia`, or `cs` font
|
|
325
|
+
slot from the run script, including complex-script bold, italic, size, and
|
|
326
|
+
RTL overrides. The browser keeps the measured per-font automatic-line
|
|
327
|
+
advance, while DOCX round trips retain the section `docGrid` type and line
|
|
328
|
+
pitch and each run's `snapToGrid` override.
|
|
314
329
|
_DOCX import/export; PDF export._
|
|
315
330
|
- **Markdown** — GFM source, visual editing, synchronized and resizable split
|
|
316
331
|
preview, source-native undo/redo with typing coalescing and selection restore,
|
|
@@ -520,6 +535,7 @@ without hard-coded return URLs.
|
|
|
520
535
|
|
|
521
536
|
- [Live Playground](https://a3s-lab.github.io/Office/)
|
|
522
537
|
- [Documentation center](https://a3s-lab.github.io/Office/docs/)
|
|
538
|
+
- [A3S Office 0.3.0 documentation](https://a3s-lab.github.io/Office/docs/0.3.0/)
|
|
523
539
|
- [A3S Office 0.2.0 documentation](https://a3s-lab.github.io/Office/docs/0.2.0/)
|
|
524
540
|
- [A3S Office 0.1.0 documentation](https://a3s-lab.github.io/Office/docs/0.1.0/)
|
|
525
541
|
- [React, Vue, and Web Component integration](https://a3s-lab.github.io/Office/docs/components/)
|
package/dist/0~4595.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Fragment as jsx_runtime_Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Fragment, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
|
-
import { Check, ChevronDown, ChevronLeft, ChevronRight, Eye, Minus, Plus, X } from "lucide-react";
|
|
4
|
+
import { Check, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Eye, Minus, Plus, X } from "lucide-react";
|
|
5
5
|
import { moveOfficeMenuFocus, useDialogFocusScope, icon_button_IconButton, Popover, matchesAriaKeyShortcuts, officeOverlayPortalRoot } from "./0~9512.js";
|
|
6
6
|
const CONTEXT_MENU_MARGIN = 8;
|
|
7
7
|
const CONTEXT_MENU_TAB_STOP_SELECTOR = 'a[href]:not([tabindex="-1"]), button:not(:disabled):not([tabindex="-1"]), input:not(:disabled):not([type="hidden"]):not([tabindex="-1"]), select:not(:disabled):not([tabindex="-1"]), textarea:not(:disabled):not([tabindex="-1"]), [contenteditable="true"]:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])';
|
|
@@ -542,7 +542,7 @@ function useTabNavigation({ items, onChange }) {
|
|
|
542
542
|
setTabElement
|
|
543
543
|
};
|
|
544
544
|
}
|
|
545
|
-
function Tabs({ ariaLabel, value, items, onChange, variant = 'segment', size = 'standard', className = '', containerRef }) {
|
|
545
|
+
function Tabs({ ariaLabel, value, items, onChange, onTabDoubleClick, variant = 'segment', size = 'standard', className = '', containerRef }) {
|
|
546
546
|
const tabNavigation = useTabNavigation({
|
|
547
547
|
items,
|
|
548
548
|
onChange
|
|
@@ -562,12 +562,14 @@ function Tabs({ ariaLabel, value, items, onChange, variant = 'segment', size = '
|
|
|
562
562
|
role: "tab",
|
|
563
563
|
id: item.tabId,
|
|
564
564
|
"data-tab-id": item.id,
|
|
565
|
+
"data-contextual": item.contextual ? 'true' : void 0,
|
|
565
566
|
"aria-label": item.label,
|
|
566
567
|
"aria-selected": selected,
|
|
567
568
|
"aria-controls": item.panelId,
|
|
568
569
|
tabIndex: selected ? 0 : -1,
|
|
569
570
|
disabled: item.disabled,
|
|
570
571
|
onClick: ()=>onChange(item.id),
|
|
572
|
+
onDoubleClick: ()=>onTabDoubleClick?.(item.id),
|
|
571
573
|
onKeyDown: (event)=>tabNavigation.handleTabKeyDown(event, item.id),
|
|
572
574
|
children: [
|
|
573
575
|
item.icon && /*#__PURE__*/ jsx("span", {
|
|
@@ -663,6 +665,11 @@ function roundSliderValue(value, step) {
|
|
|
663
665
|
return Number((Math.round(value / step) * step).toFixed(decimalPlaces));
|
|
664
666
|
}
|
|
665
667
|
const WORK_OFFICE_RIBBON_SCROLL_INSET = 34;
|
|
668
|
+
function calculateRibbonDensity(clientWidth) {
|
|
669
|
+
if (clientWidth <= 0 || clientWidth >= 1120) return 'comfortable';
|
|
670
|
+
if (clientWidth >= 760) return 'compact-low';
|
|
671
|
+
return 'compact-normal';
|
|
672
|
+
}
|
|
666
673
|
function calculateRibbonOverflow({ clientWidth, items, scrollLeft }) {
|
|
667
674
|
const lastItem = items.at(-1);
|
|
668
675
|
if (!lastItem || clientWidth <= 0) return {
|
|
@@ -689,9 +696,12 @@ function calculateRibbonScrollTarget({ clientWidth, direction, items, scrollLeft
|
|
|
689
696
|
if (!target) return scrollLeft;
|
|
690
697
|
return Math.max(0, target.left - WORK_OFFICE_RIBBON_SCROLL_INSET);
|
|
691
698
|
}
|
|
692
|
-
function WorkOfficeRibbon({ ariaLabel, tabs, defaultTab, activeTab, onTabChange, panels, fileActions, className = '', toolbarClassName = '' }) {
|
|
699
|
+
function WorkOfficeRibbon({ ariaLabel, tabs, defaultTab, activeTab, onTabChange, panels, fileActions, quickAccessActions, adaptive = false, collapsible = false, collapsed: controlledCollapsed, defaultCollapsed = false, onCollapsedChange, className = '', toolbarClassName = '' }) {
|
|
693
700
|
const reactId = useId().replaceAll(':', '');
|
|
694
701
|
const [internalTab, setInternalTab] = useState(defaultTab);
|
|
702
|
+
const [internalCollapsed, setInternalCollapsed] = useState(defaultCollapsed);
|
|
703
|
+
const [temporarilyExpanded, setTemporarilyExpanded] = useState(false);
|
|
704
|
+
const [ribbonDensity, setRibbonDensity] = useState('comfortable');
|
|
695
705
|
const [ribbonOverflow, setRibbonOverflow] = useState({
|
|
696
706
|
backward: false,
|
|
697
707
|
forward: false
|
|
@@ -702,14 +712,20 @@ function WorkOfficeRibbon({ ariaLabel, tabs, defaultTab, activeTab, onTabChange,
|
|
|
702
712
|
});
|
|
703
713
|
const tabListRef = useRef(null);
|
|
704
714
|
const toolbarRef = useRef(null);
|
|
715
|
+
const ribbonRef = useRef(null);
|
|
705
716
|
const selectedTab = activeTab ?? internalTab;
|
|
706
717
|
const selectedLabel = tabs.find((tab)=>tab.id === selectedTab)?.label ?? tabs[0]?.label ?? '';
|
|
718
|
+
const contextualTabActive = Boolean(tabs.find((tab)=>tab.id === selectedTab)?.contextual);
|
|
707
719
|
const selectedPanel = panels[selectedTab];
|
|
708
720
|
const hasSelectedPanel = null != selectedPanel;
|
|
721
|
+
const collapsed = collapsible && (controlledCollapsed ?? internalCollapsed);
|
|
722
|
+
const panelExpanded = hasSelectedPanel && (!collapsed || temporarilyExpanded);
|
|
709
723
|
const hasRibbonOverflow = ribbonOverflow.backward || ribbonOverflow.forward;
|
|
710
724
|
const updateRibbonOverflow = useCallback(()=>{
|
|
711
725
|
const toolbar = toolbarRef.current;
|
|
712
726
|
if (!toolbar) return;
|
|
727
|
+
const nextDensity = adaptive ? calculateRibbonDensity(toolbar.clientWidth) : 'comfortable';
|
|
728
|
+
setRibbonDensity((current)=>current === nextDensity ? current : nextDensity);
|
|
713
729
|
const { backward, forward } = calculateRibbonOverflow({
|
|
714
730
|
clientWidth: toolbar.clientWidth,
|
|
715
731
|
items: ribbonItemGeometry(toolbar),
|
|
@@ -720,7 +736,9 @@ function WorkOfficeRibbon({ ariaLabel, tabs, defaultTab, activeTab, onTabChange,
|
|
|
720
736
|
backward,
|
|
721
737
|
forward
|
|
722
738
|
});
|
|
723
|
-
}, [
|
|
739
|
+
}, [
|
|
740
|
+
adaptive
|
|
741
|
+
]);
|
|
724
742
|
const scrollRibbon = (direction)=>{
|
|
725
743
|
const toolbar = toolbarRef.current;
|
|
726
744
|
if (!toolbar) return;
|
|
@@ -759,8 +777,29 @@ function WorkOfficeRibbon({ ariaLabel, tabs, defaultTab, activeTab, onTabChange,
|
|
|
759
777
|
};
|
|
760
778
|
const selectTab = (tab)=>{
|
|
761
779
|
if (void 0 === activeTab) setInternalTab(tab);
|
|
780
|
+
if (collapsed) setTemporarilyExpanded(true);
|
|
762
781
|
onTabChange?.(tab);
|
|
763
782
|
};
|
|
783
|
+
const changeCollapsed = useCallback((nextCollapsed)=>{
|
|
784
|
+
if (void 0 === controlledCollapsed) setInternalCollapsed(nextCollapsed);
|
|
785
|
+
setTemporarilyExpanded(false);
|
|
786
|
+
onCollapsedChange?.(nextCollapsed);
|
|
787
|
+
}, [
|
|
788
|
+
controlledCollapsed,
|
|
789
|
+
onCollapsedChange
|
|
790
|
+
]);
|
|
791
|
+
useEffect(()=>{
|
|
792
|
+
if (!collapsed || !temporarilyExpanded) return;
|
|
793
|
+
const closeTemporaryPanel = (event)=>{
|
|
794
|
+
if (event.target instanceof Node && ribbonRef.current?.contains(event.target)) return;
|
|
795
|
+
setTemporarilyExpanded(false);
|
|
796
|
+
};
|
|
797
|
+
document.addEventListener('pointerdown', closeTemporaryPanel, true);
|
|
798
|
+
return ()=>document.removeEventListener('pointerdown', closeTemporaryPanel, true);
|
|
799
|
+
}, [
|
|
800
|
+
collapsed,
|
|
801
|
+
temporarilyExpanded
|
|
802
|
+
]);
|
|
764
803
|
useLayoutEffect(()=>{
|
|
765
804
|
const toolbar = toolbarRef.current;
|
|
766
805
|
if (!toolbar) return;
|
|
@@ -768,12 +807,12 @@ function WorkOfficeRibbon({ ariaLabel, tabs, defaultTab, activeTab, onTabChange,
|
|
|
768
807
|
updateRibbonOverflow();
|
|
769
808
|
}, [
|
|
770
809
|
selectedTab,
|
|
771
|
-
|
|
810
|
+
panelExpanded,
|
|
772
811
|
updateRibbonOverflow
|
|
773
812
|
]);
|
|
774
813
|
useLayoutEffect(()=>{
|
|
775
814
|
const toolbar = toolbarRef.current;
|
|
776
|
-
if (!toolbar) return;
|
|
815
|
+
if (!toolbar || !panelExpanded) return;
|
|
777
816
|
let frame = requestAnimationFrame(updateRibbonOverflow);
|
|
778
817
|
const scheduleOverflowUpdate = ()=>{
|
|
779
818
|
cancelAnimationFrame(frame);
|
|
@@ -803,7 +842,7 @@ function WorkOfficeRibbon({ ariaLabel, tabs, defaultTab, activeTab, onTabChange,
|
|
|
803
842
|
window.removeEventListener('resize', scheduleOverflowUpdate);
|
|
804
843
|
};
|
|
805
844
|
}, [
|
|
806
|
-
|
|
845
|
+
panelExpanded,
|
|
807
846
|
updateRibbonOverflow
|
|
808
847
|
]);
|
|
809
848
|
useLayoutEffect(()=>{
|
|
@@ -848,8 +887,11 @@ function WorkOfficeRibbon({ ariaLabel, tabs, defaultTab, activeTab, onTabChange,
|
|
|
848
887
|
updateTabOverflow
|
|
849
888
|
]);
|
|
850
889
|
return /*#__PURE__*/ jsxs("section", {
|
|
890
|
+
ref: ribbonRef,
|
|
851
891
|
className: `work-office-ribbon ${className}`.trim(),
|
|
852
892
|
"aria-label": ariaLabel,
|
|
893
|
+
"data-collapsed": collapsed ? 'true' : void 0,
|
|
894
|
+
"data-contextual-active": contextualTabActive ? 'true' : void 0,
|
|
853
895
|
children: [
|
|
854
896
|
/*#__PURE__*/ jsxs("div", {
|
|
855
897
|
className: "work-office-ribbon-tabs-row",
|
|
@@ -857,6 +899,9 @@ function WorkOfficeRibbon({ ariaLabel, tabs, defaultTab, activeTab, onTabChange,
|
|
|
857
899
|
fileActions?.length ? /*#__PURE__*/ jsx(WorkOfficeFileMenu, {
|
|
858
900
|
actions: fileActions
|
|
859
901
|
}) : null,
|
|
902
|
+
quickAccessActions?.length ? /*#__PURE__*/ jsx(WorkOfficeQuickAccessToolbar, {
|
|
903
|
+
actions: quickAccessActions
|
|
904
|
+
}) : null,
|
|
860
905
|
/*#__PURE__*/ jsxs("div", {
|
|
861
906
|
className: "work-office-ribbon-tab-strip",
|
|
862
907
|
children: [
|
|
@@ -872,7 +917,10 @@ function WorkOfficeRibbon({ ariaLabel, tabs, defaultTab, activeTab, onTabChange,
|
|
|
872
917
|
tabId: `${reactId}-tab-${tab.id}`,
|
|
873
918
|
panelId: `${reactId}-panel`
|
|
874
919
|
})),
|
|
875
|
-
onChange: selectTab
|
|
920
|
+
onChange: selectTab,
|
|
921
|
+
onTabDoubleClick: (tab)=>{
|
|
922
|
+
if (collapsible && tab === selectedTab) changeCollapsed(!collapsed);
|
|
923
|
+
}
|
|
876
924
|
}),
|
|
877
925
|
(tabOverflow.backward || tabOverflow.forward) && /*#__PURE__*/ jsxs("nav", {
|
|
878
926
|
className: "work-office-ribbon-tab-navigation",
|
|
@@ -899,6 +947,22 @@ function WorkOfficeRibbon({ ariaLabel, tabs, defaultTab, activeTab, onTabChange,
|
|
|
899
947
|
]
|
|
900
948
|
})
|
|
901
949
|
]
|
|
950
|
+
}),
|
|
951
|
+
collapsible && /*#__PURE__*/ jsx("button", {
|
|
952
|
+
type: "button",
|
|
953
|
+
className: "work-office-ribbon-collapse",
|
|
954
|
+
"aria-label": collapsed ? '展开功能区' : '折叠功能区',
|
|
955
|
+
title: collapsed ? '展开功能区' : '折叠功能区',
|
|
956
|
+
"aria-controls": `${reactId}-panel`,
|
|
957
|
+
"aria-expanded": panelExpanded,
|
|
958
|
+
onClick: ()=>changeCollapsed(!collapsed),
|
|
959
|
+
children: collapsed ? /*#__PURE__*/ jsx(ChevronDown, {
|
|
960
|
+
size: 14,
|
|
961
|
+
"aria-hidden": "true"
|
|
962
|
+
}) : /*#__PURE__*/ jsx(ChevronUp, {
|
|
963
|
+
size: 14,
|
|
964
|
+
"aria-hidden": "true"
|
|
965
|
+
})
|
|
902
966
|
})
|
|
903
967
|
]
|
|
904
968
|
}),
|
|
@@ -906,6 +970,7 @@ function WorkOfficeRibbon({ ariaLabel, tabs, defaultTab, activeTab, onTabChange,
|
|
|
906
970
|
id: `${reactId}-panel`,
|
|
907
971
|
className: "work-office-ribbon-panel",
|
|
908
972
|
"data-empty": hasSelectedPanel ? void 0 : 'true',
|
|
973
|
+
hidden: !panelExpanded,
|
|
909
974
|
role: "tabpanel",
|
|
910
975
|
"aria-labelledby": `${reactId}-tab-${selectedTab}`,
|
|
911
976
|
children: hasSelectedPanel && /*#__PURE__*/ jsxs(jsx_runtime_Fragment, {
|
|
@@ -923,6 +988,7 @@ function WorkOfficeRibbon({ ariaLabel, tabs, defaultTab, activeTab, onTabChange,
|
|
|
923
988
|
/*#__PURE__*/ jsx("div", {
|
|
924
989
|
ref: toolbarRef,
|
|
925
990
|
className: `work-office-toolbar ${toolbarClassName}`.trim(),
|
|
991
|
+
"data-density": ribbonDensity,
|
|
926
992
|
"data-has-overflow": hasRibbonOverflow ? 'true' : void 0,
|
|
927
993
|
role: "toolbar",
|
|
928
994
|
"aria-label": `${selectedLabel}工具栏`,
|
|
@@ -945,6 +1011,25 @@ function WorkOfficeRibbon({ ariaLabel, tabs, defaultTab, activeTab, onTabChange,
|
|
|
945
1011
|
]
|
|
946
1012
|
});
|
|
947
1013
|
}
|
|
1014
|
+
function WorkOfficeQuickAccessToolbar({ actions }) {
|
|
1015
|
+
return /*#__PURE__*/ jsx("div", {
|
|
1016
|
+
className: "work-office-quick-access",
|
|
1017
|
+
role: "toolbar",
|
|
1018
|
+
"aria-label": "快速访问工具栏",
|
|
1019
|
+
children: actions.map((action)=>/*#__PURE__*/ jsx("button", {
|
|
1020
|
+
type: "button",
|
|
1021
|
+
"aria-label": action.label,
|
|
1022
|
+
"aria-keyshortcuts": action.ariaKeyShortcuts,
|
|
1023
|
+
title: action.shortcut ? `${action.label}(${action.shortcut})` : action.label,
|
|
1024
|
+
disabled: action.disabled,
|
|
1025
|
+
onClick: ()=>void action.onSelect(),
|
|
1026
|
+
children: /*#__PURE__*/ jsx("span", {
|
|
1027
|
+
"aria-hidden": "true",
|
|
1028
|
+
children: action.icon
|
|
1029
|
+
})
|
|
1030
|
+
}, action.id))
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
948
1033
|
function ribbonItemGeometry(toolbar) {
|
|
949
1034
|
return Array.from(toolbar.children).flatMap((element)=>element instanceof HTMLElement ? [
|
|
950
1035
|
ribbonItemBounds(toolbar, element)
|
|
@@ -1073,10 +1158,11 @@ function WorkOfficeFileMenu({ actions }) {
|
|
|
1073
1158
|
})
|
|
1074
1159
|
});
|
|
1075
1160
|
}
|
|
1076
|
-
function WorkOfficeRibbonGroup({ label, children }) {
|
|
1161
|
+
function WorkOfficeRibbonGroup({ label, priority = 'normal', children }) {
|
|
1077
1162
|
return /*#__PURE__*/ jsxs("section", {
|
|
1078
1163
|
className: "work-office-ribbon-group",
|
|
1079
1164
|
"aria-label": label,
|
|
1165
|
+
"data-priority": priority,
|
|
1080
1166
|
children: [
|
|
1081
1167
|
/*#__PURE__*/ jsx("div", {
|
|
1082
1168
|
children: children
|
|
@@ -1108,9 +1194,10 @@ function WorkOfficeRibbonButton({ label, visibleLabel = label, badge, active, di
|
|
|
1108
1194
|
]
|
|
1109
1195
|
});
|
|
1110
1196
|
}
|
|
1111
|
-
function WorkOfficeStatusBar({ children, controls, className = '' }) {
|
|
1112
|
-
return /*#__PURE__*/ jsxs("
|
|
1197
|
+
function WorkOfficeStatusBar({ ariaLabel = '编辑器状态栏', children, controls, controlsLabel = '视图与缩放', className = '' }) {
|
|
1198
|
+
return /*#__PURE__*/ jsxs("section", {
|
|
1113
1199
|
className: `work-office-status ${className}`.trim(),
|
|
1200
|
+
"aria-label": ariaLabel,
|
|
1114
1201
|
children: [
|
|
1115
1202
|
/*#__PURE__*/ jsx("div", {
|
|
1116
1203
|
className: "work-office-status-info",
|
|
@@ -1118,11 +1205,30 @@ function WorkOfficeStatusBar({ children, controls, className = '' }) {
|
|
|
1118
1205
|
}),
|
|
1119
1206
|
controls && /*#__PURE__*/ jsx("div", {
|
|
1120
1207
|
className: "work-office-status-view",
|
|
1208
|
+
role: "toolbar",
|
|
1209
|
+
"aria-label": controlsLabel,
|
|
1210
|
+
onKeyDown: moveOfficeToolbarFocus,
|
|
1121
1211
|
children: controls
|
|
1122
1212
|
})
|
|
1123
1213
|
]
|
|
1124
1214
|
});
|
|
1125
1215
|
}
|
|
1216
|
+
function moveOfficeToolbarFocus(event) {
|
|
1217
|
+
if (![
|
|
1218
|
+
'ArrowLeft',
|
|
1219
|
+
'ArrowRight',
|
|
1220
|
+
'Home',
|
|
1221
|
+
'End'
|
|
1222
|
+
].includes(event.key) || event.target instanceof HTMLElement && 'slider' === event.target.getAttribute('role')) return;
|
|
1223
|
+
const controls = [
|
|
1224
|
+
...event.currentTarget.querySelectorAll('button:not(:disabled), [role="slider"]:not([aria-disabled="true"])')
|
|
1225
|
+
];
|
|
1226
|
+
if (!controls.length) return;
|
|
1227
|
+
const currentIndex = controls.indexOf(document.activeElement);
|
|
1228
|
+
const nextIndex = 'Home' === event.key ? 0 : 'End' === event.key ? controls.length - 1 : 'ArrowRight' === event.key ? (currentIndex + 1 + controls.length) % controls.length : (currentIndex - 1 + controls.length) % controls.length;
|
|
1229
|
+
event.preventDefault();
|
|
1230
|
+
controls[nextIndex]?.focus();
|
|
1231
|
+
}
|
|
1126
1232
|
function WorkOfficeZoomControls({ zoom, minimum = 50, maximum = 200, step = 10, decreaseLabel, increaseLabel, outputLabel, sliderLabel, onChange }) {
|
|
1127
1233
|
const clamp = (value)=>Math.min(maximum, Math.max(minimum, Math.round(value)));
|
|
1128
1234
|
return /*#__PURE__*/ jsxs(jsx_runtime_Fragment, {
|
package/dist/0~5050.js
CHANGED
|
@@ -1,8 +1,114 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Copy, Languages, MessageSquareText, Sparkles, TextQuote, WandSparkles } from "lucide-react";
|
|
3
3
|
import { showToast, createAgentProposalRequest as createWorkAgentProposalRequest } from "./6489.js";
|
|
4
|
-
const
|
|
5
|
-
|
|
4
|
+
const SURROUNDING_TEXT_LIMIT = 2000;
|
|
5
|
+
function createWorkDocumentSelectionSnapshot(editor, content) {
|
|
6
|
+
const { from, to, empty } = editor.state.selection;
|
|
7
|
+
if (empty) return null;
|
|
8
|
+
const rawText = editor.state.doc.textBetween(from, to, '\n');
|
|
9
|
+
const text = rawText.trim();
|
|
10
|
+
if (!text) return null;
|
|
11
|
+
const beforeText = editor.state.doc.textBetween(0, from, '\n').slice(-SURROUNDING_TEXT_LIMIT);
|
|
12
|
+
const afterText = editor.state.doc.textBetween(to, editor.state.doc.content.size, '\n').slice(0, SURROUNDING_TEXT_LIMIT);
|
|
13
|
+
const selectedContent = editor.state.selection.content().content.toJSON();
|
|
14
|
+
return {
|
|
15
|
+
selection: {
|
|
16
|
+
from,
|
|
17
|
+
to,
|
|
18
|
+
text,
|
|
19
|
+
rawText,
|
|
20
|
+
beforeText,
|
|
21
|
+
afterText,
|
|
22
|
+
model: {
|
|
23
|
+
type: 'doc',
|
|
24
|
+
...selectedContent?.length ? {
|
|
25
|
+
content: selectedContent
|
|
26
|
+
} : {}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
document: {
|
|
30
|
+
content,
|
|
31
|
+
html: editor.getHTML(),
|
|
32
|
+
text: editor.state.doc.textBetween(0, editor.state.doc.content.size, '\n')
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function createWorkDocumentSelectionAction(editor, snapshot, getTrackChanges) {
|
|
37
|
+
let from = snapshot.selection.from;
|
|
38
|
+
let to = snapshot.selection.to;
|
|
39
|
+
let stale = false;
|
|
40
|
+
let disposed = false;
|
|
41
|
+
const handleTransaction = ({ transaction })=>{
|
|
42
|
+
if (!transaction.docChanged || stale || disposed) return;
|
|
43
|
+
const mappedFrom = transaction.mapping.mapResult(from, 1);
|
|
44
|
+
const mappedTo = transaction.mapping.mapResult(to, -1);
|
|
45
|
+
from = mappedFrom.pos;
|
|
46
|
+
to = mappedTo.pos;
|
|
47
|
+
stale = mappedFrom.deletedAcross || mappedTo.deletedAcross || from > to || transaction.doc.textBetween(from, to, '\n') !== snapshot.selection.rawText;
|
|
48
|
+
};
|
|
49
|
+
editor.on('transaction', handleTransaction);
|
|
50
|
+
const currentRange = ()=>{
|
|
51
|
+
if (disposed || editor.isDestroyed) return 'editor-unavailable';
|
|
52
|
+
if (!editor.isEditable) return 'read-only';
|
|
53
|
+
if (stale || from < 0 || to < from || to > editor.state.doc.content.size || editor.state.doc.textBetween(from, to, '\n') !== snapshot.selection.rawText) return 'stale-selection';
|
|
54
|
+
return {
|
|
55
|
+
from,
|
|
56
|
+
to
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
const editText = (text, placement)=>{
|
|
60
|
+
if ('string' != typeof text || 'replace' !== placement && !text) return {
|
|
61
|
+
applied: false,
|
|
62
|
+
reason: 'invalid-text'
|
|
63
|
+
};
|
|
64
|
+
const range = currentRange();
|
|
65
|
+
if ('string' == typeof range) return {
|
|
66
|
+
applied: false,
|
|
67
|
+
reason: range
|
|
68
|
+
};
|
|
69
|
+
const editRange = 'before' === placement ? {
|
|
70
|
+
from: range.from,
|
|
71
|
+
to: range.from
|
|
72
|
+
} : 'after' === placement ? {
|
|
73
|
+
from: range.to,
|
|
74
|
+
to: range.to
|
|
75
|
+
} : range;
|
|
76
|
+
const applied = getTrackChanges() ? editor.commands.replaceDocumentTextWithTrackedChange(editRange.from, editRange.to, text) : editor.chain().focus().setTextSelection(editRange).insertContent(documentPlainTextAsHtml(text)).run();
|
|
77
|
+
return applied ? {
|
|
78
|
+
applied: true
|
|
79
|
+
} : {
|
|
80
|
+
applied: false,
|
|
81
|
+
reason: 'command-rejected'
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
return {
|
|
85
|
+
context: {
|
|
86
|
+
...snapshot,
|
|
87
|
+
commands: {
|
|
88
|
+
copyText: async ()=>{
|
|
89
|
+
try {
|
|
90
|
+
if ("u" < typeof navigator || !navigator.clipboard?.writeText) return false;
|
|
91
|
+
await navigator.clipboard.writeText(snapshot.selection.rawText);
|
|
92
|
+
return true;
|
|
93
|
+
} catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
insertAfter: (text)=>editText(text, 'after'),
|
|
98
|
+
insertBefore: (text)=>editText(text, 'before'),
|
|
99
|
+
replaceText: (text)=>editText(text, 'replace')
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
dispose: ()=>{
|
|
103
|
+
if (disposed) return;
|
|
104
|
+
disposed = true;
|
|
105
|
+
editor.off('transaction', handleTransaction);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function documentPlainTextAsHtml(value) {
|
|
110
|
+
return value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", ''').replace(/\r?\n/g, '<br>');
|
|
111
|
+
}
|
|
6
112
|
function documentAgentMenuItems(selection, onAgentRequest, proposalOptions) {
|
|
7
113
|
return [
|
|
8
114
|
{
|
|
@@ -173,8 +279,21 @@ function documentCurrentPage(editor) {
|
|
|
173
279
|
function documentWordCount(value) {
|
|
174
280
|
return Array.from(value.matchAll(/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]|[\p{L}\p{N}]+/gu)).length;
|
|
175
281
|
}
|
|
176
|
-
function
|
|
177
|
-
|
|
282
|
+
function documentTextStatistics(editor) {
|
|
283
|
+
const text = editor.getText({
|
|
284
|
+
blockSeparator: '\n'
|
|
285
|
+
});
|
|
286
|
+
const characters = Array.from(text).filter((character)=>'\n' !== character && '\r' !== character);
|
|
287
|
+
let paragraphCount = 0;
|
|
288
|
+
editor.state.doc.descendants((node)=>{
|
|
289
|
+
if (node.isTextblock) paragraphCount += 1;
|
|
290
|
+
});
|
|
291
|
+
return {
|
|
292
|
+
characterCountWithSpaces: characters.length,
|
|
293
|
+
characterCountWithoutSpaces: characters.filter((character)=>!/\s/u.test(character)).length,
|
|
294
|
+
paragraphCount,
|
|
295
|
+
wordCount: documentWordCount(text)
|
|
296
|
+
};
|
|
178
297
|
}
|
|
179
298
|
async function copyDocumentSelection(selection) {
|
|
180
299
|
try {
|
|
@@ -185,114 +304,6 @@ async function copyDocumentSelection(selection) {
|
|
|
185
304
|
showToast('无法访问剪贴板,请使用系统复制快捷键。', 'error');
|
|
186
305
|
}
|
|
187
306
|
}
|
|
188
|
-
const SURROUNDING_TEXT_LIMIT = 2000;
|
|
189
|
-
function createWorkDocumentSelectionSnapshot(editor, content) {
|
|
190
|
-
const { from, to, empty } = editor.state.selection;
|
|
191
|
-
if (empty) return null;
|
|
192
|
-
const rawText = editor.state.doc.textBetween(from, to, '\n');
|
|
193
|
-
const text = rawText.trim();
|
|
194
|
-
if (!text) return null;
|
|
195
|
-
const beforeText = editor.state.doc.textBetween(0, from, '\n').slice(-SURROUNDING_TEXT_LIMIT);
|
|
196
|
-
const afterText = editor.state.doc.textBetween(to, editor.state.doc.content.size, '\n').slice(0, SURROUNDING_TEXT_LIMIT);
|
|
197
|
-
const selectedContent = editor.state.selection.content().content.toJSON();
|
|
198
|
-
return {
|
|
199
|
-
selection: {
|
|
200
|
-
from,
|
|
201
|
-
to,
|
|
202
|
-
text,
|
|
203
|
-
rawText,
|
|
204
|
-
beforeText,
|
|
205
|
-
afterText,
|
|
206
|
-
model: {
|
|
207
|
-
type: 'doc',
|
|
208
|
-
...selectedContent?.length ? {
|
|
209
|
-
content: selectedContent
|
|
210
|
-
} : {}
|
|
211
|
-
}
|
|
212
|
-
},
|
|
213
|
-
document: {
|
|
214
|
-
content,
|
|
215
|
-
html: editor.getHTML(),
|
|
216
|
-
text: editor.state.doc.textBetween(0, editor.state.doc.content.size, '\n')
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
function createWorkDocumentSelectionAction(editor, snapshot, getTrackChanges) {
|
|
221
|
-
let from = snapshot.selection.from;
|
|
222
|
-
let to = snapshot.selection.to;
|
|
223
|
-
let stale = false;
|
|
224
|
-
let disposed = false;
|
|
225
|
-
const handleTransaction = ({ transaction })=>{
|
|
226
|
-
if (!transaction.docChanged || stale || disposed) return;
|
|
227
|
-
const mappedFrom = transaction.mapping.mapResult(from, 1);
|
|
228
|
-
const mappedTo = transaction.mapping.mapResult(to, -1);
|
|
229
|
-
from = mappedFrom.pos;
|
|
230
|
-
to = mappedTo.pos;
|
|
231
|
-
stale = mappedFrom.deletedAcross || mappedTo.deletedAcross || from > to || transaction.doc.textBetween(from, to, '\n') !== snapshot.selection.rawText;
|
|
232
|
-
};
|
|
233
|
-
editor.on('transaction', handleTransaction);
|
|
234
|
-
const currentRange = ()=>{
|
|
235
|
-
if (disposed || editor.isDestroyed) return 'editor-unavailable';
|
|
236
|
-
if (!editor.isEditable) return 'read-only';
|
|
237
|
-
if (stale || from < 0 || to < from || to > editor.state.doc.content.size || editor.state.doc.textBetween(from, to, '\n') !== snapshot.selection.rawText) return 'stale-selection';
|
|
238
|
-
return {
|
|
239
|
-
from,
|
|
240
|
-
to
|
|
241
|
-
};
|
|
242
|
-
};
|
|
243
|
-
const editText = (text, placement)=>{
|
|
244
|
-
if ('string' != typeof text || 'replace' !== placement && !text) return {
|
|
245
|
-
applied: false,
|
|
246
|
-
reason: 'invalid-text'
|
|
247
|
-
};
|
|
248
|
-
const range = currentRange();
|
|
249
|
-
if ('string' == typeof range) return {
|
|
250
|
-
applied: false,
|
|
251
|
-
reason: range
|
|
252
|
-
};
|
|
253
|
-
const editRange = 'before' === placement ? {
|
|
254
|
-
from: range.from,
|
|
255
|
-
to: range.from
|
|
256
|
-
} : 'after' === placement ? {
|
|
257
|
-
from: range.to,
|
|
258
|
-
to: range.to
|
|
259
|
-
} : range;
|
|
260
|
-
const applied = getTrackChanges() ? editor.commands.replaceDocumentTextWithTrackedChange(editRange.from, editRange.to, text) : editor.chain().focus().setTextSelection(editRange).insertContent(documentPlainTextAsHtml(text)).run();
|
|
261
|
-
return applied ? {
|
|
262
|
-
applied: true
|
|
263
|
-
} : {
|
|
264
|
-
applied: false,
|
|
265
|
-
reason: 'command-rejected'
|
|
266
|
-
};
|
|
267
|
-
};
|
|
268
|
-
return {
|
|
269
|
-
context: {
|
|
270
|
-
...snapshot,
|
|
271
|
-
commands: {
|
|
272
|
-
copyText: async ()=>{
|
|
273
|
-
try {
|
|
274
|
-
if ("u" < typeof navigator || !navigator.clipboard?.writeText) return false;
|
|
275
|
-
await navigator.clipboard.writeText(snapshot.selection.rawText);
|
|
276
|
-
return true;
|
|
277
|
-
} catch {
|
|
278
|
-
return false;
|
|
279
|
-
}
|
|
280
|
-
},
|
|
281
|
-
insertAfter: (text)=>editText(text, 'after'),
|
|
282
|
-
insertBefore: (text)=>editText(text, 'before'),
|
|
283
|
-
replaceText: (text)=>editText(text, 'replace')
|
|
284
|
-
}
|
|
285
|
-
},
|
|
286
|
-
dispose: ()=>{
|
|
287
|
-
if (disposed) return;
|
|
288
|
-
disposed = true;
|
|
289
|
-
editor.off('transaction', handleTransaction);
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
function documentPlainTextAsHtml(value) {
|
|
294
|
-
return value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", ''').replace(/\r?\n/g, '<br>');
|
|
295
|
-
}
|
|
296
307
|
function mergeOfficeTiptapExtensions(editorName, builtInExtensions, additionalExtensions) {
|
|
297
308
|
const names = new Set();
|
|
298
309
|
for (const extension of builtInExtensions)names.add(extension.name);
|
|
@@ -305,4 +316,4 @@ function mergeOfficeTiptapExtensions(editorName, builtInExtensions, additionalEx
|
|
|
305
316
|
...additionalExtensions
|
|
306
317
|
];
|
|
307
318
|
}
|
|
308
|
-
export {
|
|
319
|
+
export { createWorkDocumentSelectionAction, createWorkDocumentSelectionSnapshot, customSelectionMenuItems, documentAgentMenuItems, documentCurrentPage, documentCustomSelectionMenuItems, documentPageCount, documentPlainTextAsHtml, documentTextStatistics, mergeOfficeTiptapExtensions };
|
package/dist/0~9512.js
CHANGED
|
@@ -42,6 +42,7 @@ function useDialogFocusScope({ active = true, onEscape, escapeDisabled = false,
|
|
|
42
42
|
if (!isTopScope(activeScope)) return;
|
|
43
43
|
const currentScope = optionsRef.current.getActiveScope?.() ?? scopeRef.current;
|
|
44
44
|
if (!currentScope) return;
|
|
45
|
+
if (event.target instanceof Node && currentScope.contains(event.target) && 'Escape' !== event.key && 'Tab' !== event.key) return;
|
|
45
46
|
handleScopedKeyDown(event, currentScope, optionsRef.current);
|
|
46
47
|
};
|
|
47
48
|
const handleDocumentFocus = (event)=>{
|