[Space-Lua] Simple HTML ColorPicker for Current Selection

Hey guys,

I made a simple little color picker tool/widget (twidget?) using space-lua

Once activated, the tool lets you quickly visualize the color of any valid hex code that is currently selected.

Features

  • Visualize Hex Colors: Instantly see the color of any selected hex code
  • Color Palettes: Add new color pickers on the fly using the “Add Color” button. This will add a new color picker item to the current list and you can modify each one independently
  • No External Dependencies: The complete functionality—from local Lua functions to command definitions—is contained within a single space‑lua script block. This keeps everything compact and prevents polluting your user space.
  • (Chromium) Eyedropper Tool: If you’re using a Chromium based browser - you can easily sample colors using the browser’s built-in eyedropper tool.
  • Hex Code Flexibility: Supports hex codes with or without a leading # and also supports 3-character hex codes.

Note

  • Because the tool makes use of the HTML <input> tag the actual underlying color picker implementation and feature set is browser dependent.
  • For example, Firefox does not have an eyedropper tool

Install

Since this tool was fully implemented in space-lua, you can just copy the space-lua code below and pate it directly in a space-lua code block on any page.

Then reload your system and widgets (${[System: Reload]}) for the ColorPicker script to take effect.


local function getSelectedtext()
  local text = space.readPage(editor.getCurrentPage())
  local cursorSelectionRange = editor.getSelection()

  local extractedText = string.sub(text, cursorSelectionRange.from + 1, cursorSelectionRange.to)
  
  return extractedText
end


-- Check if a string is a valid 3- or 6-digit hex color (with or without a leading '#')
local function isHex(s)
  local txtMatch = (string.match(s, '^#?[A-Za-z0-9][A-Za-z0-9][A-Za-z0-9]$') or string.match(s, '^#?[A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9]$')) ~= nil
  return txtMatch
end

local function expandHex36(hex)
  -- Duplicate each character to form the 6-digit code
  local r = string.sub(hex, 1, 1)
  local g = string.sub(hex, 2, 2)
  local b = string.sub(hex, 3, 3)
  
  return r .. r .. g .. g .. b .. b
end



local function openSidePanelWContent(htmlContent)
  -- js script to update color value on change
  local jsScript = [[
  const CP_ROOT = "colorp"
  const CP_ID = "First"

  const COLOR_PICKER_ID = `${CP_ROOT}${CP_ID}`
  const COLOR_PICKER_LBL_ID = `${CP_ROOT}${CP_ID}Lbl`
  const COLOR_PICKER_ADD_BTN_ID = `${CP_ROOT}AddColor`
  const COLOR_PICKER_OL_ID = `${CP_ROOT}List`
  
  const colorPicker = document.getElementById(COLOR_PICKER_ID)
  const colorPickerLbl = document.getElementById(COLOR_PICKER_LBL_ID)
  const colorPickerList = document.getElementById(COLOR_PICKER_OL_ID)
  const addColorBtn = document.getElementById(COLOR_PICKER_ADD_BTN_ID)

  var currentColorIndex = 0;
  var lastSetColor = colorPickerLbl.innerText;
  
  colorPicker.addEventListener("input", watchColorPicker, false);
  colorPicker.addEventListener("change", watchColorPicker, false);
  addColorBtn.addEventListener("click", addNewColor, false);
  
  function watchColorPicker(event) {
    colorPickerLbl.innerText = event.target.value;
    lastSetColor = event.target.value
  }

  function watchColorPickerAll(event) {
    const colorLabelID = event.target.id + "Lbl";
    document.getElementById(colorLabelID).innerHTML = " " + event.target.value;
    lastSetColor = event.target.value;
  }



  function addNewColor(event) {
    const newColorID = `${CP_ROOT}Li${currentColorIndex}`;
    // create list item 
    var li = document.createElement("li");
    var odiv = document.createElement("div");
    var colorBtn = document.createElement("input");

    colorBtn.type = "color";
    colorBtn.id = newColorID;
    colorBtn.name = newColorID;
    colorBtn.value = lastSetColor;
    colorBtn.oninput = watchColorPickerAll;

    var colorLbl = document.createElement("label");
    colorLbl.id = `${newColorID}Lbl`;
    colorLbl.for = newColorID;
    colorLbl.innerHTML = " " + lastSetColor;

    odiv.appendChild(colorBtn);
    odiv.appendChild(colorLbl);

    // append div to list item object and update list
    li.appendChild(odiv)
    colorPickerList.appendChild(li);
    currentColorIndex++;
  }

  ]]
  -- Inject script and show the html content in the panl 
  editor.showPanel("rhs", 1, htmlContent, jsScript)
end



local function closeSidePanelWContent()
  editor.hidePanel("rhs")
end


local function buildHTMLColorPicker(hex)
  local html = "<div>" ..
  "<input type='color' id='colorpFirst' value=#" .. hex .. ">" ..
  "<label id='colorpFirstLbl' for='colorpFirst'>#" .. hex .. "</label>" ..
  "<div>" ..
  "<button id='colorpAddColor'>Add Color</button>" ..
  "<hr/>" ..
  "<ol id='colorpList'>" ..
  "</ol>" ..
  "</div>" ..
  "</div>"
  return html
end



command.define {
  name = "ColorPicker - Show",
  key = "Alt-p",
  run = function()
    local txt = getSelectedtext()
    if isHex(txt) then
      local hexVal = string.gsub(txt, '^#', '')
      
      if string.len(hexVal) == 3 then
        hexVal = expandHex36(hexVal)
      end
      
      local html = buildHTMLColorPicker(hexVal)
      openSidePanelWContent(html)
      editor.flashNotification(hexVal)
    else
      local defaultHex = "000000"
      local html = buildHTMLColorPicker(defaultHex)
      openSidePanelWContent(html)
      editor.flashNotification(defaultHex)
    end
  end
}


command.define {
  name = "ColorPicker - Hide",
  key = "Escape",
  run = function()
    closeSidePanelWContent()
  end
}

Usage

  1. Select a HEX Code
    For example, select one of these:
    • #3E56DA
    • d20
  2. Activate the Color Picker (while still selected)
    • via. Keyboard Shortcut: (Win) Alt - p
    • via. Keyboard Shortcut: (Mac) Opt - p
    • via. Command Menu: Search for “ColorPicker - Show”
  3. Deactivate the Color Picker:
    • via. Keyboard Shortcut: Escape
    • via. Command Menu: Search for “ColorPicker - Hide”

Demo Usage

Chromium

chrome_g8jml2fMQW

Firefox

firefox_CmxnPradZk

Housekeeping

  • If you wanna keep things a little more tidy, you can tag the page as #meta so it does not show up in your Page Navigation menu.
    • Then to access the page later you can use ctrl-shift-k and search for whatever you called it
  • You can place the script in a page pretty much anywhere in your space and it should work. But to keep things from getting too messy, you should create a separate file for the Color Picker
    • For example. I have my ColorPicker file located at: Library/Custom/Libs/ColorPicker/ColorPicker.md

Color Picker Components

  • Local Lua Functions: For tasks such as extracting the currently selected text, building HTML components, and injecting JavaScript event handlers, etc.
  • Panel Management: Uses editor.showPanel and editor.hidePanel to display the color picker’s HTML content with custom JS.
  • Command Definitions: Utilizes command.define to create custom commands (and keyboard shortcuts) for activating and deactivating the Color Picker.

Some Background

The idea for this came about when I was trying out the super cool onespaceman theme for sb, but I really wanted to visualize and tweak some of the base theme colors without leaving SB.

Aside from the actual color picking functionality, I also wanted something that was extremely small and minimal so that it can be easily traced, understood and extended.

The color picker tool initially started out as a traditional plugin, but then I discovered space-lua. And oh my god.

space-lua is going to change everything.

Todo:

  • Export palette as csv
  • Better UI tools (copy hex code)
  • Simple color palette generator based on existing colors

Hopefully some of you get some good use out of it! Keen to hear any thoughts and suggestions.

Cheers,
Jay