@slidev-react/client 0.2.5 → 0.2.7
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/package.json +2 -2
- package/src/addons/{registry.test.ts → __tests__/registry.test.ts} +5 -5
- package/src/addons/g2/chartThemeTokens.ts +14 -14
- package/src/features/presentation/{exportArtifacts.test.ts → __tests__/exportArtifacts.test.ts} +2 -2
- package/src/features/presentation/{location.test.ts → __tests__/location.test.ts} +2 -2
- package/src/features/presentation/__tests__/path.test.ts +146 -0
- package/src/features/presentation/{recordingFilename.test.ts → __tests__/recordingFilename.test.ts} +2 -2
- package/src/features/presentation/{session.test.ts → __tests__/session.test.ts} +2 -2
- package/src/features/presentation/draw/{persistence.test.ts → __tests__/persistence.test.ts} +2 -2
- package/src/features/presentation/navigation/{ShortcutsHelpOverlay.test.tsx → __tests__/ShortcutsHelpOverlay.test.tsx} +3 -3
- package/src/features/presentation/navigation/{keyboardShortcuts.test.ts → __tests__/keyboardShortcuts.test.ts} +2 -2
- package/src/features/presentation/presenter/{FlowTimelinePreview.test.tsx → __tests__/FlowTimelinePreview.test.tsx} +5 -5
- package/src/features/presentation/presenter/{persistence.test.ts → __tests__/persistence.test.ts} +2 -2
- package/src/features/presentation/presenter/{presentationSyncBridge.test.ts → __tests__/presentationSyncBridge.test.ts} +2 -2
- package/src/features/presentation/reveal/Reveal.tsx +6 -6
- package/src/features/presentation/stage/__tests__/slideSurface.test.ts +93 -0
- package/src/features/presentation/sync/model/{presence.test.ts → __tests__/presence.test.ts} +2 -2
- package/src/features/presentation/sync/model/{replication.test.ts → __tests__/replication.test.ts} +2 -2
- package/src/features/presentation/sync/model/{status.test.ts → __tests__/status.test.ts} +2 -2
- package/src/theme/{ThemeProvider.test.ts → __tests__/ThemeProvider.test.ts} +3 -3
- package/src/theme/{registry.test.ts → __tests__/registry.test.ts} +4 -4
- package/src/theme/layouts/__tests__/resolveLayout.test.ts +31 -0
- package/src/ui/mdx/{MagicMoveDemo.tsx → CodeMagicMove.tsx} +16 -13
- package/src/ui/mdx/index.ts +5 -5
- package/src/ui/primitives/Badge.tsx +5 -1
- package/src/ui/primitives/{Annotate.test.tsx → __tests__/Annotate.test.tsx} +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev-react/client",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "Browser-side React app and presentation UI for slidev-react",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"shiki": "4.0.1",
|
|
15
15
|
"shiki-magic-move": "1.2.1",
|
|
16
16
|
"zod": "4.3.6",
|
|
17
|
-
"@slidev-react/core": "0.2.
|
|
17
|
+
"@slidev-react/core": "0.2.7"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"@antv/g-svg": ">=2.0.0",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { describe, expect, it } from "
|
|
2
|
-
import { listRegisteredAddons, resolveAddonDefinitions, resolveSlideAddons } from "
|
|
3
|
-
import { Insight } from "
|
|
4
|
-
import { InsightAddonProvider } from "
|
|
5
|
-
import { SpotlightLayout } from "
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
|
+
import { listRegisteredAddons, resolveAddonDefinitions, resolveSlideAddons } from "../registry";
|
|
3
|
+
import { Insight } from "../insight/Insight";
|
|
4
|
+
import { InsightAddonProvider } from "../insight/InsightAddonProvider";
|
|
5
|
+
import { SpotlightLayout } from "../insight/SpotlightLayout";
|
|
6
6
|
|
|
7
7
|
describe("addon registry", () => {
|
|
8
8
|
it("registers local addons discovered from the addons directory", () => {
|
|
@@ -68,39 +68,39 @@ export function buildSlidevTheme(): G2Theme {
|
|
|
68
68
|
category10: categoryPalette,
|
|
69
69
|
category20: categoryPalette,
|
|
70
70
|
axis: {
|
|
71
|
-
labelFontSize:
|
|
72
|
-
labelFill:
|
|
71
|
+
labelFontSize: 12,
|
|
72
|
+
labelFill: '#8b95a2',
|
|
73
73
|
labelFontFamily: font,
|
|
74
|
-
titleFontSize:
|
|
75
|
-
titleFill:
|
|
74
|
+
titleFontSize: 13,
|
|
75
|
+
titleFill: '#718096',
|
|
76
76
|
titleFontFamily: font,
|
|
77
|
-
titleFontWeight: '
|
|
78
|
-
gridStroke: '#
|
|
79
|
-
gridStrokeOpacity:
|
|
80
|
-
lineStroke: '#
|
|
77
|
+
titleFontWeight: 'normal',
|
|
78
|
+
gridStroke: '#e8ecf1',
|
|
79
|
+
gridStrokeOpacity: 0.6,
|
|
80
|
+
lineStroke: '#dfe4ea',
|
|
81
81
|
lineLineWidth: 1,
|
|
82
|
-
tickStroke: '#
|
|
82
|
+
tickStroke: '#dfe4ea',
|
|
83
83
|
},
|
|
84
84
|
legendCategory: {
|
|
85
|
-
itemLabelFontSize:
|
|
85
|
+
itemLabelFontSize: 13,
|
|
86
86
|
itemLabelFill: mutedColor,
|
|
87
87
|
itemLabelFontFamily: font,
|
|
88
|
-
titleFontSize:
|
|
88
|
+
titleFontSize: 14,
|
|
89
89
|
titleFill: textColor,
|
|
90
90
|
titleFontFamily: font,
|
|
91
91
|
titleFontWeight: 'bold',
|
|
92
92
|
},
|
|
93
93
|
title: {
|
|
94
|
-
titleFontSize:
|
|
94
|
+
titleFontSize: 18,
|
|
95
95
|
titleFill: textColor,
|
|
96
96
|
titleFontFamily: font,
|
|
97
97
|
titleFontWeight: 'bold',
|
|
98
|
-
subtitleFontSize:
|
|
98
|
+
subtitleFontSize: 14,
|
|
99
99
|
subtitleFill: mutedColor,
|
|
100
100
|
subtitleFontFamily: font,
|
|
101
101
|
},
|
|
102
102
|
label: {
|
|
103
|
-
fontSize:
|
|
103
|
+
fontSize: 12,
|
|
104
104
|
fontFamily: font,
|
|
105
105
|
fill: mutedColor,
|
|
106
106
|
},
|
package/src/features/presentation/{exportArtifacts.test.ts → __tests__/exportArtifacts.test.ts}
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { describe, expect, it } from "
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
2
|
import {
|
|
3
3
|
createSlideImageFileName,
|
|
4
4
|
createSlideSnapshotFileName,
|
|
5
5
|
resolveExportSlidesBaseName,
|
|
6
6
|
trimPdfExtension,
|
|
7
|
-
} from "
|
|
7
|
+
} from "../exportArtifacts";
|
|
8
8
|
|
|
9
9
|
describe("presentation export artifacts", () => {
|
|
10
10
|
it("removes a trailing pdf extension from document titles", () => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { describe, expect, it } from "
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
2
|
import {
|
|
3
3
|
buildSlidesPath,
|
|
4
4
|
normalizePathname,
|
|
5
5
|
resolveSessionLocationState,
|
|
6
6
|
resolveSlidesLocationState,
|
|
7
|
-
} from "
|
|
7
|
+
} from "../location";
|
|
8
8
|
|
|
9
9
|
describe("presentation location", () => {
|
|
10
10
|
it("resolves presenter routes for slides navigation", () => {
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
|
+
import {
|
|
3
|
+
buildRolePathFromBase,
|
|
4
|
+
buildRolePathFromPathname,
|
|
5
|
+
buildStandalonePathFromBase,
|
|
6
|
+
buildStandalonePathFromPathname,
|
|
7
|
+
parsePresentationPath,
|
|
8
|
+
parseStandalonePath,
|
|
9
|
+
resolvePresentationBasePath,
|
|
10
|
+
} from "../path";
|
|
11
|
+
|
|
12
|
+
describe("parsePresentationPath", () => {
|
|
13
|
+
it("parses /presenter as a presenter role with no slide number", () => {
|
|
14
|
+
expect(parsePresentationPath("/presenter")).toEqual({
|
|
15
|
+
role: "presenter",
|
|
16
|
+
slideNumber: null,
|
|
17
|
+
basePath: "",
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("parses /presenter/3 as presenter role on slide 3", () => {
|
|
22
|
+
expect(parsePresentationPath("/presenter/3")).toEqual({
|
|
23
|
+
role: "presenter",
|
|
24
|
+
slideNumber: 3,
|
|
25
|
+
basePath: "",
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("parses a nested basePath like /app/presenter/5", () => {
|
|
30
|
+
expect(parsePresentationPath("/app/presenter/5")).toEqual({
|
|
31
|
+
role: "presenter",
|
|
32
|
+
slideNumber: 5,
|
|
33
|
+
basePath: "/app",
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("returns null for a plain path without a role", () => {
|
|
38
|
+
expect(parsePresentationPath("/slides")).toBeNull();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("returns null for an empty path", () => {
|
|
42
|
+
expect(parsePresentationPath("/")).toBeNull();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("returns null for slide-only paths", () => {
|
|
46
|
+
expect(parsePresentationPath("/3")).toBeNull();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe("parseStandalonePath", () => {
|
|
51
|
+
it("parses /3 as slide number 3", () => {
|
|
52
|
+
expect(parseStandalonePath("/3")).toEqual({
|
|
53
|
+
slideNumber: 3,
|
|
54
|
+
basePath: "",
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("parses /app/7 with a basePath", () => {
|
|
59
|
+
expect(parseStandalonePath("/app/7")).toEqual({
|
|
60
|
+
slideNumber: 7,
|
|
61
|
+
basePath: "/app",
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("returns null for non-numeric paths", () => {
|
|
66
|
+
expect(parseStandalonePath("/about")).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("returns null for presenter paths (handled by parsePresentationPath)", () => {
|
|
70
|
+
expect(parseStandalonePath("/presenter/3")).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("returns null for slide number 0 or negative", () => {
|
|
74
|
+
expect(parseStandalonePath("/0")).toBeNull();
|
|
75
|
+
expect(parseStandalonePath("/-1")).toBeNull();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("resolvePresentationBasePath", () => {
|
|
80
|
+
it("returns empty string for root", () => {
|
|
81
|
+
expect(resolvePresentationBasePath("/")).toBe("");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("returns empty string for /index.html", () => {
|
|
85
|
+
expect(resolvePresentationBasePath("/index.html")).toBe("");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("strips /index.html suffix", () => {
|
|
89
|
+
expect(resolvePresentationBasePath("/app/index.html")).toBe("/app");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("extracts basePath from presenter paths", () => {
|
|
93
|
+
expect(resolvePresentationBasePath("/app/presenter/3")).toBe("/app");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("extracts basePath from standalone slide paths", () => {
|
|
97
|
+
expect(resolvePresentationBasePath("/app/5")).toBe("/app");
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("buildRolePathFromBase", () => {
|
|
102
|
+
it("builds /presenter/1 from empty base", () => {
|
|
103
|
+
expect(buildRolePathFromBase("", "presenter", 1)).toBe("/presenter/1");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("builds /app/presenter/5 from /app base", () => {
|
|
107
|
+
expect(buildRolePathFromBase("/app", "presenter", 5)).toBe("/app/presenter/5");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("clamps non-positive slide numbers to 1", () => {
|
|
111
|
+
expect(buildRolePathFromBase("", "presenter", 0)).toBe("/presenter/1");
|
|
112
|
+
expect(buildRolePathFromBase("", "presenter", -3)).toBe("/presenter/1");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("floors fractional slide numbers", () => {
|
|
116
|
+
expect(buildRolePathFromBase("", "presenter", 3.7)).toBe("/presenter/3");
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe("buildRolePathFromPathname", () => {
|
|
121
|
+
it("derives basePath from a full pathname and builds the role path", () => {
|
|
122
|
+
expect(buildRolePathFromPathname("/app/presenter/2", "presenter", 5)).toBe(
|
|
123
|
+
"/app/presenter/5",
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe("buildStandalonePathFromBase", () => {
|
|
129
|
+
it("builds /3 from empty base", () => {
|
|
130
|
+
expect(buildStandalonePathFromBase("", 3)).toBe("/3");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("builds /app/3 from /app base", () => {
|
|
134
|
+
expect(buildStandalonePathFromBase("/app", 3)).toBe("/app/3");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("clamps non-positive slide numbers to 1", () => {
|
|
138
|
+
expect(buildStandalonePathFromBase("", 0)).toBe("/1");
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe("buildStandalonePathFromPathname", () => {
|
|
143
|
+
it("derives basePath from pathname and builds standalone path", () => {
|
|
144
|
+
expect(buildStandalonePathFromPathname("/app/5", 3)).toBe("/app/3");
|
|
145
|
+
});
|
|
146
|
+
});
|
package/src/features/presentation/{recordingFilename.test.ts → __tests__/recordingFilename.test.ts}
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { describe, expect, it } from "
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
2
|
import {
|
|
3
3
|
createRecordingDownloadName,
|
|
4
4
|
resolvePresentationFileNameBase,
|
|
5
5
|
resolveRecordingFileNameBase,
|
|
6
|
-
} from "
|
|
6
|
+
} from "../recordingFilename";
|
|
7
7
|
|
|
8
8
|
describe("recording filename", () => {
|
|
9
9
|
it("prefers exportFilename when present", () => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it, vi } from "
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vite-plus/test";
|
|
2
2
|
import {
|
|
3
3
|
buildPresentationEntryUrl,
|
|
4
4
|
resolvePresentationSession,
|
|
5
5
|
updateSyncModeInUrl,
|
|
6
|
-
} from "
|
|
6
|
+
} from "../session";
|
|
7
7
|
|
|
8
8
|
const originalWindow = globalThis.window;
|
|
9
9
|
|
package/src/features/presentation/draw/{persistence.test.ts → __tests__/persistence.test.ts}
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { describe, expect, it } from "
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
2
|
import {
|
|
3
3
|
DRAW_STORAGE_VERSION,
|
|
4
4
|
createPersistedDrawState,
|
|
5
5
|
parsePersistedDrawState,
|
|
6
|
-
} from "
|
|
6
|
+
} from "../persistence";
|
|
7
7
|
|
|
8
8
|
describe("draw persistence", () => {
|
|
9
9
|
it("parses persisted draw state", () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
2
|
-
import { describe, expect, it } from "
|
|
3
|
-
import { ShortcutsHelpOverlay } from "
|
|
4
|
-
import { buildShortcutHelpSections } from "
|
|
2
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
3
|
+
import { ShortcutsHelpOverlay } from "../ShortcutsHelpOverlay";
|
|
4
|
+
import { buildShortcutHelpSections } from "../keyboardShortcuts";
|
|
5
5
|
|
|
6
6
|
describe("ShortcutsHelpOverlay", () => {
|
|
7
7
|
it("renders the supported shortcut groups and help triggers", () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, it } from "
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
2
|
import {
|
|
3
3
|
buildShortcutHelpSections,
|
|
4
4
|
createShortcutHelpTriggerState,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
registerShortcutHelpKeyDown,
|
|
7
7
|
registerShortcutHelpKeyUp,
|
|
8
8
|
resolveNavigationShortcutAction,
|
|
9
|
-
} from "
|
|
9
|
+
} from "../keyboardShortcuts";
|
|
10
10
|
|
|
11
11
|
describe("keyboardShortcuts", () => {
|
|
12
12
|
it("treats shift+space as retreat and plain space as advance", () => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
2
|
-
import { describe, expect, it, vi } from "
|
|
3
|
-
import { AddonProvider } from "
|
|
2
|
+
import { describe, expect, it, vi } from "vite-plus/test";
|
|
3
|
+
import { AddonProvider } from "../../../../addons/AddonProvider";
|
|
4
4
|
import { DEFAULT_SLIDES_VIEWPORT } from "@slidev-react/core/slides/viewport";
|
|
5
|
-
import { ThemeProvider } from "
|
|
6
|
-
import { FlowTimelinePreview } from "
|
|
7
|
-
import type { CompiledSlide } from "
|
|
5
|
+
import { ThemeProvider } from "../../../../theme/ThemeProvider";
|
|
6
|
+
import { FlowTimelinePreview } from "../FlowTimelinePreview";
|
|
7
|
+
import type { CompiledSlide } from "../types";
|
|
8
8
|
|
|
9
9
|
const demoSlide: CompiledSlide = {
|
|
10
10
|
id: "timeline-demo",
|
package/src/features/presentation/presenter/{persistence.test.ts → __tests__/persistence.test.ts}
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { describe, expect, it } from "
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
2
|
import {
|
|
3
3
|
parsePersistedPresenterCursorMode,
|
|
4
4
|
parsePersistedPresenterSidebarWidth,
|
|
5
5
|
parsePersistedPresenterStageScale,
|
|
6
|
-
} from "
|
|
6
|
+
} from "../persistence";
|
|
7
7
|
|
|
8
8
|
describe("presenter persistence", () => {
|
|
9
9
|
it("parses allowed stage scale values", () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "
|
|
2
|
-
import { buildPresentationSharedState, mapRemotePresentationPatch } from "
|
|
1
|
+
import { describe, expect, it, vi } from "vite-plus/test";
|
|
2
|
+
import { buildPresentationSharedState, mapRemotePresentationPatch } from "../presentationSyncBridge";
|
|
3
3
|
|
|
4
4
|
describe("presentationSyncBridge", () => {
|
|
5
5
|
it("builds a shared state payload without mutating timestamps", () => {
|
|
@@ -35,7 +35,7 @@ function cloneWithRevealClass(
|
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export function
|
|
38
|
+
export function Step({
|
|
39
39
|
step,
|
|
40
40
|
preset = "fade-up",
|
|
41
41
|
asChild = false,
|
|
@@ -71,7 +71,7 @@ export function Reveal({
|
|
|
71
71
|
);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
export function
|
|
74
|
+
export function Steps({
|
|
75
75
|
start = 1,
|
|
76
76
|
increment = 1,
|
|
77
77
|
preset = "fade-up",
|
|
@@ -96,7 +96,7 @@ export function RevealGroup({
|
|
|
96
96
|
|
|
97
97
|
if (isValidElement(child)) {
|
|
98
98
|
return (
|
|
99
|
-
<
|
|
99
|
+
<Step
|
|
100
100
|
key={child.key ?? step}
|
|
101
101
|
step={step}
|
|
102
102
|
preset={preset}
|
|
@@ -104,14 +104,14 @@ export function RevealGroup({
|
|
|
104
104
|
asChild
|
|
105
105
|
>
|
|
106
106
|
{child}
|
|
107
|
-
</
|
|
107
|
+
</Step>
|
|
108
108
|
);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
return (
|
|
112
|
-
<
|
|
112
|
+
<Step key={step} step={step} preset={preset} reserveSpace={reserveSpace}>
|
|
113
113
|
{child}
|
|
114
|
-
</
|
|
114
|
+
</Step>
|
|
115
115
|
);
|
|
116
116
|
})}
|
|
117
117
|
</>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
|
+
import { resolveSlideSurface, resolveSlideSurfaceClassName } from "../slideSurface";
|
|
3
|
+
|
|
4
|
+
describe("resolveSlideSurfaceClassName", () => {
|
|
5
|
+
it("includes base classes for a default layout", () => {
|
|
6
|
+
const result = resolveSlideSurfaceClassName({ layout: "default" });
|
|
7
|
+
expect(result).toContain("slide-prose");
|
|
8
|
+
expect(result).toContain("slide-surface-frame");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("uses zero padding for immersive layout", () => {
|
|
12
|
+
const result = resolveSlideSurfaceClassName({ layout: "immersive" });
|
|
13
|
+
expect(result).toContain("px-0 py-0");
|
|
14
|
+
expect(result).not.toContain("slide-surface-frame");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("includes overflow-hidden when requested", () => {
|
|
18
|
+
const result = resolveSlideSurfaceClassName({
|
|
19
|
+
layout: "default",
|
|
20
|
+
overflowHidden: true,
|
|
21
|
+
});
|
|
22
|
+
expect(result).toContain("overflow-hidden");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("omits overflow-hidden by default", () => {
|
|
26
|
+
const result = resolveSlideSurfaceClassName({ layout: "default" });
|
|
27
|
+
expect(result).not.toContain("overflow-hidden");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("appends custom shadow class", () => {
|
|
31
|
+
const result = resolveSlideSurfaceClassName({
|
|
32
|
+
layout: "default",
|
|
33
|
+
shadowClass: "shadow-lg",
|
|
34
|
+
});
|
|
35
|
+
expect(result).toContain("shadow-lg");
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("resolveSlideSurface", () => {
|
|
40
|
+
it("returns default white background when no background is set", () => {
|
|
41
|
+
const result = resolveSlideSurface({
|
|
42
|
+
meta: { layout: "default" },
|
|
43
|
+
});
|
|
44
|
+
expect(result.style.backgroundColor).toBe("#ffffff");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("detects bare image URLs and sets backgroundImage", () => {
|
|
48
|
+
const result = resolveSlideSurface({
|
|
49
|
+
meta: { layout: "default", background: "https://example.com/bg.jpg" },
|
|
50
|
+
});
|
|
51
|
+
expect(result.style.backgroundImage).toContain("url(");
|
|
52
|
+
expect(result.style.backgroundSize).toBe("cover");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("detects relative image paths", () => {
|
|
56
|
+
const result = resolveSlideSurface({
|
|
57
|
+
meta: { layout: "default", background: "./images/bg.png" },
|
|
58
|
+
});
|
|
59
|
+
expect(result.style.backgroundImage).toContain("url(");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("detects data URI images", () => {
|
|
63
|
+
const result = resolveSlideSurface({
|
|
64
|
+
meta: { layout: "default", background: "data:image/svg+xml;base64,abc" },
|
|
65
|
+
});
|
|
66
|
+
expect(result.style.backgroundImage).toContain("url(");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("treats CSS values as raw background property", () => {
|
|
70
|
+
const result = resolveSlideSurface({
|
|
71
|
+
meta: { layout: "default", background: "linear-gradient(to right, #000, #fff)" },
|
|
72
|
+
});
|
|
73
|
+
expect(result.style.background).toContain("linear-gradient");
|
|
74
|
+
expect(result.style.backgroundImage).toBeUndefined();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("falls back to slidesBackground when slide has no background", () => {
|
|
78
|
+
const result = resolveSlideSurface({
|
|
79
|
+
meta: { layout: "default" },
|
|
80
|
+
slidesBackground: "#333",
|
|
81
|
+
});
|
|
82
|
+
expect(result.style.background).toBe("#333");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("merges meta.class into className", () => {
|
|
86
|
+
const result = resolveSlideSurface({
|
|
87
|
+
meta: { layout: "default", class: "custom-slide" },
|
|
88
|
+
className: "base-class",
|
|
89
|
+
});
|
|
90
|
+
expect(result.className).toContain("base-class");
|
|
91
|
+
expect(result.className).toContain("custom-slide");
|
|
92
|
+
});
|
|
93
|
+
});
|
package/src/features/presentation/sync/model/{presence.test.ts → __tests__/presence.test.ts}
RENAMED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { describe, expect, it } from "
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
2
|
import {
|
|
3
3
|
countPeers,
|
|
4
4
|
markPeerSeen,
|
|
5
5
|
removePeer,
|
|
6
6
|
resolveRemoteActive,
|
|
7
7
|
sweepStalePeers,
|
|
8
|
-
} from "
|
|
8
|
+
} from "../presence";
|
|
9
9
|
|
|
10
10
|
describe("presence model", () => {
|
|
11
11
|
it("tracks peer activity and counts peers", () => {
|
package/src/features/presentation/sync/model/{replication.test.ts → __tests__/replication.test.ts}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "
|
|
1
|
+
import { describe, expect, it, vi } from "vite-plus/test";
|
|
2
2
|
import {
|
|
3
3
|
canAuthorState,
|
|
4
4
|
canReceive,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
createEnvelope,
|
|
7
7
|
createSnapshotState,
|
|
8
8
|
isCursorEqual,
|
|
9
|
-
} from "
|
|
9
|
+
} from "../replication";
|
|
10
10
|
|
|
11
11
|
describe("replication model", () => {
|
|
12
12
|
it("creates join envelopes with protocol metadata", () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { describe, expect, it } from "
|
|
2
|
-
import { resolvePresentationSyncStatus } from "
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
|
+
import { resolvePresentationSyncStatus } from "../status";
|
|
3
3
|
|
|
4
4
|
describe("resolvePresentationSyncStatus", () => {
|
|
5
5
|
it("returns disabled when the session is disabled", () => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { describe, expect, it } from "
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_SLIDES_VIEWPORT,
|
|
4
4
|
resolveSlidesViewportMeta,
|
|
5
5
|
} from "@slidev-react/core/slides/viewport";
|
|
6
|
-
import { resolveSlideTheme } from "
|
|
7
|
-
import { resolveThemeRootAttributes } from "
|
|
6
|
+
import { resolveSlideTheme } from "../registry";
|
|
7
|
+
import { resolveThemeRootAttributes } from "../ThemeProvider";
|
|
8
8
|
|
|
9
9
|
describe("ThemeProvider root attributes", () => {
|
|
10
10
|
it("keeps theme attributes when no viewport is provided", () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { describe, expect, it } from "
|
|
2
|
-
import { Badge } from "
|
|
3
|
-
import { defaultLayouts } from "
|
|
4
|
-
import { resolveSlideTheme } from "
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
|
+
import { Badge } from "../../ui/primitives/Badge";
|
|
3
|
+
import { defaultLayouts } from "../layouts/defaultLayouts";
|
|
4
|
+
import { resolveSlideTheme } from "../registry";
|
|
5
5
|
|
|
6
6
|
describe("theme registry", () => {
|
|
7
7
|
it("falls back to the default theme when no active theme is set", () => {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
|
+
import { defaultLayouts } from "../defaultLayouts";
|
|
3
|
+
import { resolveLayout } from "../resolveLayout";
|
|
4
|
+
|
|
5
|
+
describe("resolveLayout", () => {
|
|
6
|
+
it("returns the default layout when layout is undefined", () => {
|
|
7
|
+
expect(resolveLayout(undefined)).toBe(defaultLayouts.default);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("returns the matching layout from the default registry", () => {
|
|
11
|
+
expect(resolveLayout("cover")).toBe(defaultLayouts.cover);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("falls back to default layout for an unknown layout name", () => {
|
|
15
|
+
expect(resolveLayout("nonexistent")).toBe(defaultLayouts.default);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("uses a custom registry when provided", () => {
|
|
19
|
+
const customDefault = () => null;
|
|
20
|
+
const customCover = () => null;
|
|
21
|
+
const registry = { default: customDefault, cover: customCover };
|
|
22
|
+
|
|
23
|
+
expect(resolveLayout("cover", registry)).toBe(customCover);
|
|
24
|
+
expect(resolveLayout(undefined, registry)).toBe(customDefault);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("falls back to defaultLayouts.default when custom registry has no default", () => {
|
|
28
|
+
const registry = { cover: () => null };
|
|
29
|
+
expect(resolveLayout("missing", registry)).toBe(defaultLayouts.default);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -3,7 +3,7 @@ import { useEffect, useMemo, useState } from "react";
|
|
|
3
3
|
import { createHighlighter } from "shiki";
|
|
4
4
|
import { ShikiMagicMove } from "shiki-magic-move/react";
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const DEFAULT_STEPS = [
|
|
7
7
|
`const message = 'Hello'
|
|
8
8
|
const target = 'world'
|
|
9
9
|
|
|
@@ -32,7 +32,8 @@ function getHighlighter() {
|
|
|
32
32
|
return highlighterPromise;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export function
|
|
35
|
+
export function CodeMagicMove({ steps }: { steps?: string[] }) {
|
|
36
|
+
const resolvedSteps = steps && steps.length > 0 ? steps : DEFAULT_STEPS;
|
|
36
37
|
const [stepIndex, setStepIndex] = useState(0);
|
|
37
38
|
const [highlighter, setHighlighter] = useState<HighlighterCore>();
|
|
38
39
|
|
|
@@ -51,7 +52,7 @@ export function MagicMoveDemo() {
|
|
|
51
52
|
};
|
|
52
53
|
}, []);
|
|
53
54
|
|
|
54
|
-
const code = useMemo(() =>
|
|
55
|
+
const code = useMemo(() => resolvedSteps[stepIndex], [resolvedSteps, stepIndex]);
|
|
55
56
|
|
|
56
57
|
if (!highlighter) {
|
|
57
58
|
return (
|
|
@@ -79,31 +80,33 @@ export function MagicMoveDemo() {
|
|
|
79
80
|
lineNumbers: false,
|
|
80
81
|
splitTokens: false,
|
|
81
82
|
enhanceMatching: true,
|
|
82
|
-
animateContainer:
|
|
83
|
-
containerStyle: false,
|
|
83
|
+
animateContainer: true,
|
|
84
84
|
}}
|
|
85
85
|
/>
|
|
86
86
|
</div>
|
|
87
|
-
<div className="flex flex-wrap gap-2">
|
|
87
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
88
88
|
<button
|
|
89
89
|
type="button"
|
|
90
|
-
className="rounded-lg bg-
|
|
90
|
+
className="rounded-lg bg-emerald-600 px-3.5 py-1.5 text-sm font-medium text-white shadow-sm transition hover:bg-emerald-500 active:bg-emerald-700 disabled:opacity-40 disabled:hover:bg-emerald-600"
|
|
91
91
|
onClick={() => setStepIndex((index) => Math.max(index - 1, 0))}
|
|
92
92
|
disabled={stepIndex === 0}
|
|
93
93
|
>
|
|
94
|
-
Prev
|
|
94
|
+
← Prev
|
|
95
95
|
</button>
|
|
96
96
|
<button
|
|
97
97
|
type="button"
|
|
98
|
-
className="rounded-lg bg-
|
|
99
|
-
onClick={() => setStepIndex((index) => Math.min(index + 1,
|
|
100
|
-
disabled={stepIndex >=
|
|
98
|
+
className="rounded-lg bg-emerald-600 px-3.5 py-1.5 text-sm font-medium text-white shadow-sm transition hover:bg-emerald-500 active:bg-emerald-700 disabled:opacity-40 disabled:hover:bg-emerald-600"
|
|
99
|
+
onClick={() => setStepIndex((index) => Math.min(index + 1, resolvedSteps.length - 1))}
|
|
100
|
+
disabled={stepIndex >= resolvedSteps.length - 1}
|
|
101
101
|
>
|
|
102
|
-
Next
|
|
102
|
+
Next →
|
|
103
103
|
</button>
|
|
104
|
+
<span className="text-xs text-slate-400">
|
|
105
|
+
{stepIndex + 1} / {resolvedSteps.length}
|
|
106
|
+
</span>
|
|
104
107
|
<button
|
|
105
108
|
type="button"
|
|
106
|
-
className="rounded-lg
|
|
109
|
+
className="ml-auto rounded-lg border border-slate-200 bg-white/80 px-3 py-1.5 text-sm font-medium text-slate-600 shadow-sm transition hover:bg-slate-50 active:bg-slate-100"
|
|
107
110
|
onClick={() => setStepIndex(0)}
|
|
108
111
|
>
|
|
109
112
|
Reset
|
package/src/ui/mdx/index.ts
CHANGED
|
@@ -2,19 +2,19 @@ import { PlantUmlDiagram } from "../diagrams/PlantUmlDiagram"
|
|
|
2
2
|
import { Annotate } from "../primitives/Annotate"
|
|
3
3
|
import { Badge } from "../primitives/Badge"
|
|
4
4
|
import { Callout } from "../primitives/Callout"
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { CodeMagicMove } from "./CodeMagicMove"
|
|
6
|
+
import { Step, Steps } from "../../features/presentation/reveal/Reveal"
|
|
7
7
|
import { CourseCover } from "../../../../../components/CourseCover"
|
|
8
8
|
import { MinimaxReactVisualizer } from "../../../../../components/MinimaxReactVisualizer"
|
|
9
9
|
|
|
10
10
|
export const mdxComponents = {
|
|
11
11
|
Badge,
|
|
12
12
|
Callout,
|
|
13
|
-
|
|
13
|
+
CodeMagicMove,
|
|
14
14
|
Annotate,
|
|
15
15
|
PlantUmlDiagram,
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
Step,
|
|
17
|
+
Steps,
|
|
18
18
|
CourseCover,
|
|
19
19
|
MinimaxReactVisualizer,
|
|
20
20
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
|
|
3
3
|
export function Badge({ children }: { children: ReactNode }) {
|
|
4
|
-
return
|
|
4
|
+
return (
|
|
5
|
+
<span className="inline-block rounded-full border border-emerald-500/16 bg-green-100 px-2 py-0.5 text-sm font-semibold leading-snug text-green-800">
|
|
6
|
+
{children}
|
|
7
|
+
</span>
|
|
8
|
+
);
|
|
5
9
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
2
|
-
import { describe, expect, it, vi } from "
|
|
2
|
+
import { describe, expect, it, vi } from "vite-plus/test";
|
|
3
3
|
import {
|
|
4
4
|
RevealProvider,
|
|
5
5
|
type RevealContextValue,
|
|
6
|
-
} from "
|
|
7
|
-
import { Annotate } from "
|
|
6
|
+
} from "../../../features/presentation/reveal/RevealContext";
|
|
7
|
+
import { Annotate } from "../Annotate";
|
|
8
8
|
|
|
9
9
|
function createRevealValue(clicks: number): RevealContextValue {
|
|
10
10
|
return {
|