fix: control UX — no default tool, launch loader, NPC layout

Deselect effect tools by default and on re-click, block the editor during session launch, and fix NPC overlay/window empty-state layout.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-07-25 14:41:27 +08:00
parent fed5674468
commit 9d82e74272
10 changed files with 88 additions and 57 deletions
+27 -6
View File
@@ -143,6 +143,7 @@ export function EditorApp() {
const [importReport, setImportReport] = useState<StorylineImportMergeReport | null>(null);
const [previewDialogSceneId, setPreviewDialogSceneId] = useState<SceneId | null>(null);
const [presentationOpen, setPresentationOpen] = useState(false);
const [launching, setLaunching] = useState(false);
const [licenseSnap, setLicenseSnap] = useState<LicenseSnapshot | null>(null);
const [checkUpdatesOpen, setCheckUpdatesOpen] = useState(false);
const [appPackaged, setAppPackaged] = useState(false);
@@ -351,15 +352,24 @@ export function EditorApp() {
const launchFromGraphNode = useCallback(
(graphNodeId: GraphNodeId) => {
if (!licenseActive) return;
if (!licenseActive || launching) return;
setLaunching(true);
void (async () => {
await getDndApi().invoke(ipcChannels.project.setCurrentGraphNode, { graphNodeId });
await getDndApi().invoke(ipcChannels.windows.openMultiWindow, {});
try {
await getDndApi().invoke(ipcChannels.project.setCurrentGraphNode, { graphNodeId });
await getDndApi().invoke(ipcChannels.windows.openMultiWindow, {});
} catch {
setLaunching(false);
}
})();
},
[licenseActive],
[licenseActive, launching],
);
useEffect(() => {
if (presentationOpen) setLaunching(false);
}, [presentationOpen]);
const currentProjectName = state.project?.meta.name ?? '';
const currentFileBaseName = state.project?.meta.fileBaseName ?? '';
const existingProjectNames = useMemo(() => state.projects.map((p) => p.name), [state.projects]);
@@ -646,6 +656,17 @@ export function EditorApp() {
return (
<>
{launching
? createPortal(
<div className={styles.progressOverlay} role="dialog" aria-label={t('top.launching')} aria-busy>
<div className={styles.previewBusyModal}>
<div className={styles.previewSpinner} aria-hidden />
<div className={styles.previewBusyText}>{t('top.launching')}</div>
</div>
</div>,
document.body,
)
: null}
{presentationOpen
? createPortal(
<div className={styles.editorLockOverlay} role="dialog" aria-label={t('presentation.overlay')}>
@@ -821,9 +842,9 @@ export function EditorApp() {
<>
<Button
variant="primary"
disabled={runDisabled}
disabled={runDisabled || launching}
onClick={() => {
if (!licenseActive || !graphStartGraphNodeId) return;
if (!licenseActive || !graphStartGraphNodeId || launching) return;
launchFromGraphNode(graphStartGraphNodeId);
}}
>