-
-
Notifications
You must be signed in to change notification settings - Fork 20
Add Visual Theme Builder Pro Feature #355
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 4 commits
497d8c6
12dc828
6e956c1
c5552ea
b802f20
5ea509d
4a9a473
1d7bcd7
dc7d2db
a9c1e20
0c7b22e
f089ecc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| export type WebviewType = 'presenter' | 'preview' | 'settings' | 'config-editor' | 'overview'; | ||
| export type WebviewType = 'presenter' | 'preview' | 'settings' | 'config-editor' | 'overview' | 'themeBuilder'; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,340 @@ | ||||||||||||||||||||||||||||||||||||||
| import { Uri, ViewColumn, WebviewPanel } from 'vscode'; | ||||||||||||||||||||||||||||||||||||||
| import { WebviewType } from '../models'; | ||||||||||||||||||||||||||||||||||||||
| import { Extension } from '../services'; | ||||||||||||||||||||||||||||||||||||||
| import { Config, WebViewMessages } from '@demotime/common'; | ||||||||||||||||||||||||||||||||||||||
| import { BaseWebview } from '../webview/BaseWebviewPanel'; | ||||||||||||||||||||||||||||||||||||||
| import { General } from '../constants'; | ||||||||||||||||||||||||||||||||||||||
| import { fileExists, readFile, writeFile, sanitizeFileName } from '../utils'; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| export class ThemeBuilderPanel extends BaseWebview { | ||||||||||||||||||||||||||||||||||||||
| public static id: WebviewType = 'themeBuilder'; | ||||||||||||||||||||||||||||||||||||||
| public static title: string = `${Config.title}: Theme Builder`; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||
| * Render the theme builder panel | ||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||
| public static async render() { | ||||||||||||||||||||||||||||||||||||||
| if (ThemeBuilderPanel.isOpen) { | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.reveal(); | ||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||
| await ThemeBuilderPanel.create(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| protected static onCreate() { | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.isDisposed = false; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| protected static onDispose() { | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.isDisposed = true; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| protected static async messageListener(message: any) { | ||||||||||||||||||||||||||||||||||||||
| const { command, requestId, payload } = message; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (!command) { | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| switch (command) { | ||||||||||||||||||||||||||||||||||||||
| case WebViewMessages.toVscode.themeBuilder.getExistingThemes: | ||||||||||||||||||||||||||||||||||||||
| await ThemeBuilderPanel.getExistingThemes(command, requestId); | ||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||
| case WebViewMessages.toVscode.themeBuilder.getExistingLayouts: | ||||||||||||||||||||||||||||||||||||||
| await ThemeBuilderPanel.getExistingLayouts(command, requestId); | ||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||
| case WebViewMessages.toVscode.themeBuilder.loadTheme: | ||||||||||||||||||||||||||||||||||||||
| await ThemeBuilderPanel.loadTheme(command, requestId, payload); | ||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||
| case WebViewMessages.toVscode.themeBuilder.loadLayout: | ||||||||||||||||||||||||||||||||||||||
| await ThemeBuilderPanel.loadLayout(command, requestId, payload); | ||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||
| case WebViewMessages.toVscode.themeBuilder.saveTheme: | ||||||||||||||||||||||||||||||||||||||
| await ThemeBuilderPanel.saveTheme(command, requestId, payload); | ||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||
| case WebViewMessages.toVscode.themeBuilder.saveLayout: | ||||||||||||||||||||||||||||||||||||||
| await ThemeBuilderPanel.saveLayout(command, requestId, payload); | ||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||
| case WebViewMessages.toVscode.themeBuilder.getPreviewHtml: | ||||||||||||||||||||||||||||||||||||||
| await ThemeBuilderPanel.getPreviewHtml(command, requestId, payload); | ||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||
| * Get list of existing themes | ||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||
| private static async getExistingThemes(command: string, requestId: string) { | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| const wsFolder = Extension.getInstance().workspaceFolder; | ||||||||||||||||||||||||||||||||||||||
| if (!wsFolder) { | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, []); | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const slidesFolder = Uri.joinPath(wsFolder.uri, General.demoFolder, General.slidesFolder); | ||||||||||||||||||||||||||||||||||||||
| const themeFiles: { name: string; path: string }[] = []; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Look for CSS files in slides folder | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| const files = await Extension.getInstance().context.fs.readDirectory(slidesFolder); | ||||||||||||||||||||||||||||||||||||||
| for (const [file, type] of files) { | ||||||||||||||||||||||||||||||||||||||
| if (type === 1 && file.endsWith('.css')) { | ||||||||||||||||||||||||||||||||||||||
| // File type | ||||||||||||||||||||||||||||||||||||||
| themeFiles.push({ | ||||||||||||||||||||||||||||||||||||||
| name: file.replace('.css', ''), | ||||||||||||||||||||||||||||||||||||||
| path: file, | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In
Suggested change
|
||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||
| // Folder might not exist yet | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, themeFiles); | ||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||
| console.error('Error getting existing themes:', error); | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, []); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||
| * Get list of existing layouts | ||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||
| private static async getExistingLayouts(command: string, requestId: string) { | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| const wsFolder = Extension.getInstance().workspaceFolder; | ||||||||||||||||||||||||||||||||||||||
| if (!wsFolder) { | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, []); | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const layoutsFolder = Uri.joinPath(wsFolder.uri, General.demoFolder, 'layouts'); | ||||||||||||||||||||||||||||||||||||||
| const layoutFiles: { name: string; path: string }[] = []; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Look for HBS/Handlebars files in layouts folder | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| const files = await Extension.getInstance().context.fs.readDirectory(layoutsFolder); | ||||||||||||||||||||||||||||||||||||||
| for (const [file, type] of files) { | ||||||||||||||||||||||||||||||||||||||
| if (type === 1 && (file.endsWith('.hbs') || file.endsWith('.handlebars'))) { | ||||||||||||||||||||||||||||||||||||||
| // File type | ||||||||||||||||||||||||||||||||||||||
| layoutFiles.push({ | ||||||||||||||||||||||||||||||||||||||
| name: file.replace(/\.(hbs|handlebars)$/, ''), | ||||||||||||||||||||||||||||||||||||||
| path: file, | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||
| // Folder might not exist yet | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, layoutFiles); | ||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||
| console.error('Error getting existing layouts:', error); | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, []); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||
| * Load a theme file | ||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||
| private static async loadTheme(command: string, requestId: string, payload: { name: string }) { | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| const wsFolder = Extension.getInstance().workspaceFolder; | ||||||||||||||||||||||||||||||||||||||
| if (!wsFolder) { | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, null); | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const themePath = Uri.joinPath( | ||||||||||||||||||||||||||||||||||||||
| wsFolder.uri, | ||||||||||||||||||||||||||||||||||||||
| General.demoFolder, | ||||||||||||||||||||||||||||||||||||||
| General.slidesFolder, | ||||||||||||||||||||||||||||||||||||||
| `${payload.name}.css`, | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (!(await fileExists(themePath))) { | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, null); | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const content = await readFile(themePath); | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, { | ||||||||||||||||||||||||||||||||||||||
| name: payload.name, | ||||||||||||||||||||||||||||||||||||||
| content: content?.toString() || '', | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||
| console.error('Error loading theme:', error); | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, null); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||
| * Load a layout file | ||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||
| private static async loadLayout(command: string, requestId: string, payload: { name: string }) { | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| const wsFolder = Extension.getInstance().workspaceFolder; | ||||||||||||||||||||||||||||||||||||||
| if (!wsFolder) { | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, null); | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const layoutPath = Uri.joinPath( | ||||||||||||||||||||||||||||||||||||||
| wsFolder.uri, | ||||||||||||||||||||||||||||||||||||||
| General.demoFolder, | ||||||||||||||||||||||||||||||||||||||
| 'layouts', | ||||||||||||||||||||||||||||||||||||||
| `${payload.name}.hbs`, | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+183
to
+188
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
- const layoutPath = Uri.joinPath(
- wsFolder.uri,
- General.demoFolder,
- 'layouts',
- `${payload.name}.hbs`,
- );
+ const layoutsFolder = Uri.joinPath(
+ wsFolder.uri,
+ General.demoFolder,
+ 'layouts',
+ );
+ const fileBase = payload.name;
+ const fileName =
+ fileBase.endsWith('.hbs') || fileBase.endsWith('.handlebars')
+ ? fileBase
+ : (await fileExists(Uri.joinPath(layoutsFolder, `${fileBase}.hbs`)))
+ ? `${fileBase}.hbs`
+ : `${fileBase}.handlebars`;
+ const layoutPath = Uri.joinPath(layoutsFolder, fileName);
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (!(await fileExists(layoutPath))) { | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, null); | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+186
to
+192
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Multiple handlers use
Suggested change
|
||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const content = await readFile(layoutPath); | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, { | ||||||||||||||||||||||||||||||||||||||
| name: payload.name, | ||||||||||||||||||||||||||||||||||||||
| content: content?.toString() || '', | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||
| console.error('Error loading layout:', error); | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, null); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||
| * Save a theme file | ||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||
| private static async saveTheme( | ||||||||||||||||||||||||||||||||||||||
| command: string, | ||||||||||||||||||||||||||||||||||||||
| requestId: string, | ||||||||||||||||||||||||||||||||||||||
| payload: { name: string; content: string }, | ||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| const wsFolder = Extension.getInstance().workspaceFolder; | ||||||||||||||||||||||||||||||||||||||
| if (!wsFolder) { | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, { success: false }); | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const fileName = sanitizeFileName(payload.name, '.css'); | ||||||||||||||||||||||||||||||||||||||
| const themePath = Uri.joinPath( | ||||||||||||||||||||||||||||||||||||||
| wsFolder.uri, | ||||||||||||||||||||||||||||||||||||||
| General.demoFolder, | ||||||||||||||||||||||||||||||||||||||
| General.slidesFolder, | ||||||||||||||||||||||||||||||||||||||
| fileName, | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
macroscopeapp[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||
| await writeFile(themePath, payload.content); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, { | ||||||||||||||||||||||||||||||||||||||
| success: true, | ||||||||||||||||||||||||||||||||||||||
| path: fileName, | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||
| console.error('Error saving theme:', error); | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, { success: false }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||
| * Save a layout file | ||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||
| private static async saveLayout( | ||||||||||||||||||||||||||||||||||||||
| command: string, | ||||||||||||||||||||||||||||||||||||||
| requestId: string, | ||||||||||||||||||||||||||||||||||||||
| payload: { name: string; content: string }, | ||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| const wsFolder = Extension.getInstance().workspaceFolder; | ||||||||||||||||||||||||||||||||||||||
| if (!wsFolder) { | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, { success: false }); | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Ensure layouts folder exists | ||||||||||||||||||||||||||||||||||||||
| const layoutsFolder = Uri.joinPath(wsFolder.uri, General.demoFolder, 'layouts'); | ||||||||||||||||||||||||||||||||||||||
| if (!(await fileExists(layoutsFolder))) { | ||||||||||||||||||||||||||||||||||||||
| await Extension.getInstance().context.fs.createDirectory(layoutsFolder); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const fileName = sanitizeFileName(payload.name, '.hbs'); | ||||||||||||||||||||||||||||||||||||||
| const layoutPath = Uri.joinPath(layoutsFolder, fileName); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| await writeFile(layoutPath, payload.content); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, { | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+266
to
+268
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||
| success: true, | ||||||||||||||||||||||||||||||||||||||
| path: fileName, | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||
| console.error('Error saving layout:', error); | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, { success: false }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||
| * Get preview HTML for a slide with custom theme/layout | ||||||||||||||||||||||||||||||||||||||
| * Note: This generates HTML for preview in a sandboxed iframe. | ||||||||||||||||||||||||||||||||||||||
| * The content comes from the authenticated user's own files. | ||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||
| private static async getPreviewHtml( | ||||||||||||||||||||||||||||||||||||||
| command: string, | ||||||||||||||||||||||||||||||||||||||
| requestId: string, | ||||||||||||||||||||||||||||||||||||||
| payload: { css?: string; html?: string }, | ||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| // Sanitize CSS: Remove potentially dangerous patterns | ||||||||||||||||||||||||||||||||||||||
| const sanitizedCss = (payload.css || '') | ||||||||||||||||||||||||||||||||||||||
| .replace(/@import\s+/gi, '/* @import blocked */') | ||||||||||||||||||||||||||||||||||||||
| .replace(/javascript:/gi, '/* javascript: blocked */'); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Sanitize HTML: Remove script tags and event handlers | ||||||||||||||||||||||||||||||||||||||
| const sanitizedHtml = (payload.html || '<h1>Preview</h1><p>Add your HTML content to see the preview</p>') | ||||||||||||||||||||||||||||||||||||||
| .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '<!-- script blocked -->') | ||||||||||||||||||||||||||||||||||||||
| .replace(/on\w+\s*=\s*["'][^"']*["']/gi, ''); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Generate a simple preview HTML | ||||||||||||||||||||||||||||||||||||||
| const previewHtml = ` | ||||||||||||||||||||||||||||||||||||||
| <!DOCTYPE html> | ||||||||||||||||||||||||||||||||||||||
| <html> | ||||||||||||||||||||||||||||||||||||||
|
macroscopeapp[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||
| <head> | ||||||||||||||||||||||||||||||||||||||
| <meta charset="UTF-8"> | ||||||||||||||||||||||||||||||||||||||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||||||||||||||||||||||||||||||||||
| <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; font-src 'self';"> | ||||||||||||||||||||||||||||||||||||||
| <style> | ||||||||||||||||||||||||||||||||||||||
| body { | ||||||||||||||||||||||||||||||||||||||
| margin: 0; | ||||||||||||||||||||||||||||||||||||||
| padding: 0; | ||||||||||||||||||||||||||||||||||||||
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; | ||||||||||||||||||||||||||||||||||||||
| background: var(--vscode-editor-background); | ||||||||||||||||||||||||||||||||||||||
| color: var(--vscode-editor-foreground); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| .slide { | ||||||||||||||||||||||||||||||||||||||
| width: 100%; | ||||||||||||||||||||||||||||||||||||||
| height: 100vh; | ||||||||||||||||||||||||||||||||||||||
| display: flex; | ||||||||||||||||||||||||||||||||||||||
| align-items: center; | ||||||||||||||||||||||||||||||||||||||
| justify-content: center; | ||||||||||||||||||||||||||||||||||||||
| padding: 2rem; | ||||||||||||||||||||||||||||||||||||||
| box-sizing: border-box; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| ${sanitizedCss} | ||||||||||||||||||||||||||||||||||||||
| </style> | ||||||||||||||||||||||||||||||||||||||
| </head> | ||||||||||||||||||||||||||||||||||||||
| <body> | ||||||||||||||||||||||||||||||||||||||
| <div class="slide"> | ||||||||||||||||||||||||||||||||||||||
| ${sanitizedHtml} | ||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||
| </body> | ||||||||||||||||||||||||||||||||||||||
| </html> | ||||||||||||||||||||||||||||||||||||||
| `; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, { html: previewHtml }); | ||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||
| console.error('Error generating preview HTML:', error); | ||||||||||||||||||||||||||||||||||||||
| ThemeBuilderPanel.postRequestMessage(command, requestId, { html: '' }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overriding
messageListenerdrops the base handling forgetSetting,getFileContents, andrunCommand. Consider delegating unhandled commands tosuper.messageListener(message)so core webview commands keep working.