shifting towards manual symlinks for more complex setup than laptop, allows for GUI settings changes to actually apply, and is much easier than nix-ifying every config
266 lines
10 KiB
JavaScript
266 lines
10 KiB
JavaScript
"use strict";
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
}
|
|
Object.defineProperty(o, k2, desc);
|
|
}) : (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
o[k2] = m[k];
|
|
}));
|
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
}) : function(o, v) {
|
|
o["default"] = v;
|
|
});
|
|
var __importStar = (this && this.__importStar) || (function () {
|
|
var ownKeys = function(o) {
|
|
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
var ar = [];
|
|
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
return ar;
|
|
};
|
|
return ownKeys(o);
|
|
};
|
|
return function (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
__setModuleDefault(result, mod);
|
|
return result;
|
|
};
|
|
})();
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.WebviewManager = void 0;
|
|
const vscode = __importStar(require("vscode"));
|
|
const path = __importStar(require("path"));
|
|
const fs = __importStar(require("fs"));
|
|
const storage = __importStar(require("../storage"));
|
|
const searchCtx = __importStar(require("../searchCtx"));
|
|
/**
|
|
* Manages the webview panel for pattern management
|
|
*/
|
|
class WebviewManager {
|
|
/**
|
|
* Create or show the webview panel
|
|
*/
|
|
static show(extensionUri, scope) {
|
|
const column = vscode.window.activeTextEditor
|
|
? vscode.window.activeTextEditor.viewColumn
|
|
: undefined;
|
|
// If we already have a panel, show it
|
|
if (WebviewManager.currentPanel) {
|
|
WebviewManager.currentPanel.panel.reveal(column);
|
|
return;
|
|
}
|
|
const panel = vscode.window.createWebviewPanel('patternStoreManage', 'PatternStore', column || vscode.ViewColumn.One, {
|
|
enableScripts: true,
|
|
retainContextWhenHidden: true,
|
|
});
|
|
WebviewManager.currentPanel = new WebviewManager(panel, extensionUri, scope);
|
|
}
|
|
constructor(panel, extensionUri, scope) {
|
|
this.disposables = [];
|
|
this.isInternalSave = false;
|
|
this.panel = panel;
|
|
this.extensionUri = extensionUri;
|
|
this.scope = scope;
|
|
// Set initial HTML content
|
|
this.panel.webview.html = this.getHtmlContent();
|
|
// Listen for when the panel is disposed
|
|
this.panel.onDidDispose(() => this.dispose(), null, this.disposables);
|
|
// Handle messages from the webview
|
|
this.panel.webview.onDidReceiveMessage(message => {
|
|
this.handleMessage(message);
|
|
}, null, this.disposables);
|
|
// Watch for external config changes
|
|
vscode.workspace.onDidChangeConfiguration(e => {
|
|
if (e.affectsConfiguration('patternStore')) {
|
|
this.handleExternalConfigChange();
|
|
}
|
|
}, null, this.disposables);
|
|
this.sendPatterns();
|
|
}
|
|
sendPatterns(selectId, selectScope, clearSelection) {
|
|
const allPatterns = storage.getAllPatterns();
|
|
const workspacePatterns = allPatterns.filter(p => p.scope === 'workspace');
|
|
const userPatterns = allPatterns.filter(p => p.scope === 'global');
|
|
const message = {
|
|
type: 'patterns',
|
|
workspace: workspacePatterns,
|
|
user: userPatterns
|
|
};
|
|
if (selectId && selectScope) {
|
|
message.selectPattern = {
|
|
id: selectId,
|
|
scope: selectScope
|
|
};
|
|
}
|
|
if (clearSelection) {
|
|
message.clearSelection = true;
|
|
}
|
|
this.panel.webview.postMessage(message);
|
|
}
|
|
/**
|
|
* Handle external configuration changes (e.g., manual edits to settings.json)
|
|
*/
|
|
handleExternalConfigChange() {
|
|
// Don't show notification if this was an internal save
|
|
if (this.isInternalSave) {
|
|
this.isInternalSave = false;
|
|
return;
|
|
}
|
|
// Show warning to user for actual external changes
|
|
vscode.window.showWarningMessage('Pattern settings changed externally. Webview reloaded.');
|
|
// Force full reload - this clears any dirty state and rebuilds runtime IDs
|
|
// Pass clearSelection flag to force the webview to clear the details view
|
|
this.sendPatterns(undefined, undefined, true);
|
|
}
|
|
async handleMessage(message) {
|
|
console.log('Received message from webview:', message);
|
|
switch (message.type) {
|
|
case 'ready':
|
|
// Webview is ready, send patterns
|
|
this.sendPatterns();
|
|
break;
|
|
case 'delete':
|
|
await this.handleDelete(message.id, message.scope);
|
|
break;
|
|
case 'load':
|
|
await this.handleLoad(message.id, message.scope, message.pattern);
|
|
break;
|
|
case 'create':
|
|
await this.handleCreate(message.scope);
|
|
break;
|
|
case 'save':
|
|
await this.handleSave(message.pattern);
|
|
break;
|
|
}
|
|
}
|
|
async handleCreate(scope) {
|
|
// Generate unique label for new pattern
|
|
const existingPatterns = storage.getAllPatterns().filter(p => p.scope === scope);
|
|
const baseName = 'New Pattern';
|
|
let label = baseName;
|
|
let counter = 1;
|
|
while (existingPatterns.some(p => p.label === label)) {
|
|
counter++;
|
|
label = `${baseName} ${counter}`;
|
|
}
|
|
const newPattern = {
|
|
id: 'new', // Temporary ID for new patterns
|
|
label: label,
|
|
find: '',
|
|
replace: '',
|
|
flags: {
|
|
isRegex: false,
|
|
isCaseSensitive: false,
|
|
matchWholeWord: false,
|
|
isMultiline: false
|
|
},
|
|
scope: scope
|
|
};
|
|
// Mark as internal save to suppress external change notification
|
|
this.isInternalSave = true;
|
|
await storage.savePattern(newPattern);
|
|
// New pattern will be at index 0 (savePattern uses unshift)
|
|
this.sendPatterns(`${scope}-0`, scope);
|
|
}
|
|
async handleSave(patternData) {
|
|
const { id, label, find, replace, flags, filesToInclude, filesToExclude, scope } = patternData;
|
|
const pattern = {
|
|
id: id, // Preserve existing ID
|
|
label: label,
|
|
find: find,
|
|
replace: replace,
|
|
flags: flags,
|
|
filesToInclude: filesToInclude,
|
|
filesToExclude: filesToExclude,
|
|
scope: scope
|
|
};
|
|
// Mark as internal save to suppress external change notification
|
|
this.isInternalSave = true;
|
|
await storage.savePattern(pattern);
|
|
this.sendPatterns(id, scope);
|
|
}
|
|
async handleDelete(id, scope) {
|
|
// Find pattern to get label for confirmation
|
|
const allPatterns = storage.getAllPatterns();
|
|
const pattern = allPatterns.find(p => p.id === id && p.scope === scope);
|
|
if (!pattern) {
|
|
vscode.window.showErrorMessage(`Pattern not found`);
|
|
return;
|
|
}
|
|
// Confirm deletion
|
|
const confirm = await vscode.window.showWarningMessage(`Delete pattern "${pattern.label}"?`, { modal: true }, 'Delete');
|
|
if (confirm === 'Delete') {
|
|
// Mark as internal save to suppress external change notification
|
|
this.isInternalSave = true;
|
|
await storage.deletePattern(id, scope);
|
|
// Refresh pattern list
|
|
this.sendPatterns();
|
|
}
|
|
}
|
|
async handleLoad(id, scope, providedPattern) {
|
|
let pattern = providedPattern;
|
|
// If no pattern was provided, look it up
|
|
if (!pattern) {
|
|
const allPatterns = storage.getAllPatterns();
|
|
pattern = allPatterns.find(p => p.id === id && p.scope === scope);
|
|
if (!pattern) {
|
|
vscode.window.showErrorMessage(`Pattern "${id}" not found`);
|
|
return;
|
|
}
|
|
}
|
|
await searchCtx.loadPatternIntoSearch(pattern);
|
|
}
|
|
getHtmlContent() {
|
|
// Generate nonce for CSP
|
|
const nonce = this.getNonce();
|
|
// Get URIs for webview resources
|
|
const cspSource = this.panel.webview.cspSource;
|
|
// Load HTML, CSS, and JS from files
|
|
const htmlPath = path.join(this.extensionUri.fsPath, 'src', 'webview', 'html', 'patternManager.html');
|
|
const cssPath = path.join(this.extensionUri.fsPath, 'src', 'webview', 'css', 'patternManager.css');
|
|
const jsPath = path.join(this.extensionUri.fsPath, 'src', 'webview', 'js', 'patternManager.js');
|
|
let html = fs.readFileSync(htmlPath, 'utf8');
|
|
const css = fs.readFileSync(cssPath, 'utf8');
|
|
const js = fs.readFileSync(jsPath, 'utf8');
|
|
// Build style tag with nonce
|
|
const styleTag = `<style nonce="${nonce}">\n${css}\n </style>`;
|
|
// Replace placeholders
|
|
html = html.replace(/{{nonce}}/g, nonce);
|
|
html = html.replace(/{{cspSource}}/g, cspSource);
|
|
html = html.replace(/{{styleTag}}/g, styleTag);
|
|
html = html.replace(/{{script}}/g, js);
|
|
return html;
|
|
}
|
|
/**
|
|
* Generate a nonce for CSP
|
|
*/
|
|
getNonce() {
|
|
let text = '';
|
|
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
for (let i = 0; i < 32; i++) {
|
|
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
}
|
|
return text;
|
|
}
|
|
/**
|
|
* Clean up resources
|
|
*/
|
|
dispose() {
|
|
WebviewManager.currentPanel = undefined;
|
|
this.panel.dispose();
|
|
while (this.disposables.length) {
|
|
const disposable = this.disposables.pop();
|
|
if (disposable) {
|
|
disposable.dispose();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
exports.WebviewManager = WebviewManager;
|
|
//# sourceMappingURL=WebviewManager.js.map
|