diff --git a/src/editor/HierarchyPanel.jsx b/src/editor/HierarchyPanel.jsx
index aef2513..a39613c 100644
--- a/src/editor/HierarchyPanel.jsx
+++ b/src/editor/HierarchyPanel.jsx
@@ -376,6 +376,23 @@ const HierarchyPanel = ({
useEffect(() => {
if (!selection) return;
const t = selection.type;
+ // Выделена ПАПКА (клик по сцене / вставка кита из тулбокса) — раскрыть
+ // «Сцену» и цепочку папок до неё, чтобы папка стала видна в дереве.
+ if (t === 'folder') {
+ setWorkspaceOpen(true);
+ setOpenFolders(prev => {
+ const n = new Set(prev);
+ let cur = selection.folderId;
+ const guard = new Set();
+ while (cur != null && !guard.has(cur)) {
+ guard.add(cur);
+ const f = folders.find(ff => ff.id === cur);
+ if (f && f.parentId != null) { n.add(f.parentId); cur = f.parentId; } else cur = null;
+ }
+ return n;
+ });
+ return;
+ }
// Находим объект и его folderId по выделению.
let obj = null, kind = null;
if (t === 'block') {
@@ -486,10 +503,12 @@ const HierarchyPanel = ({
const subUserModels = userModelsByFolder.get(folder.id) || [];
const totalCount = subBlocks.length + subModels.length + subPrims.length + subUserModels.length + subFolders.length;
+ const folderSelected = selection?.type === 'folder' && selection?.folderId === folder.id;
return (
{ if (el) el.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); } : null}
+ className={`${cl.folderHeader} ${folderSelected ? cl.itemSelected : ''}`}
style={{ paddingLeft: depth * 12 + 8 }}
onClick={() => onSelectFolder?.(folder.id)}
onContextMenu={(e) => handleContextMenu(e, { type: 'folder', ...folder })}
diff --git a/src/editor/engine/BabylonScene.js b/src/editor/engine/BabylonScene.js
index edaccc0..d5ff5e7 100644
--- a/src/editor/engine/BabylonScene.js
+++ b/src/editor/engine/BabylonScene.js
@@ -6604,6 +6604,25 @@ export class BabylonScene {
if (this._onSceneChange) this._onSceneChange();
}
+ /** Удалить скрипты, чей объект-носитель больше не существует (после удаления
+ * папки/объектов). Глобальные (target null/'game') не трогаем. */
+ _cleanupOrphanScripts() {
+ if (!Array.isArray(this._scripts)) return;
+ const exists = (t) => {
+ if (!t || t === 'game') return true;
+ if (t.kind === 'primitive') return !!this.primitiveManager?.instances?.has(t.id);
+ if (t.kind === 'model') return !!this.modelManager?.instances?.has(t.id);
+ if (t.kind === 'userModel') return !!this.userModelManager?.instances?.has(t.id);
+ return true; // block и пр. — не чистим
+ };
+ const before = this._scripts.length;
+ this._scripts = this._scripts.filter(s => exists(s.target));
+ if (this._scripts.length !== before) {
+ this.history?.markChange();
+ if (this._onSceneChange) this._onSceneChange();
+ }
+ }
+
/**
* Зарегистрировать колбэк для уведомлений об изменении режима Play
* (вызывается когда player сам инициирует exit, например по Esc).
diff --git a/src/editor/engine/SelectionManager.js b/src/editor/engine/SelectionManager.js
index 22b0a94..cf1dbcf 100644
--- a/src/editor/engine/SelectionManager.js
+++ b/src/editor/engine/SelectionManager.js
@@ -724,6 +724,17 @@ export class SelectionManager {
} else if (this._selection.type === 'spawn') {
// Удаление точки спавна → игрок будет появляться в (0, высота, 0).
this._scene3d?.deleteSpawn?.();
+ } else if (this._selection.type === 'folder') {
+ // Папка целиком — удаляем со ВСЕМ содержимым (рекурсивно).
+ const fid = this._selection.folderId;
+ this.clear();
+ this._scene3d?.folderManager?.removeFolder?.(fid, true);
+ // Удаляем скрипты, привязанные к объектам этой папки? Они привязаны
+ // к примитивам, которые removeFolder удалит; скрипты на них чистятся
+ // через _onSceneChange / при сохранении. Дополнительно пусть движок
+ // подчистит «осиротевшие» скрипты.
+ this._scene3d?._cleanupOrphanScripts?.();
+ return;
}
this.clear();
}