Skip to content

Commit f061775

Browse files
committed
Enhancement: Add open slide source action and toggle presentation mode functionality; #68 #67
1 parent dc818c7 commit f061775

File tree

9 files changed

+90
-6
lines changed

9 files changed

+90
-6
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
## [0.0.85] - 2025-03-xx
44

55
- [#58](https://github.com/estruyf/vscode-demo-time/issues/58): Support theme changes in Shiki codeblocks on slides
6+
- [#67](https://github.com/estruyf/vscode-demo-time/issues/67): Add the open slide source action to the slide view
7+
- [#68](https://github.com/estruyf/vscode-demo-time/issues/68): Add the toggle action for the presentation mode to the slide view
68

79
## [0.0.84] - 2025-03-18
810

src/constants/WebViewMessages.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const WebViewMessages = {
1616
getSlideTheme: "getSlideTheme",
1717
updateTitle: "updateTitle",
1818
getPreviousEnabled: "getPreviousEnabled",
19+
openFile: "openFile",
1920
},
2021
toWebview: {
2122
updateClock: "updateClock",
@@ -29,5 +30,6 @@ export const WebViewMessages = {
2930
updateFileUri: "updateFileUri",
3031
triggerUpdate: "triggerUpdate",
3132
updateStyles: "updateStyles",
33+
updateIsInPresentationMode: "updateIsInPresentationMode",
3234
},
3335
};

src/presenterView/PresenterView.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export class PresenterView {
112112
const startTime = DemoStatusBar.getCountdownStarted();
113113
PresenterView.postRequestMessage(command, requestId, startTime);
114114
} else if (command === WebViewMessages.toVscode.getPresentationStarted) {
115-
const isPresentationMode = DemoRunner.getIsPresentationMode;
115+
const isPresentationMode = DemoRunner.getIsPresentationMode();
116116
PresenterView.postRequestMessage(command, requestId, isPresentationMode);
117117
} else if (command === WebViewMessages.toVscode.detach) {
118118
const panel = PresenterView.webview;

src/preview/Preview.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Extension } from "../services/Extension";
33
import { COMMAND, Config, WebViewMessages } from "../constants";
44
import { MessageHandlerData } from "@estruyf/vscode";
55
import { getTheme, getWebviewUrl, readFile, togglePresentationView } from "../utils";
6+
import { DemoRunner } from "../services";
67

78
export class Preview {
89
private static webview: WebviewPanel | null = null;
@@ -119,10 +120,21 @@ export class Preview {
119120
Preview.postRequestMessage(WebViewMessages.toVscode.getPreviousEnabled, requestId, previousEnabled);
120121
} else if (command === WebViewMessages.toVscode.runCommand && payload) {
121122
await commands.executeCommand(payload);
123+
} else if (command === WebViewMessages.toVscode.getPresentationStarted) {
124+
const isPresentationMode = DemoRunner.getIsPresentationMode();
125+
Preview.postRequestMessage(command, requestId, isPresentationMode);
126+
} else if (command === WebViewMessages.toVscode.openFile && payload) {
127+
const fileUri = Uri.parse(payload);
128+
const filePath = Uri.file(fileUri.path);
129+
await window.showTextDocument(filePath);
122130
}
123131
}
124132

125133
public static async postMessage(command: string, payload: any) {
134+
if (Preview.isDisposed) {
135+
return;
136+
}
137+
126138
Preview.webview?.webview.postMessage({
127139
command,
128140
payload,

src/preview/components/MarkdownPreview.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export const MarkdownPreview: React.FunctionComponent<IMarkdownPreviewProps> = (
122122
</div>
123123
</div>
124124

125-
<SlideControls show={showControls && cursorVisible} />
125+
<SlideControls show={showControls && cursorVisible} path={crntFilePath} />
126126
</div>
127127
);
128128
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import * as React from 'react';
2+
3+
export interface IProjectorIconProps {
4+
className?: string;
5+
}
6+
7+
export const ProjectorIcon: React.FunctionComponent<IProjectorIconProps> = ({
8+
className
9+
}: React.PropsWithChildren<IProjectorIconProps>) => {
10+
return (
11+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className || ""}>
12+
<path stroke="currentcolor" d="M5 7L3 5" />
13+
<path stroke="currentcolor" d="M9 6V3" />
14+
<path stroke="currentcolor" d="M13 7L15 5" />
15+
<circle stroke="currentcolor" cx="9" cy="13" r="3" />
16+
<path stroke="currentcolor" d="M11.83 12H20a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h2.17" />
17+
<path stroke="currentcolor" d="M16 16h2" />
18+
</svg>
19+
);
20+
};

src/preview/components/SlideControl.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export interface ISlideControlProps {
88
title: string;
99
disabled?: boolean;
1010
isSlideControl?: boolean;
11+
className?: string;
1112
}
1213

1314
export const SlideControl: React.FunctionComponent<ISlideControlProps> = ({
@@ -17,13 +18,14 @@ export const SlideControl: React.FunctionComponent<ISlideControlProps> = ({
1718
title,
1819
disabled,
1920
isSlideControl,
21+
className,
2022
}: React.PropsWithChildren<ISlideControlProps>) => {
2123
return (
2224
<button
2325
title={title}
2426
onClick={action}
2527
disabled={disabled}
26-
className={`p-2 inline-flex justify-center items-center rounded hover:bg-[var(--vscode-toolbar-hoverBackground)] disabled:opacity-50 disabled:cursor-not-allowed ${!isSlideControl ? ' opacity-70 hover:opacity-100' : ''}`}
28+
className={`p-2 inline-flex justify-center items-center rounded disabled:opacity-50 disabled:cursor-not-allowed ${!isSlideControl ? ' opacity-70 hover:opacity-100' : ''} ${className ? className : "hover:bg-[var(--vscode-toolbar-hoverBackground)]"}`}
2729
>
2830
<span className="sr-only">{title}</span>
2931
{iconName && <Icon name={iconName as any} className="!text-[var(--vscode-editorWidget-foreground)] inline-flex justify-center items-center" />}

src/preview/components/SlideControls.tsx

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,34 @@
1-
import { messageHandler } from '@estruyf/vscode/dist/client/webview';
1+
import { messageHandler, Messenger } from '@estruyf/vscode/dist/client/webview';
22
import * as React from 'react';
33
import { COMMAND, WebViewMessages } from '../../constants';
44
import { SlideControl } from './SlideControl';
55
import { WhiteboardIcon } from './WhiteboardIcon';
66
import { Icon } from 'vscrui';
7+
import { ProjectorIcon } from './ProjectorIcon';
8+
import { EventData } from '@estruyf/vscode';
79

810
export interface ISlideControlsProps {
911
show: boolean;
12+
path?: string;
1013
}
1114

1215
export const SlideControls: React.FunctionComponent<ISlideControlsProps> = ({
13-
show
16+
show,
17+
path
1418
}: React.PropsWithChildren<ISlideControlsProps>) => {
1519
const [previousEnabled, setPreviousEnabled] = React.useState(false);
20+
const [isPresentationMode, setIsPresentationMode] = React.useState(false);
21+
22+
const messageListener = (message: MessageEvent<EventData<any>>) => {
23+
const { command, payload } = message.data;
24+
if (!command) {
25+
return;
26+
}
27+
28+
if (command === WebViewMessages.toWebview.updateIsInPresentationMode) {
29+
setIsPresentationMode(payload);
30+
}
31+
};
1632

1733
const previous = React.useCallback(() => {
1834
if (previousEnabled) {
@@ -40,6 +56,14 @@ export const SlideControls: React.FunctionComponent<ISlideControlsProps> = ({
4056
messageHandler.send(WebViewMessages.toVscode.runCommand, "demo-time.focus");
4157
}, []);
4258

59+
const togglePresentationMode = React.useCallback(() => {
60+
messageHandler.send(WebViewMessages.toVscode.runCommand, "demo-time.togglePresentationMode");
61+
}, []);
62+
63+
const openSlideSource = React.useCallback(() => {
64+
messageHandler.send(WebViewMessages.toVscode.openFile, path);
65+
}, [path]);
66+
4367
React.useEffect(() => {
4468
if (show) {
4569
messageHandler.request<boolean>(WebViewMessages.toVscode.getPreviousEnabled).then((previous) => {
@@ -48,6 +72,19 @@ export const SlideControls: React.FunctionComponent<ISlideControlsProps> = ({
4872
}
4973
}, [show]);
5074

75+
React.useEffect(() => {
76+
messageHandler.request<boolean>(WebViewMessages.toVscode.getPresentationStarted).then((value) => {
77+
console.log("value", value);
78+
setIsPresentationMode(value);
79+
});
80+
81+
Messenger.listen(messageListener);
82+
83+
return () => {
84+
Messenger.unlisten(messageListener);
85+
};
86+
}, []);
87+
5188
return (
5289
<div
5390
className={`absolute bottom-0 w-full transition-opacity duration-300 ${show ? 'opacity-90' : 'opacity-0 pointer-events-none'
@@ -58,6 +95,7 @@ export const SlideControls: React.FunctionComponent<ISlideControlsProps> = ({
5895
style={{ boxShadow: '0 0 8px 0 var(--vscode-widget-shadow)' }}
5996
>
6097
<div className='flex items-center'>
98+
<SlideControl title="Toggle presentation mode" className={`${isPresentationMode ? `bg-[var(--vscode-statusBarItem-errorBackground)] hover:-[var(--vscode-statusBarItem-errorHoverBackground)]` : ''}`} icon={<ProjectorIcon className={`w-4 h-4 inline-flex justify-center items-center ${isPresentationMode ? `text-[var(--vscode-statusBarItem-errorForeground)] hover:text-[var(--vscode-statusBarItem-errorHoverForeground)]` : `text-[var(--vscode-editorWidget-foreground)]`}`} />} action={togglePresentationMode} />
6199
<SlideControl title="Toggle fullscreen" iconName="screen-full" action={toggleFullscreen} />
62100
<SlideControl title="Toggle presentation view" icon={<WhiteboardIcon className="w-4 h-4 text-[var(--vscode-editorWidget-foreground)] inline-flex justify-center items-center" />} action={togglePresentationView} />
63101
<SlideControl title="Close sidebar" icon={(
@@ -80,7 +118,13 @@ export const SlideControls: React.FunctionComponent<ISlideControlsProps> = ({
80118

81119
<SlideControl title="Next" iconName="arrow-right" action={next} isSlideControl />
82120
</div>
83-
<div></div>
121+
<div className="flex items-center justify-end gap-4">
122+
{
123+
path && (
124+
<SlideControl title="Open slide source" iconName="preview" action={openSlideSource} />
125+
)
126+
}
127+
</div>
84128
</div>
85129
</div>
86130
);

src/services/DemoRunner.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,10 @@ export class DemoRunner {
169169
if (DemoRunner.isPresentationMode) {
170170
DemoPanel.updateMessage("Presentation mode enabled");
171171
await DemoRunner.getDemoFile();
172+
Preview.postMessage(WebViewMessages.toWebview.updateIsInPresentationMode, true);
172173
} else {
173174
DemoPanel.updateMessage();
175+
Preview.postMessage(WebViewMessages.toWebview.updateIsInPresentationMode, false);
174176
}
175177
DemoPanel.update();
176178
}

0 commit comments

Comments
 (0)