mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-09-18 16:55:05 +02:00
chat: fix the deep-linked prompt, the tab width and the tab shortcuts
The chat start page creates the conversation and hands the prompt over to the chat route, which still sees it in the query string. Sending it on both sides queues the second copy as a pending message, which shows up as a stray user bubble once the answer lands and vanishes on reload since it never reaches the database. The tab bar takes the max width of the collapsed sidebar while it is expanded, and the other way round. The tab list is pruned against a snapshot of the loaded conversations, so a conversation created while that list is still loading loses its tab even though the route just opened it. The active tab then falls out of the list and the cycling shortcut jumps to an edge on every keypress instead of moving one tab over. Tabs synced from the route are kept as they are, only the persisted ones are pruned. The rich chat input claims ctrl or alt with shift and an arrow for its badge-aware word jump, which now belongs to the tab cycling shortcut. Holding shift hands the key combination over, the plain word jump is unchanged. The close-tab shortcut consumes the event before checking whether the setting is on, and the logo background loses its importance flag.
This commit is contained in:
+1
-1
@@ -625,7 +625,7 @@
|
||||
}
|
||||
|
||||
if (rootElement && (event.key === 'ArrowLeft' || event.key === 'ArrowRight')) {
|
||||
const isWordJump = (event.altKey || event.ctrlKey) && !event.metaKey;
|
||||
const isWordJump = (event.altKey || event.ctrlKey) && !event.metaKey && !event.shiftKey;
|
||||
const isPlainLeft =
|
||||
event.key === 'ArrowLeft' && !event.altKey && !event.ctrlKey && !event.metaKey;
|
||||
|
||||
|
||||
@@ -73,9 +73,7 @@
|
||||
});
|
||||
const { handleKeydown } = useKeyboardShortcuts({
|
||||
deleteActiveConversation: () => {
|
||||
const conversation = conversationsStore.activeConversation;
|
||||
|
||||
if (conversation) {
|
||||
if (conversationsStore.activeConversation) {
|
||||
showDeleteDialog = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
let loadingIds = $derived(new Set(chatStore.getAllLoadingChats()));
|
||||
|
||||
function handleClose(id: string) {
|
||||
void tabsStore.close(id, activeId ?? null);
|
||||
void tabsStore.close(id, activeId);
|
||||
}
|
||||
|
||||
function handleStop(id: string, event: MouseEvent) {
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
$effect(() => {
|
||||
const currentIds = new Set(tabs.map((t) => t.id));
|
||||
const addedIds = tabs.filter((t) => !previousTabIds.has(t.id)).map((t) => t.id);
|
||||
const hasAddedTab = tabs.some((t) => !previousTabIds.has(t.id));
|
||||
|
||||
previousTabIds = currentIds;
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
previousActiveId = activeId;
|
||||
|
||||
// scroll when the active tab changes (a click) or when a new tab is added
|
||||
if (addedIds.length === 0 && !activeChanged) return;
|
||||
if (!hasAddedTab && !activeChanged) return;
|
||||
|
||||
// wait for the new tab to be laid out before scrolling to it
|
||||
void tick().then(() => {
|
||||
@@ -73,7 +73,7 @@
|
||||
`[${UI_DATA_ATTRS.ACTIVE_TAB}]`
|
||||
);
|
||||
|
||||
if (el && activeId) {
|
||||
if (el) {
|
||||
carousel.scrollToCenter(el);
|
||||
}
|
||||
});
|
||||
@@ -82,8 +82,8 @@
|
||||
|
||||
<nav
|
||||
class="group sticky pl-1 top-0 z-10 hidden md:block chat-tabs-fade transition-[padding] duration-200 ease-in-out pt-3.25 {uiStore.isSidebarExpanded
|
||||
? CHAT_TABS_MAX_WIDTH.COLLAPSED_SIDEBAR
|
||||
: CHAT_TABS_MAX_WIDTH.EXPANDED_SIDEBAR}"
|
||||
? CHAT_TABS_MAX_WIDTH.EXPANDED_SIDEBAR
|
||||
: CHAT_TABS_MAX_WIDTH.COLLAPSED_SIDEBAR}"
|
||||
aria-label="Open conversations"
|
||||
>
|
||||
<div class="relative">
|
||||
|
||||
+1
-1
@@ -343,7 +343,7 @@
|
||||
iconSize="h-4.5 w-4.5 md:h-4 md:w-4"
|
||||
class="{uiStore.isSidebarExpanded
|
||||
? 'bg-muted! md:bg-foreground/5!'
|
||||
: 'bg-transparent'} md:h-9 md:w-9 h-10 w-10 rounded-full md:hover:bg-foreground/10! pointer-events-auto"
|
||||
: 'bg-transparent!'} md:h-9 md:w-9 h-10 w-10 rounded-full md:hover:bg-foreground/10! pointer-events-auto"
|
||||
href={uiStore.isSidebarExpanded ? ROUTES.START : undefined}
|
||||
onclick={uiStore.isSidebarExpanded ? undefined : toggleExpandedMode}
|
||||
tooltip={uiStore.isSidebarExpanded ? undefined : 'Open Sidebar'}
|
||||
|
||||
@@ -54,11 +54,11 @@ export function useKeyboardShortcuts(callbacks: KeyboardShortcutsCallbacks) {
|
||||
// act so the synchronous navigation does not cascade-close every tab
|
||||
if (event.defaultPrevented) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
// close-tab only makes sense with conversation tabs enabled
|
||||
if (!settingsStore.config.conversationTabs) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const activeId =
|
||||
page.params.id ?? (page.route.id === '/(chat)' ? NEW_CHAT_TAB_ID : undefined);
|
||||
|
||||
|
||||
@@ -88,7 +88,10 @@ class TabsStore {
|
||||
// still kept so a reload on `#/` does not drop the tab the user is on
|
||||
const isLive = (id: string) => validIds.includes(id) || id === NEW_CHAT_TAB_ID;
|
||||
const persisted = this.load().filter(isLive);
|
||||
const extras = this.openTabs.filter((id) => isLive(id) && !persisted.includes(id));
|
||||
// tabs already in openTabs come from the live route, so they stay as they
|
||||
// are: `validIds` is a snapshot and a conversation created while the list
|
||||
// was loading is not in it
|
||||
const extras = this.openTabs.filter((id) => !persisted.includes(id));
|
||||
|
||||
this.openTabs = [...persisted, ...extras];
|
||||
this.initialized = true;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { SidebarAction } from '$lib/enums';
|
||||
import type { SidebarAction } from '$lib/enums';
|
||||
import type { Component } from 'svelte';
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,10 +61,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ?q= sends an immediate prompt in a fresh conversation
|
||||
// ?q= creates the conversation, the chat route sends the prompt once the
|
||||
// conversation id is in the URL
|
||||
if (qParam !== null) {
|
||||
await conversationsStore.createConversation();
|
||||
await chatStore.sendMessage(qParam);
|
||||
clearUrlParams();
|
||||
} else if (modelParam) {
|
||||
clearUrlParams();
|
||||
|
||||
Reference in New Issue
Block a user