Skip to main content
Known Participant
August 1, 2025
Answered

Script doesn't undo in one go, please help

  • August 1, 2025
  • 4 replies
  • 320 views

hello, the following script is not able to undo its actions in one go, any ideas of where I am making a mess? Also it deosnt work on mac, I think the file path in it maybe wrong havn't sorted that yet. 

 

The script makes guides

 

 

 

and here is the entire script 

 

/**
 * Illustrator Margins, Grid & Baseline Guide Script [CLEAN MODERN VERSION]
 * No geometry/visual styling code—pure guides only.
 * All settings and UI logic are preserved; all RGB/stroke/fill logic is stripped out.
 */

var settingsXMLPath = Folder.myDocuments + "/IllustratorGridSettings.xml";

// --- XML functions (unchanged) ---
function loadSettingsFromXML(xmlFilePath, abIdx, defaults) {
    function stringifyDefaults(defaultsObj) {
        var stringDefaults = {};
        for (var key in defaultsObj) {
            if (defaultsObj.hasOwnProperty(key)) {
                if (key === "BaselineInc") {
                    stringDefaults[key] = defaultsObj[key].toString() + "pt";
                } else if (key === "Columns" || key === "Rows") {
                    stringDefaults[key] = defaultsObj[key].toString();
                } else {
                    stringDefaults[key] = defaultsObj[key].toString() + "mm";
                }
            }
        }
        return stringDefaults;
    }

    var file = new File(xmlFilePath);
    if (!file.exists) return stringifyDefaults(defaults);

    file.open('r');
    var content = file.read();
    file.close();

    var abTag = new RegExp('<Artboard index="' + abIdx + '">([\\s\\S]*?)<\\/Artboard>');
    var found = content.match(abTag);
    if (found) {
        var vals = {};
        var tags = ["Top","Bottom","Left","Right","Columns","Rows","VertGutter","HorGutter","BaselineStart","BaselineInc"];
        for (var i = 0; i < tags.length; i++) {
            var tagName = tags[i];
            var tagRegex = new RegExp('<' + tagName + '>([\\s\\S]*?)<\\/' + tagName + '>');
            var tagMatch = found[1].match(tagRegex);
            if (tagMatch) {
                vals[tagName] = tagMatch[1];
            } else {
                if (tagName === "BaselineInc") {
                    vals[tagName] = defaults[tagName].toString() + "pt";
                } else if (tagName === "Columns" || tagName === "Rows") {
                    vals[tagName] = defaults[tagName].toString();
                } else {
                    vals[tagName] = defaults[tagName].toString() + "mm";
                }
            }
        }
        return vals;
    }
    return stringifyDefaults(defaults);
}

function saveSettingsToXML(xmlFilePath, abIdx, newVals) {
    var file = new File(xmlFilePath);
    var xmlContent = '';
    if (file.exists) {
        file.open('r');
        xmlContent = file.read();
        file.close();
    }

    var abTag = new RegExp('<Artboard index="' + abIdx + '">([\\s\\S]*?)<\\/Artboard>', 'g');
    xmlContent = xmlContent.replace(abTag, '');

    var abXML = '  <Artboard index="' + abIdx + '">\n';
    for (var key in newVals) {
        var value = newVals[key].toString()
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;');
        abXML += '    <' + key + '>' + value + '</' + key + '>\n';
    }
    abXML += '  </Artboard>\n';

    if (xmlContent.indexOf('<GridSettings>') === -1) {
        xmlContent = '<GridSettings>\n' + abXML + '</GridSettings>';
    } else {
        xmlContent = xmlContent.replace('</GridSettings>', abXML + '</GridSettings>');
    }

    file.open('w');
    file.write(xmlContent);
    file.close();
}

// --- Helpers (pure units and layer logic) ---
function mmToPt(mm) { return mm * 2.83465; }

function findLayer(doc, name) {
    for (var i = 0; i < doc.layers.length; i++) {
        if (doc.layers[i].name === name) return doc.layers[i];
    }
    return null;
}

function deleteLayerByExactName(doc, name) {
    for (var i = doc.layers.length - 1; i >= 0; i--) {
        if (doc.layers[i].name === name) {
            doc.layers[i].locked = false;
            doc.layers[i].visible = true;
            doc.layers[i].remove();
        }
    }
}

// Robust path removal using only note matching
function removePathsForArtboard(layer, abIdx) {
    if (!layer) return;
    var abTag = " ab" + abIdx + " ";
    for (var i = layer.pathItems.length - 1; i >= 0; i--) {
        try {
            var p = layer.pathItems[i];
            if (p.note && p.note.indexOf(abTag) === 0) {
                p.remove();
            }
        } catch (e) {}
    }
}

function hasPathsForArtboard(layer, abIdx) {
    if (!layer) return false;
    var abTag = " ab" + abIdx + " ";
    try {
        for (var i = 0; i < layer.pathItems.length; i++) {
            var p = layer.pathItems[i];
            if (p.note && p.note.indexOf(abTag) === 0) return true;
        }
    } catch (e) {}
    return false;
}

function cleanupEmptyLayer(doc, layerName) {
    var layer = findLayer(doc, layerName);
    if (layer && layer.pathItems.length === 0) {
        try {
            layer.locked = false;
            layer.remove();
            return null;
        } catch (e) {}
    }
    return layer;
}

// --- Unit Parsing (with baseline default to pt!) ---
function parseValueWithUnit(inputStr) {
    if (typeof inputStr !== 'string') inputStr = String(inputStr);
    inputStr = inputStr.replace(/\s/g, '');
    var match = inputStr.match(/^(-?\d*\.?\d+)([a-zA-Z%]*)$/);
    if (!match) {
        var num = parseFloat(inputStr);
        return isNaN(num) ? NaN : mmToPt(num);
    }
    var value = parseFloat(match[1]);
    var unit = match[2].toLowerCase();
    if (isNaN(value)) return NaN;
    switch (unit) {
        case '':         return mmToPt(value); // Only parseValueWithUnitPt() changes this!
        case 'mm':       return mmToPt(value);
        case 'cm':       return mmToPt(value * 10);
        case 'in': case 'inch': return value * 72;
        case 'pt': case 'point': return value;
        case 'px': case 'pixel':
            var doc = app.activeDocument;
            return (doc.rulerUnits == RulerUnits.Pixels) ? value : value * 0.75;
        case 'pc': case 'pica': return value * 12;
        default:         return mmToPt(value);
    }
}

// --- baseline: always treat no unit as pt ---
function parseValueWithUnitPt(inputStr) {
    if (typeof inputStr !== 'string') inputStr = String(inputStr);
    inputStr = inputStr.replace(/\s/g, '');
    var match = inputStr.match(/^(-?\d*\.?\d+)([a-zA-Z%]*)$/);
    if (!match) {
        var num = parseFloat(inputStr);
        return isNaN(num) ? NaN : num; // If no unit, treat as points
    }
    var value = parseFloat(match[1]);
    var unit = match[2].toLowerCase();
    if (isNaN(value)) return NaN;
    switch (unit) {
        case '':
        case 'pt':
        case 'point':
            return value;
        case 'mm':       return mmToPt(value);
        case 'cm':       return mmToPt(value * 10);
        case 'in': case 'inch': return value * 72;
        case 'px': case 'pixel':
            var doc = app.activeDocument;
            return (doc.rulerUnits == RulerUnits.Pixels) ? value : value * 0.75;
        case 'pc': case 'pica': return value * 12;
        default:         return value; // Default to pt for unknown
    }
}

// Adds default unit if user omitted it (mm or pt, depending on field)
function addDefaultUnit(inputStr, fieldType) {
    if (typeof inputStr !== 'string') inputStr = String(inputStr);
    inputStr = inputStr.replace(/\s/g, '');
    var match = inputStr.match(/^(-?\d*\.?\d+)([a-zA-Z%]*)$/);
    if (match && match[2] === '') {
        if (fieldType === "baseline") return inputStr + "pt";
        else if (fieldType === "number") return inputStr;
        else return inputStr + "mm";
    }
    return inputStr;
}

function isMarginsChanged(current, loaded) {
    return current.top !== loaded.top ||
           current.bottom !== loaded.bottom ||
           current.left !== loaded.left ||
           current.right !== loaded.right ||
           current.columns !== loaded.columns ||
           current.rows !== loaded.rows ||
           current.vertGutter !== loaded.vertGutter ||
           current.horGutter !== loaded.horGutter;
}
function isBaselineChanged(current, loaded) {
    return current.baselineStart !== loaded.baselineStart ||
           current.baselineInc !== loaded.baselineInc;
}

// --- Guide Creation: NO RGB/stroke/fill code remains ---
// Margin "guide rectangle" is simply four guide lines (no color or stroke!).
function addMarginsAndColumnsToLayer(layer, ab, abIdx, top_pt, bottom_pt, left_pt, right_pt, columns, vertGutter_pt, rows, horGutter_pt) {
    var rect = ab.artboardRect;
    var left = rect[0], top = rect[1], right = rect[2], bottom = rect[3];
    var width = right - left, height = top - bottom;
    var abTag = " ab" + abIdx + " ";
    if (width - left_pt - right_pt <= 0 || height - top_pt - bottom_pt <= 0) return;

    // Rect margin guides: four sides
    if (top_pt > 0 || bottom_pt > 0 || left_pt > 0 || right_pt > 0) {
        // Top
        var topGuide = layer.pathItems.add();
        topGuide.setEntirePath([[left + left_pt, top - top_pt], [right - right_pt, top - top_pt]]);
        topGuide.guides = true;
        topGuide.note = abTag + "margin";
        // Bottom
        var bottomGuide = layer.pathItems.add();
        bottomGuide.setEntirePath([[left + left_pt, bottom + bottom_pt], [right - right_pt, bottom + bottom_pt]]);
        bottomGuide.guides = true;
        bottomGuide.note = abTag + "margin";
        // Left
        var leftGuide = layer.pathItems.add();
        leftGuide.setEntirePath([[left + left_pt, top - top_pt], [left + left_pt, bottom + bottom_pt]]);
        leftGuide.guides = true;
        leftGuide.note = abTag + "margin";
        // Right
        var rightGuide = layer.pathItems.add();
        rightGuide.setEntirePath([[right - right_pt, top - top_pt], [right - right_pt, bottom + bottom_pt]]);
        rightGuide.guides = true;
        rightGuide.note = abTag + "margin";
    }

    columns = Math.min(columns, 20);
    if (columns > 1) {
        var usableWidth = width - left_pt - right_pt;
        var colWidth = (usableWidth - (columns - 1) * vertGutter_pt) / columns;
        var x = left + left_pt;
        for (var c = 0; c < columns - 1; c++) {
            x += colWidth;
            var vGuide1 = layer.pathItems.add();
            vGuide1.setEntirePath([[x, top - top_pt], [x, bottom + bottom_pt]]);
            vGuide1.guides = true;
            vGuide1.note = abTag + "colg";
            x += vertGutter_pt;
            var vGuide2 = layer.pathItems.add();
            vGuide2.setEntirePath([[x, top - top_pt], [x, bottom + bottom_pt]]);
            vGuide2.guides = true;
            vGuide2.note = abTag + "colg";
        }
    }

    rows = Math.min(rows, 20);
    if (rows > 1) {
        var usableHeight = height - top_pt - bottom_pt;
        var rowHeight = (usableHeight - (rows - 1) * horGutter_pt) / rows;
        var y = top - top_pt;
        for (var r = 0; r < rows - 1; r++) {
            y -= rowHeight;
            var hGuide1 = layer.pathItems.add();
            hGuide1.setEntirePath([[left + left_pt, y], [right - right_pt, y]]);
            hGuide1.guides = true;
            hGuide1.note = abTag + "rowg";
            y -= horGutter_pt;
            var hGuide2 = layer.pathItems.add();
            hGuide2.setEntirePath([[left + left_pt, y], [right - right_pt, y]]);
            hGuide2.guides = true;
            hGuide2.note = abTag + "rowg";
        }
    }
}

function addBaselineGridToLayer(layer, ab, abIdx, top_pt, bottom_pt, left_pt, right_pt, baselineStart_pt, baselineInc_pt) {
    if (baselineInc_pt <= 0 || !layer) return;
    var rect = ab.artboardRect;
    var left = rect[0], top = rect[1], right = rect[2], bottom = rect[3];
    var abTag = " ab" + abIdx + " ";
    var y0 = top - top_pt - baselineStart_pt;
    var yEnd = bottom + bottom_pt;
    var count = 0, maxLines = 500;
    try {
        while (y0 > yEnd && count < maxLines) {
            var line = layer.pathItems.add();
            line.setEntirePath([[left + left_pt, y0], [right - right_pt, y0]]);
            line.guides = true;
            line.note = abTag + "baseline";
            y0 -= baselineInc_pt;
            count++;
        }
    } catch (e) {}
}

// --- Preview Globals and Config ---
var previewUndoCount = 0;
var isCommittingPreview = false;
var CFG = { is2020: parseInt(app.version) === 24 };
var previewDebounceTimer = null;
var isInitializing = false; // NEW: Track initial preview

// --- UI Dialog ---
function showMarginsDialog() {
    if (app.documents.length === 0) { alert("Please open a document first."); return; }
    var doc = app.activeDocument;
    var abIdx = doc.artboards.getActiveArtboardIndex();
    var defaults = {
        Top:12.7, Bottom:12.7, Left:12.7, Right:12.7,
        Columns:2, Rows:2, VertGutter:4.233, HorGutter:4.233,
        BaselineStart:0, BaselineInc:12
    };
    var settings = loadSettingsFromXML(settingsXMLPath, abIdx, defaults);

    var loaded_pt = {
        top: parseValueWithUnit(settings.Top),
        bottom: parseValueWithUnit(settings.Bottom),
        left: parseValueWithUnit(settings.Left),
        right: parseValueWithUnit(settings.Right),
        columns: parseInt(settings.Columns),
        rows: parseInt(settings.Rows),
        vertGutter: parseValueWithUnit(settings.VertGutter),
        horGutter: parseValueWithUnit(settings.HorGutter),
        baselineStart: parseValueWithUnit(settings.BaselineStart),
        baselineInc: parseValueWithUnitPt(settings.BaselineInc)
    };

    var w = new Window("dialog", "Margins, Grid & Baseline (for Artboard " + (abIdx + 1) + ")");
    w.orientation = "column";
    w.alignChildren = "fill";
    w.spacing = 15;
    w.margins = 20;

    var marginPanel = w.add("panel", undefined, "Margins");
    marginPanel.orientation = "column"; marginPanel.alignChildren = "fill";
    marginPanel.margins = 15; marginPanel.spacing = 10;

    var mRow1 = marginPanel.add("group");
    mRow1.orientation = "row"; mRow1.alignChildren = "center"; mRow1.spacing = 10;
    mRow1.add("statictext", undefined, "Top:").preferredSize.width = 60;
    var topInput = mRow1.add("edittext", undefined, settings.Top); topInput.preferredSize.width = 80;
    mRow1.add("statictext", undefined, "Left:").preferredSize.width = 60;
    var leftInput = mRow1.add("edittext", undefined, settings.Left); leftInput.preferredSize.width = 80;

    var mRow2 = marginPanel.add("group");
    mRow2.orientation = "row"; mRow2.alignChildren = "center"; mRow2.spacing = 10;
    mRow2.add("statictext", undefined, "Bottom:").preferredSize.width = 60;
    var bottomInput = mRow2.add("edittext", undefined, settings.Bottom); bottomInput.preferredSize.width = 80;
    mRow2.add("statictext", undefined, "Right:").preferredSize.width = 60;
    var rightInput = mRow2.add("edittext", undefined, settings.Right); rightInput.preferredSize.width = 80;

    var gridPanel = w.add("panel", undefined, "Grid");
    gridPanel.orientation = "column"; gridPanel.alignChildren = "fill";
    gridPanel.margins = 15; gridPanel.spacing = 10;

    var gRow1 = gridPanel.add("group");
    gRow1.orientation = "row"; gRow1.alignChildren = "center"; gRow1.spacing = 10;
    gRow1.add("statictext", undefined, "Columns:").preferredSize.width = 80;
    var colInput = gRow1.add("edittext", undefined, settings.Columns); colInput.preferredSize.width = 60;
    gRow1.add("statictext", undefined, "Vertical Gutter:").preferredSize.width = 100;
    var vertGutterInput = gRow1.add("edittext", undefined, settings.VertGutter); vertGutterInput.preferredSize.width = 80;

    var gRow2 = gridPanel.add("group");
    gRow2.orientation = "row"; gRow2.alignChildren = "center"; gRow2.spacing = 10;
    gRow2.add("statictext", undefined, "Rows:").preferredSize.width = 80;
    var rowInput = gRow2.add("edittext", undefined, settings.Rows); rowInput.preferredSize.width = 60;
    gRow2.add("statictext", undefined, "Horizontal Gutter:").preferredSize.width = 100;
    var horGutterInput = gRow2.add("edittext", undefined, settings.HorGutter); horGutterInput.preferredSize.width = 80;

    var basePanel = w.add("panel", undefined, "Baseline Grid");
    basePanel.orientation = "row"; basePanel.alignChildren = "center";
    basePanel.margins = 15; basePanel.spacing = 10;
    basePanel.add("statictext", undefined, "Start:").preferredSize.width = 80;
    var baselineStartInput = basePanel.add("edittext", undefined, settings.BaselineStart); baselineStartInput.preferredSize.width = 80;
    basePanel.add("statictext", undefined, "Increment:").preferredSize.width = 80;
    var baselineIncInput = basePanel.add("edittext", undefined, settings.BaselineInc); baselineIncInput.preferredSize.width = 80;
    baselineIncInput.helpTip = "Baseline increment (bare numbers = points, e.g. 12 = 12pt)";

    var applyAllGroup = w.add("group"); applyAllGroup.margins = [0, 10, 0, 0];
    var applyAllChk = applyAllGroup.add("checkbox", undefined, "Apply to all artboards");
    applyAllChk.value = (doc.artboards.length === 1);

    var previewGroup = w.add("group"); previewGroup.margins = [0, 10, 0, 0];
    var isPreview = previewGroup.add("checkbox", undefined, "Preview (Active Artboard Only)");
    isPreview.helpTip = "Toggle to see changes live (undoable)";
    if (CFG.is2020) {
        isPreview.enabled = false;
        isPreview.helpTip = "Preview disabled due to Illustrator bug";
    }

    var btnGroup = w.add("group"); btnGroup.orientation = "row"; btnGroup.alignment = "right";
    btnGroup.spacing = 10; btnGroup.margins = [0, 15, 0, 0];
    var cancelBtn = btnGroup.add("button", undefined, "Cancel", { name: "cancel" });
    var okBtn = btnGroup.add("button", undefined, "OK", { name: "ok" });
    cancelBtn.preferredSize.width = 80; okBtn.preferredSize.width = 80;

    // FIXED: Preview function with proper initial handling
    function previewChanges() {
        if (CFG.is2020 || !isPreview.value) return;
        try {
            var doc = app.activeDocument;
            var abIdx = doc.artboards.getActiveArtboardIndex();
            var ab = doc.artboards[abIdx];
            var top_pt = parseValueWithUnit(topInput.text), bottom_pt = parseValueWithUnit(bottomInput.text),
                left_pt = parseValueWithUnit(leftInput.text), right_pt = parseValueWithUnit(rightInput.text),
                cols = parseInt(colInput.text), rows = parseInt(rowInput.text),
                vertGutter_pt = parseValueWithUnit(vertGutterInput.text), horGutter_pt = parseValueWithUnit(horGutterInput.text),
                baselineStart_pt = parseValueWithUnit(baselineStartInput.text), baselineInc_pt = parseValueWithUnitPt(baselineIncInput.text);
            if (isNaN(top_pt) || isNaN(bottom_pt) || isNaN(left_pt) || isNaN(right_pt) ||
                isNaN(cols) || isNaN(rows) || isNaN(vertGutter_pt) || isNaN(horGutter_pt) ||
                isNaN(baselineStart_pt) || isNaN(baselineInc_pt) || top_pt < 0 || bottom_pt < 0 || left_pt < 0 ||
                right_pt < 0 || cols < 0 || rows < 0 || vertGutter_pt < 0 || horGutter_pt < 0 || baselineInc_pt < 0) { return; }

            cols = Math.min(cols, 20); rows = Math.min(rows, 20);
            var check = checkGridFit(abIdx, top_pt, bottom_pt, left_pt, right_pt, cols, rows, vertGutter_pt, horGutter_pt);
            if (check.failCol) vertGutter_pt = mmToPt(defaults.VertGutter);
            if (check.failRow) horGutter_pt = mmToPt(defaults.HorGutter);

            if (previewUndoCount > 0) {
                app.undo();
                previewUndoCount = 0;
            }

            var marginsLayer = findLayer(doc, "Margins and Columns");
            var wasLockedMargins = false;
            if (!marginsLayer) { marginsLayer = doc.layers.add(); marginsLayer.name = "Margins and Columns"; }
            else { wasLockedMargins = marginsLayer.locked; }
            marginsLayer.locked = false; marginsLayer.visible = true;

            var baselineLayer = findLayer(doc, "Baseline");
            var wasLockedBaseline = false;
            if (baselineLayer) { wasLockedBaseline = baselineLayer.locked; baselineLayer.locked = false; }

            removePathsForArtboard(marginsLayer, abIdx);
            if (baselineLayer) removePathsForArtboard(baselineLayer, abIdx);

            addMarginsAndColumnsToLayer(marginsLayer, ab, abIdx, top_pt, bottom_pt, left_pt, right_pt, cols, vertGutter_pt, rows, horGutter_pt);

            if (baselineInc_pt > 0) {
                if (!baselineLayer) {
                    baselineLayer = doc.layers.add(); baselineLayer.name = "Baseline"; wasLockedBaseline = false;
                }
                baselineLayer.visible = true;
                addBaselineGridToLayer(baselineLayer, ab, abIdx, top_pt, bottom_pt, left_pt, right_pt, baselineStart_pt, baselineInc_pt);
            }

            if (baselineLayer && baselineLayer.pathItems.length === 0) {
                baselineLayer.remove(); baselineLayer = null;
            }
            if (marginsLayer.pathItems.length === 0) {
                marginsLayer.remove(); marginsLayer = null;
            }

            // FIXED: Handle locking properly for initial vs subsequent previews
            if (!isCommittingPreview) {
                if (isInitializing) {
                    // For initial preview, we want layers to end up locked
                    if (marginsLayer) marginsLayer.locked = true;
                    if (baselineLayer) baselineLayer.locked = true;
                } else {
                    // For subsequent previews, restore the original lock state
                    if (marginsLayer) marginsLayer.locked = wasLockedMargins;
                    if (baselineLayer) baselineLayer.locked = wasLockedBaseline;
                }
            }
            app.redraw();
            previewUndoCount = 1;
        } catch (err) {}
    }

    function debouncedPreview() {
        if (previewDebounceTimer) {
            previewDebounceTimer.cancel();
        }
        previewDebounceTimer = app.setTimeout(function() {
            previewChanges();
        }, 300);
    }

    function setupInputListeners(input) {
        input.onChanging = function() { 
            if (isPreview.value) debouncedPreview();
        };
        input.onChange = function() { 
            if (isPreview.value) {
                if (previewDebounceTimer) {
                    previewDebounceTimer.cancel();
                }
                previewChanges(); 
            }
        };
    }
    setupInputListeners(topInput);
    setupInputListeners(bottomInput);
    setupInputListeners(leftInput);
    setupInputListeners(rightInput);
    setupInputListeners(colInput);
    setupInputListeners(rowInput);
    setupInputListeners(vertGutterInput);
    setupInputListeners(horGutterInput);
    setupInputListeners(baselineStartInput);
    setupInputListeners(baselineIncInput);

    isPreview.onClick = function() {
        if (isPreview.value) {
            previewChanges();
        } else {
            if (previewUndoCount > 0) {
                app.undo();
                previewUndoCount = 0;
                app.redraw();
            }
        }
    };
    
    cancelBtn.onClick = function() {
        if (previewUndoCount > 0) {
            app.undo();
            previewUndoCount = 0;
        }
        w.close();
    };
    
    w.onClose = function () { 
        // Do nothing - Cancel button handles all cleanup
    };

    okBtn.onClick = function() {
        var top_pt = parseValueWithUnit(topInput.text),
            bottom_pt = parseValueWithUnit(bottomInput.text),
            left_pt = parseValueWithUnit(leftInput.text),
            right_pt = parseValueWithUnit(rightInput.text),
            cols = parseInt(colInput.text), rows = parseInt(rowInput.text),
            vertGutter_pt = parseValueWithUnit(vertGutterInput.text),
            horGutter_pt = parseValueWithUnit(horGutterInput.text),
            baselineStart_pt = parseValueWithUnit(baselineStartInput.text),
            baselineInc_pt = parseValueWithUnitPt(baselineIncInput.text);
        if (isNaN(top_pt) || isNaN(bottom_pt) || isNaN(left_pt) || isNaN(right_pt) ||
            isNaN(cols) || isNaN(rows) || isNaN(vertGutter_pt) || isNaN(horGutter_pt) ||
            isNaN(baselineStart_pt) || isNaN(baselineInc_pt) ||
            top_pt < 0 || bottom_pt < 0 || left_pt < 0 || right_pt < 0 ||
            cols < 0 || rows < 0 || vertGutter_pt < 0 || horGutter_pt < 0 || baselineInc_pt < 0) {
            alert("Please enter valid (non-negative) numbers. Baseline increment can be 0 to skip baseline grid."); return;
        }
        
        var abIndices = [];
        if (applyAllChk.value) for (var i = 0; i < doc.artboards.length; i++) abIndices.push(i);
        else abIndices.push(abIdx);

        previewUndoCount = 0;

        var vertGutterOverride = {}, horGutterOverride = {}, warnedCol = false, warnedRow = false;
        for (var i = 0; i < abIndices.length; i++) {
            var check = checkGridFit(abIndices[i], top_pt, bottom_pt, left_pt, right_pt, cols, rows, vertGutter_pt, horGutter_pt);
            if (check.failCol) { if (!warnedCol) alert("Column/gutter combination doesn't fit on at least one artboard. Vertical gutter is reset to default."), warnedCol = true;
                vertGutterOverride[abIndices[i]] = mmToPt(defaults.VertGutter); }
            if (check.failRow) { if (!warnedRow) alert("Row/gutter combination doesn't fit on at least one artboard. Horizontal gutter is reset to default."), warnedRow = true;
                horGutterOverride[abIndices[i]] = mmToPt(defaults.HorGutter); }
        }
        
        if (applyAllChk.value) {
            deleteLayerByExactName(doc, "Margins and Columns");
            deleteLayerByExactName(doc, "Baseline");
        }
        
        var marginsLayer = findLayer(doc, "Margins and Columns");
        var baselineLayer = findLayer(doc, "Baseline");
        var current_pt = {
            top: top_pt, bottom: bottom_pt, left: left_pt, right: right_pt,
            columns: cols, rows: rows, vertGutter: vertGutter_pt, horGutter: horGutter_pt,
            baselineStart: baselineStart_pt, baselineInc: baselineInc_pt
        };
        
        for (var k = 0; k < abIndices.length; k++) {
            var idx = abIndices[k];
            var thisVertGutter_pt = vertGutterOverride[idx] !== undefined ? vertGutterOverride[idx] : vertGutter_pt;
            var thisHorGutter_pt = horGutterOverride[idx] !== undefined ? horGutterOverride[idx] : horGutter_pt;
            var ab = doc.artboards[idx];

            var updateMargins = applyAllChk.value || isMarginsChanged(current_pt, loaded_pt) ||
                               !marginsLayer || !hasPathsForArtboard(marginsLayer, idx);
            if (updateMargins) {
                if (!marginsLayer) { marginsLayer = doc.layers.add(); marginsLayer.name = "Margins and Columns"; }
                marginsLayer.locked = false; marginsLayer.visible = true;
                removePathsForArtboard(marginsLayer, idx);
                addMarginsAndColumnsToLayer(marginsLayer, ab, idx, top_pt, bottom_pt, left_pt, right_pt,
                                           cols, thisVertGutter_pt, rows, thisHorGutter_pt);
            }
            var updateBaseline = applyAllChk.value || isBaselineChanged(current_pt, loaded_pt) ||
                                 (baselineInc_pt > 0 && (!baselineLayer || !hasPathsForArtboard(baselineLayer, idx))) ||
                                 (baselineInc_pt === 0 && baselineLayer && hasPathsForArtboard(baselineLayer, idx));
            if (updateBaseline) {
                if (!baselineLayer) baselineLayer = findLayer(doc, "Baseline");
                if (baselineInc_pt > 0) {
                    if (!baselineLayer) { baselineLayer = doc.layers.add(); baselineLayer.name = "Baseline"; }
                    baselineLayer.locked = false; baselineLayer.visible = true;
                    removePathsForArtboard(baselineLayer, idx);
                    addBaselineGridToLayer(baselineLayer, ab, idx, top_pt, bottom_pt, left_pt, right_pt,
                                         baselineStart_pt, baselineInc_pt);
                } else {
                    if (baselineLayer) {
                        baselineLayer.locked = false;
                        removePathsForArtboard(baselineLayer, idx);
                        var hasOtherPaths = false;
                        for (var p = 0; p < baselineLayer.pathItems.length; p++) {
                            var pathNote = baselineLayer.pathItems[p].note || "";
                            if (pathNote.indexOf(" ab") === 0 && pathNote.indexOf(" ab" + idx + " ") !== 0) { hasOtherPaths = true; break; }
                        }
                        if (!hasOtherPaths && baselineLayer.pathItems.length === 0) {
                            baselineLayer.remove(); baselineLayer = null;
                        }
                    }
                }
            }
        }
        
        marginsLayer = cleanupEmptyLayer(doc, "Margins and Columns");
        baselineLayer = cleanupEmptyLayer(doc, "Baseline");
        if (marginsLayer) marginsLayer.locked = true;
        if (baselineLayer) baselineLayer.locked = true;
        
        for (var k = 0; k < abIndices.length; k++) {
            var idx = abIndices[k];
            var valuesToSave = {
                Top: addDefaultUnit(topInput.text, "margin"),
                Bottom: addDefaultUnit(bottomInput.text, "margin"),
                Left: addDefaultUnit(leftInput.text, "margin"),
                Right: addDefaultUnit(rightInput.text, "margin"),
                Columns: addDefaultUnit(colInput.text, "number"),
                Rows: addDefaultUnit(rowInput.text, "number"),
                VertGutter: addDefaultUnit(vertGutterInput.text, "gutter"),
                HorGutter: addDefaultUnit(horGutterInput.text, "gutter"),
                BaselineStart: addDefaultUnit(baselineStartInput.text, "margin"),
                BaselineInc: addDefaultUnit(baselineIncInput.text, "baseline")
            };
            if (vertGutterOverride[idx] !== undefined) valuesToSave.VertGutter = defaults.VertGutter.toString() + "mm";
            if (horGutterOverride[idx] !== undefined) valuesToSave.HorGutter = defaults.HorGutter.toString() + "mm";
            saveSettingsToXML(settingsXMLPath, idx, valuesToSave);
        }
        w.close();
    };
    
    function checkGridFit(abIdx, t_pt, b_pt, l_pt, r_pt, c, r, vg_pt, hg_pt) {
        var rect = doc.artboards[abIdx].artboardRect;
        var artW = rect[2] - rect[0] - l_pt - r_pt,
            artH = rect[1] - rect[3] - t_pt - b_pt;
        var minW = mmToPt(1), minH = mmToPt(1), failCol = false, failRow = false;
        if (c > 1) {
            var gutW = (c - 1) * vg_pt;
            if (artW <= gutW || (artW - gutW)/c < minW) failCol = true;
        }
        if (r > 1) {
            var gutH = (r - 1) * hg_pt;
            if (artH <= gutH || (artH - gutH)/r < minH) failRow = true;
        }
        return {failCol: failCol, failRow: failRow};
    }

    // FIXED: Proper initialization that results in locked layers with 1 undo
    if (!CFG.is2020) {
        var existingBaselineLayer = findLayer(doc, "Baseline");
        if (existingBaselineLayer) {
            existingBaselineLayer.locked = false;
            removePathsForArtboard(existingBaselineLayer, abIdx);
        }
        isPreview.value = true;
        isInitializing = true;  // Mark as initializing
        previewChanges();       // This creates guides and locks layers properly
        isInitializing = false; // Clear the flag
    }
    w.center(); w.show();
}
showMarginsDialog();

 

 

any help would be great 

 

best 

 

smyth 

Correct answer m1b

One other idea @SmythWharf — seeing as you are a mad-keen scripter *doffs cap* — if you really had your heart set on a preview function, you could actually draw it in a ScriptUI CustomView. I used one recently on a hobby project (see the file MK_UI.js — look for the drawPreviewTile function and its enclosing function). It's actually pretty easy to do once you understand the parts needed to have in place.

- Mark

4 replies

m1b
Community Expert
m1bCommunity ExpertCorrect answer
Community Expert
August 1, 2025

One other idea @SmythWharf — seeing as you are a mad-keen scripter *doffs cap* — if you really had your heart set on a preview function, you could actually draw it in a ScriptUI CustomView. I used one recently on a hobby project (see the file MK_UI.js — look for the drawPreviewTile function and its enclosing function). It's actually pretty easy to do once you understand the parts needed to have in place.

- Mark

Known Participant
August 1, 2025

Okay, wil take a look into this thank you

m1b
Community Expert
Community Expert
August 1, 2025

Actually now that I think about it, unlike Indesign, Illustrator scripting can perform DOM operations (such as drawing guides) even when a modal dialog is open. So I think you are hugely overthinking the "preview" function.

 

A simpler approach would be to change the behaviour of your OK button so that it draws the guides but doesn't dismiss the dialog. And the OK button first Undos the last grid (or deletes the grid) if there is one and then draws the guides. Then you get rid of the whole preview complexity including all the event handlers on the fields. Just a thought.

- Mark

CarlosCanto
Community Expert
Community Expert
August 1, 2025

That's what I originally suggested Mark. Undo is tricky ( although Sergey has made great use of it). I would place the guides in a separate layer. Then if a new preview needs to be made I would just delete that layer and make a new one...instead of using Undo.

m1b
Community Expert
Community Expert
August 1, 2025

Yes Carlos. I have found that Undo can be quite reliable in a script that isn't doing anything too "app-y" such as app.executeMenuCommands, app.copy/app.paste, and most of all app.redraw. I think it will be okay to use in SmythWharf's project if all the preview stuff is removed.

m1b
Community Expert
Community Expert
August 1, 2025

By the way, your code crashes for me on line 504:

previewDebounceTimer = app.setTimeout(function() {

because my app doesn't have `setTimeout` function. Are you using a plugin?

- Mark

m1b
Community Expert
Community Expert
August 1, 2025

Hi @SmythWharf every time you do 

app.redraw();

it causes Illustrator to add a new Undo record to the stack. Try removing those and see how it goes. (I know you have them there for a reason, but just to confirm that there is nothing else causing the extra Undo records disable them temporarily.)

 

There are other things that cause extra Undo records, usually because they internally cause a redraw.

- Mark

Known Participant
August 1, 2025

Okay will try