seawolf2357 commited on
Commit
73bb808
ยท
verified ยท
1 Parent(s): 1bba99b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +179 -84
app.py CHANGED
@@ -349,7 +349,7 @@ if __name__ == '__main__':
349
  <head>
350
  <meta charset="UTF-8">
351
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
352
- <title>Hugging Face URL ์นด๋“œ ๋ฆฌ์ŠคํŠธ</title>
353
  <style>
354
  body {
355
  font-family: Arial, sans-serif;
@@ -361,7 +361,7 @@ if __name__ == '__main__':
361
  }
362
 
363
  .container {
364
- max-width: 1200px;
365
  margin: 0 auto;
366
  padding: 1rem;
367
  }
@@ -445,72 +445,87 @@ if __name__ == '__main__':
445
  text-decoration: underline;
446
  }
447
 
448
- .cards-container {
449
- display: flex;
450
- flex-wrap: wrap;
 
451
  gap: 1rem;
452
  }
453
 
454
- .card {
455
  border: 1px solid #ddd;
456
  border-radius: 8px;
457
- padding: 1rem;
458
- width: 300px;
459
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
460
- position: relative;
461
  background-color: #fff;
 
462
  transition: all 0.3s ease;
 
 
 
 
463
  }
464
 
465
- .card:hover {
466
  transform: translateY(-5px);
467
  box-shadow: 0 5px 15px rgba(0,0,0,0.1);
468
  }
469
 
470
- .card.liked {
471
  border-color: #ff4757;
472
  background-color: #ffebee;
473
  }
474
 
475
- .card-header {
476
- margin-bottom: 0.5rem;
477
- padding-right: 40px; /* ์ข‹์•„์š” ๋ฒ„ํŠผ ๊ณต๊ฐ„ */
 
478
  }
479
 
480
- .card-title {
481
- font-size: 1.2rem;
482
- margin: 0 0 0.5rem 0;
483
- color: #333;
 
 
 
484
  }
485
 
486
- .card a {
487
- text-decoration: none;
488
- color: #2980b9;
489
- word-break: break-all;
490
- display: block;
491
- font-size: 0.9rem;
492
  }
493
 
494
- .card a:hover {
495
- text-decoration: underline;
 
 
 
 
 
 
 
 
 
 
496
  }
497
 
498
  .like-button {
499
  position: absolute;
500
- top: 1rem;
501
- right: 1rem;
502
- width: 30px;
503
- height: 30px;
504
  display: flex;
505
  align-items: center;
506
  justify-content: center;
507
  border-radius: 50%;
508
  border: none;
509
  background: transparent;
510
- font-size: 1.5rem;
511
  cursor: pointer;
512
  transition: all 0.3s ease;
513
  color: #ddd;
 
514
  }
515
 
516
  .like-button:hover {
@@ -531,6 +546,7 @@ if __name__ == '__main__':
531
  border-radius: 4px;
532
  font-size: 0.7rem;
533
  font-weight: bold;
 
534
  }
535
 
536
  .like-status {
@@ -627,6 +643,30 @@ if __name__ == '__main__':
627
  font-size: 0.9rem;
628
  }
629
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
630
  @media (max-width: 768px) {
631
  .user-controls {
632
  flex-direction: column;
@@ -645,8 +685,8 @@ if __name__ == '__main__':
645
  margin-bottom: 0.5rem;
646
  }
647
 
648
- .card {
649
- width: 100%;
650
  }
651
  }
652
  </style>
@@ -693,13 +733,20 @@ if __name__ == '__main__':
693
  </div>
694
  </div>
695
 
 
 
 
 
 
 
 
696
  <div id="statusMessage" class="status-message"></div>
697
 
698
  <div id="loadingIndicator" class="loading">
699
  <div class="spinner"></div>
700
  </div>
701
 
702
- <div id="cardsContainer" class="cards-container"></div>
703
  </div>
704
 
705
  <script>
@@ -710,7 +757,7 @@ if __name__ == '__main__':
710
  logoutButton: document.getElementById('logoutButton'),
711
  refreshButton: document.getElementById('refreshButton'),
712
  currentUser: document.getElementById('currentUser'),
713
- cardsContainer: document.getElementById('cardsContainer'),
714
  loadingIndicator: document.getElementById('loadingIndicator'),
715
  statusMessage: document.getElementById('statusMessage'),
716
  searchInput: document.getElementById('searchInput'),
@@ -720,7 +767,8 @@ if __name__ == '__main__':
720
  totalUrlCount: document.getElementById('totalUrlCount'),
721
  likedUrlCount: document.getElementById('likedUrlCount'),
722
  allUrlsBtn: document.getElementById('allUrlsBtn'),
723
- likedUrlsBtn: document.getElementById('likedUrlsBtn')
 
724
  };
725
 
726
  // ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒํƒœ
@@ -728,7 +776,8 @@ if __name__ == '__main__':
728
  username: null,
729
  allURLs: [],
730
  isLoading: false,
731
- viewMode: 'all' // 'all' ๋˜๋Š” 'liked'
 
732
  };
733
 
734
  // ๋กœ๋”ฉ ์ƒํƒœ ํ‘œ์‹œ ํ•จ์ˆ˜
@@ -767,7 +816,11 @@ if __name__ == '__main__':
767
  elements.likedUrlCount.textContent = likedCount;
768
  }
769
 
770
- // ์„ธ์…˜ ์ƒํƒœ ํ™•์ธ
 
 
 
 
771
  async function checkSessionStatus() {
772
  try {
773
  const response = await fetch('/api/session-status');
@@ -854,8 +907,8 @@ if __name__ == '__main__':
854
 
855
  showMessage('๋กœ๊ทธ์•„์›ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
856
 
857
- // ์นด๋“œ ์ดˆ๊ธฐํ™”
858
- elements.cardsContainer.innerHTML = '';
859
  }
860
  } catch (error) {
861
  console.error('๋กœ๊ทธ์•„์›ƒ ์˜ค๋ฅ˜:', error);
@@ -876,7 +929,6 @@ if __name__ == '__main__':
876
 
877
  const data = await handleApiResponse(response);
878
 
879
-
880
  if (data.success) {
881
  // URL ๋ชฉ๋ก ๋‹ค์‹œ ๋กœ๋“œ
882
  loadUrls();
@@ -902,7 +954,7 @@ if __name__ == '__main__':
902
 
903
  state.allURLs = data;
904
  updateLikeStats();
905
- renderCards();
906
  } catch (error) {
907
  console.error('URL ๋ชฉ๋ก ๋กœ๋“œ ์˜ค๋ฅ˜:', error);
908
  showMessage(`URL ๋ชฉ๋ก ๋กœ๋“œ ์˜ค๋ฅ˜: ${error.message}`, true);
@@ -930,7 +982,7 @@ if __name__ == '__main__':
930
  if (urlObj) {
931
  urlObj.is_liked = data.is_liked;
932
  updateLikeStats();
933
- renderCards();
934
  }
935
 
936
  showMessage(data.message);
@@ -943,9 +995,9 @@ if __name__ == '__main__':
943
  }
944
  }
945
 
946
- // ์นด๋“œ ๋ Œ๋”๋ง
947
- function renderCards() {
948
- elements.cardsContainer.innerHTML = '';
949
 
950
  let urlsToShow = state.allURLs;
951
 
@@ -969,53 +1021,91 @@ if __name__ == '__main__':
969
  emptyMessage.style.padding = '1rem';
970
  emptyMessage.style.width = '100%';
971
  emptyMessage.style.textAlign = 'center';
972
- elements.cardsContainer.appendChild(emptyMessage);
973
  return;
974
  }
975
 
976
- // ์นด๋“œ ์ƒ์„ฑ
977
  urlsToShow.forEach(item => {
978
- const card = document.createElement('div');
979
- card.className = `card ${item.is_liked ? 'liked' : ''}`;
980
 
981
  if (item.is_liked) {
982
  const badge = document.createElement('div');
983
  badge.className = 'like-badge';
984
  badge.textContent = '์ข‹์•„์š”';
985
- card.appendChild(badge);
986
  }
987
 
988
- const cardContent = `
989
- <div class="card-header">
990
- <h3 class="card-title">${item.title}</h3>
991
- <button class="like-button ${item.is_liked ? 'liked' : ''}" data-url="${item.url}">
992
- โค
993
- </button>
994
- </div>
995
- <div class="card-body">
996
- <a href="${item.url}" target="_blank">${item.url}</a>
997
- <div style="margin-top: 0.5rem;">
998
- <span>์†Œ์œ ์ž: ${item.model_info.owner}</span>
999
- </div>
1000
- <div>
1001
- <span>์ €์žฅ์†Œ: ${item.model_info.repo}</span>
1002
- </div>
1003
- <div>
1004
- <span>์œ ํ˜•: ${item.model_info.type}</span>
1005
- </div>
1006
- </div>
1007
- `;
1008
 
1009
- card.innerHTML = cardContent;
1010
- elements.cardsContainer.appendChild(card);
 
1011
 
1012
- // ์ข‹์•„์š” ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์ถ”๊ฐ€
1013
- const likeButton = card.querySelector('.like-button');
 
 
1014
  likeButton.addEventListener('click', (e) => {
1015
  e.preventDefault();
1016
- const url = e.target.dataset.url;
1017
- toggleLike(url);
1018
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1019
  });
1020
  }
1021
 
@@ -1040,14 +1130,14 @@ if __name__ == '__main__':
1040
  elements.refreshButton.addEventListener('click', refreshLikes);
1041
 
1042
  // ๊ฒ€์ƒ‰ ์ž…๋ ฅ ํ•„๋“œ
1043
- elements.searchInput.addEventListener('input', renderCards);
1044
 
1045
  // ํ•„ํ„ฐ ๋ฒ„ํŠผ - ์ „์ฒด ๋ณด๊ธฐ
1046
  elements.allUrlsBtn.addEventListener('click', () => {
1047
  elements.allUrlsBtn.classList.add('active');
1048
  elements.likedUrlsBtn.classList.remove('active');
1049
  state.viewMode = 'all';
1050
- renderCards();
1051
  });
1052
 
1053
  // ํ•„ํ„ฐ ๋ฒ„ํŠผ - ์ข‹์•„์š”๋งŒ ๋ณด๊ธฐ
@@ -1055,7 +1145,13 @@ if __name__ == '__main__':
1055
  elements.likedUrlsBtn.classList.add('active');
1056
  elements.allUrlsBtn.classList.remove('active');
1057
  state.viewMode = 'liked';
1058
- renderCards();
 
 
 
 
 
 
1059
  });
1060
  }
1061
 
@@ -1070,7 +1166,6 @@ if __name__ == '__main__':
1070
  </script>
1071
  </body>
1072
  </html>
1073
- '''
1074
- )
1075
 
1076
- app.run(debug=True, host='0.0.0.0', port=7860)
 
349
  <head>
350
  <meta charset="UTF-8">
351
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
352
+ <title>Hugging Face URL ๊ทธ๋ฆฌ๋“œ</title>
353
  <style>
354
  body {
355
  font-family: Arial, sans-serif;
 
361
  }
362
 
363
  .container {
364
+ max-width: 1600px;
365
  margin: 0 auto;
366
  padding: 1rem;
367
  }
 
445
  text-decoration: underline;
446
  }
447
 
448
+ /* ๊ทธ๋ฆฌ๋“œ ๋ ˆ์ด์•„์›ƒ ์Šคํƒ€์ผ */
449
+ .grid-container {
450
+ display: grid;
451
+ grid-template-columns: repeat(4, 1fr);
452
  gap: 1rem;
453
  }
454
 
455
+ .grid-item {
456
  border: 1px solid #ddd;
457
  border-radius: 8px;
 
 
 
 
458
  background-color: #fff;
459
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
460
  transition: all 0.3s ease;
461
+ position: relative;
462
+ display: flex;
463
+ flex-direction: column;
464
+ overflow: hidden;
465
  }
466
 
467
+ .grid-item:hover {
468
  transform: translateY(-5px);
469
  box-shadow: 0 5px 15px rgba(0,0,0,0.1);
470
  }
471
 
472
+ .grid-item.liked {
473
  border-color: #ff4757;
474
  background-color: #ffebee;
475
  }
476
 
477
+ .grid-header {
478
+ padding: 0.5rem 1rem;
479
+ border-bottom: 1px solid #eee;
480
+ position: relative;
481
  }
482
 
483
+ .grid-title {
484
+ font-size: 1rem;
485
+ margin: 0;
486
+ padding-right: 30px;
487
+ white-space: nowrap;
488
+ overflow: hidden;
489
+ text-overflow: ellipsis;
490
  }
491
 
492
+ .grid-content {
493
+ flex: 1;
494
+ position: relative;
495
+ height: 300px;
 
 
496
  }
497
 
498
+ .iframe-container {
499
+ position: absolute;
500
+ top: 0;
501
+ left: 0;
502
+ width: 100%;
503
+ height: 100%;
504
+ }
505
+
506
+ .iframe-container iframe {
507
+ width: 100%;
508
+ height: 100%;
509
+ border: none;
510
  }
511
 
512
  .like-button {
513
  position: absolute;
514
+ top: 0.5rem;
515
+ right: 0.5rem;
516
+ width: 24px;
517
+ height: 24px;
518
  display: flex;
519
  align-items: center;
520
  justify-content: center;
521
  border-radius: 50%;
522
  border: none;
523
  background: transparent;
524
+ font-size: 1.2rem;
525
  cursor: pointer;
526
  transition: all 0.3s ease;
527
  color: #ddd;
528
+ padding: 0;
529
  }
530
 
531
  .like-button:hover {
 
546
  border-radius: 4px;
547
  font-size: 0.7rem;
548
  font-weight: bold;
549
+ z-index: 10;
550
  }
551
 
552
  .like-status {
 
643
  font-size: 0.9rem;
644
  }
645
 
646
+ .view-toggle {
647
+ margin-top: 1rem;
648
+ display: flex;
649
+ justify-content: space-between;
650
+ align-items: center;
651
+ }
652
+
653
+ .view-toggle button {
654
+ margin-left: 0.5rem;
655
+ }
656
+
657
+ /* ๋ฐ˜์‘ํ˜• ์„ค์ • */
658
+ @media (max-width: 1400px) {
659
+ .grid-container {
660
+ grid-template-columns: repeat(3, 1fr);
661
+ }
662
+ }
663
+
664
+ @media (max-width: 1024px) {
665
+ .grid-container {
666
+ grid-template-columns: repeat(2, 1fr);
667
+ }
668
+ }
669
+
670
  @media (max-width: 768px) {
671
  .user-controls {
672
  flex-direction: column;
 
685
  margin-bottom: 0.5rem;
686
  }
687
 
688
+ .grid-container {
689
+ grid-template-columns: 1fr;
690
  }
691
  }
692
  </style>
 
733
  </div>
734
  </div>
735
 
736
+ <div class="view-toggle">
737
+ <div>
738
+ <input type="checkbox" id="embedToggle" checked />
739
+ <label for="embedToggle">URL ์ž„๋ฒ ๋”ฉ ๋ณด๊ธฐ</label>
740
+ </div>
741
+ </div>
742
+
743
  <div id="statusMessage" class="status-message"></div>
744
 
745
  <div id="loadingIndicator" class="loading">
746
  <div class="spinner"></div>
747
  </div>
748
 
749
+ <div id="gridContainer" class="grid-container"></div>
750
  </div>
751
 
752
  <script>
 
757
  logoutButton: document.getElementById('logoutButton'),
758
  refreshButton: document.getElementById('refreshButton'),
759
  currentUser: document.getElementById('currentUser'),
760
+ gridContainer: document.getElementById('gridContainer'),
761
  loadingIndicator: document.getElementById('loadingIndicator'),
762
  statusMessage: document.getElementById('statusMessage'),
763
  searchInput: document.getElementById('searchInput'),
 
767
  totalUrlCount: document.getElementById('totalUrlCount'),
768
  likedUrlCount: document.getElementById('likedUrlCount'),
769
  allUrlsBtn: document.getElementById('allUrlsBtn'),
770
+ likedUrlsBtn: document.getElementById('likedUrlsBtn'),
771
+ embedToggle: document.getElementById('embedToggle')
772
  };
773
 
774
  // ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒํƒœ
 
776
  username: null,
777
  allURLs: [],
778
  isLoading: false,
779
+ viewMode: 'all', // 'all' ๋˜๋Š” 'liked'
780
+ embedView: true // iframe ์ž„๋ฒ ๋”ฉ ์—ฌ๋ถ€
781
  };
782
 
783
  // ๋กœ๋”ฉ ์ƒํƒœ ํ‘œ์‹œ ํ•จ์ˆ˜
 
816
  elements.likedUrlCount.textContent = likedCount;
817
  }
818
 
819
+ // ์„ธ์…˜ ์ƒํƒœ
820
+
821
+
822
+
823
+ // ์„ธ์…˜ ์ƒํƒœ ํ™•์ธ
824
  async function checkSessionStatus() {
825
  try {
826
  const response = await fetch('/api/session-status');
 
907
 
908
  showMessage('๋กœ๊ทธ์•„์›ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
909
 
910
+ // ๊ทธ๋ฆฌ๋“œ ์ดˆ๊ธฐํ™”
911
+ elements.gridContainer.innerHTML = '';
912
  }
913
  } catch (error) {
914
  console.error('๋กœ๊ทธ์•„์›ƒ ์˜ค๋ฅ˜:', error);
 
929
 
930
  const data = await handleApiResponse(response);
931
 
 
932
  if (data.success) {
933
  // URL ๋ชฉ๋ก ๋‹ค์‹œ ๋กœ๋“œ
934
  loadUrls();
 
954
 
955
  state.allURLs = data;
956
  updateLikeStats();
957
+ renderGrid();
958
  } catch (error) {
959
  console.error('URL ๋ชฉ๋ก ๋กœ๋“œ ์˜ค๋ฅ˜:', error);
960
  showMessage(`URL ๋ชฉ๋ก ๋กœ๋“œ ์˜ค๋ฅ˜: ${error.message}`, true);
 
982
  if (urlObj) {
983
  urlObj.is_liked = data.is_liked;
984
  updateLikeStats();
985
+ renderGrid();
986
  }
987
 
988
  showMessage(data.message);
 
995
  }
996
  }
997
 
998
+ // ๊ทธ๋ฆฌ๋“œ ๋ Œ๋”๋ง
999
+ function renderGrid() {
1000
+ elements.gridContainer.innerHTML = '';
1001
 
1002
  let urlsToShow = state.allURLs;
1003
 
 
1021
  emptyMessage.style.padding = '1rem';
1022
  emptyMessage.style.width = '100%';
1023
  emptyMessage.style.textAlign = 'center';
1024
+ elements.gridContainer.appendChild(emptyMessage);
1025
  return;
1026
  }
1027
 
1028
+ // ๊ทธ๋ฆฌ๋“œ ์•„์ดํ…œ ์ƒ์„ฑ
1029
  urlsToShow.forEach(item => {
1030
+ const gridItem = document.createElement('div');
1031
+ gridItem.className = `grid-item ${item.is_liked ? 'liked' : ''}`;
1032
 
1033
  if (item.is_liked) {
1034
  const badge = document.createElement('div');
1035
  badge.className = 'like-badge';
1036
  badge.textContent = '์ข‹์•„์š”';
1037
+ gridItem.appendChild(badge);
1038
  }
1039
 
1040
+ // ํ—ค๋” ๋ถ€๋ถ„
1041
+ const header = document.createElement('div');
1042
+ header.className = 'grid-header';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1043
 
1044
+ const title = document.createElement('h3');
1045
+ title.className = 'grid-title';
1046
+ title.textContent = item.title;
1047
 
1048
+ const likeButton = document.createElement('button');
1049
+ likeButton.className = `like-button ${item.is_liked ? 'liked' : ''}`;
1050
+ likeButton.innerHTML = 'โค';
1051
+ likeButton.dataset.url = item.url;
1052
  likeButton.addEventListener('click', (e) => {
1053
  e.preventDefault();
1054
+ toggleLike(item.url);
 
1055
  });
1056
+
1057
+ header.appendChild(title);
1058
+ header.appendChild(likeButton);
1059
+
1060
+ // ์ปจํ…์ธ  ๋ถ€๋ถ„ (iframe ์ž„๋ฒ ๋”ฉ)
1061
+ const content = document.createElement('div');
1062
+ content.className = 'grid-content';
1063
+
1064
+ if (state.embedView) {
1065
+ const iframeContainer = document.createElement('div');
1066
+ iframeContainer.className = 'iframe-container';
1067
+
1068
+ const iframe = document.createElement('iframe');
1069
+ iframe.src = item.url;
1070
+ iframe.title = item.title;
1071
+ iframe.sandbox = 'allow-scripts allow-same-origin allow-forms';
1072
+ iframe.loading = 'lazy';
1073
+
1074
+ iframeContainer.appendChild(iframe);
1075
+ content.appendChild(iframeContainer);
1076
+ } else {
1077
+ // ์ž„๋ฒ ๋”ฉ ์—†๋Š” ๊ฒฝ์šฐ ๋งํฌ๋งŒ ํ‘œ์‹œ
1078
+ const linkContainer = document.createElement('div');
1079
+ linkContainer.style.padding = '1rem';
1080
+
1081
+ const link = document.createElement('a');
1082
+ link.href = item.url;
1083
+ link.textContent = item.url;
1084
+ link.target = '_blank';
1085
+
1086
+ const owner = document.createElement('div');
1087
+ owner.textContent = `์†Œ์œ ์ž: ${item.model_info.owner}`;
1088
+ owner.style.marginTop = '0.5rem';
1089
+
1090
+ const repo = document.createElement('div');
1091
+ repo.textContent = `์ €์žฅ์†Œ: ${item.model_info.repo}`;
1092
+
1093
+ const type = document.createElement('div');
1094
+ type.textContent = `์œ ํ˜•: ${item.model_info.type}`;
1095
+
1096
+ linkContainer.appendChild(link);
1097
+ linkContainer.appendChild(owner);
1098
+ linkContainer.appendChild(repo);
1099
+ linkContainer.appendChild(type);
1100
+
1101
+ content.appendChild(linkContainer);
1102
+ }
1103
+
1104
+ // ๊ทธ๋ฆฌ๋“œ ์•„์ดํ…œ์— ์ถ”๊ฐ€
1105
+ gridItem.appendChild(header);
1106
+ gridItem.appendChild(content);
1107
+
1108
+ elements.gridContainer.appendChild(gridItem);
1109
  });
1110
  }
1111
 
 
1130
  elements.refreshButton.addEventListener('click', refreshLikes);
1131
 
1132
  // ๊ฒ€์ƒ‰ ์ž…๋ ฅ ํ•„๋“œ
1133
+ elements.searchInput.addEventListener('input', renderGrid);
1134
 
1135
  // ํ•„ํ„ฐ ๋ฒ„ํŠผ - ์ „์ฒด ๋ณด๊ธฐ
1136
  elements.allUrlsBtn.addEventListener('click', () => {
1137
  elements.allUrlsBtn.classList.add('active');
1138
  elements.likedUrlsBtn.classList.remove('active');
1139
  state.viewMode = 'all';
1140
+ renderGrid();
1141
  });
1142
 
1143
  // ํ•„ํ„ฐ ๋ฒ„ํŠผ - ์ข‹์•„์š”๋งŒ ๋ณด๊ธฐ
 
1145
  elements.likedUrlsBtn.classList.add('active');
1146
  elements.allUrlsBtn.classList.remove('active');
1147
  state.viewMode = 'liked';
1148
+ renderGrid();
1149
+ });
1150
+
1151
+ // ์ž„๋ฒ ๋”ฉ ํ† ๊ธ€
1152
+ elements.embedToggle.addEventListener('change', () => {
1153
+ state.embedView = elements.embedToggle.checked;
1154
+ renderGrid();
1155
  });
1156
  }
1157
 
 
1166
  </script>
1167
  </body>
1168
  </html>
1169
+ ''')
 
1170
 
1171
+ app.run(debug=True, host='0.0.0.0', port=7860)