完成 -apm 的全部更改,实现 spk://search,移除 spk://store

This commit is contained in:
2026-02-19 19:56:32 +08:00
parent 3165304016
commit b72f5f8da6
7 changed files with 95 additions and 34 deletions

View File

@@ -1,3 +1,3 @@
VITE_APM_STORE_LOCAL_MODE=true VITE_APM_STORE_LOCAL_MODE=true
VITE_APM_STORE_BASE_URL=/local_amd64-apm VITE_APM_STORE_BASE_URL=/local_amd64-store
VITE_APM_STORE_STATS_BASE_URL=/local_stats VITE_APM_STORE_STATS_BASE_URL=/local_stats

1
.vscode/launch.json vendored
View File

@@ -20,6 +20,7 @@
} }
], ],
"configurations": [ "configurations": [
{ {
"name": "Debug Main Process", "name": "Debug Main Process",
"type": "node", "type": "node",

View File

@@ -90,7 +90,7 @@ event.sender.send('install-complete', { id, success, ... });
**Exposed APIs in Preload:** **Exposed APIs in Preload:**
- `window.ipcRenderer.on/off/send/invoke` - IPC communication - `window.ipcRenderer.on/off/send/invoke` - IPC communication
- `window.apm_store.arch` - System architecture detection (amd64-apm, arm64-apm) - `window.apm_store.arch` - System architecture detection (amd64-store, arm64-store)
### 3. Installation Queue System ### 3. Installation Queue System
@@ -345,10 +345,10 @@ const response = await axiosInstance.get<AppJson[]>(
```typescript ```typescript
server: { server: {
proxy: { proxy: {
'/local_amd64-apm': { '/local_amd64-store': {
target: 'https://erotica.spark-app.store', target: 'https://erotica.spark-app.store',
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/local_amd64-apm/, ''), rewrite: (path) => path.replace(/^\/local_amd64-store/, ''),
} }
} }
} }
@@ -356,36 +356,54 @@ server: {
--- ---
## 🎯 Deep Link Protocol ## 🎯 Deep Link Protocol (SPK URI Scheme)
**URL Scheme:** `apmstore://` **URL Scheme:** `spk://`
### Supported Actions ### Supported SPK URI Format
1. **Install App:** `apmstore://install?pkg=<pkgname>` Format: `spk://search/{pkgname}`
2. **Show Updates:** `apmstore://update`
3. **Show Installed:** `apmstore://installed` **Examples:**
- `spk://search/code` - Search for and open "code" app
- `spk://search/steam` - Search for and open "steam" app
- `spk://search/store.spark-app.hmcl` - Search for and open "HMCL" game
### Implementation Pattern ### Implementation Pattern
```typescript ```typescript
// electron/main/deeplink.ts - Parse command line // electron/main/deeplink.ts - Parse command line and route
export function handleCommandLine(argv: string[]) { export function handleCommandLine(commandLine: string[]) {
const deeplinkUrl = argv.find((arg) => arg.startsWith('apmstore://')); const deeplinkUrl = commandLine.find((arg) =>
arg.startsWith('spk://')
);
if (!deeplinkUrl) return; if (!deeplinkUrl) return;
try {
const url = new URL(deeplinkUrl); const url = new URL(deeplinkUrl);
if (url.hostname === 'install') { const action = url.hostname; // 'search'
const pkg = url.searchParams.get('pkg');
sendToRenderer('deep-link-install', pkg); if (action === 'search') {
// Format: spk://search/pkgname
// url.pathname will be '/pkgname'
const pkgname = url.pathname.split('/').filter(Boolean)[0];
if (pkgname) {
listeners.emit('search', { pkgname });
}
}
} catch (error) {
logger.error({ err: error }, 'Error parsing SPK URI');
} }
} }
// src/App.vue - Handle in renderer // src/App.vue - Handle in renderer
window.ipcRenderer.on('deep-link-install', (_event, pkgname: string) => { window.ipcRenderer.on(
const target = apps.value.find((a) => a.pkgname === pkgname); 'deep-link-search',
if (target) openDetail(target); (_event: IpcRendererEvent, data: { pkgname: string }) => {
}); // Trigger search with the pkgname
searchQuery.value = data.pkgname;
}
);
``` ```
--- ---

View File

@@ -45,7 +45,7 @@ class ListenersMap {
} }
} }
const protocols = ["apmstore"]; const protocols = ["spk"];
const listeners = new ListenersMap(); const listeners = new ListenersMap();
export const deepLink = { export const deepLink = {
@@ -81,17 +81,27 @@ export function handleCommandLine(commandLine: string[]) {
try { try {
const url = new URL(target); const url = new URL(target);
const action = url.hostname; const action = url.hostname; // 'search'
logger.info(`Deep link: action found: ${action}`); logger.info(`Deep link: action found: ${action}`);
const query: Query = {}; const query: Query = {};
url.searchParams.forEach((value, key) => {
query[key] = value;
});
logger.info(`Deep link: query found: ${JSON.stringify(query)}`);
const emitCount = listeners.emit(action, query); if (action === "search") {
logger.info(`Deep link: emitted for ${emitCount} listeners`); // Format: spk://search/pkgname
// url.pathname will be '/pkgname'
const pkgname = url.pathname.split("/").filter(Boolean)[0];
if (pkgname) {
query.pkgname = pkgname;
logger.info(`Deep link: search query found: ${JSON.stringify(query)}`);
listeners.emit(action, query);
} else {
logger.warn(
`Deep link: invalid search format, expected /pkgname, got ${url.pathname}`,
);
}
} else {
logger.warn(`Deep link: unknown action ${action}`);
}
} catch (error) { } catch (error) {
logger.error(`Deep link: error parsing URL: ${error}`); logger.error(`Deep link: error parsing URL: ${error}`);
} }

View File

@@ -80,3 +80,28 @@ deepLink.on("install", (query) => {
pendingActions.push(action); pendingActions.push(action);
} }
}); });
deepLink.on("search", (query) => {
logger.info(
`Deep link: event "search" fired with query: ${JSON.stringify(query)}`,
);
const action = () => {
const win = BrowserWindow.getAllWindows()[0];
if (!win) return;
if (query.pkgname) {
win.webContents.send("deep-link-search", { pkgname: query.pkgname });
if (win.isMinimized()) win.restore();
win.focus();
}
};
logger.info(`isLoaded: ${isLoaded.value}`);
if (isLoaded.value) {
action();
} else {
pendingActions.push(action);
}
});

View File

@@ -781,6 +781,13 @@ onMounted(async () => {
}, },
); );
window.ipcRenderer.on(
"deep-link-search",
(_event: IpcRendererEvent, data: { pkgname: string }) => {
searchQuery.value = data.pkgname;
},
);
window.ipcRenderer.on( window.ipcRenderer.on(
"remove-complete", "remove-complete",
(_event: IpcRendererEvent, payload: ChannelPayload) => { (_event: IpcRendererEvent, payload: ChannelPayload) => {

View File

@@ -77,10 +77,10 @@ export default defineConfig(({ command }) => {
host: url.hostname, host: url.hostname,
port: +url.port, port: +url.port,
proxy: { proxy: {
"/local_amd64-apm": { "/local_amd64-store": {
target: "https://erotica.spark-app.store", target: "https://erotica.spark-app.store",
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/local_amd64-apm/, ""), rewrite: (path) => path.replace(/^\/local_amd64-store/, ""),
}, },
"/local_stats": { "/local_stats": {
target: "https://feedback.spark-app.store", target: "https://feedback.spark-app.store",
@@ -92,10 +92,10 @@ export default defineConfig(({ command }) => {
} else { } else {
return { return {
proxy: { proxy: {
"/local_amd64-apm": { "/local_amd64-store": {
target: "https://erotica.spark-app.store", target: "https://erotica.spark-app.store",
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/local_amd64-apm/, ""), rewrite: (path) => path.replace(/^\/local_amd64-store/, ""),
}, },
"/local_stats": { "/local_stats": {
target: "https://feedback.spark-app.store", target: "https://feedback.spark-app.store",