@necrolab/dashboard 0.4.3
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/.claude/settings.local.json +45 -0
- package/.eslintrc.js +24 -0
- package/.prettierignore +1 -0
- package/.prettierrc +10 -0
- package/.vscode/extensions.json +3 -0
- package/ICONS.md +21 -0
- package/README.md +65 -0
- package/backend/api.js +430 -0
- package/backend/auth.js +62 -0
- package/backend/batching.js +43 -0
- package/backend/endpoints.js +343 -0
- package/backend/index.js +23 -0
- package/backend/mock-data.js +66 -0
- package/backend/mock-src/classes/logger.js +112 -0
- package/backend/mock-src/classes/utils.js +42 -0
- package/backend/mock-src/ticketmaster.js +92 -0
- package/backend/validator.js +62 -0
- package/config/configs.json +20 -0
- package/config/filter.json +3 -0
- package/config/presale.csv +3 -0
- package/config/proxies.txt +6 -0
- package/config/used-codes.json +4 -0
- package/index.html +114 -0
- package/index.js +2 -0
- package/jsconfig.json +16 -0
- package/package.json +48 -0
- package/postcss.config.js +6 -0
- package/postinstall.js +9 -0
- 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/flags/ae.svg +1 -0
- package/public/flags/at.svg +1 -0
- package/public/flags/au.svg +1 -0
- package/public/flags/be.svg +1 -0
- package/public/flags/ch.svg +1 -0
- package/public/flags/cz.svg +1 -0
- package/public/flags/de.svg +1 -0
- package/public/flags/dk.svg +1 -0
- package/public/flags/es.svg +1 -0
- package/public/flags/nl.svg +1 -0
- package/public/flags/no.svg +1 -0
- package/public/flags/nz.svg +1 -0
- package/public/flags/pl.svg +1 -0
- package/public/flags/se.svg +1 -0
- package/public/flags/uk.svg +1 -0
- package/public/flags/us.svg +1 -0
- package/public/img/award.svg +3 -0
- package/public/img/background.svg +14 -0
- package/public/img/bag_w.svg +12 -0
- package/public/img/banks/amex.svg +4 -0
- package/public/img/banks/mastercard.svg +4 -0
- package/public/img/banks/visa.svg +4 -0
- package/public/img/camera.svg +3 -0
- package/public/img/close.svg +3 -0
- package/public/img/controls/disable.svg +5 -0
- package/public/img/controls/enable.svg +5 -0
- package/public/img/groups.svg +3 -0
- package/public/img/hand.svg +3 -0
- package/public/img/key.svg +3 -0
- 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/public/img/logo_trans.png +0 -0
- package/public/img/loyalty.svg +3 -0
- package/public/img/mail.svg +3 -0
- package/public/img/pencil.svg +3 -0
- package/public/img/profile.svg +4 -0
- package/public/img/reload.svg +3 -0
- package/public/img/sandclock.svg +25 -0
- package/public/img/save.svg +5 -0
- package/public/img/savings.svg +3 -0
- package/public/img/scanner.svg +3 -0
- package/public/img/sell.svg +3 -0
- package/public/img/shield.svg +3 -0
- package/public/img/ski.svg +3 -0
- package/public/img/stadium.svg +8 -0
- package/public/img/stadium_w.svg +8 -0
- package/public/img/timer.svg +3 -0
- package/public/manifest.json +27 -0
- package/public/robots.txt +2 -0
- package/run +10 -0
- package/src/App.vue +307 -0
- package/src/assets/css/_input.scss +197 -0
- package/src/assets/css/main.scss +269 -0
- package/src/assets/css/tailwind.css +3 -0
- package/src/assets/img/award.svg +3 -0
- package/src/assets/img/background.svg +11 -0
- package/src/assets/img/camera.svg +3 -0
- package/src/assets/img/close.svg +3 -0
- package/src/assets/img/eyes/closed.svg +13 -0
- package/src/assets/img/eyes/open.svg +12 -0
- package/src/assets/img/groups.svg +3 -0
- package/src/assets/img/hand.svg +3 -0
- package/src/assets/img/key.svg +3 -0
- 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/src/assets/img/logo_trans.png +0 -0
- package/src/assets/img/loyalty.svg +3 -0
- package/src/assets/img/mail.svg +3 -0
- package/src/assets/img/pencil.svg +3 -0
- package/src/assets/img/reload.svg +3 -0
- package/src/assets/img/savings.svg +3 -0
- package/src/assets/img/scanner.svg +3 -0
- package/src/assets/img/sell.svg +3 -0
- package/src/assets/img/shield.svg +3 -0
- package/src/assets/img/ski.svg +3 -0
- package/src/assets/img/square_check.svg +5 -0
- package/src/assets/img/square_uncheck.svg +5 -0
- package/src/assets/img/stadium.svg +8 -0
- package/src/assets/img/timer.svg +3 -0
- package/src/assets/img/wildcard.svg +7 -0
- package/src/components/Auth/LoginForm.vue +48 -0
- package/src/components/Editors/Account/Account.vue +119 -0
- package/src/components/Editors/Account/AccountCreator.vue +147 -0
- package/src/components/Editors/Account/AccountView.vue +87 -0
- package/src/components/Editors/Account/CreateAccount.vue +106 -0
- package/src/components/Editors/Profile/CreateProfile.vue +321 -0
- package/src/components/Editors/Profile/Profile.vue +142 -0
- package/src/components/Editors/Profile/ProfileCountryChooser.vue +75 -0
- package/src/components/Editors/Profile/ProfileView.vue +96 -0
- package/src/components/Editors/TagLabel.vue +16 -0
- package/src/components/Editors/TagToggle.vue +41 -0
- package/src/components/Filter/Filter.vue +409 -0
- package/src/components/Filter/FilterPreview.vue +236 -0
- package/src/components/Filter/PriceSortToggle.vue +105 -0
- package/src/components/Table/Header.vue +5 -0
- package/src/components/Table/Row.vue +5 -0
- package/src/components/Table/Table.vue +14 -0
- package/src/components/Table/index.js +4 -0
- package/src/components/Tasks/CheckStock.vue +62 -0
- package/src/components/Tasks/Controls/DesktopControls.vue +73 -0
- package/src/components/Tasks/Controls/MobileControls.vue +32 -0
- package/src/components/Tasks/Controls/index.js +3 -0
- package/src/components/Tasks/CreateTaskAXS.vue +339 -0
- package/src/components/Tasks/CreateTaskTM.vue +459 -0
- package/src/components/Tasks/MassEdit.vue +50 -0
- package/src/components/Tasks/QuickSettings.vue +167 -0
- package/src/components/Tasks/ScrapeVenue.vue +42 -0
- package/src/components/Tasks/Stats.vue +66 -0
- package/src/components/Tasks/Task.vue +296 -0
- package/src/components/Tasks/TaskLabel.vue +20 -0
- package/src/components/Tasks/TaskView.vue +126 -0
- package/src/components/Tasks/Utilities.vue +33 -0
- package/src/components/icons/Award.vue +8 -0
- package/src/components/icons/Bag.vue +8 -0
- package/src/components/icons/BagWhite.vue +8 -0
- package/src/components/icons/Box.vue +8 -0
- package/src/components/icons/Camera.vue +8 -0
- package/src/components/icons/Cart.vue +8 -0
- package/src/components/icons/Check.vue +5 -0
- package/src/components/icons/Checkmark.vue +11 -0
- package/src/components/icons/Click.vue +8 -0
- package/src/components/icons/Close.vue +21 -0
- package/src/components/icons/CloseX.vue +5 -0
- package/src/components/icons/Console.vue +13 -0
- package/src/components/icons/Down.vue +8 -0
- package/src/components/icons/Edit.vue +13 -0
- package/src/components/icons/Event.vue +8 -0
- package/src/components/icons/Expand.vue +8 -0
- package/src/components/icons/Filter.vue +13 -0
- package/src/components/icons/Gear.vue +8 -0
- package/src/components/icons/Group.vue +8 -0
- package/src/components/icons/Hand.vue +8 -0
- package/src/components/icons/Key.vue +21 -0
- package/src/components/icons/Logout.vue +13 -0
- package/src/components/icons/Loyalty.vue +8 -0
- package/src/components/icons/Mail.vue +8 -0
- package/src/components/icons/Menu.vue +8 -0
- package/src/components/icons/Pause.vue +5 -0
- package/src/components/icons/Pencil.vue +21 -0
- package/src/components/icons/Play.vue +8 -0
- package/src/components/icons/Plus.vue +8 -0
- package/src/components/icons/Profile.vue +18 -0
- package/src/components/icons/Reload.vue +7 -0
- package/src/components/icons/Sandclock.vue +33 -0
- package/src/components/icons/Savings.vue +8 -0
- package/src/components/icons/Scanner.vue +8 -0
- package/src/components/icons/Scrape.vue +8 -0
- package/src/components/icons/Sell.vue +21 -0
- package/src/components/icons/Shield.vue +8 -0
- package/src/components/icons/Shrink.vue +8 -0
- package/src/components/icons/Ski.vue +8 -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 +13 -0
- package/src/components/icons/StadiumWhite.vue +13 -0
- package/src/components/icons/Status.vue +8 -0
- package/src/components/icons/Tag.vue +8 -0
- package/src/components/icons/Tasks.vue +13 -0
- package/src/components/icons/Ticket.vue +8 -0
- package/src/components/icons/Timer.vue +8 -0
- package/src/components/icons/Trash.vue +8 -0
- package/src/components/icons/Up.vue +10 -0
- package/src/components/icons/Wildcard.vue +18 -0
- package/src/components/icons/index.js +111 -0
- package/src/components/ui/Modal.vue +61 -0
- package/src/components/ui/Navbar.vue +207 -0
- package/src/components/ui/ReconnectIndicator.vue +90 -0
- package/src/components/ui/Splash.vue +24 -0
- package/src/components/ui/controls/CountryChooser.vue +87 -0
- package/src/components/ui/controls/EyeToggle.vue +11 -0
- package/src/components/ui/controls/atomic/Checkbox.vue +28 -0
- package/src/components/ui/controls/atomic/Dropdown.vue +138 -0
- package/src/components/ui/controls/atomic/LoadingButton.vue +45 -0
- package/src/components/ui/controls/atomic/MultiDropdown.vue +262 -0
- package/src/components/ui/controls/atomic/Switch.vue +84 -0
- package/src/libs/Filter.js +593 -0
- package/src/libs/ansii.js +565 -0
- package/src/libs/panzoom.js +1413 -0
- package/src/main.js +23 -0
- package/src/registerServiceWorker.js +32 -0
- package/src/router/index.js +65 -0
- package/src/stores/cities.json +1 -0
- package/src/stores/connection.js +399 -0
- package/src/stores/countries.js +88 -0
- package/src/stores/logger.js +103 -0
- package/src/stores/requests.js +88 -0
- package/src/stores/sampleData.js +1034 -0
- package/src/stores/ui.js +584 -0
- package/src/stores/utils.js +554 -0
- package/src/types/index.js +42 -0
- package/src/utils/debug.js +1 -0
- package/src/views/Accounts.vue +191 -0
- package/src/views/Console.vue +224 -0
- package/src/views/Editor.vue +785 -0
- package/src/views/FilterBuilder.vue +785 -0
- package/src/views/Login.vue +27 -0
- package/src/views/Profiles.vue +209 -0
- package/src/views/Tasks.vue +157 -0
- package/static/offline.html +184 -0
- package/tailwind.config.js +57 -0
- package/vite.config.js +63 -0
- package/vue.config.js +32 -0
- package/workbox-config.js +66 -0
|
@@ -0,0 +1,785 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<!-- Heading -->
|
|
4
|
+
<GearIcon class="w-5 cursor-pointer" @click="ui.toggleModal('quick-settings')" />
|
|
5
|
+
<h4 class="text-white text-xl font-bold mb-5 pt-5 flex gap-2 items-center">
|
|
6
|
+
Editor <img src="@/assets/img/pencil.svg" />
|
|
7
|
+
</h4>
|
|
8
|
+
|
|
9
|
+
<div class="card-dark p-2">
|
|
10
|
+
<div v-if="ui.profile.admin">
|
|
11
|
+
<h5 class="text-white text-xl font-bold flex gap-x-3 mb-3">Admin Editor</h5>
|
|
12
|
+
<div class="flex justify-between items-center">
|
|
13
|
+
<div class="w-64 flex">
|
|
14
|
+
<div class="relative flex-grow">
|
|
15
|
+
<Dropdown
|
|
16
|
+
class="bg-dark-500 border-2 border-r-0 border-dark-550 admin-file-dropdown w-full"
|
|
17
|
+
default="Select a file"
|
|
18
|
+
:onClick="(f) => loadFile(f)"
|
|
19
|
+
:options="availableFiles"
|
|
20
|
+
:allowDefault="false"
|
|
21
|
+
rightAmount="right-1"
|
|
22
|
+
/>
|
|
23
|
+
</div>
|
|
24
|
+
<button
|
|
25
|
+
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
|
+
@click="loadAvailableFiles()"
|
|
27
|
+
title="Refresh file list"
|
|
28
|
+
>
|
|
29
|
+
<ReloadIcon class="refresh-icon" />
|
|
30
|
+
</button>
|
|
31
|
+
</div>
|
|
32
|
+
<!-- Admin editor buttons moved here -->
|
|
33
|
+
<div v-if="textEditorVisible" class="flex gap-x-2" style="margin-right: 1px;">
|
|
34
|
+
<button class="btn-action" @click="format()" v-if="isJsonFile()">
|
|
35
|
+
<svg
|
|
36
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
37
|
+
width="16"
|
|
38
|
+
height="16"
|
|
39
|
+
viewBox="0 0 24 24"
|
|
40
|
+
fill="none"
|
|
41
|
+
stroke="currentColor"
|
|
42
|
+
stroke-width="2"
|
|
43
|
+
stroke-linecap="round"
|
|
44
|
+
stroke-linejoin="round"
|
|
45
|
+
>
|
|
46
|
+
<path d="M21 10H7" />
|
|
47
|
+
<path d="M21 6H3" />
|
|
48
|
+
<path d="M21 14H3" />
|
|
49
|
+
<path d="M21 18H7" />
|
|
50
|
+
</svg>
|
|
51
|
+
<span>Format</span>
|
|
52
|
+
</button>
|
|
53
|
+
<button class="btn-action" @click="saveAll">
|
|
54
|
+
<svg
|
|
55
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
56
|
+
width="16"
|
|
57
|
+
height="16"
|
|
58
|
+
viewBox="0 0 24 24"
|
|
59
|
+
fill="none"
|
|
60
|
+
stroke="currentColor"
|
|
61
|
+
stroke-width="2"
|
|
62
|
+
stroke-linecap="round"
|
|
63
|
+
stroke-linejoin="round"
|
|
64
|
+
>
|
|
65
|
+
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
|
|
66
|
+
<polyline points="17 21 17 13 7 13 7 21" />
|
|
67
|
+
<polyline points="7 3 7 8 15 8" />
|
|
68
|
+
</svg>
|
|
69
|
+
<span>Save File</span>
|
|
70
|
+
</button>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<transition name="fade">
|
|
76
|
+
<div v-if="textEditorVisible" class="my-3 relative">
|
|
77
|
+
<div class="pb-4">
|
|
78
|
+
<!-- Syntax Highlighting Editor -->
|
|
79
|
+
<div class="editor-container">
|
|
80
|
+
<div class="editor-wrapper">
|
|
81
|
+
<pre ref="codeDisplay" class="language-json code-highlight"></pre>
|
|
82
|
+
<textarea
|
|
83
|
+
ref="codeEditor"
|
|
84
|
+
v-model="currentContent"
|
|
85
|
+
class="code-editor"
|
|
86
|
+
spellcheck="false"
|
|
87
|
+
@scroll="syncScroll"
|
|
88
|
+
@input="highlightCode"
|
|
89
|
+
@keydown.tab.prevent="handleTab"
|
|
90
|
+
></textarea>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<p class="text-red-400 text-bold">{{ errorMessage }}</p>
|
|
96
|
+
</div>
|
|
97
|
+
</transition>
|
|
98
|
+
<div class="border border-light-300 my-8" />
|
|
99
|
+
|
|
100
|
+
<h5 class="text-white text-xl font-bold flex gap-x-3 mb-3">Proxy Editor</h5>
|
|
101
|
+
<div class="flex justify-between items-center mb-4 relative z-60">
|
|
102
|
+
<div class="w-64">
|
|
103
|
+
<div class="relative">
|
|
104
|
+
<Dropdown
|
|
105
|
+
class="bg-dark-500 border-2 border-dark-550 rounded-lg w-full h-10"
|
|
106
|
+
:default="ui.profile.proxyList?.checkout || proxyLists[0]"
|
|
107
|
+
:onClick="loadProxies"
|
|
108
|
+
:options="proxyLists"
|
|
109
|
+
:allowDefault="false"
|
|
110
|
+
rightAmount="right-1"
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
<!-- Proxy save button moved here -->
|
|
115
|
+
<div v-if="currentProxyList" style="margin-right: 1px;">
|
|
116
|
+
<button class="btn-action h-12" @click="saveProxies">
|
|
117
|
+
<svg
|
|
118
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
119
|
+
width="16"
|
|
120
|
+
height="16"
|
|
121
|
+
viewBox="0 0 24 24"
|
|
122
|
+
fill="none"
|
|
123
|
+
stroke="currentColor"
|
|
124
|
+
stroke-width="2"
|
|
125
|
+
stroke-linecap="round"
|
|
126
|
+
stroke-linejoin="round"
|
|
127
|
+
>
|
|
128
|
+
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
|
|
129
|
+
<polyline points="17 21 17 13 7 13 7 21" />
|
|
130
|
+
<polyline points="7 3 7 8 15 8" />
|
|
131
|
+
</svg>
|
|
132
|
+
<span>Save Proxies</span>
|
|
133
|
+
</button>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
<!-- Textarea -->
|
|
137
|
+
<transition name="fade">
|
|
138
|
+
<div v-if="currentProxyList" class="relative my-3">
|
|
139
|
+
<div class="pb-4">
|
|
140
|
+
<div class="proxy-editor-container">
|
|
141
|
+
<textarea
|
|
142
|
+
v-model="proxyContent"
|
|
143
|
+
class="proxy-editor"
|
|
144
|
+
spellcheck="false"
|
|
145
|
+
></textarea>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</transition>
|
|
150
|
+
</div>
|
|
151
|
+
<QuickSettings v-if="activeModal === 'quick-settings'" />
|
|
152
|
+
</div>
|
|
153
|
+
</template>
|
|
154
|
+
<script setup>
|
|
155
|
+
import { ref, computed, watch, onMounted, nextTick } from "vue";
|
|
156
|
+
import { DownIcon, ReloadIcon, EditIcon } from "@/components/icons";
|
|
157
|
+
import { useUIStore } from "@/stores/ui";
|
|
158
|
+
import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
|
|
159
|
+
import { getProxyLists, getProxyFile } from "@/stores/requests";
|
|
160
|
+
import { DEBUG } from "@/utils/debug";
|
|
161
|
+
|
|
162
|
+
const loadFromApi = async (path) => {
|
|
163
|
+
const res = await fetch(path);
|
|
164
|
+
return res.json();
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const availableFiles = ref([]);
|
|
168
|
+
if (DEBUG) availableFiles.value.push("used-codes.json", "proxies.txt", "toMonitor.txt", "test.txt");
|
|
169
|
+
const currentFile = ref("");
|
|
170
|
+
const textEditorVisible = ref(false);
|
|
171
|
+
const currentContent = ref("");
|
|
172
|
+
const proxyContent = ref("");
|
|
173
|
+
const proxyLists = ref(["loading..."]);
|
|
174
|
+
const currentProxyList = ref("");
|
|
175
|
+
const activeModal = computed(() => ui.activeModal);
|
|
176
|
+
const ui = useUIStore();
|
|
177
|
+
|
|
178
|
+
// References to editor elements
|
|
179
|
+
const codeEditor = ref(null);
|
|
180
|
+
const codeDisplay = ref(null);
|
|
181
|
+
|
|
182
|
+
let previous = {
|
|
183
|
+
proxyContent: { list: ui.profile.proxyList, content: "" },
|
|
184
|
+
currentContent: { file: "", content: "" }
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const errorMessage = computed(() => {
|
|
188
|
+
if (!currentFile.value.endsWith(".json")) return;
|
|
189
|
+
|
|
190
|
+
let err;
|
|
191
|
+
try {
|
|
192
|
+
JSON.parse(currentContent.value);
|
|
193
|
+
} catch (e) {
|
|
194
|
+
err = e;
|
|
195
|
+
}
|
|
196
|
+
return err?.message;
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const loadAvailableFiles = async () => (availableFiles.value = await loadFromApi("/api/json-files"));
|
|
200
|
+
|
|
201
|
+
const isJsonFile = () => currentFile.value.endsWith(".json");
|
|
202
|
+
|
|
203
|
+
// Function to highlight code using Prism
|
|
204
|
+
const highlightCode = () => {
|
|
205
|
+
if (!codeDisplay.value || !codeEditor.value) return;
|
|
206
|
+
|
|
207
|
+
// Get the language based on file extension
|
|
208
|
+
let language = "javascript"; // Default language
|
|
209
|
+
|
|
210
|
+
if (currentFile.value) {
|
|
211
|
+
if (currentFile.value.endsWith(".json")) {
|
|
212
|
+
language = "json";
|
|
213
|
+
} else if (currentFile.value.endsWith(".js")) {
|
|
214
|
+
language = "javascript";
|
|
215
|
+
} else if (currentFile.value.endsWith(".txt") || currentFile.value.endsWith(".csv")) {
|
|
216
|
+
language = "text";
|
|
217
|
+
// Plain text doesn't need highlighting
|
|
218
|
+
codeDisplay.value.textContent = currentContent.value || "";
|
|
219
|
+
codeDisplay.value.className = "language-text code-highlight";
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Ensure Prism is available
|
|
225
|
+
if (typeof Prism === "undefined") {
|
|
226
|
+
console.error("Prism is not loaded");
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Use requestAnimationFrame for smoother updates
|
|
231
|
+
requestAnimationFrame(() => {
|
|
232
|
+
try {
|
|
233
|
+
// Update the pre element with highlighted HTML
|
|
234
|
+
const highlighted = Prism.highlight(currentContent.value || "", Prism.languages[language], language);
|
|
235
|
+
codeDisplay.value.innerHTML = highlighted;
|
|
236
|
+
codeDisplay.value.className = `language-${language} code-highlight`;
|
|
237
|
+
} catch (e) {
|
|
238
|
+
console.error("Highlight error:", e);
|
|
239
|
+
// Fallback to plain text if highlighting fails
|
|
240
|
+
codeDisplay.value.textContent = currentContent.value || "";
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Ensure scroll positions are synced after highlighting
|
|
244
|
+
syncScroll();
|
|
245
|
+
});
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// Function to sync scrolling between textarea and highlighted code
|
|
249
|
+
const syncScroll = () => {
|
|
250
|
+
if (!codeDisplay.value || !codeEditor.value) return;
|
|
251
|
+
|
|
252
|
+
// Synchronize scrolling between the textarea and the highlighted code
|
|
253
|
+
requestAnimationFrame(() => {
|
|
254
|
+
codeDisplay.value.scrollTop = codeEditor.value.scrollTop;
|
|
255
|
+
codeDisplay.value.scrollLeft = codeEditor.value.scrollLeft;
|
|
256
|
+
});
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// Function to handle tab key press in the editor
|
|
260
|
+
const handleTab = (e) => {
|
|
261
|
+
const textarea = codeEditor.value;
|
|
262
|
+
const start = textarea.selectionStart;
|
|
263
|
+
const end = textarea.selectionEnd;
|
|
264
|
+
|
|
265
|
+
// Insert 4 spaces at cursor position
|
|
266
|
+
const spaces = " ";
|
|
267
|
+
currentContent.value = currentContent.value.substring(0, start) + spaces + currentContent.value.substring(end);
|
|
268
|
+
|
|
269
|
+
// Move cursor position after the inserted tab
|
|
270
|
+
nextTick(() => {
|
|
271
|
+
textarea.selectionStart = textarea.selectionEnd = start + spaces.length;
|
|
272
|
+
highlightCode();
|
|
273
|
+
});
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const format = () => {
|
|
277
|
+
try {
|
|
278
|
+
if (!isJsonFile()) return;
|
|
279
|
+
|
|
280
|
+
const formatted = JSON.stringify(JSON.parse(currentContent.value), null, 4);
|
|
281
|
+
currentContent.value = formatted;
|
|
282
|
+
|
|
283
|
+
// Apply syntax highlighting after formatting
|
|
284
|
+
nextTick(() => {
|
|
285
|
+
highlightCode();
|
|
286
|
+
});
|
|
287
|
+
} catch (e) {
|
|
288
|
+
ui.logger.Error("Could not format JSON", e);
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// Watch for content changes to apply syntax highlighting
|
|
293
|
+
watch(currentContent, () => {
|
|
294
|
+
nextTick(() => {
|
|
295
|
+
highlightCode();
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
const loadFile = async (f) => {
|
|
300
|
+
currentFile.value = f;
|
|
301
|
+
if (DEBUG) {
|
|
302
|
+
textEditorVisible.value = true;
|
|
303
|
+
currentProxyList.value = "";
|
|
304
|
+
currentContent.value = new Array(100).fill(0, 0, 100).join("\n");
|
|
305
|
+
|
|
306
|
+
// Update pre element class based on file type
|
|
307
|
+
if (codeDisplay.value) {
|
|
308
|
+
if (f.endsWith(".json")) {
|
|
309
|
+
codeDisplay.value.className = "language-json";
|
|
310
|
+
} else if (f.endsWith(".js")) {
|
|
311
|
+
codeDisplay.value.className = "language-javascript";
|
|
312
|
+
} else {
|
|
313
|
+
codeDisplay.value.className = "language-text";
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Apply syntax highlighting after content is set
|
|
318
|
+
nextTick(() => {
|
|
319
|
+
highlightCode();
|
|
320
|
+
});
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const res = await loadFromApi("/api/json-file?file=" + currentFile.value);
|
|
325
|
+
const text = atob(res.content);
|
|
326
|
+
textEditorVisible.value = true;
|
|
327
|
+
currentProxyList.value = "";
|
|
328
|
+
currentContent.value = text;
|
|
329
|
+
previous.currentContent = { file: currentFile.value, content: text };
|
|
330
|
+
|
|
331
|
+
// Update pre element class based on file type
|
|
332
|
+
if (codeDisplay.value) {
|
|
333
|
+
if (f.endsWith(".json")) {
|
|
334
|
+
codeDisplay.value.className = "language-json";
|
|
335
|
+
} else if (f.endsWith(".js")) {
|
|
336
|
+
codeDisplay.value.className = "language-javascript";
|
|
337
|
+
} else {
|
|
338
|
+
codeDisplay.value.className = "language-text";
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Apply syntax highlighting after content is loaded
|
|
343
|
+
nextTick(() => {
|
|
344
|
+
highlightCode();
|
|
345
|
+
});
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const saveDataToAPI = async (data, endpoint, msg, json = true) => {
|
|
349
|
+
if (DEBUG) return;
|
|
350
|
+
const options = {
|
|
351
|
+
method: "POST",
|
|
352
|
+
headers: { "Content-Type": "application/json" },
|
|
353
|
+
body: JSON.stringify({ content: data })
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
let j;
|
|
357
|
+
const res = await fetch(endpoint, options);
|
|
358
|
+
if (json) {
|
|
359
|
+
try {
|
|
360
|
+
j = await res.json();
|
|
361
|
+
} catch (e) {
|
|
362
|
+
ui.showError("Error saving proxies");
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (j?.error) return ui.showError(j.error);
|
|
366
|
+
} else {
|
|
367
|
+
try {
|
|
368
|
+
j = await res.text();
|
|
369
|
+
} catch (e) {
|
|
370
|
+
ui.showError("Error saving proxies");
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
ui.showSuccess(msg);
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const saveFile = async () => {
|
|
378
|
+
if (
|
|
379
|
+
btoa(currentContent.value) === previous.currentContent.content &&
|
|
380
|
+
currentFile.value === previous.currentContent.file
|
|
381
|
+
)
|
|
382
|
+
return ui.logger.Info("Content did not change");
|
|
383
|
+
saveDataToAPI(btoa(currentContent.value), "/api/json-file?file=" + currentFile.value, "Successfully saved file");
|
|
384
|
+
previous.currentContent = { file: currentFile.value, content: btoa(currentContent.value) };
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
const saveAll = async () => {
|
|
388
|
+
if (DEBUG) return;
|
|
389
|
+
if (currentFile.value && currentContent.value) await saveFile();
|
|
390
|
+
await saveQuickConfig();
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const loadProxies = async (list) => {
|
|
394
|
+
try {
|
|
395
|
+
if (DEBUG) {
|
|
396
|
+
currentProxyList.value = list;
|
|
397
|
+
textEditorVisible.value = false;
|
|
398
|
+
proxyContent.value = new Array(100).fill(0, 0, 100).join("\n");
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
ui.logger.Info("Loading proxies: " + list);
|
|
403
|
+
currentProxyList.value = list;
|
|
404
|
+
const file = await getProxyFile(list);
|
|
405
|
+
proxyContent.value = file;
|
|
406
|
+
previous.proxyContent = { content: proxyContent.value };
|
|
407
|
+
textEditorVisible.value = false;
|
|
408
|
+
} catch (ex) {
|
|
409
|
+
ui.logger.Error("Could not load proxies", ex);
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const saveProxies = async () => {
|
|
414
|
+
// if (!DEBUG) return;
|
|
415
|
+
if (previous.proxyContent.content === proxyContent.value) ui.logger.Info("proxies did not change");
|
|
416
|
+
else {
|
|
417
|
+
await saveDataToAPI(
|
|
418
|
+
btoa(proxyContent.value),
|
|
419
|
+
"/api/proxies?file=" + currentProxyList.value,
|
|
420
|
+
"Successfully saved proxies",
|
|
421
|
+
false
|
|
422
|
+
);
|
|
423
|
+
previous.proxyContent.content = btoa(proxyContent.value);
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
const loadProxyLists = async () => {
|
|
428
|
+
if (DEBUG) return (proxyLists.value = ["test-proxies", "recaptcha-proxies", "checkout-proxies", "queue-proxies"]);
|
|
429
|
+
const res = await getProxyLists();
|
|
430
|
+
if (Array.isArray(res)) proxyLists.value = res;
|
|
431
|
+
else ui.showError("Could not load proxylists");
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
// Initialize syntax highlighting
|
|
435
|
+
onMounted(() => {
|
|
436
|
+
// Apply highlighting when the component is mounted
|
|
437
|
+
nextTick(() => {
|
|
438
|
+
highlightCode();
|
|
439
|
+
|
|
440
|
+
// Ensure scroll synchronization on initial load
|
|
441
|
+
if (codeEditor.value && codeDisplay.value) {
|
|
442
|
+
syncScroll();
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// Watch for editor visibility changes
|
|
447
|
+
watch(textEditorVisible, (newValue) => {
|
|
448
|
+
if (newValue) {
|
|
449
|
+
nextTick(() => {
|
|
450
|
+
highlightCode();
|
|
451
|
+
|
|
452
|
+
// Focus the editor when it becomes visible
|
|
453
|
+
if (codeEditor.value) {
|
|
454
|
+
codeEditor.value.focus();
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
if (ui.profile.admin && !DEBUG) loadAvailableFiles();
|
|
462
|
+
loadProxyLists();
|
|
463
|
+
</script>
|
|
464
|
+
<style lang="scss" scoped>
|
|
465
|
+
.console {
|
|
466
|
+
@apply bg-dark-400 p-7 rounded relative border-dark-550 border-2;
|
|
467
|
+
|
|
468
|
+
textarea {
|
|
469
|
+
background: transparent;
|
|
470
|
+
resize: none;
|
|
471
|
+
height: calc(100vh - 450px);
|
|
472
|
+
@apply w-full focus:outline-none text-xs text-white;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
$border: 2px;
|
|
476
|
+
|
|
477
|
+
// &:before {
|
|
478
|
+
// content: "";
|
|
479
|
+
// @apply absolute top-0 left-0 right-0 bottom-0 opacity-60;
|
|
480
|
+
// z-index: -2;
|
|
481
|
+
// margin: -$border;
|
|
482
|
+
// border-radius: inherit;
|
|
483
|
+
// background: radial-gradient(rgba(96, 66, 255, 0.6), rgba(255, 255, 255, 0));
|
|
484
|
+
// }
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.z-inf {
|
|
488
|
+
z-index: 1000;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.z-inf2 {
|
|
492
|
+
z-index: 2000;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.z-inf3 {
|
|
496
|
+
z-index: 3000;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.special-dropdown {
|
|
500
|
+
margin-left: 0 !important;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.button-default-smaller -smaller {
|
|
504
|
+
@apply h-10;
|
|
505
|
+
border-radius: 0.5rem;
|
|
506
|
+
font-weight: 500;
|
|
507
|
+
--tw-text-opacity: 1;
|
|
508
|
+
color: rgb(255 255 255 / 1);
|
|
509
|
+
transition-duration: 150ms;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/* Modern dropdown with refresh button styles */
|
|
513
|
+
/* Removed custom dropdown styling to ensure default behavior */
|
|
514
|
+
|
|
515
|
+
.refresh-button {
|
|
516
|
+
&:hover {
|
|
517
|
+
.refresh-icon {
|
|
518
|
+
transform: rotate(180deg);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.refresh-icon {
|
|
524
|
+
transition: transform 0.5s ease;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/* Admin file dropdown should match refresh button height and have no right corners */
|
|
528
|
+
.admin-file-dropdown {
|
|
529
|
+
height: 2.5rem !important; /* Always 40px to match refresh button */
|
|
530
|
+
border-top-right-radius: 0 !important;
|
|
531
|
+
border-bottom-right-radius: 0 !important;
|
|
532
|
+
border-top-left-radius: 0.5rem !important; /* lg border radius */
|
|
533
|
+
border-bottom-left-radius: 0.5rem !important; /* lg border radius */
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/* Prism.js syntax highlighting styles */
|
|
537
|
+
.editor-container {
|
|
538
|
+
position: relative;
|
|
539
|
+
min-height: 300px;
|
|
540
|
+
max-height: 600px;
|
|
541
|
+
border-radius: 8px;
|
|
542
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
|
543
|
+
overflow: hidden;
|
|
544
|
+
background-color: #1a1a2e;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.editor-wrapper {
|
|
548
|
+
position: relative;
|
|
549
|
+
width: 100%;
|
|
550
|
+
height: 100%;
|
|
551
|
+
min-height: 300px;
|
|
552
|
+
max-height: 600px;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
.code-editor {
|
|
556
|
+
width: 100%;
|
|
557
|
+
height: 100%;
|
|
558
|
+
min-height: 300px;
|
|
559
|
+
max-height: 600px;
|
|
560
|
+
background-color: transparent;
|
|
561
|
+
/* Make text completely transparent */
|
|
562
|
+
color: rgba(0, 0, 0, 0);
|
|
563
|
+
caret-color: #e2e8f0;
|
|
564
|
+
font-family: "JetBrains Mono", "Fira Code", "Menlo", "Monaco", "Courier New", monospace;
|
|
565
|
+
padding: 12px;
|
|
566
|
+
border: none;
|
|
567
|
+
resize: none;
|
|
568
|
+
font-size: 14px;
|
|
569
|
+
line-height: 1.6;
|
|
570
|
+
tab-size: 4;
|
|
571
|
+
outline: none;
|
|
572
|
+
border: 1px solid #2d2d3b;
|
|
573
|
+
border-radius: 8px;
|
|
574
|
+
z-index: 10;
|
|
575
|
+
position: absolute;
|
|
576
|
+
top: 0;
|
|
577
|
+
left: 0;
|
|
578
|
+
right: 0;
|
|
579
|
+
bottom: 0;
|
|
580
|
+
white-space: pre;
|
|
581
|
+
overflow: auto;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/* Enhance the text selection color */
|
|
585
|
+
.code-editor::selection {
|
|
586
|
+
background: rgba(98, 114, 164, 0.4);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
.code-highlight {
|
|
590
|
+
width: 100%;
|
|
591
|
+
height: 100%;
|
|
592
|
+
min-height: 300px;
|
|
593
|
+
max-height: 600px;
|
|
594
|
+
overflow: auto;
|
|
595
|
+
white-space: pre;
|
|
596
|
+
font-family: "JetBrains Mono", "Fira Code", "Menlo", "Monaco", "Courier New", monospace;
|
|
597
|
+
font-size: 14px;
|
|
598
|
+
line-height: 1.6;
|
|
599
|
+
background-color: transparent !important;
|
|
600
|
+
pointer-events: none;
|
|
601
|
+
z-index: 5;
|
|
602
|
+
position: absolute;
|
|
603
|
+
top: 0;
|
|
604
|
+
left: 0;
|
|
605
|
+
right: 0;
|
|
606
|
+
bottom: 0;
|
|
607
|
+
padding: 12px;
|
|
608
|
+
margin: 0;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.code-editor,
|
|
612
|
+
.prism-editor {
|
|
613
|
+
font-family: "Menlo", "Monaco", "Courier New", monospace !important;
|
|
614
|
+
font-size: 14px !important;
|
|
615
|
+
line-height: 1.5 !important;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/* Proxy editor container */
|
|
619
|
+
.proxy-editor-container {
|
|
620
|
+
@apply w-full overflow-hidden rounded-lg border border-dark-550;
|
|
621
|
+
height: 400px;
|
|
622
|
+
max-height: 60vh;
|
|
623
|
+
background-color: #1a1a2e;
|
|
624
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
@media (min-width: 1024px) {
|
|
628
|
+
.proxy-editor-container {
|
|
629
|
+
max-height: 60vh;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
@media (min-width: 1280px) {
|
|
634
|
+
.proxy-editor-container {
|
|
635
|
+
max-height: 70vh;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/* Proxy editor styles */
|
|
640
|
+
.proxy-editor {
|
|
641
|
+
@apply w-full h-full resize-none focus:outline-none;
|
|
642
|
+
background-color: transparent;
|
|
643
|
+
color: #f8f8f2;
|
|
644
|
+
font-family: "JetBrains Mono", "Fira Code", "Menlo", "Monaco", "Courier New", monospace;
|
|
645
|
+
padding: 12px;
|
|
646
|
+
border: none;
|
|
647
|
+
font-size: 14px;
|
|
648
|
+
line-height: 1.6;
|
|
649
|
+
tab-size: 4;
|
|
650
|
+
overflow: auto;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
.proxy-editor:focus {
|
|
654
|
+
outline: none;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
.proxy-editor-container:focus-within {
|
|
658
|
+
border-color: #6272a4;
|
|
659
|
+
box-shadow: 0 0 0 2px rgba(98, 114, 164, 0.25);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/* Prism.js dark theme */
|
|
663
|
+
code[class*="language-"],
|
|
664
|
+
pre[class*="language-"] {
|
|
665
|
+
color: #f8f8f2;
|
|
666
|
+
background: none;
|
|
667
|
+
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
|
|
668
|
+
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
|
|
669
|
+
text-align: left;
|
|
670
|
+
white-space: pre;
|
|
671
|
+
word-spacing: normal;
|
|
672
|
+
word-break: normal;
|
|
673
|
+
word-wrap: normal;
|
|
674
|
+
line-height: 1.5;
|
|
675
|
+
tab-size: 4;
|
|
676
|
+
hyphens: none;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/* Code blocks */
|
|
680
|
+
pre[class*="language-"] {
|
|
681
|
+
padding: 12px;
|
|
682
|
+
margin: 0;
|
|
683
|
+
overflow: auto;
|
|
684
|
+
border-radius: 4px;
|
|
685
|
+
font-family: "Menlo", "Monaco", "Courier New", monospace;
|
|
686
|
+
font-size: 14px;
|
|
687
|
+
line-height: 1.5;
|
|
688
|
+
tab-size: 4;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
:not(pre) > code[class*="language-"],
|
|
692
|
+
pre[class*="language-"] {
|
|
693
|
+
background: #1e1e2e;
|
|
694
|
+
white-space: pre;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/* Inline code */
|
|
698
|
+
:not(pre) > code[class*="language-"] {
|
|
699
|
+
padding: 0.1em;
|
|
700
|
+
border-radius: 0.3em;
|
|
701
|
+
white-space: normal;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
.token.comment,
|
|
705
|
+
.token.prolog,
|
|
706
|
+
.token.doctype,
|
|
707
|
+
.token.cdata {
|
|
708
|
+
color: #676f7d;
|
|
709
|
+
font-style: italic;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
.token.punctuation {
|
|
713
|
+
color: #a9b7c6;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
.namespace {
|
|
717
|
+
opacity: 0.8;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
.token.property,
|
|
721
|
+
.token.tag,
|
|
722
|
+
.token.constant,
|
|
723
|
+
.token.symbol,
|
|
724
|
+
.token.deleted {
|
|
725
|
+
color: #e06c75;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
.token.boolean,
|
|
729
|
+
.token.number {
|
|
730
|
+
color: #c792ea;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
.token.selector,
|
|
734
|
+
.token.attr-name,
|
|
735
|
+
.token.string,
|
|
736
|
+
.token.char,
|
|
737
|
+
.token.builtin,
|
|
738
|
+
.token.inserted {
|
|
739
|
+
color: #98c379;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
.token.operator,
|
|
743
|
+
.token.entity,
|
|
744
|
+
.token.url,
|
|
745
|
+
.language-css .token.string,
|
|
746
|
+
.style .token.string {
|
|
747
|
+
color: #89ddff;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
.token.atrule,
|
|
751
|
+
.token.attr-value,
|
|
752
|
+
.token.keyword {
|
|
753
|
+
color: #61afef;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
.token.function,
|
|
757
|
+
.token.class-name {
|
|
758
|
+
color: #ffcb6b;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
.token.regex,
|
|
762
|
+
.token.important,
|
|
763
|
+
.token.variable {
|
|
764
|
+
color: #c5e478;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/* Add a slight hover effect to the editor for better UX */
|
|
768
|
+
.editor-container:hover {
|
|
769
|
+
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3);
|
|
770
|
+
transition: box-shadow 0.3s ease;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
.token.important,
|
|
774
|
+
.token.bold {
|
|
775
|
+
font-weight: bold;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
.token.italic {
|
|
779
|
+
font-style: italic;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
.token.entity {
|
|
783
|
+
cursor: help;
|
|
784
|
+
}
|
|
785
|
+
</style>
|