package main import ( "fmt" "time" "github.com/egoist/mygo/ui" mujica "mujicaui" "mujicaui/theme" ) var filesPeople = []mujica.Collaborator{ {ID: "a", Name: "Ada Lovelace"}, {ID: "b", Name: "Blaise Pascal", Away: true}, {ID: "c", Name: "Carl Gauss"}, {ID: "d", Name: "Dora Maar"}, {ID: "e", Name: "Emmy Noether"}, } func presenceAvatarsDemo(c *ui.Context, log func(string)) { type state struct { following string disabled, empty bool max float64 } s := ui.Local(c.Root(), "392", func() state { return state{max: 3} }) mediaCheck(c, log, &s.disabled, "Disabled") mediaCheck(c, log, &s.empty, "Empty") if mujica.NumberInput(c, &s.max, mujica.NumberInputOptions{Min: 1, Max: 5, Step: 1, Label: "Visible avatars"}).Changed() { log(fmt.Sprintf("Visible: %.0f", s.max)) } people := filesPeople if s.empty { people = nil } r := mujica.PresenceAvatars(c, people, mujica.PresenceAvatarsOptions{Following: s.following, Max: int(s.max), Disabled: s.disabled}) if r.Follow != "" { s.following = r.Follow log("Follow: " + r.Follow) } if r.Unfollow { s.following = "" log("Following stopped") } } var filesReactionChoices = []string{"\U0001f44d", "\u2764\ufe0f", "\U0001f389", "\U0001f440", "\U0001f64f"} func reactionsDemo(c *ui.Context, log func(string)) { type state struct { items []mujica.Reaction disabled bool } s := ui.Local(c.Root(), "394", func() state { return state{items: []mujica.Reaction{{Emoji: filesReactionChoices[0], Count: 3, Mine: true}, {Emoji: filesReactionChoices[2], Count: 1}}} }) mediaCheck(c, log, &s.disabled, "Disabled") if mujica.Button(c, "Clear reactions", mujica.ButtonOptions{}).Clicked() { s.items = nil log("Reactions cleared") } r := mujica.Reactions(c, s.items, mujica.ReactionsOptions{Choices: filesReactionChoices, Disabled: s.disabled}) emoji := r.Toggled if emoji == "" { emoji = r.Added } if emoji == "" { return } for i := range s.items { if s.items[i].Emoji == emoji { if s.items[i].Mine { s.items[i].Mine = false s.items[i].Count-- } else { s.items[i].Mine = true s.items[i].Count++ } if s.items[i].Count == 0 { s.items = append(s.items[:i:i], s.items[i+1:]...) } log("Reaction toggled") return } } s.items = append(s.items, mujica.Reaction{Emoji: emoji, Count: 1, Mine: true}) log("Reaction added") } func shareDialogDemo(c *ui.Context, log func(string)) { type state struct { members []mujica.ShareMember open, disabled bool next int } s := ui.Local(c.Root(), "395", func() state { return state{members: []mujica.ShareMember{ {ID: "owner", Name: "Ada Lovelace", Email: "ada@court.org", Owner: true, Permission: mujica.PermissionEdit}, {ID: "b", Name: "Blaise Pascal", Email: "blaise@court.org", Permission: mujica.PermissionView}, }} }) mediaCheck(c, log, &s.disabled, "Disabled") if mujica.Button(c, "Share Ledger", mujica.ButtonOptions{Disabled: s.disabled}).Clicked() { s.open = true log("Share dialog opened") } was := s.open r := mujica.ShareDialog(c, &s.open, &s.members, mujica.ShareDialogOptions{Title: "Ledger", Link: "https://court.org/ledger", Disabled: s.disabled, OnCopy: func(link string) { c.WriteClipboard(link); log("Copy callback") }}) if r.Invited != nil { s.next++ s.members = append(s.members, mujica.ShareMember{ID: fmt.Sprintf("invite-%d", s.next), Name: r.Invited.Email, Email: r.Invited.Email, Permission: r.Invited.Permission}) log("Member invited") } if r.Changed != "" { log("Permission changed: " + r.Changed) } if r.Removed != "" { log("Member removed: " + r.Removed) } if r.Copied { log("Link copied") } if was && !s.open { log("Share dialog closed") } } func permissionSelectDemo(c *ui.Context, log func(string)) { type state struct { value mujica.Permission disabled bool } s := ui.Local(c.Root(), "396", func() state { return state{} }) mediaCheck(c, log, &s.disabled, "Disabled") if mujica.PermissionSelect(c, &s.value, mujica.PermissionSelectOptions{Disabled: s.disabled}).Changed() { log(fmt.Sprintf("Permission: %d", s.value)) } } func liveIndicatorDemo(c *ui.Context, log func(string)) { type state struct { status mujica.LiveStatus showTime bool } s := ui.Local(c.Root(), "399", func() state { return state{showTime: true} }) if mujica.Select(c, &s.status, []mujica.SelectOption[mujica.LiveStatus]{{Value: mujica.LiveSynced, Label: "Synced"}, {Value: mujica.LiveSyncing, Label: "Syncing"}, {Value: mujica.LiveReconnecting, Label: "Reconnecting"}, {Value: mujica.LiveOffline, Label: "Offline"}, {Value: mujica.LiveError, Label: "Error"}}, mujica.SelectOptions{Label: "Sync status"}).Changed() { log(fmt.Sprintf("Sync status: %d", s.status)) } mediaCheck(c, log, &s.showTime, "Last sync time") now := time.Date(2026, 10, 5, 12, 0, 0, 0, time.UTC) opts := mujica.LiveIndicatorOptions{Error: "Server unavailable", Now: now} if s.showTime { opts.LastSynced = now.Add(-3 * time.Minute) } if mujica.LiveIndicator(c, s.status, opts).Changed() { s.status = mujica.LiveReconnecting log("Sync retried") } } func remoteCursorDemo(c *ui.Context, log func(string)) { type state struct { offset float64 selection, second bool } s := ui.Local(c.Root(), "400", func() state { return state{offset: 48, selection: true, second: true} }) if mujica.NumberInput(c, &s.offset, mujica.NumberInputOptions{Min: 0, Max: 79, Step: 1, Label: "Remote text offset"}).Changed() { log(fmt.Sprintf("Offset: %.0f", s.offset)) } mediaCheck(c, log, &s.selection, "Selection") mediaCheck(c, log, &s.second, "Second collaborator") carets := []mujica.RemoteCaret{{ID: "ada", Name: "Ada", Offset: int(s.offset), Anchor: int(s.offset)}} if s.selection { carets[0].Anchor = max(0, carets[0].Offset-5) } if s.second { carets = append(carets, mujica.RemoteCaret{ID: "blaise", Name: "Blaise", Offset: 8, Anchor: 8}) } ui.Box(c).Height(140).FillWidth().Background(mujica.Tokens(c).Surface).Children(func() { ui.Column(c).Children(func() { for _, line := range []string{"The seal of the court is kept", "by the chancellor, who answers"} { ui.Text(c, line).Font(theme.MonoFont).FontSize(mujica.FontSize(c, 13)).Height(20) } }) at := func(o int) (float32, float32) { return float32(o%40) * 8, float32(o/40) * 20 } mujica.RemoteCursor(c, carets, mujica.RemoteCursorOptions{ Caret: func(o int) ui.Rect { x, y := at(o); return ui.Rect{X: x, Y: y, H: 20} }, Selection: func(from, to int) []ui.Rect { var out []ui.Rect for o := from; o < to; o++ { x, y := at(o) out = append(out, ui.Rect{X: x, Y: y, W: 8, H: 20}) } return out }, }) }) }