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