File size: 3,107 Bytes
0ad74ed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<script lang="ts">
	import LikeDislike from "./LikeDislike.svelte";
	import Copy from "./Copy.svelte";
	import type { FileData } from "@gradio/client";
	import DownloadIcon from "./Download.svelte";
	import { DownloadLink } from "@gradio/wasm/svelte";
	import type { NormalisedMessage, TextMessage } from "../types";
	import { is_component_message } from "./utils";
	import { Retry, Undo } from "@gradio/icons";
	import { IconButtonWrapper, IconButton } from "@gradio/atoms";

	export let likeable: boolean;
	export let show_retry: boolean;
	export let show_undo: boolean;
	export let show_copy_button: boolean;
	export let show: boolean;
	export let message: NormalisedMessage | NormalisedMessage[];
	export let position: "right" | "left";
	export let avatar: FileData | null;
	export let generating: boolean;

	export let handle_action: (selected: string | null) => void;
	export let layout: "bubble" | "panel";

	function is_all_text(
		message: NormalisedMessage[] | NormalisedMessage
	): message is TextMessage[] | TextMessage {
		return (
			(Array.isArray(message) &&
				message.every((m) => typeof m.content === "string")) ||
			(!Array.isArray(message) && typeof message.content === "string")
		);
	}

	function all_text(message: TextMessage[] | TextMessage): string {
		if (Array.isArray(message)) {
			return message.map((m) => m.content).join("\n");
		}
		return message.content;
	}

	$: message_text = is_all_text(message) ? all_text(message) : "";

	$: show_copy = show_copy_button && message && is_all_text(message);
	$: show_download =
		!Array.isArray(message) &&
		is_component_message(message) &&
		message.content.value?.url;
</script>

{#if show}
	<div
		class="message-buttons-{position} {layout} message-buttons {avatar !==
			null && 'with-avatar'}"
	>
		<IconButtonWrapper top_panel={false}>
			{#if show_copy}
				<Copy value={message_text} />
			{/if}
			{#if show_download && !Array.isArray(message) && is_component_message(message)}
				<DownloadLink
					href={message?.content?.value.url}
					download={message.content.value.orig_name || "image"}
				>
					<IconButton Icon={DownloadIcon} />
				</DownloadLink>
			{/if}
			{#if show_retry}
				<IconButton
					Icon={Retry}
					label="Retry"
					on:click={() => handle_action("retry")}
					disabled={generating}
				/>
			{/if}
			{#if show_undo}
				<IconButton
					label="Undo"
					Icon={Undo}
					on:click={() => handle_action("undo")}
					disabled={generating}
				/>
			{/if}
			{#if likeable}
				<LikeDislike {handle_action} />
			{/if}
		</IconButtonWrapper>
	</div>
{/if}

<style>
	.bubble :global(.icon-button-wrapper) {
		margin: 0px calc(var(--spacing-xl) * 2);
	}

	.message-buttons-left {
		align-self: flex-start;
	}

	.bubble.message-buttons-right {
		align-self: flex-end;
	}

	.message-buttons-right :global(.icon-button-wrapper) {
		margin-left: auto;
	}

	.bubble.with-avatar {
		margin-left: calc(var(--spacing-xl) * 5);
		margin-right: calc(var(--spacing-xl) * 5);
	}

	.panel {
		display: flex;
		align-self: flex-start;
		padding: 0 var(--spacing-xl);
		z-index: var(--layer-1);
	}
</style>