@necrolab/dashboard 0.5.27 → 0.5.29

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 (38) hide show
  1. package/backend/api.js +7 -5
  2. package/backend/batching.js +59 -2
  3. package/backend/index.js +1 -1
  4. package/index.html +10 -23
  5. package/package.json +1 -1
  6. package/src/assets/css/base/scroll.scss +1 -1
  7. package/src/assets/css/main.scss +14 -14
  8. package/src/components/Console/ConsoleToolbar.vue +8 -8
  9. package/src/components/Editors/Account/Account.vue +9 -5
  10. package/src/components/Editors/Account/AccountView.vue +37 -18
  11. package/src/components/Editors/Account/CreateAccount.vue +38 -4
  12. package/src/components/Editors/Profile/CreateProfile.vue +29 -4
  13. package/src/components/Editors/Profile/Profile.vue +11 -6
  14. package/src/components/Editors/Profile/ProfileCountryChooser.vue +2 -2
  15. package/src/components/Editors/Profile/ProfileView.vue +37 -18
  16. package/src/components/Tasks/CreateTaskAXS.vue +16 -2
  17. package/src/components/Tasks/CreateTaskTM.vue +28 -5
  18. package/src/components/Tasks/QuickSettings.vue +77 -10
  19. package/src/components/Tasks/Task.vue +24 -8
  20. package/src/components/Tasks/TaskView.vue +144 -58
  21. package/src/components/Tasks/ViewTask.vue +17 -3
  22. package/src/components/ui/Modal.vue +1 -1
  23. package/src/components/ui/ReadonlyFieldsSection.vue +3 -3
  24. package/src/components/ui/StatusBadge.vue +1 -1
  25. package/src/components/ui/TaskToggle.vue +2 -3
  26. package/src/components/ui/controls/CountryChooser.vue +2 -2
  27. package/src/components/ui/controls/atomic/Dropdown.vue +2 -2
  28. package/src/components/ui/controls/atomic/MultiDropdown.vue +1 -1
  29. package/src/composables/useDynamicTableHeight.js +4 -4
  30. package/src/composables/useRowSelection.js +0 -1
  31. package/src/composables/useZoomPrevention.js +16 -55
  32. package/src/stores/connection.js +453 -68
  33. package/src/stores/sampleData.js +34 -24
  34. package/src/stores/ui.js +89 -100
  35. package/src/views/Accounts.vue +2 -5
  36. package/src/views/Console.vue +13 -14
  37. package/src/views/Profiles.vue +2 -5
  38. package/vite.config.js +4 -2
@@ -27,7 +27,7 @@ export default {
27
27
  proxy: "http://events1597:yEXBe4Hs@23.26.22.61:61234",
28
28
  eventId: "grantelam@hotmail.com",
29
29
  reservedTicketsList: "• 2x 301/E ($86.47) \n• 2x 306/U ($86.47) \n$345.88",
30
- expirationTime: null,
30
+ expirationTime: new Date(Date.now() + 8 * 60 * 1000).toISOString(),
31
31
  doNotPay: false,
32
32
  inQueue: true,
33
33
  agedAccount: true,
@@ -63,7 +63,7 @@ export default {
63
63
  proxy: "http://events1338:xN4PBVze@23.26.21.58:61234",
64
64
  eventId: "01005D85964A1747",
65
65
  reservedTicketsList: "• 2x 301/E ($86.47) \n• 2x 306/U ($86.47) \n$345.88",
66
- expirationTime: null,
66
+ expirationTime: new Date(Date.now() + 5 * 60 * 1000).toISOString(),
67
67
  doNotPay: false,
68
68
  agedAccount: false,
69
69
  presaleCode: "",
@@ -902,7 +902,8 @@ export default {
902
902
  expYear: "28",
903
903
  country: "US",
904
904
  state: "NY",
905
- tags: ["admin", "amex"]
905
+ tags: ["admin", "amex"],
906
+ enabled: true
906
907
  },
907
908
  {
908
909
  id: 2,
@@ -910,7 +911,8 @@ export default {
910
911
  cardNumber: "3847463847454545",
911
912
  expMonth: "12",
912
913
  expYear: "28",
913
- tags: ["admin", "amex"]
914
+ tags: ["admin", "amex"],
915
+ enabled: true
914
916
  },
915
917
  {
916
918
  id: 3,
@@ -922,23 +924,25 @@ export default {
922
924
  enabled: true
923
925
  },
924
926
  {
925
- id: 1,
927
+ id: 4,
926
928
  profileName: "Harry Potter",
927
929
  cardNumber: "4847463847454545",
928
930
  expMonth: "12",
929
931
  expYear: "28",
930
- tags: ["admin", "amex"]
932
+ tags: ["admin", "amex"],
933
+ enabled: true
931
934
  },
932
935
  {
933
- id: 2,
936
+ id: 5,
934
937
  profileName: "Draco Malfoy",
935
938
  cardNumber: "3847463847454545",
936
939
  expMonth: "12",
937
940
  expYear: "28",
938
- tags: ["admin", "amex"]
941
+ tags: ["admin", "amex"],
942
+ enabled: true
939
943
  },
940
944
  {
941
- id: 3,
945
+ id: 6,
942
946
  profileName: "Tom Smith",
943
947
  cardNumber: "5847463847454545",
944
948
  expMonth: "12",
@@ -947,23 +951,25 @@ export default {
947
951
  enabled: true
948
952
  },
949
953
  {
950
- id: 1,
954
+ id: 7,
951
955
  profileName: "Harry Potter",
952
956
  cardNumber: "4847463847454545",
953
957
  expMonth: "12",
954
958
  expYear: "28",
955
- tags: ["admin", "amex"]
959
+ tags: ["admin", "amex"],
960
+ enabled: true
956
961
  },
957
962
  {
958
- id: 2,
963
+ id: 8,
959
964
  profileName: "Draco Malfoy",
960
965
  cardNumber: "3847463847454545",
961
966
  expMonth: "12",
962
967
  expYear: "28",
963
- tags: ["admin", "amex"]
968
+ tags: ["admin", "amex"],
969
+ enabled: true
964
970
  },
965
971
  {
966
- id: 3,
972
+ id: 9,
967
973
  profileName: "Tom Smith",
968
974
  cardNumber: "5847463847454545",
969
975
  expMonth: "12",
@@ -972,23 +978,25 @@ export default {
972
978
  enabled: true
973
979
  },
974
980
  {
975
- id: 1,
981
+ id: 10,
976
982
  profileName: "Harry Potter",
977
983
  cardNumber: "4847463847454545",
978
984
  expMonth: "12",
979
985
  expYear: "28",
980
- tags: ["admin", "amex"]
986
+ tags: ["admin", "amex"],
987
+ enabled: true
981
988
  },
982
989
  {
983
- id: 2,
990
+ id: 11,
984
991
  profileName: "Draco Malfoy",
985
992
  cardNumber: "3847463847454545",
986
993
  expMonth: "12",
987
994
  expYear: "28",
988
- tags: ["admin", "amex"]
995
+ tags: ["admin", "amex"],
996
+ enabled: true
989
997
  },
990
998
  {
991
- id: 3,
999
+ id: 12,
992
1000
  profileName: "Tom Smith",
993
1001
  cardNumber: "5847463847454545",
994
1002
  expMonth: "12",
@@ -997,23 +1005,25 @@ export default {
997
1005
  enabled: true
998
1006
  },
999
1007
  {
1000
- id: 1,
1008
+ id: 13,
1001
1009
  profileName: "Harry Potter",
1002
1010
  cardNumber: "4847463847454545",
1003
1011
  expMonth: "12",
1004
1012
  expYear: "28",
1005
- tags: ["admin", "amex"]
1013
+ tags: ["admin", "amex"],
1014
+ enabled: true
1006
1015
  },
1007
1016
  {
1008
- id: 2,
1017
+ id: 14,
1009
1018
  profileName: "Draco Malfoy",
1010
1019
  cardNumber: "3847463847454545",
1011
1020
  expMonth: "12",
1012
1021
  expYear: "28",
1013
- tags: ["admin", "amex"]
1022
+ tags: ["admin", "amex"],
1023
+ enabled: true
1014
1024
  },
1015
1025
  {
1016
- id: 3,
1026
+ id: 15,
1017
1027
  profileName: "Tom Smith",
1018
1028
  cardNumber: "5847463847454545",
1019
1029
  expMonth: "12",
package/src/stores/ui.js CHANGED
@@ -3,7 +3,6 @@ import { defineStore } from "pinia";
3
3
  import { ConnectionHandler } from "@/stores/connection.js";
4
4
  import { toast } from "vue3-toastify";
5
5
  import { createLogger } from "@/stores/logger";
6
- import { timeDifference } from "@/libs/utils/time";
7
6
  import { betterSort, sortTaskIds } from "@/libs/utils/array";
8
7
 
9
8
  const TOAST_CONFIG = {
@@ -109,94 +108,71 @@ export const useUIStore = defineStore("ui", () => {
109
108
  });
110
109
  const currentDropdown = ref("");
111
110
 
112
- // Optimized: Use requestAnimationFrame instead of setInterval to reduce CPU waste
113
- // Only update times when tab is visible and at most once per second
114
- let lastTimeUpdate = 0;
115
- let lastSyncCheck = 0;
116
- let rafId = null;
111
+ const taskTimeTick = ref(Date.now());
112
+ let tickInterval = null;
113
+ let syncCounterMs = 0;
117
114
 
118
- const updateTaskTimes = (timestamp) => {
119
- // Only update once per second
120
- if (timestamp - lastTimeUpdate >= 1000) {
121
- lastTimeUpdate = timestamp;
122
- const now = Date.now();
115
+ const syncTaskIdOrder = () => {
116
+ const allIds = new Set(Object.keys(tasks.value));
117
+ const orderIds = new Set(taskIdOrder.value);
123
118
 
124
- for (const [key, value] of Object.entries(tasks.value)) {
125
- if (value.expirationTime) {
126
- tasks.value[key]._timeLeftString =
127
- value.expirationTime == "Invalid Date"
128
- ? "No Cartholds"
129
- : timeDifference(Date.parse(value.expirationTime), now);
130
- } else {
131
- tasks.value[key]._timeLeftString = undefined;
132
- }
119
+ const missingIds = [...allIds].filter((id) => !orderIds.has(id));
120
+ const extraIds = [...orderIds].filter((id) => !allIds.has(id));
121
+
122
+ if (missingIds.length === 0 && extraIds.length === 0) return;
123
+
124
+ taskIdOrder.value = taskIdOrder.value.filter((id) => allIds.has(id));
125
+ if (missingIds.length === 0) return;
126
+
127
+ missingIds.sort(sortTaskIds);
128
+ for (const id of missingIds) {
129
+ let left = 0;
130
+ let right = taskIdOrder.value.length;
131
+ while (left < right) {
132
+ const mid = Math.floor((left + right) / 2);
133
+ if (sortTaskIds(id, taskIdOrder.value[mid]) < 0) right = mid;
134
+ else left = mid + 1;
133
135
  }
136
+ taskIdOrder.value.splice(left, 0, id);
134
137
  }
138
+ };
135
139
 
136
- // Sync taskIdOrder every 10 seconds (optimized with Map for O(1) lookups)
137
- if (timestamp - lastSyncCheck >= 10000) {
138
- lastSyncCheck = timestamp;
139
- const allIds = new Set(Object.keys(tasks.value));
140
- const orderIds = new Set(taskIdOrder.value);
141
-
142
- const missingIds = [...allIds].filter((id) => !orderIds.has(id));
143
- const extraIds = [...orderIds].filter((id) => !allIds.has(id));
144
-
145
- if (missingIds.length > 0 || extraIds.length > 0) {
146
- taskIdOrder.value = taskIdOrder.value.filter((id) => allIds.has(id));
147
-
148
- if (missingIds.length > 0) {
149
- missingIds.sort(sortTaskIds);
150
- // Optimized: Binary search for insertion point
151
- for (const id of missingIds) {
152
- let left = 0;
153
- let right = taskIdOrder.value.length;
154
- while (left < right) {
155
- const mid = Math.floor((left + right) / 2);
156
- if (sortTaskIds(id, taskIdOrder.value[mid]) < 0) {
157
- right = mid;
158
- } else {
159
- left = mid + 1;
160
- }
161
- }
162
- taskIdOrder.value.splice(left, 0, id);
163
- }
164
- }
165
- }
140
+ const tickTaskTimers = () => {
141
+ taskTimeTick.value = Date.now();
142
+ syncCounterMs += 1000;
143
+ if (syncCounterMs >= 10000) {
144
+ syncCounterMs = 0;
145
+ syncTaskIdOrder();
166
146
  }
147
+ };
167
148
 
168
- rafId = requestAnimationFrame(updateTaskTimes);
149
+ const startTaskTimerTicking = () => {
150
+ if (tickInterval) return;
151
+ tickInterval = setInterval(tickTaskTimers, 1000);
169
152
  };
170
153
 
171
- rafId = requestAnimationFrame(updateTaskTimes);
154
+ const stopTaskTimerTicking = () => {
155
+ if (!tickInterval) return;
156
+ clearInterval(tickInterval);
157
+ tickInterval = null;
158
+ };
172
159
 
173
- // Pause updates when tab is hidden to save CPU
174
- document.addEventListener('visibilitychange', () => {
160
+ startTaskTimerTicking();
161
+
162
+ document.addEventListener("visibilitychange", () => {
175
163
  if (document.hidden) {
176
- if (rafId) {
177
- cancelAnimationFrame(rafId);
178
- rafId = null;
179
- }
180
- } else {
181
- if (!rafId) {
182
- lastTimeUpdate = 0;
183
- lastSyncCheck = 0;
184
- rafId = requestAnimationFrame(updateTaskTimes);
185
- }
164
+ stopTaskTimerTicking();
165
+ return;
186
166
  }
167
+
168
+ syncCounterMs = 0;
169
+ taskTimeTick.value = Date.now();
170
+ syncTaskIdOrder();
171
+ startTaskTimerTicking();
187
172
  });
188
173
 
189
174
  connection.init("/api/updates?type=tasks");
190
175
 
191
- const pd = (e) => {
192
- if (
193
- !e.target.closest(".scrollable") &&
194
- !e.target.closest(".dropdown-menu") &&
195
- !e.target.closest(".option-list")
196
- ) {
197
- e.preventDefault();
198
- }
199
- };
200
176
  const preventScroll = () => {
201
177
  const scrollY = window.scrollY;
202
178
  document.body.style.position = "fixed";
@@ -230,8 +206,6 @@ export const useUIStore = defineStore("ui", () => {
230
206
  input.style.touchAction = "pan-x pan-y";
231
207
  });
232
208
 
233
- // Prevent touch moves
234
- document.addEventListener("touchmove", pd, { passive: false });
235
209
  };
236
210
 
237
211
  const enableScroll = () => {
@@ -269,34 +243,45 @@ export const useUIStore = defineStore("ui", () => {
269
243
  // Restore scroll position
270
244
  window.scrollTo(0, parseInt(scrollY || "0") * -1);
271
245
 
272
- // Remove event listener
273
- document.removeEventListener("touchmove", pd);
274
246
  };
275
247
 
276
248
  const refreshQueueStats = () => {
277
- queueStats.value.show = false;
278
- queueStats.value.total = 0;
279
- queueStats.value.queued = 0;
280
- queueStats.value.sleeping = 0;
281
- queueStats.value.nextQueuePasses = [];
282
-
283
- let relevantTasks = Object.values(tasks.value).filter((t) => t.siteId === currentCountry.value.siteId);
284
- if (currentEvent.value) relevantTasks = relevantTasks.filter((t) => t.eventId === currentEvent.value);
285
-
286
- queueStats.value.queued = relevantTasks.filter((t) => t.inQueue).length || 0;
287
- const sleepingStatuses = ["sleeping in queue", "waiting for drop", "waiting for carting", "waiting for queue"];
288
- queueStats.value.sleeping = relevantTasks.filter((t) =>
289
- sleepingStatuses.includes(t.status.toLowerCase())
290
- ).length;
291
- const positions = relevantTasks
292
- .map((t) => t.queuePosition)
293
- .filter((e) => !isNaN(e))
294
- .filter(Boolean);
295
- queueStats.value.nextQueuePasses = positions.sort((a, b) => a - b);
296
-
297
- // Only show stats if there are any queued tasks, sleeping tasks, or queue positions
298
- queueStats.value.show =
299
- queueStats.value.queued > 0 || queueStats.value.sleeping > 0 || queueStats.value.nextQueuePasses.length > 0;
249
+ const siteId = currentCountry.value.siteId;
250
+ const eventId = currentEvent.value;
251
+ const sleepingStatuses = new Set([
252
+ "sleeping in queue",
253
+ "waiting for drop",
254
+ "waiting for carting",
255
+ "waiting for queue"
256
+ ]);
257
+
258
+ let total = 0;
259
+ let queued = 0;
260
+ let sleeping = 0;
261
+ const positions = [];
262
+
263
+ for (const task of Object.values(tasks.value)) {
264
+ if (!task || task.siteId !== siteId) continue;
265
+ if (eventId && task.eventId !== eventId) continue;
266
+
267
+ total++;
268
+ if (task.inQueue) queued++;
269
+
270
+ const status = typeof task.status === "string" ? task.status.toLowerCase() : "";
271
+ if (sleepingStatuses.has(status)) sleeping++;
272
+
273
+ const queuePosition = Number(task.queuePosition);
274
+ if (Number.isFinite(queuePosition) && queuePosition > 0) {
275
+ positions.push(queuePosition);
276
+ }
277
+ }
278
+
279
+ positions.sort((a, b) => a - b);
280
+ queueStats.value.total = total;
281
+ queueStats.value.queued = queued;
282
+ queueStats.value.sleeping = sleeping;
283
+ queueStats.value.nextQueuePasses = positions;
284
+ queueStats.value.show = queued > 0 || sleeping > 0 || positions.length > 0;
300
285
  };
301
286
 
302
287
  const toggleModal = (name, clearValue = false) => {
@@ -466,6 +451,7 @@ export const useUIStore = defineStore("ui", () => {
466
451
 
467
452
  // refresh
468
453
  tasks,
454
+ taskTimeTick,
469
455
 
470
456
  // top main checkbox
471
457
  mainCheckbox,
@@ -580,6 +566,9 @@ export const useUIStore = defineStore("ui", () => {
580
566
  } else accounts.value.push({ ...acc, id: Math.random() });
581
567
  },
582
568
  setAccounts: (accs) => accounts.value.push(...accs),
569
+ setAccountsForModule: (module, accs) => {
570
+ accounts.value = [...accounts.value.filter((account) => account.module !== module), ...accs];
571
+ },
583
572
  profiles,
584
573
  addProfile: (profile) => {
585
574
  if (!DEBUG) return connection.sendSaveProfile(profile);
@@ -4,6 +4,7 @@
4
4
  <div class="page-header-card">
5
5
  <MailIcon />
6
6
  <h4>Accounts</h4>
7
+ <span class="pl-1.5 text-sm font-medium text-light-400">{{ ui.accounts.length }}</span>
7
8
  </div>
8
9
  <ul class="mobile-icons">
9
10
  <li>
@@ -84,7 +85,7 @@
84
85
  </div>
85
86
 
86
87
  <!-- Tasks (Table) -->
87
- <AccountView :accounts="processedTasks" />
88
+ <AccountView :accounts="ui.search.accounts.results" :privacy="privacy" />
88
89
 
89
90
  <!-- Modal -->
90
91
  <transition-group name="fade">
@@ -130,10 +131,6 @@ const filterAccounts = () => {
130
131
 
131
132
  ui.search.accounts.results = filterAccounts();
132
133
 
133
- const processedTasks = computed(() => {
134
- return ui.search.accounts.results.map((e) => ({ ...e, privacy: privacy.value }));
135
- });
136
-
137
134
  watch(
138
135
  () =>
139
136
  ui.search.accounts.query +
@@ -22,13 +22,10 @@
22
22
  @autoscroll-toggle="onAutoscrollToggle" />
23
23
  </div>
24
24
 
25
- <Smoothie
26
- :weight="0.2"
25
+ <div
27
26
  class="console-main"
28
27
  :style="consoleMainStyle"
29
28
  ref="$autoscroll"
30
- @wheel.stop
31
- @touchmove.stop
32
29
  @scroll="handleScroll">
33
30
  <div
34
31
  v-if="displayedLogs.length === 0"
@@ -47,7 +44,7 @@
47
44
  v-for="(line, index) in displayedLogs"
48
45
  v-bind:key="`log-${index}`"
49
46
  :style="{ '--index': index }"><code class="md:text-sm lg:text-base mobile-portrait:text-xs+ mobile-portrait:leading-tight" v-html="line"></code></pre>
50
- </Smoothie>
47
+ </div>
51
48
  </div>
52
49
  </div>
53
50
  </template>
@@ -105,7 +102,6 @@
105
102
  }
106
103
  </style>
107
104
  <script setup>
108
- import { Smoothie } from "vue-smoothie";
109
105
  import { DEBUG } from "@/utils/debug";
110
106
 
111
107
  import Filter from "@/libs/ansii.js";
@@ -168,6 +164,11 @@ const SCROLL_THRESHOLD = 100;
168
164
  const SCROLL_AMOUNT = 100;
169
165
  const MIN_CONSOLE_HEIGHT = 192;
170
166
 
167
+ const getConsoleScrollElement = () => {
168
+ if (!$autoscroll.value) return null;
169
+ return $autoscroll.value?.el || $autoscroll.value;
170
+ };
171
+
171
172
  const getTableHeightCap = (viewportHeight) => {
172
173
  const isPWA = window.matchMedia("(display-mode: standalone)").matches;
173
174
  const isMobile = window.innerWidth <= 768;
@@ -197,7 +198,7 @@ const consoleMainStyle = computed(() => {
197
198
  });
198
199
 
199
200
  const updateConsoleHeight = () => {
200
- const element = $autoscroll.value?.el;
201
+ const element = getConsoleScrollElement();
201
202
  if (!element) return;
202
203
 
203
204
  const viewportHeight = window.visualViewport?.height || window.innerHeight;
@@ -232,13 +233,12 @@ const handleScroll = (event) => {
232
233
 
233
234
  const performScroll = (direction, smooth = true) => {
234
235
  try {
235
- if (!$autoscroll.value?.el) {
236
+ const element = getConsoleScrollElement();
237
+ if (!element) {
236
238
  if (DEBUG) return false;
237
239
  return false;
238
240
  }
239
241
 
240
- const element = $autoscroll.value.el;
241
-
242
242
  if (direction === "up") {
243
243
  if (smooth && element.scrollTo) {
244
244
  element.scrollTo({ top: 0, behavior: "smooth" });
@@ -273,7 +273,7 @@ const startScrolling = (direction) => {
273
273
  const continuousScroll = () => {
274
274
  if (!isScrolling.value) return;
275
275
 
276
- const element = $autoscroll.value?.el;
276
+ const element = getConsoleScrollElement();
277
277
  if (!element) return;
278
278
 
279
279
  if (direction === "up") {
@@ -297,9 +297,8 @@ const stopScrolling = () => {
297
297
  };
298
298
 
299
299
  const autoScrollToBottom = () => {
300
- if (!$autoscroll.value?.el || !autoscrollToggled.value) return;
301
-
302
- const element = $autoscroll.value.el;
300
+ const element = getConsoleScrollElement();
301
+ if (!element || !autoscrollToggled.value) return;
303
302
  const targetScrollTop = element.scrollHeight - element.clientHeight;
304
303
  const currentDistanceFromBottom = element.scrollHeight - element.scrollTop - element.clientHeight;
305
304
 
@@ -4,6 +4,7 @@
4
4
  <div class="page-header-card">
5
5
  <GroupIcon />
6
6
  <h4>Profiles</h4>
7
+ <span class="pl-1.5 text-sm font-medium text-light-400">{{ ui.profiles.length }}</span>
7
8
  </div>
8
9
  <ul class="mobile-icons">
9
10
  <li>
@@ -109,7 +110,7 @@
109
110
  </div>
110
111
 
111
112
  <!-- Tasks (Table) -->
112
- <ProfileView :profiles="processedTasks" />
113
+ <ProfileView :profiles="ui.search.profiles.results" :privacy="privacy" />
113
114
 
114
115
  <!-- Modal -->
115
116
  <transition-group name="fade">
@@ -138,10 +139,6 @@ const filterFieldMap = { Name: "profileName", Card: "cardNumber" };
138
139
  const allTags = ref([]);
139
140
  const privacy = ref(true);
140
141
 
141
- const processedTasks = computed(() => {
142
- return ui.search.profiles.results.map((e) => ({ ...e, privacy: privacy.value }));
143
- });
144
-
145
142
  watch(
146
143
  () =>
147
144
  ui.search.profiles.query +
package/vite.config.js CHANGED
@@ -49,7 +49,8 @@ export default defineConfig({
49
49
  preprocessorOptions: {
50
50
  scss: {
51
51
  api: "modern-compiler",
52
- silenceDeprecations: ["legacy-js-api"]
52
+ silenceDeprecations: ["legacy-js-api"],
53
+ loadPaths: [fileURLToPath(new URL("./src/assets/css", import.meta.url))]
53
54
  }
54
55
  },
55
56
  devSourcemap: true
@@ -66,7 +67,8 @@ export default defineConfig({
66
67
  // Handle iOS connection resets gracefully
67
68
  watch: {
68
69
  usePolling: false,
69
- interval: 1000
70
+ interval: 1000,
71
+ ignored: ["**/dist/**"]
70
72
  },
71
73
  proxy: {
72
74
  "/api": {