@@ -4,22 +4,6 @@ import { FitAddon } from '@xterm/addon-fit';
44import { WebLinksAddon } from 'xterm-addon-web-links' ;
55import '@xterm/xterm/css/xterm.css' ;
66
7- // Custom beep function using Web Audio API
8- function squareBeep ( freq = 440 , duration = 0.1 ) {
9- const audioCtx = new ( window . AudioContext || window . webkitAudioContext ) ( ) ;
10- const oscillator = audioCtx . createOscillator ( ) ;
11- const gainNode = audioCtx . createGain ( ) ;
12-
13- oscillator . type = 'square' ;
14- oscillator . frequency . value = freq ; // in Hz
15-
16- oscillator . connect ( gainNode ) ;
17- gainNode . connect ( audioCtx . destination ) ;
18-
19- oscillator . start ( ) ;
20- oscillator . stop ( audioCtx . currentTime + duration ) ;
21- }
22-
237// ANSI color codes for terminal formatting
248const colors = {
259 reset : '\x1B[0m' ,
@@ -45,8 +29,6 @@ window.addEventListener('resize', () => {
4529term . open ( document . getElementById ( 'terminal' ) ) ;
4630term . loadAddon ( new WebLinksAddon ( ) ) ;
4731
48-
49-
5032let currentInput = '' ;
5133let currentLine = '' ;
5234let cursorPosition = 0 ;
@@ -56,6 +38,52 @@ let cursorPosition = 0;
5638// --- Virtual File System ---
5739let vfs = { } ;
5840const VFS_KEY = 'miaoshell-vfs' ;
41+ let cwd_path = '/' ; // Current working directory
42+
43+ //convert cwd to a return vfs object
44+ function cwd ( ) {
45+ if ( cwd_path === '/' ) {
46+ return vfs ;
47+ }
48+ const parts = cwd_path . split ( '/' ) . filter ( Boolean ) ;
49+ let current = vfs ;
50+ for ( const part of parts ) {
51+ if ( current [ part ] && typeof current [ part ] === 'object' ) {
52+ current = current [ part ] ;
53+ } else {
54+ return { } ; // Return empty object if path doesn't exist
55+ }
56+ }
57+ return current ;
58+ }
59+
60+ //convert vfs object to cwd path
61+ function vfsToCwd ( vfsObj ) {
62+ if ( vfsObj === vfs ) {
63+ return '/' ;
64+ }
65+ const parts = [ ] ;
66+ function findPath ( obj , currentPath ) {
67+ for ( const key in obj ) {
68+ if ( obj . hasOwnProperty ( key ) ) {
69+ const newPath = currentPath ? `${ currentPath } /${ key } ` : key ;
70+ if ( obj [ key ] === vfsObj ) {
71+ parts . push ( newPath ) ;
72+ return true ; // Found the path
73+ }
74+ if ( typeof obj [ key ] === 'object' ) {
75+ if ( findPath ( obj [ key ] , newPath ) ) {
76+ return true ; // Found in subdirectory
77+ }
78+ }
79+ }
80+ }
81+ return false ; // Not found in this branch
82+ }
83+ findPath ( vfs , '' ) ;
84+ return parts . length > 0 ? parts [ 0 ] : '/' ; // Return first found path or root
85+ }
86+
5987
6088function loadVFS ( ) {
6189 try {
@@ -74,20 +102,31 @@ function saveVFS() {
74102// Load VFS on startup
75103loadVFS ( ) ;
76104
105+ function getSizeInKB ( variable ) {
106+ const json = JSON . stringify ( variable ) ;
107+ const bytes = new TextEncoder ( ) . encode ( json ) . length ;
108+ const kb = bytes / 1024 ;
109+ return kb ;
110+ }
111+
77112const commands = {
78113 help : ( ) => {
79114 term . writeln ( '\r\nAvailable commands:' ) ;
80115 term . writeln ( ' help - Show this help message' ) ;
81116 term . writeln ( ' clear - Clear the terminal' ) ;
82117 term . writeln ( ' echo - Echo back your message' ) ;
83118 term . writeln ( ' date - Show current date and time' ) ;
84- term . writeln ( ' beep - Make a beep sound ' ) ;
85- term . writeln ( ' ls - List files in the virtual file system ' ) ;
119+ term . writeln ( ' cd - Change directory: cd dirname or cd .. or cd (for root) ' ) ;
120+ term . writeln ( ' ls - List files in the current directory ' ) ;
86121 term . writeln ( ' cat - Show file contents: cat filename' ) ;
87122 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' ) ;
88125 term . writeln ( ' rm - Remove a file: rm filename' ) ;
89126 term . writeln ( ' save - Save VFS to browser storage' ) ;
90127 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' ) ;
91130 term . writeln ( ' about - About this terminal' ) ;
92131 } ,
93132 clear : ( ) => {
@@ -101,23 +140,51 @@ const commands = {
101140 date : ( ) => {
102141 term . writeln ( '\r\n' + new Date ( ) . toString ( ) ) ;
103142 } ,
104- beep : ( ) => {
105- squareBeep ( ) ;
106- term . writeln ( '\r\n🔊 Beep!' ) ;
143+ cd : ( args ) => {
144+ if ( args . length === 0 ) {
145+ cwd_path = '/' ;
146+ term . writeln ( `\r\nChanged directory to: ${ cwd_path } ` ) ;
147+ return ;
148+ }
149+ const dir = args [ 0 ] ;
150+ if ( dir === '..' ) {
151+ if ( cwd_path !== '/' ) {
152+ const parts = cwd_path . split ( '/' ) . filter ( Boolean ) ;
153+ parts . pop ( ) ; // Go up one directory
154+ cwd_path = '/' + parts . join ( '/' ) ;
155+ if ( cwd_path === '/' ) cwd_path = '/' ; // Ensure root path
156+ }
157+ } else {
158+ const currentDir = cwd ( ) ;
159+ if ( currentDir [ dir ] && typeof currentDir [ dir ] === 'object' ) {
160+ cwd_path = cwd_path === '/' ? `/${ dir } ` : `${ cwd_path } /${ dir } ` ;
161+ } else {
162+ term . writeln ( `\r\nNo such directory: ${ dir } ` ) ;
163+ return ;
164+ }
165+ }
166+ term . writeln ( `\r\nChanged directory to: ${ cwd_path } ` ) ;
107167 } ,
108168 ls : ( ) => {
109- const files = Object . keys ( vfs ) ;
169+ const currentDir = cwd ( ) ;
170+ const files = Object . keys ( currentDir ) ;
110171 if ( files . length === 0 ) term . writeln ( '\r\n(no files)' ) ;
111- else term . writeln ( '\r\n' + files . join ( ' ' ) ) ;
172+ else {
173+ const displayFiles = files . map ( file => {
174+ return typeof currentDir [ file ] === 'object' ? file + '/' : file ;
175+ } ) ;
176+ term . writeln ( '\r\n' + displayFiles . join ( ' ' ) ) ;
177+ }
112178 } ,
113179 cat : ( args ) => {
114180 if ( ! args [ 0 ] ) {
115181 term . writeln ( '\r\nUsage: cat filename' ) ;
116182 return ;
117183 }
118184 const file = args [ 0 ] ;
119- if ( vfs [ file ] !== undefined ) {
120- term . writeln ( '\r\n' + vfs [ file ] ) ;
185+ const currentDir = cwd ( ) ;
186+ if ( currentDir [ file ] !== undefined && typeof currentDir [ file ] !== 'object' ) {
187+ term . writeln ( '\r\n' + currentDir [ file ] ) ;
121188 } else {
122189 term . writeln ( `\r\nFile not found: ${ file } ` ) ;
123190 }
@@ -129,17 +196,47 @@ const commands = {
129196 }
130197 const file = args [ 0 ] ;
131198 const content = args . slice ( 1 ) . join ( ' ' ) ;
132- vfs [ file ] = content ;
199+ const currentDir = cwd ( ) ;
200+ currentDir [ file ] = content ;
133201 term . writeln ( `\r\nWrote to ${ file } ` ) ;
134202 } ,
203+ mkdir : ( args ) => {
204+ if ( ! args [ 0 ] ) {
205+ term . writeln ( '\r\nUsage: mkdir directory_name' ) ;
206+ return ;
207+ }
208+ const dir = args [ 0 ] ;
209+ const currentDir = cwd ( ) ;
210+ if ( currentDir [ dir ] !== undefined ) {
211+ term . writeln ( `\r\nDirectory already exists: ${ dir } ` ) ;
212+ return ;
213+ }
214+ currentDir [ dir ] = { } ; // Create an empty directory
215+ term . writeln ( `\r\nCreated directory: ${ dir } ` ) ;
216+ } ,
217+ rmdir : ( args ) => {
218+ if ( ! args [ 0 ] ) {
219+ term . writeln ( '\r\nUsage: rmdir directory_name' ) ;
220+ return ;
221+ }
222+ const dir = args [ 0 ] ;
223+ const currentDir = cwd ( ) ;
224+ if ( currentDir [ dir ] !== undefined && typeof currentDir [ dir ] === 'object' ) {
225+ delete currentDir [ dir ] ;
226+ term . writeln ( `\r\nRemoved directory: ${ dir } ` ) ;
227+ } else {
228+ term . writeln ( `\r\nDirectory not found: ${ dir } ` ) ;
229+ }
230+ } ,
135231 rm : ( args ) => {
136232 if ( ! args [ 0 ] ) {
137233 term . writeln ( '\r\nUsage: rm filename' ) ;
138234 return ;
139235 }
140236 const file = args [ 0 ] ;
141- if ( vfs [ file ] !== undefined ) {
142- delete vfs [ file ] ;
237+ const currentDir = cwd ( ) ;
238+ if ( currentDir [ file ] !== undefined && typeof currentDir [ file ] !== 'object' ) {
239+ delete currentDir [ file ] ;
143240 term . writeln ( `\r\nDeleted ${ file } ` ) ;
144241 } else {
145242 term . writeln ( `\r\nFile not found: ${ file } ` ) ;
@@ -153,6 +250,34 @@ const commands = {
153250 loadVFS ( ) ;
154251 term . writeln ( '\r\nVFS loaded from browser storage.' ) ;
155252 } ,
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 } ` ) ;
263+ }
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+ } ) ;
274+ } ,
275+ storage : ( ) => {
276+ term . writeln ( '\r\nVirtual File System Storage:' ) ;
277+ term . writeln ( `${ getSizeInKB ( vfs ) . toFixed ( 2 ) } KB / 512 KB` ) ;
278+ term . writeln ( `(${ Object . keys ( vfs ) . length } files)` ) ;
279+ term . writeln ( `(${ ( getSizeInKB ( vfs ) / 512 * 100 ) . toFixed ( 2 ) } % used)` ) ;
280+ } ,
156281 about : ( ) => {
157282 term . writeln ( '\r\n' + colors . bold + 'MiaoShell' + colors . reset + ' - A Useless Terminal' ) ;
158283 term . writeln ( 'Version: 1.0.0' ) ;
@@ -178,7 +303,8 @@ function executeCommand(input) {
178303}
179304
180305function prompt ( ) {
181- term . write ( `\r\n${ colors . green } user@miaoshell${ colors . reset } :${ colors . blue } ~${ colors . reset } $ ` ) ;
306+ const displayPath = cwd_path === '/' ? '~' : cwd_path ;
307+ term . write ( `\r\n${ colors . green } user@miaoshell${ colors . reset } :${ colors . blue } ${ displayPath } ${ colors . reset } $ ` ) ;
182308}
183309
184310// Handle user input
@@ -205,7 +331,7 @@ term.onData(data => {
205331async function init ( ) {
206332 // Initialize terminal
207333 //print /assets/miao.txt
208- await fetch ( '/assets/miao.txt' )
334+ await fetch ( '. /assets/miao.txt' )
209335 . then ( response => {
210336 if ( response . status === 200 ) {
211337 return response . text ( ) ;
0 commit comments