25 Commits 21d616f8ed ... c91b0cd8c4

Author SHA1 Message Date
  Timothy Jaeryang Baek c91b0cd8c4 refac: vite config 8 hours ago
  Timothy Jaeryang Baek 3ff864eb63 refac: select action model behaviour 8 hours ago
  Timothy Jaeryang Baek 90257442e3 refac: styling 9 hours ago
  Timothy Jaeryang Baek 0447790e6a feat: MODEL_LIST_CACHE_TTL 9 hours ago
  Timothy Jaeryang Baek 09f0c6beee fix: windows start script 9 hours ago
  Timothy Jaeryang Baek c8e8111dd9 refac 9 hours ago
  Timothy Jaeryang Baek 38dfa6e6db refac 9 hours ago
  Tim Jaeryang Baek 276bfa02d0 Merge pull request #15314 from silentoplayz/spinner-and-xmark-icon-components 10 hours ago
  Tim Jaeryang Baek 79aa73347e Merge pull request #15318 from hula-la/fix/ko-translation 10 hours ago
  Timothy Jaeryang Baek 2ae9bc30bd refac: feedback item click issue 10 hours ago
  Timothy Jaeryang Baek aa13a7caf9 refac 10 hours ago
  Tim Jaeryang Baek 8c5beddb5f Merge pull request #15310 from silentoplayz/icons 10 hours ago
  Tim Jaeryang Baek c303232657 Merge pull request #15344 from silentoplayz/small-fixes 10 hours ago
  Tim Jaeryang Baek 8b49d6dda0 Merge pull request #15325 from headwAI-GmbH/make-qdrant-collection-prefix-configurable 10 hours ago
  Silentoplayz e3bacc3296 fix: Small fixes 12 hours ago
  Tim Jaeryang Baek 767c4426cd Merge pull request #15328 from acwoo97/feat/lock 13 hours ago
  안찬우[AI Product] 789e6a0db3 feat : Retry acquiring usage cleanup lock to handle potential stale locks 1 day ago
  guenhter a66206f44f feat: support better qdrant collection isolation 1 day ago
  soo-jin-kim 2f4604921f fix: fix korean translation 1 day ago
  Silentoplayz f429dd667f chore: remove comment 1 day ago
  Silentoplayz d1e3940abd refac: Spinner and XMark components 1 day ago
  Silentoplayz cf24e39404 chore: add strokeWidth back 1 day ago
  Silentoplayz dd0e6bf58f remove class="w-5 h-5" /> 2 days ago
  Silentoplayz 88c4f55a51 chore: update 2 days ago
  Silentoplayz 3f865f0b66 chore: MagnifyingGlass -> Search 2 days ago
88 changed files with 232 additions and 922 deletions
  1. 1 0
      backend/open_webui/config.py
  2. 10 0
      backend/open_webui/env.py
  3. 2 1
      backend/open_webui/retrieval/vector/dbs/qdrant.py
  4. 2 1
      backend/open_webui/retrieval/vector/dbs/qdrant_multitenancy.py
  5. 2 1
      backend/open_webui/routers/ollama.py
  6. 2 1
      backend/open_webui/routers/openai.py
  7. 22 4
      backend/open_webui/socket/main.py
  8. 1 1
      backend/start_windows.bat
  9. 2 2
      scripts/prepare-pyodide.js
  10. 1 0
      src/lib/apis/users/index.ts
  11. 4 34
      src/lib/components/AddConnectionModal.svelte
  12. 4 35
      src/lib/components/AddServerModal.svelte
  13. 4 10
      src/lib/components/ChangelogModal.svelte
  14. 4 33
      src/lib/components/ImportModal.svelte
  15. 2 10
      src/lib/components/admin/Evaluations/FeedbackModal.svelte
  16. 1 1
      src/lib/components/admin/Evaluations/Feedbacks.svelte
  17. 3 3
      src/lib/components/admin/Evaluations/Leaderboard.svelte
  18. 2 10
      src/lib/components/admin/Evaluations/LeaderboardModal.svelte
  19. 2 27
      src/lib/components/admin/Settings/Audio.svelte
  20. 2 10
      src/lib/components/admin/Settings/Connections/ManageOllamaModal.svelte
  21. 2 28
      src/lib/components/admin/Settings/Documents.svelte
  22. 4 33
      src/lib/components/admin/Settings/Evaluations/ArenaModelModal.svelte
  23. 2 23
      src/lib/components/admin/Settings/Images.svelte
  24. 1 1
      src/lib/components/admin/Settings/Interface.svelte
  25. 2 10
      src/lib/components/admin/Settings/Interface/Banners.svelte
  26. 1 1
      src/lib/components/admin/Settings/Models.svelte
  27. 4 34
      src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte
  28. 1 1
      src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte
  29. 2 10
      src/lib/components/admin/Settings/Models/ManageModelsModal.svelte
  30. 0 1
      src/lib/components/admin/Settings/WebSearch.svelte
  31. 2 12
      src/lib/components/admin/Users/Groups.svelte
  32. 4 33
      src/lib/components/admin/Users/Groups/AddGroupModal.svelte
  33. 4 33
      src/lib/components/admin/Users/Groups/EditGroupModal.svelte
  34. 2 12
      src/lib/components/admin/Users/Groups/Users.svelte
  35. 1 1
      src/lib/components/admin/Users/UserList.svelte
  36. 4 33
      src/lib/components/admin/Users/UserList/AddUserModal.svelte
  37. 2 10
      src/lib/components/admin/Users/UserList/EditUserModal.svelte
  38. 1 1
      src/lib/components/chat/Chat.svelte
  39. 1 0
      src/lib/components/chat/MessageInput/CallOverlay.svelte
  40. 2 10
      src/lib/components/chat/MessageInput/VoiceRecording.svelte
  41. 1 0
      src/lib/components/chat/Messages.svelte
  42. 3 10
      src/lib/components/chat/Messages/CitationsModal.svelte
  43. 2 10
      src/lib/components/chat/Messages/CodeExecutionModal.svelte
  44. 16 6
      src/lib/components/chat/Messages/ContentRenderer.svelte
  45. 3 0
      src/lib/components/chat/Messages/Message.svelte
  46. 2 0
      src/lib/components/chat/Messages/MultiResponseMessages.svelte
  47. 2 10
      src/lib/components/chat/Messages/RateComment.svelte
  48. 2 0
      src/lib/components/chat/Messages/ResponseMessage.svelte
  49. 2 2
      src/lib/components/chat/Messages/ResponseMessage/WebSearchResults.svelte
  50. 2 23
      src/lib/components/chat/ModelSelector/Selector.svelte
  51. 4 33
      src/lib/components/chat/Settings/Personalization/AddMemoryModal.svelte
  52. 4 33
      src/lib/components/chat/Settings/Personalization/EditMemoryModal.svelte
  53. 2 10
      src/lib/components/chat/ShareChatModal.svelte
  54. 2 10
      src/lib/components/chat/ShortcutsModal.svelte
  55. 2 10
      src/lib/components/chat/ToolServersModal.svelte
  56. 2 10
      src/lib/components/common/FileItem.svelte
  57. 2 10
      src/lib/components/common/Image.svelte
  58. 3 10
      src/lib/components/common/ImagePreview.svelte
  59. 1 0
      src/lib/components/common/Modal.svelte
  60. 7 2
      src/lib/components/common/Spinner.svelte
  61. 0 19
      src/lib/components/icons/MagnifyingGlass.svelte
  62. 6 4
      src/lib/components/icons/XMark.svelte
  63. 1 1
      src/lib/components/layout/ChatsModal.svelte
  64. 1 1
      src/lib/components/layout/SearchModal.svelte
  65. 2 2
      src/lib/components/layout/Sidebar.svelte
  66. 4 35
      src/lib/components/layout/Sidebar/ChannelModal.svelte
  67. 2 12
      src/lib/components/layout/Sidebar/SearchInput.svelte
  68. 1 1
      src/lib/components/notes/NoteEditor.svelte
  69. 1 1
      src/lib/components/notes/Notes.svelte
  70. 1 1
      src/lib/components/workspace/Knowledge.svelte
  71. 2 23
      src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte
  72. 3 13
      src/lib/components/workspace/Knowledge/KnowledgeBase.svelte
  73. 1 1
      src/lib/components/workspace/Models.svelte
  74. 2 12
      src/lib/components/workspace/Models/Knowledge/Selector.svelte
  75. 4 33
      src/lib/components/workspace/Models/ModelEditor.svelte
  76. 2 23
      src/lib/components/workspace/Prompts/PromptEditor.svelte
  77. 1 1
      src/lib/components/workspace/Tools.svelte
  78. 2 10
      src/lib/components/workspace/common/AccessControlModal.svelte
  79. 2 10
      src/lib/components/workspace/common/ManifestModal.svelte
  80. 3 33
      src/lib/components/workspace/common/ValvesModal.svelte
  81. 1 1
      src/lib/i18n/index.ts
  82. 5 5
      src/lib/i18n/locales/ko-KR/translation.json
  83. 1 1
      src/routes/(app)/+layout.svelte
  84. 1 1
      src/routes/(app)/admin/functions/edit/+page.svelte
  85. 1 1
      src/routes/(app)/workspace/tools/edit/+page.svelte
  86. 1 1
      src/routes/+error.svelte
  87. 1 1
      src/routes/auth/+page.svelte
  88. 1 15
      vite.config.ts

+ 1 - 0
backend/open_webui/config.py

@@ -1799,6 +1799,7 @@ QDRANT_GRPC_PORT = int(os.environ.get("QDRANT_GRPC_PORT", "6334"))
 ENABLE_QDRANT_MULTITENANCY_MODE = (
 ENABLE_QDRANT_MULTITENANCY_MODE = (
     os.environ.get("ENABLE_QDRANT_MULTITENANCY_MODE", "false").lower() == "true"
     os.environ.get("ENABLE_QDRANT_MULTITENANCY_MODE", "false").lower() == "true"
 )
 )
+QDRANT_COLLECTION_PREFIX = os.environ.get("QDRANT_COLLECTION_PREFIX", "open-webui")
 
 
 # OpenSearch
 # OpenSearch
 OPENSEARCH_URI = os.environ.get("OPENSEARCH_URI", "https://localhost:9200")
 OPENSEARCH_URI = os.environ.get("OPENSEARCH_URI", "https://localhost:9200")

+ 10 - 0
backend/open_webui/env.py

@@ -403,6 +403,16 @@ ENABLE_WEBSOCKET_SUPPORT = (
     os.environ.get("ENABLE_WEBSOCKET_SUPPORT", "True").lower() == "true"
     os.environ.get("ENABLE_WEBSOCKET_SUPPORT", "True").lower() == "true"
 )
 )
 
 
+MODEL_LIST_CACHE_TTL = os.environ.get("MODEL_LIST_CACHE_TTL", "1")
+if MODEL_LIST_CACHE_TTL == "":
+    MODEL_LIST_CACHE_TTL = None
+else:
+    try:
+        MODEL_LIST_CACHE_TTL = int(MODEL_LIST_CACHE_TTL)
+    except Exception:
+        MODEL_LIST_CACHE_TTL = 1
+
+
 WEBSOCKET_MANAGER = os.environ.get("WEBSOCKET_MANAGER", "")
 WEBSOCKET_MANAGER = os.environ.get("WEBSOCKET_MANAGER", "")
 
 
 WEBSOCKET_REDIS_URL = os.environ.get("WEBSOCKET_REDIS_URL", REDIS_URL)
 WEBSOCKET_REDIS_URL = os.environ.get("WEBSOCKET_REDIS_URL", REDIS_URL)

+ 2 - 1
backend/open_webui/retrieval/vector/dbs/qdrant.py

@@ -18,6 +18,7 @@ from open_webui.config import (
     QDRANT_ON_DISK,
     QDRANT_ON_DISK,
     QDRANT_GRPC_PORT,
     QDRANT_GRPC_PORT,
     QDRANT_PREFER_GRPC,
     QDRANT_PREFER_GRPC,
+    QDRANT_COLLECTION_PREFIX,
 )
 )
 from open_webui.env import SRC_LOG_LEVELS
 from open_webui.env import SRC_LOG_LEVELS
 
 
@@ -29,7 +30,7 @@ log.setLevel(SRC_LOG_LEVELS["RAG"])
 
 
 class QdrantClient(VectorDBBase):
 class QdrantClient(VectorDBBase):
     def __init__(self):
     def __init__(self):
-        self.collection_prefix = "open-webui"
+        self.collection_prefix = QDRANT_COLLECTION_PREFIX
         self.QDRANT_URI = QDRANT_URI
         self.QDRANT_URI = QDRANT_URI
         self.QDRANT_API_KEY = QDRANT_API_KEY
         self.QDRANT_API_KEY = QDRANT_API_KEY
         self.QDRANT_ON_DISK = QDRANT_ON_DISK
         self.QDRANT_ON_DISK = QDRANT_ON_DISK

+ 2 - 1
backend/open_webui/retrieval/vector/dbs/qdrant_multitenancy.py

@@ -9,6 +9,7 @@ from open_webui.config import (
     QDRANT_ON_DISK,
     QDRANT_ON_DISK,
     QDRANT_PREFER_GRPC,
     QDRANT_PREFER_GRPC,
     QDRANT_URI,
     QDRANT_URI,
+    QDRANT_COLLECTION_PREFIX,
 )
 )
 from open_webui.env import SRC_LOG_LEVELS
 from open_webui.env import SRC_LOG_LEVELS
 from open_webui.retrieval.vector.main import (
 from open_webui.retrieval.vector.main import (
@@ -30,7 +31,7 @@ log.setLevel(SRC_LOG_LEVELS["RAG"])
 
 
 class QdrantClient(VectorDBBase):
 class QdrantClient(VectorDBBase):
     def __init__(self):
     def __init__(self):
-        self.collection_prefix = "open-webui"
+        self.collection_prefix = QDRANT_COLLECTION_PREFIX
         self.QDRANT_URI = QDRANT_URI
         self.QDRANT_URI = QDRANT_URI
         self.QDRANT_API_KEY = QDRANT_API_KEY
         self.QDRANT_API_KEY = QDRANT_API_KEY
         self.QDRANT_ON_DISK = QDRANT_ON_DISK
         self.QDRANT_ON_DISK = QDRANT_ON_DISK

+ 2 - 1
backend/open_webui/routers/ollama.py

@@ -59,6 +59,7 @@ from open_webui.config import (
 from open_webui.env import (
 from open_webui.env import (
     ENV,
     ENV,
     SRC_LOG_LEVELS,
     SRC_LOG_LEVELS,
+    MODEL_LIST_CACHE_TTL,
     AIOHTTP_CLIENT_SESSION_SSL,
     AIOHTTP_CLIENT_SESSION_SSL,
     AIOHTTP_CLIENT_TIMEOUT,
     AIOHTTP_CLIENT_TIMEOUT,
     AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST,
     AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST,
@@ -330,7 +331,7 @@ def merge_ollama_models_lists(model_lists):
     return list(merged_models.values())
     return list(merged_models.values())
 
 
 
 
-@cached(ttl=1)
+@cached(ttl=MODEL_LIST_CACHE_TTL)
 async def get_all_models(request: Request, user: UserModel = None):
 async def get_all_models(request: Request, user: UserModel = None):
     log.info("get_all_models()")
     log.info("get_all_models()")
     if request.app.state.config.ENABLE_OLLAMA_API:
     if request.app.state.config.ENABLE_OLLAMA_API:

+ 2 - 1
backend/open_webui/routers/openai.py

@@ -21,6 +21,7 @@ from open_webui.config import (
     CACHE_DIR,
     CACHE_DIR,
 )
 )
 from open_webui.env import (
 from open_webui.env import (
+    MODEL_LIST_CACHE_TTL,
     AIOHTTP_CLIENT_SESSION_SSL,
     AIOHTTP_CLIENT_SESSION_SSL,
     AIOHTTP_CLIENT_TIMEOUT,
     AIOHTTP_CLIENT_TIMEOUT,
     AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST,
     AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST,
@@ -386,7 +387,7 @@ async def get_filtered_models(models, user):
     return filtered_models
     return filtered_models
 
 
 
 
-@cached(ttl=1)
+@cached(ttl=MODEL_LIST_CACHE_TTL)
 async def get_all_models(request: Request, user: UserModel) -> dict[str, list]:
 async def get_all_models(request: Request, user: UserModel) -> dict[str, list]:
     log.info("get_all_models()")
     log.info("get_all_models()")
 
 

+ 22 - 4
backend/open_webui/socket/main.py

@@ -1,4 +1,6 @@
 import asyncio
 import asyncio
+import random
+
 import socketio
 import socketio
 import logging
 import logging
 import sys
 import sys
@@ -105,10 +107,26 @@ else:
 
 
 
 
 async def periodic_usage_pool_cleanup():
 async def periodic_usage_pool_cleanup():
-    if not aquire_func():
-        log.debug("Usage pool cleanup lock already exists. Not running it.")
-        return
-    log.debug("Running periodic_usage_pool_cleanup")
+    max_retries = 2
+    retry_delay = random.uniform(
+        WEBSOCKET_REDIS_LOCK_TIMEOUT / 2, WEBSOCKET_REDIS_LOCK_TIMEOUT
+    )
+    for attempt in range(max_retries + 1):
+        if aquire_func():
+            break
+        else:
+            if attempt < max_retries:
+                log.debug(
+                    f"Cleanup lock already exists. Retry {attempt + 1} after {retry_delay}s..."
+                )
+                await asyncio.sleep(retry_delay)
+            else:
+                log.warning(
+                    "Failed to acquire cleanup lock after retries. Skipping cleanup."
+                )
+                return
+
+    log.debug("Running periodic_cleanup")
     try:
     try:
         while True:
         while True:
             if not renew_func():
             if not renew_func():

+ 1 - 1
backend/start_windows.bat

@@ -28,7 +28,7 @@ SET "WEBUI_SECRET_KEY=%WEBUI_SECRET_KEY%"
 SET "WEBUI_JWT_SECRET_KEY=%WEBUI_JWT_SECRET_KEY%"
 SET "WEBUI_JWT_SECRET_KEY=%WEBUI_JWT_SECRET_KEY%"
 
 
 :: Check if WEBUI_SECRET_KEY and WEBUI_JWT_SECRET_KEY are not set
 :: Check if WEBUI_SECRET_KEY and WEBUI_JWT_SECRET_KEY are not set
-IF "%WEBUI_SECRET_KEY%%WEBUI_JWT_SECRET_KEY%" == " " (
+IF "%WEBUI_SECRET_KEY% %WEBUI_JWT_SECRET_KEY%" == " " (
     echo Loading WEBUI_SECRET_KEY from file, not provided as an environment variable.
     echo Loading WEBUI_SECRET_KEY from file, not provided as an environment variable.
 
 
     IF NOT EXIST "%KEY_FILE%" (
     IF NOT EXIST "%KEY_FILE%" (

+ 2 - 2
scripts/prepare-pyodide.js

@@ -74,8 +74,8 @@ async function downloadPackages() {
 			console.log('Pyodide version mismatch, removing static/pyodide directory');
 			console.log('Pyodide version mismatch, removing static/pyodide directory');
 			await rmdir('static/pyodide', { recursive: true });
 			await rmdir('static/pyodide', { recursive: true });
 		}
 		}
-	} catch (e) {
-		console.log('Pyodide package not found, proceeding with download.');
+	} catch (err) {
+		console.log('Pyodide package not found, proceeding with download.', err);
 	}
 	}
 
 
 	try {
 	try {

+ 1 - 0
src/lib/apis/users/index.ts

@@ -403,6 +403,7 @@ export const deleteUserById = async (token: string, userId: string) => {
 };
 };
 
 
 type UserUpdateForm = {
 type UserUpdateForm = {
+	role: string;
 	profile_image_url: string;
 	profile_image_url: string;
 	email: string;
 	email: string;
 	name: string;
 	name: string;

+ 4 - 34
src/lib/components/AddConnectionModal.svelte

@@ -15,6 +15,8 @@
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import Switch from '$lib/components/common/Switch.svelte';
 	import Switch from '$lib/components/common/Switch.svelte';
 	import Tags from './common/Tags.svelte';
 	import Tags from './common/Tags.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	export let onSubmit: Function = () => {};
 	export let onSubmit: Function = () => {};
 	export let onDelete: Function = () => {};
 	export let onDelete: Function = () => {};
@@ -208,17 +210,7 @@
 					show = false;
 					show = false;
 				}}
 				}}
 			>
 			>
-				<svg
-					xmlns="http://www.w3.org/2000/svg"
-					viewBox="0 0 20 20"
-					fill="currentColor"
-					aria-hidden="true"
-					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 
@@ -524,29 +516,7 @@
 
 
 							{#if loading}
 							{#if loading}
 								<div class="ml-2 self-center">
 								<div class="ml-2 self-center">
-									<svg
-										class=" w-4 h-4"
-										viewBox="0 0 24 24"
-										fill="currentColor"
-										xmlns="http://www.w3.org/2000/svg"
-										><style>
-											.spinner_ajPY {
-												transform-origin: center;
-												animation: spinner_AtaB 0.75s infinite linear;
-											}
-											@keyframes spinner_AtaB {
-												100% {
-													transform: rotate(360deg);
-												}
-											}
-										</style><path
-											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-											opacity=".25"
-										/><path
-											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-											class="spinner_ajPY"
-										/></svg
-									>
+									<Spinner />
 								</div>
 								</div>
 							{/if}
 							{/if}
 						</button>
 						</button>

+ 4 - 35
src/lib/components/AddServerModal.svelte

@@ -15,6 +15,8 @@
 	import { getToolServerData } from '$lib/apis';
 	import { getToolServerData } from '$lib/apis';
 	import { verifyToolServerConnection } from '$lib/apis/configs';
 	import { verifyToolServerConnection } from '$lib/apis/configs';
 	import AccessControl from './workspace/common/AccessControl.svelte';
 	import AccessControl from './workspace/common/AccessControl.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	export let onSubmit: Function = () => {};
 	export let onSubmit: Function = () => {};
 	export let onDelete: Function = () => {};
 	export let onDelete: Function = () => {};
@@ -168,17 +170,7 @@
 					show = false;
 					show = false;
 				}}
 				}}
 			>
 			>
-				<svg
-					xmlns="http://www.w3.org/2000/svg"
-					viewBox="0 0 20 20"
-					fill="currentColor"
-					aria-hidden="true"
-					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 
@@ -389,30 +381,7 @@
 
 
 							{#if loading}
 							{#if loading}
 								<div class="ml-2 self-center">
 								<div class="ml-2 self-center">
-									<svg
-										aria-hidden="true"
-										class=" w-4 h-4"
-										viewBox="0 0 24 24"
-										fill="currentColor"
-										xmlns="http://www.w3.org/2000/svg"
-										><style>
-											.spinner_ajPY {
-												transform-origin: center;
-												animation: spinner_AtaB 0.75s infinite linear;
-											}
-											@keyframes spinner_AtaB {
-												100% {
-													transform: rotate(360deg);
-												}
-											}
-										</style><path
-											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-											opacity=".25"
-										/><path
-											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-											class="spinner_ajPY"
-										/></svg
-									>
+									<Spinner />
 								</div>
 								</div>
 							{/if}
 							{/if}
 						</button>
 						</button>

+ 4 - 10
src/lib/components/ChangelogModal.svelte

@@ -9,6 +9,7 @@
 
 
 	import Modal from './common/Modal.svelte';
 	import Modal from './common/Modal.svelte';
 	import { updateUserSettings } from '$lib/apis/users';
 	import { updateUserSettings } from '$lib/apis/users';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
@@ -36,18 +37,11 @@
 					localStorage.version = $config.version;
 					localStorage.version = $config.version;
 					show = false;
 					show = false;
 				}}
 				}}
+				aria-label={$i18n.t('Close')}
 			>
 			>
-				<svg
-					xmlns="http://www.w3.org/2000/svg"
-					viewBox="0 0 20 20"
-					fill="currentColor"
-					class="w-5 h-5"
-				>
+				<XMark className={'size-5'}>
 					<p class="sr-only">{$i18n.t('Close')}</p>
 					<p class="sr-only">{$i18n.t('Close')}</p>
-					<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>
+				</XMark>
 			</button>
 			</button>
 		</div>
 		</div>
 		<div class="flex items-center mt-1">
 		<div class="flex items-center mt-1">

+ 4 - 33
src/lib/components/ImportModal.svelte

@@ -3,7 +3,9 @@
 	import { getContext, onMount } from 'svelte';
 	import { getContext, onMount } from 'svelte';
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
+	import Spinner from '$lib/components/common/Spinner.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 	import { extractFrontmatter } from '$lib/utils';
 	import { extractFrontmatter } from '$lib/utils';
 
 
 	export let show = false;
 	export let show = false;
@@ -69,16 +71,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 
@@ -120,29 +113,7 @@
 
 
 							{#if loading}
 							{#if loading}
 								<div class="ml-2 self-center">
 								<div class="ml-2 self-center">
-									<svg
-										class=" w-4 h-4"
-										viewBox="0 0 24 24"
-										fill="currentColor"
-										xmlns="http://www.w3.org/2000/svg"
-										><style>
-											.spinner_ajPY {
-												transform-origin: center;
-												animation: spinner_AtaB 0.75s infinite linear;
-											}
-											@keyframes spinner_AtaB {
-												100% {
-													transform: rotate(360deg);
-												}
-											}
-										</style><path
-											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-											opacity=".25"
-										/><path
-											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-											class="spinner_ajPY"
-										/></svg
-									>
+									<Spinner />
 								</div>
 								</div>
 							{/if}
 							{/if}
 						</button>
 						</button>

+ 2 - 10
src/lib/components/admin/Evaluations/FeedbackModal.svelte

@@ -2,6 +2,7 @@
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import { getContext } from 'svelte';
 	import { getContext } from 'svelte';
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	export let show = false;
 	export let show = false;
 	export let selectedFeedback = null;
 	export let selectedFeedback = null;
@@ -22,16 +23,7 @@
 					{$i18n.t('Feedback Details')}
 					{$i18n.t('Feedback Details')}
 				</div>
 				</div>
 				<button class="self-center" on:click={close} aria-label="Close">
 				<button class="self-center" on:click={close} aria-label="Close">
-					<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>
+					<XMark className={'size-5'} />
 				</button>
 				</button>
 			</div>
 			</div>
 
 

+ 1 - 1
src/lib/components/admin/Evaluations/Feedbacks.svelte

@@ -369,7 +369,7 @@
 							{dayjs(feedback.updated_at * 1000).fromNow()}
 							{dayjs(feedback.updated_at * 1000).fromNow()}
 						</td>
 						</td>
 
 
-						<td class=" px-3 py-1 text-right font-semibold">
+						<td class=" px-3 py-1 text-right font-semibold" on:click={(e) => e.stopPropagation()}>
 							<FeedbackMenu
 							<FeedbackMenu
 								on:delete={(e) => {
 								on:delete={(e) => {
 									deleteFeedbackHandler(feedback.id);
 									deleteFeedbackHandler(feedback.id);

+ 3 - 3
src/lib/components/admin/Evaluations/Leaderboard.svelte

@@ -11,7 +11,7 @@
 
 
 	import Spinner from '$lib/components/common/Spinner.svelte';
 	import Spinner from '$lib/components/common/Spinner.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
-	import MagnifyingGlass from '$lib/components/icons/MagnifyingGlass.svelte';
+	import Search from '$lib/components/icons/Search.svelte';
 
 
 	import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
 	import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
 	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
 	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
@@ -350,7 +350,7 @@
 		<Tooltip content={$i18n.t('Re-rank models by topic similarity')}>
 		<Tooltip content={$i18n.t('Re-rank models by topic similarity')}>
 			<div class="flex flex-1">
 			<div class="flex flex-1">
 				<div class=" self-center ml-1 mr-3">
 				<div class=" self-center ml-1 mr-3">
-					<MagnifyingGlass className="size-3" />
+					<Search className="size-3" />
 				</div>
 				</div>
 				<input
 				<input
 					class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-hidden bg-transparent"
 					class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-hidden bg-transparent"
@@ -371,7 +371,7 @@
 	{#if loadingLeaderboard}
 	{#if loadingLeaderboard}
 		<div class=" absolute top-0 bottom-0 left-0 right-0 flex">
 		<div class=" absolute top-0 bottom-0 left-0 right-0 flex">
 			<div class="m-auto">
 			<div class="m-auto">
-				<Spinner />
+				<Spinner className="size-5" />
 			</div>
 			</div>
 		</div>
 		</div>
 	{/if}
 	{/if}

+ 2 - 10
src/lib/components/admin/Evaluations/LeaderboardModal.svelte

@@ -6,6 +6,7 @@
 	export let feedbacks = [];
 	export let feedbacks = [];
 	export let onClose: () => void = () => {};
 	export let onClose: () => void = () => {};
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	const close = () => {
 	const close = () => {
 		show = false;
 		show = false;
@@ -37,16 +38,7 @@
 				{model.name}
 				{model.name}
 			</div>
 			</div>
 			<button class="self-center" on:click={close} aria-label="Close">
 			<button class="self-center" on:click={close} aria-label="Close">
-				<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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 		<div class="px-5 pb-4 dark:text-gray-200">
 		<div class="px-5 pb-4 dark:text-gray-200">

+ 2 - 27
src/lib/components/admin/Settings/Audio.svelte

@@ -12,6 +12,7 @@
 	} from '$lib/apis/audio';
 	} from '$lib/apis/audio';
 	import { config, settings } from '$lib/stores';
 	import { config, settings } from '$lib/stores';
 
 
+	import Spinner from '$lib/components/common/Spinner.svelte';
 	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
 	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
 
 
 	import { TTS_RESPONSE_SPLIT } from '$lib/types';
 	import { TTS_RESPONSE_SPLIT } from '$lib/types';
@@ -373,33 +374,7 @@
 							>
 							>
 								{#if STT_WHISPER_MODEL_LOADING}
 								{#if STT_WHISPER_MODEL_LOADING}
 									<div class="self-center">
 									<div class="self-center">
-										<svg
-											class=" w-4 h-4"
-											viewBox="0 0 24 24"
-											fill="currentColor"
-											xmlns="http://www.w3.org/2000/svg"
-										>
-											<style>
-												.spinner_ajPY {
-													transform-origin: center;
-													animation: spinner_AtaB 0.75s infinite linear;
-												}
-
-												@keyframes spinner_AtaB {
-													100% {
-														transform: rotate(360deg);
-													}
-												}
-											</style>
-											<path
-												d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-												opacity=".25"
-											/>
-											<path
-												d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-												class="spinner_ajPY"
-											/>
-										</svg>
+										<Spinner />
 									</div>
 									</div>
 								{:else}
 								{:else}
 									<svg
 									<svg

+ 2 - 10
src/lib/components/admin/Settings/Connections/ManageOllamaModal.svelte

@@ -5,6 +5,7 @@
 
 
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import ManageOllama from '../Models/Manage/ManageOllama.svelte';
 	import ManageOllama from '../Models/Manage/ManageOllama.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	export let show = false;
 	export let show = false;
 	export let urlIdx: number | null = null;
 	export let urlIdx: number | null = null;
@@ -26,16 +27,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 

+ 2 - 28
src/lib/components/admin/Settings/Documents.svelte

@@ -808,33 +808,7 @@
 											>
 											>
 												{#if updateEmbeddingModelLoading}
 												{#if updateEmbeddingModelLoading}
 													<div class="self-center">
 													<div class="self-center">
-														<svg
-															class=" w-4 h-4"
-															viewBox="0 0 24 24"
-															fill="currentColor"
-															xmlns="http://www.w3.org/2000/svg"
-														>
-															<style>
-																.spinner_ajPY {
-																	transform-origin: center;
-																	animation: spinner_AtaB 0.75s infinite linear;
-																}
-
-																@keyframes spinner_AtaB {
-																	100% {
-																		transform: rotate(360deg);
-																	}
-																}
-															</style>
-															<path
-																d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-																opacity=".25"
-															/>
-															<path
-																d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-																class="spinner_ajPY"
-															/>
-														</svg>
+														<Spinner />
 													</div>
 													</div>
 												{:else}
 												{:else}
 													<svg
 													<svg
@@ -1272,7 +1246,7 @@
 		</div>
 		</div>
 	{:else}
 	{:else}
 		<div class="flex items-center justify-center h-full">
 		<div class="flex items-center justify-center h-full">
-			<Spinner />
+			<Spinner className="size-5" />
 		</div>
 		</div>
 	{/if}
 	{/if}
 </form>
 </form>

+ 4 - 33
src/lib/components/admin/Settings/Evaluations/ArenaModelModal.svelte

@@ -3,6 +3,7 @@
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
 
 
+	import Spinner from '$lib/components/common/Spinner.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import { models } from '$lib/stores';
 	import { models } from '$lib/stores';
 	import Plus from '$lib/components/icons/Plus.svelte';
 	import Plus from '$lib/components/icons/Plus.svelte';
@@ -11,6 +12,7 @@
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
 	import AccessControl from '$lib/components/workspace/common/AccessControl.svelte';
 	import AccessControl from '$lib/components/workspace/common/AccessControl.svelte';
 	import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
 	import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	export let show = false;
 	export let show = false;
 	export let edit = false;
 	export let edit = false;
@@ -141,16 +143,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 
@@ -406,29 +399,7 @@
 
 
 							{#if loading}
 							{#if loading}
 								<div class="ml-2 self-center">
 								<div class="ml-2 self-center">
-									<svg
-										class=" w-4 h-4"
-										viewBox="0 0 24 24"
-										fill="currentColor"
-										xmlns="http://www.w3.org/2000/svg"
-										><style>
-											.spinner_ajPY {
-												transform-origin: center;
-												animation: spinner_AtaB 0.75s infinite linear;
-											}
-											@keyframes spinner_AtaB {
-												100% {
-													transform: rotate(360deg);
-												}
-											}
-										</style><path
-											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-											opacity=".25"
-										/><path
-											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-											class="spinner_ajPY"
-										/></svg
-									>
+									<Spinner />
 								</div>
 								</div>
 							{/if}
 							{/if}
 						</button>
 						</button>

+ 2 - 23
src/lib/components/admin/Settings/Images.svelte

@@ -13,6 +13,7 @@
 		updateConfig,
 		updateConfig,
 		verifyConfigUrl
 		verifyConfigUrl
 	} from '$lib/apis/images';
 	} from '$lib/apis/images';
+	import Spinner from '$lib/components/common/Spinner.svelte';
 	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
 	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
 	import Switch from '$lib/components/common/Switch.svelte';
 	import Switch from '$lib/components/common/Switch.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
@@ -711,29 +712,7 @@
 
 
 			{#if loading}
 			{#if loading}
 				<div class="ml-2 self-center">
 				<div class="ml-2 self-center">
-					<svg
-						class=" w-4 h-4"
-						viewBox="0 0 24 24"
-						fill="currentColor"
-						xmlns="http://www.w3.org/2000/svg"
-						><style>
-							.spinner_ajPY {
-								transform-origin: center;
-								animation: spinner_AtaB 0.75s infinite linear;
-							}
-							@keyframes spinner_AtaB {
-								100% {
-									transform: rotate(360deg);
-								}
-							}
-						</style><path
-							d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-							opacity=".25"
-						/><path
-							d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-							class="spinner_ajPY"
-						/></svg
-					>
+					<Spinner />
 				</div>
 				</div>
 			{/if}
 			{/if}
 		</button>
 		</button>

+ 1 - 1
src/lib/components/admin/Settings/Interface.svelte

@@ -636,6 +636,6 @@
 	</form>
 	</form>
 {:else}
 {:else}
 	<div class=" h-full w-full flex justify-center items-center">
 	<div class=" h-full w-full flex justify-center items-center">
-		<Spinner />
+		<Spinner className="size-5" />
 	</div>
 	</div>
 {/if}
 {/if}

+ 2 - 10
src/lib/components/admin/Settings/Interface/Banners.svelte

@@ -2,6 +2,7 @@
 	import Switch from '$lib/components/common/Switch.svelte';
 	import Switch from '$lib/components/common/Switch.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import EllipsisVertical from '$lib/components/icons/EllipsisVertical.svelte';
 	import EllipsisVertical from '$lib/components/icons/EllipsisVertical.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 	import Sortable from 'sortablejs';
 	import Sortable from 'sortablejs';
 	import { getContext } from 'svelte';
 	import { getContext } from 'svelte';
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
@@ -85,16 +86,7 @@
 					banners = banners;
 					banners = banners;
 				}}
 				}}
 			>
 			>
-				<svg
-					xmlns="http://www.w3.org/2000/svg"
-					viewBox="0 0 20 20"
-					fill="currentColor"
-					class="w-4 h-4"
-				>
-					<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>
+				<XMark className={'size-4'} />
 			</button>
 			</button>
 		</div>
 		</div>
 	{/each}
 	{/each}

+ 1 - 1
src/lib/components/admin/Settings/Models.svelte

@@ -563,6 +563,6 @@
 	{/if}
 	{/if}
 {:else}
 {:else}
 	<div class=" h-full w-full flex justify-center items-center">
 	<div class=" h-full w-full flex justify-center items-center">
-		<Spinner />
+		<Spinner className="size-5" />
 	</div>
 	</div>
 {/if}
 {/if}

+ 4 - 34
src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte

@@ -18,6 +18,7 @@
 	import Plus from '$lib/components/icons/Plus.svelte';
 	import Plus from '$lib/components/icons/Plus.svelte';
 	import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
 	import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
 	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
 	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	export let show = false;
 	export let show = false;
 	export let initHandler = () => {};
 	export let initHandler = () => {};
@@ -129,16 +130,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 
@@ -278,29 +270,7 @@
 
 
 								{#if loading}
 								{#if loading}
 									<div class="ml-2 self-center">
 									<div class="ml-2 self-center">
-										<svg
-											class=" w-4 h-4"
-											viewBox="0 0 24 24"
-											fill="currentColor"
-											xmlns="http://www.w3.org/2000/svg"
-											><style>
-												.spinner_ajPY {
-													transform-origin: center;
-													animation: spinner_AtaB 0.75s infinite linear;
-												}
-												@keyframes spinner_AtaB {
-													100% {
-														transform: rotate(360deg);
-													}
-												}
-											</style><path
-												d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-												opacity=".25"
-											/><path
-												d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-												class="spinner_ajPY"
-											/></svg
-										>
+										<Spinner />
 									</div>
 									</div>
 								{/if}
 								{/if}
 							</button>
 							</button>
@@ -308,7 +278,7 @@
 					</form>
 					</form>
 				{:else}
 				{:else}
 					<div>
 					<div>
-						<Spinner />
+						<Spinner className="size-5" />
 					</div>
 					</div>
 				{/if}
 				{/if}
 			</div>
 			</div>

+ 1 - 1
src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte

@@ -1057,6 +1057,6 @@
 	</div>
 	</div>
 {:else}
 {:else}
 	<div class="flex justify-center items-center w-full h-full py-3">
 	<div class="flex justify-center items-center w-full h-full py-3">
-		<Spinner />
+		<Spinner className="size-5" />
 	</div>
 	</div>
 {/if}
 {/if}

+ 2 - 10
src/lib/components/admin/Settings/Models/ManageModelsModal.svelte

@@ -7,6 +7,7 @@
 
 
 	import { user } from '$lib/stores';
 	import { user } from '$lib/stores';
 
 
+	import XMark from '$lib/components/icons/XMark.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import ManageOllama from './Manage/ManageOllama.svelte';
 	import ManageOllama from './Manage/ManageOllama.svelte';
 	import { getOllamaConfig } from '$lib/apis/ollama';
 	import { getOllamaConfig } from '$lib/apis/ollama';
@@ -48,16 +49,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 

+ 0 - 1
src/lib/components/admin/Settings/WebSearch.svelte

@@ -248,7 +248,6 @@
 										bind:value={webConfig.KAGI_SEARCH_API_KEY}
 										bind:value={webConfig.KAGI_SEARCH_API_KEY}
 									/>
 									/>
 								</div>
 								</div>
-								.
 							</div>
 							</div>
 						{:else if webConfig.WEB_SEARCH_ENGINE === 'mojeek'}
 						{:else if webConfig.WEB_SEARCH_ENGINE === 'mojeek'}
 							<div class="mb-2.5 flex w-full flex-col">
 							<div class="mb-2.5 flex w-full flex-col">

+ 2 - 12
src/lib/components/admin/Users/Groups.svelte

@@ -16,6 +16,7 @@
 	import UsersSolid from '$lib/components/icons/UsersSolid.svelte';
 	import UsersSolid from '$lib/components/icons/UsersSolid.svelte';
 	import ChevronRight from '$lib/components/icons/ChevronRight.svelte';
 	import ChevronRight from '$lib/components/icons/ChevronRight.svelte';
 	import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte';
 	import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte';
+	import Search from '$lib/components/icons/Search.svelte';
 	import User from '$lib/components/icons/User.svelte';
 	import User from '$lib/components/icons/User.svelte';
 	import UserCircleSolid from '$lib/components/icons/UserCircleSolid.svelte';
 	import UserCircleSolid from '$lib/components/icons/UserCircleSolid.svelte';
 	import GroupModal from './Groups/EditGroupModal.svelte';
 	import GroupModal from './Groups/EditGroupModal.svelte';
@@ -159,18 +160,7 @@
 			<div class=" flex w-full space-x-2">
 			<div class=" flex w-full space-x-2">
 				<div class="flex flex-1">
 				<div class="flex flex-1">
 					<div class=" self-center ml-1 mr-3">
 					<div class=" self-center ml-1 mr-3">
-						<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>
+						<Search />
 					</div>
 					</div>
 					<input
 					<input
 						class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-hidden bg-transparent"
 						class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-hidden bg-transparent"

+ 4 - 33
src/lib/components/admin/Users/Groups/AddGroupModal.svelte

@@ -3,8 +3,10 @@
 	import { getContext, onMount } from 'svelte';
 	import { getContext, onMount } from 'svelte';
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
+	import Spinner from '$lib/components/common/Spinner.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Textarea from '$lib/components/common/Textarea.svelte';
 	import Textarea from '$lib/components/common/Textarea.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 	export let onSubmit: Function = () => {};
 	export let onSubmit: Function = () => {};
 	export let show = false;
 	export let show = false;
 
 
@@ -45,16 +47,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 
@@ -111,29 +104,7 @@
 
 
 							{#if loading}
 							{#if loading}
 								<div class="ml-2 self-center">
 								<div class="ml-2 self-center">
-									<svg
-										class=" w-4 h-4"
-										viewBox="0 0 24 24"
-										fill="currentColor"
-										xmlns="http://www.w3.org/2000/svg"
-										><style>
-											.spinner_ajPY {
-												transform-origin: center;
-												animation: spinner_AtaB 0.75s infinite linear;
-											}
-											@keyframes spinner_AtaB {
-												100% {
-													transform: rotate(360deg);
-												}
-											}
-										</style><path
-											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-											opacity=".25"
-										/><path
-											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-											class="spinner_ajPY"
-										/></svg
-									>
+									<Spinner />
 								</div>
 								</div>
 							{/if}
 							{/if}
 						</button>
 						</button>

+ 4 - 33
src/lib/components/admin/Users/Groups/EditGroupModal.svelte

@@ -3,6 +3,7 @@
 	import { getContext, onMount } from 'svelte';
 	import { getContext, onMount } from 'svelte';
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
+	import Spinner from '$lib/components/common/Spinner.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Display from './Display.svelte';
 	import Display from './Display.svelte';
 	import Permissions from './Permissions.svelte';
 	import Permissions from './Permissions.svelte';
@@ -10,6 +11,7 @@
 	import UserPlusSolid from '$lib/components/icons/UserPlusSolid.svelte';
 	import UserPlusSolid from '$lib/components/icons/UserPlusSolid.svelte';
 	import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte';
 	import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte';
 	import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
 	import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	export let onSubmit: Function = () => {};
 	export let onSubmit: Function = () => {};
 	export let onDelete: Function = () => {};
 	export let onDelete: Function = () => {};
@@ -124,16 +126,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 
@@ -305,29 +298,7 @@
 
 
 							{#if loading}
 							{#if loading}
 								<div class="ml-2 self-center">
 								<div class="ml-2 self-center">
-									<svg
-										class=" w-4 h-4"
-										viewBox="0 0 24 24"
-										fill="currentColor"
-										xmlns="http://www.w3.org/2000/svg"
-										><style>
-											.spinner_ajPY {
-												transform-origin: center;
-												animation: spinner_AtaB 0.75s infinite linear;
-											}
-											@keyframes spinner_AtaB {
-												100% {
-													transform: rotate(360deg);
-												}
-											}
-										</style><path
-											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-											opacity=".25"
-										/><path
-											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-											class="spinner_ajPY"
-										/></svg
-									>
+									<Spinner />
 								</div>
 								</div>
 							{/if}
 							{/if}
 						</button>
 						</button>

+ 2 - 12
src/lib/components/admin/Users/Groups/Users.svelte

@@ -7,6 +7,7 @@
 	import { WEBUI_BASE_URL } from '$lib/constants';
 	import { WEBUI_BASE_URL } from '$lib/constants';
 	import Checkbox from '$lib/components/common/Checkbox.svelte';
 	import Checkbox from '$lib/components/common/Checkbox.svelte';
 	import Badge from '$lib/components/common/Badge.svelte';
 	import Badge from '$lib/components/common/Badge.svelte';
+	import Search from '$lib/components/icons/Search.svelte';
 
 
 	export let users = [];
 	export let users = [];
 	export let userIds = [];
 	export let userIds = [];
@@ -50,18 +51,7 @@
 	<div class="flex w-full">
 	<div class="flex w-full">
 		<div class="flex flex-1">
 		<div class="flex flex-1">
 			<div class=" self-center mr-3">
 			<div class=" self-center mr-3">
-				<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>
+				<Search />
 			</div>
 			</div>
 			<input
 			<input
 				class=" w-full text-sm pr-4 rounded-r-xl outline-hidden bg-transparent"
 				class=" w-full text-sm pr-4 rounded-r-xl outline-hidden bg-transparent"

+ 1 - 1
src/lib/components/admin/Users/UserList.svelte

@@ -151,7 +151,7 @@
 
 
 {#if users === null || total === null}
 {#if users === null || total === null}
 	<div class="my-10">
 	<div class="my-10">
-		<Spinner />
+		<Spinner className="size-5" />
 	</div>
 	</div>
 {:else}
 {:else}
 	<div class="mt-0.5 mb-2 gap-1 flex flex-col md:flex-row justify-between">
 	<div class="mt-0.5 mb-2 gap-1 flex flex-col md:flex-row justify-between">

+ 4 - 33
src/lib/components/admin/Users/UserList/AddUserModal.svelte

@@ -6,8 +6,10 @@
 
 
 	import { WEBUI_BASE_URL } from '$lib/constants';
 	import { WEBUI_BASE_URL } from '$lib/constants';
 
 
+	import Spinner from '$lib/components/common/Spinner.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import { generateInitialsImage } from '$lib/utils';
 	import { generateInitialsImage } from '$lib/utils';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
@@ -132,16 +134,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 
@@ -293,29 +286,7 @@
 
 
 							{#if loading}
 							{#if loading}
 								<div class="ml-2 self-center">
 								<div class="ml-2 self-center">
-									<svg
-										class=" w-4 h-4"
-										viewBox="0 0 24 24"
-										fill="currentColor"
-										xmlns="http://www.w3.org/2000/svg"
-										><style>
-											.spinner_ajPY {
-												transform-origin: center;
-												animation: spinner_AtaB 0.75s infinite linear;
-											}
-											@keyframes spinner_AtaB {
-												100% {
-													transform: rotate(360deg);
-												}
-											}
-										</style><path
-											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-											opacity=".25"
-										/><path
-											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-											class="spinner_ajPY"
-										/></svg
-									>
+									<Spinner />
 								</div>
 								</div>
 							{/if}
 							{/if}
 						</button>
 						</button>

+ 2 - 10
src/lib/components/admin/Users/UserList/EditUserModal.svelte

@@ -8,6 +8,7 @@
 
 
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import localizedFormat from 'dayjs/plugin/localizedFormat';
 	import localizedFormat from 'dayjs/plugin/localizedFormat';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
@@ -54,16 +55,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 

+ 1 - 1
src/lib/components/chat/Chat.svelte

@@ -2227,7 +2227,7 @@
 	{:else if loading}
 	{:else if loading}
 		<div class=" flex items-center justify-center h-full w-full">
 		<div class=" flex items-center justify-center h-full w-full">
 			<div class="m-auto">
 			<div class="m-auto">
-				<Spinner />
+				<Spinner className="size-5" />
 			</div>
 			</div>
 		</div>
 		</div>
 	{/if}
 	{/if}

+ 1 - 0
src/lib/components/chat/MessageInput/CallOverlay.svelte

@@ -854,6 +854,7 @@
 				</button>
 				</button>
 			{:else}
 			{:else}
 				<div class="relative flex video-container w-full max-h-full pt-2 pb-4 md:py-6 px-2 h-full">
 				<div class="relative flex video-container w-full max-h-full pt-2 pb-4 md:py-6 px-2 h-full">
+					<!-- svelte-ignore a11y-media-has-caption -->
 					<video
 					<video
 						id="camera-feed"
 						id="camera-feed"
 						autoplay
 						autoplay

+ 2 - 10
src/lib/components/chat/MessageInput/VoiceRecording.svelte

@@ -5,6 +5,7 @@
 	import { blobToFile, calculateSHA256, extractCurlyBraceWords } from '$lib/utils';
 	import { blobToFile, calculateSHA256, extractCurlyBraceWords } from '$lib/utils';
 
 
 	import { transcribeAudio } from '$lib/apis/audio';
 	import { transcribeAudio } from '$lib/apis/audio';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	import dayjs from 'dayjs';
 	import dayjs from 'dayjs';
 	import LocalizedFormat from 'dayjs/plugin/localizedFormat';
 	import LocalizedFormat from 'dayjs/plugin/localizedFormat';
@@ -406,16 +407,7 @@
 				onCancel();
 				onCancel();
 			}}
 			}}
 		>
 		>
-			<svg
-				xmlns="http://www.w3.org/2000/svg"
-				fill="none"
-				viewBox="0 0 24 24"
-				stroke-width="3"
-				stroke="currentColor"
-				class="size-4"
-			>
-				<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
-			</svg>
+			<XMark className={'size-4'} />
 		</button>
 		</button>
 	</div>
 	</div>
 
 

+ 1 - 0
src/lib/components/chat/Messages.svelte

@@ -438,6 +438,7 @@
 						<Message
 						<Message
 							{chatId}
 							{chatId}
 							bind:history
 							bind:history
+							{selectedModels}
 							messageId={message.id}
 							messageId={message.id}
 							idx={messageIdx}
 							idx={messageIdx}
 							{user}
 							{user}

+ 3 - 10
src/lib/components/chat/Messages/CitationsModal.svelte

@@ -4,6 +4,8 @@
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import { WEBUI_API_BASE_URL } from '$lib/constants';
 	import { WEBUI_API_BASE_URL } from '$lib/constants';
 
 
+	import XMark from '$lib/components/icons/XMark.svelte';
+
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
 	export let show = false;
 	export let show = false;
@@ -67,16 +69,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 

+ 2 - 10
src/lib/components/chat/Messages/CodeExecutionModal.svelte

@@ -4,6 +4,7 @@
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Spinner from '$lib/components/common/Spinner.svelte';
 	import Spinner from '$lib/components/common/Spinner.svelte';
 	import Badge from '$lib/components/common/Badge.svelte';
 	import Badge from '$lib/components/common/Badge.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
 	export let show = false;
 	export let show = false;
@@ -49,16 +50,7 @@
 					codeExecution = null;
 					codeExecution = null;
 				}}
 				}}
 			>
 			>
-				<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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 

+ 16 - 6
src/lib/components/chat/Messages/ContentRenderer.svelte

@@ -18,6 +18,8 @@
 	export let id;
 	export let id;
 	export let content;
 	export let content;
 	export let history;
 	export let history;
+	export let selectedModels = [];
+
 	export let model = null;
 	export let model = null;
 	export let sources = null;
 	export let sources = null;
 
 
@@ -25,11 +27,10 @@
 	export let preview = false;
 	export let preview = false;
 	export let floatingButtons = true;
 	export let floatingButtons = true;
 
 
-	export let onSave = () => {};
-	export let onSourceClick = () => {};
-	export let onTaskClick = () => {};
-
-	export let onAddMessages = () => {};
+	export let onSave = (e) => {};
+	export let onSourceClick = (e) => {};
+	export let onTaskClick = (e) => {};
+	export let onAddMessages = (e) => {};
 
 
 	let contentContainerElement;
 	let contentContainerElement;
 
 
@@ -125,6 +126,11 @@
 	});
 	});
 </script>
 </script>
 
 
+{(selectedModels ?? []).includes(model?.id)
+	? model?.id
+	: (selectedModels ?? []).length > 0
+		? selectedModels.at(0)
+		: model?.id}
 <div bind:this={contentContainerElement}>
 <div bind:this={contentContainerElement}>
 	<Markdown
 	<Markdown
 		{id}
 		{id}
@@ -192,7 +198,11 @@
 	<FloatingButtons
 	<FloatingButtons
 		bind:this={floatingButtonsElement}
 		bind:this={floatingButtonsElement}
 		{id}
 		{id}
-		model={model?.id}
+		model={(selectedModels ?? []).includes(model?.id)
+			? model?.id
+			: (selectedModels ?? []).length > 0
+				? selectedModels.at(0)
+				: model?.id}
 		messages={createMessagesList(history, id)}
 		messages={createMessagesList(history, id)}
 		onAdd={({ modelId, parentId, messages }) => {
 		onAdd={({ modelId, parentId, messages }) => {
 			console.log(modelId, parentId, messages);
 			console.log(modelId, parentId, messages);

+ 3 - 0
src/lib/components/chat/Messages/Message.svelte

@@ -13,6 +13,7 @@
 	import UserMessage from './UserMessage.svelte';
 	import UserMessage from './UserMessage.svelte';
 
 
 	export let chatId;
 	export let chatId;
+	export let selectedModels = [];
 	export let idx = 0;
 	export let idx = 0;
 
 
 	export let history;
 	export let history;
@@ -70,6 +71,7 @@
 				{chatId}
 				{chatId}
 				{history}
 				{history}
 				{messageId}
 				{messageId}
+				{selectedModels}
 				isLastMessage={messageId === history.currentId}
 				isLastMessage={messageId === history.currentId}
 				siblings={history.messages[history.messages[messageId].parentId]?.childrenIds ?? []}
 				siblings={history.messages[history.messages[messageId].parentId]?.childrenIds ?? []}
 				{gotoMessage}
 				{gotoMessage}
@@ -92,6 +94,7 @@
 				bind:history
 				bind:history
 				{chatId}
 				{chatId}
 				{messageId}
 				{messageId}
+				{selectedModels}
 				isLastMessage={messageId === history?.currentId}
 				isLastMessage={messageId === history?.currentId}
 				{updateChat}
 				{updateChat}
 				{editMessage}
 				{editMessage}

+ 2 - 0
src/lib/components/chat/Messages/MultiResponseMessages.svelte

@@ -23,6 +23,7 @@
 	export let chatId;
 	export let chatId;
 	export let history;
 	export let history;
 	export let messageId;
 	export let messageId;
+	export let selectedModels = [];
 
 
 	export let isLastMessage;
 	export let isLastMessage;
 	export let readOnly = false;
 	export let readOnly = false;
@@ -252,6 +253,7 @@
 									{chatId}
 									{chatId}
 									{history}
 									{history}
 									messageId={_messageId}
 									messageId={_messageId}
+									{selectedModels}
 									isLastMessage={true}
 									isLastMessage={true}
 									siblings={groupedMessageIds[modelIdx].messageIds}
 									siblings={groupedMessageIds[modelIdx].messageIds}
 									gotoMessage={(message, messageIdx) => gotoMessage(modelIdx, messageIdx)}
 									gotoMessage={(message, messageIdx) => gotoMessage(modelIdx, messageIdx)}

+ 2 - 10
src/lib/components/chat/Messages/RateComment.svelte

@@ -4,6 +4,7 @@
 	import { createEventDispatcher, onMount, getContext } from 'svelte';
 	import { createEventDispatcher, onMount, getContext } from 'svelte';
 	import { config, models } from '$lib/stores';
 	import { config, models } from '$lib/stores';
 	import Tags from '$lib/components/common/Tags.svelte';
 	import Tags from '$lib/components/common/Tags.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
@@ -123,16 +124,7 @@
 				show = false;
 				show = false;
 			}}
 			}}
 		>
 		>
-			<svg
-				xmlns="http://www.w3.org/2000/svg"
-				fill="none"
-				viewBox="0 0 24 24"
-				stroke-width="1.5"
-				stroke="currentColor"
-				class="size-4"
-			>
-				<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
-			</svg>
+			<XMark className={'size-4'} />
 		</button>
 		</button>
 	</div>
 	</div>
 
 

+ 2 - 0
src/lib/components/chat/Messages/ResponseMessage.svelte

@@ -106,6 +106,7 @@
 	export let chatId = '';
 	export let chatId = '';
 	export let history;
 	export let history;
 	export let messageId;
 	export let messageId;
+	export let selectedModels = [];
 
 
 	let message: MessageType = JSON.parse(JSON.stringify(history.messages[messageId]));
 	let message: MessageType = JSON.parse(JSON.stringify(history.messages[messageId]));
 	$: if (history.messages) {
 	$: if (history.messages) {
@@ -795,6 +796,7 @@
 									<ContentRenderer
 									<ContentRenderer
 										id={message.id}
 										id={message.id}
 										{history}
 										{history}
+										{selectedModels}
 										content={message.content}
 										content={message.content}
 										sources={message.sources}
 										sources={message.sources}
 										floatingButtons={message?.done && !readOnly}
 										floatingButtons={message?.done && !readOnly}

+ 2 - 2
src/lib/components/chat/Messages/ResponseMessage/WebSearchResults.svelte

@@ -1,7 +1,7 @@
 <script lang="ts">
 <script lang="ts">
 	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
 	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
 	import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
 	import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
-	import MagnifyingGlass from '$lib/components/icons/MagnifyingGlass.svelte';
+	import Search from '$lib/components/icons/Search.svelte';
 	import Collapsible from '$lib/components/common/Collapsible.svelte';
 	import Collapsible from '$lib/components/common/Collapsible.svelte';
 
 
 	export let status = { urls: [], query: '' };
 	export let status = { urls: [], query: '' };
@@ -31,7 +31,7 @@
 				class="flex w-full items-center p-3 px-4 border-b border-gray-300/30 dark:border-gray-700/50 group/item justify-between font-normal text-gray-800 dark:text-gray-300 no-underline"
 				class="flex w-full items-center p-3 px-4 border-b border-gray-300/30 dark:border-gray-700/50 group/item justify-between font-normal text-gray-800 dark:text-gray-300 no-underline"
 			>
 			>
 				<div class="flex gap-2 items-center">
 				<div class="flex gap-2 items-center">
-					<MagnifyingGlass />
+					<Search />
 
 
 					<div class=" line-clamp-1">
 					<div class=" line-clamp-1">
 						{status.query}
 						{status.query}

+ 2 - 23
src/lib/components/chat/ModelSelector/Selector.svelte

@@ -7,6 +7,7 @@
 	import relativeTime from 'dayjs/plugin/relativeTime';
 	import relativeTime from 'dayjs/plugin/relativeTime';
 	dayjs.extend(relativeTime);
 	dayjs.extend(relativeTime);
 
 
+	import Spinner from '$lib/components/common/Spinner.svelte';
 	import { flyAndScale } from '$lib/utils/transitions';
 	import { flyAndScale } from '$lib/utils/transitions';
 	import { createEventDispatcher, onMount, getContext, tick } from 'svelte';
 	import { createEventDispatcher, onMount, getContext, tick } from 'svelte';
 	import { goto } from '$app/navigation';
 	import { goto } from '$app/navigation';
@@ -550,29 +551,7 @@
 					>
 					>
 						<div class="flex">
 						<div class="flex">
 							<div class="-ml-2 mr-2.5 translate-y-0.5">
 							<div class="-ml-2 mr-2.5 translate-y-0.5">
-								<svg
-									class="size-4"
-									viewBox="0 0 24 24"
-									fill="currentColor"
-									xmlns="http://www.w3.org/2000/svg"
-									><style>
-										.spinner_ajPY {
-											transform-origin: center;
-											animation: spinner_AtaB 0.75s infinite linear;
-										}
-										@keyframes spinner_AtaB {
-											100% {
-												transform: rotate(360deg);
-											}
-										}
-									</style><path
-										d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-										opacity=".25"
-									/><path
-										d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-										class="spinner_ajPY"
-									/></svg
-								>
+								<Spinner />
 							</div>
 							</div>
 
 
 							<div class="flex flex-col self-start">
 							<div class="flex flex-col self-start">

+ 4 - 33
src/lib/components/chat/Settings/Personalization/AddMemoryModal.svelte

@@ -4,6 +4,8 @@
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import { addNewMemory, updateMemoryById } from '$lib/apis/memories';
 	import { addNewMemory, updateMemoryById } from '$lib/apis/memories';
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
 
 
@@ -46,16 +48,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 
@@ -93,29 +86,7 @@
 
 
 							{#if loading}
 							{#if loading}
 								<div class="ml-2 self-center">
 								<div class="ml-2 self-center">
-									<svg
-										class=" w-4 h-4"
-										viewBox="0 0 24 24"
-										fill="currentColor"
-										xmlns="http://www.w3.org/2000/svg"
-										><style>
-											.spinner_ajPY {
-												transform-origin: center;
-												animation: spinner_AtaB 0.75s infinite linear;
-											}
-											@keyframes spinner_AtaB {
-												100% {
-													transform: rotate(360deg);
-												}
-											}
-										</style><path
-											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-											opacity=".25"
-										/><path
-											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-											class="spinner_ajPY"
-										/></svg
-									>
+									<Spinner />
 								</div>
 								</div>
 							{/if}
 							{/if}
 						</button>
 						</button>

+ 4 - 33
src/lib/components/chat/Settings/Personalization/EditMemoryModal.svelte

@@ -4,7 +4,9 @@
 
 
 	import { updateMemoryById } from '$lib/apis/memories';
 	import { updateMemoryById } from '$lib/apis/memories';
 
 
+	import Spinner from '$lib/components/common/Spinner.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
 
 
@@ -56,16 +58,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 
@@ -103,29 +96,7 @@
 
 
 							{#if loading}
 							{#if loading}
 								<div class="ml-2 self-center">
 								<div class="ml-2 self-center">
-									<svg
-										class=" w-4 h-4"
-										viewBox="0 0 24 24"
-										fill="currentColor"
-										xmlns="http://www.w3.org/2000/svg"
-										><style>
-											.spinner_ajPY {
-												transform-origin: center;
-												animation: spinner_AtaB 0.75s infinite linear;
-											}
-											@keyframes spinner_AtaB {
-												100% {
-													transform: rotate(360deg);
-												}
-											}
-										</style><path
-											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-											opacity=".25"
-										/><path
-											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-											class="spinner_ajPY"
-										/></svg
-									>
+									<Spinner />
 								</div>
 								</div>
 							{/if}
 							{/if}
 						</button>
 						</button>

+ 2 - 10
src/lib/components/chat/ShareChatModal.svelte

@@ -8,6 +8,7 @@
 
 
 	import Modal from '../common/Modal.svelte';
 	import Modal from '../common/Modal.svelte';
 	import Link from '../icons/Link.svelte';
 	import Link from '../icons/Link.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	export let chatId;
 	export let chatId;
 
 
@@ -90,16 +91,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 

+ 2 - 10
src/lib/components/chat/ShortcutsModal.svelte

@@ -4,6 +4,7 @@
 
 
 	import Tooltip from '../common/Tooltip.svelte';
 	import Tooltip from '../common/Tooltip.svelte';
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	export let show = false;
 	export let show = false;
 </script>
 </script>
@@ -18,16 +19,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 

+ 2 - 10
src/lib/components/chat/ToolServersModal.svelte

@@ -9,6 +9,7 @@
 	import Modal from '../common/Modal.svelte';
 	import Modal from '../common/Modal.svelte';
 	import Link from '../icons/Link.svelte';
 	import Link from '../icons/Link.svelte';
 	import Collapsible from '../common/Collapsible.svelte';
 	import Collapsible from '../common/Collapsible.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	export let show = false;
 	export let show = false;
 	export let selectedToolIds = [];
 	export let selectedToolIds = [];
@@ -30,16 +31,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 

+ 2 - 10
src/lib/components/common/FileItem.svelte

@@ -6,6 +6,7 @@
 	import GarbageBin from '../icons/GarbageBin.svelte';
 	import GarbageBin from '../icons/GarbageBin.svelte';
 	import Spinner from './Spinner.svelte';
 	import Spinner from './Spinner.svelte';
 	import Tooltip from './Tooltip.svelte';
 	import Tooltip from './Tooltip.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
@@ -133,16 +134,7 @@
 					dispatch('dismiss');
 					dispatch('dismiss');
 				}}
 				}}
 			>
 			>
-				<svg
-					xmlns="http://www.w3.org/2000/svg"
-					viewBox="0 0 20 20"
-					fill="currentColor"
-					class="w-4 h-4"
-				>
-					<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>
+				<XMark className={'size-4'} />
 			</button>
 			</button>
 
 
 			<!-- <button
 			<!-- <button

+ 2 - 10
src/lib/components/common/Image.svelte

@@ -1,6 +1,7 @@
 <script lang="ts">
 <script lang="ts">
 	import { WEBUI_BASE_URL } from '$lib/constants';
 	import { WEBUI_BASE_URL } from '$lib/constants';
 	import ImagePreview from './ImagePreview.svelte';
 	import ImagePreview from './ImagePreview.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	export let src = '';
 	export let src = '';
 	export let alt = '';
 	export let alt = '';
@@ -39,16 +40,7 @@
 					onDismiss();
 					onDismiss();
 				}}
 				}}
 			>
 			>
-				<svg
-					xmlns="http://www.w3.org/2000/svg"
-					viewBox="0 0 20 20"
-					fill="currentColor"
-					class="size-4"
-				>
-					<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>
+				<XMark className={'size-4'} />
 			</button>
 			</button>
 		</div>
 		</div>
 	{/if}
 	{/if}

+ 3 - 10
src/lib/components/common/ImagePreview.svelte

@@ -5,6 +5,8 @@
 	import fileSaver from 'file-saver';
 	import fileSaver from 'file-saver';
 	const { saveAs } = fileSaver;
 	const { saveAs } = fileSaver;
 
 
+	import XMark from '$lib/components/icons/XMark.svelte';
+
 	export let show = false;
 	export let show = false;
 	export let src = '';
 	export let src = '';
 	export let alt = '';
 	export let alt = '';
@@ -82,16 +84,7 @@
 						show = false;
 						show = false;
 					}}
 					}}
 				>
 				>
-					<svg
-						xmlns="http://www.w3.org/2000/svg"
-						fill="none"
-						viewBox="0 0 24 24"
-						stroke-width="2"
-						stroke="currentColor"
-						class="w-6 h-6"
-					>
-						<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
-					</svg>
+					<XMark className={'size-6'} />
 				</button>
 				</button>
 			</div>
 			</div>
 
 

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

@@ -74,6 +74,7 @@
 {#if show}
 {#if show}
 	<!-- svelte-ignore a11y-click-events-have-key-events -->
 	<!-- svelte-ignore a11y-click-events-have-key-events -->
 	<!-- svelte-ignore a11y-no-static-element-interactions -->
 	<!-- svelte-ignore a11y-no-static-element-interactions -->
+	<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
 	<div
 	<div
 		bind:this={modalElement}
 		bind:this={modalElement}
 		aria-modal="true"
 		aria-modal="true"

+ 7 - 2
src/lib/components/common/Spinner.svelte

@@ -1,9 +1,14 @@
 <script lang="ts">
 <script lang="ts">
-	export let className: string = 'size-5';
+	export let className: string = 'size-4';
 </script>
 </script>
 
 
 <div class="flex justify-center text-center">
 <div class="flex justify-center text-center">
-	<svg class={className} viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"
+	<svg
+		aria-hidden="true"
+		class={className}
+		viewBox="0 0 24 24"
+		fill="currentColor"
+		xmlns="http://www.w3.org/2000/svg"
 		><style>
 		><style>
 			.spinner_ajPY {
 			.spinner_ajPY {
 				transform-origin: center;
 				transform-origin: center;

+ 0 - 19
src/lib/components/icons/MagnifyingGlass.svelte

@@ -1,19 +0,0 @@
-<script lang="ts">
-	export let className = 'size-4';
-	export let strokeWidth = '2';
-</script>
-
-<svg
-	xmlns="http://www.w3.org/2000/svg"
-	fill="none"
-	viewBox="0 0 24 24"
-	stroke-width={strokeWidth}
-	stroke="currentColor"
-	class={className}
->
-	<path
-		stroke-linecap="round"
-		stroke-linejoin="round"
-		d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"
-	/>
-</svg>

+ 6 - 4
src/lib/components/icons/XMark.svelte

@@ -5,12 +5,14 @@
 
 
 <svg
 <svg
 	xmlns="http://www.w3.org/2000/svg"
 	xmlns="http://www.w3.org/2000/svg"
-	fill="none"
-	viewBox="0 0 24 24"
+	viewBox="0 0 20 20"
+	fill="currentColor"
 	aria-hidden="true"
 	aria-hidden="true"
 	stroke-width={strokeWidth}
 	stroke-width={strokeWidth}
-	stroke="currentColor"
 	class={className}
 	class={className}
 >
 >
-	<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
+	<slot />
+	<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>
 </svg>

+ 1 - 1
src/lib/components/layout/ChatsModal.svelte

@@ -329,7 +329,7 @@
 					</div>
 					</div>
 				{:else}
 				{:else}
 					<div class="w-full h-full flex justify-center items-center min-h-20">
 					<div class="w-full h-full flex justify-center items-center min-h-20">
-						<Spinner />
+						<Spinner className="size-5" />
 					</div>
 					</div>
 				{/if}
 				{/if}
 
 

+ 1 - 1
src/lib/components/layout/SearchModal.svelte

@@ -197,7 +197,7 @@
 				{/if}
 				{/if}
 			{:else}
 			{:else}
 				<div class="w-full h-full flex justify-center items-center">
 				<div class="w-full h-full flex justify-center items-center">
-					<Spinner />
+					<Spinner className="size-5" />
 				</div>
 				</div>
 			{/if}
 			{/if}
 		</div>
 		</div>

+ 2 - 2
src/lib/components/layout/Sidebar.svelte

@@ -59,7 +59,7 @@
 	import ChannelItem from './Sidebar/ChannelItem.svelte';
 	import ChannelItem from './Sidebar/ChannelItem.svelte';
 	import PencilSquare from '../icons/PencilSquare.svelte';
 	import PencilSquare from '../icons/PencilSquare.svelte';
 	import Home from '../icons/Home.svelte';
 	import Home from '../icons/Home.svelte';
-	import MagnifyingGlass from '../icons/MagnifyingGlass.svelte';
+	import Search from '../icons/Search.svelte';
 	import SearchModal from './SearchModal.svelte';
 	import SearchModal from './SearchModal.svelte';
 
 
 	const BREAKPOINT = 768;
 	const BREAKPOINT = 768;
@@ -554,7 +554,7 @@
 				draggable="false"
 				draggable="false"
 			>
 			>
 				<div class="self-center">
 				<div class="self-center">
-					<MagnifyingGlass strokeWidth="2" className="size-[1.1rem]" />
+					<Search strokeWidth="2" className="size-[1.1rem]" />
 				</div>
 				</div>
 
 
 				<div class="flex self-center translate-y-[0.5px]">
 				<div class="flex self-center translate-y-[0.5px]">

+ 4 - 35
src/lib/components/layout/Sidebar/ChannelModal.svelte

@@ -2,9 +2,11 @@
 	import { getContext, createEventDispatcher, onMount } from 'svelte';
 	import { getContext, createEventDispatcher, onMount } from 'svelte';
 	import { createNewChannel, deleteChannelById } from '$lib/apis/channels';
 	import { createNewChannel, deleteChannelById } from '$lib/apis/channels';
 
 
+	import Spinner from '$lib/components/common/Spinner.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import AccessControl from '$lib/components/workspace/common/AccessControl.svelte';
 	import AccessControl from '$lib/components/workspace/common/AccessControl.svelte';
 	import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
 	import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
 	import { page } from '$app/stores';
 	import { page } from '$app/stores';
@@ -84,18 +86,7 @@
 					show = false;
 					show = false;
 				}}
 				}}
 			>
 			>
-				<svg
-					xmlns="http://www.w3.org/2000/svg"
-					viewBox="0 0 20 20"
-					fill="currentColor"
-					class="w-5 h-5"
-				>
-					<path
-						fill-rule="evenodd"
-						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"
-						clip-rule="evenodd"
-					/>
-				</svg>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 
@@ -157,29 +148,7 @@
 
 
 							{#if loading}
 							{#if loading}
 								<div class="ml-2 self-center">
 								<div class="ml-2 self-center">
-									<svg
-										class=" w-4 h-4"
-										viewBox="0 0 24 24"
-										fill="currentColor"
-										xmlns="http://www.w3.org/2000/svg"
-										><style>
-											.spinner_ajPY {
-												transform-origin: center;
-												animation: spinner_AtaB 0.75s infinite linear;
-											}
-											@keyframes spinner_AtaB {
-												100% {
-													transform: rotate(360deg);
-												}
-											}
-										</style><path
-											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-											opacity=".25"
-										/><path
-											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-											class="spinner_ajPY"
-										/></svg
-									>
+									<Spinner />
 								</div>
 								</div>
 							{/if}
 							{/if}
 						</button>
 						</button>

+ 2 - 12
src/lib/components/layout/Sidebar/SearchInput.svelte

@@ -3,6 +3,7 @@
 	import { tags } from '$lib/stores';
 	import { tags } from '$lib/stores';
 	import { getContext, createEventDispatcher, onMount, onDestroy, tick } from 'svelte';
 	import { getContext, createEventDispatcher, onMount, onDestroy, tick } from 'svelte';
 	import { fade } from 'svelte/transition';
 	import { fade } from 'svelte/transition';
+	import Search from '$lib/components/icons/Search.svelte';
 	import XMark from '$lib/components/icons/XMark.svelte';
 	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
@@ -91,18 +92,7 @@
 <div class="px-1 mb-1 flex justify-center space-x-2 relative z-10" id="search-container">
 <div class="px-1 mb-1 flex justify-center space-x-2 relative z-10" id="search-container">
 	<div class="flex w-full rounded-xl" id="chat-search">
 	<div class="flex w-full rounded-xl" id="chat-search">
 		<div class="self-center py-2 rounded-l-xl bg-transparent dark:text-gray-300">
 		<div class="self-center py-2 rounded-l-xl bg-transparent dark:text-gray-300">
-			<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>
+			<Search />
 		</div>
 		</div>
 
 
 		<input
 		<input

+ 1 - 1
src/lib/components/notes/NoteEditor.svelte

@@ -700,7 +700,7 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings,
 	{#if loading}
 	{#if loading}
 		<div class=" absolute top-0 bottom-0 left-0 right-0 flex">
 		<div class=" absolute top-0 bottom-0 left-0 right-0 flex">
 			<div class="m-auto">
 			<div class="m-auto">
-				<Spinner />
+				<Spinner className="size-5" />
 			</div>
 			</div>
 		</div>
 		</div>
 	{:else}
 	{:else}

+ 1 - 1
src/lib/components/notes/Notes.svelte

@@ -462,7 +462,7 @@
 	{/if} -->
 	{/if} -->
 	{:else}
 	{:else}
 		<div class="w-full h-full flex justify-center items-center">
 		<div class="w-full h-full flex justify-center items-center">
-			<Spinner />
+			<Spinner className="size-5" />
 		</div>
 		</div>
 	{/if}
 	{/if}
 </div>
 </div>

+ 1 - 1
src/lib/components/workspace/Knowledge.svelte

@@ -215,6 +215,6 @@
 	</div>
 	</div>
 {:else}
 {:else}
 	<div class="w-full h-full flex justify-center items-center">
 	<div class="w-full h-full flex justify-center items-center">
-		<Spinner />
+		<Spinner className="size-5" />
 	</div>
 	</div>
 {/if}
 {/if}

+ 2 - 23
src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte

@@ -7,6 +7,7 @@
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
 	import { knowledge, user } from '$lib/stores';
 	import { knowledge, user } from '$lib/stores';
 	import AccessControl from '../common/AccessControl.svelte';
 	import AccessControl from '../common/AccessControl.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
 
 
 	let loading = false;
 	let loading = false;
 
 
@@ -133,29 +134,7 @@
 
 
 					{#if loading}
 					{#if loading}
 						<div class="ml-1.5 self-center">
 						<div class="ml-1.5 self-center">
-							<svg
-								class=" w-4 h-4"
-								viewBox="0 0 24 24"
-								fill="currentColor"
-								xmlns="http://www.w3.org/2000/svg"
-								><style>
-									.spinner_ajPY {
-										transform-origin: center;
-										animation: spinner_AtaB 0.75s infinite linear;
-									}
-									@keyframes spinner_AtaB {
-										100% {
-											transform: rotate(360deg);
-										}
-									}
-								</style><path
-									d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-									opacity=".25"
-								/><path
-									d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-									class="spinner_ajPY"
-								/></svg
-							>
+							<Spinner />
 						</div>
 						</div>
 					{/if}
 					{/if}
 				</button>
 				</button>

+ 3 - 13
src/lib/components/workspace/Knowledge/KnowledgeBase.svelte

@@ -49,6 +49,7 @@
 	import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
 	import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
 	import LockClosed from '$lib/components/icons/LockClosed.svelte';
 	import LockClosed from '$lib/components/icons/LockClosed.svelte';
 	import AccessControlModal from '../common/AccessControlModal.svelte';
 	import AccessControlModal from '../common/AccessControlModal.svelte';
+	import Search from '$lib/components/icons/Search.svelte';
 
 
 	let largeScreen = true;
 	let largeScreen = true;
 
 
@@ -861,18 +862,7 @@
 						<div class=" px-3">
 						<div class=" px-3">
 							<div class="flex mb-0.5">
 							<div class="flex mb-0.5">
 								<div class=" self-center ml-1 mr-3">
 								<div class=" self-center ml-1 mr-3">
-									<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>
+									<Search />
 								</div>
 								</div>
 								<input
 								<input
 									class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-hidden bg-transparent"
 									class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-hidden bg-transparent"
@@ -931,6 +921,6 @@
 			</div>
 			</div>
 		</div>
 		</div>
 	{:else}
 	{:else}
-		<Spinner />
+		<Spinner className="size-5" />
 	{/if}
 	{/if}
 </div>
 </div>

+ 1 - 1
src/lib/components/workspace/Models.svelte

@@ -572,6 +572,6 @@
 	{/if}
 	{/if}
 {:else}
 {:else}
 	<div class="w-full h-full flex justify-center items-center">
 	<div class="w-full h-full flex justify-center items-center">
-		<Spinner />
+		<Spinner className="size-5" />
 	</div>
 	</div>
 {/if}
 {/if}

+ 2 - 12
src/lib/components/workspace/Models/Knowledge/Selector.svelte

@@ -6,6 +6,7 @@
 	import { flyAndScale } from '$lib/utils/transitions';
 	import { flyAndScale } from '$lib/utils/transitions';
 	import { knowledge } from '$lib/stores';
 	import { knowledge } from '$lib/stores';
 	import Dropdown from '$lib/components/common/Dropdown.svelte';
 	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import Search from '$lib/components/icons/Search.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
@@ -93,18 +94,7 @@
 			<div class=" flex w-full space-x-2 py-0.5 px-2">
 			<div class=" flex w-full space-x-2 py-0.5 px-2">
 				<div class="flex flex-1">
 				<div class="flex flex-1">
 					<div class=" self-center ml-1 mr-3">
 					<div class=" self-center ml-1 mr-3">
-						<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>
+						<Search />
 					</div>
 					</div>
 					<input
 					<input
 						class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-hidden bg-transparent"
 						class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-hidden bg-transparent"

+ 4 - 33
src/lib/components/workspace/Models/ModelEditor.svelte

@@ -16,6 +16,8 @@
 	import AccessControl from '../common/AccessControl.svelte';
 	import AccessControl from '../common/AccessControl.svelte';
 	import { stringify } from 'postcss';
 	import { stringify } from 'postcss';
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
@@ -677,16 +679,7 @@
 													info.meta.suggestion_prompts = info.meta.suggestion_prompts;
 													info.meta.suggestion_prompts = info.meta.suggestion_prompts;
 												}}
 												}}
 											>
 											>
-												<svg
-													xmlns="http://www.w3.org/2000/svg"
-													viewBox="0 0 20 20"
-													fill="currentColor"
-													class="w-4 h-4"
-												>
-													<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>
+												<XMark className={'size-4'} />
 											</button>
 											</button>
 										</div>
 										</div>
 									{/each}
 									{/each}
@@ -775,29 +768,7 @@
 
 
 							{#if loading}
 							{#if loading}
 								<div class="ml-1.5 self-center">
 								<div class="ml-1.5 self-center">
-									<svg
-										class=" w-4 h-4"
-										viewBox="0 0 24 24"
-										fill="currentColor"
-										xmlns="http://www.w3.org/2000/svg"
-										><style>
-											.spinner_ajPY {
-												transform-origin: center;
-												animation: spinner_AtaB 0.75s infinite linear;
-											}
-											@keyframes spinner_AtaB {
-												100% {
-													transform: rotate(360deg);
-												}
-											}
-										</style><path
-											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-											opacity=".25"
-										/><path
-											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-											class="spinner_ajPY"
-										/></svg
-									>
+									<Spinner />
 								</div>
 								</div>
 							{/if}
 							{/if}
 						</button>
 						</button>

+ 2 - 23
src/lib/components/workspace/Prompts/PromptEditor.svelte

@@ -9,6 +9,7 @@
 	import AccessControlModal from '../common/AccessControlModal.svelte';
 	import AccessControlModal from '../common/AccessControlModal.svelte';
 	import { user } from '$lib/stores';
 	import { user } from '$lib/stores';
 	import { slugify } from '$lib/utils';
 	import { slugify } from '$lib/utils';
+	import Spinner from '$lib/components/common/Spinner.svelte';
 
 
 	export let onSubmit: Function;
 	export let onSubmit: Function;
 	export let edit = false;
 	export let edit = false;
@@ -191,29 +192,7 @@
 
 
 				{#if loading}
 				{#if loading}
 					<div class="ml-1.5 self-center">
 					<div class="ml-1.5 self-center">
-						<svg
-							class=" w-4 h-4"
-							viewBox="0 0 24 24"
-							fill="currentColor"
-							xmlns="http://www.w3.org/2000/svg"
-							><style>
-								.spinner_ajPY {
-									transform-origin: center;
-									animation: spinner_AtaB 0.75s infinite linear;
-								}
-								@keyframes spinner_AtaB {
-									100% {
-										transform: rotate(360deg);
-									}
-								}
-							</style><path
-								d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-								opacity=".25"
-							/><path
-								d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-								class="spinner_ajPY"
-							/></svg
-						>
+						<Spinner />
 					</div>
 					</div>
 				{/if}
 				{/if}
 			</button>
 			</button>

+ 1 - 1
src/lib/components/workspace/Tools.svelte

@@ -573,6 +573,6 @@
 	</ConfirmDialog>
 	</ConfirmDialog>
 {:else}
 {:else}
 	<div class="w-full h-full flex justify-center items-center">
 	<div class="w-full h-full flex justify-center items-center">
-		<Spinner />
+		<Spinner className="size-5" />
 	</div>
 	</div>
 {/if}
 {/if}

+ 2 - 10
src/lib/components/workspace/common/AccessControlModal.svelte

@@ -4,6 +4,7 @@
 
 
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
 	import AccessControl from './AccessControl.svelte';
 	import AccessControl from './AccessControl.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	export let show = false;
 	export let show = false;
 	export let accessControl = {};
 	export let accessControl = {};
@@ -25,16 +26,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 

+ 2 - 10
src/lib/components/workspace/common/ManifestModal.svelte

@@ -4,6 +4,7 @@
 	import { onMount, getContext } from 'svelte';
 	import { onMount, getContext } from 'svelte';
 
 
 	import Modal from '../../common/Modal.svelte';
 	import Modal from '../../common/Modal.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
@@ -22,16 +23,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 

+ 3 - 33
src/lib/components/workspace/common/ValvesModal.svelte

@@ -14,6 +14,7 @@
 	import Spinner from '../../common/Spinner.svelte';
 	import Spinner from '../../common/Spinner.svelte';
 	import Switch from '$lib/components/common/Switch.svelte';
 	import Switch from '$lib/components/common/Switch.svelte';
 	import Valves from '$lib/components/common/Valves.svelte';
 	import Valves from '$lib/components/common/Valves.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
@@ -105,16 +106,7 @@
 					show = false;
 					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>
+				<XMark className={'size-5'} />
 			</button>
 			</button>
 		</div>
 		</div>
 
 
@@ -146,29 +138,7 @@
 
 
 							{#if saving}
 							{#if saving}
 								<div class="ml-2 self-center">
 								<div class="ml-2 self-center">
-									<svg
-										class=" w-4 h-4"
-										viewBox="0 0 24 24"
-										fill="currentColor"
-										xmlns="http://www.w3.org/2000/svg"
-										><style>
-											.spinner_ajPY {
-												transform-origin: center;
-												animation: spinner_AtaB 0.75s infinite linear;
-											}
-											@keyframes spinner_AtaB {
-												100% {
-													transform: rotate(360deg);
-												}
-											}
-										</style><path
-											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-											opacity=".25"
-										/><path
-											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-											class="spinner_ajPY"
-										/></svg
-									>
+									<Spinner />
 								</div>
 								</div>
 							{/if}
 							{/if}
 						</button>
 						</button>

+ 1 - 1
src/lib/i18n/index.ts

@@ -26,7 +26,7 @@ const createIsLoadingStore = (i18n: i18nType) => {
 	// if loaded resources are empty || {}, set loading to true
 	// if loaded resources are empty || {}, set loading to true
 	i18n.on('loaded', (resources) => {
 	i18n.on('loaded', (resources) => {
 		// console.log('loaded:', resources);
 		// console.log('loaded:', resources);
-		Object.keys(resources).length !== 0 && isLoading.set(false);
+		isLoading.set(Object.keys(resources).length === 0);
 	});
 	});
 
 
 	// if resources failed loading, set loading to true
 	// if resources failed loading, set loading to true

+ 5 - 5
src/lib/i18n/locales/ko-KR/translation.json

@@ -217,7 +217,7 @@
 	"Code Execution": "코드 실행",
 	"Code Execution": "코드 실행",
 	"Code Execution Engine": "코드 실행 엔진",
 	"Code Execution Engine": "코드 실행 엔진",
 	"Code Execution Timeout": "코드 실행 시간 초과",
 	"Code Execution Timeout": "코드 실행 시간 초과",
-	"Code formatted successfully": "성공적으로 코드가 생성되었습니다",
+	"Code formatted successfully": "코드 포맷팅이 성공적으로 완료되었습니다.",
 	"Code Interpreter": "코드 인터프리터",
 	"Code Interpreter": "코드 인터프리터",
 	"Code Interpreter Engine": "코드 인터프리터 엔진",
 	"Code Interpreter Engine": "코드 인터프리터 엔진",
 	"Code Interpreter Prompt Template": "코드 인터프리터 프롬프트 템플릿",
 	"Code Interpreter Prompt Template": "코드 인터프리터 프롬프트 템플릿",
@@ -616,7 +616,7 @@
 	"Firecrawl API Key": "Firecrawl API 키",
 	"Firecrawl API Key": "Firecrawl API 키",
 	"Fluidly stream large external response chunks": "대규모 외부 응답 청크를 유연하게 스트리밍",
 	"Fluidly stream large external response chunks": "대규모 외부 응답 청크를 유연하게 스트리밍",
 	"Focus chat input": "채팅 입력창에 포커스",
 	"Focus chat input": "채팅 입력창에 포커스",
-	"Folder deleted successfully": "성공적으로 폴터가 생성되었습니다",
+	"Folder deleted successfully": "성공적으로 폴더가 삭제되었습니다",
 	"Folder name cannot be empty.": "폴더 이름을 작성해주세요",
 	"Folder name cannot be empty.": "폴더 이름을 작성해주세요",
 	"Folder name updated successfully": "성공적으로 폴더 이름이 저장되었습니다",
 	"Folder name updated successfully": "성공적으로 폴더 이름이 저장되었습니다",
 	"Follow up": "",
 	"Follow up": "",
@@ -937,7 +937,7 @@
 	"openapi.json URL or Path": "",
 	"openapi.json URL or Path": "",
 	"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
 	"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
 	"or": "또는",
 	"or": "또는",
-	"Organize your users": "사용자 ",
+	"Organize your users": "사용자 관리",
 	"Other": "기타",
 	"Other": "기타",
 	"OUTPUT": "출력",
 	"OUTPUT": "출력",
 	"Output format": "출력 형식",
 	"Output format": "출력 형식",
@@ -956,7 +956,7 @@
 	"Pending User Overlay Title": "",
 	"Pending User Overlay Title": "",
 	"Permission denied when accessing media devices": "미디어 장치 접근 권한이 거부되었습니다.",
 	"Permission denied when accessing media devices": "미디어 장치 접근 권한이 거부되었습니다.",
 	"Permission denied when accessing microphone": "마이크 접근 권한이 거부되었습니다.",
 	"Permission denied when accessing microphone": "마이크 접근 권한이 거부되었습니다.",
-	"Permission denied when accessing microphone: {{error}}": "마이크 접근 권이 거부되었습니다: {{error}}",
+	"Permission denied when accessing microphone: {{error}}": "마이크 접근 권이 거부되었습니다: {{error}}",
 	"Permissions": "권한",
 	"Permissions": "권한",
 	"Perplexity API Key": "Perplexity API 키",
 	"Perplexity API Key": "Perplexity API 키",
 	"Perplexity Model": "",
 	"Perplexity Model": "",
@@ -1047,7 +1047,7 @@
 	"Reset Upload Directory": "업로드 디렉토리 초기화",
 	"Reset Upload Directory": "업로드 디렉토리 초기화",
 	"Reset Vector Storage/Knowledge": "벡터 저장 공간/지식 기반 초기화",
 	"Reset Vector Storage/Knowledge": "벡터 저장 공간/지식 기반 초기화",
 	"Reset view": "보기 초기화",
 	"Reset view": "보기 초기화",
-	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "웹사이트 권환과 같이 응답 알림이 활성화될 수 없습니다. 필요한 접근을 사용하기 위해 브라우져 설정을 확인 부탁드립니다.",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "웹사이트 권한이 거부되어 응답 알림을 활성화할 수 없습니다. 필요한 접근 권한을 부여하려면 브라우저 설정을 확인해 주세요.",
 	"Response splitting": "응답 나누기",
 	"Response splitting": "응답 나누기",
 	"Response Watermark": "",
 	"Response Watermark": "",
 	"Result": "결과",
 	"Result": "결과",

+ 1 - 1
src/routes/(app)/+layout.svelte

@@ -333,7 +333,7 @@
 				<slot />
 				<slot />
 			{:else}
 			{:else}
 				<div class="w-full flex-1 h-full flex items-center justify-center">
 				<div class="w-full flex-1 h-full flex items-center justify-center">
-					<Spinner />
+					<Spinner className="size-5" />
 				</div>
 				</div>
 			{/if}
 			{/if}
 		</div>
 		</div>

+ 1 - 1
src/routes/(app)/admin/functions/edit/+page.svelte

@@ -87,7 +87,7 @@
 {:else}
 {:else}
 	<div class="flex items-center justify-center h-full">
 	<div class="flex items-center justify-center h-full">
 		<div class=" pb-16">
 		<div class=" pb-16">
-			<Spinner />
+			<Spinner className="size-5" />
 		</div>
 		</div>
 	</div>
 	</div>
 {/if}
 {/if}

+ 1 - 1
src/routes/(app)/workspace/tools/edit/+page.svelte

@@ -82,7 +82,7 @@
 {:else}
 {:else}
 	<div class="flex items-center justify-center h-full">
 	<div class="flex items-center justify-center h-full">
 		<div class=" pb-16">
 		<div class=" pb-16">
-			<Spinner />
+			<Spinner className="size-5" />
 		</div>
 		</div>
 	</div>
 	</div>
 {/if}
 {/if}

+ 1 - 1
src/routes/+error.svelte

@@ -5,7 +5,7 @@
 <div class=" bg-white dark:bg-gray-800 min-h-screen">
 <div class=" bg-white dark:bg-gray-800 min-h-screen">
 	<div class=" flex h-full">
 	<div class=" flex h-full">
 		<div class="m-auto my-10 dark:text-gray-300 text-3xl font-semibold">
 		<div class="m-auto my-10 dark:text-gray-300 text-3xl font-semibold">
-			{$page.status}: {$page.error.message}
+			{$page.status}: {$page.error?.message}
 		</div>
 		</div>
 	</div>
 	</div>
 </div>
 </div>

+ 1 - 1
src/routes/auth/+page.svelte

@@ -203,7 +203,7 @@
 							</div>
 							</div>
 
 
 							<div>
 							<div>
-								<Spinner />
+								<Spinner className="size-5" />
 							</div>
 							</div>
 						</div>
 						</div>
 					</div>
 					</div>

+ 1 - 15
vite.config.ts

@@ -3,20 +3,6 @@ import { defineConfig } from 'vite';
 
 
 import { viteStaticCopy } from 'vite-plugin-static-copy';
 import { viteStaticCopy } from 'vite-plugin-static-copy';
 
 
-// /** @type {import('vite').Plugin} */
-// const viteServerConfig = {
-// 	name: 'log-request-middleware',
-// 	configureServer(server) {
-// 		server.middlewares.use((req, res, next) => {
-// 			res.setHeader('Access-Control-Allow-Origin', '*');
-// 			res.setHeader('Access-Control-Allow-Methods', 'GET');
-// 			res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
-// 			res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
-// 			next();
-// 		});
-// 	}
-// };
-
 export default defineConfig({
 export default defineConfig({
 	plugins: [
 	plugins: [
 		sveltekit(),
 		sveltekit(),
@@ -41,6 +27,6 @@ export default defineConfig({
 		format: 'es'
 		format: 'es'
 	},
 	},
 	esbuild: {
 	esbuild: {
-		pure: ['console.log', 'console.debug']
+		pure: process.env.ENV === 'dev' ? [] : ['console.log', 'console.debug']
 	}
 	}
 });
 });