Extraction

Extract URLs

Extract unique web URLs from selected text.

OfficialOn-device

Download action

What Extract URLs does

Finds HTTP, HTTPS, www-style, and protocol-free domain links, removes surrounding sentence punctuation, ignores domains inside email addresses, preserves balanced URL brackets, and returns one unique URL per line.

CategoryExtraction
Action typeJavaScript
Default resultCopy the result
InternetNot required

Action and outcome

Extract URLs example
ActionOutcome
Extract URLsSelected text: See example.com/docs, then https://actionclip.app.example.com/docs https://actionclip.app

Use Extract URLs

Follow these steps to extract unique web URLs from selected text with ActionClip.

  1. Select the text you want to use.
  2. Choose Extract URLs from ActionClip.
  3. Review the extracted result, then copy it where you need it.

Requirements and privacy

  • Before you start: No setup required.
  • Your selected text: Runs locally in ActionClip’s JavaScript sandbox. Selected text does not leave your Mac.

Action script

The script below is included so you can inspect how the action works before downloading it.

JavaScript definition
function run(selected_text) {
  const domainLabel = "(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)";
  const topLevelDomain = "(?:[a-z]{2,63}|xn--[a-z0-9-]{2,59})";
  const bareDomain = "(?:" + domainLabel + "\\.)+" + topLevelDomain;
  const explicitURL = "(?:https?:\\/\\/|www\\.)[^\\s<>\\\"']+";
  const domainURL = bareDomain
    + "(?::\\d{2,5})?"
    + "(?:\\/[^\\s<>\\\"']*)?"
    + "(?:\\?[^\\s<>\\\"']*)?"
    + "(?:#[^\\s<>\\\"']*)?";
  const pattern = new RegExp("\\b(?:" + explicitURL + "|" + domainURL + ")", "gi");
  const matches = Array.from(selected_text.matchAll(pattern))
    .filter((match) => match.index === 0 || selected_text[match.index - 1] !== "@")
    .map((match) => match[0]);

  function trimTrailingPunctuation(value) {
    let result = value.replace(/[.,;:!?]+$/g, "");
    const pairs = [["(", ")"], ["[", "]"], ["{", "}"]];
    pairs.forEach(([opening, closing]) => {
      while (result.endsWith(closing)) {
        const opens = result.split(opening).length - 1;
        const closes = result.split(closing).length - 1;
        if (closes <= opens) break;
        result = result.slice(0, -1);
      }
    });
    return result;
  }

  const seen = new Set();
  const urls = [];
  matches.map(trimTrailingPunctuation).forEach((url) => {
    if (!seen.has(url)) {
      seen.add(url);
      urls.push(url);
    }
  });
  return urls.length ? urls.join("\n") : "No URLs found.";
}