mirror of
https://github.com/0PandaDEV/Qopy.git
synced 2025-04-21 21:24:05 +02:00
refactor: rename CSS classes for clarity, enhance layout responsiveness, and remove unused styles in index.scss; update Result.vue and index.vue for improved content rendering and structure
This commit is contained in:
parent
71dc5b273d
commit
96e5505b4d
3 changed files with 173 additions and 494 deletions
|
@ -25,7 +25,7 @@ main {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.container {
|
||||||
height: 376px;
|
height: 376px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -36,7 +36,7 @@ main {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 14px 8px;
|
padding: 14px 8px;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
width: 286px;
|
min-width: 286px;
|
||||||
border-right: 1px solid var(--border);
|
border-right: 1px solid var(--border);
|
||||||
|
|
||||||
.time-separator {
|
.time-separator {
|
||||||
|
@ -69,349 +69,99 @@ main {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// .bg {
|
.right {
|
||||||
// width: 100%;
|
display: flex;
|
||||||
// height: 100%;
|
flex-direction: column;
|
||||||
// background-color: $primary;
|
width: 100%;
|
||||||
// border: 1px solid $divider;
|
}
|
||||||
// border-radius: 12px;
|
|
||||||
// z-index: -1;
|
|
||||||
// position: fixed;
|
|
||||||
// outline: none;
|
|
||||||
// display: flex;
|
|
||||||
// flex-direction: column;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .search {
|
.content {
|
||||||
// width: 100%;
|
height: 100%;
|
||||||
// height: $search-height;
|
font-family: CommitMono !important;
|
||||||
// background-color: transparent;
|
font-size: 12px;
|
||||||
// outline: none;
|
letter-spacing: 1;
|
||||||
// border: none;
|
border-radius: 10px;
|
||||||
// font-size: 18px;
|
width: 100%;
|
||||||
// color: $text;
|
white-space: pre-wrap;
|
||||||
// padding-inline: 16px;
|
word-wrap: break-word;
|
||||||
// border-bottom: 1px solid $divider;
|
display: flex;
|
||||||
// font-family: SFRoundedMedium;
|
flex-direction: column;
|
||||||
// }
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 2;
|
||||||
|
color: $text;
|
||||||
|
|
||||||
// .main-container {
|
&:not(:has(.image)) {
|
||||||
// display: flex;
|
padding: 8px;
|
||||||
// flex: 1;
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// .results {
|
span {
|
||||||
// width: $sidebar-width;
|
font-family: CommitMono !important;
|
||||||
// height: calc(100vh - $search-height - $bottom-bar-height);
|
}
|
||||||
// border-right: 1px solid $divider;
|
|
||||||
// display: flex;
|
|
||||||
// flex-direction: column;
|
|
||||||
// padding-inline: 8px;
|
|
||||||
// padding-bottom: 8px;
|
|
||||||
// overflow-y: auto;
|
|
||||||
// overflow-x: hidden;
|
|
||||||
// z-index: 3;
|
|
||||||
|
|
||||||
// .result {
|
.image {
|
||||||
// height: 40px;
|
width: 100%;
|
||||||
// font-size: 14px;
|
height: 100%;
|
||||||
// display: flex;
|
object-fit: contain;
|
||||||
// align-items: center;
|
object-position: center;
|
||||||
// padding: 10px;
|
}
|
||||||
// letter-spacing: 0.5px;
|
}
|
||||||
// gap: 10px;
|
|
||||||
// overflow: hidden;
|
|
||||||
// text-overflow: clip;
|
|
||||||
// white-space: nowrap;
|
|
||||||
// color: $text;
|
|
||||||
// cursor: pointer;
|
|
||||||
|
|
||||||
// &.selected {
|
.information {
|
||||||
// background-color: $divider;
|
min-height: 160px;
|
||||||
// }
|
width: 100%;
|
||||||
// }
|
border-top: 1px solid $divider;
|
||||||
|
padding: 14px;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 14px;
|
||||||
|
|
||||||
// .time-separator {
|
.title {
|
||||||
// font-size: 12px;
|
font-family: SFRoundedSemiBold;
|
||||||
// color: $text2;
|
font-size: 12px;
|
||||||
// font-family: SFRoundedSemiBold;
|
letter-spacing: 0.6px;
|
||||||
// padding-left: 8px;
|
color: $text;
|
||||||
// padding-bottom: 8px;
|
}
|
||||||
// padding-top: 14px;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .favicon,
|
.info-content {
|
||||||
// .image,
|
display: flex;
|
||||||
// .icon {
|
gap: 0;
|
||||||
// width: 18px;
|
flex-direction: column;
|
||||||
// height: 18px;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .right-panel {
|
.info-row {
|
||||||
// display: flex;
|
display: flex;
|
||||||
// flex-direction: column;
|
width: 100%;
|
||||||
// flex: 1;
|
font-size: 12px;
|
||||||
// }
|
justify-content: space-between;
|
||||||
|
padding: 8px 0;
|
||||||
|
border-bottom: 1px solid $divider;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
// .content {
|
&:last-child {
|
||||||
// height: $content-view-height;
|
border-bottom: none;
|
||||||
// font-family: CommitMono !important;
|
padding-bottom: 0;
|
||||||
// font-size: 12px;
|
}
|
||||||
// letter-spacing: 1;
|
|
||||||
// border-radius: 10px;
|
|
||||||
// width: calc(100% - $sidebar-width);
|
|
||||||
// white-space: pre-wrap;
|
|
||||||
// word-wrap: break-word;
|
|
||||||
// display: flex;
|
|
||||||
// flex-direction: column;
|
|
||||||
// align-items: center;
|
|
||||||
// overflow: hidden;
|
|
||||||
// z-index: 2;
|
|
||||||
// color: $text;
|
|
||||||
|
|
||||||
// &:not(:has(.image)) {
|
&:first-child {
|
||||||
// padding: 8px;
|
padding-top: 22px;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// span {
|
p {
|
||||||
// font-family: CommitMono !important;
|
font-family: SFRoundedMedium;
|
||||||
// }
|
color: $text2;
|
||||||
|
font-weight: 500;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
// .image {
|
span {
|
||||||
// width: 100%;
|
font-family: CommitMono;
|
||||||
// height: 100%;
|
color: $text;
|
||||||
// object-fit: contain;
|
text-overflow: ellipsis;
|
||||||
// object-position: center;
|
overflow: hidden;
|
||||||
// }
|
white-space: nowrap;
|
||||||
// }
|
margin-left: 32px;
|
||||||
|
}
|
||||||
// .bottom-bar {
|
}
|
||||||
// height: $bottom-bar-height;
|
}
|
||||||
// width: 100%;
|
}
|
||||||
// backdrop-filter: blur(18px);
|
|
||||||
// background-color: hsla(40, 3%, 16%, 0.8);
|
|
||||||
// z-index: 100;
|
|
||||||
// border-radius: 0 0 12px 12px;
|
|
||||||
// display: flex;
|
|
||||||
// flex-direction: row;
|
|
||||||
// justify-content: space-between;
|
|
||||||
// padding-inline: 12px;
|
|
||||||
// padding-right: 6px;
|
|
||||||
// padding-top: 1px;
|
|
||||||
// align-items: center;
|
|
||||||
// font-size: 14px;
|
|
||||||
// border-top: 1px solid $divider;
|
|
||||||
|
|
||||||
// p {
|
|
||||||
// color: $text2;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .left {
|
|
||||||
// display: flex;
|
|
||||||
// align-items: center;
|
|
||||||
// gap: 8px;
|
|
||||||
|
|
||||||
// .logo {
|
|
||||||
// width: 18px;
|
|
||||||
// height: 18px;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .right {
|
|
||||||
// display: flex;
|
|
||||||
// align-items: center;
|
|
||||||
|
|
||||||
// .paste p {
|
|
||||||
// color: $text;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .actions div {
|
|
||||||
// display: flex;
|
|
||||||
// align-items: center;
|
|
||||||
// gap: 2px;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .divider {
|
|
||||||
// width: 2px;
|
|
||||||
// height: 12px;
|
|
||||||
// background-color: $divider;
|
|
||||||
// margin-left: 8px;
|
|
||||||
// margin-right: 4px;
|
|
||||||
// transition: all 0.2s;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .paste,
|
|
||||||
// .actions {
|
|
||||||
// padding: 4px;
|
|
||||||
// padding-left: 8px;
|
|
||||||
// display: flex;
|
|
||||||
// align-items: center;
|
|
||||||
// gap: 8px;
|
|
||||||
// border-radius: 7px;
|
|
||||||
// background-color: transparent;
|
|
||||||
// transition: all 0.2s;
|
|
||||||
// cursor: pointer;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .paste:hover,
|
|
||||||
// .actions:hover {
|
|
||||||
// background-color: $divider;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// &:hover .paste:hover ~ .divider,
|
|
||||||
// &:hover .actions:hover ~ .divider {
|
|
||||||
// opacity: 0;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .information {
|
|
||||||
// height: $info-panel-height;
|
|
||||||
// width: calc(100% - $sidebar-width);
|
|
||||||
// border-top: 1px solid $divider;
|
|
||||||
// background-color: $primary;
|
|
||||||
// padding: 14px;
|
|
||||||
// z-index: 1;
|
|
||||||
// display: flex;
|
|
||||||
// flex-direction: column;
|
|
||||||
// gap: 14px;
|
|
||||||
|
|
||||||
// .title {
|
|
||||||
// font-family: SFRoundedSemiBold;
|
|
||||||
// font-size: 12px;
|
|
||||||
// letter-spacing: 0.6px;
|
|
||||||
// color: $text;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .info-content {
|
|
||||||
// display: flex;
|
|
||||||
// gap: 0;
|
|
||||||
// flex-direction: column;
|
|
||||||
|
|
||||||
// .info-row {
|
|
||||||
// display: flex;
|
|
||||||
// width: 100%;
|
|
||||||
// font-size: 12px;
|
|
||||||
// justify-content: space-between;
|
|
||||||
// padding: 8px 0;
|
|
||||||
// border-bottom: 1px solid $divider;
|
|
||||||
// line-height: 1;
|
|
||||||
|
|
||||||
// &:last-child {
|
|
||||||
// border-bottom: none;
|
|
||||||
// padding-bottom: 0;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// &:first-child {
|
|
||||||
// padding-top: 22px;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// p {
|
|
||||||
// font-family: SFRoundedMedium;
|
|
||||||
// color: $text2;
|
|
||||||
// font-weight: 500;
|
|
||||||
// flex-shrink: 0;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// span {
|
|
||||||
// font-family: CommitMono;
|
|
||||||
// color: $text;
|
|
||||||
// text-overflow: ellipsis;
|
|
||||||
// overflow: hidden;
|
|
||||||
// white-space: nowrap;
|
|
||||||
// margin-left: 32px;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .clothoid-corner {
|
|
||||||
// clip-path: polygon(
|
|
||||||
// 13.890123px 0px,
|
|
||||||
// calc(100% - 13.890123px) 0px,
|
|
||||||
// calc(100% - 12.723414px) 0.004211px,
|
|
||||||
// calc(100% - 11.556933px) 0.025635px,
|
|
||||||
// calc(100% - 10.391895px) 0.085062px,
|
|
||||||
// calc(100% - 9.231074px) 0.199291px,
|
|
||||||
// calc(100% - 8.079275px) 0.382298px,
|
|
||||||
// calc(100% - 6.947448px) 0.662609px,
|
|
||||||
// calc(100% - 5.844179px) 1.039291px,
|
|
||||||
// calc(100% - 4.793324px) 1.542842px,
|
|
||||||
// calc(100% - 3.811369px) 2.169728px,
|
|
||||||
// calc(100% - 2.926417px) 2.926417px,
|
|
||||||
// calc(100% - 2.169728px) 3.811369px,
|
|
||||||
// calc(100% - 1.542842px) 4.793324px,
|
|
||||||
// calc(100% - 1.039291px) 5.844179px,
|
|
||||||
// calc(100% - 0.662609px) 6.947448px,
|
|
||||||
// calc(100% - 0.382298px) 8.079275px,
|
|
||||||
// calc(100% - 0.199291px) 9.231074px,
|
|
||||||
// calc(100% - 0.085062px) 10.391895px,
|
|
||||||
// calc(100% - 0.025635px) 11.556933px,
|
|
||||||
// calc(100% - 0.004211px) 12.723414px,
|
|
||||||
// 100% 13.890123px,
|
|
||||||
// 100% calc(100% - 13.890123px),
|
|
||||||
// calc(100% - 0.004211px) calc(100% - 12.723414px),
|
|
||||||
// calc(100% - 0.025635px) calc(100% - 11.556933px),
|
|
||||||
// calc(100% - 0.085062px) calc(100% - 10.391895px),
|
|
||||||
// calc(100% - 0.199291px) calc(100% - 9.231074px),
|
|
||||||
// calc(100% - 0.382298px) calc(100% - 8.079275px),
|
|
||||||
// calc(100% - 0.662609px) calc(100% - 6.947448px),
|
|
||||||
// calc(100% - 1.039291px) calc(100% - 5.844179px),
|
|
||||||
// calc(100% - 1.542842px) calc(100% - 4.793324px),
|
|
||||||
// calc(100% - 2.169728px) calc(100% - 3.811369px),
|
|
||||||
// calc(100% - 2.926417px) calc(100% - 2.926417px),
|
|
||||||
// calc(100% - 3.811369px) calc(100% - 2.169728px),
|
|
||||||
// calc(100% - 4.793324px) calc(100% - 1.542842px),
|
|
||||||
// calc(100% - 5.844179px) calc(100% - 1.039291px),
|
|
||||||
// calc(100% - 6.947448px) calc(100% - 0.662609px),
|
|
||||||
// calc(100% - 8.079275px) calc(100% - 0.382298px),
|
|
||||||
// calc(100% - 9.231074px) calc(100% - 0.199291px),
|
|
||||||
// calc(100% - 10.391895px) calc(100% - 0.085062px),
|
|
||||||
// calc(100% - 11.556933px) calc(100% - 0.025635px),
|
|
||||||
// calc(100% - 12.723414px) calc(100% - 0.004211px),
|
|
||||||
// calc(100% - 13.890123px) 100%,
|
|
||||||
// 13.890123px 100%,
|
|
||||||
// 12.723414px calc(100% - 0.004211px),
|
|
||||||
// 11.556933px calc(100% - 0.025635px),
|
|
||||||
// 10.391895px calc(100% - 0.085062px),
|
|
||||||
// 9.231074px calc(100% - 0.199291px),
|
|
||||||
// 8.079275px calc(100% - 0.382298px),
|
|
||||||
// 6.947448px calc(100% - 0.662609px),
|
|
||||||
// 5.844179px calc(100% - 1.039291px),
|
|
||||||
// 4.793324px calc(100% - 1.542842px),
|
|
||||||
// 3.811369px calc(100% - 2.169728px),
|
|
||||||
// 2.926417px calc(100% - 2.926417px),
|
|
||||||
// 2.169728px calc(100% - 3.811369px),
|
|
||||||
// 1.542842px calc(100% - 4.793324px),
|
|
||||||
// 1.039291px calc(100% - 5.844179px),
|
|
||||||
// 0.662609px calc(100% - 6.947448px),
|
|
||||||
// 0.382298px calc(100% - 8.079275px),
|
|
||||||
// 0.199291px calc(100% - 9.231074px),
|
|
||||||
// 0.085062px calc(100% - 10.391895px),
|
|
||||||
// 0.025635px calc(100% - 11.556933px),
|
|
||||||
// 0.004211px calc(100% - 12.723414px),
|
|
||||||
// 0px calc(100% - 13.890123px),
|
|
||||||
// 0px 13.890123px,
|
|
||||||
// 0.004211px 12.723414px,
|
|
||||||
// 0.025635px 11.556933px,
|
|
||||||
// 0.085062px 10.391895px,
|
|
||||||
// 0.199291px 9.231074px,
|
|
||||||
// 0.382298px 8.079275px,
|
|
||||||
// 0.662609px 6.947448px,
|
|
||||||
// 1.039291px 5.844179px,
|
|
||||||
// 1.542842px 4.793324px,
|
|
||||||
// 2.169728px 3.811369px,
|
|
||||||
// 2.926417px 2.926417px,
|
|
||||||
// 3.811369px 2.169728px,
|
|
||||||
// 4.793324px 1.542842px,
|
|
||||||
// 5.844179px 1.039291px,
|
|
||||||
// 6.947448px 0.662609px,
|
|
||||||
// 8.079275px 0.382298px,
|
|
||||||
// 9.231074px 0.199291px,
|
|
||||||
// 10.391895px 0.085062px,
|
|
||||||
// 11.556933px 0.025635px,
|
|
||||||
// 12.723414px 0.004211px,
|
|
||||||
// 13.890123px 0px
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:class="['result clothoid-corner', { selected }]"
|
:class="['result', { selected }]"
|
||||||
@click="$emit('select')"
|
@click="$emit('select')"
|
||||||
:ref="el => { if (selected && el) $emit('setRef', el as HTMLElement) }">
|
:ref="el => { if (selected && el) $emit('setRef', el as HTMLElement) }">
|
||||||
<template v-if="item.content_type === 'image'">
|
<template v-if="item.content_type === 'image'">
|
||||||
|
|
235
pages/index.vue
235
pages/index.vue
|
@ -1,9 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<main>
|
<main>
|
||||||
<TopBar
|
<TopBar ref="topBar" @search="searchHistory" />
|
||||||
ref="topBar"
|
<div class="container">
|
||||||
@search="searchHistory" />
|
|
||||||
<div class="content">
|
|
||||||
<OverlayScrollbarsComponent
|
<OverlayScrollbarsComponent
|
||||||
class="results"
|
class="results"
|
||||||
ref="resultsContainer"
|
ref="resultsContainer"
|
||||||
|
@ -23,113 +21,55 @@
|
||||||
:dimensions="imageDimensions[item.id]"
|
:dimensions="imageDimensions[item.id]"
|
||||||
@select="selectItem(groupIndex, index)"
|
@select="selectItem(groupIndex, index)"
|
||||||
@image-error="onImageError"
|
@image-error="onImageError"
|
||||||
@setRef="el => selectedElement = el" />
|
@setRef="(el) => (selectedElement = el)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</OverlayScrollbarsComponent>
|
</OverlayScrollbarsComponent>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<div class="content"></div>
|
<div
|
||||||
<div class="information"></div>
|
class="content"
|
||||||
|
v-if="selectedItem?.content_type === ContentType.Image">
|
||||||
|
<img :src="imageUrls[selectedItem.id]" alt="Image" class="image" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else-if="selectedItem && isYoutubeWatchUrl(selectedItem.content)"
|
||||||
|
class="content">
|
||||||
|
<img
|
||||||
|
class="image"
|
||||||
|
:src="getYoutubeThumbnail(selectedItem.content)"
|
||||||
|
alt="YouTube Thumbnail" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
v-else-if="
|
||||||
|
selectedItem?.content_type === ContentType.Link && pageOgImage
|
||||||
|
">
|
||||||
|
<img :src="pageOgImage" alt="Image" class="image" />
|
||||||
|
</div>
|
||||||
|
<OverlayScrollbarsComponent v-else class="content">
|
||||||
|
<span>{{ selectedItem?.content || "" }}</span>
|
||||||
|
</OverlayScrollbarsComponent>
|
||||||
|
<OverlayScrollbarsComponent
|
||||||
|
class="information"
|
||||||
|
:options="{ scrollbars: { autoHide: 'scroll' } }">
|
||||||
|
<div class="title">Information</div>
|
||||||
|
<div class="info-content" v-if="selectedItem && getInfo">
|
||||||
|
<div class="info-row" v-for="(row, index) in infoRows" :key="index">
|
||||||
|
<p class="label">{{ row.label }}</p>
|
||||||
|
<span
|
||||||
|
:class="{ 'url-truncate': row.isUrl }"
|
||||||
|
:data-text="row.value">
|
||||||
|
{{ row.value }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</OverlayScrollbarsComponent>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<BottomBar />
|
<BottomBar />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- <div class="bg" tabindex="0">
|
<!-- <div class="right-panel">
|
||||||
<input
|
|
||||||
ref="searchInput"
|
|
||||||
v-model="searchQuery"
|
|
||||||
@input="searchHistory"
|
|
||||||
autocorrect="off"
|
|
||||||
autocapitalize="off"
|
|
||||||
spellcheck="false"
|
|
||||||
class="search"
|
|
||||||
type="text"
|
|
||||||
placeholder="Type to filter entries..." />
|
|
||||||
|
|
||||||
<div class="main-container">
|
|
||||||
<OverlayScrollbarsComponent
|
|
||||||
class="results"
|
|
||||||
ref="resultsContainer"
|
|
||||||
:options="{ scrollbars: { autoHide: 'scroll' } }">
|
|
||||||
<template v-for="(group, groupIndex) in groupedHistory" :key="groupIndex">
|
|
||||||
<div class="time-separator">{{ group.label }}</div>
|
|
||||||
<div
|
|
||||||
v-for="(item, index) in group.items"
|
|
||||||
:key="item.id"
|
|
||||||
:class="[
|
|
||||||
'result clothoid-corner',
|
|
||||||
{ selected: isSelected(groupIndex, index) },
|
|
||||||
]"
|
|
||||||
@click="selectItem(groupIndex, index)"
|
|
||||||
:ref="
|
|
||||||
(el: any) => {
|
|
||||||
if (isSelected(groupIndex, index))
|
|
||||||
selectedElement = el as HTMLElement;
|
|
||||||
}
|
|
||||||
">
|
|
||||||
<template v-if="item.content_type === 'image'">
|
|
||||||
<img
|
|
||||||
v-if="imageUrls[item.id]"
|
|
||||||
:src="imageUrls[item.id]"
|
|
||||||
alt="Image"
|
|
||||||
class="image"
|
|
||||||
@error="onImageError" />
|
|
||||||
<img v-else src="../public/icons/Image.svg" class="icon" />
|
|
||||||
</template>
|
|
||||||
<template v-else-if="hasFavicon(item.favicon ?? '')">
|
|
||||||
<img
|
|
||||||
:src="
|
|
||||||
item.favicon
|
|
||||||
? getFaviconFromDb(item.favicon)
|
|
||||||
: '../public/icons/Link.svg'
|
|
||||||
"
|
|
||||||
alt="Favicon"
|
|
||||||
class="favicon"
|
|
||||||
@error="
|
|
||||||
($event.target as HTMLImageElement).src =
|
|
||||||
'../public/icons/Link.svg'
|
|
||||||
" />
|
|
||||||
</template>
|
|
||||||
<img
|
|
||||||
src="../public/icons/File.svg"
|
|
||||||
class="icon"
|
|
||||||
v-else-if="item.content_type === ContentType.File" />
|
|
||||||
<img
|
|
||||||
src="../public/icons/Text.svg"
|
|
||||||
class="icon"
|
|
||||||
v-else-if="item.content_type === ContentType.Text" />
|
|
||||||
<div v-else-if="item.content_type === ContentType.Color">
|
|
||||||
<svg
|
|
||||||
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>
|
|
||||||
</div>
|
|
||||||
<img
|
|
||||||
src="../public/icons/Code.svg"
|
|
||||||
class="icon"
|
|
||||||
v-else-if="item.content_type === ContentType.Code" />
|
|
||||||
<span v-if="item.content_type === ContentType.Image">
|
|
||||||
Image ({{ imageDimensions[item.id] || "Loading..." }})
|
|
||||||
</span>
|
|
||||||
<span v-else>{{ truncateContent(item.content) }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</OverlayScrollbarsComponent>
|
|
||||||
|
|
||||||
<div class="right-panel">
|
|
||||||
<div
|
<div
|
||||||
class="content"
|
class="content"
|
||||||
v-if="selectedItem?.content_type === ContentType.Image">
|
v-if="selectedItem?.content_type === ContentType.Image">
|
||||||
|
@ -168,33 +108,6 @@
|
||||||
</div>
|
</div>
|
||||||
</OverlayScrollbarsComponent>
|
</OverlayScrollbarsComponent>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bottom-bar">
|
|
||||||
<div class="left">
|
|
||||||
<img class="logo" width="18px" src="../public/logo.png" alt="" />
|
|
||||||
<p>Qopy</p>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<div class="paste" @click="pasteSelectedItem">
|
|
||||||
<p>Paste</p>
|
|
||||||
<img src="../public/enter.svg" alt="" />
|
|
||||||
</div>
|
|
||||||
<div class="divider"></div>
|
|
||||||
<div class="actions">
|
|
||||||
<p>Actions</p>
|
|
||||||
<div>
|
|
||||||
<img
|
|
||||||
v-if="os === 'windows' || os === 'linux'"
|
|
||||||
src="../public/ctrl.svg"
|
|
||||||
alt="" />
|
|
||||||
<img v-if="os === 'macos'" src="../public/cmd.svg" alt="" />
|
|
||||||
<img src="../public/k.svg" alt="" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Noise />
|
|
||||||
</div> -->
|
</div> -->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -217,7 +130,12 @@ import type {
|
||||||
InfoCode,
|
InfoCode,
|
||||||
} from "~/types/types";
|
} from "~/types/types";
|
||||||
import { Key, keyboard } from "wrdu-keyboard";
|
import { Key, keyboard } from "wrdu-keyboard";
|
||||||
import { selectedGroupIndex, selectedItemIndex, selectedElement, useSelectedResult } from '~/lib/selectedResult'
|
import {
|
||||||
|
selectedGroupIndex,
|
||||||
|
selectedItemIndex,
|
||||||
|
selectedElement,
|
||||||
|
useSelectedResult,
|
||||||
|
} from "~/lib/selectedResult";
|
||||||
|
|
||||||
interface GroupedHistory {
|
interface GroupedHistory {
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -250,7 +168,7 @@ const imageLoading = ref<boolean>(false);
|
||||||
const pageTitle = ref<string>("");
|
const pageTitle = ref<string>("");
|
||||||
const pageOgImage = ref<string>("");
|
const pageOgImage = ref<string>("");
|
||||||
|
|
||||||
const topBar = ref<{ searchInput: HTMLInputElement | null } | null>(null)
|
const topBar = ref<{ searchInput: HTMLInputElement | null } | null>(null);
|
||||||
|
|
||||||
const isSameDay = (date1: Date, date2: Date): boolean => {
|
const isSameDay = (date1: Date, date2: Date): boolean => {
|
||||||
return (
|
return (
|
||||||
|
@ -313,7 +231,8 @@ const groupedHistory = computed<GroupedHistory[]>(() => {
|
||||||
.map(([label, items]) => ({ label, items }));
|
.map(([label, items]) => ({ label, items }));
|
||||||
});
|
});
|
||||||
|
|
||||||
const { selectedItem, isSelected, selectNext, selectPrevious, selectItem } = useSelectedResult(groupedHistory)
|
const { selectedItem, isSelected, selectNext, selectPrevious, selectItem } =
|
||||||
|
useSelectedResult(groupedHistory);
|
||||||
|
|
||||||
const loadHistoryChunk = async (): Promise<void> => {
|
const loadHistoryChunk = async (): Promise<void> => {
|
||||||
if (isLoading) return;
|
if (isLoading) return;
|
||||||
|
@ -411,13 +330,19 @@ const scrollToSelectedItem = (): void => {
|
||||||
|
|
||||||
if (isAbove) {
|
if (isAbove) {
|
||||||
viewport.scrollTo({
|
viewport.scrollTo({
|
||||||
top: viewport.scrollTop + (elementRect.top - viewportRect.top) - (isFirstItemInGroup ? TOP_SCROLL_PADDING : SCROLL_PADDING),
|
top:
|
||||||
behavior: "smooth"
|
viewport.scrollTop +
|
||||||
|
(elementRect.top - viewportRect.top) -
|
||||||
|
(isFirstItemInGroup ? TOP_SCROLL_PADDING : SCROLL_PADDING),
|
||||||
|
behavior: "smooth",
|
||||||
});
|
});
|
||||||
} else if (isBelow) {
|
} else if (isBelow) {
|
||||||
viewport.scrollTo({
|
viewport.scrollTo({
|
||||||
top: viewport.scrollTop + (elementRect.bottom - viewportRect.bottom) + SCROLL_PADDING,
|
top:
|
||||||
behavior: "smooth"
|
viewport.scrollTop +
|
||||||
|
(elementRect.bottom - viewportRect.bottom) +
|
||||||
|
SCROLL_PADDING,
|
||||||
|
behavior: "smooth",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, 10);
|
}, 10);
|
||||||
|
@ -425,15 +350,15 @@ const scrollToSelectedItem = (): void => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const searchHistory = async (query: string): Promise<void> => {
|
const searchHistory = async (query: string): Promise<void> => {
|
||||||
searchQuery.value = query
|
searchQuery.value = query;
|
||||||
if (!query.trim()) {
|
if (!query.trim()) {
|
||||||
history.value = []
|
history.value = [];
|
||||||
offset = 0
|
offset = 0;
|
||||||
await loadHistoryChunk()
|
await loadHistoryChunk();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = await $history.searchHistory(query)
|
const results = await $history.searchHistory(query);
|
||||||
history.value = results.map((item) =>
|
history.value = results.map((item) =>
|
||||||
Object.assign(
|
Object.assign(
|
||||||
new HistoryItem(
|
new HistoryItem(
|
||||||
|
@ -446,12 +371,12 @@ const searchHistory = async (query: string): Promise<void> => {
|
||||||
),
|
),
|
||||||
{ id: item.id, timestamp: new Date(item.timestamp) }
|
{ id: item.id, timestamp: new Date(item.timestamp) }
|
||||||
)
|
)
|
||||||
)
|
);
|
||||||
|
|
||||||
if (groupedHistory.value.length > 0) {
|
if (groupedHistory.value.length > 0) {
|
||||||
handleSelection(0, 0, false)
|
handleSelection(0, 0, false);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const pasteSelectedItem = async (): Promise<void> => {
|
const pasteSelectedItem = async (): Promise<void> => {
|
||||||
if (!selectedItem.value) return;
|
if (!selectedItem.value) return;
|
||||||
|
@ -579,9 +504,9 @@ const handleSelection = (
|
||||||
itemIndex: number,
|
itemIndex: number,
|
||||||
shouldScroll: boolean = true
|
shouldScroll: boolean = true
|
||||||
): void => {
|
): void => {
|
||||||
selectItem(groupIndex, itemIndex)
|
selectItem(groupIndex, itemIndex);
|
||||||
if (shouldScroll) scrollToSelectedItem()
|
if (shouldScroll) scrollToSelectedItem();
|
||||||
}
|
};
|
||||||
|
|
||||||
const setupEventListeners = async (): Promise<void> => {
|
const setupEventListeners = async (): Promise<void> => {
|
||||||
await listen("clipboard-content-updated", async () => {
|
await listen("clipboard-content-updated", async () => {
|
||||||
|
@ -689,18 +614,22 @@ const hideApp = async (): Promise<void> => {
|
||||||
|
|
||||||
const focusSearchInput = (): void => {
|
const focusSearchInput = (): void => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
topBar.value?.searchInput?.focus()
|
topBar.value?.searchInput?.focus();
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const onImageError = (): void => {
|
const onImageError = (): void => {
|
||||||
imageLoadError.value = true;
|
imageLoadError.value = true;
|
||||||
imageLoading.value = false;
|
imageLoading.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
watch([selectedGroupIndex, selectedItemIndex], () => {
|
watch(
|
||||||
|
[selectedGroupIndex, selectedItemIndex],
|
||||||
|
() => {
|
||||||
scrollToSelectedItem();
|
scrollToSelectedItem();
|
||||||
}, { flush: 'post' });
|
},
|
||||||
|
{ flush: "post" }
|
||||||
|
);
|
||||||
|
|
||||||
watch(searchQuery, () => {
|
watch(searchQuery, () => {
|
||||||
searchHistory(searchQuery.value);
|
searchHistory(searchQuery.value);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue