mirror of
https://github.com/open-webui/open-webui
synced 2024-11-16 05:24:02 +00:00
Merge branch 'dev' into remove-ollama
This commit is contained in:
commit
670f28d694
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -29,9 +29,11 @@ assignees: ''
|
||||
- [ ] I have provided the exact steps to reproduce the bug in the "Steps to Reproduce" section below.
|
||||
|
||||
## Expected Behavior:
|
||||
|
||||
[Describe what you expected to happen.]
|
||||
|
||||
## Actual Behavior:
|
||||
|
||||
[Describe what actually happened.]
|
||||
|
||||
## Description
|
||||
|
22
CHANGELOG.md
22
CHANGELOG.md
@ -5,6 +5,28 @@ 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.3.12] - 2024-08-07
|
||||
|
||||
### Added
|
||||
|
||||
- **🔄 Sidebar Infinite Scroll**: Added an infinite scroll feature in the sidebar for more efficient chat navigation, reducing load times and enhancing user experience.
|
||||
- **🚀 Enhanced Markdown Rendering**: Support for rendering all code blocks and making images clickable for preview; codespan styling is also enhanced to improve readability and user interaction.
|
||||
- **🔒 Admin Shared Chat Visibility**: Admins no longer have default visibility over shared chats when ENABLE_ADMIN_CHAT_ACCESS is set to false, tightening security and privacy settings for users.
|
||||
- **🌍 Language Updates**: Added Malay (Bahasa Malaysia) translation and updated Catalan and Traditional Chinese translations to improve accessibility for more users.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **📊 Markdown Rendering Issues**: Resolved issues with markdown rendering to ensure consistent and correct display across components.
|
||||
- **🛠️ Styling Issues**: Multiple fixes applied to styling throughout the application, improving the overall visual experience and interface consistency.
|
||||
- **🗃️ Modal Handling**: Fixed an issue where modals were not closing correctly in various model chat scenarios, enhancing usability and interface reliability.
|
||||
- **📄 Missing OpenAI Usage Information**: Resolved issues where usage statistics for OpenAI services were not being correctly displayed, ensuring users have access to crucial data for managing and monitoring their API consumption.
|
||||
- **🔧 Non-Streaming Support for Functions Plugin**: Fixed a functionality issue with the Functions plugin where non-streaming operations were not functioning as intended, restoring full capabilities for async and sync integration within the platform.
|
||||
- **🔄 Environment Variable Type Correction (COMFYUI_FLUX_FP8_CLIP)**: Corrected the data type of the 'COMFYUI_FLUX_FP8_CLIP' environment variable from string to boolean, ensuring environment settings apply correctly and enhance configuration management.
|
||||
|
||||
### Changed
|
||||
|
||||
- **🔧 Backend Dependency Updates**: Updated several backend dependencies such as boto3, pypdf, python-pptx, validators, and black, ensuring up-to-date security and performance optimizations.
|
||||
|
||||
## [0.3.11] - 2024-08-02
|
||||
|
||||
### Added
|
||||
|
@ -359,8 +359,10 @@ async def generate_chat_completion(
|
||||
):
|
||||
idx = 0
|
||||
payload = {**form_data}
|
||||
payload.pop("metadata", None)
|
||||
|
||||
|
||||
if "metadata" in payload:
|
||||
del payload["metadata"]
|
||||
|
||||
model_id = form_data.get("model")
|
||||
model_info = Models.get_model_by_id(model_id)
|
||||
|
||||
|
@ -46,6 +46,7 @@ from config import (
|
||||
AppConfig,
|
||||
OAUTH_USERNAME_CLAIM,
|
||||
OAUTH_PICTURE_CLAIM,
|
||||
OAUTH_EMAIL_CLAIM,
|
||||
)
|
||||
|
||||
from apps.socket.main import get_event_call, get_event_emitter
|
||||
@ -84,6 +85,7 @@ app.state.config.ENABLE_COMMUNITY_SHARING = ENABLE_COMMUNITY_SHARING
|
||||
|
||||
app.state.config.OAUTH_USERNAME_CLAIM = OAUTH_USERNAME_CLAIM
|
||||
app.state.config.OAUTH_PICTURE_CLAIM = OAUTH_PICTURE_CLAIM
|
||||
app.state.config.OAUTH_EMAIL_CLAIM = OAUTH_EMAIL_CLAIM
|
||||
|
||||
app.state.MODELS = {}
|
||||
app.state.TOOLS = {}
|
||||
|
@ -433,6 +433,12 @@ OAUTH_PICTURE_CLAIM = PersistentConfig(
|
||||
os.environ.get("OAUTH_PICTURE_CLAIM", "picture"),
|
||||
)
|
||||
|
||||
OAUTH_EMAIL_CLAIM = PersistentConfig(
|
||||
"OAUTH_EMAIL_CLAIM",
|
||||
"oauth.oidc.email_claim",
|
||||
os.environ.get("OAUTH_EMAIL_CLAIM", "email"),
|
||||
)
|
||||
|
||||
|
||||
def load_oauth_providers():
|
||||
OAUTH_PROVIDERS.clear()
|
||||
|
@ -2158,7 +2158,8 @@ async def oauth_callback(provider: str, request: Request, response: Response):
|
||||
log.warning(f"OAuth callback failed, sub is missing: {user_data}")
|
||||
raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
|
||||
provider_sub = f"{provider}@{sub}"
|
||||
email = user_data.get("email", "").lower()
|
||||
email_claim = webui_app.state.config.OAUTH_EMAIL_CLAIM
|
||||
email = user_data.get(email_claim, "").lower()
|
||||
# We currently mandate that email addresses are provided
|
||||
if not email:
|
||||
log.warning(f"OAuth callback failed, email is missing: {user_data}")
|
||||
|
@ -32,4 +32,5 @@ We regularly audit our internal processes and system architecture for vulnerabil
|
||||
For immediate concerns or detailed reports that meet our guidelines, please create an issue in our [issue tracker](/open-webui/open-webui/issues) or contact us on [Discord](https://discord.gg/5rJgQTnV4s).
|
||||
|
||||
---
|
||||
_Last updated on **2024-08-06**._
|
||||
|
||||
_Last updated on **2024-08-06**._
|
||||
|
964
package-lock.json
generated
964
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
37
package.json
37
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "open-webui",
|
||||
"version": "0.3.11",
|
||||
"version": "0.3.12",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm run pyodide:fetch && vite dev --host",
|
||||
@ -20,30 +20,31 @@
|
||||
"pyodide:fetch": "node scripts/prepare-pyodide.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^2.0.0",
|
||||
"@sveltejs/adapter-static": "^2.0.3",
|
||||
"@sveltejs/kit": "^1.30.0",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@sveltejs/adapter-auto": "3.2.2",
|
||||
"@sveltejs/adapter-static": "^3.0.2",
|
||||
"@sveltejs/kit": "^2.5.20",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
||||
"@tailwindcss/typography": "^0.5.13",
|
||||
"@types/bun": "latest",
|
||||
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
||||
"@typescript-eslint/parser": "^6.17.0",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"cypress": "^13.8.1",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-cypress": "^3.0.2",
|
||||
"eslint-plugin-svelte": "^2.30.0",
|
||||
"i18next-parser": "^8.13.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-cypress": "^3.4.0",
|
||||
"eslint-plugin-svelte": "^2.43.0",
|
||||
"i18next-parser": "^9.0.1",
|
||||
"postcss": "^8.4.31",
|
||||
"prettier": "^2.8.0",
|
||||
"prettier-plugin-svelte": "^2.10.1",
|
||||
"svelte": "^4.0.5",
|
||||
"svelte-check": "^3.4.3",
|
||||
"prettier": "^3.3.3",
|
||||
"prettier-plugin-svelte": "^3.2.6",
|
||||
"svelte": "^4.2.18",
|
||||
"svelte-check": "^3.8.5",
|
||||
"svelte-confetti": "^1.3.2",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^4.4.2",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^5.3.5",
|
||||
"vitest": "^1.6.0"
|
||||
},
|
||||
"type": "module",
|
||||
@ -52,7 +53,7 @@
|
||||
"@codemirror/lang-python": "^6.1.6",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@pyscript/core": "^0.4.32",
|
||||
"@sveltejs/adapter-node": "^1.3.1",
|
||||
"@sveltejs/adapter-node": "^2.0.0",
|
||||
"async": "^3.2.5",
|
||||
"bits-ui": "^0.19.7",
|
||||
"codemirror": "^6.0.1",
|
||||
@ -77,5 +78,9 @@
|
||||
"tippy.js": "^6.3.7",
|
||||
"turndown": "^7.2.0",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.13.0 <=21.x.x",
|
||||
"npm": ">=6.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ dependencies = [
|
||||
|
||||
"openai",
|
||||
"anthropic",
|
||||
"google-generativeai==0.5.4",
|
||||
"google-generativeai==0.7.2",
|
||||
"tiktoken",
|
||||
|
||||
"langchain==0.2.11",
|
||||
|
@ -57,13 +57,13 @@ beautifulsoup4==4.12.3
|
||||
# via unstructured
|
||||
bidict==0.23.1
|
||||
# via python-socketio
|
||||
black==24.4.2
|
||||
black==24.8.0
|
||||
# via open-webui
|
||||
blinker==1.8.2
|
||||
# via flask
|
||||
boto3==1.34.110
|
||||
boto3==1.34.153
|
||||
# via open-webui
|
||||
botocore==1.34.110
|
||||
botocore==1.34.155
|
||||
# via boto3
|
||||
# via s3transfer
|
||||
build==1.2.1
|
||||
@ -179,7 +179,7 @@ frozenlist==1.4.1
|
||||
fsspec==2024.3.1
|
||||
# via huggingface-hub
|
||||
# via torch
|
||||
google-ai-generativelanguage==0.6.4
|
||||
google-ai-generativelanguage==0.6.6
|
||||
# via google-generativeai
|
||||
google-api-core==2.19.0
|
||||
# via google-ai-generativelanguage
|
||||
@ -196,7 +196,7 @@ google-auth==2.29.0
|
||||
# via kubernetes
|
||||
google-auth-httplib2==0.2.0
|
||||
# via google-api-python-client
|
||||
google-generativeai==0.5.4
|
||||
google-generativeai==0.7.2
|
||||
# via open-webui
|
||||
googleapis-common-protos==1.63.0
|
||||
# via google-api-core
|
||||
@ -502,7 +502,7 @@ pypandoc==1.13
|
||||
pyparsing==2.4.7
|
||||
# via httplib2
|
||||
# via oletools
|
||||
pypdf==4.2.0
|
||||
pypdf==4.3.1
|
||||
# via open-webui
|
||||
# via unstructured-client
|
||||
pypika==0.48.9
|
||||
@ -533,7 +533,7 @@ python-magic==0.4.27
|
||||
python-multipart==0.0.9
|
||||
# via fastapi
|
||||
# via open-webui
|
||||
python-pptx==0.6.23
|
||||
python-pptx==1.0.0
|
||||
# via open-webui
|
||||
python-socketio==5.11.3
|
||||
# via open-webui
|
||||
@ -684,6 +684,7 @@ typing-extensions==4.11.0
|
||||
# via opentelemetry-sdk
|
||||
# via pydantic
|
||||
# via pydantic-core
|
||||
# via python-pptx
|
||||
# via sqlalchemy
|
||||
# via torch
|
||||
# via typer
|
||||
@ -718,7 +719,7 @@ uvicorn==0.22.0
|
||||
# via open-webui
|
||||
uvloop==0.19.0
|
||||
# via uvicorn
|
||||
validators==0.28.1
|
||||
validators==0.33.0
|
||||
# via open-webui
|
||||
watchfiles==0.21.0
|
||||
# via uvicorn
|
||||
|
@ -57,13 +57,13 @@ beautifulsoup4==4.12.3
|
||||
# via unstructured
|
||||
bidict==0.23.1
|
||||
# via python-socketio
|
||||
black==24.4.2
|
||||
black==24.8.0
|
||||
# via open-webui
|
||||
blinker==1.8.2
|
||||
# via flask
|
||||
boto3==1.34.110
|
||||
boto3==1.34.153
|
||||
# via open-webui
|
||||
botocore==1.34.110
|
||||
botocore==1.34.155
|
||||
# via boto3
|
||||
# via s3transfer
|
||||
build==1.2.1
|
||||
@ -179,7 +179,7 @@ frozenlist==1.4.1
|
||||
fsspec==2024.3.1
|
||||
# via huggingface-hub
|
||||
# via torch
|
||||
google-ai-generativelanguage==0.6.4
|
||||
google-ai-generativelanguage==0.6.6
|
||||
# via google-generativeai
|
||||
google-api-core==2.19.0
|
||||
# via google-ai-generativelanguage
|
||||
@ -196,7 +196,7 @@ google-auth==2.29.0
|
||||
# via kubernetes
|
||||
google-auth-httplib2==0.2.0
|
||||
# via google-api-python-client
|
||||
google-generativeai==0.5.4
|
||||
google-generativeai==0.7.2
|
||||
# via open-webui
|
||||
googleapis-common-protos==1.63.0
|
||||
# via google-api-core
|
||||
@ -502,7 +502,7 @@ pypandoc==1.13
|
||||
pyparsing==2.4.7
|
||||
# via httplib2
|
||||
# via oletools
|
||||
pypdf==4.2.0
|
||||
pypdf==4.3.1
|
||||
# via open-webui
|
||||
# via unstructured-client
|
||||
pypika==0.48.9
|
||||
@ -533,7 +533,7 @@ python-magic==0.4.27
|
||||
python-multipart==0.0.9
|
||||
# via fastapi
|
||||
# via open-webui
|
||||
python-pptx==0.6.23
|
||||
python-pptx==1.0.0
|
||||
# via open-webui
|
||||
python-socketio==5.11.3
|
||||
# via open-webui
|
||||
@ -684,6 +684,7 @@ typing-extensions==4.11.0
|
||||
# via opentelemetry-sdk
|
||||
# via pydantic
|
||||
# via pydantic-core
|
||||
# via python-pptx
|
||||
# via sqlalchemy
|
||||
# via torch
|
||||
# via typer
|
||||
@ -718,7 +719,7 @@ uvicorn==0.22.0
|
||||
# via open-webui
|
||||
uvloop==0.19.0
|
||||
# via uvicorn
|
||||
validators==0.28.1
|
||||
validators==0.33.0
|
||||
# via open-webui
|
||||
watchfiles==0.21.0
|
||||
# via uvicorn
|
||||
|
@ -69,6 +69,7 @@ type ChatCompletedForm = {
|
||||
model: string;
|
||||
messages: string[];
|
||||
chat_id: string;
|
||||
session_id: string;
|
||||
};
|
||||
|
||||
export const chatCompleted = async (token: string, body: ChatCompletedForm) => {
|
||||
|
@ -118,47 +118,47 @@
|
||||
currentMessageId = message.id;
|
||||
let messageId = message.id;
|
||||
console.log(messageId);
|
||||
|
||||
//
|
||||
let messageChildrenIds = history.messages[messageId].childrenIds;
|
||||
while (messageChildrenIds.length !== 0) {
|
||||
messageId = messageChildrenIds.at(-1);
|
||||
messageChildrenIds = history.messages[messageId].childrenIds;
|
||||
}
|
||||
|
||||
history.currentId = messageId;
|
||||
dispatch('change');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ResponseMessage
|
||||
message={groupedMessages[model].messages[groupedMessagesIdx[model]]}
|
||||
siblings={groupedMessages[model].messages.map((m) => m.id)}
|
||||
isLastMessage={true}
|
||||
{updateChatMessages}
|
||||
{confirmEditResponseMessage}
|
||||
showPreviousMessage={() => showPreviousMessage(model)}
|
||||
showNextMessage={() => showNextMessage(model)}
|
||||
{readOnly}
|
||||
{rateMessage}
|
||||
{copyToClipboard}
|
||||
{continueGeneration}
|
||||
regenerateResponse={async (message) => {
|
||||
regenerateResponse(message);
|
||||
await tick();
|
||||
groupedMessagesIdx[model] = groupedMessages[model].messages.length - 1;
|
||||
}}
|
||||
on:save={async (e) => {
|
||||
console.log('save', e);
|
||||
{#key history.currentId}
|
||||
<ResponseMessage
|
||||
message={groupedMessages[model].messages[groupedMessagesIdx[model]]}
|
||||
siblings={groupedMessages[model].messages.map((m) => m.id)}
|
||||
isLastMessage={true}
|
||||
{updateChatMessages}
|
||||
{confirmEditResponseMessage}
|
||||
showPreviousMessage={() => showPreviousMessage(model)}
|
||||
showNextMessage={() => showNextMessage(model)}
|
||||
{readOnly}
|
||||
{rateMessage}
|
||||
{copyToClipboard}
|
||||
{continueGeneration}
|
||||
regenerateResponse={async (message) => {
|
||||
regenerateResponse(message);
|
||||
await tick();
|
||||
groupedMessagesIdx[model] = groupedMessages[model].messages.length - 1;
|
||||
}}
|
||||
on:save={async (e) => {
|
||||
console.log('save', e);
|
||||
|
||||
const message = e.detail;
|
||||
history.messages[message.id] = message;
|
||||
await updateChatById(localStorage.token, chatId, {
|
||||
messages: messages,
|
||||
history: history
|
||||
});
|
||||
}}
|
||||
/>
|
||||
const message = e.detail;
|
||||
history.messages[message.id] = message;
|
||||
await updateChatById(localStorage.token, chatId, {
|
||||
messages: messages,
|
||||
history: history
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
|
@ -125,91 +125,6 @@
|
||||
code={revertSanitizedResponseContent(token?.text ?? '')}
|
||||
/>
|
||||
{/if}
|
||||
<!-- {:else if token.type === 'heading'}
|
||||
<svelte:element this={headerComponent(token.depth)}>
|
||||
<MarkdownInlineTokens id={`${id}-${tokenIdx}-h`} tokens={token.tokens} />
|
||||
</svelte:element>
|
||||
{:else if token.type === 'hr'}
|
||||
<hr />
|
||||
{:else if token.type === 'blockquote'}
|
||||
<blockquote>
|
||||
<svelte:self id={`${id}-${tokenIdx}`} tokens={token.tokens} />
|
||||
</blockquote>
|
||||
{:else if token.type === 'html'}
|
||||
{@html token.text}
|
||||
{:else if token.type === 'paragraph'}
|
||||
<p>
|
||||
<MarkdownInlineTokens id={`${id}-${tokenIdx}-p`} tokens={token.tokens ?? []} />
|
||||
</p>
|
||||
{:else if token.type === 'list'}
|
||||
{#if token.ordered}
|
||||
<ol start={token.start || 1}>
|
||||
{#each token.items as item, itemIdx}
|
||||
<li>
|
||||
<svelte:self
|
||||
id={`${id}-${tokenIdx}-${itemIdx}`}
|
||||
tokens={item.tokens}
|
||||
top={token.loose}
|
||||
/>
|
||||
</li>
|
||||
{/each}
|
||||
</ol>
|
||||
{:else}
|
||||
<ul>
|
||||
{#each token.items as item, itemIdx}
|
||||
<li>
|
||||
<svelte:self
|
||||
id={`${id}-${tokenIdx}-${itemIdx}`}
|
||||
tokens={item.tokens}
|
||||
top={token.loose}
|
||||
/>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
{:else if token.type === 'table'}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{#each token.header as header, headerIdx}
|
||||
<th style={token.align[headerIdx] ? '' : `text-align: ${token.align[headerIdx]}`}>
|
||||
<MarkdownInlineTokens
|
||||
id={`${id}-${tokenIdx}-header-${headerIdx}`}
|
||||
tokens={header.tokens}
|
||||
/>
|
||||
</th>
|
||||
{/each}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each token.rows as row, rowIdx}
|
||||
<tr>
|
||||
{#each row ?? [] as cell, cellIdx}
|
||||
<td style={token.align[cellIdx] ? '' : `text-align: ${token.align[cellIdx]}`}>
|
||||
<MarkdownInlineTokens
|
||||
id={`${id}-${tokenIdx}-row-${rowIdx}-${cellIdx}`}
|
||||
tokens={cell.tokens}
|
||||
/>
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
{:else if token.type === 'text'} -->
|
||||
<!-- {#if top}
|
||||
<p>
|
||||
{#if token.tokens}
|
||||
<MarkdownInlineTokens id={`${id}-${tokenIdx}-t`} tokens={token.tokens} />
|
||||
{:else}
|
||||
{unescapeHtml(token.text)}
|
||||
{/if}
|
||||
</p>
|
||||
{:else if token.tokens}
|
||||
<MarkdownInlineTokens id={`${id}-${tokenIdx}-p`} tokens={token.tokens ?? []} />
|
||||
{:else}
|
||||
{unescapeHtml(token.text)}
|
||||
{/if} -->
|
||||
{:else}
|
||||
{@html marked.parse(token.raw, {
|
||||
...defaults,
|
||||
|
@ -111,6 +111,10 @@
|
||||
|
||||
if ($config) {
|
||||
const _socket = io(`${WEBUI_BASE_URL}` || undefined, {
|
||||
reconnection: true,
|
||||
reconnectionDelay: 1000,
|
||||
reconnectionDelayMax: 5000,
|
||||
randomizationFactor: 0.5,
|
||||
path: '/ws/socket.io',
|
||||
auth: { token: localStorage.token }
|
||||
});
|
||||
@ -119,6 +123,21 @@
|
||||
console.log('connected');
|
||||
});
|
||||
|
||||
_socket.on('reconnect_attempt', (attempt) => {
|
||||
console.log('reconnect_attempt', attempt);
|
||||
});
|
||||
|
||||
_socket.on('reconnect_failed', () => {
|
||||
console.log('reconnect_failed');
|
||||
});
|
||||
|
||||
_socket.on('disconnect', (reason, details) => {
|
||||
console.log(`Socket ${socket.id} disconnected due to ${reason}`);
|
||||
if (details) {
|
||||
console.log('Additional details:', details);
|
||||
}
|
||||
});
|
||||
|
||||
await socket.set(_socket);
|
||||
|
||||
_socket.on('user-count', (data) => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import adapter from '@sveltejs/adapter-static';
|
||||
import { vitePreprocess } from '@sveltejs/kit/vite';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
|
Loading…
Reference in New Issue
Block a user