Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased]
### Changed
- Moved "vanilla maps" toggle from settings into the map loading dialog to allow quick switching between mod and vanilla maps [#347](https://github.com/CCDirectLink/crosscode-map-editor/issues/347)

## [2.1.0] 2026-03-20
### Changed
- Changed scaling behaviour for json widget. Depending on content, it scales to 1-5 rows initially. When the user pastes multi-line json it also rescales to fit the content. [#344](https://github.com/CCDirectLink/crosscode-map-editor/issues/344)
Expand Down
2 changes: 1 addition & 1 deletion backend/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ app.use(bodyParser.urlencoded({ extended: true }));
*/
app.get('/api/allFiles', async (_, res) => res.json(await api.getAllFiles(config.pathToCrosscode)));
app.get('/api/allTilesets', async (_, res) => res.json(await api.getAllTilesets(config.pathToCrosscode)));
app.get('/api/allMaps', async (req, res) => res.json(await api.getAllMaps(config.pathToCrosscode, req.query['includeVanillaMaps'] == 'true')));
app.get('/api/allMaps', async (req, res) => res.json(await api.getAllMaps(config.pathToCrosscode, req.query['vanillaMaps'] == 'true')));
app.get('/api/allFilesInFolder', async (req, res) => res.json(await api.getAllFilesInFolder(config.pathToCrosscode, req.query['folder'] as string, req.query['extension'] as string)));
app.get('/api/allMods', async (_, res) => res.json(await api.getAllMods(config.pathToCrosscode)));
app.get('/api/allModMapEditorConfigs', async (_, res) => res.json(await api.getAllModMapEditorConfigs(config.pathToCrosscode)));
Expand Down
7 changes: 3 additions & 4 deletions common/src/controllers/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,14 +218,13 @@ export async function getAllTilesets(dir: string) {
return result.sort();
}

export async function getAllMaps(dir: string, includeVanillaMaps: boolean) {
export async function getAllMaps(dir: string, vanillaMaps: boolean) {
const path = await pathPromise;
const paths: string[] = [];

if (mods.length === 0 || includeVanillaMaps) {
if (mods.length === 0 || vanillaMaps) {
await listAllFiles(path.resolve(dir, 'data/maps/'), paths, 'json', path.resolve(dir));
}
if (mods.length > 0) {
} else {
const modDir = path.join(dir, 'mods', mods[0], 'assets');
await listAllFiles(path.resolve(modDir, 'data/maps/'), paths, 'json', path.resolve(modDir));
}
Expand Down
36 changes: 24 additions & 12 deletions webapp/src/app/components/dialogs/load-map/load-map.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<button mat-icon-button (click)="fileUpload.click()">
<mat-icon>open_in_new</mat-icon>
</button>
<button mat-icon-button (click)="refresh()">
<button mat-icon-button (click)="paths.reload()">
<mat-icon>refresh</mat-icon>
</button>
<button mat-icon-button (click)="close()">
Expand All @@ -18,6 +18,18 @@
</mat-toolbar>

<div class="filter no-error-msg flex flex-col">
@if (currentMod) {
<div class="mb-3">
<mat-checkbox
color="primary"
[(ngModel)]="vanillaMaps"
[disabled]="paths.isLoading()"
>
Vanilla Maps
</mat-checkbox>
</div>
}

<mat-form-field
appearance="outline"
>
Expand All @@ -28,7 +40,7 @@
<input #fileUpload type="file" class="hidden" (change)="loadMap($event)" accept=".json"/>
</div>
<div class="map-list">
@if (!loading) {
@if (!paths.isLoading()) {
<mat-tree [dataSource]="mapsSource" [treeControl]="treeControl" class="mapTree">
<mat-tree-node *matTreeNodeDef="let node">
<li class="mat-tree-node">
Expand All @@ -37,11 +49,11 @@
<mat-icon class="icon">edit</mat-icon>
@for (name of node.names; track name; let isLast = $last) {
<span>
<a appHighlight [highlightText]="name" [highlightMatch]="filter"></a>
<a appHighlight [highlightText]="name" [highlightMatch]="filter"></a>
@if (!isLast) {
<a class="separator">/</a>
}
</span>
</span>
}
</button>
</li>
Expand All @@ -55,11 +67,11 @@
</mat-icon>
@for (name of node.names; track name; let isLast = $last) {
<span>
<a appHighlight [highlightText]="name" [highlightMatch]="filter"></a>
<a appHighlight [highlightText]="name" [highlightMatch]="filter"></a>
@if (!isLast) {
<a class="separator">/</a>
}
</span>
</span>
}
</button>
@if (treeControl.isExpanded(node)) {
Expand All @@ -71,12 +83,12 @@
</mat-nested-tree-node>
</mat-tree>
} @else {

<div class="loader">
<mat-spinner [diameter]="40" [strokeWidth]="5"></mat-spinner>
</div>

}
<div class="loader">
<mat-spinner [diameter]="40" [strokeWidth]="5"></mat-spinner>
</div>
}

</div>
</div>
55 changes: 36 additions & 19 deletions webapp/src/app/components/dialogs/load-map/load-map.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NestedTreeControl } from '@angular/cdk/tree';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, inject, Input, ViewChild } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, effect, ElementRef, inject, Input, resource, untracked, ViewChild } from '@angular/core';
import { MatSidenav } from '@angular/material/sidenav';
import { MatNestedTreeNode, MatTree, MatTreeNestedDataSource, MatTreeNode, MatTreeNodeDef, MatTreeNodeOutlet, MatTreeNodeToggle } from '@angular/material/tree';

Expand All @@ -19,6 +19,9 @@ import { MatFormField, MatInput } from '@angular/material/input';
import { FormsModule } from '@angular/forms';
import { HighlightDirective } from '../../../directives/highlight.directive';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
import { SHARED_SERVICE } from '../../../services/shared-service';
import { MatCheckbox } from '@angular/material/checkbox';
import { SettingsService } from '../../../services/settings.service';


@Component({
Expand All @@ -41,8 +44,9 @@ import { MatProgressSpinner } from '@angular/material/progress-spinner';
MatNestedTreeNode,
MatTreeNodeToggle,
MatTreeNodeOutlet,
MatProgressSpinner
]
MatProgressSpinner,
MatCheckbox,
],
})
export class LoadMapComponent {
private mapLoader = inject(MapLoaderService);
Expand All @@ -51,44 +55,57 @@ export class LoadMapComponent {
private searchFilterService = inject(SearchFilterService);
private readonly eventsService = inject(GlobalEventsService);
private readonly overlayService = inject(OverlayService);
private readonly sharedService = inject(SHARED_SERVICE);
private readonly settingsService = inject(SettingsService);


@ViewChild('fileUpload', {static: true})
@ViewChild('fileUpload', { static: true })
fileUpload!: ElementRef<HTMLInputElement>;

@ViewChild('filterInput', {static: true})
@ViewChild('filterInput', { static: true })
filterInput!: ElementRef<HTMLInputElement>;

@Input()
sidenav!: MatSidenav;

loading = false;
paths = resource({
params: () => ({ vanillaMaps: this.vanillaMaps() }),
loader: async ({ params }) => {
const req = params.vanillaMaps ? this.http.getVanillaMaps() : this.http.getMaps();
return await firstValueFrom(req);
},
});

treeControl = new NestedTreeControl<VirtualMapNode>(node => node.children);
mapsSource = new MatTreeNestedDataSource<VirtualMapNode>();

root: MapNodeRoot = {name: '', displayed: true, children: []}; // The root itself is never displayed. It is used as a datasource for virtualRoot.
root: MapNodeRoot = { name: '', displayed: true, children: [] }; // The root itself is never displayed. It is used as a datasource for virtualRoot.
virtualRoot = new VirtualMapNode(this.root); // To reuse the children filtering.
filter = '';

currentMod = '';
vanillaMaps = this.settingsService.signalSettings().showVanillaMaps;

constructor() {
this.mapsSource.data = [];
this.refresh();
this.currentMod = this.sharedService.getSelectedMod();

effect(() => {
const paths = this.paths.value();
if (!paths) {
return;
}
untracked(() => {
this.displayMaps(paths);
this.update();
});
});
}

focusInput() {
this.filterInput.nativeElement.focus();
}

refresh() {
this.loading = true;
this.http.getMaps().subscribe(paths => {
this.loading = false;
this.displayMaps(paths);
this.update();
});
}

update() {
for (const node of this.root.children) {
this.filterNode(node, this.filter);
Expand All @@ -107,7 +124,7 @@ export class LoadMapComponent {
const dialogRef = this.overlayService.open(ConfirmCloseComponent, {
hasBackdrop: true,
});
const result = await firstValueFrom(dialogRef.ref.onClose, {defaultValue: false});
const result = await firstValueFrom(dialogRef.ref.onClose, { defaultValue: false });
if (result) {
this.eventsService.hasUnsavedChanges.next(false);
}
Expand Down Expand Up @@ -146,7 +163,7 @@ export class LoadMapComponent {
const node = this.resolve(data, path, lastNode, lastPath);
const name = path.substring(path.lastIndexOf('.') + 1);

node.push({name, path, displayed: true});
node.push({ name, path, displayed: true });

lastPath = path;
lastNode = node;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<div fxLayout="row" class="option-row">
<mat-form-field fxFlex="auto">
<mat-label>Mod</mat-label>
<mat-select [(value)]="mod" (selectionChange)="modSelectEvent($event.value)">
<mat-select [(value)]="mod">
<mat-option>None</mat-option>
@for (mod of mods; track mod) {
<mat-option [value]="mod.id"><span [appColoredText]="mod.displayName"></span></mat-option>
Expand All @@ -35,15 +35,6 @@
<mat-hint>Maps will be stored and loaded from the selected mod</mat-hint>
</mat-form-field>
</div>
<div fxLayout="row" class="option-row">
<mat-checkbox
[(ngModel)]="settings.includeVanillaMaps"
color="primary"
[disabled]="isIncludeVanillaMapsDisabled"
>
Include vanilla maps
</mat-checkbox>
</div>
<div fxLayout="row" class="option-row">
<mat-checkbox
[(ngModel)]="settings.wrapEventEditorLines"
Expand Down
Loading
Loading