Compare commits

...

9 Commits

Author SHA1 Message Date
amnesia
5470ba1298 remove elements if new tab 2025-07-18 10:21:57 +02:00
Hazel Noack
13fae1c23f implemented search on youtube 2025-07-16 17:38:28 +02:00
Hazel Noack
21719a6cf7 added demo toml 2025-07-16 17:27:03 +02:00
addaade269 Merge pull request 'layed out goroutine' (#2) from diyhrt/interval_fetching into main
Reviewed-on: #2
2025-07-16 15:22:43 +00:00
Hazel Noack
6ee6c9c8d9 layed out goroutine 2025-07-16 16:59:40 +02:00
Hazel Noack
a0133e0981 edited todo 2025-07-16 16:38:47 +02:00
70c668bdf1 Merge pull request 'cli' (#1) from cli into main
Reviewed-on: #1
2025-07-16 14:35:52 +00:00
Hazel Noack
68c89de1a4 made some metadata of the program dynamic 2025-07-16 16:25:22 +02:00
Hazel Noack
0b7de76874 edited readme 2025-07-16 16:13:48 +02:00
13 changed files with 143 additions and 92 deletions

View File

@@ -13,7 +13,7 @@ go install gitea.elara.ws/Hazel/transfem-startpage
Then you can run the program `transfem-startpage` Then you can run the program `transfem-startpage`
```sh ```sh
transfem-startpage transfem-startpage help
``` ```
To configure this new tab page as website, you can install the firefox extension [New Tab Override](https://addons.mozilla.org/en-US/firefox/addon/new-tab-override/). Then just configure the url as `http://127.0.0.1:{port}/`. The default port should be `5500` but it will also print it out when starting the server. Make sure to check the box `Set focus to the web page instead of the address bar` in the extension settings, because the new tab page auto focuses the search bar. To configure this new tab page as website, you can install the firefox extension [New Tab Override](https://addons.mozilla.org/en-US/firefox/addon/new-tab-override/). Then just configure the url as `http://127.0.0.1:{port}/`. The default port should be `5500` but it will also print it out when starting the server. Make sure to check the box `Set focus to the web page instead of the address bar` in the extension settings, because the new tab page auto focuses the search bar.
@@ -55,10 +55,9 @@ air dev
## TODO ## TODO
- implementing proper command line args - implement templating for every one of the frontend files
- clear cache - implement functionality to clear and clean cache
- implement fetching in intervals - host this website on a demo page
- host this website on a demo page
- implement ctl - implement ctl
- implement autocomplete with a nice go backend and fast communication. Since it all runs locally nobody should have privacy concerns NEEDS TO BE ABLE TO TOGGLED OFF FOR DEMO PAGE - implement autocomplete with a nice go backend and fast communication. Since it all runs locally nobody should have privacy concerns NEEDS TO BE ABLE TO TOGGLED OFF FOR DEMO PAGE

17
demo.toml Normal file
View File

@@ -0,0 +1,17 @@
[Server]
Port = 1312
[DiyHrt]
FetchIntervals = 60
[Template]
ActiveCard = "listings"
PageTitle = "TransfemStartpage demo"
HeaderPhrases = [
"GirlJuice.Inject();",
"You.Cute = true;",
"You.Gay = true;",
"Nazi.Punch();",
"Dolls.GiveGuns();",
"Firefox > Chrome"
]

View File

@@ -49,16 +49,6 @@ body {
color: black; color: black;
} }
@media (max-height: 300px) {
.search-grid {
grid-template-rows: 4em;
}
.search-logo {
display: none;
}
}
.cards { .cards {
height: 100%; height: 100%;
width: 100%; width: 100%;
@@ -70,6 +60,20 @@ body {
overflow: auto; overflow: auto;
} }
@media (max-height: 500px) {
.search-grid {
grid-template-rows: 4em;
}
.search-logo {
display: none;
}
.cards {
display: none;
}
}
.card { .card {
background-color: rgba(255, 255, 255, 0.5); background-color: rgba(255, 255, 255, 0.5);
width: 10em; width: 10em;
@@ -97,7 +101,6 @@ body {
margin: 0; margin: 0;
} }
.card-image { .card-image {
height: 100%; height: 100%;
aspect-ratio: 1/1; aspect-ratio: 1/1;

View File

@@ -4,90 +4,94 @@ const form = document.getElementById("search-form");
const input = document.getElementById("search-input"); const input = document.getElementById("search-input");
// https://stackoverflow.com/a/3809435/16804841 // https://stackoverflow.com/a/3809435/16804841
const expression = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi; const expression =
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi;
const urlRegex = new RegExp(expression); const urlRegex = new RegExp(expression);
const searchEngines = { const searchEngines = {
"g": { g: {
action: "https://www.google.com/search", action: "https://www.google.com/search",
name: "q", name: "q",
}, },
"d": { d: {
action: "https://duckduckgo.com/", action: "https://duckduckgo.com/",
name: "q", name: "q",
}, },
"y": { y: {
action: "https://yandex.com/search/", action: "https://www.youtube.com/results",
name: "text", name: "search_query",
}, },
"lure": { ya: {
action: "https://lure.sh/pkgs", action: "https://yandex.com/search/",
name: "q", name: "text",
}, },
lure: {
action: "https://lure.sh/pkgs",
name: "q",
},
}; };
const translationPrefixes = [ const translationPrefixes = ["t", "translation"];
"t",
"translation",
]
function getDeepLUrl(s) { function getDeepLUrl(s) {
const parts = s.split("-") const parts = s.split("-");
if (parts.length != 3) { if (parts.length != 3) {
return undefined return undefined;
} }
return `https://www.deepl.com/en/translator?/#${encodeURIComponent(parts[0].trim())}/${encodeURIComponent(parts[1].trim())}/${encodeURIComponent(parts[2].trim())}`; return `https://www.deepl.com/en/translator?/#${encodeURIComponent(
parts[0].trim()
)}/${encodeURIComponent(parts[1].trim())}/${encodeURIComponent(
parts[2].trim()
)}`;
} }
form.addEventListener("submit", (event) => {
event.preventDefault();
form.addEventListener("submit", event => { s = input.value;
event.preventDefault();
s = input.value; // check if url
if (s.match(urlRegex)) {
window.open(s, "_self");
return;
}
// check if url // deepl translations
if (s.match(urlRegex)) { let doTranslation = false;
window.open(s, "_self"); for (const value of translationPrefixes) {
return const prefix = `!${value} `;
if (s.startsWith(prefix)) {
doTranslation = true;
s = s.slice(prefix.length); // Remove the !{key} prefix
break;
} }
}
// deepl translations if (doTranslation) {
let doTranslation = false; const url = getDeepLUrl(s);
for (const value of translationPrefixes) { if (url) {
const prefix = `!${value} `; window.open(url.toString(), "_self");
if (s.startsWith(prefix)) { return;
doTranslation = true;
s = s.slice(prefix.length); // Remove the !{key} prefix
break;
}
} }
}
if (doTranslation) { // Check if the string starts with ! followed by a key from searchEngines
const url = getDeepLUrl(s); let selectedEngine = {
if (url) { action: form.getAttribute("action"),
window.open(url.toString(), "_self"); name: input.getAttribute("name"),
return; };
}
for (const [key, value] of Object.entries(searchEngines)) {
const prefix = `!${key} `;
if (s.startsWith(prefix)) {
selectedEngine = value;
s = s.slice(prefix.length); // Remove the !{key} prefix
break;
} }
}
// Check if the string starts with ! followed by a key from searchEngines const url = new URL(selectedEngine.action);
let selectedEngine = { url.searchParams.set(selectedEngine.name, s.trim());
action: form.getAttribute("action"), window.open(url.toString(), "_self");
name: input.getAttribute("name"),
};
for (const [key, value] of Object.entries(searchEngines)) {
const prefix = `!${key} `;
if (s.startsWith(prefix)) {
selectedEngine = value;
s = s.slice(prefix.length); // Remove the !{key} prefix
break;
}
}
const url = new URL(selectedEngine.action);
url.searchParams.set(selectedEngine.name, s.trim());
window.open(url.toString(), "_self");
}); });

View File

@@ -10,6 +10,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"gitea.elara.ws/Hazel/transfem-startpage/internal/utils"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
) )
@@ -23,7 +24,7 @@ func getCacheDir() (string, error) {
if err != nil { if err != nil {
baseDir = "/tmp" baseDir = "/tmp"
} }
cacheDir := filepath.Join(baseDir, "startpage") cacheDir := filepath.Join(baseDir, utils.Name)
err = os.MkdirAll(cacheDir, 0o755) err = os.MkdirAll(cacheDir, 0o755)
if err != nil { if err != nil {
return "", err return "", err

View File

@@ -4,6 +4,7 @@ import (
"log" "log"
"os" "os"
"gitea.elara.ws/Hazel/transfem-startpage/internal/utils"
"github.com/TwiN/go-color" "github.com/TwiN/go-color"
) )
@@ -22,8 +23,8 @@ type Argument struct {
Description string Description string
} }
var HelpHeader = `This is the help page of transfem-startpage. var HelpHeader = `This is the help page of ` + utils.Name + `.
` + color.Purple + `transfem-startpage {program} {...args}` + color.Reset + ` ` + color.Purple + utils.BinaryName + ` {program} {...args}` + color.Reset + `
The following Programs are available:` The following Programs are available:`
var Programs = []Program{ var Programs = []Program{
{ {

View File

@@ -5,6 +5,7 @@ import (
"os" "os"
"strings" "strings"
"gitea.elara.ws/Hazel/transfem-startpage/internal/utils"
"github.com/TwiN/go-color" "github.com/TwiN/go-color"
) )
@@ -55,7 +56,7 @@ func specificHelp(programName string) error {
fmt.Println(color.Bold + "MAN PAGE FOR " + strings.ToUpper(programName) + color.Reset) fmt.Println(color.Bold + "MAN PAGE FOR " + strings.ToUpper(programName) + color.Reset)
fmt.Println() fmt.Println()
fmt.Println(color.Purple + "transfem-startpage " + programName + color.Reset + getArgumentString(program.Arguments)) fmt.Println(color.Purple + utils.BinaryName + " " + programName + color.Reset + getArgumentString(program.Arguments))
fmt.Println() fmt.Println()
fmt.Println(color.Bold + "arguments" + color.Reset) fmt.Println(color.Bold + "arguments" + color.Reset)

View File

@@ -1,7 +1,9 @@
package diyhrt package diyhrt
type DiyHrtConfig struct { type DiyHrtConfig struct {
ApiKey string ApiKey string
FetchIntervals int
StoreFilter StoreFilter StoreFilter StoreFilter
ListingFilter ListingFilter ListingFilter ListingFilter
} }

View File

@@ -11,7 +11,7 @@ const endpoint = "https://diyhrt.market/api/listings"
func GetListings(apiKey string) ([]Listing, error) { func GetListings(apiKey string) ([]Listing, error) {
if apiKey == "" { if apiKey == "" {
return nil, errors.New("API_KEY key not set. Set it as env or in DiyHrt.ApiKey") return nil, errors.New("diyhrt API_KEY key not set. Set it as env or in DiyHrt.ApiKey")
} }
// Create HTTP client // Create HTTP client

View File

@@ -7,6 +7,7 @@ import (
"path/filepath" "path/filepath"
"gitea.elara.ws/Hazel/transfem-startpage/internal/diyhrt" "gitea.elara.ws/Hazel/transfem-startpage/internal/diyhrt"
"gitea.elara.ws/Hazel/transfem-startpage/internal/utils"
"github.com/pelletier/go-toml" "github.com/pelletier/go-toml"
) )
@@ -62,7 +63,8 @@ func NewConfig() Config {
Port: 5500, Port: 5500,
}, },
DiyHrt: diyhrt.DiyHrtConfig{ DiyHrt: diyhrt.DiyHrtConfig{
ApiKey: os.Getenv("API_KEY"), ApiKey: os.Getenv("API_KEY"),
FetchIntervals: 60, // fetch every hour
StoreFilter: diyhrt.StoreFilter{ StoreFilter: diyhrt.StoreFilter{
Limit: 0, Limit: 0,
IncludeIds: []int{7}, IncludeIds: []int{7},
@@ -105,7 +107,7 @@ func (rc *Config) ScanForConfigFile(profile string) error {
baseDir, cacheDirErr := os.UserConfigDir() baseDir, cacheDirErr := os.UserConfigDir()
if cacheDirErr == nil { if cacheDirErr == nil {
configFile := filepath.Join(baseDir, "startpage", profileFile) configFile := filepath.Join(baseDir, utils.Name, profileFile)
if err := rc.LoadConfigFile(configFile); !errors.Is(err, os.ErrNotExist) { if err := rc.LoadConfigFile(configFile); !errors.Is(err, os.ErrNotExist) {
return err return err

View File

@@ -4,6 +4,7 @@ import (
"log" "log"
"net/http" "net/http"
"strconv" "strconv"
"time"
"gitea.elara.ws/Hazel/transfem-startpage/internal/cache" "gitea.elara.ws/Hazel/transfem-startpage/internal/cache"
"gitea.elara.ws/Hazel/transfem-startpage/internal/rendering" "gitea.elara.ws/Hazel/transfem-startpage/internal/rendering"
@@ -12,12 +13,26 @@ import (
var Config = rendering.NewConfig() var Config = rendering.NewConfig()
func StartFetching() {
for {
log.Println("Fetch DiyHrt data...")
Config.FetchDiyHrt()
time.Sleep(time.Duration(Config.DiyHrt.FetchIntervals) * time.Second)
if Config.DiyHrt.FetchIntervals == 0 {
break
}
}
}
func Start(profile string) error { func Start(profile string) error {
err := Config.ScanForConfigFile(profile) err := Config.ScanForConfigFile(profile)
if err != nil { if err != nil {
return err return err
} }
go StartFetching()
err = Config.FetchDiyHrt() err = Config.FetchDiyHrt()
if err != nil { if err != nil {
log.Println(err) log.Println(err)

6
internal/utils/meta.go Normal file
View File

@@ -0,0 +1,6 @@
package utils
import "os"
var Name = "transfem-startpage"
var BinaryName = os.Args[0]

View File

@@ -1 +1 @@
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1