added favicons and youtube thumbnails

This commit is contained in:
pandadev 2024-07-05 18:43:40 +02:00
parent b65121148f
commit aa283284fe
No known key found for this signature in database
GPG key ID: C39629DACB8E762F
2 changed files with 65 additions and 12 deletions

56
app.vue
View file

@ -1,7 +1,7 @@
<template> <template>
<div class="bg" @keydown.down.prevent="selectNext" @keydown.up.prevent="selectPrevious" <div class="bg" @keydown.down.prevent="selectNext" @keydown.up.prevent="selectPrevious"
@keydown.enter.prevent="pasteSelectedItem" @keydown.esc="hideApp" tabindex="0"> @keydown.enter.prevent="pasteSelectedItem" @keydown.esc="hideApp" tabindex="0">
<input v-model="searchQuery" @input="searchHistory" autocorrect="off" autocapitalize="off" spellcheck="false" <input ref="searchInput" v-model="searchQuery" @input="searchHistory" autocorrect="off" autocapitalize="off" spellcheck="false"
class="search" type="text" placeholder="Type to filter entries..."> class="search" type="text" placeholder="Type to filter entries...">
<div class="bottom-bar"> <div class="bottom-bar">
<div class="left"> <div class="left">
@ -32,13 +32,17 @@
:class="['result clothoid-corner', { 'selected': isSelected(groupIndex, index) }]" :class="['result clothoid-corner', { 'selected': isSelected(groupIndex, index) }]"
@click="selectItem(groupIndex, index)" @click="selectItem(groupIndex, index)"
:ref="el => { if (isSelected(groupIndex, index)) selectedElement = el }"> :ref="el => { if (isSelected(groupIndex, index)) selectedElement = el }">
<FileIcon /> <img v-if="isUrl(item.content)" :src="getFavicon(item.content)" alt="Favicon" class="favicon">
{{ truncateContent(item.content) }} <FileIcon v-else />
<img v-if="item.type === 'image'" :src="item.content" alt="Image" class="preview-image">
<span v-else>{{ truncateContent(item.content) }}</span>
</div> </div>
</template> </template>
</OverlayScrollbarsComponent> </OverlayScrollbarsComponent>
<OverlayScrollbarsComponent class="content"> <OverlayScrollbarsComponent class="content">
{{ selectedItem?.content || '' }} <img v-if="selectedItem?.type === 'image'" :src="selectedItem.content" alt="Image" class="full-image">
<img v-else-if="isYoutubeWatchUrl(selectedItem?.content)" :src="getYoutubeThumbnail(selectedItem.content)" alt="YouTube Thumbnail" class="full-image">
<span v-else>{{ selectedItem?.content || '' }}</span>
</OverlayScrollbarsComponent> </OverlayScrollbarsComponent>
<Noise /> <Noise />
</div> </div>
@ -47,7 +51,6 @@
<script setup> <script setup>
import { ref, computed, onMounted, watch, nextTick } from 'vue'; import { ref, computed, onMounted, watch, nextTick } from 'vue';
import Database from '@tauri-apps/plugin-sql'; import Database from '@tauri-apps/plugin-sql';
import { register, unregister, isRegistered } from '@tauri-apps/plugin-global-shortcut';
import { writeText } from '@tauri-apps/plugin-clipboard-manager'; import { writeText } from '@tauri-apps/plugin-clipboard-manager';
import { OverlayScrollbarsComponent } from "overlayscrollbars-vue"; import { OverlayScrollbarsComponent } from "overlayscrollbars-vue";
import 'overlayscrollbars/overlayscrollbars.css'; import 'overlayscrollbars/overlayscrollbars.css';
@ -64,6 +67,7 @@ const selectedGroupIndex = ref(0);
const selectedItemIndex = ref(0); const selectedItemIndex = ref(0);
const resultsContainer = ref(null); const resultsContainer = ref(null);
const selectedElement = ref(null); const selectedElement = ref(null);
const searchInput = ref(null);
const os = platform(); const os = platform();
const groupedHistory = computed(() => { const groupedHistory = computed(() => {
@ -172,6 +176,35 @@ const truncateContent = (content) => {
return content.length > maxChars ? content.slice(0, maxChars - 3) + '...' : content; return content.length > maxChars ? content.slice(0, maxChars - 3) + '...' : content;
}; };
const isUrl = (str) => {
const urlPattern = /^(https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
return urlPattern.test(str);
};
const isYoutubeWatchUrl = (url) => {
return /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/watch\?v=[\w-]+/.test(url);
};
const getYoutubeThumbnail = (url) => {
const videoId = url.match(/[?&]v=([^&]+)/)[1];
return `https://img.youtube.com/vi/${videoId}/0.jpg`;
};
const getFavicon = (url) => {
const domain = url.replace(/^(https?:\/\/)?(www\.)?/, '').split('/')[0];
return `https://www.google.com/s2/favicons?domain=${domain}&sz=32`;
};
const refreshHistory = async () => {
const rawHistory = await db.value.select('SELECT * FROM history ORDER BY timestamp DESC');
history.value = rawHistory.map(item => {
if (item.type === 'image' && !item.content.startsWith('data:image')) {
return { ...item, content: `data:image/png;base64,${item.content}` };
}
return item;
});
};
onMounted(async () => { onMounted(async () => {
db.value = await Database.load('sqlite:data.db'); db.value = await Database.load('sqlite:data.db');
await refreshHistory(); await refreshHistory();
@ -181,12 +214,10 @@ onMounted(async () => {
} }
await listen('tauri://blur', hideApp); await listen('tauri://blur', hideApp);
await listen('tauri://focus', focusSearchInput);
focusSearchInput();
}); });
const refreshHistory = async () => {
history.value = await db.value.select('SELECT * FROM history ORDER BY timestamp DESC');
};
const hideApp = async () => { const hideApp = async () => {
await app.hide(); await app.hide();
await window.getCurrent().hide(); await window.getCurrent().hide();
@ -198,6 +229,13 @@ const showApp = async () => {
await window.getCurrent().show(); await window.getCurrent().show();
selectedGroupIndex.value = 0; selectedGroupIndex.value = 0;
selectedItemIndex.value = 0; selectedItemIndex.value = 0;
focusSearchInput();
};
const focusSearchInput = () => {
nextTick(() => {
searchInput.value?.focus();
});
}; };
const scrollToSelectedItem = () => { const scrollToSelectedItem = () => {

View file

@ -52,6 +52,10 @@ body,
background-color: transparent; background-color: transparent;
} }
.os-scrollbar-horizontal{
display: none;
}
.bg { .bg {
width: 750px; width: 750px;
height: 474px; height: 474px;
@ -120,15 +124,19 @@ body,
padding-bottom: 8px; padding-bottom: 8px;
padding-top: 14px; padding-top: 14px;
} }
.favicon{
width: 14px;
}
} }
.content { .content {
position: absolute; position: absolute;
top: 53px; top: 53px;
left: 284px; left: 284px;
padding: 10px; padding: 8px;
height: calc(100vh - 96px); height: calc(100vh - 96px);
padding-inline: 14px;
font-family: SFMonoRegular !important; font-family: SFMonoRegular !important;
font-size: 12px; font-size: 12px;
border-radius: 10px; border-radius: 10px;
@ -140,6 +148,13 @@ body,
border-radius: 10px; border-radius: 10px;
font-family: SFMonoRegular !important; font-family: SFMonoRegular !important;
} }
.full-image{
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
object-position: center;
}
} }
.bottom-bar { .bottom-bar {