EasyColor SVG iconsets + SvgEasyColorStyler

What are "EasyColor" SVG iconsets ?

These are SVG Monochrome or TwoTone/DuoTone iconsets that have been optimized (for most of them) and where the one or two colors (+ opacity for twotone/duotone) have been factorized at the top of the iconset in two style nodes (one dark and one light):

<style id="dark">
	.outline-shape { stroke: #3895ff; fill: none; }
	.primary-shape { fill: #3895ff; stroke: none; }
	.secondary-outline-shape { stroke: #ffffff; opacity: 0.7; fill: none; }
	.secondary-shape { fill: #ffffff; opacity: 0.7; stroke: none; }
</style>
<style id="light">
	.outline-shape { stroke: #ffaa0d; fill: none; }
	.secondary-outline-shape { stroke: #ffaa0d; opacity: 0.35; fill: none; }
	.secondary-shape { fill: #ffaa0d; opacity: 0.35; stroke: none; }
</style>

The main interest is the ability to recolor the whole set just by changing these few lines at the start of the set.

Provided sets in the full zip (see below) are:

What is SvgEasyColorStyler

This is an Opus script addins that will make the recolor process easier.

For each provided set (see below), you'll be able to select one of its styles (outlined, filled, duotone, etc ...).
Then you can:

  • Chose each individual icon to check its rendering
  • Choose the main colors for dark mode and light mode, with a preview of the result
  • Switch the whole Opus interface to dark/light mode in order to better see how it renders
  • Copy the colors between main and secondary, and between dark/light modes
  • Export the resulting set with the customized colors
  • Export individual icons:
    • As standalone SVG files (including the proper styles properties)
    • As SVG code to the clipboard (so you can paste them in the SVG icon dialog for toolbar buttons)
    • As SVG code inside a JScript variable (so you can paste them into a jscript addins file and use them for the UI, using the DOpus.LoadImage(svgCodeAsVariable) method).

How to install?

Preliminary considerations

Since each set has its own way of working (some are relying on lines, others on filled shapes even for outline style), in order to be able to customize them with EasyColor Styler, each set is made of 3 files:

  • The "compiled" set (.dis file) with default colors (orange for light mode, blue for dark mode). This set can be installed directly within Directory Opus
  • The raw set as an XML file.
  • A companion "driver" file that will tell EasyColor Styler how to build new versions with your customized colors.
    The Styler tool also need all these files to be installed in a common place, with a specific folder structure.

Steps

  1. Download the full sets in the zip file

  2. Unzip the sets file to a location of your choosing on your hard drive. This will create the following folder structure (each terminal leaf folder contains the 3 files mentioned above: .dis, .xml and .driver.json):

     Src_Sets/
     β”œβ”€β”€ solar/
     β”‚   β”œβ”€β”€ solar_bold_ec/
     β”‚   β”œβ”€β”€ solar_broken_ec/
     β”‚   β”œβ”€β”€ solar_line_duotone_ec/
     β”‚   β”œβ”€β”€ solar_outline_ec/
     β”‚   β”œβ”€β”€ solar_linear_ec/
     β”‚   └── solar_bold_duotone_ec/
     β”œβ”€β”€ material/
     β”‚   β”œβ”€β”€ material_twotone/
     β”‚   β”œβ”€β”€ material_sharp/
     β”‚   β”œβ”€β”€ material_filled/
     β”‚   β”œβ”€β”€ material_round/
     β”‚   └── material_outlined/
     β”œβ”€β”€ tabler io/
     β”‚   β”œβ”€β”€ tablerio_outline/
     β”‚   └── tablerio_filled/
     β”œβ”€β”€ phosphor/
     β”‚   β”œβ”€β”€ phosphor_thin/
     β”‚   β”œβ”€β”€ phosphor_regular/
     β”‚   β”œβ”€β”€ phosphor_light/
     β”‚   β”œβ”€β”€ phosphor_fill/
     β”‚   β”œβ”€β”€ phosphor_duotone/
     β”‚   └── phosphor_bold/
     └── bootstrap/
         └── bootstrap_common/
    
  3. Install the script addin

  4. Configure the script addin and make the "Sets location" setting point to the location where you unziped the sets at step 2.

  5. Launch the editor with the new command SvgEasyColorStyler

Launch the Styler and enjoy.
Happy coloring !!

A few more screenshots:









Resources (V1.1):

Release History

V1.0

Styler Script code
//@@include inc_commonSAL.js
// SvgIconSetAdapter
// (c) 2026 Stephane

// This is a script for Directory Opus.
// See https://www.gpsoft.com.au/endpoints/redirect.php?page=scripts for development information.



// Called by Directory Opus to initialize the script
/** @type {OpusOnInit} */
function OnInit(initData)
{
	initData.name = "SvgEasyColorStyler";
	initData.version = "1.0";
	initData.copyright = "(c) 2026 Stephane";
//	initData.url = "https://resource.dopus.com/c/buttons-scripts/16";
	initData.desc = "Easy coloring of 'EasyColor' SVG iconsets";
	initData.default_enable = true;
	initData.min_version = "13.24.1";   // To enable alpha channel support in SVG palette colors, we need at least 13.24.1 (2024-06-19)

	// Script configuration
	// settings & defaults
	initData.config = DOpus.Create().OrderedMap();
	initData.config_desc = DOpus.Create().OrderedMap();
	initData.config_groups = DOpus.Create().OrderedMap();
	initData.config_group_order = DOpus.NewVector('General');

	var option_name = "";
	var option_group = "";

	// ===
	option_group = 'General';

	option_name = "Sets location";
	initData.config[option_name] = "/path/to/my/icons/sets/folder";
	initData.config_desc(option_name) = "Sets the path to acess the sets and the drivers files.\n";
	initData.config_groups(option_name) = option_group;

}

// Called to add commands to Opus
/** @type {OpusOnAddCommands} */
function OnAddCommands(addCmdData)
{
	var cmd = addCmdData.AddCommand();
	cmd.name = "SvgEasyColorStyler";
	cmd.method = "OnSvgEasyColorStyler";
	cmd.desc = "";
	cmd.label = "Svg EasyColor Styler";
	cmd.template = "";
	cmd.hide = false;
	cmd.icon = "script";
}


var currentDriver = null;	// Global state holding configuration mapping
var currentXmlDoc = null;	// xmlDoc associated with the currently loaded icon set
var lastCompiledPreviewSvg = "";	// SVG code for the currently displayed icon in preview
var singleSvgMenu = null;	// The popup menu for the actions on the currently displayed icon in preview


// Implement the SvgEasyColorStyler command
/** @type {OpusOnScriptCommand} */
function OnSvgEasyColorStyler(scriptCmdData)
{
	var dlg = DOpus.Dlg;
    dlg.window = scriptCmdData.func.sourcetab;
    dlg.template = "mainDlg";
    dlg.detach = true; // Essential to run an event loop
    
    dlg.Create();
    dlg.AddCustomMsg("SystemSettingChangedEvent");

	SetButtonIcons(dlg);

	singleSvgMenu = dlg.CreateMenu();
    singleSvgMenu.AddItem("Save SVG as ...", 1);
    singleSvgMenu.AddItem("Copy SVG to clipboard as text", 2);
    singleSvgMenu.AddItem("Copy SVG to clipboard as string var", 3);

	// 1. Initial catalog scan (Populates cbSet)
    var hasFamilies = ScanAndPopulateFamilies(dlg);
    
    if (hasFamilies) {
        // 2. Cascade trigger: Populate styles (cbStyle) for the first family
        var firstFamily = dlg.Control("cbSet").GetItemAt(dlg.Control("cbSet").value).name;
		DOpus.Output("First family = " + firstFamily);
        var hasStyles = ScanAndPopulateStyles(dlg, firstFamily);
        
        if (hasStyles) {
            // 3. Load the default driver configuration and apply UI rules
            LoadDriverFromUiSelection(dlg);
        }
    } else {
        DOpus.Output("Warning: No icon families found inside 'Src_Sets'. Check your configuration path.");
    }

	dlg.Show();
    
    // --- MAIN EVENT LOOP ---
    var msg;
    while (true) {
        msg = dlg.GetMsg();
        if (!msg.result) break; // Exit if window is closed
        
        // Handle interface interactions
        if (!HandleDialogEvents(dlg, msg)) {
            break; // Custom termination requested
        }
    }
}

function SetButtonIcons(dlg) {
    dlg.control("bClose").image = DOpus.LoadImage(svg_x_square_fill);
    dlg.control("bToggleDLmode").image = DOpus.LoadImage(svg_yin_yang);
    // dlg.control("bSetAction").image = DOpus.LoadImage(svg_download_twice_square_bold);
    dlg.control("bSetAction").image = DOpus.LoadImage(svg_download_twice_square_duo);
    // dlg.control("bIconAction").image = DOpus.LoadImage(svg_floppy_fill, 20, 20);
    dlg.control("bIconAction").image = DOpus.LoadImage(svg_floppy_disk_duotone, 20, 20);

    
}

// ============================================================================
// EVENT HANDLER ROUTINE
// ============================================================================
function HandleDialogEvents(dlg, msg)
{
	// debug event loop
	// DOpus.Output("MSG: e=" + msg.event + " / c=" + msg.control + " / v=" + msg.value + " / i=" + msg.index);
    if (msg.event !== "click" && msg.event !== "selchange" && msg.event !== "color" && msg.control !== "SystemSettingChangedEvent") return true;

    switch (msg.control) {
        
        case "bClose":
            dlg.EndDlg(0);
            return false;

		case "SystemSettingChangedEvent":
            DOpus.Output("Custom event 'SystemSettingChangedEvent' intercepted. Refreshing preview controls...");
            UpdateIconPreview(dlg);
            break;

        case "cbSet":
            if (msg.event === "selchange") {
                DOpus.Output("cbSet Selchange - New val = " + msg.value);
                var currentFamily = dlg.Control("cbSet").GetItemAt(dlg.Control("cbSet").value).name;
                
                if (ScanAndPopulateStyles(dlg, currentFamily)) {
                    LoadDriverFromUiSelection(dlg);
                }
            }
            else
                DOpus.Output("Untrapped cbSet event =>" + msg.event);
            break;
            
        case "cbStyle":
            if (msg.event === "selchange") {
                LoadDriverFromUiSelection(dlg);
            }
            break;

        case "cbIcon":
            if (msg.event === "selchange") {
                UpdateIconPreview(dlg);
            }
            break;

        // --- Palette Changes (Interactive color picking) ---
        case "palColor1Light":
        case "palColor1Dark":
        case "palColor2Light":
        case "palColor2Dark":
            if (msg.event === "color") {
                UpdateIconPreview(dlg);
            }
            break;

        // --- Horizontal Copies: Light <-> Dark (Full Clone including Alpha) ---
        case "bCopyColor1_L2D":
            dlg.Control("palColor1Dark").value = dlg.Control("palColor1Light").value;
            UpdateIconPreview(dlg);
            break;
            
        case "bCopyColor1_D2L":
            dlg.Control("palColor1Light").value = dlg.Control("palColor1Dark").value;
            UpdateIconPreview(dlg);
            break;
            
        case "bCopyColor2_L2D":
            // Straight overwrite: duplicates both color hex and alpha channel state
            dlg.Control("palColor2Dark").value = dlg.Control("palColor2Light").value;
            UpdateIconPreview(dlg);
            break;
            
        case "bCopyColor2_D2L":
            // Straight overwrite: duplicates both color hex and alpha channel state
            dlg.Control("palColor2Light").value = dlg.Control("palColor2Dark").value;
            UpdateIconPreview(dlg);
            break;

        // --- Vertical Copies: Main (C1) <-> Fill (C2) (Smart Alpha Preservation) ---
        case "bCopyColorLight_1_2": // Main down to Fill (Light)
            var srcLight = dlg.Control("palColor1Light").value; // #RRGGBB
            var targetLight = dlg.Control("palColor2Light").value; // #RRGGBBAA
            
            // Extract current target alpha or fallback to '7F' (50%) if layout was uninitialized
            var alphaLight = (targetLight.length === 9) ? targetLight.substring(7, 9) : "7F";
            
            dlg.Control("palColor2Light").value = srcLight + alphaLight;
            UpdateIconPreview(dlg);
            break;
            
        case "bCopyColorLight_2_1": // Fill up to Main (Light)
            var cLight2 = dlg.Control("palColor2Light").value; // #RRGGBBAA
            // Strip alpha completely to safely inject into standard RGB palette
            dlg.Control("palColor1Light").value = cLight2.substring(0, 7);
            UpdateIconPreview(dlg);
            break;

        case "bCopyColorDark_1_2": // Main down to Fill (Dark)
            var srcDark = dlg.Control("palColor1Dark").value; // #RRGGBB
            var targetDark = dlg.Control("palColor2Dark").value; // #RRGGBBAA
            
            var alphaDark = (targetDark.length === 9) ? targetDark.substring(7, 9) : "7F";
            
            dlg.Control("palColor2Dark").value = srcDark + alphaDark;
            UpdateIconPreview(dlg);
            break;
            
        case "bCopyColorDark_2_1": // Fill up to Main (Dark)
            var cDark2 = dlg.Control("palColor2Dark").value; // #RRGGBBAA
            dlg.Control("palColor1Dark").value = cDark2.substring(0, 7);
            UpdateIconPreview(dlg);
            break;

        // --- Major Engine Actions ---
        case "bSetAction":
            ExportMasterIconSet(dlg);
            break;

		case "bIconAction":
            if (msg.event === "click") {
                HandleIconActionMenu(dlg);
            }
            break;
            
		case "bToggleDLmode":
			var toggleCmd = DOpus.Create.Command();
			toggleCmd.SetModifier("toggle", "disable"); // Gère l'état visuel du bouton si nécessaire
			toggleCmd.RunCommand("Set DARKMODE=togglesmart");
            SetButtonIcons(dlg);
            UpdateIconPreview(dlg);
			break;
    }
    
    return true;
}


// ============================================================================
// CONTEXTUAL POPUP MENU FOR SINGLE ICON ACTIONS (Native Menu Object)
// ============================================================================
function HandleIconActionMenu(dlg)
{
    if (!currentDriver || !currentXmlDoc || !lastCompiledPreviewSvg || !singleSvgMenu) return;

    var cbIconCtrl = dlg.Control("cbIcon");
    if (cbIconCtrl.value < 0) return;
    var selectedIconName = cbIconCtrl.GetItemAt(cbIconCtrl.value).name;

    // 1. Display popup menu anchored under bIconAction button control
    var menuResult = singleSvgMenu.Show(dlg, "bIconAction", "r");
    
    // 2. Handle user cancelled menu
    if (menuResult === -1) return; 

    // 3. Process according to id selected by user
	DOpus.Output("User selection = " + menuResult);
    switch (menuResult) {
        case 1: // Save SVG as a file...
            var defaultName = sanitizeFilename(selectedIconName) + ".svg";
            var savePath = DOpus.Dlg.Save(
				"Export icon as SVG file", 
				defaultName, 
				dlg,
				"SVG Files (*.svg)!*.svg"
			);
            
            if (savePath) {
                try {
					ExtSystem.WriteFileUTF8(savePath, lastCompiledPreviewSvg, false);
                } catch(err) {
                    DOpus.Dlg.Request("Unable to write SVG file (" + savePath + "):\n" + err.description, "OK", "Erreur d'Γ©criture", dlg, "e");
                }
            }
            break;

        case 2: // Copy raw SVG code to clipboard
			DOpus.Output("Setting clipboard as ='" + lastCompiledPreviewSvg + "'");
            DOpus.SetClip(lastCompiledPreviewSvg);
            break;

        case 3: // Copy raw SVG code as a JSCript variable
            var jscriptVariable = "var svg_" + sanitizeFilename(selectedIconName).replace(/[^a-zA-Z0-9]/g, "_") + " = ";
            var lines = lastCompiledPreviewSvg.split("\n");
            for (var i = 0; i < lines.length; i++) {
                var cleanLine = lines[i].replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r/g, "");

				if (i > 0) jscriptVariable += "\t"; // After first line, we have an indent
                jscriptVariable += '"' + cleanLine + (i === lines.length - 1 ? '";' : '\\n" +\n');
            }
            DOpus.SetClip(jscriptVariable);
            break;
    }
}

// ============================================================================
// POPULATE FAMILIES (cbSet) AT STARTUP (Using Src_Sets isolation)
// ============================================================================
function ScanAndPopulateFamilies(dlg)
{
    // 1. Resolve alias and target the explicit source subdirectory
    var baseRoot = DOpus.FSUtil.Resolve(Script.config["Sets location"]);
    var srcSetsPath = DOpus.FSUtil.NewPath(baseRoot);
    srcSetsPath.Add("Src_Sets");

    if (!DOpus.FSUtil.Exists(srcSetsPath)) {
        DOpus.Output("Error: Sources subdirectory does not exist: " + srcSetsPath);
        return false;
    }

    var cbSetCtrl = dlg.Control("cbSet");
    cbSetCtrl.RemoveItem(-1);

    // 2. Read only inside Src_Sets
    var folderEnum = DOpus.FSUtil.ReadDir(srcSetsPath, false);
    while (!folderEnum.complete) {
        var item = folderEnum.Next();
        if (item.is_dir) {
            cbSetCtrl.AddItem(item.name);
			DOpus.Output("Adding set : " + item.name);
        }
    }

    if (cbSetCtrl.count > 0) {
        cbSetCtrl.value = 0;
        return true;
    }
    return false;
}

// ============================================================================
// POPULATE STYLES (cbStyle) BASED ON SELECTED FAMILY
// ============================================================================
function ScanAndPopulateStyles(dlg, familyName)
{
    var baseRoot = DOpus.FSUtil.Resolve(Script.config["Sets location"]);
    var familyPath = DOpus.FSUtil.NewPath(baseRoot);
    familyPath.Add("Src_Sets");
    familyPath.Add(familyName);

    var cbStyleCtrl = dlg.Control("cbStyle");
    cbStyleCtrl.RemoveItem(-1);

    if (!DOpus.FSUtil.Exists(familyPath)) return false;

    var folderEnum = DOpus.FSUtil.ReadDir(familyPath, false);
    while (!folderEnum.complete) {
        var item = folderEnum.Next();
        
        if (item.is_dir) {
            // Build path to the expected driver file inside the style folder
            var styleJsonPath = DOpus.FSUtil.NewPath(familyPath);
            styleJsonPath.Add(item.name);
            styleJsonPath.Add(item.name + ".driver.json");

            if (DOpus.FSUtil.Exists(styleJsonPath)) {
                cbStyleCtrl.AddItem(item.name);
            }
        }
    }

    if (cbStyleCtrl.count > 0) {
        cbStyleCtrl.value = 0;
        return true;
    }
    return false;
}

// ============================================================================
// LOAD ACTIVE DRIVER OBJECT AND REFRESH DEPENDENT ICONS
// ============================================================================
function LoadDriverFromUiSelection(dlg)
{
    var family = dlg.Control("cbSet").GetItemAt(dlg.Control("cbSet").value).name;
    var style  = dlg.Control("cbStyle").GetItemAt(dlg.Control("cbStyle").value).name;
    
    var baseRoot = DOpus.FSUtil.Resolve(Script.config["Sets location"]);
    var jsonPath = DOpus.FSUtil.NewPath(baseRoot);
    jsonPath.Add("Src_Sets");
    jsonPath.Add(family);
    jsonPath.Add(style);
    jsonPath.Add(style + ".driver.json");
    
    var result = GetObjectFromJsonFile(jsonPath);
    if (result.returnCode) {
        ApplyDriverRulesToUi(dlg, result.obj);
        
        // Populate the icon list container for the newly selected target environment
        ScanAndPopulateIcons(dlg, family, style);

		InitializePalettesFromXml(dlg);
		UpdateIconPreview(dlg);
        
        DOpus.Output("Successfully loaded driver & icons: " + family + " -> " + style);
        return true;
    }
    
    return false;
}

// ============================================================================
// POPULATE ICONS (cbIcon) BY PARSING THE COMPANION XML FILE
// ============================================================================
function ScanAndPopulateIcons(dlg, familyName, styleName)
{
    var baseRoot = DOpus.FSUtil.Resolve(Script.config["Sets location"]);
    var stylePath = DOpus.FSUtil.NewPath(baseRoot);
    stylePath.Add("Src_Sets");
    stylePath.Add(familyName);
    stylePath.Add(styleName);

    // The XML filename matches the style folder name (e.g., styleName.xml)
    var xmlPath = DOpus.FSUtil.NewPath(stylePath);
    xmlPath.Add(styleName + ".xml");

    var cbIconCtrl = dlg.Control("cbIcon");
    cbIconCtrl.RemoveItem(-1); // Clear previous entries safely

    if (!DOpus.FSUtil.Exists(xmlPath)) {
        DOpus.Output("Warning: Companion XML file not found: " + xmlPath);
        currentXmlDoc = null; // Invalidate current reference if missing
        return false;
    }

    // Instantiate and store into our shared DOM pointer
    currentXmlDoc = new ActiveXObject("Microsoft.XMLDOM");
    currentXmlDoc.async = false;
    
    // Enable structural whitespace preservation to avoid strict attribute formatting constraints
    currentXmlDoc.preserveWhiteSpace = true;
    
    currentXmlDoc.load(String(xmlPath));

    if (currentXmlDoc.parseError.errorCode !== 0) {
        DOpus.Output("Error parsing XML: " + currentXmlDoc.parseError.reason);
        currentXmlDoc = null; // Invalidate current reference if broken
        return false;
    }

    // Select all <icon> elements within the document
    var iconNodes = currentXmlDoc.getElementsByTagName("icon");
    if (!iconNodes || iconNodes.length === 0) return false;

    // Loop through nodes and collect the 'name' attribute
    for (var i = 0; i < iconNodes.length; i++) {
        var iconNode = iconNodes.item(i);
        if (iconNode) {
            var iconName = iconNode.getAttribute("name");
            if (iconName) {
                cbIconCtrl.AddItem(iconName);
            }
        }
    }

    if (cbIconCtrl.count > 0) {
        cbIconCtrl.value = 0; // Focus first icon by default
        return true;
    }
    return false;
}

// ============================================================================
// EXPORT COMPILED ICON SET
// ============================================================================
function ExportMasterIconSet(dlg)
{
    if (!currentDriver || !currentXmlDoc) return;

    // Deep clone the original XML document to prevent configuration drift in memory
    var exportXmlDoc = currentXmlDoc.cloneNode(true);
    var iconsetRoot = exportXmlDoc.documentElement;
    
    if (!iconsetRoot || iconsetRoot.nodeName !== "iconset") return;

    var originalName = iconsetRoot.getAttribute("name") || "";
    
    // Extract user-facing display name from dedicated child element node
    var dispNameNode = iconsetRoot.selectSingleNode("display_name");
    var originalDisplayName = dispNameNode ? dispNameNode.text : "";

    // 1. Load the customized multi-field metadata dialogue
    var metaDlg = DOpus.Dlg;
    metaDlg.window = dlg; // Keeps it strictly modal to the main container
    metaDlg.template = "dlgExportMeta";
    
    metaDlg.Create();
    metaDlg.Control("valName").value = originalName;
    metaDlg.Control("valDisplayName").value = originalDisplayName;

    if (metaDlg.RunDlg() !== 1) {
        DOpus.Output("Export cancelled by user.");
        return; 
    }

    // Capture explicit user-defined tokens
    var chosenName = metaDlg.Control("valName").value;
    var chosenDisplayName = metaDlg.Control("valDisplayName").value;

    if (!chosenName || !chosenDisplayName) {
        DOpus.Dlg.Request(
            "Both fields are required to compile the iconset.", 
			"OK",
		 	"Error with provided metadata",
			dlg,
			"e"
        );
        return;
    }

    // Trigger confirmation request if user leaves the reference identifier untouched
    if (chosenName === originalName) {
        var confirmResult = DOpus.Dlg.Request(
            "You did not change the internal set name identifier.\n\n" +
            "Do you confirm you want to keep '" + chosenName + "' ?\n" +
            "Note that it may overwrite an existing installed set within Directory Opus.",
			"Confirm Export|Abort",
			"Duplicate Name Warning",
			dlg,
			"w"
        );
        if (confirmResult !== 1) return;
    }

    // 5. Commit mutations safely onto the decoupled clone node tree structures
    iconsetRoot.setAttribute("name", chosenName);
    if (dispNameNode) {
        dispNameNode.text = chosenDisplayName;
    } else {
        // Fallback fallback mechanism if original node was completely empty
        var newDispNode = exportXmlDoc.createElement("display_name");
        newDispNode.text = chosenDisplayName;
        iconsetRoot.appendChild(newDispNode);
    }

    // --- Process Palette Configuration Styles ---
    var darkC1  = dlg.Control("palColor1Dark").value;
    var lightC1 = dlg.Control("palColor1Light").value;
    var darkC2  = currentDriver.hasColor2 ? dlg.Control("palColor2Dark").value : "";
    var lightC2 = currentDriver.hasColor2 ? dlg.Control("palColor2Light").value : "";

    var compiledDarkCss  = ProcessCssTemplate(currentDriver.templates.dark, darkC1, darkC2);
    var compiledLightCss = ProcessCssTemplate(currentDriver.templates.light, lightC1, lightC2);

    var styleDarkNode = exportXmlDoc.selectSingleNode("//style[@id='dark']");
    var styleLightNode = exportXmlDoc.selectSingleNode("//style[@id='light']");

    // Rewrite raw style blocks without wrapping into unwanted CDATA nodes
    if (styleDarkNode) {
        styleDarkNode.text = "\n" + compiledDarkCss + "\n\t";
    }
    if (styleLightNode) {
        styleLightNode.text = "\n" + compiledLightCss + "\n\t";
    }

    var baseRoot = DOpus.FSUtil.Resolve(Script.config["Sets location"]);
    var exportPath = DOpus.FSUtil.NewPath(baseRoot);
    exportPath.Add(sanitizeFilename(chosenName) + ".xml");

	try {
        exportXmlDoc.save(String(exportPath));
		// === Zipping the XML into a .dis archive
		DOpus.Output("Export XML OK >> Now zipping to .dis iconset file ...");
		var zipCommand = DOpus.Create.Command();
		zipCommand.SetModifier('noprogress');
		zipCommand.SetDest(baseRoot);
		zipCommand.AddFile(exportPath);
		zipCommand.RunCommand('Copy ARCHIVE CREATEFOLDER="' + sanitizeFilename(chosenName) + '.dis"');
		
        DOpus.Dlg.Request("Iconset compiled and exported successfully!\n\nDestination:\n" + exportPath, "OK", "Export Success", dlg, "i");
    }
    catch (e) {
        DOpus.Dlg.Request("Failed to save the target XML file:\n" + e.description, "OK", "Disk Write Protected Error", dlg, "e");
    }
}


function ProcessCssTemplate(templateStr, color1, color2WithAlpha)
{
	// DOpus.Output("ProcessCssTemplate: color1=" + color1 + " / color2alpha=" + color2WithAlpha);
    var c1 = color1;
    var c2 = color2WithAlpha.substring(0, 7);
    var opacity = "1.0";
    
    if (currentDriver.color2HasOpacity && color2WithAlpha.length === 9) {
        var alphaHex = color2WithAlpha.substring(7, 9);
        opacity = (parseInt(alphaHex, 16) / 255).toFixed(2);
    }
    
    var output = templateStr;
    output = output.replace(/%COLOR1%/g, c1);
    output = output.replace(/%COLOR2%/g, c2);
    output = output.replace(/%OPACITY2%/g, opacity);
    
    return output;
}

// ============================================================================
// UPDATE SVG PREVIEW CONTROLS (Using single memory DOM parser instances)
// ============================================================================
function UpdateIconPreview(dlg)
{
    if (!currentDriver || !currentXmlDoc) return;

    var cbIconCtrl = dlg.Control("cbIcon");
    if (cbIconCtrl.value < 0) return;

	var selectedIconName = cbIconCtrl.GetItemAt(cbIconCtrl.value).name;

	var iconNode = null;
	// Retrieve all icon elements flat and find the matching node via standard loop to avoid MSXML XPath whitespace crash
	var allIconsList = currentXmlDoc.getElementsByTagName("icon");
	for (var j = 0; j < allIconsList.length; j++) {
		if (allIconsList.item(j).getAttribute("name") === selectedIconName) {
			iconNode = allIconsList.item(j);
			break;
		}
	}

	if (iconNode == null) return;

    var svgNode = iconNode.selectSingleNode("svg");
    if (!svgNode) return;

    var rawSvgText = svgNode.xml;

    // 1. Gather values from UI palette controls
    var darkC1  = dlg.Control("palColor1Dark").value;
    var lightC1 = dlg.Control("palColor1Light").value;
    var darkC2  = currentDriver.hasColor2 ? dlg.Control("palColor2Dark").value : "";
    var lightC2 = currentDriver.hasColor2 ? dlg.Control("palColor2Light").value : "";

    // 2. Compile CSS templates for each specific theme target
    var compiledDarkCss  = ProcessCssTemplate(currentDriver.templates.dark, darkC1, darkC2);
    var compiledLightCss = ProcessCssTemplate(currentDriver.templates.light, lightC1, lightC2);

    // 3. Build native Opus style tags with matching IDs for the live Preview
    var opusStylesPayload = "\n" +
                            "\t<style id=\"dark\">\n" + compiledDarkCss + "\n\t</style>" +
                            "\n\t<style id=\"light\">\n" + compiledLightCss + "\n\t</style>\n";

    // 4. Extract the original base styles from the XML root to feed the "Original" view
    var originalDarkNode = currentXmlDoc.selectSingleNode("//style[@id='dark']");
    var originalLightNode = currentXmlDoc.selectSingleNode("//style[@id='light']");
    
    var originalStylesPayload = "";
    if (originalDarkNode)  originalStylesPayload += "\n\t" + originalDarkNode.xml;
    if (originalLightNode) originalStylesPayload += "\n\t" + originalLightNode.xml;

    // 5. Inject respective stylesheets right after the opening <svg> tag
    var finalPreviewSvg  = rawSvgText.replace(/(<svg[^>]*>)/i, "$1" + opusStylesPayload);
    var finalOriginalSvg = rawSvgText.replace(/(<svg[^>]*>)/i, "$1" + originalStylesPayload);

    // --- HELPER INTERNAL FUNCTION: Force fixed high-res dimensions for Directory Opus image engine ---
    function forceSvgDimensions(svgString) {
        var svgOpeningMatch = svgString.match(/<svg[^>]*>/i);
        if (svgOpeningMatch) {
            var cleanTag = svgOpeningMatch[0];
            
            // On s'assure que width/height sont prΓ©cΓ©dΓ©s par un espace (\s+) 
            // et SURTOUT pas par un tiret.
            cleanTag = cleanTag.replace(/\s+width="[^"]*"/gi, "");
            cleanTag = cleanTag.replace(/\s+height="[^"]*"/gi, "");
            
            // Injecte proprement la taille fixe de preview
            cleanTag = cleanTag.replace(/(<svg)/i, '$1 width="256" height="256"');
            return svgString.replace(/<svg[^>]*>/i, cleanTag);
        }
        return svgString;
    }

    // Apply scaling hack to layout previews smoothly
    finalPreviewSvg  = forceSvgDimensions(finalPreviewSvg);
    finalOriginalSvg = forceSvgDimensions(finalOriginalSvg);

    // Backing up compiled preview for export purposes (keeps pristine file headers)
    lastCompiledPreviewSvg = rawSvgText.replace(/(<svg[^>]*>)/i, "$1" + opusStylesPayload);

    // 6. Convert SVG strings into native Opus Image objects
    // DOpus.Output("finalOriginalSvg=" + finalOriginalSvg);	// DEBUG ONLY
	// DOpus.Output("finalPreviewSvg=" + finalPreviewSvg);	// DEBUG ONLY
    dlg.Control("sOriginal").label = DOpus.LoadImage(finalOriginalSvg);
    dlg.Control("sPreview").label  = DOpus.LoadImage(finalPreviewSvg);
}


// ============================================================================
// ARCHITECTURAL UTILITIES & INI PLUMBING
// ============================================================================
function ApplyDriverRulesToUi(dlg, driverObj)
{
    currentDriver = driverObj;

	var disableColor1 = currentDriver.hasColor1;
    var disableColor2 = currentDriver.hasColor2;

	// Dynamically adjust configurations via driver definitions without code branching
    dlg.Control("palColor1Light").enabled	= disableColor1;
    dlg.Control("palColor1Dark").enabled	= disableColor1;
    dlg.Control("lblColor1").label			= currentDriver.uiSchema.color1Label;

    dlg.Control("palColor2Light").enabled	= disableColor2;
    dlg.Control("palColor2Dark").enabled	= disableColor2;
    dlg.Control("lblColor2").label			= currentDriver.uiSchema.color2Label;

    // Toggle horizontal translation and vertical structural sync tools layout availability
    dlg.Control("bCopyColorLight_1_2").enabled = disableColor2;
    dlg.Control("bCopyColorLight_2_1").enabled = disableColor2;
    dlg.Control("bCopyColorDark_1_2").enabled  = disableColor2;
    dlg.Control("bCopyColorDark_2_1").enabled  = disableColor2;
    
    dlg.Control("bCopyColor2_L2D").enabled     = disableColor2;
    dlg.Control("bCopyColor2_D2L").enabled     = disableColor2;
}
// ============================================================================
// INITIALIZE PALETTES WITH COLORS FOUND IN LOADED XML
// ============================================================================
function InitializePalettesFromXml(dlg)
{
    if (!currentXmlDoc || !currentDriver) return;

    var styleDarkNode = currentXmlDoc.selectSingleNode("//style[@id='dark']");
    var styleLightNode = currentXmlDoc.selectSingleNode("//style[@id='light']");

    // Fallbacks colors in case XML has no color
    var defaultColor1 = "#FFFFFF";
    var defaultColor2 = "#7F7F7F7F";

    // Regex to capture the first hex
    // var colorRegex = /#([0-9a-fA-F]{6,8})/;\n
    var colorRegex = /#([0-9a-fA-F]{6,8})/;

    var darkText = styleDarkNode ? styleDarkNode.text : "";
    var lightText = styleLightNode ? styleLightNode.text : "";

    // --- Processing Dark mode ---
    var matchDark = darkText.match(/#[0-9a-fA-F]{6,8}/g);
    if (matchDark && matchDark.length > 0) {
        dlg.Control("palColor1Dark").value = matchDark[0].substring(0, 7);
        if (currentDriver.hasColor2) {
            // If another color is found we use it, otherwise fallback
            dlg.Control("palColor2Dark").value = (matchDark.length > 1) ? NormalizeColorHex(matchDark[1]) : NormalizeColorHex(matchDark[0]) + "7F";
        }
    } else {
        dlg.Control("palColor1Dark").value = "#000000";
        if (currentDriver.hasColor2) dlg.Control("palColor2Dark").value = defaultColor2;
    }

    // --- Processing Light mode ---
    var matchLight = lightText.match(/#[0-9a-fA-F]{6,8}/g);
    if (matchLight && matchLight.length > 0) {
        dlg.Control("palColor1Light").value = matchLight[0].substring(0, 7);
        if (currentDriver.hasColor2) {
            dlg.Control("palColor2Light").value = (matchLight.length > 1) ? NormalizeColorHex(matchLight[1]) : NormalizeColorHex(matchLight[0]) + "7F";
        }
    } else {
        dlg.Control("palColor1Light").value = defaultColor1;
        if (currentDriver.hasColor2) dlg.Control("palColor2Light").value = defaultColor2;
    }
}

function NormalizeColorHex(hexStr) {
    if (!hexStr) return "#7F7F7F";
    return hexStr;
}

// ============================================================================
// GLOBAL EVENT: BROADCAST CUSTOM THEME SWAP EVENT
// ============================================================================
/** @type {OpusOnSystemSettingChange} */
function OnSystemSettingChange(settingChangeData)
{
    // DOpus.Output("System setting change detected. Broadcasting custom event...");
    DOpus.SendCustomMsg("SystemSettingChangedEvent");
}


var svg_download_twice_square_bold = "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n" +
	"	<style id=\"dark\">\n" +
	".primary-shape { fill: #3895ff; stroke: none; }\n" +
	".outline-shape { stroke: #3895ff; fill: none; }\n" +
	"	</style>\n" +
	"	<style id=\"light\">\n" +
	".primary-shape { fill: #000080; stroke: none; }\n" +
	".outline-shape { stroke: #000080; fill: none; }\n" +
	"	</style>\n" +
	"\n" +
	"                <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M3.46447 3.46447C2 4.92893 2 7.28595 2 12C2 16.714 2 19.0711 3.46447 20.5355C4.92893 22 7.28595 22 12 22C16.714 22 19.0711 22 20.5355 20.5355C22 19.0711 22 16.714 22 12C22 7.28595 22 4.92893 20.5355 3.46447C19.0711 2 16.714 2 12 2C7.28595 2 4.92893 2 3.46447 3.46447ZM9.25 7C9.25 6.58579 8.91421 6.25 8.5 6.25C8.08579 6.25 7.75 6.58579 7.75 7V11.9285L6.57617 10.5199C6.31099 10.2017 5.83807 10.1587 5.51986 10.4238C5.20165 10.689 5.15866 11.1619 5.42383 11.4801L7.92383 14.4801C8.06633 14.6511 8.27742 14.75 8.5 14.75C8.72259 14.75 8.93367 14.6511 9.07617 14.4801L11.5762 11.4801C11.8413 11.1619 11.7983 10.689 11.4801 10.4238C11.1619 10.1587 10.689 10.2017 10.4238 10.5199L9.25 11.9285V7ZM15.5 6.25C15.9142 6.25 16.25 6.58579 16.25 7V11.9285L17.4238 10.5199C17.689 10.2017 18.1619 10.1587 18.4801 10.4238C18.7983 10.689 18.8413 11.1619 18.5762 11.4801L16.0762 14.4801C15.9337 14.6511 15.7226 14.75 15.5 14.75C15.2774 14.75 15.0663 14.6511 14.9238 14.4801L12.4238 11.4801C12.1587 11.1619 12.2017 10.689 12.5199 10.4238C12.8381 10.1587 13.311 10.2017 13.5762 10.5199L14.75 11.9285V7C14.75 6.58579 15.0858 6.25 15.5 6.25ZM6 16.25C5.58579 16.25 5.25 16.5858 5.25 17C5.25 17.4142 5.58579 17.75 6 17.75H18C18.4142 17.75 18.75 17.4142 18.75 17C18.75 16.5858 18.4142 16.25 18 16.25H6Z\" class=\"primary-shape\"/>\n" +
	"                </svg>";

var svg_download_twice_square_duo = "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n" +
	"	<style id=\"dark\">\n" +
	".primary-shape { fill: #3895ff; opacity: 0.9; stroke: none; }\n" +
	".outline-shape { stroke: #3895ff; fill: none; }\n" +
	"	</style>\n" +
	"	<style id=\"light\">\n" +
	".primary-shape { fill: #000080; stroke: none; }\n" +
	".outline-shape { stroke: #000080; fill: none; }\n" +
	"	</style>\n" +
	"\n" +
	"                <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M3.46447 3.46447C2 4.92893 2 7.28595 2 12C2 16.714 2 19.0711 3.46447 20.5355C4.92893 22 7.28595 22 12 22C16.714 22 19.0711 22 20.5355 20.5355C22 19.0711 22 16.714 22 12C22 7.28595 22 4.92893 20.5355 3.46447C19.0711 2 16.714 2 12 2C7.28595 2 4.92893 2 3.46447 3.46447ZM9.25 7C9.25 6.58579 8.91421 6.25 8.5 6.25C8.08579 6.25 7.75 6.58579 7.75 7V11.9285L6.57617 10.5199C6.31099 10.2017 5.83807 10.1587 5.51986 10.4238C5.20165 10.689 5.15866 11.1619 5.42383 11.4801L7.92383 14.4801C8.06633 14.6511 8.27742 14.75 8.5 14.75C8.72259 14.75 8.93367 14.6511 9.07617 14.4801L11.5762 11.4801C11.8413 11.1619 11.7983 10.689 11.4801 10.4238C11.1619 10.1587 10.689 10.2017 10.4238 10.5199L9.25 11.9285V7ZM15.5 6.25C15.9142 6.25 16.25 6.58579 16.25 7V11.9285L17.4238 10.5199C17.689 10.2017 18.1619 10.1587 18.4801 10.4238C18.7983 10.689 18.8413 11.1619 18.5762 11.4801L16.0762 14.4801C15.9337 14.6511 15.7226 14.75 15.5 14.75C15.2774 14.75 15.0663 14.6511 14.9238 14.4801L12.4238 11.4801C12.1587 11.1619 12.2017 10.689 12.5199 10.4238C12.8381 10.1587 13.311 10.2017 13.5762 10.5199L14.75 11.9285V7C14.75 6.58579 15.0858 6.25 15.5 6.25ZM6 16.25C5.58579 16.25 5.25 16.5858 5.25 17C5.25 17.4142 5.58579 17.75 6 17.75H18C18.4142 17.75 18.75 17.4142 18.75 17C18.75 16.5858 18.4142 16.25 18 16.25H6Z\" class=\"primary-shape\"/>\n" +
	"                </svg>";

var svg_power = "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n" +
	"	<style id=\"dark\">\n" +
	".primary-shape { fill: #91eea0; stroke: none; }\n" +
	".outline-shape { stroke: #91eea0; fill: none; }\n" +
	"	</style>\n" +
	"	<style id=\"light\">\n" +
	".primary-shape { fill: #91eea0; stroke: none; }\n" +
	".outline-shape { stroke: #91eea0; fill: none; }\n" +
	"	</style>\n" +
	"\n" +
	"                <path d=\"M12 2V6\" stroke-width=\"1.5\" stroke-linecap=\"round\" class=\"outline-shape\" fill=\"none\"/>\n" +
	"                <path d=\"M12.75 2.75C12.75 2.33579 12.4142 2 12 2C11.5858 2 11.25 2.33579 11.25 2.75V6.75C11.25 7.16421 11.5858 7.5 12 7.5C12.4142 7.5 12.75 7.16421 12.75 6.75V2.75Z\" class=\"primary-shape\"/>\n" +
	"                <path d=\"M8.7919 5.14692C9.17345 4.98571 9.35208 4.54571 9.19087 4.16416C9.02966 3.7826 8.58966 3.60398 8.2081 3.76519C4.70832 5.24386 2.25 8.70905 2.25 12.7501C2.25 18.1349 6.61522 22.5001 12 22.5001C17.3848 22.5001 21.75 18.1349 21.75 12.7501C21.75 8.70905 19.2917 5.24386 15.7919 3.76519C15.4103 3.60398 14.9703 3.7826 14.8091 4.16416C14.6479 4.54571 14.8265 4.98571 15.2081 5.14692C18.1722 6.39927 20.25 9.33293 20.25 12.7501C20.25 17.3065 16.5563 21.0001 12 21.0001C7.44365 21.0001 3.75 17.3065 3.75 12.7501C3.75 9.33293 5.82779 6.39927 8.7919 5.14692Z\" class=\"primary-shape\"/>\n" +
	"                </svg>";

var svg_floppy_fill = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-floppy-fill\" viewBox=\"0 0 16 16\">\n" +
	"	<style id=\"dark\">\n" +
	".primary-shape { fill: #3895ff; stroke: none; }\n" +
	".outline-shape { stroke: #3895ff; fill: none; }\n" +
	"	</style>\n" +
	"	<style id=\"light\">\n" +
	".primary-shape { fill: #000080; stroke: none; }\n" +
	".outline-shape { stroke: #000080; fill: none; }\n" +
	"	</style>\n" +
	"\n" +
	"              <path d=\"M0 1.5A1.5 1.5 0 0 1 1.5 0H3v5.5A1.5 1.5 0 0 0 4.5 7h7A1.5 1.5 0 0 0 13 5.5V0h.086a1.5 1.5 0 0 1 1.06.44l1.415 1.414A1.5 1.5 0 0 1 16 2.914V14.5a1.5 1.5 0 0 1-1.5 1.5H14v-5.5A1.5 1.5 0 0 0 12.5 9h-9A1.5 1.5 0 0 0 2 10.5V16h-.5A1.5 1.5 0 0 1 0 14.5z\" class=\"primary-shape\"/>\n" +
	"              <path d=\"M3 16h10v-5.5a.5.5 0 0 0-.5-.5h-9a.5.5 0 0 0-.5.5zm9-16H4v5.5a.5.5 0 0 0 .5.5h7a.5.5 0 0 0 .5-.5zM9 1h2v4H9z\" class=\"primary-shape\"/>\n" +
	"            </svg>";

    var svg_floppy_disk_duotone = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 256\">\n" +
	"	<style id=\"dark\">\n" +
	".primary-shape { fill: #3895ff; stroke: none; }\n" +
	".outline-shape { stroke: #3895ff; fill: none; }\n" +
	".secondary-shape { fill: #3895ff; opacity: 0.74; stroke: none; }\n" +
	"	</style>\n" +
	"	<style id=\"light\">\n" +
	".primary-shape { fill: #000073; stroke: none; }\n" +
	".outline-shape { stroke: #000073; fill: none; }\n" +
	".secondary-shape { fill: #000073; opacity: 0.51; stroke: none; }\n" +
	"	</style>\n" +
	"\n" +
	"              <path fill=\"none\" d=\"M0 0h256v256H0z\"/>\n" +
	"              <path d=\"m213.66 77.66-35.32-35.32a8 8 0 0 0-5.65-2.34H48a8 8 0 0 0-8 8v160a8 8 0 0 0 8 8h32v-64a8 8 0 0 1 8-8h80a8 8 0 0 1 8 8v64h32a8 8 0 0 0 8-8V83.31a8 8 0 0 0-2.34-5.65\" class=\"secondary-shape\"/>\n" +
	"              <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"16\" d=\"M216 83.31V208a8 8 0 0 1-8 8H48a8 8 0 0 1-8-8V48a8 8 0 0 1 8-8h124.69a8 8 0 0 1 5.65 2.34l35.32 35.32a8 8 0 0 1 2.34 5.65\" class=\"outline-shape\"/>\n" +
	"              <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"16\" d=\"M80 216v-64a8 8 0 0 1 8-8h80a8 8 0 0 1 8 8v64M152 72H96\" class=\"outline-shape\"/>\n" +
	"            </svg>";

var svg_x_square_fill = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-x-square-fill\" viewBox=\"0 0 16 16\">\n" +
	"	<style id=\"dark\">\n" +
	".primary-shape { fill: #00ca00; stroke: none; }\n" +
	".outline-shape { stroke: #00ca00; fill: none; }\n" +
	"	</style>\n" +
	"	<style id=\"light\">\n" +
	".primary-shape { fill: #007400; stroke: none; }\n" +
	".outline-shape { stroke: #007400; fill: none; }\n" +
	"	</style>\n" +
	"\n" +
	"              <path d=\"M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm3.354 4.646L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 1 1 .708-.708\" class=\"primary-shape\"/>\n" +
	"            </svg>";

var svg_yin_yang = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-yin-yang\" viewBox=\"0 0 16 16\">\n" +
	"	<style id=\"dark\">\n" +
	".primary-shape { fill: #ffffff; stroke: none; }\n" +
	".outline-shape { stroke: #ffffff; fill: none; }\n" +
	"	</style>\n" +
	"	<style id=\"light\">\n" +
	".primary-shape { fill: #000000; stroke: none; }\n" +
	".outline-shape { stroke: #000000; fill: none; }\n" +
	"	</style>\n" +
	"\n" +
	"              <path d=\"M9.167 4.5a1.167 1.167 0 1 1-2.334 0 1.167 1.167 0 0 1 2.334 0\" class=\"primary-shape\"/>\n" +
	"              <path d=\"M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0M1 8a7 7 0 0 1 7-7 3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 0 0 7 7 7 0 0 1-7-7m7 4.667a1.167 1.167 0 1 1 0-2.334 1.167 1.167 0 0 1 0 2.334\" class=\"primary-shape\"/>\n" +
	"            </svg>";

//			<control arrow="yes" height="14" name="bIconAction" title="πŸ’Ύ " type="button" width="44" x="350" y="146" />

var svg_floppy_duo		= '<svg fill="#3895ff" width="800px" height="800px" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">' +
	'	<path d="M216,91.3136V208.00012a8.00009,8.00009,0,0,1-8,8l-32,.0083v-64a8.00009,8.00009,0,0,0-8-8H88a8.00009,8.00009,0,0,0-8,8v64l-32-.0083a8.00009,8.00009,0,0,1-8-8v-160a7.99993,7.99993,0,0,1,8-8H164.68628a8.00052,8.00052,0,0,1,5.657,2.343l43.31348,43.31372A7.99918,7.99918,0,0,1,216,91.3136Z" opacity="0.6" fill="#3895ff"/>' +
	'	<path d="M219.31445,79.99963,176.001,36.68811a15.88944,15.88944,0,0,0-11.31446-4.68848H48a16.01833,16.01833,0,0,0-16,16v160a16.01833,16.01833,0,0,0,16,16H79.82617c.05835.00122.11524.00879.17383.00879s.11548-.00757.17383-.00879h95.65234c.05835.00122.11524.00879.17383.00879s.11548-.00757.17383-.00879H208a16.01833,16.01833,0,0,0,16-16V91.31409A15.898,15.898,0,0,0,219.31445,79.99963ZM168,207.99963H88V152.00842h80ZM208,91.31409V207.99963H184V152.00842a16.01833,16.01833,0,0,0-16-16H88a16.01833,16.01833,0,0,0-16,16v55.99121H48v-160l116.68555.001L208,91.31409v-.001l8,.001ZM160,72.0094a8.00039,8.00039,0,0,1-8,8H96a8,8,0,0,1,0-16h56A8.00039,8.00039,0,0,1,160,72.0094Z" stroke="#3895ff"/>' +
	'</svg>';

==SCRIPT RESOURCES
<resources>
	<resource name="mainDlg" type="dialog">
		<dialog height="194" lang="english" title="EasyColor Styler" width="432">
			<control halign="left" height="8" name="lblSet" title="Set:" type="static" valign="top" width="16" x="54" y="10" />
			<control height="40" name="cbSet" type="combo" width="112" x="74" y="8" />
			<control halign="left" height="8" name="lblStyle" title="Style:" type="static" valign="top" width="20" x="242" y="10" />
			<control height="40" name="cbStyle" type="combo" width="112" x="266" y="8" />
			<control height="138" name="groupSettings" title="Settings" type="group" width="206" x="4" y="28" />
			<control halign="center" height="10" name="lblTitleLight" title="Light" type="static" valign="top" width="44" x="74" y="40" />
			<control halign="center" height="10" name="lblTitleDark" title="Dark" type="static" valign="top" width="44" x="158" y="40" />
			<control height="104" name="sepTheme" type="group" width="1" x="136" y="34" />
			<control halign="left" height="10" name="lblColor1" title="Main color:" type="static" valign="top" width="54" x="12" y="60" />
			<control height="14" name="palColor1Light" type="palette" width="44" x="74" y="58" />
			<control height="14" name="bCopyColor1_L2D" title="β–Ά" type="button" width="12" x="120" y="58" />
			<control height="14" name="bCopyColor1_D2L" title="β—€" type="button" width="12" x="142" y="58" />
			<control height="14" name="palColor1Dark" type="palette" width="46" x="158" y="58" />
			<control height="10" name="bCopyColorLight_1_2" title="β–Ό" type="button" width="44" x="74" y="76" />
			<control height="10" name="bCopyColorDark_1_2" title="β–Ό" type="button" width="46" x="158" y="76" />
			<control halign="left" height="10" name="lblColor2" title="Fill color:" type="static" valign="top" width="54" x="12" y="108" />
			<control height="14" name="palColor2Light" paltype="alpha" type="palette" width="44" x="74" y="106" />
			<control height="14" name="bCopyColor2_L2D" title="β–Ά" type="button" width="12" x="120" y="106" />
			<control height="14" name="bCopyColor2_D2L" title="β—€" type="button" width="12" x="142" y="106" />
			<control height="14" name="palColor2Dark" paltype="alpha" type="palette" width="46" x="158" y="106" />
			<control height="14" imagelabel="yes" name="bSetAction" title="  Export Set" type="button" width="74" x="98" y="144" />
			<control height="138" name="groupPreview" title="Preview" type="group" width="210" x="216" y="28" />
			<control halign="left" height="8" name="lblIcon" title="Icon:" type="static" valign="top" width="16" x="226" y="40" />
			<control height="40" name="cbIcon" type="combo" width="170" x="248" y="38" />
			<control halign="center" height="10" name="lblOriginal" title="Original" type="static" valign="top" width="28" x="256" y="56" />
			<control halign="left" height="74" image="yes" name="sOriginal" type="static" valign="top" width="92" x="224" y="68" />
			<control halign="center" height="10" name="lblNew" title="New" type="static" valign="top" width="28" x="358" y="56" />
			<control halign="left" height="74" image="yes" name="sPreview" type="static" valign="top" width="92" x="326" y="68" />
			<control arrow="yes" imagelabel="yes" height="14" name="bIconAction" title="" type="button" width="44" x="350" y="146" />
			<control height="14" imagelabel="yes" name="bToggleDLmode" title="  Switch Opus mode (Dark/Light)" type="button" width="130" x="4" y="174" />
			<control height="14" imagelabel="yes" name="bClose" title="  Close" type="button" width="56" x="370" y="174" />
			<control height="10" name="bCopyColorLight_2_1" title="β–²" type="button" width="44" x="74" y="92" />
			<control height="10" name="bCopyColorDark_2_1" title="β–²" type="button" width="46" x="158" y="92" />
		</dialog>
	</resource>
	<resource name="dlgExportMeta" type="dialog">
		<dialog height="64" lang="english" title="Export Icon Set Metadata" width="240">
			<control halign="left" height="10" name="lblName" title="Name:" type="static" valign="top" width="50" x="8" y="8" />
			<control halign="left" height="14" max="255" name="valName" type="edit" width="166" x="64" y="6" />
			<control halign="left" height="10" name="lblDisplayName" title="Display Name:" type="static" valign="top" width="50" x="8" y="26" />
			<control halign="left" height="14" max="255" name="valDisplayName" type="edit" width="166" x="64" y="24" />
			<control close="1" height="14" name="bOK" title="OK" type="button" width="50" x="180" y="44" />
			<control close="0" height="14" name="bAbort" title="Abort" type="button" width="50" x="126" y="44" />
		</dialog>
	</resource>
</resources>

13 Likes

Small update with UI adjustments (labels, controls positions, tooltips added).
Requires both script and sets updates.
SvgEasyColorStyler.opusscriptinstall (18.6 KB)
Src_Sets_V1.1.7z (7.6 MB)

4 Likes