diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..4c4cfa3b --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,32 @@ +## Pull Request Checklist + +- [ ] **Description:** Briefly describe the changes in this pull request. +- [ ] **Changelog:** Ensure a changelog entry following the format of [Keep a Changelog](https://keepachangelog.com/) is added at the bottom of the PR description. +- [ ] **Documentation:** Have you updated relevant documentation? +- [ ] **Dependencies:** Are there any new dependencies? Have you updated the dependency versions in the documentation? + +--- + +## Description + +[Insert a brief description of the changes made in this pull request] + +--- + +### Changelog Entry + +### Added + +- [List any new features or additions] + +### Fixed + +- [List any fixes or corrections] + +### Changed + +- [List any changes or updates] + +### Removed + +- [List any removed features or files] diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml new file mode 100644 index 00000000..fa3fa296 --- /dev/null +++ b/.github/workflows/build-release.yml @@ -0,0 +1,49 @@ +name: Release + +on: + push: + branches: + - main # or whatever branch you want to use + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Check for changes in package.json + run: | + git diff --cached --diff-filter=d package.json || { + echo "No changes to package.json" + exit 1 + } + + - name: Get version number from package.json + id: get_version + run: | + VERSION=$(jq -r '.version' package.json) + echo "::set-output name=version::$VERSION" + + - name: Create GitHub release + uses: actions/github-script@v5 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const release = await github.rest.repos.createRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + tag_name: `v${{ steps.get_version.outputs.version }}`, + name: `v${{ steps.get_version.outputs.version }}`, + body: 'Automatically created new release', + }) + console.log(`Created release ${release.data.html_url}`) + + - name: Upload package to GitHub release + uses: actions/upload-artifact@v3 + with: + name: package + path: . + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..48a60634 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,25 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.1.102] - 2024-02-22 + +### Added + +- **πŸ–ΌοΈ Image Generation**: Generate Images using the AUTOMATIC1111/stable-diffusion-webui API. You can set this up in Settings > Images. +- **πŸ“ Change title generation prompt**: Change the prompt used to generate titles for your chats. You can set this up in the Settings > Interface. +- **πŸ€– Change embedding model**: Change the embedding model used to generate embeddings for your chats in the Dockerfile. Use any sentence transformer model from huggingface.co. +- **πŸ“’ CHANGELOG.md/Popup**: This popup will show you the latest changes. + +## [0.1.101] - 2024-02-22 + +### Fixed + +- LaTex output formatting issue (#828) + +### Changed + +- Instead of having the previous 1.0.0-alpha.101, we switched to semantic versioning as a way to respect global conventions. diff --git a/Dockerfile b/Dockerfile index 03dccefe..7eb34dab 100644 --- a/Dockerfile +++ b/Dockerfile @@ -73,6 +73,8 @@ COPY --from=build /app/onnx /root/.cache/chroma/onnx_models/all-MiniLM-L6-v2/onn # copy built frontend files COPY --from=build /app/build /app/build +COPY --from=build /app/CHANGELOG.md /app/CHANGELOG.md +COPY --from=build /app/package.json /app/package.json # copy backend files COPY ./backend . diff --git a/README.md b/README.md index 421a9bd2..31df8c29 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ [![Discord](https://img.shields.io/badge/Discord-Open_WebUI-blue?logo=discord&logoColor=white)](https://discord.gg/5rJgQTnV4s) [![](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/tjbck) -ChatGPT-Style Web Interface for Ollama πŸ¦™ +User-friendly WebUI for LLMs, supported LLM runners include Ollama and OpenAI-compatible APIs. ![Open WebUI Demo](./demo.gif) @@ -286,6 +286,8 @@ cp -RPp .env.example .env # Building Frontend Using Node npm i npm run build +# or for development (hot reload) +# npm run dev # or Building Frontend Using Bun # bun install @@ -295,6 +297,9 @@ npm run build cd ./backend pip install -r requirements.txt -U sh start.sh +# or for development (hot reload) +# npm run build must have been run once before! +# sh dev.sh ``` You should have Open WebUI up and running at http://localhost:8080/. Enjoy! πŸ˜„ diff --git a/backend/apps/images/main.py b/backend/apps/images/main.py index 998af3dd..39d3f96a 100644 --- a/backend/apps/images/main.py +++ b/backend/apps/images/main.py @@ -1,4 +1,4 @@ -import os +import re import requests from fastapi import ( FastAPI, @@ -34,6 +34,7 @@ app.add_middleware( app.state.AUTOMATIC1111_BASE_URL = AUTOMATIC1111_BASE_URL app.state.ENABLED = app.state.AUTOMATIC1111_BASE_URL != "" +app.state.IMAGE_SIZE = "512x512" @app.get("/enabled", response_model=bool) @@ -74,6 +75,33 @@ async def update_openai_url(form_data: UrlUpdateForm, user=Depends(get_admin_use } +class ImageSizeUpdateForm(BaseModel): + size: str + + +@app.get("/size") +async def get_image_size(user=Depends(get_admin_user)): + return {"IMAGE_SIZE": app.state.IMAGE_SIZE} + + +@app.post("/size/update") +async def update_image_size( + form_data: ImageSizeUpdateForm, user=Depends(get_admin_user) +): + pattern = r"^\d+x\d+$" # Regular expression pattern + if re.match(pattern, form_data.size): + app.state.IMAGE_SIZE = form_data.size + return { + "IMAGE_SIZE": app.state.IMAGE_SIZE, + "status": True, + } + else: + raise HTTPException( + status_code=400, + detail=ERROR_MESSAGES.INCORRECT_FORMAT(" (e.g., 512x512)."), + ) + + @app.get("/models") def get_models(user=Depends(get_current_user)): try: @@ -140,7 +168,7 @@ def generate_image( if form_data.model: set_model_handler(form_data.model) - width, height = tuple(map(int, form_data.size.split("x"))) + width, height = tuple(map(int, app.state.IMAGE_SIZE.split("x"))) data = { "prompt": form_data.prompt, diff --git a/backend/apps/rag/main.py b/backend/apps/rag/main.py index 4176d567..83c10233 100644 --- a/backend/apps/rag/main.py +++ b/backend/apps/rag/main.py @@ -423,7 +423,7 @@ def get_loader(filename: str, file_content_type: str, file_path: str): "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ] or file_ext in ["xls", "xlsx"]: loader = UnstructuredExcelLoader(file_path) - elif file_ext in known_source_ext or file_content_type.find("text/") >= 0: + elif file_ext in known_source_ext or (file_content_type and file_content_type.find("text/") >= 0): loader = TextLoader(file_path) else: loader = TextLoader(file_path) @@ -486,8 +486,8 @@ def store_doc( @app.get("/scan") def scan_docs_dir(user=Depends(get_admin_user)): - try: - for path in Path(DOCS_DIR).rglob("./**/*"): + for path in Path(DOCS_DIR).rglob("./**/*"): + try: if path.is_file() and not path.name.startswith("."): tags = extract_folders_after_data_docs(path) filename = path.name @@ -535,8 +535,8 @@ def scan_docs_dir(user=Depends(get_admin_user)): ), ) - except Exception as e: - print(e) + except Exception as e: + print(e) return True diff --git a/backend/config.py b/backend/config.py index caf2cc45..fadae68c 100644 --- a/backend/config.py +++ b/backend/config.py @@ -1,11 +1,17 @@ import os import chromadb from chromadb import Settings -from secrets import token_bytes from base64 import b64encode -from constants import ERROR_MESSAGES +from bs4 import BeautifulSoup + from pathlib import Path import json +import markdown +import requests +import shutil + +from secrets import token_bytes +from constants import ERROR_MESSAGES try: @@ -15,6 +21,8 @@ try: except ImportError: print("dotenv not installed, skipping...") +WEBUI_NAME = "Open WebUI" +shutil.copyfile("../build/favicon.png", "./static/favicon.png") #################################### # ENV (dev,test,prod) @@ -22,6 +30,104 @@ except ImportError: ENV = os.environ.get("ENV", "dev") +try: + with open(f"../package.json", "r") as f: + PACKAGE_DATA = json.load(f) +except: + PACKAGE_DATA = {"version": "0.0.0"} + +VERSION = PACKAGE_DATA["version"] + + +# Function to parse each section +def parse_section(section): + items = [] + for li in section.find_all("li"): + # Extract raw HTML string + raw_html = str(li) + + # Extract text without HTML tags + text = li.get_text(separator=" ", strip=True) + + # Split into title and content + parts = text.split(": ", 1) + title = parts[0].strip() if len(parts) > 1 else "" + content = parts[1].strip() if len(parts) > 1 else text + + items.append({"title": title, "content": content, "raw": raw_html}) + return items + + +try: + with open("../CHANGELOG.md", "r") as file: + changelog_content = file.read() +except: + changelog_content = "" + +# Convert markdown content to HTML +html_content = markdown.markdown(changelog_content) + +# Parse the HTML content +soup = BeautifulSoup(html_content, "html.parser") + +# Initialize JSON structure +changelog_json = {} + +# Iterate over each version +for version in soup.find_all("h2"): + version_number = version.get_text().strip().split(" - ")[0][1:-1] # Remove brackets + date = version.get_text().strip().split(" - ")[1] + + version_data = {"date": date} + + # Find the next sibling that is a h3 tag (section title) + current = version.find_next_sibling() + + print(current) + + while current and current.name != "h2": + if current.name == "h3": + section_title = current.get_text().lower() # e.g., "added", "fixed" + section_items = parse_section(current.find_next_sibling("ul")) + version_data[section_title] = section_items + + # Move to the next element + current = current.find_next_sibling() + + changelog_json[version_number] = version_data + + +CHANGELOG = changelog_json + + +#################################### +# CUSTOM_NAME +#################################### + +CUSTOM_NAME = os.environ.get("CUSTOM_NAME", "") +if CUSTOM_NAME: + try: + r = requests.get(f"https://api.openwebui.com/api/v1/custom/{CUSTOM_NAME}") + data = r.json() + if r.ok: + if "logo" in data: + url = ( + f"https://api.openwebui.com{data['logo']}" + if data["logo"][0] == "/" + else data["logo"] + ) + + r = requests.get(url, stream=True) + if r.status_code == 200: + with open("./static/favicon.png", "wb") as f: + r.raw.decode_content = True + shutil.copyfileobj(r.raw, f) + + WEBUI_NAME = data["name"] + except Exception as e: + print(e) + pass + #################################### # DATA/FRONTEND BUILD DIR @@ -116,7 +222,7 @@ DEFAULT_PROMPT_SUGGESTIONS = ( ) -DEFAULT_USER_ROLE = "pending" +DEFAULT_USER_ROLE = os.getenv("DEFAULT_USER_ROLE", "pending") USER_PERMISSIONS = {"chat": {"deletion": True}} diff --git a/backend/constants.py b/backend/constants.py index 580db9c5..cb802c7f 100644 --- a/backend/constants.py +++ b/backend/constants.py @@ -44,3 +44,6 @@ class ERROR_MESSAGES(str, Enum): MALICIOUS = "Unusual activities detected, please try again in a few minutes." PANDOC_NOT_INSTALLED = "Pandoc is not installed on the server. Please contact your administrator for assistance." + INCORRECT_FORMAT = ( + lambda err="": f"Invalid format. Please use the correct format{err if err else ''}" + ) diff --git a/backend/main.py b/backend/main.py index 2ec741b0..b9370a18 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,5 +1,9 @@ +from bs4 import BeautifulSoup +import json +import markdown import time + from fastapi import FastAPI, Request from fastapi.staticfiles import StaticFiles from fastapi import HTTPException @@ -18,7 +22,7 @@ from apps.rag.main import app as rag_app from apps.web.main import app as webui_app -from config import ENV, FRONTEND_BUILD_DIR +from config import WEBUI_NAME, ENV, VERSION, CHANGELOG, FRONTEND_BUILD_DIR class SPAStaticFiles(StaticFiles): @@ -69,14 +73,25 @@ app.mount("/rag/api/v1", rag_app) @app.get("/api/config") async def get_app_config(): + return { "status": True, + "name": WEBUI_NAME, + "version": VERSION, "images": images_app.state.ENABLED, "default_models": webui_app.state.DEFAULT_MODELS, "default_prompt_suggestions": webui_app.state.DEFAULT_PROMPT_SUGGESTIONS, } +@app.get("/api/changelog") +async def get_app_changelog(): + return CHANGELOG + + +app.mount("/static", StaticFiles(directory="static"), name="static") + + app.mount( "/", SPAStaticFiles(directory=FRONTEND_BUILD_DIR, html=True), diff --git a/backend/static/favicon.png b/backend/static/favicon.png new file mode 100644 index 00000000..519af1db Binary files /dev/null and b/backend/static/favicon.png differ diff --git a/bun.lockb b/bun.lockb index 7768741d..e0a038da 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/kubernetes/manifest/base/webui-pvc.yaml b/kubernetes/manifest/base/webui-pvc.yaml index 285dfeef..5c75283a 100644 --- a/kubernetes/manifest/base/webui-pvc.yaml +++ b/kubernetes/manifest/base/webui-pvc.yaml @@ -4,7 +4,7 @@ metadata: labels: app: ollama-webui name: ollama-webui-pvc - namespace: ollama-namespace + namespace: open-webui spec: accessModes: ["ReadWriteOnce"] resources: diff --git a/kubernetes/manifest/kustomization.yaml b/kubernetes/manifest/kustomization.yaml index f581839e..907bff3e 100644 --- a/kubernetes/manifest/kustomization.yaml +++ b/kubernetes/manifest/kustomization.yaml @@ -5,6 +5,7 @@ resources: - base/webui-deployment.yaml - base/webui-service.yaml - base/webui-ingress.yaml +- base/webui-pvc.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization diff --git a/package-lock.json b/package-lock.json index ded65c9a..9fdfdb8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "open-webui", - "version": "0.0.1", + "version": "v1.0.0-alpha.101", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "open-webui", - "version": "0.0.1", + "version": "v1.0.0-alpha.101", "dependencies": { "@sveltejs/adapter-node": "^1.3.1", "async": "^3.2.5", @@ -38,6 +38,7 @@ "prettier-plugin-svelte": "^2.10.1", "svelte": "^4.0.5", "svelte-check": "^3.4.3", + "svelte-confetti": "^1.3.2", "tailwindcss": "^3.3.3", "tslib": "^2.4.1", "typescript": "^5.0.0", @@ -3174,6 +3175,15 @@ "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0" } }, + "node_modules/svelte-confetti": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svelte-confetti/-/svelte-confetti-1.3.2.tgz", + "integrity": "sha512-R+JwFTC7hIgWVA/OuXrkj384B7CMoceb0t9VacyW6dORTQg0pWojVBB8Bo3tM30cLEQE48Fekzqgx+XSzHESMA==", + "dev": true, + "peerDependencies": { + "svelte": "^4.0.0" + } + }, "node_modules/svelte-eslint-parser": { "version": "0.33.1", "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.33.1.tgz", @@ -5852,6 +5862,13 @@ "typescript": "^5.0.3" } }, + "svelte-confetti": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svelte-confetti/-/svelte-confetti-1.3.2.tgz", + "integrity": "sha512-R+JwFTC7hIgWVA/OuXrkj384B7CMoceb0t9VacyW6dORTQg0pWojVBB8Bo3tM30cLEQE48Fekzqgx+XSzHESMA==", + "dev": true, + "requires": {} + }, "svelte-eslint-parser": { "version": "0.33.1", "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.33.1.tgz", diff --git a/package.json b/package.json index edc4762f..30549fdd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-webui", - "version": "v1.0.0-alpha.101", + "version": "0.1.102", "private": true, "scripts": { "dev": "vite dev --host", @@ -32,6 +32,7 @@ "prettier-plugin-svelte": "^2.10.1", "svelte": "^4.0.5", "svelte-check": "^3.4.3", + "svelte-confetti": "^1.3.2", "tailwindcss": "^3.3.3", "tslib": "^2.4.1", "typescript": "^5.0.0", diff --git a/src/lib/apis/images/index.ts b/src/lib/apis/images/index.ts index b25499d6..205ee90a 100644 --- a/src/lib/apis/images/index.ts +++ b/src/lib/apis/images/index.ts @@ -131,6 +131,73 @@ export const updateAUTOMATIC1111Url = async (token: string = '', url: string) => return res.AUTOMATIC1111_BASE_URL; }; +export const getImageSize = async (token: string = '') => { + let error = null; + + const res = await fetch(`${IMAGES_API_BASE_URL}/size`, { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + } + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .catch((err) => { + console.log(err); + if ('detail' in err) { + error = err.detail; + } else { + error = 'Server connection failed'; + } + return null; + }); + + if (error) { + throw error; + } + + return res.IMAGE_SIZE; +}; + +export const updateImageSize = async (token: string = '', size: string) => { + let error = null; + + const res = await fetch(`${IMAGES_API_BASE_URL}/size/update`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + }, + body: JSON.stringify({ + size: size + }) + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .catch((err) => { + console.log(err); + if ('detail' in err) { + error = err.detail; + } else { + error = 'Server connection failed'; + } + return null; + }); + + if (error) { + throw error; + } + + return res.IMAGE_SIZE; +}; + export const getDiffusionModels = async (token: string = '') => { let error = null; diff --git a/src/lib/apis/index.ts b/src/lib/apis/index.ts index c20107ce..db29e548 100644 --- a/src/lib/apis/index.ts +++ b/src/lib/apis/index.ts @@ -21,3 +21,25 @@ export const getBackendConfig = async () => { return res; }; + +export const getChangelog = async () => { + let error = null; + + const res = await fetch(`${WEBUI_BASE_URL}/api/changelog`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .catch((err) => { + console.log(err); + error = err; + return null; + }); + + return res; +}; diff --git a/src/lib/components/ChangelogModal.svelte b/src/lib/components/ChangelogModal.svelte new file mode 100644 index 00000000..0cf1f006 --- /dev/null +++ b/src/lib/components/ChangelogModal.svelte @@ -0,0 +1,115 @@ + + + +
+
+
+ What’s New in {$WEBUI_NAME} + +
+ +
+
+
Release Notes
+
+
+ v{WEBUI_VERSION} +
+
+
+ +
+ +
+
+
+ {#if changelog} + {#each Object.keys(changelog) as version} +
+
+ v{version} - {changelog[version].date} +
+ +
+ + {#each Object.keys(changelog[version]).filter((section) => section !== 'date') as section} +
+
+ {section} +
+ +
+ {#each Object.keys(changelog[version][section]) as item} +
+
+ {changelog[version][section][item].title} +
+
{changelog[version][section][item].content}
+
+ {/each} +
+
+ {/each} +
+ {/each} + {/if} +
+
+
+ +
+
+ diff --git a/src/lib/components/chat/Messages.svelte b/src/lib/components/chat/Messages.svelte index 64574530..c9e516ab 100644 --- a/src/lib/components/chat/Messages.svelte +++ b/src/lib/components/chat/Messages.svelte @@ -222,6 +222,34 @@ scrollToBottom(); }, 100); }; + + // TODO: change delete behaviour + // const deleteMessageAndDescendants = async (messageId: string) => { + // if (history.messages[messageId]) { + // history.messages[messageId].deleted = true; + + // for (const childId of history.messages[messageId].childrenIds) { + // await deleteMessageAndDescendants(childId); + // } + // } + // }; + + // const triggerDeleteMessageRecursive = async (messageId: string) => { + // await deleteMessageAndDescendants(messageId); + // await updateChatById(localStorage.token, chatId, { history }); + // await chats.set(await getChatList(localStorage.token)); + // }; + + const messageDeleteHandler = async (messageId) => { + if (history.messages[messageId]) { + history.messages[messageId].deleted = true; + + for (const childId of history.messages[messageId].childrenIds) { + history.messages[childId].deleted = true; + } + } + await updateChatById(localStorage.token, chatId, { history }); + }; {#if messages.length == 0} @@ -230,99 +258,103 @@
{#key chatId} {#each messages as message, messageIdx} -
-
- {#if message.role === 'user'} - message.parentId === null) - .map((message) => message.id) ?? []} - {confirmEditMessage} - {showPreviousMessage} - {showNextMessage} - {copyToClipboard} - /> + {#if !message.deleted} +
+
+ {#if message.role === 'user'} + messageDeleteHandler(message.id)} + user={$user} + {message} + isFirstMessage={messageIdx === 0} + siblings={message.parentId !== null + ? history.messages[message.parentId]?.childrenIds ?? [] + : Object.values(history.messages) + .filter((message) => message.parentId === null) + .map((message) => message.id) ?? []} + {confirmEditMessage} + {showPreviousMessage} + {showNextMessage} + {copyToClipboard} + /> - {#if messages.length - 1 === messageIdx && processing !== ''} -
-
-
+
+ + @keyframes spinner_8HQG { + 0%, + 57.14% { + animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1); + transform: translate(0); + } + 28.57% { + animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33); + transform: translateY(-6px); + } + 100% { + transform: translate(0); + } + } + +
+
+ {processing} +
-
- {processing} -
-
+ {/if} + {:else} + { + console.log('save', e); + + const message = e.detail; + history.messages[message.id] = message; + await updateChatById(localStorage.token, chatId, { + messages: messages, + history: history + }); + }} + /> {/if} - {:else} - { - console.log('save', e); - - const message = e.detail; - history.messages[message.id] = message; - await updateChatById(localStorage.token, chatId, { - messages: messages, - history: history - }); - }} - /> - {/if} +
-
+ {/if} {/each} {#if bottomPadding} diff --git a/src/lib/components/chat/Messages/Placeholder.svelte b/src/lib/components/chat/Messages/Placeholder.svelte index ae9ced14..f1f3e80a 100644 --- a/src/lib/components/chat/Messages/Placeholder.svelte +++ b/src/lib/components/chat/Messages/Placeholder.svelte @@ -1,4 +1,5 @@
@@ -189,11 +196,11 @@
{message.content}
-
+
{#if siblings.length > 1}
-
+
{siblings.indexOf(message.id) + 1} / {siblings.length}
+ + {#if !isFirstMessage} + + {/if}
{/if} diff --git a/src/lib/components/chat/Settings/About.svelte b/src/lib/components/chat/Settings/About.svelte index c10a23be..6933ed3b 100644 --- a/src/lib/components/chat/Settings/About.svelte +++ b/src/lib/components/chat/Settings/About.svelte @@ -1,7 +1,7 @@ + + + diff --git a/src/lib/components/common/ImagePreview.svelte b/src/lib/components/common/ImagePreview.svelte new file mode 100644 index 00000000..cf69327f --- /dev/null +++ b/src/lib/components/common/ImagePreview.svelte @@ -0,0 +1,62 @@ + + +{#if show} + + +
+
+
+ +
+ +
+ +
+
+ +
+{/if} diff --git a/src/lib/components/common/Modal.svelte b/src/lib/components/common/Modal.svelte index 52c6a6ee..be1abcb7 100644 --- a/src/lib/components/common/Modal.svelte +++ b/src/lib/components/common/Modal.svelte @@ -1,6 +1,6 @@ - {WEBUI_NAME} + {$WEBUI_NAME} + diff --git a/src/routes/auth/+page.svelte b/src/routes/auth/+page.svelte index 05891d4f..30c5a93e 100644 --- a/src/routes/auth/+page.svelte +++ b/src/routes/auth/+page.svelte @@ -1,8 +1,8 @@