Skip to content

Commit d50ed62

Browse files
improvised help menu and import vfs system
1 parent 6d03c22 commit d50ed62

File tree

3 files changed

+120
-43
lines changed

3 files changed

+120
-43
lines changed

src/assets/help.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
MiaoShell v1.0.0 - Help Guide
2+
========================================
3+
4+
BASIC COMMANDS:
5+
about - Show terminal information
6+
clear - Clear the screen
7+
date - Show current date/time
8+
echo - Print text (echo "hello")
9+
help - Show this help
10+
11+
VIRTUAL FILE SYSTEM:
12+
cat - Show file contents (cat file.txt)
13+
cd - Change directory (cd dir / cd .. / cd)
14+
ls - List files and directories
15+
mkdir - Create directory (mkdir docs)
16+
rm - Delete file (rm file.txt)
17+
rmdir - Remove directory (rmdir docs)
18+
write - Create file (write file.txt content)
19+
20+
VFS STORAGE:
21+
export - Export VFS to JSON file (download)
22+
import - Import from JSON (import from files)
23+
load - Load VFS from browser storage
24+
save - Save VFS to browser storage
25+
storage - Show storage usage stats
26+
27+
TIPS:
28+
• Commands are case-insensitive
29+
• Directories shown with trailing "/"
30+
• VFS persists in localStorage (512 KB limit)
31+
• Use quotes for spaces: write "my file" "content"
32+
33+
Repository: https://github.com/daniel4-scratch/xtermtest

src/script.js

Lines changed: 68 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -110,24 +110,21 @@ function getSizeInKB(variable) {
110110
}
111111

112112
const commands = {
113-
help: () => {
114-
term.writeln('\r\nAvailable commands:');
115-
term.writeln(' help - Show this help message');
116-
term.writeln(' clear - Clear the terminal');
117-
term.writeln(' echo - Echo back your message');
118-
term.writeln(' date - Show current date and time');
119-
term.writeln(' cd - Change directory: cd dirname or cd .. or cd (for root)');
120-
term.writeln(' ls - List files in the current directory');
121-
term.writeln(' cat - Show file contents: cat filename');
122-
term.writeln(' write - Write to a file: write filename content');
123-
term.writeln(' mkdir - Create directory: mkdir dirname');
124-
term.writeln(' rmdir - Remove directory: rmdir dirname');
125-
term.writeln(' rm - Remove a file: rm filename');
126-
term.writeln(' save - Save VFS to browser storage');
127-
term.writeln(' load - Load VFS from browser storage');
128-
term.writeln(' import - Import VFS from JSON file: import filename.json');
129-
term.writeln(' storage - Show VFS storage usage');
130-
term.writeln(' about - About this terminal');
113+
help: async () => {
114+
await fetch('./assets/help.txt')
115+
.then(response => {
116+
if (response.status === 200) {
117+
return response.text();
118+
} else {
119+
throw new Error('HTTP ' + response.status);
120+
}
121+
})
122+
.then(text => {
123+
text.split(/\r?\n/).forEach(line => term.writeln(line));
124+
})
125+
.catch(error => {
126+
term.writeln('Error loading file: ' + error);
127+
});
131128
},
132129
clear: () => {
133130
setTimeout(() => {
@@ -250,27 +247,45 @@ const commands = {
250247
loadVFS();
251248
term.writeln('\r\nVFS loaded from browser storage.');
252249
},
253-
import: (args) => {
254-
if (!args[0]) {
255-
term.writeln('\r\nUsage: import filename.json');
256-
return;
257-
}
258-
const filename = args[0];
259-
fetch(filename)
260-
.then(response => {
261-
if (!response.ok) {
262-
throw new Error(`HTTP ${response.status}`);
250+
import: async () => {
251+
try {
252+
// Open the file picker
253+
const [fileHandle] = await window.showOpenFilePicker();
254+
const file = await fileHandle.getFile();
255+
if (file.size > 512 * 1024) {
256+
term.writeln('\r\nFile is too large. Maximum size is 512 KB.');
257+
return;
258+
}
259+
const fileContent = await file.text();
260+
//check if valid json
261+
try {
262+
const importedVFS = JSON.parse(fileContent);
263+
if (typeof importedVFS === 'object' && importedVFS !== null) {
264+
vfs = importedVFS;
265+
cwd_path = '/'; // Reset to root after import
266+
term.writeln('\r\nVFS imported successfully.');
267+
} else {
268+
term.writeln('\r\nInvalid VFS format. Must be a JSON object.');
263269
}
264-
return response.json();
265-
})
266-
.then(data => {
267-
Object.assign(vfs, data);
268-
saveVFS();
269-
term.writeln(`\r\nImported ${Object.keys(data).length} files from ${filename}`);
270-
})
271-
.catch(error => {
272-
term.writeln(`\r\nError importing ${filename}: ${error.message}`);
273-
});
270+
} catch (e) {
271+
term.writeln('\r\nError parsing VFS file. Ensure it is a valid JSON object.');
272+
}
273+
} catch (error) {
274+
term.writeln('\r\nError importing VFS:', error);
275+
}
276+
},
277+
export: () => {
278+
const vfsString = JSON.stringify(vfs, null, 2);
279+
const blob = new Blob([vfsString], { type: 'application/json' });
280+
const url = URL.createObjectURL(blob);
281+
const a = document.createElement('a');
282+
a.href = url;
283+
a.download = 'vfs.json';
284+
document.body.appendChild(a);
285+
a.click();
286+
document.body.removeChild(a);
287+
URL.revokeObjectURL(url);
288+
term.writeln('\r\nVFS exported as vfs.json');
274289
},
275290
storage: () => {
276291
term.writeln('\r\nVirtual File System Storage:');
@@ -295,10 +310,23 @@ function executeCommand(input) {
295310
}
296311

297312
if (commands[command]) {
298-
commands[command](args);
313+
const result = commands[command](args);
314+
// If the command returns a promise, wait for it to complete
315+
if (result instanceof Promise) {
316+
result.then(() => {
317+
prompt();
318+
}).catch(error => {
319+
term.writeln('\r\nCommand error: ' + error.message);
320+
prompt();
321+
});
322+
} else {
323+
// For non-async commands, show prompt immediately
324+
prompt();
325+
}
299326
} else {
300327
term.writeln('\r\nCommand not found: ' + command);
301328
term.writeln('Type "help" for available commands.');
329+
prompt();
302330
}
303331
}
304332

@@ -314,7 +342,7 @@ term.onData(data => {
314342
if (code === 13) { // Enter key
315343
executeCommand(currentInput);
316344
currentInput = '';
317-
prompt();
345+
// Note: prompt() is now called from executeCommand after async operations complete
318346
} else if (code === 127) { // Backspace
319347
if (currentInput.length > 0) {
320348
currentInput = currentInput.slice(0, -1);

vite.config.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { defineConfig } from 'vite'
2-
import { copyFileSync, mkdirSync } from 'fs'
2+
import { copyFileSync, mkdirSync, readdirSync, statSync } from 'fs'
33
import { join } from 'path'
44

55
export default defineConfig({
@@ -26,8 +26,24 @@ export default defineConfig({
2626
writeBundle() {
2727
// Create assets directory in dist
2828
mkdirSync('dist/assets', { recursive: true })
29-
// Copy miao.txt to assets folder
30-
copyFileSync('src/assets/miao.txt', 'dist/assets/miao.txt')
29+
30+
// Automatically copy all files from src/assets to dist/assets
31+
const assetsDir = 'src/assets'
32+
try {
33+
const files = readdirSync(assetsDir)
34+
files.forEach(file => {
35+
const srcPath = join(assetsDir, file)
36+
const destPath = join('dist/assets', file)
37+
38+
// Only copy files (not directories)
39+
if (statSync(srcPath).isFile()) {
40+
copyFileSync(srcPath, destPath)
41+
console.log(`Copied: ${file}`)
42+
}
43+
})
44+
} catch (error) {
45+
console.warn('Could not copy assets:', error.message)
46+
}
3147
}
3248
}
3349
]

0 commit comments

Comments
 (0)