@ecmaos/coreutils 0.3.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +48 -0
  3. package/dist/commands/awk.d.ts +4 -0
  4. package/dist/commands/awk.d.ts.map +1 -0
  5. package/dist/commands/awk.js +324 -0
  6. package/dist/commands/awk.js.map +1 -0
  7. package/dist/commands/chgrp.d.ts +4 -0
  8. package/dist/commands/chgrp.d.ts.map +1 -0
  9. package/dist/commands/chgrp.js +187 -0
  10. package/dist/commands/chgrp.js.map +1 -0
  11. package/dist/commands/chmod.d.ts.map +1 -1
  12. package/dist/commands/chmod.js +139 -2
  13. package/dist/commands/chmod.js.map +1 -1
  14. package/dist/commands/chown.d.ts +4 -0
  15. package/dist/commands/chown.d.ts.map +1 -0
  16. package/dist/commands/chown.js +257 -0
  17. package/dist/commands/chown.js.map +1 -0
  18. package/dist/commands/cksum.d.ts +4 -0
  19. package/dist/commands/cksum.d.ts.map +1 -0
  20. package/dist/commands/cksum.js +124 -0
  21. package/dist/commands/cksum.js.map +1 -0
  22. package/dist/commands/cmp.d.ts +4 -0
  23. package/dist/commands/cmp.d.ts.map +1 -0
  24. package/dist/commands/cmp.js +120 -0
  25. package/dist/commands/cmp.js.map +1 -0
  26. package/dist/commands/column.d.ts +4 -0
  27. package/dist/commands/column.d.ts.map +1 -0
  28. package/dist/commands/column.js +274 -0
  29. package/dist/commands/column.js.map +1 -0
  30. package/dist/commands/cp.d.ts.map +1 -1
  31. package/dist/commands/cp.js +81 -4
  32. package/dist/commands/cp.js.map +1 -1
  33. package/dist/commands/cron.d.ts.map +1 -1
  34. package/dist/commands/cron.js +116 -23
  35. package/dist/commands/cron.js.map +1 -1
  36. package/dist/commands/curl.d.ts +4 -0
  37. package/dist/commands/curl.d.ts.map +1 -0
  38. package/dist/commands/curl.js +238 -0
  39. package/dist/commands/curl.js.map +1 -0
  40. package/dist/commands/du.d.ts +4 -0
  41. package/dist/commands/du.d.ts.map +1 -0
  42. package/dist/commands/du.js +168 -0
  43. package/dist/commands/du.js.map +1 -0
  44. package/dist/commands/echo.d.ts.map +1 -1
  45. package/dist/commands/echo.js +125 -2
  46. package/dist/commands/echo.js.map +1 -1
  47. package/dist/commands/env.d.ts +4 -0
  48. package/dist/commands/env.d.ts.map +1 -0
  49. package/dist/commands/env.js +129 -0
  50. package/dist/commands/env.js.map +1 -0
  51. package/dist/commands/expand.d.ts +4 -0
  52. package/dist/commands/expand.d.ts.map +1 -0
  53. package/dist/commands/expand.js +197 -0
  54. package/dist/commands/expand.js.map +1 -0
  55. package/dist/commands/factor.d.ts +4 -0
  56. package/dist/commands/factor.d.ts.map +1 -0
  57. package/dist/commands/factor.js +141 -0
  58. package/dist/commands/factor.js.map +1 -0
  59. package/dist/commands/fmt.d.ts +4 -0
  60. package/dist/commands/fmt.d.ts.map +1 -0
  61. package/dist/commands/fmt.js +278 -0
  62. package/dist/commands/fmt.js.map +1 -0
  63. package/dist/commands/fold.d.ts +4 -0
  64. package/dist/commands/fold.d.ts.map +1 -0
  65. package/dist/commands/fold.js +253 -0
  66. package/dist/commands/fold.js.map +1 -0
  67. package/dist/commands/groups.d.ts +4 -0
  68. package/dist/commands/groups.d.ts.map +1 -0
  69. package/dist/commands/groups.js +61 -0
  70. package/dist/commands/groups.js.map +1 -0
  71. package/dist/commands/head.d.ts.map +1 -1
  72. package/dist/commands/head.js +184 -77
  73. package/dist/commands/head.js.map +1 -1
  74. package/dist/commands/hostname.d.ts +4 -0
  75. package/dist/commands/hostname.d.ts.map +1 -0
  76. package/dist/commands/hostname.js +80 -0
  77. package/dist/commands/hostname.js.map +1 -0
  78. package/dist/commands/less.d.ts.map +1 -1
  79. package/dist/commands/less.js +1 -0
  80. package/dist/commands/less.js.map +1 -1
  81. package/dist/commands/man.d.ts.map +1 -1
  82. package/dist/commands/man.js +3 -1
  83. package/dist/commands/man.js.map +1 -1
  84. package/dist/commands/mount.d.ts +4 -0
  85. package/dist/commands/mount.d.ts.map +1 -0
  86. package/dist/commands/mount.js +1136 -0
  87. package/dist/commands/mount.js.map +1 -0
  88. package/dist/commands/od.d.ts +4 -0
  89. package/dist/commands/od.d.ts.map +1 -0
  90. package/dist/commands/od.js +342 -0
  91. package/dist/commands/od.js.map +1 -0
  92. package/dist/commands/pr.d.ts +4 -0
  93. package/dist/commands/pr.d.ts.map +1 -0
  94. package/dist/commands/pr.js +298 -0
  95. package/dist/commands/pr.js.map +1 -0
  96. package/dist/commands/printf.d.ts +4 -0
  97. package/dist/commands/printf.d.ts.map +1 -0
  98. package/dist/commands/printf.js +271 -0
  99. package/dist/commands/printf.js.map +1 -0
  100. package/dist/commands/readlink.d.ts +4 -0
  101. package/dist/commands/readlink.d.ts.map +1 -0
  102. package/dist/commands/readlink.js +104 -0
  103. package/dist/commands/readlink.js.map +1 -0
  104. package/dist/commands/realpath.d.ts +4 -0
  105. package/dist/commands/realpath.d.ts.map +1 -0
  106. package/dist/commands/realpath.js +111 -0
  107. package/dist/commands/realpath.js.map +1 -0
  108. package/dist/commands/rev.d.ts +4 -0
  109. package/dist/commands/rev.d.ts.map +1 -0
  110. package/dist/commands/rev.js +134 -0
  111. package/dist/commands/rev.js.map +1 -0
  112. package/dist/commands/shuf.d.ts +4 -0
  113. package/dist/commands/shuf.d.ts.map +1 -0
  114. package/dist/commands/shuf.js +221 -0
  115. package/dist/commands/shuf.js.map +1 -0
  116. package/dist/commands/sleep.d.ts +4 -0
  117. package/dist/commands/sleep.d.ts.map +1 -0
  118. package/dist/commands/sleep.js +102 -0
  119. package/dist/commands/sleep.js.map +1 -0
  120. package/dist/commands/strings.d.ts +4 -0
  121. package/dist/commands/strings.d.ts.map +1 -0
  122. package/dist/commands/strings.js +170 -0
  123. package/dist/commands/strings.js.map +1 -0
  124. package/dist/commands/tac.d.ts +4 -0
  125. package/dist/commands/tac.d.ts.map +1 -0
  126. package/dist/commands/tac.js +130 -0
  127. package/dist/commands/tac.js.map +1 -0
  128. package/dist/commands/time.d.ts +4 -0
  129. package/dist/commands/time.d.ts.map +1 -0
  130. package/dist/commands/time.js +126 -0
  131. package/dist/commands/time.js.map +1 -0
  132. package/dist/commands/umount.d.ts +4 -0
  133. package/dist/commands/umount.d.ts.map +1 -0
  134. package/dist/commands/umount.js +103 -0
  135. package/dist/commands/umount.js.map +1 -0
  136. package/dist/commands/uname.d.ts +4 -0
  137. package/dist/commands/uname.d.ts.map +1 -0
  138. package/dist/commands/uname.js +149 -0
  139. package/dist/commands/uname.js.map +1 -0
  140. package/dist/commands/unexpand.d.ts +4 -0
  141. package/dist/commands/unexpand.d.ts.map +1 -0
  142. package/dist/commands/unexpand.js +286 -0
  143. package/dist/commands/unexpand.js.map +1 -0
  144. package/dist/commands/uptime.d.ts +4 -0
  145. package/dist/commands/uptime.d.ts.map +1 -0
  146. package/dist/commands/uptime.js +62 -0
  147. package/dist/commands/uptime.js.map +1 -0
  148. package/dist/commands/view.d.ts +1 -0
  149. package/dist/commands/view.d.ts.map +1 -1
  150. package/dist/commands/view.js +408 -66
  151. package/dist/commands/view.js.map +1 -1
  152. package/dist/commands/yes.d.ts +4 -0
  153. package/dist/commands/yes.d.ts.map +1 -0
  154. package/dist/commands/yes.js +58 -0
  155. package/dist/commands/yes.js.map +1 -0
  156. package/dist/index.d.ts +24 -0
  157. package/dist/index.d.ts.map +1 -1
  158. package/dist/index.js +82 -0
  159. package/dist/index.js.map +1 -1
  160. package/package.json +12 -3
  161. package/src/commands/awk.ts +340 -0
  162. package/src/commands/chmod.ts +141 -2
  163. package/src/commands/chown.ts +321 -0
  164. package/src/commands/cksum.ts +133 -0
  165. package/src/commands/cmp.ts +126 -0
  166. package/src/commands/column.ts +273 -0
  167. package/src/commands/cp.ts +93 -4
  168. package/src/commands/cron.ts +115 -23
  169. package/src/commands/curl.ts +231 -0
  170. package/src/commands/echo.ts +122 -2
  171. package/src/commands/env.ts +143 -0
  172. package/src/commands/expand.ts +207 -0
  173. package/src/commands/factor.ts +151 -0
  174. package/src/commands/fmt.ts +293 -0
  175. package/src/commands/fold.ts +257 -0
  176. package/src/commands/groups.ts +72 -0
  177. package/src/commands/head.ts +176 -77
  178. package/src/commands/hostname.ts +81 -0
  179. package/src/commands/less.ts +1 -0
  180. package/src/commands/man.ts +4 -1
  181. package/src/commands/mount.ts +1302 -0
  182. package/src/commands/od.ts +327 -0
  183. package/src/commands/pr.ts +291 -0
  184. package/src/commands/printf.ts +271 -0
  185. package/src/commands/readlink.ts +102 -0
  186. package/src/commands/realpath.ts +126 -0
  187. package/src/commands/rev.ts +143 -0
  188. package/src/commands/shuf.ts +218 -0
  189. package/src/commands/sleep.ts +109 -0
  190. package/src/commands/strings.ts +176 -0
  191. package/src/commands/tac.ts +138 -0
  192. package/src/commands/time.ts +144 -0
  193. package/src/commands/umount.ts +116 -0
  194. package/src/commands/uname.ts +130 -0
  195. package/src/commands/unexpand.ts +305 -0
  196. package/src/commands/uptime.ts +73 -0
  197. package/src/commands/view.ts +463 -73
  198. package/src/index.ts +82 -0
  199. package/tsconfig.json +4 -0
@@ -1,36 +1,40 @@
1
1
  import path from 'path';
2
2
  import chalk from 'chalk';
3
+ import { marked } from 'marked';
4
+ import '@alenaksu/json-viewer';
3
5
  import { TerminalCommand } from '../shared/terminal-command.js';
4
6
  import { writelnStderr, writelnStdout } from '../shared/helpers.js';
5
7
  function detectFileType(filePath) {
6
8
  const ext = path.extname(filePath).toLowerCase();
7
9
  // PDF
8
- if (ext === '.pdf') {
10
+ if (ext === '.pdf')
9
11
  return 'pdf';
10
- }
12
+ // Markdown
13
+ const markdownExts = ['.md', '.markdown', '.mdown', '.mkd', '.mkdn'];
14
+ if (markdownExts.includes(ext))
15
+ return 'markdown';
16
+ // JSON
17
+ const jsonExts = ['.json', '.jsonl', '.jsonc', '.jsonld'];
18
+ if (jsonExts.includes(ext))
19
+ return 'json';
11
20
  // Images
12
21
  const imageExts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg', '.ico', '.tiff', '.tif'];
13
- if (imageExts.includes(ext)) {
22
+ if (imageExts.includes(ext))
14
23
  return 'image';
15
- }
16
24
  // Audio
17
25
  const audioExts = ['.mp3', '.wav', '.ogg', '.oga', '.opus', '.m4a', '.aac', '.flac', '.webm', '.wma', '.aiff', '.aif', '.3gp', '.amr'];
18
- if (audioExts.includes(ext)) {
26
+ if (audioExts.includes(ext))
19
27
  return 'audio';
20
- }
21
28
  // Video
22
29
  const videoExts = ['.mp4', '.webm', '.ogg', '.ogv', '.mov', '.avi', '.mkv', '.m4v', '.flv', '.wmv', '.3gp'];
23
- if (videoExts.includes(ext)) {
30
+ if (videoExts.includes(ext))
24
31
  return 'video';
25
- }
26
- // Default to image for unknown types (might be an image without extension)
27
- return 'image';
32
+ return 'application/octet-stream';
28
33
  }
29
34
  function getMimeType(filePath, fileType) {
30
35
  const ext = path.extname(filePath).toLowerCase();
31
- if (fileType === 'pdf') {
36
+ if (fileType === 'pdf')
32
37
  return 'application/pdf';
33
- }
34
38
  if (fileType === 'image') {
35
39
  const mimeTypes = {
36
40
  '.jpg': 'image/jpeg',
@@ -81,11 +85,13 @@ function getMimeType(filePath, fileType) {
81
85
  };
82
86
  return mimeTypes[ext] || 'video/mp4';
83
87
  }
88
+ if (fileType === 'json')
89
+ return 'application/json';
84
90
  return 'application/octet-stream';
85
91
  }
86
92
  function printUsage(process, terminal) {
87
93
  const usage = `Usage: view [OPTIONS] [FILE...]
88
- View files in a new window. Supports PDF, images, audio, and video files.
94
+ View files in a new window. Supports PDF, markdown, JSON, images, audio, and video files.
89
95
 
90
96
  --help display this help and exit
91
97
 
@@ -102,13 +108,15 @@ Audio/Video Options (for audio and video files):
102
108
 
103
109
  Examples:
104
110
  view document.pdf view a PDF file
111
+ view README.md view a markdown file
112
+ view data.json view a JSON file
105
113
  view image.png view an image
106
114
  view song.mp3 view/play an audio file
107
115
  view movie.mp4 view/play a video file
108
- view --loop music.mp3 play audio in a loop
109
- view --no-autoplay video.mp4 load video without auto-playing
110
- view --volume 50 track.mp3 play at 50% volume
111
- view --fullscreen movie.mp4 play video in fullscreen mode`;
116
+ view --loop music.mp3 play audio in a loop
117
+ view --no-autoplay video.mp4 load video without auto-playing
118
+ view --volume 50 track.mp3 play at 50% volume
119
+ view --fullscreen movie.mp4 play video in fullscreen mode`;
112
120
  writelnStderr(process, terminal, usage);
113
121
  }
114
122
  async function loadAudioMetadata(audioElement) {
@@ -158,6 +166,10 @@ function formatDuration(seconds) {
158
166
  }
159
167
  return `${minutes}:${secs.toString().padStart(2, '0')}`;
160
168
  }
169
+ function generateRandomClass(prefix) {
170
+ const randomSuffix = Math.random().toString(36).substring(2, 8);
171
+ return `${prefix}-${randomSuffix}`;
172
+ }
161
173
  export function createCommand(kernel, shell, terminal) {
162
174
  return new TerminalCommand({
163
175
  command: 'view',
@@ -272,7 +284,278 @@ export function createCommand(kernel, shell, terminal) {
272
284
  const windowTitle = files.length > 1
273
285
  ? `${path.basename(file)} (${files.indexOf(file) + 1}/${files.length})`
274
286
  : path.basename(file);
275
- if (fileType === 'pdf') {
287
+ if (fileType === 'markdown') {
288
+ // Read and parse markdown file
289
+ const markdownText = new TextDecoder().decode(fileData);
290
+ const htmlContent = await marked.parse(markdownText);
291
+ const containerClass = generateRandomClass('markdown-container');
292
+ const container = document.createElement('div');
293
+ container.className = containerClass;
294
+ container.style.width = '100%';
295
+ container.style.height = '100%';
296
+ container.style.display = 'flex';
297
+ container.style.flexDirection = 'column';
298
+ container.style.background = '#1e1e1e';
299
+ container.style.overflow = 'auto';
300
+ container.style.padding = '40px';
301
+ container.style.boxSizing = 'border-box';
302
+ container.style.fontFamily = "-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif";
303
+ container.style.color = '#e0e0e0';
304
+ container.style.lineHeight = '1.6';
305
+ const contentWrapper = document.createElement('div');
306
+ contentWrapper.style.maxWidth = '800px';
307
+ contentWrapper.style.margin = '0 auto';
308
+ contentWrapper.style.width = '100%';
309
+ contentWrapper.innerHTML = htmlContent;
310
+ const style = document.createElement('style');
311
+ style.textContent = `
312
+ .${containerClass} h1, .${containerClass} h2, .${containerClass} h3, .${containerClass} h4, .${containerClass} h5, .${containerClass} h6 { color: #fff; margin-top: 1.5em; margin-bottom: 0.5em; }
313
+ .${containerClass} h1 { font-size: 2em; border-bottom: 1px solid #444; padding-bottom: 0.3em; }
314
+ .${containerClass} h2 { font-size: 1.5em; border-bottom: 1px solid #444; padding-bottom: 0.3em; }
315
+ .${containerClass} h3 { font-size: 1.25em; }
316
+ .${containerClass} code { background-color: #2d2d2d; padding: 2px 6px; border-radius: 3px; font-family: 'Courier New', monospace; color: #f8f8f2; }
317
+ .${containerClass} pre { background-color: #2d2d2d; padding: 16px; border-radius: 6px; overflow-x: auto; }
318
+ .${containerClass} pre code { background-color: transparent; padding: 0; }
319
+ .${containerClass} a { color: #4a9eff; text-decoration: none; }
320
+ .${containerClass} a:hover { text-decoration: underline; }
321
+ .${containerClass} blockquote { border-left: 4px solid #4a9eff; padding-left: 16px; margin-left: 0; color: #b0b0b0; }
322
+ .${containerClass} table { border-collapse: collapse; width: 100%; margin: 1em 0; }
323
+ .${containerClass} th, .${containerClass} td { border: 1px solid #444; padding: 8px 12px; text-align: left; }
324
+ .${containerClass} th { background-color: #2d2d2d; font-weight: bold; }
325
+ .${containerClass} tr:nth-child(even) { background-color: #252525; }
326
+ .${containerClass} img { max-width: 100%; height: auto; }
327
+ .${containerClass} ul, .${containerClass} ol { padding-left: 2em; }
328
+ .${containerClass} hr { border: none; border-top: 1px solid #444; margin: 2em 0; }
329
+ `;
330
+ container.appendChild(style);
331
+ container.appendChild(contentWrapper);
332
+ const win = kernel.windows.create({
333
+ title: windowTitle,
334
+ width: 900,
335
+ height: 700,
336
+ max: false
337
+ });
338
+ win.mount(container);
339
+ await writelnStdout(process, terminal, chalk.green(`Viewing: ${file}`));
340
+ }
341
+ else if (fileType === 'json') {
342
+ // Read and parse JSON file
343
+ const jsonText = new TextDecoder().decode(fileData);
344
+ let jsonData;
345
+ try {
346
+ jsonData = JSON.parse(jsonText);
347
+ }
348
+ catch (error) {
349
+ await writelnStderr(process, terminal, chalk.red(`view: invalid JSON in ${file}: ${error instanceof Error ? error.message : 'Unknown error'}`));
350
+ continue;
351
+ }
352
+ const containerClass = generateRandomClass('json-container');
353
+ const container = document.createElement('div');
354
+ container.className = containerClass;
355
+ container.style.width = '100%';
356
+ container.style.height = '100%';
357
+ container.style.display = 'flex';
358
+ container.style.flexDirection = 'column';
359
+ container.style.background = '#2a2f3a';
360
+ container.style.overflow = 'hidden';
361
+ const buttonBar = document.createElement('div');
362
+ buttonBar.style.cssText = `
363
+ display: flex;
364
+ flex-wrap: wrap;
365
+ gap: 6px;
366
+ padding: 6px;
367
+ background: #2a2f3a;
368
+ border-bottom: 1px solid #3c3c3c;
369
+ align-items: center;
370
+ `;
371
+ const createInput = (placeholder) => {
372
+ const input = document.createElement('input');
373
+ input.type = 'text';
374
+ input.placeholder = placeholder;
375
+ input.style.cssText = `
376
+ background: #263040;
377
+ border: 1px solid #3c3c3c;
378
+ border-radius: 3px;
379
+ color: #fff;
380
+ padding: 0 8px;
381
+ font-size: 11px;
382
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
383
+ width: 120px;
384
+ height: 22px;
385
+ box-sizing: border-box;
386
+ line-height: 22px;
387
+ margin: 0;
388
+ vertical-align: middle;
389
+ `;
390
+ return input;
391
+ };
392
+ const createButton = (text) => {
393
+ const button = document.createElement('button');
394
+ button.textContent = text;
395
+ button.style.cssText = `
396
+ background: #263040;
397
+ border: 1px solid #263040;
398
+ border-radius: 3px;
399
+ color: #fff;
400
+ cursor: pointer;
401
+ font-size: 11px;
402
+ font-weight: 600;
403
+ padding: 0;
404
+ height: 22px;
405
+ box-sizing: border-box;
406
+ line-height: 22px;
407
+ white-space: nowrap;
408
+ display: flex;
409
+ align-items: center;
410
+ justify-content: center;
411
+ padding-left: 8px;
412
+ padding-right: 8px;
413
+ margin: 0;
414
+ vertical-align: middle;
415
+ `;
416
+ button.onmouseenter = () => {
417
+ button.style.background = '#333';
418
+ button.style.borderColor = '#333';
419
+ };
420
+ button.onmouseleave = () => {
421
+ button.style.background = '#263040';
422
+ button.style.borderColor = '#263040';
423
+ };
424
+ button.onmousedown = () => {
425
+ button.style.background = '#263040';
426
+ };
427
+ button.onmouseup = () => {
428
+ button.style.background = '#333';
429
+ };
430
+ return button;
431
+ };
432
+ const createSection = (label) => {
433
+ const section = document.createElement('div');
434
+ section.style.cssText = `
435
+ display: flex;
436
+ align-items: center;
437
+ gap: 4px;
438
+ height: 22px;
439
+ `;
440
+ const labelEl = document.createElement('span');
441
+ labelEl.textContent = label;
442
+ labelEl.style.cssText = `
443
+ color: #f8f8f2;
444
+ font-size: 11px;
445
+ font-weight: 600;
446
+ width: 50px;
447
+ flex-shrink: 0;
448
+ line-height: 22px;
449
+ display: flex;
450
+ align-items: center;
451
+ height: 22px;
452
+ `;
453
+ section.appendChild(labelEl);
454
+ return section;
455
+ };
456
+ const filterSection = createSection('Filter:');
457
+ const filterInput = createInput('Regex or path');
458
+ filterSection.appendChild(filterInput);
459
+ const expandSection = createSection('Expand:');
460
+ const expandInput = createInput('Regex or path');
461
+ const expandButton = createButton('Expand');
462
+ const expandAllButton = createButton('Expand All');
463
+ expandSection.appendChild(expandInput);
464
+ expandSection.appendChild(expandButton);
465
+ expandSection.appendChild(expandAllButton);
466
+ const collapseSection = createSection('Collapse:');
467
+ const collapseInput = createInput('Regex or path');
468
+ const collapseButton = createButton('Collapse');
469
+ const collapseAllButton = createButton('Collapse All');
470
+ collapseSection.appendChild(collapseInput);
471
+ collapseSection.appendChild(collapseButton);
472
+ collapseSection.appendChild(collapseAllButton);
473
+ buttonBar.appendChild(filterSection);
474
+ buttonBar.appendChild(expandSection);
475
+ buttonBar.appendChild(collapseSection);
476
+ const jsonViewer = document.createElement('json-viewer');
477
+ jsonViewer.style.width = '100%';
478
+ jsonViewer.style.flex = '1';
479
+ jsonViewer.style.padding = '0.5rem';
480
+ jsonViewer.style.overflow = 'auto';
481
+ jsonViewer.data = jsonData;
482
+ jsonViewer.style.setProperty('--background-color', '#2a2f3a');
483
+ jsonViewer.style.setProperty('--color', '#f8f8f2');
484
+ jsonViewer.style.setProperty('--font-family', "'Courier New', monospace");
485
+ jsonViewer.style.setProperty('--string-color', '#a3eea0');
486
+ jsonViewer.style.setProperty('--number-color', '#d19a66');
487
+ jsonViewer.style.setProperty('--boolean-color', '#4ba7ef');
488
+ jsonViewer.style.setProperty('--null-color', '#df9cf3');
489
+ jsonViewer.style.setProperty('--property-color', '#6fb3d2');
490
+ jsonViewer.style.setProperty('--preview-color', '#deae8f');
491
+ jsonViewer.style.setProperty('--highlight-color', '#c92a2a');
492
+ filterInput.addEventListener('input', () => {
493
+ const value = filterInput.value.trim();
494
+ if (value) {
495
+ try {
496
+ const regex = new RegExp(value);
497
+ jsonViewer.filter(regex);
498
+ }
499
+ catch {
500
+ jsonViewer.filter(value);
501
+ }
502
+ }
503
+ else {
504
+ jsonViewer.resetFilter();
505
+ }
506
+ });
507
+ expandButton.onclick = () => {
508
+ const value = expandInput.value.trim();
509
+ if (value) {
510
+ try {
511
+ const regex = new RegExp(value);
512
+ jsonViewer.expand(regex);
513
+ }
514
+ catch {
515
+ jsonViewer.expand(value);
516
+ }
517
+ }
518
+ };
519
+ expandAllButton.onclick = () => {
520
+ jsonViewer.expandAll();
521
+ };
522
+ collapseButton.onclick = () => {
523
+ const value = collapseInput.value.trim();
524
+ if (value) {
525
+ try {
526
+ const regex = new RegExp(value);
527
+ jsonViewer.collapse(regex);
528
+ }
529
+ catch {
530
+ jsonViewer.collapse(value);
531
+ }
532
+ }
533
+ };
534
+ collapseAllButton.onclick = () => {
535
+ jsonViewer.collapseAll();
536
+ };
537
+ const handleEnterKey = (input, button) => {
538
+ input.addEventListener('keydown', (e) => {
539
+ if (e.key === 'Enter') {
540
+ button.click();
541
+ }
542
+ });
543
+ };
544
+ handleEnterKey(expandInput, expandButton);
545
+ handleEnterKey(collapseInput, collapseButton);
546
+ container.appendChild(buttonBar);
547
+ container.appendChild(jsonViewer);
548
+ // Create window
549
+ const win = kernel.windows.create({
550
+ title: windowTitle,
551
+ width: 900,
552
+ height: 700,
553
+ max: false
554
+ });
555
+ win.mount(container);
556
+ await writelnStdout(process, terminal, chalk.green(`Viewing: ${file}`));
557
+ }
558
+ else if (fileType === 'pdf') {
276
559
  // Convert PDF to base64 and display in object tag
277
560
  // Use chunked encoding to avoid argument limit issues with large files
278
561
  const uint8Array = new Uint8Array(fileData);
@@ -284,41 +567,73 @@ export function createCommand(kernel, shell, terminal) {
284
567
  }
285
568
  const base64Data = btoa(binaryString);
286
569
  const dataUrl = `data:application/pdf;base64,${base64Data}`;
287
- const pdfHtml = `
288
- <div style="width:100%;height:100%;display:flex;flex-direction:column;background:#1e1e1e;overflow:hidden;">
289
- <object data="${dataUrl}" type="application/pdf" style="width:100%;height:100%;flex:1;">
290
- <p style="color:#fff;padding:20px;text-align:center;">
291
- Your browser does not support PDFs.
292
- <a href="${dataUrl}" style="color:#4a9eff;" download="${path.basename(file)}">Download PDF</a>
293
- </p>
294
- </object>
295
- </div>
296
- `;
297
- kernel.windows.create({
570
+ const containerClass = generateRandomClass('pdf-container');
571
+ const container = document.createElement('div');
572
+ container.className = containerClass;
573
+ container.style.width = '100%';
574
+ container.style.height = '100%';
575
+ container.style.display = 'flex';
576
+ container.style.flexDirection = 'column';
577
+ container.style.background = '#1e1e1e';
578
+ container.style.overflow = 'hidden';
579
+ const object = document.createElement('object');
580
+ object.data = dataUrl;
581
+ object.type = 'application/pdf';
582
+ object.style.width = '100%';
583
+ object.style.height = '100%';
584
+ object.style.flex = '1';
585
+ const fallback = document.createElement('p');
586
+ fallback.style.color = '#fff';
587
+ fallback.style.padding = '20px';
588
+ fallback.style.textAlign = 'center';
589
+ fallback.textContent = 'Your browser does not support PDFs. ';
590
+ const downloadLink = document.createElement('a');
591
+ downloadLink.href = dataUrl;
592
+ downloadLink.download = path.basename(file);
593
+ downloadLink.style.color = '#4a9eff';
594
+ downloadLink.textContent = 'Download PDF';
595
+ fallback.appendChild(downloadLink);
596
+ object.appendChild(fallback);
597
+ container.appendChild(object);
598
+ const win = kernel.windows.create({
298
599
  title: windowTitle,
299
- html: pdfHtml,
300
600
  width: 800,
301
601
  height: 600,
302
602
  max: false
303
603
  });
604
+ win.mount(container);
304
605
  await writelnStdout(process, terminal, chalk.green(`Viewing: ${file}`));
305
606
  }
306
607
  else if (fileType === 'image') {
307
608
  // Display image directly
308
609
  const blob = new Blob([new Uint8Array(fileData)], { type: mimeType });
309
610
  const url = URL.createObjectURL(blob);
310
- const imageHtml = `
311
- <div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:#1e1e1e;overflow:auto;padding:20px;box-sizing:border-box;">
312
- <img src="${url}" style="max-width:100%;max-height:100%;object-fit:contain;" alt="${path.basename(file)}" />
313
- </div>
314
- `;
315
- kernel.windows.create({
611
+ const containerClass = generateRandomClass('image-container');
612
+ const container = document.createElement('div');
613
+ container.className = containerClass;
614
+ container.style.width = '100%';
615
+ container.style.height = '100%';
616
+ container.style.display = 'flex';
617
+ container.style.alignItems = 'center';
618
+ container.style.justifyContent = 'center';
619
+ container.style.background = '#1e1e1e';
620
+ container.style.overflow = 'auto';
621
+ container.style.padding = '20px';
622
+ container.style.boxSizing = 'border-box';
623
+ const img = document.createElement('img');
624
+ img.src = url;
625
+ img.alt = path.basename(file);
626
+ img.style.maxWidth = '100%';
627
+ img.style.maxHeight = '100%';
628
+ img.style.objectFit = 'contain';
629
+ container.appendChild(img);
630
+ const win = kernel.windows.create({
316
631
  title: windowTitle,
317
- html: imageHtml,
318
632
  width: 800,
319
633
  height: 600,
320
634
  max: false
321
635
  });
636
+ win.mount(container);
322
637
  await writelnStdout(process, terminal, chalk.green(`Viewing: ${file}`));
323
638
  }
324
639
  else if (fileType === 'audio') {
@@ -362,29 +677,50 @@ export function createCommand(kernel, shell, terminal) {
362
677
  else {
363
678
  // Create a simple audio player window
364
679
  const audioId = `audio-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
365
- const audioHtml = `
366
- <div style="width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;background:#1e1e1e;color:#fff;font-family:monospace;padding:20px;box-sizing:border-box;">
367
- <div style="font-size:18px;margin-bottom:20px;text-align:center;word-break:break-word;">${path.basename(file)}</div>
368
- <audio id="${audioId}" src="${url}" ${options.autoplay ? 'autoplay' : ''} ${options.loop ? 'loop' : ''} ${options.muted ? 'muted' : ''} controls style="width:100%;max-width:600px;"></audio>
369
- </div>
370
- <script>
371
- (function() {
372
- const audio = document.getElementById('${audioId}');
373
- if (audio) {
374
- audio.volume = ${options.volume / 100};
375
- audio.addEventListener('play', () => console.log('Playing: ${file}'));
376
- audio.addEventListener('ended', () => console.log('Finished: ${file}'));
377
- }
378
- })();
379
- </script>
380
- `;
381
- kernel.windows.create({
680
+ const containerClass = generateRandomClass('audio-container');
681
+ const container = document.createElement('div');
682
+ container.className = containerClass;
683
+ container.style.width = '100%';
684
+ container.style.height = '100%';
685
+ container.style.display = 'flex';
686
+ container.style.flexDirection = 'column';
687
+ container.style.alignItems = 'center';
688
+ container.style.justifyContent = 'center';
689
+ container.style.background = '#1e1e1e';
690
+ container.style.color = '#fff';
691
+ container.style.fontFamily = 'monospace';
692
+ container.style.padding = '20px';
693
+ container.style.boxSizing = 'border-box';
694
+ const title = document.createElement('div');
695
+ title.textContent = path.basename(file);
696
+ title.style.fontSize = '18px';
697
+ title.style.marginBottom = '20px';
698
+ title.style.textAlign = 'center';
699
+ title.style.wordBreak = 'break-word';
700
+ const audio = document.createElement('audio');
701
+ audio.id = audioId;
702
+ audio.src = url;
703
+ audio.controls = true;
704
+ audio.style.width = '100%';
705
+ audio.style.maxWidth = '600px';
706
+ if (options.autoplay)
707
+ audio.autoplay = true;
708
+ if (options.loop)
709
+ audio.loop = true;
710
+ if (options.muted)
711
+ audio.muted = true;
712
+ audio.volume = options.volume / 100;
713
+ audio.addEventListener('play', () => console.log(`Playing: ${file}`));
714
+ audio.addEventListener('ended', () => console.log(`Finished: ${file}`));
715
+ container.appendChild(title);
716
+ container.appendChild(audio);
717
+ const win = kernel.windows.create({
382
718
  title: windowTitle,
383
- html: audioHtml,
384
719
  width: 500,
385
720
  height: 200,
386
721
  max: false
387
722
  });
723
+ win.mount(container);
388
724
  if (duration > 0) {
389
725
  const durationStr = formatDuration(duration);
390
726
  await writelnStdout(process, terminal, chalk.green(`Playing: ${file} (${durationStr})`));
@@ -446,26 +782,32 @@ export function createCommand(kernel, shell, terminal) {
446
782
  // Ensure minimum size
447
783
  windowWidth = Math.max(windowWidth, 320);
448
784
  windowHeight = Math.max(windowHeight, 180);
449
- // Build video attributes
450
- const videoAttrs = [];
785
+ const containerClass = generateRandomClass('video-container');
786
+ const container = document.createElement('div');
787
+ container.className = containerClass;
788
+ container.style.width = '100%';
789
+ container.style.height = '100%';
790
+ const video = document.createElement('video');
791
+ video.src = url;
792
+ video.style.width = '100%';
793
+ video.style.height = '100%';
794
+ video.style.objectFit = 'contain';
451
795
  if (options.autoplay)
452
- videoAttrs.push('autoplay');
796
+ video.autoplay = true;
453
797
  if (options.controls)
454
- videoAttrs.push('controls');
798
+ video.controls = true;
455
799
  if (options.loop)
456
- videoAttrs.push('loop');
800
+ video.loop = true;
457
801
  if (options.muted)
458
- videoAttrs.push('muted');
459
- videoAttrs.push('style="width:100%;height:100%;object-fit:contain"');
460
- const videoHtml = `<video src="${url}" ${videoAttrs.join(' ')}></video>`;
461
- // Create window
462
- kernel.windows.create({
802
+ video.muted = true;
803
+ container.appendChild(video);
804
+ const win = kernel.windows.create({
463
805
  title: windowTitle,
464
- html: videoHtml,
465
806
  width: windowWidth,
466
807
  height: windowHeight,
467
808
  max: options.fullscreen
468
809
  });
810
+ win.mount(container);
469
811
  if (duration > 0) {
470
812
  const minutes = Math.floor(duration / 60);
471
813
  const seconds = Math.floor(duration % 60);