|
6 | 6 | FormValidator |
7 | 7 | } from "./utils/editor_managers.js"; |
8 | 8 | import { CodeEditorManager } from "./utils/editor_settings.js"; |
| 9 | +import { buildTampermonkeyMetadata, parseUserScriptMetadata } from "./utils/metadataParser.js"; |
9 | 10 |
|
10 | 11 | class ScriptEditor { |
11 | 12 | constructor() { |
@@ -164,6 +165,7 @@ class ScriptEditor { |
164 | 165 | "scriptName", |
165 | 166 | "scriptAuthor", |
166 | 167 | "scriptLicense", |
| 168 | + "scriptIcon", |
167 | 169 | "targetUrl", |
168 | 170 | "runAt", |
169 | 171 | "scriptVersion", |
@@ -324,6 +326,7 @@ class ScriptEditor { |
324 | 326 | if (metadata.namespace) scriptData.namespace = metadata.namespace; |
325 | 327 | if (metadata.runAt) scriptData.runAt = metadata.runAt; |
326 | 328 | if (metadata.license) scriptData.license = metadata.license; |
| 329 | + if (metadata.icon) scriptData.icon = metadata.icon; |
327 | 330 |
|
328 | 331 | // Handle matches and includes |
329 | 332 | if (metadata.matches?.length) { |
@@ -373,53 +376,22 @@ class ScriptEditor { |
373 | 376 | if (!importData) return; |
374 | 377 |
|
375 | 378 | const { code } = importData; |
376 | | - |
377 | | - // Parse metadata block |
378 | | - const metaMatch = code.match(/==UserScript==([\s\S]*?)==\/UserScript==/); |
379 | | - const metadata = {}; |
380 | | - if (metaMatch) { |
381 | | - metaMatch[1].split("\n").forEach((line) => { |
382 | | - const m = line.match(/@(\w+)\s+(.+)/); |
383 | | - if (m) { |
384 | | - const [, key, value] = m; |
385 | | - if (key === "match" || key === "include") { |
386 | | - metadata.matches = metadata.matches || []; |
387 | | - metadata.matches.push(value.trim()); |
388 | | - } else if (key === "grant") { |
389 | | - metadata.grants = metadata.grants || []; |
390 | | - metadata.grants.push(value.trim()); |
391 | | - } else if (key === "require") { |
392 | | - metadata.requires = metadata.requires || []; |
393 | | - metadata.requires.push(value.trim()); |
394 | | - } else { |
395 | | - metadata[key] = value.trim(); |
396 | | - } |
397 | | - } |
398 | | - }); |
399 | | - } |
400 | | - |
401 | | - const scriptObj = { |
402 | | - name: metadata.name || "Imported Script", |
403 | | - author: metadata.author || "Anonymous", |
404 | | - description: metadata.description || "", |
405 | | - version: metadata.version || this.config.DEFAULT_VERSION, |
406 | | - targetUrls: metadata.matches || ["*://*/*"], |
407 | | - runAt: metadata.runAt || "document_end", |
408 | | - code, |
409 | | - requires: metadata.requires || [], |
410 | | - }; |
411 | | - |
412 | | - this.populateFormWithScript(scriptObj); |
413 | | - |
| 379 | + |
| 380 | + // Parse metadata using shared utility for full support |
| 381 | + const metadata = parseUserScriptMetadata(code); |
| 382 | + |
| 383 | + // Delegate to existing import handler for form population |
| 384 | + this.handleScriptImport({ code, ...metadata }); |
| 385 | + |
414 | 386 | // Mark as unsaved draft for user review (but DO NOT autosave) |
415 | 387 | this.state.hasUnsavedChanges = true; |
416 | 388 | this.ui.updateScriptStatus(true); |
417 | | - |
| 389 | + |
418 | 390 | // Clean up storage |
419 | 391 | await chrome.storage.local.remove(key); |
420 | 392 | } catch (err) { |
421 | | - console.error("Failed to load imported script:", err); |
422 | | - this.ui.showStatusMessage("Failed to load imported script", "error"); |
| 393 | + console.error('Error loading imported script:', err); |
| 394 | + this.ui.showStatusMessage('Failed to load imported script', 'error'); |
423 | 395 | } |
424 | 396 | } |
425 | 397 |
|
@@ -554,7 +526,7 @@ class ScriptEditor { |
554 | 526 |
|
555 | 527 | // Form field change listeners for autosave |
556 | 528 | const formFields = [ |
557 | | - 'scriptName', 'scriptAuthor', 'scriptVersion', 'scriptDescription', 'scriptLicense', |
| 529 | + 'scriptName', 'scriptAuthor', 'scriptVersion', 'scriptDescription', 'scriptLicense', 'scriptIcon', |
558 | 530 | 'runAt', 'waitForSelector', 'targetUrl' |
559 | 531 | ]; |
560 | 532 |
|
@@ -649,6 +621,7 @@ class ScriptEditor { |
649 | 621 | script.version || this.config.DEFAULT_VERSION; |
650 | 622 | this.elements.scriptDescription.value = script.description || ""; |
651 | 623 | this.elements.scriptLicense.value = script.license || ""; |
| 624 | + this.elements.scriptIcon.value = script.icon || ""; |
652 | 625 | this.codeEditorManager.setValue(script.code || ""); |
653 | 626 |
|
654 | 627 | script.targetUrls?.forEach((url) => this.ui.addUrlToList(url)); |
@@ -726,6 +699,7 @@ class ScriptEditor { |
726 | 699 | this.elements.scriptVersion.value.trim() || this.config.DEFAULT_VERSION, |
727 | 700 | description: this.elements.scriptDescription.value.trim(), |
728 | 701 | license: this.elements.scriptLicense?.value.trim() || "", |
| 702 | + icon: this.elements.scriptIcon?.value.trim() || "", |
729 | 703 | code: this.codeEditorManager.getValue(), |
730 | 704 | enabled: true, |
731 | 705 | updatedAt: new Date().toISOString(), |
@@ -912,6 +886,37 @@ class ScriptEditor { |
912 | 886 | console.warn("Initial background connection failed:", error); |
913 | 887 | } |
914 | 888 | } |
| 889 | + |
| 890 | + /** |
| 891 | + * Export current script in classic Tampermonkey format (.user.js) |
| 892 | + */ |
| 893 | + exportScript() { |
| 894 | + try { |
| 895 | + const scriptData = this.gatherScriptData(); |
| 896 | + const metadata = buildTampermonkeyMetadata(scriptData); |
| 897 | + const content = `${metadata}\n\n${scriptData.code}`; |
| 898 | + |
| 899 | + const fileNameSafe = (scriptData.name || 'script') |
| 900 | + .replace(/[^a-z0-9_-]+/gi, '_') |
| 901 | + .replace(/_{2,}/g, '_') |
| 902 | + .replace(/^_|_$/g, '') || 'script'; |
| 903 | + |
| 904 | + const blob = new Blob([content], { type: 'text/javascript;charset=utf-8' }); |
| 905 | + const url = URL.createObjectURL(blob); |
| 906 | + const a = document.createElement('a'); |
| 907 | + a.href = url; |
| 908 | + a.download = `${fileNameSafe}.user.js`; |
| 909 | + document.body.appendChild(a); |
| 910 | + a.click(); |
| 911 | + document.body.removeChild(a); |
| 912 | + URL.revokeObjectURL(url); |
| 913 | + |
| 914 | + this.ui.showStatusMessage('Script exported', 'success'); |
| 915 | + } catch (err) { |
| 916 | + console.error('Export failed:', err); |
| 917 | + this.ui.showStatusMessage('Export failed', 'error'); |
| 918 | + } |
| 919 | + } |
915 | 920 | } |
916 | 921 |
|
917 | 922 | // Main init for editor |
|
0 commit comments