Adding image to visual editor popup for shortcode with wp.media and wp.mce: changing image duplicates shortcode

I suspect that clicking the Select Image button on the popup is
changing the underlying insertion point or selection in the main
editor, but I can’t for the life of me see how I can fix it.

I’m afraid I don’t know why exactly the issue happens only when the image is changed, but I can tell you the issue is with your popupwindow() function which always calls editor.insertContent() (where editor is tinyMCE.activeEditor).

And the proper way to set the content/shortcode is, or here’s how can you fix the issue: Use editor.insertContent() when adding the shortcode, whereas when editing it (e.g. changing the “Main Text” or the image), you should use the update() function that’s passed to your edit() function.

So firstly, define that function like so, and then pass the update to popupwindow():

Note: The your code here means your original code (i.e. no changes).

// 1. Add the "update" parameter.
edit: function( data, update ) {
    // ... your code here (define shortcode_data, etc).

    // 2. Pass the "update" to popupwindow().
    wp.mce.media_object.popupwindow( tinyMCE.activeEditor, values, update );
},

And then define the popupwindow() and onsubmit_callback() functions like so:

// 1. Add the "update" parameter.
popupwindow: function( editor, values, update ) {
    values = values || [];

    var onsubmit_callback = function( e ) {
        // ... your code here (define "args", etc).

        // 2. Use the "update" function, if available.
        if ( update ) { // the shortcode already in the content and is being edited
            update( wp.shortcode.string( args ) );
        } else { // the shortcode is being added for the 1st time
            editor.insertContent( wp.shortcode.string( args ) );
        }
    };

    editor.windowManager.open( /* your code here */ );
}

So I hope that helps and actually, in your code, you could just use $ instead of jQuery, e.g. $('#my-image-box-id').val(json.id). =)