fix(ui): hide Electron menu bar and polish editor chrome
Drop the system View menu across windows, show the open project name above Game properties, and keep scene list kebab visible with truncated titles and overflow tooltips. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+2
-25
@@ -143,32 +143,9 @@ if (!gotTheLock) {
|
|||||||
|
|
||||||
const projectStore = new ZipProjectStore();
|
const projectStore = new ZipProjectStore();
|
||||||
|
|
||||||
/** Без меню Electron не вешает горячие клавиши DevTools (Ctrl+Shift+I / F12). */
|
/** Системная полоса меню Electron отключена во всех режимах (в т.ч. без пункта «Вид»). */
|
||||||
function wantsDevToolsMenu(): boolean {
|
|
||||||
return (
|
|
||||||
process.env.NODE_ENV === 'development' ||
|
|
||||||
Boolean(process.env.VITE_DEV_SERVER_URL) ||
|
|
||||||
process.env.DND_OPEN_DEVTOOLS === '1'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function installAppMenuForSession(): void {
|
function installAppMenuForSession(): void {
|
||||||
if (!wantsDevToolsMenu()) {
|
Menu.setApplicationMenu(null);
|
||||||
Menu.setApplicationMenu(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const template: Electron.MenuItemConstructorOptions[] = [];
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
template.push({
|
|
||||||
label: app.name,
|
|
||||||
submenu: [{ role: 'about' }, { type: 'separator' }, { role: 'quit' }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
template.push({
|
|
||||||
label: 'Вид',
|
|
||||||
submenu: [{ role: 'reload' }, { role: 'forceReload' }, { type: 'separator' }, { role: 'toggleDevTools' }],
|
|
||||||
});
|
|
||||||
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const effectsStore = new EffectsStore();
|
const effectsStore = new EffectsStore();
|
||||||
|
|||||||
@@ -208,11 +208,11 @@ function createWindow(kind: WindowKind, opts?: CreateWindowOpts): BrowserWindow
|
|||||||
const win = new BrowserWindow({
|
const win = new BrowserWindow({
|
||||||
width: size.width,
|
width: size.width,
|
||||||
height: size.height,
|
height: size.height,
|
||||||
|
autoHideMenuBar: true,
|
||||||
...(kind === 'sceneDescription'
|
...(kind === 'sceneDescription'
|
||||||
? {
|
? {
|
||||||
minWidth: 520,
|
minWidth: 520,
|
||||||
minHeight: 420,
|
minHeight: 420,
|
||||||
autoHideMenuBar: true,
|
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...(kind === 'materials'
|
...(kind === 'materials'
|
||||||
@@ -222,7 +222,6 @@ function createWindow(kind: WindowKind, opts?: CreateWindowOpts): BrowserWindow
|
|||||||
minWidth: 260,
|
minWidth: 260,
|
||||||
maxWidth: 360,
|
maxWidth: 360,
|
||||||
minHeight: 480,
|
minHeight: 480,
|
||||||
autoHideMenuBar: true,
|
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...(kind === 'npcsEditor'
|
...(kind === 'npcsEditor'
|
||||||
@@ -231,7 +230,6 @@ function createWindow(kind: WindowKind, opts?: CreateWindowOpts): BrowserWindow
|
|||||||
height: NPCS_EDITOR_WINDOW_HEIGHT,
|
height: NPCS_EDITOR_WINDOW_HEIGHT,
|
||||||
minWidth: 1100,
|
minWidth: 1100,
|
||||||
minHeight: 640,
|
minHeight: 640,
|
||||||
autoHideMenuBar: true,
|
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...(kind === 'sceneEditor'
|
...(kind === 'sceneEditor'
|
||||||
@@ -240,7 +238,6 @@ function createWindow(kind: WindowKind, opts?: CreateWindowOpts): BrowserWindow
|
|||||||
height: 800,
|
height: 800,
|
||||||
minWidth: 960,
|
minWidth: 960,
|
||||||
minHeight: 600,
|
minHeight: 600,
|
||||||
autoHideMenuBar: true,
|
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...(kind === 'npcs'
|
...(kind === 'npcs'
|
||||||
@@ -250,7 +247,6 @@ function createWindow(kind: WindowKind, opts?: CreateWindowOpts): BrowserWindow
|
|||||||
minWidth: 560,
|
minWidth: 560,
|
||||||
maxWidth: 900,
|
maxWidth: 900,
|
||||||
minHeight: 480,
|
minHeight: 480,
|
||||||
autoHideMenuBar: true,
|
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
show: false,
|
show: false,
|
||||||
|
|||||||
@@ -265,6 +265,18 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.projectNameLabel {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 13px;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: var(--text1);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.inspectorScroll {
|
.inspectorScroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
@@ -1076,6 +1088,7 @@
|
|||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sceneCard:not(.sceneCardActive):hover {
|
.sceneCard:not(.sceneCardActive):hover {
|
||||||
@@ -1163,21 +1176,23 @@
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sceneCardHeader {
|
.sceneCardHeader {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badgeCurrent {
|
.badgeCurrent {
|
||||||
font-size: var(--text-xs);
|
font-size: var(--text-xs);
|
||||||
color: var(--accent2);
|
color: var(--accent2);
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sceneMenuBtn {
|
.sceneMenuBtn {
|
||||||
margin-left: auto;
|
|
||||||
border: none;
|
border: none;
|
||||||
background: var(--panel2);
|
background: var(--panel2);
|
||||||
border-radius: var(--radius-xs);
|
border-radius: var(--radius-xs);
|
||||||
@@ -1191,6 +1206,11 @@
|
|||||||
|
|
||||||
.sceneCardTitle {
|
.sceneCardTitle {
|
||||||
font-weight: 750;
|
font-weight: 750;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menuBackdrop {
|
.menuBackdrop {
|
||||||
|
|||||||
@@ -1018,6 +1018,9 @@ export function EditorApp() {
|
|||||||
<div className={styles.inspectorScroll}>
|
<div className={styles.inspectorScroll}>
|
||||||
{state.project ? (
|
{state.project ? (
|
||||||
<>
|
<>
|
||||||
|
<div className={styles.projectNameLabel} title={state.project.meta.name}>
|
||||||
|
{t('scenes.projectLabel', { name: state.project.meta.name })}
|
||||||
|
</div>
|
||||||
<div className={styles.inspectorTitle}>{t('scenes.inspectorGame')}</div>
|
<div className={styles.inspectorTitle}>{t('scenes.inspectorGame')}</div>
|
||||||
<CampaignInspector
|
<CampaignInspector
|
||||||
audioRefs={campaignAudioRefs}
|
audioRefs={campaignAudioRefs}
|
||||||
@@ -2759,6 +2762,8 @@ function SceneListCard({
|
|||||||
const previewUrl = useAssetUrl(scene.previewAssetId);
|
const previewUrl = useAssetUrl(scene.previewAssetId);
|
||||||
const [menu, setMenu] = useState<{ x: number; y: number } | null>(null);
|
const [menu, setMenu] = useState<{ x: number; y: number } | null>(null);
|
||||||
const [pendingDelete, setPendingDelete] = useState(false);
|
const [pendingDelete, setPendingDelete] = useState(false);
|
||||||
|
const titleRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const [titleTooltip, setTitleTooltip] = useState<string | undefined>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!menu) return;
|
if (!menu) return;
|
||||||
@@ -2875,6 +2880,22 @@ function SceneListCard({
|
|||||||
<div className={styles.sceneCardBody}>
|
<div className={styles.sceneCardBody}>
|
||||||
<div className={styles.sceneCardHeader}>
|
<div className={styles.sceneCardHeader}>
|
||||||
{scene.active ? <div className={styles.badgeCurrent}>{t('sceneCard.current')}</div> : null}
|
{scene.active ? <div className={styles.badgeCurrent}>{t('sceneCard.current')}</div> : null}
|
||||||
|
<div
|
||||||
|
ref={titleRef}
|
||||||
|
className={styles.sceneCardTitle}
|
||||||
|
title={titleTooltip}
|
||||||
|
onMouseEnter={() => {
|
||||||
|
const el = titleRef.current;
|
||||||
|
if (!el) {
|
||||||
|
setTitleTooltip(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTitleTooltip(el.scrollWidth > el.clientWidth + 1 ? scene.title : undefined);
|
||||||
|
}}
|
||||||
|
onMouseLeave={() => setTitleTooltip(undefined)}
|
||||||
|
>
|
||||||
|
{scene.title}
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label={t('sceneCard.menu')}
|
aria-label={t('sceneCard.menu')}
|
||||||
@@ -2888,7 +2909,6 @@ function SceneListCard({
|
|||||||
⋮
|
⋮
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.sceneCardTitle}>{scene.title}</div>
|
|
||||||
</div>
|
</div>
|
||||||
{menu && menuPos
|
{menu && menuPos
|
||||||
? createPortal(
|
? createPortal(
|
||||||
|
|||||||
@@ -282,6 +282,7 @@ export const EDITOR_MESSAGES: Record<EditorLocale, Record<string, string>> = {
|
|||||||
'scenes.dropSkippedNoPath': 'не удалось получить путь к файлу',
|
'scenes.dropSkippedNoPath': 'не удалось получить путь к файлу',
|
||||||
'scenes.inspectorGame': 'Свойства игры',
|
'scenes.inspectorGame': 'Свойства игры',
|
||||||
'scenes.inspectorScene': 'Свойства сцены',
|
'scenes.inspectorScene': 'Свойства сцены',
|
||||||
|
'scenes.projectLabel': 'Проект: {name}',
|
||||||
'scenes.selectHint': 'Выберите сцену слева, чтобы редактировать её свойства.',
|
'scenes.selectHint': 'Выберите сцену слева, чтобы редактировать её свойства.',
|
||||||
'scenes.openProjectHint': 'Откройте проект, чтобы редактировать кампанию и сцены.',
|
'scenes.openProjectHint': 'Откройте проект, чтобы редактировать кампанию и сцены.',
|
||||||
|
|
||||||
@@ -851,6 +852,7 @@ export const EDITOR_MESSAGES: Record<EditorLocale, Record<string, string>> = {
|
|||||||
'scenes.dropSkippedNoPath': 'could not resolve file path',
|
'scenes.dropSkippedNoPath': 'could not resolve file path',
|
||||||
'scenes.inspectorGame': 'Game properties',
|
'scenes.inspectorGame': 'Game properties',
|
||||||
'scenes.inspectorScene': 'Scene properties',
|
'scenes.inspectorScene': 'Scene properties',
|
||||||
|
'scenes.projectLabel': 'Project: {name}',
|
||||||
'scenes.selectHint': 'Select a scene on the left to edit its properties.',
|
'scenes.selectHint': 'Select a scene on the left to edit its properties.',
|
||||||
'scenes.openProjectHint': 'Open a project to edit the campaign and scenes.',
|
'scenes.openProjectHint': 'Open a project to edit the campaign and scenes.',
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user