Skip to content

Commit 3b2705c

Browse files
committed
Update website
1 parent e846fde commit 3b2705c

2 files changed

Lines changed: 118 additions & 11 deletions

File tree

mapshaper-gui.js

Lines changed: 108 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,6 +1580,19 @@
15801580
action: saveSnapshot
15811581
});
15821582

1583+
if (!gui.session.isEmpty()) {
1584+
// Surface the console "history" command via the snapshot menu so users
1585+
// can browse the session's command history without knowing about the
1586+
// console keyword. Hidden when there's nothing to show.
1587+
addMenuLink({
1588+
slug: 'history',
1589+
label: 'view session history',
1590+
action: function(gui) {
1591+
gui.console.runCommand('history');
1592+
}
1593+
});
1594+
}
1595+
15831596
// var available = await getAvailableStorage();
15841597
// if (available) {
15851598
// El('div').addClass('save-menu-entry').text(available + ' available').appendTo(menu);
@@ -1703,18 +1716,37 @@
17031716
}
17041717
gui.model.clear();
17051718
importDatasets(data.datasets, gui);
1719+
// Reinstate the session history (including its saved/unsaved boundary) that
1720+
// was in effect when the snapshot was taken. If the snapshot has no history
1721+
// field (e.g. older snapshots), this resets to a clean state.
1722+
gui.session.restoreHistorySnapshot(data.history);
17061723
gui.clearMode();
17071724
}
17081725

1709-
// Add datasets to the current project
1726+
// Import datasets from a packed .msx buffer.
1727+
// Behavior depends on whether the current session contains data:
1728+
// - empty session: full project restore -- datasets and any embedded session
1729+
// history are loaded as if continuing the original session.
1730+
// - non-empty session: merge -- datasets are added to the current project,
1731+
// but any embedded session history is discarded (the imported commands
1732+
// assume different layer indices and a different starting state, so
1733+
// merging them into the current session would produce a misleading history).
1734+
// Returns true if a full restore occurred, false if a merge occurred. The
1735+
// caller uses this to decide whether to record an additional -i command in
1736+
// the current session's history (see gui-import-control.mjs).
17101737
// TODO: figure out if interface data should be imported (e.g. should
17111738
// visibility flag of imported layers be imported)
17121739
async function importSessionData(buf, gui) {
17131740
if (buf instanceof ArrayBuffer) {
17141741
buf = new Uint8Array(buf);
17151742
}
17161743
var data = await internal.unpackSessionData(buf);
1744+
var fullRestore = gui.model.isEmpty();
17171745
importDatasets(data.datasets, gui);
1746+
if (fullRestore) {
1747+
gui.session.restoreHistorySnapshot(data.history);
1748+
}
1749+
return fullRestore;
17181750
}
17191751

17201752
function importDatasets(datasets, gui) {
@@ -1730,7 +1762,13 @@
17301762
if (!lyr) return null; // no data -- no snapshot
17311763
// compact: true applies compression to vector coordinates, for ~30% reduction
17321764
// in file size in a typical polygon or polyline file, but longer processing time
1733-
var opts = {compact: false, active_layer: lyr};
1765+
// history: capture session commands + saved/unsaved boundary so the history
1766+
// can be reinstated if this snapshot is restored or re-imported later.
1767+
var opts = {
1768+
compact: false,
1769+
active_layer: lyr,
1770+
history: gui.session.getHistorySnapshot()
1771+
};
17341772
var datasets = gui.model.getDatasets();
17351773
var obj = await internal.exportDatasetsToPack(datasets, opts);
17361774
obj.gui = getGuiState(gui);
@@ -2348,7 +2386,16 @@
23482386
await wait(35);
23492387
}
23502388
if (group[internal.PACKAGE_EXT]) {
2351-
await importSessionData(group[internal.PACKAGE_EXT].content, gui);
2389+
var fullRestore = await importSessionData(group[internal.PACKAGE_EXT].content, gui);
2390+
importCount++;
2391+
// Skip recording an -i command if the .msx import was a full project
2392+
// restore: the previous session's history (including the original -i)
2393+
// has already been reinstated, so adding another entry here would be
2394+
// misleading. For the merge case, record the import as a regular -i
2395+
// so the snapshot contributes a CLI-replayable entry to the session.
2396+
if (!fullRestore) {
2397+
gui.session.fileImported(group[internal.PACKAGE_EXT].filename, optStr);
2398+
}
23522399
} else if (await importDataset(group, groupImportOpts)) {
23532400
importCount++;
23542401
gui.session.fileImported(group.filename, optStr);
@@ -3958,13 +4005,18 @@
39584005
// expose this function, so other components can run commands (e.g. box tool)
39594006
this.runMapshaperCommands = runMapshaperCommands;
39604007

3961-
this.runInitialCommands = function(str) {
4008+
// Open the console (if closed) and run a command, as if the user had
4009+
// typed it. Used by UI controls that surface console functionality, e.g.
4010+
// the "view command history" link in the snapshot menu.
4011+
this.runCommand = function(str) {
39624012
str = str.trim();
39634013
if (!str) return;
39644014
turnOn();
39654015
submit(str);
39664016
};
39674017

4018+
this.runInitialCommands = this.runCommand;
4019+
39684020
consoleMessage(PROMPT);
39694021
gui.keyboard.on('keydown', onKeyDown);
39704022
window.addEventListener('beforeunload', saveHistory); // save history if console is open on refresh
@@ -4783,6 +4835,17 @@
47834835
await loadGeopackageLib();
47844836
}
47854837
opts.active_layer = gui.model.getActiveLayer().layer; // kludge to support restoring active layer in gui
4838+
if (opts.format == internal.PACKAGE_EXT) {
4839+
// Embed the session history in .msx exports so that re-importing the
4840+
// file into a fresh session restores the original command history.
4841+
// The .msx file itself is a durable artifact, so mark every captured
4842+
// command as "saved" -- a user reloading the file shouldn't see an
4843+
// unsaved-changes warning for work that lives in the file they just
4844+
// opened.
4845+
var snapshot = gui.session.getHistorySnapshot();
4846+
snapshot.savedAtIndex = snapshot.commands.length;
4847+
opts.history = snapshot;
4848+
}
47864849
try {
47874850
var files = await internal.exportTargetLayers(model, targets, opts);
47884851
} catch(e) {
@@ -5592,20 +5655,54 @@
55925655

55935656
function SessionHistory(gui) {
55945657
var commands = [];
5658+
// index of first command after the last "save" boundary; commands at indices
5659+
// [savedAtIndex .. commands.length) are considered unsaved
5660+
var savedAtIndex = 0;
55955661
// commands that can be ignored when checking for unsaved changes
5596-
var nonEditingCommands = 'i,target,info,version,verbose,projections,inspect,help,h,encodings,calc'.split(',');
5662+
var nonEditingCommands = 'i,target,info,version,verbose,projections,inspect,help,h,encodings,calc,comment'.split(',');
55975663

55985664
this.unsavedChanges = function() {
5599-
var cmd, cmdName;
5600-
for (var i=commands.length - 1; i >= 0; i--) {
5601-
cmdName = getCommandName(commands[i]);
5602-
if (cmdName == 'o') break;
5665+
for (var i = commands.length - 1; i >= savedAtIndex; i--) {
5666+
var cmdName = getCommandName(commands[i]);
56035667
if (nonEditingCommands.includes(cmdName)) continue;
56045668
return true;
56055669
}
56065670
return false;
56075671
};
56085672

5673+
this.isEmpty = function() {
5674+
return commands.length === 0;
5675+
};
5676+
5677+
// Mark the current end of the history as a "saved" boundary -- called after
5678+
// data has been written somewhere durable (e.g. an -o export). Snapshots
5679+
// are session-scoped and are NOT durable, so creating one does not mark saved.
5680+
this.markSaved = function() {
5681+
savedAtIndex = commands.length;
5682+
};
5683+
5684+
// Capture a serializable copy of the history for inclusion in a snapshot.
5685+
this.getHistorySnapshot = function() {
5686+
return {
5687+
commands: commands.slice(),
5688+
savedAtIndex: savedAtIndex
5689+
};
5690+
};
5691+
5692+
// Replace the current history with one captured by getHistorySnapshot().
5693+
// Used when restoring an in-session snapshot. If the snapshot has no history
5694+
// (e.g. older snapshots, or external .msx files), starts from a clean state.
5695+
this.restoreHistorySnapshot = function(obj) {
5696+
if (obj && Array.isArray(obj.commands)) {
5697+
commands = obj.commands.slice();
5698+
savedAtIndex = typeof obj.savedAtIndex == 'number' ?
5699+
Math.min(obj.savedAtIndex, commands.length) : commands.length;
5700+
} else {
5701+
commands = [];
5702+
savedAtIndex = 0;
5703+
}
5704+
};
5705+
56095706
this.fileImported = function(file, optStr) {
56105707
var cmd = '-i ' + file;
56115708
if (optStr) {
@@ -5665,6 +5762,8 @@
56655762
cmd += ' ' + optStr;
56665763
}
56675764
commands.push(cmd);
5765+
// -o writes data to a durable location, so treat this as a save boundary
5766+
savedAtIndex = commands.length;
56685767
};
56695768

56705769
this.setTargetLayer = function(lyr) {

mapshaper.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11723,7 +11723,11 @@
1172311723
version: 1,
1172411724
created: 'YYYY-MM-DDTHH:mm:ss.sssZ', // ISO string
1172511725
datasets: [],
11726-
gui: {} // see gui-session-snapshot-control.mjs
11726+
gui: {}, // see gui-session-snapshot-control.mjs
11727+
history: { // optional; only present in snapshots created by the GUI
11728+
commands: ['-i foo.shp', '-simplify 10%', ...],
11729+
savedAtIndex: 0 // index of the first command after the last save boundary
11730+
}
1172711731
}
1172811732
*/
1172911733

@@ -11747,12 +11751,16 @@
1174711751
// exporting from command line: { compact: true, file: 'tmp.msx', final: true }
1174811752
// exporting from gui export menu: {compact: true, format: 'msx'}
1174911753
// saving gui temp snapshot: {compact: false}
11754+
// opts.history: optional GUI session history captured by SessionHistory#getHistorySnapshot
1175011755
async function exportDatasetsToPack(datasets, opts) {
1175111756
var obj = {
1175211757
version: 1,
1175311758
created: (new Date).toISOString(),
1175411759
datasets: await Promise.all(datasets.map(dataset => exportDataset(dataset, opts)))
1175511760
};
11761+
if (opts.history) {
11762+
obj.history = opts.history;
11763+
}
1175611764
return obj;
1175711765
}
1175811766

@@ -51131,7 +51139,7 @@ ${svg}
5113151139
});
5113251140
}
5113351141

51134-
var version = "0.6.120";
51142+
var version = "0.6.121";
5113551143

5113651144
// Parse command line args into commands and run them
5113751145
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.

0 commit comments

Comments
 (0)