scipious commited on
Commit
3158cae
·
verified ·
1 Parent(s): b47fa88

Upload chat.html

Browse files
Files changed (1) hide show
  1. templates/chat.html +420 -563
templates/chat.html CHANGED
@@ -298,729 +298,582 @@
298
  .regulation-item {
299
  padding: 12px;
300
  margin: 8px 0;
301
- background: var(--bg-quaternary);
 
302
  border-radius: var(--radius);
303
- cursor: pointer;
304
- transition: all 0.2s ease;
305
- border: 2px solid transparent;
306
- user-select: none;
 
 
 
 
307
  }
308
- .regulation-item:hover {
309
- background: #3a3a3a;
310
- border-color: var(--primary-color);
 
311
  }
312
- .regulation-item.selected {
313
- background: var(--primary-color);
314
- color: white;
315
- border-color: var(--primary-hover);
316
  }
317
 
318
- /* ===== 상세 법규 리스트 ===== */
319
  .regulation-details-container {
320
- flex: 1;
321
- display: flex;
322
- flex-direction: column;
323
- overflow: hidden;
324
- }
325
- .regulation-details-header {
326
- display: flex;
327
- justify-content: space-between;
328
- align-items: center;
329
- padding: 16px 24px;
330
- border-bottom: 1px solid var(--border-color);
331
  }
332
 
333
- .btn {
334
- padding: 8px 16px;
335
- border: none;
336
- border-radius: var(--radius);
337
- background: var(--primary-color);
338
- color: white;
339
- cursor: pointer;
340
- font-size: 13px;
341
- font-weight: 600;
342
- transition: all 0.2s ease;
343
  }
344
 
345
- .btn:hover {
346
- background: var(--primary-hover);
347
- transform: translateY(-1px);
 
348
  }
349
 
350
- .btn-small {
351
- padding: 6px 12px;
352
  font-size: 12px;
 
 
 
 
353
  }
354
 
355
- .btn-load {
356
- background: var(--success-color);
 
 
 
 
 
 
 
 
 
 
 
 
357
  }
358
 
359
- .btn-load:hover {
360
- background: #45a049;
 
361
  }
362
 
363
- .btn-secondary {
364
- background: var(--bg-quaternary);
 
365
  }
366
 
367
- .btn-secondary:hover {
368
- background: #555;
 
369
  }
370
 
371
- .regulation-search {
372
- padding: 12px 24px;
373
- background: var(--bg-tertiary);
374
- border-bottom: 1px solid var(--border-color);
375
- }
376
- .regulation-search-input {
377
- width: 100%;
378
- padding: 8px 12px;
379
- border: 1px solid var(--border-color);
380
- border-radius: var(--radius);
381
- background: var(--bg-quaternary);
382
- color: var(--text-primary);
383
- font-size: 13px;
384
- transition: border-color 0.2s ease;
385
  }
386
- .regulation-search-input:focus {
387
- outline: none;
388
- border-color: var(--primary-color);
389
  }
390
- .regulation-search-input::placeholder {
391
- color: var(--text-muted);
 
 
392
  }
393
- .regulation-details-list {
394
- flex: 1;
395
- overflow-y: auto;
396
- padding: 12px 24px;
397
  }
398
- .regulation-detail-item {
399
- padding: 10px 12px;
400
- margin: 6px 0;
401
- background: var(--bg-quaternary);
402
- border-radius: var(--radius);
403
- cursor: pointer;
404
- transition: all 0.2s ease;
405
- border: 2px solid transparent;
406
- user-select: none;
407
- font-size: 13px;
408
- line-height: 1.4;
409
  }
410
- .regulation-detail-item:hover {
411
- background: #3a3a3a;
412
- border-color: var(--primary-color);
413
  }
414
- .regulation-detail-item.selected {
415
- background: var(--primary-color);
416
- color: white;
417
- border-color: var(--primary-hover);
 
 
 
 
418
  }
419
- .regulation-detail-item.hidden {
420
- display: none;
 
 
 
 
 
 
421
  }
422
 
423
- .action-buttons {
424
  display: flex;
425
  gap: 12px;
426
- padding: 16px 24px;
427
- border-top: 1px solid var(--border-color);
428
- background: var(--bg-secondary);
429
  }
430
 
431
- /* ===== 채팅 패널 ===== */
432
- .chat-panel {
433
- flex: 1;
434
- display: flex;
435
- flex-direction: column;
436
- background: var(--bg-primary);
437
  }
438
- .chat-header {
439
- padding: 20px 24px;
440
- background: var(--bg-secondary);
441
- border-bottom: 1px solid var(--border-color);
442
  }
443
- .chat-title {
444
- font-size: 18px;
445
- font-weight: 600;
446
- color: var(--text-primary);
447
- margin: 0;
448
- display: flex;
449
- align-items: baseline;
450
- gap: 10px;
 
 
 
 
451
  }
452
- .title-subtitle {
453
- font-size: 14px;
454
- font-weight: 400;
455
- color: var(--text-muted);
456
  }
457
 
458
- .status-text {
459
- margin-top: 8px;
460
- padding: 8px 12px;
461
- background: var(--bg-tertiary);
462
  border-radius: var(--radius);
463
- font-size: 12px;
464
- color: var(--primary-color);
 
 
 
465
  }
466
 
467
- .chat-messages {
468
- flex: 1;
469
- overflow-y: auto;
470
- padding: 24px;
471
- display: flex;
472
- flex-direction: column;
473
- gap: 16px;
474
  }
475
 
476
- /* 메시지 버블 */
477
- .message {
478
- display: flex;
479
- gap: 12px;
480
- max-width: 80%;
481
- animation: slideIn 0.3s ease;
482
- }
483
- @keyframes slideIn {
484
- from { opacity: 0; transform: translateY(10px); }
485
- to { opacity: 1; transform: translateY(0); }
486
- }
487
- .message.user {
488
- align-self: flex-end;
489
- flex-direction: row-reverse;
490
- }
491
- .message.assistant {
492
- align-self: flex-start;
493
- }
494
- .message-avatar {
495
- width: 36px;
496
- height: 36px;
497
- border-radius: 50%;
498
- background: var(--primary-color);
499
- display: flex;
500
- align-items: center;
501
- justify-content: center;
502
- color: white;
503
- font-weight: 700;
504
- font-size: 14px;
505
- flex-shrink: 0;
506
- }
507
- .message.user .message-avatar {
508
- background: var(--success-color);
509
- }
510
- .message-content {
511
- background: var(--bg-secondary);
512
- padding: 12px 16px;
513
- border-radius: 16px;
514
- color: var(--text-primary);
515
- line-height: 1.5;
516
- word-wrap: break-word;
517
- }
518
- .message.user .message-content {
519
- background: var(--primary-color);
520
- color: white;
521
  }
522
- .message-time {
523
- font-size: 11px;
524
- color: var(--text-muted);
525
- margin-top: 4px;
526
  }
527
 
528
- /* 메시지 콘텐츠 내 HTML 스타일링 */
529
- .message-content h1,
530
- .message-content h2 {
531
- color: var(--primary-color);
532
- margin: 12px 0 8px 0;
533
- font-weight: 600;
 
534
  }
535
 
536
- .message-content h1 { font-size: 18px; }
537
- .message-content h2 {
538
- font-size: 16px;
539
- border-bottom: 1px solid var(--border-color);
540
- padding-bottom: 4px;
 
541
  }
542
 
543
- .message-content ul,
544
- .message-content ol {
545
- margin: 8px 0;
546
- padding-left: 20px;
 
 
547
  }
548
 
549
- .message-content li {
550
- margin: 4px 0;
551
- line-height: 1.4;
 
 
 
 
552
  }
553
 
554
- .message-content strong {
555
- color: var(--primary-color);
 
 
556
  }
557
 
558
- .message-content p {
559
- margin: 8px 0;
 
 
 
 
 
 
 
560
  line-height: 1.5;
 
 
 
561
  }
562
 
563
- .message.user .message-content h1,
564
- .message.user .message-content h2,
565
- .message.user .message-content strong {
566
- color: white;
567
  }
568
 
569
- /* 채팅 입력 영역 */
570
- .chat-input-container {
571
- padding: 20px 24px;
572
- background: var(--bg-secondary);
573
- border-top: 1px solid var(--border-color);
574
- }
575
- .chat-input-wrapper {
576
- display: flex;
577
- gap: 12px;
578
- align-items: flex-end;
579
- }
580
- .chat-input {
581
- flex: 1;
582
- padding: 12px 16px;
583
- background: var(--bg-tertiary);
584
- border: 2px solid var(--border-color);
585
- border-radius: 24px;
586
- color: var(--text-primary);
587
- font-size: 14px;
588
- resize: none;
589
- max-height: 120px;
590
- min-height: 44px;
591
- font-family: inherit;
592
- transition: border-color 0.2s ease;
593
- }
594
- .chat-input:focus {
595
- outline: none;
596
- border-color: var(--primary-color);
597
- }
598
- .chat-input::placeholder {
599
- color: var(--text-muted);
600
- }
601
- .send-btn {
602
- width: 44px;
603
- height: 44px;
604
- border-radius: 50%;
605
- background: var(--primary-color);
606
- border: none;
607
- color: white;
608
- cursor: pointer;
609
- display: flex;
610
- align-items: center;
611
- justify-content: center;
612
- transition: all 0.2s ease;
613
- flex-shrink: 0;
614
  }
615
- .send-btn:hover {
616
- background: var(--primary-hover);
617
- transform: scale(1.05);
 
 
 
 
 
 
 
 
618
  }
619
- .send-btn:active {
620
- transform: scale(0.95);
 
 
621
  }
622
- .send-btn:disabled {
623
- background: var(--bg-quaternary);
624
- cursor: not-allowed;
625
- transform: none;
 
626
  }
627
 
628
- /* 로딩 인디케이터 */
629
- .typing-indicator {
630
- display: flex;
631
- gap: 4px;
632
- padding: 12px 16px;
633
- background: var(--bg-secondary);
634
- border-radius: 16px;
635
- width: fit-content;
 
 
 
 
 
 
636
  }
637
- .typing-dot {
638
- width: 8px;
639
- height: 8px;
640
- border-radius: 50%;
641
- background: var(--text-muted);
642
- animation: typing 1.4s infinite;
643
  }
644
- .typing-dot:nth-child(2) { animation-delay: 0.2s; }
645
- .typing-dot:nth-child(3) { animation-delay: 0.4s; }
646
 
647
- @keyframes typing {
648
- 0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
649
- 30% { opacity: 1; transform: translateY(-10px); }
 
 
650
  }
651
 
652
- /* 스크롤바 스타일 */
653
- .chat-messages::-webkit-scrollbar,
654
- .filter-panel::-webkit-scrollbar,
655
- .regulation-list-container::-webkit-scrollbar,
656
- .regulation-details-list::-webkit-scrollbar {
657
- width: 8px;
 
 
658
  }
659
 
660
- .chat-messages::-webkit-scrollbar-track,
661
- .filter-panel::-webkit-scrollbar-track,
662
- .regulation-list-container::-webkit-scrollbar-track,
663
- .regulation-details-list::-webkit-scrollbar-track {
 
664
  background: var(--bg-tertiary);
 
665
  }
666
 
667
- .chat-messages::-webkit-scrollbar-thumb,
668
- .filter-panel::-webkit-scrollbar-thumb,
669
- .regulation-list-container::-webkit-scrollbar-thumb,
670
- .regulation-details-list::-webkit-scrollbar-thumb {
671
- background: var(--bg-quaternary);
672
- border-radius: 4px;
 
 
673
  }
674
 
675
- .chat-messages::-webkit-scrollbar-thumb:hover,
676
- .filter-panel::-webkit-scrollbar-thumb:hover,
677
- .regulation-list-container::-webkit-scrollbar-thumb:hover,
678
- .regulation-details-list::-webkit-scrollbar-thumb:hover {
679
- background: #555;
680
  }
681
  </style>
682
  </head>
683
-
684
  <body>
685
  <header class="header">
686
  <div class="logo">LexiMind</div>
687
- <nav>
688
- <ul class="nav">
689
- <li><a href="#">법규 검색</a></li>
690
- <li><a href="#">체크리스트 생성</a></li>
691
- <li><a href="#">유권해석</a></li>
692
- <li><a href="#">사용문의</a></li>
693
- </ul>
694
- </nav>
695
  </header>
696
 
697
- <!-- 햄버거 버튼 -->
698
- <button class="sidebar-toggle" id="sidebarToggle">
699
- <span class="hamburger"></span>
700
- </button>
701
-
702
- <!-- 새 페이지 버튼 -->
703
- <button class="new-page-btn" id="newPageBtn">
704
- <span class="plus-icon">+ 새 대화 생성</span>
705
- </button>
706
-
707
- <!-- 사이드바 오버레이 -->
708
- <div class="sidebar-overlay" id="sidebarOverlay"></div>
709
 
710
  <!-- 사이드바 -->
711
- <nav class="sidebar" id="sidebar">
712
  <div class="sidebar-content">
713
- <h3>과거 이력</h3>
714
- <div class="date-ranges">
715
- <a href="/history/202511" class="date-range-item">
716
- <span class="date-period">2025.11.01 ~ 2025.11.30</span>
717
- <span class="date-count">검색 15건</span>
718
- </a>
719
- <a href="/history/202510" class="date-range-item">
720
- <span class="date-period">2025.10.01 ~ 2025.10.31</span>
721
- <span class="date-count">검색 8건</span>
722
- </a>
723
- <a href="/history/202509" class="date-range-item">
724
- <span class="date-period">2025.09.01 ~ 2025.09.30</span>
725
- <span class="date-count">검색 12건</span>
726
- </a>
727
  </div>
728
  </div>
729
- </nav>
730
 
731
- <main class="main">
 
 
 
 
 
 
 
732
  <!-- 필터 패널 -->
733
- <aside class="filter-panel">
734
  <div class="filter-header">
735
- <h2 class="filter-title">검색 필터</h2>
736
  <div class="toggle-group">
737
- <span class="toggle-label">지역</span>
738
- <div class="toggle-options">
739
- <button class="toggle-btn region-toggle" data-value="국내">국내</button>
740
- <button class="toggle-btn region-toggle" data-value="북미">북미</button>
741
- <button class="toggle-btn region-toggle" data-value="유럽">유럽</button>
742
- <button class="toggle-btn region-toggle active" data-value="전체">전체</button>
743
  </div>
744
  </div>
745
  </div>
746
 
 
 
 
 
 
 
 
 
 
 
 
 
747
  <!-- 상세 법규 리스트 -->
748
  <div class="regulation-details-container">
749
  <div class="regulation-details-header">
750
- <span class="toggle-label">상세 법규 리스트</span>
751
- <button class="btn btn-load btn-small" onclick="loadDetailedRegulationList()">리스트 불러오기</button>
752
  </div>
753
- <div class="regulation-search" id="regulationSearchContainer" style="display: none;">
754
- <input type="text" id="regulationSearchInput" class="regulation-search-input" placeholder="법규 검색...">
755
  </div>
756
- <div class="regulation-details-list" id="regulationDetailsList">
757
- <p style="color: var(--text-muted); padding: 20px; text-align: center; margin: 0;">
758
- 리스트 불러오기를 눌러 상세 법규를 확인하세요.
759
- </p>
760
  </div>
761
  </div>
 
762
 
763
- <div class="action-buttons">
764
- <button class="btn btn-secondary" onclick="clearAllSelections()">선택 초기화</button>
765
- </div>
766
- </aside>
767
-
768
- <!-- 채팅 패널 -->
769
- <section class="chat-panel">
770
- <div class="chat-header">
771
- <h1 class="chat-title">자동차 인증 법규 검색
772
- <span class="title-subtitle">&nbsp&nbsp관련 법규 내용을 검색으로 편리하게 찾아보세요! ※ 모든 결과물은 AI에 의해 생성된 것으로 오류가 있을 수 있습니다</span>
773
- </h1>
774
- <div id="statusText" class="status-text">시스템 준비 중...</div>
775
- </div>
776
-
777
  <div class="chat-messages" id="chatMessages">
778
- <div class="message assistant">
779
- <div class="message-avatar">Lexi</div>
780
- <div>
781
- <div class="message-content">
782
- 안녕하세요! 자동차 인증 법규 검색 도우미입니다.<br>
783
- 궁금하신 법규 내용을 검색해보세요.<br><br>
784
- (ex : ISA의 작동 요건을 알려주고, 표로 정리해줘)
785
- </div>
786
- <div class="message-time" id="welcomeTime"></div>
787
- </div>
788
- </div>
789
  </div>
790
-
791
- <!-- 채팅 입력 영역 -->
792
- <div class="chat-input-container">
793
- <div class="chat-input-wrapper">
794
- <textarea
795
- id="chatInput"
796
- class="chat-input"
797
- placeholder="검색하고 싶은 내용을 입력하세요..."
798
- rows="1"
799
- ></textarea>
800
- <button class="send-btn" id="sendBtn" title="전송">
801
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
802
- <path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z"/>
803
- </svg>
804
- </button>
805
  </div>
806
  </div>
807
- </section>
808
- </main>
809
-
810
- <script>
811
- // ===== 전역 변수 =====
812
- let selectedRegulations = [];
813
- let selectedRegions = [];
814
- let socket;
815
 
816
- // ===== 초기화 =====
817
- document.addEventListener('DOMContentLoaded', function() {
818
- initializeSocket();
819
- initializeSidebar();
820
- initializeToggles();
821
- initializeChat();
822
- initializeRegulationList();
823
- setWelcomeTime();
824
- updateSelectedCount();
825
- });
826
 
827
- // 환영 메시지 시간 설정
828
- function setWelcomeTime() {
829
- const timeElement = document.getElementById('welcomeTime');
830
- if (timeElement) {
831
- const now = new Date();
832
- timeElement.textContent = formatTime(now);
 
 
 
 
 
 
 
 
 
 
 
833
  }
834
- }
835
 
836
- // 시간 포맷팅
837
- function formatTime(date) {
838
- const hours = date.getHours().toString().padStart(2, '0');
839
- const minutes = date.getMinutes().toString().padStart(2, '0');
840
- return `${hours}:${minutes}`;
841
- }
842
 
843
- // ===== Socket.IO 초기화 =====
844
- function initializeSocket() {
845
- socket = io();
846
- socket.on('message', function(data) {
847
- document.getElementById('statusText').innerText = data.message;
848
- });
849
- }
850
 
851
- // ===== 사이드바 초기화 =====
852
- function initializeSidebar() {
853
- const sidebarToggle = document.getElementById('sidebarToggle');
854
- const sidebar = document.getElementById('sidebar');
855
- const sidebarOverlay = document.getElementById('sidebarOverlay');
856
- const newPageBtn = document.getElementById('newPageBtn');
857
 
858
- sidebarToggle.addEventListener('click', function() {
859
- const isOpen = sidebar.classList.contains('open');
860
- if (isOpen) {
861
- closeSidebar();
862
- } else {
863
- openSidebar();
864
- }
865
  });
866
 
867
- sidebarOverlay.addEventListener('click', closeSidebar);
868
-
869
- document.addEventListener('keydown', function(e) {
870
- if (e.key === 'Escape') {
871
- closeSidebar();
872
  }
873
  });
874
 
875
- if (newPageBtn) {
876
- newPageBtn.addEventListener('click', function() {
877
- window.open('/', '_blank');
878
- });
879
- }
880
- }
881
 
882
- function openSidebar() {
 
883
  const sidebar = document.getElementById('sidebar');
884
- const sidebarToggle = document.getElementById('sidebarToggle');
885
- const sidebarOverlay = document.getElementById('sidebarOverlay');
886
- const newPageBtn = document.getElementById('newPageBtn');
887
 
888
- sidebar.classList.add('open');
889
- sidebarToggle.classList.add('active');
890
- sidebarOverlay.classList.add('active');
891
- if (newPageBtn) newPageBtn.classList.add('show');
892
  }
893
 
894
- function closeSidebar() {
895
- const sidebar = document.getElementById('sidebar');
896
- const sidebarToggle = document.getElementById('sidebarToggle');
897
- const sidebarOverlay = document.getElementById('sidebarOverlay');
898
- const newPageBtn = document.getElementById('newPageBtn');
899
-
900
- sidebar.classList.remove('open');
901
- sidebarToggle.classList.remove('active');
902
- sidebarOverlay.classList.remove('active');
903
- if (newPageBtn) newPageBtn.classList.remove('show');
904
  }
905
 
906
- // ===== 토글 버튼 초기화 =====
907
- function initializeToggles() {
908
- document.querySelectorAll('.region-toggle').forEach(btn => {
909
- btn.addEventListener('click', function() {
910
- handleRegionToggle(this);
911
- });
 
 
 
 
 
 
912
  });
913
  }
914
 
915
- function handleRegionToggle(clickedBtn) {
916
- const value = clickedBtn.getAttribute('data-value');
917
- const allButtons = document.querySelectorAll('.region-toggle');
918
-
919
- if (value === '전체') {
920
- // 전체 선택 - 다른 모든 버튼 비활성화
921
- allButtons.forEach(btn => btn.classList.remove('active'));
922
- clickedBtn.classList.add('active');
923
- selectedRegions.length = 0;
924
- } else {
925
- // 개별 선택 - 전체 버튼 비활성화
926
- const allBtn = document.querySelector('.region-toggle[data-value="전체"]');
927
- if (allBtn) allBtn.classList.remove('active');
928
-
929
- clickedBtn.classList.toggle('active');
930
-
931
- if (clickedBtn.classList.contains('active')) {
932
- if (!selectedRegions.includes(value)) {
933
- selectedRegions.push(value);
934
- }
935
- } else {
936
- selectedRegions = selectedRegions.filter(region => region !== value);
937
- }
938
-
939
- // 아무것도 선택되지 않았으면 전체 활성화
940
- if (selectedRegions.length === 0 && allBtn) {
941
- allBtn.classList.add('active');
942
- }
943
- }
944
-
945
- console.log('선택된 지역:', selectedRegions);
946
  }
947
 
948
  function getSelectedRegions() {
949
- return selectedRegions.length > 0 ? selectedRegions : [];
950
  }
951
 
952
- // ===== 법규 리스트 초기화 =====
953
- function initializeRegulationList() {
 
954
  const regulationListDiv = document.getElementById('regulationList');
955
- regulationListDiv.addEventListener('click', function(e) {
956
- const item = e.target.closest('.regulation-item');
957
- if (item) {
958
- toggleRegulationSelection(item);
959
- }
 
 
 
 
 
960
  });
961
  }
962
 
963
- function toggleRegulationSelection(element) {
964
- const id = element.getAttribute('data-id');
965
- if (selectedRegulations.includes(id)) {
966
- selectedRegulations = selectedRegulations.filter(item => item !== id);
967
- element.classList.remove('selected');
968
- } else {
969
  selectedRegulations.push(id);
970
- element.classList.add('selected');
 
971
  }
 
972
  updateSelectedCount();
973
  }
974
 
975
  function updateSelectedCount() {
976
- const countElement = document.getElementById('selectedCount');
977
- if (countElement) {
978
- countElement.textContent = `선택됨: ${selectedRegulations.length}`;
979
- }
980
- }
981
-
982
- // ===== 채팅 초기화 =====
983
- function initializeChat() {
984
- const chatInput = document.getElementById('chatInput');
985
- const sendBtn = document.getElementById('sendBtn');
986
-
987
- // 자동 높이 조절
988
- chatInput.addEventListener('input', function() {
989
- this.style.height = 'auto';
990
- this.style.height = Math.min(this.scrollHeight, 120) + 'px';
991
- });
992
-
993
- // Enter 키로 전송
994
- chatInput.addEventListener('keydown', function(e) {
995
- if (e.key === 'Enter' && !e.shiftKey) {
996
- e.preventDefault();
997
- sendMessage();
998
- }
999
- });
1000
-
1001
- // 전송 버튼 클릭
1002
- sendBtn.addEventListener('click', sendMessage);
1003
  }
1004
 
1005
  // ===== 메시지 전송 =====
1006
  function sendMessage() {
1007
- const chatInput = document.getElementById('chatInput');
1008
- const message = chatInput.value.trim();
1009
- if (!message) return;
1010
-
1011
- // 사용자 메시지 추가
1012
- addMessage(message, 'user');
1013
 
1014
- // 입력창 초기화
1015
- chatInput.value = '';
1016
- chatInput.style.height = 'auto';
1017
 
1018
- // 로딩 표시
1019
  showTypingIndicator();
1020
 
1021
- // 서버에 요청
1022
  const requestData = {
1023
- query: message,
1024
  regions: getSelectedRegions(),
1025
  selectedRegulations: selectedRegulations.map(id => {
1026
  const element = document.querySelector(`[data-id="${id}"]`);
@@ -1091,6 +944,10 @@
1091
  return textarea.value;
1092
  }
1093
 
 
 
 
 
1094
  function showTypingIndicator() {
1095
  const chatMessages = document.getElementById('chatMessages');
1096
  const indicator = document.createElement('div');
 
298
  .regulation-item {
299
  padding: 12px;
300
  margin: 8px 0;
301
+ background: var(--bg-quaternary);
302
+ border: 2px solid transparent;
303
  border-radius: var(--radius);
304
+ cursor: pointer;
305
+ transition: all 0.3s ease;
306
+ font-size: 14px;
307
+ color: var(--text-primary);
308
+ position: relative;
309
+ display: flex;
310
+ align-items: center;
311
+ gap: 8px;
312
  }
313
+
314
+ .regulation-item.selected {
315
+ border-color: var(--primary-color);
316
+ background: rgba(0, 102, 255, 0.1);
317
  }
318
+
319
+ .regulation-item:hover {
320
+ transform: translateY(-2px);
321
+ background: #3a3a3a;
322
  }
323
 
324
+ /* 상세 법규 리스트 스타일 */
325
  .regulation-details-container {
326
+ padding: 24px;
327
+ flex: 1;
328
+ overflow-y: auto;
 
 
 
 
 
 
 
 
329
  }
330
 
331
+ .regulation-details-header {
332
+ display: flex;
333
+ justify-content: space-between;
334
+ align-items: center;
335
+ margin-bottom: 20px;
 
 
 
 
 
336
  }
337
 
338
+ .details-title {
339
+ font-size: 16px;
340
+ font-weight: 600;
341
+ color: var(--text-primary);
342
  }
343
 
344
+ .selected-details-count {
 
345
  font-size: 12px;
346
+ color: var(--primary-color);
347
+ background: rgba(77, 168, 255, 0.1);
348
+ padding: 4px 8px;
349
+ border-radius: 4px;
350
  }
351
 
352
+ .regulation-detail-item {
353
+ padding: 12px;
354
+ margin: 8px 0;
355
+ background: var(--bg-quaternary);
356
+ border: 2px solid transparent;
357
+ border-radius: var(--radius);
358
+ cursor: pointer;
359
+ transition: all 0.3s ease;
360
+ font-size: 14px;
361
+ color: var(--text-primary);
362
+ display: flex;
363
+ align-items: center;
364
+ gap: 8px;
365
+ position: relative;
366
  }
367
 
368
+ .regulation-detail-item.selected {
369
+ border-color: var(--primary-color);
370
+ background: rgba(0, 102, 255, 0.1);
371
  }
372
 
373
+ .regulation-detail-item:hover {
374
+ transform: translateY(-2px);
375
+ background: #3a3a3a;
376
  }
377
 
378
+ .regulation-search-container {
379
+ display: none;
380
+ margin-bottom: 16px;
381
  }
382
 
383
+ .regulation-search-input {
384
+ width: 100%;
385
+ padding: 12px;
386
+ background: var(--bg-tertiary);
387
+ border: 1px solid var(--border-color);
388
+ border-radius: var(--radius);
389
+ color: var(--text-primary);
390
+ font-size: 14px;
 
 
 
 
 
 
391
  }
392
+
393
+ .regulation-search-input::placeholder {
394
+ color: var(--text-muted);
395
  }
396
+
397
+ .regulation-search-input:focus {
398
+ border-color: var(--primary-color);
399
+ outline: none;
400
  }
401
+
402
+ .regulation-detail-item.hidden {
403
+ display: none;
 
404
  }
405
+
406
+ .clear-selection {
407
+ background: transparent;
408
+ border: none;
409
+ color: var(--text-muted);
410
+ cursor: pointer;
411
+ font-size: 12px;
412
+ padding: 4px 8px;
413
+ transition: color 0.2s ease;
 
 
414
  }
415
+
416
+ .clear-selection:hover {
417
+ color: #ff6b6b;
418
  }
419
+
420
+ /* ===== 채팅 영역 ===== */
421
+ .chat-area {
422
+ flex: 1;
423
+ display: flex;
424
+ flex-direction: column;
425
+ background: var(--bg-primary);
426
+ padding: 0;
427
  }
428
+
429
+ .chat-messages {
430
+ flex: 1;
431
+ overflow-y: auto;
432
+ padding: 24px;
433
+ display: flex;
434
+ flex-direction: column;
435
+ gap: 24px;
436
  }
437
 
438
+ .message {
439
  display: flex;
440
  gap: 12px;
441
+ max-width: 85%;
 
 
442
  }
443
 
444
+ .message.user {
445
+ margin-left: auto;
446
+ flex-direction: row-reverse;
 
 
 
447
  }
448
+
449
+ .message.assistant {
450
+ margin-right: auto;
 
451
  }
452
+
453
+ .message-avatar {
454
+ width: 40px;
455
+ height: 40px;
456
+ border-radius: 50%;
457
+ background: var(--primary-color);
458
+ display: flex;
459
+ align-items: center;
460
+ justify-content: center;
461
+ font-weight: 600;
462
+ color: white;
463
+ flex-shrink: 0;
464
  }
465
+
466
+ .message.user .message-avatar {
467
+ background: #4caf50;
 
468
  }
469
 
470
+ .message-content {
471
+ padding: 16px;
 
 
472
  border-radius: var(--radius);
473
+ background: var(--bg-tertiary);
474
+ font-size: 15px;
475
+ line-height: 1.6;
476
+ word-break: break-word;
477
+ white-space: pre-wrap;
478
  }
479
 
480
+ .message.user .message-content {
481
+ background: var(--primary-color);
482
+ color: white;
 
 
 
 
483
  }
484
 
485
+ .message-time {
486
+ font-size: 12px;
487
+ color: var(--text-muted);
488
+ margin-top: 4px;
489
+ text-align: right;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490
  }
491
+
492
+ .message.user .message-time {
493
+ text-align: left;
 
494
  }
495
 
496
+ .typing-indicator {
497
+ display: flex;
498
+ gap: 8px;
499
+ padding: 16px;
500
+ background: var(--bg-tertiary);
501
+ border-radius: var(--radius);
502
+ width: fit-content;
503
  }
504
 
505
+ .typing-dot {
506
+ width: 8px;
507
+ height: 8px;
508
+ background: var(--text-muted);
509
+ border-radius: 50%;
510
+ animation: typing 1.4s infinite ease-in-out;
511
  }
512
 
513
+ .typing-dot:nth-child(2) { animation-delay: 0.2s; }
514
+ .typing-dot:nth-child(3) { animation-delay: 0.4s; }
515
+
516
+ @keyframes typing {
517
+ 0%, 80%, 100% { transform: translateY(0); }
518
+ 40% { transform: translateY(-4px); }
519
  }
520
 
521
+ /* ===== 입력 영역 ===== */
522
+ .input-area {
523
+ padding: 24px;
524
+ background: var(--bg-secondary);
525
+ border-top: 1px solid var(--border-color);
526
+ position: sticky;
527
+ bottom: 0;
528
  }
529
 
530
+ .input-wrapper {
531
+ display: flex;
532
+ gap: 12px;
533
+ align-items: center;
534
  }
535
 
536
+ #userInput {
537
+ flex: 1;
538
+ padding: 16px 20px;
539
+ background: var(--bg-tertiary);
540
+ border: 1px solid var(--border-color);
541
+ border-radius: var(--radius);
542
+ color: var(--text-primary);
543
+ font-size: 15px;
544
+ resize: none;
545
  line-height: 1.5;
546
+ min-height: 48px;
547
+ max-height: 200px;
548
+ overflow-y: auto;
549
  }
550
 
551
+ #userInput:focus {
552
+ border-color: var(--primary-color);
553
+ outline: none;
 
554
  }
555
 
556
+ #userInput::placeholder {
557
+ color: var(--text-muted);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
  }
559
+
560
+ #sendBtn {
561
+ padding: 16px 32px;
562
+ background: var(--primary-color);
563
+ border: none;
564
+ border-radius: var(--radius);
565
+ color: white;
566
+ font-weight: 600;
567
+ cursor: pointer;
568
+ transition: all 0.2s ease;
569
+ font-size: 15px;
570
  }
571
+
572
+ #sendBtn:hover {
573
+ background: var(--primary-hover);
574
+ transform: translateY(-2px);
575
  }
576
+
577
+ #sendBtn:disabled {
578
+ background: var(--text-muted);
579
+ cursor: not-allowed;
580
+ transform: none;
581
  }
582
 
583
+ /* ===== 로딩 인디케이터 (추가) ===== */
584
+ #loading {
585
+ position: fixed;
586
+ top: 0;
587
+ left: 0;
588
+ width: 100%;
589
+ height: 100%;
590
+ background: rgba(26, 26, 26, 0.9);
591
+ display: flex;
592
+ flex-direction: column;
593
+ align-items: center;
594
+ justify-content: center;
595
+ z-index: 2000;
596
+ transition: opacity 0.3s ease;
597
  }
598
+
599
+ #loading.hidden {
600
+ opacity: 0;
601
+ visibility: hidden;
 
 
602
  }
 
 
603
 
604
+ #loading p {
605
+ font-size: 24px;
606
+ font-weight: 600;
607
+ color: var(--text-primary);
608
+ margin-bottom: 24px;
609
  }
610
 
611
+ #status {
612
+ width: 60%;
613
+ max-height: 40vh;
614
+ overflow-y: auto;
615
+ background: var(--bg-secondary);
616
+ border-radius: var(--radius);
617
+ padding: 20px;
618
+ box-shadow: var(--shadow);
619
  }
620
 
621
+ #status p {
622
+ font-size: 16px;
623
+ color: var(--text-secondary);
624
+ margin: 8px 0;
625
+ padding: 8px;
626
  background: var(--bg-tertiary);
627
+ border-radius: 4px;
628
  }
629
 
630
+ .loading-spinner {
631
+ width: 50px;
632
+ height: 50px;
633
+ border: 5px solid var(--text-muted);
634
+ border-top: 5px solid var(--primary-color);
635
+ border-radius: 50%;
636
+ animation: spin 1s linear infinite;
637
+ margin-bottom: 24px;
638
  }
639
 
640
+ @keyframes spin {
641
+ 0% { transform: rotate(0deg); }
642
+ 100% { transform: rotate(360deg); }
 
 
643
  }
644
  </style>
645
  </head>
 
646
  <body>
647
  <header class="header">
648
  <div class="logo">LexiMind</div>
649
+ <ul class="nav">
650
+ <li><a href="#">홈</a></li>
651
+ <li><a href="#">설정</a></li>
652
+ <li><a href="#">도움말</a></li>
653
+ </ul>
 
 
 
654
  </header>
655
 
656
+ <!-- 사이드바 토글 버튼 -->
657
+ <div class="sidebar-toggle" onclick="toggleSidebar()">
658
+ <div class="hamburger"></div>
659
+ </div>
 
 
 
 
 
 
 
 
660
 
661
  <!-- 사이드바 -->
662
+ <div class="sidebar" id="sidebar">
663
  <div class="sidebar-content">
664
+ <h2 style="font-size: 20px; margin-bottom: 24px; color: var(--text-primary);">대화 히스토리</h2>
665
+ <div class="date-ranges" id="dateRanges">
666
+ <!-- 대화 히스토리 항목이 동적으로 추가됨 -->
 
 
 
 
 
 
 
 
 
 
 
667
  </div>
668
  </div>
669
+ </div>
670
 
671
+ <div class="sidebar-overlay" id="sidebarOverlay" onclick="toggleSidebar()"></div>
672
+
673
+ <!-- 새 페이지 버튼 -->
674
+ <button class="new-page-btn" onclick="createNewPage()">
675
+ <span class="plus-icon">+</span> 새 페이지
676
+ </button>
677
+
678
+ <div class="main">
679
  <!-- 필터 패널 -->
680
+ <div class="filter-panel">
681
  <div class="filter-header">
682
+ <h2 class="filter-title">지역 선택</h2>
683
  <div class="toggle-group">
684
+ <label class="toggle-label">검색 지역 선택</label>
685
+ <div class="toggle-options" id="regionToggles">
686
+ <!-- 토글 버튼 동적 생성 -->
 
 
 
687
  </div>
688
  </div>
689
  </div>
690
 
691
+ <!-- 법규 리스트 -->
692
+ <div class="regulation-list-container">
693
+ <div class="regulation-header">
694
+ <h3 style="font-size: 16px; font-weight: 600; color: var(--text-primary); margin: 0;">법규 리스트</h3>
695
+ <span class="selected-count" id="selectedCount">선택: 0</span>
696
+ </div>
697
+ <div class="regulation-list" id="regulationList">
698
+ <!-- 법규 항목 동적 추가 -->
699
+ </div>
700
+ <button class="clear-selection" onclick="clearAllSelections()">선택 해제</button>
701
+ </div>
702
+
703
  <!-- 상세 법규 리스트 -->
704
  <div class="regulation-details-container">
705
  <div class="regulation-details-header">
706
+ <h3 class="details-title">상세 법규 리스트</h3>
707
+ <span class="selected-details-count" id="selectedDetailsCount">선택: 0</span>
708
  </div>
709
+ <div class="regulation-search-container" id="regulationSearchContainer">
710
+ <input type="text" class="regulation-search-input" id="regulationSearchInput" placeholder="법규 검색...">
711
  </div>
712
+ <div id="regulationDetailsList">
713
+ <!-- 상세 법규 항목 동적 추가 -->
 
 
714
  </div>
715
  </div>
716
+ </div>
717
 
718
+ <!-- 채팅 영역 -->
719
+ <div class="chat-area">
 
 
 
 
 
 
 
 
 
 
 
 
720
  <div class="chat-messages" id="chatMessages">
721
+ <!-- 메시지 동적 추가 -->
 
 
 
 
 
 
 
 
 
 
722
  </div>
723
+ <div class="input-area">
724
+ <div class="input-wrapper">
725
+ <textarea id="userInput" placeholder="질문을 입력하세요..." rows="1"></textarea>
726
+ <button id="sendBtn">전송</button>
 
 
 
 
 
 
 
 
 
 
 
727
  </div>
728
  </div>
729
+ </div>
730
+ </div>
 
 
 
 
 
 
731
 
732
+ <!-- 로딩 인디케이터 (추가) -->
733
+ <div id="loading">
734
+ <div class="loading-spinner"></div>
735
+ <p>데이터 로딩 중...</p>
736
+ <div id="status"></div>
737
+ </div>
 
 
 
 
738
 
739
+ <script>
740
+ const socket = io();
741
+
742
+ // 초기 로딩 상태
743
+ const loadingDiv = document.getElementById('loading');
744
+ const statusDiv = document.getElementById('status');
745
+ loadingDiv.classList.remove('hidden'); // 로딩 표시
746
+
747
+ // SocketIO 메시지 수신 (로딩 상태 업데이트)
748
+ socket.on('message', (data) => {
749
+ const msgP = document.createElement('p');
750
+ msgP.textContent = data.message;
751
+ statusDiv.appendChild(msgP);
752
+ statusDiv.scrollTop = statusDiv.scrollHeight;
753
+
754
+ if (data.message === 'Ready to Search') {
755
+ loadingDiv.classList.add('hidden'); // 로딩 완료 시 숨김
756
  }
757
+ });
758
 
759
+ // ===== 초기화 =====
760
+ let selectedRegulations = [];
761
+ let selectedRegions = [];
 
 
 
762
 
763
+ document.addEventListener('DOMContentLoaded', () => {
764
+ initializeRegionToggles();
765
+ loadRegulationList();
766
+ loadDetailedRegulationList();
 
 
 
767
 
768
+ const userInput = document.getElementById('userInput');
769
+ const sendBtn = document.getElementById('sendBtn');
 
 
 
 
770
 
771
+ userInput.addEventListener('input', () => {
772
+ sendBtn.disabled = !userInput.value.trim();
 
 
 
 
 
773
  });
774
 
775
+ userInput.addEventListener('keypress', (e) => {
776
+ if (e.key === 'Enter' && !e.shiftKey) {
777
+ e.preventDefault();
778
+ sendMessage();
 
779
  }
780
  });
781
 
782
+ sendBtn.addEventListener('click', sendMessage);
783
+ });
 
 
 
 
784
 
785
+ // ===== 사이드바 토글 =====
786
+ function toggleSidebar() {
787
  const sidebar = document.getElementById('sidebar');
788
+ const overlay = document.getElementById('sidebarOverlay');
789
+ const toggle = document.querySelector('.sidebar-toggle');
 
790
 
791
+ sidebar.classList.toggle('open');
792
+ overlay.classList.toggle('active');
793
+ toggle.classList.toggle('active');
 
794
  }
795
 
796
+ // ===== 새 페이지 생성 =====
797
+ function createNewPage() {
798
+ // 페이지 생성 로직 (예: 새 채팅 시작)
799
+ console.log('새 페이지 생성');
 
 
 
 
 
 
800
  }
801
 
802
+ // ===== 지역 토글 초기화 =====
803
+ function initializeRegionToggles() {
804
+ const regions = ['국내', '북미', '유럽'];
805
+ const togglesDiv = document.getElementById('regionToggles');
806
+
807
+ regions.forEach(region => {
808
+ const btn = document.createElement('button');
809
+ btn.className = 'toggle-btn';
810
+ btn.textContent = region;
811
+ btn.dataset.region = region;
812
+ btn.onclick = () => toggleRegion(btn);
813
+ togglesDiv.appendChild(btn);
814
  });
815
  }
816
 
817
+ function toggleRegion(btn) {
818
+ btn.classList.toggle('active');
819
+ selectedRegions = Array.from(document.querySelectorAll('.toggle-btn.active'))
820
+ .map(b => b.dataset.region);
821
+ loadDetailedRegulationList();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
822
  }
823
 
824
  function getSelectedRegions() {
825
+ return selectedRegions.length > 0 ? selectedRegions : ['국내', '북미', '유럽'];
826
  }
827
 
828
+ // ===== 법규 리스트 로드 =====
829
+ function loadRegulationList() {
830
+ // 법규 리스트 로드 로직 (예시 데이터)
831
  const regulationListDiv = document.getElementById('regulationList');
832
+ const regulations = ['안전기준 34조', 'FMVSS 118', 'UN Regulation 21']; // 예시
833
+
834
+ regulations.forEach((reg, index) => {
835
+ const item = document.createElement('div');
836
+ item.className = 'regulation-item';
837
+ item.dataset.id = `reg_${index}`;
838
+ item.textContent = reg;
839
+ regulationListDiv.appendChild(item);
840
+
841
+ item.addEventListener('click', () => toggleRegulationSelection(item));
842
  });
843
  }
844
 
845
+ function toggleRegulationSelection(item) {
846
+ item.classList.toggle('selected');
847
+ const id = item.dataset.id;
848
+
849
+ if (item.classList.contains('selected')) {
 
850
  selectedRegulations.push(id);
851
+ } else {
852
+ selectedRegulations = selectedRegulations.filter(regId => regId !== id);
853
  }
854
+
855
  updateSelectedCount();
856
  }
857
 
858
  function updateSelectedCount() {
859
+ const countSpan = document.getElementById('selectedCount');
860
+ countSpan.textContent = `선택: ${selectedRegulations.length}`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
861
  }
862
 
863
  // ===== 메시지 전송 =====
864
  function sendMessage() {
865
+ const userInput = document.getElementById('userInput');
866
+ const query = userInput.value.trim();
867
+ if (!query) return;
 
 
 
868
 
869
+ addMessage(query, 'user');
870
+ userInput.value = '';
871
+ sendBtn.disabled = true;
872
 
 
873
  showTypingIndicator();
874
 
 
875
  const requestData = {
876
+ query: query,
877
  regions: getSelectedRegions(),
878
  selectedRegulations: selectedRegulations.map(id => {
879
  const element = document.querySelector(`[data-id="${id}"]`);
 
944
  return textarea.value;
945
  }
946
 
947
+ function formatTime(date) {
948
+ return date.toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit' });
949
+ }
950
+
951
  function showTypingIndicator() {
952
  const chatMessages = document.getElementById('chatMessages');
953
  const indicator = document.createElement('div');