diff --git a/app/renderer/editor/EditorApp.module.css b/app/renderer/editor/EditorApp.module.css index d966fc8..ac830cf 100644 --- a/app/renderer/editor/EditorApp.module.css +++ b/app/renderer/editor/EditorApp.module.css @@ -62,8 +62,9 @@ .editorSidebar { height: 100%; min-height: 0; - display: grid; - grid-template-rows: auto 1fr; + display: flex; + flex-direction: column; + gap: 0; padding: 12px; border-right: 1px solid var(--stroke); background: var(--editor-column-bg); @@ -105,10 +106,25 @@ .gridTools { display: grid; gap: 10px; + flex: 0 0 auto; +} + +.gridTools > * { + width: 100%; + max-width: 100%; + display: flex; + box-sizing: border-box; +} + +.gridTools button { + width: 100%; + box-sizing: border-box; + flex: 1; } .spacer14 { height: 14px; + flex: 0 0 auto; } .spacer18 { @@ -124,17 +140,28 @@ .gamePropsButtons > * { width: 100%; min-width: 0; + max-width: 100%; + display: flex; + box-sizing: border-box; +} + +.gamePropsButtons button { + width: 100%; + box-sizing: border-box; + flex: 1; } .sidebarScroll { overflow: auto; padding-right: 2px; min-height: 0; + flex: 1 1 auto; align-self: stretch; } .sceneListGrid { display: grid; + grid-template-columns: minmax(0, 1fr); gap: 12px; align-content: start; justify-items: stretch; @@ -192,15 +219,19 @@ .progressModal { width: min(520px, calc(100vw - 32px)); border-radius: 12px; - padding: 14px; + padding: 16px 16px 20px; background: rgba(25, 28, 38, 0.92); border: 1px solid rgba(255, 255, 255, 0.12); box-shadow: 0 10px 40px rgba(0, 0, 0, 0.55); + display: grid; + gap: 12px; + justify-items: center; } .progressTitle { font-weight: 700; - margin-bottom: 10px; + width: 100%; + text-align: center; } .progressBar { @@ -209,6 +240,7 @@ overflow: hidden; background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.1); + width: 100%; } .progressFill { @@ -218,11 +250,13 @@ } .progressMeta { - margin-top: 10px; + margin-top: 0; display: flex; justify-content: space-between; + gap: 12px; opacity: 0.9; font-size: 12px; + width: 100%; } .inspectorTitle { @@ -610,6 +644,23 @@ gap: 10px; } +.projectPickerForm > * { + width: 100%; + max-width: 100%; + box-sizing: border-box; +} + +.projectPickerForm > *:has(button), +.projectPickerForm > span { + display: flex; +} + +.projectPickerForm button { + width: 100%; + box-sizing: border-box; + flex: 1; +} + .spacer6 { height: 6px; } @@ -918,6 +969,21 @@ background 0.15s ease; } +.audioDropEmpty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 10px; + min-height: 96px; + text-align: center; + padding: 8px 4px; +} + +.audioDropEmpty > * { + max-width: 100%; +} + .audioDropDragOver { border-color: var(--accent); background: var(--accent-fill-soft); diff --git a/app/renderer/editor/EditorApp.tsx b/app/renderer/editor/EditorApp.tsx index b431c5a..01c8f9d 100644 --- a/app/renderer/editor/EditorApp.tsx +++ b/app/renderer/editor/EditorApp.tsx @@ -2380,68 +2380,73 @@ function CampaignInspector({
{t('drop.hintAudio')}
) : null} {mediaAssets.filter((a) => a.type === 'audio').length === 0 ? ( -
{t('campaign.noFiles')}
- ) : ( -
- {mediaAssets - .filter((a) => a.type === 'audio') - .map((a) => ( -
- {a.originalName} - - - - - -
- ))} +
+
{t('campaign.noFiles')}
+
+ ) : ( + <> +
+ {mediaAssets + .filter((a) => a.type === 'audio') + .map((a) => ( +
+ {a.originalName} + + + + + +
+ ))} +
+ + )} -
); @@ -2653,68 +2658,73 @@ function SceneInspector({
{t('drop.hintAudio')}
) : null} {mediaAssets.filter((a) => a.type === 'audio').length === 0 ? ( -
{t('campaign.noFiles')}
- ) : ( -
- {mediaAssets - .filter((a) => a.type === 'audio') - .map((a) => ( -
- {a.originalName} - - - - - -
- ))} +
+
{t('campaign.noFiles')}
+
+ ) : ( + <> +
+ {mediaAssets + .filter((a) => a.type === 'audio') + .map((a) => ( +
+ {a.originalName} + + + + + +
+ ))} +
+ + )} -
{t('scene.branching')}
@@ -2754,6 +2764,7 @@ function SceneListCard({ const thumbUrl = useAssetUrl(scene.previewThumbAssetId); const previewUrl = useAssetUrl(scene.previewAssetId); const [menu, setMenu] = useState<{ x: number; y: number } | null>(null); + const [pendingDelete, setPendingDelete] = useState(false); useEffect(() => { if (!menu) return; @@ -2909,8 +2920,8 @@ function SceneListCard({ role="menuitem" className={styles.sceneCtxDanger} onClick={() => { - onDeleteScene(scene.id); setMenu(null); + setPendingDelete(true); }} > {t('common.delete')} @@ -2920,6 +2931,49 @@ function SceneListCard({ document.body, ) : null} + {pendingDelete + ? createPortal( + <> + +
+
+
+ {t('confirmDeleteScene.body', { name: scene.title })} +
+
+
+ + +
+ + , + document.body, + ) + : null} ); } diff --git a/app/renderer/editor/MaterialsBrowser.tsx b/app/renderer/editor/MaterialsBrowser.tsx index 7e253ed..8ddbc4d 100644 --- a/app/renderer/editor/MaterialsBrowser.tsx +++ b/app/renderer/editor/MaterialsBrowser.tsx @@ -114,9 +114,11 @@ export function MaterialsBrowser({
{mode === 'editor' && onAdd ? ( - +
+ +
) : null}
{filtered.map((m) => ( diff --git a/app/renderer/editor/MaterialsModals.module.css b/app/renderer/editor/MaterialsModals.module.css index df728bc..f401cd9 100644 --- a/app/renderer/editor/MaterialsModals.module.css +++ b/app/renderer/editor/MaterialsModals.module.css @@ -74,6 +74,24 @@ overflow: hidden; } +.sideAddBtn { + width: 100%; + flex: 0 0 auto; +} + +.sideAddBtn > * { + width: 100%; + max-width: 100%; + display: flex; + box-sizing: border-box; +} + +.sideAddBtn button { + width: 100%; + box-sizing: border-box; + flex: 1; +} + .list { display: grid; gap: 8px; @@ -245,6 +263,21 @@ position: relative; } +.imageDropEmpty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 10px; + min-height: 112px; + text-align: center; + padding: 8px 4px; +} + +.imageDropEmpty > * { + max-width: 100%; +} + .imageDropOver { border-color: var(--color-accent, #a78bfa); } diff --git a/app/renderer/editor/MaterialsModals.tsx b/app/renderer/editor/MaterialsModals.tsx index 2c2ba31..65c34ab 100644 --- a/app/renderer/editor/MaterialsModals.tsx +++ b/app/renderer/editor/MaterialsModals.tsx @@ -180,20 +180,36 @@ export function MaterialEditModal({ {previewSrc ? ( ) : ( -
{t('materials.imageEmpty')}
+
+
{t('materials.imageEmpty')}
+ +
)} - + {previewSrc ? ( + + ) : null}
{!hasImage ?
{t('materials.imageRequired')}
: null}
diff --git a/app/renderer/editor/i18n/editorMessages.ts b/app/renderer/editor/i18n/editorMessages.ts index 89bf700..13859f7 100644 --- a/app/renderer/editor/i18n/editorMessages.ts +++ b/app/renderer/editor/i18n/editorMessages.ts @@ -349,6 +349,9 @@ export const EDITOR_MESSAGES: Record> = { 'confirmDelete.body': 'Удалить проект «{name}» безвозвратно? Файл и кэш будут стёрты с диска.', 'confirmDelete.failedTitle': 'Не удалось удалить', + 'confirmDeleteScene.title': 'Удаление сцены', + 'confirmDeleteScene.body': 'Удалить сцену «{name}»? Её нельзя будет восстановить.', + 'picker.title': 'Проекты', 'picker.newPlaceholder': 'Название нового проекта…', 'picker.create': 'Создать проект', @@ -911,6 +914,9 @@ export const EDITOR_MESSAGES: Record> = { 'Permanently delete project “{name}”? The file and cache will be removed from disk.', 'confirmDelete.failedTitle': 'Could not delete', + 'confirmDeleteScene.title': 'Delete scene', + 'confirmDeleteScene.body': 'Delete scene “{name}”? This cannot be undone.', + 'picker.title': 'Projects', 'picker.newPlaceholder': 'New project name…', 'picker.create': 'Create project', diff --git a/app/renderer/npcs/NpcEditModal.tsx b/app/renderer/npcs/NpcEditModal.tsx index e41392f..c6c144a 100644 --- a/app/renderer/npcs/NpcEditModal.tsx +++ b/app/renderer/npcs/NpcEditModal.tsx @@ -205,19 +205,34 @@ export function NpcEditModal({ {previewSrc ? ( ) : ( -
{t('npcs.avatarEmpty')}
+
+
{t('npcs.avatarEmpty')}
+ +
)} - + {previewSrc ? ( + + ) : null} {!hasImage ?
{t('npcs.avatarRequired')}
: null} diff --git a/app/renderer/sceneEditor/SceneEditorApp.module.css b/app/renderer/sceneEditor/SceneEditorApp.module.css index fe24368..f696521 100644 --- a/app/renderer/sceneEditor/SceneEditorApp.module.css +++ b/app/renderer/sceneEditor/SceneEditorApp.module.css @@ -366,11 +366,20 @@ .clearSceneBtn { width: 100%; box-sizing: border-box; + flex: 0 0 auto; +} + +.clearSceneBtn > * { + width: 100%; + max-width: 100%; + display: flex; + box-sizing: border-box; } .clearSceneBtn button { width: 100%; box-sizing: border-box; + flex: 1; } .sceneToken {