Fix: MacOS TitleBar & History Reactive-Glitch

This commit is contained in:
Youwes09
2026-03-25 23:36:18 -05:00
parent 4b3493465d
commit 3abb4bb96c
13 changed files with 194 additions and 14 deletions
+4 -3
View File
@@ -14,10 +14,8 @@ Minor Revisions:
- Add Hover Info on Library (Make sure doesn't conflict with additional clicks)
Priority Bugs:
- Resume on Home-Page leads to Reader, not Cached 5 chapters
- Fix History (Counting Chapters, Etc)
- Cache ALL Cover Pictures & Details for Manga in Library
- MacOS Full-Screen & UI Compatability (TitleBar)
General/Misc Bugs:
- Fix Highlightable Elements
@@ -27,6 +25,9 @@ General/Misc Bugs:
- Investigate Prod vs. Dev Directory Change/Data Load (Caused by Library Algorithm Revision?)
In-Progress:`
- Fix Reader Chapter Shifts (Glitched Sentinel)
- Still Shifts Down after reading ~8+ Chapters?
- Identify When Chapters are Unloaded, How to Preserve Structure
Important Commands:
+1 -1
View File
@@ -71,7 +71,7 @@
inherit version;
src = frontendSrc;
fetcherVersion = 1;
hash = "sha256-G82kmXm1prRpU9kqUyFHSABVt1fikMzvz78+w/gFKvQ=";
hash = "sha256-4QUSgWgMu7FGn44+TGmACheokPhaBdHvA/055SqUs0Q=";
};
buildPhase = "pnpm build";
+1
View File
@@ -11,6 +11,7 @@
},
"dependencies": {
"@tauri-apps/api": "^2.0.0",
"@tauri-apps/plugin-os": "^2.3.2",
"@tauri-apps/plugin-shell": "^2.3.5",
"clsx": "^2.1.1",
"phosphor-svelte": "^3.1.0",
+10
View File
@@ -11,6 +11,9 @@ importers:
'@tauri-apps/api':
specifier: ^2.0.0
version: 2.10.1
'@tauri-apps/plugin-os':
specifier: ^2.3.2
version: 2.3.2
'@tauri-apps/plugin-shell':
specifier: ^2.3.5
version: 2.3.5
@@ -436,6 +439,9 @@ packages:
engines: {node: '>= 10'}
hasBin: true
'@tauri-apps/plugin-os@2.3.2':
resolution: {integrity: sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A==}
'@tauri-apps/plugin-shell@2.3.5':
resolution: {integrity: sha512-jewtULhiQ7lI7+owCKAjc8tYLJr92U16bPOeAa472LHJdgaibLP83NcfAF2e+wkEcA53FxKQAZ7byDzs2eeizg==}
@@ -1032,6 +1038,10 @@ snapshots:
'@tauri-apps/cli-win32-ia32-msvc': 2.10.1
'@tauri-apps/cli-win32-x64-msvc': 2.10.1
'@tauri-apps/plugin-os@2.3.2':
dependencies:
'@tauri-apps/api': 2.10.1
'@tauri-apps/plugin-shell@2.3.5':
dependencies:
'@tauri-apps/api': 2.10.1
+95
View File
@@ -1107,6 +1107,16 @@ dependencies = [
"version_check",
]
[[package]]
name = "gethostname"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
dependencies = [
"rustix",
"windows-link 0.2.1",
]
[[package]]
name = "getrandom"
version = "0.1.16"
@@ -1998,6 +2008,7 @@ dependencies = [
"tauri",
"tauri-build",
"tauri-plugin-http",
"tauri-plugin-os",
"tauri-plugin-process",
"tauri-plugin-shell",
"tauri-plugin-updater",
@@ -2061,6 +2072,18 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "nix"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
"bitflags 2.11.0",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]]
name = "nodrop"
version = "0.1.14"
@@ -2200,6 +2223,16 @@ dependencies = [
"objc2-foundation",
]
[[package]]
name = "objc2-core-location"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009"
dependencies = [
"objc2",
"objc2-foundation",
]
[[package]]
name = "objc2-core-text"
version = "0.3.2"
@@ -2316,8 +2349,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22"
dependencies = [
"bitflags 2.11.0",
"block2",
"objc2",
"objc2-cloud-kit",
"objc2-core-data",
"objc2-core-foundation",
"objc2-core-graphics",
"objc2-core-image",
"objc2-core-location",
"objc2-core-text",
"objc2-foundation",
"objc2-quartz-core",
"objc2-user-notifications",
]
[[package]]
name = "objc2-user-notifications"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e"
dependencies = [
"objc2",
"objc2-foundation",
]
@@ -2367,6 +2419,22 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "os_info"
version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224"
dependencies = [
"android_system_properties",
"log",
"nix",
"objc2",
"objc2-foundation",
"objc2-ui-kit",
"serde",
"windows-sys 0.61.2",
]
[[package]]
name = "os_pipe"
version = "1.2.3"
@@ -3806,6 +3874,15 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "sys-locale"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4"
dependencies = [
"libc",
]
[[package]]
name = "sysinfo"
version = "0.32.1"
@@ -4099,6 +4176,24 @@ dependencies = [
"urlpattern",
]
[[package]]
name = "tauri-plugin-os"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8f08346c8deb39e96f86973da0e2d76cbb933d7ac9b750f6dc4daf955a6f997"
dependencies = [
"gethostname",
"log",
"os_info",
"serde",
"serde_json",
"serialize-to-javascript",
"sys-locale",
"tauri",
"tauri-plugin",
"thiserror 2.0.18",
]
[[package]]
name = "tauri-plugin-process"
version = "2.3.1"
+1
View File
@@ -25,6 +25,7 @@ serde_json = "1"
walkdir = "2"
sysinfo = "0.32"
dirs = "5"
tauri-plugin-os = "2.3.2"
[profile.release]
codegen-units = 1
+1
View File
@@ -549,6 +549,7 @@ fn restart_app(app: tauri::AppHandle) {
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_process::init())
+2 -2
View File
@@ -17,7 +17,7 @@
"minHeight": 600,
"resizable": true,
"fullscreen": false,
"decorations": false,
"decorations": true,
"center": true
}
],
@@ -55,4 +55,4 @@
"endpoints": []
}
}
}
}
+9
View File
@@ -1,4 +1,13 @@
{
"app": {
"windows": [
{
"decorations": true,
"titleBarStyle": "overlay",
"hiddenTitle": true
}
]
},
"bundle": {
"targets": ["dmg"],
"externalBin": [
+13 -1
View File
@@ -3,6 +3,7 @@
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
import { getVersion } from "@tauri-apps/api/app";
import { getCurrentWindow } from "@tauri-apps/api/window";
import { gql } from "./lib/client";
import { GET_DOWNLOAD_STATUS } from "./lib/queries";
import { store, addToast, setActiveDownloads, setSettingsOpen } from "./store/state.svelte";
@@ -16,6 +17,7 @@
import MangaPreview from "./components/shared/MangaPreview.svelte";
const MAX_ATTEMPTS = 60;
const win = getCurrentWindow();
let serverProbeOk = $state(!store.settings.autoStartServer);
let appReady = $state(!store.settings.autoStartServer);
@@ -147,6 +149,15 @@
platformScale = await invoke<number>("get_platform_ui_scale").catch(() => 1);
applyZoom();
// ── Fullscreen state sync ─────────────────────────────────────────────────
// Seed the initial state, then keep it in sync on every resize event.
// onResized is the correct Tauri 2 API — it fires on fullscreen enter/exit,
// window snap, and manual resize. isFullscreen() is cheap (single IPC call).
store.isFullscreen = await win.isFullscreen();
const unlistenResize = await win.onResized(async () => {
store.isFullscreen = await win.isFullscreen();
});
if (store.settings.autoStartServer) {
invoke<void>("spawn_server", { binary: store.settings.serverBinary }).catch((err: any) => {
if (err?.kind === "NotConfigured") {
@@ -181,6 +192,7 @@
return () => {
cancelled = true;
unlistenResize();
if (store.settings.autoStartServer) invoke("kill_server").catch(() => {});
if (idleTimer) clearTimeout(idleTimer);
if (pollInterval) clearInterval(pollInterval);
@@ -215,7 +227,7 @@
<SplashScreen mode="idle" showCards={store.settings.splashCards ?? true}
onDismiss={() => setTimeout(() => idle = false, 340)} />
{/if}
{#if !store.activeChapter}<TitleBar />{/if}
{#if !store.activeChapter && !store.isFullscreen}<TitleBar />{/if}
<div class="content">
{#if store.activeChapter}<Reader />{:else}<Layout />{/if}
</div>
+26 -1
View File
@@ -1,10 +1,27 @@
<script lang="ts">
import { onMount } from "svelte";
import { getCurrentWindow } from "@tauri-apps/api/window";
const win = getCurrentWindow();
import { platform } from "@tauri-apps/plugin-os";
const win = getCurrentWindow();
const isMac = platform() === "macos";
let isFullscreen = $state(false);
onMount(async () => {
isFullscreen = await win.isFullscreen();
const unlisten = await win.onResized(async () => {
isFullscreen = await win.isFullscreen();
});
return unlisten;
});
</script>
{#if !isFullscreen}
<div class="bar" data-tauri-drag-region>
{#if isMac}<div class="mac-spacer"></div>{/if}
<span class="title" data-tauri-drag-region>Moku</span>
{#if !isMac}
<div class="controls">
<button onclick={() => win.minimize()} title="Minimize" aria-label="Minimize">
<svg width="10" height="1" viewBox="0 0 10 1">
@@ -23,7 +40,9 @@
</svg>
</button>
</div>
{/if}
</div>
{/if}
<style>
.bar {
@@ -38,6 +57,12 @@
user-select: none;
-webkit-app-region: drag;
}
/* Spacer to clear the native macOS traffic lights (~70px) */
.mac-spacer {
width: 70px;
flex-shrink: 0;
-webkit-app-region: drag;
}
.title {
font-family: var(--font-ui);
font-size: var(--text-2xs);
+26 -6
View File
@@ -115,6 +115,9 @@
let abortCtrl: AbortController | null = null;
let loadingId: number | null = null;
let navToken = 0;
// Only write history after the user has genuinely moved past the opening page.
// Prevents the "started on page 1" entry being saved as last position on close.
let hasNavigated = false;
// ─── Derived ──────────────────────────────────────────────────────────────────
@@ -188,9 +191,10 @@
abortCtrl = ctrl;
loadingId = id;
navToken++;
appending = false;
markedRead = new Set();
loading = true;
appending = false;
markedRead = new Set();
hasNavigated = false;
loading = true;
error = null;
pageGroups = [];
pageReady = false;
@@ -394,17 +398,33 @@
});
// ─── Progress / history tracking ─────────────────────────────────────────────
// Only records history after the user has genuinely navigated (pageNumber > 1,
// or scrolled past page 1 in longstrip). This prevents the chapter-open event
// from writing "page 1" as the last-read position, which caused the history to
// always show the chapter you started on rather than where you left off.
$effect(() => {
if (store.activeChapter && lastPage && store.activeManga) {
const chapterId = store.activeChapter.id;
const chapterName = store.activeChapter.name;
// Use displayChapter, not store.activeChapter — in longstrip with autoNext,
// store.activeChapter stays as the chapter you *opened* (e.g. ch61) while
// displayChapter tracks visibleChapterId (the chapter actually on screen).
// Using store.activeChapter here caused every history write to stamp ch61
// even when the user had scrolled all the way to ch72.
const ch = displayChapter ?? store.activeChapter;
if (ch && lastPage && store.activeManga) {
const chapterId = ch.id;
const chapterName = ch.name;
const mangaId = store.activeManga.id;
const mangaTitle = store.activeManga.title;
const thumb = store.activeManga.thumbnailUrl;
const pageNum = store.pageNumber;
const atLast = store.pageNumber === lastPage;
// Mark that the user has moved past the initial load.
if (pageNum > 1) hasNavigated = true;
untrack(() => {
// Skip the very first page-1 write that fires on chapter load.
if (!hasNavigated) return;
addHistory({ mangaId, mangaTitle, thumbnailUrl: thumb, chapterId, chapterName, pageNumber: pageNum, readAt: Date.now() });
if (style !== "longstrip" && store.settings.autoMarkRead && atLast) markChapterRead(chapterId);
});
+5
View File
@@ -272,6 +272,8 @@ class Store {
toasts: Toast[] = $state([]);
activeChapter: Chapter | null = $state(null);
activeChapterList: Chapter[] = $state([]);
// UI-only: synced from Tauri window events in App.svelte. Not persisted.
isFullscreen: boolean = $state(false);
// ── Discover session cache ────────────────────────────────────────────────
// Survives navigation within a session but is never persisted to localStorage.
@@ -300,6 +302,9 @@ class Store {
}
closeReader() {
// Null activeChapter FIRST so the history $effect in Reader can't fire
// one last time with stale chapter + pageNumber=1, overwriting the real
// last-read position with page 1.
this.activeChapter = null;
this.activeChapterList = [];
this.pageUrls = [];