SettingsModal.svelte 13.6 KB
Newer Older
Timothy J. Baek's avatar
Timothy J. Baek committed
1
<script lang="ts">
2
	import { getContext, afterUpdate } from 'svelte';
Jannik Streidl's avatar
Jannik Streidl committed
3
	import { toast } from 'svelte-sonner';
Timothy J. Baek's avatar
Timothy J. Baek committed
4
	import { models, settings, user } from '$lib/stores';
5

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
6
	import { getModels as _getModels } from '$lib/apis';
Timothy J. Baek's avatar
Timothy J. Baek committed
7
8

	import Modal from '../common/Modal.svelte';
9
	import Account from './Settings/Account.svelte';
Timothy J. Baek's avatar
Timothy J. Baek committed
10
11
12
	import About from './Settings/About.svelte';
	import General from './Settings/General.svelte';
	import Interface from './Settings/Interface.svelte';
Timothy J. Baek's avatar
Timothy J. Baek committed
13
	import Audio from './Settings/Audio.svelte';
Timothy J. Baek's avatar
Timothy J. Baek committed
14
	import Chats from './Settings/Chats.svelte';
15
16
	import User from '../icons/User.svelte';
	import Personalization from './Settings/Personalization.svelte';
17
	import { updateUserSettings } from '$lib/apis/users';
Timothy J. Baek's avatar
Timothy J. Baek committed
18
	import { goto } from '$app/navigation';
Timothy J. Baek's avatar
Timothy J. Baek committed
19
	import Valves from './Settings/Valves.svelte';
20

21
22
	const i18n = getContext('i18n');

Timothy J. Baek's avatar
Timothy J. Baek committed
23
	export let show = false;
24
25
26
27

	const saveSettings = async (updated) => {
		console.log(updated);
		await settings.set({ ...$settings, ...updated });
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
28
		await models.set(await getModels());
29
		await updateUserSettings(localStorage.token, { ui: $settings });
30
	};
31

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
32
33
	const getModels = async () => {
		return await _getModels(localStorage.token);
34
35
	};

36
	let selectedTab = 'general';
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

	// Function to handle sideways scrolling
	const handleSidewaysScroll = (event) => {
		const settingsTabsContainer = document.getElementById('settings-tabs-container');
		if (settingsTabsContainer) {
		event.preventDefault(); // Prevent default vertical scrolling
		settingsTabsContainer.scrollLeft += event.deltaY; // Scroll sideways
		}
	};

	// Use afterUpdate to add the event listener after the modal is confirmed to be visible
	// and remove it when the modal is closed
	afterUpdate(() => {
		if (show) {
			const settingsTabsContainer = document.getElementById('settings-tabs-container');
			if (settingsTabsContainer) {
				settingsTabsContainer.addEventListener('wheel', handleSidewaysScroll);
			}
		} else {
			const settingsTabsContainer = document.getElementById('settings-tabs-container');
			if (settingsTabsContainer) {
				settingsTabsContainer.removeEventListener('wheel', handleSidewaysScroll);
			}
		}
	});

Timothy J. Baek's avatar
Timothy J. Baek committed
63
64
65
</script>

<Modal bind:show>
Timothy J. Baek's avatar
Timothy J. Baek committed
66
	<div class="text-gray-700 dark:text-gray-100">
Timothy J. Baek's avatar
Timothy J. Baek committed
67
		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-1">
68
			<div class=" text-lg font-medium self-center">{$i18n.t('Settings')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
			<button
				class="self-center"
				on:click={() => {
					show = false;
				}}
			>
				<svg
					xmlns="http://www.w3.org/2000/svg"
					viewBox="0 0 20 20"
					fill="currentColor"
					class="w-5 h-5"
				>
					<path
						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
					/>
				</svg>
			</button>
		</div>

Timothy J. Baek's avatar
Timothy J. Baek committed
88
89
		<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
			<div
90
				id="settings-tabs-container"
Timothy J. Baek's avatar
Timothy J. Baek committed
91
				class="tabs flex flex-row overflow-x-auto space-x-1 md:space-x-0 md:space-y-1 md:flex-col flex-1 md:flex-none md:w-40 dark:text-gray-200 text-xs text-left mb-3 md:mb-0"
Timothy J. Baek's avatar
Timothy J. Baek committed
92
93
			>
				<button
94
					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
Timothy J. Baek's avatar
Timothy J. Baek committed
95
					'general'
Timothy J. Baek's avatar
Timothy J. Baek committed
96
97
						? 'bg-gray-200 dark:bg-gray-800'
						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
Timothy J. Baek's avatar
Timothy J. Baek committed
98
					on:click={() => {
99
						selectedTab = 'general';
Timothy J. Baek's avatar
Timothy J. Baek committed
100
					}}
Timothy J. Baek's avatar
Timothy J. Baek committed
101
				>
Timothy J. Baek's avatar
Timothy J. Baek committed
102
103
104
105
106
107
108
109
110
111
112
113
114
115
					<div class=" self-center mr-2">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							viewBox="0 0 20 20"
							fill="currentColor"
							class="w-4 h-4"
						>
							<path
								fill-rule="evenodd"
								d="M8.34 1.804A1 1 0 019.32 1h1.36a1 1 0 01.98.804l.295 1.473c.497.144.971.342 1.416.587l1.25-.834a1 1 0 011.262.125l.962.962a1 1 0 01.125 1.262l-.834 1.25c.245.445.443.919.587 1.416l1.473.294a1 1 0 01.804.98v1.361a1 1 0 01-.804.98l-1.473.295a6.95 6.95 0 01-.587 1.416l.834 1.25a1 1 0 01-.125 1.262l-.962.962a1 1 0 01-1.262.125l-1.25-.834a6.953 6.953 0 01-1.416.587l-.294 1.473a1 1 0 01-.98.804H9.32a1 1 0 01-.98-.804l-.295-1.473a6.957 6.957 0 01-1.416-.587l-1.25.834a1 1 0 01-1.262-.125l-.962-.962a1 1 0 01-.125-1.262l.834-1.25a6.957 6.957 0 01-.587-1.416l-1.473-.294A1 1 0 011 10.68V9.32a1 1 0 01.804-.98l1.473-.295c.144-.497.342-.971.587-1.416l-.834-1.25a1 1 0 01.125-1.262l.962-.962A1 1 0 015.38 3.03l1.25.834a6.957 6.957 0 011.416-.587l.294-1.473zM13 10a3 3 0 11-6 0 3 3 0 016 0z"
								clip-rule="evenodd"
							/>
						</svg>
					</div>
116
					<div class=" self-center">{$i18n.t('General')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
117
118
				</button>

119
				<button
120
					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
Timothy J. Baek's avatar
Timothy J. Baek committed
121
					'interface'
Timothy J. Baek's avatar
Timothy J. Baek committed
122
123
						? 'bg-gray-200 dark:bg-gray-800'
						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
124
					on:click={() => {
Timothy J. Baek's avatar
Timothy J. Baek committed
125
						selectedTab = 'interface';
126
127
128
129
130
					}}
				>
					<div class=" self-center mr-2">
						<svg
							xmlns="http://www.w3.org/2000/svg"
Timothy J. Baek's avatar
Timothy J. Baek committed
131
							viewBox="0 0 16 16"
132
133
134
135
							fill="currentColor"
							class="w-4 h-4"
						>
							<path
Timothy J. Baek's avatar
Timothy J. Baek committed
136
								fill-rule="evenodd"
Timothy J. Baek's avatar
Timothy J. Baek committed
137
								d="M2 4.25A2.25 2.25 0 0 1 4.25 2h7.5A2.25 2.25 0 0 1 14 4.25v5.5A2.25 2.25 0 0 1 11.75 12h-1.312c.1.128.21.248.328.36a.75.75 0 0 1 .234.545v.345a.75.75 0 0 1-.75.75h-4.5a.75.75 0 0 1-.75-.75v-.345a.75.75 0 0 1 .234-.545c.118-.111.228-.232.328-.36H4.25A2.25 2.25 0 0 1 2 9.75v-5.5Zm2.25-.75a.75.75 0 0 0-.75.75v4.5c0 .414.336.75.75.75h7.5a.75.75 0 0 0 .75-.75v-4.5a.75.75 0 0 0-.75-.75h-7.5Z"
Timothy J. Baek's avatar
Timothy J. Baek committed
138
139
140
141
								clip-rule="evenodd"
							/>
						</svg>
					</div>
142
					<div class=" self-center">{$i18n.t('Interface')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
143
144
				</button>

Timothy J. Baek's avatar
Timothy J. Baek committed
145
146
				<button
					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
147
					'personalization'
Timothy J. Baek's avatar
Timothy J. Baek committed
148
149
						? 'bg-gray-200 dark:bg-gray-800'
						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
Timothy J. Baek's avatar
Timothy J. Baek committed
150
					on:click={() => {
151
						selectedTab = 'personalization';
Timothy J. Baek's avatar
Timothy J. Baek committed
152
153
154
					}}
				>
					<div class=" self-center mr-2">
155
						<User />
Timothy J. Baek's avatar
Timothy J. Baek committed
156
					</div>
157
					<div class=" self-center">{$i18n.t('Personalization')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
158
159
				</button>

Timothy J. Baek's avatar
Timothy J. Baek committed
160
161
				<button
					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
Timothy J. Baek's avatar
Timothy J. Baek committed
162
					'audio'
Timothy J. Baek's avatar
Timothy J. Baek committed
163
164
						? 'bg-gray-200 dark:bg-gray-800'
						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
Timothy J. Baek's avatar
Timothy J. Baek committed
165
					on:click={() => {
Timothy J. Baek's avatar
Timothy J. Baek committed
166
						selectedTab = 'audio';
Timothy J. Baek's avatar
Timothy J. Baek committed
167
168
169
170
171
172
173
174
175
176
177
178
179
180
					}}
				>
					<div class=" self-center mr-2">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							viewBox="0 0 16 16"
							fill="currentColor"
							class="w-4 h-4"
						>
							<path
								d="M7.557 2.066A.75.75 0 0 1 8 2.75v10.5a.75.75 0 0 1-1.248.56L3.59 11H2a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h1.59l3.162-2.81a.75.75 0 0 1 .805-.124ZM12.95 3.05a.75.75 0 1 0-1.06 1.06 5.5 5.5 0 0 1 0 7.78.75.75 0 1 0 1.06 1.06 7 7 0 0 0 0-9.9Z"
							/>
							<path
								d="M10.828 5.172a.75.75 0 1 0-1.06 1.06 2.5 2.5 0 0 1 0 3.536.75.75 0 1 0 1.06 1.06 4 4 0 0 0 0-5.656Z"
181
182
183
							/>
						</svg>
					</div>
184
					<div class=" self-center">{$i18n.t('Audio')}</div>
185
				</button>
Timothy J. Baek's avatar
Timothy J. Baek committed
186

Timothy J. Baek's avatar
Timothy J. Baek committed
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
				<button
					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
					'valves'
						? 'bg-gray-200 dark:bg-gray-800'
						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
					on:click={() => {
						selectedTab = 'valves';
					}}
				>
					<div class=" self-center mr-2">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							viewBox="0 0 24 24"
							fill="currentColor"
							class="size-4"
						>
							<path
								d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
							/>
						</svg>
					</div>
					<div class=" self-center">{$i18n.t('Valves')}</div>
				</button>

211
212
213
				<button
					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
					'chats'
Timothy J. Baek's avatar
Timothy J. Baek committed
214
215
						? 'bg-gray-200 dark:bg-gray-800'
						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
					on:click={() => {
						selectedTab = 'chats';
					}}
				>
					<div class=" self-center mr-2">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							viewBox="0 0 16 16"
							fill="currentColor"
							class="w-4 h-4"
						>
							<path
								fill-rule="evenodd"
								d="M8 2C4.262 2 1 4.57 1 8c0 1.86.98 3.486 2.455 4.566a3.472 3.472 0 0 1-.469 1.26.75.75 0 0 0 .713 1.14 6.961 6.961 0 0 0 3.06-1.06c.403.062.818.094 1.241.094 3.738 0 7-2.57 7-6s-3.262-6-7-6ZM5 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Zm7-1a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM8 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
								clip-rule="evenodd"
							/>
						</svg>
					</div>
234
					<div class=" self-center">{$i18n.t('Chats')}</div>
235
236
				</button>

Timothy J. Baek's avatar
Timothy J. Baek committed
237
238
239
				<button
					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
					'account'
Timothy J. Baek's avatar
Timothy J. Baek committed
240
241
						? 'bg-gray-200 dark:bg-gray-800'
						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
Timothy J. Baek's avatar
Timothy J. Baek committed
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
					on:click={() => {
						selectedTab = 'account';
					}}
				>
					<div class=" self-center mr-2">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							viewBox="0 0 16 16"
							fill="currentColor"
							class="w-4 h-4"
						>
							<path
								fill-rule="evenodd"
								d="M15 8A7 7 0 1 1 1 8a7 7 0 0 1 14 0Zm-5-2a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM8 9c-1.825 0-3.422.977-4.295 2.437A5.49 5.49 0 0 0 8 13.5a5.49 5.49 0 0 0 4.294-2.063A4.997 4.997 0 0 0 8 9Z"
								clip-rule="evenodd"
							/>
						</svg>
					</div>
260
					<div class=" self-center">{$i18n.t('Account')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
261
262
				</button>

Timothy J. Baek's avatar
Timothy J. Baek committed
263
264
265
266
				{#if $user.role === 'admin'}
					<button
						class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
						'admin'
Timothy J. Baek's avatar
Timothy J. Baek committed
267
268
							? 'bg-gray-200 dark:bg-gray-800'
							: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
Timothy J. Baek's avatar
Timothy J. Baek committed
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
						on:click={async () => {
							await goto('/admin/settings');
							show = false;
						}}
					>
						<div class=" self-center mr-2">
							<svg
								xmlns="http://www.w3.org/2000/svg"
								viewBox="0 0 24 24"
								fill="currentColor"
								class="size-4"
							>
								<path
									fill-rule="evenodd"
									d="M4.5 3.75a3 3 0 0 0-3 3v10.5a3 3 0 0 0 3 3h15a3 3 0 0 0 3-3V6.75a3 3 0 0 0-3-3h-15Zm4.125 3a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Zm-3.873 8.703a4.126 4.126 0 0 1 7.746 0 .75.75 0 0 1-.351.92 7.47 7.47 0 0 1-3.522.877 7.47 7.47 0 0 1-3.522-.877.75.75 0 0 1-.351-.92ZM15 8.25a.75.75 0 0 0 0 1.5h3.75a.75.75 0 0 0 0-1.5H15ZM14.25 12a.75.75 0 0 1 .75-.75h3.75a.75.75 0 0 1 0 1.5H15a.75.75 0 0 1-.75-.75Zm.75 2.25a.75.75 0 0 0 0 1.5h3.75a.75.75 0 0 0 0-1.5H15Z"
									clip-rule="evenodd"
								/>
							</svg>
						</div>
						<div class=" self-center">{$i18n.t('Admin Settings')}</div>
					</button>
				{/if}

Timothy J. Baek's avatar
Timothy J. Baek committed
292
293
294
				<button
					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
					'about'
Timothy J. Baek's avatar
Timothy J. Baek committed
295
296
						? 'bg-gray-200 dark:bg-gray-800'
						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
Timothy J. Baek's avatar
Timothy J. Baek committed
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
					on:click={() => {
						selectedTab = 'about';
					}}
				>
					<div class=" self-center mr-2">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							viewBox="0 0 20 20"
							fill="currentColor"
							class="w-4 h-4"
						>
							<path
								fill-rule="evenodd"
								d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z"
								clip-rule="evenodd"
							/>
						</svg>
					</div>
315
					<div class=" self-center">{$i18n.t('About')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
316
				</button>
Timothy J. Baek's avatar
Timothy J. Baek committed
317
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
318
			<div class="flex-1 md:min-h-[28rem]">
319
				{#if selectedTab === 'general'}
Timothy J. Baek's avatar
Timothy J. Baek committed
320
					<General
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
321
						{getModels}
Timothy J. Baek's avatar
Timothy J. Baek committed
322
323
						{saveSettings}
						on:save={() => {
324
							toast.success($i18n.t('Settings saved successfully!'));
Timothy J. Baek's avatar
Timothy J. Baek committed
325
326
						}}
					/>
327
				{:else if selectedTab === 'interface'}
Timothy J. Baek's avatar
Timothy J. Baek committed
328
					<Interface
Timothy J. Baek's avatar
Timothy J. Baek committed
329
						{saveSettings}
Timothy J. Baek's avatar
Timothy J. Baek committed
330
						on:save={() => {
331
							toast.success($i18n.t('Settings saved successfully!'));
332
						}}
Timothy J. Baek's avatar
Timothy J. Baek committed
333
					/>
334
335
336
337
338
339
340
				{:else if selectedTab === 'personalization'}
					<Personalization
						{saveSettings}
						on:save={() => {
							toast.success($i18n.t('Settings saved successfully!'));
						}}
					/>
Timothy J. Baek's avatar
Timothy J. Baek committed
341
342
				{:else if selectedTab === 'audio'}
					<Audio
Timothy J. Baek's avatar
Timothy J. Baek committed
343
344
						{saveSettings}
						on:save={() => {
345
							toast.success($i18n.t('Settings saved successfully!'));
Timothy J. Baek's avatar
Timothy J. Baek committed
346
						}}
Timothy J. Baek's avatar
Timothy J. Baek committed
347
					/>
Timothy J. Baek's avatar
Timothy J. Baek committed
348
349
350
351
352
353
354
				{:else if selectedTab === 'valves'}
					<Valves
						{saveSettings}
						on:save={() => {
							toast.success($i18n.t('Settings saved successfully!'));
						}}
					/>
355
				{:else if selectedTab === 'chats'}
Timothy J. Baek's avatar
Timothy J. Baek committed
356
					<Chats {saveSettings} />
357
				{:else if selectedTab === 'account'}
358
359
					<Account
						saveHandler={() => {
360
							toast.success($i18n.t('Settings saved successfully!'));
361
						}}
362
					/>
Timothy J. Baek's avatar
Timothy J. Baek committed
363
				{:else if selectedTab === 'about'}
Timothy J. Baek's avatar
Timothy J. Baek committed
364
					<About />
Timothy J. Baek's avatar
Timothy J. Baek committed
365
366
				{/if}
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
367
		</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
368
369
	</div>
</Modal>
370
371
372
373
374
375
376
377
378

<style>
	input::-webkit-outer-spin-button,
	input::-webkit-inner-spin-button {
		/* display: none; <- Crashes Chrome on hover */
		-webkit-appearance: none;
		margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
	}

379
380
381
382
383
384
385
386
387
	.tabs::-webkit-scrollbar {
		display: none; /* for Chrome, Safari and Opera */
	}

	.tabs {
		-ms-overflow-style: none; /* IE and Edge */
		scrollbar-width: none; /* Firefox */
	}

388
389
390
391
	input[type='number'] {
		-moz-appearance: textfield; /* Firefox */
	}
</style>