feat: multi-material overlays, presentation guides, and release prebuilds
Align control/presentation with presentation screen rect and darkness z-order; sync window titles and session window cleanup. Pack Win/Mac/Linux with npmRebuild disabled and release-native-prep for classic-level and sharp. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { ipcChannels, type SessionState } from '../../shared/ipc/contracts';
|
||||
import { buildNpcGroupForest, type NpcGroupTreeNode } from '../../shared/npcs/npcGroups';
|
||||
import type { NpcGroupId, NpcId, ProjectNpc } from '../../shared/types';
|
||||
import matStyles from '../editor/MaterialsModals.module.css';
|
||||
import { useEditorI18n } from '../editor/i18n/EditorI18nContext';
|
||||
import { sanitizeSceneDescriptionHtml } from '../editor/sceneDescriptionHtml';
|
||||
import { getDndApi } from '../shared/dndApi';
|
||||
@@ -12,6 +13,32 @@ import { useAssetUrl } from '../shared/useAssetImageUrl';
|
||||
|
||||
import styles from './NpcsApp.module.css';
|
||||
|
||||
function ZoomInIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" aria-hidden>
|
||||
<circle cx="10.5" cy="10.5" r="6.25" fill="none" stroke="currentColor" strokeWidth="1.8" />
|
||||
<path d="M15.2 15.2 20 20" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
|
||||
<path
|
||||
d="M10.5 7.8v5.4M7.8 10.5h5.4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.8"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function ZoomOutIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" aria-hidden>
|
||||
<circle cx="10.5" cy="10.5" r="6.25" fill="none" stroke="currentColor" strokeWidth="1.8" />
|
||||
<path d="M15.2 15.2 20 20" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
|
||||
<path d="M7.8 10.5h5.4" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
/** Убрать пустые ветки групп (удобно при поиске). */
|
||||
function pruneEmptyGroupNodes(nodes: NpcGroupTreeNode[]): NpcGroupTreeNode[] {
|
||||
const out: NpcGroupTreeNode[] = [];
|
||||
@@ -137,12 +164,18 @@ export function NpcsApp() {
|
||||
void overlayApi.dispatch({ kind: 'hide' });
|
||||
return;
|
||||
}
|
||||
if (overlay?.zoomTool) {
|
||||
void overlayApi.dispatch({ kind: 'zoomTool.set', tool: null });
|
||||
return;
|
||||
}
|
||||
window.close();
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', onKey);
|
||||
return () => window.removeEventListener('keydown', onKey);
|
||||
}, [hasActive, overlayApi]);
|
||||
}, [hasActive, overlay?.zoomTool, overlayApi]);
|
||||
|
||||
const zoomTool = overlay?.zoomTool ?? null;
|
||||
|
||||
const npcs = useMemo(() => session?.project?.npcs ?? [], [session?.project?.npcs]);
|
||||
const npcGroups = useMemo(() => session?.project?.npcGroups ?? [], [session?.project?.npcGroups]);
|
||||
@@ -215,6 +248,45 @@ export function NpcsApp() {
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<div className={styles.toolbar}>
|
||||
<div className={matStyles.browserToolbarRow}>
|
||||
<Button
|
||||
variant={zoomTool === 'zoomIn' ? 'primary' : 'ghost'}
|
||||
iconOnly
|
||||
title={t('npcs.zoomIn')}
|
||||
ariaLabel={t('npcs.zoomIn')}
|
||||
tooltipPlacement="bottom"
|
||||
onClick={() => {
|
||||
void overlayApi.dispatch({
|
||||
kind: 'zoomTool.set',
|
||||
tool: zoomTool === 'zoomIn' ? null : 'zoomIn',
|
||||
});
|
||||
}}
|
||||
>
|
||||
<ZoomInIcon />
|
||||
</Button>
|
||||
<Button
|
||||
variant={zoomTool === 'zoomOut' ? 'primary' : 'ghost'}
|
||||
iconOnly
|
||||
title={t('npcs.zoomOut')}
|
||||
ariaLabel={t('npcs.zoomOut')}
|
||||
tooltipPlacement="bottom"
|
||||
onClick={() => {
|
||||
void overlayApi.dispatch({
|
||||
kind: 'zoomTool.set',
|
||||
tool: zoomTool === 'zoomOut' ? null : 'zoomOut',
|
||||
});
|
||||
}}
|
||||
>
|
||||
<ZoomOutIcon />
|
||||
</Button>
|
||||
</div>
|
||||
<div className={matStyles.browserToolbarHint}>
|
||||
{zoomTool === 'zoomIn'
|
||||
? t('npcs.zoomInHint')
|
||||
: zoomTool === 'zoomOut'
|
||||
? t('npcs.zoomOutHint')
|
||||
: t('npcs.zoomIdleHint')}
|
||||
</div>
|
||||
<div className={styles.toolbarRow}>
|
||||
<Button
|
||||
title={t('npcs.closeOverlay')}
|
||||
|
||||
Reference in New Issue
Block a user