Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
77c7bdd
fix; sidebar
lovestaco Feb 5, 2026
1ee1c54
Merge branch 'main' of github.com:HexmosTech/FreeDevTools
lovestaco Feb 5, 2026
eb1df94
create new layoting
lovestaco Feb 5, 2026
69568e8
30 items
lovestaco Feb 6, 2026
1f14949
fix: pagination and layout issues
lovestaco Feb 6, 2026
781adca
fix; search and other pages spacing issues
lovestaco Feb 6, 2026
de0f06c
fix: margin
lovestaco Feb 6, 2026
0a12fe6
fix: search and sidebar improve search UI completely
lovestaco Feb 7, 2026
5e30915
fix: browse by categories
lovestaco Feb 7, 2026
7a7ed86
Merge branch 'main' of github.com:HexmosTech/FreeDevTools into ui-rewamp
lovestaco Feb 7, 2026
b574320
fix:height
lovestaco Feb 7, 2026
dbb5c9c
fix: homepage
lovestaco Feb 7, 2026
c1b22a6
fix: search with number
lovestaco Feb 7, 2026
8c64da3
fix: color
lovestaco Feb 7, 2026
4bcd9f5
fix: spacing
lovestaco Feb 7, 2026
8a8324a
fix: search
lovestaco Feb 7, 2026
b3abd4d
fix: opacity
lovestaco Feb 7, 2026
be4d306
fix: sizing glitch when active search is happening
lovestaco Feb 7, 2026
6b1f140
fix: search and index with focus color, grouping
lovestaco Feb 7, 2026
4acaffa
fix: ctrl k in other pages
lovestaco Feb 7, 2026
bb00f4b
fix: crumbs
lovestaco Feb 7, 2026
e89b1a3
feat: basic pro banner
lovestaco Feb 7, 2026
52a1bb3
fix: homepage spacing priority
lovestaco Feb 7, 2026
156de14
fix: basic pro banner
lovestaco Feb 7, 2026
01e9854
fix: pro banner
lovestaco Feb 7, 2026
fc4f17a
fix: spacing
lovestaco Feb 7, 2026
fe9ca7c
fix: search close esc, counter color
lovestaco Feb 8, 2026
21d1de3
show pro popup on 3,6,9,12 th visit
lovestaco Feb 8, 2026
0da625a
fix: counter
lovestaco Feb 8, 2026
18b236c
fix: breadcurmbs for all pages
lovestaco Feb 8, 2026
395d4d9
fix: close icon and pro banner
lovestaco Feb 8, 2026
eb59292
fix: download count for vscode
lovestaco Feb 8, 2026
7ef3ebd
fix: container padding
lovestaco Feb 8, 2026
ecbf05a
fix: breadcrumb and h1 order for ipm pages
lovestaco Feb 8, 2026
6f89921
fix: show header for alls screens except pc
lovestaco Feb 8, 2026
d6d0d07
fix: rm unecessary things from header
lovestaco Feb 8, 2026
783be85
fix: plan url
lovestaco Feb 8, 2026
e154f08
feat: hamburger for phone menu
lovestaco Feb 8, 2026
91eadbc
fix: sidebar to look like header no glitch
lovestaco Feb 8, 2026
c41d764
Merge branch 'main' of github.com:HexmosTech/FreeDevTools into ui-rewamp
lovestaco Feb 8, 2026
7dfe918
fix: header flash in desktop mode
lovestaco Feb 8, 2026
7efdc9d
fix: title for bookarm
lovestaco Feb 8, 2026
752887a
fix: load from right side
lovestaco Feb 8, 2026
31a2be2
fix; footer and phone sidebar
lovestaco Feb 9, 2026
57c98c0
fix; adbanner glitch
lovestaco Feb 9, 2026
eff3d1e
fix: tool card shadow
lovestaco Feb 9, 2026
ca0c28b
fix: bookmark icons color
lovestaco Feb 9, 2026
94bb98a
fix: add close icon in phone mode for sidbear
lovestaco Feb 9, 2026
8cc6c99
fix: left align bookmark
lovestaco Feb 9, 2026
31267e3
fix: size of sidebar in phone
lovestaco Feb 9, 2026
658a12a
fix: placeholder for phone
lovestaco Feb 9, 2026
510d462
fix: search issues
lovestaco Feb 9, 2026
dc63785
fix: search fullscreen in phone
lovestaco Feb 9, 2026
5baf897
fix: pro banner all issues in phone
lovestaco Feb 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions frontend/.air.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"

[build]
args_bin = []
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ./cmd/server"
delay = 2000
exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules", ".git", "bin", "public", "db", "scripts", "astro_freedevtools", "frontend", "search-index", "man-pages-stuff", "docs", "md", "nginx", "fdtdb-cli"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = false

[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"

[log]
main_only = false
time = false

[misc]
clean_on_exit = false

[screen]
clear_on_rebuild = false
keep_scroll = true
10 changes: 10 additions & 0 deletions frontend/.cursor/rules/border-hover-stylings-for-ui.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
alwaysApply: true
---
Always use `border-fdt-yellow-dark` for light mode and `border-yellow-700` for dark mode when applying hover or focus border colors.

**Colors:**
- Light mode: `border-fdt-yellow-dark` (#b6b000)
- Dark mode: `dark:border-yellow-700` ()

Apply this consistently across all search inputs, buttons, and interactive elements.
70 changes: 70 additions & 0 deletions frontend/cmd/server/routes.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package main

import (
"bytes"
"encoding/json"
"log"
"net/http"
"net/http/pprof"
Expand All @@ -9,6 +11,7 @@ import (
"path/filepath"
"strconv"
"strings"
"time"

"fdt-templ/components/pages"
cheatsheets_pages "fdt-templ/components/pages/cheatsheets"
Expand Down Expand Up @@ -105,6 +108,66 @@ func matchCategoryPagination(path string) (category string, page int, ok bool) {
return "", 0, false
}

// fetchVSXExtensionInstallCount fetches the install count from Open VSX API
func fetchVSXExtensionInstallCount() int {
client := &http.Client{
Timeout: 5 * time.Second,
}

requestBody := map[string]interface{}{
"filters": []map[string]interface{}{
{
"criteria": []map[string]interface{}{
{
"filterType": 7,
"value": "hexmos.freedevtools",
},
},
},
},
"flags": 914,
}

jsonData, err := json.Marshal(requestBody)
if err != nil {
log.Printf("Error marshaling VSX API request: %v", err)
return 0
}

resp, err := client.Post("https://open-vsx.org/vscode/gallery/extensionquery", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
log.Printf("Error fetching VSX extension data: %v", err)
return 0
}
defer resp.Body.Close()

var result struct {
Results []struct {
Extensions []struct {
Statistics []struct {
StatisticName string `json:"statisticName"`
Value float64 `json:"value"`
} `json:"statistics"`
} `json:"extensions"`
} `json:"results"`
}

if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
log.Printf("Error decoding VSX API response: %v", err)
return 0
}

if len(result.Results) > 0 && len(result.Results[0].Extensions) > 0 {
for _, stat := range result.Results[0].Extensions[0].Statistics {
if stat.StatisticName == "install" {
return int(stat.Value)
}
}
}

return 0
}

// Cheatsheets route matching functions are now in cheatsheets_routes.go

func setupRoutes(mux *http.ServeMux, svgIconsDB *svg_icons.DB, manPagesDB *man_pages.DB, emojisDB *emojis.DB, mcpDB *mcp.DB, pngIconsDB *png_icons.DB, cheatsheetsDB *cheatsheets.DB, tldrDB *tldr.DB, installerpediaDB *installerpedia.DB, toolsConfig *tools.Config, fdtPgDB *bookmarks.DB) {
Expand Down Expand Up @@ -192,6 +255,13 @@ func setupRoutes(mux *http.ServeMux, svgIconsDB *svg_icons.DB, manPagesDB *man_p
handler.ServeHTTP(w, r)
})

// VS Code Extension page
mux.HandleFunc(basePath+"/vs-code-extension/", func(w http.ResponseWriter, r *http.Request) {
installCount := fetchVSXExtensionInstallCount()
handler := templ.Handler(static_pages.VSCodeExtension(installCount))
handler.ServeHTTP(w, r)
})

// Pro page
mux.HandleFunc(basePath+"/pro/", func(w http.ResponseWriter, r *http.Request) {
handler := templ.Handler(pro_pages.Pro())
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/breadcrumb.templ
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type BreadcrumbItem struct {
}

templ Breadcrumb(items []BreadcrumbItem) {
<nav id="tool-head-container" class="breadcrumb-container text-sm flex flex-wrap items-center gap-2">
<nav id="tool-head-container" class="breadcrumb-container text-sm flex flex-wrap items-center gap-2 mb-10">
for i, item := range items {
if i > 0 {
<span>/</span>
Expand Down
68 changes: 35 additions & 33 deletions frontend/components/common/banner/ad_banner.templ
Original file line number Diff line number Diff line change
Expand Up @@ -127,37 +127,11 @@ templ AdBanner() {
currentAd, index := GetRandomAdVariation()
link := fmt.Sprintf("https://hexmos.com/livereview/?variation=%d", index)
}}
<script>
(function() {
function checkAndHideLiveReviewBanner() {
const cookieStatus = window.getProStatusCookie();
console.log('LR Read Cookie Status:', cookieStatus);

const banner = document.getElementById('ad-banner');
if (banner) {
if (cookieStatus) {
banner.style.display = 'none';
} else {
banner.style.display = '';
}
}
}

// Check on initial load
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', checkAndHideLiveReviewBanner);
} else {
checkAndHideLiveReviewBanner();
}

// Listen for pro status changes
window.addEventListener('pro-status-changed', checkAndHideLiveReviewBanner);
})();
</script>
<div class="px-2 max-w-6xl mx-auto" style="max-width: 70rem;">
<div class="max-w-6xl" style="max-width: 70rem;">
<div
id="ad-banner"
class="w-full bg-yellow-100 hover:bg-yellow-200 border border-gray-200 shadow-sm rounded-md relative transition-colors duration-300 mb-2 mt-2 md:mb-8 md:mt-6 xl:h-40 xl:mb-8 xl:mt-2 xl:flex xl:justify-center"
class="w-full bg-yellow-100 hover:bg-yellow-200 border border-gray-200 shadow-sm rounded-md relative transition-colors duration-300 mb-2 mt-2 md:mt-0 md:mb-8 xl:h-40 xl:mb-8 xl:flex xl:justify-start"
style="display: none;"
>
<!-- Close Button -->
<button
Expand Down Expand Up @@ -199,7 +173,7 @@ templ AdBanner() {
<p
id="ad-title"
class="text-red-700 leading-tight tracking-wide font-semibold"
style="margin-bottom: 0.5rem; font-size: 1.875rem; line-height: 2.25rem;"
style="margin-bottom: 0.5rem; font-size: 1.5rem; line-height: 2rem;"
>
{currentAd.Title}
</p>
Expand Down Expand Up @@ -241,17 +215,45 @@ templ AdBanner() {
</a>
</div>
</div>
<script>
(function() {
function checkAndHideLiveReviewBanner() {
const cookieStatus = window.getProStatusCookie();
console.log('LR Read Cookie Status:', cookieStatus);

const banner = document.getElementById('ad-banner');
if (banner) {
if (cookieStatus) {
banner.style.display = 'none';
} else {
banner.style.display = '';
}
}
}

// Check immediately - element exists since script is right after it
checkAndHideLiveReviewBanner();

// Also check on DOMContentLoaded as fallback
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', checkAndHideLiveReviewBanner);
}

// Listen for pro status changes
window.addEventListener('pro-status-changed', checkAndHideLiveReviewBanner);
})();
</script>
<style>
/* md: breakpoint for ad banner */
@media (min-width: 768px) {
/* md:px-2 md:py-2 */
#ad-content-wrapper {
padding: 0.5rem !important;
}
/* md:text-5xl md:mb-3 */
/* md:text-4xl md:mb-3 */
#ad-title {
font-size: 3rem !important;
line-height: 1 !important;
font-size: 2.25rem !important;
line-height: 1.2 !important;
margin-bottom: 0.75rem !important;
}
/* md:text-2xl */
Expand Down
54 changes: 32 additions & 22 deletions frontend/components/common/footer.templ
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package common

templ Footer() {
<footer
class="bg-slate-50 dark:bg-slate-900 border-t border-slate-200 dark:border-slate-700 py-8 mt-auto"
class="bg-slate-100 dark:bg-slate-900 py-8 mt-auto"
role="contentinfo"
>
<div class="max-w-6xl mx-auto px-4">
<div class="flex flex-row justify-between items-start gap-6">
<!-- Left Column -->
<div class="flex flex-col space-y-3">
<div class="flex flex-col gap-4 md:gap-8">
<div class="flex flex-col gap-4">
<a
href="https://hexmos.com/livereview/"
target="_blank"
Expand Down Expand Up @@ -80,7 +80,7 @@ templ Footer() {
<img
src="/freedevtools/public/discord_footer.png"
alt="Discord Community"
class="w-5 h-5"
class="w-5 h-5 footer-icon"
width="20"
height="20"
style="width: 20px; height: 20px;"
Expand Down Expand Up @@ -144,19 +144,19 @@ templ Footer() {
</div>

<!-- Product Hunt Badge -->
<a
href="https://www.producthunt.com/products/free-devtools/reviews?utm_source=badge-product_review&utm_medium=badge&utm_source=badge-free-devtools"
target="_blank"
rel="noopener noreferrer"
class="hover:brightness-75 transition-all duration-200"
>
<img
src="/freedevtools/public/product_hunt_badge.svg"
alt="Free DevTools - 50,000+ Free Dev Resources - No Login Required | Product Hunt"
style="width: 180px; height: 54px;"
loading="lazy"
/>
</a>
// <a
// href="https://www.producthunt.com/products/free-devtools/reviews?utm_source=badge-product_review&utm_medium=badge&utm_source=badge-free-devtools"
// target="_blank"
// rel="noopener noreferrer"
// class="hover:brightness-75 transition-all duration-200"
// >
// <img
// src="/freedevtools/public/product_hunt_badge.svg"
// alt="Free DevTools - 50,000+ Free Dev Resources - No Login Required | Product Hunt"
// style="width: 180px; height: 54px;"
// loading="lazy"
// />
// </a>

<!-- Follow us section -->
<div class="flex items-center space-x-5">
Expand Down Expand Up @@ -200,16 +200,26 @@ templ Footer() {
<div
class="flex flex-wrap justify-center gap-3 sm:gap-4 text-center"
>
<a href="https://hexmos.com/about-us.html" class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">About Us</a>
<a href="https://hexmos.com/contact-us.html" class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">Contact Us</a>
<a href="https://hexmos.com/terms-of-service.html" class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">Terms & Conditions</a>
<a href="https://hexmos.com/privacy.html" class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">Privacy Policy</a>
<a href="https://hexmos.com/disclaimer.html" class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">Disclaimer</a>
<a href="https://hexmos.com/affiliate-disclosure.html" class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">Affiliate Disclosure</a>
<a href="https://hexmos.com/about-us.html" class="hover:text-blue-600 dark:hover:text-blue-400 hover:font-bold transition-all duration-200">About Us</a>
<a href="https://hexmos.com/contact-us.html" class="hover:text-blue-600 dark:hover:text-blue-400 hover:font-bold transition-all duration-200">Contact Us</a>
<a href="https://hexmos.com/terms-of-service.html" class="hover:text-blue-600 dark:hover:text-blue-400 hover:font-bold transition-all duration-200">Terms & Conditions</a>
<a href="https://hexmos.com/privacy.html" class="hover:text-blue-600 dark:hover:text-blue-400 hover:font-bold transition-all duration-200">Privacy Policy</a>
<a href="https://hexmos.com/disclaimer.html" class="hover:text-blue-600 dark:hover:text-blue-400 hover:font-bold transition-all duration-200">Disclaimer</a>
<a href="https://hexmos.com/affiliate-disclosure.html" class="hover:text-blue-600 dark:hover:text-blue-400 hover:font-bold transition-all duration-200">Affiliate Disclosure</a>
</div>
</div>
</div>
</footer>
<style>
/* Fix community icon color in dark theme */
.footer-icon {
filter: brightness(0) saturate(100%) invert(0);
transition: filter 0.2s;
}
.dark .footer-icon {
filter: brightness(0) saturate(100%) invert(1);
}
</style>
<script>
// Optimized GitHub star count fetching with caching and error handling
let starCountCache = null;
Expand Down
Loading