Skip to content

Commit 3f3faab

Browse files
committed
Added a great tooltip library to use "Tippy.js"
Updated the ToolTip component to use the new tippy library. Added tooltips in various places to help explain things to newer users
1 parent a01a3a9 commit 3f3faab

File tree

9 files changed

+65
-58
lines changed

9 files changed

+65
-58
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# [**MinimalChat: A Simple and Customizable LLM Chat App (public site link)**](https://minimalgpt.app)
22

3-
![Version](https://img.shields.io/badge/version-5.1.7-blue)
3+
![Version](https://img.shields.io/badge/version-5.1.8-blue)
44
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/fingerthief/minimal-chat/firebase-hosting-merge.yml)
55
![License](https://img.shields.io/badge/license-MIT-green)
66
![Website](https://img.shields.io/website?url=https%3A%2F%2Fminimalgpt.app)

package-lock.json

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"markdown-it": "^14.1.0",
2727
"pwacompat": "^2.0.17",
2828
"swiped-events": "^1.1.9",
29+
"tippy.js": "^6.3.7",
2930
"toastify-js": "^1.12.0",
3031
"vue": "^3.4.21",
3132
"vue-router": "^4.3.0"

src/components/ToolTip.vue

Lines changed: 22 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,53 @@
11
<script setup>
22
import { ref, onMounted, onUnmounted } from 'vue';
3+
import tippy from 'tippy.js';
4+
import 'tippy.js/animations/shift-away-subtle.css';
35
46
const props = defineProps({
5-
targetId: String,
6-
alignment: {
7-
type: String,
8-
default: 'center',
9-
validator: (value) => ['left', 'center', 'right'].includes(value)
10-
}
7+
targetId: String
118
});
129
13-
const visible = ref(false);
1410
const tooltipElement = ref(null);
11+
let tippyInstance = null;
1512
16-
const showTooltip = () => {
17-
visible.value = true;
18-
updatePosition();
19-
};
20-
21-
const hideTooltip = () => {
22-
visible.value = false;
23-
};
24-
25-
const updatePosition = () => {
13+
const createTooltip = () => {
2614
const target = document.getElementById(props.targetId);
27-
const targetRect = target.getBoundingClientRect();
28-
const tooltipRect = tooltipElement.value.getBoundingClientRect();
2915
30-
let x = 0;
31-
if (props.alignment === 'left') {
32-
x = targetRect.left;
33-
} else if (props.alignment === 'center') {
34-
x = targetRect.left + (targetRect.width - tooltipRect.width) / 2;
35-
} else if (props.alignment === 'right') {
36-
x = targetRect.right - tooltipRect.width;
16+
if (target && tooltipElement.value) {
17+
tippyInstance = tippy(target, {
18+
content: tooltipElement.value,
19+
placement: 'top',
20+
trigger: 'mouseenter focus',
21+
appendTo: document.body,
22+
arrow: true,
23+
animation: 'shift-away-subtle',
24+
interactive: true,
25+
onHidden: (instance) => {
26+
instance.destroy();
27+
}
28+
});
3729
}
38-
39-
const y = targetRect.bottom + 10; // Adjust the offset as needed
40-
41-
tooltipElement.value.style.left = `${x}px`;
42-
tooltipElement.value.style.top = `${y}px`;
4330
};
4431
4532
onMounted(() => {
46-
const target = document.getElementById(props.targetId);
47-
48-
if (target) {
49-
target.addEventListener('mouseenter', showTooltip);
50-
target.addEventListener('mouseleave', hideTooltip);
51-
window.addEventListener('resize', updatePosition);
52-
}
33+
createTooltip();
5334
});
5435
5536
onUnmounted(() => {
56-
const target = document.getElementById(props.targetId);
57-
58-
if (target) {
59-
target.removeEventListener('mouseenter', showTooltip);
60-
target.removeEventListener('mouseleave', hideTooltip);
61-
window.removeEventListener('resize', updatePosition);
37+
if (tippyInstance) {
38+
tippyInstance.destroy();
6239
}
63-
6440
});
6541
</script>
6642

6743
<template>
68-
<div ref="tooltipElement" class="tooltip-container" v-show="visible">
44+
<div ref="tooltipElement" class="tooltip-container">
6945
<slot></slot>
7046
</div>
7147
</template>
7248

7349
<style>
7450
.tooltip-container {
75-
position: fixed;
7651
padding: 8px;
7752
background-color: #333;
7853
color: white;

src/components/chat-header.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<script setup>
44
import { ref, computed } from 'vue';
5-
import { Settings, Trash2, MessagesSquare, MessageSquareDiff, Github } from 'lucide-vue-next';
5+
import { Settings, Trash2, MessagesSquare, Github, SquarePlus } from 'lucide-vue-next';
66
77
// Define props
88
const props = defineProps({
@@ -72,7 +72,7 @@ function onShowConversationsClick() {
7272
<MessagesSquare :stroke-width="1.00" :size="30" />
7373
</div>
7474
<span class="save-icon" @click="clearMessages">
75-
<MessageSquareDiff :stroke-width="1.00" :size="30" />
75+
<SquarePlus :stroke-width="1.00" :size="30" />
7676
</span>
7777
</div>
7878
</template>

src/components/chat-input.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script setup>
22
import { ref, watch, defineEmits } from 'vue';
33
import { SendHorizontal, ImageUp, CircleStop } from 'lucide-vue-next';
4+
import ToolTip from './ToolTip.vue';
45
import "swiped-events";
56
67
// Define props and emits
@@ -77,7 +78,9 @@ async function abortStream() {
7778
<textarea class="user-input-text" id="user-input" rows="1" v-model="localUserInput" ref="userInputRef"
7879
@input="autoResize" @focus="autoResize" @blur="autoResize" @keydown="handleKeyDown"
7980
placeholder="Enter your prompt"></textarea>
80-
<div class="image-button" @click="visionImageUploadClick">
81+
<ToolTip :targetId="'imageButton'">
82+
Upload image for vision processing</ToolTip>
83+
<div class="image-button" id="imageButton" @click="visionImageUploadClick">
8184
<span>
8285
<ImageUp />
8386
</span>

src/components/conversations-dialog.vue

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script setup>
22
import { onMounted, ref, computed } from 'vue';
33
import { Eraser, Download, Upload, MessageSquarePlus, MessageSquareX, Settings } from 'lucide-vue-next';
4+
import ToolTip from './ToolTip.vue';
45
56
const props = defineProps({
67
isSidebarOpen: Boolean,
@@ -102,9 +103,15 @@ function purgeConversations() {
102103
<h2>
103104
Conversations
104105
&nbsp;
105-
<Eraser @click="purgeConversations" :size="25" :stroke-width="1.00" />&nbsp;
106-
<Download @click="exportConversations" :size="25" :stroke-width="1.00" />&nbsp;
107-
<Upload @click="importConversations" :size="25" :stroke-width="1.00" />
106+
<ToolTip :targetId="'purgeConversations'">
107+
Purge all conversations</ToolTip>
108+
<Eraser @click="purgeConversations" id="purgeConversations" :size="25" :stroke-width="1.00" />&nbsp;
109+
<ToolTip :targetId="'exportConversations'">
110+
Export conversations</ToolTip>
111+
<Download @click="exportConversations" id="exportConversations" :size="25" :stroke-width="1.00" />&nbsp;
112+
<ToolTip :targetId="'importConversations'">
113+
Ixport conversations</ToolTip>
114+
<Upload @click="importConversations" id="importConversations" :size="25" :stroke-width="1.00" />
108115
</h2>
109116
</div>
110117
<div class="sidebar-content-container">

src/components/message-item.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { defineEmits, ref, nextTick } from 'vue';
88
import "/node_modules/highlight.js/scss/github-dark-dimmed.scss";
99
import ToolTip from './ToolTip.vue';
1010
11+
1112
const props = defineProps({
1213
messages: Array,
1314
isLoading: Boolean,
@@ -105,7 +106,9 @@ const saveEditedMessage = (message, event) => {
105106
<template>
106107
<div>
107108
<div v-for="(message, index) in messages" :key="index" :class="messageClass(message.role)">
108-
<div class="label" @click="copyText(message)">
109+
<ToolTip :targetId="'message-label-' + index">
110+
Copy Text</ToolTip>
111+
<div class="label" @click="copyText(message)" :id="'message-label-' + index">
109112
<RefreshCcw v-if="message.role === 'user'" class="icon"
110113
:class="{ 'loading': isLoading && loadingIcon === index }"
111114
@click="$emit('regenerate-response', message.content), startLoading(index)" />
@@ -114,10 +117,11 @@ const saveEditedMessage = (message, event) => {
114117
@click="$emit('delete-response', message.content), startLoading(index)" />
115118
{{ message.role === 'user' ? 'User' : modelDisplayName }}
116119
</div>
120+
117121
<div class="message-contents" :id="'message-' + index" :contenteditable="message.isEditing"
118122
@dblclick="editMessage(message)" @blur="saveEditedMessage(message, $event)"
119123
v-html="formatMessage(message.content)"></div>
120-
<ToolTip v-if="message.role === 'user'" :alignment="'left'" :targetId="'message-' + index">
124+
<ToolTip v-if="message.role === 'user'" :targetId="'message-' + index">
121125
Double click to
122126
edit message</ToolTip>
123127
</div>

src/components/settings-dialog.vue

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import { RefreshCcw } from 'lucide-vue-next';
44
import InputField from './InputField.vue';
55
import { ref } from 'vue';
6-
import ToolTip from './ToolTip.vue';
76
87
const props = defineProps({
98
isSidebarOpen: Boolean,
@@ -74,7 +73,7 @@ function toggleSidebar() {
7473
<span @click="reloadPage">
7574
<RefreshCcw :size="23" :stroke-width="2" />
7675
</span>
77-
Settings | V5.1.7
76+
Settings | V5.1.8
7877
</h2>
7978
</div>
8079
<div class="sidebar-content-container">

0 commit comments

Comments
 (0)