Skip to content

Commit 22f0007

Browse files
committed
improve outline UX; add print mode and reading time estimation
1 parent 81c9982 commit 22f0007

File tree

3 files changed

+140
-46
lines changed

3 files changed

+140
-46
lines changed

handlers/api_modifiers_structdef.gen.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ var AllMods Modifiers = Modifiers{
160160
Description: "DeleteOutGoingCookiesExcept prevents non-whitelisted cookies from being sent from the client to the upstream proxy server. Cookies whose names are in the whitelist are not removed.",
161161
CodeEditLink: "https://github.com/everywall/ladder/edit/origin/proxy_v2/proxychain/requestmodifiers/modify_outgoing_cookies.go",
162162
Params: []Param{
163-
{Name: "whitelist", Type: "&{Ellipsis:12348 Elt:string}"},
163+
{Name: "whitelist", Type: "&{Ellipsis:12476 Elt:string}"},
164164
},
165165
},
166166
{
@@ -485,15 +485,15 @@ var AllMods Modifiers = Modifiers{
485485
Description: "DeleteIncomingCookies prevents ALL cookies from being sent from the proxy server back down to the client.",
486486
CodeEditLink: "https://github.com/everywall/ladder/edit/origin/proxy_v2/proxychain/responsemodifiers/modify_incoming_cookies.go",
487487
Params: []Param{
488-
{Name: "_", Type: "&{Ellipsis:16342 Elt:string}"},
488+
{Name: "_", Type: "&{Ellipsis:18778 Elt:string}"},
489489
},
490490
},
491491
{
492492
Name: "DeleteIncomingCookiesExcept",
493493
Description: "DeleteIncomingCookiesExcept prevents non-whitelisted cookies from being sent from the proxy server to the client. Cookies whose names are in the whitelist are not removed.",
494494
CodeEditLink: "https://github.com/everywall/ladder/edit/origin/proxy_v2/proxychain/responsemodifiers/modify_incoming_cookies.go",
495495
Params: []Param{
496-
{Name: "whitelist", Type: "&{Ellipsis:16887 Elt:string}"},
496+
{Name: "whitelist", Type: "&{Ellipsis:19323 Elt:string}"},
497497
},
498498
},
499499
{

proxychain/responsemodifiers/generate_readable_outline.go

+96-16
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,15 @@ import (
77
"html/template"
88
"io"
99
"log"
10+
"math"
1011
"net/url"
1112
"strings"
13+
"time"
1214

1315
"github.com/everywall/ladder/proxychain"
14-
16+
"github.com/markusmobius/go-trafilatura"
1517
"golang.org/x/net/html"
1618
"golang.org/x/net/html/atom"
17-
18-
//"github.com/go-shiori/dom"
19-
"github.com/markusmobius/go-trafilatura"
2019
)
2120

2221
//go:embed vendor/generate_readable_outline.html
@@ -63,19 +62,24 @@ func GenerateReadableOutline() proxychain.ResponseModification {
6362
html.Render(&b, extract.ContentNode)
6463
distilledHTML := b.String()
6564

65+
siteName := strings.Split(extract.Metadata.Sitename, ";")[0]
66+
title := strings.Split(extract.Metadata.Title, "|")[0]
67+
fmtDate := createWikipediaDateLink(extract.Metadata.Date)
68+
readingTime := formatDuration(estimateReadingTime(extract.ContentText))
69+
6670
// populate template parameters
6771
data := map[string]interface{}{
6872
"Success": true,
6973
"Image": extract.Metadata.Image,
7074
"Description": extract.Metadata.Description,
71-
"Sitename": strings.Split(extract.Metadata.Sitename, ";")[0],
75+
"Sitename": siteName,
7276
"Hostname": extract.Metadata.Hostname,
7377
"Url": "/" + chain.Request.URL.String(),
74-
"Title": extract.Metadata.Title, // todo: modify CreateReadableDocument so we don't have <h1> titles duplicated?
75-
"Date": extract.Metadata.Date.String(),
76-
"Author": createWikipediaSearchLinks(extract.Metadata.Author),
77-
//"Author": extract.Metadata.Author,
78-
"Body": distilledHTML,
78+
"Title": title,
79+
"Date": fmtDate,
80+
"Author": createDDGFeelingLuckyLinks(extract.Metadata.Author, extract.Metadata.Hostname),
81+
"Body": distilledHTML,
82+
"ReadingTime": readingTime,
7983
}
8084

8185
// ============================================================================
@@ -157,9 +161,20 @@ func rewriteHrefLinks(n *html.Node, baseURL string, apiPath string) {
157161
recurse(n)
158162
}
159163

160-
// createWikipediaSearchLinks takes in comma or semicolon separated terms,
161-
// then turns them into <a> links searching for the term.
162-
func createWikipediaSearchLinks(searchTerms string) string {
164+
// createWikipediaDateLink takes in a date
165+
// and returns an <a> link pointing to the current events page for that day
166+
func createWikipediaDateLink(t time.Time) string {
167+
url := fmt.Sprintf("https://en.wikipedia.org/wiki/Portal:Current_events#%s", t.Format("2006_January_2"))
168+
date := t.Format("January 2, 2006")
169+
return fmt.Sprintf("<a rel=\"noreferrer\" href=\"%s\">%s</a>", url, date)
170+
}
171+
172+
// createDDGFeelingLuckyLinks takes in comma or semicolon separated terms,
173+
// then turns them into <a> links searching for the term using DuckDuckGo's I'm
174+
// feeling lucky feature. It will redirect the user immediately to the first search result.
175+
func createDDGFeelingLuckyLinks(searchTerms string, siteHostname string) string {
176+
177+
siteHostname = strings.TrimSpace(siteHostname)
163178
semiColonSplit := strings.Split(searchTerms, ";")
164179

165180
var links []string
@@ -171,11 +186,13 @@ func createWikipediaSearchLinks(searchTerms string) string {
171186
continue
172187
}
173188

174-
encodedTerm := url.QueryEscape(trimmedTerm)
189+
ddgQuery := fmt.Sprintf(` site:%s intitle:"%s"`, strings.TrimPrefix(siteHostname, "www."), trimmedTerm)
175190

176-
wikiURL := fmt.Sprintf("https://en.wikipedia.org/w/index.php?search=%s", encodedTerm)
191+
encodedTerm := `\%s:` + url.QueryEscape(ddgQuery)
192+
//ddgURL := `https://html.duckduckgo.com/html/?q=` + encodedTerm
193+
ddgURL := `https://www.duckduckgo.com/?q=` + encodedTerm
177194

178-
link := fmt.Sprintf("<a href=\"%s\">%s</a>", wikiURL, trimmedTerm)
195+
link := fmt.Sprintf("<a rel=\"noreferrer\" href=\"%s\">%s</a>", ddgURL, trimmedTerm)
179196
links = append(links, link)
180197
}
181198

@@ -187,3 +204,66 @@ func createWikipediaSearchLinks(searchTerms string) string {
187204

188205
return strings.Join(links, " ")
189206
}
207+
208+
// estimateReadingTime estimates how long the given text will take to read using the given configuration.
209+
func estimateReadingTime(text string) time.Duration {
210+
if len(text) == 0 {
211+
return 0
212+
}
213+
214+
// Init options with default values.
215+
WordsPerMinute := 200
216+
WordBound := func(b byte) bool {
217+
return b == ' ' || b == '\n' || b == '\r' || b == '\t'
218+
}
219+
220+
words := 0
221+
start := 0
222+
end := len(text) - 1
223+
224+
// Fetch bounds.
225+
for WordBound(text[start]) {
226+
start++
227+
}
228+
for WordBound(text[end]) {
229+
end--
230+
}
231+
232+
// Calculate the number of words.
233+
for i := start; i <= end; {
234+
for i <= end && !WordBound(text[i]) {
235+
i++
236+
}
237+
238+
words++
239+
240+
for i <= end && WordBound(text[i]) {
241+
i++
242+
}
243+
}
244+
245+
// Reading time stats.
246+
minutes := math.Ceil(float64(words) / float64(WordsPerMinute))
247+
duration := time.Duration(math.Ceil(minutes) * float64(time.Minute))
248+
249+
return duration
250+
251+
}
252+
253+
func formatDuration(d time.Duration) string {
254+
// Check if the duration is less than one minute
255+
if d < time.Minute {
256+
seconds := int(d.Seconds())
257+
return fmt.Sprintf("%d seconds", seconds)
258+
}
259+
260+
// Convert the duration to minutes
261+
minutes := int(d.Minutes())
262+
263+
// Format the string for one or more minutes
264+
if minutes == 1 {
265+
return "1 minute"
266+
} else {
267+
return fmt.Sprintf("%d minutes", minutes)
268+
}
269+
}

proxychain/responsemodifiers/vendor/generate_readable_outline.html

+41-27
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
xmlns="http://www.w3.org/2000/svg"
4646
xmlns:xlink="http://www.w3.org/1999/xlink"
4747
viewBox="0 0 512 512"
48-
class="h-8 focus:outline-none focus:ring focus:border-[#7AA7D1] ring-offset-2"
48+
class="noprint h-8 focus:outline-none focus:ring focus:border-[#7AA7D1] ring-offset-2"
4949
>
5050
<path
5151
fill="#7AA7D1"
@@ -67,7 +67,7 @@
6767

6868
</div>
6969

70-
<div class="flex justify-center z-10">
70+
<div class="noprint flex justify-center z-10">
7171
<div class="relative" id="dropdown">
7272
<button
7373
aria-expanded="closed"
@@ -92,6 +92,15 @@
9292
</svg>
9393
</button>
9494

95+
<button
96+
aria-expanded="closed"
97+
onclick="document.getElementById('readingtime').innerText = 'Date Accessed: '+(new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })); [...document.querySelectorAll('.noprint')].forEach(e => e.remove()); window.print()"
98+
type="button"
99+
class="inline-flex items-center justify-center whitespace-nowrap rounded-full h-12 px-4 py-2 text-sm font-medium text-slate-600 dark:text-slate-400 ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-white dark:bg-slate-900 hover:bg-slate-200 dark:hover:bg-slate-700 hover:text-slate-500 dark:hover:text-slate-200"
100+
>
101+
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" height="16" width="16" viewBox="0 0 512 512"><path d="M128 0C92.7 0 64 28.7 64 64v96h64V64H354.7L384 93.3V160h64V93.3c0-17-6.7-33.3-18.7-45.3L400 18.7C388 6.7 371.7 0 354.7 0H128zM384 352v32 64H128V384 368 352H384zm64 32h32c17.7 0 32-14.3 32-32V256c0-35.3-28.7-64-64-64H64c-35.3 0-64 28.7-64 64v96c0 17.7 14.3 32 32 32H64v64c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V384zM432 248a24 24 0 1 1 0 48 24 24 0 1 1 0-48z"/></svg>
102+
</button>
103+
95104
<div
96105
id="dropdown_panel"
97106
class="hidden absolute right-0 mt-2 w-52 rounded-md bg-white dark:bg-slate-900 shadow-md border border-slate-400 dark:border-slate-700"
@@ -326,24 +335,28 @@ <h1>
326335
{{.Error}}
327336
</code>
328337
{{else}}
329-
<div class="flex flex-col gap-1 mt-3">
330-
<h1>
331-
<a href="{{.Url}}" class="text-slate-900 dark:text-slate-200"> {{.Title}} </a>
332-
</h1>
333-
{{if ne .Date ""}}
334-
<small
335-
class="text-sm font-medium leading-none text-slate-600 dark:text-slate-400"
336-
>{{.Date}}</small
337-
>
338-
{{end}}
339-
{{if ne .Author ""}}
340-
<small
341-
class="text-sm font-medium leading-none text-slate-600 dark:text-slate-400"
342-
>{{.Author}}</small
343-
>
344-
{{end}}
345-
</div>
346338

339+
<h2>
340+
<a href="{{.Url}}" class="text-slate-900 dark:text-slate-200"> {{.Title}} </a>
341+
</h2>
342+
343+
<div class="flex justify-between items-center gap-1 mt-3">
344+
345+
346+
<div>
347+
{{if ne .Author ""}}
348+
<small class="text-sm font-medium leading-none text-slate-600 dark:text-slate-400">{{.Author}} | </small>
349+
{{end}}
350+
351+
{{if ne .Date ""}}
352+
<small class="text-sm font-medium leading-none text-slate-600 dark:text-slate-400">{{.Date}}</small>
353+
{{end}}
354+
</div>
355+
356+
<div>
357+
<small id="readingtime" class="text-sm font-medium leading-none text-slate-600 dark:text-slate-400">Reading Time: {{.ReadingTime}}</small>
358+
</div>
359+
</div>
347360
<div class="flex flex-col space-y-3">
348361
<div>
349362
<div class="grid grid-cols-1 justify-items-center">
@@ -357,21 +370,22 @@ <h1>
357370
</main>
358371

359372
<div class="my-2"></div>
360-
<footer class="mx-4 text-center text-slate-600 dark:text-slate-400">
361-
<p>
362-
Code Licensed Under GPL v3.0 |
373+
<footer class="noprint mx-4 my-2 pt-2 border-t border-gray-300 dark:border-gray-700 text-center text-slate-600 dark:text-slate-400">
374+
375+
<small>
363376
<a
364-
href="https://github.com/everywall/ladder"
377+
href="https://github.com/everywall"
365378
class="hover:text-blue-500 dark:hover:text-blue-500 hover:underline underline-offset-2 transition-colors duration-300"
366-
>View Source</a
379+
>Everywall</a
367380
>
368381
|
369382
<a
370-
href="https://github.com/everywall"
383+
href="https://github.com/everywall/ladder"
371384
class="hover:text-blue-500 dark:hover:text-blue-500 hover:underline underline-offset-2 transition-colors duration-300"
372-
>Everywall</a
385+
>Source</a
373386
>
374-
</p>
387+
| Code Licensed Under GPL v3.0
388+
</small>
375389
</footer>
376390
<div class="my-2"></div>
377391
</div>

0 commit comments

Comments
 (0)