fix: ensure version compatibility with Cursor and Windsurf editors

This commit is contained in:
Asad Karimov 2025-05-15 13:19:14 -04:00
parent b8e323a575
commit 383b1d610f
3 changed files with 127 additions and 127 deletions

@ -5,7 +5,7 @@
"author": "Asadbek Karimov <contact@asadk.dev>",
"publisher": "asadbek",
"icon": "img/logo.png",
"version": "0.0.3",
"version": "0.0.7",
"license": "Apache-2.0",
"bugs": {
"url": "https://git.sheetjs.com/asadbek064/sheetjs-vscode-extension/issues"
@ -15,7 +15,7 @@
"url": "https://git.sheetjs.com/asadbek064/sheetjs-vscode-extension.git"
},
"engines": {
"vscode": "^1.100.0"
"vscode": "^1.96.0"
},
"categories": [
"Other"
@ -147,7 +147,7 @@
"devDependencies": {
"@types/mocha": "^10.0.10",
"@types/node": "20.x",
"@types/vscode": "^1.100.0",
"@types/vscode": "^1.96.0",
"@typescript-eslint/eslint-plugin": "^8.31.1",
"@typescript-eslint/parser": "^8.31.1",
"@vscode/test-cli": "^0.0.10",

@ -15,7 +15,7 @@ importers:
specifier: 20.x
version: 20.17.47
'@types/vscode':
specifier: ^1.100.0
specifier: ^1.96.0
version: 1.100.0
'@typescript-eslint/eslint-plugin':
specifier: ^8.31.1
@ -144,8 +144,8 @@ packages:
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
'@modelcontextprotocol/sdk@1.11.2':
resolution: {integrity: sha512-H9vwztj5OAqHg9GockCQC06k1natgcxWQSRpQcPJf6i5+MWBzfKkRtxGbjQf0X2ihii0ffLZCRGbYV2f2bjNCQ==}
'@modelcontextprotocol/sdk@1.11.3':
resolution: {integrity: sha512-rmOWVRUbUJD7iSvJugjUbFZshTAuJ48MXoZ80Osx1GM0K/H1w7rSEvmw8m6vdWxNASgtaHIhAgre4H/E9GJiYQ==}
engines: {node: '>=18'}
'@nodelib/fs.scandir@2.1.5':
@ -563,8 +563,8 @@ packages:
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
electron-to-chromium@1.5.152:
resolution: {integrity: sha512-xBOfg/EBaIlVsHipHl2VdTPJRSvErNUaqW8ejTq5OlOlIYx1wOllCHsAvAIrr55jD1IYEfdR86miUEt8H5IeJg==}
electron-to-chromium@1.5.154:
resolution: {integrity: sha512-G4VCFAyKbp1QJ+sWdXYIRYsPGvlV5sDACfCmoMFog3rjm1syLhI41WXm/swZypwCIWIm4IFLWzHY14joWMQ5Fw==}
emoji-regex@10.4.0:
resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
@ -1456,8 +1456,8 @@ packages:
uglify-js:
optional: true
terser@5.39.1:
resolution: {integrity: sha512-Mm6+uad0ZuDtcV8/4uOZQDQ8RuiC5Pu+iZRedJtF7yA/27sPL7d++In/AJKpWZlU3SYMPPkVfwetn6sgZ66pUA==}
terser@5.39.2:
resolution: {integrity: sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==}
engines: {node: '>=10'}
hasBin: true
@ -1726,7 +1726,7 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
'@modelcontextprotocol/sdk@1.11.2':
'@modelcontextprotocol/sdk@1.11.3':
dependencies:
content-type: 1.0.5
cors: 2.8.5
@ -2065,7 +2065,7 @@ snapshots:
browserslist@4.24.5:
dependencies:
caniuse-lite: 1.0.30001718
electron-to-chromium: 1.5.152
electron-to-chromium: 1.5.154
node-releases: 2.0.19
update-browserslist-db: 1.1.3(browserslist@4.24.5)
@ -2211,7 +2211,7 @@ snapshots:
ee-first@1.1.1: {}
electron-to-chromium@1.5.152: {}
electron-to-chromium@1.5.154: {}
emoji-regex@10.4.0: {}
@ -2271,7 +2271,7 @@ snapshots:
'@humanfs/node': 0.16.6
'@humanwhocodes/module-importer': 1.0.1
'@humanwhocodes/retry': 0.4.3
'@modelcontextprotocol/sdk': 1.11.2
'@modelcontextprotocol/sdk': 1.11.3
'@types/estree': 1.0.7
'@types/json-schema': 7.0.15
ajv: 6.12.6
@ -3112,10 +3112,10 @@ snapshots:
jest-worker: 27.5.1
schema-utils: 4.3.2
serialize-javascript: 6.0.2
terser: 5.39.1
terser: 5.39.2
webpack: 5.99.8(webpack-cli@6.0.1)
terser@5.39.1:
terser@5.39.2:
dependencies:
'@jridgewell/source-map': 0.3.6
acorn: 8.14.1

@ -11,13 +11,13 @@ export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<
public static register(context: vscode.ExtensionContext): vscode.Disposable {
return vscode.window.registerCustomEditorProvider(
'excelViewer.spreadsheet',
'excelViewer.spreadsheet',
new ExcelEditorProvider(context),
{ webviewOptions: { retainContextWhenHidden: true } } // keep webview state when hidden
);
}
constructor(private readonly context: vscode.ExtensionContext) {}
constructor(private readonly context: vscode.ExtensionContext) { }
async openCustomDocument(uri: vscode.Uri): Promise<ExcelDocument> {
console.log(`Opening document: ${uri.fsPath}`);
@ -26,14 +26,14 @@ export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<
async resolveCustomEditor(document: ExcelDocument, webviewPanel: vscode.WebviewPanel): Promise<void> {
console.log(`Resolving editor for: ${document.uri.fsPath}`);
webviewPanel.webview.options = { enableScripts: true };
this.setLoadingView(webviewPanel);
// small timeout to ensure the loading view is rendered before heavy processing begins
await new Promise(resolve => setTimeout(resolve, 50));
try {
// process file in a non-blocking way
setImmediate(async () => {
@ -69,7 +69,7 @@ export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<
// check if we have a cached workbook for this file
let workbook: XLSX.WorkBook;
const cacheKey = document.uri.toString();
if (this.workbookCache.has(cacheKey)) {
console.log('Using cached workbook');
workbook = this.workbookCache.get(cacheKey)!;
@ -77,104 +77,104 @@ export class ExcelEditorProvider implements vscode.CustomReadonlyEditorProvider<
} else {
workbook = await this.loadWorkbook(document, webviewPanel);
}
// setup the initial view with just the sheet selector
const sheetNames = workbook.SheetNames;
this.setupWebviewContent(document, webviewPanel, workbook, sheetNames);
}
private async loadWorkbook(document: ExcelDocument, webviewPanel: vscode.WebviewPanel): Promise<XLSX.WorkBook> {
this.updateLoadingProgress(webviewPanel, 'Reading file...');
try {
const data = await vscode.workspace.fs.readFile(document.uri);
console.log(`Read ${data.byteLength} bytes`);
// very large files, update the progress message
if (data.byteLength > 5 * 1024 * 1024) { // 5MB
this.updateLoadingProgress(webviewPanel, 'Parsing large file...');
}
// get file extension to optimize parsing options
const fileExtension = document.uri.fsPath.split('.').pop()?.toLowerCase() || '';
// parse with SheetJS with options optimized for the file type
console.log(`Parsing ${fileExtension} file...`);
this.updateLoadingProgress(webviewPanel, `Parsing ${fileExtension} data...`);
// timeout to ensure UI updates before heavy parsing
await new Promise(resolve => setTimeout(resolve, 10));
const options: XLSX.ParsingOptions = {
type: 'array',
cellStyles: true,
cellDates: true,
};
// specific format handling
if (fileExtension === 'csv') {
options.cellDates = false;
options.cellStyles = false;
}
const workbook = XLSX.read(new Uint8Array(data), options);
this.updateLoadingProgress(webviewPanel, 'Preparing view...');
// cache the workbook
const cacheKey = document.uri.toString();
this.workbookCache.set(cacheKey, workbook);
return workbook;
} catch (error) {
console.error(`Error reading file: ${error}`);
this.setErrorView(webviewPanel, error);
throw error;
}
}
private async loadWorkbook(document: ExcelDocument, webviewPanel: vscode.WebviewPanel): Promise<XLSX.WorkBook> {
this.updateLoadingProgress(webviewPanel, 'Reading file...');
private setupWebviewContent(
document: ExcelDocument,
webviewPanel: vscode.WebviewPanel,
workbook: XLSX.WorkBook,
sheetNames: string[]
): void {
// exit early if there are no sheets
if (sheetNames.length === 0) {
webviewPanel.webview.html = getErrorViewHtml(new Error("No sheets or tables found in this file."));
return;
try {
const data: Uint8Array = await vscode.workspace.fs.readFile(document.uri);
console.log(`Read ${data.byteLength} bytes`);
// very large files, update the progress message
if (data.byteLength > 5 * 1024 * 1024) { // 5MB
this.updateLoadingProgress(webviewPanel, 'Parsing large file...');
}
// get file extension to optimize parsing options
const fileExtension = document.uri.fsPath.split('.').pop()?.toLowerCase() || '';
// parse with SheetJS with options optimized for the file type
console.log(`Parsing ${fileExtension} file...`);
this.updateLoadingProgress(webviewPanel, `Parsing ${fileExtension} data...`);
// timeout to ensure UI updates before heavy parsing
await new Promise(resolve => setTimeout(resolve, 10));
const options: XLSX.ParsingOptions = {
type: 'array',
cellStyles: true,
cellDates: true,
};
// specific format handling
if (fileExtension === 'csv') {
options.cellDates = false;
options.cellStyles = false;
}
const workbook: XLSX.WorkBook = XLSX.read(data, options);
this.updateLoadingProgress(webviewPanel, 'Preparing view...');
// cache the workbook
const cacheKey = document.uri.toString();
this.workbookCache.set(cacheKey, workbook);
return workbook;
} catch (error) {
console.error(`Error reading file: ${error}`);
this.setErrorView(webviewPanel, error);
throw error;
}
}
// create a dropdown for sheet selection
let sheetSelector = `
private setupWebviewContent(
document: ExcelDocument,
webviewPanel: vscode.WebviewPanel,
workbook: XLSX.WorkBook,
sheetNames: string[]
): void {
// exit early if there are no sheets
if (sheetNames.length === 0) {
webviewPanel.webview.html = getErrorViewHtml(new Error("No sheets or tables found in this file."));
return;
}
// create a dropdown for sheet selection
let sheetSelector = `
<div class="sheet-selector">
<label for="sheet-select">Select worksheet: </label>
<select id="sheet-select">
`;
sheetNames.forEach((name, index) => {
sheetSelector += `<option value="${index}">${name}</option>`;
});
sheetSelector += `
sheetNames.forEach((name, index) => {
sheetSelector += `<option value="${index}">${name}</option>`;
});
sheetSelector += `
</select>
</div>
`;
// setup up the HTML with JS to handle sheet switching
webviewPanel.webview.html = getExcelViewerHtml(sheetNames, sheetSelector);
// handle messages from the webview
this.setupMessageHandlers(document, webviewPanel, workbook);
}
// setup up the HTML with JS to handle sheet switching
webviewPanel.webview.html = getExcelViewerHtml(sheetNames, sheetSelector);
// handle messages from the webview
this.setupMessageHandlers(document, webviewPanel, workbook);
}
private setupMessageHandlers(
document: ExcelDocument,
webviewPanel: vscode.WebviewPanel,
document: ExcelDocument,
webviewPanel: vscode.WebviewPanel,
workbook: XLSX.WorkBook
): void {
const baseCacheKey = document.uri.toString();
webviewPanel.webview.onDidReceiveMessage(async message => {
if (message.type === 'getSheetPage') {
await this.handleGetSheetPage(
@ -195,35 +195,35 @@ private async loadWorkbook(document: ExcelDocument, webviewPanel: vscode.Webview
): Promise<void> {
const { sheetName, page, rowsPerPage, maxColumns } = message;
const cacheKey = `${baseCacheKey}-${sheetName}-page-${page}`;
this.updateLoadingProgress(webviewPanel, `Preparing page ${page + 1} of sheet: ${sheetName}`);
// setTimeout to ensure loading message gets displayed
setTimeout(async () => {
try {
let sheetHtml: string;
let rangeInfo: any = null;
// check if this page is already cached
if (this.sheetCache.has(cacheKey)) {
sheetHtml = this.sheetCache.get(cacheKey)!;
} else {
const sheet = workbook.Sheets[sheetName];
// get the range of the sheet (e.g., A1:Z100)
const range = sheet['!ref'] || '';
rangeInfo = parseRange(range);
if (!rangeInfo) {
sheetHtml = '<p>No data in this sheet</p>';
} else {
sheetHtml = this.processSheetPage(sheet, rangeInfo, page, rowsPerPage, maxColumns);
// cache the result
this.sheetCache.set(cacheKey, sheetHtml);
}
}
// send the sheet data back to the webview
webviewPanel.webview.postMessage({
type: 'sheetData',
@ -243,39 +243,39 @@ private async loadWorkbook(document: ExcelDocument, webviewPanel: vscode.Webview
}
private processSheetPage(
sheet: XLSX.WorkSheet,
rangeInfo: any,
page: number,
rowsPerPage: number,
sheet: XLSX.WorkSheet,
rangeInfo: any,
page: number,
rowsPerPage: number,
maxColumns: number
): string {
// calc the range for this page
const pageStartRow = Math.min(rangeInfo.startRow + (page * rowsPerPage), rangeInfo.endRow);
const pageEndRow = Math.min(pageStartRow + rowsPerPage - 1, rangeInfo.endRow);
// limit columns if there are too many
const effectiveEndColNum = Math.min(rangeInfo.endColNum, rangeInfo.startColNum + maxColumns - 1);
const effectiveEndCol = numToColLetter(effectiveEndColNum);
// create a new range for this page
const pageRange = `${rangeInfo.startCol}${pageStartRow}:${effectiveEndCol}${pageEndRow}`;
// create a new sheet with just this page's data
const newPageSheet: XLSX.WorkSheet = { '!ref': pageRange };
// preserve important sheet properties
if (sheet['!cols']) newPageSheet['!cols'] = sheet['!cols'];
if (sheet['!rows']) newPageSheet['!rows'] = sheet['!rows'];
if (sheet['!merges']) {
// filter merges that are in this page's range
newPageSheet['!merges'] = sheet['!merges'].filter(merge => {
return merge.s.r >= pageStartRow - 1 &&
merge.e.r <= pageEndRow - 1 &&
merge.s.c >= rangeInfo.startColNum - 1 &&
merge.e.c <= effectiveEndColNum - 1;
return merge.s.r >= pageStartRow - 1 &&
merge.e.r <= pageEndRow - 1 &&
merge.s.c >= rangeInfo.startColNum - 1 &&
merge.e.c <= effectiveEndColNum - 1;
});
}
// copy cells in range
Object.keys(sheet).forEach(key => {
if (key.charAt(0) !== '!') {
@ -283,16 +283,16 @@ private async loadWorkbook(document: ExcelDocument, webviewPanel: vscode.Webview
const cellCol = key.replace(/[0-9]/g, '');
const cellRow = parseInt(key.replace(/[^0-9]/g, ''));
const cellColNum = colLetterToNum(cellCol);
if (cellRow >= pageStartRow &&
cellRow <= pageEndRow &&
cellColNum >= rangeInfo.startColNum &&
cellColNum <= effectiveEndColNum) {
if (cellRow >= pageStartRow &&
cellRow <= pageEndRow &&
cellColNum >= rangeInfo.startColNum &&
cellColNum <= effectiveEndColNum) {
newPageSheet[key] = sheet[key];
}
}
});
// convert to HTML
return XLSX.utils.sheet_to_html(newPageSheet);
}