@auto-engineer/cli 0.8.11 → 0.8.13

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.
@@ -73,19 +73,7 @@
73
73
  transition: background-color 0.3s ease, color 0.3s ease;
74
74
  }
75
75
 
76
- /* Tab color indicators */
77
- .tab-indicator {
78
- display: inline-block;
79
- width: 8px;
80
- height: 8px;
81
- border-radius: 2px;
82
- margin-right: 8px;
83
- }
84
-
85
- .tab-indicator.command { background: var(--brand-primary); }
86
- .tab-indicator.event { background: var(--brand-orange); }
87
- .tab-indicator.state { background: var(--brand-green); }
88
-
76
+ /* Header */
89
77
  .header {
90
78
  background: var(--bg-secondary);
91
79
  border-bottom: 1px solid var(--border);
@@ -218,266 +206,273 @@
218
206
  color: var(--text-secondary);
219
207
  }
220
208
 
221
- /* New Layout */
209
+ /* Main layout - split into top and bottom sections */
222
210
  .app-layout {
223
211
  display: flex;
212
+ flex-direction: column;
224
213
  height: calc(100vh - 56px);
225
214
  }
226
215
 
227
- /* Left sidebar for registry */
228
- .sidebar-left {
229
- width: 280px;
216
+ /* Top section - Command interface (low profile) */
217
+ .command-section {
230
218
  background: var(--bg-secondary);
231
- border-right: 1px solid var(--border);
232
- overflow-y: auto;
233
- transition: background-color 0.3s ease;
234
- }
235
-
236
- /* Center content area */
237
- .center-content {
238
- flex: 1;
219
+ padding: 16px 24px;
239
220
  display: flex;
240
221
  flex-direction: column;
241
- overflow: hidden;
222
+ gap: 12px;
223
+ min-height: 60px;
242
224
  }
243
225
 
244
- /* Request Bar (Postman-like) */
245
- .request-bar {
246
- background: var(--bg-secondary);
247
- border-bottom: 1px solid var(--border);
248
- padding: 16px 24px;
226
+ /* Command controls row */
227
+ .command-controls {
249
228
  display: flex;
250
229
  gap: 12px;
251
230
  align-items: center;
252
231
  }
253
232
 
254
- .method-selector {
233
+ .command-dropdown {
255
234
  position: relative;
256
- min-width: 200px;
235
+ min-width: 320px;
257
236
  }
258
237
 
259
- .method-select {
238
+ .command-trigger {
260
239
  width: 100%;
261
- padding: 10px 40px 10px 12px;
240
+ padding: 8px 36px 8px 12px;
262
241
  background: var(--bg-tertiary);
263
242
  border: 1px solid var(--border);
264
- border-radius: 8px;
243
+ border-radius: 6px;
265
244
  color: var(--text-primary);
266
245
  font-size: 13px;
267
- font-weight: 600;
246
+ font-weight: 500;
268
247
  cursor: pointer;
269
- appearance: none;
270
- -webkit-appearance: none;
271
- -moz-appearance: none;
272
248
  transition: all 0.15s ease;
249
+ display: flex;
250
+ align-items: center;
251
+ justify-content: space-between;
252
+ min-height: 36px;
273
253
  }
274
254
 
275
- .method-select:hover {
255
+ .command-trigger:hover {
276
256
  background: var(--bg-hover);
277
257
  border-color: var(--border-light);
278
258
  }
279
259
 
280
- .method-select:focus {
281
- outline: none;
260
+ .command-trigger.open {
282
261
  border-color: var(--accent);
283
- box-shadow: 0 0 0 3px rgba(0, 87, 221, 0.1);
262
+ box-shadow: 0 0 0 2px rgba(0, 87, 221, 0.1);
284
263
  }
285
264
 
286
- .method-selector::after {
287
- content: '▼';
288
- position: absolute;
289
- right: 12px;
290
- top: 50%;
291
- transform: translateY(-50%);
292
- pointer-events: none;
265
+ .command-trigger-text {
266
+ color: var(--text-secondary);
267
+ }
268
+
269
+ .command-trigger.selected .command-trigger-text {
270
+ color: var(--text-primary);
271
+ font-weight: 600;
272
+ }
273
+
274
+ .command-dropdown-icon {
293
275
  color: var(--text-tertiary);
294
276
  font-size: 10px;
277
+ transition: transform 0.15s ease;
295
278
  }
296
279
 
297
- .method-indicator {
298
- display: inline-block;
299
- width: 8px;
300
- height: 8px;
301
- border-radius: 2px;
302
- margin-right: 8px;
280
+ .command-trigger.open .command-dropdown-icon {
281
+ transform: rotate(180deg);
303
282
  }
304
283
 
305
- .method-indicator.command { background: var(--brand-primary); }
306
- .method-indicator.state { background: var(--brand-green); }
307
-
308
- .endpoint-input {
309
- flex: 1;
310
- padding: 10px 14px;
311
- background: var(--bg-tertiary);
284
+ .command-options {
285
+ position: absolute;
286
+ top: calc(100% + 4px);
287
+ left: 0;
288
+ right: 0;
289
+ background: var(--bg-secondary);
312
290
  border: 1px solid var(--border);
313
291
  border-radius: 8px;
314
- color: var(--text-primary);
315
- font-size: 13px;
316
- font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
292
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
293
+ opacity: 0;
294
+ visibility: hidden;
295
+ transform: translateY(-4px);
317
296
  transition: all 0.15s ease;
297
+ z-index: 1000;
298
+ max-height: 300px;
299
+ overflow-y: auto;
300
+ padding: 4px;
318
301
  }
319
302
 
320
- .endpoint-input:focus {
321
- outline: none;
322
- border-color: var(--accent);
323
- background: var(--bg-secondary);
324
- box-shadow: 0 0 0 3px rgba(0, 87, 221, 0.1);
303
+ .command-options.open {
304
+ opacity: 1;
305
+ visibility: visible;
306
+ transform: translateY(0);
325
307
  }
326
308
 
327
- .send-button {
328
- padding: 10px 24px;
329
- background: var(--accent);
330
- color: white;
309
+ .command-option {
310
+ display: flex;
311
+ flex-direction: column;
312
+ padding: 12px 12px 12px 20px;
313
+ margin: 2px 0;
314
+ background: transparent;
331
315
  border: none;
332
- border-radius: 8px;
333
- font-size: 13px;
334
- font-weight: 600;
316
+ border-radius: 6px;
335
317
  cursor: pointer;
336
318
  transition: all 0.15s ease;
337
- display: flex;
338
- align-items: center;
339
- gap: 8px;
340
- text-transform: uppercase;
341
- letter-spacing: 0.05em;
319
+ width: 100%;
320
+ text-align: left;
321
+ gap: 2px;
322
+ position: relative;
342
323
  }
343
324
 
344
- .send-button:hover:not(:disabled) {
345
- background: var(--accent-hover);
346
- transform: translateY(-1px);
347
- box-shadow: 0 4px 12px rgba(0, 87, 221, 0.3);
325
+ .command-option::before {
326
+ content: '';
327
+ position: absolute;
328
+ left: 4px;
329
+ top: 50%;
330
+ transform: translateY(-50%);
331
+ width: 6px;
332
+ height: 6px;
333
+ background: #42c3f7;
334
+ border-radius: 2px;
348
335
  }
349
336
 
350
- .send-button:active:not(:disabled) {
351
- transform: translateY(0);
337
+ .command-option:hover {
338
+ background: var(--bg-hover);
352
339
  }
353
340
 
354
- .send-button:disabled {
355
- opacity: 0.5;
356
- cursor: not-allowed;
341
+ .command-option.selected {
342
+ background: var(--bg-tertiary);
357
343
  }
358
344
 
359
- /* Request/Response Container */
360
- .request-response-container {
361
- flex: 1;
362
- display: flex;
363
- overflow: hidden;
345
+ .command-option-name {
346
+ font-size: 13px;
347
+ font-weight: 600;
348
+ color: var(--text-primary);
349
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
364
350
  }
365
351
 
366
- /* Request Panel (left half) */
367
- .request-panel {
368
- flex: 1;
369
- background: var(--bg-primary);
370
- border-right: 1px solid var(--border);
371
- display: flex;
372
- flex-direction: column;
373
- overflow: hidden;
352
+ .command-option-org {
353
+ font-size: 11px;
354
+ color: var(--text-secondary);
355
+ font-weight: 500;
374
356
  }
375
357
 
376
- .panel-tabs {
377
- display: flex;
358
+ .command-option-package {
359
+ font-size: 11px;
360
+ color: var(--text-tertiary);
361
+ font-weight: 400;
362
+ }
363
+
364
+ .request-id-input {
365
+ padding: 8px 12px;
378
366
  background: var(--bg-tertiary);
379
- border-bottom: 1px solid var(--border);
380
- padding: 0 16px;
367
+ border: 1px solid var(--border);
368
+ border-radius: 6px;
369
+ color: var(--text-primary);
370
+ font-size: 12px;
371
+ width: 200px;
372
+ height: 36px;
373
+ box-sizing: border-box;
374
+ transition: all 0.15s ease;
381
375
  }
382
376
 
383
- .panel-tab {
384
- padding: 12px 16px;
385
- background: transparent;
377
+ .request-id-input:focus {
378
+ outline: none;
379
+ border-color: var(--accent);
380
+ background: var(--bg-secondary);
381
+ box-shadow: 0 0 0 2px rgba(0, 87, 221, 0.1);
382
+ }
383
+
384
+ .send-button {
385
+ padding: 8px 20px;
386
+ background: var(--accent);
387
+ color: white;
386
388
  border: none;
387
- color: var(--text-tertiary);
389
+ border-radius: 6px;
388
390
  font-size: 12px;
389
391
  font-weight: 600;
390
392
  cursor: pointer;
391
- border-bottom: 2px solid transparent;
393
+ height: 36px;
394
+ box-sizing: border-box;
392
395
  transition: all 0.15s ease;
393
396
  text-transform: uppercase;
394
397
  letter-spacing: 0.05em;
395
398
  }
396
399
 
397
- .panel-tab:hover {
398
- color: var(--text-secondary);
399
- }
400
-
401
- .panel-tab.active {
402
- color: var(--text-primary);
403
- border-bottom-color: var(--accent);
404
- }
405
-
406
- .panel-content {
407
- flex: 1;
408
- padding: 20px;
409
- overflow-y: auto;
410
- }
411
-
412
- /* Response Panel (right half) */
413
- .response-panel {
414
- flex: 1;
415
- background: var(--bg-primary);
416
- display: flex;
417
- flex-direction: column;
418
- overflow: hidden;
400
+ .send-button:hover:not(:disabled) {
401
+ background: var(--accent-hover);
402
+ transform: translateY(-1px);
403
+ box-shadow: 0 2px 8px rgba(0, 87, 221, 0.3);
419
404
  }
420
405
 
421
- .response-header {
422
- display: flex;
423
- align-items: center;
424
- justify-content: space-between;
425
- padding: 12px 20px;
426
- background: var(--bg-tertiary);
427
- border-bottom: 1px solid var(--border);
406
+ .send-button:disabled {
407
+ opacity: 0.5;
408
+ cursor: not-allowed;
428
409
  }
429
410
 
430
411
  .response-status {
431
412
  display: flex;
432
413
  align-items: center;
433
414
  gap: 8px;
434
- font-size: 12px;
415
+ font-size: 11px;
435
416
  font-weight: 600;
436
417
  text-transform: uppercase;
437
418
  letter-spacing: 0.05em;
438
419
  }
439
420
 
440
421
  .status-dot {
441
- width: 8px;
442
- height: 8px;
422
+ width: 6px;
423
+ height: 6px;
443
424
  border-radius: 50%;
444
425
  background: var(--text-tertiary);
445
426
  }
446
427
 
447
428
  .status-dot.success {
448
429
  background: var(--success);
449
- box-shadow: 0 0 8px rgba(94, 199, 45, 0.5);
430
+ box-shadow: 0 0 6px rgba(94, 199, 45, 0.5);
450
431
  }
451
432
 
452
433
  .status-dot.error {
453
434
  background: var(--error);
454
- box-shadow: 0 0 8px rgba(236, 63, 74, 0.5);
435
+ box-shadow: 0 0 6px rgba(236, 63, 74, 0.5);
455
436
  }
456
437
 
457
- .response-time {
458
- color: var(--text-tertiary);
459
- font-size: 11px;
460
- }
461
-
462
- .response-content {
438
+ .request-body {
463
439
  flex: 1;
464
- padding: 20px;
465
- overflow-y: auto;
440
+ height: 36px;
441
+ min-height: 36px;
442
+ max-height: 120px;
443
+ padding: 8px 12px;
444
+ background: var(--bg-tertiary);
445
+ border: 1px solid var(--border);
446
+ border-radius: 6px;
447
+ color: var(--text-primary);
448
+ font-size: 12px;
449
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
450
+ resize: vertical;
451
+ box-sizing: border-box;
452
+ transition: all 0.15s ease;
466
453
  }
467
454
 
468
- /* Right sidebar for events */
469
- .sidebar-right {
470
- width: 360px;
455
+ .request-body:focus {
456
+ outline: none;
457
+ border-color: var(--accent);
471
458
  background: var(--bg-secondary);
472
- border-left: 1px solid var(--border);
459
+ box-shadow: 0 0 0 2px rgba(0, 87, 221, 0.1);
460
+ }
461
+
462
+
463
+ /* Bottom section - Messages list */
464
+ .messages-section {
465
+ flex: 1;
473
466
  display: flex;
474
467
  flex-direction: column;
475
- transition: background-color 0.3s ease;
468
+ overflow: hidden;
469
+ margin-top: 24px;
470
+ border-top: 2px solid var(--border);
476
471
  }
477
472
 
478
473
  .messages-header {
479
- padding: 16px 20px;
480
- background: var(--bg-tertiary);
474
+ padding: 18px 24px;
475
+ background: var(--bg-primary);
481
476
  border-bottom: 1px solid var(--border);
482
477
  display: flex;
483
478
  justify-content: space-between;
@@ -485,13 +480,25 @@
485
480
  }
486
481
 
487
482
  .messages-title {
488
- font-size: 11px;
489
- font-weight: 600;
483
+ font-size: 14px;
484
+ font-weight: 700;
490
485
  text-transform: uppercase;
491
- letter-spacing: 0.08em;
492
- color: var(--text-secondary);
486
+ letter-spacing: 0.05em;
487
+ color: var(--text-primary);
493
488
  display: flex;
494
489
  align-items: center;
490
+ gap: 12px;
491
+ }
492
+
493
+ .messages-title span:last-child {
494
+ background: var(--accent);
495
+ color: var(--bg-primary);
496
+ padding: 4px 10px;
497
+ border-radius: 16px;
498
+ font-size: 11px;
499
+ font-weight: 600;
500
+ text-transform: none;
501
+ letter-spacing: normal;
495
502
  }
496
503
 
497
504
  .messages-controls {
@@ -499,529 +506,365 @@
499
506
  gap: 8px;
500
507
  }
501
508
 
502
- .message-filters {
503
- padding: 12px 20px;
509
+ .button-small {
510
+ padding: 6px 12px;
504
511
  background: var(--bg-secondary);
505
- border-bottom: 1px solid var(--border);
506
- display: flex;
507
- flex-direction: column;
508
- gap: 8px;
512
+ border: 1px solid var(--border);
513
+ border-radius: 4px;
514
+ font-size: 11px;
515
+ font-weight: 500;
516
+ color: var(--text-primary);
517
+ cursor: pointer;
518
+ transition: all 0.15s ease;
519
+ }
520
+
521
+ .button-small:hover {
522
+ background: var(--bg-hover);
523
+ border-color: var(--border-light);
509
524
  }
510
525
 
511
- .filter-row {
526
+ /* Message filters */
527
+ .message-filters {
528
+ padding: 16px 24px;
529
+ background: var(--bg-tertiary);
530
+ border-bottom: 2px solid var(--border);
512
531
  display: flex;
513
- gap: 8px;
532
+ gap: 16px;
533
+ flex-wrap: wrap;
534
+ align-items: center;
514
535
  }
515
536
 
516
- .filter-row select,
517
- .filter-row input,
518
- .filter-row textarea {
519
- flex: 1;
537
+ .filter-control {
520
538
  padding: 6px 8px;
521
539
  border: 1px solid var(--border);
522
540
  border-radius: 4px;
523
541
  background: var(--bg-primary);
524
542
  color: var(--text-primary);
525
543
  font-size: 11px;
526
- font-family: 'SF Mono', 'Monaco', monospace;
527
- }
528
-
529
- .filter-row textarea {
530
- resize: vertical;
531
- min-height: 32px;
544
+ min-width: 120px;
532
545
  }
533
546
 
534
547
  .filter-stats {
535
548
  font-size: 10px;
536
549
  color: var(--text-tertiary);
537
- text-align: center;
538
- padding: 4px 0;
550
+ margin-left: auto;
539
551
  }
540
552
 
553
+ /* Messages list */
541
554
  .messages-list {
542
555
  flex: 1;
543
556
  overflow-y: auto;
544
- padding: 12px;
557
+ display: flex;
558
+ flex-direction: column;
545
559
  }
546
560
 
547
- .message-item {
548
- background: var(--bg-secondary);
549
- border: 1px solid var(--border);
550
- border-radius: 6px;
551
- margin-bottom: 8px;
552
- overflow: hidden;
553
- transition: all 0.2s ease;
561
+ .messages-table {
562
+ width: 100%;
563
+ border-collapse: separate;
564
+ border-spacing: 0;
565
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
566
+ font-size: 11px;
567
+ table-layout: fixed;
568
+ min-width: 800px;
554
569
  }
555
570
 
556
- .message-item:hover {
557
- border-color: var(--accent);
558
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
559
- }
560
-
561
- .message-header {
562
- padding: 8px 12px;
571
+ .messages-header-row {
563
572
  background: var(--bg-tertiary);
564
- border-bottom: 1px solid var(--border);
565
- display: flex;
566
- justify-content: space-between;
567
- align-items: center;
568
- }
569
-
570
- .message-type {
571
- display: flex;
572
- align-items: center;
573
- gap: 6px;
574
- }
575
-
576
- .message-type-indicator {
577
- width: 8px;
578
- height: 8px;
579
- border-radius: 2px;
580
- }
581
-
582
- .message-type-indicator.command {
583
- background: #42c3f7;
584
- }
585
-
586
- .message-type-indicator.event {
587
- background: #ff8a1d;
573
+ border-bottom: 2px solid var(--border);
574
+ position: sticky;
575
+ top: 0;
576
+ z-index: 10;
588
577
  }
589
578
 
590
- .message-name {
591
- font-family: 'SF Mono', 'Monaco', monospace;
592
- font-size: 11px;
579
+ .messages-header-cell {
580
+ padding: 8px 6px;
581
+ text-align: left;
593
582
  font-weight: 600;
594
- color: var(--text-primary);
595
- }
596
-
597
- .message-timestamp {
598
- font-size: 9px;
599
- color: var(--text-tertiary);
600
- font-family: 'SF Mono', 'Monaco', monospace;
583
+ font-size: 10px;
584
+ text-transform: uppercase;
585
+ letter-spacing: 0.05em;
586
+ color: var(--text-secondary);
587
+ border-right: 1px solid var(--border);
588
+ position: relative;
589
+ user-select: none;
590
+ box-sizing: border-box;
591
+ overflow: hidden;
601
592
  }
602
593
 
603
- .message-body {
604
- padding: 12px;
594
+ .messages-header-cell:last-child {
595
+ border-right: none;
605
596
  }
606
597
 
607
- .message-ids {
608
- display: flex;
609
- gap: 12px;
610
- margin-bottom: 8px;
611
- font-size: 9px;
612
- color: var(--text-tertiary);
613
- font-family: 'SF Mono', 'Monaco', monospace;
614
- }
615
598
 
616
- .message-id {
617
- display: flex;
618
- gap: 4px;
599
+ .message-details-row {
600
+ background: var(--code-bg);
619
601
  }
620
602
 
621
- .message-id-label {
622
- color: var(--text-secondary);
603
+ .message-details {
604
+ padding: 16px;
605
+ background: var(--code-bg);
606
+ border-top: 1px solid var(--border);
607
+ font-family: 'SF Mono', 'Monaco', monospace;
623
608
  }
624
609
 
625
610
  .message-data {
626
611
  background: var(--bg-primary);
627
612
  border: 1px solid var(--border);
628
613
  border-radius: 4px;
629
- padding: 8px;
630
- font-family: 'SF Mono', 'Monaco', monospace;
631
- font-size: 10px;
614
+ padding: 12px;
615
+ font-size: 11px;
632
616
  line-height: 1.4;
633
617
  color: var(--text-primary);
634
- max-height: 200px;
635
- overflow-y: auto;
636
- }
637
-
638
- /* Registry sections in left sidebar */
639
- .handlers-section {
640
- flex: 1;
641
- display: flex;
642
- flex-direction: column;
643
- overflow: hidden;
644
- }
645
-
646
- .handlers-header {
647
- padding: 14px 20px;
648
- display: flex;
649
- align-items: center;
650
- justify-content: space-between;
651
- background: var(--bg-tertiary);
652
- border-bottom: 1px solid var(--border);
653
- transition: background-color 0.3s ease;
654
- }
655
-
656
- .handlers-title {
657
- font-size: 11px;
658
- font-weight: 600;
659
- text-transform: uppercase;
660
- letter-spacing: 0.08em;
661
- color: var(--text-secondary);
662
- }
663
-
664
- .handlers-count {
665
- font-size: 11px;
666
- padding: 3px 8px;
667
- background: var(--bg-primary);
668
- border-radius: 12px;
669
- color: var(--text-tertiary);
670
- font-weight: 500;
671
- transition: background-color 0.3s ease;
672
- }
673
-
674
- .handlers-list {
675
- flex: 1;
618
+ max-height: 300px;
676
619
  overflow-y: auto;
677
- padding: 0;
678
- }
679
-
680
- .handler-group {
681
- margin: 0;
620
+ white-space: pre-wrap;
621
+ word-break: break-word;
622
+ position: relative;
682
623
  }
683
624
 
684
- .handler-group-header {
685
- padding: 8px 20px;
686
- background: var(--bg-primary);
687
- border-bottom: 1px solid var(--border);
625
+ .copy-message-btn {
626
+ position: absolute;
627
+ top: 8px;
628
+ right: 8px;
629
+ padding: 4px 8px;
630
+ background: var(--bg-secondary);
631
+ border: 1px solid var(--border);
632
+ border-radius: 4px;
688
633
  font-size: 10px;
689
- font-weight: 600;
690
- text-transform: uppercase;
691
- letter-spacing: 0.08em;
692
- color: var(--text-tertiary);
693
- position: sticky;
694
- top: 0;
695
- z-index: 10;
696
- }
697
-
698
- .handler-item {
699
- display: flex;
700
- align-items: flex-start;
701
- gap: 10px;
702
- padding: 12px 20px;
703
- border-bottom: 1px solid var(--border);
704
634
  color: var(--text-secondary);
705
- transition: all 0.15s ease;
706
- position: relative;
707
635
  cursor: pointer;
636
+ transition: all 0.15s ease;
708
637
  }
709
638
 
710
- .handler-item.selected {
711
- background: var(--bg-tertiary);
712
- color: var(--text-primary);
713
- }
714
-
715
- .handler-item.selected::before {
716
- background: var(--accent);
717
- }
718
-
719
- .handler-item::before {
720
- content: '';
721
- position: absolute;
722
- left: 0;
723
- top: 0;
724
- bottom: 0;
725
- width: 3px;
726
- background: transparent;
727
- transition: background 0.15s ease;
728
- }
729
-
730
- .handler-item:hover {
639
+ .copy-message-btn:hover {
731
640
  background: var(--bg-hover);
732
641
  color: var(--text-primary);
733
642
  }
734
643
 
735
- .handler-item:hover::before {
736
- background: var(--accent);
737
- }
738
-
739
- .handler-type-indicator {
740
- width: 8px;
741
- height: 8px;
742
- border-radius: 2px;
743
- flex-shrink: 0;
744
- margin-top: 2px;
745
- }
746
-
747
- .handler-type-indicator.command {
748
- background: #42c3f7; /* Blue from logo */
749
- }
750
-
751
- .handler-type-indicator.event {
752
- background: #ff8a1d; /* Orange from logo */
644
+ .message-row {
645
+ background: var(--bg-secondary);
646
+ border-bottom: 1px solid var(--border);
647
+ transition: background-color 0.15s ease;
753
648
  }
754
649
 
755
- .handler-type-indicator.state {
756
- background: #5ec72d; /* Green from logo */
650
+ .message-row:hover {
651
+ background: var(--bg-hover);
757
652
  }
758
653
 
759
- .handler-content {
760
- flex: 1;
761
- display: flex;
762
- flex-direction: column;
763
- gap: 2px;
654
+ .message-row.expanded {
655
+ background: var(--bg-tertiary);
764
656
  }
765
657
 
766
- .handler-name {
767
- font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
768
- font-size: 12px;
769
- font-weight: 500;
770
- color: var(--text-primary);
771
- line-height: 1.2;
658
+ .message-cell {
659
+ padding: 4px 6px;
660
+ border-right: 1px solid var(--border);
661
+ vertical-align: top;
662
+ font-size: 11px;
663
+ box-sizing: border-box;
664
+ overflow: hidden;
665
+ word-wrap: break-word;
666
+ line-height: 1.3;
772
667
  }
773
668
 
774
- .handler-package {
775
- font-size: 10px;
776
- color: var(--text-tertiary);
777
- line-height: 1.2;
778
- font-weight: 400;
669
+ .message-cell:last-child {
670
+ border-right: none;
779
671
  }
780
672
 
781
- .empty {
782
- padding: 32px;
673
+ .expand-cell {
674
+ width: 30px;
783
675
  text-align: center;
784
- color: var(--text-tertiary);
785
- font-size: 12px;
786
- }
787
-
788
- /* Form elements */
789
- .form-group {
790
- margin-bottom: 20px;
791
- }
792
-
793
- .form-group label {
794
- display: block;
795
- margin-bottom: 8px;
796
- color: var(--text-secondary);
797
- font-size: 11px;
798
- font-weight: 600;
799
- text-transform: uppercase;
800
- letter-spacing: 0.08em;
676
+ cursor: pointer;
677
+ user-select: none;
801
678
  }
802
679
 
803
- .input, .textarea {
804
- width: 100%;
805
- background: var(--bg-tertiary);
680
+ .expand-btn {
681
+ width: 16px;
682
+ height: 16px;
683
+ display: inline-flex;
684
+ align-items: center;
685
+ justify-content: center;
686
+ background: var(--bg-primary);
806
687
  border: 1px solid var(--border);
807
- border-radius: 8px;
808
- padding: 10px 14px;
809
- color: var(--text-primary);
810
- font-size: 13px;
688
+ border-radius: 3px;
689
+ font-size: 9px;
690
+ font-weight: 600;
691
+ color: var(--text-secondary);
811
692
  transition: all 0.15s ease;
812
693
  }
813
694
 
814
- .input:focus, .textarea:focus {
815
- outline: none;
816
- border-color: var(--accent);
817
- background: var(--bg-secondary);
818
- box-shadow: 0 0 0 3px rgba(0, 87, 221, 0.1);
695
+ .expand-btn:hover {
696
+ background: var(--bg-hover);
697
+ color: var(--text-primary);
819
698
  }
820
699
 
821
- .textarea {
822
- font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
823
- font-size: 12px;
824
- min-height: 120px;
825
- resize: vertical;
700
+ .type-cell {
701
+ width: 80px;
702
+ text-align: center;
826
703
  }
827
704
 
828
- .button {
829
- background: var(--accent);
830
- color: white;
831
- border: none;
832
- padding: 10px 20px;
833
- border-radius: 8px;
834
- font-size: 13px;
705
+ .message-type {
706
+ font-size: 9px;
835
707
  font-weight: 600;
836
- cursor: pointer;
837
- transition: all 0.15s ease;
838
- display: inline-flex;
839
- align-items: center;
840
- gap: 8px;
841
708
  text-transform: uppercase;
842
709
  letter-spacing: 0.05em;
710
+ padding: 2px 6px;
711
+ border-radius: 3px;
712
+ display: inline-block;
713
+ min-width: 60px;
714
+ text-align: center;
843
715
  }
844
716
 
845
- .button:hover {
846
- background: var(--accent-hover);
847
- transform: translateY(-1px);
848
- box-shadow: 0 4px 12px rgba(0, 87, 221, 0.3);
717
+ .message-type.command {
718
+ color: #42c3f7;
719
+ background: rgba(66, 195, 247, 0.1);
849
720
  }
850
721
 
851
- .button:active {
852
- transform: translateY(0);
722
+ .message-type.event {
723
+ color: #ff8a1d;
724
+ background: rgba(255, 138, 29, 0.1);
853
725
  }
854
726
 
855
- .button-small {
856
- padding: 6px 12px;
857
- font-size: 11px;
727
+ .name-cell {
728
+ min-width: 120px;
729
+ max-width: 150px;
858
730
  }
859
731
 
860
- .button-secondary {
861
- background: var(--bg-tertiary);
732
+ .message-name {
733
+ font-weight: 600;
862
734
  color: var(--text-primary);
863
735
  }
864
736
 
865
- .button-secondary:hover {
866
- background: var(--bg-hover);
867
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
737
+ .id-cell {
738
+ width: 100px;
739
+ font-size: 10px;
740
+ color: var(--text-tertiary);
868
741
  }
869
742
 
870
- .code-block {
871
- background: var(--code-bg);
872
- border: 1px solid var(--border);
873
- border-radius: 8px;
874
- padding: 16px;
875
- font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
876
- font-size: 12px;
877
- line-height: 1.6;
878
- overflow: auto;
879
- white-space: pre-wrap;
880
- word-break: break-word;
881
- transition: background-color 0.3s ease;
882
- flex: 1;
743
+ .position-cell {
744
+ width: 60px;
745
+ text-align: center;
746
+ font-size: 10px;
747
+ color: var(--text-secondary);
883
748
  }
884
749
 
885
- .response-header {
886
- display: flex;
887
- align-items: center;
888
- justify-content: space-between;
889
- margin-bottom: 16px;
750
+ .time-cell {
751
+ width: 80px;
752
+ text-align: right;
753
+ font-size: 10px;
754
+ color: var(--text-tertiary);
890
755
  }
891
756
 
892
- .response-status {
757
+ .message-type-icon {
758
+ width: 12px;
759
+ height: 12px;
893
760
  display: flex;
894
761
  align-items: center;
895
- gap: 8px;
896
- font-size: 12px;
897
- font-weight: 600;
898
- text-transform: uppercase;
899
- letter-spacing: 0.05em;
900
- }
901
-
902
- .status-dot {
903
- width: 8px;
904
- height: 8px;
905
- border-radius: 50%;
906
- background: var(--text-tertiary);
907
- }
908
-
909
- .status-dot.success {
910
- background: var(--success);
911
- box-shadow: 0 0 8px rgba(94, 199, 45, 0.5);
762
+ justify-content: center;
763
+ font-size: 8px;
764
+ flex-shrink: 0;
912
765
  }
913
-
914
- .status-dot.error {
915
- background: var(--error);
916
- box-shadow: 0 0 8px rgba(236, 63, 74, 0.5);
766
+
767
+ .message-type {
768
+ min-width: 50px;
769
+ font-size: 10px;
770
+ font-weight: 600;
771
+ text-transform: uppercase;
772
+ letter-spacing: 0.05em;
773
+ flex-shrink: 0;
917
774
  }
918
775
 
919
- .response-time {
920
- color: var(--text-tertiary);
921
- font-size: 11px;
776
+ .message-type.command {
777
+ color: #42c3f7;
922
778
  }
923
779
 
924
- .empty {
925
- padding: 32px;
926
- text-align: center;
927
- color: var(--text-tertiary);
928
- font-size: 12px;
780
+ .message-type.event {
781
+ color: #ff8a1d;
929
782
  }
930
783
 
931
- .divider {
932
- height: 1px;
933
- background: var(--border);
934
- margin: 20px 0;
784
+ .message-name {
785
+ min-width: 120px;
786
+ font-weight: 500;
787
+ color: var(--text-primary);
788
+ flex-shrink: 0;
935
789
  }
936
790
 
937
- .keyboard-shortcut {
938
- display: inline-flex;
791
+ .message-meta {
792
+ display: flex;
793
+ gap: 12px;
939
794
  align-items: center;
940
- gap: 2px;
941
- padding: 2px 6px;
942
- background: var(--bg-tertiary);
943
- border: 1px solid var(--border);
944
- border-radius: 4px;
945
795
  font-size: 10px;
946
796
  color: var(--text-tertiary);
947
- font-family: 'SF Mono', monospace;
797
+ flex: 1;
948
798
  }
949
799
 
950
- /* Event items in right sidebar */
951
- .event-item {
952
- background: var(--bg-tertiary);
953
- border: 1px solid var(--border);
954
- border-radius: 8px;
955
- padding: 12px;
956
- margin-bottom: 8px;
957
- transition: all 0.15s ease;
958
- position: relative;
959
- overflow: hidden;
800
+ .message-timestamp {
801
+ font-size: 10px;
802
+ color: var(--text-tertiary);
803
+ min-width: 80px;
804
+ text-align: right;
805
+ flex-shrink: 0;
960
806
  }
961
807
 
962
- .event-item::before {
963
- content: '';
964
- position: absolute;
965
- left: 0;
966
- top: 0;
967
- bottom: 0;
968
- width: 3px;
969
- background: var(--brand-orange);
970
- opacity: 0.5;
971
- transition: opacity 0.15s ease;
808
+ .message-details {
809
+ display: none;
810
+ padding: 16px;
811
+ background: var(--code-bg);
812
+ border-top: 1px solid var(--border);
972
813
  }
973
814
 
974
- .event-item:hover {
975
- background: var(--bg-hover);
976
- border-color: var(--border-light);
815
+ .message-item.expanded .message-details {
816
+ display: block;
977
817
  }
978
818
 
979
- .event-item:hover::before {
980
- opacity: 1;
819
+ .message-ids {
820
+ display: grid;
821
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
822
+ gap: 12px;
823
+ margin-bottom: 12px;
824
+ font-size: 10px;
825
+ color: var(--text-tertiary);
826
+ font-family: 'SF Mono', 'Monaco', monospace;
981
827
  }
982
828
 
983
- .event-header {
829
+ .message-id {
984
830
  display: flex;
985
- justify-content: space-between;
986
- align-items: center;
987
- margin-bottom: 8px;
831
+ flex-direction: column;
832
+ gap: 2px;
988
833
  }
989
834
 
990
- .event-type {
835
+ .message-id-label {
836
+ color: var(--text-secondary);
991
837
  font-weight: 600;
992
- color: var(--accent);
993
- font-size: 12px;
994
- }
995
-
996
- .event-time {
997
- color: var(--text-tertiary);
998
- font-size: 10px;
838
+ text-transform: uppercase;
839
+ letter-spacing: 0.05em;
999
840
  }
1000
841
 
1001
- .event-data {
1002
- background: var(--code-bg);
842
+ .message-data {
843
+ background: var(--bg-primary);
1003
844
  border: 1px solid var(--border);
1004
845
  border-radius: 4px;
1005
- padding: 8px;
1006
- font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
1007
- font-size: 10px;
1008
- color: var(--text-secondary);
846
+ padding: 12px;
847
+ font-family: 'SF Mono', 'Monaco', monospace;
848
+ font-size: 11px;
849
+ line-height: 1.4;
850
+ color: var(--text-primary);
851
+ max-height: 300px;
852
+ overflow-y: auto;
1009
853
  white-space: pre-wrap;
1010
854
  word-break: break-word;
1011
- max-height: 100px;
1012
- overflow-y: auto;
1013
- transition: background-color 0.3s ease;
1014
855
  }
1015
856
 
1016
- @keyframes pulse {
1017
- 0%, 100% { opacity: 1; }
1018
- 50% { opacity: 0.5; }
857
+ .empty {
858
+ padding: 32px;
859
+ text-align: center;
860
+ color: var(--text-tertiary);
861
+ font-size: 12px;
1019
862
  }
1020
863
 
1021
864
  /* Scrollbar styling */
1022
865
  ::-webkit-scrollbar {
1023
- width: 10px;
1024
- height: 10px;
866
+ width: 8px;
867
+ height: 8px;
1025
868
  }
1026
869
 
1027
870
  ::-webkit-scrollbar-track {
@@ -1030,24 +873,12 @@
1030
873
 
1031
874
  ::-webkit-scrollbar-thumb {
1032
875
  background: var(--border-light);
1033
- border-radius: 5px;
876
+ border-radius: 4px;
1034
877
  }
1035
878
 
1036
879
  ::-webkit-scrollbar-thumb:hover {
1037
880
  background: var(--text-tertiary);
1038
881
  }
1039
-
1040
- @media (max-width: 1200px) {
1041
- .sidebar-left {
1042
- display: none;
1043
- }
1044
- }
1045
-
1046
- @media (max-width: 768px) {
1047
- .sidebar-right {
1048
- display: none;
1049
- }
1050
- }
1051
882
  </style>
1052
883
  </head>
1053
884
  <body data-theme="dark">
@@ -1091,134 +922,144 @@
1091
922
  </div>
1092
923
 
1093
924
  <div class="app-layout">
1094
- <!-- Left Sidebar: Registry -->
1095
- <div class="sidebar-left">
1096
- <div class="handlers-section">
1097
- <div class="handlers-header">
1098
- <span class="handlers-title">Commands</span>
1099
- <span class="handlers-count" id="totalHandlersCount">0</span>
1100
- </div>
1101
- <div class="handlers-list" id="handlersList">
1102
- <div class="empty">No handlers registered</div>
1103
- </div>
1104
- </div>
1105
- </div>
1106
-
1107
- <!-- Center Content -->
1108
- <div class="center-content">
1109
- <!-- Request Bar -->
1110
- <div class="request-bar">
1111
- <div style="flex: 1; display: flex; align-items: center; gap: 12px;">
1112
- <span style="color: var(--text-tertiary); font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em;">Selected Command:</span>
1113
- <span id="selectedCommand" style="font-family: 'SF Mono', monospace; font-size: 14px; font-weight: 600; color: var(--text-primary);">None</span>
925
+ <!-- Top section: Command interface -->
926
+ <div class="command-section">
927
+ <!-- Command controls row -->
928
+ <div class="command-controls">
929
+ <div class="command-dropdown">
930
+ <div class="command-trigger" id="commandTrigger" onclick="toggleCommandDropdown()">
931
+ <span class="command-trigger-text">Select a command...</span>
932
+ <span class="command-dropdown-icon">▼</span>
933
+ </div>
934
+ <div class="command-options" id="commandOptions">
935
+ <!-- Command options will be populated by JavaScript -->
936
+ </div>
1114
937
  </div>
1115
- <input type="text" class="endpoint-input" id="requestIdInput" placeholder="Request ID (optional)" style="max-width: 200px;" />
938
+
939
+ <textarea class="request-body" id="requestBody" placeholder='Request body (JSON)&#10;Example: {"key": "value"}'>{}</textarea>
940
+
941
+ <input type="text" class="request-id-input" id="requestIdInput" placeholder="Request ID (optional)" />
942
+
1116
943
  <button class="send-button" onclick="sendRequest()" id="sendButton" disabled>
1117
944
  Send
1118
- <span class="keyboard-shortcut">⌘ ↵</span>
1119
945
  </button>
1120
- </div>
1121
-
1122
- <!-- Request/Response Container -->
1123
- <div class="request-response-container">
1124
- <!-- Request Panel -->
1125
- <div class="request-panel">
1126
- <div class="panel-content" style="display: flex; flex-direction: column; gap: 20px; padding: 20px;">
1127
- <div id="paramsSection">
1128
- <div class="section-header" style="background: transparent; padding: 0 0 12px 0; border-bottom: 1px solid var(--border);">
1129
- <span class="section-title">Parameters</span>
1130
- </div>
1131
- <div id="paramsContainer" style="padding-top: 12px;">
1132
- <div class="empty" id="emptyParams">No parameters required</div>
1133
- <!-- Parameters will be added dynamically -->
1134
- </div>
1135
- </div>
1136
- <div id="bodySection">
1137
- <div class="section-header" style="background: transparent; padding: 0 0 12px 0; border-bottom: 1px solid var(--border);">
1138
- <span class="section-title">Request Body (JSON)</span>
1139
- </div>
1140
- <div style="padding-top: 12px;">
1141
- <textarea class="textarea" id="requestBody" style="min-height: 200px; font-family: 'SF Mono', monospace; width: 100%;">{}</textarea>
1142
- </div>
1143
- </div>
1144
- </div>
1145
- </div>
1146
946
 
1147
- <!-- Response Panel -->
1148
- <div class="response-panel">
1149
- <div class="response-header">
1150
- <div class="response-status">
1151
- <div class="status-dot" id="statusDot"></div>
1152
- <span id="statusText">Ready</span>
1153
- </div>
1154
- <span class="response-time" id="responseTime"></span>
1155
- </div>
1156
- <div class="response-content">
1157
- <div class="code-block" id="responseContent" style="height: 100%;">No response yet</div>
1158
- </div>
947
+ <div class="response-status">
948
+ <div class="status-dot" id="statusDot"></div>
949
+ <span id="statusText">Ready</span>
950
+ <span id="responseTime"></span>
1159
951
  </div>
1160
952
  </div>
1161
953
  </div>
1162
954
 
1163
- <!-- Right Sidebar: Messages -->
1164
- <div class="sidebar-right">
955
+ <!-- Bottom section: Messages list -->
956
+ <div class="messages-section">
1165
957
  <div class="messages-header">
1166
958
  <div class="messages-title">
1167
- <span class="tab-indicator message"></span>
1168
- Messages
959
+ <span>Messages</span>
960
+ <span id="messageCount">0</span>
1169
961
  </div>
1170
962
  <div class="messages-controls">
1171
- <button class="button button-small button-secondary" onclick="loadMessages()">↻</button>
1172
- <button class="button button-small button-secondary" onclick="clearMessageFilters()">Clear Filters</button>
963
+ <button class="button-small" onclick="clearFilters()">Clear Filters</button>
1173
964
  </div>
1174
965
  </div>
1175
966
 
1176
- <!-- Message Filters -->
1177
967
  <div class="message-filters">
1178
- <div class="filter-row">
1179
- <select id="messageTypeFilter" onchange="applyMessageFilters()">
1180
- <option value="">All Types</option>
1181
- <option value="command">Commands</option>
1182
- <option value="event">Events</option>
1183
- </select>
1184
-
1185
- <select id="sessionFilter" onchange="applyMessageFilters()">
1186
- <option value="">All Sessions</option>
1187
- </select>
1188
- </div>
968
+ <select class="filter-control" id="messageTypeFilter" onchange="applyFilters()">
969
+ <option value="">All Types</option>
970
+ <option value="command">Commands</option>
971
+ <option value="event">Events</option>
972
+ </select>
1189
973
 
1190
- <div class="filter-row">
1191
- <input type="text" id="messageNameFilter" placeholder="Message name..." onchange="applyMessageFilters()" />
1192
- <input type="text" id="correlationIdFilter" placeholder="Correlation ID..." onchange="applyMessageFilters()" />
1193
- </div>
974
+ <select class="filter-control" id="sessionFilter" onchange="applyFilters()">
975
+ <option value="">All Sessions</option>
976
+ </select>
1194
977
 
1195
- <div class="filter-row">
1196
- <textarea id="jsonFilter" placeholder='JSON Filter (e.g., {"userId": "123"})'
1197
- onchange="applyMessageFilters()" rows="2"></textarea>
1198
- </div>
978
+ <input type="text" class="filter-control" id="messageNameFilter" placeholder="Message name..." onchange="applyFilters()" />
979
+
980
+ <input type="text" class="filter-control" id="correlationIdFilter" placeholder="Correlation ID..." onchange="applyFilters()" />
1199
981
 
1200
982
  <div class="filter-stats" id="filterStats">
1201
983
  Showing all messages
1202
984
  </div>
1203
985
  </div>
1204
986
 
1205
- <div class="messages-list" id="messagesContainer">
1206
- <div class="empty">No messages yet</div>
987
+ <div class="messages-list" id="messagesList">
988
+ <table class="messages-table">
989
+ <colgroup>
990
+ <col style="width: 40px;">
991
+ <col style="width: 80px;">
992
+ <col style="width: 180px;">
993
+ <col style="width: 120px;">
994
+ <col style="width: 120px;">
995
+ <col style="width: 120px;">
996
+ <col style="width: 60px;">
997
+ <col style="width: 90px;">
998
+ </colgroup>
999
+ <thead>
1000
+ <tr class="messages-header-row">
1001
+ <th class="messages-header-cell expand-cell" data-column="expand">
1002
+ </th>
1003
+ <th class="messages-header-cell type-cell" data-column="type">
1004
+ Type
1005
+ </th>
1006
+ <th class="messages-header-cell name-cell" data-column="name">
1007
+ Message
1008
+ </th>
1009
+ <th class="messages-header-cell id-cell" data-column="requestId">
1010
+ Request ID
1011
+ </th>
1012
+ <th class="messages-header-cell id-cell" data-column="correlationId">
1013
+ Correlation ID
1014
+ </th>
1015
+ <th class="messages-header-cell id-cell" data-column="sessionId">
1016
+ Session ID
1017
+ </th>
1018
+ <th class="messages-header-cell position-cell" data-column="position">
1019
+ Pos
1020
+ </th>
1021
+ <th class="messages-header-cell time-cell" data-column="time">
1022
+ Time
1023
+ </th>
1024
+ </tr>
1025
+ </thead>
1026
+ <tbody id="messagesTableBody">
1027
+ <tr><td colspan="8" style="text-align: center; padding: 32px; color: var(--text-tertiary);">No messages yet</td></tr>
1028
+ </tbody>
1029
+ </table>
1207
1030
  </div>
1208
1031
  </div>
1209
1032
  </div>
1210
1033
 
1211
1034
  <script>
1212
1035
  const API_BASE = window.location.origin;
1213
- let currentState = {};
1036
+ let availableCommands = [];
1214
1037
  let currentMessages = [];
1215
1038
  let filteredMessages = [];
1216
1039
  let availableSessions = [];
1040
+ let currentCommand = null;
1217
1041
  let responseStartTime;
1218
- let availableCommands = [];
1219
- let currentCommand = localStorage.getItem('lastCommand') || null;
1042
+ let expandedMessages = new Set();
1043
+ let isResizing = false;
1044
+ let currentColumn = null;
1045
+ let startX = 0;
1046
+ let startWidth = 0;
1047
+
1048
+ // Default column widths (percentages for better responsive behavior)
1049
+ const defaultColumnWidths = {
1050
+ expand: 40,
1051
+ type: 80,
1052
+ name: 180,
1053
+ requestId: 120,
1054
+ correlationId: 120,
1055
+ sessionId: 120,
1056
+ position: 60,
1057
+ time: 90
1058
+ };
1220
1059
 
1221
- // Logo SVGs - exact content from logo files
1060
+ let columnWidths = { ...defaultColumnWidths };
1061
+
1062
+ // Logo SVGs
1222
1063
  const logoLight = `<svg width="637" height="637" viewBox="0 0 637 637" fill="none" xmlns="http://www.w3.org/2000/svg">
1223
1064
  <g clip-path="url(#clip0_1042_573)">
1224
1065
  <rect width="637" height="637"/>
@@ -1274,16 +1115,6 @@
1274
1115
  return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
1275
1116
  }
1276
1117
 
1277
- function updateFavicon(theme) {
1278
- const favicon = document.getElementById('favicon');
1279
- if (favicon) {
1280
- const isDark = theme === 'dark';
1281
- const bgColor = isDark ? '%231A1A1A' : '%23ffffff';
1282
- const fgColor = isDark ? '%23ffffff' : '%231A1A1A';
1283
- favicon.href = `data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect width='100' height='100' fill='${bgColor}'/><rect x='20' y='20' width='20' height='20' fill='%23ec3f4a'/><rect x='40' y='20' width='20' height='20' fill='%23ff8a1d'/><rect x='20' y='40' width='20' height='20' fill='%235ec72d'/><rect x='40' y='40' width='20' height='20' fill='%2342c3f7'/><rect x='60' y='40' width='20' height='20' fill='${fgColor}'/><rect x='20' y='60' width='20' height='20' fill='${fgColor}'/></svg>`;
1284
- }
1285
- }
1286
-
1287
1118
  function applyTheme(theme) {
1288
1119
  if (theme === 'system') {
1289
1120
  theme = getSystemTheme();
@@ -1295,9 +1126,6 @@
1295
1126
  if (logoMark) {
1296
1127
  logoMark.innerHTML = theme === 'dark' ? logoDark : logoLight;
1297
1128
  }
1298
-
1299
- // Update favicon based on theme
1300
- updateFavicon(theme);
1301
1129
  }
1302
1130
 
1303
1131
  function getThemeIcon(theme) {
@@ -1355,30 +1183,73 @@
1355
1183
  }
1356
1184
  });
1357
1185
 
1186
+ // Command selection
1358
1187
  function selectCommand(command) {
1188
+ if (!command) return;
1189
+
1359
1190
  currentCommand = command;
1360
1191
  localStorage.setItem('lastCommand', command);
1361
1192
 
1362
- // Update selected command display
1363
- document.getElementById('selectedCommand').textContent = command;
1364
-
1365
- // Update sidebar selection
1366
- document.querySelectorAll('.handler-item').forEach(item => {
1367
- item.classList.toggle('selected', item.dataset.command === command);
1368
- });
1369
-
1370
1193
  // Enable send button
1371
1194
  document.getElementById('sendButton').disabled = false;
1372
1195
 
1373
- // Clear response
1374
- document.getElementById('responseContent').textContent = 'No response yet';
1196
+ // Clear response status
1375
1197
  document.getElementById('statusDot').className = 'status-dot';
1376
1198
  document.getElementById('statusText').textContent = 'Ready';
1377
1199
  document.getElementById('responseTime').textContent = '';
1378
1200
  }
1379
1201
 
1380
- // Tab switching function removed - no longer needed
1202
+ // Toggle command dropdown
1203
+ function toggleCommandDropdown() {
1204
+ const trigger = document.getElementById('commandTrigger');
1205
+ const options = document.getElementById('commandOptions');
1206
+
1207
+ const isOpen = trigger.classList.contains('open');
1208
+
1209
+ if (isOpen) {
1210
+ trigger.classList.remove('open');
1211
+ options.classList.remove('open');
1212
+ } else {
1213
+ trigger.classList.add('open');
1214
+ options.classList.add('open');
1215
+ }
1216
+ }
1217
+
1218
+ // Select command from custom dropdown
1219
+ function selectCommandFromDropdown(command) {
1220
+ // Update trigger text
1221
+ const trigger = document.getElementById('commandTrigger');
1222
+ const triggerText = trigger.querySelector('.command-trigger-text');
1223
+
1224
+ // Mark trigger as selected
1225
+ trigger.classList.add('selected');
1226
+ triggerText.textContent = command;
1227
+
1228
+ // Update option selection states
1229
+ document.querySelectorAll('.command-option').forEach(option => {
1230
+ option.classList.toggle('selected', option.getAttribute('data-command') === command);
1231
+ });
1232
+
1233
+ // Close dropdown
1234
+ trigger.classList.remove('open');
1235
+ document.getElementById('commandOptions').classList.remove('open');
1236
+
1237
+ // Call original select command logic
1238
+ selectCommand(command);
1239
+ }
1240
+
1241
+ // Close dropdown when clicking outside
1242
+ document.addEventListener('click', (e) => {
1243
+ const dropdown = document.querySelector('.command-dropdown');
1244
+ if (!dropdown.contains(e.target)) {
1245
+ const trigger = document.getElementById('commandTrigger');
1246
+ const options = document.getElementById('commandOptions');
1247
+ trigger.classList.remove('open');
1248
+ options.classList.remove('open');
1249
+ }
1250
+ });
1381
1251
 
1252
+ // Send request
1382
1253
  async function sendRequest() {
1383
1254
  if (!currentCommand) {
1384
1255
  alert('Please select a command first');
@@ -1387,6 +1258,10 @@
1387
1258
 
1388
1259
  responseStartTime = Date.now();
1389
1260
 
1261
+ // Update status to sending
1262
+ document.getElementById('statusDot').className = 'status-dot';
1263
+ document.getElementById('statusText').textContent = 'Sending...';
1264
+
1390
1265
  try {
1391
1266
  const requestId = document.getElementById('requestIdInput').value;
1392
1267
  const bodyStr = document.getElementById('requestBody').value;
@@ -1413,95 +1288,70 @@
1413
1288
  const responseTime = Date.now() - responseStartTime;
1414
1289
  const result = await response.json();
1415
1290
 
1416
- document.getElementById('responseContent').textContent = JSON.stringify(result, null, 2);
1417
- document.getElementById('responseTime').textContent = `${responseTime}ms`;
1418
-
1419
1291
  const statusDot = document.getElementById('statusDot');
1420
1292
  const statusText = document.getElementById('statusText');
1421
1293
 
1422
1294
  if (response.ok && result.status === 'ack') {
1423
1295
  statusDot.className = 'status-dot success';
1424
- statusText.textContent = 'Success';
1296
+ statusText.textContent = 'ACK';
1425
1297
  } else {
1426
1298
  statusDot.className = 'status-dot error';
1427
- statusText.textContent = response.status === 404 ? 'Command Not Found' : 'Error';
1428
-
1429
- // Show available commands if provided
1430
- if (result.availableCommands) {
1431
- const availableCommands = result.availableCommands.length > 0
1432
- ? `Available commands: ${result.availableCommands.join(', ')}`
1433
- : 'No command handlers registered';
1434
- result.availableCommands = availableCommands;
1435
- }
1299
+ statusText.textContent = 'ERROR';
1436
1300
  }
1301
+ document.getElementById('responseTime').textContent = `${responseTime}ms`;
1302
+
1303
+ // Auto refresh messages after sending
1304
+ setTimeout(loadMessages, 500);
1305
+
1437
1306
  } catch (error) {
1438
1307
  const responseTime = Date.now() - responseStartTime;
1439
1308
 
1440
- document.getElementById('responseContent').textContent = error.message;
1441
1309
  document.getElementById('responseTime').textContent = `${responseTime}ms`;
1442
-
1443
1310
  document.getElementById('statusDot').className = 'status-dot error';
1444
- document.getElementById('statusText').textContent = 'Error';
1311
+ document.getElementById('statusText').textContent = 'ERROR';
1445
1312
  }
1446
1313
  }
1447
1314
 
1315
+ // Load registry and populate command dropdown
1448
1316
  async function loadRegistry() {
1449
1317
  try {
1450
1318
  const response = await fetch(`${API_BASE}/registry`);
1451
1319
  const data = await response.json();
1452
1320
 
1453
- const eventHandlers = data.eventHandlers || [];
1454
- const folds = data.folds || [];
1455
1321
  const commandHandlers = data.commandHandlers || [];
1456
1322
  const commandsWithMetadata = data.commandsWithMetadata || [];
1457
1323
 
1458
- // Update count in sidebar (only commands now)
1459
- document.getElementById('totalHandlersCount').textContent = commandHandlers.length;
1324
+ availableCommands = commandHandlers;
1460
1325
 
1461
- // Build unified handlers list
1462
- const handlersListEl = document.getElementById('handlersList');
1326
+ // Populate command dropdown with detailed 3-line format
1327
+ const commandOptions = document.getElementById('commandOptions');
1328
+ commandOptions.innerHTML = '';
1463
1329
 
1464
- if (commandHandlers.length === 0) {
1465
- handlersListEl.innerHTML = '<div class="empty">No handlers registered</div>';
1466
- } else {
1467
- let html = '';
1330
+ commandsWithMetadata.forEach(cmd => {
1331
+ const option = document.createElement('button');
1332
+ option.className = 'command-option';
1333
+ option.setAttribute('data-command', cmd.name);
1334
+ option.onclick = () => selectCommandFromDropdown(cmd.name);
1468
1335
 
1469
- // Show commands with metadata (required)
1470
- html += '<div class="handler-group">';
1471
- commandsWithMetadata.forEach(cmd => {
1472
- const packageInfo = cmd.package && cmd.package !== 'unknown' ? cmd.package : '';
1473
- const versionInfo = cmd.version ? `v${cmd.version}` : '';
1474
- const tooltip = packageInfo ? `${packageInfo} ${versionInfo}`.trim() : cmd.name;
1475
-
1476
- // Split package name at '/' to display on separate lines
1477
- let packageDisplay = '';
1478
- if (packageInfo && packageInfo.includes('/')) {
1479
- const parts = packageInfo.split('/');
1480
- packageDisplay = `${parts[0]}/<br/>${parts[1]}`;
1481
- } else {
1482
- packageDisplay = packageInfo;
1483
- }
1484
-
1485
- html += `<div class="handler-item" onclick="selectCommand('${cmd.name}')" data-command="${cmd.name}" title="${tooltip}">
1486
- <div class="handler-type-indicator command"></div>
1487
- <div class="handler-content">
1488
- <div class="handler-name">${cmd.alias}</div>
1489
- <div class="handler-package">${packageDisplay}</div>
1490
- </div>
1491
- </div>`;
1492
- });
1493
- html += '</div>';
1336
+ // Create 3-line format: command name, org, package
1337
+ const packageParts = (cmd.package || 'core').split('/');
1338
+ const org = packageParts.length > 1 ? packageParts[0] : '@auto-engineer';
1339
+ const packageName = packageParts.length > 1 ? packageParts[1] : packageParts[0];
1494
1340
 
1495
- handlersListEl.innerHTML = html;
1496
- }
1497
-
1498
- // Store available commands
1499
- availableCommands = commandHandlers;
1341
+ option.innerHTML = `
1342
+ <div class="command-option-name">${cmd.name}</div>
1343
+ <div class="command-option-org">${org}</div>
1344
+ <div class="command-option-package">${packageName}</div>
1345
+ `;
1346
+ option.title = `${cmd.name}\n\nOrganization: ${org}\nPackage: ${packageName}\nType: ${cmd.type || 'command'}\n\nDescription: ${cmd.description || 'No description available'}`;
1347
+
1348
+ commandOptions.appendChild(option);
1349
+ });
1500
1350
 
1501
- // Restore last selected command if it's still available
1351
+ // Restore last selected command
1502
1352
  const lastCommand = localStorage.getItem('lastCommand');
1503
1353
  if (lastCommand && commandHandlers.includes(lastCommand)) {
1504
- selectCommand(lastCommand);
1354
+ selectCommandFromDropdown(lastCommand);
1505
1355
  }
1506
1356
 
1507
1357
  } catch (error) {
@@ -1509,18 +1359,9 @@
1509
1359
  }
1510
1360
  }
1511
1361
 
1512
- async function loadState() {
1513
- try {
1514
- const response = await fetch(`${API_BASE}/state`);
1515
- currentState = await response.json();
1516
- } catch (error) {
1517
- console.error('Failed to load state:', error);
1518
- }
1519
- }
1520
-
1362
+ // Load messages
1521
1363
  async function loadMessages() {
1522
1364
  try {
1523
- // Load all messages
1524
1365
  const messagesResponse = await fetch(`${API_BASE}/messages?count=500`);
1525
1366
  currentMessages = await messagesResponse.json();
1526
1367
 
@@ -1529,13 +1370,14 @@
1529
1370
  availableSessions = await sessionsResponse.json();
1530
1371
  updateSessionFilter();
1531
1372
 
1532
- applyMessageFilters();
1373
+ applyFilters();
1533
1374
  } catch (error) {
1534
1375
  console.error('Failed to load messages:', error);
1535
- document.getElementById('messagesContainer').innerHTML = '<div class="empty">Failed to load messages</div>';
1376
+ document.getElementById('messagesList').innerHTML = '<div class="empty">Failed to load messages</div>';
1536
1377
  }
1537
1378
  }
1538
1379
 
1380
+ // Update session filter dropdown
1539
1381
  function updateSessionFilter() {
1540
1382
  const sessionFilter = document.getElementById('sessionFilter');
1541
1383
  const currentValue = sessionFilter.value;
@@ -1548,7 +1390,7 @@
1548
1390
  const startTime = new Date(session.startedAt).toLocaleString();
1549
1391
  const option = document.createElement('option');
1550
1392
  option.value = session.sessionId;
1551
- option.textContent = `${session.sessionId} (${startTime})`;
1393
+ option.textContent = `${session.sessionId.substr(0, 12)}... (${startTime})`;
1552
1394
  sessionFilter.appendChild(option);
1553
1395
  });
1554
1396
 
@@ -1556,21 +1398,12 @@
1556
1398
  sessionFilter.value = currentValue;
1557
1399
  }
1558
1400
 
1559
- function applyMessageFilters() {
1401
+ // Apply message filters
1402
+ function applyFilters() {
1560
1403
  const messageTypeFilter = document.getElementById('messageTypeFilter').value;
1561
1404
  const sessionFilter = document.getElementById('sessionFilter').value;
1562
1405
  const messageNameFilter = document.getElementById('messageNameFilter').value.toLowerCase();
1563
1406
  const correlationIdFilter = document.getElementById('correlationIdFilter').value.toLowerCase();
1564
- const jsonFilterText = document.getElementById('jsonFilter').value.trim();
1565
-
1566
- let jsonFilterObj = null;
1567
- if (jsonFilterText) {
1568
- try {
1569
- jsonFilterObj = JSON.parse(jsonFilterText);
1570
- } catch (error) {
1571
- console.warn('Invalid JSON filter:', error);
1572
- }
1573
- }
1574
1407
 
1575
1408
  filteredMessages = currentMessages.filter(message => {
1576
1409
  // Message type filter
@@ -1596,108 +1429,292 @@
1596
1429
  }
1597
1430
  }
1598
1431
 
1599
- // JSON filter
1600
- if (jsonFilterObj) {
1601
- if (!matchesJsonFilter(message.message.data, jsonFilterObj)) {
1602
- return false;
1603
- }
1604
- }
1605
-
1606
1432
  return true;
1607
1433
  });
1608
1434
 
1609
1435
  displayMessages();
1610
1436
  }
1611
1437
 
1612
- function matchesJsonFilter(data, filterObj) {
1613
- for (const [key, expectedValue] of Object.entries(filterObj)) {
1614
- if (data[key] !== expectedValue) {
1615
- return false;
1616
- }
1617
- }
1618
- return true;
1619
- }
1620
-
1438
+ // Display messages with preserved expansion state
1621
1439
  function displayMessages() {
1622
- const container = document.getElementById('messagesContainer');
1440
+ const tableBody = document.getElementById('messagesTableBody');
1441
+ const countElement = document.getElementById('messageCount');
1623
1442
  const statsElement = document.getElementById('filterStats');
1624
1443
 
1625
1444
  if (filteredMessages.length === 0) {
1626
- container.innerHTML = '<div class="empty">No messages match the current filters</div>';
1445
+ tableBody.innerHTML = '<tr><td colspan="8" style="text-align: center; padding: 32px; color: var(--text-tertiary);">No messages match the current filters</td></tr>';
1446
+ countElement.textContent = '0';
1627
1447
  statsElement.textContent = `Showing 0 of ${currentMessages.length} messages`;
1628
1448
  return;
1629
1449
  }
1630
1450
 
1631
- // Update stats
1451
+ // Update count and stats
1452
+ countElement.textContent = filteredMessages.length;
1632
1453
  const commandCount = filteredMessages.filter(m => m.messageType === 'command').length;
1633
1454
  const eventCount = filteredMessages.filter(m => m.messageType === 'event').length;
1634
1455
  statsElement.textContent = `Showing ${filteredMessages.length} of ${currentMessages.length} messages (${commandCount} commands, ${eventCount} events)`;
1635
1456
 
1636
- // Display messages in reverse chronological order (newest first)
1457
+ // Display messages in chronological order (oldest first)
1637
1458
  const messagesHtml = filteredMessages
1638
1459
  .slice()
1639
- .reverse()
1460
+ .sort((a, b) => parseInt(a.position) - parseInt(b.position))
1640
1461
  .map((message) => {
1641
- const time = new Date(message.timestamp).toLocaleTimeString();
1642
- const date = new Date(message.timestamp).toLocaleDateString();
1462
+ const timeObj = new Date(message.timestamp);
1463
+ const time = timeObj.toLocaleTimeString('en-US', {
1464
+ hour12: false,
1465
+ hour: '2-digit',
1466
+ minute: '2-digit',
1467
+ second: '2-digit'
1468
+ }) + '.' + timeObj.getMilliseconds().toString().padStart(3, '0');
1469
+ const isExpanded = expandedMessages.has(String(message.position));
1643
1470
 
1644
1471
  const correlationId = message.message.correlationId || 'none';
1645
1472
  const requestId = message.message.requestId || 'none';
1646
1473
  const sessionId = message.sessionId || 'none';
1647
1474
 
1648
- return `
1649
- <div class="message-item">
1650
- <div class="message-header">
1651
- <div class="message-type">
1652
- <div class="message-type-indicator ${message.messageType}"></div>
1653
- <div class="message-name">${message.message.type}</div>
1654
- </div>
1655
- <div class="message-timestamp">${date} ${time}</div>
1656
- </div>
1657
- <div class="message-body">
1658
- <div class="message-ids">
1659
- <div class="message-id">
1660
- <span class="message-id-label">Session:</span>
1661
- <span>${sessionId.substring(0, 8)}...</span>
1662
- </div>
1663
- <div class="message-id">
1664
- <span class="message-id-label">Correlation:</span>
1665
- <span>${correlationId.substring(0, 8)}...</span>
1666
- </div>
1667
- <div class="message-id">
1668
- <span class="message-id-label">Request:</span>
1669
- <span>${requestId.substring(0, 8)}...</span>
1670
- </div>
1671
- </div>
1672
- <div class="message-data">${JSON.stringify(message.message.data, null, 2)}</div>
1673
- </div>
1674
- </div>
1475
+ let rowsHtml = `
1476
+ <tr class="message-row ${isExpanded ? 'expanded' : ''}" data-position="${message.position}">
1477
+ <td class="message-cell expand-cell">
1478
+ <span class="expand-btn">${isExpanded ? '-' : '+'}</span>
1479
+ </td>
1480
+ <td class="message-cell type-cell">
1481
+ <span class="message-type ${message.messageType}">${message.messageType.toUpperCase()}</span>
1482
+ </td>
1483
+ <td class="message-cell name-cell" title="${message.message.type}">
1484
+ <span class="message-name">${message.message.type}</span>
1485
+ </td>
1486
+ <td class="message-cell id-cell" title="${requestId}">
1487
+ ${requestId.length > 35 ? requestId.substring(0, 35) + '...' : requestId}
1488
+ </td>
1489
+ <td class="message-cell id-cell" title="${correlationId}">
1490
+ ${correlationId.length > 35 ? correlationId.substring(0, 35) + '...' : correlationId}
1491
+ </td>
1492
+ <td class="message-cell id-cell" title="${sessionId}">
1493
+ ${sessionId.length > 35 ? sessionId.substring(0, 35) + '...' : sessionId}
1494
+ </td>
1495
+ <td class="message-cell position-cell">${message.position}</td>
1496
+ <td class="message-cell time-cell">${time}</td>
1497
+ </tr>
1675
1498
  `;
1499
+
1500
+ if (isExpanded) {
1501
+ const messageDataJson = JSON.stringify(message.message.data, null, 2);
1502
+ rowsHtml += `
1503
+ <tr class="message-details-row" style="background: #1a202c;">
1504
+ <td colspan="8" style="padding: 16px; border-top: 1px solid #4a5568;">
1505
+ <div style="display: flex; flex-direction: column; gap: 8px;">
1506
+ <span style="color: #a0aec0; font-size: 12px; font-weight: 600;">MESSAGE DATA</span>
1507
+ <pre style="background: #2d3748; color: #e2e8f0; padding: 16px; border-radius: 6px; font-family: 'SF Mono', Monaco, monospace; font-size: 12px; line-height: 1.4; overflow-x: auto; border: 1px solid #4a5568; margin: 0;">${messageDataJson}</pre>
1508
+ </div>
1509
+ </td>
1510
+ </tr>
1511
+ `;
1512
+ }
1513
+
1514
+ return rowsHtml;
1676
1515
  })
1677
1516
  .join('');
1678
1517
 
1679
- container.innerHTML = messagesHtml;
1518
+ // Always update to ensure expanded content shows properly
1519
+ tableBody.innerHTML = messagesHtml;
1520
+ autoScrollToBottom();
1680
1521
  }
1681
1522
 
1682
- function clearMessageFilters() {
1683
- document.getElementById('messageTypeFilter').value = '';
1684
- document.getElementById('sessionFilter').value = '';
1685
- document.getElementById('messageNameFilter').value = '';
1686
- document.getElementById('correlationIdFilter').value = '';
1687
- document.getElementById('jsonFilter').value = '';
1688
- applyMessageFilters();
1523
+ // Toggle message details (like CloudWatch)
1524
+ function toggleMessage(position) {
1525
+ console.log('toggleMessage called with position:', position, 'type:', typeof position);
1526
+ console.log('Current expandedMessages:', Array.from(expandedMessages));
1527
+
1528
+ // Ensure position is a string for consistency
1529
+ const positionStr = String(position);
1530
+
1531
+ if (expandedMessages.has(positionStr)) {
1532
+ expandedMessages.delete(positionStr);
1533
+ console.log('Collapsed message', positionStr);
1534
+ } else {
1535
+ expandedMessages.add(positionStr);
1536
+ console.log('Expanded message', positionStr);
1537
+ }
1538
+
1539
+ console.log('New expandedMessages:', Array.from(expandedMessages));
1540
+ displayMessages();
1541
+ }
1542
+
1543
+ // Handle click events on table for expansion
1544
+ document.addEventListener('click', (e) => {
1545
+ // Check if click is on expand button or expand cell (but not resize handle)
1546
+ const expandBtn = e.target.closest('.expand-btn');
1547
+ const expandCell = e.target.closest('.expand-cell');
1548
+ const resizeHandle = e.target.closest('.resize-handle');
1549
+
1550
+ if ((expandBtn || expandCell) && !resizeHandle) {
1551
+ const row = e.target.closest('tr[data-position]');
1552
+ if (row) {
1553
+ const position = row.getAttribute('data-position');
1554
+ if (position) {
1555
+ e.stopPropagation();
1556
+ e.preventDefault();
1557
+ console.log('Toggling message position:', position);
1558
+ toggleMessage(position);
1559
+ }
1560
+ }
1561
+ }
1562
+ });
1563
+
1564
+ // Column resizing functionality
1565
+ function initColumnResize() {
1566
+ const table = document.querySelector('.messages-table');
1567
+ if (!table) return;
1568
+
1569
+ // Mouse down on resize handle
1570
+ document.addEventListener('mousedown', (e) => {
1571
+ if (!e.target.classList.contains('resize-handle')) return;
1572
+
1573
+ e.preventDefault();
1574
+ isResizing = true;
1575
+
1576
+ const headerCell = e.target.parentElement;
1577
+ const column = headerCell.dataset.column;
1578
+
1579
+ currentColumn = column;
1580
+ startX = e.clientX;
1581
+ startWidth = parseInt(getComputedStyle(headerCell).width);
1582
+
1583
+ document.body.classList.add('resizing');
1584
+ table.classList.add('resizing');
1585
+ });
1586
+
1587
+ // Mouse move during resize
1588
+ document.addEventListener('mousemove', (e) => {
1589
+ if (!isResizing || !currentColumn) return;
1590
+
1591
+ const deltaX = e.clientX - startX;
1592
+ const newWidth = Math.max(30, startWidth + deltaX);
1593
+
1594
+ columnWidths[currentColumn] = newWidth;
1595
+ updateColumnWidths();
1596
+ });
1597
+
1598
+ // Mouse up to end resize
1599
+ document.addEventListener('mouseup', () => {
1600
+ if (isResizing) {
1601
+ isResizing = false;
1602
+ currentColumn = null;
1603
+
1604
+ document.body.classList.remove('resizing');
1605
+ const table = document.querySelector('.messages-table');
1606
+ if (table) table.classList.remove('resizing');
1607
+
1608
+ // Save column widths to localStorage
1609
+ localStorage.setItem('columnWidths', JSON.stringify(columnWidths));
1610
+ }
1611
+ });
1689
1612
  }
1690
1613
 
1691
- // Event search removed - no longer needed
1692
- // document.getElementById('eventSearch').addEventListener('input', displayEvents);
1614
+ // Update column widths in the table
1615
+ function updateColumnWidths() {
1616
+ const headers = document.querySelectorAll('.messages-header-cell');
1617
+ headers.forEach((cell, index) => {
1618
+ const column = cell.dataset.column;
1619
+ if (column && columnWidths[column]) {
1620
+ cell.style.width = columnWidths[column] + 'px';
1621
+ cell.style.minWidth = columnWidths[column] + 'px';
1622
+ cell.style.maxWidth = columnWidths[column] + 'px';
1623
+ }
1624
+ });
1625
+
1626
+ // Apply to colgroup if it exists
1627
+ const colgroup = document.querySelector('.messages-table colgroup');
1628
+ if (colgroup) {
1629
+ const cols = colgroup.querySelectorAll('col');
1630
+ headers.forEach((cell, index) => {
1631
+ const column = cell.dataset.column;
1632
+ if (column && columnWidths[column] && cols[index]) {
1633
+ cols[index].style.width = columnWidths[column] + 'px';
1634
+ }
1635
+ });
1636
+ }
1637
+ }
1638
+
1639
+ // Load saved column widths
1640
+ function loadColumnWidths() {
1641
+ const saved = localStorage.getItem('columnWidths');
1642
+ if (saved) {
1643
+ try {
1644
+ const parsed = JSON.parse(saved);
1645
+ columnWidths = { ...defaultColumnWidths, ...parsed };
1646
+ } catch (e) {
1647
+ columnWidths = { ...defaultColumnWidths };
1648
+ }
1649
+ }
1650
+ }
1651
+
1652
+ // Copy message data to request body field
1653
+ function copyMessageData(position, messageData) {
1654
+ try {
1655
+ const requestBodyField = document.getElementById('requestBody');
1656
+ if (requestBodyField) {
1657
+ // Parse and re-stringify to ensure proper formatting
1658
+ const parsed = JSON.parse(messageData);
1659
+ requestBodyField.value = JSON.stringify(parsed, null, 2);
1660
+
1661
+ // Visual feedback
1662
+ const copyBtn = window.event ? window.event.target : null;
1663
+ if (copyBtn) {
1664
+ const originalText = copyBtn.textContent;
1665
+ copyBtn.textContent = 'Copied!';
1666
+ copyBtn.style.background = 'var(--success)';
1667
+ copyBtn.style.color = 'white';
1668
+
1669
+ setTimeout(() => {
1670
+ copyBtn.textContent = originalText;
1671
+ copyBtn.style.background = '';
1672
+ copyBtn.style.color = '';
1673
+ }, 1500);
1674
+ }
1675
+ }
1676
+ } catch (error) {
1677
+ console.error('Failed to copy message data:', error);
1678
+
1679
+ // Error feedback
1680
+ const copyBtn = window.event ? window.event.target : null;
1681
+ if (copyBtn) {
1682
+ const originalText = copyBtn.textContent;
1683
+ copyBtn.textContent = 'Error';
1684
+ copyBtn.style.background = 'var(--error)';
1685
+ copyBtn.style.color = 'white';
1686
+
1687
+ setTimeout(() => {
1688
+ copyBtn.textContent = originalText;
1689
+ copyBtn.style.background = '';
1690
+ copyBtn.style.color = '';
1691
+ }, 1500);
1692
+ }
1693
+ }
1694
+ }
1693
1695
 
1694
- // Legacy function - no longer needed with new message system
1695
- async function clearEvents() {
1696
- console.log('clearEvents is deprecated - use clearMessageFilters instead');
1696
+ // Auto-scroll to bottom when new messages arrive
1697
+ let lastMessageCount = 0;
1698
+ function autoScrollToBottom() {
1699
+ if (currentMessages.length > lastMessageCount) {
1700
+ const messagesContainer = document.querySelector('.messages-list');
1701
+ if (messagesContainer) {
1702
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
1703
+ }
1704
+ lastMessageCount = currentMessages.length;
1705
+ }
1697
1706
  }
1698
1707
 
1708
+ // Clear filters
1709
+ function clearFilters() {
1710
+ document.getElementById('messageTypeFilter').value = '';
1711
+ document.getElementById('sessionFilter').value = '';
1712
+ document.getElementById('messageNameFilter').value = '';
1713
+ document.getElementById('correlationIdFilter').value = '';
1714
+ applyFilters();
1715
+ }
1699
1716
 
1700
- // Keyboard shortcut for send
1717
+ // Keyboard shortcuts
1701
1718
  document.addEventListener('keydown', (e) => {
1702
1719
  if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
1703
1720
  sendRequest();
@@ -1709,19 +1726,16 @@
1709
1726
 
1710
1727
  ws.onopen = () => {
1711
1728
  console.log('WebSocket connected');
1712
- // WebSocket status elements removed from UI
1713
1729
  };
1714
1730
 
1715
1731
  ws.onmessage = (event) => {
1716
1732
  const message = JSON.parse(event.data);
1717
1733
  if (message.type === 'event' || message.type === 'state') {
1718
- // Reload state when events occur
1719
- loadState();
1720
- // Also reload messages
1734
+ // Reload messages when events occur
1721
1735
  loadMessages();
1722
1736
  } else if (message.type === 'commandError') {
1723
1737
  // Show command error in response
1724
- document.getElementById('responseContent').textContent = JSON.stringify({
1738
+ document.getElementById('responsePreview').textContent = JSON.stringify({
1725
1739
  status: 'error',
1726
1740
  commandId: message.commandId,
1727
1741
  error: message.error,
@@ -1735,25 +1749,25 @@
1735
1749
 
1736
1750
  ws.onerror = (error) => {
1737
1751
  console.error('WebSocket error:', error);
1738
- // WebSocket status elements removed from UI
1739
1752
  };
1740
1753
 
1741
1754
  ws.onclose = () => {
1742
1755
  console.log('WebSocket disconnected');
1743
- // WebSocket status elements removed from UI
1744
1756
  };
1745
1757
 
1746
1758
  // Initial load
1759
+ loadColumnWidths();
1747
1760
  loadRegistry();
1748
- loadState();
1749
1761
  loadMessages();
1750
1762
 
1751
- // Initial command will be restored when registry loads
1763
+ // Initialize column resize functionality
1764
+ setTimeout(() => {
1765
+ initColumnResize();
1766
+ updateColumnWidths();
1767
+ }, 100);
1752
1768
 
1753
- // Refresh registry every 5 seconds
1769
+ // Auto-refresh
1754
1770
  setInterval(loadRegistry, 5000);
1755
-
1756
- // Auto-refresh messages every 2 seconds
1757
1771
  setInterval(loadMessages, 2000);
1758
1772
  </script>
1759
1773
  </body>