mirror of
https://github.com/0PandaDEV/Qopy.git
synced 2025-04-21 13:14:04 +02:00
feat: add Result component for displaying history items with dynamic content rendering
This commit is contained in:
parent
7b624bd352
commit
180664c0fd
3 changed files with 151 additions and 7 deletions
30
app.vue
30
app.vue
|
@ -1,5 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div style="pointer-events: auto">
|
<div>
|
||||||
|
<Noise />
|
||||||
<NuxtPage />
|
<NuxtPage />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -12,7 +13,7 @@ import { onMounted } from "vue";
|
||||||
import { keyboard } from "wrdu-keyboard";
|
import { keyboard } from "wrdu-keyboard";
|
||||||
|
|
||||||
const { $settings } = useNuxtApp();
|
const { $settings } = useNuxtApp();
|
||||||
keyboard.init()
|
keyboard.init();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await listen("settings", async () => {
|
await listen("settings", async () => {
|
||||||
|
@ -58,6 +59,24 @@ onMounted(async () => {
|
||||||
src: url("~/assets/fonts/CommitMono.woff2") format("woff2");
|
src: url("~/assets/fonts/CommitMono.woff2") format("woff2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--background: #2e2d2b;
|
||||||
|
--accent: #feb453;
|
||||||
|
--border: #ffffff0d;
|
||||||
|
|
||||||
|
--text: #e5dfd5;
|
||||||
|
--text-secondary: #ada9a1;
|
||||||
|
--text-muted: #78756f;
|
||||||
|
|
||||||
|
--sidebar-width: 286px;
|
||||||
|
--bottom-bar-height: 39px;
|
||||||
|
--info-panel-height: 160px;
|
||||||
|
--content-view-height: calc(
|
||||||
|
100% - var(--search-height) - var(--info-panel-height) -
|
||||||
|
var(--bottom-bar-height)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -66,9 +85,6 @@ onMounted(async () => {
|
||||||
font-family: SFRoundedRegular;
|
font-family: SFRoundedRegular;
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
user-select: none;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
--os-handle-bg: #ada9a1;
|
--os-handle-bg: #ada9a1;
|
||||||
--os-handle-bg-hover: #78756f;
|
--os-handle-bg-hover: #78756f;
|
||||||
|
@ -80,8 +96,8 @@ body {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
width: 750px;
|
width: 750px;
|
||||||
height: 474px;
|
height: 474px;
|
||||||
user-select: none !important;
|
z-index: -1;
|
||||||
pointer-events: none !important;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.os-scrollbar-horizontal {
|
.os-scrollbar-horizontal {
|
||||||
|
|
121
components/Result.vue
Normal file
121
components/Result.vue
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="['result clothoid-corner', { selected }]"
|
||||||
|
@click="$emit('select')"
|
||||||
|
:ref="el => { if (selected && el) $emit('setRef', el as HTMLElement) }">
|
||||||
|
<template v-if="item.content_type === 'image'">
|
||||||
|
<img
|
||||||
|
v-if="imageUrl"
|
||||||
|
:src="imageUrl"
|
||||||
|
alt="Image"
|
||||||
|
class="image"
|
||||||
|
@error="$emit('imageError')" />
|
||||||
|
<IconsImage v-else class="icon" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="hasFavicon(item.favicon ?? '')">
|
||||||
|
<img
|
||||||
|
v-if="item.favicon"
|
||||||
|
:src="getFaviconFromDb(item.favicon)"
|
||||||
|
alt="Favicon"
|
||||||
|
class="favicon"
|
||||||
|
@error="
|
||||||
|
($event.target as HTMLImageElement).src = '/public/icons/Link.svg'
|
||||||
|
" />
|
||||||
|
<IconsLink v-else class="icon" />
|
||||||
|
</template>
|
||||||
|
<IconsFile
|
||||||
|
class="icon"
|
||||||
|
v-else-if="item.content_type === ContentType.File" />
|
||||||
|
<IconsText
|
||||||
|
class="icon"
|
||||||
|
v-else-if="item.content_type === ContentType.Text" />
|
||||||
|
<svg
|
||||||
|
v-else-if="item.content_type === ContentType.Color"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
viewBox="0 0 18 18"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g>
|
||||||
|
<rect width="18" height="18" />
|
||||||
|
<path
|
||||||
|
d="M9 18C12.2154 18 15.1865 16.2846 16.7942 13.5C18.4019 10.7154 18.4019 7.28461 16.7942 4.5C15.1865 1.71539 12.2154 -1.22615e-06 9 0C5.78461 0 2.81347 1.71539 1.20577 4.5C-0.401925 7.28461 -0.401923 10.7154 1.20577 13.5C2.81347 16.2846 5.78461 18 9 18Z"
|
||||||
|
fill="#E5DFD5" />
|
||||||
|
<path
|
||||||
|
d="M9 16C7.14348 16 5.36301 15.2625 4.05025 13.9497C2.7375 12.637 2 10.8565 2 9C2 7.14348 2.7375 5.36301 4.05025 4.05025C5.36301 2.7375 7.14348 2 9 2C10.8565 2 12.637 2.7375 13.9497 4.05025C15.2625 5.36301 16 7.14348 16 9C16 10.8565 15.2625 12.637 13.9497 13.9497C12.637 15.2625 10.8565 16 9 16Z"
|
||||||
|
:fill="item.content" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
<IconsCode
|
||||||
|
class="icon"
|
||||||
|
v-else-if="item.content_type === ContentType.Code" />
|
||||||
|
<span v-if="item.content_type === ContentType.Image">
|
||||||
|
Image ({{ dimensions || "Loading..." }})
|
||||||
|
</span>
|
||||||
|
<span v-else>{{ truncateContent(item.content) }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ContentType } from "~/types/types";
|
||||||
|
import type { HistoryItem } from "~/types/types";
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
item: HistoryItem;
|
||||||
|
selected: boolean;
|
||||||
|
imageUrl?: string;
|
||||||
|
dimensions?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
(e: "select"): void;
|
||||||
|
(e: "imageError"): void;
|
||||||
|
(e: "setRef", el: HTMLElement): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const hasFavicon = (str: string): boolean => {
|
||||||
|
return str.trim() !== "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFaviconFromDb = (favicon: string): string => {
|
||||||
|
return `data:image/png;base64,${favicon}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const truncateContent = (content: string): string => {
|
||||||
|
const maxWidth = 284;
|
||||||
|
const charWidth = 9;
|
||||||
|
const maxChars = Math.floor(maxWidth / charWidth);
|
||||||
|
return content.length > maxChars
|
||||||
|
? content.slice(0, maxChars - 3) + "..."
|
||||||
|
: content;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.result {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 11px;
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background-color: var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.favicon,
|
||||||
|
.image,
|
||||||
|
.icon {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -3,6 +3,13 @@ export default defineNuxtConfig({
|
||||||
devtools: { enabled: false },
|
devtools: { enabled: false },
|
||||||
compatibilityDate: "2024-07-04",
|
compatibilityDate: "2024-07-04",
|
||||||
ssr: false,
|
ssr: false,
|
||||||
|
app: {
|
||||||
|
head: {
|
||||||
|
charset: "utf-8",
|
||||||
|
viewport:
|
||||||
|
"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0",
|
||||||
|
},
|
||||||
|
},
|
||||||
vite: {
|
vite: {
|
||||||
css: {
|
css: {
|
||||||
preprocessorOptions: {
|
preprocessorOptions: {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue