@jacksontian/mwt 1.0.0

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.
@@ -0,0 +1,579 @@
1
+ /* ========== Reset & Base ========== */
2
+ *, *::before, *::after {
3
+ box-sizing: border-box;
4
+ margin: 0;
5
+ padding: 0;
6
+ }
7
+
8
+ :root {
9
+ --radius: 6px;
10
+ --radius-sm: 4px;
11
+ --transition: 150ms ease;
12
+ }
13
+
14
+ [data-theme="dark"], :root:not([data-theme]) {
15
+ --bg-primary: #0c0e14;
16
+ --bg-secondary: #151824;
17
+ --bg-tertiary: #1c2035;
18
+ --bg-toolbar: #121520;
19
+ --bg-hover: #252a3d;
20
+ --bg-active: #2d3352;
21
+ --accent: #6c8cff;
22
+ --accent-hover: #8aa4ff;
23
+ --text-primary: #d4d8e8;
24
+ --text-secondary: #8890a8;
25
+ --text-dim: #555b72;
26
+ --border: #252a3d;
27
+ --danger: #e05560;
28
+ --danger-hover: #ff6b76;
29
+ --terminal-bg: #0a0c12;
30
+ --pane-header: #141725;
31
+ }
32
+
33
+ [data-theme="light"] {
34
+ --bg-primary: #f0f2f5;
35
+ --bg-secondary: #e4e7ec;
36
+ --bg-tertiary: #d8dce4;
37
+ --bg-toolbar: #ffffff;
38
+ --bg-hover: #dde0e8;
39
+ --bg-active: #cdd2de;
40
+ --accent: #4a6cf7;
41
+ --accent-hover: #3955d4;
42
+ --text-primary: #1a1d2e;
43
+ --text-secondary: #5a6178;
44
+ --text-dim: #9098b0;
45
+ --border: #d0d5e0;
46
+ --danger: #d43d4e;
47
+ --danger-hover: #c02838;
48
+ --terminal-bg: #ffffff;
49
+ --pane-header: #f5f6f9;
50
+ }
51
+
52
+ html, body {
53
+ height: 100%;
54
+ overflow: hidden;
55
+ background: var(--bg-primary);
56
+ color: var(--text-primary);
57
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
58
+ font-size: 13px;
59
+ }
60
+
61
+ body {
62
+ display: flex;
63
+ flex-direction: column;
64
+ }
65
+
66
+ /* ========== Toolbar ========== */
67
+ #toolbar {
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: space-between;
71
+ height: 42px;
72
+ padding: 0 12px;
73
+ background: var(--bg-toolbar);
74
+ border-bottom: 1px solid var(--border);
75
+ flex-shrink: 0;
76
+ gap: 12px;
77
+ -webkit-app-region: drag;
78
+ }
79
+
80
+ .toolbar-left, .toolbar-center, .toolbar-right {
81
+ display: flex;
82
+ align-items: center;
83
+ -webkit-app-region: no-drag;
84
+ }
85
+
86
+ .toolbar-right {
87
+ min-width: 100px;
88
+ justify-content: flex-end;
89
+ }
90
+
91
+ #terminal-count {
92
+ color: var(--text-dim);
93
+ font-size: 12px;
94
+ font-variant-numeric: tabular-nums;
95
+ }
96
+
97
+ #btn-new-terminal {
98
+ display: flex;
99
+ align-items: center;
100
+ gap: 6px;
101
+ padding: 5px 14px;
102
+ background: var(--accent);
103
+ color: #fff;
104
+ border: none;
105
+ border-radius: var(--radius);
106
+ font-size: 12px;
107
+ font-weight: 500;
108
+ cursor: pointer;
109
+ transition: background var(--transition);
110
+ white-space: nowrap;
111
+ }
112
+
113
+ #btn-new-terminal:hover {
114
+ background: var(--accent-hover);
115
+ }
116
+
117
+ #btn-new-terminal .btn-icon {
118
+ font-size: 16px;
119
+ font-weight: 300;
120
+ line-height: 1;
121
+ }
122
+
123
+ /* Layout Switcher */
124
+ .layout-switcher {
125
+ display: flex;
126
+ background: var(--bg-secondary);
127
+ border-radius: var(--radius);
128
+ border: 1px solid var(--border);
129
+ overflow: hidden;
130
+ }
131
+
132
+ .layout-btn {
133
+ display: flex;
134
+ align-items: center;
135
+ gap: 5px;
136
+ padding: 5px 12px;
137
+ background: transparent;
138
+ color: var(--text-secondary);
139
+ border: none;
140
+ font-size: 12px;
141
+ cursor: pointer;
142
+ transition: all var(--transition);
143
+ white-space: nowrap;
144
+ }
145
+
146
+ .layout-btn:not(:last-child) {
147
+ border-right: 1px solid var(--border);
148
+ }
149
+
150
+ .layout-btn:hover {
151
+ background: var(--bg-hover);
152
+ color: var(--text-primary);
153
+ }
154
+
155
+ .layout-btn.active {
156
+ background: var(--bg-active);
157
+ color: var(--accent);
158
+ }
159
+
160
+ .layout-btn svg {
161
+ flex-shrink: 0;
162
+ }
163
+
164
+ /* ========== Tab Bar ========== */
165
+ #tab-bar {
166
+ display: flex;
167
+ align-items: flex-end;
168
+ height: 34px;
169
+ padding: 0 8px;
170
+ background: var(--bg-toolbar);
171
+ border-bottom: 1px solid var(--border);
172
+ overflow-x: auto;
173
+ flex-shrink: 0;
174
+ gap: 2px;
175
+ }
176
+
177
+ #tab-bar::-webkit-scrollbar {
178
+ height: 2px;
179
+ }
180
+
181
+ #tab-bar::-webkit-scrollbar-thumb {
182
+ background: var(--text-dim);
183
+ border-radius: 1px;
184
+ }
185
+
186
+ #tab-bar.hidden {
187
+ display: none;
188
+ }
189
+
190
+ .tab {
191
+ display: flex;
192
+ align-items: center;
193
+ gap: 8px;
194
+ padding: 6px 14px;
195
+ background: var(--bg-secondary);
196
+ color: var(--text-secondary);
197
+ border: 1px solid var(--border);
198
+ border-bottom: none;
199
+ border-radius: var(--radius-sm) var(--radius-sm) 0 0;
200
+ font-size: 12px;
201
+ cursor: pointer;
202
+ transition: all var(--transition);
203
+ white-space: nowrap;
204
+ min-width: 0;
205
+ max-width: 180px;
206
+ user-select: none;
207
+ }
208
+
209
+ .tab:hover {
210
+ background: var(--bg-hover);
211
+ color: var(--text-primary);
212
+ }
213
+
214
+ .tab.active {
215
+ background: var(--terminal-bg);
216
+ color: var(--accent);
217
+ border-color: var(--border);
218
+ }
219
+
220
+ .tab-title {
221
+ overflow: hidden;
222
+ text-overflow: ellipsis;
223
+ }
224
+
225
+ .tab-close {
226
+ display: flex;
227
+ align-items: center;
228
+ justify-content: center;
229
+ width: 16px;
230
+ height: 16px;
231
+ border-radius: var(--radius-sm);
232
+ font-size: 14px;
233
+ line-height: 1;
234
+ color: var(--text-dim);
235
+ transition: all var(--transition);
236
+ flex-shrink: 0;
237
+ }
238
+
239
+ .tab-close:hover {
240
+ background: var(--danger);
241
+ color: #fff;
242
+ }
243
+
244
+ /* ========== Activity Indicator ========== */
245
+
246
+ .tab.has-activity .tab-title::before {
247
+ content: '';
248
+ display: inline-block;
249
+ width: 6px;
250
+ height: 6px;
251
+ border-radius: 50%;
252
+ background: var(--accent);
253
+ margin-right: 6px;
254
+ vertical-align: middle;
255
+ animation: activity-pulse 2s ease-in-out infinite;
256
+ }
257
+
258
+ .terminal-pane.has-activity .pane-title::before {
259
+ content: '';
260
+ display: inline-block;
261
+ width: 6px;
262
+ height: 6px;
263
+ border-radius: 50%;
264
+ background: var(--accent);
265
+ margin-right: 6px;
266
+ vertical-align: middle;
267
+ animation: activity-pulse 2s ease-in-out infinite;
268
+ }
269
+
270
+ @keyframes activity-pulse {
271
+ 0%, 100% { opacity: 1; }
272
+ 50% { opacity: 0.3; }
273
+ }
274
+
275
+ /* ========== Main Content ========== */
276
+ #main-content {
277
+ display: flex;
278
+ flex: 1;
279
+ overflow: hidden;
280
+ }
281
+
282
+ /* ========== Terminal Container ========== */
283
+ #terminal-container {
284
+ flex: 1;
285
+ overflow: hidden;
286
+ position: relative;
287
+ background: var(--bg-primary);
288
+ }
289
+
290
+ /* ========== Terminal Pane ========== */
291
+ .terminal-pane {
292
+ display: flex;
293
+ flex-direction: column;
294
+ background: var(--terminal-bg);
295
+ border: 1px solid var(--border);
296
+ border-radius: var(--radius-sm);
297
+ overflow: hidden;
298
+ min-width: 0;
299
+ min-height: 0;
300
+ }
301
+
302
+ .pane-header {
303
+ display: flex;
304
+ align-items: center;
305
+ justify-content: space-between;
306
+ height: 28px;
307
+ padding: 0 10px;
308
+ background: var(--pane-header);
309
+ border-bottom: 1px solid var(--border);
310
+ flex-shrink: 0;
311
+ user-select: none;
312
+ }
313
+
314
+ .pane-title {
315
+ font-size: 11px;
316
+ color: var(--text-secondary);
317
+ overflow: hidden;
318
+ text-overflow: ellipsis;
319
+ white-space: nowrap;
320
+ }
321
+
322
+ .pane-actions {
323
+ display: flex;
324
+ align-items: center;
325
+ gap: 4px;
326
+ }
327
+
328
+ .pane-maximize {
329
+ display: flex;
330
+ align-items: center;
331
+ justify-content: center;
332
+ width: 18px;
333
+ height: 18px;
334
+ background: transparent;
335
+ border: none;
336
+ border-radius: var(--radius-sm);
337
+ color: var(--text-dim);
338
+ cursor: pointer;
339
+ transition: all var(--transition);
340
+ line-height: 1;
341
+ }
342
+
343
+ .pane-maximize:hover {
344
+ background: var(--bg-hover);
345
+ color: var(--text-primary);
346
+ }
347
+
348
+ .pane-maximize .icon-restore {
349
+ display: none;
350
+ }
351
+
352
+ .maximized .pane-maximize .icon-maximize {
353
+ display: none;
354
+ }
355
+
356
+ .maximized .pane-maximize .icon-restore {
357
+ display: block;
358
+ }
359
+
360
+ .pane-close {
361
+ display: flex;
362
+ align-items: center;
363
+ justify-content: center;
364
+ width: 18px;
365
+ height: 18px;
366
+ background: transparent;
367
+ border: none;
368
+ border-radius: var(--radius-sm);
369
+ color: var(--text-dim);
370
+ font-size: 14px;
371
+ cursor: pointer;
372
+ transition: all var(--transition);
373
+ line-height: 1;
374
+ }
375
+
376
+ .pane-close:hover {
377
+ background: var(--danger);
378
+ color: #fff;
379
+ }
380
+
381
+ /* Maximize / Restore */
382
+ .terminal-pane.hidden-by-maximize {
383
+ display: none !important;
384
+ }
385
+
386
+ .terminal-pane.maximized {
387
+ flex: 1 !important;
388
+ min-width: 0 !important;
389
+ min-height: 0 !important;
390
+ }
391
+
392
+ .terminal-body {
393
+ flex: 1;
394
+ overflow: hidden;
395
+ padding: 4px;
396
+ }
397
+
398
+ /* xterm overrides */
399
+ .terminal-body .xterm {
400
+ height: 100%;
401
+ }
402
+
403
+ .terminal-body .xterm-viewport {
404
+ overflow-y: auto !important;
405
+ }
406
+
407
+ /* ========== Layout: Side by Side ========== */
408
+ .layout-side-by-side {
409
+ display: flex;
410
+ flex-direction: row;
411
+ gap: 2px;
412
+ padding: 2px;
413
+ }
414
+
415
+ .layout-side-by-side .terminal-pane {
416
+ flex: 1;
417
+ min-width: 200px;
418
+ }
419
+
420
+ /* ========== Layout: Grid ========== */
421
+ .layout-grid {
422
+ display: grid;
423
+ gap: 2px;
424
+ padding: 2px;
425
+ grid-template-columns: 1fr;
426
+ }
427
+
428
+ .layout-grid .terminal-pane {
429
+ min-width: 0;
430
+ min-height: 0;
431
+ }
432
+
433
+ /* ========== Layout: Tabs ========== */
434
+ .layout-tabs {
435
+ position: relative;
436
+ }
437
+
438
+ .layout-tabs .terminal-pane {
439
+ display: none;
440
+ position: absolute;
441
+ inset: 0;
442
+ border-radius: 0;
443
+ border: none;
444
+ }
445
+
446
+ .layout-tabs .terminal-pane.active {
447
+ display: flex;
448
+ }
449
+
450
+ .layout-tabs .pane-header {
451
+ display: none;
452
+ }
453
+
454
+ /* ========== Empty State ========== */
455
+ .empty-state {
456
+ display: flex;
457
+ flex-direction: column;
458
+ align-items: center;
459
+ justify-content: center;
460
+ height: 100%;
461
+ color: var(--text-dim);
462
+ gap: 12px;
463
+ }
464
+
465
+ .empty-state-icon {
466
+ font-size: 48px;
467
+ opacity: 0.3;
468
+ }
469
+
470
+ .empty-state-text {
471
+ font-size: 14px;
472
+ }
473
+
474
+ .empty-state-hint {
475
+ font-size: 12px;
476
+ opacity: 0.6;
477
+ }
478
+
479
+ /* ========== Scrollbar ========== */
480
+ ::-webkit-scrollbar {
481
+ width: 8px;
482
+ }
483
+
484
+ ::-webkit-scrollbar-track {
485
+ background: transparent;
486
+ }
487
+
488
+ ::-webkit-scrollbar-thumb {
489
+ background: var(--text-dim);
490
+ border-radius: 4px;
491
+ }
492
+
493
+ ::-webkit-scrollbar-thumb:hover {
494
+ background: var(--text-secondary);
495
+ }
496
+
497
+ /* ========== Fullscreen Toggle ========== */
498
+ #btn-fullscreen {
499
+ display: flex;
500
+ align-items: center;
501
+ justify-content: center;
502
+ width: 30px;
503
+ height: 30px;
504
+ background: var(--bg-secondary);
505
+ border: 1px solid var(--border);
506
+ border-radius: var(--radius);
507
+ color: var(--text-secondary);
508
+ cursor: pointer;
509
+ transition: all var(--transition);
510
+ margin-right: 4px;
511
+ }
512
+
513
+ #btn-fullscreen:hover {
514
+ background: var(--bg-hover);
515
+ color: var(--text-primary);
516
+ }
517
+
518
+ #btn-fullscreen .icon-enter-fs {
519
+ display: block;
520
+ }
521
+
522
+ #btn-fullscreen .icon-exit-fs {
523
+ display: none;
524
+ }
525
+
526
+ :fullscreen #btn-fullscreen .icon-enter-fs,
527
+ :-webkit-full-screen #btn-fullscreen .icon-enter-fs {
528
+ display: none;
529
+ }
530
+
531
+ :fullscreen #btn-fullscreen .icon-exit-fs,
532
+ :-webkit-full-screen #btn-fullscreen .icon-exit-fs {
533
+ display: block;
534
+ }
535
+
536
+ /* ========== Theme Toggle ========== */
537
+ #btn-theme-toggle {
538
+ display: flex;
539
+ align-items: center;
540
+ justify-content: center;
541
+ width: 30px;
542
+ height: 30px;
543
+ background: var(--bg-secondary);
544
+ border: 1px solid var(--border);
545
+ border-radius: var(--radius);
546
+ color: var(--text-secondary);
547
+ cursor: pointer;
548
+ transition: all var(--transition);
549
+ margin-right: 8px;
550
+ }
551
+
552
+ #btn-theme-toggle:hover {
553
+ background: var(--bg-hover);
554
+ color: var(--text-primary);
555
+ }
556
+
557
+ #btn-theme-toggle .icon-sun,
558
+ #btn-theme-toggle .icon-moon {
559
+ display: none;
560
+ }
561
+
562
+ [data-theme="dark"] #btn-theme-toggle .icon-sun,
563
+ :root:not([data-theme]) #btn-theme-toggle .icon-sun {
564
+ display: block;
565
+ }
566
+
567
+ [data-theme="light"] #btn-theme-toggle .icon-moon {
568
+ display: block;
569
+ }
570
+
571
+ /* ========== Keyboard shortcut hint ========== */
572
+ @media (max-width: 600px) {
573
+ .layout-btn span {
574
+ display: none;
575
+ }
576
+ .layout-btn {
577
+ padding: 5px 8px;
578
+ }
579
+ }
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <rect width="32" height="32" rx="6" fill="#1e1e2e"/>
3
+ <path d="M8 22l6-6-6-6" stroke="#50fa7b" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
4
+ <line x1="17" y1="22" x2="24" y2="22" stroke="#50fa7b" stroke-width="2.5" stroke-linecap="round"/>
5
+ </svg>
@@ -0,0 +1,56 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Multi-Window Terminal</title>
7
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg">
8
+ <link rel="stylesheet" href="/vendor/xterm/xterm.css">
9
+ <link rel="stylesheet" href="/css/style.css">
10
+ </head>
11
+ <body>
12
+ <div id="toolbar">
13
+ <div class="toolbar-left">
14
+ <button id="btn-new-terminal" title="New Terminal (Ctrl+Shift+T)">
15
+ <span class="btn-icon">+</span> New Terminal
16
+ </button>
17
+ </div>
18
+ <div class="toolbar-center">
19
+ <div class="layout-switcher">
20
+ <button class="layout-btn active" data-layout="side-by-side" title="Side by Side">
21
+ <svg width="16" height="16" viewBox="0 0 16 16"><rect x="1" y="2" width="6" height="12" rx="1" fill="currentColor" opacity="0.8"/><rect x="9" y="2" width="6" height="12" rx="1" fill="currentColor" opacity="0.5"/></svg>
22
+ <span>Side by Side</span>
23
+ </button>
24
+ <button class="layout-btn" data-layout="grid" title="Grid">
25
+ <svg width="16" height="16" viewBox="0 0 16 16"><rect x="1" y="1" width="6" height="6" rx="1" fill="currentColor" opacity="0.8"/><rect x="9" y="1" width="6" height="6" rx="1" fill="currentColor" opacity="0.5"/><rect x="1" y="9" width="6" height="6" rx="1" fill="currentColor" opacity="0.5"/><rect x="9" y="9" width="6" height="6" rx="1" fill="currentColor" opacity="0.3"/></svg>
26
+ <span>Grid</span>
27
+ </button>
28
+ <button class="layout-btn" data-layout="tabs" title="Tabs">
29
+ <svg width="16" height="16" viewBox="0 0 16 16"><rect x="1" y="4" width="14" height="11" rx="1" fill="currentColor" opacity="0.3"/><rect x="1" y="2" width="6" height="4" rx="1" fill="currentColor" opacity="0.8"/><rect x="8" y="2" width="4" height="4" rx="1" fill="currentColor" opacity="0.4"/></svg>
30
+ <span>Tabs</span>
31
+ </button>
32
+ </div>
33
+ </div>
34
+ <div class="toolbar-right">
35
+ <button id="btn-fullscreen" title="Toggle fullscreen (F11)">
36
+ <svg class="icon-enter-fs" width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 6V2h4M10 2h4v4M14 10v4h-4M6 14H2v-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
37
+ <svg class="icon-exit-fs" width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M6 2v4H2M10 6h4V2M10 14v-4h4M6 10H2v4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
38
+ </button>
39
+ <button id="btn-theme-toggle" title="Toggle theme">
40
+ <svg class="icon-sun" width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="3.5" stroke="currentColor" stroke-width="1.5"/><path d="M8 1.5v1.5M8 13v1.5M1.5 8H3M13 8h1.5M3.17 3.17l1.06 1.06M11.77 11.77l1.06 1.06M3.17 12.83l1.06-1.06M11.77 4.23l1.06-1.06" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
41
+ <svg class="icon-moon" width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M13.36 10.06A5.5 5.5 0 015.94 2.64a6 6 0 107.42 7.42z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>
42
+ </button>
43
+ <span id="terminal-count">0 terminals</span>
44
+ </div>
45
+ </div>
46
+
47
+ <div id="tab-bar" class="hidden"></div>
48
+
49
+ <div id="main-content">
50
+ <div id="terminal-container" class="layout-side-by-side"></div>
51
+ </div>
52
+
53
+ <script src="/vendor/xterm/xterm.js"></script>
54
+ <script type="module" src="/js/app.js"></script>
55
+ </body>
56
+ </html>