initial backend setup w/braille_xr page
This commit is contained in:
commit
24109aa8d8
32 changed files with 685 additions and 0 deletions
6
.gitattributes
vendored
Normal file
6
.gitattributes
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
*.svg filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||
*.webp filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.webm filter=lfs diff=lfs merge=lfs -text
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
dofdev
|
||||
|
||||
*.json
|
1
data/data.go
Normal file
1
data/data.go
Normal file
|
@ -0,0 +1 @@
|
|||
package data
|
5
go.mod
Normal file
5
go.mod
Normal file
|
@ -0,0 +1,5 @@
|
|||
module github.com/spatialfree/dofdev
|
||||
|
||||
go 1.20
|
||||
|
||||
require github.com/gorilla/mux v1.8.1 // indirect
|
2
go.sum
Normal file
2
go.sum
Normal file
|
@ -0,0 +1,2 @@
|
|||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
22
readme.md
Normal file
22
readme.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
```shell
|
||||
# build and run
|
||||
go build && ./dofdev
|
||||
|
||||
#deploy
|
||||
rsync -avz --progress ~/go/src/dofdev/ root@dofdev:/root/dofdev/
|
||||
```
|
||||
server starting at: http://localhost:3000
|
||||
|
||||
|
||||
i actually want to do this in a different way
|
||||
|
||||
instead of a server that can be attacked
|
||||
or taken down/compromised by me
|
||||
|
||||
we instead have a distributed application that receives updates
|
||||
|
||||
that way he has his copy and i have mine
|
||||
|
||||
and they are on local secure devices
|
||||
|
||||
it can just be console application for now
|
61
server.go
Normal file
61
server.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/spatialfree/dofdev/src/router"
|
||||
"github.com/spatialfree/dofdev/tem"
|
||||
)
|
||||
|
||||
func init() {
|
||||
fmt.Println("init()")
|
||||
tem.Load()
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := router.New()
|
||||
|
||||
// Start the web server
|
||||
log.Println("server starting on port 3000...")
|
||||
addr := ":3210"
|
||||
// addr := "192.168.0.21:3000"
|
||||
if err := http.ListenAndServe(addr, r); err != nil {
|
||||
log.Fatalf("error starting server: %v", err)
|
||||
}
|
||||
|
||||
// fs := http.FileServer(http.Dir("./res"))
|
||||
// http.Handle("/res/", http.StripPrefix("/res/", fs))
|
||||
|
||||
// http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
// fmt.Println("request: ", r.URL.Path)
|
||||
// http.ServeFile(w, r, "./index.html")
|
||||
// })
|
||||
|
||||
// http.HandleFunc("/pass", func(w http.ResponseWriter, r *http.Request) {
|
||||
// fmt.Println("request: ", r.URL.Path)
|
||||
// // fmt.Println("request: ", r.Header)
|
||||
// fmt.Println("request: ", r.FormValue("pass"))
|
||||
// fmt.Println("request: ", hash(r.FormValue("pass")))
|
||||
// if hash(r.FormValue("pass")) == 3556498 {
|
||||
// http.ServeFile(w, r, "./biz.html")
|
||||
// } else {
|
||||
// http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
// }
|
||||
// })
|
||||
|
||||
// fmt.Println("server starting @ http://localhost:3210 ...")
|
||||
// if err := http.ListenAndServe(":3210", nil); err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
}
|
||||
|
||||
// hash function
|
||||
func hash(s string) uint32 {
|
||||
h := uint32(0)
|
||||
for i := 0; i < len(s); i++ {
|
||||
h = 31*h + uint32(s[i])
|
||||
}
|
||||
return h
|
||||
}
|
19
src/router/router.go
Normal file
19
src/router/router.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
braillexr "github.com/spatialfree/dofdev/web/pages/braille_xr"
|
||||
"github.com/spatialfree/dofdev/web/pages/mono"
|
||||
)
|
||||
|
||||
func New() *mux.Router {
|
||||
r := mux.NewRouter() // .StrictSlash(false)
|
||||
|
||||
r.PathPrefix(("/public/")).Handler(http.StripPrefix("/public/", http.FileServer(http.Dir("web/public/"))))
|
||||
r.HandleFunc("/", mono.Handler)
|
||||
r.HandleFunc("/braille_xr", braillexr.Handler)
|
||||
|
||||
return r
|
||||
}
|
49
tem/html/biz copy.html
Normal file
49
tem/html/biz copy.html
Normal file
|
@ -0,0 +1,49 @@
|
|||
<h2>shared accounts:</h2>
|
||||
<!-- there might be sections for personal accounts later ~ -->
|
||||
|
||||
<div id='accounts'></div>
|
||||
|
||||
<script>
|
||||
accounts = []
|
||||
|
||||
el = document.getElementById('accounts')
|
||||
for (i = 0; i < accounts.length; i++) {
|
||||
a = accounts[i]
|
||||
el.innerHTML += `<div class='account ${(a.dead ? 'dead' : '')}'>
|
||||
<div>
|
||||
<b>${a.name}</b>
|
||||
<a href='${a.link}'>→</a>
|
||||
</div>
|
||||
<div>${a.user}</div>
|
||||
<div class='pass'>
|
||||
<div onclick='
|
||||
this.innerHTML="✓ ";
|
||||
navigator.clipboard.writeText("${a.pass}")
|
||||
'>⧉ </div>
|
||||
<div onclick='
|
||||
this.innerHTML === "${a.pass}" ?
|
||||
window.getSelection().selectAllChildren(this) :
|
||||
this.innerHTML = "${a.pass}"
|
||||
'>${'*'.repeat(a.pass.length)}</div>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.account {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.dead {
|
||||
opacity: 0.666;
|
||||
}
|
||||
.pass {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
font-family: 'Courier Prime', monospace;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
99
tem/html/biz.html
Normal file
99
tem/html/biz.html
Normal file
|
@ -0,0 +1,99 @@
|
|||
<h2>shared accounts:</h2>
|
||||
<!-- there might be sections for personal accounts later ~ -->
|
||||
|
||||
<div id='accounts'></div>
|
||||
|
||||
<script>
|
||||
// const generateKey = async (password, salt) => {
|
||||
// const encoder = new TextEncoder()
|
||||
// const baseKey = await window.crypto.subtle.importKey('raw', encoder.encode(password), { name: 'PBKDF2' }, false, ['deriveKey'])
|
||||
// return window.crypto.subtle.deriveKey({
|
||||
// name: 'PBKDF2',
|
||||
// salt: salt,
|
||||
// iterations: 1000,
|
||||
// hash: 'SHA-256'
|
||||
// }, baseKey, { name: 'AES-CBC', length: 256 }, true, ['encrypt', 'decrypt'])
|
||||
// }
|
||||
|
||||
// const encrypt = async (data, password) => {
|
||||
// const salt = window.crypto.getRandomValues(new Uint8Array(16))
|
||||
// const encoder = new TextEncoder()
|
||||
// const key = await generateKey(password, salt)
|
||||
// const encryptedContent = await window.crypto.subtle.encrypt({
|
||||
// name: 'AES-CBC',
|
||||
// iv: window.crypto.getRandomValues(new Uint8Array(16))
|
||||
// }, key, encoder.encode(data))
|
||||
|
||||
// return {
|
||||
// encryptedContent,
|
||||
// salt
|
||||
// }
|
||||
// }
|
||||
|
||||
// const decrypt = async (data, password, salt, iv) => {
|
||||
// const decoder = new TextDecoder()
|
||||
// const key = await generateKey(password, salt)
|
||||
// const decryptedContent = await window.crypto.subtle.decrypt({
|
||||
// name: 'AES-CBC',
|
||||
// iv
|
||||
// }, key, data)
|
||||
|
||||
// return decoder.decode(decryptedContent)
|
||||
// }
|
||||
|
||||
el = document.getElementById('accounts')
|
||||
for (i = 0; i < accounts.length; i++) {
|
||||
a = accounts[i]
|
||||
el.innerHTML += `<div class='account ${(a.dead ? 'dead' : '')}'>
|
||||
<div class='name'>
|
||||
<a href='${a.link}'>${a.name}</a>
|
||||
</div>
|
||||
<div class='info'>
|
||||
<div onclick='
|
||||
window.getSelection().selectAllChildren(this)
|
||||
'>${a.user}</div>
|
||||
</div>
|
||||
<div class='info'>
|
||||
<div onclick='
|
||||
this.innerHTML === "${a.pass}" ?
|
||||
window.getSelection().selectAllChildren(this) :
|
||||
this.innerHTML = "${a.pass}"
|
||||
'>${'*'.repeat(a.pass.length)}</div>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<!--
|
||||
<div onclick='
|
||||
this.innerHTML="✓ ";
|
||||
navigator.clipboard.writeText("${a.pass}")
|
||||
'>⧉ </div>
|
||||
-->
|
||||
|
||||
<style>
|
||||
.account {
|
||||
margin-bottom: 32px;
|
||||
padding-left: 16px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
border-left: 2px solid #0000003a;
|
||||
|
||||
> .name {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
.info {
|
||||
cursor: pointer;
|
||||
/* display: flex;
|
||||
gap: 8px;
|
||||
font-family: 'Courier Prime', monospace; */
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
34
tem/html/braille_xr.html
Normal file
34
tem/html/braille_xr.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
{{ template "head.html" .}}
|
||||
|
||||
<main>
|
||||
|
||||
<h1>braille_xr</h1>
|
||||
|
||||
<h2>problem</h2>
|
||||
<ul>
|
||||
<li>How can we help make XR (VR/AR/MR) accessible to people with visual impairments?</li>
|
||||
<li>Current refreshable braille devices have ~40x6x2 actuators with carefully tuned long levers making them very expensive~$3,500</li>
|
||||
<li>Not a very portable device</li>
|
||||
<li>1 interaction method</li>
|
||||
<li>Other interactions of input for people with low vision require voice input which is not always comfortable.</li>
|
||||
</ul>
|
||||
<img src='/public/img/brailliant_bi_40x.webp' width='60%'>
|
||||
|
||||
<h2>solution</h2>
|
||||
<ul>
|
||||
<li>Reduce the number of actuators</li>
|
||||
<li>use spatial hand tracking for accessible and extensible interactions</li>
|
||||
<li>create a comfortable and portable way to input braille</li>
|
||||
</ul>
|
||||
|
||||
<h2>progress</h2>
|
||||
<ul>
|
||||
<li>easy to manufacture braille display</li>
|
||||
<li>small enough to leverage openxr hand tracking</li>
|
||||
<li>software demo braille keyboard w/custom layouts</li>
|
||||
</ul>
|
||||
<img src='/public/img/hack.webp' width='60%'>
|
||||
|
||||
<div style='height: 100px;'></div>
|
||||
|
||||
</main>
|
8
tem/html/head.html
Normal file
8
tem/html/head.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||
<script src='https://unpkg.com/htmx.org@1.9.5'></script>
|
||||
<link rel="stylesheet" href="/public/style.css?v19">
|
||||
</head>
|
||||
|
||||
<body>
|
99
tem/html/mono.html
Normal file
99
tem/html/mono.html
Normal file
|
@ -0,0 +1,99 @@
|
|||
{{ template "head.html" .}}
|
||||
|
||||
<main>
|
||||
|
||||
<p style='
|
||||
width: 100%;
|
||||
padding-right: 20px;
|
||||
font-size: 18px;
|
||||
line-height: 2.0;
|
||||
text-align: right;
|
||||
color: white;
|
||||
'>
|
||||
An effort to open higher degrees of<br>
|
||||
spatial interaction free<br>
|
||||
to the world.
|
||||
</p>
|
||||
|
||||
<img src='/public/img/dofdev_logo.svg?v22' width='100%'>
|
||||
<p>degrees of freedom development</p>
|
||||
|
||||
<div class='shuffle' style='
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
'>
|
||||
<div style='color: white; font-size: 20px;'>Ethan Merchant
|
||||
<a href='https://ethanmerchant.com' style='color: var(--light);'>@spatialfree</a>
|
||||
</div>
|
||||
<div style='color: white; font-size: 20px;'>Niko Ryan
|
||||
<a href='https://twitter.com/opendegree' style='color: var(--light);'>@opendegree</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='t_symbol' style='
|
||||
font-size: 32px;
|
||||
'>×</div>
|
||||
|
||||
<div id='x'>
|
||||
<a href='https://stereokit.net' target='_blank'>
|
||||
<div style='
|
||||
background-image: url("https://stereokit.net/img/StereoKitLogoLight.svg");
|
||||
'></div>
|
||||
</a>
|
||||
<a href='https://www.projectnorthstar.org/' target='_blank'>
|
||||
<div style='
|
||||
background-image: url("/public/img/icons/northstar.svg");
|
||||
'></div>
|
||||
</a>
|
||||
<a href='https://stardustxr.org' target='_blank'>
|
||||
<div style='
|
||||
background-image: url("https://stardustxr.org/img/icon.gif");
|
||||
'></div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class='t_symbol' style='
|
||||
margin-top: 4rem;
|
||||
'>∮</div>
|
||||
|
||||
<!-- <div style='height: 32px;'></div> -->
|
||||
<!-- <div id='fdeg'>+</div> -->
|
||||
|
||||
<h1>projects</h1>
|
||||
<p class='subtitle'>xr based interfaces</p>
|
||||
|
||||
<a href='/braille_xr'>braille_xr</a>
|
||||
|
||||
<div style='height: 32px;'></div>
|
||||
<img src='/public/img/logo.svg' width='64px'>
|
||||
<div style='height: 24px;'></div>
|
||||
|
||||
<input type='password' autofocus
|
||||
name='pass' placeholder='pass'
|
||||
hx-post='/pass'
|
||||
hx-vals='{"pass": this.value}'
|
||||
hx-swap='outerHTML'
|
||||
>
|
||||
<!-- <button hx-post='/clicked' hx-swap='outerHTML'>
|
||||
Click Me
|
||||
</button> -->
|
||||
|
||||
<footer>© 2018-2024 dofdev</footer>
|
||||
|
||||
|
||||
<style>
|
||||
#x {
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
justify-content: center;
|
||||
gap: 32px;
|
||||
> a > div {
|
||||
min-width: 64px;
|
||||
min-height: 64px;
|
||||
background-size: contain;
|
||||
}
|
||||
}
|
||||
</style>
|
67
tem/tem.go
Normal file
67
tem/tem.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package tem
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var (
|
||||
Templates *template.Template
|
||||
)
|
||||
|
||||
func Load() {
|
||||
Templates = template.Must(template.ParseGlob("tem/html/*.html"))
|
||||
fmt.Println("templates loaded")
|
||||
}
|
||||
|
||||
func Render(
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
template string,
|
||||
page string,
|
||||
title string,
|
||||
desc string,
|
||||
page_data interface{},
|
||||
) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
data := struct {
|
||||
Host string
|
||||
Page string
|
||||
Title string
|
||||
Desc string
|
||||
Local bool
|
||||
Root bool
|
||||
Data interface{}
|
||||
}{
|
||||
Host: r.Host,
|
||||
Page: page,
|
||||
Title: title,
|
||||
Desc: desc,
|
||||
Local: true, // (os.Getenv("LOCAL") == "true"), // toggle here for flag testing
|
||||
Root: true, // (os.Getenv("HOST_IDEABRELLA") == strings.ReplaceAll(r.Host, ":3000", "")),
|
||||
Data: page_data,
|
||||
}
|
||||
|
||||
err := Templates.ExecuteTemplate(w, template, data)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
// for small refreshable templates within pages
|
||||
// so no need for page things like title, desc, etc.
|
||||
func RenderSub(
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
template string,
|
||||
page_data interface{},
|
||||
) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
err := Templates.ExecuteTemplate(w, template, page_data)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
21
web/pages/braille_xr/braille_xr.go
Normal file
21
web/pages/braille_xr/braille_xr.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package braillexr
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/spatialfree/dofdev/tem"
|
||||
)
|
||||
|
||||
// func Handler(ctx *gin.Context) {
|
||||
func Handler(w http.ResponseWriter, r *http.Request) {
|
||||
data := struct {
|
||||
}{}
|
||||
tem.Render(
|
||||
w, r,
|
||||
"braille_xr.html",
|
||||
"braille_xr",
|
||||
"braille_xr",
|
||||
"refreshable braille display for rendering and input with handtracking",
|
||||
data,
|
||||
)
|
||||
}
|
50
web/pages/mono/mono.go
Normal file
50
web/pages/mono/mono.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package mono
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/spatialfree/dofdev/tem"
|
||||
)
|
||||
|
||||
// func Handler(ctx *gin.Context) {
|
||||
func Handler(w http.ResponseWriter, r *http.Request) {
|
||||
data := struct {
|
||||
Projects []Project
|
||||
}{
|
||||
Projects,
|
||||
}
|
||||
tem.Render(
|
||||
w, r,
|
||||
"mono.html",
|
||||
"",
|
||||
"dofdev",
|
||||
"an effort to open higher degrees of spatial interaction free to the world.",
|
||||
data,
|
||||
)
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
ID int
|
||||
Title string
|
||||
Description string
|
||||
Art string
|
||||
}
|
||||
|
||||
// This would ideally come from a database
|
||||
var Projects = []Project{
|
||||
{ID: 1,
|
||||
Title: "Prompt-Train",
|
||||
Description: "PromptTrain, the PromptWriting Excellence Engine, is here to help you enhance your writing skills and unleash your creativity. Receive valuable feedback on your prompts, compete in creative challenges, connect with a supportive community, and embark on your journey towards writing excellence.",
|
||||
Art: "/res/projects/prompt-train.png",
|
||||
},
|
||||
{ID: 2,
|
||||
Title: "Echo AI",
|
||||
Description: "With EchoAI, creating AI personas is a breeze. Define your character's name, appearance, traits, speech patterns, emotional range, and background story. Submit website and training reference documents, and specify preferred topics and custom responses. EchoAI offers a user-friendly, step-by-step process to bring your Echo AI persona to life.",
|
||||
Art: "/res/projects/echo-ai.png",
|
||||
},
|
||||
{ID: 3,
|
||||
Title: "PromptScript",
|
||||
Description: "PromptScript is a web based ai prompting language that allows you to orchestrate complex systematic patterns with large language models.",
|
||||
Art: "/res/projects/promptscript.png",
|
||||
},
|
||||
}
|
BIN
web/public/img/brailliant_bi_40x.webp
(Stored with Git LFS)
Normal file
BIN
web/public/img/brailliant_bi_40x.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
web/public/img/dofdev_logo.svg
(Stored with Git LFS)
Normal file
BIN
web/public/img/dofdev_logo.svg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
web/public/img/hack.jpg
(Stored with Git LFS)
Normal file
BIN
web/public/img/hack.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
web/public/img/hack.webp
(Stored with Git LFS)
Normal file
BIN
web/public/img/hack.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
web/public/img/icons/art.svg
(Stored with Git LFS)
Normal file
BIN
web/public/img/icons/art.svg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
web/public/img/icons/com.svg
(Stored with Git LFS)
Normal file
BIN
web/public/img/icons/com.svg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
web/public/img/icons/degree.svg
(Stored with Git LFS)
Normal file
BIN
web/public/img/icons/degree.svg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
web/public/img/icons/kofi.svg
(Stored with Git LFS)
Normal file
BIN
web/public/img/icons/kofi.svg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
web/public/img/icons/merch.svg
(Stored with Git LFS)
Normal file
BIN
web/public/img/icons/merch.svg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
web/public/img/icons/net.svg
(Stored with Git LFS)
Normal file
BIN
web/public/img/icons/net.svg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
web/public/img/icons/northstar.svg
(Stored with Git LFS)
Normal file
BIN
web/public/img/icons/northstar.svg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
web/public/img/icons/org.svg
(Stored with Git LFS)
Normal file
BIN
web/public/img/icons/org.svg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
web/public/img/icons/patreon.svg
(Stored with Git LFS)
Normal file
BIN
web/public/img/icons/patreon.svg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
web/public/img/icons/scroll.svg
(Stored with Git LFS)
Normal file
BIN
web/public/img/icons/scroll.svg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
web/public/img/logo.svg
(Stored with Git LFS)
Normal file
BIN
web/public/img/logo.svg
(Stored with Git LFS)
Normal file
Binary file not shown.
94
web/public/style.css
Normal file
94
web/public/style.css
Normal file
|
@ -0,0 +1,94 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Nanum+Myeongjo&display=swap');
|
||||
html {
|
||||
height: 100dvh;
|
||||
margin: 0 auto;
|
||||
max-width: 600px;
|
||||
}
|
||||
* {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
flex-shrink: 0;
|
||||
|
||||
text-wrap: balance;
|
||||
font-family: 'Atkinson Hyperlegible', sans-serif;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
title, style, script {
|
||||
display: none;
|
||||
}
|
||||
body {
|
||||
height: 100dvh;
|
||||
margin: 0 auto; padding: 0;
|
||||
line-height: 1.5;
|
||||
background-color: #808080;
|
||||
color: #000;
|
||||
}
|
||||
main {
|
||||
height: 100dvh;
|
||||
margin: 0 auto;
|
||||
/* padding-top: 64px; */
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
}
|
||||
h1, h2 {
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
h1 {
|
||||
color: #fff;
|
||||
}
|
||||
a, b, i, code, span {
|
||||
display: inline;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
a {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:focus {
|
||||
filter: brightness(1.333);
|
||||
}
|
||||
b { font-weight: bold; }
|
||||
i { font-style: italic; }
|
||||
code {
|
||||
font-family: 'DM Mono', monospace;
|
||||
font-weight: 500;
|
||||
color: #0000FF;
|
||||
}
|
||||
img, video, canvas {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
img {
|
||||
all: revert-layer;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
border-radius: 1px;
|
||||
}
|
||||
.t_symbol {
|
||||
margin: 2rem auto;
|
||||
font-family: "Nanum Myeongjo", serif;
|
||||
text-align: center;
|
||||
font-size: 64px;
|
||||
/* font-weight: bold; */
|
||||
color: #666;
|
||||
}
|
||||
footer {
|
||||
margin-top: 128px;
|
||||
height: 64px;
|
||||
line-height: 64px;
|
||||
letter-spacing: 0.5px;
|
||||
font-size: 0.75em;
|
||||
}
|
||||
input {
|
||||
outline: none;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 32px;
|
||||
letter-spacing: 1px;
|
||||
font-size: 16px;
|
||||
}
|
Loading…
Add table
Reference in a new issue