diff options
| author | Christian Cleberg <[email protected]> | 2026-05-14 12:41:55 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-05-14 12:41:55 -0500 |
| commit | 0d61e05e98f6f020722ee4d43f024c1da348d2a9 (patch) | |
| tree | b388972b86cf83de380381c52c4390c3beafc69a /HutchSafariExtension/Resources/background.js | |
| parent | f28e5cee1643e3f99d7bf90476d1dcf9347229a0 (diff) | |
| download | hutch-3.4.0.tar.gz hutch-3.4.0.tar.bz2 hutch-3.4.0.zip | |
feat: add Safari extension for sourcehut deep linksv3.4.0
Diffstat (limited to 'HutchSafariExtension/Resources/background.js')
| -rw-r--r-- | HutchSafariExtension/Resources/background.js | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/HutchSafariExtension/Resources/background.js b/HutchSafariExtension/Resources/background.js new file mode 100644 index 0000000..81bf8b6 --- /dev/null +++ b/HutchSafariExtension/Resources/background.js @@ -0,0 +1,115 @@ +const supportedHosts = new Set([ + "git.sr.ht", + "hg.sr.ht", + "todo.sr.ht", + "builds.sr.ht", + "lists.sr.ht", + "meta.sr.ht", + "sr.ht", +]); + +const browserAPI = globalThis.browser ?? globalThis.chrome; + +function isOwnerRootPath(path) { + const components = path.split("/").filter(Boolean); + return components.length === 1 && components[0].startsWith("~"); +} + +function normalizedPath(pathname) { + return pathname.split("/").filter(Boolean).join("/"); +} + +function deepLinkPath(hostname, path) { + const components = path.split("/").filter(Boolean); + if ( + hostname === "sr.ht" && + components.length === 2 && + components[0] === "projects" && + components[1].startsWith("~") + ) { + return components[1]; + } + + return path; +} + +function deepLinkService(hostname, path) { + if (isOwnerRootPath(path)) { + return "lookup"; + } + + switch (hostname) { + case "git.sr.ht": + return "git"; + case "hg.sr.ht": + return "hg"; + case "todo.sr.ht": + return "todo"; + case "builds.sr.ht": + return "builds"; + case "lists.sr.ht": + return "lists"; + default: + return "lookup"; + } +} + +function hutchDeepLinkFor(rawURL) { + let url; + try { + url = new URL(rawURL); + } catch { + return null; + } + + if (url.protocol !== "https:" || !supportedHosts.has(url.hostname)) { + return null; + } + + const path = deepLinkPath(url.hostname, normalizedPath(url.pathname)); + const service = deepLinkService(url.hostname, path); + return `hutch://${service}${path ? `/${path}` : ""}${url.search}${url.hash}`; +} + +function showUnsupportedMessage(tabId) { + if (!tabId) { + return; + } + + const message = "Open in Hutch supports git, hg, todo, builds, lists, meta, and sr.ht pages."; + + if (browserAPI?.scripting?.executeScript) { + browserAPI.scripting.executeScript({ + target: { tabId }, + func: (text) => { + window.alert(text); + }, + args: [message], + }); + return; + } + + browserAPI?.tabs?.sendMessage?.(tabId, { type: "hutchUnsupportedPage", message }); +} + +function openURL(tabId, url) { + if (browserAPI?.tabs?.update && tabId) { + browserAPI.tabs.update(tabId, { url }); + return; + } + + if (browserAPI?.tabs?.create) { + browserAPI.tabs.create({ url }); + } +} + +browserAPI.action.onClicked.addListener((tab) => { + const hutchURL = tab?.url ? hutchDeepLinkFor(tab.url) : null; + if (!hutchURL) { + showUnsupportedMessage(tab?.id); + return; + } + + console.debug("[Hutch] Opening deep link", { sourceURL: tab.url, hutchURL }); + openURL(tab.id, hutchURL); +}); |
