andy-stack_vaultkeeper-ai/Components/TopBar.svelte
Andrew Beal 9276d2d0cd fix: improve mobile layout spacing and modal margins
- Add horizontal margin to conversation divider in TopBar
- Adjust grid row sizing in conversation history modal
- Remove margin from modals on mobile screens (≤600px)
- Reduce bottom margin for diff mobile controls
- Remove redundant background styling from diff button container
- Fix diff view height calculation on mobile to avoid control overlap
2026-01-30 10:00:16 +00:00

236 lines
6.4 KiB
Svelte

<script lang="ts">
import { Resolve } from "../Services/DependencyService";
import { Services } from "../Services/Services";
import type VaultkeeperAIPlugin from "../main";
import { Notice, setIcon, type WorkspaceLeaf } from "obsidian";
import { ConversationFileSystemService } from "../Services/ConversationFileSystemService";
import { conversationStore } from "../Stores/ConversationStore";
import type { ConversationHistoryModal } from "Modals/ConversationHistoryModal";
import { openPluginSettings } from "Helpers/Helpers";
import type { ChatService } from "Services/ChatService";
import { fade } from "svelte/transition";
import type { HelpModal } from "Modals/HelpModal";
export let leaf: WorkspaceLeaf;
export let onNewConversation: (() => void) | undefined = undefined;
const plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
const conversationFileSystemService = Resolve<ConversationFileSystemService>(Services.ConversationFileSystemService);
const chatService: ChatService = Resolve<ChatService>(Services.ChatService);
let conversationTitle: string = ""
chatService.onNameChanged = (name: string) => {
conversationTitle = "";
setTimeout(() => conversationTitle = name, 500);
};
function startNewConversation() {
chatService.stop();
conversationFileSystemService.resetCurrentConversation();
conversationStore.reset();
onNewConversation?.();
conversationTitle = "";
}
async function deleteCurrentConversation() {
const result = await conversationFileSystemService.deleteCurrentConversation();
if (result instanceof Error) {
new Notice(`Failed to delete conversation data for '${conversationFileSystemService.getCurrentConversationPath()}'`);
}
chatService.stop();
conversationStore.reset();
onNewConversation?.();
conversationTitle = "";
}
function openConversationHistory() {
const modal = Resolve<ConversationHistoryModal>(Services.ConversationHistoryModal);
modal.onModalClose = onNewConversation;
modal.open();
}
function openSettings() {
openPluginSettings(plugin);
}
function openHelpMenu() {
const modal = Resolve<HelpModal>(Services.HelpModal);
modal.open();
}
function closePlugin() {
leaf.detach();
}
let newConversationButton: HTMLButtonElement;
let deleteConversationButton: HTMLButtonElement;
let conversationHistoryButton: HTMLButtonElement;
let settingsButton: HTMLButtonElement;
let helpMenuButton: HTMLButtonElement;
let closeButton: HTMLButtonElement;
$: if (newConversationButton) {
setIcon(newConversationButton, "plus");
}
$: if (deleteConversationButton) {
setIcon(deleteConversationButton, "trash-2");
}
$: if (conversationHistoryButton) {
setIcon(conversationHistoryButton, "messages-square");
}
$: if (settingsButton) {
setIcon(settingsButton, "settings");
}
$: if (helpMenuButton) {
setIcon(helpMenuButton, "circle-help");
}
$: if (closeButton) {
setIcon(closeButton, "circle-x");
}
</script>
<main class="top-bar">
<div class="top-bar-content">
<button
bind:this={newConversationButton}
id="new-conversation-button"
class="top-bar-button clickable-icon"
on:click={() => startNewConversation()}
aria-label="New Conversation"
></button>
<button
bind:this={deleteConversationButton}
id="delete-conversation-button"
class="top-bar-button clickable-icon"
on:click={() => deleteCurrentConversation()}
aria-label="Delete Conversation"
></button>
<button
bind:this={conversationHistoryButton}
id="conversation-history-button"
class="top-bar-button clickable-icon"
on:click={() => openConversationHistory()}
aria-label="Conversation History"
></button>
<div id="conversation-divider-1" class="top-bar-divider"></div>
<button
bind:this={settingsButton}
id="settings-button"
class="top-bar-button clickable-icon"
on:click={openSettings}
aria-label="Vaultkeeper AI Settings"
></button>
<button
bind:this={helpMenuButton}
id="help-menu-button"
class="top-bar-button clickable-icon"
on:click={openHelpMenu}
aria-label="Help"
></button>
{#if conversationTitle !== ""}
<div id="conversation-divider-2" class="top-bar-divider" out:fade></div>
<div id="conversation-title" class="typing-in" out:fade>{conversationTitle}</div>
{/if}
<button
bind:this={closeButton}
id="close-button"
class="top-bar-button clickable-icon"
on:click={closePlugin}
aria-label="Close Vaultkeeper AI"
></button>
</div>
</main>
<style>
.top-bar {
display: grid;
background-color: transparent;
grid-template-rows: var(--size-4-3) 1fr var(--size-4-3);
grid-template-columns: var(--size-4-3) 1fr var(--size-4-3);
height: var(--size-4-16);
margin-left: calc(var(--size-4-3) * -1);
margin-right: calc(var(--size-4-3) * -1);
}
.top-bar-content {
grid-row: 2;
grid-column: 2;
display: grid;
grid-template-rows: auto;
grid-template-columns: var(--size-4-2) auto auto auto auto auto auto auto 1fr 0.1fr auto var(--size-4-2);
background-color: var(--background-secondary-alt);
border-radius: var(--radius-m);
}
:global(.is-mobile) .top-bar-content {
grid-template-columns: var(--size-4-1) auto auto auto auto auto auto auto 1fr 0fr auto var(--size-4-1);
}
.top-bar-divider {
width: var(--divider-width);
height: auto;
background: var(--background-modifier-border-hover);
margin: var(--size-4-2) var(--size-4-3);
}
:global(.is-mobile) .top-bar-divider {
margin: 0;
}
#new-conversation-button {
grid-row: 1;
grid-column: 2;
}
#delete-conversation-button {
grid-row: 1;
grid-column: 3;
}
#conversation-history-button {
grid-row: 1;
grid-column: 4;
}
#conversation-divider-1 {
grid-row: 1;
grid-column: 5;
margin: 0 5px;
}
#settings-button {
grid-row: 1;
grid-column: 6;
}
#help-menu-button {
grid-row: 1;
grid-column: 7;
}
#conversation-divider-2 {
grid-row: 1;
grid-column: 8;
margin: 0 5px;
}
#conversation-title {
grid-row: 1;
grid-column: 9;
display: inline-block;
align-self: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
color: var(--text-muted);
}
#close-button {
grid-row: 1;
grid-column: 11;
}
</style>