fix origin problem

This commit is contained in:
Julian Freeman
2026-02-22 19:58:34 -04:00
parent ce358e5b76
commit fb98ab5472
6 changed files with 254 additions and 53 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "gemmatrans-client",
"private": true,
"version": "0.1.2",
"version": "0.1.3",
"type": "module",
"scripts": {
"dev": "vite",

169
src-tauri/Cargo.lock generated
View File

@@ -554,7 +554,7 @@ dependencies = [
"bitflags 2.11.0",
"core-foundation 0.10.1",
"core-graphics-types",
"foreign-types",
"foreign-types 0.5.0",
"libc",
]
@@ -996,6 +996,15 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared 0.1.1",
]
[[package]]
name = "foreign-types"
version = "0.5.0"
@@ -1003,7 +1012,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
dependencies = [
"foreign-types-macros",
"foreign-types-shared",
"foreign-types-shared 0.3.1",
]
[[package]]
@@ -1017,6 +1026,12 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "foreign-types-shared"
version = "0.3.1"
@@ -1236,8 +1251,10 @@ dependencies = [
[[package]]
name = "gemmatrans-client"
version = "0.1.2"
version = "0.1.3"
dependencies = [
"futures-util",
"reqwest 0.12.28",
"serde",
"serde_json",
"tauri",
@@ -1609,6 +1626,22 @@ dependencies = [
"webpki-roots",
]
[[package]]
name = "hyper-tls"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"bytes",
"http-body-util",
"hyper",
"hyper-util",
"native-tls",
"tokio",
"tokio-native-tls",
"tower-service",
]
[[package]]
name = "hyper-util"
version = "0.1.20"
@@ -2156,6 +2189,23 @@ dependencies = [
"windows-sys 0.60.2",
]
[[package]]
name = "native-tls"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
dependencies = [
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]]
name = "ndk"
version = "0.9.0"
@@ -2465,6 +2515,50 @@ dependencies = [
"pathdiff",
]
[[package]]
name = "openssl"
version = "0.10.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
dependencies = [
"bitflags 2.11.0",
"cfg-if",
"foreign-types 0.3.2",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "openssl-probe"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
version = "0.9.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "option-ext"
version = "0.2.0"
@@ -3150,16 +3244,19 @@ dependencies = [
"cookie_store 0.22.1",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-rustls",
"hyper-tls",
"hyper-util",
"js-sys",
"log",
"mime",
"native-tls",
"percent-encoding",
"pin-project-lite",
"quinn",
@@ -3170,13 +3267,16 @@ dependencies = [
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tokio-native-tls",
"tokio-rustls",
"tokio-util",
"tower",
"tower-http",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams 0.4.2",
"web-sys",
"webpki-roots",
]
@@ -3211,7 +3311,7 @@ dependencies = [
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams",
"wasm-streams 0.5.0",
"web-sys",
]
@@ -3313,6 +3413,15 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "schannel"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "schemars"
version = "0.8.22"
@@ -3370,6 +3479,29 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "security-framework"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.11.0",
"core-foundation 0.9.4",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "selectors"
version = "0.24.0"
@@ -4316,6 +4448,16 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
dependencies = [
"native-tls",
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.26.4"
@@ -4676,6 +4818,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version-compare"
version = "0.2.1"
@@ -4838,6 +4986,19 @@ dependencies = [
"wasmparser",
]
[[package]]
name = "wasm-streams"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
dependencies = [
"futures-util",
"js-sys",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "wasm-streams"
version = "0.5.0"

View File

@@ -1,6 +1,6 @@
[package]
name = "gemmatrans-client"
version = "0.1.2"
version = "0.1.3"
description = "A translategemma client"
authors = ["Julian"]
edition = "2021"
@@ -23,4 +23,6 @@ tauri-plugin-opener = "2"
tauri-plugin-http = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
reqwest = { version = "0.12", features = ["json", "stream"] }
futures-util = "0.3"

View File

@@ -1,7 +1,62 @@
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
use tauri::{AppHandle, Emitter};
use serde::{Deserialize, Serialize};
use futures_util::StreamExt;
use reqwest::Client;
#[derive(Serialize, Deserialize, Clone)]
struct TranslationPayload {
model: String,
prompt: String,
stream: bool,
}
#[derive(Deserialize)]
struct OllamaResponse {
response: Option<String>,
// done: bool,
}
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
async fn translate(
app: AppHandle,
api_address: String,
payload: TranslationPayload,
) -> Result<String, String> {
let client = Client::new();
let url = format!("{}/api/generate", api_address);
let res = client
.post(&url)
.json(&payload)
.send()
.await
.map_err(|e| e.to_string())?;
if !payload.stream {
let data = res.json::<OllamaResponse>().await.map_err(|e| e.to_string())?;
return Ok(data.response.unwrap_or_default());
}
let mut stream = res.bytes_stream();
let mut full_response = String::new();
while let Some(item) = stream.next().await {
let chunk = item.map_err(|e| e.to_string())?;
let text = String::from_utf8_lossy(&chunk);
// Handle potential multiple JSON objects in one chunk
for line in text.lines() {
if line.trim().is_empty() { continue; }
if let Ok(json) = serde_json::from_str::<OllamaResponse>(line) {
if let Some(token) = json.response {
full_response.push_str(&token);
app.emit("translation-chunk", token).map_err(|e| e.to_string())?;
}
}
}
}
Ok(full_response)
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
@@ -9,7 +64,7 @@ pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_http::init())
.invoke_handler(tauri::generate_handler![greet])
.invoke_handler(tauri::generate_handler![translate])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "gemmatrans-client",
"version": "0.1.2",
"version": "0.1.3",
"identifier": "top.volan.gemmatrans-client",
"build": {
"beforeDevCommand": "pnpm dev",

View File

@@ -14,7 +14,8 @@ import {
Sun,
Moon
} from 'lucide-vue-next';
import { fetch } from '@tauri-apps/plugin-http';
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
import { useSettingsStore, LANGUAGES, DEFAULT_TEMPLATE } from './stores/settings';
import pkg from '../package.json';
import { clsx, type ClassValue } from 'clsx';
@@ -81,6 +82,20 @@ const targetText = ref('');
const isTranslating = ref(false);
const showCopyFeedback = ref(false);
let unlisten: (() => void) | null = null;
onMounted(async () => {
unlisten = await listen<string>('translation-chunk', (event) => {
if (isTranslating.value) {
targetText.value += event.payload;
}
});
});
onUnmounted(() => {
if (unlisten) unlisten();
});
// Language Selection
const sourceLangCode = computed({
get: () => settings.sourceLang.code,
@@ -146,50 +161,18 @@ const translate = async () => {
settings.addLog('request', requestBody);
try {
const response = await fetch(`${settings.ollamaApiAddress}/api/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody)
const response = await invoke<string>('translate', {
apiAddress: settings.ollamaApiAddress,
payload: requestBody
});
if (!response.ok) {
const errorText = await response.text();
settings.addLog('error', { status: response.status, text: errorText });
throw new Error(`API error (${response.status}): ${errorText || response.statusText}`);
}
if (settings.enableStreaming) {
const reader = response.body?.getReader();
const decoder = new TextDecoder();
if (reader) {
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
const lines = chunk.split('\n');
for (const line of lines) {
if (!line.trim()) continue;
try {
const data = JSON.parse(line);
if (data.response) {
targetText.value += data.response;
}
if (data.done) {
settings.addLog('response', 'Stream finished');
}
} catch (e) {
settings.addLog('error', `Chunk parse error: ${line}`);
}
}
}
}
} else {
const data = await response.json();
settings.addLog('response', data);
targetText.value = data.response;
// For non-streaming, response is returned as string
if (!settings.enableStreaming) {
targetText.value = response;
}
settings.addLog('response', 'Translation completed');
} catch (err: any) {
const errorMsg = err instanceof Error ? err.message : String(err);
const errorMsg = String(err);
settings.addLog('error', errorMsg);
targetText.value = `Error: ${errorMsg}`;
} finally {