Sidebar.svelte 14.4 KB
Newer Older
1
<script lang="ts">
Timothy J. Baek's avatar
Timothy J. Baek committed
2
	import { goto } from '$app/navigation';
Timothy J. Baek's avatar
Timothy J. Baek committed
3
4
5
6
7
8
9
10
	import {
		user,
		chats,
		settings,
		showSettings,
		chatId,
		tags,
		showSidebar,
Timothy J. Baek's avatar
Timothy J. Baek committed
11
12
		mobile,
		showArchivedChats
Timothy J. Baek's avatar
Timothy J. Baek committed
13
	} from '$lib/stores';
14
	import { onMount, getContext } from 'svelte';
15
16
17

	const i18n = getContext('i18n');

Timothy J. Baek's avatar
Timothy J. Baek committed
18
19
20
	import {
		deleteChatById,
		getChatList,
Henry Holloway's avatar
Henry Holloway committed
21
		getChatById,
Timothy J. Baek's avatar
Timothy J. Baek committed
22
		getChatListByTagName,
Timothy J. Baek's avatar
Timothy J. Baek committed
23
		updateChatById,
Timothy J. Baek's avatar
Timothy J. Baek committed
24
		getAllChatTags,
Timothy J. Baek's avatar
Timothy J. Baek committed
25
26
		archiveChatById,
		cloneChatById
Timothy J. Baek's avatar
Timothy J. Baek committed
27
	} from '$lib/apis/chats';
Jannik Streidl's avatar
Jannik Streidl committed
28
	import { toast } from 'svelte-sonner';
Timothy J. Baek's avatar
Timothy J. Baek committed
29
	import { fade, slide } from 'svelte/transition';
30
	import { WEBUI_BASE_URL } from '$lib/constants';
31
	import ArchivedChatsModal from './Sidebar/ArchivedChatsModal.svelte';
Timothy J. Baek's avatar
Timothy J. Baek committed
32
	import UserMenu from './Sidebar/UserMenu.svelte';
33
	import { updateUserSettings } from '$lib/apis/users';
Timothy J. Baek's avatar
Timothy J. Baek committed
34
	import ChatItem from './Sidebar/ChatItem.svelte';
35

Timothy J. Baek's avatar
Timothy J. Baek committed
36
	const BREAKPOINT = 768;
Timothy J. Baek's avatar
Timothy J. Baek committed
37

38
	let navElement;
39
	let search = '';
40

Timothy J. Baek's avatar
Timothy J. Baek committed
41
	let selectedChatId = null;
42
	let showDropdown = false;
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
	let filteredChatList = [];

	$: filteredChatList = $chats.filter((chat) => {
		if (search === '') {
			return true;
		} else {
			let title = chat.title.toLowerCase();
			const query = search.toLowerCase();

			let contentMatches = false;
			// Access the messages within chat.chat.messages
			if (chat.chat && chat.chat.messages && Array.isArray(chat.chat.messages)) {
				contentMatches = chat.chat.messages.some((message) => {
					// Check if message.content exists and includes the search query
					return message.content && message.content.toLowerCase().includes(query);
				});
			}

			return title.includes(query) || contentMatches;
		}
	});

65
	onMount(async () => {
Timothy J. Baek's avatar
Timothy J. Baek committed
66
67
68
69
70
71
72
73
74
75
		mobile.subscribe((e) => {
			if ($showSidebar && e) {
				showSidebar.set(false);
			}

			if (!$showSidebar && !e) {
				showSidebar.set(true);
			}
		});

Timothy J. Baek's avatar
Timothy J. Baek committed
76
		showSidebar.set(window.innerWidth > BREAKPOINT);
Timothy J. Baek's avatar
Timothy J. Baek committed
77
		await chats.set(await getChatList(localStorage.token));
Timothy J. Baek's avatar
Timothy J. Baek committed
78

79
80
		let touchstart;
		let touchend;
Timothy J. Baek's avatar
Timothy J. Baek committed
81
82
83

		function checkDirection() {
			const screenWidth = window.innerWidth;
84
			const swipeDistance = Math.abs(touchend.screenX - touchstart.screenX);
Timothy J. Baek's avatar
Timothy J. Baek committed
85
			if (touchstart.clientX < 40 && swipeDistance >= screenWidth / 8) {
86
				if (touchend.screenX < touchstart.screenX) {
Timothy J. Baek's avatar
Timothy J. Baek committed
87
					showSidebar.set(false);
Timothy J. Baek's avatar
Timothy J. Baek committed
88
				}
89
				if (touchend.screenX > touchstart.screenX) {
Timothy J. Baek's avatar
Timothy J. Baek committed
90
					showSidebar.set(true);
Timothy J. Baek's avatar
Timothy J. Baek committed
91
92
93
94
				}
			}
		}

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
95
		const onTouchStart = (e) => {
96
97
			touchstart = e.changedTouches[0];
			console.log(touchstart.clientX);
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
98
		};
Timothy J. Baek's avatar
Timothy J. Baek committed
99

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
100
		const onTouchEnd = (e) => {
101
			touchend = e.changedTouches[0];
Timothy J. Baek's avatar
Timothy J. Baek committed
102
			checkDirection();
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
103
104
		};

105
106
		window.addEventListener('touchstart', onTouchStart);
		window.addEventListener('touchend', onTouchEnd);
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
107
108

		return () => {
109
110
			window.removeEventListener('touchstart', onTouchStart);
			window.removeEventListener('touchend', onTouchEnd);
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
111
		};
Timothy J. Baek's avatar
Timothy J. Baek committed
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
	});

	// Helper function to fetch and add chat content to each chat
	const enrichChatsWithContent = async (chatList) => {
		const enrichedChats = await Promise.all(
			chatList.map(async (chat) => {
				const chatDetails = await getChatById(localStorage.token, chat.id).catch((error) => null); // Handle error or non-existent chat gracefully
				if (chatDetails) {
					chat.chat = chatDetails.chat; // Assuming chatDetails.chat contains the chat content
				}
				return chat;
			})
		);

		await chats.set(enrichedChats);
	};
128

129
130
	const saveSettings = async (updated) => {
		await settings.set({ ...$settings, ...updated });
131
		await updateUserSettings(localStorage.token, { ui: $settings });
132
133
		location.href = '/';
	};
134
135
</script>

Timothy J. Baek's avatar
Timothy J. Baek committed
136
<ArchivedChatsModal
Timothy J. Baek's avatar
Timothy J. Baek committed
137
	bind:show={$showArchivedChats}
Timothy J. Baek's avatar
Timothy J. Baek committed
138
139
140
141
	on:change={async () => {
		await chats.set(await getChatList(localStorage.token));
	}}
/>
Timothy J. Baek's avatar
Timothy J. Baek committed
142

Timothy J. Baek's avatar
Timothy J. Baek committed
143
144
145
146
<!-- svelte-ignore a11y-no-static-element-interactions -->

{#if $showSidebar}
	<div
Timothy J. Baek's avatar
Timothy J. Baek committed
147
		class=" fixed md:hidden z-40 top-0 right-0 left-0 bottom-0 bg-black/60 w-full min-h-screen h-screen flex justify-center overflow-hidden overscroll-contain"
Timothy J. Baek's avatar
Timothy J. Baek committed
148
149
150
151
152
153
		on:mousedown={() => {
			showSidebar.set(!$showSidebar);
		}}
	/>
{/if}

154
155
<div
	bind:this={navElement}
Timothy J. Baek's avatar
Timothy J. Baek committed
156
	id="sidebar"
Timothy J. Baek's avatar
Timothy J. Baek committed
157
	class="h-screen max-h-[100dvh] min-h-screen select-none {$showSidebar
Timothy J. Baek's avatar
Timothy J. Baek committed
158
		? 'md:relative w-[260px]'
Timothy J. Baek's avatar
Timothy J. Baek committed
159
		: '-translate-x-[260px] w-[0px]'} bg-gray-50 text-gray-900 dark:bg-gray-950 dark:text-gray-200 text-sm transition fixed z-50 top-0 left-0 rounded-r-2xl
160
        "
Timothy J. Baek's avatar
Timothy J. Baek committed
161
	data-state={$showSidebar}
162
>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
163
	<div
Timothy J. Baek's avatar
Timothy J. Baek committed
164
		class="py-2.5 my-auto flex flex-col justify-between h-screen max-h-[100dvh] w-[260px] z-50 {$showSidebar
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
165
166
167
			? ''
			: 'invisible'}"
	>
Timothy J. Baek's avatar
Timothy J. Baek committed
168
		<div class="px-2.5 flex justify-between space-x-1 text-gray-600 dark:text-gray-400">
169
			<a
Timothy J. Baek's avatar
Timothy J. Baek committed
170
				id="sidebar-new-chat-button"
Timothy J. Baek's avatar
Timothy J. Baek committed
171
				class="flex flex-1 justify-between rounded-xl px-2 py-2 hover:bg-gray-100 dark:hover:bg-gray-850 transition"
172
				href="/"
173
				draggable="false"
Timothy J. Baek's avatar
Timothy J. Baek committed
174
				on:click={async () => {
Timothy J. Baek's avatar
Timothy J. Baek committed
175
					selectedChatId = null;
176
					await goto('/');
177
					const newChatButton = document.getElementById('new-chat-button');
178
179
					setTimeout(() => {
						newChatButton?.click();
Timothy J. Baek's avatar
Timothy J. Baek committed
180
181
182
						if ($mobile) {
							showSidebar.set(false);
						}
183
					}, 0);
184
185
				}}
			>
Timothy J. Baek's avatar
Timothy J. Baek committed
186
187
				<div class="self-center mx-1.5">
					<img
Timothy J. Baek's avatar
Timothy J. Baek committed
188
						crossorigin="anonymous"
Timothy J. Baek's avatar
Timothy J. Baek committed
189
190
191
192
193
						src="{WEBUI_BASE_URL}/static/favicon.png"
						class=" size-6 -translate-x-1.5 rounded-full"
						alt="logo"
					/>
				</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
194
195
196
				<div class=" self-center font-medium text-sm text-gray-850 dark:text-white">
					{$i18n.t('New Chat')}
				</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
197
				<div class="self-center ml-auto">
198
199
200
201
					<svg
						xmlns="http://www.w3.org/2000/svg"
						viewBox="0 0 20 20"
						fill="currentColor"
Timothy J. Baek's avatar
Timothy J. Baek committed
202
						class="size-5"
203
204
205
206
207
208
209
210
211
					>
						<path
							d="M5.433 13.917l1.262-3.155A4 4 0 017.58 9.42l6.92-6.918a2.121 2.121 0 013 3l-6.92 6.918c-.383.383-.84.685-1.343.886l-3.154 1.262a.5.5 0 01-.65-.65z"
						/>
						<path
							d="M3.5 5.75c0-.69.56-1.25 1.25-1.25H10A.75.75 0 0010 3H4.75A2.75 2.75 0 002 5.75v9.5A2.75 2.75 0 004.75 18h9.5A2.75 2.75 0 0017 15.25V10a.75.75 0 00-1.5 0v5.25c0 .69-.56 1.25-1.25 1.25h-9.5c-.69 0-1.25-.56-1.25-1.25v-9.5z"
						/>
					</svg>
				</div>
212
			</a>
Timothy J. Baek's avatar
Timothy J. Baek committed
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236

			<button
				class=" cursor-pointer px-2 py-2 flex rounded-xl hover:bg-gray-100 dark:hover:bg-gray-850 transition"
				on:click={() => {
					showSidebar.set(!$showSidebar);
				}}
			>
				<div class=" m-auto self-center">
					<svg
						xmlns="http://www.w3.org/2000/svg"
						fill="none"
						viewBox="0 0 24 24"
						stroke-width="2"
						stroke="currentColor"
						class="size-5"
					>
						<path
							stroke-linecap="round"
							stroke-linejoin="round"
							d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25H12"
						/>
					</svg>
				</div>
			</button>
237
238
		</div>

Timothy J. Baek's avatar
Timothy J. Baek committed
239
		{#if $user?.role === 'admin'}
Timothy J. Baek's avatar
Timothy J. Baek committed
240
			<div class="px-2.5 flex justify-center text-gray-800 dark:text-gray-200">
241
				<a
Timothy J. Baek's avatar
Timothy J. Baek committed
242
					class="flex-grow flex space-x-3 rounded-xl px-2.5 py-2 hover:bg-gray-100 dark:hover:bg-gray-900 transition"
Timothy J. Baek's avatar
Timothy J. Baek committed
243
					href="/workspace"
Timothy J. Baek's avatar
Timothy J. Baek committed
244
245
246
					on:click={() => {
						selectedChatId = null;
						chatId.set('');
Timothy J. Baek's avatar
Timothy J. Baek committed
247
248
249
250

						if ($mobile) {
							showSidebar.set(false);
						}
Timothy J. Baek's avatar
Timothy J. Baek committed
251
					}}
252
					draggable="false"
Timothy J. Baek's avatar
Timothy J. Baek committed
253
254
255
256
257
258
				>
					<div class="self-center">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							fill="none"
							viewBox="0 0 24 24"
Timothy J. Baek's avatar
Timothy J. Baek committed
259
							stroke-width="2"
Timothy J. Baek's avatar
Timothy J. Baek committed
260
							stroke="currentColor"
Timothy J. Baek's avatar
Timothy J. Baek committed
261
							class="size-[1.1rem]"
Timothy J. Baek's avatar
Timothy J. Baek committed
262
263
264
265
						>
							<path
								stroke-linecap="round"
								stroke-linejoin="round"
Timothy J. Baek's avatar
Timothy J. Baek committed
266
								d="M13.5 16.875h3.375m0 0h3.375m-3.375 0V13.5m0 3.375v3.375M6 10.5h2.25a2.25 2.25 0 0 0 2.25-2.25V6a2.25 2.25 0 0 0-2.25-2.25H6A2.25 2.25 0 0 0 3.75 6v2.25A2.25 2.25 0 0 0 6 10.5Zm0 9.75h2.25A2.25 2.25 0 0 0 10.5 18v-2.25a2.25 2.25 0 0 0-2.25-2.25H6a2.25 2.25 0 0 0-2.25 2.25V18A2.25 2.25 0 0 0 6 20.25Zm9.75-9.75H18a2.25 2.25 0 0 0 2.25-2.25V6A2.25 2.25 0 0 0 18 3.75h-2.25A2.25 2.25 0 0 0 13.5 6v2.25a2.25 2.25 0 0 0 2.25 2.25Z"
Timothy J. Baek's avatar
Timothy J. Baek committed
267
268
269
							/>
						</svg>
					</div>
270

Timothy J. Baek's avatar
Timothy J. Baek committed
271
					<div class="flex self-center">
Timothy J. Baek's avatar
Timothy J. Baek committed
272
						<div class=" self-center font-medium text-sm">{$i18n.t('Workspace')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
273
					</div>
274
				</a>
Timothy J. Baek's avatar
Timothy J. Baek committed
275
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
276
		{/if}
277

278
279
		<div class="relative flex flex-col flex-1 overflow-y-auto">
			{#if !($settings.saveChatHistory ?? true)}
Timothy J. Baek's avatar
Timothy J. Baek committed
280
				<div class="absolute z-40 w-full h-full bg-gray-50/90 dark:bg-black/90 flex justify-center">
281
					<div class=" text-left px-5 py-2">
282
						<div class=" font-medium">{$i18n.t('Chat History is off for this browser.')}</div>
283
						<div class="text-xs mt-2">
Jannik Streidl's avatar
Jannik Streidl committed
284
285
286
287
288
							{$i18n.t(
								"When history is turned off, new chats on this browser won't appear in your history on any of your devices."
							)}
							<span class=" font-semibold"
								>{$i18n.t('This setting does not sync across browsers or devices.')}</span
289
290
291
292
293
							>
						</div>

						<div class="mt-3">
							<button
Timothy J. Baek's avatar
Timothy J. Baek committed
294
								class="flex justify-center items-center space-x-1.5 px-3 py-2.5 rounded-lg text-xs bg-gray-100 hover:bg-gray-200 transition text-gray-800 font-medium w-full"
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
								type="button"
								on:click={() => {
									saveSettings({
										saveChatHistory: true
									});
								}}
							>
								<svg
									xmlns="http://www.w3.org/2000/svg"
									viewBox="0 0 16 16"
									fill="currentColor"
									class="w-3 h-3"
								>
									<path
										fill-rule="evenodd"
										d="M8 1a.75.75 0 0 1 .75.75v6.5a.75.75 0 0 1-1.5 0v-6.5A.75.75 0 0 1 8 1ZM4.11 3.05a.75.75 0 0 1 0 1.06 5.5 5.5 0 1 0 7.78 0 .75.75 0 0 1 1.06-1.06 7 7 0 1 1-9.9 0 .75.75 0 0 1 1.06 0Z"
										clip-rule="evenodd"
									/>
								</svg>

Jannik Streidl's avatar
Jannik Streidl committed
315
								<div>{$i18n.t('Enable Chat History')}</div>
316
317
318
							</button>
						</div>
					</div>
319
				</div>
320
			{/if}
321

Timothy J. Baek's avatar
Timothy J. Baek committed
322
323
324
			<div class="px-2 mt-0.5 mb-2 flex justify-center space-x-2">
				<div class="flex w-full rounded-xl" id="chat-search">
					<div class="self-center pl-3 py-2 rounded-l-xl bg-transparent">
325
326
327
328
329
330
331
332
333
334
335
336
337
						<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="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
								clip-rule="evenodd"
							/>
						</svg>
					</div>
338

339
					<input
Timothy J. Baek's avatar
Timothy J. Baek committed
340
						class="w-full rounded-r-xl py-1.5 pl-2.5 pr-4 text-sm bg-transparent dark:text-gray-300 outline-none"
Jannik Streidl's avatar
Jannik Streidl committed
341
						placeholder={$i18n.t('Search')}
342
						bind:value={search}
Timothy J. Baek's avatar
Timothy J. Baek committed
343
344
345
						on:focus={() => {
							enrichChatsWithContent($chats);
						}}
346
347
348
					/>
				</div>
			</div>
349

Timothy J. Baek's avatar
Timothy J. Baek committed
350
			{#if $tags.length > 0}
Timothy J. Baek's avatar
Timothy J. Baek committed
351
				<div class="px-2.5 mb-2 flex gap-1 flex-wrap">
Timothy J. Baek's avatar
Timothy J. Baek committed
352
					<button
Timothy J. Baek's avatar
Timothy J. Baek committed
353
						class="px-2.5 text-xs font-medium bg-gray-50 dark:bg-gray-900 dark:hover:bg-gray-800 transition rounded-full"
Timothy J. Baek's avatar
Timothy J. Baek committed
354
355
356
357
						on:click={async () => {
							await chats.set(await getChatList(localStorage.token));
						}}
					>
Timothy J. Baek's avatar
Timothy J. Baek committed
358
						{$i18n.t('all')}
Timothy J. Baek's avatar
Timothy J. Baek committed
359
360
361
					</button>
					{#each $tags as tag}
						<button
Timothy J. Baek's avatar
Timothy J. Baek committed
362
							class="px-2.5 text-xs font-medium bg-gray-50 dark:bg-gray-900 dark:hover:bg-gray-800 transition rounded-full"
Timothy J. Baek's avatar
Timothy J. Baek committed
363
							on:click={async () => {
Timothy J. Baek's avatar
Timothy J. Baek committed
364
365
366
367
368
369
								let chatIds = await getChatListByTagName(localStorage.token, tag.name);
								if (chatIds.length === 0) {
									await tags.set(await getAllChatTags(localStorage.token));
									chatIds = await getChatList(localStorage.token);
								}
								await chats.set(chatIds);
Timothy J. Baek's avatar
Timothy J. Baek committed
370
371
372
373
374
375
376
377
							}}
						>
							{tag.name}
						</button>
					{/each}
				</div>
			{/if}

Timothy J. Baek's avatar
Timothy J. Baek committed
378
			<div class="pl-2 my-2 flex-1 flex flex-col space-y-1 overflow-y-auto scrollbar-hidden">
379
				{#each filteredChatList as chat, idx}
Timothy J. Baek's avatar
Timothy J. Baek committed
380
					{#if idx === 0 || (idx > 0 && chat.time_range !== filteredChatList[idx - 1].time_range)}
381
382
383
						<div
							class="w-full pl-2.5 text-xs text-gray-500 dark:text-gray-500 font-medium {idx === 0
								? ''
Timothy J. Baek's avatar
Timothy J. Baek committed
384
								: 'pt-5'} pb-0.5"
385
						>
Timothy J. Baek's avatar
Timothy J. Baek committed
386
							{$i18n.t(chat.time_range)}
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
							<!-- localisation keys for time_range to be recognized from the i18next parser (so they don't get automatically removed):
							{$i18n.t('Today')}
							{$i18n.t('Yesterday')}
							{$i18n.t('Previous 7 days')}
							{$i18n.t('Previous 30 days')}
							{$i18n.t('January')}
							{$i18n.t('February')}
							{$i18n.t('March')}
							{$i18n.t('April')}
							{$i18n.t('May')}
							{$i18n.t('June')}
							{$i18n.t('July')}
							{$i18n.t('August')}
							{$i18n.t('September')}
							{$i18n.t('October')}
							{$i18n.t('November')}
							{$i18n.t('December')}
							-->
405
406
407
						</div>
					{/if}

Timothy J. Baek's avatar
Timothy J. Baek committed
408
409
410
411
412
413
414
					<ChatItem
						{chat}
						selected={selectedChatId === chat.id}
						on:select={() => {
							selectedChatId = chat.id;
						}}
					/>
415
416
				{/each}
			</div>
417
418
		</div>

419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
		<div class="px-2.5">
			<!-- <hr class=" border-gray-900 mb-1 w-full" /> -->

			<div class="flex flex-col">
				{#if $user !== undefined}
					<UserMenu
						role={$user.role}
						on:show={(e) => {
							if (e.detail === 'archived-chat') {
								showArchivedChats.set(true);
							}
						}}
					>
						<button
							class=" flex rounded-xl py-3 px-3.5 w-full hover:bg-gray-100 dark:hover:bg-gray-900 transition"
							on:click={() => {
								showDropdown = !showDropdown;
Timothy J. Baek's avatar
Timothy J. Baek committed
436
							}}
437
						>
438
439
440
441
442
443
444
445
446
447
448
							<div class=" self-center mr-3">
								<img
									src={$user.profile_image_url}
									class=" max-w-[30px] object-cover rounded-full"
									alt="User profile"
								/>
							</div>
							<div class=" self-center font-semibold">{$user.name}</div>
						</button>
					</UserMenu>
				{/if}
449
			</div>
450
		</div>
451
452
	</div>

453
	<!-- <div
Timothy J. Baek's avatar
Timothy J. Baek committed
454
		id="sidebar-handle"
Timothy J. Baek's avatar
Timothy J. Baek committed
455
		class=" hidden md:fixed left-0 top-[50dvh] -translate-y-1/2 transition-transform translate-x-[255px] md:translate-x-[260px] rotate-0"
456
	>
Jannik Streidl's avatar
Jannik Streidl committed
457
458
		<Tooltip
			placement="right"
Timothy J. Baek's avatar
Timothy J. Baek committed
459
			content={`${$showSidebar ? $i18n.t('Close') : $i18n.t('Open')} ${$i18n.t('sidebar')}`}
Jannik Streidl's avatar
Jannik Streidl committed
460
461
			touch={false}
		>
462
463
464
465
			<button
				id="sidebar-toggle-button"
				class=" group"
				on:click={() => {
Timothy J. Baek's avatar
Timothy J. Baek committed
466
					showSidebar.set(!$showSidebar);
467
468
469
				}}
				><span class="" data-state="closed"
					><div
470
						class="flex h-[72px] w-8 items-center justify-center opacity-50 group-hover:opacity-100 transition"
471
472
473
					>
						<div class="flex h-6 w-6 flex-col items-center">
							<div
Timothy J. Baek's avatar
Timothy J. Baek committed
474
								class="h-3 w-1 rounded-full bg-[#0f0f0f] dark:bg-white rotate-0 translate-y-[0.15rem] {$showSidebar
475
476
477
478
									? 'group-hover:rotate-[15deg]'
									: 'group-hover:rotate-[-15deg]'}"
							/>
							<div
Timothy J. Baek's avatar
Timothy J. Baek committed
479
								class="h-3 w-1 rounded-full bg-[#0f0f0f] dark:bg-white rotate-0 translate-y-[-0.15rem] {$showSidebar
480
481
482
483
									? 'group-hover:rotate-[-15deg]'
									: 'group-hover:rotate-[15deg]'}"
							/>
						</div>
484
					</div>
485
486
487
				</span>
			</button>
		</Tooltip>
488
	</div> -->
489
</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
490
491

<style>
Timothy J. Baek's avatar
Timothy J. Baek committed
492
493
494
	.scrollbar-hidden:active::-webkit-scrollbar-thumb,
	.scrollbar-hidden:focus::-webkit-scrollbar-thumb,
	.scrollbar-hidden:hover::-webkit-scrollbar-thumb {
Timothy J. Baek's avatar
Timothy J. Baek committed
495
		visibility: visible;
Timothy J. Baek's avatar
Timothy J. Baek committed
496
	}
Timothy J. Baek's avatar
Timothy J. Baek committed
497
	.scrollbar-hidden::-webkit-scrollbar-thumb {
Timothy J. Baek's avatar
Timothy J. Baek committed
498
		visibility: hidden;
Timothy J. Baek's avatar
Timothy J. Baek committed
499
500
	}
</style>