aboutsummaryrefslogtreecommitdiff
path: root/modules/ext
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-05-28 18:01:22 +0200
committerAgando <roduccomm@gmail.com>2026-05-28 18:01:22 +0200
commitf8a04e76ea9aa5156e6e80a7534f1aa5a336429b (patch)
tree35f08d7228f1da9493e45d2fcd64e7277f008589 /modules/ext
parentd1af24deb8ad344994c0a06b909b7a6f4bac4c94 (diff)
auto: 2026-05-28_18:00 changes
Diffstat (limited to 'modules/ext')
-rw-r--r--modules/ext/alias.js2
-rw-r--r--modules/ext/busybox.js3
-rw-r--r--modules/ext/calc.js2
-rw-r--r--modules/ext/cron.js2
-rw-r--r--modules/ext/cryptogzip.js18
-rw-r--r--modules/ext/diff.js2
-rw-r--r--modules/ext/editor.js2
-rw-r--r--modules/ext/filemgr.js169
-rw-r--r--modules/ext/finder.js2
-rw-r--r--modules/ext/mindmap.js20
-rw-r--r--modules/ext/shell.js3
-rw-r--r--modules/ext/skill-pack-core.js3
-rw-r--r--modules/ext/theme.js2
-rw-r--r--modules/ext/voice.js2
-rw-r--r--modules/ext/wpm.js4
15 files changed, 179 insertions, 57 deletions
diff --git a/modules/ext/alias.js b/modules/ext/alias.js
index 4ffab72..80663ac 100644
--- a/modules/ext/alias.js
+++ b/modules/ext/alias.js
@@ -104,5 +104,5 @@ class AliasModule {
}
}
if (typeof window.webx11 !== 'undefined' && window.webx11) {
- webx11.moduleManager.registerModule(new AliasModule(), 'https://webx11.duckdns.org/spaces/chatstudio/newos4/modules/ext/alias.js');
+ webx11.moduleManager.registerModule(new AliasModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/alias.js');
}
diff --git a/modules/ext/busybox.js b/modules/ext/busybox.js
index f143087..709324f 100644
--- a/modules/ext/busybox.js
+++ b/modules/ext/busybox.js
@@ -405,3 +405,6 @@ class BusyBoxModule {
this.terminal.print(expanded, 'info');
}
}
+if (typeof window.webx11 !== 'undefined' && window.webx11) {
+ webx11.moduleManager.registerModule(new BusyBoxModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/busybox.js');
+}
diff --git a/modules/ext/calc.js b/modules/ext/calc.js
index e28079e..9b602f6 100644
--- a/modules/ext/calc.js
+++ b/modules/ext/calc.js
@@ -73,5 +73,5 @@ class CalcModule {
}
}
if (typeof window.webx11 !== 'undefined' && window.webx11) {
- webx11.moduleManager.registerModule(new CalcModule(), 'https://webx11.duckdns.org/spaces/chatstudio/newos4/modules/calc.js');
+ webx11.moduleManager.registerModule(new CalcModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/calc.js');
}
diff --git a/modules/ext/cron.js b/modules/ext/cron.js
index 66cd18e..5ed5ff6 100644
--- a/modules/ext/cron.js
+++ b/modules/ext/cron.js
@@ -106,5 +106,5 @@ class CronModule {
}
}
if (typeof window.webx11 !== 'undefined' && window.webx11) {
- webx11.moduleManager.registerModule(new CronModule(), 'https://webx11.duckdns.org/spaces/chatstudio/newos4/modules/cron.js');
+ webx11.moduleManager.registerModule(new CronModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/cron.js');
}
diff --git a/modules/ext/cryptogzip.js b/modules/ext/cryptogzip.js
index 6f873ff..b67e067 100644
--- a/modules/ext/cryptogzip.js
+++ b/modules/ext/cryptogzip.js
@@ -1,9 +1,18 @@
class CryptoGzipModule {
// CryptoGzipModule — VFS-level gzip/encrypt commands for WebX11 newOS4
- // Commands: gzip <path>, gunzip <path.gz>, encrypt <path>, decrypt <path.enc>
- constructor(terminal) {
- this.terminal = terminal;
+ // Commands: gzip <path>, gunzip <path.gz>, encrypt <path> <passphrase>, decrypt <path.enc> <passphrase>
+ constructor() {
this.commands = {};
+ this.terminal = null;
+ }
+
+ getHelpInfo() {
+ return {
+ 'gzip': 'gzip <path> — Compress a VFS file to .gz',
+ 'gunzip': 'gunzip <path.gz> — Decompress a .gz file',
+ 'encrypt': 'encrypt <path> <passphrase> — AES-256-GCM encrypt a VFS file',
+ 'decrypt': 'decrypt <path.enc> <passphrase> — Decrypt a .enc file'
+ };
}
_vfsExists(path) {
@@ -279,3 +288,6 @@ class CryptoGzipModule {
};
}
}
+if (typeof window.webx11 !== 'undefined' && window.webx11) {
+ webx11.moduleManager.registerModule(new CryptoGzipModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/cryptogzip.js');
+}
diff --git a/modules/ext/diff.js b/modules/ext/diff.js
index 8b79b6b..64469ac 100644
--- a/modules/ext/diff.js
+++ b/modules/ext/diff.js
@@ -283,5 +283,5 @@ class DiffModule {
}
}
if (typeof window.webx11 !== 'undefined' && window.webx11) {
- webx11.moduleManager.registerModule(new DiffModule(), 'https://webx11.duckdns.org/spaces/chatstudio/newos4/modules/diff.js');
+ webx11.moduleManager.registerModule(new DiffModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/diff.js');
}
diff --git a/modules/ext/editor.js b/modules/ext/editor.js
index 571a535..c597dd5 100644
--- a/modules/ext/editor.js
+++ b/modules/ext/editor.js
@@ -66,5 +66,5 @@ class EditorModule {
}
}
if (typeof window.webx11 !== 'undefined' && window.webx11) {
- webx11.moduleManager.registerModule(new EditorModule(), 'https://webx11.duckdns.org/spaces/chatstudio/newos4/modules/editor.js');
+ webx11.moduleManager.registerModule(new EditorModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/editor.js');
}
diff --git a/modules/ext/filemgr.js b/modules/ext/filemgr.js
index 5532cee..4074f06 100644
--- a/modules/ext/filemgr.js
+++ b/modules/ext/filemgr.js
@@ -7,6 +7,7 @@ class FileManagerModule {
this._selectedFile = null;
this._searchFilter = '';
this._rawMode = false;
+ this._inlineHtml = false;
}
requirements() {
@@ -15,8 +16,8 @@ class FileManagerModule {
getHelpInfo() {
return {
- 'files': 'Open the file manager overlay with VFS directory listing and preview',
- 'preview': 'Preview a specific file (usage: preview <path>)',
+ 'files': 'files — Open the file manager overlay with VFS directory listing, preview, encrypt, zip',
+ 'preview': 'preview <path> — Preview a specific file',
};
}
@@ -52,15 +53,16 @@ class FileManagerModule {
_detectType(filePath) {
const ext = filePath.split('.').pop().toLowerCase();
if (!ext || ext === filePath) return 'other';
- const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
- const videoExts = ['mp4', 'webm'];
- const audioExts = ['mp3', 'wav', 'ogg'];
- const codeExts = ['js', 'py', 'html', 'css', 'yaml', 'yml', 'toml'];
+ const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'ico'];
+ const videoExts = ['mp4', 'webm', 'avi', 'mov', 'mkv'];
+ const audioExts = ['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a'];
+ const codeExts = ['js', 'py', 'css', 'yaml', 'yml', 'toml'];
if (ext === 'md') return 'md';
if (imageExts.includes(ext)) return 'image';
if (videoExts.includes(ext)) return 'video';
if (audioExts.includes(ext)) return 'audio';
if (codeExts.includes(ext)) return 'code';
+ if (ext === 'html') return 'html';
if (ext === 'json') return 'json';
if (ext === 'csv') return 'csv';
if (ext === 'pdf') return 'pdf';
@@ -113,7 +115,10 @@ class FileManagerModule {
const fileName = path.split('/').pop() || path;
// Handle binary detection for media types
- const isLikelyBinary = typeof content === 'string' && content.length > 0 && !content.startsWith('\n') && !content.startsWith('\r') && !content.startsWith(' ') && !content.startsWith('<') && !content.startsWith('{') && !content.startsWith('[') && content.charCodeAt(0) < 32 && content.charCodeAt(0) !== 9 && content.charCodeAt(0) !== 10 && content.charCodeAt(0) !== 13;
+ if (typeof content !== 'string') {
+ return '<div style="padding:20px;text-align:center;color:var(--text-muted)">Cannot preview: non-string content</div>';
+ }
+ const isLikelyBinary = content.length > 0 && !content.startsWith('\n') && !content.startsWith('\r') && !content.startsWith(' ') && !content.startsWith('<') && !content.startsWith('{') && !content.startsWith('[') && content.charCodeAt(0) < 32 && content.charCodeAt(0) !== 9 && content.charCodeAt(0) !== 10 && content.charCodeAt(0) !== 13;
const isTextContent = typeof content === 'string' && (content.startsWith('{') || content.startsWith('[') || content.startsWith('<') || content.startsWith('#') || /^[\w\s\n\r\t.,;:!?()\-=+*@#$%^&"'\/\\|`~]/.test(content.substring(0, 100)));
@@ -121,6 +126,17 @@ class FileManagerModule {
return '<div class="markdown-preview" style="padding:12px;font-family:var(--font-primary);line-height:1.5">' + this._renderMarkdown(content) + '</div>';
}
+ if (type === 'html') {
+ // Render HTML in an inline sandbox frame
+ const safeHtml = content
+ .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
+ .replace(/on\w+\s*=\s*"[^"]*"/gi, '')
+ .replace(/on\w+\s*=\s*'[^']*'/gi, '');
+ return '<div style="padding:0;width:100%;height:60vh;border:none;overflow:hidden">' +
+ '<iframe srcdoc="' + this._escapeHtml(safeHtml.replace(/"/g, '&quot;').replace(/'/g, '&#39;')) + '" ' +
+ 'style="width:100%;height:100%;border:none;background:#fff" sandbox="allow-same-origin"></iframe></div>';
+ }
+
if (type === 'text') {
const truncated = content.substring(0, 2000);
const extra = content.length > 2000 ? '<div style="color:var(--text-muted);margin-top:8px">(truncated to 2000 chars)</div>' : '';
@@ -158,11 +174,14 @@ class FileManagerModule {
}
if (type === 'image') {
- if (isLikelyBinary && !isTextContent) {
- return '<div style="padding:20px;text-align:center;color:var(--text-muted)">📷 Image — <span style="color:var(--info-color);cursor:pointer;text-decoration:underline" onclick="' + this._escapeHtml("var a=document.createElement('a');a.href='data:image/" + ext + ";base64," + btoa(content) + "';a.download='" + fileName + "';a.click()") + '">download to view</span></div>';
+ // VFS stores images as base64 strings — detect and render inline
+ const isBase64 = /^[A-Za-z0-9+/=]+$/.test(content.substring(0, 100));
+ if (isBase64) {
+ const mimeTypes = {jpg:'image/jpeg',jpeg:'image/jpeg',png:'image/png',gif:'image/gif',webp:'image/webp',bmp:'image/bmp',ico:'image/x-icon'};
+ const mime = mimeTypes[ext] || 'image/png';
+ return '<div style="padding:12px;text-align:center;max-width:100%"><img src="data:' + mime + ';base64,' + content + '" style="max-width:100%;max-height:70vh;border-radius:4px;border:1px solid var(--border-color)" alt="' + this._escapeHtml(fileName) + '"></div>';
}
- const base64 = btoa(unescape(encodeURIComponent(content)));
- return '<div style="padding:12px;text-align:center"><img src="data:image/' + ext + ';base64,' + base64 + '" style="max-width:80%;max-height:60vh;border-radius:4px;border:1px solid var(--border-color)" alt="' + this._escapeHtml(fileName) + '"></div>';
+ return '<div style="padding:20px;text-align:center;color:var(--text-muted)">📷 Image — cannot render, file may be stored as binary</div>';
}
if (type === 'svg') {
@@ -187,28 +206,31 @@ class FileManagerModule {
}
if (type === 'video') {
- if (isLikelyBinary && !isTextContent) {
- return '<div style="padding:20px;text-align:center;color:var(--text-muted)">📷 Video — <span style="color:var(--info-color);cursor:pointer;text-decoration:underline" onclick="var a=document.createElement(\'a\');a.href=\'data:video/' + ext + ';base64,' + btoa(content) + '\';a.download=\'' + fileName + '\';a.click()">download to view</span></div>';
+ const isBase64 = /^[A-Za-z0-9+/=]+$/.test(content.substring(0, 100));
+ if (isBase64) {
+ const mimeTypes = {mp4:'video/mp4',webm:'video/webm',avi:'video/x-msvideo',mov:'video/quicktime',mkv:'video/x-matroska'};
+ const mime = mimeTypes[ext] || 'video/mp4';
+ return '<div style="padding:12px;text-align:center"><video controls style="max-width:90%;max-height:65vh;border-radius:4px"><source src="data:' + mime + ';base64,' + content + '" type="' + mime + '"></video></div>';
}
- const base64 = btoa(unescape(encodeURIComponent(content)));
- return '<div style="padding:12px;text-align:center"><video controls style="max-width:80%;max-height:60vh;border-radius:4px"><source src="data:video/' + ext + ';base64,' + base64 + '" type="video/' + ext + '"></video></div>';
+ return '<div style="padding:20px;text-align:center;color:var(--text-muted)">🎬 Video — cannot render</div>';
}
if (type === 'audio') {
- if (isLikelyBinary && !isTextContent) {
- return '<div style="padding:20px;text-align:center;color:var(--text-muted)">🎵 Audio — <span style="color:var(--info-color);cursor:pointer;text-decoration:underline" onclick="var a=document.createElement(\'a\');a.href=\'data:audio/' + ext + ';base64,' + btoa(content) + '\';a.download=\'' + fileName + '\';a.click()">download to play</span></div>';
+ const isBase64 = /^[A-Za-z0-9+/=]+$/.test(content.substring(0, 100));
+ if (isBase64) {
+ const mimeTypes = {mp3:'audio/mpeg',wav:'audio/wav',ogg:'audio/ogg',flac:'audio/flac',aac:'audio/aac',m4a:'audio/mp4'};
+ const mime = mimeTypes[ext] || 'audio/mpeg';
+ return '<div style="padding:12px;text-align:center"><audio controls style="max-width:90%"><source src="data:' + mime + ';base64,' + content + '" type="' + mime + '"></audio></div>';
}
- const base64 = btoa(unescape(encodeURIComponent(content)));
- return '<div style="padding:12px;text-align:center"><audio controls style="max-width:80%"><source src="data:audio/' + ext + ';base64,' + base64 + '" type="audio/' + ext + '"></audio></div>';
+ return '<div style="padding:20px;text-align:center;color:var(--text-muted)">🎵 Audio — cannot render</div>';
}
if (type === 'pdf') {
- try {
- const base64 = btoa(unescape(encodeURIComponent(content)));
- return '<div style="padding:12px;text-align:center;height:70vh"><embed src="data:application/pdf;base64,' + base64 + '" type="application/pdf" style="width:100%;height:100%;border:1px solid var(--border-color);border-radius:4px"></div>';
- } catch (e) {
- return '<div style="padding:20px;text-align:center;color:var(--text-muted)">📄 PDF — <span style="color:var(--info-color);cursor:pointer;text-decoration:underline" onclick="var a=document.createElement(\'a\');a.href=\'data:application/pdf;base64,' + btoa(content) + '\';a.download=\'' + fileName + '\';a.click()">download to view</span></div>';
+ const isBase64 = /^[A-Za-z0-9+/=]+$/.test(content.substring(0, 100));
+ if (isBase64) {
+ return '<div style="padding:12px;text-align:center;height:70vh"><embed src="data:application/pdf;base64,' + content + '" type="application/pdf" style="width:100%;height:100%;border:1px solid var(--border-color);border-radius:4px"></div>';
}
+ return '<div style="padding:20px;text-align:center;color:var(--text-muted)">📄 PDF — cannot render</div>';
}
if (type === 'csv') {
@@ -285,7 +307,7 @@ class FileManagerModule {
const overlay = document.createElement('div');
overlay.id = 'filemgr-overlay';
- overlay.style.cssText = 'position:fixed;inset:0;z-index:1800;display:flex;flex-direction:column;background:var(--bg-color);color:var(--text-color);font-family:var(--font-primary);font-size:14px;';
+ overlay.style.cssText = 'position:fixed;inset:0;z-index:1800;display:flex;flex-direction:column;background:var(--bg-color);color:var(--text-color);font-family:var(--font-primary);font-size:14px;height:100vh;';
// Inject responsive styles for the overlay
const styleId = 'filemgr-styles';
@@ -318,10 +340,11 @@ class FileManagerModule {
border-right: none !important;
border-bottom: 1px solid var(--border-color);
}
- #filemgr-overlay #filemgr-preview {
- width: 100% !important;
- min-height: 40vh;
- }
+ #filemgr-overlay #filemgr-preview {
+ width: 100% !important;
+ flex: 1 !important;
+ min-height: 0;
+ }
#filemgr-action-bar {
flex-wrap: wrap;
gap: 6px;
@@ -384,12 +407,12 @@ class FileManagerModule {
const mainArea = document.createElement('div');
mainArea.id = 'filemgr-main-container';
mainArea.className = 'filemgr-main';
- mainArea.style.cssText = 'overflow:hidden;';
+ mainArea.style.cssText = 'flex:1;overflow:hidden;display:flex;min-height:0;';
// Sidebar
const sidebar = document.createElement('div');
sidebar.id = 'filemgr-sidebar';
- sidebar.style.cssText = 'width:30%;min-width:200px;overflow-y:auto;background:var(--bg-secondary);border-right:1px solid var(--border-color);';
+ sidebar.style.cssText = 'width:25%;min-width:150px;overflow-y:auto;background:var(--bg-secondary);border-right:1px solid var(--border-color);';
mainArea.appendChild(sidebar);
@@ -413,6 +436,18 @@ class FileManagerModule {
statusSpan.textContent = 'No file selected';
actionBar.appendChild(statusSpan);
+ const encryptBtn = document.createElement('button');
+ encryptBtn.textContent = '🔐 Encrypt';
+ encryptBtn.style.cssText = 'padding:6px 14px;background:var(--bg-tertiary);border:1px solid var(--warning-color);color:var(--warning-color);border-radius:4px;cursor:pointer;font-family:inherit;font-size:0.9em;min-height:44px;touch-action:manipulation;';
+ encryptBtn.onclick = () => this._actionEncrypt();
+ actionBar.appendChild(encryptBtn);
+
+ const compressBtn = document.createElement('button');
+ compressBtn.textContent = '📦 Zip';
+ compressBtn.style.cssText = 'padding:6px 14px;background:var(--bg-tertiary);border:1px solid var(--info-color);color:var(--info-color);border-radius:4px;cursor:pointer;font-family:inherit;font-size:0.9em;min-height:44px;touch-action:manipulation;';
+ compressBtn.onclick = () => this._actionCompress();
+ actionBar.appendChild(compressBtn);
+
const deleteBtn = document.createElement('button');
deleteBtn.textContent = '🗑 Delete';
deleteBtn.style.cssText = 'padding:6px 14px;background:var(--bg-tertiary);border:1px solid var(--error-color);color:var(--error-color);border-radius:4px;cursor:pointer;font-family:inherit;font-size:0.9em;min-height:44px;touch-action:manipulation;';
@@ -431,6 +466,12 @@ class FileManagerModule {
editBtn.onclick = () => this._actionEdit();
actionBar.appendChild(editBtn);
+ const newFileBtn = document.createElement('button');
+ newFileBtn.textContent = '📄 New';
+ newFileBtn.style.cssText = 'padding:6px 14px;background:var(--bg-tertiary);border:1px solid var(--success-color);color:var(--success-color);border-radius:4px;cursor:pointer;font-family:inherit;font-size:0.9em;min-height:44px;touch-action:manipulation;';
+ newFileBtn.onclick = () => this._actionNewFile();
+ actionBar.appendChild(newFileBtn);
+
const previewToggleBtn = document.createElement('button');
previewToggleBtn.id = 'filemgr-toggle-raw';
previewToggleBtn.textContent = '📄 Raw';
@@ -727,9 +768,14 @@ class FileManagerModule {
this._updateStatus('No file selected to edit');
return;
}
- if (typeof webx11 !== 'undefined' && webx11 && webx11.executeCommand) {
- webx11.executeCommand('edit ' + this._selectedFile);
- this._updateStatus('✏ Opened in editor: ' + this._selectedFile);
+ const path = this._selectedFile;
+ // Close file manager first so code editor modal (z-index:1000) is visible above
+ this._closeOverlay();
+ if (typeof webx11 !== 'undefined' && webx11 && webx11.dispatchCommand) {
+ setTimeout(() => {
+ webx11.dispatchCommand('edit', [path]);
+ }, 100);
+ this._updateStatus('✏ Opened in editor: ' + path);
} else {
this._updateStatus('❌ Editor not available');
}
@@ -748,8 +794,61 @@ class FileManagerModule {
this._renderPreview(this._selectedFile, content);
}
}
+
+ _actionEncrypt() {
+ if (!this._selectedFile) {
+ this._updateStatus('No file selected');
+ return;
+ }
+ if (this._selectedFile.endsWith('.enc')) {
+ this._updateStatus('⚠ Already encrypted (.enc)');
+ return;
+ }
+ const passphrase = prompt('Enter passphrase for AES-256-GCM encryption:');
+ if (!passphrase || passphrase.length < 4) {
+ this._updateStatus('❌ Passphrase too short (min 4 chars)');
+ return;
+ }
+ // Delegate to cryptogzip module's encrypt command
+ if (typeof webx11 !== 'undefined' && webx11.executeCommand) {
+ webx11.executeCommand('encrypt ' + this._selectedFile + ' ' + passphrase);
+ this._updateStatus('🔐 Encrypted: ' + this._selectedFile + '.enc');
+ this._refreshFileList();
+ }
+ }
+
+ _actionCompress() {
+ if (!this._selectedFile) {
+ this._updateStatus('No file selected');
+ return;
+ }
+ if (this._selectedFile.endsWith('.gz')) {
+ this._updateStatus('⚠ Already compressed (.gz)');
+ return;
+ }
+ if (typeof webx11 !== 'undefined' && webx11.executeCommand) {
+ webx11.executeCommand('gzip ' + this._selectedFile);
+ this._updateStatus('📦 Compressed: ' + this._selectedFile + '.gz');
+ this._refreshFileList();
+ }
+ }
+
+ _actionNewFile() {
+ const name = prompt('New file name (e.g. myfile.txt):');
+ if (!name) return;
+ const pathStr = '/' + this.currentPath.join('/');
+ const cleanPath = pathStr === '/' ? '/' + name : pathStr + '/' + name;
+ const content = prompt('Enter file content (optional):') || '';
+ const ok = this.terminal.fs.writeFile(cleanPath, content);
+ if (ok) {
+ this._updateStatus('✅ Created: ' + cleanPath + ' (' + content.length + ' chars)');
+ this._refreshFileList();
+ } else {
+ this._updateStatus('❌ Failed to create file');
+ }
+ }
}
if (typeof window.webx11 !== 'undefined' && window.webx11) {
- webx11.moduleManager.registerModule(new FileManagerModule(), 'https://webx11.duckdns.org/spaces/chatstudio/newos4/modules/ext/filemgr.js');
+ webx11.moduleManager.registerModule(new FileManagerModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/filemgr.js');
}
diff --git a/modules/ext/finder.js b/modules/ext/finder.js
index 1ca8cf7..8230668 100644
--- a/modules/ext/finder.js
+++ b/modules/ext/finder.js
@@ -43,5 +43,5 @@ class FinderModule {
}
}
if (typeof window.webx11 !== 'undefined' && window.webx11) {
- webx11.moduleManager.registerModule(new FinderModule(), 'https://webx11.duckdns.org/spaces/chatstudio/newos4/modules/finder.js');
+ webx11.moduleManager.registerModule(new FinderModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/finder.js');
}
diff --git a/modules/ext/mindmap.js b/modules/ext/mindmap.js
index 30d6889..9f62cba 100644
--- a/modules/ext/mindmap.js
+++ b/modules/ext/mindmap.js
@@ -166,7 +166,9 @@ class MindmapModule {
if (!args[0]) return this.terminal.print('Usage: mindmap save <name>', 'warning');
if (!this.root) return this.terminal.print('No mindmap to save.', 'warning');
try {
- localStorage.setItem('webx11_mindmap_' + args[0], JSON.stringify(this.root));
+ const fs = this.terminal.fs;
+ fs.mkdirRecursive('/mindmaps');
+ fs.writeFile('/mindmaps/' + args[0] + '.json', JSON.stringify(this.root, null, 2));
this.terminal.print('Mindmap saved as: ' + args[0], 'success');
} catch (e) {
this.terminal.print('Failed to save: ' + e.message, 'error');
@@ -175,7 +177,8 @@ class MindmapModule {
_load(args) {
if (!args[0]) return this.terminal.print('Usage: mindmap load <name>', 'warning');
try {
- const data = localStorage.getItem('webx11_mindmap_' + args[0]);
+ const fs = this.terminal.fs;
+ const data = fs.readFile('/mindmaps/' + args[0] + '.json');
if (!data) return this.terminal.print('No saved mindmap: ' + args[0], 'error');
this.root = JSON.parse(data);
this.terminal.print('Loaded mindmap: ' + args[0] + ' (' + this._countNodes(this.root) + ' nodes)', 'success');
@@ -184,18 +187,17 @@ class MindmapModule {
}
}
_list() {
- const keys = [];
- for (let i = 0; i < localStorage.length; i++) {
- const k = localStorage.key(i);
- if (k && k.startsWith('webx11_mindmap_')) keys.push(k.replace('webx11_mindmap_', ''));
- }
+ const fs = this.terminal.fs;
+ const files = fs.readDir('/mindmaps') || [];
+ const keys = files.filter(f => f.name.endsWith('.json')).map(f => f.name.replace('.json', ''));
if (!keys.length) return this.terminal.print('No saved mindmaps.', 'info');
this.terminal.print('=== Saved Mindmaps ===', 'info');
keys.forEach(k => this.terminal.print(' ' + k));
}
_delete(args) {
if (!args[0]) return this.terminal.print('Usage: mindmap delete <name>', 'warning');
- localStorage.removeItem('webx11_mindmap_' + args[0]);
+ const fs = this.terminal.fs;
+ fs.deleteFile('/mindmaps/' + args[0] + '.json');
this.terminal.print('Deleted mindmap: ' + args[0], 'success');
}
_rename(args) {
@@ -232,5 +234,5 @@ class MindmapModule {
}
}
if (typeof window.webx11 !== 'undefined' && window.webx11) {
- webx11.moduleManager.registerModule(new MindmapModule(), 'https://webx11.duckdns.org/spaces/chatstudio/newos4/modules/mindmap.js');
+ webx11.moduleManager.registerModule(new MindmapModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/mindmap.js');
}
diff --git a/modules/ext/shell.js b/modules/ext/shell.js
index d5e6a43..6beb0eb 100644
--- a/modules/ext/shell.js
+++ b/modules/ext/shell.js
@@ -515,3 +515,6 @@ class ShellModule {
this.terminal.print(`Script ${path} completed (${lineNum} lines)`, 'success');
}
}
+if (typeof window.webx11 !== 'undefined' && window.webx11) {
+ webx11.moduleManager.registerModule(new ShellModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/shell.js');
+}
diff --git a/modules/ext/skill-pack-core.js b/modules/ext/skill-pack-core.js
index 51a2e55..cc8ae88 100644
--- a/modules/ext/skill-pack-core.js
+++ b/modules/ext/skill-pack-core.js
@@ -283,3 +283,6 @@ class SkillPackCoreModule {
this._seeded = true;
}
}
+if (typeof window.webx11 !== 'undefined' && window.webx11) {
+ webx11.moduleManager.registerModule(new SkillPackCoreModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/skill-pack-core.js');
+}
diff --git a/modules/ext/theme.js b/modules/ext/theme.js
index 9a1dab5..1d99b2c 100644
--- a/modules/ext/theme.js
+++ b/modules/ext/theme.js
@@ -168,5 +168,5 @@ class ThemeModule {
}
}
if (typeof window.webx11 !== 'undefined' && window.webx11) {
- webx11.moduleManager.registerModule(new ThemeModule(), 'https://webx11.duckdns.org/spaces/chatstudio/newos4/modules/theme.js');
+ webx11.moduleManager.registerModule(new ThemeModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/theme.js');
}
diff --git a/modules/ext/voice.js b/modules/ext/voice.js
index 0fd96cd..06c5480 100644
--- a/modules/ext/voice.js
+++ b/modules/ext/voice.js
@@ -98,5 +98,5 @@ class VoiceModule {
}
}
if (typeof window.webx11 !== 'undefined' && window.webx11) {
- webx11.moduleManager.registerModule(new VoiceModule(), 'https://webx11.duckdns.org/spaces/chatstudio/newos4/modules/voice.js');
+ webx11.moduleManager.registerModule(new VoiceModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/voice.js');
}
diff --git a/modules/ext/wpm.js b/modules/ext/wpm.js
index 9c8d820..4dc9c29 100644
--- a/modules/ext/wpm.js
+++ b/modules/ext/wpm.js
@@ -2,7 +2,7 @@ class WpmModule {
constructor() {
this.commands = {};
this.terminal = null;
- this.indexUrl = 'https://webx11.duckdns.org/spaces/chatstudio/newos4/packages.json';
+ this.indexUrl = 'https://webx11.duckdns.org/spaces/newos4/packages.json';
this.index = null;
this.installed = this.loadInstalled();
}
@@ -145,5 +145,5 @@ class WpmModule {
}
}
if (typeof window.webx11 !== 'undefined' && window.webx11) {
- webx11.moduleManager.registerModule(new WpmModule(), 'https://webx11.duckdns.org/spaces/chatstudio/newos4/modules/wpm.js');
+ webx11.moduleManager.registerModule(new WpmModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/wpm.js');
}