const vscode = require('vscode'); const fs = require('fs'); const path = require('path') const browserTemplate = require('./__browserTemplate') let treeView; let context; let panel; const previewKey = 'savedPreviewUrl'; // Key to store last opened URL const previewColumnKey = 'savedPreviewColumn'; // Key to store last panel position const mainEmitter = (type) => { Log(type) console.log(type) } const Log = (args) => { vscode.window.showInformationMessage.call(global, args) } const Storage = { get: (previewKey) => { return context.globalState.get(previewKey) }, set: (key, value) => { context.globalState.update(key, value) } } const frame = { initFrame: () =>{}, open: // Function to open a WebView preview function runFrame(url, column = vscode.ViewColumn.Active) { console.log("iframe open", url) if (panel) { // If panel exists, update the content and restore position Storage.set(previewKey, url); panel.webview.html = frame.getWebviewContent(url); panel.reveal(column); } else { // Create a new WebView panel in the last known position panel = vscode.window.createWebviewPanel( 'webPreview', 'Web Preview', column, { enableScripts: true, // Allows JavaScript execution retainContextWhenHidden: true, // Ensures WebView state is preserved } ); panel.webview.html = frame.getWebviewContent(url); // Save URL & position for persistence Storage.set(previewKey, url); Storage.set(previewColumnKey, column); // Track panel movement panel.onDidChangeViewState(e => { if (panel.visible) { Storage.set(previewColumnKey, panel.viewColumn); } }); // Handle WebView disposal (clear references) panel.onDidDispose(() => { panel = null; Storage.set(previewKey, undefined); Storage.set(previewColumnKey, undefined); }); panel.webview.onDidReceiveMessage( message => { if (message.command === 'urlChanged') { console.log('New URL:', message.url); // You can trigger any action in your extension here Storage.set(previewKey, message.url); } if (message.command === 'inspectUrl') { console.log('inspect url') // vscode.commands.executeCommand("workbench.action.webview.openDeveloperTools"); panel.webview.openDevTools(); // Opens DevTools for this WebView iframe only } }, undefined, context.subscriptions ); } tabs.backupTabs && tabs.backupTabs(); }, getWebviewContent: browserTemplate, getCurrentPreviewState: function getCurrentPreviewState() { return { url: Storage.get(previewKey), group: Storage.get(previewColumnKey) || vscode.ViewColumn.One } } } const menu = { updateCount(newMessagesCount) { treeView.badge = { value: newMessagesCount, tooltip: `You have ${newMessagesCount} new documentation` }; }, TabSaver: class TabSaverTreeDataProvider { constructor(tabs) { this.tabs = tabs; } getTreeItem(element) { return new vscode.TreeItem(element); } getChildren(element) { if (!element) { return Promise.resolve(this.tabs); } // If an element is provided, you can handle it accordingly return Promise.resolve([]); } }, treeClickHandler: function treeClickHandler(event, context) { if (event.selection.length > 0) { const selectedItem = event.selection[0]; console.log(`Clicked on: ${selectedItem}`); // Log the clicked item's label frame.open('https://itrum.ru?selectedItem=' + selectedItem, 2) // let cmds = { // Preview: openWelcomeFrame, // HTML: () => openHtml(null, '

asdfasdf

'), // 'Run Tests': openTestsFrame, // 'Close Tabs': closeAllTabs // } // let fn = cmds[selectedItem]; // fn && fn(); } }, initTreeView: function initTreeView() { const view = vscode.window.createTreeView('itk.mainView', { treeDataProvider: new menu.TabSaver(['HTML', 'Preview', 'Close Tabs']) }); context.subscriptions.push(view); treeView = view; const view2 = vscode.window.createTreeView('itk.runView', { treeDataProvider: new menu.TabSaver(['Run Tests', 'Close Tabs']) }); context.subscriptions.push(view); context.subscriptions.push(view2); console.log('view', view) view.onDidChangeSelection((event) => menu.treeClickHandler(event, context)); view2.onDidChangeSelection((event) => menu.treeClickHandler(event, context)); }, } const tabs = { init: () => { let disposable = vscode.commands.registerCommand('itk.getTabs', () => tabs.getTabs()); context.subscriptions.push(disposable); }, async restoreOrKeepCurrent() { try { let idKey = 'idSessionKey' let data = require('./files/openTabs') let curOpenTabsId = data[idKey]; let curVsCodeId = Storage.get(idKey) console.log("{data!!!", curOpenTabsId, curVsCodeId) let isFromScratch = curVsCodeId != curOpenTabsId; if (isFromScratch) { await tabs.closeTabs() await tabs.restoreByArr(data.tabs) Storage.set(idKey, curOpenTabsId) } mainEmitter('openTabs') Log('FRAME OPEN START') console.log('data', data.preview) if (isFromScratch) { data.preview.url && await frame.open(data.preview.url, data.preview.group) } if (!isFromScratch) { await frame.open(Storage.get(previewKey), Storage.get(previewColumnKey)) } mainEmitter('openFrame') } catch (e) { console.log('eee', e) } }, async restoreByArr(groups) { const openPromises = []; let groupTabs = {}; const workspaceFolder = vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders[0]; if (!workspaceFolder) { vscode.window.showErrorMessage('No workspace folder is open. Please open a folder or workspace.'); return; } groups.forEach((group, groupIndex) => { const groupId = groupIndex + 1; // Group ID corresponds to column // Open files first, in order group.filter(item => item.relPath).forEach((item, index) => { const filePath = vscode.Uri.joinPath(workspaceFolder.uri, item.relPath); openPromises.push(vscode.workspace.openTextDocument(filePath) .then(doc => { // Use groupId as the column index const columnToUse = groupId; // Manage the tab order per group let lastTab = groupTabs[groupId] || 1; // Start at tab 1 by default vscode.window.showTextDocument(doc, { viewColumn: columnToUse, preview: false }).then(() => { groupTabs[groupId] = lastTab + 1; // Increment tab order for next file }); })); }); // After files, open Webview (iframe) last for this group // group.filter(item => item.url).forEach(item => { // openPromises.push(openWebview(item.url, groupId)); // }); }); // Wait for all files/URLs to be opened return Promise.all(openPromises).then(() => { console.log('All files and URLs have been opened.'); }).catch(err => { console.error('Error opening files or URLs:', err); }); }, initFileChanges() { let disposable = vscode.window.onDidChangeVisibleTextEditors(tabs.backupTabs); context.subscriptions.push(disposable); // Add to subscriptions to prevent memory leaks }, getCurrentPreviewState: frame.getCurrentPreviewState, async closeTabs() { try { await vscode.commands.executeCommand('workbench.action.closeAllEditors'); console.log("all tabs are closed") } catch (e) { vscode.window.showInformationMessage("Tabs Closing Error"); } }, writeFile(relPath, content) { const filePath = path.join(__dirname, relPath); fs.writeFileSync(filePath, content); }, async backupTabs() { let v = await tabs.getTabs() tabs.writeFile('./files/backupTabs.js', `module.exports = ${JSON.stringify(v, null, 4)}`) }, getTabs() { const tabGroups = vscode.window.tabGroups.all; if (tabGroups.length === 0) { vscode.window.showInformationMessage('No open tabs found.'); return; } let tabInfo = []; const workspaceFolder = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; // Get workspace folder tabGroups.forEach((group, index) => { let curTabsInGroup = [] group.tabs.forEach(tab => { let filePath = ""; if (tab.input instanceof vscode.TabInputText) { filePath = tab.input.uri.fsPath; } let relativePath = ""; if (filePath) { relativePath = path.relative(workspaceFolder, filePath); // Use path.relative } curTabsInGroup.push({ group: tab.group.viewColumn, label: tab.label, isActive: tab.isActive, isPinned: tab.isPinned, absPath: filePath, relPath: relativePath, }); }); tabInfo.push(curTabsInGroup) }); // vscode.window.showInformationMessage(`Opened Tabs:\n${tabInfo.join('\n')}`); return { tabs: tabInfo, preview: tabs.getCurrentPreviewState() }; } } function activate(_context) { console.log('Congratulations, your extension "itk" is now active!'); context = _context; menu.initTreeView(); menu.updateCount(117) frame.initFrame() tabs.init() tabs.initFileChanges(); tabs.restoreOrKeepCurrent(); } function deactivate() { console.log("ITK DEACTIVE") } module.exports = { activate, deactivate }