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_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
1
.vscode/launch.json
vendored
@@ -20,6 +20,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"configurations": [
|
"configurations": [
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "Debug Main Process",
|
"name": "Debug Main Process",
|
||||||
"type": "node",
|
"type": "node",
|
||||||
|
|||||||
58
AGENTS.md
58
AGENTS.md
@@ -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;
|
||||||
|
|
||||||
const url = new URL(deeplinkUrl);
|
try {
|
||||||
if (url.hostname === 'install') {
|
const url = new URL(deeplinkUrl);
|
||||||
const pkg = url.searchParams.get('pkg');
|
const action = url.hostname; // 'search'
|
||||||
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;
|
||||||
|
}
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -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}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user