Skip to content

Commit 08d6dce

Browse files
authored
Add auto-render setting to VSCode extension (#97)
* vscode extension add auto-render setting - powered by `should_auto_render()` to determine if file-type is auto-renderable - `package.json` offers new configuration option `matterviz.autoRender` - `activate()` listens for document openings and triggers auto-rendering when applicable - `create_webview_panel` is refactored for better code organization and clarity - tests for auto-render functionality, ensuring correct behavior across various file types and edge cases - add `is_structure_file()` helper function in `lib/io/parse.ts` - fix several failing playwright tests * fix vscode extension not setting color-scheme CSS attribute, causing white form inputs in dark mode - `Trajectory.svelte` use responsive container query font sizes, gaps and padding in controls bar
1 parent e6082ee commit 08d6dce

32 files changed

+649
-592
lines changed

extensions/vscode/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
],
1616
"engines": { "vscode": "^1.96.0" },
1717
"main": "./dist/extension.cjs",
18+
"activationEvents": ["onStartupFinished"],
1819
"contributes": {
1920
"commands": [{
2021
"command": "matterviz.renderStructure",
@@ -50,6 +51,11 @@
5051
"enum": ["auto", "light", "dark", "white", "black"],
5152
"default": "auto",
5253
"description": "Theme for MatterViz visualizations. 'auto' follows VSCode's theme (selecting either 'light' or 'dark'), other options override it."
54+
},
55+
"matterviz.autoRender": {
56+
"type": "boolean",
57+
"default": true,
58+
"description": "Automatically render supported file types (structures and trajectories) when opening them. If rendering fails, falls back to regular editor."
5359
}
5460
}
5561
}

extensions/vscode/src/extension.ts

Lines changed: 101 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { is_structure_file } from '$lib/io/parse'
12
import type { ThemeName } from '$lib/theme'
23
import { AUTO_THEME, COLOR_THEMES, is_valid_theme_mode } from '$lib/theme'
34
import { is_trajectory_file } from '$lib/trajectory/parse'
@@ -17,7 +18,7 @@ interface WebviewData {
1718
theme: ThemeName
1819
}
1920

20-
interface MessageData {
21+
export interface MessageData {
2122
command: string
2223
text?: string
2324
filename?: string
@@ -28,6 +29,12 @@ interface MessageData {
2829
// Track active file watchers by file path
2930
const active_watchers = new Map<string, vscode.FileSystemWatcher>()
3031

32+
// Check if a file should be auto-rendered
33+
export const should_auto_render = (filename: string): boolean => {
34+
if (!filename || typeof filename !== `string`) return false
35+
return is_structure_file(filename) || is_trajectory_file(filename)
36+
}
37+
3138
// Read file from filesystem
3239
export const read_file = (file_path: string): FileData => {
3340
const filename = path.basename(file_path)
@@ -242,70 +249,77 @@ function stop_watching_file(file_path: string): void {
242249
}
243250
}
244251

245-
// Enhanced render function with file watching
246-
export const render = (context: vscode.ExtensionContext, uri?: vscode.Uri) => {
247-
try {
248-
const file = get_file(uri)
249-
const file_path = uri?.fsPath ||
250-
vscode.window.activeTextEditor?.document.fileName
251-
252-
const panel = vscode.window.createWebviewPanel(
253-
`matterviz`,
254-
`MatterViz - ${file.filename}`,
255-
vscode.ViewColumn.Beside,
256-
{
257-
enableScripts: true,
258-
retainContextWhenHidden: true,
259-
localResourceRoots: [
260-
vscode.Uri.joinPath(context.extensionUri, `dist`),
261-
vscode.Uri.joinPath(context.extensionUri, `../../static`),
262-
],
263-
},
264-
)
252+
// Create webview panel with common setup
253+
function create_webview_panel(
254+
context: vscode.ExtensionContext,
255+
file_data: FileData,
256+
file_path?: string,
257+
view_column: vscode.ViewColumn = vscode.ViewColumn.Beside,
258+
): vscode.WebviewPanel {
259+
const panel = vscode.window.createWebviewPanel(
260+
`matterviz`,
261+
`MatterViz - ${file_data.filename}`,
262+
view_column,
263+
{
264+
enableScripts: true,
265+
retainContextWhenHidden: true,
266+
localResourceRoots: [
267+
vscode.Uri.joinPath(context.extensionUri, `dist`),
268+
vscode.Uri.joinPath(context.extensionUri, `../../static`),
269+
],
270+
},
271+
)
265272

266-
if (file_path) start_watching_file(file_path, panel.webview)
273+
if (file_path) start_watching_file(file_path, panel.webview)
267274

268-
panel.webview.html = create_html(panel.webview, context, {
269-
type: is_trajectory_file(file.filename) ? `trajectory` : `structure`,
270-
data: file,
271-
theme: get_theme(),
272-
})
275+
panel.webview.html = create_html(panel.webview, context, {
276+
type: is_trajectory_file(file_data.filename) ? `trajectory` : `structure`,
277+
data: file_data,
278+
theme: get_theme(),
279+
})
273280

274-
panel.webview.onDidReceiveMessage(
275-
(msg: MessageData) => handle_msg(msg, panel.webview),
276-
undefined,
277-
context.subscriptions,
278-
)
281+
panel.webview.onDidReceiveMessage(
282+
(msg: MessageData) => handle_msg(msg, panel.webview),
283+
undefined,
284+
context.subscriptions,
285+
)
279286

280-
// Listen for theme changes and update webview
281-
const update_theme = () => {
282-
if (panel.visible) {
283-
const current_file = file_path ? read_file(file_path) : file
284-
panel.webview.html = create_html(panel.webview, context, {
285-
type: is_trajectory_file(file.filename) ? `trajectory` : `structure`,
286-
data: current_file,
287-
theme: get_theme(),
288-
})
289-
}
287+
// Theme change handling
288+
const update_theme = () => {
289+
if (panel.visible) {
290+
const current_file = file_path ? read_file(file_path) : file_data
291+
panel.webview.html = create_html(panel.webview, context, {
292+
type: is_trajectory_file(file_data.filename) ? `trajectory` : `structure`,
293+
data: current_file,
294+
theme: get_theme(),
295+
})
290296
}
297+
}
291298

292-
const theme_change_listener = vscode.window.onDidChangeActiveColorTheme(
293-
update_theme,
294-
)
295-
const config_change_listener = vscode.workspace.onDidChangeConfiguration(
296-
(event: vscode.ConfigurationChangeEvent) => {
297-
if (event.affectsConfiguration(`matterviz.theme`)) update_theme()
298-
},
299-
)
299+
const theme_listener = vscode.window.onDidChangeActiveColorTheme(update_theme)
300+
const config_listener = vscode.workspace.onDidChangeConfiguration(
301+
(event: vscode.ConfigurationChangeEvent) => {
302+
if (event.affectsConfiguration(`matterviz.theme`)) update_theme()
303+
},
304+
)
300305

301-
// Dispose listeners when panel is closed
302-
panel.onDidDispose(() => {
303-
theme_change_listener.dispose()
304-
config_change_listener.dispose()
306+
panel.onDidDispose(() => {
307+
theme_listener.dispose()
308+
config_listener.dispose()
309+
if (file_path) stop_watching_file(file_path)
310+
})
305311

306-
// Clean up file watcher
307-
if (file_path) stop_watching_file(file_path)
308-
})
312+
return panel
313+
}
314+
315+
// Enhanced render function with file watching
316+
export const render = (context: vscode.ExtensionContext, uri?: vscode.Uri) => {
317+
try {
318+
const file = get_file(uri)
319+
const file_path = uri?.fsPath ||
320+
vscode.window.activeTextEditor?.document.fileName
321+
322+
create_webview_panel(context, file, file_path)
309323
} catch (error: unknown) {
310324
const message = error instanceof Error ? error.message : String(error)
311325
vscode.window.showErrorMessage(`Failed: ${message}`)
@@ -414,6 +428,36 @@ export const activate = (context: vscode.ExtensionContext): void => {
414428
new Provider(context),
415429
{ webviewOptions: { retainContextWhenHidden: true } },
416430
),
431+
vscode.workspace.onDidOpenTextDocument((document: vscode.TextDocument) => {
432+
if (
433+
document.uri.scheme === `file` &&
434+
should_auto_render(path.basename(document.uri.fsPath))
435+
) {
436+
setTimeout(() => {
437+
try {
438+
if (
439+
!vscode.workspace.getConfiguration(`matterviz`).get<boolean>(
440+
`autoRender`,
441+
true,
442+
)
443+
) return
444+
const file_data = read_file(document.uri.fsPath)
445+
create_webview_panel(
446+
context,
447+
file_data,
448+
document.uri.fsPath,
449+
vscode.ViewColumn.One,
450+
)
451+
} catch (error: unknown) {
452+
vscode.window.showErrorMessage(
453+
`MatterViz auto-render failed: ${
454+
error instanceof Error ? error.message : String(error)
455+
}`,
456+
)
457+
}
458+
}, 100)
459+
}
460+
}),
417461
)
418462
}
419463

extensions/vscode/src/webview/main.ts

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import '$lib/app.css'
33
import { parse_structure_file } from '$lib/io/parse'
44
import Structure from '$lib/structure/Structure.svelte'
5-
import { is_valid_theme_name, type ThemeName } from '$lib/theme/index'
5+
import { apply_theme_to_dom, is_valid_theme_name, type ThemeName } from '$lib/theme/index'
66
import '$lib/theme/themes'
77
import { is_trajectory_file, parse_trajectory_data } from '$lib/trajectory/parse'
88
import Trajectory from '$lib/trajectory/Trajectory.svelte'
@@ -95,7 +95,9 @@ const handle_file_change = async (message: FileChangeMessage): Promise<void> =>
9595

9696
if (message.command === `fileUpdated` && message.data) {
9797
try {
98-
if (message.theme && is_valid_theme_name(message.theme)) apply_theme(message.theme)
98+
if (message.theme && is_valid_theme_name(message.theme)) {
99+
apply_theme_to_dom(message.theme)
100+
}
99101

100102
const { content, filename, isCompressed } = message.data
101103
const result = await parse_file_content(content, filename, isCompressed)
@@ -133,25 +135,6 @@ export function base64_to_array_buffer(base64: string): ArrayBuffer {
133135
return bytes.buffer
134136
}
135137

136-
// Apply theme to the webview DOM
137-
const apply_theme = (theme: ThemeName): void => {
138-
const root = document.documentElement
139-
root.setAttribute(`data-theme`, theme)
140-
141-
if (!globalThis.MATTERVIZ_THEMES) throw new Error(`No themes found`)
142-
143-
const matterviz_theme = globalThis.MATTERVIZ_THEMES[theme]
144-
if (!matterviz_theme) throw new Error(`Theme ${theme} not found`)
145-
146-
const css_map = globalThis.MATTERVIZ_CSS_MAP || {}
147-
148-
// Apply MatterViz theme CSS variables to root element
149-
for (const [key, value] of Object.entries(matterviz_theme)) {
150-
const css_var = css_map[key]
151-
if (css_var && value) root.style.setProperty(css_var, value)
152-
}
153-
}
154-
155138
// Parse file content and determine if it's a structure or trajectory
156139
const parse_file_content = async (
157140
content: string,
@@ -289,7 +272,7 @@ const initialize_app = async (): Promise<MatterVizApp> => {
289272
}
290273

291274
// Apply theme early
292-
if (theme) apply_theme(theme)
275+
if (theme) apply_theme_to_dom(theme)
293276

294277
const container = document.getElementById(`matterviz-app`)
295278
if (!container) throw new Error(`Target container not found in DOM`)

0 commit comments

Comments
 (0)