Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add display for multiple clipboard types #41

Merged
merged 3 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cuddly-pumpkins-pretend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"clippr": minor
---

Added information section and support multiple type display
5 changes: 5 additions & 0 deletions .changeset/good-singers-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"clippr": minor
---

Added information section and added multiple type display
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"license": "MIT",
"dependencies": {
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-fs": "~2",
"@tauri-apps/plugin-fs": "~2.0.2",
"@tauri-apps/plugin-global-shortcut": "^2.0.0",
"@tauri-apps/plugin-shell": "^2",
"@tauri-apps/plugin-sql": "~2",
Expand Down
2 changes: 1 addition & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 10 additions & 5 deletions scripts/sync-version.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import toml from '@iarna/toml';

// Read package.json
const packageJson = JSON.parse(readFileSync('package.json', 'utf8'));
const version = packageJson.version;

// Read tauri.conf.json
// Update tauri.conf.json
const tauriConfigPath = join('src-tauri', 'tauri.conf.json');
const tauriConfig = JSON.parse(readFileSync(tauriConfigPath, 'utf8'));

// Update version in tauri.conf.json
tauriConfig.version = version;

// Write back to tauri.conf.json
writeFileSync(tauriConfigPath, JSON.stringify(tauriConfig, null, 2) + '\n');

// Update Cargo.toml
const cargoTomlPath = join('src-tauri', 'Cargo.toml');
const cargoToml = toml.parse(readFileSync(cargoTomlPath, 'utf8'));
cargoToml.package.version = version;
writeFileSync(cargoTomlPath, toml.stringify(cargoToml));

console.log(`Updated version to ${version} in package.json, tauri.conf.json, and Cargo.toml`);
4 changes: 3 additions & 1 deletion src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
"clipboard:read-all",
"clipboard:write-all",
"clipboard:allow-read-text",
"fs:default"
"fs:default",
"fs:write-files",
"fs:read-files"
]
}
24 changes: 10 additions & 14 deletions src-tauri/src/api/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,34 +49,30 @@ pub async fn save_clipboard_to_db(
let existing_id: Option<i64> = if type_ == "image" {
// For images, check the image column
if let Some(img) = &image {
let mut stmt = conn.prepare("SELECT id FROM clipboard WHERE image = ?")
let mut stmt = conn
.prepare("SELECT id FROM clipboard WHERE image = ?")
.map_err(|e| e.to_string())?;
stmt.query_row(params![img], |row| row.get(0))
.ok()
stmt.query_row(params![img], |row| row.get(0)).ok()
} else {
None
}
} else {
// For other types, check the content column
let mut stmt = conn.prepare("SELECT id FROM clipboard WHERE content = ?")
let mut stmt = conn
.prepare("SELECT id FROM clipboard WHERE content = ?")
.map_err(|e| e.to_string())?;
stmt.query_row(params![content], |row| row.get(0))
.ok()
stmt.query_row(params![content], |row| row.get(0)).ok()
};

println!("Existing ID: {:?}", existing_id);

if let Some(id) = existing_id {
// Update existing entry
conn.execute(
"UPDATE clipboard SET count = count + 1, last_copied_date = ?, window_title = ?, window_exe = ? WHERE id = ?",
params![
date,
window_title,
window_exe,
id
],
).map_err(|e| e.to_string())?;
"UPDATE clipboard SET count = count + 1, last_copied_date = ? WHERE id = ?",
params![date, id],
)
.map_err(|e| e.to_string())?;
Ok(id)
} else {
// Insert new entry
Expand Down
13 changes: 9 additions & 4 deletions src-tauri/src/api/window.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use active_win_pos_rs::get_active_window;
use serde::Serialize;
use tauri::{LogicalPosition, WebviewWindow};
use active_win_pos_rs::get_active_window;

#[tauri::command]
pub fn center_window_on_current_monitor(window: &WebviewWindow) {
Expand Down Expand Up @@ -30,9 +30,14 @@ pub struct WindowInfo {
pub fn get_current_window() -> Option<WindowInfo> {
get_active_window().ok().map(|window| WindowInfo {
title: window.title,
process_path: window.process_path.file_name().and_then(|n| n.to_str()).unwrap_or("").to_string(),
process_path: window
.process_path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("")
.to_string(),
app_name: window.app_name,
window_id: window.window_id,
process_id: window.process_id
process_id: window.process_id,
})
}
}
25 changes: 13 additions & 12 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createSignal, For, onCleanup, onMount } from "solid-js";
import { emit, listen } from "@tauri-apps/api/event";
import { getCurrentWindow } from "@tauri-apps/api/window";
import {
writeFiles,
writeHtml,
writeImageBase64,
writeText,
Expand All @@ -17,7 +18,6 @@ import { ClipboardItem } from "./components/clipboard-item";

export const App = ({ db_path }: { db_path: string }) => {
const [activeIndex, setActiveIndex] = createSignal(0);
const [isInitialLoading, setIsInitialLoading] = createSignal(true);
const [isLoadingMore, setIsLoadingMore] = createSignal(false);
const [clipboardHistory, setClipboardHistory] = createSignal<
ClipboardHistory[]
Expand Down Expand Up @@ -73,9 +73,7 @@ export const App = ({ db_path }: { db_path: string }) => {
block: "center",
});
// Only load more if we have at least 20 items in the current history
if (
activeIndex() >= totalLength - 5
) {
if (activeIndex() >= totalLength - 5) {
setOffset((prev) => prev + limit);
updateHistory(offset(), limit);
}
Expand All @@ -86,7 +84,13 @@ export const App = ({ db_path }: { db_path: string }) => {
behavior: "smooth",
block: "center",
});
} else if (event.key === "Enter") {
} else if (
event.key === "Enter" ||
event.key === "NumpadEnter" ||
(event.ctrlKey &&
event.key === "c" &&
!window.getSelection()?.toString())
) {
const item = clipboardHistory()[activeIndex()];
handleCopy(item);
getCurrentWindow().hide();
Expand All @@ -100,7 +104,7 @@ export const App = ({ db_path }: { db_path: string }) => {
} else {
getCurrentWindow().hide();
}
} else {
} else if (!event.ctrlKey) {
inputRef?.focus();
}
} catch (error) {
Expand Down Expand Up @@ -130,7 +134,6 @@ export const App = ({ db_path }: { db_path: string }) => {
}).then((history) => {
if (offset === 0) {
setClipboardHistory(history);
setIsInitialLoading(false);
} else {
setClipboardHistory((prev) => [...prev, ...history]);
setIsLoadingMore(false);
Expand All @@ -142,8 +145,8 @@ export const App = ({ db_path }: { db_path: string }) => {
emit("copy-from-app");
if (item.type === "image") {
writeImageBase64(item.image);
} else if (item.type === "html") {
writeHtml(item.content);
} else if (item.type === "files") {
writeFiles(item.content.split(","));
} else {
writeText(item.content);
}
Expand Down Expand Up @@ -196,8 +199,6 @@ export const App = ({ db_path }: { db_path: string }) => {
limit: 20,
});

console.log(newHistory);

// Compare with current history
const currentHistory = clipboardHistory().slice(0, 20);

Expand Down Expand Up @@ -274,7 +275,7 @@ export const App = ({ db_path }: { db_path: string }) => {
class="h-full pb-2 overflow-y-auto invisible hover:visible max-h-[calc(100svh-4.5rem)] hover:overflow-y-auto select-none scroll-area"
>
<ul ref={listRef} class="visible w-full h-full">
{ clipboardHistory().length === 0 ? (
{clipboardHistory().length === 0 ? (
<EmptyState searchQuery={inputRef?.value || ""} />
) : (
<>
Expand Down
53 changes: 47 additions & 6 deletions src/components/clipboard-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { cn } from "../utils/tailwind";
import { DocumentIcon } from "../icons";
import { ImageIcon } from "../icons/image";
import { CodeIcon } from "../icons/code";
import { FileIcon } from "../icons/file";
import { highlightText } from "../utils/highlight";

interface ClipboardItemProps {
Expand All @@ -13,16 +14,23 @@ interface ClipboardItemProps {
searchQuery: string;
onDoubleClick: () => void;
onClick: () => void;
// onContextMenu: (e: MouseEvent) => void;
}

export const ClipboardItem: Component<ClipboardItemProps> = (props) => {
const getFaviconUrl = (url: string) => {
try {
const parsedUrl = new URL(url);
return `https://www.google.com/s2/favicons?domain=${parsedUrl.hostname}`;
} catch {
return null;
}
};

return (
<button
type="button"
onDblClick={props.onDoubleClick}
onClick={props.onClick}
// onContextMenu={props.onContextMenu}
class={cn(
"cursor-pointer w-full grid grid-cols-[auto_1fr] gap-2 p-2 h-10 rounded truncate overflow-hidden place-items-center",
{
Expand All @@ -32,7 +40,7 @@ export const ClipboardItem: Component<ClipboardItemProps> = (props) => {
>
{props.item.type === "image" ? (
<>
<ImageIcon class="size-4" />
<ImageIcon class="size-4 text-gray-400" />
<img
src={`data:image/png;base64,${props.item.image}`}
alt="clipboard content"
Expand All @@ -41,17 +49,50 @@ export const ClipboardItem: Component<ClipboardItemProps> = (props) => {
</>
) : props.item.type === "html" ? (
<>
<CodeIcon class="size-4" />
<CodeIcon class="size-4 text-gray-400" />
<p class="w-full overflow-hidden text-left text-ellipsis">
{highlightText(
props.item.content.trim().split("\n")[0],
props.searchQuery
)}
</p>
</>
) : (
) : props.item.type === "files" ? (
<>
<FileIcon class="size-4 text-gray-400" />
<p class="w-full overflow-hidden text-left text-ellipsis">
{highlightText(props.item.content.split(",")[0], props.searchQuery)}
</p>
</>
) : props.item.type === "url" ? (
<>
<img
src={getFaviconUrl(props.item.content) || ""}
alt="favicon"
class="size-4"
onError={(e) => {
e.currentTarget.src = "";
e.currentTarget.alt = "🌐";
}}
/>
<p class="w-full overflow-hidden text-left text-ellipsis">
{highlightText(props.item.content, props.searchQuery)}
</p>
</>
) : props.item.type === "color" ? (
<>
<div
style={{ background: props.item.content }}
class="size-4 rounded"
/>
<p class="w-full overflow-hidden text-left text-ellipsis">
{highlightText(props.item.content, props.searchQuery)}
</p>
</>
) :
(
<>
<DocumentIcon class="size-4" />
<DocumentIcon class="size-4 text-gray-400" />
<p class="w-full overflow-hidden text-left text-ellipsis">
{highlightText(
props.item.content.trim().split("\n")[0],
Expand Down
Loading
Loading