mirror of
https://github.com/0PandaDEV/Qopy.git
synced 2025-04-22 05:34:04 +02:00
feat: detect color
This commit is contained in:
parent
345f7e3f09
commit
4ab938a3de
3 changed files with 126 additions and 27 deletions
|
@ -63,11 +63,13 @@
|
||||||
@error="onImageError" />
|
@error="onImageError" />
|
||||||
<img v-else src="../public/icons/Image.svg" class="icon" />
|
<img v-else src="../public/icons/Image.svg" class="icon" />
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else-if="hasFavicon(item.favicon ?? '')">
|
||||||
<img
|
<img
|
||||||
v-else-if="hasFavicon(item.favicon ?? '')"
|
:src="item.favicon ? getFaviconFromDb(item.favicon) : '../public/icons/Link.svg'"
|
||||||
:src="getFaviconFromDb(item.favicon ?? '')"
|
|
||||||
alt="Favicon"
|
alt="Favicon"
|
||||||
class="favicon" />
|
class="favicon"
|
||||||
|
@error="($event.target as HTMLImageElement).src = '../public/icons/Link.svg'" />
|
||||||
|
</template>
|
||||||
<img
|
<img
|
||||||
src="../public/icons/File.svg"
|
src="../public/icons/File.svg"
|
||||||
class="icon"
|
class="icon"
|
||||||
|
@ -76,6 +78,24 @@
|
||||||
src="../public/icons/Text.svg"
|
src="../public/icons/Text.svg"
|
||||||
class="icon"
|
class="icon"
|
||||||
v-else-if="item.content_type === ContentType.Text" />
|
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
|
<img
|
||||||
src="../public/icons/Code.svg"
|
src="../public/icons/Code.svg"
|
||||||
class="icon"
|
class="icon"
|
||||||
|
@ -170,14 +190,50 @@
|
||||||
<!-- Color Information -->
|
<!-- Color Information -->
|
||||||
<template v-if="selectedItem.content_type === ContentType.Color">
|
<template v-if="selectedItem.content_type === ContentType.Color">
|
||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
<p class="label">Color</p>
|
<p class="label">Hex Code</p>
|
||||||
<div
|
<span>{{ selectedItem.content }}</span>
|
||||||
class="color-preview"
|
|
||||||
:style="{ backgroundColor: selectedItem.content }"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
<p class="label">Value</p>
|
<p class="label">RGB</p>
|
||||||
<span>{{ selectedItem.content }}</span>
|
<span>{{
|
||||||
|
selectedItem.content.startsWith("#")
|
||||||
|
? `rgb(${parseInt(
|
||||||
|
selectedItem.content.slice(1, 3),
|
||||||
|
16
|
||||||
|
)}, ${parseInt(
|
||||||
|
selectedItem.content.slice(3, 5),
|
||||||
|
16
|
||||||
|
)}, ${parseInt(selectedItem.content.slice(5, 7), 16)})`
|
||||||
|
: selectedItem.content
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<p class="label">HSL</p>
|
||||||
|
<span>{{
|
||||||
|
selectedItem.content.startsWith("#")
|
||||||
|
? (() => {
|
||||||
|
const r =
|
||||||
|
parseInt(selectedItem.content.slice(1, 3), 16) / 255;
|
||||||
|
const g =
|
||||||
|
parseInt(selectedItem.content.slice(3, 5), 16) / 255;
|
||||||
|
const b =
|
||||||
|
parseInt(selectedItem.content.slice(5, 7), 16) / 255;
|
||||||
|
const max = Math.max(r, g, b);
|
||||||
|
const min = Math.min(r, g, b);
|
||||||
|
const l = (max + min) / 2;
|
||||||
|
const d = max - min;
|
||||||
|
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||||
|
let h = 0;
|
||||||
|
if (max === r) h = (g - b) / d + (g < b ? 6 : 0);
|
||||||
|
if (max === g) h = (b - r) / d + 2;
|
||||||
|
if (max === b) h = (r - g) / d + 4;
|
||||||
|
h = Math.round(h * 60);
|
||||||
|
return `hsl(${h}, ${Math.round(s * 100)}%, ${Math.round(
|
||||||
|
l * 100
|
||||||
|
)}%)`;
|
||||||
|
})()
|
||||||
|
: selectedItem.content
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -600,10 +656,7 @@ const setupEventListeners = async (): Promise<void> => {
|
||||||
await listen("clipboard-content-updated", async () => {
|
await listen("clipboard-content-updated", async () => {
|
||||||
lastUpdateTime.value = Date.now();
|
lastUpdateTime.value = Date.now();
|
||||||
await updateHistory(true);
|
await updateHistory(true);
|
||||||
if (
|
if (groupedHistory.value[0]?.items.length > 0) {
|
||||||
groupedHistory.value.length > 0 &&
|
|
||||||
groupedHistory.value[0].items.length > 0
|
|
||||||
) {
|
|
||||||
handleSelection(0, 0, false);
|
handleSelection(0, 0, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -623,17 +676,12 @@ const setupEventListeners = async (): Promise<void> => {
|
||||||
lastUpdateTime.value = currentTime;
|
lastUpdateTime.value = currentTime;
|
||||||
handleSelection(previousState.groupIndex, previousState.itemIndex, false);
|
handleSelection(previousState.groupIndex, previousState.itemIndex, false);
|
||||||
|
|
||||||
nextTick(() => {
|
if (resultsContainer.value?.osInstance()?.elements().viewport?.scrollTo) {
|
||||||
const viewport = resultsContainer.value
|
resultsContainer.value.osInstance()?.elements().viewport?.scrollTo({
|
||||||
?.osInstance()
|
|
||||||
?.elements().viewport;
|
|
||||||
if (viewport) {
|
|
||||||
viewport.scrollTo({
|
|
||||||
top: previousState.scroll,
|
top: previousState.scroll,
|
||||||
behavior: "instant",
|
behavior: "instant",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
focusSearchInput();
|
focusSearchInput();
|
||||||
});
|
});
|
||||||
|
@ -749,7 +797,7 @@ const getFormattedDate = computed(() => {
|
||||||
if (!selectedItem.value?.timestamp) return "";
|
if (!selectedItem.value?.timestamp) return "";
|
||||||
return new Intl.DateTimeFormat("en-US", {
|
return new Intl.DateTimeFormat("en-US", {
|
||||||
dateStyle: "medium",
|
dateStyle: "medium",
|
||||||
timeStyle: "short",
|
timeStyle: "medium",
|
||||||
}).format(selectedItem.value.timestamp);
|
}).format(selectedItem.value.timestamp);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -164,6 +164,11 @@ pub fn setup<R: Runtime>(app: &AppHandle<R>) {
|
||||||
pool,
|
pool,
|
||||||
HistoryItem::new(app_name, ContentType::Code, text, None, app_icon, Some(language))
|
HistoryItem::new(app_name, ContentType::Code, text, None, app_icon, Some(language))
|
||||||
).await;
|
).await;
|
||||||
|
} else if crate::utils::commands::detect_color(&text) {
|
||||||
|
let _ = db::history::add_history_item(
|
||||||
|
pool,
|
||||||
|
HistoryItem::new(app_name, ContentType::Color, text, None, app_icon, None)
|
||||||
|
).await;
|
||||||
} else {
|
} else {
|
||||||
let _ = db::history::add_history_item(
|
let _ = db::history::add_history_item(
|
||||||
pool,
|
pool,
|
||||||
|
|
|
@ -49,3 +49,49 @@ fn _process_icon_to_base64(path: &str) -> Result<String, Box<dyn std::error::Err
|
||||||
resized.write_with_encoder(PngEncoder::new(&mut png_buffer))?;
|
resized.write_with_encoder(PngEncoder::new(&mut png_buffer))?;
|
||||||
Ok(STANDARD.encode(png_buffer))
|
Ok(STANDARD.encode(png_buffer))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn detect_color(color: &str) -> bool {
|
||||||
|
let color = color.trim().to_lowercase();
|
||||||
|
|
||||||
|
// hex
|
||||||
|
if color.starts_with('#') && color.len() == color.trim_end_matches(char::is_whitespace).len() {
|
||||||
|
let hex = &color[1..];
|
||||||
|
return match hex.len() {
|
||||||
|
3 | 6 | 8 => hex.chars().all(|c| c.is_ascii_hexdigit()),
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// rgb/rgba
|
||||||
|
if (color.starts_with("rgb(") || color.starts_with("rgba(")) && color.ends_with(")") && !color[..color.len()-1].contains(")") {
|
||||||
|
let values = color
|
||||||
|
.trim_start_matches("rgba(")
|
||||||
|
.trim_start_matches("rgb(")
|
||||||
|
.trim_end_matches(')')
|
||||||
|
.split(',')
|
||||||
|
.collect::<Vec<&str>>();
|
||||||
|
|
||||||
|
return match values.len() {
|
||||||
|
3 | 4 => values.iter().all(|v| v.trim().parse::<f32>().is_ok()),
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// hsl/hsla
|
||||||
|
if (color.starts_with("hsl(") || color.starts_with("hsla(")) && color.ends_with(")") && !color[..color.len()-1].contains(")") {
|
||||||
|
let values = color
|
||||||
|
.trim_start_matches("hsla(")
|
||||||
|
.trim_start_matches("hsl(")
|
||||||
|
.trim_end_matches(')')
|
||||||
|
.split(',')
|
||||||
|
.collect::<Vec<&str>>();
|
||||||
|
|
||||||
|
return match values.len() {
|
||||||
|
3 | 4 => values.iter().all(|v| v.trim().parse::<f32>().is_ok()),
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue