接入原生壳分享卡图片导出

新增 file.exportImage 宿主能力契约

分享卡下载在原生壳中优先走宿主图片导出

Expo 壳写入缓存图片并调用系统分享保存

Tauri 壳通过保存对话框写入图片字节

补齐能力漂移检查、测试和架构文档
This commit is contained in:
2026-06-18 01:31:28 +08:00
parent 6843185a6c
commit 910625d5e1
17 changed files with 673 additions and 34 deletions

View File

@@ -6,6 +6,7 @@ import type { HostBridgeCapability } from '../../../packages/shared/src/contract
import {
canUseHostShareGrid,
canUseNativeHostCapability,
exportHostImageFile,
exportHostTextFile,
getHostRuntime,
getNativeAppHostRuntime,
@@ -443,6 +444,7 @@ describe('hostBridge', () => {
'app.openExternalUrl',
'app.setTitle',
'share.open',
'file.exportImage',
]),
);
window.__TAURI__ = {
@@ -482,6 +484,13 @@ describe('hostBridge', () => {
url: 'https://app.genarrative.world/works/detail?work=PZ-1',
}),
).resolves.toBe(true);
await expect(
exportHostImageFile({
fileName: '分享卡.png',
base64Data: 'c2hhcmUtY2FyZA==',
mimeType: 'image/png',
}),
).resolves.toBe(true);
expect(invoke).toHaveBeenCalledWith('host_bridge_request', {
request: expect.objectContaining({
@@ -545,6 +554,17 @@ describe('hostBridge', () => {
},
}),
});
expect(invoke).toHaveBeenCalledWith('host_bridge_request', {
request: expect.objectContaining({
method: 'file.exportImage',
payload: {
fileName: '分享卡.png',
base64Data: 'c2hhcmUtY2FyZA==',
mimeType: 'image/png',
},
timeoutMs: 30000,
}),
});
});
test('原生 App 宿主不支持能力时回退到 H5 路径', async () => {
@@ -598,15 +618,29 @@ describe('hostBridge', () => {
url: 'https://app.genarrative.world/works/detail?work=PZ-1',
}),
).resolves.toBe(false);
await expect(
exportHostImageFile({
fileName: '分享卡.png',
base64Data: 'c2hhcmUtY2FyZA==',
mimeType: 'image/png',
}),
).resolves.toBe(false);
});
test('普通浏览器不处理宿主文导出', async () => {
test('普通浏览器不处理宿主文导出', async () => {
await expect(
exportHostTextFile({
fileName: '作品记录.txt',
content: 'content',
}),
).resolves.toBe(false);
await expect(
exportHostImageFile({
fileName: '分享卡.png',
base64Data: 'c2hhcmUtY2FyZA==',
mimeType: 'image/png',
}),
).resolves.toBe(false);
});
test('原生 App 宿主通过 HostBridge 导出文本文件', async () => {
@@ -694,4 +728,57 @@ describe('hostBridge', () => {
}),
).resolves.toBe(false);
});
test('原生 App 宿主通过 HostBridge 导出图片文件', async () => {
const invoke = vi.fn(
async (_command: string, args?: Record<string, unknown>) => {
const request = (args as { request: { id: string } }).request;
return {
bridge: 'GenarrativeHostBridge',
version: 1,
id: request.id,
ok: true,
result: {
action: 'saved',
fileName: '分享卡.png',
bytes: 10,
},
};
},
);
window.history.replaceState(
null,
'',
nativeAppPath(['file.exportImage']),
);
window.__TAURI__ = {
core: {
invoke: asTauriInvoke(invoke),
},
};
await expect(
exportHostImageFile({
fileName: '分享卡.png',
base64Data: 'c2hhcmUtY2FyZA==',
mimeType: 'image/png',
}),
).resolves.toEqual({
action: 'saved',
fileName: '分享卡.png',
bytes: 10,
});
expect(invoke).toHaveBeenCalledWith('host_bridge_request', {
request: expect.objectContaining({
method: 'file.exportImage',
payload: {
fileName: '分享卡.png',
base64Data: 'c2hhcmUtY2FyZA==',
mimeType: 'image/png',
},
timeoutMs: 30000,
}),
});
});
});