import * as vscode from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; import { generateTkinterCode } from './generator'; import { parsePythonToGrapes } from './pythonParser'; export class TkinterEditorProvider implements vscode.CustomTextEditorProvider { public static readonly viewType = 'tkinter-designer.editor'; public static register(context: vscode.ExtensionContext): vscode.Disposable { const provider = new TkinterEditorProvider(context); return vscode.window.registerCustomEditorProvider(TkinterEditorProvider.viewType, provider); } constructor( private readonly context: vscode.ExtensionContext ) { } private isSaving = false; public async resolveCustomTextEditor( document: vscode.TextDocument, webviewPanel: vscode.WebviewPanel, _token: vscode.CancellationToken ): Promise { webviewPanel.webview.options = { enableScripts: true, localResourceRoots: [vscode.Uri.file(path.join(this.context.extensionPath, 'media'))] }; webviewPanel.webview.html = this.getHtmlForWebview(webviewPanel.webview); webviewPanel.webview.onDidReceiveMessage(e => { switch (e.type) { case 'request-load': this.handleRequestLoad(document, webviewPanel); break; case 'update-code': this.isSaving = true; this.handleSave(document, e.payload); setTimeout(() => { this.isSaving = false; }, 500); break; case 'request-import': this.handleImport(webviewPanel); break; } }); const folderPath = path.dirname(document.uri.fsPath); const fileNameBase = path.basename(document.uri.fsPath, path.extname(document.uri.fsPath)); const pyFilePath = path.join(folderPath, `${fileNameBase}.py`); const watcher = vscode.workspace.createFileSystemWatcher(pyFilePath); watcher.onDidChange(() => { // }); webviewPanel.onDidDispose(() => watcher.dispose()); } private handleRequestLoad(document: vscode.TextDocument, panel: vscode.WebviewPanel) { const text = document.getText(); let payload = {}; try { if (text.trim().length > 0) payload = JSON.parse(text); } catch (e) { console.error(e); } panel.webview.postMessage({ type: 'load-data', payload: payload }); } private async handleSave(document: vscode.TextDocument, jsonPayload: any) { const folderPath = path.dirname(document.uri.fsPath); const fileNameBase = path.basename(document.uri.fsPath, path.extname(document.uri.fsPath)); const pyFilePath = path.join(folderPath, `${fileNameBase}.py`); const pythonCode = generateTkinterCode(jsonPayload); try { fs.writeFileSync(pyFilePath, pythonCode); } catch (e) { console.error(`Python Save error: ${e}`); } // СОХРАНЕНИЕ JSON const edit = new vscode.WorkspaceEdit(); const fullRange = new vscode.Range( document.positionAt(0), document.positionAt(document.getText().length) ); edit.replace(document.uri, fullRange, JSON.stringify(jsonPayload, null, 2)); await vscode.workspace.applyEdit(edit); await document.save(); } private async handleImport(panel: vscode.WebviewPanel) { const uris = await vscode.window.showOpenDialog({ canSelectMany: false, openLabel: 'Import Python', filters: { 'Python Files': ['py'] } }); if (uris && uris[0]) { const content = fs.readFileSync(uris[0].fsPath, 'utf-8'); const data = parsePythonToGrapes(content); panel.webview.postMessage({ type: 'import-data', payload: data }); } } private getHtmlForWebview(webview: vscode.Webview): string { const mediaPath = path.join(this.context.extensionPath, 'media'); const scriptUri = webview.asWebviewUri(vscode.Uri.file(path.join(mediaPath, 'grapes.min.js'))); const styleUri = webview.asWebviewUri(vscode.Uri.file(path.join(mediaPath, 'grapes.min.css'))); const mainScriptUri = webview.asWebviewUri(vscode.Uri.file(path.join(mediaPath, 'main.js'))); const fontUrl = "https://unpkg.com/grapesjs/dist/fonts/grapes.woff"; return ` Tkinter Designer
`; } }