Skip to content

Commit

Permalink
DALL-E as tool
Browse files Browse the repository at this point in the history
  • Loading branch information
nbonamy committed May 24, 2024
1 parent 207f3c9 commit 21e535f
Show file tree
Hide file tree
Showing 40 changed files with 519 additions and 343 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ To use Internet search you need a [Tavily API key](https://app.tavily.com/home).

## DONE

- [x] DALL-E as tool
- [x] Google Gemini API
- [x] Prompt anywhere
- [x] Cancel commands
Expand Down
2 changes: 1 addition & 1 deletion css/panel.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
font-size: 10pt;
padding-left: 32px;
padding-right: 32px;
margin-bottom: 24px;
margin-bottom: 16px;
}

.settings form .group label {
Expand Down
11 changes: 10 additions & 1 deletion css/themes/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
margin-bottom: 32px;
}

.message .image-container {
margin-bottom: 12px;
}

.message .body, .message .actions {
margin: 0px 32px;
}
Expand Down Expand Up @@ -108,4 +112,9 @@

.message .image-container:hover .download {
display: block;
}
}

.message.assistant .actions {
position: relative;
top: -16px;
}
8 changes: 1 addition & 7 deletions css/themes/openai.css
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,7 @@
border-radius: 8px;
}

.messages.openai .message.text .actions {
.messages.openai .message .actions {
position: relative;
top: -16px;
}

.messages.openai .message.image .actions {
position: relative;
top: -4px;
}

6 changes: 4 additions & 2 deletions defaults/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
},
"instructions": {
"default": "You are a helpful assistant. You are here to help the user with any questions they have.",
"routing": "Your role is to determine if the following request is a text or an image generation request. You just reply TEXT or IMAGE.",
"titling": "You are an assistant whose task is to find the best title for the conversation below. The title should be just a few words.",
"titling_user": "Provide a title for the conversation above. Do not return anything other than the title. Do not wrap responses in quotes."
},
Expand Down Expand Up @@ -106,6 +105,9 @@
"plugins": {
"browse": {},
"python": {},
"tavily": {}
"tavily": {},
"dalle": {
"enabled": true
}
}
}
10 changes: 4 additions & 6 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,12 @@ onMounted(() => {
let platform = {
'win32': 'windows',
'darwin': 'macos',
}[window.api.platform]||''
}[window.api.platform]||'generic'
// add it everywhere
if (platform) {
window.platform = platform
document.platform = platform
document.querySelector('body').classList.add(platform)
}
window.platform = platform
document.platform = platform
document.querySelector('body').classList.add(platform)
})
Expand Down
155 changes: 16 additions & 139 deletions src/components/MessageItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,15 @@

<!-- attachment -->
<div v-if="message.attachment">
<Attachment :attachment="message.attachment" class="attachment" @image-click="onFullscreen" />
<Attachment :attachment="message.attachment" class="attachment" @image-click="onClickAttachment(message.attachment)" />
</div>

<!-- image -->
<div v-if="message.type == 'image'" class="image-container">
<img :src="imageUrl" class="image" @click="onFullscreen(imageUrl)" @load="onImageLoaded(message)"/>
<BIconDownload class="download" @click="onDownload(message)" />
</div>
<!-- image for backwards compatibility -->
<MessageItemImage :image-url="imageUrl" @image-loaded="onImageLoaded(message)" v-if="message.type == 'image'" />

<!-- text -->
<div v-if="message.type == 'text'">
<div v-if="message.content !== null" v-html="mdRender(message.content)" class="text"></div>
<div v-if="message.type == 'text' && message.content !== null">
<MessageItemBody :message="message" @image-loaded="onImageLoaded" />
</div>

<!-- transient information -->
Expand All @@ -30,32 +27,17 @@
</div>

</div>
<div class="actions" v-if="hovered">
<div class="action copy" v-if="message.role == 'assistant' && !message.transient" @click="onCopy(message)">
<BIconClipboard /> {{ copyLabel }}
</div>
<div class="action read" v-if="message.role == 'assistant' && message.type == 'text' && !message.transient" @click="onToggleRead(message)">
<span v-if="mgsAudioState(message) == 'playing'"><BIconStopCircle/> Stop</span>
<span v-else-if="mgsAudioState(message) == 'loading'"><BIconXCircle/> Cancel</span>
<span v-else><BIconPlayCircle /> Read</span>
<audio/>
</div>
<div class="action edit" v-if="message.role == 'user' && message.type == 'text' && !message.transient" @click="onEdit(message)">
<BIconPencil /> Edit
</div>
</div>
<div class="fullscreen" v-if="fullScreenImageUrl" @click="onCloseFullscreen">
<img :src="fullScreenImageUrl"/>
<BIconXLg class="close" />
</div>
<MessageItemActions :message="message" v-if="hovered" />
</div>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { store } from '../services/store'
import useAudioPlayer from '../composables/audio'
import MessageItemBody from './MessageItemBody.vue'
import MessageItemImage from './MessageItemImage.vue'
import MessageItemActions from './MessageItemActions.vue'
import Chat from '../models/chat'
import Message from '../models/message'
import Loader from './Loader.vue'
Expand All @@ -73,12 +55,7 @@ const props = defineProps({
const emits = defineEmits(['image-loaded'])
const hovered = ref(false)
const fullScreenImageUrl = ref(null)
const copyLabel = ref('Copy')
const audioState = ref({
state: 'idle',
messageId: null,
})
// onUpdated is not called for an unknown reason
// so let's hack it
Expand All @@ -89,23 +66,18 @@ onMounted(() => {
link.target = "_blank"
})
}, 599)
useAudioPlayer().addListener(onAudioPlayerStatus)
})
onUnmounted(() => {
clearInterval(updateLinkInterval)
useAudioPlayer().removeListener(onAudioPlayerStatus)
})
const mgsAudioState = (message) => {
return message.uuid == audioState.value.messageId ? audioState.value.state : 'idle'
}
const authorName = computed(() => {
return props.message.role === 'assistant' ? 'Assistant' : 'You'
})
const imageUrl = computed(() => {
if (props.message.type !== 'image' || typeof props.message.content !== 'string') {
return null
} else if (props.message.content.startsWith('http')) {
Expand All @@ -116,6 +88,7 @@ const imageUrl = computed(() => {
} else {
return 'data:image/png;base64,' + props.message.content
}
})
// using simple css :hover
Expand All @@ -125,57 +98,12 @@ const onHover = (value) => {
hovered.value = value
}
const onImageLoaded = (message) => {
emits('image-loaded', message)
}
const onCopy = (message) => {
if (message.type == 'text') {
window.api.clipboard.writeText(message.content)
} else if (message.type == 'image') {
window.api.clipboard.writeImage(message.content)
}
copyLabel.value = 'Copied!'
setTimeout(() => copyLabel.value = 'Copy', 1000)
}
const onAudioPlayerStatus = (status) => {
audioState.value = { state: status.state, messageId: status.uuid }
}
const onToggleRead = async (message) => {
await useAudioPlayer().play(document.querySelector('.read audio'),message.uuid, message.content)
}
const onEdit = (message) => {
emitEvent('set-prompt', message)
}
const onFullscreen = (url) => {
fullScreenImageUrl.value = url
window.api.fullscreen(true)
const onClickAttachment = (attachment) => {
emitEvent('fullScreen', attachment.url)
}
const onCloseFullscreen = () => {
fullScreenImageUrl.value = null
window.api.fullscreen(false)
}
const onDownload = (message) => {
window.api.file.download({
url: message.content,
properties: {
filename: 'image.png',
}
})
}
const mdRender = (content) => {
if (store.chatFilter) {
const regex = new RegExp(store.chatFilter, 'gi')
content = content.replace(regex, (match) => `==${match}==`);
}
return window.api.markdown.render(content)
const onImageLoaded = (message) => {
emits('image-loaded', message)
}
</script>
Expand All @@ -195,59 +123,8 @@ const mdRender = (content) => {

<style scoped>
.message .body img {
cursor: pointer;
}
.fullscreen {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: black;
padding: 8px;
z-index: 100;
img {
cursor: pointer;
-webkit-app-region: no-drag;
}
.fullscreen img {
height: 100%;
width: 100%;
object-fit: contain;
}
.fullscreen .close {
color: white;
position: absolute;
top: 16px;
right: 16px;
font-size: 14pt;
}
.actions {
.action {
display: flex;
flex-direction: row;
align-items: center;
margin-left: 8px;
&:first-child {
margin-left: 0px;
}
svg {
margin-right: 4px;
}
&.read svg {
position: relative;
top: 1.5px;
}
}
}
.transient {
Expand Down
Loading

0 comments on commit 21e535f

Please sign in to comment.