feat(ods): implement basic ods functionality

This commit is contained in:
Julien Dessaux 2024-03-25 13:58:48 +01:00
parent 510ad0cf55
commit 4c9b51b8c6
Signed by: adyxax
GPG key ID: F92E51B86E07177E
4 changed files with 258 additions and 0 deletions

29
index.html Normal file
View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="/static/favicon.svg">
<link rel="stylesheet" type="text/css" href="/static/index4.css" media="all">
<title>ODS</title>
</head>
<body>
<main>
<form action="/" method="post">
<input class="fullwidth" type="text" placeholder="Entrez un mot" id="query" name="query" value="{{ .Query }}" minlength="2" maxlength="32" onfocus="this.select()" required autofocus/>
<div class=".btn-group">
<input type="submit" value="Valider"/>
{{ if .HasQuery }}
<input type="submit" formmethod="GET" value="Effacer"/>
{{ end }}
</div>
</form>
<p>
{{ if .HasQuery }}
<a href="https://dictionnaire.lerobert.com/definition/{{ .Query }}">{{ .Query }}</a> est un mot <span class="{{ if .Invalid }}invalid">IN{{ else }}valid">{{ end }}VALIDE</span> au scrabble.
{{ end }}
</p>
</main>
</body>
</html>

75
main.go Normal file
View file

@ -0,0 +1,75 @@
package main
import (
"embed"
"html/template"
"log/slog"
"net/http"
"strings"
)
// Variables to customise the search behaviour
const (
listenStr = "0.0.0.0:8090"
)
//go:embed ods.txt
var ods string
//go:embed index.html
var templatesFS embed.FS
//go:embed static/*
var static embed.FS
// html templates
var indexTemplate = template.Must(template.New("index").ParseFS(templatesFS, "index.html"))
type IndexData struct {
HasQuery bool
Query string
Invalid bool
}
func getIndex() http.Handler {
data := IndexData{
HasQuery: false,
Query: "",
Invalid: true,
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "no-store, no-cache")
indexTemplate.ExecuteTemplate(w, "index.html", data)
})
}
func postIndex() http.Handler {
words := strings.Split(ods,"\n")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
data := IndexData{
HasQuery: true,
Query: r.FormValue("query"),
Invalid: true,
}
query := strings.ToUpper(data.Query)
for _, w := range words {
if w == query {
data.Invalid = false
break
}
}
w.Header().Set("Cache-Control", "no-store, no-cache")
indexTemplate.ExecuteTemplate(w, "index.html", data)
})
}
// The main function
func main() {
http.Handle("GET /static/", http.FileServer(http.FS(static)))
http.Handle("GET /", getIndex())
http.Handle("POST /", postIndex())
slog.Info("listening", "addr", listenStr)
if err := http.ListenAndServe(listenStr, nil); err != nil && err != http.ErrServerClosed {
slog.Error("error listening and serving", "error", err)
}
}

97
static/favicon.svg Normal file
View file

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="180"
height="180"
viewBox="0 0 180 180"
version="1.1"
id="svg5"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="favicon.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="1.3984437"
inkscape:cx="93.675563"
inkscape:cy="74.010846"
inkscape:window-width="1362"
inkscape:window-height="717"
inkscape:window-x="0"
inkscape:window-y="49"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient3622">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3618" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3620" />
</linearGradient>
<rect
x="21.339478"
y="33.460999"
width="232.05208"
height="284.04139"
id="rect3591" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3622"
id="linearGradient3624"
x1="19.339844"
y1="108.185"
x2="103.28756"
y2="108.185"
gradientUnits="userSpaceOnUse" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#fee797;stroke-width:8;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-dasharray:none;stroke-linejoin:round;stroke-linecap:round"
id="rect113"
width="180"
height="180"
x="0"
y="0" />
<text
xml:space="preserve"
id="text3589"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:186.667px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;shape-inside:url(#rect3591);fill:#000000;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1"
transform="translate(-0.935125,-48.075078)"><tspan
x="21.339844"
y="199.55305"
id="tspan7040">A</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:53.3333px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
x="138.96565"
y="149.52071"
id="text3732"><tspan
sodipodi:role="line"
id="tspan3730"
x="138.96565"
y="149.52071">1</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

57
static/index4.css Normal file
View file

@ -0,0 +1,57 @@
* {
box-sizing: border-box;
scrollbar-gutter: stable both-edges;
}
body {
background: #009E60;
display: grid;
grid-template-rows: auto 1fr auto;
font-family: -apple-system, BlinkMacSystemFont,
"Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell",
"Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
font-feature-settings: "kern" 1;
font-kerning: normal;
}
form {
align-items: center;
display: flex;
flex-wrap: wrap;
justify-content: start;
}
input,.btn-group {
margin-top: 1em;
margin-right: 1.5em;
}
main {
background: #F6F7EB;
padding-left: 1em;
padding-right: 1em;
}
@media only screen and (min-width: 48rem) {
body {
max-width:48rem;
margin-left: auto;
margin-right: auto;
}
}
span {
font-weight: bold;
padding-bottom: 0.1em;
padding-left: 0.5em;
padding-right: 0.5em;
padding-top: 0.1em;
}
.valid {
background: #009E60;
color: #F6F7EB;
}
.invalid {
background: #E94F37;
}