feat: commands to disable/enable viewer; re-validation on file edit

This commit is contained in:
Asad Karimov 2026-01-12 17:35:30 -05:00
parent 21f4542e5f
commit 19abeee875
11 changed files with 285 additions and 21 deletions

17
.vscode/launch.json vendored Normal file

@ -0,0 +1,17 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}"
}
]
}

18
.vscode/tasks.json vendored Normal file

@ -0,0 +1,18 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

@ -12,3 +12,4 @@ vsc-extension-quickstart.md
**/*.map
**/*.ts
**/.vscode-test.*
*.vsix

@ -2,8 +2,17 @@
All notable changes to the "sheetjs-demo" extension will be documented in this file.
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [0.1.0]
## [Unreleased]
- Added commands to disable/enable viewer for specific file extensions
- Added right-click context menu options for spreadsheet files
- Automatic file reloading when files are edited externally
- Command palette integration for quick toggling
- Initial release
## [0.0.9]
- View spreadsheets directly in VSCode
- Support for 30+ file formats (XLSX, XLS, CSV, ODS, and more)
- Multi-level caching for workbooks and sheets
- On-demand sheet loading
- Pagination for large files

@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (C) 2014-present SheetJS LLC
Copyright (C) 2025-present Asadbek Karimov
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -17,19 +17,12 @@ Powered by [SheetJS](http://sheetjs.com/) a powerful VSCode extension that lets
## Key Features
The extension uses sophisticated optimization techniques to ensure smooth performance:
- **Multi-level Caching**
- Workbook cache prevents redundant parsing of the same files
- Sheet HTML cache eliminates regeneration of previously viewed sheets
- **Smart Loading Strategy**
- First sheet loads immediately for instant feedback
- Additional sheets load on-demand when selected
- Preserved webview context maintains your state even when hidden
- **Responsive Interface**
- Immediate loading spinner provides visual feedback
- Sheet-switching indicators keep you informed
- Persistent state across view changes
- Caches workbooks and sheets to avoid re-parsing files
- Loads sheets on-demand when switching between them
- Automatically reloads when files are edited externally
- Handles mega large files with pagination
- Toggle viewer on/off for specific file extensions via command palette or context menu
## Supported File Formats
@ -65,11 +58,41 @@ The extension uses sophisticated optimization techniques to ensure smooth perfor
| *.wb3 |
| *.qpw |
| *.xlr |
| *.eth |
| *.eth |
## Usage
### Disabling/Enabling the Viewer
You can easily disable the SheetJS viewer for specific file extensions:
<img src="https://git.sheetjs.com/asadbek064/sheetjs-vscode-extension/raw/branch/main/asset/toggle_context_menu.png" alt="SheetJS VSCode Extension Preview" width="600"/>
<img src="https://git.sheetjs.com/asadbek064/sheetjs-vscode-extension/raw/branch/main/asset/toggle_ext_via_palette.png" alt="SheetJS VSCode Extension Preview" width="600"/>
**Command Palette** (Ctrl/Cmd+Shift+P):
- `SheetJS: Disable Viewer for Current File Extension` - Switches to default text editor for that extension
- `SheetJS: Enable Viewer for Current File Extension` - Re-enables the viewer
**Context Menu**: Right-click any spreadsheet file in the Explorer to access the same commands.
**Built-in VSCode**: Right-click any file and select "Open With..." to choose between SheetJS Viewer and other editors.
## Getting Started
Want to integrate SheetJS in your own VSCode extension? Check out our [detailed tutorial](https://docs.sheetjs.com/docs/) to learn how to implement these capabilities in your projects.
## Development
To run the extension in development mode, install dependencies with `pnpm install` and press F5 in VSCode. This opens a new Extension Development Host window where you can test the extension by opening any spreadsheet file.
Build for production with `pnpm run package`.
## Publishing
```bash
npx vsce login foo
npx vsce publish
```
## Learn More
For more information on using this extension and integrating SheetJS capabilities in your own projects, visit our [documentation](https://docs.sheetjs.com/docs/).

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

@ -5,7 +5,7 @@
"author": "Asadbek Karimov <contact@asadk.dev>",
"publisher": "asadbek",
"icon": "img/logo.png",
"version": "0.0.8",
"version": "0.1.0",
"license": "Apache-2.0",
"bugs": {
"url": "https://git.sheetjs.com/asadbek064/sheetjs-vscode-extension/issues"
@ -30,6 +30,47 @@
"activationEvents": [],
"main": "./dist/extension.js",
"contributes": {
"commands": [
{
"command": "sheetjs.disableForExtension",
"title": "SheetJS: Disable Viewer for Current File Extension"
},
{
"command": "sheetjs.enableForExtension",
"title": "SheetJS: Enable Viewer for Current File Extension"
},
{
"command": "sheetjs.openWithViewer",
"title": "SheetJS: Open with Spreadsheet Viewer"
}
],
"menus": {
"commandPalette": [
{
"command": "sheetjs.disableForExtension",
"when": "editorIsOpen"
},
{
"command": "sheetjs.enableForExtension",
"when": "editorIsOpen"
},
{
"command": "sheetjs.openWithViewer"
}
],
"explorer/context": [
{
"command": "sheetjs.disableForExtension",
"group": "2_workspace",
"when": "resourceExtname =~ /\\.(xlsx|xls|csv|ods|xlsm|xlsb|numbers)$/"
},
{
"command": "sheetjs.enableForExtension",
"group": "2_workspace",
"when": "resourceExtname =~ /\\.(xlsx|xls|csv|ods|xlsm|xlsb|numbers)$/"
}
]
},
"customEditors": [
{
"viewType": "excelViewer.spreadsheet",

@ -7,7 +7,9 @@ import { WorkbookCache } from './cacheManagement/workbookCache';
export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<ExcelDocument> {
private cache: WorkbookCache;
private fileWatchers: Map<string, vscode.Disposable> = new Map();
private webviewPanels: Map<string, vscode.WebviewPanel> = new Map();
public static register(context: vscode.ExtensionContext): vscode.Disposable {
return vscode.window.registerCustomEditorProvider(
'excelViewer.spreadsheet',
@ -30,6 +32,45 @@ export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<
webviewPanel.webview.options = { enableScripts: true };
// store the webview panel for this document
const uriKey = document.uri.toString();
this.webviewPanels.set(uriKey, webviewPanel);
// setup file watcher for changes to this specific file
const watcher = vscode.workspace.createFileSystemWatcher(
document.uri.fsPath
);
watcher.onDidChange(async () => {
console.log(`File changed: ${document.uri.fsPath}, reloading...`);
// clear the cache for this file
this.cache.clearCachesForUri(document.uri.toString());
// reload the file
this.setLoadingView(webviewPanel);
await new Promise(resolve => setTimeout(resolve, 50));
try {
await this.processExcelFile(document, webviewPanel);
} catch (error) {
console.error('Error reloading file:', error);
this.setErrorView(webviewPanel, error);
}
});
this.fileWatchers.set(uriKey, watcher);
// clean up when the panel is disposed
webviewPanel.onDidDispose(() => {
this.webviewPanels.delete(uriKey);
const watcher = this.fileWatchers.get(uriKey);
if (watcher) {
watcher.dispose();
this.fileWatchers.delete(uriKey);
}
});
this.setLoadingView(webviewPanel);
// small timeout to ensure the loading view is rendered before heavy processing begins

@ -3,10 +3,124 @@ import { ExcelEditorProvider } from './excelEditorProvider';
export function activate(context: vscode.ExtensionContext) {
console.log('SheetJS Spreadsheet Viewer extension activating...');
const provider = ExcelEditorProvider.register(context);
context.subscriptions.push(provider);
// Command to disable viewer for current file extension
const disableCommand = vscode.commands.registerCommand('sheetjs.disableForExtension', async (uri?: vscode.Uri) => {
// Try to get URI from parameter, active text editor, or prompt user
if (!uri) {
const activeTab = vscode.window.tabGroups.activeTabGroup.activeTab;
if (activeTab?.label) {
// Try to find the file by label in workspace
const files = await vscode.workspace.findFiles(`**/${activeTab.label}`);
if (files.length > 0) {
uri = files[0];
}
}
}
if (!uri) {
vscode.window.showWarningMessage('No file is currently open or could not determine file path');
return;
}
const ext = uri.fsPath.split('.').pop()?.toLowerCase();
if (!ext) {
vscode.window.showWarningMessage('Could not determine file extension');
return;
}
// Update workbench.editorAssociations to use default editor
const workbenchConfig = vscode.workspace.getConfiguration('workbench');
const associations = workbenchConfig.get<Record<string, string>>('editorAssociations') || {};
if (associations[`*.${ext}`] === 'default') {
vscode.window.showInformationMessage(`SheetJS viewer is already disabled for .${ext} files`);
return;
}
associations[`*.${ext}`] = 'default';
await workbenchConfig.update('editorAssociations', associations, vscode.ConfigurationTarget.Global);
// Close the current tab and reopen with default editor
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
await vscode.commands.executeCommand('vscode.open', uri);
vscode.window.showInformationMessage(`SheetJS viewer disabled for .${ext} files`);
});
// Command to enable viewer for current file extension
const enableCommand = vscode.commands.registerCommand('sheetjs.enableForExtension', async (uri?: vscode.Uri) => {
// Try to get URI from parameter or active tab
if (!uri) {
uri = vscode.window.activeTextEditor?.document.uri;
if (!uri) {
const activeTab = vscode.window.tabGroups.activeTabGroup.activeTab;
if (activeTab?.label) {
const files = await vscode.workspace.findFiles(`**/${activeTab.label}`);
if (files.length > 0) {
uri = files[0];
}
}
}
}
if (!uri) {
vscode.window.showWarningMessage('No file is currently open or could not determine file path');
return;
}
const ext = uri.fsPath.split('.').pop()?.toLowerCase();
if (!ext) {
vscode.window.showWarningMessage('Could not determine file extension');
return;
}
// Remove from workbench.editorAssociations
const workbenchConfig = vscode.workspace.getConfiguration('workbench');
const associations = workbenchConfig.get<Record<string, string>>('editorAssociations') || {};
// If not set to 'default', it's already enabled
if (associations[`*.${ext}`] !== 'default') {
vscode.window.showInformationMessage(`SheetJS viewer is already enabled for .${ext} files`);
return;
}
// Create a new object without the extension (to avoid proxy issues)
const newAssociations: Record<string, string> = {};
for (const key in associations) {
if (key !== `*.${ext}`) {
newAssociations[key] = associations[key];
}
}
await workbenchConfig.update('editorAssociations', newAssociations, vscode.ConfigurationTarget.Global);
// Close the current tab and reopen with SheetJS viewer
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
await vscode.commands.executeCommand('vscode.openWith', uri, 'excelViewer.spreadsheet');
vscode.window.showInformationMessage(`SheetJS viewer enabled for .${ext} files`);
});
// Command to open with viewer
const openCommand = vscode.commands.registerCommand('sheetjs.openWithViewer', async () => {
const uris = await vscode.window.showOpenDialog({
canSelectMany: false,
openLabel: 'Open with SheetJS Viewer',
filters: {
'Spreadsheets': ['xlsx', 'xls', 'csv', 'ods', 'xlsm', 'xlsb', 'numbers']
}
});
if (uris && uris[0]) {
await vscode.commands.executeCommand('vscode.openWith', uris[0], 'excelViewer.spreadsheet');
}
});
context.subscriptions.push(disableCommand, enableCommand, openCommand);
console.log('SheetJS Spreadsheet Viewer extension is now active');
vscode.window.showInformationMessage('SheetJS Spreadsheet Viewer is ready!');
}