Repository: fmaclen/hollama (ts)

Issue:
Knowledge: keyboard shortcut help for saving is incorrect
It's currently showing as `↵` but should the actual shortcut is `Meta+Enter` so it should look like `⌘↵` (or `Ctrl+↵` in Windows).

Pull request description:
fix: minor bug fixes
- Fixes Github Action for publishing the docker image to the registry
- Adds `<Metadata>` component
- Changes the order of the metadata details
- Fixes #95

Patch:
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 7627aeb..9f9e153 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -49,12 +49,12 @@ jobs:
         uses: docker/login-action@v3
         with:
           registry: ghcr.io
-          username: ${{ github.actor }}
+          username: ${{ secrets.GH_REGISTRY_USERNAME }}
           password: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
 
       - name: Push the Docker image
         uses: docker/build-push-action@v5
         with:
           push: true
-          tags: ghcr.io/${{ github.actor }}/hollama:latest, ghcr.io/${{ github.actor }}/hollama:${{ needs.release.outputs.new_release_version }}
+          tags: ghcr.io/${{ secrets.GH_REGISTRY_USERNAME }}/hollama:latest, ghcr.io/${{ secrets.GH_USERNAME }}/hollama:${{ needs.release.outputs.new_release_version }}
           platforms: linux/arm64, linux/amd64
diff --git a/src/lib/components/Metadata.svelte b/src/lib/components/Metadata.svelte
new file mode 100644
index 0000000..a2bbac9
--- /dev/null
+++ b/src/lib/components/Metadata.svelte
@@ -0,0 +1,13 @@
+<script lang="ts">
+	export let dataTestid: string | undefined = undefined;
+</script>
+
+<p class="metadata" data-testid={dataTestid}>
+	<slot />
+</p>
+
+<style lang="scss">
+	.metadata {
+		@apply truncate whitespace-nowrap text-xs text-muted max-w-full;
+	}
+</style>
diff --git a/src/lib/components/SectionListItem.svelte b/src/lib/components/SectionListItem.svelte
index 8e529c7..3c45a37 100644
--- a/src/lib/components/SectionListItem.svelte
+++ b/src/lib/components/SectionListItem.svelte
@@ -1,6 +1,7 @@
 <script lang="ts">
 	import { page } from '$app/stores';
 	import { Sitemap } from '$lib/sitemap';
+	import Metadata from './Metadata.svelte';
 
 	export let sitemap: Sitemap;
 	export let id: string;
@@ -19,14 +20,14 @@
 	<p class="section-list-item__title">
 		{title}
 	</p>
-	<p class="section-list-item__subtitle">
+	<Metadata>
 		{subtitle}
-	</p>
+	</Metadata>
 </a>
 
 <style lang="scss">
 	.section-list-item {
-		@apply flex flex-col px-6 py-3 hover:bg-shade-1 border-b last:border-b-0;
+		@apply flex flex-col border-b px-6 py-3 last:border-b-0 hover:bg-shade-1;
 	}
 
 	.section-list-item--active {
@@ -36,8 +37,4 @@
 	.section-list-item__title {
 		@apply max-w-full truncate whitespace-nowrap text-sm font-bold;
 	}
-
-	.section-list-item__subtitle {
-		@apply flex max-w-full gap-x-2 truncate whitespace-nowrap text-sm;
-	}
 </style>
diff --git a/src/lib/sessions.ts b/src/lib/sessions.ts
index 36dc9f7..aa7df9b 100644
--- a/src/lib/sessions.ts
+++ b/src/lib/sessions.ts
@@ -64,11 +64,8 @@ export const saveSession = (session: Session): void => {
 };
 
 export function formatSessionMetadata(session: Session) {
-	const metadata: { model: string; updatedAt?: string; knowledge?: string } = {
-		model: session.model
-	};
-	if (session.updatedAt) metadata.updatedAt = formatTimestampToNow(session.updatedAt);
-	if (session.knowledge?.name) metadata.knowledge = session.knowledge?.name;
-	const subtitles: string[] = Object.values(metadata);
+	const subtitles: string[] = [];
+	if (session.updatedAt) subtitles.push(formatTimestampToNow(session.updatedAt));
+	subtitles.push(session.model);
 	return subtitles.join(' • ');
 }
diff --git a/src/routes/knowledge/[id]/+page.svelte b/src/routes/knowledge/[id]/+page.svelte
index 836f732..9790e12 100644
--- a/src/routes/knowledge/[id]/+page.svelte
+++ b/src/routes/knowledge/[id]/+page.svelte
@@ -12,6 +12,7 @@
 	import FieldTextEditor from '$lib/components/FieldTextEditor.svelte';
 	import ButtonSubmit from '$lib/components/ButtonSubmit.svelte';
 	import FieldInput from '$lib/components/FieldInput.svelte';
+	import Metadata from '$lib/components/Metadata.svelte';
 
 	export let data: PageData;
 
@@ -53,9 +54,9 @@
 				#{knowledge.id}
 			</Button>
 		</p>
-		<p data-testid="knowledge-timestamp" class="text-sm text-muted">
+		<Metadata dataTestid="knowledge-metadata">
 			{isNewKnowledge ? 'New knowledge' : formatTimestampToNow(knowledge.updatedAt)}
-		</p>
+		</Metadata>
 
 		<svelte:fragment slot="nav">
 			{#if !isNewKnowledge}
@@ -73,6 +74,6 @@
 			<FieldTextEditor label="Content" {handleSubmit} bind:value={content} />
 		{/key}
 
-		<ButtonSubmit {handleSubmit} disabled={!name || !content}>Save</ButtonSubmit>
+		<ButtonSubmit hasMetaKey={true} {handleSubmit} disabled={!name || !content}>Save</ButtonSubmit>
 	</Fieldset>
 </div>
diff --git a/src/routes/sessions/[id]/+page.svelte b/src/routes/sessions/[id]/+page.svelte
index d0c4112..f0a85af 100644
--- a/src/routes/sessions/[id]/+page.svelte
+++ b/src/routes/sessions/[id]/+page.svelte
@@ -28,6 +28,7 @@
 	import ButtonSubmit from '$lib/components/ButtonSubmit.svelte';
 	import Fieldset from '$lib/components/Fieldset.svelte';
 	import Field from '$lib/components/Field.svelte';
+	import Metadata from '$lib/components/Metadata.svelte';
 
 	export let data: PageData;
 
@@ -185,11 +186,10 @@
 				>#{session.id}</Button
 			>
 		</p>
-		<div class="grid grid-cols-[auto,auto] gap-x-1">
-			<p data-testid="session-metadata" class="text-sm text-muted">
-				{isNewSession ? 'New session' : formatSessionMetadata(session)}
-			</p>
-		</div>
+		<Metadata dataTestid="session-metadata">
+			{isNewSession ? 'New session' : formatSessionMetadata(session)}
+		</Metadata>
+
 		<svelte:fragment slot="nav">
 			{#if !isNewSession}
 				<Button title="Delete session" variant="outline" size="icon" on:click={deleteSession}>
@@ -329,11 +329,11 @@
 	}
 
 	.prompt-editor__form {
-		@apply bg-shade-1 h-full overflow-y-auto;
+		@apply h-full overflow-y-auto bg-shade-1;
 	}
 
 	.prompt-editor__toggle {
-		@apply bg-shade-1 border-b;
+		@apply border-b bg-shade-1;
 		@apply hover:bg-shade-2 active:bg-shade-2;
 		@apply 2xl:rounded-t-lg;
 	}


Test patch:
diff --git a/tests/docs.test.ts b/tests/docs.test.ts
index d91edab..8a8bbec 100644
--- a/tests/docs.test.ts
+++ b/tests/docs.test.ts
@@ -87,9 +87,9 @@ test('seed data and take screenshots for README.md', async ({ page }) => {
 
 	await seedKnowledgeAndReload(page);
 	await expect(page.getByText('No knowledge')).not.toBeVisible();
-	await expect(page.getByTestId('knowledge-timestamp')).not.toBeVisible();
+	await expect(page.getByTestId('knowledge-metadata')).not.toBeVisible();
 
 	await page.getByText(MOCK_KNOWLEDGE[0].name).click();
-	await expect(page.getByTestId('knowledge-timestamp')).toBeVisible();
+	await expect(page.getByTestId('knowledge-metadata')).toBeVisible();
 	expect(await page.screenshot()).toMatchSnapshot({ name: 'knowledge.png' });
 });
diff --git a/tests/knowledge.test.ts b/tests/knowledge.test.ts
index 9ac07d6..9c84a6a 100644
--- a/tests/knowledge.test.ts
+++ b/tests/knowledge.test.ts
@@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test';
 import { MOCK_API_TAGS_RESPONSE, MOCK_KNOWLEDGE, MOCK_SESSION_WITH_KNOWLEDGE_RESPONSE_1, chooseModelFromSettings, mockCompletionResponse, mockTagsResponse, seedKnowledgeAndReload, submitWithKeyboardShortcut, textEditorLocator } from './utils';
 
 test('creates and edits knowledge', async ({ page }) => {
-	const timestamp = page.getByTestId('knowledge-timestamp');
+	const timestamp = page.getByTestId('knowledge-metadata');
 	const fieldName = page.getByLabel('Name');
 	const fieldContent = textEditorLocator(page, 'Content');
 	const buttonSave = page.getByText('Save');
@@ -57,7 +57,7 @@ test('creates and edits knowledge', async ({ page }) => {
 });
 
 test('deletes knowledge', async ({ page }) => {
-	const timestamp = page.getByTestId('knowledge-timestamp');
+	const timestamp = page.getByTestId('knowledge-metadata');
 	const noKnowledgeMessage = page.getByText('No knowledge');
 	const noKnowledgeSelectedMessage = page.getByText('Create new knowlege or choose one from the list');
 	const knowledgeItems = page.getByTestId('knowledge-item');