Fix findUsages/undo/node-lookup bugs, split Viewport and Editor, share command and inspector helpers
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1eb28314cf
commit
f140c2fd77
58 files changed
+1969
-1171
No files matched your search
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Loading, creating, switching and deleting projects, plus the two toolbar menus that trigger
|
||||
* it. Talks only to the store and the editor state – no widgets of its own beyond the menus.
|
||||
*/
|
||||
import { SetActiveSceneCommand } from '../core/commands/assetCommands';
|
||||
import { listScenes } from '../core/model/Project';
|
||||
import type { ProjectStore } from '../core/store/ProjectStore';
|
||||
import { Dialog, Menu, type MenuItem, type Widget } from '../ui';
|
||||
import type { EditorState } from './state';
|
||||
|
||||
export class ProjectSession {
|
||||
/**
|
||||
* `beforeSwitch` runs before a different project is loaded or the current one is deleted –
|
||||
* a run in progress has to end first, since it holds a copy of the project being replaced.
|
||||
*/
|
||||
constructor(private readonly state: EditorState, private readonly store: ProjectStore, private readonly beforeSwitch: () => void) {}
|
||||
|
||||
/** Opens the project named in the URL, else the first one on the server, else a new one. */
|
||||
async boot(): Promise<void> {
|
||||
const wanted = new URLSearchParams(location.search).get('project');
|
||||
try {
|
||||
if (wanted) {
|
||||
await this.open(wanted);
|
||||
return;
|
||||
}
|
||||
await this.openFirstOrCreate();
|
||||
} catch (error) {
|
||||
this.state.setNotice(`Could not load project: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async open(id: string): Promise<void> {
|
||||
this.beforeSwitch();
|
||||
const loaded = await this.store.load(id);
|
||||
this.state.loadProject(loaded.project, loaded.revision);
|
||||
const url = new URL(location.href);
|
||||
url.searchParams.set('project', id);
|
||||
history.replaceState(null, '', url);
|
||||
}
|
||||
|
||||
private async openFirstOrCreate(): Promise<void> {
|
||||
const list = await this.store.list();
|
||||
if (list[0]) await this.open(list[0].id);
|
||||
else await this.create('My first project');
|
||||
}
|
||||
|
||||
private async create(name: string): Promise<void> {
|
||||
const created = await this.store.create(name);
|
||||
await this.open(created.project.id);
|
||||
}
|
||||
|
||||
private async deleteCurrent(): Promise<void> {
|
||||
this.beforeSwitch();
|
||||
await this.store.delete(this.state.project.id);
|
||||
await this.openFirstOrCreate();
|
||||
}
|
||||
|
||||
openProjectMenu(anchor: Widget): void {
|
||||
const open = async () => {
|
||||
const list = await this.store.list();
|
||||
const items: MenuItem[] = [
|
||||
{ label: 'New project', onSelect: () => void this.create(`Project ${list.length + 1}`) },
|
||||
...list.map((p) => ({
|
||||
label: p.id === this.state.project.id ? `● ${p.name}` : p.name,
|
||||
onSelect: () => void this.open(p.id),
|
||||
disabled: p.id === this.state.project.id,
|
||||
})),
|
||||
{
|
||||
label: 'Delete this project…',
|
||||
danger: true,
|
||||
onSelect: () =>
|
||||
new Dialog(`Delete project "${this.state.project.name}"?`, 'All assets and scenes are removed permanently.', [
|
||||
{ label: 'Cancel' },
|
||||
{ label: 'Delete', danger: true, onClick: () => void this.deleteCurrent() },
|
||||
]).openCentered(),
|
||||
},
|
||||
];
|
||||
new Menu(items).openBelow(anchor);
|
||||
};
|
||||
void open().catch((error) => this.state.setNotice(String(error)));
|
||||
}
|
||||
|
||||
openSceneMenu(anchor: Widget): void {
|
||||
const items: MenuItem[] = listScenes(this.state.project).map((scene) => ({
|
||||
label: scene.id === this.state.project.activeSceneId ? `● ${scene.name}` : scene.name,
|
||||
onSelect: () => this.state.execute(new SetActiveSceneCommand(scene.id)),
|
||||
}));
|
||||
if (items.length === 0) items.push({ label: 'No scenes – create one in Assets', onSelect: () => {}, disabled: true });
|
||||
new Menu(items).openBelow(anchor);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user