Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 36 additions & 54 deletions apps/vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,24 @@
"title": "Toggle Redaction Mode",
"category": "Demo Time",
"icon": "$(eye-closed)"
},
{
"command": "demo-time.toggleAutoProceed",
"title": "Toggle Auto-Proceed (Pause/Resume)",
"category": "Demo Time",
"icon": "$(debug-pause)"
},
{
"command": "demo-time.pauseAutoProceed",
"title": "Pause Auto-Proceed",
"category": "Demo Time",
"icon": "$(debug-pause)"
},
{
"command": "demo-time.resumeAutoProceed",
"title": "Resume Auto-Proceed",
"category": "Demo Time",
"icon": "$(play-circle)"
}
],
"menus": {
Expand Down Expand Up @@ -727,68 +745,21 @@
{
"command": "demo-time.continueDemo",
"when": "demo-time.isWaitingForNext == true"
}
],
"view/title": [
{
"command": "demo-time.togglePresentationMode",
"when": "view == demo-time-scenes && !demo-time.presentation",
"group": "navigation@-1"
},
{
"command": "demo-time.startCountdown",
"when": "view == demo-time-scenes && !demo-time.countdown && demo-time.showTimer",
"group": "navigation@0"
},
{
"command": "demo-time.showPresenterView",
"when": "view == demo-time-scenes",
"group": "navigation@1"
},
{
"command": "demo-time.reset",
"when": "view == demo-time-scenes",
"group": "navigation@2"
"command": "demo-time.toggleAutoProceed",
"when": "demo-time.autoProceedActive == true"
},
{
"command": "demo-time.exportToPdf",
"when": "view == demo-time-scenes",
"group": "navigation@3"
"command": "demo-time.pauseAutoProceed",
"when": "demo-time.autoProceedActive == true && demo-time.autoProceedPaused != true"
},
{
"command": "demo-time.treeviewFind",
"when": "view == demo-time-scenes",
"group": "navigation@4"
},
{
"command": "demo-time.createDemoFile",
"when": "view == demo-time-scenes"
},
{
"command": "demo-time.docs",
"when": "view == demo-time-scenes"
},
{
"command": "demo-time.showOverview",
"when": "view == demo-time-scenes"
},
{
"command": "demo-time.showSettings",
"when": "view == demo-time-scenes"
},
{
"command": "demo-time.showProFeatures",
"when": "view == demo-time-scenes"
},
{
"command": "demo-time.showGallery",
"when": "view == demo-time-scenes"
},
{
"command": "demo-time.openSupportTheProject",
"when": "view == demo-time-scenes"
"command": "demo-time.resumeAutoProceed",
"when": "demo-time.autoProceedActive == true && demo-time.autoProceedPaused == true"
}
],
"view/title": [],
"view/item/context": [
{
"command": "demo-time.start",
Expand Down Expand Up @@ -885,6 +856,12 @@
},
"views": {
"demo-time-container": [
{
"id": "demo-time-quick-actions",
"name": "Quick Actions",
"contextualTitle": "Demo Time Quick Actions",
"icon": "$(zap)"
},
{
"id": "demo-time-scenes",
"name": "Acts & Scenes",
Expand Down Expand Up @@ -934,6 +911,11 @@
"command": "demo-time.hackerTyperNextChunk",
"key": "down",
"when": "demo-time.isHackerTyper == true"
},
{
"command": "demo-time.toggleAutoProceed",
"key": "space",
"when": "demo-time.presentation && demo-time.autoProceedActive == true"
}
],
"viewsWelcome": [
Expand Down
2 changes: 2 additions & 0 deletions apps/vscode-extension/src/constants/ContextKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ export const ContextKeys = {
isHackerTyper: 'demo-time.isHackerTyper',
isWaitingForNext: 'demo-time.isWaitingForNext',
isSponsor: 'demo-time.isSponsor',
autoProceedActive: 'demo-time.autoProceedActive',
autoProceedPaused: 'demo-time.autoProceedPaused',
};
2 changes: 2 additions & 0 deletions apps/vscode-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from './services';
import { DemoPanel } from './panels/DemoPanel';
import { ResourcesPanel } from './panels/ResourcesPanel';
import { QuickActionsPanel } from './panels/QuickActionsPanel';
import { Preview } from './preview/Preview';
import { PresenterView } from './presenterView/PresenterView';
import { ConfigEditorProvider } from './providers/ConfigEditorProvider';
Expand Down Expand Up @@ -55,6 +56,7 @@ export async function activate(context: vscode.ExtensionContext) {
// Services
DecoratorService.register();
DemoPanel.register();
QuickActionsPanel.register();
ResourcesPanel.register();
DemoRunner.registerCommands();
DemoCreator.registerCommands();
Expand Down
31 changes: 31 additions & 0 deletions apps/vscode-extension/src/panels/QuickActionsPanel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { TreeView, window } from 'vscode';
import {
QuickActionsTreeviewProvider,
QuickActionTreeItem,
} from '../providers/QuickActionsTreeviewProvider';

export class QuickActionsPanel {
private static treeView: TreeView<QuickActionTreeItem>;
private static quickActionsProvider: QuickActionsTreeviewProvider;

public static register() {
QuickActionsPanel.quickActionsProvider = new QuickActionsTreeviewProvider();
QuickActionsPanel.treeView = window.createTreeView('demo-time-quick-actions', {
treeDataProvider: QuickActionsPanel.quickActionsProvider,
});
}
Comment on lines +11 to +16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether the Quick Actions TreeView is disposed/subscribed via ExtensionContext.
rg -n --type=ts "createTreeView\\(|QuickActionsPanel\\.register\\(|subscriptions\\.push\\(|\\.dispose\\(" apps/vscode-extension/src

Repository: estruyf/vscode-demo-time

Length of output: 9170


🏁 Script executed:

sed -n '45,70p' apps/vscode-extension/src/extension.ts

Repository: estruyf/vscode-demo-time

Length of output: 769


🏁 Script executed:

sed -n '1,100p' apps/vscode-extension/src/extension.ts | head -50

Repository: estruyf/vscode-demo-time

Length of output: 1568


🏁 Script executed:

cat -n apps/vscode-extension/src/services/Extension.ts | head -80

Repository: estruyf/vscode-demo-time

Length of output: 2326


🏁 Script executed:

sed -n '235,250p' apps/vscode-extension/src/panels/DemoPanel.ts

Repository: estruyf/vscode-demo-time

Length of output: 515


🏁 Script executed:

sed -n '210,235p' apps/vscode-extension/src/panels/DemoPanel.ts

Repository: estruyf/vscode-demo-time

Length of output: 683


🏁 Script executed:

grep -n "public static register" apps/vscode-extension/src/panels/DemoPanel.ts

Repository: estruyf/vscode-demo-time

Length of output: 100


🏁 Script executed:

sed -n '18,45p' apps/vscode-extension/src/panels/DemoPanel.ts

Repository: estruyf/vscode-demo-time

Length of output: 886


🏁 Script executed:

sed -n '180,215p' apps/vscode-extension/src/panels/DemoPanel.ts

Repository: estruyf/vscode-demo-time

Length of output: 830


🏁 Script executed:

sed -n '1,40p' apps/vscode-extension/src/panels/ResourcesPanel.ts

Repository: estruyf/vscode-demo-time

Length of output: 697


Subscribe the TreeView to extension lifecycle via the Extension singleton.

createTreeView returns a disposable resource that should be registered with Extension.getInstance().subscriptions to ensure proper cleanup on extension deactivation, consistent with command and provider registration patterns used throughout the codebase.

The proposed refactor's approach of adding a context parameter doesn't align with this extension's singleton pattern. Instead, add the subscription using the Extension class's existing accessor:

Corrected refactor
   public static register() {
     QuickActionsPanel.quickActionsProvider = new QuickActionsTreeviewProvider();
     QuickActionsPanel.treeView = window.createTreeView('demo-time-quick-actions', {
       treeDataProvider: QuickActionsPanel.quickActionsProvider,
     });
+    Extension.getInstance().subscriptions.push(QuickActionsPanel.treeView);
   }

Note: This same issue affects DemoPanel and ResourcesPanel, which also create TreeViews without subscribing them.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static register() {
QuickActionsPanel.quickActionsProvider = new QuickActionsTreeviewProvider();
QuickActionsPanel.treeView = window.createTreeView('demo-time-quick-actions', {
treeDataProvider: QuickActionsPanel.quickActionsProvider,
});
}
public static register() {
QuickActionsPanel.quickActionsProvider = new QuickActionsTreeviewProvider();
QuickActionsPanel.treeView = window.createTreeView('demo-time-quick-actions', {
treeDataProvider: QuickActionsPanel.quickActionsProvider,
});
Extension.getInstance().subscriptions.push(QuickActionsPanel.treeView);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/vscode-extension/src/panels/QuickActionsPanel.ts` around lines 11 - 16,
QuickActionsPanel.register currently creates a TreeView
(QuickActionsPanel.treeView) but doesn't add the returned disposable to the
extension singleton's subscriptions; change register so after creating the
TreeView you push the disposable into Extension.getInstance().subscriptions (and
keep creating QuickActionsPanel.quickActionsProvider as-is) to ensure cleanup on
deactivation; apply the same subscription fix to DemoPanel and ResourcesPanel
where createTreeView is used.


public static update() {
QuickActionsPanel.quickActionsProvider.update();
}

public static updatePresentationMode(isPresentationMode: boolean) {
QuickActionsPanel.quickActionsProvider.setPresentationMode(isPresentationMode);
QuickActionsPanel.quickActionsProvider.update();
}

public static updateAutoProceed(isActive: boolean, isPaused: boolean, countdown: number) {
QuickActionsPanel.quickActionsProvider.setAutoProceedState(isActive, isPaused, countdown);
QuickActionsPanel.quickActionsProvider.update();
}
}
179 changes: 179 additions & 0 deletions apps/vscode-extension/src/providers/QuickActionsTreeviewProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import {
Event,
EventEmitter,
ProviderResult,
ThemeColor,
ThemeIcon,
TreeDataProvider,
TreeItem,
TreeItemCollapsibleState,
} from 'vscode';
import { COMMAND } from '@demotime/common';

type QuickAction = {
label: string;
description: string;
icon: string;
color?: string;
command: string;
};

export class QuickActionsTreeviewProvider implements TreeDataProvider<QuickActionTreeItem> {
private isPresentationMode = false;
private isAutoProceedActive = false;
private isAutoProceedPaused = false;
private autoProceedCountdown = 0;
private _onDidChangeTreeData = new EventEmitter<QuickActionTreeItem | undefined>();

Check warning on line 26 in apps/vscode-extension/src/providers/QuickActionsTreeviewProvider.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Member '_onDidChangeTreeData' is never reassigned; mark it as `readonly`.

See more on https://sonarcloud.io/project/issues?id=estruyf_vscode-demo-time&issues=AZ5Aoo1wlcqO3lCHnUrs&open=AZ5Aoo1wlcqO3lCHnUrs&pullRequest=404
public readonly onDidChangeTreeData: Event<QuickActionTreeItem | undefined> =
this._onDidChangeTreeData.event;

public setPresentationMode(isPresentationMode: boolean): void {
this.isPresentationMode = isPresentationMode;
}

public setAutoProceedState(isActive: boolean, isPaused: boolean, countdown: number): void {
this.isAutoProceedActive = isActive;
this.isAutoProceedPaused = isPaused;
this.autoProceedCountdown = countdown;
}

getTreeItem(element: QuickActionTreeItem): TreeItem | Thenable<TreeItem> {
return element;
}

getChildren(): ProviderResult<QuickActionTreeItem[]> {
const presentationAction: QuickAction = this.isPresentationMode
? {
label: 'Stop Presentation Mode',
description: 'Leave presentation mode',
icon: 'debug-stop',
color: 'charts.red',
command: COMMAND.togglePresentationMode,
}
: {
label: 'Start Presentation Mode',
description: 'Enter presentation mode',
icon: 'play',
color: 'charts.green',
command: COMMAND.togglePresentationMode,
};

const autoProceedAction: QuickAction | null = this.isAutoProceedActive
? this.isAutoProceedPaused
? {
label: 'Resume Auto-Proceed',
description: 'Resume auto-advance countdown',
icon: 'play-circle',
color: 'charts.green',
command: COMMAND.resumeAutoProceed,
}
: {
label: `Pause Auto-Proceed`,
description: `Next scene in ${this.autoProceedCountdown}s — click to pause`,
icon: 'debug-pause',
color: 'charts.yellow',
command: COMMAND.pauseAutoProceed,
}

Check warning on line 76 in apps/vscode-extension/src/providers/QuickActionsTreeviewProvider.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=estruyf_vscode-demo-time&issues=AZ5Aoo1wlcqO3lCHnUrt&open=AZ5Aoo1wlcqO3lCHnUrt&pullRequest=404
: null;

const actions: QuickAction[] = [
presentationAction,
...(autoProceedAction ? [autoProceedAction] : []),
{
label: 'Start Timer',
description: 'Start countdown timer for your session',
icon: 'watch',
color: 'charts.yellow',
command: COMMAND.startCountdown,
},
{
label: 'Open Presenter View',
description: 'Show presenter notes and controls',
icon: 'open-preview',
command: COMMAND.showPresenterView,
},
{
label: 'Reset Demo State',
description: 'Clear highlights and reset runner state',
icon: 'discard',
color: 'charts.orange',
command: COMMAND.reset,
},
{
label: 'Export Slides to PDF',
description: 'Export your presentation to a PDF file',
icon: 'file-pdf',
command: COMMAND.exportToPdf,
},
{
label: 'Find in Acts & Scenes',
description: 'Focus and search in the Acts & Scenes tree',
icon: 'search',
command: COMMAND.treeviewFind,
},
{
label: 'Create Act File',
description: 'Create a new act file in the .demo folder',
icon: 'new-file',
command: COMMAND.createDemoFile,
},
{
label: 'Open Overview',
description: 'Open the Demo Time overview panel',
icon: 'home',
command: COMMAND.showOverview,
},
{
label: 'Open Settings',
description: 'Open Demo Time settings panel',
icon: 'settings-gear',
command: COMMAND.showSettings,
},
{
label: 'Open PRO Features',
description: 'Discover or unlock PRO capabilities',
icon: 'lock',
color: 'charts.red',
command: COMMAND.showProFeatures,
},
{
label: 'Open Documentation',
description: 'Read docs, guides, and references',
icon: 'book',
command: COMMAND.documentation,
},
{
label: 'Support the Project',
description: 'Sponsor Demo Time development',
icon: 'heart',
color: 'charts.red',
command: COMMAND.openSupportTheProject,
},
];

return actions.map((action) => new QuickActionTreeItem(action));
}

update(): void {
this._onDidChangeTreeData.fire(undefined);
}
}

export class QuickActionTreeItem extends TreeItem {
constructor(action: QuickAction) {
super(action.label, TreeItemCollapsibleState.None);

this.label = action.label;
this.description = action.description;
this.tooltip = action.description;
this.iconPath = new ThemeIcon(
action.icon,
action.color ? new ThemeColor(action.color) : undefined,
);
this.command = {
command: action.command,
title: action.label,
};
this.contextValue = 'demo-time.quick-action';
}
}
Loading