fix image preview
This commit is contained in:
8
src-tauri/Cargo.lock
generated
8
src-tauri/Cargo.lock
generated
@@ -670,6 +670,7 @@ name = "chrono-snap"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"image",
|
||||
"serde",
|
||||
@@ -2023,6 +2024,12 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-range"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.10.1"
|
||||
@@ -4902,6 +4909,7 @@ dependencies = [
|
||||
"gtk",
|
||||
"heck 0.5.0",
|
||||
"http",
|
||||
"http-range",
|
||||
"jni",
|
||||
"libc",
|
||||
"log",
|
||||
|
||||
@@ -18,7 +18,7 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2", features = ["tray-icon"] }
|
||||
tauri = { version = "2", features = ["protocol-asset", "tray-icon"] }
|
||||
tauri-plugin-opener = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
@@ -30,6 +30,7 @@ image = "0.25.10"
|
||||
chrono = "0.4.44"
|
||||
tokio = { version = "1.50.0", features = ["rt-multi-thread", "macros", "time"] }
|
||||
anyhow = "1.0.102"
|
||||
base64 = "0.22.1"
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||
tauri-plugin-autostart = "2"
|
||||
|
||||
@@ -24,6 +24,14 @@ pub fn get_pause_state(state: tauri::State<'_, AppState>) -> bool {
|
||||
state.is_paused.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_image_base64(path: String) -> Result<String, String> {
|
||||
let bytes = fs::read(path).map_err(|e| e.to_string())?;
|
||||
Ok(general_purpose::STANDARD.encode(bytes))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_timeline(date: String, base_dir: String) -> Vec<serde_json::Value> {
|
||||
let mut results = Vec::new();
|
||||
|
||||
@@ -19,7 +19,8 @@ pub fn run() {
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
engine::toggle_pause,
|
||||
engine::get_pause_state,
|
||||
engine::get_timeline
|
||||
engine::get_timeline,
|
||||
engine::get_image_base64
|
||||
])
|
||||
.setup(|app| {
|
||||
app.manage(engine::AppState {
|
||||
|
||||
@@ -18,7 +18,11 @@
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
"csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost tauri: http://localhost:*",
|
||||
"assetProtocol": {
|
||||
"enable": true,
|
||||
"scope": ["**"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
|
||||
19
src/App.vue
19
src/App.vue
@@ -12,6 +12,7 @@ const isPaused = ref(false);
|
||||
const currentDate = ref(new Date().toISOString().split("T")[0]); // YYYY-MM-DD
|
||||
const timelineImages = ref<{ time: string; path: string }[]>([]);
|
||||
const selectedImage = ref<{ time: string; path: string } | null>(null);
|
||||
const previewSrc = ref("");
|
||||
const isFullscreen = ref(false);
|
||||
const isSettingsOpen = ref(false);
|
||||
const mergeScreens = ref(false);
|
||||
@@ -59,11 +60,6 @@ const togglePauseState = async () => {
|
||||
const loadTimeline = async () => {
|
||||
if (!savePath.value) return;
|
||||
const dateStr = currentDate.value;
|
||||
// Rust is joining paths, Tauri FS plugin has a resolve function or we can use string concat.
|
||||
// Wait, readDir with an absolute path requires specific permissions in Tauri v2, or we need to use Rust to list files.
|
||||
// Actually, standard `readDir` supports absolute paths if we configure scope in capabilities.
|
||||
// We didn't allow `$fs:allow-read-all` or similar, we only added `fs:default`.
|
||||
// The simplest way to fetch timeline is a custom Rust command since we already manage the paths there!
|
||||
// It's safer to read files from Rust and return a list of { time, absolute_path }.
|
||||
timelineImages.value = await invoke("get_timeline", { date: dateStr, baseDir: savePath.value });
|
||||
};
|
||||
@@ -72,8 +68,15 @@ const refresh = async () => {
|
||||
await loadTimeline();
|
||||
};
|
||||
|
||||
const selectImage = (img: { time: string; path: string }) => {
|
||||
const selectImage = async (img: { time: string; path: string }) => {
|
||||
selectedImage.value = img;
|
||||
try {
|
||||
const base64 = await invoke("get_image_base64", { path: img.path });
|
||||
previewSrc.value = `data:image/jpeg;base64,${base64}`;
|
||||
} catch (e) {
|
||||
console.error("Failed to load image:", e);
|
||||
previewSrc.value = "";
|
||||
}
|
||||
};
|
||||
|
||||
const getSrc = (path: string) => {
|
||||
@@ -160,7 +163,7 @@ const getSrc = (path: string) => {
|
||||
<div v-if="selectedImage" class="flex-1 p-10 flex items-center justify-center overflow-hidden">
|
||||
<div class="relative max-w-full max-h-full group">
|
||||
<img
|
||||
:src="getSrc(selectedImage.path)"
|
||||
:src="previewSrc"
|
||||
class="max-w-full max-h-full object-contain rounded-2xl shadow-[0_12px_30px_rgba(0,0,0,0.08)]"
|
||||
/>
|
||||
<button
|
||||
@@ -259,7 +262,7 @@ const getSrc = (path: string) => {
|
||||
<X :size="32" />
|
||||
</button>
|
||||
<img
|
||||
:src="getSrc(selectedImage.path)"
|
||||
:src="previewSrc"
|
||||
class="max-w-full max-h-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user