mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-04-26 01:10:16 +08:00
完成 -apm 的全部更改,实现 spk://search,移除 spk://store
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
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
|
||||
|
||||
1
.vscode/launch.json
vendored
1
.vscode/launch.json
vendored
@@ -20,6 +20,7 @@
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"name": "Debug Main Process",
|
||||
"type": "node",
|
||||
|
||||
58
AGENTS.md
58
AGENTS.md
@@ -90,7 +90,7 @@ event.sender.send('install-complete', { id, success, ... });
|
||||
|
||||
**Exposed APIs in Preload:**
|
||||
- `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
|
||||
|
||||
@@ -345,10 +345,10 @@ const response = await axiosInstance.get<AppJson[]>(
|
||||
```typescript
|
||||
server: {
|
||||
proxy: {
|
||||
'/local_amd64-apm': {
|
||||
'/local_amd64-store': {
|
||||
target: 'https://erotica.spark-app.store',
|
||||
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>`
|
||||
2. **Show Updates:** `apmstore://update`
|
||||
3. **Show Installed:** `apmstore://installed`
|
||||
Format: `spk://search/{pkgname}`
|
||||
|
||||
**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
|
||||
|
||||
```typescript
|
||||
// electron/main/deeplink.ts - Parse command line
|
||||
export function handleCommandLine(argv: string[]) {
|
||||
const deeplinkUrl = argv.find((arg) => arg.startsWith('apmstore://'));
|
||||
// electron/main/deeplink.ts - Parse command line and route
|
||||
export function handleCommandLine(commandLine: string[]) {
|
||||
const deeplinkUrl = commandLine.find((arg) =>
|
||||
arg.startsWith('spk://')
|
||||
);
|
||||
if (!deeplinkUrl) return;
|
||||
|
||||
const url = new URL(deeplinkUrl);
|
||||
if (url.hostname === 'install') {
|
||||
const pkg = url.searchParams.get('pkg');
|
||||
sendToRenderer('deep-link-install', pkg);
|
||||
try {
|
||||
const url = new URL(deeplinkUrl);
|
||||
const action = url.hostname; // 'search'
|
||||
|
||||
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
|
||||
window.ipcRenderer.on('deep-link-install', (_event, pkgname: string) => {
|
||||
const target = apps.value.find((a) => a.pkgname === pkgname);
|
||||
if (target) openDetail(target);
|
||||
});
|
||||
window.ipcRenderer.on(
|
||||
'deep-link-search',
|
||||
(_event: IpcRendererEvent, data: { pkgname: string }) => {
|
||||
// Trigger search with the pkgname
|
||||
searchQuery.value = data.pkgname;
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -45,7 +45,7 @@ class ListenersMap {
|
||||
}
|
||||
}
|
||||
|
||||
const protocols = ["apmstore"];
|
||||
const protocols = ["spk"];
|
||||
const listeners = new ListenersMap();
|
||||
|
||||
export const deepLink = {
|
||||
@@ -81,17 +81,27 @@ export function handleCommandLine(commandLine: string[]) {
|
||||
try {
|
||||
const url = new URL(target);
|
||||
|
||||
const action = url.hostname;
|
||||
const action = url.hostname; // 'search'
|
||||
logger.info(`Deep link: action found: ${action}`);
|
||||
|
||||
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);
|
||||
logger.info(`Deep link: emitted for ${emitCount} listeners`);
|
||||
if (action === "search") {
|
||||
// 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) {
|
||||
logger.error(`Deep link: error parsing URL: ${error}`);
|
||||
}
|
||||
|
||||
@@ -80,3 +80,28 @@ deepLink.on("install", (query) => {
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -781,6 +781,13 @@ onMounted(async () => {
|
||||
},
|
||||
);
|
||||
|
||||
window.ipcRenderer.on(
|
||||
"deep-link-search",
|
||||
(_event: IpcRendererEvent, data: { pkgname: string }) => {
|
||||
searchQuery.value = data.pkgname;
|
||||
},
|
||||
);
|
||||
|
||||
window.ipcRenderer.on(
|
||||
"remove-complete",
|
||||
(_event: IpcRendererEvent, payload: ChannelPayload) => {
|
||||
|
||||
@@ -77,10 +77,10 @@ export default defineConfig(({ command }) => {
|
||||
host: url.hostname,
|
||||
port: +url.port,
|
||||
proxy: {
|
||||
"/local_amd64-apm": {
|
||||
"/local_amd64-store": {
|
||||
target: "https://erotica.spark-app.store",
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/local_amd64-apm/, ""),
|
||||
rewrite: (path) => path.replace(/^\/local_amd64-store/, ""),
|
||||
},
|
||||
"/local_stats": {
|
||||
target: "https://feedback.spark-app.store",
|
||||
@@ -92,10 +92,10 @@ export default defineConfig(({ command }) => {
|
||||
} else {
|
||||
return {
|
||||
proxy: {
|
||||
"/local_amd64-apm": {
|
||||
"/local_amd64-store": {
|
||||
target: "https://erotica.spark-app.store",
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/local_amd64-apm/, ""),
|
||||
rewrite: (path) => path.replace(/^\/local_amd64-store/, ""),
|
||||
},
|
||||
"/local_stats": {
|
||||
target: "https://feedback.spark-app.store",
|
||||
|
||||
Reference in New Issue
Block a user