From c6fd4f51f4273c335ce867516a1f711dc9d06986 Mon Sep 17 00:00:00 2001 From: NeilClack Date: Sat, 30 May 2026 13:11:09 -0500 Subject: [PATCH 1/3] Added frequency aligned color paletes --- internal/colors/colors.go | 100 +++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 39 deletions(-) diff --git a/internal/colors/colors.go b/internal/colors/colors.go index b0f156b..64ba63c 100644 --- a/internal/colors/colors.go +++ b/internal/colors/colors.go @@ -57,15 +57,20 @@ var ( Crimson = sdl.Color{R: 220, G: 20, B: 60, A: 255} Firebrick = sdl.Color{R: 178, G: 34, B: 34, A: 255} DarkRed = sdl.Color{R: 139, G: 0, B: 0, A: 255} + DeepBloodRed = sdl.Color{R: 139, G: 0, B: 0, A: 255} ) // Oranges var ( - OrangeRed = sdl.Color{R: 255, G: 69, B: 0, A: 255} - Tomato = sdl.Color{R: 255, G: 99, B: 71, A: 255} - Coral = sdl.Color{R: 255, G: 127, B: 80, A: 255} - DarkOrange = sdl.Color{R: 255, G: 140, B: 0, A: 255} - Orange = sdl.Color{R: 255, G: 165, B: 0, A: 255} + LightOrange = sdl.Color{R: 255, G: 200, B: 127, A: 255} + Orange = sdl.Color{R: 255, G: 165, B: 0, A: 255} + OrangeRed = sdl.Color{R: 255, G: 69, B: 0, A: 255} + DarkOrange = sdl.Color{R: 255, G: 140, B: 0, A: 255} + DarkOrangeRed = sdl.Color{R: 255, G: 140, B: 105, A: 255} + Tomato = sdl.Color{R: 255, G: 99, B: 71, A: 255} + Coral = sdl.Color{R: 255, G: 127, B: 80, A: 255} + PaleOrange = sdl.Color{R: 255, G: 223, B: 186, A: 255} + RedOrange = sdl.Color{R: 255, G: 69, B: 0, A: 255} ) // Yellows @@ -84,22 +89,25 @@ var ( // Purples & Violets var ( - Lavender = sdl.Color{R: 230, G: 230, B: 250, A: 255} - Thistle = sdl.Color{R: 216, G: 191, B: 216, A: 255} - Plum = sdl.Color{R: 221, G: 160, B: 221, A: 255} - Violet = sdl.Color{R: 238, G: 130, B: 238, A: 255} - Orchid = sdl.Color{R: 218, G: 112, B: 214, A: 255} - Magenta = sdl.Color{R: 255, G: 0, B: 255, A: 255} - MediumOrchid = sdl.Color{R: 186, G: 85, B: 211, A: 255} - MediumPurple = sdl.Color{R: 147, G: 112, B: 219, A: 255} - RebeccaPurple = sdl.Color{R: 102, G: 51, B: 153, A: 255} - BlueViolet = sdl.Color{R: 138, G: 43, B: 226, A: 255} - DarkViolet = sdl.Color{R: 148, G: 0, B: 211, A: 255} - DarkOrchid = sdl.Color{R: 153, G: 50, B: 204, A: 255} - DarkMagenta = sdl.Color{R: 139, G: 0, B: 139, A: 255} - Indigo = sdl.Color{R: 75, G: 0, B: 130, A: 255} - SlateBlue = sdl.Color{R: 106, G: 90, B: 205, A: 255} - DarkSlateBlue = sdl.Color{R: 72, G: 61, B: 139, A: 255} + Lavender = sdl.Color{R: 230, G: 230, B: 250, A: 255} + Thistle = sdl.Color{R: 216, G: 191, B: 216, A: 255} + Plum = sdl.Color{R: 221, G: 160, B: 221, A: 255} + Violet = sdl.Color{R: 238, G: 130, B: 238, A: 255} + Orchid = sdl.Color{R: 218, G: 112, B: 214, A: 255} + Magenta = sdl.Color{R: 255, G: 0, B: 255, A: 255} + MediumOrchid = sdl.Color{R: 186, G: 85, B: 211, A: 255} + MediumPurple = sdl.Color{R: 147, G: 112, B: 219, A: 255} + RebeccaPurple = sdl.Color{R: 102, G: 51, B: 153, A: 255} + BlueViolet = sdl.Color{R: 138, G: 43, B: 226, A: 255} + DarkViolet = sdl.Color{R: 148, G: 0, B: 211, A: 255} + DarkOrchid = sdl.Color{R: 153, G: 50, B: 204, A: 255} + DarkMagenta = sdl.Color{R: 139, G: 0, B: 139, A: 255} + Indigo = sdl.Color{R: 75, G: 0, B: 130, A: 255} + SlateBlue = sdl.Color{R: 106, G: 90, B: 205, A: 255} + DarkSlateBlue = sdl.Color{R: 72, G: 61, B: 139, A: 255} + MediumSlateBlue = sdl.Color{R: 123, G: 104, B: 238, A: 255} + LavenderBlush = sdl.Color{R: 255, G: 240, B: 245, A: 255} + LavenderGray = sdl.Color{R: 250, G: 240, B: 250, A: 255} ) // Greens @@ -123,6 +131,9 @@ var ( DarkSeaGreen = sdl.Color{R: 143, G: 188, B: 143, A: 255} LightSeaGreen = sdl.Color{R: 32, G: 178, B: 170, A: 255} DarkCyan = sdl.Color{R: 0, G: 139, B: 139, A: 255} + MintGreen = sdl.Color{R: 173, G: 255, B: 169, A: 255} + VeryPaleGreen = sdl.Color{R: 200, G: 255, B: 210, A: 255} + Honeydew = sdl.Color{R: 240, G: 255, B: 240, A: 255} ) // Blues & Cyans @@ -144,11 +155,10 @@ var ( DeepSkyBlue = sdl.Color{R: 0, G: 191, B: 255, A: 255} DodgerBlue = sdl.Color{R: 30, G: 144, B: 255, A: 255} CornflowerBlue = sdl.Color{R: 100, G: 149, B: 237, A: 255} - MediumSlateBlue = sdl.Color{R: 123, G: 104, B: 238, A: 255} - RoyalBlue = sdl.Color{R: 65, G: 105, B: 225, A: 255} MediumBlue = sdl.Color{R: 0, G: 0, B: 205, A: 255} DarkBlue = sdl.Color{R: 0, G: 0, B: 139, A: 255} MidnightBlue = sdl.Color{R: 25, G: 25, B: 112, A: 255} + RoyalBlue = sdl.Color{R: 65, G: 105, B: 225, A: 255} ) // Browns @@ -173,20 +183,32 @@ var ( // Whites var ( - Snow = sdl.Color{R: 255, G: 250, B: 250, A: 255} - Honeydew = sdl.Color{R: 240, G: 255, B: 240, A: 255} - MintCream = sdl.Color{R: 245, G: 255, B: 250, A: 255} - Azure = sdl.Color{R: 240, G: 255, B: 255, A: 255} - AliceBlue = sdl.Color{R: 240, G: 248, B: 255, A: 255} - GhostWhite = sdl.Color{R: 248, G: 248, B: 255, A: 255} - WhiteSmoke = sdl.Color{R: 245, G: 245, B: 245, A: 255} - Seashell = sdl.Color{R: 255, G: 245, B: 238, A: 255} - Beige = sdl.Color{R: 245, G: 245, B: 220, A: 255} - OldLace = sdl.Color{R: 253, G: 245, B: 230, A: 255} - FloralWhite = sdl.Color{R: 255, G: 250, B: 240, A: 255} - Ivory = sdl.Color{R: 255, G: 255, B: 240, A: 255} - AntiqueWhite = sdl.Color{R: 250, G: 235, B: 215, A: 255} - Linen = sdl.Color{R: 250, G: 240, B: 230, A: 255} - LavenderBlush = sdl.Color{R: 255, G: 240, B: 245, A: 255} - MistyRose = sdl.Color{R: 255, G: 228, B: 225, A: 255} + Snow = sdl.Color{R: 255, G: 250, B: 250, A: 255} + MintCream = sdl.Color{R: 245, G: 255, B: 250, A: 255} + Azure = sdl.Color{R: 240, G: 255, B: 255, A: 255} + AliceBlue = sdl.Color{R: 240, G: 248, B: 255, A: 255} + GhostWhite = sdl.Color{R: 248, G: 248, B: 255, A: 255} + WhiteSmoke = sdl.Color{R: 245, G: 245, B: 245, A: 255} + Seashell = sdl.Color{R: 255, G: 245, B: 238, A: 255} + Beige = sdl.Color{R: 245, G: 245, B: 220, A: 255} + OldLace = sdl.Color{R: 253, G: 245, B: 230, A: 255} + FloralWhite = sdl.Color{R: 255, G: 250, B: 240, A: 255} + Ivory = sdl.Color{R: 255, G: 255, B: 240, A: 255} + AntiqueWhite = sdl.Color{R: 250, G: 235, B: 215, A: 255} + Linen = sdl.Color{R: 250, G: 240, B: 230, A: 255} + MistyRose = sdl.Color{R: 255, G: 228, B: 225, A: 255} +) + +// Groups + +// Based on Solfeggio Frequencies +var ( + Freq_396_Colors = [7]sdl.Color{DeepBloodRed, Firebrick, Crimson, Tomato, DarkOrangeRed, Orange, PeachPuff} + Freq_417_Colors = [7]sdl.Color{RedOrange, Coral, DarkOrange, Orange, LightOrange, PaleOrange, BlanchedAlmond} + // Default proclamation needs to be using this + Freq_528_Colors = [7]sdl.Color{LimeGreen, MediumSeaGreen, LightGreen, PaleGreen, MintGreen, VeryPaleGreen, Honeydew} + Freq_639_Colors = [7]sdl.Color{SpringGreen, DarkTurquoise, Turquoise, CadetBlue, AquaMarine, PaleTurquoise, LightCyan} + Freq_741_Colors = [7]sdl.Color{DeepSkyBlue, DodgerBlue, RoyalBlue, CornflowerBlue, SkyBlue, LightBlue, AliceBlue} + Freq_852_Colors = [7]sdl.Color{Indigo, MediumPurple, BlueViolet, MediumSlateBlue, Orchid, Thistle, Lavender} + Freq_963_Colors = [7]sdl.Color{DarkViolet, MediumOrchid, MediumVioletRed, Plum, Violet, LavenderBlush, LavenderGray} ) From 0ebc3adf4ae52c0c944440fdf5b59f3607fc9fd3 Mon Sep 17 00:00:00 2001 From: NeilClack Date: Sun, 31 May 2026 12:08:19 -0500 Subject: [PATCH 2/3] Proclamations change colors! --- internal/application/application.go | 1 + internal/db/newDb.go | 2 +- internal/db/schema.go | 6 ++ internal/page/proclamationOptions.go | 4 +- internal/page/scrollerPage.go | 118 +++++++++++++++++++++++---- internal/sync/sync.go | 28 ++++++- 6 files changed, 138 insertions(+), 21 deletions(-) diff --git a/internal/application/application.go b/internal/application/application.go index b00ec7a..424bd4f 100644 --- a/internal/application/application.go +++ b/internal/application/application.go @@ -157,6 +157,7 @@ func Run() { // Sync the proclamations from the API to the Database sync.SyncProclamations(DB) sync.SyncCategories(DB) + sync.SyncFrequencies(DB) app.runEventLoop() } diff --git a/internal/db/newDb.go b/internal/db/newDb.go index 21a3e1b..ddfc273 100644 --- a/internal/db/newDb.go +++ b/internal/db/newDb.go @@ -42,7 +42,7 @@ func Connect(dbpath string) (*gorm.DB, error) { // Migrate the database schema func Migrate(db *gorm.DB) error { // Pass all models to be migrated to this function call. - if err := db.AutoMigrate(&AppConfig{}, &Log{}, &Proclamation{}, &ProclamationCategory{}); err != nil { + if err := db.AutoMigrate(&AppConfig{}, &Log{}, &Proclamation{}, &ProclamationCategory{}, &SolfeggioFrequency{}); err != nil { return err } return nil diff --git a/internal/db/schema.go b/internal/db/schema.go index 89775d1..1b5437a 100644 --- a/internal/db/schema.go +++ b/internal/db/schema.go @@ -42,3 +42,9 @@ type ProclamationCategory struct { Name string ParentId int } + +type SolfeggioFrequency struct { + ID int `gorm:"primaryKey"` + Name string + Value int +} diff --git a/internal/page/proclamationOptions.go b/internal/page/proclamationOptions.go index 9f94f5c..9ec0592 100644 --- a/internal/page/proclamationOptions.go +++ b/internal/page/proclamationOptions.go @@ -82,7 +82,7 @@ func (p *ProclamationOptions) Init(ctx *PageCtx) error { p.scrollTrackWidth = 30 p.scrollTrackX = p.ScreenWidth - p.scrollTrackWidth var err error - + // Syncronize proclamations first var allProcs []db.Proclamation allProcs, err = sync.SyncProclamations(ctx.DbPtr) @@ -91,6 +91,8 @@ func (p *ProclamationOptions) Init(ctx *PageCtx) error { ctx.DbPtr.Find(&allProcs) } + sync.SyncFrequencies(ctx.DbPtr) + fm := fontManager.Get() font, _ := fm.GetFont("Roboto-Regular") p.font = font diff --git a/internal/page/scrollerPage.go b/internal/page/scrollerPage.go index dd8b2c3..80dfb74 100644 --- a/internal/page/scrollerPage.go +++ b/internal/page/scrollerPage.go @@ -24,18 +24,27 @@ const ( defaultProclamationPlayCount int = 1 ) +type ScrollerProclamation struct { + db.Proclamation + colorPattern.ColorPattern + ColorPalette []sdl.Color +} + type Line struct { words []StringItem width int32 + color sdl.Color // Color for the entire line } // proclamationStream tracks one proclamation and how many full loops it should play. type proclamationStream struct { - name string - words []string - repeat int - completed int - wordIndex int + name string + words []string + repeat int + completed int + wordIndex int + palette []sdl.Color // Color palette for this proclamation + colorIndex int // Track which color to use next } // LineDeque is a double-ended queue for Line values backed by a circular buffer. @@ -155,12 +164,16 @@ type ScrollerPage struct { velocityState int autoVelocity bool hold float32 + + // Color palette settings + useColorPalettes bool } func (p *ScrollerPage) Init(ctx *PageCtx) error { p.increment = 100.0 p.velocity = 800.0 p.SetAutoVelocity(true, -4000.0, 4000.0, 100.0, 2.5, 6.0) + p.useColorPalettes = true // Default to enabled sm := shaderManager.Get() fm := fontManager.Get() @@ -268,11 +281,36 @@ func (p *ScrollerPage) Init(ctx *PageCtx) error { func (p *ScrollerPage) loadProclamations() error { p.streams = p.streams[:0] + // Load all solfeggio frequencies + var frequencies []db.SolfeggioFrequency + if err := p.Context.DbPtr.Find(&frequencies).Error; err != nil { + return fmt.Errorf("failed to load solfeggio frequencies: %v", err) + } + + // Create a map of frequency ID to its value + frequencyMap := make(map[int]int) + for _, freq := range frequencies { + frequencyMap[freq.ID] = freq.Value + } + + // Map frequency values to their color palettes + frequencyPalettes := map[int][]sdl.Color{ + 396: colors.Freq_396_Colors[:], + 417: colors.Freq_417_Colors[:], + 528: colors.Freq_528_Colors[:], // Default proclamation + 639: colors.Freq_639_Colors[:], + 741: colors.Freq_741_Colors[:], + 852: colors.Freq_852_Colors[:], + 963: colors.Freq_963_Colors[:], + } + + // Load selected proclamations var selected []db.Proclamation if err := p.Context.DbPtr.Where("selected = ?", true).Find(&selected).Error; err != nil { return err } + // Load default proclamation var defaultProclamation db.Proclamation if err := p.Context.DbPtr.Where("ID = ?", defaultProclamationID).First(&defaultProclamation).Error; err != nil { return err @@ -282,7 +320,7 @@ func (p *ScrollerPage) loadProclamations() error { // Build interleaved stream: // each selected proclamation repeats N times, then default repeats once. for _, proclamation := range selected { - // Prevent default proclamation from being treated as a normal selected proclamation. + // Skip default proclamation if it's in the selected list if proclamation.ID == defaultProclamationID { continue } @@ -292,17 +330,35 @@ func (p *ScrollerPage) loadProclamations() error { continue } + // Get the frequency value for this proclamation + freqValue, ok := frequencyMap[proclamation.SolfeggioFrequencyID] + if !ok { + logger.Warning("No frequency found for proclamation %s, using default palette", proclamation.Name) + freqValue = 528 // Default to 528 if frequency not found + } + + // Get the palette for this frequency + palette, ok := frequencyPalettes[freqValue] + if !ok { + logger.Warning("No palette found for frequency %d, using default palette", freqValue) + palette = colors.Freq_528_Colors[:] // Fallback to default palette + } + p.streams = append(p.streams, proclamationStream{ - name: proclamation.Name, - words: words, - repeat: proclamationPlayCount, // 10 + name: proclamation.Name, + words: words, + repeat: proclamationPlayCount, + palette: palette, + colorIndex: 0, }) if len(defaultWords) > 0 { p.streams = append(p.streams, proclamationStream{ - name: defaultProclamation.Name, - words: defaultWords, - repeat: 1, // play once between selected proclamations + name: defaultProclamation.Name, + words: defaultWords, + repeat: 1, + palette: colors.Freq_528_Colors[:], // Default palette for default proclamation + colorIndex: 0, }) } } @@ -310,9 +366,11 @@ func (p *ScrollerPage) loadProclamations() error { // Fallback: if no non-default selected proclamations, at least play default. if len(p.streams) == 0 && len(defaultWords) > 0 { p.streams = append(p.streams, proclamationStream{ - name: defaultProclamation.Name, - words: defaultWords, - repeat: 1, + name: defaultProclamation.Name, + words: defaultWords, + repeat: 1, + palette: colors.Freq_528_Colors[:], + colorIndex: 0, }) } @@ -346,11 +404,13 @@ func (p *ScrollerPage) nextLineWords() []string { if targetRepeats <= 0 { targetRepeats = 1 } - logger.Debug("Now playing proclamation: %s", s.name) + logger.Info("Now playing proclamation: %s", s.name) if s.completed >= targetRepeats { s.completed = 0 s.wordIndex = 0 + s.colorIndex = 0 // Reset color index when proclamation restarts p.streamIndex = (p.streamIndex + 1) % len(p.streams) + fmt.Println("Now playing proclamation:", p.streams[p.streamIndex].name) } } @@ -377,6 +437,24 @@ func (p *ScrollerPage) getCachedWord(word string) (StringItem, error) { func (p *ScrollerPage) buildLineFromWords(words []string) (Line, error) { line := Line{words: []StringItem{}} + // Get the current proclamation's color palette + var lineColor sdl.Color + if p.useColorPalettes && len(p.streams) > 0 { + currentStream := &p.streams[p.streamIndex] + if len(currentStream.palette) > 0 { + // Use the current color from the palette + lineColor = currentStream.palette[currentStream.colorIndex] + // Increment color index for the next line + currentStream.colorIndex = (currentStream.colorIndex + 1) % len(currentStream.palette) + } else { + // Fallback to default color if no palette is found + lineColor = p.colorPattern.Color() + } + } else { + // Use the default color pattern if useColorPalettes is false + lineColor = p.colorPattern.Color() + } + for _, word := range words { if word == "" { continue @@ -388,7 +466,7 @@ func (p *ScrollerPage) buildLineFromWords(words []string) (Line, error) { } item := cached - item.Color = p.colorPattern.Color() + item.Color = lineColor // Assign the line color to all words in the line if len(line.words) > 0 { line.width += p.spaceWidth @@ -397,6 +475,7 @@ func (p *ScrollerPage) buildLineFromWords(words []string) (Line, error) { line.width += item.W } + line.color = lineColor // Store the line color return line, nil } @@ -607,6 +686,11 @@ func (p *ScrollerPage) SetAutoVelocity(enabled bool, floor, ceiling, step, delay p.velocity = p.velocityFloor } +// SetUseColorPalettes enables or disables the use of color palettes for lines. +func (p *ScrollerPage) SetUseColorPalettes(enabled bool) { + p.useColorPalettes = enabled +} + // updateAutoVelocity handles automatic velocity changes. func (p *ScrollerPage) updateAutoVelocity(dt float32) { if !p.autoVelocity { diff --git a/internal/sync/sync.go b/internal/sync/sync.go index 1d27636..3aa2d4c 100644 --- a/internal/sync/sync.go +++ b/internal/sync/sync.go @@ -15,7 +15,7 @@ var htmlRegex = regexp.MustCompile(`<[^>]*>`) // SyncProclamations fetches data from the API, cleans it, and updates the local DB. // It returns the slice of proclamations so the UI can update immediately. func SyncProclamations(database *gorm.DB) ([]db.Proclamation, error) { - fullURL := "https://portal.rhemawavetech.com/api/v1/proclamations" + fullURL := "https://portal.radiantwavetech.com/api/v1/proclamations" logger.Debug("Syncronizing proclamations. URL: %s", fullURL) proclamations, err := netreq.GetJSON[db.Proclamation](fullURL) @@ -93,7 +93,7 @@ func SyncProclamations(database *gorm.DB) ([]db.Proclamation, error) { } func SyncCategories(database *gorm.DB) ([]db.ProclamationCategory, error) { - fullURL := "https://portal.rhemawavetech.com/api/v1/categories" + fullURL := "https://portal.radiantwavetech.com/api/v1/categories" categories, err := netreq.GetJSON[db.ProclamationCategory](fullURL) if err != nil { logger.Error("Category sync failed, skipping update: %v", err) @@ -118,3 +118,27 @@ func SyncCategories(database *gorm.DB) ([]db.ProclamationCategory, error) { logger.Info("Successfully synced proclamation categories: %d", len(categories)) return categories, nil } + +func SyncFrequencies(database *gorm.DB) ([]db.SolfeggioFrequency, error) { + fullURL := "https://portal.radiantwavetech.com/api/v1/frequencies" + frequencies, err := netreq.GetJSON[db.SolfeggioFrequency](fullURL) + if err != nil { + logger.Error("Frequency sync failed, skipping update: %v", err) + return nil, err + } + err = database.Transaction(func(tx *gorm.DB) error { + if err := tx.Where("1 = 1").Delete(&db.SolfeggioFrequency{}).Error; err != nil { + return err + } + if err := tx.Save(&frequencies).Error; err != nil { + return err + } + return nil + }) + if err != nil { + logger.Error("failed to sync solfeggio frequencies: %v", err) + return nil, err + } + logger.Info("Succesfully synced solfeggio frequencies: %d", len(frequencies)) + return frequencies, nil +} From 8268022d51285b7db4918f87e1142184fa24ab60 Mon Sep 17 00:00:00 2001 From: NeilClack Date: Sun, 31 May 2026 12:15:10 -0500 Subject: [PATCH 3/3] Organize proclamations alphabetically --- internal/page/proclamationOptions.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/internal/page/proclamationOptions.go b/internal/page/proclamationOptions.go index 9ec0592..336be67 100644 --- a/internal/page/proclamationOptions.go +++ b/internal/page/proclamationOptions.go @@ -2,6 +2,7 @@ package page import ( "fmt" + "sort" "strconv" "strings" "unicode" @@ -135,11 +136,6 @@ func (p *ProclamationOptions) Init(ctx *PageCtx) error { for _, pr := range allProcs { fi := p.createFlatItem(pr.Name, pr.ID, false, 0) - // Searching the name AND the content is returning too many options - // I am removing this, for now, to narrow the search results. - // I think a tag system should be implemented and search based on tags - // and names, but not necessarily content. - // fi.SearchText = strings.ToLower(pr.Name + " " + pr.Content) fi.SearchText = strings.ToLower(pr.Name) p.allProclamations = append(p.allProclamations, fi) } @@ -251,9 +247,15 @@ func (p *ProclamationOptions) expand(index int) { newItems = append(newItems, fi) } - // Load proclamations + // Load proclamations for this category var procs []db.Proclamation p.Context.DbPtr.Where("category_id = ?", parentID).Where("ID != ?", defaultProclamationID).Find(&procs) + + // Sort proclamations alphabetically by name + sort.Slice(procs, func(i, j int) bool { + return strings.ToLower(procs[i].Name) < strings.ToLower(procs[j].Name) + }) + for _, pr := range procs { var fi *FlatItem // Reuse existing proclamation from allProclamations if available