Skip to content
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
56 changes: 56 additions & 0 deletions src/main/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fs from "node:fs";
import https from "node:https";
import path from "node:path";
import { app, BrowserWindow, dialog, ipcMain, shell } from "electron";
import {
Expand Down Expand Up @@ -229,6 +230,61 @@ ipcMain.handle("install-update", async () => {
return installUpdate();
});

// Fetch GitHub releases RSS feed (bypasses CORS)
ipcMain.handle("fetch-releases-feed", async () => {
return new Promise<{ success: boolean; data?: string; error?: string }>(
(resolve) => {
const url = "https://github.com/LIUBINfighter/Tabst.app/releases.atom";
const urlObj = new URL(url);

const options = {
hostname: urlObj.hostname,
path: urlObj.pathname + urlObj.search,
method: "GET",
headers: {
"User-Agent": "Tabst/1.0",
},
};

const req = https.request(options, (res) => {
let data = "";

res.on("data", (chunk) => {
data += chunk;
});

res.on("end", () => {
if (res.statusCode === 200) {
resolve({ success: true, data });
} else {
resolve({
success: false,
error: `HTTP ${res.statusCode}`,
});
}
});
});

req.on("error", (err) => {
resolve({
success: false,
error: err.message || String(err),
});
});

req.setTimeout(10000, () => {
req.destroy();
resolve({
success: false,
error: "Request timeout",
});
});

req.end();
},
);
});

// App state persistence ---------------------------------------------------
// Keep metadata (id, path, name) of opened/created files and activeFileId
function getAppStatePath(): string {
Expand Down
6 changes: 6 additions & 0 deletions src/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ contextBridge.exposeInMainWorld("electronAPI", {
ipcRenderer.invoke("check-for-updates"),
installUpdate: (): Promise<{ ok: boolean; message?: string }> =>
ipcRenderer.invoke("install-update"),
// Fetch GitHub releases RSS feed
fetchReleasesFeed: (): Promise<{
success: boolean;
data?: string;
error?: string;
}> => ipcRenderer.invoke("fetch-releases-feed"),
onUpdateEvent: (
callback: (event: {
type:
Expand Down
103 changes: 20 additions & 83 deletions src/renderer/components/SettingsView.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { ChevronLeft, Settings } from "lucide-react";
import { useEffect, useState } from "react";
import { useEffect } from "react";
import { useAppStore } from "../store/appStore";
import { AboutPage } from "./settings/AboutPage";
import { AppearancePage } from "./settings/AppearancePage";
import { UpdatesPage } from "./settings/UpdatesPage";
import { defaultSettingsPages } from "./settings-pages";
import TopBar from "./TopBar";
import { Button } from "./ui/button";
import IconButton from "./ui/icon-button";

export default function SettingsView() {
const setWorkspaceMode = useAppStore((s) => s.setWorkspaceMode);
const [checkingUpdate, setCheckingUpdate] = useState(false);
const [updateStatus, setUpdateStatus] = useState<string | null>(null);
const activeSettingsPageId = useAppStore((s) => s.activeSettingsPageId);

// 键盘快捷键:ESC 返回编辑器
useEffect(() => {
Expand All @@ -22,31 +24,19 @@ export default function SettingsView() {
return () => window.removeEventListener("keydown", handleKeyDown);
}, [setWorkspaceMode]);

const toggleTheme = () => {
const root = document.documentElement;
root.classList.toggle("dark");
try {
localStorage.setItem(
"theme",
root.classList.contains("dark") ? "dark" : "light",
);
} catch {}
};

const handleCheckUpdate = async () => {
setCheckingUpdate(true);
setUpdateStatus("正在检查更新...");
try {
const result = await window.electronAPI.checkForUpdates();
if (!result?.supported) {
setUpdateStatus(result?.message ?? "当前环境不支持更新检查");
} else {
setUpdateStatus("已触发检查,请留意右下角更新提示");
}
} catch (err) {
setUpdateStatus(`检查失败:${String(err)}`);
} finally {
setCheckingUpdate(false);
// 根据 activeSettingsPageId 渲染对应的页面
const renderPage = () => {
const pageId = activeSettingsPageId || defaultSettingsPages[0].id;

switch (pageId) {
case "appearance":
return <AppearancePage />;
case "updates":
return <UpdatesPage />;
case "about":
return <AboutPage />;
default:
return <AppearancePage />;
}
};

Expand All @@ -67,60 +57,7 @@ export default function SettingsView() {
title="设置"
/>

<div className="flex-1 overflow-auto p-4 space-y-4">
<section className="bg-card border border-border rounded p-4">
<h3 className="text-sm font-medium mb-2">外观</h3>
<div className="flex items-center gap-3">
<Button
type="button"
variant="outline"
onClick={toggleTheme}
aria-label="切换明暗主题"
>
切换明暗主题
</Button>
<p className="text-xs text-muted-foreground">
当前主题由页面 class 控制
</p>
</div>
</section>

<section className="bg-card border border-border rounded p-4">
<h3 className="text-sm font-medium mb-2">更新</h3>
<div className="flex items-center gap-3">
<Button
type="button"
onClick={handleCheckUpdate}
disabled={checkingUpdate}
>
{checkingUpdate ? "检查中..." : "检查更新"}
</Button>
{updateStatus && (
<p className="text-xs text-muted-foreground">{updateStatus}</p>
)}
</div>
</section>

<section className="bg-card border border-border rounded p-4">
<h3 className="text-sm font-medium mb-2">关于 v0.1.4</h3>
<div className="space-y-2">
<p className="text-xs text-muted-foreground">
Tabst. Write guitar tabs like markdown. Powered by alphaTab.js.
</p>
<p className="text-xs text-muted-foreground">
高效编写 alphaTex,播放乐谱,分享 PDF/GP。
</p>
<a
href="https://github.com/LIUBINfighter/Tabst.app"
target="_blank"
rel="noopener noreferrer"
className="text-xs text-primary hover:underline inline-block"
>
GitHub →
</a>
</div>
</section>
</div>
<div className="flex-1 overflow-auto p-4">{renderPage()}</div>
</div>
);
}
23 changes: 23 additions & 0 deletions src/renderer/components/settings/AboutPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export function AboutPage() {
return (
<section className="bg-card border border-border rounded p-4">
<h3 className="text-sm font-medium mb-2">关于 v0.1.4</h3>
<div className="space-y-2">
<p className="text-xs text-muted-foreground">
Tabst. Write guitar tabs like markdown. Powered by alphaTab.js.
</p>
<p className="text-xs text-muted-foreground">
高效编写 alphaTex,播放乐谱,分享 PDF/GP。
</p>
<a
href="https://github.com/LIUBINfighter/Tabst.app"
target="_blank"
rel="noopener noreferrer"
className="text-xs text-primary hover:underline inline-block"
>
GitHub →
</a>
</div>
</section>
);
}
33 changes: 33 additions & 0 deletions src/renderer/components/settings/AppearancePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Button } from "../ui/button";

export function AppearancePage() {
const toggleTheme = () => {
const root = document.documentElement;
root.classList.toggle("dark");
try {
localStorage.setItem(
"theme",
root.classList.contains("dark") ? "dark" : "light",
);
} catch {}
};

return (
<section className="bg-card border border-border rounded p-4">
<h3 className="text-sm font-medium mb-2">外观</h3>
<div className="flex items-center gap-3">
<Button
type="button"
variant="outline"
onClick={toggleTheme}
aria-label="切换明暗主题"
>
切换明暗主题
</Button>
<p className="text-xs text-muted-foreground">
当前主题由页面 class 控制
</p>
</div>
</section>
);
}
Loading