Implement improved save functionality with custom filename, timestamp toggle, and overwrite protection

Co-authored-by: Azgaar <26469650+Azgaar@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-08-29 15:31:48 +00:00
parent ba837ab6f8
commit df15f74d95
4 changed files with 113 additions and 18 deletions

View file

@ -6076,21 +6076,37 @@
<div id="saveMapData" style="display: none" class="dialog">
<div style="margin-top: 0.3em">
<strong>Save map to</strong>
<button onclick="saveMap('machine')" data-tip="Download map file to your local disk" data-shortcut="Ctrl + S">
machine
</button>
<button onclick="saveMap('dropbox')" data-tip="Save map file to your Dropbox" data-shortcut="Ctrl + C">
dropbox
</button>
<button onclick="saveMap('storage')" data-tip="Save the project to browser storage only" data-shortcut="F6">
browser
</button>
<div style="margin-bottom: 0.8em">
<label for="customFileName" style="display: block; margin-bottom: 0.3em;"><strong>File name:</strong></label>
<input type="text" id="customFileName" style="width: 250px; margin-bottom: 0.3em;" placeholder="Enter custom filename (without .map extension)">
<div>
<input type="checkbox" id="includeTimestamp" checked>
<label for="includeTimestamp">Include timestamp in filename</label>
</div>
</div>
<div style="margin-bottom: 0.5em">
<strong>Save map to</strong>
</div>
<div>
<button onclick="saveMap('machine')" data-tip="Download map file to your local disk" data-shortcut="Ctrl + S">
machine
</button>
<button onclick="saveMap('dropbox')" data-tip="Save map file to your Dropbox" data-shortcut="Ctrl + C">
dropbox
</button>
<button onclick="saveMap('storage')" data-tip="Save the project to browser storage only" data-shortcut="F6">
browser
</button>
</div>
</div>
<div style="background-color: #ffe6e6; border: 1px solid #ff9999; padding: 0.8em; margin-top: 0.8em; border-radius: 4px;">
<p style="margin: 0; font-weight: bold; color: #cc0000;">⚠️ Important Backup Warning</p>
<p style="margin: 0.3em 0 0 0;">
Maps are saved in <i>.map</i> format, that can be loaded back via the <i>Load</i> in menu. <strong>There is no way to
restore the progress if file is lost.</strong> Please keep old save files on your machine or cloud storage as backups.
When saving to machine, files may be automatically renamed if a file with the same name already exists.
</p>
</div>
<p>
Maps are saved in <i>.map</i> format, that can be loaded back via the <i>Load</i> in menu. There is no way to
restore the progress if file is lost. Please keep old save files on your machine or cloud storage as backups.
</p>
</div>
<div id="loadMapData" style="display: none" class="dialog">

View file

@ -7,7 +7,24 @@ async function saveMap(method) {
try {
const mapData = prepareMapData();
const filename = getFileName() + ".map";
// Get custom filename settings from the dialog
const customFileNameInput = byId("customFileName");
const includeTimestampCheckbox = byId("includeTimestamp");
const customName = customFileNameInput?.value?.trim() || null;
const includeTimestamp = includeTimestampCheckbox?.checked !== false;
const filename = getFileName(null, customName, includeTimestamp) + ".map";
// For browser storage, check if file exists and ask for confirmation
if (method === "storage") {
const existingFile = await checkIfStorageFileExists(filename);
if (existingFile) {
const confirmOverwrite = await showOverwriteConfirmation(filename);
if (!confirmOverwrite) return;
}
}
saveToStorage(mapData, method === "storage"); // any method saves to indexedDB
if (method === "machine") saveToMachine(mapData, filename);
@ -259,3 +276,46 @@ function toggleSaveReminder() {
saveReminder();
}
}
// Helper function to check if a file exists in browser storage
async function checkIfStorageFileExists(filename) {
try {
// For browser storage, we only have one slot ("lastMap"), so we don't need to check filename
// This function is prepared for future enhancement if multiple saves are supported
const existingFile = await ldb.get("lastMap");
return existingFile !== null;
} catch (error) {
return false;
}
}
// Helper function to show overwrite confirmation dialog
async function showOverwriteConfirmation(filename) {
return new Promise((resolve) => {
alertMessage.innerHTML = /* html */ `
<p><strong>A map is already saved in browser storage.</strong></p>
<p>Do you want to overwrite the existing saved map with the current one?</p>
<p style="color: #cc6600; font-style: italic;">
The previous map will be permanently lost if you continue.
Consider saving to machine first as a backup.
</p>
`;
$("#alert").dialog({
resizable: false,
title: "Overwrite existing save?",
width: "28em",
buttons: {
"Yes, overwrite": function () {
$(this).dialog("close");
resolve(true);
},
"Cancel": function () {
$(this).dialog("close");
resolve(false);
}
},
position: {my: "center", at: "center", of: "svg"}
});
});
}

View file

@ -892,10 +892,15 @@ function unfog(id) {
if (!defs.selectAll("#fog path").size()) fogging.style("display", "none");
}
function getFileName(dataType) {
function getFileName(dataType, customName = null, includeTimestamp = true) {
const formatTime = time => (time < 10 ? "0" + time : time);
const name = mapName.value;
const name = customName || mapName.value;
const type = dataType ? dataType + " " : "";
if (!includeTimestamp) {
return name + (type ? " " + type.trim() : "");
}
const date = new Date();
const year = date.getFullYear();
const month = formatTime(date.getMonth() + 1);

View file

@ -738,10 +738,24 @@ function showSavePane() {
const sharableLinkContainer = byId("sharableLinkContainer");
sharableLinkContainer.style.display = "none";
// Initialize the custom filename input with current map name
const customFileNameInput = byId("customFileName");
const includeTimestampCheckbox = byId("includeTimestamp");
if (customFileNameInput) {
customFileNameInput.value = mapName.value || "Fantasy Map";
// Store default state
customFileNameInput.dataset.defaultValue = customFileNameInput.value;
}
if (includeTimestampCheckbox) {
includeTimestampCheckbox.checked = true;
}
$("#saveMapData").dialog({
title: "Save map",
resizable: false,
width: "25em",
width: "32em",
position: {my: "center", at: "center", of: "svg"},
buttons: {
Close: function () {