@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.
Files changed (131) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/LICENSE +21 -0
  3. package/README.md +16 -0
  4. package/package.json +44 -0
  5. package/src/addons/AddonProvider.tsx +25 -0
  6. package/src/addons/g2/G2Chart.tsx +370 -0
  7. package/src/addons/g2/chartPresets.ts +43 -0
  8. package/src/addons/g2/chartThemeTokens.ts +124 -0
  9. package/src/addons/g2/index.ts +36 -0
  10. package/src/addons/g2/style.css +31 -0
  11. package/src/addons/insight/Insight.tsx +10 -0
  12. package/src/addons/insight/InsightAddonProvider.tsx +20 -0
  13. package/src/addons/insight/SpotlightLayout.tsx +11 -0
  14. package/src/addons/insight/index.ts +17 -0
  15. package/src/addons/insight/style.css +34 -0
  16. package/src/addons/mermaid/MermaidDiagram.tsx +379 -0
  17. package/src/addons/mermaid/index.ts +10 -0
  18. package/src/addons/registry.test.ts +28 -0
  19. package/src/addons/registry.ts +61 -0
  20. package/src/addons/types.ts +6 -0
  21. package/src/app/App.tsx +125 -0
  22. package/src/app/README.md +18 -0
  23. package/src/app/providers/SlidesNavigationProvider.tsx +82 -0
  24. package/src/app/usePresentationBootstrap.ts +85 -0
  25. package/src/features/presentation/PresentationStatus.tsx +514 -0
  26. package/src/features/presentation/PrintSlidesView.tsx +350 -0
  27. package/src/features/presentation/browser.ts +5 -0
  28. package/src/features/presentation/draw/DrawOverlay.tsx +170 -0
  29. package/src/features/presentation/draw/DrawProvider.tsx +394 -0
  30. package/src/features/presentation/draw/persistence.test.ts +80 -0
  31. package/src/features/presentation/draw/persistence.ts +54 -0
  32. package/src/features/presentation/exportArtifacts.test.ts +48 -0
  33. package/src/features/presentation/exportArtifacts.ts +6 -0
  34. package/src/features/presentation/location.test.ts +73 -0
  35. package/src/features/presentation/location.ts +113 -0
  36. package/src/features/presentation/navigation/KeyboardController.tsx +73 -0
  37. package/src/features/presentation/navigation/PresentationNavbar.tsx +162 -0
  38. package/src/features/presentation/navigation/ShortcutsHelpOverlay.test.tsx +24 -0
  39. package/src/features/presentation/navigation/ShortcutsHelpOverlay.tsx +111 -0
  40. package/src/features/presentation/navigation/keyboardShortcuts.test.ts +74 -0
  41. package/src/features/presentation/navigation/keyboardShortcuts.ts +221 -0
  42. package/src/features/presentation/navigation/useSlidesNavigation.ts +15 -0
  43. package/src/features/presentation/overview/NotesOverview.tsx +200 -0
  44. package/src/features/presentation/overview/QuickOverview.tsx +126 -0
  45. package/src/features/presentation/path.ts +137 -0
  46. package/src/features/presentation/presenter/FlowTimelinePreview.test.tsx +54 -0
  47. package/src/features/presentation/presenter/FlowTimelinePreview.tsx +274 -0
  48. package/src/features/presentation/presenter/PresenterModeView.tsx +93 -0
  49. package/src/features/presentation/presenter/PresenterShell.tsx +286 -0
  50. package/src/features/presentation/presenter/PresenterSidePreview.tsx +68 -0
  51. package/src/features/presentation/presenter/PresenterTopProgress.tsx +28 -0
  52. package/src/features/presentation/presenter/SpeakerNotesPanel.tsx +51 -0
  53. package/src/features/presentation/presenter/StandaloneModeView.tsx +36 -0
  54. package/src/features/presentation/presenter/persistence.test.ts +26 -0
  55. package/src/features/presentation/presenter/persistence.ts +31 -0
  56. package/src/features/presentation/presenter/presentationSyncBridge.test.ts +87 -0
  57. package/src/features/presentation/presenter/presentationSyncBridge.ts +82 -0
  58. package/src/features/presentation/presenter/stage.ts +15 -0
  59. package/src/features/presentation/presenter/types.ts +30 -0
  60. package/src/features/presentation/presenter/useFullscreen.ts +58 -0
  61. package/src/features/presentation/presenter/useIdleCursor.ts +37 -0
  62. package/src/features/presentation/presenter/usePresentationFlowRuntime.ts +238 -0
  63. package/src/features/presentation/presenter/usePresenterChromeRuntime.ts +358 -0
  64. package/src/features/presentation/presenter/usePresenterSessionState.ts +226 -0
  65. package/src/features/presentation/presenter/useWakeLock.ts +110 -0
  66. package/src/features/presentation/recordingFilename.test.ts +46 -0
  67. package/src/features/presentation/recordingFilename.ts +56 -0
  68. package/src/features/presentation/reveal/Reveal.tsx +119 -0
  69. package/src/features/presentation/reveal/RevealContext.tsx +29 -0
  70. package/src/features/presentation/reveal/useRevealStep.ts +35 -0
  71. package/src/features/presentation/session.test.ts +122 -0
  72. package/src/features/presentation/session.ts +124 -0
  73. package/src/features/presentation/stage/SlidePreviewSurface.tsx +92 -0
  74. package/src/features/presentation/stage/SlideStage.tsx +159 -0
  75. package/src/features/presentation/stage/slideSurface.ts +71 -0
  76. package/src/features/presentation/stage/slideViewport.tsx +47 -0
  77. package/src/features/presentation/sync/adapters/broadcastChannelTransport.ts +40 -0
  78. package/src/features/presentation/sync/adapters/websocketTransport.ts +128 -0
  79. package/src/features/presentation/sync/model/presence.test.ts +42 -0
  80. package/src/features/presentation/sync/model/presence.ts +33 -0
  81. package/src/features/presentation/sync/model/replication.test.ts +72 -0
  82. package/src/features/presentation/sync/model/replication.ts +113 -0
  83. package/src/features/presentation/sync/model/status.test.ts +52 -0
  84. package/src/features/presentation/sync/model/status.ts +33 -0
  85. package/src/features/presentation/types.ts +1 -0
  86. package/src/features/presentation/usePresentationRecorder.ts +194 -0
  87. package/src/features/presentation/usePresentationSync.ts +423 -0
  88. package/src/index.ts +7 -0
  89. package/src/main.tsx +12 -0
  90. package/src/theme/ThemeProvider.test.ts +36 -0
  91. package/src/theme/ThemeProvider.tsx +79 -0
  92. package/src/theme/__mocks__/active-theme.ts +3 -0
  93. package/src/theme/base.css +14 -0
  94. package/src/theme/components.css +231 -0
  95. package/src/theme/index.css +11 -0
  96. package/src/theme/layouts/center.tsx +9 -0
  97. package/src/theme/layouts/cover.tsx +9 -0
  98. package/src/theme/layouts/default.tsx +5 -0
  99. package/src/theme/layouts/defaultLayouts.ts +20 -0
  100. package/src/theme/layouts/helpers.tsx +12 -0
  101. package/src/theme/layouts/image-right.tsx +21 -0
  102. package/src/theme/layouts/immersive.tsx +9 -0
  103. package/src/theme/layouts/resolveLayout.ts +9 -0
  104. package/src/theme/layouts/section.tsx +9 -0
  105. package/src/theme/layouts/statement.tsx +9 -0
  106. package/src/theme/layouts/two-cols.tsx +21 -0
  107. package/src/theme/layouts/types.ts +1 -0
  108. package/src/theme/layouts.css +133 -0
  109. package/src/theme/mark.css +379 -0
  110. package/src/theme/print.css +106 -0
  111. package/src/theme/prose.css +263 -0
  112. package/src/theme/registry.test.ts +21 -0
  113. package/src/theme/registry.ts +40 -0
  114. package/src/theme/tokens.css +148 -0
  115. package/src/theme/transitions.css +141 -0
  116. package/src/theme/types.ts +9 -0
  117. package/src/theme/useResolvedLayout.ts +24 -0
  118. package/src/types/generated-slides.d.ts +7 -0
  119. package/src/types/mdx-components.ts +7 -0
  120. package/src/types/plantuml-encoder.d.ts +7 -0
  121. package/src/ui/diagrams/PlantUmlDiagram.tsx +33 -0
  122. package/src/ui/mdx/MagicMoveDemo.tsx +114 -0
  123. package/src/ui/mdx/index.ts +21 -0
  124. package/src/ui/primitives/Annotate.test.tsx +64 -0
  125. package/src/ui/primitives/Annotate.tsx +82 -0
  126. package/src/ui/primitives/Badge.tsx +5 -0
  127. package/src/ui/primitives/Callout.tsx +24 -0
  128. package/src/ui/primitives/ChromeIconButton.tsx +58 -0
  129. package/src/ui/primitives/ChromePanel.tsx +79 -0
  130. package/src/ui/primitives/ChromeTag.tsx +70 -0
  131. package/src/ui/primitives/FormSelect.tsx +51 -0
@@ -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
+ }