mirror of
https://github.com/patarapolw/cjdic2.git
synced 2026-11-09 17:39:35 +00:00
check images and dark mode
This commit is contained in:
1 parent
978ebfa75f
commit
904cdd0413
10 files changed
+122
-19
No files matched your search
@@ -113,6 +113,8 @@ impl YomitanWriter {
|
||||
installed_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
bundle_name TEXT, -- custom
|
||||
lang TEXT NOT NULL, -- custom
|
||||
asset_count INTEGER, -- custom
|
||||
source TEXT, -- custom
|
||||
UNIQUE (title, revision)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS glossaries ( -- not used. moved to a new file
|
||||
@@ -186,7 +188,7 @@ impl YomitanWriter {
|
||||
self.ensure_db_attached(DbChild::YomitanGlossary)?;
|
||||
self.ensure_db_attached(DbChild::Search)?;
|
||||
|
||||
let schema_version = "2026-03-08";
|
||||
let schema_version = "2026-03-21";
|
||||
let current_schema_version = {
|
||||
let mut stmt = self
|
||||
.conn
|
||||
@@ -275,6 +277,15 @@ impl YomitanWriter {
|
||||
)?;
|
||||
}
|
||||
|
||||
if v < "2026-03-21" {
|
||||
self.conn.execute_batch(
|
||||
"
|
||||
ALTER TABLE dictionaries ADD COLUMN asset_count INTEGER;
|
||||
ALTER TABLE dictionaries ADD COLUMN source TEXT;
|
||||
",
|
||||
)?;
|
||||
}
|
||||
|
||||
if v < schema_version {
|
||||
self.conn
|
||||
.prepare("UPDATE schema_meta SET value = ?2 WHERE key = ?1")?
|
||||
@@ -454,6 +465,8 @@ impl YomitanWriter {
|
||||
let cursor = Cursor::new(zip_file.bytes()?);
|
||||
let mut archive = ZipArchive::new(cursor)?;
|
||||
|
||||
let mut asset_count = 0;
|
||||
|
||||
for i in 0..archive.len() {
|
||||
// Metadata — borrow ends before by_index_raw
|
||||
if let (Some(asset_name), is_dir) = {
|
||||
@@ -481,6 +494,8 @@ impl YomitanWriter {
|
||||
let mut entry = archive.by_index(i)?;
|
||||
let mut out = File::create(&out_path)?;
|
||||
io::copy(&mut entry, &mut out)?; // writes plain bytes
|
||||
|
||||
asset_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,7 +506,6 @@ impl YomitanWriter {
|
||||
serde_json::from_str::<Value>(&s)?
|
||||
}
|
||||
Err(e) => {
|
||||
// TODO: proper error handling
|
||||
eprintln!("{}", e);
|
||||
return Ok(YomitanZipImportResult {
|
||||
exists: false,
|
||||
@@ -534,17 +548,21 @@ impl YomitanWriter {
|
||||
.to_string();
|
||||
|
||||
{
|
||||
let title: &str = &title;
|
||||
// Skip if already installed
|
||||
// Skip if already installed or older revision
|
||||
let mut stmt = self
|
||||
.conn
|
||||
.prepare("SELECT (revision < ?2) FROM dictionaries WHERE title = ?1")?;
|
||||
let mut rows = stmt.query(params![title, revision])?;
|
||||
.prepare("SELECT (revision < ?2) FROM dictionaries WHERE bundle_name = ?1")?;
|
||||
let mut rows = stmt.query(params![bundle_name, revision])?;
|
||||
if let Some(row) = rows.next()? {
|
||||
let is_outdated: bool = row.get(0)?;
|
||||
if is_outdated {
|
||||
self.drop_dictionary(bundle_name, lang)?;
|
||||
} else {
|
||||
self.conn.execute(
|
||||
"UPDATE dictionaries SET asset_count = ?2 WHERE bundle_name = ?1",
|
||||
params![bundle_name, asset_count],
|
||||
)?;
|
||||
|
||||
return Ok(YomitanZipImportResult {
|
||||
exists: true,
|
||||
load: false,
|
||||
@@ -560,8 +578,8 @@ impl YomitanWriter {
|
||||
let tx = self.conn.transaction()?;
|
||||
tx.execute(
|
||||
"INSERT INTO dictionaries
|
||||
(title, revision, author, url, description, lang, bundle_name) VALUES
|
||||
( ?1, ?2, ?3, ?4, ?5, ?6, ?7)",
|
||||
(title, revision, author, url, description, lang, bundle_name, asset_count) VALUES
|
||||
( ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
|
||||
params![
|
||||
title,
|
||||
revision,
|
||||
@@ -570,6 +588,7 @@ impl YomitanWriter {
|
||||
index_file.get("description").and_then(Value::as_str),
|
||||
lang,
|
||||
bundle_name,
|
||||
asset_count,
|
||||
],
|
||||
)?;
|
||||
|
||||
|
||||
+11
-7
@@ -188,12 +188,21 @@ impl AppService {
|
||||
let mut new_dicts: Vec<String> = vec![];
|
||||
let mut to_be_removed_dicts: Vec<String> = vec![];
|
||||
|
||||
{
|
||||
let mut writer = YomitanWriter::new(self.db.dir.clone())?;
|
||||
writer.create_schema(&import_callback)?;
|
||||
}
|
||||
|
||||
let db_path = self.db.dir.join(DBFILE[DbChild::Yomitan]);
|
||||
|
||||
if db_path.exists() {
|
||||
let conn = Connection::open(&db_path)?;
|
||||
let mut stmt =
|
||||
conn.prepare("SELECT DISTINCT bundle_name FROM dictionaries WHERE lang = ?1")?;
|
||||
let mut stmt = conn.prepare(
|
||||
"
|
||||
SELECT DISTINCT bundle_name FROM dictionaries
|
||||
WHERE lang = ?1 AND asset_count IS NOT NULL
|
||||
",
|
||||
)?;
|
||||
let mut rows = stmt.query([lang])?;
|
||||
|
||||
let mut db_zip_list: HashSet<String> = HashSet::new();
|
||||
@@ -228,11 +237,6 @@ impl AppService {
|
||||
to_be_removed_dicts: to_be_removed_dicts.clone(),
|
||||
});
|
||||
|
||||
{
|
||||
let mut writer = YomitanWriter::new(self.db.dir.clone())?;
|
||||
writer.create_schema(&import_callback)?;
|
||||
}
|
||||
|
||||
{
|
||||
let conn = Connection::open(&db_path)?;
|
||||
for z in to_be_removed_dicts.clone().iter() {
|
||||
|
||||
@@ -43,4 +43,14 @@ body {
|
||||
html {
|
||||
height: 100vh;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
html.dark .glossaries {
|
||||
span {
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
img {
|
||||
filter: invert(1) hue-rotate(180deg);
|
||||
}
|
||||
}
|
||||
@@ -57,14 +57,13 @@ export default function Glossary({
|
||||
function GlossaryImage({ img, yomitanURL }: { img: any; yomitanURL: string }) {
|
||||
return (
|
||||
<img
|
||||
className="glossary-image"
|
||||
src={`${yomitanURL || ""}/${img.path}`}
|
||||
width={img.width}
|
||||
height={img.height}
|
||||
title={img.title}
|
||||
alt={img.alt}
|
||||
style={{
|
||||
imageRendering: img.imageRendering || "auto",
|
||||
width: `${img.width}em`,
|
||||
width: `${img.width}em`, // unit seems to be em, not px
|
||||
height: `${img.height}em`,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useEffect, useRef, useState } from "react";
|
||||
import { Dialog, Portal } from "@chakra-ui/react";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import { fetch } from "@tauri-apps/plugin-http";
|
||||
|
||||
import { supabase } from "../lib/supabaseClient";
|
||||
|
||||
@@ -13,6 +14,13 @@ interface YomitanDictEntry {
|
||||
lang: string;
|
||||
}
|
||||
|
||||
interface YomitanStorageWorkerDictEntry {
|
||||
key: string;
|
||||
size: number;
|
||||
uploaded: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface YomitanDownloadDictEntry {
|
||||
url: string;
|
||||
filepath: string;
|
||||
@@ -119,6 +127,52 @@ function LoadingDialog() {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const { VITE_STORAGE_WORKER_URL, VITE_STORAGE_WORKER_TOKEN } =
|
||||
import.meta.env;
|
||||
|
||||
try {
|
||||
const prefix = `${lang}/`;
|
||||
|
||||
let entries: YomitanStorageWorkerDictEntry[] = [];
|
||||
|
||||
const LAST_API_CALL_KEY = "LAST_API_CALL_KEY_storageWorkerURL";
|
||||
const LAST_API_JSON_KEY = "LAST_API_JSON_KEY_storageWorkerURL";
|
||||
|
||||
const lastCall = localStorage.getItem(LAST_API_CALL_KEY);
|
||||
const lastJSON = localStorage.getItem(LAST_API_JSON_KEY);
|
||||
|
||||
if (lastJSON && lastCall) {
|
||||
const d = new Date(lastCall);
|
||||
d.setDate(d.getDate() + 1);
|
||||
if (d > new Date()) {
|
||||
entries = JSON.parse(lastJSON);
|
||||
}
|
||||
}
|
||||
if (!entries.length) {
|
||||
console.log(`visiting ${VITE_STORAGE_WORKER_URL}`);
|
||||
|
||||
entries = await fetch(VITE_STORAGE_WORKER_URL + "/files", {
|
||||
headers: {
|
||||
Authorization: `Bearer ${VITE_STORAGE_WORKER_TOKEN}`,
|
||||
},
|
||||
}).then((r) => r.json());
|
||||
|
||||
localStorage.setItem(LAST_API_CALL_KEY, new Date().toISOString());
|
||||
localStorage.setItem(LAST_API_JSON_KEY, JSON.stringify(entries));
|
||||
}
|
||||
|
||||
dicts.push(
|
||||
...entries
|
||||
.filter((o) => o.key.startsWith(prefix))
|
||||
.map((o) => ({
|
||||
filepath: `yomitan/${lang}/${o.key.substring(prefix.length)}`,
|
||||
url: o.url,
|
||||
})),
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
await invoke("init_yomitan", { dicts, lang });
|
||||
|
||||
@@ -412,6 +412,7 @@ function SearchPage() {
|
||||
</Card.Header>
|
||||
<Card.Body>
|
||||
<div
|
||||
className="glossaries"
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
|
||||
@@ -8,7 +8,13 @@
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"opener:default",
|
||||
"http:default",
|
||||
{
|
||||
"identifier": "http:default",
|
||||
"allow": [
|
||||
{ "url": "https://api.github.com/repos/*/releases/latest" },
|
||||
{ "url": "https://storage-worker.*.workers.dev" }
|
||||
]
|
||||
},
|
||||
"fs:default",
|
||||
"clipboard-manager:allow-read-text"
|
||||
]
|
||||
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
interface Env {
|
||||
YOMITAN_STORAGE_API_TOKEN: string;
|
||||
}
|
||||
@@ -13,6 +13,12 @@
|
||||
|
||||
export default {
|
||||
async fetch(request, env) {
|
||||
// Auth check first
|
||||
const auth = request.headers.get("Authorization");
|
||||
if (auth !== `Bearer ${env.YOMITAN_STORAGE_API_TOKEN}`) {
|
||||
return new Response("Unauthorized", { status: 401 });
|
||||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
|
||||
// List files: GET /files
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
"skipLibCheck": true,
|
||||
"types": [
|
||||
"./worker-configuration.d.ts",
|
||||
"./env.d.ts",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user