1// Package url provides URL manipulation utilities for the browser.
2package url
3
4import (
5 "fmt"
6 "net/url"
7 "strings"
8)
9
10// Normalize adds https:// prefix if missing for URL-like inputs.
11// Returns the input unchanged if it already has a scheme or doesn't look like a URL.
12func Normalize(input string) string {
13 if input == "" {
14 return ""
15 }
16
17 // Already has scheme
18 switch {
19 case strings.HasPrefix(input, "http://"):
20 return input
21 case strings.HasPrefix(input, "https://"):
22 return input
23 case strings.HasPrefix(input, "dumb://"):
24 return input
25 case strings.HasPrefix(input, "file://"):
26 return input
27 case strings.HasPrefix(input, "about:"):
28 return input
29 }
30
31 // Looks like a URL (contains . and no spaces)
32 if strings.Contains(input, ".") && !strings.Contains(input, " ") {
33 return "https://" + input
34 }
35
36 return input
37}
38
39// LooksLikeURL checks if the input appears to be a URL (not a search query).
40// Returns true for strings like "github.com", "google.com/search", etc.
41// Also returns true for URLs with explicit schemes like "dumb://".
42func LooksLikeURL(input string) bool {
43 if input == "" {
44 return false
45 }
46
47 // Explicit schemes should always be treated as URLs.
48 switch {
49 case strings.HasPrefix(input, "http://"):
50 return true
51 case strings.HasPrefix(input, "https://"):
52 return true
53 case strings.HasPrefix(input, "dumb://"):
54 return true
55 case strings.HasPrefix(input, "file://"):
56 return true
57 case strings.HasPrefix(input, "about:"):
58 return true
59 }
60
61 // Contains a dot and no spaces = likely a URL
62 return strings.Contains(input, ".") && !strings.Contains(input, " ")
63}
64
65// ExtractDomain extracts the normalized domain (host) from a URL string.
66// Normalizes by stripping "www." prefix so youtube.com and www.youtube.com
67// resolve to the same value.
68func ExtractDomain(rawURL string) string {
69 if rawURL == "" {
70 return ""
71 }
72 parsed, err := url.Parse(rawURL)
73 if err != nil || parsed.Host == "" {
74 return ""
75 }
76 return strings.TrimPrefix(parsed.Host, "www.")
77}
78
79// SanitizeDomainForFilename converts a domain to a safe filename with .ico extension.
80// Replaces unsafe filesystem characters with underscores.
81func SanitizeDomainForFilename(domain string) string {
82 return sanitizeDomain(domain) + ".ico"
83}
84
85// SanitizeDomainForPNG converts a domain to a safe filename with .png extension.
86// Used for favicon export for tools like rofi/fuzzel that require PNG format.
87func SanitizeDomainForPNG(domain string) string {
88 return sanitizeDomain(domain) + ".png"
89}
90
91// sanitizeDomain replaces unsafe filesystem characters with underscores.
92func sanitizeDomain(domain string) string {
93 replacer := strings.NewReplacer(
94 ":", "_",
95 "/", "_",
96 "\\", "_",
97 "*", "_",
98 "?", "_",
99 "\"", "_",
100 "<", "_",
101 ">", "_",
102 "|", "_",
103 )
104 return replacer.Replace(domain)
105}
106
107// SanitizeDomainForPNGSized converts a domain to a safe filename with size suffix.
108// Example: "google.com" with size 32 -> "google.com.32.png"
109// Used for normalized favicon export for tools like rofi/fuzzel.
110func SanitizeDomainForPNGSized(domain string, size int) string {
111 return fmt.Sprintf("%s.%d.png", sanitizeDomain(domain), size)
112}