Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: can manually (or opt-in to automatically) check for updates #159

Merged
merged 60 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
626ff57
settings: convert each section to individual components
fmaclen Aug 18, 2024
014d454
fix styling for external links
fmaclen Aug 18, 2024
72c2b42
update env variable to `PUBLIC_ADAPTER`
fmaclen Aug 18, 2024
02229ea
use ollama-js types
fmaclen Aug 19, 2024
33dc357
improve UI for update check
fmaclen Aug 19, 2024
0190126
trigger user update check
fmaclen Aug 19, 2024
86002e4
update copy
fmaclen Aug 19, 2024
e704e7d
re-arrange status messages
fmaclen Aug 19, 2024
5fbe100
add padding to button "links"
fmaclen Aug 19, 2024
bdb7691
add `isDocker` `isDesktop` flags to `$settingsStore`
fmaclen Aug 19, 2024
2f271fb
remove 'ghost' variant
fmaclen Aug 19, 2024
3034f07
set environment from data in fetch request
fmaclen Aug 19, 2024
78070bb
only check GH API version is the server isn't already updated
fmaclen Aug 19, 2024
2ab412c
Update Badge.svelte
fmaclen Aug 19, 2024
2907c71
fix linting errors
fmaclen Aug 19, 2024
9986f0a
use relative path to hit the API endpoint
fmaclen Aug 25, 2024
8f4900c
add checkbox for enabling automatic update checks
fmaclen Aug 25, 2024
5b30f6e
add autoCheckForUpdates flag in $settingsStore
fmaclen Aug 25, 2024
4f6e2e7
remove debuggers
fmaclen Aug 27, 2024
03489e2
prevent character shifting
fmaclen Aug 28, 2024
1234031
set default values for settingsStore
fmaclen Aug 28, 2024
3695e80
add versions test
fmaclen Aug 28, 2024
1602606
remove the first test and split the assertions across other tests
fmaclen Aug 28, 2024
607ab0f
add error test
fmaclen Aug 28, 2024
d9688e5
fix knowledge tests
fmaclen Aug 28, 2024
05dedbb
add server status to settingsStore
fmaclen Aug 28, 2024
d2a5a6a
fix settings tests
fmaclen Aug 28, 2024
475e48b
Update Version.svelte
fmaclen Aug 28, 2024
f47e236
apply formatting and fix linting errors
fmaclen Aug 28, 2024
e3a6505
remove undefined checks for settingsStore
fmaclen Aug 28, 2024
9be2582
move update logic to it's own file
fmaclen Aug 28, 2024
2b120d1
unskip error test
fmaclen Aug 28, 2024
41e01b5
add documentation for updating docker
fmaclen Aug 28, 2024
c65e0ff
Merge branch 'main' into version-check
fmaclen Aug 28, 2024
64f8787
update snapshots
fmaclen Aug 28, 2024
dff7305
remove hr
fmaclen Aug 28, 2024
1aea176
disabled if there are no entries in ollamaModels
fmaclen Aug 28, 2024
edf7cb4
remove all conditional checks for $settingsStore
fmaclen Aug 28, 2024
2b9c867
fix linting errors
fmaclen Aug 28, 2024
101e6cc
add sidebar badge indicator for new updates
fmaclen Aug 28, 2024
3eea06d
2 tests fails, full of debuggers
fmaclen Aug 28, 2024
47880db
cleanup tests, remove debuggers
fmaclen Aug 29, 2024
9f28584
apply formatting
fmaclen Aug 29, 2024
e29a526
add intentionally broken GH API mock
fmaclen Aug 29, 2024
f317e6b
re-enable auto-update tests
fmaclen Aug 29, 2024
819a120
remove debugger comment
fmaclen Aug 29, 2024
d52ca98
fix tailwind syntax
fmaclen Aug 29, 2024
4fb5f33
Merge branch 'main' into version-check
fmaclen Aug 29, 2024
5ce27a9
add `.badge` tailwind utility class
fmaclen Aug 30, 2024
c98d501
add i18n keys for strings in `<Version>`
fmaclen Aug 30, 2024
fe153ec
apply formatting
fmaclen Aug 30, 2024
0059224
remane tunnel key
fmaclen Aug 30, 2024
6d80c20
add comments, cleanup syntax
fmaclen Aug 30, 2024
411e19b
use more descriptive variable names
fmaclen Aug 30, 2024
4c81dc1
add i18n keys to danger zone
fmaclen Aug 30, 2024
e83a75f
better test names
fmaclen Aug 30, 2024
1c7666c
more predictable version comparison logic
fmaclen Aug 30, 2024
f9e3ebb
apply formatting
fmaclen Aug 30, 2024
f3f4f88
use more consistent variable names
fmaclen Aug 30, 2024
56215d6
remove unused async
fmaclen Aug 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ FROM node:20-alpine

WORKDIR /app

ENV ADAPTER='docker-node'
ENV PUBLIC_ADAPTER='docker-node'

COPY . .

Expand Down
20 changes: 1 addition & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,9 @@ A minimal web-UI for talking to [Ollama](https://github.com/jmorganca/ollama/) s
- ⚡️ [Live demo](https://hollama.fernando.is)
- _No sign-up required_
- 🖥️ Download for [macOS, Windows & Linux](https://github.com/fmaclen/hollama/releases)
- 🐳 [Self-hosting](#self-host-docker) with Docker
- 🐳 [Self-hosting](SELF_HOSTING.md) with Docker
- 🐞 [Contribute](CONTRIBUTING.md)

| ![session](tests/docs.test.ts-snapshots/session.png) | ![settings](tests/docs.test.ts-snapshots/settings.png) |
| ------------------------------------------------------------ | -------------------------------------------------------- |
| ![session-new](tests/docs.test.ts-snapshots/session-new.png) | ![knowledge](tests/docs.test.ts-snapshots/knowledge.png) |

### Self-host (Docker)

To host your own Hollama server, [install Docker](https://www.docker.com/products/docker-desktop/) and run the command below in your favorite terminal:

```shell
docker run --rm -d -p 4173:4173 ghcr.io/fmaclen/hollama:latest
```

Then visit [http://localhost:4173](http://localhost:4173).

#### Connecting to an Ollama server

If you are using the publicly hosted version or your Docker server is on a separate device than the Ollama server you'll have to set the domain in `OLLAMA_ORIGINS`. [Learn more in Ollama's docs](https://github.com/ollama/ollama/blob/main/docs/faq.md#how-do-i-configure-ollama-server).

```bash
OLLAMA_ORIGINS=https://hollama.fernando.is ollama serve
```
43 changes: 43 additions & 0 deletions SELF_HOSTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Self-hosting (with Docker)

- [Getting started](#getting-started)
- [Updating to the latest version](#updating-to-the-latest-version)
- [Connecting to an Ollama server hosted elsewhere](#connecting-to-an-ollama-server-hosted-elsewhere)

## Getting started

To host your own Hollama server, [install Docker](https://www.docker.com/products/docker-desktop/) and run the command below in your favorite terminal:

```shell
docker run --rm -d -p 4173:4173 --name hollama ghcr.io/fmaclen/hollama:latest
```

Then visit [http://localhost:4173](http://localhost:4173)

## Updating to the latest version

To update, first stop the container:

```shell
docker stop hollama
```

Then pull the latest version:

```shell
docker pull ghcr.io/fmaclen/hollama:latest
```

Finally, start the container again:

```shell
docker run --rm -d -p 4173:4173 --name hollama ghcr.io/fmaclen/hollama:latest
```

## Connecting to an Ollama server hosted elsewhere

If you are using the publicly hosted version or your Docker server is on a separate device than the Ollama server you'll have to set the domain in `OLLAMA_ORIGINS`. [Learn more in Ollama's docs](https://github.com/ollama/ollama/blob/main/docs/faq.md#how-do-i-configure-ollama-server).

```bash
OLLAMA_ORIGINS=https://hollama.fernando.is ollama serve
```
10 changes: 4 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"lint": "prettier --check . && eslint .",
"format": "prettier --write .",
"electron": "electron .",
"electron:build": "cross-env ADAPTER=electron-node vite build && electron-builder"
"electron:build": "cross-env PUBLIC_ADAPTER=electron-node vite build && electron-builder"
},
"devDependencies": {
"@playwright/test": "^1.43.0",
Expand Down Expand Up @@ -49,6 +49,7 @@
"prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.1.2",
"prettier-plugin-tailwindcss": "^0.5.9",
"semver": "^7.6.3",
"svelte": "^4.2.7",
"svelte-check": "^3.6.0",
"svelte-i18next": "^2.2.2",
Expand Down
8 changes: 2 additions & 6 deletions src/app.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

@layer base {
:root {
--color-primary: 14 100% 65%;
--color-primary: 14 80% 60%;

--color-shade-0: 0 0% 100%;
--color-shade-1: 0 0% 96%;
Expand All @@ -29,7 +29,7 @@
}

[data-color-theme='dark'] {
--color-primary: 14 100% 65%;
--color-primary: 14 80% 60%;

--color-shade-0: 0 0% 10%;
--color-shade-1: 0 0% 14%;
Expand All @@ -53,7 +53,3 @@
--color-positive-muted: 120 100% 10%;
}
}

a:not(.button)[target='_blank']:after {
content: ' ↗';
}
2 changes: 0 additions & 2 deletions src/lib/components/Badge.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@

<style lang="postcss">
.badge {
@apply inline-flex select-none items-center rounded-md px-2.5 py-0.5 font-mono text-xs;

&--default {
@apply border border-shade-3 text-muted;
}
Expand Down
6 changes: 5 additions & 1 deletion src/lib/components/Button.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@
}

&--link {
@apply text-link rounded-none;
@apply text-link inline rounded-none px-1;
}

&--link[target='_blank']:after {
content: ' ↗';
}

&--icon {
Expand Down
9 changes: 9 additions & 0 deletions src/lib/components/FieldHelp.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div class="field-help">
<slot />
</div>

<style lang="postcss">
.field-help {
@apply my-2 flex flex-col gap-y-1 px-0.5 text-muted;
}
</style>
8 changes: 4 additions & 4 deletions src/lib/components/FieldSelectModel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@

export let disabled: boolean = false;

let value: string = $settingsStore?.ollamaModel || '';
$: if ($settingsStore) $settingsStore.ollamaModel = value;
let value: string = $settingsStore.ollamaModel || '';
$: $settingsStore.ollamaModel = value;
</script>

<FieldSelect
label={$i18n.t('settingsPage.availableModels')}
name="model"
options={$settingsStore?.ollamaModels.map((m) => ({ value: m.name, option: m.name }))}
disabled={disabled || !$settingsStore?.ollamaModels.length}
options={$settingsStore.ollamaModels.map((m) => ({ value: m.name, option: m.name }))}
disabled={disabled || !$settingsStore.ollamaModels.length}
bind:value
/>
4 changes: 2 additions & 2 deletions src/lib/components/FieldTextEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
let editorValue: string;

// Re-render text editor when theme changes
$: if (container && $settingsStore?.userTheme) renderTextEditor();
$: if (container) renderTextEditor();

// REF https://thememirror.net/create
const hollamaThemeLight = createTheme({
Expand Down Expand Up @@ -75,7 +75,7 @@
updateValue,
EditorView.lineWrapping,
Prec.highest(overrideModEnterKeymap),
$settingsStore?.userTheme === 'dark' ? hollamaThemeDark : hollamaThemeLight
$settingsStore.userTheme === 'dark' ? hollamaThemeDark : hollamaThemeLight
],
parent: container
});
Expand Down
13 changes: 13 additions & 0 deletions src/lib/components/P.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<p class="p">
<slot />
</p>

<style lang="postcss">
.p {
@apply text-sm;

:global(strong) {
@apply font-medium leading-none;
}
}
</style>
2 changes: 2 additions & 0 deletions src/lib/github.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const GITHUB_RELEASES_API = 'https://api.github.com/repos/fmaclen/hollama/releases';
export const GITHUB_RELEASES_URL = 'https://github.com/fmaclen/hollama/releases';
32 changes: 21 additions & 11 deletions src/lib/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,19 @@ i18next.init({
success: 'Success',
error: 'Error',
pullingModel: 'Pulling model',
pullModelPlaceholder: 'Model tag (e.g. llama3.1)',
modelWasDownloaded: '{{model}} was downloaded',
automatcallyCheckForUpdates: 'Automatically check for updates',
checkNow: 'Check now',
checkingForUpdates: 'Checking for updates...',
couldntCheckForUpdates: "Couldn't check for updates automatically",
isCurrentVersionLatest: 'You are on the latest version',
isLatestVersion: 'A newer version is available',
refreshToUpdate: 'Refresh to update',
howToUpdateDocker: 'How to update Docker container?',
goToDownloads: 'Go to downloads',
goToReleases: 'Go to releases',
releaseHistory: 'Release history',
sessionsPage: {
new: 'New session',
empty: 'No sessions',
Expand Down Expand Up @@ -54,24 +66,22 @@ i18next.init({
server: 'Server',
availableModels: 'Available models',
pullModel: 'Pull model',
pullModelPlaceholder: 'Model tag (e.g. llama3.1)',
downloadModel: 'Download model',
disconnected: 'disconnected',
connected: 'connected',
dangerZone: 'Danger zone',
deleteAllSessions: 'Delete all sessions',
deleteAllKnowledge: 'Delete all knowledge',
deleteServerSettings: 'Delete server settings',
deleteAllSettings: 'Delete all settings',
version: 'Version',
allowConnections: 'Change your server settings to allow connections from',
seeDocs: 'see docs',
seeDocs: 'See docs',
checkBrowserExtensions: 'Also check no browser extensions are blocking the connection',
tryingToConnectNotLocalhost:
'If you want to connect to an Ollama server that is not available on <code class="code">localhost</code> or <code class="code">127.0.0.1</code> you will need to',
createTunnel: 'create a tunnel',
or: 'or',
allowMixedContent: 'allow mixed content',
browseModels: 'Browse the list of available models in',
'If you want to connect to an Ollama server that is not available on <code class="badge">localhost</code> or <code class="badge">127.0.0.1</code> try',
creatingTunnel: 'Creating a tunnel',
allowMixedContent: 'Allow mixed content',
browseModels: 'Browse the list of available models',
ollamaLibrary: "Ollama's library"
},
dialogs: {
Expand All @@ -81,9 +91,9 @@ i18next.init({
},
errors: {
genericError: 'Sorry, something went wrong.\n```\n${{error}}\n```',
somethingWentWrong: 'Sorry, something went wrong.',
notFound: 'The page you are looking for does not exist.',
internalServerError: 'There was an internal server error. Please try again later.',
somethingWentWrong: 'Sorry, something went wrong',
notFound: 'The page you are looking for does not exist',
internalServerError: 'There was an internal server error, please try again later',
cantConnectToOllamaServer: "Can't connect to Ollama server",
couldntConnectToOllamaServer: "Couldn't connect to Ollama server",
ollamaConnectionError: "Couldn't connect to Ollama. Is the [server running](/settings)?"
Expand Down
73 changes: 48 additions & 25 deletions src/lib/store.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
import { browser } from '$app/environment';
import type { ModelResponse } from 'ollama/browser';
import { browser, version } from '$app/environment';
import { writable } from 'svelte/store';
import type { Session } from '$lib/sessions';
import type { OllamaModel } from './ollama';
import type { Knowledge } from './knowledge';
import { env } from '$env/dynamic/public';
import type { HollamaMetadata } from '../routes/api/metadata/+server';

function createLocalStorageStore<T>(key: string, initialValue: T | null = null) {
const localStorageValue: string | null = browser ? window.localStorage.getItem(key) : null;
let value: T | null = initialValue;
const store = writable<T | null>(initialValue);
function createLocalStorageStore<T>(key: string, defaultValue: T) {
const initialValue: T = browser
? JSON.parse(localStorage.getItem(key) || 'null') || defaultValue
: defaultValue;

// Read existing value from localStorage
if (localStorageValue) {
value = JSON.parse(localStorageValue);
store.set(value);
}
const store = writable<T>(initialValue);

// Write value to localStorage
store.subscribe((newValue) => {
store.subscribe((value) => {
if (browser) {
window.localStorage.setItem(key, JSON.stringify(newValue));
localStorage.setItem(key, JSON.stringify(value));
}
});

return store;
return {
...store,
reset: () => {
store.set(defaultValue);
}
};
}

export function sortStore<T extends { updatedAt?: string }>(store: T[]) {
Expand All @@ -39,20 +41,41 @@ export function deleteStoreItem<T extends { id: string }>(store: T[], id: string
}

export const LOCAL_STORAGE_PREFIX = 'hollama';
export enum StorageKey {
HollamaSettings = `${LOCAL_STORAGE_PREFIX}-settings`,
HollamaSessions = `${LOCAL_STORAGE_PREFIX}-sessions`,
HollamaKnowledge = `${LOCAL_STORAGE_PREFIX}-knowledge`
}

export interface Settings {
ollamaServer: string | null;
ollamaModel: string | null;
ollamaModels: OllamaModel[];
userTheme?: 'light' | 'dark';
ollamaModels: ModelResponse[];
ollamaServerStatus: 'connected' | 'disconnected';
lastUpdateCheck: number | null;
autoCheckForUpdates: boolean;
userTheme: 'light' | 'dark';
hollamaMetadata: HollamaMetadata;
}

export enum StorageKey {
HollamaSettings = `${LOCAL_STORAGE_PREFIX}-settings`,
HollamaSessions = `${LOCAL_STORAGE_PREFIX}-sessions`,
HollamaKnowledge = `${LOCAL_STORAGE_PREFIX}-knowledge`
}
const defaultSettings: Settings = {
ollamaServer: 'http://localhost:11434',
ollamaModel: null,
ollamaModels: [],
ollamaServerStatus: 'disconnected',
lastUpdateCheck: null,
autoCheckForUpdates: false,
userTheme: 'light',
hollamaMetadata: {
currentVersion: version,
isDesktop: env.PUBLIC_ADAPTER === 'electron-node',
isDocker: env.PUBLIC_ADAPTER === 'docker-node'
}
};

export const settingsStore = createLocalStorageStore<Settings>(StorageKey.HollamaSettings);
export const sessionsStore = createLocalStorageStore<Session[]>(StorageKey.HollamaSessions);
export const knowledgeStore = createLocalStorageStore<Knowledge[]>(StorageKey.HollamaKnowledge);
export const settingsStore = createLocalStorageStore<Settings>(
StorageKey.HollamaSettings,
defaultSettings
);
export const sessionsStore = createLocalStorageStore<Session[]>(StorageKey.HollamaSessions, []);
export const knowledgeStore = createLocalStorageStore<Knowledge[]>(StorageKey.HollamaKnowledge, []);
Loading