Compare commits

...

7 Commits

Author SHA1 Message Date
shenmo7192 3d4af0c492 改为5.0.1 2026-04-25 14:40:37 +08:00
shenmo7192 c39b25e393 5.1 2026-04-25 14:33:21 +08:00
shenmo7192 2086152aa5 refactor: 移除缓存清除函数并直接使用原始URL
移除cacheBuster函数及其所有调用,改为直接使用原始URL进行请求
2026-04-25 14:25:52 +08:00
shenmo7192 f8f112a782 !388 feat(build): add loong64 to build
Merge pull request !388 from AAA Elysia 猫猫侠 ⁧~喵/elysia/add-loong64
2026-04-21 14:04:55 +00:00
Elysia 6a9091b2ec feat(build): add loong64
- Downgrad electron for the sake of loong64
- Add my project to CREDIT.md

Signed-off-by: Elysia <a.elysia@proton.me>
2026-04-19 09:37:21 +08:00
shenmo7192 994dbaf9b9 !387 优化dark模式下应用管理的关闭按钮和刷新按钮、软件更新的关闭按钮的hover效果
Merge pull request !387 from zeqi/Erotica
2026-04-16 22:59:56 +00:00
zeqi 9c9f0b6076 优化dark模式下应用管理的关闭按钮和刷新按钮、软件更新的关闭按钮的hover效果
Signed-off-by: zeqi <a202128502@163.com>
2026-04-16 15:27:55 +00:00
12 changed files with 240 additions and 153 deletions
+20
View File
@@ -23,3 +23,23 @@
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
2. https://github.com/elysia-best/apm-app-store MulanPSL-2.0
Copyright (c) 2026-present The Spark Project Contributors
apm-store is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
+40 -11
View File
@@ -376,7 +376,9 @@ export const loadUpdateCenterItems = async (
storeFilter: StoreFilter = "both",
runCommand: UpdateCenterCommandRunner = runCommandCapture,
): Promise<UpdateCenterLoadItemsResult> => {
console.log(`[UpdateCenter] loadUpdateCenterItems called with storeFilter=${storeFilter}`);
console.log(
`[UpdateCenter] loadUpdateCenterItems called with storeFilter=${storeFilter}`,
);
const [sparkEnabled, apmEnabled] = await Promise.all([
isSourceEnabled(storeFilter, "spark")
? isCommandAvailable(runCommand, "aptss")
@@ -385,7 +387,9 @@ export const loadUpdateCenterItems = async (
? isCommandAvailable(runCommand, "apm")
: Promise.resolve(false),
]);
console.log(`[UpdateCenter] sparkEnabled=${sparkEnabled}, apmEnabled=${apmEnabled}`);
console.log(
`[UpdateCenter] sparkEnabled=${sparkEnabled}, apmEnabled=${apmEnabled}`,
);
const [aptssResult, apmResult, aptssInstalledResult, apmInstalledResult] =
await Promise.all([
@@ -409,10 +413,18 @@ export const loadUpdateCenterItems = async (
: Promise.resolve({ code: 0, stdout: "", stderr: "" }),
]);
console.log(`[UpdateCenter] aptssResult: code=${aptssResult.code}, stdout=${aptssResult.stdout.substring(0, 500)}, stderr=${aptssResult.stderr.substring(0, 500)}`);
console.log(`[UpdateCenter] apmResult: code=${apmResult.code}, stdout=${apmResult.stdout.substring(0, 500)}, stderr=${apmResult.stderr.substring(0, 500)}`);
console.log(`[UpdateCenter] aptssInstalledResult: code=${aptssInstalledResult.code}, stdout=${aptssInstalledResult.stdout.substring(0, 500)}`);
console.log(`[UpdateCenter] apmInstalledResult: code=${apmInstalledResult.code}, stdout=${apmInstalledResult.stdout.substring(0, 500)}`);
console.log(
`[UpdateCenter] aptssResult: code=${aptssResult.code}, stdout=${aptssResult.stdout.substring(0, 500)}, stderr=${aptssResult.stderr.substring(0, 500)}`,
);
console.log(
`[UpdateCenter] apmResult: code=${apmResult.code}, stdout=${apmResult.stdout.substring(0, 500)}, stderr=${apmResult.stderr.substring(0, 500)}`,
);
console.log(
`[UpdateCenter] aptssInstalledResult: code=${aptssInstalledResult.code}, stdout=${aptssInstalledResult.stdout.substring(0, 500)}`,
);
console.log(
`[UpdateCenter] apmInstalledResult: code=${apmInstalledResult.code}, stdout=${apmInstalledResult.stdout.substring(0, 500)}`,
);
const aptssAvailable =
sparkEnabled && (aptssResult.code === 0 || aptssInstalledResult.code === 0);
@@ -438,8 +450,14 @@ export const loadUpdateCenterItems = async (
apmEnabled && apmResult.code === 0
? parseApmUpgradableOutput(apmResult.stdout)
: [];
console.log(`[UpdateCenter] parsed aptssItems count=${aptssItems.length}`, aptssItems.map((i) => `${i.pkgname} ${i.currentVersion}->${i.nextVersion}`));
console.log(`[UpdateCenter] parsed apmItems count=${apmItems.length}`, apmItems.map((i) => `${i.pkgname} ${i.currentVersion}->${i.nextVersion}`));
console.log(
`[UpdateCenter] parsed aptssItems count=${aptssItems.length}`,
aptssItems.map((i) => `${i.pkgname} ${i.currentVersion}->${i.nextVersion}`),
);
console.log(
`[UpdateCenter] parsed apmItems count=${apmItems.length}`,
apmItems.map((i) => `${i.pkgname} ${i.currentVersion}->${i.nextVersion}`),
);
const installedSources = buildInstalledSourceMap(
aptssAvailable && aptssInstalledResult.code === 0
@@ -461,15 +479,26 @@ export const loadUpdateCenterItems = async (
? enrichApmItems(categorizedApmItems, runCommand)
: Promise.resolve({ items: [], warnings: [] }),
]);
console.log(`[UpdateCenter] enrichedAptssItems: count=${enrichedAptssItems.items.length}, warnings=${enrichedAptssItems.warnings.length}`, enrichedAptssItems.warnings);
console.log(`[UpdateCenter] enrichedApmItems: count=${enrichedApmItems.items.length}, warnings=${enrichedApmItems.warnings.length}`, enrichedApmItems.warnings);
console.log(
`[UpdateCenter] enrichedAptssItems: count=${enrichedAptssItems.items.length}, warnings=${enrichedAptssItems.warnings.length}`,
enrichedAptssItems.warnings,
);
console.log(
`[UpdateCenter] enrichedApmItems: count=${enrichedApmItems.items.length}, warnings=${enrichedApmItems.warnings.length}`,
enrichedApmItems.warnings,
);
const mergedItems = mergeUpdateSources(
enrichItemIcons(enrichedAptssItems.items),
enrichItemIcons(enrichedApmItems.items),
installedSources,
);
console.log(`[UpdateCenter] mergedItems count=${mergedItems.length}`, mergedItems.map((i) => `${i.pkgname} (${i.source}) ${i.currentVersion}->${i.nextVersion}`));
console.log(
`[UpdateCenter] mergedItems count=${mergedItems.length}`,
mergedItems.map(
(i) => `${i.pkgname} (${i.source}) ${i.currentVersion}->${i.nextVersion}`,
),
);
return {
items: mergedItems,
+21 -7
View File
@@ -263,16 +263,26 @@ const compareVersions = (left: string, right: string): number => {
export const parseAptssUpgradableOutput = (
output: string,
): UpdateCenterItem[] => {
console.log(`[UpdateCenter] parseAptssUpgradableOutput input (first 1000 chars): ${output.substring(0, 1000)}`);
console.log(
`[UpdateCenter] parseAptssUpgradableOutput input (first 1000 chars): ${output.substring(0, 1000)}`,
);
const result = parseUpgradableOutput(output, "aptss");
console.log(`[UpdateCenter] parseAptssUpgradableOutput result count=${result.length}`);
console.log(
`[UpdateCenter] parseAptssUpgradableOutput result count=${result.length}`,
);
return result;
};
export const parseApmUpgradableOutput = (output: string): UpdateCenterItem[] => {
console.log(`[UpdateCenter] parseApmUpgradableOutput input (first 1000 chars): ${output.substring(0, 1000)}`);
export const parseApmUpgradableOutput = (
output: string,
): UpdateCenterItem[] => {
console.log(
`[UpdateCenter] parseApmUpgradableOutput input (first 1000 chars): ${output.substring(0, 1000)}`,
);
const result = parseUpgradableOutput(output, "apm");
console.log(`[UpdateCenter] parseApmUpgradableOutput result count=${result.length}`);
console.log(
`[UpdateCenter] parseApmUpgradableOutput result count=${result.length}`,
);
return result;
};
@@ -283,10 +293,14 @@ export const parsePrintUrisOutput = (
"downloadUrl" | "fileName" | "size" | "sha512"
> | null => {
const trimmed = output.trim();
console.log(`[UpdateCenter] parsePrintUrisOutput input (first 500 chars): ${trimmed.substring(0, 500)}`);
console.log(
`[UpdateCenter] parsePrintUrisOutput input (first 500 chars): ${trimmed.substring(0, 500)}`,
);
const match = trimmed.match(PRINT_URIS_PATTERN);
if (!match) {
console.log(`[UpdateCenter] parsePrintUrisOutput: no match found for pattern ${PRINT_URIS_PATTERN}`);
console.log(
`[UpdateCenter] parsePrintUrisOutput: no match found for pattern ${PRINT_URIS_PATTERN}`,
);
return null;
}
+10 -3
View File
@@ -166,7 +166,9 @@ export const createUpdateCenterService = (
storeFilter: StoreFilter = currentStoreFilter,
): Promise<UpdateCenterServiceState> => {
currentStoreFilter = storeFilter;
console.log(`[UpdateCenter] service.refresh called with storeFilter=${storeFilter}`);
console.log(
`[UpdateCenter] service.refresh called with storeFilter=${storeFilter}`,
);
queue.startRefresh();
emit();
@@ -176,11 +178,16 @@ export const createUpdateCenterService = (
const loadedItems = normalizeLoadedItems(
await options.loadItems(currentStoreFilter),
);
console.log(`[UpdateCenter] loadItems returned: items=${loadedItems.items.length}, warnings=${loadedItems.warnings.length}`, loadedItems.warnings);
console.log(
`[UpdateCenter] loadItems returned: items=${loadedItems.items.length}, warnings=${loadedItems.warnings.length}`,
loadedItems.warnings,
);
const items = sortIgnoredItems(
applyIgnoredEntries(loadedItems.items, ignoredEntries),
);
console.log(`[UpdateCenter] after applying ignored: items=${items.length}`);
console.log(
`[UpdateCenter] after applying ignored: items=${items.length}`,
);
queue.setItems(items);
queue.finishRefresh(loadedItems.warnings);
return emit();
+5
View File
@@ -98,6 +98,10 @@ logger.info("User Agent: " + getUserAgent());
/** 根据启动参数 --no-apm / --no-spark 决定只展示的来源 */
function getStoreFilterFromArgv(): "spark" | "apm" | "both" {
if (process.arch === "loong64") {
// Currently loong64 only have spark support
return "spark";
} else {
const argv = process.argv;
const noApm = argv.includes("--no-apm");
const noSpark = argv.includes("--no-spark");
@@ -105,6 +109,7 @@ function getStoreFilterFromArgv(): "spark" | "apm" | "both" {
if (noApm) return "spark";
if (noSpark) return "apm";
return "both";
}
}
ipcMain.handle("get-store-filter", (): "spark" | "apm" | "both" =>
+17 -15
View File
@@ -1,12 +1,12 @@
{
"name": "spark-store",
"version": "5.0.0beta4",
"version": "5.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "spark-store",
"version": "5.0.0beta4",
"version": "5.0.0",
"license": "GPL-3.0",
"dependencies": {
"@tailwindcss/vite": "^4.1.18",
@@ -28,7 +28,7 @@
"@vue/test-utils": "^2.4.3",
"conventional-changelog": "^7.1.1",
"conventional-changelog-angular": "^8.1.0",
"electron": "^40.0.0",
"electron": "^39.2.7",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.5",
@@ -4870,15 +4870,15 @@
}
},
"node_modules/electron": {
"version": "40.8.5",
"resolved": "https://registry.npmjs.org/electron/-/electron-40.8.5.tgz",
"integrity": "sha512-pgTY/VPQKaiU4sTjfU96iyxCXrFm4htVPCMRT4b7q9ijNTRgtLmLvcmzp2G4e7xDrq9p7OLHSmu1rBKFf6Y1/A==",
"version": "39.2.7",
"resolved": "https://registry.npmjs.org/electron/-/electron-39.2.7.tgz",
"integrity": "sha512-KU0uFS6LSTh4aOIC3miolcbizOFP7N1M46VTYVfqIgFiuA2ilfNaOHLDS9tCMvwwHRowAsvqBrh9NgMXcTOHCQ==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@electron/get": "^2.0.0",
"@types/node": "^24.9.0",
"@types/node": "^22.7.7",
"extract-zip": "^2.0.1"
},
"bin": {
@@ -4889,19 +4889,21 @@
}
},
"node_modules/electron/node_modules/@types/node": {
"version": "24.12.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz",
"integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==",
"version": "22.19.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz",
"integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.16.0"
"undici-types": "~6.21.0"
}
},
"node_modules/electron/node_modules/undici-types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"dev": true
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/emoji-regex": {
"version": "8.0.0",
+3 -2
View File
@@ -1,6 +1,6 @@
{
"name": "spark-store",
"version": "5.0.0",
"version": "5.0.1",
"main": "dist-electron/main/index.js",
"description": "Client for Spark App Store",
"author": "elysia-best <elysia-best@simplelinux.cn.eu.org>",
@@ -30,6 +30,7 @@
"build:vite": "vue-tsc --noEmit && vite build --mode production",
"build:rpm": "vue-tsc --noEmit && vite build --mode production && electron-builder --config electron-builder.yml --linux rpm",
"build:deb": "vue-tsc --noEmit && vite build --mode production && electron-builder --config electron-builder.yml --linux deb",
"build:deb-loong64": "vue-tsc --noEmit && vite build --mode production && env ELECTRON_MIRROR=https://github.com/darkyzhou/electron-loong64/releases/download/ electron_use_remote_checksums=1 electron-builder --config electron-builder.yml --loong64 --linux deb",
"preview": "vite preview --mode debug",
"lint": "eslint --ext .ts,.vue src electron",
"lint:fix": "eslint --ext .ts,.vue src electron --fix",
@@ -56,7 +57,7 @@
"@vue/test-utils": "^2.4.3",
"conventional-changelog": "^7.1.1",
"conventional-changelog-angular": "^8.1.0",
"electron": "^40.0.0",
"electron": "^39.2.7",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.5",
+7 -9
View File
@@ -238,8 +238,6 @@ const fetchWithRetry = async <T,>(
}
};
const cacheBuster = (url: string) => `${url}?cb=${Date.now()}`;
// 响应式状态
const themeMode = ref<"light" | "dark" | "auto">("auto");
const systemIsDark = ref(
@@ -401,7 +399,7 @@ const fetchAppFromStore = async (
const arch = window.apm_store.arch || "amd64";
const finalArch = origin === "spark" ? `${arch}-store` : `${arch}-apm`;
const appJsonUrl = `${APM_STORE_BASE_URL}/${finalArch}/${category}/${pkgname}/app.json`;
const response = await fetch(cacheBuster(appJsonUrl));
const response = await fetch(appJsonUrl);
if (!response.ok) return null;
const appJson = await response.json();
return {
@@ -672,7 +670,7 @@ const loadHome = async () => {
// homelinks.json
try {
const res = await fetch(cacheBuster(`${base}/homelinks.json`));
const res = await fetch(`${base}/homelinks.json`);
if (res.ok) {
const links = await res.json();
const taggedLinks = links.map((l: HomeLink) => ({
@@ -687,14 +685,14 @@ const loadHome = async () => {
// homelist.json
try {
const res2 = await fetch(cacheBuster(`${base}/homelist.json`));
const res2 = await fetch(`${base}/homelist.json`);
if (res2.ok) {
const lists = await res2.json();
for (const item of lists) {
if (item.type === "appList" && item.jsonUrl) {
try {
const url = `${APM_STORE_BASE_URL}/${finalArch}${item.jsonUrl}`;
const r = await fetch(cacheBuster(url));
const r = await fetch(url);
if (r.ok) {
const appsJson = await r.json();
const rawApps = appsJson || [];
@@ -712,7 +710,7 @@ const loadHome = async () => {
try {
const realAppUrl = `${APM_STORE_BASE_URL}/${finalArch}/${baseApp.category}/${baseApp.pkgname}/app.json`;
const realRes = await fetch(cacheBuster(realAppUrl));
const realRes = await fetch(realAppUrl);
if (realRes.ok) {
const realApp = await realRes.json();
if (realApp.Filename)
@@ -1081,7 +1079,7 @@ const loadCategories = async () => {
const path = `/${finalArch}/categories.json`;
try {
const response = await axiosInstance.get(cacheBuster(path));
const response = await axiosInstance.get(path);
const data = response.data;
Object.keys(data).forEach((key) => {
if (categoryData[key]) {
@@ -1134,7 +1132,7 @@ const loadApps = async (onFirstBatch?: () => void) => {
logger.info(`加载分类: ${category} (来源: ${mode})`);
const categoryApps = await fetchWithRetry<AppJson[]>(
cacheBuster(path),
path,
);
const normalizedApps = (categoryApps || []).map((appJson) => ({
@@ -191,7 +191,9 @@ describe("update-center load items", () => {
],
});
const result = await loadUpdateCenterItems("both", async (command, args) => {
const result = await loadUpdateCenterItems(
"both",
async (command, args) => {
const key = `${command} ${args.join(" ")}`;
const match = commandResults.get(key);
if (!match) {
@@ -199,7 +201,8 @@ describe("update-center load items", () => {
}
return match;
});
},
);
expect(result.warnings).toEqual([]);
expect(result.items).toContainEqual({
@@ -233,7 +236,9 @@ describe("update-center load items", () => {
],
});
const result = await loadUpdateCenterItems("both", async (command, args) => {
const result = await loadUpdateCenterItems(
"both",
async (command, args) => {
const key = `${command} ${args.join(" ")}`;
if (key === WHICH_APTSS_KEY) {
@@ -287,7 +292,8 @@ describe("update-center load items", () => {
}
throw new Error(`Unexpected command ${key}`);
});
},
);
expect(result.items).toEqual([
{
@@ -416,7 +422,9 @@ describe("update-center load items", () => {
],
});
const result = await loadUpdateCenterItems("both", async (command, args) => {
const result = await loadUpdateCenterItems(
"both",
async (command, args) => {
const key = `${command} ${args.join(" ")}`;
if (key === WHICH_APTSS_KEY) {
@@ -461,7 +469,8 @@ describe("update-center load items", () => {
}
throw new Error(`Unexpected command ${key}`);
});
},
);
expect(result.items).toEqual([
{
+2 -2
View File
@@ -62,7 +62,7 @@
</div>
<button
type="button"
class="inline-flex items-center gap-2 rounded-2xl border border-slate-200/70 px-4 py-2 text-sm font-semibold text-slate-600 transition hover:bg-slate-50 disabled:opacity-40 dark:border-slate-700 dark:text-slate-200"
class="inline-flex items-center gap-2 rounded-2xl border border-slate-200/70 px-4 py-2 text-sm font-semibold text-slate-600 transition hover:bg-slate-50 disabled:opacity-40 dark:border-slate-700 dark:text-slate-200 dark:hover:text-white"
:disabled="loading"
@click="$emit('refresh')"
>
@@ -71,7 +71,7 @@
</button>
<button
type="button"
class="inline-flex h-10 w-10 items-center justify-center rounded-full border border-slate-200/70 text-slate-500 transition hover:text-slate-900 dark:border-slate-700"
class="inline-flex h-10 w-10 items-center justify-center rounded-full border border-slate-200/70 text-slate-500 transition hover:text-slate-900 dark:hover:text-white dark:border-slate-700 dark:hover:bg-slate-800"
@click="$emit('close')"
aria-label="关闭"
>
+3 -1
View File
@@ -47,7 +47,9 @@
v-if="store.loading.value && store.filteredItems.value.length === 0"
class="flex min-h-0 flex-1 items-center justify-center p-6"
>
<div class="flex flex-col items-center gap-3 text-slate-500 dark:text-slate-400">
<div
class="flex flex-col items-center gap-3 text-slate-500 dark:text-slate-400"
>
<i class="fas fa-circle-notch fa-spin text-3xl"></i>
<p class="text-sm">正在检查更新</p>
</div>
@@ -33,7 +33,7 @@
<button
type="button"
aria-label="关闭"
class="inline-flex h-11 w-11 items-center justify-center rounded-full border border-slate-200/70 text-slate-500 transition hover:text-slate-900 dark:border-slate-700 dark:text-slate-300"
class="inline-flex h-11 w-11 items-center justify-center rounded-full border border-slate-200/70 text-slate-500 transition hover:text-slate-900 dark:hover:text-white dark:border-slate-700 dark:text-slate-300"
@click="$emit('request-close')"
>
<i class="fas fa-xmark"></i>