diff --git a/old/main.go b/old/main.go
deleted file mode 100644
index 4d5ccde..0000000
--- a/old/main.go
+++ /dev/null
@@ -1,333 +0,0 @@
-package main
-
-import (
- "compress/gzip"
- "encoding/json"
- "fmt"
- "io"
- "net"
- "net/http"
- "os"
- "strconv"
- "strings"
- "sync"
- "time"
-)
-
-type gzipResponseWriter struct {
- http.ResponseWriter
- io.Writer
-}
-
-func (g *gzipResponseWriter) Write(data []byte) (int, error) {
- return g.Writer.Write(data)
-}
-
-func GzipMiddleware(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
- next.ServeHTTP(w, r)
- return
- }
-
- gz := gzip.NewWriter(w)
- defer gz.Close()
-
- w.Header().Set("Content-Encoding", "gzip")
- next.ServeHTTP(&gzipResponseWriter{ResponseWriter: w, Writer: gz}, r)
- })
-}
-
-func getClientIP(r *http.Request) string {
- if fwdIP := r.Header.Get("X-Forwarded-For"); fwdIP != "" {
- return strings.Split(fwdIP, ",")[0]
- }
-
- clientIP := r.RemoteAddr
- if host, _, err := net.SplitHostPort(clientIP); err == nil {
- return host
- }
- return clientIP
-}
-
-type Message struct {
- Content string
- SenderIp string
- SenderUsername string
- Timestamp string
-}
-
-func NewMessage(username, ip, content string) *Message {
- timestamp := time.Now().Format("2006-01-02 15:04:05")
- return &Message{
- Content: content,
- SenderIp: ip,
- SenderUsername: username,
- Timestamp: timestamp,
- }
-}
-
-type Server struct {
- Ip string
- Port int
- Messages []Message
- // Add mappings for IPs and usernames
- Connected map[string]time.Time // Map IP -> Last activity time
- Usernames map[string]string // Map IP -> Username
- mu sync.Mutex // For thread safety
-}
-
-func NewServer(ip string, port int) *Server {
- return &Server{
- Ip: ip,
- Port: port,
- Messages: make([]Message, 0),
- Connected: make(map[string]time.Time),
- Usernames: make(map[string]string),
- mu: sync.Mutex{},
- }
-}
-
-func (s *Server) AddMessage(username string, userip string, contents string) {
- message := NewMessage(username, userip, contents)
- s.Messages = append(s.Messages, *message)
-}
-
-func (s *Server) updateActivity(ip string) {
- s.mu.Lock()
- defer s.mu.Unlock()
- s.Connected[ip] = time.Now()
-}
-
-func (s *Server) cleanupActivity() {
- s.mu.Lock()
- defer s.mu.Unlock()
- for ip, lastActivity := range s.Connected {
- if time.Since(lastActivity) > 10*time.Second {
- delete(s.Connected, ip)
- }
- }
-}
-
-func (s *Server) handlePing(w http.ResponseWriter, r *http.Request) {
- clientIP := getClientIP(r)
- s.updateActivity(clientIP)
- s.cleanupActivity()
- w.WriteHeader(http.StatusOK)
-}
-
-func validUsername(username string) bool {
- for _, c := range username {
- if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') {
- return false
- }
- }
- return true
-}
-
-func (s *Server) handleUsername(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPut {
- http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed)
- return
- }
-
- w.Header().Set("Content-Type", "application/json")
-
- clientIP := getClientIP(r)
-
- var req struct {
- Username string `json:"username"`
- }
-
- if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
- http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
- return
- }
-
- s.mu.Lock()
-
- if len(req.Username) > 64 {
- http.Error(w, fmt.Sprintf(`{"error": "Username too long (must be less than 64 characters)"}`), http.StatusRequestEntityTooLarge)
- s.mu.Unlock()
- return
- }
-
- if !validUsername(req.Username) {
- http.Error(w, fmt.Sprintf(`{"error": "Username must only contain alphanumeric characters and/or underscores"}`), http.StatusBadRequest)
- s.mu.Unlock()
- return
- }
-
- for _, username := range s.Usernames {
- if username == req.Username {
- s.mu.Unlock()
- http.Error(w, fmt.Sprintf(`{"error": "Username already exists"}`), http.StatusConflict)
- return
- }
- }
-
- if _, ok := s.Usernames[clientIP]; ok {
- fmt.Println("Name change detected, fixing messages to reflect the change")
- for i, msg := range s.Messages {
- if msg.SenderIp == clientIP {
- s.Messages[i].SenderUsername = req.Username
- }
- }
- }
- s.Usernames[clientIP] = req.Username
-
- s.mu.Unlock()
-
- json.NewEncoder(w).Encode(map[string]string{"status": "Username registered"})
- fmt.Println(clientIP, "->", req.Username)
-}
-
-func getMessageTemplate(file string, body string) string {
- contents, _ := os.ReadFile(file)
- return strings.Replace(string(contents), "{{body}}", body, 1)
-}
-
-func (s *Server) handleMessages(w http.ResponseWriter, r *http.Request) {
- switch r.Method {
- case http.MethodGet:
- w.Header().Set("Content-Type", "text/html")
-
- var body string
- for _, msg := range s.Messages {
- body += fmt.Sprintf(`
%s %s
%s
`,
- msg.SenderUsername, msg.Timestamp, msg.Content)
- }
-
- w.Write([]byte(getMessageTemplate("messages.html", body)))
-
- case http.MethodPut:
- w.Header().Set("Content-Type", "application/json")
-
- // Get client's IP
- clientIP := getClientIP(r)
-
- // Check if IP is registered
- s.mu.Lock()
- username, exists := s.Usernames[clientIP]
- s.mu.Unlock()
-
- if !exists {
- errorFmt := fmt.Sprintf(`{"error": "IP %s not registered with username"}`, clientIP)
- http.Error(w, errorFmt, http.StatusUnauthorized)
- return
- }
-
- var msg struct {
- Message string `json:"message"`
- }
-
- if err := json.NewDecoder(r.Body).Decode(&msg); err != nil {
- http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
- return
- }
-
- // Add message using the stored username and IP
- s.AddMessage(username, clientIP, msg.Message)
-
- json.NewEncoder(w).Encode(map[string]string{
- "status": "Message received",
- "from": username,
- })
- default:
- http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed)
- return
- }
-}
-
-func (s *Server) handleUsernameStatus(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodGet {
- http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed)
- return
- }
-
- w.Header().Set("Content-Type", "application/json")
- clientIP := getClientIP(r)
-
- s.mu.Lock()
- username, exists := s.Usernames[clientIP]
- s.mu.Unlock()
-
- json.NewEncoder(w).Encode(map[string]interface{}{
- "hasUsername": exists,
- "username": username,
- })
-}
-
-func (s *Server) handleUsers(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodGet {
- http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed)
- return
- }
-
- w.Header().Set("Content-Type", "application/json")
-
- clientIP := getClientIP(r)
- s.updateActivity(clientIP)
- s.cleanupActivity()
- s.mu.Lock()
- var users []string
- for ip := range s.Connected {
- users = append(users, s.Usernames[ip])
- }
- s.mu.Unlock()
-
- json.NewEncoder(w).Encode(map[string]interface{}{
- "users": users,
- })
-}
-
-func (s *Server) handleRoot(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/" {
- http.NotFound(w, r)
- return
- }
- w.Header().Set("Content-Type", "text/html")
- w.Write(readFile("root.html"))
-}
-
-func readFile(filepath string) []byte {
- contents, _ := os.ReadFile(filepath)
- return contents
-}
-
-func (s *Server) handleJs(w http.ResponseWriter, r *http.Request) {
- _ = r
- w.Header().Set("Content-Type", "application/javascript")
- w.Write(readFile("root.js"))
-}
-
-func (s *Server) handleCss(w http.ResponseWriter, r *http.Request) {
- _ = r
- w.Header().Set("Content-Type", "text/css")
- w.Write(readFile("root.css"))
-}
-
-func (s *Server) Run() {
- handler := http.NewServeMux()
- handler.HandleFunc("/ping", s.handlePing)
- handler.HandleFunc("/username", s.handleUsername)
- handler.HandleFunc("/messages", s.handleMessages)
- handler.HandleFunc("/username/status", s.handleUsernameStatus)
- handler.HandleFunc("/users", s.handleUsers)
- handler.HandleFunc("/", s.handleRoot)
- handler.HandleFunc("/root.js", s.handleJs)
- handler.HandleFunc("/root.css", s.handleCss)
- fmt.Printf("Server starting on %s:%d\n", s.Ip, s.Port)
- if err := http.ListenAndServe(fmt.Sprintf("%s:%d", s.Ip, s.Port), GzipMiddleware(handler)); err != nil {
- fmt.Printf("Server error: %v\n", err)
- }
-}
-
-func main() {
- ip := os.Args[1]
- port, _ := strconv.Atoi(os.Args[2])
- // ip := "localhost"
- // port := 8080
- server := NewServer(ip, port)
- server.Run()
-}
diff --git a/old/main.go.bak b/old/main.go.bak
deleted file mode 100644
index cd9773e..0000000
--- a/old/main.go.bak
+++ /dev/null
@@ -1,366 +0,0 @@
-package main
-
-import (
- "compress/gzip"
- "encoding/json"
- "fmt"
- "io"
- "net"
- "net/http"
- "os"
- "strconv"
- "strings"
- "sync"
- "time"
-)
-
-type gzipResponseWriter struct {
- http.ResponseWriter
- io.Writer
-}
-
-func (g *gzipResponseWriter) Write(data []byte) (int, error) {
- return g.Writer.Write(data)
-}
-
-func GzipMiddleware(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
- next.ServeHTTP(w, r)
- return
- }
-
- gz := gzip.NewWriter(w)
- defer gz.Close()
-
- w.Header().Set("Content-Encoding", "gzip")
- next.ServeHTTP(&gzipResponseWriter{ResponseWriter: w, Writer: gz}, r)
- })
-}
-
-func getClientIP(r *http.Request) string {
- if fwdIP := r.Header.Get("X-Forwarded-For"); fwdIP != "" {
- return strings.Split(fwdIP, ",")[0]
- }
-
- clientIP := r.RemoteAddr
- if host, _, err := net.SplitHostPort(clientIP); err == nil {
- return host
- }
- return clientIP
-}
-
-type Message struct {
- Content string
- SenderIp string
- SenderUsername string
- Timestamp string
-}
-
-func NewMessage(username, ip, content string) *Message {
- timestamp := time.Now().Format("2006-01-02 15:04:05")
- return &Message{
- Content: content,
- SenderIp: ip,
- SenderUsername: username,
- Timestamp: timestamp,
- }
-}
-
-// TODO: Use it
-func writeMessageToFile(msg Message) {
- contents := fmt.Sprintf("%s,%s,%s,%s\n", msg.SenderUsername, msg.SenderIp, msg.Timestamp, msg.Content)
- os.WriteFile("messages.txt", []byte(contents), os.ModeAppend)
-}
-
-// TODO: Use it
-func writeMessagesToFile(messages []Message) {
- var contents string
- for _, msg := range messages {
- contents += fmt.Sprintf("%s,%s,%s,%s\n", msg.SenderUsername, msg.SenderIp, msg.Timestamp, msg.Content)
- }
- os.WriteFile("messages.txt", []byte(contents), os.ModeAppend)
-}
-
-// TODO: Use it
-func readMessagesFromFile(filepath string) []Message {
- contents, _ := os.ReadFile(filepath)
- lines := strings.Split(string(contents), "\n")
- var messages []Message
- for _, line := range lines {
- if line == "" {
- continue
- }
- parts := strings.Split(line, ",")
- username := parts[0]
- timestamp := parts[1]
- content := strings.Join(parts[2:], " ")
- messages = append(messages, Message{username, "", content, timestamp})
- }
- return messages
-}
-
-type Server struct {
- Ip string
- Port int
- Messages []Message
- // Add mappings for IPs and usernames
- Connected map[string]time.Time // Map IP -> Last activity time
- Usernames map[string]string // Map IP -> Username
- mu sync.Mutex // For thread safety
-}
-
-func NewServer(ip string, port int) *Server {
- return &Server{
- Ip: ip,
- Port: port,
- Messages: make([]Message, 0),
- Connected: make(map[string]time.Time),
- Usernames: make(map[string]string),
- mu: sync.Mutex{},
- }
-}
-
-func (s *Server) AddMessage(username string, userip string, contents string) {
- message := NewMessage(username, userip, contents)
- s.Messages = append(s.Messages, *message)
-}
-
-func (s *Server) updateActivity(ip string) {
- s.mu.Lock()
- defer s.mu.Unlock()
- s.Connected[ip] = time.Now()
-}
-
-func (s *Server) cleanupActivity() {
- s.mu.Lock()
- defer s.mu.Unlock()
- for ip, lastActivity := range s.Connected {
- if time.Since(lastActivity) > 10*time.Second {
- delete(s.Connected, ip)
- }
- }
-}
-
-func (s *Server) handlePing(w http.ResponseWriter, r *http.Request) {
- clientIP := getClientIP(r)
- s.updateActivity(clientIP)
- s.cleanupActivity()
- w.WriteHeader(http.StatusOK)
-}
-
-func validUsername(username string) bool {
- for _, c := range username {
- if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') {
- return false
- }
- }
- return true
-}
-
-func (s *Server) handleUsername(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPut {
- http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed)
- return
- }
-
- w.Header().Set("Content-Type", "application/json")
-
- clientIP := getClientIP(r)
-
- var req struct {
- Username string `json:"username"`
- }
-
- if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
- http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
- return
- }
-
- s.mu.Lock()
-
- if len(req.Username) > 64 {
- http.Error(w, fmt.Sprintf(`{"error": "Username too long (must be less than 64 characters)"}`), http.StatusRequestEntityTooLarge)
- s.mu.Unlock()
- return
- }
-
- if !validUsername(req.Username) {
- http.Error(w, fmt.Sprintf(`{"error": "Username must only contain alphanumeric characters and/or underscores"}`), http.StatusBadRequest)
- s.mu.Unlock()
- return
- }
-
- for _, username := range s.Usernames {
- if username == req.Username {
- s.mu.Unlock()
- http.Error(w, fmt.Sprintf(`{"error": "Username already exists"}`), http.StatusConflict)
- return
- }
- }
-
- if _, ok := s.Usernames[clientIP]; ok {
- fmt.Println("Name change detected, fixing messages to reflect the change")
- for i, msg := range s.Messages {
- if msg.SenderIp == clientIP {
- s.Messages[i].SenderUsername = req.Username
- }
- }
- }
- s.Usernames[clientIP] = req.Username
-
- s.mu.Unlock()
-
- json.NewEncoder(w).Encode(map[string]string{"status": "Username registered"})
- fmt.Println(clientIP, "->", req.Username)
-}
-
-func getMessageTemplate(file string, body string) string {
- contents, _ := os.ReadFile(file)
- return strings.Replace(string(contents), "{{body}}", body, 1)
-}
-
-func (s *Server) handleMessages(w http.ResponseWriter, r *http.Request) {
- switch r.Method {
- case http.MethodGet:
- w.Header().Set("Content-Type", "text/html")
-
- var body string
- for _, msg := range s.Messages {
- body += fmt.Sprintf(`%s %s
%s
`,
- msg.SenderUsername, msg.Timestamp, msg.Content)
- }
-
- w.Write([]byte(getMessageTemplate("messages.html", body)))
-
- case http.MethodPut:
- w.Header().Set("Content-Type", "application/json")
-
- // Get client's IP
- clientIP := getClientIP(r)
-
- // Check if IP is registered
- s.mu.Lock()
- username, exists := s.Usernames[clientIP]
- s.mu.Unlock()
-
- if !exists {
- errorFmt := fmt.Sprintf(`{"error": "IP %s not registered with username"}`, clientIP)
- http.Error(w, errorFmt, http.StatusUnauthorized)
- return
- }
-
- var msg struct {
- Message string `json:"message"`
- }
-
- if err := json.NewDecoder(r.Body).Decode(&msg); err != nil {
- http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
- return
- }
-
- // Add message using the stored username and IP
- s.AddMessage(username, clientIP, msg.Message)
-
- json.NewEncoder(w).Encode(map[string]string{
- "status": "Message received",
- "from": username,
- })
- default:
- http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed)
- return
- }
-}
-
-func (s *Server) handleUsernameStatus(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodGet {
- http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed)
- return
- }
-
- w.Header().Set("Content-Type", "application/json")
- clientIP := getClientIP(r)
-
- s.mu.Lock()
- username, exists := s.Usernames[clientIP]
- s.mu.Unlock()
-
- json.NewEncoder(w).Encode(map[string]interface{}{
- "hasUsername": exists,
- "username": username,
- })
-}
-
-func (s *Server) handleUsers(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodGet {
- http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed)
- return
- }
-
- w.Header().Set("Content-Type", "application/json")
-
- clientIP := getClientIP(r)
- s.updateActivity(clientIP)
- s.cleanupActivity()
- s.mu.Lock()
- var users []string
- for ip := range s.Connected {
- users = append(users, s.Usernames[ip])
- }
- s.mu.Unlock()
-
- json.NewEncoder(w).Encode(map[string]interface{}{
- "users": users,
- })
-}
-
-func (s *Server) handleRoot(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/" {
- http.NotFound(w, r)
- return
- }
- w.Header().Set("Content-Type", "text/html")
- w.Write(readFile("root.html"))
-}
-
-func readFile(filepath string) []byte {
- contents, _ := os.ReadFile(filepath)
- return contents
-}
-
-func (s *Server) handleJs(w http.ResponseWriter, r *http.Request) {
- _ = r
- w.Header().Set("Content-Type", "application/javascript")
- w.Write(readFile("root.js"))
-}
-
-func (s *Server) handleCss(w http.ResponseWriter, r *http.Request) {
- _ = r
- w.Header().Set("Content-Type", "text/css")
- w.Write(readFile("root.css"))
-}
-
-func (s *Server) Run() {
- handler := http.NewServeMux()
- handler.HandleFunc("/ping", s.handlePing)
- handler.HandleFunc("/username", s.handleUsername)
- handler.HandleFunc("/messages", s.handleMessages)
- handler.HandleFunc("/username/status", s.handleUsernameStatus)
- handler.HandleFunc("/users", s.handleUsers)
- handler.HandleFunc("/", s.handleRoot)
- handler.HandleFunc("/root.js", s.handleJs)
- handler.HandleFunc("/root.css", s.handleCss)
- fmt.Printf("Server starting on %s:%d\n", s.Ip, s.Port)
- if err := http.ListenAndServe(fmt.Sprintf("%s:%d", s.Ip, s.Port), GzipMiddleware(handler)); err != nil {
- fmt.Printf("Server error: %v\n", err)
- }
-}
-
-func main() {
- ip := os.Args[1]
- port, _ := strconv.Atoi(os.Args[2])
- // ip := "localhost"
- // port := 8080
- server := NewServer(ip, port)
- server.Run()
-}
diff --git a/old/messages.html b/old/messages.html
deleted file mode 100644
index 2e97c0c..0000000
--- a/old/messages.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- {{body}}
-
-
-
diff --git a/old/root.css b/old/root.css
deleted file mode 100644
index 05ceb78..0000000
--- a/old/root.css
+++ /dev/null
@@ -1,247 +0,0 @@
-:root {
- --radchat-color: #40a02b;
- --main-bg-color: #11111b;
- --pum-button-inactive-fg: #cdd6f4;
- --pum-button-inactive-bg: #11111b;
- --pum-button-active-fg: #89b4fa;
- --pum-button-active-bg: #1e1e2e;
- --pum-title-color: #cdd6f4;
- --pum-bg-color: #1e1e2e;
- --user-color: #89b4fa;
- --timestamp-color: #313244;
- --separator-color: #181825;
- --message-color: #cdd6f4;
- --message-bg-color: #1e1e2e;
- --input-bg-color: #181825;
- --input-text-color: #cdd6f4;
- --input-button-inactive-bg: #b4befe;
- --input-button-inactive-fg: #11111b;
- --input-button-active-bg: #89b4fa;
- --input-button-active-fg: #11111b;
-}
-body {
- font-family: Arial, sans-serif;
- margin: 0;
- padding: 0; /* Remove padding from body */
- background-color: var(--main-bg-color);
- color: var(--pum-title-color);
- height: 100vh;
- display: flex;
- flex-direction: column;
- overflow: hidden; /* Prevent body scroll */
-}
-.container {
- max-width: 800px;
- margin: 0 auto;
- width: 100%;
- position: relative;
- height: 100vh;
- padding: 20px; /* Move padding here */
- padding-bottom: 80px; /* Space for message input */
- display: flex;
- flex-direction: column;
-}
-
-.header-controls {
- position: fixed;
- top: 20px;
- left: 50%;
- transform: translateX(-50%);
- display: flex;
- gap: 20px; /* Space between buttons */
- align-items: center;
- justify-content: center;
-}
-
-@media (max-width: 768px) {
- .header-controls {
- gap: 10px;
- }
-}
-
-.settings-button, .users-button {
- top: 20px;
- background: none;
- border: none;
- cursor: pointer;
- padding: 5px;
- border-radius: 50%;
- width: 40px;
- height: 40px;
- display: flex;
- align-items: center;
- justify-content: center;
- background-color: var(--pum-button-inactive-bg);
-}
-
-.settings-button {
- right: calc(48%);
-}
-
-.users-button {
- right: calc(52%);
-}
-
-.settings-icon, .users-icon {
- width: 24px;
- height: 24px;
- fill: var(--pum-button-inactive-fg);
-}
-
-.settings-button:hover, .users-button:hover {
- background-color: var(--pum-button-active-bg);
-}
-
-.settings-button:hover .settings-icon,
-.users-button:hover .users-icon {
- fill: var(--pum-button-active-fg);
-}
-
-#users-list {
- margin-top: 0px;
-}
-
-.current-user {
- position: absolute;
- right: 0;
- top: 28px;
- color: var(--user-color);
- font-weight: bold;
-}
-
-.username-section, .users-section {
- position: fixed;
- transform: translateX(-50%);
- top: 70px;
- padding: 15px;
- background-color: var(--pum-bg-color);
- border-radius: 5px;
- box-shadow: 0 2px 10px rgba(0,0,0,0.3);
- z-index: 100;
- display: none;
- min-width: 250px;
-}
-
-.username-section {
- right: 50%;
- transform: translateX(10%);
-}
-
-.users-section {
- right: 50%;
- transform: translateX(-10%);
-}
-
-.users-section h3 {
- margin-top: 0;
- margin-bottom: 10px;
-}
-
-.user-item {
- padding: 8px 0;
- color: var(--user-color);
- border-bottom: 1px solid var(--separator-color);
-}
-
-.user-item:last-child {
- border-bottom: none;
-}
-.messages-section {
- flex-grow: 1;
- margin-top: 60px; /* Space for gear icon */
- margin-bottom: 20px;
- overflow-y: auto;
- height: 0; /* Allow flex-grow to work */
-}
-.message {
- padding: 10px 0;
-}
-.message .username {
- color: var(--user-color);
- font-weight: bold;
-}
-.message .timestamp {
- color: var(--timestamp-color);
- font-weight: thin;
- font-size: 0.8em;
-}
-
-.message .content {
- margin-top: 5px;
- color: var(--message-color);
- word-wrap: break-word; /* Handle long words */
- word-break: break-word; /* Break words at arbitrary points if needed */
- white-space: pre-wrap; /* Preserve whitespace and wraps */
- max-width: 100%; /* Ensure it doesn't exceed container width */
-}
-.message-section {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- padding: 20px;
- background-color: var(--message-bg-color);
- z-index: 100; /* Ensure it stays on top */
-}
-
-.message-container {
- max-width: 800px;
- margin: 0 auto;
- display: flex;
- gap: 10px;
-}
-input {
- padding: 10px;
- border: none;
- border-radius: 4px;
- background-color: var(--input-bg-color);
- color: var(--input-text-color);
- flex-grow: 1;
-}
-button {
- padding: 10px 20px;
- border: none;
- border-radius: 4px;
- background-color: var(--input-button-inactive-bg);
- color: var(--input-button-inactive-fg);
- cursor: pointer;
-}
-button:hover {
- background-color: var(--input-button-active-bg);
- color: var(--input-button-active-fg)
-}
-
-button.scroll {
- padding: 10px 20px;
- border-radius: 4px;
- border: none;
- background-color: var(--input-button-inactive-bg);
- padding: 10px;
- cursor: pointer;
-}
-
-button.scroll:hover {
- background-color: var(--input-button-active-bg);
-}
-
-.scroll-icon {
- width: 14px;
- height: 14px;
- fill: var(--var-input-button-inactive-fg);
-}
-
-.scroll-icon:hover {
- fill: var(--var-input-button-active-fg);
-}
-
-.radchat {
- position: absolute;
- left: 0;
- font-family: Arial, sans-serif;
- font-size: 2em;
- font-weight: bold;
- color: var(--radchat-color);
-}
-#status, #username-status {
- margin-top: 10px;
-}
diff --git a/old/root.html b/old/root.html
deleted file mode 100644
index 9ad3beb..0000000
--- a/old/root.html
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
-
-
-
-
-
-
- RadChat
-
-
-
-
-
-
- RadChat
-
-
-
-
-
-
-
-
Username
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/old/root.js b/old/root.js
deleted file mode 100644
index f144121..0000000
--- a/old/root.js
+++ /dev/null
@@ -1,244 +0,0 @@
-function toggleSettings() {
- const panel = document.getElementById('settings-panel');
- panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
- if (panel.style.display === 'block') {
- const username = document.getElementById('username');
- username.focus();
- username.selectionStart = username.selectionEnd = username.value.length
- }
-}
-
-function toggleUsers() {
- const panel = document.getElementById('users-panel');
- panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
-}
-
-document.addEventListener('click', function(event) {
- const settingsPanel = document.getElementById('settings-panel');
- const settingsButton = document.querySelector('.settings-button');
- const usersPanel = document.getElementById('users-panel');
- const usersButton = document.querySelector('.users-button');
-
- if (!settingsPanel.contains(event.target) && !settingsButton.contains(event.target)) {
- settingsPanel.style.display = 'none';
- }
-
- if (!usersPanel.contains(event.target) && !usersButton.contains(event.target)) {
- usersPanel.style.display = 'none';
- }
-});
-
-document.addEventListener('keypress', function(event) {
- if (event.key === 'Enter') {
- const settingsPanel = document.getElementById('settings-panel');
- const inputPanel = document.getElementById('message');
- if (settingsPanel.contains(event.target)) {
- setUsername();
- }
- if (inputPanel.contains(event.target)) {
- sendMessage();
- }
- }
-});
-
-async function loadUsers() {
- try {
- const response = await fetch('/users');
- const data = await response.json();
-
- const usersList = document.getElementById('users-list');
- usersList.innerHTML = '';
-
- data.users.sort().forEach(user => {
- const userDiv = document.createElement('div');
- userDiv.className = 'user-item';
- userDiv.textContent = user;
- usersList.appendChild(userDiv);
- });
- } catch (error) {
- console.error('Error loading users:', error);
- }
-}
-
-
-let lastMessageCount = 0;
-async function loadMessages() {
- try {
- const messagesDiv = document.getElementById('messages');
- const response = await fetch('/messages');
- const text = await response.text();
- const tempDiv = document.createElement('div');
- tempDiv.innerHTML = text;
-
- if (messagesDiv.scrollTop != bottom) {
- // show a button to scroll to the bottom
- const scrollToBottomButton = document.getElementById('scroll');
- scrollToBottomButton.style.display = 'block';
- scrollToBottomButton.onclick = scrollToBottom;
- } else {
- // hide the button
- const scrollToBottomButton = document.getElementById('scroll');
- scrollToBottomButton.style.display = 'none';
- }
-
- const messages = tempDiv.getElementsByTagName('p');
-
- const newMessageCount = messages.length;
-
- if (newMessageCount > lastMessageCount || lastMessageCount === 0) {
- messagesDiv.innerHTML = '';
- Array.from(messages).forEach(msg => {
- const messageDiv = document.createElement('div');
- messageDiv.className = 'message';
- const [username, content] = msg.innerHTML.split('
');
- messageDiv.innerHTML = '' + username + '
' +
- '' + content + '
';
- messagesDiv.appendChild(messageDiv);
- });
- scrollToBottom();
- lastMessageCount = newMessageCount;
- }
- } catch (error) {
- console.error('Error loading messages:', error);
- }
-}
-
-var bottom = 0;
-
-function scrollToBottom() {
- const messagesDiv = document.getElementById('messages');
- messagesDiv.scrollTop = messagesDiv.scrollHeight;
- bottom = messagesDiv.scrollTop;
-}
-
-async function checkUsername() {
- try {
- const response = await fetch('/username/status');
- const data = await response.json();
- if (!data.hasUsername) {
- document.getElementById('settings-panel').style.display = 'block'
- const username = document.getElementById('username');
- username.focus();
- username.selectionStart = username.selectionEnd = username.value.length
- }
- } catch (error) {
- console.error('Error checking username status:', error);
- }
-}
-
-async function updateCurrentUser() {
- try {
- const response = await fetch('/username/status');
- const data = await response.json();
- const userDiv = document.getElementById('current-user');
- if (data.hasUsername) {
- userDiv.textContent = data.username;
- } else {
- userDiv.textContent = '';
- }
- } catch (error) {
- console.error('Error getting username:', error);
- }
-}
-
-async function setUsername() {
- const username = document.getElementById('username').value;
- if (!username) {
- showUsernameStatus('Please enter a username', 'red');
- return;
- }
-
- try {
- const response = await fetch('/username', {
- method: 'PUT',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ username: username })
- });
-
- const data = await response.json();
-
- if (response.ok) {
- showUsernameStatus('Username set successfully!', 'green');
- updateCurrentUser();
- setTimeout(() => {
- document.getElementById('settings-panel').style.display = 'none';
- }, 500);
- location.reload();
- } else {
- showUsernameStatus(data.error || 'Failed to set username', 'red');
- }
- } catch (error) {
- showUsernameStatus('Error connecting to server', 'red');
- }
-}
-
-async function sendMessage() {
- const message = document.getElementById('message').value;
- if (!message) {
- return;
- }
-
- try {
- const response = await fetch('/messages', {
- method: 'PUT',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ message: message })
- });
-
- const data = await response.json();
-
- if (response.ok) {
- document.getElementById('message').value = '';
- // showStatus('Message sent!', '#64b5f6');
- loadMessages();
- } else {
- showStatus(data.error || 'Failed to send message', 'red');
- }
- } catch (error) {
- showStatus('Error connecting to server', 'red');
- }
-}
-
-function showStatus(message, color) {
- const status = document.getElementById('status');
- status.textContent = message;
- status.style.color = color;
- setTimeout(() => {
- status.textContent = '';
- }, 3000);
-}
-
-function showUsernameStatus(message, color) {
- const status = document.getElementById('username-status');
- status.textContent = message;
- status.style.color = color;
- setTimeout(() => {
- status.textContent = '';
- }, 3000);
-}
-
-async function pingCheck() {
- try {
- await fetch('/ping', {method: 'POST'});
- } catch (error) {
- console.error('Ping failed:', error);
- }
-}
-
-async function initialize() {
- await loadMessages();
- document.getElementById('users-panel').style.display = 'none';
- document.getElementById('settings-panel').style.display = 'none';
- checkUsername();
- updateCurrentUser();
- setInterval(loadMessages, 1000);
- setInterval(loadUsers, 1000);
- setInterval(pingCheck, 3000);
- scrollToBottom();
-}
-
-initialize();
diff --git a/notes/todo.md b/readme.md
similarity index 100%
rename from notes/todo.md
rename to readme.md