Skip to content

Instantly share code, notes, and snippets.

@jessedc
Last active April 29, 2025 17:00
Show Gist options
  • Save jessedc/cb6f1a8d74e5e2292cd7dcd762892df8 to your computer and use it in GitHub Desktop.
Save jessedc/cb6f1a8d74e5e2292cd7dcd762892df8 to your computer and use it in GitHub Desktop.
Apple Script to close duplicate google chrome tabs. Build with Codename Goose.

Two scripts that can automate some Chrome tasks for you:

  1. Close duplicate tabs
  2. Save all your tabs to an Apple Notes Note

Run with osascript ~/close_chrome_duplicates.scpt or osascript ~/save_chrome_tabs_to_notes.scpt

Setup some alisases

echo 'alias closetabs="osascript ~/close_chrome_duplicates.scpt"' >> ~/.zshrc
echo 'alias savetabs="osascript ~/save_chrome_tabs_to_notes.scpt"' >> ~/.zshrc
echo 'alias saveandclosetabs="osascript ~/save_chrome_tabs_to_notes.scpt close"' >> ~/.zshrc

Then run

closetabs
savetabs
saveandclosetabs
on cleanURL(theURL)
try
set oldDelims to AppleScript's text item delimiters
set AppleScript's text item delimiters to "?"
set theResult to first text item of theURL
set AppleScript's text item delimiters to oldDelims
return theResult
on error
-- If there's any error, return the original URL
return theURL
end try
end cleanURL
tell application "Google Chrome"
set tabInfo to {}
set duplicateCount to 0
-- Gather all tabs
repeat with w in windows
repeat with t in tabs of w
set currentURL to URL of t
set currentTitle to title of t
-- Clean the URL before storing
set cleanedURL to my cleanURL(currentURL)
set end of tabInfo to {cleanedURL, currentURL, currentTitle, w, t}
end repeat
end repeat
-- Process duplicates
set processedURLs to {}
set closedTabs to {}
repeat with i from 1 to (count of tabInfo)
set currentItem to item i of tabInfo
set currentCleanURL to item 1 of currentItem
set currentFullURL to item 2 of currentItem
-- If we haven't processed this URL yet
if processedURLs does not contain currentCleanURL then
-- Add to processed list
set end of processedURLs to currentCleanURL
-- Look for duplicates
repeat with j from (i + 1) to (count of tabInfo)
set checkItem to item j of tabInfo
if item 1 of checkItem is equal to currentCleanURL then
-- Store info about what we're closing
set end of closedTabs to "Closed: " & item 3 of checkItem & return & "URL: " & item 2 of checkItem
-- Close the duplicate tab
try
tell (item 4 of checkItem) -- the window
close (item 5 of checkItem) -- the tab
set duplicateCount to duplicateCount + 1
end tell
end try
end if
end repeat
end if
end repeat
if duplicateCount is 0 then
return "No duplicate tabs were closed.\n"
else
set closedTabsText to ""
repeat with tabInfo in closedTabs
set closedTabsText to closedTabsText & tabInfo & return
end repeat
return "Successfully closed " & duplicateCount & " duplicate tabs:" & return & return & closedTabsText & "\n"
end if
end tell
-- Function to check if URL should be ignored
on shouldIgnoreURL(theURL, ignoreList)
repeat with prefix in ignoreList
if theURL starts with prefix then
return true
end if
end repeat
return false
end shouldIgnoreURL
-- Function to check if title is valid
on isValidTitle(theTitle)
if theTitle is "" then return false
if theTitle is missing value then return false
return true
end isValidTitle
-- Function to extract domain from URL
on extractDomain(theURL)
try
-- Remove protocol (http://, https://, etc.)
set oldDelims to AppleScript's text item delimiters
set AppleScript's text item delimiters to "://"
if (count of text items of theURL) > 1 then
set domainPart to text item 2 of theURL
else
set domainPart to theURL
end if
-- Get domain part (up to first slash or query parameter)
set AppleScript's text item delimiters to "/"
set domain to text item 1 of domainPart
-- Remove any port number if present
set AppleScript's text item delimiters to ":"
set domain to text item 1 of domain
set AppleScript's text item delimiters to oldDelims
return domain
on error
-- If there's any error, return the original URL
set AppleScript's text item delimiters to oldDelims
return theURL
end try
end extractDomain
-- Function to sort list by title alphabetically
on sortListByTitle(theList)
-- Convert to list of lists where first item is lowercase title for sorting
set sortableList to {}
repeat with tabItem in theList
set titleForSort to (do shell script "echo " & quoted form of (tabTitle of tabItem) & " | tr '[:upper:]' '[:lower:]'")
set end of sortableList to {titleForSort, tabItem}
end repeat
-- Sort the list
set sortedPairs to my sortList(sortableList)
-- Extract just the original records in sorted order
set sortedList to {}
repeat with pair in sortedPairs
set end of sortedList to item 2 of pair
end repeat
return sortedList
end sortListByTitle
-- Helper function to sort a list of pairs by first item
on sortList(theList)
if length of theList ≤ 1 then
return theList
end if
-- Get the middle item
set midIndex to (length of theList) div 2
set pivot to item midIndex of theList
-- Split into sublists
set lessList to {}
set moreList to {}
repeat with i from 1 to length of theList
if i ≠ midIndex then
set theItem to item i of theList
if item 1 of theItem < item 1 of pivot then
set end of lessList to theItem
else
set end of moreList to theItem
end if
end if
end repeat
-- Recursively sort sublists and combine
return (my sortList(lessList)) & {pivot} & (my sortList(moreList))
end sortList
on run argv
-- Check if close argument is provided
set shouldCloseTabs to false
if (count of argv) > 0 then
if item 1 of argv is "close" then
set shouldCloseTabs to true
end if
end if
-- Set up URL prefixes to ignore
set ignoreList to {¬
"https://login.block.xyz", ¬
"chrome://", ¬
"chrome-extension://", ¬
"about:", ¬
"edge://", ¬
"file:///" ¬
}
tell application "Google Chrome"
-- Initialize variables
set tabList to {}
set ignoredCount to 0
set totalTabCount to 0
-- Get current date/time for note title
set currentDate to do shell script "date '+%Y-%m-%d %H:%M'"
-- Get current year for folder name
set currentYear to do shell script "date '+%Y'"
-- Gather all tabs from all windows into a list
repeat with w in windows
tell w
repeat with t in tabs
tell t
copy URL as string to currentURL
copy title as string to currentTitle
end tell
set totalTabCount to totalTabCount + 1
-- Only process URLs that aren't in the ignore list and have valid titles for the note
if not (my shouldIgnoreURL(currentURL, ignoreList)) and (my isValidTitle(currentTitle)) then
-- Extract domain
set domain to my extractDomain(currentURL)
-- Add to list as record
set end of tabList to {tabTitle:currentTitle, tabURL:currentURL, tabDomain:domain}
else
set ignoredCount to ignoredCount + 1
end if
end repeat
end tell
end repeat
-- Sort the list by title
set sortedList to my sortListByTitle(tabList)
-- Create HTML content
set formattedTabs to ""
repeat with tabInfo in sortedList
set currentTitle to tabTitle of tabInfo
set currentURL to tabURL of tabInfo
set domain to tabDomain of tabInfo
set formattedTabs to formattedTabs & "<li><a href=\"" & currentURL & "\">" & currentTitle & "</a> (" & domain & ")</li>" & return
end repeat
-- Create note title
set noteTitle to "Chrome Tabs - " & currentDate
-- Create note content with header and count
set noteContent to "<h1>" & noteTitle & "</h1>" & return & return
set noteContent to noteContent & "<p>Total Tabs: " & (count of sortedList) & "</p>" & return
if ignoredCount > 0 then
set noteContent to noteContent & "<p>Ignored Tabs: " & ignoredCount & "</p>" & return
end if
set noteContent to noteContent & return & "<ul>" & return & formattedTabs & "</ul>"
-- Create the note in Apple Notes in the year folder
tell application "Notes"
activate
tell account "iCloud"
-- Check if year folder exists, if not create it
if not (exists folder currentYear) then
make new folder with properties {name:currentYear}
end if
-- Create the note in the year folder
tell folder currentYear
make new note with properties {body:noteContent, name:noteTitle}
end tell
end tell
end tell
-- Close all tabs if requested
if shouldCloseTabs then
-- Close all openm windows
set windowList to windows
repeat with w in windowList
close w
end repeat
-- Open a new window
make new window
activate
open location "https://mail.google.com"
tell window 1
make new tab with properties {URL:"https://calendar.google.com"}
end tell
end if
set resultMessage to "Successfully saved " & (count of sortedList) & " tabs to Notes with title: " & noteTitle & return & ¬
"Ignored " & ignoredCount & " system/extension tabs" & return & ¬
"Note saved in folder: " & currentYear
if shouldCloseTabs then
set resultMessage to resultMessage & return & "Closed all tabs and windows except for one new tab"
end if
return resultMessage
end tell
end run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment