diff options
24 files changed, 214 insertions, 189 deletions
@@ -8,10 +8,10 @@ dev: build | |||
8 | build: clean | 8 | build: clean |
9 | sassc -t compressed css/main.scss assets/style.css | 9 | sassc -t compressed css/main.scss assets/style.css |
10 | tsc --strict --module none --outFile /dev/stdout js/* | terser --compress --mangle -o assets/main.js -- | 10 | tsc --strict --module none --outFile /dev/stdout js/* | terser --compress --mangle -o assets/main.js -- |
11 | #pyftsubset fonts/iosevka-term-ss03-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff' --with-zopfli --output-file='assets/iosevka-term-ss03-regular.woff' | 11 | #pyftsubset fonts/iosevka-fixed-ss03-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff' --with-zopfli --output-file='assets/iosevka-fixed-ss03-regular.woff' |
12 | #pyftsubset fonts/iosevka-term-ss03-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff2' --output-file='assets/iosevka-term-ss03-regular.woff2' | 12 | #pyftsubset fonts/iosevka-fixed-ss03-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff2' --output-file='assets/iosevka-fixed-ss03-regular.woff2' |
13 | #pyftsubset fonts/iosevka-aile-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff' --with-zopfli --output-file='assets/iosevka-aile-regular.woff' | 13 | #pyftsubset fonts/iosevka-fixed-ss03-bold.ttf "*" --name-IDs+=0,4,6 --flavor='woff' --with-zopfli --output-file='assets/iosevka-fixed-ss03-bold.woff' |
14 | #pyftsubset fonts/iosevka-aile-regular.ttf "*" --name-IDs+=0,4,6 --flavor='woff2' --output-file='assets/iosevka-aile-regular.woff2' | 14 | #pyftsubset fonts/iosevka-fixed-ss03-bold.ttf "*" --name-IDs+=0,4,6 --flavor='woff2' --output-file='assets/iosevka-fixed-ss03-bold.woff2' |
15 | go build -o ./port.bin ./cmd/port | 15 | go build -o ./port.bin ./cmd/port |
16 | 16 | ||
17 | profile: | 17 | profile: |
diff --git a/assets/iosevka-aile-regular.woff b/assets/iosevka-aile-regular.woff deleted file mode 100644 index 97b42ea..0000000 --- a/assets/iosevka-aile-regular.woff +++ /dev/null | |||
Binary files differ | |||
diff --git a/assets/iosevka-aile-regular.woff2 b/assets/iosevka-aile-regular.woff2 deleted file mode 100644 index fea3967..0000000 --- a/assets/iosevka-aile-regular.woff2 +++ /dev/null | |||
Binary files differ | |||
diff --git a/assets/iosevka-fixed-ss03-bold.woff b/assets/iosevka-fixed-ss03-bold.woff new file mode 100644 index 0000000..0ae2733 --- /dev/null +++ b/assets/iosevka-fixed-ss03-bold.woff | |||
Binary files differ | |||
diff --git a/assets/iosevka-fixed-ss03-bold.woff2 b/assets/iosevka-fixed-ss03-bold.woff2 new file mode 100644 index 0000000..317ee3c --- /dev/null +++ b/assets/iosevka-fixed-ss03-bold.woff2 | |||
Binary files differ | |||
diff --git a/assets/iosevka-fixed-ss03-regular.woff b/assets/iosevka-fixed-ss03-regular.woff new file mode 100644 index 0000000..6168a4a --- /dev/null +++ b/assets/iosevka-fixed-ss03-regular.woff | |||
Binary files differ | |||
diff --git a/assets/iosevka-fixed-ss03-regular.woff2 b/assets/iosevka-fixed-ss03-regular.woff2 new file mode 100644 index 0000000..27f81cb --- /dev/null +++ b/assets/iosevka-fixed-ss03-regular.woff2 | |||
Binary files differ | |||
diff --git a/assets/iosevka-term-ss03-regular.woff b/assets/iosevka-term-ss03-regular.woff deleted file mode 100644 index eb2568a..0000000 --- a/assets/iosevka-term-ss03-regular.woff +++ /dev/null | |||
Binary files differ | |||
diff --git a/assets/iosevka-term-ss03-regular.woff2 b/assets/iosevka-term-ss03-regular.woff2 deleted file mode 100644 index 957b020..0000000 --- a/assets/iosevka-term-ss03-regular.woff2 +++ /dev/null | |||
Binary files differ | |||
diff --git a/assets/main.js b/assets/main.js index eee70d7..1fa4dcf 100644 --- a/assets/main.js +++ b/assets/main.js | |||
@@ -1 +1 @@ | |||
"use strict";var KeyValueStore=function(){function e(e){this.data=e;for(var t=0,n=Object.keys(e);t<n.length;t++){var a=n[t],l=e[a];if(l.valueRange&&-1===l.valueRange.indexOf(l.value))throw new Error('Invalid value "'+l.value+'" for ID "'+a+'"')}}return e.prototype.getValue=function(e){return this.data[e].value},e.prototype.setValue=function(e,t){var n=this.data[e];if(n.valueRange&&-1===n.valueRange.indexOf(t))throw new Error('Invalid value "'+t+'" for ID "'+e+'"');n.value=t,n.callbacks&&n.callbacks.forEach((function(e){e(t)}))},e.prototype.cycleValue=function(e,t){void 0===t&&(t=1);var n=this.data[e];if(!n)throw new Error('Invalid ID "'+e+'"');var a=n.value;if(n.valueRange){var l=n.valueRange.indexOf(a)+t;l>=n.valueRange.length?l=0:l<0&&(l=n.valueRange.length-1),a=n.value=n.valueRange[l]}else{if("number"!=typeof a)throw new Error("Can't cycle \""+e+'"');a+=t,n.value=a}return n.callbacks&&n.callbacks.forEach((function(e){e(a)})),a},e.prototype.addCallback=function(e,t){var n=this.data[e];n.callbacks||(n.callbacks=[]),n.callbacks.push(t)},e}();function ensureSetting(e,t){var n=localStorage.getItem(e);return null===n&&(n=t,localStorage.setItem(e,n)),n}var settings=new KeyValueStore({wordWrap:{value:"1"===ensureSetting("word-wrap","1"),callbacks:[function(e){localStorage.setItem("word-wrap",e?"1":"0")}],valueRange:[!1,!0]},monospaceFont:{value:"1"===ensureSetting("monospace-font","1"),callbacks:[function(e){localStorage.setItem("monospace-font",e?"1":"0")}],valueRange:[!1,!0]},imagePreviews:{value:"1"===ensureSetting("image-previews","1"),callbacks:[function(e){localStorage.setItem("image-previews",e?"1":"0")}],valueRange:[!1,!0]},clickablePlainLinks:{value:"1"===ensureSetting("clickable-plain-links","1"),callbacks:[function(e){localStorage.setItem("clickable-plain-links",e?"1":"0")}],valueRange:[!1,!0]}});function generateImageThumbnails(){for(var e=document.querySelectorAll(".link--IMG, .link--GIF"),t=e.length,n=function(){var n=e[t],a=n.href.replace(/^(.*?)\/I/,"$1/T"),l=document.createTextNode("\n"),i=document.createElement("span");i.classList.add("type-annotation"),i.textContent=" -> ";var s=document.createElement("img");s.src=a,s.addEventListener("load",(function(e){s.classList.remove("faded")}));var r=document.createElement("a");r.classList.add("img-preview"),r.href=n.href,r.addEventListener("click",(function(e){return e.preventDefault(),s.classList.add("faded"),s.classList.contains("expanded")?(s.classList.remove("expanded"),s.src=a):(s.classList.add("expanded"),s.src=r.href),!1})),r.append(s),n.parentNode.insertBefore(r,n.nextSibling),n.parentNode.insertBefore(i,r),n.parentNode.insertBefore(l,i)};t--;)n()}function removeImageThumbnails(){for(var e=document.querySelectorAll(".link--IMG, .link--GIF"),t=e.length;t--;)for(var n=e[t],a=3;a--&&n.nextSibling;)n.nextSibling.remove()}function generateMarkupForPlainLinks(){if(document.body.classList.contains("is-plain")){var e=document.getElementsByClassName("content")[0];e.innerHTML=e.innerHTML.replace(/\b[a-z]*:\/\/[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,}\b[-a-zA-Z0-9@:%_\+.,~#?&//=]*/g,(function(e){var t=e;return 0===t.indexOf("gopher://")?t=t.replace(/^gopher:\/\/(.*)$/,location.origin+"/gopher/$1"):0===t.indexOf("gemini://")&&(t=t.replace(/^gemini:\/\/(.*)$/,location.origin+"/gemini/$1")),'<a href="'+t+'">'+e+"</a>"})),e.innerHTML=e.innerHTML.replace(/\bmailto:[-a-zA-Z0-9@:%._\+~#=]+@(?:[-a-zA-Z0-9@:%._\+~#=]+\.)+[a-z]{2,}\b/g,(function(e){return'<a href="'+e+'">'+e+"</a>"}))}}function removeMarkupForPlainLinks(){if(document.body.classList.contains("is-plain"))for(var e=document.getElementsByClassName("content")[0],t=e.getElementsByTagName("a"),n=t.length;n--;){var a=t[n],l=document.createTextNode(a.textContent);e.replaceChild(l,a)}}!function(){for(var e=document.getElementsByClassName("link--QRY"),t=e.length;t--;)e[t].addEventListener("click",(function(e){e.preventDefault();var t=prompt("Please enter required input: ","");return null!==t&&""!==t&&(window.location.href=e.target.href+"?"+t),!1}))}(),function(){for(var e=document.getElementsByClassName("location__prefix"),t=e.length;t--;)e[t].addEventListener("click",(function(e){e.preventDefault();var t=prompt("Please enter new location (gopher://... or gemini://...):","");return null!==t&&""!==t.trim()&&(t=0===(t=t.trim()).indexOf("gopher://")?"gopher/"+t.substring(9):0===t.indexOf("gemini://")?"gemini/"+t.substring(9):"gopher/"+t,window.location.href=window.location.origin+"/"+t),!1}))}(),function(){var e=document.getElementsByClassName("wrap")[0],t=e.getElementsByClassName("content")[0],n=document.getElementsByClassName("setting--image-previews")[0].getElementsByClassName("setting__value")[0],a=function(e,t){void 0===t&&(t=!1),e?generateImageThumbnails():t||removeImageThumbnails(),n.textContent=e?"[yes]":"[no]"};n.addEventListener("click",(function(e){return e.preventDefault(),settings.cycleValue("imagePreviews"),!1})),a(settings.getValue("imagePreviews"),!0),settings.addCallback("imagePreviews",a);var l=document.getElementsByClassName("setting--monospace-font")[0].getElementsByClassName("setting__value")[0],i=function(e){e?t.classList.add("content--has-monospace-font"):t.classList.remove("content--has-monospace-font"),l.textContent=e?"[yes]":"[no]"};l.addEventListener("click",(function(e){return e.preventDefault(),settings.cycleValue("monospaceFont"),!1})),i(settings.getValue("monospaceFont")),settings.addCallback("monospaceFont",i);var s=document.getElementsByClassName("setting--word-wrap")[0].getElementsByClassName("setting__value")[0],r=function(t){t?e.classList.add("wrap--word-wrap"):e.classList.remove("wrap--word-wrap"),s.textContent=t?"[yes]":"[no]"};s.addEventListener("click",(function(e){return e.preventDefault(),settings.cycleValue("wordWrap"),!1})),r(settings.getValue("wordWrap")),settings.addCallback("wordWrap",r);var o=document.getElementsByClassName("setting--clickable-plain-links")[0].getElementsByClassName("setting__value")[0],c=function(e){e?generateMarkupForPlainLinks():removeMarkupForPlainLinks(),o.textContent=e?"[yes]":"[no]"};o.addEventListener("click",(function(e){return e.preventDefault(),settings.cycleValue("clickablePlainLinks"),!1})),c(settings.getValue("clickablePlainLinks")),settings.addCallback("clickablePlainLinks",c)}(),function(){for(var e=document.getElementsByClassName("modal"),t=e.length,n=function(){var n=e[t],a=n.getElementsByClassName("modal__content")[0],l=n.getElementsByClassName("modal__close-btn")[0];document.addEventListener("click",(function(e){n.classList.contains("modal--visible")&&(e.target===a||a.contains(e.target)||(n.classList.remove("modal--visible"),e.preventDefault(),e.stopPropagation()))}),!0),document.addEventListener("keydown",(function(e){n.classList.contains("modal--visible")&&27===e.keyCode&&n.classList.remove("modal--visible")})),l.addEventListener("click",(function(e){return e.preventDefault(),n.classList.remove("modal--visible"),!1}))};t--;)n();var a=document.getElementsByClassName("settings-btn")[0],l=document.getElementsByClassName("modal--settings")[0];a.addEventListener("click",(function(e){return e.preventDefault(),l.classList.add("modal--visible"),!1}))}(); \ No newline at end of file | "use strict";var KeyValueStore=function(){function e(e){this.data=e;for(var t=0,n=Object.keys(e);t<n.length;t++){var a=n[t],l=e[a];if(l.valueRange&&-1===l.valueRange.indexOf(l.value))throw new Error('Invalid value "'+l.value+'" for ID "'+a+'"')}}return e.prototype.getValue=function(e){return this.data[e].value},e.prototype.setValue=function(e,t){var n=this.data[e];if(n.valueRange&&-1===n.valueRange.indexOf(t))throw new Error('Invalid value "'+t+'" for ID "'+e+'"');n.value=t,n.callbacks&&n.callbacks.forEach((function(e){e(t)}))},e.prototype.cycleValue=function(e,t){void 0===t&&(t=1);var n=this.data[e];if(!n)throw new Error('Invalid ID "'+e+'"');var a=n.value;if(n.valueRange){var l=n.valueRange.indexOf(a)+t;l>=n.valueRange.length?l=0:l<0&&(l=n.valueRange.length-1),a=n.value=n.valueRange[l]}else{if("number"!=typeof a)throw new Error("Can't cycle \""+e+'"');a+=t,n.value=a}return n.callbacks&&n.callbacks.forEach((function(e){e(a)})),a},e.prototype.addCallback=function(e,t){var n=this.data[e];n.callbacks||(n.callbacks=[]),n.callbacks.push(t)},e}();function ensureSetting(e,t){var n=localStorage.getItem(e);return null===n&&(n=t,localStorage.setItem(e,n)),n}var settings=new KeyValueStore({wordWrap:{value:"1"===ensureSetting("word-wrap","1"),callbacks:[function(e){localStorage.setItem("word-wrap",e?"1":"0")}],valueRange:[!1,!0]},monospaceFont:{value:"1"===ensureSetting("monospace-font","1"),callbacks:[function(e){localStorage.setItem("monospace-font",e?"1":"0")}],valueRange:[!1,!0]},imagePreviews:{value:"1"===ensureSetting("image-previews","1"),callbacks:[function(e){localStorage.setItem("image-previews",e?"1":"0")}],valueRange:[!1,!0]},clickablePlainLinks:{value:"1"===ensureSetting("clickable-plain-links","1"),callbacks:[function(e){localStorage.setItem("clickable-plain-links",e?"1":"0")}],valueRange:[!1,!0]}});function generateImageThumbnails(){for(var e=document.querySelectorAll(".link--IMG, .link--GIF"),t=e.length,n=function(){var n=e[t],a=n.href.replace(/^(.*?)\/I/,"$1/T"),l=document.createTextNode("\n"),i=document.createElement("span");i.classList.add("type-annotation"),i.textContent=" -> ";var s=document.createElement("img");s.src=a,s.addEventListener("load",(function(e){s.classList.remove("faded")}));var r=document.createElement("a");r.classList.add("img-preview"),r.href=n.href,r.addEventListener("click",(function(e){return e.preventDefault(),s.classList.add("faded"),s.classList.contains("expanded")?(s.classList.remove("expanded"),s.src=a):(s.classList.add("expanded"),s.src=r.href),!1})),r.append(s),n.parentNode.insertBefore(r,n.nextSibling),n.parentNode.insertBefore(i,r),n.parentNode.insertBefore(l,i)};t--;)n()}function removeImageThumbnails(){for(var e=document.querySelectorAll(".link--IMG, .link--GIF"),t=e.length;t--;)for(var n=e[t],a=3;a--&&n.nextSibling;)n.nextSibling.remove()}function generateMarkupForPlainLinks(){if(document.body.classList.contains("is-plain")){var e=document.getElementsByClassName("content")[0];e.innerHTML=e.innerHTML.replace(/\b[a-z]*:\/\/[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,}\b[-a-zA-Z0-9@:%_\+.,~#?&//=]*/g,(function(e){var t=e;return 0===t.indexOf("gopher://")?t=t.replace(/^gopher:\/\/(.*)$/,location.origin+"/gopher/$1"):0===t.indexOf("gemini://")&&(t=t.replace(/^gemini:\/\/(.*)$/,location.origin+"/gemini/$1")),'<a href="'+t+'">'+e+"</a>"})),e.innerHTML=e.innerHTML.replace(/\bmailto:[-a-zA-Z0-9@:%._\+~#=]+@(?:[-a-zA-Z0-9@:%._\+~#=]+\.)+[a-z]{2,}\b/g,(function(e){return'<a href="'+e+'">'+e+"</a>"}))}}function removeMarkupForPlainLinks(){if(document.body.classList.contains("is-plain"))for(var e=document.getElementsByClassName("content")[0],t=e.getElementsByTagName("a"),n=t.length;n--;){var a=t[n],l=document.createTextNode(a.textContent);e.replaceChild(l,a)}}!function(){for(var e=document.getElementsByClassName("link--QRY"),t=e.length;t--;)e[t].addEventListener("click",(function(e){e.preventDefault();var t=prompt("Please enter required input: ","");return null!==t&&""!==t&&(window.location.href=e.target.href+"?"+t),!1}))}(),function(){for(var e=document.getElementsByClassName("location__prefix"),t=e.length;t--;)e[t].addEventListener("click",(function(e){e.preventDefault();var t=prompt("Please enter new location (gopher://... or gemini://...):","");return null!==t&&""!==t.trim()&&(t=0===(t=t.trim()).indexOf("gopher://")?"gopher/"+t.substring(9):0===t.indexOf("gemini://")?"gemini/"+t.substring(9):"gopher/"+t,window.location.href=window.location.origin+"/"+t),!1}))}(),function(){var e=document.getElementsByClassName("wrap")[0],t=e.getElementsByClassName("content")[0],n=document.getElementsByClassName("setting--image-previews")[0].getElementsByClassName("setting__value")[0],a=function(e,t){void 0===t&&(t=!1),e?generateImageThumbnails():t||removeImageThumbnails(),n.textContent=e?"[yes]":"[no]"};n.addEventListener("click",(function(e){return e.preventDefault(),settings.cycleValue("imagePreviews"),!1})),a(settings.getValue("imagePreviews"),!0),settings.addCallback("imagePreviews",a);var l=document.getElementsByClassName("setting--monospace-font")[0].getElementsByClassName("setting__value")[0],i=function(e){e?t.classList.add("content--prefer-monospace"):t.classList.remove("content--prefer-monospace"),l.textContent=e?"[yes]":"[no]"};l.addEventListener("click",(function(e){return e.preventDefault(),settings.cycleValue("monospaceFont"),!1})),i(settings.getValue("monospaceFont")),settings.addCallback("monospaceFont",i);var s=document.getElementsByClassName("setting--word-wrap")[0].getElementsByClassName("setting__value")[0],r=function(t){t?e.classList.add("wrap--word-wrap"):e.classList.remove("wrap--word-wrap"),s.textContent=t?"[yes]":"[no]"};s.addEventListener("click",(function(e){return e.preventDefault(),settings.cycleValue("wordWrap"),!1})),r(settings.getValue("wordWrap")),settings.addCallback("wordWrap",r);var o=document.getElementsByClassName("setting--clickable-plain-links")[0].getElementsByClassName("setting__value")[0],c=function(e){e?generateMarkupForPlainLinks():removeMarkupForPlainLinks(),o.textContent=e?"[yes]":"[no]"};o.addEventListener("click",(function(e){return e.preventDefault(),settings.cycleValue("clickablePlainLinks"),!1})),c(settings.getValue("clickablePlainLinks")),settings.addCallback("clickablePlainLinks",c)}(),function(){for(var e=document.getElementsByClassName("modal"),t=e.length,n=function(){var n=e[t],a=n.getElementsByClassName("modal__content")[0],l=n.getElementsByClassName("modal__close-btn")[0];document.addEventListener("click",(function(e){n.classList.contains("modal--visible")&&(e.target===a||a.contains(e.target)||(n.classList.remove("modal--visible"),e.preventDefault(),e.stopPropagation()))}),!0),document.addEventListener("keydown",(function(e){n.classList.contains("modal--visible")&&27===e.keyCode&&n.classList.remove("modal--visible")})),l.addEventListener("click",(function(e){return e.preventDefault(),n.classList.remove("modal--visible"),!1}))};t--;)n();var a=document.getElementsByClassName("settings-btn")[0],l=document.getElementsByClassName("modal--settings")[0];a.addEventListener("click",(function(e){return e.preventDefault(),l.classList.add("modal--visible"),!1}))}(); \ No newline at end of file | ||
diff --git a/assets/style.css b/assets/style.css index 70f53a7..e662925 100644 --- a/assets/style.css +++ b/assets/style.css | |||
@@ -1 +1 @@ | |||
body{margin:0;padding:0;background-color:#14171a;color:#cad1d8;font-family:"Iosevka Term SS03","IBM Plex Mono","Fira Code","Fira Mono","Roboto Mono","Droid Sans Mono",Monaco,Consolas,Courier,monospace;font-size:1.0625em;line-height:1.5}h1,h2,h3,h4,h5,h6{font:inherit;color:#fff;margin:0}button{background:none;border:0;padding:0;color:#fff;font:inherit;text-decoration:underline;cursor:pointer}button:focus{outline:1px dotted currentColor}img{display:inline-block;vertical-align:top;max-width:8em;margin:.1em 0}img::selection{background-color:rgba(239,198,138,0.35)}img.expanded{max-width:40em;max-width:80ch}img.faded{opacity:.5}strong{font-weight:normal}::selection{color:#000;background-color:rgba(239,198,138,0.996)}:link{color:#fff}:visited{color:#cad1d8}:link:hover,:visited:hover{color:#fff}.header-base{display:flex;flex-direction:row;align-items:center;justify-content:space-between;border-bottom:1px solid #353a3f}.header{padding:.9em 1em;color:#929ba3}.location{flex:0 1 auto;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;text-overflow:ellipsis '|';margin-right:.5em}.location__prefix{margin-right:.3em;color:#929ba3;cursor:pointer}@media (hover: hover){.location__prefix:hover{color:#fff}}.location__prefix--mobile{display:none}.location__slash{margin:0 .3em}.location__uripart{color:#fff}.location__uripart+.location__slash+.location__uripart{color:#cad1d8}.location__uripart:link:hover,.location__uripart:visited:hover{color:#fff}.actions{flex:0 0 auto}.actions :visited{color:#fff}.action{display:inline}.action+.action::before{content:' | '}.wrap{padding:2em 1em;text-align:center}.wrap--word-wrap{max-width:50em;max-width:100ch;margin:0 auto}.wrap--word-wrap .content{white-space:pre-wrap;word-wrap:break-word}.content{box-sizing:border-box;display:inline-block;min-width:0;max-width:100%;margin:0;padding:0;text-align:left;font:inherit;font-family:"Iosevka Aile","Fira Sans","Roboto","Droid Sans",sans-serif}.content--has-type-annotations{padding-left:3em;padding-left:5ch}.content--has-monospace-font{font-family:"Iosevka Term SS03","IBM Plex Mono","Fira Code","Fira Mono","Roboto Mono","Droid Sans Mono",Monaco,Consolas,Courier,monospace}.type-annotation{margin-left:-3em;margin-left:-5ch;color:#929ba3;white-space:pre;font-family:"Iosevka Term SS03","IBM Plex Mono","Fira Code","Fira Mono","Roboto Mono","Droid Sans Mono",Monaco,Consolas,Courier,monospace}.modal{position:fixed;top:0;left:0;z-index:100;display:none;width:100%;height:100%;box-sizing:border-box;padding:2em;background-color:rgba(0,0,0,0.75)}.modal--visible{display:block}.modal__content{max-width:30em;padding:1.5em 1.8em;margin:0 auto;background-color:#14171a;box-shadow:0 .3em 2em #000;text-align:left}.modal__head{padding-bottom:.75em;margin-bottom:1.5em}.modal__title{padding-right:1em;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;text-transform:uppercase}.setting{display:flex;flex-direction:row;align-items:baseline;justify-content:space-between}.setting::after{order:2;flex:1 1 auto;display:block;height:0;margin:0 .5em;border-bottom:2px dotted #353a3f;content:''}.setting__label{order:1}.setting__value{order:3}@media screen and (max-width: 800px){body{font-size:1em}.modal{padding:1em}.modal__content{padding:1em 1.3em}}@media screen and (max-width: 500px){.location__prefix{display:none}.location__prefix--mobile{display:inline}.action{display:block}.action+.action::before{content:''}}@media screen and (max-width: 280px){.location__prefix--mobile{display:none}} | body{margin:0;padding:0;background-color:#14171a;color:#cad1d8;font-family:"Iosevka Term SS03","IBM Plex Mono","Fira Code","Fira Mono","Roboto Mono","Droid Sans Mono",Monaco,Consolas,Courier,monospace;font-size:1em;line-height:1.5}pre{margin:0;font:inherit;font-family:"Iosevka Term SS03","IBM Plex Mono","Fira Code","Fira Mono","Roboto Mono","Droid Sans Mono",Monaco,Consolas,Courier,monospace;white-space:pre-wrap}h1,h2,h3,h4,h5,h6{font:inherit;font-weight:bold;color:#fff;margin:0}p{margin:0}ul,ol{margin:0;padding:0;list-style-type:none}ul li{padding-left:4ch}ul li::before{display:inline-block;width:2ch;padding-left:2ch;margin-left:-4ch;content:'–'}button{background:none;border:0;padding:0;color:#fff;font:inherit;text-decoration:underline;cursor:pointer}button:focus{outline:1px dotted currentColor}img{display:inline-block;vertical-align:top;max-width:8em;margin:.1em 0}img::selection{background-color:rgba(239,198,138,0.35)}img.expanded{max-width:40em;max-width:80ch}img.faded{opacity:.5}strong{font-weight:normal}::selection{color:#000;background-color:rgba(239,198,138,0.996)}:link{color:#fff}:visited{color:#cad1d8}:link:hover,:visited:hover{color:#fff}.header-base{display:flex;flex-direction:row;align-items:center;justify-content:space-between;border-bottom:1px solid #353a3f}.header{padding:.9em 1em;color:#929ba3}.location{flex:0 1 auto;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;text-overflow:ellipsis '|';margin-right:.5em}.location__prefix{margin-right:.3em;color:#929ba3;cursor:pointer}@media (hover: hover){.location__prefix:hover{color:#fff}}.location__prefix--mobile{display:none}.location__slash{margin:0 .3em}.location__uripart{color:#fff}.location__uripart+.location__slash+.location__uripart{color:#cad1d8}.location__uripart:link:hover,.location__uripart:visited:hover{color:#fff}.actions{flex:0 0 auto}.actions :visited{color:#fff}.action{display:inline}.action+.action::before{content:' | '}.wrap{padding:2em 1em;text-align:center}.wrap--word-wrap{max-width:45em;max-width:90ch;margin:0 auto}.content{box-sizing:border-box;display:inline-block;min-width:0;max-width:100%;margin:0;padding:0;text-align:left;font:inherit;font-family:"Open Sans","Fira Sans","Roboto","Droid Sans",sans-serif}.content--monospace,.content--prefer-monospace{font-family:"Iosevka Term SS03","IBM Plex Mono","Fira Code","Fira Mono","Roboto Mono","Droid Sans Mono",Monaco,Consolas,Courier,monospace}.section{display:flex;align-items:baseline;flex-direction:row;justify-content:flex-start;min-height:1.5em;padding-left:3em;padding-left:5ch}.section__type{flex:0 0 auto;margin-left:-3em;margin-left:-5ch;padding-right:1.25em;padding-right:2ch;width:1.75em;width:3ch;color:#929ba3;font-family:"Iosevka Term SS03","IBM Plex Mono","Fira Code","Fira Mono","Roboto Mono","Droid Sans Mono",Monaco,Consolas,Courier,monospace;text-align:right;user-select:none}.section__content{min-width:0;word-wrap:break-word}.modal{position:fixed;top:0;left:0;z-index:100;display:none;width:100%;height:100%;box-sizing:border-box;padding:2em;background-color:rgba(0,0,0,0.75)}.modal--visible{display:block}.modal__content{max-width:30em;padding:1.5em 1.8em;margin:0 auto;background-color:#14171a;box-shadow:0 .3em 2em #000;text-align:left}.modal__head{padding-bottom:.75em;margin-bottom:1.5em}.modal__title{padding-right:1em;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;text-transform:uppercase}.setting{display:flex;flex-direction:row;align-items:baseline;justify-content:space-between}.setting::after{order:2;flex:1 1 auto;display:block;height:0;margin:0 .5em;border-bottom:2px dotted #353a3f;content:''}.setting__label{order:1}.setting__value{order:3}@media screen and (max-width: 800px){body{font-size:1em}.modal{padding:1em}.modal__content{padding:1em 1.3em}}@media screen and (max-width: 500px){.location__prefix{display:none}.location__prefix--mobile{display:inline}.action{display:block}.action+.action::before{content:''}}@media screen and (max-width: 280px){.location__prefix--mobile{display:none}} | ||
diff --git a/css/main.scss b/css/main.scss index 42b0b28..27a62ec 100644 --- a/css/main.scss +++ b/css/main.scss | |||
@@ -11,7 +11,7 @@ $sel-background: rgba($accent, .996); | |||
11 | $sel-text: #000; | 11 | $sel-text: #000; |
12 | 12 | ||
13 | $font-monospace: 'Iosevka Term SS03', 'IBM Plex Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', 'Droid Sans Mono', Monaco, Consolas, Courier, monospace; | 13 | $font-monospace: 'Iosevka Term SS03', 'IBM Plex Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', 'Droid Sans Mono', Monaco, Consolas, Courier, monospace; |
14 | $font-proportional: 'Iosevka Aile', 'Fira Sans', 'Roboto', 'Droid Sans', sans-serif; | 14 | $font-proportional: 'Open Sans', 'Fira Sans', 'Roboto', 'Droid Sans', sans-serif; |
15 | 15 | ||
16 | // | 16 | // |
17 | // Basic element styles | 17 | // Basic element styles |
@@ -28,16 +28,48 @@ body { | |||
28 | // ); | 28 | // ); |
29 | color: $text; | 29 | color: $text; |
30 | font-family: $font-monospace; | 30 | font-family: $font-monospace; |
31 | font-size: 1 / 16 * 17em; | 31 | font-size: 1em; //1 / 16 * 17em; |
32 | line-height: 1.5; | 32 | line-height: 1.5; |
33 | } | 33 | } |
34 | 34 | ||
35 | pre { | ||
36 | margin: 0; | ||
37 | font: inherit; | ||
38 | font-family: $font-monospace; | ||
39 | white-space: pre-wrap; | ||
40 | } | ||
41 | |||
35 | h1, h2, h3, h4, h5, h6 { | 42 | h1, h2, h3, h4, h5, h6 { |
36 | font: inherit; | 43 | font: inherit; |
44 | font-weight: bold; | ||
37 | color: $text-plus; | 45 | color: $text-plus; |
38 | margin: 0; | 46 | margin: 0; |
39 | } | 47 | } |
40 | 48 | ||
49 | p { | ||
50 | margin: 0; | ||
51 | } | ||
52 | |||
53 | ul, ol { | ||
54 | margin: 0; | ||
55 | padding: 0; | ||
56 | list-style-type: none; | ||
57 | } | ||
58 | |||
59 | ul { | ||
60 | li { | ||
61 | padding-left: 4ch; | ||
62 | |||
63 | &::before { | ||
64 | display: inline-block; | ||
65 | width: 2ch; | ||
66 | padding-left: 2ch; | ||
67 | margin-left: -4ch; | ||
68 | content: '–'; | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | |||
41 | button { | 73 | button { |
42 | background: none; | 74 | background: none; |
43 | border: 0; | 75 | border: 0; |
@@ -191,14 +223,9 @@ strong { | |||
191 | text-align: center; | 223 | text-align: center; |
192 | 224 | ||
193 | &--word-wrap { | 225 | &--word-wrap { |
194 | max-width: 50em; | 226 | max-width: 45em; |
195 | max-width: 100ch; | 227 | max-width: 90ch; |
196 | margin: 0 auto; | 228 | margin: 0 auto; |
197 | |||
198 | .content { | ||
199 | white-space: pre-wrap; | ||
200 | word-wrap: break-word; | ||
201 | } | ||
202 | } | 229 | } |
203 | } | 230 | } |
204 | 231 | ||
@@ -213,22 +240,39 @@ strong { | |||
213 | font: inherit; | 240 | font: inherit; |
214 | font-family: $font-proportional; | 241 | font-family: $font-proportional; |
215 | 242 | ||
216 | &--has-type-annotations { | 243 | &--monospace, |
217 | padding-left: 3em; | 244 | &--prefer-monospace { |
218 | padding-left: 5ch; | ||
219 | } | ||
220 | |||
221 | &--has-monospace-font { | ||
222 | font-family: $font-monospace; | 245 | font-family: $font-monospace; |
223 | } | 246 | } |
224 | } | 247 | } |
225 | 248 | ||
226 | .type-annotation { | 249 | .section { |
227 | margin-left: -3em; | 250 | display: flex; |
228 | margin-left: -5ch; | 251 | align-items: baseline; |
229 | color: $text-minus; | 252 | flex-direction: row; |
230 | white-space: pre; | 253 | justify-content: flex-start; |
231 | font-family: $font-monospace; | 254 | min-height: 1em * 1.5; |
255 | padding-left: 3em; | ||
256 | padding-left: 5ch; | ||
257 | |||
258 | &__type { | ||
259 | flex: 0 0 auto; | ||
260 | margin-left: -3em; | ||
261 | margin-left: -5ch; | ||
262 | padding-right: 1.25em; | ||
263 | padding-right: 2ch; | ||
264 | width: 1.75em; | ||
265 | width: 3ch; | ||
266 | color: $text-minus; | ||
267 | font-family: $font-monospace; | ||
268 | text-align: right; | ||
269 | user-select: none; | ||
270 | } | ||
271 | |||
272 | &__content { | ||
273 | min-width: 0; | ||
274 | word-wrap: break-word; | ||
275 | } | ||
232 | } | 276 | } |
233 | 277 | ||
234 | // | 278 | // |
diff --git a/fonts/iosevka-aile-regular.ttf b/fonts/iosevka-aile-regular.ttf deleted file mode 100644 index 7cad586..0000000 --- a/fonts/iosevka-aile-regular.ttf +++ /dev/null | |||
Binary files differ | |||
diff --git a/fonts/iosevka-term-ss03-bold.ttf b/fonts/iosevka-term-ss03-bold.ttf new file mode 100644 index 0000000..940a28f --- /dev/null +++ b/fonts/iosevka-term-ss03-bold.ttf | |||
Binary files differ | |||
diff --git a/fonts/iosevka-term-ss03-regular.ttf b/fonts/iosevka-term-ss03-regular.ttf index 0cde79e..052416d 100644 --- a/fonts/iosevka-term-ss03-regular.ttf +++ b/fonts/iosevka-term-ss03-regular.ttf | |||
Binary files differ | |||
diff --git a/internal/port/gemini.go b/internal/port/gemini.go index 0d8292c..740fccd 100644 --- a/internal/port/gemini.go +++ b/internal/port/gemini.go | |||
@@ -25,6 +25,7 @@ type GeminiTemplateVariables struct { | |||
25 | Assets AssetList | 25 | Assets AssetList |
26 | Sections []GeminiSection | 26 | Sections []GeminiSection |
27 | Nav []GeminiNavItem | 27 | Nav []GeminiNavItem |
28 | IsPlain bool | ||
28 | } | 29 | } |
29 | 30 | ||
30 | type GeminiNavItem struct { | 31 | type GeminiNavItem struct { |
@@ -33,7 +34,7 @@ type GeminiNavItem struct { | |||
33 | } | 34 | } |
34 | 35 | ||
35 | type GeminiSection struct { | 36 | type GeminiSection struct { |
36 | Type libgemini.GeminiDocSectionType | 37 | Type string |
37 | Text string | 38 | Text string |
38 | URL template.URL | 39 | URL template.URL |
39 | Items []string | 40 | Items []string |
@@ -83,19 +84,19 @@ func parseGeminiDocument(body *bytes.Buffer, uri string, hostport string) (secti | |||
83 | for _, section := range unpreppedSections { | 84 | for _, section := range unpreppedSections { |
84 | if section.Type != libgemini.LINK { | 85 | if section.Type != libgemini.LINK { |
85 | sections = append(sections, GeminiSection{ | 86 | sections = append(sections, GeminiSection{ |
86 | Type: section.Type, | 87 | Type: section.Type.String(), |
87 | Text: section.Text, | 88 | Text: section.Text, |
88 | URL: template.URL(section.URL), | 89 | URL: template.URL(section.URL), |
89 | Items: section.Items, | 90 | Items: section.Items, |
90 | }) | 91 | }) |
92 | } else { | ||
93 | sections = append(sections, GeminiSection{ | ||
94 | Type: section.Type.String(), | ||
95 | Text: section.Text, | ||
96 | URL: template.URL(resolveURL(section.URL, baseURL)), | ||
97 | Items: section.Items, | ||
98 | }) | ||
91 | } | 99 | } |
92 | |||
93 | sections = append(sections, GeminiSection{ | ||
94 | Type: section.Type, | ||
95 | Text: section.Text, | ||
96 | URL: template.URL(resolveURL(section.URL, baseURL)), | ||
97 | Items: section.Items, | ||
98 | }) | ||
99 | } | 100 | } |
100 | 101 | ||
101 | return | 102 | return |
@@ -133,9 +134,10 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
133 | URL: hostport, | 134 | URL: hostport, |
134 | Assets: assetList, | 135 | Assets: assetList, |
135 | Sections: []GeminiSection{{ | 136 | Sections: []GeminiSection{{ |
136 | Type: libgemini.RAW_TEXT, | 137 | Type: libgemini.RAW_TEXT.String(), |
137 | Text: fmt.Sprintf("Error: %s", err), | 138 | Text: fmt.Sprintf("Error: %s", err), |
138 | }}, | 139 | }}, |
140 | IsPlain: true, | ||
139 | }); e != nil { | 141 | }); e != nil { |
140 | log.Println("Template error: " + e.Error()) | 142 | log.Println("Template error: " + e.Error()) |
141 | log.Println(err.Error()) | 143 | log.Println(err.Error()) |
@@ -162,9 +164,10 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
162 | URL: fmt.Sprintf("%s/%s", hostport, uri), | 164 | URL: fmt.Sprintf("%s/%s", hostport, uri), |
163 | Assets: assetList, | 165 | Assets: assetList, |
164 | Sections: []GeminiSection{{ | 166 | Sections: []GeminiSection{{ |
165 | Type: libgemini.RAW_TEXT, | 167 | Type: libgemini.RAW_TEXT.String(), |
166 | Text: fmt.Sprintf("Error: %s", err), | 168 | Text: fmt.Sprintf("Error: %s", err), |
167 | }}, | 169 | }}, |
170 | IsPlain: true, | ||
168 | }); e != nil { | 171 | }); e != nil { |
169 | log.Println("Template error: " + e.Error()) | 172 | log.Println("Template error: " + e.Error()) |
170 | log.Println(err.Error()) | 173 | log.Println(err.Error()) |
@@ -184,9 +187,10 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
184 | URL: fmt.Sprintf("%s/%s", hostport, uri), | 187 | URL: fmt.Sprintf("%s/%s", hostport, uri), |
185 | Assets: assetList, | 188 | Assets: assetList, |
186 | Sections: []GeminiSection{{ | 189 | Sections: []GeminiSection{{ |
187 | Type: libgemini.RAW_TEXT, | 190 | Type: libgemini.RAW_TEXT.String(), |
188 | Text: fmt.Sprintf("Error: %s", err), | 191 | Text: fmt.Sprintf("Error: %s", err), |
189 | }}, | 192 | }}, |
193 | IsPlain: true, | ||
190 | }); e != nil { | 194 | }); e != nil { |
191 | log.Println("Template error: " + e.Error()) | 195 | log.Println("Template error: " + e.Error()) |
192 | log.Println(err.Error()) | 196 | log.Println(err.Error()) |
@@ -204,16 +208,17 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
204 | URL: fmt.Sprintf("%s/%s", hostport, uri), | 208 | URL: fmt.Sprintf("%s/%s", hostport, uri), |
205 | Assets: assetList, | 209 | Assets: assetList, |
206 | Sections: []GeminiSection{{ | 210 | Sections: []GeminiSection{{ |
207 | Type: libgemini.RAW_TEXT, | 211 | Type: libgemini.RAW_TEXT.String(), |
208 | Text: fmt.Sprintf("Error %d: %s", res.Header.Status, res.Header.Meta), | 212 | Text: fmt.Sprintf("Error %d: %s", res.Header.Status, res.Header.Meta), |
209 | }}, | 213 | }}, |
214 | IsPlain: true, | ||
210 | }); err != nil { | 215 | }); err != nil { |
211 | log.Println("Template error: " + err.Error()) | 216 | log.Println("Template error: " + err.Error()) |
212 | } | 217 | } |
213 | return | 218 | return |
214 | } | 219 | } |
215 | 220 | ||
216 | if strings.HasPrefix(res.Header.Meta, "text/") { | 221 | if strings.HasPrefix(res.Header.Meta, "text/") && !strings.HasPrefix(res.Header.Meta, "text/html") && !strings.HasPrefix(res.Header.Meta, "text/css") { |
217 | buf := new(bytes.Buffer) | 222 | buf := new(bytes.Buffer) |
218 | 223 | ||
219 | _, params, err := mime.ParseMediaType(res.Header.Meta) | 224 | _, params, err := mime.ParseMediaType(res.Header.Meta) |
@@ -230,12 +235,14 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
230 | } | 235 | } |
231 | 236 | ||
232 | var sections []GeminiSection | 237 | var sections []GeminiSection |
238 | isPlain := true | ||
233 | 239 | ||
234 | if strings.HasPrefix(res.Header.Meta, libgemini.MIME_GEMINI) { | 240 | if strings.HasPrefix(res.Header.Meta, libgemini.MIME_GEMINI) { |
235 | sections = parseGeminiDocument(buf, uri, hostport) | 241 | sections = parseGeminiDocument(buf, uri, hostport) |
242 | isPlain = false | ||
236 | } else { | 243 | } else { |
237 | sections = append(sections, GeminiSection{ | 244 | sections = append(sections, GeminiSection{ |
238 | Type: libgemini.RAW_TEXT, | 245 | Type: libgemini.RAW_TEXT.String(), |
239 | Text: buf.String(), | 246 | Text: buf.String(), |
240 | }) | 247 | }) |
241 | } | 248 | } |
@@ -245,6 +252,7 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
245 | URL: fmt.Sprintf("%s/%s", hostport, uri), | 252 | URL: fmt.Sprintf("%s/%s", hostport, uri), |
246 | Assets: assetList, | 253 | Assets: assetList, |
247 | Sections: sections, | 254 | Sections: sections, |
255 | IsPlain: isPlain, | ||
248 | }); err != nil { | 256 | }); err != nil { |
249 | log.Println("Template error: " + err.Error()) | 257 | log.Println("Template error: " + err.Error()) |
250 | } | 258 | } |
diff --git a/internal/port/main.go b/internal/port/main.go index 267df44..763057b 100644 --- a/internal/port/main.go +++ b/internal/port/main.go | |||
@@ -18,12 +18,12 @@ import ( | |||
18 | ) | 18 | ) |
19 | 19 | ||
20 | type AssetList struct { | 20 | type AssetList struct { |
21 | Style string | 21 | Style string |
22 | JS string | 22 | JS string |
23 | FontW string | 23 | FontRegularW string |
24 | FontW2 string | 24 | FontRegularW2 string |
25 | PropFontW string | 25 | FontBoldW string |
26 | PropFontW2 string | 26 | FontBoldW2 string |
27 | } | 27 | } |
28 | 28 | ||
29 | type startTemplateVariables struct { | 29 | type startTemplateVariables struct { |
@@ -136,29 +136,29 @@ func ListenAndServe(bind, startpagefile string, robotsfile string, robotsdebug b | |||
136 | // | 136 | // |
137 | // Fonts | 137 | // Fonts |
138 | 138 | ||
139 | fontdataw, err := box.Find("iosevka-term-ss03-regular.woff") | 139 | fontRegularWData, err := box.Find("iosevka-fixed-ss03-regular.woff") |
140 | if err != nil { | 140 | if err != nil { |
141 | fontdataw = []byte{} | 141 | fontRegularWData = []byte{} |
142 | } | 142 | } |
143 | fontwAsset := fmt.Sprintf("/iosevka-term-ss03-regular-%x.woff", md5.Sum(fontdataw)) | 143 | fontRegularWAsset := fmt.Sprintf("/iosevka-fixed-ss03-regular-%x.woff", md5.Sum(fontRegularWData)) |
144 | 144 | ||
145 | fontdataw2, err := box.Find("iosevka-term-ss03-regular.woff2") | 145 | fontRegularW2Data, err := box.Find("iosevka-fixed-ss03-regular.woff2") |
146 | if err != nil { | 146 | if err != nil { |
147 | fontdataw2 = []byte{} | 147 | fontRegularW2Data = []byte{} |
148 | } | 148 | } |
149 | fontw2Asset := fmt.Sprintf("/iosevka-term-ss03-regular-%x.woff2", md5.Sum(fontdataw2)) | 149 | fontRegularW2Asset := fmt.Sprintf("/iosevka-fixed-ss03-regular-%x.woff2", md5.Sum(fontRegularW2Data)) |
150 | 150 | ||
151 | propfontdataw, err := box.Find("iosevka-aile-regular.woff") | 151 | fontBoldWData, err := box.Find("iosevka-fixed-ss03-bold.woff") |
152 | if err != nil { | 152 | if err != nil { |
153 | propfontdataw = []byte{} | 153 | fontBoldWData = []byte{} |
154 | } | 154 | } |
155 | propfontwAsset := fmt.Sprintf("/iosevka-aile-regular-%x.woff", md5.Sum(propfontdataw)) | 155 | fontBoldWAsset := fmt.Sprintf("/iosevka-fixed-ss03-bold-%x.woff", md5.Sum(fontBoldWData)) |
156 | 156 | ||
157 | propfontdataw2, err := box.Find("iosevka-aile-regular.woff2") | 157 | fontBoldW2Data, err := box.Find("iosevka-fixed-ss03-bold.woff2") |
158 | if err != nil { | 158 | if err != nil { |
159 | propfontdataw2 = []byte{} | 159 | fontBoldW2Data = []byte{} |
160 | } | 160 | } |
161 | propfontw2Asset := fmt.Sprintf("/iosevka-aile-regular-%x.woff2", md5.Sum(propfontdataw2)) | 161 | fontBoldW2Asset := fmt.Sprintf("/iosevka-fixed-ss03-bold-%x.woff2", md5.Sum(fontBoldW2Data)) |
162 | 162 | ||
163 | // | 163 | // |
164 | // Stylesheet | 164 | // Stylesheet |
@@ -277,12 +277,12 @@ func ListenAndServe(bind, startpagefile string, robotsfile string, robotsdebug b | |||
277 | }) | 277 | }) |
278 | 278 | ||
279 | assets := AssetList{ | 279 | assets := AssetList{ |
280 | Style: styleAsset, | 280 | Style: styleAsset, |
281 | JS: jsAsset, | 281 | JS: jsAsset, |
282 | FontW: fontwAsset, | 282 | FontRegularW: fontRegularWAsset, |
283 | FontW2: fontw2Asset, | 283 | FontRegularW2: fontRegularW2Asset, |
284 | PropFontW: propfontwAsset, | 284 | FontBoldW: fontBoldWAsset, |
285 | PropFontW2: propfontw2Asset, | 285 | FontBoldW2: fontBoldW2Asset, |
286 | } | 286 | } |
287 | 287 | ||
288 | http.Handle("/", gziphandler.GzipHandler(DefaultHandler(startpageTpl, startpagetext, assets))) | 288 | http.Handle("/", gziphandler.GzipHandler(DefaultHandler(startpageTpl, startpagetext, assets))) |
@@ -292,10 +292,10 @@ func ListenAndServe(bind, startpagefile string, robotsfile string, robotsdebug b | |||
292 | http.Handle("/favicon.ico", gziphandler.GzipHandler(FaviconHandler(favicondata))) | 292 | http.Handle("/favicon.ico", gziphandler.GzipHandler(FaviconHandler(favicondata))) |
293 | http.Handle(styleAsset, gziphandler.GzipHandler(StyleHandler(styledata))) | 293 | http.Handle(styleAsset, gziphandler.GzipHandler(StyleHandler(styledata))) |
294 | http.Handle(jsAsset, gziphandler.GzipHandler(JavaScriptHandler(jsdata))) | 294 | http.Handle(jsAsset, gziphandler.GzipHandler(JavaScriptHandler(jsdata))) |
295 | http.HandleFunc(fontwAsset, FontHandler(false, fontdataw)) | 295 | http.HandleFunc(fontRegularWAsset, FontHandler(false, fontRegularWData)) |
296 | http.HandleFunc(fontw2Asset, FontHandler(true, fontdataw2)) | 296 | http.HandleFunc(fontRegularW2Asset, FontHandler(true, fontRegularW2Data)) |
297 | http.HandleFunc(propfontwAsset, FontHandler(false, propfontdataw)) | 297 | http.HandleFunc(fontBoldWAsset, FontHandler(false, fontBoldWData)) |
298 | http.HandleFunc(propfontw2Asset, FontHandler(true, propfontdataw2)) | 298 | http.HandleFunc(fontBoldW2Asset, FontHandler(true, fontBoldW2Data)) |
299 | //http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("assets/")))) | 299 | //http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("assets/")))) |
300 | 300 | ||
301 | return http.ListenAndServe(bind, nil) | 301 | return http.ListenAndServe(bind, nil) |
diff --git a/internal/port/tpl/_fonts.html b/internal/port/tpl/_fonts.html index b56aa22..a947222 100644 --- a/internal/port/tpl/_fonts.html +++ b/internal/port/tpl/_fonts.html | |||
@@ -3,14 +3,14 @@ | |||
3 | font-family: 'Iosevka Term SS03'; | 3 | font-family: 'Iosevka Term SS03'; |
4 | font-style: normal; | 4 | font-style: normal; |
5 | font-weight: normal; | 5 | font-weight: normal; |
6 | src: url('{{ .Assets.FontW2 }}') format('woff2'), | 6 | src: url('{{ .Assets.FontRegularW2 }}') format('woff2'), |
7 | url('{{ .Assets.FontW }}') format('woff'); | 7 | url('{{ .Assets.FontRegularW }}') format('woff'); |
8 | } | 8 | } |
9 | @font-face { | 9 | @font-face { |
10 | font-family: 'Iosevka Aile'; | 10 | font-family: 'Iosevka Term SS03'; |
11 | font-style: normal; | 11 | font-style: normal; |
12 | font-weight: normal; | 12 | font-weight: bold; |
13 | src: url('{{ .Assets.PropFontW2 }}') format('woff2'), | 13 | src: url('{{ .Assets.FontBoldW2 }}') format('woff2'), |
14 | url('{{ .Assets.PropFontW }}') format('woff'); | 14 | url('{{ .Assets.FontBoldW }}') format('woff'); |
15 | } | 15 | } |
16 | </style> | 16 | </style> |
diff --git a/internal/port/tpl/_header.html b/internal/port/tpl/_header.html deleted file mode 100644 index 5bcd254..0000000 --- a/internal/port/tpl/_header.html +++ /dev/null | |||
@@ -1,48 +0,0 @@ | |||
1 | <header class="header header-base"> | ||
2 | <div class="location"> | ||
3 | <a class="location__prefix">{{ .Protocol }}://</a><a class="location__prefix location__prefix--mobile">://</a> | ||
4 | |||
5 | {{- if .URL -}} | ||
6 | {{- $page := . -}} | ||
7 | {{- $href := printf "/%s" .Protocol -}} | ||
8 | {{- $uriParts := split .URL "/" -}} | ||
9 | |||
10 | {{- $uriLast := $uriParts | last -}} | ||
11 | {{- $uriParts = $uriParts | pop -}} | ||
12 | {{- if eq $uriLast "" -}} | ||
13 | {{- $uriLast = $uriParts | last -}} | ||
14 | {{- $uriParts = $uriParts | pop -}} | ||
15 | {{- end -}} | ||
16 | |||
17 | {{- range $i, $part := $uriParts -}} | ||
18 | {{- if and (eq $page.Protocol "gopher") (eq $i 1) -}} | ||
19 | {{- $href = printf "%s/1" $href -}} | ||
20 | {{- $part = $part | trimLeftChar -}} | ||
21 | {{- if not (eq $part "") -}} | ||
22 | {{- $href = printf "%s/%s" $href $part -}} | ||
23 | <span class="location__slash">/</span><a href="{{ $href }}/" class="location__uripart">{{ $part }}</a> | ||
24 | {{- end -}} | ||
25 | {{- else -}} | ||
26 | {{- $href = printf "%s/%s" $href . -}} | ||
27 | {{- if ne $i 0 -}} | ||
28 | <span class="location__slash">/</span> | ||
29 | {{- end -}} | ||
30 | <a href="{{ $href }}/" class="location__uripart">{{ . }}</a> | ||
31 | {{- end -}} | ||
32 | {{- end -}} | ||
33 | {{- if ne (len $uriParts) 0 -}} | ||
34 | <span class="location__slash">/</span> | ||
35 | {{- end -}} | ||
36 | {{- if and (eq $page.Protocol "gopher") (eq (len $uriParts) 1) -}} | ||
37 | {{- $uriLast = $uriLast | trimLeftChar -}} | ||
38 | {{- end -}} | ||
39 | <span class="location__uripart">{{ $uriLast }}</span> | ||
40 | {{- end -}} | ||
41 | </div> | ||
42 | <div class="actions"> | ||
43 | {{- if and (not .Lines) (not .Error) (eq .Protocol "gopher") -}} | ||
44 | <div class="action"><a href="/gopher/{{ .URL | replace "^([^/]*)/0" "$1/9" }}">View raw</a></div> | ||
45 | {{- end -}} | ||
46 | <div class="action"><button class="settings-btn">Settings</button></div> | ||
47 | </div> | ||
48 | </header> | ||
diff --git a/internal/port/tpl/_modals.html b/internal/port/tpl/_modals.html index 3c08d9a..3bbdef2 100644 --- a/internal/port/tpl/_modals.html +++ b/internal/port/tpl/_modals.html | |||
@@ -9,7 +9,7 @@ | |||
9 | <button class="setting__value">[N/A]</button> | 9 | <button class="setting__value">[N/A]</button> |
10 | </div> | 10 | </div> |
11 | <div class="setting setting--monospace-font"> | 11 | <div class="setting setting--monospace-font"> |
12 | <strong class="setting__label">Monospace font</strong> | 12 | <strong class="setting__label">Gemini: Monospace font</strong> |
13 | <button class="setting__value">[N/A]</button> | 13 | <button class="setting__value">[N/A]</button> |
14 | </div> | 14 | </div> |
15 | <div class="setting setting--image-previews"> | 15 | <div class="setting setting--image-previews"> |
diff --git a/internal/port/tpl/gemini.html b/internal/port/tpl/gemini.html index 08f1b8e..df50d50 100644 --- a/internal/port/tpl/gemini.html +++ b/internal/port/tpl/gemini.html | |||
@@ -7,28 +7,50 @@ | |||
7 | <link rel="stylesheet" href="{{ .Assets.Style }}" /> | 7 | <link rel="stylesheet" href="{{ .Assets.Style }}" /> |
8 | {{- template "_fonts.html" . -}} | 8 | {{- template "_fonts.html" . -}} |
9 | </head> | 9 | </head> |
10 | <body class="{{ if not .Lines }}is-plain{{ end }}"> | 10 | <body class="{{ if .IsPlain }}is-plain{{ end }}"> |
11 | {{- template "_header.html" . -}} | 11 | <header class="header header-base"> |
12 | <div class="location"> | ||
13 | <a class="location__prefix">gemini://</a><a class="location__prefix location__prefix--mobile">://</a> | ||
14 | {{- range $i, $item := .Nav -}} | ||
15 | {{- if ne $i 0 -}} | ||
16 | <span class="location__slash">/</span> | ||
17 | {{- end -}} | ||
18 | {{- if .Current -}} | ||
19 | <span class="location__uripart">{{ .Label }}</span> | ||
20 | {{- else -}} | ||
21 | <a href="{{ .URL }}/" class="location__uripart">{{ .Label }}</a> | ||
22 | {{- end -}} | ||
23 | {{- end -}} | ||
24 | </div> | ||
25 | <div class="actions"> | ||
26 | <div class="action"><button class="settings-btn">Settings</button></div> | ||
27 | </div> | ||
28 | </header> | ||
12 | 29 | ||
13 | <main class="wrap"> | 30 | <main class="wrap"> |
14 | <pre class="content content--has-monospace-font{{ if .Lines }} content--has-type-annotations{{ end }}"> | 31 | <div class="content{{ if not .IsPlain }} content--has-type-annotations{{ end }}"> |
15 | {{- if .Lines -}} | 32 | {{- range .Sections -}} |
16 | {{- $content := "" -}} | 33 | {{- if eq .Type "RAW_TEXT" -}} |
17 | {{- range .Lines -}} | 34 | <div class="section"><span class="section__type">```</span><pre class="section__content">{{- .Text -}}</pre></div> |
18 | {{- if ne $content "" -}} | 35 | {{- else if eq .Type "REFLOW_TEXT" -}} |
19 | {{- $content = printf "%s\n" $content -}} | 36 | <div class="section"><p class="section__content">{{- .Text -}}</p></div> |
20 | {{- end -}} | 37 | {{- else if eq .Type "LINK" -}} |
21 | {{- if .Link -}} | 38 | <div class="section"><span class="section__type">=></span><a class="section__content" href="{{ .URL }}">{{- .Text -}}</a></div> |
22 | {{- $content = printf "%s%s" $content (printf "<span class=\"type-annotation\">%s </span><a class=\"link link--%s\" href=\"%s\">%s</a>" .Type .Type .Link (.Text | HTMLEscape)) -}} | 39 | {{- else if eq .Type "HEADING_1" -}} |
23 | {{- else -}} | 40 | <div class="section"><span class="section__type">#</span><h1 class="section__content">{{- .Text -}}</h1></div> |
24 | {{- $content = printf "%s%s" $content (printf "<span class=\"type-annotation\"> </span>%s" (.Text | HTMLEscape)) -}} | 41 | {{- else if eq .Type "HEADING_2" -}} |
25 | {{- end -}} | 42 | <div class="section"><span class="section__type">##</span><h2 class="section__content">{{- .Text -}}</h2></div> |
26 | {{- end -}} | 43 | {{- else if eq .Type "HEADING_3" -}} |
27 | {{- $content | safeHtml -}} | 44 | <div class="section"><span class="section__type">###</span><h3 class="section__content">{{- .Text -}}</h3></div> |
28 | {{- else -}} | 45 | {{- else if eq .Type "LIST" -}} |
29 | {{- .RawText -}} | 46 | <div class="section"><ul class="section__content"> |
30 | {{- end -}} | 47 | {{- range .Items -}} |
31 | </pre> | 48 | <li>{{- . -}}</li> |
49 | {{- end -}} | ||
50 | </ul></div> | ||
51 | {{- end -}} | ||
52 | {{- end -}} | ||
53 | </div> | ||
32 | </main> | 54 | </main> |
33 | 55 | ||
34 | {{- template "_modals.html" . -}} | 56 | {{- template "_modals.html" . -}} |
diff --git a/internal/port/tpl/gopher.html b/internal/port/tpl/gopher.html index c971847..5436123 100644 --- a/internal/port/tpl/gopher.html +++ b/internal/port/tpl/gopher.html | |||
@@ -31,25 +31,16 @@ | |||
31 | </header> | 31 | </header> |
32 | 32 | ||
33 | <main class="wrap"> | 33 | <main class="wrap"> |
34 | <pre class="content content--has-monospace-font{{ if not .IsPlain }} content--has-type-annotations{{ end }}"> | 34 | <div class="content content--monospace{{ if not .IsPlain }} content--has-type-annotations{{ end }}"> |
35 | {{- $content := "" -}} | ||
36 | {{- $page := . -}} | 35 | {{- $page := . -}} |
37 | {{- range .Lines -}} | 36 | {{- range .Lines -}} |
38 | {{- if ne $content "" -}} | 37 | {{- if .Link -}} |
39 | {{- $content = printf "%s\n" $content -}} | 38 | <div class="section"><span class="section__type">{{- .Type -}}</span><a class="section__content" href="{{ .Link }}">{{- .Text -}}</a></div> |
40 | {{- end -}} | ||
41 | {{- if $page.IsPlain -}} | ||
42 | {{- $content = printf "%s%s" $content (.Text | HTMLEscape) -}} | ||
43 | {{- else -}} | 39 | {{- else -}} |
44 | {{- if .Link -}} | 40 | <div class="section"><span class="section__type"></span><pre class="section__content">{{- .Text -}}</pre></div> |
45 | {{- $content = printf "%s%s" $content (printf "<span class=\"type-annotation\">%s </span><a class=\"link link--%s\" href=\"%s\">%s</a>" .Type .Type .Link (.Text | HTMLEscape)) -}} | ||
46 | {{- else -}} | ||
47 | {{- $content = printf "%s%s" $content (printf "<span class=\"type-annotation\"> </span>%s" (.Text | HTMLEscape)) -}} | ||
48 | {{- end -}} | ||
49 | {{- end -}} | 41 | {{- end -}} |
50 | {{- end -}} | 42 | {{- end -}} |
51 | {{- $content | safeHtml -}} | 43 | </div> |
52 | </pre> | ||
53 | </main> | 44 | </main> |
54 | 45 | ||
55 | {{- template "_modals.html" . -}} | 46 | {{- template "_modals.html" . -}} |
@@ -128,9 +128,9 @@ const settings = new KeyValueStore({ | |||
128 | const settingMonospaceFontValueEl = settingMonospaceFontEl.getElementsByClassName('setting__value')[0]; | 128 | const settingMonospaceFontValueEl = settingMonospaceFontEl.getElementsByClassName('setting__value')[0]; |
129 | const settingMonospaceFontCallback = (value: boolean) => { | 129 | const settingMonospaceFontCallback = (value: boolean) => { |
130 | if (value) { | 130 | if (value) { |
131 | contentEl.classList.add("content--has-monospace-font"); | 131 | contentEl.classList.add("content--prefer-monospace"); |
132 | } else { | 132 | } else { |
133 | contentEl.classList.remove("content--has-monospace-font"); | 133 | contentEl.classList.remove("content--prefer-monospace"); |
134 | } | 134 | } |
135 | 135 | ||
136 | settingMonospaceFontValueEl.textContent = value ? '[yes]' : '[no]'; | 136 | settingMonospaceFontValueEl.textContent = value ? '[yes]' : '[no]'; |
diff --git a/pkg/libgemini/libgemini.go b/pkg/libgemini/libgemini.go index 48a8ed0..5e37490 100644 --- a/pkg/libgemini/libgemini.go +++ b/pkg/libgemini/libgemini.go | |||
@@ -53,10 +53,10 @@ var ( | |||
53 | HeaderPattern = regexp.MustCompile("^(\\d\\d)[ \\t]+(.*)$") | 53 | HeaderPattern = regexp.MustCompile("^(\\d\\d)[ \\t]+(.*)$") |
54 | LinkPattern = regexp.MustCompile("^=>[ \\t]*([^ \\t]+)(?:[ \\t]+(.*))?$") | 54 | LinkPattern = regexp.MustCompile("^=>[ \\t]*([^ \\t]+)(?:[ \\t]+(.*))?$") |
55 | ReflowModePattern = regexp.MustCompile("^```(.*)$") | 55 | ReflowModePattern = regexp.MustCompile("^```(.*)$") |
56 | Heading1Pattern = regexp.MustCompile("^#(.*)$") | 56 | Heading1Pattern = regexp.MustCompile("^# *(.*)$") |
57 | Heading2Pattern = regexp.MustCompile("^##(.*)$") | 57 | Heading2Pattern = regexp.MustCompile("^## *(.*)$") |
58 | Heading3Pattern = regexp.MustCompile("^###(.*)$") | 58 | Heading3Pattern = regexp.MustCompile("^### *(.*)$") |
59 | ListItemPattern = regexp.MustCompile("^\\*(.*)$") | 59 | ListItemPattern = regexp.MustCompile("^\\* *(.*)$") |
60 | TermEscapeSGRPattern = regexp.MustCompile("\\[\\d+(;\\d+)*m") | 60 | TermEscapeSGRPattern = regexp.MustCompile("\\[\\d+(;\\d+)*m") |
61 | ) | 61 | ) |
62 | 62 | ||
@@ -82,6 +82,27 @@ const ( | |||
82 | LIST = GeminiDocSectionType(6) | 82 | LIST = GeminiDocSectionType(6) |
83 | ) | 83 | ) |
84 | 84 | ||
85 | func (it GeminiDocSectionType) String() string { | ||
86 | switch it { | ||
87 | case RAW_TEXT: | ||
88 | return "RAW_TEXT" | ||
89 | case REFLOW_TEXT: | ||
90 | return "REFLOW_TEXT" | ||
91 | case LINK: | ||
92 | return "LINK" | ||
93 | case HEADING_1: | ||
94 | return "HEADING_1" | ||
95 | case HEADING_2: | ||
96 | return "HEADING_2" | ||
97 | case HEADING_3: | ||
98 | return "HEADING_3" | ||
99 | case LIST: | ||
100 | return "LIST" | ||
101 | default: | ||
102 | return "???" | ||
103 | } | ||
104 | } | ||
105 | |||
85 | type GeminiDocSection struct { | 106 | type GeminiDocSection struct { |
86 | Type GeminiDocSectionType | 107 | Type GeminiDocSectionType |
87 | Text string | 108 | Text string |
@@ -191,20 +212,13 @@ func ParseGeminiDocument(body *bytes.Buffer) (sections []GeminiDocSection) { | |||
191 | 212 | ||
192 | if !reflow { | 213 | if !reflow { |
193 | if !ignoreSection { | 214 | if !ignoreSection { |
194 | if section.Type != RAW_TEXT { | 215 | sections = append(sections, section) |
195 | sections = append(sections, section) | ||
196 | section = GeminiDocSection{ | ||
197 | Type: RAW_TEXT, | ||
198 | } | ||
199 | } | ||
200 | } else { | ||
201 | ignoreSection = false | ||
202 | section = GeminiDocSection{ | ||
203 | Type: RAW_TEXT, | ||
204 | } | ||
205 | } | 216 | } |
206 | 217 | ||
207 | section.Text = section.Text + "\n" + line | 218 | section = GeminiDocSection{ |
219 | Type: RAW_TEXT, | ||
220 | Text: line, | ||
221 | } | ||
208 | 222 | ||
209 | continue | 223 | continue |
210 | } | 224 | } |
@@ -297,20 +311,14 @@ func ParseGeminiDocument(body *bytes.Buffer) (sections []GeminiDocSection) { | |||
297 | } | 311 | } |
298 | 312 | ||
299 | if !ignoreSection { | 313 | if !ignoreSection { |
300 | if section.Type != REFLOW_TEXT { | 314 | sections = append(sections, section) |
301 | sections = append(sections, section) | ||
302 | section = GeminiDocSection{ | ||
303 | Type: REFLOW_TEXT, | ||
304 | } | ||
305 | } | ||
306 | } else { | ||
307 | ignoreSection = false | ||
308 | section = GeminiDocSection{ | ||
309 | Type: REFLOW_TEXT, | ||
310 | } | ||
311 | } | 315 | } |
312 | 316 | ||
313 | section.Text = section.Text + "\n" + line | 317 | ignoreSection = false |
318 | section = GeminiDocSection{ | ||
319 | Type: REFLOW_TEXT, | ||
320 | Text: line, | ||
321 | } | ||
314 | } | 322 | } |
315 | 323 | ||
316 | if !ignoreSection { | 324 | if !ignoreSection { |