@necrolab/dashboard 0.4.39 → 0.4.40

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.
@@ -26,28 +26,48 @@
26
26
  <h3 class="text-sm text-white">Hide Monitors</h3>
27
27
  <Switch class="scale-75" v-model="filteredLogs" />
28
28
  </button>
29
- <button class="flex rounded gap-3 card-dark px-2 h-10 flex-center">
29
+ <button class="flex rounded gap-3 card-dark px-2 h-10 flex-center relative">
30
30
  <h3 class="text-sm text-white">Auto</h3>
31
- <Switch class="scale-75" v-model="autoscrollToggled" />
31
+ <Switch class="scale-75" v-model="autoscrollToggled" @change="onAutoscrollToggle" />
32
+ <div
33
+ v-if="userScrolledUp && autoscrollToggled"
34
+ class="absolute -top-1 -right-1 w-2 h-2 bg-yellow-500 rounded-full animate-pulse"
35
+ title="Autoscroll paused - scroll to bottom to resume"
36
+ ></div>
32
37
  </button>
33
38
  </div>
34
39
  <!-- Scroll buttons always visible -->
35
- <button class="rounded card-dark w-10 h-10 flex-center">
36
- <UpIcon class="cursor-pointer mx-1 lg:w-4 w-5 h-5" @click="scrollTo('up')" />
40
+ <button
41
+ class="rounded card-dark w-10 h-10 flex-center hover:bg-dark-300 active:bg-dark-200 transition-colors duration-150"
42
+ @mousedown="startScrolling('up')"
43
+ @mouseup="stopScrolling"
44
+ @mouseleave="stopScrolling"
45
+ @touchstart="startScrolling('up')"
46
+ @touchend="stopScrolling"
47
+ >
48
+ <UpIcon class="w-5 h-5 pointer-events-none" />
37
49
  </button>
38
- <button class="rounded card-dark w-10 h-10 flex-center">
39
- <DownIcon class="cursor-pointer ml-1 lg:w-4 w-5 h-5" @click="scrollTo('down')" />
50
+ <button
51
+ class="rounded card-dark w-10 h-10 flex-center hover:bg-dark-300 active:bg-dark-200 transition-colors duration-150"
52
+ @mousedown="startScrolling('down')"
53
+ @mouseup="stopScrolling"
54
+ @mouseleave="stopScrolling"
55
+ @touchstart="startScrolling('down')"
56
+ @touchend="stopScrolling"
57
+ >
58
+ <DownIcon class="w-5 h-5 pointer-events-none" />
40
59
  </button>
41
60
  </div>
42
61
  </div>
43
62
 
44
63
  <Smoothie
45
- :weight="0.1"
46
- class="console overflow-y-auto overflow-x-hidden font-mono text-white scrollable"
64
+ :weight="0.2"
65
+ class="console overflow-y-auto overflow-x-hidden font-mono text-white scrollable smooth-scroll"
47
66
  style="min-height: 14rem !important"
48
67
  ref="$autoscroll"
49
68
  @wheel.stop
50
69
  @touchmove.stop
70
+ @scroll="handleScroll"
51
71
  >
52
72
  <div
53
73
  v-if="
@@ -64,11 +84,12 @@
64
84
  </div>
65
85
  <pre
66
86
  v-else
67
- class="hidden-scrollbars"
87
+ class="hidden-scrollbars log-entry"
68
88
  v-for="(line, index) in currentTaskLog && currentTaskLog !== ''
69
89
  ? taskLogMapping[currentTaskLog]
70
90
  : logLines.filter((l) => (filteredLogs ? !['-DISCORD'].some((s) => l.includes(s)) : true))"
71
91
  v-bind:key="`log-${index}`"
92
+ :style="{ '--index': index }"
72
93
  ><code class="md:text-sm lg:text-base" v-html="line"></code></pre>
73
94
  </Smoothie>
74
95
  <div class="flex ipadlg:hidden justify-between mt-3">
@@ -76,9 +97,14 @@
76
97
  <h3 class="text-sm text-white">Hide Monitors</h3>
77
98
  <Switch class="scale-75" v-model="filteredLogs" />
78
99
  </button>
79
- <button class="flex rounded gap-3 card-dark px-2 h-10 flex-center">
100
+ <button class="flex rounded gap-3 card-dark px-2 h-10 flex-center relative">
80
101
  <h3 class="text-sm text-white">Auto</h3>
81
- <Switch class="scale-75" v-model="autoscrollToggled" />
102
+ <Switch class="scale-75" v-model="autoscrollToggled" @change="onAutoscrollToggle" />
103
+ <div
104
+ v-if="userScrolledUp && autoscrollToggled"
105
+ class="absolute -top-1 -right-1 w-2 h-2 bg-yellow-500 rounded-full animate-pulse"
106
+ title="Autoscroll paused - scroll to bottom to resume"
107
+ ></div>
82
108
  </button>
83
109
  </div>
84
110
  </div>
@@ -99,17 +125,52 @@
99
125
 
100
126
  &::-webkit-scrollbar-track {
101
127
  background: #2e2f34;
128
+ border-radius: 4px;
102
129
  }
103
130
 
104
131
  &::-webkit-scrollbar-thumb {
105
132
  background: #555;
106
133
  border-radius: 4px;
134
+ transition: background-color 0.2s ease;
107
135
  }
108
136
 
109
137
  &::-webkit-scrollbar-thumb:hover {
110
138
  background: #777;
111
139
  }
112
140
 
141
+ // Smooth scrolling behavior with momentum
142
+ &.smooth-scroll {
143
+ scroll-behavior: smooth;
144
+ scroll-padding: 0.5rem;
145
+ -webkit-overflow-scrolling: touch;
146
+ overscroll-behavior: contain;
147
+ }
148
+
149
+ // Improved log entry animations
150
+ .log-entry {
151
+ opacity: 0;
152
+ transform: translateY(4px);
153
+ animation: slideInLog 0.2s ease-out forwards;
154
+ transition: all 0.15s ease;
155
+
156
+ &:hover {
157
+ background-color: rgba(255, 255, 255, 0.02);
158
+ transform: translateX(2px);
159
+ }
160
+ }
161
+
162
+ // Stagger animation for new logs
163
+ .log-entry:last-child {
164
+ animation-delay: 0.05s;
165
+ }
166
+
167
+ @keyframes slideInLog {
168
+ to {
169
+ opacity: 1;
170
+ transform: translateY(0);
171
+ }
172
+ }
173
+
113
174
  // Empty state styling
114
175
  .empty-state {
115
176
  min-height: 14rem;
@@ -175,7 +236,7 @@ import Filter from "@/libs/ansii.js";
175
236
  import { ConsoleIcon, DownIcon, UpIcon } from "@/components/icons";
176
237
  import Switch from "@/components/ui/controls/atomic/Switch.vue";
177
238
  import WebsocketHeartbeatJs from "websocket-heartbeat-js";
178
- import { onMounted, ref, nextTick } from "vue";
239
+ import { onMounted, onUnmounted, ref, nextTick, computed, watch } from "vue";
179
240
  import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
180
241
  import { sortAlphaNum } from "@/stores/utils";
181
242
 
@@ -190,29 +251,142 @@ const autoscrollToggled = ref(true);
190
251
  const taskLogMapping = ref({});
191
252
  const currentTaskLog = ref("");
192
253
  const filteredLogs = ref(true);
254
+ const userScrolledUp = ref(false);
255
+ const lastScrollTime = ref(0);
256
+ const scrollInterval = ref(null);
257
+ const isScrolling = ref(false);
193
258
 
194
259
  const path = "/api/updates?type=console";
195
260
  const url = (window.location.protocol === "http:" ? "ws://" : "wss://") + window.location.host + path;
196
- const scrollTo = (dir) => {
261
+ // Handle manual scroll detection
262
+ const handleScroll = (event) => {
263
+ if (!autoscrollToggled.value) return;
264
+
265
+ const element = event.target;
266
+ if (!element) return;
267
+
268
+ // Check if user is near bottom (within 50px)
269
+ const threshold = 50;
270
+ const isNearBottom = element.scrollTop + element.clientHeight >= element.scrollHeight - threshold;
271
+
272
+ // Only update if there's an actual change
273
+ if (userScrolledUp.value === isNearBottom) {
274
+ userScrolledUp.value = !isNearBottom;
275
+ }
276
+ };
277
+
278
+ // Bulletproof scroll function with multiple fallbacks
279
+ const performScroll = (direction, smooth = true) => {
197
280
  try {
198
281
  if (!$autoscroll.value?.el) {
199
282
  if (DEBUG) console.log("Autoscroll element not ready");
200
- return;
283
+ return false;
201
284
  }
202
- if (dir === "up") $autoscroll.value.el.scrollTop = 0;
203
- else $autoscroll.value.el.scrollTop = parseInt($autoscroll.value.el.children[1].style.height);
285
+
286
+ const element = $autoscroll.value.el;
287
+
288
+ if (direction === "up") {
289
+ if (smooth && element.scrollTo) {
290
+ element.scrollTo({ top: 0, behavior: "smooth" });
291
+ } else {
292
+ element.scrollTop = 0;
293
+ }
294
+ } else {
295
+ const targetTop = element.scrollHeight - element.clientHeight;
296
+ if (smooth && element.scrollTo) {
297
+ element.scrollTo({ top: targetTop, behavior: "smooth" });
298
+ } else {
299
+ element.scrollTop = targetTop;
300
+ }
301
+ userScrolledUp.value = false;
302
+ }
303
+
304
+ return true;
204
305
  } catch (e) {
205
306
  if (DEBUG) console.log("Error scrolling", e);
307
+ return false;
308
+ }
309
+ };
310
+
311
+ // Continuous scrolling for held buttons
312
+ const startScrolling = (direction) => {
313
+ if (isScrolling.value) return;
314
+
315
+ isScrolling.value = true;
316
+
317
+ // Immediate scroll
318
+ performScroll(direction, true);
319
+
320
+ // Continue scrolling while held (for long content)
321
+ scrollInterval.value = setInterval(() => {
322
+ if (!isScrolling.value) return;
323
+
324
+ const element = $autoscroll.value?.el;
325
+ if (!element) return;
326
+
327
+ const scrollAmount = 100;
328
+ if (direction === "up") {
329
+ element.scrollTop = Math.max(0, element.scrollTop - scrollAmount);
330
+ } else {
331
+ element.scrollTop = Math.min(element.scrollHeight - element.clientHeight, element.scrollTop + scrollAmount);
332
+ }
333
+ }, 50);
334
+ };
335
+
336
+ const stopScrolling = () => {
337
+ isScrolling.value = false;
338
+ if (scrollInterval.value) {
339
+ clearInterval(scrollInterval.value);
340
+ scrollInterval.value = null;
341
+ }
342
+ };
343
+
344
+ // Legacy function for compatibility
345
+ const scrollTo = (dir) => {
346
+ performScroll(dir, true);
347
+ };
348
+
349
+ // Simple autoscroll to bottom
350
+ const autoScrollToBottom = () => {
351
+ if (!$autoscroll.value?.el || !autoscrollToggled.value) return;
352
+
353
+ // Only scroll if user hasn't manually scrolled up
354
+ if (!userScrolledUp.value) {
355
+ const element = $autoscroll.value.el;
356
+
357
+ // Calculate the target scroll position to show the last log
358
+ const targetScrollTop = element.scrollHeight - element.clientHeight;
359
+
360
+ // Use smooth scrolling to ensure new logs are visible
361
+ if (element.scrollTo && Math.abs(element.scrollTop - targetScrollTop) > 5) {
362
+ element.scrollTo({
363
+ top: targetScrollTop,
364
+ behavior: 'smooth'
365
+ });
366
+ } else {
367
+ // For small differences or fallback, use instant scroll
368
+ element.scrollTop = targetScrollTop;
369
+ }
370
+ }
371
+ };
372
+
373
+ // Handle autoscroll toggle
374
+ const onAutoscrollToggle = () => {
375
+ if (autoscrollToggled.value) {
376
+ userScrolledUp.value = false;
377
+ nextTick().then(autoScrollToBottom);
206
378
  }
207
379
  };
208
380
 
209
381
  const addAnsiToOutput = (a) => {
210
382
  const html = ansii.toHtml(a?.log || a);
211
383
  logLines.value.push(html);
212
- if (autoscrollToggled.value)
213
- nextTick().then(() => {
214
- scrollTo("down");
215
- });
384
+
385
+ // Auto scroll after adding new content with proper timing
386
+ nextTick().then(() => {
387
+ // Use a small delay to ensure DOM is fully updated
388
+ setTimeout(autoScrollToBottom, 10);
389
+ });
216
390
  };
217
391
 
218
392
  const handleWebsocketMessages = (msg) => {
@@ -259,11 +433,19 @@ window.startDebugConsoleMessages = () => {
259
433
  };
260
434
  addAnsiToOutput(log);
261
435
  makeTaskLogMapping([log]);
262
- }, 500);
436
+ }, 100);
263
437
  };
264
438
 
265
439
  if (DEBUG) window.startDebugConsoleMessages();
266
440
 
441
+ // Watch for log filter changes and reset scroll state
442
+ watch([currentTaskLog, filteredLogs], () => {
443
+ userScrolledUp.value = false;
444
+ nextTick().then(() => {
445
+ setTimeout(autoScrollToBottom, 10);
446
+ });
447
+ });
448
+
267
449
  // Listen for messages
268
450
  onMounted(() => {
269
451
  const socket = new WebsocketHeartbeatJs({ url, pingMsg: "ping" });
@@ -276,4 +458,9 @@ onMounted(() => {
276
458
  });
277
459
  };
278
460
  });
461
+
462
+ // Cleanup on unmount
463
+ onUnmounted(() => {
464
+ stopScrolling();
465
+ });
279
466
  </script>
@@ -686,10 +686,6 @@ loadProxyLists();
686
686
  overflow: auto;
687
687
  }
688
688
 
689
- /* Enhance the text selection color */
690
- .code-editor::selection {
691
- background: rgba(98, 114, 164, 0.4);
692
- }
693
689
 
694
690
  /* iOS keyboard focus fix and scrolling */
695
691
  .code-editor:focus,
@@ -788,8 +784,8 @@ textarea.proxy-editor {
788
784
  }
789
785
 
790
786
  .proxy-editor-container:focus-within {
791
- border-color: #5d7cc0;
792
- box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.4);
787
+ border-color: #44454b;
788
+ box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.2);
793
789
  }
794
790
 
795
791
  /* Prism.js dark theme */
@@ -10,12 +10,10 @@
10
10
  <input
11
11
  class="h-10 text-white text-sm p-2 bg-dark-500 w-48 flex items-center rounded-l rounded-r-none relative border-2 border-dark-550"
12
12
  placeholder="Event ID"
13
- v-model="eventId"
14
- />
13
+ v-model="eventId" />
15
14
  <button
16
15
  class="h-10 text-white text-sm px-3 bg-dark-400 flex items-center rounded-r relative font-medium border-2 border-dark-550 smooth-hover"
17
- @click="updateShownVenue"
18
- >
16
+ @click="updateShownVenue">
19
17
  Load
20
18
  </button>
21
19
  </div>
@@ -27,8 +25,7 @@
27
25
  <div class="grid grid-cols-1 lg:grid-cols-5 gap-3 lg:gap-4 w-full h-full">
28
26
  <!-- Map -->
29
27
  <div
30
- class="col-span-1 lg:col-span-3 w-full h-full rounded-lg relative flex flex-col lg:max-w-none lg:overflow-hidden"
31
- >
28
+ class="col-span-1 lg:col-span-3 w-full h-full rounded-lg relative flex flex-col lg:max-w-none lg:overflow-hidden">
32
29
  <div v-if="svg" class="flex items-center mb-1">
33
30
  <div class="flex items-center justify-between w-20 px-2 text-white font-black text-sm">
34
31
  <span class="cursor-pointer" @click="handleZoom(true)">+</span>
@@ -39,25 +36,21 @@
39
36
  <div class="overflow-hidden selecto-wrapper flex-1">
40
37
  <div
41
38
  v-if="svg"
42
- class="h-full overflow-auto hidden-scrollbars p-2 rounded shadow border-2 border-dark-550 svg-container"
43
- >
39
+ class="h-full overflow-auto hidden-scrollbars p-2 rounded shadow border-2 border-dark-550 svg-container">
44
40
  <div class="svg-wrapper" id="svg-wrapper" v-html="svg"></div>
45
41
  </div>
46
42
  <div
47
43
  v-else
48
- class="h-full flex items-center justify-center p-2 rounded shadow border-2 border-dark-550 svg-container"
49
- >
44
+ class="h-full flex items-center justify-center p-2 rounded shadow border-2 border-dark-550 svg-container">
50
45
  <div class="text-center">
51
46
  <svg
52
47
  class="w-12 h-12 mb-3 mx-auto opacity-50"
53
48
  viewBox="0 0 19 19"
54
49
  fill="none"
55
- xmlns="http://www.w3.org/2000/svg"
56
- >
50
+ xmlns="http://www.w3.org/2000/svg">
57
51
  <path
58
52
  d="M2.37499 5.54165V2.37498L5.54166 3.95831L2.37499 5.54165ZM14.25 5.54165V2.37498L17.4167 3.95831L14.25 5.54165ZM8.70833 4.74998V1.58331L11.875 3.16665L8.70833 4.74998ZM8.70833 17.4166C7.70555 17.3903 6.77218 17.3079 5.9082 17.1696C5.0437 17.0308 4.29162 16.8559 3.65195 16.6448C3.01176 16.4337 2.50694 16.1896 2.13749 15.9125C1.76805 15.6354 1.58333 15.3451 1.58333 15.0416V7.91665C1.58333 7.58678 1.79127 7.27988 2.20716 6.99594C2.62252 6.71252 3.18645 6.46183 3.89895 6.24385C4.61145 6.02641 5.4493 5.85488 6.4125 5.72927C7.37569 5.60419 8.40486 5.54165 9.49999 5.54165C10.5951 5.54165 11.6243 5.60419 12.5875 5.72927C13.5507 5.85488 14.3885 6.02641 15.101 6.24385C15.8135 6.46183 16.3775 6.71252 16.7928 6.99594C17.2087 7.27988 17.4167 7.58678 17.4167 7.91665V15.0416C17.4167 15.3451 17.2319 15.6354 16.8625 15.9125C16.493 16.1896 15.9885 16.4337 15.3488 16.6448C14.7086 16.8559 13.9565 17.0308 13.0926 17.1696C12.2281 17.3079 11.2944 17.3903 10.2917 17.4166V14.25H8.70833V17.4166ZM9.49999 8.70831C10.7799 8.70831 11.885 8.63231 12.8155 8.48031C13.7454 8.32884 14.4875 8.15415 15.0417 7.95623C15.0417 7.89026 14.5403 7.73509 13.5375 7.49073C12.5347 7.2469 11.1889 7.12498 9.49999 7.12498C7.81111 7.12498 6.46527 7.2469 5.46249 7.49073C4.45972 7.73509 3.95833 7.89026 3.95833 7.95623C4.51249 8.15415 5.25481 8.32884 6.18529 8.48031C7.11523 8.63231 8.22013 8.70831 9.49999 8.70831ZM7.12499 15.7146V12.6666H11.875V15.7146C12.9305 15.609 13.7948 15.4538 14.4677 15.2491C15.1406 15.0448 15.5958 14.8635 15.8333 14.7052V9.34165C15.1076 9.63192 14.1972 9.86283 13.1021 10.0344C12.0069 10.2059 10.8062 10.2916 9.49999 10.2916C8.19374 10.2916 6.99305 10.2059 5.89791 10.0344C4.80277 9.86283 3.89236 9.63192 3.16666 9.34165V14.7052C3.40416 14.8635 3.85937 15.0448 4.53229 15.2491C5.2052 15.4538 6.06944 15.609 7.12499 15.7146Z"
59
- fill="#F5F5F5"
60
- />
53
+ fill="#F5F5F5" />
61
54
  </svg>
62
55
  <p class="text-light-400 text-sm">No Map</p>
63
56
  <p class="text-light-500 text-xs">
@@ -69,13 +62,15 @@
69
62
  </div>
70
63
  <div class="col-span-1 lg:col-span-2 w-full flex flex-col h-full">
71
64
  <div class="flex justify-between mb-2 items-center text-white">
72
- <div class="rounded flex justify-between gap-2 items-center" v-if="hasLoaded">
65
+ <div class="rounded flex gap-2 items-center" v-if="hasLoaded">
73
66
  <PriceSortToggle
74
67
  :current="filterBuilder.globalFilter.priceSort"
75
68
  class="smooth-hover"
76
- @change="(e) => filterBuilder.updateGlobalFilter({ priceSort: e })"
77
- />
78
-
69
+ @change="(e) => filterBuilder.updateGlobalFilter({ priceSort: e })" />
70
+ </div>
71
+ <div class="flex items-center gap-1" v-if="hasLoaded">
72
+ <SavingsIcon class="w-4 h-4 text-light-400" />
73
+ <label class="text-sm text-light-400">Max Price:</label>
79
74
  <input
80
75
  type="number"
81
76
  :value="filterBuilder.globalFilter.maxPrice"
@@ -86,8 +81,7 @@
86
81
  })
87
82
  "
88
83
  class="w-14 bg-dark-500 border-2 border-dark-550 px-1 pl-2 h-8 rounded"
89
- placeholder="max"
90
- />
84
+ placeholder="max" />
91
85
  </div>
92
86
  </div>
93
87
  <Table class="border-2 border-dark-550 shadow-xl flex-1 flex flex-col">
@@ -101,8 +95,7 @@
101
95
  <PriceSortToggle
102
96
  class="w-14 smooth-hover"
103
97
  :options="['All', 'WL', 'BL']"
104
- @change="(e) => (shownFilters = e)"
105
- />
98
+ @change="(e) => (shownFilters = e)" />
106
99
  <button class="header-btn save-btn" @click="saveFilter">
107
100
  <EditIcon class="w-4 h-4" />
108
101
  <span class="lg:block hidden">Save</span>
@@ -131,23 +124,20 @@
131
124
  filterBuilder.updateCss();
132
125
  cssUpdateTrigger++;
133
126
  }
134
- "
135
- >
127
+ ">
136
128
  <template #item="{ element: f, index: i }">
137
129
  <Filter
138
130
  v-if="doesFilterShow(f)"
139
131
  :filter="f"
140
132
  :index="i"
141
133
  :filterBuilder="filterBuilder"
142
- class="compact-filter"
143
- />
134
+ class="compact-filter" />
144
135
  </template>
145
136
  </draggable>
146
137
  </div>
147
138
  <div
148
139
  v-else
149
- class="empty-state flex flex-col items-center justify-center py-8 text-center"
150
- >
140
+ class="empty-state flex flex-col items-center justify-center py-8 text-center">
151
141
  <FilterIcon class="w-12 h-12 text-dark-400 mb-3 opacity-50" />
152
142
  <p class="text-light-400 text-sm">No filters yet</p>
153
143
  <p class="text-light-500 text-xs mt-1">Click on the map to create filters</p>
@@ -164,14 +154,12 @@
164
154
  ? 'text-gray opacity-50 cursor-not-allowed'
165
155
  : 'text-gray smooth-hover hover:bg-dark-400 hover:border-light-300'
166
156
  ]"
167
- :title="hasWildcardFilter ? 'Wildcard filter already exists' : 'Add wildcard filter'"
168
- >
157
+ :title="hasWildcardFilter ? 'Wildcard filter already exists' : 'Add wildcard filter'">
169
158
  * Wildcard
170
159
  </button>
171
160
  <button
172
161
  @click="ui.toggleModal('preview-filter')"
173
- class="border-2 gap-1 flex justify-between items-center rounded border-dark-550 px-2 h-7 text-gray text-xs bg-dark-500 overflow-hidden shadow smooth-hover"
174
- >
162
+ class="border-2 gap-1 flex justify-between items-center rounded border-dark-550 px-2 h-7 text-gray text-xs bg-dark-500 overflow-hidden shadow smooth-hover">
175
163
  <CameraIcon class="w-3 h-3" />
176
164
  JSON
177
165
  </button>
@@ -197,7 +185,7 @@ import { FilterIcon } from "@/components/icons";
197
185
  import DragSelect from "dragselect";
198
186
  import { DEBUG } from "@/utils/debug";
199
187
 
200
- import { ReloadIcon, TrashIcon, EditIcon, CameraIcon, StadiumIcon } from "@/components/icons";
188
+ import { ReloadIcon, TrashIcon, EditIcon, CameraIcon, StadiumIcon, SavingsIcon } from "@/components/icons";
201
189
  import { sendSaveFilter } from "@/stores/requests";
202
190
  import PriceSortToggle from "@/components/Filter/PriceSortToggle.vue";
203
191
  import { useUIStore } from "@/stores/ui";
@@ -372,12 +360,7 @@ const addWildcardFilter = () => {
372
360
  };
373
361
 
374
362
  // Initialize RendererFactory
375
- let RendererFactory;
376
- if (DEBUG) {
377
- RendererFactory = class {
378
- constructor() {}
379
- };
380
- } else RendererFactory = import("@necrolab/tm-renderer");
363
+ let RendererFactory = import("@necrolab/tm-renderer");
381
364
 
382
365
  // Real-time CSS injection system
383
366
  let styleElement = null;
@@ -490,13 +473,12 @@ const doesFilterShow = (filter) => {
490
473
  };
491
474
 
492
475
  let rendererFactory;
493
- if (!DEBUG)
494
- RendererFactory.then((r) => {
495
- rendererFactory = new r.default();
496
- rendererFactory.init();
497
- }).catch((error) => {
498
- console.error("Failed to initialize renderer:", error);
499
- });
476
+ RendererFactory.then((r) => {
477
+ rendererFactory = new r.default();
478
+ rendererFactory.init();
479
+ }).catch((error) => {
480
+ console.error("Failed to initialize renderer:", error);
481
+ });
500
482
 
501
483
  const updateShownVenue = async () => {
502
484
  eventId.value = eventId.value.trim();