Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f69b971
Add editable map notes with sharp multiline text
alcatron Oct 11, 2026
609418a
Guard note deletion and propagate configuration write failures
alcatron Oct 11, 2026
3f5f48a
Exercise template saves and failing configuration streams
alcatron Oct 11, 2026
d90c70e
Keep sharp notes at logical live-view size and extract palette transl…
alcatron Oct 11, 2026
9ccb9cf
Preserve sharp notes across layers and clarify note deletion
alcatron Oct 11, 2026
22f266f
Preserve note rendering, defaults and failed configuration writes
alcatron Oct 12, 2026
b56c171
Verify atomic saves preserve template and traffic objects
alcatron Oct 12, 2026
2c9f1d6
Keep atomic-save regression checks portable across platforms
alcatron Oct 12, 2026
9a63dea
Fall back safely when a note font is unavailable
alcatron Oct 12, 2026
2978a41
Coordinate configuration writes, deletion and registration by directory
alcatron Oct 12, 2026
20feb14
Return a retry when configuration deletion encounters a busy directory
alcatron Oct 12, 2026
213a88b
Merge updated develop and preserve editor changes
alcatron Oct 12, 2026
eacc6df
Merge develop after gallery resize landed
alcatron Oct 12, 2026
f55a0be
Report stale edits and failed saves; fall back from unreadable fonts
alcatron Oct 12, 2026
1c37cae
Reject stale saves and competing creates; retain text on allocation f…
alcatron Oct 12, 2026
f4b5fda
Let unused note canvases release with the rendering scope
alcatron Oct 12, 2026
9f701fc
Reject stale editor pages and reserve new maps before registration
alcatron Oct 12, 2026
d9c907a
Keep assets and clone reservations safe on failed saves
alcatron Oct 12, 2026
3279a83
Refresh translations for registration failure reporting
alcatron Oct 12, 2026
826118f
Cover legacy background and icon repair failure paths
alcatron Oct 12, 2026
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 @@ -16,6 +16,9 @@ IMPORTANT NOTE: This version only works on CACTI 1.x++!

--- develop ---

* Make configuration saves and repairs atomic; coordinate writes, registration and deletion with destination-directory locks and report failed editor saves

* Add editable map notes with alignment and sharp text at logical map dimensions
* Reduce gallery preview cards by 20% while preserving generated thumbnail resolution
* Add a searchable node icon picker and bundled enterprise site icons

Expand Down
113 changes: 108 additions & 5 deletions js/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,9 @@ function wmEditorReturnUrl(target) {
}

function attach_click_events() {
$('#xycapture').off('click.wmnote').on('click.wmnote', function(event) {
if ($('#action').val() === 'place_note' || ($('#action').val() === 'move_node' && typeof WMNotes !== 'undefined' && WMNotes[$('#param').val()])) coord_update(event);
});
$("area[id^='LINK:']").attr('href', '#').off('click').on('click', click_handler);
$("area[id^='NODE:']").attr('href', '#').off('click').on('click', click_handler);
$("area[id^='TIMES']").attr('href', '#').off('click').on('click', position_timestamp);
Expand All @@ -554,6 +557,35 @@ function attach_click_events() {
$('#tb_mapstyle').off('click').on('click', map_style);

$('#tb_addlink').off('click').on('click', add_link);
$('#tb_addnote').off('click').on('click', add_note);
$('.wm-note-colour').off('click').on('click', function() {
$('#note_colour').val($(this).attr('data-colour'));
wmNoteRefreshPalette();
});
$('#note_colour').off('input change').on('input change', wmNoteRefreshPalette);
$('#note_advanced_toggle').off('click').on('click', function() {
var open = $(this).attr('aria-expanded') !== 'true';
$(this).attr('aria-expanded', String(open)).text(open ? wmEditorText.hideAdvanced : wmEditorText.advanced);
$('#note_advanced').toggle(open);
});
$('#note_clone').off('click').on('click', function() {
$('#note_name').val('');
$('#note_x').val(Math.min(parseInt($('#note_x').attr('max'), 10), parseInt($('#note_x').val(), 10) + 20));
$('#note_y').val(Math.min(parseInt($('#note_y').attr('max'), 10), parseInt($('#note_y').val(), 10) + 20));
$('#note_delete,#note_move,#note_clone').hide();
$('#note_text').trigger('focus');
});
$('#note_move').off('click').on('click', function() {
$('#param').val($('#note_name').val());
$('#node_name').val($('#note_name').val());
hide_dialog('dlgNoteProperties');
move_node();
Comment thread
Copilot marked this conversation as resolved.
});
$('#note_delete').off('click').on('click', function() {
$('#param').val($('#note_name').val());
$('#node_name').val($('#note_name').val());
delete_node();
Comment thread
Copilot marked this conversation as resolved.
});
$('#tb_poslegend').off('click').on('click', position_first_legend);
$('#tb_postime').off('click').on('click', position_timestamp);
$('#tb_colours').off('click').on('click', manage_colours);
Expand Down Expand Up @@ -751,20 +783,21 @@ function delete_node() {

var name = $('#node_name').val();
var connected = Object.keys(Links).filter(function(key) { return Links[key].a === name || Links[key].b === name; }).length;
var message = delNodePrompt.replace('%s', function() { return wmNodeDisplayName(name); });
var isNote = typeof WMNotes !== 'undefined' && Object.prototype.hasOwnProperty.call(WMNotes, name);
var message = (isNote ? wmEditorText.noteDelete : delNodePrompt).replace('%s', function() { return wmNodeDisplayName(name); });
if (connected) {
message += ' ' + delNodeConnected.replace('%s', String(connected));
}
$('.dlgConfirm').text(message + ' ' + delNodeKeepDevice);
$('.dlgConfirm').text(message + (isNote ? '' : ' ' + delNodeKeepDevice));

mapmode('xy');

$('.dlgConfirm').dialog({
resizable: false,
title: delNodeTitle,
title: isNote ? wmEditorText.noteDeleteTitle : delNodeTitle,
height: 'auto',
width: 400,
modal: false,
modal: true,
buttons: [
{
text: txtCancel,
Expand All @@ -774,10 +807,11 @@ function delete_node() {
}
},
{
text: txtDelNode,
text: isNote ? wmEditorText.noteDeleteAction : txtDelNode,
click: function() {
$(this).dialog('close');
hide_all_dialogs();
$('#param').val(name);
$('#action').val('delete_node');
form_submit();
}
Expand Down Expand Up @@ -871,12 +905,32 @@ function delete_link() {
}

function form_submit() {
if ($('#action').val() === 'place_note') {
show_note('', $('#x').val(), $('#y').val());
return;
}
var noteAction = $('#action').val();
var noteTarget = noteAction === 'set_note_properties' ? $('#note_name').val() : noteAction === 'move_node' ? $('#node_name').val() : $('#param').val();
var savingNote = noteAction === 'set_note_properties' || (['move_node', 'delete_node'].indexOf(noteAction) !== -1 && typeof WMNotes !== 'undefined' && WMNotes[noteTarget]);
var data = $('input, select, textarea').serialize();
if (typeof WMConfigRevision !== 'undefined') data += '&config_revision=' + encodeURIComponent(WMConfigRevision);

$.ajax({
type: 'POST',
url: editor_url,
data: data,
error: function(xhr) {
if (xhr && xhr.status === 409) {
window.alert(wmEditorText.staleError);
return;
}
if (savingNote) {
if (noteAction !== 'set_note_properties') show_note(noteTarget);
$('#wm-note-error').text(wmEditorText.noteError).show();
Comment thread
alcatron marked this conversation as resolved.
} else {
window.alert(wmEditorText.saveError);
}
},
success: function(html) {
hide_all_dialogs();

Expand Down Expand Up @@ -1041,6 +1095,10 @@ function prime_node_form(name) {
}

function show_node(name) {
if (typeof WMNotes !== 'undefined' && Object.prototype.hasOwnProperty.call(WMNotes, name)) {
show_note(name);
return;
}
mapmode('existing');

hide_all_dialogs();
Expand Down Expand Up @@ -1250,6 +1308,7 @@ function hide_all_dialogs() {
hide_dialog('dlgLinkProperties');
hide_dialog('dlgTextEdit');
hide_dialog('dlgNodeProperties');
hide_dialog('dlgNoteProperties');
hide_dialog('dlgColours');
hide_dialog('dlgImages');
hide_dialog('dlgEditorSettings');
Expand Down Expand Up @@ -1721,3 +1780,47 @@ function wmFriendlyLinkNames() {
function wmDecodeEditorUrl(value) {
return String(value || '').replace(/&/g, '&');
}
// Notes are ordinary label-only nodes, with a small editor-specific style form.
function add_note() {
hide_all_dialogs();
$('#tb_help').addClass('wm-action-help').text(wmEditorText.addNoteHelp);
$('#action').val('place_note');
mapmode('xy');
Comment thread
alcatron marked this conversation as resolved.
}
function wmNoteRefreshPalette() {
var colour = String($('#note_colour').val()).toLowerCase();
$('.wm-note-colour').each(function() {
var selected = $(this).attr('data-colour') === colour;
$(this).attr('aria-pressed', selected ? 'true' : 'false').css('outline', selected ? '2px solid currentColor' : '');
});
}
function show_note(name, x, y) {
hide_all_dialogs();
mapmode('existing');
var node = name ? Nodes[name] : null;
var style = name ? WMNotes[name] : null;
$('#note_name').val(name);
$('#note_align').val(style && ['left', 'centre', 'right'].indexOf(style.align) !== -1 ? style.align : 'left');
$('#note_text').val(node ? node.label.replace(/\\n/g, '\n') : '');
$('#note_x').val(node ? node.x : x);
$('#note_y').val(node ? node.y : y);
$('#note_colour').val(style ? style.colour : '#000000');
wmNoteRefreshPalette();
$('#note_advanced').hide();
$('#note_advanced_toggle').attr('aria-expanded', 'false').text(wmEditorText.advanced);
if (style) {
if (!$('#note_font option').filter(function() { return this.value === style.font; }).length) {
$('<option>').val(style.font).text(style.fontLabel).appendTo('#note_font');
}
$('#note_font').val(style.font);
} else {
var defaultFont = $('#note_font option').filter(function() { return this.value === 'bundled:Vera.ttf:9' || $(this).attr('data-wm-note-default') === '1'; }).first();
$('#note_font').val(defaultFont.length ? defaultFont.val() : '3');
}
$('#note_delete,#note_move,#note_clone').toggle(!!node);
$('#wm-note-error').hide();
$('#action').val('set_note_properties');
show_dialog('dlgNoteProperties');
$('#note_font,#note_align').each(function() { if ($(this).data('ui-selectmenu')) $(this).selectmenu('refresh'); });
$('#note_text').trigger('focus');
}
Loading