@necrolab/dashboard 0.5.15 → 0.5.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/backend/api.js +2 -3
  2. package/eslint.config.js +46 -0
  3. package/index.html +2 -1
  4. package/package.json +5 -2
  5. package/src/App.vue +140 -170
  6. package/src/assets/css/base/mixins.scss +72 -0
  7. package/src/assets/css/base/reset.scss +0 -2
  8. package/src/assets/css/base/scroll.scss +43 -36
  9. package/src/assets/css/base/typography.scss +9 -10
  10. package/src/assets/css/base/variables.scss +43 -0
  11. package/src/assets/css/components/accessibility.scss +37 -0
  12. package/src/assets/css/components/buttons.scss +58 -15
  13. package/src/assets/css/components/forms.scss +31 -32
  14. package/src/assets/css/components/headers.scss +12 -20
  15. package/src/assets/css/components/modals.scss +2 -2
  16. package/src/assets/css/components/search-groups.scss +28 -22
  17. package/src/assets/css/components/tables.scss +5 -7
  18. package/src/assets/css/components/toasts.scss +7 -7
  19. package/src/assets/css/components/utilities.scss +220 -0
  20. package/src/assets/css/main.scss +66 -77
  21. package/src/components/Auth/LoginForm.vue +5 -84
  22. package/src/components/Editors/Account/Account.vue +8 -10
  23. package/src/components/Editors/Account/AccountCreator.vue +28 -59
  24. package/src/components/Editors/Account/AccountView.vue +38 -86
  25. package/src/components/Editors/Account/CreateAccount.vue +8 -50
  26. package/src/components/Editors/Profile/CreateProfile.vue +74 -131
  27. package/src/components/Editors/Profile/Profile.vue +15 -17
  28. package/src/components/Editors/Profile/ProfileCountryChooser.vue +16 -60
  29. package/src/components/Editors/Profile/ProfileView.vue +46 -96
  30. package/src/components/Editors/TagLabel.vue +16 -55
  31. package/src/components/Editors/TagToggle.vue +20 -8
  32. package/src/components/Filter/Filter.vue +62 -75
  33. package/src/components/Filter/FilterPreview.vue +161 -135
  34. package/src/components/Filter/PriceSortToggle.vue +36 -43
  35. package/src/components/Table/Header.vue +1 -1
  36. package/src/components/Table/Table.vue +45 -51
  37. package/src/components/Tasks/CheckStock.vue +7 -16
  38. package/src/components/Tasks/Controls/DesktopControls.vue +15 -60
  39. package/src/components/Tasks/Controls/MobileControls.vue +5 -20
  40. package/src/components/Tasks/CreateTaskAXS.vue +20 -118
  41. package/src/components/Tasks/CreateTaskTM.vue +33 -189
  42. package/src/components/Tasks/EventDetailRow.vue +21 -0
  43. package/src/components/Tasks/MassEdit.vue +6 -16
  44. package/src/components/Tasks/QuickSettings.vue +140 -216
  45. package/src/components/Tasks/ScrapeVenue.vue +4 -13
  46. package/src/components/Tasks/Stats.vue +19 -38
  47. package/src/components/Tasks/Task.vue +65 -268
  48. package/src/components/Tasks/TaskLabel.vue +9 -3
  49. package/src/components/Tasks/TaskView.vue +43 -63
  50. package/src/components/Tasks/Utilities.vue +10 -44
  51. package/src/components/Tasks/ViewTask.vue +23 -107
  52. package/src/components/icons/Close.vue +2 -8
  53. package/src/components/icons/Gear.vue +8 -8
  54. package/src/components/icons/Hash.vue +5 -0
  55. package/src/components/icons/Key.vue +2 -8
  56. package/src/components/icons/Pencil.vue +2 -8
  57. package/src/components/icons/Profile.vue +2 -8
  58. package/src/components/icons/Sell.vue +2 -8
  59. package/src/components/icons/Spinner.vue +4 -7
  60. package/src/components/icons/SquareCheck.vue +2 -8
  61. package/src/components/icons/SquareUncheck.vue +2 -8
  62. package/src/components/icons/Wildcard.vue +2 -8
  63. package/src/components/icons/index.js +3 -1
  64. package/src/components/ui/ActionButtonGroup.vue +113 -52
  65. package/src/components/ui/BalanceIndicator.vue +60 -0
  66. package/src/components/ui/EmptyState.vue +24 -0
  67. package/src/components/ui/EnableDisableToggle.vue +23 -0
  68. package/src/components/ui/FormField.vue +48 -48
  69. package/src/components/ui/IconLabel.vue +23 -0
  70. package/src/components/ui/InfoRow.vue +21 -54
  71. package/src/components/ui/Modal.vue +89 -56
  72. package/src/components/ui/Navbar.vue +60 -41
  73. package/src/components/ui/ReadonlyFieldsSection.vue +31 -0
  74. package/src/components/ui/ReconnectIndicator.vue +111 -124
  75. package/src/components/ui/SectionCard.vue +6 -14
  76. package/src/components/ui/Splash.vue +2 -10
  77. package/src/components/ui/StatusBadge.vue +26 -28
  78. package/src/components/ui/TaskToggle.vue +54 -0
  79. package/src/components/ui/controls/CountryChooser.vue +27 -64
  80. package/src/components/ui/controls/EyeToggle.vue +1 -1
  81. package/src/components/ui/controls/atomic/Checkbox.vue +40 -121
  82. package/src/components/ui/controls/atomic/Dropdown.vue +103 -139
  83. package/src/components/ui/controls/atomic/MultiDropdown.vue +71 -119
  84. package/src/components/ui/controls/atomic/Switch.vue +21 -84
  85. package/src/composables/useColorMapping.js +15 -0
  86. package/src/composables/useCopyToClipboard.js +1 -1
  87. package/src/composables/useDateFormatting.js +21 -0
  88. package/src/composables/useDeviceDetection.js +14 -0
  89. package/src/composables/useDropdownPosition.js +3 -4
  90. package/src/composables/useDynamicTableHeight.js +31 -0
  91. package/src/composables/useRowSelection.js +0 -3
  92. package/src/composables/useTicketPricing.js +16 -0
  93. package/src/composables/useWindowDimensions.js +21 -0
  94. package/src/libs/Filter.js +14 -20
  95. package/src/libs/panzoom.js +1 -5
  96. package/src/libs/utils/array.js +60 -0
  97. package/src/{stores/utils.js → libs/utils/dataGeneration.js} +2 -250
  98. package/src/libs/utils/eventUrl.js +40 -0
  99. package/src/libs/utils/string.js +28 -0
  100. package/src/libs/utils/time.js +20 -0
  101. package/src/libs/utils/validation.js +88 -0
  102. package/src/main.js +0 -2
  103. package/src/stores/connection.js +1 -4
  104. package/src/stores/logger.js +6 -12
  105. package/src/stores/sampleData.js +1 -2
  106. package/src/stores/ui.js +59 -36
  107. package/src/views/Accounts.vue +13 -24
  108. package/src/views/Console.vue +70 -172
  109. package/src/views/Editor.vue +211 -379
  110. package/src/views/FilterBuilder.vue +188 -371
  111. package/src/views/Login.vue +3 -28
  112. package/src/views/Profiles.vue +8 -15
  113. package/src/views/Tasks.vue +49 -36
  114. package/tailwind.config.js +82 -71
  115. package/workbox-config.cjs +47 -5
  116. package/docs/plans/2026-02-08-tailwind-consolidation.md +0 -2438
  117. package/exit +0 -209
  118. package/run +0 -177
  119. package/src/assets/css/base/color-fallbacks.scss +0 -10
  120. package/switch-branch.sh +0 -41
  121. /package/public/{reconnect-logo.png → img/reconnect-logo.png} +0 -0
@@ -8,27 +8,25 @@
8
8
  </div>
9
9
  </div>
10
10
 
11
- <div class="bg-dark-400 border border-dark-650 rounded shadow-sm p-2">
11
+ <div class="bg-dark-400 border-2 rounded-lg shadow-sm p-2" style="border-color: var(--color-border);">
12
12
  <!-- Admin Editor Section - Hidden when proxy editor is open -->
13
13
  <div v-if="ui.profile.admin" class="admin-editor-section" :class="{ 'landscape-hidden': currentProxyList }">
14
14
  <h5 class="text-white text-xl font-bold flex gap-x-3 mb-3">Admin Editor</h5>
15
15
  <div class="flex flex-wrap items-center gap-3 w-full">
16
- <div class="w-full lg:w-64 flex flex-shrink-0 dropdown-container z-dropdown-high">
17
- <div class="relative flex-grow">
18
- <Dropdown
19
- class="bg-dark-500 border-2 border-r-0 border-dark-550 admin-file-dropdown w-full"
20
- default="Select a file"
21
- :onClick="(f) => loadFile(f)"
22
- :options="availableFiles"
23
- :allowDefault="false"
24
- :includeAdjacentButtons="true"
25
- rightAmount="right-1" />
26
- </div>
16
+ <div class="unified-search-group flex w-full flex-shrink-0 dropdown-container z-dropdown-high lg:w-auto lg:min-w-64">
17
+ <Dropdown
18
+ class="admin-file-dropdown flex-1"
19
+ default="Select a file"
20
+ :onClick="(f) => loadFile(f)"
21
+ :options="availableFiles"
22
+ :allowDefault="false"
23
+ :includeAdjacentButtons="true"
24
+ rightAmount="right-1" />
27
25
  <button
28
- class="refresh-button flex items-center justify-center w-10 h-10 text-white bg-dark-400 rounded-r-lg border-2 border-dark-550 border-l-0"
26
+ class="refresh-button flex items-center justify-center w-9 h-10 flex-shrink-0 text-white bg-dark-400 transition-all duration-150 hover:bg-dark-450"
29
27
  @click="loadAvailableFiles()"
30
28
  title="Refresh file list">
31
- <ReloadIcon class="refresh-icon" />
29
+ <ReloadIcon class="refresh-icon h-4 w-4" />
32
30
  </button>
33
31
  </div>
34
32
  <!-- Admin editor buttons moved here -->
@@ -92,9 +90,9 @@
92
90
  <div v-if="textEditorVisible" class="my-3 relative">
93
91
  <div class="pb-4">
94
92
  <!-- Syntax Highlighting Editor -->
95
- <div class="editor-container" :class="{ 'has-error': errorMessage }">
96
- <div class="editor-wrapper">
97
- <pre ref="codeDisplay" class="language-json code-highlight"></pre>
93
+ <div class="editor-container overflow-hidden rounded-lg shadow-card bg-dark-350" :class="{ 'has-error': errorMessage }">
94
+ <div class="relative w-full h-full">
95
+ <pre ref="codeDisplay" class="code-highlight language-json"></pre>
98
96
  <textarea
99
97
  ref="codeEditor"
100
98
  v-model="currentContent"
@@ -199,11 +197,11 @@
199
197
  </div>
200
198
  </template>
201
199
  <script setup>
202
- import { ref, computed, watch, onMounted, onUnmounted, nextTick } from "vue";
203
- import { DownIcon, ReloadIcon, EditIcon } from "@/components/icons";
200
+ import { ref, computed, watch, onMounted, nextTick } from "vue";
201
+ import { ReloadIcon } from "@/components/icons";
204
202
  import { useUIStore } from "@/stores/ui";
205
203
  import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
206
- import { getProxyLists, getProxyFile } from "@/stores/requests";
204
+ import { getProxyLists, getProxyFile, getQuickConfig, sendQuickConfig } from "@/stores/requests";
207
205
  import { DEBUG } from "@/utils/debug";
208
206
 
209
207
  const loadFromApi = async (path) => {
@@ -219,7 +217,6 @@ const currentContent = ref("");
219
217
  const proxyContent = ref("");
220
218
  const proxyLists = ref(["loading..."]);
221
219
  const currentProxyList = ref("");
222
- const activeModal = computed(() => ui.activeModal);
223
220
  const ui = useUIStore();
224
221
 
225
222
  // References to editor elements
@@ -247,12 +244,15 @@ const loadAvailableFiles = async () => (availableFiles.value = await loadFromApi
247
244
 
248
245
  const isJsonFile = () => currentFile.value.endsWith(".json");
249
246
 
247
+ // Debounce timer for highlighting
248
+ let highlightTimer = null;
249
+
250
250
  // Function to highlight code using Prism
251
251
  const highlightCode = () => {
252
252
  if (!codeDisplay.value || !codeEditor.value) return;
253
253
 
254
254
  // Get the language based on file extension
255
- let language = "javascript"; // Default language
255
+ let language = "javascript";
256
256
 
257
257
  if (currentFile.value) {
258
258
  if (currentFile.value.endsWith(".json")) {
@@ -261,50 +261,65 @@ const highlightCode = () => {
261
261
  language = "javascript";
262
262
  } else if (currentFile.value.endsWith(".txt") || currentFile.value.endsWith(".csv")) {
263
263
  language = "text";
264
- // Plain text doesn't need highlighting
265
- codeDisplay.value.textContent = currentContent.value || "";
266
- codeDisplay.value.className = "language-text code-highlight";
264
+ if (codeDisplay.value) {
265
+ codeDisplay.value.textContent = currentContent.value || "";
266
+ codeDisplay.value.className = "language-text code-highlight";
267
+ }
268
+ syncScroll();
267
269
  return;
268
270
  }
269
271
  }
270
272
 
271
- // Ensure Prism is available
272
- if (typeof Prism === "undefined") {
273
- console.error("Prism is not loaded");
274
- return;
275
- }
273
+ // Debounce to avoid excessive highlighting
274
+ if (highlightTimer) clearTimeout(highlightTimer);
275
+
276
+ highlightTimer = setTimeout(() => {
277
+ requestAnimationFrame(() => {
278
+ try {
279
+ // Check if Prism and language are available
280
+ if (typeof window.Prism === "undefined" || !window.Prism.languages?.[language]) {
281
+ if (codeDisplay.value) {
282
+ codeDisplay.value.textContent = currentContent.value || "";
283
+ codeDisplay.value.className = `language-${language} code-highlight`;
284
+ }
285
+ syncScroll();
286
+ return;
287
+ }
276
288
 
277
- // Use requestAnimationFrame for smoother updates
278
- requestAnimationFrame(() => {
279
- try {
280
- // Update the pre element with highlighted HTML
281
- const highlighted = Prism.highlight(currentContent.value || "", Prism.languages[language], language);
282
- codeDisplay.value.innerHTML = highlighted;
283
- codeDisplay.value.className = `language-${language} code-highlight`;
284
- } catch (e) {
285
- console.error("Highlight error:", e);
286
- // Fallback to plain text if highlighting fails
287
- codeDisplay.value.textContent = currentContent.value || "";
288
- }
289
+ // Highlight the code (Prism.highlight returns sanitized HTML)
290
+ const highlighted = window.Prism.highlight(
291
+ currentContent.value || "",
292
+ window.Prism.languages[language],
293
+ language
294
+ );
289
295
 
290
- // Ensure scroll positions are synced after highlighting
291
- syncScroll();
292
- });
296
+ if (codeDisplay.value) {
297
+ codeDisplay.value.innerHTML = highlighted;
298
+ codeDisplay.value.className = `language-${language} code-highlight`;
299
+ }
300
+ } catch (e) {
301
+ ui.logger.Error("Highlight error:", e);
302
+ if (codeDisplay.value) {
303
+ codeDisplay.value.textContent = currentContent.value || "";
304
+ }
305
+ } finally {
306
+ syncScroll();
307
+ }
308
+ });
309
+ }, 50);
293
310
  };
294
311
 
295
312
  // Function to sync scrolling between textarea and highlighted code
296
313
  const syncScroll = () => {
297
314
  if (!codeDisplay.value || !codeEditor.value) return;
298
315
 
299
- // Synchronize scrolling between the textarea and the highlighted code
300
- requestAnimationFrame(() => {
301
- codeDisplay.value.scrollTop = codeEditor.value.scrollTop;
302
- codeDisplay.value.scrollLeft = codeEditor.value.scrollLeft;
303
- });
316
+ // Direct synchronization without RAF for immediate response
317
+ codeDisplay.value.scrollTop = codeEditor.value.scrollTop;
318
+ codeDisplay.value.scrollLeft = codeEditor.value.scrollLeft;
304
319
  };
305
320
 
306
321
  // Function to handle tab key press in the editor
307
- const handleTab = (e) => {
322
+ const handleTab = () => {
308
323
  const textarea = codeEditor.value;
309
324
  const start = textarea.selectionStart;
310
325
  const end = textarea.selectionEnd;
@@ -405,7 +420,7 @@ const saveDataToAPI = async (data, endpoint, msg, json = true) => {
405
420
  if (json) {
406
421
  try {
407
422
  j = await res.json();
408
- } catch (e) {
423
+ } catch {
409
424
  ui.showError("Error saving proxies");
410
425
  return;
411
426
  }
@@ -413,7 +428,7 @@ const saveDataToAPI = async (data, endpoint, msg, json = true) => {
413
428
  } else {
414
429
  try {
415
430
  j = await res.text();
416
- } catch (e) {
431
+ } catch {
417
432
  ui.showError("Error saving proxies");
418
433
  return;
419
434
  }
@@ -434,6 +449,11 @@ const saveFile = async () => {
434
449
  };
435
450
  };
436
451
 
452
+ const saveQuickConfig = async () => {
453
+ const content = await getQuickConfig();
454
+ await sendQuickConfig(content);
455
+ };
456
+
437
457
  const saveAll = async () => {
438
458
  if (DEBUG) return;
439
459
  if (currentFile.value && currentContent.value) await saveFile();
@@ -474,7 +494,6 @@ const loadProxies = async (list) => {
474
494
  };
475
495
 
476
496
  const saveProxies = async () => {
477
- // if (!DEBUG) return;
478
497
  if (previous.proxyContent.content === proxyContent.value) ui.logger.Info("proxies did not change");
479
498
  else {
480
499
  await saveDataToAPI(
@@ -569,9 +588,8 @@ loadProxyLists();
569
588
  </script>
570
589
  <style lang="scss" scoped>
571
590
  /* ==========================================================================
572
- Z-INDEX MANAGEMENT
591
+ Z-INDEX MANAGEMENT - Required for dropdown layering
573
592
  ========================================================================== */
574
-
575
593
  .z-dropdown-high {
576
594
  position: relative;
577
595
  z-index: 200;
@@ -593,188 +611,139 @@ loadProxyLists();
593
611
  }
594
612
 
595
613
  /* ==========================================================================
596
- ADMIN EDITOR SECTION CONDITIONAL VISIBILITY
597
- ========================================================================== */
598
-
599
- /* Hide admin editor when proxy editor is open - ALL SCREEN SIZES */
600
- .admin-editor-section.landscape-hidden {
601
- display: none !important;
602
- }
603
-
604
- /* ==========================================================================
605
- PROXY EDITOR SECTION CONDITIONAL VISIBILITY
614
+ CONDITIONAL VISIBILITY - Required for editor switching logic
606
615
  ========================================================================== */
607
-
608
- /* Hide proxy editor when admin editor is open - ALL SCREEN SIZES */
616
+ .admin-editor-section.landscape-hidden,
609
617
  .proxy-editor-section.landscape-hidden {
610
618
  display: none !important;
611
619
  }
612
620
 
613
621
  /* ==========================================================================
614
- SECTION HEADERS
615
- ========================================================================== */
616
-
617
- /* ==========================================================================
618
- ADMIN FILE DROPDOWN STYLING
622
+ FILE DROPDOWN STYLING
619
623
  ========================================================================== */
620
-
621
624
  .admin-file-dropdown {
622
625
  height: 2.5rem !important;
623
- border-top-right-radius: 0 !important;
624
- border-bottom-right-radius: 0 !important;
625
- border-top-left-radius: 0.5rem !important;
626
- border-bottom-left-radius: 0.5rem !important;
627
626
  }
628
627
 
629
628
  .proxy-file-dropdown {
629
+ @apply rounded-lg;
630
630
  height: 2.5rem !important;
631
- border-radius: 0.5rem !important;
632
631
  }
633
632
 
634
633
  .refresh-button {
635
634
  height: 2.5rem !important;
636
- transition: all 0.2s ease;
637
635
 
638
636
  &:hover {
639
637
  .refresh-icon {
640
- transform: rotate(180deg);
638
+ @apply rotate-180;
641
639
  }
642
640
  }
643
641
  }
644
642
 
645
643
  .refresh-icon {
646
- transition: transform 0.5s ease;
644
+ @apply transition-transform duration-500;
647
645
  }
648
646
 
649
647
  /* ==========================================================================
650
648
  EDITOR CONTAINERS
651
649
  ========================================================================== */
652
-
653
- .editor-container,
654
- .proxy-editor-container {
650
+ .editor-container {
655
651
  position: relative;
656
- overflow: hidden;
657
652
  height: 400px;
658
653
  max-height: 60vh;
659
- border-radius: 8px;
660
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
661
- background-color: oklch(0.19 0 0);
662
- transition: height 0.3s ease;
663
654
  }
664
655
 
665
656
  .proxy-editor-container {
666
- /* Ensure proxy editor takes full container space */
667
- display: flex;
668
- flex-direction: column;
657
+ @apply relative overflow-hidden rounded-lg shadow-card bg-dark-350 flex flex-col;
658
+ height: 400px;
659
+ max-height: 60vh;
660
+ transition: height 0.3s ease;
669
661
  }
670
662
 
671
663
  .proxy-editor-container .pb-4 {
672
- /* Remove bottom padding to maximize space */
664
+ @apply flex-1 flex flex-col;
673
665
  padding-bottom: 0 !important;
674
- flex: 1;
675
- display: flex;
676
- flex-direction: column;
677
666
  }
678
667
 
679
668
  .proxy-editor-container .table-component {
680
- /* Make proxy editor container take full height */
681
- flex: 1;
682
- display: flex;
683
- flex-direction: column;
684
- }
685
-
686
- .editor-wrapper {
687
- position: relative;
688
- width: 100%;
689
- height: 100%;
690
- overflow: hidden;
691
- border-radius: 8px;
669
+ @apply flex-1 flex flex-col;
692
670
  }
693
671
 
694
672
  /* ==========================================================================
695
- EDITOR TEXTAREAS
673
+ EDITOR TEXTAREAS - CRITICAL: Custom font, scrolling behavior
696
674
  ========================================================================== */
697
-
698
- .code-editor,
699
675
  .proxy-editor {
676
+ @apply w-full h-full overflow-auto resize-none border-none outline-none bg-transparent p-3 m-0 box-border text-light-300 flex-1;
700
677
  font-family: "JetBrains Mono", "Fira Code", "Menlo", "Monaco", "Courier New", monospace;
701
678
  font-size: 14px;
702
679
  line-height: 1.6;
703
680
  tab-size: 4;
704
- background-color: transparent;
705
- border: none;
706
- outline: none;
707
- resize: none;
681
+ -webkit-overflow-scrolling: touch;
682
+ min-height: 100%;
683
+ }
684
+
685
+ /* EXACT COPY from FilterPreview.vue - Shared base styles for perfect alignment */
686
+ .code-editor,
687
+ .code-highlight {
688
+ position: absolute;
689
+ inset: 0;
708
690
  width: 100%;
709
691
  height: 100%;
710
- overflow: auto;
711
- -webkit-overflow-scrolling: touch;
692
+ min-height: 300px;
693
+ max-height: 500px;
712
694
  padding: 12px;
713
695
  margin: 0;
696
+ overflow: auto;
697
+ white-space: pre;
698
+ font-family: "JetBrains Mono", "Fira Code", "Menlo", "Monaco", "Courier New", monospace;
699
+ font-size: 14px;
700
+ line-height: 1.6;
701
+ tab-size: 4;
702
+ -moz-tab-size: 4;
703
+ word-wrap: normal;
704
+ word-break: normal;
705
+ border: 0;
706
+ outline: none;
707
+ background: transparent;
714
708
  box-sizing: border-box;
715
709
  }
716
710
 
711
+ /* Code editor layer - interactive */
717
712
  .code-editor {
718
- /* Make textarea text transparent so syntax highlighting shows through */
719
- color: transparent !important;
720
713
  z-index: 2;
721
- position: relative;
722
- caret-color: #ffffff; /* Keep cursor visible */
723
-
724
- /* Selection styling */
725
- &::selection {
726
- background-color: rgba(255, 255, 255, 0.2);
727
- }
714
+ color: transparent !important;
715
+ caret-color: #ffffff;
716
+ resize: none;
728
717
  }
729
718
 
730
- .proxy-editor {
731
- @apply w-full h-full resize-none focus:outline-none;
732
- color: oklch(0.90 0 0); /* Keep normal text color for proxy editor */
733
- /* Ensure proxy editor takes full height */
734
- flex: 1;
735
- min-height: 100%;
719
+ .code-editor::selection {
720
+ background-color: rgba(38, 79, 120, 0.6);
721
+ color: transparent;
736
722
  }
737
723
 
738
- /* ==========================================================================
739
- CODE HIGHLIGHTING
740
- ========================================================================== */
724
+ .code-editor::-moz-selection {
725
+ background-color: rgba(38, 79, 120, 0.6);
726
+ color: transparent;
727
+ }
741
728
 
729
+ /* Highlight layer - visual only */
742
730
  .code-highlight {
743
- font-family: "JetBrains Mono", "Fira Code", "Menlo", "Monaco", "Courier New", monospace;
744
- font-size: 14px;
745
- line-height: 1.6;
746
- tab-size: 4;
747
- /* Remove transparent color - let syntax highlighting show */
748
- color: #f8f8f2 !important;
749
- background: transparent;
750
- border: none;
751
- outline: none;
752
- resize: none;
753
- width: 100%;
754
- height: 100%;
755
- overflow: hidden;
756
- white-space: pre;
757
- pointer-events: none;
758
731
  z-index: 1;
759
- position: absolute;
760
- top: 0;
761
- left: 0;
762
- padding: 12px;
763
- margin: 0;
764
- box-sizing: border-box;
732
+ pointer-events: none;
733
+ user-select: none;
734
+ -webkit-user-select: none;
735
+ -moz-user-select: none;
765
736
  }
766
737
 
767
738
  /* ==========================================================================
768
- PRISM.JS SYNTAX HIGHLIGHTING THEME
739
+ PRISM.JS SYNTAX HIGHLIGHTING THEME - CRITICAL: Token colors
769
740
  ========================================================================== */
770
-
771
741
  code[class*="language-"],
772
742
  pre[class*="language-"] {
743
+ @apply bg-transparent text-left;
773
744
  color: #f8f8f2;
774
- background: none;
775
- text-shadow: 0 1px rgba(0, 0, 0, 0.3);
745
+ text-shadow: 0 1px oklch(0 0 0 / 0.3);
776
746
  font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
777
- text-align: left;
778
747
  white-space: pre;
779
748
  word-spacing: normal;
780
749
  word-break: normal;
@@ -784,43 +753,46 @@ pre[class*="language-"] {
784
753
  hyphens: none;
785
754
  }
786
755
 
787
- pre[class*="language-"] {
788
- padding: 12px;
789
- margin: 0;
790
- overflow: auto;
791
- border-radius: 4px;
756
+ pre[class*="language-"]:not(.code-highlight) {
757
+ @apply p-3 m-0 overflow-auto rounded;
792
758
  font-family: "Menlo", "Monaco", "Courier New", monospace;
793
759
  font-size: 14px;
794
760
  line-height: 1.5;
795
761
  tab-size: 4;
796
762
  }
797
763
 
764
+ /* Override for code-highlight to match code-editor */
765
+ .code-highlight.language-json,
766
+ .code-highlight[class*="language-"] {
767
+ line-height: 1.6 !important;
768
+ font-family: "JetBrains Mono", "Fira Code", "Menlo", "Monaco", "Courier New", monospace !important;
769
+ }
770
+
798
771
  :not(pre) > code[class*="language-"],
799
772
  pre[class*="language-"] {
800
- background: oklch(0.19 0 0);
801
- white-space: pre;
773
+ @apply bg-dark-350 whitespace-pre;
802
774
  }
803
775
 
804
776
  :not(pre) > code[class*="language-"] {
777
+ @apply whitespace-normal rounded-sm;
805
778
  padding: 0.1em;
806
- border-radius: 0.3em;
807
- white-space: normal;
808
779
  }
809
780
 
781
+ /* CRITICAL: VSCode Dark+ theme for syntax highlighting */
810
782
  .token.comment,
811
783
  .token.prolog,
812
784
  .token.doctype,
813
785
  .token.cdata {
814
- color: #676f7d;
815
786
  font-style: italic;
787
+ color: #6a9955;
816
788
  }
817
789
 
818
790
  .token.punctuation {
819
- color: #a9b7c6;
791
+ color: #d4d4d4;
820
792
  }
821
793
 
822
794
  .namespace {
823
- opacity: 0.8;
795
+ @apply opacity-80;
824
796
  }
825
797
 
826
798
  .token.property,
@@ -828,12 +800,12 @@ pre[class*="language-"] {
828
800
  .token.constant,
829
801
  .token.symbol,
830
802
  .token.deleted {
831
- color: #e06c75;
803
+ color: #b5cea8;
832
804
  }
833
805
 
834
806
  .token.boolean,
835
807
  .token.number {
836
- color: #c792ea;
808
+ color: #b5cea8;
837
809
  }
838
810
 
839
811
  .token.selector,
@@ -842,32 +814,35 @@ pre[class*="language-"] {
842
814
  .token.char,
843
815
  .token.builtin,
844
816
  .token.inserted {
845
- color: #98c379;
817
+ color: #ce9178;
846
818
  }
847
819
 
848
820
  .token.operator,
849
821
  .token.entity,
850
- .token.url,
822
+ .token.url {
823
+ color: #d4d4d4;
824
+ }
825
+
851
826
  .language-css .token.string,
852
827
  .style .token.string {
853
- color: #89ddff;
828
+ color: #ce9178;
854
829
  }
855
830
 
856
831
  .token.atrule,
857
832
  .token.attr-value,
858
833
  .token.keyword {
859
- color: #61afef;
834
+ color: #c586c0;
860
835
  }
861
836
 
862
837
  .token.function,
863
838
  .token.class-name {
864
- color: #ffcb6b;
839
+ color: #dcdcaa;
865
840
  }
866
841
 
867
842
  .token.regex,
868
843
  .token.important,
869
844
  .token.variable {
870
- color: #c5e478;
845
+ color: #9cdcfe;
871
846
  }
872
847
 
873
848
  .token.important,
@@ -880,25 +855,19 @@ pre[class*="language-"] {
880
855
  }
881
856
 
882
857
  .token.entity {
883
- cursor: help;
858
+ @apply cursor-help;
884
859
  }
885
860
 
886
861
  .editor-container:hover {
887
- box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3);
862
+ @apply shadow-xl;
888
863
  transition: box-shadow 0.3s ease;
889
864
  }
890
865
 
891
866
  /* ==========================================================================
892
867
  ERROR MESSAGES
893
868
  ========================================================================== */
894
-
895
869
  .error-container {
896
- @apply flex items-start gap-3 p-4 bg-red-500/10 border border-red-500/20 rounded-lg mt-4 mb-4;
897
-
898
- /* Prevent overflow in landscape mode */
899
- max-width: 100%;
900
- word-wrap: break-word;
901
- overflow-wrap: break-word;
870
+ @apply flex items-start gap-3 p-4 bg-red-500/10 border border-red-500/20 rounded-lg mt-4 mb-4 max-w-full break-words;
902
871
  }
903
872
 
904
873
  .error-icon {
@@ -906,8 +875,7 @@ pre[class*="language-"] {
906
875
  }
907
876
 
908
877
  .error-content {
909
- @apply flex-1;
910
- min-width: 0; /* Allow text to shrink */
878
+ @apply flex-1 min-w-0;
911
879
  }
912
880
 
913
881
  .error-title {
@@ -915,72 +883,39 @@ pre[class*="language-"] {
915
883
  }
916
884
 
917
885
  .error-text {
918
- @apply text-red-300 text-xs;
919
- word-break: break-word;
886
+ @apply text-red-300 text-xs break-words;
920
887
  }
921
888
 
922
889
  /* ==========================================================================
923
- SECTION DIVIDER
890
+ SECTION DIVIDER - CRITICAL: Gradient effect
924
891
  ========================================================================== */
925
-
926
892
  .section-divider {
927
- margin: 24px 0; /* Balanced margin for better centering */
893
+ @apply relative w-full border-none;
894
+ margin: 24px 0;
928
895
  height: 1px;
929
- width: 100%;
930
- position: relative;
931
-
932
- /* Add subtle gradient effect for better visual separation */
933
896
  background: linear-gradient(90deg, transparent 0%, oklch(0.26 0 0) 20%, oklch(0.26 0 0) 80%, transparent 100%);
934
- border: none;
935
-
936
- /* Add a subtle glow effect */
937
- box-shadow: 0 0 8px rgba(61, 62, 68, 0.3);
897
+ box-shadow: 0 0 8px oklch(0.26 0 0 / 0.3);
938
898
  }
939
899
 
940
- /* ==========================================================================
941
- RESPONSIVE LAYOUT UTILITIES
942
- ========================================================================== */
943
-
944
- .flex-responsive-wrap {
945
- @apply flex flex-wrap items-center gap-4;
946
- }
947
-
948
-
949
900
  /* ==========================================================================
950
901
  MOBILE RESPONSIVE STYLES (max-width: 767px)
951
902
  ========================================================================== */
952
-
953
903
  @media (max-width: 767px) {
954
- /* Layout adjustments - almost no gap between dropdown and buttons */
955
- .flex-responsive-wrap {
956
- @apply flex-col items-stretch !important;
957
- gap: 0px !important; /* Reduced from 2px to eliminate space between dropdown and buttons */
958
- }
959
-
960
- /* Button container - even spacing across full width */
961
- .flex-responsive-wrap > div:last-child {
962
- @apply flex justify-center !important;
963
- }
964
-
965
- /* Editor container heights - fill available space on mobile */
966
904
  .editor-container,
967
905
  .proxy-editor-container {
968
- height: calc(100vh - 380px) !important; /* Fill viewport minus header/controls */
969
- max-height: 60vh !important; /* Allow more height */
970
- min-height: 300px !important; /* Ensure minimum usable height */
906
+ height: calc(100vh - 380px) !important;
907
+ max-height: 60vh !important;
908
+ min-height: 300px !important;
971
909
  }
972
910
 
973
- /* Reduce editor height when JSON error is showing in portrait */
974
911
  .editor-container.has-error {
975
- height: calc(100vh - 460px) !important; /* Account for error message */
912
+ height: calc(100vh - 460px) !important;
976
913
  max-height: 50vh !important;
977
914
  min-height: 250px !important;
978
915
  }
979
916
 
980
- /* Error message spacing on mobile */
981
917
  .error-container {
982
- margin: 8px 0 !important;
983
- padding: 8px !important;
918
+ @apply my-2 p-2;
984
919
  font-size: 11px !important;
985
920
  }
986
921
 
@@ -992,36 +927,18 @@ pre[class*="language-"] {
992
927
  font-size: 10px !important;
993
928
  }
994
929
 
995
- /* Compact editor controls spacing - no margins */
996
- .editor-controls-row,
997
- .proxy-controls-row {
998
- margin-bottom: 0px !important; /* Eliminated margin completely */
999
- }
1000
-
1001
- /* Compact editor containers - reduced margins for more space */
1002
930
  .my-3 {
1003
- margin: 6px 0 !important; /* Reduced margin between editor and controls */
931
+ @apply my-1.5;
1004
932
  }
1005
933
 
1006
934
  .pb-4 {
1007
- padding-bottom: 4px !important; /* Reduced padding */
935
+ @apply pb-1;
1008
936
  }
1009
937
  }
1010
938
 
1011
939
  /* ==========================================================================
1012
- TABLET RESPONSIVE STYLES (min-width: 768px)
940
+ DESKTOP RESPONSIVE STYLES
1013
941
  ========================================================================== */
1014
-
1015
- @media (min-width: 768px) {
1016
- .flex-responsive-wrap {
1017
- @apply flex-row justify-between items-center !important;
1018
- }
1019
- }
1020
-
1021
- /* ==========================================================================
1022
- DESKTOP RESPONSIVE STYLES (min-width: 1024px)
1023
- ========================================================================== */
1024
-
1025
942
  @media (min-width: 1024px) {
1026
943
  .editor-container,
1027
944
  .proxy-editor-container {
@@ -1029,10 +946,6 @@ pre[class*="language-"] {
1029
946
  }
1030
947
  }
1031
948
 
1032
- /* ==========================================================================
1033
- LARGE DESKTOP RESPONSIVE STYLES (min-width: 1280px)
1034
- ========================================================================== */
1035
-
1036
949
  @media (min-width: 1280px) {
1037
950
  .editor-container,
1038
951
  .proxy-editor-container {
@@ -1041,32 +954,17 @@ pre[class*="language-"] {
1041
954
  }
1042
955
 
1043
956
  /* ==========================================================================
1044
- MOBILE LANDSCAPE MODE (max-height: 500px + landscape)
957
+ LANDSCAPE MODE - CRITICAL: Complex responsive behavior
1045
958
  ========================================================================== */
1046
-
1047
959
  @media (max-height: 500px) and (orientation: landscape) and (min-width: 568px) {
1048
- /* Truncate card cleanly when admin editor is open */
1049
960
  .admin-editor-section:not(.landscape-hidden) + .section-divider + .proxy-editor-section.landscape-hidden {
1050
- display: none !important;
1051
- }
1052
-
1053
- /* Ensure proxy editor takes full space when visible */
1054
- .proxy-editor-container {
1055
- height: 130px !important;
1056
- max-height: 130px !important;
1057
- min-height: 100px !important;
1058
- }
1059
-
1060
- .proxy-editor {
1061
- height: 100% !important;
1062
- min-height: 100% !important;
961
+ @apply hidden;
1063
962
  }
1064
963
 
1065
- /* Compact headings */
1066
964
  h4 {
965
+ @apply pt-4;
1067
966
  font-size: 16px !important;
1068
967
  margin-bottom: 6px !important;
1069
- padding-top: 1rem !important;
1070
968
  }
1071
969
 
1072
970
  h5 {
@@ -1074,170 +972,109 @@ pre[class*="language-"] {
1074
972
  margin-bottom: 4px !important;
1075
973
  }
1076
974
 
1077
- /* Section divider */
1078
975
  .section-divider {
1079
- margin: 6px 0 !important;
976
+ @apply my-1.5;
1080
977
  }
1081
978
 
1082
- /* Editor controls layout */
1083
- .editor-controls-row,
1084
- .proxy-controls-row {
1085
- gap: 6px !important;
1086
- }
1087
-
1088
- /* Hide button text to save space in landscape mode */
1089
- .button-default span {
1090
- display: none !important;
1091
- }
1092
-
1093
- /* Compact editor containers - reduced margins for more space */
1094
979
  .my-3 {
1095
- margin: 4px 0 !important; /* Reduced from 6px */
980
+ @apply my-1;
1096
981
  }
1097
982
 
1098
983
  .pb-4 {
1099
- padding-bottom: 2px !important; /* Reduced from 4px */
984
+ @apply pb-0.5;
1100
985
  }
1101
986
 
1102
- /* Editor container heights - increased to use more available space */
1103
987
  .editor-container,
1104
988
  .proxy-editor-container {
1105
- height: 180px !important; /* Increased from 130px */
989
+ height: 180px !important;
1106
990
  max-height: 180px !important;
1107
- min-height: 150px !important; /* Increased from 100px */
991
+ min-height: 150px !important;
1108
992
  }
1109
993
 
1110
- /* Reduce editor height when JSON error is showing */
1111
994
  .editor-container.has-error {
1112
- height: 120px !important; /* Reduced to make room for error message */
995
+ height: 120px !important;
1113
996
  max-height: 120px !important;
1114
997
  min-height: 100px !important;
1115
998
  }
1116
999
 
1117
- /* Ensure proxy editor takes full space when visible - increased height */
1118
- .proxy-editor-container {
1119
- height: 180px !important; /* Increased from 130px */
1120
- max-height: 180px !important;
1121
- min-height: 150px !important;
1000
+ .proxy-editor {
1001
+ @apply h-full;
1002
+ min-height: 100% !important;
1122
1003
  }
1123
1004
 
1124
- /* Editor textarea heights - ensure syntax highlighting works */
1125
- .code-editor,
1126
1005
  .proxy-editor {
1127
- font-size: 11px !important;
1006
+ @apply p-1.5 text-xs;
1128
1007
  line-height: 1.4 !important;
1129
- padding: 6px !important;
1130
1008
  }
1131
1009
 
1132
- /* Syntax highlighting in landscape mode */
1010
+ .code-editor,
1133
1011
  .code-highlight {
1134
- font-size: 11px !important;
1135
- line-height: 1.4 !important;
1136
1012
  padding: 6px !important;
1137
- /* Ensure syntax highlighting is visible in landscape */
1138
- color: #f8f8f2 !important;
1139
- opacity: 1 !important;
1140
- display: block !important;
1013
+ font-size: 12px !important;
1014
+ line-height: 1.4 !important;
1141
1015
  }
1142
1016
 
1143
- /* Compact error messages */
1144
1017
  .error-container {
1145
- margin: 2px 0 !important; /* Reduced from 4px */
1146
- padding: 4px 6px !important; /* More compact padding */
1147
- font-size: 9px !important; /* Reduced from 10px */
1148
- border-radius: 4px !important; /* Smaller border radius */
1149
- gap: 6px !important; /* Reduced gap */
1018
+ @apply rounded gap-1.5 my-0.5 px-1.5 py-1;
1019
+ font-size: 9px !important;
1150
1020
  }
1151
1021
 
1152
1022
  .error-icon {
1153
- width: 12px !important;
1154
- height: 12px !important;
1155
- }
1156
-
1157
- .error-icon svg {
1158
- width: 12px !important;
1159
- height: 12px !important;
1023
+ @apply w-3 h-3;
1160
1024
  }
1161
1025
 
1162
1026
  .error-title {
1163
- font-size: 9px !important; /* Reduced from 10px */
1164
- margin-bottom: 2px !important;
1165
- font-weight: 500 !important;
1027
+ @apply mb-0.5 font-medium;
1028
+ font-size: 9px !important;
1166
1029
  }
1167
1030
 
1168
1031
  .error-text {
1169
- font-size: 8px !important; /* Reduced from 9px */
1032
+ font-size: 8px !important;
1170
1033
  line-height: 1.2 !important;
1171
1034
  }
1172
1035
  }
1173
1036
 
1174
- /* ==========================================================================
1175
- VERY SHORT LANDSCAPE SCREENS (max-height: 400px + landscape)
1176
- ========================================================================== */
1177
-
1037
+ /* Very short landscape screens */
1178
1038
  @media (max-height: 400px) and (orientation: landscape) {
1179
1039
  .editor-container,
1180
1040
  .proxy-editor-container {
1181
- height: 140px !important; /* Increased from 100px */
1041
+ height: 140px !important;
1182
1042
  max-height: 140px !important;
1183
- min-height: 120px !important; /* Increased from 80px */
1043
+ min-height: 120px !important;
1184
1044
  }
1185
1045
 
1186
- /* Reduce editor height when JSON error is showing in very short landscape */
1187
1046
  .editor-container.has-error {
1188
- height: 90px !important; /* Reduced to make room for error message */
1047
+ height: 90px !important;
1189
1048
  max-height: 90px !important;
1190
1049
  min-height: 80px !important;
1191
1050
  }
1192
1051
 
1193
- /* Ensure proxy editor takes full space in very short landscape */
1194
1052
  .proxy-editor {
1195
- height: 100% !important;
1053
+ @apply h-full p-1;
1196
1054
  min-height: 100% !important;
1197
1055
  font-size: 10px !important;
1198
1056
  line-height: 1.3 !important;
1199
- padding: 4px !important;
1200
- }
1201
-
1202
- .code-editor {
1203
- font-size: 10px !important;
1204
- line-height: 1.3 !important;
1205
- padding: 4px !important;
1206
1057
  }
1207
1058
 
1208
- /* Ensure syntax highlighting works in very short landscape */
1059
+ .code-editor,
1209
1060
  .code-highlight {
1061
+ padding: 4px !important;
1210
1062
  font-size: 10px !important;
1211
1063
  line-height: 1.3 !important;
1212
- padding: 4px !important;
1213
- color: #f8f8f2 !important;
1214
- opacity: 1 !important;
1215
- display: block !important;
1216
1064
  }
1217
1065
 
1218
- /* Ultra-compact error messages for very short landscape */
1219
1066
  .error-container {
1220
- margin: 1px 0 !important;
1221
- padding: 2px 4px !important;
1067
+ @apply rounded-sm gap-1 my-px px-1 py-0.5;
1222
1068
  font-size: 8px !important;
1223
- border-radius: 3px !important;
1224
- gap: 4px !important;
1225
1069
  }
1226
1070
 
1227
1071
  .error-icon {
1228
- width: 10px !important;
1229
- height: 10px !important;
1230
- }
1231
-
1232
- .error-icon svg {
1233
- width: 10px !important;
1234
- height: 10px !important;
1072
+ @apply w-2.5 h-2.5;
1235
1073
  }
1236
1074
 
1237
1075
  .error-title {
1076
+ @apply mb-px font-medium;
1238
1077
  font-size: 8px !important;
1239
- margin-bottom: 1px !important;
1240
- font-weight: 500 !important;
1241
1078
  }
1242
1079
 
1243
1080
  .error-text {
@@ -1247,37 +1084,32 @@ pre[class*="language-"] {
1247
1084
  }
1248
1085
 
1249
1086
  /* ==========================================================================
1250
- MOBILE & TABLET - COMPACT EDITOR BUTTONS FOR ONE-ROW LAYOUT
1087
+ COMPACT EDITOR BUTTONS
1251
1088
  ========================================================================== */
1252
-
1253
- /* Icon-sized buttons - compact for all screens */
1254
1089
  .button-default {
1255
- @apply h-7 w-7 p-0; // 28x28px on desktop/tablet
1090
+ @apply h-7 w-7 p-0 rounded-md;
1256
1091
  min-width: 28px !important;
1257
1092
  max-width: 28px !important;
1258
1093
  width: 28px !important;
1259
1094
  height: 28px !important;
1260
1095
  padding: 0 !important;
1261
- border-radius: 6px;
1262
1096
 
1263
1097
  svg {
1264
- @apply w-4 h-4; // 16x16px icons
1098
+ @apply w-4 h-4;
1265
1099
  }
1266
1100
  }
1267
1101
 
1268
- /* Even more compact on mobile */
1269
1102
  @media (max-width: 640px) {
1270
1103
  .button-default {
1271
- @apply h-6 w-6; // 24x24px on mobile
1104
+ @apply h-6 w-6;
1272
1105
  min-width: 24px !important;
1273
1106
  max-width: 24px !important;
1274
1107
  width: 24px !important;
1275
1108
  height: 24px !important;
1276
1109
 
1277
1110
  svg {
1278
- @apply w-3.5 h-3.5; // 14x14px icons
1111
+ @apply w-3.5 h-3.5;
1279
1112
  }
1280
1113
  }
1281
1114
  }
1282
-
1283
1115
  </style>