@necrolab/dashboard 0.4.221 → 0.5.1
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.
- package/.prettierrc +27 -1
- package/.vscode/extensions.json +1 -1
- package/README.md +64 -2
- package/artwork/image.png +0 -0
- package/backend/api.js +26 -24
- package/backend/auth.js +2 -2
- package/backend/batching.js +1 -1
- package/backend/endpoints.js +8 -11
- package/backend/index.js +2 -2
- package/backend/mock-data.js +27 -36
- package/backend/mock-src/classes/logger.js +5 -7
- package/backend/mock-src/classes/utils.js +3 -2
- package/backend/mock-src/ticketmaster.js +4 -4
- package/backend/validator.js +2 -2
- package/config/configs.json +0 -1
- package/dev-server.js +134 -0
- package/exit +209 -0
- package/index.html +78 -8
- package/index.js +1 -1
- package/jsconfig.json +16 -0
- package/package.json +39 -25
- package/postcss.config.js +1 -1
- package/postinstall.js +124 -20
- package/public/android-chrome-192x192.png +0 -0
- package/public/android-chrome-512x512.png +0 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/img/logo_trans.png +0 -0
- package/public/img/necro_logo.png +0 -0
- package/public/manifest.json +16 -10
- package/run +176 -9
- package/src/App.vue +498 -85
- package/src/assets/css/base/reset.scss +43 -0
- package/src/assets/css/base/scroll.scss +114 -0
- package/src/assets/css/base/typography.scss +37 -0
- package/src/assets/css/components/buttons.scss +216 -0
- package/src/assets/css/components/forms.scss +221 -0
- package/src/assets/css/components/modals.scss +13 -0
- package/src/assets/css/components/tables.scss +27 -0
- package/src/assets/css/components/toasts.scss +100 -0
- package/src/assets/css/main.scss +201 -122
- package/src/assets/img/background.svg +2 -2
- package/src/assets/img/background.svg.backup +11 -0
- package/src/assets/img/logo_trans.png +0 -0
- package/src/components/Auth/LoginForm.vue +62 -11
- package/src/components/Editors/Account/Account.vue +116 -40
- package/src/components/Editors/Account/AccountCreator.vue +88 -39
- package/src/components/Editors/Account/AccountView.vue +102 -34
- package/src/components/Editors/Account/CreateAccount.vue +80 -32
- package/src/components/Editors/Profile/CreateProfile.vue +269 -83
- package/src/components/Editors/Profile/Profile.vue +132 -47
- package/src/components/Editors/Profile/ProfileCountryChooser.vue +82 -20
- package/src/components/Editors/Profile/ProfileView.vue +89 -32
- package/src/components/Editors/TagLabel.vue +67 -6
- package/src/components/Editors/TagToggle.vue +7 -2
- package/src/components/Filter/Filter.vue +288 -71
- package/src/components/Filter/FilterPreview.vue +202 -31
- package/src/components/Filter/PriceSortToggle.vue +76 -6
- package/src/components/Table/Header.vue +1 -1
- package/src/components/Table/Row.vue +1 -1
- package/src/components/Table/Table.vue +19 -2
- package/src/components/Tasks/CheckStock.vue +6 -8
- package/src/components/Tasks/Controls/DesktopControls.vue +27 -17
- package/src/components/Tasks/Controls/MobileControls.vue +8 -45
- package/src/components/Tasks/CreateTaskAXS.vue +80 -72
- package/src/components/Tasks/CreateTaskTM.vue +95 -141
- package/src/components/Tasks/MassEdit.vue +4 -6
- package/src/components/Tasks/QuickSettings.vue +199 -30
- package/src/components/Tasks/ScrapeVenue.vue +5 -6
- package/src/components/Tasks/Stats.vue +50 -24
- package/src/components/Tasks/Task.vue +384 -179
- package/src/components/Tasks/TaskLabel.vue +2 -2
- package/src/components/Tasks/TaskView.vue +136 -48
- package/src/components/Tasks/Utilities.vue +25 -10
- package/src/components/Tasks/ViewTask.vue +321 -0
- package/src/components/icons/Bag.vue +1 -1
- package/src/components/icons/Check.vue +5 -0
- package/src/components/icons/Close.vue +21 -0
- package/src/components/icons/CloseX.vue +5 -0
- package/src/components/icons/Eye.vue +6 -0
- package/src/components/icons/Key.vue +21 -0
- package/src/components/icons/Loyalty.vue +1 -1
- package/src/components/icons/Mail.vue +2 -2
- package/src/components/icons/Pencil.vue +21 -0
- package/src/components/icons/Play.vue +2 -2
- package/src/components/icons/Profile.vue +18 -0
- package/src/components/icons/Reload.vue +4 -5
- package/src/components/icons/Sandclock.vue +2 -2
- package/src/components/icons/Sell.vue +21 -0
- package/src/components/icons/Spinner.vue +42 -0
- package/src/components/icons/SquareCheck.vue +18 -0
- package/src/components/icons/SquareUncheck.vue +18 -0
- package/src/components/icons/Stadium.vue +1 -1
- package/src/components/icons/Wildcard.vue +18 -0
- package/src/components/icons/index.js +26 -1
- package/src/components/ui/Modal.vue +107 -13
- package/src/components/ui/Navbar.vue +175 -40
- package/src/components/ui/ReconnectIndicator.vue +351 -55
- package/src/components/ui/Splash.vue +5 -35
- package/src/components/ui/controls/CountryChooser.vue +200 -62
- package/src/components/ui/controls/atomic/Checkbox.vue +119 -10
- package/src/components/ui/controls/atomic/Dropdown.vue +216 -39
- package/src/components/ui/controls/atomic/LoadingButton.vue +45 -0
- package/src/components/ui/controls/atomic/MultiDropdown.vue +300 -37
- package/src/components/ui/controls/atomic/Switch.vue +53 -25
- package/src/composables/useClickOutside.js +21 -0
- package/src/composables/useDropdownPosition.js +174 -0
- package/src/libs/Filter.js +60 -24
- package/src/registerServiceWorker.js +1 -1
- package/src/stores/connection.js +4 -4
- package/src/stores/sampleData.js +172 -199
- package/src/stores/ui.js +55 -20
- package/src/stores/utils.js +30 -4
- package/src/types/index.js +41 -0
- package/src/utils/debug.js +1 -0
- package/src/views/Accounts.vue +116 -50
- package/src/views/Console.vue +394 -77
- package/src/views/Editor.vue +1176 -123
- package/src/views/FilterBuilder.vue +528 -250
- package/src/views/Login.vue +76 -14
- package/src/views/Profiles.vue +119 -34
- package/src/views/Tasks.vue +266 -98
- package/static/offline.html +192 -50
- package/switch-branch.sh +41 -0
- package/tailwind.config.js +119 -27
- package/vite.config.js +73 -16
- package/workbox-config.cjs +63 -0
- package/ICONS.md +0 -21
- package/public/img/background.svg +0 -14
- package/public/img/logo.png +0 -0
- package/public/img/logo_icon.png +0 -0
- package/public/img/logo_icon_2.png +0 -0
- package/src/assets/css/_input.scss +0 -143
- package/src/assets/img/logo.png +0 -0
- package/src/assets/img/logo_icon.png +0 -0
- package/src/assets/img/logo_icon_2.png +0 -0
- package/vue.config.js +0 -32
- package/workbox-config.js +0 -7
package/src/views/Console.vue
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
|
-
<h4 class="
|
|
3
|
+
<h4 class="mb-2 flex items-center gap-2 pt-5 text-sm font-bold text-white lg:mt-1">
|
|
4
|
+
Console
|
|
5
|
+
<ConsoleIcon />
|
|
6
|
+
</h4>
|
|
4
7
|
|
|
5
8
|
<div>
|
|
6
|
-
<div class="flex items-center
|
|
7
|
-
<div class="flex
|
|
8
|
-
<div class="w-
|
|
9
|
+
<div class="mb-3 flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
|
|
10
|
+
<div class="flex flex-col gap-3 md:flex-row md:items-center md:flex-1">
|
|
11
|
+
<div class="w-full md:w-64">
|
|
9
12
|
<Dropdown
|
|
10
|
-
class="w-
|
|
13
|
+
class="console-dropdown input-default w-full border-2 border-dark-550 bg-dark-500"
|
|
11
14
|
rightAmount="right-2"
|
|
12
15
|
default="All logs"
|
|
13
16
|
:allowDefault="true"
|
|
@@ -17,62 +20,118 @@
|
|
|
17
20
|
Object.entries(taskLogMapping)
|
|
18
21
|
.map(([k, v]) => `${k} (${v.length})`)
|
|
19
22
|
.sort((a, b) => a.localeCompare(b))
|
|
20
|
-
"
|
|
21
|
-
|
|
23
|
+
" />
|
|
24
|
+
</div>
|
|
25
|
+
<div class="flex items-center gap-2 flex-1">
|
|
26
|
+
<div class="input-default flex items-center flex-1 md:max-w-64">
|
|
27
|
+
<input
|
|
28
|
+
v-model="searchQuery"
|
|
29
|
+
type="text"
|
|
30
|
+
placeholder="Search logs..."
|
|
31
|
+
class="h-full w-full bg-transparent text-sm text-white outline-none" />
|
|
32
|
+
<span v-if="searchQuery" class="ml-2 text-xs text-light-500">{{ filteredCount }}</span>
|
|
33
|
+
</div>
|
|
34
|
+
<!-- Scroll buttons on mobile - inline with search -->
|
|
35
|
+
<button
|
|
36
|
+
class="console-scroll-btn flex h-10 w-10 items-center justify-center rounded border-2 bg-dark-400 shadow-sm md:hidden"
|
|
37
|
+
@mousedown="startScrolling('up')"
|
|
38
|
+
@mouseup="stopScrolling"
|
|
39
|
+
@mouseleave="stopScrolling"
|
|
40
|
+
@touchstart="startScrolling('up')"
|
|
41
|
+
@touchend="stopScrolling">
|
|
42
|
+
<UpIcon class="pointer-events-none h-5 w-5" />
|
|
43
|
+
</button>
|
|
44
|
+
<button
|
|
45
|
+
class="console-scroll-btn flex h-10 w-10 items-center justify-center rounded border-2 bg-dark-400 shadow-sm md:hidden"
|
|
46
|
+
@mousedown="startScrolling('down')"
|
|
47
|
+
@mouseup="stopScrolling"
|
|
48
|
+
@mouseleave="stopScrolling"
|
|
49
|
+
@touchstart="startScrolling('down')"
|
|
50
|
+
@touchend="stopScrolling">
|
|
51
|
+
<DownIcon class="pointer-events-none h-5 w-5" />
|
|
52
|
+
</button>
|
|
22
53
|
</div>
|
|
23
54
|
</div>
|
|
24
|
-
<div class="flex
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
55
|
+
<div class="flex items-center gap-3 hidden md:flex">
|
|
56
|
+
<!-- Hide Monitors and Auto buttons only on desktop -->
|
|
57
|
+
<div class="hidden items-center gap-3 md:flex">
|
|
58
|
+
<button
|
|
59
|
+
class="flex h-10 items-center justify-center gap-3 rounded border border-dark-650 bg-dark-400 px-2 shadow-sm">
|
|
60
|
+
<h3 class="text-sm text-white">Hide Monitors</h3>
|
|
61
|
+
<Switch class="scale-75" v-model="filteredLogs" />
|
|
62
|
+
</button>
|
|
63
|
+
<button
|
|
64
|
+
class="relative flex h-10 items-center justify-center gap-3 rounded border border-dark-650 bg-dark-400 px-2 shadow-sm">
|
|
65
|
+
<h3 class="text-sm text-white">Auto</h3>
|
|
66
|
+
<Switch class="scale-75" v-model="autoscrollToggled" @change="onAutoscrollToggle" />
|
|
67
|
+
<div
|
|
68
|
+
v-if="userScrolledUp && autoscrollToggled"
|
|
69
|
+
class="absolute -right-1 -top-1 h-2 w-2 animate-pulse rounded-full bg-yellow-500"
|
|
70
|
+
title="Autoscroll paused - scroll to bottom to resume"></div>
|
|
71
|
+
</button>
|
|
72
|
+
</div>
|
|
73
|
+
<!-- Scroll buttons - desktop only (mobile has them inline with search) -->
|
|
37
74
|
<button
|
|
38
|
-
class="
|
|
39
|
-
|
|
40
|
-
|
|
75
|
+
class="hidden md:flex h-10 w-10 items-center justify-center rounded border border-dark-650 bg-dark-400 shadow-sm transition-colors duration-150 hover:bg-dark-300 active:bg-dark-200"
|
|
76
|
+
@mousedown="startScrolling('up')"
|
|
77
|
+
@mouseup="stopScrolling"
|
|
78
|
+
@mouseleave="stopScrolling"
|
|
79
|
+
@touchstart="startScrolling('up')"
|
|
80
|
+
@touchend="stopScrolling">
|
|
81
|
+
<UpIcon class="pointer-events-none h-5 w-5" />
|
|
41
82
|
</button>
|
|
42
83
|
<button
|
|
43
|
-
class="
|
|
44
|
-
|
|
45
|
-
|
|
84
|
+
class="hidden md:flex h-10 w-10 items-center justify-center rounded border border-dark-650 bg-dark-400 shadow-sm transition-colors duration-150 hover:bg-dark-300 active:bg-dark-200"
|
|
85
|
+
@mousedown="startScrolling('down')"
|
|
86
|
+
@mouseup="stopScrolling"
|
|
87
|
+
@mouseleave="stopScrolling"
|
|
88
|
+
@touchstart="startScrolling('down')"
|
|
89
|
+
@touchend="stopScrolling">
|
|
90
|
+
<DownIcon class="pointer-events-none h-5 w-5" />
|
|
46
91
|
</button>
|
|
47
92
|
</div>
|
|
48
93
|
</div>
|
|
49
94
|
|
|
50
95
|
<Smoothie
|
|
51
|
-
:weight="0.
|
|
52
|
-
class="
|
|
53
|
-
style="min-height:
|
|
96
|
+
:weight="0.2"
|
|
97
|
+
class="console scrollable smooth-scroll overflow-y-auto overflow-x-hidden font-mono text-white"
|
|
98
|
+
style="min-height: 12rem !important"
|
|
54
99
|
ref="$autoscroll"
|
|
55
|
-
|
|
100
|
+
@wheel.stop
|
|
101
|
+
@touchmove.stop
|
|
102
|
+
@scroll="handleScroll">
|
|
103
|
+
<div
|
|
104
|
+
v-if="displayedLogs.length === 0"
|
|
105
|
+
class="empty-state flex h-full flex-col items-center justify-center text-center">
|
|
106
|
+
<ConsoleIcon class="mb-3 h-12 w-12 text-dark-400 opacity-50" />
|
|
107
|
+
<p class="text-sm text-light-400">
|
|
108
|
+
{{ searchQuery ? "No logs match your search" : "No logs yet" }}
|
|
109
|
+
</p>
|
|
110
|
+
<p class="mt-1 text-xs text-light-500">
|
|
111
|
+
{{ searchQuery ? "Try a different search term" : "Logs will appear here in real time" }}
|
|
112
|
+
</p>
|
|
113
|
+
</div>
|
|
56
114
|
<pre
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
v-
|
|
62
|
-
><code class="md:text-sm lg:text-base" v-html="line"></code></pre>
|
|
115
|
+
v-else
|
|
116
|
+
class="hidden-scrollbars log-entry"
|
|
117
|
+
v-for="(line, index) in displayedLogs"
|
|
118
|
+
v-bind:key="`log-${index}`"
|
|
119
|
+
:style="{ '--index': index }"><code class="md:text-sm lg:text-base" v-html="line"></code></pre>
|
|
63
120
|
</Smoothie>
|
|
64
|
-
<div class="flex
|
|
121
|
+
<div class="mt-3 flex justify-between md:hidden">
|
|
65
122
|
<button
|
|
66
|
-
class="flex
|
|
67
|
-
>
|
|
123
|
+
class="flex h-10 items-center justify-center gap-3 rounded border border-dark-650 bg-dark-400 px-2 shadow-sm">
|
|
68
124
|
<h3 class="text-sm text-white">Hide Monitors</h3>
|
|
69
125
|
<Switch class="scale-75" v-model="filteredLogs" />
|
|
70
126
|
</button>
|
|
71
127
|
<button
|
|
72
|
-
class="flex
|
|
73
|
-
>
|
|
128
|
+
class="relative flex h-10 items-center justify-center gap-3 rounded border border-dark-650 bg-dark-400 px-2 shadow-sm">
|
|
74
129
|
<h3 class="text-sm text-white">Auto</h3>
|
|
75
|
-
<Switch class="scale-75" v-model="autoscrollToggled" />
|
|
130
|
+
<Switch class="scale-75" v-model="autoscrollToggled" @change="onAutoscrollToggle" />
|
|
131
|
+
<div
|
|
132
|
+
v-if="userScrolledUp && autoscrollToggled"
|
|
133
|
+
class="absolute -right-1 -top-1 h-2 w-2 animate-pulse rounded-full bg-yellow-500"
|
|
134
|
+
title="Autoscroll paused - scroll to bottom to resume"></div>
|
|
76
135
|
</button>
|
|
77
136
|
</div>
|
|
78
137
|
</div>
|
|
@@ -80,23 +139,124 @@
|
|
|
80
139
|
</template>
|
|
81
140
|
<style lang="scss" scoped>
|
|
82
141
|
.console {
|
|
83
|
-
@apply
|
|
84
|
-
height: calc(100vh -
|
|
142
|
+
@apply relative rounded border-2 border-dark-550 bg-dark-400 p-2 lg:p-5;
|
|
143
|
+
height: calc(100vh - 18rem);
|
|
144
|
+
scrollbar-width: thin;
|
|
145
|
+
scrollbar-color: oklch(0.35 0 0) oklch(0.19 0 0);
|
|
146
|
+
|
|
147
|
+
@media (min-width: 768px) {
|
|
148
|
+
height: calc(100vh - 16rem);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@media (min-width: 1024px) {
|
|
152
|
+
height: calc(100vh - 14rem);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
&::-webkit-scrollbar {
|
|
156
|
+
width: 8px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
&::-webkit-scrollbar-track {
|
|
160
|
+
background: oklch(0.19 0 0);
|
|
161
|
+
border-radius: 4px;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
&::-webkit-scrollbar-thumb {
|
|
165
|
+
background: oklch(0.35 0 0);
|
|
166
|
+
border-radius: 4px;
|
|
167
|
+
transition: background-color 0.2s ease;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
&::-webkit-scrollbar-thumb:hover {
|
|
171
|
+
background: oklch(0.45 0 0);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Smooth scrolling behavior with momentum
|
|
175
|
+
&.smooth-scroll {
|
|
176
|
+
scroll-behavior: smooth;
|
|
177
|
+
scroll-padding: 0.5rem;
|
|
178
|
+
-webkit-overflow-scrolling: touch;
|
|
179
|
+
overscroll-behavior: contain;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Improved log entry animations
|
|
183
|
+
.log-entry {
|
|
184
|
+
opacity: 0;
|
|
185
|
+
transform: translateY(4px);
|
|
186
|
+
animation: slideInLog 0.2s ease-out forwards;
|
|
187
|
+
transition: all 0.15s ease;
|
|
188
|
+
|
|
189
|
+
&:hover {
|
|
190
|
+
background-color: rgba(255, 255, 255, 0.02);
|
|
191
|
+
transform: translateX(2px);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Stagger animation for new logs
|
|
196
|
+
.log-entry:last-child {
|
|
197
|
+
animation-delay: 0.05s;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
@keyframes slideInLog {
|
|
201
|
+
to {
|
|
202
|
+
opacity: 1;
|
|
203
|
+
transform: translateY(0);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Empty state styling
|
|
208
|
+
.empty-state {
|
|
209
|
+
min-height: 14rem;
|
|
210
|
+
font-family:
|
|
211
|
+
"Inter",
|
|
212
|
+
-apple-system,
|
|
213
|
+
BlinkMacSystemFont,
|
|
214
|
+
"Segoe UI",
|
|
215
|
+
Helvetica,
|
|
216
|
+
Arial,
|
|
217
|
+
sans-serif;
|
|
218
|
+
}
|
|
219
|
+
|
|
85
220
|
textarea {
|
|
86
221
|
background: transparent;
|
|
87
222
|
resize: none;
|
|
223
|
+
@apply w-full text-white focus:outline-none;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.console-scroll-btn {
|
|
228
|
+
border-color: oklch(0.2809 0 0);
|
|
229
|
+
transition: all 0.15s ease;
|
|
88
230
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
231
|
+
&:hover, &:active {
|
|
232
|
+
border-color: oklch(0.72 0.15 145) !important;
|
|
233
|
+
outline: 1px solid oklch(0.72 0.15 145);
|
|
234
|
+
outline-offset: 0;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* Mobile portrait console optimizations */
|
|
239
|
+
@screen mobile-portrait {
|
|
240
|
+
.console {
|
|
241
|
+
height: calc(100vh - 19.5rem);
|
|
242
|
+
@apply p-1 text-xs;
|
|
243
|
+
overflow: auto;
|
|
244
|
+
|
|
245
|
+
pre {
|
|
246
|
+
line-height: 1.2;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
code {
|
|
250
|
+
font-size: 0.7rem !important;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* Mobile landscape console optimizations */
|
|
256
|
+
@media (max-width: 1024px) and (orientation: landscape) {
|
|
257
|
+
.console {
|
|
258
|
+
height: calc(100vh - 10.5rem);
|
|
259
|
+
}
|
|
100
260
|
}
|
|
101
261
|
|
|
102
262
|
.text-xxs {
|
|
@@ -110,21 +270,17 @@
|
|
|
110
270
|
height: 30px;
|
|
111
271
|
}
|
|
112
272
|
}
|
|
113
|
-
|
|
114
|
-
max-height: calc(100vh - 12rem);
|
|
115
|
-
overflow: hidden;
|
|
116
|
-
}
|
|
273
|
+
/* Console-specific styles handled by utilities */
|
|
117
274
|
</style>
|
|
118
275
|
<script setup>
|
|
119
276
|
import { Smoothie } from "vue-smoothie";
|
|
120
|
-
|
|
121
|
-
const DEBUG = window.location.href.startsWith("http://localhost:5173");
|
|
277
|
+
import { DEBUG } from "@/utils/debug";
|
|
122
278
|
|
|
123
279
|
import Filter from "@/libs/ansii.js";
|
|
124
280
|
import { ConsoleIcon, DownIcon, UpIcon } from "@/components/icons";
|
|
125
281
|
import Switch from "@/components/ui/controls/atomic/Switch.vue";
|
|
126
282
|
import WebsocketHeartbeatJs from "websocket-heartbeat-js";
|
|
127
|
-
import { onMounted, ref, nextTick } from "vue";
|
|
283
|
+
import { onMounted, onUnmounted, ref, nextTick, computed, watch } from "vue";
|
|
128
284
|
import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
|
|
129
285
|
import { sortAlphaNum } from "@/stores/utils";
|
|
130
286
|
|
|
@@ -137,27 +293,169 @@ const logLines = ref([]);
|
|
|
137
293
|
const ansii = new Filter();
|
|
138
294
|
const autoscrollToggled = ref(true);
|
|
139
295
|
const taskLogMapping = ref({});
|
|
140
|
-
const currentTaskLog = ref(
|
|
296
|
+
const currentTaskLog = ref("");
|
|
141
297
|
const filteredLogs = ref(true);
|
|
298
|
+
const userScrolledUp = ref(false);
|
|
299
|
+
const lastScrollTime = ref(0);
|
|
300
|
+
const scrollInterval = ref(null);
|
|
301
|
+
const isScrolling = ref(false);
|
|
302
|
+
const searchQuery = ref("");
|
|
303
|
+
|
|
304
|
+
// Computed filtered logs based on search query
|
|
305
|
+
const displayedLogs = computed(() => {
|
|
306
|
+
let logs =
|
|
307
|
+
currentTaskLog.value && currentTaskLog.value !== ""
|
|
308
|
+
? taskLogMapping.value[currentTaskLog.value]
|
|
309
|
+
: logLines.value.filter((l) => (filteredLogs.value ? !["-DISCORD"].some((s) => l.includes(s)) : true));
|
|
310
|
+
|
|
311
|
+
if (searchQuery.value.trim()) {
|
|
312
|
+
const query = searchQuery.value.toLowerCase();
|
|
313
|
+
logs = logs.filter((log) => {
|
|
314
|
+
// Remove HTML tags for search
|
|
315
|
+
const plainText = log.replace(/<[^>]*>/g, "").toLowerCase();
|
|
316
|
+
return plainText.includes(query);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return logs;
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
const filteredCount = computed(() => {
|
|
324
|
+
if (!searchQuery.value.trim()) return "";
|
|
325
|
+
return `${displayedLogs.value.length}`;
|
|
326
|
+
});
|
|
142
327
|
|
|
143
328
|
const path = "/api/updates?type=console";
|
|
144
329
|
const url = (window.location.protocol === "http:" ? "ws://" : "wss://") + window.location.host + path;
|
|
145
|
-
|
|
330
|
+
// Handle manual scroll detection
|
|
331
|
+
const handleScroll = (event) => {
|
|
332
|
+
if (!autoscrollToggled.value) return;
|
|
333
|
+
|
|
334
|
+
const element = event.target;
|
|
335
|
+
if (!element) return;
|
|
336
|
+
|
|
337
|
+
// Check if user is near bottom (within 50px)
|
|
338
|
+
const threshold = 50;
|
|
339
|
+
const isNearBottom = element.scrollTop + element.clientHeight >= element.scrollHeight - threshold;
|
|
340
|
+
|
|
341
|
+
// Only update if there's an actual change
|
|
342
|
+
if (userScrolledUp.value === isNearBottom) {
|
|
343
|
+
userScrolledUp.value = !isNearBottom;
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
// Bulletproof scroll function with multiple fallbacks
|
|
348
|
+
const performScroll = (direction, smooth = true) => {
|
|
146
349
|
try {
|
|
147
|
-
if (
|
|
148
|
-
|
|
350
|
+
if (!$autoscroll.value?.el) {
|
|
351
|
+
if (DEBUG) console.log("Autoscroll element not ready");
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const element = $autoscroll.value.el;
|
|
356
|
+
|
|
357
|
+
if (direction === "up") {
|
|
358
|
+
if (smooth && element.scrollTo) {
|
|
359
|
+
element.scrollTo({ top: 0, behavior: "smooth" });
|
|
360
|
+
} else {
|
|
361
|
+
element.scrollTop = 0;
|
|
362
|
+
}
|
|
363
|
+
} else {
|
|
364
|
+
const targetTop = element.scrollHeight - element.clientHeight;
|
|
365
|
+
if (smooth && element.scrollTo) {
|
|
366
|
+
element.scrollTo({ top: targetTop, behavior: "smooth" });
|
|
367
|
+
} else {
|
|
368
|
+
element.scrollTop = targetTop;
|
|
369
|
+
}
|
|
370
|
+
userScrolledUp.value = false;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return true;
|
|
149
374
|
} catch (e) {
|
|
150
375
|
if (DEBUG) console.log("Error scrolling", e);
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
// Continuous scrolling for held buttons
|
|
381
|
+
const startScrolling = (direction) => {
|
|
382
|
+
if (isScrolling.value) return;
|
|
383
|
+
|
|
384
|
+
isScrolling.value = true;
|
|
385
|
+
|
|
386
|
+
// Immediate scroll
|
|
387
|
+
performScroll(direction, true);
|
|
388
|
+
|
|
389
|
+
// Continue scrolling while held (for long content)
|
|
390
|
+
scrollInterval.value = setInterval(() => {
|
|
391
|
+
if (!isScrolling.value) return;
|
|
392
|
+
|
|
393
|
+
const element = $autoscroll.value?.el;
|
|
394
|
+
if (!element) return;
|
|
395
|
+
|
|
396
|
+
const scrollAmount = 100;
|
|
397
|
+
if (direction === "up") {
|
|
398
|
+
element.scrollTop = Math.max(0, element.scrollTop - scrollAmount);
|
|
399
|
+
} else {
|
|
400
|
+
element.scrollTop = Math.min(element.scrollHeight - element.clientHeight, element.scrollTop + scrollAmount);
|
|
401
|
+
}
|
|
402
|
+
}, 50);
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
const stopScrolling = () => {
|
|
406
|
+
isScrolling.value = false;
|
|
407
|
+
if (scrollInterval.value) {
|
|
408
|
+
clearInterval(scrollInterval.value);
|
|
409
|
+
scrollInterval.value = null;
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
// Legacy function for compatibility
|
|
414
|
+
const scrollTo = (dir) => {
|
|
415
|
+
performScroll(dir, true);
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
// Simple autoscroll to bottom
|
|
419
|
+
const autoScrollToBottom = () => {
|
|
420
|
+
if (!$autoscroll.value?.el || !autoscrollToggled.value) return;
|
|
421
|
+
|
|
422
|
+
// Only scroll if user hasn't manually scrolled up
|
|
423
|
+
if (!userScrolledUp.value) {
|
|
424
|
+
const element = $autoscroll.value.el;
|
|
425
|
+
|
|
426
|
+
// Calculate the target scroll position to show the last log
|
|
427
|
+
const targetScrollTop = element.scrollHeight - element.clientHeight;
|
|
428
|
+
|
|
429
|
+
// Use smooth scrolling to ensure new logs are visible
|
|
430
|
+
if (element.scrollTo && Math.abs(element.scrollTop - targetScrollTop) > 5) {
|
|
431
|
+
element.scrollTo({
|
|
432
|
+
top: targetScrollTop,
|
|
433
|
+
behavior: "smooth"
|
|
434
|
+
});
|
|
435
|
+
} else {
|
|
436
|
+
// For small differences or fallback, use instant scroll
|
|
437
|
+
element.scrollTop = targetScrollTop;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
// Handle autoscroll toggle
|
|
443
|
+
const onAutoscrollToggle = () => {
|
|
444
|
+
if (autoscrollToggled.value) {
|
|
445
|
+
userScrolledUp.value = false;
|
|
446
|
+
nextTick().then(autoScrollToBottom);
|
|
151
447
|
}
|
|
152
448
|
};
|
|
153
449
|
|
|
154
450
|
const addAnsiToOutput = (a) => {
|
|
155
|
-
const html = ansii.toHtml(a
|
|
451
|
+
const html = ansii.toHtml(a?.log || a);
|
|
156
452
|
logLines.value.push(html);
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
453
|
+
|
|
454
|
+
// Auto scroll after adding new content with proper timing
|
|
455
|
+
nextTick().then(() => {
|
|
456
|
+
// Use a small delay to ensure DOM is fully updated
|
|
457
|
+
setTimeout(autoScrollToBottom, 10);
|
|
458
|
+
});
|
|
161
459
|
};
|
|
162
460
|
|
|
163
461
|
const handleWebsocketMessages = (msg) => {
|
|
@@ -192,16 +490,30 @@ const makeTaskLogMapping = (lines) => {
|
|
|
192
490
|
|
|
193
491
|
window.startDebugConsoleMessages = () => {
|
|
194
492
|
setInterval(() => {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
493
|
+
var taskId = Math.round(Math.random() * 10);
|
|
494
|
+
const log = {
|
|
495
|
+
log: `\u001b[96m[12:16:02.273]\u001b[00m \u001b[96m[TASK-${taskId}]\u001b[00m TEST TEST TEST TEST TEST TEST TEST`,
|
|
496
|
+
metadata: {
|
|
497
|
+
taskId: taskId,
|
|
498
|
+
siteId: "TM_US",
|
|
499
|
+
global: false
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
addAnsiToOutput(log);
|
|
503
|
+
makeTaskLogMapping([log]);
|
|
504
|
+
}, 100);
|
|
201
505
|
};
|
|
202
506
|
|
|
203
507
|
if (DEBUG) window.startDebugConsoleMessages();
|
|
204
508
|
|
|
509
|
+
// Watch for log filter changes and reset scroll state
|
|
510
|
+
watch([currentTaskLog, filteredLogs], () => {
|
|
511
|
+
userScrolledUp.value = false;
|
|
512
|
+
nextTick().then(() => {
|
|
513
|
+
setTimeout(autoScrollToBottom, 10);
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
|
|
205
517
|
// Listen for messages
|
|
206
518
|
onMounted(() => {
|
|
207
519
|
const socket = new WebsocketHeartbeatJs({ url, pingMsg: "ping" });
|
|
@@ -214,4 +526,9 @@ onMounted(() => {
|
|
|
214
526
|
});
|
|
215
527
|
};
|
|
216
528
|
});
|
|
529
|
+
|
|
530
|
+
// Cleanup on unmount
|
|
531
|
+
onUnmounted(() => {
|
|
532
|
+
stopScrolling();
|
|
533
|
+
});
|
|
217
534
|
</script>
|