Skip to content

Commit c91dec8

Browse files
hyperpolymathclaude
andcommitted
feat: migrate bookmark scripts from Python to Julia
Converted organize_bookmarks.py, organize_edge_bookmarks.py, generate_bookmarks_html.py to Julia equivalents. Deleted Python originals. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9312e5b commit c91dec8

6 files changed

Lines changed: 268 additions & 208 deletions

generate_bookmarks_html.jl

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
# generate_bookmarks_html.jl — Generate Netscape bookmark HTML from categorized bookmarks
3+
# Migrated from Python (generate_bookmarks_html.py)
4+
5+
const THEMES = Dict{String, Vector{String}}(
6+
"Tech & Programming" => ["github", "programming", "docker", "server", "code", "dev", "tech", "node", "python", "rust", "npm", "stack", "api", "web", "cloud", "aws", "google", "microsoft", "apple", "app", "software"],
7+
"Linux & OS" => ["fedora", "linux", "ubuntu", "debian", "kernel", "cli", "shell", "bash", "terminal", "desktop", "os", "system", "config"],
8+
"News & Finance" => ["news", "breaking", "msn", "trump", "politics", "economy", "market", "finance", "bank", "money", "stock", "crypto", "bitcoin", "business", "invest", "trading"],
9+
"Psychology & Growth" => ["psychology", "mental", "self", "growth", "happiness", "manipulation", "relationships", "advice", "life", "wellness", "mindset", "success", "productivity"],
10+
"Art, Culture & Design" => ["art", "artist", "design", "museum", "culture", "style", "creative", "photo", "music", "history", "philosophy", "books", "writing"],
11+
"Lifestyle & Health" => ["food", "cooking", "recipe", "health", "fitness", "yoga", "travel", "holiday", "home", "garden", "shopping", "amazon", "buy", "deal"],
12+
"Education & Career" => ["learn", "course", "study", "university", "school", "job", "career", "resume", "cv", "interview", "skills", "training"],
13+
"Media & Social" => ["youtube", "video", "tv", "movie", "entertainment", "facebook", "twitter", "x.com", "instagram", "reddit", "watch", "podcast"],
14+
)
15+
16+
"""Categorize a bookmark line by matching keywords against themes."""
17+
function categorize(line::AbstractString)::String
18+
line_lower = lowercase(line)
19+
for (theme, keywords) in THEMES
20+
if any(kw -> occursin(kw, line_lower), keywords)
21+
return theme
22+
end
23+
end
24+
return "Uncategorized"
25+
end
26+
27+
"""Escape HTML special characters."""
28+
function html_escape(s::AbstractString)::String
29+
s = replace(s, "&" => "&amp;")
30+
s = replace(s, "<" => "&lt;")
31+
s = replace(s, ">" => "&gt;")
32+
s = replace(s, "\"" => "&quot;")
33+
return s
34+
end
35+
36+
function main()
37+
all_bookmarks = Dict{String, Vector{String}}()
38+
39+
for path in ["/tmp/firefox_bookmarks.txt", "/tmp/edge_bookmarks.txt"]
40+
isfile(path) || continue
41+
for line in eachline(path)
42+
stripped = strip(line)
43+
isempty(stripped) && continue
44+
theme = categorize(stripped)
45+
push!(get!(all_bookmarks, theme, String[]), stripped)
46+
end
47+
end
48+
49+
sorted_themes = sort(collect(filter(t -> t != "Uncategorized", keys(all_bookmarks))))
50+
haskey(all_bookmarks, "Uncategorized") && push!(sorted_themes, "Uncategorized")
51+
52+
repos_dir = get(ENV, "REPOS_DIR", "/var/mnt/eclipse/repos")
53+
outpath = joinpath(repos_dir, "organized_bookmarks.html")
54+
55+
open(outpath, "w") do f
56+
println(f, "<!DOCTYPE NETSCAPE-Bookmark-file-1>")
57+
println(f, "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">")
58+
println(f, "<TITLE>Bookmarks</TITLE>")
59+
println(f, "<H1>Bookmarks</H1>")
60+
println(f, "<DL><p>")
61+
62+
for theme in sorted_themes
63+
items = sort(unique(all_bookmarks[theme]))
64+
println(f, " <DT><H3>$theme</H3>")
65+
println(f, " <DL><p>")
66+
for item in items
67+
parts = split(item, "|"; limit=2)
68+
if length(parts) == 2
69+
name = html_escape(String(parts[1]))
70+
url = String(parts[2])
71+
println(f, " <DT><A HREF=\"$url\">$name</A>")
72+
end
73+
end
74+
println(f, " </DL><p>")
75+
end
76+
77+
println(f, "</DL><p>")
78+
end
79+
80+
println("Success: $outpath generated.")
81+
end
82+
83+
main()

generate_bookmarks_html.py

Lines changed: 0 additions & 61 deletions
This file was deleted.

organize_bookmarks.jl

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
# organize_bookmarks.jl — Categorize Firefox+Edge bookmarks into themed Markdown
3+
# Migrated from Python (organize_bookmarks.py)
4+
5+
const THEMES = Dict{String, Vector{String}}(
6+
"Tech & Programming" => ["github", "programming", "docker", "server", "code", "dev", "tech", "node", "python", "rust", "npm", "stack", "api", "web", "cloud", "aws", "google", "microsoft", "apple", "app", "software"],
7+
"Linux & OS" => ["fedora", "linux", "ubuntu", "debian", "kernel", "cli", "shell", "bash", "terminal", "desktop", "os", "system", "config"],
8+
"News & Finance" => ["news", "breaking", "msn", "trump", "politics", "economy", "market", "finance", "bank", "money", "stock", "crypto", "bitcoin", "business", "invest", "trading"],
9+
"Psychology & Growth" => ["psychology", "mental", "self", "growth", "happiness", "manipulation", "relationships", "advice", "life", "wellness", "mindset", "success", "productivity"],
10+
"Art, Culture & Design" => ["art", "artist", "design", "museum", "culture", "style", "creative", "photo", "music", "history", "philosophy", "books", "writing"],
11+
"Lifestyle & Health" => ["food", "cooking", "recipe", "health", "fitness", "yoga", "travel", "holiday", "home", "garden", "shopping", "amazon", "buy", "deal"],
12+
"Education & Career" => ["learn", "course", "study", "university", "school", "job", "career", "resume", "cv", "interview", "skills", "training"],
13+
"Media & Social" => ["youtube", "video", "tv", "movie", "entertainment", "facebook", "twitter", "x.com", "instagram", "reddit", "watch", "podcast"],
14+
)
15+
16+
"""Categorize a bookmark line by matching keywords against themes."""
17+
function categorize(line::AbstractString)::String
18+
line_lower = lowercase(line)
19+
for (theme, keywords) in THEMES
20+
if any(kw -> occursin(kw, line_lower), keywords)
21+
return theme
22+
end
23+
end
24+
return "Uncategorized"
25+
end
26+
27+
function main()
28+
all_bookmarks = Dict{String, Vector{String}}()
29+
30+
for path in ["/tmp/firefox_bookmarks.txt", "/tmp/edge_bookmarks.txt"]
31+
isfile(path) || continue
32+
for line in eachline(path)
33+
stripped = strip(line)
34+
isempty(stripped) && continue
35+
theme = categorize(stripped)
36+
push!(get!(all_bookmarks, theme, String[]), stripped)
37+
end
38+
end
39+
40+
sorted_themes = sort(collect(filter(t -> t != "Uncategorized", keys(all_bookmarks))))
41+
haskey(all_bookmarks, "Uncategorized") && push!(sorted_themes, "Uncategorized")
42+
43+
repos_dir = get(ENV, "REPOS_DIR", "/var/mnt/eclipse/repos")
44+
outpath = joinpath(repos_dir, "organized_bookmarks.md")
45+
46+
open(outpath, "w") do f
47+
println(f, "# Refined Organized Bookmarks\n")
48+
for theme in sorted_themes
49+
items = sort(unique(all_bookmarks[theme]))
50+
println(f, "## $theme")
51+
for item in items
52+
parts = split(item, "|"; limit=2)
53+
if length(parts) == 2
54+
println(f, "- [$(parts[1])]($(parts[2]))")
55+
else
56+
println(f, "- $item")
57+
end
58+
end
59+
println(f)
60+
end
61+
end
62+
63+
println("Success: $outpath generated.")
64+
end
65+
66+
main()

organize_bookmarks.py

Lines changed: 0 additions & 51 deletions
This file was deleted.

organize_edge_bookmarks.jl

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
# organize_edge_bookmarks.jl — Categorize bookmarks and write Edge Bookmarks JSON
3+
# Migrated from Python (organize_edge_bookmarks.py)
4+
5+
using JSON3
6+
using UUIDs
7+
using Dates
8+
9+
const THEMES = Dict{String, Vector{String}}(
10+
"Tech & Programming" => ["github", "programming", "docker", "server", "code", "dev", "tech", "node", "python", "rust", "npm", "stack", "api", "web", "cloud", "aws", "google", "microsoft", "apple", "app", "software"],
11+
"Linux & OS" => ["fedora", "linux", "ubuntu", "debian", "kernel", "cli", "shell", "bash", "terminal", "desktop", "os", "system", "config"],
12+
"News & Finance" => ["news", "breaking", "msn", "trump", "politics", "economy", "market", "finance", "bank", "money", "stock", "crypto", "bitcoin", "business", "invest", "trading"],
13+
"Psychology & Growth" => ["psychology", "mental", "self", "growth", "happiness", "manipulation", "relationships", "advice", "life", "wellness", "mindset", "success", "productivity"],
14+
"Art, Culture & Design" => ["art", "artist", "design", "museum", "culture", "style", "creative", "photo", "music", "history", "philosophy", "books", "writing"],
15+
"Lifestyle & Health" => ["food", "cooking", "recipe", "health", "fitness", "yoga", "travel", "holiday", "home", "garden", "shopping", "amazon", "buy", "deal"],
16+
"Education & Career" => ["learn", "course", "study", "university", "school", "job", "career", "resume", "cv", "interview", "skills", "training"],
17+
"Media & Social" => ["youtube", "video", "tv", "movie", "entertainment", "facebook", "twitter", "x.com", "instagram", "reddit", "watch", "podcast"],
18+
)
19+
20+
"""Chrome/Edge timestamp: microseconds since Jan 1, 1601."""
21+
function chrome_timestamp(dt::DateTime)::String
22+
epoch_1601 = DateTime(1601, 1, 1)
23+
diff_seconds = Dates.value(dt - epoch_1601) / 1000 # ms → s
24+
return string(round(Int64, diff_seconds * 1_000_000))
25+
end
26+
27+
"""Categorize a bookmark line by matching keywords against themes."""
28+
function categorize(line::AbstractString)::String
29+
line_lower = lowercase(line)
30+
for (theme, keywords) in THEMES
31+
if any(kw -> occursin(kw, line_lower), keywords)
32+
return theme
33+
end
34+
end
35+
return "Uncategorized"
36+
end
37+
38+
function main()
39+
all_bookmarks = Dict{String, Vector{String}}()
40+
41+
for path in ["/tmp/firefox_bookmarks.txt", "/tmp/edge_bookmarks.txt"]
42+
isfile(path) || continue
43+
for line in eachline(path)
44+
stripped = strip(line)
45+
isempty(stripped) && continue
46+
theme = categorize(stripped)
47+
push!(get!(all_bookmarks, theme, String[]), stripped)
48+
end
49+
end
50+
51+
now_ts = chrome_timestamp(now())
52+
current_id = Ref(4)
53+
54+
function next_id()
55+
id = current_id[]
56+
current_id[] += 1
57+
return string(id)
58+
end
59+
60+
bookmark_bar_children = []
61+
62+
sorted_themes = sort(collect(filter(t -> t != "Uncategorized", keys(all_bookmarks))))
63+
haskey(all_bookmarks, "Uncategorized") && push!(sorted_themes, "Uncategorized")
64+
65+
for theme in sorted_themes
66+
items = sort(unique(all_bookmarks[theme]))
67+
children = []
68+
for item in items
69+
parts = split(item, "|"; limit=2)
70+
if length(parts) == 2
71+
push!(children, Dict(
72+
"date_added" => now_ts,
73+
"date_last_used" => "0",
74+
"guid" => string(uuid4()),
75+
"id" => next_id(),
76+
"name" => parts[1],
77+
"type" => "url",
78+
"url" => parts[2],
79+
))
80+
end
81+
end
82+
push!(bookmark_bar_children, Dict(
83+
"date_added" => now_ts,
84+
"date_last_used" => "0",
85+
"guid" => string(uuid4()),
86+
"id" => next_id(),
87+
"name" => theme,
88+
"type" => "folder",
89+
"children" => children,
90+
))
91+
end
92+
93+
output = Dict(
94+
"checksum" => "",
95+
"roots" => Dict(
96+
"bookmark_bar" => Dict(
97+
"children" => bookmark_bar_children,
98+
"date_added" => now_ts,
99+
"date_last_used" => "0",
100+
"guid" => string(uuid4()),
101+
"id" => "1",
102+
"name" => "Favorites Bar",
103+
"type" => "folder",
104+
),
105+
"other" => Dict("children" => [], "date_added" => "0", "date_last_used" => "0", "guid" => string(uuid4()), "id" => "2", "name" => "Other Favorites", "type" => "folder"),
106+
"synced" => Dict("children" => [], "date_added" => "0", "date_last_used" => "0", "guid" => string(uuid4()), "id" => "3", "name" => "Mobile Favorites", "type" => "folder"),
107+
),
108+
"version" => 1,
109+
)
110+
111+
outpath = expanduser("~/.var/app/com.microsoft.EdgeDev/config/microsoft-edge-dev/Default/Bookmarks")
112+
open(outpath, "w") do f
113+
JSON3.pretty(f, output; allow_inf=true)
114+
end
115+
116+
println("Success: Edge Bookmarks file overwritten.")
117+
end
118+
119+
main()

0 commit comments

Comments
 (0)