合并原生壳桥接统一分支

合并 Expo 移动壳与 Tauri 桌面壳工程
保留创作主页、项目入口和现有生产发布脚本
同步 HostBridge 文档、依赖锁文件和原生壳门禁
This commit is contained in:
2026-06-22 22:28:31 +08:00
263 changed files with 72457 additions and 2263 deletions
@@ -116,6 +116,7 @@ import type {
} from '../../../packages/shared/src/contracts/visualNovel';
import type { WoodenFishWorkSummaryResponse } from '../../../packages/shared/src/contracts/woodenFish';
import { buildCustomWorldPlayableCharacters } from '../../data/characterPresets';
import { useHostNetworkOnline } from '../../hooks/useHostNetworkOnline';
import {
buildPublicWorkStagePath,
pushAppHistoryPath,
@@ -179,7 +180,6 @@ import {
streamCreativeAgentMessage,
streamCreativeDraftEdit,
} from '../../services/creative-agent';
import { getExternalGenerationQueueOverview } from '../../services/external-generation';
import {
readCustomWorldAgentUiState,
shouldRestoreCustomWorldAgentUiState,
@@ -193,6 +193,7 @@ import {
regenerateBabyObjectMatchDraftAssets,
saveBabyObjectMatchDraft,
} from '../../services/edutainment-baby-object';
import { getExternalGenerationQueueOverview } from '../../services/external-generation';
import {
jumpHopClient,
type JumpHopGalleryCardResponse,
@@ -227,7 +228,6 @@ import {
buildSquareHoleGenerationAnchorEntries,
buildWoodenFishGenerationAnchorEntries,
createMiniGameDraftGenerationState,
type MiniGameDraftGenerationKind,
type MiniGameDraftGenerationState,
resolveMiniGameDraftGenerationStartedAtMs,
} from '../../services/miniGameDraftGenerationProgress';
@@ -243,7 +243,6 @@ import {
buildSquareHolePublicWorkCode,
buildVisualNovelPublicWorkCode,
buildWoodenFishPublicWorkCode,
isSamePuzzleClearPublicWorkCode,
isSamePuzzlePublicWorkCode,
} from '../../services/publicWorkCode';
import {
@@ -320,6 +319,7 @@ import {
recordRpgEntryWorldGalleryPlay,
remixRpgEntryWorldGallery,
} from '../../services/rpg-entry/rpgEntryLibraryClient';
import { navigateHostNativePage } from '../../services/host-bridge/hostBridge';
import {
getRpgProfilePlayStats,
submitRpgProfileFeedback,
@@ -359,8 +359,8 @@ import {
publishVisualNovelWork,
updateVisualNovelWork,
} from '../../services/visual-novel-works';
import { requestGenerationResultSubscribePermission } from '../../services/wechatMiniProgramSubscribe';
import { postWechatMiniProgramShareTarget } from '../../services/wechatMiniProgramShareTarget';
import { requestGenerationResultSubscribePermission } from '../../services/wechatMiniProgramSubscribe';
import {
woodenFishClient,
type WoodenFishGalleryCardResponse,
@@ -376,7 +376,6 @@ import { PlatformAcknowledgeStatusDialog } from '../common/PlatformAcknowledgeSt
import { PlatformActionButton } from '../common/PlatformActionButton';
import { PlatformDangerConfirmDialog } from '../common/PlatformDangerConfirmDialog';
import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
import { PlatformSubpanel } from '../common/PlatformSubpanel';
import { PublishShareModal } from '../common/PublishShareModal';
import type { PublishShareModalPayload } from '../common/publishShareModalModel';
@@ -396,7 +395,6 @@ import {
isBigFishGalleryEntry,
isEdutainmentGalleryEntry,
isJumpHopGalleryEntry,
isPuzzleClearGalleryEntry,
isPuzzleGalleryEntry,
mapPuzzleClearWorkToPlatformGalleryCard,
type PlatformPublicGalleryCard,
@@ -418,7 +416,7 @@ import {
buildVisualNovelEntryGenerationProgress,
type VisualNovelEntryFormPayload,
} from '../visual-novel-creation/visualNovelEntryGeneration';
import { createMockVisualNovelRunFromDraft } from '../visual-novel-runtime/visualNovelMockData';
import { createLocalPreviewVisualNovelRunFromDraft } from '../visual-novel-runtime/visualNovelLocalPreviewData';
import {
buildBarkBattleDraftConfigFromWorkSummary,
buildBarkBattlePublishedConfigFromDraft,
@@ -462,7 +460,6 @@ import {
resolveWoodenFishCreationUrlRestoreStage,
} from './platformCreationUrlStateModel';
import { resolvePlatformCreationWorkDeleteConfirmationModel } from './platformCreationWorkDeleteFlow';
import { buildExternalGenerationQueueStatus } from './platformExternalGenerationQueueStatusModel';
import {
buildPlatformErrorDialogDismissKey,
buildPlatformTaskCompletionDialogDismissKey,
@@ -477,6 +474,10 @@ import {
resolveActivePlatformDialog,
resolvePlatformErrorDialog,
} from './platformDialogStateModel';
import {
type DraftGenerationPointNotice,
PlatformDraftGenerationPointNoticeDialog,
} from './PlatformDraftGenerationPointNoticeDialog';
import {
buildCreationWorkShelfRuntimeState,
buildDraftCompletionDialogSource,
@@ -484,13 +485,13 @@ import {
buildPendingBigFishWorks,
buildPendingJumpHopWorks,
buildPendingMatch3DWorks,
buildPendingPuzzleClearWorks,
buildPendingPuzzleWorks,
buildPendingSquareHoleWorks,
buildPendingVisualNovelWorks,
buildPendingWoodenFishWorks,
collectDraftNoticeKeys,
collectVisibleDraftNoticeKeys,
countUnreadDraftGenerationUpdates,
createPendingDraftShelfState,
type DraftGenerationNoticeMap,
type DraftGenerationNoticeStatus,
@@ -515,10 +516,6 @@ import {
canExposePublicWork,
EDUTAINMENT_HIDDEN_MESSAGE,
} from './platformEdutainmentVisibility';
import {
PlatformDraftGenerationPointNoticeDialog,
type DraftGenerationPointNotice,
} from './PlatformDraftGenerationPointNoticeDialog';
import { PlatformEntryCreationTypeModal } from './PlatformEntryCreationTypeModal';
import type { PlatformCreationTypeId } from './platformEntryCreationTypes';
import {
@@ -548,8 +545,20 @@ import type {
} from './platformEntryTypes';
import { PlatformEntryWorldDetailView } from './PlatformEntryWorldDetailView';
import { PlatformErrorDialog } from './PlatformErrorDialog';
import { buildExternalGenerationQueueStatus } from './platformExternalGenerationQueueStatusModel';
import { PlatformFeedbackView } from './PlatformFeedbackView';
import { resolvePlatformGenerationProgressTickDecision } from './platformGenerationProgressTickModel';
import {
buildPlatformHostDraftFailureNotification,
buildPlatformHostDraftNotificationResetKeys,
buildPlatformHostDraftReadyNotification,
type PlatformHostDraftNotification,
sendPlatformHostDraftNotificationOnce,
} from './platformHostNotificationModel';
import {
sendPlatformHostDraftNotificationToHost,
syncPlatformHostDraftBadgeCountToHost,
} from './platformHostBridgeSync';
import {
buildMatch3DProfileFromSession,
hasMatch3DRuntimeAsset,
@@ -575,6 +584,7 @@ import {
rebaseMiniGameDraftBackgroundCompileTaskForDisplay,
rebaseMiniGameDraftGenerationStateForDisplay,
resolveFinishedMiniGameDraftGenerationState,
resolveMiniGameGenerationViewBusy,
} from './platformMiniGameDraftGenerationStateModel';
import {
buildJumpHopDraftActionPayload,
@@ -660,7 +670,6 @@ import {
buildPuzzleResultWorkId,
} from './platformPuzzleIdentityModel';
import { mergePuzzleServiceRuntimeState } from './platformPuzzleRuntimeStateModel';
import { buildPlatformRecommendedEntries } from './platformRecommendation';
import {
type PlatformPuzzleRuntimeAuthMode,
resolvePlatformRecommendRuntimeAuthPlan,
@@ -696,38 +705,6 @@ type PuzzleBackgroundCompileTask = {
error: string | null;
};
type MiniGameGenerationProgressTickStateMap = Partial<
Record<MiniGameDraftGenerationKind, MiniGameDraftGenerationState | null>
>;
export function resolveMiniGameGenerationProgressTickState(
selectionStage: SelectionStage,
states: MiniGameGenerationProgressTickStateMap,
) {
const stageKindMap: Partial<
Record<SelectionStage, MiniGameDraftGenerationKind>
> = {
'puzzle-generating': 'puzzle',
'big-fish-generating': 'big-fish',
'square-hole-generating': 'square-hole',
'match3d-generating': 'match3d',
'baby-object-match-generating': 'baby-object-match',
'jump-hop-generating': 'jump-hop',
'puzzle-clear-generating': 'puzzle-clear',
'wooden-fish-generating': 'wooden-fish',
};
const kind = stageKindMap[selectionStage];
return kind ? (states[kind] ?? null) : null;
}
export function resolveMiniGameGenerationViewBusy(
isBusy: boolean,
state: MiniGameDraftGenerationState | null | undefined,
) {
return isBusy || isMiniGameDraftGenerating(state ?? null);
}
function isExternalGenerationQueueStage(selectionStage: SelectionStage) {
return (
selectionStage === 'puzzle-generating' ||
@@ -941,7 +918,6 @@ const PUZZLE_DRAFT_GENERATION_POINT_COST = 2;
const PUZZLE_BACKGROUND_ACTION_POLL_INTERVAL_MS = 3000;
const PUZZLE_BACKGROUND_ACTION_MAX_POLL_ATTEMPTS = 160;
const MATCH3D_DRAFT_GENERATION_POINT_COST = 10;
const BARK_BATTLE_DRAFT_GENERATION_POINT_COST = 3;
function isPuzzleBackgroundAction(payload: PuzzleAgentActionRequest) {
return (
@@ -2117,6 +2093,9 @@ export function PlatformEntryFlowShellImpl({
const profileWalletLocalDeltaRef = useRef(0);
const lastProfileDashboardSnapshotRef =
useRef<ProfileDashboardSummary | null>(null);
const sentPlatformHostDraftNotificationKeysRef = useRef<Set<string>>(
new Set(),
);
const [
pendingPlatformTaskCompletionDialog,
setPendingPlatformTaskCompletionDialog,
@@ -2133,6 +2112,34 @@ export function PlatformEntryFlowShellImpl({
);
const handledPuzzleRuntimeUrlStateKeyRef = useRef<string | null>(null);
const sendPlatformHostDraftNotification = useCallback(
(notification: PlatformHostDraftNotification | null) => {
if (!notification) {
return;
}
sendPlatformHostDraftNotificationOnce(
sentPlatformHostDraftNotificationKeysRef.current,
notification,
sendPlatformHostDraftNotificationToHost,
);
},
[],
);
const resetPlatformHostDraftNotifications = useCallback(
(kind: CreationWorkShelfKind, ids: Array<string | null | undefined>) => {
const sentKeys = sentPlatformHostDraftNotificationKeysRef.current;
for (const key of buildPlatformHostDraftNotificationResetKeys(
kind,
ids,
)) {
sentKeys.delete(key);
}
},
[],
);
useEffect(() => {
selectionStageRef.current = selectionStage;
}, [selectionStage]);
@@ -2263,12 +2270,14 @@ export function PlatformEntryFlowShellImpl({
(kind: CreationWorkShelfKind, ids: Array<string | null | undefined>) => {
setPendingPlatformTaskCompletionDialog(null);
setPendingPlatformTaskFailureDialog(null);
resetPlatformHostDraftNotifications(kind, ids);
updateDraftGenerationNotices(
collectDraftNoticeKeys(kind, ids),
'generating',
);
},
[
resetPlatformHostDraftNotifications,
setPendingPlatformTaskCompletionDialog,
setPendingPlatformTaskFailureDialog,
updateDraftGenerationNotices,
@@ -2288,6 +2297,9 @@ export function PlatformEntryFlowShellImpl({
);
setProfileTaskRefreshKey((current) => current + 1);
const completedAtMs = Date.now();
sendPlatformHostDraftNotification(
buildPlatformHostDraftReadyNotification(kind, ids),
);
setPendingPlatformTaskCompletionDialog({
key: `${kind}:${collectDraftNoticeKeys(kind, ids).join('|')}:${completedAtMs}`,
source: buildDraftCompletionDialogSource(kind, ids),
@@ -2296,6 +2308,7 @@ export function PlatformEntryFlowShellImpl({
});
},
[
sendPlatformHostDraftNotification,
setPendingPlatformTaskCompletionDialog,
setPendingPlatformTaskFailureDialog,
updateDraftGenerationNotices,
@@ -2314,6 +2327,13 @@ export function PlatformEntryFlowShellImpl({
const normalizedErrorMessage = errorMessage?.trim();
if (normalizedErrorMessage && showFailureDialog) {
const failedAtMs = Date.now();
sendPlatformHostDraftNotification(
buildPlatformHostDraftFailureNotification(
kind,
ids,
normalizedErrorMessage,
),
);
setPendingPlatformTaskFailureDialog({
key: `draft-failure:${kind}:${noticeKeys.join('|')}:${failedAtMs}`,
source: buildDraftCompletionDialogSource(kind, ids),
@@ -2323,6 +2343,7 @@ export function PlatformEntryFlowShellImpl({
}
},
[
sendPlatformHostDraftNotification,
setPendingPlatformTaskCompletionDialog,
setPendingPlatformTaskFailureDialog,
updateDraftGenerationNotices,
@@ -3296,16 +3317,6 @@ export function PlatformEntryFlowShellImpl({
],
[jumpHopWorks, pendingDraftShelfItems],
);
const puzzleClearShelfItems = useMemo(
() => [
...buildPendingPuzzleClearWorks(
pendingDraftShelfItems['puzzle-clear'],
puzzleClearWorks,
),
...puzzleClearWorks,
],
[pendingDraftShelfItems, puzzleClearWorks],
);
const woodenFishShelfItems = useMemo(
() => [
...buildPendingWoodenFishWorks(
@@ -3372,28 +3383,24 @@ export function PlatformEntryFlowShellImpl({
}),
[draftGenerationNotices, pendingDraftShelfItems],
);
const visibleDraftNoticeKeys = useMemo(
() =>
collectVisibleDraftNoticeKeys({
rpgItems: creationHubItems,
bigFishItems: bigFishShelfItems,
jumpHopItems: jumpHopShelfItems,
woodenFishItems: woodenFishShelfItems,
match3dItems: match3dShelfItems,
squareHoleItems: isSquareHoleCreationVisible
? squareHoleShelfItems
: [],
puzzleItems: puzzleShelfItems,
visualNovelItems: visualNovelShelfItems,
barkBattleItems: barkBattleShelfItems,
babyObjectMatchItems: babyObjectMatchDrafts,
}),
const visibleDraftNoticeSources = useMemo(
() => ({
rpgItems: creationHubItems,
bigFishItems: bigFishShelfItems,
jumpHopItems: jumpHopShelfItems,
woodenFishItems: woodenFishShelfItems,
match3dItems: match3dShelfItems,
squareHoleItems: isSquareHoleCreationVisible ? squareHoleShelfItems : [],
puzzleItems: puzzleShelfItems,
visualNovelItems: visualNovelShelfItems,
barkBattleItems: barkBattleShelfItems,
babyObjectMatchItems: babyObjectMatchDrafts,
}),
[
babyObjectMatchDrafts,
barkBattleShelfItems,
bigFishShelfItems,
jumpHopShelfItems,
puzzleClearShelfItems,
woodenFishShelfItems,
creationHubItems,
isSquareHoleCreationVisible,
@@ -3403,6 +3410,18 @@ export function PlatformEntryFlowShellImpl({
visualNovelShelfItems,
],
);
const visibleDraftNoticeKeys = useMemo(
() => collectVisibleDraftNoticeKeys(visibleDraftNoticeSources),
[visibleDraftNoticeSources],
);
const unreadDraftGenerationUpdateCount = useMemo(
() =>
countUnreadDraftGenerationUpdates(
draftGenerationNotices,
visibleDraftNoticeSources,
),
[draftGenerationNotices, visibleDraftNoticeSources],
);
const hasUnreadDraftUpdates = useMemo(
() =>
hasUnreadDraftGenerationUpdates(
@@ -3411,6 +3430,9 @@ export function PlatformEntryFlowShellImpl({
),
[draftGenerationNotices, visibleDraftNoticeKeys],
);
useEffect(() => {
syncPlatformHostDraftBadgeCountToHost(unreadDraftGenerationUpdateCount);
}, [unreadDraftGenerationUpdateCount]);
const resultViewError =
autosaveCoordinator.customWorldAutoSaveError ??
sessionController.customWorldError;
@@ -4896,14 +4918,24 @@ export function PlatformEntryFlowShellImpl({
(selectionStage === 'platform' &&
platformBootstrap.platformTab === 'profile' &&
platformBootstrap.canReadProtectedData);
const isHostNetworkOnline = useHostNetworkOnline();
const externalGenerationQueueOwnerKey =
shouldShowExternalGenerationQueueStatus
? (authUi?.user?.id ?? 'anonymous')
: null;
useEffect(() => {
if (!shouldShowExternalGenerationQueueStatus) {
setExternalGenerationQueueOverview(null);
setExternalGenerationQueueOverview(null);
}, [externalGenerationQueueOwnerKey]);
useEffect(() => {
if (
!shouldShowExternalGenerationQueueStatus ||
!isHostNetworkOnline
) {
return;
}
setExternalGenerationQueueOverview(null);
let disposed = false;
let controller: AbortController | null = null;
@@ -4931,7 +4963,11 @@ export function PlatformEntryFlowShellImpl({
controller?.abort();
window.clearInterval(intervalId);
};
}, [authUi?.user?.id, shouldShowExternalGenerationQueueStatus]);
}, [
externalGenerationQueueOwnerKey,
isHostNetworkOnline,
shouldShowExternalGenerationQueueStatus,
]);
const activeExternalGenerationJobState = useMemo(() => {
const candidates = [
puzzleOperation?.queueState ?? null,
@@ -7169,7 +7205,12 @@ export function PlatformEntryFlowShellImpl({
}, [setSelectionStage]);
const startChildMotionDemo = useCallback(() => {
window.location.assign('/child-motion-demo');
void (async () => {
const navigated = await navigateHostNativePage('/child-motion-demo');
if (!navigated) {
window.location.assign('/child-motion-demo');
}
})();
}, []);
const resolveBabyObjectMatchRuntimeDraft = useCallback(
@@ -7349,7 +7390,7 @@ export function PlatformEntryFlowShellImpl({
const profileId =
visualNovelWork?.summary.profileId?.trim() ?? draft.profileId?.trim();
if (!profileId) {
const run = createMockVisualNovelRunFromDraft(draft);
const run = createLocalPreviewVisualNovelRunFromDraft(draft);
setVisualNovelRun(run);
setVisualNovelRuntimeReturnStage('visual-novel-result');
setSelectionStage('visual-novel-runtime');
@@ -7369,7 +7410,7 @@ export function PlatformEntryFlowShellImpl({
} catch (error) {
// 中文注释:VN-07 只要求结果页进入测试运行时;真实 runtime 接口未就绪时,
// 使用当前 draft 生成本地 test run,不扩展到正式玩家运行链路。
setVisualNovelRun(createMockVisualNovelRunFromDraft(draft));
setVisualNovelRun(createLocalPreviewVisualNovelRunFromDraft(draft));
setVisualNovelRuntimeReturnStage('visual-novel-result');
setSelectionStage('visual-novel-runtime');
setVisualNovelError(
@@ -14518,25 +14559,6 @@ export function PlatformEntryFlowShellImpl({
openPublicWorkDetail(matchedEntry.detail);
};
const tryOpenPuzzleClearGalleryEntry = async () => {
const entries =
puzzleClearGalleryEntries.length > 0
? puzzleClearGalleryEntries
: await refreshPuzzleClearGallery();
const matchedEntry = entries.find((entry) => {
const detailEntry = mapPuzzleClearWorkToPlatformGalleryCard(entry);
return (
canExposePublicWork(detailEntry) &&
isSamePuzzleClearPublicWorkCode(normalizedKeyword, entry.profileId)
);
});
if (!matchedEntry) {
throw new Error('未找到拼消消作品。');
}
await openPuzzleClearPublicWorkDetail(matchedEntry.profileId);
};
const tryOpenMatch3DGalleryEntry = async () => {
const entries =
match3dGalleryEntries.length > 0