forked from open-webui/open-webui
Merge pull request #30 from ollama-webui/dev
feat: server url edit & markdown styling added
This commit is contained in:
commit
886c19246f
7 changed files with 316 additions and 51 deletions
28
README.md
28
README.md
|
@ -117,6 +117,34 @@ docker run -d -p 3000:8080 --name ollama-webui --restart always ollama-webui
|
||||||
caddy run --envfile .env --config ./Caddyfile.localhost
|
caddy run --envfile .env --config ./Caddyfile.localhost
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Connection Errors
|
||||||
|
|
||||||
|
If you encounter difficulties connecting to the Ollama server, please follow these steps to diagnose and resolve the issue:
|
||||||
|
|
||||||
|
**1. Verify Ollama Server Configuration**
|
||||||
|
|
||||||
|
Ensure that the Ollama server is properly configured to accept incoming connections from all origins. To do this, make sure the server is launched with the `OLLAMA_ORIGINS=*` environment variable, as shown in the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
OLLAMA_HOST=0.0.0.0 OLLAMA_ORIGINS=* ollama serve
|
||||||
|
```
|
||||||
|
|
||||||
|
This configuration allows Ollama to accept connections from any source.
|
||||||
|
|
||||||
|
**2. Check Ollama URL Format**
|
||||||
|
|
||||||
|
Ensure that the Ollama URL is correctly formatted in the application settings. Follow these steps:
|
||||||
|
|
||||||
|
- Go to "Settings" within the Ollama WebUI.
|
||||||
|
- Navigate to the "General" section.
|
||||||
|
- Verify that the Ollama URL is in the following format: `http://localhost:11434/api`.
|
||||||
|
|
||||||
|
It is crucial to include the `/api` at the end of the URL to ensure that the Ollama Web UI can communicate with the server.
|
||||||
|
|
||||||
|
By following these troubleshooting steps, you should be able to identify and resolve connection issues with your Ollama server configuration. If you require further assistance or have additional questions, please don't hesitate to reach out or refer to our documentation for comprehensive guidance.
|
||||||
|
|
||||||
## What's Next? 🚀
|
## What's Next? 🚀
|
||||||
|
|
||||||
### To-Do List 📝
|
### To-Do List 📝
|
||||||
|
|
77
package-lock.json
generated
77
package-lock.json
generated
|
@ -21,6 +21,7 @@
|
||||||
"@sveltejs/adapter-auto": "^2.0.0",
|
"@sveltejs/adapter-auto": "^2.0.0",
|
||||||
"@sveltejs/adapter-static": "^2.0.3",
|
"@sveltejs/adapter-static": "^2.0.3",
|
||||||
"@sveltejs/kit": "^1.20.4",
|
"@sveltejs/kit": "^1.20.4",
|
||||||
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||||
"@typescript-eslint/parser": "^6.0.0",
|
"@typescript-eslint/parser": "^6.0.0",
|
||||||
"autoprefixer": "^10.4.16",
|
"autoprefixer": "^10.4.16",
|
||||||
|
@ -838,6 +839,34 @@
|
||||||
"vite": "^4.0.0"
|
"vite": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tailwindcss/typography": {
|
||||||
|
"version": "0.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz",
|
||||||
|
"integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"lodash.castarray": "^4.4.0",
|
||||||
|
"lodash.isplainobject": "^4.0.6",
|
||||||
|
"lodash.merge": "^4.6.2",
|
||||||
|
"postcss-selector-parser": "6.0.10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"tailwindcss": ">=3.0.0 || insiders"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": {
|
||||||
|
"version": "6.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
|
||||||
|
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"cssesc": "^3.0.0",
|
||||||
|
"util-deprecate": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/cookie": {
|
"node_modules/@types/cookie": {
|
||||||
"version": "0.5.2",
|
"version": "0.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.2.tgz",
|
||||||
|
@ -2603,6 +2632,18 @@
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.castarray": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/lodash.isplainobject": {
|
||||||
|
"version": "4.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||||
|
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/lodash.merge": {
|
"node_modules/lodash.merge": {
|
||||||
"version": "4.6.2",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||||
|
@ -4670,6 +4711,30 @@
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@tailwindcss/typography": {
|
||||||
|
"version": "0.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz",
|
||||||
|
"integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"lodash.castarray": "^4.4.0",
|
||||||
|
"lodash.isplainobject": "^4.0.6",
|
||||||
|
"lodash.merge": "^4.6.2",
|
||||||
|
"postcss-selector-parser": "6.0.10"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"postcss-selector-parser": {
|
||||||
|
"version": "6.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
|
||||||
|
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"cssesc": "^3.0.0",
|
||||||
|
"util-deprecate": "^1.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/cookie": {
|
"@types/cookie": {
|
||||||
"version": "0.5.2",
|
"version": "0.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.2.tgz",
|
||||||
|
@ -5909,6 +5974,18 @@
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
},
|
},
|
||||||
|
"lodash.castarray": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"lodash.isplainobject": {
|
||||||
|
"version": "4.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||||
|
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"lodash.merge": {
|
"lodash.merge": {
|
||||||
"version": "4.6.2",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
"@sveltejs/adapter-auto": "^2.0.0",
|
"@sveltejs/adapter-auto": "^2.0.0",
|
||||||
"@sveltejs/adapter-static": "^2.0.3",
|
"@sveltejs/adapter-static": "^2.0.3",
|
||||||
"@sveltejs/kit": "^1.20.4",
|
"@sveltejs/kit": "^1.20.4",
|
||||||
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||||
"@typescript-eslint/parser": "^6.0.0",
|
"@typescript-eslint/parser": "^6.0.0",
|
||||||
"autoprefixer": "^10.4.16",
|
"autoprefixer": "^10.4.16",
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Modal from '../common/Modal.svelte';
|
import Modal from '../common/Modal.svelte';
|
||||||
|
|
||||||
import { API_BASE_URL } from '$lib/constants';
|
import { API_BASE_URL as BUILD_TIME_API_BASE_URL } from '$lib/constants';
|
||||||
import toast from 'svelte-french-toast';
|
import toast from 'svelte-french-toast';
|
||||||
|
|
||||||
export let show = false;
|
export let show = false;
|
||||||
export let saveSettings: Function;
|
export let saveSettings: Function;
|
||||||
export let getModelTags: Function;
|
export let getModelTags: Function;
|
||||||
|
|
||||||
|
let API_BASE_URL = BUILD_TIME_API_BASE_URL;
|
||||||
let system = '';
|
let system = '';
|
||||||
let temperature = 0.8;
|
let temperature = 0.8;
|
||||||
|
|
||||||
|
@ -33,6 +34,22 @@
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const checkOllamaConnection = async () => {
|
||||||
|
if (API_BASE_URL === '') {
|
||||||
|
API_BASE_URL = BUILD_TIME_API_BASE_URL;
|
||||||
|
}
|
||||||
|
const res = await getModelTags(API_BASE_URL);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
toast.success('Server connection verified');
|
||||||
|
saveSettings(
|
||||||
|
API_BASE_URL,
|
||||||
|
system != '' ? system : null,
|
||||||
|
temperature != 0.8 ? temperature : null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const pullModelHandler = async () => {
|
const pullModelHandler = async () => {
|
||||||
const res = await fetch(`${API_BASE_URL}/pull`, {
|
const res = await fetch(`${API_BASE_URL}/pull`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -139,6 +156,7 @@
|
||||||
|
|
||||||
$: if (show) {
|
$: if (show) {
|
||||||
let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
|
let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
|
||||||
|
API_BASE_URL = settings.API_BASE_URL ?? BUILD_TIME_API_BASE_URL;
|
||||||
system = settings.system ?? '';
|
system = settings.system ?? '';
|
||||||
temperature = settings.temperature ?? 0.8;
|
temperature = settings.temperature ?? 0.8;
|
||||||
}
|
}
|
||||||
|
@ -227,6 +245,50 @@
|
||||||
<div class="flex-1 md:min-h-[300px]">
|
<div class="flex-1 md:min-h-[300px]">
|
||||||
{#if selectedMenu === 'general'}
|
{#if selectedMenu === 'general'}
|
||||||
<div class="flex flex-col space-y-3">
|
<div class="flex flex-col space-y-3">
|
||||||
|
<div>
|
||||||
|
<div class=" mb-2.5 text-sm font-medium">Ollama Server URL</div>
|
||||||
|
<div class="flex w-full">
|
||||||
|
<div class="flex-1 mr-2">
|
||||||
|
<input
|
||||||
|
class="w-full rounded py-2 px-4 text-sm text-gray-300 bg-gray-800 outline-none"
|
||||||
|
placeholder="Enter URL (e.g. http://localhost:11434/api)"
|
||||||
|
bind:value={API_BASE_URL}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="px-3 bg-gray-600 hover:bg-gray-700 rounded transition"
|
||||||
|
on:click={() => {
|
||||||
|
checkOllamaConnection();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<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="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-2 text-xs text-gray-500">
|
||||||
|
Trouble accessing Ollama? <a
|
||||||
|
class=" text-gray-300 font-medium"
|
||||||
|
href="https://github.com/ollama-webui/ollama-webui#troubleshooting"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Click here for help.
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class=" border-gray-700" />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class=" mb-2.5 text-sm font-medium">System Prompt</div>
|
<div class=" mb-2.5 text-sm font-medium">System Prompt</div>
|
||||||
<textarea
|
<textarea
|
||||||
|
@ -260,6 +322,7 @@
|
||||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 transition rounded"
|
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 transition rounded"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
saveSettings(
|
saveSettings(
|
||||||
|
API_BASE_URL === '' ? BUILD_TIME_API_BASE_URL : API_BASE_URL,
|
||||||
system != '' ? system : null,
|
system != '' ? system : null,
|
||||||
temperature != 0.8 ? temperature : null
|
temperature != 0.8 ? temperature : null
|
||||||
);
|
);
|
||||||
|
@ -305,11 +368,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-2 text-xs text-gray-500">
|
<div class="mt-2 text-xs text-gray-500">
|
||||||
To access the available model names for downloading, click <a
|
To access the available model names for downloading, <a
|
||||||
class=" text-gray-300 font-medium"
|
class=" text-gray-300 font-medium"
|
||||||
href="https://ollama.ai/library"
|
href="https://ollama.ai/library"
|
||||||
target="_blank">here</a
|
target="_blank">click here.</a
|
||||||
>.
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if pullProgress !== ''}
|
{#if pullProgress !== ''}
|
||||||
|
|
|
@ -6,13 +6,14 @@
|
||||||
import fileSaver from 'file-saver';
|
import fileSaver from 'file-saver';
|
||||||
const { saveAs } = fileSaver;
|
const { saveAs } = fileSaver;
|
||||||
import hljs from 'highlight.js';
|
import hljs from 'highlight.js';
|
||||||
import 'highlight.js/styles/dark.min.css';
|
import 'highlight.js/styles/github-dark.min.css';
|
||||||
import { API_BASE_URL } from '$lib/constants';
|
import { API_BASE_URL as BUILD_TIME_API_BASE_URL } from '$lib/constants';
|
||||||
import { onMount, tick } from 'svelte';
|
import { onMount, tick } from 'svelte';
|
||||||
|
|
||||||
import Navbar from '$lib/components/layout/Navbar.svelte';
|
import Navbar from '$lib/components/layout/Navbar.svelte';
|
||||||
import SettingsModal from '$lib/components/chat/SettingsModal.svelte';
|
import SettingsModal from '$lib/components/chat/SettingsModal.svelte';
|
||||||
|
|
||||||
|
let API_BASE_URL = BUILD_TIME_API_BASE_URL;
|
||||||
let suggestions = ''; // $page.url.searchParams.get('suggestions');
|
let suggestions = ''; // $page.url.searchParams.get('suggestions');
|
||||||
|
|
||||||
let models = [];
|
let models = [];
|
||||||
|
@ -26,26 +27,24 @@
|
||||||
|
|
||||||
let chats = [];
|
let chats = [];
|
||||||
let chatId = uuidv4();
|
let chatId = uuidv4();
|
||||||
let title = ``;
|
let title = '';
|
||||||
let prompt = '';
|
let prompt = '';
|
||||||
let messages = [];
|
let messages = [];
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
|
||||||
|
|
||||||
|
API_BASE_URL = settings.API_BASE_URL ?? BUILD_TIME_API_BASE_URL;
|
||||||
console.log(API_BASE_URL);
|
console.log(API_BASE_URL);
|
||||||
|
system = settings.system ?? null;
|
||||||
|
temperature = settings.temperature ?? null;
|
||||||
|
|
||||||
await getModelTags();
|
await getModelTags();
|
||||||
|
|
||||||
let settings = localStorage.getItem('settings');
|
selectedModel =
|
||||||
if (settings) {
|
settings.model && models.map((model) => model.name).includes(settings.model)
|
||||||
settings = JSON.parse(settings);
|
? settings.model
|
||||||
console.log(settings);
|
: '';
|
||||||
|
|
||||||
selectedModel =
|
|
||||||
settings.model && models.map((model) => model.name).includes(settings.model)
|
|
||||||
? settings.model
|
|
||||||
: '';
|
|
||||||
system = settings.system ?? null;
|
|
||||||
temperature = settings.temperature ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
db = await openDB('Chats', 1, {
|
db = await openDB('Chats', 1, {
|
||||||
upgrade(db) {
|
upgrade(db) {
|
||||||
|
@ -117,6 +116,41 @@
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createCopyCodeBlockButton = () => {
|
||||||
|
// use a class selector if available
|
||||||
|
let blocks = document.querySelectorAll('pre');
|
||||||
|
console.log(blocks);
|
||||||
|
|
||||||
|
blocks.forEach((block) => {
|
||||||
|
// only add button if browser supports Clipboard API
|
||||||
|
|
||||||
|
if (navigator.clipboard && block.childNodes.length < 2) {
|
||||||
|
let button = document.createElement('button');
|
||||||
|
|
||||||
|
button.innerText = 'Copy Code';
|
||||||
|
block.appendChild(button);
|
||||||
|
|
||||||
|
button.addEventListener('click', async () => {
|
||||||
|
await copyCode(block, button);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function copyCode(block, button) {
|
||||||
|
let code = block.querySelector('code');
|
||||||
|
let text = code.innerText;
|
||||||
|
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
|
||||||
|
// visual feedback that task is completed
|
||||||
|
button.innerText = 'Code Copied';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
button.innerText = 'Copy Code';
|
||||||
|
}, 700);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
// Web functions
|
// Web functions
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
@ -133,21 +167,23 @@
|
||||||
toast.success('Default model updated');
|
toast.success('Default model updated');
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveSettings = (_system, _temperature) => {
|
const saveSettings = async (_api_base_url, _system, _temperature) => {
|
||||||
|
API_BASE_URL = _api_base_url;
|
||||||
system = _system;
|
system = _system;
|
||||||
temperature = _temperature;
|
temperature = _temperature;
|
||||||
|
|
||||||
let settings = localStorage.getItem('settings') ?? '{}';
|
let settings = localStorage.getItem('settings') ?? '{}';
|
||||||
if (settings) {
|
if (settings) {
|
||||||
settings = JSON.parse(settings);
|
settings = JSON.parse(settings);
|
||||||
|
|
||||||
|
settings.API_BASE_URL = API_BASE_URL;
|
||||||
settings.system = system;
|
settings.system = system;
|
||||||
settings.temperature = temperature;
|
settings.temperature = temperature;
|
||||||
localStorage.setItem('settings', JSON.stringify(settings));
|
localStorage.setItem('settings', JSON.stringify(settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(settings);
|
console.log(settings);
|
||||||
|
await getModelTags();
|
||||||
console.log('saved');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createNewChat = () => {
|
const createNewChat = () => {
|
||||||
|
@ -163,7 +199,10 @@
|
||||||
settings = JSON.parse(settings);
|
settings = JSON.parse(settings);
|
||||||
console.log(settings);
|
console.log(settings);
|
||||||
|
|
||||||
selectedModel = settings.model ?? selectedModel;
|
selectedModel =
|
||||||
|
settings.model && models.map((model) => model.name).includes(settings.model)
|
||||||
|
? settings.model
|
||||||
|
: '';
|
||||||
system = settings.system ?? system;
|
system = settings.system ?? system;
|
||||||
temperature = settings.temperature ?? temperature;
|
temperature = settings.temperature ?? temperature;
|
||||||
}
|
}
|
||||||
|
@ -172,12 +211,18 @@
|
||||||
|
|
||||||
const loadChat = async (id) => {
|
const loadChat = async (id) => {
|
||||||
const chat = await db.get('chats', id);
|
const chat = await db.get('chats', id);
|
||||||
messages = chat.messages;
|
if (chatId !== chat.id) {
|
||||||
title = chat.title;
|
messages = chat.messages;
|
||||||
chatId = chat.id;
|
title = chat.title;
|
||||||
selectedModel = chat.model ?? selectedModel;
|
chatId = chat.id;
|
||||||
system = chat.system ?? system;
|
selectedModel = chat.model ?? selectedModel;
|
||||||
temperature = chat.temperature ?? temperature;
|
system = chat.system ?? system;
|
||||||
|
temperature = chat.temperature ?? temperature;
|
||||||
|
|
||||||
|
await tick();
|
||||||
|
hljs.highlightAll();
|
||||||
|
createCopyCodeBlockButton();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteChatHistory = async () => {
|
const deleteChatHistory = async () => {
|
||||||
|
@ -219,8 +264,8 @@
|
||||||
// Ollama functions
|
// Ollama functions
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
|
||||||
const getModelTags = async () => {
|
const getModelTags = async (url = null) => {
|
||||||
const res = await fetch(`${API_BASE_URL}/tags`, {
|
const res = await fetch(`${url === null ? API_BASE_URL : url}/tags`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
|
@ -233,11 +278,13 @@
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return { models: [] };
|
toast.error('Server connection failed');
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(res);
|
console.log(res);
|
||||||
models = res.models ?? [];
|
models = res?.models ?? [];
|
||||||
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitPrompt = async (user_prompt) => {
|
const submitPrompt = async (user_prompt) => {
|
||||||
|
@ -334,6 +381,7 @@
|
||||||
responseMessage.context = data.context;
|
responseMessage.context = data.context;
|
||||||
messages = messages;
|
messages = messages;
|
||||||
hljs.highlightAll();
|
hljs.highlightAll();
|
||||||
|
createCopyCodeBlockButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,24 +389,25 @@
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
window.scrollTo({ top: document.body.scrollHeight });
|
||||||
|
|
||||||
|
await db.put('chats', {
|
||||||
|
id: chatId,
|
||||||
|
title: title === '' ? 'New Chat' : title,
|
||||||
|
model: selectedModel,
|
||||||
|
system: system,
|
||||||
|
options: {
|
||||||
|
temperature: temperature
|
||||||
|
},
|
||||||
|
timestamp: Date.now(),
|
||||||
|
messages: messages
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
window.scrollTo({ top: document.body.scrollHeight });
|
||||||
|
|
||||||
if (messages.length == 2) {
|
if (messages.length == 2) {
|
||||||
await generateTitle(user_prompt);
|
await generateTitle(chatId, user_prompt);
|
||||||
}
|
}
|
||||||
await db.put('chats', {
|
|
||||||
id: chatId,
|
|
||||||
title: title,
|
|
||||||
model: selectedModel,
|
|
||||||
system: system,
|
|
||||||
options: {
|
|
||||||
temperature: temperature
|
|
||||||
},
|
|
||||||
timestamp: Date.now(),
|
|
||||||
messages: messages
|
|
||||||
});
|
|
||||||
chats = await db.getAllFromIndex('chats', 'timestamp');
|
chats = await db.getAllFromIndex('chats', 'timestamp');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -430,6 +479,7 @@
|
||||||
responseMessage.context = data.context;
|
responseMessage.context = data.context;
|
||||||
messages = messages;
|
messages = messages;
|
||||||
hljs.highlightAll();
|
hljs.highlightAll();
|
||||||
|
createCopyCodeBlockButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -442,7 +492,7 @@
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
window.scrollTo({ top: document.body.scrollHeight });
|
||||||
await db.put('chats', {
|
await db.put('chats', {
|
||||||
id: chatId,
|
id: chatId,
|
||||||
title: title,
|
title: title === '' ? 'New Chat' : title,
|
||||||
model: selectedModel,
|
model: selectedModel,
|
||||||
system: system,
|
system: system,
|
||||||
options: {
|
options: {
|
||||||
|
@ -457,7 +507,7 @@
|
||||||
console.log(messages);
|
console.log(messages);
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateTitle = async (user_prompt) => {
|
const generateTitle = async (_chatId, user_prompt) => {
|
||||||
console.log('generateTitle');
|
console.log('generateTitle');
|
||||||
|
|
||||||
const res = await fetch(`${API_BASE_URL}/generate`, {
|
const res = await fetch(`${API_BASE_URL}/generate`, {
|
||||||
|
@ -482,7 +532,11 @@
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
console.log(res);
|
console.log(res);
|
||||||
title = res.response;
|
const chat = await db.get('chats', _chatId);
|
||||||
|
await db.put('chats', { ...chat, title: res.response === '' ? 'New Chat' : res.response });
|
||||||
|
if (chat.id === chatId) {
|
||||||
|
title = res.response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -609,7 +663,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="markdown-body whitespace-pre-line">
|
<div
|
||||||
|
class="prose max-w-full prose-invert prose-headings:my-0 prose-p:my-0 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-img:my-0 prose-ul:-my-2 prose-ol:-my-2 prose-li:-my-2 whitespace-pre-line"
|
||||||
|
>
|
||||||
{@html marked.parse(message.content)}
|
{@html marked.parse(message.content)}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -863,4 +919,33 @@
|
||||||
clip-path: inset(0 -1ch 0 0);
|
clip-path: inset(0 -1ch 0 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pre[class*='language-'] {
|
||||||
|
position: relative;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
/* make space */
|
||||||
|
margin: 5px 0;
|
||||||
|
padding: 1.75rem 0 1.75rem 1rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*='language-'] button {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
|
||||||
|
font-size: 0.9rem;
|
||||||
|
padding: 0.15rem;
|
||||||
|
background-color: #828282;
|
||||||
|
|
||||||
|
border: ridge 1px #7b7b7c;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-shadow: #c4c4c4 0 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*='language-'] button:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #bcbabb;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -17,8 +17,19 @@ export default {
|
||||||
900: '#202123',
|
900: '#202123',
|
||||||
950: '#050509'
|
950: '#050509'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
typography: {
|
||||||
|
DEFAULT: {
|
||||||
|
css: {
|
||||||
|
pre: false,
|
||||||
|
code: false,
|
||||||
|
'pre code': false,
|
||||||
|
'code::before': false,
|
||||||
|
'code::after': false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: []
|
plugins: [require('@tailwindcss/typography')]
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue