@slidev-react/client 0.2.5
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/CHANGELOG.md +42 -0
- package/LICENSE +21 -0
- package/README.md +16 -0
- package/package.json +44 -0
- package/src/addons/AddonProvider.tsx +25 -0
- package/src/addons/g2/G2Chart.tsx +370 -0
- package/src/addons/g2/chartPresets.ts +43 -0
- package/src/addons/g2/chartThemeTokens.ts +124 -0
- package/src/addons/g2/index.ts +36 -0
- package/src/addons/g2/style.css +31 -0
- package/src/addons/insight/Insight.tsx +10 -0
- package/src/addons/insight/InsightAddonProvider.tsx +20 -0
- package/src/addons/insight/SpotlightLayout.tsx +11 -0
- package/src/addons/insight/index.ts +17 -0
- package/src/addons/insight/style.css +34 -0
- package/src/addons/mermaid/MermaidDiagram.tsx +379 -0
- package/src/addons/mermaid/index.ts +10 -0
- package/src/addons/registry.test.ts +28 -0
- package/src/addons/registry.ts +61 -0
- package/src/addons/types.ts +6 -0
- package/src/app/App.tsx +125 -0
- package/src/app/README.md +18 -0
- package/src/app/providers/SlidesNavigationProvider.tsx +82 -0
- package/src/app/usePresentationBootstrap.ts +85 -0
- package/src/features/presentation/PresentationStatus.tsx +514 -0
- package/src/features/presentation/PrintSlidesView.tsx +350 -0
- package/src/features/presentation/browser.ts +5 -0
- package/src/features/presentation/draw/DrawOverlay.tsx +170 -0
- package/src/features/presentation/draw/DrawProvider.tsx +394 -0
- package/src/features/presentation/draw/persistence.test.ts +80 -0
- package/src/features/presentation/draw/persistence.ts +54 -0
- package/src/features/presentation/exportArtifacts.test.ts +48 -0
- package/src/features/presentation/exportArtifacts.ts +6 -0
- package/src/features/presentation/location.test.ts +73 -0
- package/src/features/presentation/location.ts +113 -0
- package/src/features/presentation/navigation/KeyboardController.tsx +73 -0
- package/src/features/presentation/navigation/PresentationNavbar.tsx +162 -0
- package/src/features/presentation/navigation/ShortcutsHelpOverlay.test.tsx +24 -0
- package/src/features/presentation/navigation/ShortcutsHelpOverlay.tsx +111 -0
- package/src/features/presentation/navigation/keyboardShortcuts.test.ts +74 -0
- package/src/features/presentation/navigation/keyboardShortcuts.ts +221 -0
- package/src/features/presentation/navigation/useSlidesNavigation.ts +15 -0
- package/src/features/presentation/overview/NotesOverview.tsx +200 -0
- package/src/features/presentation/overview/QuickOverview.tsx +126 -0
- package/src/features/presentation/path.ts +137 -0
- package/src/features/presentation/presenter/FlowTimelinePreview.test.tsx +54 -0
- package/src/features/presentation/presenter/FlowTimelinePreview.tsx +274 -0
- package/src/features/presentation/presenter/PresenterModeView.tsx +93 -0
- package/src/features/presentation/presenter/PresenterShell.tsx +286 -0
- package/src/features/presentation/presenter/PresenterSidePreview.tsx +68 -0
- package/src/features/presentation/presenter/PresenterTopProgress.tsx +28 -0
- package/src/features/presentation/presenter/SpeakerNotesPanel.tsx +51 -0
- package/src/features/presentation/presenter/StandaloneModeView.tsx +36 -0
- package/src/features/presentation/presenter/persistence.test.ts +26 -0
- package/src/features/presentation/presenter/persistence.ts +31 -0
- package/src/features/presentation/presenter/presentationSyncBridge.test.ts +87 -0
- package/src/features/presentation/presenter/presentationSyncBridge.ts +82 -0
- package/src/features/presentation/presenter/stage.ts +15 -0
- package/src/features/presentation/presenter/types.ts +30 -0
- package/src/features/presentation/presenter/useFullscreen.ts +58 -0
- package/src/features/presentation/presenter/useIdleCursor.ts +37 -0
- package/src/features/presentation/presenter/usePresentationFlowRuntime.ts +238 -0
- package/src/features/presentation/presenter/usePresenterChromeRuntime.ts +358 -0
- package/src/features/presentation/presenter/usePresenterSessionState.ts +226 -0
- package/src/features/presentation/presenter/useWakeLock.ts +110 -0
- package/src/features/presentation/recordingFilename.test.ts +46 -0
- package/src/features/presentation/recordingFilename.ts +56 -0
- package/src/features/presentation/reveal/Reveal.tsx +119 -0
- package/src/features/presentation/reveal/RevealContext.tsx +29 -0
- package/src/features/presentation/reveal/useRevealStep.ts +35 -0
- package/src/features/presentation/session.test.ts +122 -0
- package/src/features/presentation/session.ts +124 -0
- package/src/features/presentation/stage/SlidePreviewSurface.tsx +92 -0
- package/src/features/presentation/stage/SlideStage.tsx +159 -0
- package/src/features/presentation/stage/slideSurface.ts +71 -0
- package/src/features/presentation/stage/slideViewport.tsx +47 -0
- package/src/features/presentation/sync/adapters/broadcastChannelTransport.ts +40 -0
- package/src/features/presentation/sync/adapters/websocketTransport.ts +128 -0
- package/src/features/presentation/sync/model/presence.test.ts +42 -0
- package/src/features/presentation/sync/model/presence.ts +33 -0
- package/src/features/presentation/sync/model/replication.test.ts +72 -0
- package/src/features/presentation/sync/model/replication.ts +113 -0
- package/src/features/presentation/sync/model/status.test.ts +52 -0
- package/src/features/presentation/sync/model/status.ts +33 -0
- package/src/features/presentation/types.ts +1 -0
- package/src/features/presentation/usePresentationRecorder.ts +194 -0
- package/src/features/presentation/usePresentationSync.ts +423 -0
- package/src/index.ts +7 -0
- package/src/main.tsx +12 -0
- package/src/theme/ThemeProvider.test.ts +36 -0
- package/src/theme/ThemeProvider.tsx +79 -0
- package/src/theme/__mocks__/active-theme.ts +3 -0
- package/src/theme/base.css +14 -0
- package/src/theme/components.css +231 -0
- package/src/theme/index.css +11 -0
- package/src/theme/layouts/center.tsx +9 -0
- package/src/theme/layouts/cover.tsx +9 -0
- package/src/theme/layouts/default.tsx +5 -0
- package/src/theme/layouts/defaultLayouts.ts +20 -0
- package/src/theme/layouts/helpers.tsx +12 -0
- package/src/theme/layouts/image-right.tsx +21 -0
- package/src/theme/layouts/immersive.tsx +9 -0
- package/src/theme/layouts/resolveLayout.ts +9 -0
- package/src/theme/layouts/section.tsx +9 -0
- package/src/theme/layouts/statement.tsx +9 -0
- package/src/theme/layouts/two-cols.tsx +21 -0
- package/src/theme/layouts/types.ts +1 -0
- package/src/theme/layouts.css +133 -0
- package/src/theme/mark.css +379 -0
- package/src/theme/print.css +106 -0
- package/src/theme/prose.css +263 -0
- package/src/theme/registry.test.ts +21 -0
- package/src/theme/registry.ts +40 -0
- package/src/theme/tokens.css +148 -0
- package/src/theme/transitions.css +141 -0
- package/src/theme/types.ts +9 -0
- package/src/theme/useResolvedLayout.ts +24 -0
- package/src/types/generated-slides.d.ts +7 -0
- package/src/types/mdx-components.ts +7 -0
- package/src/types/plantuml-encoder.d.ts +7 -0
- package/src/ui/diagrams/PlantUmlDiagram.tsx +33 -0
- package/src/ui/mdx/MagicMoveDemo.tsx +114 -0
- package/src/ui/mdx/index.ts +21 -0
- package/src/ui/primitives/Annotate.test.tsx +64 -0
- package/src/ui/primitives/Annotate.tsx +82 -0
- package/src/ui/primitives/Badge.tsx +5 -0
- package/src/ui/primitives/Callout.tsx +24 -0
- package/src/ui/primitives/ChromeIconButton.tsx +58 -0
- package/src/ui/primitives/ChromePanel.tsx +79 -0
- package/src/ui/primitives/ChromeTag.tsx +70 -0
- package/src/ui/primitives/FormSelect.tsx +51 -0
package/src/app/App.tsx
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { MDXProvider } from "@mdx-js/react";
|
|
2
|
+
import compiledSlides from "@generated/slides";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { SlidesNavigationProvider } from "./providers/SlidesNavigationProvider";
|
|
5
|
+
import { AddonProvider, useSlideAddons } from "../addons/AddonProvider";
|
|
6
|
+
import { PrintSlidesView } from "../features/presentation/PrintSlidesView";
|
|
7
|
+
import { PresenterShell } from "../features/presentation/presenter/PresenterShell";
|
|
8
|
+
import { buildSlidesUrl } from "@slidev-react/core/presentation/export/urls";
|
|
9
|
+
import { type PresentationSession } from "../features/presentation/session";
|
|
10
|
+
import type { PresentationSyncMode } from "../features/presentation/types";
|
|
11
|
+
import { ThemeProvider, useSlideTheme } from "../theme/ThemeProvider";
|
|
12
|
+
import type { ThemeMDXComponents } from "../theme/types";
|
|
13
|
+
import { usePresentationBootstrap } from "./usePresentationBootstrap";
|
|
14
|
+
|
|
15
|
+
function ThemeBoundApp({
|
|
16
|
+
exportMode,
|
|
17
|
+
exportWithClicks,
|
|
18
|
+
exportBaseName,
|
|
19
|
+
slidesDocument,
|
|
20
|
+
drawStorageKey,
|
|
21
|
+
presentationSession,
|
|
22
|
+
handleSyncModeChange,
|
|
23
|
+
}: {
|
|
24
|
+
exportMode: "print" | null;
|
|
25
|
+
exportWithClicks: boolean;
|
|
26
|
+
exportBaseName: string;
|
|
27
|
+
slidesDocument: typeof compiledSlides;
|
|
28
|
+
drawStorageKey: string;
|
|
29
|
+
presentationSession: PresentationSession;
|
|
30
|
+
handleSyncModeChange: (mode: PresentationSyncMode) => void;
|
|
31
|
+
}) {
|
|
32
|
+
const theme = useSlideTheme();
|
|
33
|
+
const addons = useSlideAddons();
|
|
34
|
+
const mdxComponents = useMemo<ThemeMDXComponents>(
|
|
35
|
+
() => ({
|
|
36
|
+
...theme.mdxComponents,
|
|
37
|
+
...addons.mdxComponents,
|
|
38
|
+
}),
|
|
39
|
+
[addons.mdxComponents, theme.mdxComponents],
|
|
40
|
+
);
|
|
41
|
+
const runtimeProviders = useMemo(
|
|
42
|
+
() =>
|
|
43
|
+
[theme.provider, ...addons.providers].filter(
|
|
44
|
+
(provider): provider is NonNullable<typeof provider> => Boolean(provider),
|
|
45
|
+
),
|
|
46
|
+
[addons.providers, theme.provider],
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const content =
|
|
50
|
+
exportMode === "print" ? (
|
|
51
|
+
<PrintSlidesView
|
|
52
|
+
slides={slidesDocument.slides}
|
|
53
|
+
slidesTitle={slidesDocument.meta.title}
|
|
54
|
+
slidesViewport={slidesDocument.meta.viewport}
|
|
55
|
+
slidesLayout={slidesDocument.meta.layout}
|
|
56
|
+
slidesBackground={slidesDocument.meta.background}
|
|
57
|
+
exportBaseName={exportBaseName}
|
|
58
|
+
withClicks={exportWithClicks}
|
|
59
|
+
onBack={() => {
|
|
60
|
+
window.location.assign(buildSlidesUrl(window.location.href));
|
|
61
|
+
}}
|
|
62
|
+
/>
|
|
63
|
+
) : (
|
|
64
|
+
<SlidesNavigationProvider total={slidesDocument.slides.length}>
|
|
65
|
+
<PresenterShell
|
|
66
|
+
slides={slidesDocument.slides}
|
|
67
|
+
slidesTitle={slidesDocument.meta.title}
|
|
68
|
+
slidesConfig={{
|
|
69
|
+
slidesViewport: slidesDocument.meta.viewport,
|
|
70
|
+
slidesLayout: slidesDocument.meta.layout,
|
|
71
|
+
slidesBackground: slidesDocument.meta.background,
|
|
72
|
+
slidesTransition: slidesDocument.meta.transition,
|
|
73
|
+
}}
|
|
74
|
+
slidesExportFilename={slidesDocument.meta.exportFilename}
|
|
75
|
+
slidesSessionSeed={slidesDocument.sourceHash}
|
|
76
|
+
drawStorageKey={drawStorageKey}
|
|
77
|
+
session={presentationSession}
|
|
78
|
+
onSyncModeChange={handleSyncModeChange}
|
|
79
|
+
/>
|
|
80
|
+
</SlidesNavigationProvider>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<MDXProvider components={mdxComponents}>
|
|
85
|
+
{runtimeProviders.reduceRight(
|
|
86
|
+
(children, Provider) => (
|
|
87
|
+
<Provider>{children}</Provider>
|
|
88
|
+
),
|
|
89
|
+
content,
|
|
90
|
+
)}
|
|
91
|
+
</MDXProvider>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export default function App() {
|
|
96
|
+
const slidesDocument = compiledSlides;
|
|
97
|
+
const {
|
|
98
|
+
exportMode,
|
|
99
|
+
exportWithClicks,
|
|
100
|
+
exportBaseName,
|
|
101
|
+
drawStorageKey,
|
|
102
|
+
presentationSession,
|
|
103
|
+
handleSyncModeChange,
|
|
104
|
+
} = usePresentationBootstrap({
|
|
105
|
+
slidesDocument,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<ThemeProvider
|
|
110
|
+
slidesViewport={slidesDocument.meta.viewport}
|
|
111
|
+
>
|
|
112
|
+
<AddonProvider addonIds={slidesDocument.meta.addons}>
|
|
113
|
+
<ThemeBoundApp
|
|
114
|
+
exportMode={exportMode}
|
|
115
|
+
exportWithClicks={exportWithClicks}
|
|
116
|
+
exportBaseName={exportBaseName}
|
|
117
|
+
slidesDocument={slidesDocument}
|
|
118
|
+
drawStorageKey={drawStorageKey}
|
|
119
|
+
presentationSession={presentationSession}
|
|
120
|
+
handleSyncModeChange={handleSyncModeChange}
|
|
121
|
+
/>
|
|
122
|
+
</AddonProvider>
|
|
123
|
+
</ThemeProvider>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# `app/` Guide
|
|
2
|
+
|
|
3
|
+
`app/` 负责应用装配,而不是承载具体产品能力。
|
|
4
|
+
|
|
5
|
+
术语约定见:
|
|
6
|
+
`../../../docs/slide-react-terminology.md`
|
|
7
|
+
|
|
8
|
+
## What Belongs Here
|
|
9
|
+
|
|
10
|
+
- 入口级组件装配,例如 `App.tsx`
|
|
11
|
+
- 应用级 provider,例如页码导航、路由模式、全局 composition
|
|
12
|
+
- 未来若有 `shell/`,应承接模式切换和页面级骨架
|
|
13
|
+
|
|
14
|
+
## What Does Not Belong Here
|
|
15
|
+
|
|
16
|
+
- reveal、draw、presentation sync 这类明确 feature 语义
|
|
17
|
+
- 纯展示组件
|
|
18
|
+
- slides parsing / compiling 逻辑
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
buildSlidesPath,
|
|
4
|
+
clampSlideIndex,
|
|
5
|
+
normalizePathname,
|
|
6
|
+
resolveSlidesLocationState,
|
|
7
|
+
type PresentationRouteMode,
|
|
8
|
+
} from "../../features/presentation/location";
|
|
9
|
+
|
|
10
|
+
interface SlidesContextValue {
|
|
11
|
+
currentIndex: number;
|
|
12
|
+
total: number;
|
|
13
|
+
next: () => void;
|
|
14
|
+
prev: () => void;
|
|
15
|
+
first: () => void;
|
|
16
|
+
last: () => void;
|
|
17
|
+
goTo: (index: number) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const SlidesContext = createContext<SlidesContextValue | null>(null);
|
|
21
|
+
|
|
22
|
+
export function SlidesNavigationProvider({
|
|
23
|
+
total,
|
|
24
|
+
children,
|
|
25
|
+
}: {
|
|
26
|
+
total: number;
|
|
27
|
+
children: React.ReactNode;
|
|
28
|
+
}) {
|
|
29
|
+
const initialLocation = resolveSlidesLocationState(window.location.pathname, total);
|
|
30
|
+
const [currentIndex, setCurrentIndex] = useState(initialLocation.index);
|
|
31
|
+
const routeModeRef = useRef<PresentationRouteMode | null>(initialLocation.mode);
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
setCurrentIndex((index) => clampSlideIndex(index, total));
|
|
35
|
+
}, [total]);
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const syncFromLocation = () => {
|
|
39
|
+
const locationState = resolveSlidesLocationState(window.location.pathname, total);
|
|
40
|
+
routeModeRef.current = locationState.mode;
|
|
41
|
+
setCurrentIndex(locationState.index);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
window.addEventListener("popstate", syncFromLocation);
|
|
45
|
+
return () => {
|
|
46
|
+
window.removeEventListener("popstate", syncFromLocation);
|
|
47
|
+
};
|
|
48
|
+
}, [total]);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
const routeMode =
|
|
52
|
+
routeModeRef.current ?? resolveSlidesLocationState(window.location.pathname, total).mode;
|
|
53
|
+
|
|
54
|
+
const nextPath = buildSlidesPath(routeMode, currentIndex);
|
|
55
|
+
const currentPath = normalizePathname(window.location.pathname);
|
|
56
|
+
|
|
57
|
+
if (currentPath !== nextPath || window.location.hash)
|
|
58
|
+
window.history.replaceState(null, "", nextPath);
|
|
59
|
+
}, [currentIndex]);
|
|
60
|
+
|
|
61
|
+
const value = useMemo<SlidesContextValue>(
|
|
62
|
+
() => ({
|
|
63
|
+
currentIndex,
|
|
64
|
+
total,
|
|
65
|
+
next: () => setCurrentIndex((index) => clampSlideIndex(index + 1, total)),
|
|
66
|
+
prev: () => setCurrentIndex((index) => clampSlideIndex(index - 1, total)),
|
|
67
|
+
first: () => setCurrentIndex(0),
|
|
68
|
+
last: () => setCurrentIndex(Math.max(total - 1, 0)),
|
|
69
|
+
goTo: (index: number) => setCurrentIndex(clampSlideIndex(index, total)),
|
|
70
|
+
}),
|
|
71
|
+
[currentIndex, total],
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return <SlidesContext.Provider value={value}>{children}</SlidesContext.Provider>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function useSlidesState() {
|
|
78
|
+
const context = useContext(SlidesContext);
|
|
79
|
+
if (!context) throw new Error("useSlidesState must be used inside SlidesNavigationProvider");
|
|
80
|
+
|
|
81
|
+
return context;
|
|
82
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type compiledSlides from "@generated/slides";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { resolvePresentationFileNameBase } from "../features/presentation/recordingFilename";
|
|
4
|
+
import {
|
|
5
|
+
createPrintExportSession,
|
|
6
|
+
resolvePresentationSession,
|
|
7
|
+
updateSyncModeInUrl,
|
|
8
|
+
type PresentationSession,
|
|
9
|
+
} from "../features/presentation/session";
|
|
10
|
+
import type { PresentationSyncMode } from "../features/presentation/types";
|
|
11
|
+
import {
|
|
12
|
+
resolvePresentationExportMode,
|
|
13
|
+
resolvePrintExportWithClicks,
|
|
14
|
+
} from "@slidev-react/core/presentation/export/urls";
|
|
15
|
+
|
|
16
|
+
export function usePresentationBootstrap({
|
|
17
|
+
slidesDocument,
|
|
18
|
+
}: {
|
|
19
|
+
slidesDocument: typeof compiledSlides;
|
|
20
|
+
}) {
|
|
21
|
+
const exportMode = useMemo(
|
|
22
|
+
() => resolvePresentationExportMode(window.location.search),
|
|
23
|
+
[],
|
|
24
|
+
);
|
|
25
|
+
const exportWithClicks = useMemo(
|
|
26
|
+
() => resolvePrintExportWithClicks(window.location.search),
|
|
27
|
+
[],
|
|
28
|
+
);
|
|
29
|
+
const slidesHash = useMemo(() => slidesDocument.sourceHash, [slidesDocument.sourceHash]);
|
|
30
|
+
const drawStorageKey = useMemo(() => `slide-react:draw:${slidesHash}`, [slidesHash]);
|
|
31
|
+
const exportBaseName = useMemo(
|
|
32
|
+
() =>
|
|
33
|
+
resolvePresentationFileNameBase({
|
|
34
|
+
exportFilename: slidesDocument.meta.exportFilename,
|
|
35
|
+
slidesTitle: slidesDocument.meta.title,
|
|
36
|
+
}),
|
|
37
|
+
[slidesDocument.meta.exportFilename, slidesDocument.meta.title],
|
|
38
|
+
);
|
|
39
|
+
const sessionBase = useMemo<PresentationSession>(() => {
|
|
40
|
+
if (exportMode === "print") return createPrintExportSession();
|
|
41
|
+
|
|
42
|
+
return resolvePresentationSession(slidesHash);
|
|
43
|
+
}, [exportMode, slidesHash]);
|
|
44
|
+
const [syncMode, setSyncMode] = useState<PresentationSyncMode>(sessionBase.syncMode);
|
|
45
|
+
const presentationSession = useMemo<PresentationSession>(
|
|
46
|
+
() => ({
|
|
47
|
+
...sessionBase,
|
|
48
|
+
syncMode,
|
|
49
|
+
}),
|
|
50
|
+
[sessionBase, syncMode],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
setSyncMode(sessionBase.syncMode);
|
|
55
|
+
}, [sessionBase.syncMode]);
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
document.title =
|
|
59
|
+
exportMode === "print"
|
|
60
|
+
? `${exportBaseName}.pdf`
|
|
61
|
+
: (slidesDocument.meta.title ?? "Slide React MVP");
|
|
62
|
+
}, [slidesDocument.meta.title, exportBaseName, exportMode]);
|
|
63
|
+
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
const mode = exportMode === "print" ? "print" : "live";
|
|
66
|
+
document.documentElement.dataset.presentationMode = mode;
|
|
67
|
+
return () => {
|
|
68
|
+
delete document.documentElement.dataset.presentationMode;
|
|
69
|
+
};
|
|
70
|
+
}, [exportMode]);
|
|
71
|
+
|
|
72
|
+
const handleSyncModeChange = useCallback((mode: PresentationSyncMode) => {
|
|
73
|
+
setSyncMode(mode);
|
|
74
|
+
updateSyncModeInUrl(mode);
|
|
75
|
+
}, []);
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
exportMode,
|
|
79
|
+
exportWithClicks,
|
|
80
|
+
exportBaseName,
|
|
81
|
+
drawStorageKey,
|
|
82
|
+
presentationSession,
|
|
83
|
+
handleSyncModeChange,
|
|
84
|
+
};
|
|
85
|
+
}
|