1<script lang="ts">
  2	import { Trash2, Pencil, MoreHorizontal, Download, Loader2, Square } from '@lucide/svelte';
  3	import { ActionDropdown } from '$lib/components/app';
  4	import * as Tooltip from '$lib/components/ui/tooltip';
  5	import { getAllLoadingChats } from '$lib/stores/chat.svelte';
  6	import { conversationsStore } from '$lib/stores/conversations.svelte';
  7	import { onMount } from 'svelte';
  8
  9	interface Props {
 10		isActive?: boolean;
 11		conversation: DatabaseConversation;
 12		handleMobileSidebarItemClick?: () => void;
 13		onDelete?: (id: string) => void;
 14		onEdit?: (id: string) => void;
 15		onSelect?: (id: string) => void;
 16		onStop?: (id: string) => void;
 17	}
 18
 19	let {
 20		conversation,
 21		handleMobileSidebarItemClick,
 22		onDelete,
 23		onEdit,
 24		onSelect,
 25		onStop,
 26		isActive = false
 27	}: Props = $props();
 28
 29	let renderActionsDropdown = $state(false);
 30	let dropdownOpen = $state(false);
 31
 32	let isLoading = $derived(getAllLoadingChats().includes(conversation.id));
 33
 34	function handleEdit(event: Event) {
 35		event.stopPropagation();
 36		onEdit?.(conversation.id);
 37	}
 38
 39	function handleDelete(event: Event) {
 40		event.stopPropagation();
 41		onDelete?.(conversation.id);
 42	}
 43
 44	function handleStop(event: Event) {
 45		event.stopPropagation();
 46		onStop?.(conversation.id);
 47	}
 48
 49	function handleGlobalEditEvent(event: Event) {
 50		const customEvent = event as CustomEvent<{ conversationId: string }>;
 51
 52		if (customEvent.detail.conversationId === conversation.id && isActive) {
 53			handleEdit(event);
 54		}
 55	}
 56
 57	function handleMouseLeave() {
 58		if (!dropdownOpen) {
 59			renderActionsDropdown = false;
 60		}
 61	}
 62
 63	function handleMouseOver() {
 64		renderActionsDropdown = true;
 65	}
 66
 67	function handleSelect() {
 68		onSelect?.(conversation.id);
 69	}
 70
 71	$effect(() => {
 72		if (!dropdownOpen) {
 73			renderActionsDropdown = false;
 74		}
 75	});
 76
 77	onMount(() => {
 78		document.addEventListener('edit-active-conversation', handleGlobalEditEvent as EventListener);
 79
 80		return () => {
 81			document.removeEventListener(
 82				'edit-active-conversation',
 83				handleGlobalEditEvent as EventListener
 84			);
 85		};
 86	});
 87</script>
 88
 89<!-- svelte-ignore a11y_mouse_events_have_key_events -->
 90<button
 91	class="group flex min-h-9 w-full cursor-pointer items-center justify-between space-x-3 rounded-lg px-3 py-1.5 text-left transition-colors hover:bg-foreground/10 {isActive
 92		? 'bg-foreground/5 text-accent-foreground'
 93		: ''}"
 94	onclick={handleSelect}
 95	onmouseover={handleMouseOver}
 96	onmouseleave={handleMouseLeave}
 97>
 98	<div class="flex min-w-0 flex-1 items-center gap-2">
 99		{#if isLoading}
100			<Tooltip.Root>
101				<Tooltip.Trigger>
102					<div
103						class="stop-button flex h-4 w-4 shrink-0 cursor-pointer items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground"
104						onclick={handleStop}
105						onkeydown={(e) => e.key === 'Enter' && handleStop(e)}
106						role="button"
107						tabindex="0"
108						aria-label="Stop generation"
109					>
110						<Loader2 class="loading-icon h-3.5 w-3.5 animate-spin" />
111
112						<Square class="stop-icon hidden h-3 w-3 fill-current text-destructive" />
113					</div>
114				</Tooltip.Trigger>
115
116				<Tooltip.Content>
117					<p>Stop generation</p>
118				</Tooltip.Content>
119			</Tooltip.Root>
120		{/if}
121
122		<!-- svelte-ignore a11y_click_events_have_key_events -->
123		<!-- svelte-ignore a11y_no_static_element_interactions -->
124		<span class="truncate text-sm font-medium" onclick={handleMobileSidebarItemClick}>
125			{conversation.name}
126		</span>
127	</div>
128
129	{#if renderActionsDropdown}
130		<div class="actions flex items-center">
131			<ActionDropdown
132				triggerIcon={MoreHorizontal}
133				triggerTooltip="More actions"
134				bind:open={dropdownOpen}
135				actions={[
136					{
137						icon: Pencil,
138						label: 'Edit',
139						onclick: handleEdit,
140						shortcut: ['shift', 'cmd', 'e']
141					},
142					{
143						icon: Download,
144						label: 'Export',
145						onclick: (e) => {
146							e.stopPropagation();
147							conversationsStore.downloadConversation(conversation.id);
148						},
149						shortcut: ['shift', 'cmd', 's']
150					},
151					{
152						icon: Trash2,
153						label: 'Delete',
154						onclick: handleDelete,
155						variant: 'destructive',
156						shortcut: ['shift', 'cmd', 'd'],
157						separator: true
158					}
159				]}
160			/>
161		</div>
162	{/if}
163</button>
164
165<style>
166	button {
167		:global([data-slot='dropdown-menu-trigger']:not([data-state='open'])) {
168			opacity: 0;
169		}
170
171		&:is(:hover) :global([data-slot='dropdown-menu-trigger']) {
172			opacity: 1;
173		}
174		@media (max-width: 768px) {
175			:global([data-slot='dropdown-menu-trigger']) {
176				opacity: 1 !important;
177			}
178		}
179
180		.stop-button {
181			:global(.stop-icon) {
182				display: none;
183			}
184
185			:global(.loading-icon) {
186				display: block;
187			}
188		}
189
190		&:is(:hover) .stop-button {
191			:global(.stop-icon) {
192				display: block;
193			}
194
195			:global(.loading-icon) {
196				display: none;
197			}
198		}
199	}
200</style>