Is it possible to get a preview button on an panel?
Hello, I have a script for making guides, and would like to have a preview button similar to InDesign where you can see a preview.
I not sure if someone has all araboards that having a preview for all would run fast enough... so would it better to show only one artboard previewed... not sure
Anyhow the larger problem is what is the starting point for adding previews features.
Below is a sreenshot if the interface which is pretty self expanator


/**
* Illustrator Margins, Grid & Baseline Script [Mac-Ready]
* No ES6 code—fully ExtendScript compatible. Tested on Mac.
*/
var settingsXMLPath = Folder.myDocuments + "/IllustratorGridSettings.xml";
// --- XML functions (Modified to handle raw string values) ---
function loadSettingsFromXML(xmlFilePath, abIdx, defaults) {
// Helper to convert default numbers to strings with units
function stringifyDefaults(defaultsObj) {
var stringDefaults = {};
for (var key in defaultsObj) {
if (defaultsObj.hasOwnProperty(key)) {
if (key === "BaselineInc") {
// Baseline increment defaults to points
stringDefaults[key] = defaultsObj[key].toString() + "pt";
} else if (key === "Columns" || key === "Rows") {
// Numbers only, no units
stringDefaults[key] = defaultsObj[key].toString();
} else {
// Everything else defaults to mm
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 {
// Apply default with appropriate unit
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, '&').replace(/</g, '<').replace(/>/g, '>');
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 ---
function mmToPt(mm) { return mm * 2.83465; }
function ptToMm(pt) { return pt / 2.83465; }
function makeRGB(r, g, b) {
var color = new RGBColor();
color.red = r; color.green = g; color.blue = b;
return color;
}
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();
}
}
}
function removePathsForArtboard(layer, abIdx) {
var abTag = " ab" + abIdx + " ";
for (var i = layer.pathItems.length - 1; i >= 0; i--) {
var p = layer.pathItems[i];
if (p.note && p.note.indexOf(abTag) === 0) p.remove();
}
}
// --- Enhanced Unit Parsing Functions with Default Unit Addition ---
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 '':
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;
if (doc.rulerUnits == RulerUnits.Pixels) {
return value;
} else {
return value * 0.75;
}
case 'pc':
case 'pica':
return value * 12;
default:
return mmToPt(value);
}
}
function parseValueWithUnitPt(inputStr) {
return parseValueWithUnit(inputStr);
}
// --- Helper function to add default units to user input ---
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] === '') {
// No unit specified, add appropriate default
if (fieldType === "baseline") {
return inputStr + "pt";
} else if (fieldType === "number") {
return inputStr; // Columns/Rows don't need units
} else {
return inputStr + "mm"; // Default for margins and gutters
}
}
// Unit already specified or invalid input, return as-is
return inputStr;
}
// --- Guide Creation (works directly with points) ---
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 + " ";
var path = layer.pathItems.rectangle(top - top_pt, left + left_pt, width - left_pt - right_pt, height - top_pt - bottom_pt);
path.stroked = true; path.strokeColor = makeRGB(224, 0, 255); path.strokeWidth = 0.1; path.filled = false; path.guides = true; path.note = abTag + "margin";
var gridColor = makeRGB(0, 176, 255);
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.stroked = true; vGuide1.strokeColor = gridColor; vGuide1.strokeWidth = 0.1; vGuide1.filled = false; 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.stroked = true; vGuide2.strokeColor = gridColor; vGuide2.strokeWidth = 0.1; vGuide2.filled = false; vGuide2.guides = true; vGuide2.note = abTag + "colg";
}
}
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.stroked = true; hGuide1.strokeColor = gridColor; hGuide1.strokeWidth = 0.1; hGuide1.filled = false; 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.stroked = true; hGuide2.strokeColor = gridColor; hGuide2.strokeWidth = 0.1; hGuide2.filled = false; hGuide2.guides = true; hGuide2.note = abTag + "rowg";
}
}
}
// --- ENHANCED: Baseline Grid Creation (now handles 0 increment gracefully) ---
function addBaselineGridToLayer(layer, ab, abIdx, top_pt, bottom_pt, left_pt, right_pt, baselineStart_pt, baselineInc_pt) {
// If baseline increment is 0, skip creating baseline guides entirely
if (baselineInc_pt <= 0) {
return; // Exit function without creating any baseline guides
}
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;
while (y0 > yEnd) {
var line = layer.pathItems.add();
line.setEntirePath([[left + left_pt, y0], [right - right_pt, y0]]);
line.stroked = true; line.strokeColor = makeRGB(80, 80, 80); line.strokeWidth = 0.05; line.filled = false; line.guides = true; line.note = abTag + "baseline";
y0 -= baselineInc_pt;
}
}
// --- UI Dialog (IMPROVED LAYOUT) ---
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);
// Create a wider dialog with better spacing
var w = new Window("dialog", "Margins, Grid & Baseline (for Artboard " + (abIdx + 1) + ")");
w.orientation = "column";
w.alignChildren = "fill";
w.spacing = 15;
w.margins = 20;
// MARGINS PANEL - wider layout with more space
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;
// GRID PANEL - better organized layout
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;
// BASELINE PANEL - cleaner layout
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;
// APPLY TO ALL CHECKBOX
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);
// BUTTONS - better spacing
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;
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);
// --- ENHANCED VALIDATION: Allow 0 for baseline increment ---
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) { // Changed from baselineInc_pt <= 0 to baselineInc_pt < 0
alert("Please enter valid (non-negative) numbers. Baseline increment can be 0 to skip baseline grid.");
return;
}
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; var artH = rect[1] - rect[3] - t_pt - b_pt;
var minW = mmToPt(1), minH = mmToPt(1);
var 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};
}
var abIndices = [];
if (applyAllChk.value) { for (var i = 0; i < doc.artboards.length; i++) abIndices.push(i); } else { abIndices.push(abIdx); }
var vertGutterOverride = {}, horGutterOverride = {};
var 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);
}
}
// --- ENHANCED LAYER MANAGEMENT: Handle baseline layer conditionally ---
if (applyAllChk.value) {
deleteLayerByExactName(doc, "Margins and Columns");
// Only delete baseline layer if we're going to recreate it
if (baselineInc_pt > 0) {
deleteLayerByExactName(doc, "Baseline");
}
}
var marginsLayer = findLayer(doc, "Margins and Columns");
if (!marginsLayer) { marginsLayer = doc.layers.add(); marginsLayer.name = "Margins and Columns"; }
marginsLayer.locked = false; marginsLayer.visible = true;
// Only create baseline layer if we need it (baselineInc_pt > 0)
var baselineLayer = null;
if (baselineInc_pt > 0) {
baselineLayer = findLayer(doc, "Baseline");
if (!baselineLayer) { baselineLayer = doc.layers.add(); baselineLayer.name = "Baseline"; }
baselineLayer.locked = false; baselineLayer.visible = true;
}
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];
if (!applyAllChk.value) {
removePathsForArtboard(marginsLayer, idx);
// Only clean baseline layer if it exists
if (baselineLayer) {
removePathsForArtboard(baselineLayer, idx);
}
}
// Always create margins and columns
addMarginsAndColumnsToLayer(marginsLayer, ab, idx, top_pt, bottom_pt, left_pt, right_pt, cols, thisVertGutter_pt, rows, thisHorGutter_pt);
// Only create baseline grid if increment > 0 and layer exists
if (baselineInc_pt > 0 && baselineLayer) {
addBaselineGridToLayer(baselineLayer, ab, idx, top_pt, bottom_pt, left_pt, right_pt, baselineStart_pt, baselineInc_pt);
}
// --- SAVE LOGIC REMAINS THE SAME ---
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);
}
// Lock layers that were created
marginsLayer.locked = true;
if (baselineLayer) {
baselineLayer.locked = true;
}
w.close();
};
w.center();
w.show();
}
showMarginsDialog();