Browse Source

feat: light mode support

Timothy J. Baek 1 year ago
parent
commit
f0e1ec75d8

+ 12 - 0
src/app.html

@@ -4,6 +4,18 @@
 		<meta charset="utf-8" />
 		<link rel="icon" href="%sveltekit.assets%/favicon.png" />
 		<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
+		<script>
+			// On page load or when changing themes, best to add inline in `head` to avoid FOUC
+			if (
+				localStorage.theme === 'light' ||
+				(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: light)').matches)
+			) {
+				document.documentElement.classList.add('light');
+			} else {
+				document.documentElement.classList.add('dark');
+			}
+		</script>
+
 		%sveltekit.head%
 	</head>
 	<body data-sveltekit-preload-data="hover">

+ 101 - 43
src/lib/components/chat/SettingsModal.svelte

@@ -14,6 +14,7 @@
 	// General
 	let API_BASE_URL = BUILD_TIME_API_BASE_URL;
 	let system = '';
+	let theme = 'dark';
 
 	// Models
 	let modelTag = '';
@@ -74,6 +75,19 @@
 		}
 	};
 
+	const toggleTheme = async () => {
+		if (theme === 'dark') {
+			theme = 'light';
+		} else {
+			theme = 'dark';
+		}
+
+		localStorage.theme = theme;
+
+		document.documentElement.classList.remove(theme === 'dark' ? 'light' : 'dark');
+		document.documentElement.classList.add(theme);
+	};
+
 	const pullModelHandler = async () => {
 		const res = await fetch(`${API_BASE_URL}/pull`, {
 			method: 'POST',
@@ -182,6 +196,7 @@
 		let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
 		console.log(settings);
 
+		theme = localStorage.theme ?? 'dark';
 		API_BASE_URL = settings.API_BASE_URL ?? BUILD_TIME_API_BASE_URL;
 		system = settings.system ?? '';
 
@@ -197,8 +212,8 @@
 </script>
 
 <Modal bind:show>
-	<div class="rounded-lg bg-gray-900">
-		<div class=" flex justify-between text-gray-300 px-5 py-4">
+	<div class="rounded-lg dark:bg-gray-900">
+		<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
 			<div class=" text-lg font-medium self-center">Settings</div>
 			<button
 				class="self-center"
@@ -218,17 +233,17 @@
 				</svg>
 			</button>
 		</div>
-		<hr class=" border-gray-800" />
+		<hr class=" dark:border-gray-800" />
 
 		<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
 			<div
-				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 text-gray-200 text-xs text-left mb-3 md:mb-0"
+				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"
 			>
 				<button
 					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
 					'general'
-						? 'bg-gray-700'
-						: 'hover:bg-gray-800'}"
+						? 'bg-gray-200 dark:bg-gray-700'
+						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
 					on:click={() => {
 						selectedTab = 'general';
 					}}
@@ -253,8 +268,8 @@
 				<button
 					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
 					'advanced'
-						? 'bg-gray-700'
-						: 'hover:bg-gray-800'}"
+						? 'bg-gray-200 dark:bg-gray-700'
+						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
 					on:click={() => {
 						selectedTab = 'advanced';
 					}}
@@ -277,8 +292,8 @@
 				<button
 					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
 					'models'
-						? 'bg-gray-700'
-						: 'hover:bg-gray-800'}"
+						? 'bg-gray-200 dark:bg-gray-700'
+						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
 					on:click={() => {
 						selectedTab = 'models';
 					}}
@@ -303,8 +318,8 @@
 				<button
 					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
 					'addons'
-						? 'bg-gray-700'
-						: 'hover:bg-gray-800'}"
+						? 'bg-gray-200 dark:bg-gray-700'
+						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
 					on:click={() => {
 						selectedTab = 'addons';
 					}}
@@ -327,18 +342,61 @@
 			<div class="flex-1 md:min-h-[300px]">
 				{#if selectedTab === 'general'}
 					<div class="flex flex-col space-y-3">
+						<div>
+							<div class=" py-1 flex w-full justify-between">
+								<div class=" self-center text-sm font-medium">Theme</div>
+
+								<button
+									class="p-1 px-3 text-xs flex rounded transition"
+									on:click={() => {
+										toggleTheme();
+									}}
+								>
+									{#if theme === 'dark'}
+										<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="M7.455 2.004a.75.75 0 01.26.77 7 7 0 009.958 7.967.75.75 0 011.067.853A8.5 8.5 0 116.647 1.921a.75.75 0 01.808.083z"
+												clip-rule="evenodd"
+											/>
+										</svg>
+
+										<span class="ml-2 self-center"> Dark </span>
+									{:else}
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											viewBox="0 0 20 20"
+											fill="currentColor"
+											class="w-4 h-4 self-center"
+										>
+											<path
+												d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z"
+											/>
+										</svg>
+										<span class="ml-2 self-center"> Light </span>
+									{/if}
+								</button>
+							</div>
+						</div>
+
+						<hr class=" dark:border-gray-700" />
 						<div>
 							<div class=" mb-2.5 text-sm font-medium">Ollama Server URL</div>
 							<div class="flex w-full">
 								<div class="flex-1 mr-2">
 									<input
-										class="w-full rounded py-2 px-4 text-sm text-gray-300 bg-gray-800 outline-none"
+										class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
 										placeholder="Enter URL (e.g. http://localhost:11434/api)"
 										bind:value={API_BASE_URL}
 									/>
 								</div>
 								<button
-									class="px-3 bg-gray-600 hover:bg-gray-700 rounded transition"
+									class="px-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-600 dark:hover:bg-gray-700 rounded transition"
 									on:click={() => {
 										checkOllamaConnection();
 									}}
@@ -358,9 +416,9 @@
 								</button>
 							</div>
 
-							<div class="mt-2 text-xs text-gray-500">
+							<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
 								Trouble accessing Ollama? <a
-									class=" text-gray-300 font-medium"
+									class=" text-gray-500 dark:text-gray-300 font-medium"
 									href="https://github.com/ollama-webui/ollama-webui#troubleshooting"
 									target="_blank"
 								>
@@ -369,20 +427,20 @@
 							</div>
 						</div>
 
-						<hr class=" border-gray-700" />
+						<hr class=" dark:border-gray-700" />
 
 						<div>
 							<div class=" mb-2.5 text-sm font-medium">System Prompt</div>
 							<textarea
 								bind:value={system}
-								class="w-full rounded p-4 text-sm text-gray-300 bg-gray-800 outline-none resize-none"
+								class="w-full rounded p-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none resize-none"
 								rows="4"
 							/>
 						</div>
 
 						<div class="flex justify-end pt-3 text-sm font-medium">
 							<button
-								class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 transition rounded"
+								class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
 								on:click={() => {
 									saveSettings({
 										API_BASE_URL: API_BASE_URL === '' ? BUILD_TIME_API_BASE_URL : API_BASE_URL,
@@ -402,13 +460,13 @@
 							<div class="flex w-full">
 								<div class="flex-1 mr-2">
 									<input
-										class="w-full rounded py-2 px-4 text-sm text-gray-300 bg-gray-800 outline-none"
+										class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
 										placeholder="Enter model tag (e.g. mistral:7b)"
 										bind:value={modelTag}
 									/>
 								</div>
 								<button
-									class="px-3 bg-emerald-600 hover:bg-emerald-700 rounded transition"
+									class="px-3 text-gray-100 bg-emerald-600 hover:bg-emerald-700 rounded transition"
 									on:click={() => {
 										pullModelHandler();
 									}}
@@ -429,9 +487,9 @@
 								</button>
 							</div>
 
-							<div class="mt-2 text-xs text-gray-500">
+							<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
 								To access the available model names for downloading, <a
-									class=" text-gray-300 font-medium"
+									class=" text-gray-500 dark:text-gray-300 font-medium"
 									href="https://ollama.ai/library"
 									target="_blank">click here.</a
 								>
@@ -440,34 +498,34 @@
 							{#if pullProgress !== null}
 								<div class="mt-2">
 									<div class=" mb-2 text-xs">Pull Progress</div>
-									<div class="w-full rounded-full bg-gray-800">
+									<div class="w-full rounded-full dark:bg-gray-800">
 										<div
-											class="bg-gray-600 text-xs font-medium text-blue-100 text-center p-0.5 leading-none rounded-full"
+											class="dark:bg-gray-600 text-xs font-medium text-blue-100 text-center p-0.5 leading-none rounded-full"
 											style="width: {Math.max(15, pullProgress ?? 0)}%"
 										>
 											{pullProgress ?? 0}%
 										</div>
 									</div>
-									<div class="mt-1 text-xs text-gray-700" style="font-size: 0.5rem;">
+									<div class="mt-1 text-xs dark:text-gray-700" style="font-size: 0.5rem;">
 										{digest}
 									</div>
 								</div>
 							{/if}
 						</div>
-						<hr class=" border-gray-700" />
+						<hr class=" dark:border-gray-700" />
 
 						<div>
 							<div class=" mb-2.5 text-sm font-medium">Delete a model</div>
 							<div class="flex w-full">
 								<div class="flex-1 mr-2">
 									<input
-										class="w-full rounded py-2 px-4 text-sm text-gray-300 bg-gray-800 outline-none"
+										class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
 										placeholder="Enter model tag (e.g. mistral:7b)"
 										bind:value={deleteModelTag}
 									/>
 								</div>
 								<button
-									class="px-3 bg-red-700 hover:bg-red-800 rounded transition"
+									class="px-3 bg-red-700 hover:bg-red-800 text-gray-100 rounded transition"
 									on:click={() => {
 										deleteModelHandler();
 									}}
@@ -496,7 +554,7 @@
 								<div class="flex w-full">
 									<div class="flex-1">
 										<input
-											class="w-full rounded py-2 px-4 text-sm text-gray-300 bg-gray-800 outline-none"
+											class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
 											type="number"
 											placeholder="Enter Seed"
 											bind:value={seed}
@@ -507,7 +565,7 @@
 								</div>
 							</div>
 
-							<hr class=" border-gray-700" />
+							<hr class=" dark:border-gray-700" />
 
 							<div>
 								<label for="steps-range" class=" mb-2 text-sm font-medium flex justify-between">
@@ -523,7 +581,7 @@
 									max="1"
 									bind:value={temperature}
 									step="0.05"
-									class="w-full h-2 rounded-lg appearance-none cursor-pointer bg-gray-700"
+									class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
 								/>
 							</div>
 
@@ -541,7 +599,7 @@
 									max="2"
 									bind:value={repeat_penalty}
 									step="0.05"
-									class="w-full h-2 rounded-lg appearance-none cursor-pointer bg-gray-700"
+									class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
 								/>
 							</div>
 
@@ -559,7 +617,7 @@
 									max="100"
 									bind:value={top_k}
 									step="0.5"
-									class="w-full h-2 rounded-lg appearance-none cursor-pointer bg-gray-700"
+									class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
 								/>
 							</div>
 
@@ -577,14 +635,14 @@
 									max="1"
 									bind:value={top_p}
 									step="0.05"
-									class="w-full h-2 rounded-lg appearance-none cursor-pointer bg-gray-700"
+									class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
 								/>
 							</div>
 						</div>
 
 						<div class="flex justify-end pt-3 text-sm font-medium">
 							<button
-								class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 transition rounded"
+								class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
 								on:click={() => {
 									saveSettings({
 										seed: (seed !== 0 ? seed : undefined) ?? undefined,
@@ -620,7 +678,7 @@
 								<div class="flex w-full">
 									<div class="flex-1">
 										<input
-											class="w-full rounded py-2 px-4 text-sm text-gray-300 bg-gray-800 outline-none"
+											class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
 											placeholder="Enter Your Email"
 											bind:value={gravatarEmail}
 											autocomplete="off"
@@ -628,16 +686,16 @@
 										/>
 									</div>
 								</div>
-								<div class="mt-2 text-xs text-gray-500">
+								<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
 									Changes user profile image to match your <a
-										class=" text-gray-300 font-medium"
+										class=" text-gray-500 dark:text-gray-300 font-medium"
 										href="https://gravatar.com/"
 										target="_blank">Gravatar.</a
 									>
 								</div>
 							</div>
 
-							<hr class=" border-gray-700" />
+							<hr class=" dark:border-gray-700" />
 							<div>
 								<div class=" mb-2.5 text-sm font-medium">
 									OpenAI API Key <span class=" text-gray-400 text-sm">(optional)</span>
@@ -645,14 +703,14 @@
 								<div class="flex w-full">
 									<div class="flex-1">
 										<input
-											class="w-full rounded py-2 px-4 text-sm text-gray-300 bg-gray-800 outline-none"
+											class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
 											placeholder="Enter OpenAI API Key"
 											bind:value={OPENAI_API_KEY}
 											autocomplete="off"
 										/>
 									</div>
 								</div>
-								<div class="mt-2 text-xs text-gray-500">
+								<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
 									Adds optional support for 'gpt-*' models available.
 								</div>
 							</div>
@@ -660,7 +718,7 @@
 
 						<div class="flex justify-end pt-3 text-sm font-medium">
 							<button
-								class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 transition rounded"
+								class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
 								type="submit"
 							>
 								Save

+ 20 - 12
src/lib/components/chat/Suggestions.svelte

@@ -4,17 +4,19 @@
 
 <div class=" grid sm:grid-cols-2 gap-2.5 mb-4 md:p-2 text-left">
 	<button
-		class=" flex justify-between w-full px-4 py-2.5 bg-gray-800 hover:bg-gray-700 outline outline-1 outline-gray-600 rounded-lg transition group"
+		class=" flex justify-between w-full px-4 py-2.5 bg-white dark:bg-gray-800 dark:hover:bg-gray-700 outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg transition group"
 		on:click={() => {
 			submitPrompt(`Tell me a random fun fact about the Roman Empire`);
 		}}
 	>
 		<div class="flex flex-col text-left">
-			<div class="text-sm font-medium text-gray-300">Tell me a fun fact</div>
+			<div class="text-sm font-medium dark:text-gray-300">Tell me a fun fact</div>
 			<div class="text-sm text-gray-500">about the Roman Empire</div>
 		</div>
 
-		<div class="self-center group-hover:text-gray-300 text-gray-800 transition">
+		<div
+			class="self-center text-white group-hover:text-gray-800 dark:group-hover:text-gray-300 dark:text-gray-800 transition"
+		>
 			<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" class="w-4 h-4"
 				><path
 					d="M.5 1.163A1 1 0 0 1 1.97.28l12.868 6.837a1 1 0 0 1 0 1.766L1.969 15.72A1 1 0 0 1 .5 14.836V10.33a1 1 0 0 1 .816-.983L8.5 8 1.316 6.653A1 1 0 0 1 .5 5.67V1.163Z"
@@ -25,16 +27,18 @@
 	</button>
 
 	<button
-		class="flex justify-between w-full px-4 py-2.5 bg-gray-800 hover:bg-gray-700 outline outline-1 outline-gray-600 rounded-lg transition group"
+		class="flex justify-between w-full px-4 py-2.5 bg-white dark:bg-gray-800 dark:hover:bg-gray-700 outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg transition group"
 		on:click={() => {
 			submitPrompt(`Show me a code snippet of a website's sticky header in CSS and JavaScript.`);
 		}}
 	>
 		<div class="flex flex-col text-left">
-			<div class="text-sm font-medium text-gray-300">Show me a code snippet</div>
+			<div class="text-sm font-medium dark:text-gray-300">Show me a code snippet</div>
 			<div class="text-sm text-gray-500">of a website's sticky header</div>
 		</div>
-		<div class="self-center group-hover:text-gray-300 text-gray-800 transition">
+		<div
+			class="self-center text-white group-hover:text-gray-800 dark:group-hover:text-gray-300 dark:text-gray-800 transition"
+		>
 			<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" class="w-4 h-4"
 				><path
 					d="M.5 1.163A1 1 0 0 1 1.97.28l12.868 6.837a1 1 0 0 1 0 1.766L1.969 15.72A1 1 0 0 1 .5 14.836V10.33a1 1 0 0 1 .816-.983L8.5 8 1.316 6.653A1 1 0 0 1 .5 5.67V1.163Z"
@@ -45,7 +49,7 @@
 	</button>
 
 	<button
-		class=" hidden sm:flex justify-between w-full px-4 py-2.5 bg-gray-800 hover:bg-gray-700 outline outline-1 outline-gray-600 rounded-lg transition group"
+		class=" hidden sm:flex justify-between w-full px-4 py-2.5 bg-white dark:bg-gray-800 dark:hover:bg-gray-700 outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg transition group"
 		on:click={() => {
 			submitPrompt(
 				`Help me study vocabulary: write a sentence for me to fill in the blank, and I'll try to pick the correct option.`
@@ -53,10 +57,12 @@
 		}}
 	>
 		<div class="flex flex-col text-left">
-			<div class="text-sm font-medium text-gray-300">Help me study</div>
+			<div class="text-sm font-medium dark:text-gray-300">Help me study</div>
 			<div class="text-sm text-gray-500">vocabulary for a college entrance exam</div>
 		</div>
-		<div class="self-center group-hover:text-gray-300 text-gray-800 transition">
+		<div
+			class="self-center text-white group-hover:text-gray-800 dark:group-hover:text-gray-300 dark:text-gray-800 transition"
+		>
 			<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" class="w-4 h-4"
 				><path
 					d="M.5 1.163A1 1 0 0 1 1.97.28l12.868 6.837a1 1 0 0 1 0 1.766L1.969 15.72A1 1 0 0 1 .5 14.836V10.33a1 1 0 0 1 .816-.983L8.5 8 1.316 6.653A1 1 0 0 1 .5 5.67V1.163Z"
@@ -67,7 +73,7 @@
 	</button>
 
 	<button
-		class="  hidden sm:flex justify-between w-full px-4 py-2.5 bg-gray-800 hover:bg-gray-700 outline outline-1 outline-gray-600 rounded-lg transition group"
+		class="  hidden sm:flex justify-between w-full px-4 py-2.5 bg-white dark:bg-gray-800 dark:hover:bg-gray-700 outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg transition group"
 		on:click={() => {
 			submitPrompt(
 				`What are 5 creative things I could do with my kids' art? I don't want to throw them away, but it's also so much clutter.`
@@ -75,10 +81,12 @@
 		}}
 	>
 		<div class="flex flex-col text-left">
-			<div class="text-sm font-medium text-gray-300">Give me ideas</div>
+			<div class="text-sm font-medium dark:text-gray-300">Give me ideas</div>
 			<div class="text-sm text-gray-500">for what to do with my kids' art</div>
 		</div>
-		<div class="self-center group-hover:text-gray-300 text-gray-800 transition">
+		<div
+			class="self-center text-white group-hover:text-gray-800 dark:group-hover:text-gray-300 dark:text-gray-800 transition"
+		>
 			<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" class="w-4 h-4"
 				><path
 					d="M.5 1.163A1 1 0 0 1 1.97.28l12.868 6.837a1 1 0 0 1 0 1.766L1.969 15.72A1 1 0 0 1 .5 14.836V10.33a1 1 0 0 1 .816-.983L8.5 8 1.316 6.653A1 1 0 0 1 .5 5.67V1.163Z"

+ 1 - 1
src/lib/components/common/Modal.svelte

@@ -28,7 +28,7 @@
 		}}
 	>
 		<div
-			class="m-auto min-h-52 max-w-full w-[36rem] mx-2 bg-gray-900 rounded-lg shadow-3xl"
+			class="m-auto min-h-52 max-w-full w-[36rem] mx-2 bg-gray-50 dark:bg-gray-900 rounded-lg shadow-3xl"
 			transition:fade={{ delay: 100, duration: 200 }}
 			on:click={(e) => {
 				e.stopPropagation();

+ 4 - 4
src/lib/components/layout/Navbar.svelte

@@ -45,14 +45,14 @@
 </script>
 
 <div
-	class=" fixed top-0 flex flex-row justify-center bg-stone-100/5 text-gray-200 backdrop-blur-xl w-full z-30"
+	class=" fixed top-0 flex flex-row justify-center dark:bg-stone-100/5 dark:text-gray-200 backdrop-blur-xl w-full z-30"
 >
 	<div class="basis-full px-5">
 		<nav class="py-3" id="nav">
 			<div class="flex flex-row justify-between">
 				<div class="pl-2">
 					<button
-						class=" cursor-pointer p-1 flex hover:bg-gray-700 rounded-lg transition"
+						class=" cursor-pointer p-1 flex dark:hover:bg-gray-700 rounded-lg transition"
 						on:click={() => {
 							show = !show;
 						}}
@@ -75,12 +75,12 @@
 				</div>
 
 				<div class=" self-center">
-					{title != '' ? title.split(' ').slice(0, 7).join(' ') : 'Ollama Web UI'}
+					{title != '' ? title.split(' ').slice(0, 6).join(' ') : 'Ollama Web UI'}
 				</div>
 
 				<div class="pr-2">
 					<button
-						class=" cursor-pointer p-1 flex hover:bg-gray-700 rounded-lg transition"
+						class=" cursor-pointer p-1 flex dark:hover:bg-gray-700 rounded-lg transition"
 						on:click={() => {
 							createNewChat();
 						}}

+ 38 - 34
src/routes/+page.svelte

@@ -768,8 +768,10 @@
 	}}
 />
 
-<div class="app text-gray-100">
-	<div class=" bg-gray-800 min-h-screen overflow-auto flex flex-row">
+<div class="app">
+	<div
+		class=" text-gray-700 dark:text-gray-100 bg-white dark:bg-gray-800 min-h-screen overflow-auto flex flex-row"
+	>
 		<Navbar
 			selectedChatId={chatId}
 			{chats}
@@ -789,16 +791,16 @@
 		<div class="min-h-screen w-full flex justify-center">
 			<div class=" py-2.5 flex flex-col justify-between w-full">
 				<div class="max-w-2xl mx-auto w-full px-2.5 mt-14">
-					<div class="p-3 rounded-lg bg-gray-900">
+					<div class="p-3 rounded-lg bg-gray-100 dark:bg-gray-900">
 						<div>
 							<label
 								for="models"
-								class="block mb-2 text-sm font-medium text-gray-200 flex justify-between"
+								class="block mb-2 text-sm font-medium dark:text-gray-200 flex justify-between"
 							>
 								<div class="self-center">Model</div>
 
 								<button
-									class=" self-center hover:text-gray-300"
+									class=" self-center dark:hover:text-gray-300"
 									on:click={() => {
 										openSettings();
 									}}
@@ -828,7 +830,7 @@
 							<div>
 								<select
 									id="models"
-									class="outline-none border border-gray-600 bg-gray-700 text-gray-200 text-sm rounded-lg block w-full p-2.5 placeholder-gray-400"
+									class="outline-none border dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 text-sm rounded-lg block w-full p-2.5 placeholder-gray-400"
 									bind:value={selectedModel}
 									disabled={messages.length != 0}
 								>
@@ -856,17 +858,17 @@
 							<div class="flex justify-center mt-8">
 								<img src="/ollama.png" class="w-16 invert-[80%]" />
 							</div>
-							<div class="mt-6 text-3xl text-gray-500 font-semibold">
+							<div class="mt-6 text-3xl text-gray-400 dark:text-gray-500 font-semibold">
 								Get up and running with large language models, locally.
 							</div>
 
-							<div class=" my-4 text-gray-600">
+							<div class=" my-4 text-gray-300 dark:text-gray-600">
 								Run Llama 2, Code Llama, and other models. <br /> Customize and create your own.
 							</div>
 						</div>
 					{:else}
 						{#each messages as message, messageIdx}
-							<div class=" w-full {message.role == 'user' ? '' : ' bg-gray-700'}">
+							<div class=" w-full {message.role == 'user' ? '' : ' bg-gray-100  dark:bg-gray-700'}">
 								<div class="flex justify-between p-5 py-10 max-w-3xl mx-auto rounded-lg group">
 									<div class="space-x-7 flex w-full">
 										<div class="">
@@ -884,25 +886,27 @@
 											<div class="w-full pr-28">
 												<div class="animate-pulse flex w-full">
 													<div class="space-y-2 w-full">
-														<div class="h-2 bg-gray-600 rounded mr-14" />
+														<div class="h-2 bg-gray-200 dark:bg-gray-600 rounded mr-14" />
 
 														<div class="grid grid-cols-3 gap-4">
-															<div class="h-2 bg-gray-600 rounded col-span-2" />
-															<div class="h-2 bg-gray-600 rounded col-span-1" />
+															<div class="h-2 bg-gray-200 dark:bg-gray-600 rounded col-span-2" />
+															<div class="h-2 bg-gray-200 dark:bg-gray-600 rounded col-span-1" />
 														</div>
 														<div class="grid grid-cols-4 gap-4">
-															<div class="h-2 bg-gray-600 rounded col-span-1" />
-															<div class="h-2 bg-gray-600 rounded col-span-2" />
-															<div class="h-2 bg-gray-600 rounded col-span-1 mr-4" />
+															<div class="h-2 bg-gray-200 dark:bg-gray-600 rounded col-span-1" />
+															<div class="h-2 bg-gray-200 dark:bg-gray-600 rounded col-span-2" />
+															<div
+																class="h-2 bg-gray-200 dark:bg-gray-600 rounded col-span-1 mr-4"
+															/>
 														</div>
 
-														<div class="h-2 bg-gray-600 rounded" />
+														<div class="h-2 bg-gray-200 dark:bg-gray-600 rounded" />
 													</div>
 												</div>
 											</div>
 										{:else}
 											<div
-												class="prose chat-{message.role} w-full max-w-full prose-invert prose-headings:my-0 prose-p:my-0 prose-p:-mb-4 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-img:my-0 prose-ul:-my-4 prose-ol:-my-4 prose-li:-my-3 prose-ul:-mb-8 prose-ol:-mb-8 prose-li:-mb-4 whitespace-pre-line"
+												class="prose chat-{message.role} w-full max-w-full dark:prose-invert prose-headings:my-0 prose-p:my-0 prose-p:-mb-4 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-img:my-0 prose-ul:-my-4 prose-ol:-my-4 prose-li:-my-3 prose-ul:-mb-8 prose-ol:-mb-8 prose-li:-mb-4 whitespace-pre-line"
 											>
 												{#if message.role == 'user'}
 													{#if message?.edit === true}
@@ -920,9 +924,9 @@
 																}}
 															/>
 
-															<div class=" flex justify-end space-x-2 text-sm text-gray-100">
+															<div class=" flex justify-end space-x-2 text-sm font-medium">
 																<button
-																	class="px-4 py-2.5 bg-emerald-600 hover:bg-emerald-700 transition rounded-lg"
+																	class="px-4 py-2.5 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded-lg"
 																	on:click={() => {
 																		confirmEditMessage(messageIdx);
 																	}}
@@ -931,7 +935,7 @@
 																</button>
 
 																<button
-																	class=" px-4 py-2.5 bg-gray-800 hover:bg-gray-700 transition outline outline-1 outline-gray-600 rounded-lg"
+																	class=" px-4 py-2.5 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 text-gray-700 transition outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg"
 																	on:click={() => {
 																		cancelEditMessage(messageIdx);
 																	}}
@@ -949,8 +953,8 @@
 													{#if message.done}
 														<div class=" flex justify-end space-x-1 text-gray-400">
 															<button
-																class="p-1 rounded hover:bg-gray-800 {message.rating === 1
-																	? 'bg-gray-800'
+																class="p-1 rounded dark:hover:bg-gray-800 {message.rating === 1
+																	? ' bg-gray-200 dark:bg-gray-800'
 																	: ''} transition"
 																on:click={() => {
 																	rateMessage(messageIdx, 1);
@@ -971,8 +975,8 @@
 																>
 															</button>
 															<button
-																class="p-1 rounded hover:bg-gray-800 {message.rating === -1
-																	? 'bg-gray-800'
+																class="p-1 rounded dark:hover:bg-gray-800 {message.rating === -1
+																	? 'bg-gray-200 dark:bg-gray-800'
 																	: ''} transition"
 																on:click={() => {
 																	rateMessage(messageIdx, -1);
@@ -1004,7 +1008,7 @@
 										{#if message.role == 'user'}
 											{#if message?.edit !== true}
 												<button
-													class="invisible group-hover:visible p-1 rounded hover:bg-gray-700 transition"
+													class="invisible group-hover:visible p-1 rounded dark:hover:bg-gray-700 transition"
 													on:click={() => {
 														editMessage(messageIdx);
 													}}
@@ -1026,7 +1030,7 @@
 											{/if}
 										{:else if message.done}
 											<button
-												class="p-1 rounded hover:bg-gray-700 transition"
+												class="p-1 rounded dark:hover:bg-gray-700 transition"
 												on:click={() => {
 													copyToClipboard(message.content);
 												}}
@@ -1058,7 +1062,7 @@
 			<div class="fixed bottom-0 w-full">
 				<!-- <hr class=" mb-3 border-gray-600" /> -->
 
-				<div class=" bg-gradient-to-t from-gray-900 pt-5">
+				<div class=" bg-gradient-to-t from-gray-100 dark:from-gray-900 pt-5">
 					<div class="max-w-3xl p-2.5 -mb-0.5 mx-auto inset-x-0">
 						{#if messages.length == 0 && suggestions !== 'false'}
 							<Suggestions {submitPrompt} />
@@ -1068,7 +1072,7 @@
 							{#if messages.at(-1).done == true}
 								<div class=" flex justify-end mb-2.5">
 									<button
-										class=" flex px-4 py-2.5 bg-gray-800 hover:bg-gray-700 outline outline-1 outline-gray-600 rounded-lg"
+										class=" flex px-4 py-2.5 bg-white hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg"
 										on:click={regenerateResponse}
 									>
 										<div class=" self-center mr-1">
@@ -1085,13 +1089,13 @@
 												/>
 											</svg>
 										</div>
-										<div class=" self-center text-sm">Regenerate</div>
+										<div class=" self-center text-sm font-medium">Regenerate</div>
 									</button>
 								</div>
 							{:else}
 								<div class=" flex justify-end mb-2.5">
 									<button
-										class=" flex px-4 py-2.5 bg-gray-800 hover:bg-gray-700 outline outline-1 outline-gray-600 rounded-lg"
+										class=" flex px-4 py-2.5 bg-white hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg"
 										on:click={stopResponse}
 									>
 										<div class=" self-center mr-1">
@@ -1108,7 +1112,7 @@
 												/>
 											</svg>
 										</div>
-										<div class=" self-center text-sm">Stop generating</div>
+										<div class=" self-center text-sm font-medium">Stop generating</div>
 									</button>
 								</div>
 							{/if}
@@ -1120,7 +1124,7 @@
 							}}
 						>
 							<textarea
-								class="rounded-xl bg-gray-700 outline-none w-full py-3 px-5 pr-12 resize-none"
+								class="rounded-xl dark:bg-gray-700 dark:text-gray-100 outline-none shadow border dark:border-gray-700 w-full py-3 px-5 pr-12 resize-none"
 								placeholder="Send a message"
 								bind:value={prompt}
 								on:keypress={(e) => {
@@ -1135,7 +1139,7 @@
 								rows="1"
 								on:input={(e) => {
 									e.target.style.height = '';
-									e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px';
+									e.target.style.height = Math.min(e.target.scrollHeight, 200) + 2 + 'px';
 								}}
 							/>
 							<div class=" absolute right-0 bottom-0">
@@ -1144,7 +1148,7 @@
 										<button
 											class="{prompt !== ''
 												? 'bg-emerald-600 text-gray-100 hover:bg-emerald-700 '
-												: 'text-gray-600 disabled'} transition rounded p-2"
+												: 'text-gray-200 dark:text-gray-600 disabled'} transition rounded p-2"
 											type="submit"
 											disabled={prompt === ''}
 										>

+ 1 - 0
tailwind.config.js

@@ -1,5 +1,6 @@
 /** @type {import('tailwindcss').Config} */
 export default {
+	darkMode: 'class',
 	content: ['./src/**/*.{html,js,svelte,ts}'],
 	theme: {
 		extend: {